@rr0/ufoathome 0.138.0 → 0.140.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-embed-scene/{SceneNames_es-BS2ioieu.js → SceneNames_es-N44Pf1L2.js} +1 -1
- package/dist-embed-scene/SceneNames_fr-CjP77vdN.js +1 -0
- package/dist-embed-scene/SceneNames_it-BwKgT2Yi.js +1 -0
- package/dist-embed-scene/rr0-scene.mjs +590 -306
- package/dist-embed-scene/sightingSchema-t3YCF0eV.js +1 -0
- package/dist-embed-sighting/{SceneNames_es-wkWHMHet.js → SceneNames_es-CPCIUfgZ.js} +1 -1
- package/dist-embed-sighting/SceneNames_fr-DixomaFq.js +1 -0
- package/dist-embed-sighting/SceneNames_it-BTNeU7-M.js +1 -0
- package/dist-embed-sighting/SightingMessages_es-Dh2PWDVp.js +1 -0
- package/dist-embed-sighting/SightingMessages_fr-CC8wrc-A.js +1 -0
- package/dist-embed-sighting/SightingMessages_it-DlF58PB6.js +1 -0
- package/dist-embed-sighting/rr0-sighting.mjs +595 -311
- package/dist-embed-sighting/sightingSchema-t3YCF0eV.js +1 -0
- package/dist-embed-sighting-editor/{InterviewEditorElement-D6TqSXYB.js → InterviewEditorElement-DBuxtdMB.js} +2 -2
- package/dist-embed-sighting-editor/{SceneNames_es-umTyyCxN.js → SceneNames_es-BJ791O35.js} +1 -1
- package/dist-embed-sighting-editor/SceneNames_fr-B7V4VIzu.js +1 -0
- package/dist-embed-sighting-editor/SceneNames_it-Bvd-wMlt.js +1 -0
- package/dist-embed-sighting-editor/{SightingEditorMessages_es-UiUywEk4.js → SightingEditorMessages_es-CYIx9rC9.js} +1 -1
- package/dist-embed-sighting-editor/{SightingEditorMessages_fr-D5pajHZd.js → SightingEditorMessages_fr-Cj4oAsL_.js} +1 -1
- package/dist-embed-sighting-editor/{SightingEditorMessages_it-9SduHYDB.js → SightingEditorMessages_it-CzlDZwso.js} +1 -1
- package/dist-embed-sighting-editor/{SightingFileEditor-DkRqrb-a.js → SightingFileEditor-DDAzzasL.js} +1 -1
- package/dist-embed-sighting-editor/rr0-sighting-editor.mjs +730 -421
- package/dist-embed-sighting-editor/sightingSchema-t3YCF0eV.js +1 -0
- package/dist-embed-sighting-editor/surroundingsSources-BK1okKkz.js +1 -0
- package/package.json +1 -1
- package/dist-embed-scene/SceneNames_fr-B-fRwUUu.js +0 -1
- package/dist-embed-scene/SceneNames_it-B-QEwmuM.js +0 -1
- package/dist-embed-scene/sightingSchema-DT9g-OyF.js +0 -1
- package/dist-embed-sighting/SceneNames_fr-DbbzZ1bB.js +0 -1
- package/dist-embed-sighting/SceneNames_it-BsxnweGu.js +0 -1
- package/dist-embed-sighting/SightingMessages_es-B-iNzmf3.js +0 -1
- package/dist-embed-sighting/SightingMessages_fr-Dq30mEEs.js +0 -1
- package/dist-embed-sighting/SightingMessages_it-CSJFk0JC.js +0 -1
- package/dist-embed-sighting/sightingSchema-DT9g-OyF.js +0 -1
- package/dist-embed-sighting-editor/SceneNames_fr-BNgnxIHx.js +0 -1
- package/dist-embed-sighting-editor/SceneNames_it-5UYhmJHo.js +0 -1
- package/dist-embed-sighting-editor/sightingSchema-DT9g-OyF.js +0 -1
- package/dist-embed-sighting-editor/surroundingsSources-B1931yh4.js +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{f as V,O as B,g as q,T as j,h as C,i as X,j as tt,k as et}from"./rr0-sighting-editor.mjs";class I{constructor(t){this.survey=t}static CACHE_NAME="rr0-surroundings";static MAX_AGE_MS=720*3600*1e3;static FORMAT=7;static MAX_STORED=400;static asked=new Map;get id(){return this.survey.id}get name(){return this.survey.name}get attribution(){return this.survey.attribution}get creditUrl(){return this.survey.creditUrl}get contemporary(){return this.survey.contemporary}covers(t,e){return this.survey.covers(t,e)}async getSurroundings(t,e){const o=I.key(this.survey.id,t,e);let s=I.asked.get(o);return s===void 0&&(s=this.recalled(o,t,e),I.asked.set(o,s),s.catch(()=>I.asked.delete(o))),structuredClone(await s)}later(t,e){const o=this.survey.later?.(t,e);if(!o)return;const s=I.key(this.survey.id,t,e);return o.then(n=>{const a=I.partial.get(s);if(a){I.partial.delete(s);const{incomplete:r,...i}=a,h=[...a.credits??[],...n.credits??[]].filter((l,f,M)=>M.findIndex(d=>d.url===l.url)===f),c={...i,...n,dates:{...a.dates,...n.dates},...h.length>0?{credits:h}:{}};I.asked.set(s,Promise.resolve(c)),I.openCache().then(l=>l&&I.store(l,I.request(s),c))}return structuredClone(n)})}static partial=new Map;static forget(){I.partial.clear(),I.asked.clear()}static key(t,e,o){return`${t}/v${I.FORMAT}/${e.lat.toFixed(5)}/${e.lng.toFixed(5)}/${Math.round(o)}`}async recalled(t,e,o){const s=await I.openCache(),n=I.request(t);if(s)try{const r=await s.match(n);if(r){const{savedAt:i,surroundings:h}=await r.json();if(Date.now()-i<I.MAX_AGE_MS)return h;await s.delete(n)}}catch{}const a=await this.survey.getSurroundings(e,o);return a.incomplete?(I.asked.delete(t),I.partial.set(t,structuredClone(a)),a):(s&&I.store(s,n,a),a)}static async store(t,e,o){try{const s={savedAt:Date.now(),surroundings:o};await t.put(e,new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}}));const n=await t.keys();for(const a of n.slice(0,Math.max(0,n.length-I.MAX_STORED)))await t.delete(a)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(I.CACHE_NAME)}catch{return}}static request(t){const e=typeof location>"u"?"http://localhost":location.origin;return new Request(`${e}/__surroundings__/${encodeURIComponent(t)}`)}}class st{constructor(t){if(this.candidates=t,t.length===0)throw new Error("A survey needs at least one candidate");this.current=t[0]}current;get id(){return this.current.id}get name(){return this.current.name}get attribution(){return this.current.attribution}get creditUrl(){return this.current.creditUrl}get contemporary(){return this.current.contemporary}covers(t,e){return this.candidates.some(o=>o.covers(t,e))}async getSurroundings(t,e){let o;for(const s of this.candidates)if(s.covers(t.lat,t.lng))try{const n=await s.getSurroundings(t,e);return this.current=s,n}catch(n){if(o??=n,!(n instanceof V))throw n}throw o??new V("no-coverage","No survey reaches this place")}later(t,e){return this.current.later?.(t,e)}}class D{constructor(t,e,o,s){this.heights=t,this.building=e,this.width=o,this.height=s}static ROOF_PERCENTILE=.9;static STEP_M=.9;static RECTANGLE_FILL=.72;static MIN_DEPTH_M=2.5;static MAX_PARTS=24;static MAX_CELLS=640;buildings(t){const e=t.metresPerPixel,o=t.minAreaM2??12,s=new Uint8Array(this.width*this.height),n=[];for(let a=0;a<this.building.length;a++){if(!this.building[a]||s[a])continue;const r=this.patchFrom(a,s);if(!(r.length*e*e<o))for(const i of this.buildingsOf(r,e,o))Math.hypot(i.eastM,i.northM)<=t.radiusM&&n.push(i)}return n.sort((a,r)=>Math.hypot(a.eastM,a.northM)-Math.hypot(r.eastM,r.northM)),n.slice(0,t.maxBuildings??60)}patchFrom(t,e){const o=[],s=[t];for(e[t]=1;s.length>0;){const n=s.pop();o.push(n);const a=n%this.width,r=Math.floor(n/this.width);for(const[i,h]of[[1,0],[-1,0],[0,1],[0,-1]]){const c=a+i,l=r+h;if(c<0||l<0||c>=this.width||l>=this.height)continue;const f=l*this.width+c;this.building[f]&&!e[f]&&Math.abs(this.heights[f]-this.heights[n])<=D.STEP_M&&(e[f]=1,s.push(f))}}return o}buildingsOf(t,e,o){const s=this.width/2,n=this.height/2,a=[],r=[],i=[];for(const h of t){const c=h%this.width,l=Math.floor(h/this.width);a.push((c+.5-s)*e),r.push((n-l-.5)*e),i.push(this.heights[h])}return D.ofPoints(a,r,i,e,o)}static ofPoints(t,e,o,s,n,a=D.RECTANGLE_FILL,r=[]){const i=[];for(let p=0;p<t.length;p++)for(const[E,v]of[[-.5,-.5],[.5,-.5],[-.5,.5],[.5,.5]])i.push([t[p]+E*s,e[p]+v*s]);const h=D.hull(i),{angle:c,rectangle:l}=D.smallestRectangle(h),f=t.length*s*s,M=(l.maxU-l.minU)*(l.maxV-l.minV);if(f/M>=a||f<4*n)return[D.building(l,c,D.roof(o))];const d=h.map((p,E)=>({from:{east:p[0],north:p[1]},to:{east:h[(E+1)%h.length][0],north:h[(E+1)%h.length][1]}})),g=D.dominantAngles([...r,...d]);return D.parts(t,e,o,g.length>0?g:[c],s,n)}static dominantAngles(t,e=3){const o=new Map;for(const s of t){const n=Math.hypot(s.to.east-s.from.east,s.to.north-s.from.north);if(n<1e-6)continue;const a=(Math.atan2(s.to.north-s.from.north,s.to.east-s.from.east)%(Math.PI/2)+Math.PI/2)%(Math.PI/2),r=Math.round(a/(Math.PI/60));o.set(r,(o.get(r)??0)+n)}return[...o.entries()].sort((s,n)=>n[1]-s[1]).slice(0,e).map(([s])=>s*Math.PI/60)}static parts(t,e,o,s,n,a){let r=t.map((h,c)=>c);const i=[];for(let h=0;h<D.MAX_PARTS&&r.length>0;h++){let c;for(const M of s){const d=D.largestIn(t,e,r,M,n);d&&(c===void 0||d.area>c.area)&&(c=d)}if(c===void 0||c.area<a)break;const l=c.covered;Math.min(c.rectangle.maxU-c.rectangle.minU,c.rectangle.maxV-c.rectangle.minV)>=D.MIN_DEPTH_M&&i.push(D.building(c.rectangle,c.angle,D.roof([...l].map(M=>o[M])))),r=r.filter(M=>!l.has(M))}return i}static largestIn(t,e,o,s,n){const a=Math.cos(s),r=Math.sin(s);let i=1/0,h=-1/0,c=1/0,l=-1/0;const f=[],M=[];for(const A of o){const y=t[A]*a+e[A]*r,R=-t[A]*r+e[A]*a;f.push(y),M.push(R),i=Math.min(i,y),h=Math.max(h,y),c=Math.min(c,R),l=Math.max(l,R)}const d=Math.max(n*Math.SQRT2,Math.max(h-i,l-c)/D.MAX_CELLS),g=Math.max(1,Math.ceil((h-i)/d+1e-9)),p=Math.max(1,Math.ceil((l-c)/d+1e-9)),E=new Uint8Array(g*p),v=o.map((A,y)=>{const R=Math.min(g-1,Math.max(0,Math.floor((f[y]-i)/d))),x=Math.min(p-1,Math.max(0,Math.floor((M[y]-c)/d)));return E[x*g+R]=1,x*g+R}),w=D.largestRectangle(E,g,p);if(w===void 0)return;const m=new Set;return o.forEach((A,y)=>{const R=v[y]%g,x=Math.floor(v[y]/g);R>=w.column&&R<w.column+w.columns&&x>=w.row&&x<w.row+w.rows&&m.add(A)}),{area:w.area*d*d,angle:s,covered:m,rectangle:{minU:i+w.column*d,maxU:i+(w.column+w.columns)*d,minV:c+w.row*d,maxV:c+(w.row+w.rows)*d}}}static largestRectangle(t,e,o){const s=new Array(e).fill(0);let n;for(let a=0;a<o;a++){for(let i=0;i<e;i++)s[i]=t[a*e+i]?s[i]+1:0;const r=[];for(let i=0;i<=e;i++){const h=i===e?0:s[i];for(;r.length>0&&s[r[r.length-1]]>=h;){const c=r.pop(),l=r.length===0?i:i-r[r.length-1]-1,f=s[c]*l;(n===void 0||f>n.area)&&(n={column:i-l,row:a-s[c]+1,columns:l,rows:s[c],area:f})}r.push(i)}}return n}static roof(t){const e=[...t].sort((o,s)=>o-s);return Math.max(2,e[Math.min(e.length-1,Math.floor(e.length*D.ROOF_PERCENTILE))])}static building(t,e,o){const s=Math.cos(e),n=Math.sin(e),a=t.maxU-t.minU,r=t.maxV-t.minV,i=(t.minU+t.maxU)/2,h=(t.minV+t.maxV)/2,l=((90-(a<r?e:e+Math.PI/2)*180/Math.PI)%180+180)%180;return{eastM:D.tenth(i*s-h*n),northM:D.tenth(i*n+h*s),widthM:D.tenth(Math.max(a,r)),lengthM:D.tenth(Math.min(a,r)),heightM:D.tenth(o),headingDeg:Math.round(l*10)/10}}static smallestRectangle(t){let e={area:1/0,angle:0,rectangle:{minU:0,maxU:0,minV:0,maxV:0}};for(let o=0;o<Math.max(1,t.length);o++){const[s,n]=t[o]??[0,0],[a,r]=t[(o+1)%t.length]??[1,0],i=Math.atan2(r-n,a-s),h=Math.cos(i),c=Math.sin(i);let l=1/0,f=-1/0,M=1/0,d=-1/0;for(const[p,E]of t){const v=p*h+E*c,w=-p*c+E*h;l=Math.min(l,v),f=Math.max(f,v),M=Math.min(M,w),d=Math.max(d,w)}const g=(f-l)*(d-M);g<e.area&&(e={area:g,angle:i,rectangle:{minU:l,maxU:f,minV:M,maxV:d}})}return e}static hull(t){const e=[...t].sort((r,i)=>r[0]-i[0]||r[1]-i[1]),o=e.filter((r,i)=>i===0||r[0]!==e[i-1][0]||r[1]!==e[i-1][1]);if(o.length<3)return o;const s=(r,i,h)=>(i[0]-r[0])*(h[1]-r[1])-(i[1]-r[1])*(h[0]-r[0]),n=[];for(const r of o){for(;n.length>=2&&s(n[n.length-2],n[n.length-1],r)<=0;)n.pop();n.push(r)}const a=[];for(const r of[...o].reverse()){for(;a.length>=2&&s(a[a.length-2],a[a.length-1],r)<=0;)a.pop();a.push(r)}return[...n.slice(0,-1),...a.slice(0,-1)]}static tenth(t){return Math.round(t*10)/10}}class H{constructor(t,e,o,s){this.heights=t,this.vegetation=e,this.width=o,this.height=s}static SUMMIT_RADIUS_M=2.5;static CROWN_REACH_M=8;static MAX_WIDTH_M=16;static MIN_WIDTH_M=2;static tenth(t){return Math.round(t*10)/10}trees(t){const e=t.metresPerPixel,o=t.minHeightM??4,s=t.eyeM??1.6,n=t.minElevationDeg??2,a=Math.max(1,Math.round(H.SUMMIT_RADIUS_M/e)),r=Math.round(H.CROWN_REACH_M/e),i=this.width/2,h=this.height/2,c=[];for(let l=0;l<this.height;l++)for(let f=0;f<this.width;f++){const M=this.canopy(f,l);if(M<o)continue;const d=(f+.5-i)*e,g=(h-l-.5)*e,p=Math.hypot(d,g);if(p>t.radiusM||!this.isSummit(f,l,M,a))continue;const E=Math.atan2(M-s,Math.max(p,1))*180/Math.PI;E<n||c.push({tree:{eastM:H.tenth(d),northM:H.tenth(g),heightM:H.tenth(M),widthM:H.tenth(this.crownWidth(f,l,M,e,r))},elevation:E})}return c.sort((l,f)=>f.elevation-l.elevation),c.slice(0,t.maxTrees??150).map(l=>l.tree)}canopy(t,e){if(t<0||e<0||t>=this.width||e>=this.height)return 0;const o=e*this.width+t;return this.vegetation[o]?this.heights[o]:0}isSummit(t,e,o,s){for(let n=-s;n<=s;n++)for(let a=-s;a<=s;a++){if(a===0&&n===0)continue;const r=this.canopy(t+a,e+n);if(r>o||r===o&&(n<0||n===0&&a<0))return!1}return!0}crownWidth(t,e,o,s,n){let a=0;for(let i=-n;i<=n;i++)for(let h=-n;h<=n;h++)Math.hypot(h,i)*s<=H.CROWN_REACH_M&&this.canopy(t+h,e+i)>=o*.5&&a++;const r=2*Math.sqrt(a*s*s/Math.PI);return Math.max(H.MIN_WIDTH_M,Math.min(r,H.MAX_WIDTH_M))}}class _{static WALLS={1:"stone",2:"meuliere",3:"concrete",4:"brick",5:"block",6:"wood",9:"other"};static ROOFS={1:"tile",2:"slate",3:"zinc",4:"concrete",9:"other"};static WALL_COLOURS={stone:"#b9ae9b",meuliere:"#b8a78a",concrete:"#b4b2ac",brick:"#a65f48",block:"#d0c6b2",wood:"#7d5c3e",other:"#c9c0ae"};static PLASTER="#d3cab8";static ROOF_COLOURS={tile:"#a0583f",slate:"#4a5059",zinc:"#8d969c",concrete:"#8f8e8a",other:"#7a7872"};static VARIATION=.12;static OLD_ROOF="#7d858c";static OLD_STOREY_M=3.6;static GRAND_STOREY_M=4.5;static MIN_STOREY_M=2.4;static MAX_STOREY_M=6;static of(t,e){const o=String(t??"").trim();if(!/^\d{1,2}$/.test(o))return;const s=Number(o),n=s%10!==0?s%10:Math.floor(s/10);return(e==="wall"?_.WALLS:_.ROOFS)[String(n)]}static roofColor(t){return t?_.ROOF_COLOURS[t]:void 0}static fit(t){return t.map(e=>{const o=e.style,s={...e.facade,color:e.facade?.color??_.wallColor(e)};o==="church"?s.openings="lancet":o==="monument"||o==="works"?s.plain=!0:(o==="old"||o==="grand")&&(s.openings="tall"),e.closedAtNight&&!s.plain&&(s.closedAtNight=!0);const n=_.storeyOf(e);n!==void 0&&o!=="church"&&o!=="monument"&&(s.storeyM=n);const a=o==="old"&&e.roof?.color===void 0?{shape:"flat",riseM:0,...e.roof,color:_.OLD_ROOF}:e.roof;return{...e,facade:s,...a?{roof:a}:{}}})}static storeyOf(t){const e=t.heightM-(t.roof?.riseM??0);if(t.floors&&t.floors>0){const o=e/t.floors;if(o>=_.MIN_STOREY_M&&o<=_.MAX_STOREY_M)return Math.round(o*100)/100}return t.style==="old"?_.OLD_STOREY_M:t.style==="grand"?_.GRAND_STOREY_M:void 0}static wallColor(t){const e=t.style==="church"||t.style==="monument"||t.style==="old",o=t.wallMaterial?_.WALL_COLOURS[t.wallMaterial]:e?_.WALL_COLOURS.stone:_.PLASTER,s=Math.sin(t.eastM*12.9898+t.northM*78.233)*43758.5453,n=1+(s-Math.floor(s)-.5)*2*_.VARIATION,a=r=>Math.max(0,Math.min(255,Math.round(parseInt(o.slice(r,r+2),16)*n))).toString(16).padStart(2,"0");return`#${a(1)}${a(3)}${a(5)}`}}class N{static MIN_RISE_M=1.2;static MIN_EAVES_M=2.5;static MIN_SEEN=.35;static NO_DATA=-9999;static BANDS=7;static INSET_M=.9;static LIFT=1.18;static PEAK=.3;static LEVEL=.45;static fit(t,e,o){return!e&&!o&&!t.some(s=>s.roofMaterial)?t:t.map(s=>{const n=e?N.shapeOf(s,e):void 0,a=(o?N.colorOf(s,o):void 0)??_.roofColor(s.roofMaterial);if(!n&&!a)return s;const r={shape:n?.shape??"flat",...n?.ridge==="depth"?{ridge:"depth"}:{},riseM:n?.riseM??0,...a?{color:a}:{}},i=n&&n.shape!=="flat"?n.topM:s.heightM;return{...s,heightM:i,roof:r}})}static world(t,e,o){const s=t.headingDeg*Math.PI/180;return{east:t.eastM+e*Math.cos(s)+o*Math.sin(s),north:t.northM-e*Math.sin(s)+o*Math.cos(s)}}static shapeOf(t,e){const o=N.BANDS,s=Math.max(e.metresPerPixel,.5),n=t.widthM/2-N.INSET_M,a=t.lengthM/2-N.INSET_M;if(n<=0||a<=0)return;const r=[],i=Array.from({length:o},()=>({sum:0,n:0})),h=Array.from({length:o},()=>({sum:0,n:0}));let c=0;for(let y=-n;y<=n;y+=s)for(let R=-a;R<=a;R+=s){c++;const{east:x,north:$}=N.world(t,y,R),z=Math.floor(x/e.metresPerPixel+e.size/2),Y=Math.floor(e.size/2-$/e.metresPerPixel);if(z<0||Y<0||z>=e.size||Y>=e.size)continue;const U=Y*e.size+z,P=e.heights[U];if(e.vegetation[U]||P===N.NO_DATA||!(P>0))continue;r.push(P);const J=i[Math.min(o-1,Math.floor((y+n)/(2*n)*o))],Q=h[Math.min(o-1,Math.floor((R+a)/(2*a)*o))];J.sum+=P,J.n++,Q.sum+=P,Q.n++}if(c===0||r.length/c<N.MIN_SEEN)return;r.sort((y,R)=>y-R);const l=N.percentile(r,.08),f=N.percentile(r,.92),M=f-l;if(l<N.MIN_EAVES_M||M>Math.min(t.widthM,t.lengthM))return;if(M<N.MIN_RISE_M)return{shape:"flat",riseM:0,topM:f};const d=N.profile(i),g=N.profile(h);if(!d||!g)return;const p=N.peak(g)/M,E=N.peak(d)/M,v=N.spread(d)/M,w=N.spread(g)/M,m=p>=N.PEAK&&v<=N.LEVEL,A=E>=N.PEAK&&w<=N.LEVEL;if(m&&(!A||p>=E))return{shape:"gable",ridge:"width",riseM:M,topM:f};if(A)return{shape:"gable",ridge:"depth",riseM:M,topM:f};if(p>=N.PEAK&&E>=N.PEAK/2)return{shape:"hip",ridge:"width",riseM:M,topM:f}}static profile(t){const e=t.map(o=>o.n>0?o.sum/o.n:NaN);return e.filter(Number.isFinite).length>=t.length-1?e.map((o,s)=>Number.isFinite(o)?o:N.neighbour(e,s)):void 0}static neighbour(t,e){return Number.isFinite(t[e-1])?t[e-1]:t[e+1]}static peak(t){const e=Math.max(1,Math.floor(t.length/3)),o=n=>n.reduce((a,r)=>a+r,0)/n.length,s=[...t.slice(0,e),...t.slice(t.length-e)];return o(t.slice(e,t.length-e))-o(s)}static spread(t){return Math.max(...t)-Math.min(...t)}static percentile(t,e){return t[Math.min(t.length-1,Math.floor(t.length*e))]}static colorOf(t,e){const o=Math.max(e.metresPerPixel,.5),s=Math.max(t.widthM/2-N.INSET_M,o),n=Math.max(t.lengthM/2-N.INSET_M,o),a=[],r=[],i=[];for(let l=-s;l<=s;l+=o)for(let f=-n;f<=n;f+=o){const{east:M,north:d}=N.world(t,l,f),g=Math.floor(M/e.metresPerPixel+e.size/2),p=Math.floor(e.size/2-d/e.metresPerPixel);if(g<0||p<0||g>=e.size||p>=e.size)continue;const E=(p*e.size+g)*e.samples,v=e.values[E],w=e.values[E+1],m=e.values[E+2];w>v&&w>m||(a.push(v),r.push(w),i.push(m))}if(a.length===0)return;const h=l=>l.sort((f,M)=>f-M)[Math.floor(l.length/2)],c=l=>Math.min(255,Math.round(l*N.LIFT)).toString(16).padStart(2,"0");return`#${c(h(a))}${c(h(r))}${c(h(i))}`}}class G{static DEFAULT_HEIGHT_M=6;static METRES_PER_DEGREE=111320;static STREET_SPACING_M=35;static KERB_OFFSET_M=1;static DEFAULT_WIDTH_M=5.5;static PATIENCE_MS=15e3;static MAX=150;static lampsFrom(t,e,o){const s=[],n=new Set;for(const r of t.elements??[]){if(r.id!==void 0){const l=`${r.type}/${r.id}`;if(n.has(l))continue;n.add(l)}if(r.type==="way"){r.tags?.lit==="yes"&&G.alongStreet(r,e,s);continue}if(r.tags?.highway!=="street_lamp"||typeof r.lat!="number"||typeof r.lon!="number")continue;const i=(r.lon-e.lng)*G.METRES_PER_DEGREE*Math.cos(e.lat*Math.PI/180),h=(r.lat-e.lat)*G.METRES_PER_DEGREE;if(Math.hypot(i,h)>o)continue;const c=Number.parseFloat(r.tags?.height??"");s.push({eastM:Math.round(i*10)/10,northM:Math.round(h*10)/10,...Number.isFinite(c)&&c>=2&&c<=20?{heightM:c}:{}})}const a=s.filter(r=>Math.hypot(r.eastM,r.northM)<=o);return s.length=0,s.push(...a),s.sort((r,i)=>Math.hypot(r.eastM,r.northM)-Math.hypot(i.eastM,i.northM)),s.slice(0,G.MAX)}static alongStreet(t,e,o){const s=t.geometry;if(!s||s.length<2)return;const n=d=>[(d.lon-e.lng)*G.METRES_PER_DEGREE*Math.cos(e.lat*Math.PI/180),(d.lat-e.lat)*G.METRES_PER_DEGREE],a=s.map(n),r=Number.parseFloat(t.tags?.width??""),i=Number.parseInt(t.tags?.lanes??"",10),c=(Number.isFinite(r)&&r>0?r:Number.isFinite(i)&&i>0?i*3.5:G.DEFAULT_WIDTH_M)/2+G.KERB_OFFSET_M;let l=1,f=G.STREET_SPACING_M/2,M=0;for(let d=1;d<a.length;d++){const[g,p]=a[d-1],[E,v]=a[d],w=Math.hypot(E-g,v-p);if(w===0)continue;const m=(E-g)/w,A=(v-p)/w;for(;f<=M+w;f+=G.STREET_SPACING_M){const y=f-M;o.push({eastM:Math.round((g+m*y+A*c*l)*10)/10,northM:Math.round((p+A*y-m*c*l)*10)/10}),l=-l}M+=w}}}class u{constructor(t={},e=[],o=[]){this.lampSources=e,this.signalSources=o,this.client=new B(t)}static METRES_PER_DEGREE=111320;static KERB_OFFSET_M=.6;static SETBACK_M=.5;static MIN_CROSSING_M=3;static MAX_CROSSING_M=30;static SAME_CROSSING_M=5;static SAME_HEAD_M=2.5;static SAME_HEAD_DEG=45;static MAX_SIGNALS=160;static MAX_CROSSINGS=150;static MAX_SIGNS=100;static ROADS=new Set([...q.DRIVABLE,"living_street","busway","road"]);static ON_ROAD="^(traffic_signals|crossing|stop|give_way)$";static CREDIT={name:"OpenStreetMap",url:"https://www.openstreetmap.org/copyright",licence:"ODbL"};client;local(t,e){const o=u.boxOf(t,e),s=(r,i)=>{if(!r)return Promise.resolve(void 0);const h=`${r.credit.url} ${o}`;let c=u.localPending.get(h);return c||(c=i().then(l=>({credit:r.credit,found:l}),()=>{u.localPending.delete(h)}),u.localPending.set(h,c)),c},n=this.lampSources.find(r=>r.covers(t.lat,t.lng)),a=this.signalSources.find(r=>r.covers(t.lat,t.lng));return Promise.all([s(n,()=>n.lampsNear(t,e)),s(a,()=>a.signalsNear(t,e))]).then(([r,i])=>({lamps:r,signals:i}))}static localPending=new Map;static withLocal(t,e){const o=e?.lamps?e.lamps.found:t.lamps,s=e?.signals?e.signals.found:t.signals,a=[...u.found({crossings:t.crossings,signs:t.signs,...e?.lamps?{}:{lamps:o},...e?.signals?{}:{signals:s}})?[u.CREDIT]:[],...e?.lamps&&e.lamps.found.length>0?[e.lamps.credit]:[],...e?.signals&&e.signals.found.length>0?[e.signals.credit]:[]],{lamps:r,signals:i,credits:h,...c}=t;return{...c,...o?.length?{lamps:o}:{},...s?.length?{signals:s}:{},...a.length>0?{credits:a}:{}}}static today(){return new Date().toISOString().slice(0,10)}static later(t,e,o){return t?.answered(e,o).then(s=>({...s,...u.found(s)?{dates:{lamps:u.today()}}:{}}))}static found(t){return!!(t?.lamps?.length||t?.signals?.length||t?.crossings?.length||t?.signs?.length)}static query(t){const e=u.ON_ROAD;return`[out:json][timeout:25];(node["highway"="street_lamp"](${t});way["highway"]["lit"="yes"](${t});node["highway"~"${e}"](${t});node["traffic_sign"](${t});way["footway"="crossing"](${t}););out geom;(node._["highway"~"${e}"];node._["traffic_sign"];)->.kerb;way(bn.kerb)["highway"];out geom;`}static pending=new Map;async near(t,e){const o=this.ask(u.boxOf(t,e)),s=this.local(t,e);let n;const a=Symbol("late"),r=new Promise(i=>{n=setTimeout(()=>i(a),G.PATIENCE_MS)});try{const[i,h]=await Promise.all([Promise.race([o,r]),Promise.race([s,r])]),c=h===a?void 0:h;return i===a||i===void 0?{...u.withLocal({},c),incomplete:!0}:u.withLocal(u.from(i,t,e),c)}finally{clearTimeout(n)}}static RETRY_DELAYS_MS=[15e3,6e4,18e4,6e5];async answered(t,e){const o=u.boxOf(t,e);for(let s=0;;s++){const n=await this.ask(o);if(n)return u.withLocal(u.from(n,t,e),await this.local(t,e));if(s>=u.RETRY_DELAYS_MS.length)throw new Error("Overpass did not answer");await new Promise(a=>setTimeout(a,u.RETRY_DELAYS_MS[s]))}}ask(t){let e=u.pending.get(t);return e||(e=this.client.query(u.query(t)).catch(()=>{u.pending.delete(t)}),u.pending.set(t,e)),e}static boxOf(t,e){const o=e/u.METRES_PER_DEGREE,s=e/(u.METRES_PER_DEGREE*Math.cos(t.lat*Math.PI/180));return[t.lat-o,t.lng-s,t.lat+o,t.lng+s].map(n=>n.toFixed(6)).join(",")}static forget(){u.pending.clear(),u.localPending.clear()}static from(t,e,o){const s=G.lampsFrom(t,e,o),n=Math.cos(e.lat*Math.PI/180),a=(m,A)=>[(A-e.lng)*u.METRES_PER_DEGREE*n,(m-e.lat)*u.METRES_PER_DEGREE],r=(m,A)=>`${m.toFixed(7)},${A.toFixed(7)}`,i=new Map,h=new Set,c=[],l=[];for(const m of t.elements??[]){if(m.type==="node"){typeof m.lat=="number"&&typeof m.lon=="number"&&l.push(m);continue}if(m.type!=="way"||!m.geometry||m.geometry.length<2)continue;if(m.id!==void 0){if(h.has(m.id))continue;h.add(m.id)}const A=m.tags??{};if(A.footway==="crossing"&&c.push(m),!u.ROADS.has(A.highway??""))continue;const y=m.geometry.map(R=>a(R.lat,R.lon));m.geometry.forEach((R,x)=>{const $=r(R.lat,R.lon);i.set($,[...i.get($)??[],{way:m,points:y,index:x}])})}const f=[],M=[],d=[],g=new Set;for(const m of l){if(m.id!==void 0){if(g.has(m.id))continue;g.add(m.id)}const A=m.tags??{},y=a(m.lat,m.lon),R=i.get(r(m.lat,m.lon))??[],x=A.highway;if(x==="traffic_signals")for(const U of u.approaches(R,u.directionOf(A),!1))f.push(u.head(y,U,"vehicles"));if(x==="crossing"||x==="traffic_signals"&&A.crossing!==void 0){const U=R[0];if(U&&u.marked(A)){const P=u.directionAt(U);P&&M.push({eastM:u.round(y[0]),northM:u.round(y[1]),headingDeg:u.heading(P),widthM:q.widthOf(U.way.tags??{})}),P&&(A.crossing==="traffic_signals"||A["crossing:signals"]==="yes")&&f.push(...u.pedestrianHeads(y,P,U))}}if(x==="traffic_signals")continue;const $=x==="stop"?{face:"stop"}:x==="give_way"?{face:"give-way"}:u.faceOf(A);if(!$)continue;const z=A.traffic_sign?{code:A.traffic_sign}:{};if(R.length>0){const U=u.approaches(R,u.directionOf(A),!0)[0];if(U){const P=u.head(y,U,"vehicles");d.push({eastM:P.eastM,northM:P.northM,headingDeg:P.headingDeg,...$,...z})}continue}const Y=u.compassOf(A.direction);Y!==void 0&&d.push({eastM:u.round(y[0]),northM:u.round(y[1]),headingDeg:(Y+180)%360,...$,...z})}for(const m of c){const A=u.crossingAlong(m,a);A&&!M.some(y=>Math.hypot(y.eastM-A.eastM,y.northM-A.northM)<u.SAME_CROSSING_M)&&M.push(A)}const p=f.filter((m,A)=>!f.slice(0,A).some(y=>y.for===m.for&&Math.hypot(y.eastM-m.eastM,y.northM-m.northM)<u.SAME_HEAD_M&&Math.abs((y.headingDeg-m.headingDeg+540)%360-180)<u.SAME_HEAD_DEG)),E=(m,A)=>m.filter(y=>Math.hypot(y.eastM,y.northM)<=o).sort((y,R)=>Math.hypot(y.eastM,y.northM)-Math.hypot(R.eastM,R.northM)).slice(0,A),v={};s.length>0&&(v.lamps=s);const w={signals:E(p,u.MAX_SIGNALS),crossings:E(M,u.MAX_CROSSINGS),signs:E(d,u.MAX_SIGNS)};return w.signals.length>0&&(v.signals=w.signals),w.crossings.length>0&&(v.crossings=w.crossings),w.signs.length>0&&(v.signs=w.signs),v}static approaches(t,e,o){const s=[];for(const a of t){const r=a.way.tags??{},i=r.oneway==="yes"||r.oneway==="true"||r.oneway==="1"||r.junction==="roundabout"||r.highway==="motorway",h=r.oneway==="-1",{points:c,index:l}=a,f=(d,g)=>{let p=0;for(let E=Math.min(d,g);E<Math.max(d,g);E++)p+=Math.hypot(c[E+1][0]-c[E][0],c[E+1][1]-c[E][1]);return p},M=d=>{const g=c[l][0]-c[d][0],p=c[l][1]-c[d][1],E=Math.hypot(g,p);return E>0?[g/E,p/E]:void 0};if(l>0&&!h){const d=M(l-1);d&&s.push({approach:{unit:d,road:a},toEndM:f(l,c.length-1),forward:!0})}if(l<c.length-1&&!i){const d=M(l+1);d&&s.push({approach:{unit:d,road:a},toEndM:f(0,l),forward:!1})}}const n=e!==void 0&&t.length===1?s.filter(a=>a.forward===(e==="forward")):s;return!o||n.length<=1?n.map(a=>a.approach):[n.reduce((a,r)=>r.toEndM<a.toEndM?r:a).approach]}static head(t,e,o){const[s,n]=e.unit,a=q.widthOf(e.road.way.tags??{})/2+u.KERB_OFFSET_M,r=u.SETBACK_M,i=u.heading(e.unit);return{eastM:u.round(t[0]-s*r+n*a),northM:u.round(t[1]-n*r-s*a),headingDeg:(i+180)%360,for:o,phase:j.phaseOf(i)}}static pedestrianHeads(t,e,o){const s=q.widthOf(o.way.tags??{})/2+u.KERB_OFFSET_M,n=u.heading(e),a=j.phaseOf(n);return[1,-1].map(r=>({eastM:u.round(t[0]+e[1]*s*r),northM:u.round(t[1]-e[0]*s*r),headingDeg:(n+(r===1?270:90))%360,for:"pedestrians",phase:a}))}static crossingAlong(t,e){if(!u.marked(t.tags??{}))return;const o=t.geometry.map(h=>e(h.lat,h.lon)),[s,n]=o[0],[a,r]=o[o.length-1],i=Math.hypot(a-s,r-n);if(!(i<u.MIN_CROSSING_M))return{eastM:u.round((s+a)/2),northM:u.round((n+r)/2),headingDeg:(u.heading([(a-s)/i,(r-n)/i])+90)%360,widthM:Math.round(Math.min(i,u.MAX_CROSSING_M)*10)/10}}static marked(t){const e=t.crossing;return e!=="unmarked"&&e!=="no"&&e!=="informal"&&t["crossing:markings"]!=="no"}static directionAt(t){const{points:e,index:o}=t,[s,n]=o<e.length-1?[e[o],e[o+1]]:[e[o-1],e[o]],a=Math.hypot(n[0]-s[0],n[1]-s[1]);return a>0?[(n[0]-s[0])/a,(n[1]-s[1])/a]:void 0}static directionOf(t){const e=t["traffic_signals:direction"]??t["traffic_sign:direction"]??t.direction;return e==="forward"||e==="backward"?e:void 0}static compassOf(t){if(t===void 0)return;const e=Number.parseFloat(t);if(Number.isFinite(e)&&/^\s*-?\d/.test(t))return(e%360+360)%360;const s=["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"].indexOf(t.trim().toUpperCase());return s<0?void 0:s*22.5}static faceOf(t){const e=t.traffic_sign;if(!e||e==="no"||e==="none")return;const o=e.split(/[;,]/).map(s=>s.trim());if(o.some(s=>/stop|:AB4\b|:206\b|:R1-1\b|:B2\b/i.test(s)))return{face:"stop"};if(o.some(s=>/give_way|yield|:AB3a\b|:205\b|:R1-2\b|:B1\b/i.test(s)))return{face:"give-way"};for(const s of o){const n=/maxspeed|:B14\b|:274\b|:C14\b|:R2-1\b/i.test(s)?Number.parseInt(/\[(\d+)\]/.exec(s)?.[1]??t.maxspeed??"",10):Number.NaN;if(Number.isFinite(n)&&n>0)return{face:"speed-limit",value:n}}return{face:"other"}}static heading([t,e]){return Math.round((Math.atan2(t,e)*180/Math.PI+360)%360*10)/10}static round(t){return Math.round(t*10)/10}}class ot{id="ign-lidar-hd";name="IGN LiDAR HD";attribution="Canopy and roofs © IGN, LiDAR HD (Licence Ouverte 2.0), as surveyed on a day the service does not give";creditUrl="https://geoservices.ign.fr/lidarhd";contemporary=!0;platform;furniture;constructor(t={},e){this.platform=new C(t),this.furniture=e}covers(t,e){return C.covers(t,e)}later(t,e){return u.later(this.furniture,t,e)}async getSurroundings(t,e){if(!this.covers(t.lat,t.lng))throw new V("no-coverage","The LiDAR HD survey covers France only");const o=C.lidarRadius(e),[s,n,a,r,i]=await Promise.all([this.platform.lidar(t,o),this.platform.photo(t,o).catch(()=>{}),this.furniture?.near(t,e),this.platform.laserDate(t),this.platform.photoDate(t)]);return{trees:new H(s.heights,s.vegetation,s.size,s.size).trees({metresPerPixel:s.metresPerPixel,radiusM:o}),buildings:_.fit(N.fit(new D(s.heights,s.building,s.size,s.size).buildings({metresPerPixel:s.metresPerPixel,radiusM:o,maxBuildings:300}),s,n)),bridges:[],lines:[],...a,dates:{...r?{laser:r}:{},...n&&i?{photo:i}:{},...u.found(a)?{lamps:u.today()}:{}}}}}class O{static MAP_NATURES={Eglise:"church",Chapelle:"church",Monument:"monument","Tour, donjon":"monument","Fort, blockhaus, casemate":"monument","Arc de triomphe":"monument","Arène ou théâtre antique":"monument",Château:"old","Industriel, agricole ou commercial":"works",Serre:"works",Silo:"works"};static MAP_WORKS=new Set(["Industriel","Agricole","Annexe"]);static MAP_CLOSED_AT_NIGHT=new Set(["Commercial et services","Sportif"]);static OSM_CLOSED_AT_NIGHT=new Set(["retail","commercial","office","supermarket","kiosk","sports_hall","sports_centre","stadium","school","university","college","kindergarten","public","civic","government","train_station","transportation"]);static OSM_BUILDINGS={cathedral:"church",church:"church",chapel:"church",basilica:"church",temple:"church",mosque:"church",synagogue:"church",shrine:"church",religious:"church",castle:"old",palace:"old",fortress:"monument",bunker:"monument",triumphal_arch:"monument",industrial:"works",warehouse:"works",barn:"works",farm_auxiliary:"works",shed:"works",hangar:"works",garage:"works",garages:"works",greenhouse:"works",silo:"works",storage_tank:"works",cowshed:"works",stable:"works",sty:"works",manufacture:"works"};static OSM_HISTORIC={church:"church",chapel:"church",monastery:"old",castle:"old",manor:"old",fort:"monument",tower:"monument",monument:"monument",memorial:"monument",city_gate:"monument"};static OLD_BEFORE=1900;static GRAND_FROM_M=15;static GRAND_UP_TO_M=40;static TOWER_SLENDERNESS=2;static CHURCH_PITCH=.7;static CHURCH_ROOF_OF_WALLS=.35;static CHURCH_ROOF_COLOUR="#5a6068";static ofMap(t){const e=O.year(t.date_d_apparition),s=O.MAP_NATURES[String(t.nature??"")]??(t.usage_1==="Religieux"?"church":void 0)??(O.MAP_WORKS.has(String(t.usage_1??""))?"works":void 0)??O.ofYear(e)??O.grand(t),n=O.MAP_CLOSED_AT_NIGHT.has(String(t.usage_1??""));return{...s?{style:s}:{},...e!==void 0?{builtYear:e}:{},...n?{closedAtNight:!0}:{}}}static ofOsm(t){const e=O.year(t.start_date),o=t.historic&&t.historic!=="no"?O.OSM_HISTORIC[t.historic]??"old":void 0,s=t.amenity==="place_of_worship"?"church":void 0,n=O.OSM_BUILDINGS[t.building??""]??o??s??O.ofYear(e),a=O.OSM_CLOSED_AT_NIGHT.has(t.building??"")||t.shop!==void 0&&t.shop!=="no";return{...n?{style:n}:{},...e!==void 0?{builtYear:e}:{},...a?{closedAtNight:!0}:{}}}static year(t){const e=Number(/\b(\d{4})\b/.exec(String(t??""))?.[1]);return Number.isFinite(e)&&e>=1e3&&e<=2100?e:void 0}static fit(t){return t.map(e=>e.style==="church"?O.church(e):e)}static isTower(t){return t.heightM>=O.TOWER_SLENDERNESS*Math.max(t.widthM,t.lengthM)}static church(t){const e=t.roof?.color??O.CHURCH_ROOF_COLOUR;if(O.isTower(t))return{...t,roof:{shape:"flat",riseM:0,color:e}};if(t.roof&&t.roof.shape!=="flat")return t;const o=Math.round(Math.min(O.CHURCH_PITCH*t.lengthM,O.CHURCH_ROOF_OF_WALLS*t.heightM)*10)/10,s={shape:"gable",riseM:o,color:e};return{...t,heightM:Math.round((t.heightM+o)*10)/10,roof:s}}static ofYear(t){return t!==void 0&&t<O.OLD_BEFORE?"old":void 0}static grand(t){const e=Number(t.hauteur),o=Number(t.nombre_d_etages);if(!(Number.isFinite(o)&&o>0)&&!(t.usage_1==="Résidentiel"||t.date_d_apparition))return e>=O.GRAND_FROM_M&&e<=O.GRAND_UP_TO_M?"grand":void 0}}class b{static METRES_PER_DEGREE=111320;static NAMED_NATURES={Silo:"Silo"};static DEFAULT_HEIGHT_M=6;static FLOOR_HEIGHT_M=3;static FINE_M=.5;static COARSE_M=1;static MAX_CELLS=4e4;static RECTANGLE_FILL=.9;static buildings(t,e,o,s=12,n=300,a=[]){const r=[];for(const h of b.reached(t,a,e,o)){const c=h.feature,l=Number(c.properties.nombre_d_etages),f=Number(c.properties.hauteur),M=Number.isFinite(f)&&f>0?f:Number.isFinite(l)&&l>0?l*b.FLOOR_HEIGHT_M:b.DEFAULT_HEIGHT_M,{style:d,builtYear:g,closedAtNight:p}=O.ofMap(c.properties);for(const E of b.rectanglesOf(h.rings,M,s)){const v=_.of(c.properties.materiaux_des_murs,"wall"),w=_.of(c.properties.materiaux_de_la_toiture,"roof"),m=b.NAMED_NATURES[String(c.properties.nature??"")];r.push({...E,...m?{name:m}:{},...Number.isFinite(l)&&l>0?{floors:l}:{},...v?{wallMaterial:v}:{},...w?{roofMaterial:w}:{},...d?{style:d}:{},...g!==void 0?{builtYear:g}:{},...p?{closedAtNight:p}:{}})}}const i=h=>Math.hypot(h.eastM,h.northM);if(r.length>n){const h=c=>(c.heightM+6)*Math.sqrt(c.widthM*c.lengthM)/Math.max(i(c),50);r.sort((c,l)=>h(l)-h(c)),r.length=n}return r.sort((h,c)=>i(h)-i(c)),r}static named(t,e,o){const s=e.map(n=>({name:typeof n.properties.toponyme=="string"?n.properties.toponyme:"",rings:b.rings(n,o)})).filter(n=>n.name.length>0&&n.rings.length>0);return s.length===0?t:t.map(n=>{const a=s.find(r=>b.inside(n.eastM,n.northM,r.rings));return a?{...n,name:a.name}:n})}static reached(t,e,o,s){const n=t.map(i=>({feature:i,rings:b.rings(i,o)})).filter(i=>i.rings.length>0),a=n.map(i=>b.reach(i.rings)<=s),r=e.filter(i=>typeof i.properties.toponyme=="string"&&i.properties.toponyme.length>0).map(i=>b.rings(i,o)).filter(i=>i.length>0);if(r.length>0){const i=n.map(c=>{const l=c.rings[0],f=l.reduce((d,g)=>d+g.east,0)/l.length,M=l.reduce((d,g)=>d+g.north,0)/l.length;return r.findIndex(d=>b.inside(f,M,d))}),h=new Set(i.filter((c,l)=>c>=0&&a[l]));i.forEach((c,l)=>{h.has(c)&&(a[l]=!0)})}return n.filter((i,h)=>a[h])}static reach(t){if(b.inside(0,0,t))return 0;let e=1/0;for(const o of t)for(let s=0;s+1<o.length;s++){const n=o[s],a=o[s+1],r=a.east-n.east,i=a.north-n.north,h=r*r+i*i>0?Math.max(0,Math.min(1,-(n.east*r+n.north*i)/(r*r+i*i))):0;e=Math.min(e,Math.hypot(n.east+h*r,n.north+h*i))}return e}static inside(t,e,o){let s=0;for(const n of o)for(let a=0;a+1<n.length;a++){const r=n[a],i=n[a+1];r.north<=e!=i.north<=e&&r.east+(e-r.north)/(i.north-r.north)*(i.east-r.east)>t&&s++}return s%2===1}static rings(t,e){const o=t.geometry,s=o.type==="Polygon"?[o.coordinates]:o.type==="MultiPolygon"?o.coordinates:[],n=Math.cos(e.lat*Math.PI/180),a=[];for(const r of s)for(const i of r)a.push(i.map(([h,c])=>({east:(h-e.lng)*b.METRES_PER_DEGREE*n,north:(c-e.lat)*b.METRES_PER_DEGREE})));return a}static rectanglesOf(t,e,o){let s=1/0,n=-1/0,a=1/0,r=-1/0;for(const f of t)for(const M of f)s=Math.min(s,M.east),n=Math.max(n,M.east),a=Math.min(a,M.north),r=Math.max(r,M.north);let i=b.FINE_M;(n-s)*(r-a)/(i*i)>b.MAX_CELLS&&(i=b.COARSE_M);const h=[],c=[];for(let f=a+i/2;f<r;f+=i){const M=[];for(const d of t)for(let g=0;g+1<d.length;g++){const p=d[g],E=d[g+1];p.north<=f!=E.north<=f&&M.push(p.east+(f-p.north)/(E.north-p.north)*(E.east-p.east))}M.sort((d,g)=>d-g);for(let d=0;d+1<M.length;d+=2)for(let g=Math.ceil((M[d]-i/2)/i)*i+i/2;g<M[d+1];g+=i)h.push(g),c.push(f)}if(h.length*i*i<o)return[];const l=t.flatMap(f=>f.slice(0,-1).map((M,d)=>({from:M,to:f[d+1]})));return D.ofPoints(h,c,new Array(h.length).fill(e),i,o,b.RECTANGLE_FILL,l)}}class F{id="ign-lidar-topo";name="IGN LiDAR HD + BD TOPO";attribution="Canopy © IGN, LiDAR HD; buildings, bridges and lines © IGN, BD TOPO (Licence Ouverte 2.0), as surveyed on days the services do not give";creditUrl="https://geoservices.ign.fr/lidarhd";contemporary=!0;static BRIDGE_MARGIN_M=30;static MAX_BRIDGE_WINDOWS=8;platform;furniture;constructor(t={},e){this.platform=new C(t),this.furniture=e}covers(t,e){return C.covers(t,e)}later(t,e){return u.later(this.furniture,t,e)}async getSurroundings(t,e){if(!this.covers(t.lat,t.lng))throw new V("no-coverage","The IGN's surveys cover France only");const o=C.radius(e),[s,n,a,r,i,h]=await Promise.allSettled([this.platform.lidar(t,o),this.platform.vectors(t,o,!0),this.platform.photo(t,o),this.furniture?.near(t,e)??Promise.resolve(void 0),this.platform.laserDate(t),this.platform.photoDate(t)]),c=C.lidarRadius(o);if(s.status==="rejected"&&n.status==="rejected")throw s.reason;const l=s.status==="fulfilled"?s.value:void 0,f=n.status==="fulfilled"?n.value:void 0,M=f!==void 0&&f.buildings.length===0&&f.bridges.length===0&&f.lines.length===0;if(s.status==="rejected"&&s.reason instanceof V&&s.reason.reason==="no-coverage"&&M)throw s.reason;return{trees:l?new H(l.heights,l.vegetation,l.size,l.size).trees({metresPerPixel:l.metresPerPixel,radiusM:c}):[],buildings:this.roofed(this.buildings(l,f,t,o),l,a.status==="fulfilled"?a.value:void 0),bridges:f?await this.bridges(f,t,o,l?.structures,c):[],lines:f?X.lines(f,t,o,l?.structures):[],...r.status==="fulfilled"?r.value:{},dates:{...s.status==="fulfilled"&&i.status==="fulfilled"&&i.value?{laser:i.value}:{},...a.status==="fulfilled"&&h.status==="fulfilled"&&h.value?{photo:h.value}:{},...f?.mapDate?{map:f.mapDate}:{},...r.status==="fulfilled"&&u.found(r.value)?{lamps:u.today()}:{}}}}async bridges(t,e,o,s,n){const a=X.bridges(t,e,o,s);let r=0;return Promise.all(a.map(async i=>{if(s!==void 0&&Math.hypot(i.eastM,i.northM)+i.lengthM/2<=n)return i;const h=i.lengthM/2+F.BRIDGE_MARGIN_M;if(r++>=F.MAX_BRIDGE_WINDOWS)return i;const c=tt(i.eastM,-i.northM,e.lat,e.lng);try{const l=await this.platform.lidar(c,h),f=X.bridges(t,c,h,l.structures).sort((M,d)=>Math.hypot(M.eastM,M.northM)-Math.hypot(d.eastM,d.northM))[0];return!f||Math.hypot(f.eastM,f.northM)>i.lengthM/2?i:{...f,eastM:X.tenth(f.eastM+i.eastM),northM:X.tenth(f.northM+i.northM)}}catch{return i}}))}roofed(t,e,o){return _.fit(O.fit(N.fit(t,e,o)))}buildings(t,e,o,s){const n=e?b.buildings(e.buildings,o,s,void 0,void 0,e.zones):[],a=n.length>0||t===void 0?n:new D(t.heights,t.building,t.size,t.size).buildings({metresPerPixel:t.metresPerPixel,radiusM:C.lidarRadius(s),maxBuildings:300});return e?b.named(a,e.zones,o):a}}class nt{id="ign-bd-topo";name="IGN BD TOPO";attribution="Buildings, bridges and lines © IGN, BD TOPO (Licence Ouverte 2.0), as drawn on a day the service does not give";creditUrl="https://geoservices.ign.fr/bdtopo";contemporary=!0;platform;furniture;constructor(t={},e){this.platform=new C(t),this.furniture=e}covers(t,e){return C.covers(t,e)}later(t,e){return u.later(this.furniture,t,e)}async getSurroundings(t,e){if(!this.covers(t.lat,t.lng))throw new V("no-coverage","BD TOPO covers France only");const o=C.radius(e),[s,n,a,r]=await Promise.all([this.platform.vectors(t,o,!0),this.platform.photo(t,o).catch(()=>{}),this.furniture?.near(t,e),this.platform.photoDate(t)]);return{trees:[],buildings:_.fit(O.fit(N.fit(b.named(b.buildings(s.buildings,t,o,void 0,void 0,s.zones),s.zones,t),void 0,n))),bridges:X.bridges(s,t,o,void 0),lines:X.lines(s,t,o,void 0),...a,dates:{...s.mapDate?{map:s.mapDate}:{},...n&&r?{photo:r}:{},...u.found(a)?{lamps:u.today()}:{}}}}}class S{static ROOF_SHAPES={flat:"flat",gabled:"gable",gambrel:"gable",saltbox:"gable","half-hipped":"hip",hipped:"hip",pyramidal:"hip",mansard:"hip",round:"gable",dome:"dome",onion:"dome"};static WALL_MATERIALS={brick:"brick",stone:"stone",granite:"stone",sandstone:"stone",limestone:"stone",concrete:"concrete",cement_block:"block",block:"block",wood:"wood",timber_framing:"wood"};static ROOF_MATERIALS={roof_tiles:"tile",tiles:"tile",tile:"tile",slate:"slate",metal:"zinc",zinc:"zinc",tin:"zinc",copper:"zinc",concrete:"concrete",eternit:"other"};static COLOURS={white:"#e8e6e0",grey:"#8c8c8a",gray:"#8c8c8a",black:"#3a3a3a",red:"#a8483a",brown:"#6e4f38",beige:"#d6c9a8",yellow:"#d8c070",orange:"#c9773a",green:"#5f7a52",blue:"#506a8a",tan:"#c0a37a",cream:"#e6dcc0",pink:"#c99a96",silver:"#b4b6b6"};static height(t){const e=Number.parseFloat((t??"").replace(",","."));return Number.isFinite(e)&&e>=2&&e<=500?e:void 0}static levels(t){const e=Number.parseInt(t??"",10);return Number.isFinite(e)&&e>=1&&e<=150?e:void 0}static roofShape(t){return S.ROOF_SHAPES[(t??"").trim().toLowerCase()]}static wallMaterial(t){return S.WALL_MATERIALS[(t??"").trim().toLowerCase()]}static roofMaterial(t){return S.ROOF_MATERIALS[(t??"").trim().toLowerCase()]}static colour(t){const e=(t??"").trim().toLowerCase();return/^#[0-9a-f]{6}$/.test(e)?e:/^#[0-9a-f]{3}$/.test(e)?`#${e[1]}${e[1]}${e[2]}${e[2]}${e[3]}${e[3]}`:S.COLOURS[e]}}class W{id="osm-buildings";name="OpenStreetMap buildings";attribution="Buildings © OpenStreetMap contributors (ODbL), as drawn on the day of the data, which a recording is not";creditUrl="https://www.openstreetmap.org/copyright";contemporary=!0;static MAX_RADIUS_M=1500;static MIN_AREA_M2=12;static MAX_BUILDINGS=300;client;furniture;constructor(t={},e){this.client=new B(t),this.furniture=e}covers(){return!0}later(t,e){return u.later(this.furniture,t,e)}async getSurroundings(t,e){const o=Math.min(e,W.MAX_RADIUS_M);let s;try{s=await this.client.query(`[out:json][timeout:30];(way["building"](around:${o},${t.lat},${t.lng});relation["building"]["type"="multipolygon"](around:${o},${t.lat},${t.lng}););out geom;`)}catch(r){throw new V("unreachable",r instanceof Error?r.message:String(r))}const n=await this.furniture?.near(t,e),a=(s.osm3s?.timestamp_osm_base??"").slice(0,10);return{trees:[],buildings:_.fit(O.fit(W.buildings(s,t,o))),bridges:[],lines:[],...n,dates:{.../^\d{4}-\d{2}-\d{2}$/.test(a)?{map:a}:{},...u.found(n)?{lamps:u.today()}:{}}}}static buildings(t,e,o){const s=[];for(const n of t.elements??[]){const a=W.ringsOf(n);if(a.length===0)continue;const r=n.tags??{},i=S.levels(r["building:levels"]),h=S.height(r.height),c={geometry:{type:"Polygon",coordinates:a},properties:{...h?{hauteur:h}:{},...i?{nombre_d_etages:i}:{}}};for(const l of b.buildings([c],e,o,W.MIN_AREA_M2,50))s.push(W.described(l,r,h!==void 0))}return s.sort((n,a)=>Math.hypot(n.eastM,n.northM)-Math.hypot(a.eastM,a.northM)),s.slice(0,W.MAX_BUILDINGS)}static ringsOf(t){const e=s=>{if(!s||s.length<4)return;const n=s.map(i=>[i.lon,i.lat]),a=n[0],r=n[n.length-1];return a[0]===r[0]&&a[1]===r[1]?n:void 0};if(t.type==="way"){const s=e(t.geometry);return s?[s]:[]}return(t.members??[]).filter(s=>s.role==="outer").map(s=>e(s.geometry)).filter(s=>s!==void 0).slice(0,1)}static described(t,e,o){const s=S.wallMaterial(e["building:material"]),n=S.roofMaterial(e["roof:material"]),a=S.colour(e["building:colour"]),r=S.colour(e["roof:colour"])??_.roofColor(n),i=S.roofShape(e["roof:shape"]),h=a?{color:a}:void 0;let c,l=t.heightM;if(i==="gable"||i==="hip"||i==="dome"){const g=S.height(e["roof:height"]),p=Math.round((g??Math.min(5,Math.max(1.5,.29*Math.min(t.widthM,t.lengthM))))*10)/10;o||(l=Math.round((t.heightM+p)*10)/10),c={shape:i,riseM:p,...e["roof:orientation"]==="across"?{ridge:"depth"}:{},...r?{color:r}:{}}}else(i==="flat"||r)&&(c={shape:"flat",riseM:0,...r?{color:r}:{}});const{style:f,builtYear:M,closedAtNight:d}=O.ofOsm(e);return{...t,heightM:l,...f?{style:f}:{},...M!==void 0?{builtYear:M}:{},...d?{closedAtNight:d}:{},...e.name?{name:e.name}:{},...s?{wallMaterial:s}:{},...n?{roofMaterial:n}:{},...h?{facade:h}:{},...c?{roof:c}:{}}}}class L{credit={name:"Ville de Paris, Éclairage public",url:"https://opendata.paris.fr/explore/dataset/eclairage-public/",licence:"ODbL",of:"lamps"};static BOUNDS={south:48.8156,north:48.9022,west:2.2241,east:2.4698};static ENDPOINT="https://opendata.paris.fr/api/explore/v2.1/catalog/datasets/eclairage-public/records";static PAGE=100;static MAX=150;static MAX_PAGES=4;static METRES_PER_DEGREE=111320;static SAME_COLUMN_M=.5;static MIN_HEIGHT_M=2;fetchImpl;constructor(t){this.fetchImpl=t??fetch.bind(globalThis)}covers(t,e){const o=L.BOUNDS;return t>=o.south&&t<=o.north&&e>=o.west&&e<=o.east}async lampsNear(t,e){const o=[];let s=[];for(let n=0;n<L.MAX_PAGES&&s.length<L.MAX;n++){const a=await this.fetchImpl(L.query(t,e,L.PAGE,n*L.PAGE));if(!a.ok)throw new Error(`opendata.paris.fr refused (${a.status})`);const r=(await a.json()).results??[];if(o.push(...r),s=L.lampsFrom(o,t),r.length<L.PAGE)break}return s.slice(0,L.MAX)}static query(t,e,o,s){const n=`geom'POINT(${t.lng.toFixed(6)} ${t.lat.toFixed(6)})'`,a=new URLSearchParams({select:"geo_point_2d,lib_ouvrag,lampe_temperature_couleur,lampe_flux,support_hauteur",where:`within_distance(geo_point_2d,${n},${Math.round(e)}m)`,order_by:`distance(geo_point_2d,${n})`,limit:String(o),offset:String(s)});return`${L.ENDPOINT}?${a}`}static lampsFrom(t,e){const o=Math.cos(e.lat*Math.PI/180),s=a=>{const r=typeof a=="number"?a:Number.parseFloat(a??"");return Number.isFinite(r)&&r>0?r:void 0},n=[];for(const a of t){const r=a.geo_point_2d;if(!r||a.lib_ouvrag==="Projecteur")continue;const i=Math.round((r.lon-e.lng)*L.METRES_PER_DEGREE*o*10)/10,h=Math.round((r.lat-e.lat)*L.METRES_PER_DEGREE*10)/10,c=s(a.lampe_flux),l=n.find(d=>Math.hypot(d.eastM-i,d.northM-h)<L.SAME_COLUMN_M);if(l){c!==void 0&&(l.lumens=(l.lumens??0)+c);continue}const f=s(a.lampe_temperature_couleur),M=s(a.support_hauteur);n.push({eastM:i,northM:h,...M!==void 0&&M>=L.MIN_HEIGHT_M?{heightM:M}:{},...f!==void 0?{colorTemperatureK:f}:{},...c!==void 0?{lumens:c}:{},...a.lib_ouvrag==="Console"?{mount:"wall"}:{}})}return n}}class T{constructor(t,e){this.roads=t,this.fetchImpl=e??fetch.bind(globalThis)}credit={name:"Ville de Paris, Signalisation lumineuse tricolore",url:"https://opendata.paris.fr/explore/dataset/signalisation-tricolore/",licence:"ODbL",of:"signals"};static BOUNDS={south:48.8156,north:48.9022,west:2.2241,east:2.4698};static ENDPOINT="https://opendata.paris.fr/api/explore/v2.1/catalog/datasets/signalisation-tricolore/records";static PAGE=100;static MAX_PAGES=5;static MAX=160;static METRES_PER_DEGREE=111320;static SAME_POST_M=.8;static SEARCH_M=15;static PEDESTRIAN_ALONG_M=.4;fetchImpl;covers(t,e){const o=T.BOUNDS;return t>=o.south&&t<=o.north&&e>=o.west&&e<=o.east}async signalsNear(t,e){const o=(e+T.SEARCH_M)/T.METRES_PER_DEGREE,s=o/Math.cos(t.lat*Math.PI/180),[n,a]=await Promise.all([this.lenses(t,e),this.roads.getRoads({south:t.lat-o,north:t.lat+o,west:t.lng-s,east:t.lng+s})]);return T.signalsFrom(n,a,t)}async lenses(t,e){const o=[];for(let s=0;s<T.MAX_PAGES;s++){const n=await this.fetchImpl(T.query(t,e,T.PAGE,s*T.PAGE));if(!n.ok)throw new Error(`opendata.paris.fr refused (${n.status})`);const a=(await n.json()).results??[];if(o.push(...a),a.length<T.PAGE)break}return o}static query(t,e,o,s){const n=`geom'POINT(${t.lng.toFixed(6)} ${t.lat.toFixed(6)})'`,a=new URLSearchParams({select:"geo_point_2d,lib_regime",where:`within_distance(geo_point_2d,${n},${Math.round(e)}m)`,order_by:`distance(geo_point_2d,${n})`,limit:String(o),offset:String(s)});return`${T.ENDPOINT}?${a}`}static signalsFrom(t,e,o){const s=Math.cos(o.lat*Math.PI/180),n=(h,c)=>[(c-o.lng)*T.METRES_PER_DEGREE*s,(h-o.lat)*T.METRES_PER_DEGREE],a=[];for(const h of t){if(!h.geo_point_2d)continue;const c=n(h.geo_point_2d.lat,h.geo_point_2d.lon),l=(h.lib_regime??"").toUpperCase(),f=/ROUGE|JAUNE|VERT/.test(l),M=/PASSEZ|ATTENDEZ/.test(l);if(!f&&!M)continue;let d=a.find(g=>Math.hypot(g.at[0]-c[0],g.at[1]-c[1])<T.SAME_POST_M);d||a.push(d={at:c,vehicles:0,pedestrians:0}),f&&d.vehicles++,M&&d.pedestrians++}const r=e.map(h=>({way:h,points:h.points.map(c=>n(c.lat,c.lng))})),i=[];for(const h of a){const c=T.nearestRoad(h.at,r);if(!c)continue;const[l,f]=c.unit,M=T.heading(c.unit),d=(h.at[0]-c.foot[0])*f-(h.at[1]-c.foot[1])*l>=0,g=c.way.direction,p=g==="forward"?M:g==="backward"?(M+180)%360:d?M:(M+180)%360,E=w=>Math.round(w*10)/10,v=w=>E((w%360+360)%360);if(h.vehicles>0&&i.push({eastM:E(h.at[0]),northM:E(h.at[1]),headingDeg:v(p+180),for:"vehicles",phase:j.phaseOf(p)}),h.pedestrians>0){const w=T.heading([c.foot[0]-h.at[0],c.foot[1]-h.at[1]]),m=h.vehicles>0?T.PEDESTRIAN_ALONG_M:0;i.push({eastM:E(h.at[0]+l*m),northM:E(h.at[1]+f*m),headingDeg:v(Number.isNaN(w)?M+90:w),for:"pedestrians",phase:j.phaseOf(M)})}}return i.sort((h,c)=>Math.hypot(h.eastM,h.northM)-Math.hypot(c.eastM,c.northM)).slice(0,T.MAX)}static nearestRoad(t,e){let o;for(const{way:s,points:n}of e)for(let a=1;a<n.length;a++){const[r,i]=n[a-1],[h,c]=n[a],l=Math.hypot(h-r,c-i);if(l===0)continue;const f=[(h-r)/l,(c-i)/l],M=Math.max(0,Math.min(l,(t[0]-r)*f[0]+(t[1]-i)*f[1])),d=[r+f[0]*M,i+f[1]*M],g=Math.hypot(t[0]-d[0],t[1]-d[1]);g<=T.SEARCH_M&&(!o||g<o.distance)&&(o={way:s,unit:f,foot:d,distance:g})}return o}static heading([t,e]){return t===0&&e===0?Number.NaN:Math.round((Math.atan2(t,e)*180/Math.PI+360)%360*10)/10}}const K=[{id:"ign-lidar-topo",name:"IGN LiDAR HD + BD TOPO",credit:"© IGN, LiDAR HD and BD TOPO, Licence Ouverte 2.0; street lamps © OpenStreetMap contributors",creditUrl:"https://geoservices.ign.fr/lidarhd",create:()=>new I(new F({},new u({},k.TOWN_LAMPS,k.TOWN_SIGNALS)))},{id:"ign-lidar-hd",name:"IGN LiDAR HD",credit:"© IGN, LiDAR HD, Licence Ouverte 2.0; street lamps © OpenStreetMap contributors",creditUrl:"https://geoservices.ign.fr/lidarhd",create:()=>new I(new ot({},new u({},k.TOWN_LAMPS,k.TOWN_SIGNALS)))},{id:"ign-bd-topo",name:"IGN BD TOPO",credit:"© IGN, BD TOPO, Licence Ouverte 2.0; street lamps © OpenStreetMap contributors",creditUrl:"https://geoservices.ign.fr/bdtopo",create:()=>new I(new nt({},new u({},k.TOWN_LAMPS,k.TOWN_SIGNALS)))},{id:"osm-buildings",name:"OpenStreetMap buildings",credit:"Buildings and street lamps © OpenStreetMap contributors (ODbL)",creditUrl:"https://www.openstreetmap.org/copyright",create:()=>new I(new W({},new u({},k.TOWN_LAMPS,k.TOWN_SIGNALS)))}];class k{static DEFAULT_ID=K[0].id;static WORLD_ID="osm-buildings";static TOWN_LAMPS=[new L];static TOWN_SIGNALS=[new T(new et)];static NONE="none";static byId(t){return t===k.NONE?void 0:K.find(e=>e.id===t)??K[0]}static providerFor(t,e,o){const s=k.byId(t)?.create();if(t!==void 0&&t!==k.DEFAULT_ID)return s?.covers(e,o)?s:void 0;const n=K.find(r=>r.id===k.WORLD_ID).create(),a=[s,n].filter(r=>r!==void 0&&r.covers(e,o));return a.length>1?new st(a):a[0]}}export{K as SURROUNDINGS_SOURCES,k as SurroundingsSources};
|