|
39 | 39 |
|
40 | 40 | #=============================================================================== |
41 | 41 |
|
| 42 | +# Earth circumference (WGS84 equatorial radius for Web Mercator EPSG:3857) |
| 43 | +EARTH_CIRCUMFERENCE = 2 * math.pi * 6378137.0 # meters |
| 44 | + |
| 45 | +#=============================================================================== |
| 46 | + |
42 | 47 | class GeoJSONOutput(object): |
43 | 48 | def __init__(self, flatmap: FlatMap, layer: MapLayer, output_dir: str): |
44 | 49 | #====================================================================== |
@@ -120,6 +125,10 @@ def __save_features(self, features): |
120 | 125 | if scale > 6 and 'group' not in properties and 'minzoom' not in properties: |
121 | 126 | geojson['tippecanoe']['minzoom'] = 4 |
122 | 127 | 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] |
123 | 132 | geojson['properties']['scale'] = 10 |
124 | 133 | geojson['properties'].update(properties) |
125 | 134 |
|
@@ -158,3 +167,43 @@ def __save_features(self, features): |
158 | 167 | progress_bar.update(1) |
159 | 168 |
|
160 | 169 | 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