UNPKG

itowns

Version:

A JS/WebGL framework for 3D geospatial data visualization

277 lines (232 loc) 9.42 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _three = require("three"); var _pbf = _interopRequireDefault(require("pbf")); var _vectorTile = require("@mapbox/vector-tile"); var _Extent = require("../Core/Geographic/Extent"); var _Feature = require("../Core/Feature"); var _mapboxGlStyleSpec = require("@mapbox/mapbox-gl-style-spec"); var _Style = _interopRequireDefault(require("../Core/Style")); var worldDimension3857 = _Extent.globalExtentTMS.get('EPSG:3857').dimensions(); var globalExtent = new _three.Vector3(worldDimension3857.x, worldDimension3857.y, 1); var lastPoint = new _three.Vector2(); var firstPoint = new _three.Vector2(); var styleCache = new Map(); // Classify option, it allows to classify a full polygon and its holes. // Each polygon with its holes are in one FeatureGeometry. // A polygon is determined by its clockwise direction and the holes are in the opposite direction. // Clockwise direction is determined by Shoelace formula https://en.wikipedia.org/wiki/Shoelace_formula // Draw polygon with canvas doesn't need to classify however it is necessary for meshs. function vtFeatureToFeatureGeometry(vtFeature, feature) { var classify = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var geometry = feature.bindNewGeometry(); classify = classify && feature.type === _Feature.FEATURE_TYPES.POLYGON; geometry.properties = vtFeature.properties; var pbf = vtFeature._pbf; pbf.pos = vtFeature._geometry; var end = pbf.readVarint() + pbf.pos; var cmd = 1; var length = 0; var x = 0; var y = 0; var count = 0; var sum = 0; while (pbf.pos < end) { if (length <= 0) { var cmdLen = pbf.readVarint(); cmd = cmdLen & 0x7; length = cmdLen >> 3; } length--; if (cmd === 1 || cmd === 2) { x += pbf.readSVarint(); y += pbf.readSVarint(); if (cmd === 1) { if (count) { if (classify && sum > 0 && geometry.indices.length > 0) { feature.updateExtent(geometry); geometry = feature.bindNewGeometry(); geometry.properties = vtFeature.properties; } geometry.closeSubGeometry(count); geometry.getLastSubGeometry().ccw = sum < 0; } count = 0; sum = 0; } count++; geometry.pushCoordinatesValues(x, y); if (count == 1) { firstPoint.set(x, y); lastPoint.set(x, y); } else if (classify && count > 1) { sum += (lastPoint.x - x) * (lastPoint.y + y); lastPoint.set(x, y); } } else if (cmd === 7) { if (count) { count++; geometry.pushCoordinatesValues(firstPoint.x, firstPoint.y); if (classify) { sum += (lastPoint.x - firstPoint.x) * (lastPoint.y + firstPoint.y); } } } else { throw new Error("unknown command ".concat(cmd)); } } if (count) { if (classify && sum > 0 && geometry.indices.length > 0) { feature.updateExtent(geometry); geometry = feature.bindNewGeometry(); geometry.properties = vtFeature.properties; } geometry.closeSubGeometry(count); geometry.getLastSubGeometry().ccw = sum < 0; } feature.updateExtent(geometry); } var defaultFilter = function () { return true; }; function readPBF(file, options) { var vectorTile = new _vectorTile.VectorTile(new _pbf["default"](file)); var extentSource = options.extentSource || file.extent; var sourceLayers = Object.keys(vectorTile.layers); if (sourceLayers.length < 1) { return; } // x,y,z tile coordinates var x = extentSource.col; var z = extentSource.zoom; // We need to move from TMS to Google/Bing/OSM coordinates // https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates/ // Only if the layer.origin is top var y = options.isInverted ? extentSource.row : (1 << z) - extentSource.row - 1; options.buildExtent = true; options.mergeFeatures = true; options.withAltitude = false; options.withNormal = false; var features = new _Feature.FeatureCollection('EPSG:3857', options); // TODO remove defaultFilter; features.filter = options.filter || defaultFilter; var vFeature = vectorTile.layers[sourceLayers[0]]; // TODO: verify if size is correct because is computed with only one feature (vFeature). var size = vFeature.extent * Math.pow(2, z); var center = -0.5 * size; features.scale.set(size, -size, 1).divide(globalExtent); features.translation.set(-(vFeature.extent * x + center), -(vFeature.extent * y + center), 0).divide(features.scale); var allLayers = features.filter; if (!features.filter.loaded) { allLayers.forEach(function (l) { l.filterExpression = (0, _mapboxGlStyleSpec.featureFilter)(l.filter); }); features.filter.loaded = true; } sourceLayers.forEach(function (layer_id) { var sourceLayer = vectorTile.layers[layer_id]; var layersSource = allLayers.filter(function (l) { return sourceLayer.name == l['source-layer']; }); var _loop = function (i) { var vtFeature = sourceLayer.feature(i); var layers = layersSource.filter(function (l) { return l.filterExpression({ zoom: z }, vtFeature) && (!l.minzoom || l.minzoom <= z) && (!l.maxzoom || l.maxzoom >= z); }); var feature = void 0; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { var _loop2 = function () { var layer = _step.value; var tag = "".concat(layer.id, "_zoom_").concat(z); // Fix doens't pass instance of properties var style = styleCache.get(tag); if (!style) { style = new _Style["default"](); style.setFromVectorTileLayer(layer, extentSource.zoom, options.sprites); styleCache.set(tag, style); } var order = allLayers.findIndex(function (l) { return l.id == layer.id; }); if (!feature) { feature = features.requestFeatureById(layer.id, vtFeature.type - 1); feature.id = layer.id; feature.order = order; feature.style = style; vtFeatureToFeatureGeometry(vtFeature, feature); } else if (!features.features.find(function (f) { return f.id === layer.id; })) { feature = features.newFeatureByReference(feature); feature.id = layer.id; feature.order = order; feature.style = style; } }; for (var _iterator = layers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { _loop2(); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator["return"] != null) { _iterator["return"](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }; for (var i = sourceLayer.length - 1; i >= 0; i--) { _loop(i); } }); features.removeEmptyFeature(); // TODO verify if is needed to updateExtent for previous features. features.updateExtent(); features.features.sort(function (a, b) { return a.order - b.order; }); features.extent = extentSource; return Promise.resolve(features); } /** * @module VectorTileParser */ var _default = { /** * Parse a vector tile file and return a [Feature]{@link module:GeoJsonParser.Feature} * or an array of Features. While multiple formats of vector tile are * available, the only one supported for the moment is the * [Mapbox Vector Tile]{@link https://www.mapbox.com/vector-tiles/specification/}. * * @param {ArrayBuffer} file - The vector tile file to parse. * @param {Object} options - Options controlling the parsing. * @param {Extent} options.extent - The Extent to convert the input coordinates to. * @param {Extent=} options.filteringExtent - Optional filter to reject features * outside of this extent. * @param {boolean} [options.mergeFeatures=true] - If true all geometries are merged by type and multi-type * @param {boolean} [options.withNormal=true] - If true each coordinate normal is computed * @param {boolean} [options.withAltitude=true] - If true each coordinate altitude is kept * @param {function=} options.filter - Filter function to remove features. * @param {string=} options.isInverted - This option is to be set to the * correct value, true or false (default being false), if the computation of * the coordinates needs to be inverted to same scheme as OSM, Google Maps * or other system. See [this link]{@link * https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates} * for more informations. * * @return {Promise} A Promise resolving with a Feature or an array a * Features. */ parse: function parse(file, options) { return Promise.resolve(readPBF(file, options)); } }; exports["default"] = _default;