private void AddHeightMapBase(Triangulation triangulation, HeightMap heightMap, BoxBaseThickness thickness, float zValue) { // bake coordinates to avoid executing the coords transfrom pipeline var coords = heightMap.Coordinates.ToList(); int capacity = heightMap.Width * 2 + (heightMap.Height - 2) * 2; var basePoints = new List <GeoPoint>(capacity); var baseIndexes = new List <int>(capacity); float baseElevation = 0; switch (thickness) { case BoxBaseThickness.FromMinimumPoint: baseElevation = (float)coords.Min(p => p.Elevation).GetValueOrDefault(0) - zValue; break; default: case BoxBaseThickness.FixedElevation: baseElevation = zValue; break; } // x : 0 => width // y : 0 int baseIndex0 = coords.Count; int baseIndex = baseIndex0; for (int x = 0; x < heightMap.Width - 1; x++) { var pBase = coords[x].Clone(); pBase.Elevation = baseElevation; basePoints.Add(pBase); baseIndexes.Add(x); baseIndexes.Add(baseIndex + 1); baseIndexes.Add(baseIndex); baseIndexes.Add(x + 1); baseIndexes.Add(baseIndex + 1); baseIndexes.Add(x); baseIndex++; } // x : width // y : 0 => height // for (int y = 0; y < heightMap.Height - 1; y++) { int x = heightMap.Width - 1; int index = x + y * heightMap.Width; var pBase = coords[index].Clone(); pBase.Elevation = baseElevation; basePoints.Add(pBase); baseIndexes.Add(x + y * heightMap.Width); baseIndexes.Add(baseIndex + 1); baseIndexes.Add(baseIndex); baseIndexes.Add(x + y * heightMap.Width); baseIndexes.Add(x + (y + 1) * heightMap.Width); baseIndexes.Add(baseIndex + 1); baseIndex++; } //// x : width => 0 // y : height for (int x = heightMap.Width - 1; x > 0; x--) { int index = x + (heightMap.Height - 1) * heightMap.Width; var pBase = coords[index].Clone(); pBase.Elevation = baseElevation; basePoints.Add(pBase); baseIndexes.Add(index); baseIndexes.Add(index - 1); baseIndexes.Add(baseIndex); baseIndexes.Add(index - 1); baseIndexes.Add(baseIndex + 1); baseIndexes.Add(baseIndex); baseIndex++; } //// x : 0 // y : height => 0 for (int y = heightMap.Height - 1; y > 0; y--) { int x = 0; int index = x + y * heightMap.Width; var pBase = coords[index].Clone(); pBase.Elevation = baseElevation; basePoints.Add(pBase); // last base position is the first base generated int nextBaseIndex = baseIndex + 1 > baseIndex0 + capacity - 1 ? baseIndex0 : baseIndex + 1; baseIndexes.Add(x + y * heightMap.Width); baseIndexes.Add(nextBaseIndex); baseIndexes.Add(baseIndex); baseIndexes.Add(x + y * heightMap.Width); baseIndexes.Add(x + (y - 1) * heightMap.Width); baseIndexes.Add(nextBaseIndex); baseIndex++; } // base (2 big triangles) baseIndexes.Add(baseIndex0); baseIndexes.Add(baseIndex0 + heightMap.Width - 1); baseIndexes.Add(baseIndex0 + heightMap.Width - 1 + heightMap.Height - 1); baseIndexes.Add(baseIndex0); baseIndexes.Add(baseIndex0 + heightMap.Width - 1 + heightMap.Height - 1); baseIndexes.Add(baseIndex0 + 2 * (heightMap.Width - 1) + heightMap.Height - 1); triangulation.Positions = triangulation.Positions.Concat(basePoints); triangulation.NumPositions += basePoints.Count; triangulation.Indices = triangulation.Indices.Concat(baseIndexes); }
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)); } } } Debug.Assert(heightMap.Width * heightMap.Height == coords.Count); heightMap.Coordinates = coords; return(heightMap); }
public HeightMap GetHeightMap(FileMetadata metadata) { if (this.isTiled) { throw new NotImplementedException("Whole height map with tile geoTiff is not implemented"); } HeightMap heightMap = new HeightMap(metadata.Width, metadata.Height); heightMap.Count = heightMap.Width * heightMap.Height; var coords = new List <GeoPoint>(heightMap.Count); // 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]; for (int y = 0; y < metadata.Height; y++) { TiffFile.ReadScanline(byteScanline, y); double latitude = metadata.DataStartLat + (metadata.pixelSizeY * y); for (int x = 0; x < metadata.Width; x++) { double longitude = metadata.DataStartLon + (metadata.pixelSizeX * x); float heightValue = 0; switch (metadata.SampleFormat) { case RasterSampleFormat.FLOATING_POINT: heightValue = BitConverter.ToSingle(byteScanline, x * metadata.BitsPerSample / 8); break; case RasterSampleFormat.INTEGER: heightValue = BitConverter.ToInt16(byteScanline, x * metadata.BitsPerSample / 8); break; case RasterSampleFormat.UNSIGNED_INTEGER: heightValue = BitConverter.ToUInt16(byteScanline, x * metadata.BitsPerSample / 8); break; default: throw new Exception("Sample format unsupported."); } if (heightValue < 32768) { heightMap.Minimum = Math.Min(metadata.MinimumAltitude, heightValue); heightMap.Maximum = Math.Max(metadata.MaximumAltitude, heightValue); } else { heightValue = 0; } coords.Add(new GeoPoint(latitude, longitude, heightValue)); } } heightMap.Coordinates = coords; return(heightMap); }
/// <summary> /// Sort height map coordinates (lat descending, then lon) /// </summary> /// <param name="heightMap"></param> /// <returns></returns> public static HeightMap Sort(this HeightMap heightMap) { heightMap.Coordinates = heightMap.Coordinates.Sort(); return(heightMap); }
/// <summary> /// Rescale the height map in order that it fits into the specified size /// </summary> /// <param name="heightMap"></param> /// <param name="maxSize"></param> /// <returns></returns> public static HeightMap FitInto(this HeightMap heightMap, float maxSize) { return(FitInto(heightMap, maxSize, out float scale)); }
/// <summary> /// Helper to get an in memory coordinate list /// useful to generate normal maps and let the same height map follow the pipeline (reproj, center, ...) /// </summary> /// <returns></returns> public static HeightMap BakeCoordinates(this HeightMap heightMap) { heightMap.Coordinates = heightMap.Coordinates.ToList(); return(heightMap); }
public HeightMap GetHeightMapInBBox(BoundingBox bbox, FileMetadata metadata, float noDataValue = 0) { // 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]; int yStart = (int)Math.Floor((bbox.yMax - metadata.StartLat) / metadata.pixelSizeY); int yEnd = (int)Math.Ceiling((bbox.yMin - metadata.StartLat) / metadata.pixelSizeY); int xStart = (int)Math.Floor((bbox.xMin - metadata.StartLon) / metadata.pixelSizeX); int xEnd = (int)Math.Ceiling((bbox.xMax - metadata.StartLon) / metadata.pixelSizeX); xStart = Math.Max(0, xStart); xEnd = Math.Min(metadata.Width - 1, xEnd); yStart = Math.Max(0, yStart); yEnd = Math.Min(metadata.Height - 1, yEnd); 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); for (int y = yStart; y <= yEnd; y++) { TiffFile.ReadScanline(byteScanline, y); double latitude = metadata.StartLat + (metadata.pixelSizeY * y); // bounding box if (y == yStart) { heightMap.BoundingBox.yMax = latitude; heightMap.BoundingBox.xMin = metadata.StartLon + (metadata.pixelSizeX * xStart); heightMap.BoundingBox.xMax = metadata.StartLon + (metadata.pixelSizeX * xEnd); } else if (y == yEnd) { heightMap.BoundingBox.yMin = latitude; } for (int x = xStart; x <= xEnd; x++) { double longitude = metadata.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)); } } Debug.Assert(heightMap.Width * heightMap.Height == coords.Count); heightMap.Coordinates = coords; return(heightMap); }
public HeightMap GetHeightMap(FileMetadata metadata) { HeightMap heightMap = new HeightMap(metadata.Width, metadata.Height); heightMap.Count = heightMap.Width * heightMap.Height; var coords = new List <GeoPoint>(heightMap.Count); _hgtStream.Seek(0, SeekOrigin.Begin); // 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]; for (int y = 0; y < metadata.Height; y++) { _hgtStream.Read(byteScanline, 0, metadata.ScanlineSize); double latitude = metadata.StartLat + (metadata.pixelSizeY * y); for (int x = 0; x < metadata.Width; x++) { double longitude = metadata.StartLon + (metadata.pixelSizeX * x); float heightValue = 0; byte[] heightBytes = new byte[bytesPerSample];; if (BitConverter.IsLittleEndian) { // reverse bytes for (int i = 0; i < bytesPerSample; i++) { heightBytes[i] = byteScanline[x * bytesPerSample + bytesPerSample - i - 1]; } switch (metadata.SampleFormat) { case RasterSampleFormat.FLOATING_POINT: heightValue = BitConverter.ToSingle(heightBytes, 0); break; case RasterSampleFormat.INTEGER: heightValue = BitConverter.ToInt16(heightBytes, 0); break; case RasterSampleFormat.UNSIGNED_INTEGER: heightValue = BitConverter.ToUInt16(heightBytes, 0); break; default: throw new Exception("Sample format unsupported."); } } else { 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 < 32768) { heightMap.Minimum = Math.Min(metadata.MinimumAltitude, heightValue); heightMap.Maximum = Math.Max(metadata.MaximumAltitude, heightValue); } else { heightValue = 0; } coords.Add(new GeoPoint(latitude, longitude, heightValue)); } } heightMap.Coordinates = coords; return(heightMap); }