Geo::Hash - Encode / decode geohash.org locations.


Geo-Hash documentation Contained in the Geo-Hash distribution.

Index


Code Index:

NAME

Top

Geo::Hash - Encode / decode geohash.org locations.

VERSION

Top

This document describes Geo::Hash version 0.02

SYNOPSIS

Top

    use Geo::Hash;

    my $gh = Geo::Hash->new;
    my $hash = $gh->encode( $lat, $lon );
    my ( $lat, $lon ) = $gh->decode( $hash );

DESCRIPTION

Top

Geohash is a latitude/longitude geocode system invented by Gustavo Niemeyer when writing the web service at geohash.org, and put into the public domain.

This module encodes and decodes geohash locations.

See http://en.wikipedia.org/wiki/Geohash and http://geohash.org for more information.

INTERFACE

Top

new

Create a new Geo::Hash object.

    my $gh = Geo::Hash->new;

precision

Infer a suitable precision (number of character in hash) for a given lat, lon pair.

    my $prec = $gh->precision( $lat, $lon );

encode

Encode a lat, long pair into a geohash.

    my $hash = $gh->encode( $lat, $lon );

You may optionally supply the length of the desired geohash:

    # Very precise
    my $hash = $gh->encode( $lat, $lon, 10 );

If the precision argument is omitted precision will be used to provide a default.

decode_to_interval

Like decode but instead of returning a pair of coordinates returns the interval for each coordinate. This gives some indication of how precisely the original hash specified the location.

The return value is a pair of array refs. Each referred to array contains the upper and lower bounds for each coordinate.

    my ( $lat_range, $lon_range ) = $gh->decode_to_interval( $hash );
    # $lat_range and $lon_range are references to two element arrays

decode

Decode a geohash into a lat, long pair.

    my ( $lat, $lon ) = $gh->decode( $hash );

CONFIGURATION AND ENVIRONMENT

Top

Geo::Hash requires no configuration files or environment variables.

DEPENDENCIES

Top

None.

INCOMPATIBILITIES

Top

None reported.

BUGS AND LIMITATIONS

Top

No bugs have been reported.

Please report any bugs or feature requests to bug-geo-hash@rt.cpan.org, or through the web interface at http://rt.cpan.org.

AUTHOR

Top

Andy Armstrong <andy@hexten.net>

http://geohash.org/gcwrdtsvrfgr

LICENCE AND COPYRIGHT

Top


Geo-Hash documentation Contained in the Geo-Hash distribution.
package Geo::Hash;

use warnings;
use strict;
use Carp;

our $VERSION = '0.02';

sub new { bless {}, shift }

my @ENC = qw(
  0 1 2 3 4 5 6 7 8 9 b c d e f g h j k m n p q r s t u v w x y z
);

my %DEC = map { $ENC[$_] => $_ } 0 .. $#ENC;

sub _mid {
    my ( $ar, $wh ) = @_;
    return ( $ar->[$wh][0] + $ar->[$wh][1] ) / 2;
}

# The number of bits necessary to represent the specified number of
# decimal digits
sub _d2b { int( shift() * 3.32192809488736 + 1 ) }

sub _bits_for_number {
    my $n = shift;
    return 0 unless $n =~ s/.*\.//;
    return _d2b( length $n );
}

sub precision {
    my ( $self, $lat, $lon ) = @_;
    my $lab = _bits_for_number( $lat ) + 8;
    my $lob = _bits_for_number( $lon ) + 9;
    return int( ( ( $lab > $lob ? $lab : $lob ) + 1 ) / 2.5 );
}

sub encode {
    croak "encode needs two or three arguments"
      unless @_ >= 3 && @_ <= 4;
    my ( $self, @pos ) = splice @_, 0, 3;
    my $prec = shift || $self->precision( @pos );
    my $int  = [ [ 90, -90 ], [ 180, -180 ] ];
    my $flip = 1;
    my @enc  = ();
    while ( @enc < $prec ) {
        my $bits = 0;
        for ( 0 .. 4 ) {
            my $mid = _mid( $int, $flip );
            my $bit = $pos[$flip] >= $mid ? 1 : 0;
            $bits = ( ( $bits << 1 ) | $bit );
            $int->[$flip][$bit] = $mid;
            $flip ^= 1;
        }
        push @enc, $ENC[$bits];
    }
    return join '', @enc;
}

sub decode_to_interval {
    croak "Needs one argument"
      unless @_ == 2;
    my ( $self, $hash ) = @_;

    my $int = [ [ 90, -90 ], [ 180, -180 ] ];
    my $flip = 1;

    for my $ch ( split //, $hash ) {
        if ( defined( my $bits = $DEC{$ch} ) ) {
            for ( 0 .. 4 ) {
                $int->[$flip][ ( $bits & 16 ) >> 4 ]
                  = _mid( $int, $flip );
                $flip ^= 1;
                $bits <<= 1;
            }
        }
        else {
            croak "Bad character '$ch' in hash '$hash'";
        }
    }

    return @$int;
}

sub decode {
    my @int = shift->decode_to_interval( @_ );
    return map { _mid( \@int, $_ ) } 0 .. 1;
}

1;
__END__