UNPKG

terriajs

Version:

Geospatial data visualization platform.

301 lines 16.2 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { action, makeObservable, observable, runInAction } from "mobx"; import Color from "terriajs-cesium/Source/Core/Color"; import createGuid from "terriajs-cesium/Source/Core/createGuid"; import DeveloperError from "terriajs-cesium/Source/Core/DeveloperError"; import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import ColorMaterialProperty from "terriajs-cesium/Source/DataSources/ColorMaterialProperty"; import ConstantPositionProperty from "terriajs-cesium/Source/DataSources/ConstantPositionProperty"; import ConstantProperty from "terriajs-cesium/Source/DataSources/ConstantProperty"; import SplitDirection from "terriajs-cesium/Source/Scene/SplitDirection"; import isDefined from "../Core/isDefined"; import TerriaError from "../Core/TerriaError"; import ProtomapsImageryProvider from "../Map/ImageryProvider/ProtomapsImageryProvider"; import featureDataToGeoJson from "../Map/PickedFeatures/featureDataToGeoJson"; import MappableMixin from "../ModelMixins/MappableMixin"; import TimeVarying from "../ModelMixins/TimeVarying"; import MouseCoords from "../ReactViewModels/MouseCoords"; import TableColorStyleTraits from "../Traits/TraitsClasses/Table/ColorStyleTraits"; import TableOutlineStyleTraits, { OutlineSymbolTraits } from "../Traits/TraitsClasses/Table/OutlineStyleTraits"; import TableStyleTraits from "../Traits/TraitsClasses/Table/StyleTraits"; import CommonStrata from "./Definition/CommonStrata"; import createStratumInstance from "./Definition/createStratumInstance"; import TerriaFeature from "./Feature/Feature"; import HighlightColorTraits from "../Traits/TraitsClasses/HighlightColorTraits"; import hasTraits from "./Definition/hasTraits"; import "./Feature/ImageryLayerFeatureInfo"; // overrides Cesium's prototype.configureDescriptionFromProperties export default class GlobeOrMap { static featureHighlightID = "___$FeatureHighlight&__"; static _featureHighlightName = "TerriaJS Feature Highlight Marker"; _removeHighlightCallback; _highlightPromise; _tilesLoadingCountMax = 0; supportsPolylinesOnTerrain; // True if zoomTo() was called and the map is currently zooming to dataset isMapZooming = false; // An internal id to track an in progress call to zoomTo() _currentZoomId; // This is updated by Leaflet and Cesium objects. // Avoid duplicate mousemove events. Why would we get duplicate mousemove events? I'm glad you asked: // http://stackoverflow.com/questions/17818493/mousemove-event-repeating-every-second/17819113 // I (Kevin Ring) see this consistently on my laptop when Windows Media Player is running. mouseCoords = new MouseCoords(); constructor() { makeObservable(this); } /** * Zoom map to a dataset or the given bounds. * * @param target A bounds item to zoom to * @param flightDurationSeconds Optional time in seconds for the zoom animation to complete * @returns A promise that resolves when the zoom animation is complete */ zoomTo(target, flightDurationSeconds = 3.0) { this.isMapZooming = true; const zoomId = createGuid(); this._currentZoomId = zoomId; return this.doZoomTo(target, flightDurationSeconds).finally(action(() => { // Unset isMapZooming only if the local zoomId matches _currentZoomId. // If they do not match, it means there was another call to zoomTo which // could still be in progress and it will handle unsetting isMapZooming. if (zoomId === this._currentZoomId) { this.isMapZooming = false; this._currentZoomId = undefined; if (MappableMixin.isMixedInto(target) && TimeVarying.is(target)) { // Set the target as the source for timeline this.terria.timelineStack.promoteToTop(target); } } })); } /** * List of the attributions (credits) for data currently displayed on map. */ get attributions() { return []; } /** * Creates a {@see Feature} (based on an {@see Entity}) from a {@see ImageryLayerFeatureInfo}. * @param imageryFeature The imagery layer feature for which to create an entity-based feature. * @return The created feature. */ _createFeatureFromImageryLayerFeature(imageryFeature) { const feature = new TerriaFeature({ id: imageryFeature.name }); feature.name = imageryFeature.name; if (imageryFeature.description) { feature.description = new ConstantProperty(imageryFeature.description); // already defined by the new Entity } feature.properties = imageryFeature.properties; feature.data = imageryFeature.data; feature.imageryLayer = imageryFeature.imageryLayer; if (imageryFeature.position) { feature.position = new ConstantPositionProperty(Ellipsoid.WGS84.cartographicToCartesian(imageryFeature.position)); } feature.coords = imageryFeature.coords; return feature; } /** * Adds loading progress for cesium */ _updateTilesLoadingCount(tilesLoadingCount) { if (tilesLoadingCount > this._tilesLoadingCountMax) { this._tilesLoadingCountMax = tilesLoadingCount; } else if (tilesLoadingCount === 0) { this._tilesLoadingCountMax = 0; } this.terria.tileLoadProgressEvent.raiseEvent(tilesLoadingCount, this._tilesLoadingCountMax); } /** * Adds loading progress (boolean) for 3DTileset layers where total tiles is not known */ _updateTilesLoadingIndeterminate(loading) { this.terria.indeterminateTileLoadProgressEvent.raiseEvent(loading); } /** * Returns the side of the splitter the `position` lies on. * * @param The screen position. * @return The side of the splitter on which `position` lies. */ _getSplitterSideForScreenPosition(position) { const container = this.terria.currentViewer.getContainer(); if (!isDefined(container)) { return; } const splitterX = container.clientWidth * this.terria.splitPosition; if (position.x <= splitterX) { return SplitDirection.LEFT; } else { return SplitDirection.RIGHT; } } async _highlightFeature(feature) { if (isDefined(this._removeHighlightCallback)) { await this._removeHighlightCallback(); this._removeHighlightCallback = undefined; this._highlightPromise = undefined; } // Lazy import here to avoid cyclic dependencies. const { default: GeoJsonCatalogItem } = await import("./Catalog/CatalogItems/GeoJsonCatalogItem"); if (isDefined(feature)) { let hasGeometry = false; if (isDefined(feature._cesium3DTileFeature)) { const originalColor = feature._cesium3DTileFeature.color; const defaultColor = Color.fromCssColorString("#fffffe"); // Get the highlight color from the catalogItem trait or default to baseMapContrastColor const catalogItem = feature._catalogItem; let highlightColorString; if (hasTraits(catalogItem, HighlightColorTraits, "highlightColor")) { highlightColorString = runInAction(() => catalogItem.highlightColor); runInAction(() => catalogItem.highlightColor); } else { highlightColorString = this.terria.baseMapContrastColor; } const highlightColor = isDefined(highlightColorString) ? Color.fromCssColorString(highlightColorString) : defaultColor; // highlighting doesn't work if the highlight colour is full white // so in this case use something close to white instead feature._cesium3DTileFeature.color = Color.equals(highlightColor, Color.WHITE) ? defaultColor : highlightColor; this._removeHighlightCallback = function () { if (isDefined(feature._cesium3DTileFeature) && feature._cesium3DTileFeature.tileset.isDestroyed() === false) { try { feature._cesium3DTileFeature.color = originalColor; } catch (err) { TerriaError.from(err).log(); } } }; } else if (isDefined(feature.polygon)) { hasGeometry = true; const cesiumPolygon = feature.cesiumEntity || feature; const polygonOutline = cesiumPolygon.polygon.outline; const polygonOutlineColor = cesiumPolygon.polygon.outlineColor; const polygonMaterial = cesiumPolygon.polygon.material; cesiumPolygon.polygon.outline = new ConstantProperty(true); cesiumPolygon.polygon.outlineColor = new ConstantProperty(Color.fromCssColorString(this.terria.baseMapContrastColor) ?? Color.GRAY); cesiumPolygon.polygon.material = new ColorMaterialProperty(new ConstantProperty((Color.fromCssColorString(this.terria.baseMapContrastColor) ?? Color.LIGHTGRAY).withAlpha(0.75))); this._removeHighlightCallback = function () { if (cesiumPolygon.polygon) { cesiumPolygon.polygon.outline = polygonOutline; cesiumPolygon.polygon.outlineColor = polygonOutlineColor; cesiumPolygon.polygon.material = polygonMaterial; } }; } else if (isDefined(feature.polyline)) { hasGeometry = true; const cesiumPolyline = feature.cesiumEntity || feature; const polylineMaterial = cesiumPolyline.polyline.material; const polylineWidth = cesiumPolyline.polyline.width; cesiumPolyline.polyline.material = Color.fromCssColorString(this.terria.baseMapContrastColor) ?? Color.LIGHTGRAY; cesiumPolyline.polyline.width = new ConstantProperty(2); this._removeHighlightCallback = function () { if (cesiumPolyline.polyline) { cesiumPolyline.polyline.material = polylineMaterial; cesiumPolyline.polyline.width = polylineWidth; } }; } if (!hasGeometry) { let vectorTileHighlightCreated = false; // Feature from ProtomapsImageryProvider if (feature.imageryLayer?.imageryProvider instanceof ProtomapsImageryProvider) { const highlightImageryProvider = feature.imageryLayer.imageryProvider.createHighlightImageryProvider(feature); if (highlightImageryProvider) this._removeHighlightCallback = this.terria.currentViewer._addVectorTileHighlight(highlightImageryProvider, feature.imageryLayer.imageryProvider.rectangle); vectorTileHighlightCreated = true; } // No vector tile highlight was created so try to convert feature to GeoJSON // This flag is necessary to check as it is possible for a feature to use ProtomapsImageryProvider and also have GeoJson data - but maybe failed to createHighlightImageryProvider if (!vectorTileHighlightCreated) { const geoJson = featureDataToGeoJson(feature.data); // Don't show points; the targeting cursor is sufficient. if (geoJson) { geoJson.features = geoJson.features.filter((f) => f.geometry.type !== "Point"); let catalogItem = this.terria.getModelById(GeoJsonCatalogItem, GlobeOrMap.featureHighlightID); if (catalogItem === undefined) { catalogItem = new GeoJsonCatalogItem(GlobeOrMap.featureHighlightID, this.terria); catalogItem.setTrait(CommonStrata.definition, "name", GlobeOrMap._featureHighlightName); this.terria.addModel(catalogItem); } catalogItem.setTrait(CommonStrata.user, "geoJsonData", geoJson); catalogItem.setTrait(CommonStrata.user, "useOutlineColorForLineFeatures", true); catalogItem.setTrait(CommonStrata.user, "defaultStyle", createStratumInstance(TableStyleTraits, { outline: createStratumInstance(TableOutlineStyleTraits, { null: createStratumInstance(OutlineSymbolTraits, { width: 4, color: this.terria.baseMapContrastColor }) }), color: createStratumInstance(TableColorStyleTraits, { nullColor: "rgba(0,0,0,0)" }) })); this.terria.overlays.add(catalogItem); this._highlightPromise = catalogItem.loadMapItems(); const removeCallback = (this._removeHighlightCallback = () => { if (!isDefined(this._highlightPromise)) { return; } return this._highlightPromise .then(() => { if (removeCallback !== this._removeHighlightCallback) { return; } if (isDefined(catalogItem)) { catalogItem.setTrait(CommonStrata.user, "show", false); } }) .catch(function () { }); }); (await catalogItem.loadMapItems()).logError("Error occurred while loading picked feature"); // Check to make sure we don't have a different `catalogItem` after loading if (removeCallback !== this._removeHighlightCallback) { return; } catalogItem.setTrait(CommonStrata.user, "show", true); this._highlightPromise = this.terria.overlays .add(catalogItem) .then((r) => r.throwIfError()); } } } } } /** * Captures a screenshot of the map. * @return A promise that resolves to a data URL when the screenshot is ready. */ captureScreenshot() { throw new DeveloperError("captureScreenshot must be implemented in the derived class."); } } __decorate([ observable ], GlobeOrMap.prototype, "isMapZooming", void 0); __decorate([ action ], GlobeOrMap.prototype, "zoomTo", null); //# sourceMappingURL=GlobeOrMap.js.map