@doegis/core
Version:
DOE GIS API
3 lines (1 loc) • 15.3 kB
JavaScript
import e from"../../../../../core/BidiEngine.js";import{assertIsSome as t}from"../../../../../core/maybe.js";import{numericHash as n}from"../../../../../core/string.js";import{GeometryType as i,Point as s}from"../../../../../geometry/support/TileClipper.js";import{BucketType as a}from"../enums.js";import{C_DEG_TO_RAD as o,log2 as r,interpolate as l}from"../GeometryUtils.js";import{TILE_PIXEL_RATIO as h,Anchor as x,TILE_COORD_SIZE as c}from"../Placement.js";import{SDF_GLYPH_SIZE as d,TextShaping as g}from"../TextShaping.js";import f from"./BaseBucket.js";import{TextTransform as y,SymbolPlacement as m,RotationAlignment as u,TextWritingMode as p,SymbolAnchor as _,TextJustification as M}from"../style/StyleDefinition.js";import{IconLayout as P,TextLayout as I}from"../style/StyleLayer.js";const b=10;function A(e,t){return e.iconMosaicItem&&t.iconMosaicItem?e.iconMosaicItem.page===t.iconMosaicItem.page?0:e.iconMosaicItem.page-t.iconMosaicItem.page:e.iconMosaicItem&&!t.iconMosaicItem?1:!e.iconMosaicItem&&t.iconMosaicItem?-1:0}class L extends f{constructor(e,t,n,i,s,o,r,l){super(e,t,l.getSpriteItems()),this.type=a.SYMBOL,this._markerMap=new Map,this._glyphMap=new Map,this._glyphBufferDataStorage=new Map,this._isIconSDF=!1,this._iconVertexBuffer=n,this._iconIndexBuffer=i,this._textVertexBuffer=s,this._textIndexBuffer=o,this._placementEngine=r,this._workerTileHandler=l}get markerPageMap(){return this._markerMap}get glyphsPageMap(){return this._glyphMap}get symbolInstances(){return this._symbolInstances}getResources(e,t,i){const s=this.layer,a=this.zoom;e&&e.setExtent(this.layerExtent);const o=s.getLayoutProperty("icon-image"),r=s.getLayoutProperty("text-field");let l=s.getLayoutProperty("text-transform"),h=s.getLayoutProperty("text-font");const x=[];let c,d,g,f;o&&!o.isDataDriven&&(c=o.getValue(a)),r&&!r.isDataDriven&&(d=r.getValue(a)),l&&l.isDataDriven||(g=s.getLayoutValue("text-transform",a),l=null),h&&h.isDataDriven||(f=s.getLayoutValue("text-font",a),h=null);for(const m of this._features){const u=m.getGeometry(e);if(!u||0===u.length)continue;let p,_;o&&(p=o.isDataDriven?o.getValue(a,m):this._replaceKeys(c,m.values),p&&t(p));let M=!1;if(r&&(_=r.isDataDriven?r.getValue(a,m):this._replaceKeys(d,m.values),_)){switch(_=_.replace(/\\n/g,"\n"),l&&(g=l.getValue(a,m)),g){case y.LOWERCASE:_=_.toLowerCase();break;case y.UPPERCASE:_=_.toUpperCase()}if(L._bidiEngine.hasBidiChar(_)){let e;e="rtl"===L._bidiEngine.checkContextual(_)?"IDNNN":"ICNNN",_=L._bidiEngine.bidiTransform(_,e,"VLYSN"),M=!0}const e=_.length;if(e>0){h&&(f=h.getValue(a,m));for(const t of f){let n=i[t];n||(n=i[t]=new Set);for(let t=0;t<e;t++){const e=_.charCodeAt(t);n.add(e)}}}}if(!p&&!_)continue;const P=s.getLayoutValue("symbol-sort-key",a,m),I={feature:m,sprite:p,label:_,rtl:M,geometry:u,hash:(_?n(_):0)^(p?n(p):0),priority:P,textFont:f};x.push(I)}this._symbolFeatures=x}processFeatures(e){e&&e.setExtent(this.layerExtent);const n=this.layer,s=this.zoom,a=n.getLayoutValue("symbol-placement",s),r=a!==m.POINT,l=n.getLayoutValue("symbol-spacing",s)*h,f=n.getLayoutProperty("icon-image"),y=n.getLayoutProperty("text-field"),b=f?new P(n,s,r):null,V=y?new I(n,s,r):null,T=this._workerTileHandler;let w;f&&(w=T.getSpriteItems()),this._iconIndexStart=3*this._iconIndexBuffer.index,this._textIndexStart=3*this._textIndexBuffer.index,this._iconIndexCount=0,this._textIndexCount=0,this._markerMap.clear(),this._glyphMap.clear();const B=[];let C=1;V&&V.size&&(C=V.size/d);const R=V?V.maxAngle*o:0,D=V?V.size*h:0;for(const o of this._symbolFeatures){let e;b&&w&&o.sprite&&(e=w[o.sprite],e&&e.sdf&&(this._isIconSDF=!0));let n;!!e&&b.update(s,o.feature);let f=0;const y=o.label;if(y){t(V),V.update(s,o.feature);const e=r&&V.rotationAlignment===u.MAP?V.keepUpright:V.writingMode&&V.writingMode.includes(p.VERTICAL);let i=.5;switch(V.anchor){case _.TOP_LEFT:case _.LEFT:case _.BOTTOM_LEFT:i=0;break;case _.TOP_RIGHT:case _.RIGHT:case _.BOTTOM_RIGHT:i=1}let a=.5;switch(V.anchor){case _.TOP_LEFT:case _.TOP:case _.TOP_RIGHT:a=0;break;case _.BOTTOM_LEFT:case _.BOTTOM:case _.BOTTOM_RIGHT:a=1}let l=.5;switch(V.justify){case M.AUTO:l=i;break;case M.LEFT:l=0;break;case M.RIGHT:l=1}const x=V.letterSpacing*d,c=r?0:V.maxWidth*d,m=V.lineHeight*d,P=o.textFont.map((e=>T.getGlyphItems(e)));if(n=new g(P,c,m,x,i,a,l).getShaping(y,o.rtl,e),n&&n.length>0){let e=1e30,t=-1e30;for(const i of n)e=Math.min(e,i.x),t=Math.max(t,i.x);f=(t-e+2*d)*C*h}}for(let t of o.geometry){const s=[];if(a===m.LINE){if(n?.length&&V?.size){const e=V.size*h*(2+Math.min(2,4*Math.abs(V.offset[1])));t=L._smoothVertices(t,e)}L._pushAnchors(s,t,l,f)}else a===m.LINE_CENTER?L._pushCenterAnchor(s,t):o.feature.type===i.Polygon?L._pushCentroid(s,t):s.push(new x(t[0].x,t[0].y));for(const i of s){if(i.x<0||i.x>c||i.y<0||i.y>c)continue;if(r&&f>0&&V?.rotationAlignment===u.MAP&&!L._honorsTextMaxAngle(t,i,f,R,D))continue;const s={shaping:n,line:t,iconMosaicItem:e,anchor:i,symbolFeature:o,textColliders:[],iconColliders:[],textVertexRanges:[],iconVertexRanges:[]};B.push(s),this._processFeature(s,b,V)}}}B.sort(A),this._addPlacedGlyphs(),this._symbolInstances=B}serialize(){let e=11;e+=this.layerUIDs.length,e+=3*this.markerPageMap.size,e+=3*this.glyphsPageMap.size,e+=L._symbolsSerializationLength(this._symbolInstances),e+=this._iconVertexBuffer.array.length,e+=this._iconIndexBuffer.array.length,e+=this._textVertexBuffer.array.length,e+=this._textIndexBuffer.array.length;const t=new Uint32Array(e),n=new Int32Array(t.buffer),i=new Float32Array(t.buffer);let s=0;t[s++]=this.type,t[s++]=this.layerUIDs.length;for(let a=0;a<this.layerUIDs.length;a++)t[s++]=this.layerUIDs[a];t[s++]=this._isIconSDF?1:0,t[s++]=this.markerPageMap.size;for(const[a,[o,r]]of this.markerPageMap)t[s++]=a,t[s++]=o,t[s++]=r;t[s++]=this.glyphsPageMap.size;for(const[a,[o,r]]of this.glyphsPageMap)t[s++]=a,t[s++]=o,t[s++]=r;t[s++]=this._iconVertexBuffer.index/4,t[s++]=this._textVertexBuffer.index/4,s=L.serializeSymbols(t,n,i,s,this._symbolInstances),t[s++]=this._iconVertexBuffer.array.length;for(let a=0;a<this._iconVertexBuffer.array.length;a++)n[s++]=this._iconVertexBuffer.array[a];t[s++]=this._iconIndexBuffer.array.length;for(let a=0;a<this._iconIndexBuffer.array.length;a++)t[s++]=this._iconIndexBuffer.array[a];t[s++]=this._textVertexBuffer.array.length;for(let a=0;a<this._textVertexBuffer.array.length;a++)n[s++]=this._textVertexBuffer.array[a];t[s++]=this._textIndexBuffer.array.length;for(let a=0;a<this._textIndexBuffer.array.length;a++)t[s++]=this._textIndexBuffer.array[a];return t.buffer}static _symbolsSerializationLength(e){let t=0;t+=1;for(const n of e||[]){t+=4,t+=1;for(const e of n.textColliders)t+=b;for(const e of n.iconColliders)t+=b;t+=1,t+=2*n.textVertexRanges.length,t+=1,t+=2*n.iconVertexRanges.length}return t}static serializeSymbols(e,t,n,i,s){s=s||[],t[i++]=s.length;for(const a of s){t[i++]=a.anchor.x,t[i++]=a.anchor.y,t[i++]=a.symbolFeature.hash,t[i++]=a.symbolFeature.priority,t[i++]=a.textColliders.length+a.iconColliders.length;for(const e of a.textColliders)t[i++]=e.xTile,t[i++]=e.yTile,t[i++]=e.dxPixels,t[i++]=e.dyPixels,t[i++]=e.hard?1:0,t[i++]=e.partIndex,n[i++]=e.minLod,n[i++]=e.maxLod,t[i++]=e.width,t[i++]=e.height;for(const e of a.iconColliders)t[i++]=e.xTile,t[i++]=e.yTile,t[i++]=e.dxPixels,t[i++]=e.dyPixels,t[i++]=e.hard?1:0,t[i++]=e.partIndex,n[i++]=e.minLod,n[i++]=e.maxLod,t[i++]=e.width,t[i++]=e.height;t[i++]=a.textVertexRanges.length;for(const[e,n]of a.textVertexRanges)t[i++]=e,t[i++]=n;t[i++]=a.iconVertexRanges.length;for(const[e,n]of a.iconVertexRanges)t[i++]=e,t[i++]=n}return i}_replaceKeys(e,t){return e.replace(/{([^{}]+)}/g,((e,n)=>n in t?t[n]:""))}_processFeature(e,t,n){const{line:i,iconMosaicItem:s,shaping:a,anchor:o}=e,l=this.zoom,h=this.layer,x=!!s;let c=!0;x&&(c=t?.optional||!s);const d=a&&a.length>0,g=!d||n?.optional;let f,y;if(x&&(f=this._placementEngine.getIconPlacement(o,s,t)),(f||c)&&(d&&(y=this._placementEngine.getTextPlacement(o,a,i,n)),y||g)){if(f&&y||(g||c?g||y?c||f||(y=null):f=null:(f=null,y=null)),y){const t=h.hasDataDrivenText?h.textMaterial.encodeAttributes(e.symbolFeature.feature,l,h):null;if(this._storePlacedGlyphs(e,y.shapes,l,n.rotationAlignment,t),y.textColliders){e.textColliders=y.textColliders;for(const e of y.textColliders){e.minLod=Math.max(l+r(e.minLod),0),e.maxLod=Math.min(l+r(e.maxLod),25);const t=e.angle;if(t){const n=Math.cos(t),i=Math.sin(t),s=e.dxPixels*n-e.dyPixels*i,a=e.dxPixels*i+e.dyPixels*n,o=(e.dxPixels+e.width)*n-e.dyPixels*i,r=(e.dxPixels+e.width)*i+e.dyPixels*n,l=e.dxPixels*n-(e.dyPixels+e.height)*i,h=e.dxPixels*i+(e.dyPixels+e.height)*n,x=(e.dxPixels+e.width)*n-(e.dyPixels+e.height)*i,c=(e.dxPixels+e.width)*i+(e.dyPixels+e.height)*n,d=Math.min(s,o,l,x),g=Math.max(s,o,l,x),f=Math.min(a,r,h,c),y=Math.max(a,r,h,c);e.dxPixels=d,e.dyPixels=f,e.width=g-d,e.height=y-f}}}}if(f){const n=h.hasDataDrivenIcon?h.iconMaterial.encodeAttributes(e.symbolFeature.feature,l,h):null;if(this._addPlacedIcons(e,f.shapes,l,s.page,t.rotationAlignment===u.VIEWPORT,n),f.iconColliders){e.iconColliders=f.iconColliders;for(const e of f.iconColliders){e.minLod=Math.max(l+r(e.minLod),0),e.maxLod=Math.min(l+r(e.maxLod),25);const t=e.angle;if(t){const n=Math.cos(t),i=Math.sin(t),s=e.dxPixels*n-e.dyPixels*i,a=e.dxPixels*i+e.dyPixels*n,o=(e.dxPixels+e.width)*n-e.dyPixels*i,r=(e.dxPixels+e.width)*i+e.dyPixels*n,l=e.dxPixels*n-(e.dyPixels+e.height)*i,h=e.dxPixels*i+(e.dyPixels+e.height)*n,x=(e.dxPixels+e.width)*n-(e.dyPixels+e.height)*i,c=(e.dxPixels+e.width)*i+(e.dyPixels+e.height)*n,d=Math.min(s,o,l,x),g=Math.max(s,o,l,x),f=Math.min(a,r,h,c),y=Math.max(a,r,h,c);e.dxPixels=d,e.dyPixels=f,e.width=g-d,e.height=y-f}}}}}}_addPlacedIcons(e,t,n,i,s,a){const o=Math.max(n-1,0),l=this._iconVertexBuffer,h=this._iconIndexBuffer,x=this._markerMap;for(const c of t){const t=s?0:Math.max(n+r(c.minzoom),o),d=s?25:Math.min(n+r(c.maxzoom),25);if(d<=t)continue;const g=c.tl,f=c.tr,y=c.bl,m=c.br,u=c.mosaicRect,p=c.labelAngle,_=c.minAngle,M=c.maxAngle,P=c.anchor,I=l.index,b=u.x,A=u.y,L=b+u.width,V=A+u.height,T=l.index;l.add(P.x,P.y,g.x,g.y,b,A,p,_,M,t,d,a),l.add(P.x,P.y,f.x,f.y,L,A,p,_,M,t,d,a),l.add(P.x,P.y,y.x,y.y,b,V,p,_,M,t,d,a),l.add(P.x,P.y,m.x,m.y,L,V,p,_,M,t,d,a),e.iconVertexRanges.length>0&&e.iconVertexRanges[0][0]+e.iconVertexRanges[0][1]===T?e.iconVertexRanges[0][1]+=4:e.iconVertexRanges.push([T,4]),h.add(I+0,I+1,I+2),h.add(I+1,I+2,I+3),x.has(i)?x.get(i)[1]+=6:x.set(i,[this._iconIndexStart+this._iconIndexCount,6]),this._iconIndexCount+=6}}_addPlacedGlyphs(){const e=this._textVertexBuffer,t=this._textIndexBuffer,n=this._glyphMap;for(const[i,s]of this._glyphBufferDataStorage)for(const a of s){const s=e.index,o=a.symbolInstance,r=a.ddAttributes,l=e.index;e.add(a.glyphAnchor[0],a.glyphAnchor[1],a.tl[0],a.tl[1],a.xmin,a.ymin,a.labelAngle,a.minAngle,a.maxAngle,a.minLod,a.maxLod,r),e.add(a.glyphAnchor[0],a.glyphAnchor[1],a.tr[0],a.tr[1],a.xmax,a.ymin,a.labelAngle,a.minAngle,a.maxAngle,a.minLod,a.maxLod,r),e.add(a.glyphAnchor[0],a.glyphAnchor[1],a.bl[0],a.bl[1],a.xmin,a.ymax,a.labelAngle,a.minAngle,a.maxAngle,a.minLod,a.maxLod,r),e.add(a.glyphAnchor[0],a.glyphAnchor[1],a.br[0],a.br[1],a.xmax,a.ymax,a.labelAngle,a.minAngle,a.maxAngle,a.minLod,a.maxLod,r),o.textVertexRanges.length>0&&o.textVertexRanges[0][0]+o.textVertexRanges[0][1]===l?o.textVertexRanges[0][1]+=4:o.textVertexRanges.push([l,4]),t.add(s+0,s+1,s+2),t.add(s+1,s+2,s+3),n.has(i)?n.get(i)[1]+=6:n.set(i,[this._textIndexStart+this._textIndexCount,6]),this._textIndexCount+=6}this._glyphBufferDataStorage.clear()}_storePlacedGlyphs(e,t,n,i,s){const a=Math.max(n-1,0),o=i===u.VIEWPORT;let l,h,x,c,d,g,f,y,m,p,_;for(const u of t){if(l=o?0:Math.max(n+r(u.minzoom),a),h=o?25:Math.min(n+r(u.maxzoom),25),h<=l)continue;x=u.tl,c=u.tr,d=u.bl,g=u.br,f=u.labelAngle,y=u.minAngle,m=u.maxAngle,p=u.anchor,_=u.mosaicRect,this._glyphBufferDataStorage.has(u.page)||this._glyphBufferDataStorage.set(u.page,[]);this._glyphBufferDataStorage.get(u.page).push({glyphAnchor:[p.x,p.y],tl:[x.x,x.y],tr:[c.x,c.y],bl:[d.x,d.y],br:[g.x,g.y],xmin:_.x,ymin:_.y,xmax:_.x+_.width,ymax:_.y+_.height,labelAngle:f,minAngle:y,maxAngle:m,minLod:l,maxLod:h,placementLod:a,symbolInstance:e,ddAttributes:s})}}static _pushAnchors(e,t,n,i){n+=i;let a=0;const o=t.length-1;for(let l=0;l<o;l++)a+=s.distance(t[l],t[l+1]);let r=i||n;if(r*=.5,a<=r)return;const h=r/a;let c=0,d=-(n=a/Math.max(Math.round(a/n),1))/2;const g=t.length-1;for(let s=0;s<g;s++){const i=t[s],a=t[s+1],o=a.x-i.x,r=a.y-i.y,g=Math.sqrt(o*o+r*r);let f;for(;d+n<c+g;){d+=n;const t=(d-c)/g,y=l(i.x,a.x,t),m=l(i.y,a.y,t);void 0===f&&(f=Math.atan2(r,o)),e.push(new x(y,m,f,s,h))}c+=g}}static _pushCenterAnchor(e,t){let n=0;const i=t.length-1;for(let l=0;l<i;l++)n+=s.distance(t[l],t[l+1]);const a=n/2;let o=0;const r=t.length-1;for(let s=0;s<r;s++){const n=t[s],i=t[s+1],r=i.x-n.x,h=i.y-n.y,c=Math.sqrt(r*r+h*h);if(a<o+c){const t=(a-o)/c,d=l(n.x,i.x,t),g=l(n.y,i.y,t),f=Math.atan2(h,r);return void e.push(new x(d,g,f,s,0))}o+=c}}static _deviation(e,t,n){const i=(t.x-e.x)*(n.x-t.x)+(t.y-e.y)*(n.y-t.y),s=(t.x-e.x)*(n.y-t.y)-(t.y-e.y)*(n.x-t.x);return Math.atan2(s,i)}static _honorsTextMaxAngle(e,t,n,i,a){let o=0;const r=n/2;let l=new s(t.x,t.y),h=t.segment+1;for(;o>-r;){if(--h,h<0)return!1;o-=s.distance(e[h],l),l=e[h]}o+=s.distance(e[h],e[h+1]);const x=[];let c=0;const d=e.length;for(;o<r;){const t=e[h];let n,r=h;do{if(++r,r===d)return!1;n=e[r]}while(n.isEqual(t));let l,g=r;do{if(++g,g===d)return!1;l=e[g]}while(l.isEqual(n));const f=this._deviation(t,n,l);for(x.push({deviation:f,distToAnchor:o}),c+=f;o-x[0].distToAnchor>a;)c-=x.shift().deviation;if(Math.abs(c)>i)return!1;o+=s.distance(n,l),h=r}return!0}static _smoothVertices(e,t){if(t<=0)return e;let n=e.length;if(n<3)return e;const i=[];let a=0,o=0;i.push(0);for(let y=1;y<n;y++){const t=s.distance(e[y],e[y-1]);t>0&&(a+=t,i.push(a),o++,o!==y&&(e[o]=e[y]))}if(n=o+1,n<3)return e;t=Math.min(t,.2*a);const r=e[0].x,l=e[0].y,h=e[n-1].x,x=e[n-1].y,c=s.sub(e[0],e[1]);c.normalize(),e[0].x+=t*c.x,e[0].y+=t*c.y,c.assignSub(e[n-1],e[n-2]),c.normalize(),e[n-1].x+=t*c.x,e[n-1].y+=t*c.y,i[0]-=t,i[n-1]+=t;const d=[];d.push(new s(r,l));const g=1e-6,f=.5*t;for(let y=1;y<n-1;y++){let a=0,o=0,r=0;for(let n=y-1;n>=0;n--){const s=f+i[n+1]-i[y];if(s<0)break;const l=i[n+1]-i[n],h=i[y]-i[n]<f?1:s/l;if(h<g)break;const x=h*h,c=h*s-.5*x*l,d=h*l/t,m=e[n+1],u=e[n].x-m.x,p=e[n].y-m.y;a+=d/c*(m.x*h*s+.5*x*(s*u-l*m.x)-x*h*l*u/3),o+=d/c*(m.y*h*s+.5*x*(s*p-l*m.y)-x*h*l*p/3),r+=d}for(let s=y+1;s<n;s++){const n=f-i[s-1]+i[y];if(n<0)break;const l=i[s]-i[s-1],h=i[s]-i[y]<f?1:n/l;if(h<g)break;const x=h*h,c=h*n-.5*x*l,d=h*l/t,m=e[s-1],u=e[s].x-m.x,p=e[s].y-m.y;a+=d/c*(m.x*h*n+.5*x*(n*u-l*m.x)-x*h*l*u/3),o+=d/c*(m.y*h*n+.5*x*(n*p-l*m.y)-x*h*l*p/3),r+=d}d.push(new s(a/r,o/r))}return d.push(new s(h,x)),e[0].x=r,e[0].y=l,e[n-1].x=h,e[n-1].y=x,d}static _pushCentroid(e,t){const n=0,i=0,s=4096,a=4096,o=t.length-1;let r=0,l=0,h=0,c=t[0].x,d=t[0].y;c>s&&(c=s),c<n&&(c=n),d>a&&(d=a),d<i&&(d=i);for(let x=1;x<o;x++){let e=t[x].x,o=t[x].y,g=t[x+1].x,f=t[x+1].y;e>s&&(e=s),e<n&&(e=n),o>a&&(o=a),o<i&&(o=i),g>s&&(g=s),g<n&&(g=n),f>a&&(f=a),f<i&&(f=i);const y=(e-c)*(f-d)-(g-c)*(o-d);r+=y*(c+e+g),l+=y*(d+o+f),h+=y}r/=3*h,l/=3*h,isNaN(r)||isNaN(l)||e.push(new x(r,l))}}L._bidiEngine=new e;export{L as default};