@npm9912/v-map 0.1.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 +21 -0
- package/README.md +185 -0
- package/dist/cjs/_commonjsHelpers-B83fTs8d.js +36 -0
- package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
- package/dist/cjs/cesium-provider-BiFFyAl9.js +2598 -0
- package/dist/cjs/deck-provider-Ctq3Q8a1.js +47824 -0
- package/dist/cjs/geotiff-CEwvF9cG.js +47 -0
- package/dist/cjs/geotiff-source-RaNzzWkC.js +1522 -0
- package/dist/cjs/index-B1oGO1g-.js +10658 -0
- package/dist/cjs/index-B8LHqjyg.js +1765 -0
- package/dist/cjs/index-BIL4VsgP.js +310 -0
- package/dist/cjs/index-Blku2QY8.js +167 -0
- package/dist/cjs/index-CJvvX4yx.js +21 -0
- package/dist/cjs/index-CbVT-Con.js +699 -0
- package/dist/cjs/index-ISOEpMC3.js +20478 -0
- package/dist/cjs/index-JSwBbvGA.js +1621 -0
- package/dist/cjs/index.browser-DQhD8Jwl.js +6873 -0
- package/dist/cjs/index.cjs.js +2 -0
- package/dist/cjs/layer-extension-B_olS0rc.js +65 -0
- package/dist/cjs/leaflet-provider-DOqfs7g5.js +1815 -0
- package/dist/cjs/loader.cjs.js +13 -0
- package/dist/cjs/main-dist-7TykwFci.js +2655 -0
- package/dist/cjs/messages-D7h4m8Tx.js +186 -0
- package/dist/cjs/openlayers-provider-Dfeg6L4n.js +1604 -0
- package/dist/cjs/polygon-layer-B9PrN7vr.js +1300 -0
- package/dist/cjs/scenegraph-layer-DwNoxQdi.js +2530 -0
- package/dist/cjs/styleconfig-CVRqArk-.js +23 -0
- package/dist/cjs/v-map-builder.cjs.entry.js +3786 -0
- package/dist/cjs/v-map-layer-geojson_12.cjs.entry.js +40894 -0
- package/dist/cjs/v-map-layer-helper-iAzxAg9I.js +285 -0
- package/dist/cjs/v-map-layer-terrain-geotiff.cjs.entry.js +258 -0
- package/dist/cjs/v-map-layercontrol.cjs.entry.js +247 -0
- package/dist/cjs/v-map.cjs.js +25 -0
- package/dist/cjs/v-map.v-map-layer-osm.v-map-layergroup-BsXp3BoL.js +582 -0
- package/dist/cjs/v-map_3.cjs.entry.js +12 -0
- package/dist/collection/collection-manifest.json +30 -0
- package/dist/collection/components/v-map/v-map.css +3 -0
- package/dist/collection/components/v-map/v-map.js +467 -0
- package/dist/collection/components/v-map/v-map.test.js +33 -0
- package/dist/collection/components/v-map-builder/v-map-builder.css +1 -0
- package/dist/collection/components/v-map-builder/v-map-builder.js +913 -0
- package/dist/collection/components/v-map-builder/v-map-builder.test.js +56 -0
- package/dist/collection/components/v-map-layer-geojson/v-map-layer-geojson.js +862 -0
- package/dist/collection/components/v-map-layer-geojson/v-map-layer-geojson.test.js +42 -0
- package/dist/collection/components/v-map-layer-geotiff/v-map-layer-geotiff.css +4 -0
- package/dist/collection/components/v-map-layer-geotiff/v-map-layer-geotiff.js +500 -0
- package/dist/collection/components/v-map-layer-geotiff/v-map-layer-geotiff.test.js +38 -0
- package/dist/collection/components/v-map-layer-google/v-map-layer-google.css +1 -0
- package/dist/collection/components/v-map-layer-google/v-map-layer-google.js +442 -0
- package/dist/collection/components/v-map-layer-osm/error-api.test.js +108 -0
- package/dist/collection/components/v-map-layer-osm/v-map-layer-osm.css +4 -0
- package/dist/collection/components/v-map-layer-osm/v-map-layer-osm.js +311 -0
- package/dist/collection/components/v-map-layer-osm/v-map-layer-osm.test.js +36 -0
- package/dist/collection/components/v-map-layer-scatterplot/v-map-layer-scatterplot.css +1 -0
- package/dist/collection/components/v-map-layer-scatterplot/v-map-layer-scatterplot.js +305 -0
- package/dist/collection/components/v-map-layer-terrain/v-map-layer-terrain.css +3 -0
- package/dist/collection/components/v-map-layer-terrain/v-map-layer-terrain.js +548 -0
- package/dist/collection/components/v-map-layer-terrain/v-map-layer-terrain.test.js +36 -0
- package/dist/collection/components/v-map-layer-terrain-geotiff/v-map-layer-terrain-geotiff.css +3 -0
- package/dist/collection/components/v-map-layer-terrain-geotiff/v-map-layer-terrain-geotiff.js +735 -0
- package/dist/collection/components/v-map-layer-terrain-geotiff/v-map-layer-terrain-geotiff.test.js +42 -0
- package/dist/collection/components/v-map-layer-tile3d/v-map-layer-tile3d.css +3 -0
- package/dist/collection/components/v-map-layer-tile3d/v-map-layer-tile3d.js +449 -0
- package/dist/collection/components/v-map-layer-tile3d/v-map-layer-tile3d.test.js +50 -0
- package/dist/collection/components/v-map-layer-wcs/v-map-layer-wcs.css +1 -0
- package/dist/collection/components/v-map-layer-wcs/v-map-layer-wcs.js +448 -0
- package/dist/collection/components/v-map-layer-wcs/v-map-layer-wcs.test.js +39 -0
- package/dist/collection/components/v-map-layer-wfs/v-map-layer-wfs.css +1 -0
- package/dist/collection/components/v-map-layer-wfs/v-map-layer-wfs.js +489 -0
- package/dist/collection/components/v-map-layer-wfs/v-map-layer-wfs.test.js +43 -0
- package/dist/collection/components/v-map-layer-wkt/v-map-layer-wkt.css +1 -0
- package/dist/collection/components/v-map-layer-wkt/v-map-layer-wkt.js +811 -0
- package/dist/collection/components/v-map-layer-wkt/v-map-layer-wkt.test.js +34 -0
- package/dist/collection/components/v-map-layer-wms/v-map-layer-wms.css +1 -0
- package/dist/collection/components/v-map-layer-wms/v-map-layer-wms.js +453 -0
- package/dist/collection/components/v-map-layer-wms/v-map-layer-wms.test.js +36 -0
- package/dist/collection/components/v-map-layer-xyz/v-map-layer-xyz.css +1 -0
- package/dist/collection/components/v-map-layer-xyz/v-map-layer-xyz.js +331 -0
- package/dist/collection/components/v-map-layer-xyz/v-map-layer-xyz.test.js +28 -0
- package/dist/collection/components/v-map-layercontrol/v-map-layercontrol.css +74 -0
- package/dist/collection/components/v-map-layercontrol/v-map-layercontrol.js +277 -0
- package/dist/collection/components/v-map-layercontrol/v-map-layercontrol.test.js +134 -0
- package/dist/collection/components/v-map-layergroup/v-map-layergroup.css +4 -0
- package/dist/collection/components/v-map-layergroup/v-map-layergroup.js +212 -0
- package/dist/collection/components/v-map-layergroup/v-map-layergroup.test.js +39 -0
- package/dist/collection/components/v-map-style/v-map-style.css +38 -0
- package/dist/collection/components/v-map-style/v-map-style.js +492 -0
- package/dist/collection/components/v-map-style/v-map-style.unit.js +62 -0
- package/dist/collection/index.js +1 -0
- package/dist/collection/layer/v-map-layer-helper.js +281 -0
- package/dist/collection/layer/v-map-layer-helper.unit.js +234 -0
- package/dist/collection/lib/cesium-loader.js +58 -0
- package/dist/collection/lib/ensure-importmap.js +12 -0
- package/dist/collection/lib/ensure-importmap.unit.js +57 -0
- package/dist/collection/lib/versions.gen.js +6 -0
- package/dist/collection/lib/vstyle.js +8 -0
- package/dist/collection/map-provider/cesium/CesiumGeoTIFFTerrainProvider.js +285 -0
- package/dist/collection/map-provider/cesium/CesiumLayerGroups.js +159 -0
- package/dist/collection/map-provider/cesium/GeoTIFFImageryProvider.js +192 -0
- package/dist/collection/map-provider/cesium/GeoTIFFImageryProvider.test.js +57 -0
- package/dist/collection/map-provider/cesium/cesium-provider.js +1408 -0
- package/dist/collection/map-provider/cesium/i-layer.js +1 -0
- package/dist/collection/map-provider/cesium/layer-manager.js +467 -0
- package/dist/collection/map-provider/deck/DeckGLGeoTIFFLayer.js +483 -0
- package/dist/collection/map-provider/deck/DeckGLGeoTIFFTerrainLayer.js +410 -0
- package/dist/collection/map-provider/deck/LayerGroupWithModel.js +169 -0
- package/dist/collection/map-provider/deck/LayerGroups.js +192 -0
- package/dist/collection/map-provider/deck/LayerModel.js +1 -0
- package/dist/collection/map-provider/deck/RenderableGroup.js +1 -0
- package/dist/collection/map-provider/deck/deck-provider.js +1563 -0
- package/dist/collection/map-provider/geotiff/geotiff-source.js +172 -0
- package/dist/collection/map-provider/geotiff/utils/AABB2D.js +24 -0
- package/dist/collection/map-provider/geotiff/utils/BVHNode2D.js +166 -0
- package/dist/collection/map-provider/geotiff/utils/GeoTIFFTileProcessor.js +484 -0
- package/dist/collection/map-provider/geotiff/utils/Triangle.js +1 -0
- package/dist/collection/map-provider/geotiff/utils/Triangulation.js +321 -0
- package/dist/collection/map-provider/geotiff/utils/colormap-utils.js +190 -0
- package/dist/collection/map-provider/geotiff/utils/normalization-utils.js +122 -0
- package/dist/collection/map-provider/geotiff/utils/sampling-utils.js +108 -0
- package/dist/collection/map-provider/leaflet/GeoTIFFGridLayer.js +147 -0
- package/dist/collection/map-provider/leaflet/WCSGridLayer.js +124 -0
- package/dist/collection/map-provider/leaflet/google-map-tiles-layer.js +352 -0
- package/dist/collection/map-provider/leaflet/leaflet-helpers.js +94 -0
- package/dist/collection/map-provider/leaflet/leaflet-provider.js +1095 -0
- package/dist/collection/map-provider/ol/CustomGeoTiff.js +145 -0
- package/dist/collection/map-provider/ol/openlayers-helper.js +26 -0
- package/dist/collection/map-provider/ol/openlayers-provider.js +1427 -0
- package/dist/collection/map-provider/provider-factory.js +44 -0
- package/dist/collection/map-provider/provider-factory.unit.js +66 -0
- package/dist/collection/testing/browser-test-utils.js +49 -0
- package/dist/collection/testing/e2e-testing.js +122 -0
- package/dist/collection/testing/e2e-utils.js +70 -0
- package/dist/collection/testing/geotiff-test-server.js +100 -0
- package/dist/collection/testing/mocks/geostyler-lyrx-parser.js +12 -0
- package/dist/collection/testing/mocks/geostyler-mapbox-parser.js +12 -0
- package/dist/collection/testing/mocks/geostyler-qgis-parser.js +12 -0
- package/dist/collection/testing/mocks/geostyler-sld-parser.js +13 -0
- package/dist/collection/testing/mocks/geostyler-style.js +5 -0
- package/dist/collection/testing/setupTests.browser.js +1 -0
- package/dist/collection/testing/setupTests.stencil.js +20 -0
- package/dist/collection/testing/setupTests.vitest.js +59 -0
- package/dist/collection/testing/stencil-testing-wrapper.js +43 -0
- package/dist/collection/testing/styleMock.js +1 -0
- package/dist/collection/types/color.js +1 -0
- package/dist/collection/types/cssmode.js +1 -0
- package/dist/collection/types/flavour.js +1 -0
- package/dist/collection/types/layerconfig.js +1 -0
- package/dist/collection/types/lonlat.js +1 -0
- package/dist/collection/types/mapinitoptions.js +1 -0
- package/dist/collection/types/mapprovider.js +1 -0
- package/dist/collection/types/provideroptions.js +1 -0
- package/dist/collection/types/styleconfig.js +19 -0
- package/dist/collection/types/styling.js +13 -0
- package/dist/collection/types/styling.unit.js +37 -0
- package/dist/collection/types/vmaplayer.js +1 -0
- package/dist/collection/utils/async-mutex.js +28 -0
- package/dist/collection/utils/diff.js +142 -0
- package/dist/collection/utils/diff.unit.js +59 -0
- package/dist/collection/utils/dom-env.js +43 -0
- package/dist/collection/utils/dom-env.unit.js +92 -0
- package/dist/collection/utils/events.js +8 -0
- package/dist/collection/utils/logger.js +183 -0
- package/dist/collection/utils/logger.unit.js +98 -0
- package/dist/collection/utils/messages.js +12 -0
- package/dist/collection/utils/spatial-utils.js +27 -0
- package/dist/collection/utils/spatial-utils.unit.js +24 -0
- package/dist/components/_commonjsHelpers.js +1 -0
- package/dist/components/cesium-provider.js +1 -0
- package/dist/components/deck-provider.js +1 -0
- package/dist/components/events.js +1 -0
- package/dist/components/geotiff-source.js +1 -0
- package/dist/components/geotiff.js +4 -0
- package/dist/components/index.browser.js +15 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index2.js +1 -0
- package/dist/components/index3.js +1 -0
- package/dist/components/index4.js +1 -0
- package/dist/components/index5.js +1 -0
- package/dist/components/index6.js +1 -0
- package/dist/components/index7.js +1 -0
- package/dist/components/index8.js +7 -0
- package/dist/components/layer-extension.js +1 -0
- package/dist/components/leaflet-provider.js +1 -0
- package/dist/components/main-dist.js +1 -0
- package/dist/components/messages.js +1 -0
- package/dist/components/openlayers-provider.js +1 -0
- package/dist/components/polygon-layer.js +1 -0
- package/dist/components/scenegraph-layer.js +1 -0
- package/dist/components/styleconfig.js +1 -0
- package/dist/components/styling.js +1 -0
- package/dist/components/v-map-builder.d.ts +11 -0
- package/dist/components/v-map-builder.js +2 -0
- package/dist/components/v-map-layer-geojson.d.ts +11 -0
- package/dist/components/v-map-layer-geojson.js +1 -0
- package/dist/components/v-map-layer-geojson2.js +1 -0
- package/dist/components/v-map-layer-geotiff.d.ts +11 -0
- package/dist/components/v-map-layer-geotiff.js +1 -0
- package/dist/components/v-map-layer-geotiff2.js +1 -0
- package/dist/components/v-map-layer-google.d.ts +11 -0
- package/dist/components/v-map-layer-google.js +1 -0
- package/dist/components/v-map-layer-google2.js +1 -0
- package/dist/components/v-map-layer-helper.js +1 -0
- package/dist/components/v-map-layer-osm.d.ts +11 -0
- package/dist/components/v-map-layer-osm.js +1 -0
- package/dist/components/v-map-layer-osm2.js +1 -0
- package/dist/components/v-map-layer-scatterplot.d.ts +11 -0
- package/dist/components/v-map-layer-scatterplot.js +1 -0
- package/dist/components/v-map-layer-scatterplot2.js +1 -0
- package/dist/components/v-map-layer-terrain-geotiff.d.ts +11 -0
- package/dist/components/v-map-layer-terrain-geotiff.js +1 -0
- package/dist/components/v-map-layer-terrain.d.ts +11 -0
- package/dist/components/v-map-layer-terrain.js +1 -0
- package/dist/components/v-map-layer-terrain2.js +1 -0
- package/dist/components/v-map-layer-tile3d.d.ts +11 -0
- package/dist/components/v-map-layer-tile3d.js +1 -0
- package/dist/components/v-map-layer-tile3d2.js +1 -0
- package/dist/components/v-map-layer-wcs.d.ts +11 -0
- package/dist/components/v-map-layer-wcs.js +1 -0
- package/dist/components/v-map-layer-wcs2.js +1 -0
- package/dist/components/v-map-layer-wfs.d.ts +11 -0
- package/dist/components/v-map-layer-wfs.js +1 -0
- package/dist/components/v-map-layer-wfs2.js +1 -0
- package/dist/components/v-map-layer-wkt.d.ts +11 -0
- package/dist/components/v-map-layer-wkt.js +1 -0
- package/dist/components/v-map-layer-wkt2.js +1 -0
- package/dist/components/v-map-layer-wms.d.ts +11 -0
- package/dist/components/v-map-layer-wms.js +1 -0
- package/dist/components/v-map-layer-wms2.js +1 -0
- package/dist/components/v-map-layer-xyz.d.ts +11 -0
- package/dist/components/v-map-layer-xyz.js +1 -0
- package/dist/components/v-map-layer-xyz2.js +1 -0
- package/dist/components/v-map-layercontrol.d.ts +11 -0
- package/dist/components/v-map-layercontrol.js +1 -0
- package/dist/components/v-map-layergroup.d.ts +11 -0
- package/dist/components/v-map-layergroup.js +1 -0
- package/dist/components/v-map-layergroup2.js +1 -0
- package/dist/components/v-map-style.d.ts +11 -0
- package/dist/components/v-map-style.js +1 -0
- package/dist/components/v-map-style2.js +10 -0
- package/dist/components/v-map.d.ts +11 -0
- package/dist/components/v-map.js +1 -0
- package/dist/components/v-map2.js +1 -0
- package/dist/esm/_commonjsHelpers-E-ZsRS8r.js +32 -0
- package/dist/esm/app-globals-DQuL1Twl.js +3 -0
- package/dist/esm/cesium-provider-BJfAup3w.js +2596 -0
- package/dist/esm/deck-provider-C7U9VDEq.js +47709 -0
- package/dist/esm/geotiff-BEWxTIfH.js +45 -0
- package/dist/esm/geotiff-source-esnDnC-u.js +1516 -0
- package/dist/esm/index-B1zwA4IC.js +685 -0
- package/dist/esm/index-BBpiaTpT.js +165 -0
- package/dist/esm/index-BIEmlzCf.js +1697 -0
- package/dist/esm/index-BUHa4Jj0.js +307 -0
- package/dist/esm/index-DbSdn93t.js +20461 -0
- package/dist/esm/index-RpJarvr_.js +10656 -0
- package/dist/esm/index-jN06TXUp.js +14 -0
- package/dist/esm/index-jzneDarq.js +1613 -0
- package/dist/esm/index.browser-DhQAXuA7.js +6860 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/layer-extension-CZXK5goK.js +63 -0
- package/dist/esm/leaflet-provider-Q41TB6ku.js +1794 -0
- package/dist/esm/loader.js +11 -0
- package/dist/esm/main-dist-CwnA7_Xn.js +2652 -0
- package/dist/esm/messages-CMKJzsgL.js +180 -0
- package/dist/esm/openlayers-provider-CMsDsQTQ.js +1602 -0
- package/dist/esm/polygon-layer-ByhxGhWC.js +1295 -0
- package/dist/esm/scenegraph-layer-09K_B6DT.js +2526 -0
- package/dist/esm/styleconfig-B-bAcABs.js +21 -0
- package/dist/esm/v-map-builder.entry.js +3784 -0
- package/dist/esm/v-map-layer-geojson_12.entry.js +40881 -0
- package/dist/esm/v-map-layer-helper-Dys44Cgo.js +283 -0
- package/dist/esm/v-map-layer-terrain-geotiff.entry.js +256 -0
- package/dist/esm/v-map-layercontrol.entry.js +245 -0
- package/dist/esm/v-map.js +21 -0
- package/dist/esm/v-map.v-map-layer-osm.v-map-layergroup-B4pFHuSf.js +572 -0
- package/dist/esm/v-map_3.entry.js +4 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/types/cesium-augment.d.ts +5 -0
- package/dist/types/components/v-map/v-map.d.ts +70 -0
- package/dist/types/components/v-map/v-map.test.d.ts +1 -0
- package/dist/types/components/v-map-builder/v-map-builder.d.ts +48 -0
- package/dist/types/components/v-map-builder/v-map-builder.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-geojson/v-map-layer-geojson.d.ts +129 -0
- package/dist/types/components/v-map-layer-geojson/v-map-layer-geojson.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-geotiff/v-map-layer-geotiff.d.ts +74 -0
- package/dist/types/components/v-map-layer-geotiff/v-map-layer-geotiff.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-google/v-map-layer-google.d.ts +78 -0
- package/dist/types/components/v-map-layer-osm/error-api.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-osm/v-map-layer-osm.d.ts +50 -0
- package/dist/types/components/v-map-layer-osm/v-map-layer-osm.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-scatterplot/v-map-layer-scatterplot.d.ts +54 -0
- package/dist/types/components/v-map-layer-terrain/v-map-layer-terrain.d.ts +74 -0
- package/dist/types/components/v-map-layer-terrain/v-map-layer-terrain.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-terrain-geotiff/v-map-layer-terrain-geotiff.d.ts +117 -0
- package/dist/types/components/v-map-layer-terrain-geotiff/v-map-layer-terrain-geotiff.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-tile3d/v-map-layer-tile3d.d.ts +69 -0
- package/dist/types/components/v-map-layer-tile3d/v-map-layer-tile3d.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-wcs/v-map-layer-wcs.d.ts +47 -0
- package/dist/types/components/v-map-layer-wcs/v-map-layer-wcs.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-wfs/v-map-layer-wfs.d.ts +59 -0
- package/dist/types/components/v-map-layer-wfs/v-map-layer-wfs.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-wkt/v-map-layer-wkt.d.ts +132 -0
- package/dist/types/components/v-map-layer-wkt/v-map-layer-wkt.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-wms/v-map-layer-wms.d.ts +76 -0
- package/dist/types/components/v-map-layer-wms/v-map-layer-wms.test.d.ts +1 -0
- package/dist/types/components/v-map-layer-xyz/v-map-layer-xyz.d.ts +59 -0
- package/dist/types/components/v-map-layer-xyz/v-map-layer-xyz.test.d.ts +1 -0
- package/dist/types/components/v-map-layercontrol/v-map-layercontrol.d.ts +44 -0
- package/dist/types/components/v-map-layercontrol/v-map-layercontrol.test.d.ts +1 -0
- package/dist/types/components/v-map-layergroup/v-map-layergroup.d.ts +31 -0
- package/dist/types/components/v-map-layergroup/v-map-layergroup.test.d.ts +1 -0
- package/dist/types/components/v-map-style/v-map-style.d.ts +75 -0
- package/dist/types/components/v-map-style/v-map-style.unit.d.ts +1 -0
- package/dist/types/components.d.ts +2391 -0
- package/dist/types/globals.d.ts +16 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/layer/v-map-layer-helper.d.ts +45 -0
- package/dist/types/layer/v-map-layer-helper.unit.d.ts +1 -0
- package/dist/types/leaflet-augment.d.ts +15 -0
- package/dist/types/lib/cesium-loader.d.ts +3 -0
- package/dist/types/lib/ensure-importmap.d.ts +3 -0
- package/dist/types/lib/ensure-importmap.unit.d.ts +1 -0
- package/dist/types/lib/versions.gen.d.ts +5 -0
- package/dist/types/lib/vstyle.d.ts +44 -0
- package/dist/types/map-provider/cesium/CesiumGeoTIFFTerrainProvider.d.ts +92 -0
- package/dist/types/map-provider/cesium/CesiumLayerGroups.d.ts +64 -0
- package/dist/types/map-provider/cesium/GeoTIFFImageryProvider.d.ts +75 -0
- package/dist/types/map-provider/cesium/GeoTIFFImageryProvider.test.d.ts +1 -0
- package/dist/types/map-provider/cesium/cesium-provider.d.ts +87 -0
- package/dist/types/map-provider/cesium/i-layer.d.ts +11 -0
- package/dist/types/map-provider/cesium/layer-manager.d.ts +31 -0
- package/dist/types/map-provider/deck/DeckGLGeoTIFFLayer.d.ts +91 -0
- package/dist/types/map-provider/deck/DeckGLGeoTIFFTerrainLayer.d.ts +82 -0
- package/dist/types/map-provider/deck/LayerGroupWithModel.d.ts +55 -0
- package/dist/types/map-provider/deck/LayerGroups.d.ts +63 -0
- package/dist/types/map-provider/deck/LayerModel.d.ts +8 -0
- package/dist/types/map-provider/deck/RenderableGroup.d.ts +20 -0
- package/dist/types/map-provider/deck/deck-provider.d.ts +92 -0
- package/dist/types/map-provider/geotiff/geotiff-source.d.ts +30 -0
- package/dist/types/map-provider/geotiff/utils/AABB2D.d.ts +28 -0
- package/dist/types/map-provider/geotiff/utils/BVHNode2D.d.ts +36 -0
- package/dist/types/map-provider/geotiff/utils/GeoTIFFTileProcessor.d.ts +116 -0
- package/dist/types/map-provider/geotiff/utils/Triangle.d.ts +5 -0
- package/dist/types/map-provider/geotiff/utils/Triangulation.d.ts +94 -0
- package/dist/types/map-provider/geotiff/utils/colormap-utils.d.ts +47 -0
- package/dist/types/map-provider/geotiff/utils/normalization-utils.d.ts +39 -0
- package/dist/types/map-provider/geotiff/utils/sampling-utils.d.ts +13 -0
- package/dist/types/map-provider/leaflet/GeoTIFFGridLayer.d.ts +34 -0
- package/dist/types/map-provider/leaflet/WCSGridLayer.d.ts +38 -0
- package/dist/types/map-provider/leaflet/google-map-tiles-layer.d.ts +73 -0
- package/dist/types/map-provider/leaflet/leaflet-helpers.d.ts +6 -0
- package/dist/types/map-provider/leaflet/leaflet-provider.d.ts +73 -0
- package/dist/types/map-provider/ol/CustomGeoTiff.d.ts +14 -0
- package/dist/types/map-provider/ol/openlayers-helper.d.ts +2 -0
- package/dist/types/map-provider/ol/openlayers-provider.d.ts +80 -0
- package/dist/types/map-provider/provider-factory.d.ts +12 -0
- package/dist/types/map-provider/provider-factory.unit.d.ts +1 -0
- package/dist/types/namespaces.d.ts +3 -0
- package/dist/types/ol-augment.d.ts +3 -0
- package/dist/types/ol-override.d.ts +7 -0
- package/dist/types/ol.d.ts +10 -0
- package/dist/types/stencil-public-runtime.d.ts +1860 -0
- package/dist/types/testing/browser-test-utils.d.ts +6 -0
- package/dist/types/testing/e2e-testing.d.ts +5 -0
- package/dist/types/testing/e2e-utils.d.ts +4 -0
- package/dist/types/testing/geotiff-test-server.d.ts +5 -0
- package/dist/types/testing/mocks/geostyler-lyrx-parser.d.ts +11 -0
- package/dist/types/testing/mocks/geostyler-mapbox-parser.d.ts +11 -0
- package/dist/types/testing/mocks/geostyler-qgis-parser.d.ts +11 -0
- package/dist/types/testing/mocks/geostyler-sld-parser.d.ts +11 -0
- package/dist/types/testing/mocks/geostyler-style.d.ts +5 -0
- package/dist/types/testing/setupTests.browser.d.ts +1 -0
- package/dist/types/testing/setupTests.stencil.d.ts +1 -0
- package/dist/types/testing/setupTests.vitest.d.ts +1 -0
- package/dist/types/testing/stencil-testing-wrapper.d.ts +3 -0
- package/dist/types/types/color.d.ts +1 -0
- package/dist/types/types/cssmode.d.ts +1 -0
- package/dist/types/types/flavour.d.ts +1 -0
- package/dist/types/types/layerconfig.d.ts +207 -0
- package/dist/types/types/lonlat.d.ts +1 -0
- package/dist/types/types/mapinitoptions.d.ts +4 -0
- package/dist/types/types/mapprovider.d.ts +46 -0
- package/dist/types/types/provideroptions.d.ts +8 -0
- package/dist/types/types/styleconfig.d.ts +27 -0
- package/dist/types/types/styling.d.ts +24 -0
- package/dist/types/types/styling.unit.d.ts +1 -0
- package/dist/types/types/vmaplayer.d.ts +10 -0
- package/dist/types/utils/async-mutex.d.ts +7 -0
- package/dist/types/utils/diff.d.ts +64 -0
- package/dist/types/utils/diff.unit.d.ts +1 -0
- package/dist/types/utils/dom-env.d.ts +5 -0
- package/dist/types/utils/dom-env.unit.d.ts +1 -0
- package/dist/types/utils/events.d.ts +29 -0
- package/dist/types/utils/logger.d.ts +47 -0
- package/dist/types/utils/logger.unit.d.ts +1 -0
- package/dist/types/utils/messages.d.ts +12 -0
- package/dist/types/utils/spatial-utils.d.ts +6 -0
- package/dist/types/utils/spatial-utils.unit.d.ts +1 -0
- package/dist/types/versions.d.ts +7 -0
- package/dist/v-map/index.esm.js +0 -0
- package/dist/v-map/p--vVleK-M.js +1 -0
- package/dist/v-map/p-09d10db0.entry.js +1 -0
- package/dist/v-map/p-5eba6058.entry.js +10 -0
- package/dist/v-map/p-6b102336.entry.js +1 -0
- package/dist/v-map/p-B-bAcABs.js +1 -0
- package/dist/v-map/p-BBpiaTpT.js +1 -0
- package/dist/v-map/p-BdijL4Av.js +1 -0
- package/dist/v-map/p-Be3r33VF.js +4 -0
- package/dist/v-map/p-BeFu0ap4.js +1 -0
- package/dist/v-map/p-BxFJezdK.js +1 -0
- package/dist/v-map/p-CMKJzsgL.js +1 -0
- package/dist/v-map/p-CXfA_q8m.js +1 -0
- package/dist/v-map/p-CZqY0yW4.js +1 -0
- package/dist/v-map/p-CafTHT9i.js +1 -0
- package/dist/v-map/p-DCTHyf58.js +1 -0
- package/dist/v-map/p-DQuL1Twl.js +1 -0
- package/dist/v-map/p-DR9McdNX.js +1 -0
- package/dist/v-map/p-Dckgonw8.js +1 -0
- package/dist/v-map/p-DhQAXuA7.js +15 -0
- package/dist/v-map/p-DmICdG34.js +7 -0
- package/dist/v-map/p-DrOQ9V4h.js +1 -0
- package/dist/v-map/p-DvHXtWUg.js +1 -0
- package/dist/v-map/p-E-ZsRS8r.js +1 -0
- package/dist/v-map/p-MyTSFnEk.js +1 -0
- package/dist/v-map/p-RpJarvr_.js +1 -0
- package/dist/v-map/p-WaMDUuAz.js +1 -0
- package/dist/v-map/p-aa410e64.entry.js +2 -0
- package/dist/v-map/p-c21c93fe.entry.js +1 -0
- package/dist/v-map/p-jzneDarq.js +2 -0
- package/dist/v-map/p-uiIP-taz.js +1 -0
- package/dist/v-map/v-map.esm.js +1 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +193 -0
|
@@ -0,0 +1,1516 @@
|
|
|
1
|
+
import { w as warn, l as log } from './messages-CMKJzsgL.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ColorMap utilities for GeoTIFF visualization
|
|
5
|
+
* Extracted for testability
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Predefined color maps
|
|
9
|
+
*/
|
|
10
|
+
const PREDEFINED_COLORMAPS = {
|
|
11
|
+
grayscale: [
|
|
12
|
+
{ value: 0.0, color: [0, 0, 0] },
|
|
13
|
+
{ value: 1.0, color: [255, 255, 255] },
|
|
14
|
+
],
|
|
15
|
+
viridis: [
|
|
16
|
+
{ value: 0.0, color: [68, 1, 84] },
|
|
17
|
+
{ value: 0.25, color: [59, 82, 139] },
|
|
18
|
+
{ value: 0.5, color: [33, 145, 140] },
|
|
19
|
+
{ value: 0.75, color: [94, 201, 98] },
|
|
20
|
+
{ value: 1.0, color: [253, 231, 37] },
|
|
21
|
+
],
|
|
22
|
+
terrain: [
|
|
23
|
+
{ value: 0.0, color: [0, 128, 0] }, // dark green (low)
|
|
24
|
+
{ value: 0.25, color: [139, 195, 74] }, // light green
|
|
25
|
+
{ value: 0.5, color: [255, 235, 59] }, // yellow
|
|
26
|
+
{ value: 0.75, color: [255, 152, 0] }, // orange
|
|
27
|
+
{ value: 1.0, color: [255, 255, 255] }, // white (high)
|
|
28
|
+
],
|
|
29
|
+
turbo: [
|
|
30
|
+
{ value: 0.0, color: [48, 18, 59] },
|
|
31
|
+
{ value: 0.2, color: [33, 102, 172] },
|
|
32
|
+
{ value: 0.4, color: [68, 190, 112] },
|
|
33
|
+
{ value: 0.6, color: [253, 231, 37] },
|
|
34
|
+
{ value: 0.8, color: [234, 51, 35] },
|
|
35
|
+
{ value: 1.0, color: [122, 4, 3] },
|
|
36
|
+
],
|
|
37
|
+
rainbow: [
|
|
38
|
+
{ value: 0.0, color: [148, 0, 211] }, // violet
|
|
39
|
+
{ value: 0.2, color: [0, 0, 255] }, // blue
|
|
40
|
+
{ value: 0.4, color: [0, 255, 0] }, // green
|
|
41
|
+
{ value: 0.6, color: [255, 255, 0] }, // yellow
|
|
42
|
+
{ value: 0.8, color: [255, 127, 0] }, // orange
|
|
43
|
+
{ value: 1.0, color: [255, 0, 0] }, // red
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Parse a hex color string to RGB array
|
|
48
|
+
* @param hexColor - Hex color string (e.g., "#FF0000" or "#F00")
|
|
49
|
+
* @returns RGB array [r, g, b] with values 0-255
|
|
50
|
+
*/
|
|
51
|
+
function parseHexColor(hexColor) {
|
|
52
|
+
let hex = hexColor.trim();
|
|
53
|
+
if (hex.startsWith('#')) {
|
|
54
|
+
hex = hex.slice(1);
|
|
55
|
+
}
|
|
56
|
+
// Handle shorthand hex (#RGB -> #RRGGBB)
|
|
57
|
+
if (hex.length === 3) {
|
|
58
|
+
hex = hex
|
|
59
|
+
.split('')
|
|
60
|
+
.map(c => c + c)
|
|
61
|
+
.join('');
|
|
62
|
+
}
|
|
63
|
+
if (hex.length !== 6) {
|
|
64
|
+
warn(`Invalid hex color: ${hexColor}, using black`);
|
|
65
|
+
return [0, 0, 0];
|
|
66
|
+
}
|
|
67
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
68
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
69
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
70
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) {
|
|
71
|
+
warn(`Invalid hex color: ${hexColor}, using black`);
|
|
72
|
+
return [0, 0, 0];
|
|
73
|
+
}
|
|
74
|
+
return [r, g, b];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Convert GeoStyler ColorMap to internal ColorStop array
|
|
78
|
+
* @param geoStylerColorMap - GeoStyler ColorMap object
|
|
79
|
+
* @param valueRange - Optional value range [min, max] for normalization
|
|
80
|
+
* @returns Object with ColorStop array and computed range
|
|
81
|
+
*/
|
|
82
|
+
function convertGeoStylerColorMap(geoStylerColorMap, valueRange) {
|
|
83
|
+
const entries = geoStylerColorMap.colorMapEntries || [];
|
|
84
|
+
if (entries.length === 0) {
|
|
85
|
+
warn('GeoStyler ColorMap has no entries, using grayscale');
|
|
86
|
+
return { stops: PREDEFINED_COLORMAPS.grayscale };
|
|
87
|
+
}
|
|
88
|
+
// Extract quantity values for normalization
|
|
89
|
+
const quantities = entries
|
|
90
|
+
.map(e => e.quantity)
|
|
91
|
+
.filter((q) => typeof q === 'number');
|
|
92
|
+
let minVal;
|
|
93
|
+
let maxVal;
|
|
94
|
+
if (valueRange) {
|
|
95
|
+
[minVal, maxVal] = valueRange;
|
|
96
|
+
}
|
|
97
|
+
else if (quantities.length > 0) {
|
|
98
|
+
minVal = Math.min(...quantities);
|
|
99
|
+
maxVal = Math.max(...quantities);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// No quantities and no valueRange - assume normalized 0-1
|
|
103
|
+
minVal = 0;
|
|
104
|
+
maxVal = 1;
|
|
105
|
+
}
|
|
106
|
+
// Avoid division by zero
|
|
107
|
+
if (maxVal === minVal) {
|
|
108
|
+
maxVal = minVal + 1;
|
|
109
|
+
}
|
|
110
|
+
const stops = entries.map(entry => {
|
|
111
|
+
const quantity = typeof entry.quantity === 'number' ? entry.quantity : 0;
|
|
112
|
+
const normalizedValue = (quantity - minVal) / (maxVal - minVal);
|
|
113
|
+
// Parse color (handle both string and Expression<string>)
|
|
114
|
+
const colorStr = typeof entry.color === 'string' ? entry.color : String(entry.color);
|
|
115
|
+
const color = parseHexColor(colorStr);
|
|
116
|
+
return {
|
|
117
|
+
value: Math.max(0, Math.min(1, normalizedValue)), // Clamp to [0, 1]
|
|
118
|
+
color,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
// Sort by value for binary search
|
|
122
|
+
stops.sort((a, b) => a.value - b.value);
|
|
123
|
+
return {
|
|
124
|
+
stops,
|
|
125
|
+
computedRange: valueRange ? undefined : [minVal, maxVal],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Apply colormap to a normalized value using interpolation
|
|
130
|
+
* @param normalizedValue - Value between 0 and 1
|
|
131
|
+
* @param colorStops - Array of color stops (must be sorted by value)
|
|
132
|
+
* @returns RGB color [r, g, b]
|
|
133
|
+
*/
|
|
134
|
+
function applyColorMap(normalizedValue, colorStops) {
|
|
135
|
+
const v = Math.max(0, Math.min(1, normalizedValue));
|
|
136
|
+
if (colorStops.length === 0) {
|
|
137
|
+
return [0, 0, 0]; // Black fallback
|
|
138
|
+
}
|
|
139
|
+
if (colorStops.length === 1) {
|
|
140
|
+
return colorStops[0].color;
|
|
141
|
+
}
|
|
142
|
+
// Binary search for surrounding color stops
|
|
143
|
+
let left = 0;
|
|
144
|
+
let right = colorStops.length - 1;
|
|
145
|
+
// Handle edge cases
|
|
146
|
+
if (v <= colorStops[left].value) {
|
|
147
|
+
return colorStops[left].color;
|
|
148
|
+
}
|
|
149
|
+
if (v >= colorStops[right].value) {
|
|
150
|
+
return colorStops[right].color;
|
|
151
|
+
}
|
|
152
|
+
// Binary search
|
|
153
|
+
while (right - left > 1) {
|
|
154
|
+
const mid = Math.floor((left + right) / 2);
|
|
155
|
+
if (colorStops[mid].value <= v) {
|
|
156
|
+
left = mid;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
right = mid;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Interpolate between left and right
|
|
163
|
+
const lower = colorStops[left];
|
|
164
|
+
const upper = colorStops[right];
|
|
165
|
+
const ratio = (v - lower.value) / (upper.value - lower.value);
|
|
166
|
+
const r = Math.round(lower.color[0] + ratio * (upper.color[0] - lower.color[0]));
|
|
167
|
+
const g = Math.round(lower.color[1] + ratio * (upper.color[1] - lower.color[1]));
|
|
168
|
+
const b = Math.round(lower.color[2] + ratio * (upper.color[2] - lower.color[2]));
|
|
169
|
+
return [r, g, b];
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get ColorStop array from ColorMapName or GeoStyler ColorMap
|
|
173
|
+
* @param colorMap - Predefined name or GeoStyler ColorMap
|
|
174
|
+
* @param valueRange - Optional value range for GeoStyler ColorMap
|
|
175
|
+
* @returns ColorStop array and computed range (if applicable)
|
|
176
|
+
*/
|
|
177
|
+
function getColorStops(colorMap, valueRange) {
|
|
178
|
+
if (typeof colorMap === 'string') {
|
|
179
|
+
// Predefined colormap name
|
|
180
|
+
const stops = PREDEFINED_COLORMAPS[colorMap];
|
|
181
|
+
if (!stops) {
|
|
182
|
+
warn(`Unknown colormap: ${colorMap}, using grayscale`);
|
|
183
|
+
return { stops: PREDEFINED_COLORMAPS.grayscale };
|
|
184
|
+
}
|
|
185
|
+
return { stops };
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// GeoStyler ColorMap
|
|
189
|
+
return convertGeoStylerColorMap(colorMap, valueRange);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class AABB2D {
|
|
194
|
+
min;
|
|
195
|
+
max;
|
|
196
|
+
constructor(min, max) {
|
|
197
|
+
this.min = min;
|
|
198
|
+
this.max = max;
|
|
199
|
+
}
|
|
200
|
+
contains(point) {
|
|
201
|
+
return (point.x >= this.min.x &&
|
|
202
|
+
point.x <= this.max.x &&
|
|
203
|
+
point.y >= this.min.y &&
|
|
204
|
+
point.y <= this.max.y);
|
|
205
|
+
}
|
|
206
|
+
static fromTriangle(triangle) {
|
|
207
|
+
const minX = Math.min(triangle.a.x, triangle.b.x, triangle.c.x);
|
|
208
|
+
const minY = Math.min(triangle.a.y, triangle.b.y, triangle.c.y);
|
|
209
|
+
const maxX = Math.max(triangle.a.x, triangle.b.x, triangle.c.x);
|
|
210
|
+
const maxY = Math.max(triangle.a.y, triangle.b.y, triangle.c.y);
|
|
211
|
+
return new AABB2D({ x: minX, y: minY }, { x: maxX, y: maxY });
|
|
212
|
+
}
|
|
213
|
+
static union(a, b) {
|
|
214
|
+
return new AABB2D({ x: Math.min(a.min.x, b.min.x), y: Math.min(a.min.y, b.min.y) }, { x: Math.max(a.max.x, b.max.x), y: Math.max(a.max.y, b.max.y) });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
class BVHNode2D {
|
|
219
|
+
bbox;
|
|
220
|
+
triangles;
|
|
221
|
+
left;
|
|
222
|
+
right;
|
|
223
|
+
constructor(bbox, triangles = [], left = null, right = null) {
|
|
224
|
+
this.bbox = bbox;
|
|
225
|
+
this.triangles = triangles;
|
|
226
|
+
this.left = left;
|
|
227
|
+
this.right = right;
|
|
228
|
+
}
|
|
229
|
+
static build(triangles, depth = 0, maxDepth = 10) {
|
|
230
|
+
if (triangles.length <= 2 || depth >= maxDepth) {
|
|
231
|
+
let bbox = AABB2D.fromTriangle(triangles[0]);
|
|
232
|
+
for (let i = 1; i < triangles.length; i++) {
|
|
233
|
+
bbox = AABB2D.union(bbox, AABB2D.fromTriangle(triangles[i]));
|
|
234
|
+
}
|
|
235
|
+
return new BVHNode2D(bbox, triangles);
|
|
236
|
+
}
|
|
237
|
+
const centroids = triangles.map(t => ({
|
|
238
|
+
x: (t.a.x + t.b.x + t.c.x) / 3,
|
|
239
|
+
y: (t.a.y + t.b.y + t.c.y) / 3,
|
|
240
|
+
}));
|
|
241
|
+
const { min, max } = centroids.reduce((acc, c) => ({
|
|
242
|
+
min: { x: Math.min(acc.min.x, c.x), y: Math.min(acc.min.y, c.y) },
|
|
243
|
+
max: { x: Math.max(acc.max.x, c.x), y: Math.max(acc.max.y, c.y) },
|
|
244
|
+
}), { min: centroids[0], max: centroids[0] });
|
|
245
|
+
const axis = max.x - min.x > max.y - min.y ? 'x' : 'y';
|
|
246
|
+
centroids.sort((a, b) => a[axis] - b[axis]);
|
|
247
|
+
const mid = Math.floor(centroids.length / 2);
|
|
248
|
+
const leftTriangles = [];
|
|
249
|
+
const rightTriangles = [];
|
|
250
|
+
for (let i = 0; i < triangles.length; i++) {
|
|
251
|
+
(i < mid ? leftTriangles : rightTriangles).push(triangles[i]);
|
|
252
|
+
}
|
|
253
|
+
const left = BVHNode2D.build(leftTriangles, depth + 1, maxDepth);
|
|
254
|
+
const right = BVHNode2D.build(rightTriangles, depth + 1, maxDepth);
|
|
255
|
+
const bbox = AABB2D.union(left.bbox, right.bbox);
|
|
256
|
+
return new BVHNode2D(bbox, [], left, right);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Finds the triangle containing the given point.
|
|
260
|
+
* @param point The point to test.
|
|
261
|
+
* @returns The containing triangle or `null`.
|
|
262
|
+
*/
|
|
263
|
+
findContainingTriangle(point) {
|
|
264
|
+
if (!this.bbox.contains(point)) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
if (this.triangles.length > 0) {
|
|
268
|
+
for (const triangle of this.triangles) {
|
|
269
|
+
if (BVHNode2D.punktInDreieck2D(point, triangle)) {
|
|
270
|
+
return triangle;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
return (this.left?.findContainingTriangle(point) ||
|
|
276
|
+
this.right?.findContainingTriangle(point) ||
|
|
277
|
+
null);
|
|
278
|
+
}
|
|
279
|
+
static punktInDreieck2D(p, triangle) {
|
|
280
|
+
const { a, b, c } = triangle;
|
|
281
|
+
const v0 = { x: c.x - a.x, y: c.y - a.y };
|
|
282
|
+
const v1 = { x: b.x - a.x, y: b.y - a.y };
|
|
283
|
+
const v2 = { x: p.x - a.x, y: p.y - a.y };
|
|
284
|
+
const dot00 = v0.x * v0.x + v0.y * v0.y;
|
|
285
|
+
const dot01 = v0.x * v1.x + v0.y * v1.y;
|
|
286
|
+
const dot02 = v0.x * v2.x + v0.y * v2.y;
|
|
287
|
+
const dot11 = v1.x * v1.x + v1.y * v1.y;
|
|
288
|
+
const dot12 = v1.x * v2.x + v1.y * v2.y;
|
|
289
|
+
const invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
|
290
|
+
const u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
|
291
|
+
const v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
|
292
|
+
return u >= 0 && v >= 0 && u + v <= 1;
|
|
293
|
+
//return u >= 0 && v >= 0 && u + v < 1;
|
|
294
|
+
}
|
|
295
|
+
static toTriangle2D(triangle) {
|
|
296
|
+
return {
|
|
297
|
+
a: { x: triangle.target[0][0], y: triangle.target[0][1] },
|
|
298
|
+
b: { x: triangle.target[1][0], y: triangle.target[1][1] },
|
|
299
|
+
c: { x: triangle.target[2][0], y: triangle.target[2][1] },
|
|
300
|
+
triangle: triangle,
|
|
301
|
+
transform: null,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Returns a string representation of the BVH tree structure
|
|
306
|
+
* @param indent - Internal parameter for recursive formatting
|
|
307
|
+
* @returns Formatted string showing tree structure
|
|
308
|
+
*/
|
|
309
|
+
toString(indent = '') {
|
|
310
|
+
const isLeaf = this.triangles.length > 0;
|
|
311
|
+
const nodeType = isLeaf ? 'LEAF' : 'NODE';
|
|
312
|
+
const bboxStr = `[${this.bbox.min.x.toFixed(2)}, ${this.bbox.min.y.toFixed(2)}, ${this.bbox.max.x.toFixed(2)}, ${this.bbox.max.y.toFixed(2)}]`;
|
|
313
|
+
let result = `${indent}${nodeType} bbox=${bboxStr}`;
|
|
314
|
+
if (isLeaf) {
|
|
315
|
+
const triArray = this.triangles.map(t2D => t2D.a.x.toFixed(2) +
|
|
316
|
+
',' +
|
|
317
|
+
t2D.a.y.toFixed(2) +
|
|
318
|
+
' ' +
|
|
319
|
+
t2D.b.x.toFixed(2) +
|
|
320
|
+
',' +
|
|
321
|
+
t2D.b.y.toFixed(2) +
|
|
322
|
+
' ' +
|
|
323
|
+
t2D.c.x.toFixed(2) +
|
|
324
|
+
',' +
|
|
325
|
+
t2D.c.y.toFixed(2));
|
|
326
|
+
const triArrayStr = triArray.toString();
|
|
327
|
+
result += ` triangles=${this.triangles.length} - ${triArrayStr}`;
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
result += '\n';
|
|
331
|
+
if (this.left) {
|
|
332
|
+
result += `${indent}├─ left:\n${this.left.toString(indent + '│ ')}`;
|
|
333
|
+
}
|
|
334
|
+
if (this.right) {
|
|
335
|
+
result += `${indent}└─ right:\n${this.right.toString(indent + ' ')}`;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get statistics about the BVH tree
|
|
342
|
+
* @returns Object with tree statistics
|
|
343
|
+
*/
|
|
344
|
+
getStats() {
|
|
345
|
+
const stats = {
|
|
346
|
+
depth: 1,
|
|
347
|
+
nodeCount: 1,
|
|
348
|
+
leafCount: 0,
|
|
349
|
+
triangleCount: this.triangles.length,
|
|
350
|
+
minTrianglesPerLeaf: Infinity,
|
|
351
|
+
maxTrianglesPerLeaf: 0,
|
|
352
|
+
};
|
|
353
|
+
if (this.triangles.length > 0) {
|
|
354
|
+
// This is a leaf
|
|
355
|
+
stats.leafCount = 1;
|
|
356
|
+
stats.minTrianglesPerLeaf = this.triangles.length;
|
|
357
|
+
stats.maxTrianglesPerLeaf = this.triangles.length;
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
// This is an internal node
|
|
361
|
+
if (this.left) {
|
|
362
|
+
const leftStats = this.left.getStats();
|
|
363
|
+
stats.depth = Math.max(stats.depth, leftStats.depth + 1);
|
|
364
|
+
stats.nodeCount += leftStats.nodeCount;
|
|
365
|
+
stats.leafCount += leftStats.leafCount;
|
|
366
|
+
stats.triangleCount += leftStats.triangleCount;
|
|
367
|
+
stats.minTrianglesPerLeaf = Math.min(stats.minTrianglesPerLeaf, leftStats.minTrianglesPerLeaf);
|
|
368
|
+
stats.maxTrianglesPerLeaf = Math.max(stats.maxTrianglesPerLeaf, leftStats.maxTrianglesPerLeaf);
|
|
369
|
+
}
|
|
370
|
+
if (this.right) {
|
|
371
|
+
const rightStats = this.right.getStats();
|
|
372
|
+
stats.depth = Math.max(stats.depth, rightStats.depth + 1);
|
|
373
|
+
stats.nodeCount += rightStats.nodeCount;
|
|
374
|
+
stats.leafCount += rightStats.leafCount;
|
|
375
|
+
stats.triangleCount += rightStats.triangleCount;
|
|
376
|
+
stats.minTrianglesPerLeaf = Math.min(stats.minTrianglesPerLeaf, rightStats.minTrianglesPerLeaf);
|
|
377
|
+
stats.maxTrianglesPerLeaf = Math.max(stats.maxTrianglesPerLeaf, rightStats.maxTrianglesPerLeaf);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return stats;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Triangulation for raster reprojection
|
|
386
|
+
*
|
|
387
|
+
* Based on OpenLayers' triangulation approach for efficient raster reprojection.
|
|
388
|
+
* Instead of transforming every pixel, we:
|
|
389
|
+
* 1. Divide the target extent into triangles
|
|
390
|
+
* 2. Transform only triangle vertices with proj4
|
|
391
|
+
* 3. Use affine transformation to map pixels within each triangle
|
|
392
|
+
*
|
|
393
|
+
* This reduces proj4 calls from ~65,536 per tile to ~50-200 per tile.
|
|
394
|
+
*/
|
|
395
|
+
function calculateBounds(sourceRef, resolution, targetExtent, transformFn, step = 10) {
|
|
396
|
+
const [west, south, east, north] = targetExtent;
|
|
397
|
+
let minX = Infinity;
|
|
398
|
+
let minY = Infinity;
|
|
399
|
+
let maxX = -Infinity;
|
|
400
|
+
let maxY = -Infinity;
|
|
401
|
+
let minXTarget = [0, 0];
|
|
402
|
+
let minYTarget = [0, 0];
|
|
403
|
+
let maxXTarget = [0, 0];
|
|
404
|
+
let maxYTarget = [0, 0];
|
|
405
|
+
// Funktion zum Abtasten einer Kante
|
|
406
|
+
const sampleEdge = (start, end, steps) => {
|
|
407
|
+
for (let i = 0; i <= steps; i++) {
|
|
408
|
+
const t = i / steps;
|
|
409
|
+
const x = start[0] + (end[0] - start[0]) * t;
|
|
410
|
+
const y = start[1] + (end[1] - start[1]) * t;
|
|
411
|
+
const target = [x, y];
|
|
412
|
+
const [xSrc, ySrc] = transformFn(target);
|
|
413
|
+
if (xSrc < minX) {
|
|
414
|
+
minX = xSrc;
|
|
415
|
+
minXTarget = target;
|
|
416
|
+
}
|
|
417
|
+
if (ySrc < minY) {
|
|
418
|
+
minY = ySrc;
|
|
419
|
+
minYTarget = target;
|
|
420
|
+
}
|
|
421
|
+
if (xSrc > maxX) {
|
|
422
|
+
maxX = xSrc;
|
|
423
|
+
maxXTarget = target;
|
|
424
|
+
}
|
|
425
|
+
if (ySrc > maxY) {
|
|
426
|
+
maxY = ySrc;
|
|
427
|
+
maxYTarget = target;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
// Abtasten aller vier Kanten
|
|
432
|
+
sampleEdge([west, north], [east, north], step); // Top edge
|
|
433
|
+
sampleEdge([east, north], [east, south], step); // Right edge
|
|
434
|
+
sampleEdge([east, south], [west, south], step); // Bottom edge
|
|
435
|
+
sampleEdge([west, south], [west, north], step); // Left edge
|
|
436
|
+
if (sourceRef != null && resolution != null) {
|
|
437
|
+
const minXPix = Math.floor((minX - sourceRef[0]) / resolution);
|
|
438
|
+
minX = sourceRef[0] + minXPix * resolution;
|
|
439
|
+
const minYPix = Math.floor((minY - sourceRef[1]) / resolution);
|
|
440
|
+
minY = sourceRef[1] + minYPix * resolution;
|
|
441
|
+
const maxXPix = Math.ceil((maxX - sourceRef[0]) / resolution);
|
|
442
|
+
maxX = sourceRef[0] + maxXPix * resolution;
|
|
443
|
+
const maxYPix = Math.ceil((maxY - sourceRef[1]) / resolution);
|
|
444
|
+
maxY = sourceRef[1] + maxYPix * resolution;
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
source: {
|
|
448
|
+
minX,
|
|
449
|
+
minY,
|
|
450
|
+
maxX,
|
|
451
|
+
maxY,
|
|
452
|
+
},
|
|
453
|
+
target: {
|
|
454
|
+
minX: minXTarget[0],
|
|
455
|
+
minY: minYTarget[1],
|
|
456
|
+
maxX: maxXTarget[0],
|
|
457
|
+
maxY: maxYTarget[1],
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
const MAX_SUBDIVISION = 10;
|
|
462
|
+
/**
|
|
463
|
+
* Class for triangulation of the given target extent
|
|
464
|
+
* Used for determining source data and the reprojection itself
|
|
465
|
+
*/
|
|
466
|
+
class Triangulation {
|
|
467
|
+
triangles_ = [];
|
|
468
|
+
transformFn_;
|
|
469
|
+
errorThresholdSquared_;
|
|
470
|
+
bvh_ = null;
|
|
471
|
+
bounds;
|
|
472
|
+
/**
|
|
473
|
+
* @param transformFn - Function that transforms [x, y] from target to source projection
|
|
474
|
+
* @param targetExtent - [west, south, east, north] in target projection
|
|
475
|
+
* @param errorThreshold - Maximum allowed error in pixels (default: 0.5)
|
|
476
|
+
*/
|
|
477
|
+
constructor(transformFn, targetExtent, errorThreshold = 0.5, sourceRef = null, resolution = null, step = 10) {
|
|
478
|
+
this.transformFn_ = transformFn;
|
|
479
|
+
this.errorThresholdSquared_ = errorThreshold * errorThreshold;
|
|
480
|
+
const [west, south, east, north] = targetExtent;
|
|
481
|
+
// Transform the four corners
|
|
482
|
+
const a = [west, north]; // top-left
|
|
483
|
+
const b = [east, north]; // top-right
|
|
484
|
+
const c = [east, south]; // bottom-right
|
|
485
|
+
const d = [west, south]; // bottom-left
|
|
486
|
+
const extBounds = calculateBounds(sourceRef, resolution, targetExtent, this.transformFn_, step);
|
|
487
|
+
// const aSrc = this.transformFn_(a);
|
|
488
|
+
// const bSrc = this.transformFn_(b);
|
|
489
|
+
// const cSrc = this.transformFn_(c);
|
|
490
|
+
// const dSrc = this.transformFn_(d);
|
|
491
|
+
const aSrc = [
|
|
492
|
+
extBounds.source.minX,
|
|
493
|
+
extBounds.source.maxY,
|
|
494
|
+
]; // top-left
|
|
495
|
+
const bSrc = [
|
|
496
|
+
extBounds.source.maxX,
|
|
497
|
+
extBounds.source.maxY,
|
|
498
|
+
]; // top-right
|
|
499
|
+
const cSrc = [
|
|
500
|
+
extBounds.source.maxX,
|
|
501
|
+
extBounds.source.minY,
|
|
502
|
+
]; // bottom-right
|
|
503
|
+
const dSrc = [
|
|
504
|
+
extBounds.source.minX,
|
|
505
|
+
extBounds.source.minY,
|
|
506
|
+
]; // bottom-left
|
|
507
|
+
this.bounds = extBounds.source;
|
|
508
|
+
this.addQuad_(a, b, c, d, aSrc, bSrc, cSrc, dSrc, MAX_SUBDIVISION);
|
|
509
|
+
}
|
|
510
|
+
getBounds() {
|
|
511
|
+
return this.bounds;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Recursively subdivide a quadrilateral if needed
|
|
515
|
+
*/
|
|
516
|
+
addQuad_(a, b, c, d, aSrc, bSrc, cSrc, dSrc, maxSubdivision) {
|
|
517
|
+
let needsSubdivision = false;
|
|
518
|
+
// Check if we need to subdivide based on error threshold
|
|
519
|
+
if (maxSubdivision > 0) {
|
|
520
|
+
// Calculate midpoints of all edges (linear interpolation in target space)
|
|
521
|
+
const abTarget = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
522
|
+
const bcTarget = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
|
|
523
|
+
const cdTarget = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
|
|
524
|
+
const daTarget = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
|
|
525
|
+
// Transform midpoints to source space
|
|
526
|
+
const abSrc = this.transformFn_(abTarget);
|
|
527
|
+
const bcSrc = this.transformFn_(bcTarget);
|
|
528
|
+
const cdSrc = this.transformFn_(cdTarget);
|
|
529
|
+
const daSrc = this.transformFn_(daTarget);
|
|
530
|
+
// Calculate expected midpoints (linear interpolation in source space)
|
|
531
|
+
const abExpected = [
|
|
532
|
+
(aSrc[0] + bSrc[0]) / 2,
|
|
533
|
+
(aSrc[1] + bSrc[1]) / 2,
|
|
534
|
+
];
|
|
535
|
+
const bcExpected = [
|
|
536
|
+
(bSrc[0] + cSrc[0]) / 2,
|
|
537
|
+
(bSrc[1] + cSrc[1]) / 2,
|
|
538
|
+
];
|
|
539
|
+
const cdExpected = [
|
|
540
|
+
(cSrc[0] + dSrc[0]) / 2,
|
|
541
|
+
(cSrc[1] + dSrc[1]) / 2,
|
|
542
|
+
];
|
|
543
|
+
const daExpected = [
|
|
544
|
+
(dSrc[0] + aSrc[0]) / 2,
|
|
545
|
+
(dSrc[1] + aSrc[1]) / 2,
|
|
546
|
+
];
|
|
547
|
+
// Calculate squared errors
|
|
548
|
+
const abError = this.getSquaredError_(abSrc, abExpected);
|
|
549
|
+
const bcError = this.getSquaredError_(bcSrc, bcExpected);
|
|
550
|
+
const cdError = this.getSquaredError_(cdSrc, cdExpected);
|
|
551
|
+
const daError = this.getSquaredError_(daSrc, daExpected);
|
|
552
|
+
// Check if any edge exceeds error threshold
|
|
553
|
+
if (abError > this.errorThresholdSquared_ ||
|
|
554
|
+
bcError > this.errorThresholdSquared_ ||
|
|
555
|
+
cdError > this.errorThresholdSquared_ ||
|
|
556
|
+
daError > this.errorThresholdSquared_) {
|
|
557
|
+
needsSubdivision = true;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (needsSubdivision) {
|
|
561
|
+
// Subdivide into 4 smaller quads
|
|
562
|
+
// Calculate center point
|
|
563
|
+
const eTarget = [
|
|
564
|
+
(a[0] + b[0] + c[0] + d[0]) / 4,
|
|
565
|
+
(a[1] + b[1] + c[1] + d[1]) / 4,
|
|
566
|
+
];
|
|
567
|
+
const eSrc = this.transformFn_(eTarget);
|
|
568
|
+
// Calculate edge midpoints
|
|
569
|
+
const abTarget = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
570
|
+
const bcTarget = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
|
|
571
|
+
const cdTarget = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
|
|
572
|
+
const daTarget = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
|
|
573
|
+
const abSrc = this.transformFn_(abTarget);
|
|
574
|
+
const bcSrc = this.transformFn_(bcTarget);
|
|
575
|
+
const cdSrc = this.transformFn_(cdTarget);
|
|
576
|
+
const daSrc = this.transformFn_(daTarget);
|
|
577
|
+
const newMaxSubdivision = maxSubdivision - 1;
|
|
578
|
+
// Recursively add 4 sub-quads
|
|
579
|
+
this.addQuad_(a, abTarget, eTarget, daTarget, aSrc, abSrc, eSrc, daSrc, newMaxSubdivision);
|
|
580
|
+
this.addQuad_(abTarget, b, bcTarget, eTarget, abSrc, bSrc, bcSrc, eSrc, newMaxSubdivision);
|
|
581
|
+
this.addQuad_(eTarget, bcTarget, c, cdTarget, eSrc, bcSrc, cSrc, cdSrc, newMaxSubdivision);
|
|
582
|
+
this.addQuad_(daTarget, eTarget, cdTarget, d, daSrc, eSrc, cdSrc, dSrc, newMaxSubdivision);
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
// No subdivision needed - add two triangles for this quad
|
|
586
|
+
// Triangle 1: a-b-d
|
|
587
|
+
this.addTriangle_(a, b, d, aSrc, bSrc, dSrc);
|
|
588
|
+
// Triangle 2: b-c-d
|
|
589
|
+
this.addTriangle_(b, c, d, bSrc, cSrc, dSrc);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Add a single triangle
|
|
594
|
+
*/
|
|
595
|
+
addTriangle_(a, b, c, aSrc, bSrc, cSrc) {
|
|
596
|
+
// Check for non-finite coordinates
|
|
597
|
+
if (!isFinite(aSrc[0]) ||
|
|
598
|
+
!isFinite(aSrc[1]) ||
|
|
599
|
+
!isFinite(bSrc[0]) ||
|
|
600
|
+
!isFinite(bSrc[1]) ||
|
|
601
|
+
!isFinite(cSrc[0]) ||
|
|
602
|
+
!isFinite(cSrc[1])) {
|
|
603
|
+
return; // Skip invalid triangles
|
|
604
|
+
}
|
|
605
|
+
this.triangles_.push({
|
|
606
|
+
source: [aSrc, bSrc, cSrc],
|
|
607
|
+
target: [a, b, c],
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Calculate squared error between two points
|
|
612
|
+
*/
|
|
613
|
+
getSquaredError_(actual, expected) {
|
|
614
|
+
const dx = actual[0] - expected[0];
|
|
615
|
+
const dy = actual[1] - expected[1];
|
|
616
|
+
return dx * dx + dy * dy;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Get all triangles
|
|
620
|
+
*/
|
|
621
|
+
getTriangles() {
|
|
622
|
+
return this.triangles_;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Calculate the bounding extent of all source coordinates
|
|
626
|
+
*/
|
|
627
|
+
calculateSourceExtent() {
|
|
628
|
+
if (this.triangles_.length === 0) {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
let minX = Infinity;
|
|
632
|
+
let minY = Infinity;
|
|
633
|
+
let maxX = -Infinity;
|
|
634
|
+
let maxY = -Infinity;
|
|
635
|
+
for (const triangle of this.triangles_) {
|
|
636
|
+
for (const [x, y] of triangle.source) {
|
|
637
|
+
if (x < minX)
|
|
638
|
+
minX = x;
|
|
639
|
+
if (x > maxX)
|
|
640
|
+
maxX = x;
|
|
641
|
+
if (y < minY)
|
|
642
|
+
minY = y;
|
|
643
|
+
if (y > maxY)
|
|
644
|
+
maxY = y;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return [minX, minY, maxX, maxY];
|
|
648
|
+
}
|
|
649
|
+
buildBVH() {
|
|
650
|
+
const triangles2D = this.triangles_.map(BVHNode2D.toTriangle2D);
|
|
651
|
+
this.bvh_ = BVHNode2D.build(triangles2D);
|
|
652
|
+
}
|
|
653
|
+
findSourceTriangleForTargetPoint(point, extraTri = null) {
|
|
654
|
+
if (!this.bvh_)
|
|
655
|
+
this.buildBVH();
|
|
656
|
+
const point2D = { x: point[0], y: point[1] };
|
|
657
|
+
if (extraTri?.tri &&
|
|
658
|
+
BVHNode2D.punktInDreieck2D(point2D, BVHNode2D.toTriangle2D(extraTri.tri))) {
|
|
659
|
+
return extraTri;
|
|
660
|
+
}
|
|
661
|
+
const result = this.bvh_.findContainingTriangle(point2D);
|
|
662
|
+
if (result && !result.transform) {
|
|
663
|
+
result.transform = this.calculateAffineTransform(result.triangle);
|
|
664
|
+
}
|
|
665
|
+
return result
|
|
666
|
+
? { tri: result.triangle, transform: result.transform }
|
|
667
|
+
: null;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Calculate affine transformation matrix for a triangle
|
|
671
|
+
* Maps from target triangle to source triangle
|
|
672
|
+
*/
|
|
673
|
+
calculateAffineTransform(triangle) {
|
|
674
|
+
const [[x0t, y0t], [x1t, y1t], [x2t, y2t]] = triangle.target;
|
|
675
|
+
const [[x0s, y0s], [x1s, y1s], [x2s, y2s]] = triangle.source;
|
|
676
|
+
// Solve for affine transformation: [xs, ys] = [a*xt + b*yt + c, d*xt + e*yt + f]
|
|
677
|
+
// Using the three triangle vertices
|
|
678
|
+
const det = (x1t - x0t) * (y2t - y0t) - (x2t - x0t) * (y1t - y0t);
|
|
679
|
+
if (Math.abs(det) < 1e-10) {
|
|
680
|
+
// Degenerate triangle - return identity-like transform
|
|
681
|
+
return { a: 1, b: 0, c: x0s, d: 0, e: 1, f: y0s };
|
|
682
|
+
}
|
|
683
|
+
const a = ((x1s - x0s) * (y2t - y0t) - (x2s - x0s) * (y1t - y0t)) / det;
|
|
684
|
+
const b = ((x2s - x0s) * (x1t - x0t) - (x1s - x0s) * (x2t - x0t)) / det;
|
|
685
|
+
const c = x0s - a * x0t - b * y0t;
|
|
686
|
+
const d = ((y1s - y0s) * (y2t - y0t) - (y2s - y0s) * (y1t - y0t)) / det;
|
|
687
|
+
const e = ((y2s - y0s) * (x1t - x0t) - (y1s - y0s) * (x2t - x0t)) / det;
|
|
688
|
+
const f = y0s - d * x0t - e * y0t;
|
|
689
|
+
return { a, b, c, d, e, f };
|
|
690
|
+
}
|
|
691
|
+
// ============================================================================
|
|
692
|
+
// REPROJECTION METHODS
|
|
693
|
+
// ============================================================================
|
|
694
|
+
/**
|
|
695
|
+
* Apply affine transformation to a point
|
|
696
|
+
*/
|
|
697
|
+
applyAffineTransform(x, y, transform) {
|
|
698
|
+
return [
|
|
699
|
+
transform.a * x + transform.b * y + transform.c,
|
|
700
|
+
transform.d * x + transform.e * y + transform.f,
|
|
701
|
+
];
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Normalization utilities for raster data
|
|
707
|
+
* Handles conversion of different TypedArray types to normalized 0-255 range
|
|
708
|
+
*/
|
|
709
|
+
/**
|
|
710
|
+
* Normalize a raw value to 0-255 range based on array type
|
|
711
|
+
* @param rawValue - The raw value from the raster
|
|
712
|
+
* @param arrayType - The TypedArray type name
|
|
713
|
+
* @returns Normalized value 0-255
|
|
714
|
+
*/
|
|
715
|
+
function normalizeValue(rawValue, arrayType) {
|
|
716
|
+
switch (arrayType) {
|
|
717
|
+
case 'Uint8Array':
|
|
718
|
+
return rawValue; // Already 0-255
|
|
719
|
+
case 'Uint16Array':
|
|
720
|
+
// 0-65535 → 0-255
|
|
721
|
+
return Math.round((rawValue / 65535) * 255);
|
|
722
|
+
case 'Int16Array':
|
|
723
|
+
// -32768-32767 → 0-255
|
|
724
|
+
return Math.round(((rawValue + 32768) / 65535) * 255);
|
|
725
|
+
case 'Uint32Array':
|
|
726
|
+
// 0-4294967295 → 0-255
|
|
727
|
+
return Math.round((rawValue / 4294967295) * 255);
|
|
728
|
+
case 'Int32Array':
|
|
729
|
+
// -2147483648-2147483647 → 0-255
|
|
730
|
+
return Math.round(((rawValue + 2147483648) / 4294967295) * 255);
|
|
731
|
+
case 'Float32Array':
|
|
732
|
+
case 'Float64Array':
|
|
733
|
+
// Assume 0.0-1.0 range for Float arrays (common for normalized data)
|
|
734
|
+
// Clamp to [0, 1] first
|
|
735
|
+
const clamped = Math.max(0, Math.min(1, rawValue));
|
|
736
|
+
return Math.round(clamped * 255);
|
|
737
|
+
default:
|
|
738
|
+
warn(`Unknown array type: ${arrayType}, treating as Uint8`);
|
|
739
|
+
return rawValue;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Normalize a raw value to 0-1 range for colormap application
|
|
744
|
+
* @param rawValue - The raw value from the raster
|
|
745
|
+
* @param valueRange - Optional [min, max] range. If not provided, assumes normalized data.
|
|
746
|
+
* @returns Normalized value 0-1
|
|
747
|
+
*/
|
|
748
|
+
function normalizeToColorMapRange(rawValue, valueRange) {
|
|
749
|
+
{
|
|
750
|
+
// Assume already normalized or single-value range
|
|
751
|
+
return Math.max(0, Math.min(1, rawValue));
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Nearest-Neighbor Sampling with window-based reading and multi-band support
|
|
757
|
+
* Returns [R, G, B, A] values (0-255)
|
|
758
|
+
*/
|
|
759
|
+
function sampleNearest(x, y, rasterBands, arrayType, width, height, offsetX, offsetY, colorStops) {
|
|
760
|
+
const px = Math.round(x) - offsetX;
|
|
761
|
+
const py = Math.round(y) - offsetY;
|
|
762
|
+
if (px < 0 || px >= width || py < 0 || py >= height) {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
const idx = py * width + px;
|
|
766
|
+
const bandCount = rasterBands.length;
|
|
767
|
+
if (bandCount === 1) {
|
|
768
|
+
// Grayscale - apply colormap if specified
|
|
769
|
+
const rawValue = rasterBands[0][idx];
|
|
770
|
+
if (colorStops) {
|
|
771
|
+
// Apply colormap (only for Float types or when colorStops provided)
|
|
772
|
+
const normalizedValue = normalizeToColorMapRange(rawValue);
|
|
773
|
+
const [r, g, b] = applyColorMap(normalizedValue, colorStops);
|
|
774
|
+
return [r, g, b, 255];
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
// Regular grayscale normalization
|
|
778
|
+
const gray = normalizeValue(rawValue, arrayType);
|
|
779
|
+
return [gray, gray, gray, 255];
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else if (bandCount === 3) {
|
|
783
|
+
// RGB
|
|
784
|
+
const r = normalizeValue(rasterBands[0][idx], arrayType);
|
|
785
|
+
const g = normalizeValue(rasterBands[1][idx], arrayType);
|
|
786
|
+
const b = normalizeValue(rasterBands[2][idx], arrayType);
|
|
787
|
+
return [r, g, b, 255];
|
|
788
|
+
}
|
|
789
|
+
else if (bandCount >= 4) {
|
|
790
|
+
// RGBA
|
|
791
|
+
const r = normalizeValue(rasterBands[0][idx], arrayType);
|
|
792
|
+
const g = normalizeValue(rasterBands[1][idx], arrayType);
|
|
793
|
+
const b = normalizeValue(rasterBands[2][idx], arrayType);
|
|
794
|
+
const a = normalizeValue(rasterBands[3][idx], arrayType);
|
|
795
|
+
return [r, g, b, a];
|
|
796
|
+
}
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Bilinear Interpolation with window-based reading and multi-band support
|
|
801
|
+
* Returns [R, G, B, A] values (0-255)
|
|
802
|
+
*/
|
|
803
|
+
function sampleBilinear(x, y, rasterBands, arrayType, width, height, offsetX, offsetY, colorStops) {
|
|
804
|
+
const localX = x - offsetX;
|
|
805
|
+
const localY = y - offsetY;
|
|
806
|
+
// Bounds-Check
|
|
807
|
+
if (localX < 0 ||
|
|
808
|
+
localX >= width - 1 ||
|
|
809
|
+
localY < 0 ||
|
|
810
|
+
localY >= height - 1) {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
// Bilineare Interpolation
|
|
814
|
+
const x0 = Math.floor(localX);
|
|
815
|
+
const x1 = Math.ceil(localX);
|
|
816
|
+
const y0 = Math.floor(localY);
|
|
817
|
+
const y1 = Math.ceil(localY);
|
|
818
|
+
const fx = localX - x0;
|
|
819
|
+
const fy = localY - y0;
|
|
820
|
+
const bandCount = rasterBands.length;
|
|
821
|
+
const result = [0, 0, 0, 255];
|
|
822
|
+
if (bandCount === 1) {
|
|
823
|
+
// Grayscale - interpolate first, then apply colormap
|
|
824
|
+
const band = rasterBands[0];
|
|
825
|
+
const v00 = band[y0 * width + x0];
|
|
826
|
+
const v10 = band[y0 * width + x1];
|
|
827
|
+
const v01 = band[y1 * width + x0];
|
|
828
|
+
const v11 = band[y1 * width + x1];
|
|
829
|
+
const v0 = v00 * (1 - fx) + v10 * fx;
|
|
830
|
+
const v1 = v01 * (1 - fx) + v11 * fx;
|
|
831
|
+
const interpolated = v0 * (1 - fy) + v1 * fy;
|
|
832
|
+
if (colorStops) {
|
|
833
|
+
// Apply colormap (only for Float types or when colorStops provided)
|
|
834
|
+
const normalizedValue = normalizeToColorMapRange(interpolated);
|
|
835
|
+
const [r, g, b] = applyColorMap(normalizedValue, colorStops);
|
|
836
|
+
return [r, g, b, 255];
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
// Regular grayscale normalization
|
|
840
|
+
const gray = normalizeValue(interpolated, arrayType);
|
|
841
|
+
return [gray, gray, gray, 255];
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
// RGB/RGBA - interpolate each band
|
|
846
|
+
const bandsToProcess = Math.min(bandCount, 4);
|
|
847
|
+
for (let b = 0; b < bandsToProcess; b++) {
|
|
848
|
+
const band = rasterBands[b];
|
|
849
|
+
const v00 = band[y0 * width + x0];
|
|
850
|
+
const v10 = band[y0 * width + x1];
|
|
851
|
+
const v01 = band[y1 * width + x0];
|
|
852
|
+
const v11 = band[y1 * width + x1];
|
|
853
|
+
const v0 = v00 * (1 - fx) + v10 * fx;
|
|
854
|
+
const v1 = v01 * (1 - fx) + v11 * fx;
|
|
855
|
+
const interpolated = v0 * (1 - fy) + v1 * fy;
|
|
856
|
+
result[b] = normalizeValue(interpolated, arrayType);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return result;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Processes GeoTIFF tiles with triangulation-based reprojection
|
|
864
|
+
*
|
|
865
|
+
* This class encapsulates the tile rendering logic using triangulation
|
|
866
|
+
* for efficient reprojection from arbitrary source projections to Web Mercator.
|
|
867
|
+
*/
|
|
868
|
+
class GeoTIFFTileProcessor {
|
|
869
|
+
// Configuration
|
|
870
|
+
config;
|
|
871
|
+
worldSize = 40075016.686; // Earth circumference in meters (Web Mercator)
|
|
872
|
+
// Global triangulation for the entire image (created once)
|
|
873
|
+
globalTriangulation;
|
|
874
|
+
constructor(config) {
|
|
875
|
+
this.config = config;
|
|
876
|
+
if (config.worldSize) {
|
|
877
|
+
this.worldSize = config.worldSize;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Create global triangulation for the entire GeoTIFF image
|
|
882
|
+
* This is called once to avoid recreating triangulation for every tile
|
|
883
|
+
*/
|
|
884
|
+
createGlobalTriangulation() {
|
|
885
|
+
const startTime = performance.now();
|
|
886
|
+
const [srcWest, srcSouth, srcEast, srcNorth] = this.config.sourceBounds;
|
|
887
|
+
// Transform source corners to target projection (Mercator)
|
|
888
|
+
const transformFnToProj = (coord) => {
|
|
889
|
+
return this.config.transformSourceMapToViewFn(coord);
|
|
890
|
+
};
|
|
891
|
+
const { source: bounds } = calculateBounds(null, null, [srcWest, srcSouth, srcEast, srcNorth], transformFnToProj);
|
|
892
|
+
const mercatorBounds = [
|
|
893
|
+
bounds.minX,
|
|
894
|
+
bounds.minY,
|
|
895
|
+
bounds.maxX,
|
|
896
|
+
bounds.maxY,
|
|
897
|
+
];
|
|
898
|
+
log('Creating global triangulation for bounds:', {
|
|
899
|
+
source: this.config.sourceBounds,
|
|
900
|
+
mercator: mercatorBounds,
|
|
901
|
+
});
|
|
902
|
+
// Inverse transformation: from target (Mercator) back to source
|
|
903
|
+
const inverseTransformFn = (coord) => {
|
|
904
|
+
return this.config.transformViewToSourceMapFn(coord);
|
|
905
|
+
};
|
|
906
|
+
// Use adaptive error threshold based on pixel resolution
|
|
907
|
+
const errorThreshold = this.config.resolution / 2.0;
|
|
908
|
+
const step = Math.min(10, Math.max(this.config.imageWidth, this.config.imageHeight) / 256);
|
|
909
|
+
this.globalTriangulation = new Triangulation(inverseTransformFn, mercatorBounds, errorThreshold, this.config.sourceRef, this.config.resolution, step);
|
|
910
|
+
// Force indexing
|
|
911
|
+
this.globalTriangulation.findSourceTriangleForTargetPoint([0, 0]);
|
|
912
|
+
const triangles = this.globalTriangulation.getTriangles();
|
|
913
|
+
log(`Global triangulation created: ${triangles.length} triangles in ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Get the global triangulation (may be undefined if not created yet)
|
|
917
|
+
*/
|
|
918
|
+
getGlobalTriangulation() {
|
|
919
|
+
return this.globalTriangulation;
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Normalize terrain samples so mesh generation does not create spikes
|
|
923
|
+
* for nodata or otherwise invalid height values.
|
|
924
|
+
*/
|
|
925
|
+
sanitizeElevationValue(value) {
|
|
926
|
+
if (!Number.isFinite(value)) {
|
|
927
|
+
return 0;
|
|
928
|
+
}
|
|
929
|
+
if (this.config.noDataValue !== undefined &&
|
|
930
|
+
value === this.config.noDataValue) {
|
|
931
|
+
return 0;
|
|
932
|
+
}
|
|
933
|
+
return value;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Calculate tile size in meters for a given zoom level
|
|
937
|
+
*/
|
|
938
|
+
getTileSizeInMeter(z) {
|
|
939
|
+
return this.worldSize / Math.pow(2, z);
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Convert tile coordinates to View projection bounds
|
|
943
|
+
* For Web Mercator (deck.gl/Leaflet/Cesium): returns bounds in meters
|
|
944
|
+
* Note: This assumes Web Mercator tiling scheme. Override for other projections.
|
|
945
|
+
*/
|
|
946
|
+
getTileBounds(x, y, z) {
|
|
947
|
+
const tileSize = this.getTileSizeInMeter(z);
|
|
948
|
+
const west = -this.worldSize / 2 + x * tileSize;
|
|
949
|
+
const north = this.worldSize / 2 - y * tileSize;
|
|
950
|
+
const east = west + tileSize;
|
|
951
|
+
const south = north - tileSize;
|
|
952
|
+
return [west, south, east, north];
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Select best overview image based on zoom level
|
|
956
|
+
*/
|
|
957
|
+
selectOverviewImage(z, tileSize) {
|
|
958
|
+
const baseResolution = this.config.baseImage.getResolution()[0];
|
|
959
|
+
if (!this.config.overviewImages ||
|
|
960
|
+
this.config.overviewImages.length === 0) {
|
|
961
|
+
return {
|
|
962
|
+
bestImage: this.config.baseImage,
|
|
963
|
+
bestResolution: baseResolution,
|
|
964
|
+
imageLevel: 0,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
const tileSizeInMeter = this.getTileSizeInMeter(z);
|
|
968
|
+
const tileResolution = tileSizeInMeter / tileSize;
|
|
969
|
+
// Check base image and all overviews
|
|
970
|
+
const allImages = [this.config.baseImage, ...this.config.overviewImages];
|
|
971
|
+
const maxResImage = allImages[this.config.overviewImages.length];
|
|
972
|
+
const maxResolution = baseResolution * Math.pow(2, this.config.overviewImages.length);
|
|
973
|
+
let imageLevel = this.config.overviewImages.length;
|
|
974
|
+
let bestResolution = maxResolution;
|
|
975
|
+
let bestImage = maxResImage;
|
|
976
|
+
let levelCounter = 0;
|
|
977
|
+
let resolution = baseResolution;
|
|
978
|
+
for (const img of allImages) {
|
|
979
|
+
const ratio = tileResolution / (resolution * 2.0);
|
|
980
|
+
if (ratio <= 1.0) {
|
|
981
|
+
bestImage = img;
|
|
982
|
+
bestResolution = resolution;
|
|
983
|
+
imageLevel = levelCounter;
|
|
984
|
+
break;
|
|
985
|
+
}
|
|
986
|
+
levelCounter++;
|
|
987
|
+
resolution = resolution * 2;
|
|
988
|
+
}
|
|
989
|
+
return { bestImage, bestResolution, imageLevel };
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Calculate source bounds for a tile after transformation
|
|
993
|
+
* @param viewBounds - Tile bounds in View projection
|
|
994
|
+
*/
|
|
995
|
+
calculateTileSourceBounds(viewBounds) {
|
|
996
|
+
const [viewWest, viewSouth, viewEast, viewNorth] = viewBounds;
|
|
997
|
+
const { source: bounds } = calculateBounds(this.config.sourceRef, this.config.resolution, [viewWest, viewSouth, viewEast, viewNorth], this.config.transformViewToSourceMapFn);
|
|
998
|
+
// Transform tile corners from View to Source projection
|
|
999
|
+
const sw = this.config.transformViewToSourceMapFn([viewWest, viewSouth]);
|
|
1000
|
+
const ne = this.config.transformViewToSourceMapFn([viewEast, viewNorth]);
|
|
1001
|
+
const nw = this.config.transformViewToSourceMapFn([viewWest, viewNorth]);
|
|
1002
|
+
const se = this.config.transformViewToSourceMapFn([viewEast, viewSouth]);
|
|
1003
|
+
// Calculate bounding box from transformed corners
|
|
1004
|
+
const tileSrcWest = Math.min(bounds.minX, sw[0], ne[0], nw[0], se[0]);
|
|
1005
|
+
const tileSrcEast = Math.max(bounds.maxX, sw[0], ne[0], nw[0], se[0]);
|
|
1006
|
+
const tileSrcSouth = Math.min(bounds.minY, sw[1], ne[1], nw[1], se[1]);
|
|
1007
|
+
const tileSrcNorth = Math.max(bounds.maxY, sw[1], ne[1], nw[1], se[1]);
|
|
1008
|
+
return { tileSrcWest, tileSrcEast, tileSrcSouth, tileSrcNorth };
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Calculate pixel window for reading from GeoTIFF
|
|
1012
|
+
*/
|
|
1013
|
+
calculateReadWindow(tileSrcBounds, ovWidth, ovHeight) {
|
|
1014
|
+
const [srcWest, srcSouth, srcEast, srcNorth] = this.config.sourceBounds;
|
|
1015
|
+
const srcWidth = srcEast - srcWest;
|
|
1016
|
+
const srcHeight = srcNorth - srcSouth;
|
|
1017
|
+
const { tileSrcWest, tileSrcEast, tileSrcSouth, tileSrcNorth } = tileSrcBounds;
|
|
1018
|
+
// Calculate pixel window for this tile area
|
|
1019
|
+
const pixelXMin = Math.floor(((tileSrcWest - srcWest) / srcWidth) * ovWidth);
|
|
1020
|
+
const pixelXMax = Math.ceil(((tileSrcEast - srcWest) / srcWidth) * ovWidth);
|
|
1021
|
+
const pixelYMin = Math.floor(((srcNorth - tileSrcNorth) / srcHeight) * ovHeight);
|
|
1022
|
+
const pixelYMax = Math.ceil(((srcNorth - tileSrcSouth) / srcHeight) * ovHeight);
|
|
1023
|
+
// Bounds check with 2-pixel padding
|
|
1024
|
+
const readXMin = Math.min(ovWidth, Math.max(0, pixelXMin - 2));
|
|
1025
|
+
const readXMax = Math.max(0, Math.min(ovWidth, pixelXMax + 2));
|
|
1026
|
+
const readYMin = Math.min(ovHeight, Math.max(0, pixelYMin - 2));
|
|
1027
|
+
const readYMax = Math.max(0, Math.min(ovHeight, pixelYMax + 2));
|
|
1028
|
+
const readWidth = readXMax - readXMin;
|
|
1029
|
+
const readHeight = readYMax - readYMin;
|
|
1030
|
+
if (readWidth <= 0 || readHeight <= 0) {
|
|
1031
|
+
warn('Invalid read window for tile', {
|
|
1032
|
+
readXMin,
|
|
1033
|
+
readXMax,
|
|
1034
|
+
readYMin,
|
|
1035
|
+
readYMax,
|
|
1036
|
+
ovWidth,
|
|
1037
|
+
ovHeight,
|
|
1038
|
+
});
|
|
1039
|
+
return null;
|
|
1040
|
+
}
|
|
1041
|
+
return {
|
|
1042
|
+
readXMin,
|
|
1043
|
+
readXMax,
|
|
1044
|
+
readYMin,
|
|
1045
|
+
readYMax,
|
|
1046
|
+
readWidth,
|
|
1047
|
+
readHeight,
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Load and convert raster data from GeoTIFF image
|
|
1052
|
+
*/
|
|
1053
|
+
async loadAndConvertRasterData(image, readWindow) {
|
|
1054
|
+
const { readXMin, readYMin, readXMax, readYMax } = readWindow;
|
|
1055
|
+
let rasters = null;
|
|
1056
|
+
let lasterr = null;
|
|
1057
|
+
for (let i = 0; i <= 2; i++) {
|
|
1058
|
+
try {
|
|
1059
|
+
// Read only the needed area from GeoTIFF (COG-optimized!)
|
|
1060
|
+
rasters = await image.readRasters({
|
|
1061
|
+
window: [readXMin, readYMin, readXMax, readYMax],
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
catch (err) {
|
|
1065
|
+
// warn('Error - readRasters - read window: ', readWindow);
|
|
1066
|
+
lasterr = err;
|
|
1067
|
+
}
|
|
1068
|
+
if (rasters != null) {
|
|
1069
|
+
lasterr = null;
|
|
1070
|
+
break;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (lasterr != null) {
|
|
1074
|
+
log(lasterr);
|
|
1075
|
+
warn('Error - readRasters - read window: ', readWindow);
|
|
1076
|
+
throw lasterr;
|
|
1077
|
+
}
|
|
1078
|
+
// Convert to TypedArray array and detect type
|
|
1079
|
+
const rasterBands = [];
|
|
1080
|
+
let arrayType = '';
|
|
1081
|
+
for (let i = 0; i < rasters.length; i++) {
|
|
1082
|
+
const raster = rasters[i];
|
|
1083
|
+
if (typeof raster === 'number') {
|
|
1084
|
+
warn('Unexpected number in rasters array');
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
rasterBands.push(raster);
|
|
1088
|
+
if (i === 0) {
|
|
1089
|
+
arrayType = raster.constructor.name;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
return { rasterBands, arrayType };
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Render tile pixels using triangulation-based reprojection
|
|
1096
|
+
*/
|
|
1097
|
+
renderTilePixels(params) {
|
|
1098
|
+
const { sampleSize, mercatorBounds, triangulation, rasterBands, arrayType, readWindow, ovWidth, ovHeight, resampleMethod, colorStops, } = params;
|
|
1099
|
+
const [mercWest, mercSouth, mercEast, mercNorth] = mercatorBounds;
|
|
1100
|
+
const [srcWest, srcSouth, srcEast, srcNorth] = this.config.sourceBounds;
|
|
1101
|
+
const srcWidth = srcEast - srcWest;
|
|
1102
|
+
const srcHeight = srcNorth - srcSouth;
|
|
1103
|
+
const outputData = new Uint8ClampedArray(sampleSize * sampleSize * 4); // RGBA
|
|
1104
|
+
let tri = null;
|
|
1105
|
+
for (let py = 0; py < sampleSize; py++) {
|
|
1106
|
+
for (let px = 0; px < sampleSize; px++) {
|
|
1107
|
+
const idx = (py * sampleSize + px) * 4; // RGBA index
|
|
1108
|
+
// Pixel position in Mercator
|
|
1109
|
+
const mercX = mercWest + (px / sampleSize) * (mercEast - mercWest);
|
|
1110
|
+
const mercY = mercNorth - (py / sampleSize) * (mercNorth - mercSouth);
|
|
1111
|
+
// Find triangle containing this pixel
|
|
1112
|
+
const targetPoint = [mercX, mercY];
|
|
1113
|
+
tri = triangulation.findSourceTriangleForTargetPoint(targetPoint, tri);
|
|
1114
|
+
if (tri) {
|
|
1115
|
+
// Transform using affine transformation
|
|
1116
|
+
const [srcX, srcY] = triangulation.applyAffineTransform(mercX, mercY, tri.transform);
|
|
1117
|
+
// Check if point is within source bounds
|
|
1118
|
+
if (srcX < srcWest ||
|
|
1119
|
+
srcX > srcEast ||
|
|
1120
|
+
srcY < srcSouth ||
|
|
1121
|
+
srcY > srcNorth) {
|
|
1122
|
+
// Outside bounds - transparent
|
|
1123
|
+
outputData[idx] = 0;
|
|
1124
|
+
outputData[idx + 1] = 0;
|
|
1125
|
+
outputData[idx + 2] = 0;
|
|
1126
|
+
outputData[idx + 3] = 0;
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
// Convert source coordinates to pixel coordinates (in overview image)
|
|
1130
|
+
const imgX = ((srcX - srcWest) / srcWidth) * ovWidth;
|
|
1131
|
+
const imgY = ((srcNorth - srcY) / srcHeight) * ovHeight;
|
|
1132
|
+
// Sample pixel from source raster
|
|
1133
|
+
const rgba = resampleMethod === 'near'
|
|
1134
|
+
? sampleNearest(imgX, imgY, rasterBands, arrayType, readWindow.readWidth, readWindow.readHeight, readWindow.readXMin, readWindow.readYMin, colorStops)
|
|
1135
|
+
: sampleBilinear(imgX, imgY, rasterBands, arrayType, readWindow.readWidth, readWindow.readHeight, readWindow.readXMin, readWindow.readYMin, colorStops);
|
|
1136
|
+
if (rgba) {
|
|
1137
|
+
outputData[idx] = rgba[0];
|
|
1138
|
+
outputData[idx + 1] = rgba[1];
|
|
1139
|
+
outputData[idx + 2] = rgba[2];
|
|
1140
|
+
outputData[idx + 3] = rgba[3];
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
// No data
|
|
1144
|
+
outputData[idx] = 0;
|
|
1145
|
+
outputData[idx + 1] = 0;
|
|
1146
|
+
outputData[idx + 2] = 0;
|
|
1147
|
+
outputData[idx + 3] = 0;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
// Pixel not in any triangle (should not happen)
|
|
1153
|
+
outputData[idx] = 0;
|
|
1154
|
+
outputData[idx + 1] = 0;
|
|
1155
|
+
outputData[idx + 2] = 0;
|
|
1156
|
+
outputData[idx + 3] = 0;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
return outputData;
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Check if tile intersects with GeoTIFF source bounds
|
|
1164
|
+
* @param viewBounds - Tile bounds in View projection (e.g., Web Mercator for deck.gl/Leaflet/Cesium)
|
|
1165
|
+
*/
|
|
1166
|
+
tileIntersectsSource(viewBounds) {
|
|
1167
|
+
const [viewWest, viewSouth, viewEast, viewNorth] = viewBounds;
|
|
1168
|
+
const [srcWest, srcSouth, srcEast, srcNorth] = this.config.sourceBounds;
|
|
1169
|
+
// Transform tile corners from View to Source projection
|
|
1170
|
+
const sw = this.config.transformViewToSourceMapFn([viewWest, viewSouth]);
|
|
1171
|
+
const ne = this.config.transformViewToSourceMapFn([viewEast, viewNorth]);
|
|
1172
|
+
const nw = this.config.transformViewToSourceMapFn([viewWest, viewNorth]);
|
|
1173
|
+
const se = this.config.transformViewToSourceMapFn([viewEast, viewSouth]);
|
|
1174
|
+
// Calculate bounding box of transformed tile in source projection
|
|
1175
|
+
const tileMinX = Math.min(sw[0], ne[0], nw[0], se[0]);
|
|
1176
|
+
const tileMaxX = Math.max(sw[0], ne[0], nw[0], se[0]);
|
|
1177
|
+
const tileMinY = Math.min(sw[1], ne[1], nw[1], se[1]);
|
|
1178
|
+
const tileMaxY = Math.max(sw[1], ne[1], nw[1], se[1]);
|
|
1179
|
+
// Check for intersection
|
|
1180
|
+
const intersects = tileMaxX >= srcWest &&
|
|
1181
|
+
tileMinX <= srcEast &&
|
|
1182
|
+
tileMaxY >= srcSouth &&
|
|
1183
|
+
tileMinY <= srcNorth;
|
|
1184
|
+
return intersects;
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Generate tile data with triangulation-based reprojection
|
|
1188
|
+
*
|
|
1189
|
+
* This is the main method that orchestrates the entire tile rendering process.
|
|
1190
|
+
*/
|
|
1191
|
+
async getTileData(params) {
|
|
1192
|
+
const { x, y, z, tileSize, resolution, resampleMethod, colorStops } = params;
|
|
1193
|
+
// 1. Calculate View projection bounds for the tile
|
|
1194
|
+
const viewBounds = this.getTileBounds(x, y, z);
|
|
1195
|
+
log(`v-map - geotiff - getTileData(${x},${y},${z}): viewBounds=[${viewBounds.map(v => v.toFixed(0)).join(',')}], sourceBounds=[${this.config.sourceBounds.map(v => v.toFixed(0)).join(',')}], toProjection=${this.config.toProjection}`);
|
|
1196
|
+
// 2. Early exit: Check if tile intersects with source bounds
|
|
1197
|
+
if (!this.tileIntersectsSource(viewBounds)) {
|
|
1198
|
+
// Tile is completely outside source bounds - return transparent tile
|
|
1199
|
+
log(`v-map - geotiff - getTileData(${x},${y},${z}): no intersection, returning transparent`);
|
|
1200
|
+
const sampleSize = Math.ceil(tileSize * resolution);
|
|
1201
|
+
return new Uint8ClampedArray(sampleSize * sampleSize * 4);
|
|
1202
|
+
}
|
|
1203
|
+
// 3. Calculate sampling resolution
|
|
1204
|
+
const sampleSize = Math.ceil(tileSize * resolution);
|
|
1205
|
+
// 4. Get or create triangulation
|
|
1206
|
+
let triangulation;
|
|
1207
|
+
if (!this.globalTriangulation) {
|
|
1208
|
+
warn('Global triangulation not available, creating fallback for tile');
|
|
1209
|
+
triangulation = new Triangulation(this.config.transformViewToSourceMapFn, viewBounds, 0.5);
|
|
1210
|
+
}
|
|
1211
|
+
else {
|
|
1212
|
+
triangulation = this.globalTriangulation;
|
|
1213
|
+
}
|
|
1214
|
+
// 5. Calculate source bounds for this tile
|
|
1215
|
+
const tileSrcBounds = this.calculateTileSourceBounds(viewBounds);
|
|
1216
|
+
// 6. Select best overview image based on zoom
|
|
1217
|
+
const { bestImage, bestResolution, imageLevel } = this.selectOverviewImage(z, tileSize);
|
|
1218
|
+
const ovWidth = bestImage.getWidth();
|
|
1219
|
+
const ovHeight = bestImage.getHeight();
|
|
1220
|
+
// 7. Calculate pixel window for reading
|
|
1221
|
+
const readWindow = this.calculateReadWindow(tileSrcBounds, ovWidth, ovHeight);
|
|
1222
|
+
if (!readWindow) {
|
|
1223
|
+
return new Uint8ClampedArray(sampleSize * sampleSize * 4);
|
|
1224
|
+
}
|
|
1225
|
+
// 8. Load and convert raster data
|
|
1226
|
+
const { rasterBands, arrayType } = await this.loadAndConvertRasterData(bestImage, readWindow);
|
|
1227
|
+
const bandCount = rasterBands.length;
|
|
1228
|
+
log(`Read window: [${readWindow.readXMin}, ${readWindow.readYMin}, ${readWindow.readXMax}, ${readWindow.readYMax}] (${readWindow.readWidth}x${readWindow.readHeight} pixels), ${bandCount} bands, type: ${arrayType}, imageLevel: ${imageLevel}, resolution: ${bestResolution}`);
|
|
1229
|
+
// 9. Render tile pixels
|
|
1230
|
+
const outputData = this.renderTilePixels({
|
|
1231
|
+
sampleSize,
|
|
1232
|
+
mercatorBounds: viewBounds, // Pass View bounds (kept name for backward compat)
|
|
1233
|
+
triangulation,
|
|
1234
|
+
rasterBands,
|
|
1235
|
+
arrayType,
|
|
1236
|
+
readWindow,
|
|
1237
|
+
ovWidth,
|
|
1238
|
+
ovHeight,
|
|
1239
|
+
resampleMethod,
|
|
1240
|
+
colorStops,
|
|
1241
|
+
});
|
|
1242
|
+
return outputData;
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Get raw elevation values for a tile as Float32Array.
|
|
1246
|
+
*
|
|
1247
|
+
* Returns a (tileSize+1) × (tileSize+1) float array suitable for Martini
|
|
1248
|
+
* terrain mesh generation. Border pixels are backfilled for Martini compatibility.
|
|
1249
|
+
* Band 0 of the GeoTIFF is used as the elevation source.
|
|
1250
|
+
*/
|
|
1251
|
+
async getElevationData(params) {
|
|
1252
|
+
const { x, y, z, tileSize } = params;
|
|
1253
|
+
const gridSize = tileSize + 1; // Martini requires (2^n + 1)^2 grid
|
|
1254
|
+
const viewBounds = this.getTileBounds(x, y, z);
|
|
1255
|
+
if (!this.tileIntersectsSource(viewBounds)) {
|
|
1256
|
+
return new Float32Array(gridSize * gridSize);
|
|
1257
|
+
}
|
|
1258
|
+
let triangulation;
|
|
1259
|
+
if (!this.globalTriangulation) {
|
|
1260
|
+
warn('Global triangulation not available, creating fallback for elevation tile');
|
|
1261
|
+
triangulation = new Triangulation(this.config.transformViewToSourceMapFn, viewBounds, 0.5);
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
triangulation = this.globalTriangulation;
|
|
1265
|
+
}
|
|
1266
|
+
const tileSrcBounds = this.calculateTileSourceBounds(viewBounds);
|
|
1267
|
+
const { bestImage } = this.selectOverviewImage(z, tileSize);
|
|
1268
|
+
const ovWidth = bestImage.getWidth();
|
|
1269
|
+
const ovHeight = bestImage.getHeight();
|
|
1270
|
+
const readWindow = this.calculateReadWindow(tileSrcBounds, ovWidth, ovHeight);
|
|
1271
|
+
if (!readWindow) {
|
|
1272
|
+
return new Float32Array(gridSize * gridSize);
|
|
1273
|
+
}
|
|
1274
|
+
const { rasterBands } = await this.loadAndConvertRasterData(bestImage, readWindow);
|
|
1275
|
+
const [mercWest, mercSouth, mercEast, mercNorth] = viewBounds;
|
|
1276
|
+
const [srcWest, srcSouth, srcEast, srcNorth] = this.config.sourceBounds;
|
|
1277
|
+
const srcWidth = srcEast - srcWest;
|
|
1278
|
+
const srcHeight = srcNorth - srcSouth;
|
|
1279
|
+
const output = new Float32Array(gridSize * gridSize);
|
|
1280
|
+
let tri = null;
|
|
1281
|
+
for (let py = 0; py < tileSize; py++) {
|
|
1282
|
+
for (let px = 0; px < tileSize; px++) {
|
|
1283
|
+
const mercX = mercWest + (px / tileSize) * (mercEast - mercWest);
|
|
1284
|
+
const mercY = mercNorth - (py / tileSize) * (mercNorth - mercSouth);
|
|
1285
|
+
const targetPoint = [mercX, mercY];
|
|
1286
|
+
tri = triangulation.findSourceTriangleForTargetPoint(targetPoint, tri);
|
|
1287
|
+
if (tri) {
|
|
1288
|
+
const [srcX, srcY] = triangulation.applyAffineTransform(mercX, mercY, tri.transform);
|
|
1289
|
+
if (srcX >= srcWest && srcX <= srcEast && srcY >= srcSouth && srcY <= srcNorth) {
|
|
1290
|
+
const imgX = ((srcX - srcWest) / srcWidth) * ovWidth;
|
|
1291
|
+
const imgY = ((srcNorth - srcY) / srcHeight) * ovHeight;
|
|
1292
|
+
const sampleX = Math.round(imgX) - readWindow.readXMin;
|
|
1293
|
+
const sampleY = Math.round(imgY) - readWindow.readYMin;
|
|
1294
|
+
if (sampleX >= 0 && sampleX < readWindow.readWidth &&
|
|
1295
|
+
sampleY >= 0 && sampleY < readWindow.readHeight) {
|
|
1296
|
+
const sampleValue = Number(rasterBands[0][sampleY * readWindow.readWidth + sampleX]);
|
|
1297
|
+
output[py * gridSize + px] =
|
|
1298
|
+
this.sanitizeElevationValue(sampleValue);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
// Backfill right border column (Martini requirement)
|
|
1305
|
+
for (let row = 0; row < tileSize; row++) {
|
|
1306
|
+
output[row * gridSize + tileSize] = output[row * gridSize + tileSize - 1];
|
|
1307
|
+
}
|
|
1308
|
+
// Backfill bottom border row (Martini requirement)
|
|
1309
|
+
for (let col = 0; col <= tileSize; col++) {
|
|
1310
|
+
output[tileSize * gridSize + col] = output[(tileSize - 1) * gridSize + col];
|
|
1311
|
+
}
|
|
1312
|
+
return output;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
async function getTileProcessorConfig(tiffSource, viewProjection) {
|
|
1316
|
+
const { default: proj4 } = await import('./index-RpJarvr_.js');
|
|
1317
|
+
// Transform from View projection to Source projection
|
|
1318
|
+
const transformViewToSourceMapFn = (coord) => {
|
|
1319
|
+
const result = proj4(viewProjection, tiffSource.fromProjection, coord);
|
|
1320
|
+
return [Number(result[0]), Number(result[1])];
|
|
1321
|
+
};
|
|
1322
|
+
// Inverse: Transform from Source projection to View projection
|
|
1323
|
+
const transformSourceMapToViewFn = (coord) => {
|
|
1324
|
+
const result = proj4(tiffSource.fromProjection, viewProjection, coord);
|
|
1325
|
+
return [Number(result[0]), Number(result[1])];
|
|
1326
|
+
};
|
|
1327
|
+
const config = {
|
|
1328
|
+
transformViewToSourceMapFn,
|
|
1329
|
+
transformSourceMapToViewFn,
|
|
1330
|
+
sourceBounds: tiffSource.sourceBounds,
|
|
1331
|
+
sourceRef: tiffSource.sourceRef,
|
|
1332
|
+
resolution: tiffSource.resolution,
|
|
1333
|
+
imageWidth: tiffSource.width,
|
|
1334
|
+
imageHeight: tiffSource.height,
|
|
1335
|
+
fromProjection: tiffSource.fromProjection,
|
|
1336
|
+
toProjection: viewProjection,
|
|
1337
|
+
baseImage: tiffSource.baseImage,
|
|
1338
|
+
overviewImages: tiffSource.overviewImages ?? [],
|
|
1339
|
+
noDataValue: tiffSource.noDataValue,
|
|
1340
|
+
};
|
|
1341
|
+
return config;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
//const DEFAULT_TO_PROJECTION = 'EPSG:3857';
|
|
1345
|
+
async function loadGeoTIFFSource(url, options, deps) {
|
|
1346
|
+
const { geotiff, proj4, geokeysToProj4 } = deps;
|
|
1347
|
+
const { fromUrl } = geotiff;
|
|
1348
|
+
const { toProj4 } = geokeysToProj4;
|
|
1349
|
+
let tiff = null;
|
|
1350
|
+
let lasterr = null;
|
|
1351
|
+
for (let i = 0; i <= 2; i++) {
|
|
1352
|
+
try {
|
|
1353
|
+
tiff = await fromUrl(url, {
|
|
1354
|
+
allowFullFile: true,
|
|
1355
|
+
blockSize: 1024 * 1024, // 1MB blocks to reduce HTTP range-request count for non-COG files
|
|
1356
|
+
cacheSize: 100,
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
catch (err) {
|
|
1360
|
+
lasterr = err;
|
|
1361
|
+
}
|
|
1362
|
+
if (tiff != null) {
|
|
1363
|
+
lasterr = null;
|
|
1364
|
+
break;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
if (lasterr != null) {
|
|
1368
|
+
log(lasterr);
|
|
1369
|
+
warn('Error - loadGeoTIFFSource - fromUrl: ', url);
|
|
1370
|
+
throw lasterr;
|
|
1371
|
+
}
|
|
1372
|
+
const baseImage = await tiff.getImage(0);
|
|
1373
|
+
const imageCount = await tiff.getImageCount();
|
|
1374
|
+
const overviewImages = [];
|
|
1375
|
+
for (let i = 1; i < imageCount; i++) {
|
|
1376
|
+
overviewImages.push(await tiff.getImage(i));
|
|
1377
|
+
}
|
|
1378
|
+
const width = baseImage.getWidth();
|
|
1379
|
+
const height = baseImage.getHeight();
|
|
1380
|
+
const samplesPerPixel = Math.max(1, baseImage.getSamplesPerPixel?.() ?? 1);
|
|
1381
|
+
let fromProjection = options.forceProjection && options.projection
|
|
1382
|
+
? options.projection
|
|
1383
|
+
: options.projection ?? 'EPSG:4326';
|
|
1384
|
+
let proj4String = null;
|
|
1385
|
+
if (!options.forceProjection) {
|
|
1386
|
+
const geoKeys = typeof baseImage.getGeoKeys === 'function'
|
|
1387
|
+
? baseImage.getGeoKeys() ?? null
|
|
1388
|
+
: null;
|
|
1389
|
+
if (geoKeys) {
|
|
1390
|
+
try {
|
|
1391
|
+
const projParams = toProj4(geoKeys);
|
|
1392
|
+
const epsg = geoKeys.ProjectedCSTypeGeoKey ?? geoKeys.GeographicTypeGeoKey;
|
|
1393
|
+
if (epsg) {
|
|
1394
|
+
fromProjection = `EPSG:${epsg}`;
|
|
1395
|
+
}
|
|
1396
|
+
if (projParams?.proj4) {
|
|
1397
|
+
proj4String = projParams.proj4;
|
|
1398
|
+
const numericCode = String(epsg);
|
|
1399
|
+
if (epsg && !proj4.defs(fromProjection)) {
|
|
1400
|
+
proj4.defs(fromProjection, projParams.proj4);
|
|
1401
|
+
}
|
|
1402
|
+
if (epsg && !proj4.defs(numericCode)) {
|
|
1403
|
+
proj4.defs(numericCode, projParams.proj4);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
catch (err) {
|
|
1408
|
+
warn('v-map - geotiff - failed to parse GeoKeys', err);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
if (!proj4String) {
|
|
1413
|
+
switch (fromProjection) {
|
|
1414
|
+
case 'EPSG:4326':
|
|
1415
|
+
proj4String = '+proj=longlat +datum=WGS84 +no_defs';
|
|
1416
|
+
break;
|
|
1417
|
+
case 'EPSG:3857':
|
|
1418
|
+
proj4String =
|
|
1419
|
+
'+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs';
|
|
1420
|
+
break;
|
|
1421
|
+
case 'EPSG:32632':
|
|
1422
|
+
proj4String = '+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs';
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (!fromProjection || fromProjection.trim() === '') {
|
|
1427
|
+
fromProjection = 'EPSG:4326';
|
|
1428
|
+
}
|
|
1429
|
+
if (proj4String && !proj4.defs(fromProjection)) {
|
|
1430
|
+
proj4.defs(fromProjection, proj4String);
|
|
1431
|
+
}
|
|
1432
|
+
const sourceBounds = baseImage.getBoundingBox();
|
|
1433
|
+
const sourceRef = [sourceBounds[0], sourceBounds[1]];
|
|
1434
|
+
const resolution = baseImage.getResolution()[0];
|
|
1435
|
+
const rawNoData = options.nodata !== undefined && options.nodata !== null
|
|
1436
|
+
? Number(options.nodata)
|
|
1437
|
+
: typeof baseImage.getGDALNoData === 'function'
|
|
1438
|
+
? baseImage.getGDALNoData()
|
|
1439
|
+
: undefined;
|
|
1440
|
+
const noDataValue = rawNoData !== undefined && rawNoData !== null
|
|
1441
|
+
? Number(rawNoData)
|
|
1442
|
+
: undefined;
|
|
1443
|
+
const clampLon = (lon) => Math.max(-180, Math.min(180, Number.isFinite(lon) ? lon : 0));
|
|
1444
|
+
const clampLat = (lat) => Math.max(-90, Math.min(90, Number.isFinite(lat) ? lat : 0));
|
|
1445
|
+
let transformToWgs84;
|
|
1446
|
+
if (!fromProjection || fromProjection === 'EPSG:4326') {
|
|
1447
|
+
transformToWgs84 = coord => coord;
|
|
1448
|
+
}
|
|
1449
|
+
else {
|
|
1450
|
+
transformToWgs84 = (coord) => {
|
|
1451
|
+
try {
|
|
1452
|
+
const result = proj4(fromProjection, 'EPSG:4326', coord);
|
|
1453
|
+
return [Number(result[0]), Number(result[1])];
|
|
1454
|
+
}
|
|
1455
|
+
catch (err) {
|
|
1456
|
+
warn('v-map - geotiff - transform to WGS84 failed, falling back', err);
|
|
1457
|
+
return coord;
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
const [minX, minY, maxX, maxY] = sourceBounds;
|
|
1462
|
+
const corners = [
|
|
1463
|
+
transformToWgs84([minX, minY]),
|
|
1464
|
+
transformToWgs84([maxX, minY]),
|
|
1465
|
+
transformToWgs84([maxX, maxY]),
|
|
1466
|
+
transformToWgs84([minX, maxY]),
|
|
1467
|
+
];
|
|
1468
|
+
const west = clampLon(Math.min(...corners.map(c => c[0])));
|
|
1469
|
+
const east = clampLon(Math.max(...corners.map(c => c[0])));
|
|
1470
|
+
const south = clampLat(Math.min(...corners.map(c => c[1])));
|
|
1471
|
+
const north = clampLat(Math.max(...corners.map(c => c[1])));
|
|
1472
|
+
log('v-map - geotiff - loaded source', {
|
|
1473
|
+
url,
|
|
1474
|
+
width,
|
|
1475
|
+
height,
|
|
1476
|
+
samplesPerPixel,
|
|
1477
|
+
fromProjection,
|
|
1478
|
+
bounds: sourceBounds,
|
|
1479
|
+
});
|
|
1480
|
+
return {
|
|
1481
|
+
tiff,
|
|
1482
|
+
baseImage,
|
|
1483
|
+
overviewImages,
|
|
1484
|
+
width,
|
|
1485
|
+
height,
|
|
1486
|
+
samplesPerPixel,
|
|
1487
|
+
fromProjection,
|
|
1488
|
+
//toProjection: DEFAULT_TO_PROJECTION,
|
|
1489
|
+
sourceBounds,
|
|
1490
|
+
sourceRef,
|
|
1491
|
+
resolution,
|
|
1492
|
+
proj4,
|
|
1493
|
+
noDataValue,
|
|
1494
|
+
wgs84Bounds: [west, south, east, north],
|
|
1495
|
+
transformToWgs84,
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
async function getGeoTIFFSource(url, projection, forceProjection, nodata) {
|
|
1499
|
+
const [geotiffModule, { default: proj4 }, geokeysModule] = await Promise.all([
|
|
1500
|
+
import('./geotiff-BEWxTIfH.js').then(function (n) { return n.g; }),
|
|
1501
|
+
import('./index-RpJarvr_.js'),
|
|
1502
|
+
import('./main-dist-CwnA7_Xn.js').then(function (n) { return n.m; }),
|
|
1503
|
+
]);
|
|
1504
|
+
const source = await loadGeoTIFFSource(url, {
|
|
1505
|
+
projection: projection,
|
|
1506
|
+
forceProjection: forceProjection,
|
|
1507
|
+
nodata: nodata,
|
|
1508
|
+
}, {
|
|
1509
|
+
geotiff: geotiffModule,
|
|
1510
|
+
proj4,
|
|
1511
|
+
geokeysToProj4: geokeysModule,
|
|
1512
|
+
});
|
|
1513
|
+
return source;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
export { GeoTIFFTileProcessor as G, getGeoTIFFSource as a, getTileProcessorConfig as b, getColorStops as g, loadGeoTIFFSource as l };
|