diff --git a/src/_helpers/generators.js b/src/_helpers/generators.js new file mode 100644 index 0000000..df5b241 --- /dev/null +++ b/src/_helpers/generators.js @@ -0,0 +1,65 @@ +function* pairwise(iter) { + let last; + let firstLoop = true; + for (const it of iter) { + if (firstLoop) { + firstLoop = false; + } else { + yield [last, it]; + } + last = it; + } +} + +function* enumerate(iter) { + let i = 0; + for (const it of iter) { + yield [i, it]; + i++; + } +} + +const map = (fn) => + function* (iter) { + for (const [i, it] of enumerate(iter)) { + yield fn(it, i); + } + }; + +const filter = (fn) => + function* (iter) { + for (const it of iter) { + if (fn(it)) { + yield it; + } + } + }; + +const reduce = (fn, init) => (iter) => { + let acc = init; + for (const it of iter) { + acc = fn(acc, it); + } + return acc; +}; + +const scan = (fn) => + function* (iter, init) { + let acc = init; + for (const it of iter) { + acc = fn(acc, it); + yield acc; + } + }; + +const flow = (...reducers) => (input) => reducers.reduce((c, fn) => fn(c), input); + +module.exports = { + filter, + map, + enumerate, + pairwise, + flow, + reduce, + scan, +}; diff --git a/src/models/TrackData.js b/src/models/TrackData.js index 2c7817e..9c92a90 100644 --- a/src/models/TrackData.js +++ b/src/models/TrackData.js @@ -2,6 +2,8 @@ const mongoose = require('mongoose'); const uniqueValidator = require('mongoose-unique-validator'); const turf = require('turf'); +const { flow, filter, map, pairwise, reduce } = require('../_helpers/generators'); + const schema = new mongoose.Schema( { slug: { type: String, lowercase: true, unique: true }, @@ -76,12 +78,17 @@ class TrackData extends mongoose.Model { } measureTrackLength() { - let totalLength = 0; - for (const [a, b] of pairwise(map(this.points, (p) => turf.point([p.longitude, p.latitude])))) { - const legLengthMeters = turf.distance(a, b) * 1000; - totalLength += legLengthMeters; - } - return totalLength; + return flow( + filter((p) => p.latitude != null && p.longitude != null), + map((p) => turf.point([p.longitude, p.latitude])), + pairwise, + map(([a, b]) => turf.distance(a, b) * 1000), + + // Ignore distances between two points that are bigger than 100m, this + // must be a gap in the data or a mistake. + filter((d) => d <= 100), + reduce((c, d) => c + d, 0), + )(this.points); } get duration() { @@ -93,33 +100,6 @@ class TrackData extends mongoose.Model { } } -function* pairwise(iter) { - let last; - let firstLoop = true; - for (const it of iter) { - if (firstLoop) { - firstLoop = false; - } else { - yield [last, it]; - } - last = it; - } -} - -function* enumerate(iter) { - let i = 0; - for (const it of iter) { - yield [i, it]; - i++; - } -} - -function* map(iter, fn) { - for (const [i, it] of enumerate(iter)) { - yield fn(it, i); - } -} - mongoose.model(TrackData, schema); module.exports = TrackData;