OpenLayers 在Vue中增删改

目的

使用openlayers在vue中完成Feature的增删改动作,达到可以编辑,保存矢量地图的目的。

  1. WFS
    Web Feature Service——Web要素服务
    WMS是由服务器将一地图图像发送给客户端,而WFS是服务器将矢量数据发送给客户端,也就是在使用WMS时地图由服务器绘制,在使用WFS时地图由客户端绘制。

  2. WFS请求
    WFS支持直接在URL地址中加参数方式的操作,这些操作包括:

  • GetFeature —— 获取要素数据
  • Transaction —— 创建、更新和删除要素数据

下面是在WFS中GetFeatures操作的例子,该请求用于获取本地计算机GeoServer的Demo数据的nyc_roads:nyc_roads图层的要素数据:
http://localhost:8088/geoserver/wfs?service=wfs&version=1.1.0&request=GetFeature&typeNames=nyc_roads:nyc_roads&outputFormat=application/json&srsname=EPSG:4326
在上述请求中:
http://localhost:8088/geoserver/wfs?说明请求的是wfs地图服务。
service=wfs&version=1.1.0说明请求的服务是wfs服务,版本号是1.1.0。
request=GetFeature说明请求的是获取这个图层所有的要素。
typeNames=nyc_roads:nyc_roads说明请求的是GeoServer中工作区nyc_roads中nyc_roads图层。
outputFormat=application/json说明请求的地图输出格式是json数据。
srsname=EPSG:4326是指定请求的地图输出坐标系为4326

更详细说明,请参考官方文档

在这里插入图片描述

前提

  1. 启动geoserver
  2. 发布测试用矢量图层。使用geoserver官方提供的例子。url如下:
    https://docs.geoserver.org/latest/en/user/gettingstarted/shapefile-quickstart/index.html

核心代码

  1. 加载Geoserver发布的wfs地图服务
// 加载wfs图层
queryWfs() {
    // 如果已经加载过了wfs图层,那我们就移除这个图层,然后重新添加
    if (this.wfsVectorLayer) {
        this.map.removeLayer(this.wfsVectorLayer);
    }

    // 创建新的图层来加载wfs的要素,即重新加载wfs矢量图层
    this.wfsVectorLayer = new VectorLayer({
        source: new Vector({
            format: new GeoJSON({
                geometryName: 'the_geom' // 因为数据源里面字段the_geom存储的是geometry,所以需要指定
            }),
            url: 'http://localhost:8088/geoserver/wfs?service=wfs&version=1.1.0&request=GetFeature&typeNames=nyc_roads:nyc_roads&outputFormat=application/json&srsname=EPSG:4326'
        }),
        //使用style+feature的方式修改添加的矢量图层的feature样式
        style: function(feature, resolution) {
            return new Style({
                stroke: new Stroke({
                    color: 'blue',
                    width: 5
                })
            });
        }
    });
    this.map.addLayer(this.wfsVectorLayer);
},
  1. 通过wfs修改要素
let WFSTSerializer = new WFS();
let featObject = WFSTSerializer.writeTransaction(null,
    features, null, {
        featureType: 'nyc_roads',
        featureNS: 'http://geoserver.org/nyc_roads',  // 注意这个值必须为创建工作区时的命名空间URI
        srsName: 'EPSG:4326'      //参考坐标系
    });
// 转换为xml内容发送到服务器端
let serializer = new XMLSerializer();
let featString = serializer.serializeToString(featObject);
let request = new XMLHttpRequest();
request.open('POST', 'http://localhost:8088/geoserver/wfs?service=wfs');
// 指定内容为xml类型
request.setRequestHeader('Content-Type', 'text/xml');
request.send(featString);

writeTransaction 的参数为:(inserts, updates, deletes, options)
前三个分别时,插入,更新,删除的Feature数组。

如果返回类似 is read-only 的字样,那是因为geoserver图层默认是只读的,修改的Security属性即可。

<?xml version="1.0" encoding="UTF-8"?><ows:ExceptionReport xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ows="http://www.opengis.net/ows" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/ows http://localhost:8080/geoserver/schemas/ows/1.0.0/owsExceptionReport.xsd">
<ows:Exception exceptionCode="NoApplicableCode">
<ows:ExceptionText>{http://geoserver.org/nyc_roads}nyc_roads is read-only</ows:ExceptionText>
</ows:Exception>
</ows:ExceptionReport>
  1. 使用openlayers的Select,Modify,Draw交互对象,完成选择,修改,增加动作。
// 选取feature的交互类
this.selectInteraction = new Select({
    style: new Style({
        stroke: new Stroke({
            color: 'red',
            width: 2
        })
    })
});
// 修改feature的交互类
this.modifyInteraction = new Modify({
    style: new Style({
        stroke: new Stroke({
            color: 'red',
            width: 5
        })
    }),
    //这个要修改的feature是我们之前通过ol.interaction.Select选中的feature
    features: this.selectInteraction.getFeatures()
});
//监听修改feature
this.modifyInteraction.on('modifyend', this.modifyEnd);

// 绘制feature的交互类
this.drawInteraction = new Draw({
    type: 'LineString', // 设定为线条
    style: new Style({
        stroke: new Stroke({
            color: 'red',
            width: 10
        })
    }),
    source: this.drawLayer.getSource()
});
//监听绘制feature
this.drawInteraction.on('drawend', this.drawEnd );

完整代码

贴上完整代码,便于测试验证。

<template>
    <div id="map" ref="rootmap">
        <div style="position: absolute;z-index: 9999; background: ghostwhite;display: flex;align-items: center;padding: 10px;">
            <input type="checkbox" value="select" ref="select" @click="selectAction"/>选择
            <input type="checkbox" value="modify" ref="modify" @click="modifyAction" style="margin-left:10px; "/>编辑
            <input type="checkbox" value="add" ref="add" @click="addAction" style="margin-left:10px; "/>新增
            <input type="button" value="保存" @click="saveAction" style="margin-left:10px; "/>
            <input type="button" value="删除选中" @click="deleteFeature" style="margin-left:10px; "/>
        </div>
    </div>
</template>

<script>
    import "ol/ol.css";
    import {Map, View, Feature} from "ol";
    import TileLayer from "ol/layer/Tile";
    import VectorLayer from "ol/layer/Vector"
    import {OSM, TileWMS, Vector} from "ol/source";
    import GeoJSON from "ol/format/GeoJSON";
    import {Style, Stroke} from "ol/style";
    import Draw from "ol/interaction/Draw";
    import {defaults, FullScreen, MousePosition, OverviewMap, ScaleLine, ZoomSlider, ZoomToExtent} from "ol/control"
    import {Select, Modify} from "ol/interaction";
    import {WFS} from "ol/format";
    import {MultiLineString} from "ol/geom";

    export default {
        data() {
            return {
                map: null,
                newId: 1,
                action: [],

                wfsVectorLayer: null,
                drawLayer: null,

                selectInteraction: null,
                modifyInteraction: null,
                drawInteraction: null,

                modifiedFeatures: null,
                drewFeature: null
            };
        },
        mounted() {
            let osmLayer = new TileLayer({
                source: new OSM()
            });

            // 创建用于新绘制feature的layer
            this.drawLayer = new VectorLayer({
                source: new Vector(),
                style: new Style({
                    stroke: new Stroke({
                        color: 'blue',
                        width: 5
                    })
                })
            });

            // 选取feature的交互类
            this.selectInteraction = new Select({
                style: new Style({
                    stroke: new Stroke({
                        color: 'red',
                        width: 2
                    })
                })
            });
            // 修改feature的交互类
            this.modifyInteraction = new Modify({
                style: new Style({
                    stroke: new Stroke({
                        color: 'red',
                        width: 5
                    })
                }),
                //这个要修改的feature是我们之前通过ol.interaction.Select选中的feature
                features: this.selectInteraction.getFeatures()
            });
            //监听修改feature
            this.modifyInteraction.on('modifyend', this.modifyEnd);

            // 绘制feature的交互类
            this.drawInteraction = new Draw({
                type: 'LineString', // 设定为线条
                style: new Style({
                    stroke: new Stroke({
                        color: 'red',
                        width: 10
                    })
                }),
                source: this.drawLayer.getSource()
            });
            //监听绘制feature
            this.drawInteraction.on('drawend', this.drawEnd);

            this.map = new Map({
                target: "map",
                layers: [
                    osmLayer
                    , this.drawLayer
                ],
                view: new View({
                    center: [-73.99710639567148, 40.742270050255556],
                    maxZoom: 19,
                    zoom: 14,
                    projection: 'EPSG:4326'
                })
            });

            this.queryWfs();

        },

        methods: {
            // 加载wfs图层
            queryWfs() {
                // 如果已经加载过了wfs图层,那我们就移除这个图层,然后重新添加
                if (this.wfsVectorLayer) {
                    this.map.removeLayer(this.wfsVectorLayer);
                }

                // 创建新的图层来加载wfs的要素,即重新加载wfs矢量图层
                this.wfsVectorLayer = new VectorLayer({
                    source: new Vector({
                        format: new GeoJSON({
                            geometryName: 'the_geom' // 因为数据源里面字段the_geom存储的是geometry,所以需要指定
                        }),
                        url: 'http://localhost:8088/geoserver/wfs?service=wfs&version=1.1.0&request=GetFeature&typeNames=nyc_roads:nyc_roads&outputFormat=application/json&srsname=EPSG:4326'
                    }),
                    //使用style+feature的方式修改添加的矢量图层的feature样式
                    style: function (feature, resolution) {
                        return new Style({
                            stroke: new Stroke({
                                color: 'blue',
                                width: 5
                            })
                        });
                    }
                });
                this.map.addLayer(this.wfsVectorLayer);
            },
            //选择地图要素时执行的方法
            selectAction() {
                if (this.$refs["select"].checked) {
                    this.$refs["add"].checked = false;
                    // 勾选选择复选框时,添加选择器到地图。先移除后添加
                    this.map.removeInteraction(this.selectInteraction);
                    this.map.addInteraction(this.selectInteraction);
                } else {
                    // 不勾选选择复选框的情况下,移出选择器和修改器
                    //移除选择器
                    this.map.removeInteraction(this.selectInteraction);
                    //把修改的checkbox置为false
                    this.$refs["modify"].checked = false;
                    //移除修改选择器
                    this.map.removeInteraction(this.modifyInteraction);
                    //把修改features置为空
                    this.modifiedFeatures = null;
                }
            },
            //修改地图要素时执行的方法
            modifyAction() {
                if (this.$refs["modify"].checked) {
                    // 勾选修改复选框时,添加选择器和修改器到地图
                    //把选择地图要素的checkbox置为true
                    this.$refs["select"].checked = true;
                    this.$refs["add"].checked = false;
                    //先移除修改器
                    this.map.removeInteraction(this.modifyInteraction);
                    //再添加修改器
                    this.map.addInteraction(this.modifyInteraction);
                    //先移除选择器
                    this.map.removeInteraction(this.selectInteraction);
                    //再添加选择器
                    this.map.addInteraction(this.selectInteraction);
                } else {
                    // 不勾选修改复选框时,移出修改器
                    this.map.removeInteraction(this.modifyInteraction);
                    this.modifiedFeatures = null;
                }
            },
            addAction() {
                if (this.$refs["add"].checked) {
                    this.$refs["select"].checked = false;
                    this.$refs["modify"].checked = false;
                    // 勾选新增复选框时,添加绘制的Interaction
                    this.map.removeInteraction(this.drawInteraction);
                    this.map.addInteraction(this.drawInteraction);
                } else {
                    // 取消勾选新增复选框时,移出绘制的Interaction,删除已经绘制的feature
                    this.map.removeInteraction(this.drawInteraction);
                    if (this.drewFeature) {
                        this.drawLayer.getSource().removeFeature(this.drewFeature);
                    }
                    this.drewFeature = null;
                }
            },
            modifyEnd(e) {
                console.log(e);
                // 把修改完成的feature暂存起来
                this.modifiedFeatures = e.features;
            },
            drawEnd(e) {
                // 绘制结束时暂存绘制的feature
                this.drewFeature = e.feature;
            },
            saveAction() {
                if (this.$refs["add"].checked) {
                    this.onSaveNew();
                }
                if (this.$refs["modify"].checked) {
                    this.onSaveModify();
                }
            },
            deleteFeature() {
                // 删选择器选中的feature
                if (this.selectInteraction.getFeatures().getLength() > 0) {
                    this.deleteWfs([this.selectInteraction.getFeatures().item(0)]);
                    // 3秒后自动更新features
                    setTimeout(() => {
                        this.selectInteraction.getFeatures().clear();
                        this.queryWfs();
                    }, 3000);
                }
            },
            // 保存已经编辑的要素
            onSaveModify() {
                //如果修改的地图要素不为空
                console.log("save wfs....0")
                if (this.modifiedFeatures && this.modifiedFeatures.getLength() > 0) {
                    // 转换坐标
                    let modifiedFeature = this.modifiedFeatures.item(0).clone();
                    // 注意ID是必须,通过ID才能找到对应修改的feature
                    modifiedFeature.setId(this.modifiedFeatures.item(0).getId());
                    // 调换经纬度坐标,以符合wfs协议中经纬度的位置
                    modifiedFeature.getGeometry().applyTransform(function (flatCoordinates, flatCoordinates2, stride) {
                        for (let j = 0; j < flatCoordinates.length; j += stride) {
                            let y = flatCoordinates[j];
                            let x = flatCoordinates[j + 1];
                            flatCoordinates[j] = x;
                            flatCoordinates[j + 1] = y;
                        }
                    });

                    console.log("save wfs....1")
                    //把修改提交到服务端
                    this.modifyWfs([modifiedFeature]);
                }
            },
            // 把修改提交到服务器端
            modifyWfs(features) {
                console.log("save wfs....")
                let WFSTSerializer = new WFS();
                let featObject = WFSTSerializer.writeTransaction(null,
                    features, null, {
                        featureType: 'nyc_roads',
                        featureNS: 'http://geoserver.org/nyc_roads',  // 注意这个值必须为创建工作区时的命名空间URI
                        srsName: 'EPSG:4326'      //参考坐标系
                    });
                // 转换为xml内容发送到服务器端
                let serializer = new XMLSerializer();
                let featString = serializer.serializeToString(featObject);
                let request = new XMLHttpRequest();
                request.open('POST', 'http://localhost:8088/geoserver/wfs?service=wfs');
                // 指定内容为xml类型
                request.setRequestHeader('Content-Type', 'text/xml');
                request.send(featString);
            },

            // 保存新绘制的feature
            onSaveNew() {
                // 转换坐标
                let geometry = this.drewFeature.getGeometry().clone();
                geometry.applyTransform(function (flatCoordinates, flatCoordinates2, stride) {
                    for (let j = 0; j < flatCoordinates.length; j += stride) {
                        let y = flatCoordinates[j];
                        let x = flatCoordinates[j + 1];
                        flatCoordinates[j] = x;
                        flatCoordinates[j + 1] = y;
                    }
                });

                // 设置feature对应的属性,这些属性是根据数据源的字段来设置的
                let newFeature = new Feature();
                newFeature.setId('nyc_roads.new.' + this.newId);
                newFeature.setGeometryName('the_geom');
                newFeature.set('the_geom', null);
                newFeature.set('name', newFeature.getId());
                newFeature.set('modified', newFeature.getId());
                newFeature.set('vsam', 0);
                newFeature.set('sourcedate', '');
                newFeature.set('sourcetype', '');
                newFeature.set('source_id', this.newId);
                newFeature.set('borough', '');
                newFeature.set('feat_code', 0);
                newFeature.set('feat_desc', '11');
                newFeature.set('feat_type', 0);
                newFeature.set('exported', 'true');
                newFeature.setGeometry(new MultiLineString([geometry.getCoordinates()]));

                this.addWfs([newFeature]);
                // 更新id
                this.newId = this.newId + 1;
                // 3秒后,自动刷新页面上的feature
                setTimeout(() => {
                    this.drawLayer.getSource().clear();
                    this.queryWfs();
                }, 3000);
            },

            // 添加到服务器端
            addWfs(features) {
                let WFSTSerializer = new WFS();
                let featObject = WFSTSerializer.writeTransaction(features,
                    null, null, {
                        featureType: 'nyc_roads',
                        featureNS: 'http://geoserver.org/nyc_roads',
                        srsName: 'EPSG:4326'
                    });
                let serializer = new XMLSerializer();
                let featString = serializer.serializeToString(featObject);
                let request = new XMLHttpRequest();
                request.open('POST', 'http://localhost:8088/geoserver/wfs?service=wfs');
                request.setRequestHeader('Content-Type', 'text/xml');
                request.send(featString);
            },
            // 在服务器端删除feature
            deleteWfs(features) {
                let WFSTSerializer = new WFS();
                let featObject = WFSTSerializer.writeTransaction(null,
                    null, features, {
                        featureType: 'nyc_roads',
                        featureNS: 'http://geoserver.org/nyc_roads',
                        srsName: 'EPSG:4326'
                    });
                let serializer = new XMLSerializer();
                let featString = serializer.serializeToString(featObject);
                let request = new XMLHttpRequest();
                request.open('POST', 'http://localhost:8088/geoserver/wfs?service=wfs');
                request.setRequestHeader('Content-Type', 'text/xml');
                request.send(featString);
            }

        }
    };
</script>

<style>
    #map {
        height: 100%;
    }

    /*隐藏ol的一些自带元素*/
    .ol-attribution, .ol-zoom {
        display: none;
    }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值