//FIXME Implement projection handling goog.provide('ol.format.MVT'); goog.require('ol'); goog.require('ol.ext.PBF'); goog.require('ol.ext.vectortile.VectorTile'); goog.require('ol.format.Feature'); goog.require('ol.format.FormatType'); goog.require('ol.geom.GeometryLayout'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); goog.require('ol.geom.MultiLineString'); goog.require('ol.geom.MultiPoint'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.proj.Projection'); goog.require('ol.proj.Units'); goog.require('ol.render.Feature'); /** * @classdesc * Feature format for reading data in the Mapbox MVT format. * * @constructor * @extends {ol.format.Feature} * @param {olx.format.MVTOptions=} opt_options Options. * @api */ ol.format.MVT = function(opt_options) { ol.format.Feature.call(this); var options = opt_options ? opt_options : {}; /** * @type {ol.proj.Projection} */ this.defaultDataProjection = new ol.proj.Projection({ code: '', units: ol.proj.Units.TILE_PIXELS }); /** * @private * @type {function((ol.geom.Geometry|Object.)=)| * function(ol.geom.GeometryType,Array., * (Array.|Array.>),Object.,number)} */ this.featureClass_ = options.featureClass ? options.featureClass : ol.render.Feature; /** * @private * @type {string|undefined} */ this.geometryName_ = options.geometryName; /** * @private * @type {string} */ this.layerName_ = options.layerName ? options.layerName : 'layer'; /** * @private * @type {Array.} */ this.layers_ = options.layers ? options.layers : null; /** * @private * @type {ol.Extent} */ this.extent_ = null; }; ol.inherits(ol.format.MVT, ol.format.Feature); /** * @inheritDoc * @api */ ol.format.MVT.prototype.getLastExtent = function() { return this.extent_; }; /** * @inheritDoc */ ol.format.MVT.prototype.getType = function() { return ol.format.FormatType.ARRAY_BUFFER; }; /** * @private * @param {Object} rawFeature Raw Mapbox feature. * @param {string} layer Layer. * @param {olx.format.ReadOptions=} opt_options Read options. * @return {ol.Feature} Feature. */ ol.format.MVT.prototype.readFeature_ = function( rawFeature, layer, opt_options) { var feature = new this.featureClass_(); var id = rawFeature.id; var values = rawFeature.properties; values[this.layerName_] = layer; if (this.geometryName_) { feature.setGeometryName(this.geometryName_); } var geometry = ol.format.Feature.transformWithOptions( ol.format.MVT.readGeometry_(rawFeature), false, this.adaptOptions(opt_options)); feature.setGeometry(geometry); feature.setId(id); feature.setProperties(values); return feature; }; /** * @private * @param {Object} rawFeature Raw Mapbox feature. * @param {string} layer Layer. * @return {ol.render.Feature} Feature. */ ol.format.MVT.prototype.readRenderFeature_ = function(rawFeature, layer) { var coords = rawFeature.loadGeometry(); var ends = []; var flatCoordinates = []; ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends); var type = rawFeature.type; /** @type {ol.geom.GeometryType} */ var geometryType; if (type === 1) { geometryType = coords.length === 1 ? ol.geom.GeometryType.POINT : ol.geom.GeometryType.MULTI_POINT; } else if (type === 2) { if (coords.length === 1) { geometryType = ol.geom.GeometryType.LINE_STRING; } else { geometryType = ol.geom.GeometryType.MULTI_LINE_STRING; } } else if (type === 3) { geometryType = ol.geom.GeometryType.POLYGON; } var values = rawFeature.properties; values[this.layerName_] = layer; var id = rawFeature.id; return new this.featureClass_(geometryType, flatCoordinates, ends, values, id); }; /** * @inheritDoc * @api */ ol.format.MVT.prototype.readFeatures = function(source, opt_options) { var layers = this.layers_; var pbf = new ol.ext.PBF(/** @type {ArrayBuffer} */ (source)); var tile = new ol.ext.vectortile.VectorTile(pbf); var features = []; var featureClass = this.featureClass_; var layer, feature; for (var name in tile.layers) { if (layers && layers.indexOf(name) == -1) { continue; } layer = tile.layers[name]; var rawFeature; for (var i = 0, ii = layer.length; i < ii; ++i) { rawFeature = layer.feature(i); if (featureClass === ol.render.Feature) { feature = this.readRenderFeature_(rawFeature, name); } else { feature = this.readFeature_(rawFeature, name, opt_options); } features.push(feature); } this.extent_ = layer ? [0, 0, layer.extent, layer.extent] : null; } return features; }; /** * @inheritDoc * @api */ ol.format.MVT.prototype.readProjection = function(source) { return this.defaultDataProjection; }; /** * Sets the layers that features will be read from. * @param {Array.} layers Layers. * @api */ ol.format.MVT.prototype.setLayers = function(layers) { this.layers_ = layers; }; /** * @private * @param {Object} coords Raw feature coordinates. * @param {Array.} flatCoordinates Flat coordinates to be populated by * this function. * @param {Array.} ends Ends to be populated by this function. */ ol.format.MVT.calculateFlatCoordinates_ = function( coords, flatCoordinates, ends) { var end = 0; for (var i = 0, ii = coords.length; i < ii; ++i) { var line = coords[i]; var j, jj; for (j = 0, jj = line.length; j < jj; ++j) { var coord = line[j]; // Non-tilespace coords can be calculated here when a TileGrid and // TileCoord are known. flatCoordinates.push(coord.x, coord.y); } end += 2 * j; ends.push(end); } }; /** * @private * @param {Object} rawFeature Raw Mapbox feature. * @return {ol.geom.Geometry} Geometry. */ ol.format.MVT.readGeometry_ = function(rawFeature) { var type = rawFeature.type; if (type === 0) { return null; } var coords = rawFeature.loadGeometry(); var ends = []; var flatCoordinates = []; ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends); var geom; if (type === 1) { geom = coords.length === 1 ? new ol.geom.Point(null) : new ol.geom.MultiPoint(null); } else if (type === 2) { if (coords.length === 1) { geom = new ol.geom.LineString(null); } else { geom = new ol.geom.MultiLineString(null); } } else if (type === 3) { geom = new ol.geom.Polygon(null); } geom.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates, ends); return geom; }; /** * Not implemented. * @override */ ol.format.MVT.prototype.readFeature = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.readGeometry = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.writeFeature = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.writeGeometry = function() {}; /** * Not implemented. * @override */ ol.format.MVT.prototype.writeFeatures = function() {};