Geo-Distance-XS

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

    - Remove deprecated usage defined(@array) from a test case.
    - Optimized the case where both coordinates are identical.
    - Updated ppport.h.

0.11  Sat Apr 14 03:45:08 UTC 2012
    - Adjusted constants for better accuracy.

0.10  Fri Apr 13 11:57:14 UTC 2012
    - Fixed floating point errors.
    - Fixed for antipodal coordinates.
    - Fixed formulas for use with multiple Geo::Distance objects.
    - Added new formula- alt: Andoyer-Lambert-Thomas.
    - Updated benchmarks.

0.09  Mon Aug  1 21:49:37 UTC 2011
    - Required XSLoader; stopped falling back to DynaLoader.

0.08  Fri Oct 22 03:44:02 UTC 2010
    - Added GIS::Distance::Fast to the benchmark.

0.07  Fri Oct 16 17:00:29 UTC 2009
    - RT #50545: Set minimum required version of Geo::Distance to 0.16.

XS.xs  view on Meta::CPAN

    NV lat1
    NV lon2
    NV lat2
PREINIT:
    SV **key;
    int index = 1;
    double (*func)(double, double, double, double);
CODE:
    if (lat2 == lat1 && lon2 == lon1)
        XSRETURN_NV(0.);
    key = hv_fetchs((HV *)SvRV(self), "formula_index", 0);
    if (key) index = SvIV(*key);
    switch (index) {
        case 1: func = &haversine; break;
        case 2: func = &cosines; break;
        case 3: func = &vincenty; break;
        case 4: func = &great_circle; break;
        case 5: func = &polar; break;
        case 6: func = &andoyer_lambert_thomas; break;
    }
    XSRETURN_NV(_count_units(self, unit) * (*func)(lat1, lon1, lat2, lon2));

ex/benchmark.pl  view on Meta::CPAN

sub geo {
    my $d = $geo->distance(mile => @coord);
}

sub gis {
    # Uses lat/lon instead of lon/lat
    my $d = $gis->distance(@coord[ 1, 0, 3, 2 ]);
    return $d->mile;
}

my %gis_formula = (
    hsin  => 'Haversine',
    polar => 'Polar',
    cos   => 'Cosine',
    gcd   => 'GreatCircle',
    mt    => 'MathTrig',
    tv    => 'Vincenty',
);

for my $formula (qw(hsin tv polar cos gcd mt)) {
    print "---- [ Formula: $formula ] ------------------------------------\n";

    $geo->formula($formula);
    $gis->formula($gis_formula{$formula});

    Geo::Distance::XS->unimport;
    printf "perl     - distance from LA to NY: %s miles\n", geo();

    Geo::Distance::XS->import;
    printf "xs       - distance from LA to NY: %s miles\n", geo();

    printf "gis_fast - distance from LA to NY: %s miles\n", gis();
    print "\n";

ex/benchmark2.pl  view on Meta::CPAN

);
my @coords = (
    [ -118.6414,   34.3502,   -117.9739,  34.1607 ],
    [ -118.243103, 34.159545, -73.987427, 40.853293 ],
    [ 0.,          0.,        -179.,      1. ],
    [ 175.,        12.,       -5.,        -12. ],
    [ 0.,          90.,       0.,         -90. ],
);

my %geos;
my @formulas = @Geo::Distance::XS::FORMULAS;
my $max_name_len = 0;
for my $f (@formulas) {
    my $geo = Geo::Distance->new;
    $geo->formula($f);
    $geos{$f} = sub { $geo->distance(mile => @$_) for @coords };
    $max_name_len = max $max_name_len, length($f);
}

cmpthese - 1, \%geos;

print "\n";
for my $idx (0 .. $#tests) {
    print "Calculated length for $tests[$idx]:\n";
    for my $f (@formulas) {
        my $geo = Geo::Distance->new;
        $geo->formula($f);
        my $d = $geo->distance(mile => @{$coords[$idx]});
        printf "    %-*s: %s miles\n", $max_name_len, $f, $d;
    }
    print "\n";
}

lib/Geo/Distance/XS.pm  view on Meta::CPAN

use Carp qw(croak);
use Geo::Distance;
use XSLoader;

our $VERSION    = '0.13';
our $XS_VERSION = $VERSION;
$VERSION = eval $VERSION;

XSLoader::load(__PACKAGE__, $XS_VERSION);

my ($orig_distance_sub, $orig_formula_sub);
BEGIN {
    $orig_distance_sub = \&Geo::Distance::distance;
    $orig_formula_sub  = \&Geo::Distance::formula;
}

my %formulas; @formulas{qw(hsin cos mt tv gcd polar alt)} = (1, 2, 2..6);
our @FORMULAS = sort keys %formulas;

sub import {
    no warnings qw(redefine);
    no strict qw(refs);

    *Geo::Distance::distance = \&{__PACKAGE__.'::distance'};
    *Geo::Distance::formula = sub {
        my $self = shift;
        if (@_) {
            my $formula = shift;
            croak "Invalid formula: $formula"
                unless exists $formulas{$formula};
            $self->{formula} = $formula;
            $self->{formula_index} = $formulas{$formula};
        }
        return $self->{formula};
    };
}

# Fall back to pure perl after calling 'no Geo::Distance::XS'.
sub unimport {
    no warnings qw(redefine);

    *Geo::Distance::formula  = $orig_formula_sub;
    *Geo::Distance::distance = $orig_distance_sub;
}


1;

__END__

=head1 NAME

lib/Geo/Distance/XS.pm  view on Meta::CPAN


The C<Geo::Distance::XS> module provides faster C implementations of the
distance calculations found in C<Geo::Distance>.  See the documentation for
that module for usage.

NOTE: As of version 0.13, Geo::Distance automatically uses this module if
it is installed.

=head1 FORMULAS

In addition to the formulas offered in C<Geo::Distance>, this module
implements the additional formulas:

=head2 alt: Andoyer-Lambert-Thomas Formula

This is faster than the Vincenty formula, but trades a bit of accuracy.

=head1 PERFORMANCE

This distribution contains a benchmarking script which compares
C<Geo::Distance::XS> with C<Geo::Distance> and C<GIS::Distance::Fast>. These
are the results on a MacBook 2GHz with Perl 5.14.2:

    ---- [ Formula: hsin ] ------------------------------------
    perl     - distance from LA to NY: 2443.08796228363 miles
    xs       - distance from LA to NY: 2443.08796228363 miles

lib/Geo/Distance/XS.pm  view on Meta::CPAN

    perl     - distance from LA to NY: 2443.08796228363 miles
    xs       - distance from LA to NY: 2443.08796228363 miles
    gis_fast - distance from LA to NY: 2443.08796228363 miles

                Rate gis_fast     perl       xs
    gis_fast   17935/s       --     -75%     -98%
    perl       71739/s     300%       --     -94%
    xs       1135525/s    6231%    1483%       --

This distribution contains another benchmarking script which compares
only the XS formulas over several different coordinates:

            Rate    tv  hsin   alt   cos    mt   gcd polar
    tv     16906/s    --  -90%  -90%  -91%  -91%  -91%  -92%
    hsin  165414/s  878%    --   -4%   -8%  -10%  -13%  -17%
    alt   172032/s  918%    4%    --   -5%   -7%   -9%  -14%
    cos   180326/s  967%    9%    5%    --   -2%   -5%  -10%
    mt    184357/s  991%   11%    7%    2%    --   -3%   -8%
    gcd   189253/s 1019%   14%   10%    5%    3%    --   -6%
    polar 200386/s 1085%   21%   16%   11%    9%    6%    --

t/01_new.t  view on Meta::CPAN

use strict;
use warnings;
use Geo::Distance::XS;
use Test::More;

my $geo = Geo::Distance->new;
isa_ok $geo, 'Geo::Distance', 'new';
can_ok 'Geo::Distance', qw(distance formula);
cmp_ok scalar @Geo::Distance::XS::FORMULAS, '>', 2, '@FORMULAS exists';

done_testing;

t/03_methods.t  view on Meta::CPAN

use strict;
use warnings;
use Geo::Distance::XS;
use Test::More;

my @formulas = @Geo::Distance::XS::FORMULAS;
for my $f (@formulas) {
    my $geo = Geo::Distance->new;
    $geo->formula($f);

    my @coords = (-118.243103, 34.159545, -73.987427, 40.853293);
    my %expected = (
        map({ $_ => 2443 } @formulas), polar => 2766, tv => 2448, alt => 2448,
    );
    my $d = $geo->distance(mile => @coords);
    is int $d, $expected{$f}, "$f: distance from LA to NY";

    @coords = (175, 12, -5, -12);
    $d = $geo->distance(mile => @coords);
    ok $d == $d, "$f with antipodal coordinates is not NaN";
}

done_testing;



( run in 0.324 second using v1.01-cache-2.11-cpan-3cd7ad12f66 )