olMap.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  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 { Tile } from 'ol';
  29. import {Graticule} from "ol/layer";
  30. import { mergeGeoJsonPolygons } from '@/utils/gisUtils';
  31. // import olPlot from 'ol-plot';
  32. // import { activate } from '../ol-plot/ol-plot'
  33. const commonUrl = import.meta.env.VITE_APP_BASE_API2 + 'api/oneShare/proxyHandler/gd/';
  34. // proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs +type=crs');
  35. 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"]]');
  36. // 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');
  37. register(proj4);
  38. const projection = new Projection({
  39. code: 'EPSG:4490',
  40. units: 'degrees',
  41. axisOrientation: 'neu'
  42. });
  43. projection.setExtent([-180, -90, 180, 90])
  44. projection.setWorldExtent([-180, -90, 180, 90])
  45. const projectionExtent = [-180, -90, 180, 90];
  46. const size = getWidth(projectionExtent) / 256;
  47. const resolutions = [];
  48. for (let z = 2; z < 22; ++z) {
  49. resolutions[z] = size / Math.pow(2, z);
  50. }
  51. export class olMap {
  52. private map;
  53. private options;
  54. private markers = [];
  55. private drawOptions = {
  56. graphicsType: 'circle',
  57. strokeColor: '#f80102',
  58. strokeOpacity: 1,
  59. strokeWeight: 2,
  60. fillColor: '#f80102',
  61. fillOpacity: 0,
  62. strokeStyle: 'solid'
  63. };
  64. // private { currentState, commit, undo, history, future } = useHistory();
  65. private overlays = [];
  66. private overlaysData = [];
  67. private graphicsType = '';
  68. private plot;
  69. private vectorLayer;
  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. addMarker(points) {
  245. this.clearMarker('point');
  246. points.forEach((point) => {
  247. // 创建标注点
  248. const feature = new Feature({
  249. geometry: new Point(point.lnglat),
  250. name: point.name
  251. });
  252. // 定义样式
  253. const style = new Style({
  254. image: new Icon({
  255. anchor: [0.5, point.size[1]],
  256. anchorXUnits: 'fraction',
  257. anchorYUnits: 'pixels',
  258. src: point.icon
  259. }),
  260. text: new Text({
  261. text: point.name,
  262. fill: new Fill({
  263. color: '#000'
  264. }),
  265. stroke: new Stroke({
  266. color: '#fff',
  267. width: 3
  268. })
  269. })
  270. });
  271. feature.setStyle(style);
  272. this.markers.push(point);
  273. this.vectorLayer.getSource().addFeature(feature);
  274. });
  275. }
  276. // 清除所有标加
  277. clearMarker(id) {
  278. if (!this.vectorLayer) return;
  279. this.vectorLayer.getSource().clear();
  280. }
  281. /**
  282. *
  283. * @param {Geojon} chaozhou 根据geojson对象创建Featrue对象
  284. * @returns VectorLayer
  285. */
  286. createVecByJson(json, options) {
  287. const format = new GeoJSON();
  288. const fs = format.readFeatures(json);
  289. this.maskLayer = new VectorLayer({
  290. source: new VectorSource(),
  291. style: new Style({
  292. fill: new Fill({
  293. color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
  294. }),
  295. stroke: new Stroke({
  296. color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
  297. width: 2
  298. })
  299. }),
  300. zIndex: options.zIndex ? options.zIndex : 99
  301. });
  302. this.map.addLayer(this.maskLayer);
  303. const extent = [-180, -90, 180, 90];
  304. const polygonRing = fromExtent(extent);
  305. fs.forEach((x) => {
  306. const ft = x.values_.geometry;
  307. const coords = ft.getCoordinates();
  308. coords.forEach((coord) => {
  309. const linearRing = new LinearRing(coord[0]);
  310. polygonRing.appendLinearRing(linearRing);
  311. });
  312. });
  313. const convertFt = new Feature({
  314. geometry: polygonRing
  315. });
  316. this.maskLayer.getSource().addFeature(convertFt);
  317. }
  318. createVecByJson2(json, options) {
  319. if (this.maskLayer2 && this.maskLayer2.length > 0) {
  320. this.maskLayer2.forEach((layer) => {
  321. this.map.addLayer(layer);
  322. });
  323. } else {
  324. // const layer = new VectorLayer({
  325. // source: new VectorSource(),
  326. // style: new Style({
  327. // fill: new Fill({
  328. // color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
  329. // }),
  330. // stroke: new Stroke({
  331. // color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
  332. // width: 2
  333. // })
  334. // }),
  335. // zIndex: options.zIndex ? options.zIndex : 99
  336. // });
  337. // // 合并区边界
  338. // const format = new GeoJSON();
  339. // const data2 = mergeGeoJsonPolygons(json);
  340. // const fs = format.readFeatures(data2);
  341. // const extent = [-180, -90, 180, 90];
  342. // const polygonRing = fromExtent(extent);
  343. // fs.forEach((x) => {
  344. // const ft = x.values_.geometry;
  345. // const coords = ft.getCoordinates();
  346. // coords.forEach((coord) => {
  347. // const linearRing = new LinearRing(coord[0]);
  348. // polygonRing.appendLinearRing(linearRing);
  349. // });
  350. // });
  351. // const convertFt = new Feature({
  352. // geometry: polygonRing
  353. // });
  354. // layer.getSource().addFeature(convertFt);
  355. // this.maskLayer2.push(layer);
  356. // this.map.addLayer(layer);
  357. // 边界部分
  358. json.features.forEach((feature) => {
  359. if (feature.geometry.type === 'Polygon') {
  360. const polygonPath = feature.geometry.coordinates[0].map((coord) => {
  361. return [coord[0], coord[1]];
  362. });
  363. const feature2 = new Feature({
  364. geometry: new Polygon([polygonPath])
  365. });
  366. const vector = new VectorLayer({
  367. source: new VectorSource(),
  368. style: new Style({
  369. fill: new Fill({
  370. color: 'rgba(0, 0, 0, 0)'
  371. }),
  372. stroke: new Stroke({
  373. color: options.strokeColor ? options.strokeColor : '#268ab9',
  374. width: options.strokeWeight ? options.strokeWeight : 1
  375. })
  376. })
  377. });
  378. vector.getSource().addFeature(feature2);
  379. this.maskLayer2.push(vector);
  380. this.map.addLayer(vector);
  381. } else if (feature.geometry.type === 'MultiPolygon') {
  382. feature.geometry.coordinates.forEach((polygonCoords) => {
  383. const polygonPath = polygonCoords.map((ring) => {
  384. return ring.map((coord) => {
  385. return [coord[0], coord[1]];
  386. });
  387. });
  388. const outerPath = polygonPath;
  389. const feature2 = new Feature({ geometry: new Polygon(outerPath), });
  390. const vector = new VectorLayer({
  391. source: new VectorSource(),
  392. style: new Style({
  393. fill: new Fill({
  394. color: 'rgba(0, 0, 0, 0)'
  395. }),
  396. stroke: new Stroke({
  397. color: options.strokeColor ? options.strokeColor : '#268ab9',
  398. width: options.strokeWeight ? options.strokeWeight : 1
  399. })
  400. })
  401. });
  402. vector.getSource().addFeature(feature2);
  403. this.maskLayer2.push(vector);
  404. this.map.addLayer(vector);
  405. });
  406. }
  407. });
  408. }
  409. }
  410. /**
  411. * @description 创建矢量图层
  412. * @param {String} layerName 图层名称
  413. * @param {Number} zIndex 地图层级默认是0
  414. * @returns
  415. */
  416. createVecLayer(layerName = '', zIndex = 0) {
  417. const source = new SourceVector({
  418. crossOrigin: 'anonymous'
  419. });
  420. const layer = new Vector({
  421. source,
  422. zIndex
  423. });
  424. layer.set('layerName', layerName);
  425. return layer;
  426. }
  427. // 分布图遮罩层
  428. createMask(data) {
  429. this.removeMask();
  430. if (!data || data.length === 0) return;
  431. data.forEach((item) => {
  432. if (!item.points || item.points.length === 0) return;
  433. // 遮罩图层的样式
  434. const maskStyle = new Style({
  435. fill: new Fill({
  436. color: item.color // 红色遮罩,50%透明度
  437. }),
  438. stroke: new Stroke({
  439. color: 'rgba(159,159,159,0.7)',
  440. width: 1
  441. })
  442. });
  443. // 遮罩图层的矢量数据源(初始为空)
  444. const maskSource = new VectorSource();
  445. // 创建一个多边形特征
  446. const polygonFeature = new Feature({
  447. geometry: new Polygon(item.points)
  448. });
  449. const maskLayer = new VectorLayer({
  450. source: maskSource,
  451. style: maskStyle,
  452. properties: {
  453. name: 'mask'
  454. }
  455. });
  456. this.map.addLayer(maskLayer);
  457. // 将多边形特征添加到遮罩数据源中
  458. maskSource.addFeature(polygonFeature);
  459. });
  460. }
  461. removeMask() {
  462. //移除图层
  463. const layersArray = this.map.getLayers().getArray();
  464. layersArray.forEach((layer) => {
  465. // 检查图层是否有自定义属性,并且该属性是否匹配你要移除的图层的标识符
  466. if (layer.get('name') === 'mask') {
  467. this.map.removeLayer(layer);
  468. }
  469. });
  470. }
  471. removeMask2() {
  472. if (this.maskLayer) {
  473. this.map.removeLayer(this.maskLayer);
  474. this.maskLayer = null;
  475. }
  476. }
  477. removeMask3(isHide) {
  478. if (this.maskLayer2 && this.maskLayer2.length > 0) {
  479. this.maskLayer2.forEach((layer) => {
  480. this.map.removeLayer(layer);
  481. });
  482. if (!isHide) {
  483. this.maskLayer2 = [];
  484. }
  485. }
  486. }
  487. /**
  488. * 绘制经纬线
  489. * visible: boolean 是否可见
  490. */
  491. handleLngLatLine(visible: boolean) {
  492. // 创建经纬网图层
  493. const graticule = new Graticule({
  494. name: 'Graticule',
  495. showLabels: true, // 为每条刻度线绘制一个带有各自纬度/经度的标签
  496. wrapX: false, // 是否水平重复经纬网
  497. targetSize: 230,
  498. zIndex: 99999,
  499. strokeStyle: new Stroke({ // 用于绘制刻度线的样式
  500. color: '#dcdcdc', // 线条颜色
  501. width: 1, // 线条宽度
  502. }),
  503. lonLabelStyle: new Text({
  504. font: '12px Calibri,sans-serif',
  505. textBaseline: 'bottom',
  506. fill: new Fill({
  507. color: '#9d9d9d'
  508. })
  509. }),
  510. latLabelPosition: 0,
  511. latLabelStyle: new Text({
  512. font: '12px Calibri,sans-serif',
  513. textAlign: 'left',
  514. textBaseline: 'end',
  515. fill: new Fill({
  516. color: '#9d9d9d'
  517. })
  518. })
  519. });
  520. if (visible) {
  521. this.map.addLayer(graticule)
  522. } else {
  523. this.removeLayer('Graticule')
  524. }
  525. }
  526. /**
  527. * 移除指定name的layer
  528. * layerName: string
  529. * */
  530. removeLayer(layerName: string) {
  531. const layers = this.map.getLayers();
  532. layers.forEach((element) => {
  533. if (!!element && element.get('name') === layerName) {
  534. //移除
  535. this.map.removeLayer(element);
  536. }
  537. });
  538. }
  539. }