UNPKG

itowns

Version:

A JS/WebGL framework for 3D geospatial data visualization

389 lines (312 loc) 15.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var THREE = _interopRequireWildcard(require("three")); var _LayerUpdateState = _interopRequireDefault(require("./LayerUpdateState")); var _ObjectRemovalHelper = _interopRequireDefault(require("../Process/ObjectRemovalHelper")); var _Layer2 = _interopRequireDefault(require("./Layer")); var _Coordinates = _interopRequireDefault(require("../Core/Geographic/Coordinates")); var _Extent = _interopRequireDefault(require("../Core/Geographic/Extent")); var _Label = _interopRequireDefault(require("../Core/Label")); var _Feature = require("../Core/Feature"); var _Style = require("../Core/Style"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } var coord = new _Coordinates["default"]('EPSG:4326', 0, 0, 0); var _extent = new _Extent["default"]('EPSG:4326', 0, 0, 0, 0); /** * A layer to handle a bunch of `Label`. This layer can be created on its own, * but it is better to use the option `addLabelLayer` on another `Layer` to let * it work with it (see the `vector_tile_raster_2d` example). * * @property {boolean} isLabelLayer - Used to checkout whether this layer is a * LabelLayer. Default is true. You should not change this, as it is used * internally for optimisation. */ var LabelLayer = /*#__PURE__*/function (_Layer) { (0, _inherits2["default"])(LabelLayer, _Layer); var _super = _createSuper(LabelLayer); /** * @constructor * @extends Layer * * @param {string} id - The id of the layer, that should be unique. It is * not mandatory, but an error will be emitted if this layer is added a * {@link View} that already has a layer going by that id. * @param {Object} [config] - Optional configuration, all elements in it * will be merged as is in the layer. For example, if the configuration * contains three elements `name, protocol, extent`, these elements will be * available using `layer.name` or something else depending on the property * name. * @param {domElement|function} config.domElement - An HTML domElement. * If set, all `Label` displayed within the current instance `LabelLayer` * will be this domElement. * * It can be set to a method. The single parameter of this method gives the * properties of each feature on which a `Label` is created. * * If set, all the parameters set in the `LabelLayer` `Style.text` will be overridden, * except for the `Style.text.anchor` parameter which can help place the label. */ function LabelLayer(id) { var _this; var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; (0, _classCallCheck2["default"])(this, LabelLayer); _this = _super.call(this, id, config); _this.isLabelLayer = true; _this.domElement = document.createElement('div'); _this.domElement.id = "itowns-label-".concat(_this.id); _this.defineLayerProperty('visible', true, function () { _this.domElement.style.display = _this.visible ? 'block' : 'none'; }); _this.buildExtent = true; _this.labelDomelement = config.domElement; return _this; } /** * Reads each {@link FeatureGeometry} that contains label configuration, and * creates the corresponding {@link Label}. To create a `Label`, a geometry * needs to have a `label` object with at least a few properties: * - `content`, which refers to `Label#content` * - `position`, which refers to `Label#position` * - (optional) `config`, containing miscellaneous configuration for the * label * * The geometry (or its parent Feature) needs to have a Style set. * * @param {FeatureCollection} data - The FeatureCollection to read the * labels from. * @param {Extent} extent * * @return {Label[]} An array containing all the created labels. */ (0, _createClass2["default"])(LabelLayer, [{ key: "convert", value: function convert(data, extent) { var _this2 = this; var labels = []; var layerField = this.style && this.style.text && this.style.text.field; // Converting the extent now is faster for further operation extent.as(data.crs, _extent); coord.crs = data.crs; var globals = { zoom: extent.zoom }; data.features.forEach(function (f) { // TODO: add support for LINE and POLYGON if (f.type !== _Feature.FEATURE_TYPES.POINT) { return; } var featureField = f.style && f.style.text.field; f.geometries.forEach(function (g) { // NOTE: this only works because only POINT is supported, it // needs more work for LINE and POLYGON coord.setFromArray(f.vertices, g.size * g.indices[0].offset); // Transform coordinate to data.crs projection coord.applyMatrix4(data.matrixWorld); if (f.size == 2) { coord.z = 0; } if (!_extent.isPointInside(coord)) { return; } var geometryField = g.properties.style && g.properties.style.text.field; var content; var context = { globals: globals, properties: function properties() { return g.properties; } }; if (_this2.labelDomelement) { content = (0, _Style.readExpression)(_this2.labelDomelement, context); } else if (!geometryField && !featureField && !layerField) { // Check if there is an icon, with no text if (!(g.properties.style && (g.properties.style.icon.source || g.properties.style.icon.key)) && !(f.style && (f.style.icon.source || f.style.icon.key)) && !(_this2.style && (_this2.style.icon.source || _this2.style.icon.key))) { return; } } else if (geometryField) { content = g.properties.style.getTextFromProperties(context); } else if (featureField) { content = f.style.getTextFromProperties(context); } else if (layerField) { content = _this2.style.getTextFromProperties(context); } var style = (g.properties.style || f.style || _this2.style).symbolStylefromContext(context); var label = new _Label["default"](content, coord.clone(), style, _this2.source.sprites); label.layerId = _this2.id; if (f.size == 2) { label.needsAltitude = true; } labels.push(label); }); }); return labels; } // placeholder }, { key: "preUpdate", value: function preUpdate() {} }, { key: "update", value: function update(context, layer, node, parent) { var _this3 = this; if (!parent && node.children.length) { // if node has been removed dispose three.js resource _ObjectRemovalHelper["default"].removeChildrenAndCleanupRecursively(this, node); return; } if (this.frozen || !node.visible || !this.visible) { return; } var extentsDestination = node.getExtentsByProjection(this.source.crs) || [node.extent]; var zoomDest = extentsDestination[0].zoom; if (zoomDest < layer.zoom.min || zoomDest > layer.zoom.max) { return; } if (node.layerUpdateState[this.id] === undefined) { node.layerUpdateState[this.id] = new _LayerUpdateState["default"](); } var elevationLayer = node.material.getElevationLayer(); if (elevationLayer && node.layerUpdateState[elevationLayer.id].canTryUpdate()) { node.children.forEach(function (c) { if (c.isLabel && c.needsAltitude && c.updateElevationFromLayer(_this3.parent)) { c.update3dPosition(context.view.referenceCrs); } }); } if (!node.layerUpdateState[this.id].canTryUpdate()) { return; } else if (!this.source.extentInsideLimit(node.extent, zoomDest)) { node.layerUpdateState[this.id].noMoreUpdatePossible(); return; } node.layerUpdateState[this.id].newTry(); var command = { layer: this, extentsSource: extentsDestination, view: context.view, threejsLayer: this.threejsLayer, requester: node }; return context.scheduler.execute(command).then(function (result) { if (!result) { return; } var renderer = context.view.mainLoop.gfxEngine.label2dRenderer; var labelsDiv = []; result.forEach(function (labels) { if (!node.parent) { labels.forEach(function (l) { _ObjectRemovalHelper["default"].removeChildrenAndCleanupRecursively(_this3, l); renderer.removeLabelDOM(l); }); return; } labels.forEach(function (label) { if (label.needsAltitude) { label.updateElevationFromLayer(_this3.parent); } var present = node.children.find(function (l) { return l.isLabel && l.baseContent == label.baseContent; }); if (!present) { node.add(label); label.update3dPosition(context.view.referenceCrs); if (node.level < 4) { label.horizonCullingPoint = new THREE.Vector3(); label.updateHorizonCullingPoint(); } labelsDiv.push(label.content); } }); }); if (labelsDiv.length > 0) { var _nodeDomElement$dom; // Add all labels for this tile at once to batch it var nodeDomElement = node.domElements[_this3.id]; if (!nodeDomElement) { nodeDomElement = { dom: document.createElement('div'), visible: true }; node.domElements[_this3.id] = nodeDomElement; } (_nodeDomElement$dom = nodeDomElement.dom).append.apply(_nodeDomElement$dom, labelsDiv); var closestDomElement = node.findClosestDomElement(_this3.id); (closestDomElement && closestDomElement.dom || _this3.domElement).appendChild(nodeDomElement.dom); nodeDomElement.visible = true; // Batch update the dimensions of labels all at once to avoid // redraw for at least this tile. result.forEach(function (labels) { return labels.forEach(function (label) { return label.initDimensions(); }); }); result.forEach(function (labels) { return labels.forEach(function (label) { label.visible = false; }); }); // Sort labels so they can be the first in the renderer. That // way, we cull labels on parent tile first, and then on // children tile. This allows a z-order priority, and reduce // flickering. node.children.sort(function (c) { return c.isLabel ? -c.order : 1; }); // Necessary event listener, to remove any Label attached to // this tile node.addEventListener('removed', function () { result.forEach(function (labels) { return labels.forEach(function (l) { return node.remove(l); }); }); _this3.removeNodeDomElement(node); }); } node.layerUpdateState[_this3.id].noMoreUpdatePossible(); }); } }, { key: "removeLabelsFromNodeRecursive", value: function removeLabelsFromNodeRecursive(node) { var _this4 = this; node.children.forEach(function (c) { if (c.isLabel && c.layerId === _this4.id) { node.remove(c); } else if (c.isTileMesh) { _this4.removeLabelsFromNodeRecursive(c); } }); this.removeNodeDomElement(node); } }, { key: "removeNodeDomElement", value: function removeNodeDomElement(node) { if (node.domElements[this.id]) { var child = node.domElements[this.id].dom; child.parentElement.removeChild(child); delete node.domElements[this.id]; } } }, { key: "delete", value: function _delete() { var _this5 = this; this.domElement.parentElement.removeChild(this.domElement); this.parent.level0Nodes.forEach(function (obj) { return _this5.removeLabelsFromNodeRecursive(obj); }); } }]); return LabelLayer; }(_Layer2["default"]); var _default = LabelLayer; exports["default"] = _default;