(null);
- // Сегменты трека: LBS-участки (хотя бы одна точка сегмента — по вышкам)
- // рисуем серым пунктиром и тоньше, остальные — по скорости начальной точки.
- // При > 800 точек прореживаем до ~800 (базовая полилиния из всех точек
- // остаётся под низом). 800 сегментов оба провайдера тянут нормально;
- // если Яндекс просядет — порог снизить до 400.
+ // Сегменты трека, окрашенные по скорости в начальной точке сегмента —
+ // только пары GPS↔GPS (LBS-точки линиями не соединяются никогда).
+ // При > 800 GPS-точек прореживаем до ~800 для раскраски (базовые холсты
+ // из всех GPS-точек остаются под низом). 800 сегментов оба провайдера
+ // тянут нормально; если Яндекс просядет — порог снизить до 400.
const COLORIZE_POINTS_LIMIT = 800;
- const { speedSegments, lbsSegments } = useMemo(() => {
- const speed: GpsMapPolyline[] = [];
- const lbs: GpsMapPolyline[] = [];
- if (activePoints.length < 2) return { speedSegments: speed, lbsSegments: lbs };
- const step = Math.max(1, Math.ceil(activePoints.length / COLORIZE_POINTS_LIMIT));
- const pts = step === 1 ? activePoints : activePoints.filter((_, idx) => idx % step === 0);
+ const speedSegments: GpsMapPolyline[] = useMemo(() => {
+ if (gpsPoints.length < 2) return [];
+ const step = Math.max(1, Math.ceil(gpsPoints.length / COLORIZE_POINTS_LIMIT));
+ const pts = step === 1 ? gpsPoints : gpsPoints.filter((_, idx) => idx % step === 0);
+ const segments: GpsMapPolyline[] = [];
for (let i = 1; i < pts.length; i++) {
- const pair: Array<[number, number]> = [
- [pts[i - 1].lat, pts[i - 1].lng],
- [pts[i].lat, pts[i].lng],
- ];
- if (pts[i - 1].isLbs || pts[i].isLbs) {
- lbs.push({ id: `lbs-${i}`, points: pair, color: SPEED_UNKNOWN_COLOR, dashed: true, width: 3 });
- } else {
- speed.push({ id: `seg-${i}`, points: pair, color: speedColor(pts[i - 1].speed) });
- }
+ segments.push({
+ id: `seg-${i}`,
+ points: [
+ [pts[i - 1].lat, pts[i - 1].lng],
+ [pts[i].lat, pts[i].lng],
+ ],
+ color: speedColor(pts[i - 1].speed),
+ });
}
- return { speedSegments: speed, lbsSegments: lbs };
- }, [activePoints]);
+ return segments;
+ }, [gpsPoints]);
// Линия по дорогам (OSRM) поверх раскраски: сплошная, цвет объекта, толще
+ const matched = wantMatched ? matchedTrackData?.matched : undefined;
const matchedLine: GpsMapPolyline[] =
- matchRoads && trackData?.matched && trackData.matched.points.length >= 2
+ matched && matched.points.length >= 2
? [
{
id: 'matched',
- points: trackData.matched.points,
+ points: matched.points,
color: selectedAsset?.color || '#3b82f6',
width: 5,
},
]
: [];
- // Данные для провайдер-агностичной карты: полилиния трека + маркеры старта/финиша/плейхеда + точки записи.
- // При включённой раскраске: базовая полилиния из всех точек под низом,
- // сверху — сегменты, окрашенные по скорости (с прореживанием до 800 точек);
- // LBS-участки (по вышкам) — всегда серым пунктиром поверх;
+ // Данные для провайдер-агностичной карты: холсты трека из GPS-точек +
+ // маркеры старта/финиша/плейхеда + точки записи (LBS — полые кружки).
+ // При включённой раскраске: холсты под низом серым, сверху — сегменты,
+ // окрашенные по скорости (с прореживанием до 800 точек);
// matched-линия по дорогам — верхним слоем
- const colorizeEnabled = colorize && activePoints.length >= 2;
- const mapPolylines: GpsMapPolyline[] =
- trackCoords.length >= 2
- ? [
- {
- id: 'track',
- points: trackCoords,
- color: colorizeEnabled ? '#94a3b8' : selectedAsset?.color || '#3b82f6',
- },
- ...(colorizeEnabled ? speedSegments : []),
- ...lbsSegments,
- ...matchedLine,
- ]
- : matchedLine;
+ const colorizeEnabled = colorize && gpsPoints.length >= 2;
+ const mapPolylines: GpsMapPolyline[] = [
+ ...trackCanvases.map((pts, idx) => ({
+ id: `track-${idx}`,
+ points: pts,
+ color: colorizeEnabled ? '#94a3b8' : selectedAsset?.color || '#3b82f6',
+ })),
+ ...(colorizeEnabled ? speedSegments : []),
+ ...matchedLine,
+ ];
const mapMarkers: GpsMapMarker[] = [
- // Точки записи координат (стрелки курса с попапом; в кластер не включаем;
+ // Точки записи координат (стрелки курса для GPS-точек; LBS — полые серые
+ // кружки без стрелки: курс у LBS фейковый; в кластер не включаем;
// клик — попап + установка плейхеда проигрывания)
...displayedPoints.map((p) => ({
id: `tp-${p.id}`,
@@ -372,7 +421,8 @@ export function GpsRoutesTab() {
lng: p.lng,
kind: 'dot' as const,
color: '#64748b',
- course: p.course ?? undefined,
+ course: p.isLbs ? undefined : (p.course ?? undefined),
+ approximate: p.isLbs === true ? true : undefined,
onClick: () => seekToPoint(p),
noCluster: true,
zIndex: 100, // над полилиниями (matched/сегменты), иначе клики перехватывает линия
@@ -445,8 +495,10 @@ export function GpsRoutesTab() {
onClick={() => setSelectedTrip(idx)}
title={`${trip.points.length} точек`}
>
- Поездка {idx + 1} · {formatTime(trip.startTs)}–{formatTime(trip.endTs)} · {trip.distanceKm.toFixed(1)} км
- {trip.lbsDistanceKm > 0 && ` (+ ~${trip.lbsDistanceKm.toFixed(1)})`}
+ Поездка {idx + 1} · {formatTime(trip.startTs)}–{formatTime(trip.endTs)} ·{' '}
+ {trip.allLbs
+ ? '~по вышкам'
+ : `${trip.distanceKm.toFixed(1)} км${trip.lbsDistanceKm > 0 ? ` (+ ~${trip.lbsDistanceKm.toFixed(1)})` : ''}`}
))}
@@ -477,15 +529,17 @@ export function GpsRoutesTab() {
{selectedPoint.isLbs && (
Примерная позиция (по вышкам)
)}
- {selectedPoint.course != null && Направление: {Math.round(selectedPoint.course)}°
}
+ {selectedPoint.course != null && !selectedPoint.isLbs && (
+ Направление: {Math.round(selectedPoint.course)}°
+ )}
{selectedPoint.lat.toFixed(6)}, {selectedPoint.lng.toFixed(6)}
Плейхед установлен на эту точку
)}
- {/* Легенда раскраски трека по скорости + LBS-участки (пунктир) */}
- {(colorizeEnabled || lbsSegments.length > 0) && (
+ {/* Легенда раскраски трека по скорости + LBS-точки (полые кружки) */}
+ {(colorizeEnabled || hasLbsPoints) && (
{colorizeEnabled && (
<>
@@ -498,10 +552,13 @@ export function GpsRoutesTab() {
км/ч
>
)}
- {lbsSegments.length > 0 && (
+ {hasLbsPoints && (
-
- пунктир — нет GPS (по вышкам)
+
+ полый кружок — нет GPS (по вышкам)
)}
@@ -535,21 +592,26 @@ export function GpsRoutesTab() {
)}
- Пробег: {stats.distance.toFixed(1)} км
+ Пробег:{' '}
+ {stats.hasGpsPairs ? (
+ {stats.distance.toFixed(1)} км
+ ) : (
+ — (нет GPS-данных)
+ )}
{stats.lbsDistance > 0 && (
- {' '}+ ~{stats.lbsDistance.toFixed(1)} км по вышкам
+ {' '}+ ~{stats.lbsDistance.toFixed(1)} км по вышкам (недостоверно)
)}
- {trackData?.matched && (
+ {matched && (
Пробег по дорогам:{' '}
- {(trackData.matched.distance / 1000).toFixed(1)} км
+ {(matched.distance / 1000).toFixed(1)} км
)}