Saturday, March 10, 2012

Revisiting a single InfoWindow setup


Instead of keeping track of several InfoWindows, we can store the information with each marker and use only one global InfoWindow.



Global variables:

      var infowindow;
      var count = 0;

Event listener and InfoWindow creation:

      function initialize() {
        map = new google.maps.Map($("map_canvas"), mapOptions);
        google.maps.event.addListener(map, 'click', function (event) {
          addMarker(event.latLng, "InfoWindow index #" + count);
          count += 1;
        });


        infowindow = new google.maps.InfoWindow({ });
      }

Defining a marker:


      function addMarker(pos, content) {
        var marker = new google.maps.Marker({
          map: map,
          position: pos
        });


When clicked, the marker opens the InfoWindow with the current content.


        google.maps.event.addListener(marker, 'click', function (event) { 
          infowindow.setContent(content);
          infowindow.open(map, marker);
        });
      }


Full source code:

Thursday, March 8, 2012

Zooming in with a window

I often get tired of using the scroll wheel to zoom, and get too lazy to move the mouse to the zoom slider. Here's an alternative to these zoom methods: define a zoom window to cover an area of interest.


Left-click to define a corner, let go, and move the mouse to draw the zoom window. Click again to zoom in. Right-click to cancel an open rectangle. Double right-click to zoom out (this behavior is standard).


In the definitions, I add a two helper functions to simplify the creation of LatLngs and LatLngBounds.

      var map;
      var mapOptions = {
        center: new google.maps.LatLng(0.0, 0.0),
        zoom: 2,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      };
      var state = 0;
      var bounds;
      var pt1, pt2;
      var rect;


      function toLatLng(lat, lng) {
        return new google.maps.LatLng(lat, lng);
      }


      function toBounds(j,k) {
        var pts = [];
        var latMin, latMax, lngMin, lngMax;
        var sw, ne;


        latMin = Math.min(j.lat(), k.lat());
        latMax = Math.max(j.lat(), k.lat());


        lngMin = Math.min(j.lng(), k.lng());
        lngMax = Math.max(j.lng(), k.lng());


        sw = toLatLng(latMin, lngMin);
        ne = toLatLng(latMax, lngMax);
        return new google.maps.LatLngBounds(sw, ne);
      }


The initialize() function defines the map and the Rectangle's style. To minimize the effects of Internet Explorer's slow refresh, the size is fixed around (0,0). 


Next, a click listener on the map serves to place the first corner of the zoom window. The 'state' variable keeps track of whether the zoom window is open or not.

        google.maps.event.addListener(map, 'click', function(event) {
          if(state == 0) {
            pt1 = event.latLng;
            rect.setMap(map);
            state = 1;
          }
        });


With movement (mousemove), the Rectangle (zoom window) resizes according to the current position:

        google.maps.event.addListener(map, 'mousemove', function(event) {
          if(rect.getMap() == map) {
            rect.setBounds(toBounds(pt1, event.latLng));
          }
        });


Finally, if another click is detected (note that once the Rectangle is visible, it lies on top of the map) the Rectangle is hidden and the map is zoomed in.

        google.maps.event.addListener(rect, 'click', function(event) {
          rect.setMap(null);
          rect.setBounds(toBounds(toLatLng(0.0, 0.0), toLatLng(0.000001,0.000001)));
          pt2 = event.latLng;
          map.fitBounds(toBounds(pt1, pt2));
          state = 0;
        });


Finally, we add some behavior to correct the size of the zoom window. If movement is detected in the direction of the Rectangle, its size is reduced.



        // Allows shrinking the zoom window
        google.maps.event.addListener(rect, 'mousemove', function(event) {
          if(rect.getMap() == map) {
            rect.setBounds(toBounds(pt1, event.latLng));
          }
        });

and right-click closes an open Rectangle.

        // Cancel the zoom window with a left click
        google.maps.event.addListener(rect, 'rightclick', function(event) {
          rect.setMap(null);
          rect.setBounds(toBounds(toLatLng(0.0, 0.0), toLatLng(0.000001,0.000001)));
          state = 0;
        });


Full source code:

Friday, March 2, 2012

Keeping a single InfoWindow open

For a long time I couldn't find a good solution for this scenario: keep multiple markers on the map, with a corresponding InfoWindow assigned to each marker. At most one InfoWindow should stay open. (Here's another way of accomplishing this)



In addition to the map, we declare a global array variable, infowindows. Accessing this variable allows us to close all InfoWindows.

When initializing the map, we add a click listener so we can place markers.


      function initialize() {
        map = new google.maps.Map($("map_canvas"), mapOptions);
        google.maps.event.addListener(map, 'click', function (event) {
          addMarker(event.latLng, "InfoWindow index #" + infowindows.length);
        });
      }

When adding markers, we also add the InfoWindow and its contents at the same time.


      function addMarker(pos, content) {
        var marker = new google.maps.Marker({
          map: map,
          position: pos
        });


        var infowindow = new google.maps.InfoWindow({ 
          content: content
        });


        infowindows.push(infowindow);


        google.maps.event.addListener(marker, 'click', function (event) {
          closeInfoWindows();
          infowindow.open(map, marker);
        });
      }


Finally, we define a simple function that closes all InfoWindows. Without an array to keep track of them, we cannot locate the previously opened InfoWindow(s).


      function closeInfoWindows() {
        for (var i = 0; i < infowindows.length; i++) {
          infowindows[i].close();
        }
      }


Full source code: