diff --git a/library/composables/poster/useMapPoster.js b/library/composables/poster/useMapPoster.js index 6dd383b75..b69f773b6 100644 --- a/library/composables/poster/useMapPoster.js +++ b/library/composables/poster/useMapPoster.js @@ -374,6 +374,12 @@ async function _spawnHiddenMap(sourceMap, w, h, opts = {}) { if (!gl) throw new Error('useMapPoster: ' + (isMaplibre ? 'maplibregl' : 'mapboxgl') + ' global not found'); const styleObj = sourceMap.getStyle(); + // MapLibre's style spec wants projection as an OBJECT ({ type: 'globe' }); a live MapLibre map can + // hand back Mapbox's string spelling ('globe'), which MapLibre then refuses to load — measured + // 2026-09-25: every keyless poster failed with "projection: object expected, string found". + if (isMaplibre && styleObj && typeof styleObj.projection === 'string') { + styleObj.projection = { type: styleObj.projection }; + } const hiddenMap = new gl.Map({ container, diff --git a/plugin-designs/doxa-research-map/scripts/check-export-one-click.mjs b/plugin-designs/doxa-research-map/scripts/check-export-one-click.mjs index 978b66a09..59ac0386b 100644 --- a/plugin-designs/doxa-research-map/scripts/check-export-one-click.mjs +++ b/plugin-designs/doxa-research-map/scripts/check-export-one-click.mjs @@ -68,7 +68,7 @@ try { p.on('pageerror', e => errs.push(String(e).slice(0, 160))) // A failed export is CAUGHT and logged (console.error), never thrown: read the console, or a dead // button reports "no file" with no reason. - const consoleErrs = []; p.on('console', m => { if (m.type() === 'error') consoleErrs.push(m.text().slice(0, 200)) }) + const consoleErrs = []; p.on('console', m => { if (m.type() === 'error' || m.type() === 'warning') consoleErrs.push(m.text().slice(0, 200)) }) const downloads = []; p.on('download', d => downloads.push(d)) await p.goto(url); await p.waitForTimeout(12000) const btn = p.locator('button.export-poster-btn').first() @@ -97,7 +97,13 @@ try { info = `${png ? 'PNG' : 'NOT-PNG'} ${w}x${h} ${Math.round(buf.length / 1024)}KB` if (!png || w < 1600 || h < 2100) errs.push(`file is not the expected print PNG (${info})`) } - if (!file) for (const c of consoleErrs.filter(c => /poster|useMapPoster/i.test(c)).slice(-2)) errs.push(c) + if (!file) { + for (const c of consoleErrs.filter(c => /poster|useMapPoster/i.test(c)).slice(-2)) errs.push(c) + // SLOW or DEAD? A button still busy at the deadline is a render still running; an idle one is an + // export that ended without a file. The verdict is FAIL either way; the reason says which. + const busy = await btn.evaluate(el => el.disabled || el.getAttribute('aria-busy') === 'true').catch(() => null) + errs.push(busy ? 'still rendering at the deadline (button busy)' : 'the export ended with no file (button idle)') + } const ok = clicks === 1 && !!file && !errs.length console.log(`${ok ? 'PASS' : 'FAIL'} [${where} ${lang}${PROVIDER ? ' ' + PROVIDER : ''}] "${label}" · 1 click → ${file ? `${file} (${info})` : 'NO FILE within 120s'}${errs.length ? ' · ' + [...new Set(errs)].slice(0, 3).join(' | ') : ' · 0 errors'}`) if (!ok) fails.push(`${where} ${lang}`) diff --git a/public/doxa-maps/country-posters/countries-map/index.html b/public/doxa-maps/country-posters/countries-map/index.html index 224982d12..e31a53903 100644 --- a/public/doxa-maps/country-posters/countries-map/index.html +++ b/public/doxa-maps/country-posters/countries-map/index.html @@ -29,7 +29,7 @@ - + - + - + \n\n" + "script": "\n\n\n\n" } }, { @@ -192,7 +192,7 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -206,7 +206,7 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } } ] @@ -354,7 +354,7 @@ "bundle": "doxa-research-map", "scriptUrl": "./doxa-research-map/doxa-research-map.js", "output": "doxa-maps/doxa-research-map/doxa-research-map.js", - "integrity": "sha384-Ov7KgYijm6lLh0mk873M3onNHZcs7dW+03uaoMalV+SDgj8gOe/LRVTCTY0NtS8U", + "integrity": "sha384-Drjih7xIR1Qe7Tfx+0BjRAIiuLsK3UH8FHEqim5t27Sn5rWrv1nY+sQ005L+wlc7", "elements": [ "doxa-research-map" ], @@ -370,7 +370,7 @@ "scriptUrl": "./doxa-research-map/doxa-research-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" }, "instances": [ { diff --git a/public/doxa-maps/doxa-research-map/doxa-research-map.js b/public/doxa-maps/doxa-research-map/doxa-research-map.js index 9e75bcffe..c8c88bbc0 100644 --- a/public/doxa-maps/doxa-research-map/doxa-research-map.js +++ b/public/doxa-maps/doxa-research-map/doxa-research-map.js @@ -1462,7 +1462,7 @@ `,"legend-mobile-sheet");const o=e,n=Ne("uiStore");Ne("mapStore");const a=Ne("dataStore"),s=Ne("onSemanticTreeSelect",()=>{}),r=Ne("onLegendExport",null),i=oe(()=>typeof r=="function"),l=$s(o,"legendType"),{items:c,columns:u,title:p,activeFilter:g,setFilter:S,totalCount:v,totalPopulation:b,fmtPop:_,fmtCount:$}=Ka(l,ir),f=Ne("normalizedPeopleGroups",null),m=oe(()=>{var x,G;if((x=f==null?void 0:f.value)!=null&&x.length)return f.value;const w=[];for(const X in(a==null?void 0:a.sources)||{}){const me=a.sources[X];(G=me==null?void 0:me.features)!=null&&G.length&&w.push(...me.features)}return w}),k={get value(){return m.value}},{langTree:h}=gl(k),{affinityTree:P}=_g(k),{regionsTree:d}=vg(k),C=[{id:"family",label:"Lang Family",info:"A language family is a group of languages descended from a common ancestor."},{id:"language",label:"Language",info:"A language is a system of communication used by a people."},{id:"dialect",label:"Dialect",info:"A dialect/variety is a regional or social form of a language."}],A=[{id:"bloc",label:"Block",info:"Affinity Block (rop1) — broadest grouping. 16 blocks total."},{id:"cluster",label:"Cluster",info:"Cluster (imb_reg_of_people_2, ROP 2) — finer grouping inside a block. 194 total."},{id:"group",label:"PG",info:"People Group (imb_reg_of_people_25, ROP 2.5) — abstract identity. 1,736 PGs."},{id:"pgic",label:"PGIC",info:"PG in Country (imb_reg_of_people_3 + country_code) — one row per PG-country pair. 2,106 rows."}],B=[{id:"region",label:"Region",info:'WAGF macro-region (e.g. "Asia", "Africa"). Color-coded on the map.'},{id:"block",label:"Bloc",info:'WAGF bloc — organizational sub-grouping within each region (e.g. "South East Asia").'},{id:"country",label:"Country",info:"Individual countries, listed by unreached-population size within their bloc."}],V=oe(()=>["prayer","engagement","adoption","religion"].includes(o.legendType));function J(w){const x=[];for(const G of w){x.push({id:G.key,label:G.label,color:G.color,count:G.count??0,pop:G.population??0,filter:G.filterKey?["==",["get","_flat_filter"],G.filterKey]:null,children:[]});for(const X of G.children||[])x.push({id:X.key,label:X.label,color:X.color,count:X.count??0,pop:X.population??0,filter:X.filterKey?["==",["get","_flat_filter"],X.filterKey]:null,children:[]})}return x}const z=oe(()=>o.nodes?o.nodes:V.value?J(c.value):o.legendType==="affinity-block"?P.value:o.legendType==="doxa-regions"?d.value:h.value),D=oe(()=>o.nodes?o.tabs:V.value?null:o.legendType==="affinity-block"?A:o.legendType==="doxa-regions"?B:C),L=oe(()=>o.title?o.title:V.value?t("legend.titleBar.legend"):o.legendType==="affinity-block"?t("legend.titleBar.peopleGroups"):o.legendType==="doxa-regions"?t("legend.titleBar.wagfRegions"):t("legend.titleBar.languages")),y=oe(()=>o.columnLabel!==null?o.columnLabel:["religion","prayer","engagement","adoption"].includes(o.legendType)?t("legend.colLabel."+o.legendType):V.value?t("legend.colLabel.status"):""),R=he(null),U=he(!1),H={pending:null,active:!1,startY:0,startH:0,startState:"",containerH:0,maxH:0,lastH:0,contentEl:null};let Q=0;const re=()=>Date.now()-Q<400,j=oe(()=>n.legendState),be=oe(()=>n.legendHeight),xe=oe(()=>n.caretRotation),fe=oe(()=>n.showPullTab),we=oe(()=>n.legendMode),$e=oe(()=>n.selectedPeopleGroup),ne=oe(()=>{var w;return we.value==="detail"&&$e.value?((w=$e.value.properties)==null?void 0:w.name)||t("legend.header.peopleGroupFallback"):j.value==="collapsed"?o.title||t("legend.header.collapsed"):o.title||p.value});function te(w){n.handleLegendItemClick(),S(w)}function de(){re()||n.toggleLegend()}function Se(){re()||(n.selectPeopleGroup(null),n.openLegend())}const T=oe(()=>{var x;if(!$e.value)return null;const w=$e.value.properties||{};return w.imageUrl||((x=w._raw)==null?void 0:x.ImageURL)||null});function F(w){var ce;if(j.value==="collapsed")return;const x=w.touches[0],G=R.value;if(!x||!G)return;const me=x.clientY-G.getBoundingClientRect().top<=BS?"grab":we.value==="detail"?"content":null;me&&(H.pending=me,H.active=!1,H.startY=x.clientY,H.startH=G.offsetHeight,H.lastH=H.startH,H.startState=j.value,H.containerH=((ce=G.parentElement)==null?void 0:ce.offsetHeight)||window.innerHeight,H.maxH=H.containerH-wg,H.contentEl=G.querySelector(".legend-content"))}function I(w){if(!H.pending&&!H.active)return;const x=w.touches[0];if(!x)return;const G=H.startY-x.clientY;if(!H.active){if(G===0)return;let me=H.pending==="grab";if(H.pending==="content"){const ce=!H.contentEl||H.contentEl.scrollTop<=0;me=G>0?H.startH0?n.fullyOpenLegend():H.startState==="fullyOpen"?n.openLegend():n.collapseLegend()}function Y(w){w==="fullyOpen"?n.fullyOpenLegend():w==="collapsed"?n.collapseLegend():n.openLegend()}let O=null,ue=null,ee=null;return $o(()=>{const w=R.value;if(!w||typeof ResizeObserver>"u")return;const x=w.getRootNode();ue=x instanceof ShadowRoot?x.host:x.documentElement||null,ue&&(ee=w,O=new ResizeObserver(()=>{const G=w.offsetHeight;G<=0||(ue.style.setProperty(kg,`${G}px`),ue.__legendSheetVarOwner=w)}),O.observe(w))}),Xt(()=>{O&&(O.disconnect(),O=null),ue&&ue.__legendSheetVarOwner===ee&&(ue.style.removeProperty(kg),ue.__legendSheetVarOwner=null),ue=null,ee=null}),(w,x)=>{var G;return ae(),pe("div",CS,[we.value==="detail"&&$e.value&&j.value==="open"&&T.value?(ae(),pe("div",{key:0,class:"floating-mini-image",onClick:x[0]||(x[0]=X=>ge(n).fullyOpenLegend())},[M("img",{src:T.value,alt:(G=$e.value.properties)==null?void 0:G.name,class:"mini-image"},null,8,ES)])):Le("",!0),M("div",{ref_key:"sheetRef",ref:R,class:Re(["legend-mobile-sheet",[j.value,{"sheet-dark":e.isDark,dragging:U.value}]]),style:dt({height:be.value,"--legend-sheet-top-reserve":wg+"px"}),onTouchstart:F,onTouchmove:I,onTouchend:W,onTouchcancel:W},[M("div",{class:"sheet-drag-strip",onClick:de},[fe.value?(ae(),pe("div",TS)):Le("",!0)]),we.value==="detail"&&j.value!=="collapsed"?(ae(),pe("button",{key:0,class:"detail-collapse-btn-mobile",onClick:gt(de,["stop"]),"aria-label":ge(t)("aria.toggleLegend")},null,8,NS)):Le("",!0),we.value==="detail"&&j.value!=="collapsed"?(ae(),pe("button",{key:1,class:"detail-close-btn",onClick:gt(Se,["stop"]),"aria-label":ge(t)("aria.backToLegend")},[...x[2]||(x[2]=[M("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none"},[M("path",{d:"M15 5L5 15M5 5L15 15",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round"})],-1)])],8,RS)):Le("",!0),j.value==="collapsed"&&we.value==="detail"&&$e.value?(ae(),pe("div",{key:2,class:"collapsed-detail-footer",onClick:de},[M("button",{class:"mobile-collapse-caret",style:dt({transform:`rotate(${xe.value}deg)`}),"aria-label":ge(t)("aria.toggleLegend"),tabindex:"-1"},[...x[3]||(x[3]=[M("svg",{width:"9",height:"9",viewBox:"0 0 12 12",fill:"none"},[M("path",{d:"M4.5 3L7.5 6L4.5 9",stroke:"currentColor","stroke-width":"1.8","stroke-linecap":"round","stroke-linejoin":"round"})],-1)])],12,MS),M("span",FS,ye(ne.value),1)])):Le("",!0),M("div",IS,[we.value==="detail"&&$e.value?(ae(),pe(Ue,{key:0},[j.value!=="collapsed"?(ae(),et(hl,{key:0,peopleGroup:$e.value,hideHeader:!0,dark:e.isDark,action:e.popupAction},null,8,["peopleGroup","dark","action"])):Le("",!0)],64)):e.legendType!=="default"?(ae(),et(yg,{key:1,nodes:z.value,tabs:D.value,"default-tab":e.defaultTab,title:ne.value||L.value,hideTabs:e.nodes?!e.tabs:V.value,columnLabel:y.value,columns:e.columns||["count","pop"],"export-enabled":i.value,onSelect:ge(s),onExport:x[1]||(x[1]=X=>ge(r)&&ge(r)(X))},{"title-caret":jt(()=>[M("button",{class:"mobile-collapse-caret",style:dt({transform:`rotate(${xe.value}deg)`}),onClick:gt(de,["stop"]),"aria-label":ge(t)("aria.toggleLegend")},[...x[4]||(x[4]=[M("svg",{width:"9",height:"9",viewBox:"0 0 12 12",fill:"none"},[M("path",{d:"M4.5 3L7.5 6L4.5 9",stroke:"currentColor","stroke-width":"1.8","stroke-linecap":"round","stroke-linejoin":"round"})],-1)])],12,OS)]),_:1},8,["nodes","tabs","default-tab","title","hideTabs","columnLabel","columns","export-enabled","onSelect"])):(ae(),et(cg,{key:2,title:ne.value,items:ge(c),columns:ge(u),activeFilter:ge(g),isDark:e.isDark,hideColumnHeader:!1,showFooter:!1,disableCollapse:!0,totalCount:ge(v),totalPopulation:ge(b),fmtPop:ge(_),fmtCount:ge($),onFilterClick:te},{"title-caret":jt(()=>[M("button",{class:"mobile-collapse-caret",style:dt({transform:`rotate(${xe.value}deg)`}),onClick:gt(de,["stop"]),"aria-label":ge(t)("aria.toggleLegend")},[...x[5]||(x[5]=[M("svg",{width:"9",height:"9",viewBox:"0 0 12 12",fill:"none"},[M("path",{d:"M4.5 3L7.5 6L4.5 9",stroke:"currentColor","stroke-width":"1.8","stroke-linecap":"round","stroke-linejoin":"round"})],-1)])],12,DS)]),_:1},8,["title","items","columns","activeFilter","isDark","totalCount","totalPopulation","fmtPop","fmtCount"]))])],38)])}}},[["styles",[PS]],["__scopeId","data-v-f1552af9"]])},Symbol.toStringTag,{value:"Module"})),Lg={regular:"Noto Sans Regular",medium:"Noto Sans Bold",bold:"Noto Sans Bold"};function zS(e){return typeof window<"u"&&!!window.maplibregl&&e instanceof window.maplibregl.Map}function US(e,{weight:t="regular",mapbox:o}={}){return zS(e)?{"text-font":[Lg[t]||Lg.regular]}:Array.isArray(o)&&o.length?{"text-font":o}:{}}function Ag(e,t,o,n){const a=parseFloat(t)-parseFloat(n),s=parseFloat(e)-parseFloat(o);return Math.sqrt(a*a+s*s)}function VS(e,t){const o={};for(const n of e){const a=t(n);o[a]||(o[a]=[]),o[a].push(n)}return o}const KS=Object.freeze({cluster:!0,clusterMaxZoom:6,clusterRadius:40}),HS=Object.freeze(["step",["get","point_count"],"#51bbd6",10,"#f1f075",50,"#f28cb1",500,"#e74c3c"]),qS=Object.freeze(["step",["get","point_count"],15,10,20,50,25,500,35]);class Sg{constructor(t={}){this.maxConnectionDistance=t.maxConnectionDistance??1/0}_calculateDistance(t,o){return Ag(parseFloat(t.latitude),parseFloat(t.longitude),parseFloat(o.latitude),parseFloat(o.longitude))}_createUnionFind(t){const o=Array.from({length:t},(r,i)=>i),n=new Array(t).fill(0),a=r=>(o[r]!==r&&(o[r]=a(o[r])),o[r]);return{find:a,union:(r,i)=>{const l=a(r),c=a(i);return l===c?!1:(n[l]n[c]?o[c]=l:(o[c]=l,n[l]++),!0)}}}generateMSTLines(t,o={}){const n=o.maxConnectionDistance??this.maxConnectionDistance;if(!t||t.length<2)return[];const a=t.filter(p=>{const g=parseFloat(p.latitude),S=parseFloat(p.longitude);return!isNaN(g)&&!isNaN(S)});if(a.length<2)return[];const s=[];for(let p=0;pp.distance-g.distance);const r=this._createUnionFind(a.length),i=[];let l=0;const c=a.length-1;for(const p of s){if(l>=c)break;r.union(p.from,p.to)&&(i.push(p),l++)}const u=i.map(p=>({type:"Feature",geometry:{type:"LineString",coordinates:[[parseFloat(p.fromPoint.longitude),parseFloat(p.fromPoint.latitude)],[parseFloat(p.toPoint.longitude),parseFloat(p.toPoint.latitude)]]},properties:{distance:p.distance,algorithm:"MST"}}));return i.length${n} deg).`),u}generateMultiGroupMSTLines(t,o=null,n={}){const a={},s=Object.keys(t).sort((r,i)=>t[i].length-t[r].length);for(const r of s){const i=t[r];if(!i||i.length<2){a[r]={lines:[],count:0,pointCount:(i==null?void 0:i.length)||0};continue}const l=this.generateMSTLines(i,n);if(typeof o=="function"){const c=o(r);l.forEach(u=>{u.properties={...u.properties,group:r,color:c}})}else l.forEach(c=>{c.properties={...c.properties,group:r}});a[r]={lines:l,count:l.length,pointCount:i.length}}return a}generateAllMSTLines(t,o=null,n={}){const a=this.generateMultiGroupMSTLines(t,o,n),s=[];for(const r in a)s.push(...a[r].lines);return s}getMSTStats(t,o={}){const n=this.generateMultiGroupMSTLines(t,null,o);let a=0,s=0;const r=[];for(const l in n){const{lines:c,pointCount:u}=n[l];a+=c.length,s+=u;const p=Math.max(0,u-1),g=u>1?(c.length/p*100).toFixed(1):100;r.push({name:l,points:u,lines:c.length,expected:p,coverage:g+"%",hasOrphans:c.lengthc.points-l.points);const i=r.filter(l=>l.hasOrphans).length;return{algorithm:"MST (Minimum Spanning Tree)",totalGroups:Object.keys(t).length,totalPoints:s,totalLines:a,expectedLines:Math.max(0,s-Object.keys(t).length),groupsWithOrphans:i,allPointsConnected:i===0,groups:r}}}new Sg;class Pg{constructor(t={}){this.maxDistance=t.maxDistance??15,this.maxNeighbors=t.maxNeighbors??15,this.sortKey=t.sortKey??"longitude"}_calculateDistance(t,o,n,a){return Ag(t,o,n,a)}generateNetworkLines(t,o={}){const n=o.maxDistance??this.maxDistance,a=o.maxNeighbors??this.maxNeighbors,s=o.sortKey??this.sortKey,r=o.connectOrphans??!0;if(!t||t.length<2)return[];const i=t.filter(p=>{const g=parseFloat(p.latitude),S=parseFloat(p.longitude);return!isNaN(g)&&!isNaN(S)});if(i.length<2)return[];const l=[],c=new Set,u=[...i].sort((p,g)=>parseFloat(p[s])-parseFloat(g[s]));for(let p=0;pt[i].length-t[r].length);for(const r of s){const i=t[r];if(!i||i.length<2){a[r]={lines:[],count:0,pointCount:(i==null?void 0:i.length)||0};continue}const l=this.generateNetworkLines(i,n);if(typeof o=="function"){const c=o(r);l.forEach(u=>{u.properties={...u.properties,group:r,color:c}})}else l.forEach(c=>{c.properties={...c.properties,group:r}});a[r]={lines:l,count:l.length,pointCount:i.length}}return a}generateAllNetworkLines(t,o=null,n={}){const a=this.generateMultiGroupNetworkLines(t,o,n),s=[];for(const r in a)s.push(...a[r].lines);return s}getClusteringStats(t,o={}){const n=this.generateMultiGroupNetworkLines(t,null,o);let a=0,s=0;const r=[];for(const i in n){const{lines:l,pointCount:c}=n[i];a+=l.length,s+=c,r.push({name:i,points:c,lines:l.length,ratio:c>0?(l.length/c).toFixed(2):0})}return r.sort((i,l)=>l.points-i.points),{algorithm:"Network (nearest-neighbor)",totalGroups:Object.keys(t).length,totalPoints:s,totalLines:a,avgLinesPerPoint:s>0?(a/s).toFixed(2):0,groups:r}}}new Pg({maxDistance:15,maxNeighbors:15,sortKey:"longitude"});const na="cluster-lines-source",aa="cluster-lines",rn="mapbox-clusters",Qt="mapbox-clusters-circle",sa="mapbox-clusters-count",WS=Object.freeze({type:"FeatureCollection",features:[]});function Cg(e={}){const{getMap:t=()=>null,mapId:o="unknown",getPeopleGroups:n=()=>[],pinSourceId:a="language-families",pinLayerId:s="language-family-pins",groupKey:r="languageFamily",getGroupColor:i=null,mapboxClusterOptions:l={}}=e,c=Ne("mapStore",null),u=new Sg({maxConnectionDistance:1/0}),p=new Pg({maxDistance:15,maxNeighbors:15,sortKey:"longitude"}),g=he(!1),S=he("mapbox"),v=oe(()=>c&&"clusteringEnabled"in c?!!c.clusteringEnabled:g.value),b=oe(()=>c&&c.clusteringMode?c.clusteringMode:S.value),_=he([]);let $=null,f=null,m=null;function k(j){const be=["mapbox","mst","network"];if(!be.includes(j)){console.warn(`[${o}] useMapClustering.setMode: unknown mode "${j}". Allowed: ${be.join(", ")}`);return}c&&typeof c.setClusteringMode=="function"?c.setClusteringMode(j):c&&"clusteringMode"in c?c.clusteringMode=j:S.value=j,v.value&&(B(),A())}function h(j){const be=!!j;c&&typeof c.setClusteringEnabled=="function"?c.setClusteringEnabled(be):c&&"clusteringEnabled"in c?c.clusteringEnabled=be:g.value=be,be?A():B()}function P(j){!j||!j.property||j.value==null?m=null:m={property:j.property,value:j.value},c&&("clusterFilterProperty"in c&&(c.clusterFilterProperty=(m==null?void 0:m.property)??null),"clusterFilterValue"in c&&(c.clusterFilterValue=(m==null?void 0:m.value)??null)),v.value&&(B(),A())}function d(j){if(!m)return j;const{property:be,value:xe}=m,fe=$e=>String($e||"").toLowerCase().replace(/[^a-z0-9]+/g,""),we=fe(xe);return we?j.filter($e=>{var te,de;const ne=($e==null?void 0:$e[be])??((te=$e==null?void 0:$e.properties)==null?void 0:te[be])??((de=$e==null?void 0:$e._normalized)==null?void 0:de[be]);return fe(ne)===we}):j}function C(j){$=j}function A(){const j=t();if(j){switch(B(),b.value){case"mapbox":D(j);break;case"mst":L(j,"mst");break;case"network":L(j,"network");break;default:console.warn(`[${o}] Unknown clustering mode: ${b.value}`)}re(j,!0)}}function B(){const j=t();j&&(J(j,sa,!1),J(j,Qt,!1),z(j,rn),J(j,aa,!1),z(j,na),_.value=[],R(j),re(j,!1))}function V(){const j=t();j&&(R(j),j.getLayer(sa)&&j.removeLayer(sa),j.getLayer(Qt)&&j.removeLayer(Qt),j.getSource(rn)&&j.removeSource(rn),j.getLayer(aa)&&j.removeLayer(aa),j.getSource(na)&&j.removeSource(na),re(j,!1)),_.value=[],$=null}function J(j,be,xe){if(j.getLayer(be))try{j.setLayoutProperty(be,"visibility",xe?"visible":"none")}catch{}}function z(j,be){const xe=j.getSource(be);if(xe&&typeof xe.setData=="function")try{xe.setData(WS)}catch{}}function D(j){const be=d(n());if(!be.length)return;const xe=be.map($e=>{const ne=parseFloat($e.longitude??$e.lng),te=parseFloat($e.latitude??$e.lat);return isNaN(ne)||isNaN(te)?null:{type:"Feature",geometry:{type:"Point",coordinates:[ne,te]},properties:{...$e.properties||$e}}}).filter(Boolean),fe={type:"FeatureCollection",features:xe},we=j.getSource(rn);we&&typeof we.setData=="function"?we.setData(fe):j.addSource(rn,{type:"geojson",data:fe,...KS,...l}),j.getLayer(Qt)?J(j,Qt,!0):j.addLayer({id:Qt,type:"circle",source:rn,filter:["has","point_count"],paint:{"circle-color":HS,"circle-radius":qS,"circle-stroke-width":2,"circle-stroke-color":"#ffffff",...So(j),"circle-opacity":.85}}),j.getLayer(sa)?J(j,sa,!0):j.addLayer({id:sa,type:"symbol",source:rn,filter:["has","point_count"],layout:{"text-field":["get","point_count_abbreviated"],...US(j,{weight:"medium",mapbox:["DIN Offc Pro Medium","Arial Unicode MS Bold"]}),"text-size":12},paint:{"text-color":"#000000"}}),y(j),console.log(`[${o}] Clustering: mapbox-native applied (${xe.length} features)`)}function L(j,be){const xe=d(n());if(!xe.length)return;const fe=VS(xe,Se=>{var T;return Se[r]??((T=Se._normalized)==null?void 0:T[r])??"Unknown"}),we=typeof i=="function"?i:()=>"#666666";let $e=[],ne=null;if(be==="mst"?($e=u.generateAllMSTLines(fe,we),ne=u.getMSTStats(fe)):($e=p.generateAllNetworkLines(fe,we),ne=p.getClusteringStats(fe)),_.value=$e,$e.length===0)return;const te={type:"FeatureCollection",features:$e},de=j.getSource(na);if(de&&typeof de.setData=="function"?de.setData(te):j.addSource(na,{type:"geojson",data:te}),j.getLayer(aa))J(j,aa,!0);else{const Se={id:aa,type:"line",source:na,paint:{"line-color":["coalesce",["get","color"],"#666666"],"line-width":["interpolate",["linear"],["zoom"],0,.5,4,.75,8,1,12,1.5],...So(j,"line"),"line-opacity":.5},layout:{"line-cap":"round","line-join":"round"}};j.getLayer(s)?j.addLayer(Se,s):j.addLayer(Se)}console.log(`[${o}] Clustering: ${be} applied — ${$e.length} lines across ${(ne==null?void 0:ne.totalGroups)??0} groups`)}function y(j){j.getLayer(Qt)&&(j.on("click",Qt,U),j.on("mouseenter",Qt,H),j.on("mouseleave",Qt,Q),f=Qt)}function R(j){if(!f)return;const be=f;try{j.off("click",be,U),j.off("mouseenter",be,H),j.off("mouseleave",be,Q)}catch{}f=null}function U(j){const be=t();if(!be)return;const xe=be.queryRenderedFeatures(j.point,{layers:[Qt]});if(!xe.length)return;const fe=xe[0],we=fe.properties.cluster_id,$e=fe.geometry.coordinates,ne=fe.properties.point_count;if(typeof $=="function"){$({clusterId:we,coordinates:$e,pointCount:ne,originalEvent:j});return}const te=be.getSource(rn);!te||typeof te.getClusterExpansionZoom!="function"||te.getClusterExpansionZoom(we,(de,Se)=>{de||be.easeTo({center:$e,zoom:Se})})}function H(){const j=t();j&&(j.getCanvas().style.cursor="pointer")}function Q(){const j=t();j&&(j.getCanvas().style.cursor="")}function re(j,be){if(j.getLayer(s))try{j.setPaintProperty(s,"circle-opacity",be?.25:.9)}catch{}}return{enabled:v,mode:b,clusterLines:_,setMode:k,setEnabled:h,setSelectionFilter:P,onClusterClick:C,applyClustering:A,clearClustering:B,cleanup:V,mstUtil:u,networkUtil:p}}const YS=Object.freeze(Object.defineProperty({__proto__:null,default:Cg,useMapClustering:Cg},Symbol.toStringTag,{value:"Module"})),yt={dpi:300,margins:{top:.75,right:.75,bottom:.75,left:.75},bleed:.125,gutter:{in:.2},title:{heightIn:1.25,fontFamily:'Inter, system-ui, -apple-system, "Segoe UI", sans-serif',fontWeightTitle:700,fontWeightSubtitle:500,fontSizeTitlePt:36,fontSizeSubtitlePt:16,color:"#0a0a0a"},legend:{widthIn:3,fontFamily:"Inter, system-ui, sans-serif",fontSizePt:11,swatchSize:14,backgroundColor:"rgba(255,255,255,0.94)",borderColor:"#cccccc"},chromeBand:{heightIn:.6},scaleBar:{widthIn:2.5,units:"both",color:"#111",backgroundColor:"rgba(255,255,255,0.9)"},north:{sizeIn:.6,color:"#111"},footer:{heightIn:.45,fontFamily:"Inter, system-ui, sans-serif",fontSizePt:10,color:"#222",attribution:"© Mapbox © OpenStreetMap",backgroundColor:"rgba(255,255,255,0.92)"}};function XS(e){return{widthIn:e.widthIn,heightIn:e.heightIn,dpi:e.dpi||yt.dpi,orientation:e.orientation||(e.heightIn>=e.widthIn?"portrait":"landscape"),margins:{...yt.margins},bleed:yt.bleed,slots:{title:{text:"Untitled",subtitle:"",heightIn:yt.title.heightIn},legend:{rows:[],type:"generic",widthIn:yt.legend.widthIn},scaleBar:!0,north:!0,footer:{left:"",right:"",attribution:yt.footer.attribution,heightIn:yt.footer.heightIn}},renderPath:"auto"}}const Eg=Object.freeze(Object.defineProperty({__proto__:null,defaultPosterSpec:XS,posterDefaults:yt},Symbol.toStringTag,{value:"Module"}));function Mt(e,t){return Math.round(e*t)}function ZS(e,t){var h,P,d,C,A,B,V,J;const o=t.dpi,n=e.margins||yt.margins,a={x:0,y:0,w:t.width,h:t.height},s={x:Mt(n.left,o),y:Mt(n.top,o),w:t.width-Mt(n.left+n.right,o),h:t.height-Mt(n.top+n.bottom,o)},r=(h=e.slots)!=null&&h.title?Mt(e.slots.title.heightIn||yt.title.heightIn,o):0,i={x:s.x,y:s.y,w:s.w,h:r},l=Mt(((d=(P=e.slots)==null?void 0:P.footer)==null?void 0:d.heightIn)||yt.footer.heightIn,o),c={x:s.x,y:s.y+s.h-l,w:s.w,h:l},u=(C=e.slots)!=null&&C.legend?Mt(e.slots.legend.widthIn||yt.legend.widthIn,o):0,p=(A=e.slots)!=null&&A.scaleBar||(B=e.slots)!=null&&B.north?Mt(yt.chromeBand.heightIn,o):0,g=s.y+r+(r?Mt(yt.gutter.in,o):0),S=c.y-p-(p?Mt(yt.gutter.in,o):0),v={x:s.x,y:g,w:s.w-(u?u+Mt(yt.gutter.in,o):0),h:S-g},b=u?{x:s.x+s.w-u,y:g,w:u,h:v.h}:{x:0,y:0,w:0,h:0},_=Mt(yt.scaleBar.widthIn,o),$=p,f=(V=e.slots)!=null&&V.scaleBar?{x:s.x,y:S+(p?Mt(yt.gutter.in,o):0),w:_,h:$}:{x:0,y:0,w:0,h:0},m=Mt(yt.north.sizeIn,o),k=(J=e.slots)!=null&&J.north?{x:s.x+s.w-m-(u?u+Mt(yt.gutter.in,o):0),y:S+(p?Mt(yt.gutter.in,o):0),w:m,h:m}:{x:0,y:0,w:0,h:0};return{page:a,content:s,title:i,map:v,legend:b,scaleBar:f,north:k,footer:c}}const Ln=4096,Co=2048,Tg=15e3;function Ng(e){const t=he({phase:"idle",completed:0,total:0,message:"",fraction:0,percent:0}),o=he(!1);let n=null;async function a(v,b="png"){if(o.value)throw new Error("useMapPoster: already rendering");const _=e();if(!_)throw new Error("useMapPoster: no source map");n=new AbortController;const $=oP(v.signal,n.signal);o.value=!0,l=0,c("init",1,1,"preparing render");try{const f=Rg(v),m=JS(v.renderPath||"auto",f);m==="single"&&(f.width>Ln||f.height>Ln)&&console.warn(`[useMapPoster] requested single-canvas ${f.width}×${f.height}px > ${Ln}; auto-switching to tile-stitch.`);const h=(m==="single"&&(f.width>Ln||f.height>Ln)?"stitch":m)==="single"?await u(_,f,$):await p(_,f,$);c("compose",0,1,"compositing overlay");const P=await g(h,v,f);c("encode",0,1,`encoding ${b.toUpperCase()}`);const d=b==="pdf"?await tP(P,v):await eP(P,"image/png");return c("done",1,1,"render complete"),d}finally{o.value=!1,n=null}}function s(){n&&n.abort()}const r={init:.02,"hidden-map":.3,stitch:.42,capture:.14,compose:.06,encode:.05,done:.01},i=["init","hidden-map","stitch","capture","compose","encode","done"];let l=0;function c(v,b,_,$){const f=i.indexOf(v);let m=0;for(let P=0;P0?Math.min(1,b/_):0,h=m+(r[v]||0)*k;l=v==="init"?h:Math.max(l,h),t.value={phase:v,completed:b,total:_,message:$,fraction:Math.min(1,l),percent:Math.round(Math.min(1,l)*100)}}async function u(v,b,_){c("hidden-map",0,3,"creating hidden map");const{hiddenMap:$,restoreDpr:f,container:m}=await Mg(v,b.width,b.height);try{_l(_),c("hidden-map",1,3,"loading map tiles"),await Ig($),c("hidden-map",3,3,"tiles loaded"),_l(_),c("capture",0,1,"capturing canvas");const k=$.getCanvas().toDataURL("image/png"),h=await Og(k),P=document.createElement("canvas");return P.width=b.width,P.height=b.height,P.getContext("2d").drawImage(h,0,0,b.width,b.height),P}finally{try{$.remove()}catch{}m!=null&&m.parentNode&&m.parentNode.removeChild(m),f()}}async function p(v,b,_){const $=Math.ceil(b.width/Co),f=Math.ceil(b.height/Co),m=$*f;c("hidden-map",0,m,`creating hidden map (${$}×${f} tiles)`);const k=v.getBounds(),h=v.getCenter(),P=v.getZoom(),d=v.getBearing(),C=v.getPitch(),{hiddenMap:A,restoreDpr:B,container:V}=await Mg(v,Co,Co,{preserveBounds:!0,bounds:k});try{const J=document.createElement("canvas");J.width=b.width,J.height=b.height;const z=J.getContext("2d"),D=k.getNorthEast(),L=k.getSouthWest(),y=D.lng-L.lng,R=D.lat-L.lat,U=y/$,H=R/f;let Q=0;for(let re=0;ret&&([t,o]=[o,t]),e.orientation==="portrait"&&t>o&&([t,o]=[o,t]);const n=e.dpi||300;return{width:Math.round(t*n),height:Math.round(o*n),dpi:n}}function JS(e,t){return e==="single"||e==="stitch"?e:t.width>Ln||t.height>Ln?"stitch":"single"}async function Mg(e,t,o,n={}){const a=document.createElement("div");a.style.cssText=` position: fixed; top: -100000px; left: -100000px; width: ${t}px; height: ${o}px; pointer-events: none; visibility: hidden; - `,document.body.appendChild(a);const s=window.devicePixelRatio,r=()=>{try{Object.defineProperty(window,"devicePixelRatio",{value:s,configurable:!0})}catch{}};try{Object.defineProperty(window,"devicePixelRatio",{value:1,configurable:!0})}catch{}const i=typeof window<"u"?window:{},l=!!i.maplibregl&&e instanceof i.maplibregl.Map,c=l?i.maplibregl:i.mapboxgl;if(!c)throw new Error("useMapPoster: "+(l?"maplibregl":"mapboxgl")+" global not found");const u=e.getStyle(),p=new c.Map({container:a,style:u,center:e.getCenter(),zoom:e.getZoom(),bearing:e.getBearing(),pitch:e.getPitch(),interactive:!1,attributionControl:!1,preserveDrawingBuffer:!0,canvasContextAttributes:{preserveDrawingBuffer:!0},fadeDuration:0});let g="";await new Promise((S,v)=>{let b=!1;const _=m=>{b||(b=!0,clearTimeout($),clearInterval(f),m?v(m):S())},$=setTimeout(()=>_(new Error("useMapPoster: hidden map style.load timeout"+(g?" ("+g+")":""))),Tg),f=setInterval(()=>{try{p.isStyleLoaded()&&_()}catch{}},100);p.once("style.load",()=>_()),p.once("load",()=>_()),p.on("error",m=>{var k;g=String(((k=m==null?void 0:m.error)==null?void 0:k.message)||(m==null?void 0:m.message)||m).slice(0,160)})});try{const S=typeof e.getLanguage=="function"?e.getLanguage():null;S&&typeof p.setLanguage=="function"&&p.setLanguage(S)}catch{}return n.preserveBounds&&n.bounds&&p.fitBounds(n.bounds,{padding:0,animate:!1,duration:0}),{hiddenMap:p,restoreDpr:r,container:a}}const Fg=6e4;function Ig(e){return new Promise(t=>{let o=!1;const n=[],a=r=>{o||(o=!0,n.forEach(clearTimeout),r&&console.warn("[useMapPoster] "+r),requestAnimationFrame(()=>t()))},s=()=>{try{return typeof e.areTilesLoaded=="function"&&e.areTilesLoaded()}catch{return!1}};n.push(setTimeout(()=>{s()&&a()},Tg)),n.push(setTimeout(()=>a("hidden map not idle after "+Fg/1e3+"s; capturing what is drawn"),Fg)),e.loaded()&&!e.isMoving()?a():e.once("idle",()=>a())})}function Og(e){return new Promise((t,o)=>{const n=new Image;n.crossOrigin="anonymous",n.onload=()=>t(n),n.onerror=a=>o(new Error("image load failed: "+((a==null?void 0:a.message)||"unknown"))),n.src=typeof e=="string"?e:URL.createObjectURL(e)})}function QS(e,t,o){const n=new Blob([e],{type:"image/svg+xml;charset=utf-8"}),a=URL.createObjectURL(n);return new Promise((s,r)=>{const i=new Image;i.onload=()=>{URL.revokeObjectURL(a),s(i)},i.onerror=l=>{URL.revokeObjectURL(a),r(new Error("svg load failed: "+((l==null?void 0:l.message)||"unknown")))},i.width=t,i.height=o,i.src=a})}function eP(e,t){return new Promise((o,n)=>{e.toBlob(a=>a?o(a):n(new Error("toBlob failed")),t)})}async function tP(e,t){let o;try{const r=await oo(()=>import("https://esm.sh/jspdf@4.2.1"),void 0);o=r.jsPDF||r.default}catch{throw new Error("useMapPoster.toPdf: jspdf not installed (npm i jspdf svg2pdf.js)")}const n=e.toDataURL("image/png"),a=e.width>e.height?"l":"p",s=new o({orientation:a,unit:"in",format:[t.widthIn,t.heightIn]});return s.addImage(n,"PNG",0,0,t.widthIn,t.heightIn),s.output("blob")}function _l(e){if(e!=null&&e.aborted)throw new DOMException("useMapPoster: aborted","AbortError")}function oP(...e){const t=new AbortController;for(const o of e)if(o){if(o.aborted){t.abort();break}o.addEventListener("abort",()=>t.abort(),{once:!0})}return t.signal}function Dg(e){return(e==null?void 0:e.dir)==="rtl"}function vl(e,t,o){return Dg(e)?`x="${t.w-o}" text-anchor="start" direction="rtl"`:`x="${o}"`}function nP(e,t){var a,s,r,i;const o=(((s=(a=e.slots)==null?void 0:a.title)==null?void 0:s.text)||"").replace(/[<>&]/g,An),n=(((i=(r=e.slots)==null?void 0:r.title)==null?void 0:i.subtitle)||"").replace(/[<>&]/g,An);return` + `,document.body.appendChild(a);const s=window.devicePixelRatio,r=()=>{try{Object.defineProperty(window,"devicePixelRatio",{value:s,configurable:!0})}catch{}};try{Object.defineProperty(window,"devicePixelRatio",{value:1,configurable:!0})}catch{}const i=typeof window<"u"?window:{},l=!!i.maplibregl&&e instanceof i.maplibregl.Map,c=l?i.maplibregl:i.mapboxgl;if(!c)throw new Error("useMapPoster: "+(l?"maplibregl":"mapboxgl")+" global not found");const u=e.getStyle();l&&u&&typeof u.projection=="string"&&(u.projection={type:u.projection});const p=new c.Map({container:a,style:u,center:e.getCenter(),zoom:e.getZoom(),bearing:e.getBearing(),pitch:e.getPitch(),interactive:!1,attributionControl:!1,preserveDrawingBuffer:!0,canvasContextAttributes:{preserveDrawingBuffer:!0},fadeDuration:0});let g="";await new Promise((S,v)=>{let b=!1;const _=m=>{b||(b=!0,clearTimeout($),clearInterval(f),m?v(m):S())},$=setTimeout(()=>_(new Error("useMapPoster: hidden map style.load timeout"+(g?" ("+g+")":""))),Tg),f=setInterval(()=>{try{p.isStyleLoaded()&&_()}catch{}},100);p.once("style.load",()=>_()),p.once("load",()=>_()),p.on("error",m=>{var k;g=String(((k=m==null?void 0:m.error)==null?void 0:k.message)||(m==null?void 0:m.message)||m).slice(0,160)})});try{const S=typeof e.getLanguage=="function"?e.getLanguage():null;S&&typeof p.setLanguage=="function"&&p.setLanguage(S)}catch{}return n.preserveBounds&&n.bounds&&p.fitBounds(n.bounds,{padding:0,animate:!1,duration:0}),{hiddenMap:p,restoreDpr:r,container:a}}const Fg=6e4;function Ig(e){return new Promise(t=>{let o=!1;const n=[],a=r=>{o||(o=!0,n.forEach(clearTimeout),r&&console.warn("[useMapPoster] "+r),requestAnimationFrame(()=>t()))},s=()=>{try{return typeof e.areTilesLoaded=="function"&&e.areTilesLoaded()}catch{return!1}};n.push(setTimeout(()=>{s()&&a()},Tg)),n.push(setTimeout(()=>a("hidden map not idle after "+Fg/1e3+"s; capturing what is drawn"),Fg)),e.loaded()&&!e.isMoving()?a():e.once("idle",()=>a())})}function Og(e){return new Promise((t,o)=>{const n=new Image;n.crossOrigin="anonymous",n.onload=()=>t(n),n.onerror=a=>o(new Error("image load failed: "+((a==null?void 0:a.message)||"unknown"))),n.src=typeof e=="string"?e:URL.createObjectURL(e)})}function QS(e,t,o){const n=new Blob([e],{type:"image/svg+xml;charset=utf-8"}),a=URL.createObjectURL(n);return new Promise((s,r)=>{const i=new Image;i.onload=()=>{URL.revokeObjectURL(a),s(i)},i.onerror=l=>{URL.revokeObjectURL(a),r(new Error("svg load failed: "+((l==null?void 0:l.message)||"unknown")))},i.width=t,i.height=o,i.src=a})}function eP(e,t){return new Promise((o,n)=>{e.toBlob(a=>a?o(a):n(new Error("toBlob failed")),t)})}async function tP(e,t){let o;try{const r=await oo(()=>import("https://esm.sh/jspdf@4.2.1"),void 0);o=r.jsPDF||r.default}catch{throw new Error("useMapPoster.toPdf: jspdf not installed (npm i jspdf svg2pdf.js)")}const n=e.toDataURL("image/png"),a=e.width>e.height?"l":"p",s=new o({orientation:a,unit:"in",format:[t.widthIn,t.heightIn]});return s.addImage(n,"PNG",0,0,t.widthIn,t.heightIn),s.output("blob")}function _l(e){if(e!=null&&e.aborted)throw new DOMException("useMapPoster: aborted","AbortError")}function oP(...e){const t=new AbortController;for(const o of e)if(o){if(o.aborted){t.abort();break}o.addEventListener("abort",()=>t.abort(),{once:!0})}return t.signal}function Dg(e){return(e==null?void 0:e.dir)==="rtl"}function vl(e,t,o){return Dg(e)?`x="${t.w-o}" text-anchor="start" direction="rtl"`:`x="${o}"`}function nP(e,t){var a,s,r,i;const o=(((s=(a=e.slots)==null?void 0:a.title)==null?void 0:s.text)||"").replace(/[<>&]/g,An),n=(((i=(r=e.slots)==null?void 0:r.title)==null?void 0:i.subtitle)||"").replace(/[<>&]/g,An);return` window.MAP_APP_API_URL = "https://pray.doxa.life"; - +