??本文介紹在Linux的Ubuntu操作系統(tǒng)中,基于Docker快速配置Python與C++這2種不同編程語言可用的地理數(shù)據(jù)處理庫GDAL開發(fā)環(huán)境的方法。
??本文就將Python與C++這2種不同編程語言的GDAL模塊配置方法分開來介紹,大家依據(jù)自己的需求來選擇即可——但無論是哪種方法,配置GDAL模塊的方法都非常簡單,終端中輸入幾句代碼就完成了。和我們之前在Windows系統(tǒng)中配置GDAL模塊的文章GDAL庫在Visual Studio C++環(huán)境中的配置比起來,真的是方便了很多。
1 Python版本
??首先,我們訪問GDAL庫的Docker鏡像官方網(wǎng)站。這里需要注意,雖然這個官方網(wǎng)站似乎并沒有明確說明它提供的版本只能Python使用,但是我這里下載后發(fā)現(xiàn)C++代碼確實無法調(diào)用這個鏡像中的GDAL模塊。
??其中,官方網(wǎng)站提供了Alpine和Ubuntu兩種不同系統(tǒng)的Docker鏡像;并且對于不同的系統(tǒng)版本,其還提供了Small和Full兩種不同的鏡像內(nèi)容,其中前者包含的內(nèi)容相對較少,而后者包含的內(nèi)容較為齊全(因此后者的鏡像大小也就更大一些),而這兩種鏡像自身都是包含Python的3.8或以上版本的。此外,關(guān)于Small和Full兩種不同鏡像的具體詳細(xì)內(nèi)容差異,我們這里就不再贅述了,大家在其官方網(wǎng)站查閱即可;具體如下圖所示。

??在我這里,由于只是需要用GDAL庫完成一些讀取.tif格式文件的操作,所以并不需要特別完整的GDAL庫,所以就選擇了Small這個小一點的版本。
??接下來,我們在Ubuntu電腦的終端中執(zhí)行如下的代碼。這里需要注意,由于我需要的是Ubuntu系統(tǒng)的Small版本,所以我就輸入如下的代碼即可;如果大家使用的是Alpine操作系統(tǒng),或者是Ubuntu系統(tǒng)的Full版本,那么按照上圖中自己所需要的版本對應(yīng)的名稱,修改下述代碼并執(zhí)行即可。
docker pull ghcr.io/osgeo/gdal:ubuntu-small-latest
??運行上述代碼,如下圖所示。

??稍等片刻,我們就完成了鏡像的獲取。此時,我們可以通過如下的代碼,查看當(dāng)前電腦中Docker鏡像的下載情況(也就是看看我們已經(jīng)有了哪些鏡像)。
docker images
??運行上述代碼,如下圖所示。

??其中,那個ghcr.io/osgeo/gdal就是我們剛剛下載好的GDAL庫的鏡像。
??接下來,運行如下的代碼,從而基于剛剛下載好的鏡像運行一個容器。
docker run -it --rm ghcr.io/osgeo/gdal:ubuntu-small-latest
??其中,docker run是運行容器的命令,-it表示以交互模式運行容器,并分配一個終端,--rm表示在容器停止后自動刪除容器(如果大家在使用容器后不想讓它自動刪除,就將這里的--rm去掉即可;如果大家是第一次接觸Docker,那么建議帶上這個--rm,防止自己摸索過程中不知不覺建立了好多個無用的容器,到時候還要手動一個一個刪除);后面的就是我們剛剛下載好的鏡像,表示我們要基于這個鏡像去運行一個容器。運行上述代碼,如下圖所示。

??接下來,我們就進入了容器。此時,繼續(xù)輸入如下的代碼,查看當(dāng)前容器中GDAL庫的版本信息。
gdalinfo --version
??運行上述代碼,如下圖所示??梢钥吹剑藭r將打印出我們GDAL庫的版本信息。

??接下來,我們先通過如下的代碼,退出當(dāng)前鏡像,回到終端中。
exit
??運行上述代碼,如下圖所示。

??我們既然配置了一個GDAL庫的Docker鏡像,那么后續(xù)肯定是需要將一些我們自己電腦中的文件(比如柵格圖像、矢量數(shù)據(jù)等文件)帶入到這個鏡像的容器中去運行,所以肯定需要這個GDAL庫的Docker鏡像要和我們Ubuntu電腦中文件可以交互(換句話說,也就是可以讀取、修改我們電腦中的文件與數(shù)據(jù))。因此,我們在之后進入我們這個GDAL庫的Docker鏡像的容器時,需要通過如下的代碼。
docker run -it --rm -v /home/dell/cppGDAL:/home/dell/cppGDAL ghcr.io/osgeo/gdal:ubuntu-small-latest
??上述代碼和我們前面的docker run -it --rm ghcr.io/osgeo/gdal:ubuntu-small-latest相比,很顯然是多了-v /home/dell/cppGDAL:/home/dell/cppGDAL這一個部分——這一部分是用于掛載主機文件系統(tǒng)中的目錄到容器中的命令參數(shù)。其中,-v是Docker命令中用于掛載文件或目錄的選項,其后面的/home/dell/cppGDAL:/home/dell/cppGDAL,則是文件掛載的源目錄和目標(biāo)目錄的路徑——它指定了主機文件系統(tǒng)中的/home/dell/cppGDAL目錄將被掛載到容器內(nèi)的/home/dell/cppGDAL目錄。
??這里多提一句,我們這里是將主機中的一個指定文件路徑掛載到了容器中,所以屬于Docker中的Bind mounts;如果我們這里是手動創(chuàng)建了一個Volume,然后掛載到容器中,那么就叫做Volume;此外還有一種叫做tmpfs mounts,是把容器的數(shù)據(jù)寫入主機的內(nèi)存中——上述的Bind mounts、Volume與tmpfs mounts,這3種都是Docker用以數(shù)據(jù)管理、數(shù)據(jù)記憶的方式。
??回到前述的代碼。換句話說,上述命令將我的Ubuntu電腦中的/home/dell/cppGDAL目錄與GDAL庫的Docker鏡像的容器中的/home/dell/cppGDAL目錄進行了掛載。這樣,在容器中對掛載點/home/dell/cppGDAL的操作將反映在主機系統(tǒng)的/home/dell/cppGDAL目錄上,反之亦然。
??相當(dāng)于通過這種方式,只要我將我需要用GDAL庫處理的數(shù)據(jù)、代碼等文件,都放在電腦的/home/dell/cppGDAL目錄下,那么就可以在容器中對這些數(shù)據(jù)加以訪問和處理。這樣即實現(xiàn)了文件的交互,同樣可以保證容器不會訪問我們電腦中其他文件夾內(nèi)的數(shù)據(jù)或者文件,保證了數(shù)據(jù)的安全。
??如果大家還是沒有明白這句代碼的意義,不著急,我們先運行上述代碼,如下圖所示。

??上圖中運行完代碼,我又不小心多運行了一句pwd代碼,大家理解即可。
??為了更清晰地看到前述那一種進入容器的代碼的意義,我們做一個如下的對比。如下圖所示,這是我們用了那一句包含掛載文件夾命令的代碼,進入我們的容器后,執(zhí)行的操作;可以看到,此時在容器中,我們就可以進入/home/dell/cppGDAL目錄下。

??而如果我們并沒有掛載文件,而是用了本文中第一次出現(xiàn)的那一句代碼進入容器的代碼,也就是前面的docker run -it --rm ghcr.io/osgeo/gdal:ubuntu-small-latest代碼,進入容器后會發(fā)現(xiàn),cd進入home文件夾后,再ls,是看不到我們這個cppGDAL文件夾的;換句話說,此時我們就沒有辦法在容器內(nèi)部讀取我們電腦里/home/dell/cppGDAL目錄下的文件了——連文件、數(shù)據(jù)都無法獲取,那么這個GDAL鏡像肯定也是沒有用處的了。

??此外,前面我們還提到,-v /home/dell/cppGDAL:/home/dell/cppGDAL這一個部分可以保證鏡像可以且僅可以讀取/home/dell/cppGDAL目錄下的文件,而不會讀取到我們沒有掛載的其他文件夾。針對這一個內(nèi)容,我們再做一個對比。如下圖所示,是我們直接在Ubuntu電腦的終端中,進入/home/dell目錄的情況;可以很明顯地看到,在電腦中的/home/dell目錄下,不僅有我們的這個cppGDAL文件夾,還有很多很多其他的文件或者文件夾;而在上上圖中,可以看到在容器中,我們進入/home/dell/cppGDAL目錄下只能看到這個cppGDAL文件夾,而看不到電腦中這一路徑下原本還有的其他文件或者文件夾。所以很明顯,相當(dāng)于我們就是可以在鏡像中訪問/home/dell/cppGDAL目錄,但是無法訪問沒有掛載的其他文件夾,從而保證了其他無關(guān)文件夾的安全性。

??明白了上述內(nèi)容,就可以開始我們的GDAL操作了。例如,我這里在/home/dell/cppGDAL目錄下還有一個名稱為TIF的文件夾,其中保存了一景遙感影像,那么我就可以通過gdalinfo語句,查看這一柵格數(shù)據(jù)的信息。如下圖所示。

??最后,每一次完成鏡像中的操作后,不要忘記通過exit命令,退出鏡像。
??因為我這里是需要C++版本的GDAL模塊,所以后來也就沒有對上述Python版本的再加以代碼測試;但經(jīng)過上述配置,運行Python代碼的GDAL程序應(yīng)該是沒有問題了。
2 C++版本
??接下來,我們介紹配置C++版本的GDAL模塊的方法。
??由于GDAL官方似乎并未提供直接的C++版本鏡像,所以我們這里就自己創(chuàng)建一個Docker鏡像,隨后在其中配置GDAL模塊。這里需要注意,如果大家剛剛根據(jù)前文的流程,先配置了一個Python語言的GDAL模塊的鏡像,那么建議大家在另一個新的鏡像內(nèi)重新配置C++版本的,不要直接在前面的Python語言鏡像中配置GDAL模塊——因為官網(wǎng)說,在前面這個Python語言的GDAL模塊的鏡像內(nèi)配置其他版本的GDAL模塊,會容易由于GDAL模塊的版本沖突導(dǎo)致容器無法工作(雖然我當(dāng)時簡單嘗試了一下,發(fā)現(xiàn)即使如此,容器似乎還是可以正常工作的)。
??我們這里就在一個新的Ubuntu鏡像中加以配置。首先,在終端中輸入如下代碼,創(chuàng)建一個Ubuntu鏡像。
docker pull ubuntu
??運行上述代碼,如下圖所示。

??接下來,我們用前文提到的這一句代碼,運行一個容器。這里我就不再用--rm了,從而使得我們這個容器之后可以多次重復(fù)使用。
docker run -it -v /home/dell/cppGDAL:/home/cppGDAL ubuntu:latest
??運行上述代碼,如下圖所示。

??接下來,因為我們這個容器是基于一個空白的Ubuntu鏡像創(chuàng)建的,很多執(zhí)行GDAL的C++代碼所需的配置都沒有處理,我們需要配置一下基本的環(huán)境。
??首先,通過如下代碼更新軟件包列表。
apt update
??運行上述代碼,如下圖所示。

??隨后,輸入如下的代碼,配置GDAL模塊的C++庫。其中,libpq-dev是PostgreSQL數(shù)據(jù)庫的開發(fā)庫,包含了開發(fā)PostgreSQL應(yīng)用程序所需的頭文件和靜態(tài)庫;gdal-bin是GDAL的二進制工具包,提供了一些用于處理地理空間數(shù)據(jù)的工具,如轉(zhuǎn)換、裁剪等;libgdal-dev是GDAL的開發(fā)庫,包含了開發(fā)GDAL應(yīng)用程序所需的頭文件和靜態(tài)庫。
apt install libpq-dev gdal-bin libgdal-dev
??運行上述代碼,如下圖所示。

??稍等片刻,中間有一個環(huán)節(jié)需要我們根據(jù)自己所在位置加以選擇,從而配置自己的時區(qū);如下圖所示。

??完成配置后,通過如下的代碼查看GDAL庫的版本。
gdalinfo --version
??運行上述代碼,如下圖所示。

??接下來,我們再按照文章在Linux系統(tǒng)cmd配置C++環(huán)境的方法介紹的方法,配置Ubuntu的C++代碼開發(fā)環(huán)境,這里就不再贅述了。
??隨后,我們就可以在Docker中執(zhí)行一個簡單的C++程序,來驗證這個GDAL庫的配置是否成功。其中,我們因為已經(jīng)掛載了文件夾,所以既可以在主機中通過其他編輯器來撰寫這個C++代碼,也可以在容器中通過Vim來撰寫。但無論怎么撰寫,都要記得將這個代碼文件(也就是.cpp格式的文件)放在已經(jīng)掛載了的文件路徑內(nèi)。
??這個簡單的C++代碼如下;其含義就是,從我們已經(jīng)掛載了的主機的一個文件夾中,讀取一景柵格影像,獲取并打印其像元的行數(shù)與列數(shù)。
#include <iostream>
#include <gdal/gdal.h>
#include <gdal/gdal_priv.h>
using namespace std;
int main() {
const char* image_path = "/home/cppGDAL/TIF/LAI_A2000057_h30v05.tif";
GDALAllRegister();
GDALDataset* dataset = (GDALDataset*)GDALOpen(image_path, GA_ReadOnly);
if (dataset != nullptr)
{
int rows = dataset->GetRasterYSize();
int cols = dataset->GetRasterXSize();
printf("Rows: %d\n", rows);
printf("Cols: %d\n", cols);
GDALClose(dataset);
}
return 0;
}
??隨后,在容器內(nèi)的上述代碼文件目錄下,執(zhí)行如下的代碼。
g++ `gdal-config --cflags` rec.cpp `gdal-config --libs` `gdal-config --dep-libs` -o test
??其中,g++是GNU C++編譯器的命令,用于編譯和鏈接C++代碼。gdal-config --cflags表示使用gdal-config命令獲取GDAL庫的編譯選項,包括頭文件路徑和其他必要的編譯標(biāo)志;--cflags參數(shù)告訴gdal-config命令返回編譯選項。rec.cpp是要編譯的C++源文件的文件名,也就是前面我們寫的代碼文件的文件名稱。需要注意的是,上述代碼中沒有單引號,而都是反引號,大家輸入的時候不要輸錯了。
??其次,gdal-config --libs使用gdal-config命令來獲取GDAL庫的鏈接選項,包括庫文件路徑和其他必要的鏈接標(biāo)志;--libs參數(shù)告訴gdal-config命令返回鏈接選項。gdal-config --dep-libs使gdal-config命令來獲取GDAL庫所依賴的其他庫的鏈接選項;--dep-libs參數(shù)告訴gdal-config命令返回依賴庫的鏈接選項。
??最后,-o test是編譯器選項,用于指定生成的可執(zhí)行文件的名稱為test;-o選項后跟著要生成的可執(zhí)行文件的名稱。
??完成上述步驟,在當(dāng)前目錄下就會有一個可執(zhí)行文件,名稱為test。我們執(zhí)行如下的代碼,就可以執(zhí)行這個可執(zhí)行文件。
./test
??運行上述代碼,如下圖所示。

??可以看到,已經(jīng)可以打印出這一景遙感影像的像元行數(shù)與列數(shù)了。
??至此,大功告成。