olMap.ts 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512
  1. // 引入OpenLayers的主模块
  2. import Map from 'ol/Map';
  3. import View from 'ol/View';
  4. import TileState from 'ol/TileState';
  5. import Feature from 'ol/Feature';
  6. import Point from 'ol/geom/Point';
  7. import VectorLayer from 'ol/layer/Vector';
  8. import VectorSource from 'ol/source/Vector';
  9. import Style from 'ol/style/Style';
  10. import Icon from 'ol/style/Icon';
  11. import Text from 'ol/style/Text';
  12. import Projection from 'ol/proj/Projection';
  13. import { getWidth, getTopLeft } from 'ol/extent';
  14. import TileLayer from 'ol/layer/Tile';
  15. import WMTS from 'ol/source/WMTS';
  16. import WMTSTileGrid from 'ol/tilegrid/WMTS';
  17. import WMTSCapabilities from 'ol/format/WMTSCapabilities';
  18. import { Fill, Stroke } from 'ol/style';
  19. import proj4 from 'proj4';
  20. import { register } from 'ol/proj/proj4';
  21. import { defaults, ScaleLine } from 'ol/control';
  22. import Vector from 'ol/layer/Vector';
  23. import SourceVector from 'ol/source/Vector';
  24. import GeoJSON from 'ol/format/GeoJSON';
  25. import axios from 'axios';
  26. import { fromExtent } from 'ol/geom/Polygon';
  27. import { LinearRing, LineString, Polygon } from 'ol/geom';
  28. import { Graticule } from 'ol/layer';
  29. import { Cluster } from 'ol/source';
  30. import CircleStyle from 'ol/style/Circle';
  31. import Overlay from 'ol/Overlay';
  32. import { DoubleClickZoom, DragPan, Draw, Select } from 'ol/interaction';
  33. import { click } from 'ol/events/condition';
  34. import Circle from 'ol/geom/Circle';
  35. import { deepClone, getRgba, hexToRgba, initDrag, rgbToRgba } from '@/utils';
  36. import { createBox } from 'ol/interaction/Draw';
  37. import * as turf from '@turf/turf';
  38. import { nanoid } from 'nanoid';
  39. import carImg from '@/assets/images/car.png';
  40. import { globalHeaders } from '@/utils/request';
  41. import { iconList } from '@/views/globalMap/data/mapData';
  42. import gdJson from '@/assets/json/gd.json';
  43. const tk = 'a8df87f1695d224d2679aa805c1268d9';
  44. const commonUrl = import.meta.env.VITE_APP_BASE_API2 + 'api/oneShare/proxyHandler/gd/';
  45. // proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs +type=crs');
  46. proj4.defs(
  47. 'EPSG:4490',
  48. '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"]]'
  49. );
  50. // 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');
  51. register(proj4);
  52. const projection = new Projection({
  53. code: 'EPSG:4490',
  54. units: 'degrees',
  55. axisOrientation: 'neu'
  56. });
  57. projection.setExtent([-180, -90, 180, 90]);
  58. projection.setWorldExtent([-180, -90, 180, 90]);
  59. const projectionExtent = [-180, -90, 180, 90];
  60. const size = getWidth(projectionExtent) / 256;
  61. const resolutions = [];
  62. for (let z = 2; z < 22; ++z) {
  63. resolutions[z] = size / Math.pow(2, z);
  64. }
  65. export class olMap {
  66. private map;
  67. private options;
  68. private markers = [];
  69. private drawOptions = {
  70. graphicsType: 'circle',
  71. strokeColor: '#f80102',
  72. strokeOpacity: 1,
  73. strokeWeight: 2,
  74. fillColor: '#f80102',
  75. fillOpacity: 0,
  76. strokeStyle: 'solid',
  77. icon: '',
  78. iconName: ''
  79. };
  80. private drawVector;
  81. private drawTool;
  82. private vectorLayer;
  83. // 边界遮罩图层
  84. private maskLayer;
  85. private maskLayer2;
  86. // 显示信息框
  87. private clickMarker;
  88. private infoWindow;
  89. private select;
  90. // 车辆轨迹
  91. private carLayer;
  92. private carFeature;
  93. private traceFeature;
  94. // 自定义绘制结束调用方法
  95. private drawEndMethod;
  96. private drawing;
  97. private path;
  98. private anyLine;
  99. private scale;
  100. // 标绘图层
  101. private plotLayers = {};
  102. private addressMarker;
  103. private addPoints = [];
  104. constructor(options) {
  105. this.options = options;
  106. this.map = new Map({
  107. controls: defaults({
  108. zoom: false,
  109. rotate: false
  110. }),
  111. layers: [],
  112. target: options.dom,
  113. view: new View({
  114. center: options.center && options.center.length === 2 ? [options.center[0], options.center[1]] : [110.90153121597234, 21.98323671981171],
  115. zoom: options.zoom ? options.zoom : 9.6,
  116. projection: projection,
  117. maxZoom: options.maxZoom ? options.maxZoom : 18,
  118. minZoom: options.minZoom ? options.minZoom : 1
  119. })
  120. });
  121. // 初始化比例尺
  122. if (options.showScale) {
  123. this.scale = new ScaleLine();
  124. this.map.addControl(this.scale);
  125. }
  126. if (options.drawTool?.use) {
  127. this.initMouseTool(options.drawTool);
  128. }
  129. this.initLayer(options);
  130. // 给标注点添加手势
  131. this.map.on('pointermove', (e) => {
  132. this.map.getTargetElement().style.cursor = 'auto';
  133. const features = this.map.getFeaturesAtPixel(e.pixel);
  134. features.forEach((feature) => {
  135. const originalFeature = feature.get('features') ? feature.get('features')[0] : '';
  136. if (!!originalFeature && originalFeature.get('pointer')) {
  137. this.map.getTargetElement().style.cursor = 'pointer'; //设置鼠标样式
  138. } else {
  139. this.map.getTargetElement().style.cursor = 'auto';
  140. }
  141. });
  142. });
  143. // 创建选择交互
  144. this.select = new Select({
  145. condition: click,
  146. style: null,
  147. filter: (feature, layer) => {
  148. // 检查当前图层是否在 plotLayers 中
  149. return Object.values(this.plotLayers).includes(layer);
  150. }
  151. });
  152. this.map.addInteraction(this.select);
  153. // 监听Select交互的select事件
  154. this.select.on('select', (event) => {
  155. const feature = event.selected[0]; // 获取被选中的要素集合
  156. const extData = feature.get('extData');
  157. if (!!feature) {
  158. if (this.clickMarker) {
  159. const selectData = this.clickMarker.get('extData');
  160. this.clickMarker.setStyle(
  161. new Style({
  162. image: new Icon({
  163. src: selectData.image,
  164. scale: selectData.scale,
  165. anchor: [0.5, 0.5],
  166. anchorXUnits: 'fraction',
  167. anchorYUnits: 'fraction'
  168. })
  169. })
  170. );
  171. }
  172. if (['1', '2'].includes(extData.type)) {
  173. // 多点位 单点
  174. if (this.clickMarker !== feature) {
  175. this.clickMarker = feature;
  176. feature.setStyle(
  177. new Style({
  178. image: new Icon({
  179. src: extData.imageHover,
  180. scale: extData.scale,
  181. anchor: [0.5, 0.5],
  182. anchorXUnits: 'fraction',
  183. anchorYUnits: 'fraction'
  184. })
  185. })
  186. );
  187. options.onMarkerClick(extData);
  188. }
  189. } else if (extData.type === '3') {
  190. // 聚合要素
  191. // this.select.getFeatures().clear();
  192. const currentZoom = this.map.getView().getZoom();
  193. this.map.getView().setZoom(currentZoom + 1);
  194. this.map.getView().setCenter([Number(extData.longitude), Number(extData.latitude)]);
  195. event.selected = [];
  196. }
  197. }
  198. });
  199. }
  200. async initLayer(options) {
  201. // 添加新的图层
  202. if (Array.isArray(options.id)) {
  203. for (const layer of options.id) {
  204. if (layer.layerType === 'JSON') {
  205. await this.createJsonLayer(layer);
  206. } else {
  207. await this.formatXml(layer);
  208. }
  209. }
  210. } else if (options.id === 'tianditu') {
  211. await this.formatXml2();
  212. } else if (options.id) {
  213. await this.formatXml(options);
  214. }
  215. // 创建Vector层并添加到地图上
  216. this.vectorLayer = new VectorLayer({
  217. source: new VectorSource({
  218. features: []
  219. }),
  220. zIndex: options.zIndex ? options.zIndex : 100
  221. });
  222. this.map.addLayer(this.vectorLayer);
  223. if (typeof this.options.onLoadCompleted === 'function') {
  224. this.options.onLoadCompleted(this.map);
  225. }
  226. }
  227. formatXml2() {
  228. const baseUrl = 'https://t{0-7}.tianditu.gov.cn/';
  229. const vecType = 'vec'; // 矢量图层类型
  230. const projType = 'w'; // 投影类型:web mercator
  231. const reqParams = {
  232. SERVICE: 'WMTS',
  233. REQUEST: 'GetTile',
  234. VERSION: '1.0.0',
  235. LAYER: '',
  236. STYLE: 'default',
  237. TILEMATRIXSET: projType,
  238. FORMAT: 'tiles',
  239. TILECOL: '{x}',
  240. TILEROW: '{y}',
  241. TILEMATRIX: '{z}',
  242. tk: key
  243. };
  244. const newParams = Object.assign({}, params, { LAYER: dataType });
  245. let paramsStr = '';
  246. for (const [k, v] of Object.entries(newParams)) {
  247. paramsStr += `${k}=${v}&`;
  248. }
  249. return `${baseUrl}${dataType}_${projType}/wmts?${paramsStr.slice(0, -1)}`;
  250. }
  251. formatXml(options) {
  252. const xml = new WMTSCapabilities();
  253. return this.getCapabilities(options.code, options.layerType).then((res) => {
  254. if (options.layerType === 'WMTS') {
  255. const geoJson = xml.read(res.data);
  256. const data = geoJson.Contents.Layer[0];
  257. const layerParam = {
  258. layerName: data.Abstract,
  259. styleName: data.Identifier,
  260. tilematrixset: data.TileMatrixSetLink[0].TileMatrixSet,
  261. format: data.Format[0]
  262. };
  263. this.createWmtsLayer(options, layerParam);
  264. } else if (options.layerType === 'WFS') {
  265. const parser = new DOMParser();
  266. const xmlDoc = parser.parseFromString(res.data, 'text/xml');
  267. const featureType = xmlDoc.getElementsByTagName('FeatureType')[0];
  268. const layerParam = {
  269. typeName: featureType.getElementsByTagName('Name')[0].textContent
  270. };
  271. this.createWfsLayer(options, layerParam);
  272. }
  273. });
  274. }
  275. // 请求接口获取地图信息
  276. getCapabilities(code, service) {
  277. return axios.get(commonUrl + code + '?SERVICE=' + service + '&REQUEST=GetCapabilities', {
  278. headers: globalHeaders()
  279. });
  280. }
  281. // 请求WMTS地图图片加载图层
  282. createWmtsLayer(options, layerParam) {
  283. const source = new WMTS({
  284. url: commonUrl + options.code,
  285. crossOrigin: 'Anonymous',
  286. layer: layerParam.layerName,
  287. style: layerParam.styleName,
  288. matrixSet: layerParam.tilematrixset,
  289. format: layerParam.format,
  290. wrapX: true,
  291. tileGrid: new WMTSTileGrid({
  292. origin: getTopLeft(projectionExtent),
  293. resolutions: resolutions,
  294. matrixIds: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21']
  295. }),
  296. tileLoadFunction: function (tile, src) {
  297. const xhr = new XMLHttpRequest();
  298. xhr.open('GET', src);
  299. // 添加自定义 Headers
  300. const headers = globalHeaders();
  301. xhr.setRequestHeader('Authorization', headers.Authorization);
  302. xhr.responseType = 'arraybuffer'; // 确保支持图片二进制数据
  303. xhr.onload = function () {
  304. if (xhr.status === 200) {
  305. const type = xhr.getResponseHeader('Content-Type');
  306. const blob = new Blob([xhr.response], { type: type });
  307. const url = window.URL.createObjectURL(blob);
  308. tile.getImage().src = url; // 将图像数据绑定到瓦片
  309. } else {
  310. tile.setState(TileState.ERROR); // 处理请求失败
  311. }
  312. };
  313. xhr.onerror = function () {
  314. tile.setState(TileState.ERROR);
  315. };
  316. xhr.send();
  317. }
  318. });
  319. const layer = new TileLayer({
  320. source: source,
  321. zIndex: options.zIndex,
  322. visible: options.visible
  323. });
  324. layer.set('layerName', options.layer);
  325. layer.set('id', options.code);
  326. this.map.addLayer(layer);
  327. }
  328. //
  329. createWfsLayer(options, layerParam) {
  330. const source = new VectorSource({
  331. format: new GeoJSON(),
  332. loader: function (extent, resolution, projection) {
  333. // 构建带认证头的请求参数
  334. const url =
  335. `${commonUrl}${options.code}?` +
  336. `REQUEST=GetFeature` +
  337. `&typeName=${encodeURIComponent(layerParam.typeName)}` +
  338. `&outputFormat=application/json`;
  339. const headers = globalHeaders();
  340. fetch(url, {
  341. headers: {
  342. 'Authorization': headers.Authorization
  343. }
  344. })
  345. .then((response) => {
  346. if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  347. return response.json();
  348. })
  349. .then((json) => {
  350. const features = new GeoJSON().readFeatures(json, {
  351. dataProjection: layerParam.srsName,
  352. featureProjection: projection
  353. });
  354. this.addFeatures(features);
  355. })
  356. .catch(error => console.error('WFS加载失败:', error));
  357. }
  358. });
  359. const vectorLayer = new VectorLayer({
  360. source: source,
  361. style: new Style({
  362. // 必须设置样式才能显示
  363. fill: new Fill({ color: 'rgba(0,0,0, 0)' }),
  364. stroke: new Stroke({ color: 'rgba(0,0,0, 1)', width: 1 })
  365. }),
  366. zIndex: options.zIndex ? options.zIndex : -99
  367. });
  368. this.map.addLayer(vectorLayer);
  369. }
  370. // 加载json图层
  371. createJsonLayer(layer) {
  372. return new Promise((resolve, reject) => {
  373. const geojsonParser = new GeoJSON();
  374. console.log(this.map.getView().getProjection());
  375. const features = geojsonParser.readFeatures(gdJson, {
  376. dataProjection: 'EPSG:4326',
  377. featureProjection: this.map.getView().getProjection()
  378. });
  379. const jsonLayer = new VectorLayer({
  380. source: new VectorSource({ features }),
  381. style: new Style({
  382. // 必须设置样式才能显示
  383. fill: new Fill({ color: '#ffffff' }),
  384. stroke: new Stroke({ color: 'rgba(0,0,0, 1)', width: 1 })
  385. }),
  386. zIndex: layer.zIndex ? layer.zIndex : -99
  387. });
  388. this.map.addLayer(jsonLayer);
  389. resolve({});
  390. });
  391. }
  392. // 初始化绘画工具
  393. initMouseTool(options) {
  394. this.drawOptions = {
  395. graphicsType: options.graphicsType ? options.graphicsType : 'cirlce',
  396. strokeColor: options.color,
  397. strokeOpacity: 1,
  398. strokeWeight: 2,
  399. fillColor: options.color,
  400. fillOpacity: options.drawType === '1' ? 0 : 0.5,
  401. strokeStyle: 'solid'
  402. };
  403. // 创建矢量图层用于绘制
  404. this.drawVector = new Vector({
  405. source: new VectorSource({
  406. features: []
  407. })
  408. });
  409. this.map.addLayer(this.drawVector);
  410. }
  411. // 绘制结束事件
  412. onDrawEnd(event) {
  413. const feature = event.feature;
  414. const aa = feature.getGeometry();
  415. // 继续编辑编辑
  416. this.drawGraphics(this.drawOptions.graphicsType);
  417. }
  418. // 绘制图形
  419. drawGraphics(newOptions: MouseTool) {
  420. const typeList = {
  421. circle: 'Circle',
  422. rectangle: 'Circle',
  423. polygon: 'Polygon',
  424. measureArea: 'Polygon',
  425. straightLine: 'LineString',
  426. marker: 'Point',
  427. text: 'Point',
  428. anyLine: 'AnyLine'
  429. };
  430. const data = getRgba(newOptions.color);
  431. let geometryFunction = null;
  432. if (newOptions.graphicsType === 'rectangle') {
  433. // 绘制矩形的方法
  434. geometryFunction = createBox();
  435. }
  436. if (!!typeList[newOptions.graphicsType]) {
  437. if (newOptions.graphicsType === 'text') {
  438. this.drawOptions = {
  439. type: newOptions.graphicsType,
  440. title: newOptions.title,
  441. text: newOptions.text,
  442. fontSize: newOptions.fontSize,
  443. fontColor: newOptions.fontColor,
  444. lnglat: newOptions.lnglat
  445. };
  446. // 绘制文字
  447. return this.addText(this.drawOptions);
  448. } else {
  449. this.drawOptions = {
  450. type: newOptions.graphicsType,
  451. title: newOptions.title,
  452. strokeColor: !!data.color ? data.color : newOptions.color,
  453. strokeOpacity: 1,
  454. strokeWeight: 1,
  455. fillColor: data.color,
  456. fillOpacity: data.opacity,
  457. strokeStyle: 'solid'
  458. };
  459. if (newOptions.graphicsType === 'marker') {
  460. this.drawOptions.icon = newOptions.icon;
  461. this.drawOptions.iconName = newOptions.iconName;
  462. this.drawOptions.size = newOptions.size;
  463. }
  464. this.closeDraw();
  465. if (newOptions.graphicsType === 'anyLine') {
  466. this.drawOptions.fillOpacity = 0;
  467. this.drawAnyLine(this.drawOptions);
  468. } else {
  469. // 创建绘制交互
  470. let style = new Style({
  471. stroke: new Stroke({
  472. color: rgbToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity),
  473. width: this.drawOptions.strokeWeight
  474. }),
  475. fill: new Fill({
  476. color: rgbToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity)
  477. })
  478. });
  479. this.drawTool = new Draw({
  480. source: this.drawVector.getSource(),
  481. type: typeList[newOptions.graphicsType],
  482. geometryFunction: geometryFunction,
  483. style: style
  484. });
  485. // 添加绘制交互到地图
  486. this.map.addInteraction(this.drawTool);
  487. // 监听绘制结束事件
  488. this.drawTool.on('drawend', (event) => {
  489. const feature = event.feature;
  490. // 获取几何对象
  491. if (newOptions.graphicsType !== 'marker') {
  492. if (newOptions.graphicsType === 'measureArea') {
  493. const geometry = feature.getGeometry();
  494. const coordinates = geometry.getCoordinates();
  495. const pathArr = coordinates[0];
  496. const area = turf.area(turf.polygon([pathArr]));
  497. style = new Style({
  498. stroke: style.getStroke(),
  499. fill: style.getFill(),
  500. text: new Text({
  501. text: '区域面积' + area.toFixed(2) + '平方米',
  502. font: '14px Calibri,sans-serif',
  503. fill: new Fill({ color: '#000' }),
  504. stroke: new Stroke({
  505. color: '#fff',
  506. width: 3
  507. }),
  508. overflow: true
  509. })
  510. });
  511. }
  512. feature.setStyle(style);
  513. }
  514. });
  515. }
  516. }
  517. }
  518. return this.drawOptions;
  519. }
  520. // 空间分析绘制图形
  521. drawGraphics2(newOptions: MouseTool) {
  522. const typeList = {
  523. circle: 'Circle',
  524. rectangle: 'Circle',
  525. polygon: 'Polygon'
  526. };
  527. let geometryFunction = null;
  528. if (newOptions.graphicsType === 'rectangle') {
  529. // 绘制矩形的方法
  530. geometryFunction = createBox();
  531. }
  532. if (!!typeList[newOptions.graphicsType]) {
  533. this.drawOptions = {
  534. type: newOptions.graphicsType,
  535. strokeColor: newOptions.color,
  536. strokeOpacity: 1,
  537. strokeWeight: 1,
  538. fillColor: newOptions.color,
  539. fillOpacity: newOptions.drawType === '1' ? '0' : '0.5',
  540. strokeStyle: 'solid'
  541. };
  542. this.closeDraw();
  543. // 创建绘制交互
  544. const style = new Style({
  545. stroke: new Stroke({
  546. color: hexToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity),
  547. width: this.drawOptions.strokeWeight
  548. }),
  549. fill: new Fill({
  550. color: hexToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity)
  551. })
  552. });
  553. this.drawTool = new Draw({
  554. source: this.drawVector.getSource(),
  555. type: typeList[newOptions.graphicsType],
  556. geometryFunction: geometryFunction,
  557. style: style
  558. });
  559. // 添加绘制交互到地图
  560. this.map.addInteraction(this.drawTool);
  561. // 监听绘制结束事件
  562. this.drawTool.on('drawend', (event) => {
  563. const feature = event.feature;
  564. // 获取几何对象
  565. feature.setStyle(style);
  566. });
  567. }
  568. }
  569. addText(options) {
  570. // 创建文本覆盖物
  571. const style = new Style({
  572. text: new Text({
  573. text: options.text,
  574. font: options.fontSize + ' Calibri,sans-serif',
  575. fill: new Fill({ color: options.fontColor }),
  576. // stroke: new Stroke({
  577. // color: '#fff',
  578. // width: 3
  579. // }),
  580. overflow: true
  581. })
  582. });
  583. const text = new Feature({
  584. geometry: new Point(options.lnglat)
  585. });
  586. text.setStyle(style);
  587. // 将文本覆盖物添加到地图
  588. this.drawVector.getSource().addFeature(text);
  589. const id = nanoid();
  590. text.set('id', id);
  591. const data: any = deepClone(options);
  592. data.id = id;
  593. return { text, data };
  594. }
  595. drawAnyLine(drawOptions) {
  596. const handleTouchStart = (e) => {
  597. this.drawing = true;
  598. const interactions = this.map.getInteractions();
  599. interactions.forEach((interaction) => {
  600. if (interaction instanceof DragPan || interaction instanceof DoubleClickZoom) {
  601. interaction.setActive(false); // 修改为需要的值,true或false
  602. }
  603. });
  604. this.path = [e.coordinate];
  605. // 移除旧的线段
  606. if (this.anyLine) {
  607. this.drawVector.getSource().removeFeature(this.anyLine);
  608. }
  609. };
  610. const handleTouchMove = (e) => {
  611. if (!this.drawing) return;
  612. this.path.push(e.coordinate);
  613. // 移除旧的线段
  614. if (this.anyLine) {
  615. this.drawVector.getSource().removeFeature(this.anyLine);
  616. }
  617. this.anyLine = new Feature({
  618. geometry: new LineString(this.path)
  619. });
  620. const lineStyle = new Style({
  621. stroke: new Stroke({
  622. color: hexToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity), // 合并颜色与透明度
  623. width: this.drawOptions.strokeWeight,
  624. lineJoin: 'round' // 线段连接处圆角
  625. })
  626. });
  627. this.anyLine.setStyle(lineStyle);
  628. this.drawVector.getSource().addFeature(this.anyLine);
  629. };
  630. const handleTouchEnd = () => {
  631. this.drawing = false;
  632. document.removeEventListener('touchend', handleTouchEnd);
  633. this.map.un('pointerdown', handleTouchStart);
  634. this.map.un('pointermove', handleTouchMove);
  635. document.removeEventListener('mouseup', handleTouchEnd);
  636. if (!!this.drawEndMethod) {
  637. this.drawEndMethod(drawOptions, this.anyLine);
  638. }
  639. this.anyLine = null;
  640. };
  641. document.addEventListener('touchend', handleTouchEnd);
  642. this.map.on('pointerdown', handleTouchStart);
  643. this.map.on('pointermove', handleTouchMove);
  644. document.addEventListener('mouseup', handleTouchEnd);
  645. }
  646. // 关闭绘制
  647. closeDraw() {
  648. if (!this.drawTool) return;
  649. this.map.removeInteraction(this.drawTool);
  650. // this.drawTool.abortDrawing();
  651. }
  652. // 切换图层
  653. async replaceLayers(newLayers, loadendFunc) {
  654. // 遍历当前的所有图层并移除它们
  655. const layers = this.map.getLayers();
  656. const layerArray = layers.getArray().slice();
  657. layerArray.forEach((layer) => {
  658. // 标注现在都是用同一个暂不移除'annotation'
  659. if (!!layer && ['map'].includes(layer.get('layerName'))) {
  660. layer.getSource().clear();
  661. layer.dispose();
  662. this.map.removeLayer(layer);
  663. }
  664. });
  665. if (Array.isArray(newLayers)) {
  666. for (const layer of newLayers) {
  667. await this.formatXml(layer);
  668. }
  669. } else if (newLayers.id === 'tianditu') {
  670. await this.formatXml2();
  671. } else {
  672. await this.formatXml(newLayers);
  673. }
  674. if (loadendFunc) {
  675. loadendFunc();
  676. }
  677. }
  678. // 集群点样式
  679. clusterStyle(feature) {
  680. const originalFeature = feature.get('features')[0];
  681. const size = feature.get('features').length;
  682. if (size > 1) {
  683. const outerCircle = new CircleStyle({
  684. radius: 20,
  685. fill: new Fill({
  686. color: 'rgba(79, 176, 206, 0.5)'
  687. }),
  688. stroke: new Stroke({
  689. color: 'rgba(79, 176, 206, 1)'
  690. })
  691. });
  692. return [
  693. new Style({
  694. image: outerCircle,
  695. text: new Text({
  696. text: size.toString(),
  697. font: '14px sans-serif',
  698. fill: new Fill({
  699. color: '#ffff'
  700. })
  701. })
  702. })
  703. ];
  704. }
  705. const pointSize = originalFeature.get('size');
  706. const originalWidth = originalFeature.get('originalWidth');
  707. const originalHeight = originalFeature.get('originalHeight');
  708. const icon = originalFeature.get('icon');
  709. const name = originalFeature.get('name');
  710. const showName = originalFeature.get('showName');
  711. const scale = !!pointSize[0] ? pointSize[0] / originalWidth : 1;
  712. const style = new Style({
  713. geometry: originalFeature.getGeometry(),
  714. image: new Icon({
  715. src: icon,
  716. scale: scale,
  717. anchor: [0.5, 0.5],
  718. anchorXUnits: 'fraction',
  719. anchorYUnits: 'fraction'
  720. })
  721. });
  722. if (!!showName) {
  723. style.setText(
  724. new Text({
  725. text: name,
  726. font: '12px sans-serif',
  727. fill: new Fill({
  728. color: '#000'
  729. }),
  730. stroke: new Stroke({
  731. color: '#fff',
  732. width: 3
  733. }),
  734. offsetY: (originalHeight / 2) * scale + 4
  735. })
  736. );
  737. }
  738. return style;
  739. }
  740. // 添加搜索的标记的
  741. addSearchMarker(item) {
  742. const view = this.map.getView();
  743. view.setZoom(18);
  744. view.setCenter([Number(item.longitude), Number(item.latitude)]);
  745. // 获取到上一次的搜索标记并移除
  746. const index = this.markers.findIndex((m) => {
  747. return m.dataType === 'search';
  748. });
  749. if (index > -1) {
  750. this.markers.splice(index, 1);
  751. }
  752. if (!this.plotLayers['search']) {
  753. this.plotLayers['search'] = new VectorLayer({
  754. source: new VectorSource({
  755. features: []
  756. })
  757. });
  758. this.map.addLayer(this.plotLayers['search']);
  759. } else {
  760. this.plotLayers['search'].getSource().clear();
  761. }
  762. this.addMarker2({ 'search': item });
  763. this.clickMarker = item;
  764. this.options.onMarkerClick(item);
  765. }
  766. addMarker(points) {
  767. this.clearMarker();
  768. const vectorSource = new VectorSource({
  769. features: []
  770. });
  771. points.forEach((point) => {
  772. // 创建标注点
  773. const feature = new Feature({
  774. // 必须是数字类型,字符串不识别
  775. geometry: new Point([Number(point.longitude), Number(point.latitude)]),
  776. name: point.name,
  777. icon: point.icon,
  778. image: point.icon,
  779. imageHover: point.imageHover,
  780. size: point.size,
  781. showName: point.showName,
  782. pointer: true,
  783. extData: point
  784. });
  785. // 设置自定义属性
  786. const img = new Image();
  787. img.onload = () => {
  788. // 图片加载完成后,可以访问其 width 和 height 属性
  789. const width = img.width;
  790. const height = img.width;
  791. feature.set('originalWidth', width);
  792. feature.set('originalHeight', height);
  793. feature.set('img', img);
  794. vectorSource.addFeature(feature);
  795. };
  796. img.src = point.icon; // 设置图片的 URL,触发加载
  797. this.markers.push(point);
  798. });
  799. const clusterSource = new Cluster({
  800. distance: 30,
  801. source: vectorSource
  802. });
  803. this.vectorLayer.setStyle(this.clusterStyle);
  804. this.vectorLayer.setSource(clusterSource);
  805. }
  806. addMarker2(obj) {
  807. this.clearMarker2('point');
  808. if (!!this.plotLayers['point']) {
  809. this.markers = [];
  810. }
  811. let hideInfoFlag = true;
  812. Object.keys(obj).forEach((key: string) => {
  813. const data = obj[key];
  814. const name = key === 'search' ? 'search' : 'point';
  815. if (this.clickMarker) {
  816. const extData = this.clickMarker.get('extData');
  817. if (data.id === extData.id && data.dataType === extData.dataType) {
  818. hideInfoFlag = false;
  819. data.isHover = true;
  820. }
  821. }
  822. if (data.type === '3') {
  823. // 聚合点
  824. const outerCircle = new CircleStyle({
  825. radius: 20,
  826. fill: new Fill({
  827. color: 'rgba(79, 176, 206, 0.5)'
  828. }),
  829. stroke: new Stroke({
  830. color: 'rgba(79, 176, 206, 1)'
  831. })
  832. });
  833. const feature = new Feature({
  834. // 必须是数字类型,字符串不识别
  835. geometry: new Point([Number(data.longitude), Number(data.latitude)]),
  836. name: data.name,
  837. pointer: true,
  838. extData: data
  839. });
  840. feature.setStyle(
  841. new Style({
  842. image: outerCircle,
  843. text: new Text({
  844. text: data.count.toString(),
  845. font: '14px sans-serif',
  846. fill: new Fill({
  847. color: '#ffff'
  848. })
  849. })
  850. })
  851. );
  852. this.plotLayers[name].getSource().addFeature(feature);
  853. this.markers.push(data);
  854. } else {
  855. // 单个点
  856. const iconConfig = iconList[data.dataType] || (name === 'search' ? iconList.common2 : iconList.common);
  857. data.image = iconConfig.image;
  858. data.imageHover = iconConfig.imageHover;
  859. data.size = iconConfig.size;
  860. if (data.materia_name) {
  861. data.name = data.materia_name;
  862. }
  863. if (data.dataType === 43) {
  864. data.showName = true;
  865. }
  866. if (!data.id) {
  867. data.id = nanoid(8);
  868. }
  869. data.lnglat = [data.longitude, data.latitude];
  870. const feature = new Feature({
  871. // 必须是数字类型,字符串不识别
  872. geometry: new Point([Number(data.longitude), Number(data.latitude)]),
  873. name: data.name,
  874. pointer: true,
  875. extData: data
  876. });
  877. // 设置自定义属性
  878. const img = new Image();
  879. img.onload = () => {
  880. // 图片加载完成后,可以访问其 width 和 height 属性
  881. const scale = data.size[0] ? data.size[0] / img.width : 1;
  882. data.scale = scale;
  883. feature.set('extData', data);
  884. feature.setStyle(
  885. new Style({
  886. image: new Icon({
  887. src: data.isHover ? data.imageHover : data.image,
  888. scale: scale,
  889. anchor: [0.5, 0.5],
  890. anchorXUnits: 'fraction',
  891. anchorYUnits: 'fraction'
  892. })
  893. })
  894. );
  895. this.plotLayers[name].getSource().addFeature(feature);
  896. if (data.isHover) {
  897. this.clickMarker = feature;
  898. this.options.onMarkerClick(data);
  899. }
  900. };
  901. img.src = data.image; // 设置图片的 URL,触发加载
  902. this.markers.push(data);
  903. }
  904. });
  905. if (hideInfoFlag) {
  906. this.clickMarker = null;
  907. this.hideInfo(true);
  908. }
  909. }
  910. // 清除所有标加
  911. clearMarker() {
  912. if (!this.vectorLayer) return;
  913. this.vectorLayer.getSource().clear();
  914. }
  915. clearMarker2(name) {
  916. // 新增图层
  917. if (!this.plotLayers[name]) {
  918. this.plotLayers[name] = new VectorLayer({
  919. source: new VectorSource({
  920. features: []
  921. })
  922. });
  923. this.map.addLayer(this.plotLayers[name]);
  924. } else {
  925. this.plotLayers[name].getSource().clear();
  926. // this.clickMarker = null;
  927. // this.hideInfo();
  928. }
  929. }
  930. // 设置灾害地点 单独图册
  931. setAddress(data) {
  932. this.clearMarker2('address');
  933. this.addressMarker = new Feature({
  934. // 必须是数字类型,字符串不识别
  935. geometry: new Point([Number(data.longitude), Number(data.latitude)]),
  936. name: data.name,
  937. pointer: true,
  938. extData: data
  939. });
  940. this.addressMarker.setStyle(
  941. new Style({
  942. image: new Icon({
  943. src: data.image,
  944. scale: [0.288, 0.288],
  945. anchor: [0.5, 0.5],
  946. anchorXUnits: 'fraction',
  947. anchorYUnits: 'fraction'
  948. })
  949. })
  950. );
  951. this.plotLayers['address'].getSource().addFeature(this.addressMarker);
  952. }
  953. showInfo(content, position, offsetY, isCustom) {
  954. this.hideInfo();
  955. if (!this.infoWindow) {
  956. this.infoWindow = new Overlay({
  957. element: content,
  958. positioning: 'bottom-center', // 你可以根据需要调整定位方式
  959. offset: [0, offsetY ? offsetY : 0] // 偏移量,用于调整覆盖层相对于要素的位置
  960. });
  961. }
  962. this.infoWindow.setPosition(position);
  963. initDrag(this.infoWindow.element);
  964. this.map.addOverlay(this.infoWindow);
  965. }
  966. hideInfo(flag?: boolean) {
  967. this.map.removeOverlay(this.infoWindow);
  968. this.infoWindow = null;
  969. if (!!flag && this.clickMarker) {
  970. const selectData = this.clickMarker.get('extData');
  971. this.clickMarker.setStyle(
  972. new Style({
  973. image: new Icon({
  974. src: selectData.image,
  975. scale: selectData.scale,
  976. anchor: [0.5, 0.5],
  977. anchorXUnits: 'fraction',
  978. anchorYUnits: 'fraction'
  979. })
  980. })
  981. );
  982. this.clickMarker = null;
  983. }
  984. }
  985. /**
  986. *
  987. * @param {Geojon} chaozhou 根据geoJson对象创建Featrue对象
  988. * @returns VectorLayer
  989. */
  990. createVecByJson(json, options) {
  991. const format = new GeoJSON();
  992. const fs = format.readFeatures(json);
  993. this.maskLayer = new VectorLayer({
  994. source: new VectorSource(),
  995. style: new Style({
  996. fill: new Fill({
  997. color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
  998. }),
  999. stroke: new Stroke({
  1000. color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
  1001. width: 2
  1002. })
  1003. }),
  1004. zIndex: options.zIndex ? options.zIndex : 99
  1005. });
  1006. this.map.addLayer(this.maskLayer);
  1007. const extent = [-180, -90, 180, 90];
  1008. const polygonRing = fromExtent(extent);
  1009. fs.forEach((x) => {
  1010. const ft = x.values_.geometry;
  1011. const coords = ft.getCoordinates();
  1012. coords.forEach((coord) => {
  1013. const linearRing = new LinearRing(coord[0]);
  1014. polygonRing.appendLinearRing(linearRing);
  1015. });
  1016. });
  1017. const convertFt = new Feature({
  1018. geometry: polygonRing
  1019. });
  1020. this.maskLayer.getSource().addFeature(convertFt);
  1021. }
  1022. createVecByJson2(json, options) {
  1023. if (!!this.maskLayer2) {
  1024. // this.map.addLayer(this.maskLayer);
  1025. this.map.addLayer(this.maskLayer2);
  1026. } else {
  1027. this.maskLayer2 = new VectorLayer({
  1028. source: new VectorSource(),
  1029. style: new Style({
  1030. fill: new Fill({
  1031. color: 'rgba(0, 0, 0, 0)'
  1032. }),
  1033. stroke: new Stroke({
  1034. color: options.strokeColor ? options.strokeColor : '#268ab9',
  1035. width: options.strokeWeight ? options.strokeWeight : 1
  1036. })
  1037. })
  1038. });
  1039. this.map.addLayer(this.maskLayer2);
  1040. this.maskLayer = new VectorLayer({
  1041. source: new VectorSource(),
  1042. style: new Style({
  1043. fill: new Fill({
  1044. color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
  1045. }),
  1046. stroke: new Stroke({
  1047. color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
  1048. width: 2
  1049. })
  1050. }),
  1051. zIndex: options.zIndex ? options.zIndex : -97
  1052. });
  1053. // // 合并区边界
  1054. // const format = new GeoJSON();
  1055. // const data2 = mergeGeoJsonPolygons(json);
  1056. // const fs = format.readFeatures(data2);
  1057. // const extent = [-180, -90, 180, 90];
  1058. // const polygonRing = fromExtent(extent);
  1059. // fs.forEach((x) => {
  1060. // const ft = x.values_.geometry;
  1061. // const coords = ft.getCoordinates();
  1062. // coords.forEach((coord) => {
  1063. // const linearRing = new LinearRing(coord[0]);
  1064. // polygonRing.appendLinearRing(linearRing);
  1065. // });
  1066. // });
  1067. // const convertFt = new Feature({
  1068. // geometry: polygonRing
  1069. // });
  1070. // this.maskLayer.getSource().addFeature(convertFt);
  1071. // this.map.addLayer(this.maskLayer);
  1072. // 边界部分
  1073. json.features.forEach((feature) => {
  1074. if (feature.geometry.type === 'Polygon') {
  1075. const feature2 = new Feature({
  1076. geometry: new LineString(feature.geometry.coordinates[0])
  1077. });
  1078. this.maskLayer2.getSource().addFeature(feature2);
  1079. } else if (feature.geometry.type === 'MultiPolygon') {
  1080. feature.geometry.coordinates.forEach((polygonCoords) => {
  1081. const feature2 = new Feature({
  1082. geometry: new LineString(polygonCoords[0])
  1083. });
  1084. this.maskLayer2.getSource().addFeature(feature2);
  1085. });
  1086. }
  1087. });
  1088. }
  1089. }
  1090. /**
  1091. * @description 创建矢量图层
  1092. * @param {String} layerName 图层名称
  1093. * @param {Number} zIndex 地图层级默认是0
  1094. * @returns
  1095. */
  1096. createVecLayer(layerName = '', zIndex = 0) {
  1097. const source = new SourceVector({
  1098. crossOrigin: 'anonymous'
  1099. });
  1100. const layer = new Vector({
  1101. source,
  1102. zIndex
  1103. });
  1104. layer.set('layerName', layerName);
  1105. return layer;
  1106. }
  1107. // 分布图遮罩层
  1108. createMask(data) {
  1109. this.removeMask();
  1110. if (!data || data.length === 0) return;
  1111. data.forEach((item) => {
  1112. if (!item.points || item.points.length === 0) return;
  1113. // 遮罩图层的样式
  1114. const maskStyle = new Style({
  1115. fill: new Fill({
  1116. color: item.color // 红色遮罩,50%透明度
  1117. }),
  1118. stroke: new Stroke({
  1119. color: 'rgba(159,159,159,0.7)',
  1120. width: 1
  1121. })
  1122. });
  1123. // 遮罩图层的矢量数据源(初始为空)
  1124. const maskSource = new VectorSource();
  1125. // 创建一个多边形特征
  1126. const polygonFeature = new Feature({
  1127. geometry: new Polygon(item.points)
  1128. });
  1129. const maskLayer = new VectorLayer({
  1130. source: maskSource,
  1131. style: maskStyle,
  1132. properties: {
  1133. name: 'mask'
  1134. }
  1135. });
  1136. this.map.addLayer(maskLayer);
  1137. // 将多边形特征添加到遮罩数据源中
  1138. maskSource.addFeature(polygonFeature);
  1139. });
  1140. }
  1141. removeMask() {
  1142. //移除图层
  1143. const layersArray = this.map.getLayers().getArray();
  1144. layersArray.forEach((layer) => {
  1145. // 检查图层是否有自定义属性,并且该属性是否匹配你要移除的图层的标识符
  1146. if (layer.get('name') === 'mask') {
  1147. this.map.removeLayer(layer);
  1148. }
  1149. });
  1150. }
  1151. removeMask2() {
  1152. if (this.maskLayer) {
  1153. this.map.removeLayer(this.maskLayer);
  1154. this.maskLayer = null;
  1155. }
  1156. }
  1157. removeMask3(isHide) {
  1158. if (this.maskLayer) {
  1159. this.map.removeLayer(this.maskLayer);
  1160. if (!isHide) {
  1161. this.maskLayer = [];
  1162. }
  1163. }
  1164. if (this.maskLayer2) {
  1165. this.map.removeLayer(this.maskLayer2);
  1166. if (!isHide) {
  1167. this.maskLayer2 = [];
  1168. }
  1169. }
  1170. }
  1171. /**
  1172. * 绘制经纬线
  1173. * visible: boolean 是否可见
  1174. */
  1175. handleLngLatLine(visible: boolean) {
  1176. // 创建经纬网图层
  1177. const graticule = new Graticule({
  1178. name: 'Graticule',
  1179. showLabels: true, // 为每条刻度线绘制一个带有各自纬度/经度的标签
  1180. wrapX: false, // 是否水平重复经纬网
  1181. targetSize: 230,
  1182. zIndex: 99999,
  1183. strokeStyle: new Stroke({
  1184. // 用于绘制刻度线的样式
  1185. color: '#dcdcdc', // 线条颜色
  1186. width: 1 // 线条宽度
  1187. }),
  1188. lonLabelStyle: new Text({
  1189. font: '12px Calibri,sans-serif',
  1190. textBaseline: 'bottom',
  1191. fill: new Fill({
  1192. color: '#9d9d9d'
  1193. })
  1194. }),
  1195. latLabelPosition: 0,
  1196. latLabelStyle: new Text({
  1197. font: '12px Calibri,sans-serif',
  1198. textAlign: 'left',
  1199. textBaseline: 'end',
  1200. fill: new Fill({
  1201. color: '#9d9d9d'
  1202. })
  1203. })
  1204. });
  1205. if (visible) {
  1206. this.map.addLayer(graticule);
  1207. } else {
  1208. this.removeLayer('Graticule');
  1209. }
  1210. }
  1211. /**
  1212. * 移除指定name的layer
  1213. * layerName: string
  1214. * */
  1215. removeLayer(layerName: string) {
  1216. const layers = this.map.getLayers();
  1217. layers.forEach((element) => {
  1218. if (!!element && element.get('name') === layerName) {
  1219. //移除
  1220. this.map.removeLayer(element);
  1221. }
  1222. });
  1223. }
  1224. // 创建图形
  1225. createGraphics(data: any) {
  1226. if (data.type === 'circle') {
  1227. // 绘制圆形
  1228. return this.createCircle(data);
  1229. } else if (['polygon', 'rectangle'].includes(data.type)) {
  1230. // 绘制矩形、多边形
  1231. return this.createPolygon(data);
  1232. }
  1233. // else if (data.type === 'marker') {
  1234. // // 绘制图标
  1235. // return createMarker(data);
  1236. // } else if (data.type === 'measureArea') {
  1237. // // 绘制面积
  1238. // return createMeasureArea(data);
  1239. // } else if (data.type === 'text') {
  1240. // // 文字
  1241. // return addText(data);
  1242. // } else if (data.type === 'straightLine') {
  1243. // // 直线
  1244. // return createStraightLine(data);
  1245. // }
  1246. }
  1247. createCircle(data) {
  1248. const circle = new Circle(data.center, data.radius);
  1249. const feature = new Feature(circle);
  1250. feature.setStyle(
  1251. new Style({
  1252. stroke: new Stroke({
  1253. color: rgbToRgba(data.strokeColor, data.strokeOpacity),
  1254. width: data.strokeWeight
  1255. }),
  1256. fill: new Fill({
  1257. color: rgbToRgba(data.fillColor, data.fillOpacity)
  1258. })
  1259. })
  1260. );
  1261. this.drawVector.getSource().addFeature(feature);
  1262. return feature;
  1263. }
  1264. createPolygon(data) {
  1265. const polygon = new Polygon([data.path]);
  1266. const feature = new Feature(polygon);
  1267. feature.setStyle(
  1268. new Style({
  1269. stroke: new Stroke({
  1270. color: rgbToRgba(data.strokeColor, data.strokeOpacity),
  1271. width: data.strokeWeight
  1272. }),
  1273. fill: new Fill({
  1274. color: rgbToRgba(data.fillColor, data.fillOpacity)
  1275. })
  1276. })
  1277. );
  1278. this.drawVector.getSource().addFeature(feature);
  1279. return feature;
  1280. }
  1281. createLineString(data) {
  1282. const lineString = new LineString(data.path);
  1283. const feature = new Feature(lineString);
  1284. feature.setStyle(
  1285. new Style({
  1286. stroke: new Stroke({
  1287. color: rgbToRgba(data.strokeColor, data.strokeOpacity),
  1288. width: data.strokeWeight
  1289. }),
  1290. fill: new Fill({
  1291. color: rgbToRgba(data.fillColor, data.fillOpacity)
  1292. })
  1293. })
  1294. );
  1295. this.drawVector.getSource().addFeature(feature);
  1296. return feature;
  1297. }
  1298. trackPlayback(lineArr) {
  1299. if (!!this.carFeature) {
  1300. this.carLayer.getSource().removeFeature(this.carFeature);
  1301. }
  1302. if (!!this.traceFeature) {
  1303. this.carLayer.getSource().removeFeature(this.traceFeature);
  1304. }
  1305. const getAngle = (point1, point2) => {
  1306. let arc = 0;
  1307. if (point2 && point2.length && point1 && point1.length) {
  1308. if ((point2[0] - point1[0] >= 0 && point2[1] - point1[1] >= 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] > 0)) {
  1309. arc = Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1]));
  1310. } else if ((point2[0] - point1[0] > 0 && point2[1] - point1[1] < 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] < 0)) {
  1311. arc = Math.PI + Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1]));
  1312. }
  1313. }
  1314. return arc;
  1315. };
  1316. let lastTime = Date.now();
  1317. const source = new VectorSource();
  1318. let distance = 0;
  1319. const angle = getAngle(lineArr[0], lineArr[1]);
  1320. const speed = 500;
  1321. let animationFlag = false;
  1322. this.carFeature = new Feature({
  1323. geometry: new Point(lineArr[0])
  1324. });
  1325. const icon = new Icon({
  1326. crossOrigin: 'anonymous',
  1327. src: carImg,
  1328. width: 13,
  1329. height: 26,
  1330. anchor: [0.5, 0.5],
  1331. rotation: angle
  1332. });
  1333. this.carFeature.setStyle(
  1334. new Style({
  1335. image: icon
  1336. })
  1337. );
  1338. this.traceFeature = new Feature({
  1339. geometry: new LineString(lineArr)
  1340. });
  1341. const route = new LineString(lineArr);
  1342. this.carLayer = new VectorLayer({
  1343. source: source,
  1344. style: new Style({
  1345. stroke: new Stroke({
  1346. color: '#AF5',
  1347. width: 5
  1348. })
  1349. })
  1350. });
  1351. this.carLayer.getSource().addFeatures([this.carFeature, this.traceFeature]);
  1352. const move = (e) => {
  1353. const time = e.frameState.time;
  1354. // 时间戳差(毫秒)
  1355. const elapsedTime = time - lastTime;
  1356. // 距离(其实是比例的概念)
  1357. distance = distance + (speed * elapsedTime) / 1e6;
  1358. if (distance >= 1) {
  1359. distance = 0;
  1360. animationFlag = false;
  1361. stopAnimation();
  1362. return;
  1363. }
  1364. // 保存当前时间
  1365. lastTime = time;
  1366. // 上次坐标
  1367. const lastCoord = this.carFeature.getGeometry().getCoordinates();
  1368. // 获取新位置的坐标点
  1369. const curCoord = route.getCoordinateAt(distance);
  1370. // 设置新坐标
  1371. this.carFeature.getGeometry().setCoordinates(curCoord);
  1372. this.map.getView().setCenter(curCoord);
  1373. // 设置角度
  1374. this.carFeature.getStyle().getImage().setRotation(getAngle(lastCoord, curCoord));
  1375. // 调用地图渲染
  1376. this.map.render();
  1377. };
  1378. const stopAnimation = () => {
  1379. this.carLayer.un('postrender', move);
  1380. };
  1381. this.map.addLayer(this.carLayer);
  1382. this.carLayer.on('postrender', move);
  1383. // 触发地图渲染
  1384. const geo = this.carFeature.getGeometry().clone();
  1385. this.carFeature.setGeometry(geo);
  1386. return [this.carLayer];
  1387. }
  1388. drawData(data) {
  1389. const res = [];
  1390. data.forEach((item) => {
  1391. let graphic;
  1392. if (['rectangle', 'polygon', 'anyLine'].includes(item.type)) {
  1393. graphic = this.createPolygon(item);
  1394. graphic.set('id', item.id);
  1395. res.push(graphic);
  1396. } else if (item.type === 'circle') {
  1397. graphic = this.createCircle(item);
  1398. graphic.set('id', item.id);
  1399. res.push(graphic);
  1400. } else if (item.type === 'straightLine') {
  1401. graphic = this.createLineString(item);
  1402. graphic.set('id', item.id);
  1403. res.push(graphic);
  1404. } else if (item.type === 'text') {
  1405. const { text } = this.addText(item);
  1406. res.push(text);
  1407. } else if (item.type === 'measureArea') {
  1408. graphic = this.createPolygon(item);
  1409. graphic.set('id', item.id);
  1410. const style = new Style({
  1411. stroke: new Stroke({
  1412. color: rgbToRgba(item.strokeColor, item.strokeOpacity),
  1413. width: item.strokeWeight
  1414. }),
  1415. fill: new Fill({
  1416. color: rgbToRgba(item.fillColor, item.fillOpacity)
  1417. }),
  1418. text: new Text({
  1419. text: '区域面积' + item.area.toFixed(2) + '平方米',
  1420. font: '14px Calibri,sans-serif',
  1421. fill: new Fill({ color: '#000' }),
  1422. stroke: new Stroke({
  1423. color: '#fff',
  1424. width: 3
  1425. }),
  1426. overflow: true
  1427. })
  1428. });
  1429. graphic.setStyle(style);
  1430. res.push(graphic);
  1431. } else if (item.type === 'marker') {
  1432. // 创建标注点
  1433. const marker = new Feature({
  1434. // 必须是数字类型,字符串不识别
  1435. geometry: new Point([item.longitude, item.latitude]),
  1436. // name: item.name,
  1437. // icon: item.icon,
  1438. // imageHover: item.imageHover,
  1439. // size: item.size,
  1440. pointer: true
  1441. });
  1442. marker.set('id', item.id);
  1443. const img = new Image();
  1444. img.onload = () => {
  1445. // 图片加载完成后,可以访问其 width 和 height 属性
  1446. const width = img.width;
  1447. const height = img.height;
  1448. const style = new Style({
  1449. image: new Icon({
  1450. anchor: [0.5, 1],
  1451. src: item.icon,
  1452. size: [width, height],
  1453. scale: !!item.size[0] ? item.size[0] / width : 1
  1454. }),
  1455. text: new Text({
  1456. text: item.name,
  1457. fill: new Fill({
  1458. color: '#000'
  1459. }),
  1460. stroke: new Stroke({
  1461. color: '#fff',
  1462. width: 3
  1463. })
  1464. })
  1465. });
  1466. marker.setStyle(style);
  1467. };
  1468. img.src = item.icon; // 设置图片的 URL,触发加载
  1469. this.drawVector.getSource().addFeature(marker);
  1470. res.push(marker);
  1471. }
  1472. });
  1473. return res;
  1474. }
  1475. getVectorLayer() {
  1476. return this.vectorLayer;
  1477. }
  1478. getDrawVector() {
  1479. return this.drawVector;
  1480. }
  1481. getMap() {
  1482. return this.map;
  1483. }
  1484. getScale() {
  1485. return this.scale;
  1486. }
  1487. getMouseTool() {
  1488. return this.drawTool;
  1489. }
  1490. setDrawEndMethod(newMethod) {
  1491. this.drawEndMethod = newMethod;
  1492. }
  1493. }