Example #1
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 #2
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);
        }
Example #3
0
        public TextureInfo ConstructTexture(TileRange tiles, BoundingBox bbox, string fileName, TextureImageFormat mimeType)
        {
            // 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);

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

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

            using (Bitmap bmp = new Bitmap((int)projectedBbox.Width, (int)projectedBbox.Height))
            {
                int xOffset = (int)(tilesBbox.xMin - projectedBbox.xMin);
                int yOffset = (int)(tilesBbox.yMin - projectedBbox.yMin);
                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);
            }
#else
            using (Image <Rgba32> outputImage = new Image <Rgba32>((int)projectedBbox.Width, (int)projectedBbox.Height))
            {
                int xOffset = (int)(tilesBbox.xMin - projectedBbox.xMin);
                int yOffset = (int)(tilesBbox.yMin - projectedBbox.yMin);

                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)
                                           );
                    }
                }

                // 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);
        }
Example #4
0
        public TileRange DownloadTiles(BoundingBox bbox, ImageryProvider provider, int minTilesPerImage = 4)
        {
            TileRange tiles = this.ComputeBoundingBoxTileRange(bbox, provider, minTilesPerImage);

            return(this.DownloadTiles(tiles, provider));
        }
Example #5
0
        public TextureInfo ConstructTexture(TileRange tiles, BoundingBox bbox, string fileName,
                                            TextureImageFormat mimeType, float quality = 0.98f)
        {
            // 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);

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

            using (Image <Rgba32> outputImage = new Image <Rgba32>((int)Math.Ceiling(projectedBbox.Width), (int)Math.Ceiling(projectedBbox.Height)))
            {
                int xOffset = (int)(tilesBbox.xMin - projectedBbox.xMin);
                int yOffset = (int)(tilesBbox.yMin - projectedBbox.yMin);

                foreach (var tile in tiles.Tiles)
                {
                    try
                    {
                        int x = (tile.TileInfo.X - tiles.Start.X) * tileSize + xOffset;
                        int y = (tile.TileInfo.Y - tiles.Start.Y) * tileSize + yOffset;

                        if (x >= projectedBbox.Width || y >= projectedBbox.Height)
                        {
                            continue;
                        }

                        if (tile.Image.Length == 0)
                        {
                            _logger.LogWarning($"Tile {tile.Uri} is empty. Skipping.");
                            continue;
                        }
                        using (Image <Rgba32> tileImg = Image.Load(tile.Image))
                        {
                            outputImage.Mutate(o => o
                                               .DrawImage(tileImg, new Point(x, y), 1f)
                                               );
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError($"Error while generating texture: {ex.Message}");
                    }
                }

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

                // Make image power of two dimensions
                if (options.PowerOfTwoImages)
                {
                    int nearestPowerOf2 = ToNextNearestPowerOf2(Math.Max(outputImage.Width, outputImage.Height));
                    outputImage.Mutate(o => o.Resize(nearestPowerOf2, nearestPowerOf2));
                }

                SaveImage(outputImage, fileName, quality);
            }

            return(new TextureInfo(fileName, mimeType, (int)Math.Ceiling(projectedBbox.Width), (int)Math.Ceiling(projectedBbox.Height), zoomLevel,
                                   projectedBbox, tiles.Count));
        }
Example #6
0
        public TileRange DownloadTiles(TileRange tiles, ImageryProvider provider)
        {
            Stopwatch sw         = Stopwatch.StartNew();
            int       intervalMs = 1000;

            if (provider is ITileGenerator generator)
            {
                // download tiles
                using (TimeSpanBlock timer = new TimeSpanBlock("Tile generation", _logger))
                {
                    // Max download threads defined in provider
                    var parallelOptions = new ParallelOptions()
                    {
                        MaxDegreeOfParallelism = provider.MaxDegreeOfParallelism
                    };
                    var range = tiles.TilesInfo.ToList();
                    _logger?.LogInformation($"Generating {range.Count} tiles with {provider.Name} generator...");
                    Parallel.ForEach(range, parallelOptions, tile =>
                    {
                        var contentbytes = generator.GenerateTile(tile.X, tile.Y, tile.Zoom);
                        tiles.Add(new MapTile(contentbytes, provider.TileSize, null, tile));
                    }
                                     );
                }
            }
            else
            {
                // download tiles
                Stopwatch swDownload = Stopwatch.StartNew();
                _logger?.LogTrace("Starting images download");


                // Max download threads defined in provider
                var parallelOptions = new ParallelOptions()
                {
                    MaxDegreeOfParallelism = provider.MaxDegreeOfParallelism
                };
                var range = tiles.TilesInfo.ToList();
                int numTilesDownloaded = 0;
                _logger?.LogInformation($"Downloading {range.Count} tiles...");
                try
                {
                    Parallel.ForEach(range, parallelOptions, (tile, state) =>
                    {
                        Uri tileUri = BuildUri(provider, tile.X, tile.Y, tile.Zoom);

                        var contentBytes = cache.GetTile(tileUri, provider, tile);

                        tiles.Add(new MapTile(contentBytes, provider.TileSize, tileUri, tile));

                        Interlocked.Increment(ref numTilesDownloaded);

                        if (sw.ElapsedMilliseconds > intervalMs)
                        {
                            _logger.LogInformation($"{numTilesDownloaded:N0}/{range.Count:N0} tiles downloaded...");
                            sw.Restart();
                        }
                    }
                                     );
                }
                catch (AggregateException ex)
                {
                    throw ex.GetInnerMostException();
                }
                catch (Exception)
                {
                    throw;
                }


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


            return(tiles);
        }