Multi GPS Geocoding

Google Maps API is very useful for mapping and locating sites, assets and sites. Google offers many tiers of service. Visit the Google Maps API page to get your own key.

A project came up where I needed to plot out GPS coordinates and determine which country they were in. The list was over 4,000 sites long. Since manually looking up each GPS location would have taken days. A short JavaScript made quick work of the list. Ok, it still took hours to make all the calls, but once running the script was able process the information by itself.

This project was ceated at JSFiddle. It’s still available to try out. The API key is Google’s key. Use your own key to take full advatage of the service.

Issue with timeouts

A big issue that I ran into was Google only always so many calls before the requests are refused. Using a setTimeout I was able to delay the calls far enough that even on the free tier of Google Maps I didn’t timeout.

You might be wondering why I’m passing the death value in the bigListAction() around instead of keeping it local; since the way it’s set up now, it’ll never pass as anything but false. During a larger trial, I added code that extended the delay time based on the value of the death value. Each time it came through as true I doubled the time, once it came through as false, I havled it until it went back to 15000.

HTML

The HTML is quick. I needed button to start the action.

<input id="listBigAction" type="button" value="Start Processing">

A div to place the data. Granted this could be a file instead.

<div id="bigList"></div>

This div is used to attach the Google Map object. This allows the Map object to output a map. I didn’t need to output a map since I only needed the country name.

<div id="map"></div>

The script for the Google Map API. In the Fiddle above I’m using the Google General API Key. It only works from JS Fiddle.

<!-- Replace the value of the key parameter with your own API key. -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=[GOOGLE-MAP-API-KEY]&callback=initMap">
</script>

CSS

CSS doesn’t have any effect on the workings so go ahead and style it any way you want.

JavaScript

Now for the real meat of the project.

The first function initMap() is called by the Google Map API. A map object and geocoder object are created. The geocoder object is used in call to a function that is attached to a button.

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {});

  var geocoder = new google.maps.Geocoder;

  document.getElementById('listBigAction').addEventListener('click', function() {
    bigListAction(geocoder, 0, false);
  });
}

The bigListAction() handles the calling for the location information, pulling that information out and then adding it to the list.

The function is passed the geocoder object created earlier, the current asset number, and if there was an issue the last time around.

function bigListAction(geocoder, current, death) {

First part is to make a contentNode. This is used to store the data that is to be outputted. The latlng object is the current location from the assets object.

  var contentNode = document.createElement('div');
  var latlng = {
    lat: parseFloat(assets[current].Latitude),
    lng: parseFloat(assets[current].Longitude)
  };

Here’s were we make the call to get the information.

  geocoder.geocode({
    'location': latlng
  }, function(results, status, assetTag) {

Once the call is returned this part will run if the status is OK. First duty is to check if a result is returned.

    if (status === 'OK') {
      if (results[1]) {

If data is present, cycle through until the type is country

        for (var unit in results[1].address_components) {
          if (results[1].address_components[unit].types[0] == "country") {

If it’s country grab the rest of the data.

            var assetTag = assets[current].Reference;
            var content = current +','+ assetTag + ',' + results[1].address_components[unit].long_name;
            var newNode = document.createElement('div');

Store the data in the proper container.

            newNode.innerHTML = content;
            contentNode.appendChild(newNode);
          }
        }

Error sections if there is no results or if there is some other error. If you want it to continue even with one failing because of no location, don’t set the death to true and it’ll move on to the next asset.

      } else {
        window.alert('No results found' + status);
        death = true;
      }
    } else {
      window.alert('Geocoder failed due to: ' + status);
      console.log(results)
      death = true;
    }
  });

Adding the contentNode that was created using the retrieved data to the bidList.

  document.getElementById("bigList").appendChild(contentNode);

The looping is done through a simple setTimeout. First parameter is the function to be called. And then how many milliseconds to delay until the next location call. The next three is the passed directly to the function; geocoder, the current asset number and weather there was an issue.

  if ((current < (assets.length-1)) && (!death)) {
    window.setTimeout(bigListAction, 15000, geocoder, (current+1), false);
  };
}

The Assets object could be loaded from a file, or API or however you want. This needs to be done before the API starts to get called. Since the calls don’t start until the button is pushed and the Assets object is locally located, I doubt that someone will start pushing the button before the object is loaded. If there is a chance that the data won’t be available, a check needs to put in place to keep errors from happening.

var assets = [
  {
    "Latitude": 0.58554,
    "Longitude": 16.7772,
    "Reference": 100
  },
  {
    "Latitude": -12.989,
    "Longitude": 40.5262,
    "Reference": 101
  },
  {
    "Latitude": -16.109,
    "Longitude": 33.6443,
    "Reference": 102
  },
  {
    "Latitude": 17.7012,
    "Longitude": -64.7557,
    "Reference": 103
  },
  {
    "Latitude": 17.9515,
    "Longitude": -102.175,
    "Reference": 104
  },
  {
    "Latitude": 17.9515,
    "Longitude": -102.175,
    "Reference": 105
  },
  {
    "Latitude": 17.9515,
    "Longitude": -102.175,
    "Reference": 106
  },
  {
    "Latitude": 17.9518,
    "Longitude": -102.176,
    "Reference": 107
  },
  {
    "Latitude": 44.7904,
    "Longitude": -93.4117,
    "Reference": 108
  },
  {
    "Latitude": 18.3309,
    "Longitude": -64.7927,
    "Reference": 109
  },
  {
    "Latitude": 38.5975,
    "Longitude": -90.128,
    "Reference": 110
  },
  {
    "Latitude": 18.3363,
    "Longitude": -64.9482,
    "Reference": 111
  },
  {
    "Latitude": 18.3559,
    "Longitude": -77.3978,
    "Reference": 112
  }
];