@tileflow/core 0.1.0-alpha.36 → 0.1.0-alpha.38
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/README.md +13 -12
- package/dist/3d-D9c3i78O.d.ts +1080 -0
- package/dist/3d.d.ts +1 -0
- package/dist/3d.js +12082 -0
- package/dist/browser.d.ts +530 -1306
- package/dist/browser.js +7166 -5194
- package/dist/build.d.ts +2 -2
- package/dist/build.js +1707 -824
- package/dist/config-reference.json +3772 -1380
- package/dist/{icon-composition-sN3JkXdb.d.ts → icon-composition-Br4Bd2bY.d.ts} +348 -6
- package/dist/index.d.ts +162 -154
- package/dist/index.js +4003 -2740
- package/dist/worker/camera-tile-worker.global.js +1 -0
- package/docs/application-tile-reading.md +5 -2
- package/docs/authoring-model.md +8 -0
- package/docs/buildings-3d.md +185 -25
- package/docs/compiled-style-performance.md +11 -2
- package/docs/data-is-separate-from-design.md +18 -34
- package/docs/nautical-marks.md +200 -0
- package/docs/public-api-and-browser-subpath.md +8 -1
- package/docs/street-lights.md +5 -1
- package/docs/trees-3d.md +11 -6
- package/package.json +6 -1
- package/dist/worker/buildings-3d-worker.global.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(()=>{var Be=new Set,ln=!1;function Pe(e,n,t=2){let i=n&&n.length,o=i?n[0]*t:e.length;Be.size&&Be.clear();let r=jn(e,0,o,t,!0),s=[];if(!r||r.next===r.prev)return s;let a=0,l=0,u=0;if(i&&(r=wt(e,n,r,t)),e.length>80*t){a=e[0],l=e[1];let c=a,f=l;for(let d=t;d<o;d+=t){let g=e[d],m=e[d+1];g<a&&(a=g),m<l&&(l=m),g>c&&(c=g),m>f&&(f=m)}u=Math.max(c-a,f-l),u=u!==0?32767/u:0}return un(r,s,a,l,u),s}function jn(e,n,t,i,o){let r=null;if(o===Ut(e,n,t,i)>0)for(let s=n;s<t;s+=i)r=In(s/i|0,e[s],e[s+1],r);else for(let s=t-i;s>=n;s-=i)r=In(s/i|0,e[s],e[s+1],r);return r&&Ue(r,r.next)&&(Oe(r),r=r.next),r}function ge(e,n=e){let t=n===e,i=e,o;do o=!1,i!==i.next&&(Be.size===0||!Be.has(i))&&(Ue(i,i.next)||Q(i.prev,i,i.next)===0)?((t||i===n)&&(n=i.prev),ln=!0,Oe(i),i=i.prev,o=!0):(t||i!==n)&&(i=i.next,o=!t);while(o||i!==n);return n}function un(e,n,t,i,o){o&&At(e,t,i,o);let r=e,s=!1;for(;e.prev!==e.next;){let a=e.prev,l=e.next;if(Q(a,e,l)<0&&(o?yt(e,t,i,o):pt(e))){n.push(a.i,e.i,l.i),Oe(e),e=l,r=l;continue}if(e=l,e===r){if(ln=!1,e=ge(e),ln){r=e;continue}if(!s){e=bt(e,n),r=e,s=!0;continue}gt(e,n,t,i,o);break}}}function pt(e){let n=e.prev,t=e,i=e.next,o=n.x,r=t.x,s=i.x,a=n.y,l=t.y,u=i.y,c=Math.min(o,r,s),f=Math.min(a,l,u),d=Math.max(o,r,s),g=Math.max(a,l,u),m=i.next;for(;m!==n;){if(m.x>=c&&m.x<=d&&m.y>=f&&m.y<=g&&!(o===m.x&&a===m.y)&&Ke(o,a,r,l,s,u,m.x,m.y)&&Q(m.prev,m,m.next)>=0)return!1;m=m.next}return!0}function yt(e,n,t,i){let o=e.prev,r=e,s=e.next,a=o.x,l=r.x,u=s.x,c=o.y,f=r.y,d=s.y,g=Math.min(a,l,u),m=Math.min(c,f,d),p=Math.max(a,l,u),h=Math.max(c,f,d),A=mn(g,m,n,t,i),F=mn(p,h,n,t,i),k=e.prevZ;for(;k&&k.z>=A;){if(k.x>=g&&k.x<=p&&k.y>=m&&k.y<=h&&k!==s&&!(a===k.x&&c===k.y)&&Ke(a,c,l,f,u,d,k.x,k.y)&&Q(k.prev,k,k.next)>=0)return!1;k=k.prevZ}let M=e.nextZ;for(;M&&M.z<=F;){if(M.x>=g&&M.x<=p&&M.y>=m&&M.y<=h&&M!==s&&!(a===M.x&&c===M.y)&&Ke(a,c,l,f,u,d,M.x,M.y)&&Q(M.prev,M,M.next)>=0)return!1;M=M.nextZ}return!0}function bt(e,n){let t=e,i=!1;do{let o=t.prev,r=t.next.next;Nn(o,t,t.next,r,!1)&&Ce(o,r)&&Ce(r,o)&&(n.push(o.i,t.i,r.i),Oe(t),Oe(t.next),t=e=r,i=!0),t=t.next}while(t!==e);return i?ge(t):t}function gt(e,n,t,i,o){let r=e;do{let s=r.next.next;for(;s!==r.prev;){if(r.i!==s.i&&_t(r,s)){let a=Vn(r,s);r=ge(r,r.next),a=ge(a,a.next),un(r,n,t,i,o),un(a,n,t,i,o);return}s=s.next}r=r.next}while(r!==e)}var cn=!1;function wt(e,n,t,i){let o=[];for(let r=0,s=n.length;r<s;r++){let a=n[r]*i,l=r<s-1?n[r+1]*i:e.length,u=jn(e,a,l,i,!1);u===u.next&&Be.add(u),o.push(St(u))}o.sort(xt),Mt(e.length/i,n.length),zn(t,t),cn=!0;for(let r=0;r<o.length;r++)t=Tt(o[r],t);return cn=!1,ge(t)}function xt(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 Tt(e,n){let t=vt(e,n);if(!t)return n;let i=Vn(t,e),o=i.next;return zn(t,o.next),ge(i,i.next),ge(t,t.next)}var Dn=16,X=new Float64Array(0),Je=0,fn=[],dn=[];function Mt(e,n){let t=Math.ceil((e+2*n)/Dn)+n+2;X.length<t*4&&(X=new Float64Array(t*4)),Je=0}function zn(e,n){let t=e;do{let i=Je++;fn[i]=t;let o=t.x,r=t.y,s=t.x,a=t.y,l=0;do{let c=t.next;t.z=i,c.x<o&&(o=c.x),c.x>s&&(s=c.x),c.y<r&&(r=c.y),c.y>a&&(a=c.y),t=c}while(++l<Dn&&t!==n);dn[i]=t;let u=i*4;X[u]=o,X[u+1]=r,X[u+2]=s,X[u+3]=a}while(t!==n)}function kt(e,n){let t=e.z*4;n.x<X[t]&&(X[t]=n.x),n.y<X[t+1]&&(X[t+1]=n.y),n.x>X[t+2]&&(X[t+2]=n.x),n.y>X[t+3]&&(X[t+3]=n.y)}function Pn(e){let n=dn[e];for(;n.prev.next!==n;)n=n.next;return dn[e]=n,n}function En(e){let n=fn[e];for(;n.prev.next!==n;)n=n.next;return fn[e]=n,n}function vt(e,n){let t=n,i=e.x,o=e.y,r=-1/0,s;if(Ue(e,t))return t;for(let d=0,g=0;d<Je;d++,g+=4){if(o<X[g+1]||o>X[g+3]||X[g]>i||X[g+2]<=r)continue;let m=Pn(d);t=En(d);do{if(t.prev.next===t){if(Ue(e,t.next))return t.next;if(o<=t.y&&o>=t.next.y&&t.next.y!==t.y){let p=t.x+(o-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(p<=i&&p>r&&(r=p,s=t.x<t.next.x?t:t.next,p===i))return s}}t=t.next}while(t!==m)}if(!s)return null;let a=s.x,l=s.y,u=Math.min(o,l),c=Math.max(o,l),f=1/0;for(let d=0,g=0;d<Je;d++,g+=4){if(X[g+2]<a||X[g]>i||X[g+3]<u||X[g+1]>c)continue;let m=Pn(d);t=En(d);do{if(t.prev.next===t&&i>=t.x&&t.x>=a&&i!==t.x&&Ke(o<l?i:r,o,a,l,o<l?r:i,o,t.x,t.y)){let p=Math.abs(o-t.y)/(i-t.x);(Ce(t,e)||t.y===o&&t.next.y===o&&t.next.x>i)&&(p<f||p===f&&(t.x>s.x||t.x===s.x&&Rt(s,t)))&&(s=t,f=p)}t=t.next}while(t!==m)}return s}function Rt(e,n){return Q(e.prev,e,n.prev)<0&&Q(n.next,e,e.next)<0}var ie=[],ve=[],be=new Uint32Array(0),_e=new Uint32Array(0),Le=new Uint32Array(256);function At(e,n,t,i){let o=e,r=0;do o.z=mn(o.x,o.y,n,t,i),ie[r++]=o,o=o.next;while(o!==e);Ft(r);let s=null;for(let a=0;a<r;a++){let l=ie[a];l.prevZ=s,s&&(s.nextZ=l),s=l}s.nextZ=null,ie.fill(null,0,r),ve.fill(null,0,r)}function Ft(e){if(e<=32){for(let n=1;n<e;n++){let t=ie[n],i=t.z,o=n-1;for(;o>=0&&ie[o].z>i;)ie[o+1]=ie[o],o--;ie[o+1]=t}return}be.length<e&&(be=new Uint32Array(e),_e=new Uint32Array(e),ve=new Array(e));for(let n=0;n<e;n++)be[n]=ie[n].z;Xe(e,ie,be,ve,_e,0),Xe(e,ve,_e,ie,be,8),Xe(e,ie,be,ve,_e,16),Xe(e,ve,_e,ie,be,24)}function Xe(e,n,t,i,o,r){Le.fill(0);for(let a=0;a<e;a++)Le[t[a]>>>r&255]++;let s=0;for(let a=0;a<256;a++){let l=Le[a];Le[a]=s,s+=l}for(let a=0;a<e;a++){let l=t[a],u=Le[l>>>r&255]++;i[u]=n[a],o[u]=l}}function mn(e,n,t,i,o){return e=(e-t)*o|0,n=(n-i)*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 St(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 Ke(e,n,t,i,o,r,s,a){return(o-s)*(n-a)>=(e-s)*(r-a)&&(e-s)*(i-a)>=(t-s)*(n-a)&&(t-s)*(r-a)>=(o-s)*(i-a)}function _t(e,n){let t=Ue(e,n)&&Q(e.prev,e,e.next)>0&&Q(n.prev,n,n.next)>0;return e.next.i!==n.i&&(t||Ce(e,n)&&Ce(n,e)&&(Q(e.prev,e,n.prev)!==0||Q(e,n.prev,n)!==0))&&!Lt(e,n)&&(t||Bt(e,n))}function Q(e,n,t){return(n.y-e.y)*(t.x-n.x)-(n.x-e.x)*(t.y-n.y)}function Ue(e,n){return e.x===n.x&&e.y===n.y}function Nn(e,n,t,i,o=!0){let r=Q(e,n,t),s=Q(e,n,i),a=Q(t,i,e),l=Q(t,i,n);return(r>0&&s<0||r<0&&s>0)&&(a>0&&l<0||a<0&&l>0)?!0:o?!!(r===0&&Ye(e,t,n)||s===0&&Ye(e,i,n)||a===0&&Ye(t,e,i)||l===0&&Ye(t,n,i)):!1}function Ye(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 Lt(e,n){let t=Math.min(e.x,n.x),i=Math.max(e.x,n.x),o=Math.min(e.y,n.y),r=Math.max(e.y,n.y),s=e;do{let a=s.next;if(s.x>i&&a.x>i||s.x<t&&a.x<t||s.y>r&&a.y>r||s.y<o&&a.y<o){s=a;continue}if(s.i!==e.i&&a.i!==e.i&&s.i!==n.i&&a.i!==n.i&&Nn(s,a,e,n))return!0;s=a}while(s!==e);return!1}function Ce(e,n){return Q(e.prev,e,e.next)<0?Q(e,n,e.next)>=0&&Q(e,e.prev,n)>=0:Q(e,n,e.prev)<0||Q(e,e.next,n)<0}function Bt(e,n){let t=e,i=!1,o=(e.x+n.x)/2,r=(e.y+n.y)/2;do{let s=t.next;t.y>r!=s.y>r&&o<(s.x-t.x)*(r-t.y)/(s.y-t.y)+t.x&&(i=!i),t=s}while(t!==e);return i}function Vn(e,n){let t=hn(e.i,e.x,e.y),i=hn(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,i.next=t,t.prev=i,r.next=i,i.prev=r,i}function In(e,n,t,i){let o=hn(e,n,t);return i?(o.next=i.next,o.prev=i,i.next.prev=o,i.next=o):(o.prev=o,o.next=o),o}function Oe(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),cn&&kt(e.prev,e.next)}function hn(e,n,t){return{i:e,x:n,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Ut(e,n,t,i){let o=0;for(let r=n,s=t-i;r<t;r+=i)o+=(e[s]-e[r])*(e[r+1]+e[s+1]),s=r;return o}var Ct=.7,Ot=Math.tan(Math.PI/6),Pt=Math.tan(35*Math.PI/180),Et=Math.tan(Math.PI/18),Ee=.65,Ie=.4;function Zn(e,n,t,i,o,r,s){let a=String(t.roof_shape??"");if(!a||a==="flat"||a==="sawtooth"||Nt(e,s))return;let l=Dt(e);if(!l||Math.abs(pn(e))<Ct*4*l.halfU*l.halfV)return;let u=l.u,c=l.v,f=l.halfU*r,d=l.halfV*r;f<d&&([u,c,f,d]=[c,u,d,f]);let g=Hn(t.roof_direction),m=g===void 0?void 0:[Math.sin(g*Math.PI/180),-Math.cos(g*Math.PI/180)];(m!==void 0?Math.abs(m[0]*u[0]+m[1]*u[1])>Math.abs(m[0]*c[0]+m[1]*c[1]):t.roof_orientation==="across")&&a!=="skillion"&&([u,c,f,d]=[c,u,d,f]);let h=jt(a,f,d,m&&[Gn(m,u),Gn(m,c)]);if(!h)return;let A=Math.max(0,o-i),F=Hn(t.roof_height),k,M;if(F!==void 0&&F>0)M=o,k=Math.max(i+Math.min(2,A/2),o-F);else{let y=Math.min(It(a,d),A*.6,30);t.height_source==="height"?(M=o,k=o-y):(k=o,M=o+y)}let B=M-k;if(!(B>=.3))return;let[C,E]=l.centre,O=(y,b)=>{let x=(y-C)*r,T=(b-E)*r;return[x*u[0]+T*u[1],x*c[0]+T*c[1]]},I=(y,b)=>[C+(y*u[0]+b*c[0])/r,E+(y*u[1]+b*c[1])/r],N=(y,b)=>{let x=1/0;for(let[T,S,v]of h)x=Math.min(x,T*y+S*b+v);return Math.max(0,x)},K=(y,b)=>k+B*N(...O(y,b)),G=[];for(let y=0;y+1<n.length;y+=2)G.push(...O(n[y],n[y+1]));pn(G)<0&&Vt(G);let z=1.01,Y=[-f*z,-d*z,f*z,-d*z,f*z,d*z,-f*z,d*z],w=[];for(let[y,b]of h.entries()){let x=Y;for(let[P,Z]of h.entries()){if(P===y)continue;let D=b[0]-Z[0],H=b[1]-Z[1],W=b[2]-Z[2];if(Math.abs(D)+Math.abs(H)+Math.abs(W)<1e-9){P<y&&(x=[]);continue}if(x=$n(x,D,H,W),x.length<6)break}if(x.length<6)continue;let T=G,S=x.length/2;for(let P=0;P<S&&T.length>=6;P+=1){let[Z,D]=[x[P*2],x[P*2+1]],[H,W]=[x[(P+1)%S*2],x[(P+1)%S*2+1]];T=$n(T,W-D,Z-H,-(W-D)*Z-(Z-H)*D)}if(T.length<6||Math.abs(pn(T))<1e-6)continue;let v=[];for(let P=0;P<T.length;P+=2)v.push(...I(T[P],T[P+1]));let L=Pe(v);if(L.length===0)continue;let U=B*b[0],V=B*b[1],R=U*u[0]+V*c[0],_=U*u[1]+V*c[1],j=Math.hypot(R,_,1);w.push({ring:v,triangles:L,normal:[-R/j,-_/j]})}if(w.length!==0)return{eaves:k,ridge:M,faces:w,heightAt:K,breaks(y,b,x,T){let[S,v]=O(y,b),[L,U]=O(x,T),V=[];for(let R=0;R<h.length;R+=1)for(let _=R+1;_<h.length;_+=1){let j=(ae,$)=>(h[R][0]-h[_][0])*ae+(h[R][1]-h[_][1])*$+(h[R][2]-h[_][2]),P=j(S,v),Z=j(L,U);if(P===Z||P*Z>0)continue;let D=P/(P-Z);if(D<=1e-4||D>=1-1e-4)continue;let H=S+(L-S)*D,W=v+(U-v)*D;h[R][0]*H+h[R][1]*W+h[R][2]<=N(H,W)+1e-6&&V.push(D)}return[...new Set(V.map(R=>Math.round(R*1e6)/1e6))].sort((R,_)=>R-_)}}}function It(e,n){switch(e){case"skillion":return 2*n*Et;case"pyramidal":return n*Pt;case"gambrel":case"mansard":return n*.8;case"dome":case"onion":case"round":return n;default:return n*Ot}}function jt(e,n,t,i){let o=n,r=t,s=[[0,-1/r,1],[0,1/r,1]],a=u=>u.flatMap(c=>[1,-1].flatMap(f=>{let d=c==="v"?r:o,g=Ee/(Ie*r)*f,m=(1-Ee)/((1-Ie)*r)*f,p=h=>c==="v"?[0,-h]:[-h,0];return[[...p(g),Ee/(Ie*r)*d],[...p(m),Ee+(1-Ee)/((1-Ie)*r)*(d-Ie*r)]]})),l=[.99,.8,.5,.2];switch(e){case"gabled":return s;case"saltbox":{let u=r/3;return[[0,-1/(r-u),1+u/(r-u)],[0,1/(r+u),1-u/(r+u)]]}case"hipped":return[...s,[-1/r,0,o/r],[1/r,0,o/r]];case"half_hipped":return[...s,[-1/r,0,.5+o/r],[1/r,0,.5+o/r]];case"pyramidal":return[...s,[-1/o,0,1],[1/o,0,1]];case"gambrel":return a(["v"]);case"mansard":return a(["v","u"]);case"skillion":{let[u,c]=i??[0,1],f=Math.abs(u)*o+Math.abs(c)*r;return f>0?[[-u/(2*f),-c/(2*f),.5]]:void 0}case"round":return[[0,0,1],...l.flatMap(u=>{let c=Math.sqrt(1-u*u);return[1,-1].map(f=>[0,-f*u/(r*c),1/c])})];case"dome":case"onion":return[[0,0,1],...l.flatMap(u=>{let c=Math.sqrt(1-u*u);return Array.from({length:12},(f,d)=>{let g=d*Math.PI/6;return[-u*Math.cos(g)/(o*c),-u*Math.sin(g)/(r*c),1/c]})})];default:return}}function $n(e,n,t,i){let o=e.length/2,r=[];for(let s=0;s<o;s+=1){let a=e[s*2],l=e[s*2+1],u=e[(s+1)%o*2],c=e[(s+1)%o*2+1],f=n*a+t*l+i,d=n*u+t*c+i;if(f<=0&&r.push(a,l),f<0&&d>0||f>0&&d<0){let g=f/(f-d);r.push(a+(u-a)*g,l+(c-l)*g)}}return r}function Dt(e){let n=zt(e);if(n.length<3)return;let t,i=1/0;for(let o=0;o<n.length;o+=1){let[r,s]=n[o],[a,l]=n[(o+1)%n.length],u=Math.hypot(a-r,l-s);if(u===0)continue;let c=[(a-r)/u,(l-s)/u],f=[-c[1],c[0]],[d,g,m,p]=[1/0,-1/0,1/0,-1/0];for(let[A,F]of n){let k=A*c[0]+F*c[1],M=A*f[0]+F*f[1];d=Math.min(d,k),g=Math.max(g,k),m=Math.min(m,M),p=Math.max(p,M)}let h=(g-d)*(p-m);if(h<i){i=h;let A=(d+g)/2,F=(m+p)/2;t={centre:[A*c[0]+F*f[0],A*c[1]+F*f[1]],u:c,v:f,halfU:(g-d)/2,halfV:(p-m)/2}}}return t&&t.halfU>0&&t.halfV>0?t:void 0}function zt(e){let n=[];for(let r=0;r+1<e.length;r+=2)n.push([e[r],e[r+1]]);n.sort((r,s)=>r[0]-s[0]||r[1]-s[1]);let t=(r,s,a)=>(s[0]-r[0])*(a[1]-r[1])-(s[1]-r[1])*(a[0]-r[0]),i=[];for(let r of n){for(;i.length>=2&&t(i.at(-2),i.at(-1),r)<=0;)i.pop();i.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[...i.slice(0,-1),...o.slice(0,-1)]}function Nt(e,n){let[t,i,o,r]=[1/0,-1/0,1/0,-1/0];for(let a=0;a+1<e.length;a+=2)t=Math.min(t,e[a]),i=Math.max(i,e[a]),o=Math.min(o,e[a+1]),r=Math.max(r,e[a+1]);let s=e.length/2;for(let a=0;a<s;a+=1){let l=e[a*2],u=e[a*2+1],c=e[(a+1)%s*2],f=e[(a+1)%s*2+1];if(t<0&&l===t&&c===t||i>n&&l===i&&c===i||o<0&&u===o&&f===o||r>n&&u===r&&f===r)return!0}return!1}function pn(e){let n=0,t=e.length/2;for(let i=0;i<t;i+=1){let o=(i+1)%t;n+=e[i*2]*e[o*2+1]-e[o*2]*e[i*2+1]}return n/2}function Vt(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 Gn(e,n){return e[0]*n[0]+e[1]*n[1]}function Hn(e){let n=typeof e=="string"?Number(e):e;return typeof n=="number"&&Number.isFinite(n)?n:void 0}var jr=Object.freeze({surface:0,secondary:1,glass:2,accent:3,roof:4}),Dr=Object.freeze({unclassified:0,wall:1,door:2,roof:3,window:4,lamp:5,logo:6}),zr=Object.freeze([1,1,4,1,3]),Nr=Object.freeze({levels:3,vertices:2e5,triangles:2e5,footprintPoints:4096,radius:2e3,height:1e3,models:512,bytes:16*1024*1024});function yn(e,n,t){for(let i of e){let o=!1;for(let r=0,s=i.length-2;r<i.length;s=r,r+=2){let a=i[r],l=i[r+1],u=i[s],c=i[s+1];l>t!=c>t&&n<(u-a)*(t-l)/(c-l)+a&&(o=!o)}if(o)return!0}return!1}function Yn(e,n,t){for(let i of n){let[o,r,s,a]=Xn(i);for(let l of e){let[u,c,f,d]=Xn(l);if(!(u>s-t||f<o+t||c>a-t||d<r+t)&&(Wn(l,i,t)||Wn(i,l,t)))return!0}}return!1}function Wn(e,n,t){let i=(o,r)=>yn([n],o,r)&&Gt(n,o,r)>t;for(let o=0;o+1<e.length;o+=2)if(i(e[o],e[o+1]))return!0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[s,a,l,u]=[e[r],e[r+1],e[o],e[o+1]],c=[0,1];for(let f=0,d=n.length-2;f<n.length;d=f,f+=2){let g=$t(s,a,l,u,n[d],n[d+1],n[f],n[f+1]);g!==void 0&&c.push(g)}if(c.length!==2){c.sort((f,d)=>f-d);for(let f=1;f<c.length;f+=1){let d=(c[f-1]+c[f])/2;if(i(s+(l-s)*d,a+(u-a)*d))return!0}}}return!1}function $t(e,n,t,i,o,r,s,a){let l=t-e,u=i-n,c=s-o,f=a-r,d=l*f-u*c;if(d===0)return;let g=((o-e)*f-(r-n)*c)/d,m=((o-e)*u-(r-n)*l)/d;return g>0&&g<1&&m>=0&&m<=1?g:void 0}function Gt(e,n,t){let i=1/0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[s,a,l,u]=[e[r],e[r+1],e[o],e[o+1]],c=(l-s)**2+(u-a)**2,f=c>0?Math.max(0,Math.min(1,((n-s)*(l-s)+(t-a)*(u-a))/c)):0;i=Math.min(i,Math.hypot(n-(s+(l-s)*f),t-(a+(u-a)*f)))}return i}function Xn(e){let[n,t,i,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]),i=Math.max(i,e[r]),t=Math.min(t,e[r+1]),o=Math.max(o,e[r+1]);return[n,t,i,o]}var ue=8192,pe=28,Ht={commercial:1,active:2,destination:3},Zt={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]))},Wt={dining:1,entertainment:2,nightlife:3},bn={...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]))},Xt=6,Yt=.5,Jt=.5;function Kn(e,n){let t=ue/e.extent,i=n.tileMetres/ue,o=n.defaultHeight??5,r=Number.isFinite(n.edgeRadius)?Math.max(0,Math.min(1,n.edgeRadius))/i:0,s=new wn,a=new Qe,l=new Qe,u=[],c=[],f=0,d=er(e,n);for(let[M,B]of e.features.entries()){if(B.type!=="Polygon"||u.length>=65535)continue;let C=B.properties,E=C.hide_3d;if(E===!0||E===1||E==="1"||E==="true")continue;let O=Math.min(6e3,Jn(C.render_height)??o),I=Math.min(O,Jn(C.render_min_height)??0),N=Ht[String(C.building_tone??"")]??0,K=Zt[String(C.class??"")]??0,G=d.get(M)??0,z=u.length,Y=!1;for(let w of nr(B.geometry,t)){let y=w.map(rr).filter(_=>_.length>=6);if(y.length===0||qe(y[0])===0)continue;let[b,x]=tr(y[0]);if(n.masks?.length&&(yn(n.masks,b,x)||Yn(y,n.masks,Jt/i)))continue;let T={cell:Math.min(7,Math.max(0,Math.floor(x/1024)))*8+Math.min(7,Math.max(0,Math.floor(b/1024))),bounds:[1/0,1/0,-1/0,-1/0,O+.1],roofsFrom:a.length,roofsCount:0,wallsFrom:l.length,wallsCount:0};for(let _ of y)for(let j=0;j<_.length;j+=2)T.bounds[0]=Math.min(T.bounds[0],_[j]-1),T.bounds[1]=Math.min(T.bounds[1],_[j+1]-1),T.bounds[2]=Math.max(T.bounds[2],_[j]+1),T.bounds[3]=Math.max(T.bounds[3],_[j+1]+1);Y=!0,f=Math.max(f,O);let S=Number.isSafeInteger(B.id),v=gn(S?B.id*.0731:b*.0731+x*.19137+z*1.618),L=!S&&y[0].some(_=>_<=0||_>=ue)?gn(O*7.31+I*3.17+N):v,U={base:I,top:O,jitter:L,seed:v,tone:N,group:K,use:G,cx:b,cy:x,index:z},V=y.map((_,j)=>qt(_,j,r,n.cornerAngle));y=V.map(({ring:_})=>_);let R=w.length===1&&y.length===1?Zn(w[0],y[0],C,I,O,i,ue):void 0;R?(f=Math.max(f,R.ridge),T.bounds[4]=Math.max(T.bounds[4],R.ridge+.1),Qt(R,U,s,a)):Kt(y,U,s,a);for(let[_,j]of y.entries()){let P=qe(j)>0,Z=_===0?P?-1:1:P?1:-1,D=j.length/2,H=$=>{let J=($%D+D)%D;return[j[J*2],j[J*2+1]]},W=$=>{let[J,re]=H($-1),[ne,te]=H($),[le,de]=H($+1);if(we(J,re,ne,te)||we(ne,te,le,de))return!1;let me=Math.hypot(ne-J,te-re),ce=Math.hypot(le-ne,de-te);if(!me||!ce)return!1;let De=-(te-re)/me*Z,ze=(ne-J)/me*Z;return(De*(le-ne)+ze*(de-te))/ce>Yt},se=($,J)=>R?R.heightAt($,J):O,ae=0;for(let $=0;$<D;$+=1){let[J,re]=H($),[ne,te]=H($+1),le=Math.hypot(ne-J,te-re);if(le===0||we(J,re,ne,te))continue;let de=-(te-re)/le*Z,me=(ne-J)/le*Z,ce=V[_].normals[$]??[de,me],De=V[_].normals[($+1)%D]??[de,me],ze=oe=>{let Ae=ce[0]+(De[0]-ce[0])*oe,Fe=ce[1]+(De[1]-ce[1])*oe,ye=Math.hypot(Ae,Fe)||1;return[Ae/ye,Fe/ye]},he=[0,...R?R.breaks(J,re,ne,te):[],1];for(let oe=0;oe+1<he.length;oe+=1){let Ae=oe===0,Fe=oe+2===he.length,ye=J+(ne-J)*he[oe],Ne=re+(te-re)*he[oe],Ve=J+(ne-J)*he[oe+1],$e=re+(te-re)*he[oe+1],Se=Math.hypot(Ve-ye,$e-Ne)/ue*n.tileMetres,Ge=(Ae&&W($)?1:0)|(Fe&&W($+1)?2:0)|(_>0?4:0),He=(Ae&&xn(J,re)?1:0)|(Fe&&xn(ne,te)?2:0),an=ae+Se,Ze=ze(he[oe]),We=ze(he[oe+1]),Cn=se(ye,Ne),On=se(Ve,$e),ke=s.write(U,ye,Ne,0,Ze[0],Ze[1],1,ae,Ge,Se,0,He,Cn);s.write(U,Ve,$e,0,We[0],We[1],1,an,Ge,Se,1,He,On),s.write(U,ye,Ne,1,Ze[0],Ze[1],1,ae,Ge,Se,0,He,Cn),s.write(U,Ve,$e,1,We[0],We[1],1,an,Ge,Se,1,He,On),l.push(ke,ke+1,ke+2,ke+1,ke+3,ke+2),ae=an}}}T.roofsCount=a.length-T.roofsFrom,T.wallsCount=l.length-T.wallsFrom,c.push(T)}Y&&u.push(C)}if(s.count===0)return;let g=new Uint32Array(a.length+l.length),m=[],p=0,h=a.length,A=a.values(),F=l.values();c.sort((M,B)=>M.cell-B.cell);let k=-1;for(let M of c){M.cell!==k&&(k=M.cell,m.push({bounds:[...M.bounds],roofsFrom:p,roofsCount:0,wallsFrom:h,wallsCount:0}));let B=m.at(-1);for(let C=0;C<5;C+=1)B.bounds[C]=C<2?Math.min(B.bounds[C],M.bounds[C]):Math.max(B.bounds[C],M.bounds[C]);g.set(A.subarray(M.roofsFrom,M.roofsFrom+M.roofsCount),p),g.set(F.subarray(M.wallsFrom,M.wallsFrom+M.wallsCount),h),p+=M.roofsCount,h+=M.wallsCount,B.roofsCount+=M.roofsCount,B.wallsCount+=M.wallsCount}return{groups:m,vertices:s.bytes(),indices:g,wallsFrom:a.length,top:f,features:u}}function Kt(e,n,t,i){let o=[],r=[];for(let l of e)r.push(o.length/2),o.push(...l);r.push(o.length/2);let s=(l,u)=>{let c=r.findIndex((p,h)=>l>=p&&l<r[h+1]),[f,d]=[r[c],r[c+1]-1];if(u<f||u>d)return!1;let[g,m]=l<u?[l,u]:[u,l];return m-g!==1&&!(g===f&&m===d)?!1:!we(o[l*2],o[l*2+1],o[u*2],o[u*2+1])},a=Pe(o,r.slice(1,-1),2);for(let l=0;l+2<a.length;l+=3){let u=[a[l],a[l+1],a[l+2]],c=0;for(let f=0;f<3;f+=1)s(u[(f+1)%3],u[(f+2)%3])&&(c|=1<<f);for(let[f,d]of u.entries())i.push(t.write(n,o[d*2],o[d*2+1],1,0,0,0,0,0,0,f,c))}}function Qt(e,n,t,i){for(let o of e.faces){let r=o.ring,s=r.length/2,a=(l,u)=>{let[c,f]=l<u?[l,u]:[u,l];return f-c!==1&&!(c===0&&f===s-1)?!1:!we(r[l*2],r[l*2+1],r[u*2],r[u*2+1])};for(let l=0;l+2<o.triangles.length;l+=3){let u=[o.triangles[l],o.triangles[l+1],o.triangles[l+2]],c=0;for(let f=0;f<3;f+=1)a(u[(f+1)%3],u[(f+2)%3])&&(c|=1<<f);for(let[f,d]of u.entries()){let g=r[d*2],m=r[d*2+1];i.push(t.write(n,g,m,1,o.normal[0],o.normal[1],0,0,0,0,f,c,e.heightAt(g,m)))}}}}function qt(e,n,t,i=Math.PI/6){if(t<1)return{ring:e,normals:[]};let o=e.length/2,r=qe(e)>0,s=n===0?r?-1:1:r?1:-1,a=[],l=[],u=(c,f,d)=>{c=Re(c),f=Re(f),!(a.at(-2)===c&&a.at(-1)===f)&&(a.push(c,f),l.push(d))};for(let c=0;c<o;c+=1){let f=(c+o-1)%o,d=(c+1)%o,[g,m]=[e[f*2],e[f*2+1]],[p,h]=[e[c*2],e[c*2+1]],[A,F]=[e[d*2],e[d*2+1]],k=Math.hypot(p-g,h-m),M=Math.hypot(A-p,F-h);if(!k||!M||xn(p,h)||we(g,m,p,h)||we(p,h,A,F)){u(p,h);continue}let[B,C]=[(p-g)/k,(h-m)/k],[E,O]=[(A-p)/M,(F-h)/M],I=Math.atan2(B*O-C*E,B*E+C*O);if(I*s>=-.08||Math.abs(I)>Math.PI*.85){u(p,h);continue}let N=Math.tan(Math.abs(I)/2),K=Math.min(t*N,k*.2,M*.2),G=K/N;if(G<1){u(p,h);continue}let[z,Y]=[-C*s,B*s],[w,y]=[p-B*K-z*G,h-C*K-Y*G],b=Math.atan2(Y,z),x=Math.max(i<=Math.PI/6?2:1,Math.ceil(Math.abs(I)/Math.max(Math.PI/6,Math.min(Math.PI/3,i))));for(let T=0;T<=x;T+=1){let S=b+I*T/x,v=[Math.cos(S),Math.sin(S)];u(w+v[0]*G,y+v[1]*G,v)}}return a[0]===a.at(-2)&&a[1]===a.at(-1)&&(a.splice(-2),l.pop()),a.length>=6?{ring:a,normals:l}:{ring:e,normals:[]}}var wn=class{#e=new ArrayBuffer(4096*pe);#n=new DataView(this.#e);count=0;write(n,t,i,o,r,s,a,l,u,c,f=0,d=0,g=n.top){let m=pe;if((this.count+1)*m>this.#e.byteLength){let A=new ArrayBuffer(this.#e.byteLength*2);new Uint8Array(A).set(new Uint8Array(this.#e)),this.#e=A,this.#n=new DataView(A)}let p=this.#n,h=this.count*m;return p.setUint16(h,Re(t),!0),p.setUint16(h+2,Re(i),!0),p.setUint16(h+4,Math.min(65535,Math.round(n.base*10)),!0),p.setUint16(h+6,Math.min(65535,Math.max(0,Math.round(g*10))),!0),p.setInt8(h+8,Math.round(r*127)),p.setInt8(h+9,Math.round(s*127)),p.setUint8(h+10,o),p.setUint8(h+11,a),p.setUint16(h+12,Math.round(l*10)%65536,!0),p.setUint8(h+14,Math.round(n.jitter*255)),p.setUint8(h+15,Math.round(n.seed*255)),p.setUint16(h+16,Re(n.cx),!0),p.setUint16(h+18,Re(n.cy),!0),p.setUint8(h+20,n.tone+4*n.group),p.setUint8(h+21,n.use),p.setUint8(h+22,u),p.setUint8(h+23,Math.min(255,Math.round(c))),p.setUint16(h+24,n.index,!0),p.setUint8(h+26,f),p.setUint8(h+27,d),this.count+=1,this.count-1}bytes(){return new Uint8Array(this.#e.slice(0,this.count*pe))}},Qe=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 er(e,n){let t=new Map;if(e.features.some(a=>a.properties.night_use!==void 0)){for(let[a,l]of e.features.entries()){let u=Wt[String(l.properties.night_use??"")];u&&t.set(a,u)}return t}let i=n.places;if(!i||i.features.length===0)return t;let o=Xt/n.tileMetres*e.extent,r=[];for(let[a,l]of e.features.entries()){if(l.type!=="Polygon")continue;let u=l.properties.hide_3d;if(u===!0||u===1||u==="1"||u==="true")continue;let c=[1/0,1/0,-1/0,-1/0];for(let f of l.geometry)for(let d=0;d+1<f.length;d+=2)c[0]=Math.min(c[0],f[d]),c[1]=Math.min(c[1],f[d+1]),c[2]=Math.max(c[2],f[d]),c[3]=Math.max(c[3],f[d+1]);r.push({index:a,rings:l.geometry,box:c})}let s=e.extent/i.extent;for(let a of i.features){if(a.type!=="Point")continue;let l=a.properties,u=bn[String(l.type??"")]??bn[String(l.subclass??"")]??bn[String(l.class??"")];if(u)for(let c of a.geometry)for(let f=0;f+1<c.length;f+=2){let d=c[f]*s,g=c[f+1]*s,m,p,h=o;for(let F of r){let[k,M,B,C]=F.box;if(d<k-o||d>B+o||g<M-o||g>C+o)continue;let E=!1;for(let O of F.rings)for(let I=0,N=O.length-2;I+1<O.length;N=I,I+=2){let K=O[N],G=O[N+1],z=O[I],Y=O[I+1];Y>g!=G>g&&d<(K-z)*(g-Y)/(G-Y)+z&&(E=!E);let w=z-K,y=Y-G,b=w*w+y*y,x=b?Math.min(1,Math.max(0,((d-K)*w+(g-G)*y)/b)):0,T=Math.hypot(d-K-x*w,g-G-x*y);T<h&&(h=T,p=F.index)}if(E){m=F.index;break}}let A=m??p;A!==void 0&&t.set(A,Math.max(t.get(A)??0,u))}}return t}function Re(e){return Math.min(ue,Math.max(0,Math.round(e)))}function Jn(e){let n=Number(e);return Number.isFinite(n)&&n>0?n:void 0}function nr(e,n){let t=[],i;for(let o of e){let r=[];for(let a=0;a+1<o.length;a+=2){let l=o[a]*n,u=o[a+1]*n;r.length&&l===r.at(-2)&&u===r.at(-1)||r.push(l,u)}if(r.length>=6&&r[0]===r.at(-2)&&r[1]===r.at(-1)&&(r.length-=2),r.length<6)continue;let s=Math.sign(qe(r));s&&(i??=s,s===i||t.length===0?t.push([r]):t.at(-1).push(r))}return t}function qe(e){let n=0,t=e.length/2;for(let i=0;i<t;i+=1){let o=(i+1)%t;n+=e[i*2]*e[o*2+1]-e[o*2]*e[i*2+1]}return n}function tr(e){let n=0,t=0,i=e.length/2;for(let o=0;o<i;o+=1)n+=e[o*2],t+=e[o*2+1];return[n/i,t/i]}function xn(e,n){return e<=0||n<=0||e>=ue||n>=ue}function we(e,n,t,i){let o=ue;return e<=0&&t<=0||e>=o&&t>=o||n<=0&&i<=0||n>=o&&i>=o}function rr(e){let n=ue,t=[...e],i=[[0,0,o=>o>=0],[0,n,o=>o<=n],[1,0,o=>o>=0],[1,n,o=>o<=n]];for(let[o,r,s]of i){let a=[],l=t.length/2;for(let u=0;u<l;u+=1){let c=[t[u*2],t[u*2+1]],f=[t[(u+1)%l*2],t[(u+1)%l*2+1]],d=s(c[o]),g=s(f[o]);if(d&&a.push(c[0],c[1]),d!==g){let m=(r-c[o])/(f[o]-c[o]);a.push(c[0]+(f[0]-c[0])*m,c[1]+(f[1]-c[1])*m)}}if(t=a,t.length<6)return[]}return t}function gn(e){let n=Math.sin(e*12.9898+78.233)*43758.5453;return n-Math.floor(n)}function Qn(e){let n=(e.indices.length-e.wallsFrom)/6,t=new Uint8Array(n*16);for(let i=0;i<n;i+=1){let o=e.wallsFrom+i*6,r=e.indices[o]*pe,s=e.indices[o+1]*pe;t.set(e.vertices.subarray(r,r+4),i*16),t.set(e.vertices.subarray(s,s+4),i*16+4),t.set(e.vertices.subarray(r+4,r+8),i*16+8),t[i*16+12]=e.vertices[r+14],t[i*16+13]=e.vertices[r+27]}return t}function qn(e){let n=e.vertices.length/pe;return{mesh:{...e.groups?{groups:e.groups}:{},wallsFrom:e.wallsFrom,top:e.top,features:e.features,vertexCount:n,indexCount:e.indices.length},data:{vertices:e.vertices,indices:n<=65536?Uint16Array.from(e.indices):e.indices,contact:Qn(e)}}}function et({data:e}){return[e.vertices.buffer,e.indices.buffer,e.contact.buffer]}function Tn(e){let n=or(e);return n?[Math.min(1,Math.max(0,n[0]/255)),Math.min(1,Math.max(0,n[1]/255)),Math.min(1,Math.max(0,n[2]/255)),Math.min(1,Math.max(0,n[3]))]:null}function or(e){let n=e.trim().toLowerCase(),t=/^#([\da-f]{3,8})$/u.exec(n)?.[1];if(t&&[3,4,6,8].includes(t.length)){let A=(t.length<=4?[...t].map(F=>F+F).join(""):t).match(/../gu).map(F=>Number.parseInt(F,16));return[A[0],A[1],A[2],A.length>3?A[3]/255:1]}let i=/^(rgba?|hsla?)\(([^)]+)\)$/u.exec(n);if(!i)return null;let o=i[2].split(/[\s,/]+/u).filter(Boolean);if(o.length<3||o.length>4)return null;let r=o[3]===void 0?1:en(o[3],1);if(i[1].startsWith("rgb")){let[h,A,F]=o.slice(0,3).map(k=>en(k,255));return[h,A,F,r].some(k=>k===null)?null:[h,A,F,r]}let s=Number.parseFloat(o[0]),a=en(o[1],1),l=en(o[2],1);if(!Number.isFinite(s)||a===null||l===null||r===null)return null;let u=(1-Math.abs(2*l-1))*a,c=(s%360+360)%360/60,f=u*(1-Math.abs(c%2-1)),[d,g,m]=c<1?[u,f,0]:c<2?[f,u,0]:c<3?[0,u,f]:c<4?[0,f,u]:c<5?[f,0,u]:[u,0,f],p=l-u/2;return[(d+p)*255,(g+p)*255,(m+p)*255,r]}function en(e,n){let t=e.endsWith("%"),i=Number.parseFloat(e);return Number.isFinite(i)?t?i/100*n:i:null}var fe=8192,Mn=12,ir={primary:{spacing:30,kerb:10},secondary:{spacing:32,kerb:8.5},tertiary:{spacing:34,kerb:7},minor:{spacing:36,kerb:5.5},pedestrian:{spacing:26,kerb:3}},sr={motorway:{spacing:45,kerb:14},trunk:{spacing:40,kerb:12},service:{spacing:38,kerb:4},path:{spacing:28,kerb:1.5},track:{spacing:40,kerb:2.5}},ar={spacing:30,kerb:0},lr=12,ur=18,cr=9,fr={sodium:"#ffa64d",high_pressure_sodium:"#ffa64d",low_pressure_sodium:"#ff9326",led:"#fff1db",mercury:"#e3ecff",metal_halide:"#f4f4ff",fluorescent:"#eef9ff",electric:"#ffd59a",incandescent:"#ffd59a",halogen:"#ffe3b0",gas:"#ffd28a",gaslight:"#ffd28a"},dr={orange:"#ffa64d",yellow:"#ffd27a",warm_white:"#ffe2b8",white:"#fff4e6",cool_white:"#eef4ff",neutral_white:"#fff8f0",blue:"#b8d0ff",green:"#c4ffd0",red:"#ff9a8a"},mr=new Set(["wall","wall_mounted","wall_bracket"]),hr=new Set(["suspended","cable","catenary","wire"]);function ot(e,n){let t=n.estimate??"lit",i=new vn,o=new nn(ur),r=new nn(cr),s=e.streetFurniture;if(s){let m=fe/s.extent;for(let p of s.features){let h=p.properties;if(p.type!=="Point"||h.subclass!=="street_lamp")continue;let A=pr(h),F=yr(h),k=Math.max(1,Math.min(4,Number(h.light_count)||1)),M=Math.min(24,Math.max(5,A*1.7*(k>1?1.15:1)));for(let B of p.geometry)for(let C=0;C+1<B.length;C+=2){let E=B[C]*m,O=B[C+1]*m;if(!tt(E,O))continue;i.write(E,O,A,M,!0,F);let I=rt(E,O,n.tileMetres);o.add(...I),r.add(...I)}}}let a=i.count;if(t==="none")return i.done(a);let l=e.transportation,u=0;if(l){let m=n.tileMetres/l.extent;for(let p of l.features)if(!(p.type!=="LineString"||!nt(p.properties,"streets")))for(let h of p.geometry)u+=br(h,l.extent)*m}let c=u>0&&a/(u/1e3)>=lr,f=t==="streets"&&!c?"streets":"lit",d=(m,p,h,A)=>{let F=fe/m.extent,k=[];for(let N=0;N+1<p.length;N+=2)k.push(p[N]*F,p[N+1]*F);let M=n.tileMetres/fe,B=h.kerb*.45/M,C=Rn(k[0]*.013+k[1]*.029+A)*h.spacing,E=0,O=C,I=Rn(k[0]+k[1]*3.1)<.5?1:-1;for(let N=2;N<k.length;N+=2){let K=k[N-2],G=k[N-1],z=k[N],Y=k[N+1],w=Math.hypot(z-K,Y-G)*M;if(w!==0){for(;O<=E+w;){let y=(O-E)/w,b=-(Y-G)/(w/M)*I,x=(z-K)/(w/M)*I,T=K+(z-K)*y+b*B,S=G+(Y-G)*y+x*B;if(O+=h.kerb>0?h.spacing/2:h.spacing,h.kerb>0&&(I=-I),!tt(T,S))continue;let v=rt(T,S,n.tileMetres);o.near(...v)||r.near(...v)||(r.add(...v),i.write(T,S,7,15,!1))}E+=w}}};if(l)for(let m of l.features){if(m.type!=="LineString")continue;let p=nt(m.properties,f);if(p)for(let h of m.geometry)d(l,h,p,1)}let g=e.sidewalks;if(g){for(let m of g.features)if(!(m.type==="Point"||!kn(m.properties.lit)))for(let p of m.geometry)d(g,p,ar,2)}return i.done(a)}function nt(e,n){if(e.brunnel==="tunnel"||e.covered===!0)return;let t=e.lit;if(t===!1||t==="no")return;let i=String(e.class??""),o=String(e.subclass??""),r=i==="path"&&(o==="pedestrian"||o==="living_street")?"pedestrian":i,s=ir[r];if(s&&(n==="streets"||kn(t)))return s;let a=sr[r];if(a&&kn(t))return a}function kn(e){return e===!0||e==="yes"||e===1||e==="1"||e==="true"}function pr(e){let n=Number(e.height);if(Number.isFinite(n)&&n>=.5&&n<=40)return n;let t=String(e.lamp_mount??"").toLowerCase();return mr.has(t)?4.5:hr.has(t)?7.5:7}function yr(e){let n=e.light_colour??e["light:colour"];if(typeof n=="string"&&n){let r=n.toLowerCase().replaceAll(" ","_"),s=Tn(dr[r]??n);if(s)return[s[0],s[1],s[2]]}let t=String(e.lamp_type??"").toLowerCase().replaceAll("-","_").replaceAll(" ","_"),i=fr[t];if(!i)return;let o=Tn(i);return[o[0],o[1],o[2]]}function tt(e,n){return e>=0&&n>=0&&e<fe&&n<fe}function rt(e,n,t){return[e/fe*t,n/fe*t]}function br(e,n){let t=0;for(let i=2;i+1<e.length;i+=2){let o=(e[i-2]+e[i])/2,r=(e[i-1]+e[i+1])/2;o<0||r<0||o>=n||r>=n||(t+=Math.hypot(e[i]-e[i-2],e[i+1]-e[i-1]))}return t}var nn=class{constructor(n){this.size=n}size;#e=new Map;add(n,t){let i=`${Math.floor(n/this.size)}/${Math.floor(t/this.size)}`,o=this.#e.get(i);o?o.push([n,t]):this.#e.set(i,[[n,t]])}near(n,t){let i=Math.floor(n/this.size),o=Math.floor(t/this.size);for(let r=-1;r<=1;r+=1)for(let s=-1;s<=1;s+=1)for(let[a,l]of this.#e.get(`${i+r}/${o+s}`)??[])if(Math.hypot(a-n,l-t)<this.size)return!0;return!1}},vn=class{#e=new ArrayBuffer(256*Mn);#n=new DataView(this.#e);count=0;write(n,t,i,o,r,s){let a=Mn;if((this.count+1)*a>this.#e.byteLength){let c=new ArrayBuffer(this.#e.byteLength*2);new Uint8Array(c).set(new Uint8Array(this.#e)),this.#e=c,this.#n=new DataView(c)}let l=this.#n,u=this.count*a;l.setUint16(u,Math.min(fe,Math.max(0,Math.round(n))),!0),l.setUint16(u+2,Math.min(fe,Math.max(0,Math.round(t))),!0),l.setUint16(u+4,Math.min(65535,Math.round(i*10)),!0),l.setUint8(u+6,Math.min(255,Math.round(o))),l.setUint8(u+7,(r?1:0)|(s?2:0)),l.setUint8(u+8,Math.round((s?.[0]??0)*255)),l.setUint8(u+9,Math.round((s?.[1]??0)*255)),l.setUint8(u+10,Math.round((s?.[2]??0)*255)),l.setUint8(u+11,Math.round(Rn(n*.0731+t*.19137)*255)),this.count+=1}done(n){if(this.count!==0)return{lamps:new Uint8Array(this.#e.slice(0,this.count*Mn)),mapped:n,estimated:this.count-n}}};function Rn(e){let n=Math.sin(e*12.9898+78.233)*43758.5453;return n-Math.floor(n)}var eo=Object.freeze({source:"tileflow",layers:Object.freeze({streetFurniture:"street_furniture",transportation:"transportation",sidewalk:"sidewalk"}),estimate:"lit",color:"#ffcf8f",intensity:1});var Me=["round","open","tall","conifer","columnar","palm"];var lt=Object.freeze({height:"height",diameterCrown:"diameter_crown",circumference:"circumference",genus:"genus",species:"species",leafType:"leaf_type",taxon:"taxon",canopyRank:"rank",canopyClearance:"clearance_px_z15",canopyCover:"cover_pct",canopyHeight:"height_m",subclass:"subclass",hedge:"hedge"}),_n=Object.freeze({round:1,open:1,tall:1}),tn=16,q=8192,rn=16,it={round:{height:[6,12],crown:[.66,.9]},open:{height:[5,10],crown:[.8,1.05]},tall:{height:[9,17],crown:[.48,.68]},conifer:{height:[7,16],crown:[.46,.66]},columnar:{height:[9,18],crown:[.22,.36]},palm:{height:[12,20],crown:[.42,.55]}};var on=[2,45],An=[1,25],gr=1.2,wr=1.5,xr=1.3,Tr=1,Mr=[.3,10],kr=new Set(["acoelorrhaphe","adonidia","archontophoenix","areca","arecastrum","bismarckia","brahea","butia","caryota","chamaedorea","chamaerops","coccothrinax","cocos","dypsis","elaeis","howea","jubaea","licuala","livistona","phoenix","pritchardia","ptychosperma","rhapis","roystonea","sabal","serenoa","syagrus","thrinax","trachycarpus","veitchia","washingtonia","wodyetia"]),vr=new Set(["cupressus","calocedrus","thuja"]),Rr=new Set(["abies","araucaria","cedrus","cryptomeria","juniperus","larix","metasequoia","picea","pinus","pseudotsuga","sequoia","sequoiadendron","taxodium","taxus","tsuga"]),st=Object.freeze({...Object.fromEntries(["acacia","albizia","arbutus","bursera","ceratonia","delonix","ficus","gleditsia","jacaranda","olea","quercus","robinia","schinus","sophora","styphnolobium","tamarix"].map(e=>[e,"open"])),...Object.fromEntries(["betula","celtis","fraxinus","ginkgo","liquidambar","liriodendron","magnolia","platanus","populus","pyrus","ulmus","zelkova"].map(e=>[e,"tall"])),...Object.fromEntries(["acer","aesculus","carpinus","castanea","catalpa","cercis","citrus","fagus","lagerstroemia","malus","melia","morus","prunus","sorbus","tilia"].map(e=>[e,"round"]))});function at(e,n={}){let t={...lt,...n},i=String(e[t.leafType]??"").toLowerCase(),o=String(e[t.taxon]??"").toLowerCase(),r=Fn(e[t.genus])??Fn(e[t.species])??(o.endsWith("aceae")?void 0:Fn(e[t.taxon]));if(i.includes("palm")||o.includes("arecaceae")||r&&kr.has(r))return"palm";if(o.includes("cupressaceae")&&!r||o.includes("pinaceae"))return"conifer";if(r&&vr.has(r))return"columnar";if(i.includes("needle")||r&&Rr.has(r))return"conifer";if(r&&st[r])return st[r];if(i.includes("broad"))return"broadleaf"}function Fn(e){return typeof e!="string"?void 0:e.trim().split(/\s+/u)[0]?.toLowerCase()||void 0}function ut(e,n){let t={...lt,...n.fields},i=n.unknownForms??_n,o=n.regions??[],{tile:r,tileMetres:s}=n,a=s/2**(15-r.z)/512,l=[],u=0,c=0,f=(w,y)=>{if(o.length===0)return i;let[b,x]=Fr(r,w/q,y/q);for(let T of o){let[S,v,L,U]=T.bounds;if(x>=v&&x<=U&&(S<=L?b>=S&&b<=L:b>=S||b<=L))return T.unknownForms}return i},d=(w,y,b,x)=>{let T=it[w],S=Sn(T.crown,ee(y,2)),v=b,L=x;return v===void 0&&L===void 0&&(v=Sn(T.height,ee(y,1))),v===void 0&&(v=L/S),L===void 0&&(L=v*S),{height:Te(v,on),crown:Te(L,An)}},g=(w,y,b,x,T,S)=>{let v=je(r.x*q+w,r.y*q+y,7),L=b===void 0?sn(f(w,y),ee(v,3)):b==="broadleaf"?sn(Ar(f(w,y)),ee(v,3)):b,U=x===void 0&&T===void 0&&S!==void 0?d(L,v,Sn(it[L].height,Te(S/3,[0,1]))):d(L,v,x,T);l.push({x:w,y,...U,turn:ee(v,4),shade:ee(v,5),form:Me.indexOf(L),rank:Math.floor(ee(v,6)*rn),seed:ee(v,7),canopy:0}),u+=1},m=e.trees;if(m){let w=q/m.extent;for(let y of m.features){if(y.type!=="Point")continue;let b=y.properties,x=at(b,t),T=xe(b[t.height],on),S=xe(b[t.diameterCrown],An),v=xe(b[t.circumference],[.1,12]);for(let L of y.geometry)for(let U=0;U+1<L.length;U+=2){let V=L[U],R=L[U+1];V<0||R<0||V>=m.extent||R>=m.extent||g(V*w,R*w,x,T,S,v)}}}let p=e.trees;if(p){let w=q/p.extent,y=(n.rowSpacing??8)/(s/p.extent);for(let b of p.features){if(b.type!=="LineString")continue;let x=b.properties,T=at(x,t),S=xe(x[t.height],on),v=xe(x[t.diameterCrown],An);for(let L of b.geometry){let U=y/2,V=0;for(let R=0;R+3<L.length;R+=2){let[_,j,P,Z]=[L[R],L[R+1],L[R+2],L[R+3]],D=Math.hypot(P-_,Z-j);for(;U<=V+D;){let H=D>0?(U-V)/D:0,W=_+(P-_)*H,se=j+(Z-j)*H;W>=0&&se>=0&&W<p.extent&&se<p.extent&&g(W*w,se*w,T,S,v,void 0),U+=y}V+=D}}}}let h=e.canopy;if(h){let w=q/h.extent;for(let y of h.features){if(y.type!=="Point")continue;let b=y.properties,x=Te(Math.floor(Number(b[t.canopyRank]??8)),[0,rn-1]),T=Math.max(4,Number(b[t.canopyClearance]??8))*a,S=Number(b[t.canopyCover]??30),v=S>=60?3:S>=30?2:1;for(let L of y.geometry)for(let U=0;U+1<L.length;U+=2){let V=L[U],R=L[U+1];if(V<0||R<0||V>=h.extent||R>=h.extent)continue;let _=V*w,j=R*w,P=je(r.x*q+_,r.y*q+j,11),Z=f(_,j),D=xe(b[t.canopyHeight],on),H=Te(Math.min(T*(v===1?1.3:.95),11)*(.85+ee(P,1)*.25),[3,12]),W=Math.max(0,Math.min(H*.5,T-H/2)),se=ee(P,2)*Math.PI*2;for(let ae=0;ae<v;ae+=1){let $=je(P,ae,13),J=se+ae*Math.PI*2/v,re=v===1?W*.3*ee($,1):W,ne=_+Math.cos(J)*re/(s/q),te=j+Math.sin(J)*re/(s/q);if(ne<0||te<0||ne>=q||te>=q)continue;let le=sn(Z,ee($,3)),de=H*(.85+ee($,2)*.3),me=D===void 0?void 0:D*(.85+ee($,6)*.25),ce=d(le,$,me,le==="palm"?void 0:de);l.push({x:ne,y:te,...ce,turn:ee($,4),shade:ee($,5),form:Me.indexOf(le),rank:x,seed:ee($,7),canopy:1}),c+=1}}}}let A=[],F=e.hedges;if(F){let w=q/F.extent,y=gr/(s/F.extent);for(let b of F.features){if(b.type==="Point"||b.properties[t.subclass]!=="hedge")continue;let x=b.properties[t.hedge]==="hedge_bank"?Tr:0,T=(xe(b.properties[t.height],Mr)??xr)+x;for(let S of b.geometry){let v=y/2,L=0;for(let U=0;U+3<S.length;U+=2){let[V,R,_,j]=[S[U],S[U+1],S[U+2],S[U+3]],P=Math.hypot(_-V,j-R),Z=(Math.atan2(R-j,_-V)/(Math.PI*2)+1)%1;for(;v<=L+P;){let D=P>0?(v-L)/P:0,H=V+(_-V)*D,W=R+(j-R)*D;if(H>=0&&W>=0&&H<F.extent&&W<F.extent){let se=je(r.x*q+H*w,r.y*q+W*w,17);A.push({x:H*w,y:W*w,height:T,turn:Z,shade:ee(se,5),seed:ee(se,7)})}v+=y}L+=P}}}}let k=w=>Te(Math.floor(n.variants?.[Me[w]]??1),[1,4]),M=w=>Math.min(k(w.form)-1,Math.floor(ee(Math.floor(w.seed*256),9)*k(w.form))),B=l.map(M),C=l.map((w,y)=>y);C.sort((w,y)=>l[w].form-l[y].form||B[w]-B[y]||l[w].rank-l[y].rank);let E=new Uint8Array(l.length*tn),O=new DataView(E.buffer),I=0,N=()=>({start:0,count:0,upTo:new Array(rn).fill(0)}),K=Object.fromEntries(Me.map((w,y)=>[w,{...N(),variants:Array.from({length:k(y)},N)}])),G=(w,y,b)=>{w.count===0&&(w.start=y),w.count+=1;for(let x=b;x<rn;x+=1)w.upTo[x]+=1};C.forEach((w,y)=>{let b=l[w],x=y*tn;O.setUint16(x,Math.min(q-1,Math.round(b.x)),!0),O.setUint16(x+2,Math.min(q-1,Math.round(b.y)),!0),O.setUint16(x+4,Math.round(b.height*10),!0),O.setUint16(x+6,Math.round(b.crown*10),!0),E[x+8]=Math.floor(b.turn*256)&255,E[x+9]=Math.floor(b.shade*256)&255,E[x+10]=b.form,E[x+11]=b.rank,E[x+12]=Math.floor(b.seed*256)&255,E[x+13]=b.canopy,E[x+14]=B[w],I=Math.max(I,b.height);let T=K[Me[b.form]];G(T,y,b.rank),G(T.variants[B[w]],y,b.rank)});let z=new Uint8Array(A.length*tn),Y=new DataView(z.buffer);return A.forEach((w,y)=>{let b=y*tn;Y.setUint16(b,Math.min(q-1,Math.round(w.x)),!0),Y.setUint16(b+2,Math.min(q-1,Math.round(w.y)),!0),Y.setUint16(b+4,Math.round(w.height*10),!0),Y.setUint16(b+6,Math.round(wr*10),!0),z[b+8]=Math.round(w.turn*256)&255,z[b+9]=Math.floor(w.shade*256)&255,z[b+12]=Math.floor(w.seed*256)&255,I=Math.max(I,w.height)}),{trees:E,forms:K,hedges:z,tallest:I,mapped:u,canopy:c}}function Ar(e){let n={round:e.round??0,open:e.open??0,tall:e.tall??0};return n.round+n.open+n.tall>0?n:_n}function sn(e,n){let t=0;for(let o of Me)t+=Math.max(0,e[o]??0);if(t<=0)return sn(_n,n);let i=n*t;for(let o of Me){let r=Math.max(0,e[o]??0);if(i<r)return o;i-=r}return"round"}function xe(e,n){let t=typeof e=="number"?e:typeof e=="string"?Number.parseFloat(e):NaN;return Number.isFinite(t)&&t>0?Te(t,n):void 0}function Te(e,[n,t]){return Math.min(t,Math.max(n,e))}function Sn([e,n],t){return e+(n-e)*t}function Fr(e,n,t){let i=2**e.z,o=(e.x+n)/i*360-180,r=Math.atan(Math.sinh(Math.PI*(1-2*(e.y+t)/i)))*180/Math.PI;return[o,r]}function je(e,n,t){let i=(Math.imul(Math.floor(e)|0,668265261)^Math.imul(Math.floor(n)|0,374761393)^Math.imul(t,2654435761))>>>0;return i=Math.imul(i^i>>>15,2246822507)>>>0,i=Math.imul(i^i>>>13,3266489909)>>>0,(i^i>>>16)>>>0}function ee(e,n){return je(e,n,20973)/4294967296}var Sr=[void 0,"Point","LineString","Polygon"];function ct(e,n={}){let t=n.layers?new Set(n.layers):void 0,i={},o=new Bn(e,0,e.length);for(;o.more();){let[r,s]=o.tag();if(r!==3){o.skip(s);continue}let a=o.message(),l=_r(a,t);l&&!(l.name in i)&&(i[l.name]=l.layer)}return i}function _r(e,n){let t,i=4096,o=[],r=[],s=[];for(;e.more();){let[a,l]=e.tag();if(a===1?t=e.string():a===2?s.push(e.message()):a===3?o.push(e.string()):a===4?r.push(Ur(e.message())):a===5?i=e.varint():e.skip(l),t!==void 0&&n&&!n.has(t))return}if(!(t===void 0||n&&!n.has(t)))return{name:t,layer:Object.freeze({extent:i,features:Object.freeze(s.flatMap(a=>{let l=Lr(a,o,r);return l?[l]:[]}))})}}function Lr(e,n,t){let i,o,r={},s=[];for(;e.more();){let[a,l]=e.tag();if(a===1)i=e.varint();else if(a===2){let u=e.packed();for(let c=0;c+1<u.length;c+=2){let f=n[u[c]],d=t[u[c+1]];f!==void 0&&d!==void 0&&(r[f]=d)}}else a===3?o=Sr[e.varint()]:a===4?s=e.packed():e.skip(l)}if(o)return Object.freeze({...i===void 0?{}:{id:i},type:o,properties:Object.freeze(r),geometry:Object.freeze(Br(s,o))})}function Br(e,n){let t=[],i,o=0,r=0,s=0;for(;s<e.length;){let a=e[s++],l=a&7,u=a>>>3;if(l===7){i&&i.length>=2&&i.push(i[0],i[1]);continue}if(l!==1&&l!==2)throw new Error(`Unknown geometry command ${l} in vector tile.`);if(s+u*2>e.length)throw new Error("Truncated vector tile geometry.");for(let c=0;c<u;c+=1){if(o+=Ln(e[s++]),r+=Ln(e[s++]),l===1&&(n!=="Point"||c===0)&&t.push(i=[]),!i)throw new Error("Vector tile geometry draws a line before moving to its start.");i.push(o,r)}}return t}var Ln=e=>e%2===1?-(e+1)/2:e/2;function Ur(e){let n="";for(;e.more();){let[t,i]=e.tag();t===1?n=e.string():t===2?n=e.float():t===3?n=e.double():t===4?n=e.varint(!0):t===5?n=e.varint():t===6?n=Ln(e.varint()):t===7?n=e.varint()!==0:e.skip(i)}return n}var Cr=new TextDecoder,Bn=class e{constructor(n,t,i){this.bytes=n;this.end=i;this.position=t,this.view=new DataView(n.buffer,n.byteOffset,n.byteLength)}bytes;end;position;view;more(){return this.position<this.end}tag(){let n=this.varint();return[Math.floor(n/8),n&7]}varint(n=!1){let t=0,i=0;for(let o=0;o<10;o+=1){if(this.position>=this.end)throw new Error("Truncated vector tile.");let r=this.bytes[this.position++];if(o<4?t|=(r&127)<<o*7:o===4?(t|=(r&15)<<28,i=(r&112)>>4):o<9?i|=(r&127)<<(o-5)*7+3:i|=(r&1)<<31,r<128)return n?i*4294967296+(t>>>0):(i>>>0)*4294967296+(t>>>0)}throw new Error("Invalid varint in vector tile.")}span(){let n=this.varint(),t=this.position;if(t+n>this.end)throw new Error("Truncated vector tile.");return this.position=t+n,[t,t+n]}message(){let[n,t]=this.span();return new e(this.bytes,n,t)}string(){let[n,t]=this.span();return Cr.decode(this.bytes.subarray(n,t))}packed(){let[n,t]=this.span(),i=new e(this.bytes,n,t),o=[];for(;i.more();)o.push(i.varint());return o}float(){if(this.position+4>this.end)throw new Error("Truncated vector tile.");let n=this.view.getFloat32(this.position,!0);return this.position+=4,n}double(){if(this.position+8>this.end)throw new Error("Truncated vector tile.");let n=this.view.getFloat64(this.position,!0);return this.position+=8,n}skip(n){if(n===0)this.varint();else if(n===1)this.position+=8;else if(n===2)this.span();else if(n===5)this.position+=4;else throw new Error(`Unsupported protocol buffer wire type ${n} in vector tile.`);if(this.position>this.end)throw new Error("Truncated vector tile.")}};function ft(e){return Object.values(e.layers).filter(n=>typeof n=="string")}function dt(e,n){if(e.kind==="buildings"){let r=n(e.layers.building);if(!r)return;let s=n(e.layers.places),a=Kn(r,{...e.options,...s?{places:s}:{}});return a&&qn(a)}if(e.kind==="trees"){let r=n(e.layers.tree),s=n(e.layers.canopy),a=n(e.layers.hedge);if(!r&&!s&&!a)return;let l=ut({...r?{trees:r}:{},...s?{canopy:s}:{},...a?{hedges:a}:{}},e.options);return l.trees.length>0||l.hedges.length>0?l:void 0}let t=n(e.layers.streetFurniture),i=n(e.layers.transportation),o=n(e.layers.sidewalk);return ot({...t?{streetFurniture:t}:{},...i?{transportation:i}:{},...o?{sidewalks:o}:{}},e.options)}function mt(e,n){if(!n)return[];if(e==="buildings")return et(n);if(e==="trees"){let{trees:t,hedges:i}=n;return[t.buffer,i.buffer].filter((o,r,s)=>s.indexOf(o)===r)}return[n.lamps.buffer]}function ht(e=16){let n=new Map;return{read(t,i,o){let r=n.get(t);for(n.delete(t),r??={bytes:i,layers:new Map},n.set(t,r);n.size>e;)n.delete(n.keys().next().value);let s=o.filter(l=>!r.layers.has(l));if(s.length>0){let l=ct(r.bytes,{layers:s});for(let u of s)r.layers.set(u,l[u])}let a=r.layers;return l=>l===void 0?void 0:a.get(l)}}}var Un=globalThis,Or=ht();Un.onmessage=({data:{url:e,bytes:n,task:t}})=>{try{let i=dt(t,Or.read(e,n,ft(t)));Un.postMessage({result:i},mt(t.kind,i))}catch(i){Un.postMessage({error:String(i)})}};})();
|
|
@@ -65,12 +65,15 @@ state on the map.
|
|
|
65
65
|
## Requests and cache
|
|
66
66
|
|
|
67
67
|
- **One request per tile.** However many reads wait for a tile, it is requested once. A read whose
|
|
68
|
-
signal aborts stops waiting
|
|
68
|
+
signal aborts stops waiting. Once every read waiting for a request has stopped, a request still
|
|
69
|
+
waiting its turn is dropped; one already sent finishes for the cache, or stops when `cacheSize`
|
|
70
|
+
is 0.
|
|
69
71
|
- **Cache.** The reader keeps up to `cacheSize` decoded tiles (128 by default), the most recently
|
|
70
72
|
read first. It remembers a tile the source does not have (404 or 204) as absent, and does not
|
|
71
73
|
remember errors. `clear()` forgets everything.
|
|
72
74
|
- **Concurrency.** At most `concurrency` requests (6 by default) run at once; the rest wait their
|
|
73
|
-
turn.
|
|
75
|
+
turn, lowest `priority` first (a read option, 0 by default). `readArea` reads the tiles nearest
|
|
76
|
+
its centre first.
|
|
74
77
|
- **Requests.** `transformRequest` rewrites each request as a MapLibre map's option does. Pass the
|
|
75
78
|
map's own so tile reads follow the same rules. The browser's HTTP cache applies as for any
|
|
76
79
|
request.
|
package/docs/authoring-model.md
CHANGED
|
@@ -259,3 +259,11 @@ data; passes placed beside a moved target follow it. Operations sharing a placem
|
|
|
259
259
|
declaration order, the first lowest. For symbols, a higher layer is also placed first in MapLibre
|
|
260
260
|
collisions, so placement decides both drawing and precedence. An owner whose layers are all
|
|
261
261
|
render passes still satisfies other operations' `requirements`.
|
|
262
|
+
|
|
263
|
+
A target exists only in the mode that draws it: buildings draw `buildings.flat.fill` and
|
|
264
|
+
`buildings.flat.outline` in mode `'flat'`, and `buildings.extrusion` in mode `'3d'`. `refine()`
|
|
265
|
+
keeps the inherited render stack, so refining an inherited module's `mode` fails when a pass is
|
|
266
|
+
placed beside, or a refinement targets, the other mode's target; the error names both modes.
|
|
267
|
+
Streets' shadows and its extrusion pass stand beside its flat buildings. To draw native extrusions
|
|
268
|
+
in a map that extends Streets, remove that stack with `refine({mode: '3d', renderStack: reset()})`,
|
|
269
|
+
or declare `buildings({mode: '3d'})` to replace the module.
|
package/docs/buildings-3d.md
CHANGED
|
@@ -8,16 +8,73 @@ 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
|
|
|
14
17
|
It is a custom WebGL2 layer for MapLibre GL JS, drawn above the map's buildings and below its
|
|
15
18
|
labels. It needs WebGL2 and the Mercator projection, which MapLibre's globe turns into from about
|
|
16
|
-
zoom 12. On every map
|
|
17
|
-
|
|
18
|
-
Their
|
|
19
|
+
zoom 12. On every map the city comes in as the camera zooms in: placed models and landmarks first,
|
|
20
|
+
fading in and rising from the ground between zooms 14.2 and 14.6, then the buildings between 15 and
|
|
21
|
+
15.3. Their shadows and contact shading follow their height. Zooming back out lowers and fades them
|
|
22
|
+
again. With reduced motion (`prefers-reduced-motion`) they keep their height and only fade.
|
|
19
23
|
`hide()` keeps them hidden at every zoom until `show()` is called.
|
|
20
24
|
|
|
25
|
+
A map sets these bands with the buildings module's `threeDimensional.entrance`, each a
|
|
26
|
+
`[from, to]` pair of zooms; a band left out keeps its default:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
buildings({threeDimensional: {entrance: {buildings: [15.5, 16], models: [14.5, 15]}}});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The renderer reads its tiles from a zoom below its `minzoom`, so a band that starts earlier than
|
|
33
|
+
that comes in from there.
|
|
34
|
+
|
|
35
|
+
## The map's 3D scene
|
|
36
|
+
|
|
37
|
+
React, Vue and Svelte `Map` components show the 3D scene of every map whose style declares one, by
|
|
38
|
+
default: buildings with Tileflow landmarks and the map's Team building models, 3D trees, and street
|
|
39
|
+
lights where the map enables them. They load the 3D renderers from `@tileflow/core/3d` only when
|
|
40
|
+
the style declares a scene, so a 2D map does not download them. Set `viewMode="2d"` (Vue:
|
|
41
|
+
`view-mode="2d"`) to opt out: no 3D buildings or street lights are mounted, and vegetation follows
|
|
42
|
+
its [display policy](trees-3d.md#visibility-and-representation) for the 2D view. Changing it back to
|
|
43
|
+
`3d` shows the scene on the same map. React Native, image mode, Capture and Static Maps do not draw
|
|
44
|
+
the scene.
|
|
45
|
+
|
|
46
|
+
Applications that build their own MapLibre map mount the same scene with `createTileflowMap3D`.
|
|
47
|
+
It mounts each renderer the style declares, follows theme and style changes, and releases a
|
|
48
|
+
renderer when the style no longer declares it:
|
|
49
|
+
|
|
50
|
+
<!-- docs:check -->
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import {
|
|
54
|
+
createTileflowMap3D,
|
|
55
|
+
type TileflowMap3DMap,
|
|
56
|
+
type TileflowThemeController,
|
|
57
|
+
} from '@tileflow/core/browser';
|
|
58
|
+
|
|
59
|
+
export function showTheScene(map: TileflowMap3DMap, controller: TileflowThemeController) {
|
|
60
|
+
const scene = createTileflowMap3D({map, controller});
|
|
61
|
+
// Shown on creation; hide() and show() switch the whole scene, dispose() removes it.
|
|
62
|
+
return scene;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`hide()` takes the buildings down and releases the street lights; `show()` brings them back. Both
|
|
67
|
+
take the same transition as the buildings' `show()` and `hide()`. `setVegetation(false)` hides
|
|
68
|
+
every tree and `setVegetation(undefined)` restores the map's default. To load the renderers only
|
|
69
|
+
for maps that need them, call `attachTileflowMap3D({map, controller, load: () =>
|
|
70
|
+
import('@tileflow/core/3d')})` from `@tileflow/core/browser`, as the framework components do; its
|
|
71
|
+
`setViewMode()` and `setVegetation()` apply once the scene mounts.
|
|
72
|
+
|
|
73
|
+
A MapLibre application that loads a compiled `style.json` directly gets no 3D scene: buildings,
|
|
74
|
+
landmarks, models, trees and lights are custom WebGL layers added only by this runtime. Where a map
|
|
75
|
+
draws a plain `fill-extrusion` building pass, the style ships it hidden and grouped under
|
|
76
|
+
`metadata['tileflow:3d-toggle'] = 'building'`; showing those layers gives extruded footprints only.
|
|
77
|
+
|
|
21
78
|
## Show the buildings
|
|
22
79
|
|
|
23
80
|
This example assumes an existing MapLibre map showing a Tileflow map whose vector source is called
|
|
@@ -146,11 +203,17 @@ or the renderer is removed; it resizes with the drawing buffer.
|
|
|
146
203
|
Maps on Tileflow World show Tileflow's landmarks, such as the Eiffel Tower, as 3D models in place of
|
|
147
204
|
the buildings under them. They come from a landmarks tileset kept apart from the map's data: each of
|
|
148
205
|
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
|
|
150
|
-
|
|
151
|
-
near it,
|
|
152
|
-
|
|
153
|
-
|
|
206
|
+
region's index names its tiles by their hash, with where each landmark stands and its footprint.
|
|
207
|
+
The renderer reads the tileset's TileJSON, then only the indexes of the regions round the camera
|
|
208
|
+
and the landmark tiles near it, from a zoom before the buildings show, so a landmark in Paris costs
|
|
209
|
+
nothing in Madrid. New and improved landmarks reach every map without a new build of the map.
|
|
210
|
+
|
|
211
|
+
The buildings under a landmark are never raised and then taken away. A tile of buildings is built
|
|
212
|
+
once the indexes round it have arrived, waiting at most a second and a half; while a landmark's
|
|
213
|
+
tile is still on its way, the footprint its index names keeps the buildings under it flat, and the
|
|
214
|
+
landmark fades in when it arrives. A landmark tile that fails, or has not arrived three seconds
|
|
215
|
+
after the buildings round it show, lets those buildings rise until it does. A tileset that cannot be read is tried again
|
|
216
|
+
after five minutes, and the buildings stay drawn meanwhile.
|
|
154
217
|
|
|
155
218
|
A map switches them off, or names the TileJSON of another landmarks tileset:
|
|
156
219
|
|
|
@@ -185,7 +248,7 @@ longer raised while it stands. Put glTF binaries (`.glb`) in a directory, declar
|
|
|
185
248
|
<!-- docs:check -->
|
|
186
249
|
|
|
187
250
|
```ts
|
|
188
|
-
import {defineMap, refine} from '@tileflow/core';
|
|
251
|
+
import {defineMap, fixed, refine} from '@tileflow/core';
|
|
189
252
|
import {streets, streetsModels} from '@tileflow/maps';
|
|
190
253
|
|
|
191
254
|
export default defineMap({
|
|
@@ -201,7 +264,10 @@ export default defineMap({
|
|
|
201
264
|
buildings: refine({
|
|
202
265
|
threeDimensional: {
|
|
203
266
|
models: [{model: 'eiffel-tower', at: [2.294481, 48.858258], heading: 0}],
|
|
204
|
-
modelColors: {
|
|
267
|
+
modelColors: {
|
|
268
|
+
surface: fixed('#7a5c45', {reason: "The tower's bronze paint"}),
|
|
269
|
+
accent: fixed('#5b4332', {reason: "The tower's bronze paint, in shadow"}),
|
|
270
|
+
},
|
|
205
271
|
},
|
|
206
272
|
}),
|
|
207
273
|
},
|
|
@@ -219,8 +285,14 @@ How to prepare a model:
|
|
|
219
285
|
`roof` (an exporter's `.001` suffix is ignored); a model whose materials name a tree's parts is a
|
|
220
286
|
[tree model](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/trees-3d.md)
|
|
221
287
|
instead. Each part takes its colour from `modelColors`, or from the buildings' colour when the
|
|
222
|
-
design gives it none, so the model follows the theme from day to night
|
|
288
|
+
design gives it none, so the model follows the theme from day to night. Like every visual
|
|
289
|
+
colour, a `modelColors` entry is a theme token such as `token.color('buildings.modelRoof')`, or
|
|
290
|
+
a literal wrapped in `fixed(value, {reason})`; vertex colours lighter or
|
|
223
291
|
darker than the rest of their part shade it within that colour.
|
|
292
|
+
- **Architectural roles control lighting.** A material may set
|
|
293
|
+
`extras.tileflow.lightingRole` to `wall`, `door`, `roof`, `window`, `lamp`, `logo` or
|
|
294
|
+
`unclassified`. This preserves its palette slot. Without a role, `surface`, `secondary` and
|
|
295
|
+
`accent` are walls, `glass` is a window, and `roof` is a roof.
|
|
224
296
|
- **Metres round an origin on the ground.** The origin is where the model stands, with the model
|
|
225
297
|
above it and its own north along +y once read Z up; nothing may lie more than half a metre below
|
|
226
298
|
it.
|
|
@@ -237,12 +309,49 @@ The build reads each model, reports a problem with the file and what to change,
|
|
|
237
309
|
footprint and writes it beside the style as a prepared model file: a glTF binary whose meshes are
|
|
238
310
|
quantised (`KHR_mesh_quantization`) and compressed by meshoptimizer (`EXT_meshopt_compression`),
|
|
239
311
|
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
|
|
241
|
-
|
|
242
|
-
|
|
312
|
+
the camera, whether or not those tiles hold buildings, keeps the nearest dozen, and builds the tiles
|
|
313
|
+
under it again without the buildings that
|
|
314
|
+
reach more than half a metre into its footprint; a neighbour that only touches it stays. A tile of
|
|
315
|
+
buildings near a model waits for it, at most a second and a half, so its buildings are not raised
|
|
316
|
+
and then taken away, and the model fades in when it comes to stand. A model that cannot be
|
|
317
|
+
downloaded leaves those buildings standing. Reading a prepared model file loads
|
|
243
318
|
meshoptimizer's WebAssembly decoder, so a page with a Content Security Policy allows
|
|
244
319
|
`'wasm-unsafe-eval'` in its `script-src`.
|
|
245
320
|
|
|
321
|
+
Preparation also computes local geometric ambient visibility, independently of the model's
|
|
322
|
+
colour. `modelOcclusion` sets its strength from zero to one (default `0.75`); `occlusion: false`
|
|
323
|
+
disables it. Occluded cavities receive less ambient light while direct sun and dynamic shadows
|
|
324
|
+
remain independent. Existing prepared files with fully exposed vertices retain their appearance.
|
|
325
|
+
Reprepare their original GLBs to gain geometric occlusion. See
|
|
326
|
+
[model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
|
|
327
|
+
for authoring, sampling and costs.
|
|
328
|
+
|
|
329
|
+
## Team 3D models
|
|
330
|
+
|
|
331
|
+
On Tileflow Hosted, a map's building models are its Team's 3D models: each is published once, under
|
|
332
|
+
its name, and every map of the Team that places a model of that name draws it. Locally, the map
|
|
333
|
+
draws its own `.glb` files, so the map is previewed and developed without an account.
|
|
334
|
+
|
|
335
|
+
```sh
|
|
336
|
+
npx tileflow models publish ./models/eiffel-tower.glb --team @acme
|
|
337
|
+
npx tileflow models status eiffel-tower --team @acme
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
`tileflow models publish` prepares each building model as a build does and publishes it as the
|
|
341
|
+
model's new current version; with no path, it publishes the building models of every map in the
|
|
342
|
+
project's config. Publishing the bytes of the current version again changes nothing. A hosted map
|
|
343
|
+
follows each model's current version: a new version, or `tileflow models restore`, which makes the
|
|
344
|
+
previous version current again, changes every map that places it the next time the map loads,
|
|
345
|
+
without deploying the maps again. A model keeps its current and previous versions.
|
|
346
|
+
|
|
347
|
+
`tileflow deploy` writes each placement of a building model as the placeholder
|
|
348
|
+
`tileflow://team-models/<name>` (`tileflowTeamModelUrl(name)`), which the deploy replaces with the
|
|
349
|
+
model's address in the map's Team; a model the Team has not published fails the deploy. The deploy
|
|
350
|
+
also reports a model whose published version was prepared from other files than the map's, so an
|
|
351
|
+
outdated model is easy to spot. A model's file, and what Tileflow shows about it, can be read
|
|
352
|
+
without decoding its meshes with `readTileflowModelFileSummary`. Models are served publicly and may
|
|
353
|
+
be cached, so they must not hold secrets or restricted personal data.
|
|
354
|
+
|
|
246
355
|
## Roofs
|
|
247
356
|
|
|
248
357
|
A building whose tiles carry a roof shape (World's `roof_shape`) gets that roof: `gabled`,
|
|
@@ -331,6 +440,42 @@ by day and night, mixed between the themes either side of a blend's position.
|
|
|
331
440
|
|
|
332
441
|
## Night light
|
|
333
442
|
|
|
443
|
+
Architectural illumination has two independent controls:
|
|
444
|
+
|
|
445
|
+
- `floodLight` selects ordinary buildings by height. Ground light starts at `minGroundHeight`
|
|
446
|
+
(30 m by default), with a stable per-building `groundRadius` in metres (default `[30, 100]`).
|
|
447
|
+
Wall light starts above `minWallHeight` (200 m); its reach is building height times
|
|
448
|
+
`wallHeightRatio` (default `1/3`). `intensity` is zero to one and `color` is a theme colour.
|
|
449
|
+
The effect fades with scene brightness; `brightnessScale` compensates an authored light-colour
|
|
450
|
+
transform and defaults to one. Overlapping ground contributions use their strongest value.
|
|
451
|
+
- `modelLighting` assigns physical materials and emission to architectural roles. Each profile accepts
|
|
452
|
+
`emissiveStrength` (zero to five), optional `emissiveColor` (otherwise the surface's own colour), and
|
|
453
|
+
`emissiveHeight`: `[beginHeight, endHeight, beginMultiplier, endMultiplier, curve]`.
|
|
454
|
+
Heights are fractions of the source mesh's vertical bounds (the full model for legacy files),
|
|
455
|
+
multipliers are zero to one, and the exponent is
|
|
456
|
+
`10 ** curve`, with `curve` from -1 to 1. Equal heights give a constant multiplier of one.
|
|
457
|
+
Every gradient component, strength and colour may use theme tokens. Emission is mixed in linear light and is independent
|
|
458
|
+
of ambient occlusion and solar shadows. Values above one saturate the surface emission at one;
|
|
459
|
+
they also control the falloff of explicitly authored area emitters.
|
|
460
|
+
|
|
461
|
+
Physical profiles also accept `color`, `colorMix` (zero to one, default one when a tint exists),
|
|
462
|
+
`roughness` and `metallic` (zero to one). Explicit `modelColors` override role tints. Missing
|
|
463
|
+
physical overrides retain the glTF material. `emissiveVariation` adds a stable, model-seeded amount
|
|
464
|
+
from zero to its value. Optional `colorVariation` supplies independent `[min, max]` ranges for
|
|
465
|
+
`hue` (0–360), `saturation` and `lightness` (0–100), with `space: 'srgb'` (default) or `'linear'`.
|
|
466
|
+
The same model identity keeps its samples across LODs and theme changes.
|
|
467
|
+
|
|
468
|
+
Streets leaves ordinary flood lighting off in its light presets. Its dusk/night wall profile uses a strength
|
|
469
|
+
of `0.1` and `[0.5, 0, 0, 1, 0.8]`, concentrating a warm wash near the foot. Monochrome uses a
|
|
470
|
+
weaker neutral wall profile and no architectural ground flood. Prepared models without role
|
|
471
|
+
metadata use their documented palette mapping. Windows use metallic reflection and varied emission;
|
|
472
|
+
doors and logos have separate profiles, and roofs remain non-emissive. Day/dawn profiles follow
|
|
473
|
+
the scene's brighter material response. A role does not generate missing windows or ground emitters.
|
|
474
|
+
See [building-model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
|
|
475
|
+
for glTF channels, explicit area emitters, compatibility and resource costs.
|
|
476
|
+
|
|
477
|
+
The separate `nightLight` control retains its decorative building and shop effects:
|
|
478
|
+
|
|
334
479
|
With `nightLight` above 0, the buildings light the street from data at night (as the sun's
|
|
335
480
|
`brightness` falls):
|
|
336
481
|
|
|
@@ -355,7 +500,8 @@ download.
|
|
|
355
500
|
|
|
356
501
|
## Options
|
|
357
502
|
|
|
358
|
-
The renderer's own options say only how it is delivered: `minzoom` (15 by default
|
|
503
|
+
The renderer's own options say only how it is delivered: `minzoom` (15 by default; the default
|
|
504
|
+
entrance bands start from it), `maxTiles`,
|
|
359
505
|
`beforeId` to place the layer before another one, and `transformRequest` to apply the map's request
|
|
360
506
|
rules to the tiles it reads.
|
|
361
507
|
|
|
@@ -365,10 +511,14 @@ During partial opacity, a depth-only geometry pass resolves the nearest surfaces
|
|
|
365
511
|
blending, preventing rear walls from bleeding through. At full opacity only the normal geometry
|
|
366
512
|
pass runs; zoom fading schedules no extra animation frames or per-zoom mesh rebuilds.
|
|
367
513
|
|
|
368
|
-
The browser bundle
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
514
|
+
The browser bundle builds from the tiles' bytes in one worker that the page's 3D buildings, trees
|
|
515
|
+
and street lights share, nearest tiles first: it decodes each tile once for all of them, meshes its
|
|
516
|
+
buildings, keeps their indices to 16 bits when the vertices allow, lays out the walls' contact-band
|
|
517
|
+
segments and transfers the finished arrays. The renderer uploads them to the GPU and keeps no copy;
|
|
518
|
+
a style reloaded in full keeps the uploaded buffers. No worker URL or extra asset hosting is needed.
|
|
519
|
+
A Content Security Policy must allow `worker-src blob:` to use this path; if workers are
|
|
520
|
+
unavailable or blocked, the layers build in the page instead. The worker is terminated when the
|
|
521
|
+
last of those layers is disposed. Tiles the camera has left are not downloaded or built. Pending tile updates retain
|
|
372
522
|
the previous mesh until their replacement is ready; cancelled and obsolete results are discarded.
|
|
373
523
|
|
|
374
524
|
Mercator rendering batches buildings spatially and skips batches outside the camera. Shadow
|
|
@@ -385,7 +535,16 @@ Mercator texel grid stays stable during small pans, including changes of latitud
|
|
|
385
535
|
the meshes' physical metre scale. One filtered depth comparison keeps edges sharp.
|
|
386
536
|
Missing camera capabilities, terrain or a view near the horizon use the full loaded area instead.
|
|
387
537
|
The depth pass runs again when its focused area, light or casting geometry changes and is reused
|
|
388
|
-
when they stay unchanged.
|
|
538
|
+
when they stay unchanged. While the camera moves, the focused area takes a quarter of its span as a
|
|
539
|
+
margin on each side and is kept while it still covers the view at a similar resolution, so the
|
|
540
|
+
depth pass is not drawn on every frame; once the camera stops, it is fitted to the view again.
|
|
541
|
+
|
|
542
|
+
Model ambient visibility and lighting roles retain the existing 16-byte vertex stride. They add
|
|
543
|
+
no geometry pass or texture. Architectural ground lighting uses a separate one-byte-per-pixel
|
|
544
|
+
mask and one geometry/composite pair of passes while active; its mask is released when inactive.
|
|
545
|
+
The contact band and the architectural light draw their masks in the layer's prerender, before
|
|
546
|
+
the map's main pass, so the map's framebuffer is never switched while its layers draw.
|
|
547
|
+
At 1280 × 720 and DPR 1, the mask occupies 921,600 bytes, excluding driver overhead.
|
|
389
548
|
|
|
390
549
|
## Limits
|
|
391
550
|
|
|
@@ -395,8 +554,9 @@ when they stay unchanged.
|
|
|
395
554
|
- Roofs follow the building's smallest enclosing rectangle: a building with courtyards or far from a
|
|
396
555
|
rectangle keeps a flat roof, and `sawtooth` roofs are drawn flat.
|
|
397
556
|
- A colour expression is evaluated at zoom 16.
|
|
398
|
-
- Building models and landmarks take no edges
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
557
|
+
- Building models and landmarks take no illuminated edges and cannot be picked or styled by state.
|
|
558
|
+
- Model materials use semantic colours and role emission; physical roughness, metallic
|
|
559
|
+
reflections and authored textures are not rendered.
|
|
560
|
+
- A hosted map places its building models as its Team's 3D models: `tileflow deploy` uploads no
|
|
561
|
+
model files (see [Team 3D models](#team-3d-models)). A deploy with `--with-native` does not take
|
|
562
|
+
them, since MapLibre Native draws no models. Models named by `url` deploy as they are.
|
|
@@ -160,17 +160,26 @@ texture's marks remain approximately constant in screen pixels across classes, r
|
|
|
160
160
|
|
|
161
161
|
`classes.pedestrian` styles line-like pedestrian ways. Polygon pedestrian plazas are a distinct
|
|
162
162
|
geometry and use `areas.pedestrian`, including optional fill opacity, outline color, or sprite
|
|
163
|
-
pattern. This prevents plazas from degrading into a thin polygon outline.
|
|
163
|
+
pattern. This prevents plazas from degrading into a thin polygon outline. With a
|
|
164
|
+
`transportationOutline` binding, as in Tileflow World, area outlines stroke only the outer boundary
|
|
165
|
+
of each area, never its holes.
|
|
164
166
|
|
|
165
167
|
Road treatments refine a class without creating another class or exposing source fields.
|
|
166
168
|
`construction`, `expressway`, `indoor`, `official`, `ramp`, and `unpaved` live under `modifiers`;
|
|
167
169
|
general, bicycle, foot, horse, and toll restrictions live under `restrictions`; `alley`,
|
|
168
170
|
`crossover`, `driveway`, `parkingAisle`, and `yard` live under `serviceTypes`; and exact
|
|
169
|
-
mountain-bike difficulty values live under `mountainBike`. Treatments reuse
|
|
171
|
+
mountain-bike difficulty values, which apply to paths and tracks, live under `mountainBike`. Treatments reuse
|
|
170
172
|
surface/tunnel/bridge and casing/fill/shadow paint controls, plus a relative `widthScale`. The
|
|
171
173
|
compiler emits feature-driven paint expressions inside the existing stable class layers rather
|
|
172
174
|
than multiplying every possible combination.
|
|
173
175
|
|
|
176
|
+
A solid dash, `[1, 0]`, is emitted as no `line-dasharray` at all, so MapLibre draws the line with
|
|
177
|
+
its solid-line shader rather than its dash shader. A treatment's dash that depends on the road
|
|
178
|
+
class is settled for each class layer: a dash for construction paths leaves a motorway's layers
|
|
179
|
+
solid. Roundabout rings state their road widths at each zoom as numbers where the widths allow,
|
|
180
|
+
and name the feature conditions they repeat once with `let`, which keeps their style compact and
|
|
181
|
+
quick to validate.
|
|
182
|
+
|
|
174
183
|
Road names, shields, and motorway junctions remain label concerns:
|
|
175
184
|
|
|
176
185
|
```ts
|
|
@@ -180,8 +180,8 @@ layer is a protection tint, while urban parks and gardens come from
|
|
|
180
180
|
`landcover.class=grass/subclass=park|garden`. The ambiguous legacy `park` class is therefore not
|
|
181
181
|
a public land-cover target.
|
|
182
182
|
|
|
183
|
-
Tileflow World V1 makes `bathymetry`, `globallandcover`, `circular_feature`, `sidewalk`,
|
|
184
|
-
`street_furniture` mandatory. Use `tileflowWorldV1Schema()` for that product instead of making raw
|
|
183
|
+
Tileflow World V1 makes `bathymetry`, `globallandcover`, `circular_feature`, `sidewalk`,
|
|
184
|
+
`street_furniture`, and `transportation_outline` mandatory. Use `tileflowWorldV1Schema()` for that product instead of making raw
|
|
185
185
|
layer names part of a style. Publication tooling calls `validateTileflowWorldV1Tilejson(...)` and
|
|
186
186
|
fails closed unless each layer occurs exactly once, declares its contracted native zooms, and
|
|
187
187
|
advertises the typed fields consumed by the maps. This includes bathymetry z0–z9, global land cover
|
|
@@ -212,48 +212,29 @@ export default defineMap({
|
|
|
212
212
|
marine: 'chart', // 'none' | 'bathymetry' | 'nautical' | 'chart'
|
|
213
213
|
modules: {
|
|
214
214
|
nautical: nautical({
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
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
|
|
242
|
-
`nautical(...)` module
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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.
|
|
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
|
|
|
@@ -483,7 +464,10 @@ add `tileflow:sources` plus `tileflow:sourceRequirements` for independent produc
|
|
|
483
464
|
Detailed city datasets can bind `sidewalk`, `streetFurniture`, and `circularFeature` source layers
|
|
484
465
|
through `openMapTiles({layers, fields})`. When present, `roads.sidewalks` owns source-backed
|
|
485
466
|
pedestrian polygons, `roads.crossings` owns oriented crossing icons, and `roads.roundabouts` owns
|
|
486
|
-
metric circular road rings.
|
|
467
|
+
metric circular road rings. A `transportationOutline` binding names lines that trace the outer
|
|
468
|
+
boundary of each road area; when bound, `roads.areas` outlines stroke those lines instead of the
|
|
469
|
+
area polygons, so the holes of a plaza (tree pits, planting beds) are not stroked. These bindings
|
|
470
|
+
are optional: a generic OpenMapTiles source remains
|
|
487
471
|
valid and the compiler omits unsupported detail instead of inventing geometry. Crossing icons are
|
|
488
472
|
explicit because the data contract cannot assume a sprite name. They require positive physical
|
|
489
473
|
`markings` evidence and reject `crossing=no`; both fields are semantic bindings, so custom schemas
|