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); }
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; int maxSize = provider.TileSize * minTilesPerImage; 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 < maxSize || mapBbox.Height < maxSize)); // 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); }
public byte[] GenerateTile(int x, int y, int zoom) { byte[] tileBytes = null; var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var corner = TileUtils.TileXYToPixelXY(x, y); var latLong = TileUtils.PixelXYToLatLong(corner.X, corner.Y, zoom); var latLongOffset = TileUtils.PixelXYToLatLong(corner.X + TileSize, corner.Y + TileSize, zoom); 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.LatLongToPixelXY(DebugPoint.Latitude, DebugPoint.Longitude, zoom); var testTile = TileUtils.PixelXYToTileXY(testPixel.X, testPixel.Y); // Draw test pixel if (testTile.X == x && testTile.Y == y) { var basex = TileUtils.TileXYToPixelXY(x, y); var ptLoc = new PointF(testPixel.X - basex.X, 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); }