gralobe 1.0.66 → 1.0.67

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.
@@ -485,7 +485,7 @@
485
485
  });
486
486
  }
487
487
  };
488
- `;class ht{canvas;ctx;countries=[];loaded=!1;statsMap;featureLabels=[];topologyConfig;currentRenderId=0;onProgress;onTextureUpdate;static cache=new Map;constructor(e,t,i){this.canvas=document.createElement("canvas"),this.canvas.width=jt,this.canvas.height=Ht,this.ctx=this.canvas.getContext("2d",{willReadFrequently:!0}),this.onProgress=t,this.onTextureUpdate=i,this.topologyConfig={url:e?.url??"https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",objectName:e?.objectName||"countries",disableNormalization:e?.disableNormalization||!1,idProperty:e?.idProperty,labelProperty:e?.labelProperty},this.statsMap=new Map,Be.forEach(r=>{this.statsMap.set(r.id,r)}),this.loadCountries()}async loadCountries(){const e=`${this.topologyConfig.url}|${this.topologyConfig.objectName}|${this.topologyConfig.idProperty||""}`;if(!this.topologyConfig.url){this.loaded=!0,this.onProgress?.(1);return}if(this.onProgress?.(.1),!ht.cache.has(e)){const t=(async()=>{try{let i;i=await this.loadInWorker(this.topologyConfig.url,this.topologyConfig.objectName,this.topologyConfig.idProperty),this.onProgress?.(.4);const r=200,n=i.length;for(let a=0;a<n;a+=r){i.slice(a,a+r).forEach(u=>{u.path=this.createPath(u)});const l=.4+.6*(a+r)/n;this.onProgress?.(Math.min(.99,l)),await new Promise(u=>setTimeout(u,0))}return console.log(`Loaded and processed ${i.length} boundaries from ${this.topologyConfig.objectName}`),i}catch(i){throw console.error("Failed to load map boundaries:",i),i}})();ht.cache.set(e,t)}try{this.countries=await ht.cache.get(e),this.loaded=!0,this.updateFeatureLabels(),this.onProgress?.(1)}catch(t){ht.cache.delete(e),console.error("Error loading cached topology:",t),this.loaded=!0}}loadInWorker(e,t,i){return new Promise((r,n)=>{const a=new Blob([to],{type:"application/javascript"}),o=URL.createObjectURL(a),l=new Worker(o),u=setTimeout(()=>{l.terminate(),URL.revokeObjectURL(o),n(new Error(`Worker timed out after 15s loading ${t}`))},15e3);l.onmessage=p=>{if(p.data.status){this.onProgress?.(p.data.progress||0,p.data.status);return}clearTimeout(u),URL.revokeObjectURL(o),l.terminate(),p.data.success?r(p.data.features):n(new Error(p.data.error))},l.onerror=p=>{clearTimeout(u),URL.revokeObjectURL(o),l.terminate(),n(new Error("Worker error: "+p.message))};const c=new URL(e,window.location.href).href;l.postMessage({url:c,objectName:t,idProperty:i,topojsonUrl:"https://unpkg.com/topojson-client@3/dist/topojson-client.min.js"})})}async waitForLoad(){const e=Date.now();for(;!this.loaded;){if(Date.now()-e>2e4){console.error("ChoroplethRenderer.waitForLoad timed out after 20s. Forcing continuation."),this.loaded=!0;break}await new Promise(t=>setTimeout(t,100))}}setFeatures(e){e.forEach(t=>{t.path||(t.path=this.createPath(t))}),this.countries=e,this.loaded=!0,this.topologyConfig&&(this.topologyConfig.disableNormalization=!0),this.updateFeatureLabels()}addFeatures(e){e.forEach(t=>{t.path||(t.path=this.createPath(t))}),this.countries=[...this.countries,...e],this.topologyConfig&&(this.topologyConfig.disableNormalization=!0),this.updateFeatureLabels()}updateFeatureLabels(){this.featureLabels=this.countries.map(e=>{const t=this.computeCentroid(e);if(!t)return null;let i="";if(this.topologyConfig?.labelProperty&&(i=e.properties?.[this.topologyConfig.labelProperty]||""),i||(i=e.properties?.name||e.properties?.NAME||e.properties?.Name||e.properties?.label||e.properties?.LABEL||""),!i&&e.properties){const n=Object.keys(e.properties);for(const a of n){const o=a.toLowerCase(),l=e.properties[a];if(typeof l=="string"&&(o.includes("name")||o.includes("label")||o.includes("title"))){i=l;break}}}let r=e.id||e.properties?.id;return this.topologyConfig?.idProperty&&(r=e.properties?.[this.topologyConfig.idProperty]),{id:String(r||""),name:i,lat:t[1],lon:t[0]}}).filter(e=>e!==null&&e.id!==""&&e.name!=="")}getFeatureLabels(){return this.featureLabels}computeCentroid(e){const t=e.geometry;if(!t)return null;let i=[];if(t.type==="Point")return t.coordinates;if(t.type==="Polygon")i=t.coordinates.flat();else if(t.type==="MultiPolygon")i=t.coordinates.flat(2);else return null;if(i.length===0)return null;let r=0,n=0;for(const a of i)r+=a[0],n+=a[1];return[r/i.length,n/i.length]}renderTexture(e){this.currentRenderId++;const t=this.currentRenderId;if(this.ctx.fillStyle="rgba(10, 20, 30, 0.9)",this.ctx.fillRect(0,0,jt,Ht),this.onTextureUpdate?.(),!this.loaded)return this.canvas;const i=this.countries.length<1500;this.ctx.lineWidth=.5,this.ctx.strokeStyle="rgba(0, 0, 0, 0.3)";const r=500,n=this.countries.length;let a=0;const o=()=>{if(this.currentRenderId!==t)return;const l=Math.min(a+r,n);for(let u=a;u<l;u++){const c=this.countries[u],h=this.statsMap.get(c.id);let p="#2a2a2a";if(h){const f=e.accessor(h),m=An(e,f);p=this.interpolateColor(e.colorScale,m)}this.drawFeature(c,p,i)}a=l,this.onTextureUpdate?.(),a<n&&requestAnimationFrame(o)};return requestAnimationFrame(o),this.canvas}drawFeature(e,t,i){const r=e.path;r&&(this.ctx.fillStyle=t,this.ctx.fill(r),i&&this.ctx.stroke(r))}createPath(e){const t=new Path2D,{geometry:i}=e;if(i.type==="Polygon")this.addPolygonToPath(t,i.coordinates);else if(i.type==="MultiPolygon")i.coordinates.forEach(r=>{this.addPolygonToPath(t,r)});else if(i.type==="Point"){const[r,n]=i.coordinates,[a,o]=this.projectPoint(r,n);t.arc(a,o,4,0,Math.PI*2)}return t}addPolygonToPath(e,t){t.forEach(i=>{let r=null;i.forEach((n,a)=>{const o=n[0],l=n[1],[u,c]=this.projectPoint(o,l),h=r!==null&&Math.abs(o-r)>180;a===0||h?e.moveTo(u,c):e.lineTo(u,c),r=o}),e.closePath()})}projectPoint(e,t){const i=(e+180)/360*jt,r=(90-t)/180*Ht;return[i,r]}interpolateColor(e,t){const i=c=>({r:parseInt(c.slice(1,3),16),g:parseInt(c.slice(3,5),16),b:parseInt(c.slice(5,7),16)}),[r,n,a]=e.map(i);let o,l,u;if(t<.5){const c=t*2;o=Math.round(r.r+(n.r-r.r)*c),l=Math.round(r.g+(n.g-r.g)*c),u=Math.round(r.b+(n.b-r.b)*c)}else{const c=(t-.5)*2;o=Math.round(n.r+(a.r-n.r)*c),l=Math.round(n.g+(a.g-n.g)*c),u=Math.round(n.b+(a.b-n.b)*c)}return`rgb(${o}, ${l}, ${u})`}renderCustomTexture(e,t,i){this.currentRenderId++;const r=this.currentRenderId;if(this.ctx.fillStyle="rgba(10, 20, 30, 0.9)",this.ctx.fillRect(0,0,jt,Ht),this.onTextureUpdate?.(),!this.loaded)return this.canvas;const n=this.topologyConfig?.disableNormalization?e instanceof Map?Object.fromEntries(e):e:Yi(e),a=this.countries.length<1500;this.ctx.lineWidth=.5,this.ctx.strokeStyle="rgba(0, 0, 0, 0.3)";const o=500,l=this.countries.length;let u=0;const c=()=>{if(this.currentRenderId!==r)return;const h=Math.min(u+o,l);for(let p=u;p<h;p++){const f=this.countries[p],m=n[f.id];let d="#2a2a2a";if(m!==void 0){const g=Math.max(0,Math.min(1,(m-i[0])/(i[1]-i[0])));d=this.interpolateColor(t,g)}this.drawFeature(f,d,a)}u=h,this.onTextureUpdate?.(),u<l&&requestAnimationFrame(c)};return requestAnimationFrame(c),this.canvas}getCanvas(){return this.canvas}getDataURL(){return this.canvas.toDataURL("image/png")}getStatsMap(){return this.statsMap}getFeatureName(e){let t=this.featureLabels.find(r=>r.id===e);if(!t&&e.startsWith("0")){const r=String(parseInt(e,10));t=this.featureLabels.find(n=>n.id===r)}if(t)return t.name;const i=this.countries.find(r=>r.id===e||r.properties?.id===e);if(i)return i.properties.name||i.properties.NAME||i.properties.Name||i.id}getBounds(){if(this.countries.length===0)return null;let e=1/0,t=-1/0,i=1/0,r=-1/0,n=0,a=1/0,o=-1/0,l=1/0,u=-1/0,c=0;const h=b=>{if(typeof b[0]=="number"){const y=b[0],x=b[1];y<0?(y<e&&(e=y),y>t&&(t=y),x<i&&(i=x),x>r&&(r=x),n++):(y<a&&(a=y),y>o&&(o=y),x<l&&(l=x),x>u&&(u=x),c++)}else b.forEach(h)};if(this.countries.forEach(b=>{b.geometry&&h(b.geometry.coordinates)}),n===0&&c===0)return null;if(n===0)return[a,l,o,u];if(c===0)return[e,i,t,r];const p=Math.min(e,a),f=Math.max(t,o);if(f-p>180){if(n>c*2)return[e,i,t,r];if(c>n*2)return[a,l,o,u]}const d=Math.min(i,l),g=Math.max(r,u);return[p,d,f,g]}}const io={CN:[35,105],IN:[22,78],US:[39,-98],ID:[-2,118],PK:[30,70],BR:[-10,-55],NG:[9,8],BD:[24,90],RU:[60,100],MX:[23,-102],JP:[36,138],ET:[9,38.5],PH:[12,122],EG:[27,30],VN:[16,108],DE:[51,10],TR:[39,35],IR:[32,53],TH:[15,101],GB:[54,-2],FR:[46,2],IT:[42.5,12.5],ZA:[-29,24],TZ:[-6,35],KE:[0,38],KR:[36,128],CO:[4,-72],ES:[40,-4],AR:[-34,-64],UG:[1,32],DZ:[28,3],UA:[49,32],IQ:[33,44],PL:[52,20],CA:[56,-106],MA:[32,-5],SA:[24,45],PE:[-10,-76],AU:[-25,134],MY:[4,109.5],GH:[8,-1],NP:[28,84],VE:[7,-66],MG:[-19,47],CM:[6,12],NL:[52.5,5.5],CL:[-34,-71],SE:[62,15],NO:[64,10],SG:[1.3,103.8],NZ:[-42,174],IE:[53,-8],IL:[31,35],AE:[24,54],CH:[47,8],AT:[47.5,14.5],PT:[39.5,-8],GR:[39,22],CZ:[49.8,15.5],BE:[50.8,4],HU:[47,20],FI:[64,26],DK:[56,10],IS:[65,-18],CD:[-3,22],SD:[16,30],AO:[-12.5,18.5],MZ:[-18,35],CI:[7.5,-5.5],NE:[17,10],BF:[12,-1.5],ML:[17,-4],SN:[14.5,-14.5],ZM:[-15,28],ZW:[-19,29.5],RW:[-2,30],AF:[33,65],MM:[21,96],KP:[40,127],MN:[46,105],LK:[7.8,80.8],KZ:[48,67],UZ:[41,64],CU:[22,-79.5],EC:[-1.5,-78.5],GT:[15.5,-90.3],BO:[-17,-65],HN:[15,-86.5],PY:[-23,-58],UY:[-33,-56],CR:[10,-84],PA:[9,-80]},ro=new Set(["CN","IN","US","BR","RU","JP","DE","GB","FR","AU","CA","MX","ID","SA","ZA","EG","NG","AR","IT","ES","KR","TR","PL","NL","CH","SE","NO","PK","BD","VN"]),no=new Set(["CN","IN","US","BR","RU","AU","CA"]),ao=new Set(["RU","CA","US","CN","BR","AU"]),oo=new Set(["IN","AR","KZ","DZ","CD","SA","MX","ID","SD","LY","IR","MN","PE","TD","NE","AO","ML","ZA","CO","ET","BO","MR","EG","TZ","NG","VE","PK","TR","CL","MM"]),so=new Set(["AF","UA","MG","MZ","FR","ES","TH","CM","PG","JP","DE","VN","MY","CI","PL","IT","PH","EC","BF","NZ","GB","GH","RO","LA","GY","OM","BY","KH","SN","UG","NO","SE","FI","MR","ZM","ZW","NP","MA","IQ","BD"]);class lo{labelRenderer;labels=[];labelGroup;currentStyle="none";sphereRadius;currentMorph=0;globe=null;camera=null;dataIds=new Set;constructor(e,t){this.sphereRadius=t,this.labelRenderer=new qt.CSS2DRenderer;const i=e.clientWidth||800,r=e.clientHeight||600;this.labelRenderer.setSize(i,r),this.labelRenderer.domElement.style.position="absolute",this.labelRenderer.domElement.style.top="0",this.labelRenderer.domElement.style.left="0",this.labelRenderer.domElement.style.pointerEvents="none",this.labelRenderer.domElement.style.zIndex="5",e.appendChild(this.labelRenderer.domElement),this.labelGroup=new S.Group,this.injectStyles(),this.createLabels()}injectStyles(){const e=document.createElement("style");e.textContent=`
488
+ `;class ht{canvas;ctx;countries=[];loaded=!1;statsMap;featureLabels=[];topologyConfig;currentRenderId=0;onProgress;onTextureUpdate;static cache=new Map;constructor(e,t,i){this.canvas=document.createElement("canvas"),this.canvas.width=jt,this.canvas.height=Ht,this.ctx=this.canvas.getContext("2d",{willReadFrequently:!0}),this.onProgress=t,this.onTextureUpdate=i,this.topologyConfig={url:e?.url??"https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json",objectName:e?.objectName||"countries",disableNormalization:e?.disableNormalization||!1,idProperty:e?.idProperty,labelProperty:e?.labelProperty},this.statsMap=new Map,Be.forEach(r=>{this.statsMap.set(r.id,r)}),this.loadCountries()}async loadCountries(){const e=`${this.topologyConfig.url}|${this.topologyConfig.objectName}|${this.topologyConfig.idProperty||""}`;if(!this.topologyConfig.url){this.loaded=!0,this.onProgress?.(1);return}if(this.onProgress?.(.1),!ht.cache.has(e)){const t=(async()=>{try{let i;i=await this.loadInWorker(this.topologyConfig.url,this.topologyConfig.objectName,this.topologyConfig.idProperty),this.onProgress?.(.4);const r=200,n=i.length;for(let a=0;a<n;a+=r){i.slice(a,a+r).forEach(u=>{u.path=this.createPath(u)});const l=.4+.6*(a+r)/n;this.onProgress?.(Math.min(.99,l)),await new Promise(u=>setTimeout(u,0))}return console.log(`Loaded and processed ${i.length} boundaries from ${this.topologyConfig.objectName}`),i}catch(i){throw console.error("Failed to load map boundaries:",i),i}})();ht.cache.set(e,t)}try{this.countries=await ht.cache.get(e),this.loaded=!0,this.updateFeatureLabels(),this.onProgress?.(1)}catch(t){ht.cache.delete(e),console.error("Error loading cached topology:",t),this.loaded=!0}}loadInWorker(e,t,i){return new Promise((r,n)=>{const a=new Blob([to],{type:"application/javascript"}),o=URL.createObjectURL(a),l=new Worker(o),u=setTimeout(()=>{l.terminate(),URL.revokeObjectURL(o),n(new Error(`Worker timed out after 15s loading ${t}`))},15e3);l.onmessage=p=>{if(p.data.status){this.onProgress?.(p.data.progress||0,p.data.status);return}clearTimeout(u),URL.revokeObjectURL(o),l.terminate(),p.data.success?r(p.data.features):n(new Error(p.data.error))},l.onerror=p=>{clearTimeout(u),URL.revokeObjectURL(o),l.terminate(),n(new Error("Worker error: "+p.message))};const c=new URL(e,window.location.href).href;l.postMessage({url:c,objectName:t,idProperty:i,topojsonUrl:"https://unpkg.com/topojson-client@3/dist/topojson-client.min.js"})})}async waitForLoad(){const e=Date.now();for(;!this.loaded;){if(Date.now()-e>2e4){console.error("ChoroplethRenderer.waitForLoad timed out after 20s. Forcing continuation."),this.loaded=!0;break}await new Promise(t=>setTimeout(t,100))}}setFeatures(e){e.forEach(t=>{t.path||(t.path=this.createPath(t))}),this.countries=e,this.loaded=!0,this.topologyConfig&&(this.topologyConfig.disableNormalization=!0),this.updateFeatureLabels()}addFeatures(e){e.forEach(t=>{t.path||(t.path=this.createPath(t))}),this.countries=[...this.countries,...e],this.topologyConfig&&(this.topologyConfig.disableNormalization=!0),this.updateFeatureLabels()}updateFeatureLabels(){this.featureLabels=this.countries.map(e=>{const t=this.computeCentroid(e);if(!t)return null;let i="";if(this.topologyConfig?.labelProperty&&(i=e.properties?.[this.topologyConfig.labelProperty]||""),i||(i=e.properties?.name||e.properties?.NAME||e.properties?.Name||e.properties?.label||e.properties?.LABEL||""),!i&&e.properties){const n=Object.keys(e.properties);for(const a of n){const o=a.toLowerCase(),l=e.properties[a];if(typeof l=="string"&&(o.includes("name")||o.includes("label")||o.includes("title"))){i=l;break}}}let r=e.id||e.properties?.id;return this.topologyConfig?.idProperty&&(r=e.properties?.[this.topologyConfig.idProperty]),{id:String(r||""),name:i,lat:t[1],lon:t[0]}}).filter(e=>e!==null&&e.id!==""&&e.name!=="")}getFeatureLabels(){return this.featureLabels}computeCentroid(e){const t=e.geometry;if(!t)return null;let i=[];if(t.type==="Point")return t.coordinates;if(t.type==="Polygon")i=t.coordinates.flat();else if(t.type==="MultiPolygon")i=t.coordinates.flat(2);else return null;if(i.length===0)return null;let r=0,n=0;for(const a of i)r+=a[0],n+=a[1];return[r/i.length,n/i.length]}renderTexture(e){this.currentRenderId++;const t=this.currentRenderId;if(this.ctx.fillStyle="rgba(10, 20, 30, 0.9)",this.ctx.fillRect(0,0,jt,Ht),this.onTextureUpdate?.(),!this.loaded)return this.canvas;const i=this.countries.length<1500;this.ctx.lineWidth=.5,this.ctx.strokeStyle="rgba(0, 0, 0, 0.3)";const r=500,n=this.countries.length;let a=0;const o=()=>{if(this.currentRenderId!==t)return;const l=Math.min(a+r,n);for(let u=a;u<l;u++){const c=this.countries[u],h=this.statsMap.get(c.id);let p="#2a2a2a";if(h){const f=e.accessor(h),m=An(e,f);p=this.interpolateColor(e.colorScale,m)}this.drawFeature(c,p,i)}a=l,this.onTextureUpdate?.(),a<n&&requestAnimationFrame(o)};return requestAnimationFrame(o),this.canvas}drawFeature(e,t,i){const r=e.path;r&&(this.ctx.fillStyle=t,this.ctx.fill(r),i&&this.ctx.stroke(r))}createPath(e){const t=new Path2D,{geometry:i}=e;if(i.type==="Polygon")this.addPolygonToPath(t,i.coordinates);else if(i.type==="MultiPolygon")i.coordinates.forEach(r=>{this.addPolygonToPath(t,r)});else if(i.type==="Point"){const[r,n]=i.coordinates,[a,o]=this.projectPoint(r,n);t.arc(a,o,4,0,Math.PI*2)}return t}addPolygonToPath(e,t){t.forEach(i=>{let r=null;i.forEach((n,a)=>{const o=n[0],l=n[1],[u,c]=this.projectPoint(o,l),h=r!==null&&Math.abs(o-r)>180;a===0||h?e.moveTo(u,c):e.lineTo(u,c),r=o}),e.closePath()})}projectPoint(e,t){const i=(e+180)/360*jt,r=(90-t)/180*Ht;return[i,r]}interpolateColor(e,t){const i=c=>({r:parseInt(c.slice(1,3),16),g:parseInt(c.slice(3,5),16),b:parseInt(c.slice(5,7),16)}),[r,n,a]=e.map(i);let o,l,u;if(t<.5){const c=t*2;o=Math.round(r.r+(n.r-r.r)*c),l=Math.round(r.g+(n.g-r.g)*c),u=Math.round(r.b+(n.b-r.b)*c)}else{const c=(t-.5)*2;o=Math.round(n.r+(a.r-n.r)*c),l=Math.round(n.g+(a.g-n.g)*c),u=Math.round(n.b+(a.b-n.b)*c)}return`rgb(${o}, ${l}, ${u})`}renderCustomTexture(e,t,i){this.currentRenderId++;const r=this.currentRenderId;if(this.ctx.fillStyle="rgba(10, 20, 30, 0.9)",this.ctx.fillRect(0,0,jt,Ht),this.onTextureUpdate?.(),!this.loaded)return this.canvas;const n=this.topologyConfig?.disableNormalization?e instanceof Map?Object.fromEntries(e):e:Yi(e),a=this.countries.length<1500;this.ctx.lineWidth=.5,this.ctx.strokeStyle="rgba(0, 0, 0, 0.3)";const o=500,l=this.countries.length;let u=0;const c=()=>{if(this.currentRenderId!==r)return;const h=Math.min(u+o,l);for(let p=u;p<h;p++){const f=this.countries[p],m=n[f.id];let d="#2a2a2a";if(m!==void 0){const g=Math.max(0,Math.min(1,(m-i[0])/(i[1]-i[0])));d=this.interpolateColor(t,g)}this.drawFeature(f,d,a)}u=h,this.onTextureUpdate?.(),u<l&&requestAnimationFrame(c)};return requestAnimationFrame(c),this.canvas}getCanvas(){return this.canvas}getDataURL(){return this.canvas.toDataURL("image/png")}getStatsMap(){return this.statsMap}getFeatureName(e){let t=this.featureLabels.find(r=>r.id===e);if(!t&&e.startsWith("0")){const r=String(parseInt(e,10));t=this.featureLabels.find(n=>n.id===r)}if(t)return t.name;const i=this.countries.find(r=>r.id===e||r.properties?.id===e);if(i)return i.properties.name||i.properties.NAME||i.properties.Name||i.id}getBounds(){if(this.countries.length===0)return null;let e=1/0,t=-1/0,i=1/0,r=-1/0,n=0,a=1/0,o=-1/0,l=1/0,u=-1/0,c=0;const h=b=>{if(typeof b[0]=="number"){const y=b[0],x=b[1];y<0?(y<e&&(e=y),y>t&&(t=y),x<i&&(i=x),x>r&&(r=x),n++):(y<a&&(a=y),y>o&&(o=y),x<l&&(l=x),x>u&&(u=x),c++)}else b.forEach(h)};if(this.countries.forEach(b=>{b.geometry&&h(b.geometry.coordinates)}),n===0&&c===0)return null;if(n===0)return[a,l,o,u];if(c===0)return[e,i,t,r];const p=Math.min(e,a),f=Math.max(t,o);if(f-p>180){if(n>c*2)return[e,i,t,r];if(c>n*2)return[a,l,o,u]}const d=Math.min(i,l),g=Math.max(r,u);return[p,d,f,g]}}const io={CN:[35,105],IN:[22,78],US:[39,-98],ID:[-2,118],PK:[30,70],BR:[-10,-55],NG:[9,8],BD:[24,90],RU:[60,100],MX:[23,-102],JP:[36,138],ET:[9,38.5],PH:[12,122],EG:[27,30],VN:[16,108],DE:[51,10],TR:[39,35],IR:[32,53],TH:[15,101],GB:[54,-2],FR:[46,2],IT:[42.5,12.5],ZA:[-29,24],TZ:[-6,35],KE:[0,38],KR:[36,128],CO:[4,-72],ES:[40,-4],AR:[-34,-64],UG:[1,32],DZ:[28,3],UA:[49,32],IQ:[33,44],PL:[52,20],CA:[56,-106],MA:[32,-5],SA:[24,45],PE:[-10,-76],AU:[-25,134],MY:[4,109.5],GH:[8,-1],NP:[28,84],VE:[7,-66],MG:[-19,47],CM:[6,12],NL:[52.5,5.5],CL:[-34,-71],SE:[62,15],NO:[64,10],SG:[1.3,103.8],NZ:[-42,174],IE:[53,-8],IL:[31,35],AE:[24,54],CH:[47,8],AT:[47.5,14.5],PT:[39.5,-8],GR:[39,22],CZ:[49.8,15.5],BE:[50.8,4],HU:[47,20],FI:[64,26],DK:[56,10],IS:[65,-18],CD:[-3,22],SD:[16,30],AO:[-12.5,18.5],MZ:[-18,35],CI:[7.5,-5.5],NE:[17,10],BF:[12,-1.5],ML:[17,-4],SN:[14.5,-14.5],ZM:[-15,28],ZW:[-19,29.5],RW:[-2,30],AF:[33,65],MM:[21,96],KP:[40,127],MN:[46,105],LK:[7.8,80.8],KZ:[48,67],UZ:[41,64],CU:[22,-79.5],EC:[-1.5,-78.5],GT:[15.5,-90.3],BO:[-17,-65],HN:[15,-86.5],PY:[-23,-58],UY:[-33,-56],CR:[10,-84],PA:[9,-80]},ro=new Set(["CN","IN","US","BR","RU","JP","DE","GB","FR","AU","CA","MX","ID","SA","ZA","EG","NG","AR","IT","ES","KR","TR","PL","NL","CH","SE","NO","PK","BD","VN"]),no=new Set(["CN","IN","US","BR","RU","AU","CA"]),ao=new Set(["RU","CA","US","CN","BR","AU"]),oo=new Set(["IN","AR","KZ","DZ","CD","SA","MX","ID","SD","LY","IR","MN","PE","TD","NE","AO","ML","ZA","CO","ET","BO","MR","EG","TZ","NG","VE","PK","TR","CL","MM"]),so=new Set(["AF","UA","MG","MZ","FR","ES","TH","CM","PG","JP","DE","VN","MY","CI","PL","IT","PH","EC","BF","NZ","GB","GH","RO","LA","GY","OM","BY","KH","SN","UG","NO","SE","FI","MR","ZM","ZW","NP","MA","IQ","BD"]);class lo{labelRenderer;labels=[];labelGroup;currentStyle="none";sphereRadius;currentMorph=0;globe=null;camera=null;dataIds=new Set;constructor(e,t){this.sphereRadius=t,window.getComputedStyle(e).position==="static"&&(e.style.position="relative"),this.labelRenderer=new qt.CSS2DRenderer;const r=e.clientWidth||800,n=e.clientHeight||600;this.labelRenderer.setSize(r,n),this.labelRenderer.domElement.style.position="absolute",this.labelRenderer.domElement.style.top="0",this.labelRenderer.domElement.style.left="0",this.labelRenderer.domElement.style.pointerEvents="none",this.labelRenderer.domElement.style.zIndex="5",e.appendChild(this.labelRenderer.domElement),this.labelGroup=new S.Group,this.injectStyles(),this.createLabels()}injectStyles(){const e=document.createElement("style");e.textContent=`
489
489
  .country-label {
490
490
  font-family: system-ui, -apple-system, sans-serif;
491
491
  font-weight: 500;
@@ -1497,7 +1497,7 @@ void main() {
1497
1497
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1498
1498
  <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2-2h3"></path>
1499
1499
  </svg>
1500
- `,this.fsBtn.onclick=n=>{n.stopPropagation(),t.onToggleFullscreen()},this.element.appendChild(i),this.element.appendChild(this.projectionBtn),this.element.appendChild(this.fsBtn),getComputedStyle(e).position==="static"&&(e.style.position="relative"),e.appendChild(this.element)}updateProjectionIcon(e){this.isGlobeView=e,e?(this.projectionBtn.innerHTML=Tt.MAP_ICON,this.projectionBtn.title=`View as Flat Map${this.shortcutsEnabled?" (G)":""}`):(this.projectionBtn.innerHTML=Tt.GLOBE_ICON,this.projectionBtn.title=`View as Globe${this.shortcutsEnabled?" (G)":""}`)}setShortcutsEnabled(e){this.shortcutsEnabled=e,e?(this.projectionBtn.classList.add("show-shortcut"),this.fsBtn.classList.add("show-shortcut")):(this.projectionBtn.classList.remove("show-shortcut"),this.fsBtn.classList.remove("show-shortcut")),this.updateProjectionIcon(this.isGlobeView),this.fsBtn.title=`Toggle Fullscreen${e?" (F)":""}`}dispose(){this.element.remove()}}const Mt={lifeExpectancy:{id:"lifeExpectancy",name:"Life Expectancy",unit:"years",description:"Average life expectancy at birth",colorScale:["#feedde","#fdbe85","#d94701"],domain:[55,85],format:s=>`${s.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:s=>s.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:s=>`$${(s/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:s=>`${s.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:s=>`${s.toFixed(0)}%`},internetUsers:{id:"internetUsers",name:"Internet Penetration",unit:"%",description:"Percentage of population using the internet",colorScale:["#f2f0f7","#9e9ac8","#54278f"],domain:[0,100],format:s=>`${s.toFixed(0)}%`},urbanPopulation:{id:"urbanPopulation",name:"Urbanization",unit:"%",description:"Percentage of population living in urban areas",colorScale:["#fff5eb","#fd8d3c","#7f2704"],domain:[15,100],format:s=>`${s.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:s=>`${s.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:s=>`${s.toFixed(0)}%`},population:{id:"population",name:"Population",unit:"millions",description:"Total population",colorScale:["#fff7bc","#fec44f","#d95f0e"],domain:[1,1500],format:s=>`${s.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:s=>`${s.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:s=>`${s.toFixed(1)}%`}};Mt.lifeExpectancy;const Hi={satellite:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg"},natural:{type:"url",url:"https://unpkg.com/three-globe@2.45.0/example/img/earth-blue-marble.jpg"},dark:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png"},light:{type:"url",url:"https://raw.githubusercontent.com/turban/webgl-earth/master/images/2_no_clouds_4k.jpg"},night:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png"},topographic:{type:"url",url:"https://unpkg.com/three-globe@2.45.0/example/img/earth-topology.png"},day:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_day_4096.jpg"},bathymetry:{type:"url",url:"https://unpkg.com/three-globe@2.45.0/example/img/earth-water.png"},atlas:{type:"svg",url:"https://upload.wikimedia.org/wikipedia/commons/5/5e/BlankMap-World-Sovereign_Nations.svg",width:8192,height:4096}},Go=Object.keys(Hi),Vo="https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_10m_urban_areas.geojson";class X{static urbanFeatures=null;static isLoading=!1;static loadPromise=null;static async loadBaseTopology(){return X.urbanFeatures?X.urbanFeatures:(X.loadPromise||(X.isLoading=!0,X.loadPromise=(async()=>{try{const e=await fetch(Vo);if(!e.ok)throw new Error("Failed to load urban areas");const t=await e.json();return X.urbanFeatures=t.features,X.urbanFeatures||[]}catch(e){return console.error("UrbanMapper load error:",e),[]}finally{X.isLoading=!1}})()),X.loadPromise)}static generateSyntheticBoundary(e,t,i=85){const n=[],o=i/6371*(180/Math.PI),l=o/Math.cos(e*Math.PI/180);for(let u=0;u<=16;u++){const c=u/16*2*Math.PI,h=e+o*Math.sin(c),p=t+l*Math.cos(c);n.push([p,h])}return{type:"Feature",id:`synthetic_${e.toFixed(4)}_${t.toFixed(4)}_${i}`,properties:{name:"Unknown City",featurecla:"Synthetic Urban Area"},geometry:{type:"Polygon",coordinates:[n]}}}static async mapPointsToTopology(e,t=85,i=!1){console.log("UrbanMapper.mapPointsToTopology called with radius:",t,"forceSynthetic:",i);const r=i?[]:await X.loadBaseTopology(),n=[],a={},o=new Set;for(const l of e){let u=null;if(!i){for(const c of r)if(X.isPointInFeature(l,c)){u=c;break}}if(u){const c=u.properties.name_conve||u.properties.name||`ua_${Math.random()}`,h=o.has(c)?null:JSON.parse(JSON.stringify(u));h&&(h.id=c,l.name?h.properties.name=l.name:h.properties.name||(h.properties.name=c),n.push(h),o.add(c)),a[c]=(a[c]||0)+l.value}else{const c=X.generateSyntheticBoundary(l.lat,l.lon,t);l.id&&(c.id=l.id),l.name&&(c.properties.name=l.name),n.push(c),a[c.id]=l.value}}return{features:n,statistics:a}}static isPointInFeature(e,t){const{lat:i,lon:r}=e,n=t.geometry;if(!n)return!1;const a=n.coordinates;if(n.type==="Polygon")return X.pointInPolygon([r,i],a);if(n.type==="MultiPolygon"){for(const o of a)if(X.pointInPolygon([r,i],o))return!0}return!1}static pointInPolygon(e,t){const i=e[0],r=e[1];let n=!1;const a=t[0];for(let o=0,l=a.length-1;o<a.length;l=o++){const u=a[o][0],c=a[o][1],h=a[l][0],p=a[l][1];c>r!=p>r&&i<(h-u)*(r-c)/(p-c)+u&&(n=!n)}return n}}const Cn={texture:"satellite",labels:"data",statistic:"lifeExpectancy",autoRotate:!1,initialView:"globe",showControls:!1,showDebug:!1,showToolbar:!1,showLegend:!0,effects:{atmosphereIntensity:0,atmosphere:!1,clouds:!1,cloudSpeed:1,cloudOpacity:.6,starTwinkle:!0,aurora:!1,cityLights:!1,oceanSpecular:!1,gridLines:!1,gridOpacity:.5,hologramMode:!1,vintageMode:!1,thermalMode:!1,blueprintMode:!1,glowPulse:!1},extrudeHeight:!1,pointRadius:140,enableShortcuts:!0};class jo{container;config;scene;camera;renderer;controls;globe=null;material=null;atmosphere=null;stars=null;categoryGUIs=[];choropleth=null;legend=null;exporter=null;countryLabels=null;markerLayer=null;toolbar=null;dataGrid=null;textureLoader=new S.TextureLoader;dataTexture=null;morph=0;currentStatistic=null;currentValues=null;animationId=null;isDestroyed=!1;urbanPoints=null;ready;resolveReady;rejectReady;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.container.classList.add("gralobe-viz-container"),this.textureLoader.setCrossOrigin("anonymous"),this.config={...Cn,...t,effects:{...Cn.effects,...t.effects||{}}},console.log("GlobeViz v5 initialized",this.config.effects),this.ready=new Promise((i,r)=>{this.resolveReady=i,this.rejectReady=r}),this.init()}async init(){try{const e=this.config.width||this.container.clientWidth||800,t=this.config.height||this.container.clientHeight||600;this.scene=new S.Scene,this.scene.background=new S.Color(2066),this.camera=new S.PerspectiveCamera(50,e/t,.1,1e3),this.camera.position.set(0,0,this.config.initialView==="flat"?350:150),this.renderer=new S.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 Pn.OrbitControls(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.minDistance=2,this.controls.maxDistance=400,this.choropleth=new ht(this.config.topology,(r,n)=>{this.config.onLoadProgress?.(r,n)},()=>{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&&Y.to(this.material.uniforms.uDataOpacity,{value:.7,duration:1}))}),this.config.showLegend&&(this.legend=new Lo(this.container)),await this.createGlobe(),this.createStars(),this.config.effects.atmosphere&&this.createAtmosphere(),this.countryLabels=new lo(this.container,te),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 To(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.config.showDebug)&&this.createGUI(),this.setupInteraction(),await this.choropleth.waitForLoad();const i=this.choropleth.getFeatureLabels();i.length>0&&this.addCustomLabels(i),this.setStatistic(this.config.statistic),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.config.showToolbar||this.config.showControls)&&(this.toolbar=new Tt(this.container,{onShowData:()=>{const n=(this.currentStatistic?this.getStatisticMetadata(this.currentStatistic):null)?.definition.name||"Current Data";this.dataGrid?.show(n,this.getCurrentData())},onToggleFullscreen:()=>this.toggleFullscreen(),onToggleProjection:()=>this.toggleProjection()}),this.dataGrid=new uo(this.container),this.toolbar.updateProjectionIcon(this.config.initialView==="globe"),this.toolbar.setShortcutsEnabled(!!this.config.enableShortcuts)),this.resolveReady()}catch(e){console.error("GlobeViz init failed:",e),this.rejectReady(e)}}handleKeydown=e=>{this.isDestroyed||document.activeElement===this.renderer.domElement&&this.config.enableShortcuts&&((e.key==="g"||e.key==="G")&&this.toggleProjection(),(e.key==="f"||e.key==="F")&&this.toggleFullscreen())};async createGlobe(){const e=this.loadTextureSource(Hi[this.config.texture]),t=new Promise((a,o)=>setTimeout(()=>o(new Error("Texture load timed out after 10s")),1e4)),i=await Promise.race([e,t]);if(this.isDestroyed||!this.renderer)return;i.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),i.minFilter=S.LinearMipmapLinearFilter,i.magFilter=S.LinearFilter;const r=document.createElement("canvas");r.width=2048,r.height=1024,this.dataTexture=new S.CanvasTexture(r);const n=new S.PlaneGeometry(Math.PI*2*te,Math.PI*te,256,128);this.material=new S.ShaderMaterial({vertexShader:Do,fragmentShader:Io,uniforms:{uMorph:{value:0},uTime:{value:0},uParchment:{value:0},uExtremeParchment:{value:0},uTransitionEffect:{value:0},uTexture:{value:i},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 S.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 S.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 S.Color(4491519)}},side:S.DoubleSide}),this.globe=new S.Mesh(n,this.material),this.scene.add(this.globe)}createAtmosphere(){const e=new S.PlaneGeometry(Math.PI*2*te*1.15,Math.PI*te*1.15,128,64),t=new S.ShaderMaterial({vertexShader:Ro,fragmentShader:ko,uniforms:{uMorph:{value:0},uOpacity:{value:1}},side:S.BackSide,transparent:!0,blending:S.AdditiveBlending,depthWrite:!1});this.atmosphere=new S.Mesh(e,t),this.scene.add(this.atmosphere)}createStars(){const t=new S.BufferGeometry,i=new Float32Array(3e3*3),r=new Float32Array(3e3),n=new Float32Array(3e3);for(let o=0;o<3e3;o++){const l=300+Math.random()*300,u=Math.random()*Math.PI*2,c=Math.acos(2*Math.random()-1);i[o*3]=l*Math.sin(c)*Math.cos(u),i[o*3+1]=l*Math.sin(c)*Math.sin(u),i[o*3+2]=l*Math.cos(c),r[o]=.5+Math.random()*1.5,n[o]=Math.random()*Math.PI*2}t.setAttribute("position",new S.BufferAttribute(i,3)),t.setAttribute("aSize",new S.BufferAttribute(r,1)),t.setAttribute("aPhase",new S.BufferAttribute(n,1));const a=new S.ShaderMaterial({vertexShader:Oo,fragmentShader:Fo,uniforms:{uTime:{value:0},uTwinkle:{value:this.config.effects.starTwinkle?1:0},uOpacity:{value:1}},transparent:!0,blending:S.AdditiveBlending,depthWrite:!1});this.stars=new S.Points(t,a),this.scene.add(this.stars)}createGUI(){getComputedStyle(this.container).position==="static"&&(this.container.style.position="relative"),console.warn("Creating GUI v6 (Two-Stage Navigation)");let t=document.querySelector("style#gralobe-gui-style");t||(t=document.createElement("style"),t.id="gralobe-gui-style",document.head.appendChild(t)),t.textContent=`
1500
+ `,this.fsBtn.onclick=n=>{n.stopPropagation(),t.onToggleFullscreen()},this.element.appendChild(i),this.element.appendChild(this.projectionBtn),this.element.appendChild(this.fsBtn),getComputedStyle(e).position==="static"&&(e.style.position="relative"),e.appendChild(this.element)}updateProjectionIcon(e){this.isGlobeView=e,e?(this.projectionBtn.innerHTML=Tt.MAP_ICON,this.projectionBtn.title=`View as Flat Map${this.shortcutsEnabled?" (G)":""}`):(this.projectionBtn.innerHTML=Tt.GLOBE_ICON,this.projectionBtn.title=`View as Globe${this.shortcutsEnabled?" (G)":""}`)}setShortcutsEnabled(e){this.shortcutsEnabled=e,e?(this.projectionBtn.classList.add("show-shortcut"),this.fsBtn.classList.add("show-shortcut")):(this.projectionBtn.classList.remove("show-shortcut"),this.fsBtn.classList.remove("show-shortcut")),this.updateProjectionIcon(this.isGlobeView),this.fsBtn.title=`Toggle Fullscreen${e?" (F)":""}`}dispose(){this.element.remove()}}const Mt={lifeExpectancy:{id:"lifeExpectancy",name:"Life Expectancy",unit:"years",description:"Average life expectancy at birth",colorScale:["#feedde","#fdbe85","#d94701"],domain:[55,85],format:s=>`${s.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:s=>s.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:s=>`$${(s/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:s=>`${s.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:s=>`${s.toFixed(0)}%`},internetUsers:{id:"internetUsers",name:"Internet Penetration",unit:"%",description:"Percentage of population using the internet",colorScale:["#f2f0f7","#9e9ac8","#54278f"],domain:[0,100],format:s=>`${s.toFixed(0)}%`},urbanPopulation:{id:"urbanPopulation",name:"Urbanization",unit:"%",description:"Percentage of population living in urban areas",colorScale:["#fff5eb","#fd8d3c","#7f2704"],domain:[15,100],format:s=>`${s.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:s=>`${s.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:s=>`${s.toFixed(0)}%`},population:{id:"population",name:"Population",unit:"millions",description:"Total population",colorScale:["#fff7bc","#fec44f","#d95f0e"],domain:[1,1500],format:s=>`${s.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:s=>`${s.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:s=>`${s.toFixed(1)}%`}};Mt.lifeExpectancy;const Hi={satellite:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg"},natural:{type:"url",url:"https://unpkg.com/three-globe@2.45.0/example/img/earth-blue-marble.jpg"},dark:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png"},light:{type:"url",url:"https://raw.githubusercontent.com/turban/webgl-earth/master/images/2_no_clouds_4k.jpg"},night:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png"},topographic:{type:"url",url:"https://unpkg.com/three-globe@2.45.0/example/img/earth-topology.png"},day:{type:"url",url:"https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_day_4096.jpg"},bathymetry:{type:"url",url:"https://unpkg.com/three-globe@2.45.0/example/img/earth-water.png"},atlas:{type:"svg",url:"https://upload.wikimedia.org/wikipedia/commons/5/5e/BlankMap-World-Sovereign_Nations.svg",width:8192,height:4096}},Go=Object.keys(Hi),Vo="https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_10m_urban_areas.geojson";class X{static urbanFeatures=null;static isLoading=!1;static loadPromise=null;static async loadBaseTopology(){return X.urbanFeatures?X.urbanFeatures:(X.loadPromise||(X.isLoading=!0,X.loadPromise=(async()=>{try{const e=await fetch(Vo);if(!e.ok)throw new Error("Failed to load urban areas");const t=await e.json();return X.urbanFeatures=t.features,X.urbanFeatures||[]}catch(e){return console.error("UrbanMapper load error:",e),[]}finally{X.isLoading=!1}})()),X.loadPromise)}static generateSyntheticBoundary(e,t,i=85){const n=[],o=i/6371*(180/Math.PI),l=o/Math.cos(e*Math.PI/180);for(let u=0;u<=16;u++){const c=u/16*2*Math.PI,h=e+o*Math.sin(c),p=t+l*Math.cos(c);n.push([p,h])}return{type:"Feature",id:`synthetic_${e.toFixed(4)}_${t.toFixed(4)}_${i}`,properties:{name:"Unknown City",featurecla:"Synthetic Urban Area"},geometry:{type:"Polygon",coordinates:[n]}}}static async mapPointsToTopology(e,t=85,i=!1){console.log("UrbanMapper.mapPointsToTopology called with radius:",t,"forceSynthetic:",i);const r=i?[]:await X.loadBaseTopology(),n=[],a={},o=new Set;for(const l of e){let u=null;if(!i){for(const c of r)if(X.isPointInFeature(l,c)){u=c;break}}if(u){const c=u.properties.name_conve||u.properties.name||`ua_${Math.random()}`,h=o.has(c)?null:JSON.parse(JSON.stringify(u));h&&(h.id=c,l.name?h.properties.name=l.name:h.properties.name||(h.properties.name=c),n.push(h),o.add(c)),a[c]=(a[c]||0)+l.value}else{const c=X.generateSyntheticBoundary(l.lat,l.lon,t);l.id&&(c.id=l.id),l.name&&(c.properties.name=l.name),n.push(c),a[c.id]=l.value}}return{features:n,statistics:a}}static isPointInFeature(e,t){const{lat:i,lon:r}=e,n=t.geometry;if(!n)return!1;const a=n.coordinates;if(n.type==="Polygon")return X.pointInPolygon([r,i],a);if(n.type==="MultiPolygon"){for(const o of a)if(X.pointInPolygon([r,i],o))return!0}return!1}static pointInPolygon(e,t){const i=e[0],r=e[1];let n=!1;const a=t[0];for(let o=0,l=a.length-1;o<a.length;l=o++){const u=a[o][0],c=a[o][1],h=a[l][0],p=a[l][1];c>r!=p>r&&i<(h-u)*(r-c)/(p-c)+u&&(n=!n)}return n}}const Cn={texture:"satellite",labels:"data",statistic:"lifeExpectancy",autoRotate:!1,initialView:"globe",showControls:!1,showDebug:!1,showToolbar:!1,showLegend:!0,effects:{atmosphereIntensity:0,atmosphere:!1,clouds:!1,cloudSpeed:1,cloudOpacity:.6,starTwinkle:!0,aurora:!1,cityLights:!1,oceanSpecular:!1,gridLines:!1,gridOpacity:.5,hologramMode:!1,vintageMode:!1,thermalMode:!1,blueprintMode:!1,glowPulse:!1},extrudeHeight:!1,pointRadius:140,enableShortcuts:!0};class jo{container;config;scene;camera;renderer;controls;globe=null;material=null;atmosphere=null;stars=null;categoryGUIs=[];choropleth=null;legend=null;exporter=null;countryLabels=null;markerLayer=null;toolbar=null;dataGrid=null;textureLoader=new S.TextureLoader;dataTexture=null;morph=0;currentStatistic=null;currentValues=null;animationId=null;isDestroyed=!1;urbanPoints=null;resizeObserver=null;lastContainerWidth=0;lastContainerHeight=0;ready;resolveReady;rejectReady;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.container.classList.add("gralobe-viz-container"),this.textureLoader.setCrossOrigin("anonymous"),this.config={...Cn,...t,effects:{...Cn.effects,...t.effects||{}}},console.log("GlobeViz v5 initialized",this.config.effects),this.ready=new Promise((i,r)=>{this.resolveReady=i,this.rejectReady=r}),this.init()}async init(){try{const e=this.config.width||this.container.clientWidth||800,t=this.config.height||this.container.clientHeight||600;this.scene=new S.Scene,this.scene.background=new S.Color(2066),this.camera=new S.PerspectiveCamera(50,e/t,.1,1e3),this.camera.position.set(0,0,this.config.initialView==="flat"?350:150),this.renderer=new S.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 Pn.OrbitControls(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.minDistance=2,this.controls.maxDistance=400,this.choropleth=new ht(this.config.topology,(r,n)=>{this.config.onLoadProgress?.(r,n)},()=>{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&&Y.to(this.material.uniforms.uDataOpacity,{value:.7,duration:1}))}),this.config.showLegend&&(this.legend=new Lo(this.container)),await this.createGlobe(),this.createStars(),this.config.effects.atmosphere&&this.createAtmosphere(),this.countryLabels=new lo(this.container,te),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 To(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.config.showDebug)&&this.createGUI(),this.setupInteraction(),await this.choropleth.waitForLoad();const i=this.choropleth.getFeatureLabels();i.length>0&&this.addCustomLabels(i),this.setStatistic(this.config.statistic),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),this.resizeObserver=new ResizeObserver(r=>{if(!this.isDestroyed)for(const n of r){const{width:a,height:o}=n.contentRect;(a!==this.lastContainerWidth||o!==this.lastContainerHeight)&&(this.lastContainerWidth=a,this.lastContainerHeight=o,this.handleResize())}}),this.resizeObserver.observe(this.container),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.config.showToolbar||this.config.showControls)&&(this.toolbar=new Tt(this.container,{onShowData:()=>{const n=(this.currentStatistic?this.getStatisticMetadata(this.currentStatistic):null)?.definition.name||"Current Data";this.dataGrid?.show(n,this.getCurrentData())},onToggleFullscreen:()=>this.toggleFullscreen(),onToggleProjection:()=>this.toggleProjection()}),this.dataGrid=new uo(this.container),this.toolbar.updateProjectionIcon(this.config.initialView==="globe"),this.toolbar.setShortcutsEnabled(!!this.config.enableShortcuts)),this.lastContainerWidth=this.container.clientWidth,this.lastContainerHeight=this.container.clientHeight,this.handleResize(),this.resolveReady()}catch(e){console.error("GlobeViz init failed:",e),this.rejectReady(e)}}handleKeydown=e=>{this.isDestroyed||document.activeElement===this.renderer.domElement&&this.config.enableShortcuts&&((e.key==="g"||e.key==="G")&&this.toggleProjection(),(e.key==="f"||e.key==="F")&&this.toggleFullscreen())};async createGlobe(){const e=this.loadTextureSource(Hi[this.config.texture]),t=new Promise((a,o)=>setTimeout(()=>o(new Error("Texture load timed out after 10s")),1e4)),i=await Promise.race([e,t]);if(this.isDestroyed||!this.renderer)return;i.anisotropy=this.renderer.capabilities.getMaxAnisotropy(),i.minFilter=S.LinearMipmapLinearFilter,i.magFilter=S.LinearFilter;const r=document.createElement("canvas");r.width=2048,r.height=1024,this.dataTexture=new S.CanvasTexture(r);const n=new S.PlaneGeometry(Math.PI*2*te,Math.PI*te,256,128);this.material=new S.ShaderMaterial({vertexShader:Do,fragmentShader:Io,uniforms:{uMorph:{value:0},uTime:{value:0},uParchment:{value:0},uExtremeParchment:{value:0},uTransitionEffect:{value:0},uTexture:{value:i},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 S.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 S.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 S.Color(4491519)}},side:S.DoubleSide}),this.globe=new S.Mesh(n,this.material),this.scene.add(this.globe)}createAtmosphere(){const e=new S.PlaneGeometry(Math.PI*2*te*1.15,Math.PI*te*1.15,128,64),t=new S.ShaderMaterial({vertexShader:Ro,fragmentShader:ko,uniforms:{uMorph:{value:0},uOpacity:{value:1}},side:S.BackSide,transparent:!0,blending:S.AdditiveBlending,depthWrite:!1});this.atmosphere=new S.Mesh(e,t),this.scene.add(this.atmosphere)}createStars(){const t=new S.BufferGeometry,i=new Float32Array(3e3*3),r=new Float32Array(3e3),n=new Float32Array(3e3);for(let o=0;o<3e3;o++){const l=300+Math.random()*300,u=Math.random()*Math.PI*2,c=Math.acos(2*Math.random()-1);i[o*3]=l*Math.sin(c)*Math.cos(u),i[o*3+1]=l*Math.sin(c)*Math.sin(u),i[o*3+2]=l*Math.cos(c),r[o]=.5+Math.random()*1.5,n[o]=Math.random()*Math.PI*2}t.setAttribute("position",new S.BufferAttribute(i,3)),t.setAttribute("aSize",new S.BufferAttribute(r,1)),t.setAttribute("aPhase",new S.BufferAttribute(n,1));const a=new S.ShaderMaterial({vertexShader:Oo,fragmentShader:Fo,uniforms:{uTime:{value:0},uTwinkle:{value:this.config.effects.starTwinkle?1:0},uOpacity:{value:1}},transparent:!0,blending:S.AdditiveBlending,depthWrite:!1});this.stars=new S.Points(t,a),this.scene.add(this.stars)}createGUI(){getComputedStyle(this.container).position==="static"&&(this.container.style.position="relative"),console.warn("Creating GUI v6 (Two-Stage Navigation)");let t=document.querySelector("style#gralobe-gui-style");t||(t=document.createElement("style"),t.id="gralobe-gui-style",document.head.appendChild(t)),t.textContent=`
1501
1501
  /* Root container helper */
1502
1502
  .gralobe-viz-container {
1503
1503
  position: relative;
@@ -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 a=document.createElement("div");a.className="gralobe-category-bar",this.container.appendChild(a);let o=!1,l=null;const u={},c={},h=()=>{Object.keys(u).forEach(A=>{u[A].domElement.classList.remove("active-panel"),c[A].classList.remove("active")}),l=null},p=A=>{l===A?h():(h(),l=A,u[A].domElement.classList.add("active-panel"),c[A].classList.add("active"))},f=A=>{o=A,o?(a.classList.add("visible"),n.classList.add("expanded")):(a.classList.remove("visible"),n.classList.remove("expanded"),h())};n.onclick=A=>{A.stopPropagation(),f(!o)},f(!1);const m=(A,Se)=>{const ie=document.createElement("button");ie.className="gralobe-category-btn",ie.innerText=Se,ie.onclick=ve=>{ve.stopPropagation(),p(A)},a.appendChild(ie),c[A]=ie;const fe=new $i({container:this.container,title:""});this.categoryGUIs.push(fe),fe.domElement.classList.add("root");const xe=fe.domElement.querySelector(".title");if(xe)xe.remove();else if(fe.domElement.children.length>0){const ve=fe.domElement.children[0];ve.classList.contains("children")||ve.remove()}return u[A]=fe,fe},g=m("texture","Texture").add(this.config,"texture",Go).name("Theme").onChange(A=>this.setTexture(A));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(A=>this.setMorph(A));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(Mt)).name("Metric").onChange(A=>this.setStatistic(A));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,T=P.addFolder("Atmosphere"),L=T.add(E,"atmosphere").onChange(A=>{this.material&&(this.material.uniforms.uAtmosphereIntensity.value=A?1:0)}),M=T.add(E,"clouds").onChange(A=>{this.material&&(this.material.uniforms.uClouds.value=A?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."),T.add(E,"cloudSpeed",0,5).name("Cloud Speed").onChange(A=>{this.material&&(this.material.uniforms.uCloudSpeed.value=A)}),T.add(E,"cloudOpacity",0,1).name("Cloud Opacity").onChange(A=>{this.material&&(this.material.uniforms.uCloudOpacity.value=A)}),T.add(E,"aurora").name("Aurora").onChange(A=>{this.material&&(this.material.uniforms.uAurora.value=A?1:0)}),T.add(E,"starTwinkle").name("Star Twinkle"),T.close();const I=P.addFolder("Lighting");I.add(E,"cityLights").name("City Lights").onChange(A=>{this.material&&(this.material.uniforms.uCityLights.value=A?1:0)}),I.add(E,"oceanSpecular").name("Ocean Specular").onChange(A=>{this.material&&(this.material.uniforms.uOceanSpecular.value=A?1:0)}),I.close();const R=P.addFolder("Grid System");R.add(E,"gridLines").name("Enable Grid").onChange(A=>{this.material&&(this.material.uniforms.uGridLines.value=A?1:0)}),R.add(E,"gridOpacity",0,1).name("Opacity").onChange(A=>{this.material&&(this.material.uniforms.uGridOpacity.value=A)}),R.close();const N=A=>{A.forEach(Se=>{const ie=Se.open;Se.open=function(){return ie.apply(this),A.forEach(fe=>{fe!==this&&fe.close()}),this}})},k=P.addFolder("Special Modes");k.add(E,"hologramMode").name("Hologram").onChange(A=>{this.material&&(this.material.uniforms.uHologram.value=A?1:0)}),k.add(E,"vintageMode").name("Vintage").onChange(A=>{this.material&&(this.material.uniforms.uVintage.value=A?1:0)}),k.add(E,"thermalMode").name("Thermal").onChange(A=>{this.material&&(this.material.uniforms.uThermal.value=A?1:0)}),k.add(E,"blueprintMode").name("Blueprint").onChange(A=>{this.material&&(this.material.uniforms.uBlueprint.value=A?1:0)}),k.add(E,"glowPulse").name("Glow Pulse").onChange(A=>{this.material&&(this.material.uniforms.uGlowPulse.value=A?1:0)}),k.close(),N([T,I,R,k]);const O=m("settings","Settings"),J=O.add(this.config,"labels",["none","minimal","all","data"]).onChange(A=>this.setLabels(A));this.addTooltip(J,"<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 Je=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(Je,"<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(A=>{this.material&&(this.material.uniforms.uExtrudeRaw.value=A)}),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(A=>{this.toolbar?.setShortcutsEnabled(A)})}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,a]=t;e=-((i+n)/2)*(Math.PI/180)}Y.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)}}),Y.to(this.camera.position,{x:0,y:0,z:200,duration:2.5,ease:"power2.inOut"}),Y.to(this.controls.target,{x:0,y:0,z:0,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.controls.update()}}),this.globe&&Y.to(this.globe.rotation,{y:e,x:0,z:0,duration:2.5,ease:"power2.inOut"}),this.stars&&Y.to(this.stars.material.uniforms.uOpacity,{value:1,duration:1}),this.atmosphere&&Y.to(this.atmosphere.material.uniforms.uOpacity,{value:1,duration:1}),this.toolbar?.updateProjectionIcon(!0)}toFlat(){const e=this.choropleth?.getBounds(),t=Math.PI*2*te,i=Math.PI*te;let r=0,n=0,a=t,o=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,a=(y-b)*1.2,o=(_-x)*1.2}const l=this.camera.fov*Math.PI/180,u=this.camera.aspect,c=o/2/Math.tan(l/2),h=a/(2*Math.tan(l/2)*u),p=Math.max(c,h);this.controls.enabled=!1,Y.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&&Y.to(this.globe.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),this.atmosphere&&Y.to(this.atmosphere.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),Y.to(this.camera.position,{x:r,y:n,z:p,duration:2,ease:"power3.inOut"}),Y.to(this.controls.target,{x:r,y:n,z:0,duration:2,ease:"power3.inOut"}),Y.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&&Y.to(this.stars.material.uniforms.uOpacity,{value:0,duration:1}),this.atmosphere&&Y.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,a=new Date().getTime();this.renderer.domElement.addEventListener("mousedown",()=>{n=!1,a=new Date().getTime()}),this.renderer.domElement.addEventListener("mousemove",()=>{n=!0}),this.renderer.domElement.addEventListener("click",o=>{if(n&&new Date().getTime()-a>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,r),r)){const u=Math.PI*te,c=Math.PI*te/2;Math.abs(r.x)<=u&&Math.abs(r.y)<=c&&(Y.to(this.controls.target,{x:r.x,y:r.y,z:0,duration:1.5,ease:"power2.inOut"}),Y.to(this.camera.position,{x:r.x,y:r.y,z:50,duration:1.5,ease:"power2.inOut"}))}})}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;Be.forEach(a=>{i.has(a.id)?(r.add(a.code),n.add(a.id)):i.has(a.code)&&(r.add(a.code),n.add(a.code))}),i.forEach(a=>{n.has(a)||r.add(a)}),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&&Y.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(Hi[e]),i=new Promise((n,a)=>setTimeout(()=>a(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(),a=this.resizeSvgTexture(n,t,i),o=new Blob([a],{type:"image/svg+xml"}),l=URL.createObjectURL(o);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(a=>setTimeout(a,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 Bo(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 X.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 a=Object.values(i.statistics),o=Math.max(...a,1);this.choropleth.renderCustomTexture(i.statistics,n,[0,o]),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 a=this.choropleth.getFeatureName(r)||r;t[a]=n}return t}return this.currentValues}const e=Xt.find(t=>t.id===this.currentStatistic);if(e){const t={},i=this.choropleth?.getStatsMap();return i&&i.size>0?i.forEach((n,a)=>{const o=e.accessor(n),l=n.name||a;o!=null&&(t[l]=o)}):Be.forEach(n=>{const a=e.accessor(n);a!=null&&(t[n.name||n.code]=a)}),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.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(),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 a=document.createElement("div");Object.assign(a.style,{display:"inline-flex",marginLeft:"auto",paddingLeft:"8px",cursor:"help",pointerEvents:"auto"});const o=document.createElement("div");o.className="gralobe-help-icon",o.innerText="?",Object.assign(o.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"}),o.onmouseenter=()=>{o.style.background="rgba(0, 180, 255, 0.8)",o.style.boxShadow="0 0 10px rgba(0, 180, 255, 0.6)"},o.onmouseleave=()=>{o.style.background="rgba(0, 180, 255, 0.2)",o.style.boxShadow="none"},a.appendChild(o),n.appendChild(a);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),a.removeEventListener("mouseleave",f)};window.addEventListener("mousemove",p),a.addEventListener("mouseleave",f)};a.addEventListener("mouseenter",l)},500)}getStatisticMetadata(e){if(Mt[e]){const i=Mt[e],r=Xt.find(a=>a.id===e),n={};return r&&Be.forEach(a=>{const o=r.accessor(a);o!=null&&(n[a.id]=o)}),{definition:i,values:n}}const t=Xt.find(i=>i.id===e);if(t){const i={};return Be.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}}Q.BUILT_IN_STATISTICS=Mt,Q.GlobeViz=jo,Q.WORLD_STATISTICS=Be,Q.createFormatter=Kt,Q.formatValue=Ln,Q.normalizeCountryValues=Yi,Q.toNumericCode=Zt,Object.defineProperty(Q,Symbol.toStringTag,{value:"Module"})}));
1748
+ `,n.title="Toggle Toolbar",this.container.appendChild(n);const a=document.createElement("div");a.className="gralobe-category-bar",this.container.appendChild(a);let o=!1,l=null;const u={},c={},h=()=>{Object.keys(u).forEach(A=>{u[A].domElement.classList.remove("active-panel"),c[A].classList.remove("active")}),l=null},p=A=>{l===A?h():(h(),l=A,u[A].domElement.classList.add("active-panel"),c[A].classList.add("active"))},f=A=>{o=A,o?(a.classList.add("visible"),n.classList.add("expanded")):(a.classList.remove("visible"),n.classList.remove("expanded"),h())};n.onclick=A=>{A.stopPropagation(),f(!o)},f(!1);const m=(A,Se)=>{const ie=document.createElement("button");ie.className="gralobe-category-btn",ie.innerText=Se,ie.onclick=ve=>{ve.stopPropagation(),p(A)},a.appendChild(ie),c[A]=ie;const fe=new $i({container:this.container,title:""});this.categoryGUIs.push(fe),fe.domElement.classList.add("root");const xe=fe.domElement.querySelector(".title");if(xe)xe.remove();else if(fe.domElement.children.length>0){const ve=fe.domElement.children[0];ve.classList.contains("children")||ve.remove()}return u[A]=fe,fe},g=m("texture","Texture").add(this.config,"texture",Go).name("Theme").onChange(A=>this.setTexture(A));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(A=>this.setMorph(A));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(Mt)).name("Metric").onChange(A=>this.setStatistic(A));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,T=P.addFolder("Atmosphere"),L=T.add(E,"atmosphere").onChange(A=>{this.material&&(this.material.uniforms.uAtmosphereIntensity.value=A?1:0)}),M=T.add(E,"clouds").onChange(A=>{this.material&&(this.material.uniforms.uClouds.value=A?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."),T.add(E,"cloudSpeed",0,5).name("Cloud Speed").onChange(A=>{this.material&&(this.material.uniforms.uCloudSpeed.value=A)}),T.add(E,"cloudOpacity",0,1).name("Cloud Opacity").onChange(A=>{this.material&&(this.material.uniforms.uCloudOpacity.value=A)}),T.add(E,"aurora").name("Aurora").onChange(A=>{this.material&&(this.material.uniforms.uAurora.value=A?1:0)}),T.add(E,"starTwinkle").name("Star Twinkle"),T.close();const I=P.addFolder("Lighting");I.add(E,"cityLights").name("City Lights").onChange(A=>{this.material&&(this.material.uniforms.uCityLights.value=A?1:0)}),I.add(E,"oceanSpecular").name("Ocean Specular").onChange(A=>{this.material&&(this.material.uniforms.uOceanSpecular.value=A?1:0)}),I.close();const R=P.addFolder("Grid System");R.add(E,"gridLines").name("Enable Grid").onChange(A=>{this.material&&(this.material.uniforms.uGridLines.value=A?1:0)}),R.add(E,"gridOpacity",0,1).name("Opacity").onChange(A=>{this.material&&(this.material.uniforms.uGridOpacity.value=A)}),R.close();const N=A=>{A.forEach(Se=>{const ie=Se.open;Se.open=function(){return ie.apply(this),A.forEach(fe=>{fe!==this&&fe.close()}),this}})},k=P.addFolder("Special Modes");k.add(E,"hologramMode").name("Hologram").onChange(A=>{this.material&&(this.material.uniforms.uHologram.value=A?1:0)}),k.add(E,"vintageMode").name("Vintage").onChange(A=>{this.material&&(this.material.uniforms.uVintage.value=A?1:0)}),k.add(E,"thermalMode").name("Thermal").onChange(A=>{this.material&&(this.material.uniforms.uThermal.value=A?1:0)}),k.add(E,"blueprintMode").name("Blueprint").onChange(A=>{this.material&&(this.material.uniforms.uBlueprint.value=A?1:0)}),k.add(E,"glowPulse").name("Glow Pulse").onChange(A=>{this.material&&(this.material.uniforms.uGlowPulse.value=A?1:0)}),k.close(),N([T,I,R,k]);const O=m("settings","Settings"),J=O.add(this.config,"labels",["none","minimal","all","data"]).onChange(A=>this.setLabels(A));this.addTooltip(J,"<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 Je=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(Je,"<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(A=>{this.material&&(this.material.uniforms.uExtrudeRaw.value=A)}),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(A=>{this.toolbar?.setShortcutsEnabled(A)})}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,a]=t;e=-((i+n)/2)*(Math.PI/180)}Y.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)}}),Y.to(this.camera.position,{x:0,y:0,z:200,duration:2.5,ease:"power2.inOut"}),Y.to(this.controls.target,{x:0,y:0,z:0,duration:2.5,ease:"power2.inOut",onUpdate:()=>{this.controls.update()}}),this.globe&&Y.to(this.globe.rotation,{y:e,x:0,z:0,duration:2.5,ease:"power2.inOut"}),this.stars&&Y.to(this.stars.material.uniforms.uOpacity,{value:1,duration:1}),this.atmosphere&&Y.to(this.atmosphere.material.uniforms.uOpacity,{value:1,duration:1}),this.toolbar?.updateProjectionIcon(!0)}toFlat(){const e=this.choropleth?.getBounds(),t=Math.PI*2*te,i=Math.PI*te;let r=0,n=0,a=t,o=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,a=(y-b)*1.2,o=(_-x)*1.2}const l=this.camera.fov*Math.PI/180,u=this.camera.aspect,c=o/2/Math.tan(l/2),h=a/(2*Math.tan(l/2)*u),p=Math.max(c,h);this.controls.enabled=!1,Y.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&&Y.to(this.globe.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),this.atmosphere&&Y.to(this.atmosphere.rotation,{x:0,y:0,z:0,duration:2,ease:"power3.inOut"}),Y.to(this.camera.position,{x:r,y:n,z:p,duration:2,ease:"power3.inOut"}),Y.to(this.controls.target,{x:r,y:n,z:0,duration:2,ease:"power3.inOut"}),Y.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&&Y.to(this.stars.material.uniforms.uOpacity,{value:0,duration:1}),this.atmosphere&&Y.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,a=new Date().getTime();this.renderer.domElement.addEventListener("mousedown",()=>{n=!1,a=new Date().getTime()}),this.renderer.domElement.addEventListener("mousemove",()=>{n=!0}),this.renderer.domElement.addEventListener("click",o=>{if(n&&new Date().getTime()-a>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,r),r)){const u=Math.PI*te,c=Math.PI*te/2;Math.abs(r.x)<=u&&Math.abs(r.y)<=c&&(Y.to(this.controls.target,{x:r.x,y:r.y,z:0,duration:1.5,ease:"power2.inOut"}),Y.to(this.camera.position,{x:r.x,y:r.y,z:50,duration:1.5,ease:"power2.inOut"}))}})}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;Be.forEach(a=>{i.has(a.id)?(r.add(a.code),n.add(a.id)):i.has(a.code)&&(r.add(a.code),n.add(a.code))}),i.forEach(a=>{n.has(a)||r.add(a)}),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&&Y.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(Hi[e]),i=new Promise((n,a)=>setTimeout(()=>a(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(),a=this.resizeSvgTexture(n,t,i),o=new Blob([a],{type:"image/svg+xml"}),l=URL.createObjectURL(o);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(a=>setTimeout(a,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 Bo(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 X.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 a=Object.values(i.statistics),o=Math.max(...a,1);this.choropleth.renderCustomTexture(i.statistics,n,[0,o]),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 a=this.choropleth.getFeatureName(r)||r;t[a]=n}return t}return this.currentValues}const e=Xt.find(t=>t.id===this.currentStatistic);if(e){const t={},i=this.choropleth?.getStatsMap();return i&&i.size>0?i.forEach((n,a)=>{const o=e.accessor(n),l=n.name||a;o!=null&&(t[l]=o)}):Be.forEach(n=>{const a=e.accessor(n);a!=null&&(t[n.name||n.code]=a)}),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(),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 a=document.createElement("div");Object.assign(a.style,{display:"inline-flex",marginLeft:"auto",paddingLeft:"8px",cursor:"help",pointerEvents:"auto"});const o=document.createElement("div");o.className="gralobe-help-icon",o.innerText="?",Object.assign(o.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"}),o.onmouseenter=()=>{o.style.background="rgba(0, 180, 255, 0.8)",o.style.boxShadow="0 0 10px rgba(0, 180, 255, 0.6)"},o.onmouseleave=()=>{o.style.background="rgba(0, 180, 255, 0.2)",o.style.boxShadow="none"},a.appendChild(o),n.appendChild(a);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),a.removeEventListener("mouseleave",f)};window.addEventListener("mousemove",p),a.addEventListener("mouseleave",f)};a.addEventListener("mouseenter",l)},500)}getStatisticMetadata(e){if(Mt[e]){const i=Mt[e],r=Xt.find(a=>a.id===e),n={};return r&&Be.forEach(a=>{const o=r.accessor(a);o!=null&&(n[a.id]=o)}),{definition:i,values:n}}const t=Xt.find(i=>i.id===e);if(t){const i={};return Be.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}}Q.BUILT_IN_STATISTICS=Mt,Q.GlobeViz=jo,Q.WORLD_STATISTICS=Be,Q.createFormatter=Kt,Q.formatValue=Ln,Q.normalizeCountryValues=Yi,Q.toNumericCode=Zt,Object.defineProperty(Q,Symbol.toStringTag,{value:"Module"})}));
1749
1749
  //# sourceMappingURL=gralobe.umd.cjs.map