private Geometry ReadLineString(TileGeometryTransform tgs, IList <uint> geometry)
        {
            int currentIndex = 0; int currentX = 0; int currentY = 0;
            var sequences = ReadCoordinateSequences(tgs, geometry, ref currentIndex, ref currentX, ref currentY);

            return(CreateLineal(sequences));
        }
        /// <summary>
        /// Reads a Vector Tile stream.
        /// </summary>
        /// <param name="stream">Vector tile stream.</param>
        /// <param name="tileDefinition">Tile information.</param>
        /// <param name="idAttributeName">Optional. Specifies the name of the attribute that the vector tile feature's ID should be stored in the NetTopologySuite Features AttributeTable.</param>
        /// <returns></returns>
        public VectorTile Read(Stream stream, Tiles.Tile tileDefinition, string idAttributeName)
        {
            // Deserialize the tile
            var tile = ProtoBuf.Serializer.Deserialize <Mapbox.Tile>(stream);

            var vectorTile = new VectorTile {
                TileId = tileDefinition.Id
            };

            foreach (var mbTileLayer in tile.Layers)
            {
                Debug.Assert(mbTileLayer.Version == 2U);

                var tgs   = new TileGeometryTransform(tileDefinition, mbTileLayer.Extent);
                var layer = new Layer {
                    Name = mbTileLayer.Name
                };
                foreach (var mbTileFeature in mbTileLayer.Features)
                {
                    var feature = ReadFeature(tgs, mbTileLayer, mbTileFeature, idAttributeName);
                    layer.Features.Add(feature);
                }
                vectorTile.Layers.Add(layer);
            }

            return(vectorTile);
        }
        private Geometry ReadPolygon(TileGeometryTransform tgs, IList <uint> geometry)
        {
            int currentIndex = 0; int currentX = 0; int currentY = 0;
            var sequences = ReadCoordinateSequences(tgs, geometry, ref currentIndex, ref currentX, ref currentY, 1);

            return(CreatePolygonal(sequences));
        }
        private static IEnumerable <uint> Encode(IPolygonal polygonal, TileGeometryTransform tgt, int zoom)
        {
            var geometry = (Geometry)polygonal;

            //Test the whole polygon geometry is larger than a single pixel.
            if (IsGreaterThanOnePixelOfTile(geometry, zoom))
            {
                int currentX = 0, currentY = 0;
                for (int i = 0; i < geometry.NumGeometries; i++)
                {
                    var polygon = (Polygon)geometry.GetGeometryN(i);

                    //Test that individual polygons are larger than a single pixel.
                    if (!IsGreaterThanOnePixelOfTile(polygon, zoom))
                    {
                        continue;
                    }

                    foreach (uint encoded in Encode(polygon.Shell.CoordinateSequence, tgt, ref currentX, ref currentY, true, false))
                    {
                        yield return(encoded);
                    }
                    foreach (var hole in polygon.InteriorRings)
                    {
                        foreach (uint encoded in Encode(hole.CoordinateSequence, tgt, ref currentX, ref currentY, true, true))
                        {
                            yield return(encoded);
                        }
                    }
                }
            }
        }
        private static IEnumerable <uint> Encode(IPuntal puntal, TileGeometryTransform tgt)
        {
            const int CoordinateIndex = 0;

            var geometry = (Geometry)puntal;
            int currentX = 0, currentY = 0;

            var parameters = new List <uint>();

            for (int i = 0; i < geometry.NumGeometries; i++)
            {
                var point = (Point)geometry.GetGeometryN(i);
                (int x, int y) = tgt.Transform(point.CoordinateSequence, CoordinateIndex, ref currentX, ref currentY);
                if (i == 0 || x > 0 || y > 0)
                {
                    parameters.Add(GenerateParameterInteger(x));
                    parameters.Add(GenerateParameterInteger(y));
                }
            }

            // Return result
            yield return(GenerateCommandInteger(MapboxCommandType.MoveTo, parameters.Count / 2));

            foreach (uint parameter in parameters)
            {
                yield return(parameter);
            }
        }
        private IFeature ReadFeature(TileGeometryTransform tgs, Tile.Layer mbTileLayer, Tile.Feature mbTileFeature, string idAttributeName)
        {
            var geometry   = ReadGeometry(tgs, mbTileFeature.Type, mbTileFeature.Geometry);
            var attributes = ReadAttributeTable(mbTileFeature, mbTileLayer.Keys, mbTileLayer.Values);

            //Check to see if an id value is already captured in the attributes, if not, add it.
            if (!string.IsNullOrEmpty(idAttributeName) && !mbTileLayer.Keys.Contains(idAttributeName))
            {
                ulong id = mbTileFeature.Id;
                attributes.Add(idAttributeName, id);
            }

            return(new Feature(geometry, attributes));
        }
        private static IEnumerable <uint> Encode(ILineal lineal, TileGeometryTransform tgt)
        {
            var geometry = (Geometry)lineal;
            int currentX = 0, currentY = 0;

            for (int i = 0; i < geometry.NumGeometries; i++)
            {
                var lineString = (LineString)geometry.GetGeometryN(i);
                foreach (uint encoded in Encode(lineString.CoordinateSequence, tgt, ref currentX, ref currentY, false))
                {
                    yield return(encoded);
                }
            }
        }
        private Geometry ReadGeometry(TileGeometryTransform tgs, Tile.GeomType type, IList <uint> geometry)
        {
            switch (type)
            {
            case Tile.GeomType.Point:
                return(ReadPoint(tgs, geometry));

            case Tile.GeomType.LineString:
                return(ReadLineString(tgs, geometry));

            case Tile.GeomType.Polygon:
                return(ReadPolygon(tgs, geometry));
            }

            return(null);
        }
        private CoordinateSequence[] ReadSinglePointSequences(TileGeometryTransform tgs, IList <uint> geometry,
                                                              int numSequences, ref int currentIndex, ref int currentX, ref int currentY)
        {
            var res             = new CoordinateSequence[numSequences];
            var currentPosition = (currentX, currentY);

            for (int i = 0; i < numSequences; i++)
            {
                res[i] = _factory.CoordinateSequenceFactory.Create(1, 2);

                currentPosition = ParseOffset(currentPosition, geometry, ref currentIndex);
                TransformOffsetAndAddToSequence(tgs, currentPosition, res[i], 0);
            }

            currentX = currentPosition.currentX;
            currentY = currentPosition.currentY;
            return(res);
        }
 private void TransformOffsetAndAddToSequence(TileGeometryTransform tgs, (int x, int y) localPosition, CoordinateSequence sequence, int index)
        private CoordinateSequence[] ReadCoordinateSequences(
            TileGeometryTransform tgs, IList <uint> geometry,
            ref int currentIndex, ref int currentX, ref int currentY, int buffer = 0, bool forPoint = false)
        {
            (var command, int count) = ParseCommandInteger(geometry[currentIndex]);
            Debug.Assert(command == MapboxCommandType.MoveTo);
            if (count > 1)
            {
                currentIndex++;
                return(ReadSinglePointSequences(tgs, geometry, count, ref currentIndex, ref currentX, ref currentY));
            }

            var sequences       = new List <CoordinateSequence>();
            var currentPosition = (currentX, currentY);

            while (currentIndex < geometry.Count)
            {
                (command, count) = ParseCommandInteger(geometry[currentIndex++]);
                Debug.Assert(command == MapboxCommandType.MoveTo);
                Debug.Assert(count == 1);

                // Read the current position
                currentPosition = ParseOffset(currentPosition, geometry, ref currentIndex);

                if (!forPoint)
                {
                    // Read the next command (should be LineTo)
                    (command, count) = ParseCommandInteger(geometry[currentIndex++]);
                    if (command != MapboxCommandType.LineTo)
                    {
                        count = 0;
                    }
                }
                else
                {
                    count = 0;
                }

                // Create sequence, add starting point
                var sequence      = _factory.CoordinateSequenceFactory.Create(1 + count + buffer, 2);
                int sequenceIndex = 0;
                TransformOffsetAndAddToSequence(tgs, currentPosition, sequence, sequenceIndex++);

                // Read and add offsets
                for (int i = 1; i <= count; i++)
                {
                    currentPosition = ParseOffset(currentPosition, geometry, ref currentIndex);
                    TransformOffsetAndAddToSequence(tgs, currentPosition, sequence, sequenceIndex++);
                }

                // Check for ClosePath command
                if (currentIndex < geometry.Count)
                {
                    (command, _) = ParseCommandInteger(geometry[currentIndex]);
                    if (command == MapboxCommandType.ClosePath)
                    {
                        Debug.Assert(buffer > 0);
                        sequence.SetOrdinate(sequenceIndex, Ordinate.X, sequence.GetOrdinate(0, Ordinate.X));
                        sequence.SetOrdinate(sequenceIndex, Ordinate.Y, sequence.GetOrdinate(0, Ordinate.Y));

                        currentIndex++;
                        sequenceIndex++;
                    }
                }

                Debug.Assert(sequenceIndex == sequence.Count);

                sequences.Add(sequence);
            }

            // update current position values
            currentX = currentPosition.currentX;
            currentY = currentPosition.currentY;

            return(sequences.ToArray());
        }
        /// <summary>
        /// Writes the tile to the given stream.
        /// </summary>
        /// <param name="vectorTile">The vector tile.</param>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="extent">The extent.</param>
        /// <param name="idAttributeName">The name of an attribute property to use as the ID for the Feature. Vector tile feature ID's should be integer or ulong numbers.</param>
        public static void Write(this VectorTile vectorTile, Stream stream, uint extent = 4096, string idAttributeName = "id")
        {
            var tile = new Tiles.Tile(vectorTile.TileId);
            var tgt  = new TileGeometryTransform(tile, extent);

            var mapboxTile = new Mapbox.Tile();

            foreach (var localLayer in vectorTile.Layers)
            {
                var layer = new Mapbox.Tile.Layer {
                    Version = 2, Name = localLayer.Name, Extent = extent
                };

                var keys   = new Dictionary <string, uint>();
                var values = new Dictionary <Tile.Value, uint>();

                foreach (var localLayerFeature in localLayer.Features)
                {
                    var feature = new Mapbox.Tile.Feature();

                    // Encode geometry
                    switch (localLayerFeature.Geometry)
                    {
                    case IPuntal puntal:
                        feature.Type = Tile.GeomType.Point;
                        feature.Geometry.AddRange(Encode(puntal, tgt));
                        break;

                    case ILineal lineal:
                        feature.Type = Tile.GeomType.LineString;
                        feature.Geometry.AddRange(Encode(lineal, tgt));
                        break;

                    case IPolygonal polygonal:
                        feature.Type = Tile.GeomType.Polygon;
                        feature.Geometry.AddRange(Encode(polygonal, tgt, tile.Zoom));
                        break;

                    default:
                        feature.Type = Tile.GeomType.Unknown;
                        break;
                    }

                    // If geometry collapsed during encoding, we don't add the feature at all
                    if (feature.Geometry.Count == 0)
                    {
                        continue;
                    }

                    // Translate attributes for feature
                    AddAttributes(feature.Tags, keys, values, localLayerFeature.Attributes);

                    //Try and retrieve an ID from the attributes.
                    var id = localLayerFeature.Attributes.GetOptionalValue(idAttributeName);

                    //Converting ID to string, then trying to parse. This will handle situations will ignore situations where the ID value is not actually an integer or ulong number.
                    if (id != null && ulong.TryParse(id.ToString(), out ulong idVal))
                    {
                        feature.Id = idVal;
                    }

                    // Add feature to layer
                    layer.Features.Add(feature);
                }

                layer.Keys.AddRange(keys.Keys);
                layer.Values.AddRange(values.Keys);

                mapboxTile.Layers.Add(layer);
            }

            ProtoBuf.Serializer.Serialize <Tile>(stream, mapboxTile);
        }
        private static IEnumerable <uint> Encode(CoordinateSequence sequence, TileGeometryTransform tgt,
                                                 ref int currentX, ref int currentY,
                                                 bool ring = false, bool ccw = false)
        {
            // how many parameters for LineTo command
            int count = sequence.Count;

            // if we have a ring we need to check orientation
            if (ring)
            {
                if (ccw != Algorithm.Orientation.IsCCW(sequence))
                {
                    sequence = sequence.Copy();
                    CoordinateSequences.Reverse(sequence);
                }
            }
            var encoded = new List <uint>();

            // Start point
            encoded.Add(GenerateCommandInteger(MapboxCommandType.MoveTo, 1));
            var position = tgt.Transform(sequence, 0, ref currentX, ref currentY);

            encoded.Add(GenerateParameterInteger(position.x));
            encoded.Add(GenerateParameterInteger(position.y));

            // Add LineTo command (stub)
            int lineToCount = 0;

            encoded.Add(GenerateCommandInteger(MapboxCommandType.LineTo, lineToCount));
            for (int i = 1; i < count; i++)
            {
                position = tgt.Transform(sequence, i, ref currentX, ref currentY);

                if (position.x != 0 || position.y != 0)
                {
                    encoded.Add(GenerateParameterInteger(position.x));
                    encoded.Add(GenerateParameterInteger(position.y));
                    lineToCount++;
                }
            }
            if (lineToCount > 0)
            {
                encoded[3] = GenerateCommandInteger(MapboxCommandType.LineTo, lineToCount);
            }

            // Validate encoded data
            if (ring)
            {
                // A ring has 1 MoveTo and 1 LineTo command.
                // A ring is only valid if we have at least 3 points, otherwise collapse
                if (encoded.Count - 2 >= 6)
                {
                    encoded.Add(GenerateCommandInteger(MapboxCommandType.ClosePath, 1));
                }
                else
                {
                    encoded.Clear();
                }
            }
            else
            {
                // A line has 1 MoveTo and 1 LineTo command.
                // A line is valid if it has at least 2 points
                if (encoded.Count - 2 < 4)
                {
                    encoded.Clear();
                }
            }

            return(encoded);
        }