GDAL 2.X升級(jí)3.X需要注意的問(wèn)題總結(jié)
1 引言
最近終于將使用的GDAL 2.X升級(jí)到成了3.X版本,總結(jié)一下遇到的各種問(wèn)題。
2 詳論
2.1 數(shù)據(jù)路徑
GDAL 3.X以后深度依賴PROJ庫(kù),以前只是可選構(gòu)建項(xiàng),現(xiàn)在已經(jīng)是必須構(gòu)建項(xiàng)了。最直接的體現(xiàn)是如果涉及到空間參考相關(guān)的內(nèi)容時(shí),除了要配置GDAL_DATA環(huán)境變量,還必須配置PROJ_DATA環(huán)境變量。GDAL_DATA和PROJ_DATA分別是GDAL和PROJ庫(kù)的數(shù)據(jù),里面存儲(chǔ)了一些空間參考相關(guān)的參數(shù),因此一般在使用GDAL之前,需要配置一下相關(guān)的路徑:
string gdalDir = shareDataDir + string("/gdal");
CPLSetConfigOption("GDAL_DATA", gdalDir.c_str());
std::string projDir = shareDataDir + string("/proj");
CPLSetConfigOption("PROJ_DATA", projDir.c_str());
這些數(shù)據(jù)一般在構(gòu)建的時(shí)候會(huì)安裝到指定的目錄的share文件夾內(nèi),如下圖所示:

注意以下幾點(diǎn):
- 經(jīng)過(guò)筆者的測(cè)試,一般設(shè)置PROJ_DATA目錄就可以了。但是有的資料顯示還必須繼續(xù)設(shè)置GDAL_DATA目錄。因?yàn)镚DAL_DATA目錄中可能保存的不僅僅是空間參考信息,可能保存了很多GIS相關(guān)的數(shù)據(jù)。
- 另外,很多資料推薦直接設(shè)置操作系統(tǒng)的環(huán)境變量。這樣做不是不行,前提是與操作系統(tǒng)的其他GDAL環(huán)境不沖突。如果你安裝過(guò)QGIS或者PostGIS等環(huán)境就知道,這些程序也會(huì)在操作系統(tǒng)中設(shè)置GDAL_DATA、PROJ_DATA環(huán)境變量,那么就只能像筆者這樣在程序中配置。
- 在PROJ 9.1版本以前,PROJ數(shù)據(jù)的環(huán)境變量名為PROJ_LIB;在后續(xù)版本中優(yōu)先使用PROJ_DATA,PROJ_LIB還會(huì)使用一段時(shí)間,但是最好替換成PROJ_DATA,避免后續(xù)的版本廢棄。
2.2 坐標(biāo)順序
GDAL升級(jí)到3.X后另外一個(gè)問(wèn)題就是坐標(biāo)順序的問(wèn)題。例如如果要進(jìn)行空間參考坐標(biāo)轉(zhuǎn)換:
// CGCS2000
gcs.importFromEPSG(4326);
// Tm投影
pcs.importFromEPSG(3857);
OGRCoordinateTransformation* lonLat2XY =
OGRCreateCoordinateTransformation(&gcs, &pcs);
OGRCoordinateTransformation* xy2LonLat =
OGRCreateCoordinateTransformation(&pcs, &gcs);
if (!lonLat2XY || !xy2LonLat) {
return 1;
}
double x = 113.6;
double y = 38.8;
printf("經(jīng)緯度坐標(biāo):%.9lf\t%.9lf\n", x, y);
if (!lonLat2XY->Transform(1, &x, &y)) {
return 1;
}
printf("平面坐標(biāo):%.9lf\t%.9lf\n", x, y);
if (!xy2LonLat->Transform(1, &x, &y)) {
return 1;
}
printf("再次轉(zhuǎn)換回的經(jīng)緯度坐標(biāo):%.9lf\t%.9lf\n", x, y);
OGRCoordinateTransformation::DestroyCT(lonLat2XY);
lonLat2XY = nullptr;
OGRCoordinateTransformation::DestroyCT(xy2LonLat);
xy2LonLat = nullptr;
這段代碼在3.X的結(jié)果就不正確。原因是GDAL 3.X更換了坐標(biāo)軸的順序,認(rèn)為y在前,x在后才是更加專業(yè)的坐標(biāo)表達(dá)。不過(guò)這樣就破壞了向后兼容性。解決方案是給空間參考設(shè)置軸策略為傳統(tǒng)順序[1]:
// CGCS2000
gcs.importFromEPSG(4326);
// Tm投影
pcs.importFromEPSG(3857);
gcs.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
pcs.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
//...
想這樣一個(gè)一個(gè)坐標(biāo)參考修改很麻煩,另一個(gè)更加合適的辦法是設(shè)置全局的坐標(biāo)軸策略為傳統(tǒng)順序[2]:
// 設(shè)置全局坐標(biāo)順序?yàn)閭鹘y(tǒng)GIS順序(經(jīng)度,緯度)
CPLSetConfigOption("OGR_CT_FORCE_TRADITIONAL_GIS_ORDER", "YES");
3 總結(jié)
最好的辦法就是在程序的最開(kāi)始階段執(zhí)行一個(gè)初始化函數(shù):
void Init(const char *shareDataDir) {
GDALAllRegister(); //注冊(cè)所有的格式
CPLSetConfigOption("SHAPE_ENCODING", ""); //解決中文亂碼問(wèn)題
string gdalDir = shareDataDir + string("/gdal");
CPLSetConfigOption("GDAL_DATA", gdalDir.c_str());
std::string projDir = shareDataDir + string("/proj");
CPLSetConfigOption("PROJ_DATA", projDir.c_str());
// 設(shè)置全局坐標(biāo)順序?yàn)閭鹘y(tǒng)GIS順序(經(jīng)度,緯度)
CPLSetConfigOption("OGR_CT_FORCE_TRADITIONAL_GIS_ORDER", "YES");
}

浙公網(wǎng)安備 33010602011771號(hào)