mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 06:06:00 -06:00
327 lines
8.8 KiB
C++
327 lines
8.8 KiB
C++
#include "geotiff.h"
|
|
#define uint8 tif_uint8
|
|
#define uint16 tif_uint16
|
|
#define int16 tif_int16
|
|
#define int32 tif_int32
|
|
#define uint32 tif_uint32
|
|
#include <plugin/tif/tif.h>
|
|
#include "libgeotiff/xtiffio.h"
|
|
#include "libgeotiff/gcore/gdal_priv.h"
|
|
#undef uint8
|
|
#undef uint16
|
|
#undef int16
|
|
#undef int32
|
|
#undef uint32
|
|
|
|
GDALDriver *CreateGTIFFDriver();
|
|
|
|
extern "C" TIFF* XTIFFOpen(const char* name, const char* mode)
|
|
{
|
|
return TIFFFileStreamOpen(name, mode);
|
|
}
|
|
|
|
GLOBAL_VARP(One<GDALDriver>, GTIFFDriver, = CreateGTIFFDriver());
|
|
|
|
EXITBLOCK {
|
|
VSICleanupFileManager();
|
|
}
|
|
|
|
GeoTIFF::Block::Block(Size size_, GDALDataType type_, byte pixel_bytes_)
|
|
: size(size_)
|
|
, type(type_)
|
|
, pixel_bytes(pixel_bytes_)
|
|
{
|
|
bytes.Alloc(pixel_bytes * size.cx * size.cy);
|
|
}
|
|
|
|
#define CONVERT_LOOP(type, src_convert) \
|
|
{ \
|
|
const type *src = (const type *)raw_src; \
|
|
const type *end = src + count; \
|
|
for(; src < end; src++, out++) \
|
|
*out = (src_convert); \
|
|
break; \
|
|
}
|
|
|
|
|
|
void GeoTIFF::Block::GetInt(int *out, int x, int y, int count) const
|
|
{
|
|
ASSERT(x >= 0 && x + count <= size.cx);
|
|
ASSERT(y >= 0 && y < size.cy);
|
|
int offset = (x + y * size.cx) * pixel_bytes;
|
|
const byte *raw_src = &bytes[offset];
|
|
switch(type) {
|
|
case GDT_Byte: CONVERT_LOOP(byte, *src);
|
|
case GDT_UInt16: CONVERT_LOOP(uint16, *src);
|
|
case GDT_Int16: CONVERT_LOOP(int16, *src);
|
|
case GDT_UInt32: CONVERT_LOOP(uint32, (int)*src);
|
|
case GDT_Int32: CONVERT_LOOP(int32, (int)*src);
|
|
case GDT_Float32: CONVERT_LOOP(float, fround((double)*src));
|
|
case GDT_Float64: CONVERT_LOOP(double, fround(*src));
|
|
case GDT_CInt16: CONVERT_LOOP(Point16, src->x);
|
|
case GDT_CInt32: CONVERT_LOOP(Point, src->x);
|
|
case GDT_CFloat32: CONVERT_LOOP(Point_<float>, fround((double)src->x));
|
|
case GDT_CFloat64: CONVERT_LOOP(Pointf, fround(src->x));
|
|
default: {
|
|
Fill(out, out + count, (int)Null);
|
|
RLOG("unsupported GDAL data type: " << (int)type);
|
|
NEVER();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GeoTIFF::Block::GetDouble(double *out, int x, int y, int count) const
|
|
{
|
|
ASSERT(x >= 0 && x + count <= size.cx);
|
|
ASSERT(y >= 0 && y < size.cy);
|
|
int offset = (x + y * size.cx) * pixel_bytes;
|
|
const byte *raw_src = &bytes[offset];
|
|
switch(type) {
|
|
case GDT_Byte: CONVERT_LOOP(byte, *src);
|
|
case GDT_UInt16: CONVERT_LOOP(uint16, *src);
|
|
case GDT_Int16: CONVERT_LOOP(int16, *src);
|
|
case GDT_UInt32: CONVERT_LOOP(uint32, (int)*src);
|
|
case GDT_Int32: CONVERT_LOOP(int32, (int)*src);
|
|
case GDT_Float32: CONVERT_LOOP(float, (double)*src);
|
|
case GDT_Float64: CONVERT_LOOP(double, *src);
|
|
case GDT_CInt16: CONVERT_LOOP(Point16, src->x);
|
|
case GDT_CInt32: CONVERT_LOOP(Point, src->x);
|
|
case GDT_CFloat32: CONVERT_LOOP(Point_<float>, (double)src->x);
|
|
case GDT_CFloat64: CONVERT_LOOP(Pointf, src->x);
|
|
default: {
|
|
Fill(out, out + count, (double)Null);
|
|
RLOG("unsupported GDAL data type: " << (int)type);
|
|
NEVER();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
GeoTIFF::Band::Band(GeoTIFF& owner_, GDALRasterBand *band_)
|
|
: owner(owner_), band(band_)
|
|
{
|
|
size.cx = band->GetXSize();
|
|
size.cy = band->GetYSize();
|
|
band->GetBlockSize(&block_size.cx, &block_size.cy);
|
|
block_count = idivceil(size, block_size);
|
|
type = band->GetRasterDataType();
|
|
pixel_bytes = (GDALGetDataTypeSize(type) + 7) >> 3;
|
|
complex = GDALDataTypeIsComplex(type);
|
|
int nodata_flag;
|
|
nodata_value = band->GetNoDataValue(&nodata_flag);
|
|
is_nodata = !!nodata_flag;
|
|
is_stat = false;
|
|
nodata_int = fround(nodata_value);
|
|
blocks.Alloc(block_count.cx * block_count.cy);
|
|
}
|
|
|
|
double GeoTIFF::Band::GetMinimum() const
|
|
{
|
|
int success;
|
|
double min = band->GetMinimum(&success);
|
|
if(success)
|
|
return min;
|
|
CalcStatistics();
|
|
return stat_min;
|
|
}
|
|
|
|
double GeoTIFF::Band::GetMaximum() const
|
|
{
|
|
int success;
|
|
double max = band->GetMaximum(&success);
|
|
if(success)
|
|
return max;
|
|
CalcStatistics();
|
|
return stat_max;
|
|
}
|
|
|
|
void GeoTIFF::Band::GetInt(int *out, int x, int y, int count) const
|
|
{
|
|
int y_in_block = y % block_size.cy;
|
|
int block_y = y / block_size.cy;
|
|
int block_offset = block_y * block_count.cx;
|
|
int block_x_min = x / block_size.cx;
|
|
int block_x_max = (x + count - 1) / block_size.cx;
|
|
int x_in_block = x - block_size.cx * block_x_min;
|
|
for(int block_x = block_x_min; block_x <= block_x_max; block_x++) {
|
|
int fill_count = min(count, block_size.cx - x_in_block);
|
|
int block_index = block_x + block_offset;
|
|
if(!blocks[block_index])
|
|
LoadBlock(x, block_y);
|
|
if(blocks[block_index]) {
|
|
blocks[block_index]->GetInt(out, x_in_block, y_in_block, fill_count);
|
|
if(is_nodata) {
|
|
for(int *p = out, *e = out + fill_count; p < e; p++)
|
|
if(*p == nodata_int)
|
|
*p = Null;
|
|
}
|
|
}
|
|
else
|
|
Fill(out, out + fill_count, (int)Null);
|
|
x_in_block = 0;
|
|
count -= fill_count;
|
|
x += fill_count;
|
|
}
|
|
}
|
|
|
|
void GeoTIFF::Band::GetDouble(double *out, int x, int y, int count) const
|
|
{
|
|
int y_in_block = y % block_size.cy;
|
|
int block_y = y / block_size.cy;
|
|
int block_offset = block_y * block_count.cx;
|
|
int block_x_min = x / block_size.cx;
|
|
int block_x_max = (x + count - 1) / block_size.cx;
|
|
int x_in_block = x - block_size.cx * block_x_min;
|
|
for(int block_x = block_x_min; block_x <= block_x_max; block_x++) {
|
|
int fill_count = min(count, block_size.cx - x_in_block);
|
|
int block_index = block_x + block_offset;
|
|
if(!blocks[block_index])
|
|
LoadBlock(block_x, block_y);
|
|
if(blocks[block_index]) {
|
|
blocks[block_index]->GetDouble(out, x_in_block, y_in_block, fill_count);
|
|
if(is_nodata) {
|
|
for(double *p = out, *e = out + fill_count; p < e; p++)
|
|
if(*p == nodata_value)
|
|
*p = Null;
|
|
}
|
|
}
|
|
else
|
|
Fill(out, out + fill_count, (double)Null);
|
|
x_in_block = 0;
|
|
count -= fill_count;
|
|
out += fill_count;
|
|
}
|
|
}
|
|
|
|
int GeoTIFF::Band::GetInt(int x, int y) const
|
|
{
|
|
int out;
|
|
GetInt(&out, x, y, 1);
|
|
return out;
|
|
}
|
|
|
|
double GeoTIFF::Band::GetDouble(int x, int y) const
|
|
{
|
|
double out;
|
|
GetDouble(&out, x, y, 1);
|
|
return out;
|
|
}
|
|
|
|
double GeoTIFF::Band::GetIntAt(Pointf pt) const
|
|
{
|
|
Pointf src = pt * owner.GetInverseTransform();
|
|
int x = ffloor(src.x);
|
|
int y = ffloor(src.y);
|
|
if(x < 0 || x >= size.cx || y < 0 || y >= size.cy)
|
|
return Null;
|
|
return GetInt(x, y);
|
|
}
|
|
|
|
double GeoTIFF::Band::GetDoubleAt(Pointf pt) const
|
|
{
|
|
Pointf src = pt * owner.GetInverseTransform();
|
|
int x = ffloor(src.x);
|
|
int y = ffloor(src.y);
|
|
if(x < 0 || x >= size.cx || y < 0 || y >= size.cy)
|
|
return Null;
|
|
return GetDouble(x, y);
|
|
}
|
|
|
|
void GeoTIFF::Band::LoadBlock(int column, int row) const
|
|
{
|
|
ASSERT(column >= 0 && column < block_count.cx);
|
|
ASSERT(row >= 0 && row < block_count.cy);
|
|
int block_index = column + row * block_count.cx;
|
|
if(blocks[block_index])
|
|
return;
|
|
Point block_offset(block_size.cx * column, block_size.cy * row);
|
|
One<Block> new_block = new Block(block_size, type, pixel_bytes);
|
|
band->ReadBlock(column, row, new_block->bytes);
|
|
blocks[block_index] = pick(new_block);
|
|
}
|
|
|
|
void GeoTIFF::Band::CalcStatistics() const
|
|
{
|
|
if(is_stat)
|
|
return;
|
|
is_stat = true;
|
|
stat_min = stat_max = Null;
|
|
bool done_stat = false;
|
|
Buffer<double> row(block_size.cx);
|
|
for(int by = 0; by < block_count.cy; by++)
|
|
for(int bx = 0; bx < block_count.cx; bx++) {
|
|
LoadBlock(bx, by);
|
|
Size valid_size = min(block_size, size - Size(bx, by) * block_size);
|
|
const One<Block>& block = blocks[bx + by * block_count.cx];
|
|
if(block)
|
|
for(int r = 0; r < valid_size.cy; r++) {
|
|
block->GetDouble(row, 0, r, valid_size.cx);
|
|
for(const double *p = row, *e = p + valid_size.cx; p < e; p++)
|
|
if(!done_stat) {
|
|
stat_min = stat_max = *p;
|
|
done_stat = true;
|
|
}
|
|
else {
|
|
if(*p < stat_min) stat_min = *p;
|
|
if(*p > stat_max) stat_max = *p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GeoTIFF::GeoTIFF()
|
|
{
|
|
}
|
|
|
|
GeoTIFF::~GeoTIFF()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
bool GeoTIFF::Open(const char *fn)
|
|
{
|
|
Close();
|
|
|
|
GDALOpenInfo openinfo(filename = fn, GA_ReadOnly);
|
|
dataset = GTIFFDriver()->pfnOpen(&openinfo);
|
|
if(!dataset)
|
|
return false;
|
|
int nbands = dataset->GetRasterCount();
|
|
for(int i = 1; i <= nbands; i++)
|
|
bands.Add(new Band(*this, dataset->GetRasterBand(i)));
|
|
double affine[6];
|
|
dataset->GetGeoTransform(affine);
|
|
transform.a.x = affine[0];
|
|
transform.x.x = affine[1];
|
|
transform.y.x = affine[2];
|
|
transform.a.y = affine[3];
|
|
transform.x.y = affine[4];
|
|
transform.y.y = affine[5];
|
|
if(fabs(Determinant(transform)) <= 1e-10)
|
|
inverse_transform = Matrixf_1();
|
|
else
|
|
inverse_transform = MatrixfInverse(transform);
|
|
return true;
|
|
}
|
|
|
|
void GeoTIFF::Close()
|
|
{
|
|
bands.Clear();
|
|
dataset.Clear();
|
|
}
|
|
|
|
String GeoTIFF::GetProjection() const
|
|
{
|
|
return dataset->GetProjectionRef();
|
|
}
|
|
|
|
Size GeoTIFF::GetPixelSize() const
|
|
{
|
|
return Size(dataset->GetRasterXSize(), dataset->GetRasterYSize());
|
|
}
|
|
|
|
Rectf GeoTIFF::GetExtent() const
|
|
{
|
|
return Rectf(Sizef(GetPixelSize())) * GetTransform();
|
|
}
|