2025-04-13 23:33:09
背景
从tiff数据中按经纬度读取高程数据,要让依赖环境尽量简单,查阅资料GDAL依赖Boost,太重了,全球高程tif从www.ncei.noaa.gov/products/et… 下载
依赖库
libtiff libgeotiff libproj zlib libcurl sqlite
#include <geotiff.h>
#include <geotiffio.h>
#include <proj.h>
#include <tiffio.h>
#include <iostream>
#include "xtiffio.h"
#include "geo_normalize.h"
void MyTIFFErrorHandler(const char* module, const char* fmt, va_list args) {
// 格式化错误消息
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), fmt, args);
// 输出到标准错误(或日志文件)
std::cerr << "[TIFF ERROR] Module: " << (module ? module : "unknown")
<< ", Message: " << buffer << std::endl;
}
float ReadFloatTifGrid(const char *file,double lon, double lat ) {
TIFFErrorHandler oldHandler = TIFFSetErrorHandler(MyTIFFErrorHandler);
TIFF *tif = NULL;
GTIF *gtif = NULL;
tif = XTIFFOpen(file, "r");
if (!tif) {
return -99999 ;
}
gtif = GTIFNew(tif);
if (!gtif) {
GTIFFree(gtif);
XTIFFClose(tif);
return -99999 ;
}
unsigned short sampleFormat, samplesPerPixel, bitsPerSample;
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);// 1: 无符号整数,2: 有符号整数,3: 浮点
TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleFormat);
if (sampleFormat != SAMPLEFORMAT_IEEEFP || bitsPerSample != 32 || samplesPerPixel != 1) {
GTIFFree(gtif);
XTIFFClose(tif);
return -99999 ;
}
//图片大小
int width, height;
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
short tiepointsize, pixscalesize;
double* tiepoints;//[6];
double* pixscale;//[3];
if(!TIFFGetField(tif, TIFFTAG_GEOTIEPOINTS, &tiepointsize,
&tiepoints)||
!TIFFGetField(tif, TIFFTAG_GEOPIXELSCALE, &pixscalesize, &pixscale))
{
GTIFFree(gtif);
TIFFClose(tif);
return -99999 ;
}
// 3. 获取 GeoTIFF 定义
GTIFDefn defn;
if (!GTIFGetDefn(gtif, &defn)) {
std::cerr << "Could not parse GeoTIFF definition." << std::endl;
GTIFFree(gtif);
TIFFClose(tif);
return -99999 ;
}
// 4. 坐标转换(WGS84经纬度 -> 图片坐标系)
char *target_crs = NULL;
if (defn.Model == ModelTypeProjected && defn.PCS != KvUserDefined) {
target_crs = (char *)malloc(16);
snprintf(target_crs, 16, "EPSG:%d", defn.PCS);
} else if (defn.Model == ModelTypeGeographic && defn.GCS != KvUserDefined) {
target_crs = (char *)malloc(16);
snprintf(target_crs, 16, "EPSG:%d", defn.GCS);
} else {
// 自定义坐标系处理(示例:UTM zone 50N)
target_crs = "+proj=utm +zone=50 +north +datum=WGS84";
}
PJ_CONTEXT *ctx = proj_context_create();
PJ *trans = proj_create_crs_to_crs(ctx, "EPSG:4326", target_crs, NULL);
PJ_COORD proj = proj_trans(trans, PJ_FWD, proj_coord(lon, lat, 0, 0));
// 5. 计算经纬度对应的像素坐标
double A = tiepoints[3]; // 左上角地理坐标X
double C = tiepoints[4]; // 左上角地理坐标Y
double B = pixscale[0]; // 东西向像素分辨率
double D = -pixscale[1]; // 南北向像素分辨率(取负号,因为Y轴向下)
int x_pixel = (proj.xy.x - A) / B;
int y_pixel = (proj.xy.y - C) / D;
// 5. 读取数据
double value=-99999;
if (TIFFIsTiled(tif)) {//块存储
// 处理分块图像
uint32_t tile_width, tile_height;
TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
unsigned char* buf = (unsigned char*)_TIFFmalloc(TIFFTileSize(tif));
//unsigned char* buf1 = (unsigned char*)_TIFFmalloc(TIFFTileSize(tif));
TIFFReadTile(tif, buf, x_pixel, y_pixel, 0, 0);
// TIFFReadTile(tif, buf1,x_pixel+1, y_pixel, 0, 0);//即使偏移buf也是一样的 因为是按块读取
if (bitsPerSample == 32) {
float* floatData = (float*)buf;
uint32_t offset_x = x_pixel % tile_width;
uint32_t offset_y = y_pixel % tile_height;
value = (floatData)[offset_y * tile_width + offset_x];
// float* floatData1 = (float*)buf1;
// float value1 = (floatData1)[offset_y * tile_width + offset_x];
// int a=0;
// 处理32位浮点数据...
} else if (bitsPerSample == 64) {
double* doubleData = (double*)buf;
// 处理64位浮点数据...
}
else if (bitsPerSample == 16) {
uint16_t* shortData = (uint16_t*)buf;
}
//读取所有
// for (uint32_t y = 0; y < height; y += tileHeight) {
// for (uint32_t x = 0; x < width; x += tileWidth) {
// TIFFReadTile(tif, buf, x, y, 0, 0);
// if(y>=3437&&x>18042)
// {
// int a=0;
// }
// if (bitsPerSample == 32) {
// float* floatData = (float*)buf;
// int a=0;
// // 处理32位浮点数据...
// } else if (bitsPerSample == 64) {
// double* doubleData = (double*)buf;
// int a=0;
// // 处理64位浮点数据...
// }
// else if (bitsPerSample == 16) {
// uint16_t* shortData = (uint16_t*)buf;
// }
// }
// }
_TIFFfree(buf);
}
else
{
int size=TIFFTileSize(tif);
unsigned char* buf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(tif));
for (long i = 0; i < height; i++) {
if (TIFFReadScanline(tif, buf, (unsigned int)i, 0) == -1) {
GTIFFree(gtif);
XTIFFClose(tif);
return value;
}
}
}
GTIFFree(gtif);
XTIFFClose(tif);
return value;
}
int main(int argc, char *argv[]) {
float value=ReadFloatTifGrid("D:/Download/ETOPO_2022_v1_60s_N90W180_surface.tif",115.7,39.4);
return 0;
}
注意
自己编译tiff库要开启zlib
总结
大模型不是那么好,生成代码乱七八糟,用于查查API还可以