User:AeroIllini/HashPlacer

From Geohashing

HashPlacer

The HashPlacer is a perl script that allows you to define limit boxes on a map, and will notify you via email if the day's geohash point lands in one of these boxes. This is especially useful if you define a limit box surrounding your favorite park, or your neighborhood, or even your couch. Currently the only areas supported are "boxes"... two latitude points and two longitude points define the diagonal corners of a graticule-shaped "box".

Requirements

Runs under Linux with Perl 5.10 and Sendmail. I run it as a cron job at about 9:15am EST every day, although it could be modified to notify about the weekend's coordinates on Friday morning.

This script requires the following CPAN Modules:

Source Code

#!/usr/bin/perl -w
#
# HashPlacer - (C) 2010 AeroIllini <aeroillini@gmail.com>
#
# This script is designed to notify you via email when a Geohash point
# for a particular day happens to fall within a limit box you define.
# One possible implementation would be to set the box to a square
# encompassing your house; a hash landing in this limit box would win
# you the coveted Couch Potato Geohashing Award. Or perhaps the box
# could be set to a range you are willing to go on short notice; maybe
# your neighborhood or a portion of your city, so you would get
# notification when the hash lands here and you can jet off on a short-
# notice expedition.
#
# The script supports checking an arbitrary number of limit boxes in
# an arbitrary number of graticules. Shapes other than boxes are not
# yet supported.
#
# More information on Geohashing:
# <http://wiki.xkcd.com/geohashing/Main_Page>
#
# ---------------------------------------------------------------------
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# ---------------------------------------------------------------------

use strict;
use Geo::Hashing;
use List::Util qw(min max);
use Email::Sender::Simple qw(sendmail);
use Email::Simple;
use Email::Simple::Creator;

# Set these values to your own notification limits. Each limit box is defined
# by a pair of latitude decimals and a pair of longitude decimals. You can
# add more defined boxes by adding additional entries; just be sure to give
# them unique names.
my %boxlist = ( 'box01' => {    'validfor'=> '47,-122',           # the graticule the box is in
                                'lat'     => '0.65395,0.66733',   # latitude max and min
                                'lon'     => '0.40341,0.42242',   # longitude max and min
                                'message' => 'Discovery Park!' }, # message to appear in email
                'box02' => {    'validfor'=> '47,-122',
                                'lat'     => '0.62533,0.60126',
                                'lon'     => '0.34806,0.30196',
                                'message' => 'Downtown Seattle!' }
                                );

# Specify where to send the email
my $mailto = '"Geohasher" <address@example.com>';

# Grab the current date
my $date = sprintf("%04d-%02d-%02d",    (localtime)[5]+1900,
                                        (localtime)[4]+1,
                                        (localtime)[3]);

# Start the loop
for my $boxname ( keys %boxlist ) {

        # Parse the limit box variables
        my ( $intlat, $intlon ) = split /\s*,\s*/, $boxlist{$boxname}{'validfor'};
        my ( $lat1, $lat2 ) = split /\s*,\s*/, $boxlist{$boxname}{'lat'};
        my ( $lon1, $lon2 ) = split /\s*,\s*/, $boxlist{$boxname}{'lon'};
        my $msg = $boxlist{$boxname}{'message'};

        # Create the limit values for this graticule, correcting for negative values
        if ( $intlat >= 0 ) {
                $lat1 += $intlat;
                $lat2 += $intlat;
        } else {
                $lat1 = $intlat-$lat1;
                $lat2 = $intlat-$lat2;
        }
        if ( $intlon >= 0 ) {
                $lon1 += $intlon;
                $lon2 += $intlon;
        } else {
                $lon1 = $intlon-$lon1;
                $lon2 = $intlon-$lon2;
        }

        # Create a new hash object to do all the work. Magic happens!
        my $geo = new Geo::Hashing(lat => $intlat, lon => $intlon, date => $date);

        # Check to see if we have a result
        if ($geo->lat && $geo->lon) {

                # Check to see if the point is inside our box
                if (    $geo->lat >= min ($lat1,$lat2) &&
                        $geo->lat <= max ($lat1,$lat2) &&
                        $geo->lon >= min ($lon1,$lon2) &&
                        $geo->lon <= max ($lon1,$lon2) ) {

                        # It is! Quick, notify somebody!
                        notifier ($date, $intlat, $intlon, $msg);
                } else {
                        # Not in the box. Back to your regularly scheduled lives.
                }
        } else {
                # No results were given by the Geo::Hashing object. Are you sure it's after 9am EST?
        }

}

# Send the email
sub notifier {
        my $date = shift;
        my $lat = shift;
        my $lon = shift;
        my $msg = shift;
        my $email = Email::Simple->create(
                header => [
                To      => $mailto,
                From    => '"Geohash Notifier" <noreply@xkcd.com>',
                Subject => "Geohash Notifier: $date ($lat, $lon) - $msg",
                        ],
                body => "http://irc.peeron.com/xkcd/map/map.html?date=$date&lat=$lat&long=$lon&zoom=10\n\nhttp://wiki.xkcd.com/geohashing/$lat,$lon",
                );
        my $result = sendmail($email);
}