1

I have a code for toggling the visibility of markers by category, which works well (see code below).

What I now would like to add and cannot figure out how:

After each show/hide of a markergroup, I would like the zoom and center of the map to be adjusted to show all visible markers.
I have found another code for fitBounds(), but don't manage to get the two to work together (I am absolutely new to Javascript).

I have found examples of just fitting bounds to visible markers, but not in combination with showing/hiding markers by category. (e.g. Center/Set Zoom of Map to cover all visible Markers?)

Here the two code examples:

Show/hide markers by category:

function xmlParse(str) {
    if (typeof ActiveXObject != 'undefined' && typeof GetObject != 'undefined') {
        var doc = new ActiveXObject('Microsoft.XMLDOM');
        doc.loadXML(str);
        return doc;
    }

    if (typeof DOMParser != 'undefined') {
        return (new DOMParser()).parseFromString(str, 'text/xml');
    }

    return createElement('div', null);
}
var infoWindow = new google.maps.InfoWindow();
var customIcons = {
    monumento: {
        icon: 'http://maps.google.com/mapfiles/ms/icons/blue.png'
    },
    hotel: {
        icon: 'http://maps.google.com/mapfiles/ms/icons/green.png'
    },
    restaurantes: {
        icon: 'http://maps.google.com/mapfiles/ms/icons/yellow.png'
    },
    museus: {
        icon: 'http://maps.google.com/mapfiles/ms/icons/purple.png'
    }
};

var markerGroups = {
    "museus": [],
    "monumentos": [],
    "restaurantes": [],
    "hotel": []
};

function load() {
    var map = new google.maps.Map(document.getElementById("mapCanvas"), {
        center: new google.maps.LatLng(38.639104, -8.210413),
        zoom: 12,
        mapTypeId: 'satellite'
    });
    var infoWindow = new google.maps.InfoWindow();



    map.set('styles', [{
        zoomControl: false
    }, {
        featureType: "road.highway",
        elementType: "geometry.fill",
        stylers: [{
            color: "#ffd986"
        }]
    }, {
        featureType: "road.arterial",
        elementType: "geometry.fill",
        stylers: [{
            color: "#9e574f"
        }]
    }, {
        featureType: "road.local",
        elementType: "geometry.fill",
        stylers: [{
                color: "#d0cbc0"
            }, {
                weight: 1.1
            }

        ]
    }, {
        featureType: 'road',
        elementType: 'labels',
        stylers: [{
            saturation: -100
        }]
    }, {
        featureType: 'landscape',
        elementType: 'geometry',
        stylers: [{
            hue: '#ffff00'
        }, {
            gamma: 1.4
        }, {
            saturation: 82
        }, {
            lightness: 96
        }]
    }, {
        featureType: 'poi.school',
        elementType: 'geometry',
        stylers: [{
            hue: '#fff700'
        }, {
            lightness: -15
        }, {
            saturation: 99
        }]
    }]);

    //         downloadUrl("markers.xml", function (data) {
    var xml = xmlParse('<markers><marker name="Castelo" address="Rua da Condessa de Valença" lat="38.64351973190569" lng="-8.216521812152905" type="monumento" /><marker name="Anta 1 de Tourais" address="Estrada Nacional 114" lat="38.64260059929888" lng="-8.159376865959189" type="monumento" /><marker name="Hotel da Ameira" address="Herdade da Ameira" lat="38.64109640475479" lng="-8.180432206726096" type="hotel" /><marker name="Hotel Montemor" address="Avenida Gago Coutinho 8, 7050-248 Montemor-o-Novo" lat="38.64925541964039" lng="-8.216489625644726" type="hotel" /><marker name="Restaurante Monte Alentejano" address="Av. Gago Coutinho 8" lat="38.6492329" lng="-8.216665" type="restaurantes" /><marker name="Restaurante A Ribeira" address="Rua de São Domingos" lat="38.6347498199708" lng="-8.206468892765088" type="restaurantes" /><marker name="Núcleo Museológico do Convento de S. Domingos" address="" lat="38.643139" lng="-8.212732" type="museus" /><marker name="Centro Interpretativo do Castelo de Montemor-o-Novo" address="Rua Condessa de Valença" lat="38.64258748216167" lng="-8.21467108793263" type="museus" /></markers>');
    // var xml = data.responseXML;
    var markers = xml.documentElement.getElementsByTagName("marker");
    for (var i = 0; i < markers.length; i++) {
        var name = markers[i].getAttribute("name");
        var address = markers[i].getAttribute("address");
        var type = markers[i].getAttribute("type");

        var point = new google.maps.LatLng(
            parseFloat(markers[i].getAttribute("lat")),
            parseFloat(markers[i].getAttribute("lng")));
        var html = "<b>" + name + "</b> <br/>" + address;
        // var icon = customIcons[type] || {};
        var marker = createMarker(point, name, address, type, map);
        bindInfoWindow(marker, map, infoWindow, html);
    }
    // });
}

function createMarker(point, name, address, type, map) {
    var icon = customIcons[type] || {};
    var marker = new google.maps.Marker({
        map: map,
        position: point,
        icon: icon.icon,
        // shadow: icon.shadow,
        type: type
    });
    if (!markerGroups[type]) markerGroups[type] = [];
    markerGroups[type].push(marker);
    var html = "<b>" + name + "</b> <br/>" + address;
    bindInfoWindow(marker, map, infoWindow, html);
    return marker;
}

function toggleGroup(type) {
    for (var i = 0; i < markerGroups[type].length; i++) {
        var marker = markerGroups[type][i];
        if (!marker.getVisible()) {
            marker.setVisible(true);
        } else {
            marker.setVisible(false);
        }
    }
}


function bindInfoWindow(marker, map, infoWindow, html) {
    google.maps.event.addListener(marker, 'click', function() {
        infoWindow.setContent(html);
        infoWindow.open(map, marker);

    });
}

function downloadUrl(url, callback) {
    var request = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();

    request.onreadystatechange = function() {
        if (request.readyState == 4) {
            request.onreadystatechange = doNothing;
            callback(request, request.status);
        }
    };

    request.open('GET', url, true);
    request.send(null);
}

function doNothing() {}
google.maps.event.addDomListener(window, 'load', load);

fitBounds() to visible markers:

function fitBoundsToVisibleMarkers() {

   var bounds = new google.maps.LatLngBounds();

   for (var i = 0; i < markers.length; i++) {
       if (markers[i].getVisible()) {
           bounds.extend(markers[i].getPosition());
       }
   }

   map.fitBounds(bounds);
}
Community
  • 1
  • 1
gliitschi8
  • 33
  • 6
  • Possible duplicate of [Center/Set Zoom of Map to cover all markers visible Markers?](http://stackoverflow.com/questions/19304574/center-set-zoom-of-map-to-cover-all-markers-visible-markers). Also, Can you create a snippet or bin with those examples so we could help you? – Mosh Feu Jan 24 '16 at 10:18
  • hi Mosh Feu, thank you for the comment! here is a jsfiddle of the first code: http://jsfiddle.net/YEPB7/6/ does this help? – gliitschi8 Jan 24 '16 at 10:24

2 Answers2

0

It just needs a slight adjustment to your toggleGroup function. At this point you're already setting the markers visibility; just add a few lines there to also add those visible markers to a LatLngBounds object. Then after you've adjusted all the markers, update the map to fit that new Bounds.

function toggleGroup(type) {
    var bounds = new google.maps.LatLngBounds();

    for (var i = 0; i < markerGroups[type].length; i++) {
        var marker = markerGroups[type][i];
        if (!marker.getVisible()) {
            marker.setVisible(true);
            bounds.extend(marker.getPosition());
        } else {
            marker.setVisible(false);
        }
    }

    map.fitBounds(bounds);
}

The only other thing you'd need to do is make your map variable global; right now it's just local to the load() function.

duncan
  • 31,401
  • 13
  • 78
  • 99
0

There are some problem with your code:

  1. You got an error: Uncaught TypeError: map.fitBounds is not a function. That's because you need to set the map variable out of the function load.
  2. You need to collect the all visible markers, then, run the map.fitBounds on them.

Working Code:

function xmlParse(str) {
  if (typeof ActiveXObject != 'undefined' && typeof GetObject != 'undefined') {
    var doc = new ActiveXObject('Microsoft.XMLDOM');
    doc.loadXML(str);
    return doc;
  }

  if (typeof DOMParser != 'undefined') {
    return (new DOMParser()).parseFromString(str, 'text/xml');
  }

  return createElement('div', null);
}
var infoWindow = new google.maps.InfoWindow();
var customIcons = {
  monumento: {
    icon: 'http://maps.google.com/mapfiles/ms/icons/blue.png'
  },
  hotel: {
    icon: 'http://maps.google.com/mapfiles/ms/icons/green.png'
  },
  restaurantes: {
    icon: 'http://maps.google.com/mapfiles/ms/icons/yellow.png'
  },
  museus: {
    icon: 'http://maps.google.com/mapfiles/ms/icons/purple.png'
  }
};

var markerGroups = {
  "museus": [],
  "monumentos": [],
  "restaurantes": [],
  "hotel": []
};

var ctr = new google.maps.LatLng(38.639104, -8.210413),
    zoom = 12;

var map;
function load() {
  map = new google.maps.Map(document.getElementById("map"), {
    center: ctr,
    zoom: zoom,
    mapTypeId: 'roadmap'
  });

  var infoWindow = new google.maps.InfoWindow();

  map.set('styles', [{
    zoomControl: false
  }, {
    featureType: "road.highway",
    elementType: "geometry.fill",
    stylers: [{
      color: "#ffd986"
    }]
  }, {
    featureType: "road.arterial",
    elementType: "geometry.fill",
    stylers: [{
      color: "#9e574f"
    }]
  }, {
    featureType: "road.local",
    elementType: "geometry.fill",
    stylers: [{
      color: "#d0cbc0"
    }, {
      weight: 1.1
    }]
  }, {
    featureType: 'road',
    elementType: 'labels',
    stylers: [{
      saturation: -100
    }]
  }, {
    featureType: 'landscape',
    elementType: 'geometry',
    stylers: [{
      hue: '#ffff00'
    }, {
      gamma: 1.4
    }, {
      saturation: 82
    }, {
      lightness: 96
    }]
  }, {
    featureType: 'poi.school',
    elementType: 'geometry',
    stylers: [{
      hue: '#fff700'
    }, {
      lightness: -15
    }, {
      saturation: 99
    }]
  }]);

  //         downloadUrl("markers.xml", function (data) {
  var xml = xmlParse('<markers><marker name="Castelo" address="Rua da Condessa de Valença" lat="38.64351973190569" lng="-8.216521812152905" type="monumento" /><marker name="Anta 1 de Tourais" address="Estrada Nacional 114" lat="38.64260059929888" lng="-8.159376865959189" type="monumento" /><marker name="Hotel da Ameira" address="Herdade da Ameira" lat="38.64109640475479" lng="-8.180432206726096" type="hotel" /><marker name="Hotel Montemor" address="Avenida Gago Coutinho 8, 7050-248 Montemor-o-Novo" lat="38.64925541964039" lng="-8.216489625644726" type="hotel" /><marker name="Restaurante Monte Alentejano" address="Av. Gago Coutinho 8" lat="38.6492329" lng="-8.216665" type="restaurantes" /><marker name="Restaurante A Ribeira" address="Rua de São Domingos" lat="38.6347498199708" lng="-8.206468892765088" type="restaurantes" /><marker name="Núcleo Museológico do Convento de S. Domingos" address="" lat="38.643139" lng="-8.212732" type="museus" /><marker name="Centro Interpretativo do Castelo de Montemor-o-Novo" address="Rua Condessa de Valença" lat="38.64258748216167" lng="-8.21467108793263" type="museus" /></markers>');
  // var xml = data.responseXML;
  var markers = xml.documentElement.getElementsByTagName("marker");
  for (var i = 0; i < markers.length; i++) {
    var name = markers[i].getAttribute("name");
    var address = markers[i].getAttribute("address");
    var type = markers[i].getAttribute("type");

    var point = new google.maps.LatLng(
      parseFloat(markers[i].getAttribute("lat")),
      parseFloat(markers[i].getAttribute("lng")));
    var html = "<b>" + name + "</b> <br/>" + address;
    // var icon = customIcons[type] || {};
    var marker = createMarker(point, name, address, type, map);
    bindInfoWindow(marker, map, infoWindow, html);
  }
  // });
  //fitMap();
}

function createMarker(point, name, address, type, map) {
  var icon = customIcons[type] || {};
  var marker = new google.maps.Marker({
    map: map,
    position: point,
    icon: icon.icon,
    // shadow: icon.shadow,
    type: type,
    visible:false
  });
  if (!markerGroups[type]) markerGroups[type] = [];
  markerGroups[type].push(marker);
  var html = "<b>" + name + "</b> <br/>" + address;
  bindInfoWindow(marker, map, infoWindow, html);
  return marker;
}

function toggleGroup(type) {
  for (var i = 0; i < markerGroups[type].length; i++) {
    var marker = markerGroups[type][i];
    if (!marker.getVisible()) {
      marker.setVisible(true);
    } else {
      marker.setVisible(false);
    }
  }

  if (document.querySelectorAll(':checked').length == 0) {
    map.setCenter(ctr);
    map.setZoom(zoom);
  }
  else {
    fitMap();
  }
}


function fitMap() {
  var visibleMarkers = [];
  for (var i in markerGroups) {
    for (var j = 0; j < markerGroups[i].length; j++) {
      var marker = markerGroups[i][j];
      if (marker.getVisible()) {
        visibleMarkers.push(marker);
      }
    }
  }

  var bounds = new google.maps.LatLngBounds();
  for (var i = 0; i < visibleMarkers.length; i++) {
    bounds.extend(visibleMarkers[i].getPosition());
  }

  map.fitBounds(bounds);  
}

function bindInfoWindow(marker, map, infoWindow, html) {
  google.maps.event.addListener(marker, 'click', function () {
    infoWindow.setContent(html);
    infoWindow.open(map, marker);
  });
}

function downloadUrl(url, callback) {
  var request = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();

  request.onreadystatechange = function () {
    if (request.readyState == 4) {
      request.onreadystatechange = doNothing;
      callback(request, request.status);
    }
  };

  request.open('GET', url, true);
  request.send(null);
}

function doNothing() {}
google.maps.event.addDomListener(window, 'load', load);
html, body, #map, #map_wrap {
  height: 100%;
  width:100%;
}
<script src="https://maps.googleapis.com/maps/api/js?v=3&ext=.js"></script>
<div class="map_wrap">
  <div class="siderbarmap">
    <ul>
      <input id="monumentoCheckbox" type="checkbox" onclick="toggleGroup('monumento')" />
      <input id="museusCheckbox" type="checkbox" onclick="toggleGroup('museus')"  />
      <input id="restaurantesCheckbox" type="checkbox" onclick="toggleGroup('restaurantes')" />
      <input id="hotelCheckbox" type="checkbox" onclick="toggleGroup('hotel')" />
    </ul>
  </div>
  <div id="map" style="width:100%;height:585px; z-index: 1;"></div>
</div>

Update:

  1. I changed the code a little so now the function who find the visible markers is independent so we can call it in the page load and after checkbox was changed (now in comment for the requirement 2. To use it, just remove the comment //).
  2. None of the markers are visible at the start, just after the user click on a checkbox
  3. If none of the checkboxes are :checked the map will reset to the start zoom and center.
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
  • My pleasure! Good luck! – Mosh Feu Jan 24 '16 at 11:36
  • hi Mosh Feu, another question: now it only fits the bounds to the visible markers after the first time a category is clicked, not already when the page is loaded. What would i need to change to have the map size adjusted already in the beginning? and maybe you could help me with another small addition: What would i have to add / change, if i want the markers to be hidden in the beginning and only be toggled visible on checking the checkbox? – gliitschi8 Jan 24 '16 at 12:21
  • I just was updated my answer. A point of thinking: The first request conflict with the second. Because if none of the markers will be visible, you can't center the `map` according them. – Mosh Feu Jan 24 '16 at 12:45
  • thank you, i see the problem now. Also if i uncheck all of them, the map centers on an ocean... do you think it is possible to set the starting zoom and center as latlang when all markers are not visible? – gliitschi8 Jan 24 '16 at 12:55