openlayers with google maps 教程

what is openlayer

openlayers是一個(gè)高性能、功能全面的地圖庫辅辩。有以下特性:

  1. 支持各種格式的tiled layers數(shù)據(jù)
  2. 使用canvas的高性能vector layer
  3. 細(xì)粒度豐富的交互操作
  4. 支持commanjs風(fēng)格
  5. 開源免費(fèi)

why use it

一般來說google map api就滿足需求了演顾,但是當(dāng)需要在地圖上繪制大量節(jié)點(diǎn)時(shí)重抖,使用svg來顯示部件就會(huì)出現(xiàn)很嚴(yán)重的性能問題楚午,一般情況下google maps等的編輯操作也不夠流暢宴偿。而openlayers使用canvas作為矢量層湘捎,大量的點(diǎn)線面信息對(duì)于canvas的影響是較小的,而且canvas的交互也更加流暢窄刘。

why write this article

  1. 與高德地圖窥妇、google maps、百度地圖等國(guó)內(nèi)常用地圖沒有很方便的接口
  2. 大版本更新后官方文檔不全面而且不夠清晰娩践,沒有g(shù)uild
  3. 而且網(wǎng)上簡(jiǎn)明詳細(xì)的教程很少

針對(duì)以上幾點(diǎn)活翩,覺得自己的小經(jīng)驗(yàn)會(huì)對(duì)大家有所幫助烹骨,可以少走些彎路,也是對(duì)自己這幾天工作的一個(gè)總結(jié)材泄。

a simple example(step by step)

下面通過一個(gè)簡(jiǎn)單地小例子介紹openlayers的一些基本用法沮焕,以及我覺得重要的地方。

需求

需要使用google map或者高德地圖作為底圖脸爱,在地圖上繪制一個(gè)矩形遇汞,實(shí)現(xiàn)矩形的編輯并且能夠得到矩形被修改之后的回調(diào)函數(shù)。大概會(huì)涉及到這些技術(shù)點(diǎn):

  1. 如何與google map等地圖組合使用
  2. 知道坐標(biāo)數(shù)組或者某格式的geo數(shù)據(jù)簿废,如何在地圖上添加feature
  3. 完成feature的修改
  4. 得到feature修改后的回調(diào)方法

附上簡(jiǎn)單截圖

title

初始化地圖

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

只使用地圖的底圖化戳,在底圖上覆蓋一層 openlayer 的 canvas 層來顯示數(shù)據(jù)并攔截與底圖之間的交互单料,通過地圖的 api 來重設(shè)狀態(tài)。
此方案還有以下優(yōu)點(diǎn):

  1. 解決了頁面節(jié)點(diǎn)過多之后的性能問題
  2. 底圖可以被抽象出去点楼,能夠在不影響交互邏輯的基礎(chǔ)上更換底圖扫尖。

代碼如下:

<!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掠廓。
        目標(biāo)是加載完畢之后olmap覆蓋與gmap之上并且攔截交互操作换怖。
        開始時(shí)放在同一層的好處是都能根據(jù)父節(jié)點(diǎn)來設(shè)置長(zhǎng)寬。也可以在js中動(dòng)態(tài)生成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用于控制地圖的 坐標(biāo)系標(biāo)準(zhǔn) zoom center rotate等操作的對(duì)象沉颂,在實(shí)例化map時(shí)候需要使用
  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' // 設(shè)置為標(biāo)準(zhǔn)經(jīng)緯度的坐標(biāo)標(biāo)準(zhǔn),十分重要悦污! 默認(rèn)是'EPSG:3857'
  });

  // view 拖動(dòng)時(shí)觸發(fā)事件铸屉,根據(jù)當(dāng)前的坐標(biāo)轉(zhuǎn)化為經(jīng)緯度,調(diào)用谷歌地圖setCenter方法同步地圖位置
  view.on('change:center', function () {
    var center = view.getCenter();
    gmap.setCenter(new google.maps.LatLng(center[1], center[0])); // 注意順序
  });

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

  // ol.source.Vector 作為 ol.layer.Vector的數(shù)據(jù)集梦重,增刪改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], // 所使用的圖層
    // 禁用掉默認(rèn)的拖動(dòng)、旋轉(zhuǎn)等交互
    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}) 但是在這里需要通過手動(dòng)設(shè)置來觸發(fā)google maps調(diào)節(jié)到正確地zoom與center
  });
  view.setCenter([10.689697265625, -25.0927734375]); // 如果未設(shè)置view的坐標(biāo)標(biāo)準(zhǔn)藕夫,這里千萬要注意不要直接寫經(jīng)緯度
  view.setZoom(6);  // 設(shè)置縮放等級(jí)
  
  // 將openlayers容器放置到google地圖容器中
  olMapDiv.parentNode.removeChild(olMapDiv);
  gmap.controls[google.maps.ControlPosition.TOP_LEFT].push(olMapDiv);
</script>
</body>
</html>

有了這段代碼應(yīng)該就有了一個(gè)可以拖動(dòng)縮放的地圖了丸凭。

添加feature

當(dāng)然光有一個(gè)地圖是不行的,如果需要往地圖上面添加feature怎么辦呢椰于?大致有以下兩種場(chǎng)景:

  1. 提供整個(gè)地圖的信息描述數(shù)據(jù)怠益,比如geoJson,WKT或者自定義的數(shù)據(jù)結(jié)構(gòu)等瘾婿,需要解析整個(gè)數(shù)據(jù)并批量顯示在地圖上蜻牢。
  2. 拿到某個(gè)feature的坐標(biāo)數(shù)據(jù)烤咧,需要添加到地圖上,并實(shí)現(xiàn)特異化的控制抢呆。

批量添加數(shù)據(jù)

先上代碼

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

ol.format下有很多種數(shù)據(jù)類型煮嫌,選擇匹配的數(shù)據(jù)格式。
比如WKT使用ol.format.WKT.readFeature

readFeature返回的是ol.Feature的數(shù)組抱虐,可以通過遍歷其來獲得feature對(duì)象昌阿,從而按需求修改或者掛載監(jiān)聽事件。

添加單個(gè)feature

如果現(xiàn)有的數(shù)據(jù)是geoJson等標(biāo)準(zhǔn)格式的數(shù)據(jù)恳邀,可以通過ol.format中的類進(jìn)行轉(zhuǎn)換懦冰。如果有需求則使用ol.proj.transform進(jìn)行坐標(biāo)系轉(zhuǎn)換。

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

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

如果拿到的是經(jīng)緯度信息谣沸,添加一個(gè)polygon

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

修改feature

介紹如何修改feature以及掛載監(jiān)聽事件刷钢。

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

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

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

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

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

// 當(dāng)元件被取消選中時(shí)觸發(fā)内地,一般把元件的修改回調(diào)放在這
    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
    });
    

一般來說如果需要后續(xù)對(duì)feature進(jìn)行操作,可以使用getId方法拿到featureid赋除,可以通過setId來設(shè)置自己想要的id阱缓,否則會(huì)自動(dòng)生成。 將id存在常駐的對(duì)象中供以后使用举农。

假設(shè)拿到ol.Feature對(duì)象 feature

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

需要注意的是這個(gè)onChange事件在修改的過程中會(huì)不斷地觸發(fā)荆针,如果需要的是修改完成之后的回調(diào),需要使用selectremove事件并蝗。

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

修改feature對(duì)象

設(shè)置id

feature.setId(id)

得到geometry對(duì)象

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

feature to string

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

需要注意的地方

  • openlayers默認(rèn)的坐標(biāo)系是'EPSG:3857'祭犯,標(biāo)準(zhǔn)經(jīng)緯度坐標(biāo)系是'EPSG:4326'
  • 看openlayer文檔最重要的技巧是注意類型
  • geometry接受的coordinates其實(shí)是一個(gè)三維數(shù)組,一定要注意

常用操作

坐標(biāo)系轉(zhuǎn)換

根據(jù)當(dāng)前坐標(biāo)系與目標(biāo)坐標(biāo)系進(jìn)行轉(zhuǎn)換滚停。

ol.proj.transform(coordinate, source, destination)
coordinate 在文檔中得類型是 Coordinate其實(shí)就是一個(gè)有橫縱坐標(biāo)組成的數(shù)組沃粗,因此一定要注意官方文檔中得數(shù)據(jù)類型。
source 當(dāng)前坐標(biāo)編碼 string類型
destination 目標(biāo)坐標(biāo)編碼 string類型

從經(jīng)緯度轉(zhuǎn)化到指定坐標(biāo)系

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

從某坐標(biāo)轉(zhuǎn)經(jīng)緯度

ol.proj.toLonLat(coordinate, opt_projection)

數(shù)據(jù)格式化

ol.format

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末键畴,一起剝皮案震驚了整個(gè)濱河市最盅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌起惕,老刑警劉巖涡贱,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惹想,居然都是意外死亡问词,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門嘀粱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來激挪,“玉大人辰狡,你說我怎么就攤上這事÷⒎郑” “怎么了宛篇?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)薄湿。 經(jīng)常有香客問我叫倍,道長(zhǎng),這世上最難降的妖魔是什么豺瘤? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任吆倦,我火速辦了婚禮,結(jié)果婚禮上炉奴,老公的妹妹穿的比我還像新娘逼庞。我一直安慰自己,他們只是感情好瞻赶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著派任,像睡著了一般砸逊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掌逛,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天师逸,我揣著相機(jī)與錄音,去河邊找鬼豆混。 笑死篓像,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的皿伺。 我是一名探鬼主播员辩,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鸵鸥!你這毒婦竟也來了奠滑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤妒穴,失蹤者是張志新(化名)和其女友劉穎宋税,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讼油,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杰赛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矮台。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乏屯。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡根时,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓶珊,到底是詐尸還是另有隱情啸箫,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布伞芹,位于F島的核電站忘苛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏唱较。R本人自食惡果不足惜扎唾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望南缓。 院中可真熱鬧,春花似錦汉形、人聲如沸纸镊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逗威。三九已至,卻和暖如春岔冀,著一層夾襖步出監(jiān)牢的瞬間凯旭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工使套, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罐呼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓侦高,卻偏偏與公主長(zhǎng)得像嫉柴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子矫膨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容