Wednesday, February 29, 2012

Measuring distance with markers

https://files.nyu.edu/hc742/public/googlemaps/distance2.html
Double-click to remove point, click on line to add intermediary point.

maps.google.com has a tool that measures the distance along a path created by successive mouse clicks. To activate the tool, open the Maps Labs link (at the bottom of the left sidebar), activate the Distance Measurement Tool, and then click on the small icon at the bottom left corner of the map.


Once active, clicking on the map allows you to draw a path, whose distance will be shown on the sidebar.

A reproduction of this tool uses Markers, a Polyline, the Geometry library, and some event listeners.

Click on the map to add Markers. A path will be drawn between the markers, and return the distance of the path. Drag a marker to change the path, and double-click one to delete it.



First we declare a marker array and the polyline:

    function initialize() {
      var map;
      var mapOptions = {
        center: new google.maps.LatLng(42.3584308, -71.0597732),
        zoom: 16,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      };


      var markers = [];
      var line;


A difference between this distance measurement tool and the Labs tool is that we will allow points along the path to be dragged and deleted.

Markers are added in the map event listener activated by clicks, and for each of these new Markers, a double-click (for its deletion) and drag listener is added.

        google.maps.event.addListener(map, 'click', function(event) {
          var marker = new google.maps.Marker({
            map: map,
            position: event.latLng,
            draggable:true
          });
          markers.push(marker);
          drawPath();


          google.maps.event.addListener(marker, 'dblclick', function(event) {
            marker.setMap(null);
            drawPath();
          });
          
          google.maps.event.addListener(marker, 'drag', function(event) {
            drawPath();
          });
        });


Note that marker.setMap(null) removes the marker from view, though it remains in our markers[] array. Managing this array is done in the following function:

      function countMarkers() {
        count = 0;
        for (var i = markers.length - 1; i >= 0; i--) {
          if (markers[i].getMap() == null) {
            markers.splice(i, 1);
          } else {
            count++;
          }
        }
        return count;
      }


We wish to remove all markers whose map is set to null, and because removing an item shifts subsequent items to the left, we traverse the array backwards. Otherwise, we would run out of bounds after removing any single (or more) item from the middle of the array. splice removes one item (amount given in the second argument) at index i.


Next, knowing where all our active points are, we pass this information to update the Polyline display.

      function drawPath() {
        countMarkers();
        var coords = [];
        for (var i = 0; i < markers.length; i++) {
          coords.push(markers[i].getPosition());
        }
        line.setPath(coords);


        meters = google.maps.geometry.spherical.computeLength(coords);
        $("distKm").value = Math.round(meters/1000 * 100) / 100;
        $("distMi").value = Math.round(meters/1609 * 100) / 100;
      }

At the same time, the distance is measured with the geometry library, using a simple call to computeLength. The result is in meters, and conversions to kilometers and miles are done to update the textboxes. Finally, a straightforward function clears all the markers.

      function clearMarkers() {
        for (var i = 0; i < markers.length; i++) {
          markers[i].setMap(null)
        }
        drawPath();
      }

The HTML form looks like this:

          <form id="form">
            <input id="distKm" type="text"> km<br />
            <input id="distMi" type="text"> miles<br />
            
            <input id="button1" type="button" value="Clear markers" onclick="clearMarkers()">
          </form>


Full source code:

Friday, February 24, 2012

Finding the address for a given spot

Reverse geocoding estimates the address of a given map point. Result accuracy depends on what information is stored in Google Maps servers.

In this example, an address will be returned after each mouse click anywhere on land.

Click here to open

In addition to the minimal initialization with global variables map and geocoder, an event listener is added to initialize().

function initialize() {
        geocoder = new google.maps.Geocoder();
        map = new google.maps.Map($("map_canvas"),
          mapOptions);
        google.maps.event.addListener(map, 'click',
          function(event) {
            getAddress(event.latLng);
          }
        );
      }


The HTML body contains a form with a text field for the retrieved address, and two zoom controls. By default, the map is zoomed and centered with a mouse click.


A mouse click calls getAddress with the captured latLng. The geocoder checks for two things: whether there is a result for the location, and the status of the geocoding request. In this example, if either value returns zero or not OK, the error is shown in the address field.



      function getAddress(latLng) {
        geocoder.geocode(
          {latLng: latLng},
          function(results, status) {
            if(status == google.maps.GeocoderStatus.OK) {
              if(results[0]) {


                $("text_address").value =
                   results[0].formatted_address;


                // zoom controls, of secondary importance
                if(map.getZoom() < 16) {
                  if($("autoZoom").checked) {
                    map.setCenter(latLng);
                    map.setZoom(map.getZoom() + 2);
                  }
                }


              } 
              else {
                $("text_address").value = "No results";
              }
            }
            else {
              $("text_address").value = status;
            }
          }
        );
      }

Friday, February 17, 2012

Adding markers for given locations

Today I'll cover the first example given in this documentation page. Try interacting with the map below. For the given scale, cities show up best in the map, although street addresses all the way to continent names will be recognized.

Click here to open

First, we have the standard header and JavaScript include for the Google Maps API.

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>

Next, before defining the initialize() function, both map and geocoder variables should be declared global, because they will be called from more than one function. The geocoder is initialized with the map.

    <script type="text/javascript">
      var map;
      var geocoder;
      mapCenter = new google.maps.LatLng(39.11, 0.94);


      function initialize() {
        geocoder = new google.maps.Geocoder();
        map = new google.maps.Map(document.getElementById("map_canvas"),
        { center: mapCenter, 
          zoom: 1, 
          mapTypeId: google.maps.MapTypeId.ROADMAP
        });
      }

The HTML body and form contain calls to two map functions, addMarkers() and zoomout(). 

  <body onload="initialize()">
    Add a list of cities in the text box, one per line:
    <form action="#">
      <textarea rows="4" cols="50" id="cityList">Chicago
Taipei
Curitiba</textarea><br>
      <input type="button" value="Clear text" onclick="document.getElementById('cityList').value=''">
      <input type="button" value="Add markers" onclick="addMarkers(document.getElementById('cityList').value)">
      <input type="button" value="Zoom out" onclick="zoomout()">
    </form>
    <div id="map_canvas" style="width:500px; height:300px"></div>
  </body>


zoomout() is straightforward, and uses the global variable mapCenter:

      function zoomout() {
        map.setZoom(1);
        map.setCenter(mapCenter);
      }

The core function, addMarkers, does two things:
  • add markers for each location given by the user, line by line
  • set up mouse click event listeners for each marker to center the map to itself and zoom in
      function addMarkers(cityList) {
        var cities = cityList.split('\n');


        for (var i = 0; i < cities.length; i++) {
          geocoder.geocode(
            { address: cities[i] },
            function(results, status) {
              var marker = new google.maps.Marker({
                map: map,
                position: results[0].geometry.location
              })


             google.maps.event.addListener(marker, 'click',
                function(event) {
                  map.setCenter(event.latLng);
map.setZoom(8);
                });
            }
         );
        }
      }


For each city in our list, we use the geocoder to accept a city name, such as "Sacramento," and convert it to a latitude and longitude that is used to place a marker. The first argument, given in curly braces, is the GeocodeRequest object literal. Passing a (human-readable) address returns a latitude and longitude, and vice-versa. 

Following the request is the function that holds the return value, in our case, the latitude and longitude for each address. Note that we are ignoring the status code here. First, we can place a marker with the geometry.location field, whose type is latLng.

Once a marker is set, an event listener can be added. The first parameter is the object receiving the listener, the second, an event type (found here under "Events" for each class), and the third parameter, the function to be executed when the event is triggered.

Thursday, February 16, 2012

Getting started

There are a few different ways of adding Google Maps content to your website:

If you only need markers and static content, the easiest way is to find the desired location directly on http://maps.google.com/ and add any markers, lines and shapes with the "My places" option (you need a Google account to use this feature).

Next, embedding from maps.google.com is very simple. In that website, click on the link icon (circled below) and copy the given links.



Another straightforward option is going to Google Web Elements and choosing Maps under the Search tab. I find this option more limited because only one marker may be added.

Finally, there is Google Maps API, the focus of this blog (specifically the Javascript API). The following map is stored in my NYU web space. This page is merely a copy of the "Hello World" example from Google Maps API's tutorial page.

Click here to open

Introducing Explore Google Maps

Welcome to Explore Google Maps!

The objective of this blog is to show and explain the usage of Google Maps Javascript v3 API, and cover applications such as referencing markers and areas to external sources.

References will be given and I will provide step-by-step instructions to accomplish each demonstration.