cheap-ruler
Advanced tools
Comparing version 2.5.1 to 3.0.0
@@ -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 @@ }, |
100
README.md
# 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
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
0
0
25699
13
5
416
1