1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228 |
- // 引入OpenLayers的主模块
- import Map from 'ol/Map';
- import View from 'ol/View';
- import Feature from 'ol/Feature';
- import Point from 'ol/geom/Point';
- import VectorLayer from 'ol/layer/Vector';
- import VectorSource from 'ol/source/Vector';
- import Style from 'ol/style/Style';
- import Icon from 'ol/style/Icon';
- import Text from 'ol/style/Text';
- import Projection from 'ol/proj/Projection';
- import { getWidth, getTopLeft } from 'ol/extent';
- import TileLayer from 'ol/layer/Tile';
- import WMTS from 'ol/source/WMTS';
- import WMTSTileGrid from 'ol/tilegrid/WMTS';
- import WMTSCapabilities from 'ol/format/WMTSCapabilities';
- import { Fill, Stroke } from 'ol/style';
- import proj4 from 'proj4';
- import { register } from 'ol/proj/proj4';
- import { defaults } from 'ol/control';
- import Vector from 'ol/layer/Vector';
- import SourceVector from 'ol/source/Vector';
- import GeoJSON from 'ol/format/GeoJSON';
- import { fromLonLat } from 'ol/proj';
- import axios from 'axios';
- import { fromExtent } from 'ol/geom/Polygon';
- import { LinearRing, LineString, Polygon } from 'ol/geom';
- import { Graticule } from 'ol/layer';
- import { getPointsCenter, mergeGeoJsonPolygons } from '@/utils/gisUtils';
- import { Cluster } from 'ol/source';
- import CircleStyle from 'ol/style/Circle';
- import Overlay from 'ol/Overlay';
- import { DoubleClickZoom, DragPan, Draw, Select } from 'ol/interaction';
- import { click } from 'ol/events/condition';
- import Circle from 'ol/geom/Circle';
- import { deepClone, getRgba, hexToRgba, initDrag, rgbToRgba } from '@/utils';
- import { createBox } from 'ol/interaction/Draw';
- import * as turf from '@turf/turf';
- import { nanoid } from 'nanoid';
- import carImg from '@/assets/images/car.png';
- const tk = 'a8df87f1695d224d2679aa805c1268d9';
- const commonUrl = import.meta.env.VITE_APP_BASE_API2 + 'api/oneShare/proxyHandler/gd/';
- // proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs +type=crs');
- 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"]]'
- );
- // 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');
- register(proj4);
- const projection = new Projection({
- code: 'EPSG:4490',
- units: 'degrees',
- axisOrientation: 'neu'
- });
- projection.setExtent([-180, -90, 180, 90]);
- projection.setWorldExtent([-180, -90, 180, 90]);
- const projectionExtent = [-180, -90, 180, 90];
- const size = getWidth(projectionExtent) / 256;
- const resolutions = [];
- for (let z = 2; z < 22; ++z) {
- resolutions[z] = size / Math.pow(2, z);
- }
- export class olMap {
- private map;
- private options;
- private markers = [];
- private drawOptions = {
- graphicsType: 'circle',
- strokeColor: '#f80102',
- strokeOpacity: 1,
- strokeWeight: 2,
- fillColor: '#f80102',
- fillOpacity: 0,
- strokeStyle: 'solid',
- icon: '',
- iconName: ''
- };
- private plot;
- private drawVector;
- private drawTool;
- private vectorLayer;
- // 边界遮罩图层
- private maskLayer;
- private maskLayer2;
- // 显示信息框
- private infoWindow;
- private select;
- // 车辆轨迹
- private carLayer;
- private carFeature;
- private traceFeature;
- // 自定义绘制结束调用方法
- private drawEndMethod;
- private selectedFeature;
- constructor(options) {
- this.options = options;
- this.map = new Map({
- controls: defaults({
- zoom: false,
- rotate: false
- }),
- layers: [],
- target: options.dom,
- view: new View({
- center: options.center && options.center.length === 2 ? [options.center[0], options.center[1]] : [110.90153121597234, 21.98323671981171],
- zoom: options.zoom ? options.zoom : 9.6,
- projection: projection,
- maxZoom: options.maxZoom ? options.maxZoom : 18,
- minZoom: options.minZoom ? options.minZoom : 1
- })
- });
- // 初始化比例尺
- if (options.showScale) {
- // this.map.addControl(new AMap.Scale());
- }
- if (options.drawTool?.use) {
- this.initMouseTool(options.drawTool);
- }
- this.initLayer(options);
- // 给标注点添加手势
- this.map.on('pointermove', (e) => {
- this.map.getTargetElement().style.cursor = 'auto';
- const features = this.map.getFeaturesAtPixel(e.pixel);
- features.forEach((feature) => {
- const originalFeature = feature.get('features') ? feature.get('features')[0] : '';
- if (!!originalFeature && originalFeature.get('pointer')) {
- this.map.getTargetElement().style.cursor = 'pointer'; //设置鼠标样式
- } else {
- this.map.getTargetElement().style.cursor = 'auto';
- }
- });
- });
- // 创建选择交互
- this.select = new Select({
- condition: click,
- style: null,
- filter: (feature, layer) => {
- // 只有vectorLayer图层可选择
- return layer === this.vectorLayer;
- }
- });
- this.map.addInteraction(this.select);
- // 监听Select交互的select事件
- this.select.on('select', (event) => {
- const selectedFeatures = event.selected[0]; // 获取被选中的要素集合
- const features = selectedFeatures.get('features');
- if (selectedFeatures && !!features) {
- const originalFeature = features[0];
- const size = features.length;
- if (size === 1) {
- if (this.selectedFeature !== originalFeature) {
- if (this.selectedFeature) {
- this.selectedFeature.set('icon', this.selectedFeature.get('image'));
- }
- this.selectedFeature = originalFeature;
- const icon = originalFeature.get('imageHover');
- const extData = originalFeature.get('extData');
- originalFeature.set('icon', icon);
- options.onMarkerClick(extData);
- }
- } else {
- // 聚合要素
- this.select.getFeatures().clear();
- const currentZoom = this.map.getView().getZoom();
- this.map.getView().setZoom(currentZoom + 1);
- const points = [];
- features.forEach((feature) => {
- const geometry = feature.getGeometry(); // 获取要素的几何对象
- const type = geometry.getType(); // 获取几何类型
- if (type === 'Point') {
- points.push(geometry.getCoordinates());
- }
- });
- const newFeature = getPointsCenter(points);
- this.map.getView().setCenter(newFeature.geometry.coordinates);
- event.selected = [];
- }
- }
- });
- }
- async initLayer(options) {
- // 添加新的图层
- if (Array.isArray(options.id)) {
- for (const layer of options.id) {
- if (typeof layer === 'string') {
- await this.formatXml(layer); // 等待当前 layer 处理完成
- } else {
- await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成
- }
- }
- } else if (options.id === 'tianditu') {
- await this.formatXml2();
- } else if (options.id) {
- // 如果 options.id 不是数组,但确实是一个图层,则直接处理
- await this.formatXml(options.id, options.minZoom, options.maxZoom, options.zIndex, options.visible);
- }
- // 创建Vector层并添加到地图上
- this.vectorLayer = new VectorLayer({
- source: new VectorSource({
- features: []
- })
- });
- this.map.addLayer(this.vectorLayer);
- if (typeof this.options.onLoadCompleted === 'function') {
- this.options.onLoadCompleted(this.map);
- }
- }
- formatXml2() {
- const baseUrl = 'https://t{0-7}.tianditu.gov.cn/';
- const vecType = 'vec'; // 矢量图层类型
- const projType = 'w'; // 投影类型:web mercator
- const reqParams = {
- SERVICE: 'WMTS',
- REQUEST: 'GetTile',
- VERSION: '1.0.0',
- LAYER: '',
- STYLE: 'default',
- TILEMATRIXSET: projType,
- FORMAT: 'tiles',
- TILECOL: '{x}',
- TILEROW: '{y}',
- TILEMATRIX: '{z}',
- tk: key
- };
- const newParams = Object.assign({}, params, { LAYER: dataType });
- let paramsStr = '';
- for (const [k, v] of Object.entries(newParams)) {
- paramsStr += `${k}=${v}&`;
- }
- return `${baseUrl}${dataType}_${projType}/wmts?${paramsStr.slice(0, -1)}`;
- }
- formatXml(code: string, minZoom?: number, maxZoom?: number, zIndex?: number, visible?: boolean) {
- const xml = new WMTSCapabilities();
- return this.getCapabilities(code).then((lists) => {
- const geojson = xml.read(lists.data);
- const data = geojson.Contents.Layer[0];
- const layerParam = {
- layerName: data.Abstract,
- styleName: data.Identifier,
- tilematrixset: data.TileMatrixSetLink[0].TileMatrixSet,
- format: data.Format[0]
- };
- this.createWmsLayer(code, layerParam, minZoom, maxZoom, zIndex, visible);
- });
- }
- // 请求接口获取地图信息
- getCapabilities(code) {
- return axios.get(commonUrl + code + '?SERVICE=WMTS&REQUEST=GetCapabilities');
- }
- // 请求地图图片加载图层
- createWmsLayer(code, layerParam, minZoom = 0, maxZoom, zIndex = -1, visible = true) {
- const source = new WMTS({
- url: commonUrl + code,
- crossOrigin: 'Anonymous',
- layer: layerParam.layerName,
- style: layerParam.styleName,
- matrixSet: layerParam.tilematrixset,
- format: layerParam.format,
- wrapX: true,
- tileGrid: new WMTSTileGrid({
- origin: getTopLeft(projectionExtent),
- resolutions: resolutions,
- matrixIds: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21']
- })
- });
- const layer = new TileLayer({
- name: code,
- source: source,
- zIndex: zIndex,
- minZoom: minZoom,
- maxZoom,
- visible: visible
- });
- layer.set('layerName', code);
- this.map.addLayer(layer);
- }
- // 初始化绘画工具
- initMouseTool(options) {
- this.drawOptions = {
- graphicsType: options.graphicsType ? options.graphicsType : 'cirlce',
- strokeColor: options.color,
- strokeOpacity: 1,
- strokeWeight: 2,
- fillColor: options.color,
- fillOpacity: options.drawType === '1' ? 0 : 0.5,
- strokeStyle: 'solid'
- };
- // 创建矢量图层用于绘制
- this.drawVector = new Vector({
- source: new VectorSource({
- features: []
- })
- });
- this.map.addLayer(this.drawVector);
- }
- // 绘制结束事件
- onDrawEnd(event) {
- const feature = event.feature;
- const aa = feature.getGeometry();
- // 继续编辑编辑
- this.drawGraphics(this.drawOptions.graphicsType);
- }
- // 绘制图形
- drawGraphics(newOptions: MouseTool) {
- const typeList = {
- circle: 'Circle',
- rectangle: 'Circle',
- polygon: 'Polygon',
- measureArea: 'Polygon',
- straightLine: 'LineString',
- marker: 'Point',
- text: 'Point',
- anyLine: 'AnyLine'
- };
- const data = getRgba(newOptions.color);
- let geometryFunction = null;
- if (newOptions.graphicsType === 'rectangle') {
- // 绘制矩形的方法
- geometryFunction = createBox();
- }
- if (!!typeList[newOptions.graphicsType]) {
- if (newOptions.graphicsType === 'text') {
- this.drawOptions = {
- type: newOptions.graphicsType,
- title: newOptions.title,
- text: newOptions.text,
- fontSize: newOptions.fontSize,
- fontColor: newOptions.fontColor,
- lnglat: newOptions.lnglat
- };
- // 绘制文字
- return this.addText(this.drawOptions);
- } else {
- this.drawOptions = {
- type: newOptions.graphicsType,
- title: newOptions.title,
- strokeColor: !!data.color ? data.color : newOptions.color,
- strokeOpacity: 1,
- strokeWeight: 1,
- fillColor: data.color,
- fillOpacity: data.opacity,
- strokeStyle: 'solid'
- };
- if (newOptions.graphicsType === 'marker') {
- this.drawOptions.icon = newOptions.icon;
- this.drawOptions.iconName = newOptions.iconName;
- this.drawOptions.size = newOptions.size;
- }
- this.closeDraw();
- if (newOptions.graphicsType === 'anyLine') {
- this.drawAnyLine();
- } else {
- // 创建绘制交互
- let style = new Style({
- stroke: new Stroke({
- color: rgbToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity),
- width: this.drawOptions.strokeWeight
- }),
- fill: new Fill({
- color: rgbToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity)
- })
- });
- this.drawTool = new Draw({
- source: this.drawVector.getSource(),
- type: typeList[newOptions.graphicsType],
- geometryFunction: geometryFunction,
- style: style
- });
- // 添加绘制交互到地图
- this.map.addInteraction(this.drawTool);
- // 监听绘制结束事件
- this.drawTool.on('drawend', (event) => {
- const feature = event.feature;
- // 获取几何对象
- if (newOptions.graphicsType !== 'marker') {
- if (newOptions.graphicsType === 'measureArea') {
- const geometry = feature.getGeometry();
- const coordinates = geometry.getCoordinates();
- const pathArr = coordinates[0];
- const area = turf.area(turf.polygon([pathArr]));
- style = new Style({
- stroke: style.getStroke(),
- fill: style.getFill(),
- text: new Text({
- text: '区域面积' + area.toFixed(2) + '平方米',
- font: '14px Calibri,sans-serif',
- fill: new Fill({ color: '#000' }),
- stroke: new Stroke({
- color: '#fff',
- width: 3
- }),
- overflow: true
- })
- });
- }
- feature.setStyle(style);
- }
- });
- }
- }
- }
- return this.drawOptions;
- }
- // 空间分析绘制图形
- drawGraphics2(newOptions: MouseTool) {
- const typeList = {
- circle: 'Circle',
- rectangle: 'Circle',
- polygon: 'Polygon'
- };
- let geometryFunction = null;
- if (newOptions.graphicsType === 'rectangle') {
- // 绘制矩形的方法
- geometryFunction = createBox();
- }
- if (!!typeList[newOptions.graphicsType]) {
- this.drawOptions = {
- type: newOptions.graphicsType,
- strokeColor: newOptions.color,
- strokeOpacity: 1,
- strokeWeight: 1,
- fillColor: newOptions.color,
- fillOpacity: newOptions.drawType === '1' ? '0' : '0.5',
- strokeStyle: 'solid'
- };
- this.closeDraw();
- // 创建绘制交互
- const style = new Style({
- stroke: new Stroke({
- color: hexToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity),
- width: this.drawOptions.strokeWeight
- }),
- fill: new Fill({
- color: hexToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity)
- })
- });
- this.drawTool = new Draw({
- source: this.drawVector.getSource(),
- type: typeList[newOptions.graphicsType],
- geometryFunction: geometryFunction,
- style: style
- });
- // 添加绘制交互到地图
- this.map.addInteraction(this.drawTool);
- // 监听绘制结束事件
- this.drawTool.on('drawend', (event) => {
- const feature = event.feature;
- // 获取几何对象
- feature.setStyle(style);
- });
- }
- }
- addText(options) {
- // 创建文本覆盖物
- const style = new Style({
- text: new Text({
- text: options.text,
- font: options.fontSize + ' Calibri,sans-serif',
- fill: new Fill({ color: options.fontColor }),
- // stroke: new Stroke({
- // color: '#fff',
- // width: 3
- // }),
- overflow: true
- })
- });
- const text = new Feature({
- geometry: new Point(options.lnglat)
- });
- text.setStyle(style);
- // 将文本覆盖物添加到地图
- this.drawVector.getSource().addFeature(text);
- const id = nanoid();
- text.set('id', id);
- const data: any = deepClone(options);
- data.id = id;
- return { text, data };
- }
- drawAnyLine() {
- document.addEventListener('touchend', this.handleTouchEnd);
- this.map.on('pointerdown', this.handleTouchStart);
- this.map.on('pointermove', this.handleTouchMove.bind(null, this.options));
- document.addEventListener('mouseup', this.handleTouchEnd.bind(null, this.options));
- }
- handleTouchStart(e) {
- this.drawing = true;
- const interactions = this.map.getInteractions();
- interactions.forEach((interaction) => {
- if (interaction instanceof DragPan || interaction instanceof DoubleClickZoom) {
- interaction.setActive(false); // 修改为需要的值,true或false
- }
- });
- this.path = [e.lnglat];
- // if (this.anyLine) {
- // map.remove(anyLine);
- // }
- }
- handleTouchMove(options, e) {
- if (!this.drawing) return;
- this.path.push(e.lnglat);
- // if (anyLine) {
- // map.remove(anyLine);
- // }
- this.anyLine = new Feature({
- geometry: new LineString(this.path) // path为坐标数组,如[[x1,y1], [x2,y2]]
- });
- const lineStyle = new Style({
- stroke: new Stroke({
- color: hexToRgba(options.strokeColor, options.strokeOpacity), // 合并颜色与透明度
- width: options.strokeWeight,
- lineJoin: 'round' // 线段连接处圆角
- })
- });
- this.anyLine.setStyle(lineStyle);
- // new AMap.Polyline({
- // path: path,
- // strokeColor: options.strokeColor,
- // strokeOpacity: options.strokeOpacity,
- // strokeWeight: options.strokeWeight,
- // strokeStyle: options.strokeStyle,
- // lineJoin: 'round'
- // });
- this.drawVector.getSource().addFeature(this.anyLine);
- }
- handleTouchEnd(options) {
- // drawing = false;
- // map.setStatus({
- // showIndoorMap: true,
- // dragEnable: true,
- // keyboardEnable: true,
- // doubleClickZoom: true,
- // zoomEnable: true,
- // rotateEnable: true
- // });
- // map.off('touchstart', handleTouchStart);
- // map.on('touchmove', handleTouchMove.bind(null, options));
- // document.addEventListener('touchend', handleTouchEnd.bind(null, options));
- // map.off('mousedown', handleTouchStart);
- // map.off('mousemove', handleTouchMove);
- // document.removeEventListener('mouseup', handleTouchEnd);
- // if (!!drawEndMethod) {
- // drawEndMethod(options, anyLine);
- // }
- // anyLine = null;
- }
- // 关闭绘制
- closeDraw() {
- if (!this.drawTool) return;
- this.map.removeInteraction(this.drawTool);
- // this.drawTool.abortDrawing();
- }
- // 切换图层
- async replaceLayers(newLayers, loadendFunc) {
- // 遍历当前的所有图层并移除它们
- this.map.getLayers().forEach((layer) => {
- this.map.removeLayer(layer);
- });
- if (Array.isArray(newLayers)) {
- for (const layer of newLayers) {
- if (typeof layer === 'string') {
- await this.formatXml(layer); // 等待当前 layer 处理完成
- } else {
- await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成
- }
- }
- } else {
- // 如果 options.id 不是数组,但确实是一个图层,则直接处理
- await this.formatXml(newLayers.id, newLayers.minZoom, newLayers.maxZoom, newLayers.zIndex, newLayers.visible);
- }
- // 创建Vector层并添加到地图上
- this.vectorLayer = new VectorLayer({
- source: new VectorSource({
- features: []
- })
- });
- this.map.addLayer(this.vectorLayer);
- const point = JSON.parse(JSON.stringify(this.markers));
- this.markers = [];
- this.addMarker(point);
- if (loadendFunc) {
- loadendFunc();
- }
- }
- // 集群点样式
- clusterStyle(feature) {
- const originalFeature = feature.get('features')[0];
- const size = feature.get('features').length;
- if (size > 1) {
- const outerCircle = new CircleStyle({
- radius: 20,
- fill: new Fill({
- color: 'rgba(79, 176, 206, 0.5)'
- }),
- stroke: new Stroke({
- color: 'rgba(79, 176, 206, 1)'
- })
- });
- return [
- new Style({
- image: outerCircle,
- text: new Text({
- text: size.toString(),
- font: '14px sans-serif',
- fill: new Fill({
- color: '#ffff'
- })
- })
- })
- ];
- }
- return new Style({
- geometry: originalFeature.getGeometry(),
- image: new Icon({
- anchor: [0.5, 0.5],
- scale: 0.146,
- anchorXUnits: 'fraction',
- anchorYUnits: 'pixels',
- src: originalFeature.get('icon')
- })
- });
- }
- addMarker(points) {
- this.clearMarker();
- const features = [];
- points.forEach((point) => {
- // 创建标注点
- const feature = new Feature({
- // 必须是数字类型,字符串不识别
- geometry: new Point([Number(point.longitude), Number(point.latitude)]),
- name: point.name,
- icon: point.icon,
- imageHover: point.imageHover,
- size: point.size,
- pointer: true,
- extData: point
- });
- // 设置自定义属性
- feature.set('pointer', true);
- const img = new Image();
- img.onload = () => {
- // 图片加载完成后,可以访问其 width 和 height 属性
- const width = img.width;
- const height = img.height;
- const style = new Style({
- image: new Icon({
- anchor: [0.5, 0.5],
- anchorXUnits: 'fraction',
- anchorYUnits: 'pixels',
- src: point.icon,
- size: [width, height],
- scale: !!point.size[0] ? point.size[0] / width : 1
- }),
- text: new Text({
- text: point.name,
- fill: new Fill({
- color: '#000'
- }),
- stroke: new Stroke({
- color: '#fff',
- width: 3
- })
- })
- });
- features.setStyle(style);
- };
- img.src = point.icon; // 设置图片的 URL,触发加载
- features.push(feature);
- this.markers.push(point);
- });
- const vectorSource = new VectorSource({
- features: features
- });
- const clusterSource = new Cluster({
- distance: 30,
- source: vectorSource
- });
- this.vectorLayer.setStyle(this.clusterStyle);
- this.vectorLayer.setSource(clusterSource);
- }
- // 清除所有标加
- clearMarker() {
- if (!this.vectorLayer) return;
- this.vectorLayer.getSource().clear();
- }
- showInfo(content, position, isCustom) {
- this.hideInfo();
- if (!this.infoWindow) {
- this.infoWindow = new Overlay({
- element: content,
- positioning: 'bottom-center', // 你可以根据需要调整定位方式
- offset: [0, -10] // 偏移量,用于调整覆盖层相对于要素的位置
- });
- }
- this.infoWindow.setPosition(position);
- initDrag(this.infoWindow.element);
- this.map.addOverlay(this.infoWindow);
- }
- hideInfo(flag) {
- this.map.removeOverlay(this.infoWindow);
- this.infoWindow = null;
- if (!!flag && this.select) {
- this.select.getFeatures().clear();
- }
- }
- /**
- *
- * @param {Geojon} chaozhou 根据geojson对象创建Featrue对象
- * @returns VectorLayer
- */
- createVecByJson(json, options) {
- const format = new GeoJSON();
- const fs = format.readFeatures(json);
- this.maskLayer = new VectorLayer({
- source: new VectorSource(),
- style: new Style({
- fill: new Fill({
- color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
- }),
- stroke: new Stroke({
- color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
- width: 2
- })
- }),
- zIndex: options.zIndex ? options.zIndex : 99
- });
- this.map.addLayer(this.maskLayer);
- const extent = [-180, -90, 180, 90];
- const polygonRing = fromExtent(extent);
- fs.forEach((x) => {
- const ft = x.values_.geometry;
- const coords = ft.getCoordinates();
- coords.forEach((coord) => {
- const linearRing = new LinearRing(coord[0]);
- polygonRing.appendLinearRing(linearRing);
- });
- });
- const convertFt = new Feature({
- geometry: polygonRing
- });
- this.maskLayer.getSource().addFeature(convertFt);
- }
- createVecByJson2(json, options) {
- if (!!this.maskLayer2) {
- // this.map.addLayer(this.maskLayer);
- this.map.addLayer(this.maskLayer2);
- } else {
- this.maskLayer2 = new VectorLayer({
- source: new VectorSource(),
- style: new Style({
- fill: new Fill({
- color: 'rgba(0, 0, 0, 0)'
- }),
- stroke: new Stroke({
- color: options.strokeColor ? options.strokeColor : '#268ab9',
- width: options.strokeWeight ? options.strokeWeight : 1
- })
- })
- });
- this.map.addLayer(this.maskLayer2);
- this.maskLayer = new VectorLayer({
- source: new VectorSource(),
- style: new Style({
- fill: new Fill({
- color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)'
- }),
- stroke: new Stroke({
- color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)',
- width: 2
- })
- }),
- zIndex: options.zIndex ? options.zIndex : 99
- });
- // // 合并区边界
- // const format = new GeoJSON();
- // const data2 = mergeGeoJsonPolygons(json);
- // const fs = format.readFeatures(data2);
- // const extent = [-180, -90, 180, 90];
- // const polygonRing = fromExtent(extent);
- // fs.forEach((x) => {
- // const ft = x.values_.geometry;
- // const coords = ft.getCoordinates();
- // coords.forEach((coord) => {
- // const linearRing = new LinearRing(coord[0]);
- // polygonRing.appendLinearRing(linearRing);
- // });
- // });
- // const convertFt = new Feature({
- // geometry: polygonRing
- // });
- // this.maskLayer.getSource().addFeature(convertFt);
- // this.map.addLayer(this.maskLayer);
- // 边界部分
- json.features.forEach((feature) => {
- if (feature.geometry.type === 'Polygon') {
- const feature2 = new Feature({
- geometry: new LineString(feature.geometry.coordinates[0])
- });
- this.maskLayer2.getSource().addFeature(feature2);
- } else if (feature.geometry.type === 'MultiPolygon') {
- feature.geometry.coordinates.forEach((polygonCoords) => {
- const feature2 = new Feature({
- geometry: new LineString(polygonCoords[0])
- });
- this.maskLayer2.getSource().addFeature(feature2);
- });
- }
- });
- }
- }
- /**
- * @description 创建矢量图层
- * @param {String} layerName 图层名称
- * @param {Number} zIndex 地图层级默认是0
- * @returns
- */
- createVecLayer(layerName = '', zIndex = 0) {
- const source = new SourceVector({
- crossOrigin: 'anonymous'
- });
- const layer = new Vector({
- source,
- zIndex
- });
- layer.set('layerName', layerName);
- return layer;
- }
- // 分布图遮罩层
- createMask(data) {
- this.removeMask();
- if (!data || data.length === 0) return;
- data.forEach((item) => {
- if (!item.points || item.points.length === 0) return;
- // 遮罩图层的样式
- const maskStyle = new Style({
- fill: new Fill({
- color: item.color // 红色遮罩,50%透明度
- }),
- stroke: new Stroke({
- color: 'rgba(159,159,159,0.7)',
- width: 1
- })
- });
- // 遮罩图层的矢量数据源(初始为空)
- const maskSource = new VectorSource();
- // 创建一个多边形特征
- const polygonFeature = new Feature({
- geometry: new Polygon(item.points)
- });
- const maskLayer = new VectorLayer({
- source: maskSource,
- style: maskStyle,
- properties: {
- name: 'mask'
- }
- });
- this.map.addLayer(maskLayer);
- // 将多边形特征添加到遮罩数据源中
- maskSource.addFeature(polygonFeature);
- });
- }
- removeMask() {
- //移除图层
- const layersArray = this.map.getLayers().getArray();
- layersArray.forEach((layer) => {
- // 检查图层是否有自定义属性,并且该属性是否匹配你要移除的图层的标识符
- if (layer.get('name') === 'mask') {
- this.map.removeLayer(layer);
- }
- });
- }
- removeMask2() {
- if (this.maskLayer) {
- this.map.removeLayer(this.maskLayer);
- this.maskLayer = null;
- }
- }
- removeMask3(isHide) {
- if (this.maskLayer) {
- this.map.removeLayer(this.maskLayer);
- if (!isHide) {
- this.maskLayer = [];
- }
- }
- if (this.maskLayer2) {
- this.map.removeLayer(this.maskLayer2);
- if (!isHide) {
- this.maskLayer2 = [];
- }
- }
- }
- /**
- * 绘制经纬线
- * visible: boolean 是否可见
- */
- handleLngLatLine(visible: boolean) {
- // 创建经纬网图层
- const graticule = new Graticule({
- name: 'Graticule',
- showLabels: true, // 为每条刻度线绘制一个带有各自纬度/经度的标签
- wrapX: false, // 是否水平重复经纬网
- targetSize: 230,
- zIndex: 99999,
- strokeStyle: new Stroke({
- // 用于绘制刻度线的样式
- color: '#dcdcdc', // 线条颜色
- width: 1 // 线条宽度
- }),
- lonLabelStyle: new Text({
- font: '12px Calibri,sans-serif',
- textBaseline: 'bottom',
- fill: new Fill({
- color: '#9d9d9d'
- })
- }),
- latLabelPosition: 0,
- latLabelStyle: new Text({
- font: '12px Calibri,sans-serif',
- textAlign: 'left',
- textBaseline: 'end',
- fill: new Fill({
- color: '#9d9d9d'
- })
- })
- });
- if (visible) {
- this.map.addLayer(graticule);
- } else {
- this.removeLayer('Graticule');
- }
- }
- /**
- * 移除指定name的layer
- * layerName: string
- * */
- removeLayer(layerName: string) {
- const layers = this.map.getLayers();
- layers.forEach((element) => {
- if (!!element && element.get('name') === layerName) {
- //移除
- this.map.removeLayer(element);
- }
- });
- }
- // 创建图形
- createGraphics(data: any) {
- if (data.type === 'circle') {
- // 绘制圆形
- return this.createCircle(data);
- } else if (['polygon', 'rectangle'].includes(data.type)) {
- // 绘制矩形、多边形
- return this.createPolygon(data);
- }
- // else if (data.type === 'marker') {
- // // 绘制图标
- // return createMarker(data);
- // } else if (data.type === 'measureArea') {
- // // 绘制面积
- // return createMeasureArea(data);
- // } else if (data.type === 'text') {
- // // 文字
- // return addText(data);
- // } else if (data.type === 'straightLine') {
- // // 直线
- // return createStraightLine(data);
- // }
- }
- createCircle(data) {
- const circle = new Circle(data.center, data.radius);
- const feature = new Feature(circle);
- feature.setStyle(
- new Style({
- stroke: new Stroke({
- color: rgbToRgba(data.strokeColor, data.strokeOpacity),
- width: data.strokeWeight
- }),
- fill: new Fill({
- color: rgbToRgba(data.fillColor, data.fillOpacity)
- })
- })
- );
- this.drawVector.getSource().addFeature(feature);
- return feature;
- }
- createPolygon(data) {
- const polygon = new Polygon([data.path]);
- const feature = new Feature(polygon);
- feature.setStyle(
- new Style({
- stroke: new Stroke({
- color: rgbToRgba(data.strokeColor, data.strokeOpacity),
- width: data.strokeWeight
- }),
- fill: new Fill({
- color: rgbToRgba(data.fillColor, data.fillOpacity)
- })
- })
- );
- this.drawVector.getSource().addFeature(feature);
- return feature;
- }
- createLineString(data) {
- const lineString = new LineString(data.path);
- const feature = new Feature(lineString);
- feature.setStyle(
- new Style({
- stroke: new Stroke({
- color: rgbToRgba(data.strokeColor, data.strokeOpacity),
- width: data.strokeWeight
- }),
- fill: new Fill({
- color: rgbToRgba(data.fillColor, data.fillOpacity)
- })
- })
- );
- this.drawVector.getSource().addFeature(feature);
- return feature;
- }
- trackPlayback(lineArr) {
- if (!!this.carFeature) {
- this.carLayer.getSource().removeFeature(this.carFeature);
- }
- if (!!this.traceFeature) {
- this.carLayer.getSource().removeFeature(this.traceFeature);
- }
- const getAngle = (point1, point2) => {
- let arc = 0;
- if (point2 && point2.length && point1 && point1.length) {
- if ((point2[0] - point1[0] >= 0 && point2[1] - point1[1] >= 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] > 0)) {
- arc = Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1]));
- } else if ((point2[0] - point1[0] > 0 && point2[1] - point1[1] < 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] < 0)) {
- arc = Math.PI + Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1]));
- }
- }
- return arc;
- };
- let lastTime = Date.now();
- const source = new VectorSource();
- let distance = 0;
- const angle = getAngle(lineArr[0], lineArr[1]);
- const speed = 500;
- let animationFlag = false;
- this.carFeature = new Feature({
- geometry: new Point(lineArr[0])
- });
- const icon = new Icon({
- crossOrigin: 'anonymous',
- src: carImg,
- width: 13,
- height: 26,
- anchor: [0.5, 0.5],
- rotation: angle
- });
- this.carFeature.setStyle(
- new Style({
- image: icon
- })
- );
- this.traceFeature = new Feature({
- geometry: new LineString(lineArr)
- });
- let route = new LineString(lineArr);
- this.carLayer = new VectorLayer({
- source: source,
- style: new Style({
- stroke: new Stroke({
- color: '#AF5',
- width: 5
- })
- })
- });
- this.carLayer.getSource().addFeatures([this.carFeature, this.traceFeature]);
- const move = (e) => {
- const time = e.frameState.time;
- // 时间戳差(毫秒)
- const elapsedTime = time - lastTime;
- // 距离(其实是比例的概念)
- distance = distance + (speed * elapsedTime) / 1e6;
- if (distance >= 1) {
- distance = 0;
- animationFlag = false;
- stopAnimation();
- return;
- }
- // 保存当前时间
- lastTime = time;
- // 上次坐标
- const lastCoord = this.carFeature.getGeometry().getCoordinates();
- // 获取新位置的坐标点
- const curCoord = route.getCoordinateAt(distance);
- // 设置新坐标
- this.carFeature.getGeometry().setCoordinates(curCoord);
- this.map.getView().setCenter(curCoord);
- // 设置角度
- this.carFeature.getStyle().getImage().setRotation(getAngle(lastCoord, curCoord));
- // 调用地图渲染
- this.map.render();
- };
- const stopAnimation = () => {
- this.carLayer.un('postrender', move);
- };
- this.map.addLayer(this.carLayer);
- this.carLayer.on('postrender', move);
- // 触发地图渲染
- const geo = this.carFeature.getGeometry().clone();
- this.carFeature.setGeometry(geo);
- return [this.carLayer];
- }
- drawData(data) {
- const res = [];
- data.forEach((item) => {
- let graphic;
- if (['rectangle', 'polygon', 'anyLine'].includes(item.type)) {
- graphic = this.createPolygon(item);
- graphic.set('id', item.id);
- res.push(graphic);
- } else if (item.type === 'circle') {
- graphic = this.createCircle(item);
- graphic.set('id', item.id);
- res.push(graphic);
- } else if (item.type === 'straightLine') {
- graphic = this.createLineString(item);
- graphic.set('id', item.id);
- res.push(graphic);
- } else if (item.type === 'text') {
- const { text } = this.addText(item);
- res.push(text);
- } else if (item.type === 'measureArea') {
- graphic = this.createPolygon(item);
- graphic.set('id', item.id);
- const style = new Style({
- stroke: new Stroke({
- color: rgbToRgba(item.strokeColor, item.strokeOpacity),
- width: item.strokeWeight
- }),
- fill: new Fill({
- color: rgbToRgba(item.fillColor, item.fillOpacity)
- }),
- text: new Text({
- text: '区域面积' + item.area.toFixed(2) + '平方米',
- font: '14px Calibri,sans-serif',
- fill: new Fill({ color: '#000' }),
- stroke: new Stroke({
- color: '#fff',
- width: 3
- }),
- overflow: true
- })
- });
- graphic.setStyle(style);
- res.push(graphic);
- } else if (item.type === 'marker') {
- // 创建标注点
- const marker = new Feature({
- // 必须是数字类型,字符串不识别
- geometry: new Point([item.longitude, item.latitude]),
- // name: item.name,
- // icon: item.icon,
- // imageHover: item.imageHover,
- // size: item.size,
- pointer: true
- });
- marker.set('id', item.id);
- const img = new Image();
- img.onload = () => {
- // 图片加载完成后,可以访问其 width 和 height 属性
- const width = img.width;
- const height = img.height;
- const style = new Style({
- image: new Icon({
- anchor: [0.5, 1],
- src: item.icon,
- size: [width, height],
- scale: !!item.size[0] ? item.size[0] / width : 1
- }),
- text: new Text({
- text: item.name,
- fill: new Fill({
- color: '#000'
- }),
- stroke: new Stroke({
- color: '#fff',
- width: 3
- })
- })
- });
- marker.setStyle(style);
- };
- img.src = item.icon; // 设置图片的 URL,触发加载
- this.drawVector.getSource().addFeature(marker);
- res.push(marker);
- }
- });
- return res;
- }
- getVectorLayer() {
- return this.vectorLayer;
- }
- getDrawVector() {
- return this.drawVector;
- }
- getMap() {
- return this.map;
- }
- getMouseTool() {
- return this.drawTool;
- }
- setDrawEndMethod(newMethod) {
- this.drawEndMethod = newMethod;
- }
- }
|