Skip to content

Instantly share code, notes, and snippets.

@rgoldfinger
Created August 23, 2014 04:08
Show Gist options
  • Save rgoldfinger/84d49def17283f864c18 to your computer and use it in GitHub Desktop.
Save rgoldfinger/84d49def17283f864c18 to your computer and use it in GitHub Desktop.
algorithm to sort a trail, where the points are out of order
var fs = require('fs');
var path = require('path');
var simple = require('simplify-js');
var geolib = require('geolib');
fs.readFile(path.join(__dirname, 'map.geojson'), 'utf8', function(err, data) {
// var jsonPoints = data;
// console.log(data);
var parsedData = JSON.parse(data);
var waypoints = parsedData.features;
var newWaypoints = [];
var tempPoint = {};
for (var i = 0; i < waypoints.length; i++) {
tempPoint.lat = '' + waypoints[i]['geometry']['coordinates'][1];
tempPoint.lon = '' + waypoints[i]['geometry']['coordinates'][0];
tempPoint.id = "_id" + i;
newWaypoints.push(tempPoint);
tempPoint = {};
}
var segmentsUnsorted = [];
var segmentStart = 0; //initialize to zero for the first segment
var tempDistance;
var tempSegment = [];
for (var j = 1; j < newWaypoints.length - 1; j ++) {
tempDistance = geolib.getDistance({
latitude: newWaypoints[j].lat,
longitude: newWaypoints[j].lon
}, {
latitude: newWaypoints[j + 1].lat,
longitude: newWaypoints[j + 1].lon
});
if (tempDistance > 700) {
// console.log("distance is: ", tempDistance, " at index ", j);
//we have a segment
for (var k = segmentStart; k <= j; k ++) {
tempSegment.push(newWaypoints[k]);
}
segmentsUnsorted.push(tempSegment);
tempSegment = [];
segmentStart = j + 1;
}
}
//push the first segment's children to results to get us started
var results = [];
segmentsUnsorted[0].forEach(function(item) {
results.push(item);
});
// strategy:
// iterate through all segments repetedly until no more matches have been made
// keep track of segments that are used.
// push the closest segment, within bounds, to 'result' segment
var usedSegment = {0: true};//we always start by using the first segment
var nextSegment;
var distances;
var shortestIndex;
var shortest;
var tempShortest = null;
var getAllFourDistances = function(segment1, segment2) {
var segment1start = segment1[0];
var segment1end = segment1[segment1.length -1];
var segment2start = segment2[0];
var segment2end = segment2[segment2.length -1];
var a = geolib.getDistance({
latitude: segment1start.lat,
longitude: segment1start.lon
}, {
latitude: segment2start.lat,
longitude: segment2start.lon
});
var b = geolib.getDistance({
latitude: segment1start.lat,
longitude: segment1start.lon
}, {
latitude: segment2end.lat,
longitude: segment2end.lon
});
var c = geolib.getDistance({
latitude: segment1end.lat,
longitude: segment1end.lon
}, {
latitude: segment2start.lat,
longitude: segment2start.lon
});
var d = geolib.getDistance({
latitude: segment1end.lat,
longitude: segment1end.lon
}, {
latitude: segment2end.lat,
longitude: segment2end.lon
});
// start-start, start-end, end-start, end-end
return [a, b, c, d];
};
var getIndexOfShortest = function(arr) {
var shortest = arr[0];
var index = 0;
for (var i = 1; i < arr.length; i ++) {
if (shortest > arr[i]) {
shortest = arr[i];
index = i;
}
}
return index;
};
var getShortest = function(arr) {
var shortest = arr[0];
for (var i = 1; i < arr.length; i ++) {
if (shortest > arr[i]) {
shortest = arr[i];
}
}
return shortest;
};
var matchesThisRun = true;
var forwards;
var unshift;
while (matchesThisRun) {
console.log('new run', results.length);
matchesThisRun = false;
//iterate through all of the segments
for (var q = 1; q < segmentsUnsorted.length; q ++) {
// skip segments that have been used
if (!usedSegment[q]) {
//compare all four directions: start-start, start-end, end-start, end-end
distances = getAllFourDistances(results, segmentsUnsorted[q]);
shortestIndex = getIndexOfShortest(distances);
shortest = getShortest(distances);
if (! tempShortest || shortest < tempShortest.distance) {
// 0: start-start, 1: start-end, 2: end-start, 3: end-end
// this is a little complicated, but it determines whether the path is forwards or backwards
// and whether it goes at the beginning or end of the results
if (shortestIndex === 0 || shortestIndex === 2) {
forwards = true;
} else {
forwards = false;
}
if (shortestIndex === 0 || shortestIndex === 1) {
unshift = true;
} else {
unshift = false;
}
tempShortest = {
index: q,
distance: shortest,
forwards: forwards,
unshift: unshift
};
}
if (tempShortest.distance < 4000) {
console.log('making match', tempShortest.distance);
matchesThisRun = true;
nextSegment = segmentsUnsorted[tempShortest.index];
//now that we have the shortest, push it's members to results
if (tempShortest.forwards) {
if (tempShortest.unshift) {
nextSegment.forEach(function(item) {
results.unshift(item);
});
} else {
nextSegment.forEach(function(item) {
results.push(item);
});
}
} else { //segment is reversed
if (tempShortest.unshift) {
for (var t = nextSegment.length - 1; t > -1; t --) {
results.unshift(nextSegment[t]);
}
} else {
for (var t = nextSegment.length - 1; t > -1; t --) {
results.push(nextSegment[t]);
}
}
}
usedSegment[tempShortest.index] = true;
}
tempShortest = null;
}
}
}
var stringWaypoints = "var trailJSON = '" + JSON.stringify(results) + "'";
fs.writeFile(path.join(__dirname, 'ct_clean.json'), stringWaypoints);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment