cheap-ruler
Advanced tools
+372
-334
@@ -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})); |
+1
-1
| ISC License | ||
| Copyright (c) 2016, Mapbox | ||
| Copyright (c) 2020, Mapbox | ||
@@ -5,0 +5,0 @@ Permission to use, copy, modify, and/or distribute this software for any purpose |
+23
-19
| { | ||
| "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 @@ }, |
+50
-50
| # cheap-ruler [](https://travis-ci.org/mapbox/cheap-ruler) [](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 |
-36
| declare function cheapRuler(lat: number, units?: string): cheapRuler.CheapRuler; | ||
| declare namespace cheapRuler { | ||
| type BBox = [number, number, number, number] | ||
| type Point = [number, number] | ||
| type Line = Point[] | ||
| type Points = Point[] | ||
| type Polygon = Point[][] | ||
| class CheapRuler { | ||
| distance(a: cheapRuler.Point, b: Point): number; | ||
| bearing(a: Point, b: Point): number; | ||
| destination(p: Point, dist: number, bearing: number): Point; | ||
| lineDistance(points: Points): number; | ||
| area(polygon: Polygon): number; | ||
| along(line: Line, dist: number): Point; | ||
| pointOnLine(line: Line, p: Point): {point: Point, index: number, t: number}; | ||
| lineSlice(start: Point, stop: Point, line: Line): Line; | ||
| lineSliceAlong(start: number, stop: number, line: Line): Line; | ||
| bufferPoint(p: Point, buffer: number): BBox; | ||
| bufferBBox(bbox: BBox, buffer: number): BBox; | ||
| insideBBox(p: Point, bbox: BBox): boolean; | ||
| offset(p: Point, dx: number, dy: number): Point; | ||
| } | ||
| const units: { | ||
| kilometers: number; | ||
| miles: number; | ||
| nauticalmiles: number; | ||
| meters: number; | ||
| metres: number; | ||
| yards: number; | ||
| feet: number; | ||
| inches: number; | ||
| } | ||
| function fromTile(y: number, z: number, units?: string): CheapRuler; | ||
| } | ||
| export = cheapRuler; |
-421
| 'use strict'; /* @flow */ | ||
| 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. | ||
| * | ||
| * @example | ||
| * // convert 50 meters to yards | ||
| * 50 * cheapRuler.units.yards / cheapRuler.units.meters; | ||
| */ | ||
| var factors = cheapRuler.units = { | ||
| kilometers: 1, | ||
| miles: 1000 / 1609.344, | ||
| nauticalmiles: 1000 / 1852, | ||
| meters: 1000, | ||
| metres: 1000, | ||
| yards: 1000 / 0.9144, | ||
| feet: 1000 / 0.3048, | ||
| inches: 1000 / 0.0254 | ||
| }; | ||
| /** | ||
| * Creates a ruler object from tile coordinates (y and z). Convenient in tile-reduce scripts. | ||
| * | ||
| * @param {number} y | ||
| * @param {number} z | ||
| * @param {string} [units='kilometers'] | ||
| * @returns {CheapRuler} | ||
| * @example | ||
| * var ruler = cheapRuler.fromTile(1567, 12); | ||
| * //=ruler | ||
| */ | ||
| cheapRuler.fromTile = function (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; | ||
| 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(', ')); | ||
| var m = units ? factors[units] : 1; | ||
| 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; | ||
| // 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); | ||
| } | ||
| 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 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; | ||
| }, | ||
| /** | ||
| * 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); | ||
| }, | ||
| /** | ||
| * 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 | ||
| ]; | ||
| }, | ||
| /** | ||
| * 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]); | ||
| } | ||
| 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; | ||
| for (var i = 0; i < polygon.length; i++) { | ||
| var ring = polygon[i]; | ||
| 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); | ||
| } | ||
| } | ||
| return (Math.abs(sum) / 2) * this.kx * this.ky; | ||
| }, | ||
| /** | ||
| * 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; | ||
| if (dist <= 0) return line[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); | ||
| } | ||
| return line[line.length - 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; | ||
| for (var i = 0; i < line.length - 1; i++) { | ||
| 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; | ||
| if (dx !== 0 || dy !== 0) { | ||
| var t = ((p[0] - x) * this.kx * dx + (p[1] - y) * this.ky * dy) / (dx * dx + dy * dy); | ||
| if (t > 1) { | ||
| x = line[i + 1][0]; | ||
| y = line[i + 1][1]; | ||
| } else if (t > 0) { | ||
| x += (dx / this.kx) * t; | ||
| y += (dy / this.ky) * t; | ||
| } | ||
| } | ||
| dx = (p[0] - x) * this.kx; | ||
| dy = (p[1] - y) * this.ky; | ||
| var sqDist = dx * dx + dy * dy; | ||
| if (sqDist < minDist) { | ||
| minDist = sqDist; | ||
| minX = x; | ||
| minY = y; | ||
| minI = i; | ||
| minT = t; | ||
| } | ||
| } | ||
| return { | ||
| point: [minX, minY], | ||
| index: minI, | ||
| t: Math.max(0, Math.min(1, minT)) | ||
| }; | ||
| }, | ||
| /** | ||
| * 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 slice = [p1.point]; | ||
| var l = p1.index + 1; | ||
| var r = p2.index; | ||
| if (!equals(line[l], slice[0]) && l <= r) | ||
| slice.push(line[l]); | ||
| for (var i = l + 1; i <= r; i++) { | ||
| slice.push(line[i]); | ||
| } | ||
| if (!equals(line[r], p2.point)) | ||
| slice.push(p2.point); | ||
| return slice; | ||
| }, | ||
| /** | ||
| * 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 = 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 > start && slice.length === 0) { | ||
| slice.push(interpolate(p0, p1, (start - (sum - d)) / d)); | ||
| } | ||
| if (sum >= stop) { | ||
| slice.push(interpolate(p0, p1, (stop - (sum - d)) / d)); | ||
| return slice; | ||
| } | ||
| if (sum > start) slice.push(p1); | ||
| } | ||
| 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 | ||
| ]; | ||
| }, | ||
| /** | ||
| * 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 | ||
| ]; | ||
| }, | ||
| /** | ||
| * 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]; | ||
| } | ||
| }; | ||
| function equals(a, b) { | ||
| return a[0] === b[0] && a[1] === b[1]; | ||
| } | ||
| 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 | ||
| ]; | ||
| } |
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
25699
-35.21%13
30%5
-28.57%416
-47.61%1
Infinity%