Example #1
0
        BoundingBox ConvertWorldToMap(BoundingBox bbox, int zoomLevel)
        {
            var bboxTopLeft     = TileUtils.LatLongToPixelXY(bbox.yMax, bbox.xMin, zoomLevel);
            var bboxBottomRight = TileUtils.LatLongToPixelXY(bbox.yMin, bbox.xMax, zoomLevel);

            return(new BoundingBox(bboxTopLeft.X, bboxBottomRight.X, bboxTopLeft.Y, bboxBottomRight.Y));
        }
Example #2
0
        public Uri BuildUri(ImageryProvider provider, int x, int y, int zoom)
        {
            string[] serverNodes = provider.UrlModel.Servers;
            string   server      = string.Empty;

            if (serverNodes != null && serverNodes.Length > 0)
            {
                _serverCycle = (_serverCycle + 1) % serverNodes.Length;
                server       = serverNodes[_serverCycle];
            }

            string url = provider.UrlModel.UrlFormat;

            url = url.Replace("{s}", server);
            url = url.Replace("{x}", x.ToString());
            url = url.Replace("{y}", y.ToString());
            url = url.Replace("{z}", zoom.ToString());
            url = url.Replace("{quadkey}", TileUtils.TileXYToQuadKey(x, y, zoom));
            if (url.Contains("{t}"))
            {
                var token = GetToken(provider);
                if (String.IsNullOrWhiteSpace(token))
                {
                    var message =
                        $"There is no token found for {provider.Name} provider. Make sure a user secrets are set with a {provider.TokenUserSecretsKey} value.";
                    _logger?.LogError(message);
                    throw new Exception(message);
                }

                url = url.Replace("{t}", token);
            }

            return(new Uri(url, UriKind.Absolute));
        }
Example #3
0
        public TileRange ComputeBoundingBoxTileRange(BoundingBox bbox, ImageryProvider provider, int minTilesPerImage = 4)
        {
            TileRange   tiles = new TileRange(provider);
            BoundingBox mapBbox;
            PointInt    topLeft;
            PointInt    bottomRight;

            // optimal zoom calculation (maybe there's a direct way)
            // calculate the size of the full bbox at increasing zoom levels
            // until the full image would be greater than a tile
            int zoom = 0;

            do
            {
                zoom++;

                // coords are pixels in global map image (see TileUtils.MapSize(zoom))
                topLeft     = TileUtils.LatLongToPixelXY(bbox.yMax, bbox.xMin, zoom);
                bottomRight = TileUtils.LatLongToPixelXY(bbox.yMin, bbox.xMax, zoom);
                mapBbox     = new BoundingBox(topLeft.X, bottomRight.X, topLeft.Y, bottomRight.Y);
            }while (zoom < provider.MaxZoom &&
                    (mapBbox.Width < provider.TileSize * minTilesPerImage && mapBbox.Height < provider.TileSize * minTilesPerImage));

            // now we have the minimum zoom without image
            // we can know which tiles are needed
            tiles.Start          = new MapTileInfo(TileUtils.PixelXYToTileXY(topLeft.X, topLeft.Y), zoom);
            tiles.End            = new MapTileInfo(TileUtils.PixelXYToTileXY(bottomRight.X, bottomRight.Y), zoom);
            tiles.AreaOfInterest = mapBbox;

            return(tiles);
        }
Example #4
0
        public List <Vector2> ComputeUVMap(HeightMap heightMap, TextureInfo textureInfo)
        {
            /**********************************
             * We need to map texture pixels to heightmap points
             * Linear mapping does not work because or Mercator projection distortion
             * Pseudo code :
             * for each point
             *   project to texture coordinates (pixelXY at same zoom than texture)
             *   get pixel offset from origin
             *   map this offset to (0 -> texWidth, 0 -> textHeight) => (0->1, 0->1)
             */
            List <Vector2> uvs  = new List <Vector2>(heightMap.Count);
            var            bbox = textureInfo.ProjectedBounds;

            foreach (GeoPoint geoPoint in heightMap.Coordinates)
            {
                PointInt projPoint = TileUtils.LatLongToPixelXY(geoPoint.Latitude, geoPoint.Longitude, textureInfo.ProjectedZoom);

                float xOffset = projPoint.X - (float)bbox.xMin;
                float uvX     = MathHelper.Map(1, textureInfo.Width, 0, 1, xOffset, true);

                float yOffset = projPoint.Y - (float)bbox.yMin;
                float uvY     = MathHelper.Map(1, textureInfo.Height, 0, 1, yOffset, true);

                uvs.Add(new Vector2(uvX, uvY));
            }

            return(uvs);
        }
Example #5
0
        BoundingBox GetTilesBoundingBox(TileRange tiles)
        {
            var bboxTopLeft     = TileUtils.TileXYToPixelXY(tiles.Min(tile => tile.TileInfo.X), tiles.Min(tile => tile.TileInfo.Y));
            var bboxBottomRight = TileUtils.TileXYToPixelXY(tiles.Max(tile => tile.TileInfo.X) + 1, tiles.Max(tile => tile.TileInfo.Y) + 1);

            return(new BoundingBox(bboxTopLeft.X, bboxBottomRight.X, bboxTopLeft.Y, bboxBottomRight.Y));
        }
Example #6
0
        public TileRange DownloadTiles(BoundingBox bbox, ImageryProvider provider, int minTilesPerImage = 4)
        {
            TileRange   tiles       = new TileRange(provider);
            BoundingBox mapBbox     = null;
            PointInt    topLeft     = new PointInt();
            PointInt    bottomRight = new PointInt();

            // optimal zoom calculation (maybe there's a direct way)
            // calculate the size of the full bbox at increasing zoom levels
            // until the full image would be greater than a tile
            int zoom = 0;

            do
            {
                zoom++;

                // coords are pixels in global map image (see TileUtils.MapSize(zoom))
                topLeft     = TileUtils.LatLongToPixelXY(bbox.yMax, bbox.xMin, zoom);
                bottomRight = TileUtils.LatLongToPixelXY(bbox.yMin, bbox.xMax, zoom);
                mapBbox     = new BoundingBox(topLeft.X, bottomRight.X, topLeft.Y, bottomRight.Y);
            }while (zoom < provider.MaxZoom &&
                    (mapBbox.Width < provider.TileSize * minTilesPerImage && mapBbox.Height < provider.TileSize * minTilesPerImage));

            // now we have the minimum zoom without image
            // we can know which tiles are needed
            tiles.Start          = new MapTileInfo(TileUtils.PixelXYToTileXY(topLeft.X, topLeft.Y), zoom);
            tiles.End            = new MapTileInfo(TileUtils.PixelXYToTileXY(bottomRight.X, bottomRight.Y), zoom);
            tiles.AreaOfInterest = mapBbox;

            // downdload tiles
            Stopwatch swDownload = Stopwatch.StartNew();

            _logger?.LogTrace("Starting images download");


            // 2 max download threads
            var options = new ParallelOptions()
            {
                MaxDegreeOfParallelism = provider.MaxDegreeOfParallelism
            };
            var range = tiles.EnumerateRange().ToList();

            _logger.LogInformation($"Downloading {range.Count} tiles...");

            Parallel.ForEach(range, options, tileInfo =>
            {
                Uri tileUri = BuildUri(provider, tileInfo.X, tileInfo.Y, tileInfo.Zoom);
                _logger?.LogInformation($"Downloading {tileUri}");

                var contentbytes = _httpClient.GetByteArrayAsync(tileUri).Result;
                tiles.Add(new MapTile(contentbytes, provider.TileSize, tileUri, tileInfo));
            }
                             );

            swDownload.Stop();
            _logger?.LogInformation($"DownloadImages done in : {swDownload.Elapsed:g}");


            return(tiles);
        }
Example #7
0
        BoundingBox ConvertWorldToMap(BoundingBox bbox, int zoomLevel, int tileSize)
        {
            var bboxTopLeft     = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMax, bbox.xMin), zoomLevel, tileSize);
            var bboxBottomRight = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMin, bbox.xMax), zoomLevel, tileSize);

            return(new BoundingBox(bboxTopLeft.X, bboxBottomRight.X, bboxTopLeft.Y, bboxBottomRight.Y, bbox.zMin, bbox.zMax));
        }
Example #8
0
        public TextureInfo ConstructTextureWithGpxTrack(TileRange tiles, BoundingBox bbox, string fileName,
                                                        TextureImageFormat mimeType, IEnumerable <GeoPoint> gpxPoints, bool drawGpxVertices = false, Rgba32 color = default(Rgba32), float lineWidth = 5f)
        {
            // where is the bbox in the final image ?

            // get pixel in full map
            int zoomLevel     = tiles.Tiles.First().TileInfo.Zoom;
            var projectedBbox = ConvertWorldToMap(bbox, zoomLevel, tiles.TileSize);
            var tilesBbox     = GetTilesBoundingBox(tiles);
            int xOffset       = (int)(tilesBbox.xMin - projectedBbox.xMin);
            int yOffset       = (int)(tilesBbox.yMin - projectedBbox.yMin);


            //DrawDebugBmpBbox(tiles, localBbox, tilesBbox, fileName, mimeType);
            int tileSize = tiles.TileSize;

            var pointsOnTexture = gpxPoints
                                  .Select(pt => TileUtils.PositionToGlobalPixel(new LatLong(pt.Latitude, pt.Longitude), zoomLevel, tiles.TileSize))
                                  .Select(pt => new PointF((float)(pt.X - (int)projectedBbox.xMin), (float)(pt.Y - (int)projectedBbox.yMin)));


            using (Image <Rgba32> outputImage = new Image <Rgba32>((int)projectedBbox.Width, (int)projectedBbox.Height))
            {
                foreach (var tile in tiles.Tiles)
                {
                    using (Image <Rgba32> tileImg = Image.Load(tile.Image))
                    {
                        int x = (tile.TileInfo.X - tiles.Start.X) * tileSize + xOffset;
                        int y = (tile.TileInfo.Y - tiles.Start.Y) * tileSize + yOffset;

                        outputImage.Mutate(o => o
                                           .DrawImage(tileImg, new Point(x, y), 1f)
                                           );
                    }
                }

                outputImage.Mutate(o => o
                                   .DrawLines(color == default(Rgba32) ? new Rgba32(1, 0, 0, 1f) : color, lineWidth, pointsOnTexture.ToArray())
                                   );

                if (drawGpxVertices)
                {
                    PathCollection pc = new PathCollection(pointsOnTexture.Select(p => new EllipsePolygon(p, new SizeF(10f, 10f))));
                    outputImage.Mutate(o => o.Draw(GraphicsOptions.Default, Pens.Solid(Rgba32.Violet, 3), pc));
                }

                // with encoder
                //IImageEncoder encoder = ConvertFormat(mimeType);
                //outputImage.Save(fileName, encoder);

                outputImage.Save(fileName);
            }

            return(new TextureInfo(fileName, mimeType, (int)projectedBbox.Width, (int)projectedBbox.Height, zoomLevel,
                                   projectedBbox));
        }
Example #9
0
        BoundingBox GetTilesBoundingBox(TileRange tiles)
        {
            var tileInfos   = tiles.TilesInfo.ToList();
            var bboxTopLeft =
                TileUtils.TileXYToGlobalPixel(tileInfos.Min(t => t.X), tileInfos.Min(t => t.Y), tiles.TileSize);
            var bboxBottomRight = TileUtils.TileXYToGlobalPixel(tileInfos.Max(t => t.X) + 1,
                                                                tileInfos.Max(t => t.Y) + 1, tiles.TileSize);

            return(new BoundingBox(bboxTopLeft.X, bboxBottomRight.X, bboxTopLeft.Y, bboxBottomRight.Y));
        }
Example #10
0
        public MapTileInfo ZoomIn(string quadIndex)
        {
            if (Zoom == 23)
            {
                return(this);
            }

            TileUtils.QuadKeyToTileXY(string.Concat(TileUtils.TileXYToQuadKey(X, Y, Zoom), quadIndex), out int x0, out int y0, out int z0);
            return(new MapTileInfo(x0, y0, z0, this.TileSize));
        }
Example #11
0
        public byte[] GenerateTile(int x, int y, int zoom)
        {
            byte[] tileBytes = null;
            var    font      = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);

            var corner        = TileUtils.TileXYToGlobalPixel(x, y, TileSize);
            var latLong       = TileUtils.GlobalPixelToPosition(new Point <double>(corner.X, corner.Y), zoom, TileSize);
            var latLongOffset = TileUtils.GlobalPixelToPosition(new Point <double>(corner.X + TileSize, corner.Y + TileSize), zoom, TileSize);



            var graticules = graticuleService.DrawCore(latLong, latLongOffset);


            using (Image <Rgba32> outputImage = new Image <Rgba32>(this.TileSize, this.TileSize))
            {
                string tileText = $"{x}/{y}/{zoom}{Environment.NewLine}";
                outputImage.Mutate(o => o
                                   .Fill(Rgba32.White)
                                   .DrawText(tileText, font, Rgba32.Black, new PointF(10, 10))
                                   );
                outputImage.Mutate(o => DrawGraticules(o, graticules, corner, zoom));



                // Test
                if (DebugPoint != null)
                {
                    var testPixel = TileUtils.PositionToGlobalPixel(new LatLong(DebugPoint.Latitude, DebugPoint.Longitude), zoom, TileSize);
                    var testTile  = TileUtils.GlobalPixelToTileXY(testPixel.X, testPixel.Y, TileSize);

                    // Draw test pixel
                    if (testTile.X == x && testTile.Y == y)
                    {
                        var basex = TileUtils.TileXYToGlobalPixel(x, y, TileSize);
                        var ptLoc = new PointF((float)(testPixel.X - basex.X), (float)(testPixel.Y - basex.Y));
                        outputImage.Mutate(o =>
                                           o.DrawLines(Rgba32.Blue, 1f,
                                                       new PointF[] { new PointF(ptLoc.X - 10, ptLoc.Y - 10), new PointF(ptLoc.X + 10, ptLoc.Y + 10) })
                                           .DrawLines(Rgba32.Blue, 1f,
                                                      new PointF[] { new PointF(ptLoc.X - 10, ptLoc.Y + 10), new PointF(ptLoc.X + 10, ptLoc.Y - 10) }));
                    }
                }

                using (MemoryStream ms = new MemoryStream())
                {
                    outputImage.SaveAsPng(ms);
                    tileBytes = ms.ToArray();
                }
            }

            return(tileBytes);
        }
Example #12
0
        public MapTileInfo ZoomOut()
        {
            if (Zoom == 1)
            {
                return(this);
            }

            var quadKey = TileUtils.TileXYToQuadKey(X, Y, Zoom);

            TileUtils.QuadKeyToTileXY(quadKey.Substring(0, quadKey.Length - 1), out int x0, out int y0, out int z0);
            return(new MapTileInfo(x0, y0, z0, this.TileSize));
        }
Example #13
0
        public TileRange ComputeBoundingBoxTileRangeForZoomLevel(BoundingBox bbox, ImageryProvider provider, int zoom)
        {
            TileRange      tiles       = new TileRange(provider);
            Point <double> topLeft     = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMax, bbox.xMin), zoom, provider.TileSize);
            Point <double> bottomRight = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMin, bbox.xMax), zoom, provider.TileSize);
            BoundingBox    mapBbox     = new BoundingBox(topLeft.X, bottomRight.X, topLeft.Y, bottomRight.Y);

            tiles.Start          = new MapTileInfo(TileUtils.GlobalPixelToTileXY(topLeft.X, topLeft.Y, provider.TileSize), zoom, provider.TileSize);
            tiles.End            = new MapTileInfo(TileUtils.GlobalPixelToTileXY(bottomRight.X, bottomRight.Y, provider.TileSize), zoom, provider.TileSize);
            tiles.AreaOfInterest = mapBbox;

            return(tiles);
        }
Example #14
0
        public TileRange ComputeBoundingBoxTileRangeForTargetResolution(BoundingBox bbox, ImageryProvider imageryProvider, int width, int height)
        {
            TileRange tiles = new TileRange(imageryProvider);

            TileUtils.BestMapView(new double[] { bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax }, width, height, 0, imageryProvider.TileSize, out double _, out double _, out double zoom);
            zoom = Math.Round(zoom, 0);
            var topLeft     = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMax, bbox.xMin), (int)zoom, imageryProvider.TileSize);
            var bottomRight = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMin, bbox.xMax), (int)zoom, imageryProvider.TileSize);
            var mapBbox     = new BoundingBox(topLeft.X, bottomRight.X, topLeft.Y, bottomRight.Y);

            tiles.Start          = new MapTileInfo(TileUtils.GlobalPixelToTileXY(topLeft.X, topLeft.Y, imageryProvider.TileSize), (int)zoom, imageryProvider.TileSize);
            tiles.End            = new MapTileInfo(TileUtils.GlobalPixelToTileXY(bottomRight.X, bottomRight.Y, imageryProvider.TileSize), (int)zoom, imageryProvider.TileSize);
            tiles.AreaOfInterest = mapBbox;
            return(tiles);
        }
Example #15
0
        private void DrawGraticules(IImageProcessingContext img, GraticuleLabels graticules, Point <double> corner, int zoom)
        {
            var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 8);

            foreach (var meridian in graticules.VerticalLabels)
            {
                var loc   = meridian.worldLocation;
                var pt    = TileUtils.PositionToGlobalPixel(new LatLong(loc.Lat, loc.Long), zoom, TileSize);
                var xpos  = pt.X - corner.X;
                var start = new PointF((float)xpos, 0);
                var end   = new PointF((float)xpos, TileSize);
                img.DrawLines(Rgba32.Gray, 1f, new PointF[] { start, end });
                try
                {
                    if (xpos < TileSize - 10)
                    {
                        img.DrawText(Math.Round(loc.Long, 2).ToString(), font, Rgba32.Black, new PointF((float)xpos, 50));
                    }
                }
                catch (Exception)
                {
                }
            }
            foreach (var parallel in graticules.HorizontalLabels)
            {
                var loc   = parallel.worldLocation;
                var pt    = TileUtils.PositionToGlobalPixel(new LatLong(loc.Lat, loc.Long), zoom, TileSize);
                var ypos  = pt.Y - corner.Y;
                var start = new PointF(0, (float)ypos);
                var end   = new PointF(TileSize, (float)ypos);
                img.DrawLines(Rgba32.Gray, 1f, new PointF[] { start, end });
                try
                {
                    img.DrawText(Math.Round(loc.Lat, 4).ToString(), font, Rgba32.Black, new PointF(50, (float)ypos));
                }
                catch (Exception)
                {
                }
            }
        }
Example #16
0
        public TileRange ComputeBoundingBoxTileRange(BoundingBox bbox, ImageryProvider provider,
                                                     int minTilesPerImage = 4)
        {
            // TODO good one, to test
            // texture quality would be expressed in tex size
            //TileUtils.BestMapView(new double[] { bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax }, 16384, 16384, 0, provider.TileSize, out double centerLat, out double centerLon, out double zoomBestView);

            TileRange      tiles = new TileRange(provider);
            BoundingBox    mapBbox;
            Point <double> topLeft;
            Point <double> bottomRight;

            // optimal zoom calculation (maybe there's a direct way)
            // see : https://docs.microsoft.com/fr-fr/azure/azure-maps/zoom-levels-and-tile-grid?tabs=csharp#tile-math-source-code (prepare tissues for nose bleed, and don't go if you're allergic to trigo and/or magical constants)
            // calculate the size of the full bbox at increasing zoom levels
            // until the full image would be greater than a tile
            int zoom    = 0;
            int maxSize = 256 * minTilesPerImage; /* fixed to 256px to limit number of tiles */

            do
            {
                zoom++;

                // coords are pixels in global map image (see TileUtils.MapSize(zoom))
                topLeft     = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMax, bbox.xMin), zoom, provider.TileSize);
                bottomRight = TileUtils.PositionToGlobalPixel(new LatLong(bbox.yMin, bbox.xMax), zoom, provider.TileSize);
                mapBbox     = new BoundingBox(topLeft.X, bottomRight.X, topLeft.Y, bottomRight.Y);
            } while (zoom < provider.MaxZoom &&
                     Math.Min(mapBbox.Width, mapBbox.Height) < maxSize);

            // now we have the minimum zoom without image
            // we can know which tiles are needed
            tiles.Start          = new MapTileInfo(TileUtils.GlobalPixelToTileXY(topLeft.X, topLeft.Y, provider.TileSize), zoom, provider.TileSize);
            tiles.End            = new MapTileInfo(TileUtils.GlobalPixelToTileXY(bottomRight.X, bottomRight.Y, provider.TileSize), zoom, provider.TileSize);
            tiles.AreaOfInterest = mapBbox;

            return(tiles);
        }
Example #17
0
        public TextureInfo ConstructTextureWithGpxTrack(TileRange tiles, BoundingBox bbox, string fileName, TextureImageFormat mimeType, IEnumerable <GeoPoint> gpxPoints)
        {
            // where is the bbox in the final image ?

            // get pixel in full map
            int zoomLevel     = tiles.First().TileInfo.Zoom;
            var projectedBbox = ConvertWorldToMap(bbox, zoomLevel);
            var tilesBbox     = GetTilesBoundingBox(tiles);
            int xOffset       = (int)(tilesBbox.xMin - projectedBbox.xMin);
            int yOffset       = (int)(tilesBbox.yMin - projectedBbox.yMin);


            //DrawDebugBmpBbox(tiles, localBbox, tilesBbox, fileName, mimeType);
            int tileSize = tiles.Provider.TileSize;

            var pointsOnTexture = gpxPoints.Select(pt => TileUtils.LatLongToPixelXY(pt.Latitude, pt.Longitude, zoomLevel))
                                  .Select(pt => new PointF(pt.X - (int)projectedBbox.xMin, pt.Y - (int)projectedBbox.yMin));

#if NETFULL
            ImageFormat format = mimeType == TextureImageFormat.image_jpeg ?
                                 ImageFormat.Jpeg
                 : ImageFormat.Png;

            using (Bitmap bmp = new Bitmap((int)projectedBbox.Width, (int)projectedBbox.Height))
            {
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    foreach (var tile in tiles)
                    {
                        using (MemoryStream stream = new MemoryStream(tile.Image))
                        {
                            using (Image tileImg = Image.FromStream(stream))
                            {
                                int x = (tile.TileInfo.X - tiles.Start.X) * tileSize + xOffset;
                                int y = (tile.TileInfo.Y - tiles.Start.Y) * tileSize + yOffset;
                                g.DrawImage(tileImg, x, y);
                            }
                        }
                    }
                }
                bmp.Save(fileName, format);
            }
            throw new NotImplementedException("GPX drawing not implemented yet in .Net Full");
#else
            using (Image <Rgba32> outputImage = new Image <Rgba32>((int)projectedBbox.Width, (int)projectedBbox.Height))
            {
                foreach (var tile in tiles)
                {
                    using (Image <Rgba32> tileImg = Image.Load(tile.Image))
                    {
                        int x = (tile.TileInfo.X - tiles.Start.X) * tileSize + xOffset;
                        int y = (tile.TileInfo.Y - tiles.Start.Y) * tileSize + yOffset;

                        outputImage.Mutate(o => o
                                           .DrawImage(tileImg, new Point(x, y), 1f)
                                           );
                    }
                }

                outputImage.Mutate(o => o
                                   .DrawLines(new Rgba32(1, 0, 0, 1f), 5f, pointsOnTexture.ToArray())
                                   );
                // with encoder
                //IImageEncoder encoder = ConvertFormat(mimeType);
                //outputImage.Save(fileName, encoder);

                outputImage.Save(fileName);
            }
#endif
            return(new TextureInfo(fileName, mimeType, (int)projectedBbox.Width, (int)projectedBbox.Height, zoomLevel, projectedBbox));
            //return new TextureInfo(fileName, format, (int)tilesBbox.Width, (int)tilesBbox.Height);
        }