From cd16e843228671ee76c41aebcc6cee028fc6a958 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Wed, 10 Aug 2016 19:00:40 +0200 Subject: [PATCH] refactor parser, improve centroid func, add fixture --- src/wrap.js | 44 +++++++++++++++++++++++++++++++++++++++ stream/centroid.js | 6 +++--- stream/decode.js | 4 +++- stream/parser.js | 38 ++++++++++++++++++++++++++++----- stream/unwrap.js | 25 ++++++++++++++++++++++ test/fixture/example.raw | Bin 0 -> 10103 bytes 6 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 src/wrap.js create mode 100644 stream/unwrap.js create mode 100644 test/fixture/example.raw diff --git a/src/wrap.js b/src/wrap.js new file mode 100644 index 0000000..bc8a9cd --- /dev/null +++ b/src/wrap.js @@ -0,0 +1,44 @@ + +/** + normalize co-ordinates that lie outside of the normal ranges. + + longitude wrapping simply requires adding +- 360 to the value until it comes + in to range. + + for the latitude values we need to flip the longitude whenever the latitude + crosses a pole. +**/ + + +function wrap( lat, lon ){ + + var point = { lat: lat, lon: lon }; + var quadrant = Math.floor( Math.abs(lat) / 90) % 4; + var pole = ( lat > 0 ) ? 90 : -90; + var offset = lat % 90; + + switch( quadrant ){ + case 0: + point.lat = offset; + break; + case 1: + point.lat = pole - offset; + point.lon += 180; + break; + case 2: + point.lat = -offset; + point.lon += 180; + break; + case 3: + point.lat = -pole + offset; + break; + } + + if( point.lon > 180 || point.lon <= -180 ){ + point.lon -= Math.floor(( point.lon + 180 ) / 360) * 360; + } + + return point; +} + +module.exports = wrap; diff --git a/stream/centroid.js b/stream/centroid.js index 104e46a..313c8af 100644 --- a/stream/centroid.js +++ b/stream/centroid.js @@ -5,15 +5,15 @@ var through = require('through2'), // https://github.com/turf-junkyard/turf-along // https://github.com/turf-junkyard/turf-line-distance -var UNIT = 'km'; +var UNIT = 'kilometers'; function centroid(){ return through.obj( function( geojson, _, next ){ try { - // total distance in km + // total distance in meters var dist = distance( geojson, UNIT ); - geojson.properties.distance = dist; + geojson.properties.distance = (dist * 1000).toFixed(4); // interpolate middle of path var point = along( geojson, dist/2, UNIT ); diff --git a/stream/decode.js b/stream/decode.js index d1f87d1..0e2b8f7 100644 --- a/stream/decode.js +++ b/stream/decode.js @@ -2,12 +2,14 @@ var split = require('split'), through = require('through2'), parser = require('./parser'), + unwrap = require('./unwrap'), centroid = require('./centroid'); function decode( streamIn, streamOut ){ return streamIn .pipe( split() ) - .pipe( parser() ) + .pipe( parser( 6 ) ) + .pipe( unwrap() ) .pipe( centroid() ) .pipe( through.obj( function( obj, _, next ){ next( null, JSON.stringify( obj, null, 2 ) ); diff --git a/stream/parser.js b/stream/parser.js index d18a546..0e5ce25 100644 --- a/stream/parser.js +++ b/stream/parser.js @@ -2,21 +2,49 @@ var through = require('through2'), polyline = require('polyline'); -function parser(){ +/** + note: you must select the same 'precision' value that was used when encoding + the polyline. + + valhalla: 6 + osrm: 5 + + parser expects data to be: + - newline terminated with \n + - column delimited with \0 + - in the format: {encoded_polyline}\0{name1}\0{name2}...\n +**/ +function parser( precision ){ return through.obj( function( row, _, next ){ var cols = row.split('\0'); try { - var names = cols.slice(1); - if( names.length ){ - var geojson = polyline.toGeoJSON(cols[0]); - geojson.properties = { name: names[0] }; + // must contain a polyline and at least one name + if( cols.length > 1 ){ + + // decode polyline + var geojson = polyline.toGeoJSON(cols[0], precision); + + // select name + geojson.properties = { name: selectName(cols.slice(1)) }; + this.push( geojson ); } } catch( e ){ console.error( 'polyline parsing error', e ); } + next(); }); } +// each connected road can have one or more names +// we select one name to be the default. +function selectName( names ){ + // return the longest name + // @todo: can we improve this logic? + return names.reduce( function( a, b ){ + return a.length > b.length ? a : b; + }); +} + module.exports = parser; diff --git a/stream/unwrap.js b/stream/unwrap.js new file mode 100644 index 0000000..6a75b64 --- /dev/null +++ b/stream/unwrap.js @@ -0,0 +1,25 @@ + +var through = require('through2'), + wrap = require('../src/wrap'); + +function unwrap(){ + return through.obj( function( geojson, _, next ){ + try { + + // unwrap coordinates which go around the globe + geojson.coordinates = geojson.coordinates.map( function( coord ){ + var w = wrap( coord[1], coord[0] ); + return [ w.lon, w.lat ]; + }); + + this.push( geojson ); + + } catch( e ){ + console.error( 'polyline unwrap error', e ); + } + + next(); + }); +} + +module.exports = unwrap; diff --git a/test/fixture/example.raw b/test/fixture/example.raw new file mode 100644 index 0000000000000000000000000000000000000000..2e6e443d3f3f52ea1d6c58dda44dc28a1e00ebb7 GIT binary patch literal 10103 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 kCneJqe?HVn4~95MlFO^4b;#_D_af{F@38q!`y