@tomtom-org/maps-sdk-plugin-landmarks-3d 0.0.12 → 0.0.13

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.txt CHANGED
@@ -14,7 +14,7 @@ You may not use the SDK or accept this Agreement if you are barred from receivin
14
14
 
15
15
  3.1. You agree to use the SDK only for purposes permitted by (a) this Agreement and (b) any applicable law, regulation or generally accepted practice or guideline in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
16
16
 
17
- 3.2. Other than as permitted by applicable legislation, you are prohibited from copying (except for backup purposes), modifying, adapting, (re)distributing, renting, lending, publicly presenting, decompiling, reverse engineering, disassembling or creating derivative works of the SDK or any part of it. Notwithstanding the foregoing, you may inspect the SDK as distributed to you, including any source code embedded in its source maps, solely for the purpose of debugging your Permitted Application, provided that it is not shared or redistributed. The readability of the SDK’s source code in its distribution does not constitute “Open-Source” licensing and grants you no rights under any Open Source Initiative (OSI) approved license, nor shall it be construed as a grant of rights to modify, fork or redistribute the SDK beyond those expressly granted in Clause 1 and this Clause 3.2.
17
+ 3.2. Except as permitted by applicable law or this Clause 3.2, you may not copy (except for backup purposes), modify, adapt, distribute, rent, lend, publicly present, decompile, reverse engineer, disassemble or create derivative works of the SDK. Solely for your Permitted Applications and subject to Clauses 3.3 to 3.5, you may: (a) inspect the SDK, including source code in its source maps, for debugging; (b) copy the SDK as reasonably necessary to develop, build, test and deploy them, including in internal repositories, mirrors and offline build environments used only by you, your Affiliates and your contractors; (c) modify the SDK to fix errors or security vulnerabilities, to maintain compatibility, or to prepare a Contribution under Clause 11.5, but you may not alter, circumvent or disable any functionality for authentication, metering, billing, the notices in Clause 5.2 or the data collection in Clause 4; and (d) distribute the SDK only as a bundled part of a Permitted Application, and not on a standalone basis or as a library. Modified versions remain part of the SDK, and TomTom need not support them. On termination, you must delete all copies held under (b), except copies already distributed under (d). The readability of the SDK's source code does not make it open source and grants no rights beyond those in Clause 1 and this Clause 3.2.
18
18
 
19
19
  3.3. You may not use the SDK in connection with any illegal, fraudulent, dishonest or unethical activity or behaviour, and in particular not to create or develop, or help others create or develop, a product, service or library that competes, directly or indirectly, with the SDK or the TomTom APIs.
20
20
 
@@ -22,7 +22,7 @@ You may not use the SDK or accept this Agreement if you are barred from receivin
22
22
 
23
23
  3.5. The SDK may include data or software that TomTom licenses from third parties, including open-source software. Such components are made available to you under the terms and conditions of their applicable licenses, and you shall comply with all requirements and restrictions that those third parties may require TomTom to impose on you (as may be updated and amended by TomTom from time to time). It is your responsibility to check, review and comply with those terms as they apply to the SDK licensed to you under this Agreement. The applicable third party and open-source notices are set out in the accompanying THIRD_PARTY.txt file. In the event of a conflict between this Agreement and an open-source license governing a specific dependency, that open-source license shall prevail solely with respect to that dependency.
24
24
 
25
- 4. PRIVACY: In order to improve the TomTom Developer Portal and the SDK, TomTom may collect certain usage related information from the software, including but not limited to general SDK and product identifiers, version numbers, and information on which tools and/or services in the SDK are used and how you use them. In addition, TomTom may collect your unique identifier and associated IP address for legitimate business reasons (such as, without limitation, the processing of invoices). This data is retained for a maximum period of 120 days and is maintained in accordance with our Privacy Policy.
25
+ 4. PRIVACY: In order to improve the TomTom Developer Portal and the SDK, TomTom may collect certain usage related information from the software, including but not limited to general SDK and product identifiers, version numbers, and information on which tools and/or services in the SDK are used and how you use them. In addition, TomTom may collect your unique identifier and associated IP address for legitimate business reasons (such as, without limitation, the processing of invoices). This data is retained for a maximum period of 120 days and is maintained in accordance with our Privacy Policy (https://www.tomtom.com/privacy/).
26
26
 
27
27
  5. INTELLECTUAL PROPERTY RIGHTS AND COPYRIGHT NOTICES:
28
28
 
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Landmarks 3D Plugin
2
2
 
3
- Plugin for the [TomTom Maps SDK for JavaScript](https://docs.tomtom.com/maps-sdk-js/introduction/overview) that renders TomTom Orbis 3D Landmarks — high-detail building meshes streamed as tiles — on a `TomTomMap` using Three.js.
3
+ This plugin for the [TomTom Maps SDK for JavaScript](https://docs.tomtom.com/maps-sdk-js/introduction/overview) renders TomTom Orbis 3D Landmarks on a `TomTomMap` with Three.js. The landmarks are high-detail building meshes, streamed as tiles.
4
4
 
5
- > **Private Preview**: the [Orbis 3D Landmarks API](https://developer.tomtom.com/map-display-api/documentation/tomtom-orbis-maps/3d/landmarks) is in Private Preview. Your API key needs Orbis 3D Landmarks entitlements.
5
+ > **Private Preview.** The [Orbis 3D Landmarks API](https://developer.tomtom.com/map-display-api/documentation/tomtom-orbis-maps/3d/landmarks) is in Private Preview, so your API key needs Orbis 3D Landmarks entitlements.
6
6
 
7
7
  ## Docs & examples
8
8
 
@@ -14,18 +14,18 @@ Plugin for the [TomTom Maps SDK for JavaScript](https://docs.tomtom.com/maps-sdk
14
14
  - Streams 3D landmark tiles for the current viewport.
15
15
  - Shows the basemap 3D building layer (hidden by default in the standard styles) for full 3D city context.
16
16
  - Hides basemap extruded buildings flagged with `has_landmark` so they don't clip through the landmark meshes.
17
- - Display modes: `inherited` (default, mirrors the basemap building colour/opacity), `dark`, and `light`.
17
+ - Three display modes: `inherited` (the default, mirrors the basemap building colour and opacity), `dark` and `light`.
18
18
  - Restores itself automatically across map style changes.
19
19
 
20
20
  ## Quickstart
21
21
 
22
- Note: this plugin declares `@tomtom-org/maps-sdk`, `maplibre-gl` and `three` as peer dependencies — ensure all three are installed in your project.
22
+ This plugin declares `@tomtom-org/maps-sdk`, `maplibre-gl` and `three` as peer dependencies, so install all three in your project too.
23
23
 
24
24
  ```bash
25
25
  npm install @tomtom-org/maps-sdk maplibre-gl three @tomtom-org/maps-sdk-plugin-landmarks-3d
26
26
  ```
27
27
 
28
- 1. Follow the SDK [Project setup](https://docs.tomtom.com/maps-sdk-js/introduction/project-setup) or the Map [quickstart](https://docs.tomtom.com/maps-sdk-js/guides/map/quickstart) to create and initialize a `TomTomMap`. Use a pitched camera for the best result; the plugin enables the standard style's 3D building layer automatically.
28
+ 1. Follow the SDK [Project setup](https://docs.tomtom.com/maps-sdk-js/introduction/project-setup) or the Map [quickstart](https://docs.tomtom.com/maps-sdk-js/guides/map/quickstart) to create and initialize a `TomTomMap`. For the best result, use a pitched camera. The plugin turns on the standard style's 3D building layer for you.
29
29
 
30
30
  2. Import and use the plugin (plugin-specific steps only):
31
31
 
@@ -42,7 +42,7 @@ await landmarks.setDisplayMode('dark');
42
42
  await landmarks.setVisible(false);
43
43
  ```
44
44
 
45
- `new Landmarks3D(map, { displayMode, visible, minZoom, maxZoom })` sets the same options up front; the zoom range defaults to that of the base map's 3D building layer.
45
+ `new Landmarks3D(map, { displayMode, visible, minZoom, maxZoom })` sets the same options up front. By default, the zoom range matches that of the base map's 3D building layer.
46
46
 
47
47
  ## License
48
48
 
package/THIRD_PARTY.txt CHANGED
@@ -14,7 +14,7 @@ by the licenses reproduced here.
14
14
  --------------------------------------------------------------------------------
15
15
 
16
16
  Name: maplibre-gl
17
- Version: 6.12.0
17
+ Version: 6.13.0
18
18
  License: BSD-3-Clause
19
19
  Bundled: false (peer dependency)
20
20
  Description: BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
@@ -4,7 +4,7 @@ your application bundles them. The licenses below apply to those copies, and eac
4
4
  these components may in turn install its own dependencies under their own licenses.
5
5
 
6
6
  Name: maplibre-gl
7
- Version: 6.12.0
7
+ Version: 6.13.0
8
8
  License: BSD-3-Clause
9
9
  Bundled: false (peer dependency)
10
10
  Description: BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
package/dist/index.d.ts CHANGED
@@ -162,6 +162,7 @@ export declare class ModelsSource {
162
162
  private readonly gltfLoader;
163
163
  private readonly specification;
164
164
  private readonly withCredentials;
165
+ private coveringTileKeys;
165
166
  constructor(id: string, options: ModelsSourceSpecification);
166
167
  onAdd(map: Map, renderer: WebGLRenderer): void;
167
168
  updateTiles(): void;
@@ -180,6 +181,10 @@ export declare class ModelsSource {
180
181
  * viewport, renders them as maplibre-style fill-extrusion buildings, and keeps its
181
182
  * lighting in sync with the map style light.
182
183
  *
184
+ * It draws whenever the map draws, and asks for a frame of its own only when a tile arrives,
185
+ * so a still map stays idle. After changing one of its fields yourself, call
186
+ * `triggerRepaint()` on the MapLibre map.
187
+ *
183
188
  * @group Landmarks 3D
184
189
  */
185
190
  export declare class ModelsLayer implements CustomLayerInterface {
@@ -201,11 +206,15 @@ export declare class ModelsLayer implements CustomLayerInterface {
201
206
  private readonly fillExtrusionMaterial;
202
207
  private readonly depthPrepassMaterial;
203
208
  private readonly maskedFillExtrusionMaterials;
209
+ private readonly tilesMainMatrix;
210
+ private tilesStale;
211
+ private readonly markTilesStale;
204
212
  constructor(options: ModelsLayerSpecification, source: ModelsSourceSpecification);
205
213
  updateProperties(options: ModelsLayerSpecification): void;
206
214
  setDirectionalLight(radius: number, phi: number, theta: number): void;
207
215
  onAdd(map: Map, gl: WebGLRenderingContext | WebGL2RenderingContext): void;
208
216
  render(_gl: WebGLRenderingContext | WebGL2RenderingContext, options: CustomRenderMethodInput): void;
217
+ private updateTilesWhenStale;
209
218
  private groundLandmarksOnTerrain;
210
219
  private groundMeshOnTerrain;
211
220
  private terrainSamplePoints;
package/dist/index.es.js CHANGED
@@ -1 +1 @@
1
- import{TomTomConfig as e,isProxyCredentialsMode as i}from"@tomtom-org/maps-sdk/core";import{LayerFilterComposer as t,mapStyleLayerIDs as a}from"@tomtom-org/maps-sdk/map";import{AmbientLight as s,BufferAttribute as r,Camera as n,Color as o,CustomBlending as l,DirectionalLight as h,DoubleSide as d,FrontSide as m,Group as c,LoaderUtils as u,LoadingManager as p,Matrix4 as f,OneFactor as y,OneMinusSrcAlphaFactor as g,SRGBColorSpace as v,Scene as x,ShaderMaterial as M,Vector3 as L,WebGLRenderer as b}from"three";import{MeshoptDecoder as w}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{GLTFLoader as k}from"three/examples/jsm/loaders/GLTFLoader.js";import{KTX2Loader as C}from"three/examples/jsm/loaders/KTX2Loader.js";import{toCreasedNormals as P}from"three/examples/jsm/utils/BufferGeometryUtils.js";import{MercatorCoordinate as D}from"maplibre-gl";var S={diffuseColor:"hsl(38, 6%, 90%)",opacity:.7,verticalGradient:!0},z={diffuseColor:"hsl(210, 9%, 17%)",opacity:.7,verticalGradient:!0},T=()=>{const{commonBaseURL:i,apiKey:t}=e.instance.get();return`${i}/maps/orbis/display/3d/landmarks/tile/{z}/{x}/{y}?${t?`key=${t}&`:""}apiVersion=2`},B="fillExtrusionGradient",O=`\n attribute float ${B};\n varying vec3 vWorldNormal;\n varying float vGradient;\n #ifdef USE_ALPHA_MASK\n varying vec2 vUv;\n #endif\n\n void main() {\n vWorldNormal = normalize(mat3(modelMatrix) * normal);\n vGradient = ${B};\n #ifdef USE_ALPHA_MASK\n vUv = uv;\n #endif\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n`,E="\n #ifdef USE_ALPHA_MASK\n uniform sampler2D alphaMask;\n uniform float alphaMaskCutoff;\n varying vec2 vUv;\n #endif\n",I="\n #ifdef USE_ALPHA_MASK\n if (texture2D(alphaMask, vUv).a < alphaMaskCutoff) {\n discard;\n }\n #endif\n",A=`\n uniform vec3 diffuseColor;\n uniform vec3 lightPosition;\n uniform float lightIntensity;\n uniform vec3 lightColor;\n uniform float layerOpacity;\n uniform float verticalGradient;\n varying vec3 vWorldNormal;\n varying float vGradient;\n ${E}\n\n void main() {\n ${I}\n vec3 normal = normalize(vWorldNormal);\n // Double-sided (alpha-masked) surfaces show their interior through carved openings;\n // flip backface normals so those interiors shade like the facade instead of going dark.\n if (!gl_FrontFacing) {\n normal = -normal;\n }\n // Extrusions have only vertical walls and flat roofs, so a sloped landmark roof would shade\n // darker than every basemap roof around it: up-facing normals light like a flat roof, the\n // rest like a wall, blended for curved surfaces.\n vec3 flattened = vec3(normal.xy, 0.0);\n vec3 wallNormal = flattened / max(length(flattened), 1e-5);\n float upness = smoothstep(0.4, 0.8, normal.z);\n normal = normalize(mix(wallNormal, vec3(0.0, 0.0, 1.0), upness));\n float colorValue = dot(diffuseColor, vec3(0.2126, 0.7152, 0.0722));\n vec3 color = diffuseColor + vec3(0.03);\n float directional = clamp(dot(normal, lightPosition), 0.0, 1.0);\n directional = mix(1.0 - lightIntensity, max(1.0 - colorValue + lightIntensity, 1.0), directional);\n if (abs(normal.y) > 0.001) {\n directional *=\n (1.0 - verticalGradient) +\n verticalGradient * clamp(vGradient, mix(0.7, 0.98, 1.0 - lightIntensity), 1.0);\n }\n vec3 shaded = clamp(\n color * directional * lightColor,\n mix(vec3(0.0), vec3(0.3), vec3(1.0) - lightColor),\n vec3(1.0)\n );\n gl_FragColor = vec4(shaded, 1.0) * layerOpacity;\n }\n`,F=new L(.2875,-.498,.996),G=new o,_=new o,R=class extends M{constructor(e){super({uniforms:{diffuseColor:{value:new L(1,1,1)},lightPosition:{value:F.clone()},lightIntensity:{value:.5},lightColor:{value:new L(1,1,1)},layerOpacity:{value:1},verticalGradient:{value:1},...e&&{alphaMask:{value:e.map},alphaMaskCutoff:{value:e.cutoff}}},...e&&{defines:{USE_ALPHA_MASK:""}},vertexShader:O,fragmentShader:A,side:e?d:m,transparent:!0,blending:l,blendSrc:y,blendDst:g})}copyShadingFrom(e){this.uniforms.diffuseColor.value.copy(e.uniforms.diffuseColor.value),this.uniforms.lightPosition.value.copy(e.uniforms.lightPosition.value),this.uniforms.lightIntensity.value=e.uniforms.lightIntensity.value,this.uniforms.lightColor.value.copy(e.uniforms.lightColor.value),this.uniforms.layerOpacity.value=e.uniforms.layerOpacity.value,this.uniforms.verticalGradient.value=e.uniforms.verticalGradient.value}setDiffuseColor(e){G.setStyle(e),G.getRGB(_,v),this.uniforms.diffuseColor.value.set(_.r,_.g,_.b)}setLayerOpacity(e){this.uniforms.layerOpacity.value=e}setVerticalGradient(e){this.uniforms.verticalGradient.value=e?1:0}setLight(e,i,t,a){const s=Math.cos(i),r=Math.sin(i);this.uniforms.lightPosition.value.set(e.x*s-e.y*r,e.x*r+e.y*s,e.z),this.uniforms.lightIntensity.value=t,this.uniforms.lightColor.value.set(a.r,a.g,a.b)}},U=class extends M{constructor(e){super({...e&&{uniforms:{alphaMask:{value:e.map},alphaMaskCutoff:{value:e.cutoff}},defines:{USE_ALPHA_MASK:""}},vertexShader:O,fragmentShader:`\n ${E}\n void main() {\n ${I}\n gl_FragColor = vec4(0.0);\n }\n `,side:e?d:m,colorWrite:!1})}},Z=e=>!0===e.isMesh,j=1/(2*Math.PI*6378137),V=Math.PI/6,$=class{type="models";id;minzoom;maxzoom;map;tiles;scene;loadedTiles=/* @__PURE__ */new Set;sceneOrigin=null;onTileLoaded;ktx2Loader;transcoderReady;gltfLoader;specification;withCredentials;constructor(e,i){this.id=e,this.minzoom=i.minzoom??0,this.maxzoom=i.maxzoom??22,this.tiles=i.tiles,this.specification=i,this.withCredentials=i.withCredentials??!1,this.scene=new c,this.ktx2Loader=new C(new p).setCrossOrigin("anonymous").setTranscoderPath(i.transcoderPath??""),this.gltfLoader=(new k).setKTX2Loader(this.ktx2Loader).setMeshoptDecoder(w)}onAdd(e,i){this.map=e,this.ktx2Loader.detectSupport(i)}updateTiles(){if(0===this.tiles.length)return;const e=/* @__PURE__ */new Set;for(const i of this.coveringTiles())e.add(i.key),this.loadTile(i);this.showCoveringTilesOnly(e)}showCoveringTilesOnly(e){for(const i of this.scene.children)i.visible=e.has(i.userData.key)}coveringTiles(){const e={tileSize:this.map._camera.transform.tileSize,minzoom:this.minzoom,maxzoom:this.maxzoom,terrain:this.map.terrain};return this.map.coveringTiles(e)}loadTile(e){if(this.loadedTiles.has(e.key))return;this.loadedTiles.add(e.key);const i=e.canonical.url(this.tiles,this.map.getPixelRatio(),this.specification.scheme);this.fetchTile(i).then(i=>{i&&(W(i),this.rebaseToSceneOrigin(i),i.userData={isTile:!0,key:e.key},this.scene.add(i),this.onTileLoaded?.(i,e.key))}).catch(i=>{console.warn(`Problem with loading ${e.toString()}: ${i?.message??"unknown"}`)})}rebaseToSceneOrigin(e){const i=[];if(e.traverse(e=>{Z(e)&&i.push(e)}),0===i.length)return;const t=i[0].position,a={x:Math.round(t.x),y:Math.round(t.y)};if(this.sceneOrigin){if(Math.hypot(t.x-this.sceneOrigin.x,t.y-this.sceneOrigin.y)>1e5){const e=a.x-this.sceneOrigin.x,i=a.y-this.sceneOrigin.y;this.scene.traverse(t=>{Z(t)&&(t.position.x-=e,t.position.y-=i)}),this.sceneOrigin=a}}else this.sceneOrigin=a;for(const s of i)s.position.x-=this.sceneOrigin.x,s.position.y-=this.sceneOrigin.y}async fetchTile(e){const i=await fetch(e,this.withCredentials?{credentials:"include"}:void 0);if(204===i.status||404===i.status)return null;if(403===i.status&&"InvalidReferer"!==await(async e=>(await e.json().catch(()=>{}))?.detailedError?.code)(i))throw new Error("tile request failed with 403: Orbis 3D Landmarks is in Private Preview (https://docs.tomtom.com/legal/private-preview), and the API key needs access to it: request it from TomTom Sales at https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=map-display-api-3d");if(!i.ok)throw new Error(`tile request failed with ${i.status}`);const t=await i.arrayBuffer();this.transcoderReady??=(async(e,i)=>{if(void 0!==i)return;const{resolveBundledTranscoderUrl:t}=await(import("./bundledTranscoder-Dn27cuYm.js"));e.setURLModifier(t)})(this.ktx2Loader.manager,this.specification.transcoderPath),await this.transcoderReady;const{scene:a}=await this.gltfLoader.parseAsync(t,u.extractUrlBase(e));return a}},W=e=>{e.traverse(e=>{if(!Z(e))return;const i=e.material;i.alphaTest=.1,i.side=d,e.userData.originalMaterial=i,e.geometry=P(e.geometry,V),K(e)})},K=e=>{const i=e.geometry,t=i.getAttribute("position");i.computeBoundingBox();const a=i.boundingBox?.max.z??0,s=a>0?Math.sqrt(a/150)/a:0,n=new Float32Array(t.count);for(let r=0;r<t.count;r++)n[r]=t.getZ(r)*s;i.setAttribute(B,new r(n,1))},H=e=>e*Math.PI/180,N=class{id;type="custom";renderingMode="3d";renderer;scene;camera;tiles;directionalLight;ambientLight;map;source;minzoom=0;maxzoom=22;visible=!0;diffuseColor="#ffffff";fillExtrusionMaterial=new R;depthPrepassMaterial=new U;maskedFillExtrusionMaterials=/* @__PURE__ */new Map;constructor(e,i){this.id=e.id,this.source=new $(this.id,i),this.tiles=this.source.scene,this.updateProperties(e)}updateProperties(e){this.visible="none"!==e.layout?.visibility,this.diffuseColor=e.paint?.["models-base-color"]??"#ffffff",this.minzoom=e.minzoom??0,this.maxzoom=e.maxzoom??22}setDirectionalLight(e,i,t){this.directionalLight.position.setFromSphericalCoords(e,H(i),H(t))}onAdd(e,i){this.map=e,this.scene=new x,this.tiles=this.source.scene,this.camera=new n,this.directionalLight=new h(16777215,.5),this.setDirectionalLight(1.15,90,0),this.scene.add(this.directionalLight),this.ambientLight=new s(16777215,.1),this.scene.add(this.ambientLight),this.scene.add(this.tiles),this.renderer=new b({canvas:e.getCanvas(),context:i,antialias:!0}),this.renderer.autoClear=!1,this.source.onAdd(e,this.renderer)}render(e,i){const t=this.map._camera.transform.tileZoom,a=t>=this.minzoom&&t<=this.maxzoom;this.tiles.visible=this.visible&&a,this.visible&&a&&(this.syncLighting(),this.source.updateTiles(),this.groundLandmarksOnTerrain(),this.alignCameraToMap(i),this.renderer.resetState(),this.needsTranslucentPrepass()&&this.renderDepthPrepass(),this.applyMaterials(),this.renderer.render(this.scene,this.camera),this.map.triggerRepaint())}groundLandmarksOnTerrain(){for(const e of this.tiles.children)e.visible&&e.traverse(e=>{Z(e)&&this.groundMeshOnTerrain(e)})}groundMeshOnTerrain(e){const i=this.terrainSamplePoints(e);if(!i)return;let t=null;for(const a of i){const i=this.map.queryTerrainElevation(a);if(null===i)return void(e.position.z=0);(null===t||i<t)&&(t=i)}null!==t&&(e.position.z=t-.5)}terrainSamplePoints(e){let i=e.userData.terrainSamplePoints;if(!i){e.geometry.boundingBox||e.geometry.computeBoundingBox();const t=e.geometry.boundingBox;if(!t||t.isEmpty())return null;const a=e.position.x+this.sceneOrigin.x,s=e.position.y+this.sceneOrigin.y,r=(t.min.x+t.max.x)/2,n=(t.min.y+t.max.y)/2;i=[[r,n],[t.min.x,t.min.y],[t.min.x,t.max.y],[t.max.x,t.min.y],[t.max.x,t.max.y],[r,t.min.y],[r,t.max.y],[t.min.x,n],[t.max.x,n]].map(([e,i])=>new D(.5+(a+e)*j,.5-(s+i)*j).toLngLat().toArray()),e.userData.terrainSamplePoints=i}return i}alignCameraToMap(e){const i=j/Math.cos(H(this.map.getCenter().lat)),t=.5+j*this.sceneOrigin.x,a=.5-j*this.sceneOrigin.y,s=new f;s.set(j,0,0,t,0,-j,0,a,0,0,i,0,0,0,0,1);const r=(new f).fromArray(e.defaultProjectionData.mainMatrix);this.camera.projectionMatrix.copy(r).multiply(s)}get sceneOrigin(){return this.source.sceneOrigin??{x:0,y:0}}needsTranslucentPrepass(){return this.fillExtrusionMaterial.uniforms.layerOpacity.value<1}renderDepthPrepass(){this.tiles.traverse(e=>{Z(e)&&(e.material=this.maskedFillExtrusionVariant(e)?.depth??this.depthPrepassMaterial)}),this.renderer.render(this.scene,this.camera)}setOpacity(e){this.forEachFillExtrusionMaterial(i=>i.setLayerOpacity(e))}setVerticalGradient(e){this.forEachFillExtrusionMaterial(i=>i.setVerticalGradient(e))}getMaterial(e){return this.maskedFillExtrusionVariant(e)?.color??this.fillExtrusionMaterial}maskedFillExtrusionVariant(e){const i=e.userData.originalMaterial,t=i?.map;if(!t)return null;let a=this.maskedFillExtrusionMaterials.get(t);if(!a){const e={map:t,cutoff:i.alphaTest||.1};a={color:new R(e),depth:new U(e)},a.color.copyShadingFrom(this.fillExtrusionMaterial),this.maskedFillExtrusionMaterials.set(t,a)}return a}forEachFillExtrusionMaterial(e){e(this.fillExtrusionMaterial),this.maskedFillExtrusionMaterials.forEach(i=>e(i.color))}syncLighting(){const e=this.map,i=e.style?.light,t=i?.properties,a=i?.getCartesianPosition?.();if(t&&a)try{const i=t.get("intensity");this.directionalLight.intensity=i*Math.PI,this.ambientLight.intensity=(1-i)*Math.PI;const s={x:-a[0],y:a[1],z:a[2]};this.directionalLight.position.set(s.x,s.y,s.z);const r="viewport"===t.get("anchor")?-(e._camera.transform.bearingInRadians??0):0,n=t.get("color");this.forEachFillExtrusionMaterial(e=>e.setLight(s,r,i,n))}catch{}}applyMaterials(){this.forEachFillExtrusionMaterial(e=>e.setDiffuseColor(this.diffuseColor)),this.tiles.traverse(e=>{Z(e)&&(e.material=this.getMaterial(e))})}},q=["!",["coalesce",["get","has_landmark"],!1]],X=class{map;layerID="orbis-3d-landmarks";basemapBuildingLayerID="3D - Building";modelsLayer;layerMinZoom;layerMaxZoom;displayMode;constructor(e,t={}){this.map=e,this.layerMinZoom=t.minZoom,this.layerMaxZoom=t.maxZoom,this.displayMode=t.displayMode??"inherited";const a={id:this.layerID,type:"models",source:this.layerID,minzoom:t.minZoom??10,maxzoom:t.maxZoom??22},s={type:"models",tiles:[T()],minzoom:15,maxzoom:15,withCredentials:t.withCredentials??i()};this.modelsLayer=new N(a,s),this.modelsLayer.visible=t.visible??!0,this.map.addStyleChangeHandler({onStyleChanged:()=>this.install()}),this.installWhenReady()}installWhenReady(){this.map.mapReady?this.install():this.map.mapLibreMap.once("styledata",()=>this.installWhenReady())}get layer(){return this.modelsLayer}getDisplayMode(){return this.displayMode}async setDisplayMode(e){this.displayMode=e,this.map.mapReady&&this.applyDisplayMode()}isVisible(){return this.modelsLayer.visible}async setVisible(e){this.modelsLayer.visible=e,this.map.mapReady&&this.map.mapLibreMap.triggerRepaint()}install(){const e=this.map.mapLibreMap;e.getLayer(this.layerID)||e.addLayer(this.modelsLayer,this.landmarkInsertBeforeID()),e.getLayer(this.basemapBuildingLayerID)&&e.setLayoutProperty(this.basemapBuildingLayerID,"visibility","visible"),this.alignZoomRangeWithBasemapBuildings(),this.excludeLandmarkBuildingsFromBasemap(),this.modelsLayer.source.onTileLoaded=()=>{this.resolveCurrentMaterialState().opacity<1&&this.applyDisplayMode()},this.applyDisplayMode()}landmarkInsertBeforeID(){const e=a.poi;return this.map.mapLibreMap.getLayer(e)?e:void 0}alignZoomRangeWithBasemapBuildings(){const e=this.map.mapLibreMap.getLayer(this.basemapBuildingLayerID);e&&(this.modelsLayer.minzoom=this.layerMinZoom??e.minzoom??10,this.modelsLayer.maxzoom=this.layerMaxZoom??e.maxzoom??22)}excludeLandmarkBuildingsFromBasemap(){t.for(this.map).setClause(this.basemapBuildingLayerID,"landmarks3d.buildings",q)}readBasemapBuildingMaterial(){const e=this.map.mapLibreMap;if(!e.getLayer(this.basemapBuildingLayerID))return null;const i=(e=>{if("string"==typeof e)return e;if(Array.isArray(e)&&"match"===e[0]){const i=e.at(-1);if("string"==typeof i)return i}return null})(e.getPaintProperty(this.basemapBuildingLayerID,"fill-extrusion-color"));if(!i)return null;const t=e.getPaintProperty(this.basemapBuildingLayerID,"fill-extrusion-opacity"),a=e.getPaintProperty(this.basemapBuildingLayerID,"fill-extrusion-vertical-gradient");return{diffuseColor:i,..."number"==typeof t&&{opacity:t},..."boolean"==typeof a&&{verticalGradient:a}}}resolveCurrentMaterialState(){const e="inherited"===this.displayMode?this.readBasemapBuildingMaterial():null;return((e,i)=>{switch(e){case"dark":return z;case"light":return S;default:return{...S,...i}}})(this.displayMode,e)}applyDisplayMode(){const e=this.resolveCurrentMaterialState();this.modelsLayer.diffuseColor=e.diffuseColor,this.modelsLayer.setOpacity(e.opacity),this.modelsLayer.setVerticalGradient(e.verticalGradient),this.map.mapLibreMap.triggerRepaint()}};export{X as Landmarks3D,N as ModelsLayer,$ as ModelsSource,T as buildLandmarksTileURL};
1
+ import{TomTomConfig as e,isProxyCredentialsMode as i}from"@tomtom-org/maps-sdk/core";import{LayerFilterComposer as t,mapStyleLayerIDs as a}from"@tomtom-org/maps-sdk/map";import{AmbientLight as s,BufferAttribute as r,Camera as n,Color as o,CustomBlending as l,DirectionalLight as h,DoubleSide as m,FrontSide as d,Group as c,LoaderUtils as u,LoadingManager as p,Matrix4 as f,OneFactor as y,OneMinusSrcAlphaFactor as g,SRGBColorSpace as v,Scene as x,ShaderMaterial as M,Vector3 as L,WebGLRenderer as w}from"three";import{MeshoptDecoder as b}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{GLTFLoader as k}from"three/examples/jsm/loaders/GLTFLoader.js";import{KTX2Loader as C}from"three/examples/jsm/loaders/KTX2Loader.js";import{toCreasedNormals as S}from"three/examples/jsm/utils/BufferGeometryUtils.js";import{MercatorCoordinate as P}from"maplibre-gl";var T={diffuseColor:"hsl(38, 6%, 90%)",opacity:.7,verticalGradient:!0},D={diffuseColor:"hsl(210, 9%, 17%)",opacity:.7,verticalGradient:!0},z=()=>{const{commonBaseURL:i,apiKey:t}=e.instance.get();return`${i}/maps/orbis/display/3d/landmarks/tile/{z}/{x}/{y}?${t?`key=${t}&`:""}apiVersion=2`},B="fillExtrusionGradient",O=`\n attribute float ${B};\n varying vec3 vWorldNormal;\n varying float vGradient;\n #ifdef USE_ALPHA_MASK\n varying vec2 vUv;\n #endif\n\n void main() {\n vWorldNormal = normalize(mat3(modelMatrix) * normal);\n vGradient = ${B};\n #ifdef USE_ALPHA_MASK\n vUv = uv;\n #endif\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n`,E="\n #ifdef USE_ALPHA_MASK\n uniform sampler2D alphaMask;\n uniform float alphaMaskCutoff;\n varying vec2 vUv;\n #endif\n",I="\n #ifdef USE_ALPHA_MASK\n if (texture2D(alphaMask, vUv).a < alphaMaskCutoff) {\n discard;\n }\n #endif\n",A=`\n uniform vec3 diffuseColor;\n uniform vec3 lightPosition;\n uniform float lightIntensity;\n uniform vec3 lightColor;\n uniform float layerOpacity;\n uniform float verticalGradient;\n varying vec3 vWorldNormal;\n varying float vGradient;\n ${E}\n\n void main() {\n ${I}\n vec3 normal = normalize(vWorldNormal);\n // Double-sided (alpha-masked) surfaces show their interior through carved openings;\n // flip backface normals so those interiors shade like the facade instead of going dark.\n if (!gl_FrontFacing) {\n normal = -normal;\n }\n // Extrusions have only vertical walls and flat roofs, so a sloped landmark roof would shade\n // darker than every basemap roof around it: up-facing normals light like a flat roof, the\n // rest like a wall, blended for curved surfaces.\n vec3 flattened = vec3(normal.xy, 0.0);\n vec3 wallNormal = flattened / max(length(flattened), 1e-5);\n float upness = smoothstep(0.4, 0.8, normal.z);\n normal = normalize(mix(wallNormal, vec3(0.0, 0.0, 1.0), upness));\n float colorValue = dot(diffuseColor, vec3(0.2126, 0.7152, 0.0722));\n vec3 color = diffuseColor + vec3(0.03);\n float directional = clamp(dot(normal, lightPosition), 0.0, 1.0);\n directional = mix(1.0 - lightIntensity, max(1.0 - colorValue + lightIntensity, 1.0), directional);\n if (abs(normal.y) > 0.001) {\n directional *=\n (1.0 - verticalGradient) +\n verticalGradient * clamp(vGradient, mix(0.7, 0.98, 1.0 - lightIntensity), 1.0);\n }\n vec3 shaded = clamp(\n color * directional * lightColor,\n mix(vec3(0.0), vec3(0.3), vec3(1.0) - lightColor),\n vec3(1.0)\n );\n gl_FragColor = vec4(shaded, 1.0) * layerOpacity;\n }\n`,F=new L(.2875,-.498,.996),G=new o,_=new o,R=class extends M{constructor(e){super({uniforms:{diffuseColor:{value:new L(1,1,1)},lightPosition:{value:F.clone()},lightIntensity:{value:.5},lightColor:{value:new L(1,1,1)},layerOpacity:{value:1},verticalGradient:{value:1},...e&&{alphaMask:{value:e.map},alphaMaskCutoff:{value:e.cutoff}}},...e&&{defines:{USE_ALPHA_MASK:""}},vertexShader:O,fragmentShader:A,side:e?m:d,transparent:!0,blending:l,blendSrc:y,blendDst:g})}copyShadingFrom(e){this.uniforms.diffuseColor.value.copy(e.uniforms.diffuseColor.value),this.uniforms.lightPosition.value.copy(e.uniforms.lightPosition.value),this.uniforms.lightIntensity.value=e.uniforms.lightIntensity.value,this.uniforms.lightColor.value.copy(e.uniforms.lightColor.value),this.uniforms.layerOpacity.value=e.uniforms.layerOpacity.value,this.uniforms.verticalGradient.value=e.uniforms.verticalGradient.value}setDiffuseColor(e){G.setStyle(e),G.getRGB(_,v),this.uniforms.diffuseColor.value.set(_.r,_.g,_.b)}setLayerOpacity(e){this.uniforms.layerOpacity.value=e}setVerticalGradient(e){this.uniforms.verticalGradient.value=e?1:0}setLight(e,i,t,a){const s=Math.cos(i),r=Math.sin(i);this.uniforms.lightPosition.value.set(e.x*s-e.y*r,e.x*r+e.y*s,e.z),this.uniforms.lightIntensity.value=t,this.uniforms.lightColor.value.set(a.r,a.g,a.b)}},U=class extends M{constructor(e){super({...e&&{uniforms:{alphaMask:{value:e.map},alphaMaskCutoff:{value:e.cutoff}},defines:{USE_ALPHA_MASK:""}},vertexShader:O,fragmentShader:`\n ${E}\n void main() {\n ${I}\n gl_FragColor = vec4(0.0);\n }\n `,side:e?m:d,colorWrite:!1})}},j=e=>!0===e.isMesh,Z=1/(2*Math.PI*6378137),K=Math.PI/6,V=class{type="models";id;minzoom;maxzoom;map;tiles;scene;loadedTiles=/* @__PURE__ */new Set;sceneOrigin=null;onTileLoaded;ktx2Loader;transcoderReady;gltfLoader;specification;withCredentials;coveringTileKeys=/* @__PURE__ */new Set;constructor(e,i){this.id=e,this.minzoom=i.minzoom??0,this.maxzoom=i.maxzoom??22,this.tiles=i.tiles,this.specification=i,this.withCredentials=i.withCredentials??!1,this.scene=new c,this.ktx2Loader=new C(new p).setCrossOrigin("anonymous").setTranscoderPath(i.transcoderPath??""),this.gltfLoader=(new k).setKTX2Loader(this.ktx2Loader).setMeshoptDecoder(b)}onAdd(e,i){this.map=e,this.ktx2Loader.detectSupport(i)}updateTiles(){if(0===this.tiles.length)return;const e=this.coveringTiles();this.coveringTileKeys=new Set(e.map(e=>e.key));for(const i of e)this.loadTile(i);this.showCoveringTilesOnly()}showCoveringTilesOnly(){for(const e of this.scene.children)e.visible=this.coveringTileKeys.has(e.userData.key)}coveringTiles(){const e={tileSize:this.map._camera.transform.tileSize,minzoom:this.minzoom,maxzoom:this.maxzoom,terrain:this.map.terrain};return this.map.coveringTiles(e)}loadTile(e){if(this.loadedTiles.has(e.key))return;this.loadedTiles.add(e.key);const i=e.canonical.url(this.tiles,this.map.getPixelRatio(),this.specification.scheme);this.fetchTile(i).then(i=>{i&&(W(i),this.rebaseToSceneOrigin(i),i.userData={isTile:!0,key:e.key},i.visible=this.coveringTileKeys.has(e.key),this.scene.add(i),this.map.triggerRepaint(),this.onTileLoaded?.(i,e.key))}).catch(i=>{console.warn(`Problem with loading ${e.toString()}: ${i?.message??"unknown"}`)})}rebaseToSceneOrigin(e){const i=[];if(e.traverse(e=>{j(e)&&i.push(e)}),0===i.length)return;const t=i[0].position,a={x:Math.round(t.x),y:Math.round(t.y)};if(this.sceneOrigin){if(Math.hypot(t.x-this.sceneOrigin.x,t.y-this.sceneOrigin.y)>1e5){const e=a.x-this.sceneOrigin.x,i=a.y-this.sceneOrigin.y;this.scene.traverse(t=>{j(t)&&(t.position.x-=e,t.position.y-=i)}),this.sceneOrigin=a}}else this.sceneOrigin=a;for(const s of i)s.position.x-=this.sceneOrigin.x,s.position.y-=this.sceneOrigin.y}async fetchTile(e){const i=await fetch(e,this.withCredentials?{credentials:"include"}:void 0);if(204===i.status||404===i.status)return null;if(403===i.status&&"InvalidReferer"!==await(async e=>(await e.json().catch(()=>{}))?.detailedError?.code)(i))throw new Error("tile request failed with 403: Orbis 3D Landmarks is in Private Preview (https://docs.tomtom.com/legal/private-preview), and the API key needs access to it: request it from TomTom Sales at https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=map-display-api-3d");if(!i.ok)throw new Error(`tile request failed with ${i.status}`);const t=await i.arrayBuffer();this.transcoderReady??=(async(e,i)=>{if(void 0!==i)return;const{resolveBundledTranscoderUrl:t}=await(import("./bundledTranscoder-Dn27cuYm.js"));e.setURLModifier(t)})(this.ktx2Loader.manager,this.specification.transcoderPath),await this.transcoderReady;const{scene:a}=await this.gltfLoader.parseAsync(t,u.extractUrlBase(e));return a}},W=e=>{e.traverse(e=>{if(!j(e))return;const i=e.material;i.alphaTest=.1,i.side=m,e.userData.originalMaterial=i,e.geometry=S(e.geometry,K),$(e)})},$=e=>{const i=e.geometry,t=i.getAttribute("position");i.computeBoundingBox();const a=i.boundingBox?.max.z??0,s=a>0?Math.sqrt(a/150)/a:0,n=new Float32Array(t.count);for(let r=0;r<t.count;r++)n[r]=t.getZ(r)*s;i.setAttribute(B,new r(n,1))},H=e=>e*Math.PI/180,N=class{id;type="custom";renderingMode="3d";renderer;scene;camera;tiles;directionalLight;ambientLight;map;source;minzoom=0;maxzoom=22;visible=!0;diffuseColor="#ffffff";fillExtrusionMaterial=new R;depthPrepassMaterial=new U;maskedFillExtrusionMaterials=/* @__PURE__ */new Map;tilesMainMatrix=/* @__PURE__ */new Float64Array(16);tilesStale=!0;markTilesStale=()=>{this.tilesStale=!0};constructor(e,i){this.id=e.id,this.source=new V(this.id,i),this.tiles=this.source.scene,this.updateProperties(e)}updateProperties(e){this.visible="none"!==e.layout?.visibility,this.diffuseColor=e.paint?.["models-base-color"]??"#ffffff",this.minzoom=e.minzoom??0,this.maxzoom=e.maxzoom??22}setDirectionalLight(e,i,t){this.directionalLight.position.setFromSphericalCoords(e,H(i),H(t))}onAdd(e,i){this.map=e,this.scene=new x,this.tiles=this.source.scene,this.camera=new n,this.directionalLight=new h(16777215,.5),this.setDirectionalLight(1.15,90,0),this.scene.add(this.directionalLight),this.ambientLight=new s(16777215,.1),this.scene.add(this.ambientLight),this.scene.add(this.tiles),this.renderer=new w({canvas:e.getCanvas(),context:i,antialias:!0}),this.renderer.autoClear=!1,this.source.onAdd(e,this.renderer),this.tilesStale=!0,e.off("data",this.markTilesStale),e.on("data",this.markTilesStale)}render(e,i){const t=this.map._camera.transform.tileZoom,a=t>=this.minzoom&&t<=this.maxzoom;this.tiles.visible=this.visible&&a,this.visible&&a&&(this.syncLighting(),this.updateTilesWhenStale(i.defaultProjectionData.mainMatrix),this.groundLandmarksOnTerrain(),this.alignCameraToMap(i),this.renderer.resetState(),this.needsTranslucentPrepass()&&this.renderDepthPrepass(),this.applyMaterials(),this.renderer.render(this.scene,this.camera))}updateTilesWhenStale(e){(this.tilesMainMatrix.some((i,t)=>i!==e[t])||this.tilesStale)&&(this.tilesMainMatrix.set(e),this.tilesStale=!1,this.source.updateTiles())}groundLandmarksOnTerrain(){for(const e of this.tiles.children)e.visible&&e.traverse(e=>{j(e)&&this.groundMeshOnTerrain(e)})}groundMeshOnTerrain(e){const i=this.terrainSamplePoints(e);if(!i)return;let t=null;for(const a of i){const i=this.map.queryTerrainElevation(a);if(null===i)return void(e.position.z=0);(null===t||i<t)&&(t=i)}null!==t&&(e.position.z=t-.5)}terrainSamplePoints(e){let i=e.userData.terrainSamplePoints;if(!i){e.geometry.boundingBox||e.geometry.computeBoundingBox();const t=e.geometry.boundingBox;if(!t||t.isEmpty())return null;const a=e.position.x+this.sceneOrigin.x,s=e.position.y+this.sceneOrigin.y,r=(t.min.x+t.max.x)/2,n=(t.min.y+t.max.y)/2;i=[[r,n],[t.min.x,t.min.y],[t.min.x,t.max.y],[t.max.x,t.min.y],[t.max.x,t.max.y],[r,t.min.y],[r,t.max.y],[t.min.x,n],[t.max.x,n]].map(([e,i])=>new P(.5+(a+e)*Z,.5-(s+i)*Z).toLngLat().toArray()),e.userData.terrainSamplePoints=i}return i}alignCameraToMap(e){const i=Z/Math.cos(H(this.map.getCenter().lat)),t=.5+Z*this.sceneOrigin.x,a=.5-Z*this.sceneOrigin.y,s=new f;s.set(Z,0,0,t,0,-Z,0,a,0,0,i,0,0,0,0,1);const r=(new f).fromArray(e.defaultProjectionData.mainMatrix);this.camera.projectionMatrix.copy(r).multiply(s)}get sceneOrigin(){return this.source.sceneOrigin??{x:0,y:0}}needsTranslucentPrepass(){return this.fillExtrusionMaterial.uniforms.layerOpacity.value<1}renderDepthPrepass(){this.tiles.traverse(e=>{j(e)&&(e.material=this.maskedFillExtrusionVariant(e)?.depth??this.depthPrepassMaterial)}),this.renderer.render(this.scene,this.camera)}setOpacity(e){this.forEachFillExtrusionMaterial(i=>i.setLayerOpacity(e))}setVerticalGradient(e){this.forEachFillExtrusionMaterial(i=>i.setVerticalGradient(e))}getMaterial(e){return this.maskedFillExtrusionVariant(e)?.color??this.fillExtrusionMaterial}maskedFillExtrusionVariant(e){const i=e.userData.originalMaterial,t=i?.map;if(!t)return null;let a=this.maskedFillExtrusionMaterials.get(t);if(!a){const e={map:t,cutoff:i.alphaTest||.1};a={color:new R(e),depth:new U(e)},a.color.copyShadingFrom(this.fillExtrusionMaterial),this.maskedFillExtrusionMaterials.set(t,a)}return a}forEachFillExtrusionMaterial(e){e(this.fillExtrusionMaterial),this.maskedFillExtrusionMaterials.forEach(i=>e(i.color))}syncLighting(){const e=this.map,i=e.style?.light,t=i?.properties,a=i?.getCartesianPosition?.();if(t&&a)try{const i=t.get("intensity");this.directionalLight.intensity=i*Math.PI,this.ambientLight.intensity=(1-i)*Math.PI;const s={x:-a[0],y:a[1],z:a[2]};this.directionalLight.position.set(s.x,s.y,s.z);const r="viewport"===t.get("anchor")?-(e._camera.transform.bearingInRadians??0):0,n=t.get("color");this.forEachFillExtrusionMaterial(e=>e.setLight(s,r,i,n))}catch{}}applyMaterials(){this.forEachFillExtrusionMaterial(e=>e.setDiffuseColor(this.diffuseColor)),this.tiles.traverse(e=>{j(e)&&(e.material=this.getMaterial(e))})}},q=["!",["coalesce",["get","has_landmark"],!1]],X=class{map;layerID="orbis-3d-landmarks";basemapBuildingLayerID="3D - Building";modelsLayer;layerMinZoom;layerMaxZoom;displayMode;constructor(e,t={}){this.map=e,this.layerMinZoom=t.minZoom,this.layerMaxZoom=t.maxZoom,this.displayMode=t.displayMode??"inherited";const a={id:this.layerID,type:"models",source:this.layerID,minzoom:t.minZoom??10,maxzoom:t.maxZoom??22},s={type:"models",tiles:[z()],minzoom:15,maxzoom:15,withCredentials:t.withCredentials??i()};this.modelsLayer=new N(a,s),this.modelsLayer.visible=t.visible??!0,this.map.addStyleChangeHandler({onStyleChanged:()=>this.install()}),this.installWhenReady()}installWhenReady(){this.map.mapReady?this.install():this.map.mapLibreMap.once("styledata",()=>this.installWhenReady())}get layer(){return this.modelsLayer}getDisplayMode(){return this.displayMode}async setDisplayMode(e){this.displayMode=e,this.map.mapReady&&this.applyDisplayMode()}isVisible(){return this.modelsLayer.visible}async setVisible(e){this.modelsLayer.visible=e,this.map.mapReady&&this.map.mapLibreMap.triggerRepaint()}install(){const e=this.map.mapLibreMap;e.getLayer(this.layerID)||e.addLayer(this.modelsLayer,this.landmarkInsertBeforeID()),e.getLayer(this.basemapBuildingLayerID)&&e.setLayoutProperty(this.basemapBuildingLayerID,"visibility","visible"),this.alignZoomRangeWithBasemapBuildings(),this.excludeLandmarkBuildingsFromBasemap(),this.modelsLayer.source.onTileLoaded=()=>{this.resolveCurrentMaterialState().opacity<1&&this.applyDisplayMode()},this.applyDisplayMode()}landmarkInsertBeforeID(){const e=a.poi;return this.map.mapLibreMap.getLayer(e)?e:void 0}alignZoomRangeWithBasemapBuildings(){const e=this.map.mapLibreMap.getLayer(this.basemapBuildingLayerID);e&&(this.modelsLayer.minzoom=this.layerMinZoom??e.minzoom??10,this.modelsLayer.maxzoom=this.layerMaxZoom??e.maxzoom??22)}excludeLandmarkBuildingsFromBasemap(){t.for(this.map).setClause(this.basemapBuildingLayerID,"landmarks3d.buildings",q)}readBasemapBuildingMaterial(){const e=this.map.mapLibreMap;if(!e.getLayer(this.basemapBuildingLayerID))return null;const i=(e=>{if("string"==typeof e)return e;if(Array.isArray(e)&&"match"===e[0]){const i=e.at(-1);if("string"==typeof i)return i}return null})(e.getPaintProperty(this.basemapBuildingLayerID,"fill-extrusion-color"));if(!i)return null;const t=e.getPaintProperty(this.basemapBuildingLayerID,"fill-extrusion-opacity"),a=e.getPaintProperty(this.basemapBuildingLayerID,"fill-extrusion-vertical-gradient");return{diffuseColor:i,..."number"==typeof t&&{opacity:t},..."boolean"==typeof a&&{verticalGradient:a}}}resolveCurrentMaterialState(){const e="inherited"===this.displayMode?this.readBasemapBuildingMaterial():null;return((e,i)=>{switch(e){case"dark":return D;case"light":return T;default:return{...T,...i}}})(this.displayMode,e)}applyDisplayMode(){const e=this.resolveCurrentMaterialState();this.modelsLayer.diffuseColor=e.diffuseColor,this.modelsLayer.setOpacity(e.opacity),this.modelsLayer.setVerticalGradient(e.verticalGradient),this.map.mapLibreMap.triggerRepaint()}};export{X as Landmarks3D,N as ModelsLayer,V as ModelsSource,z as buildLandmarksTileURL};
@@ -1,7 +1,7 @@
1
1
  [
2
2
  {
3
3
  "name": "maplibre-gl",
4
- "version": "6.12.0",
4
+ "version": "6.13.0",
5
5
  "license": "BSD-3-Clause",
6
6
  "bundled": false,
7
7
  "description": "BSD licensed community fork of mapbox-gl, a WebGL interactive maps library",
package/package.json CHANGED
@@ -2,25 +2,27 @@
2
2
  "name": "@tomtom-org/maps-sdk-plugin-landmarks-3d",
3
3
  "author": "TomTom International B.V.",
4
4
  "license": "See in LICENSE.txt",
5
- "version": "0.0.12",
5
+ "version": "0.0.13",
6
6
  "type": "module",
7
7
  "description": "Plugin to render TomTom Orbis 3D Landmarks (GLB tiles) on a TomTomMap with Three.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "peerDependencies": {
10
10
  "@tomtom-org/maps-sdk": ">=0.64.0 <1.0.0 || >=1.0.0-rc.0 <1.0.0",
11
- "maplibre-gl": "^6.12.0",
11
+ "maplibre-gl": "^6.13.0",
12
12
  "three": "^0.170.0"
13
13
  },
14
14
  "devDependencies": {
15
+ "@playwright/test": "1.60.0",
15
16
  "@tsconfig/recommended": "^1.0.13",
16
17
  "@types/node": "^26.6.4",
17
18
  "@types/three": "^0.170.0",
18
- "@vitest/coverage-v8": "^4.1.11",
19
- "rolldown": "^1.2.12",
19
+ "@vitest/coverage-v8": "^5.0.3",
20
+ "rolldown": "^1.2.13",
20
21
  "typescript": "^6.0.3",
21
- "vite": "^8.3.2",
22
- "vitest": "^4.1.11",
23
- "@tomtom-org/maps-sdk": "1.0.0-rc.1"
22
+ "vite": "8.3.3",
23
+ "vitest": "^5.0.3",
24
+ "@testing/core-utils": "0.0.0",
25
+ "@tomtom-org/maps-sdk": "1.0.0-rc.2"
24
26
  },
25
27
  "files": [
26
28
  "dist",
@@ -41,6 +43,11 @@
41
43
  "build:watch": "vite build --watch --config ../plugin-vite-config.ts",
42
44
  "test": "vitest run",
43
45
  "test:coverage": "vitest run --coverage",
46
+ "test:e2e": "playwright test --config e2e-tests/playwright.config.ts",
47
+ "test:e2e:ui": "playwright test --config e2e-tests/playwright.config.ts --ui",
48
+ "test:e2e:update-snapshots": "playwright test --config e2e-tests/playwright.config.ts --update-snapshots",
49
+ "test:e2e:server": "vite --config e2e-tests/vite.config.ts",
50
+ "test:e2e:install": "playwright install chromium",
44
51
  "type-check": "tsc --noEmit"
45
52
  }
46
53
  }