public float GetElevationAtPoint(FileMetadata metadata, int x, int y) { float heightValue = 0; try { if (this.IsTiled) { // TODO store in metadata int tileWidth = this.TileWidth; int tileHeight = this.TileHeight; int tileSize = this.TileSize; byte[] buffer; var tileX = (x / tileWidth) * tileWidth; var tileY = (y / tileHeight) * tileHeight; if (tilesCache == null) { tilesCache = new Dictionary <int, byte[]>(); } var tileKey = (x / tileWidth) + (y / tileHeight) * (metadata.Width / tileWidth + 1); if (!tilesCache.TryGetValue(tileKey, out buffer)) { buffer = new byte[tileSize]; TiffFile.ReadTile(buffer, 0, tileX, tileY, 0, 0); tilesCache.Add(tileKey, buffer); } var offset = x - tileX + (y - tileY) * tileHeight; heightValue = GetElevationAtPoint(metadata, offset, buffer); } else { // metadata.BitsPerSample // When 16 we have 2 bytes per sample // When 32 we have 4 bytes per sample int bytesPerSample = metadata.BitsPerSample / 8; byte[] byteScanline = new byte[metadata.ScanlineSize]; TiffFile.ReadScanline(byteScanline, y); heightValue = GetElevationAtPoint(metadata, x, byteScanline); } } catch (Exception e) { throw new Exception($"Error in ParseGeoDataAtPoint: {e.Message}"); } return(heightValue); }
public HeightMap GetHeightMapInBBox(BoundingBox bbox, FileMetadata metadata, float noDataValue = 0) { int yStart = 0; int yEnd = 0; int xStart = 0; int xEnd = 0; if (metadata.FileFormat.Registration == DEMFileRegistrationMode.Grid) { yStart = (int)Math.Floor((bbox.yMax - metadata.PhysicalEndLat) / metadata.pixelSizeY); yEnd = (int)Math.Ceiling((bbox.yMin - metadata.PhysicalEndLat) / metadata.pixelSizeY); xStart = (int)Math.Floor((bbox.xMin - metadata.PhysicalStartLon) / metadata.pixelSizeX); xEnd = (int)Math.Ceiling((bbox.xMax - metadata.PhysicalStartLon) / metadata.pixelSizeX); } else { yStart = (int)Math.Floor((bbox.yMax - metadata.DataEndLat) / metadata.pixelSizeY); yEnd = (int)Math.Ceiling((bbox.yMin - metadata.DataEndLat) / metadata.pixelSizeY); xStart = (int)Math.Floor((bbox.xMin - metadata.DataStartLon) / metadata.pixelSizeX); xEnd = (int)Math.Ceiling((bbox.xMax - metadata.DataStartLon) / metadata.pixelSizeX); } // Tiled geotiffs like aster have overlapping 1px borders int overlappingPixel = this.IsTiled ? 1 : 0; xStart = Math.Max(0, xStart); xEnd = Math.Min(metadata.Width - 1, xEnd) - overlappingPixel; yStart = Math.Max(0, yStart); yEnd = Math.Min(metadata.Height - 1, yEnd) - overlappingPixel; HeightMap heightMap = new HeightMap(xEnd - xStart + 1, yEnd - yStart + 1); heightMap.Count = heightMap.Width * heightMap.Height; var coords = new List <GeoPoint>(heightMap.Count); heightMap.BoundingBox = new BoundingBox(0, 0, 0, 0); if (this.IsTiled) { // Tiled rasters are composed of multiple "sub" images // TODO store in metadata int tileWidth = this.TileWidth; int tileHeight = this.TileHeight; int tileSize = this.TileSize; byte[] buffer; for (int y = yStart; y <= yEnd; y++) { double latitude = metadata.DataEndLat + (metadata.pixelSizeY * y); // bounding box if (y == yStart) { heightMap.BoundingBox.yMax = latitude; heightMap.BoundingBox.xMin = metadata.DataStartLon + (metadata.pixelSizeX * xStart); heightMap.BoundingBox.xMax = metadata.DataStartLon + (metadata.pixelSizeX * xEnd); } if (y == yEnd) { heightMap.BoundingBox.yMin = latitude; } for (int x = xStart; x <= xEnd; x++) { double longitude = metadata.DataStartLon + (metadata.pixelSizeX * x); var tileX = (x / tileWidth) * tileWidth; var tileY = (y / tileHeight) * tileHeight; if (tilesCache == null) { tilesCache = new Dictionary <int, byte[]>(); } var tileKey = (x / tileWidth) + (y / tileHeight) * (metadata.Width / tileWidth + 1); if (!tilesCache.TryGetValue(tileKey, out buffer)) { buffer = new byte[tileSize]; TiffFile.ReadTile(buffer, 0, tileX, tileY, 0, 0); tilesCache.Add(tileKey, buffer); } var offset = x - tileX + (y - tileY) * tileHeight; float heightValue = GetElevationAtPoint(metadata, offset, buffer); if (heightValue <= 0) { heightMap.Minimum = Math.Min(heightMap.Minimum, heightValue); heightMap.Maximum = Math.Max(heightMap.Maximum, heightValue); } else if (heightValue < 32768) { heightMap.Minimum = Math.Min(heightMap.Minimum, heightValue); heightMap.Maximum = Math.Max(heightMap.Maximum, heightValue); } else { heightValue = (float)noDataValue; } coords.Add(new GeoPoint(latitude, longitude, heightValue)); } } } else { // metadata.BitsPerSample // When 16 we have 2 bytes per sample // When 32 we have 4 bytes per sample int bytesPerSample = metadata.BitsPerSample / 8; byte[] byteScanline = new byte[metadata.ScanlineSize]; double endLat = metadata.DataEndLat + metadata.pixelSizeY / 2d; double startLon = metadata.DataStartLon + metadata.pixelSizeX / 2d; for (int y = yStart; y <= yEnd; y++) { TiffFile.ReadScanline(byteScanline, y); // TODO: handle Cell registered DEMs: lat is 1/2 pixel off double latitude = endLat + (metadata.pixelSizeY * y); // bounding box if (y == yStart) { heightMap.BoundingBox.yMax = latitude; heightMap.BoundingBox.xMin = startLon + (metadata.pixelSizeX * xStart); heightMap.BoundingBox.xMax = startLon + (metadata.pixelSizeX * xEnd); } else if (y == yEnd) { heightMap.BoundingBox.yMin = latitude; } for (int x = xStart; x <= xEnd; x++) { double longitude = startLon + (metadata.pixelSizeX * x); float heightValue = 0; switch (metadata.SampleFormat) { case RasterSampleFormat.FLOATING_POINT: heightValue = BitConverter.ToSingle(byteScanline, x * bytesPerSample); break; case RasterSampleFormat.INTEGER: heightValue = BitConverter.ToInt16(byteScanline, x * bytesPerSample); break; case RasterSampleFormat.UNSIGNED_INTEGER: heightValue = BitConverter.ToUInt16(byteScanline, x * bytesPerSample); break; default: throw new Exception("Sample format unsupported."); } if (heightValue <= 0) { heightMap.Minimum = Math.Min(heightMap.Minimum, heightValue); heightMap.Maximum = Math.Max(heightMap.Maximum, heightValue); } else if (heightValue < 32768) { heightMap.Minimum = Math.Min(heightMap.Minimum, heightValue); heightMap.Maximum = Math.Max(heightMap.Maximum, heightValue); } else { heightValue = (float)noDataValue; } coords.Add(new GeoPoint(latitude, longitude, heightValue)); } } } heightMap.BoundingBox.zMin = heightMap.Minimum; heightMap.BoundingBox.zMax = heightMap.Maximum; Debug.Assert(heightMap.Width * heightMap.Height == coords.Count); heightMap.Coordinates = coords; return(heightMap); }