feat 对齐数据

This commit is contained in:
Ironsp
2026-09-17 16:26:30 +08:00
parent c1d2958095
commit faf4ea1155
5 changed files with 82 additions and 60 deletions

View File

@@ -1,12 +1,14 @@
/** 热力图图层与色阶工具 */
/** 热力图图层与色阶工具(字段与 RtkTrack / MockRtkPoint 对齐) */
export type HeatmapLayerKey = 'compaction' | 'speed' | 'temperature' | 'passCount'
import type { MockRtkPoint } from '@/mock/rtkTrack'
export type HeatmapLayerKey = 'compaction_value' | 'speed' | 'steel_wheel_temp' | 'pass_count'
export const HEATMAP_LAYERS: { key: HeatmapLayerKey, label: string }[] = [
{ key: 'compaction', label: '压实度' },
{ key: 'compaction_value', label: '压实度' },
{ key: 'speed', label: '压实速度' },
{ key: 'temperature', label: '温度' },
{ key: 'passCount', label: '碾压遍数' },
{ key: 'steel_wheel_temp', label: '温度' },
{ key: 'pass_count', label: '碾压遍数' },
]
/** 色阶阈值:0–90 绿,90–95 黄,95–100 红 */
@@ -22,46 +24,37 @@ export function colorByScore(score: number): string {
}
/** 各图层原始值映射到 0–100 分,统一用 90/95 分色 */
export function layerScore(
layer: HeatmapLayerKey,
point: { compaction: number, speed: number, temperature: number, passCount: number },
): number {
export function layerScore(layer: HeatmapLayerKey, point: MockRtkPoint): number {
switch (layer) {
case 'compaction':
return point.compaction
case 'compaction_value':
return point.compaction_value
case 'speed': {
// 目标约 4~5 km/h 偏优,偏快偏慢降分
const ideal = 4.2
const diff = Math.abs(point.speed - ideal)
return Math.max(0, Math.min(100, 100 - diff * 18))
}
case 'temperature': {
// 目标约 150℃
case 'steel_wheel_temp': {
const ideal = 150
const diff = Math.abs(point.temperature - ideal)
const diff = Math.abs(point.steel_wheel_temp - ideal)
return Math.max(0, Math.min(100, 100 - diff * 2.5))
}
case 'passCount':
// 目标遍数 6
return Math.max(0, Math.min(100, (point.passCount / 6) * 100))
case 'pass_count':
return Math.max(0, Math.min(100, (point.pass_count / 6) * 100))
default:
return 0
}
}
export function layerRawText(
layer: HeatmapLayerKey,
point: { compaction: number, speed: number, temperature: number, passCount: number },
): string {
export function layerRawText(layer: HeatmapLayerKey, point: MockRtkPoint): string {
switch (layer) {
case 'compaction':
return `${point.compaction}%`
case 'compaction_value':
return `${point.compaction_value}%`
case 'speed':
return `${point.speed}km/h`
case 'temperature':
return `${point.temperature}℃`
case 'passCount':
return `${point.passCount}遍`
case 'steel_wheel_temp':
return `${point.steel_wheel_temp}℃`
case 'pass_count':
return `${point.pass_count}遍`
default:
return '-'
}

View File

@@ -56,7 +56,7 @@ const emit = defineEmits<{
压实度
</text>
<text class="value">
{{ point.compaction }}%
{{ point.compaction_value }}%
</text>
</view>
<view class="row">
@@ -64,7 +64,7 @@ const emit = defineEmits<{
温度
</text>
<text class="value">
{{ point.temperature }}℃
{{ point.steel_wheel_temp }}℃
</text>
</view>
<view class="row">
@@ -72,7 +72,15 @@ const emit = defineEmits<{
碾压遍数
</text>
<text class="value">
{{ point.passCount }}遍
{{ point.pass_count }}遍
</text>
</view>
<view class="row">
<text class="label">
合格率
</text>
<text class="value">
{{ point.qualified_rate }}%
</text>
</view>

View File

@@ -142,7 +142,7 @@ function draw() {
ctx.setLineWidth(ROLLER_WIDTH)
for (let i = 1; i < coords.length; i++) {
ctx.beginPath()
ctx.setStrokeStyle(compactionColor(path[i].compaction))
ctx.setStrokeStyle(compactionColor(path[i].compaction_value))
ctx.moveTo(coords[i - 1].x, coords[i - 1].y)
ctx.lineTo(coords[i].x, coords[i].y)
ctx.stroke()

View File

@@ -1,16 +1,25 @@
/**
* RTK 轨迹假数据:蛇形碾压路径 + 持续推送
* 字段:经纬度、航向、压实度、速度、温度、碾压遍数
* 字段与 `@/service/rtkTrack` 的 RtkTrack 对齐
*/
import type { RtkTrack } from '@/service/rtkTrack'
export interface MockRtkPoint {
latitude: number // 纬度
longitude: number // 经度
heading: number // 航向角(度,0=北,顺时针)
compaction: number // 压实度 %
speed: number // 压实速度 km/h
temperature: number // 温度 ℃
passCount: number // 碾压遍数
/** 假数据点:与 rtk_track 表结构一致(坐标类字段必填便于绘制) */
export type MockRtkPoint = Required<
Pick<
RtkTrack,
| 'latitude'
| 'longitude'
| 'speed'
| 'heading'
| 'steel_wheel_temp'
| 'compaction_value'
| 'pass_count'
| 'qualified_rate'
>
> & {
work_record_id?: number
created_at?: string
}
export interface MockRtkStreamOptions {
@@ -18,14 +27,15 @@ export interface MockRtkStreamOptions {
intervalMs?: number
/** 结束后是否循环,默认 true */
loop?: boolean
/** 横向行数,默认 80(约 10 倍于早期 8 行) */
/** 横向行数,默认 80 */
rows?: number
/** 每行采样点数,默认 40 */
pointsPerRow?: number
/** 关联作业记录 id,默认 1 */
workRecordId?: number
}
export interface MockRtkStream {
/** 完整路径(用于画布定界) */
readonly points: MockRtkPoint[]
start: (onPoint: (point: MockRtkPoint, index: number) => void) => void
stop: () => void
@@ -33,12 +43,11 @@ export interface MockRtkStream {
getIndex: () => number
}
/** 航向归一化到 [0, 360) */
function normalizeHeading(deg: number) {
return ((deg % 360) + 360) % 360
}
function makeCompaction(seed: number) {
function makeCompactionValue(seed: number) {
const wave = Math.sin(seed / 4) * 4
return Number(Math.min(98, Math.max(78, 88 + wave + (Math.random() - 0.5) * 3)).toFixed(1))
}
@@ -47,7 +56,7 @@ function makeSpeed(seed: number) {
return Number((3.2 + Math.sin(seed / 5) * 1.2 + Math.random() * 0.8).toFixed(1))
}
function makeTemperature(seed: number) {
function makeSteelWheelTemp(seed: number) {
return Number((148 + Math.sin(seed / 7) * 8 + (Math.random() - 0.5) * 6).toFixed(0))
}
@@ -55,27 +64,36 @@ function makePassCount(seed: number) {
return Math.max(1, Math.min(8, Math.round(2 + (seed % 9) / 2 + Math.random())))
}
function makeQualifiedRate(compaction: number) {
return Number(Math.min(100, Math.max(70, compaction - 2 + (Math.random() - 0.5) * 4)).toFixed(1))
}
function nowDateTime() {
return new Date().toISOString().replace('T', ' ').slice(0, 19)
}
function buildPoint(
lat: number,
lon: number,
heading: number,
seed: number,
workRecordId: number,
): MockRtkPoint {
const compaction_value = makeCompactionValue(seed)
return {
work_record_id: workRecordId,
latitude: Number(lat.toFixed(7)),
longitude: Number(lon.toFixed(7)),
heading,
compaction: makeCompaction(seed),
speed: makeSpeed(seed),
temperature: makeTemperature(seed),
passCount: makePassCount(seed),
heading,
steel_wheel_temp: makeSteelWheelTemp(seed),
compaction_value,
pass_count: makePassCount(seed),
qualified_rate: makeQualifiedRate(compaction_value),
created_at: nowDateTime(),
}
}
/**
* 行末三角掉头:行末 → 外侧顶点 → 下行起点
* 航向先转 60°(到顶点),再转 120°(合计 180° 衔接下行)
*/
function pushUTurnTriangle(
points: MockRtkPoint[],
startLat: number,
@@ -84,6 +102,7 @@ function pushUTurnTriangle(
latStep: number,
eastward: boolean,
seedRef: { value: number },
workRecordId: number,
) {
const nextLat = startLat + latStep
const midLat = startLat + latStep / 2
@@ -99,19 +118,22 @@ function pushUTurnTriangle(
tipLon,
normalizeHeading(fromHeading + 60 * turnSign),
seedRef.value++,
workRecordId,
))
points.push(buildPoint(
nextLat,
startLon,
normalizeHeading(fromHeading + 180 * turnSign),
seedRef.value++,
workRecordId,
))
}
/** 生成类似示意图的左右蛇形碾压路径 */
/** 生成蛇形碾压路径假数据 */
export function generateSerpentineTrack(options: MockRtkStreamOptions = {}): MockRtkPoint[] {
const rows = options.rows ?? 80
const pointsPerRow = options.pointsPerRow ?? 40
const workRecordId = options.workRecordId ?? 1
const originLat = 39.0425
const originLon = 115.8620
@@ -129,13 +151,13 @@ export function generateSerpentineTrack(options: MockRtkStreamOptions = {}): Moc
for (let col = 0; col < pointsPerRow; col++) {
const t = eastward ? col : (pointsPerRow - 1 - col)
const lon = originLon + t * lonStep
points.push(buildPoint(lat, lon, heading, seedRef.value++))
points.push(buildPoint(lat, lon, heading, seedRef.value++, workRecordId))
}
if (row < rows - 1) {
const endT = eastward ? (pointsPerRow - 1) : 0
const endLon = originLon + endT * lonStep
pushUTurnTriangle(points, lat, endLon, heading, latStep, eastward, seedRef)
pushUTurnTriangle(points, lat, endLon, heading, latStep, eastward, seedRef, workRecordId)
}
}
@@ -175,8 +197,7 @@ export function createMockRtkStream(options: MockRtkStreamOptions = {}): MockRtk
return
}
}
const point = points[index]
onPoint(point, index)
onPoint(points[index], index)
index += 1
}, intervalMs)
}

View File

@@ -10,7 +10,7 @@ definePage({
},
})
const layer = ref<HeatmapLayerKey>('compaction')
const layer = ref<HeatmapLayerKey>('compaction_value')
const layerVisible = ref(false)
const detailVisible = ref(false)
const selectedPoint = ref<MockRtkPoint | null>(null)