public void CanConvertLatLngToPixelOffset() { List <WGSPoint> latLngs = new List <WGSPoint> { new WGSPoint(36.210.LatDegreesToRadians(), -115.025.LonDegreesToRadians()), new WGSPoint(36.205.LatDegreesToRadians(), -115.029.LonDegreesToRadians()), new WGSPoint(36.200.LatDegreesToRadians(), -115.018.LonDegreesToRadians()) }; var topLeft = new Point(100, 250); var pixelPoints = TileServiceUtils.LatLngToPixelOffset(latLngs, topLeft, 32768); var expectedPoints = new PointF[3] { new PointF { X = 1513777.25F, Y = 3287930F }, new PointF { X = 1513684F, Y = 3288074.5F }, new PointF { X = 1513940.38F, Y = 3288218.75F }, }; for (int i = 0; i < 3; i++) { Assert.Equal(expectedPoints[i], pixelPoints[i]); } }
private async Task JoinDataOceanTiles(FileData dxfFile, MasterDataModels.Point tileTopLeft, MasterDataModels.Point tileBottomRight, Image <Rgba32> tileBitmap, int zoomLevel) { var fileName = DataOceanFileUtil.DataOceanFileName(dxfFile.Name, dxfFile.ImportedFileType == ImportedFileType.SurveyedSurface || dxfFile.ImportedFileType == ImportedFileType.GeoTiff, Guid.Parse(dxfFile.ImportedFileUid), dxfFile.SurveyedUtc); fileName = DataOceanFileUtil.GeneratedFileName(fileName, dxfFile.ImportedFileType); var dataOceanFileUtil = new DataOceanFileUtil($"{DataOceanUtil.PathSeparator}{dataOceanRootFolder}{dxfFile.Path}{DataOceanUtil.PathSeparator}{fileName}"); log.LogDebug($"{nameof(JoinDataOceanTiles)}: fileName: {fileName} dataOceanFileUtil.FullFileName {dataOceanFileUtil.FullFileName}"); for (int yTile = (int)tileTopLeft.y; yTile <= (int)tileBottomRight.y; yTile++) { for (int xTile = (int)tileTopLeft.x; xTile <= (int)tileBottomRight.x; xTile++) { var targetFile = dataOceanFileUtil.GetTileFileName(zoomLevel, yTile, xTile); log.LogDebug($"JoinDxfTiles: getting tile {targetFile}"); var file = await dataOceanClient.GetFile(targetFile, authn.CustomHeaders()); if (file != null) { Image <Rgba32> tile = Image.Load <Rgba32>(file); Point offset = new Point( (xTile - (int)tileTopLeft.x) * MasterDataModels.WebMercatorProjection.TILE_SIZE, (yTile - (int)tileTopLeft.y) * MasterDataModels.WebMercatorProjection.TILE_SIZE); tileBitmap.Mutate(ctx => ctx.DrawImage(tile, PixelBlenderMode.Normal, 1f, offset)); } } } }
/// <summary> /// Gets a tile at a higher zoom level and scales part of it for the requested tile /// </summary> /// <param name="topLeftTile">The top left tile coordinates</param> /// <param name="zoomLevel">The requested zoom level</param> /// <param name="path">The file path</param> /// <param name="generatedName">The name of the DXF file (for design and alignment files it is generated)</param> /// <param name="filespaceId">The filespace ID</param> /// <param name="maxZoomLevel">The maximum zoom level for which tiles have been generated</param> /// <param name="numTiles">The number of tiles for the requested zoom level</param> /// <returns>A scaled tile</returns> private async Task <byte[]> GetTileAtHigherZoom(Point topLeftTile, int zoomLevel, string path, string generatedName, string filespaceId, int maxZoomLevel, int numTiles) { int zoomLevelFound = maxZoomLevel; // Calculate the tile coords of the higher zoom level tile that covers the requested tile Point ptRequestedTile = new Point(topLeftTile.y, topLeftTile.x); Point ptRequestedPixel = WebMercatorProjection.TileToPixel(ptRequestedTile); int numTilesAtRequestedZoomLevel = numTiles; Point ptRequestedWorld = WebMercatorProjection.PixelToWorld(ptRequestedPixel, numTilesAtRequestedZoomLevel); int numTilesAtFoundZoomLevel = TileServiceUtils.NumberOfTiles(maxZoomLevel); Point ptHigherPixel = WebMercatorProjection.WorldToPixel(ptRequestedWorld, numTilesAtFoundZoomLevel); Point ptHigherTile = WebMercatorProjection.PixelToTile(ptHigherPixel); //Note PixelToTile uses Math.Floor so this tile coordinate will be the top left of the tile // With the new tile coords of the higher zoom level tile, see if it exists on TCC string fullHigherTileName = $"{FileUtils.ZoomPath(FileUtils.TilePath(path, generatedName), zoomLevelFound)}/{ptHigherTile.y}/{ptHigherTile.x}.png"; log.LogDebug("DxfTileExecutor: looking for higher tile {0}", fullHigherTileName); byte[] tileData = await DownloadTile(filespaceId, fullHigherTileName, "higher"); if (tileData != null) { tileData = ScaleTile(tileData, zoomLevel - zoomLevelFound, ptHigherTile, ptRequestedWorld, numTilesAtFoundZoomLevel); } return(tileData); }
public void MapPointToXYZ() { var gridPoint = new Point(1.234, 5.678); var xyz = AutoMapperUtility.Automapper.Map <XYZ>(gridPoint); Assert.Equal(gridPoint.Longitude, xyz.X); Assert.Equal(gridPoint.Latitude, xyz.Y); Assert.Equal(0, xyz.Z); }
/// <summary> /// Gets a tile at the requested zoom level. /// </summary> /// <param name="topLeftTile">The top left tile coordinates</param> /// <param name="zoomLevel">The requested zoom level</param> /// <param name="path">The file path</param> /// <param name="fileName">The name of the DXF file</param> /// <returns>A generated tile</returns> protected Task <byte[]> GetTileAtRequestedZoom(Point topLeftTile, int zoomLevel, string path, string fileName) { //Work out tile location var fullTileName = GetFullTileName(topLeftTile, zoomLevel, path, fileName); log.LogDebug($"DxfTileExecutor: looking for requested tile {fullTileName}"); return(DownloadTile(fullTileName, "requested")); }
/// <summary> /// Converts the lat/lng points to pixels and offsets them from the top left corner of the tile. /// </summary> /// <param name="latLngs">The list of points to convert in radians</param> /// <param name="pixelTopLeft">The top left corner of the tile in pixels</param> /// <param name="numTiles">The number of tiles for the zoom level</param> /// <returns>The points in pixels relative to the top left corner of the tile.</returns> public static PointF[] LatLngToPixelOffset(IEnumerable <WGSPoint> latLngs, MasterDataModels.Point pixelTopLeft, long numTiles) { List <PointF> pixelPoints = new List <PointF>(); foreach (WGSPoint ll in latLngs) { MasterDataModels.Point pixelPt = LatLngToPixel(ll.Lat, ll.Lon, numTiles); pixelPoints.Add(new PointF((float)(pixelPt.x - pixelTopLeft.x), (float)(pixelPt.y - pixelTopLeft.y))); } return(pixelPoints.ToArray()); }
/// <summary> /// Scales a tile /// </summary> /// <param name="tileData">The tile to scale</param> /// <param name="zoomLevelDifference">The difference between the downloaded tile and the rqeuested tile zoom levels</param> /// <param name="ptHigherTile"></param> /// <param name="ptRequestedWorld"></param> /// <param name="numTilesAtFoundZoomLevel">The number of tiles for the higher zoom level</param> /// <returns>A scaled tile</returns> private byte[] ScaleTile(byte[] tileData, int zoomLevelDifference, Point ptHigherTile, Point ptRequestedWorld, int numTilesAtFoundZoomLevel) { // Calculate the tile coords of the BR corner of the higher zoom level tile // so that we can identify which sub-part of it to crop and scale Point ptHigherTileTopLeft = ptHigherTile; Point ptHigherTileBotRight = new Point(ptHigherTile.y + 1, ptHigherTile.x + 1); // Calculate the sub-tile rectangle that we need to crop out of the higher tile // using a simple proportion calculation based on which part of the higher tile // covers the original requested tile in world coordinates Point ptHigherWorldTopLeft = WebMercatorProjection.PixelToWorld(WebMercatorProjection.TileToPixel(ptHigherTileTopLeft), numTilesAtFoundZoomLevel); Point ptHigherWorldBotRight = WebMercatorProjection.PixelToWorld(WebMercatorProjection.TileToPixel(ptHigherTileBotRight), numTilesAtFoundZoomLevel); double ratioX = (ptRequestedWorld.x - ptHigherWorldTopLeft.x) / (ptHigherWorldBotRight.x - ptHigherWorldTopLeft.x); double ratioY = (ptRequestedWorld.y - ptHigherWorldTopLeft.y) / (ptHigherWorldBotRight.y - ptHigherWorldTopLeft.y); int startX = (int)Math.Floor(WebMercatorProjection.TILE_SIZE * ratioX); int startY = (int)Math.Floor(WebMercatorProjection.TILE_SIZE * ratioY); // Calculate how much up-scaling of higher level zoom tile we need to do // based on the difference between the requested and higher zoom levels int croppedTileSize = WebMercatorProjection.TILE_SIZE / (1 << zoomLevelDifference); // Set the crop rectangle and draw it into a new bitmap, scaling it up to the standard tile size Rectangle cropRect = new Rectangle(startX, startY, croppedTileSize, croppedTileSize); log.LogDebug("DxfTileExecutor: crop rectangle x = {0}, y = {1}, size = {2}", startX, startY, croppedTileSize); //source bitmap using (var tileStream = new MemoryStream(tileData)) using (Bitmap higherBitmap = new Bitmap(tileStream)) //destination bitmap using (Bitmap target = new Bitmap(WebMercatorProjection.TILE_SIZE, WebMercatorProjection.TILE_SIZE)) using (Graphics g = Graphics.FromImage(target)) { g.CompositingMode = CompositingMode.SourceCopy; g.CompositingQuality = CompositingQuality.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.SmoothingMode = SmoothingMode.HighQuality; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.DrawImage(higherBitmap, new Rectangle(0, 0, target.Width, target.Height), cropRect, GraphicsUnit.Pixel); g.Flush(); return(target.BitmapToByteArray()); } }
/// <summary> /// Gets a tile at the requested zoom level. /// </summary> /// <param name="topLeftTile">The top left tile coordinates</param> /// <param name="zoomLevel">The requested zoom level</param> /// <param name="path">The file path</param> /// <param name="generatedName">The name of the DXF file (for design and alignment files it is generated)</param> /// <param name="filespaceId">The filespace ID</param> /// <returns>A generated tile</returns> private async Task <byte[]> GetTileAtRequestedZoom(Point topLeftTile, int zoomLevel, string path, string generatedName, string filespaceId) { //Work out tile location string fullTileName = GetFullTileName(topLeftTile, zoomLevel, path, generatedName); log.LogDebug("DxfTileExecutor: looking for requested tile {0}", fullTileName); //Download the tile return(await DownloadTile(filespaceId, fullTileName, "requested")); }
public void MapPointToFencePoint() { var point = new Point { x = 10, y = 15 }; var fencePoint = AutoMapperUtility.Automapper.Map <FencePoint>(point); Assert.Equal(point.x, fencePoint.X); Assert.Equal(point.y, fencePoint.Y); Assert.Equal(0, fencePoint.Z); }
/// <summary> /// Scales a tile /// </summary> /// <param name="tileData">The tile to scale</param> /// <param name="zoomLevelDifference">The difference between the downloaded tile and the rqeuested tile zoom levels</param> /// <param name="ptHigherTile"></param> /// <param name="ptRequestedWorld"></param> /// <param name="numTilesAtFoundZoomLevel">The number of tiles for the higher zoom level</param> /// <returns>A scaled tile</returns> private byte[] ScaleTile(byte[] tileData, int zoomLevelDifference, Point ptHigherTile, Point ptRequestedWorld, int numTilesAtFoundZoomLevel) { // Calculate the tile coords of the BR corner of the higher zoom level tile // so that we can identify which sub-part of it to crop and scale Point ptHigherTileTopLeft = ptHigherTile; Point ptHigherTileBotRight = new Point(ptHigherTile.y + 1, ptHigherTile.x + 1); // Calculate the sub-tile rectangle that we need to crop out of the higher tile // using a simple proportion calculation based on which part of the higher tile // covers the original requested tile in world coordinates var ptHigherWorldTopLeft = WebMercatorProjection.PixelToWorld(WebMercatorProjection.TileToPixel(ptHigherTileTopLeft), numTilesAtFoundZoomLevel); var ptHigherWorldBotRight = WebMercatorProjection.PixelToWorld(WebMercatorProjection.TileToPixel(ptHigherTileBotRight), numTilesAtFoundZoomLevel); var ratioX = (ptRequestedWorld.x - ptHigherWorldTopLeft.x) / (ptHigherWorldBotRight.x - ptHigherWorldTopLeft.x); var ratioY = (ptRequestedWorld.y - ptHigherWorldTopLeft.y) / (ptHigherWorldBotRight.y - ptHigherWorldTopLeft.y); var startX = (int)Math.Floor(WebMercatorProjection.TILE_SIZE * ratioX); var startY = (int)Math.Floor(WebMercatorProjection.TILE_SIZE * ratioY); // Calculate how much up-scaling of higher level zoom tile we need to do // based on the difference between the requested and higher zoom levels var croppedTileSize = WebMercatorProjection.TILE_SIZE / (1 << zoomLevelDifference); // Set the crop rectangle and draw it into a new bitmap, scaling it up to the standard tile size var cropRect = new Rectangle(startX, startY, croppedTileSize, croppedTileSize); log.LogDebug("DxfTileExecutor: crop rectangle x = {0}, y = {1}, size = {2}", startX, startY, croppedTileSize); //source bitmap using (var tileStream = new MemoryStream(tileData)) using (var higherBitmap = Image.Load(tileStream)) { //destination bitmap var target = higherBitmap.Clone(ctx => ctx.Crop(cropRect).Resize(WebMercatorProjection.TILE_SIZE, WebMercatorProjection.TILE_SIZE)); return(target.BitmapToByteArray()); } }
/// <summary> /// Joins standard size DXF tiles together to form one large tile for the report /// </summary> private async Task <byte[]> JoinDxfTiles(MapParameters parameters, FileData dxfFile) { log.LogDebug($"JoinDxfTiles: {dxfFile.ImportedFileUid}, {dxfFile.Name}"); //Find the tiles that the bounding box fits into. MasterDataModels.Point tileTopLeft = MasterDataModels.WebMercatorProjection.PixelToTile(parameters.pixelTopLeft); MasterDataModels.Point pixelBottomRight = TileServiceUtils.LatLngToPixel( parameters.bbox.minLat, parameters.bbox.maxLng, parameters.numTiles); MasterDataModels.Point tileBottomRight = MasterDataModels.WebMercatorProjection.PixelToTile(pixelBottomRight); int xnumTiles = (int)(tileBottomRight.x - tileTopLeft.x) + 1; int ynumTiles = (int)(tileBottomRight.y - tileTopLeft.y) + 1; int width = xnumTiles * MasterDataModels.WebMercatorProjection.TILE_SIZE; int height = ynumTiles * MasterDataModels.WebMercatorProjection.TILE_SIZE; using (Image <Rgba32> tileBitmap = new Image <Rgba32>(width, height)) { //Find the offset of the bounding box top left point inside the top left tile var point = new MasterDataModels.Point { x = tileTopLeft.x * MasterDataModels.WebMercatorProjection.TILE_SIZE, y = tileTopLeft.y * MasterDataModels.WebMercatorProjection.TILE_SIZE }; //Clip to the actual bounding box within the tiles. int clipWidth = parameters.mapWidth; int clipHeight = parameters.mapHeight; int xClipTopLeft = (int)(parameters.pixelTopLeft.x - point.x); int yClipTopLeft = (int)(parameters.pixelTopLeft.y - point.y); //Unlike System.Drawing, which allows the clipRect to have negative x, y and which moves as well as clips when used with DrawImage //as the source rectangle, ImageSharp does not respect negative values. So we will have to do extra work in this situation. if (xClipTopLeft < 0) { clipWidth += xClipTopLeft; xClipTopLeft = 0; } if (yClipTopLeft < 0) { clipHeight += yClipTopLeft; yClipTopLeft = 0; } var clipRect = new Rectangle(xClipTopLeft, yClipTopLeft, clipWidth, clipHeight); //Join all the DXF tiles into one large tile await JoinDataOceanTiles(dxfFile, tileTopLeft, tileBottomRight, tileBitmap, parameters.zoomLevel); //Now clip the large tile tileBitmap.Mutate(ctx => ctx.Crop(clipRect)); if (clipWidth >= parameters.mapWidth && clipHeight >= parameters.mapHeight) { return(tileBitmap.BitmapToByteArray()); } //and resize it if required tile area was overlapping rather than within the large tile //(negative clip values above) using (Image <Rgba32> resizedBitmap = new Image <Rgba32>(parameters.mapWidth, parameters.mapHeight)) { Point offset = new Point(parameters.mapWidth - clipWidth, parameters.mapHeight - clipHeight); resizedBitmap.Mutate(ctx => ctx.DrawImage(tileBitmap, PixelBlenderMode.Normal, 1f, offset)); return(resizedBitmap.BitmapToByteArray()); } } }
/// <summary> /// Converts the lat/lng point to pixels /// </summary> /// <param name="latitude">The latitude to convert in radians</param> /// <param name="longitude">The longitude to convert in radians</param> /// <param name="numTiles">The number of tiles</param> /// <returns>Pixel point</returns> public static Point LatLngToPixel(double latitude, double longitude, long numTiles) { var point = new Point(latitude.LatRadiansToDegrees(), longitude.LonRadiansToDegrees()); return(WebMercatorProjection.LatLngToPixel(point, numTiles)); }
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)); }
/// <summary> /// Gets the full file name for a tile /// </summary> /// <param name="topLeftTile">The top left tile coordinates</param> /// <param name="zoomLevel">The zoom level of the tile</param> /// <param name="path">The file path</param> /// <param name="generatedName">The name of the DXF file (for design and alignment files it is generated)</param> /// <returns></returns> private string GetFullTileName(Point topLeftTile, int zoomLevel, string path, string generatedName) { return ($"{FileUtils.ZoomPath(FileUtils.TilePath(path, generatedName), zoomLevel)}/{topLeftTile.y}/{topLeftTile.x}.png"); }
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)); }
/// <summary> /// Gets the full file name for a tile /// </summary> private static string GetFullTileName(Point topLeftTile, int zoomLevel, string path, string fileName) { return(new DataOceanFileUtil($"{path}{DataOceanUtil.PathSeparator}{fileName}").GetTileFileName(zoomLevel, (int)topLeftTile.y, (int)topLeftTile.x)); }