/// <summary> /// Converts a Mapbox feature in Mapbox coordinates into a VectorTileFeature /// </summary> /// <param name="tileInfo">TileInfo for tile informations like left top coordinates</param> /// <param name="layerName">Name of vector tile layer to which this vector tile feature belongs</param> /// <param name="feature">Mapbox feature to convert</param> /// <param name="keys">List of known keys for this tile</param> /// <param name="values">List of known values for this tile</param> /// <param name="extent">Extent/width of this Mapbox formated tile (normally 4096)</param> /// <param name="scale">Factor for scaling of coordinates because of overzooming</param> /// <param name="offsetX">Offset in X direction because of overzooming</param> /// <param name="offsetY">Offset in Y direction because of overzooming</param> /// <returns></returns> public static VectorTileFeature Parse(TileInfo tileInfo, string layerName, Feature feature, List <string> keys, List <Value> values, uint extent, Overzoom overzoom, float scale) { var vtf = new VectorTileFeature(layerName, feature.Id.ToString()); var geometries = GeometryParser.ParseGeometry(feature.Geometry, feature.Type, overzoom, scale); int i; // Add the geometry switch (feature.Type) { case GeomType.Point: // Convert all Points if (geometries.Count == 1) { // Single point vtf.Geometry = new Point(geometries[0][0]); } else { // Multi point var multiPoints = new List <Point>(); foreach (var points in geometries) { foreach (var point in points) { multiPoints.Add(new Point(point)); } } vtf.Geometry = new MultiPoint(multiPoints.ToArray()); } break; case GeomType.LineString: // Convert all LineStrings if (geometries.Count == 1) { // Single line vtf.Geometry = new LineString(geometries[0].ToArray()); } else { // Multi line var multiLines = new LineString[geometries.Count]; for (i = 0; i < geometries.Count; i++) { multiLines[i] = new LineString(geometries[i].ToArray()); } vtf.Geometry = new MultiLineString(multiLines); } break; case GeomType.Polygon: // Convert all Polygons var polygons = new List <Polygon>(); LinearRing polygon = null; List <LinearRing> holes = new List <LinearRing>(); i = 0; do { // Check, if first and last are the same points if (!geometries[i].First().Equals(geometries[i].Last())) { geometries[i].Add(geometries[i].First()); } // Convert all points of this ring var ring = new LinearRing(geometries[i].ToArray()); // We must use CCW instead of CW, because y axis is oriented from up to down. if (ring.IsCCW && polygon != null) { holes.Add(ring); } else { if (polygon != null) { polygons.Add(new Polygon(polygon, holes.ToArray())); } polygon = ring; holes.Clear(); } i++; } while (i < geometries.Count); // Save last one polygons.Add(new Polygon(polygon, holes.ToArray())); // Now save correct geometry if (polygons.Count == 1) { vtf.Geometry = polygons[0]; } else { vtf.Geometry = new MultiPolygon(polygons.ToArray()); } break; } // now add the tags vtf.Tags.Add(TagsParser.Parse(keys, values, feature.Tags)); return(vtf); }
/// <summary> /// Parses a unzipped tile in Mapbox format /// </summary> /// <param name="tileInfo">TileInfo of this tile</param> /// <param name="stream">Stream containing tile data in Pbf format</param> /// <param name="scale">Factor for scaling of coordinates because of overzooming</param> /// <param name="offsetX">Offset in X direction because of overzooming</param> /// <param name="offsetY">Offset in Y direction because of overzooming</param> /// <returns>List of VectorTileLayers, which contain Name and VectorTilesFeatures of each layer, this tile containing</returns> public static IList <VectorTileFeature> Parse(TileInfo tileInfo, Stream stream, Overzoom overzoom, float scale) { // Get tile information from Pbf format var tile = Serializer.Deserialize <Tile>(stream); // Create new vector tile layer var features = new List <VectorTileFeature>(); foreach (var layer in tile.Layers) { // Convert all features from Mapbox format into Mapsui format foreach (var feature in layer.Features) { var vectorTileFeature = FeatureParser.Parse(tileInfo, layer.Name, feature, layer.Keys, layer.Values, layer.Extent, overzoom, scale); // Add to layer features.Add(vectorTileFeature); } } return(features); }
/// <summary> /// Convert Mapbox tile format (see https://www.mapbox.com/vector-tiles/specification/) /// </summary> /// <param name="geom">Geometry information in Mapbox format</param> /// <param name="geomType">GeometryType of this geometry</param> /// <param name="scale">Factor for scaling of coordinates because of overzooming</param> /// <param name="offsetX">Offset in X direction because of overzooming</param> /// <param name="offsetY">Offset in Y direction because of overzooming</param> /// <returns>List of list of points in world coordinates</returns> public static List <List <Coordinate> > ParseGeometry(List <uint> geom, GeomType geomType, Overzoom overzoom, float scale) { const uint cmdMoveTo = 1; //const uint cmdLineTo = 2; const uint cmdSegEnd = 7; //const uint cmdBits = 3; long x = 0; long y = 0; var coordsList = new List <List <Coordinate> >(); List <Coordinate> coords = null; var geometryCount = geom.Count; uint length = 0; uint command = 0; var i = 0; while (i < geometryCount) { if (length <= 0) { length = geom[i++]; command = length & ((1 << 3) - 1); length = length >> 3; } if (length > 0) { if (command == cmdMoveTo) { coords = new List <Coordinate>(); coordsList.Add(coords); } } if (command == cmdSegEnd) { if (geomType != GeomType.Point && coords?.Count != 0) { coords?.Add(coords[0]); } length--; continue; } var dx = geom[i++]; var dy = geom[i++]; length--; var ldx = ZigZag.Decode(dx); var ldy = ZigZag.Decode(dy); x = x + ldx; y = y + ldy; // Correct coordinates for overzoom var coord = new Coordinate((x * overzoom.Scale - overzoom.OffsetX) * scale, (y * overzoom.Scale - overzoom.OffsetY) * scale); coords?.Add(coord); } return(coordsList); }