NAME

Geo::CheapRuler

VERSION

v0.1.0

SYNOPSIS

A collection of very fast approximations to common geodesic measurements. Useful for performance-sensitive code that measures things on a city scale (less than 500km, not near the poles). Can be an order of magnitude faster than Haversine based methods.

A Perl port of Mapbox's cheap-ruler v4.0.0 https://github.com/mapbox/cheap-ruler

Very fast as they use just 1 trig function per call.

MATHS MODEL

The Maths model is based upon an approximation to Vicenty's formulae, which uses the Earth's actual shape, an oblate ellipsoid (squashed sphere). For 'city' scale work, it is still more accurate than the Haversine formulae (which uses several trig calls based upon a spherical Earth). For an explanation, see https://blog.mapbox.com/fast-geodesic-approximations-with-cheap-ruler-106f229ad016

EXPORT

Nothing

WEBSITE

https://github.com/aavmurphy/CheapRuler

USAGE

This module uses "geojson" style GPS geometery. Points are [lon, lat]. Polygons are a series of rings. The first ring is exterior and clockwise. Subsequent rings are interior (holes) and anticlockwise.

The latitude (lat) parameter passed to the constructor should be the 'median' of the lat's used, i.e. (max-lat + min-lat)/2.

Some methods have units, e.g. "expand a bounding box by 10 meters/miles/kilometers". The default 'units' are 'kilometers', you may which to use 'meters'.

Data is passed / retured as arrayrefs, e.g. $p = [ 0.1, 54.1 ];

EXAMPLES

In the examples below, $p is a point, $a and $b are a line segment.

$p = [ -1, 57 ];

$a =  [0.1, 54.1];
$b =  [0.2, 54.2];

$ruler = Cheap::Ruler->new( ( 54.1 + 54.2 )/2, 'meters' );
# so the 'units' below are meters

$distance = $ruler->distance( $a, $b );
# return meters

$bearing = $ruler->bearing( $a, $b );
# returns degrees

$point = $ruler->destination( $a, 1000, 90);
# returns a new point, 1000 units away at 90 degrees

$point = $ruler->offset( $p, 100, 200 );
# returns a point 100 units east, 200 units north

$distance = $ruler->lineDistance( [ $p, $a, $b ] );
# length of the line

$area = $ruler->area( [
	[-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534], [-66.929, 50.458], [-67.031, 50.458]
	] ); # area of a polygon

$point = $ruler->along( [ [-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534] ], 2.5);
# returns a point 2.5 units along the line

$distance = $ruler->pointToSegmentDistance( $p, $a, $b );
# distance from point to a 2 point line segment 

API

CheapRuler::fromTile( $y, $z, $units='kilometers' )

Creates a ruler object from Google web mercator tile coordinates (y and z). That's correct, y and z, not x.

See 'new' below for available units.

Example

$ruler = CheapRuler::fromTile( 11041, 15, 'meters');

units()

Multipliers for converting between units.

See 'new' below for available units.

Example : convert 50 meters to yards

$units =  CheapRuler::units();

$yards = 50 * $units->{yards} / $units->{meters};

CheapRuler->new( $lat, $units='kilometers' )

Create a ruler instance for very fast approximations to common geodesic measurements around a certain latitude.

	param latitude, e.g. 54.31

	param units (optional), one of: kilometers miles nauticalmiles meters metres yards feet inches   
 

Example

$ruler = CheapRuler->new(35.05, 'miles');

distance( $a, $b )

Given two points of the form [longitude, latitude], returns the distance in 'ruler' units.

param $a, point [longitude, latitude]

param $b, point [longitude, latitude]

returns distance (in chosen units)

Example

$distance = $ruler->distance([30.5, 50.5], [30.51, 50.49]);

bearing( $a, $b )

Returns the bearing between two points in degrees

param $a, point [longitude, latitude]

param $b, point [longitude, latitude]

returns $bearing (degrees)

Example

$bearing = $ruler->bearing([30.5, 50.5], [30.51, 50.49]);

destination( $point, $distance, $bearing)

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

param $p point [longitude, latitude]

param $dist distance in chosen units

param $bearing (degrees)

returns $point [longitude, latitude]

Example $point = ruler->destination([30.5, 50.5], 0.1, 90);

offset( $point, dx, dy )

Returns a new point given easting and northing offsets (in ruler units) from the starting point.

param $point, [longitude, latitude]

param $dx, easting, in ruler units

param $dy, northing, in ruler units

returns $point [longitude, latitude]

Example

$point = ruler.offset([30.5, 50.5], 10, 10);

lineDistance ( $points )

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

param $points, listref of points, where a point is [longitude, latitude]

returns $number, total line distance in 'ruler' units

Example

$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.

param $polygon, a list-ref of rings, where a ring is a list of points [lon,lat], 1st ring is outer, 2nd+ rings are inner (holes)

returns $number, area value in the specified 'ruler' units (square kilometers by default)

Example

$area = $ruler->area([[
	[-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534], [-66.929, 50.458], [-67.031, 50.458]
	]]);

along( $line, $distance)

Returns the point at a specified distance along the line.

param $line, a list-ref of points of [lon, lat]

param $dist, distance in ruler units

returns $point, a list-ref [lon, lat]

Example

$point = $ruler->along(
	[ [-67.031, 50.458], [-67.031, 50.534], [-66.929, 50.534] ],
	2.5);

pointToSegmentDistance( $p, $a, $b )

Returns the distance from a point `p` to a line segment `a` to `b`.

	param $p, point, [longitude, latitude]

	param $a, segment point 1, [longitude, latitude]

	param $b, segment point 2, [longitude, latitude]

	returns $distance (in ruler units)
    

Example

$distance = $ruler->pointToSegmentDistance([-67.04, 50.5], [-67.05, 50.57], [-67.03, 50.54]);

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,

* t is a parameter from 0 to 1 that indicates where the closest point is on that segment.


param $line, listref of points of [lon, lat]

param $p, point of [longitude, latitude]

returns { point => [lon, lat], index => number, t => number }

Example

$info = $ruler->pointOnLine( $line, [-67.04, 50.5])

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).

param $start, point [longitude, latitude]

param $stop, point [longitude, latitude]

param $line, arrayref of points of [lon,lat]

returns $linea_slice (a listref) part of the line

Example

$line_slice = $ruler->lineSlice([-67.04, 50.5], [-67.05, 50.56], $line);

lineSliceAlong( $start, $stop, $line )

Returns a part of the given line between the start and the stop points indicated by distance (in 'units') along the line.

param $start, distance, in ruler units

param $stop stop, distance, in ruler units

param $line, listref of points

returns $line_slice, listref of points, part of a line

Example

$line_slice = $ruler->lineSliceAlong(10, 20, $line);

bufferPoint( point, buffer_distance )

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

param $p point [longitude, latitude]

param $buffer, a distance in ruler units

returns $bbox, listref, [w, s, e, n]

Example

	$bbox = $ruler->bufferPoint([30.5, 50.5], 0.01);
 

bufferBBox( $bbox, $buffer )

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

param $bbox, listref of [w, s, e, n]

param $buffer, distance in ruler units

returns $bbox, a listref, [w, s, e, n]

Example

$bbox = $ruler->bufferBBox([30.5, 50.5, 31, 51], 0.2);

insideBBox( $point, $bbox )

Returns true (1) if the given point is inside in the given bounding box, otherwise false (0).

param $p point [longitude, latitude]

param $bbox, listref [w, s, e, n]

returns 0 or 1 (boolean)

Example

$is_inside = $ruler->insideBBox([30.5, 50.5], [30, 50, 31, 51]);

CheapRuler::equals( $a, $b)

Tests if 2 points are equal.

a function not a method!

param $a, point, [ lon, lat ]

param $b, point, [ lon, lat ]

CheapRuler::interpolate( $a, $b, $t )

Returns a point along a line segment from $a to $b

a function not a method!

param $a, point, [lon, lat]

param $b, point, [lon, lat]

param $t, ratio (0 <= $t  < 1 ), of the way along the line segment

returns $p, point [ lon, lat]

CheapRuler::normalize( $degrees )

Normalize a lon degree value into [-180..180] range

a function not a method!

	param $degrees

	return $degrees
 

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Geo::CheapRuler

Or at

https://github.com/aavmurphy/CheapRuler

BUGS

Please report any bugs or feature requests of this port to

https://github.com/aavmurphy/CheapRuler

For the original, please see

https://github.com/mapbox/cheap-ruler

AUTHOR

Andrew Murphy, <aavm at perl.org>

LICENSE AND COPYRIGHT

The original, mapbox/cheap-ruler, is (c) Mapbox.

This port is Copyright (c) 2025 by Andrew Murphy.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)

ACKNOWLEDGEMENTS

This module is a direct port of mapbox/cheap-ruler

GITHUB README

README.md is auto-generated from Perl POD