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

# Content
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