赞
踩
这一系列翻译自openlayers官网的WorkShop。OL官网提供了多个系列教程供开发者学习参考,其中QuickStart是面向初学者的hello world,Tutorials提供了构建OL应用的一些基础知识,WorkShop(本系列)详细介绍了一些入门向的高阶应用,最后是APIDocs,适合开发时查阅接口。教程中需要下载的资源可以在WorkShop原网站获得链接。
在这一章节,我们将编写一个矢量数据编辑工具。该工具可以供使用者引入数据,绘制新的矢量要素,修改现有矢量要素,以及导出结果。本章教程的样例数据采用的是GeoJSON格式,不过OpenLayers也支持大量其他的矢量数据格式。
在实现矢量编辑之前,我们先引入矢量数据,使用VectorSource和Layer将其渲染在地图上。教程包含了一个 countries.json GeoJSON文件,我们首先将他加载到地图上。
首先,html页面需要一个用以承载地图的div。
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>OpenLayers</title>
- <style>
- html, body, #map-container {
- margin: 0;
- height: 100%;
- width: 100%;
- font-family: sans-serif;
- background-color: #04041b;
- }
- </style>
- </head>
- <body>
- <div id="map-container"></div>
- </body>
- </html>
现在我们将导入处理矢量数据的三个重要组件:
JavaScript代码如下
- import 'ol/ol.css';
- import GeoJSON from 'ol/format/GeoJSON';
- import Map from 'ol/Map';
- import VectorLayer from 'ol/layer/Vector';
- import VectorSource from 'ol/source/Vector';
- import View from 'ol/View';
-
- new Map({
- target: 'map-container',
- layers: [
- new VectorLayer({
- source: new VectorSource({
- format: new GeoJSON(),
- url: './data/countries.json'
- })
- })
- ],
- view: new View({
- center: [0, 0],
- zoom: 2
- })
- });
完成后结果应该是下图这样
//
由于我们将大量重新加载页面,如果地图停留在我们重新加载时离开的位置,那就太好了。我们可以引入ol-hashed来完成这项工作。此软件包已作为教程依赖项的一部分安装。如果尚未包含,您可以使用npm install ol-hashed。
然后在js里导入
import sync from 'ol-hashed';
现在我们需要将地图分配给一个变量(命名map
),以便我们可以将该变量传递给sync
函数
const map = new Map({
现在我们可以使用sync同步我们的地图
sync(map);
现在页面重新加载后地图视图应该会保持原样,后退操作也能如期进行。
对于我们的要素编辑工具,我们希望用户能够导入自己的数据进行编辑。我们将使用DragAndDrop添加这种交互。和以前一样,我们将使用 GeoJSON 格式来解析要素,但其他矢量格式也可以进行下面的操作。
我们将在本章节中将地图传递给许多其他组件,因此请确保您已将地图分配给名为map的变量。
首先在js里引入DragAndDrop模块
import DragAndDrop from 'ol/interaction/DragAndDrop';
接下来,我们将创建一个没有初始数据的矢量数据源。该数据源将存储用户拖放到地图上的要素,而不是像前面的示例那样加载外部数据。
const source = new VectorSource();
现在使用新的数据源创建一个矢量图层,将其添加到地图上
- const layer = new VectorLayer({
- source: source
- });
- map.addLayer(layer);
最后,我们将创建一个拖放交互,将矢量数据源配置进去,并将其添加到地图中
- map.addInteraction(new DragAndDrop({
- source: source,
- formatConstructors: [GeoJSON]
- }));
现在应该可以将GeoJSON 文件拖放到地图上直接加载
现在用户可以将数据拖拽加载到编辑工具中,我们希望用户可以编辑要素。我们将引入Modify这个交互组件,将刚才的矢量数据源配置进去
首先引入Modify
import Modify from 'ol/interaction/Modify';
接下来,为地图对象添加一个连接到矢量数据源的交互
- map.addInteraction(new Modify({
- source: source
- }));
将数据添加到地图后,可以通过拖动要素节点来修改位置,以及Alt+Click来删除节点
目前我们的要素编辑工具可以加载并编辑要素,接下来添加Draw组件用以绘制新要素并添加到数据源中。
首先在js里引入Draw
import Draw from 'ol/interaction/Draw';
然后为地图对象添加绘制交互,将数据源配置进去
- map.addInteraction(new Draw({
- type: 'Polygon',
- source: source
- }));
Draw对象的type属性控制该交互绘制什么类型的几何。该值可以是任何GeoJSON几何类型。这里也可以引入OL的几何类型枚举(import GeometryType from 'ol/geom/GeometryType';),然后使用GeometryType.POLYGON代替上边的polygon字符串。
现在应该可以使用绘制功能在数据源中添加新的要素。
您可能已经注意到,绘制与现有要素不一致的新要素很容易。此外,在修改要素时,我们可以打破拓扑——在之前相邻的多边形之间添加空隙。
首先引入Snap交互
import Snap from 'ol/interaction/Snap';
和之前的交互一样,添加到地图上
- map.addInteraction(new Snap({
- source: source
- }));
在绘制、修改和吸附交互都激活的情况下,可以在保持拓扑结构的同时编辑要素
上传数据并编辑后,我们想让用户可以下载结果。为此需要将矢量要素序列化为GeoJSON并在html里创建<a>标签让用户下载。同时,在页面加个按钮让用户可以清除现有要素重新绘制。
html里添加下面
- <div id="tools">
- <a id="clear">Clear</a>
- <a id="download" download="features.json">Download</a>
- </div>
为上边的按钮添加样式
- #tools {
- position: absolute;
- top: 1rem;
- right: 1rem;
- }
- #tools a {
- display: inline-block;
- padding: 0.5rem;
- background: white;
- cursor: pointer;
- }
要素的清除可以通过source.clear() 方法,为clear按钮添加监听触发清除函数
- const clear = document.getElementById('clear');
- clear.addEventListener('click', function() {
- source.clear();
- });
为了序列化我们的要素数据以供下载,我们将使用GeoJSON格式(format,之前引用的)。这里监听了数据源的变化,随时重写下载的uri
- const format = new GeoJSON({featureProjection: 'EPSG:3857'});
- const download = document.getElementById('download');
- source.on('change', function() {
- const features = source.getFeatures();
- const json = format.writeFeatures(features);
- download.href = 'data:text/json;charset=utf-8,' + json;
- });
此时,我们有一个具有基本导入、编辑和导出功能的要素编辑器。但是我们没有花时间让它看起来好看。在 OpenLayers 中创建矢量图层时,会默认添加一组样式。编辑交互(绘制和修改)也有自己的默认样式。下面通过样式(Style)修改默认的图层样式。
首先引入样式(style),填充(fill)和路径(stroke)
import {Style, Fill, Stroke} from 'ol/style';
静态样式
如果想给一个图层里所有要素同样的样式,可以这样配置
- const layer = new VectorLayer({
- source: source,
- style: new Style({
- fill: new Fill({
- color: 'red'
- }),
- stroke: new Stroke({
- color: 'white'
- })
- })
- });
也可以将style设置为样式数组,形成比如下边一条宽线上边一条窄线这种样式。
动态样式
若想根据要素的属性字段或当前视图分辨率等来配置不同的样式,可以使用样式函数配置矢量图层。这个函数在每个要素的渲染帧都会被调用,所以如果你有很多要素属性并且想要保持良好的渲染性能,那么编写一个高效的函数很重要。
这里示例了根据‘name’属性的开头是‘A-M’还是‘N-Z’来渲染不同的样式
- const layer = new VectorLayer({
- source: source,
- style: function(feature, resolution) {
- const name = feature.get('name').toUpperCase();
- return name < "N" ? style1 : style2; // 假设这两个style是在别处创建的
- }
- });
根据几何面积样式化
为了了解动态样式的工作原理,我们将创建一个样式函数,该函数根据几何面积呈现特征。为此,我们将使用npm 上的一个colorMap包,安装(npm install colormap
)后导入js,使用ol/sphere进行球面面积计算
- import {getArea} from 'ol/sphere';
- import colormap from 'colormap';
下面的函数用以根据几何图形的面积确定颜色
- const min = 1e8; // 最小面积
- const max = 2e13; // 最大面积
- const steps = 50;
- const ramp = colormap({
- colormap: 'blackbody',
- nshades: steps
- });
-
- function clamp(value, low, high) {
- return Math.max(low, Math.min(value, high));
- }
-
- function getColor(feature) {
- const area = getArea(feature.getGeometry());
- const f = Math.pow(clamp((area - min) / (max - min), 0, 1), 1 / 2);
- const index = Math.round(f * (steps - 1));
- return ramp[index];
- }
下面为图层添加基于几何面积填充颜色的样式
- const layer = new VectorLayer({
- source: source,
- style: function(feature) {
- return new Style({
- fill: new Fill({
- color: getColor(feature)
- }),
- stroke: new Stroke({
- color: 'rgba(255,255,255,0.8)'
- })
- });
- }
- });
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。