@codewithvincent/react-native-gps-filter
Version:
Kalman-based adaptive GPS filter for React Native.
120 lines (119 loc) • 5.75 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useLocationStore = void 0;
//@ts-ignore
const zustand_1 = require("zustand"); //@ts-ignore
const kalmanjs_1 = __importDefault(require("kalmanjs"));
const locationFilters_1 = require("../utils/locationFilters");
const helpers_1 = require("../utils/helpers");
const MIN_DISTANCE_FOR_ROUTE_DRAWING = 10; // meters, adjust as needed
//@ts-ignore
exports.useLocationStore = (0, zustand_1.create)((set, get) => ({
kalmanFilterLat: new kalmanjs_1.default(locationFilters_1.kalmanFilterOptions),
kalmanFilterLon: new kalmanjs_1.default(locationFilters_1.kalmanFilterOptions),
consecutiveRejectCount: 0,
totalDistanceTraveled: 0,
lastAcceptedPredictedLocation: null,
currentSpeed: 0,
runStartTimeInMillis: Date.now(),
routeCoordinates: [],
//@ts-ignore
filterAndAddLocation(location, onPredictLocation) {
const now = Date.now();
const age = now - location.timestamp;
if (age > 10000)
return { result: false, reason: "old" }; //@ts-ignore
if (location.coords.accuracy <= 0)
return { result: false, reason: "no_accuracy" }; //@ts-ignore
if (location.coords.accuracy > 100)
return { result: false, reason: "inaccurate" };
const state = get();
const speed = location.coords.speed || 0;
const { minDelta, maxDelta, maxAllowedDelta, mode } = (0, locationFilters_1.getAdaptiveThresholds)(speed);
const isMoving = speed > locationFilters_1.GPS_SPEED_THRESHOLD;
if (!state.lastAcceptedPredictedLocation) {
state.kalmanFilterLat.filter(location.coords.latitude);
state.kalmanFilterLon.filter(location.coords.longitude);
set({ lastAcceptedPredictedLocation: location });
return {
result: true,
acceptedLocation: location,
predictedLocation: location,
distanceTraveled: 0,
};
}
const predictedLat = state.kalmanFilterLat.filter(location.coords.latitude);
const predictedLon = state.kalmanFilterLon.filter(location.coords.longitude);
const predictedDelta = (0, helpers_1.getDistanceFromLatLonInMeters)(predictedLat, predictedLon, location.coords.latitude, location.coords.longitude);
if (predictedDelta > maxAllowedDelta) {
const rejectCount = state.consecutiveRejectCount + 1;
if (rejectCount > locationFilters_1.KALMAN_REJECT_THRESHOLD) {
set({
kalmanFilterLat: new kalmanjs_1.default(locationFilters_1.kalmanFilterOptions),
kalmanFilterLon: new kalmanjs_1.default(locationFilters_1.kalmanFilterOptions),
consecutiveRejectCount: 0,
});
}
else {
set({ consecutiveRejectCount: rejectCount });
}
return { result: false, reason: `kalman_reject_${mode}` };
}
else {
set({ consecutiveRejectCount: 0 });
}
const predictedLocation = Object.assign(Object.assign({}, location), { coords: Object.assign(Object.assign({}, location.coords), { latitude: predictedLat, longitude: predictedLon }) });
const lastAccepted = state.lastAcceptedPredictedLocation;
const distanceSinceLast = (0, helpers_1.getDistanceFromLatLonInMeters)(predictedLat, predictedLon, lastAccepted.coords.latitude, lastAccepted.coords.longitude);
if (isMoving &&
distanceSinceLast > minDelta &&
distanceSinceLast < maxDelta) {
const newTotalDistance = state.totalDistanceTraveled + distanceSinceLast;
const routeCoordinates = [...state.routeCoordinates];
const lastPointInRoute = routeCoordinates.length
? routeCoordinates[routeCoordinates.length - 1]
: null;
if (!lastPointInRoute ||
(0, helpers_1.getDistanceFromLatLonInMeters)(lastPointInRoute[1], lastPointInRoute[0], predictedLocation.coords.latitude, predictedLocation.coords.longitude) >= MIN_DISTANCE_FOR_ROUTE_DRAWING) {
routeCoordinates.push([
predictedLocation.coords.longitude,
predictedLocation.coords.latitude,
]);
console.log("Adding significant point to route:", routeCoordinates.slice(-1)[0]);
}
set({
totalDistanceTraveled: newTotalDistance,
lastAcceptedPredictedLocation: predictedLocation,
currentSpeed: speed,
routeCoordinates,
});
if (onPredictLocation) {
onPredictLocation(predictedLocation);
}
return {
result: true,
acceptedLocation: location,
predictedLocation,
distanceTraveled: newTotalDistance,
};
}
else {
return { result: false, reason: `distance_too_small_or_noise_${mode}` };
}
},
resetFilters() {
set({
kalmanFilterLat: new kalmanjs_1.default(locationFilters_1.kalmanFilterOptions),
kalmanFilterLon: new kalmanjs_1.default(locationFilters_1.kalmanFilterOptions),
consecutiveRejectCount: 0,
totalDistanceTraveled: 0,
lastAcceptedPredictedLocation: null,
currentSpeed: 0,
runStartTimeInMillis: Date.now(),
routeCoordinates: [],
});
},
}));