Openlayers
常用的API
了解的差不多了,就开始进入实战了,首先从绘制基本的图形开始,这里主要介绍一下绘制圆形、矩形和多边形。
通过使用openlayers的ol.interaction.Draw
和ol.interaction.Modify
模块实现地图上绘制圆形、矩形、多边形并修改编辑。
首先我们需要创建一个地图容器,并添加地图和矢量图层。底图我这里使用了高德图层,矢量图层用于显示用户绘制结果。然后我们通过添加Draw
交互控件来实现绘制功能。每个绘制按钮都有一个点击事件处理函数,调用addInteraction
方法来添加相应类型的绘制控件。通过添加Modify
交互控件实现修改功能。
一、创建地图
在Openlayers中创建一个简单的地图可以通过一下步骤完成:
- 引入Openlayers库。
- 创建地图视图
ol/View
实例,并设置初始中心点和缩放级别。 - 创建地图层
ol/layer/Tile
,通常使用瓦片图层ol/source/OSM
。 - 创建地图实例
ol/Map
,并将之前创建的视图和图层添加进去。 - 获取DOM元素并将地图实例挂载到该元素上。
// 引入OpenLayers库
import { Map, View } from 'ol';
import { Tile as TileLayer } from 'ol/layer';
import * as Proj from 'ol/proj';
const [map, setMap] = useState(null); // 地图
const [view, setView] = useState(null); // 地图视图
useEffect(() => {
// 监听地图视图并创建地图实例
if (view) {
// 创建一个高德图层
const tileLayer = new TileLayer({
source: new XYZ({
url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&scl=1&size=1&style=7&x={x}&y={y}&z={z}'
})
});
// 创建实例
const _map = new Map({
target: 'map',
layers: [tileLayer], // 使用高德图层
view: view
});
setMap(_map);
}
}, [view]);
useEffect(() => {
// 创建一个地图视图
const viewObj = new View({
center: Proj.transform([104.06403453968424, 30.597419070782898], 'EPSG:4326', 'EPSG:3857'), // 使用'EPSG:3857'投影
zoom: 16
});
setView(viewObj);
}, []);
return <div id='map' style={{ width: '100%', height: '100%' }}></div>
二、绘制图形
可以使用VectorLayer
和VectorSource
添加一个矢量图层,该图层可以用来绘制图形。
import { Vector as VectorLayer } from 'ol/layer';
import { Vector as VectorSource } from 'ol/source';
import { Style, Stroke, Fill } from 'ol/style';
let drawSource = null;
let drawLayer = null;
useEffect(() => {
if (map) {
drawSource = new VectorSource();
// 添加画板layer
drawLayer = new VectorLayer({
source: drawSource,
style: new Style({
fill: new Fill({
color: 'rgba(237, 57, 47, 0.35)'
}),
stroke: new Stroke({
color: '#ED392F',
width: 4,
lineDash: [10]
})
})
});
map.addLayer(drawLayer);
}
}, [map]);
三、添加地图交互操作
添加Draw
和Modify
交互操作,允许用户在地图上绘图并编辑。
ol.interaction.Draw简述
使用 ol.interaction.Draw
绘制图形,Draw
控件需要指定一个矢量源和绘制类型。当用户在地图上进行绘制时,绘制结果会被添加到矢量源中。可调用removeInteraction
方法取消绘制。
常用属性
- type:绘制的几何图形的几何类型,包括
Point
单点、LineString
线、Circle
圆、Polygon
多边形四种类型。 - features:表示绘制的图形将添加在指定的要素上。
- source:绘制时指定的数据来源。表示绘制的图形将添加在指定的图层上。
- geometryFunction:用于指定
geometry
,包括两参数,分别是coordinates
,geometry
,返回geometry
。通过该方法可以重新构建图形,比如将圆形构造成五角星。 - style:指定绘制图形的样式。
- freehand:手绘线、面等图形。设置属性为true开启手绘模式(随意绘制曲线或者直线)。
- maxPoints:最多可以绘制多少个点,比如设置成5,在绘制多边形时,如果超过了5个点就不能绘制了。
- minPoints:在完成多边形环或线时,必须指定绘制的点数。多边形的默认值为3,线段的默认值为2。
常用方法
- createBox:当
type
为Circle
时将geometryFunction
设置成该方法,可以绘制矩形。 - createRegularPolygon:当
type
为Circle
时将geometryFunction
设置成该方法,可以正多边形。 - appendCoordinates:当在绘制完线或者多边形后,还可以通过该方法往图形中添加坐标。
- extend:扩展图片,可以往图形上再添加一个图形,仅限于线条。
- finishDrawing:结束绘制。
- removeLastPoint:删除最后一个坐标点。
常用事件
- drawend:绘制结束时触发。
- drawstart:绘制开始时触发。
Draw实例
import { Draw } from 'ol/interaction';
import { Style, Stroke, Fill, Circle as CircleStyle } from 'ol/style';
let draw = null;
// 添加Draw交互操作
draw = new Draw({
source: drawSource,
type: 'Polygon',
style: new Style({
fill: new Fill({
color: 'rgba(237, 57, 47, 0.35)'
}),
stroke: new Stroke({
color: '#ED392F',
width: 4,
lineDash: [10]
}),
image: new CircleStyle({
radius: 7,
fill: new Fill({
color: '#ED392F'
})
})
})
});
// 添加Draw交互
map.addInteraction(draw);
draw.on('drawend',function() {
// 绘制结束时触发
});
draw.on('drawstart',function() {
// 绘制开始时触发
});
ol.interaction.Modify简述
使用 ol.interaction.Modify
编辑图形。只要将它初始化作为交互控件加入Map对象,就可以对几何图形进行动态编辑。
将鼠标移动到已经绘制好的线条或点上面,再移动鼠标,可以对图形进行修改。
按住Alt键时,再点击鼠标,可以删除选中的点。
常用属性
- condition:设置一个函数,在修改时监听点击事件,返回一个布尔值表示是否处理该点击事件。比如返回false时,选中点后,点击选中的点再移动鼠标时将不处理移动事件。
- deleteCondition:设置一个函数,返回一个布尔值是否执行删除的操作。
- insertVertexCondition:设置一个函数,返回一个布尔值表示是否添加新的点。比如我们在编辑多边形时,点击多边形上的线条时,默认是可以在点击的位置添加一个点。如果返回值为false,不会添加新的坐标点。
- pixelTolerance:设置捕捉点的像素差,如果设置的很大,离坐标点很远也能捕捉到点,默认值 为10px。
- style:用于修改点或顶点的样式。对于LineString和Polygon类,style将影响它们的顶点;对于Circle类,style将影响沿着圆的点;对于Point,style影响的就是实际的点。如果没有配置的话,就会使用默认的配置,默认配置是蓝色的。
- source:要修改的数据源。如果未提供数据源,则必须提供要修改的Feature。
- features:要修改的Feature。如果未提供Feature,则必须提供数据源。
常用方法
- removePoint:删除当前正在编辑的点。
常用事件
- modifystart:编辑开始时触发
- modifyend:编辑接触时触发
Modify实例
import { Collection } from 'ol';
import { Modify } from 'ol/interaction';
import { Style, Stroke, Fill, Circle as CircleStyle } from 'ol/style';
let modify = null;
let features = new Collection();
features.push(event.feature);
modify = new Modify({
features: features // 选中的要素
});
// 添加Modify交互
map.addInteraction(modify);
modify.on('modifystart', () => {
// 编辑开始时触发
});
modify.on('modifyend', () => {
// 编辑接触时触发
});
四、完整代码示例
import React, { useState, useEffect } from 'react';
import { Map, View, Collection } from 'ol'; // 地图Collection
import * as Proj from 'ol/proj'; // 转化
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'; // 图层
import { XYZ, Vector as VectorSource } from 'ol/source'; // 资源
import Draw, { createBox } from 'ol/interaction/Draw';
import { Modify } from 'ol/interaction';
import { Style, Stroke, Fill, Circle as CircleStyle } from 'ol/style'; // 样式
let drawSource = null;
let drawLayer = null;
let draw = null;
let modify = null;
const OpenlayerInteraction = () => {
const [map, setMap] = useState(null); // 地图
const [view, setView] = useState(null); // 地图视图
const [drawType, setDrawType] = useState(null); // 画板类型 1圆形 2矩形 3多边形
// 设置绘制类型
function setDraw(type) {
let kind = null;
let geometryFunction = null;
if (type === 1) {
kind = 'Circle';
} else if (type === 2) {
kind = 'Circle';
geometryFunction = createBox();
} else if (type === 3) {
kind = 'Polygon';
}
draw = new Draw({
source: drawSource,
type: kind,
geometryFunction: geometryFunction,
style: new Style({
fill: new Fill({
color: 'rgba(237, 57, 47, 0.35)'
}),
stroke: new Stroke({
color: '#ED392F',
width: 4,
lineDash: [10]
}),
image: new CircleStyle({
radius: 7,
fill: new Fill({
color: '#ED392F'
})
})
})
});
map.addInteraction(draw);
setDrawType(type);
// 监听线绘制结束事件,获取坐标
draw.on('drawend', (event) => {
// 移除绘制功能
map.removeInteraction(draw);
// 重置数据
setDrawType(null);
draw = null;
// 获取绘制信息
let geometryDraw = event.feature.getGeometry();
console.log(geometryDraw);
// 添加修改功能
let features = new Collection();
features.push(event.feature);
modify = new Modify({
features: features,
style: new Style({
image: new CircleStyle({
radius: 7,
fill: new Fill({
color: '#ED392F'
})
})
})
});
map.addInteraction(modify);
// 监听修改事件
modify.on('modifyend', () => {
});
});
}
useEffect(() => {
if (map) {
// 添加画板layer
drawSource = new VectorSource();
drawLayer = new VectorLayer({
source: drawSource,
style: new Style({
fill: new Fill({
color: 'rgba(237, 57, 47, 0.35)'
}),
stroke: new Stroke({
color: '#ED392F',
width: 4,
lineDash: [10]
})
})
});
drawLayer.setZIndex(2);
map.addLayer(drawLayer);
}
}, [map]);
useEffect(() => {
// 监听地图视图,创建地图
if (view) {
// 使用高德图层
const tileLayer = new TileLayer({
source: new XYZ({
url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&scl=1&size=1&style=7&x={x}&y={y}&z={z}'
})
});
// 创建实例
const _map = new Map({
target: 'map',
layers: [tileLayer], // 使用高德图层
view: view
});
setMap(_map);
}
}, [view]);
useEffect(() => {
// View用于创建2D视图
const viewObj = new View({
center: Proj.transform([104.06403453968424, 30.597419070782898], 'EPSG:4326', 'EPSG:3857'), // 使用'EPSG:3857'投影
zoom: 16
});
setView(viewObj);
}, []);
return <div className='interaction_container'>
<div>openlayer地图绘制、修改圆形、矩形、多边形</div>
<div className='map_box'>
<div className='draw_type'>
<span className="title">选择绘制图形</span>
<div className={`list ${drawType === 1 ? "active" : ''}`} onClick={() => { setDraw(1); }}>圆形</div>
<div className={`list ${drawType === 2 ? "active" : ''}`} onClick={() => { setDraw(2); }}>矩形</div>
<div className={`list ${drawType === 3 ? "active" : ''}`} onClick={() => { setDraw(3); }}>多边形</div>
</div>
<div id='map' style={{ width: '100%', height: '100%' }}></div>
</div>
</div>;
}
export default OpenlayerInteraction;
// 样式
.interaction_container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.map_box {
flex: 1;
position: relative;
.draw_type {
position: absolute;
z-index: 4;
top: 16px;
right: 16px;
background-color: #ffffff;
box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: 16px;
color: #333333;
.title {
font-size: 14px;
line-height: 20px;
margin-bottom: 6px;
display: inline-block;
}
.list {
width: 130px;
height: 32px;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 8px;
cursor: pointer;
border: 1px solid #D5D5D5;
color: #333333;
background: #ffffff;
transition: all .1s ease;
}
.list:hover {
border: 1px solid #10BDFA;
color: #ffffff;
background: #10BDFA;
}
.active {
border: 1px solid #10BDFA;
color: #ffffff;
background: #10BDFA;
}
}
}
}