From efac2b5d68c7fc4912077613f7f35944e419b223 Mon Sep 17 00:00:00 2001 From: Fabricio Rodrigues Date: Wed, 4 May 2022 11:33:46 -0300 Subject: [PATCH] Reading CoordinateSystemId and other GeoTiff attributes --- GeoTiffCOG/GeoTiff.cs | 182 +++++++++++++++++- GeoTiffCOG/GeoTiffCOG.csproj | 9 +- GeoTiffCOG/Struture/AngularUnits.cs | 20 ++ GeoTiffCOG/Struture/GEOTIFF_TAGS.cs | 47 +++++ GeoTiffCOG/Struture/GEOTiffProperties.cs | 30 +++ GeoTiffCOG/Struture/GeoTiffKey.cs | 63 ++++++ GeoTiffCOG/Struture/LinearUnits.cs | 27 +++ .../Struture/{FileMetaData.cs => MetaData.cs} | 27 +-- GeoTiffCOG/Struture/ModelType.cs | 29 +++ GeoTiffCOG/Struture/RasterType.cs | 17 ++ GeoTiffCOG/Utils.cs | 2 +- 11 files changed, 423 insertions(+), 30 deletions(-) create mode 100644 GeoTiffCOG/Struture/AngularUnits.cs create mode 100644 GeoTiffCOG/Struture/GEOTIFF_TAGS.cs create mode 100644 GeoTiffCOG/Struture/GEOTiffProperties.cs create mode 100644 GeoTiffCOG/Struture/GeoTiffKey.cs create mode 100644 GeoTiffCOG/Struture/LinearUnits.cs rename GeoTiffCOG/Struture/{FileMetaData.cs => MetaData.cs} (84%) create mode 100644 GeoTiffCOG/Struture/ModelType.cs create mode 100644 GeoTiffCOG/Struture/RasterType.cs diff --git a/GeoTiffCOG/GeoTiff.cs b/GeoTiffCOG/GeoTiff.cs index 7240731..66380ad 100644 --- a/GeoTiffCOG/GeoTiff.cs +++ b/GeoTiffCOG/GeoTiff.cs @@ -14,7 +14,7 @@ public class GeoTiff string _tiffPath; TraceTiffErrorHandler _traceLogHandler = new TraceTiffErrorHandler(); Dictionary tilesCache; - public FileMetadata metadata { get; private set; } + public Metadata metadata { get; private set; } internal Tiff TiffFile { @@ -286,9 +286,10 @@ private GDALMetaData ParseXml(string xml) return gdal; } - private FileMetadata ParseMetaData(DEMFileDefinition format) + + private Metadata ParseMetaData(DEMFileDefinition format) { - FileMetadata _metadata = new FileMetadata(FilePath, format); + Metadata _metadata = new Metadata(FilePath, format); _metadata.Height = TiffFile.GetField(TiffTag.IMAGELENGTH)[0].ToInt(); _metadata.Width = TiffFile.GetField(TiffTag.IMAGEWIDTH)[0].ToInt(); @@ -347,29 +348,190 @@ private FileMetadata ParseMetaData(DEMFileDefinition format) _metadata.BitsPerSample = TiffFile.GetField(TiffTag.BITSPERSAMPLE)[0].ToInt(); var sampleFormat = TiffFile.GetField(TiffTag.SAMPLEFORMAT); _metadata.SampleFormat = sampleFormat[0].Value.ToString(); - _metadata.WorldUnits = "meter"; + + LoadGEOTiffTags(_metadata); + + return _metadata; + } + + private void LoadGEOTiffTags(Metadata _metadata) + { + //defaults + _metadata.Properties.CoordinateSystemId = 4326; // WGS 84 + _metadata.Properties.ModelType = ModelType.Geographic; + _metadata.Properties.RasterType = RasterType.RasterPixelIsArea; + _metadata.Properties.AngularUnit = AngularUnits.Degree; + + var geoKeys = TiffFile.GetField((TiffTag)GEOTIFF_TAGS.GEOTIFF_GEOKEYDIRECTORYTAG); + if (geoKeys != null) + { + var geoDoubleParams = TiffFile.GetField((TiffTag)GEOTIFF_TAGS.GEOTIFF_GEODOUBLEPARAMSTAG); + double[] doubleParams = null; + if (geoDoubleParams != null) + { + doubleParams = geoDoubleParams[1].ToDoubleArray(); + } + var geoAsciiParams = TiffFile.GetField((TiffTag)GEOTIFF_TAGS.GEOTIFF_GEOASCIIPARAMSTAG); + if (geoAsciiParams != null) _metadata.Properties.GeotiffAsciiParams = geoAsciiParams[1].ToString().Trim('\0'); + + // 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)GeoTiffKey.GTModelTypeGeoKey: + { + _metadata.Properties.ModelType = (ModelType)keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GTRasterTypeGeoKey: + { + _metadata.Properties.RasterType = (RasterType)keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GTCitationGeoKey: + { + _metadata.Properties.GTCitationGeo = keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeographicTypeGeoKey: + { + _metadata.Properties.CoordinateSystemId = keys[keyIndex + 3]; //geographicType + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogCitationGeoKey: + { + _metadata.Properties.GeogCitation = keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogGeodeticDatumGeoKey: + { + // 6.3.2.2 Geodetic Datum Codes + _metadata.Properties.GeodeticDatum = keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogPrimeMeridianGeoKey: + { + // 6.3.2.4 Prime Meridian Codes + _metadata.Properties.PrimeMeridian = keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogAngularUnitsGeoKey: + { + _metadata.Properties.AngularUnit = (AngularUnits)keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogAngularUnitSizeGeoKey: + { + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogEllipsoidGeoKey: + { + // 6.3.2.3 Ellipsoid Codes + _metadata.Properties.Ellipsoid = keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogSemiMajorAxisGeoKey: + { + if (doubleParams != null) + _metadata.Properties.SemiMajorAxis = doubleParams[keys[keyIndex + 3]]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogSemiMinorAxisGeoKey: + { + if (doubleParams != null) + _metadata.Properties.SemiMinorAxis = doubleParams[keys[keyIndex + 3]]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogInvFlatteningGeoKey: + { + if (doubleParams != null) + _metadata.Properties.InvFlattening = doubleParams[keys[keyIndex + 3]]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogAzimuthUnitsGeoKey: + { + _metadata.Properties.AngularUnit = (AngularUnits)keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.GeogPrimeMeridianLongGeoKey: + { + _metadata.Properties.PrimeMeridianLong = keys[keyIndex + 3]; + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.ProjectedCSTypeGeoKey: + { + _metadata.Properties.CoordinateSystemId = keys[keyIndex + 3]; //projectedCSType + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.PCSCitationGeoKey: + { + keyIndex += 4; + break; + } + case (ushort)GeoTiffKey.ProjLinearUnitsGeoKey: + { + _metadata.Properties.LinearUnit = (LinearUnits)keys[keyIndex + 3]; + keyIndex += 4; + break; + } + default: + { + // Just skipping all unprocessed keys + keyIndex += 4; + break; + } + } + } + + } + + } // TIFF Tag GDAL_METADATA 42112 _metadata.Scale = 1; - var tag42112 = TiffFile.GetField((TiffTag)42112); + var tag42112 = TiffFile.GetField((TiffTag)GEOTIFF_TAGS.GEOTIFF_GDAL_METADATA); if (tag42112 != null && tag42112.Length >= 1) { - var xml = tag42112[1].ToString().Trim('\0'); - var gd = ParseXml(xml); + _metadata.Properties.GdalMetadata = tag42112[1].ToString().Trim('\0'); + var gd = ParseXml(_metadata.Properties.GdalMetadata); _metadata.Offset = gd.Offset; _metadata.Scale = gd.Scale; } // TIFF Tag GDAL_NODATA 42113 _metadata.NoDataValue = "-10000"; - var tag42113 = TiffFile.GetField((TiffTag)42113); + var tag42113 = TiffFile.GetField((TiffTag)GEOTIFF_TAGS.GEOTIFF_GDAL_NODATA); if (tag42113 != null && tag42113.Length >= 1) { _metadata.NoDataValue = tag42113[1].ToString().Trim('\0'); } - - return _metadata; } + public HeightMap GetHeightMapInBBox(BoundingBox bbox, float noDataValue = 0) { int yStart = 0; diff --git a/GeoTiffCOG/GeoTiffCOG.csproj b/GeoTiffCOG/GeoTiffCOG.csproj index 3273a0b..34e7079 100644 --- a/GeoTiffCOG/GeoTiffCOG.csproj +++ b/GeoTiffCOG/GeoTiffCOG.csproj @@ -51,19 +51,26 @@ + - + + + + + + + diff --git a/GeoTiffCOG/Struture/AngularUnits.cs b/GeoTiffCOG/Struture/AngularUnits.cs new file mode 100644 index 0000000..ebad982 --- /dev/null +++ b/GeoTiffCOG/Struture/AngularUnits.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeoTiffCOG.Struture +{ + public enum AngularUnits + { + Radian = 9101, + Degree = 9102, + ArcMinute = 9103, + ArcSecond = 9104, + Grad = 9105, + Gon = 9106, + DMS = 9107, + DMSHemisphere = 9108, + } +} diff --git a/GeoTiffCOG/Struture/GEOTIFF_TAGS.cs b/GeoTiffCOG/Struture/GEOTIFF_TAGS.cs new file mode 100644 index 0000000..5004466 --- /dev/null +++ b/GeoTiffCOG/Struture/GEOTIFF_TAGS.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeoTiffCOG.Struture +{ + enum GEOTIFF_TAGS + { + /// + /// This tag is defining exact affine transformations between raster and model space. Used in interchangeable GeoTIFF files. + /// + GEOTIFF_MODELPIXELSCALETAG = 33550, + + /// + /// This tag stores raster->model tiepoint pairs. Used in interchangeable GeoTIFF files. + /// + GEOTIFF_MODELTIEPOINTTAG = 33922, + + /// + /// This tag is optionally provided for defining exact affine transformations between raster and model space. Used in interchangeable GeoTIFF files. + /// + GEOTIFF_MODELTRANSFORMATIONTAG = 34264, + + /// + /// This tag may be used to store the GeoKey Directory, which defines and references the "GeoKeys". Used in interchangeable GeoTIFF files. + /// + GEOTIFF_GEOKEYDIRECTORYTAG = 34735, + + /// + /// This tag is used to store all of the DOUBLE valued GeoKeys, referenced by the GeoKeyDirectoryTag. Used in interchangeable GeoTIFF files. + /// + GEOTIFF_GEODOUBLEPARAMSTAG = 34736, + + /// + /// This tag is used to store all of the ASCII valued GeoKeys, referenced by the GeoKeyDirectoryTag. Used in interchangeable GeoTIFF files. + /// + GEOTIFF_GEOASCIIPARAMSTAG = 34737, + + // TIFF Tag GDAL_METADATA 42112 + GEOTIFF_GDAL_METADATA = 42112, + + // TIFF Tag GDAL_NODATA 42113 + GEOTIFF_GDAL_NODATA = 42113 + } +} diff --git a/GeoTiffCOG/Struture/GEOTiffProperties.cs b/GeoTiffCOG/Struture/GEOTiffProperties.cs new file mode 100644 index 0000000..ce1be19 --- /dev/null +++ b/GeoTiffCOG/Struture/GEOTiffProperties.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeoTiffCOG.Struture +{ + public class GEOTiffProperties + { + public ModelType ModelType { get; set; } + public RasterType RasterType { get; set; } + public ushort GTCitationGeo { get; set; } + public ushort GeogCitation { get; set; } + public ushort GeodeticDatum { get; set; } + public ushort PrimeMeridian { get; set; } + public AngularUnits AngularUnit { get; set; } + public ushort Ellipsoid { get; set; } + public double SemiMajorAxis { get; set; } + public double SemiMinorAxis { get; set; } + public double InvFlattening { get; set; } + public ushort PrimeMeridianLong { get; set; } + public LinearUnits LinearUnit { get; set; } + public ushort CoordinateSystemId { get; set; } + public string GdalMetadata { get; set; } + public string GeotiffAsciiParams { get; set; } + + + } +} diff --git a/GeoTiffCOG/Struture/GeoTiffKey.cs b/GeoTiffCOG/Struture/GeoTiffKey.cs new file mode 100644 index 0000000..d6fe437 --- /dev/null +++ b/GeoTiffCOG/Struture/GeoTiffKey.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeoTiffCOG.Struture +{ + enum GeoTiffKey + { + // 6.2.1 GeoTIFF Configuration Keys + GTModelTypeGeoKey = 1024, + GTRasterTypeGeoKey = 1025, + GTCitationGeoKey = 1026, + + // 6.2.2 Geographic CS Parameter Keys + GeographicTypeGeoKey = 2048, + GeogCitationGeoKey = 2049, // documentation + GeogGeodeticDatumGeoKey = 2050, + GeogPrimeMeridianGeoKey = 2051, + GeogLinearUnitsGeoKey = 2052, + GeogLinearUnitSizeGeoKey = 2053, // meters + GeogAngularUnitsGeoKey = 2054, + GeogAngularUnitSizeGeoKey = 2055, // radians + GeogEllipsoidGeoKey = 2056, // Section 6.3.2.3 Codes + GeogSemiMajorAxisGeoKey = 2057, // GeogLinearUnits + GeogSemiMinorAxisGeoKey = 2058, // GeogLinearUnits + GeogInvFlatteningGeoKey = 2059, + GeogAzimuthUnitsGeoKey = 2060, + GeogPrimeMeridianLongGeoKey = 2061, + + // 6.2.3 Projected CS Parameter Keys + ProjectedCSTypeGeoKey = 3072, + PCSCitationGeoKey = 3073, + ProjCoordTransGeoKey = 3075, + ProjLinearUnitsGeoKey = 3076, + ProjLinearUnitSizeGeoKey = 3077, + ProjStdParallel1GeoKey = 3078, + ProjStdParallel2GeoKey = 3079, + ProjNatOriginLongGeoKey = 3080, + ProjNatOriginLatGeoKey = 3081, + ProjFalseEastingGeoKey = 3082, + ProjFalseNorthingGeoKey = 3083, + ProjFalseOriginLongGeoKey = 3084, + ProjFalseOriginLatGeoKey = 3085, + ProjFalseOriginEastingGeoKey = 3086, + ProjFalseOriginNorthingGeoKey = 3087, + ProjCenterLongGeoKey = 3088, + ProjCenterLatGeoKey = 3089, + ProjCenterEastingGeoKey = 3090, + ProjCenterNorthingGeoKey = 3091, + ProjScaleAtNatOriginGeoKey = 3092, + ProjScaleAtCenterGeoKey = 3093, + ProjAzimuthAngleGeoKey = 3094, + ProjStraightVertPoleLongGeoKey = 3095, + + // 6.2.4 Vertical CS Keys + VerticalCSTypeGeoKey = 4096, + VerticalCitationGeoKey = 4097, + VerticalDatumGeoKey = 4098, + VerticalUnitsGeoKey = 4099, + } +} \ No newline at end of file diff --git a/GeoTiffCOG/Struture/LinearUnits.cs b/GeoTiffCOG/Struture/LinearUnits.cs new file mode 100644 index 0000000..3b7914f --- /dev/null +++ b/GeoTiffCOG/Struture/LinearUnits.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeoTiffCOG.Struture +{ + public enum LinearUnits + { + Meter = 9001, + Foot = 9002, + FootUSSurvey = 9003, + FootModifiedAmerican = 9004, + FootClarke = 9005, + FootIndian = 9006, + Link = 9007, + LinkBenoit = 9008, + LinkSears = 9009, + ChainBenoit = 9010, + ChainSears = 9011, + YardSears = 9012, + YardIndian = 9013, + Fathom = 9014, + MileInternationalNautical = 9015, + } +} diff --git a/GeoTiffCOG/Struture/FileMetaData.cs b/GeoTiffCOG/Struture/MetaData.cs similarity index 84% rename from GeoTiffCOG/Struture/FileMetaData.cs rename to GeoTiffCOG/Struture/MetaData.cs index 21790a6..042a133 100644 --- a/GeoTiffCOG/Struture/FileMetaData.cs +++ b/GeoTiffCOG/Struture/MetaData.cs @@ -7,25 +7,16 @@ namespace GeoTiffCOG.Struture { - public class FileMetadata : IEquatable + public class Metadata : IEquatable { - #region Versioning + public const string METADATA_VERSION = "2.3"; - /* History - * - * 2.1 : file name are relative to data directory - * 2.2 : [Metadata regneration required] file format is now mapped to DEMFileDefinition, lat/lon bounds names changed for clarity, file format changed from DEMFileFormat (name + file extenstion) - */ - - public const string FILEMETADATA_VERSION = "2.2"; - #endregion - - - public FileMetadata(string filename, DEMFileDefinition fileFormat, string version = FILEMETADATA_VERSION) + public Metadata(string filename, DEMFileDefinition fileFormat, string version = METADATA_VERSION) { this.Filename = filename; this.FileFormat = fileFormat; this.Version = version; + this.Properties = new GEOTiffProperties(); } /// @@ -59,7 +50,6 @@ public FileMetadata(string filename, DEMFileDefinition fileFormat, string versio /// public double DataEndLon { get; set; } public int BitsPerSample { get; set; } - public string WorldUnits { get; set; } public string SampleFormat { get; set; } public string NoDataValue { get; set; } public int ScanlineSize { get; set; } @@ -75,6 +65,7 @@ public FileMetadata(string filename, DEMFileDefinition fileFormat, string versio public double pixelSizeX { get; set; } public double pixelSizeY { get; set; } public DEMFileDefinition FileFormat { get; set; } + public GEOTiffProperties Properties { get; set; } public float MinimumAltitude { get; set; } public float MaximumAltitude { get; set; } @@ -106,7 +97,7 @@ public override string ToString() public override bool Equals(object obj) { - FileMetadata objTyped = obj as FileMetadata; + Metadata objTyped = obj as Metadata; if (objTyped == null) return false; @@ -117,7 +108,7 @@ public override int GetHashCode() return Path.GetFileName(Filename).GetHashCode(); } - public bool Equals(FileMetadata other) + public bool Equals(Metadata other) { if (this == null || other == null) return false; @@ -141,9 +132,9 @@ public BoundingBox BoundingBox } } - public FileMetadata Clone() + public Metadata Clone() { - FileMetadata clone = (FileMetadata)this.MemberwiseClone(); + Metadata clone = (Metadata)this.MemberwiseClone(); clone.Filename = Guid.NewGuid().ToString(); clone._boundingBox = null; return clone; diff --git a/GeoTiffCOG/Struture/ModelType.cs b/GeoTiffCOG/Struture/ModelType.cs new file mode 100644 index 0000000..66552c5 --- /dev/null +++ b/GeoTiffCOG/Struture/ModelType.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeoTiffCOG.Struture +{ + /// + /// Model Type. + /// + public enum ModelType + { + /// + /// Projection Coordinate System. + /// + Projected = 1, + + /// + /// Geographic latitude-longitude System. + /// + Geographic = 2, + + /// + /// Geocentric (X,Y,Z) Coordinate System. + /// + Geocentric = 3, + } +} diff --git a/GeoTiffCOG/Struture/RasterType.cs b/GeoTiffCOG/Struture/RasterType.cs new file mode 100644 index 0000000..1862c06 --- /dev/null +++ b/GeoTiffCOG/Struture/RasterType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GeoTiffCOG.Struture +{ + /// + /// Raster Types. + /// + public enum RasterType + { + RasterPixelIsArea = 1, + RasterPixelIsPoint = 2, + } +} diff --git a/GeoTiffCOG/Utils.cs b/GeoTiffCOG/Utils.cs index ee73f7e..aaf6da8 100644 --- a/GeoTiffCOG/Utils.cs +++ b/GeoTiffCOG/Utils.cs @@ -10,7 +10,7 @@ namespace GeoTiffCOG public static class Utils { - public static void GetXYFromLatLon(FileMetadata metadata, double latitude, double longitude, out int x, out int y) + public static void GetXYFromLatLon(Metadata metadata, double latitude, double longitude, out int x, out int y) { var bbox = new double[] { metadata.DataStartLon, metadata.DataStartLat, metadata.DataEndLon, metadata.DataEndLat }; var pixelWidth = metadata.Width;