Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cheap-ruler

Package Overview
Dependencies
Maintainers
15
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cheap-ruler - npm Package Compare versions

Comparing version 2.5.1 to 3.0.0

706

cheap-ruler.js

@@ -1,22 +0,8 @@

(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){
'use strict'; /* @flow */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.CheapRuler = factory());
}(this, (function () { 'use strict';
module.exports = cheapRuler;
module.exports.default = cheapRuler;
/**
* A collection of very fast approximations to common geodesic measurements. Useful for performance-sensitive code that measures things on a city scale.
*
* @param {number} lat latitude
* @param {string} [units='kilometers']
* @returns {CheapRuler}
* @example
* var ruler = cheapRuler(35.05, 'miles');
* //=ruler
*/
function cheapRuler(lat /*: number */, units /*: ?string */) {
return new CheapRuler(lat, units);
}
/**
* Multipliers for converting between units.

@@ -28,3 +14,3 @@ *

*/
var factors = cheapRuler.units = {
var factors = {
kilometers: 1,

@@ -40,374 +26,418 @@ miles: 1000 / 1609.344,

// Values that define WGS84 ellipsoid model of the Earth
var RE = 6378.137; // equatorial radius
var FE = 1 / 298.257223563; // flattening
var E2 = FE * (2 - FE);
var RAD = Math.PI / 180;
/**
* Creates a ruler object from tile coordinates (y and z). Convenient in tile-reduce scripts.
* A collection of very fast approximations to common geodesic measurements. Useful for performance-sensitive code that measures things on a city scale.
*
* @param {number} y
* @param {number} z
* @param {number} lat latitude
* @param {string} [units='kilometers']
* @returns {CheapRuler}
* @example
* var ruler = cheapRuler.fromTile(1567, 12);
* const ruler = cheapRuler(35.05, 'miles');
* //=ruler
*/
cheapRuler.fromTile = function (y, z, units) {
var CheapRuler = function CheapRuler(lat, units) {
if (lat === undefined) { throw new Error('No latitude given.'); }
if (units && !factors[units]) { throw new Error(("Unknown unit " + units + ". Use one of: " + (Object.keys(factors).join(', ')))); }
// Curvature formulas from https://en.wikipedia.org/wiki/Earth_radius#Meridional
var m = RAD * RE * (units ? factors[units] : 1);
var coslat = Math.cos(lat * RAD);
var w2 = 1 / (1 - E2 * (1 - coslat * coslat));
var w = Math.sqrt(w2);
// multipliers for converting longitude and latitude degrees into distance
this.kx = m * w * coslat; // based on normal radius of curvature
this.ky = m * w * w2 * (1 - E2); // based on meridonal radius of curvature
};
var staticAccessors = { units: { configurable: true } };
/**
* Given two points of the form [longitude, latitude], returns the distance.
*
* @param {Array<number>} a point [longitude, latitude]
* @param {Array<number>} b point [longitude, latitude]
* @returns {number} distance
* @example
* const distance = ruler.distance([30.5, 50.5], [30.51, 50.49]);
* //=distance
*/
CheapRuler.fromTile = function fromTile (y, z, units) {
var n = Math.PI * (1 - 2 * (y + 0.5) / Math.pow(2, z));
var lat = Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))) * 180 / Math.PI;
var lat = Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))) / RAD;
return new CheapRuler(lat, units);
};
function CheapRuler(lat, units) {
if (lat === undefined) throw new Error('No latitude given.');
if (units && !factors[units]) throw new Error('Unknown unit ' + units + '. Use one of: ' + Object.keys(factors).join(', '));
staticAccessors.units.get = function () {
return factors;
};
var m = units ? factors[units] : 1;
CheapRuler.prototype.distance = function distance (a, b) {
var dx = wrap(a[0] - b[0]) * this.kx;
var dy = (a[1] - b[1]) * this.ky;
return Math.sqrt(dx * dx + dy * dy);
};
var cos = Math.cos(lat * Math.PI / 180);
var cos2 = 2 * cos * cos - 1;
var cos3 = 2 * cos * cos2 - cos;
var cos4 = 2 * cos * cos3 - cos2;
var cos5 = 2 * cos * cos4 - cos3;
/**
* Returns the bearing between two points in angles.
*
* @param {Array<number>} a point [longitude, latitude]
* @param {Array<number>} b point [longitude, latitude]
* @returns {number} bearing
* @example
* const bearing = ruler.bearing([30.5, 50.5], [30.51, 50.49]);
* //=bearing
*/
CheapRuler.prototype.bearing = function bearing (a, b) {
var dx = wrap(b[0] - a[0]) * this.kx;
var dy = (b[1] - a[1]) * this.ky;
return Math.atan2(dx, dy) / RAD;
};
// multipliers for converting longitude and latitude degrees into distance (http://1.usa.gov/1Wb1bv7)
this.kx = m * (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5);
this.ky = m * (111.13209 - 0.56605 * cos2 + 0.0012 * cos4);
}
/**
* Returns a new point given distance and bearing from the starting point.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {number} dist distance
* @param {number} bearing
* @returns {Array<number>} point [longitude, latitude]
* @example
* const point = ruler.destination([30.5, 50.5], 0.1, 90);
* //=point
*/
CheapRuler.prototype.destination = function destination (p, dist, bearing) {
var a = bearing * RAD;
return this.offset(p,
Math.sin(a) * dist,
Math.cos(a) * dist);
};
CheapRuler.prototype = {
/**
* Given two points of the form [longitude, latitude], returns the distance.
*
* @param {Array<number>} a point [longitude, latitude]
* @param {Array<number>} b point [longitude, latitude]
* @returns {number} distance
* @example
* var distance = ruler.distance([30.5, 50.5], [30.51, 50.49]);
* //=distance
*/
distance: function (a, b) {
var dx = (a[0] - b[0]) * this.kx;
var dy = (a[1] - b[1]) * this.ky;
return Math.sqrt(dx * dx + dy * dy);
},
/**
* Returns a new point given easting and northing offsets (in ruler units) from the starting point.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {number} dx easting
* @param {number} dy northing
* @returns {Array<number>} point [longitude, latitude]
* @example
* const point = ruler.offset([30.5, 50.5], 10, 10);
* //=point
*/
CheapRuler.prototype.offset = function offset (p, dx, dy) {
return [
p[0] + dx / this.kx,
p[1] + dy / this.ky
];
};
/**
* Returns the bearing between two points in angles.
*
* @param {Array<number>} a point [longitude, latitude]
* @param {Array<number>} b point [longitude, latitude]
* @returns {number} bearing
* @example
* var bearing = ruler.bearing([30.5, 50.5], [30.51, 50.49]);
* //=bearing
*/
bearing: function (a, b) {
var dx = (b[0] - a[0]) * this.kx;
var dy = (b[1] - a[1]) * this.ky;
if (!dx && !dy) return 0;
var bearing = Math.atan2(dx, dy) * 180 / Math.PI;
if (bearing > 180) bearing -= 360;
return bearing;
},
/**
* Given a line (an array of points), returns the total line distance.
*
* @param {Array<Array<number>>} points [longitude, latitude]
* @returns {number} total line distance
* @example
* const length = ruler.lineDistance([
* [-67.031, 50.458], [-67.031, 50.534],
* [-66.929, 50.534], [-66.929, 50.458]
* ]);
* //=length
*/
CheapRuler.prototype.lineDistance = function lineDistance (points) {
var total = 0;
for (var i = 0; i < points.length - 1; i++) {
total += this.distance(points[i], points[i + 1]);
}
return total;
};
/**
* Returns a new point given distance and bearing from the starting point.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {number} dist distance
* @param {number} bearing
* @returns {Array<number>} point [longitude, latitude]
* @example
* var point = ruler.destination([30.5, 50.5], 0.1, 90);
* //=point
*/
destination: function (p, dist, bearing) {
var a = (90 - bearing) * Math.PI / 180;
return this.offset(p,
Math.cos(a) * dist,
Math.sin(a) * dist);
},
/**
* Given a polygon (an array of rings, where each ring is an array of points), returns the area.
*
* @param {Array<Array<Array<number>>>} polygon
* @returns {number} area value in the specified units (square kilometers by default)
* @example
* const area = ruler.area([[
* [-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534],
* [-66.929, 50.458], [-67.031, 50.458]
* ]]);
* //=area
*/
CheapRuler.prototype.area = function area (polygon) {
var sum = 0;
/**
* Returns a new point given easting and northing offsets (in ruler units) from the starting point.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {number} dx easting
* @param {number} dy northing
* @returns {Array<number>} point [longitude, latitude]
* @example
* var point = ruler.offset([30.5, 50.5], 10, 10);
* //=point
*/
offset: function (p, dx, dy) {
return [
p[0] + dx / this.kx,
p[1] + dy / this.ky
];
},
for (var i = 0; i < polygon.length; i++) {
var ring = polygon[i];
/**
* Given a line (an array of points), returns the total line distance.
*
* @param {Array<Array<number>>} points [longitude, latitude]
* @returns {number} total line distance
* @example
* var length = ruler.lineDistance([
* [-67.031, 50.458], [-67.031, 50.534],
* [-66.929, 50.534], [-66.929, 50.458]
* ]);
* //=length
*/
lineDistance: function (points) {
var total = 0;
for (var i = 0; i < points.length - 1; i++) {
total += this.distance(points[i], points[i + 1]);
for (var j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
sum += wrap(ring[j][0] - ring[k][0]) * (ring[j][1] + ring[k][1]) * (i ? -1 : 1);
}
return total;
},
}
/**
* Given a polygon (an array of rings, where each ring is an array of points), returns the area.
*
* @param {Array<Array<Array<number>>>} polygon
* @returns {number} area value in the specified units (square kilometers by default)
* @example
* var area = ruler.area([[
* [-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534],
* [-66.929, 50.458], [-67.031, 50.458]
* ]]);
* //=area
*/
area: function (polygon) {
var sum = 0;
return (Math.abs(sum) / 2) * this.kx * this.ky;
};
for (var i = 0; i < polygon.length; i++) {
var ring = polygon[i];
/**
* Returns the point at a specified distance along the line.
*
* @param {Array<Array<number>>} line
* @param {number} dist distance
* @returns {Array<number>} point [longitude, latitude]
* @example
* const point = ruler.along(line, 2.5);
* //=point
*/
CheapRuler.prototype.along = function along (line, dist) {
var sum = 0;
for (var j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
sum += (ring[j][0] - ring[k][0]) * (ring[j][1] + ring[k][1]) * (i ? -1 : 1);
}
}
if (dist <= 0) { return line[0]; }
return (Math.abs(sum) / 2) * this.kx * this.ky;
},
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); }
}
/**
* Returns the point at a specified distance along the line.
*
* @param {Array<Array<number>>} line
* @param {number} dist distance
* @returns {Array<number>} point [longitude, latitude]
* @example
* var point = ruler.along(line, 2.5);
* //=point
*/
along: function (line, dist) {
var sum = 0;
return line[line.length - 1];
};
if (dist <= 0) return line[0];
/**
* Returns the distance from a point `p` to a line segment `a` to `b`.
*
* @pointToSegmentDistance
* @param {Array<number>} p point [longitude, latitude]
* @param {Array<number>} p1 segment point 1 [longitude, latitude]
* @param {Array<number>} p2 segment point 2 [longitude, latitude]
* @returns {number} distance
* @example
* const distance = ruler.pointToSegmentDistance([-67.04, 50.5], [-67.05, 50.57], [-67.03, 50.54]);
* //=distance
*/
CheapRuler.prototype.pointToSegmentDistance = function pointToSegmentDistance (p, a, b) {
var x = a[0];
var y = a[1];
var dx = wrap(b[0] - x) * this.kx;
var dy = (b[1] - y) * this.ky;
var t = 0;
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);
}
if (dx !== 0 || dy !== 0) {
t = (wrap(p[0] - x) * this.kx * dx + (p[1] - y) * this.ky * dy) / (dx * dx + dy * dy);
return line[line.length - 1];
},
if (t > 1) {
x = b[0];
y = b[1];
/**
* Returns an object of the form {point, index, t}, where point is closest point on the line
* from the given point, index is the start index of the segment with the closest point,
* and t is a parameter from 0 to 1 that indicates where the closest point is on that segment.
*
* @pointOnLine
* @param {Array<Array<number>>} line
* @param {Array<number>} p point [longitude, latitude]
* @returns {Object} {point, index, t}
* @example
* var point = ruler.pointOnLine(line, [-67.04, 50.5]).point;
* //=point
*/
pointOnLine: function (line, p) {
var minDist = Infinity;
var minX, minY, minI, minT;
} else if (t > 0) {
x += (dx / this.kx) * t;
y += (dy / this.ky) * t;
}
}
for (var i = 0; i < line.length - 1; i++) {
dx = wrap(p[0] - x) * this.kx;
dy = (p[1] - y) * this.ky;
var x = line[i][0];
var y = line[i][1];
var dx = (line[i + 1][0] - x) * this.kx;
var dy = (line[i + 1][1] - y) * this.ky;
return Math.sqrt(dx * dx + dy * dy);
};
if (dx !== 0 || dy !== 0) {
/**
* Returns an object of the form {point, index, t}, where point is closest point on the line
* from the given point, index is the start index of the segment with the closest point,
* and t is a parameter from 0 to 1 that indicates where the closest point is on that segment.
*
* @param {Array<Array<number>>} line
* @param {Array<number>} p point [longitude, latitude]
* @returns {Object} {point, index, t}
* @example
* const point = ruler.pointOnLine(line, [-67.04, 50.5]).point;
* //=point
*/
CheapRuler.prototype.pointOnLine = function pointOnLine (line, p) {
var minDist = Infinity;
var minX, minY, minI, minT;
var t = ((p[0] - x) * this.kx * dx + (p[1] - y) * this.ky * dy) / (dx * dx + dy * dy);
for (var i = 0; i < line.length - 1; i++) {
if (t > 1) {
x = line[i + 1][0];
y = line[i + 1][1];
var x = line[i][0];
var y = line[i][1];
var dx = wrap(line[i + 1][0] - x) * this.kx;
var dy = (line[i + 1][1] - y) * this.ky;
var t = 0;
} else if (t > 0) {
x += (dx / this.kx) * t;
y += (dy / this.ky) * t;
}
}
if (dx !== 0 || dy !== 0) {
t = (wrap(p[0] - x) * this.kx * dx + (p[1] - y) * this.ky * dy) / (dx * dx + dy * dy);
dx = (p[0] - x) * this.kx;
dy = (p[1] - y) * this.ky;
if (t > 1) {
x = line[i + 1][0];
y = line[i + 1][1];
var sqDist = dx * dx + dy * dy;
if (sqDist < minDist) {
minDist = sqDist;
minX = x;
minY = y;
minI = i;
minT = t;
} else if (t > 0) {
x += (dx / this.kx) * t;
y += (dy / this.ky) * t;
}
}
return {
point: [minX, minY],
index: minI,
t: Math.max(0, Math.min(1, minT))
};
},
dx = wrap(p[0] - x) * this.kx;
dy = (p[1] - y) * this.ky;
/**
* Returns a part of the given line between the start and the stop points (or their closest points on the line).
*
* @param {Array<number>} start point [longitude, latitude]
* @param {Array<number>} stop point [longitude, latitude]
* @param {Array<Array<number>>} line
* @returns {Array<Array<number>>} line part of a line
* @example
* var line2 = ruler.lineSlice([-67.04, 50.5], [-67.05, 50.56], line1);
* //=line2
*/
lineSlice: function (start, stop, line) {
var p1 = this.pointOnLine(line, start);
var p2 = this.pointOnLine(line, stop);
if (p1.index > p2.index || (p1.index === p2.index && p1.t > p2.t)) {
var tmp = p1;
p1 = p2;
p2 = tmp;
var sqDist = dx * dx + dy * dy;
if (sqDist < minDist) {
minDist = sqDist;
minX = x;
minY = y;
minI = i;
minT = t;
}
}
var slice = [p1.point];
return {
point: [minX, minY],
index: minI,
t: Math.max(0, Math.min(1, minT))
};
};
var l = p1.index + 1;
var r = p2.index;
/**
* Returns a part of the given line between the start and the stop points (or their closest points on the line).
*
* @param {Array<number>} start point [longitude, latitude]
* @param {Array<number>} stop point [longitude, latitude]
* @param {Array<Array<number>>} line
* @returns {Array<Array<number>>} line part of a line
* @example
* const line2 = ruler.lineSlice([-67.04, 50.5], [-67.05, 50.56], line1);
* //=line2
*/
CheapRuler.prototype.lineSlice = function lineSlice (start, stop, line) {
var p1 = this.pointOnLine(line, start);
var p2 = this.pointOnLine(line, stop);
if (!equals(line[l], slice[0]) && l <= r)
slice.push(line[l]);
if (p1.index > p2.index || (p1.index === p2.index && p1.t > p2.t)) {
var tmp = p1;
p1 = p2;
p2 = tmp;
}
for (var i = l + 1; i <= r; i++) {
slice.push(line[i]);
}
var slice = [p1.point];
if (!equals(line[r], p2.point))
slice.push(p2.point);
var l = p1.index + 1;
var r = p2.index;
return slice;
},
if (!equals(line[l], slice[0]) && l <= r)
{ slice.push(line[l]); }
/**
* Returns a part of the given line between the start and the stop points indicated by distance along the line.
*
* @param {number} start distance
* @param {number} stop distance
* @param {Array<Array<number>>} line
* @returns {Array<Array<number>>} line part of a line
* @example
* var line2 = ruler.lineSliceAlong(10, 20, line1);
* //=line2
*/
lineSliceAlong: function (start, stop, line) {
var sum = 0;
var slice = [];
for (var i = l + 1; i <= r; i++) {
slice.push(line[i]);
}
for (var i = 0; i < line.length - 1; i++) {
var p0 = line[i];
var p1 = line[i + 1];
var d = this.distance(p0, p1);
if (!equals(line[r], p2.point))
{ slice.push(p2.point); }
sum += d;
return slice;
};
if (sum > start && slice.length === 0) {
slice.push(interpolate(p0, p1, (start - (sum - d)) / d));
}
/**
* Returns a part of the given line between the start and the stop points indicated by distance along the line.
*
* @param {number} start distance
* @param {number} stop distance
* @param {Array<Array<number>>} line
* @returns {Array<Array<number>>} line part of a line
* @example
* const line2 = ruler.lineSliceAlong(10, 20, line1);
* //=line2
*/
CheapRuler.prototype.lineSliceAlong = function lineSliceAlong (start, stop, line) {
var sum = 0;
var slice = [];
if (sum >= stop) {
slice.push(interpolate(p0, p1, (stop - (sum - d)) / d));
return slice;
}
for (var i = 0; i < line.length - 1; i++) {
var p0 = line[i];
var p1 = line[i + 1];
var d = this.distance(p0, p1);
if (sum > start) slice.push(p1);
sum += d;
if (sum > start && slice.length === 0) {
slice.push(interpolate(p0, p1, (start - (sum - d)) / d));
}
return slice;
},
if (sum >= stop) {
slice.push(interpolate(p0, p1, (stop - (sum - d)) / d));
return slice;
}
/**
* Given a point, returns a bounding box object ([w, s, e, n]) created from the given point buffered by a given distance.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {number} buffer
* @returns {Array<number>} box object ([w, s, e, n])
* @example
* var bbox = ruler.bufferPoint([30.5, 50.5], 0.01);
* //=bbox
*/
bufferPoint: function (p, buffer) {
var v = buffer / this.ky;
var h = buffer / this.kx;
return [
p[0] - h,
p[1] - v,
p[0] + h,
p[1] + v
];
},
if (sum > start) { slice.push(p1); }
}
/**
* Given a bounding box, returns the box buffered by a given distance.
*
* @param {Array<number>} box object ([w, s, e, n])
* @param {number} buffer
* @returns {Array<number>} box object ([w, s, e, n])
* @example
* var bbox = ruler.bufferBBox([30.5, 50.5, 31, 51], 0.2);
* //=bbox
*/
bufferBBox: function (bbox, buffer) {
var v = buffer / this.ky;
var h = buffer / this.kx;
return [
bbox[0] - h,
bbox[1] - v,
bbox[2] + h,
bbox[3] + v
];
},
return slice;
};
/**
* Returns true if the given point is inside in the given bounding box, otherwise false.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {Array<number>} box object ([w, s, e, n])
* @returns {boolean}
* @example
* var inside = ruler.insideBBox([30.5, 50.5], [30, 50, 31, 51]);
* //=inside
*/
insideBBox: function (p, bbox) {
return p[0] >= bbox[0] &&
p[0] <= bbox[2] &&
p[1] >= bbox[1] &&
p[1] <= bbox[3];
}
/**
* Given a point, returns a bounding box object ([w, s, e, n]) created from the given point buffered by a given distance.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {number} buffer
* @returns {Array<number>} box object ([w, s, e, n])
* @example
* const bbox = ruler.bufferPoint([30.5, 50.5], 0.01);
* //=bbox
*/
CheapRuler.prototype.bufferPoint = function bufferPoint (p, buffer) {
var v = buffer / this.ky;
var h = buffer / this.kx;
return [
p[0] - h,
p[1] - v,
p[0] + h,
p[1] + v
];
};
/**
* Given a bounding box, returns the box buffered by a given distance.
*
* @param {Array<number>} box object ([w, s, e, n])
* @param {number} buffer
* @returns {Array<number>} box object ([w, s, e, n])
* @example
* const bbox = ruler.bufferBBox([30.5, 50.5, 31, 51], 0.2);
* //=bbox
*/
CheapRuler.prototype.bufferBBox = function bufferBBox (bbox, buffer) {
var v = buffer / this.ky;
var h = buffer / this.kx;
return [
bbox[0] - h,
bbox[1] - v,
bbox[2] + h,
bbox[3] + v
];
};
/**
* Returns true if the given point is inside in the given bounding box, otherwise false.
*
* @param {Array<number>} p point [longitude, latitude]
* @param {Array<number>} box object ([w, s, e, n])
* @returns {boolean}
* @example
* const inside = ruler.insideBBox([30.5, 50.5], [30, 50, 31, 51]);
* //=inside
*/
CheapRuler.prototype.insideBBox = function insideBBox (p, bbox) {
return wrap(p[0] - bbox[0]) >= 0 &&
wrap(p[0] - bbox[2]) <= 0 &&
p[1] >= bbox[1] &&
p[1] <= bbox[3];
};
Object.defineProperties( CheapRuler, staticAccessors );
function equals(a, b) {

@@ -418,3 +448,3 @@ return a[0] === b[0] && a[1] === b[1];

function interpolate(a, b, t) {
var dx = b[0] - a[0];
var dx = wrap(b[0] - a[0]);
var dy = b[1] - a[1];

@@ -427,3 +457,11 @@ return [

},{}]},{},[1])(1)
});
// normalize a degree value into [-180..180] range
function wrap(deg) {
while (deg < -180) { deg += 360; }
while (deg > 180) { deg -= 360; }
return deg;
}
return CheapRuler;
})));

@@ -1,1 +0,1 @@

!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).cheapRuler=t()}}(function(){return function t(n,e,r){function i(u,f){if(!e[u]){if(!n[u]){var a="function"==typeof require&&require;if(!f&&a)return a(u,!0);if(o)return o(u,!0);var s=new Error("Cannot find module '"+u+"'");throw s.code="MODULE_NOT_FOUND",s}var h=e[u]={exports:{}};n[u][0].call(h.exports,function(t){var e=n[u][1][t];return i(e||t)},h,h.exports,t,n,e,r)}return e[u].exports}for(var o="function"==typeof require&&require,u=0;u<r.length;u++)i(r[u]);return i}({1:[function(t,n,e){"use strict";function r(t,n){return new i(t,n)}function i(t,n){if(void 0===t)throw new Error("No latitude given.");if(n&&!f[n])throw new Error("Unknown unit "+n+". Use one of: "+Object.keys(f).join(", "));var e=n?f[n]:1,r=Math.cos(t*Math.PI/180),i=2*r*r-1,o=2*r*i-r,u=2*r*o-i,a=2*r*u-o;this.kx=e*(111.41513*r-.09455*o+12e-5*a),this.ky=e*(111.13209-.56605*i+.0012*u)}function o(t,n){return t[0]===n[0]&&t[1]===n[1]}function u(t,n,e){var r=n[0]-t[0],i=n[1]-t[1];return[t[0]+r*e,t[1]+i*e]}n.exports=r,n.exports.default=r;var f=r.units={kilometers:1,miles:1e3/1609.344,nauticalmiles:1e3/1852,meters:1e3,metres:1e3,yards:1e3/.9144,feet:1e3/.3048,inches:1e3/.0254};r.fromTile=function(t,n,e){var r=Math.PI*(1-2*(t+.5)/Math.pow(2,n));return new i(180*Math.atan(.5*(Math.exp(r)-Math.exp(-r)))/Math.PI,e)},i.prototype={distance:function(t,n){var e=(t[0]-n[0])*this.kx,r=(t[1]-n[1])*this.ky;return Math.sqrt(e*e+r*r)},bearing:function(t,n){var e=(n[0]-t[0])*this.kx,r=(n[1]-t[1])*this.ky;if(!e&&!r)return 0;var i=180*Math.atan2(e,r)/Math.PI;return i>180&&(i-=360),i},destination:function(t,n,e){var r=(90-e)*Math.PI/180;return this.offset(t,Math.cos(r)*n,Math.sin(r)*n)},offset:function(t,n,e){return[t[0]+n/this.kx,t[1]+e/this.ky]},lineDistance:function(t){for(var n=0,e=0;e<t.length-1;e++)n+=this.distance(t[e],t[e+1]);return n},area:function(t){for(var n=0,e=0;e<t.length;e++)for(var r=t[e],i=0,o=r.length,u=o-1;i<o;u=i++)n+=(r[i][0]-r[u][0])*(r[i][1]+r[u][1])*(e?-1:1);return Math.abs(n)/2*this.kx*this.ky},along:function(t,n){var e=0;if(n<=0)return t[0];for(var r=0;r<t.length-1;r++){var i=t[r],o=t[r+1],f=this.distance(i,o);if((e+=f)>n)return u(i,o,(n-(e-f))/f)}return t[t.length-1]},pointOnLine:function(t,n){for(var e,r,i,o,u=1/0,f=0;f<t.length-1;f++){var a=t[f][0],s=t[f][1],h=(t[f+1][0]-a)*this.kx,c=(t[f+1][1]-s)*this.ky;if(0!==h||0!==c){var l=((n[0]-a)*this.kx*h+(n[1]-s)*this.ky*c)/(h*h+c*c);l>1?(a=t[f+1][0],s=t[f+1][1]):l>0&&(a+=h/this.kx*l,s+=c/this.ky*l)}var d=(h=(n[0]-a)*this.kx)*h+(c=(n[1]-s)*this.ky)*c;d<u&&(u=d,e=a,r=s,i=f,o=l)}return{point:[e,r],index:i,t:Math.max(0,Math.min(1,o))}},lineSlice:function(t,n,e){var r=this.pointOnLine(e,t),i=this.pointOnLine(e,n);if(r.index>i.index||r.index===i.index&&r.t>i.t){var u=r;r=i,i=u}var f=[r.point],a=r.index+1,s=i.index;!o(e[a],f[0])&&a<=s&&f.push(e[a]);for(var h=a+1;h<=s;h++)f.push(e[h]);return o(e[s],i.point)||f.push(i.point),f},lineSliceAlong:function(t,n,e){for(var r=0,i=[],o=0;o<e.length-1;o++){var f=e[o],a=e[o+1],s=this.distance(f,a);if((r+=s)>t&&0===i.length&&i.push(u(f,a,(t-(r-s))/s)),r>=n)return i.push(u(f,a,(n-(r-s))/s)),i;r>t&&i.push(a)}return i},bufferPoint:function(t,n){var e=n/this.ky,r=n/this.kx;return[t[0]-r,t[1]-e,t[0]+r,t[1]+e]},bufferBBox:function(t,n){var e=n/this.ky,r=n/this.kx;return[t[0]-r,t[1]-e,t[2]+r,t[3]+e]},insideBBox:function(t,n){return t[0]>=n[0]&&t[0]<=n[2]&&t[1]>=n[1]&&t[1]<=n[3]}}},{}]},{},[1])(1)});
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t=t||self).CheapRuler=n()}(this,(function(){"use strict";var t={kilometers:1,miles:1e3/1609.344,nauticalmiles:1e3/1852,meters:1e3,metres:1e3,yards:1e3/.9144,feet:1e3/.3048,inches:1e3/.0254},n=1/298.257223563,e=n*(2-n),i=Math.PI/180,r=function(n,r){if(void 0===n)throw new Error("No latitude given.");if(r&&!t[r])throw new Error("Unknown unit "+r+". Use one of: "+Object.keys(t).join(", "));var o=6378.137*i*(r?t[r]:1),s=Math.cos(n*i),h=1/(1-e*(1-s*s)),a=Math.sqrt(h);this.kx=o*a*s,this.ky=o*a*h*(1-e)},o={units:{configurable:!0}};function s(t,n){return t[0]===n[0]&&t[1]===n[1]}function h(t,n,e){var i=a(n[0]-t[0]),r=n[1]-t[1];return[t[0]+i*e,t[1]+r*e]}function a(t){for(;t<-180;)t+=360;for(;t>180;)t-=360;return t}return r.fromTile=function(t,n,e){var o=Math.PI*(1-2*(t+.5)/Math.pow(2,n)),s=Math.atan(.5*(Math.exp(o)-Math.exp(-o)))/i;return new r(s,e)},o.units.get=function(){return t},r.prototype.distance=function(t,n){var e=a(t[0]-n[0])*this.kx,i=(t[1]-n[1])*this.ky;return Math.sqrt(e*e+i*i)},r.prototype.bearing=function(t,n){var e=a(n[0]-t[0])*this.kx,r=(n[1]-t[1])*this.ky;return Math.atan2(e,r)/i},r.prototype.destination=function(t,n,e){var r=e*i;return this.offset(t,Math.sin(r)*n,Math.cos(r)*n)},r.prototype.offset=function(t,n,e){return[t[0]+n/this.kx,t[1]+e/this.ky]},r.prototype.lineDistance=function(t){for(var n=0,e=0;e<t.length-1;e++)n+=this.distance(t[e],t[e+1]);return n},r.prototype.area=function(t){for(var n=0,e=0;e<t.length;e++)for(var i=t[e],r=0,o=i.length,s=o-1;r<o;s=r++)n+=a(i[r][0]-i[s][0])*(i[r][1]+i[s][1])*(e?-1:1);return Math.abs(n)/2*this.kx*this.ky},r.prototype.along=function(t,n){var e=0;if(n<=0)return t[0];for(var i=0;i<t.length-1;i++){var r=t[i],o=t[i+1],s=this.distance(r,o);if((e+=s)>n)return h(r,o,(n-(e-s))/s)}return t[t.length-1]},r.prototype.pointToSegmentDistance=function(t,n,e){var i=n[0],r=n[1],o=a(e[0]-i)*this.kx,s=(e[1]-r)*this.ky,h=0;return 0===o&&0===s||((h=(a(t[0]-i)*this.kx*o+(t[1]-r)*this.ky*s)/(o*o+s*s))>1?(i=e[0],r=e[1]):h>0&&(i+=o/this.kx*h,r+=s/this.ky*h)),o=a(t[0]-i)*this.kx,s=(t[1]-r)*this.ky,Math.sqrt(o*o+s*s)},r.prototype.pointOnLine=function(t,n){for(var e,i,r,o,s=1/0,h=0;h<t.length-1;h++){var u=t[h][0],f=t[h][1],p=a(t[h+1][0]-u)*this.kx,c=(t[h+1][1]-f)*this.ky,y=0;0===p&&0===c||((y=(a(n[0]-u)*this.kx*p+(n[1]-f)*this.ky*c)/(p*p+c*c))>1?(u=t[h+1][0],f=t[h+1][1]):y>0&&(u+=p/this.kx*y,f+=c/this.ky*y));var k=(p=a(n[0]-u)*this.kx)*p+(c=(n[1]-f)*this.ky)*c;k<s&&(s=k,e=u,i=f,r=h,o=y)}return{point:[e,i],index:r,t:Math.max(0,Math.min(1,o))}},r.prototype.lineSlice=function(t,n,e){var i=this.pointOnLine(e,t),r=this.pointOnLine(e,n);if(i.index>r.index||i.index===r.index&&i.t>r.t){var o=i;i=r,r=o}var h=[i.point],a=i.index+1,u=r.index;!s(e[a],h[0])&&a<=u&&h.push(e[a]);for(var f=a+1;f<=u;f++)h.push(e[f]);return s(e[u],r.point)||h.push(r.point),h},r.prototype.lineSliceAlong=function(t,n,e){for(var i=0,r=[],o=0;o<e.length-1;o++){var s=e[o],a=e[o+1],u=this.distance(s,a);if((i+=u)>t&&0===r.length&&r.push(h(s,a,(t-(i-u))/u)),i>=n)return r.push(h(s,a,(n-(i-u))/u)),r;i>t&&r.push(a)}return r},r.prototype.bufferPoint=function(t,n){var e=n/this.ky,i=n/this.kx;return[t[0]-i,t[1]-e,t[0]+i,t[1]+e]},r.prototype.bufferBBox=function(t,n){var e=n/this.ky,i=n/this.kx;return[t[0]-i,t[1]-e,t[2]+i,t[3]+e]},r.prototype.insideBBox=function(t,n){return a(t[0]-n[0])>=0&&a(t[0]-n[2])<=0&&t[1]>=n[1]&&t[1]<=n[3]},Object.defineProperties(r,o),r}));
{
"name": "cheap-ruler",
"version": "2.5.1",
"version": "3.0.0",
"description": "A collection of fast approximations to common geographic measurements.",
"main": "index.js",
"jsdelivr": "cheap-ruler.js",
"unpkg": "cheap-ruler.js",
"types": "index.d.ts",
"main": "cheap-ruler.js",
"module": "index.js",
"jsdelivr": "cheap-ruler.min.js",
"unpkg": "cheap-ruler.min.js",
"types": "cheap-ruler.d.ts",
"dependencies": {},
"devDependencies": {
"@turf/turf": "^5.0.4",
"@rollup/plugin-buble": "^0.21.3",
"@turf/turf": "^5.1.6",
"benchmark": "^2.1.4",
"browserify": "^14.5.0",
"eslint": "^4.12.0",
"eslint-config-mourner": "^2.0.3",
"eslint": "^7.0.0",
"eslint-config-mourner": "^3.0.0",
"esm": "^3.2.25",
"node-vincenty": "0.0.6",
"nyc": "^11.3.0",
"tape": "^4.8.0",
"typescript": "^2.6.2",
"uglify-js": "^3.2.0"
"nyc": "^15.0.1",
"rollup": "^2.10.2",
"rollup-plugin-terser": "^5.3.0",
"tape": "^5.0.0",
"ts-node": "^8.10.1",
"typescript": "^3.9.2"
},
"scripts": {
"pretest": "eslint index.js bench test/*.js && npm run types",
"test": "tape test/test.js",
"types": "tsc test/types.ts && rm test/types.js",
"build": "browserify index.js -s cheapRuler > cheap-ruler.js",
"build-min": "browserify index.js -s cheapRuler | uglifyjs -c -m > cheap-ruler.min.js",
"prepare": "npm run build && npm run build-min",
"pretest": "eslint index.js bench test/*.js",
"test": "tape -r esm test/test.js",
"posttest": "npm run build && npm run types",
"types": "ts-node test/types.ts",
"build": "rollup -c",
"prepublishOnly": "npm test",
"cov": "nyc tape test/test.js"

@@ -30,0 +34,0 @@ },

# cheap-ruler [![Build Status](https://travis-ci.org/mapbox/cheap-ruler.svg?branch=master)](https://travis-ci.org/mapbox/cheap-ruler) [![](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects)
A collection of very fast approximations to common geodesic measurements.
Useful for performance-sensitive code that measures things on a city scale.
Useful for performance-sensitive code that measures things on a city scale. Can be an order of magnitude faster than corresponding [Turf](http://turfjs.org/) methods.
The approximations are based on an [FCC-approved formula of ellipsoidal Earth projection](https://www.gpo.gov/fdsys/pkg/CFR-2005-title47-vol4/pdf/CFR-2005-title47-vol4-sec73-208.pdf).
The approximations are based on the [WGS84 ellipsoid model of the Earth](https://en.wikipedia.org/wiki/Earth_radius#Meridional), projecting coordinates to a flat surface that approximates the ellipsoid around a certain latitude.
For distances under 500 kilometers and not on the poles,

@@ -12,27 +12,7 @@ the results are very precise — within [0.1% margin of error](#precision)

## Performance
Compared to corresponding [Turf](http://turfjs.org/) methods (using Node v6):
- `distance`: ~31x faster
- `bearing`: ~3.6x faster
- `destination`: ~7.2x faster
- `lineDistance`: ~31x faster
- `area`: ~3.4x faster
- `along`: ~31x faster
- `pointOnLine`: ~78x faster
- `lineSlice`: ~60x faster
Additional utility methods:
- `lineSliceAlong`: ~285x faster than `turf.lineSlice(turf.along(...`
- `bufferPoint`: ~260x faster than creating a bounding box with two diagonal `turf.destination` calls
- `bufferBBox`: ~260x faster (likewise)
- `insideBBox`: ~19x faster than `turf.inside(turf.point(p), turf.bboxPolygon(bbox))`
## Usage
```js
var ruler = cheapRuler(35.05, 'miles');
// ...
var ruler = new CheapRuler(35.05, 'miles'); // calculations around latitude 35
...
var distance = ruler.distance([30.51, 50.32], [30.52, 50.312]);

@@ -50,3 +30,3 @@ var lineLength = ruler.lineDistance(line.geometry.coordinates);

#### cheapRuler(latitude[, units])
#### new CheapRuler(latitude[, units])

@@ -56,8 +36,12 @@ Creates a ruler object that will approximate measurements around the given latitude.

#### cheapRuler.fromTile(y, z[, units])
```js
const ruler = new CheapRuler(50.5, 'meters');
````
Creates a ruler object from tile coordinates (`y` and `z`). Convenient in `tile-reduce` scripts.
#### CheapRuler.fromTile(y, z[, units])
Creates a ruler object from tile coordinates (`y` and `z`).
```js
var ruler = cheapRuler.fromTile(1567, 12);
const ruler = CheapRuler.fromTile(1567, 12);
```

@@ -72,3 +56,3 @@

```js
var distance = ruler.distance([30.5, 50.5], [30.51, 50.49]);
const distance = ruler.distance([30.5, 50.5], [30.51, 50.49]);
```

@@ -81,3 +65,3 @@

```js
var bearing = ruler.bearing([30.5, 50.5], [30.51, 50.49]);
const bearing = ruler.bearing([30.5, 50.5], [30.51, 50.49]);
```

@@ -90,3 +74,3 @@

```js
var point = ruler.destination([30.5, 50.5], 0.1, 90);
const point = ruler.destination([30.5, 50.5], 0.1, 90);
```

@@ -99,3 +83,3 @@

```js
var point = ruler.offset([30.5, 50.5], 10, 5); // 10km east and 5km north
const point = ruler.offset([30.5, 50.5], 10, 5); // 10km east and 5km north
```

@@ -108,3 +92,3 @@

```js
var length = ruler.lineDistance([
const length = ruler.lineDistance([
[-67.031, 50.458], [-67.031, 50.534],

@@ -122,3 +106,3 @@ [-66.929, 50.534], [-66.929, 50.458]

```js
var area = ruler.area([[
const area = ruler.area([[
[-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534],

@@ -129,2 +113,11 @@ [-66.929, 50.458], [-67.031, 50.458]

#### pointToSegmentDistance(p, a, b)
Returns the distance from a point `p` to a line segment `a` to `b`.
```js
const distance = ruler.pointToSegmentDistance([-77.034076, 38.882017],
[-77.031669, 38.878605] [-77.029609, 38.881946]);
````
#### along(line, dist)

@@ -135,3 +128,3 @@

```js
var point = ruler.along(line, 2.5);
const point = ruler.along(line, 2.5);
```

@@ -141,7 +134,8 @@

Returns an object of the form `{point, index}` where `point` is closest point on the line from the given point,
and `index` is the start index of the segment with the closest point.
Returns an object of the form `{point, index, t}`, where `point` is closest point on the line from the given point,
`index` is the start index of the segment with the closest point, and `t` is a parameter from 0 to 1 that indicates
where the closest point is on that segment.
```js
var point = ruler.pointOnLine(line, [-67.04, 50.5]).point;
const point = ruler.pointOnLine(line, [-67.04, 50.5]).point;
```

@@ -154,3 +148,3 @@

```js
ruler.lineSlice([-67.04, 50.5], [-67.05, 50.56], line);
const part = ruler.lineSlice([-67.04, 50.5], [-67.05, 50.56], line);
```

@@ -163,3 +157,3 @@

```js
ruler.lineSliceAlong(10, 20, line);
const part = ruler.lineSliceAlong(10, 20, line);
```

@@ -172,3 +166,3 @@

```js
var bbox = ruler.bufferPoint([30.5, 50.5], 0.01);
const bbox = ruler.bufferPoint([30.5, 50.5], 0.01);
```

@@ -181,3 +175,3 @@

```js
var bbox = ruler.bufferBBox([30.5, 50.5, 31, 51], 0.2);
const bbox = ruler.bufferBBox([30.5, 50.5, 31, 51], 0.2);
```

@@ -190,3 +184,3 @@

```js
var inside = ruler.insideBBox([30.5, 50.5], [30, 50, 31, 51]);
const inside = ruler.insideBBox([30.5, 50.5], [30, 50, 31, 51]);
```

@@ -196,3 +190,3 @@

Multipliers for converting between units are also exposed in `cheapRuler.units`:
Multipliers for converting between units are also exposed in `CheapRuler.units`:

@@ -210,6 +204,6 @@ ```js

// get distance between points in feet
var distanceInFeet = ruler.distance(a, b) * cheapRuler.units.feet;
const distanceInFeet = ruler.distance(a, b) * cheapRuler.units.feet;
// make a bbox from a point with a 200 inch buffer
var box = ruler.bufferPoint(p, 200 / cheapRuler.units.inches);
const box = ruler.bufferPoint(p, 200 / cheapRuler.units.inches);
```

@@ -220,3 +214,4 @@

- NPM: `npm install cheap-ruler`
- Browser build (CDN): https://unpkg.com/cheap-ruler@2.5.0/cheap-ruler.js
- [Browser build on CDN](https://unpkg.com/cheap-ruler@2.5.1/cheap-ruler.js)
- [Browser build on CDN (minified)](https://unpkg.com/cheap-ruler@2.5.1/cheap-ruler.min.js)

@@ -231,6 +226,11 @@ ## Precision

| 1km | 0% | 0% | 0% | 0% | 0% | 0% | 0% | 0% | 0% |
| 100km | 0% | 0% | 0% | 0% | 0% | 0% | 0.01% | 0.01% | 0.04% |
| 500km | 0% | 0% | 0% | 0.01% | 0.02% | 0.04% | 0.08% | 0.2% | 0.83% |
| 1000km | 0% | 0% | 0.02% | 0.04% | 0.07% | 0.15% | 0.31% | 0.78% | 3.36% |
| 100km | 0% | 0% | 0% | 0% | 0% | 0% | 0% | 0.01% | 0.03% |
| 500km | 0.01% | 0.01% | 0.01% | 0.01% | 0.02% | 0.04% | 0.08% | 0.2% | 0.83% |
| 1000km | 0.03% | 0.03% | 0.04% | 0.06% | 0.1% | 0.17% | 0.33% | 0.8% | 3.38% |
Errors for all other methods are similar.
## Related
- [cheap-ruler-cpp](https://github.com/mapbox/cheap-ruler-cpp) – C++ port of this library
- [flat-projection](https://github.com/Turbo87/flat-projection-rs) – Rust library based on the same concept

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc