@tileflow/core 0.1.0-alpha.36 → 0.1.0-alpha.37

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.
@@ -1 +1 @@
1
- "use strict";(()=>{var An=new Set,Dn=!1;function kn(n,e,t=2){let s=e&&e.length,o=s?e[0]*t:n.length;An.size&&An.clear();let r=he(n,0,o,t,!0),i=[];if(!r||r.next===r.prev)return i;let u=0,l=0,c=0;if(s&&(r=_e(n,e,r,t)),n.length>80*t){u=n[0],l=n[1];let a=u,f=l;for(let h=t;h<o;h+=t){let m=n[h],b=n[h+1];m<u&&(u=m),b<l&&(l=b),m>a&&(a=m),b>f&&(f=b)}c=Math.max(a-u,f-l),c=c!==0?32767/c:0}return Wn(r,i,u,l,c),i}function he(n,e,t,s,o){let r=null;if(o===Ye(n,e,t,s)>0)for(let i=e;i<t;i+=s)r=fe(i/s|0,n[i],n[i+1],r);else for(let i=t-s;i>=e;i-=s)r=fe(i/s|0,n[i],n[i+1],r);return r&&In(r,r.next)&&(Tn(r),r=r.next),r}function fn(n,e=n){let t=e===n,s=n,o;do o=!1,s!==s.next&&(An.size===0||!An.has(s))&&(In(s,s.next)||O(s.prev,s,s.next)===0)?((t||s===e)&&(e=s.prev),Dn=!0,Tn(s),s=s.prev,o=!0):(t||s!==e)&&(s=s.next,o=!t);while(o||s!==e);return e}function Wn(n,e,t,s,o){o&&Se(n,t,s,o);let r=n,i=!1;for(;n.prev!==n.next;){let u=n.prev,l=n.next;if(O(u,n,l)<0&&(o?ke(n,t,s,o):Te(n))){e.push(u.i,n.i,l.i),Tn(n),n=l,r=l;continue}if(n=l,n===r){if(Dn=!1,n=fn(n),Dn){r=n;continue}if(!i){n=Be(n,e),r=n,i=!0;continue}Fe(n,e,t,s,o);break}}}function Te(n){let e=n.prev,t=n,s=n.next,o=e.x,r=t.x,i=s.x,u=e.y,l=t.y,c=s.y,a=Math.min(o,r,i),f=Math.min(u,l,c),h=Math.max(o,r,i),m=Math.max(u,l,c),b=s.next;for(;b!==e;){if(b.x>=a&&b.x<=h&&b.y>=f&&b.y<=m&&!(o===b.x&&u===b.y)&&Vn(o,u,r,l,i,c,b.x,b.y)&&O(b.prev,b,b.next)>=0)return!1;b=b.next}return!0}function ke(n,e,t,s){let o=n.prev,r=n,i=n.next,u=o.x,l=r.x,c=i.x,a=o.y,f=r.y,h=i.y,m=Math.min(u,l,c),b=Math.min(a,f,h),d=Math.max(u,l,c),x=Math.max(a,f,h),I=$n(m,b,e,t,s),B=$n(d,x,e,t,s),M=n.prevZ;for(;M&&M.z>=I;){if(M.x>=m&&M.x<=d&&M.y>=b&&M.y<=x&&M!==i&&!(u===M.x&&a===M.y)&&Vn(u,a,l,f,c,h,M.x,M.y)&&O(M.prev,M,M.next)>=0)return!1;M=M.prevZ}let y=n.nextZ;for(;y&&y.z<=B;){if(y.x>=m&&y.x<=d&&y.y>=b&&y.y<=x&&y!==i&&!(u===y.x&&a===y.y)&&Vn(u,a,l,f,c,h,y.x,y.y)&&O(y.prev,y,y.next)>=0)return!1;y=y.nextZ}return!0}function Be(n,e){let t=n,s=!1;do{let o=t.prev,r=t.next.next;xe(o,t,t.next,r,!1)&&Un(o,r)&&Un(r,o)&&(e.push(o.i,t.i,r.i),Tn(t),Tn(t.next),t=n=r,s=!0),t=t.next}while(t!==n);return s?fn(t):t}function Fe(n,e,t,s,o){let r=n;do{let i=r.next.next;for(;i!==r.prev;){if(r.i!==i.i&&He(r,i)){let u=de(r,i);r=fn(r,r.next),u=fn(u,u.next),Wn(r,e,t,s,o),Wn(u,e,t,s,o);return}i=i.next}r=r.next}while(r!==n)}var Kn=!1;function _e(n,e,t,s){let o=[];for(let r=0,i=e.length;r<i;r++){let u=e[r]*s,l=r<i-1?e[r+1]*s:n.length,c=he(n,u,l,s,!1);c===c.next&&An.add(c),o.push(Ee(c))}o.sort(Pe),Le(n.length/s,e.length),be(t,t),Kn=!0;for(let r=0;r<o.length;r++)t=Oe(o[r],t);return Kn=!1,fn(t)}function Pe(n,e){return n.x-e.x||n.y-e.y||(n.next.y-n.y)/(n.next.x-n.x)-(e.next.y-e.y)/(e.next.x-e.x)}function Oe(n,e){let t=ze(n,e);if(!t)return e;let s=de(t,n),o=s.next;return be(t,o.next),fn(s,s.next),fn(t,t.next)}var me=16,_=new Float64Array(0),Nn=0,Jn=[],Qn=[];function Le(n,e){let t=Math.ceil((n+2*e)/me)+e+2;_.length<t*4&&(_=new Float64Array(t*4)),Nn=0}function be(n,e){let t=n;do{let s=Nn++;Jn[s]=t;let o=t.x,r=t.y,i=t.x,u=t.y,l=0;do{let a=t.next;t.z=s,a.x<o&&(o=a.x),a.x>i&&(i=a.x),a.y<r&&(r=a.y),a.y>u&&(u=a.y),t=a}while(++l<me&&t!==e);Qn[s]=t;let c=s*4;_[c]=o,_[c+1]=r,_[c+2]=i,_[c+3]=u}while(t!==e)}function Ce(n,e){let t=n.z*4;e.x<_[t]&&(_[t]=e.x),e.y<_[t+1]&&(_[t+1]=e.y),e.x>_[t+2]&&(_[t+2]=e.x),e.y>_[t+3]&&(_[t+3]=e.y)}function ce(n){let e=Qn[n];for(;e.prev.next!==e;)e=e.next;return Qn[n]=e,e}function ae(n){let e=Jn[n];for(;e.prev.next!==e;)e=e.next;return Jn[n]=e,e}function ze(n,e){let t=e,s=n.x,o=n.y,r=-1/0,i;if(In(n,t))return t;for(let h=0,m=0;h<Nn;h++,m+=4){if(o<_[m+1]||o>_[m+3]||_[m]>s||_[m+2]<=r)continue;let b=ce(h);t=ae(h);do{if(t.prev.next===t){if(In(n,t.next))return t.next;if(o<=t.y&&o>=t.next.y&&t.next.y!==t.y){let d=t.x+(o-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(d<=s&&d>r&&(r=d,i=t.x<t.next.x?t:t.next,d===s))return i}}t=t.next}while(t!==b)}if(!i)return null;let u=i.x,l=i.y,c=Math.min(o,l),a=Math.max(o,l),f=1/0;for(let h=0,m=0;h<Nn;h++,m+=4){if(_[m+2]<u||_[m]>s||_[m+3]<c||_[m+1]>a)continue;let b=ce(h);t=ae(h);do{if(t.prev.next===t&&s>=t.x&&t.x>=u&&s!==t.x&&Vn(o<l?s:r,o,u,l,o<l?r:s,o,t.x,t.y)){let d=Math.abs(o-t.y)/(s-t.x);(Un(t,n)||t.y===o&&t.next.y===o&&t.next.x>s)&&(d<f||d===f&&(t.x>i.x||t.x===i.x&&Ze(i,t)))&&(i=t,f=d)}t=t.next}while(t!==b)}return i}function Ze(n,e){return O(n.prev,n,e.prev)<0&&O(e.next,n,n.next)<0}var K=[],dn=[],an=new Uint32Array(0),vn=new Uint32Array(0),Rn=new Uint32Array(256);function Se(n,e,t,s){let o=n,r=0;do o.z=$n(o.x,o.y,e,t,s),K[r++]=o,o=o.next;while(o!==n);je(r);let i=null;for(let u=0;u<r;u++){let l=K[u];l.prevZ=i,i&&(i.nextZ=l),i=l}i.nextZ=null,K.fill(null,0,r),dn.fill(null,0,r)}function je(n){if(n<=32){for(let e=1;e<n;e++){let t=K[e],s=t.z,o=e-1;for(;o>=0&&K[o].z>s;)K[o+1]=K[o],o--;K[o+1]=t}return}an.length<n&&(an=new Uint32Array(n),vn=new Uint32Array(n),dn=new Array(n));for(let e=0;e<n;e++)an[e]=K[e].z;En(n,K,an,dn,vn,0),En(n,dn,vn,K,an,8),En(n,K,an,dn,vn,16),En(n,dn,vn,K,an,24)}function En(n,e,t,s,o,r){Rn.fill(0);for(let u=0;u<n;u++)Rn[t[u]>>>r&255]++;let i=0;for(let u=0;u<256;u++){let l=Rn[u];Rn[u]=i,i+=l}for(let u=0;u<n;u++){let l=t[u],c=Rn[l>>>r&255]++;s[c]=e[u],o[c]=l}}function $n(n,e,t,s,o){return n=(n-t)*o|0,e=(e-s)*o|0,n=(n|n<<8)&16711935,n=(n|n<<4)&252645135,n=(n|n<<2)&858993459,n=(n|n<<1)&1431655765,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,n|e<<1}function Ee(n){let e=n,t=n;do(e.x<t.x||e.x===t.x&&e.y<t.y)&&(t=e),e=e.next;while(e!==n);return t}function Vn(n,e,t,s,o,r,i,u){return(o-i)*(e-u)>=(n-i)*(r-u)&&(n-i)*(s-u)>=(t-i)*(e-u)&&(t-i)*(r-u)>=(o-i)*(s-u)}function He(n,e){let t=In(n,e)&&O(n.prev,n,n.next)>0&&O(e.prev,e,e.next)>0;return n.next.i!==e.i&&(t||Un(n,e)&&Un(e,n)&&(O(n.prev,n,e.prev)!==0||O(n,e.prev,e)!==0))&&!Ne(n,e)&&(t||Ve(n,e))}function O(n,e,t){return(e.y-n.y)*(t.x-e.x)-(e.x-n.x)*(t.y-e.y)}function In(n,e){return n.x===e.x&&n.y===e.y}function xe(n,e,t,s,o=!0){let r=O(n,e,t),i=O(n,e,s),u=O(t,s,n),l=O(t,s,e);return(r>0&&i<0||r<0&&i>0)&&(u>0&&l<0||u<0&&l>0)?!0:o?!!(r===0&&Hn(n,t,e)||i===0&&Hn(n,s,e)||u===0&&Hn(t,n,s)||l===0&&Hn(t,e,s)):!1}function Hn(n,e,t){return e.x<=Math.max(n.x,t.x)&&e.x>=Math.min(n.x,t.x)&&e.y<=Math.max(n.y,t.y)&&e.y>=Math.min(n.y,t.y)}function Ne(n,e){let t=Math.min(n.x,e.x),s=Math.max(n.x,e.x),o=Math.min(n.y,e.y),r=Math.max(n.y,e.y),i=n;do{let u=i.next;if(i.x>s&&u.x>s||i.x<t&&u.x<t||i.y>r&&u.y>r||i.y<o&&u.y<o){i=u;continue}if(i.i!==n.i&&u.i!==n.i&&i.i!==e.i&&u.i!==e.i&&xe(i,u,n,e))return!0;i=u}while(i!==n);return!1}function Un(n,e){return O(n.prev,n,n.next)<0?O(n,e,n.next)>=0&&O(n,n.prev,e)>=0:O(n,e,n.prev)<0||O(n,n.next,e)<0}function Ve(n,e){let t=n,s=!1,o=(n.x+e.x)/2,r=(n.y+e.y)/2;do{let i=t.next;t.y>r!=i.y>r&&o<(i.x-t.x)*(r-t.y)/(i.y-t.y)+t.x&&(s=!s),t=i}while(t!==n);return s}function de(n,e){let t=qn(n.i,n.x,n.y),s=qn(e.i,e.x,e.y),o=n.next,r=e.prev;return n.next=e,e.prev=n,t.next=o,o.prev=t,s.next=t,t.prev=s,r.next=s,s.prev=r,s}function fe(n,e,t,s){let o=qn(n,e,t);return s?(o.next=s.next,o.prev=s,s.next.prev=o,s.next=o):(o.prev=o,o.next=o),o}function Tn(n){n.next.prev=n.prev,n.prev.next=n.next,n.prevZ&&(n.prevZ.nextZ=n.nextZ),n.nextZ&&(n.nextZ.prevZ=n.prevZ),Kn&&Ce(n.prev,n.next)}function qn(n,e,t){return{i:n,x:e,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Ye(n,e,t,s){let o=0;for(let r=e,i=t-s;r<t;r+=s)o+=(n[i]-n[r])*(n[r+1]+n[i+1]),i=r;return o}var Xe=.7,Ge=Math.tan(Math.PI/6),De=Math.tan(35*Math.PI/180),We=Math.tan(Math.PI/18),Bn=.65,Fn=.4;function Me(n,e,t,s,o,r,i){let u=String(t.roof_shape??"");if(!u||u==="flat"||u==="sawtooth"||qe(n,i))return;let l=Qe(n);if(!l||Math.abs(ne(n))<Xe*4*l.halfU*l.halfV)return;let c=l.u,a=l.v,f=l.halfU*r,h=l.halfV*r;f<h&&([c,a,f,h]=[a,c,h,f]);let m=ge(t.roof_direction),b=m===void 0?void 0:[Math.sin(m*Math.PI/180),-Math.cos(m*Math.PI/180)];(b!==void 0?Math.abs(b[0]*c[0]+b[1]*c[1])>Math.abs(b[0]*a[0]+b[1]*a[1]):t.roof_orientation==="across")&&u!=="skillion"&&([c,a,f,h]=[a,c,h,f]);let x=Je(u,f,h,b&&[pe(b,c),pe(b,a)]);if(!x)return;let I=Math.max(0,o-s),B=ge(t.roof_height),M,y;if(B!==void 0&&B>0)y=o,M=Math.max(s+Math.min(2,I/2),o-B);else{let p=Math.min(Ke(u,h),I*.6,30);t.height_source==="height"?(y=o,M=o-p):(M=o,y=o+p)}let k=y-M;if(!(k>=.3))return;let[R,N]=l.centre,F=(p,A)=>{let v=(p-R)*r,g=(A-N)*r;return[v*c[0]+g*c[1],v*a[0]+g*a[1]]},S=(p,A)=>[R+(p*c[0]+A*a[0])/r,N+(p*c[1]+A*a[1])/r],J=(p,A)=>{let v=1/0;for(let[g,E,H]of x)v=Math.min(v,g*p+E*A+H);return Math.max(0,v)},Q=(p,A)=>M+k*J(...F(p,A)),L=[];for(let p=0;p+1<e.length;p+=2)L.push(...F(e[p],e[p+1]));ne(L)<0&&nt(L);let C=1.01,$=[-f*C,-h*C,f*C,-h*C,f*C,h*C,-f*C,h*C],V=[];for(let[p,A]of x.entries()){let v=$;for(let[T,j]of x.entries()){if(T===p)continue;let z=A[0]-j[0],nn=A[1]-j[1],en=A[2]-j[2];if(Math.abs(z)+Math.abs(nn)+Math.abs(en)<1e-9){T<p&&(v=[]);continue}if(v=ye(v,z,nn,en),v.length<6)break}if(v.length<6)continue;let g=L,E=v.length/2;for(let T=0;T<E&&g.length>=6;T+=1){let[j,z]=[v[T*2],v[T*2+1]],[nn,en]=[v[(T+1)%E*2],v[(T+1)%E*2+1]];g=ye(g,en-z,j-nn,-(en-z)*j-(j-nn)*z)}if(g.length<6||Math.abs(ne(g))<1e-6)continue;let H=[];for(let T=0;T<g.length;T+=2)H.push(...S(g[T],g[T+1]));let q=kn(H);if(q.length===0)continue;let on=k*A[0],Y=k*A[1],w=on*c[0]+Y*a[0],U=on*c[1]+Y*a[1],rn=Math.hypot(w,U,1);V.push({ring:H,triangles:q,normal:[-w/rn,-U/rn]})}if(V.length!==0)return{eaves:M,ridge:y,faces:V,heightAt:Q,breaks(p,A,v,g){let[E,H]=F(p,A),[q,on]=F(v,g),Y=[];for(let w=0;w<x.length;w+=1)for(let U=w+1;U<x.length;U+=1){let rn=(Z,P)=>(x[w][0]-x[U][0])*Z+(x[w][1]-x[U][1])*P+(x[w][2]-x[U][2]),T=rn(E,H),j=rn(q,on);if(T===j||T*j>0)continue;let z=T/(T-j);if(z<=1e-4||z>=1-1e-4)continue;let nn=E+(q-E)*z,en=H+(on-H)*z;x[w][0]*nn+x[w][1]*en+x[w][2]<=J(nn,en)+1e-6&&Y.push(z)}return[...new Set(Y.map(w=>Math.round(w*1e6)/1e6))].sort((w,U)=>w-U)}}}function Ke(n,e){switch(n){case"skillion":return 2*e*We;case"pyramidal":return e*De;case"gambrel":case"mansard":return e*.8;case"dome":case"onion":case"round":return e;default:return e*Ge}}function Je(n,e,t,s){let o=e,r=t,i=[[0,-1/r,1],[0,1/r,1]],u=c=>c.flatMap(a=>[1,-1].flatMap(f=>{let h=a==="v"?r:o,m=Bn/(Fn*r)*f,b=(1-Bn)/((1-Fn)*r)*f,d=x=>a==="v"?[0,-x]:[-x,0];return[[...d(m),Bn/(Fn*r)*h],[...d(b),Bn+(1-Bn)/((1-Fn)*r)*(h-Fn*r)]]})),l=[.99,.8,.5,.2];switch(n){case"gabled":return i;case"saltbox":{let c=r/3;return[[0,-1/(r-c),1+c/(r-c)],[0,1/(r+c),1-c/(r+c)]]}case"hipped":return[...i,[-1/r,0,o/r],[1/r,0,o/r]];case"half_hipped":return[...i,[-1/r,0,.5+o/r],[1/r,0,.5+o/r]];case"pyramidal":return[...i,[-1/o,0,1],[1/o,0,1]];case"gambrel":return u(["v"]);case"mansard":return u(["v","u"]);case"skillion":{let[c,a]=s??[0,1],f=Math.abs(c)*o+Math.abs(a)*r;return f>0?[[-c/(2*f),-a/(2*f),.5]]:void 0}case"round":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return[1,-1].map(f=>[0,-f*c/(r*a),1/a])})];case"dome":case"onion":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return Array.from({length:12},(f,h)=>{let m=h*Math.PI/6;return[-c*Math.cos(m)/(o*a),-c*Math.sin(m)/(r*a),1/a]})})];default:return}}function ye(n,e,t,s){let o=n.length/2,r=[];for(let i=0;i<o;i+=1){let u=n[i*2],l=n[i*2+1],c=n[(i+1)%o*2],a=n[(i+1)%o*2+1],f=e*u+t*l+s,h=e*c+t*a+s;if(f<=0&&r.push(u,l),f<0&&h>0||f>0&&h<0){let m=f/(f-h);r.push(u+(c-u)*m,l+(a-l)*m)}}return r}function Qe(n){let e=$e(n);if(e.length<3)return;let t,s=1/0;for(let o=0;o<e.length;o+=1){let[r,i]=e[o],[u,l]=e[(o+1)%e.length],c=Math.hypot(u-r,l-i);if(c===0)continue;let a=[(u-r)/c,(l-i)/c],f=[-a[1],a[0]],[h,m,b,d]=[1/0,-1/0,1/0,-1/0];for(let[I,B]of e){let M=I*a[0]+B*a[1],y=I*f[0]+B*f[1];h=Math.min(h,M),m=Math.max(m,M),b=Math.min(b,y),d=Math.max(d,y)}let x=(m-h)*(d-b);if(x<s){s=x;let I=(h+m)/2,B=(b+d)/2;t={centre:[I*a[0]+B*f[0],I*a[1]+B*f[1]],u:a,v:f,halfU:(m-h)/2,halfV:(d-b)/2}}}return t&&t.halfU>0&&t.halfV>0?t:void 0}function $e(n){let e=[];for(let r=0;r+1<n.length;r+=2)e.push([n[r],n[r+1]]);e.sort((r,i)=>r[0]-i[0]||r[1]-i[1]);let t=(r,i,u)=>(i[0]-r[0])*(u[1]-r[1])-(i[1]-r[1])*(u[0]-r[0]),s=[];for(let r of e){for(;s.length>=2&&t(s.at(-2),s.at(-1),r)<=0;)s.pop();s.push(r)}let o=[];for(let r of e.reverse()){for(;o.length>=2&&t(o.at(-2),o.at(-1),r)<=0;)o.pop();o.push(r)}return[...s.slice(0,-1),...o.slice(0,-1)]}function qe(n,e){let[t,s,o,r]=[1/0,-1/0,1/0,-1/0];for(let u=0;u+1<n.length;u+=2)t=Math.min(t,n[u]),s=Math.max(s,n[u]),o=Math.min(o,n[u+1]),r=Math.max(r,n[u+1]);let i=n.length/2;for(let u=0;u<i;u+=1){let l=n[u*2],c=n[u*2+1],a=n[(u+1)%i*2],f=n[(u+1)%i*2+1];if(t<0&&l===t&&a===t||s>e&&l===s&&a===s||o<0&&c===o&&f===o||r>e&&c===r&&f===r)return!0}return!1}function ne(n){let e=0,t=n.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;e+=n[s*2]*n[o*2+1]-n[o*2]*n[s*2+1]}return e/2}function nt(n){let e=[];for(let t=0;t<n.length;t+=2)e.push([n[t],n[t+1]]);e.reverse(),n.splice(0,n.length,...e.flat())}function pe(n,e){return n[0]*e[0]+n[1]*e[1]}function ge(n){let e=typeof n=="string"?Number(n):n;return typeof e=="number"&&Number.isFinite(e)?e:void 0}var gt=Object.freeze({surface:0,secondary:1,glass:2,accent:3,roof:4}),Mt=Object.freeze({levels:3,vertices:2e5,triangles:2e5,footprintPoints:4096,radius:2e3,height:1e3,models:512,bytes:16*1024*1024});function ee(n,e,t){for(let s of n){let o=!1;for(let r=0,i=s.length-2;r<s.length;i=r,r+=2){let u=s[r],l=s[r+1],c=s[i],a=s[i+1];l>t!=a>t&&e<(c-u)*(t-l)/(a-l)+u&&(o=!o)}if(o)return!0}return!1}function Re(n,e,t){for(let s of e){let[o,r,i,u]=ve(s);for(let l of n){let[c,a,f,h]=ve(l);if(!(c>i-t||f<o+t||a>u-t||h<r+t)&&(we(l,s,t)||we(s,l,t)))return!0}}return!1}function we(n,e,t){let s=(o,r)=>ee([e],o,r)&&tt(e,o,r)>t;for(let o=0;o+1<n.length;o+=2)if(s(n[o],n[o+1]))return!0;for(let o=0,r=n.length-2;o<n.length;r=o,o+=2){let[i,u,l,c]=[n[r],n[r+1],n[o],n[o+1]],a=[0,1];for(let f=0,h=e.length-2;f<e.length;h=f,f+=2){let m=et(i,u,l,c,e[h],e[h+1],e[f],e[f+1]);m!==void 0&&a.push(m)}if(a.length!==2){a.sort((f,h)=>f-h);for(let f=1;f<a.length;f+=1){let h=(a[f-1]+a[f])/2;if(s(i+(l-i)*h,u+(c-u)*h))return!0}}}return!1}function et(n,e,t,s,o,r,i,u){let l=t-n,c=s-e,a=i-o,f=u-r,h=l*f-c*a;if(h===0)return;let m=((o-n)*f-(r-e)*a)/h,b=((o-n)*c-(r-e)*l)/h;return m>0&&m<1&&b>=0&&b<=1?m:void 0}function tt(n,e,t){let s=1/0;for(let o=0,r=n.length-2;o<n.length;r=o,o+=2){let[i,u,l,c]=[n[r],n[r+1],n[o],n[o+1]],a=(l-i)**2+(c-u)**2,f=a>0?Math.max(0,Math.min(1,((e-i)*(l-i)+(t-u)*(c-u))/a)):0;s=Math.min(s,Math.hypot(e-(i+(l-i)*f),t-(u+(c-u)*f)))}return s}function ve(n){let[e,t,s,o]=[1/0,1/0,-1/0,-1/0];for(let r=0;r+1<n.length;r+=2)e=Math.min(e,n[r]),s=Math.max(s,n[r]),t=Math.min(t,n[r+1]),o=Math.max(o,n[r+1]);return[e,t,s,o]}var tn=8192,te=28,ot={commercial:1,active:2,destination:3},rt={residential:1,...Object.fromEntries(["civic","commercial","education","entertainment","medical","transportation"].map(n=>[n,2])),...Object.fromEntries(["agricultural","industrial","military","outbuilding","religious","service"].map(n=>[n,3]))},st={dining:1,entertainment:2,nightlife:3},oe={...Object.fromEntries(["alcoholic_beverage_venue","bar","beer","biergarten","brewery","casino","cocktail_bar","dance_club","karaoke","lounge","music_venue","night_club","nightclub","pub","wine_bar"].map(n=>[n,3])),...Object.fromEntries(["arts_centre","bowling_alley","cinema","comedy_club","concert_hall","movie_theater","opera","performing_arts_venue","theater","theatre","theatre_venue"].map(n=>[n,2])),...Object.fromEntries(["casual_eatery","fast_food","fast_food_restaurant","food_court","restaurant"].map(n=>[n,1]))},ut=6,it=.5,lt=.5;function Ue(n,e){let t=tn/n.extent,s=e.tileMetres/tn,o=e.defaultHeight??5,r=Number.isFinite(e.edgeRadius)?Math.max(0,Math.min(1,e.edgeRadius))/s:0,i=new re,u=new Yn,l=new Yn,c=[],a=[],f=0,h=ht(n,e);for(let[y,k]of n.features.entries()){if(k.type!=="Polygon"||c.length>=65535)continue;let R=k.properties,N=R.hide_3d;if(N===!0||N===1||N==="1"||N==="true")continue;let F=Math.min(6e3,Ae(R.render_height)??o),S=Math.min(F,Ae(R.render_min_height)??0),J=ot[String(R.building_tone??"")]??0,Q=rt[String(R.class??"")]??0,L=h.get(y)??0,C=c.length,$=!1;for(let V of mt(k.geometry,t)){let p=V.map(xt).filter(w=>w.length>=6);if(p.length===0||Xn(p[0])===0)continue;let[A,v]=bt(p[0]);if(e.masks?.length&&(ee(e.masks,A,v)||Re(p,e.masks,lt/s)))continue;let g={cell:Math.min(7,Math.max(0,Math.floor(v/1024)))*8+Math.min(7,Math.max(0,Math.floor(A/1024))),bounds:[1/0,1/0,-1/0,-1/0,F+.1],roofsFrom:u.length,roofsCount:0,wallsFrom:l.length,wallsCount:0};for(let w of p)for(let U=0;U<w.length;U+=2)g.bounds[0]=Math.min(g.bounds[0],w[U]-1),g.bounds[1]=Math.min(g.bounds[1],w[U+1]-1),g.bounds[2]=Math.max(g.bounds[2],w[U]+1),g.bounds[3]=Math.max(g.bounds[3],w[U+1]+1);$=!0,f=Math.max(f,F);let E=Ie(A*.0731+v*.19137+C*1.618),H=p[0].some(w=>w<=0||w>=tn)?Ie(F*7.31+S*3.17+J):E,q={base:S,top:F,jitter:H,seed:E,tone:J,group:Q,use:L,cx:A,cy:v,index:C},on=p.map((w,U)=>ft(w,U,r,e.cornerAngle));p=on.map(({ring:w})=>w);let Y=V.length===1&&p.length===1?Me(V[0],p[0],R,S,F,s,tn):void 0;Y?(f=Math.max(f,Y.ridge),g.bounds[4]=Math.max(g.bounds[4],Y.ridge+.1),at(Y,q,i,u)):ct(p,q,i,u);for(let[w,U]of p.entries()){let rn=Xn(U)>0,T=w===0?rn?-1:1:rn?1:-1,j=U.length/2,z=Z=>{let P=(Z%j+j)%j;return[U[P*2],U[P*2+1]]},nn=Z=>{let[P,D]=z(Z-1),[X,G]=z(Z),[un,mn]=z(Z+1);if(hn(P,D,X,G)||hn(X,G,un,mn))return!1;let bn=Math.hypot(X-P,G-D),ln=Math.hypot(un-X,mn-G);if(!bn||!ln)return!1;let _n=-(G-D)/bn*T,Pn=(X-P)/bn*T;return(_n*(un-X)+Pn*(mn-G))/ln>it},en=(Z,P)=>Y?Y.heightAt(Z,P):F,pn=0;for(let Z=0;Z<j;Z+=1){let[P,D]=z(Z),[X,G]=z(Z+1),un=Math.hypot(X-P,G-D);if(un===0||hn(P,D,X,G))continue;let mn=-(G-D)/un*T,bn=(X-P)/un*T,ln=on[w].normals[Z]??[mn,bn],_n=on[w].normals[(Z+1)%j]??[mn,bn],Pn=W=>{let gn=ln[0]+(_n[0]-ln[0])*W,Mn=ln[1]+(_n[1]-ln[1])*W,cn=Math.hypot(gn,Mn)||1;return[gn/cn,Mn/cn]},sn=[0,...Y?Y.breaks(P,D,X,G):[],1];for(let W=0;W+1<sn.length;W+=1){let gn=W===0,Mn=W+2===sn.length,cn=P+(X-P)*sn[W],On=D+(G-D)*sn[W],Ln=P+(X-P)*sn[W+1],Cn=D+(G-D)*sn[W+1],wn=Math.hypot(Ln-cn,Cn-On)/tn*e.tileMetres,zn=(gn&&nn(Z)?1:0)|(Mn&&nn(Z+1)?2:0)|(w>0?4:0),Zn=(gn&&se(P,D)?1:0)|(Mn&&se(X,G)?2:0),Gn=pn+wn,Sn=Pn(sn[W]),jn=Pn(sn[W+1]),ie=en(cn,On),le=en(Ln,Cn),xn=i.write(q,cn,On,0,Sn[0],Sn[1],1,pn,zn,wn,0,Zn,ie);i.write(q,Ln,Cn,0,jn[0],jn[1],1,Gn,zn,wn,1,Zn,le),i.write(q,cn,On,1,Sn[0],Sn[1],1,pn,zn,wn,0,Zn,ie),i.write(q,Ln,Cn,1,jn[0],jn[1],1,Gn,zn,wn,1,Zn,le),l.push(xn,xn+1,xn+2,xn+1,xn+3,xn+2),pn=Gn}}}g.roofsCount=u.length-g.roofsFrom,g.wallsCount=l.length-g.wallsFrom,a.push(g)}$&&c.push(R)}if(i.count===0)return;let m=new Uint32Array(u.length+l.length),b=[],d=0,x=u.length,I=u.values(),B=l.values();a.sort((y,k)=>y.cell-k.cell);let M=-1;for(let y of a){y.cell!==M&&(M=y.cell,b.push({bounds:[...y.bounds],roofsFrom:d,roofsCount:0,wallsFrom:x,wallsCount:0}));let k=b.at(-1);for(let R=0;R<5;R+=1)k.bounds[R]=R<2?Math.min(k.bounds[R],y.bounds[R]):Math.max(k.bounds[R],y.bounds[R]);m.set(I.subarray(y.roofsFrom,y.roofsFrom+y.roofsCount),d),m.set(B.subarray(y.wallsFrom,y.wallsFrom+y.wallsCount),x),d+=y.roofsCount,x+=y.wallsCount,k.roofsCount+=y.roofsCount,k.wallsCount+=y.wallsCount}return{groups:b,vertices:i.bytes(),indices:m,wallsFrom:u.length,top:f,features:c}}function ct(n,e,t,s){let o=[],r=[];for(let l of n)r.push(o.length/2),o.push(...l);r.push(o.length/2);let i=(l,c)=>{let a=r.findIndex((d,x)=>l>=d&&l<r[x+1]),[f,h]=[r[a],r[a+1]-1];if(c<f||c>h)return!1;let[m,b]=l<c?[l,c]:[c,l];return b-m!==1&&!(m===f&&b===h)?!1:!hn(o[l*2],o[l*2+1],o[c*2],o[c*2+1])},u=kn(o,r.slice(1,-1),2);for(let l=0;l+2<u.length;l+=3){let c=[u[l],u[l+1],u[l+2]],a=0;for(let f=0;f<3;f+=1)i(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries())s.push(t.write(e,o[h*2],o[h*2+1],1,0,0,0,0,0,0,f,a))}}function at(n,e,t,s){for(let o of n.faces){let r=o.ring,i=r.length/2,u=(l,c)=>{let[a,f]=l<c?[l,c]:[c,l];return f-a!==1&&!(a===0&&f===i-1)?!1:!hn(r[l*2],r[l*2+1],r[c*2],r[c*2+1])};for(let l=0;l+2<o.triangles.length;l+=3){let c=[o.triangles[l],o.triangles[l+1],o.triangles[l+2]],a=0;for(let f=0;f<3;f+=1)u(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries()){let m=r[h*2],b=r[h*2+1];s.push(t.write(e,m,b,1,o.normal[0],o.normal[1],0,0,0,0,f,a,n.heightAt(m,b)))}}}}function ft(n,e,t,s=Math.PI/6){if(t<1)return{ring:n,normals:[]};let o=n.length/2,r=Xn(n)>0,i=e===0?r?-1:1:r?1:-1,u=[],l=[],c=(a,f,h)=>{a=yn(a),f=yn(f),!(u.at(-2)===a&&u.at(-1)===f)&&(u.push(a,f),l.push(h))};for(let a=0;a<o;a+=1){let f=(a+o-1)%o,h=(a+1)%o,[m,b]=[n[f*2],n[f*2+1]],[d,x]=[n[a*2],n[a*2+1]],[I,B]=[n[h*2],n[h*2+1]],M=Math.hypot(d-m,x-b),y=Math.hypot(I-d,B-x);if(!M||!y||se(d,x)||hn(m,b,d,x)||hn(d,x,I,B)){c(d,x);continue}let[k,R]=[(d-m)/M,(x-b)/M],[N,F]=[(I-d)/y,(B-x)/y],S=Math.atan2(k*F-R*N,k*N+R*F);if(S*i>=-.08||Math.abs(S)>Math.PI*.85){c(d,x);continue}let J=Math.tan(Math.abs(S)/2),Q=Math.min(t*J,M*.2,y*.2),L=Q/J;if(L<1){c(d,x);continue}let[C,$]=[-R*i,k*i],[V,p]=[d-k*Q-C*L,x-R*Q-$*L],A=Math.atan2($,C),v=Math.max(s<=Math.PI/6?2:1,Math.ceil(Math.abs(S)/Math.max(Math.PI/6,Math.min(Math.PI/3,s))));for(let g=0;g<=v;g+=1){let E=A+S*g/v,H=[Math.cos(E),Math.sin(E)];c(V+H[0]*L,p+H[1]*L,H)}}return u[0]===u.at(-2)&&u[1]===u.at(-1)&&(u.splice(-2),l.pop()),u.length>=6?{ring:u,normals:l}:{ring:n,normals:[]}}var re=class{#n=new ArrayBuffer(4096*te);#e=new DataView(this.#n);count=0;write(e,t,s,o,r,i,u,l,c,a,f=0,h=0,m=e.top){let b=te;if((this.count+1)*b>this.#n.byteLength){let I=new ArrayBuffer(this.#n.byteLength*2);new Uint8Array(I).set(new Uint8Array(this.#n)),this.#n=I,this.#e=new DataView(I)}let d=this.#e,x=this.count*b;return d.setUint16(x,yn(t),!0),d.setUint16(x+2,yn(s),!0),d.setUint16(x+4,Math.min(65535,Math.round(e.base*10)),!0),d.setUint16(x+6,Math.min(65535,Math.max(0,Math.round(m*10))),!0),d.setInt8(x+8,Math.round(r*127)),d.setInt8(x+9,Math.round(i*127)),d.setUint8(x+10,o),d.setUint8(x+11,u),d.setUint16(x+12,Math.round(l*10)%65536,!0),d.setUint8(x+14,Math.round(e.jitter*255)),d.setUint8(x+15,Math.round(e.seed*255)),d.setUint16(x+16,yn(e.cx),!0),d.setUint16(x+18,yn(e.cy),!0),d.setUint8(x+20,e.tone+4*e.group),d.setUint8(x+21,e.use),d.setUint8(x+22,c),d.setUint8(x+23,Math.min(255,Math.round(a))),d.setUint16(x+24,e.index,!0),d.setUint8(x+26,f),d.setUint8(x+27,h),this.count+=1,this.count-1}bytes(){return new Uint8Array(this.#n.slice(0,this.count*te))}},Yn=class{#n=new Uint32Array(8192);length=0;push(...e){if(this.length+e.length>this.#n.length){let t=new Uint32Array(Math.max(this.length+e.length,this.#n.length*2));t.set(this.#n),this.#n=t}for(let t of e)this.#n[this.length++]=t}values(){return this.#n.subarray(0,this.length)}};function ht(n,e){let t=new Map;if(n.features.some(u=>u.properties.night_use!==void 0)){for(let[u,l]of n.features.entries()){let c=st[String(l.properties.night_use??"")];c&&t.set(u,c)}return t}let s=e.places;if(!s||s.features.length===0)return t;let o=ut/e.tileMetres*n.extent,r=[];for(let[u,l]of n.features.entries()){if(l.type!=="Polygon")continue;let c=l.properties.hide_3d;if(c===!0||c===1||c==="1"||c==="true")continue;let a=[1/0,1/0,-1/0,-1/0];for(let f of l.geometry)for(let h=0;h+1<f.length;h+=2)a[0]=Math.min(a[0],f[h]),a[1]=Math.min(a[1],f[h+1]),a[2]=Math.max(a[2],f[h]),a[3]=Math.max(a[3],f[h+1]);r.push({index:u,rings:l.geometry,box:a})}let i=n.extent/s.extent;for(let u of s.features){if(u.type!=="Point")continue;let l=u.properties,c=oe[String(l.type??"")]??oe[String(l.subclass??"")]??oe[String(l.class??"")];if(c)for(let a of u.geometry)for(let f=0;f+1<a.length;f+=2){let h=a[f]*i,m=a[f+1]*i,b,d,x=o;for(let B of r){let[M,y,k,R]=B.box;if(h<M-o||h>k+o||m<y-o||m>R+o)continue;let N=!1;for(let F of B.rings)for(let S=0,J=F.length-2;S+1<F.length;J=S,S+=2){let Q=F[J],L=F[J+1],C=F[S],$=F[S+1];$>m!=L>m&&h<(Q-C)*(m-$)/(L-$)+C&&(N=!N);let V=C-Q,p=$-L,A=V*V+p*p,v=A?Math.min(1,Math.max(0,((h-Q)*V+(m-L)*p)/A)):0,g=Math.hypot(h-Q-v*V,m-L-v*p);g<x&&(x=g,d=B.index)}if(N){b=B.index;break}}let I=b??d;I!==void 0&&t.set(I,Math.max(t.get(I)??0,c))}}return t}function yn(n){return Math.min(tn,Math.max(0,Math.round(n)))}function Ae(n){let e=Number(n);return Number.isFinite(e)&&e>0?e:void 0}function mt(n,e){let t=[],s;for(let o of n){let r=[];for(let u=0;u+1<o.length;u+=2){let l=o[u]*e,c=o[u+1]*e;r.length&&l===r.at(-2)&&c===r.at(-1)||r.push(l,c)}if(r.length>=6&&r[0]===r.at(-2)&&r[1]===r.at(-1)&&(r.length-=2),r.length<6)continue;let i=Math.sign(Xn(r));i&&(s??=i,i===s||t.length===0?t.push([r]):t.at(-1).push(r))}return t}function Xn(n){let e=0,t=n.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;e+=n[s*2]*n[o*2+1]-n[o*2]*n[s*2+1]}return e}function bt(n){let e=0,t=0,s=n.length/2;for(let o=0;o<s;o+=1)e+=n[o*2],t+=n[o*2+1];return[e/s,t/s]}function se(n,e){return n<=0||e<=0||n>=tn||e>=tn}function hn(n,e,t,s){let o=tn;return n<=0&&t<=0||n>=o&&t>=o||e<=0&&s<=0||e>=o&&s>=o}function xt(n){let e=tn,t=[...n],s=[[0,0,o=>o>=0],[0,e,o=>o<=e],[1,0,o=>o>=0],[1,e,o=>o<=e]];for(let[o,r,i]of s){let u=[],l=t.length/2;for(let c=0;c<l;c+=1){let a=[t[c*2],t[c*2+1]],f=[t[(c+1)%l*2],t[(c+1)%l*2+1]],h=i(a[o]),m=i(f[o]);if(h&&u.push(a[0],a[1]),h!==m){let b=(r-a[o])/(f[o]-a[o]);u.push(a[0]+(f[0]-a[0])*b,a[1]+(f[1]-a[1])*b)}}if(t=u,t.length<6)return[]}return t}function Ie(n){let e=Math.sin(n*12.9898+78.233)*43758.5453;return e-Math.floor(e)}var ue=globalThis;ue.onmessage=({data:n})=>{try{let e=Ue(n.layer,n.options);ue.postMessage({mesh:e},e?[e.vertices.buffer,e.indices.buffer]:[])}catch(e){ue.postMessage({error:String(e)})}};})();
1
+ "use strict";(()=>{var Re=new Set,De=!1;function Ue(e,n,t=2){let s=n&&n.length,o=s?n[0]*t:e.length;Re.size&&Re.clear();let r=bn(e,0,o,t,!0),u=[];if(!r||r.next===r.prev)return u;let i=0,l=0,c=0;if(s&&(r=Pn(e,n,r,t)),e.length>80*t){i=e[0],l=e[1];let a=i,f=l;for(let h=t;h<o;h+=t){let m=e[h],b=e[h+1];m<i&&(i=m),b<l&&(l=b),m>a&&(a=m),b>f&&(f=b)}c=Math.max(a-i,f-l),c=c!==0?32767/c:0}return We(r,u,i,l,c),u}function bn(e,n,t,s,o){let r=null;if(o===Xn(e,n,t,s)>0)for(let u=n;u<t;u+=s)r=mn(u/s|0,e[u],e[u+1],r);else for(let u=t-s;u>=n;u-=s)r=mn(u/s|0,e[u],e[u+1],r);return r&&Ae(r,r.next)&&(Te(r),r=r.next),r}function ae(e,n=e){let t=n===e,s=e,o;do o=!1,s!==s.next&&(Re.size===0||!Re.has(s))&&(Ae(s,s.next)||P(s.prev,s,s.next)===0)?((t||s===n)&&(n=s.prev),De=!0,Te(s),s=s.prev,o=!0):(t||s!==n)&&(s=s.next,o=!t);while(o||s!==n);return n}function We(e,n,t,s,o){o&&jn(e,t,s,o);let r=e,u=!1;for(;e.prev!==e.next;){let i=e.prev,l=e.next;if(P(i,e,l)<0&&(o?Bn(e,t,s,o):kn(e))){n.push(i.i,e.i,l.i),Te(e),e=l,r=l;continue}if(e=l,e===r){if(De=!1,e=ae(e),De){r=e;continue}if(!u){e=Fn(e,n),r=e,u=!0;continue}_n(e,n,t,s,o);break}}}function kn(e){let n=e.prev,t=e,s=e.next,o=n.x,r=t.x,u=s.x,i=n.y,l=t.y,c=s.y,a=Math.min(o,r,u),f=Math.min(i,l,c),h=Math.max(o,r,u),m=Math.max(i,l,c),b=s.next;for(;b!==n;){if(b.x>=a&&b.x<=h&&b.y>=f&&b.y<=m&&!(o===b.x&&i===b.y)&&Ne(o,i,r,l,u,c,b.x,b.y)&&P(b.prev,b,b.next)>=0)return!1;b=b.next}return!0}function Bn(e,n,t,s){let o=e.prev,r=e,u=e.next,i=o.x,l=r.x,c=u.x,a=o.y,f=r.y,h=u.y,m=Math.min(i,l,c),b=Math.min(a,f,h),d=Math.max(i,l,c),x=Math.max(a,f,h),U=$e(m,b,n,t,s),k=$e(d,x,n,t,s),M=e.prevZ;for(;M&&M.z>=U;){if(M.x>=m&&M.x<=d&&M.y>=b&&M.y<=x&&M!==u&&!(i===M.x&&a===M.y)&&Ne(i,a,l,f,c,h,M.x,M.y)&&P(M.prev,M,M.next)>=0)return!1;M=M.prevZ}let y=e.nextZ;for(;y&&y.z<=k;){if(y.x>=m&&y.x<=d&&y.y>=b&&y.y<=x&&y!==u&&!(i===y.x&&a===y.y)&&Ne(i,a,l,f,c,h,y.x,y.y)&&P(y.prev,y,y.next)>=0)return!1;y=y.nextZ}return!0}function Fn(e,n){let t=e,s=!1;do{let o=t.prev,r=t.next.next;yn(o,t,t.next,r,!1)&&Ie(o,r)&&Ie(r,o)&&(n.push(o.i,t.i,r.i),Te(t),Te(t.next),t=e=r,s=!0),t=t.next}while(t!==e);return s?ae(t):t}function _n(e,n,t,s,o){let r=e;do{let u=r.next.next;for(;u!==r.prev;){if(r.i!==u.i&&Nn(r,u)){let i=pn(r,u);r=ae(r,r.next),i=ae(i,i.next),We(r,n,t,s,o),We(i,n,t,s,o);return}u=u.next}r=r.next}while(r!==e)}var Ke=!1;function Pn(e,n,t,s){let o=[];for(let r=0,u=n.length;r<u;r++){let i=n[r]*s,l=r<u-1?n[r+1]*s:e.length,c=bn(e,i,l,s,!1);c===c.next&&Re.add(c),o.push(Hn(c))}o.sort(On),zn(e.length/s,n.length),dn(t,t),Ke=!0;for(let r=0;r<o.length;r++)t=Ln(o[r],t);return Ke=!1,ae(t)}function On(e,n){return e.x-n.x||e.y-n.y||(e.next.y-e.y)/(e.next.x-e.x)-(n.next.y-n.y)/(n.next.x-n.x)}function Ln(e,n){let t=Sn(e,n);if(!t)return n;let s=pn(t,e),o=s.next;return dn(t,o.next),ae(s,s.next),ae(t,t.next)}var xn=16,_=new Float64Array(0),He=0,Je=[],Qe=[];function zn(e,n){let t=Math.ceil((e+2*n)/xn)+n+2;_.length<t*4&&(_=new Float64Array(t*4)),He=0}function dn(e,n){let t=e;do{let s=He++;Je[s]=t;let o=t.x,r=t.y,u=t.x,i=t.y,l=0;do{let a=t.next;t.z=s,a.x<o&&(o=a.x),a.x>u&&(u=a.x),a.y<r&&(r=a.y),a.y>i&&(i=a.y),t=a}while(++l<xn&&t!==n);Qe[s]=t;let c=s*4;_[c]=o,_[c+1]=r,_[c+2]=u,_[c+3]=i}while(t!==n)}function Cn(e,n){let t=e.z*4;n.x<_[t]&&(_[t]=n.x),n.y<_[t+1]&&(_[t+1]=n.y),n.x>_[t+2]&&(_[t+2]=n.x),n.y>_[t+3]&&(_[t+3]=n.y)}function fn(e){let n=Qe[e];for(;n.prev.next!==n;)n=n.next;return Qe[e]=n,n}function hn(e){let n=Je[e];for(;n.prev.next!==n;)n=n.next;return Je[e]=n,n}function Sn(e,n){let t=n,s=e.x,o=e.y,r=-1/0,u;if(Ae(e,t))return t;for(let h=0,m=0;h<He;h++,m+=4){if(o<_[m+1]||o>_[m+3]||_[m]>s||_[m+2]<=r)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t){if(Ae(e,t.next))return t.next;if(o<=t.y&&o>=t.next.y&&t.next.y!==t.y){let d=t.x+(o-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(d<=s&&d>r&&(r=d,u=t.x<t.next.x?t:t.next,d===s))return u}}t=t.next}while(t!==b)}if(!u)return null;let i=u.x,l=u.y,c=Math.min(o,l),a=Math.max(o,l),f=1/0;for(let h=0,m=0;h<He;h++,m+=4){if(_[m+2]<i||_[m]>s||_[m+3]<c||_[m+1]>a)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t&&s>=t.x&&t.x>=i&&s!==t.x&&Ne(o<l?s:r,o,i,l,o<l?r:s,o,t.x,t.y)){let d=Math.abs(o-t.y)/(s-t.x);(Ie(t,e)||t.y===o&&t.next.y===o&&t.next.x>s)&&(d<f||d===f&&(t.x>u.x||t.x===u.x&&Zn(u,t)))&&(u=t,f=d)}t=t.next}while(t!==b)}return u}function Zn(e,n){return P(e.prev,e,n.prev)<0&&P(n.next,e,e.next)<0}var J=[],de=[],ce=new Uint32Array(0),we=new Uint32Array(0),ve=new Uint32Array(256);function jn(e,n,t,s){let o=e,r=0;do o.z=$e(o.x,o.y,n,t,s),J[r++]=o,o=o.next;while(o!==e);En(r);let u=null;for(let i=0;i<r;i++){let l=J[i];l.prevZ=u,u&&(u.nextZ=l),u=l}u.nextZ=null,J.fill(null,0,r),de.fill(null,0,r)}function En(e){if(e<=32){for(let n=1;n<e;n++){let t=J[n],s=t.z,o=n-1;for(;o>=0&&J[o].z>s;)J[o+1]=J[o],o--;J[o+1]=t}return}ce.length<e&&(ce=new Uint32Array(e),we=new Uint32Array(e),de=new Array(e));for(let n=0;n<e;n++)ce[n]=J[n].z;je(e,J,ce,de,we,0),je(e,de,we,J,ce,8),je(e,J,ce,de,we,16),je(e,de,we,J,ce,24)}function je(e,n,t,s,o,r){ve.fill(0);for(let i=0;i<e;i++)ve[t[i]>>>r&255]++;let u=0;for(let i=0;i<256;i++){let l=ve[i];ve[i]=u,u+=l}for(let i=0;i<e;i++){let l=t[i],c=ve[l>>>r&255]++;s[c]=n[i],o[c]=l}}function $e(e,n,t,s,o){return e=(e-t)*o|0,n=(n-s)*o|0,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,n=(n|n<<8)&16711935,n=(n|n<<4)&252645135,n=(n|n<<2)&858993459,n=(n|n<<1)&1431655765,e|n<<1}function Hn(e){let n=e,t=e;do(n.x<t.x||n.x===t.x&&n.y<t.y)&&(t=n),n=n.next;while(n!==e);return t}function Ne(e,n,t,s,o,r,u,i){return(o-u)*(n-i)>=(e-u)*(r-i)&&(e-u)*(s-i)>=(t-u)*(n-i)&&(t-u)*(r-i)>=(o-u)*(s-i)}function Nn(e,n){let t=Ae(e,n)&&P(e.prev,e,e.next)>0&&P(n.prev,n,n.next)>0;return e.next.i!==n.i&&(t||Ie(e,n)&&Ie(n,e)&&(P(e.prev,e,n.prev)!==0||P(e,n.prev,n)!==0))&&!Vn(e,n)&&(t||Yn(e,n))}function P(e,n,t){return(n.y-e.y)*(t.x-n.x)-(n.x-e.x)*(t.y-n.y)}function Ae(e,n){return e.x===n.x&&e.y===n.y}function yn(e,n,t,s,o=!0){let r=P(e,n,t),u=P(e,n,s),i=P(t,s,e),l=P(t,s,n);return(r>0&&u<0||r<0&&u>0)&&(i>0&&l<0||i<0&&l>0)?!0:o?!!(r===0&&Ee(e,t,n)||u===0&&Ee(e,s,n)||i===0&&Ee(t,e,s)||l===0&&Ee(t,n,s)):!1}function Ee(e,n,t){return n.x<=Math.max(e.x,t.x)&&n.x>=Math.min(e.x,t.x)&&n.y<=Math.max(e.y,t.y)&&n.y>=Math.min(e.y,t.y)}function Vn(e,n){let t=Math.min(e.x,n.x),s=Math.max(e.x,n.x),o=Math.min(e.y,n.y),r=Math.max(e.y,n.y),u=e;do{let i=u.next;if(u.x>s&&i.x>s||u.x<t&&i.x<t||u.y>r&&i.y>r||u.y<o&&i.y<o){u=i;continue}if(u.i!==e.i&&i.i!==e.i&&u.i!==n.i&&i.i!==n.i&&yn(u,i,e,n))return!0;u=i}while(u!==e);return!1}function Ie(e,n){return P(e.prev,e,e.next)<0?P(e,n,e.next)>=0&&P(e,e.prev,n)>=0:P(e,n,e.prev)<0||P(e,e.next,n)<0}function Yn(e,n){let t=e,s=!1,o=(e.x+n.x)/2,r=(e.y+n.y)/2;do{let u=t.next;t.y>r!=u.y>r&&o<(u.x-t.x)*(r-t.y)/(u.y-t.y)+t.x&&(s=!s),t=u}while(t!==e);return s}function pn(e,n){let t=qe(e.i,e.x,e.y),s=qe(n.i,n.x,n.y),o=e.next,r=n.prev;return e.next=n,n.prev=e,t.next=o,o.prev=t,s.next=t,t.prev=s,r.next=s,s.prev=r,s}function mn(e,n,t,s){let o=qe(e,n,t);return s?(o.next=s.next,o.prev=s,s.next.prev=o,s.next=o):(o.prev=o,o.next=o),o}function Te(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ),Ke&&Cn(e.prev,e.next)}function qe(e,n,t){return{i:e,x:n,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Xn(e,n,t,s){let o=0;for(let r=n,u=t-s;r<t;r+=s)o+=(e[u]-e[r])*(e[r+1]+e[u+1]),u=r;return o}var Gn=.7,Dn=Math.tan(Math.PI/6),Wn=Math.tan(35*Math.PI/180),Kn=Math.tan(Math.PI/18),ke=.65,Be=.4;function vn(e,n,t,s,o,r,u){let i=String(t.roof_shape??"");if(!i||i==="flat"||i==="sawtooth"||et(e,u))return;let l=$n(e);if(!l||Math.abs(en(e))<Gn*4*l.halfU*l.halfV)return;let c=l.u,a=l.v,f=l.halfU*r,h=l.halfV*r;f<h&&([c,a,f,h]=[a,c,h,f]);let m=wn(t.roof_direction),b=m===void 0?void 0:[Math.sin(m*Math.PI/180),-Math.cos(m*Math.PI/180)];(b!==void 0?Math.abs(b[0]*c[0]+b[1]*c[1])>Math.abs(b[0]*a[0]+b[1]*a[1]):t.roof_orientation==="across")&&i!=="skillion"&&([c,a,f,h]=[a,c,h,f]);let x=Qn(i,f,h,b&&[Mn(b,c),Mn(b,a)]);if(!x)return;let U=Math.max(0,o-s),k=wn(t.roof_height),M,y;if(k!==void 0&&k>0)y=o,M=Math.max(s+Math.min(2,U/2),o-k);else{let p=Math.min(Jn(i,h),U*.6,30);t.height_source==="height"?(y=o,M=o-p):(M=o,y=o+p)}let I=y-M;if(!(I>=.3))return;let[R,V]=l.centre,B=(p,A)=>{let w=(p-R)*r,g=(A-V)*r;return[w*c[0]+g*c[1],w*a[0]+g*a[1]]},j=(p,A)=>[R+(p*c[0]+A*a[0])/r,V+(p*c[1]+A*a[1])/r],Q=(p,A)=>{let w=1/0;for(let[g,N,E]of x)w=Math.min(w,g*p+N*A+E);return Math.max(0,w)},$=(p,A)=>M+I*Q(...B(p,A)),L=[];for(let p=0;p+1<n.length;p+=2)L.push(...B(n[p],n[p+1]));en(L)<0&&nt(L);let z=1.01,q=[-f*z,-h*z,f*z,-h*z,f*z,h*z,-f*z,h*z],Y=[];for(let[p,A]of x.entries()){let w=q;for(let[F,H]of x.entries()){if(F===p)continue;let C=A[0]-H[0],X=A[1]-H[1],ne=A[2]-H[2];if(Math.abs(C)+Math.abs(X)+Math.abs(ne)<1e-9){F<p&&(w=[]);continue}if(w=gn(w,C,X,ne),w.length<6)break}if(w.length<6)continue;let g=L,N=w.length/2;for(let F=0;F<N&&g.length>=6;F+=1){let[H,C]=[w[F*2],w[F*2+1]],[X,ne]=[w[(F+1)%N*2],w[(F+1)%N*2+1]];g=gn(g,ne-C,H-X,-(ne-C)*H-(H-X)*C)}if(g.length<6||Math.abs(en(g))<1e-6)continue;let E=[];for(let F=0;F<g.length;F+=2)E.push(...j(g[F],g[F+1]));let se=Ue(E);if(se.length===0)continue;let ee=I*A[0],oe=I*A[1],T=ee*c[0]+oe*a[0],v=ee*c[1]+oe*a[1],O=Math.hypot(T,v,1);Y.push({ring:E,triangles:se,normal:[-T/O,-v/O]})}if(Y.length!==0)return{eaves:M,ridge:y,faces:Y,heightAt:$,breaks(p,A,w,g){let[N,E]=B(p,A),[se,ee]=B(w,g),oe=[];for(let T=0;T<x.length;T+=1)for(let v=T+1;v<x.length;v+=1){let O=(he,S)=>(x[T][0]-x[v][0])*he+(x[T][1]-x[v][1])*S+(x[T][2]-x[v][2]),F=O(N,E),H=O(se,ee);if(F===H||F*H>0)continue;let C=F/(F-H);if(C<=1e-4||C>=1-1e-4)continue;let X=N+(se-N)*C,ne=E+(ee-E)*C;x[T][0]*X+x[T][1]*ne+x[T][2]<=Q(X,ne)+1e-6&&oe.push(C)}return[...new Set(oe.map(T=>Math.round(T*1e6)/1e6))].sort((T,v)=>T-v)}}}function Jn(e,n){switch(e){case"skillion":return 2*n*Kn;case"pyramidal":return n*Wn;case"gambrel":case"mansard":return n*.8;case"dome":case"onion":case"round":return n;default:return n*Dn}}function Qn(e,n,t,s){let o=n,r=t,u=[[0,-1/r,1],[0,1/r,1]],i=c=>c.flatMap(a=>[1,-1].flatMap(f=>{let h=a==="v"?r:o,m=ke/(Be*r)*f,b=(1-ke)/((1-Be)*r)*f,d=x=>a==="v"?[0,-x]:[-x,0];return[[...d(m),ke/(Be*r)*h],[...d(b),ke+(1-ke)/((1-Be)*r)*(h-Be*r)]]})),l=[.99,.8,.5,.2];switch(e){case"gabled":return u;case"saltbox":{let c=r/3;return[[0,-1/(r-c),1+c/(r-c)],[0,1/(r+c),1-c/(r+c)]]}case"hipped":return[...u,[-1/r,0,o/r],[1/r,0,o/r]];case"half_hipped":return[...u,[-1/r,0,.5+o/r],[1/r,0,.5+o/r]];case"pyramidal":return[...u,[-1/o,0,1],[1/o,0,1]];case"gambrel":return i(["v"]);case"mansard":return i(["v","u"]);case"skillion":{let[c,a]=s??[0,1],f=Math.abs(c)*o+Math.abs(a)*r;return f>0?[[-c/(2*f),-a/(2*f),.5]]:void 0}case"round":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return[1,-1].map(f=>[0,-f*c/(r*a),1/a])})];case"dome":case"onion":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return Array.from({length:12},(f,h)=>{let m=h*Math.PI/6;return[-c*Math.cos(m)/(o*a),-c*Math.sin(m)/(r*a),1/a]})})];default:return}}function gn(e,n,t,s){let o=e.length/2,r=[];for(let u=0;u<o;u+=1){let i=e[u*2],l=e[u*2+1],c=e[(u+1)%o*2],a=e[(u+1)%o*2+1],f=n*i+t*l+s,h=n*c+t*a+s;if(f<=0&&r.push(i,l),f<0&&h>0||f>0&&h<0){let m=f/(f-h);r.push(i+(c-i)*m,l+(a-l)*m)}}return r}function $n(e){let n=qn(e);if(n.length<3)return;let t,s=1/0;for(let o=0;o<n.length;o+=1){let[r,u]=n[o],[i,l]=n[(o+1)%n.length],c=Math.hypot(i-r,l-u);if(c===0)continue;let a=[(i-r)/c,(l-u)/c],f=[-a[1],a[0]],[h,m,b,d]=[1/0,-1/0,1/0,-1/0];for(let[U,k]of n){let M=U*a[0]+k*a[1],y=U*f[0]+k*f[1];h=Math.min(h,M),m=Math.max(m,M),b=Math.min(b,y),d=Math.max(d,y)}let x=(m-h)*(d-b);if(x<s){s=x;let U=(h+m)/2,k=(b+d)/2;t={centre:[U*a[0]+k*f[0],U*a[1]+k*f[1]],u:a,v:f,halfU:(m-h)/2,halfV:(d-b)/2}}}return t&&t.halfU>0&&t.halfV>0?t:void 0}function qn(e){let n=[];for(let r=0;r+1<e.length;r+=2)n.push([e[r],e[r+1]]);n.sort((r,u)=>r[0]-u[0]||r[1]-u[1]);let t=(r,u,i)=>(u[0]-r[0])*(i[1]-r[1])-(u[1]-r[1])*(i[0]-r[0]),s=[];for(let r of n){for(;s.length>=2&&t(s.at(-2),s.at(-1),r)<=0;)s.pop();s.push(r)}let o=[];for(let r of n.reverse()){for(;o.length>=2&&t(o.at(-2),o.at(-1),r)<=0;)o.pop();o.push(r)}return[...s.slice(0,-1),...o.slice(0,-1)]}function et(e,n){let[t,s,o,r]=[1/0,-1/0,1/0,-1/0];for(let i=0;i+1<e.length;i+=2)t=Math.min(t,e[i]),s=Math.max(s,e[i]),o=Math.min(o,e[i+1]),r=Math.max(r,e[i+1]);let u=e.length/2;for(let i=0;i<u;i+=1){let l=e[i*2],c=e[i*2+1],a=e[(i+1)%u*2],f=e[(i+1)%u*2+1];if(t<0&&l===t&&a===t||s>n&&l===s&&a===s||o<0&&c===o&&f===o||r>n&&c===r&&f===r)return!0}return!1}function en(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n/2}function nt(e){let n=[];for(let t=0;t<e.length;t+=2)n.push([e[t],e[t+1]]);n.reverse(),e.splice(0,e.length,...n.flat())}function Mn(e,n){return e[0]*n[0]+e[1]*n[1]}function wn(e){let n=typeof e=="string"?Number(e):e;return typeof n=="number"&&Number.isFinite(n)?n:void 0}var Mt=Object.freeze({surface:0,secondary:1,glass:2,accent:3,roof:4}),wt=Object.freeze({unclassified:0,wall:1,door:2,roof:3,window:4,lamp:5,logo:6}),vt=Object.freeze([1,1,4,1,3]),Rt=Object.freeze({levels:3,vertices:2e5,triangles:2e5,footprintPoints:4096,radius:2e3,height:1e3,models:512,bytes:16*1024*1024});function nn(e,n,t){for(let s of e){let o=!1;for(let r=0,u=s.length-2;r<s.length;u=r,r+=2){let i=s[r],l=s[r+1],c=s[u],a=s[u+1];l>t!=a>t&&n<(c-i)*(t-l)/(a-l)+i&&(o=!o)}if(o)return!0}return!1}function In(e,n,t){for(let s of n){let[o,r,u,i]=An(s);for(let l of e){let[c,a,f,h]=An(l);if(!(c>u-t||f<o+t||a>i-t||h<r+t)&&(Rn(l,s,t)||Rn(s,l,t)))return!0}}return!1}function Rn(e,n,t){let s=(o,r)=>nn([n],o,r)&&ot(n,o,r)>t;for(let o=0;o+1<e.length;o+=2)if(s(e[o],e[o+1]))return!0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=[0,1];for(let f=0,h=n.length-2;f<n.length;h=f,f+=2){let m=tt(u,i,l,c,n[h],n[h+1],n[f],n[f+1]);m!==void 0&&a.push(m)}if(a.length!==2){a.sort((f,h)=>f-h);for(let f=1;f<a.length;f+=1){let h=(a[f-1]+a[f])/2;if(s(u+(l-u)*h,i+(c-i)*h))return!0}}}return!1}function tt(e,n,t,s,o,r,u,i){let l=t-e,c=s-n,a=u-o,f=i-r,h=l*f-c*a;if(h===0)return;let m=((o-e)*f-(r-n)*a)/h,b=((o-e)*c-(r-n)*l)/h;return m>0&&m<1&&b>=0&&b<=1?m:void 0}function ot(e,n,t){let s=1/0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=(l-u)**2+(c-i)**2,f=a>0?Math.max(0,Math.min(1,((n-u)*(l-u)+(t-i)*(c-i))/a)):0;s=Math.min(s,Math.hypot(n-(u+(l-u)*f),t-(i+(c-i)*f)))}return s}function An(e){let[n,t,s,o]=[1/0,1/0,-1/0,-1/0];for(let r=0;r+1<e.length;r+=2)n=Math.min(n,e[r]),s=Math.max(s,e[r]),t=Math.min(t,e[r+1]),o=Math.max(o,e[r+1]);return[n,t,s,o]}var te=8192,tn=28,rt={commercial:1,active:2,destination:3},st={residential:1,...Object.fromEntries(["civic","commercial","education","entertainment","medical","transportation"].map(e=>[e,2])),...Object.fromEntries(["agricultural","industrial","military","outbuilding","religious","service"].map(e=>[e,3]))},it={dining:1,entertainment:2,nightlife:3},on={...Object.fromEntries(["alcoholic_beverage_venue","bar","beer","biergarten","brewery","casino","cocktail_bar","dance_club","karaoke","lounge","music_venue","night_club","nightclub","pub","wine_bar"].map(e=>[e,3])),...Object.fromEntries(["arts_centre","bowling_alley","cinema","comedy_club","concert_hall","movie_theater","opera","performing_arts_venue","theater","theatre","theatre_venue"].map(e=>[e,2])),...Object.fromEntries(["casual_eatery","fast_food","fast_food_restaurant","food_court","restaurant"].map(e=>[e,1]))},ut=6,lt=.5,ct=.5;function Un(e,n){let t=te/e.extent,s=n.tileMetres/te,o=n.defaultHeight??5,r=Number.isFinite(n.edgeRadius)?Math.max(0,Math.min(1,n.edgeRadius))/s:0,u=new sn,i=new Ve,l=new Ve,c=[],a=[],f=0,h=mt(e,n);for(let[y,I]of e.features.entries()){if(I.type!=="Polygon"||c.length>=65535)continue;let R=I.properties,V=R.hide_3d;if(V===!0||V===1||V==="1"||V==="true")continue;let B=Math.min(6e3,Tn(R.render_height)??o),j=Math.min(B,Tn(R.render_min_height)??0),Q=rt[String(R.building_tone??"")]??0,$=st[String(R.class??"")]??0,L=h.get(y)??0,z=c.length,q=!1;for(let Y of bt(I.geometry,t)){let p=Y.map(dt).filter(v=>v.length>=6);if(p.length===0||Ye(p[0])===0)continue;let[A,w]=xt(p[0]);if(n.masks?.length&&(nn(n.masks,A,w)||In(p,n.masks,ct/s)))continue;let g={cell:Math.min(7,Math.max(0,Math.floor(w/1024)))*8+Math.min(7,Math.max(0,Math.floor(A/1024))),bounds:[1/0,1/0,-1/0,-1/0,B+.1],roofsFrom:i.length,roofsCount:0,wallsFrom:l.length,wallsCount:0};for(let v of p)for(let O=0;O<v.length;O+=2)g.bounds[0]=Math.min(g.bounds[0],v[O]-1),g.bounds[1]=Math.min(g.bounds[1],v[O+1]-1),g.bounds[2]=Math.max(g.bounds[2],v[O]+1),g.bounds[3]=Math.max(g.bounds[3],v[O+1]+1);q=!0,f=Math.max(f,B);let N=Number.isSafeInteger(I.id),E=rn(N?I.id*.0731:A*.0731+w*.19137+z*1.618),se=!N&&p[0].some(v=>v<=0||v>=te)?rn(B*7.31+j*3.17+Q):E,ee={base:j,top:B,jitter:se,seed:E,tone:Q,group:$,use:L,cx:A,cy:w,index:z},oe=p.map((v,O)=>ht(v,O,r,n.cornerAngle));p=oe.map(({ring:v})=>v);let T=Y.length===1&&p.length===1?vn(Y[0],p[0],R,j,B,s,te):void 0;T?(f=Math.max(f,T.ridge),g.bounds[4]=Math.max(g.bounds[4],T.ridge+.1),ft(T,ee,u,i)):at(p,ee,u,i);for(let[v,O]of p.entries()){let F=Ye(O)>0,H=v===0?F?-1:1:F?1:-1,C=O.length/2,X=S=>{let Z=(S%C+C)%C;return[O[Z*2],O[Z*2+1]]},ne=S=>{let[Z,W]=X(S-1),[G,D]=X(S),[ie,me]=X(S+1);if(fe(Z,W,G,D)||fe(G,D,ie,me))return!1;let be=Math.hypot(G-Z,D-W),ue=Math.hypot(ie-G,me-D);if(!be||!ue)return!1;let Fe=-(D-W)/be*H,_e=(G-Z)/be*H;return(Fe*(ie-G)+_e*(me-D))/ue>lt},Xe=(S,Z)=>T?T.heightAt(S,Z):B,he=0;for(let S=0;S<C;S+=1){let[Z,W]=X(S),[G,D]=X(S+1),ie=Math.hypot(G-Z,D-W);if(ie===0||fe(Z,W,G,D))continue;let me=-(D-W)/ie*H,be=(G-Z)/ie*H,ue=oe[v].normals[S]??[me,be],Fe=oe[v].normals[(S+1)%C]??[me,be],_e=K=>{let pe=ue[0]+(Fe[0]-ue[0])*K,ge=ue[1]+(Fe[1]-ue[1])*K,le=Math.hypot(pe,ge)||1;return[pe/le,ge/le]},re=[0,...T?T.breaks(Z,W,G,D):[],1];for(let K=0;K+1<re.length;K+=1){let pe=K===0,ge=K+2===re.length,le=Z+(G-Z)*re[K],Pe=W+(D-W)*re[K],Oe=Z+(G-Z)*re[K+1],Le=W+(D-W)*re[K+1],Me=Math.hypot(Oe-le,Le-Pe)/te*n.tileMetres,ze=(pe&&ne(S)?1:0)|(ge&&ne(S+1)?2:0)|(v>0?4:0),Ce=(pe&&un(Z,W)?1:0)|(ge&&un(G,D)?2:0),Ge=he+Me,Se=_e(re[K]),Ze=_e(re[K+1]),cn=Xe(le,Pe),an=Xe(Oe,Le),xe=u.write(ee,le,Pe,0,Se[0],Se[1],1,he,ze,Me,0,Ce,cn);u.write(ee,Oe,Le,0,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),u.write(ee,le,Pe,1,Se[0],Se[1],1,he,ze,Me,0,Ce,cn),u.write(ee,Oe,Le,1,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),l.push(xe,xe+1,xe+2,xe+1,xe+3,xe+2),he=Ge}}}g.roofsCount=i.length-g.roofsFrom,g.wallsCount=l.length-g.wallsFrom,a.push(g)}q&&c.push(R)}if(u.count===0)return;let m=new Uint32Array(i.length+l.length),b=[],d=0,x=i.length,U=i.values(),k=l.values();a.sort((y,I)=>y.cell-I.cell);let M=-1;for(let y of a){y.cell!==M&&(M=y.cell,b.push({bounds:[...y.bounds],roofsFrom:d,roofsCount:0,wallsFrom:x,wallsCount:0}));let I=b.at(-1);for(let R=0;R<5;R+=1)I.bounds[R]=R<2?Math.min(I.bounds[R],y.bounds[R]):Math.max(I.bounds[R],y.bounds[R]);m.set(U.subarray(y.roofsFrom,y.roofsFrom+y.roofsCount),d),m.set(k.subarray(y.wallsFrom,y.wallsFrom+y.wallsCount),x),d+=y.roofsCount,x+=y.wallsCount,I.roofsCount+=y.roofsCount,I.wallsCount+=y.wallsCount}return{groups:b,vertices:u.bytes(),indices:m,wallsFrom:i.length,top:f,features:c}}function at(e,n,t,s){let o=[],r=[];for(let l of e)r.push(o.length/2),o.push(...l);r.push(o.length/2);let u=(l,c)=>{let a=r.findIndex((d,x)=>l>=d&&l<r[x+1]),[f,h]=[r[a],r[a+1]-1];if(c<f||c>h)return!1;let[m,b]=l<c?[l,c]:[c,l];return b-m!==1&&!(m===f&&b===h)?!1:!fe(o[l*2],o[l*2+1],o[c*2],o[c*2+1])},i=Ue(o,r.slice(1,-1),2);for(let l=0;l+2<i.length;l+=3){let c=[i[l],i[l+1],i[l+2]],a=0;for(let f=0;f<3;f+=1)u(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries())s.push(t.write(n,o[h*2],o[h*2+1],1,0,0,0,0,0,0,f,a))}}function ft(e,n,t,s){for(let o of e.faces){let r=o.ring,u=r.length/2,i=(l,c)=>{let[a,f]=l<c?[l,c]:[c,l];return f-a!==1&&!(a===0&&f===u-1)?!1:!fe(r[l*2],r[l*2+1],r[c*2],r[c*2+1])};for(let l=0;l+2<o.triangles.length;l+=3){let c=[o.triangles[l],o.triangles[l+1],o.triangles[l+2]],a=0;for(let f=0;f<3;f+=1)i(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries()){let m=r[h*2],b=r[h*2+1];s.push(t.write(n,m,b,1,o.normal[0],o.normal[1],0,0,0,0,f,a,e.heightAt(m,b)))}}}}function ht(e,n,t,s=Math.PI/6){if(t<1)return{ring:e,normals:[]};let o=e.length/2,r=Ye(e)>0,u=n===0?r?-1:1:r?1:-1,i=[],l=[],c=(a,f,h)=>{a=ye(a),f=ye(f),!(i.at(-2)===a&&i.at(-1)===f)&&(i.push(a,f),l.push(h))};for(let a=0;a<o;a+=1){let f=(a+o-1)%o,h=(a+1)%o,[m,b]=[e[f*2],e[f*2+1]],[d,x]=[e[a*2],e[a*2+1]],[U,k]=[e[h*2],e[h*2+1]],M=Math.hypot(d-m,x-b),y=Math.hypot(U-d,k-x);if(!M||!y||un(d,x)||fe(m,b,d,x)||fe(d,x,U,k)){c(d,x);continue}let[I,R]=[(d-m)/M,(x-b)/M],[V,B]=[(U-d)/y,(k-x)/y],j=Math.atan2(I*B-R*V,I*V+R*B);if(j*u>=-.08||Math.abs(j)>Math.PI*.85){c(d,x);continue}let Q=Math.tan(Math.abs(j)/2),$=Math.min(t*Q,M*.2,y*.2),L=$/Q;if(L<1){c(d,x);continue}let[z,q]=[-R*u,I*u],[Y,p]=[d-I*$-z*L,x-R*$-q*L],A=Math.atan2(q,z),w=Math.max(s<=Math.PI/6?2:1,Math.ceil(Math.abs(j)/Math.max(Math.PI/6,Math.min(Math.PI/3,s))));for(let g=0;g<=w;g+=1){let N=A+j*g/w,E=[Math.cos(N),Math.sin(N)];c(Y+E[0]*L,p+E[1]*L,E)}}return i[0]===i.at(-2)&&i[1]===i.at(-1)&&(i.splice(-2),l.pop()),i.length>=6?{ring:i,normals:l}:{ring:e,normals:[]}}var sn=class{#e=new ArrayBuffer(4096*tn);#n=new DataView(this.#e);count=0;write(n,t,s,o,r,u,i,l,c,a,f=0,h=0,m=n.top){let b=tn;if((this.count+1)*b>this.#e.byteLength){let U=new ArrayBuffer(this.#e.byteLength*2);new Uint8Array(U).set(new Uint8Array(this.#e)),this.#e=U,this.#n=new DataView(U)}let d=this.#n,x=this.count*b;return d.setUint16(x,ye(t),!0),d.setUint16(x+2,ye(s),!0),d.setUint16(x+4,Math.min(65535,Math.round(n.base*10)),!0),d.setUint16(x+6,Math.min(65535,Math.max(0,Math.round(m*10))),!0),d.setInt8(x+8,Math.round(r*127)),d.setInt8(x+9,Math.round(u*127)),d.setUint8(x+10,o),d.setUint8(x+11,i),d.setUint16(x+12,Math.round(l*10)%65536,!0),d.setUint8(x+14,Math.round(n.jitter*255)),d.setUint8(x+15,Math.round(n.seed*255)),d.setUint16(x+16,ye(n.cx),!0),d.setUint16(x+18,ye(n.cy),!0),d.setUint8(x+20,n.tone+4*n.group),d.setUint8(x+21,n.use),d.setUint8(x+22,c),d.setUint8(x+23,Math.min(255,Math.round(a))),d.setUint16(x+24,n.index,!0),d.setUint8(x+26,f),d.setUint8(x+27,h),this.count+=1,this.count-1}bytes(){return new Uint8Array(this.#e.slice(0,this.count*tn))}},Ve=class{#e=new Uint32Array(8192);length=0;push(...n){if(this.length+n.length>this.#e.length){let t=new Uint32Array(Math.max(this.length+n.length,this.#e.length*2));t.set(this.#e),this.#e=t}for(let t of n)this.#e[this.length++]=t}values(){return this.#e.subarray(0,this.length)}};function mt(e,n){let t=new Map;if(e.features.some(i=>i.properties.night_use!==void 0)){for(let[i,l]of e.features.entries()){let c=it[String(l.properties.night_use??"")];c&&t.set(i,c)}return t}let s=n.places;if(!s||s.features.length===0)return t;let o=ut/n.tileMetres*e.extent,r=[];for(let[i,l]of e.features.entries()){if(l.type!=="Polygon")continue;let c=l.properties.hide_3d;if(c===!0||c===1||c==="1"||c==="true")continue;let a=[1/0,1/0,-1/0,-1/0];for(let f of l.geometry)for(let h=0;h+1<f.length;h+=2)a[0]=Math.min(a[0],f[h]),a[1]=Math.min(a[1],f[h+1]),a[2]=Math.max(a[2],f[h]),a[3]=Math.max(a[3],f[h+1]);r.push({index:i,rings:l.geometry,box:a})}let u=e.extent/s.extent;for(let i of s.features){if(i.type!=="Point")continue;let l=i.properties,c=on[String(l.type??"")]??on[String(l.subclass??"")]??on[String(l.class??"")];if(c)for(let a of i.geometry)for(let f=0;f+1<a.length;f+=2){let h=a[f]*u,m=a[f+1]*u,b,d,x=o;for(let k of r){let[M,y,I,R]=k.box;if(h<M-o||h>I+o||m<y-o||m>R+o)continue;let V=!1;for(let B of k.rings)for(let j=0,Q=B.length-2;j+1<B.length;Q=j,j+=2){let $=B[Q],L=B[Q+1],z=B[j],q=B[j+1];q>m!=L>m&&h<($-z)*(m-q)/(L-q)+z&&(V=!V);let Y=z-$,p=q-L,A=Y*Y+p*p,w=A?Math.min(1,Math.max(0,((h-$)*Y+(m-L)*p)/A)):0,g=Math.hypot(h-$-w*Y,m-L-w*p);g<x&&(x=g,d=k.index)}if(V){b=k.index;break}}let U=b??d;U!==void 0&&t.set(U,Math.max(t.get(U)??0,c))}}return t}function ye(e){return Math.min(te,Math.max(0,Math.round(e)))}function Tn(e){let n=Number(e);return Number.isFinite(n)&&n>0?n:void 0}function bt(e,n){let t=[],s;for(let o of e){let r=[];for(let i=0;i+1<o.length;i+=2){let l=o[i]*n,c=o[i+1]*n;r.length&&l===r.at(-2)&&c===r.at(-1)||r.push(l,c)}if(r.length>=6&&r[0]===r.at(-2)&&r[1]===r.at(-1)&&(r.length-=2),r.length<6)continue;let u=Math.sign(Ye(r));u&&(s??=u,u===s||t.length===0?t.push([r]):t.at(-1).push(r))}return t}function Ye(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n}function xt(e){let n=0,t=0,s=e.length/2;for(let o=0;o<s;o+=1)n+=e[o*2],t+=e[o*2+1];return[n/s,t/s]}function un(e,n){return e<=0||n<=0||e>=te||n>=te}function fe(e,n,t,s){let o=te;return e<=0&&t<=0||e>=o&&t>=o||n<=0&&s<=0||n>=o&&s>=o}function dt(e){let n=te,t=[...e],s=[[0,0,o=>o>=0],[0,n,o=>o<=n],[1,0,o=>o>=0],[1,n,o=>o<=n]];for(let[o,r,u]of s){let i=[],l=t.length/2;for(let c=0;c<l;c+=1){let a=[t[c*2],t[c*2+1]],f=[t[(c+1)%l*2],t[(c+1)%l*2+1]],h=u(a[o]),m=u(f[o]);if(h&&i.push(a[0],a[1]),h!==m){let b=(r-a[o])/(f[o]-a[o]);i.push(a[0]+(f[0]-a[0])*b,a[1]+(f[1]-a[1])*b)}}if(t=i,t.length<6)return[]}return t}function rn(e){let n=Math.sin(e*12.9898+78.233)*43758.5453;return n-Math.floor(n)}var ln=globalThis;ln.onmessage=({data:e})=>{try{let n=Un(e.layer,e.options);ln.postMessage({mesh:n},n?[n.vertices.buffer,n.indices.buffer]:[])}catch(n){ln.postMessage({error:String(n)})}};})();
@@ -8,6 +8,9 @@ Showing, hiding and changing height are immediate. Illuminated outlines, random
8
8
  invented windows and night lights remain off. Every effect is independently overridable and
9
9
  independent of the map's trees.
10
10
 
11
+ Streets supplies architectural flood lighting and subtle model wall emission for its dark
12
+ presets. These controls are independent of invented windows and the optional `nightLight` effects.
13
+
11
14
  Tileflow landmarks and a map's own building models can stand in the same 3D scene, replacing
12
15
  the buildings under their footprints.
13
16
 
@@ -146,11 +149,17 @@ or the renderer is removed; it resizes with the drawing buffer.
146
149
  Maps on Tileflow World show Tileflow's landmarks, such as the Eiffel Tower, as 3D models in place of
147
150
  the buildings under them. They come from a landmarks tileset kept apart from the map's data: each of
148
151
  its tiles carries the models standing in it, with their levels of detail and footprints, and each
149
- region's index names its tiles by their hash. The renderer reads the tileset's TileJSON once the
150
- buildings show in 3D, then only the indexes of the regions round the camera and the landmark tiles
151
- near it, so a landmark in Paris costs nothing in Madrid. New and improved landmarks reach every map
152
- without a new build of the map. A tileset that cannot be read is tried again after five minutes,
153
- and the buildings stay drawn meanwhile.
152
+ region's index names its tiles by their hash, with where each landmark stands and its footprint.
153
+ The renderer reads the tileset's TileJSON, then only the indexes of the regions round the camera
154
+ and the landmark tiles near it, from a zoom before the buildings show, so a landmark in Paris costs
155
+ nothing in Madrid. New and improved landmarks reach every map without a new build of the map.
156
+
157
+ The buildings under a landmark are never raised and then taken away. A tile of buildings is built
158
+ once the indexes round it have arrived, waiting at most a second and a half; while a landmark's
159
+ tile is still on its way, the footprint its index names keeps the buildings under it flat, and the
160
+ landmark fades in when it arrives. A landmark tile that fails, or has not arrived three seconds
161
+ after the buildings round it show, lets those buildings rise until it does. A tileset that cannot be read is tried again
162
+ after five minutes, and the buildings stay drawn meanwhile.
154
163
 
155
164
  A map switches them off, or names the TileJSON of another landmarks tileset:
156
165
 
@@ -221,6 +230,10 @@ How to prepare a model:
221
230
  instead. Each part takes its colour from `modelColors`, or from the buildings' colour when the
222
231
  design gives it none, so the model follows the theme from day to night; vertex colours lighter or
223
232
  darker than the rest of their part shade it within that colour.
233
+ - **Architectural roles control lighting.** A material may set
234
+ `extras.tileflow.lightingRole` to `wall`, `door`, `roof`, `window`, `lamp`, `logo` or
235
+ `unclassified`. This preserves its palette slot. Without a role, `surface`, `secondary` and
236
+ `accent` are walls, `glass` is a window, and `roof` is a roof.
224
237
  - **Metres round an origin on the ground.** The origin is where the model stands, with the model
225
238
  above it and its own north along +y once read Z up; nothing may lie more than half a metre below
226
239
  it.
@@ -237,12 +250,49 @@ The build reads each model, reports a problem with the file and what to change,
237
250
  footprint and writes it beside the style as a prepared model file: a glTF binary whose meshes are
238
251
  quantised (`KHR_mesh_quantization`) and compressed by meshoptimizer (`EXT_meshopt_compression`),
239
252
  which any glTF viewer opens. The renderer downloads a model only when it stands near the tiles round
240
- the camera, keeps the nearest dozen, and builds the tiles under it again without the buildings that
241
- reach more than half a metre into its footprint; a neighbour that only touches it stays. A model
242
- that cannot be downloaded leaves those buildings standing. Reading a prepared model file loads
253
+ the camera, whether or not those tiles hold buildings, keeps the nearest dozen, and builds the tiles
254
+ under it again without the buildings that
255
+ reach more than half a metre into its footprint; a neighbour that only touches it stays. A tile of
256
+ buildings near a model waits for it, at most a second and a half, so its buildings are not raised
257
+ and then taken away, and the model fades in when it comes to stand. A model that cannot be
258
+ downloaded leaves those buildings standing. Reading a prepared model file loads
243
259
  meshoptimizer's WebAssembly decoder, so a page with a Content Security Policy allows
244
260
  `'wasm-unsafe-eval'` in its `script-src`.
245
261
 
262
+ Preparation also computes local geometric ambient visibility, independently of the model's
263
+ colour. `modelOcclusion` sets its strength from zero to one (default `0.75`); `occlusion: false`
264
+ disables it. Occluded cavities receive less ambient light while direct sun and dynamic shadows
265
+ remain independent. Existing prepared files with fully exposed vertices retain their appearance.
266
+ Reprepare their original GLBs to gain geometric occlusion. See
267
+ [model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
268
+ for authoring, sampling and costs.
269
+
270
+ ## Team 3D models
271
+
272
+ On Tileflow Hosted, a map's building models are its Team's 3D models: each is published once, under
273
+ its name, and every map of the Team that places a model of that name draws it. Locally, the map
274
+ draws its own `.glb` files, so the map is previewed and developed without an account.
275
+
276
+ ```sh
277
+ npx tileflow models publish ./models/eiffel-tower.glb --team @acme
278
+ npx tileflow models status eiffel-tower --team @acme
279
+ ```
280
+
281
+ `tileflow models publish` prepares each building model as a build does and publishes it as the
282
+ model's new current version; with no path, it publishes the building models of every map in the
283
+ project's config. Publishing the bytes of the current version again changes nothing. A hosted map
284
+ follows each model's current version: a new version, or `tileflow models restore`, which makes the
285
+ previous version current again, changes every map that places it the next time the map loads,
286
+ without deploying the maps again. A model keeps its current and previous versions.
287
+
288
+ `tileflow deploy` writes each placement of a building model as the placeholder
289
+ `tileflow://team-models/<name>` (`tileflowTeamModelUrl(name)`), which the deploy replaces with the
290
+ model's address in the map's Team; a model the Team has not published fails the deploy. The deploy
291
+ also reports a model whose published version was prepared from other files than the map's, so an
292
+ outdated model is easy to spot. A model's file, and what Tileflow shows about it, can be read
293
+ without decoding its meshes with `readTileflowModelFileSummary`. Models are served publicly and may
294
+ be cached, so they must not hold secrets or restricted personal data.
295
+
246
296
  ## Roofs
247
297
 
248
298
  A building whose tiles carry a roof shape (World's `roof_shape`) gets that roof: `gabled`,
@@ -331,6 +381,42 @@ by day and night, mixed between the themes either side of a blend's position.
331
381
 
332
382
  ## Night light
333
383
 
384
+ Architectural illumination has two independent controls:
385
+
386
+ - `floodLight` selects ordinary buildings by height. Ground light starts at `minGroundHeight`
387
+ (30 m by default), with a stable per-building `groundRadius` in metres (default `[30, 100]`).
388
+ Wall light starts above `minWallHeight` (200 m); its reach is building height times
389
+ `wallHeightRatio` (default `1/3`). `intensity` is zero to one and `color` is a theme colour.
390
+ The effect fades with scene brightness; `brightnessScale` compensates an authored light-colour
391
+ transform and defaults to one. Overlapping ground contributions use their strongest value.
392
+ - `modelLighting` assigns physical materials and emission to architectural roles. Each profile accepts
393
+ `emissiveStrength` (zero to five), optional `emissiveColor` (otherwise the surface's own colour), and
394
+ `emissiveHeight`: `[beginHeight, endHeight, beginMultiplier, endMultiplier, curve]`.
395
+ Heights are fractions of the source mesh's vertical bounds (the full model for legacy files),
396
+ multipliers are zero to one, and the exponent is
397
+ `10 ** curve`, with `curve` from -1 to 1. Equal heights give a constant multiplier of one.
398
+ Every gradient component, strength and colour may use theme tokens. Emission is mixed in linear light and is independent
399
+ of ambient occlusion and solar shadows. Values above one saturate the surface emission at one;
400
+ they also control the falloff of explicitly authored area emitters.
401
+
402
+ Physical profiles also accept `color`, `colorMix` (zero to one, default one when a tint exists),
403
+ `roughness` and `metallic` (zero to one). Explicit `modelColors` override role tints. Missing
404
+ physical overrides retain the glTF material. `emissiveVariation` adds a stable, model-seeded amount
405
+ from zero to its value. Optional `colorVariation` supplies independent `[min, max]` ranges for
406
+ `hue` (0–360), `saturation` and `lightness` (0–100), with `space: 'srgb'` (default) or `'linear'`.
407
+ The same model identity keeps its samples across LODs and theme changes.
408
+
409
+ Streets leaves ordinary flood lighting off in its light presets. Its dusk/night wall profile uses a strength
410
+ of `0.1` and `[0.5, 0, 0, 1, 0.8]`, concentrating a warm wash near the foot. Monochrome uses a
411
+ weaker neutral wall profile and no architectural ground flood. Prepared models without role
412
+ metadata use their documented palette mapping. Windows use metallic reflection and varied emission;
413
+ doors and logos have separate profiles, and roofs remain non-emissive. Day/dawn profiles follow
414
+ the scene's brighter material response. A role does not generate missing windows or ground emitters.
415
+ See [building-model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
416
+ for glTF channels, explicit area emitters, compatibility and resource costs.
417
+
418
+ The separate `nightLight` control retains its decorative building and shop effects:
419
+
334
420
  With `nightLight` above 0, the buildings light the street from data at night (as the sun's
335
421
  `brightness` falls):
336
422
 
@@ -387,6 +473,11 @@ Missing camera capabilities, terrain or a view near the horizon use the full loa
387
473
  The depth pass runs again when its focused area, light or casting geometry changes and is reused
388
474
  when they stay unchanged.
389
475
 
476
+ Model ambient visibility and lighting roles retain the existing 16-byte vertex stride. They add
477
+ no geometry pass or texture. Architectural ground lighting uses a separate one-byte-per-pixel
478
+ mask and one geometry/composite pair of passes while active; its mask is released when inactive.
479
+ At 1280 × 720 and DPR 1, the mask occupies 921,600 bytes, excluding driver overhead.
480
+
390
481
  ## Limits
391
482
 
392
483
  - Mercator only; the layer draws nothing below its `minzoom`.
@@ -395,8 +486,9 @@ when they stay unchanged.
395
486
  - Roofs follow the building's smallest enclosing rectangle: a building with courtyards or far from a
396
487
  rectangle keeps a flat roof, and `sawtooth` roofs are drawn flat.
397
488
  - A colour expression is evaluated at zoom 16.
398
- - Building models and landmarks take no edges or night light of their own yet, and cannot be
399
- picked or styled by state.
400
- - A hosted map takes up to 64 building models of its own, 32 MiB once prepared; `tileflow deploy`
401
- uploads them with the map. A deploy with `--with-native` does not take them, since MapLibre Native
402
- draws no models. Models named by `url` deploy as they are.
489
+ - Building models and landmarks take no illuminated edges and cannot be picked or styled by state.
490
+ - Model materials use semantic colours and role emission; physical roughness, metallic
491
+ reflections and authored textures are not rendered.
492
+ - A hosted map places its building models as its Team's 3D models: `tileflow deploy` uploads no
493
+ model files (see [Team 3D models](#team-3d-models)). A deploy with `--with-native` does not take
494
+ them, since MapLibre Native draws no models. Models named by `url` deploy as they are.
@@ -212,48 +212,29 @@ export default defineMap({
212
212
  marine: 'chart', // 'none' | 'bathymetry' | 'nautical' | 'chart'
213
213
  modules: {
214
214
  nautical: nautical({
215
- soundings: {minZoom: 13},
216
- aids: {minZoom: 11},
217
- lights: {minZoom: 12},
218
- hazards: {minZoom: 12},
219
- hazardAreas: {
220
- fill: {opacity: fixed(0.1, {reason: 'Chart hazard wash is invariant'})},
221
- },
222
- wrecks: {minZoom: 12},
223
- wreckAreas: {
224
- outline: {opacity: fixed(0.6, {reason: 'Chart wreck outline is invariant'})},
225
- },
226
- reefs: {fill: {opacity: fixed(0.3, {reason: 'Chart reef wash is invariant'})}},
215
+ buoys: {minZoom: 12},
216
+ lights: {minZoom: 9},
227
217
  navigationAreas: {
228
218
  fill: {opacity: fixed(0.05, {reason: 'Chart navigation-area wash is invariant'})},
229
219
  },
230
- coverage: {outline: {visible: false}},
231
- labels: {
232
- navigationAreas: {minZoom: 11},
233
- reefs: {minZoom: 13},
234
- },
220
+ reefs: {fill: {opacity: fixed(0.3, {reason: 'Chart reef wash is invariant'})}},
221
+ labels: {navigationAreas: {minZoom: 12}},
235
222
  }),
236
223
  },
237
224
  });
238
225
  ```
239
226
 
240
227
  `bathymetry` resolves to vector source `tileflow-bathymetry` and supplies the 13-stop
241
- `bathymetry-v1` band contract to `water(...)`. `nautical` resolves to `tileflow-nautical`; the
242
- `nautical(...)` module
243
- styles its `sounding`, `aid`, `light`, `hazard`, `wreck`, `reef`, `navigation_area`, and `coverage`
244
- layers. The schema also names the product's provider-neutral provenance fields (`provenance`,
245
- `provider`, `cell`, `edition`, `update`, `scale`, `coverage`, and `licence`). `chart` enables both.
246
- Point features label depth, name, or light character as applicable. The `lighthouses` symbol style
247
- targets `aid` points whose canonical `class` is `lighthouse`, leaving other aids under `aids`.
248
- Polygon reefs, navigation
249
- areas, hazards, wrecks, and named/provider coverage have separate collision-aware labels under
250
- `nautical({labels: {...}})`; unnamed coverage does not emit empty text.
228
+ `bathymetry-v1` band contract to `water(...)`. `nautical` resolves to `tileflow-nautical` at
229
+ `/tiles/nautical/tiles.json`; the `nautical(...)` module draws its `nautical-v1` buoys, beacons,
230
+ lighthouses, lights, hazards, wrecks, reefs, navigation areas, routes, cables, harbours, ports, and
231
+ a restrained coverage indication. `chart` enables both. Every map that draws Nautical declares a
232
+ "Not for navigation" notice, which the browser runtimes render on the map. The
233
+ [Nautical marks guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/nautical-marks.md)
234
+ describes the schema, the semantic groups, the theme image tokens, and the notice.
251
235
  Core does not hard-code a nautical provider or attribution: the selected TileJSON owns its actual
252
236
  coverage and attribution, and derived vector tiles are not represented as a certified ENC for
253
- navigation. The V1 geometry contract preserves `sounding`, `aid`, and `light` as points;
254
- `reef`, `navigation_area`, and `coverage` as polygons; and both point and polygon geometries for
255
- `hazard` and `wreck`. Point symbols and polygon areas compile separately, without fabricated
256
- centroids.
237
+ navigation. Point symbols and polygon areas compile separately, without fabricated centroids.
257
238
  Advanced callers can override either TileJSON URL, attribution, or source ID and can set the other
258
239
  member to `false`:
259
240
 
@@ -0,0 +1,200 @@
1
+ # Nautical marks
2
+
3
+ The `nautical` module draws navigation marks from a `nautical-v1` vector source: buoys, beacons,
4
+ lighthouses, lights, rocks and other hazards, wrecks, reefs, navigation areas, routes, cables,
5
+ harbours, ports, and a restrained indication of where a provider publishes marks. It is reference
6
+ cartography for general-purpose maps. It is not a nautical chart, and a map that draws it always
7
+ shows a "Not for navigation" notice.
8
+
9
+ ## Enable Nautical on a map
10
+
11
+ Nautical is opt-in through the map's `marine` option. `'nautical'` adds it alone, `'chart'` adds it
12
+ with Bathymetry, and the object form selects or replaces each member. The official Soundings map
13
+ enables it with its own chart symbols; every other official map draws it with the module's default
14
+ symbology once the map opts in.
15
+
16
+ <!-- docs:check -->
17
+
18
+ ```ts
19
+ import {defineMap} from '@tileflow/core';
20
+ import {streets} from '@tileflow/maps';
21
+
22
+ export default defineMap({
23
+ id: 'harbour-guide',
24
+ version: 1,
25
+ extends: streets,
26
+ marine: 'nautical',
27
+ view: {center: [-1.3, 50.77], zoom: 12},
28
+ });
29
+ ```
30
+
31
+ `marine` is atomic: declaring it replaces the inherited value. Without a URL, the source is the
32
+ TileJSON at `/tiles/nautical/tiles.json` on the map's API base, with the style source ID
33
+ `tileflow-nautical`. Compiling a map that names this URL does not establish that a hosted Nautical
34
+ source is available to it. To use any schema-compatible source, name its TileJSON or PMTiles
35
+ archive and, when the TileJSON does not carry one, its attribution:
36
+
37
+ <!-- docs:check -->
38
+
39
+ ```ts
40
+ import {defineMap} from '@tileflow/core';
41
+ import {streets} from '@tileflow/maps';
42
+
43
+ export default defineMap({
44
+ id: 'harbour-guide',
45
+ version: 1,
46
+ extends: streets,
47
+ marine: {
48
+ bathymetry: false,
49
+ nautical: {
50
+ url: 'https://tiles.example.com/nautical/tiles.json',
51
+ attribution: '© Example © OpenStreetMap contributors',
52
+ },
53
+ },
54
+ });
55
+ ```
56
+
57
+ The selected TileJSON owns the provider credit. Core never invents a nautical provider or
58
+ attribution, and an explicit `attribution` replaces the one the TileJSON supplies.
59
+
60
+ ## The "Not for navigation" notice
61
+
62
+ When any layer of the compiled style reads the Nautical source, the style declares a notice in its
63
+ `tileflow:notices` metadata:
64
+
65
+ ```json
66
+ [
67
+ {
68
+ "id": "nautical-not-for-navigation",
69
+ "sources": ["tileflow-nautical"],
70
+ "text": "Not for navigation"
71
+ }
72
+ ]
73
+ ```
74
+
75
+ React, Vue, Svelte, the development preview, and standalone capture render it as a compact,
76
+ always-visible control beside the attribution, and keep it in step with style replacements. A map
77
+ that does not draw Nautical, including one whose `nautical` module is disabled, declares no notice.
78
+ The notice is separate from the source attribution, so the provider credit stays complete.
79
+
80
+ A direct MapLibre integration attaches the same control after constructing the map:
81
+
82
+ ```ts
83
+ import maplibregl from 'maplibre-gl';
84
+ import {attachTileflowNotices} from '@tileflow/core/browser';
85
+
86
+ const map = new maplibregl.Map({
87
+ container: 'map',
88
+ style: '/tileflow/styles/harbour-guide/light.json',
89
+ });
90
+ const detach = attachTileflowNotices(map, {position: 'bottom-right'});
91
+ // Call detach() to remove the control early; removing the map also removes it.
92
+ ```
93
+
94
+ `readTileflowNotices(style.metadata)` returns the bounded notices of a style for an application
95
+ that draws its own legend or exports images by other means. Show the notice wherever such a map is
96
+ displayed.
97
+
98
+ ## Source layers and fields
99
+
100
+ Every feature carries `provider`, `provenance`, `edition`, `coverage`, `licence`, and `min_zoom`,
101
+ the first zoom at which the feature appears. `rank` (1 is the most important feature of its layer)
102
+ and `name` are optional. Colours are S-57 colour names, and multi-valued colours and categories are
103
+ comma-separated in source order, for example `black,yellow`. `tileflowNauticalV1Schema` lists the
104
+ field names, layers, and geometry types.
105
+
106
+ | Source layer | Geometry | Fields the module reads |
107
+ | ----------------- | -------------------------- | -------------------------------------------------------- |
108
+ | `aid` | Point | `class`, `subclass`, `category`, `color`, `rank`, `name` |
109
+ | `light` | Point | `class` (`major` or `minor`), `color`, `label`, `rank` |
110
+ | `hazard` | Point, LineString, Polygon | `class`, `water_level`, `name` |
111
+ | `wreck` | Point, Polygon | `class`, `name` |
112
+ | `reef` | Polygon | `name` |
113
+ | `navigation_area` | Point, LineString, Polygon | `class`, `size`, `rank`, `name` |
114
+ | `coverage` | Polygon | `families` |
115
+ | `sounding` | Point | `depth` |
116
+
117
+ Every layer filters on `min_zoom`, so a compatible source that keeps features in lower-zoom tiles
118
+ still shows each one from its own first zoom. Point groups place a lower `rank` first.
119
+
120
+ ## Semantic groups
121
+
122
+ Each group is a style option of `nautical(...)`, with the same marker, symbol, area, and line style
123
+ objects as other modules.
124
+
125
+ | Option | Draws | Default symbology |
126
+ | ----------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------- |
127
+ | `buoys` | `aid` buoys, light floats, and light vessels | Circle in the first colour, ringed with the second |
128
+ | `beacons` | `aid` beacons and daymarks | Small circle in the first colour |
129
+ | `lighthouses` | `aid` lighthouses | Ink circle and name |
130
+ | `aids` | Other `aid` classes, such as moorings, fog signals, and racons | Small hollow circle |
131
+ | `lights` | `light` points, one per sector | Flare in the light's first colour and its chart `label` |
132
+ | `hazards` | `hazard` points | `+` for rocks, `*` for rocks that show; circled obstacles |
133
+ | `hazardAreas` | `hazard` polygons; the outline also strokes linear hazards | Accent wash with a dotted outline |
134
+ | `wrecks` | `wreck` points | `Wk`, in the accent colour when dangerous |
135
+ | `wreckAreas` | `wreck` polygons | Ink wash with a dashed outline |
136
+ | `reefs` | `reef` polygons | Sand wash with a dotted outline |
137
+ | `navigationAreas` | `navigation_area` polygons | Dashed outlines; accent for operational areas |
138
+ | `routes` | `navigation_area` lines of class `route` and `traffic-separation` | Dashed line; accent for traffic separation |
139
+ | `cables` | `navigation_area` lines of class `cable`, `pipeline`, and their area classes | Fine dashed accent line |
140
+ | `harbours` | `navigation_area` points of class `harbour` | Ink circle and name |
141
+ | `ports` | `navigation_area` points of class `port` | Ink circle and name sized by `size`, from low zooms |
142
+ | `coverage` | `coverage` polygons that cover every family | Faint tint at low zooms only |
143
+ | `soundings` | `sounding` points | Depth figure |
144
+ | `labels` | Names inside hazard, navigation-area, reef, and wreck polygons; coverage opt-in | Collision-aware text |
145
+
146
+ Operational areas are restricted, military, anchorage, anchor-berth, dumping-ground, and
147
+ traffic-separation areas. Marks keep their conventional colours in every theme; a white light is
148
+ drawn as a warm yellow so it remains visible on pale water. Ink, halo, and accent come from the
149
+ theme's `hydro.label`, `hydro.water`, and `poi.transport` colours, so a theme can restyle Nautical
150
+ without new options. Override any group like any other module style:
151
+
152
+ <!-- docs:check -->
153
+
154
+ ```ts
155
+ import {defineMap, fixed, nautical} from '@tileflow/core';
156
+ import {streets} from '@tileflow/maps';
157
+
158
+ export default defineMap({
159
+ id: 'harbour-guide',
160
+ version: 1,
161
+ extends: streets,
162
+ marine: 'nautical',
163
+ modules: {
164
+ nautical: nautical({
165
+ buoys: {minZoom: 12},
166
+ cables: {visible: false},
167
+ navigationAreas: {
168
+ fill: {opacity: fixed(0.04, {reason: 'Keep operational areas quiet on the base map'})},
169
+ },
170
+ labels: {coverage: {visible: true}},
171
+ }),
172
+ },
173
+ });
174
+ ```
175
+
176
+ ## Chart symbols from the theme
177
+
178
+ A theme that declares the image tokens below replaces a point group's circle marker with that icon.
179
+ Buoy variants fall back to `nautical.buoy`. `tileflowNauticalImageRoles` lists the tokens.
180
+
181
+ | Token | Group |
182
+ | ------------------------- | ------------------------------------------ |
183
+ | `nautical.buoy` | `buoys` |
184
+ | `nautical.buoy.port` | Lateral buoys of a port-hand category |
185
+ | `nautical.buoy.starboard` | Lateral buoys of a starboard-hand category |
186
+ | `nautical.buoy.cardinal` | Cardinal buoys |
187
+ | `nautical.beacon` | `beacons` |
188
+ | `nautical.lighthouse` | `lighthouses` |
189
+ | `nautical.light` | `lights` |
190
+ | `nautical.aid` | `aids` |
191
+ | `nautical.wreck` | `wrecks` |
192
+ | `nautical.harbour` | `harbours` |
193
+ | `nautical.port` | `ports` |
194
+
195
+ Buoys, beacons, other aids, and lights colour their icon with the mark's own colour, so declare
196
+ them as [SDF icons](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/native-icons.md)
197
+ that share one representation within each group. The defaults size icons for the SDF profile,
198
+ whose drawing occupies the central 12 of 24 pixels; set `icon.size` for other artwork. Soundings
199
+ declares these tokens with its own buoy, light, lighthouse, wreck, and harbour symbols and keeps
200
+ beacons as coloured dots.
@@ -79,13 +79,16 @@ import {
79
79
  attachTileflowAtmosphere,
80
80
  attachTileflowFairUseNotice,
81
81
  attachTileflowMapLifecycle,
82
+ attachTileflowNotices,
82
83
  createTileflowSessionStarter,
83
84
  createTileflowTransformRequest,
84
85
  registerTileflowWorldRequestBridge,
85
86
  } from '@tileflow/core/browser';
86
87
  ```
87
88
 
88
- The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
89
+ `attachTileflowNotices(map)` renders the notices a compiled style declares, such as Nautical's
90
+ "Not for navigation", as a compact map control; see
91
+ [Nautical marks](nautical-marks.md). The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
89
92
  runtime dependency, and reads no browser global during module evaluation. See the
90
93
  [framework browser runtime contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/framework-browser-runtime.md) and the
91
94
  [cartographic authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/cartographic-authoring.md). Exact field
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/core",
3
- "version": "0.1.0-alpha.36",
3
+ "version": "0.1.0-alpha.37",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Typed Tileflow config and MapLibre style compiler.",
6
6
  "type": "module",