Skip to content

Perl port of Mapbox/CheapRuler - fast GPS geodesic functions

License

Notifications You must be signed in to change notification settings

aavmurphy/CheapRuler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NAME

Geo::CheapRuler - Fast GPS Geodesic functions, a Perl port of mapbox/cheap-ruler

VERSION

v0.1.2

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 Vincenty'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

SEE ALSO

GIS::Distance

    https://metacpan.org/pod/GIS::Distance

    Group of modules with XS versions which implement Haversine and Vincenty distance formulas

    Consider for longer than 'city scale' distances, or near the Poles.

GIS::Distance::Vincenty

    The more accurate formulae which this module approximates. There is an XS version of it.

    https://metacpan.org/pod/GIS::Distance::Vincenty

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

For testing, see

    https://github.com/aavmurphy/CheapRuler/blob/main/test/test.pl

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

DEVELOPER NOTES

README.md is auto-generated from Perl POD

The original's test were also ported. See the Github ./tests directory.

About

Perl port of Mapbox/CheapRuler - fast GPS geodesic functions

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published