Package: fast-crossing
fast crossing(See jupyter notebookhere)Fast polyline (line segments) intersection (fast version of bentley-ottmann).Installationvia pippipinstall-Ufast-crossingfrom sourcegitclone--recursivehttps://github.com/cubao/fast-crossing
pipinstall./fast-crossingOrpipinstallgit+https://github.com/cubao/fast-crossing.git(you can build wheels for later reuse bypip wheel git+https://github.com/cubao/fast-crossing.git)RelatedInspired byanvaka/isect: Segments intersection detection library.Usage & TestsSeetests/test_basic.py:importnumpyasnpimportpytestfromfast_crossingimportFastCrossingdeftest_fast_crossing():fc=FastCrossing()# add your polylines"""2 C|1 D0               |                  5A---------------o------------------B||-2 E"""fc.add_polyline(np.array([[0.0,0.0],[5.0,0.0]]))# ABfc.add_polyline(np.array([[2.5,2.0],[2.5,1.0],[2.5,-2.0]]))# CDE# build indexfc.finish()# num_poylinesassert2==fc.num_poylines()rulers=fc.polyline_rulers()assertlen(rulers)==2ruler0=fc.polyline_ruler(0)ruler1=fc.polyline_ruler(1)assertnotruler0.is_wgs84()assertnotruler1.is_wgs84()assertruler0.length()==5assertruler1.length()==4assertfc.polyline_ruler(10)isNone# intersectionsret=fc.intersections([1.5,0],[3.5,2])assertlen(ret)==2assertnp.linalg.norm(fc.coordinates(ret[0])-[1.5,0,0])<1e-15assertnp.linalg.norm(fc.coordinates(ret[1])-[2.5,1,0])<1e-15xyz=fc.coordinates(0,0,0.2)assertnp.linalg.norm(xyz-[1.0,0,0])<1e-15withpytest.raises(IndexError)asexcinfo:xyz=fc.coordinates(2,0,0.5)assert"map::at"instr(excinfo)# query all line segment intersections# [#    (array([2.5, 0. ]),#     array([0.5       , 0.33333333]),#     array([0, 0], dtype=int32),#     array([1, 1], dtype=int32))# ]ret=fc.intersections()# print(ret)assertlen(ret)==1forxy,ts,label1,label2inret:# xy 是交点，即图中的 o 点坐标# t,s 是分位点，(0.5, 0.33)#        0.5  ->   o 在 AB 1/2 处#        0.33 ->   o 在 DE 1/3 处# label1 是 line segment 索引，(polyline_index, point_index)#        e.g. (0, 0)，polyline AB 的第一段# label2 是另一个条 line seg 的索引#        e.g. (1, 1)，polyline CDE 的第二段（DE 段）# print(xy)# print(ts)# print(label1)# print(label2)assertnp.all(xy==[2.5,0])assertnp.all(ts==[0.5,1/3.0])assertnp.all(label1==[0,0])assertnp.all(label2==[1,1])# query intersections against provided polylinepolyline=np.array([[-6.0,-1.0],[-5.0,1.0],[5.0,-1.0]])ret=fc.intersections(polyline)ret=np.array(ret)# 还是转化成 numpy 比较好用xy=ret[:,0]# 直接取出所有交点ts=ret[:,1]# 所有分位点label1=ret[:,2]# 所有 label1（当前 polyline 的 label）label2=ret[:,3]# tree 中 line segs 的 label# print(ret, xy, ts, label1, label2)assertnp.all(xy[0]==[0,0])assertnp.all(xy[1]==[2.5,-0.5])assertnp.all(ts[0]==[0.5,0])assertnp.all(ts[1]==[0.75,0.5])assertnp.all(label1==[[0,1],[0,1]])assertnp.all(label2==[[0,0],[1,1]])polyline2=np.column_stack((polyline,np.zeros(len(polyline))))ret2=np.array(fc.intersections(polyline2[:,:2]))assertstr(ret)==str(ret2)deftest_fast_crossing_intersection3d():fc=FastCrossing()"""2 C|1 D0               |                  5A---------------o------------------B||-2 E"""fc.add_polyline(np.array([[0.0,0.0,0.0],[5.0,0.0,100]]))# ABfc.add_polyline(np.array([[2.5,2.0,0.0],[2.5,1.0,100],[2.5,-2.0,0]]))# CDEfc.finish()ret=fc.intersections()assertlen(ret)==1ret=ret[0]xyz1=fc.coordinates(ret,second=False)xyz2=fc.coordinates(ret)assertnp.linalg.norm(xyz1-[2.5,0,50])<1e-10assertnp.linalg.norm(xyz2-[2.5,0,2/3*100.0])<1e-10deftest_fast_crossing_auto_rebuild_flatbush():fc=FastCrossing()fc.add_polyline(np.array([[0.0,0.0,0.0],[5.0,0.0,100]]))# ABfc.add_polyline(np.array([[2.5,2.0,0.0],[2.5,1.0,100],[2.5,-2.0,0]]))# CDEret=fc.intersections()assertlen(ret)==1fc.add_polyline([[1.5,0],[3.5,2]])ret=fc.intersections()assertlen(ret)==4# should dedup to 3?deftest_fast_crossing_filter_by_z():fc=FastCrossing()fc.add_polyline([[0,0,0],[1,0,0]])fc.add_polyline([[0,0,10],[1,0,10]])fc.add_polyline([[0,0,20],[1,0,20]])ret=fc.intersections([[0.5,-1],[0.5,1]])assertlen(ret)==3ret=fc.intersections([[0.5,-1],[0.5,1]],z_min=-1,z_max=1)assertlen(ret)==1assertfc.coordinates(ret[0])[2]==0ret=fc.intersections([[0.5,-1,10],[0.5,1,10]],z_min=-1,z_max=1)assertlen(ret)==1assertfc.coordinates(ret[0])[2]==10ret=fc.intersections([[0.5,-1,20],[0.5,1,20]],z_min=-1,z_max=1)assertlen(ret)==1assertfc.coordinates(ret[0])[2]==20ret=fc.intersections([[0.5,-1,15],[0.5,1,15]],z_min=-6,z_max=6)assertlen(ret)==2assertfc.coordinates(ret[0])[2]==10assertfc.coordinates(ret[1])[2]==20deftest_fast_crossing_dedup():# should be stablefor_inrange(100):fc=FastCrossing()fc.add_polyline([[0,0,0],[1,0,0],[2,0,0]])fc.add_polyline([[0,1,0],[1,1,0],[2,1,0]])ret=fc.intersections([[1,-1],[1,1]])assertlen(ret)==2assertnp.all(ret[0][-1]==[0,0]),retassertnp.all(ret[1][-1]==[1,0]),retassertret[0][1][1]==1.0,retassertret[1][1][1]==1.0,retret=fc.intersections([[1,-1],[1,1]],dedup=False)# for idx, row in enumerate(ret):#     print(idx, row)assertlen(ret)==4