UNPKG

@arcgis/core

Version:

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

3 lines (2 loc) • 12.7 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */ import{memoize as e}from"../../../../../../../core/MapUtils.js";import{pt2px as t}from"../../../../../../../core/screenUtils.js";import{numericHash as i}from"../../../../../../../core/string.js";import{fromRotation as r,translate as s}from"../../../../../../../core/libs/gl-matrix-2/math/mat2d.js";import{create as o}from"../../../../../../../core/libs/gl-matrix-2/factories/mat2df32.js";import{transformMat2d as n,set as a,sub as l,normalize as c,add as h,scale as m}from"../../../../../../../core/libs/gl-matrix-2/math/vec2.js";import{fromValues as d}from"../../../../../../../core/libs/gl-matrix-2/factories/vec2f32.js";import{create as u}from"../../../../../../../core/libs/gl-matrix-2/factories/vec2f64.js";import{convertGeometryToFlat as f,convertOptimizedGeometryToFlat as g,convertFlatToOptimizedGeometry as p,FlatGeometry as _}from"../../../../../../../geometry/FlatGeometry.js";import{GeometryCursor as y}from"../../../../../../../geometry/GeometryCursor.js";import{getLabelPoint as x}from"../../../../../../../geometry/support/labelPoint.js";import{generalizeOptimizedGeometry as b,convertToGeometry as P}from"../../../../../../../layers/graphics/featureConversionUtils.js";import v from"../../../../../../../layers/graphics/OptimizedGeometry.js";import{importLazily as M}from"../../../../../../../symbols/cim/utils.js";import{tileSize as w,minMaxZoomPrecisionFactor as L}from"../../../definitions.js";import I from"../../../collisions/BoundingBox.js";import{LabelMetric as S}from"../../../collisions/LabelMetric.js";import{smoothPaths as z,pathDivide as B}from"../../../mesh/templates/segmentUtils.js";import{getMinMaxZoom as D,processColorInput as j}from"../fill/meshWriterUtils.js";import{TextMeshWriter as G,maxLabelZoom as A}from"../text/TextMeshWriter.js";const F=1,Z=0,k=128,R=w*w/16,$=M(()=>import("../../../../../../../geometry/operators/gx/operatorIntersection.js")),C=M(()=>import("../../../../../../../chunks/FlatGeometry.js"));function E(e,t,r){return i(`${e}${t}${r}`)}function O(e,t,r,s,o){return i(`${e}${t}${r}${s*2**(A-o)}`)}function T(e,t,r){return i(`${e}${t}${r}`)}function W(e,t,r,s,o){return i(`${e}${o}${t}${r*2**(A-s)}`)}const X=e(e=>{let t=0;if(0===e)return 1/0;for(;!(e%2);)t++,e/=2;return t});class Y extends G{constructor(){super(...arguments),this._zoomLevel=0}async loadDependencies(){await Promise.all([super.loadDependencies(),$.getImportPromise(),C.getImportPromise()])}_write(e,t,i,r,s){if(this._zoomLevel=r||0,null!=i)throw new Error("InternalError: EffectGeometry not support for LabelMeshWriter");switch(t.geometryType){case"esriGeometryPoint":{const i=t.readXForDisplay(),r=t.readYForDisplay();this._writePoint(e,i,r,0,t);break}case"esriGeometryEnvelope":case"esriGeometryPolygon":this._writePolygon(e,t,s);break;case"esriGeometryMultipoint":{let i=0;const r=y.fromFeatureSetReader(t);if(r?.nextPath())for(;r.nextPoint();)this._writePoint(e,r.x,r.y,i++,t);break}case"esriGeometryPolyline":this._writeLines(e,t)}}_getMetricDir(){const{horizontalAlignment:e,verticalAlignment:t}=this.evaluatedMeshParams;return["center"===e?0:"right"===e?-1:1,"middle"===t?0:"bottom"===t?-1:1]}_createLineLabelMetric(e,t,i,r,s,o){const[n,a]=this._getMetricDir(),l=this.evaluatedMeshParams.scaleInfo?.maxScale??0,c=this.evaluatedMeshParams.scaleInfo?.minScale??0,h=this.evaluatedMeshParams.labelClassId;return new S(e,h,t,i,r,s,n,a,l,c,o)}_writePolygon(e,t,i){const r=$.module,s=C.module.constructFromFlatGeometry,o=t.readGeometryForDisplay(),n=t.readCentroidForDisplay()?.coords,a=o?.area()||0;if(!n)return;const l=a>=R;e.requiresRefresh||=l;const c=s(f(i)),h=s(f({x:n[0],y:n[1]})),m=!!r.execute(h,c,null);if(!o||!l||!m)return void this._writePoint(e,n[0],n[1],0,t);const d=s(g("polygon",o,null)),u=r.execute(d,c,null);if(!u)return void this._writePoint(e,n[0],n[1],0,t);const b=p(new _(u.toFlatGeometry())),P=y.fromOptimized(b,"esriGeometryPolygon",1),v=x(P)??n;this._writePoint(e,v[0],v[1],0,t)}_writePoint(e,t,i,r,s){if(t<0||t>w||i<0||i>w)return;const o=this._getShaping();if(!o)return;const n=s.getDisplayId(),a=this.evaluatedMeshParams.labelClassId,l=E(this.evaluatedMeshParams.layerId,s.getObjectId(),r),c=T(s.getObjectId(),a,r),[h,m]=this._getMetricDir(),d=this.evaluatedMeshParams.scaleInfo?.maxScale??0,u=this.evaluatedMeshParams.scaleInfo?.minScale??0,f=this._getPointReferenceBounds()||{offsetX:0,offsetY:0,size:0};e.metricStart(new S(n,a,l,c,t,i,h,m,d,u,f)),this._writeGlyphs(e,n,t,i,o,0,f,void 0,!1),e.metricBoxWrite(o.boundsT),e.metricEnd()}_getPointReferenceBounds(){if(!this._references)return null;for(const e of this._references){const t=e.getBoundsInfo();if(t)return t}return null}_writeLines(e,t){const{scaleInfo:i,verticalAlignment:r}=this.evaluatedMeshParams,s=this.evaluatedMeshParams.repeatLabelDistance||128,o=this._getShaping("middle");if(!o)return;const n=(e,t,i,r)=>this._placeSubdivGlyphs(e,t,i,r),a=(o.bounds.width+s)/(1<<F);this._current={out:e,id:t.getDisplayId(),objId:t.getObjectId(),shaping:o,zoomRange:D(i,this.getTileInfo()),referenceBounds:this._getPointReferenceBounds()||{offsetX:0,offsetY:0,size:0},offsetDirection:null,pathIndex:0},this._verticalPlacement="bottom"===r?"above":"top"===r?"below":null,this._verticalPlacement?this._writeAboveAndBelowAlong(t,n,a):this._writeCenterAlong(t,n,a)}_writeAboveAndBelowAlong(e,t,i){const{repeatLabel:r}=this.evaluatedMeshParams,{shaping:s}=this._current,o=s.bounds.halfHeight,n=e.readGeometryForDisplay();if(!n)return;const a=b(n,"esriGeometryPolyline",1)??new v,l=U(a,o),c=U(a,-o),h=P(c,"esriGeometryPolyline",!1,!1),m=P(l,"esriGeometryPolyline",!1,!1),d=z(m.paths,s.bounds.width),u=z(h.paths,s.bounds.width);this._current.offsetDirection="above";for(let f=0;f<d.length;f++)this._current.pathIndex=f,B(d[f],i,t,!!r);this._current.offsetDirection="below";for(let f=0;f<u.length;f++)this._current.pathIndex=f,B(u[f],i,t,!!r)}_writeCenterAlong(e,t,i){const{repeatLabel:r}=this.evaluatedMeshParams,{shaping:s}=this._current,o=z(e.readLegacyGeometryForDisplay().paths,s.bounds.width);for(let n=0;n<o.length;n++)this._current.pathIndex=n,B(o[n],i,t,!!r)}_placeSubdivGlyphs(e,t,i,r){const{allowOverrun:s,labelPosition:o,repeatLabelDistance:n,layerId:a,labelClassId:l}=this.evaluatedMeshParams,{objId:c,shaping:h,pathIndex:m}=this._current,d=this._current.zoomRange[0],u=X(t),f=this._current.shaping.bounds.width/(1<<F),g=Math.sqrt(n||k)/(1<<F),p=Math.min(i,r-i),_=h.isMultiline?A:Math.log2(p/(g+f/2)),y=0===t?_:Math.min(u,_),x=Math.max(d,this._zoomLevel+F-y),b=this._zoomLevel-x,P=h.bounds.width/2*2**b,v=O(a,c,m,t,this._zoomLevel),M=W(c,m,t,this._zoomLevel,l);this._current.shaping.isMultiline?0===t&&this._placeStraight(e,x,v,M):s&&b<0?this._placeStraightAlong(e,d,v,M):"parallel"===o?this._placeStraightAlong(e,x,v,M):"curved"===o&&this._placeCurved(e,x,P,v,M)}_placeStraight(e,t,i,r){const{out:s,id:o,shaping:n,referenceBounds:a}=this._current,{x:l,y:c}=e;s.metricStart(this._createLineLabelMetric(o,i,r,l,c)),s.metricBoxWrite(n.boundsT);const h=e.angle*(180/Math.PI)%360,m=(e.angle*(180/Math.PI)+180)%360;if(!this._verticalPlacement||this._verticalPlacement===this._current.offsetDirection){const e={clipAngle:h,mapAligned:!0,isLineLabel:!0,minZoom:t};this._writeGlyphs(s,o,l,c,n,0,a,e,!1)}if(!this._verticalPlacement||this._verticalPlacement!==this._current.offsetDirection){const e={clipAngle:m,mapAligned:!0,isLineLabel:!0,minZoom:t};this._writeGlyphs(s,o,l,c,n,0,a,e,!1)}s.metricEnd()}_placeCurved(e,t,i,r,s){const{out:o,id:n}=this._current;o.metricStart(this._createLineLabelMetric(n,r,s,e.x,e.y));const a=e.clone(),l=e.angle*(180/Math.PI)%360,c=(e.angle*(180/Math.PI)+180)%360;this._verticalPlacement&&this._verticalPlacement!==this._current.offsetDirection||(this._placeFirst(a,t,1,l),this._placeBack(e,a,t,i,1,l),this._placeForward(e,a,t,i,1,l)),this._verticalPlacement&&this._verticalPlacement===this._current.offsetDirection||(this._placeFirst(a,t,0,c),this._placeBack(e,a,t,i,0,c),this._placeForward(e,a,t,i,0,c)),o.metricEnd()}_placeStraightAlong(e,i,n,a){const{out:l,id:c,shaping:h,zoomRange:m,referenceBounds:u}=this._current,{boxBorderLineColor:f,boxBackgroundColor:g}=this.evaluatedMeshParams,p=e.clone(),_=e.angle*(180/Math.PI)%360,y=(e.angle*(180/Math.PI)+180)%360,x=h.glyphs.length>0&&!(!f&&!g);if(l.metricStart(this._createLineLabelMetric(c,n,a,e.x,e.y)),x){const n=Math.max(i,m[0],0),a=Math.min(A,m[1]),f=r(o(),-e.angle),g={minZoom:n,maxZoom:a,clipAngle:_,mapAligned:!0,isLineLabel:!0},p=t(this.evaluatedMeshParams.offsetX),x=t(this.evaluatedMeshParams.offsetY);if(!this._verticalPlacement||this._verticalPlacement===this._current.offsetDirection){const t=d(p,-1*x),[i,r]=h.shapeBackground(s(o(),f,t));l.recordStart(this.instanceId,this.attributeLayout,h.glyphs[0].textureBinding),this._writeTextBox(l,c,e.x,e.y,r,u,g),l.recordEnd()}if(!this._verticalPlacement||this._verticalPlacement!==this._current.offsetDirection){const t=d(p,x),[i,r]=h.shapeBackground(s(o(),f,t));g.clipAngle=y,l.recordStart(this.instanceId,this.attributeLayout,h.glyphs[0].textureBinding),this._writeTextBox(l,c,e.x,e.y,r,u,g),l.recordEnd()}}this._verticalPlacement&&this._verticalPlacement!==this._current.offsetDirection||this._placeFirst(p,i,1,_,!0),this._verticalPlacement&&this._verticalPlacement===this._current.offsetDirection||this._placeFirst(p,i,0,y,!0),l.metricEnd()}_placeBack(e,t,i,r,s,o){const n=e.clone();let a=e.backwardLength+Z;for(;n.prev()&&!(a>=r);)this._placeOnSegment(n,t,a,i,-1,s,o),a+=n.length+Z}_placeForward(e,t,i,r,s,o){const n=e.clone();let a=e.remainingLength+Z;for(;n.next()&&!(a>=r);)this._placeOnSegment(n,t,a,i,1,s,o),a+=n.length+Z}_placeFirst(e,i,s,a,l=!1){const{out:c,id:h,shaping:m,zoomRange:u,referenceBounds:f}=this._current,g=m.glyphs,p=t(this.evaluatedMeshParams.offsetX),_=t(this.evaluatedMeshParams.offsetY),y=d(p,_),x=r(o(),-e.angle);n(y,y,x);for(const t of g){const r=t.x>m.bounds.x?s:1-s,o=r*e.remainingLength+(1-r)*e.backwardLength,n=Math.abs(t.x+t.width/2-m.bounds.x),d=Math.max(0,this._zoomLevel+Math.log2(n/(o+Z))),g=Math.max(i,l?0:d);if(t.maxZoom=Math.min(u[1],A),t.angle=e.angle+(1-s)*Math.PI,t.minZoom=Math.max(u[0],g),this._writeLineGlyph(c,h,e.x,e.y,t,a,f,!0),(s||this._current.offsetDirection)&&this._isVisible(t.minZoom,t.maxZoom)){const e=new I(t.bounds.x+y[0],t.bounds.y+y[1],t.bounds.width,t.bounds.height);c.metricBoxWrite(e)}}}_placeOnSegment(e,i,s,a,l,c,h){const{out:m,id:u,shaping:f,referenceBounds:g}=this._current,p=f.glyphs,_=e.dx/e.length,y=e.dy/e.length,x={x:e.x+s*-l*_,y:e.y+s*-l*y},b=t(this.evaluatedMeshParams.offsetX),P=t(this.evaluatedMeshParams.offsetY),v=d(b,P),M=r(o(),-e.angle);n(v,v,M);for(const t of p){const i=t.x>f.bounds.x?c:1-c;if(!(i&&1===l||!i&&-1===l))continue;const r=Math.abs(t.x+t.width/2-f.bounds.x),o=Math.max(0,this._zoomLevel+Math.log2(r/s)-.1),n=Math.max(a,this._zoomLevel+Math.log2(r/(s+e.length+Z)));if(0!==o&&(t.angle=e.angle+(1-c)*Math.PI,t.minZoom=n,t.maxZoom=o,this._writeLineGlyph(m,u,x.x,x.y,t,h,g,!0),(c||this._current.offsetDirection)&&this._isVisible(t.minZoom,t.maxZoom))){const e=new I(t.bounds.x+v[0],t.bounds.y+v[1],t.bounds.width,t.bounds.height);m.metricBoxWrite(e)}}}_writeLineGlyph(e,t,i,r,s,o,n,a){if(i<0||i>w||r<0||r>w)return;e.recordStart(this.instanceId,this.attributeLayout,s.textureBinding);const{texcoords:l,offsets:c}=s,{fontSize:h,haloSize:m,outlineSize:d}=this._textMeshTransformProps,{sdfSize:u,sdfRadius:f}=this.evaluatedMeshParams.glyphs;this._writeQuad(e,t,i,r,{sdfSize:u,sdfRadius:f,texcoords:l,offsets:c,fontSize:h,haloSize:m,outlineSize:d,color:j(this.evaluatedMeshParams.color),isBackground:!1,referenceBounds:n,minZoom:Math.max(this._current.zoomRange[0],s.minZoom),maxZoom:Math.min(this._current.zoomRange[1],s.maxZoom),clipAngle:o,mapAligned:a,isLineLabel:!0}),e.recordEnd()}_packedZoom(e){return Math.floor(e*L)/L}_isVisible(e,t){let i=Math.max(this._current.zoomRange[0],e),r=Math.min(this._current.zoomRange[1],t);i=this._packedZoom(i),r=this._packedZoom(r);const s=this._packedZoom(this._zoomLevel);return i<=s&&s<=r}}function U(e,t){const i=new v,{coords:r,lengths:s}=e,o=u(),n=u(),d=u(),f=u(),g=u(),p=u(),_=2;let y=0;for(let u=0;u<s.length;u++){const e=s[u];for(let s=0;s<e;s++){const u=_*(s+y-1),x=_*(s+y),b=_*(s+y+1);s>0?a(o,r[u],r[u+1]):a(o,0,0),a(n,r[x],r[x+1]),s<e-1?a(d,r[b],r[b+1]):a(d,0,0),0===s?a(f,0,0):(l(f,n,o),c(f,f),a(f,f[1],-f[0])),s===e-1?a(g,0,0):(l(g,d,n),c(g,g),a(g,g[1],-g[0])),h(p,f,g),c(p,p);const P=p[0]*g[0]+p[1]*g[1];0!==P&&m(p,p,P),m(p,p,t),i.coords.push(n[0]+p[0],n[1]+p[1])}i.lengths.push(e),y+=e}return i}export{Y as LabelMeshWriter,E as labelIdHash,T as labelMetricHash,O as lineLabelIdHash,W as lineLabelMetricHash};