Fixed conf bug
[geohash-tool.git] / geohash-tool
1 #!/usr/bin/perl
2
3 # Copyright (C) 2015 Gabriel Pérez-Cerezo
4 # This library is free software; you can redistribute it and/or modify
5 # it under the same terms as Perl itself, either Perl version 5.10.0 or,
6 # at your option, any later version of Perl 5 you may have available.
7
8 =encoding utf8
9
10 =head1 NAME
11
12
13 geohash-tool - Get the geohash's location and its distance to your home or workplace
14
15 =head1 SYNOPSIS
16
17 geohash-tool [B<-d> I<DATE>] [B<-l> I<LAT.LON>]
18
19 =head1 DESCRIPTION
20
21 This script calculates the geohash's location for your graticule for
22 any (past) date. Then it queries Openstreetmap and prints the place
23 name. Finally, it prints the distance between your home (or any other
24 point you specify) and the geohash, using spherical trigonometry.
25
26 =head1 CONFIGURATION
27
28 geohash-tool is configured using a YAML file located at F<~/.geohashrc.>
29 It should look like this:
30
31 ---
32 lat: 0
33 lon: 1
34 0.1:
35 - name: Work
36 lat: 0.23142
37 lon: 1.2414921
38 - name: Home
39 lat: 0.1424439
40 lon: 1.2413813
41 0.3:
42 - name: Office
43 lat: 0.1424439
44 lon: 3.2413813
45
46 Where lat: 0 and lon: 1 indicate your default graticule (can be
47 overriden with the B<-l> switch). The places indicated below 0.1 and
48 0.3 are the places to which the distance will be computed when looking
49 for the Geohash in the 0,1 and 0,3 graticule, respectively.
50
51 =head1 AUTHOR
52
53 Gabriel Pérez-Cerezo - L<http://gpcf.eu>
54
55 =head1 COPYRIGHT AND LICENSE
56
57 Copyright (C) 2015 by Gabriel Pérez-Cerezo
58
59 This library is free software; you can redistribute it and/or modify
60 it under the same terms as Perl itself, either Perl version 5.10.0 or,
61 at your option, any later version of Perl 5 you may have available.
62
63 =cut
64 use strict;
65 use warnings;
66 use Geo::Hashing;
67 use Math::Trig;
68 use POSIX qw(strftime);
69 use diagnostics;
70 use utf8;
71 use YAML;
72 use Getopt::Std;
73 binmode(STDOUT, ":utf8");
74 my $today;
75 my $content;
76 my $url;
77 our %opts;
78 ### Read the configuration file:
79 my $string;
80 our $VERSION = 0.1.3;
81 my $pi = 3.14159265358979;
82 $Getopt::Std::STANDARD_HELP_VERSION = 1;
83 my $default = "
84 ---
85 lat: 0
86 lon: 1
87 0.1:
88 - name: Work
89 lat: 0.23142
90 lon: 1.2414921
91 - name: Home
92 lat: 0.1424439
93 lon: 1.2413813
94 ";
95 sub HELP_MESSAGE {
96 print <<EOF;
97 Usage: $0 [-d DATE] [-l LAT.LONG]\n
98 DATE must have the following format: YYYY-MM-DD.
99 DATE must be the current day or earlier.
100 LAT and LONG are integers. They describe your graticule.
101 Example:
102 Get the Geohash for 2014-03-12 for the 40,10 graticule:
103 $0 -d 2014-03-12 -l 40.10
104 EOF
105 }
106 sub distance {
107 # gives a rough estimate of the distance to the geohash. Accuracy:
108 # +/- cos(angle between coordinates)*37/2pi. (about +/- 50 m for
109 # geohashing distances).
110 my ($lat1, $long1, $lat2, $long2) = @_;
111 my $a = deg_to_rad(90 - $lat1);
112 my $b = deg_to_rad(90 - $lat2);
113 my $C = deg_to_rad(abs($long1-$long2));
114 return acos(cos($a)*cos($b)+sin($a)*sin($b)*cos($C))*40040/(2*$pi);
115 }
116
117 sub read_conf {
118 local $/ = undef;
119 unless (open FILE, $ENV{"HOME"} . "/.geohashrc")
120 {
121 print "Couldn't open conf file: $!. Using default configuration: $default";
122 return $default
123 }
124 binmode(FILE, ":utf8");
125 $string = <FILE>;
126 close FILE;
127 return $string;
128 }
129
130 $string = read_conf();
131 unless ($string =~ m/\n$/s) {
132 $string .= "\n";
133 }
134 my $data = Load $string;
135 # parse CLI options
136 getopts('d:l:', \%opts);
137 # set date
138 $today = $opts{d} if defined $opts{d};
139
140 # set graticule
141 if (defined $opts{l} and $opts{l} =~ m/^-?[0-9]{1,3}\.-?[0-9]{1,3}$/g) {
142 ($data->{lat}, $data->{lon}) = split /\./, $opts{l};
143 }
144
145 unless (defined $data->{lat} and defined $data->{lon}) {
146 die "Please create a configuration file in ~/.geohashrc. Like this: \n $default";
147 }
148
149 my $key = $data->{lat} . "." . $data->{lon};
150
151 my @places = ();
152 if (defined $data->{$key}) {
153 @places = @{$data->{$key}};
154 }
155 # Get the date
156 unless ($today) {
157 $today = strftime "%F", localtime;
158 }
159
160 my $g = new Geo::Hashing(lat => $data->{lat}, lon => $data->{lon}, date => $today);
161
162 sub deg_to_rad { ($_[0]/180) * $pi }
163 printf "The Location for $today is at %.6f, %.6f.\n", $g->lat, $g->lon;
164
165 use LWP::Simple;
166 import LWP::Simple 'get';
167 $url = sprintf "http://nominatim.openstreetmap.org/reverse?format=xml&lat=%.6f&lon=%.6f&zoom=18&addressdetails=1", $g->lat, $g->lon;
168 unless (defined ($content = get($url))) {
169 die "could not get $url\n";
170 }
171 $content =~ s/^.*<result[^>]*>(.*)$/$1/s;
172 $content =~ s/<.*//s;
173 printf "Location: $content\n";
174
175
176 foreach (@places) {
177 printf "Distance from $_->{name}: %.3f km\n", distance($g->lat, $g->lon, $_->{lat}, $_->{lon});
178 }
179
180