UNPKG

gisviewer-vue3-arcgis

Version:

在应用中引入私服的包, 项目根目录下 新建文件 .npmrc 在 .npmrc 中对包源进行配置:

216 lines (215 loc) 8.63 kB
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 };