UNPKG

@ngageoint/mage.arcgis.service

Version:

A mage service plugin that synchronizes mage observations to a configured ArcGIS feature layer.

156 lines 8.27 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FeatureLayerProcessor = void 0; const ArcObjects_1 = require("./ArcObjects"); const FeatureQuerier_1 = require("./FeatureQuerier"); const ObservationBins_1 = require("./ObservationBins"); const ObservationsSender_1 = require("./ObservationsSender"); /** * Processes new, updated, and deleted observations and sends the changes to a specific arc feature layer. */ class FeatureLayerProcessor { /** * Creates a new instance of FeatureLayerProcessor. * @param {LayerInfo} layerInfo - Information about the arc feature layer this class sends observations to. * @param {ArcGISPluginConfig} config - Contains certain parameters that can be configured. * @param {ArcGISIdentityManager} identityManager - ArcGIS identity manager for authentication. * @param {Console} console - Used to log messages to the console. */ constructor(layerInfo, config, identityManager, console) { /** * The last time we checked for new/modified observations. */ this.lastTimeStamp = 0; /** * The number of existence queries we are still waiting for. */ this._existenceQueryCounts = 0; this._addedObs = new Set(); this.layerInfo = layerInfo; this._config = config; this._console = console; this.featureQuerier = new FeatureQuerier_1.FeatureQuerier(layerInfo, config, identityManager, console); this.sender = new ObservationsSender_1.ObservationsSender(layerInfo, config, identityManager, console); this._pendingNewAndUpdates = new ObservationBins_1.ObservationBins; } /** * Indicates if this binner has pending updates still waiting to be processed. * @returns {boolean} True if it is still waiting for updates to be processed, false otherwise. */ hasPendingUpdates() { return this._existenceQueryCounts > 0; } /** * Checks to see if there are any updates that need to be sent to the feature layer. */ processPendingUpdates() { return __awaiter(this, void 0, void 0, function* () { const newAndUpdates = new ObservationBins_1.ObservationBins(); for (let i = 0; i < this._pendingNewAndUpdates.adds.count(); i++) { if (!this._addedObs.has(this._pendingNewAndUpdates.adds.observations[i].id)) { newAndUpdates.adds.add(this._pendingNewAndUpdates.adds.observations[i]); this._addedObs.add(this._pendingNewAndUpdates.adds.observations[i].id); } } for (let i = 0; i < this._pendingNewAndUpdates.updates.count(); i++) { newAndUpdates.updates.add(this._pendingNewAndUpdates.updates.observations[i]); } this._pendingNewAndUpdates.clear(); yield this.send(newAndUpdates); }); } /** * Goes through each observation and figures out if the geometry type matches the arc feature layer. * If so it then separates the adds from the updates and sends them to the arc feature layer. * @param {ArcObjects} observations - The observations to process. */ processArcObjects(observations) { return __awaiter(this, void 0, void 0, function* () { const arcObjectsForLayer = new ArcObjects_1.ArcObjects(); arcObjectsForLayer.firstRun = observations.firstRun; for (const arcObservation of observations.observations) { if (this.layerInfo.geometryType === arcObservation.esriGeometryType) { arcObjectsForLayer.add(arcObservation); } } const bins = new ObservationBins_1.ObservationBins(); for (const arcObservation of arcObjectsForLayer.observations) { // TODO: Would probably want a better way to determine which observations need to be updated in arcgis if (arcObjectsForLayer.firstRun || arcObservation.lastModified !== arcObservation.createdAt) { bins.updates.add(arcObservation); } else if (!this._addedObs.has(arcObservation.id)) { bins.adds.add(arcObservation); this._addedObs.add(arcObservation.id); } } for (const arcObservation of bins.updates.observations) { this._existenceQueryCounts++; this.featureQuerier.queryObservation(arcObservation.id, (result) => { this._existenceQueryCounts--; if (result.features != null && result.features.length > 0) { this._addedObs.add(arcObservation.id); const arcAttributes = result.features[0].attributes; let lastEdited = null; if (this._config.lastEditedDateField != null) { lastEdited = Number(arcAttributes[this._config.lastEditedDateField]); } if (!lastEdited || lastEdited < arcObservation.lastModified) { const objectIdField = result.objectIdFieldName; const updateAttributes = arcObservation.object.attributes; updateAttributes[objectIdField] = arcAttributes[objectIdField]; // Determine if any editable attribute values should be deleted const lowerCaseUpdateAttributes = Object.fromEntries(Object.entries(updateAttributes).map(([k, v]) => [k.toLowerCase(), v])); for (const arcAttribute of Object.keys(arcAttributes)) { if (this.layerInfo.isEditable(arcAttribute) && arcAttributes[arcAttribute] != null && lowerCaseUpdateAttributes[arcAttribute.toLowerCase()] == null) { updateAttributes[arcAttribute] = null; } } this._pendingNewAndUpdates.updates.add(arcObservation); } } else { this._pendingNewAndUpdates.adds.add(arcObservation); } }, undefined, false); } bins.updates = this._pendingNewAndUpdates.updates; yield this.send(bins); for (const arcObservation of observations.deletions) { if (this.layerInfo.geometryType === arcObservation.esriGeometryType && this._addedObs.has(arcObservation.id)) { yield this.sender.sendDelete(arcObservation.id); this._addedObs.delete(arcObservation.id); } } }); } /** * Sends all the observations to the arc server. * @param {ObservationBins} bins - The observations to send. */ send(bins) { return __awaiter(this, void 0, void 0, function* () { const promises = []; if (!bins.adds.isEmpty()) { promises.push(this.sender.sendAdds(bins.adds)); } if (!bins.updates.isEmpty()) { promises.push(this.sender.sendUpdates(bins.updates)); } yield Promise.all(promises); }); } } exports.FeatureLayerProcessor = FeatureLayerProcessor; //# sourceMappingURL=FeatureLayerProcessor.js.map