/// <summary> /// Apply the write operation for the specified geometry. /// </summary> /// <param name="geometry">The geometry.</param> /// <exception cref="System.ArgumentException">The number of images in the stream has reached the maximum.</exception> protected override void ApplyWriteGeometry(IGeometry geometry) { if (_imageCount == _maxImageCount) { throw new InvalidOperationException("The number of images in the stream has reached the maximum."); } IRaster raster = (geometry as ISpectralGeometry).Raster; if (raster == null) { return; } // TODO: compute the format more exactly Int64 contentSize = (Int64)raster.RadiometricResolution / 8 * raster.NumberOfBands * raster.NumberOfColumns * raster.NumberOfRows; InitializeStream(contentSize > BigTiffThreshold ? TiffStructure.BigTiff : TiffStructure.RegularTiff); TiffCompression compression = TiffCompression.None; // TODO: support other compressions TiffSampleFormat sampleFormat = (geometry as ISpectralGeometry).Raster.Format == RasterFormat.Floating ? TiffSampleFormat.Floating : TiffSampleFormat.UnsignedInteger; TiffImageFileDirectory imageFileDirectory = ComputeImageFileDirectory(geometry as ISpectralGeometry, compression, sampleFormat); // compute and update raster content position Int64 imageFileDirectorySize = ComputeImageFileDirectorySize(imageFileDirectory); Int64 rasterContentStartPosition = _baseStream.Position + imageFileDirectorySize; Int64 rasterContentSize = ComputeRasterContentSize(raster); // strip offset and length switch (_structure) { case TiffStructure.RegularTiff: imageFileDirectory[TiffTag.StripOffsets][0] = (UInt32)rasterContentStartPosition; imageFileDirectory[TiffTag.StripByteCounts][0] = (UInt32)rasterContentSize; WriteImageFileDirectory(imageFileDirectory); break; case TiffStructure.BigTiff: imageFileDirectory[TiffTag.StripOffsets][0] = (UInt64)rasterContentStartPosition; imageFileDirectory[TiffTag.StripByteCounts][0] = (UInt64)rasterContentSize; WriteBigImageFileDirectory(imageFileDirectory); break; } // perform writing based on representation WriteRasterContentToStrip((geometry as ISpectralGeometry).Raster, compression, sampleFormat); _imageCount++; }
/// <summary> /// Writes the raster of the geometry into a strip. /// </summary> /// <param name="raster">The raster.</param> /// <param name="compression">The compression.</param> /// <param name="format">The sample format.</param> private void WriteRasterContentToStrip(IRaster raster, TiffCompression compression, TiffSampleFormat format) { _baseStream.Seek(_currentImageStartPosition, SeekOrigin.Begin); // mark the starting position of the strip UInt32 numberOfBytes = (UInt32)(Math.Ceiling(raster.RadiometricResolution / 8.0) * raster.NumberOfBands * raster.NumberOfRows * raster.NumberOfColumns); UInt32 numberOfBytesLeft = numberOfBytes; if (numberOfBytes % 2 != 0) // correct the number of bytes { numberOfBytes++; } Byte[] bytes = new Byte[numberOfBytes < NumberOfWritableBytes ? numberOfBytes : NumberOfWritableBytes]; Int32 byteIndex = 0; Int32 bitIndex = 8; for (Int32 rowIndex = 0; rowIndex < raster.NumberOfRows; rowIndex++) { for (Int32 columnIndex = 0; columnIndex < raster.NumberOfColumns; columnIndex++) { // write the values for each band into the buffer for (Int32 bandIndex = 0; bandIndex < raster.NumberOfBands; bandIndex++) { switch (format) { case TiffSampleFormat.Undefined: case TiffSampleFormat.UnsignedInteger: WriteUnsignedIntergerValue(raster.GetValue(rowIndex, columnIndex, bandIndex), raster.RadiometricResolution, bytes, ref byteIndex, ref bitIndex); break; case TiffSampleFormat.SignedInteger: case TiffSampleFormat.Floating: WriteFloatValue(raster.GetFloatValue(rowIndex, columnIndex, bandIndex), raster.RadiometricResolution, bytes, ref byteIndex, ref bitIndex); break; } if (byteIndex == bytes.Length) { // write the buffer to the file _baseStream.Write(bytes, 0, byteIndex); byteIndex = 0; numberOfBytesLeft -= (UInt32)byteIndex; // the final array of bytes should not be the number of bytes left if (numberOfBytes > NumberOfWritableBytes && numberOfBytesLeft > 0 && numberOfBytesLeft < NumberOfWritableBytes) { bytes = new Byte[numberOfBytesLeft % 2 == 0 ? numberOfBytesLeft : numberOfBytesLeft + 1]; } } } } } // if any values are left if (numberOfBytesLeft > 0) { _baseStream.Write(bytes, 0, byteIndex); } }
/// <summary> /// Computes the image file directory of a geometry. /// </summary> /// <param name="geometry">The geometry.</param> /// <param name="compression">The compression.</param> /// <param name="format">The sample format.</param> /// <param name="startPosition">The starting position of the raster content within the stream.</param> /// <param name="endPosition">The ending position of the raster content within the stream.</param> /// <returns>The computed image file directory.</returns> protected virtual TiffImageFileDirectory ComputeImageFileDirectory(ISpectralGeometry geometry, TiffCompression compression, TiffSampleFormat format) { TiffImageFileDirectory imageFileDirectory = new TiffImageFileDirectory(); // compute photometric interpretation TiffPhotometricInterpretation photometricInterpretation = ComputePhotometricInterpretation(geometry); // add raster properties AddImageTag(imageFileDirectory, TiffTag.PhotometricInterpretation, (UInt16)photometricInterpretation); AddImageTag(imageFileDirectory, TiffTag.Compression, (UInt16)compression); AddImageTag(imageFileDirectory, TiffTag.ImageLength, (UInt32)geometry.Raster.NumberOfRows); AddImageTag(imageFileDirectory, TiffTag.ImageWidth, (UInt32)geometry.Raster.NumberOfColumns); AddImageTag(imageFileDirectory, TiffTag.ResolutionUnit, (UInt16)2); AddImageTag(imageFileDirectory, TiffTag.XResolution, (geometry.Raster.Mapper != null) ? (Rational)geometry.Raster.Mapper.ColumnSize : (Rational)1); AddImageTag(imageFileDirectory, TiffTag.YResolution, (geometry.Raster.Mapper != null) ? (Rational)geometry.Raster.Mapper.RowSize : (Rational)1); AddImageTag(imageFileDirectory, TiffTag.RowsPerStrip, (UInt32)geometry.Raster.NumberOfRows); AddImageTag(imageFileDirectory, TiffTag.StripOffsets, (UInt32)0); AddImageTag(imageFileDirectory, TiffTag.StripByteCounts, (UInt32)0); AddImageTag(imageFileDirectory, TiffTag.BitsPerSample, Enumerable.Repeat((UInt16)geometry.Raster.RadiometricResolution, geometry.Raster.NumberOfBands).Cast <Object>().ToArray()); AddImageTag(imageFileDirectory, TiffTag.SamplesPerPixel, (UInt32)geometry.Raster.NumberOfBands); AddImageTag(imageFileDirectory, TiffTag.SampleFormat, (UInt16)format); // add color palette if (photometricInterpretation == TiffPhotometricInterpretation.PaletteColor) { imageFileDirectory.Add(TiffTag.ColorMap, ComputeColorMap(geometry)); } // add metadata AddImageTag(imageFileDirectory, TiffTag.DocumentName, geometry.Metadata, "GeoTIFF::DocumentName"); AddImageTag(imageFileDirectory, TiffTag.ImageDescription, geometry.Metadata, "GeoTIFF::ImageDescription"); AddImageTag(imageFileDirectory, TiffTag.DocumentName, geometry.Metadata, "GeoTIFF::Make"); AddImageTag(imageFileDirectory, TiffTag.Make, geometry.Metadata, "GeoTIFF::DocumentName"); AddImageTag(imageFileDirectory, TiffTag.Model, geometry.Metadata, "GeoTIFF::Model"); AddImageTag(imageFileDirectory, TiffTag.PageName, geometry.Metadata, "GeoTIFF::PageName"); AddImageTag(imageFileDirectory, TiffTag.Artist, geometry.Metadata, "GeoTIFF::Artist"); AddImageTag(imageFileDirectory, TiffTag.HostComputer, geometry.Metadata, "GeoTIFF::HostComputer"); AddImageTag(imageFileDirectory, TiffTag.Copyright, geometry.Metadata, "GeoTIFF::Copyright"); AddImageTag(imageFileDirectory, TiffTag.Software, "AEGIS Geospatial Framework"); AddImageTag(imageFileDirectory, TiffTag.DateTime, DateTime.Now.ToString(CultureInfo.InvariantCulture.DateTimeFormat)); Dictionary <String, Object> attributes = geometry.Metadata.Where(attribute => !attribute.Key.Contains("GeoTIFF")).ToDictionary(attribute => attribute.Key, attribute => attribute.Value); if (geometry.Metadata.Count(attribute => !attribute.Key.Contains("GeoTIFF")) > 0) { imageFileDirectory.Add(TiffTag.AegisAttributes, new Object[] { JsonConvert.SerializeObject(attributes, Formatting.None, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }) }); } return(imageFileDirectory); }
/// <summary> /// Computes the Image File Directory of a geometry. /// </summary> /// <param name="geometry">The geometry.</param> /// <param name="compression">The compression.</param> /// <param name="format">The sample format.</param> /// <param name="startPosition">The starting position of the raster content within the stream.</param> /// <param name="endPosition">The ending position of the raster content within the stream.</param> /// <returns>The computed Image File Directory.</returns> protected override TiffImageFileDirectory ComputeImageFileDirectory(ISpectralGeometry geometry, TiffCompression compression, TiffSampleFormat format) { TiffImageFileDirectory imageFileDirectory = base.ComputeImageFileDirectory(geometry, compression, format); CoordinateReferenceSystem referenceSystem = geometry.ReferenceSystem as CoordinateReferenceSystem; GeoKeyDirectory geoKeyDirectory = new GeoKeyDirectory(); AddGeoKey(geoKeyDirectory, GeoKey.Citation, geometry.Metadata, "GeoTIFF::GeoCitation"); AddGeoKey(geoKeyDirectory, GeoKey.GeodeticCoordinateReferenceSystemCitation, geometry.Metadata, "GeoTIFF::GeodeticCoordinateReferenceSystemCitation"); AddGeoKey(geoKeyDirectory, GeoKey.ProjectedCoordinateReferenceSystemCitation, geometry.Metadata, "GeoTIFF::ProjectedCoordinateReferenceSystemCitation"); if (geometry.Raster.Mapper != null) // if mapper is available { geoKeyDirectory.Add(GeoKey.RasterType, (Int16)((geometry.Raster.Mapper.Mode == RasterMapMode.ValueIsArea) ? 1 : 2)); imageFileDirectory.Add(TiffTag.ModelTiepointTag, new Object[] { 0.0, 0.0, 0.0, geometry.Raster.Mapper.Translation.X, geometry.Raster.Mapper.Translation.Y, 0.0 }); imageFileDirectory.Add(TiffTag.ModelPixelScaleTag, new Object[] { geometry.Raster.Mapper.ColumnSize, geometry.Raster.Mapper.RowSize, 1.0 }); } if (referenceSystem != null) // if reference system is available (and supported) { switch (referenceSystem.Type) { case ReferenceSystemType.Projected: ComputeProjectedCoordinateReferenceSystem(geoKeyDirectory, referenceSystem as ProjectedCoordinateReferenceSystem); break; case ReferenceSystemType.Geographic2D: case ReferenceSystemType.Geographic3D: ComputeGeodeticCoordinateReferenceSystem(geoKeyDirectory, referenceSystem as GeographicCoordinateReferenceSystem); break; default: // other reference systems are not supported return(imageFileDirectory); } } WriteGeoKeyDirectory(imageFileDirectory, geoKeyDirectory); if (geometry.Imaging != null) // add imaging data { imageFileDirectory.Add(57410, new Object[] { geometry.Imaging.Device.Name }); imageFileDirectory.Add(57411, new Object[] { geometry.Imaging.Time.ToString(CultureInfo.InvariantCulture.DateTimeFormat) }); imageFileDirectory.Add(57412, new Object[] { geometry.Imaging.DeviceLocation.Latitude.BaseValue, geometry.Imaging.DeviceLocation.Longitude.BaseValue, geometry.Imaging.DeviceLocation.Height.BaseValue }); imageFileDirectory.Add(57413, new Object[] { geometry.Imaging.IncidenceAngle, geometry.Imaging.ViewingAngle, geometry.Imaging.SunAzimuth, geometry.Imaging.SunElevation }); imageFileDirectory.Add(57417, geometry.Imaging.Bands.Select(band => band.PhysicalGain).Cast <Object>().ToArray()); imageFileDirectory.Add(57418, geometry.Imaging.Bands.Select(band => band.PhysicalBias).Cast <Object>().ToArray()); imageFileDirectory.Add(57419, geometry.Imaging.Bands.Select(band => band.SolarIrradiance).Cast <Object>().ToArray()); Object[] imageLocation = new Object[12]; for (Int32 coordinateIndex = 0; coordinateIndex < geometry.Imaging.ImageLocation.Count; coordinateIndex++) { imageLocation[3 * coordinateIndex] = geometry.Imaging.ImageLocation[coordinateIndex].Latitude.BaseValue; imageLocation[3 * coordinateIndex + 1] = geometry.Imaging.ImageLocation[coordinateIndex].Longitude.BaseValue; imageLocation[3 * coordinateIndex + 2] = geometry.Imaging.ImageLocation[coordinateIndex].Height.BaseValue; } imageFileDirectory.Add(57420, imageLocation); } return(imageFileDirectory); }