@mapless/core 0.1.0 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/index.js +904 -141
  2. package/dist/types/Mapless/icons/icons.d.ts +1 -0
  3. package/dist/types/Mapless/icons/icons.d.ts.map +1 -1
  4. package/dist/types/Mapless/mana/movable.d.ts +19 -3
  5. package/dist/types/Mapless/mana/movable.d.ts.map +1 -1
  6. package/dist/types/Mapless/mana/newstation.d.ts.map +1 -1
  7. package/dist/types/Mapless/mana-3d/airspace.d.ts +41 -12
  8. package/dist/types/Mapless/mana-3d/airspace.d.ts.map +1 -1
  9. package/dist/types/Mapless/mana-3d/index.d.ts +4 -3
  10. package/dist/types/Mapless/mana-3d/index.d.ts.map +1 -1
  11. package/dist/types/Mapless/mana-3d/mapModel.d.ts +103 -3
  12. package/dist/types/Mapless/mana-3d/mapModel.d.ts.map +1 -1
  13. package/dist/types/Mapless/mana-three/index.d.ts +2 -1
  14. package/dist/types/Mapless/mana-three/index.d.ts.map +1 -1
  15. package/dist/types/Mapless/mana-three/instancedModel.d.ts +127 -0
  16. package/dist/types/Mapless/mana-three/instancedModel.d.ts.map +1 -0
  17. package/dist/types/Mapless/mana-three/line2Manager.d.ts +1 -1
  18. package/dist/types/Mapless/mana-three/line2Manager.d.ts.map +1 -1
  19. package/dist/types/Mapless/mana-three/three.d.ts +99 -1
  20. package/dist/types/Mapless/mana-three/three.d.ts.map +1 -1
  21. package/dist/types/Mapless/path/PathTracker.d.ts.map +1 -1
  22. package/dist/types/Mapless/path/attach.d.ts +1 -1
  23. package/dist/types/Mapless/path/attach.d.ts.map +1 -1
  24. package/dist/types/Mapless/path/strategies/click.d.ts.map +1 -1
  25. package/dist/types/Mapless/path/strategies/free.d.ts.map +1 -1
  26. package/dist/types/Mapless/types.d.ts +20 -0
  27. package/dist/types/Mapless/types.d.ts.map +1 -1
  28. package/dist/types/Three/MapThree.d.ts +39 -0
  29. package/dist/types/Three/MapThree.d.ts.map +1 -1
  30. package/dist/types/Three/ModelCache.d.ts +68 -0
  31. package/dist/types/Three/ModelCache.d.ts.map +1 -0
  32. package/dist/types/Three/types.d.ts +7 -12
  33. package/dist/types/Three/types.d.ts.map +1 -1
  34. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { area as turf_area, center as turf_center, centroid, circle, distance as turf_distance, ellipse, length as turf_length, pointsWithinPolygon, polygon as turf_polygon } from "@turf/turf";
2
2
  import mapbox_gl, { LngLat, MapMouseEvent, Marker, MercatorCoordinate, Popup } from "mapbox-gl";
3
- import { AmbientLight, AnimationMixer, BoxGeometry, BufferGeometry, CatmullRomCurve3, Clock, Color, ConeGeometry, CurvePath, CylinderGeometry, DirectionalLight, DoubleSide, Group, Line, LineBasicMaterial, LineCurve3, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, PerspectiveCamera, PointLight, Scene, SphereGeometry, SpotLight, TorusGeometry, TubeGeometry, Vector3, WebGLRenderer } from "three";
3
+ import { AmbientLight, AnimationMixer, BoxGeometry, BoxHelper, BufferGeometry, CatmullRomCurve3, Clock, Color, ConeGeometry, CurvePath, CylinderGeometry, DirectionalLight, DoubleSide, Group, InstancedMesh, Line, LineBasicMaterial, LineCurve3, LoopRepeat, Matrix4, Mesh, MeshBasicMaterial, Object3D, PerspectiveCamera, PointLight, Raycaster, Scene, SphereGeometry, SpotLight, TorusGeometry, TubeGeometry, Vector3, Vector4, WebGLRenderer } from "three";
4
4
  import gcoord from "gcoord";
5
5
  import mapbox_gl_draw from "@mapbox/mapbox-gl-draw";
6
6
  import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css";
7
- import { GLTFLoader, Line2, LineGeometry, LineMaterial } from "three-stdlib";
7
+ import { DRACOLoader, GLTFLoader, Line2, LineGeometry, LineMaterial } from "three-stdlib";
8
8
  class MapThree {
9
9
  mapless = void 0;
10
10
  scene = null;
@@ -16,6 +16,7 @@ class MapThree {
16
16
  renderableObjects = new Map();
17
17
  isInitialized = false;
18
18
  customLayerId = null;
19
+ lastProjectionMatrix = null;
19
20
  constructor(props){
20
21
  const { mapless } = props;
21
22
  this.mapless = mapless;
@@ -54,6 +55,7 @@ class MapThree {
54
55
  renderableObj.mesh.visible = false;
55
56
  });
56
57
  this.camera.projectionMatrix = m;
58
+ this.lastProjectionMatrix = m.clone();
57
59
  this.renderer.resetState();
58
60
  this.renderer.render(this.scene, this.camera);
59
61
  this.renderableObjects.forEach((renderableObj)=>{
@@ -317,6 +319,140 @@ class MapThree {
317
319
  this.removeRenderableObject(id);
318
320
  });
319
321
  }
322
+ raycast(point, objects, tolerance = 30) {
323
+ if (!this.camera || !this.scene || !this.renderer) return null;
324
+ const map = this.mapless?.map;
325
+ if (!map) return null;
326
+ const mapContainer = map.getCanvas();
327
+ const ndcX = point.x / mapContainer.width * 2 - 1;
328
+ const ndcY = 2 * -(point.y / mapContainer.height) + 1;
329
+ const targetObjects = objects || Array.from(this.renderableObjects.values()).map((obj)=>obj.mesh);
330
+ let closestHit = null;
331
+ let minDistance = 1 / 0;
332
+ const screenPositions = new Map();
333
+ for (const targetObj of targetObjects){
334
+ const renderableObj = Array.from(this.renderableObjects.values()).find((obj)=>obj.mesh === targetObj);
335
+ if (renderableObj && this.lastProjectionMatrix) {
336
+ let l;
337
+ if (renderableObj.transform.matrix) l = renderableObj.transform.matrix.clone();
338
+ else {
339
+ l = new Matrix4().makeTranslation(renderableObj.transform.translateX, renderableObj.transform.translateY, renderableObj.transform.translateZ).scale(new Vector3(renderableObj.transform.scale, -renderableObj.transform.scale, renderableObj.transform.scale));
340
+ if (void 0 !== renderableObj.transform.rotateX || void 0 !== renderableObj.transform.rotateY || void 0 !== renderableObj.transform.rotateZ) {
341
+ const rotationX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), renderableObj.transform.rotateX || 0);
342
+ const rotationY = new Matrix4().makeRotationAxis(new Vector3(0, 1, 0), renderableObj.transform.rotateY || 0);
343
+ const rotationZ = new Matrix4().makeRotationAxis(new Vector3(0, 0, 1), renderableObj.transform.rotateZ || 0);
344
+ l.multiply(rotationX).multiply(rotationY).multiply(rotationZ);
345
+ }
346
+ }
347
+ const mvpMatrix = this.lastProjectionMatrix.clone().multiply(l);
348
+ const inverseMVP = mvpMatrix.invert();
349
+ const nearPoint = new Vector4(ndcX, ndcY, -1, 1).applyMatrix4(inverseMVP);
350
+ nearPoint.divideScalar(nearPoint.w);
351
+ const farPoint = new Vector4(ndcX, ndcY, 1, 1).applyMatrix4(inverseMVP);
352
+ farPoint.divideScalar(farPoint.w);
353
+ const origin = new Vector3(nearPoint.x, nearPoint.y, nearPoint.z);
354
+ const direction = new Vector3(farPoint.x - nearPoint.x, farPoint.y - nearPoint.y, farPoint.z - nearPoint.z).normalize();
355
+ const raycaster = new Raycaster(origin, direction);
356
+ const intersects = raycaster.intersectObject(targetObj, true);
357
+ const centerPoint = new Vector4(0, 0, 0, 1).applyMatrix4(mvpMatrix);
358
+ const screenX = (centerPoint.x / centerPoint.w + 1) * 0.5 * mapContainer.width;
359
+ const screenY = (-centerPoint.y / centerPoint.w + 1) * 0.5 * mapContainer.height;
360
+ const depth = centerPoint.z / centerPoint.w;
361
+ const scale = renderableObj.transform.scale;
362
+ const screenRadius = 50 * scale / Math.abs(depth);
363
+ screenPositions.set(targetObj, {
364
+ x: screenX,
365
+ y: screenY,
366
+ radius: screenRadius
367
+ });
368
+ if (intersects.length > 0 && intersects[0].distance < minDistance) {
369
+ minDistance = intersects[0].distance;
370
+ closestHit = {
371
+ object: intersects[0].object,
372
+ distance: intersects[0].distance,
373
+ point: intersects[0].point
374
+ };
375
+ }
376
+ }
377
+ }
378
+ if (!closestHit) for (const [obj, screenPos] of screenPositions){
379
+ const dx = screenPos.x - point.x;
380
+ const dy = screenPos.y - point.y;
381
+ const distance = Math.sqrt(dx * dx + dy * dy);
382
+ if (distance < Math.max(screenPos.radius, tolerance)) return {
383
+ object: obj,
384
+ distance: 0,
385
+ point: new Vector3()
386
+ };
387
+ }
388
+ return closestHit;
389
+ }
390
+ registerRenderableObjectClick(id, onClick, tolerance = 30) {
391
+ const renderableObj = this.renderableObjects.get(id);
392
+ if (!renderableObj) {
393
+ console.warn(`RenderableObject with id "${id}" not found`);
394
+ return null;
395
+ }
396
+ const map = this.mapless?.map;
397
+ if (!map) {
398
+ console.warn('Map not initialized');
399
+ return null;
400
+ }
401
+ const handleClick = (e)=>{
402
+ map.triggerRepaint();
403
+ requestAnimationFrame(()=>{
404
+ const point = {
405
+ x: e.point.x,
406
+ y: e.point.y
407
+ };
408
+ const hit = this.raycast(point, [
409
+ renderableObj.mesh
410
+ ], tolerance);
411
+ if (hit?.object) onClick(hit.object, hit.point);
412
+ });
413
+ };
414
+ map.on('click', handleClick);
415
+ return ()=>{
416
+ map.off('click', handleClick);
417
+ };
418
+ }
419
+ registerGlobalClick(onClick) {
420
+ const map = this.mapless?.map;
421
+ if (!map) {
422
+ console.warn('Map not initialized');
423
+ return null;
424
+ }
425
+ const handleClick = (e)=>{
426
+ const point = {
427
+ x: e.point.x,
428
+ y: e.point.y
429
+ };
430
+ const hit = this.raycast(point);
431
+ if (hit) {
432
+ let renderableId;
433
+ for (const [id, obj] of this.renderableObjects)if (this.isObjectInHierarchy(hit.object, obj.mesh)) {
434
+ renderableId = id;
435
+ break;
436
+ }
437
+ onClick({
438
+ ...hit,
439
+ renderableId
440
+ });
441
+ }
442
+ };
443
+ map.on('click', handleClick);
444
+ return ()=>{
445
+ map.off('click', handleClick);
446
+ };
447
+ }
448
+ isObjectInHierarchy(obj, root) {
449
+ let current = obj;
450
+ while(current){
451
+ if (current === root) return true;
452
+ current = current.parent;
453
+ }
454
+ return false;
455
+ }
320
456
  dispose() {
321
457
  this.clear();
322
458
  this.clearRenderableObjects();
@@ -832,6 +968,7 @@ const mona_loading_default_namespaceObject = "data:image/gif;base64,R0lGODlhgAGA
832
968
  const center_namespaceObject = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjIxMTZBRkZCNkJFRDExRUY4RTc4ODI1OTI5RDg2NEIzIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjIxMTZBRkZDNkJFRDExRUY4RTc4ODI1OTI5RDg2NEIzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MjExNkFGRjk2QkVEMTFFRjhFNzg4MjU5MjlEODY0QjMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MjExNkFGRkE2QkVEMTFFRjhFNzg4MjU5MjlEODY0QjMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7kzyMtAAAGuElEQVR42uxd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";
833
969
  const fly_namespaceObject = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjFGODkyQTNFNkJFRDExRUZCQzQzOTRDMjJCMTlEMDU3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjFGODkyQTNGNkJFRDExRUZCQzQzOTRDMjJCMTlEMDU3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUY4OTJBM0M2QkVEMTFFRkJDNDM5NEMyMkIxOUQwNTciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MUY4OTJBM0Q2QkVEMTFFRkJDNDM5NEMyMkIxOUQwNTciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6Kn+fFAAAJmElEQVR42uxd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";
834
970
  const car_namespaceObject = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjFFQjNCNEQ4NkJFRDExRUY5RDFCQzVGRTFERTM5QURDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjFFQjNCNEQ5NkJFRDExRUY5RDFCQzVGRTFERTM5QURDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUVCM0I0RDY2QkVEMTFFRjlEMUJDNUZFMURFMzlBREMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MUVCM0I0RDc2QkVEMTFFRjlEMUJDNUZFMURFMzlBREMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4N8EJcAAAKr0lEQVR42uxd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";
971
+ const controller_namespaceObject = "data:image/png;base64,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";
835
972
  const flight_namespaceObject = "data:image/png;base64,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";
836
973
  const ship_namespaceObject = "data:image/png;base64,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";
837
974
  const circle_namespaceObject = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAG6ElEQVR4nO2d228VVRSHv04hpSBRrFwE1Eq5S0gEg4SgUoOVEDG+oP+A+g/4ou9GjLWY+OSbPBofxCYomvhoSCkXUzS03JoaraDYVFMCNsA5Pqzd9pz2XHtmZs3MXl/yezk9Pf3NWnP2TPfstXYT2WQZsBHYBGwG1gErgTanVmABsNS9fwK4B9wBxpz+BIaBIeAScBkYj+0IYqJJ20AIPArsBfYAO5CEr4job/2FnBDngVPAj8D1iP6WUYYWoAs4CgwCeWUNOi9dzpsRAQuBA8DnyDCsnfRyGnceDzjPRoN0AB8Ao+gnt16NOu8doUfFAzqBE0AO/UQ2qpw7ls5QI5RRXgHOoJ+0qHQGOBRatDJEF3Aa/QTFpdPumL1nPTI8aidES9+4GHhHK3AEmEQ/CdqadLFobSiiKWIfMqumHfik6TIZv1FsQSZMsnBnH5VyLkaZm1DaAgygH+C0aMDFLBO8jjxs0Q5q2jThYpdamoGPsCG/EeWAbhfLVLEE+Ar9AGZFx11MU8EK/JrUiUunie4xd2isBi6iH6ys6qKLcSJpR1bQaAcp6xp2sU4Ua4Er6AfHF111MU8Ey7FhX0ODLvaqLAb60A+Gr+pzOVChGeitYtAUvXpRmifomYdZUzTqqZKr0DmMzfAlSTmXk1jYgs3tJ1G3gK0V8hYKLdhTvSRrgDofJdd789ANvFbn7xjxsRJ4APg+ig/fh13306AcdawsqrU2sBW4gKcLGFPIVWA7UuxakVovAe8DrzbiyIiVh5Hq5x+qvbGWEWAj8AtW65Y27gLbkMWmZQlq+KAeLPlpZCGywLQh9qN/U2NqTPvnZLWAapeAPuDZKu8xkk0/FXJY6RJwqNIvGqlhFxUKUiuNAOeQlitG+jkP7Cz1g3IjwD4s+VliB5LTOZQ7Ad6JzIqhRcmclroErENmkrLQQcyYIY/M5A4XvlhqBHgTS34WaQLeKvViIQuA34BVcTgyYucG8BjSFBOYOwK8hCU/y6xiVmua2SdAqqtRjZooWjpWeAloQYaIh2K1Y8TNP8hIMAnFI8ALWPJ94CEk10DxCfBy/F4MJaZzXXgCHFQwYugwneupe4DVSG9bwx/WAqNTI8Dzmk4MFZ6DmUvAbkUjhg67YeYE2KVoxNDhGZB7gCbgX2b2zzH8YAJ4MEDajVjy/WMp0B4QQ0GhkVi2To0Ahp+02wngN+0B8IS2C0ON9gB4RNuFoUZbQAJajRlqLLcRwG/aAjzaq8aYw+KAFPaiN0KjuQlZL254Si39AYwMEyD95Qw/uRUA97VdGGrcD6ihk5SRWW4HwN/aLgw1xgLgprYLQ42bNgL4zVgA/KrtwlBjJABGtF0YatgJ4DkjAbLjl+EnF21ZuL9MLwvPI82gDb+4AOSnHgb1azoxVDgLM08D+xSNGDr0wUx5+Brgdz0vhgJF5eGjwJCiGSNehnD9IAoXhJzU8WIoMJ3rwhPgOwUjhg7TubY2cf5Rtk3cJLITtZFtenHJh7mLQr+M14uhQFGOrVm0X9wAHke2lAPmjgD3gGMxGjLi5RgFyQfbMMIn8sAG4Frhi6UKQ4aBb+NwZMTKSWYlH8pXBn0crRdDge5SL9q2cX5Q97ZxIDuGG9mgbC4rjQBNyCjwdOh2jDj5Cfn2l6wCrzQC5IF3o3BkxMp7NNgC4AT6O2Cb5qcTJfJZRC3/628Cfkb2ozfSw11gO1XWedTSHmYMWALsDcGUER89wBfV3lTrbF8rsop0fSOOjNi4inz7q5b+19oi5g7wNtZPKA3kkVzV1Pehng5hI8hiEdtdJNl8CnwW1Ye3AAPo392aSmsAWFQ2eyGxFSkr0j5YU7FuEePeD4eBXMQHZKpPse/73BOScVPj6qmSq0hoRhYYah+87+pFsd3vYqTGTDsIvqrP5UCV5cAg+sHwTUMkaK+Htcjsk3ZQfNE1F/NE8SSynlA7OFnXsIt1IlmNXQ6i1CBSyp9oViAdR7SDlTX1u9imgiXAcfSDlhV97WKaKpqRpcjawUu7ukn5tj5vYM8O5qMJF7tM8BSuLZmpJl0Ats0r0glmEfAJ9hCpknIuRpE/0tXkReAK+sFOmq642HhBK3AE6VShHXhtTQIf4ukGnhuQqlXtJGjpJLCx4ShmgC78mjzqd8dszOIQ0sNWO0FR6aw7Rmu+UYVOpKwpC/8x5NyxdIYaIU/oQG6Q/kA/kfXquvPeEXpUPGQhcBBpcjSOfnLLadx5PIjVUkZGC3IDdZRkPHoedF66nLdUkYWbkTXAHqedwGaiWyp1E1mKdQ445TQa0d+KhSycAKVYhvx/vRkpb1+HNL9sc1qEfFunFlTeRiZk/kOqoceQporDwCUk6ZeRIT5T/A/76/ETQRsz9wAAAABJRU5ErkJggg==";
@@ -879,7 +1016,8 @@ const icons = {
879
1016
  mobileRect: mobile_rect_namespaceObject,
880
1017
  otherCircle: other_circle_namespaceObject,
881
1018
  movableRect: movable_rect_namespaceObject,
882
- portableRect: portable_rect_namespaceObject
1019
+ portableRect: portable_rect_namespaceObject,
1020
+ controllerRect: controller_namespaceObject
883
1021
  };
884
1022
  const borderIcon = {
885
1023
  circle: {
@@ -13779,8 +13917,15 @@ const stateColorRule = [
13779
13917
  ];
13780
13918
  class MovableManager extends EmitManager {
13781
13919
  movables = void 0;
13920
+ iconRotationAlignment = 'map';
13921
+ showNameByDefault;
13922
+ iconBaseSize;
13923
+ circleBaseRadius;
13782
13924
  constructor(mapless, options){
13783
13925
  super(mapless, options, 'movable');
13926
+ this.showNameByDefault = options?.showNameByDefault ?? false;
13927
+ this.iconBaseSize = options?.iconBaseSize ?? 0.3;
13928
+ this.circleBaseRadius = options?.circleBaseRadius ?? 8;
13784
13929
  this.onInitialized();
13785
13930
  this.setupClickHandler();
13786
13931
  this.setupHoverHandler();
@@ -13847,8 +13992,8 @@ class MovableManager extends EmitManager {
13847
13992
  ],
13848
13993
  true
13849
13994
  ],
13850
- 0.4,
13851
- 0.3
13995
+ this.iconBaseSize + 0.1,
13996
+ this.iconBaseSize
13852
13997
  ],
13853
13998
  'icon-rotate': [
13854
13999
  'get',
@@ -13856,8 +14001,11 @@ class MovableManager extends EmitManager {
13856
14001
  ],
13857
14002
  'icon-allow-overlap': true,
13858
14003
  'icon-ignore-placement': true,
13859
- 'icon-rotation-alignment': 'map',
13860
- 'text-field': [
14004
+ 'icon-rotation-alignment': this.iconRotationAlignment,
14005
+ 'text-field': this.showNameByDefault ? [
14006
+ 'get',
14007
+ 'name'
14008
+ ] : [
13861
14009
  'case',
13862
14010
  [
13863
14011
  '==',
@@ -13925,8 +14073,8 @@ class MovableManager extends EmitManager {
13925
14073
  ],
13926
14074
  true
13927
14075
  ],
13928
- 6,
13929
- 5
14076
+ this.circleBaseRadius + 2,
14077
+ this.circleBaseRadius
13930
14078
  ],
13931
14079
  'circle-color': stateColorRule,
13932
14080
  'circle-stroke-width': [
@@ -14109,12 +14257,19 @@ class MovableManager extends EmitManager {
14109
14257
  padding: padding ?? 50
14110
14258
  });
14111
14259
  }
14260
+ setIconRotationAlignment(alignment) {
14261
+ this.iconRotationAlignment = alignment;
14262
+ if (this.map && this.idHub?.detailLayerId) this.map.setLayoutProperty(this.idHub.detailLayerId, 'icon-rotation-alignment', alignment);
14263
+ }
14264
+ getIconRotationAlignment() {
14265
+ return this.iconRotationAlignment;
14266
+ }
14112
14267
  destroy() {
14113
14268
  super.destroy();
14114
14269
  this.movables = void 0;
14115
14270
  }
14116
14271
  }
14117
- const attachMovable = (mapless, name)=>mapless?.use(MovableManager, name);
14272
+ const attachMovable = (mapless, name, options)=>mapless?.use(MovableManager, name, options);
14118
14273
  const getMovable = (mapless, name)=>mapless?.get(MovableManager, name);
14119
14274
  const detachMovable = (mapless, name)=>mapless?.unuse(MovableManager, name);
14120
14275
  const hasAzimuth = (station)=>maplessPreset.movableTypes.includes(station.category || '');
@@ -14511,6 +14666,7 @@ class NewStationManager extends EmitManager {
14511
14666
  'portable',
14512
14667
  'movable'
14513
14668
  ].includes(station.category?.toLowerCase())) return station.category?.toLowerCase() || '';
14669
+ if (station.category && 'controller' === station.category.toLowerCase()) return 'controller';
14514
14670
  return 'other';
14515
14671
  };
14516
14672
  getFeatures = (stations)=>stations.filter((station)=>{
@@ -16425,36 +16581,26 @@ const getTrack = (mapless, name)=>mapless?.get(TrackManager, name);
16425
16581
  const detachTrack = (mapless, name)=>mapless?.unuse(TrackManager, name);
16426
16582
  class AirspaceManager extends manager_Manager {
16427
16583
  zones = {};
16428
- config;
16584
+ config = {
16585
+ opacity: 0.3,
16586
+ defaultColor: '#4a90e2',
16587
+ defaultEdgeColor: '#ff00ff',
16588
+ defaultEdgeOpacity: 0.8,
16589
+ defaultEdgeWidth: 2,
16590
+ defaultMinAltitude: 0,
16591
+ defaultMaxAltitude: 1000,
16592
+ defaultFillColor: 'rgba(74, 144, 226, 0.3)',
16593
+ defaultFillOpacity: 0.3
16594
+ };
16429
16595
  originalProperties = {};
16430
16596
  constructor(mapless, options){
16431
16597
  super(mapless, options, 'airspace');
16432
- this.config = {
16433
- minAltitude: 0,
16434
- maxAltitude: 1000,
16435
- color: 0x4a90e2,
16436
- opacity: 0.3,
16437
- edgeColor: 0xff00ff,
16438
- edgeOpacity: 0.8,
16439
- edgeWidth: 2
16440
- };
16441
16598
  this.onInitialized();
16442
16599
  }
16443
- setConfig(config) {
16444
- this.config = {
16445
- minAltitude: config.minAltitude ?? this.config.minAltitude,
16446
- maxAltitude: config.maxAltitude ?? this.config.maxAltitude,
16447
- color: config.color ?? this.config.color,
16448
- opacity: config.opacity ?? this.config.opacity,
16449
- edgeColor: config.edgeColor ?? this.config.edgeColor,
16450
- edgeOpacity: config.edgeOpacity ?? this.config.edgeOpacity,
16451
- edgeWidth: config.edgeWidth ?? this.config.edgeWidth
16452
- };
16453
- if (this.map && this.idHub?.lineLayerId) {
16454
- this.map.setPaintProperty(this.idHub.lineLayerId, 'line-color', this.numberToColor(this.config.edgeColor));
16455
- this.map.setPaintProperty(this.idHub.lineLayerId, 'line-opacity', this.config.edgeOpacity);
16456
- this.map.setPaintProperty(this.idHub.lineLayerId, 'line-width', this.config.edgeWidth);
16457
- this.map.setPaintProperty(this.idHub.fillLayerId, 'fill-extrusion-opacity', this.config.opacity);
16600
+ setConfig(options) {
16601
+ if (void 0 !== options.opacity) {
16602
+ this.config.opacity = options.opacity;
16603
+ if (this.map && this.idHub?.fillLayerId) this.map.setPaintProperty(this.idHub.fillLayerId, 'fill-extrusion-opacity', this.config.opacity);
16458
16604
  }
16459
16605
  }
16460
16606
  handleCoordSystemChange(_system) {
@@ -16467,6 +16613,7 @@ class AirspaceManager extends manager_Manager {
16467
16613
  const newHub = new core_IdHub({
16468
16614
  layers: {
16469
16615
  fillLayerId: 'dzmap-airspace-fill-layer',
16616
+ fill2DLayerId: 'dzmap-airspace-fill-2d-layer',
16470
16617
  lineLayerId: 'dzmap-airspace-line-layer'
16471
16618
  },
16472
16619
  sources: {
@@ -16479,14 +16626,38 @@ class AirspaceManager extends manager_Manager {
16479
16626
  if (!this.mapless) return;
16480
16627
  const sid = this.idHub?.sourceId;
16481
16628
  this.mapless.addEmptySource(sid);
16629
+ this.mapless.addLayer({
16630
+ id: this.idHub?.fill2DLayerId,
16631
+ type: 'fill',
16632
+ source: sid,
16633
+ paint: {
16634
+ 'fill-color': [
16635
+ 'get',
16636
+ 'fillColor'
16637
+ ],
16638
+ 'fill-opacity': [
16639
+ 'get',
16640
+ 'fillOpacity'
16641
+ ]
16642
+ }
16643
+ });
16482
16644
  this.mapless.addLayer({
16483
16645
  id: this.idHub?.lineLayerId,
16484
16646
  type: 'line',
16485
16647
  source: sid,
16486
16648
  paint: {
16487
- 'line-color': this.numberToColor(this.config.edgeColor),
16488
- 'line-opacity': this.config.edgeOpacity,
16489
- 'line-width': this.config.edgeWidth
16649
+ 'line-color': [
16650
+ 'get',
16651
+ 'edgeColor'
16652
+ ],
16653
+ 'line-opacity': [
16654
+ 'get',
16655
+ 'edgeOpacity'
16656
+ ],
16657
+ 'line-width': [
16658
+ 'get',
16659
+ 'edgeWidth'
16660
+ ]
16490
16661
  }
16491
16662
  });
16492
16663
  this.mapless.addLayer({
@@ -16498,7 +16669,7 @@ class AirspaceManager extends manager_Manager {
16498
16669
  'get',
16499
16670
  'color'
16500
16671
  ],
16501
- 'fill-extrusion-opacity': this.config.opacity || 0,
16672
+ 'fill-extrusion-opacity': this.config.opacity,
16502
16673
  'fill-extrusion-height': [
16503
16674
  'get',
16504
16675
  'maxAltitude'
@@ -16510,15 +16681,11 @@ class AirspaceManager extends manager_Manager {
16510
16681
  }
16511
16682
  });
16512
16683
  }
16513
- numberToColor(num) {
16514
- return `#${num.toString(16).padStart(6, '0')}`;
16515
- }
16516
16684
  generateId() {
16517
16685
  return `airspace-${Object.keys(this.zones).length}`;
16518
16686
  }
16519
16687
  drawZone(id, options) {
16520
- const { name = '', geometry, minAltitude = this.config.minAltitude, maxAltitude = this.config.maxAltitude, color = this.config.color, opacity = this.config.opacity } = options;
16521
- const zoneColor = this.numberToColor(color);
16688
+ const { name = '', geometry, minAltitude = this.config.defaultMinAltitude, maxAltitude = this.config.defaultMaxAltitude, color = this.config.defaultColor, edgeColor = this.config.defaultEdgeColor, edgeOpacity = this.config.defaultEdgeOpacity, edgeWidth = this.config.defaultEdgeWidth, fillColor = this.config.defaultFillColor, fillOpacity = this.config.defaultFillOpacity } = options;
16522
16689
  const feature = {
16523
16690
  type: 'Feature',
16524
16691
  geometry: {
@@ -16530,8 +16697,12 @@ class AirspaceManager extends manager_Manager {
16530
16697
  name,
16531
16698
  minAltitude,
16532
16699
  maxAltitude,
16533
- color: zoneColor,
16534
- opacity
16700
+ color,
16701
+ edgeColor,
16702
+ edgeOpacity,
16703
+ edgeWidth,
16704
+ fillColor,
16705
+ fillOpacity
16535
16706
  }
16536
16707
  };
16537
16708
  this.zones[id] = {
@@ -16541,20 +16712,38 @@ class AirspaceManager extends manager_Manager {
16541
16712
  minAltitude,
16542
16713
  maxAltitude,
16543
16714
  color,
16544
- opacity,
16715
+ edgeColor,
16716
+ edgeOpacity,
16717
+ edgeWidth,
16718
+ fillColor,
16719
+ fillOpacity,
16545
16720
  feature
16546
16721
  };
16547
16722
  this.originalProperties[id] = {
16548
- color,
16549
- opacity
16723
+ color
16550
16724
  };
16551
16725
  this.updateSource();
16552
16726
  }
16553
16727
  updateSource() {
16554
- this.mapless?.setData(this.idHub?.sourceId, {
16728
+ const featureCollection = {
16555
16729
  type: 'FeatureCollection',
16556
16730
  features: Object.values(this.zones).map((z)=>z.feature)
16557
- });
16731
+ };
16732
+ this.mapless?.setData(this.idHub?.sourceId, featureCollection);
16733
+ if (this.map && this.idHub?.lineLayerId && Object.keys(this.zones).length > 0) {
16734
+ this.map.setPaintProperty(this.idHub.lineLayerId, 'line-color', [
16735
+ 'get',
16736
+ 'edgeColor'
16737
+ ]);
16738
+ this.map.setPaintProperty(this.idHub.lineLayerId, 'line-opacity', [
16739
+ 'get',
16740
+ 'edgeOpacity'
16741
+ ]);
16742
+ this.map.setPaintProperty(this.idHub.lineLayerId, 'line-width', [
16743
+ 'get',
16744
+ 'edgeWidth'
16745
+ ]);
16746
+ }
16558
16747
  }
16559
16748
  addZone(options) {
16560
16749
  const id = options.id || this.generateId();
@@ -16575,44 +16764,36 @@ class AirspaceManager extends manager_Manager {
16575
16764
  this.originalProperties = {};
16576
16765
  this.mapless?.setEmptyData(this.idHub?.sourceId);
16577
16766
  }
16578
- updateZoneColor(id, color) {
16767
+ updateZone(id, options) {
16579
16768
  const zone = this.zones[id];
16580
16769
  if (!zone) return;
16581
- zone.color = color;
16582
- const newColor = this.numberToColor(color);
16583
- if (zone.feature.properties) zone.feature.properties.color = newColor;
16770
+ Object.assign(zone, options);
16771
+ if (zone.feature.properties) Object.assign(zone.feature.properties, options);
16772
+ if (options.color && !this.originalProperties[id]) this.originalProperties[id] = {
16773
+ color: zone.color
16774
+ };
16584
16775
  this.updateSource();
16585
16776
  }
16586
- updateZoneOpacity(id, opacity) {
16587
- const zone = this.zones[id];
16588
- if (!zone) return;
16589
- zone.opacity = opacity;
16590
- if (zone.feature.properties) zone.feature.properties.opacity = opacity;
16591
- this.updateSource();
16777
+ updateZoneColor(id, color) {
16778
+ this.updateZone(id, {
16779
+ color
16780
+ });
16592
16781
  }
16593
16782
  updateZoneAltitude(id, minAltitude, maxAltitude) {
16594
- const zone = this.zones[id];
16595
- if (!zone) return;
16596
- zone.minAltitude = minAltitude;
16597
- zone.maxAltitude = maxAltitude;
16598
- if (zone.feature.properties) {
16599
- zone.feature.properties.minAltitude = minAltitude;
16600
- zone.feature.properties.maxAltitude = maxAltitude;
16601
- }
16602
- this.updateSource();
16783
+ this.updateZone(id, {
16784
+ minAltitude,
16785
+ maxAltitude
16786
+ });
16603
16787
  }
16604
16788
  highlightZone(id, highlighted = true) {
16605
- const zone = this.zones[id];
16606
- if (!zone) return;
16607
- if (highlighted) {
16608
- this.updateZoneColor(id, 0xff0000);
16609
- this.updateZoneOpacity(id, 0.6);
16610
- } else {
16789
+ if (highlighted) this.updateZone(id, {
16790
+ color: '#ff0000'
16791
+ });
16792
+ else {
16611
16793
  const original = this.originalProperties[id];
16612
- if (original) {
16613
- this.updateZoneColor(id, original.color);
16614
- this.updateZoneOpacity(id, original.opacity);
16615
- }
16794
+ if (original) this.updateZone(id, {
16795
+ color: original.color
16796
+ });
16616
16797
  }
16617
16798
  }
16618
16799
  getZone(id) {
@@ -16646,6 +16827,21 @@ class AirspaceManager extends manager_Manager {
16646
16827
  });
16647
16828
  }
16648
16829
  }
16830
+ setFillExtrusionVisible(visible) {
16831
+ if (this.map && this.idHub?.fillLayerId) this.map.setLayoutProperty(this.idHub.fillLayerId, 'visibility', visible ? 'visible' : 'none');
16832
+ }
16833
+ isFillExtrusionVisible() {
16834
+ if (this.map && this.idHub?.fillLayerId) {
16835
+ const visibility = this.map.getLayoutProperty(this.idHub.fillLayerId, 'visibility');
16836
+ return 'none' !== visibility;
16837
+ }
16838
+ return true;
16839
+ }
16840
+ toggleFillExtrusion() {
16841
+ const currentState = this.isFillExtrusionVisible();
16842
+ this.setFillExtrusionVisible(!currentState);
16843
+ return !currentState;
16844
+ }
16649
16845
  destroy() {
16650
16846
  super.destroy();
16651
16847
  this.zones = {};
@@ -16798,10 +16994,18 @@ const elevatedLine = ElevatedLineManager;
16798
16994
  const attachElevatedLine = (mapless, name, options)=>mapless?.use(ElevatedLineManager, name, options);
16799
16995
  const getElevatedLine = (mapless, name)=>mapless?.get(ElevatedLineManager, name);
16800
16996
  const detachElevatedLine = (mapless, name)=>mapless?.unuse(ElevatedLineManager, name);
16801
- class MapModelManager extends manager_Manager {
16997
+ class MapModelManager extends EmitManager {
16802
16998
  models = new Map();
16803
16999
  modelCounter = 0;
16804
17000
  _idHub;
17001
+ globalClickHandler;
17002
+ selectedModelId = null;
17003
+ defaultSelectedColor = [
17004
+ 1,
17005
+ 0,
17006
+ 0
17007
+ ];
17008
+ themeColorModels = new Set();
16805
17009
  constructor(mapless, options){
16806
17010
  super(mapless, options, 'mapModel');
16807
17011
  this.map = mapless.map;
@@ -16809,11 +17013,76 @@ class MapModelManager extends manager_Manager {
16809
17013
  sources: {},
16810
17014
  layers: {}
16811
17015
  });
16812
- this.init();
17016
+ this.onInitialized();
17017
+ this.setupGlobalClickHandler();
16813
17018
  }
16814
- init() {
16815
- if (!this.map) console.warn('MapModelManager: Map not initialized');
17019
+ setupGlobalClickHandler() {
17020
+ if (!this.map) return;
17021
+ this.globalClickHandler = (e)=>{
17022
+ if (!this.map) return;
17023
+ const modelLayerIds = Array.from(this.models.values()).map((m)=>m.layerId);
17024
+ if (0 === modelLayerIds.length) return;
17025
+ const features = this.map.queryRenderedFeatures(e.point, {
17026
+ layers: modelLayerIds
17027
+ });
17028
+ if (!features || 0 === features.length) return;
17029
+ const feature = features[0];
17030
+ const layerId = feature.layer?.id;
17031
+ const modelData = Array.from(this.models.values()).find((m)=>m.layerId === layerId);
17032
+ if (modelData) {
17033
+ const clickEvent = {
17034
+ id: modelData.id,
17035
+ url: modelData.url,
17036
+ lngLat: modelData.config.position || modelData.config.lngLat || [
17037
+ 0,
17038
+ 0
17039
+ ],
17040
+ altitude: modelData.config.altitude || 0,
17041
+ rotation: modelData.config.orientation,
17042
+ scale: modelData.config.scale,
17043
+ originalEvent: e
17044
+ };
17045
+ this.emit('modelClick', clickEvent);
17046
+ }
17047
+ };
17048
+ this.map.on('click', this.globalClickHandler);
17049
+ }
17050
+ getDefaultThemeColor() {
17051
+ const isDark = this.mapless?.isDark();
17052
+ if (isDark) return [
17053
+ 68 / 255,
17054
+ 233 / 255,
17055
+ 234 / 255
17056
+ ];
17057
+ return null;
17058
+ }
17059
+ removeModelColor(id) {
17060
+ const modelData = this.models.get(id);
17061
+ if (!modelData) return;
17062
+ if (!this.map) return;
17063
+ if (this.map.getLayer(modelData.layerId)) {
17064
+ this.map.setPaintProperty(modelData.layerId, 'model-color', void 0);
17065
+ this.map.setPaintProperty(modelData.layerId, 'model-color-mix-intensity', void 0);
17066
+ }
16816
17067
  }
17068
+ updateAllModelsThemeColor() {
17069
+ const themeColor = this.getDefaultThemeColor();
17070
+ this.themeColorModels.forEach((modelId)=>{
17071
+ const modelData = this.models.get(modelId);
17072
+ if (!modelData) return void this.themeColorModels.delete(modelId);
17073
+ if (modelId !== this.selectedModelId) if (themeColor) this.updateModelColor(modelId, themeColor, 0.8);
17074
+ else this.removeModelColor(modelId);
17075
+ });
17076
+ }
17077
+ onStyleChanged = (_tag)=>{
17078
+ if (!this.map) return;
17079
+ this.updateAllModelsThemeColor();
17080
+ this.map.once('style.load', ()=>{
17081
+ setTimeout(()=>{
17082
+ this.updateAllModelsThemeColor();
17083
+ }, 100);
17084
+ });
17085
+ };
16817
17086
  generateSourceId() {
16818
17087
  const id = `dzmap-model-source-${this.modelCounter}`;
16819
17088
  this._idHub.addSource(`model-${this.modelCounter}`, id);
@@ -16841,7 +17110,6 @@ class MapModelManager extends manager_Manager {
16841
17110
  };
16842
17111
  if (materialOverrides && Object.keys(materialOverrides).length > 0) sourceConfig.models[modelId].materialOverrideNames = Object.keys(materialOverrides);
16843
17112
  if (nodeOverrides && Object.keys(nodeOverrides).length > 0) sourceConfig.models[modelId].nodeOverrideNames = Object.keys(nodeOverrides);
16844
- console.log('Adding model source:', sourceConfig);
16845
17113
  this.map.addSource(sourceId, sourceConfig);
16846
17114
  const layerConfig = {
16847
17115
  id: layerId,
@@ -16949,7 +17217,6 @@ class MapModelManager extends manager_Manager {
16949
17217
  rotationMatch.push(defaultRotation);
16950
17218
  layerConfig.paint['model-rotation'] = rotationMatch;
16951
17219
  }
16952
- console.log('Adding model layer:', layerConfig);
16953
17220
  this.map.addLayer(layerConfig);
16954
17221
  return {
16955
17222
  sourceId,
@@ -16964,34 +17231,27 @@ class MapModelManager extends manager_Manager {
16964
17231
  0
16965
17232
  ], scale = 1, materialOverrides, nodeOverrides } = options;
16966
17233
  this.removeModel(id);
16967
- try {
16968
- const mainConfig = {
16969
- uri: url,
16970
- position: lngLat,
16971
- altitude,
16972
- orientation: rotation,
16973
- scale
16974
- };
16975
- const { sourceId, layerId } = this.addSingleModel(id, mainConfig, materialOverrides, nodeOverrides);
16976
- this.models.set(id, {
16977
- id,
16978
- sourceId,
16979
- layerId,
16980
- url,
16981
- config: mainConfig,
16982
- materialOverrides,
16983
- nodeOverrides
16984
- });
16985
- console.log(`MapModel added: ${id}`, {
16986
- url,
16987
- lngLat,
16988
- altitude,
16989
- rotation,
16990
- scale
16991
- });
16992
- } catch (error) {
16993
- console.error(`Failed to add model ${id}:`, error);
16994
- throw error;
17234
+ const mainConfig = {
17235
+ uri: url,
17236
+ position: lngLat,
17237
+ altitude,
17238
+ orientation: rotation,
17239
+ scale
17240
+ };
17241
+ const { sourceId, layerId } = this.addSingleModel(id, mainConfig, materialOverrides, nodeOverrides);
17242
+ this.models.set(id, {
17243
+ id,
17244
+ sourceId,
17245
+ layerId,
17246
+ url,
17247
+ config: mainConfig,
17248
+ materialOverrides,
17249
+ nodeOverrides
17250
+ });
17251
+ if (!materialOverrides || 0 === Object.keys(materialOverrides).length) {
17252
+ this.themeColorModels.add(id);
17253
+ const themeColor = this.getDefaultThemeColor();
17254
+ if (themeColor) this.updateModelColor(id, themeColor, 0.8);
16995
17255
  }
16996
17256
  }
16997
17257
  addModels(models) {
@@ -17001,7 +17261,7 @@ class MapModelManager extends manager_Manager {
17001
17261
  }
17002
17262
  updateModelPosition(id, lngLat, altitude = 0) {
17003
17263
  const modelData = this.models.get(id);
17004
- if (!modelData) return void console.warn(`Model with id "${id}" not found`);
17264
+ if (!modelData) return;
17005
17265
  if (!this.map) return;
17006
17266
  const source = this.map.getSource(modelData.sourceId);
17007
17267
  if (source?.models?.[id]) {
@@ -17013,25 +17273,18 @@ class MapModelManager extends manager_Manager {
17013
17273
  0,
17014
17274
  altitude
17015
17275
  ]);
17016
- console.log(`MapModel position updated: ${id}`, {
17017
- lngLat,
17018
- altitude
17019
- });
17020
17276
  }
17021
17277
  updateModelRotation(id, rotation) {
17022
17278
  const modelData = this.models.get(id);
17023
- if (!modelData) return void console.warn(`Model with id "${id}" not found`);
17279
+ if (!modelData) return;
17024
17280
  if (!this.map) return;
17025
17281
  const source = this.map.getSource(modelData.sourceId);
17026
17282
  if (source?.models?.[id]) source.models[id].orientation = rotation;
17027
17283
  if (this.map.getLayer(modelData.layerId)) this.map.setPaintProperty(modelData.layerId, 'model-rotation', rotation);
17028
- console.log(`MapModel rotation updated: ${id}`, {
17029
- rotation
17030
- });
17031
17284
  }
17032
17285
  updateModelScale(id, scale) {
17033
17286
  const modelData = this.models.get(id);
17034
- if (!modelData) return void console.warn(`Model with id "${id}" not found`);
17287
+ if (!modelData) return;
17035
17288
  if (!this.map) return;
17036
17289
  const source = this.map.getSource(modelData.sourceId);
17037
17290
  if (source?.models?.[id]) source.models[id].scale = scale;
@@ -17040,9 +17293,6 @@ class MapModelManager extends manager_Manager {
17040
17293
  scale,
17041
17294
  scale
17042
17295
  ]);
17043
- console.log(`MapModel scale updated: ${id}`, {
17044
- scale
17045
- });
17046
17296
  }
17047
17297
  removeModel(id) {
17048
17298
  const modelData = this.models.get(id);
@@ -17051,7 +17301,8 @@ class MapModelManager extends manager_Manager {
17051
17301
  if (this.map.getLayer(modelData.layerId)) this.map.removeLayer(modelData.layerId);
17052
17302
  if (this.map.getSource(modelData.sourceId)) this.map.removeSource(modelData.sourceId);
17053
17303
  this.models.delete(id);
17054
- console.log(`MapModel removed: ${id}`);
17304
+ this.themeColorModels.delete(id);
17305
+ if (this.selectedModelId === id) this.selectedModelId = null;
17055
17306
  }
17056
17307
  clearModels() {
17057
17308
  const modelIds = Array.from(this.models.keys());
@@ -17059,7 +17310,6 @@ class MapModelManager extends manager_Manager {
17059
17310
  this.removeModel(id);
17060
17311
  });
17061
17312
  this.models.clear();
17062
- console.log('All MapModels cleared');
17063
17313
  }
17064
17314
  getModelCount() {
17065
17315
  return this.models.size;
@@ -17070,7 +17320,81 @@ class MapModelManager extends manager_Manager {
17070
17320
  getModelIds() {
17071
17321
  return Array.from(this.models.keys());
17072
17322
  }
17323
+ updateModelColor(id, color, intensity = 1) {
17324
+ const modelData = this.models.get(id);
17325
+ if (!modelData) return;
17326
+ if (!this.map) return;
17327
+ if (this.map.getLayer(modelData.layerId)) {
17328
+ this.map.setPaintProperty(modelData.layerId, 'model-color', `rgb(${255 * color[0]}, ${255 * color[1]}, ${255 * color[2]})`);
17329
+ this.map.setPaintProperty(modelData.layerId, 'model-color-mix-intensity', intensity);
17330
+ }
17331
+ }
17332
+ setDefaultSelectedColor(color) {
17333
+ this.defaultSelectedColor = color;
17334
+ }
17335
+ getSelectedModelId() {
17336
+ return this.selectedModelId;
17337
+ }
17338
+ selectModel(id, color) {
17339
+ const previousId = this.selectedModelId;
17340
+ const selectColor = color || this.defaultSelectedColor;
17341
+ if (previousId && previousId !== id) if (this.themeColorModels.has(previousId)) {
17342
+ const themeColor = this.getDefaultThemeColor();
17343
+ if (themeColor) this.updateModelColor(previousId, themeColor, 0.8);
17344
+ else this.removeModelColor(previousId);
17345
+ } else this.removeModelColor(previousId);
17346
+ this.selectedModelId = id;
17347
+ if (id) this.updateModelColor(id, selectColor, 1);
17348
+ this.emit('modelSelect', {
17349
+ id,
17350
+ previousId
17351
+ });
17352
+ }
17353
+ hideLayer(id) {
17354
+ if (void 0 === id) return void this.models.forEach((modelData)=>{
17355
+ if (this.map?.getLayer(modelData.layerId)) this.map?.setLayoutProperty(modelData.layerId, 'visibility', 'none');
17356
+ });
17357
+ const modelData = this.models.get(id);
17358
+ if (!modelData) return;
17359
+ if (!this.map) return;
17360
+ if (this.map.getLayer(modelData.layerId)) this.map.setLayoutProperty(modelData.layerId, 'visibility', 'none');
17361
+ }
17362
+ showLayer(id) {
17363
+ if (void 0 === id) return void this.models.forEach((modelData)=>{
17364
+ if (this.map?.getLayer(modelData.layerId)) this.map?.setLayoutProperty(modelData.layerId, 'visibility', 'visible');
17365
+ });
17366
+ const modelData = this.models.get(id);
17367
+ if (!modelData) return;
17368
+ if (!this.map) return;
17369
+ if (this.map.getLayer(modelData.layerId)) this.map.setLayoutProperty(modelData.layerId, 'visibility', 'visible');
17370
+ }
17371
+ toggleLayer(id) {
17372
+ if (void 0 === id) {
17373
+ this.models.forEach((modelData)=>{
17374
+ const isVisible = this.isLayerVisible(modelData.id);
17375
+ if (isVisible) this.hideLayer(modelData.id);
17376
+ else this.showLayer(modelData.id);
17377
+ });
17378
+ return true;
17379
+ }
17380
+ const isVisible = this.isLayerVisible(id);
17381
+ if (isVisible) this.hideLayer(id);
17382
+ else this.showLayer(id);
17383
+ return !isVisible;
17384
+ }
17385
+ isLayerVisible(id) {
17386
+ const modelData = this.models.get(id);
17387
+ if (!modelData) return false;
17388
+ if (!this.map) return false;
17389
+ const layer = this.map.getLayer(modelData.layerId);
17390
+ if (!layer) return false;
17391
+ const visibility = layer.visibility;
17392
+ return 'none' !== visibility;
17393
+ }
17073
17394
  destroy() {
17395
+ if (this.map && this.globalClickHandler) this.map.off('click', this.globalClickHandler);
17396
+ this.selectedModelId = null;
17397
+ this.themeColorModels.clear();
17074
17398
  this.clearModels();
17075
17399
  }
17076
17400
  }
@@ -17485,6 +17809,260 @@ const curve3d = Curve3DManager;
17485
17809
  const attachCurve3D = (mapless, name, options)=>mapless?.use(Curve3DManager, name, options);
17486
17810
  const getCurve3D = (mapless, name)=>mapless?.get(Curve3DManager, name);
17487
17811
  const detachCurve3D = (mapless, name)=>mapless?.unuse(Curve3DManager, name);
17812
+ class InstancedModelManager extends manager_Manager {
17813
+ scene = null;
17814
+ camera = null;
17815
+ renderer = null;
17816
+ customLayerId;
17817
+ isInitialized = false;
17818
+ instancedMeshes = new Map();
17819
+ rotationAngles = new Map();
17820
+ autoRotateEnabled = new Map();
17821
+ rotationSpeeds = new Map();
17822
+ renderCount = 0;
17823
+ constructor(mapless){
17824
+ super(mapless);
17825
+ this.map = mapless.map;
17826
+ this.customLayerId = `instanced-model-layer-${this.gmid}`;
17827
+ }
17828
+ initThree() {
17829
+ if (this.isInitialized || !this.map) return;
17830
+ const canvas = this.map.getCanvas();
17831
+ const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
17832
+ if (!gl) return void console.error('InstancedModelManager: WebGL context not available');
17833
+ this.scene = new Scene();
17834
+ this.camera = new PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 10000);
17835
+ this.renderer = new WebGLRenderer({
17836
+ canvas: canvas,
17837
+ context: gl,
17838
+ antialias: false
17839
+ });
17840
+ this.renderer.autoClear = false;
17841
+ this.renderer.setClearColor(0x000000, 0);
17842
+ this.renderer.setPixelRatio(window.devicePixelRatio);
17843
+ const ambientLight = new AmbientLight(0xffffff, 0.6);
17844
+ this.scene.add(ambientLight);
17845
+ const directionalLight = new DirectionalLight(0xffffff, 0.8);
17846
+ directionalLight.position.set(0, -70, 100);
17847
+ this.scene.add(directionalLight);
17848
+ const directionalLight2 = new DirectionalLight(0xffffff, 0.5);
17849
+ directionalLight2.position.set(0, 70, 100);
17850
+ this.scene.add(directionalLight2);
17851
+ this.setupCustomLayer();
17852
+ this.isInitialized = true;
17853
+ console.log('InstancedModelManager: Initialized', {
17854
+ canvas: `${canvas.width}x${canvas.height}`,
17855
+ customLayerId: this.customLayerId
17856
+ });
17857
+ }
17858
+ setupCustomLayer() {
17859
+ if (!this.map) return;
17860
+ const customLayer = {
17861
+ id: this.customLayerId,
17862
+ type: 'custom',
17863
+ renderingMode: '3d',
17864
+ onAdd: ()=>{
17865
+ console.log('InstancedModelManager: Custom layer added');
17866
+ },
17867
+ render: (_gl, matrix)=>{
17868
+ if (!this.camera || !this.renderer || !this.scene) return;
17869
+ this.renderCount++;
17870
+ if (this.renderCount % 60 === 0) console.log('InstancedModelManager: Rendering', {
17871
+ frame: this.renderCount,
17872
+ instanceCount: this.instancedMeshes.size
17873
+ });
17874
+ this.updateAutoRotate();
17875
+ this.camera.projectionMatrix = new Matrix4().fromArray(matrix);
17876
+ _gl.enable(_gl.DEPTH_TEST);
17877
+ this.renderer.resetState();
17878
+ this.renderer.render(this.scene, this.camera);
17879
+ if (this.map) this.map.triggerRepaint();
17880
+ }
17881
+ };
17882
+ this.map.addLayer(customLayer);
17883
+ }
17884
+ updateAutoRotate() {
17885
+ this.autoRotateEnabled.forEach((enabled, id)=>{
17886
+ if (!enabled) return;
17887
+ const instancedMesh = this.instancedMeshes.get(id);
17888
+ const angles = this.rotationAngles.get(id);
17889
+ const speed = this.rotationSpeeds.get(id) || 0.01;
17890
+ if (!instancedMesh || !angles) return;
17891
+ const dummy = new Object3D();
17892
+ const count = instancedMesh.count;
17893
+ for(let i = 0; i < count; i++){
17894
+ instancedMesh.getMatrixAt(i, dummy.matrix);
17895
+ dummy.matrix.decompose(dummy.position, dummy.quaternion, dummy.scale);
17896
+ angles[i] += speed;
17897
+ dummy.rotation.set(0, 0, angles[i]);
17898
+ dummy.updateMatrix();
17899
+ instancedMesh.setMatrixAt(i, dummy.matrix);
17900
+ }
17901
+ instancedMesh.instanceMatrix.needsUpdate = true;
17902
+ });
17903
+ }
17904
+ generateInstances(config) {
17905
+ const instances = [];
17906
+ for(let i = 0; i < config.count; i++){
17907
+ const lngOffset = (Math.random() - 0.5) * config.spread * 2;
17908
+ const latOffset = (Math.random() - 0.5) * config.spread * 2;
17909
+ const altitude = 100 * Math.random() + 50;
17910
+ const rotation = Math.random() * Math.PI * 2;
17911
+ const scaleVariation = 0.8 + 0.4 * Math.random();
17912
+ instances.push({
17913
+ lngLat: [
17914
+ config.center[0] + lngOffset,
17915
+ config.center[1] + latOffset
17916
+ ],
17917
+ altitude,
17918
+ rotation,
17919
+ scale: scaleVariation
17920
+ });
17921
+ }
17922
+ return instances;
17923
+ }
17924
+ async load(id, config) {
17925
+ this.initThree();
17926
+ if (!this.scene || !this.map) throw new Error('Three.js scene not initialized');
17927
+ this.remove(id);
17928
+ const dracoLoader = new DRACOLoader();
17929
+ dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');
17930
+ const loader = new GLTFLoader();
17931
+ loader.setDRACOLoader(dracoLoader);
17932
+ try {
17933
+ console.log('InstancedModel: Loading model', config.url);
17934
+ const gltf = await new Promise((resolve, reject)=>{
17935
+ loader.load(config.url, resolve, void 0, reject);
17936
+ });
17937
+ const gltfScene = gltf.scene;
17938
+ let meshToUse = null;
17939
+ gltfScene.traverse((object)=>{
17940
+ if (object instanceof Mesh && !meshToUse) meshToUse = object;
17941
+ });
17942
+ if (!meshToUse) throw new Error('No mesh found in model');
17943
+ const mesh = meshToUse;
17944
+ const materialToUse = Array.isArray(mesh.material) ? mesh.material[0] : mesh.material;
17945
+ const instancedMesh = new InstancedMesh(mesh.geometry.clone(), materialToUse.clone(), config.count);
17946
+ instancedMesh.castShadow = true;
17947
+ instancedMesh.receiveShadow = true;
17948
+ if ('side' in instancedMesh.material) instancedMesh.material.side = DoubleSide;
17949
+ const instances = this.generateInstances(config);
17950
+ const angles = new Float32Array(config.count);
17951
+ const meterInMercator = mapbox_gl.MercatorCoordinate.fromLngLat(config.center, 0).meterInMercatorCoordinateUnits();
17952
+ console.log('InstancedModel: Creating instances', {
17953
+ instanceCount: config.count,
17954
+ center: config.center,
17955
+ scale: config.scale,
17956
+ meterInMercator,
17957
+ firstInstanceLngLat: instances[0].lngLat,
17958
+ firstInstanceAlt: instances[0].altitude
17959
+ });
17960
+ const dummy = new Object3D();
17961
+ instances.forEach((instance, i)=>{
17962
+ const mercator = mapbox_gl.MercatorCoordinate.fromLngLat(instance.lngLat, instance.altitude);
17963
+ dummy.position.set(mercator.x, mercator.y, mercator.z);
17964
+ dummy.rotation.set(0, 0, instance.rotation);
17965
+ const scale = meterInMercator * config.scale * instance.scale;
17966
+ dummy.scale.set(scale, -scale, scale);
17967
+ dummy.updateMatrix();
17968
+ instancedMesh.setMatrixAt(i, dummy.matrix);
17969
+ angles[i] = instance.rotation;
17970
+ });
17971
+ instancedMesh.instanceMatrix.needsUpdate = true;
17972
+ this.scene.add(instancedMesh);
17973
+ this.instancedMeshes.set(id, instancedMesh);
17974
+ this.rotationAngles.set(id, angles);
17975
+ this.autoRotateEnabled.set(id, config.autoRotate || false);
17976
+ this.rotationSpeeds.set(id, config.rotationSpeed || 0.01);
17977
+ console.log(`InstancedModel "${id}" loaded with ${config.count} instances`);
17978
+ } catch (error) {
17979
+ console.error(`Failed to load instanced model "${id}":`, error);
17980
+ throw error;
17981
+ }
17982
+ }
17983
+ enableAutoRotate(id, speed = 0.01) {
17984
+ this.autoRotateEnabled.set(id, true);
17985
+ this.rotationSpeeds.set(id, speed);
17986
+ }
17987
+ disableAutoRotate(id) {
17988
+ this.autoRotateEnabled.set(id, false);
17989
+ }
17990
+ updateInstance(id, index, lngLat, altitude) {
17991
+ const instancedMesh = this.instancedMeshes.get(id);
17992
+ if (!instancedMesh) return;
17993
+ const mercator = mapbox_gl.MercatorCoordinate.fromLngLat(lngLat, altitude);
17994
+ const dummy = new Object3D();
17995
+ instancedMesh.getMatrixAt(index, dummy.matrix);
17996
+ dummy.matrix.decompose(dummy.position, dummy.quaternion, dummy.scale);
17997
+ dummy.position.set(mercator.x, mercator.y, mercator.z);
17998
+ dummy.updateMatrix();
17999
+ instancedMesh.setMatrixAt(index, dummy.matrix);
18000
+ instancedMesh.instanceMatrix.needsUpdate = true;
18001
+ }
18002
+ setInstanceVisible(id, visible) {
18003
+ const instancedMesh = this.instancedMeshes.get(id);
18004
+ if (instancedMesh) instancedMesh.visible = visible;
18005
+ }
18006
+ setVisible(visible) {
18007
+ this.IsVisible = visible;
18008
+ this.instancedMeshes.forEach((mesh)=>{
18009
+ mesh.visible = visible;
18010
+ });
18011
+ }
18012
+ getInstanceCount(id) {
18013
+ const instancedMesh = this.instancedMeshes.get(id);
18014
+ return instancedMesh?.count || 0;
18015
+ }
18016
+ getIds() {
18017
+ return Array.from(this.instancedMeshes.keys());
18018
+ }
18019
+ remove(id) {
18020
+ const instancedMesh = this.instancedMeshes.get(id);
18021
+ if (!instancedMesh) return;
18022
+ if (this.scene) this.scene.remove(instancedMesh);
18023
+ instancedMesh.geometry.dispose();
18024
+ if (Array.isArray(instancedMesh.material)) instancedMesh.material.forEach((m)=>{
18025
+ m.dispose();
18026
+ });
18027
+ else instancedMesh.material.dispose();
18028
+ this.instancedMeshes.delete(id);
18029
+ this.rotationAngles.delete(id);
18030
+ this.autoRotateEnabled.delete(id);
18031
+ this.rotationSpeeds.delete(id);
18032
+ }
18033
+ clear() {
18034
+ const ids = this.getIds();
18035
+ ids.forEach((id)=>{
18036
+ this.remove(id);
18037
+ });
18038
+ }
18039
+ setColor(id, color) {
18040
+ const instancedMesh = this.instancedMeshes.get(id);
18041
+ if (!instancedMesh) return;
18042
+ const meshColor = 'string' == typeof color ? new Color(color) : color;
18043
+ if (Array.isArray(instancedMesh.material)) instancedMesh.material.forEach((m)=>{
18044
+ if ('color' in m) m.color.set(meshColor);
18045
+ });
18046
+ else if ('color' in instancedMesh.material) instancedMesh.material.color.set(meshColor);
18047
+ }
18048
+ destroy() {
18049
+ this.clear();
18050
+ if (this.map && this.customLayerId) try {
18051
+ this.map.removeLayer(this.customLayerId);
18052
+ } catch {}
18053
+ if (this.renderer) {
18054
+ this.renderer.dispose();
18055
+ this.renderer = null;
18056
+ }
18057
+ this.scene = null;
18058
+ this.camera = null;
18059
+ this.isInitialized = false;
18060
+ }
18061
+ }
18062
+ const instancedModel = InstancedModelManager;
18063
+ const attachInstancedModel = (mapless, name)=>mapless?.use(InstancedModelManager, name);
18064
+ const getInstancedModel = (mapless, name)=>mapless?.get(InstancedModelManager, name);
18065
+ const detachInstancedModel = (mapless, name)=>mapless?.unuse(InstancedModelManager, name);
17488
18066
  class Line2Manager extends manager_Manager {
17489
18067
  mapThree = null;
17490
18068
  lines = new Map();
@@ -17632,11 +18210,6 @@ class Line2Manager extends manager_Manager {
17632
18210
  points: config.points,
17633
18211
  transform
17634
18212
  });
17635
- console.log(`Line2 added: ${id}`, {
17636
- pointsCount: config.points.length,
17637
- linewidth: lineConfig.linewidth,
17638
- color: lineConfig.color
17639
- });
17640
18213
  }
17641
18214
  addLines(lines) {
17642
18215
  lines.forEach((line)=>{
@@ -18168,6 +18741,19 @@ class ThreeManager extends manager_Manager {
18168
18741
  scale: 1
18169
18742
  };
18170
18743
  userScale = 1;
18744
+ baseRotation = [
18745
+ 0,
18746
+ 0,
18747
+ 0
18748
+ ];
18749
+ currentOrientation = {
18750
+ heading: 0,
18751
+ pitch: 0,
18752
+ roll: 0
18753
+ };
18754
+ boxHelper = null;
18755
+ isSelected = false;
18756
+ clickCleanup = null;
18171
18757
  constructor(mapless){
18172
18758
  super(mapless);
18173
18759
  this.map = mapless.map;
@@ -18193,34 +18779,40 @@ class ThreeManager extends manager_Manager {
18193
18779
  0,
18194
18780
  0,
18195
18781
  0
18196
- ], scale = 1) {
18782
+ ], scale = 1, color) {
18197
18783
  const mapThree = this.getMapThree();
18198
18784
  if (!mapThree) throw new Error('MapThree not initialized. Call useThree first.');
18199
18785
  const loader = new GLTFLoader();
18200
18786
  return new Promise((resolve, reject)=>{
18201
18787
  loader.load(url, (gltf)=>{
18202
18788
  this.model = gltf.scene;
18203
- this.model.traverse((child)=>{
18789
+ if (color) this.model.traverse((child)=>{
18204
18790
  if (child instanceof Mesh) {
18205
18791
  const material = child.material;
18206
18792
  if (material.emissive) {
18207
- material.emissive = new Color(0x00ffff);
18793
+ material.emissive = new Color(color);
18208
18794
  material.emissiveIntensity = 0.5;
18209
18795
  }
18210
18796
  }
18211
18797
  });
18212
18798
  this.userScale = scale;
18799
+ this.baseRotation = [
18800
+ ...rotation
18801
+ ];
18213
18802
  const modelAsMercator = mapbox_gl.MercatorCoordinate.fromLngLat(lngLat, altitude);
18214
18803
  this.modelTransform = {
18215
18804
  translateX: modelAsMercator.x,
18216
18805
  translateY: modelAsMercator.y,
18217
18806
  translateZ: modelAsMercator.z,
18218
- rotateX: rotation[0],
18219
- rotateY: rotation[1],
18220
- rotateZ: rotation[2],
18221
18807
  scale: modelAsMercator.meterInMercatorCoordinateUnits() * scale
18222
18808
  };
18809
+ this.currentOrientation = {
18810
+ heading: 0,
18811
+ pitch: 0,
18812
+ roll: 0
18813
+ };
18223
18814
  mapThree.addRenderableObject(this.modelId, this.model, this.modelTransform);
18815
+ this._applyBaseRotation();
18224
18816
  this.animations = gltf.animations || [];
18225
18817
  if (this.animations.length > 0) {
18226
18818
  this.mixer = new AnimationMixer(gltf.scene);
@@ -18253,6 +18845,21 @@ class ThreeManager extends manager_Manager {
18253
18845
  this.currentAction = action;
18254
18846
  console.log(`Playing animation: ${animationName}`);
18255
18847
  }
18848
+ stopAnimation() {
18849
+ if (!this.mixer || !this.currentAction) return void console.warn('No animation to stop');
18850
+ this.currentAction.stop();
18851
+ console.log('Animation stopped');
18852
+ }
18853
+ pauseAnimation() {
18854
+ if (!this.currentAction) return void console.warn('No animation to pause');
18855
+ this.currentAction.paused = true;
18856
+ console.log('Animation paused');
18857
+ }
18858
+ resumeAnimation() {
18859
+ if (!this.currentAction) return void console.warn('No animation to resume');
18860
+ this.currentAction.paused = false;
18861
+ console.log('Animation resumed');
18862
+ }
18256
18863
  updatePosition(lngLat, altitude = 0) {
18257
18864
  const mapThree = this.getMapThree();
18258
18865
  if (!mapThree || !this.model) return;
@@ -18261,8 +18868,98 @@ class ThreeManager extends manager_Manager {
18261
18868
  this.modelTransform.translateY = modelAsMercator.y;
18262
18869
  this.modelTransform.translateZ = modelAsMercator.z;
18263
18870
  this.modelTransform.scale = modelAsMercator.meterInMercatorCoordinateUnits() * this.userScale;
18871
+ this._buildFullTransformMatrix(this.currentOrientation.heading, this.currentOrientation.pitch, this.currentOrientation.roll);
18872
+ }
18873
+ _applyBaseRotation() {
18874
+ const mapThree = this.getMapThree();
18875
+ if (!mapThree || !this.model) return;
18876
+ const fullTransform = new Matrix4();
18877
+ fullTransform.makeTranslation(this.modelTransform.translateX, this.modelTransform.translateY, this.modelTransform.translateZ);
18878
+ const scaleVec = new Vector3(this.modelTransform.scale, -this.modelTransform.scale, this.modelTransform.scale);
18879
+ fullTransform.scale(scaleVec);
18880
+ const baseRotationMatrix = new Matrix4();
18881
+ if (0 !== this.baseRotation[0]) baseRotationMatrix.multiply(new Matrix4().makeRotationX(this.baseRotation[0]));
18882
+ if (0 !== this.baseRotation[1]) baseRotationMatrix.multiply(new Matrix4().makeRotationY(this.baseRotation[1]));
18883
+ if (0 !== this.baseRotation[2]) baseRotationMatrix.multiply(new Matrix4().makeRotationZ(this.baseRotation[2]));
18884
+ const headingRad = -this.currentOrientation.heading * Math.PI / 180;
18885
+ const pitchRad = this.currentOrientation.pitch * Math.PI / 180;
18886
+ const rollRad = this.currentOrientation.roll * Math.PI / 180;
18887
+ const headingMatrix = new Matrix4().makeRotationZ(headingRad);
18888
+ const pitchMatrix = new Matrix4().makeRotationX(pitchRad);
18889
+ const rollMatrix = new Matrix4().makeRotationY(rollRad);
18890
+ const orientationMatrix = new Matrix4();
18891
+ orientationMatrix.multiply(rollMatrix).multiply(pitchMatrix).multiply(headingMatrix);
18892
+ const fullRotation = new Matrix4();
18893
+ fullRotation.multiply(orientationMatrix).multiply(baseRotationMatrix);
18894
+ fullTransform.multiply(fullRotation);
18895
+ this.modelTransform.matrix = fullTransform;
18896
+ this.modelTransform.rotateX = void 0;
18897
+ this.modelTransform.rotateY = void 0;
18898
+ this.modelTransform.rotateZ = void 0;
18264
18899
  mapThree.updateRenderableObjectTransform(this.modelId, this.modelTransform);
18265
18900
  }
18901
+ _buildFullTransformMatrix(heading, pitch, roll) {
18902
+ const mapThree = this.getMapThree();
18903
+ if (!mapThree || !this.model) return;
18904
+ this.currentOrientation = {
18905
+ heading,
18906
+ pitch,
18907
+ roll
18908
+ };
18909
+ const fullTransform = new Matrix4();
18910
+ fullTransform.makeTranslation(this.modelTransform.translateX, this.modelTransform.translateY, this.modelTransform.translateZ);
18911
+ const scaleVec = new Vector3(this.modelTransform.scale, -this.modelTransform.scale, this.modelTransform.scale);
18912
+ fullTransform.scale(scaleVec);
18913
+ const baseRotationMatrix = new Matrix4();
18914
+ if (0 !== this.baseRotation[0]) baseRotationMatrix.multiply(new Matrix4().makeRotationX(this.baseRotation[0]));
18915
+ if (0 !== this.baseRotation[1]) baseRotationMatrix.multiply(new Matrix4().makeRotationY(this.baseRotation[1]));
18916
+ if (0 !== this.baseRotation[2]) baseRotationMatrix.multiply(new Matrix4().makeRotationZ(this.baseRotation[2]));
18917
+ const headingRad = -heading * Math.PI / 180;
18918
+ const pitchRad = pitch * Math.PI / 180;
18919
+ const rollRad = roll * Math.PI / 180;
18920
+ const headingMatrix = new Matrix4().makeRotationZ(headingRad);
18921
+ const pitchMatrix = new Matrix4().makeRotationX(pitchRad);
18922
+ const rollMatrix = new Matrix4().makeRotationY(rollRad);
18923
+ const orientationMatrix = new Matrix4();
18924
+ orientationMatrix.multiply(rollMatrix).multiply(pitchMatrix).multiply(headingMatrix);
18925
+ const fullRotation = new Matrix4();
18926
+ fullRotation.multiply(orientationMatrix).multiply(baseRotationMatrix);
18927
+ fullTransform.multiply(fullRotation);
18928
+ this.modelTransform.matrix = fullTransform;
18929
+ this.modelTransform.rotateX = void 0;
18930
+ this.modelTransform.rotateY = void 0;
18931
+ this.modelTransform.rotateZ = void 0;
18932
+ mapThree.updateRenderableObjectTransform(this.modelId, this.modelTransform);
18933
+ }
18934
+ setRotation(rotation) {
18935
+ const mapThree = this.getMapThree();
18936
+ if (!mapThree || !this.model) return;
18937
+ this.modelTransform.rotateX = rotation[0];
18938
+ this.modelTransform.rotateY = rotation[1];
18939
+ this.modelTransform.rotateZ = rotation[2];
18940
+ mapThree.updateRenderableObjectTransform(this.modelId, this.modelTransform);
18941
+ }
18942
+ setOrientation(heading, pitch, roll) {
18943
+ this._buildFullTransformMatrix(heading, pitch, roll);
18944
+ }
18945
+ resetToBaseOrientation() {
18946
+ this.currentOrientation = {
18947
+ heading: 0,
18948
+ pitch: 0,
18949
+ roll: 0
18950
+ };
18951
+ this._applyBaseRotation();
18952
+ }
18953
+ getBaseRotation() {
18954
+ return [
18955
+ ...this.baseRotation
18956
+ ];
18957
+ }
18958
+ getOrientation() {
18959
+ return {
18960
+ ...this.currentOrientation
18961
+ };
18962
+ }
18266
18963
  enable() {
18267
18964
  const mapThree = this.getMapThree();
18268
18965
  if (mapThree) mapThree.setRenderableObjectVisible(this.modelId, true);
@@ -18271,7 +18968,62 @@ class ThreeManager extends manager_Manager {
18271
18968
  const mapThree = this.getMapThree();
18272
18969
  if (mapThree) mapThree.setRenderableObjectVisible(this.modelId, false);
18273
18970
  }
18971
+ select() {
18972
+ if (!this.model || this.isSelected) return;
18973
+ this.boxHelper = new BoxHelper(this.model, 0xffff00);
18974
+ this.model.add(this.boxHelper);
18975
+ this.isSelected = true;
18976
+ this._updateBoxHelper();
18977
+ }
18978
+ deselect() {
18979
+ if (!this.isSelected || !this.boxHelper) return;
18980
+ if (this.model) this.model.remove(this.boxHelper);
18981
+ this.boxHelper = null;
18982
+ this.isSelected = false;
18983
+ }
18984
+ toggleSelect() {
18985
+ if (this.isSelected) this.deselect();
18986
+ else this.select();
18987
+ }
18988
+ setSelectionColor(color) {
18989
+ if (this.boxHelper) {
18990
+ const boxColor = 'string' == typeof color ? new Color(color) : color;
18991
+ const material = this.boxHelper.material;
18992
+ if (material) material.color.set(boxColor);
18993
+ }
18994
+ }
18995
+ getSelected() {
18996
+ return this.isSelected;
18997
+ }
18998
+ _updateBoxHelper() {
18999
+ if (!this.boxHelper || !this.model) return;
19000
+ this.boxHelper.update();
19001
+ }
19002
+ registerClick(onClick, tolerance = 30) {
19003
+ const mapThree = this.getMapThree();
19004
+ if (!mapThree || !this.modelId) {
19005
+ console.warn('ThreeManager: Cannot register click, MapThree or modelId not available');
19006
+ return null;
19007
+ }
19008
+ if (this.clickCleanup) {
19009
+ this.clickCleanup();
19010
+ this.clickCleanup = null;
19011
+ }
19012
+ const cleanup = mapThree.registerRenderableObjectClick(this.modelId, (_object, point)=>{
19013
+ if (onClick && this.model) onClick(this.model, point);
19014
+ }, tolerance);
19015
+ this.clickCleanup = cleanup;
19016
+ return cleanup;
19017
+ }
19018
+ unregisterClick() {
19019
+ if (this.clickCleanup) {
19020
+ this.clickCleanup();
19021
+ this.clickCleanup = null;
19022
+ }
19023
+ }
18274
19024
  dispose() {
19025
+ this.unregisterClick();
19026
+ this.deselect();
18275
19027
  const mapThree = this.getMapThree();
18276
19028
  if (mapThree && this.modelId) mapThree.removeRenderableObject(this.modelId);
18277
19029
  this.model = null;
@@ -19549,6 +20301,8 @@ class ClickStrategy extends PathCollectionStrategy {
19549
20301
  onStopCollection() {
19550
20302
  if (!this.map) return;
19551
20303
  this.map.getCanvas().style.cursor = '';
20304
+ this.clearPoints();
20305
+ this.updateLayers();
19552
20306
  }
19553
20307
  handleClick = (e)=>{
19554
20308
  if (!this.isActive) return void console.log('[ClickStrategy] Click ignored, not active');
@@ -19709,6 +20463,9 @@ class FreeStrategy extends PathCollectionStrategy {
19709
20463
  if (!this.map) return;
19710
20464
  this.map.getCanvas().style.cursor = '';
19711
20465
  this.isDragging = false;
20466
+ this.clearPoints();
20467
+ this.lastPoint = null;
20468
+ this.updateLayer();
19712
20469
  }
19713
20470
  handleMouseDown = (e)=>{
19714
20471
  if (!this.isActive || !this.map) return;
@@ -19937,6 +20694,9 @@ class PathTracker {
19937
20694
  type: pathType,
19938
20695
  segments: [],
19939
20696
  style: {
20697
+ color: '#1890ff',
20698
+ width: 2,
20699
+ opacity: 0.8,
19940
20700
  ...this.options.defaultStyle
19941
20701
  }
19942
20702
  };
@@ -19995,10 +20755,12 @@ class PathTracker {
19995
20755
  if (0 === this.currentPath.segments.length) {
19996
20756
  this.currentPath = null;
19997
20757
  this.isTracking = false;
20758
+ this.updateResultLayer();
19998
20759
  }
19999
20760
  } else {
20000
20761
  this.currentPath = null;
20001
20762
  this.isTracking = false;
20763
+ this.updateResultLayer();
20002
20764
  }
20003
20765
  this.callbacks.onCancel?.();
20004
20766
  }
@@ -20052,6 +20814,7 @@ class PathTracker {
20052
20814
  cancelTracking() {
20053
20815
  this.stopTracking();
20054
20816
  this.currentPath = null;
20817
+ this.updateResultLayer();
20055
20818
  }
20056
20819
  clearPaths() {
20057
20820
  this.triggerOnClear();
@@ -20206,4 +20969,4 @@ function detachPathTracker(mapless) {
20206
20969
  pathTrackerManagers.delete(mapless);
20207
20970
  }
20208
20971
  }
20209
- export { AirspaceManager, basicObject as BasicThreeObjectManager, ClickStrategy, CoordTransformer, curve3d as Curve3DManager, Draw, elevatedLine as ElevatedLineManager, EmitManager, FixedRectangleLayer as FixedRectangleLayerManager, FreeStrategy, Lang, line2Manager as Line2Manager, LngLat, manager_Manager as Manager, mapModel as MapModelManager, MapMouseEvent, Mapless_Base as Mapless, Marker, MercatorCoordinate, mana_three_model as ModelManager, MultiCoordTransformer, OldStationManager, paint_PaintBoard as PaintBoard, PathCollectionStrategy, path_PathTracker as PathTracker, Popup, PositionMarker, PositionMouse, QuickTrans, RectangleLayer as RectangleLayerManager, icons_Singleton as Singleton, three as ThreeManager, adjustMapConfigByCoord, attachAirspace, attachBasicObject, attachBoundary, attachCluster, attachCurve, attachCurve3D, attachElevatedLine, attachFixedRectangleLayer, attachHeatmap, attachImageData, attachLine, attachLine2, attachMapModel, attachMarker, attachModel, attachMovable, attachNewStation, attachNode, attachOldStation, attachPathTracker, attachPolygon, attachRectangleLayer, attachRoutes, attachShape, attachStep, attachText, attachThree, attachTrack, buildTileUrl, calculateObjectsInPolygon, createCoordTransformer, darkFog, darkSource, defaultSource, detachAirspace, detachBasicObject, detachBoundary, detachCluster, detachCurve, detachCurve3D, detachElevatedLine, detachFixedRectangleLayer, detachHeatmap, detachImageData, detachLine, detachLine2, detachMapModel, detachMarker, detachModel, detachMovable, detachNewStation, detachNode, detachOldStation, detachPathTracker, detachPolygon, detachRectangleLayer, detachRoutes, detachShape, detachStep, detachText, detachThree, detachTrack, ellipse, genPrefixId, getAirspace, getAllSourceCoordSystems, getBasicObject, getBoundary, getCluster, getCoordDisplayName, getCurve, getCurve3D, getElevatedLine, getFixedRectangleLayer, getHeatmap, getImageData, getLine, getLine2, getMapModel, getMarker, getModel, getMovable, getNewStation, getNode, getOldStation, getPaintCenter, getPaintCenterNRadius, getPaintPolygon, getPathTracker, getPolygon, getRectangleLayer, getRoutes, getShape, getSourceCoordSystem, getSourcesByCoordSystem, getStep, getText, getThree, getTrack, isValidCoordinate, lightFog, lightSource, lite, mapbox_gl as mapboxgl, maplessPreset, point2Feature, propwmsTransformRequest, sourcesMap, newstation_stateColorRule as stateColorRule, test2Point, throttle, transformCoordinate, transformCoordinateBetweenSources, transformCoordinates, transformMapCenter, tryFunc, tryTrans };
20972
+ export { AirspaceManager, basicObject as BasicThreeObjectManager, ClickStrategy, CoordTransformer, curve3d as Curve3DManager, Draw, elevatedLine as ElevatedLineManager, EmitManager, FixedRectangleLayer as FixedRectangleLayerManager, FreeStrategy, instancedModel as InstancedModelManager, Lang, line2Manager as Line2Manager, LngLat, manager_Manager as Manager, mapModel as MapModelManager, MapMouseEvent, Mapless_Base as Mapless, Marker, MercatorCoordinate, mana_three_model as ModelManager, MultiCoordTransformer, OldStationManager, paint_PaintBoard as PaintBoard, PathCollectionStrategy, path_PathTracker as PathTracker, Popup, PositionMarker, PositionMouse, QuickTrans, RectangleLayer as RectangleLayerManager, icons_Singleton as Singleton, three as ThreeManager, adjustMapConfigByCoord, attachAirspace, attachBasicObject, attachBoundary, attachCluster, attachCurve, attachCurve3D, attachElevatedLine, attachFixedRectangleLayer, attachHeatmap, attachImageData, attachInstancedModel, attachLine, attachLine2, attachMapModel, attachMarker, attachModel, attachMovable, attachNewStation, attachNode, attachOldStation, attachPathTracker, attachPolygon, attachRectangleLayer, attachRoutes, attachShape, attachStep, attachText, attachThree, attachTrack, buildTileUrl, calculateObjectsInPolygon, createCoordTransformer, darkFog, darkSource, defaultSource, detachAirspace, detachBasicObject, detachBoundary, detachCluster, detachCurve, detachCurve3D, detachElevatedLine, detachFixedRectangleLayer, detachHeatmap, detachImageData, detachInstancedModel, detachLine, detachLine2, detachMapModel, detachMarker, detachModel, detachMovable, detachNewStation, detachNode, detachOldStation, detachPathTracker, detachPolygon, detachRectangleLayer, detachRoutes, detachShape, detachStep, detachText, detachThree, detachTrack, ellipse, genPrefixId, getAirspace, getAllSourceCoordSystems, getBasicObject, getBoundary, getCluster, getCoordDisplayName, getCurve, getCurve3D, getElevatedLine, getFixedRectangleLayer, getHeatmap, getImageData, getInstancedModel, getLine, getLine2, getMapModel, getMarker, getModel, getMovable, getNewStation, getNode, getOldStation, getPaintCenter, getPaintCenterNRadius, getPaintPolygon, getPathTracker, getPolygon, getRectangleLayer, getRoutes, getShape, getSourceCoordSystem, getSourcesByCoordSystem, getStep, getText, getThree, getTrack, isValidCoordinate, lightFog, lightSource, lite, mapbox_gl as mapboxgl, maplessPreset, point2Feature, propwmsTransformRequest, sourcesMap, newstation_stateColorRule as stateColorRule, test2Point, throttle, transformCoordinate, transformCoordinateBetweenSources, transformCoordinates, transformMapCenter, tryFunc, tryTrans };