A modern C++20 library for reading and writing GeoTIFF files with integrated geospatial coordinate system support.
- Complete GeoTIFF Support: Read and write GeoTIFF files following the GeoTIFF Specification
- Multi-layer/Multi-IFD Support: Handle complex GeoTIFF files with multiple image layers, each IFD with its own tags
- Per-IFD Geospatial Metadata: Each layer can have independent coordinate systems, datums, and resolutions
- Comprehensive Tag Support: Full TIFF tag support including custom tags per IFD
- ENU Shift-Based Positioning: Use local ENU coordinates for precise spatial positioning with automatic WGS84 conversion
- Geospatial Grid Conversion: Automatic conversion between TIFF pixel data and georeferenced coordinate grids
- 8-bit Raster Support: Optimized for 8-bit grayscale raster data
- Header-only Library: Easy integration with CMake FetchContent or as a Git submodule
- Cross-platform: Works on Linux, macOS, and Windows
#include "geotiv/geotiv.hpp"
// Read a GeoTIFF file
auto rasterCollection = geotiv::ReadRasterCollection("input.tif");
// Access georeferenced data
const auto& layer = rasterCollection.layers[0];
const auto& grid = layer.grid;
// Get pixel value at specific grid coordinates
uint8_t pixelValue = grid(row, col);
// Write modified data back to GeoTIFF
geotiv::WriteRasterCollection(rasterCollection, "output.tif");
// Create a georeferenced raster using the high-level API
concord::Datum datum{48.0, 11.0, 500.0}; // WGS84 reference point
concord::Pose shift{concord::Point{100.0, 200.0, 0.0}, // ENU position: 100m East, 200m North
concord::Euler{0, 0, 0}}; // No rotation
double resolution = 2.0; // 2 meters per pixel
geotiv::Raster raster(datum, shift, resolution);
// Add a grid layer
uint32_t width = 200, height = 100;
raster.addGrid(width, height, "elevation", "terrain");
// Fill with data
auto& grid = raster.getGrid("elevation");
for (uint32_t r = 0; r < height; ++r) {
for (uint32_t c = 0; c < width; ++c) {
grid.grid(r, c) = computePixelValue(r, c);
}
}
// Save as GeoTIFF
raster.toFile("generated.tif");
geotiv::Raster
: High-level interface for creating and managing georeferenced raster datageotiv::RasterCollection
: Container for one or more georeferenced raster layersgeotiv::Layer
: Individual raster layer with pixel data and metadataconcord::Grid<uint8_t>
: Georeferenced grid with ENU shift-based positioning
Geotiv provides comprehensive support for multi-IFD GeoTIFF files, where each IFD represents a separate image layer with its own metadata and geospatial properties:
- Independent Geospatial Parameters: Each layer can have its own datum, ENU shift, and resolution
- Per-IFD Tag Storage: Every IFD maintains its own set of TIFF tags including:
- Standard TIFF tags (ImageWidth, ImageLength, BitsPerSample, etc.)
- GeoTIFF-specific tags (ModelPixelScaleTag, GeoKeyDirectoryTag, etc.)
- Custom application-specific tags via
customTags
map
- IFD Chain Management: Proper linking and traversal of IFD chains in multi-layer files
- Offset Tracking: Each layer stores its original IFD offset for reference
geotiv::Layer layer;
layer.datum = {47.5, 8.5, 200.0}; // WGS84 reference point (lat, lon, alt)
layer.shift = {concord::Point{100, 200, 0}, concord::Euler{0, 0, 0.5}}; // ENU position and rotation
layer.resolution = 2.0; // Meters per pixel
layer.imageDescription = "Layer 1 data"; // Custom description
layer.customTags[50000] = {42, 100}; // Custom application tags
geotiv::RasterCollection rc;
// Create multiple layers with different properties
for (int i = 0; i < 3; ++i) {
geotiv::Layer layer;
layer.datum = {47.0 + i * 0.1, 8.0 + i * 0.1, 100.0 + i * 50};
layer.shift = {concord::Point{i * 50.0, i * 100.0, 0}, concord::Euler{0, 0, i * 0.1}};
layer.resolution = 1.0 + i * 0.5;
// Each layer gets its own grid and metadata
layer.grid = createGridForLayer(i);
layer.width = layer.grid.cols();
layer.height = layer.grid.rows();
rc.layers.push_back(std::move(layer));
}
// Write multi-IFD GeoTIFF - each layer becomes its own IFD
geotiv::WriteRasterCollection(rc, "multi_layer.tif");
geotiv::ReadRasterCollection()
: Parse GeoTIFF files into structured datageotiv::WriteRasterCollection()
: Export raster collections to GeoTIFF formatgeotiv::toTiffBytes()
: Generate raw TIFF byte data for custom handling
Geotiv uses a shift-based coordinate system for maximum flexibility and precision:
- WGS84 Reference: All coordinate systems use WGS84 with EPSG:4326
- ENU Shift: Local positioning in East-North-Up coordinates relative to datum
- Automatic Conversion: ENU coordinates automatically converted to WGS84 for GeoTIFF storage
- Datum: WGS84 reference point for coordinate transformations
- Shift: ENU position and orientation (concord::Pose) for precise grid placement
- Automatic georeferencing: ModelTiepointTag maps image center to calculated WGS84 coordinates
- QGIS Compatible: Proper georeferencing tags for seamless GIS integration
// Coordinate system setup
concord::Datum datum{47.5, 8.5, 200.0}; // WGS84 reference point
concord::Pose shift{concord::Point{100.0, 200.0, 0.0}, // ENU position: 100m East, 200m North
concord::Euler{0, 0, 0.5}}; // 0.5 radians yaw rotation
// The raster will be positioned at datum + shift in ENU space
geotiv::Raster raster(datum, shift, resolution);
Feature | Support | Per-IFD |
---|---|---|
Reading GeoTIFF | β Full | β Yes |
Writing GeoTIFF | β Full | β Yes |
Multi-layer files | β Yes | β Independent |
8-bit grayscale | β Yes | β Per layer |
WGS84 coordinates | β Yes | β Standard |
ENU shift positioning | β Yes | β Per layer |
EPSG:4326 support | β Yes | β Automatic |
ModelTiepointTag | β Yes | β Per layer |
QGIS compatibility | β Yes | β Full |
Pixel scaling | β Yes | β Per layer |
Strip-based TIFF | β Yes | β Yes |
Little/Big endian | β Both | β Yes |
Custom TIFF tags | β Yes | β Per IFD |
Independent datums | β Yes | β Per layer |
The library supports comprehensive TIFF tag handling:
- 256: ImageWidth
- 257: ImageLength
- 258: BitsPerSample
- 259: Compression
- 262: PhotometricInterpretation
- 270: ImageDescription (with geospatial metadata)
- 273: StripOffsets
- 277: SamplesPerPixel
- 278: RowsPerStrip
- 279: StripByteCounts
- 284: PlanarConfiguration
- 33550: ModelPixelScaleTag (pixel scale in X, Y, Z)
- 33922: ModelTiepointTag (image-to-world coordinate mapping)
- 34735: GeoKeyDirectoryTag (coordinate system keys including EPSG:4326)
- 34736: GeoDoubleParamsTag (floating-point parameters)
- 34737: GeoAsciiParamsTag (string parameters)
// Add custom tags to any IFD
layer.customTags[50000] = {42, 100, 255}; // Custom numeric data
layer.customTags[50001] = {timestamp_value}; // Application-specific tags
include(FetchContent)
FetchContent_Declare(
geotiv
GIT_REPOSITORY https://github.com/your-org/geotiv.git
GIT_TAG main
)
FetchContent_MakeAvailable(geotiv)
target_link_libraries(your_target geotiv::geotiv)
git clone https://github.com/your-org/geotiv.git
cd geotiv
mkdir build && cd build
cmake -DGEOTIV_BUILD_EXAMPLES=ON -DGEOTIV_ENABLE_TESTS=ON ..
make -j$(nproc)
The library includes comprehensive tests covering:
- Round-trip file I/O operations
- Multi-layer GeoTIFF handling
- Coordinate system transformations
- TIFF format validation
- Error handling scenarios
make test
- Concord: Coordinate system transformations and geodetic calculations
- Pigment: Color and pixel data handling
- C++20 compiler: Modern C++ features for performance and safety
- Time-Series Data: Store temporal raster datasets with different timestamps per IFD
- Multi-Resolution Pyramids: Create resolution pyramids with different scales per layer
- Multi-Spectral Imagery: Handle different spectral bands with independent geospatial parameters
- Precise Spatial Positioning: Use ENU shift coordinates for sub-meter accuracy
- Metadata Preservation: Maintain application-specific tags and metadata per image layer
geotiv::RasterCollection timeSeries;
for (const auto& timestamp : timestamps) {
geotiv::Layer layer;
layer.datum = surveyLocation;
layer.shift = {concord::Point{0, 0, 0}, concord::Euler{0, 0, 0}}; // No spatial offset
layer.resolution = 0.5; // 50cm resolution
// Store timestamp in custom tag
layer.customTags[50100] = {static_cast<uint32_t>(timestamp)};
layer.imageDescription = "Survey data " + formatTimestamp(timestamp);
// Load raster data for this time point
layer.grid = loadRasterForTime(timestamp);
layer.width = layer.grid.cols();
layer.height = layer.grid.rows();
timeSeries.layers.push_back(std::move(layer));
}
geotiv::WriteRasterCollection(timeSeries, "temporal_survey.tif");
Geotiv uses a sophisticated shift-based coordinate system that provides both precision and flexibility:
- Datum: WGS84 reference point (lat, lon, alt) for coordinate transformations
- Shift: ENU position and orientation relative to datum for precise grid placement
- Grid Creation: Uses ENU shift directly for
concord::Grid
positioning - GeoTIFF Storage: Converts ENU shift to WGS84 for ModelTiepointTag
- GIS Integration: QGIS reads ModelTiepointTag for proper georeferencing
// Survey area with known WGS84 reference
concord::Datum surveyDatum{52.1234, 5.6789, 45.0};
// Position grid 150m East, 300m North of datum, rotated 15 degrees
concord::Pose gridShift{concord::Point{150.0, 300.0, 0.0},
concord::Euler{0, 0, 0.262}}; // 15 degrees in radians
geotiv::Raster raster(surveyDatum, gridShift, 0.5); // 50cm resolution
- ImageDescription:
"CRS WGS84 DATUM lat lon alt SHIFT x y z yaw"
- ModelTiepointTag: Maps image center to WGS84 coordinates
- Automatic Conversion: ENU coordinates converted to WGS84 using datum
- Geospatial Analysis: Process satellite imagery and aerial photography
- Terrain Modeling: Work with Digital Elevation Models (DEMs)
- Environmental Monitoring: Handle time-series raster data
- GIS Applications: Integrate with geographic information systems
- Scientific Computing: Process research datasets with spatial components
Contributions are welcome! Please see our contributing guidelines and ensure all tests pass before submitting pull requests.