基于OpenLayers的Web GIS示例

jopen 10年前發布 | 241K 次閱讀 地理信息系統GIS OpenLayers

GIS(Geographic Information System) 在可視化方面,無疑是有很多優勢的,相比于傳統的表格或者圖表,它更容易將讀者快速的帶入到具體場景中,并更好的理解圖形背后的含義。

很多信息事實上都和地理位置有關,比如關鍵客戶的分布圖,貨源/倉庫在地理上的分布等,當這些信息以可視化的方式展現在地圖上時,我們可以獲得很多之前無法看到的信息,而這些信息可以幫助我們在未來做出更合理的決策。

本文將使用開源工具OpenLayers做一個實例,并在這個過程中詳細討論GIS背后的一些技術細節。在這個例子中,我們將會把地球上的地震統計信息用可視化的方式,直觀的展現在地圖上。

例子的最終運行結果如下:

f1.png

圖中的綠色點表示小于里氏3級的地震,紅色的點表示大于里氏3級的地震。

Web GIS簡介

Web GIS是將傳統的GIS與Web集成起來,借助于Web的高可用性,Web GIS可以使地圖服務本身更容易被人們獲得。成熟的Web GIS產品已經有很多,比如Google Maps,Bing Maps等,這些GIS產品已經很早就深入了我們的日常生活,并為我們的學習工作帶來了眾多的便利,比如去一個陌生的城市出差時找到預定的酒店,查找離你目前所處位置最近的銀行等等。

那么我們看到的網頁上很漂亮的地圖是怎么產生的呢?它們又是如何被渲染到頁面上的呢?

地圖的渲染方式

幾乎所有我們看到的基于Web的地圖,都是通過不同層次疊加出來的結果。GIS系統會將 河流,建筑物,街道,文本標簽的信息分別存儲與不同的層次上(物理上不同的文件/數據庫中),這樣做不但可以在編輯地圖時更容易管理(比如表示街道的圖層的更新頻率肯定會高于表示河流的圖層的更新頻率 ),而且可以按需加載(只查看河流,街道,不要建筑物信息等),從而減少服務器上的負載。

地圖服務器將不同的層次疊加,最終生成一張完整的地圖。這個最終的地圖包含了所有的信息,比如河流,建筑物等都會帶上名稱。而且每個層次都可以獨立的配色,這樣最終的地圖就是我們見到的樣子了:藍色的水域,黃色的高速路,白色的建筑,綠色的公園等等。

f2.png

圖片來源(http://www.srh.noaa.gov/bmx/?n=gis)

地圖在服務器端被渲染出來之后,尺寸一般會非常大。需要由專門的模塊將這些大圖切分成很多組的小圖,這些小圖被稱之為瓦片(tile)。為了給不同縮放級別的客戶端提供不同的圖片,這些瓦片被精心的分成了多個組,每個組都有編號。如果地圖支持18級的縮放,就會現有18個分組。當然分組好越靠后,分組中的瓦片越多。

f3.png

服務器上的瓦片

而在客戶端,當我們在Google Maps上拖動地圖,或者放大某一個感興趣的區域時,往往會看到一些灰色的塊。這些灰色的塊過幾秒會被真實的地圖替換掉。這是因為,我們不可能也不需要將 GIS服務器上的地圖完全加載到客戶端呈現,而是分批獲取。在獲取地圖瓦片的過程中,客戶端會展現一些占位符,當真正的瓦片加載完成后,再完成替換。

比如當我們查看西安市地圖時,前端的JavaScript腳本會根據縮放級別和目前瀏覽的區域發送少量的請求獲取地圖瓦片,然后再將這些瓦片拼成一副“完整”的地圖。

WMS請求

WMS(Web Map Service)是一個基于HTTP的簡單協議,客戶端發送的請求中包含請求類型,地圖的層次,邊界等信息,服務器根據這個信息生成圖片,并返回該圖片:

f4.png

Chrome中的一次WMS請求詳情

當然,WMS本身支持多種類型的請求,最常見的就是GetMap。具體的細節大家可以參考OGC規范及具體服務器的實現。而對于后端的服務器來說,從請求中獲取這些信息之后,會首先從數據庫/數據文件中得到數據,并使用渲染引擎繪制圖片,并最后將圖片返回客戶端。

地圖圖層的分類

圖層可以分為矢量圖層和柵格圖層,柵格圖片一般的來源為航拍,遙感等技術,本質上來說就是照片,不過由于拍攝角度,器材本身產生的失真等,這些照片需要經過校正才能使用。由于柵格圖片本身是拍攝的結果,那么放到到一定范圍之后,必然會產生模糊。柵格圖片通常會附帶一些具有特別含義的屬性,比如降水量,人口數等。

f5.png

圖片來源(http://earthobservatory.nasa.gov/Features/FalseColor/page3.php)

而矢量圖層是數學上的抽象,矢量圖層只需要記錄在某個特定的坐標系中,各個點,線,面的關系即可,它可以任意縮放而不至于失真。同樣,矢量圖層中的元素也可以附加一些屬性。

f6.png

OpenLayers簡介

OpenLayers是一個開源的JavaScript庫,主要用于將不同來源的地圖圖層集成在一起,從而產生有價值的應用程序。一個最簡單的場景是,底圖使用Google Maps提供的衛星圖層,而在這個圖層之上加入與業務相關的矢量層,幫助讀者快速的理解圖層背后的含義。

OpenLayers支持的數據格式

OpenLayers支持很多種的數據格式,如GML,GeoJSON,GPX等等,大部分的GIS服務器都支持生成這樣的數據格式以供展現。

其中,GML,GPX,Atom等都是基于XML的文檔,而GeoJSON則是基于JSON格式的。我們的這個例子主要關注GeoJSON格式,它相對而言更小巧,更加可讀。

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [125.6, 10.1]
  },
  "properties": {
    "name": "Dinagat Islands"
  }
}

一個GeoJSON中的節點包含三部分的信息:類型,幾何要素和屬性集。幾何要素可以是點,線,面或者多點,多線,多面。而屬性則可以包含任意復雜的信息,比如一個表示建筑物的多邊形上的可以包含諸如綠化面積,降雨量,植物種類等信息。

數據來源

美國地理信息調查局是一個科學組織,他公開了很多地球上的災難信息,比如對地震的統計,并提供編程接口。它公開的地震統計信息,包含全世界各地報告過的地震,以及全美所有檢測到的地震,并以多種周期(小時,天,周,月等),多種格式(GeoJSON,KML,Atom等)公開,以便應用程序的開發者只用這些數據。

我們的這個例子中,將使用USGS提供的GeoJSON格式的數據,并通過OpenLayers的API將這些數據展現在地圖上。

逐步實現

設置基本環境

我們將借助bower來安裝所有的代碼依賴。首先,我們需要bower將所有的包都安裝在components目錄下,這個可以通過在當前目錄的.bowerrc文件中指定directory:

{
    "directory": "components"
}

然后運行bower安裝jquery以及openlayers:

$ bower install jquery
$ bower install openlayers

通過bower安裝OpenLayers之后,可以通過OpenLayers自帶的build工具將所有的源碼合并壓縮為一個文件:

$ cd components/openlayers/build
$ ./build.py #將會在當前目錄下生成一個OpenLayers.js的文件
$ mv OpenLayers.js ../

然后,創建一個簡單的HTML文件,引用jquery.js和OpenLayers.js,以及我們的入口腳本app.js,本文所有的代碼都只是修改這個文件。

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <title>Earthquake distribution</title>
        <link rel="stylesheet" href="style.css" />
    </head>
    <body>
        <div id="container">
            <div id="map">
            </div>
        </div>
        <script src="components/jquery/jquery.js" type="text/javascript"></script>
        <script src="components/openlayers/OpenLayers.js" type="text/javascript"></script>
        <script src="app.js" type="text/javascript"></script>
    </body>
</html>

基本代碼

一個最簡單的OpenLayers應用,只需要7行代碼:

$(function() {
    var map = new OpenLayers.Map("map");
    var osm = new OpenLayers.Layer.OSM();

    map.addLayers([osm]);
    map.zoomToMaxExtent();
});

這段代碼在id為map的HTML元素創建了一個地圖,這個地圖上有一個叫OSM的層(即OpenStreetMap,一個開源,類似于維基百科的地圖平臺),并將地圖縮小到邊界范圍(以獲得最大的視野):

f7.png

生成矢量圖層

使用OpenLayers來生成矢量圖非常容易:

var geo = new OpenLayers.Layer.Vector("EarthQuake", {
    strategies: [new OpenLayers.Strategy.Fixed()],
    protocol: new OpenLayers.Protocol.HTTP({
        url: '/all_day.geojson',
        format: new OpenLayers.Format.GeoJSON({ignoreExtraDims: true})
    })
});

注意此處的all_day.geojson是從USGS網站上下載的,過去一天中世界各地的所有地震統計。

上邊的代碼創建了一個名稱為EarthQuake的矢量層,strategies中的Fixed策略表示僅請求一次資源,然后緩存在前端,不再請求。protocol表明數據來源為all_day.geojson,格式為OpenLayers.Format.GeoJSON。由于USGS返回的地理信息除了經緯度還包含深度,而OpenLayers默認只處理經緯度的,因此需要此處的ignoreExtraDims來忽略那個額外的深度信息。

f8.png

定制樣式

雖然我們已經加上了新的層,也可以看到很多表示地震的點信息,但是并不能看出哪些地震是嚴重的,比如里氏3級以下的地震,幾乎沒有危害,可以標注成一種顏色;而更高震級的可以標記成另外一種顏色。

OpenLayers可以很容易的做到這個定制化:

var style = new OpenLayers.Style();

var ruleLow = new OpenLayers.Rule({
  filter: new OpenLayers.Filter.Function({
        evaluate: function(properties) {
            return properties.mag < 3.0;
        }
    }),
  symbolizer: {pointRadius: 3, fillColor: "green",
               fillOpacity: 0.5, strokeColor: "black"}
});

var ruleHigh = new OpenLayers.Rule({
  filter: new OpenLayers.Filter.Function({
        evaluate: function(properties) {
            return properties.mag >= 3.0;
        }
    }),
    symbolizer: {pointRadius: 5, fillColor: "red",
               fillOpacity: 0.7, strokeColor: "black"}
});

style.addRules([ruleLow, ruleHigh]);

geo.styleMap = new OpenLayers.StyleMap(style);

首先創建一個Style對象,為Style添加兩條規則Rule,然后將Style對象包裝成StyleMap并賦值給表示地震的矢量層earthquake。

對于規則ruleLow,我們定義了,當一個feature的屬性值mag(震級)小于三的時候后,使用綠色的,半徑為3px的小圓圈來表示。而ruleHigh則定義了當震級大于等于三的時候,用紅色,半徑為5px的圓圈來表示。

f9.png

更高級的樣式規則

OpenLayers有完善的樣式規則模型,比如我們需要按等級生成不同的顏色標記:在里氏1到3級為綠色,3級到5級為黃色,大于5級為紅色:

var rules = [
        new OpenLayers.Rule({
            filter: new OpenLayers.Filter.Function({
                evaluate: function(properties) {
                    return properties.mag < 3.0;
                }
            }),
            symbolizer: {
            pointRadius: 3, fillColor: "green",
            fillOpacity: 0.5, strokeColor: "black"
            }
        }),  
        new OpenLayers.Rule({
            filter: new OpenLayers.Filter.Function({
                evaluate: function(properties) {
                    return properties.mag >= 3.0 && properties.mag < 5.0;
                }
            }),
            symbolizer: {
            pointRadius: 5, fillColor: "orange",
            fillOpacity: 0.5, strokeColor: "black"
            }
        }),  
        new OpenLayers.Rule({
            filter: new OpenLayers.Filter.Function({
                evaluate: function(properties) {
                    return properties.mag >= 5.0;
                }
            }),
            symbolizer: {
            pointRadius: 7, fillColor: "red",
            fillOpacity: 0.5, strokeColor: "black"
            }
        }),
    ];

f10.png

事件處理

雖然我們已經可以直觀的根據震級不同而看到不同顏色的點,但是整個應用仍然沒有多少意義:它不具備于用戶的交互能力。我們需要添加上事件處理,當用戶點擊地圖上的一個圓點的時候,應該看到一個更詳細的窗口。

var selectControl = new OpenLayers.Control.SelectFeature(geo, {
    onSelect: onFeatureSelect,
    onUnselect: onFeatureUnselect
});

map.addControl(selectControl);
selectControl.activate();

function onFeatureSelect(feature) {
    var html = "<span>"+feature.attributes.title+"</span>";

    var popup = new OpenLayers.Popup.FramedCloud("popup",
            feature.geometry.getBounds().getCenterLonLat(),
            null,
            html,
            null,
            true
        );

    popup.panMapIfOutOfView = true;
    popup.autoSize = true;

    feature.popup = popup;

    map.addPopup(popup);
}

function onFeatureUnselect(feature) {
    map.removePopup(feature.popup);
    feature.popup.destroy();
    feature.popup = null;
}

我們在地圖上添加了一個SelectFeature控件,并注冊了回調函數:當矢量層中的矢量被選中之后,函數onFeatureSelect將被執行。我們可以在這個函數中添加對彈出窗口的控制。當onFeatureSelect執行時,OpenLayers會將當前的Feature傳遞進來,我們可以動態的取得震級,標題,鏈接等信息,并展現給最終用戶。

f11.png

完整的代碼示例可以看這里。通過這個例子,我們知道了如何使用OpenLayers展現既有的數據源生成矢量圖層,并了解了如何為這些圖層應用不同的樣式。最后,我們了解了如何為矢量圖層中的特征注冊事件處理器。

毫無疑問,我們最終的地圖應用中對數據的展現,是其他傳統展現方式無法比擬的。通過使用OpenLayers和公開的數據源,我們可以很容易的搭建起一些很有用的可視化應用,這些應用也在很多方面使得我們的生活更加便利。

來自:http://www.infoq.com/cn/articles/visualization-of-the-global-seismic-system

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!