Skip to content

Commit

Permalink
Add filters.minAccuracy config option
Browse files Browse the repository at this point in the history
This allows us to ignore location points which do not meet the configured
accuracy requirement.

Closes #35.
  • Loading branch information
linusg committed May 11, 2020
1 parent bb87ec0 commit b76cbdc
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 27 deletions.
22 changes: 22 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ window.owntracks.config = {};
- [`baseUrl`](#apibaseurl)
- [`fetchOptions`](#apifetchoptions)
- [`endDateTime`](#enddatetime)
- `filters`
- [`minAccuracy`](#filtersminaccuracy)
- [`ignorePingLocation`](#ignorepinglocation)
- [`locale`](#locale)
- `map`
Expand Down Expand Up @@ -124,6 +126,26 @@ Initial end date and time (browser timezone) for fetched data.
};
```

### `filters.minAccuracy`

Minimum accuracy in meters for location points to be rendered & included in the travelled distance.

This filter is disabled by default as accuracies can vary across devices an locations, but you're
encouraged to set it as it can be a simple way to remove outliers and vastly improve the travelled
distance calculation.

- Type: [`Number`] or `null`
- Default: `null`
- Example:
```js
// Don't include location points with an accuracy of less than 100 meters
window.owntracks.config = {
filters: {
minAccuracy: 100
}
};
```

### `ignorePingLocation`

Remove the `ping/ping` location from the fetched data. This is useful when using the
Expand Down
3 changes: 3 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const DEFAULT_CONFIG = {
fetchOptions: {},
},
endDateTime,
filters: {
minAccuracy: null,
},
ignorePingLocation: true,
locale: "en",
map: {
Expand Down
9 changes: 7 additions & 2 deletions src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,13 @@ const _getDistanceTravelled = locationHistory => {
Object.keys(locationHistory).forEach(user => {
Object.keys(locationHistory[user]).forEach(device => {
let lastLatLng = null;
locationHistory[user][device].forEach(coordinate => {
const latLng = L.latLng(coordinate.lat, coordinate.lon);
locationHistory[user][device].forEach(location => {
if (
config.minAccurac !== null &&
location.acc > config.filters.minAccuracy
)
return;
const latLng = L.latLng(location.lat, location.lon);
if (lastLatLng !== null) {
const distance = distanceBetweenCoordinates(lastLatLng, latLng);
if (
Expand Down
58 changes: 42 additions & 16 deletions src/store/getters.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,46 @@ import config from "@/config";
import { distanceBetweenCoordinates } from "@/util";

/**
* From the selected users' and devices' location histories, create an
* array of all coordinates.
* Apply filters to the selected users' and devices' location histories.
*
* @param {State} state
* @param {LocationHistory} state.locationHistory
* Location history of selected users and devices
* @returns {L.LatLng[]} All coordinates
* @returns {LocationHistory} Filtered location history
*/
const locationHistoryLatLngs = state => {
const latLngs = [];
const filteredLocationHistory = state => {
const locationHistory = {};
Object.keys(state.locationHistory).forEach(user => {
locationHistory[user] = {};
Object.keys(state.locationHistory[user]).forEach(device => {
state.locationHistory[user][device].forEach(coordinate => {
latLngs.push(L.latLng(coordinate.lat, coordinate.lon));
locationHistory[user][device] = [];
state.locationHistory[user][device].forEach(location => {
if (
config.minAccurac !== null &&
location.acc > config.filters.minAccuracy
)
return;
locationHistory[user][device].push(location);
});
});
});
return locationHistory;
};

/**
* From the selected users' and devices' location histories, create an
* array of all coordinates.
*
* @param {State} state
* @returns {L.LatLng[]} All coordinates
*/
const filteredLocationHistoryLatLngs = state => {
const latLngs = [];
const locationHistory = filteredLocationHistory(state);
Object.keys(locationHistory).forEach(user => {
Object.keys(locationHistory[user]).forEach(device => {
locationHistory[user][device].forEach(location => {
latLngs.push(L.latLng(location.lat, location.lon));
});
});
});
Expand All @@ -30,17 +56,16 @@ const locationHistoryLatLngs = state => {
* coordinates does not exceed `config.map.maxPointDistance`.
*
* @param {State} state
* @param {LocationHistory} state.locationHistory
* Location history of selected users and devices
* @returns {L.LatLng[][]} Groups of coherent coordinates
*/
const locationHistoryLatLngGroups = state => {
const filteredLocationHistoryLatLngGroups = state => {
const groups = [];
Object.keys(state.locationHistory).forEach(user => {
Object.keys(state.locationHistory[user]).forEach(device => {
const locationHistory = filteredLocationHistory(state);
Object.keys(locationHistory).forEach(user => {
Object.keys(locationHistory[user]).forEach(device => {
let latLngs = [];
state.locationHistory[user][device].forEach(coordinate => {
const latLng = L.latLng(coordinate.lat, coordinate.lon);
locationHistory[user][device].forEach(location => {
const latLng = L.latLng(location.lat, location.lon);
// Skip if group splitting is disabled or this is the first
// coordinate in the current group
if (
Expand Down Expand Up @@ -68,6 +93,7 @@ const locationHistoryLatLngGroups = state => {
};

export default {
locationHistoryLatLngs,
locationHistoryLatLngGroups,
filteredLocationHistory,
filteredLocationHistoryLatLngs,
filteredLocationHistoryLatLngGroups,
};
22 changes: 13 additions & 9 deletions src/views/Map.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@

<template v-if="map.layers.line">
<LPolyline
v-for="(group, i) in locationHistoryLatLngGroups"
v-for="(group, i) in filteredLocationHistoryLatLngGroups"
:key="i"
:lat-lngs="group"
v-bind="polyline"
/>
</template>

<template v-if="map.layers.points">
<template v-for="(userDevices, user) in locationHistory">
<template v-for="(userDevices, user) in filteredLocationHistory">
<template v-for="(deviceLocations, device) in userDevices">
<LCircleMarker
v-for="(l, n) in deviceLocationsWithNameAndFace(
Expand Down Expand Up @@ -95,8 +95,8 @@

<template v-if="map.layers.heatmap">
<LHeatmap
v-if="locationHistoryLatLngs.length"
:lat-lng="locationHistoryLatLngs"
v-if="filteredLocationHistoryLatLngs.length"
:lat-lng="filteredLocationHistoryLatLngs"
:max="heatmap.max"
:radius="heatmap.radius"
:blur="heatmap.blur"
Expand Down Expand Up @@ -171,8 +171,12 @@ export default {
});
},
computed: {
...mapGetters(["locationHistoryLatLngs", "locationHistoryLatLngGroups"]),
...mapState(["lastLocations", "locationHistory", "map"]),
...mapGetters([
"filteredLocationHistory",
"filteredLocationHistoryLatLngs",
"filteredLocationHistoryLatLngGroups",
]),
...mapState(["lastLocations", "map"]),
},
methods: {
...mapMutations({
Expand All @@ -187,10 +191,10 @@ export default {
(this.map.layers.line ||
this.map.layers.points ||
this.map.layers.heatmap) &&
this.locationHistoryLatLngs.length > 0
this.filteredLocationHistoryLatLngs.length > 0
) {
this.$refs.map.mapObject.fitBounds(
new L.LatLngBounds(this.locationHistoryLatLngs)
new L.LatLngBounds(this.filteredLocationHistoryLatLngs)
);
} else if (this.map.layers.last && this.lastLocations.length > 0) {
const locations = this.lastLocations.map(l => L.latLng(l.lat, l.lon));
Expand Down Expand Up @@ -227,7 +231,7 @@ export default {
lastLocations() {
this.fitView();
},
locationHistory() {
filteredLocationHistory() {
this.fitView();
},
},
Expand Down

0 comments on commit b76cbdc

Please sign in to comment.