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

news2025/2/25 20:21:14

在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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1585648.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【线段树】2213. 由单个字符重复的最长子字符串

算法可以发掘本质&#xff0c;如&#xff1a; 一&#xff0c;若干师傅和徒弟互有好感&#xff0c;有好感的师徒可以结对学习。师傅和徒弟都只能参加一个对子。如何让对子最多。 二&#xff0c;有无限多1X2和2X1的骨牌&#xff0c;某个棋盘若干格子坏了&#xff0c;如何在没有坏…

Compose UI 之 Card 卡片组件

Card Card 是用于显示带有圆角和可选阴影的矩形内容容器。它通常用于构建用户界面,并可以包含标题、文本、图像、按钮等元素,表示界面上的可交互元素,我们称它是 “卡片”。 Card 使用的一些经典的场景: 列表数据,例如 新闻列表,产品列表等。信息提示框,使用 Card 组件…

Canon佳能打印机在扫描时会提示缺少组件解决方法

问题截图 解决方法 1.复制佳能驱动网址&#xff1a;下载与支持 – 服务与支持 - 佳能&#xff08;中国&#xff09;到浏览器访问&#xff0c;输入打印机型号&#xff0c;点击驱动程序。 2.在驱动程序栏目下&#xff0c;下载并安装打印机驱动。 3.点击应用程序栏目&#xff0c;…

计算机中的数字表示:正码、反码和补码

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 在计算机科学领域&#xff0c;数字表示是一个基础而且至关重要的概念。正码、反码和补码是计算机中常 目录 &am…

uniapp开发小程序,实现堆叠卡片轮播图

一、实现堆叠卡片轮播图: 需求: 实现堆叠轮播图效果堆叠到后面的图片有虚化效果可以在堆叠图片上写文字或叠加图片等效果可以手动滑动&#xff0c;也可以定时自动轮播 二、代码实现&#xff1a; 1.封装一个组件myswiper.vue <!-- 折叠轮播图 组件--> <template>…

Linux从入门到精通 --- 3.用户、权限

文章目录 第三章&#xff1a;3.1 root用户3.1.1 su3.1.2 exit3.1.3 sudo 3.2 用户和用户组3.2.1 用户组管理创建用户组删除用户组 3.2.2 用户管理创建用户删除用户查看用户所属组修改用户所属组 3.2.3 getent一&#xff1a;二&#xff1a; 3.3 查看权限控制信息3.3.1 认知权限信…

蓝桥杯算法题:栈(Stack)

这道题考的是递推动态规划&#xff0c;可能不是很难&#xff0c;不过这是自己第一次靠自己想出状态转移方程&#xff0c;所以纪念一下&#xff1a; 要做这些题目&#xff0c;首先要把题目中会出现什么状态给找出来&#xff0c;然后想想他们的状态可以通过什么操作转移&#xf…

DSP笔记8-通用GPIO

电源类 AD引脚类 系统相关JTAG 时钟 GPIO (general purpose input output)复用&#xff0c; 复用&#xff0c;I/O引脚&#xff0c;外设的功能引脚&#xff0c; 88个GPIO引脚&#xff0c;通用的输入输出口&#xff0c;功能复用的。 GPIO特点 输入电平与TTL电平兼容。>2.0V…

html 实现多个文本内容,轮播效果类似gif图片

前几日&#xff0c;产品要求后端实现一个将文字转为gif图片&#xff0c;要用于官网首页广告栏。我想这不是前段就能实现吗&#xff0c;怎么还要求后端生成gif&#xff0c;然后前段在展示。你确定招的前段不是对手公司过来的卧底&#xff1f;&#xff1f;&#xff1f; <!DOCT…

SSL中的CA证书

目录 一、CA概述 二、数据加密 三、身份认证 一、CA概述 SSL如何保证网络通信的安全和数据的完整性呢&#xff1f;就是采用了两种手段&#xff1a;身份认证和数据加密。身份认证就需要用到CA证书。 CA是证书的签发机构&#xff0c;它是公钥基础设施&#xff08;Public Key In…

neo4j-01

Neo4j是&#xff1a; 开源的&#xff08;社区版开源免费&#xff09;无模式&#xff08;不用预设数据的格式&#xff0c;数据更加灵活&#xff09;noSQL&#xff08;非关系型数据库&#xff0c;数据更易拓展&#xff09;图数据库&#xff08;使用图这种数据结构作为数据存储方…

腾讯云4核8G服务器多少钱?4核8G能干啥?

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

USB主机驱动分析

对于usb控制器来讲&#xff0c;不管是dwc3,还是xhci,ehci,ohci这些接口标准&#xff1b;如果半导体厂商是用这些标准来实现的usb控制器&#xff1b;那他们基本上给可以用这些核的通用驱动&#xff0c;因为通用寄存器都是一致的。 在Linux内核中&#xff0c;用usb_hcd结构体描述…

C++初阶 | [十二] 模板进阶

摘要&#xff1a;非类型模板参数&#xff0c;类模板的特化&#xff0c;模板的分离编译&#xff0c;模板总结 前言&#xff1a;C初阶终篇 1. 非类型模板参数 类型模板参数&#xff1a;如下代码&#xff0c;T 为模板的类型参数。 #define N 10 template<class T> class …

如何理解单片机 pwm 控制的基本原理?

单片机PWM&#xff08;脉宽调制&#xff09;控制的基本原理&#xff0c;简而言之&#xff0c;就是通过改变脉冲信号的宽度&#xff08;占空比&#xff09;来控制模拟电路。这涉及到单片机生成一系列脉冲信号&#xff0c;每个脉冲信号的高电平持续时间和整个周期的比值&#xff…

4051A/B/C/D/E–S信号/频谱分析仪

4051A/B/C/D/E–S信号/频谱分析仪 频段26.5GHz 计数分辨率0.001Hz 同轴频率26.5GHz 4051-S系列信号/频谱分析仪可广泛应用于移动通信、汽车电子、物联网、半导体等领域的信号及设备测试。 PART.01 产品综述 —— 频率范围覆盖&#xff1a;9kHz~26.5GHz# 4051-S系列信号/频…

一起找bug之购物

如果不是购物车满了&#xff0c;大概都不会发现这个 bug 淘宝 APP 修复了购物车满的情况下&#xff0c;往里面添加新商品时&#xff0c;会把一个老商品移入收藏夹&#xff0c; 但是如果这个老商品是已失效状态&#xff0c;就无法自动移入收藏夹&#xff0c;而且会一直在购物车…

python-study-day1-(病人管理系统-带sql)

MainWindow代码 from tkinter import * from tkinter import messagebox from tkinter.ttk import Comboboxclass MianWindow(Frame):def __init__(self, masterNone):super().__init__(master, padx30, pady20)self.flag 0self.pack(expandTrue, fillBOTH)self.id StringVa…

C语言面试题之合法二叉搜索树

合法二叉搜索树 实例要求 实现一个函数&#xff0c;检查一棵二叉树是否为二叉搜索树&#xff1b; 示例 1: 输入:2/ \1 3 输出: true 示例 2: 输入:5/ \1 4/ \3 6 输出: false 解释: 输入为: [5,1,4,null,null,3,6]。根节点的值为 5 &#xff0c;但是其右子节点值为 4 …

pmp就是智商税?

首先要明白的是&#xff0c;证书的价值并不在于证书本身&#xff0c;而在于学习过程中所获得的知识和经验&#xff0c;这才是证书真正的价值&#xff0c;是无法被复制的个人能力。 学习和考证都是经验的积累&#xff0c;通过这个过程可以不断地获取所需的知识&#xff0c;并加…