在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