views:

1550

answers:

2

I'm using the Google Maps API v2. I add markers to the map and then zoom to fit those markers. This works fine if the map is visible I do this. But if not - for example, if I have a tabstrip, and the map's tab isn't selected when the page loads - then when I do show the map, the zoom level and center are wrong.

Here's a simple test case (uses jQuery):

<script type="text/javascript">

    var scale = Math.random() * 20;

    $(document).ready(function() {
        var $container = $('#container');
        // $container.hide();
        var map = new GMap2($('#map')[0]);
        $container.show();
        var markerBounds = new GLatLngBounds();
        for (var i = 0; i < 10; i++) {
            var randomPoint = new GLatLng(38.935394 + (Math.random() - 0.5) * scale, -77.061382 + (Math.random() - 0.5) * scale);
            map.addOverlay(new GMarker(randomPoint));
            markerBounds.extend(randomPoint);
        }
        map.setCenter(markerBounds.getCenter(), map.getBoundsZoomLevel(markerBounds));
    });

</script>

<div id="container">
    <div id="map" style="margin: 100px; width: 450px; height: 300px;"></div>
</div>

This works fine as is, but if you uncomment $container.hide() it's all whacked out.

Is there a way to get the Google Maps API to work properly on a div that's not visible?

+1  A: 

All you would need to do is to create the GMaps2() before anything else. You can then hide() the container, add the points, get the getBoundsZoomLevel(), show() it again, and it should work fine.

Try the following:

$(document).ready(function() {
   var $container = $('#container');

   // First create the Map.
   var map = new GMap2($('#map')[0]);

   // The container can be hidden immediately afterwards.
   $container.hide();

   // Now you can do whatever you like!
   var markerBounds = new GLatLngBounds();
   for (var i = 0; i < 10; i++) {
      var randomPoint = new GLatLng( 38.935394 + (Math.random() - 0.5) * scale, 
                                    -77.061382 + (Math.random() - 0.5) * scale);
      map.addOverlay(new GMarker(randomPoint));
      markerBounds.extend(randomPoint);
   }
   map.setCenter(markerBounds.getCenter(), map.getBoundsZoomLevel(markerBounds));

   // Finally unhide the container.
   $container.show();
});

getBoundsZoomLevel

Daniel Vassallo
You're right, that does work. The way my tabstrip works, though, is that everything is hidden on load (with CSS) and then the correct tab is displayed. Otherwise there would be a brief period in which all tabs are visible. So if it's at all possible I'd like to find a way to create the `GMap2` on a div while it's still hidden.
Herb Caudill
@Herb: Unfortunately it looks like the `GMap2` is not created properly with the mapping div set to `display: none`. If you search on google for "getBoundsZoomLevel returning 0" you will find some related stories. I think that your best bet is to work around this problem, by tweaking how your tabs work... I don't know if this may help, but if you hide the map with `$container.css('visibility', 'hidden');`, you can call `GMap2()` afterwards and it would work fine when you resume the visibility with `$container.css('visibility', 'visible');`.
Daniel Vassallo
@Daniel - Thanks. This is very helpful. The `display:none` vs `visibility:hidden` distinction is interesting - not something I'd really understood before. It wouldn't work to use the visibility property to hide the tabs, since items with `visibility:hidden` still take up space on the page.
Herb Caudill
A: 

Here's what I've ended up doing, for what it's worth.

            $(".TabPanel").watch("display,visibility", function() {
                $(".MapContainer", this).each(function() {
                    if ($(this).is(":visible") == true) {
                        $(this).zoomToFitMarkers();
                    };
                });
            });

This uses Rick Strahl's monitoring plugin for jQuery to watch the tab panel for visibility changes, and then reapplies the zoom logic.

For completeness here's my zoomToFitMarkers extension:

$.fn.zoomToFitMarkers = function() {
    var map = this[0];
    map.gmap.checkResize();
    map.bounds = new GLatLngBounds();
    if (!!map.gmap.getOverlays) {
        for (i = 0; i < map.gmap.getOverlays.length; i++) {
            map.bounds.extend(map.gmap.getOverlays[i].getLatLng());
        }
        if (map.bounds && !map.bounds.isEmpty()) {
            var zoomLevel = map.gmap.getBoundsZoomLevel(map.bounds);
            zoomLevel = zoomLevel > 9 ? 9 : zoomLevel;
            zoomLevel = zoomLevel < 2 ? 2 : zoomLevel;
            map.gmap.setCenter(map.bounds.getCenter(), zoomLevel);
        }
    }
    map.gmap.checkResize();
};

This relies on a couple of conventions:

  1. The GMap2 object is stored in map.gmap, where map is the target DOM element:

    var map= $("div#MapTarget")[0];
    map.gmap = new google.maps.Map2(map);
    
  2. Each time a marker is added to the map, it's stored in an array for future use:

    var marker = new GMarker(point);
    map.gmap.addOverlay(marker);
    // Keep track of new marker in getOverlays array
    if (!map.gmap.getOverlays) map.gmap.getOverlays = new Array();
    map.gmap.getOverlays.push(marker);
    
Herb Caudill