開源 GIS 數據庫的 20 個年頭


2001 年 5 月 31 日,雅虎小組上發送了一封郵件,宣佈 PostGIS 第一個版本發佈。

PostGIS 與原作者 Paul Ramsey 在 90 年代創辦的一家諮詢公司 Refractions Research 有很大關係。他的第一桶金是 British Columbia 省政府給的,政府由於自己的原因不想用 ESRI 的軟件,所以他的公司就積累了大多數“GIS公司”沒有的技能和經驗。

他們比較擅長數據庫和 FME,最開始擅長 Perl,最後變成了 Java。他們起初最開始的產品叫 Facet,現在不用了,是他們最初四年左右的業務核心。這玩意兒是那個政府任務的關鍵部分,他們做的事情是編寫分析程序,對外提供空間數據分析和導出表格、地圖的功能。

考慮到那個時代的算力,他們不得不使用多個 Sun 工作站在全省範圍內跑運算,然後把結果放進當時的 PostgreSQL 中。

將碎化的空間數據作爲 BLOB 導入到 PostgreSQL 是 PostGIS 的靈感來源,很明顯,他們那時候已經具備了交互式實時分析引擎的基礎,倘若他們可以更進一步,對這些 blob 做更多的事情,而不是簡單的存個 blob 字段值,那就更有趣了。

或許應該做一個空間數據庫

觀察 2000 年那時的空間數據庫,會發現有這些:

  • Oracle 8i 的空間數據選項
  • OpenGIS Simple Features for SQL 規範

然後就沒別的了。

最終,他們採取了兩個措施,一成一敗。

第一個措施,希望省政府能聘用一家精通 Oracle 空間功能的諮詢公司。省政府實際上希望在 Oracle 空間功能基礎上做標準化,但是幾經努力仍沒什麼進展。

那時的甲骨文並沒把 GIS 作爲普遍需求,認爲 GIS 仍然是一個特殊的領域,沒有看到“空間數據庫”有啥用途。

下圖:當時的 Oracle 中的空間數據功能。

image

或許,這樣未嘗不是一件好事。

第二個措施,那就是探索 OpenGIS Simple Features for SQL 規範中所描述的空間數據模型是否可以落地。除了對空間數據類型和要素的描述外,規範中還描述瞭如何在表中存儲空間數據。

image

存儲方案如下:

方案1A:每個要素分解其座標分量,即 x、y、z 等,分別存儲在普通關係數據表的行列中;

方案1B:存“二進制大對象(BLOB)”

方案2:存“幾何類型”

由於之前的工作積累了使用 PostgreSQL 的經驗,他們對其進行了理論測試,希望能把空間數據存儲在數據庫中,並且有效地能提取出來,這樣才能製作一個由 PostgreSQL 支撐起來的空間數據查看器。

image

對於查看器的實現,他們使用了 JShape(一種 Java applet)庫來實現並實驗,順帶給客戶做了一些小網頁應用。因爲它能連接到動態數據庫而不是靜態文件,這讓人很感興趣。

所有的開發工作都是在 Sun Ultra 10 臺式機上完成的,說的是創辦公司時借了 10000 美刀貸款買的。當時,他們的收入大部分來自 Facet 軟件,這玩意兒只跑在 Sun 的硬件平臺上。

image

對於方案1A,把幾何座標切碎分到關係表裏然後取出來重新組成幾何,這個慢到爆,根本沒法用;

對於方案1B,使用 PostgreSQL 的 BLOB 類型存儲大對象,速度好很多,但是還是不夠滿意,因爲沒什麼辦法給空間數據添加空間索引。

突破

由於方案1A/B 比較糟糕,他們幾乎停止了開發,他們甚至已經翻遍了 PostgreSQL 文檔。

團隊中的大佬,Dave Blasby,他學過計算機科學(當時團隊大部分人是數學和物理學的背景),不忌憚低級語言,他上去就是看 PostgreSQL 的源代碼和如何貢獻代碼的資料,然後就排出“給我點時間,我做一個自定義的類型來存儲空間數據”的豪言壯語。

Dave 花了幾天時間,結果:成了。

當 Dave 寫出一個大概之後,他們把草雞程序連接到 applet 程序上,隨後就高歌猛進了。

即使加載足夠大的數據表,縮放數據、繪製地圖,它也很快。原本是 Unix 工作站上的 XWindows 屏幕上能看到的玩意兒,現在居然能在普通 PC 的 applet 上運行,太感人了。

團隊已經從 PostgreSQL 那獲得了不錯的體驗,但是 PostgreSQL 的擴展插件沒什麼商業生態,所以最好的辦法就是把 PostGIS 開源放在那晾着,看看有沒有什麼關注度。

當時 Refractions 好像有 6 個人吧(作者記得不是很清楚了),很多人都爲首發做出了貢獻。

  • Dave Blasby 繼續完善代碼,添加了一些有趣的功能;
  • Jeff Lounsbury 是唯一會 C 語言的,他負責把 shp 文件轉換成 SQL,以便更輕鬆加載已有的空間數據文件;
  • 原作者本人 Paul Ramsey 則承擔了些 Makefile、CVS版本管理、寫文檔以及爲開源做準備的相關工作;
  • 生意上的合作人 Graeme Leeming 和 Phil Kayal 默許了這種開源行爲。Chris Hodgson 是一個很聰明的開發者,他應該挺忙的,起初沒來,不過後面也出現在了提交日誌中。

發佈

最後,在 2001 年 5 月 31 日,Dave 發佈首發消息,它就是 PostGIS 0.1,如果你想,你現在仍然可以下載到它。

譯者注:GitHub 官方倉庫的第一個 commit tag 還保存得很完整,那個就是 PostGIS 0.1 的提交,你甚至能看到簡樸的文本日誌。

第一個版本有一個幾何類型(GEOMETRY),它使用 PostgreSQL GIST API 作爲空間索引,以及附送了下列函數:

  • npoints(GEOMETRY)
  • nrings(GEOMETRY)
  • mem_size(GEOMETRY)
  • numb_sub_objs(GEOMETRY)
  • summary(GEOMETRY)
  • length3d(GEOMETRY)
  • length2d(GEOMETRY)
  • area2d(GEOMETRY)
  • perimeter3d(GEOMETRY)
  • perimeter2d(GEOMETRY)
  • truly_inside(GEOMETRY, GEOMETRY)

truly_inside() 是唯一的分析函數,它只是用來計算一個點是不是在一個多邊形內。

現在回過頭看 2001 年那些早期的郵件,感嘆 20 年裏 PostGIS 集成各路開源地理空間成果的速度是那麼的快。

GDAL 的 Frank Warmerdam 和 MapServer 的 Daniel Morissette 在 PostGIS 發佈的第一個月內就發佈了貼子,Java GeoTools/GeoServer 方面的開發人員也很快就出現了。

對開源空間數據庫的需求之大,可以想象。他們只是出現在了合適的時間點。

他們現在怎麼樣啦

  • Graeme,Phil,Jeff 和 Chris 仍然在 Refractions Research 從事地理空間諮詢服務的工作
  • Dave 最初的幾年裏維護和改進了 PostGIS,離開 Refractions 後從事別的工作,但是依舊是開源地理空間方面的,主要是 GeoServer 和其它 Java 項目中
  • 作者本人 Paul Ramsey 覺得 PostGIS 有光明的未來,反而地理諮詢工作並不能激起他的興趣,2008年,他離開了 Refractions 並學習了足夠的 C 語言,作爲貢獻者加入 PostGIS 開發社區,從那時到現在一直在做這件事,目前是 Crunchy Data 的地理空間執行工程師

譯者注

原文來自 PostGIS 的初創,也是核心貢獻者 Paul Ramsey 在 2021 年 5 月的博客 http://blog.cleverelephant.ca/2021/05/postgis-20-years.html

通過了解這段歷史,在震撼、驚訝於世界開源 GIS 社區在這 20 年的持續發展的成果的同時,我更敬佩的是這羣大叔在那個新千年伊始,那個或許與現在我差不多的年紀能做出那麼有意義的事情。放到現在,說是開源 GIS 社區的開天闢地也不爲過。

GIS 重數據,重概念,天生合適後端,彼時還沒有數據庫廠商把空間數據作爲一個菜籃子,頂多就是存儲一個非常簡單的座標、線、多邊形之類的簡單功能。

笑。現在國內跑業務的大部分團隊不也是這樣嗎?

可是就是 20 年前那會兒,他們就意識到了這點,並做了開源。

時至今日,PostGIS 仍然是開源 GIS 數據庫的一哥,MySQL 等數據庫雖然有簡單的空間功能,但與 PostGIS 融合了世界圖形界多個成熟庫做出來的一批函數相比,實在只能說還不夠“GIS”味,好比早餐喫油條少了豆漿,缺了點什麼。

現在,PostGIS 已經來到了 3.x,也有公司在背後背書,GitHub 上的源代碼也仍然在有節奏地更新,有不錯的學習價值。

希望閱讀這些資料能對自己有所啓迪,如果能啓發讀者,那是我的榮幸。

附件 PostGIS 中幾何類型有關的結構體定義

第一個版本

typedef struct
{
	double x,y,z;  //for lat/long   x=long, y=lat
} POINT3D;

typedef struct
{
  POINT3D LLB,URT; /* corner POINT3Ds on long diagonal */
} BOX3D;

typedef struct
{
  int32 size;           // postgres variable-length type requirement
  int32 type;           // this type of geometry
  bool is3d;            // true if the points are 3d (only for output)
  BOX3D bvol;           // bounding volume of all the geo objects
  int32 nobjs;          // how many sub-objects in this object
  int32 objType[1];     // type of object
  int32 objOffset[1];   // offset (in bytes) into this structure where 
                        // the object is located
  char objData[1];      // store for actual objects
} GEOMETRY;


// ONLY FOR INDEXING
typedef struct geomkey {
  int32 size; /* size in varlena terms */
  BOX key;
} GEOMETRYKEY;

最新版本

而現在,它位於 https://github.com/postgis/postgis 倉庫中 liblwgeom/liblwgeom.h.in 文件下,有着非常不一樣的定義:

/******************************************************************
* LWGEOM (any geometry type)
*
* Abstract type, note that 'type', 'bbox' and 'srid' are available in
* all geometry variants.
*/
typedef struct
{
  GBOX *bbox;
  void *data;
  int32_t srid;
  lwflags_t flags;
  uint8_t type;
  char pad[1]; /* Padding to 24 bytes (unused) */
}
LWGEOM;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章