??本文介紹在Linux的Ubuntu操作系統中,基于Docker快速配置Python與C++這2
種不同編程語言可用的地理數據處理庫GDAL
開發環境的方法。
??本文就將Python與C++這2
種不同編程語言的GDAL
模塊配置方法分開來介紹,大家依據自己的需求來選擇即可——但無論是哪種方法,配置GDAL
模塊的方法都非常簡單,終端中輸入幾句代碼就完成了。和我們之前在Windows系統中配置GDAL
模塊的文章GDAL庫在Visual Studio C++環境中的配置比起來,真的是方便了很多。
1 Python版本
??首先,我們訪問GDAL
庫的Docker鏡像官方網站。這里需要注意,雖然這個官方網站似乎并沒有明確說明它提供的版本只能Python使用,但是我這里下載后發現C++代碼確實無法調用這個鏡像中的GDAL
模塊。
??其中,官方網站提供了Alpine和Ubuntu兩種不同系統的Docker鏡像;并且對于不同的系統版本,其還提供了Small和Full兩種不同的鏡像內容,其中前者包含的內容相對較少,而后者包含的內容較為齊全(因此后者的鏡像大小也就更大一些),而這兩種鏡像自身都是包含Python的3.8
或以上版本的。此外,關于Small和Full兩種不同鏡像的具體詳細內容差異,我們這里就不再贅述了,大家在其官方網站查閱即可;具體如下圖所示。
??在我這里,由于只是需要用GDAL
庫完成一些讀取.tif
格式文件的操作,所以并不需要特別完整的GDAL
庫,所以就選擇了Small這個小一點的版本。
??接下來,我們在Ubuntu電腦的終端中執行如下的代碼。這里需要注意,由于我需要的是Ubuntu系統的Small版本,所以我就輸入如下的代碼即可;如果大家使用的是Alpine操作系統,或者是Ubuntu系統的Full版本,那么按照上圖中自己所需要的版本對應的名稱,修改下述代碼并執行即可。
docker pull ghcr.io/osgeo/gdal:ubuntu-small-latest
??運行上述代碼,如下圖所示。
??稍等片刻,我們就完成了鏡像的獲取。此時,我們可以通過如下的代碼,查看當前電腦中Docker鏡像的下載情況(也就是看看我們已經有了哪些鏡像)。
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
,防止自己摸索過程中不知不覺建立了好多個無用的容器,到時候還要手動一個一個刪除);后面的就是我們剛剛下載好的鏡像,表示我們要基于這個鏡像去運行一個容器。運行上述代碼,如下圖所示。
??接下來,我們就進入了容器。此時,繼續輸入如下的代碼,查看當前容器中GDAL
庫的版本信息。
gdalinfo --version
??運行上述代碼,如下圖所示。可以看到,此時將打印出我們GDAL
庫的版本信息。
??接下來,我們先通過如下的代碼,退出當前鏡像,回到終端中。
exit
??運行上述代碼,如下圖所示。
??我們既然配置了一個GDAL
庫的Docker鏡像,那么后續肯定是需要將一些我們自己電腦中的文件(比如柵格圖像、矢量數據等文件)帶入到這個鏡像的容器中去運行,所以肯定需要這個GDAL
庫的Docker鏡像要和我們Ubuntu電腦中文件可以交互(換句話說,也就是可以讀取、修改我們電腦中的文件與數據)。因此,我們在之后進入我們這個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
這一個部分——這一部分是用于掛載主機文件系統中的目錄到容器中的命令參數。其中,-v
是Docker命令中用于掛載文件或目錄的選項,其后面的/home/dell/cppGDAL:/home/dell/cppGDAL
,則是文件掛載的源目錄和目標目錄的路徑——它指定了主機文件系統中的/home/dell/cppGDAL
目錄將被掛載到容器內的/home/dell/cppGDAL
目錄。
??這里多提一句,我們這里是將主機中的一個指定文件路徑掛載到了容器中,所以屬于Docker中的Bind mounts;如果我們這里是手動創建了一個Volume,然后掛載到容器中,那么就叫做Volume;此外還有一種叫做tmpfs mounts,是把容器的數據寫入主機的內存中——上述的Bind mounts、Volume與tmpfs mounts,這3
種都是Docker用以數據管理、數據記憶的方式。
??回到前述的代碼。換句話說,上述命令將我的Ubuntu電腦中的/home/dell/cppGDAL
目錄與GDAL
庫的Docker鏡像的容器中的/home/dell/cppGDAL
目錄進行了掛載。這樣,在容器中對掛載點/home/dell/cppGDAL
的操作將反映在主機系統的/home/dell/cppGDAL
目錄上,反之亦然。
??相當于通過這種方式,只要我將我需要用GDAL
庫處理的數據、代碼等文件,都放在電腦的/home/dell/cppGDAL
目錄下,那么就可以在容器中對這些數據加以訪問和處理。這樣即實現了文件的交互,同樣可以保證容器不會訪問我們電腦中其他文件夾內的數據或者文件,保證了數據的安全。
??如果大家還是沒有明白這句代碼的意義,不著急,我們先運行上述代碼,如下圖所示。
??上圖中運行完代碼,我又不小心多運行了一句pwd
代碼,大家理解即可。
??為了更清晰地看到前述那一種進入容器的代碼的意義,我們做一個如下的對比。如下圖所示,這是我們用了那一句包含掛載文件夾命令的代碼,進入我們的容器后,執行的操作;可以看到,此時在容器中,我們就可以進入/home/dell/cppGDAL
目錄下。
??而如果我們并沒有掛載文件,而是用了本文中第一次出現的那一句代碼進入容器的代碼,也就是前面的docker run -it --rm ghcr.io/osgeo/gdal:ubuntu-small-latest
代碼,進入容器后會發現,cd
進入home
文件夾后,再ls
,是看不到我們這個cppGDAL
文件夾的;換句話說,此時我們就沒有辦法在容器內部讀取我們電腦里/home/dell/cppGDAL
目錄下的文件了——連文件、數據都無法獲取,那么這個GDAL
鏡像肯定也是沒有用處的了。
??此外,前面我們還提到,-v /home/dell/cppGDAL:/home/dell/cppGDAL
這一個部分可以保證鏡像可以且僅可以讀取/home/dell/cppGDAL
目錄下的文件,而不會讀取到我們沒有掛載的其他文件夾。針對這一個內容,我們再做一個對比。如下圖所示,是我們直接在Ubuntu電腦的終端中,進入/home/dell
目錄的情況;可以很明顯地看到,在電腦中的/home/dell
目錄下,不僅有我們的這個cppGDAL
文件夾,還有很多很多其他的文件或者文件夾;而在上上圖中,可以看到在容器中,我們進入/home/dell/cppGDAL
目錄下只能看到這個cppGDAL
文件夾,而看不到電腦中這一路徑下原本還有的其他文件或者文件夾。所以很明顯,相當于我們就是可以在鏡像中訪問/home/dell/cppGDAL
目錄,但是無法訪問沒有掛載的其他文件夾,從而保證了其他無關文件夾的安全性。
??明白了上述內容,就可以開始我們的GDAL
操作了。例如,我這里在/home/dell/cppGDAL
目錄下還有一個名稱為TIF
的文件夾,其中保存了一景遙感影像,那么我就可以通過gdalinfo
語句,查看這一柵格數據的信息。如下圖所示。
??最后,每一次完成鏡像中的操作后,不要忘記通過exit
命令,退出鏡像。
??因為我這里是需要C++版本的GDAL
模塊,所以后來也就沒有對上述Python版本的再加以代碼測試;但經過上述配置,運行Python代碼的GDAL
程序應該是沒有問題了。
2 C++版本
??接下來,我們介紹配置C++版本的GDAL
模塊的方法。
??由于GDAL
官方似乎并未提供直接的C++版本鏡像,所以我們這里就自己創建一個Docker鏡像,隨后在其中配置GDAL
模塊。這里需要注意,如果大家剛剛根據前文的流程,先配置了一個Python語言的GDAL
模塊的鏡像,那么建議大家在另一個新的鏡像內重新配置C++版本的,不要直接在前面的Python語言鏡像中配置GDAL
模塊——因為官網說,在前面這個Python語言的GDAL
模塊的鏡像內配置其他版本的GDAL
模塊,會容易由于GDAL
模塊的版本沖突導致容器無法工作(雖然我當時簡單嘗試了一下,發現即使如此,容器似乎還是可以正常工作的)。
??我們這里就在一個新的Ubuntu鏡像中加以配置。首先,在終端中輸入如下代碼,創建一個Ubuntu鏡像。
docker pull ubuntu
??運行上述代碼,如下圖所示。
??接下來,我們用前文提到的這一句代碼,運行一個容器。這里我就不再用--rm
了,從而使得我們這個容器之后可以多次重復使用。
docker run -it -v /home/dell/cppGDAL:/home/cppGDAL ubuntu:latest
??運行上述代碼,如下圖所示。
??接下來,因為我們這個容器是基于一個空白的Ubuntu鏡像創建的,很多執行GDAL
的C++代碼所需的配置都沒有處理,我們需要配置一下基本的環境。
??首先,通過如下代碼更新軟件包列表。
apt update
??運行上述代碼,如下圖所示。
??隨后,輸入如下的代碼,配置GDAL
模塊的C++庫。其中,libpq-dev
是PostgreSQL數據庫的開發庫,包含了開發PostgreSQL應用程序所需的頭文件和靜態庫;gdal-bin
是GDAL
的二進制工具包,提供了一些用于處理地理空間數據的工具,如轉換、裁剪等;libgdal-dev
是GDAL
的開發庫,包含了開發GDAL
應用程序所需的頭文件和靜態庫。
apt install libpq-dev gdal-bin libgdal-dev
??運行上述代碼,如下圖所示。
??稍等片刻,中間有一個環節需要我們根據自己所在位置加以選擇,從而配置自己的時區;如下圖所示。
??完成配置后,通過如下的代碼查看GDAL
庫的版本。
gdalinfo --version
??運行上述代碼,如下圖所示。
??接下來,我們再按照文章在Linux系統cmd配置C++環境的方法介紹的方法,配置Ubuntu的C++代碼開發環境,這里就不再贅述了。
??隨后,我們就可以在Docker中執行一個簡單的C++程序,來驗證這個GDAL
庫的配置是否成功。其中,我們因為已經掛載了文件夾,所以既可以在主機中通過其他編輯器來撰寫這個C++代碼,也可以在容器中通過Vim來撰寫。但無論怎么撰寫,都要記得將這個代碼文件(也就是.cpp
格式的文件)放在已經掛載了的文件路徑內。
??這個簡單的C++代碼如下;其含義就是,從我們已經掛載了的主機的一個文件夾中,讀取一景柵格影像,獲取并打印其像元的行數與列數。
#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;
}
??隨后,在容器內的上述代碼文件目錄下,執行如下的代碼。
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
庫的編譯選項,包括頭文件路徑和其他必要的編譯標志;--cflags
參數告訴gdal-config
命令返回編譯選項。rec.cpp
是要編譯的C++源文件的文件名,也就是前面我們寫的代碼文件的文件名稱。需要注意的是,上述代碼中沒有單引號,而都是反引號,大家輸入的時候不要輸錯了。
??其次,gdal-config --libs
使用gdal-config
命令來獲取GDAL
庫的鏈接選項,包括庫文件路徑和其他必要的鏈接標志;--libs
參數告訴gdal-config
命令返回鏈接選項。gdal-config --dep-libs
使gdal-config
命令來獲取GDAL
庫所依賴的其他庫的鏈接選項;--dep-libs
參數告訴gdal-config
命令返回依賴庫的鏈接選項。
??最后,-o test
是編譯器選項,用于指定生成的可執行文件的名稱為test
;-o
選項后跟著要生成的可執行文件的名稱。
??完成上述步驟,在當前目錄下就會有一個可執行文件,名稱為test
。我們執行如下的代碼,就可以執行這個可執行文件。
./test
??運行上述代碼,如下圖所示。
??可以看到,已經可以打印出這一景遙感影像的像元行數與列數了。
??至此,大功告成。