Skip to content

Commit cbe98e4

Browse files
committed
Calculate and assign minzoom and maxzoom values for path properties (#191).
1 parent 1b20322 commit cbe98e4

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

mapmaker/output/geojson.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939

4040
#===============================================================================
4141

42+
# Earth circumference (WGS84 equatorial radius for Web Mercator EPSG:3857)
43+
EARTH_CIRCUMFERENCE = 2 * math.pi * 6378137.0 # meters
44+
45+
#===============================================================================
46+
4247
class GeoJSONOutput(object):
4348
def __init__(self, flatmap: FlatMap, layer: MapLayer, output_dir: str):
4449
#======================================================================
@@ -120,6 +125,10 @@ def __save_features(self, features):
120125
if scale > 6 and 'group' not in properties and 'minzoom' not in properties:
121126
geojson['tippecanoe']['minzoom'] = 4
122127
else:
128+
if (nodes:=self.__flatmap.connectivity()['paths'].get(feature.models)):
129+
zoom_range = self.__get_path_zoom_range(nodes)
130+
geojson['properties']['minzoom'] = zoom_range[0]
131+
geojson['properties']['maxzoom'] = zoom_range[1]
123132
geojson['properties']['scale'] = 10
124133
geojson['properties'].update(properties)
125134

@@ -158,3 +167,43 @@ def __save_features(self, features):
158167
progress_bar.update(1)
159168

160169
progress_bar.close()
170+
171+
def __get_path_zoom_range(self, nodes):
172+
path_min_coverage = settings.get('pathMinCoverage', 0.7)
173+
path_max_coverage = settings.get('pathMaxCoverage', 5.0)
174+
175+
points = [geom.centroid
176+
for node in nodes.get('nodes', [])
177+
if (f := self.__flatmap.get_feature_by_geojson_id(node)) is not None
178+
and (geom := f.geometry) is not None]
179+
180+
if len(points) > 1:
181+
# extent
182+
xs = [p.x for p in points]
183+
ys = [p.y for p in points]
184+
# World-space extents (meters, EPSG:3857)
185+
x_extent = max(xs) - min(xs)
186+
y_extent = max(ys) - min(ys)
187+
max_dist = 0.0
188+
for i, p1 in enumerate(points):
189+
for p2 in points[i+1:]:
190+
dist = math.hypot(p2.x - p1.x, p2.y - p1.y)
191+
if dist > max_dist:
192+
max_dist = dist
193+
extent = max(x_extent, y_extent, max_dist)
194+
if not math.isfinite(extent) or extent <= 0:
195+
return self.__flatmap.min_zoom, self.__flatmap.max_zoom
196+
197+
# minzoom and maxzoom
198+
z_min = math.ceil(math.log2((path_min_coverage * EARTH_CIRCUMFERENCE) / extent))
199+
z_max = math.floor(math.log2((path_max_coverage * EARTH_CIRCUMFERENCE) / extent))
200+
if z_max < z_min:
201+
z_max = z_min
202+
z_min = max(self.__flatmap.min_zoom, z_min)
203+
z_max = min(self.__flatmap.max_zoom, z_max)
204+
if z_min >= self.__flatmap.max_zoom:
205+
z_min = self.__flatmap.max_zoom - 1
206+
z_max = self.__flatmap.max_zoom
207+
return z_min, z_max
208+
209+
return self.__flatmap.min_zoom, self.__flatmap.max_zoom

0 commit comments

Comments
 (0)