cheap-ruler
Advanced tools
Comparing version 2.0.0 to 2.1.0
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -11,4 +11,2 @@ var cheapRuler = require('../'); | ||
var suite = new Benchmark.Suite(); | ||
var distances = lines.map(function (line) { | ||
@@ -18,16 +16,13 @@ return ruler.lineDistance(line); | ||
suite | ||
.add('turf.along', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.along(turf.linestring(lines[i]), distances[i], 'kilometers'); | ||
runBench({ | ||
'turf.along': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.along(turf.linestring(lines[i]), distances[i], 'kilometers'); | ||
} | ||
}, | ||
'ruler.along': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.along(lines[i], distances[i]); | ||
} | ||
} | ||
}) | ||
.add('ruler.along', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.along(lines[i], distances[i]); | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); |
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -11,4 +11,2 @@ var cheapRuler = require('../'); | ||
var suite = new Benchmark.Suite(); | ||
var polygons = []; | ||
@@ -22,16 +20,13 @@ | ||
suite | ||
.add('turf.area', function () { | ||
for (var i = 0; i < polygons.length; i++) { | ||
turf.area(turf.polygon(polygons[i])); | ||
runBench({ | ||
'turf.area': function () { | ||
for (var i = 0; i < polygons.length; i++) { | ||
turf.area(turf.polygon(polygons[i])); | ||
} | ||
}, | ||
'ruler.area': function () { | ||
for (var i = 0; i < polygons.length; i++) { | ||
ruler.area(polygons[i]); | ||
} | ||
} | ||
}) | ||
.add('ruler.area', function () { | ||
for (var i = 0; i < polygons.length; i++) { | ||
ruler.area(polygons[i]); | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); |
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -8,27 +8,17 @@ var cheapRuler = require('../'); | ||
var lines = require('../test/fixtures/lines.json'); | ||
var points = Array.prototype.concat.apply([], lines); | ||
var ruler = cheapRuler(32.8351); | ||
var suite = new Benchmark.Suite(); | ||
suite | ||
.add('turf.bearing', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
var line = lines[i]; | ||
for (var j = 0; j < line.length - 1; j++) { | ||
turf.bearing(turf.point(lines[i][j]), turf.point(lines[i][j + 1])); | ||
runBench({ | ||
'turf.bearing': function () { | ||
for (var i = 0; i < points.length - 1; i++) { | ||
turf.bearing(turf.point(points[i]), turf.point(points[i + 1])); | ||
} | ||
} | ||
}) | ||
.add('ruler.bearing', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
var line = lines[i]; | ||
for (var j = 0; j < line.length - 1; j++) { | ||
ruler.bearing(lines[i][j], lines[i][j + 1]); | ||
}, | ||
'ruler.bearing': function () { | ||
for (var i = 0; i < points.length - 1; i++) { | ||
ruler.bearing(points[i], points[i + 1]); | ||
} | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); |
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -8,28 +8,18 @@ var cheapRuler = require('../'); | ||
var lines = require('../test/fixtures/lines.json'); | ||
var points = Array.prototype.concat.apply([], lines); | ||
var ruler = cheapRuler(32.8351); | ||
var suite = new Benchmark.Suite(); | ||
suite | ||
.add('turf.destination-based bbox', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
var line = lines[i]; | ||
for (var j = 0; j < line.length; j++) { | ||
bboxBuffer(turf.point(line[j]), 0.01); | ||
runBench({ | ||
'turf.destination-based bbox': function () { | ||
for (var i = 0; i < points.length; i++) { | ||
bboxBuffer(turf.point(points[i]), 0.01); | ||
} | ||
} | ||
}) | ||
.add('ruler.bufferPoint', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
var line = lines[i]; | ||
for (var j = 0; j < line.length; j++) { | ||
ruler.bufferPoint(line[j], 0.01); | ||
}, | ||
'ruler.bufferPoint': function () { | ||
for (var i = 0; i < points.length; i++) { | ||
ruler.bufferPoint(points[i], 0.01); | ||
} | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); | ||
@@ -36,0 +26,0 @@ function bboxBuffer(pt, distance) { |
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -8,31 +8,17 @@ var cheapRuler = require('../'); | ||
var lines = require('../test/fixtures/lines.json'); | ||
var points = Array.prototype.concat.apply([], lines); | ||
var ruler = cheapRuler(32.8351); | ||
var suite = new Benchmark.Suite(); | ||
suite | ||
.add('turf.destination', function () { | ||
var k = 0; | ||
for (var i = 0; i < lines.length; i++) { | ||
var line = lines[i]; | ||
for (var j = 0; j < line.length; j++) { | ||
k++; | ||
turf.destination(turf.point(lines[i][j]), 1, (k % 360) - 180, 'kilometers'); | ||
runBench({ | ||
'turf.destination': function () { | ||
for (var i = 0; i < points.length; i++) { | ||
turf.destination(turf.point(points[i]), 1, (i % 360) - 180, 'kilometers'); | ||
} | ||
} | ||
}) | ||
.add('ruler.destination', function () { | ||
var k = 0; | ||
for (var i = 0; i < lines.length; i++) { | ||
var line = lines[i]; | ||
for (var j = 0; j < line.length; j++) { | ||
k++; | ||
ruler.destination(lines[i][j], 1, (k % 360) - 180); | ||
}, | ||
'ruler.destination': function () { | ||
for (var i = 0; i < points.length; i++) { | ||
ruler.destination(points[i], 1, (i % 360) - 180); | ||
} | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); |
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -11,18 +11,13 @@ var cheapRuler = require('../'); | ||
var suite = new Benchmark.Suite(); | ||
suite | ||
.add('turf.lineDistance', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.lineDistance(turf.linestring(lines[i])); | ||
runBench({ | ||
'turf.lineDistance': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.lineDistance(turf.linestring(lines[i])); | ||
} | ||
}, | ||
'ruler.lineDistance': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.lineDistance(lines[i]); | ||
} | ||
} | ||
}) | ||
.add('ruler.lineDistance', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.lineDistance(lines[i]); | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); |
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -11,4 +11,2 @@ var cheapRuler = require('../'); | ||
var suite = new Benchmark.Suite(); | ||
var endpoints = lines.map(function (line) { | ||
@@ -22,19 +20,16 @@ var dist = ruler.lineDistance(line); | ||
suite | ||
.add('turf.lineSlice', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.lineSlice( | ||
turf.point(endpoints[i].start), | ||
turf.point(endpoints[i].stop), | ||
turf.linestring(lines[i])); | ||
runBench({ | ||
'turf.lineSlice': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.lineSlice( | ||
turf.point(endpoints[i].start), | ||
turf.point(endpoints[i].stop), | ||
turf.linestring(lines[i])); | ||
} | ||
}, | ||
'ruler.lineSlice': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.lineSlice(endpoints[i].start, endpoints[i].stop, lines[i]); | ||
} | ||
} | ||
}) | ||
.add('ruler.lineSlice', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.lineSlice(endpoints[i].start, endpoints[i].stop, lines[i]); | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); |
'use strict'; | ||
var Benchmark = require('benchmark'); | ||
var runBench = require('./bench-run.js'); | ||
@@ -12,18 +12,13 @@ var cheapRuler = require('../'); | ||
var suite = new Benchmark.Suite(); | ||
suite | ||
.add('turf.pointOnLine', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.pointOnLine(turf.linestring(lines[i]), turf.point(p)); | ||
runBench({ | ||
'turf.pointOnLine': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
turf.pointOnLine(turf.linestring(lines[i]), turf.point(p)); | ||
} | ||
}, | ||
'ruler.pointOnLine': function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.pointOnLine(lines[i], p); | ||
} | ||
} | ||
}) | ||
.add('ruler.pointOnLine', function () { | ||
for (var i = 0; i < lines.length; i++) { | ||
ruler.pointOnLine(lines[i], p); | ||
} | ||
}) | ||
.on('cycle', function (event) { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); | ||
}); |
@@ -33,2 +33,20 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.cheapRuler = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
bearing: function (a, b) { | ||
var dx = (b[0] - a[0]) * this.e; | ||
var dy = b[1] - a[1]; | ||
if (!dx && !dy) return 0; | ||
var bearing = Math.atan2(-dy, dx) * 180 / Math.PI + 90; | ||
if (bearing > 180) bearing -= 360; | ||
return bearing; | ||
}, | ||
destination: function (p, dist, bearing) { | ||
var a = (90 - bearing) * Math.PI / 180; | ||
var d = dist / this.d; | ||
return [ | ||
p[0] + d * Math.cos(a) / this.e, | ||
p[1] + d * Math.sin(a) | ||
]; | ||
}, | ||
lineDistance: function (points) { | ||
@@ -56,38 +74,16 @@ var total = 0; | ||
bearing: function (a, b) { | ||
var dx = (b[0] - a[0]) * this.e; | ||
var dy = b[1] - a[1]; | ||
if (!dx && !dy) return 0; | ||
var bearing = Math.atan2(-dy, dx) * 180 / Math.PI + 90; | ||
if (bearing > 180) bearing -= 360; | ||
return bearing; | ||
}, | ||
along: function (line, dist) { | ||
var sum = 0; | ||
bufferPoint: function (p, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
p[0] - h, | ||
p[1] - v, | ||
p[0] + h, | ||
p[1] + v | ||
]; | ||
}, | ||
if (dist <= 0) return line[0]; | ||
bufferBBox: function (bbox, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
bbox[0] - h, | ||
bbox[1] - v, | ||
bbox[2] + h, | ||
bbox[3] + v | ||
]; | ||
}, | ||
for (var i = 0; i < line.length - 1; i++) { | ||
var p0 = line[i]; | ||
var p1 = line[i + 1]; | ||
var d = this.distance(p0, p1); | ||
sum += d; | ||
if (sum > dist) return interpolate(p0, p1, (dist - (sum - d)) / d); | ||
} | ||
insideBBox: function (p, bbox) { | ||
return p[0] >= bbox[0] && | ||
p[0] <= bbox[2] && | ||
p[1] >= bbox[1] && | ||
p[1] <= bbox[3]; | ||
return line[line.length - 1]; | ||
}, | ||
@@ -168,36 +164,55 @@ | ||
along: function (line, dist) { | ||
lineSliceAlong: function (start, stop, line) { | ||
var sum = 0; | ||
var slice = []; | ||
if (dist <= 0) return line[0]; | ||
for (var i = 0; i < line.length - 1; i++) { | ||
var p0 = line[i]; | ||
var p = line[i + 1]; | ||
var d = this.distance(p0, p); | ||
var p1 = line[i + 1]; | ||
var d = this.distance(p0, p1); | ||
sum += d; | ||
if (sum > dist) { | ||
var t = (dist - (sum - d)) / d; | ||
var dx = p[0] - p0[0]; | ||
var dy = p[1] - p0[1]; | ||
if (sum > start && slice.length === 0) { | ||
slice.push(interpolate(p0, p1, (start - (sum - d)) / d)); | ||
} | ||
return [ | ||
p0[0] + dx * t, | ||
p0[1] + dy * t | ||
]; | ||
if (sum >= stop) { | ||
slice.push(interpolate(p0, p1, (stop - (sum - d)) / d)); | ||
return slice; | ||
} | ||
if (sum > start) slice.push(p1); | ||
} | ||
return line[line.length - 1]; | ||
return slice; | ||
}, | ||
destination: function (p, dist, bearing) { | ||
var a = (90 - bearing) * Math.PI / 180; | ||
var d = dist / this.d; | ||
bufferPoint: function (p, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
p[0] + d * Math.cos(a) / this.e, | ||
p[1] + d * Math.sin(a) | ||
p[0] - h, | ||
p[1] - v, | ||
p[0] + h, | ||
p[1] + v | ||
]; | ||
}, | ||
bufferBBox: function (bbox, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
bbox[0] - h, | ||
bbox[1] - v, | ||
bbox[2] + h, | ||
bbox[3] + v | ||
]; | ||
}, | ||
insideBBox: function (p, bbox) { | ||
return p[0] >= bbox[0] && | ||
p[0] <= bbox[2] && | ||
p[1] >= bbox[1] && | ||
p[1] <= bbox[3]; | ||
} | ||
@@ -210,3 +225,12 @@ }; | ||
function interpolate(a, b, t) { | ||
var dx = b[0] - a[0]; | ||
var dy = b[1] - a[1]; | ||
return [ | ||
a[0] + dx * t, | ||
a[1] + dy * t | ||
]; | ||
} | ||
},{}]},{},[1])(1) | ||
}); |
44
debug.js
@@ -5,22 +5,36 @@ 'use strict'; | ||
var turf = require('turf'); | ||
var vincenty = require('node-vincenty'); | ||
var p = [-96.914526,32.836763]; | ||
var ruler = createRuler(32.83); | ||
process.stdout.write('| lat | '); | ||
var p2 = ruler.destination(p, 1, 90); | ||
for (var lat = 0; lat <= 80; lat += 10) { | ||
process.stdout.write(lat + '° | '); | ||
} | ||
// console.log(ruler.distance(p, p2)); | ||
process.stdout.write('\n| --- | '); | ||
for (var lat = 0; lat <= 80; lat += 10) process.stdout.write(' --- |'); | ||
process.stdout.write('\n'); | ||
console.log(JSON.stringify(turf.featurecollection([ | ||
turf.point(p, {start: true}), | ||
turf.point(p2, {stop: true, color: 'red'}) | ||
]))); | ||
var distances = [1, 100, 1000, 5000]; | ||
// var turfSlice = turf.lineSlice(turf.point(start), turf.point(stop), turf.linestring(line)).geometry.coordinates; | ||
for (var i = 0; i < distances.length; i++) { | ||
var dist = distances[i]; | ||
process.stdout.write('| ' + dist + 'km | '); | ||
// console.log(JSON.stringify(turf.featurecollection([ | ||
// turf.linestring(line), | ||
// turf.point(start, {start: true}), | ||
// turf.point(stop, {stop: true}), | ||
// turf.linestring(slice, {slice: true, stroke: 'red'}) | ||
// ]))); | ||
for (var lat = 0; lat <= 80; lat += 10) { | ||
var ruler = createRuler(lat); | ||
var p1 = ruler.destination([0, lat], dist / 2, 90); | ||
var p2 = ruler.destination([0, lat], dist / 2, 90 - 180); | ||
var d = ruler.distance(p1, p2); | ||
var d2 = turf.distance(turf.point(p1), turf.point(p2)); | ||
// var d2 = vincenty.distVincenty(p1[1], p1[0], p2[1], p2[0]).distance / 1000; | ||
var err = Math.abs((d - d2) / d2); | ||
var errStr = (Math.round(100 * 1e2 * err) / 1e2) + '%'; | ||
process.stdout.write(errStr + ' | '); | ||
// console.log('distance: %dkm, latitude: %d, error: %d%', dist, lat, errStr); | ||
} | ||
process.stdout.write('\n'); | ||
} |
128
index.js
@@ -32,2 +32,20 @@ 'use strict'; | ||
bearing: function (a, b) { | ||
var dx = (b[0] - a[0]) * this.e; | ||
var dy = b[1] - a[1]; | ||
if (!dx && !dy) return 0; | ||
var bearing = Math.atan2(-dy, dx) * 180 / Math.PI + 90; | ||
if (bearing > 180) bearing -= 360; | ||
return bearing; | ||
}, | ||
destination: function (p, dist, bearing) { | ||
var a = (90 - bearing) * Math.PI / 180; | ||
var d = dist / this.d; | ||
return [ | ||
p[0] + d * Math.cos(a) / this.e, | ||
p[1] + d * Math.sin(a) | ||
]; | ||
}, | ||
lineDistance: function (points) { | ||
@@ -55,38 +73,16 @@ var total = 0; | ||
bearing: function (a, b) { | ||
var dx = (b[0] - a[0]) * this.e; | ||
var dy = b[1] - a[1]; | ||
if (!dx && !dy) return 0; | ||
var bearing = Math.atan2(-dy, dx) * 180 / Math.PI + 90; | ||
if (bearing > 180) bearing -= 360; | ||
return bearing; | ||
}, | ||
along: function (line, dist) { | ||
var sum = 0; | ||
bufferPoint: function (p, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
p[0] - h, | ||
p[1] - v, | ||
p[0] + h, | ||
p[1] + v | ||
]; | ||
}, | ||
if (dist <= 0) return line[0]; | ||
bufferBBox: function (bbox, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
bbox[0] - h, | ||
bbox[1] - v, | ||
bbox[2] + h, | ||
bbox[3] + v | ||
]; | ||
}, | ||
for (var i = 0; i < line.length - 1; i++) { | ||
var p0 = line[i]; | ||
var p1 = line[i + 1]; | ||
var d = this.distance(p0, p1); | ||
sum += d; | ||
if (sum > dist) return interpolate(p0, p1, (dist - (sum - d)) / d); | ||
} | ||
insideBBox: function (p, bbox) { | ||
return p[0] >= bbox[0] && | ||
p[0] <= bbox[2] && | ||
p[1] >= bbox[1] && | ||
p[1] <= bbox[3]; | ||
return line[line.length - 1]; | ||
}, | ||
@@ -167,36 +163,55 @@ | ||
along: function (line, dist) { | ||
lineSliceAlong: function (start, stop, line) { | ||
var sum = 0; | ||
var slice = []; | ||
if (dist <= 0) return line[0]; | ||
for (var i = 0; i < line.length - 1; i++) { | ||
var p0 = line[i]; | ||
var p = line[i + 1]; | ||
var d = this.distance(p0, p); | ||
var p1 = line[i + 1]; | ||
var d = this.distance(p0, p1); | ||
sum += d; | ||
if (sum > dist) { | ||
var t = (dist - (sum - d)) / d; | ||
var dx = p[0] - p0[0]; | ||
var dy = p[1] - p0[1]; | ||
if (sum > start && slice.length === 0) { | ||
slice.push(interpolate(p0, p1, (start - (sum - d)) / d)); | ||
} | ||
return [ | ||
p0[0] + dx * t, | ||
p0[1] + dy * t | ||
]; | ||
if (sum >= stop) { | ||
slice.push(interpolate(p0, p1, (stop - (sum - d)) / d)); | ||
return slice; | ||
} | ||
if (sum > start) slice.push(p1); | ||
} | ||
return line[line.length - 1]; | ||
return slice; | ||
}, | ||
destination: function (p, dist, bearing) { | ||
var a = (90 - bearing) * Math.PI / 180; | ||
var d = dist / this.d; | ||
bufferPoint: function (p, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
p[0] + d * Math.cos(a) / this.e, | ||
p[1] + d * Math.sin(a) | ||
p[0] - h, | ||
p[1] - v, | ||
p[0] + h, | ||
p[1] + v | ||
]; | ||
}, | ||
bufferBBox: function (bbox, buffer) { | ||
var v = buffer / this.d; | ||
var h = v / this.e; | ||
return [ | ||
bbox[0] - h, | ||
bbox[1] - v, | ||
bbox[2] + h, | ||
bbox[3] + v | ||
]; | ||
}, | ||
insideBBox: function (p, bbox) { | ||
return p[0] >= bbox[0] && | ||
p[0] <= bbox[2] && | ||
p[1] >= bbox[1] && | ||
p[1] <= bbox[3]; | ||
} | ||
@@ -208,1 +223,10 @@ }; | ||
} | ||
function interpolate(a, b, t) { | ||
var dx = b[0] - a[0]; | ||
var dy = b[1] - a[1]; | ||
return [ | ||
a[0] + dx * t, | ||
a[1] + dy * t | ||
]; | ||
} |
{ | ||
"name": "cheap-ruler", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "A collection of fast approximations to common geographic measurements.", | ||
@@ -12,2 +12,3 @@ "main": "index.js", | ||
"eslint-config-mourner": "^2.0.1", | ||
"istanbul": "^0.4.3", | ||
"tape": "^4.5.1", | ||
@@ -20,3 +21,4 @@ "turf": "^2.0.2" | ||
"build": "browserify index.js -s cheapRuler > cheap-ruler.js", | ||
"prepublish": "npm run build" | ||
"prepublish": "npm run build", | ||
"cov": "istanbul cover tape test/test.js" | ||
}, | ||
@@ -23,0 +25,0 @@ "eslintConfig": { |
126
README.md
@@ -7,2 +7,25 @@ # cheap-ruler [![Build Status](https://travis-ci.org/mapbox/cheap-ruler.svg?branch=master)](https://travis-ci.org/mapbox/cheap-ruler) | ||
For distances under a hundred miles and not on the poles, | ||
the results are [typically within 0.1%](#precision) of corresponding Turf functions. | ||
## Performance | ||
Compared to corresponding Turf methods (using Node v5.10): | ||
- `distance`: ~26x faster | ||
- `bearing`: ~3.5x faster | ||
- `destination`: ~6.4x faster | ||
- `lineDistance`: ~26x faster | ||
- `area`: ~3.6x faster | ||
- `along`: ~21x faster | ||
- `pointOnLine`: ~72x faster | ||
- `lineSlice`: ~56x faster | ||
Additional utility methods: | ||
- `lineSliceAlong`: ~268x faster than `turf.lineSlice(turf.along(...` | ||
- `bufferPoint`: ~210x faster than creating a bounding box with two diagonal `turf.destination` calls | ||
- `bufferBBox`: ~210x faster (likewise) | ||
- `insideBBox`: ~24x faster than `turf.inside(turf.point(p), turf.bboxPolygon(bbox))` | ||
## Usage | ||
@@ -18,5 +41,2 @@ | ||
For a city scale (a few dozen miles) and far away from poles, | ||
the results are typically within 0.1% of corresponding Turf functions. | ||
**Note**: to get the full performance benefit, create the ruler object once per an area of calculation (such as a tile), and then reuse it as much as possible. | ||
@@ -43,14 +63,39 @@ | ||
Given two points of the form `[x, y]`, returns the distance. | ||
20–25 times faster than `turf.distance`. | ||
Given two points of the form `[longitude, latitude]`, returns the distance. | ||
```js | ||
var distance = ruler.distance([30.5, 50.5], [30.51, 50.49]); | ||
``` | ||
#### bearing(a, b) | ||
Returns the bearing between two points in angles. | ||
```js | ||
var bearing = ruler.bearing([30.5, 50.5], [30.51, 50.49]); | ||
``` | ||
#### destination(p, dist, bearing) | ||
Returns a new point given distance and bearing from the starting point. | ||
```js | ||
var point = ruler.destination([30.5, 50.5], 0.1, 90); | ||
``` | ||
#### lineDistance(line) | ||
Given a line (an array of points), returns the total line distance. | ||
20–25 times faster than `turf.lineDistance`. | ||
```js | ||
var length = ruler.lineDistance([ | ||
[-67.031, 50.458], [-67.031, 50.534], | ||
[-66.929, 50.534], [-66.929, 50.458] | ||
]); | ||
``` | ||
#### area(polygon) | ||
Given a polygon (an array of rings, where each ring is an array of points), returns the area. | ||
3–4 times faster than `turf.area`. Note that it returns the value in the specified units | ||
Note that it returns the value in the specified units | ||
(square kilometers by default) rather than square meters as in `turf.area`. | ||
@@ -60,25 +105,15 @@ | ||
var area = ruler.area([[ | ||
[-67.031, 50.458], | ||
[-67.031, 50.534], | ||
[-66.929, 50.534], | ||
[-66.929, 50.458], | ||
[-67.031, 50.458] | ||
[-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534], | ||
[-66.929, 50.458], [-67.031, 50.458] | ||
]]); | ||
``` | ||
#### bearing(a, b) | ||
Returns the bearing between two points in angles. | ||
3–4 times faster than `turf.bearing`. | ||
#### along(line, dist) | ||
Returns the point at a specified distance along the line. | ||
20-25 times faster than `turf.along`. | ||
#### destination(p, dist, bearing) | ||
```js | ||
var point = ruler.along(line, 2.5); | ||
``` | ||
Returns a new point given distance and bearing from the starting point. | ||
6–7 times faster than `turf.destination`. | ||
#### pointOnLine(line, p) | ||
@@ -88,13 +123,26 @@ | ||
and `index` is the start index of the segment with the closest point. | ||
70–75 times faster than `turf.pointOnLine`. | ||
```js | ||
var point = ruler.pointOnLine(line, [-67.04, 50.5]).point; | ||
``` | ||
#### lineSlice(start, stop, line) | ||
Returns a part of the given line between the start and the stop points (or their closest points on the line). | ||
50–60 times faster than `turf.lineSlice`. | ||
```js | ||
ruler.lineSlice([-67.04, 50.5], [-67.05, 50.56], line); | ||
``` | ||
#### lineSliceAlong(startDist, stopDist, line) | ||
Returns a part of the given line between the start and the stop points indicated by distance along the line. | ||
```js | ||
ruler.lineSliceAlong(10, 20, line); | ||
``` | ||
#### bufferPoint(p, buffer) | ||
Given a point, returns a bounding box object (`[w, s, e, n]`) created from the given point buffered by a given distance. | ||
About _200 times faster_ than creating a bounding box with two diagonal `turf.destination` calls. | ||
@@ -109,2 +157,6 @@ ```js | ||
```js | ||
var bbox = ruler.bufferBBox([30.5, 50.5, 31, 51], 0.2); | ||
``` | ||
#### insideBBox(p, bbox) | ||
@@ -114,2 +166,6 @@ | ||
```js | ||
var inside = ruler.insideBBox([30.5, 50.5], [30, 50, 31, 51]); | ||
``` | ||
## Install | ||
@@ -119,1 +175,21 @@ | ||
- Browser build (CDN): https://npmcdn.com/cheap-ruler@1.3.0/cheap-ruler.js | ||
## Precision | ||
A table that shows the margin of error for `ruler.distance` compared to `turf.distance`: | ||
| lat | 0° | 10° | 20° | 30° | 40° | 50° | 60° | 70° | 80° | | ||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | ||
| 1km | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | | ||
| 100km | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | 0.08% | 0.09% | 0.11% | | ||
| 1000km | 0.11% | 0.11% | 0.12% | 0.14% | 0.18% | 0.25% | 0.42% | 0.89% | 3.48% | | ||
The same table for a much more precise Vincenty distance formula (using `node-vincenty` module): | ||
| lat | 0° | 10° | 20° | 30° | 40° | 50° | 60° | 70° | 80° | | ||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | ||
| 1km | 0.34% | 0.32% | 0.26% | 0.17% | 0.06% | 0.06% | 0.17% | 0.26% | 0.31% | | ||
| 100km | 0.34% | 0.32% | 0.26% | 0.17% | 0.06% | 0.06% | 0.16% | 0.25% | 0.28% | | ||
| 1000km | 0.36% | 0.34% | 0.3% | 0.23% | 0.16% | 0.11% | 0.17% | 0.55% | 3.08% | | ||
Errors for all other methods are similar. |
132
test/test.js
@@ -18,2 +18,9 @@ 'use strict'; | ||
test('cheapRuler constructor', function (t) { | ||
t.throws(function () { | ||
createRuler(); | ||
}, 'errors without latitude'); | ||
t.end(); | ||
}); | ||
test('distance', function (t) { | ||
@@ -29,2 +36,24 @@ for (var i = 0; i < points.length - 1; i++) { | ||
test('bearing', function (t) { | ||
for (var i = 0; i < points.length - 1; i++) { | ||
var expected = turf.bearing(turf.point(points[i]), turf.point(points[i + 1])); | ||
var actual = ruler.bearing(points[i], points[i + 1]); | ||
assertErr(t, expected, actual, 0.0001, 'bearing'); | ||
} | ||
t.pass('bearing within 0.01%'); | ||
t.end(); | ||
}); | ||
test('destination', function (t) { | ||
for (var i = 0; i < points.length; i++) { | ||
var bearing = (i % 360) - 180; | ||
var expected = turf.destination(turf.point(points[i]), 1.0, bearing, 'kilometers').geometry.coordinates; | ||
var actual = ruler.destination(points[i], 1.0, bearing); | ||
assertErr(t, expected[0], actual[0], 3e-7, 'destination longitude'); | ||
assertErr(t, expected[1], actual[1], 3e-7, 'destination latitude'); | ||
} | ||
t.pass('destination within 3e-7'); | ||
t.end(); | ||
}); | ||
test('lineDistance', function (t) { | ||
@@ -40,2 +69,14 @@ for (var i = 0; i < lines.length; i++) { | ||
test('area', function (t) { | ||
for (var i = 0; i < lines.length; i++) { | ||
if (lines[i].length < 3) continue; | ||
var poly = turf.polygon([lines[i].concat([lines[i][0]])]); | ||
var expected = turf.area(poly) / 1e6; | ||
var actual = ruler.area([lines[i]]); | ||
assertErr(t, expected, actual, 0.0002, 'area'); | ||
} | ||
t.pass('area within 0.02%'); | ||
t.end(); | ||
}); | ||
test('along', function (t) { | ||
@@ -54,2 +95,20 @@ for (var i = 0; i < lines.length; i++) { | ||
test('along with dist <= 0', function (t) { | ||
t.same(ruler.along(lines[0], -5), lines[0][0], 'first point'); | ||
t.end(); | ||
}); | ||
test('along with dist > length', function (t) { | ||
t.same(ruler.along(lines[0], 1000), lines[0][lines[0].length - 1], 'last point'); | ||
t.end(); | ||
}); | ||
test('pointOnLine', function (t) { | ||
// not Turf comparison because pointOnLine is bugged https://github.com/Turfjs/turf/issues/344 | ||
var line = [[-77.031669, 38.878605], [-77.029609, 38.881946]]; | ||
var p = ruler.pointOnLine(line, [-77.034076, 38.882017]).point; | ||
t.same(p, [-77.03051972665213, 38.88046894284234], 'pointOnLine'); | ||
t.end(); | ||
}); | ||
test('lineSlice', function (t) { | ||
@@ -74,33 +133,28 @@ for (var i = 0; i < lines.length; i++) { | ||
test('area', function (t) { | ||
test('lineSliceAlong', function (t) { | ||
for (var i = 0; i < lines.length; i++) { | ||
if (lines[i].length < 3) continue; | ||
var poly = turf.polygon([lines[i].concat([lines[i][0]])]); | ||
var expected = turf.area(poly) / 1e6; | ||
var actual = ruler.area([lines[i]]); | ||
assertErr(t, expected, actual, 0.0002, 'area'); | ||
} | ||
t.pass('area within 0.02%'); | ||
t.end(); | ||
}); | ||
if (i === 46) continue; // skip due to Turf bug https://github.com/Turfjs/turf/issues/351 | ||
test('bearing', function (t) { | ||
for (var i = 0; i < points.length - 1; i++) { | ||
var expected = turf.bearing(turf.point(points[i]), turf.point(points[i + 1])); | ||
var actual = ruler.bearing(points[i], points[i + 1]); | ||
assertErr(t, expected, actual, 0.0001, 'bearing'); | ||
var line = lines[i]; | ||
var dist = ruler.lineDistance(line); | ||
var start = ruler.along(line, dist * 0.3); | ||
var stop = ruler.along(line, dist * 0.7); | ||
var expected = ruler.lineDistance(turf.lineSlice( | ||
turf.point(start), turf.point(stop), turf.linestring(line)).geometry.coordinates); | ||
var actual = ruler.lineDistance(ruler.lineSliceAlong(dist * 0.3, dist * 0.7, line)); | ||
assertErr(t, expected, actual, 0.001, 'lineSliceAlong length'); | ||
} | ||
t.pass('bearing within 0.01%'); | ||
t.pass('lineSliceAlong length within 0.1%'); | ||
t.end(); | ||
}); | ||
test('destination', function (t) { | ||
for (var i = 0; i < points.length; i++) { | ||
var bearing = (i % 360) - 180; | ||
var expected = turf.destination(turf.point(points[i]), 1.0, bearing, 'kilometers').geometry.coordinates; | ||
var actual = ruler.destination(points[i], 1.0, bearing); | ||
assertErr(t, expected[0], actual[0], 3e-7, 'destination longitude'); | ||
assertErr(t, expected[1], actual[1], 3e-7, 'destination latitude'); | ||
} | ||
t.pass('destination within 3e-7'); | ||
test('lineSlice reverse', function (t) { | ||
var line = lines[0]; | ||
var dist = ruler.lineDistance(line); | ||
var start = ruler.along(line, dist * 0.7); | ||
var stop = ruler.along(line, dist * 0.3); | ||
var actual = ruler.lineDistance(ruler.lineSlice(start, stop, line)); | ||
t.equal(actual, 0.018665535420681036, 'lineSlice reversed length'); | ||
t.end(); | ||
@@ -122,10 +176,28 @@ }); | ||
test('pointOnLine', function (t) { | ||
// not Turf comparison because pointOnLine is bugged https://github.com/Turfjs/turf/issues/344 | ||
var line = [[-77.031669, 38.878605], [-77.029609, 38.881946]]; | ||
var p = ruler.pointOnLine(line, [-77.034076, 38.882017]).point; | ||
t.same(p, [-77.03051972665213, 38.88046894284234]); | ||
test('bufferBBox', function (t) { | ||
var bbox = [30, 38, 40, 39]; | ||
var bbox2 = ruler.bufferBBox(bbox, 1); | ||
t.same(bbox2, [29.989308794440007, 37.991016879283826, 40.01069120555999, 39.008983120716174], 'bufferBBox'); | ||
t.end(); | ||
}); | ||
test('insideBBox', function (t) { | ||
var bbox = [30, 38, 40, 39]; | ||
t.ok(ruler.insideBBox([35, 38.5], bbox), 'insideBBox inside'); | ||
t.notOk(ruler.insideBBox([45, 45], bbox), 'insideBBox outside'); | ||
t.end(); | ||
}); | ||
test('cheapRuler.fromTile', function (t) { | ||
var ruler1 = createRuler(50.5); | ||
var ruler2 = createRuler.fromTile(11041, 15); | ||
var p1 = [30.5, 50.5]; | ||
var p2 = [30.51, 50.51]; | ||
assertErr(t, ruler1.distance(p1, p2), ruler2.distance(p1, p2), 2e-5, 'cheapRuler.fromTile distance'); | ||
t.end(); | ||
}); | ||
function turfPointBuffer(p, distance) { | ||
@@ -132,0 +204,0 @@ var dist = Math.sqrt(2) * distance; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
44853
21
804
188
7