7

本文简介

戴尬猴,我是德育处主任


本文介绍如何使用 OpenLayers.js (后面简称 ol)。ol 是一个开源 JavaScript 库,可用于在Web页面上创建交互式地图。 ol能帮助我们在浏览器轻松地使用地图功能,例如地图缩放、地图拖动、地图标记和地图交互等。

本文适合想有JS基础,又想了解 ol 的工友。



OpenLayers简介

⚡️ OpenLayers官网

file

ol 使得在任何网页中放置动态地图变得很容易。它可以显示从任何来源加载的地图块、矢量数据和标记。OpenLayers 的开发是为了进一步利用各种地理信息。它是完全免费的,开放源代码 JavaScript,根据包含2个子句的 BSD 许可证(也称为 FreeBSD)发布。

上面这段话来自 ol 官网的简介。

简单来说,ol 能显示和编辑地图。如果你不想用百度、高德、腾讯等地图,如果你需要高度定制地图,那可以试试 ol

在某些特定情况(比如内网)要用到地图编辑功能,也可以使用 ol



安装 OpenLayers

本文用到的 ol 版本是 7.3.0

如果是用脚手架创建的项目,大概率会用 npm 的方式将 ol 安装到项目里。

不管是 npm 还是 cdn 的方式,都需要引入 olcssjs

本文为了方便,我都会用 cdn 的方式讲解。


npm

安装命令

npm i ol

使用方法

<style>
  .map-x {
    width: 600px;
    height: 600px;
  }
</style>

<div id="map" class="map-x"></div>

<script>
import 'ol/ol.css'
import { Map, View } from 'ol'
import Tile from 'ol/layer/Tile'
import OSM from 'ol/source/OSM'

new Map({
  target: 'map', // 绑定html元素
  layers: [ // 图层
    new Tile({
      source: new OSM() // 图层数据源(OSM可以在练习时使用,千万别用在真实项目!!!)
    })
  ],
  view: new View({ // 地图视图
    projection: "EPSG:4326", // 坐标系,有EPSG:4326和EPSG:3857
    center: [114.064839, 22.548857], // 深圳坐标
    zoom: 12 // 地图缩放级别(打开页面时默认级别)
  })
})
</script>

上面的代码看不懂没关系,后面会讲到的。


cdn

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v7.3.0/ol.css">
<script src="https://cdn.jsdelivr.net/npm/ol@v7.3.0/dist/ol.js"></script>

如果你想使用其他版本,可以在这里找找 ⚡️ ol的其他版本



重点声明!!!

在使用 ol 渲染地图时,需要用到一些底图。

本文会提到 OSM 图源,但 OSM 图源的边界可能不是那么精准,在学习时使用该图源没问题,但切勿在真实项目中使用OSM图源!!!切记!切记!



起步

学习 ol 会接触到很多地图相关的概念,但作为入门阶段,我不打算一上来就把所有概念过一遍,这样太打机学习热情了。我打算学到哪个功能就讲那个功能的概念。


起步阶段,先了解一下用 ol 怎么在页面创建地图。

  1. 引入 ol.jsol.css
  2. 创建地图容器(一个 HTML 标签,通常使用 div )。
  3. 设置地图容器宽高(必须做的一项!!!)。
  4. 使用 ol 绑定地图容器。
  5. 创建地图底图。
  6. 设置地图中心点。


先来看看效果

file

<!-- 引入 ol.css -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v7.3.0/ol.css">

<style>
/* 设置地图容器宽高 */
#map {
  width: 400px;
  height: 400px;
}
</style>

<!-- 地图容器 -->
<div id="map"></div>

<!-- 引入 ol.js -->
<script src="https://cdn.jsdelivr.net/npm/ol@v7.3.0/dist/ol.js"></script>
<script>
  const map = new ol.Map({
    target: 'map', // 绑定地图容器
    layers: [ // 底图图层
      new ol.layer.Tile({
        source: new ol.source.OSM() // 这里使用了OSM底图,切记生产环境千万不能用这个图源。
      })
    ],
    view: new ol.View({ // 设置视图
      center: [0, 0], // 中心点
      zoom: 1 // 地图默认缩放级别
    })
  })
</script>


根据前面提到的流程创建出一个地图。

ol.Map() 是地图的容器,它返回一个 ol 地图对象。它可以配置各种图层、加载各种控件。


上面的例子中,ol.Map() 中有3个核心要素:

  • target:绑定地图容器的属性,传入容器的 id 即可。
  • layers:底图图层。ol 支持多图层配置,所以 layers 的值是一个数组。
  • view:地图视图。可以配置地图的缩放、投影坐标系、中心点、旋转角度等配置项。



视图常用配置

视图指的是 ol.View 这个类,这是一个很重要的类,它拥有设置地图的中心点、缩放级别、旋转角度等功能。

ol.View 也是我认为初学者比较容易掌握的一个知识点,所以先讲这个。


投影坐标系

我们知道在现实世界中使用经纬度可以定义一个位置。

ol 中,支持多种定义位置的格式,这些格式都叫投影坐标系。

ol 默认使用的是 EPSG:3857 。如果你习惯了百度地图,比如你使用 百度地图拾取坐标系统 获得的坐标点,想放在 ol 中能对应得上,那就需要配置投影坐标系了。


ol 中配置投影坐标系,需要在 ol.View 类里配置,配置项叫做 projection

如果你要配置成百度地图的坐标系,可以将 projection 设置为 'EPSG:4326'

file

// 省略部分代码

const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [113.267252, 23.137949], // 设置视图中心点
    projection: 'EPSG:4326', // 配置投影坐标系
    zoom: 11
  })
})

在这个例子中,113.267252, 23.137949 这个坐标点是在 百度地图拾取坐标系统 搜“广州”获取到的。


设置中心点

在前面的例子中我们已经知道,ol.View 里可以设置 center,这就是设置中心点的属性。

// 省略部分代码
const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [113.267252, 23.137949], // 设置视图中心点
    projection: 'EPSG:4326', // 配置投影坐标系
    zoom: 11
  })
})

中心点是打开地图时画布的中心坐标。

同一个坐标在不同投影坐标系所呈现出来的位置是不一样的。


缩放级别

每种底图都有一定程度的缩放级别,这要看你所使用的底图支持多大的缩放级别。

默认缩放级别

ol 中,使用 zoom 可以配置默认的缩放级别,前面的例子中已经演示过了。

前一个例子的缩放级别是 11 ,这次我使用 20 来看看效果。

file

// 省略部分代码
const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [113.267252, 23.137949],
    projection: 'EPSG:4326',
    zoom: 20 // 缩放级别
  })
})

明显大了很多。


最大/小缩放级别

在地图上使用鼠标滚轮上下滚动时,地图层级会缩放。

默认情况下 ol 是不限定缩放级别的,最小缩放级别就是当前底图的最小级别,最大也同理。

file


如果想要设置最大和最小的缩放级别,可以使用 maxZoomminZoom 进行设置。

file

// 省略部分代码
const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [113.267252, 23.137949],
    projection: 'EPSG:4326',
    zoom: 12, // 缩放级别
    minZoom: 10, // 设置最小缩放级别
    maxZoom: 14, // 设置最大缩放级别
  })
})


旋转地图

我在其他 canvas 类的文章也有讲过,为了方便开发者观察,旋转的公式可以写成这样:

Math.PI / 180 * 旋转角度

比如要旋转 45度,可以这样写:Math.PI / 180 * 45

ol 中要旋转地图的话,可以配置 rotation 属性。

file

// 省略部分代码
const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [113.267252, 23.137949],
    projection: 'EPSG:4326',
    zoom: 12, // 缩放级别
    rotation: Math.PI / 180 * 45, // 旋转地图45度
  })
})


好了,简单的用法就大概讲这么多(赶进度)。

以后再单独开篇讲某些特殊属性配置。



图层模块

图层模块也是 ol 一个非常重要的模块。图层模块为我们提供了在地图上展示和处理各种地理数据的能力。无论是矢量数据、栅格数据还是其他地理信息,ol 的图层模块都能帮助我们轻松加载、显示和操作这些数据。

ol 中,图层是通过 layers 来配置的。


加载地图数据

这里所说的地图数据是图源的意思,比如百度地图、必应地图等。

ol 中,一般使用瓦片地图的图片作为底图。

我们可以通过 layers 配置图层,细心的工友可能发现了,layers 的值是一个数组,而且这个单词后面是加了 s 的,表示复数,这说明图层是可以存在多个的。有 PhotoShop 经验的工友应该能更轻松理解图层的概念。

ol.layer.Tileol 中的图层类,用于显示瓦片图提供的地图数据。

所谓的瓦片是将一整块地图切割成一小块一小块那样,可以想象一下瓦片房的房顶。

file

这么设计的原因是为了加快加载速度。

网页上的地图通常有不同的缩放级别,而且地图的面积也很大,如果一次要加载一整张地图,那加载速度会非常慢,于是就出现了瓦片图的概念。

屏幕的尺寸是有限的,在一定的尺寸内只展示该展示的部分,这样就能提升图片的加载速度。

ol.layer.Tile 里需要使用 source 指定要加载的图源。


OSM

在前面的例子中我们已经用过 OSM 底图了。OSM 的全称是 OpenStreetMap ,是 ol 内置的底图。

// 省略部分代码

new ol.Map({
  target:'map',
  layers: [
    new ol.layer.Tils({
      source: new ol.source.OSM()
    })
  ]
})


必应地图 Bing

前面用到的 OSM 底图是一种图层的图源,但没用魔法工具的工友使用这种图源可能地图会加载得很慢,或者加载不出来。

如果你自己有部署了瓦片图,可以使用自己的图源。又或者使用其他平台提供的图源,比如必应的底图。

使用必应或者百度等底图,需要在他们的平台申请一个 Key,以必应为例。

申请必应地图的 Key 可以在该网址里申请:https://www.bingmapsportal.com/Application

申请完就可以使用了。

file

// 省略部分代码

const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      // 使用必应地图
      source: new ol.source.BingMaps({
        key: '你的Key', // 这里需要填入你申请到的Key
        imagerySet: 'Aerial'
      })
    })
  ],
  view: new ol.View({
    center: [113.267252, 23.137949], // 设置视图中心点
    projection: 'EPSG:4326', // 配置投影坐标系
    zoom: 12, // 默认缩放级别
  })
})

这段代码的关键点是使用 new ol.source.BingMaps() 加载必应地图。

key 是你在 必应地图 中申请到的,imagerySet 是用来设置瓦片图类型的,你可以设置其他类型试试看,比如 Road

file


显示 / 隐藏图层

图层有一个 visible 属性可以控制它显示或者隐藏。

visible 默认值是 true,也就是显示。如果你在初始化图层时将它设置为 false 那么它就会隐藏起来。

// 省略部分代码

const map = new ol.Map({
  target: 'map',
  // 图层
  layers: [
    // 加载瓦片图
    new ol.layer.Tile({
      source: new ol.source.OSM(),
      visible: false // 隐藏图层
    })
  ],
  view: ...
})


除了在初始化的时候隐藏图层外,还可以使用 getVisible() 获取图层当前的 visible 状态,可以使用 setVisible 设置图层的 visible

file

// 省略部分代码

const map = new ol.Map({
  target: 'map',
  // 图层
  layers: [
    // 加载瓦片图
    new ol.layer.Tile({
      // 必应图源
      source: new ol.source.BingMaps({
        key: '你的Key',
        imagerySet: 'Aerial'
      })
    })
  ],
  view: new ol.View({
    center: [113.267252, 23.137949], // 设置视图中心点
    projection: 'EPSG:4326', // 配置投影坐标系
    zoom: 12, // 默认缩放级别
  })
})

// 显示或隐藏图层
function toggleLayer() {
  let layers = map.getLayers() // 获取图层组
  let layer = layers.item(0) // 因为只有一个图层,所以直接 item(0) 即可拿到
  let visible = layer.getVisible() // 获取图层当前显示状态
  layer.setVisible(!visible) // 设置图层显示状态
}

这段代码需要注意的是,我们当前只有一个图层,所以可以直接使用 map.getLayers().item(0) 获取,如果你的项目中用到多个图层,需要另外判断一下才行。


图层不透明度

要设置图层的不透明度,用到的属性叫 opacity。它接受 0 ~ 1 的值。

// 省略部分代码
const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM(),
      opacity: 0.5 // 设置图层不透明度
    })
  ],
  view: ...
})


可以使用 getOpacity() 方法获取图层的不透明度,可以使用 setOpacity() 设置图层不透明度。

file

<style>
  #map {
    width: 400px;
    height: 400px;
  }
</style>

<label for="checkboxEl">不透明度</label>
<!-- 滑块控件,用来设置图层不透明度的值 -->
<input
  id="rangeEl"
  type="range"
  checked="true"
  min="0"
  max="1"
  step="0.01"
  value="0.5"
/>

<div id="opacityValue">不透明度: 0.5</div>

<div id="map"></div>

<script src="../ol/ol.js"></script>
<script>

  const map = new ol.Map({
    target: 'map',
    layers: [
      new ol.layer.Tile({
        source: new ol.source.OSM(),
        opacity: 0.5 // 设置图层不透明度
      })
    ],
    view: new ol.View({
      center: [0, 0],
      zoom: 2
    })
  })

  let rangeEl = document.getElementById('rangeEl')
  rangeEl.addEventListener('change', function() {

    let layers = map.getLayers() // 获取图层组
    let osmLayer = layers.item(0) // 因为只有一个图层,所以直接 item(0) 即可拿到

    osmLayer.setOpacity(parseFloat(this.value)) // 设置图层不透明度

    opacityValue.innerHTML = `不透明度: ${osmLayer.getOpacity()}` // 获取图层不透明度,并展示在页面中
  })
</script>

在这个例子中,我使用了滑块控件去控制图层不透明度,用 div 标签在页面展示当前图层的不透明度。


修改底图

可以使用 setSource 设置底图,比如将 OSM 改成必应。

修改底图用到的方法叫 setSource()

setSource() 接收一个图源作为参数。


file

<style>
  #map {
    width: 400px;
    height: 400px;
  }
</style>

<button onclick="switchSource('osm')">设置为OSM</button>
<button onclick="switchSource('bingmap')">设置为BingMaps</button>
<div id="map"></div>

<script src="../ol/ol.js"></script>
<script>
  let osm = new ol.source.OSM()

  let bingmap = new ol.source.BingMaps({
    key: '你的key',
    imagerySet: 'Aerial'
  })


  let layer = new ol.layer.Tile() 

  let map = new ol.Map({
    target: 'map',
    layers: [layer],
    view: new ol.View({
      projection: "EPSG:4326", // 坐标系,有EPSG:4326和EPSG:3857
      center: [114.064839, 22.548857], // 深圳坐标
      zoom: 12
    })
  })

  // 设置图层源
  layer.setSource(osm)

  // 切换图层源
  function switchSource(data) {
    switch(data) {
      case 'osm':
        layer.setSource(osm)
        break
      case 'bingmap':
        layer.setSource(bingmap)
        break
    }
  }
</script>


加载矢量图

前面用到的都是位图作为底图,相信各位接触过前端的工友都知道位图和矢量图的差别,位图加载速度可能会慢点,而且某些情况可能会失真,而矢量图是不失真的。

我推荐一个矢量地图下载地址:DataV.GeoAtlas

大家可以在这个地址下载矢量图并应用到 ol 里。

file

加载方式如下

// 省略部分代码

const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Vector({
      source: new ol.source.Vector({
        format: new ol.format.GeoJSON(),
        url: '../data/geojson.json'
      })
    })
  ],
  view: new ol.View({
    center: [0, 0],
    zoom: 2
  })
})

我们加载的是一份 GeoJSON 的数据,GeoJSON 是一种对各种空间数据结构进行编码的格式,可以理解为它就是一份“具有地图规范格式的 JSON ”。

url 属性指向的是这份 JSON 的位置。



基础控件

ol 自带了一些控件,比如比例尺、全屏等控件。

ol 要添加控件,可以在创建地图时加多一个 controls 属性。


比例尺

基本所有地图都有比例尺,而尺子也有各种单位,比如度、英尺、海里、公制等。需要什么单位的单位可以自行设置。

ol 要将比例尺调取出来,用的是 ol.control.ScaleLine()

首先,要实例化比例尺,然后在创建地图时,将比例尺添加到 controls 属性里。

file

上图左下角就是比例尺。

// 省略部分代码

// 实例化比例尺
const scaleLineControl = new ol.control.ScaleLine() // 比例尺工具

const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [0, 0],
    zoom: 2
  }),
  // 添加控件
  controls: ol.control.defaults.defaults().extend([
    scaleLineControl
  ])
})

ol 的比例尺支持的单位:degrees度、imperial英制英尺、us美制英尺、nautical海里、metric公制。

如果不传单位,默认使用 metric

你可以在初始化比例尺的时候传入单位:

// 省略部分代码

const scaleLineControl = new ol.control.ScaleLine({
  units: 'degrees'
})


除了初始化时设置比例尺单位之外,还可以使用通过 setUnits() 方法设置

file

<select id="units">
  <option value="degrees">度</option>
  <option value="imperial">英制英尺</option>
  <option value="us">美制英尺</option>
  <option value="nautical">海里</option>
  <option value="metric" selected>公制</option>
</select>

<div id="map"></div>

<script>
  // 比例尺工具
  const scaleLineControl = new ol.control.ScaleLine({
    units: 'degrees'
  })

  const map = new ol.Map({
    target: 'map',
    layers: [
      new ol.layer.Tile({
        source: new ol.source.OSM()
      })
    ],
    view: new ol.View({
      projection: "EPSG:4326",
      center: [114.064839, 22.548857],
      zoom: 12
    }),
    // 添加控件
    controls: ol.control.defaults.defaults().extend([
      scaleLineControl
    ])
  })

  // 获取页面上的单位选择器
  const unitsSelect = document.getElementById('units')

  // 修改比例尺单位的方法
  function onChange() {
    scaleLineControl.setUnits(unitsSelect.value)
  }

  unitsSelect.addEventListener('change', onChange)
</script>


全屏控件

ol 在地图内提供了全屏控件,点击一下就可以进入全屏模式。

全屏控件的名字叫 FullScreen


file

// 省略部分代码...

const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [0, 0],
    zoom: 2
  }),
  // 添加控件
  controls: ol.control.defaults.defaults().extend([
    new ol.control.FullScreen() // 全屏控件
  ])
})


鹰眼控件(小地图)

鹰眼控件就是左下角出现的小地图,它也叫缩略图或者鸟瞰图。

file

鹰眼控件在 ol 里的名字叫 OverviewMap

既然它是小地图,那理所当然的就可以配一个底图给它。


file

// 省略部分代码

// 鹰眼控件
let overviewMapControl = new ol.control.OverviewMap({
  className: 'ol-overviewmap ol-custom-overviewmap',
  layers: [
    new ol.layer.Tile({
      // 使用必应地图
      source: new ol.source.BingMaps({
        key: 'AiZrfxUNMRpOOlCpcMkBPxMUSKOEzqGeJTcVKUrXBsUdQDXutUBFN3-GnMNSlso-',
        imagerySet: 'Aerial'
      })
    })
    
  ],
  collapsed: false
})

const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM() // 基础地图使用 OSM
    })
  ],
  view: new ol.View({
    projection: "EPSG:4326",
    center: [114.064839, 22.548857],
    zoom: 6
  }),
  // 添加控件
  controls: ol.control.defaults.defaults().extend([
    overviewMapControl
  ])
})

从这个例子可以看出,默认的大地图使用了 OSM,小地图用了必应地图。

鹰眼控件的 collapsed 属性设置为 false ,意味着小地图默认是打开的。


导览控件

导览控件是一个缩放到指定范围的按钮。

file

导览控件在 ol 中叫 ZoomToExtent,它接收一个对象参数,通过 extent 属性指定了一个范围坐标。这个范围坐标定义了地图应该缩放到的目标范围。

// 省略部分代码
const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: [0, 0],
    zoom: 2
  }),
  // 添加控件
  controls: ol.control.defaults.defaults().extend([
    new ol.control.ZoomToExtent({
      // 定义要展示区域的4个角的坐标
      extent: [
        813079.7791264898, 5929220.284081122,
        848966.9639063801, 5936863.986909639
      ]
    })
  ])
})


缩放控件

ol 除了使用鼠标滚轮缩放地图外,还提供了一个滑块控件给用户缩放地图。

这个控件的名字叫 ZoomSlider

file

// 省略部分代码

const map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    projection: 'EPSG:4326',
    center: [114.064839, 22.548857],
    zoom: 2
  }),
  // 添加控件
  controls: ol.control.defaults.defaults().extend([
    // 缩放滑块控件
    new ol.control.ZoomSlider()
  ])
})


常用的控件先讲到这,其他特殊控件以后还会开文章继续讲~



本文总结

以上是 ol 的入门内容,但 ol 的功能远不止本文介绍的这丁点,之后我会在本文的基础上继续介绍 ol 的其他内容~



代码仓库

openlayers基础用法



推荐阅读

👍《Fabric.js 从入门到膨胀》

👍《物理世界的互动之旅:Matter.js入门指南》

👍《眨个眼就学会了Pixi.js》

👍《p5.js 光速入门》

👍《Canvas 从入门到劝朋友放弃(图解版)》

👍《SVG 从入门到后悔,怎么不早点学起来(图解版)》


最后我想推荐一下 『新手村』 这个专栏,里面记录了各种框架的入门教程,后续会更新前端以外的文章,有兴趣的工友敬请留意~


点赞 + 关注 + 收藏 = 学会了
代码仓库


德育处主任
167 声望16 粉丝