@arcgis/map-components
Version:
ArcGIS Map Components
499 lines (498 loc) • 23.7 kB
JavaScript
/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */
import { c as S } from "../../chunks/runtime.js";
import { css as M, html as m } from "lit";
import { keyed as C } from "lit/directives/keyed.js";
import { i as k, q as D } from "../../chunks/layer-utils.js";
import { createReactiveMap as I, findUtilityNetwork as V, getOrCreateMapValue as j } from "@arcgis/core/applications/Components/featureUtils.js";
import { stripHTML as v } from "@arcgis/core/applications/Components/stringUtils.js";
import w from "@arcgis/core/core/Collection.js";
import { ignoreAbortErrors as E } from "@arcgis/core/core/promiseUtils.js";
import { watch as A } from "@arcgis/core/core/reactiveUtils.js";
import * as x from "@arcgis/core/core/sql.js";
import P from "@arcgis/core/rest/networks/support/NetworkElement.js";
import U from "@arcgis/core/rest/networks/support/QueryAssociationsParameters.js";
import { LitElement as Q, renderElement as q } from "@arcgis/lumina";
import { debounce as T } from "@arcgis/toolkit/function";
import G from "@arcgis/core/popup/Feature.js";
const B = M`.list-container{min-height:var(--calcite-spacing-xxxl)}.loading-container{display:flex;justify-content:center;padding:var(--calcite-spacing-md) 0;width:100%}.sticky-loading-container{display:flex;position:sticky;bottom:var(--calcite-spacing-sm);align-items:center;justify-content:center;z-index:2;margin:0;padding:0;height:var(--calcite-spacing-xxxl);pointer-events:none;height:500px}`, L = 100, $ = {
assetGroup: "assetgroup",
assetType: "assettype"
}, N = "{00000000-0000-0000-0000-000000000000}";
class R extends Q {
constructor() {
super(...arguments), this._addFlowItem = (t) => {
t && this.flowItems?.push(t);
}, this._attachmentAssociations = new w(), this._attachmentFeatureCount = 0, this._cancelQuery = () => {
const { _queryAbortController: t } = this;
t && t.abort(), this._queryAbortController = null;
}, this._cancelQueryFeatureCount = () => {
const { _queryFeatureCountAbortController: t } = this;
t && t.abort(), this._queryFeatureCountAbortController = null;
}, this._connectivityAssociations = new w(), this._connectivityFeatureCount = 0, this._containerAssociations = new w(), this._containerFeatureCount = 0, this._contentAssociations = new w(), this._contentFeatureCount = 0, this._networkSourceIdsInUse = /* @__PURE__ */ new Set(), this._query = async (t) => {
if (!t)
return;
await this._loadUtiltyNetworks();
const { _queryAbortController: e } = this;
this._destroyAssociatedFeatureViewModels(), this._clearFeatures(), this._featureCount && await this._queryAssociatedFeatures(t, { signal: e?.signal });
}, this._queryController = async (t) => {
this._cancelQuery();
const e = new AbortController();
this._queryAbortController = e, await E(this._query(t)), this._queryAbortController === e && (this._queryAbortController = null);
}, this._queryFeatureCountController = async () => {
this._loaded = !1, this._cancelQueryFeatureCount();
const t = new AbortController();
this._queryFeatureCountAbortController = t, await E(this._queryFeatureCount()), this._queryFeatureCountAbortController === t && (this._queryFeatureCountAbortController = null), this._loaded = !0;
}, this._queryDebounced = T(this._queryController, L), this._queryFeatureCountDebounced = T(this._queryFeatureCountController, L), this._structureAssociations = new w(), this._structureFeatureCount = 0, this._queryAssociationType = async () => {
this._activeAssociationType && this._queryDebounced(this._activeAssociationType);
}, this._associationFeatures = I(), this._associationViewModels = I(), this._featureCount = 0, this._loaded = !1, this._queryAbortController = null, this._queryFeatureCountAbortController = null, this._queryPageAbortController = null, this.associationTypes = null, this.autoDestroyDisabled = !1, this.useFeatureNextFlowItem = !1, this.flowType = "feature-utility-network-associations", this.headingLevel = 2, this.source = "popup";
}
static {
this.properties = { _activeAssociationType: 16, _associationFeatures: 16, _associationViewModels: 16, _canLoad: 16, _canQuery: 16, _featureCount: 16, _loaded: 16, _queryAbortController: 16, _queryFeatureCountAbortController: 16, _queryPageAbortController: 16, _state: 16, associationTypes: 0, autoDestroyDisabled: 5, description: 1, flowItems: 0, useFeatureNextFlowItem: 5, flowType: 1, graphic: 0, heading: 1, headingLevel: 9, layer: 0, map: 0, messages: 0, parentFeatureViewModel: 0, referenceElement: 1, source: 1, spatialReference: 0, timeZone: 1, view: 0 };
}
static {
this.styles = B;
}
get _globalId() {
return (this._globalIdField && this.graphic?.attributes?.[this._globalIdField]) ?? null;
}
get _globalIdField() {
return this.layer?.globalIdField;
}
get _objectId() {
return (this._objectIdField && this.graphic?.attributes?.[this._objectIdField]) ?? null;
}
get _objectIdField() {
return this.layer?.objectIdField;
}
get _supportsCacheHint() {
return !!this.layer?.capabilities?.queryRelated?.supportsCacheHint;
}
get _utilityNetwork() {
const { layer: t, map: e } = this;
if (!t?.loaded || !e)
return null;
const s = k(t) ? t.parent : t;
return V(e, s);
}
get _canLoad() {
return !!this.map;
}
get _canQuery() {
return !!this.layer?.capabilities?.query?.supportsPagination;
}
get _state() {
const { _canLoad: t, _canQuery: e, _featureCount: s, _loaded: o, _queryAbortController: i, _queryFeatureCountAbortController: a, _queryPageAbortController: r, associationTypes: c } = this;
return a || t && !o ? "loading" : i || r ? "querying" : !this._globalId || !c || !e || !s ? "disabled" : "ready";
}
get heading() {
return this._heading;
}
set heading(t) {
this._heading = t;
}
async destroy() {
await this.manager.destroy();
}
loaded() {
this.manager.onLifecycle(() => [
A(() => [
this.graphic,
this.layer,
this.map,
this.associationTypes,
this._canQuery,
this._objectId,
this._globalId
], () => {
this._queryFeatureCountDebounced(), this._queryAssociationType();
}, { initial: !0 }),
A(() => this._activeAssociationType, (t) => {
this._queryDebounced(t);
}),
A(() => this._associationFeatures.values(), () => {
this._setUpUtilityNetworkAssociationsViewModels();
})
]), this.manager.onDestroy(() => {
this._cancelQuery(), this._cancelQueryFeatureCount();
});
}
_addAssociationTypeFlowItem(t) {
const e = this.useFeatureNextFlowItem ? q(m`<arcgis-feature-utility-network-association-list .autoDestroyDisabled=${this.autoDestroyDisabled} .associationViewModels=${this._associationViewModels} .description=${this._getParentFeatureDescription()} .flowItems=${this.flowItems} .flowType=${this.flowType} .heading=${this._getAssociationTypeTitle(t)} .messages=${this.messages} .map=${this.map} .parentFeatureViewModel=${this.parentFeatureViewModel} .referenceElement=${this.referenceElement} .state=${this._state} .spatialReference=${this.spatialReference} .timeZone=${this.timeZone} use-feature-next-flow-item .view=${this.view}></arcgis-feature-utility-network-association-list>`) : q(m`<arcgis-feature-utility-network-association-list .autoDestroyDisabled=${this.autoDestroyDisabled} .associationViewModels=${this._associationViewModels} .description=${this._getParentFeatureDescription()} .flowItems=${this.flowItems} .flowType=${this.flowType} .heading=${this._getAssociationTypeTitle(t)} .messages=${this.messages} .map=${this.map} .parentFeatureViewModel=${this.parentFeatureViewModel} .referenceElement=${this.referenceElement} .state=${this._state} .spatialReference=${this.spatialReference} .timeZone=${this.timeZone} .view=${this.view}></arcgis-feature-utility-network-association-list>`);
this._addFlowItem(e);
}
_clearAssociations() {
this._attachmentAssociations.removeAll(), this._connectivityAssociations.removeAll(), this._containerAssociations.removeAll(), this._contentAssociations.removeAll(), this._structureAssociations.removeAll();
}
_clearFeatures() {
this._associationFeatures.forEach((t) => t.removeAll()), this._associationFeatures.clear();
}
_compareByFeatureTitle(t, e) {
return t.title.localeCompare(e.title, void 0, {
numeric: !0
});
}
async _countAssociatedFeatureCount(t, e, s, o, i) {
const a = this._getFeatureQueryWhereClause(t, e, s, o), r = t.createQuery();
return r.where = a, r.outFields = ["*"], r.returnGeometry = !1, await t.queryFeatureCount(r, {
signal: i?.signal
});
}
async _countAssociatedFeatures(t, e, s, o, i) {
if (!e.length)
return 0;
const a = await this._findLayersBySourceId(t);
await this._loadLayers(a);
const r = a.map((l) => this._countAssociatedFeatureCount(l, e, s, o, i));
return (await Promise.all(r)).reduce((l, d) => l + d, 0);
}
async _createAssociationFeatureObjects(t, e, s, o, i) {
if (!t.length)
return [];
const a = /* @__PURE__ */ new Map(), r = Array.from(e.entries()).map(async ([l, d]) => {
const u = await this._findLayersBySourceId(l);
await this._loadLayers(u);
const y = u.map((n) => this._queryLayer(n, d, s, o, i));
(await Promise.all(y)).forEach((n) => {
n.forEach((h) => {
if (this.source === "popup" ? h.sourceLayer && h.getEffectivePopupTemplate() : !!h.sourceLayer) {
const f = h.layer.globalIdField ?? "", g = h.getAttribute(f), F = a.get(g) ?? [];
F.push(h), a.set(g, F);
}
});
});
});
await Promise.all(r);
const c = [];
return await Promise.all(t.toArray().map(async (l) => {
const { fromNetworkElement: d, toNetworkElement: u } = l, y = d.globalId === this._globalId ? u : d, p = a.get(y.globalId) ?? [];
await Promise.all(p.map(async (n) => {
const h = this._utilityNetwork?.getTerminalById(y?.terminalId ?? void 0)?.name, _ = await n.sourceLayer.getFeatureTitle(n) || this._getFeatureTitle(n);
c.push({
title: _,
feature: n,
association: l,
terminalName: h
});
}));
})), c;
}
_destroyAssociatedFeatureViewModels() {
this._associationViewModels.forEach((t) => t.destroyAll());
}
_equals(t, e) {
if (t.globalId === N && e.globalId === N) {
let s = function(d, u) {
return d.networkSourceId === u.networkSourceId && d.globalId === u.globalId && d.terminalId === u.terminalId && d.firstUnit === u.firstUnit && d.numUnits === u.numUnits;
};
const o = t.fromNetworkElement, i = t.toNetworkElement, a = e.fromNetworkElement, r = e.toNetworkElement, c = s(o, a), l = s(i, r);
return c && l && t.associationType === e.associationType;
}
return t.globalId != null && e.globalId != null && t.globalId === e.globalId;
}
async _findLayersBySourceId(t) {
const { _utilityNetwork: e, map: s } = this;
e?.loaded || await e?.load();
const o = e.getLayerIdBySourceId(t), i = s.allLayers.filter((c) => !!e?.isUtilityLayer(c) && c.layerId === o), a = s.allTables.filter((c) => !!e?.isUtilityLayer(c) && c.layerId === o);
return i.concat(a).toArray();
}
_getAssociationsByType(t) {
switch (t) {
case "attachment":
return this._attachmentAssociations;
case "structure":
return this._structureAssociations;
case "connectivity":
return this._connectivityAssociations;
case "container":
return this._containerAssociations;
case "content":
return this._contentAssociations;
}
}
_getAssociationTypeTitle(t) {
const { messages: e } = this;
if (t.title)
return t.title;
switch (t.type) {
case "attachment":
return e?.associationsAttachments ?? this.messages?.noTitle ?? "";
case "connectivity":
return e?.associationsConnectivity ?? this.messages?.noTitle ?? "";
case "structure":
return e?.associationsStructure ?? this.messages?.noTitle ?? "";
case "content":
return e?.associationsContents ?? this.messages?.noTitle ?? "";
case "container":
return e?.associationsContainer ?? this.messages?.noTitle ?? "";
}
}
_getFeatureCountForAssociationType(t) {
switch (t) {
case "attachment":
return this._attachmentFeatureCount;
case "structure":
return this._structureFeatureCount;
case "content":
return this._contentFeatureCount;
case "container":
return this._containerFeatureCount;
case "connectivity":
return this._connectivityFeatureCount;
}
}
_getFeatureQueryWhereClause(t, e, s, o) {
const i = t.globalIdField, a = t.fieldsIndex.get($.assetGroup), r = t.fieldsIndex.get($.assetType), c = s != null, l = o != null, d = i ? x.sqlIn(i, e) : null, u = c ? `(${a?.name} = ${s})` : null, y = c && l ? `(${r?.name} = ${o})` : null;
return [d, u, y].filter(Boolean).join(" AND ");
}
_getFeatureTitle(t) {
const e = t.attributes, s = t.sourceLayer;
if (!e || !s)
return "";
const o = "displayField" in s ? s.displayField : null, i = o != null ? e[o.toString()] : null, a = i != null ? i.toString() : null, r = t.getObjectId()?.toString();
return a || r || "";
}
_getParentFeatureDescription() {
const { parentFeatureViewModel: t } = this;
if (t) {
if ("heading" in t)
return t.heading;
if ("title" in t)
return t.title;
}
}
async _loadUtiltyNetworks() {
const t = this.map;
if (!t)
return;
t.utilityNetworks && await Promise.allSettled(t.utilityNetworks.map((s) => s.load()));
const e = this._utilityNetwork;
if (e) {
const s = (o) => {
if ("layerId" in o && e.isUtilityLayer(o)) {
const i = o.layerId != null ? e.getSourceIdByLayerId(o.layerId) : null;
i != null && this._networkSourceIdsInUse.add(i);
}
};
this._networkSourceIdsInUse = /* @__PURE__ */ new Set(), t.allLayers.forEach(s), t.allTables.forEach(s);
}
}
async _loadLayers(t) {
const e = t.filter((s) => !s.loaded).map((s) => s.load());
e.length && await Promise.allSettled(e);
}
_matchesGlobalFeature(t, e) {
return this._equals(t.association, e.association);
}
async _queryAssociatedFeatures(t, e) {
const { layer: s, _globalId: o, associationTypes: i, _utilityNetwork: a, _canQuery: r, _associationFeatures: c } = this;
if (await Promise.allSettled([s?.load(), a?.load()]), !r || !s || !i || !a)
return;
const l = this._getAssociationsByType(t.type), { associatedAssetGroup: d, associatedAssetType: u } = t, y = /* @__PURE__ */ new Map();
l.forEach((n) => {
const { fromNetworkElement: h, toNetworkElement: _ } = n, { networkSourceId: f, elementGlobalId: g } = h.globalId === o ? {
networkSourceId: _.networkSourceId,
elementGlobalId: _.globalId
} : {
networkSourceId: h.networkSourceId,
elementGlobalId: h.globalId
}, F = y.get(f) || [];
F.push(g), y.set(f, F);
});
const p = await this._createAssociationFeatureObjects(l, y, d, u, e);
this._parseFeatureObjects(p, c);
}
async _queryAssociations(t) {
const { layer: e, _globalId: s, associationTypes: o, _utilityNetwork: i, _canQuery: a } = this;
if (await Promise.allSettled([e?.load(), i?.load()]), this._clearAssociations(), !a || !e || !o || !i || !s)
return;
const r = k(e) ? e.parent : e, c = new P({
globalId: s,
networkSourceId: i.getSourceIdByLayerId(r.layerId)
}), l = /* @__PURE__ */ new Set();
o.forEach((n) => {
switch (n.type) {
case "attachment":
case "structure":
l.add("attachment");
break;
case "container":
case "content":
l.add("containment");
break;
case "connectivity":
l.add("connectivity"), l.add("junction-junction-connectivity"), l.add("junction-edge-from-connectivity"), l.add("junction-edge-midspan-connectivity"), l.add("junction-edge-to-connectivity");
break;
}
});
const d = await i?.queryAssociations(new U({
elements: [c],
types: Array.from(l)
}), {
signal: t?.signal
}), u = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map();
o.forEach((n) => {
y.set(n.type, n), u.set(n.type, []);
}), d.forEach((n) => {
const { toNetworkElement: h, fromNetworkElement: _ } = n;
switch (n.associationType) {
case "attachment":
if (_?.globalId === s) {
if (this._shouldDiscardNetworkElement(h, "attachment", y))
break;
u.get("attachment")?.push(h.globalId), this._attachmentAssociations.add(n);
} else {
if (this._shouldDiscardNetworkElement(_, "structure", y))
break;
u.get("structure")?.push(_.globalId), this._structureAssociations.add(n);
}
break;
case "connectivity":
case "junction-edge-from-connectivity":
case "junction-edge-midspan-connectivity":
case "junction-edge-to-connectivity":
case "junction-junction-connectivity":
if (_?.globalId === s) {
if (this._shouldDiscardNetworkElement(h, "connectivity", y))
break;
u.get("connectivity")?.push(h.globalId);
} else {
if (this._shouldDiscardNetworkElement(_, "connectivity", y))
break;
u.get("connectivity")?.push(_.globalId);
}
this._connectivityAssociations.add(n);
break;
case "containment":
if (_?.globalId === s) {
if (this._shouldDiscardNetworkElement(h, "content", y))
break;
u.get("content")?.push(h.globalId), this._contentAssociations.add(n);
} else {
if (this._shouldDiscardNetworkElement(_, "container", y))
break;
u.get("container")?.push(_.globalId), this._containerAssociations.add(n);
}
break;
}
});
const p = o.map(async (n) => {
const { associatedNetworkSourceId: h, associatedAssetGroup: _, associatedAssetType: f } = n, g = u.get(n.type), b = _ != null ? await this._countAssociatedFeatures(h, g, _, f, t) : g.length;
switch (n.type) {
case "attachment":
this._attachmentFeatureCount = b;
break;
case "connectivity":
this._connectivityFeatureCount = b;
break;
case "container":
this._containerFeatureCount = b;
break;
case "content":
this._contentFeatureCount = b;
break;
case "structure":
this._structureFeatureCount = b;
break;
}
});
await Promise.allSettled(p);
}
async _queryFeatureCount() {
await this._loadUtiltyNetworks();
const { _queryFeatureCountAbortController: t, _canQuery: e } = this;
if (!e) {
this._featureCount = 0;
return;
}
await this._queryAssociations(t), this._featureCount = this._attachmentFeatureCount + this._connectivityFeatureCount + this._containerFeatureCount + this._contentFeatureCount + this._structureFeatureCount;
}
async _queryLayer(t, e, s, o, i) {
const a = this._getFeatureQueryWhereClause(t, e, s, o), r = t.createQuery();
r.where = a, r.outFields = ["*"], r.returnGeometry = !1, r.cacheHint = this._supportsCacheHint;
const { features: c } = await D(t, r, i);
return c;
}
_parseFeatureObjects(t, e) {
const s = I();
t.forEach((o) => {
const a = (o?.feature).sourceLayer;
j(s, a, () => new w()).add(o);
});
for (const [o, i] of s)
this._sortFeatureObjectsByTitle(i), e.set(o, i);
}
_selectAssociationType(t) {
const { flowItems: e } = this;
e && (this._activeAssociationType = t, this._setUpUtilityNetworkAssociationsViewModels(), this._addAssociationTypeFlowItem(t));
}
_setUpUtilityNetworkAssociationsViewModels() {
this._associationViewModels.forEach((i, a) => {
const r = this._associationFeatures.get(a);
r ? i.forEach((c) => {
r.find((d) => this._matchesGlobalFeature(c, d)) || (i.remove(c), i.length || this._associationViewModels.delete(a));
}) : (i.removeAll(), this._associationViewModels.delete(a));
});
const { map: t, view: e, spatialReference: s, timeZone: o } = this;
this._associationFeatures.forEach((i, a) => {
const r = this._associationViewModels.get(a) || new w();
i?.filter(Boolean).forEach((c, l) => {
if (!r.some((u) => this._matchesGlobalFeature(u, c))) {
const { association: u, feature: y, terminalName: p, title: n } = c;
r.add({
title: n,
association: u,
featureViewModel: new G({
abilities: { utilityNetworkAssociationsContent: !1 },
map: t,
view: e,
spatialReference: s,
timeZone: o,
graphic: y
}),
terminalName: p
}, l);
}
}), this._associationViewModels.set(a, r);
});
}
_shouldDiscardNetworkElement(t, e, s) {
if (!t)
return !1;
const { networkSourceId: o } = t, i = s.get(e)?.associatedNetworkSourceId, a = this._networkSourceIdsInUse.has(o);
return i != null && i !== o || !a;
}
_sortFeatureObjectsByTitle(t) {
t.sort(this._compareByFeatureTitle);
}
render() {
const { _state: t } = this;
return m`<div class="root"><arcgis-feature-element-info .heading=${this.heading} .headingLevel=${this.headingLevel} .description=${this.description}></arcgis-feature-element-info>${t === "loading" ? this._renderLoading() : t === "disabled" ? this._renderAssociationNotFound() : this._renderContent()}</div>`;
}
_renderAssociationNotFound() {
return m`<calcite-notice icon=information kind=info open scale=s width=full><div slot=message>${this.messages?.noAssociations}</div></calcite-notice>`;
}
_renderAssociationTypeList(t) {
return this._getFeatureCountForAssociationType(t.type) > 0 ? this._renderAssociationTypeListItem(t) : null;
}
_renderAssociationTypeListItem(t) {
const e = this._getAssociationTypeTitle(t);
return C(`association-type-${t.type}`, m`<calcite-list-item .description=${v(t.description)} .label=${v(e)} .value=${t.type} =${() => this._selectAssociationType(t)}><calcite-icon flip-rtl icon=chevron-right scale=s slot=content-end></calcite-icon></calcite-list-item>`);
}
_renderContent() {
const { _state: t, associationTypes: e, messages: s } = this, o = s?.associationsList ?? s?.noTitle ?? "";
return C("list-container", m`<div class="list-container">${t === "ready" ? m`<div><calcite-list display-mode=flat .label=${o}>${e.map((i) => this._renderAssociationTypeList(i))}</calcite-list></div>` : null}${this._renderStickyLoading()}</div>`);
}
_renderLoading() {
return m`<div class="loading-container">${this._renderLoadingIcon()}${this._renderStickyLoading()}</div>`;
}
_renderLoadingIcon() {
return m`<calcite-loader inline label></calcite-loader>`;
}
_renderStickyLoading() {
return this._state === "querying" ? m`<div class="sticky-loading-container">${this._renderLoadingIcon()}</div>` : null;
}
}
S("arcgis-feature-utility-network-associations", R);
export {
R as ArcgisFeatureUtilityNetworkAssociations
};