leaflet-city-mapper 0.0.6 → 0.0.7

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.
Files changed (241) hide show
  1. package/README.md +10 -5
  2. package/dist/leaflet-city-mapper-src.js +7059 -6566
  3. package/dist/leaflet-city-mapper-src.js.map +4 -4
  4. package/dist/leaflet-city-mapper-worker-src.js +2766 -0
  5. package/dist/leaflet-city-mapper-worker-src.js.map +4 -4
  6. package/dist/leaflet-city-mapper-worker.js +1 -1
  7. package/dist/leaflet-city-mapper-worker.js.map +4 -4
  8. package/dist/leaflet-city-mapper.js +5 -3
  9. package/dist/leaflet-city-mapper.js.map +4 -4
  10. package/libs/leaflet.js +1 -1
  11. package/package.json +1 -1
  12. package/src/camera/setBearing.js +5 -0
  13. package/src/camera/setPitch.js +5 -0
  14. package/src/cog/initCog.js +2 -2
  15. package/src/core/gridlayerGetTiledPixelBounds.js +37 -4
  16. package/src/core/markerSetPos.js +1 -1
  17. package/src/core/popupGetEvents.js +1 -1
  18. package/src/core/popupUpdatePosition.js +8 -8
  19. package/src/core/tooltipGetEvents.js +1 -1
  20. package/src/core/tooltipSetPosition.js +2 -2
  21. package/src/geometry/appendNativeTriangle.js +2 -2
  22. package/src/geometry/createModelGeometry.js +2 -9
  23. package/src/geometry/dashedSegment.js +10 -5
  24. package/src/geometry/disk.js +1 -1
  25. package/src/geometry/fillNativePolygon.js +20 -0
  26. package/src/geometry/lineJoin.js +2 -2
  27. package/src/geometry/markerQuadCorners.js +6 -0
  28. package/src/geometry/nativeGeometry.js +1 -1
  29. package/src/geometry/polygon.js +4 -10
  30. package/src/geometry/segment.js +4 -3
  31. package/src/globals/colorTexture.js +6 -0
  32. package/src/globals/depthTexture.js +1 -0
  33. package/src/globals/drawMeshEntry.js +1 -1
  34. package/src/globals/meshPipeline.js +1 -0
  35. package/src/globals/mvtTextDecoder.js +1 -0
  36. package/src/globals/nativeScalableStyleKeys.js +1 -0
  37. package/src/globals/scenePass.js +1 -0
  38. package/src/globals/vectorStyleNamedColors.js +8 -0
  39. package/src/globals/vectorTileEvents.js +1 -0
  40. package/src/globals/workerCogSource.js +1 -0
  41. package/src/gpu/mountRenderer.js +4 -11
  42. package/src/gpu/releaseRenderer.js +1 -0
  43. package/src/index.js +6 -12
  44. package/src/materials/createProgramUniforms.js +1 -0
  45. package/src/models/loadModel.js +6 -10
  46. package/src/models/loadModelResources.js +2 -8
  47. package/src/models/modelNode.js +3 -7
  48. package/src/models/prepareModel.js +19 -0
  49. package/src/models/prepareModelGeometry.js +25 -0
  50. package/src/models/prepareModelPrimitive.js +17 -0
  51. package/src/mvt/addMvtCrossings.js +17 -0
  52. package/src/mvt/appendMvtBandTriangles.js +31 -0
  53. package/src/mvt/appendMvtCoordinates.js +11 -0
  54. package/src/mvt/appendMvtPolygonTriangles.js +19 -0
  55. package/src/mvt/beginMvtPath.js +9 -0
  56. package/src/mvt/closeMvtPath.js +7 -0
  57. package/src/mvt/collectMvtEdges.js +19 -0
  58. package/src/mvt/createMvtReader.js +7 -0
  59. package/src/mvt/decodeVectorTile.js +32 -0
  60. package/src/mvt/expectMvtWire.js +3 -0
  61. package/src/mvt/finishMvtPath.js +5 -0
  62. package/src/mvt/finiteMvtCoordinate.js +7 -0
  63. package/src/mvt/groupMvtPolygons.js +25 -0
  64. package/src/mvt/isMvtFallbackError.js +6 -0
  65. package/src/mvt/mergeMvtActiveEdges.js +9 -0
  66. package/src/mvt/mvtEdgeX.js +4 -0
  67. package/src/mvt/mvtPathsVertices.js +8 -0
  68. package/src/mvt/mvtPolygonPaths.js +12 -0
  69. package/src/mvt/numericMvtValue.js +4 -0
  70. package/src/mvt/readMvtFeature.js +34 -0
  71. package/src/mvt/readMvtField.js +8 -0
  72. package/src/mvt/readMvtGeometry.js +29 -0
  73. package/src/mvt/readMvtInteger.js +12 -0
  74. package/src/mvt/readMvtKnownValue.js +21 -0
  75. package/src/mvt/readMvtLayer.js +31 -0
  76. package/src/mvt/readMvtMessage.js +10 -0
  77. package/src/mvt/readMvtNumber.js +9 -0
  78. package/src/mvt/readMvtNumbers.js +11 -0
  79. package/src/mvt/readMvtString.js +6 -0
  80. package/src/mvt/readMvtValue.js +19 -0
  81. package/src/mvt/skipMvtField.js +14 -0
  82. package/src/mvt/sortMvtActiveEdges.js +15 -0
  83. package/src/mvt/sortedMvtLevels.js +22 -0
  84. package/src/mvt/takeMvtBytes.js +8 -0
  85. package/src/mvt/tessellateVectorRings.js +19 -0
  86. package/src/mvt/triangulateVectorRings.js +19 -0
  87. package/src/native/beforeNativeRender.js +6 -0
  88. package/src/native/canScaleNativeStyle.js +17 -0
  89. package/src/native/createNativeMesh.js +31 -0
  90. package/src/native/disposeNativeLayer.js +1 -1
  91. package/src/native/getGridTerrainTile.js +34 -0
  92. package/src/native/gridTerrainElevation.js +30 -0
  93. package/src/native/loadGridTerrain.js +26 -0
  94. package/src/native/nativeLayer.js +2 -2
  95. package/src/native/nativeLayerMethod.js +5 -14
  96. package/src/native/nativeProgram.js +20 -0
  97. package/src/native/nativeVectorGeometry.js +23 -0
  98. package/src/native/orderNativeGridLayer.js +14 -0
  99. package/src/native/putNativeMesh.js +15 -21
  100. package/src/native/refreshNativeEvent.js +13 -2
  101. package/src/native/refreshNativeLayer.js +1 -0
  102. package/src/native/syncGridLayer.js +48 -3
  103. package/src/native/syncNativeStrokeAnchors.js +12 -0
  104. package/src/native/syncNativeStrokeZoom.js +29 -0
  105. package/src/native/syncVectorLayer.js +15 -13
  106. package/src/native/terrainGridMesh.js +34 -0
  107. package/src/native/updateNativeStrokeRadius.js +9 -0
  108. package/src/projections/displayContainerPoint.js +23 -3
  109. package/src/projections/groundBounds.js +2 -2
  110. package/src/projections/localPixel.js +7 -2
  111. package/src/projections/localPixelForGeometry.js +4 -2
  112. package/src/projections/projectCameraPoint.js +6 -5
  113. package/src/rendering/collectVisibleMesh.js +5 -2
  114. package/src/rendering/drawSceneMeshes.js +3 -1
  115. package/src/rendering/nativeStrokeExpansion.js +4 -0
  116. package/src/rendering/prepareMeshUniforms.js +2 -0
  117. package/src/rendering/resizeRenderer.js +9 -0
  118. package/src/scene/initializerFor.js +3 -5
  119. package/src/scene/layerInitialize.js +14 -12
  120. package/src/scene/resolveNextInitialization.js +1 -1
  121. package/src/shaders/materialShaders.js +1 -0
  122. package/src/shaders/materialVertexShader.js +7 -2
  123. package/src/shaders/nativeStrokeShader.js +6 -0
  124. package/src/sources/attachAreaMesh.js +5 -6
  125. package/src/sources/buildArea.js +4 -5
  126. package/src/sources/buildAreaFeature.js +3 -19
  127. package/src/sources/initVectorTiles.js +15 -0
  128. package/src/sources/isVectorTileSource.js +4 -0
  129. package/src/sources/readyVectorTiles.js +10 -0
  130. package/src/sources/sourceAreaContext.js +1 -6
  131. package/src/sunlight/createSun.js +0 -1
  132. package/src/terrain/initTerrain.js +7 -1
  133. package/src/terrain/loadTerrain.js +4 -1
  134. package/src/terrain/mapElevationOf.js +5 -1
  135. package/src/terrain/onTerrainAdd.js +11 -1
  136. package/src/terrain/onTerrainRemove.js +10 -1
  137. package/src/utils/yieldTask.js +5 -0
  138. package/src/vector-style/appendQuotedStyleCharacter.js +22 -0
  139. package/src/vector-style/arrayContainsRange.js +8 -0
  140. package/src/vector-style/evaluateBoolean.js +18 -0
  141. package/src/vector-style/evaluateCoalesceOrCase.js +21 -0
  142. package/src/vector-style/evaluateComparison.js +16 -0
  143. package/src/vector-style/evaluateIExpression.js +8 -0
  144. package/src/vector-style/evaluateInput.js +6 -0
  145. package/src/vector-style/evaluateInterpolate.js +27 -0
  146. package/src/vector-style/evaluateMatch.js +12 -0
  147. package/src/vector-style/evaluateMath.js +14 -0
  148. package/src/vector-style/evaluateMembership.js +18 -0
  149. package/src/vector-style/evaluateNegation.js +13 -0
  150. package/src/vector-style/evaluatePrimitive.js +9 -0
  151. package/src/vector-style/evaluateStep.js +11 -0
  152. package/src/vector-style/evaluateStyle.js +33 -0
  153. package/src/vector-style/evaluateToExpression.js +14 -0
  154. package/src/vector-style/fillVectorPaint.js +17 -0
  155. package/src/vector-style/lineVectorPaint.js +21 -0
  156. package/src/vector-style/parseFunctionalColor.js +14 -0
  157. package/src/vector-style/parseHexColor.js +13 -0
  158. package/src/vector-style/parseVectorStyle.js +13 -0
  159. package/src/vector-style/readPaintValue.js +6 -0
  160. package/src/vector-style/removeTrailingCommas.js +12 -0
  161. package/src/vector-style/scaleDashArray.js +13 -0
  162. package/src/vector-style/skipStyleComment.js +9 -0
  163. package/src/vector-style/stripStyleComments.js +21 -0
  164. package/src/vector-style/styleColor.js +11 -0
  165. package/src/vector-style/validateVectorLayers.js +10 -0
  166. package/src/vector-style/vectorPaint.js +8 -0
  167. package/src/vector-style/visibleStyle.js +7 -0
  168. package/src/vector-tiles/appendVectorBatch.js +22 -0
  169. package/src/vector-tiles/attachVectorTileBatch.js +17 -0
  170. package/src/vector-tiles/attachVectorTileBatches.js +15 -0
  171. package/src/vector-tiles/compareVectorLabelPriority.js +3 -0
  172. package/src/vector-tiles/compareVectorTileDistance.js +3 -0
  173. package/src/vector-tiles/configureVectorTileLayer.js +28 -0
  174. package/src/vector-tiles/forwardVectorTileError.js +5 -0
  175. package/src/vector-tiles/loadVectorSprite.js +5 -0
  176. package/src/vector-tiles/loadVectorTile.js +36 -0
  177. package/src/vector-tiles/onVectorTileAdd.js +39 -0
  178. package/src/vector-tiles/onVectorTileMessage.js +10 -0
  179. package/src/vector-tiles/onVectorTileRemove.js +20 -0
  180. package/src/vector-tiles/onVectorTileWorkerError.js +11 -0
  181. package/src/vector-tiles/paintVectorTileBatch.js +12 -0
  182. package/src/vector-tiles/prepareVectorLabel.js +43 -0
  183. package/src/vector-tiles/prepareVectorStyleLayer.js +30 -0
  184. package/src/vector-tiles/prepareVectorTile.js +16 -0
  185. package/src/vector-tiles/projectVectorPaths.js +15 -0
  186. package/src/vector-tiles/projectVectorPoint.js +6 -0
  187. package/src/vector-tiles/projectVectorTriangles.js +18 -0
  188. package/src/vector-tiles/pruneVectorTiles.js +11 -0
  189. package/src/vector-tiles/pumpVectorTiles.js +18 -0
  190. package/src/vector-tiles/readTileJSON.js +17 -0
  191. package/src/vector-tiles/readVectorStyle.js +17 -0
  192. package/src/vector-tiles/refreshVectorTiles.js +5 -0
  193. package/src/vector-tiles/reserveVectorLabelCells.js +6 -0
  194. package/src/vector-tiles/retireVectorTileGroup.js +13 -0
  195. package/src/vector-tiles/revokeVectorWorkerUrl.js +9 -0
  196. package/src/vector-tiles/rotateVectorLabel.js +12 -0
  197. package/src/vector-tiles/rotateVectorLabels.js +6 -0
  198. package/src/vector-tiles/simplifyVectorPath.js +21 -0
  199. package/src/vector-tiles/updateVectorLabelMarker.js +27 -0
  200. package/src/vector-tiles/updateVectorTileLabels.js +28 -0
  201. package/src/vector-tiles/updateVectorTilePaint.js +20 -0
  202. package/src/vector-tiles/updateVectorTiles.js +29 -0
  203. package/src/vector-tiles/validateVectorTileZooms.js +6 -0
  204. package/src/vector-tiles/vectorFeaturePaint.js +15 -0
  205. package/src/vector-tiles/vectorFeaturePaths.js +16 -0
  206. package/src/vector-tiles/vectorFeatureZoom.js +8 -0
  207. package/src/vector-tiles/vectorLabelAnchors.js +24 -0
  208. package/src/vector-tiles/vectorLabelCandidates.js +11 -0
  209. package/src/vector-tiles/vectorLabelCells.js +26 -0
  210. package/src/vector-tiles/vectorLabelIcon.js +30 -0
  211. package/src/vector-tiles/vectorLabelProperty.js +3 -0
  212. package/src/vector-tiles/vectorSpriteIcon.js +11 -0
  213. package/src/vector-tiles/vectorStraightRun.js +14 -0
  214. package/src/vector-tiles/vectorStyleDependencies.js +15 -0
  215. package/src/vector-tiles/vectorStyleValue.js +5 -0
  216. package/src/vector-tiles/vectorStyleZoom.js +6 -0
  217. package/src/vector-tiles/vectorTileAwaited.js +9 -0
  218. package/src/vector-tiles/vectorTileOverlaps.js +4 -0
  219. package/src/vector-tiles/vectorTilePaint.js +10 -0
  220. package/src/vector-tiles/vectorTileSettings.js +17 -0
  221. package/src/vector-tiles/vectorTilesIdle.js +3 -0
  222. package/src/vector-tiles/vectorTolerance.js +4 -0
  223. package/src/vector-tiles/wrapVectorLabel.js +11 -0
  224. package/src/weakmaps/nativeProgramsByRenderer.js +1 -0
  225. package/src/weakmaps/streamTerrainByLayer.js +1 -0
  226. package/src/weakmaps/streamTerrainByMap.js +1 -0
  227. package/src/weakmaps/vectorTileOptionsByLayer.js +1 -0
  228. package/src/weakmaps/vectorWorkerByLayer.js +1 -0
  229. package/src/weakmaps/workerMapByLayer.js +1 -0
  230. package/src/workers/buildAreaInWorker.js +3 -27
  231. package/src/workers/cogWorker.js +21 -0
  232. package/src/workers/createGeometryWorker.js +1 -0
  233. package/src/workers/geometryWorker.js +15 -0
  234. package/src/workers/getCogInWorker.js +7 -0
  235. package/src/workers/modelWorker.js +10 -0
  236. package/src/workers/openCogInWorker.js +14 -0
  237. package/src/workers/runSourceWorker.js +32 -0
  238. package/src/workers/settleGeometryWorker.js +1 -1
  239. package/src/workers/vectorTileWorker.js +10 -0
  240. package/src/geometry/buildExtrudedPolygon.js +0 -10
  241. package/src/sources/buildAreaPoint.js +0 -7
@@ -0,0 +1,17 @@
1
+ import { L } from '../globals/L.js';
2
+ import { nativeScalableStyleKeys } from '../globals/nativeScalableStyleKeys.js';
3
+
4
+ export function canScaleNativeStyle(name, state, args) {
5
+ const { source } = state;
6
+ if (name !== 'setStyle' || source.options.interactive !== false || state.dirty && !state.zoomOnly) return false;
7
+ const stroke = state.strokeCoordinates && state.meshes.get('stroke')?.program.uniforms.strokeScale;
8
+ const fill = source instanceof L.Polygon && !source.options.stroke && state.meshes.has('fill');
9
+ if (!stroke && !fill) return false;
10
+ let key;
11
+ if (args[0]) {
12
+ for (key of Object.keys(args[0])) {
13
+ if (!nativeScalableStyleKeys.has(key)) return false;
14
+ }
15
+ }
16
+ return true;
17
+ }
@@ -0,0 +1,31 @@
1
+ import { L } from '../globals/L.js';
2
+ import { disposeMesh } from '../rendering/disposeMesh.js';
3
+ import { createMesh } from '../rendering/createMesh.js';
4
+ import { nativeProgram } from './nativeProgram.js';
5
+
6
+ export function createNativeMesh(state, key, position, color, opacity, texture, uv, terrain, nativeStroke) {
7
+ const { backend: layer, source, meshes } = state;
8
+ // Unlit, noninteractive strokes use the normal slot for stationary anchors.
9
+ const normal = nativeStroke ? state.strokeAnchors : /* alloc */ [];
10
+ let index, mesh = meshes.get(key);
11
+ if (!nativeStroke) {
12
+ for (index = 0; index < position.length; index++) normal.push(index % 3 === 1 ? 1 : 0);
13
+ }
14
+ const data = /* alloc */ { position, normal };
15
+ if (uv) data.uv = uv;
16
+ const material = /* alloc */ {
17
+ vertexColors: false, color, opacity, unlit: true, alphaMode: 'BLEND', depthWrite: terrain, nativeStroke,
18
+ };
19
+ if (texture) material.baseTexture = texture;
20
+ if (mesh) disposeMesh(layer, mesh);
21
+ mesh = createMesh(layer, data, material, 0, source instanceof L.GridLayer
22
+ ? (terrain ? state.terrainRasterProgram : state.rasterProgram) || nativeProgram(layer, material)
23
+ : nativeProgram(layer, material));
24
+ if (source instanceof L.GridLayer) {
25
+ if (terrain) state.terrainRasterProgram ||= mesh.program;
26
+ else state.rasterProgram ||= mesh.program;
27
+ mesh.texture = texture;
28
+ }
29
+ meshes.set(key, mesh);
30
+ return mesh;
31
+ }
@@ -17,7 +17,7 @@ export function disposeNativeLayer() {
17
17
  const { source, map, backend: layer, meshes, textures, images } = state;
18
18
  const events = 'load tileload tileunload move drag';
19
19
  source.off(events, refreshNativeEvent, state);
20
- map.off('zoomend', refreshNativeEvent, state);
20
+ map.off('zoomend terrainchange', refreshNativeEvent, state);
21
21
  if (state.marker) map.off('move zoom rotate pitch resize viewreset', updateNativeLayerView, state);
22
22
  removeNativeEvents(state);
23
23
  let name, methods, mesh, texture, image;
@@ -0,0 +1,34 @@
1
+ export async function getGridTerrainTile(state, z, x, y) {
2
+ const limit = 2 ** z;
3
+ x = ((x % limit) + limit) % limit;
4
+ y = Math.max(0, Math.min(limit - 1, y));
5
+ const key = `${z}/${x}/${y}`;
6
+ let entry = state.terrainCache.get(key);
7
+ if (!entry) {
8
+ entry = /* alloc */ { promise: state.layer.options.getTile(z, x, y, state.terrainController.signal) };
9
+ state.terrainCache.set(key, entry);
10
+ } else {
11
+ state.terrainCache.delete(key);
12
+ state.terrainCache.set(key, entry);
13
+ }
14
+ try {
15
+ if (!entry.data) entry.data = await entry.promise;
16
+ if (!(entry.data instanceof Float32Array) || entry.data.length !== 65536) {
17
+ throw /* alloc */ new Error(`Invalid elevation tile ${key}: expected 256x256 Float32Array`);
18
+ }
19
+ if (!entry.range) {
20
+ let minimum = Infinity, maximum = -Infinity, elevation;
21
+ for (elevation of entry.data) {
22
+ if (!Number.isFinite(elevation) || elevation <= -1000) continue;
23
+ minimum = Math.min(minimum, elevation);
24
+ maximum = Math.max(maximum, elevation);
25
+ }
26
+ entry.range = /* alloc */ [minimum, maximum];
27
+ }
28
+ } catch (error) {
29
+ if (state.terrainCache.get(key) === entry) state.terrainCache.delete(key);
30
+ throw error;
31
+ }
32
+ while (state.terrainCache.size > 256) state.terrainCache.delete(state.terrainCache.keys().next().value);
33
+ return entry.data;
34
+ }
@@ -0,0 +1,30 @@
1
+ import { terrainElevation } from '../terrain/terrainElevation.js';
2
+ import { nativeGPUByLayer } from '../weakmaps/nativeGPUByLayer.js';
3
+
4
+ export function gridTerrainElevation(state, location) {
5
+ let z = Math.max(0, Math.min(24, Math.round(state.map.getZoom())));
6
+ const tiles = /* alloc */ new Map();
7
+ let point, limit, x, y, dx, dy, tx, ty, entry, elevation, layer, native, request;
8
+ for (; z >= 0; z--) {
9
+ point = state.map.project(location, z), limit = 2 ** z;
10
+ x = Math.floor(point.x / 256), y = Math.floor(point.y / 256);
11
+ for (layer of Object.values(state.map._layers)) {
12
+ native = nativeGPUByLayer.get(layer);
13
+ if (native?.disposed || native?.terrainStream !== state) continue;
14
+ request = native.terrainRequests?.get(`${x}:${y}:${z}`);
15
+ if (!request?.tiles || layer._tiles[`${x}:${y}:${z}`] !== request.tile) continue;
16
+ elevation = terrainElevation(point, request.tiles);
17
+ if (Number.isFinite(elevation)) return elevation;
18
+ }
19
+ tiles.clear();
20
+ for (dy = -1; dy <= 1; dy++) for (dx = -1; dx <= 1; dx++) {
21
+ tx = ((x + dx) % limit + limit) % limit;
22
+ ty = Math.max(0, Math.min(limit - 1, y + dy));
23
+ entry = state.terrainCache.get(`${z}/${tx}/${ty}`);
24
+ if (entry?.data) tiles.set(`${x + dx}/${y + dy}`, entry.data);
25
+ }
26
+ elevation = terrainElevation(point, tiles);
27
+ if (Number.isFinite(elevation)) return elevation;
28
+ }
29
+ return null;
30
+ }
@@ -0,0 +1,26 @@
1
+ import { getGridTerrainTile } from './getGridTerrainTile.js';
2
+ import { refreshNativeLayer } from './refreshNativeLayer.js';
3
+
4
+ export async function loadGridTerrain(state, key, tile, request) {
5
+ const { x, y, z } = tile.coords, stream = request.stream;
6
+ const jobs = /* alloc */ [], tiles = /* alloc */ new Map();
7
+ let dx, dy, i = 0;
8
+ try {
9
+ for (dy = -1; dy <= 1; dy++) for (dx = -1; dx <= 1; dx++) {
10
+ jobs.push(getGridTerrainTile(stream, z, x + dx, y + dy));
11
+ }
12
+ const values = await Promise.all(jobs);
13
+ if (state.disposed || stream.terrainController.signal.aborted ||
14
+ state.source._tiles[key] !== tile || state.terrainRequests.get(key) !== request) return;
15
+ for (dy = -1; dy <= 1; dy++) for (dx = -1; dx <= 1; dx++) tiles.set(`${x + dx}/${y + dy}`, values[i++]);
16
+ request.tiles = tiles;
17
+ refreshNativeLayer(state);
18
+ state.map.fire('terrainchange');
19
+ state.source._update();
20
+ } catch (error) {
21
+ if (!state.disposed && !stream.terrainController.signal.aborted && state.source._tiles[key] === tile) {
22
+ request.error = error;
23
+ stream.layer.fire('error', /* alloc */ { error, coords: tile.coords });
24
+ }
25
+ }
26
+ }
@@ -25,7 +25,7 @@ import { synchronizeNativeLayer } from './synchronizeNativeLayer.js';
25
25
  export async function nativeLayer(state) {
26
26
  const { source, map } = state;
27
27
  const layer = state.backend = createLayer(/* alloc */ {
28
- source: /* alloc */ { type: '3d' },
28
+ source: /* alloc */ { type: 'vector' },
29
29
  });
30
30
  state.meshes = /* alloc */ new Map();
31
31
  state.textures = /* alloc */ new Map();
@@ -43,7 +43,7 @@ export async function nativeLayer(state) {
43
43
  state.disposed = false;
44
44
  const currentEvents = 'load tileload tileunload move drag';
45
45
  source.on(currentEvents, refreshNativeEvent, state);
46
- map.on('zoomend', refreshNativeEvent, state);
46
+ map.on('zoomend terrainchange', refreshNativeEvent, state);
47
47
  if (state.marker) map.on('move zoom rotate pitch resize viewreset', updateNativeLayerView, state);
48
48
  nativeEvents(state);
49
49
  let name, own, original;
@@ -3,25 +3,15 @@ import { nativeGPUBackByMap } from '../weakmaps/nativeGPUBackByMap.js';
3
3
  import { nativeGPUOrderByMap } from '../weakmaps/nativeGPUOrderByMap.js';
4
4
 
5
5
 
6
- import { L } from '../globals/L.js';
7
-
8
-
9
6
  import { refreshNativeLayer } from './refreshNativeLayer.js';
7
+ import { canScaleNativeStyle } from './canScaleNativeStyle.js';
8
+ import { orderNativeGridLayer } from './orderNativeGridLayer.js';
10
9
 
11
10
  export function nativeLayerMethod(name, source, args) {
12
11
  const state = nativeGPUByLayer.get(source), original = state.methods.get(name).original;
12
+ const scalable = canScaleNativeStyle(name, state, args);
13
13
  const result = original.apply(source, args);
14
- let layer, zIndex;
15
- if ((name === 'bringToFront' || name === 'bringToBack') && source instanceof L.GridLayer) {
16
- let extreme = name === 'bringToFront' ? -Infinity : Infinity;
17
- for (layer of Object.values(state.map._layers)) {
18
- if (layer === source || !(layer instanceof L.GridLayer) || layer.getPane() !== source.getPane()) continue;
19
- zIndex = layer.options.zIndex || 0;
20
- extreme = name === 'bringToFront' ? Math.max(extreme, zIndex) : Math.min(extreme, zIndex);
21
- }
22
- const edge = name === 'bringToFront' ? extreme + 1 : extreme - 1;
23
- if (Number.isFinite(edge)) source.setZIndex(edge);
24
- }
14
+ orderNativeGridLayer(state, name);
25
15
  if (name === 'bringToFront') {
26
16
  state.order = nativeGPUOrderByMap.get(state.map) + 1;
27
17
  nativeGPUOrderByMap.set(state.map, state.order);
@@ -31,5 +21,6 @@ export function nativeLayerMethod(name, source, args) {
31
21
  nativeGPUBackByMap.set(state.map, state.order);
32
22
  }
33
23
  refreshNativeLayer(state);
24
+ if (scalable && (!source.options.stroke || Number(source.options.weight) > 0)) state.zoomOnly = true;
34
25
  return result;
35
26
  }
@@ -0,0 +1,20 @@
1
+ import { rendererByLayer } from '../weakmaps/rendererByLayer.js';
2
+ import { nativeProgramsByRenderer } from '../weakmaps/nativeProgramsByRenderer.js';
3
+ import { createProgram } from '../materials/createProgram.js';
4
+ import { createProgramUniforms } from '../materials/createProgramUniforms.js';
5
+
6
+ export function nativeProgram(layer, material) {
7
+ const renderer = rendererByLayer.get(layer);
8
+ let programs = nativeProgramsByRenderer.get(renderer);
9
+ if (!programs) {
10
+ programs = /* alloc */ new Map();
11
+ nativeProgramsByRenderer.set(renderer, programs);
12
+ }
13
+ const key = `${Boolean(material.baseTexture)}:${Boolean(material.nativeStroke)}:${Boolean(material.depthWrite)}`;
14
+ let template = programs.get(key);
15
+ if (!template) {
16
+ template = createProgram(layer, /* alloc */ { doubleSided: true, ...material });
17
+ programs.set(key, template);
18
+ }
19
+ return /* alloc */ { ...template, uniforms: createProgramUniforms(material), _layerMaterial: material };
20
+ }
@@ -0,0 +1,23 @@
1
+ import { geometryPixelStates } from '../globals/geometryPixelStates.js';
2
+ import { nativeGeometry } from '../geometry/nativeGeometry.js';
3
+ import { localPixelForGeometry } from '../projections/localPixelForGeometry.js';
4
+ import { locationByLayer } from '../weakmaps/locationByLayer.js';
5
+ import { radians } from '../globals/radians.js';
6
+ import { L } from '../globals/L.js';
7
+
8
+ export function nativeVectorGeometry(state) {
9
+ const { source } = state;
10
+ state.strokeCoordinates = null;
11
+ state.strokeAnchors = source instanceof L.Polyline && !source.options.dashArray ? /* alloc */ [] : null;
12
+ const crs = state.map.options.crs, origin = locationByLayer.get(state.backend);
13
+ state.pixelProjection = /* alloc */ {
14
+ transformation: crs.transformation, scale: crs.scale(state.map.getZoom()),
15
+ origin: crs.project(origin), metres: Math.cos(origin.lat * radians),
16
+ };
17
+ geometryPixelStates.push(state);
18
+ try {
19
+ return nativeGeometry(source, state.map, localPixelForGeometry);
20
+ } finally {
21
+ geometryPixelStates.pop();
22
+ }
23
+ }
@@ -0,0 +1,14 @@
1
+ import { L } from '../globals/L.js';
2
+
3
+ export function orderNativeGridLayer(state, name) {
4
+ const { source } = state;
5
+ if (!(source instanceof L.GridLayer) || name !== 'bringToFront' && name !== 'bringToBack') return;
6
+ let extreme = name === 'bringToFront' ? -Infinity : Infinity, layer, zIndex;
7
+ for (layer of Object.values(state.map._layers)) {
8
+ if (layer === source || !(layer instanceof L.GridLayer) || layer.getPane() !== source.getPane()) continue;
9
+ zIndex = layer.options.zIndex || 0;
10
+ extreme = name === 'bringToFront' ? Math.max(extreme, zIndex) : Math.min(extreme, zIndex);
11
+ }
12
+ const edge = name === 'bringToFront' ? extreme + 1 : extreme - 1;
13
+ if (Number.isFinite(edge)) source.setZIndex(edge);
14
+ }
@@ -1,36 +1,30 @@
1
1
  import { L } from '../globals/L.js';
2
- import { disposeMesh } from '../rendering/disposeMesh.js';
3
- import { createMesh } from '../rendering/createMesh.js';
4
2
  import { styleNativeMesh } from './styleNativeMesh.js';
3
+ import { createNativeMesh } from './createNativeMesh.js';
4
+ import { updateNativeStrokeRadius } from './updateNativeStrokeRadius.js';
5
5
 
6
- export function putNativeMesh(state, key, position, color, opacity, texture, uv) {
7
- const { backend: layer, source, meshes } = state;
6
+ export function putNativeMesh(state, key, position, color, opacity, texture, uv, terrain = false) {
7
+ const { source, meshes } = state;
8
8
  if (!position.length) return;
9
+ const nativeStroke = key === 'stroke' && Boolean(state.strokeCoordinates) && source.options.interactive === false;
9
10
  let mesh = meshes.get(key);
10
11
  if (!mesh || mesh.geometry.attributes.position.data.length !== position.length ||
11
- mesh.program.uniforms.baseTexture?.value !== texture) {
12
- const normal = /* alloc */ [];
13
- let index;
14
- for (index = 0; index < position.length; index++) normal.push(index % 3 === 1 ? 1 : 0);
15
- const data = /* alloc */ { position, normal };
16
- if (uv) data.uv = uv;
17
- const material = /* alloc */ {
18
- vertexColors: false, color, opacity, unlit: true, alphaMode: 'BLEND',
19
- };
20
- if (texture) material.baseTexture = texture;
21
- if (mesh) disposeMesh(layer, mesh);
22
- mesh = createMesh(layer, data, material, 0, source instanceof L.GridLayer ? state.rasterProgram : undefined);
23
- if (source instanceof L.GridLayer) {
24
- state.rasterProgram ||= mesh.program;
25
- mesh.texture = texture;
26
- }
27
- meshes.set(key, mesh);
12
+ (source instanceof L.GridLayer && mesh.program.depthWrite !== terrain) ||
13
+ mesh.program.uniforms.baseTexture?.value !== texture ||
14
+ Boolean(mesh.program._layerMaterial.nativeStroke) !== nativeStroke) {
15
+ mesh = createNativeMesh(state, key, position, color, opacity, texture, uv, terrain, nativeStroke);
28
16
  } else {
29
17
  const attribute = mesh.geometry.attributes.position;
30
18
  attribute.data.set(position);
31
19
  attribute.needsUpdate = true;
32
20
  mesh.geometry.bounds = null;
21
+ if (nativeStroke) {
22
+ mesh.geometry.attributes.normal.data.set(state.strokeAnchors);
23
+ mesh.geometry.attributes.normal.needsUpdate = true;
24
+ }
33
25
  }
26
+ if (nativeStroke) updateNativeStrokeRadius(mesh);
27
+ if (mesh.program.uniforms.strokeScale) mesh.program.uniforms.strokeScale.value = 1;
34
28
  styleNativeMesh(state, mesh, color, opacity);
35
29
  return mesh;
36
30
  }
@@ -1,5 +1,16 @@
1
1
  import { refreshNativeLayer } from './refreshNativeLayer.js';
2
+ import { L } from '../globals/L.js';
3
+ import { update } from '../scene/scheduleSceneUpdate.js';
2
4
 
3
- export function refreshNativeEvent() {
4
- refreshNativeLayer(this);
5
+ export function refreshNativeEvent(event) {
6
+ if (event?.type === 'zoomend' && this.source instanceof L.Polyline &&
7
+ (!this.source.options.stroke || !(Number(this.source.options.weight) > 0))) return;
8
+ if (event?.type === 'zoomend' && this.strokeCoordinates && (!this.dirty || this.zoomOnly)) {
9
+ this.zoomOnly = true;
10
+ this.dirty = true;
11
+ update(this.backend);
12
+ return;
13
+ }
14
+ if (event?.type === 'terrainchange' && this.marker) this.source.update();
15
+ refreshNativeLayer(this);
5
16
  }
@@ -10,6 +10,7 @@ import { update } from '../scene/scheduleSceneUpdate.js';
10
10
  import { hideTargets } from './hideTargets.js';
11
11
 
12
12
  export function refreshNativeLayer(state = this) {
13
+ state.zoomOnly = false;
13
14
  state.dirty = true;
14
15
  state.viewDirty = true;
15
16
  state.failed = false;
@@ -1,22 +1,58 @@
1
1
  import { L } from '../globals/L.js';
2
2
  import { coreWhite } from '../globals/coreWhite.js';
3
+ import { streamTerrainByMap } from '../weakmaps/streamTerrainByMap.js';
3
4
 
4
5
  import { styleNativeMesh } from './styleNativeMesh.js';
5
6
  import { quadNativeMesh } from './quadNativeMesh.js';
6
7
  import { boundsQuad } from '../geometry/boundsQuad.js';
8
+ import { loadGridTerrain } from './loadGridTerrain.js';
9
+ import { terrainGridMesh } from './terrainGridMesh.js';
7
10
 
8
11
 
9
12
  export function syncGridLayer(state) {
10
13
  const { source, textures, meshes } = state;
11
- let key, tile, opacity, retained, mesh, nw, se;
14
+ const stream = streamTerrainByMap.get(state.map);
15
+ if (state.terrainStream !== stream) {
16
+ state.terrainStream = stream;
17
+ state.terrainRequests = stream ? /* alloc */ new Map() : null;
18
+ }
19
+ let key, tile, opacity, retained, mesh, nw, se, request, pending = false;
20
+ if (state.terrainRequests) {
21
+ for (key of state.terrainRequests.keys()) {
22
+ if (!source._tiles[key]) state.terrainRequests.delete(key);
23
+ }
24
+ }
12
25
  for (key in source._tiles) {
13
26
  if (!Object.hasOwn(source._tiles, key)) continue;
14
27
  tile = source._tiles[key];
15
- if (!tile.loaded || tile.el instanceof HTMLImageElement && !tile.el.naturalWidth) continue;
28
+ if (!tile.loaded || tile.el instanceof HTMLImageElement && !tile.el.naturalWidth) {
29
+ if (stream) pending = true;
30
+ continue;
31
+ }
16
32
  opacity = (source.options.opacity ?? 1) * (tile._gpuOpacity ?? 1);
17
33
  retained = textures.get(key);
18
34
  mesh = meshes.get(key);
19
- if (mesh && retained?.image === tile.el && retained.url === (tile.el.currentSrc || tile.el.src)) {
35
+ if (state.terrainRequests) {
36
+ request = state.terrainRequests.get(key);
37
+ if (!request || request.tile !== tile) {
38
+ request = /* alloc */ { tile, stream };
39
+ state.terrainRequests.set(key, request);
40
+ loadGridTerrain(state, key, tile, request);
41
+ }
42
+ if (!request.tiles) {
43
+ pending = true;
44
+ if (mesh && retained?.image === tile.el) {
45
+ retained.used = true;
46
+ styleNativeMesh(state, mesh, coreWhite, opacity);
47
+ }
48
+ continue;
49
+ }
50
+ if (mesh && mesh._terrainRequest === request && retained?.image === tile.el &&
51
+ retained.url === (tile.el.currentSrc || tile.el.src)) {
52
+ retained.used = true;
53
+ styleNativeMesh(state, mesh, coreWhite, opacity);
54
+ } else mesh = terrainGridMesh(state, key, tile, request, opacity);
55
+ } else if (mesh && !mesh._terrainRequest && retained?.image === tile.el && retained.url === (tile.el.currentSrc || tile.el.src)) {
20
56
  retained.used = true;
21
57
  styleNativeMesh(state, mesh, coreWhite, opacity);
22
58
  } else {
@@ -26,4 +62,13 @@ export function syncGridLayer(state) {
26
62
  }
27
63
  if (mesh) mesh.renderOrder += tile.coords.z / 100;
28
64
  }
65
+ if (stream && (pending || !Object.keys(source._tiles).length)) {
66
+ for ([key, mesh] of meshes) {
67
+ if (source._tiles[key] || !mesh._terrainRequest) continue;
68
+ retained = textures.get(key);
69
+ if (!retained) continue;
70
+ retained.used = true;
71
+ mesh._nativeUsed = true;
72
+ }
73
+ }
29
74
  }
@@ -0,0 +1,12 @@
1
+ export function syncNativeStrokeAnchors(state, stroke) {
2
+ const { source } = state;
3
+ if (state.strokeAnchors?.length === stroke.length && stroke.length) {
4
+ if (source.options.interactive === false) state.strokeCoordinates = stroke;
5
+ else {
6
+ state.strokeCoordinates = /* alloc */ new Float64Array(stroke);
7
+ state.strokeAnchors = /* alloc */ new Float64Array(state.strokeAnchors);
8
+ }
9
+ state.strokeZoom = state.map.getZoom();
10
+ state.strokeWeight = Number(source.options.weight);
11
+ } else state.strokeAnchors = null;
12
+ }
@@ -0,0 +1,29 @@
1
+ import { styleNativeMesh } from './styleNativeMesh.js';
2
+ import { parseColor } from '../colors/parseColor.js';
3
+
4
+ export function syncNativeStrokeZoom(state) {
5
+ const { source, meshes, strokeCoordinates, strokeAnchors } = state;
6
+ const mesh = meshes.get('stroke');
7
+ let i, r, g, b, fill;
8
+ if (mesh) {
9
+ const factor = 2 ** (state.strokeZoom - state.map.getZoom()) *
10
+ Number(source.options.weight) / state.strokeWeight;
11
+ const attribute = mesh.geometry.attributes.position;
12
+ if (mesh.program.uniforms.strokeScale) {
13
+ mesh.program.uniforms.strokeScale.value = factor;
14
+ } else {
15
+ for (i = 0; i < strokeCoordinates.length; i++) {
16
+ attribute.data[i] = strokeAnchors[i] + (strokeCoordinates[i] - strokeAnchors[i]) * factor;
17
+ }
18
+ attribute.needsUpdate = true;
19
+ mesh.geometry.bounds = null;
20
+ }
21
+ ({ r, g, b } = parseColor(source.options.color));
22
+ styleNativeMesh(state, mesh, /* alloc */ [r, g, b], source.options.opacity ?? 1);
23
+ }
24
+ fill = meshes.get('fill');
25
+ if (fill) {
26
+ ({ r, g, b } = parseColor(source.options.fillColor || source.options.color));
27
+ styleNativeMesh(state, fill, /* alloc */ [r, g, b], source.options.fillOpacity ?? 1);
28
+ }
29
+ }
@@ -1,22 +1,19 @@
1
- import { geometryPixelStates } from '../globals/geometryPixelStates.js';
2
-
3
-
4
- import { nativeGeometry } from '../geometry/nativeGeometry.js';
5
- import { localPixelForGeometry } from '../projections/localPixelForGeometry.js';
6
1
  import { parseColor } from '../colors/parseColor.js';
7
2
  import { putNativeMesh } from './putNativeMesh.js';
3
+ import { syncNativeStrokeZoom } from './syncNativeStrokeZoom.js';
4
+ import { nativeVectorGeometry } from './nativeVectorGeometry.js';
5
+ import { syncNativeStrokeAnchors } from './syncNativeStrokeAnchors.js';
8
6
 
9
7
  export function syncVectorLayer(state) {
10
8
  const { source } = state;
11
- geometryPixelStates.push(state);
12
- let geometry;
13
- try {
14
- geometry = nativeGeometry(source, state.map, localPixelForGeometry);
15
- } finally {
16
- geometryPixelStates.pop();
9
+ if (state.zoomOnly) {
10
+ syncNativeStrokeZoom(state);
11
+ state.zoomOnly = false;
12
+ return;
17
13
  }
18
- const { fill, stroke } = geometry;
19
- let i, key, positions, color, opacity, r, g, b;
14
+ const { fill, stroke } = nativeVectorGeometry(state);
15
+ syncNativeStrokeAnchors(state, stroke);
16
+ let i, key, positions, color, opacity, r, g, b, attributes;
20
17
  for (i = 0; i < 2; i++) {
21
18
  key = i ? 'stroke' : 'fill';
22
19
  positions = i ? stroke : fill;
@@ -25,5 +22,10 @@ export function syncVectorLayer(state) {
25
22
  if (!positions.length) continue;
26
23
  ({ r, g, b } = parseColor(color));
27
24
  putNativeMesh(state, key, positions, /* alloc */ [r, g, b], opacity ?? 1);
25
+ if (key === 'stroke' && source.options.interactive === false && state.strokeCoordinates) {
26
+ attributes = state.meshes.get(key).geometry.attributes;
27
+ state.strokeCoordinates = attributes.position.data;
28
+ state.strokeAnchors = attributes.normal.data;
29
+ }
28
30
  }
29
31
  }
@@ -0,0 +1,34 @@
1
+ import { rootByLayer } from '../weakmaps/rootByLayer.js';
2
+ import { coreWhite } from '../globals/coreWhite.js';
3
+ import { textureFor } from '../textures/nativeTextureFor.js';
4
+ import { terrainElevation } from '../terrain/terrainElevation.js';
5
+ import { localPosition } from '../projections/localPosition.js';
6
+ import { putNativeMesh } from './putNativeMesh.js';
7
+
8
+ export function terrainGridMesh(state, key, tile, request, opacity) {
9
+ const texture = textureFor(state, key, tile.el);
10
+ if (!texture) return;
11
+ const { x, y, z } = tile.coords, divisions = 32;
12
+ const northwest = localPosition(state, state.map.unproject(/* alloc */ [x * 256, y * 256], z));
13
+ const southeast = localPosition(state, state.map.unproject(/* alloc */ [(x + 1) * 256, (y + 1) * 256], z));
14
+ const vertices = /* alloc */ [], position = /* alloc */ [], uv = /* alloc */ [];
15
+ const base = rootByLayer.get(state.backend).position[1];
16
+ let row, col, u, v, elevation, a, b, c, d, index;
17
+ for (row = 0; row <= divisions; row++) for (col = 0; col <= divisions; col++) {
18
+ u = col / divisions; v = row / divisions;
19
+ elevation = terrainElevation(/* alloc */ { x: (x + u) * 256, y: (y + v) * 256 }, request.tiles);
20
+ if (!Number.isFinite(elevation)) throw /* alloc */ new Error(`Missing elevation sample in ${z}/${x}/${y}`);
21
+ vertices.push(/* alloc */ [northwest[0] + (southeast[0] - northwest[0]) * u, elevation - base,
22
+ northwest[2] + (southeast[2] - northwest[2]) * v, u, v]);
23
+ }
24
+ for (row = 0; row < divisions; row++) for (col = 0; col < divisions; col++) {
25
+ a = row * (divisions + 1) + col; b = a + 1; c = a + divisions + 1; d = c + 1;
26
+ for (index of /* alloc */ [a, c, b, b, c, d]) {
27
+ position.push(vertices[index][0], vertices[index][1], vertices[index][2]);
28
+ uv.push(vertices[index][3], vertices[index][4]);
29
+ }
30
+ }
31
+ const mesh = putNativeMesh(state, key, position, coreWhite, opacity, texture, uv, true);
32
+ mesh._terrainRequest = request;
33
+ return mesh;
34
+ }
@@ -0,0 +1,9 @@
1
+ export function updateNativeStrokeRadius(mesh) {
2
+ const attributes = mesh.geometry.attributes, positions = attributes.position.data, anchors = attributes.normal.data;
3
+ let i, radius = 0;
4
+ for (i = 0; i < positions.length; i += 3) {
5
+ radius = Math.max(radius, Math.hypot(positions[i] - anchors[i],
6
+ positions[i + 1] - anchors[i + 1], positions[i + 2] - anchors[i + 2]));
7
+ }
8
+ mesh.geometry.nativeStrokeRadius = radius;
9
+ }
@@ -1,3 +1,23 @@
1
- export function displayPoint(map, point) {
2
- return map.layerPointToContainerPoint(point).subtract(map._getMapPanePos());
3
- }
1
+ import { streamTerrainByMap } from '../weakmaps/streamTerrainByMap.js';
2
+ import { renderers } from '../weakmaps/renderers.js';
3
+ import { sceneOriginByLayer } from '../weakmaps/sceneOriginByLayer.js';
4
+ import { cameraViewByMap } from '../weakmaps/cameraViewByMap.js';
5
+ import { mapMethods } from '../globals/mapMethods.js';
6
+ import { radians } from '../globals/radians.js';
7
+ import { earthCirc } from '../globals/earthCirc.js';
8
+ import { mapElevationOf } from '../terrain/mapElevationOf.js';
9
+ import { animationPoint } from './transformZoomAnimationPoint.js';
10
+ import { project } from './projectCameraPoint.js';
11
+
12
+ export function displayPoint(map, point, location) {
13
+ const shared = renderers.get(map), origin = shared && sceneOriginByLayer.get(shared);
14
+ if (!streamTerrainByMap.get(map) || !origin) {
15
+ return map.layerPointToContainerPoint(point).subtract(map._getMapPanePos());
16
+ }
17
+ const elevation = mapElevationOf(map, location || map.layerPointToLatLng(point)) ?? 0;
18
+ const center = mapElevationOf(map, map.getCenter()) ?? 0;
19
+ const zoom = cameraViewByMap.get(map)?.zoom ?? map.getZoom();
20
+ const height = (elevation - center) * map.options.crs.scale(zoom) / (earthCirc * Math.cos(origin.lat * radians));
21
+ const raw = animationPoint(map, mapMethods.value.layerPointToContainerPoint.call(map, point));
22
+ return project(map, raw, false, false, height).subtract(map._getMapPanePos());
23
+ }
@@ -4,7 +4,7 @@ import { pointOf } from './toPoint.js';
4
4
  import { project } from './projectCameraPoint.js';
5
5
 
6
6
 
7
- export function groundBounds(map, padding = cameraZeroPadding) {
7
+ export function groundBounds(map, padding = cameraZeroPadding, height = 0) {
8
8
  const min = pointOf(padding).divideBy(2), max = map.getSize().subtract(min);
9
9
  const bounds = /* alloc */ new L.Bounds();
10
10
  const minX = min.x, minY = min.y;
@@ -12,7 +12,7 @@ export function groundBounds(map, padding = cameraZeroPadding) {
12
12
  for (i = 0; i < 4; i++) {
13
13
  min.x = i % 2 ? max.x : minX;
14
14
  min.y = i < 2 ? minY : max.y;
15
- bounds.extend(project(map, min, true, true));
15
+ bounds.extend(project(map, min, true, true, height));
16
16
  }
17
17
  return bounds;
18
18
  }
@@ -1,5 +1,10 @@
1
1
  import { localPosition } from './localPosition.js';
2
2
 
3
- export function localPixel(state, point) {
4
- return localPosition(state, state.map.unproject(point));
3
+ export function localPixel(state, point) {
4
+ if (state.pixelProjection) {
5
+ const { transformation, scale, origin, metres } = state.pixelProjection;
6
+ const projected = transformation.untransform(point, scale);
7
+ return /* alloc */ [(projected.x - origin.x) * metres, 0, -(projected.y - origin.y) * metres];
8
+ }
9
+ return localPosition(state, state.map.unproject(point));
5
10
  }
@@ -2,6 +2,8 @@ import { geometryPixelStates } from '../globals/geometryPixelStates.js';
2
2
 
3
3
  import { localPixel } from './localPixel.js';
4
4
 
5
- export function localPixelForGeometry(point) {
6
- return localPixel(geometryPixelStates[geometryPixelStates.length - 1], point);
5
+ export function localPixelForGeometry(point, anchor) {
6
+ const state = geometryPixelStates[geometryPixelStates.length - 1];
7
+ if (anchor && state.strokeAnchors) state.strokeAnchors.push(...localPixel(state, anchor));
8
+ return localPixel(state, point);
7
9
  }