private static double YToGeoTiffPixelY(M.RasterProperties rasterProperties, double y) { if (rasterProperties.ProjectedBounds == null) { throw new ArgumentNullException(nameof(rasterProperties), "rasterProperties.ProjectedBounds is null."); } return((rasterProperties.ProjectedBounds.Top - y) / rasterProperties.PixelHeight); }
private static double XToGeoTiffPixelX(M.RasterProperties rasterProperties, double x) { if (rasterProperties.ProjectedBounds == null) { throw new ArgumentNullException(nameof(rasterProperties), "rasterProperties.ProjectedBounds is null."); } return((x - rasterProperties.ProjectedBounds.Left) / rasterProperties.PixelWidth); }
private static GeoTiff.TileCoordinates GetGeoTiffTileCoordinatesAtPoint( M.RasterProperties rasterProperties, double x, double y) { var tileX = (int)Math.Floor(XToGeoTiffPixelX(rasterProperties, x) / (double)rasterProperties.TileWidth); var tileY = (int)Math.Floor(YToGeoTiffPixelY(rasterProperties, y) / (double)rasterProperties.TileHeight); return(new GeoTiff.TileCoordinates(tileX, tileY)); }
private static List <GeoTiff.TileCoordinates> BuildTileCoordinatesList( M.RasterProperties rasterProperties, M.Bounds bounds) { if (rasterProperties == null) { throw new ArgumentNullException(nameof(rasterProperties)); } if (rasterProperties.ProjectedBounds == null) { throw new ArgumentException("ProjectedBounds property is null."); } var tileCoordMin = GetGeoTiffTileCoordinatesAtPoint( rasterProperties, Math.Max(bounds.Left, rasterProperties.ProjectedBounds.Left), Math.Min(bounds.Top, rasterProperties.ProjectedBounds.Top)); var tileCoordMax = GetGeoTiffTileCoordinatesAtPoint( rasterProperties, Math.Min(bounds.Right, rasterProperties.ProjectedBounds.Right), Math.Min(bounds.Bottom, rasterProperties.ProjectedBounds.Bottom)); var tileCoordinates = new List <GeoTiff.TileCoordinates>(); for (var tileX = tileCoordMin.X; tileX <= tileCoordMax.X; tileX++) { for (var tileY = tileCoordMin.Y; tileY <= tileCoordMax.Y; tileY++) { tileCoordinates.Add(new GeoTiff.TileCoordinates(tileX, tileY)); } } return(tileCoordinates); }
private static M.RasterProperties ReadGeoTiffProperties(string path) { using var tiff = Tiff.Open(path, ModeOpenReadTiff); var planarConfig = (PlanarConfig)tiff.GetField(TiffTag.PLANARCONFIG)[0].ToInt(); if (planarConfig != PlanarConfig.CONTIG) { throw new FormatException($"Only single image plane storage organization ({PlanarConfig.CONTIG}) is supported"); } if (!tiff.IsTiled()) { throw new FormatException($"Only tiled storage scheme is supported"); } var imageWidth = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt(); var imageHeight = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt(); var tileWidth = tiff.GetField(TiffTag.TILEWIDTH)[0].ToInt(); var tileHeight = tiff.GetField(TiffTag.TILELENGTH)[0].ToInt(); // ModelPixelScale https://freeimage.sourceforge.io/fnet/html/CC586183.htm var modelPixelScale = tiff.GetField(TiffTag.GEOTIFF_MODELPIXELSCALETAG); var pixelSizesCount = modelPixelScale[0].ToInt(); var pixelSizes = modelPixelScale[1].ToDoubleArray(); // ModelTiePoints https://freeimage.sourceforge.io/fnet/html/38F9430A.htm var tiePointTag = tiff.GetField(TiffTag.GEOTIFF_MODELTIEPOINTTAG); var tiePointsCount = tiePointTag[0].ToInt(); var tiePoints = tiePointTag[1].ToDoubleArray(); if ((tiePoints.Length != 6) || (tiePoints[0] != 0) || (tiePoints[1] != 0) || (tiePoints[2] != 0) || (tiePoints[5] != 0)) { throw new FormatException($"Only single tie point is supported"); // TODO: Only simple tie points scheme is supported } var modelTransformation = tiff.GetField(TiffTag.GEOTIFF_MODELTRANSFORMATIONTAG); if (modelTransformation != null) { throw new FormatException($"Only simple projection without transformation is supported"); } var srId = 0; // Simple check SRS of GeoTIFF tie points var geoKeys = tiff.GetField(TiffTag.GEOTIFF_GEOKEYDIRECTORYTAG); if (geoKeys != null) { var geoDoubleParams = tiff.GetField(TiffTag.GEOTIFF_GEODOUBLEPARAMSTAG); double[]? doubleParams = null; if (geoDoubleParams != null) { doubleParams = geoDoubleParams[1].ToDoubleArray(); } var geoAsciiParams = tiff.GetField(TiffTag.GEOTIFF_GEOASCIIPARAMSTAG); // Array of GeoTIFF GeoKeys values var keys = geoKeys[1].ToUShortArray(); if (keys.Length > 4) { // Header={KeyDirectoryVersion, KeyRevision, MinorRevision, NumberOfKeys} var keyDirectoryVersion = keys[0]; var keyRevision = keys[1]; var minorRevision = keys[2]; var numberOfKeys = keys[3]; for (var keyIndex = 4; keyIndex < keys.Length;) { switch (keys[keyIndex]) { case (ushort)GeoTiff.Key.GTModelTypeGeoKey: { var modelType = (GeoTiff.ModelType)keys[keyIndex + 3]; if (!((modelType == GeoTiff.ModelType.Projected) || (modelType == GeoTiff.ModelType.Geographic))) { throw new FormatException("Only coordinate systems ModelTypeProjected (1) or ModelTypeGeographic (2) are supported"); } keyIndex += 4; break; } case (ushort)GeoTiff.Key.GTRasterTypeGeoKey: { var rasterType = (GeoTiff.RasterType)keys[keyIndex + 3]; // TODO: use RasterTypeCode value keyIndex += 4; break; } case (ushort)GeoTiff.Key.GTCitationGeoKey: { var gtc = keys[keyIndex + 3]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeographicTypeGeoKey: { var geographicType = keys[keyIndex + 3]; if (geographicType != 4326) { throw new FormatException("Only EPSG:4326 geodetic coordinate system is supported"); } srId = geographicType; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogCitationGeoKey: { var geogCitation = keys[keyIndex + 3]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogGeodeticDatumGeoKey: { // 6.3.2.2 Geodetic Datum Codes var geodeticDatum = keys[keyIndex + 3]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogPrimeMeridianGeoKey: { // 6.3.2.4 Prime Meridian Codes var primeMeridian = keys[keyIndex + 3]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogAngularUnitsGeoKey: { var geogAngularUnit = (GeoTiff.AngularUnits)keys[keyIndex + 3]; if (geogAngularUnit != GeoTiff.AngularUnits.Degree) { throw new FormatException("Only degree angular unit is supported"); } keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogAngularUnitSizeGeoKey: { keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogEllipsoidGeoKey: { // 6.3.2.3 Ellipsoid Codes var geogEllipsoid = keys[keyIndex + 3]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogSemiMajorAxisGeoKey: { if (doubleParams == null) { throw new FormatException($"Double values were not found in '{TiffTag.GEOTIFF_GEODOUBLEPARAMSTAG}' tag"); } var geogSemiMajorAxis = doubleParams[keys[keyIndex + 3]]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogSemiMinorAxisGeoKey: { if (doubleParams == null) { throw new FormatException($"Double values were not found in '{TiffTag.GEOTIFF_GEODOUBLEPARAMSTAG}' tag"); } var geogSemiMinorAxis = doubleParams[keys[keyIndex + 3]]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogInvFlatteningGeoKey: { if (doubleParams == null) { throw new FormatException($"Double values were not found in '{TiffTag.GEOTIFF_GEODOUBLEPARAMSTAG}' tag"); } var geogInvFlattening = doubleParams[keys[keyIndex + 3]]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogAzimuthUnitsGeoKey: { // 6.3.1.4 Angular Units Codes var geogAzimuthUnits = (GeoTiff.AngularUnits)keys[keyIndex + 3]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.GeogPrimeMeridianLongGeoKey: { var geogPrimeMeridianLong = keys[keyIndex + 3]; keyIndex += 4; break; } case (ushort)GeoTiff.Key.ProjectedCSTypeGeoKey: { var projectedCSType = keys[keyIndex + 3]; if (projectedCSType != 3857) { throw new FormatException($"Only EPSG:3857 projected coordinate system is supported (input was: {projectedCSType})"); } // TODO: UTM (EPSG:32636 and others) support srId = projectedCSType; keyIndex += 4; break; } case (ushort)GeoTiff.Key.PCSCitationGeoKey: { keyIndex += 4; break; } case (ushort)GeoTiff.Key.ProjLinearUnitsGeoKey: { var linearUnit = (GeoTiff.LinearUnits)keys[keyIndex + 3]; if (linearUnit != GeoTiff.LinearUnits.Meter) { throw new FormatException("Only meter linear unit is supported"); } keyIndex += 4; break; } default: { keyIndex += 4; // Just skipping all unprocessed keys break; } } } } } M.GeographicalBounds?geographicalBounds = null; M.Bounds? projectedBounds = null; double pixelWidth = 0.0, pixelHeight = 0.0; switch (srId) { case 4326: // TODO: const { geographicalBounds = new M.GeographicalBounds( tiePoints[3], tiePoints[4] - imageHeight * pixelSizes[1], tiePoints[3] + imageWidth * pixelSizes[0], tiePoints[4]); projectedBounds = new M.Bounds( U.WebMercator.X(tiePoints[3]), U.WebMercator.Y(tiePoints[4] - imageHeight * pixelSizes[1]), U.WebMercator.X(tiePoints[3] + imageWidth * pixelSizes[0]), U.WebMercator.Y(tiePoints[4])); pixelWidth = U.WebMercator.X(tiePoints[3] + pixelSizes[0]) - U.WebMercator.X(tiePoints[3]); pixelHeight = U.WebMercator.Y(tiePoints[4]) - U.WebMercator.Y(tiePoints[4] - pixelSizes[1]); break; } case 3857: // TODO: const { projectedBounds = new M.Bounds( tiePoints[3], tiePoints[4] - imageHeight * pixelSizes[1], tiePoints[3] + imageWidth * pixelSizes[0], tiePoints[4]); geographicalBounds = new M.GeographicalBounds( U.WebMercator.Longitude(tiePoints[3]), U.WebMercator.Latitude(tiePoints[4] - imageHeight * pixelSizes[1]), U.WebMercator.Longitude(tiePoints[3] + imageWidth * pixelSizes[0]), U.WebMercator.Latitude(tiePoints[4])); pixelWidth = pixelSizes[0]; pixelHeight = pixelSizes[1]; break; } default: { throw new InvalidOperationException($"SRID '{srId}' is not supported"); } } var result = new M.RasterProperties { Srid = srId, ImageWidth = imageWidth, ImageHeight = imageHeight, TileWidth = tileWidth, TileHeight = tileHeight, TileSize = tiff.TileSize(), ProjectedBounds = projectedBounds, GeographicalBounds = geographicalBounds, PixelWidth = pixelWidth, PixelHeight = pixelHeight, }; return(result); }