greedy-points-to-multiline.py (2363B) - Raw
1 #!/usr/bin/env python3 2 """ 3 This script takes a point layer and connects them all in a "greedy" way: it 4 will take a random point, connect its closest one, and repeat the loop until 5 all of the points are connected. 6 7 This is useful if you have point area boudnaries and want to make a real 8 connected polygon. 9 """ 10 11 import sys 12 import rtree 13 import os 14 from osgeo import ogr 15 16 17 def main(): 18 driver = ogr.GetDriverByName('gpkg') 19 20 inDataSource = driver.Open(sys.argv[1], 0) 21 inLayer = inDataSource.GetLayer() 22 srs = inLayer.GetSpatialRef() 23 outDataSource = driver.CreateDataSource(sys.argv[2]) 24 outLayerName, _ = os.path.splitext(os.path.basename(sys.argv[2])) 25 outLayer = outDataSource.CreateLayer( 26 outLayerName, 27 srs, 28 geom_type=ogr.wkbLineString 29 ) 30 31 points = extract_layer_points(inLayer) 32 path = greedy_path(points) 33 write_path(srs, outLayer, path) 34 35 inDataSource.Destroy() 36 outDataSource.Destroy() 37 38 39 def extract_layer_points(layer): 40 """Extracts points from layer in a format: 41 dict( 42 i : int 43 points: (x, y : int) 44 ) 45 """ 46 points = {} 47 for (i, pt) in enumerate(layer): 48 p = pt.GetGeometryRef().GetPoint() 49 points[i] = (p[0], p[1]) 50 return points 51 52 53 def greedy_path(points): 54 """Given a dict of points, return a "greedy" path between them. 55 56 It will start with a random point, and keep navigating to the closest one, 57 until all the points have been visited. 58 """ 59 idx = rtree.index.Index(interleaved=False) 60 for (i, pt) in points.items(): 61 idx.insert(i, pt) 62 63 def _bounds(_pts): 64 return (_pts[0], _pts[0], _pts[1], _pts[1]) 65 66 ret = [0] 67 idx.delete(0, _bounds(points[0])) 68 while len(ret) < len(points): 69 pt = _bounds(points[ret[-1]]) 70 nearest = next(idx.nearest(pt)) 71 ret.append(nearest) 72 idx.delete(nearest, _bounds(points[nearest])) 73 ret.append(0) 74 return [points[point] for point in ret] 75 76 77 def write_path(srs, outLayer, path): 78 "writes the linestring (path) to the outLayer." 79 line = ogr.Geometry(ogr.wkbLineString) 80 for point in path: 81 line.AddPoint(point[0], point[1]) 82 83 outFeature = ogr.Feature(outLayer.GetLayerDefn()) 84 outFeature.SetGeometry(line) 85 outLayer.CreateFeature(outFeature) 86 87 88 if __name__ == '__main__': 89 main()