import { getCenterByExtent } from '@/utils/common.js'; import { Map, View, Collection, Feature } from 'ol'; import WFS from 'ol/format/WFS.js'; import { transform, fromLonLat } from 'ol/proj'; import { Point } from 'ol/geom'; import TileLayer from 'ol/layer/Tile'; import XYZ from 'ol/source/XYZ'; import { Style, Stroke, Fill, Text, Circle, Icon } from 'ol/style'; import { extend, createEmpty } from 'ol/extent'; import VectorLayer from 'ol/layer/Vector'; import Image from 'ol/layer/Image'; import ImageWMS from 'ol/source/ImageWMS.js'; import VectorSource from 'ol/source/Vector'; import TileWMS from 'ol/source/TileWMS'; import GeoJSON from 'ol/format/GeoJSON'; import { get as getProjection } from 'ol/proj'; import { getWidth, getTopLeft } from 'ol/extent'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; import WKT from 'ol/format/WKT'; import WMTS from 'ol/source/WMTS'; import { Overlay } from 'ol'; const pointVectorSource = new VectorSource(); import PluggableMap from 'ol/PluggableMap'; import { MousePosition, defaults } from 'ol/control'; import { createStringXY } from 'ol/coordinate'; import { Draw, Modify } from 'ol/interaction'; import { get, post } from '@/api/index.js'; import { MultiPolygon } from 'ol/geom'; class GisUtils { map = null; overLayer = null; instance = null; constructor(domId) { this.getMap(domId) this.initView() return this } /** * 获取一个地图容器 * @param {string} domId dom节点id */ getMap (domId) { let view = new View({ projection: 'EPSG:3857' }); PluggableMap.prototype.getEventPixel = function (event) { // eslint-disable-next-line no-underscore-dangle const viewportPosition = this.viewport_.getBoundingClientRect(); let size = [viewportPosition.width, viewportPosition.height]; const view = this.getView(); if (view) { size = view.getSizeFromViewport_(); } const eventPosition = 'changedTouches' in event /** @type {TouchEvent} */ ? event.changedTouches[0] /** @type {MouseEvent} */ : event; return [ (eventPosition.clientX - viewportPosition.left) * size[0] / viewportPosition.width, (eventPosition.clientY - viewportPosition.top) * size[1] / viewportPosition.height ]; }; let mousePositionControl = new MousePosition({ coordinateFormat: createStringXY(4), projection: 'EPSG:3857', className: 'custom-mouse-position', target: document.getElementById('mouse-position'), undefinedHTML: ' ' }); let map = new Map({ target: domId, view: view, controls: defaults({ zoom: false, rotate: false, attribution: false, attributionOptions: { collapsible: false } }).extend([mousePositionControl]), layers: [ ] }); this.map = map; return this.map; } /** * 获取适量图层 * @param {*} color * @returns */ getVectorLayerByColor (color) { let layer = new VectorLayer({ renderMode: 'webgl', // 使用WebGL渲染器 source: new VectorSource(), zIndex: 101, style: new Style({ fill: new Fill({ color: color }), stroke: new Stroke({ color: color, width: 2 }) }) }); this.map.addLayer(layer); return layer; } getVectorLayerByFs (features, zIndex = 1012) { const vectorSource = new VectorSource({ features: features, }); // 创建矢量图层 let layer = new VectorLayer({ zIndex, source: vectorSource }); this.map.addLayer(layer) return layer } addLayer (layer) { this.map.addLayer(layer); } getPolygon (coordinates) { const polygon = new MultiPolygon([coordinates]); return polygon; } getFeature (info, icon) { let feature = new Feature({ geometry: new Point( fromLonLat([info.lng, info.lat]) ), deptId: info.id, deptName: info.label, orgCode: info.orgCode, lat: info.lat, lng: info.lng, deptLevel: info.deptLevel, level: 'deptPoint', }) feature.setStyle(this.createFeatureStyle(feature, icon)); return feature; } getFeature2 (info, icon) { let feature = new Feature({ geometry: new Point( JSON.parse(info.theGeom).coordinates ), detail: info, level: 'permanent', }) feature.setStyle(this.createFeatureStyle(feature, icon)); return feature; } getFeature3 (item, fill, stroke) { let feature = new Feature({ geometry: new MultiPolygon(JSON.parse(item.theGeom).coordinates), // name: item.centerName, // centerDeptId: item.deptId, level: 'resource', detail: item }) feature.setStyle(this.createMultiPolygonFeatureStyle(fill, stroke)); return feature; } createMultiPolygonFeatureStyle (fill, stroke) { return new Style({ fill: new Fill({ //矢量图层填充颜色,以及透明度 color: fill, }), stroke: new Stroke({ //边界样式 color: stroke, width: 2, }) }); } // 设置坐标点的样式 createFeatureStyle (feature, locationIcon) { return new Style({ image: new Icon({ //设置图标偏移 anchor: [0.5, 1], //标注样式的起点位置 anchorOrigin: "top-right", //X方向单位:分数 anchorXUnits: "fraction", //Y方向单位:像素 anchorYUnits: "pixels", //偏移起点位置的方向 offsetOrigin: "top-right", //透明度 opacity: 0.9, // rotation: (Math.PI / 180) * 20, //图片路径 src: locationIcon, }), text: new Text({ textAlign: "center", //位置 textBaseline: "bottom", //基准线 font: "normal 14px 微软雅黑", //文字样式 text: feature.get("deptName"), //文本内容 fill: new Fill({ //文本填充样式(即文字颜色) color: "#ffff00", }), }) }); } writeFeature (fs) { return new GeoJSON().writeFeature(fs); } addModify (layer) { const modify = new Modify({ features: new Collection(layer.getSource().getFeatures()), pixelTolerance: 4 }); return modify; } drawLineSplit (source) { let draw = new Draw({ source: source, type: "LineString", style: new Style({ image: new Circle({ radius: 5, fill: new Fill({ color: "#03a9f4" }) }), stroke: new Stroke({ color: "#03a9f4", width: 2 }), fill: new Fill({ color: "rgba(255, 255, 255, 0.7)" }) }) }); return draw; } getMapContainer () { return this.map; } /** * 键入弹窗 * @param {*} el dom * @param {*} coord 位置 */ addOverlay (el, coord) { let overLayer = new Overlay({ element: el, // 挂载元素 position: coord, className: 'overLay', positioning: 'top-center', autoPanAnimation: { duration: 250 }, stopEvent: true }); this.map.addOverlay(overLayer); this.overLayer = overLayer; } /** * 移除弹窗 */ removeOverlay () { if (this.overLayer) { this.map.addOverlay(this.overLayer); } } /** * 飞入动画 */ flyTo (duration = 2000, position = [0, 0]) { this.map.getView().animate({ center: position, duration: duration }); } /** * * @param {*} evt 事件 * @param {*} layerName 图层名称 * @param {*} owsUrl 请求路径 */ async getFeatureByEvt (evt, layerName, owsUrl) { let point = new Point([evt.coordinate[0], evt.coordinate[1]]); let cql_filter = `INTERSECTS (the_geom,${new WKT().writeGeometry(point)})`; const params = this.getWFSparams(layerName, cql_filter); const feature = await this.getFeaturesByWFS(owsUrl, params); return feature; } /** * 获取一个视图 * @param {array} extent 四至 * @param {int} zoom 缩放级别 * @param {int} minZoom 最小缩放级别 * @param {int} maxZoom 最大缩放级别 */ initView (extent = [100, 100, 100, 100], zoom = 4, minZoom = 3, maxZoom = 18.4) { let view = new View({ center: getCenterByExtent(extent), zoom, minZoom, maxZoom, projection: 'EPSG:3857' }); this.map.setView(view); return view; } getView () { return this.map.getView() } /** * 获取WMS图层 * @param {*} wmsurl wms路径 * @param {*} layerName 图层名 * @param {*} isShow 是否隐藏 */ getWMSLayer (wmsurl, layerName, isShow = false, zIndex = 50) { let layer = new TileLayer({ visible: isShow, zIndex, source: new TileWMS({ url: wmsurl, params: { 'LAYERS': layerName, 'TILED': true, SRID: 4326 }, transition: 0 }) }); this.map.addLayer(layer); return layer; } /** * 获取天地图图层 * @param {string} url 最小缩放级别 */ addTianDiTuLayer (visible = true) { let tianDitTu = new TileLayer({ visible, source: new XYZ({ url: 'http://t{0-7}.tianditu.com/img_w/wmts?' + 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles' + '&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=cc4aba6e967096098249efa069733067' }) }); this.map.addLayer(tianDitTu); return tianDitTu; } /** * 获取天地图图层 * @param {string} url 最小缩放级别 */ addAnnotationLayer (visible = true) { let tianDitTu = new TileLayer({ visible, source: new XYZ({ url: 'http://t{0-7}.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=cc4aba6e967096098249efa069733067' }) }); this.map.addLayer(tianDitTu); return tianDitTu; } addImageLayer (mapGeoServerUrl, layerName, deptIds) { let layer = new Image({ source: new ImageWMS({ url: mapGeoServerUrl + '/wms', params: { LAYERS: layerName, cql_filter: "dept_id in (" + deptIds + ")", SRID: 3857, } }), name: 'countyBorderLayer' }); this.map.addLayer(layer) return layer } /** * 获取wfs服务请求参数 * @param {string} typename 请求的图层名 * @param {string} cql cql语句 * @return {obj} params wfs 的请求参数 */ getWFSparams (typename, cql = null) { var params = { srsName: 'EPSG:4326', service: 'WFS', version: '1.0.0', request: 'GetFeature', typename, outputFormat: 'application/json' }; if (cql) { params.CQL_FILTER = cql; } return params; } getVectorSource () { let source = new VectorSource(); return source; } getVectorLayer () { let layer = new VectorLayer({ editable: true, // 确保图层是可编辑的 source: new VectorSource(), zIndex: 101, style: new Style({ fill: new Fill({ color: [255, 80, 80, 0.5] }), stroke: new Stroke({ color: [255, 80, 80, 1], width: 2 }) }) }); this.map.addLayer(layer); return layer; } /** * 根据描边颜色获取新的要素集合 * @param {string} strokeColor 描边颜色 * @param {array} fsList 要素数组 */ getStrokeStyle (fsList, strokeColor = '#00FEFA', textName = 'NAME') { const arr = fsList.map(item => { item.setStyle( new Style({ stroke: new Stroke({ color: strokeColor, width: 1 }), text: new Text({ text: item.values_[textName], overflow: true, font: 'bold 11px serif', fill: new Fill({ color: strokeColor }) }) }) ); return item; }); return arr; } /** * 根据要素集合设置填充样式 * @param {array} fsList 要素集合 * @param {*} fillColor 填充色 * @returns 返回矢量图层 */ getFillStyle (fsList, fillColor = '#00FEFA', testName = 'XZQHMC') { fsList.forEach(item => { item.setStyle( new Style({ stroke: new Stroke({ color: fillColor, width: 2 }), text: new Text({ text: item.values_[testName], overflow: true, font: 'bold 11px serif', fill: new Fill({ color: '#00FEFA' }) }) }) ); }); return fsList; } /** * 通过wfs服务拿到要素 * @param {string} url 请求的路径 * @param {obj} params 参数 * @returns */ async getFeaturesByWFS (url, params = {}) { const res = await get(url, params); const fs = new GeoJSON().readFeatures(res); return fs; } getStyle (strokeColor = [255, 0, 0, 1], fillColor = [255, 0, 0, 0.1]) { let style = new Style({ stroke: new Stroke({ color: strokeColor, width: 2 }), fill: new Stroke({ color: fillColor }) }); return style; } changeTextName (arr, layerName) { this.textName[layerName] = arr; } getStyleWithText (strokeColor = [180, 180, 0, 1], fillColor = [180, 180, 0, 0.1]) { let style = new Style({ stroke: new Stroke({ color: strokeColor, width: 2 }), fill: new Stroke({ color: fillColor }), text: new Text({ text: '333', overflow: true, font: 'bold 11px serif', fill: new Fill({ color: '#00FEFA' }) }) }); return style; } /** *根据要素集合获得矢量图层 * @param {array} fs 要素集合 */ getVectorLayerByFsWidthText (fs, zIndex = 200, opacity = 1, strokeColor = [0, 180, 241, 1], fillColor = [0, 180, 241, 0.1], layerName) { const source = new VectorSource({ }); if (fs.length) { source.addFeatures(fs); } const layer = new VectorLayer({ renderMode: 'webgl', style: (feature) => { let arr = ''; this.textName[layerName].forEach(item => { arr += feature.getProperties()[item] + '\n'; }); return new Style({ fill: new Fill({ color: fillColor }), text: new Text({ overflow: false, text: arr, font: 'bold 12px serif', fill: new Fill({ color: '#ffffff' }) }), stroke: new Stroke({ color: strokeColor, width: 1 }) }); }, opacity, source: source, zIndex: zIndex }); this.map.addLayer(layer); return layer; } /** * 根据图层名获取瓦片图层 */ getTileLayerByLayerName (url, layerName, visible = true, zIndex = 50) { const layer = new TileLayer({ visible, zIndex: zIndex, source: new TileWMS({ url, params: { 'LAYERS': layerName, 'TILED': true, SRID: 3857 }, transition: 0 }) }); return layer; } /** * 键入弹窗 * @param {*} el dom * @param {*} coord 位置 */ addOverlay (el, coord) { let overLayer = new Overlay({ element: el, // 挂载元素 position: coord, autoPanAnimation: { duration: 250 }, stopEvent: true }); this.map.addOverlay(overLayer); this.overLayer = overLayer; } /** * 根据要素集合适配视图 * @param {map} 地图容器 * @param {*} featureList 要素集合 */ mapSetFit (featureList, padding = [100, 20, 20, 20], duration = 2000) { let options = { padding, duration }; let extent = createEmpty(); featureList.forEach(item => { const itemExtent = item.getGeometry().getExtent(); extent = extend(extent, itemExtent); }); if (featureList.length) { this.map.getView().fit(extent, options); } } /** * 坐标转换 * @param {coordinate } openlayer 输出坐标值 * Amap public index.html 导入 */ transform (coordinate) { return transform(coordinate, 'EPSG:3857', 'EPSG:4326'); } // 在地图上添加点, 并添加点的样式 setPoint (map, pointList) { pointVectorSource.clear(); if (pointList.length === 0) return; const pointStyle = new Style({ image: new Icon({ src: locationIcon, scale: 1, anchor: [0.5, 0.5], rotateWithView: false, rotation: 45 }) }); let pointFeature; pointList.forEach(item => { pointFeature = new Feature({ geometry: new Point(new fromLonLat([item.longitude, item.latitude])), url: item.url, showText: true, showPicture: item.url ? true : false }); pointFeature.setStyle(pointStyle); pointVectorSource.addFeature(pointFeature); }); const vectorLayer = new VectorLayer({ // zIndex: 999, source: pointVectorSource }); map.addLayer(vectorLayer); // 每隔1秒旋转10度 let rotation = 0; setInterval(() => { rotation += 5 * Math.PI / 180; pointStyle.getImage().setRotation(rotation); pointFeature.setStyle(pointStyle); }, 100); } // 初始化地块图层 initDKlayer (layerName, extent) { // 地块图层 let projection = getProjection('EPSG:3857'); let projectionExtent = projection.getExtent(); var size = getWidth(projectionExtent) / 256; var resolutions = []; for (var z = 0; z < 20; ++z) { resolutions[z] = size / Math.pow(2, z); } const layer = new TileLayer({ zIndex: 51, visible: true, source: new WMTS({ url: '/geoserver/gwc/service/wmts', layer: layerName, matrixSet: 'EPSG:3857', format: 'image/png', projection: projection, tileGrid: new WMTSTileGrid({ tileSize: [256, 256], resolutions: resolutions, origin: getTopLeft(projectionExtent), matrixIds: ['EPSG:3857:0', 'EPSG:3857:1', 'EPSG:3857:2', 'EPSG:3857:3', 'EPSG:3857:4', 'EPSG:3857:5', 'EPSG:3857:6', 'EPSG:3857:7', 'EPSG:3857:8', 'EPSG:3857:9', 'EPSG:3857:10', 'EPSG:3857:11', 'EPSG:3857:12', 'EPSG:3857:13', 'EPSG:3857:14', 'EPSG:3857:15', 'EPSG:3857:16', 'EPSG:3857:17', 'EPSG:3857:18', 'EPSG:3857:19', 'EPSG:3857:20', 'EPSG:3857:21', 'EPSG:3857:22', 'EPSG:3857:23', 'EPSG:3857:24', 'EPSG:3857:25', 'EPSG:3857:26', 'EPSG:3857:27', 'EPSG:3857:28', 'EPSG:3857:29', 'EPSG:3857:30'], extent: extent }), style: '', wrapX: false }) }); this.map.addLayer(layer); return layer; } /** * 根据一个带有经纬度,返回一个矢量图层 * @param {*} dataList 带有经纬度的数组 * { type: type, geometry: [经度, 维度] } * @param {*} icon 图标 */ getVectorLayerByGeometrylist (dataList, icon, zIndex = 100) { let featureList = []; const style = new Style({ image: new Icon({ anchor: [0.5, 28], anchorXUnits: 'fraction', anchorYUnits: 'pixels', src: icon }) }); dataList.forEach(item => { const iconFeature = new Feature({ _geometry: item.geometry, geometry: new Point(fromLonLat(item.geometry)) // geometry: new Point(item.geometry) }); iconFeature.setProperties(item.data); iconFeature.setStyle(style); featureList.push(iconFeature); }); const source = new VectorSource({ features: featureList }); const iconLayer = new VectorLayer({ zIndex, source: source }); this.map.addLayer(iconLayer); return iconLayer; } /** * 加载geojson数据 */ loadGeojsonData (geoData) { const fs = new GeoJSON().readFeatures(geoData); this.mapSetFit(fs); let layer = this.getVectorLayerByFs(fs); return layer; } removeLayer (layer) { this.map.removeLayer(layer); } /** * 增删改查要素 * @param {*} inserts 要插入的要素集合 * @param {*} updates 要更新的要素集合 * @param {*} deletes 要删除的要素集合 * @param {*} layer 要操作的图层 * @param {*} fs */ async crudFs (inserts = null, updates = null, deletes = null, layer = 'cwd2') { const geoserverData = { workSpaceName: 'qianguo', uri: 'http://124.235.241.109:8080/geoserver/qianguo', wfsURL: 'http://124.235.241.109:8080/geoserver/qianguo/ows?', layer: layer }; let wfs = new WFS(); let transact_xml = wfs.writeTransaction( inserts, updates, deletes, { version: '1.1.0', srsName: 'EPSG:3857', featureNS: geoserverData.uri, featurePrefix: geoserverData.workSpaceName, featureType: [geoserverData.layer], } ); let transact_str = (new XMLSerializer()).serializeToString(transact_xml); let updStr = transact_str.replace(/geometry/g, 'the_geom'); let res = await post('/geoserver/wfs', updStr); let transactRes = wfs.readTransactionResponse(res); let str = transactRes.transactionSummary.totalInserted + " totalInserted!, insertIds: " + transactRes.insertIds + "\n"; str += transactRes.transactionSummary.totalUpdated + " totalUpdated!\n"; str += transactRes.transactionSummary.totalDeleted + " totalDeleted!"; } } export default GisUtils;