From 3d24a15fb30ece42f5360db39b3db43b5412156f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=D1=81=20=D0=A1=D1=83=D0=BB=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2?= Date: Tue, 25 Aug 2026 17:49:38 +0300 Subject: [PATCH] =?UTF-8?q?fix(gps):=20=D0=BE=D1=82=D1=81=D0=B5=D0=B2=20?= =?UTF-8?q?=D1=83=D1=81=D1=82=D0=B0=D1=80=D0=B5=D0=B2=D1=88=D0=B8=D1=85=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BF=D0=B8=D0=B9=20=D0=B8=20LBS-=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D0=B5=D0=BF=D0=BE=D1=80=D1=82=D0=BE=D0=B2=20(stale/jump?= =?UTF-8?q?=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D1=8B),=20=D0=BD=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA=D0=B0=20maxJumpSpeedKmh,?= =?UTF-8?q?=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=B0=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20=D1=82=D1=80=D0=B5=D0=BA=D0=B0=20=D0=BF=D1=80=D0=B8=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=B0=D1=87=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/gps/RoutesTab.tsx | 6 ++ client/src/pages/gps/SettingsTab.tsx | 43 +++++++++-- client/src/pages/gps/types.ts | 2 + migrations/0071_gps_settings_jump.sql | 4 ++ server/gps/geozone.service.ts | 100 ++++++++++++++++++++++---- server/gps/routes.ts | 53 ++++++++++---- server/gps/storage.ts | 18 +++-- shared/schema.ts | 1 + 8 files changed, 189 insertions(+), 38 deletions(-) create mode 100644 migrations/0071_gps_settings_jump.sql diff --git a/client/src/pages/gps/RoutesTab.tsx b/client/src/pages/gps/RoutesTab.tsx index 12d9f31..a294620 100644 --- a/client/src/pages/gps/RoutesTab.tsx +++ b/client/src/pages/gps/RoutesTab.tsx @@ -332,6 +332,12 @@ export function GpsRoutesTab() { {trackData?.sampled && ` (всего ${trackData.total}, показана каждая N-я)`} {pointsThinned && `, маркеров на карте: ${displayedPoints.length}`} + {(trackData?.filteredCount ?? 0) > 0 && ( + + Отфильтровано мусорных точек:{' '} + {trackData!.filteredCount} + + )} Пробег: {stats.distance.toFixed(1)} км diff --git a/client/src/pages/gps/SettingsTab.tsx b/client/src/pages/gps/SettingsTab.tsx index e25f02a..e7da7ef 100644 --- a/client/src/pages/gps/SettingsTab.tsx +++ b/client/src/pages/gps/SettingsTab.tsx @@ -11,12 +11,13 @@ import { useToast } from '@/hooks/use-toast'; interface GpsSettings { success: boolean; jitterMeters: number; + maxJumpSpeedKmh: number; } /** * Таб «Настройки» GPS-модуля. - * Секция «Трекинг»: фильтр дрожания позиции (джиттер) — сервер не записывает - * точки ближе N метров при скорости < 5 км/ч. Сюда же позже добавятся + * Секция «Трекинг»: фильтры входящих точек — дрожание позиции (джиттер) + * и максимальный скачок скорости (LBS-прыжки). Сюда же позже добавятся * другие настройки модуля — структура секций-Card под это готова. */ export function GpsSettingsTab() { @@ -26,15 +27,22 @@ export function GpsSettingsTab() { }); const [jitterMeters, setJitterMeters] = useState('0'); + const [maxJumpSpeedKmh, setMaxJumpSpeedKmh] = useState('0'); useEffect(() => { - if (data) setJitterMeters(String(data.jitterMeters)); + if (data) { + setJitterMeters(String(data.jitterMeters)); + setMaxJumpSpeedKmh(String(data.maxJumpSpeedKmh)); + } }, [data]); const saveMutation = useMutation({ mutationFn: async () => { - const value = Number(jitterMeters); - const res = await apiRequest('PUT', '/api/gps/settings', { jitterMeters: value }); + // Оба поля сохраняются одним PUT (непереданные на сервере сохраняются) + const res = await apiRequest('PUT', '/api/gps/settings', { + jitterMeters: Number(jitterMeters), + maxJumpSpeedKmh: Number(maxJumpSpeedKmh), + }); return res.json(); }, onSuccess: () => { @@ -49,7 +57,9 @@ export function GpsSettingsTab() { }, }); - const jitterValid = /^\d+$/.test(jitterMeters.trim()) && Number(jitterMeters) >= 0 && Number(jitterMeters) <= 1000; + const isIntInRange = (v: string, max: number) => /^\d+$/.test(v.trim()) && Number(v) >= 0 && Number(v) <= max; + const jitterValid = isIntInRange(jitterMeters, 1000); + const jumpValid = isIntInRange(maxJumpSpeedKmh, 5000); return (
@@ -84,10 +94,29 @@ export function GpsSettingsTab() {

Целое число от 0 до 1000

)}
+ +
+ + setMaxJumpSpeedKmh(e.target.value)} + /> +

+ 0 = выключен. Точки, требующие перемещения быстрее N км/ч от предыдущей, + отбрасываются — отсекает LBS-прыжки на вышки при потере GPS. Действует на новые точки. +

+ {!jumpValid && ( +

Целое число от 0 до 5000

+ )} +
+