UNPKG

terriajs

Version:

Geospatial data visualization platform.

390 lines (327 loc) 15.1 kB
'use strict'; /*global require*/ var Color = require('terriajs-cesium/Source/Core/Color'); var defined = require('terriajs-cesium/Source/Core/defined'); var DeveloperError = require('terriajs-cesium/Source/Core/DeveloperError'); var Ellipsoid = require('terriajs-cesium/Source/Core/Ellipsoid'); var featureDataToGeoJson = require('../Map/featureDataToGeoJson'); var MapboxVectorTileImageryProvider = require('../Map/MapboxVectorTileImageryProvider'); var MapboxVectorCanvasTileLayer = require('../Map/MapboxVectorCanvasTileLayer'); var GeoJsonCatalogItem = require('./GeoJsonCatalogItem'); var Feature = require('./Feature'); var ImageryLayer = require('terriajs-cesium/Source/Scene/ImageryLayer'); var rectangleToLatLngBounds = require('../Map/rectangleToLatLngBounds'); require('./ImageryLayerFeatureInfo'); // overrides Cesium's prototype.configureDescriptionFromProperties /** * The base class for map/globe viewers. * * @constructor * @alias GlobeOrMap * * @param {Terria} terria The Terria instance. * @param {String} disclaimerClass Class of a disclaimer element that should be shifted upwards to make room for other ui elements. * * @see Cesium * @see Leaflet */ var GlobeOrMap = function(terria) { /** * Gets or sets the Terria instance. * @type {Terria} */ this.terria = terria; /** * Gets or sets whether this viewer _can_ show a splitter. Default false. * @type {Boolean} */ this.canShowSplitter = false; this._tilesLoadingCountMax = 0; this._removeHighlightCallback = undefined; this._highlightPromise = undefined; }; GlobeOrMap._featureHighlightName = '___$FeatureHighlight&__'; /** * Creates a {@see Feature} (based on an {@see Entity}) from a {@see ImageryLayerFeatureInfo}. * @param {ImageryLayerFeatureInfo} imageryFeature The imagery layer feature for which to create an entity-based feature. * @return {Feature} The created feature. * @protected */ GlobeOrMap.prototype._createFeatureFromImageryLayerFeature = function(imageryFeature) { var feature = new Feature({ id : imageryFeature.name, }); feature.name = imageryFeature.name; feature.description = imageryFeature.description; // already defined by the new Entity feature.properties = imageryFeature.properties; feature.data = imageryFeature.data; feature.imageryLayer = imageryFeature.imageryLayer; feature.position = Ellipsoid.WGS84.cartographicToCartesian(imageryFeature.position); feature.coords = imageryFeature.coords; return feature; }; GlobeOrMap.prototype.updateTilesLoadingCount = function(tilesLoadingCount) { if (tilesLoadingCount > this._tilesLoadingCountMax) { this._tilesLoadingCountMax = tilesLoadingCount; } else if (tilesLoadingCount === 0) { this._tilesLoadingCountMax = 0; } this.terria.tileLoadProgressEvent.raiseEvent(tilesLoadingCount, this._tilesLoadingCountMax); }; GlobeOrMap.prototype.isDestroyed = function() { return false; }; /** * Picks features based off a latitude, longitude and (optionally) height. * @param {Object} latlng The position on the earth to pick. * @param {Object} imageryLayerCoords A map of imagery provider urls to the coords used to get features for those imagery * providers - i.e. x, y, level * @param existingFeatures An optional list of existing features to concatenate the ones found from asynchronous picking to. */ GlobeOrMap.prototype.pickFromLocation = function(latlng, imageryLayerCoords, existingFeatures) { throw new DeveloperError('pickFromLocation must be implemented in the derived class.'); }; GlobeOrMap.prototype.destroy = function() { throw new DeveloperError('destroy must be implemented in the derived class.'); }; /** * Gets the current extent of the camera. This may be approximate if the viewer does not have a strictly rectangular view. * @return {Rectangle} The current visible extent. */ GlobeOrMap.prototype.getCurrentExtent = function() { throw new DeveloperError('getCurrentExtent must be implemented in the derived class.'); }; /** * Gets the current container element. * @return {Element} The current container element. */ GlobeOrMap.prototype.getContainer = function() { throw new DeveloperError('getContainer must be implemented in the derived class.'); }; /** * Zooms to a specified camera view or extent with a smooth flight animation. * * @param {CameraView|Rectangle} viewOrExtent The view or extent to which to zoom. * @param {Number} [flightDurationSeconds=3.0] The length of the flight animation in seconds. */ GlobeOrMap.prototype.zoomTo = function(viewOrExtent, flightDurationSeconds) { throw new DeveloperError('zoomTo must be implemented in the derived class.'); }; /** * Captures a screenshot of the map. * @return {Promise<string>} A promise that resolves to a data URL when the screenshot is ready. */ GlobeOrMap.prototype.captureScreenshot = function() { throw new DeveloperError('captureScreenshot must be implemented in the derived class.'); }; /** * Notifies the viewer that a repaint is required. */ GlobeOrMap.prototype.notifyRepaintRequired = function() { throw new DeveloperError('notifyRepaintRequired must be implemented in the derived class.'); }; /** * Computes the screen position of a given world position. * @param {Cartesian3} position The world position in Earth-centered Fixed coordinates. * @param {Cartesian2} [result] The instance to which to copy the result. * @return {Cartesian2} The screen position, or undefined if the position is not on the screen. */ GlobeOrMap.prototype.computePositionOnScreen = function(position, result) { throw new DeveloperError('computePositionOnScreen must be implemented in the derived class.'); }; /** * Adds an attribution to the globe or map. * @param {Credit} attribution The attribution to add. */ GlobeOrMap.prototype.addAttribution = function(attribution) { throw new DeveloperError('addAttribution must be implemented in the derived class.'); }; /** * Removes an attribution from the globe or map. * @param {Credit} attribution The attribution to remove. */ GlobeOrMap.prototype.removeAttribution = function(attribution) { throw new DeveloperError('removeAttribution must be implemented in the derived class.'); }; /** * Gets all attribution currently active on the globe or map. * @returns {String[]} The list of current attributions, as HTML strings. */ GlobeOrMap.prototype.getAllAttribution = function() { return []; }; /** * Perform any updates to the order of layers required by raise and lower, * but after the items have been reordered. * This allows for the possibility that raise and lower do nothing, and instead we * call updateLayerOrder */ GlobeOrMap.prototype.updateLayerOrderAfterReorder = function() { throw new DeveloperError('updateLayerOrderAfterReorder must be implemented in the derived class.'); }; /** * Raise an item's level in the viewer * This does not check that index is valid * @param {Number} index The index of the item to raise */ GlobeOrMap.prototype.raise = function(index) { throw new DeveloperError('raise must be implemented in the derived class.'); }; /** * Lower an item's level in the viewer * This does not check that index is valid * @param {Number} index The index of the item to lower */ GlobeOrMap.prototype.lower = function(index) { throw new DeveloperError('lower must be implemented in the derived class.'); }; /** * Lowers this imagery layer to the bottom, underneath all other layers. If this item is not enabled or not shown, * this method does nothing. * @param {CatalogItem} item The item to lower to the bottom (usually a basemap) */ GlobeOrMap.prototype.lowerToBottom = function(item) { throw new DeveloperError('lowerToBottom must be implemented in the derived class.'); }; GlobeOrMap.prototype._highlightFeature = function(feature) { if (defined(this._removeHighlightCallback)) { this._removeHighlightCallback(); this._removeHighlightCallback = undefined; this._highlightPromise = undefined; } if (defined(feature)) { var hasGeometry = false; if (defined(feature.polygon)) { hasGeometry = true; var polygonOutline = feature.polygon.outline; var polygonOutlineColor = feature.polygon.outlineColor; var polygonMaterial = feature.polygon.material; feature.polygon.outline = true; feature.polygon.outlineColor = Color.fromCssColorString(this.terria.baseMapContrastColor); feature.polygon.material = Color.fromCssColorString(this.terria.baseMapContrastColor).withAlpha(0.75); this._removeHighlightCallback = function() { feature.polygon.outline = polygonOutline; feature.polygon.outlineColor = polygonOutlineColor; feature.polygon.material = polygonMaterial; }; } if (defined(feature.polyline)) { hasGeometry = true; var polylineMaterial = feature.polyline.material; var polylineWidth = feature.polyline.width; feature.polyline.material = Color.fromCssColorString(this.terria.baseMapContrastColor); feature.polyline.width = 2; this._removeHighlightCallback = function() { feature.polyline.material = polylineMaterial; feature.polyline.width = polylineWidth; }; } if (!hasGeometry) { if (feature.imageryLayer && feature.imageryLayer.imageryProvider instanceof MapboxVectorTileImageryProvider) { if (defined(this.terria.cesium)) { var result = new ImageryLayer(feature.imageryLayer.imageryProvider.createHighlightImageryProvider(feature.data.id), { show : true, alpha : 1 }); var scene = this.terria.cesium.scene; scene.imageryLayers.add(result); this._removeHighlightCallback = function () { scene.imageryLayers.remove(result); }; } else if (defined(this.terria.leaflet)) { var map = this.terria.leaflet.map; var options = { async: true, opacity: 1, bounds : rectangleToLatLngBounds(feature.imageryLayer.imageryProvider.rectangle), }; if (defined(map.options.maxZoom)) { options.maxZoom = map.options.maxZoom; } var layer = new MapboxVectorCanvasTileLayer(feature.imageryLayer.imageryProvider.createHighlightImageryProvider(feature.data.id), options); layer.addTo(map); layer.bringToFront(); this._removeHighlightCallback = function () { map.removeLayer(layer); }; } } else { var geoJson = featureDataToGeoJson(feature.data); // Show geometry associated with the feature. // Don't show points; the targeting cursor is sufficient. if (geoJson && geoJson.geometry && geoJson.geometry.type !== 'Point') { var catalogItem = new GeoJsonCatalogItem(this.terria); catalogItem.name = GlobeOrMap._featureHighlightName; catalogItem.data = geoJson; catalogItem.style = { 'stroke-width': 2, 'stroke': this.terria.baseMapContrastColor, 'fill-opacity': 0, 'marker-color': this.terria.baseMapContrastColor }; var that = this; var removeCallback = this._removeHighlightCallback = function() { that._highlightPromise.then(function() { if (removeCallback !== that._removeHighlightCallback) { return; } catalogItem._hide(); catalogItem._disable(); }).otherwise(function(){}); }; that._highlightPromise = catalogItem.load().then(function() { if (removeCallback !== that._removeHighlightCallback) { return; } catalogItem._enable(); catalogItem._show(); }); } } } } }; GlobeOrMap.prototype.addImageryProvider = function(options) { throw new DeveloperError('addImageryProvider must be implemented in the derived class.'); }; GlobeOrMap.prototype.removeImageryLayer = function(options) { throw new DeveloperError('removeImageryLayer must be implemented in the derived class.'); }; GlobeOrMap.prototype.showImageryLayer = function(options) { throw new DeveloperError('showImageryLayer must be implemented in the derived class.'); }; GlobeOrMap.prototype.hideImageryLayer = function(options) { throw new DeveloperError('hideImageryLayer must be implemented in the derived class.'); }; GlobeOrMap.prototype.isImageryLayerShown = function(options) { throw new DeveloperError('isImageryLayerShown must be implemented in the derived class.'); }; GlobeOrMap.prototype.addDataSource = function(options) { this.terria.dataSources.add(options.dataSource); }; GlobeOrMap.prototype.removeDataSource = function(options) { this.terria.dataSources.remove(options.dataSource, false); }; GlobeOrMap.prototype.updateAllItemsForSplitter = function() { this.terria.nowViewing.items.forEach(item => { this.updateItemForSplitter(item); }); this.notifyRepaintRequired(); }; GlobeOrMap.prototype.updateItemForSplitter = function(item) { }; GlobeOrMap.prototype.pauseMapInteraction = function() { }; GlobeOrMap.prototype.resumeMapInteraction = function() { }; GlobeOrMap.disposeCommonListeners = function(globeOrMap) { if (defined(globeOrMap._removeHighlightCallback)) { globeOrMap._removeHighlightCallback(); globeOrMap._removeHighlightCallback = undefined; globeOrMap._highlightPromise = undefined; } if (defined(globeOrMap._disclaimerShiftSubscription)) { globeOrMap._disclaimerShiftSubscription.dispose(); globeOrMap._disclaimerShiftSubscription = undefined; } }; module.exports = GlobeOrMap;