您好,歡迎來電子發(fā)燒友網! ,新用戶?[免費注冊]

您的位置:電子發(fā)燒友網>電子百科>通信技術>衛(wèi)星通信>

GIS地圖開發(fā) - 全文

2018年01月15日 18:33 電子發(fā)燒友網 作者: 用戶評論(0
關鍵字:GIS(42039)

一、 地圖是怎么做出來的

首先說一下地圖是怎么出來的,可能你感覺是廢話,但實際上很多人并不知道如何下手。我覺得這里需要先給你個思路準備:地圖就是使用繪圖語句畫出來的!

從底層繪制地圖,能使用的就是繪圖函數(shù),在.NET里,就是用Graphics類的方法,在窗口中繪制點、線、面、標準、柵格等,組合起來,就是一張地圖(瓦片圖方式除外)。

關于.NET的繪圖,本文不進行講解,如果你還還不熟悉,建議你先看看這方面資料。

二、 坐標轉換-地圖繪制的關鍵

.NET提供了大量繪圖方法,基本上都是以Graphics類的函數(shù)形式提供,包括各類幾何形狀、圖像、文字的繪制,靈活運用這些方法,就可以畫出精美的圖出來。假設你已熟悉.NET的繪圖,這樣就只有有一個問題要解決:圖我會畫了,但拿到地圖元素一般為地理坐標(經緯度),應該畫在地圖上什么位置?這就需要涉及到坐標轉換問題。

先不考慮怎么實現(xiàn),首先需要這么一個函數(shù):

/// 《summary》

/// 經緯度轉換為屏幕坐標

/// 《/summary》

/// 《param name=“xy”》經緯度《/param》

/// 《returns》屏幕坐標《/returns》

public Point WorldToScreen(PointF xy)

再一個,有時,還需要根據(jù)屏幕上點位置反算出它的經緯度,如在需要顯示鼠標指針處的經緯度,所以還需要這么一個函數(shù):

/// 《summary》

/// 屏幕坐標轉換為經緯度

/// 《/summary》

/// 《param name=“xy”》屏幕坐標《/param》

/// 《returns》經緯度《/returns》

public PointF ScreenToWorld(Point xy)

有了這兩個函數(shù),就可以將以經緯度表示的地理坐標轉換為屏幕坐標,然后再屏幕繪圖了。

為了完成坐標轉換,需要使用幾個地圖參數(shù)的變量:地圖縮放倍數(shù)、地圖中心點經緯度、地圖大小,關于地圖參數(shù),可參考這篇文章:

http://hi.baidu.com/geochenyj/blog/item/6b5c5c1294057557f819b835.html

另外,還需要對地圖進行縮放、平移,這些操作實質上也是對地圖參數(shù)的操作,如放大就是對地圖縮放倍數(shù)操作,平移就是對地圖中心點進行操作,我們將這些操作也寫Coordinator類的方法。投影變換也作為坐標轉換的一部分,Coordinator類還增加了投影方面方法,這個后面再講。

將上面兩個坐標轉換函數(shù)和三個地圖參數(shù)封裝為一個類Coordinator。,的類如下所示:

?GIS地圖開發(fā)

三、 繪圖

有了坐標轉換類Coordinator,就可以用經緯度數(shù)據(jù)來繪圖了,如拿到某省的行政邊界經緯度坐標數(shù)據(jù),就可以將經緯度數(shù)據(jù)轉換為屏幕坐標,然后用Graphics的方法來畫出來了,Graphics對象又從哪里來呢?可以從一個Image對象創(chuàng)建,也可以從一個控件的Paint事件中取得,總之,有了坐標,發(fā)揮你的想象力,自己畫吧。

在氣象數(shù)據(jù)分析中,除了要繪制點、線、面、文字、柵格外,還需要繪制一些特殊符號,如風、天氣現(xiàn)象、云等。這些符號,可以用圖片、天氣字庫、符號庫來實現(xiàn),圖片方式實現(xiàn)簡單,色彩豐富,但縮放效果不好;字庫方式,需要安裝字庫,程序部署比較麻煩;符號庫方式代碼編寫較麻煩。FreeMicaps的天氣現(xiàn)象符號采用符號庫方式,祥見: http://blog.csdn.net/HZGJF/archive/2009/05/27/4220508.aspx

風符號和云量符號采用計算坐標繪制方式。

為了使用方便,F(xiàn)reeMicaps把符號繪制功能封裝到三個符號類中,以靜態(tài)方法提供。

?GIS地圖開發(fā)

.NET的繪圖是對GDI+的封裝,包括了對點、線、面等各種圖形元素的封裝,圖形圖像的繪制、坐標旋轉,各種反走樣和平滑等功能,功能十分強大(當然,效率不太高),利用它可以繪出漂亮的圖形。

根據(jù)OGC標準,GIS系統(tǒng)首先需要對地圖元素進行抽象和封裝,但FreeMicaps中,經再三考慮,放棄了這種方式,一個是因為工作量比較大,另一個是因為我不敢保證能很好地進行封裝,可能給插件開發(fā)帶來麻煩,不如把繪圖權完全交給圖層,大家自由發(fā)揮。

四、 圖層

為了使繪圖過程便于管理,可將繪圖過程分為組,如可以將一張地圖的繪制分為:繪制世界地圖、繪制中國地圖、繪制河流、填地名幾個過程,每次繪圖好像就是在一張玻璃上繪制,疊加起來就形成了一張地圖圖,這里把每次繪圖過程形象地稱為一個圖層。地圖分層后,圖層可以增刪,每個圖層可以單獨進行隱藏、設置屬性等,更重要的是可以將利用面向對象技術把每個圖層當做一個對象進行管理。詳細介紹見:http://blog.csdn.net/HZGJF/archive/2008/10/03/3014558.aspx

對圖層進行抽象,它應該有一個圖層繪制方法(Render),一個圖層標題(LayerName),一個用于表示數(shù)據(jù)源的字符串(DataSource),一個用于表示繪圖樣式的設置的LayerStyle,加上一些輔助方法屬性,最終形成如下抽象圖層類(CustomLayer),各種圖層均從它繼承:

?GIS地圖開發(fā)

FreeMicaps中,每種數(shù)據(jù)對應一種圖層類,為了使圖層類編寫方便,使用了設計模式中的模板方法,定義繪制流程,主程序在調用圖層的Render()方法時,會自動判斷是否已經讀入數(shù)據(jù),根據(jù)需要讀數(shù)據(jù)繪圖。

對于一種類型數(shù)據(jù),需要從CustomLayer繼承新建一個圖層類。各種類型數(shù)據(jù)圖層的工作方式完全一樣,僅在數(shù)據(jù)讀取和繪制方面不同,所以,寫新圖層類時,僅需實現(xiàn)DoLoad()和DoRender()兩個抽象方法,完成讀取數(shù)據(jù)和繪制圖層代碼即可。FreeMicaps里使用了字符串作為數(shù)據(jù)源標識,通用GIS系統(tǒng)對數(shù)據(jù)源進行了抽象,我也嘗試這么做,但代碼過于復雜,增加圖層開發(fā)難度,最終增大插件開發(fā)難度,所以放棄了。

前面說了,一張地圖有多個圖層,所以還需要將圖層放入一個列表,繪制地圖時遍歷圖層,調用每個圖層的Render()方法,畫出一張完整的地圖。對于圖層列表,大家馬上會想到使用List類,但圖層繪制是需要有順序的,如在衛(wèi)星云圖上面疊加地名,需要先畫衛(wèi)星云圖,再填地名,否則云圖會把地名蓋住,所以在圖層的樣式(LayerStyle)中放了一個ZOrder屬性,通過它來控制圖層順序。但由于List本身的排序方法是一種“非穩(wěn)固排序”,也就是說當兩個圖層的ZOrder相等時,它們的順序是不確定的,為了避免這個問題,F(xiàn)reeMicaps從CollectionBase繼承了一個類LayerList,實現(xiàn)對圖層的管理,并實現(xiàn)了IXmlSerializable接口,完成圖層序列化功能。另外,還增加了添加圖層、刪除圖層事件。LayerList類如下:

FreeMicaps中,每種數(shù)據(jù)對應一種圖層類,為了使圖層類編寫方便,使用了設計模式中的模板方法,定義繪制流程,主程序在調用圖層的Render()方法時,會自動判斷是否已經讀入數(shù)據(jù),根據(jù)需要讀數(shù)據(jù)繪圖。

對于一種類型數(shù)據(jù),需要從CustomLayer繼承新建一個圖層類。各種類型數(shù)據(jù)圖層的工作方式完全一樣,僅在數(shù)據(jù)讀取和繪制方面不同,所以,寫新圖層類時,僅需實現(xiàn)DoLoad()和DoRender()兩個抽象方法,完成讀取數(shù)據(jù)和繪制圖層代碼即可。FreeMicaps里使用了字符串作為數(shù)據(jù)源標識,通用GIS系統(tǒng)對數(shù)據(jù)源進行了抽象,我也嘗試這么做,但代碼過于復雜,增加圖層開發(fā)難度,最終增大插件開發(fā)難度,所以放棄了。

前面說了,一張地圖有多個圖層,所以還需要將圖層放入一個列表,繪制地圖時遍歷圖層,調用每個圖層的Render()方法,畫出一張完整的地圖。對于圖層列表,大家馬上會想到使用List類,但圖層繪制是需要有順序的,如在衛(wèi)星云圖上面疊加地名,需要先畫衛(wèi)星云圖,再填地名,否則云圖會把地名蓋住,所以在圖層的樣式(LayerStyle)中放了一個ZOrder屬性,通過它來控制圖層順序。但由于List本身的排序方法是一種“非穩(wěn)固排序”,也就是說當兩個圖層的ZOrder相等時,它們的順序是不確定的,為了避免這個問題,F(xiàn)reeMicaps從CollectionBase繼承了一個類LayerList,實現(xiàn)對圖層的管理,并實現(xiàn)了IXmlSerializable接口,完成圖層序列化功能。另外,還增加了添加圖層、刪除圖層事件。?

五、 封裝地圖

有了坐標轉換類、圖層類、圖層列表類,就可以利用它們做出一個具有縮放平移、圖層管理等功能的地圖了,但為了更方便地對地圖進行操作,還需要對這些類進行組合封裝。新建一個類WeatherMap,添加Coordinator和LayerList類的實例作為它的屬性,為了更符合大家操作習慣,將Coordinator類的實例作為私有成員,將地圖坐標轉換等方法加入WeatherMap類,也就是說地圖坐標轉換中,不訪問Coordinator,而要調用WeatherMap類的方法。類圖如下:

?GIS地圖開發(fā)

再回到抽象圖層類CustomLayer,它有一個成員Map,即為WeatherMap對象,在將圖層加入圖層列表時會自動賦值。在編寫CustomLayer的子類時,可調用它來進行坐標轉換和地圖操作。

為了使地圖在繪制復雜圖形過程中不至于假死,并在繪圖過程中能隨時中斷繪圖,如快速縮放平移地圖中可終止前次繪圖過程直接繪制最后一次,地圖繪制使用了多線程,但多線程增加了代碼編寫難度,特別是多線程操作UI,對程序流程造成了一定混亂,程序結構受到影響,所幸并不會對圖層代碼造成困難。

六、 再次封裝-增加UI

上面已完成了地圖繪制的核心代碼,為了使代碼編寫更加容易,需要對WeatherMap類再次進行封裝(MapView類),加入UI部分,即給地圖加一個具有界面的殼,并在上面實現(xiàn)地圖的操作如縮放、拖動功能。

MapView從PictureBox類繼承,內建了WeatherMap類的實例,在MapView的Refresh()方法中調用WeatherMap.Render()對地圖進行繪制。

為了完成對地圖的操作,F(xiàn)reeMicaps定義一個IMapTool接口,包含了鼠標和鍵盤操作方法,MapView類內建一個IMapTool接口成員,MapView的鼠標和鍵盤操作,將被IMapTool接口的實例接管,在實現(xiàn)IMapTool接口的類中,可對地圖做各種操作,如平移、縮放等操作,這個對象可隨時替換以實現(xiàn)不同方式的地圖操作。在FreeMicaps中,已完成一個實現(xiàn)IMapTool接口的類ZoomTool,此類為默認的地圖縮放和平移工具。IMapTool接口類圖如下:

?GIS地圖開發(fā)

另外,在MapView中,還引入了一個當前圖層的概念CurrentLayer,用它來表示當前操作的圖層,后面用它來實現(xiàn)圖層元素拾取、圖層工具條等功能。

MapView類圖如下:

?GIS地圖開發(fā)

七、 總覽

地圖部分類關系圖如下:

?GIS地圖開發(fā)

地圖繪制部分活動圖如下:

?GIS地圖開發(fā)

以上已經介紹完FreeMicaps地圖部分設計框架,相信大家的已對設計思路已有一定了解,此框架不僅適用于天氣圖分析軟件,也適用于一般的GIS系統(tǒng)。本文僅對FreeMicaps的地圖部分框架進行了介紹,未涉及到具體的地圖數(shù)據(jù)讀取及繪制,這些將在下一篇文章中介紹。

上一頁12全文

非常好我支持^.^

(0) 0%

不好我反對

(0) 0%

( 發(fā)表人:彭菁 )

      發(fā)表評論

      用戶評論
      評價:好評中評差評

      發(fā)表評論,獲取積分! 請遵守相關規(guī)定!

      ?