/// <summary>
        /// Gets a vector tile.
        /// </summary>
        public Result <VectorTile> GetVectorTile(ulong tileId)
        {
            try
            {
                var tile = new Itinero.VectorTiles.Tiles.Tile(tileId);
                var z    = tile.Zoom;

                var config = new SegmentLayerConfig()
                {
                    Name = "transportation",
                    IncludeProfileFunc = (p, m) =>
                    {
                        if (z > Program.ProfilesPerZoom.Length)
                        {
                            return(true);
                        }
                        var profileSet = Program.ProfilesPerZoom[z];
                        if (profileSet == null)
                        {
                            return(false);
                        }
                        return(profileSet.Contains(p));
                    }
                };

                return(new Result <VectorTile>(Program.RouterDb.ExtractTile(tileId, config)));
            }
            catch (System.Exception ex)
            {
                return(new Result <VectorTile>(ex.Message));
            }
        }
        /// <summary>
        /// Extracts a segment layer for the given tile.
        /// </summary>
        public static SegmentLayer ExtractSegmentLayer(this RouterDb routerDb, ulong tileId,
                                                       SegmentLayerConfig config)
        {
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            if (config.Name == null)
            {
                throw new ArgumentException("Layer configuration has no name set.");
            }

            return(new SegmentLayer()
            {
                Meta = routerDb.EdgeMeta,
                Profiles = routerDb.EdgeProfiles,
                Name = config.Name,
                Segments = routerDb.ExtractSegments(tileId, config)
            });
        }
        /// <summary>
        /// Extracts a vector tile of the given tile.
        /// </summary>
        public static VectorTile ExtractTile(this RouterDb routerDb, ulong tileId,
                                             SegmentLayerConfig config)
        {
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            if (config.Name == null)
            {
                throw new ArgumentException("Layer configuration has no name set.");
            }

            var layers = new List <Layer>(1);

            layers.Add(routerDb.ExtractSegmentLayer(tileId, config));

            return(new VectorTile()
            {
                Layers = layers,
                TileId = tileId
            });
        }
        /// <summary>
        /// Extracts segments for the given tile.
        /// </summary>
        public static Segment[] ExtractSegments(this RouterDb routerDb, ulong tileId,
                                                SegmentLayerConfig config)
        {
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            if (config.Name == null)
            {
                throw new ArgumentException("Layer configuration has no name set.");
            }

            var tile    = new Tile(tileId);
            var diffX   = (tile.Top - tile.Bottom);
            var diffY   = (tile.Right - tile.Left);
            var marginX = diffX / 1024;
            var marginY = diffY / 1024;

            var tileBox = new LocalGeo.Box(tile.Bottom - marginY, tile.Left - marginX,
                                           tile.Top + marginY, tile.Right + marginX);
            var segments = new List <Segment>();

            var vertices = HilbertExtensions.Search(routerDb.Network.GeometricGraph,
                                                    tileBox.MinLat - diffY, tileBox.MinLon - diffX,
                                                    tileBox.MaxLat + diffY, tileBox.MaxLon + diffX);
            var edges = new HashSet <long>();

            var edgeEnumerator = routerDb.Network.GetEdgeEnumerator();

            foreach (var vertex in vertices)
            {
                var coordinateFrom = routerDb.Network.GetVertex(vertex);

                edgeEnumerator.MoveTo(vertex);
                edgeEnumerator.Reset();
                while (edgeEnumerator.MoveNext())
                {
                    if (edges.Contains(edgeEnumerator.Id))
                    {
                        continue;
                    }
                    edges.Add(edgeEnumerator.Id);

                    // loop over shape.
                    var edgeData = edgeEnumerator.Data;

                    // check if this edge needs to be included or not.
                    if (config != null && config.IncludeProfileFunc != null &&
                        !config.IncludeProfileFunc(edgeData.Profile, edgeData.MetaId))
                    { // include profile returns false
                        continue;
                    }

                    // get shape.
                    var coordinateTo = routerDb.Network.GetVertex(edgeEnumerator.To);
                    var shape        = new List <Coordinate>();
                    var enumShape    = routerDb.Network.GetShape(edgeEnumerator.Current);

                    // reverse shape if edge is reversed.
                    if (edgeEnumerator.DataInverted)
                    {
                        enumShape.Reverse();
                    }

                    // split at tile edges.
                    var previous = false;
                    for (var i = 0; i < enumShape.Count; i++)
                    {
                        var location = enumShape[i];
                        if (tileBox.Overlaps(location.Latitude, location.Longitude))
                        {
                            if (previous == false && i > 0)
                            { // come up with intersection point and add that first.
                                var intersection = tileBox.Intersection(new Line(location, enumShape[i - 1]));
                                if (intersection != null)
                                {
                                    shape.Add(intersection.Value);
                                }
                            }

                            // add location.
                            shape.Add(location);
                            previous = true;
                        }
                        else if (previous)
                        { // come up with intersection point and add that as last point.
                            var intersection = tileBox.Intersection(new Line(location, enumShape[i - 1]));
                            if (intersection != null)
                            {
                                shape.Add(intersection.Value);
                            }

                            segments.Add(new Segment()
                            {
                                Meta    = edgeData.MetaId,
                                Profile = edgeData.Profile,
                                Shape   = shape.ToArray()
                            });
                            shape.Clear();
                            previous = false;
                        }
                    }

                    if (shape.Count >= 2)
                    {
                        segments.Add(new Segment()
                        {
                            Meta    = edgeData.MetaId,
                            Profile = edgeData.Profile,
                            Shape   = shape.ToArray()
                        });
                        shape.Clear();
                    }
                }
            }

            return(segments.ToArray());
        }