openlayer实现webgis端绘制制图及编辑

时间:2024-04-11 19:33:59

在WebGIS端制图是指通过Web浏览器界面实现地理信息数据的可视化、编辑、分析以及地图产品的制作。这一过程通常涉及以下几个关键环节:

**1. 前端技术栈:

•HTML/CSS/JavaScript:作为Web开发的基础,用于构建用户界面布局、样式设计以及交互逻辑。

•Web地图库:

•Leaflet、OpenLayers、Mapbox GL JS等开源库,提供地图容器、图层管理、交互控件等功能,便于快速构建Web地图应用。

•GIS服务接口:

•Web Map Service (WMS):用于请求地图影像瓦片。

•Web Feature Service (WFS):用于获取矢量地理数据。

•Web Coverage Service (WCS):用于获取栅格数据。

•GeoJSON、TopoJSON、KML等数据格式,直接嵌入或通过API加载。

•地图样式与主题:

•Mapbox Studio、QGIS Cloud等在线工具,或使用JSON-based样式语言(如Mapbox Style Specification、OpenLayers Style Format)自定义地图样式。

**2. 数据准备与集成:

•数据源接入:

连接本地或远程GIS服务器、云存储(如AWS S3、Azure Blob Storage)、开放数据平台(如OpenStreetMap、ArcGIS Online)等,获取所需GIS数据。

•数据处理:

•对接地理编码服务(如Google Maps Geocoding API、Mapbox Geocoding API)进行地址解析或逆地理编码。

•使用客户端库(如Turf.js、Mapbox GL Geostats)进行空间分析、统计计算。

•缓存与优化:

•利用客户端缓存(如IndexedDB、localStorage)存储常用数据或结果,提高加载速度。

•使用矢量瓦片、切片金字塔技术优化大规模数据展示。

**3. 地图交互设计:

•基础交互:缩放、平移、旋转地图,开启/关闭图层,调整图层透明度等。

•高级交互:

•查询与标注:实现点击查询、范围选择、热点分析等功能,显示相关信息或动态标注。

•编辑与绘图:支持用户在地图上添加、修改、删除地理要素,如使用Leaflet.draw、OpenLayers Editing等插件。

•路由规划:集成路线规划服务(如OSRM、GraphHopper),提供驾车、步行、骑行路径规划。

•空间分析:在浏览器端进行简单分析操作,如缓冲区分析、叠加分析等。

**4. 地图定制与输出:

•地图样式定制:通过调整颜色、图标、文字样式等元素,创建符合项目主题的地图样式。

•地图打印与导出:

•实现Web页面打印预览,支持自定义打印范围、比例尺、布局等。

•提供地图截图、PDF导出功能,或集成地图打包服务(如Mapfish Print、GeoServer Print)。

•地图分享与嵌入:

•提供短链接、嵌入代码,方便用户将地图分享至社交媒体或嵌入到其他网站。

**5. 跨平台兼容与响应式设计:

•移动端适配:确保地图应用在不同尺寸的移动设备上具有良好用户体验,如使用触屏手势、自适应布局。

•多浏览器支持:测试在主流浏览器(Chrome、Firefox、Safari、Edge)上的兼容性。

**6. 性能监控与优化:

•性能指标跟踪:监控地图加载时间、内存占用、网络请求等指标,识别性能瓶颈。

•优化措施:

•压缩与合并静态资源,减少HTTP请求。

•使用Web Workers进行大数据处理,避免阻塞主线程。

•采用矢量瓦片、LOD(Level of Detail)策略,按需加载数据。通过上述技术手段和流程,WebGIS端制图实现了从数据获取、处理到地图展现、交互的全流程操作,让用户能够在Web浏览器环境中便捷地进行地图制作与分析,满足各种GIS应用场景的需求。

关键代码实现

组件化代码:

import Draw from 'ol/interaction/Draw'
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';

import {unByKey} from 'ol/Observable.js';
import Overlay from 'ol/Overlay';
import {getArea, getLength} from 'ol/sphere.js';
import View from 'ol/View';
import {LineString, Polygon} from 'ol/geom.js';
import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style.js';
import {generateUUID, getLayerByCode} from "@/components/iClientOpenLayers/MapCommon";

var DrawMap= /** @class */ (function () {
    function DrawMap(map, drawMapType,drawType,freeHand) {
        /**
         * Currently drawn feature.
         * @type {module:ol/Feature~Feature}
         */
        this.sketch=null;

        this.freeHand=freeHand;


        /**
         * The help tooltip element.
         * @type {Element}
         */
        this.helpTooltipElement=null;


        /**
         * Overlay to show the help messages.
         * @type {module:ol/Overlay}
         */
        this.helpTooltip=null;


        /**
         * The drawMap tooltip element.
         * @type {Element}
         */
        this.drawMapTooltipElement=null;


        /**
         * Overlay to show the drawMapment.
         * @type {module:ol/Overlay}
         */
        this.drawMapTooltip=null;


        /**
         * Message to show when the user is drawing a polygon.
         * @type {string}
         */
        this.continuePolygonMsg = '继续点击绘制多边形';


        /**
         * Message to show when the user is drawing a line.
         * @type {string}
         */
        this.continueLineMsg = '继续点击绘制线';
        this.map=map;
        this.drawMapType=drawMapType;
        this.draw=null;
        this.listener=null;
        this.source=null;
        // var layer ;
        // 获取存放feature的vectorlayer层。map初始化的时候可以添加好了
        for(let layerTmp of map.getLayers().getArray()){
            if(layerTmp.get("name")=="DrawMap"){
                this.source= layerTmp.getSource();
            }
        }
        if(this.source==undefined||this.source==null){
            this.source=getLayerByCode(this.map,"CRegion").getSource()
            // this.source = new VectorSource();

            // var vector = new VectorLayer({
            //     source: this.source,
            //     style: new Style({
            //         fill: new Fill({
            //             color: 'rgba(255, 255, 255, 0.2)',
            //         }),
            //         stroke: new Stroke({
            //             color: '#ffcc33',
            //             width: 2,
            //         }),
            //         image: new CircleStyle({
            //             radius: 7,
            //             fill: new Fill({
            //                 color: '#ffcc33',
            //             }),
            //         }),
            //     }),
            // });
            // vector.set("name","DrawMap");
            // vector.set("code","DrawMap");
            // this.map.addLayer(vector);
        }
        if(drawType=="DrawMap"){
            this.createDrawMapTooltip();
            this.createHelpTooltip();
            let that=this;
            this.pointerMoveHandler = function (evt) {
                if (evt.dragging) {
                    return;
                }
                /** @type {string} */
                var helpMsg = '请点击开始绘制';

                if (that.sketch) {
                    var geom = (that.sketch.getGeometry());
                    if (geom instanceof Polygon) {
                        helpMsg = that.continuePolygonMsg;
                    } else if (geom instanceof LineString) {
                        helpMsg = that.continueLineMsg;
                    }
                }

                that.helpTooltipElement.innerHTML = helpMsg;
                that.helpTooltip.setPosition(evt.coordinate);

                that.helpTooltipElement.classList.remove('hidden');
            };
            /**
             * Handle pointer move.
             * @param {module:ol/MapBrowserEvent~MapBrowserEvent} evt The event.
             */
            map.on('pointermove', this.pointerMoveHandler);

            map.getViewport().addEventListener('mouseout',() =>{
                this.helpTooltipElement.classList.add('hidden');
            });
            // 量测调用
            this.addInteraction();
        }else if(drawType=="Draw"){
            // 量测调用
            this.addInteractionEx();
        }

    };
    DrawMap.prototype. formatLength = function (line) {
        var length = getLength(line,{projection:'EPSG:4326'});
        var output;
        if (length > 100) {
            output = (Math.round(length / 1000 * 100) / 100) +
                ' ' + 'km';
        } else {
            output = (Math.round(length * 100) / 100) +
                ' ' + 'm';
        }
        return output;
    };
    DrawMap.prototype. formatArea = function (polygon) {
        var area = getArea(polygon,{projection:'EPSG:4326'});
        var output;
        if (area > 10000) {
            output = (Math.round(area / 1000000 * 100) / 100) +
                ' ' + 'km<sup>2</sup>';
        } else {
            output = (Math.round(area * 100) / 100) +
                ' ' + 'm<sup>2</sup>';
        }
        return output;
    };
    DrawMap.prototype.addInteraction=function() {
        var type = (this.drawMapType == 'area' ? 'Polygon' : 'LineString');
        this.draw = new Draw({
            ol_uid:'draw',
            source: this.source,
            type: type,
            snapTolerance:20,
            freehand: this.freeHand,
            style: new Style({
                fill: new Fill({
                    color: 'rgba(255, 255, 255, 0.2)'
                }),
                stroke: new Stroke({
                    color: 'rgba(0, 0, 0, 0.5)',
                    lineDash: [10, 10],
                    width: 2
                }),
                image: new CircleStyle({
                    radius: 5,
                    stroke: new Stroke({
                        color: 'rgba(0, 0, 0, 0.7)'
                    }),
                    fill: new Fill({
                        color: 'rgba(255, 255, 255, 0.2)'
                    })
                })
            })
        });
        this.map.addInteraction(this.draw);


        this.draw.on('drawstart',
            (evt)=> {
                // set sketch
                this.sketch = evt.feature;

                /** @type {module:ol/coordinate~Coordinate|undefined} */
                var tooltipCoord = evt.coordinate;

                this.listener = this.sketch.getGeometry().on('change', (evt)=> {
                    var geom = evt.target;
                    var output;
                    if (geom instanceof Polygon) {
                        output = this.formatArea(geom);
                        tooltipCoord = geom.getInteriorPoint().getCoordinates();
                    } else if (geom instanceof LineString) {
                        output = this.formatLength(geom);
                        tooltipCoord = geom.getLastCoordinate();
                    }
                    this.drawMapTooltipElement.innerHTML = output;
                    this.drawMapTooltip.setPosition(tooltipCoord);
                });
            }, this);

        this.draw.on('drawend',
            (e)=> {
                this.drawMapTooltipElement.className = 'ol-tooltip ol-tooltip-static';
                this.drawMapTooltip.setOffset([0, -7]);
                let cFeature = e.feature;
                cFeature.values_["ID"]=generateUUID()
                // unset sketch
                this.clearDraw();
            }, this);
    };

    DrawMap.prototype.addInteractionEx=function(){
        var type = (this.drawMapType == 'area' ? 'Polygon' : 'LineString');
        if(this.drawMapType=="Point"){
            type=this.drawMapType;
        }
        this.draw = new Draw({
            source: this.source,
            type: type,
            freehand: this.freeHand,
            snapTolerance:20,
        });
        this.draw.on('drawend',
            (e)=> {
                const geometry = e.feature.getGeometry()
                const corrdinates = geometry.getCoordinates()
                let cFeature = e.feature;
                cFeature.values_["ID"]=generateUUID()
                // unset sketch
                this.clearDraw();
            }, this);
        this.map.addInteraction(this.draw);
    };

    DrawMap.prototype.createDrawMapTooltip=function() {
        if (this.drawMapTooltipElement) {
            this.drawMapTooltipElement.parentNode.removeChild(this.drawMapTooltipElement);
        }
        this.drawMapTooltipElement = document.createElement('div');
        this.drawMapTooltipElement.className = 'ol-tooltip ol-tooltip-drawMap';
        this.drawMapTooltip = new Overlay({
            element: this.drawMapTooltipElement,
            offset: [0, -15],
            positioning: 'bottom-center'
        });
        this.drawMapTooltip.set("name","DrawMap");
        debugger
        this.drawMapTooltip.getElement().style.display = ''
        this.map.addOverlay(this.drawMapTooltip);
    };
    DrawMap.prototype.createHelpTooltip=function () {
        if (this.helpTooltipElement) {
            this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
        }
        this.helpTooltipElement = document.createElement('div');
        this.helpTooltipElement.className = 'ol-tooltip hidden';
        this.helpTooltip = new Overlay({
            element: this.helpTooltipElement,
            offset: [15, 0],
            positioning: 'center-left'
        });
        this.map.addOverlay(this.helpTooltip);
    };

    DrawMap.prototype.clearDraw=function () {
        debugger
        this.sketch = null;
        // unset tooltip so that a new one can be created
        this.drawMapTooltipElement = null;
        // this.createDrawMapTooltip();
        if(this.listener!=undefined&&this.listener!=null){
            unByKey(this.listener);
        }
        try{
            this.map.un('pointermove', this.pointerMoveHandler);
        }
        catch (e){

        }
        if(this.draw!=undefined&&this.draw!=null){
            for(let i = 0; i < this.map.interactions.array_.length; i++){
                if(this.draw.ol_uid == this.map.interactions.array_[i].ol_uid){
                    this.map.removeInteraction(this.map.interactions.array_[i]);break
                }
            }
        }
        if(this.helpTooltipElement!=undefined&&this.helpTooltipElement!=null){
            this.helpTooltipElement.classList.add('hidden');
        }
        if(this.helpTooltip!=undefined&&this.helpTooltip!=null){
            this.map.removeOverlay(this.helpTooltip);
        }

    };
    return DrawMap;
}());

export {DrawMap};

调用代码:

//绘制制图切换
drawMapSwitch(pIndex){
  if(this.curDraw!=null){
    this.curDraw.clearDraw();
    this.curDraw=null;
  }
  if(pIndex==0){
    this.curDraw=new DrawMap(this.sMap, "LineString","Draw",false);
  }else if(pIndex==1){
    this.curDraw=new DrawMap(this.sMap, "area","Draw",false);
  }else if(pIndex==2){
    this.curDraw=new DrawMap(this.sMap, "Point","Draw",false);
  }else if(pIndex==3){
    this.curDraw=new DrawMap(this.sMap, "LineString","Draw",true);
  }else if(pIndex==4){
    this.curDraw=new DrawMap(this.sMap, "area","Draw",true);
  }else if(pIndex==5){
    for(let layerTmp of this.sMap.getLayers().getArray()){
      if(layerTmp.get("code")=="CRegion"){
        layerTmp.getSource().clear();
        break;
      }
    }
    for(let i=0;i<this.sMap.getOverlays().getArray().length;i++){
      if(this.sMap.getOverlays().getArray()[i].get("code")=="CRegion"){
        this.sMap.removeOverlay(this.sMap.getOverlays().getArray()[i]);
        i--;
      }
    }
  }else if(pIndex==6){
    let geojson=getGeojsonByLayerCode(this.sMap,"CRegion")
    alert(geojson)
  }

}

前端代码设计

<div class="operate map_huizhi">
  <i class="icon huizhi"></i>
  <ul class="map-sub-nav">
    <li v-for="(item,index) in drawMapData" @click="drawMapSwitch(index)">{{item}}</li>
  </ul>
</div>
.huizhi{
  width: 38px;
  height: 38px;
  background: url('./img/huizhi.png') no-repeat center center;
  background-image: -webkit-image-set(url('./img/huizhi.png') 1x, url("./img/huizhi@2x.png") 2x);
  background-image: image-set(url('./img/huizhi.png') 1x, url("./img/huizhi@2x.png") 2x);
  background-repeat: no-repeat;
  background-position: center center;
}

软件实现效果:

最后分享下地图下载器下载地址

通过百度网盘分享的文件:V-2.0jbr…
链接:https://pan.baidu.com/s/1AiFKTTknkEHkJ7t4nQ2P1g 
提取码:8664
复制这段内容打开「百度网盘APP 即可获取」

如果对您有所帮助,请点赞打赏支持!

技术合作交流qq:2401315930