gisviewer-vue3-arcgis
Version:
在应用中引入私服的包, 项目根目录下 新建文件 .npmrc 在 .npmrc 中对包源进行配置:
216 lines (215 loc) • 8.63 kB
JavaScript
import { lineString as v } from "@turf/helpers";
import y from "@turf/length";
class S {
/**
* 计算要素集合的包围盒范围(extent)
* @param features 要素数组
* @returns [minX, minY, maxX, maxY],无有效几何时返回 undefined
*/
static getFeaturesExtent(t) {
var o;
let s = 1 / 0, n = 1 / 0, r = -1 / 0, i = -1 / 0;
for (const c of t) {
const a = (o = c.getGeometry()) == null ? void 0 : o.getExtent();
a && (s = Math.min(s, a[0]), n = Math.min(n, a[1]), r = Math.max(r, a[2]), i = Math.max(i, a[3]));
}
if (Number.isFinite(s))
return [s, n, r, i];
}
/**
* 根据车道中心线 shape 和宽度(米)计算多边形顶点。
* 将中心线按 width 向两侧法线方向偏移,形成封闭多边形。
*/
static laneToPolygon(t, s) {
if (t.length < 2)
return [];
const n = t[0][1], r = 111320, i = 111320 * Math.cos(n * Math.PI / 180), o = s / 2 / r, c = s / 2 / i, a = [], m = [];
for (let f = 0; f < t.length; f++) {
let T = 0, d = 0;
f < t.length - 1 ? (T = t[f + 1][0] - t[f][0], d = t[f + 1][1] - t[f][1]) : (T = t[f][0] - t[f - 1][0], d = t[f][1] - t[f - 1][1]);
const R = Math.sqrt(T * T + d * d) || 1, u = -d / R, l = T / R;
a.push([
t[f][0] + u * c,
t[f][1] + l * o
]), m.push([
t[f][0] - u * c,
t[f][1] - l * o
]);
}
return [...a, ...m.reverse(), a[0]];
}
/**
* 对车道中心线 shape 做均匀三次 B 样条平滑插值,生成更多采样点使线型平滑。
* 与 Catmull-Rom(强制经过所有点)不同,这里平滑度优先,
* 曲线不要求经过中间原始点,仅通过首尾端点以保证连接段与上下游道路衔接。
* @param shape 原始形状点(经纬度坐标)
* @param samplesPerSegment 每段插值采样点数,默认 10
* @returns 插值后的平滑点序列
*/
static smoothShape(t, s = 10) {
if (t.length < 3)
return [...t];
const n = [
t[0],
t[0],
t[0],
...t,
t[t.length - 1],
t[t.length - 1],
t[t.length - 1]
], r = [];
for (let o = 0; o < n.length - 3; o++) {
const c = n[o], a = n[o + 1], m = n[o + 2], f = n[o + 3];
for (let T = 0; T <= s; T++) {
const d = T / s, R = d * d, u = R * d, l = 1 - d, e = l * l * l / 6, b = (3 * u - 6 * R + 4) / 6, g = (-3 * u + 3 * R + 3 * d + 1) / 6, h = u / 6;
r.push([
e * c[0] + b * a[0] + g * m[0] + h * f[0],
e * c[1] + b * a[1] + g * m[1] + h * f[1]
]);
}
}
const i = [];
for (const o of r) {
const c = i[i.length - 1];
(!c || c[0] !== o[0] || c[1] !== o[1]) && i.push(o);
}
return i;
}
/**
* 创建交通信号灯样式图像
* @returns canvas 元素,包含绘制好的交通信号灯图像
*/
static createTrafficLightImage() {
const s = document.createElement("canvas");
s.width = 64, s.height = 64;
const n = s.getContext("2d");
if (!n)
throw new Error("Unable to create traffic light canvas context");
const r = 64 * 0.45, i = 64 * 0.82, o = (64 - r) / 2, c = (64 - i) / 2, a = 4;
n.fillStyle = "#2d2d2d", n.beginPath(), n.moveTo(o + a, c), n.lineTo(o + r - a, c), n.quadraticCurveTo(o + r, c, o + r, c + a), n.lineTo(o + r, c + i - a), n.quadraticCurveTo(
o + r,
c + i,
o + r - a,
c + i
), n.lineTo(o + a, c + i), n.quadraticCurveTo(o, c + i, o, c + i - a), n.lineTo(o, c + a), n.quadraticCurveTo(o, c, o + a, c), n.closePath(), n.fill();
const m = r * 0.2, f = i * 0.14, T = (i - f * 2 - m * 2 * 3) / 2, d = 64 / 2, R = ["#ef4444", "#f59e0b", "#22c55e"];
for (let u = 0; u < 3; u++) {
const l = c + f + m + u * (m * 2 + T);
n.beginPath(), n.arc(d, l, m, 0, Math.PI * 2), n.fillStyle = R[u], n.fill();
}
return s;
}
/**
* 由车道中心线 shape 计算车道导向箭头的绘制位置和角度
* 计算方法:
* 1. 使用 turf.length 计算车道中心线总长度(米)
* 2. 在距离车道终点(停止线)3 米处取精确位置,作为箭头位置
* 3. 计算该位置前后两点的方向向量,使用 atan2(dx, dy) 计算方位角(bearing)
* 4. 返回箭头位置和角度
* @param shape
* @returns
*/
static getLaneArrowPosition(t) {
if (t.length < 2)
return { point: t[0] || [0, 0], angle: 0 };
const s = y(v(t), { units: "meters" }), r = Math.max(s - 5, 0);
let i = 0;
const o = [];
for (let e = 1; e < t.length; e++) {
const b = t[e][0] - t[e - 1][0], g = t[e][1] - t[e - 1][1], h = Math.sqrt(b * b + g * g);
o.push(h), i += h;
}
const c = s === 0 ? 0 : r / s, a = i * c;
let m = 0, f = t[t.length - 1], T = t[t.length - 2], d = t[t.length - 1];
for (let e = 0; e < o.length; e++) {
if (m + o[e] >= a) {
const b = o[e] || 1, g = Math.min(Math.max((a - m) / b, 0), 1);
f = [
t[e][0] + g * (t[e + 1][0] - t[e][0]),
t[e][1] + g * (t[e + 1][1] - t[e][1])
], T = t[e], d = t[e + 1];
break;
}
m += o[e];
}
const R = d[0] - T[0], u = d[1] - T[1], l = Math.atan2(R, u) * (180 / Math.PI);
return { point: f, angle: l };
}
/**
* 在 Canvas 上绘制指定类型的转向箭头(道路标线风格:箭杆+箭头,朝上绘制)
*/
static drawArrowIcon(t, s, n) {
const r = n / 2, i = n * 0.88, o = n * 0.12, c = n * 0.08, a = n * 0.28, m = n * 0.22;
t.fillStyle = "#ffffff", t.strokeStyle = "#ffffff";
const f = (u, l) => {
const e = r + u, b = c * l, g = a * l, h = m * l, P = o + n * (1 - l) * 0.15, w = i - n * (1 - l) * 0.15, L = P + h;
t.fillRect(e - b / 2, L, b, w - L), t.beginPath(), t.moveTo(e, P), t.lineTo(e - g / 2, P + h), t.lineTo(e + g / 2, P + h), t.closePath(), t.fill();
}, T = (u, l) => {
const e = r + u, b = c * l, g = a * l * 0.8, h = m * l * 0.8, P = i - n * (1 - l) * 0.15, w = n * 0.45, L = e - n * 0.25 * l;
t.fillRect(e - b / 2, w, b, P - w), t.fillRect(L, w - b / 2, e - L, b), t.beginPath(), t.moveTo(L - h * 0.5, w), t.lineTo(L + h * 0.5, w - g / 2), t.lineTo(L + h * 0.5, w + g / 2), t.closePath(), t.fill();
}, d = (u, l) => {
const e = r + u, b = c * l, g = a * l * 0.8, h = m * l * 0.8, P = i - n * (1 - l) * 0.15, w = n * 0.45, L = e + n * 0.25 * l;
t.fillRect(e - b / 2, w, b, P - w), t.fillRect(e, w - b / 2, L - e, b), t.beginPath(), t.moveTo(L + h * 0.5, w), t.lineTo(L - h * 0.5, w - g / 2), t.lineTo(L - h * 0.5, w + g / 2), t.closePath(), t.fill();
}, R = () => {
const u = c, l = a * 0.7, e = m * 0.7, b = i, g = n * 0.35, h = n * 0.15;
t.fillRect(r + h - u / 2, g, u, b - g), t.lineWidth = u, t.beginPath(), t.arc(r, g, h, 0, Math.PI, !0), t.stroke();
const P = g + e;
t.beginPath(), t.moveTo(r - h, P), t.lineTo(r - h - l / 2, g), t.lineTo(r - h + l / 2, g), t.closePath(), t.fill();
};
switch (s) {
case "S":
f(0, 1);
break;
case "L":
T(0, 1);
break;
case "R":
d(0, 1);
break;
case "SL":
f(n * 0.13, 0.75), T(-n * 0.08, 0.75);
break;
case "SR":
f(-n * 0.13, 0.75), d(n * 0.08, 0.75);
break;
case "LR":
T(-n * 0.1, 0.75), d(n * 0.1, 0.75);
break;
case "SLR":
T(-n * 0.16, 0.65), f(0, 0.65), d(n * 0.16, 0.65);
break;
case "T":
R();
break;
case "default":
default:
f(0, 1);
break;
}
}
/**
* 创建转向箭头图标 canvas(根据车道功能类型)
* @param type 箭头类型
* @param size 图标尺寸(宽高,px),默认 64
* @returns 绘制好箭头的 canvas 元素
*/
static createArrowIcon(t, s = 64) {
const n = document.createElement("canvas");
n.width = s, n.height = s;
const r = n.getContext("2d");
if (!r)
throw new Error("Unable to create arrow icon canvas context");
return S.drawArrowIcon(r, t, s), n;
}
/**
* 根据车道已有连接方向判定应显示的箭头图标类型
* dirs 中的方向统一按大写处理,兼容 SUMO 返回的小写字符(如 's'/'l'/'r'/'t')
*/
static determineArrowType(t) {
const s = new Set(t.map((c) => c.toUpperCase())), n = s.has("S"), r = s.has("L"), i = s.has("R");
return s.has("T") && s.size === 1 ? "T" : n && r && i ? "SLR" : n && r ? "SL" : n && i ? "SR" : r && i ? "LR" : r ? "L" : i ? "R" : n ? "S" : "default";
}
}
export {
S as default
};