@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
3 lines (2 loc) • 18.3 kB
JavaScript
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.19/LICENSE.txt */
import{__decorate as e}from"tslib";import t from"../../Graphic.js";import{isSome as i}from"../../core/arrayUtils.js";import s from"../../core/Error.js";import{EventedAccessor as r}from"../../core/Evented.js";import o from"../../core/Logger.js";import{debounce as a,isAbortError as n}from"../../core/promiseUtils.js";import l from"../../core/ReactiveSet.js";import{watch as u,when as h,sync as c}from"../../core/reactiveUtils.js";import{property as d,subclass as p}from"../../core/accessorSupport/decorators.js";import g from"../../geometry/Point.js";import{initializeProjection as f,project as m}from"../../geometry/projectionUtils.js";import y from"../../geometry/SpatialReference.js";import{getPointFromGeometry as w}from"../../geometry/support/geometryUtils.js";import{geographicToWebMercator as F}from"../../geometry/support/webMercatorUtils.js";import{fallbackObjectIDAttribute as _}from"../../layers/LayerConstants.js";import v from"../../symbols/SimpleFillSymbol.js";import{getDisplayedSymbol as C}from"../../symbols/support/symbolUtils.js";import{system as b}from"../../time/constants.js";import{highlightsSupported as P}from"../../views/support/layerViewUtils.js";import L from"../Feature/FeatureViewModel.js";import{ActionsCollection as M,zoomToFeature as A,zoomToClusteredFeatures as E,browseClusteredFeatures as V}from"../Popup/actions.js";import{triggerAction as j,isClusterFeature as I,getSelectedTarget as R,removeClusteredFeaturesForBrowsing as x,displayClusterExtent as O,browseAggregateFeatures as T}from"../Popup/actionUtils.js";import{AnchorElementViewModelMixin as U}from"../support/AnchorElementViewModel.js";import{GoTo as S}from"../support/GoTo.js";const B="location-scale-handle",H=()=>[A.clone()],G=()=>[E.clone(),V.clone()];let Z=null;function z(e,t){return"building-scene"===e||"map-image"===e||"tile"===e||"imagery"===e||"2d"===t&&"imagery-tile"===e}let D=class extends(S(U(r))){constructor(e){super(e),this._pendingPromises=new l,this._fetchFeaturesController=null,this._centerAtLocationController=null,this._locationUpdateController=null,this._highlightPromises={"highlight-active-feature":null,"highlight-selected-feature":null},this._selectedClusterFeature=null,this.actions=new M,this.activeFeature=null,this.autoCloseEnabled=!1,this.browseClusterEnabled=!1,this.content=null,this.defaultPopupTemplateEnabled=!1,this.featurePage=null,this.featuresPerPage=20,this.featureMenuOpen=!1,this.featureMenuTitle=null,this.featureViewModelAbilities=null,this.featureViewModels=[],this.highlightEnabled=!0,this.includeDefaultActions=!0,this.initialDisplayMode="feature",this.selectedClusterBoundaryFeature=new t({symbol:new v({outline:{width:1.5,color:"cyan"},style:"none"})}),this.title=null,this.updateLocationEnabled=!1,this.view=null,this.visible=!1,this.zoomFactor=4,this.zoomToLocation=null,this._debouncedLocationUpdate=a(async e=>{this._cancelLocationUpdate();const t=new AbortController,{signal:i}=t;this._locationUpdateController=t,await this._locationUpdate(e,{signal:i}).catch(()=>{}).finally(()=>{this._locationUpdateController===t&&(this._locationUpdateController=null)})})}initialize(){this.addHandles([this.on("view-change",()=>this._autoClose()),u(()=>[this.highlightEnabled,this.selectedFeature,this.visible,this.view],()=>this._highlightSelectedFeature()),u(()=>[this.highlightEnabled,this.activeFeature,this.visible,this.view],()=>this._highlightActiveFeature()),u(()=>this.view?.animation?.state,e=>this._animationStateChange(e)),u(()=>this.location,e=>this._locationChange(e)),u(()=>this.selectedFeature,e=>this._selectedFeatureChange(e)),u(()=>[this.selectedFeatureIndex,this.featureCount,this.featuresPerPage],()=>this._selectedFeatureIndexChange()),u(()=>[this.featurePage,this.selectedFeatureIndex,this.featureCount,this.featuresPerPage,this.featureViewModels],()=>this._setGraphicOnFeatureViewModels()),u(()=>this.featureViewModels,()=>this._featureViewModelsChange()),this.on("trigger-action",e=>j({event:e,viewModel:this,view:this.view})),h(()=>!this.waitingForResult,()=>this._waitingForResultChange(),c),u(()=>[this.features,this.map,this.spatialReference,this.timeZone],()=>this._updateFeatureVMs()),u(()=>this.view?.scale,()=>this._viewScaleChange()),h(()=>!this.visible,()=>this.browseClusterEnabled=!1),u(()=>this.browseClusterEnabled,e=>e?this.enableClusterBrowsing():this.disableClusterBrowsing())])}destroy(){this._cancelLocationUpdate(),this._cancelFetchingFeatures(),this._cancelCenterAtLocation(),this._pendingPromises.clear(),this.browseClusterEnabled=!1,this.view=null,this.map=null,this.spatialReference=null,this.timeZone=null}get active(){return!(!this.visible||this.waitingForResult)}get allActions(){const e=this._get("allActions")||new M;e.removeAll();const{actions:t,defaultActions:i,defaultPopupTemplateEnabled:s,includeDefaultActions:r,selectedFeature:o}=this,a=r?i.concat(t):t,n=o&&("function"==typeof o.getEffectivePopupTemplate&&o.getEffectivePopupTemplate(s)||o.popupTemplate),l=n?.actions,u=n?.overwriteActions?l:l?.concat(a)??a;return u?.filter(Boolean).forEach(t=>e.add(t)),e}get defaultActions(){const e=this._get("defaultActions")||new M;return e.removeAll(),e.addMany(I(this.selectedFeature)?G():H()),e}get featureCount(){return this.features.length}set features(e){if(this._get("features")===e)return;const t=e||[];this._set("features",t);const{pendingPromisesCount:i,promiseCount:s,selectedFeatureIndex:r}=this,o=s&&t.length;"list"!==this.initialDisplayMode?o&&i&&-1===r?this.selectedFeatureIndex=0:o&&-1!==r||(this.selectedFeatureIndex=t.length?0:-1):(!o||o&&i===s)&&(this.selectedFeatureIndex=-1)}set location(e){let t=e;const i=this.spatialReference?.isWebMercator,s=e?.spatialReference?.isWGS84;s&&i&&(t=F(e)),this._set("location",t)}get map(){return this.view?.map??null}set map(e){this._override("map",e)}get pendingPromisesCount(){return this._pendingPromises.size}get promiseCount(){return this.promises.length}get promises(){return this._get("promises")||[]}set promises(e){this._get("promises")!==e&&(this._pendingPromises.clear(),this.features=[],Array.isArray(e)&&e.length?(this._set("promises",e),(e=e.slice()).forEach(e=>this._pendingPromises.add(e)),e.reduce((e,t)=>e.finally(()=>t.then(e=>{this._pendingPromises.has(t)&&this._updateFeatures(e)}).finally(()=>this._pendingPromises.delete(t)).catch(()=>{})),Promise.resolve())):this._set("promises",[]))}get screenLocation(){return super.screenLocation}get selectedFeature(){const{features:e,selectedFeatureIndex:t}=this;if(-1===t)return null;return e[t]||null}get selectedFeatureIndex(){const e=this._get("selectedFeatureIndex");return"number"==typeof e?e:-1}set selectedFeatureIndex(e){const{featureCount:t}=this;(isNaN(e)||e<0||!t)&&(e=-1),this.activeFeature=null,this._set("selectedFeatureIndex",e)}get selectedFeatureViewModel(){return this.featureViewModels[this.selectedFeatureIndex]||null}get spatialReference(){return this.view?.spatialReference??null}set spatialReference(e){this._override("spatialReference",e)}get state(){const{view:e,map:t}=this;return e?e.ready?"ready":"disabled":t?"ready":"disabled"}get timeZone(){return this.view?.timeZone??b}set timeZone(e){this._overrideIfSome("timeZone",e)}get waitingForContents(){return this.featureViewModels.some(e=>e.waitingForContent)}get waitingForResult(){return!(!(!!this._fetchFeaturesController||this.pendingPromisesCount>0)||0!==this.featureCount)}centerAtLocation(){const{view:e}=this,t=R(this);this._cancelCenterAtLocation();const i=new AbortController,{signal:r}=i;return this._centerAtLocationController=i,t&&e?this.callGoTo({target:{target:t,scale:e.scale},options:{signal:r}}).catch(e=>{n(e)||o.getLogger(this).error(e)}):Promise.reject(new s("center-at-location:invalid-target-or-view","Cannot center at a location without a target and view.",{target:t,view:e}))}zoomTo(e){return this.callGoTo(e)}clear(){this.set({promises:[],features:[],content:null,title:null,location:null,activeFeature:null})}fetchFeatures(e,t){const{view:i}=this;if(!i||!e)throw new s("fetch-features:invalid-screenpoint-or-view","Cannot fetch features without a screenPoint and view.",{screenPoint:e,view:i});return i.fetchPopupFeatures(e,{pointerType:t?.event?.pointerType,defaultPopupTemplateEnabled:this.defaultPopupTemplateEnabled,signal:t?.signal})}open(e){const t={updateLocationEnabled:!1,promises:[],fetchFeatures:!1,...e,visible:!0},{fetchFeatures:i}=t;delete t.fetchFeatures,i&&this._setFetchFeaturesPromises(t.location);const s=["actionsMenuOpen","collapsed"];for(const r of s)delete t[r];this.set(t)}triggerAction(e){const t=this.allActions.at(e);t&&!t.disabled&&this.emit("trigger-action",{action:t})}next(){return this.selectedFeatureIndex=this._getRoundRobinIndex(this.selectedFeatureIndex+1,this.featureCount),this}previous(){return this.selectedFeatureIndex=this._getRoundRobinIndex(this.selectedFeatureIndex-1,this.featureCount),this}disableClusterBrowsing(){x(this),this._clearBrowsedClusterGraphics()}async enableClusterBrowsing(){const{view:e,selectedFeature:t}=this;"2d"===e?.type?I(t)?(await O(this),await T(this)):o.getLogger(this).warn("enableClusterBrowsing:invalid-selectedFeature: Selected feature must represent an aggregate/cluster graphic.",t):o.getLogger(this).warn("enableClusterBrowsing:invalid-view: View must be 2d MapView.",t)}async handleViewClick(e){return this._fetchFeaturesAndOpen(e)}_getRoundRobinIndex(e,t){return(e+t)%t}_animationStateChange(e){this.zoomToLocation||(A.disabled="waiting-for-target"===e)}_clearBrowsedClusterGraphics(){const e=[this.selectedClusterBoundaryFeature,this._selectedClusterFeature].filter(i);this.view?.graphics?.removeMany(e),this._selectedClusterFeature=null,this.selectedClusterBoundaryFeature.geometry=null}_viewScaleChange(){if(I(this.selectedFeature))return this.browseClusterEnabled=!1,this.visible=!1,void this.clear();this.browseClusterEnabled&&(this.features=this.selectedFeature?[this.selectedFeature]:[])}_locationChange(e){const{selectedFeature:t,updateLocationEnabled:i,view:s}=this;s&&!this._locationUpdateController&&i&&e&&(!t||t.geometry)&&this.centerAtLocation()}_selectedFeatureIndexChange(){this.featurePage=this.featureCount>0?Math.floor(this.selectedFeatureIndex/this.featuresPerPage)+1:null}_featureViewModelsChange(){this.featurePage=this.featureCount>0?1:null}_setGraphicOnFeatureViewModels(){const{features:e,featureCount:t,featurePage:i,featuresPerPage:s,featureViewModels:r}=this;if(null==i)return;const o=((i-1)*s+t)%t,a=o+s;r.slice(o,a).forEach((t,i)=>{t&&(t.graphic??=e[o+i])})}async _selectedFeatureChange(e){const{location:t,updateLocationEnabled:i,view:s}=this;if(!e||!s)return;if(this.browseClusterEnabled){if(this._selectedClusterFeature&&(s.graphics.remove(this._selectedClusterFeature),this._selectedClusterFeature=null),I(e))return;return e.symbol=await C(e),this._selectedClusterFeature=e,void s.graphics.add(this._selectedClusterFeature)}const r=e.sourceLayer?.type;if("map-image"!==r&&"imagery"!==r&&"imagery-tile"!==r||(e.symbol=await C(e)),!i&&t||!e.geometry){if(i&&!e.geometry){await this.centerAtLocation();const e=s.center?.clone();e&&(this.location=e)}}else this.location=w(e.geometry)}_waitingForResultChange(){!this.featureCount&&this.promises&&(this.visible=!1)}async _setFetchFeaturesPromises(e){const{pendingFeatures:t}=await this._fetchFeaturesWithController({mapPoint:e});this.promises=t}_destroyFeatureVMs(){this.featureViewModels.forEach(e=>e&&!e.destroyed&&e.destroy()),this._set("featureViewModels",[])}_updateFeatureVMs(){const{selectedFeature:e,features:t,featureViewModels:i,view:s,spatialReference:r,map:o,timeZone:a,location:n}=this;if(I(t[0])||(this.browseClusterEnabled=!1),this._destroyFeatureVMs(),!t?.length)return;const l=i.slice(),u=[];t.forEach((t,i)=>{if(!t)return;let h=null;if(l.some((e,i)=>(e&&e.graphic===t&&(h=e,l.splice(i,1)),!!h)),h)u[i]=h;else{const l=new L({abilities:this.featureViewModelAbilities,defaultPopupTemplateEnabled:this.defaultPopupTemplateEnabled,spatialReference:r,graphic:t===e?t:null,location:n,map:o,view:s,timeZone:a});u[i]=l}}),l.forEach(e=>e&&!e.destroyed&&e.destroy()),this._set("featureViewModels",u)}async _getScreenPoint(e,t){const{spatialReference:i,view:s}=this;if(!s)return null;await(s?.when());const r=e?.spatialReference;return r&&i?(await f(r,i,null,t),s.toScreen(e)):null}_cancelCenterAtLocation(){this._centerAtLocationController?.abort(),this._centerAtLocationController=null}_cancelFetchingFeatures(){this._fetchFeaturesController?.abort(),this._fetchFeaturesController=null}async _projectScreenPointAndFetchFeatures({mapPoint:e,screenPoint:t,event:i,signal:s}){return this.fetchFeatures(t??await this._getScreenPoint(e??this.location,{signal:s}),{signal:s,event:i})}_fetchFeaturesWithController({mapPoint:e,screenPoint:t,event:i}){this._cancelFetchingFeatures();const s=new AbortController,{signal:r}=s;this._fetchFeaturesController=s;const o=this._projectScreenPointAndFetchFeatures({mapPoint:e,screenPoint:t,signal:r,event:i});return o.catch(()=>{}).finally(()=>{this._fetchFeaturesController===s&&(this._fetchFeaturesController=null)}),o}async _fetchFeaturesAndOpen(e){const{mapPoint:t,screenPoint:i}=e;this.removeHandles(B),this.addHandles([u(()=>this.view?.scale,()=>this._debouncedLocationUpdate(t).catch(e=>{n(e)||o.getLogger(this).error(e)})),h(()=>!this.visible,()=>{this.removeHandles(B)},{once:!0})],B);const{pendingFeatures:s}=await this._fetchFeaturesWithController({mapPoint:t,screenPoint:i,event:e});return{location:t??void 0,promises:s}}async _locationUpdate(e,t){const{spatialReference:i}=this,s=this.selectedFeature?.geometry?.type,r=this.location??e;if(s&&"mesh"!==s&&i&&r&&this.selectedFeature)if("point"!==s)try{const e=this.selectedFeature;let s=e.geometry;const o=this._getHighlightLayer(e),a=e.getObjectId();if(!o||!this.view)return;if(a){const e=this.view?.allLayerViews.find(e=>e.layer.uid===o.uid);if(!e||!("queryFeatures"in e))return;const r=e.createQuery();r.outSpatialReference=i,r.objectIds=[a],r.returnGeometry=!0;const{features:n}=await e.queryFeatures(r,t);s=n[0]?.geometry}if(!s||"mesh"===s.type)return;s=m(s,i),Z||(Z=await Promise.all([import("../../geometry/operators/intersectsOperator.js"),import("../../geometry/operators/proximityOperator.js")]));const[n,l]=Z;if(!n.execute(s,r)){const t=l.getNearestCoordinate(s,r).coordinate??r;this.selectedFeature===e&&(this.location=t)}}catch(a){n(a)||o.getLogger(this).error(a)}else this.location=w(this.selectedFeature.geometry)??r}_cancelLocationUpdate(){this._locationUpdateController?.abort(),this._locationUpdateController=null}_autoClose(){this.autoCloseEnabled&&(this.visible=!1)}async _getLayerView(e,t){return await e.when(),e.whenLayerView(t)}_getHighlightLayer(e){const{layer:t,sourceLayer:i}=e;return i&&"layer"in i&&i.layer?i.layer:"map-notes"===i?.type||"subtype-group"===i?.type?i:t??i}_getHighlightLayerView(e,t){return"subtype-sublayer"===t.type&&t.parent&&this._mapIncludesLayer(t.parent)?this._getLayerView(e,t.parent):t&&this._mapIncludesLayer(t)?this._getLayerView(e,t):null}_getHighlightTarget(e,t,i){if(z(t.type,i))return e;const s=e.getObjectId();if(null!=s)return s;const r="imagery"===t.type?void 0:"objectIdField"in t?t.objectIdField||_:null,o=e.attributes;return o&&r&&o[r]||e}_mapIncludesLayer(e){return!!this.map?.allLayers?.includes(e)}async _highlightFeature(e,t){this.removeHandles(e);const i=this[t];if(!i)return;const{highlightEnabled:s,view:r,visible:o}=this;if(!r||!s||!o)return;const a=this._getHighlightLayer(i);if(!a)return;const n=this._getHighlightLayerView(r,a);if(!n)return;this._highlightPromises[e]=n;const l=await n;if(!(l&&P(l)&&this._highlightPromises[e]===n&&this[t]&&this.highlightEnabled))return;const u=l.highlight(this._getHighlightTarget(i,a,r.type));this.addHandles(u,e)}async _highlightActiveFeature(){return this._highlightFeature("highlight-active-feature","activeFeature")}async _highlightSelectedFeature(){return this._highlightFeature("highlight-selected-feature","selectedFeature")}_updateFeatures(e){const{features:t}=this,i=e.filter(e=>!t.includes(e));i?.length&&(this.features=t.concat(i))}};e([d()],D.prototype,"_fetchFeaturesController",void 0),e([d({type:M})],D.prototype,"actions",void 0),e([d({readOnly:!0})],D.prototype,"active",null),e([d()],D.prototype,"activeFeature",void 0),e([d({readOnly:!0})],D.prototype,"allActions",null),e([d()],D.prototype,"autoCloseEnabled",void 0),e([d()],D.prototype,"browseClusterEnabled",void 0),e([d()],D.prototype,"content",void 0),e([d({type:M,readOnly:!0})],D.prototype,"defaultActions",null),e([d({type:Boolean})],D.prototype,"defaultPopupTemplateEnabled",void 0),e([d({readOnly:!0})],D.prototype,"featureCount",null),e([d()],D.prototype,"featurePage",void 0),e([d({value:[]})],D.prototype,"features",null),e([d()],D.prototype,"featuresPerPage",void 0),e([d()],D.prototype,"featureMenuOpen",void 0),e([d()],D.prototype,"featureMenuTitle",void 0),e([d()],D.prototype,"featureViewModelAbilities",void 0),e([d({readOnly:!0})],D.prototype,"featureViewModels",void 0),e([d()],D.prototype,"highlightEnabled",void 0),e([d()],D.prototype,"includeDefaultActions",void 0),e([d()],D.prototype,"initialDisplayMode",void 0),e([d({type:g})],D.prototype,"location",null),e([d()],D.prototype,"map",null),e([d({readOnly:!0})],D.prototype,"pendingPromisesCount",null),e([d({readOnly:!0})],D.prototype,"promiseCount",null),e([d()],D.prototype,"promises",null),e([d({readOnly:!0})],D.prototype,"selectedClusterBoundaryFeature",void 0),e([d({value:null,readOnly:!0})],D.prototype,"selectedFeature",null),e([d({value:-1})],D.prototype,"selectedFeatureIndex",null),e([d({readOnly:!0})],D.prototype,"selectedFeatureViewModel",null),e([d({type:y})],D.prototype,"spatialReference",null),e([d({readOnly:!0})],D.prototype,"state",null),e([d()],D.prototype,"timeZone",null),e([d()],D.prototype,"title",void 0),e([d()],D.prototype,"updateLocationEnabled",void 0),e([d()],D.prototype,"view",void 0),e([d()],D.prototype,"visible",void 0),e([d({readOnly:!0})],D.prototype,"waitingForContents",null),e([d({readOnly:!0})],D.prototype,"waitingForResult",null),e([d()],D.prototype,"zoomFactor",void 0),e([d()],D.prototype,"zoomToLocation",void 0),e([d()],D.prototype,"centerAtLocation",null),D=e([p("esri.widgets.Features.FeaturesViewModel")],D);export{D as default};