(一)項目介紹
網址:http://www.openlayers.org/
OpenLayers 是由MetaCarta公司開發的,用於WebGIS用戶端的JavaScript包,目前的最高版本是2.5 V,通過BSD License 發行。它實現訪問地理空間資料的方法都符合行業標準,比如OpenGIS的WMS和WFS規範, OpenLayers採用純物件導向的JavaScript方式開發,同時借用了Prototype架構和Rico庫的一些組件。
採用OpenLayers作為用戶端不存在瀏覽器依賴性。由於OpenLayers採用JavaScript語言實現,而應用於Web瀏覽器中的DOM(文件物件模型)由JavaScript實現,同時,Web瀏覽器(比如IE,FF等)都支援DOM 。
OpenLayers APIs採用動態類型指令碼語言JavaScript編寫,實現了類似與Ajax功能的無重新整理更新頁面,能夠帶給使用者豐富的桌面體驗(它本身就有一個Ajax類,用於實現Ajax功能)。
目前,OpenLayers所能夠支援的Format有:XML、GML、GeoJSON、GeoRSS、JSON、KML、WFS、WKT(Well-Known Text)。在OPenlayers.Format名稱空間下的各個類裡,實現了具體讀/寫這些Format的解析器。
OpenLayers所能夠利用的地圖資料資源“豐富多彩”,在這方面提供給擁護較多的選擇,比如WMS、WFS、GoogleMap、KaMap、MSVirtualEarth、WorldWind等等。當然,也可以用簡單的圖片作為源。
第一次使用OpenLayers:
先到它的官方網站http://www.openlayers.org下載他的壓縮包,解壓後可以看到其中的一些目錄和檔案,拷貝目錄下的OpenLayer.js、根目錄下的lib目錄、根目錄下的img目錄到你網站的Scripts目錄下(當然,這個只是例子,您網站的目錄結構您自己說得算,只要保證OpenLayers.js,/lib,/img在同一目錄中即可)。 然後,建立一個index.html作為查看地圖的頁面,匯入OpenLayers.js和你將要建立的js。
我們以載入WMS和GML檔案為例。
<script src="../lib/OpenLayers.js"></script>
<script type="text/javascript">
var lon = 5; //x-axis coodinate in map units
var lat = 40; //y-axis coordinate in map units
var zoom = 5; //number of zoom levels
var map, layer;
//聲明變數map、layer;等同於 var map = null; var layer = null;
map = new OpenLayers.Map('map');
//執行個體化一個地圖類OpenLayers.Map
layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
//以WMS的格式執行個體化圖層類OpenLayers.Layer
map.addLayer(layer);
map.zoomToExtent(newOpenLayers.Bounds(-3.922119,44.335327,
4.866943,49.553833));
//在Map對象上載入Layer對象,並用map.zoomToExtent函數使地圖合適地顯示
map.addLayer(new OpenLayers.Layer.GML("GML", "gml/polygon.xml"));
//再在剛載入的WMS檔案上,載入一GML檔案
剩下的工作就是,加上一些控制項OpenLayers.Control之類的東西,比如LayerSwitcher等。它們會在地圖瀏覽的“視窗”上增加一些工具列或是“按鈕”,增加互動性和功能性。
當然,Openlayers中的東西遠不止這些,至於它的架構分析、APIs實現機制,會在後續文章中說出。寫這個的過程,也是一個學習的過程,其中難免有不妥之處,熱烈歡迎大家批評指正,相互交流。
(二)原始碼總體結構分析
通過前面的項目介紹,我們大概已經知道Openlayers是什麼,能夠做什麼,有什麼意義。接下來我們分析它怎麼樣,以及怎樣實現的等問題。
這個圖是從它的文檔上截取的,旨在從感官上認識一下OpenLayers的類。下面分別介紹(文檔中的類是按字母順序排列的,也按這個順序說吧):
我們看到在類的頂層“高高在上”的是OpenLayers,它為整個項目實現提供名稱空間(JavaScript語言沒有名稱空間一說,但是它確實有自己的機制實作類別似的功能,後面會說明),它直接擁有一常量 VERSION_NUMBER,以標識版本。
Ajax:顧名思義,用於實現Ajax功能,只是OpenLayers的開發者們把它單獨寫到一個類裡了,其中用到了Prototype.js架構裡的一些東西。同時,設計的時候也考慮了跨瀏覽器的問題。
BaseTypes:這裡定製了OpenLayers中用到的string,number 和 function。比如,OpenLayers. String. startsWith,用於測試一個字串是否一以另一個字串開頭;OpenLayers. Number. limitSigDigs,用於限制整數的有效數位;OpenLayers. Function.bind,用於把某一函數綁定於對象等等。
Console:OpenLayers.Console,此名稱空間用於調試和把錯誤等輸出到“控制台”上,需要結合使用../Firebug/firebug.js。
Control:我們通常所說的控制項類,它提供各種各樣的控制項,比如上節中說的圖層開關LayerSwitcher,編輯工具條EditingToolbar等等。載入控制項的例子:
class = new OpenLayers.Map('map', { controls: [] });
map.addControl(new OpenLayers.Control.PanZoomBar());
map.addControl(new OpenLayers.Control.MouseToolbar());
Events:用於實現OpenLayers的事件機制。具體來說,OpenLayers中的事件分為兩種,一種是瀏覽器事件,例如mouseup,mousedown之類的;另外一種是自訂的,如addLayer之類的。OpenLayers中的事件機制是非常值得我們學習的,後面將具體討論。
Feature:我們知道:Feature是geography 和attributes的集合。在OpenLayers中,特別地OpenLayers.Feature 類由一個marker 和一個lonla組成。
OpenLayers. Feature.WFS與OpenLayers. Feature. Vector繼承於它。
Format:此類用於讀/寫各種格式的資料,它的子類都分別建立了各個格式的解析器。這些格式有:XML、GML、GeoJSON、GeoRSS、JSON、KML、WFS、WKT(Well-Known Text)。
Geometry:怎麼翻譯呢,幾何?是對地理對象的描述。它的子類有Collection、Curve、LinearRing、LineString、MultiLineString、MultiPoint、MultiPolygon、Point、Polygon、Rectangle、Surface,正是這些類的執行個體,構成了我們看到的地圖。需要說明的是,Surface 類暫時還沒有實現。
Handler:這個類用於處理序列事件,可被啟用和取消。同時,它也有命名類似於瀏覽器事件的方法。當一個handler 被啟用,處理事件的方法就會被註冊到瀏覽器監聽器listener ,以響應相應的事件;當一個handler被取消,這些方法在事件監聽器中也會相應的被取消註冊。Handler通過控制項control被建立,而control通過icon表現。
Icon:在電腦螢幕上以表徵圖的形式呈現,有url、尺寸size和位置position3個屬性。一般情況,它與 OpenLayers.Marker結合應用,表現為一個Marker。
Layer:圖層。
Map:網業中動態地圖。它就像容器,可向裡面添加圖層Layer和控制項Control。實際上,單個Map是毫無意義的,正是Layer和Control成就了它。
Marker:它的執行個體是OpenLayers.LonLat 和OpenLayers.Icon的集合。通俗一點兒說,Icon附上一定的經緯度就是Marker。
它們的組合關係是:
Popup:地圖上一個小巧的層,實現地圖“開關”功能。使用例子:
Class = new OpenLayers.Popup("chicken",
new OpenLayers.LonLat(5,40),
new OpenLayers.Size(200,200),
"example popup",
true);
map.addPopup(popup);
Renderer:渲染類。在OpenLayers中,渲染功能是作為向量圖層的一個屬性存在的,我們稱之為渲染器,向量圖層就是通過這個渲染器提供的方法將向量資料顯示出來。以SVG和VML為例,繼承關係是這樣的:
至於OpenLayers. Renderer. Elements為什麼要存在,以及它的渲染機制,後面會說。
Tile:設計這個類用於指明單個“瓦片”Tile,或者更小的解析度。Tiles儲存它們自身的資訊,比如url和size等。它的類繼承關係如下:
Util:“跑龍套”的類。
寫到這裡,可以看到OpenLayers 的類纏繞的挺麻煩的,接下來的文章將從代碼部分分析更細部的東西。
(三)BaseTypes :定義底層類與定製JS內建類
先說基底類型BaseTypes下,OpenLyers構建的“自己”的類。它們分別是:OpenLayers. LonLat、OpenLayers. Pixel、OpenLayers.Size、OpenLayers. Element、OpenLayers. Bounds和OpenLayers. Class。下面分別介紹:
OpenLayers. LonLat:經緯度類,其執行個體為地圖提供一經度、緯度對,即位置。有兩個屬性lon(x-axis coodinate )和lat(y-axis coordinate )。這裡說明一下,怎麼經緯度又與x軸座標、y軸座標糾纏在一起?是這樣:當地圖是在地理座標投影下,它就是經緯度;不然就是地圖上的x/y軸座標。除建構函式外,實現了五個函數:
toShortString:function() 把座標轉換為字串;
clone:function() 複製一個LonLat對象;
Add:function(lon,lat) 改變現有地圖的位置;
return new OpenLayers.LonLat(this.lon + lon, this.lat + lat);
equals:function(ll) 判斷傳入的lon,lat對是否與當前的相等;
wrapDateLine:function(maxExtent) 複製下(lon,lat),指定為邊界的最大範圍。
OpenLayers. Pixel:像素類,在顯示器上以(x,y)座標的的形式呈現像素位置。有兩個屬性x座標、y座標,提供四個成員函數:
clone:function() 拷貝像素;
equals:function(px) 判斷兩像素是否相等;
add:function(x,y) 改變(x,y)使其成為新像素;
return new OpenLayers.Pixel(this.x + x, this.y + y);
offset:function(px) 調用add()使像素位置發生位移。
newPx = this.add(px.x, px.y);
OpenLayers.Size:也有兩個屬性,寬度width、高度height。實現了兩個成員函數:clone:function()和equals:function(sz)不多說了。
OpenLayers. Element:在這個名稱空間下,開發者寫了好多API,有visible、toggle、hide、show、remove、getHeight、getDimensions和getStyle,以實現元素的顯示、隱藏、刪除、取得高度,取得範圍等功能。以getHeight函數為例我們看看它的代碼:
/**
* APIFunction: getHeight
*
* Parameters:
* element - {DOMElement}
*
* Returns:
* {Integer} The offset height of the element passed in
*/
getHeight: function(element) {
element = OpenLayers.Util.getElement(element);
return element.offsetHeight;
}
這裡涉及到文件物件模型DOM的一些東西,函數本身很簡單,最後返回元素的高度。
OpenLayers. Bounds:在這個類中,資料以四個浮點型數left, bottom, right, top 的格式儲存,它是一個像盒子一樣的範圍。它實現了三個描述一個Bound的函數:toString、toArray和toBBOX。其中,toString的代碼如下:
/**
* APIMethod: toString
*
* Returns:
* {String} String representation of bounds object.
* (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
*/
toString:function() {
return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+ " right-top=(" + this.right + "," + this.top + ")" );
}
結果類似於"left-bottom=(5,42) right-top=(10,45)"
三個Bound資料來源函數:fromString、fromArray和fromSize;
五個擷取對象屬性的函數:getWidth、getHeight、getSize、getCenterPixel、getCenterLonLat;
餘下還有:add:function(x,y),extend:function(object),containsLonLat,containsPixel,contains,intersectsBounds,containsBounds,determineQuadrant,wrapDateLine。以函數extend為例,看看源碼。
extend:function(object) {
var bounds = null;
if (object) {
switch(object.CLASS_NAME) {
case "OpenLayers.LonLat":
bounds = new OpenLayers.Bounds (object.lon, object.lat, object.lon, object.lat);
break;
case "OpenLayers.Geometry.Point":
bounds = new OpenLayers.Bounds(object.x, object.y,object.x, object.y);
break;
case "OpenLayers.Bounds":
bounds = object;
break;
}
if (bounds) {
if ( (this.left == null) || (bounds.left < thi s.left)) {
this.left = bounds.left;}
if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) {
this.bottom = bounds.bottom;}
if ( (this.right == null) || (bounds.right > t his.right) ) {
this.right = bounds.right;}
if ( (this.top == null) || (bounds.top > this. top) ) {
this.top = bounds.top;}
}
}
}
可以看出,對Bounds的擴充可以有三種形式:point, lonlat, 或者bounds,計算的條件是零座標是在螢幕的左上方。
OpenLayers. Class:這個類是OpenLayers 中的“大紅人”,只要建立其他類就得用它,同時也實現了多重繼承。用法如下:
單繼承建立:class = OpenLayers.Class(prototype);
多繼承建立:class = OpenLayers.Class(Class1, Class2, prototype);
淨說底層類了,對js內建類的擴充下回寫