UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

3 lines (2 loc) • 15.1 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */ import e from"../../../../../core/BidiEngine.js";import{assertIsSome as t}from"../../../../../core/maybe.js";import{numericHash as n}from"../../../../../core/string.js";import{Point as s}from"../../../../../geometry/support/TileClipper.js";import{tilePixelRatio as i,tileSizeInTileUnits as a}from"../constants.js";import{cDegToRad as o,log2 as r,interpolate as l}from"../GeometryUtils.js";import{Anchor as h}from"../Placement.js";import{sdfGlyphSize as x,TextShaping as c}from"../TextShaping.js";import d from"./BaseBucket.js";import{IconLayout as f,TextLayout as g}from"../style/StyleLayer.js";const y=10;function m(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 u extends d{constructor(e,t,n,s,i,a,o,r,l){super(t,n,l.getSpriteItems()),this.type=3,this._markerMap=new Map,this._glyphMap=new Map,this._glyphBufferDataStorage=new Map,this._isIconSDF=!1,this._sourceTileKey=e,this._iconVertexBuffer=s,this._iconIndexBuffer=i,this._textVertexBuffer=a,this._textIndexBuffer=o,this._placementEngine=r,this._workerTileHandler=l}get markerPageMap(){return this._markerMap}get glyphsPageMap(){return this._glyphMap}get symbolInstances(){return this._symbolInstances}static{this._bidiEngine=new e}getResources(e,t,s){const i=this.layer,a=this.zoom;e&&e.setExtent(this.layerExtent);const o=i.getLayoutProperty("icon-image"),r=i.getLayoutProperty("text-field");let l=i.getLayoutProperty("text-transform"),h=i.getLayoutProperty("text-font");const x=[];let c,d,f,g;o&&!o.isDataDriven&&(c=o.getValue(a)),r&&!r.isDataDriven&&(d=r.getValue(a)),l&&l.isDataDriven||(f=i.getLayoutValue("text-transform",a),l=null),h&&h.isDataDriven||(g=i.getLayoutValue("text-font",a),h=null);for(const y of this._features){const m=y.getGeometry(e);if(!m||0===m.length)continue;let p,_;o&&(p=o.isDataDriven?o.getValue(a,y):this._replaceKeys(c,y.values),p&&t(p));let M=!1;if(r&&(_=r.isDataDriven?r.getValue(a,y):this._replaceKeys(d,y.values),_)){switch(_=_.replaceAll("\\n","\n"),l&&(f=l.getValue(a,y)),f){case 2:_=_.toLowerCase();break;case 1:_=_.toUpperCase()}if(u._bidiEngine.hasBidiChar(_)){let e;e="rtl"===u._bidiEngine.checkContextual(_)?"IDNNN":"ICNNN",_=u._bidiEngine.bidiTransform(_,e,"VLYSN"),M=!0}if(_.length>0){h&&(g=h.getValue(a,y));for(const e of g){let t=s[e];t||(t=s[e]=new Set);for(const e of _){const n=e.codePointAt(0);null!=n&&t.add(n)}}}}if(!p&&!_)continue;const b=i.getLayoutValue("symbol-sort-key",a,y),P={feature:y,sprite:p,label:_,rtl:M,geometry:m,hash:(_?n(_):0)^(p?n(p):0),priority:b,textFont:g};x.push(P)}this._symbolFeatures=x}processFeatures(e){e&&e.setExtent(this.layerExtent);const n=this.layer,s=this.zoom,r=n.getLayoutValue("symbol-placement",s),l=0!==r,d=n.getLayoutValue("symbol-spacing",s)*i,y=n.getLayoutProperty("icon-image"),p=n.getLayoutProperty("text-field"),_=y?new f(n,s,l):null,M=p?new g(n,s,l):null,b=this._workerTileHandler;let P;y&&(P=b.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 I=[];let A=1;M?.size&&(A=M.size/x);const V=M?M.maxAngle*o:0,w=M?M.size*i:0;for(const o of this._symbolFeatures){let e;_&&P&&o.sprite&&(e=P[o.sprite],e&&e.sdf&&(this._isIconSDF=!0));let n;!!e&&_.update(s,o.feature);let f=0;const g=o.label;if(g){t(M),M.update(s,o.feature);const e=l&&0===M.rotationAlignment?M.keepUpright:M.writingMode&&M.writingMode.includes(1);let a=.5;switch(M.anchor){case 5:case 1:case 7:a=0;break;case 6:case 2:case 8:a=1}let r=.5;switch(M.anchor){case 5:case 3:case 6:r=0;break;case 7:case 4:case 8:r=1}let h=.5;switch(M.justify){case 0:h=a;break;case 1:h=0;break;case 3:h=1}const d=M.letterSpacing*x,y=l?0:M.maxWidth*x,m=M.lineHeight*x,u=o.textFont.map(e=>b.getGlyphItems(e));if(n=new c(u,y,m,d,a,r,h).getShaping(g,o.rtl,e),n&&n.length>0){let e=1e30,t=-1e30;for(const s of n)e=Math.min(e,s.x),t=Math.max(t,s.x);f=(t-e+2*x)*A*i}}for(let t of o.geometry){const s=[];if(1===r){if(n?.length&&M?.size){const e=M.size*i*(2+Math.min(2,4*Math.abs(M.offset[1])));t=u._smoothVertices(t,e)}u._pushAnchors(s,t,d,f)}else 2===r?u._pushCenterAnchor(s,t):3===o.feature.type?u._pushCentroid(s,t):s.push(new h(t[0].x,t[0].y));for(const i of s){if(i.x<0||i.x>a||i.y<0||i.y>a)continue;if(l&&f>0&&0===M?.rotationAlignment&&!u._honorsTextMaxAngle(t,i,f,V,w))continue;const s={shaping:n,line:t,iconMosaicItem:e,anchor:i,symbolFeature:o,textColliders:[],iconColliders:[],textVertexRanges:[],iconVertexRanges:[]};I.push(s),this._processFeature(s,_,M)}}}I.sort(m),this._addPlacedGlyphs(),this._symbolInstances=I}serialize(){let e=14;e+=this.layerUIDs.length,e+=3*this.markerPageMap.size,e+=3*this.glyphsPageMap.size,e+=u._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),s=new Float32Array(t.buffer),[i,a,o]=this._sourceTileKey.split("/");let r=0;t[r++]=this.type,t[r++]=this.layerUIDs.length;for(let l=0;l<this.layerUIDs.length;l++)t[r++]=this.layerUIDs[l];t[r++]=this._isIconSDF?1:0,t[r++]=parseFloat(i),t[r++]=parseFloat(a),t[r++]=parseFloat(o),t[r++]=this.markerPageMap.size;for(const[l,[h,x]]of this.markerPageMap)t[r++]=l,t[r++]=h,t[r++]=x;t[r++]=this.glyphsPageMap.size;for(const[l,[h,x]]of this.glyphsPageMap)t[r++]=l,t[r++]=h,t[r++]=x;t[r++]=this._iconVertexBuffer.index/4,t[r++]=this._textVertexBuffer.index/4,r=u.serializeSymbols(t,n,s,r,this._symbolInstances),t[r++]=this._iconVertexBuffer.array.length;for(let l=0;l<this._iconVertexBuffer.array.length;l++)n[r++]=this._iconVertexBuffer.array[l];t[r++]=this._iconIndexBuffer.array.length;for(let l=0;l<this._iconIndexBuffer.array.length;l++)t[r++]=this._iconIndexBuffer.array[l];t[r++]=this._textVertexBuffer.array.length;for(let l=0;l<this._textVertexBuffer.array.length;l++)n[r++]=this._textVertexBuffer.array[l];t[r++]=this._textIndexBuffer.array.length;for(let l=0;l<this._textIndexBuffer.array.length;l++)t[r++]=this._textIndexBuffer.array[l];return t.buffer}static _symbolsSerializationLength(e){let t=0;t+=1;for(const n of e||[]){t+=5,t+=1;for(const e of n.textColliders)t+=y;for(const e of n.iconColliders)t+=y;t+=1,t+=2*n.textVertexRanges.length,t+=1,t+=2*n.iconVertexRanges.length}return t}static serializeSymbols(e,t,n,s,i){i=i||[],t[s++]=i.length;for(const a of i){t[s++]=a.anchor.x,t[s++]=a.anchor.y,t[s++]=a.symbolFeature.hash,t[s++]=a.symbolFeature.priority,t[s++]=a.symbolFeature.feature.featureIndex,t[s++]=a.textColliders.length+a.iconColliders.length;for(const e of a.textColliders)t[s++]=e.xTile,t[s++]=e.yTile,t[s++]=e.dxPixels,t[s++]=e.dyPixels,t[s++]=e.hard?1:0,t[s++]=e.partIndex,n[s++]=e.minLod,n[s++]=e.maxLod,t[s++]=e.width,t[s++]=e.height;for(const e of a.iconColliders)t[s++]=e.xTile,t[s++]=e.yTile,t[s++]=e.dxPixels,t[s++]=e.dyPixels,t[s++]=e.hard?1:0,t[s++]=e.partIndex,n[s++]=e.minLod,n[s++]=e.maxLod,t[s++]=e.width,t[s++]=e.height;t[s++]=a.textVertexRanges.length;for(const[e,n]of a.textVertexRanges)t[s++]=e,t[s++]=n;t[s++]=a.iconVertexRanges.length;for(const[e,n]of a.iconVertexRanges)t[s++]=e,t[s++]=n}return s}_replaceKeys(e,t){return e.replaceAll(/{([^{}]+)}/g,(e,n)=>n in t?t[n]:"")}_processFeature(e,t,n){const{line:s,iconMosaicItem:i,shaping:a,anchor:o}=e,l=this.zoom,h=this.layer,x=!!i;let c=!0;x&&(c=t?.optional||!i);const d=a&&a.length>0,f=!d||n?.optional;let g,y;if(x&&(g=this._placementEngine.getIconPlacement(o,i,t)),(g||c)&&(d&&(y=this._placementEngine.getTextPlacement(o,a,s,n)),y||f)){if(g&&y||(f||c?f||y?c||g||(y=null):g=null:(g=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),s=Math.sin(t),i=e.dxPixels*n-e.dyPixels*s,a=e.dxPixels*s+e.dyPixels*n,o=(e.dxPixels+e.width)*n-e.dyPixels*s,r=(e.dxPixels+e.width)*s+e.dyPixels*n,l=e.dxPixels*n-(e.dyPixels+e.height)*s,h=e.dxPixels*s+(e.dyPixels+e.height)*n,x=(e.dxPixels+e.width)*n-(e.dyPixels+e.height)*s,c=(e.dxPixels+e.width)*s+(e.dyPixels+e.height)*n,d=Math.min(i,o,l,x),f=Math.max(i,o,l,x),g=Math.min(a,r,h,c),y=Math.max(a,r,h,c);e.dxPixels=d,e.dyPixels=g,e.width=f-d,e.height=y-g}}}}if(g){const n=h.hasDataDrivenIcon?h.iconMaterial.encodeAttributes(e.symbolFeature.feature,l,h):null;if(this._addPlacedIcons(e,g.shapes,l,i.page,1===t.rotationAlignment,n),g.iconColliders){e.iconColliders=g.iconColliders;for(const e of g.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),s=Math.sin(t),i=e.dxPixels*n-e.dyPixels*s,a=e.dxPixels*s+e.dyPixels*n,o=(e.dxPixels+e.width)*n-e.dyPixels*s,r=(e.dxPixels+e.width)*s+e.dyPixels*n,l=e.dxPixels*n-(e.dyPixels+e.height)*s,h=e.dxPixels*s+(e.dyPixels+e.height)*n,x=(e.dxPixels+e.width)*n-(e.dyPixels+e.height)*s,c=(e.dxPixels+e.width)*s+(e.dyPixels+e.height)*n,d=Math.min(i,o,l,x),f=Math.max(i,o,l,x),g=Math.min(a,r,h,c),y=Math.max(a,r,h,c);e.dxPixels=d,e.dyPixels=g,e.width=f-d,e.height=y-g}}}}}}_addPlacedIcons(e,t,n,s,i,a){const o=Math.max(n-1,0),l=this._iconVertexBuffer,h=this._iconIndexBuffer,x=this._markerMap;for(const c of t){const t=i?0:Math.max(n+r(c.minzoom),o),d=i?25:Math.min(n+r(c.maxzoom),25);if(d<=t)continue;const f=c.tl,g=c.tr,y=c.bl,m=c.br,u=c.mosaicRect,p=c.labelAngle,_=c.minAngle,M=c.maxAngle,b=c.anchor,P=l.index,I=u.x,A=u.y,V=I+u.width,w=A+u.height,L=l.index;l.add(b.x,b.y,f.x,f.y,I,A,p,_,M,t,d,a),l.add(b.x,b.y,g.x,g.y,V,A,p,_,M,t,d,a),l.add(b.x,b.y,y.x,y.y,I,w,p,_,M,t,d,a),l.add(b.x,b.y,m.x,m.y,V,w,p,_,M,t,d,a),e.iconVertexRanges.length>0&&e.iconVertexRanges[0][0]+e.iconVertexRanges[0][1]===L?e.iconVertexRanges[0][1]+=4:e.iconVertexRanges.push([L,4]),h.add(P,P+1,P+2),h.add(P+1,P+2,P+3),x.has(s)?x.get(s)[1]+=6:x.set(s,[this._iconIndexStart+this._iconIndexCount,6]),this._iconIndexCount+=6}}_addPlacedGlyphs(){const e=this._textVertexBuffer,t=this._textIndexBuffer,n=this._glyphMap;for(const[s,i]of this._glyphBufferDataStorage)for(const a of i){const i=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(i,i+1,i+2),t.add(i+1,i+2,i+3),n.has(s)?n.get(s)[1]+=6:n.set(s,[this._textIndexStart+this._textIndexCount,6]),this._textIndexCount+=6}this._glyphBufferDataStorage.clear()}_storePlacedGlyphs(e,t,n,s,i){const a=Math.max(n-1,0),o=1===s;let l,h,x,c,d,f,g,y,m,u,p;for(const _ of t){if(l=o?0:Math.max(n+r(_.minzoom),a),h=o?25:Math.min(n+r(_.maxzoom),25),h<=l)continue;x=_.tl,c=_.tr,d=_.bl,f=_.br,g=_.labelAngle,y=_.minAngle,m=_.maxAngle,u=_.anchor,p=_.mosaicRect,this._glyphBufferDataStorage.has(_.page)||this._glyphBufferDataStorage.set(_.page,[]);this._glyphBufferDataStorage.get(_.page).push({glyphAnchor:[u.x,u.y],tl:[x.x,x.y],tr:[c.x,c.y],bl:[d.x,d.y],br:[f.x,f.y],xmin:p.x,ymin:p.y,xmax:p.x+p.width,ymax:p.y+p.height,labelAngle:g,minAngle:y,maxAngle:m,minLod:l,maxLod:h,placementLod:a,symbolInstance:e,ddAttributes:i})}}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 x=r/a;let c=0,d=-(n=a/Math.max(Math.round(a/n),1))/2;const f=t.length-1;for(let s=0;s<f;s++){const i=t[s],a=t[s+1],o=a.x-i.x,r=a.y-i.y,f=Math.sqrt(o*o+r*r);let g;for(;d+n<c+f;){d+=n;const t=(d-c)/f,y=l(i.x,a.x,t),m=l(i.y,a.y,t);void 0===g&&(g=Math.atan2(r,o)),e.push(new h(y,m,g,s,x))}c+=f}}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,x=i.y-n.y,c=Math.sqrt(r*r+x*x);if(a<o+c){const t=(a-o)/c,d=l(n.x,i.x,t),f=l(n.y,i.y,t),g=Math.atan2(x,r);return void e.push(new h(d,f,g,s,0))}o+=c}}static _deviation(e,t,n){const s=(t.x-e.x)*(n.x-t.x)+(t.y-e.y)*(n.y-t.y),i=(t.x-e.x)*(n.y-t.y)-(t.y-e.y)*(n.x-t.x);return Math.atan2(i,s)}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,f=r;do{if(++f,f===d)return!1;l=e[f]}while(l.isEqual(n));const g=this._deviation(t,n,l);for(x.push({deviation:g,distToAnchor:o}),c+=g;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 f=1e-6,g=.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=g+i[n+1]-i[y];if(s<0)break;const l=i[n+1]-i[n],h=i[y]-i[n]<g?1:s/l;if(h<f)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=g-i[s-1]+i[y];if(n<0)break;const l=i[s]-i[s-1],h=i[s]-i[y]<g?1:n/l;if(h<f)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,s=0,i=a,o=a,r=t.length-1;let l=0,x=0,c=0,d=t[0].x,f=t[0].y;d>i&&(d=i),d<n&&(d=n),f>o&&(f=o),f<s&&(f=s);for(let a=1;a<r;a++){let e=t[a].x,r=t[a].y,h=t[a+1].x,g=t[a+1].y;e>i&&(e=i),e<n&&(e=n),r>o&&(r=o),r<s&&(r=s),h>i&&(h=i),h<n&&(h=n),g>o&&(g=o),g<s&&(g=s);const y=(e-d)*(g-f)-(h-d)*(r-f);l+=y*(d+e+h),x+=y*(f+r+g),c+=y}l/=3*c,x/=3*c,isNaN(l)||isNaN(x)||e.push(new h(l,x))}}export{u as default};