@aibee/crc-bmap 0.8.39 → 0.8.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bmap.cjs.min.js +375 -375
- package/lib/bmap.esm.js +351 -9
- package/lib/bmap.esm.min.js +375 -375
- package/lib/bmap.min.js +375 -375
- package/lib/src/plugins/car-inertial-position/car-inertial-position.d.ts +33 -0
- package/lib/src/plugins/car-inertial-position/car-inertial-position.js +90 -0
- package/lib/src/plugins/car-inertial-position/compass.d.ts +44 -0
- package/lib/src/plugins/car-inertial-position/compass.js +125 -0
- package/lib/src/plugins/car-inertial-position/index.d.ts +2 -0
- package/lib/src/plugins/car-inertial-position/index.js +3 -0
- package/lib/src/plugins/car-inertial-position/utils.d.ts +41 -0
- package/lib/src/plugins/car-inertial-position/utils.js +157 -0
- package/lib/src/plugins/index.d.ts +1 -0
- package/lib/src/plugins/index.js +1 -0
- package/lib/src/plugins/navigation/navigation.d.ts +2 -2
- package/lib/src/plugins/navigation/position-navigation.d.ts +7 -5
- package/lib/src/plugins/navigation/position-navigation.js +13 -5
- package/lib/src/utils/road2.d.ts +8 -0
- package/lib/src/utils/road2.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { EventDispatcher } from "three";
|
|
2
|
+
import { Compass } from "./compass";
|
|
3
|
+
export interface CarPosition {
|
|
4
|
+
position: [number, number];
|
|
5
|
+
time: number;
|
|
6
|
+
type: "vision" | "beacon";
|
|
7
|
+
}
|
|
8
|
+
export interface CarPosInfo {
|
|
9
|
+
success: boolean;
|
|
10
|
+
pos: [number, number];
|
|
11
|
+
compass: null | number;
|
|
12
|
+
speed: number;
|
|
13
|
+
}
|
|
14
|
+
interface CarInertialPositionEventMap {
|
|
15
|
+
'change-compass': {
|
|
16
|
+
value: number;
|
|
17
|
+
};
|
|
18
|
+
'change-pos': {
|
|
19
|
+
value: CarPosInfo;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare class CarInertialPosition extends EventDispatcher<CarInertialPositionEventMap> {
|
|
23
|
+
history: CarPosition[];
|
|
24
|
+
compass: Compass;
|
|
25
|
+
compassAngle: number;
|
|
26
|
+
constructor();
|
|
27
|
+
startCompass(): void;
|
|
28
|
+
setPosition(position: CarPosition['position'], time: number): void;
|
|
29
|
+
setBeaconPosition(position: CarPosition['position'], time: number): void;
|
|
30
|
+
getPosition(): CarPosInfo;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// 车辆惯性导航
|
|
2
|
+
import { EventDispatcher } from "three";
|
|
3
|
+
import { Compass } from "./compass";
|
|
4
|
+
import { calculateLineDirection, predictFuturePosition, predictFutureSpeed, transformSpeed } from "./utils";
|
|
5
|
+
export class CarInertialPosition extends EventDispatcher {
|
|
6
|
+
startCompass() {
|
|
7
|
+
this.compass.start();
|
|
8
|
+
}
|
|
9
|
+
setPosition(position, time) {
|
|
10
|
+
this.history.push({
|
|
11
|
+
position,
|
|
12
|
+
time,
|
|
13
|
+
type: "vision"
|
|
14
|
+
});
|
|
15
|
+
// 保留最近的6条数据
|
|
16
|
+
while(this.history.length > 6){
|
|
17
|
+
this.history.shift();
|
|
18
|
+
}
|
|
19
|
+
const posInfo = this.getPosition();
|
|
20
|
+
this.dispatchEvent({
|
|
21
|
+
type: "change-pos",
|
|
22
|
+
value: posInfo
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
setBeaconPosition(position, time) {
|
|
26
|
+
this.history.push({
|
|
27
|
+
position,
|
|
28
|
+
time,
|
|
29
|
+
type: "beacon"
|
|
30
|
+
});
|
|
31
|
+
const posInfo = this.getPosition();
|
|
32
|
+
this.dispatchEvent({
|
|
33
|
+
type: "change-pos",
|
|
34
|
+
value: posInfo
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
getPosition() {
|
|
38
|
+
if (this.history.length === 0) {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
pos: [
|
|
42
|
+
0,
|
|
43
|
+
0
|
|
44
|
+
],
|
|
45
|
+
compass: null,
|
|
46
|
+
speed: 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const lastHistory = this.history.slice(-1)[0];
|
|
50
|
+
if (this.history.length < 2) {
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
pos: lastHistory.position,
|
|
54
|
+
compass: null,
|
|
55
|
+
speed: 0
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const visionHistory = this.history.filter((item)=>item.type === "vision");
|
|
59
|
+
// 推导到当前时间距离最后一个定位点的时间
|
|
60
|
+
const angle = calculateLineDirection(visionHistory.map((history)=>history.position));
|
|
61
|
+
if (angle !== null) {
|
|
62
|
+
this.compass.setAbsoluteCompass(angle, lastHistory.time);
|
|
63
|
+
}
|
|
64
|
+
const lastTime = lastHistory.time;
|
|
65
|
+
const deltaTime = Date.now() - lastTime;
|
|
66
|
+
// 预估后的速度
|
|
67
|
+
const speed = predictFutureSpeed(this.history, deltaTime);
|
|
68
|
+
// 根据角度和速度时间计算预估后的位置
|
|
69
|
+
const pos = this.compassAngle ? predictFuturePosition(lastHistory.position, speed, 360 - this.compassAngle, deltaTime) : lastHistory.position;
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
pos,
|
|
73
|
+
compass: this.compassAngle,
|
|
74
|
+
speed: transformSpeed(speed)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
dispose() {
|
|
78
|
+
this.compass.stop();
|
|
79
|
+
}
|
|
80
|
+
constructor(){
|
|
81
|
+
super(), this.history = [], this.compass = new Compass(), this.compassAngle = 0;
|
|
82
|
+
this.compass.addEventListener("compass", ({ value })=>{
|
|
83
|
+
this.compassAngle = value;
|
|
84
|
+
this.dispatchEvent({
|
|
85
|
+
type: "change-compass",
|
|
86
|
+
value
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { EventDispatcher } from "three";
|
|
2
|
+
interface SensorEventMap {
|
|
3
|
+
start: {};
|
|
4
|
+
stop: {};
|
|
5
|
+
compass: {
|
|
6
|
+
value: number;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
interface CompassData {
|
|
10
|
+
timestamp: number;
|
|
11
|
+
res: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 监听compass 在获取到视觉定位的角度之后,计算视觉和compass的误差,保存一下这个误差,在后续定位中不断的调整这个误差
|
|
15
|
+
*/
|
|
16
|
+
export declare class Compass extends EventDispatcher<SensorEventMap> {
|
|
17
|
+
compassData: CompassData[];
|
|
18
|
+
absoluteCompass: null | {
|
|
19
|
+
time: number;
|
|
20
|
+
compass: number;
|
|
21
|
+
};
|
|
22
|
+
delta: number;
|
|
23
|
+
deltas: number[];
|
|
24
|
+
constructor();
|
|
25
|
+
start(): void;
|
|
26
|
+
deviceOrientationAbsHandler: (e: DeviceOrientationEvent) => void;
|
|
27
|
+
/**
|
|
28
|
+
* 设置一个根据视觉定位返回的绝对角度 校准delta
|
|
29
|
+
* @param compass
|
|
30
|
+
* @param time
|
|
31
|
+
*/
|
|
32
|
+
setAbsoluteCompass(compass: number, time: number): void;
|
|
33
|
+
emitCompass(compass: number): void;
|
|
34
|
+
/**
|
|
35
|
+
* 获取 compass 和 deviceMotion
|
|
36
|
+
*/
|
|
37
|
+
listenDeviceOrientation(): void;
|
|
38
|
+
checkSensor(): Promise<{
|
|
39
|
+
deviceOrientation: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
checkDeviceOrientation(): Promise<boolean>;
|
|
42
|
+
stop(): void;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator";
|
|
2
|
+
import { isNil } from "lodash";
|
|
3
|
+
import { isIphone } from "../../utils";
|
|
4
|
+
import { EventDispatcher } from "three";
|
|
5
|
+
import { removeOutliers } from "./utils";
|
|
6
|
+
/**
|
|
7
|
+
* 监听compass 在获取到视觉定位的角度之后,计算视觉和compass的误差,保存一下这个误差,在后续定位中不断的调整这个误差
|
|
8
|
+
*/ export class Compass extends EventDispatcher {
|
|
9
|
+
start() {
|
|
10
|
+
this.listenDeviceOrientation();
|
|
11
|
+
this.dispatchEvent({
|
|
12
|
+
type: "start"
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 设置一个根据视觉定位返回的绝对角度 校准delta
|
|
17
|
+
* @param compass
|
|
18
|
+
* @param time
|
|
19
|
+
*/ setAbsoluteCompass(compass, time) {
|
|
20
|
+
if (!this.delta) {
|
|
21
|
+
this.emitCompass(compass);
|
|
22
|
+
}
|
|
23
|
+
if (!this.compassData.length) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.absoluteCompass = {
|
|
27
|
+
compass: compass,
|
|
28
|
+
time
|
|
29
|
+
};
|
|
30
|
+
let cutTimeCompassIndex = this.compassData.findIndex((item)=>item.timestamp >= time);
|
|
31
|
+
if (cutTimeCompassIndex === -1) {
|
|
32
|
+
cutTimeCompassIndex = this.compassData.length - 1;
|
|
33
|
+
}
|
|
34
|
+
const prevCompass = this.compassData[cutTimeCompassIndex - 1];
|
|
35
|
+
const nextCompass = this.compassData[cutTimeCompassIndex];
|
|
36
|
+
let curCompass;
|
|
37
|
+
if (!prevCompass) {
|
|
38
|
+
curCompass = nextCompass;
|
|
39
|
+
} else if (!nextCompass) {
|
|
40
|
+
return;
|
|
41
|
+
} else {
|
|
42
|
+
curCompass = nextCompass.timestamp - time > prevCompass.timestamp - time ? prevCompass : nextCompass;
|
|
43
|
+
}
|
|
44
|
+
const delta = compass - curCompass.res;
|
|
45
|
+
this.deltas.push(delta);
|
|
46
|
+
const deltas = removeOutliers(this.deltas);
|
|
47
|
+
if (deltas.length) {
|
|
48
|
+
this.delta = deltas.reduce((sum, cur)=>sum + cur, 0) / deltas.length;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
emitCompass(compass) {
|
|
52
|
+
// 把 compass 限制在 0 ~ 360
|
|
53
|
+
console.log("compass delta", this.delta, compass);
|
|
54
|
+
this.dispatchEvent({
|
|
55
|
+
type: "compass",
|
|
56
|
+
value: (compass + this.delta + 360) % 360
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 获取 compass 和 deviceMotion
|
|
61
|
+
*/ listenDeviceOrientation() {
|
|
62
|
+
if (isIphone) {
|
|
63
|
+
window.addEventListener("deviceorientation", this.deviceOrientationAbsHandler, false);
|
|
64
|
+
} else {
|
|
65
|
+
window.addEventListener("deviceorientationabsolute", this.deviceOrientationAbsHandler, {
|
|
66
|
+
absolute: true
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
checkSensor() {
|
|
71
|
+
var _this = this;
|
|
72
|
+
return _async_to_generator(function*() {
|
|
73
|
+
const deviceOrientation = yield _this.checkDeviceOrientation();
|
|
74
|
+
return {
|
|
75
|
+
deviceOrientation
|
|
76
|
+
};
|
|
77
|
+
})();
|
|
78
|
+
}
|
|
79
|
+
checkDeviceOrientation() {
|
|
80
|
+
return _async_to_generator(function*() {
|
|
81
|
+
var _window_DeviceOrientationEvent;
|
|
82
|
+
if (!isIphone) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (typeof window.DeviceOrientationEvent !== "undefined" && typeof ((_window_DeviceOrientationEvent = window.DeviceOrientationEvent) == null ? void 0 : _window_DeviceOrientationEvent.requestPermission) === "function") {
|
|
86
|
+
try {
|
|
87
|
+
var _window_DeviceOrientationEvent1;
|
|
88
|
+
const permission = yield (_window_DeviceOrientationEvent1 = window.DeviceOrientationEvent) == null ? void 0 : _window_DeviceOrientationEvent1.requestPermission();
|
|
89
|
+
return permission === "granted";
|
|
90
|
+
} catch (e) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
})();
|
|
96
|
+
}
|
|
97
|
+
stop() {
|
|
98
|
+
if (isIphone) {
|
|
99
|
+
window.removeEventListener("deviceorientation", this.deviceOrientationAbsHandler, false);
|
|
100
|
+
} else {
|
|
101
|
+
window.removeEventListener("deviceorientationabsolute", this.deviceOrientationAbsHandler, {
|
|
102
|
+
absolute: true
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
this.compassData = [];
|
|
106
|
+
this.dispatchEvent({
|
|
107
|
+
type: "stop"
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
constructor(){
|
|
111
|
+
super(), this.compassData = [], this.absoluteCompass = null, this.delta = 0, this.deltas = [], this.deviceOrientationAbsHandler = (e)=>{
|
|
112
|
+
const currTime = Date.now();
|
|
113
|
+
const { alpha, beta, gamma } = e;
|
|
114
|
+
if (isNil(alpha) || isNil(beta) || isNil(gamma)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const compass = isIphone ? e.webkitCompassHeading : 360 - alpha;
|
|
118
|
+
this.compassData.push({
|
|
119
|
+
timestamp: currTime,
|
|
120
|
+
res: compass
|
|
121
|
+
});
|
|
122
|
+
this.emitCompass(compass);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CarPosition } from "./car-inertial-position";
|
|
2
|
+
/**
|
|
3
|
+
* 计算一组点位的斜率和方向
|
|
4
|
+
* @param points
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export declare function calculateLineDirection(points: [number, number][]): number | null;
|
|
8
|
+
/**
|
|
9
|
+
* 计算速度,单位是m/ms km/s
|
|
10
|
+
* @param positions
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
export declare function calculateInstantaneousSpeed(positions: CarPosition[]): number[];
|
|
14
|
+
/**
|
|
15
|
+
* 预测一段时间后的速度
|
|
16
|
+
* @param speeds
|
|
17
|
+
* @param timeIntervalMs
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
export declare function predictFutureSpeed(positions: CarPosition[], timeIntervalMs: number): number;
|
|
21
|
+
/**
|
|
22
|
+
* 转换速度从 m/ms 转成 km/h
|
|
23
|
+
* @param speed
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
export declare function transformSpeed(speed: number): number;
|
|
27
|
+
/**
|
|
28
|
+
* 预估未来的位置
|
|
29
|
+
* @param lastPosition
|
|
30
|
+
* @param speed
|
|
31
|
+
* @param angle
|
|
32
|
+
* @param time
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
export declare function predictFuturePosition(lastPosition: [number, number], speed: number, angle: number, time: number): [number, number];
|
|
36
|
+
/**
|
|
37
|
+
* 过滤偏差大的值
|
|
38
|
+
* @param data
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
export declare function removeOutliers(data: number[]): number[];
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { Vector2 } from "three";
|
|
2
|
+
/**
|
|
3
|
+
* 计算一组点位的斜率和方向
|
|
4
|
+
* @param points
|
|
5
|
+
* @returns
|
|
6
|
+
*/ export function calculateLineDirection(points) {
|
|
7
|
+
const n = points.length;
|
|
8
|
+
if (n < 2) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const angles = [];
|
|
12
|
+
for(let i = 1; i < points.length; i++){
|
|
13
|
+
const point1 = new Vector2(points[i][0], points[i][1]);
|
|
14
|
+
const point0 = new Vector2(points[i - 1][0], points[i - 1][1]);
|
|
15
|
+
if (point1.distanceTo(point0) === 0) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const radians = new Vector2().subVectors(point1, point0).angle() // 于X轴的正方向的夹角 这个是顺时针的角度
|
|
19
|
+
;
|
|
20
|
+
const angle = 360 - (radians / Math.PI * 180 - 90 + 360) % 360;
|
|
21
|
+
angles.push(angle);
|
|
22
|
+
}
|
|
23
|
+
const filteredAngles = removeOutliers(angles);
|
|
24
|
+
if (!filteredAngles.length) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
let weight = 0;
|
|
28
|
+
// 最近的方向权重更大
|
|
29
|
+
const averageAngle = filteredAngles.reduce((sum, angle, i)=>{
|
|
30
|
+
weight += i + 1;
|
|
31
|
+
return sum + angle * (i + 1);
|
|
32
|
+
}, 0) / weight;
|
|
33
|
+
return averageAngle;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 计算速度,单位是m/ms km/s
|
|
37
|
+
* @param positions
|
|
38
|
+
* @returns
|
|
39
|
+
*/ export function calculateInstantaneousSpeed(positions) {
|
|
40
|
+
const speeds = [];
|
|
41
|
+
for(let i = 1; i < positions.length; i++){
|
|
42
|
+
const prev = positions[i - 1];
|
|
43
|
+
const current = positions[i];
|
|
44
|
+
// 计算位置变化
|
|
45
|
+
const deltaX = current.position[0] - prev.position[0];
|
|
46
|
+
const deltaY = current.position[1] - prev.position[1];
|
|
47
|
+
const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2);
|
|
48
|
+
// 计算时间变化
|
|
49
|
+
const deltaTime = current.time - prev.time; // 时间单位为毫秒
|
|
50
|
+
// 计算瞬时速度
|
|
51
|
+
if (deltaTime > 0) {
|
|
52
|
+
const speed = distance / deltaTime;
|
|
53
|
+
speeds.push(speed);
|
|
54
|
+
} else {
|
|
55
|
+
speeds.push(0); // 如果时间变化为0,速度为0
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return speeds;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 预测一段时间后的速度
|
|
62
|
+
* @param speeds
|
|
63
|
+
* @param timeIntervalMs
|
|
64
|
+
* @returns
|
|
65
|
+
*/ export function predictFutureSpeed(positions, timeIntervalMs) {
|
|
66
|
+
console.log("timeIntervalMs", timeIntervalMs);
|
|
67
|
+
const speeds = calculateInstantaneousSpeed(positions);
|
|
68
|
+
console.log("speeds", speeds);
|
|
69
|
+
const n = speeds.length;
|
|
70
|
+
if (n === 0) {
|
|
71
|
+
return 0; // 如果没有速度数据,返回0
|
|
72
|
+
}
|
|
73
|
+
// 获取当前速度
|
|
74
|
+
const currentSpeed = speeds[n - 1];
|
|
75
|
+
// 计算加速度(简单的平均加速度)
|
|
76
|
+
let acceleration = 0;
|
|
77
|
+
if (n > 1) {
|
|
78
|
+
const speedDifferences = speeds.slice(1).map((speed, index)=>speed - speeds[index]);
|
|
79
|
+
const time = positions.slice(1).reduce((sum, cur, index)=>sum + (cur.time - positions[index].time), 0);
|
|
80
|
+
console.log("time", time);
|
|
81
|
+
if (!time) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
acceleration = speedDifferences.reduce((sum, value)=>sum + value, 0) / time;
|
|
85
|
+
}
|
|
86
|
+
console.log("acceleration", acceleration);
|
|
87
|
+
// 预测未来速度 m/ms
|
|
88
|
+
const futureSpeed = Math.max(currentSpeed + acceleration * timeIntervalMs, 0);
|
|
89
|
+
return futureSpeed;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 转换速度从 m/ms 转成 km/h
|
|
93
|
+
* @param speed
|
|
94
|
+
* @returns
|
|
95
|
+
*/ export function transformSpeed(speed) {
|
|
96
|
+
// speed m/ms km/s
|
|
97
|
+
return speed * 60 * 60; // km/h
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 预估未来的位置
|
|
101
|
+
* @param lastPosition
|
|
102
|
+
* @param speed
|
|
103
|
+
* @param angle
|
|
104
|
+
* @param time
|
|
105
|
+
* @returns
|
|
106
|
+
*/ export function predictFuturePosition(lastPosition, speed, angle, time) {
|
|
107
|
+
// 将角度转换为弧度
|
|
108
|
+
const angleInRadians = angle * (Math.PI / 180);
|
|
109
|
+
const distance = speed * time;
|
|
110
|
+
console.log("angle speed", speed, distance);
|
|
111
|
+
const vec = new Vector2(lastPosition[0], lastPosition[1]);
|
|
112
|
+
// 计算角度在x和y方向的分量
|
|
113
|
+
const vX = Math.cos(angleInRadians);
|
|
114
|
+
const vY = Math.sin(angleInRadians);
|
|
115
|
+
const dir = new Vector2(vX, vY);
|
|
116
|
+
vec.add(dir.normalize().multiplyScalar(distance));
|
|
117
|
+
// // 计算未来位置
|
|
118
|
+
// const futureX = lastPosition[0] + vX * time;
|
|
119
|
+
// const futureY = lastPosition[1] + vY * time;
|
|
120
|
+
return [
|
|
121
|
+
vec.x,
|
|
122
|
+
vec.y
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 过滤偏差大的值
|
|
127
|
+
* @param data
|
|
128
|
+
* @returns
|
|
129
|
+
*/ export function removeOutliers(data) {
|
|
130
|
+
// 排序数据
|
|
131
|
+
const d = [
|
|
132
|
+
...data
|
|
133
|
+
].sort((a, b)=>a - b);
|
|
134
|
+
// 计算四分位数 Q1 和 Q3
|
|
135
|
+
const q1 = percentile(d, 25);
|
|
136
|
+
const q3 = percentile(d, 75);
|
|
137
|
+
// 计算四分位距 IQR
|
|
138
|
+
const iqr = q3 - q1;
|
|
139
|
+
// 计算上下界限
|
|
140
|
+
const lowerBound = q1 - 1.5 * iqr;
|
|
141
|
+
const upperBound = q3 + 1.5 * iqr;
|
|
142
|
+
// 筛选出正常范围内的数据
|
|
143
|
+
const filteredData = d.filter((value)=>value >= lowerBound && value <= upperBound);
|
|
144
|
+
const originSortData = data.filter((item)=>filteredData.includes(item));
|
|
145
|
+
return originSortData;
|
|
146
|
+
}
|
|
147
|
+
// 计算百分位数(分位数)
|
|
148
|
+
function percentile(arr, p) {
|
|
149
|
+
const index = p / 100 * (arr.length - 1);
|
|
150
|
+
const lower = Math.floor(index);
|
|
151
|
+
const upper = Math.ceil(index);
|
|
152
|
+
if (lower === upper) {
|
|
153
|
+
return arr[lower];
|
|
154
|
+
}
|
|
155
|
+
const weight = index - lower;
|
|
156
|
+
return arr[lower] * (1 - weight) + arr[upper] * weight;
|
|
157
|
+
}
|
package/lib/src/plugins/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BMap } from "../../bmap";
|
|
2
2
|
import { Plugin } from "../base";
|
|
3
3
|
import { Path, PathConfig } from "./path";
|
|
4
|
-
import { PathData2, TweenUtil,
|
|
4
|
+
import { PathData2, TweenUtil, RoadData2 } from "../../utils";
|
|
5
5
|
import { Floor, Poi2, PoiOptions2 } from "../../elements";
|
|
6
6
|
import { Tween } from "@tweenjs/tween.js";
|
|
7
7
|
import { type NavigationProcessInfo, PositionNavigation, type PositionNavigationConfig } from "./position-navigation";
|
|
@@ -24,7 +24,7 @@ interface EventMap {
|
|
|
24
24
|
info: NavigationProcessInfo;
|
|
25
25
|
};
|
|
26
26
|
"add-path": {
|
|
27
|
-
paths: PathData2
|
|
27
|
+
paths: PathData2;
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
export interface NavigationConfig {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventDispatcher } from "three";
|
|
2
2
|
import { Navigation } from "./navigation";
|
|
3
|
-
import {
|
|
3
|
+
import { PathData2, PathDirection, RoadData2, Timer, TweenUtil } from "../../utils";
|
|
4
4
|
import { Tween } from "@tweenjs/tween.js";
|
|
5
5
|
import { DebouncedFuncLeading } from 'lodash';
|
|
6
6
|
export interface PositionNavigationConfig {
|
|
@@ -16,6 +16,7 @@ export interface NavigationProcessInfo {
|
|
|
16
16
|
dir: PathDirection;
|
|
17
17
|
pos: [number, number];
|
|
18
18
|
routeIndex: number;
|
|
19
|
+
crossName: string;
|
|
19
20
|
offset: boolean;
|
|
20
21
|
reset: boolean;
|
|
21
22
|
}
|
|
@@ -31,7 +32,7 @@ export declare class PositionNavigation extends EventDispatcher<PositionNavigati
|
|
|
31
32
|
position: [number, number];
|
|
32
33
|
routeIndex: number;
|
|
33
34
|
pointIndex: number;
|
|
34
|
-
paths:
|
|
35
|
+
paths: PathData2;
|
|
35
36
|
options: PositionNavigationConfig;
|
|
36
37
|
timer: Timer;
|
|
37
38
|
resetTimer: number | null;
|
|
@@ -41,15 +42,15 @@ export declare class PositionNavigation extends EventDispatcher<PositionNavigati
|
|
|
41
42
|
roadData: RoadData2[];
|
|
42
43
|
paused: boolean;
|
|
43
44
|
constructor(navigation: Navigation, options: Partial<PositionNavigationConfig>, roadData: RoadData2[]);
|
|
44
|
-
get curRoutePath():
|
|
45
|
+
get curRoutePath(): PathData2[0] | null;
|
|
45
46
|
get curRoutePathPoints(): [number, number][];
|
|
46
47
|
registryEvent(): void;
|
|
47
48
|
unRegistryEvent(): void;
|
|
48
49
|
onAddPaths: ({ paths }: {
|
|
49
|
-
paths:
|
|
50
|
+
paths: PathData2;
|
|
50
51
|
}) => void;
|
|
51
52
|
onUpdate: () => void;
|
|
52
|
-
resetStatus(paths:
|
|
53
|
+
resetStatus(paths: PathData2): void;
|
|
53
54
|
emitNavigationStatus(): void;
|
|
54
55
|
adsorb(floor: string, _position: [number, number]): {
|
|
55
56
|
distance: number;
|
|
@@ -72,6 +73,7 @@ export declare class PositionNavigation extends EventDispatcher<PositionNavigati
|
|
|
72
73
|
getNextDirDistance(): {
|
|
73
74
|
dir: PathDirection;
|
|
74
75
|
distance: number;
|
|
76
|
+
crossName: string;
|
|
75
77
|
} | null;
|
|
76
78
|
private getNavigationInfo;
|
|
77
79
|
dispose(): void;
|
|
@@ -249,12 +249,14 @@ export class PositionNavigation extends EventDispatcher {
|
|
|
249
249
|
}
|
|
250
250
|
// 计算下一个拐弯点的方向和距离
|
|
251
251
|
getNextDirDistance() {
|
|
252
|
+
var _curRoutePath_pointInfos_index;
|
|
252
253
|
if (!this.position) {
|
|
253
254
|
return null;
|
|
254
255
|
}
|
|
255
|
-
const { pointIndex, curRoutePathPoints, position } = this;
|
|
256
|
+
const { pointIndex, curRoutePathPoints, position, curRoutePath } = this;
|
|
256
257
|
let index = pointIndex;
|
|
257
258
|
let dir = PathDirection.FRONT;
|
|
259
|
+
let crossName = (curRoutePath == null ? void 0 : (_curRoutePath_pointInfos_index = curRoutePath.pointInfos[index]) == null ? void 0 : _curRoutePath_pointInfos_index.crossName) || "";
|
|
258
260
|
let distance = 0;
|
|
259
261
|
let t = Date.now(); // 循环控制在5ms内,防止死循环
|
|
260
262
|
while(dir === PathDirection.FRONT && distance < this.options.directionEmitThreshold && Date.now() - t < 5){
|
|
@@ -276,6 +278,8 @@ export class PositionNavigation extends EventDispatcher {
|
|
|
276
278
|
dir = PathDirection.END;
|
|
277
279
|
} else {
|
|
278
280
|
dir = calc_direction(p1, p2, p3);
|
|
281
|
+
var _curRoutePath_pointInfos__crossName;
|
|
282
|
+
crossName = (_curRoutePath_pointInfos__crossName = curRoutePath == null ? void 0 : curRoutePath.pointInfos[index + 1].crossName) != null ? _curRoutePath_pointInfos__crossName : "";
|
|
279
283
|
index += 1;
|
|
280
284
|
}
|
|
281
285
|
}
|
|
@@ -285,7 +289,8 @@ export class PositionNavigation extends EventDispatcher {
|
|
|
285
289
|
if (dir === PathDirection.END && distance <= this.options.directionEmitThreshold) {
|
|
286
290
|
return {
|
|
287
291
|
dir,
|
|
288
|
-
distance
|
|
292
|
+
distance,
|
|
293
|
+
crossName
|
|
289
294
|
};
|
|
290
295
|
}
|
|
291
296
|
// 其他情况下判断距离起点的距离,如果是 <= 5米 就是start
|
|
@@ -293,7 +298,8 @@ export class PositionNavigation extends EventDispatcher {
|
|
|
293
298
|
if (distanceToStart <= 5) {
|
|
294
299
|
return {
|
|
295
300
|
dir: PathDirection.START,
|
|
296
|
-
distance
|
|
301
|
+
distance,
|
|
302
|
+
crossName
|
|
297
303
|
};
|
|
298
304
|
}
|
|
299
305
|
// 其他情况根据拐弯距离判断是不是直行
|
|
@@ -302,7 +308,8 @@ export class PositionNavigation extends EventDispatcher {
|
|
|
302
308
|
}
|
|
303
309
|
return {
|
|
304
310
|
dir,
|
|
305
|
-
distance
|
|
311
|
+
distance,
|
|
312
|
+
crossName
|
|
306
313
|
};
|
|
307
314
|
}
|
|
308
315
|
getNavigationInfo() {
|
|
@@ -314,7 +321,7 @@ export class PositionNavigation extends EventDispatcher {
|
|
|
314
321
|
if (!nextDirInfo) {
|
|
315
322
|
return null;
|
|
316
323
|
}
|
|
317
|
-
let { dir, distance: nextDirDistance } = nextDirInfo;
|
|
324
|
+
let { dir, distance: nextDirDistance, crossName } = nextDirInfo;
|
|
318
325
|
if (dir === PathDirection.END && nextDirDistance > 15) {
|
|
319
326
|
// 请直行
|
|
320
327
|
dir = PathDirection.FRONT;
|
|
@@ -330,6 +337,7 @@ export class PositionNavigation extends EventDispatcher {
|
|
|
330
337
|
dir,
|
|
331
338
|
pos: this.position,
|
|
332
339
|
routeIndex: this.routeIndex,
|
|
340
|
+
crossName,
|
|
333
341
|
offset: this.offset,
|
|
334
342
|
reset: this.reset
|
|
335
343
|
};
|