分类 学习 下的文章 - 浮川的小窝
首页
休闲直播
4K壁纸
统计
更多
关于
留言
归档
搜 索
1
emby+alist3+cloudDriver2 emby无法播放网盘资源问题
164 阅读
2
HelloWorld!
154 阅读
3
前端算法整理
146 阅读
4
关于服务器配置反代隐藏端口中遇到的问题
141 阅读
5
maptalks 一些基础api的再封装(待解耦改装纯方法类)
121 阅读
生活
学习
随笔
吐槽
小秘密
小故事
登录
/
注册
搜 索
SnowLove
累计撰写
69
篇文章
累计收到
5
条评论
首页
栏目
生活
学习
随笔
吐槽
小秘密
小故事
页面
休闲直播
4K壁纸
统计
关于
留言
归档
用户登录
登录
注册
找到
45
篇与
学习
相关的结果
2023-03-12
node小项目练手-模拟考试系统
[line]此为分割线[/line]最近在粗浅的研究用node做后台 起一个简单的h5项目 就当练手加简单的实践项目部署的相关知识 本人小白 大神互喷思路就是简单的考试系统 有一个登录页用于考员身份验证(此处借用了公司redis库中的身份信息 身份证输入框进行校验 不符合无法进行下一步)[typing]操作如下机效果[/typing] 难点其实没有特别难的 也并未涉及node中对文件的增删改查其实一些简单的小项目node完全可以胜任 后续还会更新我独立开发的一个日志系统(有可能胎死腹中= =)这将是对node的更加深入的使用~项目地址:[hide]https://gitee.com/MMsnowLove/test-collect/tree/兼容vite/项目收集/exam-h5[/hide]
2023年03月12日
41
0
0
2023-03-02
使用Worker子线程不阻塞处理数据
话接上文 顺嘴一提 因为业务场景中要使用到计算点位的重复数据(最low版 理想情况假设下GPS推送过来的点位 如果不动是完全一样的 但是实际场景中GPS即使不动 点位也会"飘" 不可能前后点位完全相同 所以这算是low逼版version0.1吧 后续点位推送过来在进行代码优化)因为点位数据大 考虑到不阻塞主流程显示 采用了子线程异步加载 不耽误事 上代码~ // 计算停留点点位 子线程计算 const worker = new Worker(); // 主线程给 worker 子线程发送数据 ElMessage({ message: "正在获取停留点终数据,请稍候", grouping: true, type: "warning", customClass: "command-message", }); worker.postMessage(); // 打印其他点位 // 主线程注册子线程的 postmessage 数据回调 worker.onmessage = (: MessageEvent) => { try { // console.log("data.trakCollection",data) HBMap.drawOtherHistoryMarkers(data.trakCollection); } catch (err) { console.log(err); } setTimeout(() => { ElMessage({ message: "获取停留点点位成功!", grouping: true, type: "success", customClass: "command-message", }); }, 2000); // 主线程关闭 worker 子线程 worker.terminate(); }; // worker.js hook代码 import type from "@/map"; import from "element-plus"; self.onmessage = async () => { const trakCollection = await handleCoordinateArr(data.trakCollection); self.postMessage(); }; self.onerror = () => { // worker 线程发生了错误! ElMessage({ message: "获取停留点终数据失败!", grouping: true, type: "error", customClass: "command-message", }); }; function handleCoordinateArr(arr: Coordinate[] = []): Promise<Coordinate[]> { const [start, ...args] = arr, // eslint-disable-next-line @typescript-eslint/no-unused-vars [end, ...argss] = [...args].reverse(), pauseSet = new Set(); return new Promise((reslove) => { arr.reduce( (prv: Coordinate[], cur: Coordinate) => { if (prv.some((t) => t.toString() === cur.toString())) { pauseSet.add(JSON.stringify(cur)); prv.push(cur); } return prv; }, [arr[0]] ); reslove([ start, ...Array.from([...pauseSet]).map((pause) => JSON.parse(pause as string)), end, ] as Coordinate[]); }); } //
2023年03月02日
31
0
0
2023-02-17
maptalks.routeplayer.js 魔改版
原项目地址https://github.com/maptalks/maptalks.routeplayer/魔改后效果因为项目需求这个3年前就停止更新的插件已经没法满足饥渴难耐的产品了(旺财),所以在此基础上做了一些修改 将点位和时间关联在了一起 加上进度条可以进行点位的前进和后退 类似一个视频动画的回放 不说了上代码 import * as maptalks from "maptalks"; const options = { unitTime: 1 * 1000, showRoutes: true, showTrail: true, maxTrailLine: 0, markerSymbol: null, lineSymbol: { lineWidth: 2, lineColor: "#004A8D", }, trailLineSymbol: { lineColor: "rgba(250,0,0,1)", lineWidth: 4, lineJoin: "round", //miter, round, bevel lineCap: "round", //butt, round, square lineDasharray: null, //dasharray, e.g. [10, 5, 5] "lineOpacity ": 1, }, }; export class Route { constructor(r) { this.route = r; this.path = r.path; } // 计算方位角 getAzimuth(prvCoordinate, curCoordinate) { let azimuth = 0; const [lat1, lon1] = prvCoordinate, [lat2, lon2] = curCoordinate, dlat = lat2 - lat1, dlon = lon2 - lon1; if (dlon === 0) { azimuth = dlat > 0 ? 0 : -90; } else if (dlat === 0) { azimuth = dlon > 0 ? 90 : 270; } else { azimuth = (Math.atan(dlat / dlon) * 180) / Math.PI; if (dlon < 0) { azimuth += 180; } else if (dlon >= 0 && dlat < 0) { azimuth += 360; } } return azimuth; } // _total_distance getCoordinates(distance, map) { if (distance < this.getStart() || distance > this.getEnd()) { return null; } var idx = null; let payload = null, currentCoordinate = null; for (let i = 0, l = this.path.length; i < l; i++) { if (distance < this.path[i][2]) { idx = i; payload = this.path[i][2]; currentCoordinate = this.path[i - 1]; // console.log("this.path", this.path[i]); break; } if (distance == this.path[i][2]) { payload = this.path[i][2]; currentCoordinate = this.path[i - 1]; // console.log("this.path2", this.path[i]); } } if (idx === null) { idx = this.path.length - 1; } const p1 = this.path[idx - 1], p2 = this.path[idx], span = distance - p1[2], r = span / (p2[2] - p1[2]); const x = p1[0] + (p2[0] - p1[0]) * r, y = p1[1] + (p2[1] - p1[1]) * r, coord = new maptalks.Coordinate(x, y), vp = map.coordinateToViewPoint(coord); const degree = this.getAzimuth([p1[0], p1[1]], [x, y]); // maptalks.Util.computeDegree( // map.coordinateToViewPoint(new maptalks.Coordinate(p1)), // vp // ); // console.log("coord", coord); return { coordinate: coord, // now: p currentCoordinate, // 当前数组中的点位对象 viewPoint: vp, degree: degree, index: idx, payload, }; } getStart() { return this.path[0][2]; } getEnd() { return this.path[this.getCount() - 1][2]; } getStartTimeStamp() { return this.path[0][3]; } getEndTimeStamp() { return this.path[this.getCount() - 1][3]; } getCount() { return this.path.length; } get markerSymbol() { return this.route.markerSymbol; } set markerSymbol(symbol) { this.route.markerSymbol = symbol; if (this._painter && this._painter.marker) { this._painter.marker.setSymbol(symbol); } } get lineSymbol() { return this.route.lineSymbol; } set lineSymbol(symbol) { this.route.lineSymbol = symbol; if (this._painter && this._painter.marker) { this._painter.line.setSymbol(symbol); } } get trailLineSymbol() { return this.route.trailLineSymbol; } set trailLineSymbol(symbol) { this.route.trailLineSymbol = symbol; if (this._painter && this._painter.marker) { this._painter.trailLine.setSymbol(symbol); } } } export class RoutePlayer extends maptalks.Eventable(maptalks.Class) { constructor(routes, map, timeCost, opts) { super(opts); if (!Array.isArray(routes)) { routes = [routes]; } this.id = maptalks.Util.UID(); this._map = map; this._routers = []; // 记录总用时时间缓存 方便倍速计算取用 this.timeCost_temp = routes[0].timeCost; // 记录总用时时间 this._timeCost = routes[0].timeCost; // 倍速 this._rat = 1; // 已经跑完的路程 this.played = 0; // 总路程 this._total_distance = 0; /** * 操作是否为点击倍速 当选中倍速是需要重绘player 需要将旧player进行 finish终止动画 * 后重新创建新的 避免内存消耗 会出发动画回调_step方法 将无法判断是重绘引发的动画播放完成 * 还是动画真正播放完成 */ this._ratMode = false; // 是否为拖动进度条操作 this._barMode = false; // 动画播放完成的回调 this.finishCallback = routes[0].finishCallback || null; // path点位变化回调 this.pathCoordinateChange = routes[0].pathCoordinateChange || null; // 是否获取物理地址 this._address = routes[0].address || false; // 记录上一次获取的物理地址值 this._addressTemp = null; // 记录上一次调用方法时间戳 this._prvTimestamp = 0; // 记录当前累加时间出 this._accumulateTimestamp = 0; // 降低地理位置请求频率 this._timer = null; // 降低当前时间请求频率 this._timer_time = null; // 地理位置延时时间 this._timerDelay = routes[0].timerDelay || 1 * 1000; this._self = this; this._setup(routes); } remove() { if (!this.markerLayer) { return this; } this.finish(); this.markerLayer.remove(); this.lineLayer.remove(); this.trailLineLayer.remove(); delete this.markerLayer; delete this.lineLayer; delete this.trailLineLayer; delete this._map; return this; } play(delay, callback) { if (this.player.playState === "running") { return this; } this.player.play(); this.fire("playstart"); if (delay) { setTimeout(() => { this.player.pause(); setTimeout(callback, delay); }, 1); } return this; } pause() { if (this.player.playState === "paused") { return this; } this.player.pause(); this.fire("playpause"); return this; } cancel() { this.player.cancel(); this.played = 0; this._timeCost = this.timeCost_temp; this.trailLinePoints = []; let line = this.trailLineLayer.getGeometries()[0]; if (line !== undefined) line.setCoordinates(this.trailLinePoints); setTimeout(() => { this._createPlayer(); this._step( }); this.fire("playcancel"); // console.log("cancel"); }, 10); return this; } finish() { if (this.player.playState === "finished") { return this; } // complete trail line let line = this.trailLineLayer.getGeometries()[0]; let coors = this.routes[0].path.map((item) => { return [item[0], item[1]]; }); this.trailLinePoints = coors; line.setCoordinates(this.trailLinePoints); this._timeCost = this.timeCost_temp; this.player.finish(); this._step( }); this.fire("playfinish"); return this; } getStartTime() { return this.startTime || 0; } getEndTime() { return this.endTime || 0; } setTime(t) { this.played = t - this.startTime; if (this.played < 0) { this.played = 0; } this._resetPlayer(); return this; } getUnitTime() { return this.options["unitTime"]; } setUnitTime(ut) { this.options["unitTime"] = +ut; this._resetPlayer(); } getCurrentProperties(index) { if (!index) { index = 0; } if (!this.routes[index] || !this.routes[index]._painter) { return null; } return this.routes[index]._painter.marker.getProperties(); } getCurrentCoordinates(index) { if (!index) { index = 0; } if (!this.routes[index] || !this.routes[index]._painter) { return null; } return this.routes[index]._painter.marker.getCoordinates(); } getMarkerSymbol(idx) { if (this.routes && this.routes[idx]) { return this.routes[idx].markerSymbol; } return null; } setMarkerSymbol(idx, symbol) { if (this.routes && this.routes[idx]) { this.routes[idx].markerSymbol = symbol; } return this; } getLineSymbol(idx) { if (this.routes && this.routes[idx]) { return this.routes[idx].lineSymbol; } return null; } setLineSymbol(idx, symbol) { if (this.routes && this.routes[idx]) { this.routes[idx].lineSymbol = symbol; } return this; } showRoute() { this.lineLayer.show(); } showTrail() { this.trailLineLayer.show(); } hideRoute() { this.lineLayer.hide(); } hideTrail() { this.trailLineLayer.hide(); } _resetPlayer(play) { const playing = this.player && this.player.playState === "running"; if (playing) { this.player.finish(); this.trailLinePoints = []; let line = this.trailLineLayer.getGeometries()[0]; if (line !== undefined) line.setCoordinates(this.trailLinePoints); } this._createPlayer(); if (playing && play) { this.player.play(); } } /** 倍速 */ setRat(rat) { // 将2x 4x 等转换为数字 rat = parseFloat(rat); this._rat = rat; this._timeCost = this.timeCost_temp; this._ratMode = true; // 重新计算倍速用时 if (rat !== 1) this._timeCost = this._timeCost * (1 / rat); this._resetPlayer(false); } /** 拖动进度条更新点位和轨迹 */ _updateMarkerAndTrailLineString(schedule) { const arr = this.routes[0].path; this._barMode = true; this.trailLinePoints = []; this.pause(); this.played = this._total_distance * schedule; /** * 注: * 1.点位每两秒1个(不是精准2秒) * 2.timeCost_temp为点位真实时长的总和 * 3.地图时间跟点位真实时间不是一个概念 * 4.maptalks.animation.Animation.animate duration用时要跟进度条时间一致 */ //查找传入点位集合 点位每两秒推送一次 故进度条step为2 所以接受的index要除2拿到对应点位的数组下标 for (let i = 0; i < arr.length; i++) { // console.log("_updateMarkerAndTrailLineString", arr[i]); if (this.played <= arr[i][2]) { // return console.log( // "_updateMarkerAndTrailLineString", // this.played, // arr, // arr[i] // ); let coordinates = this.routes[0].getCoordinates(this.played, this._map); // 总时间减当前选中进度条点位的下标计算剩余时间 this._timeCost = (this.timeCost_temp - this.timeCost_temp * schedule) * (1 / this._rat); this._accumulateTimestamp = coordinates.currentCoordinate.slice(-1)[0]; // 点位marker定位 this._step( }); // 创建新的player动画 [this.played / this.duration, 1] 代表动画点位的起点 // this.played / this.duration 为小于零的百分比 return this._createPlayer(this._timeCost, [schedule, 1]); } } } _createPlayer(timeCost, t = [0, 1]) { this.player = null; let duration = this._timeCost; // 如果有timeCost 代表是进度条变更 if (timeCost) duration = timeCost; // 没有则进行倍速计算 防止进度条与动画不同步 else { duration = this.timeCost_temp * (1 / this._rat); } this.player = maptalks.animation.Animation.animate( { t, }, { duration, easing: "easing", }, this._step.bind(this) ); } _step(frame) { if (frame && frame.state && frame.state.playState !== "running") { if (frame.state.playState === "finished" && !this._barMode) { if (this.finishCallback) { this.finishCallback(); } this.cancel(); if (this._ratMode) { return (this._ratMode = false); } } return; } if (this._barMode) { for (let i = 0, l = this.routes.length; i < l; i++) { this._drawRoute(this.routes[i], this.played, frame.styles.t); this._barMode = false; } } else { this.played = this._total_distance * frame.styles.t; for (let i = 0, l = this.routes.length; i < l; i++) { this._drawRoute(this.routes[i], this.played, frame.styles.t); this.fire("playing"); } } } _http(long, lat) { const httpRequest = new XMLHttpRequest(); httpRequest.open( "GET", `https://restapi.amap.com/v3/geocode/regeo?key=a5432ed29b40ef351524ea736aa7aca5&location=$,$`, true ); //第二步:打开连接 将请求参数写在url中 ps:"http://localhost:8080/rest/xxx" httpRequest.send(); //第三步:发送请求 将请求参数写在URL中 /** * 获取数据后的处理程序 */ return new Promise((reslove) => { httpRequest.onreadystatechange = function () { if (httpRequest.readyState == 4 && httpRequest.status == 200) { var json = httpRequest.responseText; //获取到json字符串,还需解析 console.log(JSON.parse(json)); const = } = JSON.parse(json); if (status === "1") { reslove(formatted_address); } } }; }); } async _addressFun(long, lat) { if (this._timer) { clearTimeout(this._timer); this._timer = null; } this._addressTemp = await this._http(long, lat); } /** 延迟获取逆向物理地址 */ _throttle(coordinates) { const [long, lat] = coordinates.currentCoordinate.slice(0, 2); this._timer = setTimeout(async () => { this._addressFun(long, lat); }, this._timerDelay); } _throttle_time(func, delay) { if (!this._timer_time) { this._timer_time = setTimeout(async () => { func(); clearTimeout(this._timer_time); this._timer_time = null; }, delay); } } _drawRoute(route, distance, t) { if (!this._map) return; let coordinates = route.getCoordinates(distance, this._map), timestamp = new Date().getTime(), duration = 0; // 计算_drawRoute回调方法调用时间段 if (!this._prvTimestamp) { this._prvTimestamp = timestamp; this._nowTimestamp = timestamp; this._accumulateTimestamp = coordinates.currentCoordinate.slice(-1)[0]; } else this._nowTimestamp = timestamp; duration = this._nowTimestamp - this._prvTimestamp; this._prvTimestamp = timestamp; if (!coordinates) { if (route._painter && route._painter.marker) { route._painter.marker.remove(); delete route._painter.marker; } return; } if (!route._painter) { route._painter = ; } if (!route._painter.marker) { const marker = new maptalks.Marker(coordinates.coordinate, { zIndex: 10, symbol: Object.assign( route.markerSymbol || this.options["markerSymbol"], { markerRotation: -coordinates.degree, } ), }).addTo(this.markerLayer); route._painter.marker = marker; } else { // route._painter.marker.setProperties(coordinates.payload); route._painter.marker.setCoordinates(coordinates.coordinate); route._painter.marker.updateSymbol({ markerRotation: -coordinates.degree, }); if (this.pathCoordinateChange) { if (this._address) { if (!this._timer) { this._addressFun( coordinates.coordinate.x, coordinates.coordinate.y ); this._throttle(coordinates); } // coordinates.currentCoordinate [long, lat,distance,timestamp] // [long, lat,distance,timestamp,schedule,timeCost,address] this.pathCoordinateChange([ coordinates.coordinate.x, coordinates.coordinate.y, this._total_distance, (this._accumulateTimestamp += duration), t, this.timeCost_temp, this._addressTemp, ]); } else { this.pathCoordinateChange([ coordinates.coordinate.x, coordinates.coordinate.y, this._total_distance, (this._accumulateTimestamp += duration), t, this.timeCost_temp, "", ]); } } } if (!route._painter.line) { const line = new maptalks.LineString(route.path, { symbol: route.lineSymbol || this.options["lineSymbol"], zIndex: 5, }).addTo(this.lineLayer); route._painter.line = line; } if (!route._painter.trailLine) { this.trailLinePoints = [coordinates.coordinate]; const trailLine = new maptalks.LineString([], { symbol: route.trailLineSymbol || this.options["trailLineSymbol"], zIndex: 5, }).addTo(this.trailLineLayer); route._painter.trailLine = trailLine; } else { // remove extra trail point by maxTrailLine, 0 => disable const maxLineCount = this.options["maxTrailLine"]; if (maxLineCount !== 0 && this.trailLinePoints.length > maxLineCount) { this.trailLinePoints.shift(); } this.trailLinePoints.push(coordinates.coordinate); if (this.trailLinePoints.length > 1) { route._painter.trailLine.setCoordinates(this.trailLinePoints); } } } _setup(rs) { const routes = rs.map((r) => new Route(r)); this._routers = routes[0]; var start = routes[0].getStart(), end = routes[0].getEnd(); console.log("this._routers", this._routers); for (let i = 1; i < routes.length; i++) { let route = routes[i]; if (route.getStart() < start) { start = route.getStart(); } if (route.getEnd() > end) { end = route.getEnd(); } } this.trailLinePoints = []; this.routes = routes; this.startTime = start; this.endTime = end; this.played = 0; this._total_distance = end - start; this._createLayers(); this._createPlayer(); } _createLayers() { this.lineLayer = new maptalks.VectorLayer( maptalks.INTERNAL_LAYER_PREFIX + "_routeplay_r_" + this.id, [], ).addTo(this._map); this.trailLineLayer = new maptalks.VectorLayer( maptalks.INTERNAL_LAYER_PREFIX + "_routeplay_t_" + this.id, [], ).addTo(this._map); this.markerLayer = new maptalks.VectorLayer( maptalks.INTERNAL_LAYER_PREFIX + "_routeplay_m_" + this.id, [], { zIndex: 6, } ).addTo(this._map); } _clearLayers() { this.lineLayer.clear(); this.trailLineLayer.clear(); this.markerLayer.clear(); this.played = 0; this.player.finish(); this.trailLinePoints = []; let line = this.trailLineLayer.getGeometries()[0]; if (line !== undefined) line.setCoordinates(this.trailLinePoints); } } RoutePlayer.mergeOptions(options); /** 使用方式 */ animatePath( timeCost, path, () => { setPlayerStatus("stop"); setHistoryCarInfo(null); }, // path点位变化 (coordinate) => pathCoordinateChange(coordinate), carCode, carTypeId, trakCollection, uploadTime, HBMap ); /** 轨迹动画点位path */ function animatePath( timeCost: number, path: number[], finishCallback: () => void, pathCoordinateChange: ( coordinate: [number, number, number, number, number, number, string] ) => void, carCode: string, carTypeId: number, trakCollection: Coordinate[], uploadTime: number[], HBMap: HBMap ) { HBMap.addNewCarMapObject({ carCode, carGeo: trakCollection[0], workStatus: "", follow: false, trakCollection, uploadTime, line: null, carMarker: null, azimuth: [], } as unknown as CarMarkerData); return [ { // 动画执行完的回调 finishCallback: () => { console.log("finishCallback"); finishCallback(); }, // path点位变化 pathCoordinateChange, // 是否获取物理地址 address: false, // 获取地址延迟 timerDelay: path.length * 2, // (path.length / 2) * 10, // 总用时 毫秒 timeCost, // 路线 path, //marker's symbol markerSymbol: { markerFile: utils.getCarStatusSvg(Number(carTypeId), "ONLINE"), markerHeight: 60, markerWidth: 60, markerOpacity: 1, markerVerticalAlignment: "middle", // markerRotation: azimuth.concat().pop(), }, //route line's symbol lineSymbol: , //route line's trail symbol maxTrailLine: 2, trailLineSymbol: { lineColor: "#1d51d2", lineWidth: 10, lineJoin: "round", //miter, round, bevel lineCap: "round", //butt, round, square lineDasharray: null, //dasharray, e.g. [10, 5, 5] "lineOpacity ": 1, }, }, ]; } // 重绘动画点位 进度条拖动 @params:param 进度条当前位置 百分比或1之内的小数 player.value._updateMarkerAndTrailLineString(param);
2023年02月17日
36
0
0
2022-12-14
maptalks 一些基础api的再封装(待解耦改装纯方法类)
import * as maptalks from "maptalks"; // import { // RoutePlayer, // getAllPointsArr, // } from "^/assets/js/maptalks.routeplayer.es.js"; import from "^/assets/js/maptalks.routeplayer.js"; import convertor from "chncrs"; import from "^/composables/infoWindow"; import type { WSCarCoordinate, WSCarStatus, CarMarkerData, Coordinate, LineColor, InfoWindowCallback, ChangeModeCallback, AnimateiHstoryCarPath, InfoWindowData, MarkerLocation, BaseParameterData, DispatchMarker, AreaData, } from "@/map"; import from "^/hook/infoWindow"; import utils from "^u/utils"; // 警车基础配置参数对象 import POLICE_CAR_CONFIG from "../../config/config"; import dispatchSvg from "^/assets/svg/icons/调度点-dispatch.svg"; import type from "element-plus"; const HBmap_base_config = ( cssFilter: string, spatialReference: any, tileSystem: any[] ) => ({ urlTemplate: POLICE_CAR_CONFIG.MAP_URL, // 替换url中的子域 ?不是很理解 subdomains: ["a", "b", "c", "d"], cssFilter, // 图片没出来 站位用的 placeholder: true, // 平铺图像的 crossOrigin 跨域 crossOrigin: "anonymous", // offset: offset as unknown as number[], //设置瓦片图层的空间参考spatialReference默认就是3857,googlemap的分辨率 spatialReference, //tileSystem 控制瓦片的x,y以及行列,后两个是origin原点位置(很重要) tileSystem, // 是否启用此地图上光标样式图层的命中检测,禁用它以提高性能。(鼠标检测) hitDetect: false, }); const GAODEmap_base_config = ( cssFilter: string, spatialReference: any, tileSystem: any[] ) => ({ // urlTemplate: // "http://wprd.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&z=&x=&y=&scl=1<ype=11", // // 替换url中的子域 ?不是很理解 // subdomains: ["01", "02", "03", "04"], urlTemplate: "http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x=&y=&z=", subdomains: ["0", "1", "2", "3"], cssFilter, // 图片没出来 站位用的 placeholder: true, // 平铺图像的 crossOrigin 跨域 crossOrigin: "anonymous", // offset: offset as unknown as number[], //设置瓦片图层的空间参考spatialReference默认就是3857,googlemap的分辨率 spatialReference, //tileSystem 控制瓦片的x,y以及行列,后两个是origin原点位置(很重要) tileSystem, // 是否启用此地图上光标样式图层的命中检测,禁用它以提高性能。(鼠标检测) hitDetect: false, }); // 高德卫星图 const GAODEmap_satellite_base_config = ( cssFilter: string, spatialReference: any, tileSystem: any[] ) => ({ // urlTemplate: // "http://wprd.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&z=&x=&y=&scl=1<ype=11", // // 替换url中的子域 ?不是很理解 // subdomains: ["01", "02", "03", "04"], urlTemplate: "https://webst01.is.autonavi.com/appmaptile?lang=zh_cn&style=6&x=&y=&z=", subdomains: ["0", "1", "2", "3"], cssFilter, // 图片没出来 站位用的 placeholder: true, // 平铺图像的 crossOrigin 跨域 crossOrigin: "anonymous", // offset: offset as unknown as number[], //设置瓦片图层的空间参考spatialReference默认就是3857,googlemap的分辨率 spatialReference, //tileSystem 控制瓦片的x,y以及行列,后两个是origin原点位置(很重要) tileSystem, // 是否启用此地图上光标样式图层的命中检测,禁用它以提高性能。(鼠标检测) hitDetect: false, }); interface Extentable { getExtent(): maptalks.Extent; } // 颜色转换 const colorToRGB = (color: string, opt: number) => { let color1, color2, color3; color = "" + color; if (typeof color !== "string") return; if (color.charAt(0) == "#") { color = color.substring(1); } if (color.length == 3) { color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2]; } if (/^[0-9a-fA-F]$/.test(color)) { color1 = parseInt(color.substr(0, 2), 16); color2 = parseInt(color.substr(2, 2), 16); color3 = parseInt(color.substr(4, 2), 16); return "rgba(" + color1 + "," + color2 + "," + color3 + "," + opt + ")"; } }, // 总体瓦片偏移,[dx, dy],对来自不同坐标系的瓦片源很有用,例如(wgs84 和 gcj02) // 底图不能重叠 这个是因为百度地图用的是bd09ll坐标系, // 天地图是cgcs2000坐标系,两个坐标系有偏转造成的。 offset = (z: number) => { if (!this.map) return; const center = this.map.getCenter(); const transformCenter = convertor.transform( center.toArray(), "gcj02", "wgs84" ); targetCoordinate.set(transformCenter[0], transformCenter[1]); return this.map .coordToPoint(center, z, POINT0) .sub(this.map.coordToPoint(targetCoordinate, z, POINT1)) .round() .toArray(); }; // 全局绑定HBMap实例对象 let self: HBMap; // 海博地图Base对象 export class HBMap { // map对象 map: maptalks.Map; // 地图基础瓦片图层 baseLayer: maptalks.TileLayer[] = []; // 点位图层 markerLayer: maptalks.VectorLayer; // 轨迹图层 trakLayer: maptalks.VectorLayer; // 调度图层 no dispatchLayer: maptalks.VectorLayer; // 区域图层 no areaLayer: maptalks.VectorLayer | null | undefined; // 网格图层 no gridLayer: maptalks.VectorLayer; // 车辆全量信息 carsArr: CarMarkerData[] = []; // 车辆全量信息Map集合 yes carsMap: Map<string, CarMarkerData> = new Map(); // 当前跟踪对象 yes followCarArr: string[] = []; // 当前点击车辆marker对象 yes currentCarMarker: CarMarkerData | null = null; // 全局所有车辆跟踪 no carFocus: boolean = false; // 选择点位模式 no coordinateLocationMode: boolean = false; // 当前点位信息 no DispatchMarkerMap: Map<string, MarkerLocation> = new Map(); // 弹窗点击事件回调 infoWindowCallback: InfoWindowCallback<CarMarkerData> | null = null; // 画点完毕回调 changeModeCallback: ChangeModeCallback | null = null; /** 初始点位 */ // eslint-disable-next-line no-loss-of-precision static InitialCoordinate: Coordinate = [120.38422855590826, 36.1354355861087]; // [120.377365, 36.066538]; /** 初始缩放等级 */ static InitialZoom = 12.3; /** 点击车辆 缩放比例 */ static InitialCarZoom = 15; /** 构造Coordinate */ static Coordinate = maptalks.Coordinate; /** 弹窗最多个数 */ static infoWindowNum = 5; constructor( renderer: HTMLElement, infoWindowCallback: InfoWindowCallback<CarMarkerData> | null, changeModeCallback: ChangeModeCallback | null ) { const center = HBMap.InitialCoordinate; self = this; /** * WGS84:World Geodetic System 1984,是为GPS全球定位系统使用而建立的坐标系统 * * EPSG:4326 (WGS84) WGS84 是目前最流行的地理坐标系统。 * 在国际上,每个坐标系统都会被分配一个 EPSG 代码, * EPSG:4326 就是 WGS84 的代码。GPS是基于WGS84的, * 所以通常我们得到的坐标数据都是WGS84的。 * 一般我们在存储数据时,仍然按WGS84存储 * * EPSG:3857 (Pseudo-Mercator) 伪墨卡托投影,也被称为球体墨卡托, * Web Mercator它是基于墨卡托投影的,把 WGS84坐标系投影到正方形 * */ //与map一样,支持更详细的设置resolutions,fullExtent等 const spatialReference = , // 精确位置 [1, 1, -20037508.3427890,-20037508.3427890] tileSystem = [1, -1, -20037508.34, 20037508.34], // 滤镜 grayscale(1%) sepia(10%) hue-rotate(26.3deg) invert(0%) contrast(131%) saturate(63%) cssFilter = "grayscale(30%) sepia(10%) brightness(80%)", // 添加瓦片纠偏 targetCoordinate = new maptalks.Coordinate(0, 0), POINT0 = new maptalks.Point(0, 0), POINT1 = new maptalks.Point(0, 0); // 基础瓦片图层 this.baseLayer[0] = new maptalks.TileLayer( "base", // HBmap_base_config(cssFilter, spatialReference, tileSystem) GAODEmap_base_config(cssFilter, spatialReference, tileSystem) // GAODEmap_satellite_base_config(cssFilter, spatialReference, tileSystem) ) as maptalks.TileLayer; // 基础瓦片卫星图层 this.baseLayer[1] = new maptalks.TileLayer( "base", // HBmap_base_config(cssFilter, spatialReference, tileSystem), // GAODEmap_base_config(cssFilter, spatialReference, tileSystem), GAODEmap_satellite_base_config(cssFilter, spatialReference, tileSystem) ); // 巡逻图层 this.markerLayer = new maptalks.VectorLayer("patrol", null, { hitDetect: false, zIndex: 10, // enableAltitude: true, // enable altitude // altitudeProperty: "altitude", }); // 历史动画轨迹图层 this.trakLayer = new maptalks.VectorLayer("trak", null, { hitDetect: false, zIndex: 9, }); // 调度图层 this.dispatchLayer = new maptalks.VectorLayer("dispatch", null, { hitDetect: false, zIndex: 8, }); // // 区域图层 // this.areaLayer = new maptalks.VectorLayer("area", null, { // hitDetect: false, // zIndex: 3, // }); // 网格图层 this.gridLayer = new maptalks.VectorLayer("grid", null, { hitDetect: false, zIndex: 2, }); // 初始化地图实例 this.map = new maptalks.Map(renderer, { // 是否在地图上显示属性控件。如果为真,属性显示 maptalks 信息; // 如果对象,您可以指定位置或您的基本内容,以及两者; attribution: false, // 地图的初始中心 center, // 缩放等级 zoom: HBMap.InitialZoom, minZoom: 11.5, maxZoom: 19, // 地图的空间参考,默认使用 EPSG:3857 投影,分辨率由 google map/osm 使用 spatialReference, // 鹰眼 // overviewControl: true, // 标尺 scaleControl: { position: "bottom-right", maxWidth: 100, metric: true, // metric 米 nautical 海里 imperial 英寸 imperial: false, }, // 是否允许地图通过双击事件缩放 doubleClickZoom: false, // 默认为真。如果为 true,则可以通过右键单击或 ctrl + 左键单击来拖动地 // 图行旋转 dragRotate: false, // 默认为真。如果为 true,则可以通过右键单击或 ctrl + 左键单击将地图拖动 // 到俯仰 dragPitch: false, // 如果为 true,地图将被拖动到同时俯仰和旋转 dragRotatePitch: false, // 基础瓦片层 baseLayer: this.baseLayer[0], // 最初将添加到地图的图层 layers: [ this.markerLayer, this.trakLayer, this.dispatchLayer, this.gridLayer, // this.areaLayer, ], }); // 默认为真。拖动或触摸结束时继续动画平移 this.map.config("panAnimation", false); // 默认为真。当拖动或触摸旋转结束时继续动画旋转 this.map.config("rotateAnimation", false); // 默认为真。是否启用此地图上光标样式的图层命中检测,禁用它以提高性能 this.map.config("hitDetect", false); // this.map.setPitch(30); // this.map.setBearing(5); // this.map.setFov(1); // 赋值弹窗回调函数 this.infoWindowCallback = infoWindowCallback; this.changeModeCallback = changeModeCallback; } /** 根据carCode获取当前点位坐标 */ getCoordinatesByCarCode(carCode: string) { const marker = this.carsMap.get(carCode); // console.log("getCoordinatesByCarCode", marker); if (marker) { return marker.carGeo; } return null; } /** 切换地图为卫星图 */ changeBaseLayer(type: string) { switch (type) { case "base": this.map.setBaseLayer(this.baseLayer[0]); break; case "satellite": this.map.setBaseLayer(this.baseLayer[1]); break; default: break; } } /** 选择点位坐标 */ setDispatchMarkerCoordinateMode( flag: boolean | undefined ): boolean | undefined { console.log("setDispatchMarkerCoordinateMode"); if (typeof flag !== "undefined") { this.coordinateLocationMode = flag; return undefined; } this.coordinateLocationMode = !this.coordinateLocationMode; if (this.coordinateLocationMode) { // 绑定地图右键点击事件 this.map.once("click", this.contextmenuEvent); console.log("contextmenu"); } else { this.map.off("click", this.contextmenuEvent); } return this.coordinateLocationMode; } /** 右击时间回调 */ contextmenuEvent(: any) { // console.log("setDispatchMarkerCoordinateMode", self.coordinateLocationMode); const dispatchId = utils.getUUID(), DispatchMarker = ; // 创建调度点位 if (self.coordinateLocationMode) { if (self.changeModeCallback) { // 判断是否为 undefined undefined代表主动回复不打点默认状态 if ( typeof self.changeModeCallback({ dispatchId, coordinate, }) !== "undefined" ) { self.setDispatchMarkerCoordinateLocation(DispatchMarker); } } } } /** 回显调度点位 */ setDispatchMarker( dispatchId: string, coordinate: Coordinate ): DispatchMarker { const DispatchMarker = ; this.setDispatchMarkerCoordinateLocation(DispatchMarker); return DispatchMarker; } /** 新建调度点位 */ setDispatchMarkerCoordinateLocation(dispatchData: MarkerLocation) { const dispatchMarker = self.DispatchMarkerMap.get(dispatchData.dispatchId); // console.log( // "setDispatchMarkerCoordinateLocation", // dispatchMarker, // dispatchData // ); if (!dispatchMarker) { // 新建点位 const DispatchMarker = new maptalks.Marker(dispatchData.coordinate, { id: dispatchData.dispatchId, zIndex: 15, symbol: [ { markerFile: dispatchSvg, markerHeight: 60, markerWidth: 60, markerOpacity: 1, markerVerticalAlignment: "middle", }, ], }), option = { content: `<div class="icon">终</div><div>经度:${ (dispatchData.coordinate && dispatchData.coordinate[0]) || dispatchData.coordinate?.x } 纬度:${ (dispatchData.coordinate && dispatchData.coordinate[1]) || dispatchData.coordinate?.y }</div>`, className: "maptalks-tooltip-address", dy: -65, }, // 创建自定义ui组件 UI = this.createCustomUIComponent(), toolTip = new UI(dispatchData.coordinate, option); UI.mergeOptions(option); toolTip.addTo(this.map).show(); DispatchMarker.addTo(this.dispatchLayer); self.DispatchMarkerMap.set(dispatchData.dispatchId, { ...dispatchData, toolTip, marker: DispatchMarker, }); } else { // console.log("dispatchMarker.marker", dispatchMarker.marker); dispatchMarker.marker?.remove(); dispatchMarker.toolTip?.remove(); self.DispatchMarkerMap.delete(dispatchData.dispatchId); } } /** 更新车辆标记并更新最新方位角 */ private updateDrawCar( { carCode, carTypeId, carMarker, infoWindowData, existCamera, existCarGeo, infoWindowVisiable, }: CarMarkerData, data: WSCarCoordinate | WSCarStatus, azimuth: number ): maptalks.Marker { // 更改点位 if (carMarker) { if ("carGeo" in data) { data.carGeo && carMarker.setCoordinates(data.carGeo); } } // 判断是否要重绘消息弹窗 if (typeof infoWindowVisiable === "boolean" && infoWindowVisiable) { // 绘制消息弹窗 createInfoWindow({ carCode, pointInfo: { ...infoWindowData, coordinate: (data as WSCarCoordinate).carGeo, existCamera, existCarGeo, }, eventFunction: (type, carCode) => { if (this.infoWindowCallback) { const marker = this.carsMap.get(carCode as string); if (marker) { this.infoWindowCallback(type, marker); } } }, }); } // 更改点位信息 // console.log( // "markerFile", // workStatus, // utils.getCarStatusSvg(carTypeId, workStatus) // ); const updateConfig: any = [ { markerRotation: -azimuth, }, ]; if ("workStatus" in data) { updateConfig[0].markerFile = utils.getCarStatusSvg( carTypeId, data.workStatus as string ); // console.log( // 222, // data.workStatus, // utils.getCarStatusSvg(carTypeId, data.workStatus) // ); } carMarker!.updateSymbol(updateConfig); return carMarker!; } /** 批量更新车辆标记 */ bathUpdateDrawCar(data: WSCarCoordinate | WSCarStatus) { // 存储全量车辆信息 间隔相同时间 请求全量数据 if (Array.isArray(data)) { // 使用map更新单个点位 } else if (data !== null && Object.keys(data).length) { const marker = this.carsMap.get(data.carCode); if (marker) { // 最新方位角(我也不知道为啥是取负 正数会显示镜像的方位) let newAzimuth: number = 0; // 包含点位 计算方位角 if ("carGeo" in data) { newAzimuth = this.getAzimuth( marker.carGeo, // 旧点位 data.carGeo as Coordinate // 新点位 ); // 状态变更 隐藏调度弹窗 } else if ("workStatus" in data) { // 无坐标 取坐标角数组最后一位 } else newAzimuth = marker.azimuth && marker.azimuth.slice(-1)[0]; // console.log("azimuth Arr", data); this.carsMap.set( data.carCode, Object.assign(marker, { ...data, carMarker: this.updateDrawCar(marker, data, newAzimuth), azimuth: [...marker.azimuth, newAzimuth], }) ); } } } /** 创建新的ui组件 调度 */ createCustomUIComponent() { class MyUI extends maptalks.ui.UIComponent { constructor(coordinate: Coordinate, options: BaseParameterData) { super(options); this._coordinate = new maptalks.Coordinate(coordinate); } // 创建dom元素 buildOn() { const dom = document.createElement("div"); dom.className = this.options.className; dom.innerHTML = this.options["content"]; return dom; } getOffset() { const size = this.getSize(); return new maptalks.Point(-size.width / 2, -size.height / 2); } getEvents() { return { zoomend: this._flash, }; } onRemove() { if (this._flashTimeout) { clearTimeout(this._flashTimeout); } } _flash() { this.hide(); this._flashTimeout = setTimeout(() => { this.show(this._coordinate); }, 200) as unknown as number; } } return MyUI; } /** 批量更新车辆状态 */ bathUpdateMarkerStatus(data: WSCarStatus | WSCarStatus) { if ("workStatus" in data) { const = data; const marker = this.carsMap.get(carCode); if (marker) { // eslint-disable-next-line prefer-const let = marker; switch (workStatus) { case "DISPATCH": // console.log("DISPATCH 123"); // eslint-disable-next-line no-case-declarations const option = { content: `<div>$ 调度中</div>`, className: "maptalks-tooltip", dy: 45, }, // 创建自定义ui组件 UI = this.createCustomUIComponent(), ui = new UI(carGeo, option); UI.mergeOptions(option); ui.addTo(this.map).show(); carMarker!.closeInfoWindow(); toolTip = ui; showTip = true; break; default: if (toolTip) { toolTip.hide(); toolTip = null; showTip = false; } break; } this.carsMap.set( carCode, Object.assign(marker, { carMarker: this.updateDrawCar( marker, data, marker.azimuth && marker.azimuth.slice(-1)[0] ), toolTip, showTip, }) ); } } else if ("status" in data) { const = data; const marker = this.carsMap.get(carCode); console.log("ENABLED DISABLED", marker); if (marker) { if (status == "ENABLED") { // 关闭或展示 marker.carMarker?.show(); // 显示轨迹 marker.line?.show(); this.carsMap.set( carCode, Object.assign(marker, { show: true, enabled: true, }) ); } else { // 关闭或展示 marker.carMarker?.hide(); // 显示轨迹 marker.line?.hide(); this.carsMap.set( carCode, Object.assign(marker, { show: false, enabled: false, }) ); } } } } /** 绘制历史动画轨迹 */ drawTrakLine( : WSCarCoordinate, : LineColor = { shadowColor: "#FFA300", lineColor: "#FFA300", } ): maptalks.LineString | null { const marker = this.carsMap.get(carCode); // console.log("drawTrakLine", marker, carCode); if (marker) { const line = new maptalks.LineString( (carGeo as Coordinate[]).map((item) => item), { zIndex: 9, symbol: { shadowColor, shadowBlur: 14, lineColor, lineWidth: 8, lineDasharray: [0, 0], }, } ); line.addTo(this.trakLayer); this.carsMap.set(carCode, { ...marker, line, }); return line; } return null; } /** 绘制车辆历史标记点 */ drawHistoryCar(carCode: string, carGeo: Coordinate) { const marker = this.carsMap.get(carCode); if (marker) { // 创建一个Marker对象 const carMarker = new maptalks.Marker(carGeo, { symbol: [ { markerFile: utils.getCarStatusSvg(marker.carTypeId, "OFFLINE"), markerHeight: 60, markerWidth: 60, markerOpacity: 1, markerVerticalAlignment: "middle", }, ], }).addTo(this.trakLayer); this.carsMap.set(carCode, { ...marker, carMarker, }); } } /** 插入车辆点线动画 */ playHistoryAnimateVideo(carCode: string, route: any): null | RoutePlayer { const marker = this.carsMap.get(carCode); if (marker) { const player = new RoutePlayer(route, this.map, { maxTrailLine: 10, }); this.carsMap.set(carCode, { ...marker, carHistoryVideo: player, }); return player; } return null; } /** 绘制车其他标记点 */ drawOtherHistoryMarkers(carGeos: Coordinate[]) { for (let i = 0; i < carGeos.length; i++) { let markerFile: any = "ONLINE"; // 开始 if (i === 0) { markerFile = utils.getCarStatusSvg(-1, "START"); // 结束 } else if (i === carGeos.length - 1) { markerFile = utils.getCarStatusSvg(-1, "END"); // 停留 } else { markerFile = utils.getCarStatusSvg(-1, "PAUSE"); } const car = new maptalks.Marker(carGeos[i], { id: i, zIndex: 9, symbol: i === 0 || i === carGeos.length - 1 ? { markerFile, markerHeight: 40, markerWidth: 40, markerOpacity: 1, markerVerticalAlignment: "top", markerDx: 3, markerDy: -10, } : { markerFile, markerHeight: 80, markerWidth: 80, markerOpacity: 0.9, markerVerticalAlignment: "middle", markerDx: -12, markerDy: -10, }, }); car.addTo(this.trakLayer); } } /** 绘制车辆历史路线 */ // drawHistoryCarPath({ // carCode, // carGeos = [], // lineColor = "#000", // }: AnimateiHstoryCarPath) { // const marker = this.carsMap.get(carCode); // if (marker) { // // 创建一个Path对象 // const linePath = new maptalks.Path( // carGeos.length ? carGeos : marker.line?.getCoordinates(), // { // symbol: { // lineColor, // lineWidth: 2, // }, // } // ).addTo(this.trakLayer); // this.carsMap.set(carCode, { // ...marker, // linePath, // }); // } // } /** 执行车辆历史路线动画 */ // takeHistoryCarAnimate(carCode: string, duration: number) { // const marker = this.carsMap.get(carCode); // if (marker) { // // 执行Marker的animate动画方法 // marker.carMarker!.animate({ // path: marker.linePath, // 设置路径为刚刚创建的Path对象 // duration, // 动画持续时间为5000毫秒 // easing: "linear", // 动画缓动函数为线性 // repeat: false, // 动画循环执行 // autoRotate: true, // 自动旋转方向 // }); // } // } /** 车辆展示或关闭弹框 */ /** * @description: * @param carCode :车辆标识 * @param carMarker :车辆点位 * @response: DISABLED 启用禁用状态 * DISCOORDINATE 该车不存在或无点位 * @return */ async showCarInfoWindow( carCode: string, carMarker: maptalks.Marker | null ): Promise<boolean | string> { const marker = this.carsMap.get(carCode); console.log("能不能获取到", this.carsMap, carCode); if (marker) { // 如果当前为打开自定义组件ui时终止 if (marker.showTip) return true; // 如果为跟踪点位 添加到跟踪数组中 存在着不添加 if (marker.follow) { !this.followCarArr.includes(marker.carCode) && this.followCarArr.push(marker.carCode); return true; } // 判断车辆启用禁用状态 if (!marker.enabled) return "DISABLED"; // 创建点位弹窗 (carMarker || marker.carMarker!).setInfoWindow({ // 如果您不希望地图进行平移动画以适应打开的窗口,请将其设置为 false。 autoPan: true, // 自动打开所有者事件的信息窗口,例如“单击”将通过单击或右键单击窗口所有者打开信息窗口。 autoOpenOn: null, // UI是否为全局单个UI,设置为true则同一时间只显示一个UI single: false, // 如果您想要自定义信息窗口、自定义 html 代码或将 HTMLElement 设置为内容,请将其设置为 true。 custom: true, // 信息窗口的内容。 content: "", visible: true, dx: -130, dy: -160, animation: "fade", }); // console.log("marker", marker); // 获取当前点位上的弹窗对象 const infoW = (carMarker || marker.carMarker!).getInfoWindow(); // 写入弹窗内容 this.infoWindowContent(infoW, marker.carCode); // 如果没有当前点击marker值 赋值 if (!this.currentCarMarker) { (carMarker || marker.carMarker!).openInfoWindow(); this.currentCarMarker = marker; // 获取弹窗展示的信息 let info = await infoWindowData( carCode, this.getCoordinatesByCarCode(carCode) ); // console.log("1231231213 infoWindowData",marker) info = { ...info, existCamera: marker?.existCamera, existCarGeo: marker?.existCarGeo, }; console.log("单机弹窗", info); // 放入对应点位对象 this.setInfoWindowData(carCode, info); // 绘制消息弹窗 createInfoWindow({ carCode, pointInfo: info, eventFunction: (type, carCode) => { if (this.infoWindowCallback) { const marker = this.carsMap.get(carCode as string); if (marker) { this.infoWindowCallback(type, marker); } } }, }); this.updateInfoWindowVisiable(carCode, true); // 定位车辆 this.map.animateTo( { center: marker.carGeo, zoom: HBMap.InitialCarZoom, }, { duration: 2 * 1000, } ); // 相同代表点击同一个marker } else if (this.currentCarMarker.carCode === marker.carCode) { // 更新点位消息弹窗状态 this.updateInfoWindowVisiable(carCode, false); // 再删除弹窗 setTimeout( () => (carMarker || marker.carMarker!).removeInfoWindow(), 0 ); this.currentCarMarker = null; // 定位车辆 this.map.animateTo( { center: HBMap.InitialCoordinate, zoom: HBMap.InitialZoom, }, { duration: 2 * 1000, } ); // 否则是关闭一个已经打开的弹窗 打开一个新marker弹窗 } else { // 其中 先判断上一个点位对象是否在跟踪数组中 不存在则关闭上一个弹窗 if (!this.followCarArr.includes(this.currentCarMarker.carCode)) { // console.log(1231231123); // 更新点位消息弹窗状态 this.updateInfoWindowVisiable(this.currentCarMarker?.carCode, false); this.currentCarMarker?.carMarker?.removeInfoWindow(); } // 更新点位消息弹窗状态 // setTimeout(async () => { (carMarker || marker.carMarker!).openInfoWindow(); this.currentCarMarker = marker; // 获取弹窗展示的信息 let info = await infoWindowData( carCode, this.getCoordinatesByCarCode(carCode) ); info = { ...info, existCamera: marker?.existCamera, existCarGeo: marker?.existCarGeo, }; // 放入对应点位对象 this.setInfoWindowData(carCode, info); // 绘制消息弹窗 createInfoWindow({ carCode, pointInfo: info, eventFunction: (type, carCode) => { if (this.infoWindowCallback) { const marker = this.carsMap.get(carCode as string); if (marker) { this.infoWindowCallback(type, marker); } } }, }); this.updateInfoWindowVisiable(carCode, true); // 定位车辆 this.map.animateTo( { center: marker.carGeo, zoom: HBMap.InitialCarZoom, }, { duration: 2 * 1000, } ); // }, 1000); } return true; } else return "DISCOORDINATE"; } /** 绘制车辆标记 */ private drawCar({ carCode, carTypeId, carGeo, workStatus, azimuth, }: CarMarkerData): maptalks.Marker { // console.log( // "drawCar", // workStatus, // utils.getCarStatusSvg(carTypeId, workStatus || "OFFLINE") // ); const that = this, car = new maptalks.Marker(carGeo, { id: carCode, zIndex: 15, symbol: [ { markerFile: utils.getCarStatusSvg( carTypeId, workStatus || "OFFLINE" ), markerHeight: 60, markerWidth: 60, markerOpacity: 1, markerVerticalAlignment: "middle", markerRotation: azimuth.concat().pop(), }, // { // markerHeight: 60, // markerWidth: 60, // markerFill: "rgba(255, 255, 255, 0)", // markerType: "ellipse", // markerLineWidth: 0, // }, ], properties: { altitude: 400, }, }); car.addTo(this.markerLayer); car.on("click", async function (e) { // 不为跟踪点位 不同点位点击需要进行弹窗重绘 that.showCarInfoWindow(e.target._id, car); }); // 显示车牌 // car.on("mouseenter", async function (e) { // const marker = that.carsMap.get(e.target._id); // if (marker) { // if (marker.toolTip2) { // marker.toolTip2.show(new maptalks.Coordinate(marker.carGeo)); // } else { // const toolTip2 = new maptalks.ui.ToolTip(`$`, { // width: 70, // height: 30, // showTimeout: 60 * 1000, // animationDuration: 2000, // animation: "scale | fade", // // autoPan: true, 不能加这个煞笔属性 艹卡了我一下午 // // 自动打开所有者事件的信息窗口,例如“单击”将通过单击或右键单击窗口所有者打开信息窗口。 // // cssName: "marker-tooltip-car-num", // // UI是否为全局单个UI,设置为true则同一时间只显示一个UI // single: false, // dx: -35, // dy: 35, // }); // toolTip2.setStyle("marker-tooltip-car-num"); // toolTip2.addTo(marker.carMarker!); // toolTip2.show(new maptalks.Coordinate(marker.carGeo)); // that.carsMap.set(e.target._id, { // ...marker, // toolTip2, // }); // } // } // }); // // 隐藏车牌 // car.on("mouseout", async function (e) { // const marker = that.carsMap.get(carCode); // if (marker) { // marker.toolTip2!.hide(); // } // }); return car; } /** 批量绘制车辆标记 */ bathDrawCar(carsArr: Array<CarMarkerData>): void { // 存储全量车辆信息 console.log("bathDrawCar", carsArr); for (let i = 0; i < carsArr.length; i++) { const carData = { ...carsArr[i], }; console.log("carData", carData); carsArr[i] = Object.assign(carData, { carMarker: this.drawCar(carData), }); // 隐藏禁用的车辆 if (!carsArr[i].enabled) carsArr[i].carMarker?.hide(); this.carsMap.set(carsArr[i].carCode, carData); // console.log("!carsArr[i].enabled", !carsArr[i].enabled); // console.log("bathDrawCar carsArr[i]", carsArr[i], this.carsMap); } // this.carsArr = carsArr; } /** 添加指定carCode车辆信息 */ addNewCarMapObject(car: CarMarkerData) { const = car; this.carsMap.set(carCode, car); } /** 点位消息窗口是否打开 */ updateInfoWindowVisiable(carCode: string, visiable: boolean) { const marker = this.carsMap.get(carCode); if (marker) { this.carsMap.set(carCode, { ...marker, infoWindowVisiable: visiable, }); } } /** 点位赋值消息窗口信息 */ setInfoWindowData(carCode: string, infoWindowData: InfoWindowData) { const marker = this.carsMap.get(carCode); if (marker) { this.carsMap.set(carCode, { ...marker, infoWindowData, }); } } /** 重绘消息窗口 */ infoWindowContent(ifoWindow: maptalks.ui.InfoWindow, carCode: string) { return ( ifoWindow && ifoWindow.setContent(`<div id="info-content-$"></div>`) ); } /** 跟踪车辆消息窗口 */ drawTrackCarinfoWindow(carCode: string, carInfo: any) { const marker = this.carsMap.get(carCode), that = this; if (marker) { marker?.carMarker?.setInfoWindow({ // 如果您不希望地图进行平移动画以适应打开的窗口,请将其设置为 false。 autoPan: false, zIndex: 16, // 自动打开所有者事件的信息窗口,例如“单击”将通过单击或右键单击窗口所有者打开信息窗口。 autoOpenOn: null, // UI是否为全局单个UI,设置为true则同一时间只显示一个UI single: false, // 如果您想要自定义信息窗口、自定义 html 代码或将 HTMLElement 设置为内容,请将其设置为 true。 custom: true, // 信息窗口的内容。 content: "", dx: -130, dy: -160, animation: "fade", }); // 获取当前点位上的弹窗对象 // 写入弹窗内容 that.infoWindowContent(marker?.carMarker?.getInfoWindow()!, carCode); marker?.carMarker?.openInfoWindow(); // console.log("carInfo carInfo", carInfo); createInfoWindow({ carCode, pointInfo: { ...carInfo, existCamera: marker?.existCamera, existCarGeo: marker?.existCarGeo, }, eventFunction: (type, carCode) => { if (this.infoWindowCallback) { const marker = this.carsMap.get(carCode as string); if (marker) { this.infoWindowCallback(type, marker); } } }, }); // 更新点位消息弹窗状态 that.updateInfoWindowVisiable(carCode, true); } } /** 初始化跟踪车辆轨迹点位数组 */ initTrckCarCoordinateArr( carCode: string, coordinates: Coordinate[] ): Coordinate[] | null { const marker = this.carsMap.get(carCode as string); if (marker) { marker.trakCollection = coordinates; this.carsMap.set(carCode, marker); return coordinates; } return null; } /** 查找跟踪车辆轨迹点位数组 */ getTrckCarCoordinateArr(carCode: string): Coordinate[] | [] { const marker = this.carsMap.get(carCode); // console.log("getTrckCarCoordinateArr", marker, this.carsMap); return marker ? marker.trakCollection : []; } /** 更新跟踪车辆轨迹点位数组 */ updateTrckCarCoordinateArr({ carCode, carGeo, }: WSCarCoordinate): Coordinate[] | null { const marker = this.carsMap.get(carCode as string); if (marker) { marker.trakCollection.push(carGeo as Coordinate); this.carsMap.set(carCode as string, marker); return marker.trakCollection; } return null; } /** 混合更新跟踪车辆轨迹点位数组 */ updateMixTrckCarCoordinateArr({ carCode, carGeo, uploadTime, }: WSCarCoordinate): Coordinate[] | null { const marker = this.carsMap.get(carCode); // console.log( // ` updateMixTrckCarCoordinateArr`, // carCode, // uploadTime, // this.carsMap, // marker // ); if (marker) { if ( Array.isArray((carGeo as Coordinate[])[0]) && Array.isArray(uploadTime as string[]) ) { const trakCollection = marker.trakCollection.concat( carGeo as Coordinate[] ); // console.log("marker.uploadTime", marker.uploadTime); const uploadTimeTemp = ( Array.isArray(marker.uploadTime) ? marker.uploadTime : [marker.uploadTime] ).concat(uploadTime as string[]); this.carsMap.set(carCode, { ...marker, trakCollection, uploadTime: uploadTimeTemp, }); return trakCollection; } else { const trakCollection = [ ...marker.trakCollection, carGeo, ] as Coordinate[]; let uploadTimeArr = []; if (Array.isArray(marker.uploadTime)) { uploadTimeArr = [...marker.uploadTime, uploadTime] as string[]; } else uploadTimeArr.push(uploadTime); this.carsMap.set(carCode, { ...marker, trakCollection, uploadTime: uploadTimeArr as string[], }); // console.log("updateMixTrckCarCoordinateArr", this.carsMap.get(carCode)); return trakCollection; } } return null; } /** 清空跟踪车辆轨迹点位数组 */ clearTrckCarCoordinateArr(carCode: string) { const marker = this.carsMap.get(carCode as string); if (marker) { this.carsMap.set(carCode as string, { ...marker, trakCollection: [], }); } } /** 更新轨迹 */ updateTrakLine(: WSCarCoordinate): void { const marker = this.carsMap.get(carCode); if (marker) { const = marker; line?.setCoordinates(carGeo as Coordinate[]); // console.log("updateTrakLine carGeo", carGeo); } } /** 清除轨迹 */ clearTrakLine(carCode: string): void { const marker = this.carsMap.get(carCode); if (marker) { const = marker; line?.remove(); this.carsMap.set(carCode, ); } } /** 聚焦 */ focusOnMarker(points: Coordinate, duration: number, focus: boolean = false) { // console.log("focusOnMarker", this.carFocus, points); if (!focus) return; this.map.animateTo( { center: points, zoom: this.map.getZoom(), }, { duration, } ); } /** 获取当前跟踪车辆信息 */ getMapObjectById(carCode: string): CarMarkerData | null { return this.carsMap.get(carCode) ?? null; } /** 已知两点计算角标 */ // getAzimuth(prvCoordinate: Coordinate, curCoordinate: Coordinate): number { // // console.log("getAzimuth", prvCoordinate, curCoordinate); // let azimuth = 0; // const [lat1, lon1] = prvCoordinate, // [lat2, lon2] = curCoordinate, // dlat = lat2 - lat1, // dlon = lon2 - lon1; // if (dlon === 0) { // azimuth = dlat > 0 ? 90 : -90; // } else if (dlat === 0) { // azimuth = dlon > 0 ? 0 : 270; // } else { // azimuth = (Math.atan(dlat / dlon) * 180) / Math.PI; // if (dlon < 0) { // azimuth += 180; // } else if (dlon >= 0 && dlat < 0) { // azimuth += 360; // } // } // return azimuth; // } getAzimuth([lon1, lat1]: Coordinate, [lon2, lat2]: Coordinate) { const lon1_rad = (lon1 * Math.PI) / 180; const lat1_rad = (lat1 * Math.PI) / 180; const lon2_rad = (lon2 * Math.PI) / 180; const lat2_rad = (lat2 * Math.PI) / 180; const tan_y = Math.tan(lat2_rad - lat1_rad); const tan_x = Math.sin(lon2_rad - lon1_rad) * Math.cos(lat1_rad) - Math.cos(lon2_rad - lon1_rad) * Math.sin(lat1_rad) * Math.cos(lat2_rad - lat1_rad); let azimuth = Math.atan2( Math.sin(lon2_rad - lon1_rad) * Math.cos(lat2_rad), Math.cos(lat1_rad) * Math.sin(lat2_rad) - Math.sin(lat1_rad) * Math.cos(lat2_rad) * Math.cos(lon2_rad - lon1_rad) ); azimuth = (azimuth * 180) / Math.PI; return azimuth; } /** 关闭跟踪车辆弹窗 */ closeTrackCarinfoWindow(carCode: string) { const marker = this.carsMap.get(carCode); // // console.log("closeTrackCarinfoWindow", marker); if (marker) { const infoW = marker?.carMarker?.getInfoWindow(); marker?.carMarker?.closeInfoWindow(); // 在删除弹窗dom setTimeout(() => infoW?.remove(), 0); } } /** 设置车辆跟踪状态 */ updateCarMarkerFollowStatus(carCode: string): boolean { if ( this.followCarArr.length >= HBMap.infoWindowNum && !this.followCarArr.includes(carCode) ) return false; const marker = this.carsMap.get(carCode); if (marker) { this.carsMap.set(carCode, { ...marker, follow: !marker.follow, }); const newMarker = this.carsMap.get(carCode)!; // 变更跟随模式时 更新当前点击对象 this.currentCarMarker = newMarker; // 更新最新跟踪数组 if (newMarker.follow && !this.followCarArr.includes(carCode)) this.followCarArr.push(carCode); else this.followCarArr.splice( this.followCarArr.findIndex((f) => f === carCode), 1 ); // 如果当前跟踪车辆大约两台 取消跟随某辆车状态 // this.carFocus = this.followCarArr.length < 2; // // console.log("this.carsMap.get(carCode)", newMarker); } return true; } /** 改变车辆map属性变化 */ setCarMapProperty(carCode: string, property: any): void { const marker = this.carsMap.get(carCode); if (marker) { this.carsMap.set(carCode, Object.assign(marker, property)); } } /** 改变车辆marker显隐 */ async setCarMarkerVisiable(carTypeArr: number[]) { // eslint-disable-next-line @typescript-eslint/no-unused-vars for (const [key, value] of this.carsMap.entries()) { let inArr = false; const marker = this.carsMap.get(value.carCode); carTypeArr.forEach((type) => type === value.carTypeId && (inArr = true)); if (inArr) { if (marker) { // 关闭或展示 marker.carMarker?.show(); // 显示轨迹 marker.line?.show(); // 如果为跟踪状态 需要重新绑定 if (marker.follow) { // 获取当前点位上的弹窗对象 const infoW = marker.carMarker?.getInfoWindow(), carCode = marker.carCode; if (infoW) { marker.carMarker?.openInfoWindow(); // // 写入弹窗内容 this.infoWindowContent(infoW, carCode); // 如果没有当前点击marker值 赋值 marker.carMarker?.openInfoWindow(); this.currentCarMarker = marker; // 获取弹窗展示的信息 let info = await infoWindowData( carCode, this.getCoordinatesByCarCode(carCode) ); info = { ...info, existCamera: marker?.existCamera, existCarGeo: marker?.existCarGeo, }; // 放入对应点位对象 this.setInfoWindowData(carCode, info); // console.log(11111); // 绘制消息弹窗 createInfoWindow({ carCode, pointInfo: info, eventFunction: (type) => { if (this.infoWindowCallback) { if (marker) { this.infoWindowCallback(type, marker); } } }, }); } } this.carsMap.set( value.carCode, Object.assign(marker, { show: true, }) ); } } else { if (marker) { // 关闭或展示 marker.carMarker?.hide(); // 隐藏轨迹 marker.line?.hide(); this.carsMap.set( value.carCode, Object.assign(marker, { show: false, }) ); } } } } /** 根据carCode获取点位 */ getCoordinateByCarCode(carCode: string) { const marker = this.carsMap.get(carCode); return marker ? marker.carGeo : []; } /** 清空carCode轨迹数组 */ clearTrakCollectionByCarCode(carCode: string) { const marker = this.carsMap.get(carCode); if (marker) { this.carsMap.set(carCode, { ...marker, trakCollection: [], }); } } /** 根据carCode获取轨迹数组 */ getTrakCollectionByCarCode(carCode: string): Coordinate[] | Coordinate { const marker = this.carsMap.get(carCode); // console.log("getTrakCollectionByCarCode", marker); if (marker) { return marker.trakCollection.length ? marker.trakCollection || [] : marker.carGeo; } return []; } /** 添加独立tooltip */ addToolTip(polygon: maptalks.Polygon, name: string) { const toolTip = new maptalks.ui.ToolTip(name, { width: 100, height: 30, animation: "scale | fade", // autoPan: true, // 自动打开所有者事件的信息窗口,例如“单击”将通过单击或右键单击窗口所有者打开信息窗口。 // cssName: "marker-tooltip-car-num", // UI是否为全局单个UI,设置为true则同一时间只显示一个UI single: false, dx: -35, dy: -55, }); toolTip.setStyle("marker-tooltip-grid"); toolTip.addTo(polygon); return toolTip; } /** * 地图上划区域 * @param points * @param config 用来设置区域样式 * @param eventType 时间类型 格式 'mousedown mouseup click dblclick contextmenu touchstart touchend mouseenter mouseout' * @param onEvent 时间总处理方法 */ // points: Coordinate[] = [], // config: BaseParameterData, // eventType: string | null, // onEvent: maptalks.EvenableHandlerFun | null drawArea(, onEvent = null }: AreaData) { // const areaArr: maptalks.Polygon[] = []; let isFocus: boolean = false; points.forEach((point) => { const area = new maptalks.Polygon(point.geo as Coordinate[], { ...config, id: point.id, }), toolTip = this.addToolTip(area, point.name); area .addTo(this.areaLayer!) .on("click", (e) => { if (onEvent) { onEvent(e, point, "click"); } }) .on("dblclick", (e) => { isFocus = !isFocus; if (isFocus) { this.map?.fitExtent(area.getExtent(), 1); } else { this.map.setZoom(HBMap.InitialZoom); } if (onEvent) { onEvent(e, point, "dblclick"); } }) .on("mouseenter", (e) => { console.log("mouseenter", e); toolTip.show(); area.updateSymbol({ polygonOpacity: 0.4, }); }) .on("mouseout", (e) => { console.log("mouseenter", e); toolTip.hide(); area.updateSymbol({ polygonOpacity: 0.1, }); }); // areaArr.push(area); }); } /** * 地图上划区域 * @param points * @param config 用来设置区域border样式 * @param eventType 时间类型 格式 'mousedown mouseup click dblclick contextmenu touchstart touchend mouseenter mouseout' * @param onEvent 时间总处理方法 */ drawAreaBorder({ points = [], config = , eventType = null, onEvent = null, }: AreaData): maptalks.LineString[] { const lineArr: maptalks.LineString[] = []; console.log("points", points); points.forEach((point) => { // console.log(123123, point); lineArr.push( new maptalks.LineString(point.geo as Coordinate[], { ...config, id: `line-$`, properties: { altitude: new Array(point.geo.length).fill( config?.properties?.altitude || 0 ), }, }).on(eventType ? eventType : "click", (e) => { console.log("drawAreaBorder", lineArr); if (onEvent) { onEvent(e, point); } }) ); }); console.log("this.areaLayer this.areaLayer", this.areaLayer); if (!this.areaLayer) { this.areaLayer = new maptalks.VectorLayer("area", lineArr, { enableAltitude: true, drawAltitude: { polygonFill: "#489bfb", polygonOpacity: 0.3, lineWidth: 0, }, zindex: 3, }); this.areaLayer.addTo(this.map); } else { this.areaLayer.addGeometry(lineArr); } return lineArr; } /** 删除当前添加的下级网格 */ removeVectorLayerGeometrys( removeGeometry: maptalks.Geometry | maptalks.Geometry[] ) { this.areaLayer!.removeGeometry(removeGeometry); } /** 清除所有网格 */ clearAllAreas() { this.areaLayer?.clear(); this.map?.removeLayer(this.areaLayer!); this.areaLayer = null; } // ------------------------------------------------------------------------------------ /** 清除交互后 ImageData 缓存 */ clearMapCache() { setTimeout(() => { const layers = this.map.getLayers(); layers.forEach((layer) => { layer.getRenderer().clearImageData(); }); }, 100); } /** 设置车辆标记位置 */ moveCar(point: number[]) { this.car.setCoordinates(point); } /** 地图自适应展示图形 */ fitExtent(geo: Extentable, zoomOffset = 0) { this.map.fitExtent(geo.getExtent(), zoomOffset); } }
2022年12月14日
121
0
0
2022-09-01
Sub.prototype = Object.create(Vue.prototype) 和 Sub = Object.create(Vue) 的差异理解
不完全相同。Sub.prototype = Object.create(Vue.prototype) 是将 Sub 函数的原型对象设置为一个从 Vue.prototype 对象继承而来的新对象。这意味着 Sub.prototype 对象继承了 Vue.prototype 对象的所有属性和方法,并且任何通过 new Sub() 创建的实例都将继承这些属性和方法。而 Sub = Object.create(Vue) 是将 Sub 对象本身设置为一个从 Vue 对象继承而来的新对象。这意味着 Sub 对象继承了 Vue 对象的所有属性和方法,但是 Sub.prototype 对象仍然是一个空对象,任何通过 new Sub() 创建的实例将不会继承任何 Sub.prototype 对象中的属性和方法。通常在创建一个自定义类时,我们会使用 Sub.prototype = Object.create(Super.prototype) 来设置子类的原型对象,以便继承父类的方法。而使用 Sub = Object.create(Super) 则更适合创建一个新的对象来扩展现有对象的功能,而不是创建一个新的类。下面是针对cerate的验证过程 可能有些絮叨(旺柴)function Vue() `)} this.getAge = function () `)}}Vue.prototype.getProtoName = function () )}Vue.prototype.getProtoAge = function () )}function Sub() // 让Sub构造函数中的prototype等于Vue.prototype/**让Sub构造函数中的prototype等于Vue.prototype等同于Sub.prototype.__proto__ = Vue.prototype因为之前纠结于sub.__propo__ = Sub.prototype实例对象的__proto__指向构造函数的prototype 所以误认为sub.prototype == Sub.__proto__等式也成立此段代码的意义是实现构造函数Sub所声明的所有实例对象 可以通过__proto__属性访问到Vue.prototype中的属性和方法因为sub.__proto__ = Sub.prototype 所以sub.__proto__.__proto__ = Vue.prototype隐式原型链__proto__的作用就是当一个当前对象中没有的属性或方法时通过__proto__属性去当前对象的隐式原型链中查找sub.__proto__指向了构造函数Sub的prototype原型值又因为Object.create所创建的对象的隐式原型链指向了第一个参数对象等同于Sub.prototype = Object.create[这个新创建的对象拥有Vue上所有的属性和方法]注:不包含构造函数中的属性和方法 如果想要继承构造函数中的属性和方法需要使用call或apply方法将构造函数中的属性和方法绑定到新创建的对象上例如:function Sub(name,age) 等同于sub.__proto__= Sub.prototype = Vue.prototypesub.__proto__.__proto__ = Sub.prototype.__proto__ = Vue.prototype */Sub.prototype = Object.create(Vue.prototype, ,})const sub = new Sub()
2022年09月01日
16
0
0
1
...
4
5
6
...
9