gralobe 1.0.69 → 1.0.70
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/README.md +18 -0
- package/dist/gralobe.js +7 -7
- package/dist/gralobe.js.map +1 -1
- package/dist/gralobe.umd.cjs +6 -6
- package/dist/gralobe.umd.cjs.map +1 -1
- package/package.json +1 -1
package/dist/gralobe.umd.cjs
CHANGED
|
@@ -581,7 +581,7 @@
|
|
|
581
581
|
display: flex;
|
|
582
582
|
align-items: center;
|
|
583
583
|
justify-content: center;
|
|
584
|
-
z-index:
|
|
584
|
+
z-index: 80;
|
|
585
585
|
opacity: 0;
|
|
586
586
|
pointer-events: none;
|
|
587
587
|
transition: opacity 0.3s ease;
|
|
@@ -1413,7 +1413,7 @@ void main() {
|
|
|
1413
1413
|
right: 12px;
|
|
1414
1414
|
display: flex;
|
|
1415
1415
|
gap: 8px;
|
|
1416
|
-
z-index:
|
|
1416
|
+
z-index: 50;
|
|
1417
1417
|
pointer-events: auto;
|
|
1418
1418
|
}
|
|
1419
1419
|
|
|
@@ -1518,7 +1518,7 @@ void main() {
|
|
|
1518
1518
|
align-items: center !important;
|
|
1519
1519
|
justify-content: center !important;
|
|
1520
1520
|
cursor: pointer !important;
|
|
1521
|
-
z-index:
|
|
1521
|
+
z-index: 95 !important;
|
|
1522
1522
|
backdrop-filter: blur(4px) !important;
|
|
1523
1523
|
-webkit-backdrop-filter: blur(4px) !important;
|
|
1524
1524
|
transition: all 0.2s ease !important;
|
|
@@ -1552,7 +1552,7 @@ void main() {
|
|
|
1552
1552
|
display: flex !important;
|
|
1553
1553
|
flex-direction: column !important;
|
|
1554
1554
|
gap: 6px !important;
|
|
1555
|
-
z-index:
|
|
1555
|
+
z-index: 90 !important;
|
|
1556
1556
|
transition: opacity 0.2s ease, transform 0.2s ease !important;
|
|
1557
1557
|
pointer-events: none !important;
|
|
1558
1558
|
opacity: 0 !important;
|
|
@@ -1601,7 +1601,7 @@ void main() {
|
|
|
1601
1601
|
top: 50px !important; /* Align with top of toolbar */
|
|
1602
1602
|
right: 100px !important; /* To the left of the toolbar */
|
|
1603
1603
|
width: 160px !important;
|
|
1604
|
-
z-index:
|
|
1604
|
+
z-index: 70 !important;
|
|
1605
1605
|
|
|
1606
1606
|
--background-color: rgba(0, 10, 20, 0.9) !important;
|
|
1607
1607
|
--text-color: #ddd !important;
|
|
@@ -1745,5 +1745,5 @@ void main() {
|
|
|
1745
1745
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1746
1746
|
<polyline points="15 18 9 12 15 6"></polyline>
|
|
1747
1747
|
</svg>
|
|
1748
|
-
`,n.title="Toggle Toolbar",this.container.appendChild(n);const o=document.createElement("div");o.className="gralobe-category-bar",this.container.appendChild(o);let a=!1,l=null;const u={},c={},h=()=>{Object.keys(u).forEach(T=>{u[T].domElement.classList.remove("active-panel"),c[T].classList.remove("active")}),l=null},p=T=>{l===T?h():(h(),l=T,u[T].domElement.classList.add("active-panel"),c[T].classList.add("active"))},f=T=>{a=T,a?(o.classList.add("visible"),n.classList.add("expanded")):(o.classList.remove("visible"),n.classList.remove("expanded"),h())};n.onclick=T=>{T.stopPropagation(),f(!a)},f(!1);const m=(T,be)=>{const Qe=document.createElement("button");Qe.className="gralobe-category-btn",Qe.innerText=be,Qe.onclick=Zt=>{Zt.stopPropagation(),p(T)},o.appendChild(Qe),c[T]=Qe;const we=new Hi({container:this.container,title:""});this.categoryGUIs.push(we),we.domElement.classList.add("root");const An=we.domElement.querySelector(".title");if(An)An.remove();else if(we.domElement.children.length>0){const Zt=we.domElement.children[0];Zt.classList.contains("children")||Zt.remove()}return u[T]=we,we},g=m("texture","Texture").add(this.config,"texture",Ya).name("Theme").onChange(T=>this.setTexture(T));this.addTooltip(g,"<b>Visual Theme</b><br><br>Change the base texture of the globe. Options include satellite imagery, natural earth, dark mode (lights), and more.");const b=m("nav","Navigation"),y=b.add({toGlobe:()=>this.toGlobe()},"toGlobe").name("→ Globe"),x=b.add({toFlat:()=>this.toFlat()},"toFlat").name("→ Flat"),_=b.add({morph:this.morph},"morph",0,1).name("Morph").listen().onChange(T=>this.setMorph(T));this.addTooltip(y,"<b>Switch to Globe View</b><br><br>Smoothly animate to the 3D spherical view."),this.addTooltip(x,"<b>Switch to Map View</b><br><br>Smoothly flatten the globe into a 2D map projection."),this.addTooltip(_,"<b>Projection Morph</b><br><br>Manually control the transition between Globe (1) and Flat Map (0).");const v=m("stats","Data"),C=typeof this.config.statistic=="string"?this.config.statistic:this.config.statistic.definition.id,w=v.add({stat:C},"stat",Object.keys(Lt)).name("Metric").onChange(T=>this.setStatistic(T));this.addTooltip(w,"<b>Data Metric</b><br><br>Select the statistical dataset to visualize on the globe.");const P=m("fx","Effects"),E=this.config.effects,A=P.addFolder("Atmosphere"),L=A.add(E,"atmosphere").onChange(T=>{this.material&&(this.material.uniforms.uAtmosphereIntensity.value=T?1:0)}),M=A.add(E,"clouds").onChange(T=>{this.material&&(this.material.uniforms.uClouds.value=T?1:0)});this.addTooltip(L,"<b>Atmosphere Glow</b><br><br>Toggle the outer atmospheric glow effect."),this.addTooltip(M,"<b>Cloud Layer</b><br><br>Toggle the moving cloud layer."),A.add(E,"cloudSpeed",0,5).name("Cloud Speed").onChange(T=>{this.material&&(this.material.uniforms.uCloudSpeed.value=T)}),A.add(E,"cloudOpacity",0,1).name("Cloud Opacity").onChange(T=>{this.material&&(this.material.uniforms.uCloudOpacity.value=T)}),A.add(E,"aurora").name("Aurora").onChange(T=>{this.material&&(this.material.uniforms.uAurora.value=T?1:0)}),A.add(E,"starTwinkle").name("Star Twinkle"),A.close();const D=P.addFolder("Lighting");D.add(E,"cityLights").name("City Lights").onChange(T=>{this.material&&(this.material.uniforms.uCityLights.value=T?1:0)}),D.add(E,"oceanSpecular").name("Ocean Specular").onChange(T=>{this.material&&(this.material.uniforms.uOceanSpecular.value=T?1:0)}),D.close();const R=P.addFolder("Grid System");R.add(E,"gridLines").name("Enable Grid").onChange(T=>{this.material&&(this.material.uniforms.uGridLines.value=T?1:0)}),R.add(E,"gridOpacity",0,1).name("Opacity").onChange(T=>{this.material&&(this.material.uniforms.uGridOpacity.value=T)}),R.close();const N=T=>{T.forEach(be=>{const Qe=be.open;be.open=function(){return Qe.apply(this),T.forEach(we=>{we!==this&&we.close()}),this}})},k=P.addFolder("Special Modes");k.add(E,"hologramMode").name("Hologram").onChange(T=>{this.material&&(this.material.uniforms.uHologram.value=T?1:0)}),k.add(E,"vintageMode").name("Vintage").onChange(T=>{this.material&&(this.material.uniforms.uVintage.value=T?1:0)}),k.add(E,"thermalMode").name("Thermal").onChange(T=>{this.material&&(this.material.uniforms.uThermal.value=T?1:0)}),k.add(E,"blueprintMode").name("Blueprint").onChange(T=>{this.material&&(this.material.uniforms.uBlueprint.value=T?1:0)}),k.add(E,"glowPulse").name("Glow Pulse").onChange(T=>{this.material&&(this.material.uniforms.uGlowPulse.value=T?1:0)}),k.close(),N([A,D,R,k]);const O=m("settings","Settings"),Q=O.add(this.config,"labels",["none","minimal","all","data"]).onChange(T=>this.setLabels(T));this.addTooltip(Q,"<b>Label Visibility</b><br><br>Control which labels are shown.<br>• <b>none</b>: No labels<br>• <b>minimal</b>: Top 7 major countries<br>• <b>all</b>: All countries<br>• <b>data</b>: Only entities with active data");const Ke=O.add(this.config,"pointRadius",50,500).name("Point Radius").onChange(()=>{this.urbanPoints?this.setUrbanData(this.urbanPoints):this.currentStatistic&&this.setStatistic(this.currentStatistic)});this.addTooltip(Ke,"<b>Synthetic Geometry Radius</b><br><br>If our data consists of point locations (like cities) without defined 2D borders, we generate synthetic circular boundaries for them.<br><br>This control scales the size (in km) of these generated circles. Larger values make small cities more visible on the global map."),O.add(this.config,"extrudeHeight",0,2).name("Extrude").onChange(T=>{this.material&&(this.material.uniforms.uExtrudeRaw.value=T)}),O.add(this.config,"autoRotate").name("Auto Rotate"),O.add({screenshot:()=>this.screenshot({width:1920,height:1080})},"screenshot").name("Screenshot"),O.add(this.config,"enableShortcuts").name("Keyboard Shortcuts").onChange(T=>{this.toolbar?.setShortcutsEnabled(T)});const Ae=m("hover","Hover Info"),Je=Ae.add(this.config.hover,"enabled").name("Enable Hover");this.addTooltip(Je,"<b>Hover Information</b><br><br>Show a tooltip with feature information when hovering over countries or regions.");const Me=Ae.add(this.config.hover,"minZoom",0,1).name("Min Zoom Level").step(.1);this.addTooltip(Me,"<b>Minimum Zoom Level</b><br><br>Only show hover information when zoomed in past this level.<br>• <b>0</b>: Always show<br>• <b>1</b>: Only when very close");const qt=Ae.add(this.config.hover,"showValue").name("Show Value");this.addTooltip(qt,"<b>Show Value</b><br><br>Display the data value in the hover tooltip (if available for the current statistic).")}handleResize=()=>{if(this.isDestroyed)return;const e=this.config.width||this.container.clientWidth,t=this.config.height||this.container.clientHeight;this.camera.aspect=e/t,this.camera.updateProjectionMatrix(),this.renderer.setSize(e,t),this.countryLabels?.resize(e,t)};handleFullscreenChange=()=>{this.isDestroyed||setTimeout(()=>this.handleResize(),50)};animate=()=>{if(this.isDestroyed)return;this.animationId=requestAnimationFrame(this.animate);const e=performance.now()*.001;this.material&&(this.material.uniforms.uTime.value=e),this.stars&&(this.stars.material.uniforms.uTime.value=e),this.controls.update(),this.config.autoRotate&&this.globe&&(this.globe.rotation.y+=.002*this.morph),this.countryLabels?.update(),this.markerLayer?.update(e),this.renderer.render(this.scene,this.camera),this.countryLabels?.render(this.scene,this.camera)};toGlobe(){this.controls.enableRotate=!0,this.controls.minAzimuthAngle=-1/0,this.controls.maxAzimuthAngle=1/0,this.controls.minPolarAngle=0,this.controls.maxPolarAngle=Math.PI,this.controls.screenSpacePanning=!1,this.controls.mouseButtons={LEFT:S.MOUSE.ROTATE,MIDDLE:S.MOUSE.DOLLY,RIGHT:S.MOUSE.PAN};let e=0;const t=this.choropleth?.getBounds();if(t){const[i,r,n,o]=t;e=-((i+n)/2)*(Math.PI/180)}W.to(this,{morph:1,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.material&&(this.material.uniforms.uMorph.value=this.morph),this.atmosphere&&(this.atmosphere.material.uniforms.uMorph.value=this.morph),this.countryLabels?.setMorph(this.morph),this.markerLayer?.setMorph(this.morph),this.config.onViewChange?.("globe",this.morph)}}),W.to(this.camera.position,{x:0,y:0,z:200,duration:2.5,ease:"power2.inOut"}),W.to(this.controls.target,{x:0,y:0,z:0,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.controls.update()}}),this.globe&&W.to(this.globe.rotation,{y:e,x:0,z:0,duration:2.5,ease:"power2.inOut"}),this.stars&&W.to(this.stars.material.uniforms.uOpacity,{value:1,duration:1}),this.atmosphere&&W.to(this.atmosphere.material.uniforms.uOpacity,{value:1,duration:1}),this.toolbar?.updateProjectionIcon(!0)}toFlat(){const e=this.choropleth?.getBounds(),t=Math.PI*2*H,i=Math.PI*H;let r=0,n=0,o=t,a=i;if(e){const[f,m,d,g]=e,b=f/180*(t/2),y=d/180*(t/2),x=m/90*(i/2),_=g/90*(i/2);r=(b+y)/2,n=(x+_)/2,o=(y-b)*1.2,a=(_-x)*1.2}const l=this.camera.fov*Math.PI/180,u=this.camera.aspect,c=a/2/Math.tan(l/2),h=o/(2*Math.tan(l/2)*u),p=Math.max(c,h);this.controls.enabled=!1,W.to(this,{morph:0,duration:2,ease:"power3.inOut",onUpdate:()=>{this.material&&(this.material.uniforms.uMorph.value=this.morph),this.atmosphere&&(this.atmosphere.material.uniforms.uMorph.value=this.morph),this.countryLabels?.setMorph(this.morph),this.markerLayer?.setMorph(this.morph),this.config.onViewChange?.("flat",this.morph)},onComplete:()=>{this.controls.enabled=!0,this.controls.enableRotate=!1,this.controls.enableZoom=!0,this.controls.enablePan=!0,this.controls.minAzimuthAngle=0,this.controls.maxAzimuthAngle=0,this.controls.minPolarAngle=Math.PI/2,this.controls.maxPolarAngle=Math.PI/2,this.controls.target.set(r,n,0),this.controls.update()}}),this.globe&&W.to(this.globe.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),this.atmosphere&&W.to(this.atmosphere.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),W.to(this.camera.position,{x:r,y:n,z:p,duration:2,ease:"power3.inOut"}),W.to(this.controls.target,{x:r,y:n,z:0,duration:2,ease:"power3.inOut"}),W.to(this.camera.up,{x:0,y:1,z:0,duration:2,ease:"power3.inOut"}),this.controls.screenSpacePanning=!0,this.controls.mouseButtons={LEFT:S.MOUSE.PAN,MIDDLE:S.MOUSE.DOLLY,RIGHT:S.MOUSE.ROTATE},this.stars&&W.to(this.stars.material.uniforms.uOpacity,{value:0,duration:1}),this.atmosphere&&W.to(this.atmosphere.material.uniforms.uOpacity,{value:0,duration:1}),this.toolbar?.updateProjectionIcon(!1)}setupInteraction(){const e=new S.Raycaster,t=new S.Vector2,i=new S.Plane(new S.Vector3(0,0,1),0),r=new S.Vector3;let n=!1,o=new Date().getTime();this.createHoverTooltip(),this.renderer.domElement.addEventListener("mousedown",()=>{n=!1,o=new Date().getTime()}),this.renderer.domElement.addEventListener("mousemove",a=>{n=!0,this.config.hover?.enabled&&this.handleHover(a,e,t,i,r)}),this.renderer.domElement.addEventListener("mouseleave",()=>{this.hideHoverTooltip()}),this.renderer.domElement.addEventListener("click",a=>{if(n&&new Date().getTime()-o>200)return;const l=this.renderer.domElement.getBoundingClientRect();if(t.x=(a.clientX-l.left)/l.width*2-1,t.y=-((a.clientY-l.top)/l.height)*2+1,this.morph<.1&&(e.setFromCamera(t,this.camera),e.ray.intersectPlane(i,r),r)){const u=Math.PI*H,c=Math.PI*H/2;Math.abs(r.x)<=u&&Math.abs(r.y)<=c&&(W.to(this.controls.target,{x:r.x,y:r.y,z:0,duration:1.5,ease:"power2.inOut"}),W.to(this.camera.position,{x:r.x,y:r.y,z:50,duration:1.5,ease:"power2.inOut"}))}})}createHoverTooltip(){this.hoverTooltip=document.createElement("div"),this.hoverTooltip.className="gralobe-hover-tooltip";const e=this.config.hover?.style||{};Object.assign(this.hoverTooltip.style,{position:"absolute",display:"none",pointerEvents:"none",zIndex:"1000",padding:"8px 12px",borderRadius:"6px",fontSize:"12px",fontFamily:"'Inter', system-ui, -apple-system, sans-serif",lineHeight:"1.4",maxWidth:"200px",backdropFilter:"blur(8px)",WebkitBackdropFilter:"blur(8px)",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.4)",transition:"opacity 0.15s ease",background:e.background||"rgba(10, 20, 35, 0.95)",color:e.color||"#fff",border:`1px solid ${e.borderColor||"rgba(100, 150, 200, 0.3)"}`}),this.container.appendChild(this.hoverTooltip)}handleHover(e,t,i,r,n){if(!this.choropleth||!this.hoverTooltip)return;const o=performance.now();if(o-this.lastHoverTime<this.HOVER_THROTTLE_MS)return;this.lastHoverTime=o;const a=this.camera.position.length(),u=400-(this.config.hover?.minZoom??0)*350;if(a>u){this.hideHoverTooltip();return}const c=this.renderer.domElement.getBoundingClientRect();i.x=(e.clientX-c.left)/c.width*2-1,i.y=-((e.clientY-c.top)/c.height)*2+1;let h=null,p=null;if(this.morph<.5){if(t.setFromCamera(i,this.camera),t.ray.intersectPlane(r,n),n){const f=Math.PI*H,m=Math.PI*H/2;Math.abs(n.x)<=f&&Math.abs(n.y)<=m&&(p=n.x/(Math.PI*H)*180,h=n.y/(Math.PI*H/2)*90)}}else{t.setFromCamera(i,this.camera);const f=this.raySphereIntersection(t.ray.origin,t.ray.direction,H);if(f&&this.globe){const m=new S.Quaternion;this.globe.getWorldQuaternion(m),m.invert();const d=f.clone().applyQuaternion(m),g=H;h=Math.asin(d.y/g)*(180/Math.PI),p=Math.atan2(d.x,d.z)*(180/Math.PI)}}if(h!==null&&p!==null){const f=this.findFeatureAtLatLon(h,p);if(f){const m=f.id||f.properties?.id,d=this.choropleth.getFeatureName(m)||m;let g;this.currentValues&&m&&(g=this.currentValues[m]),this.showHoverTooltip(e,d,g),this.currentHoveredFeature!==m&&(this.currentHoveredFeature=m,this.config.onHover?.(m,d,g))}else this.hideHoverTooltip(),this.currentHoveredFeature!==null&&(this.currentHoveredFeature=null,this.config.onHover?.(null,null))}else this.hideHoverTooltip(),this.currentHoveredFeature!==null&&(this.currentHoveredFeature=null,this.config.onHover?.(null,null))}raySphereIntersection(e,t,i){const r=t.dot(t),n=2*e.dot(t),o=e.dot(e)-i*i,a=n*n-4*r*o;if(a<0)return null;const l=Math.sqrt(a),u=(-n-l)/(2*r),c=(-n+l)/(2*r);let h=null;return u>.001?h=u:c>.001&&(h=c),h===null?null:e.clone().add(t.clone().multiplyScalar(h))}findFeatureAtLatLon(e,t){if(!this.choropleth)return null;const i=this.choropleth.getFeatures();if(!i||i.length===0)return null;for(const r of i)if(this.isPointInFeature(t,e,r))return r;return null}isPointInFeature(e,t,i){const r=i.geometry;if(!r)return!1;if(r.type==="Polygon")return this.isPointInPolygon(e,t,r.coordinates);if(r.type==="MultiPolygon")return r.coordinates.some(n=>this.isPointInPolygon(e,t,n));if(r.type==="Point"){const[n,o]=r.coordinates;return Math.sqrt((e-n)**2+(t-o)**2)<2}return!1}isPointInPolygon(e,t,i){const r=i[0];if(!r||r.length<3)return!1;let n=!1;for(let o=0,a=r.length-1;o<r.length;a=o++){const l=r[o][0],u=r[o][1],c=r[a][0],h=r[a][1];u>t!=h>t&&e<(c-l)*(t-u)/(h-u)+l&&(n=!n)}return n}showHoverTooltip(e,t,i){if(!this.hoverTooltip)return;let r=`<strong>${t}</strong>`;if(this.config.hover?.showValue!==!1&&i!==void 0){const u=this.currentStatistic?this.getStatisticMetadata(this.currentStatistic):null;let c;u?.definition.format?c=u.definition.format(i):u?.definition.unit?c=`${i.toLocaleString()} ${u.definition.unit}`:c=i.toLocaleString(),r+=`<br><span style="color: rgba(255,255,255,0.7); font-size: 11px;">${c}</span>`}this.hoverTooltip.innerHTML=r,this.hoverTooltip.style.display="block";const n=this.container.getBoundingClientRect(),o=this.hoverTooltip.getBoundingClientRect();let a=e.clientX-n.left+15,l=e.clientY-n.top+15;a+o.width>n.width&&(a=e.clientX-n.left-o.width-15),l+o.height>n.height&&(l=e.clientY-n.top-o.height-15),this.hoverTooltip.style.left=`${a}px`,this.hoverTooltip.style.top=`${l}px`}hideHoverTooltip(){this.hoverTooltip&&(this.hoverTooltip.style.display="none")}setMorph(e){this.morph=e,this.material&&(this.material.uniforms.uMorph.value=e),this.atmosphere&&(this.atmosphere.material.uniforms.uMorph.value=e),this.countryLabels?.setMorph(e),this.markerLayer?.setMorph(e)}getMorph(){return this.morph}setStatistic(e){if(this.isDestroyed)return;let t;if(typeof e=="string"){this.currentStatistic=e;const i=this.getStatisticMetadata(e);i&&(t=i)}else t=e,this.currentStatistic=t.definition.id;if(t){if(this.currentValues=t.values instanceof Map?Object.fromEntries(t.values):t.values,this.countryLabels&&this.currentValues){const i=new Set(Object.keys(this.currentValues)),r=new Set,n=new Set;Ne.forEach(o=>{i.has(o.id)?(r.add(o.code),n.add(o.id)):i.has(o.code)&&(r.add(o.code),n.add(o.code))}),i.forEach(o=>{n.has(o)||r.add(o)}),this.countryLabels.setDataIds(Array.from(r))}if(this.choropleth){const i=this.choropleth.renderCustomTexture(t.values,t.definition.colorScale,t.definition.domain);if(this.material&&i){const r=new S.CanvasTexture(i);r.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),r.needsUpdate=!0,this.material.uniforms.uDataTexture.value=r,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value===0&&W.to(this.material.uniforms.uDataOpacity,{value:.7,duration:1})}this.legend&&this.legend.show(t.definition)}}}setLabels(e){this.countryLabels?.setStyle(e)}addCustomLabels(e){this.countryLabels?.addCustomLabels(e)}clearCustomLabels(){this.countryLabels?.clearCustomLabels()}async setTexture(e){if(!(!this.renderer||!this.material)){this.config.texture=e;try{const t=this.loadTextureSource(Xi[e]),i=new Promise((n,o)=>setTimeout(()=>o(new Error("Texture load timed out after 10s")),1e4)),r=await Promise.race([t,i]);if(this.isDestroyed||!this.material||!this.material.uniforms.uTexture)return;r.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),r.minFilter=S.LinearMipmapLinearFilter,r.magFilter=S.LinearFilter,this.material.uniforms.uTexture.value=r,this.material.needsUpdate=!0}catch(t){console.error(`Failed to set texture ${e}:`,t)}}}async loadTextureSource(e){return e.type==="svg"?this.loadSvgTexture(e.url,e.width,e.height):this.textureLoader.loadAsync(e.url)}async loadSvgTexture(e,t,i){const r=await fetch(e);if(!r.ok)throw new Error(`Failed to fetch SVG texture (${r.status})`);const n=await r.text(),o=this.resizeSvgTexture(n,t,i),a=new Blob([o],{type:"image/svg+xml"}),l=URL.createObjectURL(a);try{return await this.textureLoader.loadAsync(l)}finally{URL.revokeObjectURL(l)}}resizeSvgTexture(e,t,i){let r=e;return r.includes("width=")?r=r.replace(/\bwidth="[^"]*"/i,`width="${t}"`):r=r.replace("<svg",`<svg width="${t}"`),r.includes("height=")?r=r.replace(/\bheight="[^"]*"/i,`height="${i}"`):r=r.replace("<svg",`<svg height="${i}"`),r.includes("preserveAspectRatio=")?r=r.replace(/\bpreserveAspectRatio="[^"]*"/i,'preserveAspectRatio="none"'):r=r.replace("<svg",'<svg preserveAspectRatio="none"'),r}setAutoRotate(e){this.config.autoRotate=e}screenshot(e){this.exporter?.screenshot(e)}async recordGif(e){if(!this.exporter)return;const t=e?.duration||5,i=e?.fps||20,r=t*i;this.exporter.startGifCapture(e);for(let n=0;n<r;n++)this.exporter.captureGifFrame(),await new Promise(o=>setTimeout(o,1e3/i))}async recordVideo(e){if(!this.exporter)return;const t=e?.duration||5;await this.exporter.startVideoRecording(e),await new Promise(i=>setTimeout(i,t*1e3)),this.exporter.stopVideoRecording()}setEffects(e){Object.assign(this.config.effects,e),this.material&&(e.atmosphere!==void 0&&(e.atmosphere&&!this.atmosphere?this.createAtmosphere():!e.atmosphere&&this.atmosphere&&(this.scene.remove(this.atmosphere),this.atmosphere.geometry.dispose(),this.atmosphere.material.dispose(),this.atmosphere=null)),e.clouds!==void 0&&(this.material.uniforms.uClouds.value=e.clouds?1:0),e.cloudSpeed!==void 0&&(this.material.uniforms.uCloudSpeed.value=e.cloudSpeed),e.cloudOpacity!==void 0&&(this.material.uniforms.uCloudOpacity.value=e.cloudOpacity),e.atmosphereIntensity!==void 0&&(this.material.uniforms.uAtmosphereIntensity.value=e.atmosphereIntensity),e.gridLines!==void 0&&(this.material.uniforms.uGridLines.value=e.gridLines?1:0),e.gridOpacity!==void 0&&(this.material.uniforms.uGridOpacity.value=e.gridOpacity),e.glowPulse!==void 0&&(this.material.uniforms.uGlowPulse.value=e.glowPulse?1:0),e.starTwinkle!==void 0&&this.stars&&(this.stars.material.uniforms.uTwinkle.value=e.starTwinkle?1:0))}setHover(e){this.config.hover||(this.config.hover={enabled:!0,minZoom:0,showValue:!0}),Object.assign(this.config.hover,e),e.enabled===!1&&(this.hideHoverTooltip(),this.currentHoveredFeature=null)}setMarkers(e,t){this.markerLayer?t&&this.markerLayer.setConfig(t):(this.markerLayer=new Ha(t),this.scene.add(this.markerLayer.getGroup()),this.markerLayer.setMorph(this.morph)),this.markerLayer.setMarkers(e)}async setUrbanData(e){if(!this.choropleth)return;this.urbanPoints=e;const t=this.config.pointRadius||140,i=await Z.mapPointsToTopology(e,t,!0);this.choropleth.setFeatures(i.features),this.clearCustomLabels();const r=this.choropleth.getFeatureLabels();if(r.length>0){const c=r.map(h=>({...h,size:"medium"}));this.addCustomLabels(c)}let n=["#ffffb2","#fd8d3c","#bd0026"];if(this.currentStatistic){const c=this.getStatisticMetadata(this.currentStatistic);c&&c.definition.colorScale&&(n=c.definition.colorScale)}const o=Object.values(i.statistics),a=Math.max(...o,1);this.choropleth.renderCustomTexture(i.statistics,n,[0,a]),this.material&&this.material.uniforms.uDataTexture.value&&(this.material.uniforms.uDataTexture.value.needsUpdate=!0,this.material.uniforms.uDataOverlay.value=1);const l={};let u=!1;e.forEach(c=>{if(c.name||c.label){u=!0;const h=c.name||c.label||c.id||"Unknown";l[h]=c.value}}),this.currentValues=u?l:i.statistics,this.material&&(this.material.uniforms.uCityLights.value=1,this.config.effects.cityLights=!0),this.countryLabels&&(this.countryLabels.getGroup().visible=!0)}resize(e,t){this.config.width=e,this.config.height=t,this.handleResize()}toggleProjection(){this.morph>.5?this.toFlat():this.toGlobe()}async toggleFullscreen(){document.fullscreenElement?(await document.exitFullscreen(),setTimeout(()=>this.handleResize(),100)):(await this.container.requestFullscreen(),setTimeout(()=>this.handleResize(),100))}isFullscreen(){return document.fullscreenElement===this.container}getCurrentData(){if(this.currentStatistic){if(this.currentValues){if(this.choropleth){const t={},i=Array.isArray(this.currentValues)?this.currentValues:Object.entries(this.currentValues);for(const[r,n]of i){const o=this.choropleth.getFeatureName(r)||r;t[o]=n}return t}return this.currentValues}const e=Jt.find(t=>t.id===this.currentStatistic);if(e){const t={},i=this.choropleth?.getStatsMap();return i&&i.size>0?i.forEach((n,o)=>{const a=e.accessor(n),l=n.name||o;a!=null&&(t[l]=a)}):Ne.forEach(n=>{const o=e.accessor(n);o!=null&&(t[n.name||n.code]=o)}),t}}return{}}destroy(){this.isDestroyed=!0,this.animationId&&cancelAnimationFrame(this.animationId),window.removeEventListener("resize",this.handleResize),window.removeEventListener("keydown",this.handleKeydown),document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.categoryGUIs.forEach(e=>e.destroy()),this.categoryGUIs=[],this.legend?.dispose(),this.countryLabels?.dispose(),this.markerLayer?.dispose(),this.controls?.dispose(),this.toolbar?.dispose(),this.dataGrid?.dispose(),this.hoverTooltip&&this.hoverTooltip.parentNode&&(this.hoverTooltip.parentNode.removeChild(this.hoverTooltip),this.hoverTooltip=null),document.querySelectorAll(".lil-gui-tooltip").forEach(e=>e.remove()),this.globe?.geometry.dispose(),this.globe?.material?.dispose(),this.atmosphere?.geometry.dispose(),this.atmosphere?.material?.dispose(),this.stars?.geometry.dispose(),this.stars?.material?.dispose(),this.dataTexture?.dispose(),this.scene?.clear();try{this.renderer?.dispose(),this.renderer?.forceContextLoss(),this.renderer?.getContext()?.getExtension("WEBGL_lose_context")?.loseContext()}catch(e){console.warn("Error forcing context loss:",e)}this.renderer?.domElement&&this.container?.contains(this.renderer.domElement)&&this.container.removeChild(this.renderer.domElement),this.renderer=null,this.scene=null,this.camera=null}addTooltip(e,t){setTimeout(()=>{const i=e.domElement;if(!i)return;const r=i.closest(".lil-controller")||i.closest(".controller");if(!r)return;const n=r.querySelector(".lil-name")||r.querySelector(".name");if(!n||n.querySelector(".gralobe-help-icon"))return;const o=document.createElement("div");Object.assign(o.style,{display:"inline-flex",marginLeft:"auto",paddingLeft:"8px",cursor:"help",pointerEvents:"auto"});const a=document.createElement("div");a.className="gralobe-help-icon",a.innerText="?",Object.assign(a.style,{display:"flex",alignItems:"center",justifyContent:"center",width:"10px",height:"10px",borderRadius:"50%",background:"rgba(0, 180, 255, 0.15)",border:"1px solid rgba(0, 180, 255, 0.3)",color:"rgba(255, 255, 255, 0.8)",fontSize:"8px",fontWeight:"bold",transition:"all 0.2s ease"}),a.onmouseenter=()=>{a.style.background="rgba(0, 180, 255, 0.8)",a.style.boxShadow="0 0 10px rgba(0, 180, 255, 0.6)"},a.onmouseleave=()=>{a.style.background="rgba(0, 180, 255, 0.2)",a.style.boxShadow="none"},o.appendChild(a),n.appendChild(o);const l=u=>{const c=document.getElementById("gralobe-active-tooltip");c&&c.remove();const h=document.createElement("div");h.id="gralobe-active-tooltip",h.innerHTML=t,Object.assign(h.style,{position:"fixed",background:"rgba(10, 20, 30, 0.98)",color:"#fff",padding:"10px 14px",borderRadius:"6px",fontSize:"12px",lineHeight:"1.5",maxWidth:"250px",zIndex:"100000",border:"1px solid rgba(0, 180, 255, 0.3)",boxShadow:"0 8px 32px rgba(0,0,0,0.8)",pointerEvents:"none",fontFamily:"system-ui",backdropFilter:"blur(8px)",left:u.clientX+15+"px",top:u.clientY+15+"px"}),document.body.appendChild(h);const p=m=>{h.style.left=m.clientX+15+"px",h.style.top=m.clientY+15+"px"},f=()=>{h.remove(),window.removeEventListener("mousemove",p),o.removeEventListener("mouseleave",f)};window.addEventListener("mousemove",p),o.addEventListener("mouseleave",f)};o.addEventListener("mouseenter",l)},500)}getStatisticMetadata(e){if(Lt[e]){const i=Lt[e],r=Jt.find(o=>o.id===e),n={};return r&&Ne.forEach(o=>{const a=r.accessor(o);a!=null&&(n[o.id]=a)}),{definition:i,values:n}}const t=Jt.find(i=>i.id===e);if(t){const i={};return Ne.forEach(r=>{const n=t.accessor(r);n!=null&&(i[r.id]=n)}),{definition:{id:t.id,name:t.name,unit:t.unit,description:t.description,colorScale:t.colorScale,domain:t.domain,format:t.format},values:i}}return null}}ee.BUILT_IN_STATISTICS=Lt,ee.GlobeViz=Xa,ee.WORLD_STATISTICS=Ne,ee.createFormatter=ei,ee.formatValue=kn,ee.normalizeCountryValues=Zi,ee.toNumericCode=Qt,Object.defineProperty(ee,Symbol.toStringTag,{value:"Module"})}));
|
|
1748
|
+
`,n.title="Toggle Toolbar",this.container.appendChild(n);const o=document.createElement("div");o.className="gralobe-category-bar",this.container.appendChild(o);let a=!1,l=null;const u={},c={},h=()=>{Object.keys(u).forEach(T=>{u[T].domElement.classList.remove("active-panel"),c[T].classList.remove("active")}),l=null},p=T=>{l===T?h():(h(),l=T,u[T].domElement.classList.add("active-panel"),c[T].classList.add("active"))},f=T=>{a=T,a?(o.classList.add("visible"),n.classList.add("expanded")):(o.classList.remove("visible"),n.classList.remove("expanded"),h())};n.onclick=T=>{T.stopPropagation(),f(!a)},f(!1);const m=(T,be)=>{const Qe=document.createElement("button");Qe.className="gralobe-category-btn",Qe.innerText=be,Qe.onclick=Zt=>{Zt.stopPropagation(),p(T)},o.appendChild(Qe),c[T]=Qe;const we=new Hi({container:this.container,title:""});this.categoryGUIs.push(we),we.domElement.classList.add("root");const An=we.domElement.querySelector(".title");if(An)An.remove();else if(we.domElement.children.length>0){const Zt=we.domElement.children[0];Zt.classList.contains("children")||Zt.remove()}return u[T]=we,we},g=m("texture","Texture").add(this.config,"texture",Ya).name("Theme").onChange(T=>this.setTexture(T));this.addTooltip(g,"<b>Visual Theme</b><br><br>Change the base texture of the globe. Options include satellite imagery, natural earth, dark mode (lights), and more.");const b=m("nav","Navigation"),y=b.add({toGlobe:()=>this.toGlobe()},"toGlobe").name("→ Globe"),x=b.add({toFlat:()=>this.toFlat()},"toFlat").name("→ Flat"),_=b.add({morph:this.morph},"morph",0,1).name("Morph").listen().onChange(T=>this.setMorph(T));this.addTooltip(y,"<b>Switch to Globe View</b><br><br>Smoothly animate to the 3D spherical view."),this.addTooltip(x,"<b>Switch to Map View</b><br><br>Smoothly flatten the globe into a 2D map projection."),this.addTooltip(_,"<b>Projection Morph</b><br><br>Manually control the transition between Globe (1) and Flat Map (0).");const v=m("stats","Data"),C=typeof this.config.statistic=="string"?this.config.statistic:this.config.statistic.definition.id,w=v.add({stat:C},"stat",Object.keys(Lt)).name("Metric").onChange(T=>this.setStatistic(T));this.addTooltip(w,"<b>Data Metric</b><br><br>Select the statistical dataset to visualize on the globe.");const P=m("fx","Effects"),E=this.config.effects,A=P.addFolder("Atmosphere"),L=A.add(E,"atmosphere").onChange(T=>{this.material&&(this.material.uniforms.uAtmosphereIntensity.value=T?1:0)}),M=A.add(E,"clouds").onChange(T=>{this.material&&(this.material.uniforms.uClouds.value=T?1:0)});this.addTooltip(L,"<b>Atmosphere Glow</b><br><br>Toggle the outer atmospheric glow effect."),this.addTooltip(M,"<b>Cloud Layer</b><br><br>Toggle the moving cloud layer."),A.add(E,"cloudSpeed",0,5).name("Cloud Speed").onChange(T=>{this.material&&(this.material.uniforms.uCloudSpeed.value=T)}),A.add(E,"cloudOpacity",0,1).name("Cloud Opacity").onChange(T=>{this.material&&(this.material.uniforms.uCloudOpacity.value=T)}),A.add(E,"aurora").name("Aurora").onChange(T=>{this.material&&(this.material.uniforms.uAurora.value=T?1:0)}),A.add(E,"starTwinkle").name("Star Twinkle"),A.close();const D=P.addFolder("Lighting");D.add(E,"cityLights").name("City Lights").onChange(T=>{this.material&&(this.material.uniforms.uCityLights.value=T?1:0)}),D.add(E,"oceanSpecular").name("Ocean Specular").onChange(T=>{this.material&&(this.material.uniforms.uOceanSpecular.value=T?1:0)}),D.close();const R=P.addFolder("Grid System");R.add(E,"gridLines").name("Enable Grid").onChange(T=>{this.material&&(this.material.uniforms.uGridLines.value=T?1:0)}),R.add(E,"gridOpacity",0,1).name("Opacity").onChange(T=>{this.material&&(this.material.uniforms.uGridOpacity.value=T)}),R.close();const N=T=>{T.forEach(be=>{const Qe=be.open;be.open=function(){return Qe.apply(this),T.forEach(we=>{we!==this&&we.close()}),this}})},k=P.addFolder("Special Modes");k.add(E,"hologramMode").name("Hologram").onChange(T=>{this.material&&(this.material.uniforms.uHologram.value=T?1:0)}),k.add(E,"vintageMode").name("Vintage").onChange(T=>{this.material&&(this.material.uniforms.uVintage.value=T?1:0)}),k.add(E,"thermalMode").name("Thermal").onChange(T=>{this.material&&(this.material.uniforms.uThermal.value=T?1:0)}),k.add(E,"blueprintMode").name("Blueprint").onChange(T=>{this.material&&(this.material.uniforms.uBlueprint.value=T?1:0)}),k.add(E,"glowPulse").name("Glow Pulse").onChange(T=>{this.material&&(this.material.uniforms.uGlowPulse.value=T?1:0)}),k.close(),N([A,D,R,k]);const O=m("settings","Settings"),Q=O.add(this.config,"labels",["none","minimal","all","data"]).onChange(T=>this.setLabels(T));this.addTooltip(Q,"<b>Label Visibility</b><br><br>Control which labels are shown.<br>• <b>none</b>: No labels<br>• <b>minimal</b>: Top 7 major countries<br>• <b>all</b>: All countries<br>• <b>data</b>: Only entities with active data");const Ke=O.add(this.config,"pointRadius",50,500).name("Point Radius").onChange(()=>{this.urbanPoints?this.setUrbanData(this.urbanPoints):this.currentStatistic&&this.setStatistic(this.currentStatistic)});this.addTooltip(Ke,"<b>Synthetic Geometry Radius</b><br><br>If our data consists of point locations (like cities) without defined 2D borders, we generate synthetic circular boundaries for them.<br><br>This control scales the size (in km) of these generated circles. Larger values make small cities more visible on the global map."),O.add(this.config,"extrudeHeight",0,2).name("Extrude").onChange(T=>{this.material&&(this.material.uniforms.uExtrudeRaw.value=T)}),O.add(this.config,"autoRotate").name("Auto Rotate"),O.add({screenshot:()=>this.screenshot({width:1920,height:1080})},"screenshot").name("Screenshot"),O.add(this.config,"enableShortcuts").name("Keyboard Shortcuts").onChange(T=>{this.toolbar?.setShortcutsEnabled(T)});const Ae=m("hover","Hover Info"),Je=Ae.add(this.config.hover,"enabled").name("Enable Hover");this.addTooltip(Je,"<b>Hover Information</b><br><br>Show a tooltip with feature information when hovering over countries or regions.");const Me=Ae.add(this.config.hover,"minZoom",0,1).name("Min Zoom Level").step(.1);this.addTooltip(Me,"<b>Minimum Zoom Level</b><br><br>Only show hover information when zoomed in past this level.<br>• <b>0</b>: Always show<br>• <b>1</b>: Only when very close");const qt=Ae.add(this.config.hover,"showValue").name("Show Value");this.addTooltip(qt,"<b>Show Value</b><br><br>Display the data value in the hover tooltip (if available for the current statistic).")}handleResize=()=>{if(this.isDestroyed)return;const e=this.config.width||this.container.clientWidth,t=this.config.height||this.container.clientHeight;this.camera.aspect=e/t,this.camera.updateProjectionMatrix(),this.renderer.setSize(e,t),this.countryLabels?.resize(e,t)};handleFullscreenChange=()=>{this.isDestroyed||setTimeout(()=>this.handleResize(),50)};animate=()=>{if(this.isDestroyed)return;this.animationId=requestAnimationFrame(this.animate);const e=performance.now()*.001;this.material&&(this.material.uniforms.uTime.value=e),this.stars&&(this.stars.material.uniforms.uTime.value=e),this.controls.update(),this.config.autoRotate&&this.globe&&(this.globe.rotation.y+=.002*this.morph),this.countryLabels?.update(),this.markerLayer?.update(e),this.renderer.render(this.scene,this.camera),this.countryLabels?.render(this.scene,this.camera)};toGlobe(){this.controls.enableRotate=!0,this.controls.minAzimuthAngle=-1/0,this.controls.maxAzimuthAngle=1/0,this.controls.minPolarAngle=0,this.controls.maxPolarAngle=Math.PI,this.controls.screenSpacePanning=!1,this.controls.mouseButtons={LEFT:S.MOUSE.ROTATE,MIDDLE:S.MOUSE.DOLLY,RIGHT:S.MOUSE.PAN};let e=0;const t=this.choropleth?.getBounds();if(t){const[i,r,n,o]=t;e=-((i+n)/2)*(Math.PI/180)}W.to(this,{morph:1,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.material&&(this.material.uniforms.uMorph.value=this.morph),this.atmosphere&&(this.atmosphere.material.uniforms.uMorph.value=this.morph),this.countryLabels?.setMorph(this.morph),this.markerLayer?.setMorph(this.morph),this.config.onViewChange?.("globe",this.morph)}}),W.to(this.camera.position,{x:0,y:0,z:200,duration:2.5,ease:"power2.inOut"}),W.to(this.controls.target,{x:0,y:0,z:0,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.controls.update()}}),this.globe&&W.to(this.globe.rotation,{y:e,x:0,z:0,duration:2.5,ease:"power2.inOut"}),this.stars&&W.to(this.stars.material.uniforms.uOpacity,{value:1,duration:1}),this.atmosphere&&W.to(this.atmosphere.material.uniforms.uOpacity,{value:1,duration:1}),this.toolbar?.updateProjectionIcon(!0)}toFlat(){const e=this.choropleth?.getBounds(),t=Math.PI*2*H,i=Math.PI*H;let r=0,n=0,o=t,a=i;if(e){const[f,m,d,g]=e,b=f/180*(t/2),y=d/180*(t/2),x=m/90*(i/2),_=g/90*(i/2);r=(b+y)/2,n=(x+_)/2,o=(y-b)*1.2,a=(_-x)*1.2}const l=this.camera.fov*Math.PI/180,u=this.camera.aspect,c=a/2/Math.tan(l/2),h=o/(2*Math.tan(l/2)*u),p=Math.max(c,h);this.controls.enabled=!1,W.to(this,{morph:0,duration:2,ease:"power3.inOut",onUpdate:()=>{this.material&&(this.material.uniforms.uMorph.value=this.morph),this.atmosphere&&(this.atmosphere.material.uniforms.uMorph.value=this.morph),this.countryLabels?.setMorph(this.morph),this.markerLayer?.setMorph(this.morph),this.config.onViewChange?.("flat",this.morph)},onComplete:()=>{this.controls.enabled=!0,this.controls.enableRotate=!1,this.controls.enableZoom=!0,this.controls.enablePan=!0,this.controls.minAzimuthAngle=0,this.controls.maxAzimuthAngle=0,this.controls.minPolarAngle=Math.PI/2,this.controls.maxPolarAngle=Math.PI/2,this.controls.target.set(r,n,0),this.controls.update()}}),this.globe&&W.to(this.globe.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),this.atmosphere&&W.to(this.atmosphere.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),W.to(this.camera.position,{x:r,y:n,z:p,duration:2,ease:"power3.inOut"}),W.to(this.controls.target,{x:r,y:n,z:0,duration:2,ease:"power3.inOut"}),W.to(this.camera.up,{x:0,y:1,z:0,duration:2,ease:"power3.inOut"}),this.controls.screenSpacePanning=!0,this.controls.mouseButtons={LEFT:S.MOUSE.PAN,MIDDLE:S.MOUSE.DOLLY,RIGHT:S.MOUSE.ROTATE},this.stars&&W.to(this.stars.material.uniforms.uOpacity,{value:0,duration:1}),this.atmosphere&&W.to(this.atmosphere.material.uniforms.uOpacity,{value:0,duration:1}),this.toolbar?.updateProjectionIcon(!1)}setupInteraction(){const e=new S.Raycaster,t=new S.Vector2,i=new S.Plane(new S.Vector3(0,0,1),0),r=new S.Vector3;let n=!1,o=new Date().getTime();this.createHoverTooltip(),this.renderer.domElement.addEventListener("mousedown",()=>{n=!1,o=new Date().getTime()}),this.renderer.domElement.addEventListener("mousemove",a=>{n=!0,this.config.hover?.enabled&&this.handleHover(a,e,t,i,r)}),this.renderer.domElement.addEventListener("mouseleave",()=>{this.hideHoverTooltip()}),this.renderer.domElement.addEventListener("click",a=>{if(n&&new Date().getTime()-o>200)return;const l=this.renderer.domElement.getBoundingClientRect();if(t.x=(a.clientX-l.left)/l.width*2-1,t.y=-((a.clientY-l.top)/l.height)*2+1,this.morph<.1&&(e.setFromCamera(t,this.camera),e.ray.intersectPlane(i,r),r)){const u=Math.PI*H,c=Math.PI*H/2;Math.abs(r.x)<=u&&Math.abs(r.y)<=c&&(W.to(this.controls.target,{x:r.x,y:r.y,z:0,duration:1.5,ease:"power2.inOut"}),W.to(this.camera.position,{x:r.x,y:r.y,z:50,duration:1.5,ease:"power2.inOut"}))}})}createHoverTooltip(){this.hoverTooltip=document.createElement("div"),this.hoverTooltip.className="gralobe-hover-tooltip";const e=this.config.hover?.style||{};Object.assign(this.hoverTooltip.style,{position:"absolute",display:"none",pointerEvents:"none",zIndex:"60",padding:"8px 12px",borderRadius:"6px",fontSize:"12px",fontFamily:"'Inter', system-ui, -apple-system, sans-serif",lineHeight:"1.4",maxWidth:"200px",backdropFilter:"blur(8px)",WebkitBackdropFilter:"blur(8px)",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.4)",transition:"opacity 0.15s ease",background:e.background||"rgba(10, 20, 35, 0.95)",color:e.color||"#fff",border:`1px solid ${e.borderColor||"rgba(100, 150, 200, 0.3)"}`}),this.container.appendChild(this.hoverTooltip)}handleHover(e,t,i,r,n){if(!this.choropleth||!this.hoverTooltip)return;const o=performance.now();if(o-this.lastHoverTime<this.HOVER_THROTTLE_MS)return;this.lastHoverTime=o;const a=this.camera.position.length(),u=400-(this.config.hover?.minZoom??0)*350;if(a>u){this.hideHoverTooltip();return}const c=this.renderer.domElement.getBoundingClientRect();i.x=(e.clientX-c.left)/c.width*2-1,i.y=-((e.clientY-c.top)/c.height)*2+1;let h=null,p=null;if(this.morph<.5){if(t.setFromCamera(i,this.camera),t.ray.intersectPlane(r,n),n){const f=Math.PI*H,m=Math.PI*H/2;Math.abs(n.x)<=f&&Math.abs(n.y)<=m&&(p=n.x/(Math.PI*H)*180,h=n.y/(Math.PI*H/2)*90)}}else{t.setFromCamera(i,this.camera);const f=this.raySphereIntersection(t.ray.origin,t.ray.direction,H);if(f&&this.globe){const m=new S.Quaternion;this.globe.getWorldQuaternion(m),m.invert();const d=f.clone().applyQuaternion(m),g=H;h=Math.asin(d.y/g)*(180/Math.PI),p=Math.atan2(d.x,d.z)*(180/Math.PI)}}if(h!==null&&p!==null){const f=this.findFeatureAtLatLon(h,p);if(f){const m=f.id||f.properties?.id,d=this.choropleth.getFeatureName(m)||m;let g;this.currentValues&&m&&(g=this.currentValues[m]),this.showHoverTooltip(e,d,g),this.currentHoveredFeature!==m&&(this.currentHoveredFeature=m,this.config.onHover?.(m,d,g))}else this.hideHoverTooltip(),this.currentHoveredFeature!==null&&(this.currentHoveredFeature=null,this.config.onHover?.(null,null))}else this.hideHoverTooltip(),this.currentHoveredFeature!==null&&(this.currentHoveredFeature=null,this.config.onHover?.(null,null))}raySphereIntersection(e,t,i){const r=t.dot(t),n=2*e.dot(t),o=e.dot(e)-i*i,a=n*n-4*r*o;if(a<0)return null;const l=Math.sqrt(a),u=(-n-l)/(2*r),c=(-n+l)/(2*r);let h=null;return u>.001?h=u:c>.001&&(h=c),h===null?null:e.clone().add(t.clone().multiplyScalar(h))}findFeatureAtLatLon(e,t){if(!this.choropleth)return null;const i=this.choropleth.getFeatures();if(!i||i.length===0)return null;for(const r of i)if(this.isPointInFeature(t,e,r))return r;return null}isPointInFeature(e,t,i){const r=i.geometry;if(!r)return!1;if(r.type==="Polygon")return this.isPointInPolygon(e,t,r.coordinates);if(r.type==="MultiPolygon")return r.coordinates.some(n=>this.isPointInPolygon(e,t,n));if(r.type==="Point"){const[n,o]=r.coordinates;return Math.sqrt((e-n)**2+(t-o)**2)<2}return!1}isPointInPolygon(e,t,i){const r=i[0];if(!r||r.length<3)return!1;let n=!1;for(let o=0,a=r.length-1;o<r.length;a=o++){const l=r[o][0],u=r[o][1],c=r[a][0],h=r[a][1];u>t!=h>t&&e<(c-l)*(t-u)/(h-u)+l&&(n=!n)}return n}showHoverTooltip(e,t,i){if(!this.hoverTooltip)return;let r=`<strong>${t}</strong>`;if(this.config.hover?.showValue!==!1&&i!==void 0){const u=this.currentStatistic?this.getStatisticMetadata(this.currentStatistic):null;let c;u?.definition.format?c=u.definition.format(i):u?.definition.unit?c=`${i.toLocaleString()} ${u.definition.unit}`:c=i.toLocaleString(),r+=`<br><span style="color: rgba(255,255,255,0.7); font-size: 11px;">${c}</span>`}this.hoverTooltip.innerHTML=r,this.hoverTooltip.style.display="block";const n=this.container.getBoundingClientRect(),o=this.hoverTooltip.getBoundingClientRect();let a=e.clientX-n.left+15,l=e.clientY-n.top+15;a+o.width>n.width&&(a=e.clientX-n.left-o.width-15),l+o.height>n.height&&(l=e.clientY-n.top-o.height-15),this.hoverTooltip.style.left=`${a}px`,this.hoverTooltip.style.top=`${l}px`}hideHoverTooltip(){this.hoverTooltip&&(this.hoverTooltip.style.display="none")}setMorph(e){this.morph=e,this.material&&(this.material.uniforms.uMorph.value=e),this.atmosphere&&(this.atmosphere.material.uniforms.uMorph.value=e),this.countryLabels?.setMorph(e),this.markerLayer?.setMorph(e)}getMorph(){return this.morph}setStatistic(e){if(this.isDestroyed)return;let t;if(typeof e=="string"){this.currentStatistic=e;const i=this.getStatisticMetadata(e);i&&(t=i)}else t=e,this.currentStatistic=t.definition.id;if(t){if(this.currentValues=t.values instanceof Map?Object.fromEntries(t.values):t.values,this.countryLabels&&this.currentValues){const i=new Set(Object.keys(this.currentValues)),r=new Set,n=new Set;Ne.forEach(o=>{i.has(o.id)?(r.add(o.code),n.add(o.id)):i.has(o.code)&&(r.add(o.code),n.add(o.code))}),i.forEach(o=>{n.has(o)||r.add(o)}),this.countryLabels.setDataIds(Array.from(r))}if(this.choropleth){const i=this.choropleth.renderCustomTexture(t.values,t.definition.colorScale,t.definition.domain);if(this.material&&i){const r=new S.CanvasTexture(i);r.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),r.needsUpdate=!0,this.material.uniforms.uDataTexture.value=r,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value===0&&W.to(this.material.uniforms.uDataOpacity,{value:.7,duration:1})}this.legend&&this.legend.show(t.definition)}}}setLabels(e){this.countryLabels?.setStyle(e)}addCustomLabels(e){this.countryLabels?.addCustomLabels(e)}clearCustomLabels(){this.countryLabels?.clearCustomLabels()}async setTexture(e){if(!(!this.renderer||!this.material)){this.config.texture=e;try{const t=this.loadTextureSource(Xi[e]),i=new Promise((n,o)=>setTimeout(()=>o(new Error("Texture load timed out after 10s")),1e4)),r=await Promise.race([t,i]);if(this.isDestroyed||!this.material||!this.material.uniforms.uTexture)return;r.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),r.minFilter=S.LinearMipmapLinearFilter,r.magFilter=S.LinearFilter,this.material.uniforms.uTexture.value=r,this.material.needsUpdate=!0}catch(t){console.error(`Failed to set texture ${e}:`,t)}}}async loadTextureSource(e){return e.type==="svg"?this.loadSvgTexture(e.url,e.width,e.height):this.textureLoader.loadAsync(e.url)}async loadSvgTexture(e,t,i){const r=await fetch(e);if(!r.ok)throw new Error(`Failed to fetch SVG texture (${r.status})`);const n=await r.text(),o=this.resizeSvgTexture(n,t,i),a=new Blob([o],{type:"image/svg+xml"}),l=URL.createObjectURL(a);try{return await this.textureLoader.loadAsync(l)}finally{URL.revokeObjectURL(l)}}resizeSvgTexture(e,t,i){let r=e;return r.includes("width=")?r=r.replace(/\bwidth="[^"]*"/i,`width="${t}"`):r=r.replace("<svg",`<svg width="${t}"`),r.includes("height=")?r=r.replace(/\bheight="[^"]*"/i,`height="${i}"`):r=r.replace("<svg",`<svg height="${i}"`),r.includes("preserveAspectRatio=")?r=r.replace(/\bpreserveAspectRatio="[^"]*"/i,'preserveAspectRatio="none"'):r=r.replace("<svg",'<svg preserveAspectRatio="none"'),r}setAutoRotate(e){this.config.autoRotate=e}screenshot(e){this.exporter?.screenshot(e)}async recordGif(e){if(!this.exporter)return;const t=e?.duration||5,i=e?.fps||20,r=t*i;this.exporter.startGifCapture(e);for(let n=0;n<r;n++)this.exporter.captureGifFrame(),await new Promise(o=>setTimeout(o,1e3/i))}async recordVideo(e){if(!this.exporter)return;const t=e?.duration||5;await this.exporter.startVideoRecording(e),await new Promise(i=>setTimeout(i,t*1e3)),this.exporter.stopVideoRecording()}setEffects(e){Object.assign(this.config.effects,e),this.material&&(e.atmosphere!==void 0&&(e.atmosphere&&!this.atmosphere?this.createAtmosphere():!e.atmosphere&&this.atmosphere&&(this.scene.remove(this.atmosphere),this.atmosphere.geometry.dispose(),this.atmosphere.material.dispose(),this.atmosphere=null)),e.clouds!==void 0&&(this.material.uniforms.uClouds.value=e.clouds?1:0),e.cloudSpeed!==void 0&&(this.material.uniforms.uCloudSpeed.value=e.cloudSpeed),e.cloudOpacity!==void 0&&(this.material.uniforms.uCloudOpacity.value=e.cloudOpacity),e.atmosphereIntensity!==void 0&&(this.material.uniforms.uAtmosphereIntensity.value=e.atmosphereIntensity),e.gridLines!==void 0&&(this.material.uniforms.uGridLines.value=e.gridLines?1:0),e.gridOpacity!==void 0&&(this.material.uniforms.uGridOpacity.value=e.gridOpacity),e.glowPulse!==void 0&&(this.material.uniforms.uGlowPulse.value=e.glowPulse?1:0),e.starTwinkle!==void 0&&this.stars&&(this.stars.material.uniforms.uTwinkle.value=e.starTwinkle?1:0))}setHover(e){this.config.hover||(this.config.hover={enabled:!0,minZoom:0,showValue:!0}),Object.assign(this.config.hover,e),e.enabled===!1&&(this.hideHoverTooltip(),this.currentHoveredFeature=null)}setMarkers(e,t){this.markerLayer?t&&this.markerLayer.setConfig(t):(this.markerLayer=new Ha(t),this.scene.add(this.markerLayer.getGroup()),this.markerLayer.setMorph(this.morph)),this.markerLayer.setMarkers(e)}async setUrbanData(e){if(!this.choropleth)return;this.urbanPoints=e;const t=this.config.pointRadius||140,i=await Z.mapPointsToTopology(e,t,!0);this.choropleth.setFeatures(i.features),this.clearCustomLabels();const r=this.choropleth.getFeatureLabels();if(r.length>0){const c=r.map(h=>({...h,size:"medium"}));this.addCustomLabels(c)}let n=["#ffffb2","#fd8d3c","#bd0026"];if(this.currentStatistic){const c=this.getStatisticMetadata(this.currentStatistic);c&&c.definition.colorScale&&(n=c.definition.colorScale)}const o=Object.values(i.statistics),a=Math.max(...o,1);this.choropleth.renderCustomTexture(i.statistics,n,[0,a]),this.material&&this.material.uniforms.uDataTexture.value&&(this.material.uniforms.uDataTexture.value.needsUpdate=!0,this.material.uniforms.uDataOverlay.value=1);const l={};let u=!1;e.forEach(c=>{if(c.name||c.label){u=!0;const h=c.name||c.label||c.id||"Unknown";l[h]=c.value}}),this.currentValues=u?l:i.statistics,this.material&&(this.material.uniforms.uCityLights.value=1,this.config.effects.cityLights=!0),this.countryLabels&&(this.countryLabels.getGroup().visible=!0)}resize(e,t){this.config.width=e,this.config.height=t,this.handleResize()}toggleProjection(){this.morph>.5?this.toFlat():this.toGlobe()}async toggleFullscreen(){document.fullscreenElement?(await document.exitFullscreen(),setTimeout(()=>this.handleResize(),100)):(await this.container.requestFullscreen(),setTimeout(()=>this.handleResize(),100))}isFullscreen(){return document.fullscreenElement===this.container}getCurrentData(){if(this.currentStatistic){if(this.currentValues){if(this.choropleth){const t={},i=Array.isArray(this.currentValues)?this.currentValues:Object.entries(this.currentValues);for(const[r,n]of i){const o=this.choropleth.getFeatureName(r)||r;t[o]=n}return t}return this.currentValues}const e=Jt.find(t=>t.id===this.currentStatistic);if(e){const t={},i=this.choropleth?.getStatsMap();return i&&i.size>0?i.forEach((n,o)=>{const a=e.accessor(n),l=n.name||o;a!=null&&(t[l]=a)}):Ne.forEach(n=>{const o=e.accessor(n);o!=null&&(t[n.name||n.code]=o)}),t}}return{}}destroy(){this.isDestroyed=!0,this.animationId&&cancelAnimationFrame(this.animationId),window.removeEventListener("resize",this.handleResize),window.removeEventListener("keydown",this.handleKeydown),document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.categoryGUIs.forEach(e=>e.destroy()),this.categoryGUIs=[],this.legend?.dispose(),this.countryLabels?.dispose(),this.markerLayer?.dispose(),this.controls?.dispose(),this.toolbar?.dispose(),this.dataGrid?.dispose(),this.hoverTooltip&&this.hoverTooltip.parentNode&&(this.hoverTooltip.parentNode.removeChild(this.hoverTooltip),this.hoverTooltip=null),document.querySelectorAll(".lil-gui-tooltip").forEach(e=>e.remove()),this.globe?.geometry.dispose(),this.globe?.material?.dispose(),this.atmosphere?.geometry.dispose(),this.atmosphere?.material?.dispose(),this.stars?.geometry.dispose(),this.stars?.material?.dispose(),this.dataTexture?.dispose(),this.scene?.clear();try{this.renderer?.dispose(),this.renderer?.forceContextLoss(),this.renderer?.getContext()?.getExtension("WEBGL_lose_context")?.loseContext()}catch(e){console.warn("Error forcing context loss:",e)}this.renderer?.domElement&&this.container?.contains(this.renderer.domElement)&&this.container.removeChild(this.renderer.domElement),this.renderer=null,this.scene=null,this.camera=null}addTooltip(e,t){setTimeout(()=>{const i=e.domElement;if(!i)return;const r=i.closest(".lil-controller")||i.closest(".controller");if(!r)return;const n=r.querySelector(".lil-name")||r.querySelector(".name");if(!n||n.querySelector(".gralobe-help-icon"))return;const o=document.createElement("div");Object.assign(o.style,{display:"inline-flex",marginLeft:"auto",paddingLeft:"8px",cursor:"help",pointerEvents:"auto"});const a=document.createElement("div");a.className="gralobe-help-icon",a.innerText="?",Object.assign(a.style,{display:"flex",alignItems:"center",justifyContent:"center",width:"10px",height:"10px",borderRadius:"50%",background:"rgba(0, 180, 255, 0.15)",border:"1px solid rgba(0, 180, 255, 0.3)",color:"rgba(255, 255, 255, 0.8)",fontSize:"8px",fontWeight:"bold",transition:"all 0.2s ease"}),a.onmouseenter=()=>{a.style.background="rgba(0, 180, 255, 0.8)",a.style.boxShadow="0 0 10px rgba(0, 180, 255, 0.6)"},a.onmouseleave=()=>{a.style.background="rgba(0, 180, 255, 0.2)",a.style.boxShadow="none"},o.appendChild(a),n.appendChild(o);const l=u=>{const c=document.getElementById("gralobe-active-tooltip");c&&c.remove();const h=document.createElement("div");h.id="gralobe-active-tooltip",h.innerHTML=t,Object.assign(h.style,{position:"fixed",background:"rgba(10, 20, 30, 0.98)",color:"#fff",padding:"10px 14px",borderRadius:"6px",fontSize:"12px",lineHeight:"1.5",maxWidth:"250px",zIndex:"99",border:"1px solid rgba(0, 180, 255, 0.3)",boxShadow:"0 8px 32px rgba(0,0,0,0.8)",pointerEvents:"none",fontFamily:"system-ui",backdropFilter:"blur(8px)",left:u.clientX+15+"px",top:u.clientY+15+"px"}),document.body.appendChild(h);const p=m=>{h.style.left=m.clientX+15+"px",h.style.top=m.clientY+15+"px"},f=()=>{h.remove(),window.removeEventListener("mousemove",p),o.removeEventListener("mouseleave",f)};window.addEventListener("mousemove",p),o.addEventListener("mouseleave",f)};o.addEventListener("mouseenter",l)},500)}getStatisticMetadata(e){if(Lt[e]){const i=Lt[e],r=Jt.find(o=>o.id===e),n={};return r&&Ne.forEach(o=>{const a=r.accessor(o);a!=null&&(n[o.id]=a)}),{definition:i,values:n}}const t=Jt.find(i=>i.id===e);if(t){const i={};return Ne.forEach(r=>{const n=t.accessor(r);n!=null&&(i[r.id]=n)}),{definition:{id:t.id,name:t.name,unit:t.unit,description:t.description,colorScale:t.colorScale,domain:t.domain,format:t.format},values:i}}return null}}ee.BUILT_IN_STATISTICS=Lt,ee.GlobeViz=Xa,ee.WORLD_STATISTICS=Ne,ee.createFormatter=ei,ee.formatValue=kn,ee.normalizeCountryValues=Zi,ee.toNumericCode=Qt,Object.defineProperty(ee,Symbol.toStringTag,{value:"Module"})}));
|
|
1749
1749
|
//# sourceMappingURL=gralobe.umd.cjs.map
|