@osm-editor-kit/osm-coverage 0.1.0-alpha.2 → 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,8 +22,7 @@ import { createOsmCoverageApi, OsmDataSource } from '@osm-editor-kit/osm-coverag
22
22
  const api = createOsmCoverageApi<{ source: OsmDataSource }>({
23
23
  getSessionKey: ({ source }) => ['osm', source] as const,
24
24
  minZoom: 14,
25
- getDownloadUrl: (bounds, { source }) =>
26
- /* build Overpass or Map API URL */ '',
25
+ getDownloadUrl: (bounds, { source }) => /* build Overpass or Map API URL */ '',
27
26
  // optional: storage: { load, save, clear }
28
27
  })
29
28
 
@@ -31,6 +31,11 @@ export declare function isValidBounds(bounds: MapBounds): boolean;
31
31
  export declare function boundsArea(bounds: MapBounds): number;
32
32
  export declare function bufferBoundsInViewport(request: MapBounds, viewport: MapBounds, zoom: number, mapSizePx: MapSizePx): MapBounds;
33
33
  export declare function clampBoundsToViewport(request: MapBounds, viewport: MapBounds): MapBounds;
34
+ /** Lon/lat length of the zoom-aware seam overlap (same magnitude as fetch buffer). */
35
+ export declare function overlapDeltaAtZoom(viewport: MapBounds, zoom: number, mapSizePx: MapSizePx): {
36
+ lon: number;
37
+ lat: number;
38
+ };
34
39
  export declare function createViewportFetchRequest(viewport: MapBounds, zoom: number, mapSizePx: MapSizePx, kind: CoverageFetchKind): CoverageFetchRequest | null;
35
40
  export declare function computeMissingFetchRequests(viewport: MapBounds, coverage: Feature<Polygon | MultiPolygon> | null, zoom: number, mapSizePx: MapSizePx): CoverageFetchRequest[];
36
41
  export declare function unionIntoCoverage(coverage: Feature<Polygon | MultiPolygon> | null, requestBounds: MapBounds): Feature<Polygon | MultiPolygon>;
package/dist/index.js CHANGED
@@ -48,19 +48,15 @@ function bufferBoundsInViewport(request, viewport, zoom, mapSizePx) {
48
48
  north: request.north + latBuffer
49
49
  };
50
50
  }
51
- function clampBoundsToViewport(request, viewport) {
51
+ function overlapDeltaAtZoom(viewport, zoom, mapSizePx) {
52
+ const px = bufferPxAtZoom(zoom);
52
53
  return {
53
- west: Math.max(request.west, viewport.west),
54
- south: Math.max(request.south, viewport.south),
55
- east: Math.min(request.east, viewport.east),
56
- north: Math.min(request.north, viewport.north)
54
+ lon: px * (viewport.east - viewport.west) / mapSizePx.width,
55
+ lat: px * (viewport.north - viewport.south) / mapSizePx.height
57
56
  };
58
57
  }
59
58
  function prepareFetchBounds(request, viewport, zoom, mapSizePx) {
60
- const buffered = clampBoundsToViewport(
61
- bufferBoundsInViewport(request, viewport, zoom, mapSizePx),
62
- viewport
63
- );
59
+ const buffered = bufferBoundsInViewport(request, viewport, zoom, mapSizePx);
64
60
  if (!isValidBounds(buffered)) return null;
65
61
  const viewportArea = boundsArea(viewport);
66
62
  if (boundsArea(buffered) < MIN_MISSING_AREA_RATIO * viewportArea) return null;
@@ -74,27 +70,28 @@ function missingParts(viewport, coverage) {
74
70
  (feature) => feature.geometry.type === "Polygon"
75
71
  );
76
72
  }
77
- function stripCandidates(viewport, coverage, missingPart) {
73
+ function stripCandidates(viewport, coverage, missingPart, zoom, mapSizePx) {
78
74
  const viewportPoly = boundsToPolygon(viewport);
79
75
  const covered = intersect(featureCollection([viewportPoly, coverage]));
80
76
  if (!covered) return [];
81
77
  const coveredBbox = polygonToBounds(covered);
78
+ const { lon, lat } = overlapDeltaAtZoom(viewport, zoom, mapSizePx);
82
79
  const candidates = [
83
80
  {
84
81
  west: viewport.west,
85
82
  south: viewport.south,
86
- east: coveredBbox.west,
83
+ east: Math.min(viewport.east, coveredBbox.west + lon),
87
84
  north: viewport.north
88
85
  },
89
86
  {
90
- west: coveredBbox.east,
87
+ west: Math.max(viewport.west, coveredBbox.east - lon),
91
88
  south: viewport.south,
92
89
  east: viewport.east,
93
90
  north: viewport.north
94
91
  },
95
92
  {
96
93
  west: viewport.west,
97
- south: coveredBbox.north,
94
+ south: Math.max(viewport.south, coveredBbox.north - lat),
98
95
  east: viewport.east,
99
96
  north: viewport.north
100
97
  },
@@ -102,7 +99,7 @@ function stripCandidates(viewport, coverage, missingPart) {
102
99
  west: viewport.west,
103
100
  south: viewport.south,
104
101
  east: viewport.east,
105
- north: coveredBbox.south
102
+ north: Math.min(viewport.north, coveredBbox.south + lat)
106
103
  }
107
104
  ];
108
105
  return candidates.filter((strip) => {
@@ -111,14 +108,14 @@ function stripCandidates(viewport, coverage, missingPart) {
111
108
  return overlap !== null && area(overlap) > 0;
112
109
  });
113
110
  }
114
- function chooseBoundsForMissingPart(viewport, coverage, missingPart) {
111
+ function chooseBoundsForMissingPart(viewport, coverage, missingPart, zoom, mapSizePx) {
115
112
  const partArea = area(missingPart);
116
113
  const partBbox = polygonToBounds(missingPart);
117
114
  const fillRatio = partArea / area(boundsToPolygon(partBbox));
118
115
  if (fillRatio >= HIGH_FILL_RATIO) {
119
116
  return [partBbox];
120
117
  }
121
- const strips = stripCandidates(viewport, coverage, missingPart);
118
+ const strips = stripCandidates(viewport, coverage, missingPart, zoom, mapSizePx);
122
119
  const stripAreas = strips.map((strip) => ({ bounds: strip, area: boundsArea(strip) })).sort((a, b) => b.area - a.area);
123
120
  const stripTotalArea = stripAreas.reduce((sum, strip) => sum + strip.area, 0);
124
121
  const bboxArea = boundsArea(partBbox);
@@ -143,7 +140,7 @@ function computeMissingFetchRequests(viewport, coverage, zoom, mapSizePx) {
143
140
  );
144
141
  if (significantParts.length === 0) return [];
145
142
  const rawBounds = significantParts.flatMap(
146
- (part) => chooseBoundsForMissingPart(viewport, coverage, part)
143
+ (part) => chooseBoundsForMissingPart(viewport, coverage, part, zoom, mapSizePx)
147
144
  );
148
145
  let requests = rawBounds.map((request) => {
149
146
  const prepared = prepareFetchBounds(request, viewport, zoom, mapSizePx);
@@ -160,6 +157,15 @@ function computeMissingFetchRequests(viewport, coverage, zoom, mapSizePx) {
160
157
  return full ? [full] : [];
161
158
  }
162
159
  requests = requests.slice(0, MAX_STRIP_REQUESTS);
160
+ let projected = coverage;
161
+ for (const request of requests) {
162
+ projected = unionIntoCoverage(projected, request.bounds);
163
+ }
164
+ const leftover = missingParts(viewport, projected).reduce((sum, part) => sum + area(part), 0);
165
+ if (leftover >= MIN_MISSING_AREA_RATIO * viewportArea) {
166
+ const full = createViewportFetchRequest(viewport, zoom, mapSizePx, "full");
167
+ return full ? [full] : [];
168
+ }
163
169
  return requests.map((request) => ({
164
170
  bounds: request.bounds,
165
171
  kind: request.kind
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@osm-editor-kit/osm-coverage",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "Incremental OSM coverage fetching for map viewports with TanStack Query session state.",
5
+ "homepage": "https://github.com/osmberlin/street-space-editor/tree/main/packages/osm-coverage",
5
6
  "license": "MIT",
6
7
  "author": "Tobias Jordans",
7
8
  "repository": {
@@ -9,12 +10,11 @@
9
10
  "url": "git+https://github.com/osmberlin/street-space-editor.git",
10
11
  "directory": "packages/osm-coverage"
11
12
  },
12
- "homepage": "https://github.com/osmberlin/street-space-editor/tree/main/packages/osm-coverage",
13
- "type": "module",
14
- "sideEffects": false,
15
13
  "files": [
16
14
  "dist"
17
15
  ],
16
+ "type": "module",
17
+ "sideEffects": false,
18
18
  "exports": {
19
19
  ".": {
20
20
  "types": "./dist/index.d.ts",
@@ -36,7 +36,7 @@
36
36
  "postpack": "bun ../../scripts/package-exports-restore.ts"
37
37
  },
38
38
  "dependencies": {
39
- "@osm-editor-kit/osm-data": "workspace:*",
39
+ "@osm-editor-kit/osm-data": "^0.1.0-alpha.1",
40
40
  "@tanstack/react-query": "^5.85.5",
41
41
  "@turf/area": "^7.3.5",
42
42
  "@turf/bbox": "^7.3.5",