Skip Menu |

This queue is for tickets about the Geo-Calc CPAN distribution.

Report information
The Basics
Id: 89292
Status: new
Priority: 0/
Queue: Geo-Calc

People
Owner: Nobody in particular
Requestors: daviddlowe.flimm [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.12
Fixed in: (no value)



Subject: Incorrect documentation concerning units, patch included
There are several errors in Geo::Calc's documentation. The worst ones are where the documentation specifies that the units should be in kilometers, when in reality, the units should be in the object's distance unit, defined in the constructor. I've attached a patch that fixes the documentation, and that also includes a comprehensive test for the behaviour of methods depending on the object's distance unit.
Subject: Fixed-documentation-and-added-tests-to-test-unit.patch
diff --git a/lib/Geo/Calc.pm b/lib/Geo/Calc.pm index db17eff..13fb2bb 100644 --- a/lib/Geo/Calc.pm +++ b/lib/Geo/Calc.pm @@ -56,13 +56,17 @@ Creates a new Geo::Calc object from a latitude and longitude. The default deciaml precision is -6 for all functions => meaning by default it always returns the results with 6 deciamls. -The default unit distance is 'm' (meter), but you cand define another unit using 'units'. -Accepted values are: 'm' (meters), 'k-m' (kilometers), 'yd' (yards), 'ft' (feet) and 'mi' (miles) +The default distance unit is 'm' (meter), but you can define another unit using +'units'. Accepted values are: 'm' (meters), 'k-m' (kilometers), 'yd' (yards), +'ft' (feet) and 'mi' (miles) Returns ref to a Geo::Calc object. =head2 Parameters +Each of these parameters can be accessed after construction using C<get_lat>, +C<get_lon>, C<get_radius> or C<get_units>. + =over 4 =item lat @@ -77,6 +81,10 @@ C<>=> longitude of the point ( required ) C<>=> earth radius in km ( defaults to 6371 ) +=item units + +C<>=> the distance unit received and output by this object ( default to 'm' ) + =back =cut @@ -151,7 +159,7 @@ angular distance in radians, and a is the square of half the chord length betwee the points). Returns with the distance using the precision defined or -6 -( -6 = 6 decimals ( eg 4.000001 ) ) +( -6 = 6 decimals ( eg 4.000001 ) ), in this object's distance unit. =cut @@ -209,9 +217,9 @@ method bearing_to( HashRef[Num] $point!, Int $precision? = -6 ) returns (Num) { my $f_brng = $gc->final_bearing_to( $point[, $precision] ); my $f_brng = $gc->final_bearing_to( { lat => 40.422371, lon => -3.704298 } ); -Returns final bearing arriving at supplied destination point from this point; -the final bearing will differ from the initial bearing by varying degrees -according to distance and latitude +Returns final bearing (in degrees) arriving at supplied destination point from +this point; the final bearing will differ from the initial bearing by varying +degrees according to distance and latitude. =cut @@ -268,6 +276,9 @@ method midpoint_to( HashRef[Num] $point!, Int $precision? = -6 ) returns (HashRe Returns the destination point and the final bearing using Vincenty inverse formula for ellipsoids. +C<$bearing> must be specified in degrees, where 0 is north and 90 is east, and +C<$distance> must be specified in this object's distance unit. + =cut method destination_point ( Num $brng!, Num $s!, Int $precision? = -6 ) returns (HashRef[Num]) { @@ -367,12 +378,17 @@ method destination_point_hs( Num $brng!, Num $dist!, Int $precision? = -6 ) retu =head2 boundry_box - $gc->boundry_box( $width[, $height[, $precision]] ); # in km - $gc->boundry_box( 3, 4 ); # will generate a 3x4m box around the point - $gc->boundry_box( 1 ); # will generate a 2x2m box around the point (radius) + $gc->boundry_box( $width[, $height[, $precision]] ); + $gc->boundry_box( 3, 4 ); # will generate a 3x4m box around the point, assuming the object's distance unit is meters + $gc->boundry_box( 1 ); # will generate a 2x2m box around the point (radius), assuming the object's distance unit is meters Returns the boundry box min/max having the initial point defined as the center -of the boundry box, given the widht and height +of the boundry box, given the width and height. + +If only one dimension has been specified, than that dimension is considered a +radius. + +Dimensions should be specified in the object's distance unit. =cut @@ -405,8 +421,8 @@ method boundry_box( Num $width!, Maybe[Num] $height?, Int $precision? = -6 ) ret $gc->rhumb_distance_to( $point[, $precision] ); $gc->rhumb_distance_to( { lat => 40.422371, lon => -3.704298 } ); -Returns the distance from this point to the supplied point, in km, travelling -along a rhumb line. +Returns the distance from this point to the supplied point, in the object's +distance unit, travelling along a rhumb line. A 'rhumb line' (or loxodrome) is a path of constant bearing, which crosses all meridians at the same angle. @@ -475,7 +491,7 @@ method rhumb_bearing_to( HashRef[Num] $point!, Int $precision? = -6 ) returns (N $gc->rhumb_destination_point( 30, 1 ); Returns the destination point from this point having travelled the given distance -(in km) on the given bearing along a rhumb line. +(in the object's distance unit) on the given bearing along a rhumb line. =cut @@ -515,7 +531,8 @@ method rhumb_destination_point( Num $brng!, Num $dist!, Int $precision? = -6 ) r $gc->intersection( $brng1, $point, $brng2[, $precision] ); $gc->intersection( 90, { lat => 40.422371, lon => -3.704298 }, 180 ); -Returns the point of intersection of two paths defined by point and bearing +Returns the point of intersection of two paths defined by point and bearing (in +degrees). see http://williams.best.vwh.net/avform.htm#Intersection @@ -570,12 +587,16 @@ method intersection( Num $brng1!, HashRef[Num] $point!, Num $brng2!, Int $precis =head2 distance_at Returns the distance in meters for 1deg of latitude and longitude at the -specified latitude +specified latitude. my $m_distance = $self->distance_at([$precision]); my $m_distance = $self->distance_at(); # at lat 2 with precision -6 returns { m_lat => 110575.625009, m_lon => 111252.098718 } +Note that this method always returns distances in meters, unlike all the other +methods which use the object's distance unit. This is kept as it is for backwards +compatibility. + =cut method distance_at(Int $precision? = -6 ) returns (HashRef[Num]) { diff --git a/t/02units.t b/t/02units.t new file mode 100644 index 0000000..f11c553 --- /dev/null +++ b/t/02units.t @@ -0,0 +1,102 @@ +#!/usr/bin/perl -T + +use strict; +use warnings; + +use Test::More; +use Math::Units; + +use_ok 'Geo::Calc'; + +my %cardiff = ( + lat => '51.483435', + lon => '-3.213501', +); +my %london = ( + lat => '51.490277', + lon => '-0.181274', +); + +my @units = ("", "m", "k-m", "yd", "ft", "mi"); + +for my $unit (@units) { + note "Setting default unit to \"$unit\""; + my $gc = Geo::Calc->new( + lat => $cardiff{lat}, + lon => $cardiff{lon}, + ($unit ? (units => $unit) : ()) + ); + + $unit ||= "m"; + + is($gc->get_units(), $unit, "unit $unit got set properly"); + + # distance_to + is_deeply( + round( $gc->distance_to( \%london ) ), + round( Math::Units::convert( 209954.832717, 'm', $unit ) ), + "distance_to 20km" + ); + + # destination_point + is_deeply( + $gc->destination_point(0, Math::Units::convert(1, 'm', $unit)), + { lat => '51.483444', lon => '-3.213501', final_bearing => 0 }, + "destination_point 1m north" + ); + is_deeply( + $gc->destination_point(90, Math::Units::convert(1000, 'm', $unit)), + { lat => '51.483434', lon => '-3.199105', final_bearing => '90.011264' }, + "destination_point 1000m east" + ); + + # boundary_box + is_deeply( + $gc->boundry_box( Math::Units::convert(1, 'm', $unit) ), + { lat_min => '51.483426', lon_min => '-3.213515', lat_max => '51.483444', lon_max => '-3.213487' }, + "boundry_box 1m radius" + ); + is_deeply( + $gc->boundry_box( Math::Units::convert('0.5', 'm', $unit), Math::Units::convert('0.5', 'm', $unit) ), + { lat_min => '51.483433', lon_min => '-3.213505', lat_max => '51.483437', lon_max => '-3.213497' }, + "boundry_box 0.5m width by 0.5m height" + ); + + # rhumb_distance_to + is( + round( $gc->rhumb_distance_to( \%london ) ), + round( Math::Units::convert( '209954.082043', 'm', $unit ) ), + "rhumb_distance_to" + ); + + # distance_at + is_deeply( + $gc->distance_at(), + { m_lat => '111257.478549', m_lon => '69465.660366' }, + "distance_at" + ); + + # rhumb_destination_point + is_deeply( + $gc->rhumb_destination_point( 0, Math::Units::convert( 30, 'm', $unit ) ), + { lat => '51.483705', lon => '-3.213501' }, + "rhumb_destination_point" + ); + + is_deeply( + [$gc->get_lat, $gc->get_lon], + [$cardiff{lat}, $cardiff{lon}], + '$gc remained unchanged' + ); +} + +done_testing(); + +sub round { + my ( $number ) = @_; + + my $mbf = Math::BigFloat->new( $number ); + $mbf->precision( '-4' ); + + return $mbf->bstr() + 0; +} -- 1.8.1.2