CIMSymbolRasterizer.js 8.7 KB

12345
  1. /*
  2. All material copyright ESRI, All Rights Reserved, unless otherwise specified.
  3. See https://js.arcgis.com/4.25/esri/copyright.txt for details.
  4. */
  5. import e from"../../Color.js";import t from"../../request.js";import{unwrapOrThrow as a}from"../../core/maybe.js";import{throwIfAborted as i}from"../../core/promiseUtils.js";import{pt2px as r}from"../../core/screenUtils.js";import{analyzeCIMSymbol as s,analyzeCIMResource as o}from"./cimAnalyzer.js";import n from"./CIMResourceManager.js";import{Transformation as c,CanvasDrawHelper as h}from"./CIMSymbolDrawHelper.js";import{OverrideHelper as l,CIMSymbolHelper as m}from"./CIMSymbolHelper.js";import g from"./Rasterizer.js";import u from"./TextRasterizer.js";import{mapCIMSymbolToGeometryType as f,createLabelOverrideFunction as d,evaluateValueOrFunction as p,colorToArray as y}from"./utils.js";import{scaleCIMMarker as w}from"../support/cimSymbolUtils.js";import{Symbol3DAnchorPosition2D as C}from"../support/Symbol3DAnchorPosition2D.js";var M;!function(e){e.Legend="legend",e.Preview="preview"}(M||(M={}));const _=e=>e&&e.scaleFactor?e.scaleFactor:1,v=96/72;class z{constructor(e,t){this._spatialReference=e,this._avoidSDF=t,this._resourceCache=new Map,this._imageDataCanvas=null,this._pictureMarkerCache=new Map,this._textRasterizer=new u,this._cimResourceManager=new n,this._rasterizer=new g(this._cimResourceManager)}get resourceManager(){return this._cimResourceManager}async rasterizeCIMSymbolAsync(e,t,a,i,r,s,o,n){if(!e)return null;const{data:g}=e;if(!g||"CIMSymbolReference"!==g.type||!g.symbol)return null;const{symbol:u}=g;s||(s=f(u));const d=await l.resolveSymbolOverrides(g,t,this._spatialReference,r,s,o,n);this._imageDataCanvas||(this._imageDataCanvas=document.createElement("canvas"));const p=this._imageDataCanvas,y=this._cimResourceManager,w=[];m.fetchResources(d,y,w),w.length>0&&await Promise.all(w);const{width:C,height:M}=a,_=I(s,C,M,i),z=m.getEnvelope(d,_,y);if(!z)return null;const x=(window.devicePixelRatio||1)*v;let R=1,b=0,P=0;switch(u.type){case"CIMPointSymbol":case"CIMTextSymbol":{let e=1;z.width>C&&(e=C/z.width);let t=1;z.height>M&&(t=M/z.height),"preview"===i&&(z.width<C&&(e=C/z.width),z.height<M&&(t=M/z.height)),R=Math.min(e,t),b=z.x+z.width/2,P=z.y+z.height/2}break;case"CIMLineSymbol":{let e=1;z.height>M&&(e=M/z.height),R=e,P=z.y+z.height/2;const t=z.x*R+C/2,a=(z.x+z.width)*R+C/2;if(t<0){const{paths:e}=_;e[0][0][0]-=t}if(a>C){const{paths:e}=_;e[0][2][0]-=a-C}}break;case"CIMPolygonSymbol":{b=z.x+z.width/2,P=z.y+z.height/2;const e=z.x*R+C/2,t=(z.x+z.width)*R+C/2,a=z.y*R+M/2,i=(z.y+z.height)*R+M/2,{rings:r}=_;e<0&&(r[0][0][0]-=e,r[0][3][0]-=e,r[0][4][0]-=e),a<0&&(r[0][0][1]+=a,r[0][1][1]+=a,r[0][4][1]+=a),t>C&&(r[0][1][0]-=t-C,r[0][2][0]-=t-C),i>M&&(r[0][2][1]+=i-M,r[0][3][1]+=i-M)}}p.width=C*x,p.height=M*x;const D=1;p.width+=2*D,p.height+=2*D;const S=p.getContext("2d"),k=c.createIdentity();k.translate(-b,-P),k.scale(R*x,-R*x),k.translate(C*x/2+D,M*x/2+D),S.clearRect(0,0,p.width,p.height);return new h(S,y,k,!0).drawSymbol(d,_),S.getImageData(0,0,p.width,p.height)}async analyzeCIMSymbol(e,t,a,r,o){const n=[],c=t?{geometryType:r,spatialReference:this._spatialReference,fields:t}:null;let h;await s(e.data,c,this._cimResourceManager,n,this._avoidSDF),i(o);for(const i of n)"CIMPictureMarker"!==i.cim.type&&"CIMPictureFill"!==i.cim.type&&"CIMPictureStroke"!==i.cim.type||(h||(h=[]),h.push(this._fetchPictureMarkerResource(i,o))),a&&"text"===i.type&&"string"==typeof i.text&&i.text.includes("[")&&(i.text=d(a,i.text,i.cim.textCase));return h&&await Promise.all(h),n}rasterizeCIMSymbol3D(e,t,a,i,r,s){const o=[];for(const n of e){i&&"function"==typeof i.scaleFactor&&(i.scaleFactor=i.scaleFactor(t,r,s));const e=this._getRasterizedResource(n,t,a,i,r,s);if(!e)continue;let c=0,h=e.anchorX||0,l=e.anchorY||0,m=!1,g=0,u=0;if("esriGeometryPoint"===a){const e=_(i);if(g=p(n.offsetX,t,r,s)*e||0,u=p(n.offsetY,t,r,s)*e||0,"marker"===n.type)c=p(n.rotation,t,r,s)||0,m=!!n.rotateClockwise&&n.rotateClockwise;else if("text"===n.type){if(c=p(n.angle,t,r,s)||0,void 0!==n.horizontalAlignment)switch(n.horizontalAlignment){case"left":h=-.5;break;case"right":h=.5;break;default:h=0}if(void 0!==n.verticalAlignment)switch(n.verticalAlignment){case"top":l=.5;break;case"bottom":l=-.5;break;case"baseline":l=-.25;break;default:l=0}}}null!=e&&o.push({angle:c,rotateClockWise:m,anchorX:h,anchorY:l,offsetX:g,offsetY:u,rasterizedResource:e})}return this.getSymbolImage(o)}getSymbolImage(e){const t=document.createElement("canvas"),i=a(t.getContext("2d"));let s=0,o=0,n=0,c=0;const h=[];for(let a=0;a<e.length;a++){const t=e[a],l=t.rasterizedResource;if(!l)continue;const m=l.size,g=t.offsetX,u=t.offsetY,f=t.anchorX,d=t.anchorY,p=t.rotateClockWise||!1;let y=t.angle,w=r(g)-m[0]*(.5+f),C=r(u)-m[1]*(.5+d),M=w+m[0],_=C+m[1];if(y){p&&(y=-y);const e=Math.sin(y*Math.PI/180),t=Math.cos(y*Math.PI/180),a=w*t-C*e,i=w*e+C*t,r=w*t-_*e,s=w*e+_*t,o=M*t-_*e,n=M*e+_*t,c=M*t-C*e,h=M*e+C*t;w=Math.min(a,r,o,c),C=Math.min(i,s,n,h),M=Math.max(a,r,o,c),_=Math.max(i,s,n,h)}s=w<s?w:s,o=C<o?C:o,n=M>n?M:n,c=_>c?_:c;const v=i.createImageData(l.size[0],l.size[1]);v.data.set(new Uint8ClampedArray(l.image.buffer));const z={offsetX:g,offsetY:u,rotateClockwise:p,angle:y,rasterizedImage:v,anchorX:f,anchorY:d};h.push(z)}t.width=n-s,t.height=c-o;const l=-s,m=c;for(let a=0;a<h.length;a++){const e=h[a],t=this._imageDataToCanvas(e.rasterizedImage),s=e.rasterizedImage.width,o=e.rasterizedImage.height,n=l-s*(.5+e.anchorX),c=m-o*(.5-e.anchorY);if(e.angle){const a=(360-e.angle)*Math.PI/180;i.save(),i.translate(r(e.offsetX),-r(e.offsetY)),i.translate(l,m),i.rotate(a),i.translate(-l,-m),i.drawImage(t,n,c),i.restore()}else i.drawImage(t,n+r(e.offsetX),c-r(e.offsetY))}const g=new C({x:l/t.width-.5,y:m/t.height-.5});return{imageData:0!==t.width&&0!==t.height?i.getImageData(0,0,t.width,t.height):i.createImageData(1,1),anchorPosition:g}}async _fetchPictureMarkerResource(e,a){const i=e.materialHash;if(!this._pictureMarkerCache.get(i)){const r=(await t(e.cim.url,{responseType:"image",signal:a&&a.signal})).data;this._pictureMarkerCache.set(i,r)}}_imageDataToCanvas(e){this._imageDataCanvas||(this._imageDataCanvas=document.createElement("canvas"));const t=this._imageDataCanvas,i=a(t.getContext("2d"));return t.width=e.width,t.height=e.height,i.putImageData(e,0,0),t}_imageTo32Array(t,i,r,s){this._imageDataCanvas||(this._imageDataCanvas=document.createElement("canvas"));const o=this._imageDataCanvas,n=a(o.getContext("2d"));if(o.width=i,o.height=r,n.drawImage(t,0,0,i,r),s){n.save();const a=new e(s);n.fillStyle=a.toHex(),n.globalCompositeOperation="multiply",n.fillRect(0,0,i,r),n.globalCompositeOperation="destination-atop",n.drawImage(t,0,0,i,r),n.restore()}return new Uint32Array(n.getImageData(0,0,i,r).data.buffer)}_getRasterizedResource(e,t,i,r,s,o){let n,c,h;const l=null,m=null;if("text"===e.type)return this._rasterizeTextResource(e,t,r,s,o);({analyzedCIM:n,hash:c}=x(e,t,s,o));const g=_(r);if("CIMPictureMarker"===e.cim.type){const i=p(e.size,t,s,o)*g,{image:r,width:n,height:c}=a(this._getPictureResource(e,i,p(e.color,t,s,o)));return h={image:r,size:[n,c],sdf:!1,simplePattern:!1,anchorX:e.anchorPoint?e.anchorPoint.x:0,anchorY:e.anchorPoint?e.anchorPoint.y:0},h}w(n,g,{preserveOutlineWidth:!1});const u=n;c+=i,r&&(c+=JSON.stringify(r));const f=this._resourceCache;return f.has(c)?f.get(c):(h=this._rasterizer.rasterizeJSONResource({cim:u,type:e.type,url:e.url,mosaicHash:c,size:l,path:m},window.devicePixelRatio||1,this._avoidSDF),f.set(c,h),h)}_rasterizeTextResource(e,t,a,i,r){const s=_(a),o=p(e.text,t,i,r);if(!o||0===o.length)return null;const n=p(e.fontName,t,i,r),c=p(e.style,t,i,r),h=p(e.weight,t,i,r),l=p(e.decoration,t,i,r),m=p(e.size,t,i,r)*s,g=p(e.horizontalAlignment,t,i,r),u=p(e.verticalAlignment,t,i,r),f=y(p(e.color,t,i,r)),d=y(p(e.outlineColor,t,i,r)),w={color:f,size:m,horizontalAlignment:g,verticalAlignment:u,font:{family:n,style:c,weight:h,decoration:l},halo:{size:p(e.outlineSize,t,i,r)||0,color:d,style:c},pixelRatio:1,premultiplyColors:!this._avoidSDF};return this._textRasterizer.rasterizeText(o,w)}_getPictureResource(e,t,a){const i=this._pictureMarkerCache.get(e.materialHash);if(!i)return null;const s=i.height/i.width,o=t?s>1?r(t):r(t)/s:i.width,n=t?s>1?r(t)*s:r(t):i.height;return{image:this._imageTo32Array(i,o,n,a),width:o,height:n}}}function I(e,t,a,i){const r=1,s=-t/2+r,o=t/2-r,n=a/2-r,c=-a/2+r;switch(e){case"esriGeometryPoint":return{x:0,y:0};case"esriGeometryPolyline":return{paths:[[[s,0],[0,0],[o,0]]]};default:return"legend"===i?{rings:[[[s,n],[o,0],[o,c],[s,c],[s,n]]]}:{rings:[[[s,n],[o,n],[o,c],[s,c],[s,n]]]}}}function x(e,t,a,i){let r,s;if("function"==typeof e.materialHash){r=(0,e.materialHash)(t,a,i),s=o(e.cim,e.materialOverrides)}else r=e.materialHash,s=e.cim;return{analyzedCIM:s,hash:r}}export{z as CIMSymbolRasterizer,M as GeometryStyle};