openlayers with google maps 教程

what is openlayer

openlayers是一個高性能、功能全面的地圖庫。有以下特性:

  1. 支持各種格式的tiled layers數據
  2. 使用canvas的高性能vector layer
  3. 細粒度豐富的交互操作
  4. 支持commanjs風格
  5. 開源免費

why use it

一般來說google map api就滿足需求了,但是當需要在地圖上繪制大量節點時,使用svg來顯示部件就會出現很嚴重的性能問題,一般情況下google maps等的編輯操作也不夠流暢。而openlayers使用canvas作為矢量層,大量的點線面信息對于canvas的影響是較小的,而且canvas的交互也更加流暢。

why write this article

  1. 與高德地圖、google maps、百度地圖等國內常用地圖沒有很方便的接口
  2. 大版本更新后官方文檔不全面而且不夠清晰,沒有guild
  3. 而且網上簡明詳細的教程很少

針對以上幾點,覺得自己的小經驗會對大家有所幫助,可以少走些彎路,也是對自己這幾天工作的一個總結。

a simple example(step by step)

下面通過一個簡單地小例子介紹openlayers的一些基本用法,以及我覺得重要的地方。

需求

需要使用google map或者高德地圖作為底圖,在地圖上繪制一個矩形,實現矩形的編輯并且能夠得到矩形被修改之后的回調函數。大概會涉及到這些技術點:

  1. 如何與google map等地圖組合使用
  2. 知道坐標數組或者某格式的geo數據,如何在地圖上添加feature
  3. 完成feature的修改
  4. 得到feature修改后的回調方法

附上簡單截圖

title

初始化地圖

我們希望openlayer能和google map等組合使用,但是由于google等地圖廠商不愿意向openlayer“妥協”,因此現在無法直接使用Tiled Layers,但我們可以將openlayer與地圖api組合起來使用。

只使用地圖的底圖,在底圖上覆蓋一層 openlayer 的 canvas 層來顯示數據并攔截與底圖之間的交互,通過地圖的 api 來重設狀態。
此方案還有以下優點:

  1. 解決了頁面節點過多之后的性能問題
  2. 底圖可以被抽象出去,能夠在不影響交互邏輯的基礎上更換底圖。

代碼如下:

<!DOCTYPE html>
<html>
<head>
  <title>Snap interaction example</title>
  <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
  <link rel="stylesheet" >
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
  <link rel="stylesheet"  type="text/css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.js"></script>
  <script src="https://maps.googleapis.com/maps/api/js"></script>
  <style type="text/css">
    div.fill {
      width: 100%;
      height: 100%;
    }

    .map {
      width: 800px;
      height: 400px;
    }
  </style>
</head>
<body>
<div class="container-fluid">
  <div class="row-fluid">
    <div class="span12">
      <div id="map" class="map">
        <!-- gmap用于加載google maps, olmap用于加載openlayer canvas。
        目標是加載完畢之后olmap覆蓋與gmap之上并且攔截交互操作。
        開始時放在同一層的好處是都能根據父節點來設置長寬。也可以在js中動態生成div,渲染后插入 -->
        <div id="gmap" class="fill"></div>
        <div id="olmap" class="fill"></div>
      </div>
    </div>
  </div>
</div>
<script type="application/javascript">
  // 加載google map并禁用地圖的交互操作
  var gmap = new google.maps.Map(document.getElementById('gmap'), {
    disableDefaultUI: true,
    keyboardShortcuts: false,
    draggable: false,
    disableDoubleClickZoom: true,
    scrollwheel: false,
    streetViewControl: false
  });

  // ol.View 是openlayers用于控制地圖的 坐標系標準 zoom center rotate等操作的對象,在實例化map時候需要使用
  var view = new ol.View({
    // make sure the view doesn't go beyond the 22 zoom levels of Google Maps
    maxZoom: 21,
    projection: 'EPSG:4326' // 設置為標準經緯度的坐標標準,十分重要! 默認是'EPSG:3857'
  });

  // view 拖動時觸發事件,根據當前的坐標轉化為經緯度,調用谷歌地圖setCenter方法同步地圖位置
  view.on('change:center', function () {
    var center = view.getCenter();
    gmap.setCenter(new google.maps.LatLng(center[1], center[0])); // 注意順序
  });

  // 同上,更改焦距時觸發的時間
  view.on('change:resolution', function () {
    gmap.setZoom(view.getZoom());
  });

  // ol.source.Vector 作為 ol.layer.Vector的數據集,增刪改feature的方法由source提供
  var vectorSource = new ol.source.Vector();

  var vector = new ol.layer.Vector({
    source: vectorSource
  });

  var olMapDiv = document.getElementById('olmap');
  var map = new ol.Map({
    layers: [vector], // 所使用的圖層
    // 禁用掉默認的拖動、旋轉等交互
    interactions: ol.interaction.defaults({
      altShiftDragRotate: false,
      dragPan: false,
      rotate: false
    }).extend([new ol.interaction.DragPan({kinetic: null})]),
    target: olMapDiv,
    view: view  // 這里可以使用 new ol.View({options}) 但是在這里需要通過手動設置來觸發google maps調節到正確地zoom與center
  });
  view.setCenter([10.689697265625, -25.0927734375]); // 如果未設置view的坐標標準,這里千萬要注意不要直接寫經緯度
  view.setZoom(6);  // 設置縮放等級
  
  // 將openlayers容器放置到google地圖容器中
  olMapDiv.parentNode.removeChild(olMapDiv);
  gmap.controls[google.maps.ControlPosition.TOP_LEFT].push(olMapDiv);
</script>
</body>
</html>

有了這段代碼應該就有了一個可以拖動縮放的地圖了。

添加feature

當然光有一個地圖是不行的,如果需要往地圖上面添加feature怎么辦呢?大致有以下兩種場景:

  1. 提供整個地圖的信息描述數據,比如geoJson,WKT或者自定義的數據結構等,需要解析整個數據并批量顯示在地圖上。
  2. 拿到某個feature的坐標數據,需要添加到地圖上,并實現特異化的控制。

批量添加數據

先上代碼

/**
   * 將geoJson字符串解析后添加到地圖中
   * @param vectorSource {ol.source.Vector} 需要添加feature的矢量層數據對象
   * @param data {string} geoJson字符串
   */
  function addFeatures(vectorSource, data){
 vectorSource.addFeatures(ol.format.GeoJSON.readFeatures(data, {
      // 數據的坐標code
      dataProjection: 'EPSG:3857',
      // 地圖view使用的坐標code
      featureProjection: 'EPSG:4326'
    }));
  }

ol.format下有很多種數據類型,選擇匹配的數據格式。
比如WKT使用ol.format.WKT.readFeature

readFeature返回的是ol.Feature的數組,可以通過遍歷其來獲得feature對象,從而按需求修改或者掛載監聽事件。

添加單個feature

如果現有的數據是geoJson等標準格式的數據,可以通過ol.format中的類進行轉換。如果有需求則使用ol.proj.transform進行坐標系轉換。

// 返回單個feature對象
var feature = ol.format.GeoJSON.readFeature(data)

// 添加到source
vectorSource.addFeature(feature);

如果拿到的是經緯度信息,添加一個polygon

// data 是一個coordinates的二維數組 需要注意
var feature = new ol.feature(new ol.geom.Polygon(data));

修改feature

介紹如何修改feature以及掛載監聽事件。

初始化修改添加、修改交互

// 聲明選擇交互
 var select = new ol.interaction.Select({
      // 根據 feature editable 選項來判斷是否可以選中
      filter: function(feature) {
        if (_this._featureMap[feature.getId()].editable) {
          return true;
        }
      }
    });

// 得到被選中元件的對象
var selected = select.getFeatures();

// 聲明修改交互,可以修改被選中的feature
var modify = new ol.interaction.Modify({
  features: selected
});

// 當新元件被選中時觸發
selected.on('add', event => {
  var feature = event.element
})

// 當元件被取消選中時觸發,一般把元件的修改回調放在這
    selected.on('remove', evt => {
      var feature = evt.element;
      var fid = feature.getId();
      // 判斷元件是否被修改還是需要feature的change事件
      console.log(fid);
    });

// 在interactions中添加
this._map = new ol.Map({
      layers: [vector],
      interactions: ol.interaction.defaults({
        altShiftDragRotate: false,
        dragPan: false,
        rotate: false
      }).extend([new ol.interaction.DragPan({kinetic: null}), select, modify]),
      target: $olMapDiv,
      view: this._view
    });
    

一般來說如果需要后續對feature進行操作,可以使用getId方法拿到featureid,可以通過setId來設置自己想要的id,否則會自動生成。 將id存在常駐的對象中供以后使用。

假設拿到ol.Feature對象 feature

feature.on('change:geometry', function(e){
  var feature = e.element;
  // do what you want  比如標記元件已被修改
})

需要注意的是這個onChange事件在修改的過程中會不斷地觸發,如果需要的是修改完成之后的回調,需要使用selectremove事件。

select.getFeatures().on('remove', function(e){})

修改feature對象

設置id

feature.setId(id)

得到geometry對象

var geometry = feature.getGeometry();
// 通過調用geometry類的方法修改元件坐標

feature to string

var format = new ol.format.GeoJSON();
format.writeFeature(feature);

需要注意的地方

  • openlayers默認的坐標系是'EPSG:3857',標準經緯度坐標系是'EPSG:4326'
  • 看openlayer文檔最重要的技巧是注意類型
  • geometry接受的coordinates其實是一個三維數組,一定要注意

常用操作

坐標系轉換

根據當前坐標系與目標坐標系進行轉換。

ol.proj.transform(coordinate, source, destination)
coordinate 在文檔中得類型是 Coordinate其實就是一個有橫縱坐標組成的數組,因此一定要注意官方文檔中得數據類型。
source 當前坐標編碼 string類型
destination 目標坐標編碼 string類型

從經緯度轉化到指定坐標系

ol.proj.fromLonLat(coordinate, opt_projection)
opt_projection 目標坐標編碼 string類型

從某坐標轉經緯度

ol.proj.toLonLat(coordinate, opt_projection)

數據格式化

ol.format

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容