public void CanCalculateZoomLevel() { Assert.Equal(1, TileServiceUtils.CalculateZoomLevel(Math.PI / 2, Math.PI)); Assert.Equal(11, TileServiceUtils.CalculateZoomLevel(Math.PI / 2000, Math.PI / 1000)); Assert.Equal(21, TileServiceUtils.CalculateZoomLevel(Math.PI / 2000000, Math.PI / 1000000)); Assert.Equal(24, TileServiceUtils.CalculateZoomLevel(Math.PI / 2000000000, Math.PI / 1000000000)); }
public void RoundingZoomLevelTest() { double diff1 = 0.000365585907798893; double diff2 = 0.000766990393942846; double diff1_1 = 0.000365339467977011; double diff2_1 = 0.000766990393942818; var res1 = TileServiceUtils.CalculateZoomLevel(diff1, diff2); var res2 = TileServiceUtils.CalculateZoomLevel(diff1_1, diff2_1); Assert.Equal(res1, res2); Assert.Equal(13, res1); }
/// <summary> /// Get the map parameters for the report tile /// </summary> public MapParameters GetMapParameters(string bbox, int width, int height, bool addMargin, bool adjustBoundingBox) { log.LogDebug($"GetMapParameters: bbox={bbox}, width={width}, height={height}, addMargin={addMargin}, adjustBoundingBox={adjustBoundingBox}"); var bboxRadians = boundingBoxHelper.GetBoundingBox(bbox); MapBoundingBox mapBox = new MapBoundingBox { minLat = bboxRadians.BottomLeftLat, minLng = bboxRadians.BottomLeftLon, maxLat = bboxRadians.TopRightLat, maxLng = bboxRadians.TopRightLon }; int zoomLevel = TileServiceUtils.CalculateZoomLevel(mapBox.maxLat - mapBox.minLat, mapBox.maxLng - mapBox.minLng); long numTiles = TileServiceUtils.NumberOfTiles(zoomLevel); MapParameters parameters = new MapParameters { bbox = mapBox, zoomLevel = zoomLevel, numTiles = numTiles, mapWidth = width, mapHeight = height, addMargin = addMargin }; if (adjustBoundingBox) { boundingBoxService.AdjustBoundingBoxToFit(parameters); } parameters.pixelTopLeft = TileServiceUtils.LatLngToPixel(mapBox.maxLat, mapBox.minLng, parameters.numTiles); log.LogDebug("MapParameters: " + JsonConvert.SerializeObject(parameters)); return(parameters); }
protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { var request = item as DxfTileRequest; if (request == null) { ThrowRequestTypeCastException <DxfTileRequest>(); } string filespaceId = FileDescriptorExtensions.GetFileSpaceId(configStore, log); //Calculate zoom level int zoomLevel = TileServiceUtils.CalculateZoomLevel(request.bbox.TopRightLat - request.bbox.BottomLeftLat, request.bbox.TopRightLon - request.bbox.BottomLeftLon); log.LogDebug("DxfTileExecutor: BBOX differences {0} {1} {2}", request.bbox.TopRightLat - request.bbox.BottomLeftLat, request.bbox.TopRightLon - request.bbox.BottomLeftLon, zoomLevel); int numTiles = TileServiceUtils.NumberOfTiles(zoomLevel); Point topLeftLatLng = new Point(request.bbox.TopRightLat.LatRadiansToDegrees(), request.bbox.BottomLeftLon.LonRadiansToDegrees()); Point topLeftTile = WebMercatorProjection.LatLngToTile(topLeftLatLng, numTiles); log.LogDebug("DxfTileExecutor: zoomLevel={0}, numTiles={1}, xtile={2}, ytile={3}", zoomLevel, numTiles, topLeftTile.x, topLeftTile.y); log.LogDebug("DxfTileExecutor: {0} files", request.files.Count()); //Short circuit overlaying if there no files to overlay as ForAll is an expensive operation if (!request.files.Any()) { byte[] emptyOverlayData = null; using (Bitmap bitmap = new Bitmap(WebMercatorProjection.TILE_SIZE, WebMercatorProjection.TILE_SIZE)) { emptyOverlayData = bitmap.BitmapToByteArray(); } return(new TileResult(emptyOverlayData)); } log.LogDebug(string.Join(",", request.files.Select(f => f.Name).ToList())); List <byte[]> tileList = new List <byte[]>(); var fileTasks = request.files.Select(async file => { //foreach (var file in request.files) //Check file type to see if it has tiles if (file.ImportedFileType == ImportedFileType.Alignment || file.ImportedFileType == ImportedFileType.DesignSurface || file.ImportedFileType == ImportedFileType.Linework) { if (zoomLevel >= file.MinZoomLevel) { var suffix = FileUtils.GeneratedFileSuffix(file.ImportedFileType); string generatedName = FileUtils.GeneratedFileName(file.Name, suffix, FileUtils.DXF_FILE_EXTENSION); byte[] tileData = null; if (zoomLevel <= file.MaxZoomLevel || file.MaxZoomLevel == 0) //0 means not calculated { tileData = await GetTileAtRequestedZoom(topLeftTile, zoomLevel, file.Path, generatedName, filespaceId); } else if (zoomLevel - file.MaxZoomLevel <= 5) //Don't try to scale if the difference is too excessive { tileData = await GetTileAtHigherZoom(topLeftTile, zoomLevel, file.Path, generatedName, filespaceId, file.MaxZoomLevel, numTiles); } else { log.LogDebug( "DxfTileExecutor: difference between requested and maximum zooms too large; not even going to try to scale tile"); } if (tileData != null) { tileList.Add(tileData); } } } }); await Task.WhenAll(fileTasks); //Overlay the tiles. Return an empty tile if none to overlay. log.LogDebug("DxfTileExecutor: Overlaying {0} tiles", tileList.Count); byte[] overlayData = null; System.Drawing.Point origin = new System.Drawing.Point(0, 0); using (Bitmap bitmap = new Bitmap(WebMercatorProjection.TILE_SIZE, WebMercatorProjection.TILE_SIZE)) using (Graphics g = Graphics.FromImage(bitmap)) { foreach (byte[] tileData in tileList) { using (var tileStream = new MemoryStream(tileData)) { Image image = Image.FromStream(tileStream); g.DrawImage(image, origin); } } overlayData = bitmap.BitmapToByteArray(); } return(new TileResult(overlayData)); }
protected override async Task <ContractExecutionResult> ProcessAsyncEx <T>(T item) { List <FileData> files = null; int zoomLevel = 0; Point topLeftTile = null; int numTiles = 0; BoundingBox2DLatLon bbox = null; if (item is DxfTileRequest request) { files = request.files?.ToList(); bbox = request.bbox; //Calculate zoom level zoomLevel = TileServiceUtils.CalculateZoomLevel(request.bbox.TopRightLat - request.bbox.BottomLeftLat, request.bbox.TopRightLon - request.bbox.BottomLeftLon); log.LogDebug("DxfTileExecutor: BBOX differences {0} {1} {2}", request.bbox.TopRightLat - request.bbox.BottomLeftLat, request.bbox.TopRightLon - request.bbox.BottomLeftLon, zoomLevel); numTiles = TileServiceUtils.NumberOfTiles(zoomLevel); Point topLeftLatLng = new Point(request.bbox.TopRightLat.LatRadiansToDegrees(), request.bbox.BottomLeftLon.LonRadiansToDegrees()); topLeftTile = WebMercatorProjection.LatLngToTile(topLeftLatLng, numTiles); log.LogDebug($"DxfTileExecutor: zoomLevel={zoomLevel}, numTiles={numTiles}, xtile={topLeftTile.x}, ytile={topLeftTile.y}"); } else if (item is DxfTile3dRequest request3d) { files = request3d.files?.ToList(); zoomLevel = request3d.zoomLevel; numTiles = TileServiceUtils.NumberOfTiles(zoomLevel); topLeftTile = new Point { x = request3d.xTile, y = request3d.yTile }; } else { ThrowRequestTypeCastException <DxfTileRequest>(); } log.LogDebug($"DxfTileExecutor: {files?.Count ?? 0} files"); //Short circuit overlaying if there no files to overlay as ForAll is an expensive operation if (files == null || !files.Any()) { byte[] emptyOverlayData; using (var bitmap = new Image <Rgba32>(WebMercatorProjection.TILE_SIZE, WebMercatorProjection.TILE_SIZE)) { emptyOverlayData = bitmap.BitmapToByteArray(); } return(new TileResult(emptyOverlayData)); } log.LogDebug(string.Join(",", files.Select(f => f.Name).ToList())); var tileList = new List <byte[]>(); const string DATA_OCEAN_ROOT_FOLDER_ID_KEY = "DATA_OCEAN_ROOT_FOLDER_ID"; var dataOceanRootFolder = configStore.GetValueString(DATA_OCEAN_ROOT_FOLDER_ID_KEY); if (string.IsNullOrEmpty(dataOceanRootFolder)) { throw new ArgumentException($"Missing environment variable {DATA_OCEAN_ROOT_FOLDER_ID_KEY}"); } //For GeoTIFF files, use the latest version of a file var geoTiffFiles = files.Where(x => x.ImportedFileType == ImportedFileType.GeoTiff).ToList(); if (geoTiffFiles.Any()) { //Find any with multiple versions and remove old ones from the list var latestFiles = geoTiffFiles.GroupBy(g => g.Name).Select(g => g.OrderBy(o => o.SurveyedUtc).Last()).ToList(); foreach (var geoTiffFile in geoTiffFiles) { if (!latestFiles.Contains(geoTiffFile)) { files.Remove(geoTiffFile); } } } var fileTasks = files.Select(async file => { //foreach (var file in request.files) //Check file type to see if it has tiles if (file.ImportedFileType == ImportedFileType.Linework || file.ImportedFileType == ImportedFileType.GeoTiff) { var fullPath = DataOceanFileUtil.DataOceanPath(dataOceanRootFolder, file.CustomerUid, file.ProjectUid); var fileName = DataOceanFileUtil.DataOceanFileName(file.Name, file.ImportedFileType == ImportedFileType.SurveyedSurface || file.ImportedFileType == ImportedFileType.GeoTiff, Guid.Parse(file.ImportedFileUid), file.SurveyedUtc); fileName = DataOceanFileUtil.GeneratedFileName(fileName, file.ImportedFileType); if (zoomLevel >= file.MinZoomLevel) { byte[] tileData = null; if (zoomLevel <= file.MaxZoomLevel || file.MaxZoomLevel == 0) //0 means not calculated { tileData = await GetTileAtRequestedZoom(topLeftTile, zoomLevel, fullPath, fileName); } else if (zoomLevel - file.MaxZoomLevel <= 5) //Don't try to scale if the difference is too excessive { tileData = await GetTileAtHigherZoom(topLeftTile, zoomLevel, fullPath, fileName, file.MaxZoomLevel, numTiles); } else { log.LogDebug( "DxfTileExecutor: difference between requested and maximum zooms too large; not even going to try to scale tile"); } if (tileData != null && tileData.Length > 0) { tileList.Add(tileData); } } } }); await Task.WhenAll(fileTasks); log.LogDebug($"DxfTileExecutor: Overlaying {tileList.Count} tiles"); byte[] overlayData = TileOverlay.OverlayTiles(tileList); return(new TileResult(overlayData)); }