@navaramap/three 0.0.0
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/LICENSE +8 -0
- package/dist/assets/atmosphere/README.md +1 -0
- package/dist/assets/atmosphere/higher_order_scattering.bin +0 -0
- package/dist/assets/atmosphere/higher_order_scattering.exr +0 -0
- package/dist/assets/atmosphere/irradiance.bin +0 -0
- package/dist/assets/atmosphere/irradiance.exr +0 -0
- package/dist/assets/atmosphere/scattering.bin +0 -0
- package/dist/assets/atmosphere/scattering.exr +0 -0
- package/dist/assets/atmosphere/single_mie_scattering.bin +0 -0
- package/dist/assets/atmosphere/single_mie_scattering.exr +0 -0
- package/dist/assets/atmosphere/stars.bin +0 -0
- package/dist/assets/atmosphere/transmittance.bin +0 -0
- package/dist/assets/atmosphere/transmittance.exr +0 -0
- package/dist/assets/cloud/README.md +1 -0
- package/dist/assets/cloud/local_weather.png +0 -0
- package/dist/assets/cloud/shape.bin +1 -0
- package/dist/assets/cloud/shape_detail.bin +1 -0
- package/dist/assets/cloud/turbulence.png +0 -0
- package/dist/assets/fontWorker-CEJEfRpb.js +2 -0
- package/dist/assets/fontWorker-CEJEfRpb.js.map +1 -0
- package/dist/assets/index-BUB-ijMO.js +12 -0
- package/dist/assets/index-BUB-ijMO.js.map +1 -0
- package/dist/assets/navara_wasm_font_worker_bg-665OLxjy.wasm +0 -0
- package/dist/assets/navara_wasm_worker_bg-BXhKC5bE.wasm +0 -0
- package/dist/assets/noise/README.md +1 -0
- package/dist/assets/noise/stbn.bin +0 -0
- package/dist/assets/water/README.md +3 -0
- package/dist/assets/water/waternormals.jpg +0 -0
- package/dist/index.d.ts +10114 -0
- package/dist/index.js +34481 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.cjs +6345 -0
- package/dist/index.umd.cjs.map +1 -0
- package/package.json +95 -0
- package/src/Color.ts +111 -0
- package/src/ShadowMapViewers.ts +49 -0
- package/src/atmosphere.test.ts +76 -0
- package/src/atmosphere.ts +364 -0
- package/src/bufferView.ts +90 -0
- package/src/camera.ts +182 -0
- package/src/concurrency.ts +15 -0
- package/src/constants/assets.ts +30 -0
- package/src/constants/decoder.ts +7 -0
- package/src/constants/index.ts +2 -0
- package/src/core/BaseDesc.ts +142 -0
- package/src/core/BaseHandle.ts +126 -0
- package/src/core/DescRegistry.ts +88 -0
- package/src/core/EffectDesc.ts +343 -0
- package/src/core/EffectDescRegistry.ts +31 -0
- package/src/core/InstancedMeshDesc.ts +413 -0
- package/src/core/LightDesc.ts +216 -0
- package/src/core/LightDescRegistry.ts +32 -0
- package/src/core/MeshDesc.ts +457 -0
- package/src/core/MeshDescRegistry.ts +32 -0
- package/src/core/MeshDescWithSelectiveEffect.ts +129 -0
- package/src/core/Registries.ts +59 -0
- package/src/core/SelectiveEffectRegistry.ts +90 -0
- package/src/core/ViewContext.ts +235 -0
- package/src/core/errors.ts +16 -0
- package/src/core/index.ts +50 -0
- package/src/device.ts +414 -0
- package/src/effects/CustomEffectPass.ts +28 -0
- package/src/effects/copy.ts +10 -0
- package/src/effects/effect.ts +87 -0
- package/src/effects/index.ts +4 -0
- package/src/effects/render.ts +10 -0
- package/src/evaluations/FeatureEvaluator.ts +529 -0
- package/src/evaluations/index.ts +1 -0
- package/src/event/HillshadeContext.test.ts +232 -0
- package/src/event/HillshadeContext.ts +299 -0
- package/src/event/context.ts +279 -0
- package/src/event/feature.ts +357 -0
- package/src/event/featureEvent.ts +81 -0
- package/src/event/features/billboard.ts +30 -0
- package/src/event/features/model.ts +192 -0
- package/src/event/features/modelGpuBytes.test.ts +69 -0
- package/src/event/features/modelGpuBytes.ts +87 -0
- package/src/event/features/point.ts +25 -0
- package/src/event/features/polygon.ts +23 -0
- package/src/event/features/polygonOutline.ts +19 -0
- package/src/event/features/polyline.ts +19 -0
- package/src/event/features/text.ts +71 -0
- package/src/event/hillshade.test.ts +842 -0
- package/src/event/hillshade.ts +588 -0
- package/src/event/index.ts +700 -0
- package/src/event/loaders.ts +53 -0
- package/src/event/tile.ts +19 -0
- package/src/event/worker.ts +999 -0
- package/src/event/workerTaskSettlement.test.ts +243 -0
- package/src/event/workerTaskSettlement.ts +128 -0
- package/src/inMemoryBufferStore.test.ts +60 -0
- package/src/inMemoryBufferStore.ts +67 -0
- package/src/index.iife.ts +11 -0
- package/src/index.ts +2556 -0
- package/src/input.ts +144 -0
- package/src/layer.ts +183 -0
- package/src/layers/effect/FinalCopyEffectDesc.ts +29 -0
- package/src/layers/effect/MRTPassEffectDesc.ts +105 -0
- package/src/layers/effect/README.md +9 -0
- package/src/layers/effect/SelectiveEffectDesc.ts +113 -0
- package/src/layers/effect/SkyEnvMapEffectDesc.ts +58 -0
- package/src/layers/effect/TransparentPassEffectDesc.ts +62 -0
- package/src/layers/effect/index.ts +5 -0
- package/src/layers/index.ts +1 -0
- package/src/layersManager.ts +63 -0
- package/src/light.ts +15 -0
- package/src/loaders/AbortableImageLoader.ts +203 -0
- package/src/loaders/AbortableTextureLoader.ts +74 -0
- package/src/loaders/index.ts +5 -0
- package/src/material/enhancer/DESIGN.md +300 -0
- package/src/material/enhancer/MaterialEnhancer.ts +112 -0
- package/src/material/enhancer/ShaderReplacer.test.ts +114 -0
- package/src/material/enhancer/ShaderReplacer.ts +97 -0
- package/src/material/enhancer/batchTexture.ts +8 -0
- package/src/material/enhancer/index.ts +46 -0
- package/src/material/enhancer/instancedSprite/index.ts +7 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/index.test.ts +214 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/index.ts +134 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/markers.ts +21 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/material.ts +32 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/mutates.test.ts +277 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/mutates.ts +151 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/shader.test.ts +114 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/shader.ts +49 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/state.test.ts +82 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/state.ts +80 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/types.ts +146 -0
- package/src/material/enhancer/instancedSprite/instancedSpriteMaterialEnhancer.ts +105 -0
- package/src/material/enhancer/model/index.ts +25 -0
- package/src/material/enhancer/model/modelBaseEnhancer/index.test.ts +74 -0
- package/src/material/enhancer/model/modelBaseEnhancer/index.ts +110 -0
- package/src/material/enhancer/model/modelBaseEnhancer/markers.ts +27 -0
- package/src/material/enhancer/model/modelBaseEnhancer/material.test.ts +66 -0
- package/src/material/enhancer/model/modelBaseEnhancer/material.ts +44 -0
- package/src/material/enhancer/model/modelBaseEnhancer/mutates.test.ts +84 -0
- package/src/material/enhancer/model/modelBaseEnhancer/mutates.ts +47 -0
- package/src/material/enhancer/model/modelBaseEnhancer/shader.test.ts +24 -0
- package/src/material/enhancer/model/modelBaseEnhancer/shader.ts +162 -0
- package/src/material/enhancer/model/modelBaseEnhancer/state.test.ts +103 -0
- package/src/material/enhancer/model/modelBaseEnhancer/state.ts +52 -0
- package/src/material/enhancer/model/modelBaseEnhancer/types.ts +89 -0
- package/src/material/enhancer/model/modelMaterialEnhancer.ts +91 -0
- package/src/material/enhancer/model/modelWaterEnhancer/index.test.ts +71 -0
- package/src/material/enhancer/model/modelWaterEnhancer/index.ts +175 -0
- package/src/material/enhancer/model/modelWaterEnhancer/material.test.ts +48 -0
- package/src/material/enhancer/model/modelWaterEnhancer/material.ts +25 -0
- package/src/material/enhancer/model/modelWaterEnhancer/mutates.test.ts +89 -0
- package/src/material/enhancer/model/modelWaterEnhancer/mutates.ts +89 -0
- package/src/material/enhancer/model/modelWaterEnhancer/shader.test.ts +34 -0
- package/src/material/enhancer/model/modelWaterEnhancer/shader.ts +111 -0
- package/src/material/enhancer/model/modelWaterEnhancer/state.test.ts +105 -0
- package/src/material/enhancer/model/modelWaterEnhancer/state.ts +59 -0
- package/src/material/enhancer/model/modelWaterEnhancer/types.ts +141 -0
- package/src/material/enhancer/model/pntsEnhancer/index.test.ts +96 -0
- package/src/material/enhancer/model/pntsEnhancer/index.ts +82 -0
- package/src/material/enhancer/model/pntsEnhancer/markers.ts +6 -0
- package/src/material/enhancer/model/pntsEnhancer/material.test.ts +37 -0
- package/src/material/enhancer/model/pntsEnhancer/material.ts +21 -0
- package/src/material/enhancer/model/pntsEnhancer/mutates.test.ts +80 -0
- package/src/material/enhancer/model/pntsEnhancer/mutates.ts +29 -0
- package/src/material/enhancer/model/pntsEnhancer/shader.test.ts +32 -0
- package/src/material/enhancer/model/pntsEnhancer/shader.ts +56 -0
- package/src/material/enhancer/model/pntsEnhancer/state.test.ts +87 -0
- package/src/material/enhancer/model/pntsEnhancer/state.ts +29 -0
- package/src/material/enhancer/model/pntsEnhancer/types.ts +38 -0
- package/src/material/enhancer/polygon/index.ts +22 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/index.test.ts +101 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/index.ts +130 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/markers.ts +23 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/material.test.ts +19 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/material.ts +38 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/mutates.test.ts +75 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/mutates.ts +122 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.test.ts +25 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.ts +249 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/state.test.ts +102 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/state.ts +92 -0
- package/src/material/enhancer/polygon/polygonBaseEnhancer/types.ts +150 -0
- package/src/material/enhancer/polygon/polygonMaterialEnhancer.ts +97 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/index.test.ts +72 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/index.ts +164 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/material.test.ts +48 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/material.ts +35 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/mutates.ts +61 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/shader.test.ts +34 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/shader.ts +96 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/state.test.ts +98 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/state.ts +57 -0
- package/src/material/enhancer/polygon/polygonWaterEnhancer/types.ts +115 -0
- package/src/material/enhancer/polyline/index.ts +7 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/index.test.ts +157 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/index.ts +154 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/markers.ts +19 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/material.test.ts +42 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/material.ts +38 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/mutates.test.ts +120 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/mutates.ts +169 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/shader.test.ts +67 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/shader.ts +75 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/state.test.ts +55 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/state.ts +91 -0
- package/src/material/enhancer/polyline/polylineBaseEnhancer/types.ts +170 -0
- package/src/material/enhancer/polyline/polylineMaterialEnhancer.ts +102 -0
- package/src/material/enhancer/sdfText/index.ts +7 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/index.ts +125 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/markers.ts +19 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/material.test.ts +35 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/material.ts +32 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.test.ts +381 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.ts +232 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/shader.ts +36 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.test.ts +118 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.ts +107 -0
- package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/types.ts +184 -0
- package/src/material/enhancer/sdfText/sdfTextMaterialEnhancer.ts +100 -0
- package/src/material/enhancer/shadowMap/index.ts +7 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/index.ts +97 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/markers.ts +10 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/material.ts +31 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/mutates.ts +8 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/shader.test.ts +17 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/shader.ts +53 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/state.ts +17 -0
- package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/types.ts +26 -0
- package/src/material/enhancer/test-utils/shaderCompatibility.ts +124 -0
- package/src/material/enhancer/tileComposite/compose.test.ts +95 -0
- package/src/material/enhancer/tileComposite/compose.ts +112 -0
- package/src/material/enhancer/tileComposite/index.ts +23 -0
- package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/index.ts +11 -0
- package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/mutates.ts +132 -0
- package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/shader.ts +245 -0
- package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/types.ts +43 -0
- package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/index.ts +29 -0
- package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/mutates.ts +68 -0
- package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/shader.ts +24 -0
- package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/index.ts +29 -0
- package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/mutates.ts +34 -0
- package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/shader.ts +36 -0
- package/src/material/enhancer/tileComposite/tileWaterEnhancer/index.ts +23 -0
- package/src/material/enhancer/tileComposite/tileWaterEnhancer/mutates.ts +23 -0
- package/src/material/enhancer/tileComposite/tileWaterEnhancer/shader.ts +8 -0
- package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/index.ts +23 -0
- package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/mutates.ts +34 -0
- package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/shader.ts +13 -0
- package/src/material/enhancer/tileComposite/types.ts +58 -0
- package/src/material/index.ts +3 -0
- package/src/material/macro/index.ts +1 -0
- package/src/material/macro/tileShader.test.ts +136 -0
- package/src/material/macro/tileShader.ts +287 -0
- package/src/material/overrideMaterialsForMRT.ts +439 -0
- package/src/material/selectiveEffectMaterialSetup.test.ts +52 -0
- package/src/material/selectiveEffectMaterialSetup.ts +53 -0
- package/src/material/types/index.ts +1 -0
- package/src/material/types/uniform.ts +1 -0
- package/src/mesh/DrapedMesh.ts +101 -0
- package/src/mesh/batchTexture.test.ts +646 -0
- package/src/mesh/batchTexture.ts +437 -0
- package/src/mesh/batchedFeature.ts +186 -0
- package/src/mesh/batchedSdfText.ts +484 -0
- package/src/mesh/featureMesh.ts +43 -0
- package/src/mesh/index.ts +16 -0
- package/src/mesh/instanced.ts +117 -0
- package/src/mesh/model.ts +483 -0
- package/src/mesh/pickableMesh.ts +55 -0
- package/src/mesh/pickableMeshWrapper.ts +529 -0
- package/src/mesh/polygon.ts +699 -0
- package/src/mesh/polygonOutline.ts +393 -0
- package/src/mesh/polyline.ts +523 -0
- package/src/mesh/releaseGeometryArrays.ts +57 -0
- package/src/mesh/rtcRteHelper.ts +211 -0
- package/src/mesh/sdfText.test.ts +200 -0
- package/src/mesh/sdfText.ts +1019 -0
- package/src/mesh/sprite/billboardAtlas.test.ts +177 -0
- package/src/mesh/sprite/billboardAtlas.ts +210 -0
- package/src/mesh/sprite/billboardAtlasImageLoader.ts +29 -0
- package/src/mesh/sprite/index.ts +1 -0
- package/src/mesh/sprite/instancedSprite.ts +694 -0
- package/src/mesh/tile.test.ts +393 -0
- package/src/mesh/tile.ts +1790 -0
- package/src/object3DEvent.ts +6 -0
- package/src/orchestrators/RenderPassOrchestrator.ts +147 -0
- package/src/orchestrators/index.ts +1 -0
- package/src/passes/AllDepthCopyPass.ts +165 -0
- package/src/passes/CustomRenderPass.ts +279 -0
- package/src/passes/NormalCopyPass.ts +125 -0
- package/src/passes/RenderTargetCopyPass.ts +91 -0
- package/src/passes/SkyEnvMapPass.ts +58 -0
- package/src/passes/index.ts +5 -0
- package/src/pick/pickHelper.ts +273 -0
- package/src/pick/pickTerrain.ts +254 -0
- package/src/plugins/AttributionPlugin/AttributionPlugin.test.ts +210 -0
- package/src/plugins/AttributionPlugin/attribution.test.ts +225 -0
- package/src/plugins/AttributionPlugin/attribution.ts +355 -0
- package/src/plugins/AttributionPlugin/attributionStyles.ts +163 -0
- package/src/plugins/AttributionPlugin/index.ts +702 -0
- package/src/plugins/index.ts +1 -0
- package/src/quality.ts +1 -0
- package/src/renderOrder.ts +3 -0
- package/src/scene.test.ts +164 -0
- package/src/scene.ts +127 -0
- package/src/shaders/index.ts +1 -0
- package/src/solar.test.ts +81 -0
- package/src/solar.ts +101 -0
- package/src/source.ts +80 -0
- package/src/stats.ts +102 -0
- package/src/tasks/computeVertexNormalsAsync.ts +15 -0
- package/src/tasks/constructPolygonBatchedFeature.ts +34 -0
- package/src/tasks/constructPolylineBatchedFeature.ts +27 -0
- package/src/tasks/constructQuantizedMeshTerrainMesh.ts +24 -0
- package/src/tasks/constructTerrainMesh.ts +25 -0
- package/src/tasks/getImageDataFromBlob.ts +13 -0
- package/src/tasks/getImageDataFromImageBitmap.ts +12 -0
- package/src/tasks/parseMvtTile.ts +22 -0
- package/src/tasks/queueTask.ts +19 -0
- package/src/tasks/toCreasedNormalsAsync.ts +18 -0
- package/src/tasks/upsampleQuantizedMeshTerrainMesh.ts +44 -0
- package/src/tasks/upsampleTerrainMesh.ts +39 -0
- package/src/texture.ts +19 -0
- package/src/textures.ts +13 -0
- package/src/tileTexture/CompositeShader.snapshot.test.ts +59 -0
- package/src/tileTexture/CompositeShader.test.ts +168 -0
- package/src/tileTexture/CompositeShader.ts +53 -0
- package/src/tileTexture/SlotPlanner.test.ts +105 -0
- package/src/tileTexture/SlotPlanner.ts +102 -0
- package/src/tileTexture/TileTextureCache.test.ts +227 -0
- package/src/tileTexture/TileTextureCache.ts +192 -0
- package/src/tileTexture/TileTextureCompositor.test.ts +260 -0
- package/src/tileTexture/TileTextureCompositor.ts +421 -0
- package/src/tileTexture/__snapshots__/CompositeShader.snapshot.test.ts.snap +2649 -0
- package/src/tileTexture/index.ts +8 -0
- package/src/tileTexture/types.ts +150 -0
- package/src/type/index.ts +261 -0
- package/src/uniforms.ts +20 -0
- package/src/utils/arraysEqual.ts +9 -0
- package/src/utils/blendModes.ts +80 -0
- package/src/utils/geometry.ts +53 -0
- package/src/utils/hillshadeNormalMapGenerator.ts +193 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/object.test.ts +14 -0
- package/src/utils/object.ts +7 -0
- package/src/utils/replacer.test.ts +42 -0
- package/src/utils/replacer.ts +89 -0
- package/src/utils/screen.ts +29 -0
- package/src/utils/textureFragmentIndex.test.ts +224 -0
- package/src/utils/textureFragmentIndex.ts +97 -0
- package/src/utils/worker.ts +3 -0
- package/src/vite-env.d.ts +2 -0
- package/src/worker/index.ts +13 -0
- package/src/worker/tasks/computeVertexNormals.ts +15 -0
- package/src/worker/tasks/toCreasedNormals.ts +22 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AttributionPlugin — Navara Plugin for map data attribution (credit) UI.
|
|
3
|
+
*
|
|
4
|
+
* Renders a non-modal popover (ⓘ button, bottom-right by default) listing data-source
|
|
5
|
+
* attributions, with a separate always-visible logo frame in the bottom-left
|
|
6
|
+
* corner. Each source can carry zoom-banded child credits that switch as the
|
|
7
|
+
* camera zooms, and per-layer feature credits are tracked dynamically.
|
|
8
|
+
*
|
|
9
|
+
* `ThreeView` creates one by default and exposes it as `view.attribution`
|
|
10
|
+
* (pass `defaultAttribution: false` to opt out).
|
|
11
|
+
*
|
|
12
|
+
* ## Usage
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import ThreeView from "@navaramap/three";
|
|
16
|
+
*
|
|
17
|
+
* const view = new ThreeView({ container });
|
|
18
|
+
* await view.init();
|
|
19
|
+
*
|
|
20
|
+
* // A 3D-tiles layer whose tiles embed their own copyright (tracked dynamically).
|
|
21
|
+
* const photoreal = view.addLayer({ type: "cesium3dtiles", data: { url } });
|
|
22
|
+
*
|
|
23
|
+
* // `add` / `remove` manage the set of displayed attributions.
|
|
24
|
+
* view.attribution?.add([
|
|
25
|
+
* {
|
|
26
|
+
* attribution: "Geospatial Information Authority of Japan (GSI)",
|
|
27
|
+
* attributionUrl: "https://maps.gsi.go.jp/development/ichiran.html",
|
|
28
|
+
* children: [
|
|
29
|
+
* { attribution: "Nationwide latest aerial photos (seamless)", minZoom: 14, maxZoom: 18 },
|
|
30
|
+
* { attribution: "GRUS画像(© Axelspace)", minZoom: 14, maxZoom: 18 },
|
|
31
|
+
* ],
|
|
32
|
+
* },
|
|
33
|
+
* {
|
|
34
|
+
* // Per-tile copyright is tracked dynamically by resolving this layer id.
|
|
35
|
+
* attribution: "Google Maps Photorealistic 3D Tiles",
|
|
36
|
+
* creditLayerId: photoreal.id,
|
|
37
|
+
* },
|
|
38
|
+
* {
|
|
39
|
+
* attributionHtml:
|
|
40
|
+
* '<a href="https://s2maps.eu">Sentinel-2 cloudless 2020</a> by <a href="https://eox.at">EOX IT Services GmbH</a>',
|
|
41
|
+
* },
|
|
42
|
+
* ]);
|
|
43
|
+
*
|
|
44
|
+
* // Drop a source again when its data leaves the map (matched structurally).
|
|
45
|
+
* view.attribution?.remove([{ attribution: "Google Maps Photorealistic 3D Tiles", creditLayerId: photoreal.id }]);
|
|
46
|
+
*
|
|
47
|
+
* // Re-theme at runtime (e.g. light / dark switch).
|
|
48
|
+
* view.attribution?.setStyle({ backgroundColor: "#14181c", textColor: "#e6e9ee" });
|
|
49
|
+
*
|
|
50
|
+
* // The popover is collapsed by default; show() opens it and hide() collapses
|
|
51
|
+
* // it (the ⓘ button toggles the same state).
|
|
52
|
+
* view.attribution?.hide();
|
|
53
|
+
* view.attribution?.show();
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
import { Plugin } from "@navaramap/core";
|
|
57
|
+
|
|
58
|
+
import type { ViewContext } from "../../core";
|
|
59
|
+
import type ThreeView from "../../index";
|
|
60
|
+
import type {
|
|
61
|
+
Layer,
|
|
62
|
+
FeatureCreatedParams,
|
|
63
|
+
FeatureRemovedParams,
|
|
64
|
+
FeatureVisibilityChangedParams,
|
|
65
|
+
} from "../../layer";
|
|
66
|
+
|
|
67
|
+
import {
|
|
68
|
+
aggregateCredits,
|
|
69
|
+
appendSanitizedHtml,
|
|
70
|
+
attributionItemKey,
|
|
71
|
+
createSafeAnchor,
|
|
72
|
+
dedupeAttributionItems,
|
|
73
|
+
isAttributionHtml,
|
|
74
|
+
matchesZoom,
|
|
75
|
+
safeHref,
|
|
76
|
+
type AttributionItem,
|
|
77
|
+
type AttributionStyle,
|
|
78
|
+
} from "./attribution";
|
|
79
|
+
import {
|
|
80
|
+
STYLE_ELEMENT_ID,
|
|
81
|
+
STYLE_TEXT,
|
|
82
|
+
SVG_ICON_HTML,
|
|
83
|
+
} from "./attributionStyles";
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
isAttributionHtml,
|
|
87
|
+
type AttributionItem,
|
|
88
|
+
type AttributionSource,
|
|
89
|
+
type AttributionHtml,
|
|
90
|
+
type AttributionChild,
|
|
91
|
+
type AttributionStyle,
|
|
92
|
+
} from "./attribution";
|
|
93
|
+
|
|
94
|
+
type View = ThreeView;
|
|
95
|
+
|
|
96
|
+
/** Which bottom corner the attribution UI anchors to. */
|
|
97
|
+
export type AttributionPosition = "bottom-left" | "bottom-right";
|
|
98
|
+
|
|
99
|
+
/** Options for {@link AttributionPlugin}. */
|
|
100
|
+
export type AttributionPluginOptions = {
|
|
101
|
+
/** Initial color overrides; tweak later with {@link AttributionPlugin.setStyle}. */
|
|
102
|
+
style?: AttributionStyle;
|
|
103
|
+
/**
|
|
104
|
+
* Bottom corner for the ⓘ button and its popover card. Defaults to
|
|
105
|
+
* `"bottom-right"`; use `"bottom-left"` when the bottom-right corner is
|
|
106
|
+
* occupied (e.g. a page with its own HUD there). The logo frame lives in the
|
|
107
|
+
* bottom-left area in both modes; in `"bottom-left"` the ⓘ takes the far-left
|
|
108
|
+
* corner and the logos shift right to sit beside it.
|
|
109
|
+
*/
|
|
110
|
+
position?: AttributionPosition;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Number of live instances using the shared `<style>` element. The style is
|
|
115
|
+
* injected once and removed only when the last instance tears down, so
|
|
116
|
+
* multiple plugins don't duplicate the id or strip each other's styles.
|
|
117
|
+
*/
|
|
118
|
+
let styleRefCount = 0;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Built-in Navara credit, always shown as the first attribution. It is kept
|
|
122
|
+
* separate from the user-managed set, so `add` / `remove` / `clear` never touch
|
|
123
|
+
* it, and it keeps the UI visible even before any data-source credit is added.
|
|
124
|
+
*/
|
|
125
|
+
const NAVARA_CREDIT: AttributionItem = {
|
|
126
|
+
attribution: "Navara",
|
|
127
|
+
attributionUrl: "https://navara-docs.netlify.app/",
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Renders map data attributions as a non-modal popover (ⓘ button, bottom-right
|
|
132
|
+
* by default). Top-level sources can carry nested, optionally zoom-banded child
|
|
133
|
+
* credits.
|
|
134
|
+
*
|
|
135
|
+
* A `ThreeView` creates one by default (`view.attribution`); construct it
|
|
136
|
+
* manually and `view.addPlugin(plugin)` **before** `view.init()` only when the
|
|
137
|
+
* view was created with `defaultAttribution: false`.
|
|
138
|
+
*/
|
|
139
|
+
export class AttributionPlugin extends Plugin<View, ViewContext> {
|
|
140
|
+
private view?: View;
|
|
141
|
+
private items: AttributionItem[] = [];
|
|
142
|
+
|
|
143
|
+
private styleEl?: HTMLStyleElement;
|
|
144
|
+
private dock?: HTMLDivElement;
|
|
145
|
+
private card?: HTMLDivElement;
|
|
146
|
+
private listEl?: HTMLUListElement;
|
|
147
|
+
private logosEl?: HTMLDivElement;
|
|
148
|
+
private toggle?: HTMLButtonElement;
|
|
149
|
+
// Collapsed by default; users open it with the toggle.
|
|
150
|
+
private isOpen = false;
|
|
151
|
+
|
|
152
|
+
/** Last computed integer zoom level. Used to skip no-op re-renders. */
|
|
153
|
+
private lastZoomLevel?: number;
|
|
154
|
+
/** Whether any source has zoom-banded children — gates the per-frame poll. */
|
|
155
|
+
private hasZoomBands = false;
|
|
156
|
+
|
|
157
|
+
/** Per-layer dynamic credits, keyed by `layer.id`. */
|
|
158
|
+
private layerCredits = new Map<
|
|
159
|
+
string,
|
|
160
|
+
{ credits: Map<bigint, string>; visible: Set<bigint> }
|
|
161
|
+
>();
|
|
162
|
+
private layerCleanups: (() => void)[] = [];
|
|
163
|
+
|
|
164
|
+
/** Color overrides, applied as CSS custom properties on the dock. */
|
|
165
|
+
private style: AttributionStyle;
|
|
166
|
+
|
|
167
|
+
/** Bottom corner for the ⓘ button / popover card. */
|
|
168
|
+
private position: AttributionPosition;
|
|
169
|
+
|
|
170
|
+
private boundKeydown: (event: KeyboardEvent) => void;
|
|
171
|
+
private boundPreRender: () => void;
|
|
172
|
+
|
|
173
|
+
constructor(options: AttributionPluginOptions = {}) {
|
|
174
|
+
super();
|
|
175
|
+
this.style = options.style ?? {};
|
|
176
|
+
this.position = options.position ?? "bottom-right";
|
|
177
|
+
this.boundKeydown = this.handleKeydown.bind(this);
|
|
178
|
+
this.boundPreRender = this.handlePreRender.bind(this);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The full list to render: the built-in Navara credit first, then the
|
|
183
|
+
* user-managed set. Always non-empty, so the dock stays visible.
|
|
184
|
+
*/
|
|
185
|
+
private displayedItems(): AttributionItem[] {
|
|
186
|
+
return [NAVARA_CREDIT, ...this.items];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async init(view: View, _ctx: ViewContext): Promise<void> {
|
|
190
|
+
this.view = view;
|
|
191
|
+
// Recompute the zoom level on render. `fovy` is undefined until the first
|
|
192
|
+
// frame, so a camera `moveend` alone would miss the initial value on a
|
|
193
|
+
// static map; `preRender` reliably fires while the scene renders. The
|
|
194
|
+
// level-change gate in `handlePreRender` keeps this from churning the DOM.
|
|
195
|
+
view.on("preRender", this.boundPreRender);
|
|
196
|
+
// Build the UI now that the view exists. The built-in Navara credit is
|
|
197
|
+
// always present, so the dock shows even before any source is added.
|
|
198
|
+
this.apply();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Add attributions to the displayed set. Merged with the current entries;
|
|
203
|
+
* exact duplicates are dropped so several data sources that share one credit
|
|
204
|
+
* (e.g. multiple Overture themes) render a single line, not one each.
|
|
205
|
+
*
|
|
206
|
+
* Sources that declare a `creditLayerId` have that layer's per-feature credits
|
|
207
|
+
* tracked dynamically; the layer is resolved from the view by id, so callers
|
|
208
|
+
* don't pass the `Layer` object separately.
|
|
209
|
+
*
|
|
210
|
+
* @param items - Attribution entries (sources or raw HTML credits)
|
|
211
|
+
*/
|
|
212
|
+
add(items: AttributionItem[]): void {
|
|
213
|
+
this.items = dedupeAttributionItems([...this.items, ...items]);
|
|
214
|
+
this.apply();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Remove attributions from the displayed set. Entries are matched
|
|
219
|
+
* structurally (same rendered content), so pass the same object shape that
|
|
220
|
+
* was added — no separate id is needed. Unmatched entries are ignored.
|
|
221
|
+
*
|
|
222
|
+
* @param items - Attribution entries to drop
|
|
223
|
+
*/
|
|
224
|
+
remove(items: AttributionItem[]): void {
|
|
225
|
+
const keys = new Set(items.map(attributionItemKey));
|
|
226
|
+
this.items = this.items.filter(
|
|
227
|
+
(item) => !keys.has(attributionItemKey(item)),
|
|
228
|
+
);
|
|
229
|
+
this.apply();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Remove all user-added attributions, keeping the plugin usable. The built-in
|
|
234
|
+
* Navara credit and the dock stay visible; the logo frame hides if no logo
|
|
235
|
+
* remains. Use {@link dispose} to tear the DOM down.
|
|
236
|
+
*/
|
|
237
|
+
clear(): void {
|
|
238
|
+
this.items = [];
|
|
239
|
+
this.apply();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Resolve/track the current items' credit layers and render. */
|
|
243
|
+
private apply(): void {
|
|
244
|
+
const layers = this.resolveCreditLayers(this.items);
|
|
245
|
+
// Retrack only when the credit-layer set changes (avoids wiping tracked credits).
|
|
246
|
+
const nextIds = new Set(layers.map((l) => l.id));
|
|
247
|
+
const sameSet =
|
|
248
|
+
nextIds.size === this.layerCredits.size &&
|
|
249
|
+
[...nextIds].every((id) => this.layerCredits.has(id));
|
|
250
|
+
if (!sameSet) this.trackLayers(layers);
|
|
251
|
+
this.render();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Resolve the `Layer` objects referenced by sources' `creditLayerId`, deduped
|
|
256
|
+
* by id so a layer referenced by multiple sources is tracked only once.
|
|
257
|
+
*/
|
|
258
|
+
private resolveCreditLayers(items: AttributionItem[]): Layer[] {
|
|
259
|
+
const view = this.view;
|
|
260
|
+
if (!view) return [];
|
|
261
|
+
const seen = new Set<string>();
|
|
262
|
+
const layers: Layer[] = [];
|
|
263
|
+
for (const item of items) {
|
|
264
|
+
if (isAttributionHtml(item) || !item.creditLayerId) continue;
|
|
265
|
+
if (seen.has(item.creditLayerId)) continue;
|
|
266
|
+
seen.add(item.creditLayerId);
|
|
267
|
+
const layer = view.findLayerById(item.creditLayerId);
|
|
268
|
+
if (layer) layers.push(layer);
|
|
269
|
+
}
|
|
270
|
+
return layers;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Open the attribution popover. It is collapsed by default, so call this (or
|
|
275
|
+
* use the ⓘ button, which toggles the same state) to open it. Affects the
|
|
276
|
+
* popover card only — the always-visible logo frame stays put.
|
|
277
|
+
*/
|
|
278
|
+
show(): void {
|
|
279
|
+
this.setOpen(true);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Close the attribution popover. Affects the popover card only; the tracked
|
|
284
|
+
* attributions and the always-visible logo frame are untouched (use
|
|
285
|
+
* {@link remove} to drop entries, {@link dispose} to tear everything down).
|
|
286
|
+
*/
|
|
287
|
+
hide(): void {
|
|
288
|
+
this.setOpen(false);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Release all DOM nodes, camera listeners, and layer listeners. */
|
|
292
|
+
dispose(): void {
|
|
293
|
+
this.view?.off("preRender", this.boundPreRender);
|
|
294
|
+
this.teardownDom();
|
|
295
|
+
this.items = [];
|
|
296
|
+
this.view = undefined;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Update the UI colors. Merges over the current style and re-themes the live
|
|
301
|
+
* DOM in place (no rebuild), so it suits runtime switches like light ⇄ dark.
|
|
302
|
+
*/
|
|
303
|
+
setStyle(style: AttributionStyle): void {
|
|
304
|
+
this.style = { ...this.style, ...style };
|
|
305
|
+
this.applyStyle();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Push the current style onto the dock as CSS custom properties. */
|
|
309
|
+
private applyStyle(): void {
|
|
310
|
+
const dock = this.dock;
|
|
311
|
+
if (!dock) return;
|
|
312
|
+
const set = (name: string, value: string | undefined): void => {
|
|
313
|
+
if (value !== undefined) dock.style.setProperty(name, value);
|
|
314
|
+
};
|
|
315
|
+
set("--nvr-attr-title", this.style.titleColor);
|
|
316
|
+
set("--nvr-attr-link", this.style.linkColor);
|
|
317
|
+
set("--nvr-attr-bullet", this.style.listStyleColor);
|
|
318
|
+
set("--nvr-attr-text", this.style.textColor);
|
|
319
|
+
set("--nvr-attr-nested", this.style.nestedTextColor);
|
|
320
|
+
set("--nvr-attr-bg", this.style.backgroundColor);
|
|
321
|
+
set("--nvr-attr-border", this.style.borderColor);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Build / refresh the popover DOM from {@link displayedItems}, filtering
|
|
326
|
+
* zoom-banded children by the current camera zoom.
|
|
327
|
+
*/
|
|
328
|
+
private render(): void {
|
|
329
|
+
if (!this.view) return;
|
|
330
|
+
this.ensureDom();
|
|
331
|
+
const items = this.displayedItems();
|
|
332
|
+
this.hasZoomBands = items.some(
|
|
333
|
+
(item) =>
|
|
334
|
+
!isAttributionHtml(item) &&
|
|
335
|
+
(item.children?.some(
|
|
336
|
+
(c) => c.minZoom !== undefined || c.maxZoom !== undefined,
|
|
337
|
+
) ??
|
|
338
|
+
false),
|
|
339
|
+
);
|
|
340
|
+
this.lastZoomLevel = this.currentZoomLevel();
|
|
341
|
+
this.populateList();
|
|
342
|
+
this.populateLogos();
|
|
343
|
+
// The dock always shows: the built-in Navara credit is always present. The
|
|
344
|
+
// logo frame only shows when at least one source declares a logo.
|
|
345
|
+
if (this.dock) this.dock.hidden = false;
|
|
346
|
+
const hasLogos = items.some(
|
|
347
|
+
(item) => !isAttributionHtml(item) && !!item.logo,
|
|
348
|
+
);
|
|
349
|
+
if (this.logosEl) this.logosEl.hidden = !hasLogos;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** Create the dock DOM and inject (or reuse) the shared styles. */
|
|
353
|
+
private ensureDom(): void {
|
|
354
|
+
if (!this.styleEl) {
|
|
355
|
+
// Reuse a shared style element if another instance already injected it,
|
|
356
|
+
// and count this instance as an owner (removed on the last teardown).
|
|
357
|
+
const existing = document.getElementById(STYLE_ELEMENT_ID);
|
|
358
|
+
if (existing instanceof HTMLStyleElement) {
|
|
359
|
+
this.styleEl = existing;
|
|
360
|
+
} else {
|
|
361
|
+
const style = document.createElement("style");
|
|
362
|
+
style.id = STYLE_ELEMENT_ID;
|
|
363
|
+
style.textContent = STYLE_TEXT;
|
|
364
|
+
document.head.appendChild(style);
|
|
365
|
+
this.styleEl = style;
|
|
366
|
+
}
|
|
367
|
+
styleRefCount += 1;
|
|
368
|
+
}
|
|
369
|
+
if (this.dock) return;
|
|
370
|
+
|
|
371
|
+
const dock = document.createElement("div");
|
|
372
|
+
dock.className = "navara-attr-dock";
|
|
373
|
+
if (this.position === "bottom-left") {
|
|
374
|
+
dock.classList.add("navara-attr-dock--left");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const card = document.createElement("div");
|
|
378
|
+
card.className = "navara-attr-card";
|
|
379
|
+
// Reflect the current open intent (see setOpen).
|
|
380
|
+
card.hidden = !this.isOpen;
|
|
381
|
+
|
|
382
|
+
const head = document.createElement("div");
|
|
383
|
+
head.className = "navara-attr-head";
|
|
384
|
+
const title = document.createElement("h3");
|
|
385
|
+
title.textContent = "Attributions";
|
|
386
|
+
const close = document.createElement("button");
|
|
387
|
+
close.className = "navara-attr-close";
|
|
388
|
+
close.type = "button";
|
|
389
|
+
close.textContent = "✕";
|
|
390
|
+
close.setAttribute("aria-label", "Close");
|
|
391
|
+
close.addEventListener("click", () => this.setOpen(false));
|
|
392
|
+
head.appendChild(title);
|
|
393
|
+
head.appendChild(close);
|
|
394
|
+
|
|
395
|
+
const list = document.createElement("ul");
|
|
396
|
+
list.className = "navara-attr-list";
|
|
397
|
+
|
|
398
|
+
card.appendChild(head);
|
|
399
|
+
card.appendChild(list);
|
|
400
|
+
|
|
401
|
+
const toggle = document.createElement("button");
|
|
402
|
+
toggle.className = "navara-attr-toggle";
|
|
403
|
+
toggle.type = "button";
|
|
404
|
+
toggle.innerHTML = SVG_ICON_HTML;
|
|
405
|
+
toggle.setAttribute("aria-expanded", String(this.isOpen));
|
|
406
|
+
toggle.setAttribute(
|
|
407
|
+
"aria-label",
|
|
408
|
+
this.isOpen ? "Hide attributions" : "Show attributions",
|
|
409
|
+
);
|
|
410
|
+
toggle.addEventListener("click", () => this.setOpen());
|
|
411
|
+
|
|
412
|
+
dock.appendChild(card);
|
|
413
|
+
dock.appendChild(toggle);
|
|
414
|
+
document.body.appendChild(dock);
|
|
415
|
+
|
|
416
|
+
// Always-visible logo frame in the separate bottom-left corner, so
|
|
417
|
+
// contractually-mandated marks stay visible independent of the popover.
|
|
418
|
+
const logoFrame = document.createElement("div");
|
|
419
|
+
logoFrame.className = "navara-attr-logoframe";
|
|
420
|
+
// In bottom-left mode the ⓘ button sits at the far left, so shift the logo
|
|
421
|
+
// frame right of it to keep them in one row instead of overlapping.
|
|
422
|
+
if (this.position === "bottom-left") {
|
|
423
|
+
logoFrame.classList.add("navara-attr-logoframe--left");
|
|
424
|
+
}
|
|
425
|
+
document.body.appendChild(logoFrame);
|
|
426
|
+
|
|
427
|
+
document.addEventListener("keydown", this.boundKeydown);
|
|
428
|
+
|
|
429
|
+
this.dock = dock;
|
|
430
|
+
this.card = card;
|
|
431
|
+
this.listEl = list;
|
|
432
|
+
this.logosEl = logoFrame;
|
|
433
|
+
this.toggle = toggle;
|
|
434
|
+
this.applyStyle();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Create a bullet list item, returning the `<li>` and its text `<span>`. */
|
|
438
|
+
private createItemShell(): { li: HTMLLIElement; text: HTMLSpanElement } {
|
|
439
|
+
const li = document.createElement("li");
|
|
440
|
+
li.className = "navara-attr-item";
|
|
441
|
+
const name = document.createElement("div");
|
|
442
|
+
name.className = "navara-attr-name";
|
|
443
|
+
const bullet = document.createElement("span");
|
|
444
|
+
bullet.className = "navara-attr-bullet";
|
|
445
|
+
name.appendChild(bullet);
|
|
446
|
+
const text = document.createElement("span");
|
|
447
|
+
name.appendChild(text);
|
|
448
|
+
li.appendChild(name);
|
|
449
|
+
return { li, text };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Rebuild the list. Each source shows its sub-credits — zoom-filtered static
|
|
454
|
+
* `children` plus the dynamic credits of the layer it links via
|
|
455
|
+
* `creditLayerId` — as a nested list.
|
|
456
|
+
*/
|
|
457
|
+
private populateList(): void {
|
|
458
|
+
if (!this.listEl) return;
|
|
459
|
+
this.listEl.replaceChildren();
|
|
460
|
+
|
|
461
|
+
for (const item of this.displayedItems()) {
|
|
462
|
+
const { li, text } = this.createItemShell();
|
|
463
|
+
|
|
464
|
+
if (isAttributionHtml(item)) {
|
|
465
|
+
appendSanitizedHtml(text, item.attributionHtml);
|
|
466
|
+
this.listEl.appendChild(li);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const href = item.attributionUrl
|
|
471
|
+
? safeHref(item.attributionUrl)
|
|
472
|
+
: undefined;
|
|
473
|
+
if (href) {
|
|
474
|
+
text.appendChild(createSafeAnchor(href, item.attribution));
|
|
475
|
+
} else {
|
|
476
|
+
text.textContent = item.attribution;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Sub-credits: zoom-banded static children, then the linked layer's
|
|
480
|
+
// dynamic credits. Both go through the sanitizer so embedded `<a>` links
|
|
481
|
+
// stay clickable while scripts/handlers from untrusted tile metadata are
|
|
482
|
+
// dropped to text.
|
|
483
|
+
const sub = document.createElement("ul");
|
|
484
|
+
sub.className = "navara-attr-related";
|
|
485
|
+
if (item.children) {
|
|
486
|
+
for (const child of item.children) {
|
|
487
|
+
if (!matchesZoom(child, this.lastZoomLevel)) continue;
|
|
488
|
+
const childLi = document.createElement("li");
|
|
489
|
+
appendSanitizedHtml(childLi, child.attribution);
|
|
490
|
+
sub.appendChild(childLi);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (item.creditLayerId) {
|
|
494
|
+
for (const credit of this.layerCreditStrings(item.creditLayerId)) {
|
|
495
|
+
const creditLi = document.createElement("li");
|
|
496
|
+
appendSanitizedHtml(creditLi, credit);
|
|
497
|
+
sub.appendChild(creditLi);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (sub.childElementCount > 0) li.appendChild(sub);
|
|
502
|
+
|
|
503
|
+
this.listEl.appendChild(li);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** Aggregated, deduped credit strings for a tracked layer's visible features. */
|
|
508
|
+
private layerCreditStrings(layerId: string): string[] {
|
|
509
|
+
const state = this.layerCredits.get(layerId);
|
|
510
|
+
if (!state) return [];
|
|
511
|
+
const strings: string[] = [];
|
|
512
|
+
for (const id of state.visible) {
|
|
513
|
+
const credit = state.credits.get(id);
|
|
514
|
+
if (credit) strings.push(credit);
|
|
515
|
+
}
|
|
516
|
+
return aggregateCredits(strings);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Rebuild the list if the popover is open; while closed, do nothing —
|
|
521
|
+
* `setOpen()` repopulates on open, so deferred updates are picked up then.
|
|
522
|
+
*/
|
|
523
|
+
private requestRender(): void {
|
|
524
|
+
if (this.dock && this.isOpen) this.populateList();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** Rebuild the always-visible logo frame from sources that declare a `logo`. */
|
|
528
|
+
private populateLogos(): void {
|
|
529
|
+
if (!this.logosEl) return;
|
|
530
|
+
this.logosEl.replaceChildren();
|
|
531
|
+
|
|
532
|
+
for (const item of this.displayedItems()) {
|
|
533
|
+
if (isAttributionHtml(item) || !item.logo) continue;
|
|
534
|
+
const img = document.createElement("img");
|
|
535
|
+
img.src = item.logo;
|
|
536
|
+
img.alt = item.attribution;
|
|
537
|
+
img.className = "navara-attr-logo";
|
|
538
|
+
img.draggable = false;
|
|
539
|
+
|
|
540
|
+
// Link the logo only when the source declares a (safe) `logoUrl`, opened in
|
|
541
|
+
// a new tab. A mark that must be shown but not turned into a link omits
|
|
542
|
+
// `logoUrl` and stays a plain `<img>`.
|
|
543
|
+
const href = item.logoUrl ? safeHref(item.logoUrl) : undefined;
|
|
544
|
+
let node: HTMLElement = img;
|
|
545
|
+
if (href) {
|
|
546
|
+
node = createSafeAnchor(href);
|
|
547
|
+
node.appendChild(img);
|
|
548
|
+
}
|
|
549
|
+
this.logosEl.appendChild(node);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** Open / close the popover. Non-modal: no backdrop is added. */
|
|
554
|
+
private setOpen(open?: boolean): void {
|
|
555
|
+
// Record intent before the DOM guard; ensureDom() applies it when the card is built.
|
|
556
|
+
this.isOpen = open ?? !this.isOpen;
|
|
557
|
+
if (!this.card || !this.toggle) return;
|
|
558
|
+
this.card.hidden = !this.isOpen;
|
|
559
|
+
this.toggle.setAttribute("aria-expanded", String(this.isOpen));
|
|
560
|
+
// Keep the accessible name in sync with what activating the button does.
|
|
561
|
+
this.toggle.setAttribute(
|
|
562
|
+
"aria-label",
|
|
563
|
+
this.isOpen ? "Hide attributions" : "Show attributions",
|
|
564
|
+
);
|
|
565
|
+
// On open, refresh once: the per-frame zoom poll is skipped while closed,
|
|
566
|
+
// so the zoom band may be stale.
|
|
567
|
+
if (this.isOpen) {
|
|
568
|
+
if (this.hasZoomBands) this.lastZoomLevel = this.currentZoomLevel();
|
|
569
|
+
this.populateList();
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
private handleKeydown(event: KeyboardEvent): void {
|
|
574
|
+
if (event.key === "Escape" && this.isOpen) this.setOpen(false);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Recompute the zoom and, only when the integer level changed, re-render the
|
|
579
|
+
* list (a "quiet" update — no flicker, no animation). Called on every
|
|
580
|
+
* `preRender`; the level-change gate makes it a no-op unless the band changes.
|
|
581
|
+
*/
|
|
582
|
+
private handlePreRender(): void {
|
|
583
|
+
// Poll only while the popover is open and a source has zoom bands. The
|
|
584
|
+
// `camera.zoom` poll crosses the WASM boundary, so skipping it while closed
|
|
585
|
+
// (or when nothing is zoom-banded) avoids per-frame overhead for users who
|
|
586
|
+
// never open the UI; `setOpen()` refreshes the level on open.
|
|
587
|
+
if (!this.dock || !this.hasZoomBands || !this.isOpen) return;
|
|
588
|
+
const level = this.currentZoomLevel();
|
|
589
|
+
if (level === this.lastZoomLevel) return;
|
|
590
|
+
this.lastZoomLevel = level;
|
|
591
|
+
this.requestRender();
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Current integer Web Mercator zoom level. The engine computes the fractional
|
|
596
|
+
* zoom (altitude/FOV/viewport) in Rust via `camera.zoom`; floor it to a tile
|
|
597
|
+
* level for band matching. `undefined` (e.g. before the first frame) shows all
|
|
598
|
+
* bands.
|
|
599
|
+
*/
|
|
600
|
+
private currentZoomLevel(): number | undefined {
|
|
601
|
+
const z = this.view?.camera.zoom;
|
|
602
|
+
return z === undefined ? undefined : Math.floor(z);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Remove this instance's DOM nodes, document + layer listeners, and state.
|
|
607
|
+
* The shared style element is removed only when the last instance tears down.
|
|
608
|
+
*/
|
|
609
|
+
private teardownDom(): void {
|
|
610
|
+
for (const off of this.layerCleanups) off();
|
|
611
|
+
this.layerCleanups = [];
|
|
612
|
+
|
|
613
|
+
if (this.dock) {
|
|
614
|
+
document.removeEventListener("keydown", this.boundKeydown);
|
|
615
|
+
this.dock.remove();
|
|
616
|
+
}
|
|
617
|
+
this.logosEl?.remove();
|
|
618
|
+
if (this.styleEl) {
|
|
619
|
+
// Only remove the shared style when the last owning instance tears down.
|
|
620
|
+
styleRefCount = Math.max(0, styleRefCount - 1);
|
|
621
|
+
if (styleRefCount === 0) this.styleEl.remove();
|
|
622
|
+
}
|
|
623
|
+
this.dock = undefined;
|
|
624
|
+
this.card = undefined;
|
|
625
|
+
this.listEl = undefined;
|
|
626
|
+
this.logosEl = undefined;
|
|
627
|
+
this.toggle = undefined;
|
|
628
|
+
this.styleEl = undefined;
|
|
629
|
+
this.isOpen = false;
|
|
630
|
+
|
|
631
|
+
this.layerCredits.clear();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Subscribe to the given layers' feature events and merge per-feature credits
|
|
636
|
+
* into the rendered list as features appear / disappear. Detaches any
|
|
637
|
+
* previously registered listeners first, so repeated `add()` / `remove()`
|
|
638
|
+
* don't leak.
|
|
639
|
+
*/
|
|
640
|
+
private trackLayers(layers: Layer[]): void {
|
|
641
|
+
for (const off of this.layerCleanups) off();
|
|
642
|
+
this.layerCleanups = [];
|
|
643
|
+
this.layerCredits.clear();
|
|
644
|
+
|
|
645
|
+
for (const layer of layers) {
|
|
646
|
+
const state = {
|
|
647
|
+
credits: new Map<bigint, string>(),
|
|
648
|
+
visible: new Set<bigint>(),
|
|
649
|
+
};
|
|
650
|
+
this.layerCredits.set(layer.id, state);
|
|
651
|
+
|
|
652
|
+
// Track only features that actually carry a credit: layers can emit many
|
|
653
|
+
// credit-less features (e.g. PLATEAU), and tracking them would grow these
|
|
654
|
+
// sets unbounded and churn the UI with no visible effect.
|
|
655
|
+
const onCreated = ({ featureSetId, credit }: FeatureCreatedParams) => {
|
|
656
|
+
if (!credit) return;
|
|
657
|
+
state.credits.set(featureSetId, credit);
|
|
658
|
+
state.visible.add(featureSetId);
|
|
659
|
+
this.requestRender();
|
|
660
|
+
};
|
|
661
|
+
const onRemoved = ({ featureSetId }: FeatureRemovedParams) => {
|
|
662
|
+
if (!state.credits.has(featureSetId)) return;
|
|
663
|
+
state.credits.delete(featureSetId);
|
|
664
|
+
state.visible.delete(featureSetId);
|
|
665
|
+
this.requestRender();
|
|
666
|
+
};
|
|
667
|
+
const onVisibility = ({
|
|
668
|
+
featureSetId,
|
|
669
|
+
visible,
|
|
670
|
+
}: FeatureVisibilityChangedParams) => {
|
|
671
|
+
if (!state.credits.has(featureSetId)) return;
|
|
672
|
+
if (visible) state.visible.add(featureSetId);
|
|
673
|
+
else state.visible.delete(featureSetId);
|
|
674
|
+
this.requestRender();
|
|
675
|
+
};
|
|
676
|
+
// Assigned below; declared first so `onDeleted` can reference it.
|
|
677
|
+
let detach = () => {};
|
|
678
|
+
// `deleted` fires once for the layer (no per-feature `featureRemoved`).
|
|
679
|
+
// Fully release the layer — drop its credits entry and detach its
|
|
680
|
+
// listeners (and this cleanup) — so a deleted layer doesn't linger until
|
|
681
|
+
// the next add()/remove()/clear() or dispose().
|
|
682
|
+
const onDeleted = () => {
|
|
683
|
+
detach();
|
|
684
|
+
this.layerCleanups = this.layerCleanups.filter((c) => c !== detach);
|
|
685
|
+
this.layerCredits.delete(layer.id);
|
|
686
|
+
this.requestRender();
|
|
687
|
+
};
|
|
688
|
+
detach = () => {
|
|
689
|
+
layer.off("featureCreated", onCreated);
|
|
690
|
+
layer.off("featureRemoved", onRemoved);
|
|
691
|
+
layer.off("featureVisibilityChanged", onVisibility);
|
|
692
|
+
layer.off("deleted", onDeleted);
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
layer.on("featureCreated", onCreated);
|
|
696
|
+
layer.on("featureRemoved", onRemoved);
|
|
697
|
+
layer.on("featureVisibilityChanged", onVisibility);
|
|
698
|
+
layer.on("deleted", onDeleted);
|
|
699
|
+
this.layerCleanups.push(detach);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./AttributionPlugin";
|
package/src/quality.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Quality = "low" | "medium" | "high" | "ultra";
|