olMap.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. // 引入OpenLayers的主模块
  2. import Map from 'ol/Map';
  3. import View from 'ol/View';
  4. import Feature from 'ol/Feature';
  5. import Point from 'ol/geom/Point';
  6. import VectorLayer from 'ol/layer/Vector';
  7. import VectorSource from 'ol/source/Vector';
  8. import Style from 'ol/style/Style';
  9. import Icon from 'ol/style/Icon';
  10. import Text from 'ol/style/Text';
  11. import Projection from 'ol/proj/Projection';
  12. import { getWidth, getTopLeft } from 'ol/extent';
  13. import TileLayer from 'ol/layer/Tile';
  14. import WMTS from 'ol/source/WMTS';
  15. import WMTSTileGrid from 'ol/tilegrid/WMTS';
  16. import WMTSCapabilities from 'ol/format/WMTSCapabilities';
  17. import { Fill, Stroke } from 'ol/style';
  18. import proj4 from 'proj4';
  19. import { register } from 'ol/proj/proj4';
  20. import { defaults } from 'ol/control';
  21. import Vector from 'ol/layer/Vector';
  22. import SourceVector from 'ol/source/Vector';
  23. import GeoJSON from 'ol/format/GeoJSON';
  24. import { fromLonLat } from 'ol/proj';
  25. import axios from 'axios';
  26. import { fromExtent } from 'ol/geom/Polygon';
  27. import { LinearRing, Polygon } from 'ol/geom';
  28. import {Graticule} from "ol/layer";
  29. import { mergeGeoJsonPolygons } from '@/utils/gisUtils';
  30. import { Cluster } from 'ol/source';
  31. import CircleStyle from 'ol/style/Circle';
  32. const commonUrl = import.meta.env.VITE_APP_BASE_API2 + 'api/oneShare/proxyHandler/gd/';
  33. // proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs +type=crs');
  34. proj4.defs('EPSG:4490', 'GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378137,298.257222101,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]]');
  35. // proj4.defs('EPSG:4525', '+proj=tmerc +lat_0=0 +lon_0=111 +k=1 +x_0=37500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs');
  36. register(proj4);
  37. const projection = new Projection({
  38. code: 'EPSG:4490',
  39. units: 'degrees',
  40. axisOrientation: 'neu'
  41. });
  42. projection.setExtent([-180, -90, 180, 90])
  43. projection.setWorldExtent([-180, -90, 180, 90])
  44. const projectionExtent = [-180, -90, 180, 90];
  45. const size = getWidth(projectionExtent) / 256;
  46. const resolutions = [];
  47. for (let z = 2; z < 22; ++z) {
  48. resolutions[z] = size / Math.pow(2, z);
  49. }
  50. export class olMap {
  51. private map;
  52. private options;
  53. private markers = [];
  54. private drawOptions = {
  55. graphicsType: 'circle',
  56. strokeColor: '#f80102',
  57. strokeOpacity: 1,
  58. strokeWeight: 2,
  59. fillColor: '#f80102',
  60. fillOpacity: 0,
  61. strokeStyle: 'solid'
  62. };
  63. // private { currentState, commit, undo, history, future } = useHistory();
  64. private overlays = [];
  65. private overlaysData = [];
  66. private graphicsType = '';
  67. private plot;
  68. private vectorLayer;
  69. private cluster;
  70. private maskLayer;
  71. private maskLayer2 = [];
  72. constructor(options) {
  73. this.options = options;
  74. this.map = new Map({
  75. controls: defaults({
  76. zoom: false,
  77. rotate: false
  78. }),
  79. layers: [],
  80. target: options.dom,
  81. view: new View({
  82. center: options.center && options.center.length === 2 ? [options.center[0], options.center[1]] : [110.90153121597234, 21.98323671981171],
  83. zoom: options.zoom ? options.zoom : 9.6,
  84. projection: projection,
  85. maxZoom: options.maxZoom ? options.maxZoom : 18,
  86. minZoom: options.minZoom ? options.minZoom : 1
  87. })
  88. });
  89. // 初始化比例尺
  90. if (options.showScale) {
  91. // this.map.addControl(new AMap.Scale());
  92. }
  93. if (options.drawTool?.use) {
  94. this.initMouseTool(options.drawTool);
  95. }
  96. this.initLayer(options);
  97. }
  98. async initLayer(options) {
  99. // 添加新的图层
  100. if (Array.isArray(options.id)) {
  101. for (const layer of options.id) {
  102. if (typeof layer === "string") {
  103. await this.formatXml(layer); // 等待当前 layer 处理完成
  104. } else {
  105. await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成
  106. }
  107. }
  108. } else if (options.id) {
  109. // 如果 options.id 不是数组,但确实是一个图层,则直接处理
  110. await this.formatXml(options.id, options.minZoom, options.maxZoom, options.zIndex, options.visible);
  111. }
  112. // 创建Vector层并添加到地图上
  113. this.vectorLayer = new VectorLayer({
  114. source: new VectorSource({
  115. features: []
  116. })
  117. });
  118. this.map.addLayer(this.vectorLayer);
  119. if (typeof this.options.onLoadCompleted === 'function') {
  120. this.options.onLoadCompleted(this.map);
  121. }
  122. }
  123. formatXml(code: string, minZoom?: number, maxZoom?: number, zIndex?: number, visible?: boolean) {
  124. const xml = new WMTSCapabilities();
  125. return this.getCapabilities(code).then((lists) => {
  126. const geojson = xml.read(lists.data);
  127. const data = geojson.Contents.Layer[0];
  128. const layerParam = {
  129. layerName: data.Abstract,
  130. styleName: data.Identifier,
  131. tilematrixset: data.TileMatrixSetLink[0].TileMatrixSet,
  132. format: data.Format[0]
  133. };
  134. this.createWmsLayer(code, layerParam, minZoom, maxZoom, zIndex, visible);
  135. });
  136. }
  137. // 请求接口获取地图信息
  138. getCapabilities(code) {
  139. return axios.get(commonUrl + code + '?SERVICE=WMTS&REQUEST=GetCapabilities');
  140. }
  141. // 请求地图图片加载图层
  142. createWmsLayer(code, layerParam, minZoom = 0, maxZoom, zIndex = -1, visible = true) {
  143. const source = new WMTS({
  144. url: commonUrl + code,
  145. crossOrigin: 'Anonymous',
  146. layer: layerParam.layerName,
  147. style: layerParam.styleName,
  148. matrixSet: layerParam.tilematrixset,
  149. format: layerParam.format,
  150. wrapX: true,
  151. tileGrid: new WMTSTileGrid({
  152. origin: getTopLeft(projectionExtent),
  153. resolutions: resolutions,
  154. matrixIds: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21']
  155. })
  156. });
  157. const layer = new TileLayer({
  158. name: code,
  159. source: source,
  160. zIndex: zIndex,
  161. minZoom: minZoom,
  162. maxZoom,
  163. visible: visible
  164. });
  165. layer.set('layerName', code);
  166. this.map.addLayer(layer);
  167. }
  168. // 初始化绘画工具
  169. initMouseTool(options) {
  170. this.drawOptions = {
  171. graphicsType: options.graphicsType ? options.graphicsType : 'cirlce',
  172. strokeColor: options.color,
  173. strokeOpacity: 1,
  174. strokeWeight: 2,
  175. fillColor: options.color,
  176. fillOpacity: options.drawType === '1' ? 0 : 0.5,
  177. strokeStyle: 'solid'
  178. };
  179. // this.plot = new olPlot(this.map, {
  180. // zoomToExtent: true,
  181. // ...this.drawOptions
  182. // });
  183. // this.plot.plotDraw.on('drawEnd', this.onDrawEnd.bind(this));
  184. }
  185. // 绘制结束事件
  186. onDrawEnd(event) {
  187. const feature = event.feature;
  188. const aa = feature.getGeometry();
  189. // 继续编辑编辑
  190. this.drawGraphics(this.drawOptions.graphicsType);
  191. }
  192. // 绘制图形
  193. drawGraphics(type: string) {
  194. if (type === 'circle') {
  195. // 绘制圆形
  196. // activate('Circle');
  197. } else if (type === 'rectangle') {
  198. // 绘制矩形
  199. this.plot.plotDraw.activate('RectAngle');
  200. } else if (type === 'polygon') {
  201. // 绘制多边形
  202. this.plot.plotDraw.activate('Polygon');
  203. } else if (type === 'freePolygon') {
  204. // 绘制索套
  205. this.plot.plotDraw.activate('FreePolygon');
  206. }
  207. }
  208. // 关闭绘制
  209. closeDraw() {
  210. this.plot.plotDraw.deactivate();
  211. }
  212. // 切换图层
  213. async replaceLayers(newLayers, loadendFunc) {
  214. // 遍历当前的所有图层并移除它们
  215. this.map.getLayers().forEach((layer) => {
  216. this.map.removeLayer(layer);
  217. });
  218. if (Array.isArray(newLayers)) {
  219. for (const layer of newLayers) {
  220. if (typeof layer === "string") {
  221. await this.formatXml(layer); // 等待当前 layer 处理完成
  222. } else {
  223. await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成
  224. }
  225. }
  226. } else {
  227. // 如果 options.id 不是数组,但确实是一个图层,则直接处理
  228. await this.formatXml(newLayers.id, newLayers.minZoom, newLayers.maxZoom, newLayers.zIndex, newLayers.visible);
  229. }
  230. // 创建Vector层并添加到地图上
  231. this.vectorLayer = new VectorLayer({
  232. source: new VectorSource({
  233. features: []
  234. })
  235. });
  236. this.map.addLayer(this.vectorLayer);
  237. const point = JSON.parse(JSON.stringify(this.markers));
  238. this.markers = [];
  239. this.addMarker(point);
  240. if (loadendFunc) {
  241. loadendFunc();
  242. }
  243. }
  244. // 集群点样式
  245. clusterStyle(feature) {
  246. const originalFeature = feature.get('features')[0];
  247. const size = feature.get('features').length;
  248. if (size > 1) {
  249. const outerCircle = new CircleStyle({
  250. radius: 20,
  251. fill: new Fill({
  252. color: 'rgba(79, 176, 206, 0.5)'
  253. }),
  254. stroke: new Stroke({
  255. color: 'rgba(79, 176, 206, 1)'
  256. })
  257. });
  258. return [
  259. new Style({
  260. image: outerCircle,
  261. text: new Text({
  262. text: size.toString(),
  263. font: '14px sans-serif',
  264. fill: new Fill({
  265. color: '#ffff'
  266. })
  267. })
  268. })
  269. ];
  270. }
  271. return new Style({
  272. geometry: originalFeature.getGeometry(),
  273. image: new Icon({
  274. anchor: [0.5, 0.5],
  275. scale: 0.146,
  276. anchorXUnits: 'fraction',
  277. anchorYUnits: 'pixels',
  278. src: originalFeature.get('icon')
  279. })
  280. });
  281. }
  282. addMarker(points) {
  283. this.clearMarker();
  284. const features = [];
  285. points.forEach((point) => {
  286. // 创建标注点
  287. const feature = new Feature({
  288. // 必须是数字类型,字符串不识别
  289. geometry: new Point([Number(point.longitude), Number(point.latitude)]),
  290. name: point.name,
  291. icon: point.icon,
  292. size: point.size,
  293. type: 'point'
  294. });
  295. // 定义样式
  296. const style = new Style({
  297. image: new Icon({
  298. anchor: [0.5, 0.5],
  299. scale: 0.146,
  300. anchorXUnits: 'fraction',
  301. anchorYUnits: 'pixels',
  302. src: point.icon
  303. }),
  304. text: new Text({
  305. text: point.name,
  306. fill: new Fill({
  307. color: '#000'
  308. }),
  309. stroke: new Stroke({
  310. color: '#fff',
  311. width: 3
  312. })
  313. })
  314. });
  315. feature.setStyle(style);
  316. features.push(feature);
  317. this.markers.push(point);
  318. });
  319. const vectorSource = new VectorSource({
  320. features: features
  321. });
  322. const clusterSource = new Cluster({
  323. distance: 30,
  324. source: vectorSource
  325. });
  326. this.vectorLayer.setStyle(this.clusterStyle);
  327. this.vectorLayer.setSource(clusterSource);
  328. }
  329. // 清除所有标加
  330. clearMarker() {
  331. if (!this.vectorLayer) return;
  332. this.vectorLayer.getSource().clear();
  333. }
  334. /**
  335. *
  336. * @param {Geojon} chaozhou 根据geojson对象创建Featrue对象
  337. * @returns VectorLayer
  338. */
  339. createVecByJson(json, options) {
  340. const format = new GeoJSON();
  341. const fs = format.readFeatures(json);
  342. this.maskLayer = new VectorLayer({
  343. source: new VectorSource(),
  344. style: new Style({
  345. fill: new Fill({
  346. color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
  347. }),
  348. stroke: new Stroke({
  349. color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
  350. width: 2
  351. })
  352. }),
  353. zIndex: options.zIndex ? options.zIndex : 99
  354. });
  355. this.map.addLayer(this.maskLayer);
  356. const extent = [-180, -90, 180, 90];
  357. const polygonRing = fromExtent(extent);
  358. fs.forEach((x) => {
  359. const ft = x.values_.geometry;
  360. const coords = ft.getCoordinates();
  361. coords.forEach((coord) => {
  362. const linearRing = new LinearRing(coord[0]);
  363. polygonRing.appendLinearRing(linearRing);
  364. });
  365. });
  366. const convertFt = new Feature({
  367. geometry: polygonRing
  368. });
  369. this.maskLayer.getSource().addFeature(convertFt);
  370. }
  371. createVecByJson2(json, options) {
  372. if (this.maskLayer2 && this.maskLayer2.length > 0) {
  373. this.maskLayer2.forEach((layer) => {
  374. this.map.addLayer(layer);
  375. });
  376. } else {
  377. // const layer = new VectorLayer({
  378. // source: new VectorSource(),
  379. // style: new Style({
  380. // fill: new Fill({
  381. // color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
  382. // }),
  383. // stroke: new Stroke({
  384. // color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
  385. // width: 2
  386. // })
  387. // }),
  388. // zIndex: options.zIndex ? options.zIndex : 99
  389. // });
  390. // // 合并区边界
  391. // const format = new GeoJSON();
  392. // const data2 = mergeGeoJsonPolygons(json);
  393. // const fs = format.readFeatures(data2);
  394. // const extent = [-180, -90, 180, 90];
  395. // const polygonRing = fromExtent(extent);
  396. // fs.forEach((x) => {
  397. // const ft = x.values_.geometry;
  398. // const coords = ft.getCoordinates();
  399. // coords.forEach((coord) => {
  400. // const linearRing = new LinearRing(coord[0]);
  401. // polygonRing.appendLinearRing(linearRing);
  402. // });
  403. // });
  404. // const convertFt = new Feature({
  405. // geometry: polygonRing
  406. // });
  407. // layer.getSource().addFeature(convertFt);
  408. // this.maskLayer2.push(layer);
  409. // this.map.addLayer(layer);
  410. // 边界部分
  411. json.features.forEach((feature) => {
  412. if (feature.geometry.type === 'Polygon') {
  413. const polygonPath = feature.geometry.coordinates[0].map((coord) => {
  414. return [coord[0], coord[1]];
  415. });
  416. const feature2 = new Feature({
  417. geometry: new Polygon([polygonPath])
  418. });
  419. const vector = new VectorLayer({
  420. source: new VectorSource(),
  421. style: new Style({
  422. fill: new Fill({
  423. color: 'rgba(0, 0, 0, 0)'
  424. }),
  425. stroke: new Stroke({
  426. color: options.strokeColor ? options.strokeColor : '#268ab9',
  427. width: options.strokeWeight ? options.strokeWeight : 1
  428. })
  429. })
  430. });
  431. vector.getSource().addFeature(feature2);
  432. this.maskLayer2.push(vector);
  433. this.map.addLayer(vector);
  434. } else if (feature.geometry.type === 'MultiPolygon') {
  435. feature.geometry.coordinates.forEach((polygonCoords) => {
  436. const polygonPath = polygonCoords.map((ring) => {
  437. return ring.map((coord) => {
  438. return [coord[0], coord[1]];
  439. });
  440. });
  441. const outerPath = polygonPath;
  442. const feature2 = new Feature({ geometry: new Polygon(outerPath), });
  443. const vector = new VectorLayer({
  444. source: new VectorSource(),
  445. style: new Style({
  446. fill: new Fill({
  447. color: 'rgba(0, 0, 0, 0)'
  448. }),
  449. stroke: new Stroke({
  450. color: options.strokeColor ? options.strokeColor : '#268ab9',
  451. width: options.strokeWeight ? options.strokeWeight : 1
  452. })
  453. })
  454. });
  455. vector.getSource().addFeature(feature2);
  456. this.maskLayer2.push(vector);
  457. this.map.addLayer(vector);
  458. });
  459. }
  460. });
  461. }
  462. }
  463. /**
  464. * @description 创建矢量图层
  465. * @param {String} layerName 图层名称
  466. * @param {Number} zIndex 地图层级默认是0
  467. * @returns
  468. */
  469. createVecLayer(layerName = '', zIndex = 0) {
  470. const source = new SourceVector({
  471. crossOrigin: 'anonymous'
  472. });
  473. const layer = new Vector({
  474. source,
  475. zIndex
  476. });
  477. layer.set('layerName', layerName);
  478. return layer;
  479. }
  480. // 分布图遮罩层
  481. createMask(data) {
  482. this.removeMask();
  483. if (!data || data.length === 0) return;
  484. data.forEach((item) => {
  485. if (!item.points || item.points.length === 0) return;
  486. // 遮罩图层的样式
  487. const maskStyle = new Style({
  488. fill: new Fill({
  489. color: item.color // 红色遮罩,50%透明度
  490. }),
  491. stroke: new Stroke({
  492. color: 'rgba(159,159,159,0.7)',
  493. width: 1
  494. })
  495. });
  496. // 遮罩图层的矢量数据源(初始为空)
  497. const maskSource = new VectorSource();
  498. // 创建一个多边形特征
  499. const polygonFeature = new Feature({
  500. geometry: new Polygon(item.points)
  501. });
  502. const maskLayer = new VectorLayer({
  503. source: maskSource,
  504. style: maskStyle,
  505. properties: {
  506. name: 'mask'
  507. }
  508. });
  509. this.map.addLayer(maskLayer);
  510. // 将多边形特征添加到遮罩数据源中
  511. maskSource.addFeature(polygonFeature);
  512. });
  513. }
  514. removeMask() {
  515. //移除图层
  516. const layersArray = this.map.getLayers().getArray();
  517. layersArray.forEach((layer) => {
  518. // 检查图层是否有自定义属性,并且该属性是否匹配你要移除的图层的标识符
  519. if (layer.get('name') === 'mask') {
  520. this.map.removeLayer(layer);
  521. }
  522. });
  523. }
  524. removeMask2() {
  525. if (this.maskLayer) {
  526. this.map.removeLayer(this.maskLayer);
  527. this.maskLayer = null;
  528. }
  529. }
  530. removeMask3(isHide) {
  531. if (this.maskLayer2 && this.maskLayer2.length > 0) {
  532. this.maskLayer2.forEach((layer) => {
  533. this.map.removeLayer(layer);
  534. });
  535. if (!isHide) {
  536. this.maskLayer2 = [];
  537. }
  538. }
  539. }
  540. /**
  541. * 绘制经纬线
  542. * visible: boolean 是否可见
  543. */
  544. handleLngLatLine(visible: boolean) {
  545. // 创建经纬网图层
  546. const graticule = new Graticule({
  547. name: 'Graticule',
  548. showLabels: true, // 为每条刻度线绘制一个带有各自纬度/经度的标签
  549. wrapX: false, // 是否水平重复经纬网
  550. targetSize: 230,
  551. zIndex: 99999,
  552. strokeStyle: new Stroke({ // 用于绘制刻度线的样式
  553. color: '#dcdcdc', // 线条颜色
  554. width: 1, // 线条宽度
  555. }),
  556. lonLabelStyle: new Text({
  557. font: '12px Calibri,sans-serif',
  558. textBaseline: 'bottom',
  559. fill: new Fill({
  560. color: '#9d9d9d'
  561. })
  562. }),
  563. latLabelPosition: 0,
  564. latLabelStyle: new Text({
  565. font: '12px Calibri,sans-serif',
  566. textAlign: 'left',
  567. textBaseline: 'end',
  568. fill: new Fill({
  569. color: '#9d9d9d'
  570. })
  571. })
  572. });
  573. if (visible) {
  574. this.map.addLayer(graticule)
  575. } else {
  576. this.removeLayer('Graticule')
  577. }
  578. }
  579. /**
  580. * 移除指定name的layer
  581. * layerName: string
  582. * */
  583. removeLayer(layerName: string) {
  584. const layers = this.map.getLayers();
  585. layers.forEach((element) => {
  586. if (!!element && element.get('name') === layerName) {
  587. //移除
  588. this.map.removeLayer(element);
  589. }
  590. });
  591. }
  592. getVectorLayer() {
  593. return this.vectorLayer;
  594. }
  595. getMap() {
  596. return this.map;
  597. }
  598. }