-
Notifications
You must be signed in to change notification settings - Fork 11
/
make-osm-blocks.py
executable file
·158 lines (137 loc) · 4.65 KB
/
make-osm-blocks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/python
from shapely.geometry import Point, Polygon, MultiPolygon, asShape, LineString
from shapely.ops import cascaded_union, polygonize, transform
import sys, json, math, pickle, os, geojson
from shapely.validation import explain_validity
from functools import partial
import fiona
import json
import geojson
import traceback
import pyproj
import rtree
from optparse import OptionParser
parser = OptionParser()
parser.add_option("--reload", dest="reload", action="store_true", default=False)
parser.add_option("-r", "--resegment", dest="resegment", action="store_true", default=False)
(options, args) = parser.parse_args()
rtree_basename = 'rtree'
should_do_load = True
if os.path.exists(rtree_basename + '.idx') and not options.reload:
print 'found existing index %s.idx, and --reload not specified, reusing index' % rtree_basename
should_do_load = False
idx = rtree.index.Index(rtree_basename, overwrite = should_do_load)
lineIndex = 0
lines = {}
minlat = sys.float_info.max
minlng = sys.float_info.max
maxlat = sys.float_info.min
maxlng = sys.float_info.min
def process_line(line):
global lineIndex, minlat, minlng, maxlat, maxlng
lineIndex += 1
lines[lineIndex] = line
for c in line.coords:
minlat = min(minlat, c[1])
maxlat = max(maxlat, c[1])
minlng = min(minlng, c[0])
maxlng = max(maxlng, c[0])
def loadGenerator():
for f in args:
source = fiona.open(f, 'r')
for index, feature in enumerate(source):
if index % 1000 == 0:
print "loaded %s of %s" % (index, len(source))
if feature['geometry']['type'] == 'LineString':
coords = feature['geometry']['coordinates']
for (start, end) in zip(coords[:-1], coords[1:]):
line = LineString([start, end])
process_line(line)
yield (lineIndex, line.bounds, line)
if feature['geometry']['type'] == 'MultiLineString':
print 'got multilinestring'
for coords in feature['geometry']['coordinates']:
print coords
for (start, end) in zip(coords[:-1], coords[1:]):
line = LineString([start, end])
process_line(line)
yield (lineIndex, line.bounds, line)
# load everything into an rtree
def doLoad():
global idx
idx = rtree.index.Index(loadGenerator())
# for every line, fine all the intersections
def doResegment():
global lines
newLines = []
for lineIndex, line in enumerate(lines.values()):
if lineIndex % 1000 == 0:
print "processed %s of %s" % (lineIndex, len(lines))
lineParts = [line]
# split the line into all the intersecting pieces
intersections = idx.intersection(line.bounds, objects = True)
for intersectingLine in intersections:
newLineParts = []
for linePart in lineParts:
intersection = linePart.intersection(intersectingLine.object)
if intersection.is_empty or intersection.coords[0] in linePart.coords:
newLineParts.append(linePart)
else:
newLineParts.append(LineString([linePart.coords[0], intersection.coords[0]]))
newLineParts.append(LineString([linePart.coords[1], intersection.coords[0]]))
lineParts = newLineParts
newLines = newLines + lineParts
lines = newLines
def writeBlocks(blocks, filename):
outputFile = open(filename, 'w')
outputFile.write('{ "type": "FeatureCollection", "features": [')
for index, poly in enumerate(blocks):
if index % 1000 == 0:
print "wrote %s blocks so far" % (index)
try:
if not poly.is_valid:
poly = poly.buffer(0)
jsonGeom = json.loads(geojson.dumps(poly.__geo_interface__))
if index != 0:
outputFile.write(',')
outputFile.write(json.dumps({'geometry': jsonGeom, 'properties': {}}))
except:
print b
print traceback.print_exc()
outputFile.write(']}')
outputFile.close()
def doPolygonize():
blocks = polygonize(lines)
writeBlocks(blocks, args[0] + '-blocks.geojson')
blocks = polygonize(lines)
bounds = Polygon([
[minlng, minlat],
[minlng, maxlat],
[maxlng, maxlat],
[maxlng, minlat],
[minlng, minlat]
])
# Geometry transform function based on pyproj.transform
project = partial(
pyproj.transform,
pyproj.Proj(init='EPSG:3785'),
pyproj.Proj(init='EPSG:4326'))
print bounds
print transform(project, bounds)
print 'finding holes'
for index, block in enumerate(blocks):
if index % 1000 == 0:
print "diff'd %s" % (index)
if not block.is_valid:
print explain_validity(block)
print transform(project, block)
else:
bounds = bounds.difference(block)
print bounds
if should_do_load:
doLoad()
if options.resegment:
doResegment()
else:
lines = lines.values()
doPolygonize()