Openlayers6 图形绘制和修改功能(结合React)

Openlayers常用的API了解的差不多了,就开始进入实战了,首先从绘制基本的图形开始,这里主要介绍一下绘制圆形、矩形和多边形。

通过使用openlayers的ol.interaction.Drawol.interaction.Modify模块实现地图上绘制圆形、矩形、多边形并修改编辑。

首先我们需要创建一个地图容器,并添加地图和矢量图层。底图我这里使用了高德图层,矢量图层用于显示用户绘制结果。然后我们通过添加Draw交互控件来实现绘制功能。每个绘制按钮都有一个点击事件处理函数,调用addInteraction方法来添加相应类型的绘制控件。通过添加Modify交互控件实现修改功能。

一、创建地图

在Openlayers中创建一个简单的地图可以通过一下步骤完成:

  1. 引入Openlayers库。

  2. 创建地图视图ol/View实例,并设置初始中心点和缩放级别。

  3. 创建地图层ol/layer/Tile,通常使用瓦片图层ol/source/OSM

  4. 创建地图实例ol/Map,并将之前创建的视图和图层添加进去。

  5. 获取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%' }}>

二、绘制图形

可以使用VectorLayerVectorSource添加一个矢量图层,该图层可以用来绘制图形。

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]);

三、添加地图交互操作

添加DrawModify交互操作,允许用户在地图上绘图并编辑。

ol.interaction.Draw简述

使用 ol.interaction.Draw 绘制图形,Draw控件需要指定一个矢量源和绘制类型。当用户在地图上进行绘制时,绘制结果会被添加到矢量源中。可调用removeInteraction方法取消绘制。

常用属性
  • type :绘制的几何图形的几何类型,包括Point单点、LineString线、Circle圆、Polygon多边形四种类型。
  • features:表示绘制的图形将添加在指定的要素上。
  • source:绘制时指定的数据来源。表示绘制的图形将添加在指定的图层上。
  • geometryFunction :用于指定geometry,包括两参数,分别是coordinatesgeometry,返回geometry。通过该方法可以重新构建图形,比如将圆形构造成五角星。
  • style:指定绘制图形的样式。
  • freehand:手绘线、面等图形。设置属性为true开启手绘模式(随意绘制曲线或者直线)。
  • maxPoints:最多可以绘制多少个点,比如设置成5,在绘制多边形时,如果超过了5个点就不能绘制了。
  • minPoints:在完成多边形环或线时,必须指定绘制的点数。多边形的默认值为3,线段的默认值为2。
常用方法
  • createBox :当typeCircle时将geometryFunction设置成该方法,可以绘制矩形。
  • createRegularPolygon :当typeCircle时将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;
            }
        }
    }
}
相关推荐
neter.asia几秒前
vue中如何关闭eslint检测?
前端·javascript·vue.js
~甲壳虫1 分钟前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
光影少年20 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
As977_22 分钟前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
susu108301891124 分钟前
vue3 css的样式如果background没有,如何覆盖有background的样式
前端·css
Ocean☾25 分钟前
前端基础-html-注册界面
前端·算法·html
Rattenking25 分钟前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
Dragon Wu27 分钟前
前端 Canvas 绘画 总结
前端
CodeToGym32 分钟前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化
~甲壳虫33 分钟前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js