楊艷林, 靖 晶, 劉廣寧, 王世昌
(1.中國地質調查局 武漢地質調查中心,武漢 430205; 2.中國地質大學(武漢) 環境學院,武漢 430074)
MapGis軟件是水工環地質調查中提交圖件及數據庫的專用軟件,而圖元屬性是MapGis管理數據的強項,它不再是簡單的紙質圖形,而是圖形與屬性的融合,大大提升了圖件的信息量與實用價值,如屬性數據可為后期的空間分析、地下水水量與水質評價等方面提供數據支撐。在水工環地質調查過程中,獲得了大量野外調查點及點屬性的卡片(如水文地質調查中的井點卡片),包括的屬性數據有:井位、井結構、井開采、井水位水質等,其數據項多達50項[1],而這些屬性數據最后都要包含在管理數據庫和編制圖件的空間數據庫中,才能進行成果提交。
通常在進行野外水工環地質測繪時,每天都會將記錄的卡片數據錄入到數據庫中進行管理。當野外測繪完成后,在進行成果圖件編制時,若采取一邊投點,一邊錄入屬性的方法,將會擠占技術人員大量寶貴的時間,而且也沒有充分利用管理數據庫中數據可導出再利用的特點,故非常有必要在數據庫與編圖之間架起一座橋梁,尤其是調查點與屬性數據的關聯。
在這方面,已有不少學者進行了相關研究,秦威[2]利用VC編程語言對MapGis進行二次開發,完成了屬性數據的輸入;汪新慶等[3]在MapGis二次開發環境下,利用VC編程語言實現了屬性數據的邏輯檢查,提高了圖件檢查驗收的工作效率,并保證了數據的準確性與可靠性;張厚泉[4]利用MapGis軟件中“投影變換”子系統內的“用戶文件投影變換”命令實現了水井點圖元批量編圖;史文博[5]利用MapGis軟件討論了5種屬性連接的方法;張良紅等[6]利用MapGis提供的MFC類庫,完成了航點生成MapGis點文件的方法。綜觀目前的方法主要有三種:①利用MapGis的明碼文件對野外調查點進行投點,但不能將屬性數據一并進行導入;②利用MapGis平臺進行二次開發,但不能解決同其他軟件數據的無縫轉換;③利用MapGis軟件提供的功能,需熟練地掌握MapGis軟件,也無法完成同其他軟件之間的數據共享。
目前,GIS軟件產品眾多,如ArcGis、MapInfo、AutoCAD、MapGis、GeoStar、SuperMap、CityStar等,不同軟件平臺之間的數據轉換越來越頻繁,特別是與MapGis數據之間的轉換;而不同的GIS系統對各種地質現象的理解、描述方式、概念模型、數據結構、實現手段等互不相同,且缺少統一的數據接口,雖然可以通過一些中間文件實現互轉,但極易丟失屬性數據,造成GIS數據之間共享困難,形成了人力、財力上的浪費,資源得不到有效地利用[7]。閆琰等[8]在研究以直接和間接轉換的方法實現ArcGis向MapGis軟件的數據格式轉換時,發現很難進行一次性批量轉換,數據易失真,圖形渲染易變樣,無法實現GIS數據的無損轉換;王星捷[9]以MapGis點文件的數據格式進行分析研究,并編制程序實現了MapGis點文件的讀取實驗,但對屬性數據的處理未進行深入的研究。
鑒于此,為了充分發揮計算機的強大計算性能,從根本上解決:快速輸入野外采集的屬性數據、快速讀取前人調查點的屬性數據、MapGis多屬性數據快速成圖、打破不同GIS平臺之間的“信息孤島”、且不受MapGis平臺限制的接口程序等方面的問題,以及有效提高成果資料的信息化和服務能力,其關鍵是對MapGis點文件的分析,筆者將從MapGis點文件(無TIC點)及其屬性塊的數據結構、存儲算法和關鍵環節等方面進行了詳細闡述,并且為了敘述的方面,對用到的數據結構、函數或算法,都以Visual C++語言的形式列出。
MapGis點文件是以WT為后綴名的二進制文件,主要包括空間數據與屬性數據。空間數據為字符串、文本、子圖、圓、弧段、圖片等五種類型,參考MapGis安裝目錄中的baseDefine.h文件,其數據結構見表1~表6。屬性數據的數據類型多達17種,常用的主要有字符串(string)、短整型(short)、長整型(long)、浮點型(float)、雙精度型(double)、日期型和時間型等,屬性的個數不受限制[10]。

表1 MapGis點文件中字符串的數據結構

表2 MapGis點文件中文本的數據結構

表3 MapGis點文件中子圖數據結構

表4 MapGis點文件中圓的數據結構

表5 MapGis點文件中弧的數據結構

表6 MapGis中圖片的數據結構
利用UltraEdit工具打開點文件,并結合MapGis軟件對點文件的顯示以及前人的研究[9],可將點文件存儲的數據分為三個數據段:
1) 在前面的657個字節,主要用于記錄點文件標識(57 4D 41 50 60 44 32 32)、地圖參數(投影參數、單位及比例尺、圖幅范圍等)和點個數。
2) 緊接著的160字節是用于記錄16個數據塊的位置、長度,其中位置和長度都由4字節進行存儲,后2字節是結束標記,值為-1,其數據結構如表7所示。在MapGis6.7版本中,16個數據塊中目前主要使用的有5個:數據塊1主要是字符串、文本、弧、圓、圖片等繪制參數,每個點由93個字節組成,其數據結構見表8、表9、表10;數據塊2存放的是字符串ch_struct結構中的str_text數據值,或文本text_struct結構中的str_word數據值等,其長度由表8結構中的len變量記錄;數據塊3是屬性數據,屬性數據由屬性頭(348個字節)、字段頭(39個字節)、字段內容(為變長度,字段頭中進行了定義)三部分組成;數據塊4是每個點的范圍數據(左下角坐標和右上角坐標);數據塊5是點所在的圖層和圖元類型。
3)存放各數據塊的數據,MapGis6.7版本中主要有5個。

表7 數據塊位置數據結構

表8 MapGis點文件中點數據結構

表9 PNT_INFO_UNION數據結構

表10 D_DOT數據結構
MapGis點文件的屬性數據是各類地物特征信息的具體記錄,主要用于描述實體要素的類別、特征和性質。由于各領域的專業屬性差異甚大,不能用一個已知屬性集描述或概括所有應用的專業屬性。在水文地質調查中,記錄了大量井、泉等水文地質點的屬性數據,為了快速無縫地將這些屬性數據加入到成果圖件的空間數據庫中,需準確掌握MapGis點文件屬性數據的存儲與讀取算法,而屬性數據結構是關鍵。
基于MapGis點文件的二進制分析,屬性數據由屬性頭、字段頭和屬性數據三部分構成,屬性頭的數據結構(表11),占348個字節,記錄的信息有:屬性長度(值為426+字段數×39)、點個數、字段個數、記錄長度等。為了便于理解和處理,將字段頭與屬性數據進行組合,其數據結構如表12所示,包括了字段名,字段類型,字段字節長度,字段字符的長度、小數位數、編輯狀態,及字段值等。其中字段類型有字符串型、浮點型、長整型、短整形等10余種,字段的屬性值則存放在fieldVal中。

表11 屬性頭數據結構

表12 字段信息數據結構
為了對多屬性數據進行讀/寫操作,將屬性頭數據結構(記為INFO_HEAD)與字段信息數據結構(記為FIELD_HEAD)進行組合,形成了屬性塊數據結構(表13)。

表13 屬性塊的數據結構
按照數據結構中各數據類型進行讀寫,在進行讀寫之前,需對字段分配內存空間,如fldEntry = newFIELD_HEAD[hd.numbfield];然后對屬性頭數據結構中的字段數據,可按fread/fwrite(&numbfield, sizeof(numfield), 1, pFile)(pFile為文件指針)進行讀寫操作;字段數據結構中字段名,可按fread/fwrite(fieldname, 20, 1, pFile)進行讀寫操作(字段名的長度不能超過20);以及字段值可按fread/fwrite(fieldVal[iField], fieldlength, 1, pFile) (iField為第幾個字段)進行讀寫操作。

圖1 野外泉點部分屬性數據Fig.1 Paritial attribute data of the field springs
將野外調查卡片數據錄入管理數據庫中后,各種屬性數據可直接導出為Excel或TXT文件格式。編制圖件時,參考建庫指南[1],篩選目標屬性數據,通過類似于圖1所示的交互性對話框,將目標屬性數據復制到對話框上,后將其寫出為所需的目標點文件。

圖2 MapGis中屬性結構設置Fig.2 Attribute structure settings in the MapGis
為了準確寫出點文件,計算屬性數據塊的長度十分重要,按照前面的分析,其長度可分為3個部分,屬性頭、字段頭和屬性數據,其中屬性頭長為348字節,字段頭的長為39×字段個數(即hd.numbfield),而屬性數據的長度為:數據個數╳(標識符 + 每個點數據中屬性的長度)(其中標識的長度通常為一個字節),其中每個點數據的屬性長度可按式1進行計算。
len=0 for (int i = 0; i (1) 在MapGis中子圖號通常是用數字來進行標識,而注釋通常是文字,故可依據這一點進行子圖號與注釋的判定。 在將點數據寫成MapGis點文件時,若某個字段為注釋,則注釋的長度是變長的,圖1中的地層時代,若在程序直接分配一個大大的內存來進行存儲,當然是可以,但這樣浪費了太多的內存空間,故需要自動識別字符串的長度。通過遍歷操作地層時代,計算出該字段下的最大字符串長度,可按函數strlen(),或CString的GetLength()函數。為了處理中文的需要,常會用Unicode字符集,這時在計算字符串長度時,需利用WideCharToMultiByte函數將寬字節轉變為多字節來計算字符串長度。 在使用MapGis軟件編輯點文件屬性結構時,常會彈出圖2所示的對話框,經常會誤認為字段長度就是浮點數的內存長度,其實這是一種誤解。目前,在計算機內部,浮點數主要分為單精度(float)和雙精度(double),其計算內存大小分別是4位和8位,有效小數位數分別是7和16。而屬性結構中的字段長度是指將浮點數轉為字符串時字符串的顯示長度,小數位數就是字符串中顯示小數的個數(圖3)。 圖3 浮點數內存存儲及MapGis中顯示對比Fig.3 Floating-point memory storage and comparison in the MapGis 由前面分析知,當某字段的數據類型為浮點型時,如圖1中的泉流量,若直接賦為雙精度類型,不會有問題,但從節約內存的角度來看,這樣就有點欠妥,故需判定該字段是單精度還是雙精度,并計算出浮點數的長度和小數位數等數據。這時需要遍歷該字段的所有數據,按字符串形式將數據讀入,計算字符串的長度,找到小數點并計算小數點位數,通過遍歷找到最長字符串(記為nMaxLen,則msk_leng等于nMaxLen)和最長小數點位數長度(記為nDigit,則point_leng等于nDigit)。由于單精度浮點數的有效位數為7位,若nDigit小于7,則可賦為單精度浮點型(即fieldtype等于4,fieldlength等于4),否則為雙精度浮點型(即fieldtype等于5,fieldlength等于8)。當然還需判定該數是否超過了單精度浮點數的范圍(-3.4E38~3.4E38);在水工環調查中,數據通常都在單精度浮點數范圍內。 圖4 屬性值寫出過程及實例Fig.4 Attribute value writing process and example 短整型與長整型的主要區別是取值范圍和字節大小不同。短整形所能表示的整數域為-32 768~32 767,占2字節內存;而長整型表示的整數域為-2 147 483 648~2 147 483 647,占4字節內存。若某字段數據無小數點,則為整型數,后通過遍歷判斷字段內的數據是否在-32 768~32 767范圍內,若在范圍內,則為短整型(即fieldtype等于2,fieldlength等于2),否則為長整型(即fieldtype等于3,fieldlength等于4)。 確定了字段長度(fieldlength)后,就可以為屬性值(fieldVal)分配內存,其大小為hd.num1×fieldlength。在進行屬性值寫出時,不能直接將屬性值(fieldVal存儲的)直接寫出,需將屬性值轉換為該字段的數據類型,并按字段數據類型進行存儲(圖4)。若在進行屬性值讀入時,則是圖4的逆過程。 通過多次MapGis軟件對時間、日期數據的讀入與寫出,確定了時間和日期的數據結構類型分別為TIME_STR和DATE_STRU,所占內存大小分別為10字節和4字節(表14、表15)。 表14 自定義時間數據結構 表15 自定義日期數據結構 根據MapGis點文件二進制分析,尤其在點文件屬性數據方面,采用面向對象可視化語言Visual C++,編制了MapGis點文件讀取與寫入程序,其讀取過程見函數ReadMapGisWTData;并為其研發了人機交互界面(圖2)。當進行點文件生成時,通過設置點數和屬性數,將點數據輸入到網格中,也可以直接將Excel表格中的數據復制到表格,點擊“導出”即可完成。當進行點文件屬性編輯時,點擊“導入”即可讀入已有的點文件,并將點數據顯示在網格中,編輯修改后點擊“導出”生成點文件。并且也提供了點文件投影參數的設置。最后,將生成的點文件加載到MapGis軟件中,即可完成多屬性點數據的快速成圖。 通過對比分析基于交互式界面輸入的點數據與MapGis中的點屬性數據,結果一致,表明前面對點文件分析和程序編寫的正確性(圖5)。在2018年江西省清溪村幅1:50 000水文地質調查中,借助本程序,完成了井、泉、水文孔等水文地質點的屬性數據從管理數據庫到空間數據庫的快速成圖,大大提高了成圖效率。 boolReadMapGisWTData(FILE* pFile) { ReadFileHead(pFile);//文件頭- 657個字節進行讀入 ReadDataHead(pFile);//數據頭- 160個字節進行讀入 SetSize(nLin_show);//分配數據內存空間 fseek(pFile, position[0], SEEK_SET);//子圖數據 char* old_locale = _strdup( setlocale(LC_CTYPE,NULL) ); setlocale( LC_CTYPE, "chs");//讀出中文 longpos = ftell(pFile); for (inti = 0; i { wtData[i].ReadData(pFile, position[1]);//讀取點數據 pos += 93;fseek(pFile, pos, SEEK_SET);//文件指針 } setlocale( LC_CTYPE, old_locale); free( old_locale );//還原區域設定 fseek(pFile, position[2], SEEK_SET); wtAttData.ReadField(pFile, nLin_show);//讀取屬性數據段 fseek(pFile, position[3], SEEK_SET); for (inti = 0; i fseek(pFile, position[4], SEEK_SET);//點圖元數據段 for (inti = 0; i { fread(&layer[i], 2, 1, pFile); fread(&kind[i], 2, 1, pFile); } return 1; } 圖5 點數據寫入MapGis點文件中Fig.5 Data written to the MapGis point file 1)通過對MapGis點文件的剖析,將點文件存儲的數據分為三個數據段,并給出點文件中空間數據的數據結構。 2)基于點文件屬性數據的分析,屬性數據由屬性頭、字段頭和屬性數據三部分構成,給出了對應的數據結構,并對屬性數據中各字段的讀寫進行了介紹。 3)針對屬性數據易誤解、準確寫出易出錯、節省內存空間等關鍵部分,如屬性數據的長度計算、注釋與子圖的自動識別、常用數據類型自動識別、浮點數據類型的判定、屬性數據的讀入與寫出、以及時間與日期數據結構等方面進行了詳述,確保屬性數據的準確寫出。 4)利用VC++語言,研發了界面友好,使用方便、快捷、操作性強的可視化界面,明顯降低了勞動強度,有效地提高了工作效率,更重要的是確保了屬性數據的準確性和可靠性。 為了實現其它GIS系統的點、線、區等屬性數據向MapGis的無縫轉換,筆者還未對MapGis點文件中包含TIC點,以及線、區文件進行研究,這將是后面繼續研究的方向。4.2 子圖號與注釋自動識別
4.3 字符串長度
4.4 易被誤解的地方

4.5 小數位數及字段長度自動識別

4.6 長、短整型自動識別
4.7 屬性值快速讀/寫
4.8 時間、日期數據結構


5 算法編制及實例應用

6 結論與建議