stud/misc/greedy-points-to-multiline.py

90 lines
2.3 KiB
Python
Raw Permalink Normal View History

2020-03-29 17:38:14 +03:00
#!/usr/bin/env python3
2020-04-13 11:24:26 +03:00
"""
This script takes a point layer and connects them all in a "greedy" way: it
will take a random point, connect its closest one, and repeat the loop until
all of the points are connected.
This is useful if you have point area boudnaries and want to make a real
connected polygon.
"""
2020-03-29 17:38:14 +03:00
import sys
import rtree
import os
from osgeo import ogr
2020-03-30 16:42:53 +03:00
2020-03-29 17:38:14 +03:00
def main():
driver = ogr.GetDriverByName('gpkg')
2020-03-29 17:47:59 +03:00
inDataSource = driver.Open(sys.argv[1], 0)
2020-03-29 17:38:14 +03:00
inLayer = inDataSource.GetLayer()
srs = inLayer.GetSpatialRef()
outDataSource = driver.CreateDataSource(sys.argv[2])
outLayerName, _ = os.path.splitext(os.path.basename(sys.argv[2]))
2020-03-30 16:42:53 +03:00
outLayer = outDataSource.CreateLayer(
outLayerName,
srs,
geom_type=ogr.wkbLineString
)
2020-03-29 17:38:14 +03:00
2020-03-29 17:47:59 +03:00
points = extract_layer_points(inLayer)
path = greedy_path(points)
write_path(srs, outLayer, path)
2020-03-29 17:38:14 +03:00
inDataSource.Destroy()
outDataSource.Destroy()
2020-03-29 17:47:59 +03:00
def extract_layer_points(layer):
"""Extracts points from layer in a format:
dict(
i : int
points: (x, y : int)
)
"""
2020-03-29 17:38:14 +03:00
points = {}
for (i, pt) in enumerate(layer):
p = pt.GetGeometryRef().GetPoint()
points[i] = (p[0], p[1])
return points
2020-03-30 16:42:53 +03:00
2020-03-29 17:38:14 +03:00
def greedy_path(points):
2020-03-29 17:47:59 +03:00
"""Given a dict of points, return a "greedy" path between them.
It will start with a random point, and keep navigating to the closest one,
until all the points have been visited.
"""
2020-03-30 16:42:53 +03:00
idx = rtree.index.Index(interleaved=False)
2020-03-29 17:38:14 +03:00
for (i, pt) in points.items():
idx.insert(i, pt)
def _bounds(_pts):
return (_pts[0], _pts[0], _pts[1], _pts[1])
ret = [0]
idx.delete(0, _bounds(points[0]))
while len(ret) < len(points):
pt = _bounds(points[ret[-1]])
nearest = next(idx.nearest(pt))
ret.append(nearest)
idx.delete(nearest, _bounds(points[nearest]))
ret.append(0)
return [points[point] for point in ret]
2020-03-29 17:47:59 +03:00
def write_path(srs, outLayer, path):
"writes the linestring (path) to the outLayer."
line = ogr.Geometry(ogr.wkbLineString)
for point in path:
line.AddPoint(point[0], point[1])
outFeature = ogr.Feature(outLayer.GetLayerDefn())
outFeature.SetGeometry(line)
outLayer.CreateFeature(outFeature)
2020-03-29 17:38:14 +03:00
if __name__ == '__main__':
main()