ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Geo-LatLon2Place/LatLon2Place.pm
Revision: 1.1
Committed: Mon Mar 14 02:41:51 2022 UTC (2 years, 2 months ago) by root
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Geo::LatLon2Place - convert latitude and longitude to nearest place
4    
5     =head1 SYNOPSIS
6    
7     use Geo::LatLon2Place;
8    
9     my $db = Geo::LatLon2Place->new ("/var/lib/mydb.cdb");
10    
11     =head1 DESCRIPTION
12    
13     This is a simple-purpose module that tries to do one job: find the nearest
14     placename for a point on earth. It doesn't claim to do a perfect job, but
15     it tries to be simple to set up, simple to use and be fast.
16    
17     =head2 BUILDING AND SETTING UP
18    
19     To build this module, you need tinycdb, a cdb implementation by Michael
20     Tokarev, or a compatible library. On GNU/Debian-based systems you can get
21     this by executing F<apt-get install libcdb-dev>.
22    
23     After install the module, you need to generate a database using the
24     F<geo-latlon2place-makedb> command.
25    
26     Currently, it accepts various databases from geonames
27     (L<https://www.geonames.org/export/>, note the license), for example,
28     F<cities500.zip>, which lists all places with population 500 or more:
29    
30     wget https://download.geonames.org/export/dump/cities500.zip
31     unzip cities500.zip
32     geo-latlon2place-makedb --geonames-gazetteer cities500.txt ll2place.cdb
33    
34     This will create a file F<ll2place.cdb> that you can use for lookups
35     with this module. At the time of this writing, the F<cities500> database
36     results in about a 10MB file while the F<allCountries> database results in
37     about 120MB.
38    
39     =over 4
40    
41     =cut
42    
43     package Geo::LatLon2Place;
44    
45     use common::sense;
46    
47     use Carp ();
48    
49     BEGIN {
50     our $VERSION = 0.01;
51    
52     require XSLoader;
53     XSLoader::load __PACKAGE__, $VERSION;
54    
55     eval 'sub TORAD() { ' . ((atan2 1,0) / 180) . ' }';
56     }
57    
58     sub new {
59     my ($class, $path) = @_;
60    
61     open my $fh, "<", $path
62     or Carp::croak "$path: $!\n";
63    
64     my $self = bless [$fh, ""], $class;
65    
66     cdb_init $self->[1], fileno $self->[0]
67     and Carp::croak "$path: unable to open as cdb file\n";
68    
69     (my ($magic, $version), $self->[2], $self->[3]) = unpack "a4VVV", cdb_get $self->[1], "";
70    
71     $magic eq "SRGL"
72     or Carp::croak "$path: not a Geo::LatLon2Place file";
73    
74     $version == 1
75     or Carp::croak "$path: version mismatch (got $version, expected 1)";
76    
77     $self
78     }
79    
80     sub DESTROY {
81     my ($self) = @_;
82    
83     cdb_free $self->[1];
84     }
85    
86     sub lookup {
87     my ($self, $lat, $lon, $radius) = @_;
88    
89     $radius ||= $self->[2];
90     $radius = int +($radius + $self->[2] - 1) / $self->[2];
91    
92     my $coslat = cos abs $lat * TORAD;
93    
94     my $blat = int $self->[3] * $coslat;
95     my $cx = int (($lon + 180) * $blat / 360);
96     my $cy = int (($lat + 90) * $self->[3] / 180);
97    
98     my ($min, $res) = (1e00);
99    
100     for my $y ($cy - $radius .. $cy + $radius) {
101     for my $x ($cx - $radius .. $cx + $radius) {
102     for (unpack "(C/a*)*", cdb_get $self->[1], pack "s< s<", $x, $y) {
103     my ($plat, $plon, $w, $data) = unpack "s< s< C a*";
104     $plat = $plat * ( 90 / 32767);
105     $plon = $plon * (180 / 32767);
106    
107     my $dx = ($lon - $plon) * TORAD * $coslat;
108     my $dy = ($lat - $plat) * TORAD;
109     my $d2 = ($dx * $dx + $dy * $dy) * $w;
110    
111     $d2 >= $min
112     or ($min, $res) = ($d2, $data);
113     }
114     }
115     }
116    
117     $res
118     }
119    
120     =head1 AUTHOR
121    
122     Marc Lehmann <schmorp@schmorp.de>
123     http://home.schmorp.de/
124    
125     =cut
126    
127     1
128