Work the Shell - Exploring Lat/Lon with Shell Scripts

 in
Never get lost at the command line again.

(The invocation is substitute/old-pattern/new-pattern/.)

Now we've got what we set out to create initially. Let's try it with yet another address:

$ sh whereis.sh 1313 S. Disneyland Drive, Anaheim CA
33.814413,-117.924424

Yep, that's the parking structure for Disneyland in California.

Distance between Two Points

Now comes the hard part of this, actually. We can get the lat/lon of any address we desire, but calculating the distance between two points is a bit more tricky, as the mathematics involved is rather hairy, because what we're basically going to do is measure relative to the circumference of Earth.

I found a formula in JavaScript on-line as a starting point:

var R    = 6371;        // kilometers
var dLat = (lat2-lat1);
var dLon = (lon2-lon1);
var a    = Math.sin(dLat/2) * Math.sin(dLat/2) +
           Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
           Math.sin(dLon/2) * Math.sin(dLon/2);
var c    = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d    = R * c;

In this case, the circumference is R, and it's 6,371km. Because Earth is an oblate spheroid, not a perfect sphere, I expect this will have some small level of error, but let's proceed and see where we get.

To accomplish any sophisticated mathematics in a Linux shell, we're pretty much stuck with bc, but it's plenty powerful enough for this task, even if it's a bit clunky.

As an example, here's how you'd set the value of pi within a bc script:

pi=$(echo "scale=10; 4*a(1)" | bc -l)

The first stumble we have is that bc wants to work with radians, not degrees, but the lat/lon values we're getting are in degrees, so we need to convert them.

But before we do that, here's the intermediate output we seek, as we now need to work with two addresses, not just one:

$ sh farapart.sh \
  "1600 pennsylvania ave, washington dc" \
  "1313 s. disneyland drive, anaheim, ca"

Lat/long for 1600 pennsylvania ave, washington dc

= 38.89859, -77.035971

Lat/long for 1313 s. disneyland drive, anaheim, ca

= 33.814413, -117.924424

Next month, we'll crack open the script to see how I am working with two addresses at the same time and splitting it into the four variables we'll later need. Then, we'll look at how to use bc to do the math.

Dave Taylor has been involved with UNIX since he first logged in to the on-line network in 1980. That means that, yes, he's coming up to the 30-year mark now. You can find him just about everywhere on-line, but start here: www.DaveTaylorOnline.com. In addition to all his other projects, Dave is now a film critic. You can read his reviews at www.DaveOnFilm.com.

______________________

Dave Taylor has been hacking shell scripts for over thirty years. Really. He's the author of the popular "Wicked Cool Shell Scripts" and can be found on Twitter as @DaveTaylor and more generally at www.DaveTaylorOnline.com.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

error?

Anonymous's picture

When I run this script I get:
whereis: 7: Syntax error: Unterminated quoted string

Here is the script as I have it:

#!/bin/sh

url='http://api.maps.yahoo.com/ajax/geocode'
args='?appid=onestep&qt=1&id=m&qs="
converter="$url$args"

addr="$(echo $* | sed 's/ /+/g')"
  curl -s "$CONVERTER$ADDR" | \
      sed -e 's/.*{\("Lat":[^}]*\).*/\1/' -e 's/"L.."://g'
exit 0

Here is the command I'm running:
sh whereis 2001 Blake Street, Denver, CO

This seems pretty simple but I cannot see what I'm missing.

Cupla Problems

Mitch Frazier's picture

You've got a couple problems in your script, the one that's giving you the error is here:

args='?appid=onestep&qt=1&id=m&qs="

You've got a string with mismatched quotes: it starts with a single quote and ends with a double quote, you need this:

args='?appid=onestep&qt=1&id=m&qs='
#                                 ^

or this

args="?appid=onestep&qt=1&id=m&qs="
#    ^

The other problem is that the shell is case sensitive, so you can't use "converter" and "CONVERTER" interchangeably, pick one. Same with "addr" and "ADDR". So change:

  curl -s "$CONVERTER$ADDR" | \

to

  curl -s "$converter$addr" | \

p.s. The first error (the mismatched quotes) was in the original text, it has now been fixed.

Mitch Frazier is an Associate Editor for Linux Journal.

What about bad addresses?

Grishnakh's picture

Is there any way to tell if the address you input is invalid? I've tried a few made-up bad addresses and it still seems to give valid lat/lon coordinates.

bad address

Anonymous's picture

That's a "Feature" of yahoo's service. They'll give you their best estimate. If the house number is bad, they'll tell you the location of the street; if the street is bad, they'll tell you the location of the city; if the city is bad, they'll tell you the location of the state....

To tell if the address is bad you could, perhaps, compare the results with the results of a less specific look up.

shell errors

zeugma's picture

As written, I get only longitude.

when I change the sed to...

    cut -d\" -f11,13 | \

I get the expected output

$ cat whereis.sh
#!/bin/bash

URL='http://api.maps.yahoo.com/ajax/geocode'
ARGS='?appid=onestep&qt=1&d=m&qs='
CONVERTER="$URL$ARGS"
ADDR="$(echo $* | sed 's/ /+/g')"

curl -s "$CONVERTER$ADDR" | \
    cut -d\" -f11,13 | \
    sed 's/[^0-9\.\,\-]//g;s/,$//'
exit 0
$ ./whereis.sh 1313 S Disneyland Drive, Anaheim, CA
33.814413,-117.924424

Response Is Different

Mitch Frazier's picture

The response you're getting is different, the GeoID value is not a quoted value as above. The following would avoid problems related to quotes:

  curl -s "$CONVERTER$ADDR" | \
      sed -e 's/.*{\("Lat":[^}]*\).*/\1/' -e 's/"L.."://g'

Mitch Frazier is an Associate Editor for Linux Journal.

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix