@treasuryspatial/map-react 0.1.7 → 0.1.9

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.
@@ -14,7 +14,7 @@ export type MapModeViewportHandle = {
14
14
  animateToView: (_viewKey: string) => void;
15
15
  capturePngDataUrl: () => string;
16
16
  };
17
- export type ApplyLightingPreset = (scene: THREE.Scene, presetKey: string) => void;
17
+ export type ApplyLightingPreset = (scene: THREE.Scene, renderer: THREE.WebGLRenderer, presetKey: string) => void;
18
18
  export type ResolveMeshGroup = (geometry3dm: string) => Promise<THREE.Group | null>;
19
19
  export type MapModeViewportProps = {
20
20
  active: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"MapModeViewport.d.ts","sourceRoot":"","sources":["../src/MapModeViewport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAgF,MAAM,OAAO,CAAC;AAErG,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EASL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC3B,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;IACxB,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,iBAAiB,EAAE,MAAM,MAAM,CAAC;CACjC,CAAC;AAIF,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;AAClF,MAAM,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;AAEpF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,aAAa,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;IACpC,iBAAiB,EAAE,CAAC,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAMF,eAAO,MAAM,eAAe,oGA8W1B,CAAC"}
1
+ {"version":3,"file":"MapModeViewport.d.ts","sourceRoot":"","sources":["../src/MapModeViewport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA6F,MAAM,OAAO,CAAC;AAElH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EASL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC3B,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;IACxB,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,iBAAiB,EAAE,MAAM,MAAM,CAAC;CACjC,CAAC;AAIF,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;AACjH,MAAM,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;AAEpF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,aAAa,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;IACpC,iBAAiB,EAAE,CAAC,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAMF,eAAO,MAAM,eAAe,oGAma1B,CAAC"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
4
4
  import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
5
5
  import * as THREE from 'three';
6
6
  import { buildGeoJsonPolygon, computeCentroid, computeFootprintStats, createMapThreeLayer, ensureFootprintLayers, getOuterRing, toLocalMeters, toPolygonFeature, } from '@treasuryspatial/map-kit';
@@ -25,6 +25,17 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
25
25
  const reverseGeocodeRequestRef = useRef(0);
26
26
  const [mapReady, setMapReady] = useState(false);
27
27
  const [mapError, setMapError] = useState('');
28
+ const debug = useMemo(() => {
29
+ if (typeof window === 'undefined')
30
+ return false;
31
+ const params = new URLSearchParams(window.location.search);
32
+ return params.has('mapDebug') || process.env.NODE_ENV !== 'production';
33
+ }, []);
34
+ const log = useCallback((...args) => {
35
+ if (!debug)
36
+ return;
37
+ console.info('[map-react]', ...args);
38
+ }, [debug]);
28
39
  useEffect(() => {
29
40
  lightingPresetRef.current = lightingPreset;
30
41
  }, [lightingPreset]);
@@ -44,8 +55,8 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
44
55
  useImperativeHandle(ref, () => ({
45
56
  setLightingPreset: (presetKey) => {
46
57
  lightingPresetRef.current = presetKey;
47
- if (sceneRef.current) {
48
- (applyLightingPreset ?? defaultApplyLightingPreset)(sceneRef.current, presetKey);
58
+ if (sceneRef.current && rendererRef.current) {
59
+ (applyLightingPreset ?? defaultApplyLightingPreset)(sceneRef.current, rendererRef.current, presetKey);
49
60
  }
50
61
  },
51
62
  animateToView: () => { },
@@ -71,6 +82,8 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
71
82
  return;
72
83
  }
73
84
  let cancelled = false;
85
+ let cleanupCanvasEvents = null;
86
+ let cleanupMapEvents = null;
74
87
  const boot = async () => {
75
88
  try {
76
89
  const module = await import('mapbox-gl');
@@ -95,6 +108,13 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
95
108
  preserveDrawingBuffer: true,
96
109
  attributionControl: false,
97
110
  });
111
+ log('map init', {
112
+ style: mapStyle,
113
+ center: [startCenter.lng, startCenter.lat],
114
+ zoom: startZoom,
115
+ bearing: startBearing,
116
+ pitch: startPitch,
117
+ });
98
118
  map.addControl(new mapboxgl.NavigationControl({ visualizePitch: true }), 'bottom-right');
99
119
  const geocoder = new MapboxGeocoder({
100
120
  accessToken,
@@ -106,6 +126,30 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
106
126
  });
107
127
  map.addControl(geocoder, 'top-left');
108
128
  mapRef.current = map;
129
+ const onMapError = (event) => {
130
+ const err = event?.error ?? event;
131
+ console.error('[map-react] Mapbox error', err);
132
+ };
133
+ map.on('error', onMapError);
134
+ cleanupMapEvents = () => {
135
+ map.off('error', onMapError);
136
+ };
137
+ const canvas = map.getCanvas();
138
+ const onContextLost = (event) => {
139
+ event.preventDefault();
140
+ console.error('[map-react] WebGL context lost');
141
+ setMapError('WebGL context lost.');
142
+ };
143
+ const onContextRestored = () => {
144
+ console.info('[map-react] WebGL context restored');
145
+ setMapError('');
146
+ };
147
+ canvas.addEventListener('webglcontextlost', onContextLost, { passive: false });
148
+ canvas.addEventListener('webglcontextrestored', onContextRestored);
149
+ cleanupCanvasEvents = () => {
150
+ canvas.removeEventListener('webglcontextlost', onContextLost);
151
+ canvas.removeEventListener('webglcontextrestored', onContextRestored);
152
+ };
109
153
  map.on('style.load', () => {
110
154
  if (!map.isStyleLoaded())
111
155
  return;
@@ -153,6 +197,8 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
153
197
  threeLayerRef.current = null;
154
198
  setMapReady(false);
155
199
  handlersBoundRef.current = false;
200
+ cleanupCanvasEvents?.();
201
+ cleanupMapEvents?.();
156
202
  };
157
203
  }, [active, accessToken, mapStyle, mapVisibility]);
158
204
  useEffect(() => {
@@ -223,12 +269,13 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
223
269
  threeLayerRef.current = createMapThreeLayer({
224
270
  id: 'courtyard-mesh',
225
271
  zOffsetMeters: 0.05,
272
+ debug,
226
273
  onInit: (scene, renderer) => {
227
274
  sceneRef.current = scene;
228
275
  rendererRef.current = renderer;
229
- renderer.toneMapping = THREE.ACESFilmicToneMapping;
230
- renderer.toneMappingExposure = 1.2;
231
- (applyLightingPreset ?? defaultApplyLightingPreset)(scene, lightingPresetRef.current);
276
+ renderer.shadowMap.enabled = true;
277
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
278
+ (applyLightingPreset ?? defaultApplyLightingPreset)(scene, renderer, lightingPresetRef.current);
232
279
  },
233
280
  });
234
281
  }
@@ -313,6 +360,31 @@ export const MapModeViewport = forwardRef(function MapModeViewport({ active, geo
313
360
  };
314
361
  return (_jsxs("div", { className: "absolute inset-0", children: [_jsx("div", { ref: containerRef, className: "w-full h-full" }), !accessToken && (_jsx("div", { className: "absolute inset-0 z-[2] flex items-center justify-center bg-black/70 text-xs text-white font-nunito", children: "Mapbox access token missing." })), mapError && (_jsx("div", { className: "absolute inset-0 z-[2] flex items-center justify-center bg-black/70 text-xs text-white font-nunito", children: mapError })), !mapReady && !mapError && accessToken && (_jsx("div", { className: "absolute inset-0 z-[2] flex items-center justify-center bg-black/60 text-xs text-white font-nunito", children: "loading map\u2026" }))] }));
315
362
  });
363
+ const ensureGeometryUvs = (geometry) => {
364
+ if (geometry.getAttribute('uv'))
365
+ return;
366
+ const pos = geometry.getAttribute('position');
367
+ if (!pos)
368
+ return;
369
+ geometry.computeBoundingBox();
370
+ const bbox = geometry.boundingBox;
371
+ if (!bbox)
372
+ return;
373
+ const size = new THREE.Vector3();
374
+ bbox.getSize(size);
375
+ const uvs = new Float32Array(pos.count * 2);
376
+ const denomX = size.x || 1;
377
+ const denomZ = size.z || 1;
378
+ for (let i = 0; i < pos.count; i += 1) {
379
+ const x = pos.getX(i);
380
+ const z = pos.getZ(i);
381
+ const u = (x - bbox.min.x) / denomX;
382
+ const v = (z - bbox.min.z) / denomZ;
383
+ uvs[i * 2] = u;
384
+ uvs[i * 2 + 1] = v;
385
+ }
386
+ geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
387
+ };
316
388
  function applyMaterialOverrides(group, settings) {
317
389
  const color = new THREE.Color(settings?.color ?? '#ffffff');
318
390
  const textureUrl = settings?.textureUrl;
@@ -337,9 +409,12 @@ function applyMaterialOverrides(group, settings) {
337
409
  else
338
410
  material?.dispose?.();
339
411
  }
412
+ if (texture) {
413
+ ensureGeometryUvs(mesh.geometry);
414
+ }
340
415
  mesh.material = new THREE.MeshStandardMaterial({
341
416
  color,
342
- roughness: 0.6,
417
+ roughness: 0.75,
343
418
  metalness: 0,
344
419
  side: THREE.DoubleSide,
345
420
  map: texture ?? null,
@@ -347,10 +422,12 @@ function applyMaterialOverrides(group, settings) {
347
422
  mesh.material.polygonOffset = true;
348
423
  mesh.material.polygonOffsetFactor = 1.0;
349
424
  mesh.material.polygonOffsetUnits = 1.0;
425
+ mesh.castShadow = false;
426
+ mesh.receiveShadow = false;
350
427
  });
351
428
  }
352
- function defaultApplyLightingPreset(scene, _presetKey) {
353
- scene.background = null;
429
+ function defaultApplyLightingPreset(scene, renderer, _presetKey) {
430
+ scene.background = new THREE.Color('#ffffff');
354
431
  const lightsToRemove = scene.children.filter((child) => child instanceof THREE.Light);
355
432
  lightsToRemove.forEach((light) => scene.remove(light));
356
433
  scene.add(new THREE.AmbientLight('#ffffff', 0.6));
@@ -361,6 +438,8 @@ function defaultApplyLightingPreset(scene, _presetKey) {
361
438
  const fill = new THREE.DirectionalLight('#ffffff', 0.4);
362
439
  fill.position.set(-6, 8, -6);
363
440
  scene.add(fill);
441
+ renderer.toneMapping = THREE.NeutralToneMapping;
442
+ renderer.toneMappingExposure = 1.05;
364
443
  }
365
444
  function applyMapVisibility(map, mode, cacheRef) {
366
445
  if (!map?.getStyle?.())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treasuryspatial/map-react",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "access": "public"
19
19
  },
20
20
  "dependencies": {
21
- "@treasuryspatial/map-kit": "^0.1.1"
21
+ "@treasuryspatial/map-kit": "^0.1.4"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "react": ">=18",