gralobe 1.0.38 → 1.0.40

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.
@@ -1264,5 +1264,5 @@ void main() {
1264
1264
  float alpha = intensity * 0.6 * pulse;
1265
1265
  gl_FragColor = vec4(glow, alpha);
1266
1266
  }
1267
- `,transparent:!0,blending:A.AdditiveBlending,side:A.BackSide,depthWrite:!1})}getGroup(){return this.group}setMarkers(e){this.markers=e,this.rebuild()}setConfig(e){this.config={...this.config,...e},e.color&&(this.markerMaterial.color.set(e.color),this.glowMaterial.uniforms.uColor.value.set(e.color)),e.opacity!==void 0&&(this.markerMaterial.opacity=e.opacity),e.pulseAnimation!==void 0&&(this.glowMaterial.uniforms.uPulse.value=e.pulseAnimation?1:0),this.rebuild()}setMorph(e){this.morph=e,this.updatePositions()}update(e){this.time=e,this.glowMaterial.uniforms.uTime.value=e}rebuild(){if(this.group.clear(),this.markerMeshes=[],this.glowMeshes=[],this.markers.length===0)return;const e=this.markers.map(i=>i.value),t=Math.max(...e,1);for(const i of this.markers){const n=i.value/t,r=this.createMarkerMesh(i,n);if(this.markerMeshes.push(r),this.group.add(r),this.config.style!=="dot"){const s=this.createGlowMesh(i,n);this.glowMeshes.push(s),this.group.add(s)}}this.updatePositions()}createMarkerMesh(e,t){let i;const n=2+t*this.config.maxHeight;switch(this.config.style){case"dot":i=new A.SphereGeometry(1+t*2,16,12);break;case"pin":i=new A.ConeGeometry(1.5,n,8);break;case"spike":default:i=new A.CylinderGeometry(.3,1.2,n,8);break}const r=e.color?new A.MeshBasicMaterial({color:new A.Color(e.color),transparent:!0,opacity:this.config.opacity}):this.markerMaterial,s=new A.Mesh(i,r);return s.userData={marker:e,height:n},s}createGlowMesh(e,t){const i=2+t*3,n=new A.SphereGeometry(i,16,12),r=e.color?new A.ShaderMaterial({...this.glowMaterial,uniforms:{...this.glowMaterial.uniforms,uColor:{value:new A.Color(e.color)}}}):this.glowMaterial,s=new A.Mesh(n,r);return s.userData={marker:e},s}updatePositions(){for(let e=0;e<this.markerMeshes.length;e++){const t=this.markerMeshes[e],i=t.userData.marker,n=t.userData.height,r=ro(i.lat,i.lng,ee),s=so(i.lat,i.lng),o=this.morph*this.morph*(3-2*this.morph);if(t.position.lerpVectors(s,r,o),o>.01){if(t.lookAt(t.position.clone().multiplyScalar(2)),this.config.style==="spike"||this.config.style==="pin"){t.rotateX(Math.PI/2);const l=r.clone().normalize().multiplyScalar(n/2);t.position.add(l.multiplyScalar(o))}}else t.rotation.set(-Math.PI/2,0,0),(this.config.style==="spike"||this.config.style==="pin")&&(t.position.z=n/2);this.glowMeshes[e]&&this.glowMeshes[e].position.copy(t.position)}}getMarkerAtPosition(e,t,i){e.setFromCamera(i,t);const n=e.intersectObjects(this.markerMeshes);return n.length>0?n[0].object.userData.marker:null}dispose(){this.group.clear(),this.markerMaterial.dispose(),this.glowMaterial.dispose();for(const e of this.markerMeshes)e.geometry.dispose(),e.material!==this.markerMaterial&&e.material.dispose();for(const e of this.glowMeshes)e.geometry.dispose(),e.material!==this.glowMaterial&&e.material.dispose();this.markerMeshes=[],this.glowMeshes=[]}}const Wt={lifeExpectancy:{id:"lifeExpectancy",name:"Life Expectancy",unit:"years",description:"Average life expectancy at birth",colorScale:["#feedde","#fdbe85","#d94701"],domain:[55,85],format:a=>`${a.toFixed(1)} years`},humanDevIndex:{id:"humanDevIndex",name:"Human Development Index",unit:"",description:"UN composite index of life expectancy, education, and income",colorScale:["#fee5d9","#fcae91","#cb181d"],domain:[.4,1],format:a=>a.toFixed(3)},gdpPerCapita:{id:"gdpPerCapita",name:"GDP per Capita (PPP)",unit:"$",description:"Purchasing power parity adjusted GDP per person",colorScale:["#edf8e9","#74c476","#006d2c"],domain:[1e3,8e4],format:a=>`$${(a/1e3).toFixed(1)}k`},co2Emissions:{id:"co2Emissions",name:"CO₂ Emissions",unit:"t/capita",description:"Carbon dioxide emissions per capita",colorScale:["#f7fbff","#6baed6","#08306b"],domain:[0,20],format:a=>`${a.toFixed(1)}t`},renewableEnergy:{id:"renewableEnergy",name:"Renewable Energy",unit:"%",description:"Share of renewable energy in total energy consumption",colorScale:["#f7fcf5","#74c476","#00441b"],domain:[0,100],format:a=>`${a.toFixed(0)}%`},internetUsers:{id:"internetUsers",name:"Internet Penetration",unit:"%",description:"Percentage of population using the internet",colorScale:["#f2f0f7","#9e9ac8","#54278f"],domain:[0,100],format:a=>`${a.toFixed(0)}%`},urbanPopulation:{id:"urbanPopulation",name:"Urbanization",unit:"%",description:"Percentage of population living in urban areas",colorScale:["#fff5eb","#fd8d3c","#7f2704"],domain:[15,100],format:a=>`${a.toFixed(0)}%`},healthExpenditure:{id:"healthExpenditure",name:"Health Spending",unit:"% GDP",description:"Total health expenditure as percentage of GDP",colorScale:["#fff5f0","#fb6a4a","#99000d"],domain:[2,18],format:a=>`${a.toFixed(1)}%`},forestArea:{id:"forestArea",name:"Forest Coverage",unit:"%",description:"Forest area as percentage of total land area",colorScale:["#f7fcf5","#41ab5d","#00441b"],domain:[0,75],format:a=>`${a.toFixed(0)}%`},population:{id:"population",name:"Population",unit:"millions",description:"Total population",colorScale:["#fff7bc","#fec44f","#d95f0e"],domain:[1,1500],format:a=>`${a.toFixed(0)}M`},accessElectricity:{id:"accessElectricity",name:"Electricity Access",unit:"%",description:"Percentage of population with access to electricity",colorScale:["#ffeda0","#feb24c","#f03b20"],domain:[20,100],format:a=>`${a.toFixed(0)}%`},educationExpenditure:{id:"educationExpenditure",name:"Education Spending",unit:"% GDP",description:"Government expenditure on education as percentage of GDP",colorScale:["#edf8fb","#7bccc4","#0868ac"],domain:[1,10],format:a=>`${a.toFixed(1)}%`}};Wt.lifeExpectancy;const lo="https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_10m_urban_areas.geojson";class ho{static urbanFeatures=null;static isLoading=!1;static loadPromise=null;static async loadBaseTopology(){return this.urbanFeatures?this.urbanFeatures:this.loadPromise?this.loadPromise:(this.isLoading=!0,this.loadPromise=(async()=>{try{const e=await fetch(lo);if(!e.ok)throw new Error("Failed to load urban areas");const t=await e.json();return this.urbanFeatures=t.features,this.urbanFeatures||[]}catch(e){return console.error("UrbanMapper load error:",e),[]}finally{this.isLoading=!1}})(),this.loadPromise)}static generateSyntheticBoundary(e,t,i=20){const r=[],o=i/6371*(180/Math.PI),l=o/Math.cos(e*Math.PI/180);for(let h=0;h<=16;h++){const c=h/16*2*Math.PI,u=e+o*Math.sin(c),f=t+l*Math.cos(c);r.push([f,u])}return{type:"Feature",id:`synthetic_${e.toFixed(4)}_${t.toFixed(4)}`,properties:{name:"Unknown City",featurecla:"Synthetic Urban Area"},geometry:{type:"Polygon",coordinates:[r]}}}static async mapPointsToTopology(e){const t=await this.loadBaseTopology(),i=[],n={},r=new Set;for(const s of e){let o=null;for(const l of t)if(this.isPointInFeature(s,l)){o=l;break}if(o){const l=o.properties.name_conve||o.properties.name||`ua_${Math.random()}`,h=r.has(l)?null:JSON.parse(JSON.stringify(o));h&&(h.id=l,i.push(h),r.add(l)),n[l]=(n[l]||0)+s.value}else{const l=this.generateSyntheticBoundary(s.lat,s.lon);s.id&&(l.id=s.id),i.push(l),n[l.id]=s.value}}return{features:i,statistics:n}}static isPointInFeature(e,t){const{lat:i,lon:n}=e,r=t.geometry;if(!r)return!1;const s=r.coordinates;if(r.type==="Polygon")return this.pointInPolygon([n,i],s);if(r.type==="MultiPolygon"){for(const o of s)if(this.pointInPolygon([n,i],o))return!0}return!1}static pointInPolygon(e,t){const i=e[0],n=e[1];let r=!1;const s=t[0];for(let o=0,l=s.length-1;o<s.length;l=o++){const h=s[o][0],c=s[o][1],u=s[l][0],f=s[l][1];c>n!=f>n&&i<(u-h)*(n-c)/(f-c)+h&&(r=!r)}return r}}const Or={satellite:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg",natural:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_day_4096.jpg",dark:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png",light:"https://raw.githubusercontent.com/turban/webgl-earth/master/images/2_no_clouds_4k.jpg",night:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png",topographic:"https://eoimages.gsfc.nasa.gov/images/imagerecords/74000/74117/world.topo.200407.3x5400x2700.jpg"},Ir={texture:"satellite",labels:"all",statistic:"lifeExpectancy",autoRotate:!1,initialView:"globe",showControls:!1,showLegend:!0,effects:{atmosphereIntensity:0,atmosphere:!1,clouds:!1,starTwinkle:!0},extrudeHeight:!1};class co{container;config;scene;camera;renderer;controls;globe=null;material=null;atmosphere=null;stars=null;gui=null;choropleth=null;legend=null;exporter=null;countryLabels=null;markerLayer=null;textureLoader=new A.TextureLoader;dataTexture=null;morph=0;currentStatistic=null;animationId=null;isDestroyed=!1;ready;resolveReady;constructor(e,t={}){if(typeof e=="string"){const i=document.querySelector(e);if(!i)throw new Error(`Container not found: ${e}`);this.container=i}else this.container=e;this.config={...Ir,...t,effects:{...Ir.effects,...t.effects}},this.ready=new Promise(i=>{this.resolveReady=i}),this.init()}async init(){const e=this.config.width||this.container.clientWidth||800,t=this.config.height||this.container.clientHeight||600;this.scene=new A.Scene,this.scene.background=new A.Color(2066),this.camera=new A.PerspectiveCamera(50,e/t,1,1e3),this.camera.position.set(0,0,this.config.initialView==="flat"?350:150),this.renderer=new A.WebGLRenderer({antialias:!0}),this.renderer.setSize(e,t),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.container.appendChild(this.renderer.domElement),this.controls=new aa(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.minDistance=10,this.controls.maxDistance=400;try{this.choropleth=new ht(this.config.topology,this.config.onLoadProgress,()=>{this.material&&this.material.uniforms.uDataTexture.value&&(this.material.uniforms.uDataTexture.value.needsUpdate=!0,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value===0&&K.to(this.material.uniforms.uDataOpacity,{value:.7,duration:1}))}),this.config.showLegend&&(this.legend=new qa(this.container)),await this.createGlobe(),this.createStars(),this.config.effects.atmosphere&&this.createAtmosphere(),this.countryLabels=new Da(this.container,ee),this.scene.add(this.countryLabels.getGroup()),this.globe&&this.countryLabels.setGlobe(this.globe),this.countryLabels.setCamera(this.camera),this.countryLabels.setStyle(this.config.labels),this.exporter=new Za(this.renderer,this.scene,this.camera),this.legend&&this.exporter.setLegendElement(this.legend.getElement()),this.countryLabels&&this.exporter.setCountryLabels(this.countryLabels),this.config.showControls&&this.createGUI(),this.setupInteraction(),await this.choropleth.waitForLoad(),this.setStatistic(this.config.statistic)}catch(i){console.error("GlobeViz init failed:",i)}this.morph=this.config.initialView==="globe"?1:0,this.material&&(this.material.uniforms.uMorph.value=this.morph),this.countryLabels?.setMorph(this.morph),window.addEventListener("resize",this.handleResize),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.renderer.domElement.tabIndex=0,this.renderer.domElement.style.outline="none",this.renderer.domElement.addEventListener("mousedown",()=>{this.renderer.domElement.focus()}),this.renderer.domElement.addEventListener("keydown",this.handleKeydown),this.animate(),this.resolveReady()}handleKeydown=e=>{this.isDestroyed||document.activeElement===this.renderer.domElement&&((e.key==="g"||e.key==="G")&&(this.morph>.5?this.toFlat():this.toGlobe()),(e.key==="f"||e.key==="F")&&this.toggleFullscreen())};async createGlobe(){const e=await this.textureLoader.loadAsync(Or[this.config.texture]);e.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),e.minFilter=A.LinearMipmapLinearFilter,e.magFilter=A.LinearFilter;const t=document.createElement("canvas");t.width=2048,t.height=1024,this.dataTexture=new A.CanvasTexture(t);const i=new A.PlaneGeometry(Math.PI*2*ee,Math.PI*ee,256,128);this.material=new A.ShaderMaterial({vertexShader:Qa,fragmentShader:Ja,uniforms:{uMorph:{value:0},uTime:{value:0},uParchment:{value:0},uExtremeParchment:{value:0},uTransitionEffect:{value:0},uTexture:{value:e},uDataTexture:{value:this.dataTexture},uCloudTexture:{value:null},uNightTexture:{value:null},uDataOpacity:{value:0},uDataOverlay:{value:0},uExtrudeHeight:{value:this.config.extrudeHeight?1:0},uSunDir:{value:new A.Vector3(1,.5,1).normalize()},uClouds:{value:this.config.effects.clouds?1:0},uCloudSpeed:{value:this.config.effects.cloudSpeed||1},uCloudOpacity:{value:this.config.effects.cloudOpacity||.6},uAtmosphereIntensity:{value:this.config.effects.atmosphereIntensity||0},uAurora:{value:this.config.effects.aurora?1:0},uAuroraIntensity:{value:1},uCityLights:{value:this.config.effects.cityLights?1:0},uCityLightsIntensity:{value:1},uOceanSpecular:{value:this.config.effects.oceanSpecular?1:0},uSpecularIntensity:{value:1},uSunGlow:{value:0},uGridLines:{value:this.config.effects.gridLines?1:0},uGridOpacity:{value:this.config.effects.gridOpacity||.5},uScanEffect:{value:0},uScanSpeed:{value:1},uHologram:{value:this.config.effects.hologramMode?1:0},uHologramColor:{value:new A.Color(65535)},uVintage:{value:this.config.effects.vintageMode?1:0},uThermal:{value:this.config.effects.thermalMode?1:0},uBlueprint:{value:this.config.effects.blueprintMode?1:0},uGlowPulse:{value:this.config.effects.glowPulse?1:0},uGlowColor:{value:new A.Color(4491519)}},side:A.DoubleSide}),this.globe=new A.Mesh(i,this.material),this.scene.add(this.globe)}createAtmosphere(){const e=new A.PlaneGeometry(Math.PI*2*ee*1.15,Math.PI*ee*1.15,128,64),t=new A.ShaderMaterial({vertexShader:eo,fragmentShader:to,uniforms:{uMorph:{value:0},uOpacity:{value:1}},side:A.BackSide,transparent:!0,blending:A.AdditiveBlending,depthWrite:!1});this.atmosphere=new A.Mesh(e,t),this.scene.add(this.atmosphere)}createStars(){const t=new A.BufferGeometry,i=new Float32Array(3e3*3),n=new Float32Array(3e3),r=new Float32Array(3e3);for(let o=0;o<3e3;o++){const l=300+Math.random()*300,h=Math.random()*Math.PI*2,c=Math.acos(2*Math.random()-1);i[o*3]=l*Math.sin(c)*Math.cos(h),i[o*3+1]=l*Math.sin(c)*Math.sin(h),i[o*3+2]=l*Math.cos(c),n[o]=.5+Math.random()*1.5,r[o]=Math.random()*Math.PI*2}t.setAttribute("position",new A.BufferAttribute(i,3)),t.setAttribute("aSize",new A.BufferAttribute(n,1)),t.setAttribute("aPhase",new A.BufferAttribute(r,1));const s=new A.ShaderMaterial({vertexShader:io,fragmentShader:no,uniforms:{uTime:{value:0},uTwinkle:{value:this.config.effects.starTwinkle?1:0},uOpacity:{value:1}},transparent:!0,blending:A.AdditiveBlending,depthWrite:!1});this.stars=new A.Points(t,s),this.scene.add(this.stars)}createGUI(){getComputedStyle(this.container).position==="static"&&(this.container.style.position="relative"),this.gui=new zi({container:this.container,title:"⚙ Controls",width:220,closeFolders:!0});const t=this.gui.domElement;t.style.position="absolute",t.style.top="8px",t.style.right="8px",t.style.zIndex="100",this.gui.close();const i=this.gui.addFolder("View");i.add({toGlobe:()=>this.toGlobe()},"toGlobe").name("→ Globe"),i.add({toFlat:()=>this.toFlat()},"toFlat").name("→ Flat"),i.add({morph:this.morph},"morph",0,1).name("Morph").onChange(l=>this.setMorph(l));const n=this.gui.addFolder("Statistics"),r=Object.keys(Wt);n.add({stat:this.config.statistic},"stat",r).name("Statistic").onChange(l=>this.setStatistic(l));const s=["none","minimal","major","all"];this.gui.addFolder("Display").add({labels:this.config.labels},"labels",s).name("Labels").onChange(l=>this.setLabels(l)),this.gui.add(this.config,"autoRotate").name("Auto Rotate"),this.gui.addFolder("Export").add({screenshot:()=>this.screenshot({width:1920,height:1080})},"screenshot").name("📷 Screenshot")}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:A.MOUSE.ROTATE,MIDDLE:A.MOUSE.DOLLY,RIGHT:A.MOUSE.PAN};let e=0;const t=this.choropleth?.getBounds();if(t){const[i,n,r,s]=t;e=-((i+r)/2)*(Math.PI/180)}K.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)}}),K.to(this.camera.position,{x:0,y:0,z:200,duration:2.5,ease:"power2.inOut"}),K.to(this.controls.target,{x:0,y:0,z:0,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.controls.update()}}),this.globe&&K.to(this.globe.rotation,{y:e,x:0,z:0,duration:2.5,ease:"power2.inOut"}),this.stars&&K.to(this.stars.material.uniforms.uOpacity,{value:1,duration:1}),this.atmosphere&&K.to(this.atmosphere.material.uniforms.uOpacity,{value:1,duration:1})}toFlat(){const e=this.choropleth?.getBounds(),t=Math.PI*2*ee,i=Math.PI*ee;let n=0,r=0,s=t,o=i;if(e){const[g,p,d,m]=e,_=g/180*(t/2),y=d/180*(t/2),b=p/90*(i/2),x=m/90*(i/2);n=(_+y)/2,r=(b+x)/2,s=(y-_)*1.2,o=(x-b)*1.2}const l=this.camera.fov*Math.PI/180,h=this.camera.aspect,c=o/2/Math.tan(l/2),u=s/(2*Math.tan(l/2)*h),f=Math.max(c,u);this.controls.enabled=!1,K.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(n,r,0),this.controls.update()}}),this.globe&&K.to(this.globe.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),this.atmosphere&&K.to(this.atmosphere.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),K.to(this.camera.position,{x:n,y:r,z:f,duration:2,ease:"power3.inOut"}),K.to(this.controls.target,{x:n,y:r,z:0,duration:2,ease:"power3.inOut"}),K.to(this.camera.up,{x:0,y:1,z:0,duration:2,ease:"power3.inOut"}),this.controls.screenSpacePanning=!0,this.controls.mouseButtons={LEFT:A.MOUSE.PAN,MIDDLE:A.MOUSE.DOLLY,RIGHT:A.MOUSE.ROTATE},this.stars&&K.to(this.stars.material.uniforms.uOpacity,{value:0,duration:1}),this.atmosphere&&K.to(this.atmosphere.material.uniforms.uOpacity,{value:0,duration:1})}setupInteraction(){const e=new A.Raycaster,t=new A.Vector2,i=new A.Plane(new A.Vector3(0,0,1),0),n=new A.Vector3;let r=!1,s=new Date().getTime();this.renderer.domElement.addEventListener("mousedown",()=>{r=!1,s=new Date().getTime()}),this.renderer.domElement.addEventListener("mousemove",()=>{r=!0}),this.renderer.domElement.addEventListener("click",o=>{if(r&&new Date().getTime()-s>200)return;const l=this.renderer.domElement.getBoundingClientRect();if(t.x=(o.clientX-l.left)/l.width*2-1,t.y=-((o.clientY-l.top)/l.height)*2+1,this.morph<.1&&(e.setFromCamera(t,this.camera),e.ray.intersectPlane(i,n),n)){const h=Math.PI*ee,c=Math.PI*ee/2;Math.abs(n.x)<=h&&Math.abs(n.y)<=c&&(K.to(this.controls.target,{x:n.x,y:n.y,z:0,duration:1.5,ease:"power2.inOut"}),K.to(this.camera.position,{x:n.x,y:n.y,z:50,duration:1.5,ease:"power2.inOut"}))}}),this.renderer.domElement.addEventListener("dblclick",()=>{this.morph<.1&&this.toFlat()})}setMorph(e){this.morph=Math.max(0,Math.min(1,e)),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)}getMorph(){return this.morph}setStatistic(e){if(typeof e=="string"){if(!Wt[e]){console.warn(`Unknown statistic: ${e}`);return}this.currentStatistic=e;const i=ba.find(n=>n.id===e);if(i&&this.choropleth){const n=this.choropleth.renderTexture(i);if(this.material&&n){const r=new A.CanvasTexture(n);r.needsUpdate=!0,this.material.uniforms.uDataTexture.value=r,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value=.7}}if(this.legend&&i&&this.legend.show(i),this.countryLabels&&this.choropleth){const n=this.choropleth.getStatsMap(),r=n?Array.from(n.keys()):[];this.countryLabels.setDataIds(r)}}else{const t=e;if(this.currentStatistic=t.definition.id,this.choropleth){const i=this.choropleth.renderCustomTexture(t.values,t.definition.colorScale,t.definition.domain);if(this.material&&i){const n=new A.CanvasTexture(i);n.needsUpdate=!0,this.material.uniforms.uDataTexture.value=n,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value=.7}}if(this.legend&&this.legend.show(t.definition),this.countryLabels){const i=t.values instanceof Map?Object.fromEntries(t.values):t.values;this.countryLabels.setDataIds(Object.keys(i))}}}setLabels(e){this.countryLabels?.setStyle(e)}addCustomLabels(e){this.countryLabels?.addCustomLabels(e)}clearCustomLabels(){this.countryLabels?.clearCustomLabels()}async setTexture(e){const t=Or[e];if(!(!t||!this.material))try{const i=await this.textureLoader.loadAsync(t);i.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),i.minFilter=A.LinearMipmapLinearFilter,i.magFilter=A.LinearFilter,this.material.uniforms.uTexture.value=i}catch(i){console.error("Failed to load texture:",e,i)}}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,n=t*i;this.exporter.startGifCapture(e);for(let r=0;r<n;r++)this.exporter.captureGifFrame(),await new Promise(s=>setTimeout(s,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))}setMarkers(e,t){this.markerLayer?t&&this.markerLayer.setConfig(t):(this.markerLayer=new oo(t),this.scene.add(this.markerLayer.getGroup()),this.markerLayer.setMorph(this.morph)),this.markerLayer.setMarkers(e)}async setUrbanData(e){if(!this.choropleth)return;const t=await ho.mapPointsToTopology(e);this.choropleth.setFeatures(t.features);const i=["#ffffb2","#fd8d3c","#bd0026"],n=Object.values(t.statistics),r=Math.max(...n,1);this.choropleth.renderCustomTexture(t.statistics,i,[0,r]),this.material&&(this.material.uniforms.uCityLights.value=1,this.config.effects.cityLights=!0),this.countryLabels&&(this.countryLabels.getGroup().visible=!1)}resize(e,t){this.config.width=e,this.config.height=t,this.handleResize()}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}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.gui?.destroy(),this.legend?.dispose(),this.countryLabels?.dispose(),this.markerLayer?.dispose(),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.renderer.dispose(),this.container.removeChild(this.renderer.domElement)}}le.BUILT_IN_STATISTICS=Wt,le.GlobeViz=co,le.WORLD_STATISTICS=$i,le.createFormatter=Hi,le.formatValue=Ka,le.normalizeCountryValues=fr,le.toNumericCode=Vi,Object.defineProperty(le,Symbol.toStringTag,{value:"Module"})}));
1267
+ `,transparent:!0,blending:A.AdditiveBlending,side:A.BackSide,depthWrite:!1})}getGroup(){return this.group}setMarkers(e){this.markers=e,this.rebuild()}setConfig(e){this.config={...this.config,...e},e.color&&(this.markerMaterial.color.set(e.color),this.glowMaterial.uniforms.uColor.value.set(e.color)),e.opacity!==void 0&&(this.markerMaterial.opacity=e.opacity),e.pulseAnimation!==void 0&&(this.glowMaterial.uniforms.uPulse.value=e.pulseAnimation?1:0),this.rebuild()}setMorph(e){this.morph=e,this.updatePositions()}update(e){this.time=e,this.glowMaterial.uniforms.uTime.value=e}rebuild(){if(this.group.clear(),this.markerMeshes=[],this.glowMeshes=[],this.markers.length===0)return;const e=this.markers.map(i=>i.value),t=Math.max(...e,1);for(const i of this.markers){const n=i.value/t,r=this.createMarkerMesh(i,n);if(this.markerMeshes.push(r),this.group.add(r),this.config.style!=="dot"){const s=this.createGlowMesh(i,n);this.glowMeshes.push(s),this.group.add(s)}}this.updatePositions()}createMarkerMesh(e,t){let i;const n=2+t*this.config.maxHeight;switch(this.config.style){case"dot":i=new A.SphereGeometry(1+t*2,16,12);break;case"pin":i=new A.ConeGeometry(1.5,n,8);break;case"spike":default:i=new A.CylinderGeometry(.3,1.2,n,8);break}const r=e.color?new A.MeshBasicMaterial({color:new A.Color(e.color),transparent:!0,opacity:this.config.opacity}):this.markerMaterial,s=new A.Mesh(i,r);return s.userData={marker:e,height:n},s}createGlowMesh(e,t){const i=2+t*3,n=new A.SphereGeometry(i,16,12),r=e.color?new A.ShaderMaterial({...this.glowMaterial,uniforms:{...this.glowMaterial.uniforms,uColor:{value:new A.Color(e.color)}}}):this.glowMaterial,s=new A.Mesh(n,r);return s.userData={marker:e},s}updatePositions(){for(let e=0;e<this.markerMeshes.length;e++){const t=this.markerMeshes[e],i=t.userData.marker,n=t.userData.height,r=ro(i.lat,i.lng,ee),s=so(i.lat,i.lng),o=this.morph*this.morph*(3-2*this.morph);if(t.position.lerpVectors(s,r,o),o>.01){if(t.lookAt(t.position.clone().multiplyScalar(2)),this.config.style==="spike"||this.config.style==="pin"){t.rotateX(Math.PI/2);const l=r.clone().normalize().multiplyScalar(n/2);t.position.add(l.multiplyScalar(o))}}else t.rotation.set(-Math.PI/2,0,0),(this.config.style==="spike"||this.config.style==="pin")&&(t.position.z=n/2);this.glowMeshes[e]&&this.glowMeshes[e].position.copy(t.position)}}getMarkerAtPosition(e,t,i){e.setFromCamera(i,t);const n=e.intersectObjects(this.markerMeshes);return n.length>0?n[0].object.userData.marker:null}dispose(){this.group.clear(),this.markerMaterial.dispose(),this.glowMaterial.dispose();for(const e of this.markerMeshes)e.geometry.dispose(),e.material!==this.markerMaterial&&e.material.dispose();for(const e of this.glowMeshes)e.geometry.dispose(),e.material!==this.glowMaterial&&e.material.dispose();this.markerMeshes=[],this.glowMeshes=[]}}const Wt={lifeExpectancy:{id:"lifeExpectancy",name:"Life Expectancy",unit:"years",description:"Average life expectancy at birth",colorScale:["#feedde","#fdbe85","#d94701"],domain:[55,85],format:a=>`${a.toFixed(1)} years`},humanDevIndex:{id:"humanDevIndex",name:"Human Development Index",unit:"",description:"UN composite index of life expectancy, education, and income",colorScale:["#fee5d9","#fcae91","#cb181d"],domain:[.4,1],format:a=>a.toFixed(3)},gdpPerCapita:{id:"gdpPerCapita",name:"GDP per Capita (PPP)",unit:"$",description:"Purchasing power parity adjusted GDP per person",colorScale:["#edf8e9","#74c476","#006d2c"],domain:[1e3,8e4],format:a=>`$${(a/1e3).toFixed(1)}k`},co2Emissions:{id:"co2Emissions",name:"CO₂ Emissions",unit:"t/capita",description:"Carbon dioxide emissions per capita",colorScale:["#f7fbff","#6baed6","#08306b"],domain:[0,20],format:a=>`${a.toFixed(1)}t`},renewableEnergy:{id:"renewableEnergy",name:"Renewable Energy",unit:"%",description:"Share of renewable energy in total energy consumption",colorScale:["#f7fcf5","#74c476","#00441b"],domain:[0,100],format:a=>`${a.toFixed(0)}%`},internetUsers:{id:"internetUsers",name:"Internet Penetration",unit:"%",description:"Percentage of population using the internet",colorScale:["#f2f0f7","#9e9ac8","#54278f"],domain:[0,100],format:a=>`${a.toFixed(0)}%`},urbanPopulation:{id:"urbanPopulation",name:"Urbanization",unit:"%",description:"Percentage of population living in urban areas",colorScale:["#fff5eb","#fd8d3c","#7f2704"],domain:[15,100],format:a=>`${a.toFixed(0)}%`},healthExpenditure:{id:"healthExpenditure",name:"Health Spending",unit:"% GDP",description:"Total health expenditure as percentage of GDP",colorScale:["#fff5f0","#fb6a4a","#99000d"],domain:[2,18],format:a=>`${a.toFixed(1)}%`},forestArea:{id:"forestArea",name:"Forest Coverage",unit:"%",description:"Forest area as percentage of total land area",colorScale:["#f7fcf5","#41ab5d","#00441b"],domain:[0,75],format:a=>`${a.toFixed(0)}%`},population:{id:"population",name:"Population",unit:"millions",description:"Total population",colorScale:["#fff7bc","#fec44f","#d95f0e"],domain:[1,1500],format:a=>`${a.toFixed(0)}M`},accessElectricity:{id:"accessElectricity",name:"Electricity Access",unit:"%",description:"Percentage of population with access to electricity",colorScale:["#ffeda0","#feb24c","#f03b20"],domain:[20,100],format:a=>`${a.toFixed(0)}%`},educationExpenditure:{id:"educationExpenditure",name:"Education Spending",unit:"% GDP",description:"Government expenditure on education as percentage of GDP",colorScale:["#edf8fb","#7bccc4","#0868ac"],domain:[1,10],format:a=>`${a.toFixed(1)}%`}};Wt.lifeExpectancy;const lo="https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_10m_urban_areas.geojson";class ho{static urbanFeatures=null;static isLoading=!1;static loadPromise=null;static async loadBaseTopology(){return this.urbanFeatures?this.urbanFeatures:this.loadPromise?this.loadPromise:(this.isLoading=!0,this.loadPromise=(async()=>{try{const e=await fetch(lo);if(!e.ok)throw new Error("Failed to load urban areas");const t=await e.json();return this.urbanFeatures=t.features,this.urbanFeatures||[]}catch(e){return console.error("UrbanMapper load error:",e),[]}finally{this.isLoading=!1}})(),this.loadPromise)}static generateSyntheticBoundary(e,t,i=20){const r=[],o=i/6371*(180/Math.PI),l=o/Math.cos(e*Math.PI/180);for(let h=0;h<=16;h++){const c=h/16*2*Math.PI,u=e+o*Math.sin(c),f=t+l*Math.cos(c);r.push([f,u])}return{type:"Feature",id:`synthetic_${e.toFixed(4)}_${t.toFixed(4)}`,properties:{name:"Unknown City",featurecla:"Synthetic Urban Area"},geometry:{type:"Polygon",coordinates:[r]}}}static async mapPointsToTopology(e){const t=await this.loadBaseTopology(),i=[],n={},r=new Set;for(const s of e){let o=null;for(const l of t)if(this.isPointInFeature(s,l)){o=l;break}if(o){const l=o.properties.name_conve||o.properties.name||`ua_${Math.random()}`,h=r.has(l)?null:JSON.parse(JSON.stringify(o));h&&(h.id=l,i.push(h),r.add(l)),n[l]=(n[l]||0)+s.value}else{const l=this.generateSyntheticBoundary(s.lat,s.lon);s.id&&(l.id=s.id),i.push(l),n[l.id]=s.value}}return{features:i,statistics:n}}static isPointInFeature(e,t){const{lat:i,lon:n}=e,r=t.geometry;if(!r)return!1;const s=r.coordinates;if(r.type==="Polygon")return this.pointInPolygon([n,i],s);if(r.type==="MultiPolygon"){for(const o of s)if(this.pointInPolygon([n,i],o))return!0}return!1}static pointInPolygon(e,t){const i=e[0],n=e[1];let r=!1;const s=t[0];for(let o=0,l=s.length-1;o<s.length;l=o++){const h=s[o][0],c=s[o][1],u=s[l][0],f=s[l][1];c>n!=f>n&&i<(u-h)*(n-c)/(f-c)+h&&(r=!r)}return r}}const Or={satellite:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg",natural:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_day_4096.jpg",dark:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png",light:"https://raw.githubusercontent.com/turban/webgl-earth/master/images/2_no_clouds_4k.jpg",night:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png",topographic:"https://eoimages.gsfc.nasa.gov/images/imagerecords/74000/74117/world.topo.200407.3x5400x2700.jpg"},Ir={texture:"satellite",labels:"all",statistic:"lifeExpectancy",autoRotate:!1,initialView:"globe",showControls:!1,showLegend:!0,effects:{atmosphereIntensity:0,atmosphere:!1,clouds:!1,starTwinkle:!0},extrudeHeight:!1};class co{container;config;scene;camera;renderer;controls;globe=null;material=null;atmosphere=null;stars=null;gui=null;choropleth=null;legend=null;exporter=null;countryLabels=null;markerLayer=null;textureLoader=new A.TextureLoader;dataTexture=null;morph=0;currentStatistic=null;animationId=null;isDestroyed=!1;ready;resolveReady;constructor(e,t={}){if(typeof e=="string"){const i=document.querySelector(e);if(!i)throw new Error(`Container not found: ${e}`);this.container=i}else this.container=e;this.config={...Ir,...t,effects:{...Ir.effects,...t.effects}},this.ready=new Promise(i=>{this.resolveReady=i}),this.init()}async init(){const e=this.config.width||this.container.clientWidth||800,t=this.config.height||this.container.clientHeight||600;this.scene=new A.Scene,this.scene.background=new A.Color(2066),this.camera=new A.PerspectiveCamera(50,e/t,1,1e3),this.camera.position.set(0,0,this.config.initialView==="flat"?350:150),this.renderer=new A.WebGLRenderer({antialias:!0}),this.renderer.setSize(e,t),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.container.appendChild(this.renderer.domElement),this.controls=new aa(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.minDistance=10,this.controls.maxDistance=400;try{this.choropleth=new ht(this.config.topology,this.config.onLoadProgress,()=>{this.material&&this.material.uniforms.uDataTexture.value&&(this.material.uniforms.uDataTexture.value.needsUpdate=!0,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value===0&&K.to(this.material.uniforms.uDataOpacity,{value:.7,duration:1}))}),this.config.showLegend&&(this.legend=new qa(this.container)),await this.createGlobe(),this.createStars(),this.config.effects.atmosphere&&this.createAtmosphere(),this.countryLabels=new Da(this.container,ee),this.scene.add(this.countryLabels.getGroup()),this.globe&&this.countryLabels.setGlobe(this.globe),this.countryLabels.setCamera(this.camera),this.countryLabels.setStyle(this.config.labels),this.exporter=new Za(this.renderer,this.scene,this.camera),this.legend&&this.exporter.setLegendElement(this.legend.getElement()),this.countryLabels&&this.exporter.setCountryLabels(this.countryLabels),this.config.showControls&&this.createGUI(),this.setupInteraction(),await this.choropleth.waitForLoad(),this.setStatistic(this.config.statistic)}catch(i){console.error("GlobeViz init failed:",i)}this.morph=this.config.initialView==="globe"?1:0,this.material&&(this.material.uniforms.uMorph.value=this.morph),this.countryLabels?.setMorph(this.morph),window.addEventListener("resize",this.handleResize),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.renderer.domElement.tabIndex=0,this.renderer.domElement.style.outline="none",this.renderer.domElement.addEventListener("mousedown",()=>{this.renderer.domElement.focus()}),this.renderer.domElement.addEventListener("keydown",this.handleKeydown),this.animate(),this.resolveReady()}handleKeydown=e=>{this.isDestroyed||document.activeElement===this.renderer.domElement&&((e.key==="g"||e.key==="G")&&(this.morph>.5?this.toFlat():this.toGlobe()),(e.key==="f"||e.key==="F")&&this.toggleFullscreen())};async createGlobe(){const e=await this.textureLoader.loadAsync(Or[this.config.texture]);e.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),e.minFilter=A.LinearMipmapLinearFilter,e.magFilter=A.LinearFilter;const t=document.createElement("canvas");t.width=2048,t.height=1024,this.dataTexture=new A.CanvasTexture(t);const i=new A.PlaneGeometry(Math.PI*2*ee,Math.PI*ee,256,128);this.material=new A.ShaderMaterial({vertexShader:Qa,fragmentShader:Ja,uniforms:{uMorph:{value:0},uTime:{value:0},uParchment:{value:0},uExtremeParchment:{value:0},uTransitionEffect:{value:0},uTexture:{value:e},uDataTexture:{value:this.dataTexture},uCloudTexture:{value:null},uNightTexture:{value:null},uDataOpacity:{value:0},uDataOverlay:{value:0},uExtrudeHeight:{value:this.config.extrudeHeight?1:0},uSunDir:{value:new A.Vector3(1,.5,1).normalize()},uClouds:{value:this.config.effects.clouds?1:0},uCloudSpeed:{value:this.config.effects.cloudSpeed||1},uCloudOpacity:{value:this.config.effects.cloudOpacity||.6},uAtmosphereIntensity:{value:this.config.effects.atmosphereIntensity||0},uAurora:{value:this.config.effects.aurora?1:0},uAuroraIntensity:{value:1},uCityLights:{value:this.config.effects.cityLights?1:0},uCityLightsIntensity:{value:1},uOceanSpecular:{value:this.config.effects.oceanSpecular?1:0},uSpecularIntensity:{value:1},uSunGlow:{value:0},uGridLines:{value:this.config.effects.gridLines?1:0},uGridOpacity:{value:this.config.effects.gridOpacity||.5},uScanEffect:{value:0},uScanSpeed:{value:1},uHologram:{value:this.config.effects.hologramMode?1:0},uHologramColor:{value:new A.Color(65535)},uVintage:{value:this.config.effects.vintageMode?1:0},uThermal:{value:this.config.effects.thermalMode?1:0},uBlueprint:{value:this.config.effects.blueprintMode?1:0},uGlowPulse:{value:this.config.effects.glowPulse?1:0},uGlowColor:{value:new A.Color(4491519)}},side:A.DoubleSide}),this.globe=new A.Mesh(i,this.material),this.scene.add(this.globe)}createAtmosphere(){const e=new A.PlaneGeometry(Math.PI*2*ee*1.15,Math.PI*ee*1.15,128,64),t=new A.ShaderMaterial({vertexShader:eo,fragmentShader:to,uniforms:{uMorph:{value:0},uOpacity:{value:1}},side:A.BackSide,transparent:!0,blending:A.AdditiveBlending,depthWrite:!1});this.atmosphere=new A.Mesh(e,t),this.scene.add(this.atmosphere)}createStars(){const t=new A.BufferGeometry,i=new Float32Array(3e3*3),n=new Float32Array(3e3),r=new Float32Array(3e3);for(let o=0;o<3e3;o++){const l=300+Math.random()*300,h=Math.random()*Math.PI*2,c=Math.acos(2*Math.random()-1);i[o*3]=l*Math.sin(c)*Math.cos(h),i[o*3+1]=l*Math.sin(c)*Math.sin(h),i[o*3+2]=l*Math.cos(c),n[o]=.5+Math.random()*1.5,r[o]=Math.random()*Math.PI*2}t.setAttribute("position",new A.BufferAttribute(i,3)),t.setAttribute("aSize",new A.BufferAttribute(n,1)),t.setAttribute("aPhase",new A.BufferAttribute(r,1));const s=new A.ShaderMaterial({vertexShader:io,fragmentShader:no,uniforms:{uTime:{value:0},uTwinkle:{value:this.config.effects.starTwinkle?1:0},uOpacity:{value:1}},transparent:!0,blending:A.AdditiveBlending,depthWrite:!1});this.stars=new A.Points(t,s),this.scene.add(this.stars)}createGUI(){getComputedStyle(this.container).position==="static"&&(this.container.style.position="relative"),this.gui=new zi({container:this.container,title:"⚙ Controls",width:220,closeFolders:!0});const t=this.gui.domElement;t.style.position="absolute",t.style.top="8px",t.style.right="8px",t.style.zIndex="100",this.gui.close();const i=this.gui.addFolder("View");i.add({toGlobe:()=>this.toGlobe()},"toGlobe").name("→ Globe"),i.add({toFlat:()=>this.toFlat()},"toFlat").name("→ Flat"),i.add({morph:this.morph},"morph",0,1).name("Morph").onChange(l=>this.setMorph(l));const n=this.gui.addFolder("Statistics"),r=Object.keys(Wt);n.add({stat:this.config.statistic},"stat",r).name("Statistic").onChange(l=>this.setStatistic(l));const s=["none","minimal","major","all","data"];this.gui.addFolder("Display").add({labels:this.config.labels},"labels",s).name("Labels").onChange(l=>this.setLabels(l)),this.gui.add(this.config,"autoRotate").name("Auto Rotate"),this.gui.addFolder("Export").add({screenshot:()=>this.screenshot({width:1920,height:1080})},"screenshot").name("📷 Screenshot")}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:A.MOUSE.ROTATE,MIDDLE:A.MOUSE.DOLLY,RIGHT:A.MOUSE.PAN};let e=0;const t=this.choropleth?.getBounds();if(t){const[i,n,r,s]=t;e=-((i+r)/2)*(Math.PI/180)}K.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)}}),K.to(this.camera.position,{x:0,y:0,z:200,duration:2.5,ease:"power2.inOut"}),K.to(this.controls.target,{x:0,y:0,z:0,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.controls.update()}}),this.globe&&K.to(this.globe.rotation,{y:e,x:0,z:0,duration:2.5,ease:"power2.inOut"}),this.stars&&K.to(this.stars.material.uniforms.uOpacity,{value:1,duration:1}),this.atmosphere&&K.to(this.atmosphere.material.uniforms.uOpacity,{value:1,duration:1})}toFlat(){const e=this.choropleth?.getBounds(),t=Math.PI*2*ee,i=Math.PI*ee;let n=0,r=0,s=t,o=i;if(e){const[g,p,d,m]=e,_=g/180*(t/2),y=d/180*(t/2),b=p/90*(i/2),x=m/90*(i/2);n=(_+y)/2,r=(b+x)/2,s=(y-_)*1.2,o=(x-b)*1.2}const l=this.camera.fov*Math.PI/180,h=this.camera.aspect,c=o/2/Math.tan(l/2),u=s/(2*Math.tan(l/2)*h),f=Math.max(c,u);this.controls.enabled=!1,K.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(n,r,0),this.controls.update()}}),this.globe&&K.to(this.globe.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),this.atmosphere&&K.to(this.atmosphere.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),K.to(this.camera.position,{x:n,y:r,z:f,duration:2,ease:"power3.inOut"}),K.to(this.controls.target,{x:n,y:r,z:0,duration:2,ease:"power3.inOut"}),K.to(this.camera.up,{x:0,y:1,z:0,duration:2,ease:"power3.inOut"}),this.controls.screenSpacePanning=!0,this.controls.mouseButtons={LEFT:A.MOUSE.PAN,MIDDLE:A.MOUSE.DOLLY,RIGHT:A.MOUSE.ROTATE},this.stars&&K.to(this.stars.material.uniforms.uOpacity,{value:0,duration:1}),this.atmosphere&&K.to(this.atmosphere.material.uniforms.uOpacity,{value:0,duration:1})}setupInteraction(){const e=new A.Raycaster,t=new A.Vector2,i=new A.Plane(new A.Vector3(0,0,1),0),n=new A.Vector3;let r=!1,s=new Date().getTime();this.renderer.domElement.addEventListener("mousedown",()=>{r=!1,s=new Date().getTime()}),this.renderer.domElement.addEventListener("mousemove",()=>{r=!0}),this.renderer.domElement.addEventListener("click",o=>{if(r&&new Date().getTime()-s>200)return;const l=this.renderer.domElement.getBoundingClientRect();if(t.x=(o.clientX-l.left)/l.width*2-1,t.y=-((o.clientY-l.top)/l.height)*2+1,this.morph<.1&&(e.setFromCamera(t,this.camera),e.ray.intersectPlane(i,n),n)){const h=Math.PI*ee,c=Math.PI*ee/2;Math.abs(n.x)<=h&&Math.abs(n.y)<=c&&(K.to(this.controls.target,{x:n.x,y:n.y,z:0,duration:1.5,ease:"power2.inOut"}),K.to(this.camera.position,{x:n.x,y:n.y,z:50,duration:1.5,ease:"power2.inOut"}))}}),this.renderer.domElement.addEventListener("dblclick",()=>{this.morph<.1&&this.toFlat()})}setMorph(e){this.morph=Math.max(0,Math.min(1,e)),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)}getMorph(){return this.morph}setStatistic(e){if(typeof e=="string"){if(!Wt[e]){console.warn(`Unknown statistic: ${e}`);return}this.currentStatistic=e;const i=ba.find(n=>n.id===e);if(i&&this.choropleth){const n=this.choropleth.renderTexture(i);if(this.material&&n){const r=new A.CanvasTexture(n);r.needsUpdate=!0,this.material.uniforms.uDataTexture.value=r,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value=.7}}if(this.legend&&i&&this.legend.show(i),this.countryLabels&&this.choropleth){const n=this.choropleth.getStatsMap(),r=n?Array.from(n.values()).map(s=>s.code):[];this.countryLabels.setDataIds(r)}}else{const t=e;if(this.currentStatistic=t.definition.id,this.choropleth){const i=this.choropleth.renderCustomTexture(t.values,t.definition.colorScale,t.definition.domain);if(this.material&&i){const n=new A.CanvasTexture(i);n.needsUpdate=!0,this.material.uniforms.uDataTexture.value=n,this.material.uniforms.uDataOverlay.value=1,this.material.uniforms.uDataOpacity.value=.7}}if(this.legend&&this.legend.show(t.definition),this.countryLabels){const i=t.values instanceof Map?Object.fromEntries(t.values):t.values;this.countryLabels.setDataIds(Object.keys(i))}}}setLabels(e){this.countryLabels?.setStyle(e)}addCustomLabels(e){this.countryLabels?.addCustomLabels(e)}clearCustomLabels(){this.countryLabels?.clearCustomLabels()}async setTexture(e){const t=Or[e];if(!(!t||!this.material))try{const i=await this.textureLoader.loadAsync(t);i.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),i.minFilter=A.LinearMipmapLinearFilter,i.magFilter=A.LinearFilter,this.material.uniforms.uTexture.value=i}catch(i){console.error("Failed to load texture:",e,i)}}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,n=t*i;this.exporter.startGifCapture(e);for(let r=0;r<n;r++)this.exporter.captureGifFrame(),await new Promise(s=>setTimeout(s,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))}setMarkers(e,t){this.markerLayer?t&&this.markerLayer.setConfig(t):(this.markerLayer=new oo(t),this.scene.add(this.markerLayer.getGroup()),this.markerLayer.setMorph(this.morph)),this.markerLayer.setMarkers(e)}async setUrbanData(e){if(!this.choropleth)return;const t=await ho.mapPointsToTopology(e);this.choropleth.setFeatures(t.features);const i=["#ffffb2","#fd8d3c","#bd0026"],n=Object.values(t.statistics),r=Math.max(...n,1);this.choropleth.renderCustomTexture(t.statistics,i,[0,r]),this.material&&(this.material.uniforms.uCityLights.value=1,this.config.effects.cityLights=!0),this.countryLabels&&(this.countryLabels.getGroup().visible=!1)}resize(e,t){this.config.width=e,this.config.height=t,this.handleResize()}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}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.gui?.destroy(),this.legend?.dispose(),this.countryLabels?.dispose(),this.markerLayer?.dispose(),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.renderer.dispose(),this.container.removeChild(this.renderer.domElement)}}le.BUILT_IN_STATISTICS=Wt,le.GlobeViz=co,le.WORLD_STATISTICS=$i,le.createFormatter=Hi,le.formatValue=Ka,le.normalizeCountryValues=fr,le.toNumericCode=Vi,Object.defineProperty(le,Symbol.toStringTag,{value:"Module"})}));
1268
1268
  //# sourceMappingURL=gralobe.umd.cjs.map