Giter Club home page Giter Club logo

cheap-ruler's Introduction

cheap-ruler Node

A collection of very fast approximations to common geodesic measurements. Useful for performance-sensitive code that measures things on a city scale. Can be an order of magnitude faster than corresponding Turf methods.

The approximations are based on the WGS84 ellipsoid model of the Earth, projecting coordinates to a flat surface that approximates the ellipsoid around a certain latitude. For distances under 500 kilometers and not on the poles, the results are very precise — within 0.1% margin of error compared to Vincenti formulas, and usually much less for shorter distances.

Usage

var ruler = new CheapRuler(35.05, 'miles'); // calculations around latitude 35
...
var distance = ruler.distance([30.51, 50.32], [30.52, 50.312]);
var lineLength = ruler.lineDistance(line.geometry.coordinates);
var bbox = ruler.bufferPoint([30.5, 50.5], 0.01);

Note: to get the full performance benefit, create a ruler object only once per a general area of calculation, and then reuse it as much as possible. Don't create a new ruler for every calculation.

Creating a ruler object

new CheapRuler(latitude[, units])

Creates a ruler object that will approximate measurements around the given latitude. Units are one of: kilometers (default), miles, nauticalmiles, meters, yards, feet, inches.

const ruler = new CheapRuler(50.5, 'meters');

CheapRuler.fromTile(y, z[, units])

Creates a ruler object from tile coordinates (y and z).

const ruler = CheapRuler.fromTile(1567, 12);

Ruler methods

distance(a, b)

Given two points of the form [longitude, latitude], returns the distance.

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

bearing(a, b)

Returns the bearing between two points in angles.

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

destination(p, dist, bearing)

Returns a new point given distance and bearing from the starting point.

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

offset(p, dx, dy)

Returns a new point given easting and northing offsets from the starting point.

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

lineDistance(line)

Given a line (an array of points), returns the total line distance.

const length = ruler.lineDistance([
    [-67.031, 50.458], [-67.031, 50.534],
    [-66.929, 50.534], [-66.929, 50.458]
]);

area(polygon)

Given a polygon (an array of rings, where each ring is an array of points), returns the area. Note that it returns the value in the specified units (square kilometers by default) rather than square meters as in turf.area.

const area = ruler.area([[
    [-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534],
    [-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.

const distance = ruler.pointToSegmentDistance([-77.034076, 38.882017],
    [-77.031669, 38.878605], [-77.029609, 38.881946]);

along(line, dist)

Returns the point at a specified distance along the line.

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

pointOnLine(line, p)

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.

const point = ruler.pointOnLine(line, [-67.04, 50.5]).point;

lineSlice(start, stop, line)

Returns a part of the given line between the start and the stop points (or their closest points on the line).

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

lineSliceAlong(startDist, stopDist, line)

Returns a part of the given line between the start and the stop points indicated by distance along the line.

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

bufferPoint(p, buffer)

Given a point, returns a bounding box object ([w, s, e, n]) created from the given point buffered by a given distance.

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

bufferBBox(bbox, buffer)

Given a bounding box, returns the box buffered by a given distance.

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

insideBBox(p, bbox)

Returns true if the given point is inside in the given bounding box, otherwise false.

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

Units conversion

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

// convert 50 meters to yards
50 * CheapRuler.units.yards / CheapRuler.units.meters;

If you don't specify units when creating a ruler object, you can use these constants to convert return values (using multiplication) and input arguments (using division) to any units:

// get distance between points in feet
const distanceInFeet = ruler.distance(a, b) * CheapRuler.units.feet;

// make a bbox from a point with a 200 inch buffer
const box = ruler.bufferPoint(p, 200 / CheapRuler.units.inches);

Install

Precision

A table that shows the margin of error for ruler.distance compared to node-vincenty (a state of the art distance formula):

lat 10° 20° 30° 40° 50° 60° 70° 80°
1km 0% 0% 0% 0% 0% 0% 0% 0% 0%
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's People

Contributors

anandthakker avatar cheeaun avatar deniscarriere avatar mourner avatar npmcdn-to-unpkg-bot avatar tcql avatar tdillon avatar tmcw avatar tmpsantos avatar turbo87 avatar vipera avatar waldyrious avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cheap-ruler's Issues

Options for measuring distance of coordinates with large latitude difference

Hello,

I understand that this library is only meant to measure small distances under 1000km, and the reason for specifying a latitude when creating a ruler object is to optimize and simplify the calculation.

However, I would like to be able to measure both large and small distances.

My question is, if I want to use this library for small distances and fall back to a different library for large distances to calculate those using Haversine/Vincenty, what conditions should I be looking for when deciding to fall back to Haversine/Vincenty? (e.g. look at the difference in latitude and if it's greater than say 20.5 or something then use Haversine/Vincenty instead of cheap-ruler)

I tried measuring the distance from Seattle (47.60776837497131, -122.3328546420654) to Auckland (-36.84868556109566, 174.7618109826828) using Seattle's latitude for the ruler object and got the following results:

cheap-ruler: 24,232 km
Haversine: 11,272 km
Vincenty: 11,247 km

That's over 100% error, which is alarming, so perhaps I may be using cheap-ruler wrongly somehow, but for shorter distances it worked just fine. (P.S. I'm actually using cheap-ruler-cpp, the C++ port of this library, so it might be a problem with that port)

If you have any suggestions on when to fall back to Haversine/Vincenty, it would be greatly appreciated.
Thanks!

C# version?

Hi, can we get a C# version of this? :)


using System.Collections.Generic;
using System;
using UnityEngine;

class CheapRuler 
{
	private float lat;
	private string units;

    private float kx;
    private float ky;

	/**
	 * Multipliers for converting between units.
	 *
	 * @example
	 * // convert 50 meters to yards
	 * 50 * cheapRuler.units.yards / cheapRuler.units.meters;
	 */
	//units
	public Dictionary<string,float> factors = new Dictionary<string,float>(){
		["kilometers"]    = 1.0f,
		["miles"]         = 1000.0f / 1609.344f,
		["nauticalmiles"] = 1000f / 1852f,
		["meters"]        = 1000f,
		["metres"]        = 1000f,
		["yards"]         = 1000f / 0.9144f,
		["feet"]          = 1000f / 0.3048f,
		["inches"]        = 1000f / 0.0254f
	};

	/**
	 * 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
	 */
	static CheapRuler FromTile(float y,float z,string units) 
	{
		var n = Mathf.PI * (1 - 2 * (y + 0.5f) / Mathf.Pow(2, z));
		var lat = Mathf.Atan(0.5f * (Mathf.Exp(n) - Mathf.Exp(-n))) * 180f / Mathf.PI;
		return new CheapRuler(lat, units);
	}

    string GetFactorsContents()
    {
        string r = "";
        foreach (KeyValuePair<string, float> kvp in factors)
        {
            r += String.Format("Key = {0} Value = {1}", kvp.Key, kvp.Value);
        }

        return r;
    }

    /**
     * A collection of very fast approximations to common geodesic measurements. Useful for performance-sensitive code that measures things on a city scale.
     *
     * @param latitude
     * @param unitsName [units="kilometers"]
     * @returns {CheapRuler}
     * @example
     * var ruler = new cheapRuler(35.05f, "miles");
     * //=ruler
     */
	public CheapRuler(float latitude, string unitsName) 
	{
		lat   = latitude;
		units = unitsName;
        if (!factors.ContainsKey(units))
        {
            throw new Exception("Unknown unit " + units + ". Use one of: " + GetFactorsContents());
        }


        float m = factors.ContainsKey(units) ? factors[units] : 1f;

		float cos = Mathf.Cos(lat * Mathf.PI / 180f);
		float cos2 = 2f * cos * cos - 1f;
		float cos3 = 2f * cos * cos2 - cos;
		float cos4 = 2f * cos * cos3 - cos2;
		float cos5 = 2f * cos * cos4 - cos3;

		// multipliers for converting longitude and latitude degrees into distance (http://1.usa.gov/1Wb1bv7)
		this.kx = m * (111.41513f * cos - 0.09455f * cos3 + 0.00012f * cos5);
		this.ky = m * (111.13209f - 0.56605f * cos2 + 0.0012f * cos4);
	}
	
	/**
	 * 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
	 */
	float distance(Vector2 a,Vector2 b) 
	{
		var dx = (a[0] - b[0]) * this.kx;
		var dy = (a[1] - b[1]) * this.ky;
		return Mathf.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
	 */
    public float bearing(Vector2 a, Vector2 b)
    {
        var dx = (b[0] - a[0]) * kx;
        var dy = (b[1] - a[1]) * ky;
        float bearing = Mathf.Atan2(dx, dy) * 180f / Mathf.PI;
        if (bearing > 180) bearing -= 360f;
        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
     */
	public Vector2 destination(Vector2 p,float dist,float bearing) 
    {
		float a = bearing * Mathf.PI / 180f;
		return offset(p,
			Mathf.Sin(a) * dist,
			Mathf.Cos(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
	 */
	public Vector2 offset(Vector2 p, float dx, float dy) {
		return new Vector2(
			p[0] + dx / kx,
			p[1] + dy / 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
	 */
	public float lineDistance(List<Vector2> points) {
		float total = 0f;
		for (var i = 0; i < points.Count - 1; i++) {
			total += 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
	 */
	public float area(List<List<Vector2>> polygon) 
    {
		var sum = 0f;

		for (int i = 0; i < polygon.Count; i++) {
			var ring = polygon[i];

			for (int j = 0, len = ring.Count, k = len - 1; j < len; k = j++)
            {
                sum += (ring[j][0] - ring[k][0]) * (ring[j][1] + ring[k][1]) * (i == 1 ? -1 : 1);
            }
		}

		return (Mathf.Abs(sum) / 2f) * kx * 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
	 */
	public Vector2 along(List<Vector2> line, float dist) 
    {
		float sum = 0f;

		if (dist <= 0f) return line[0];

		for (var i = 0; i < line.Count - 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.Count - 1];
	}

    public struct PointOnLine
    {
        public Vector2 point;
        public int index;
        public float t;
    }

    /**
     * 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
     */
	public PointOnLine pointOnLine(List<Vector2> line,Vector2 p) {
		float minDist = float.PositiveInfinity;
		float minX, minY, minT;
        int minI = 0;
        minX = minY = minT = 0.0f;
		for (int i = 0; i < line.Count - 1; i++) {

			float x = line[i][0];
			float y = line[i][1];
			float dx = (line[i + 1][0] - x) * this.kx;
			float dy = (line[i + 1][1] - y) * this.ky;
            float t  = 0.0f;
			if (dx != 0f || dy != 0f) 
            {
				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;

			float sqDist = dx * dx + dy * dy;
			if (sqDist < minDist) {
				minDist = sqDist;
				minX = x;
				minY = y;
				minI = i;
				minT = t;
			}
		}

        return new PointOnLine()
        {
            point = new Vector2(minX, minY),
            index = (int)minI,
            t = Math.Max(0, Math.Min(1f, 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
	 */
	List<Vector2> lineSlice (Vector2 start,Vector2 stop,List<Vector2> line) 
	{
		var p1 = pointOnLine(line, start);
		var p2 = 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 = new List<Vector2>(){p1.point};

		int l = p1.index + 1;
		var r = p2.index;

		if (!equals(line[l], slice[0]) && l <= r)
			slice.Add(line[l]);

		for (var i = l + 1; i <= r; i++) {
			slice.Add(line[i]);
		}

		if (!equals(line[r], p2.point))
			slice.Add(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
	 */
	List<Vector2> lineSliceAlong (float start,float stop, List<Vector2> line) 
	{
		float sum = 0.0f;
		List<Vector2> slice = new List<Vector2>();

		for (int i = 0; i < line.Count - 1; i++) {
			Vector2 p0 = line[i];
			Vector2 p1 = line[i + 1];
			float d = distance(p0, p1);

			sum += d;

			if (sum > start && slice.Count == 0) {
				slice.Add(interpolate(p0, p1, (start - (sum - d)) / d));
			}

			if (sum >= stop) {
				slice.Add(interpolate(p0, p1, (stop - (sum - d)) / d));
				return slice;
			}

			if (sum > start) slice.Add(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
	 */
	/*public float bufferPoint(Vector2 p,Quaternion 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
	 */
	/*public float bufferBBox (Quaternion bbox, float 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
	 */
	public bool insideBBox(Vector2 p, Quaternion bbox) 
	{
		return p[0] >= bbox[0] &&
			   p[0] <= bbox[2] &&
			   p[1] >= bbox[1] &&
			   p[1] <= bbox[3];
	}
	

	public bool equals(Vector2 a,Vector2 b) {
		return a[0] == b[0] && a[1] == b[1];
	}

	public Vector2 interpolate(Vector2 a,Vector2 b,float t) {
		var dx = b[0] - a[0];
		var dy = b[1] - a[1];
		return new Vector2(
			a[0] + dx * t,
			a[1] + dy * t
		);
	}
}

lineSliceDist

In addition to lineSlice, it would be great to have a line slicing method that accepts two distance values:

var slice = ruler.lineSliceDist(line, 10, 20); // slice a part from 10km to 20km along the line

This would avoid doing pointOnLine calculations, making slicing faster for many cases where we know what part of the way we need.

Return minDist from pointOnLine

It would be helpful to return the distance to the line from the "pointOnLine" method. I'm trying to find all points located on a given line, from a set of points. Some points may not be located on the line.
By returning minDist it will be possible to determine this without having to measure the distance again using the "distance" method.

Dead link in comments

The minified url on this line no longer works:

// multipliers for converting longitude and latitude degrees into distance (http://1.usa.gov/1Wb1bv7)

(Perhaps it pointed to the PDF that is linked-to in the README?)

Better way to handle poles?

Calculating the distance over a pole, e.g., between [0, 89.99] and [180, 89.99] (with 89.99 as the latitude passed into the cheap ruler constructor) gives me a 57% error relative to the Vincenty calculation. (For reference, I used https://github.com/chrisveness/geodesy which seems to be better maintained than the derivative https://github.com/TankofVines/node-vincenty)

I know that this is already documented as a shortcoming, but food for thought: if you can calculate the maximum error that your shortcut formula would give you in advance (which seems likely, given that you have a % error chart in the readme), could you not simply fall back to the Vincenty (or even haversine) formula if the error is too large? That would fix the pole issue... (for comparison, the haversine formula gives only a 0.4% error for those same two coordinates -- and could probably be improved upon by tweaking the earth radius).

precision compared to haversine (vincenty as reference)

First of all: this fast approx is really fast: I really like it ! Thanks!

But I can't really proof the statements to its precision, stated at https://www.mapbox.com/blog/cheap-ruler/. At least when it comes to calculating points by a startingpoint and a specified distance and angle.

(To give you some idea, what I need: I need to calculate lots of lonlats from a lonlat at different distances and angle: For me distances up to 150km-200km are of interest)

Using vincenty as a reference, all my calculations leave me with a significantly larger error, compared to those of haversine - starting in mid-latitudes,say at 40-60km - and not after some 100s miles.

I've made a fiddle to visualize this.
https://jsfiddle.net/badorties/awc44k9L/15/

The question: Am I doing something wrong?
Short answer would be very appreciated.

Buffering lineStrings and polygons

Hi there,

Any plans to update this extremely useful ruler to also be able to buffer line strings and polygons? Or would the calculations required mean that it ultimately would be just as fast as turf (and thus you'd be better off using turf to start with)?

/Cheers,
Jelte

along giving incorrect results

I've implemented along into a project and noticed some strange results (returned points being either too far along the line, or not far enough).
I just found this Codepen via Google and it is seems to be giving incorrect results too.
The green circle should be 250m from the top blue circle, but measuring the distance on Google Maps puts it not even close, at ~208m
image

image

Interestingly, ruler.distance puts the total line length at 852m, but Google Maps says it's 739m.

Am I missing something here?

PS. the codepen example is using an older version, but changing to v3.0.1, and after a few tweaks to get it running, the results are basically the same.

Adding an option to use GeoPoint objects as input

@mourner Thanks for this library, I love it. :)
But I have a slight issue with its integration into my project. Most other geolocation libraries I'm using are storing location data as an object, in this format:
{ latitude: Number, longitude: Number }
For example Google Firestore is using such format:
https://firebase.google.com/docs/reference/android/com/google/firebase/firestore/GeoPoint

So I'd like to modify cheapRuler to use this format as well, to keep my code clean.

My question is, would that be something that you would support?
Is so, how would you go about the implementation? So I can do it correctly and open a PR.
Otherwise I will just create a fork where I simply change the format a post a link here.

Incorrect distance calculation when crossing anti-meridian

Hello,
I need to admit, that I didn't tried than with original code, but find out when I transferred algorithm into different language. :-)

Due to missing normalization of longitude input used in subtraction of distance function:

    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);
},

distance is wrongly calculated if shortest path between two points is across 0 Meridian, for example:

a[ 0, +179]
b[0, -179]

distance calculated: 21591.780281076 NM is obviously wrong. :-)

As we are calculating distance on equator, 1deg lon == 60NM, and therefore distance at example shall be 120NM, but original formula simple calculate distance around globe, which is probably not what you want, if you are not Phileas Fogg. :-)

Sorry for Perl snippet bellow, but something like this will fix issue:

$dx = ( $a[0] - $b[0] ) * $kx;
$dy = $a[1] - $b[1];
if ( $dy > 180 ) {
  $dy -= 360;
} elsif ( $dy < -180 ) {
  $dy += 360;
}
$dy *= $ky;

Please, consider to specify a license

Please, consider to specify a license

First of all, thanks to release this work on a public repository.

Nonetheless, this repo doesn't have any concrete license (or it isn't easy to see), so I would like that you consider to choose one to avoid the confusion that it causes with its absence, the work has applied the default copyright laws which the most of the people we aren't sure the implications because we aren't lawyers.

This page may help you to decide which one is more convenient for you and all the benefits that an open license has over a closed one, however, don't get me wrong, choose the one that fits better to your specific case, independently if it's open or not.

Thanks for considering.

pointOnLine jsdoc typings

Updating the jsdoc typing information for the return type of pointOnLine would beneficial for TypeScript developers.

By inspecting the pointOnLine function, the return type is:

     * @returns {{ point: [ lon: number | undefined, lat: number | undefined ], index: number | undefined, t: number }}

or the undefined types could be removed with a bit of work to simplify the type:

     * @returns {{ point: [ lon: number, lat: number ], index: number, t: number }}

* @returns {Object} {point, index, t}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.