Skip to content

Instantly share code, notes, and snippets.

@bootsified
Last active April 5, 2021 12:57
Show Gist options
  • Save bootsified/7d3bbfe05e0b73d5c5d3 to your computer and use it in GitHub Desktop.
Save bootsified/7d3bbfe05e0b73d5c5d3 to your computer and use it in GitHub Desktop.
Google Map with multiple markers - Geocoded addresses, unique InfoWindows, and automatic zoom and centering to show all markers. Working demo here: https://codepen.io/bootsified/details/XWbgwNr
<!--
* GOOGLE MAP W/ MULTIPLE MARKERS AND OPTIONAL GEOCODING
* by Boots (www.boots.media)
* Working demo here: https://codepen.io/bootsified/details/XWbgwNr
*
* To use geocoding for locations, set `useGeocoding = true;` (limit 10 locations).
* To manually place markers by lat/lng, set `useGeocoding = false;` (no limit). Locations array must contain lat/lng data.
-->
<script src="https://maps.googleapis.com/maps/api/js?key=[YOUR_APP_KEY_GOES_HERE]"></script>
<script>
// Set global variables
var map;
var bounds;
var locationCount;
var mapOptions;
var infoWindow = null;
var defaultZoom = 14; // The minimum zoom level
var mapElementId = "myGoogleMap";
var mapElement = document.getElementById(mapElementId);
var useGeocoding = true; // true: geocode marker from street address, false: set markers using lat/lng
// Set marker attributes. If you need unique values for each marker,
// you can update the values directly in the locations array.
var markerWidth = 32;
var markerHeight = 32;
var markerScale = 1; // Scale the image, if you can't control the source file (0 - 1).
// The array of locations to mark on the map.
// Google limits geocoded locations to 10 per pageload.
// No limit for markers set with lat/lng.
var locations = [
[
"Empire State Building", // name
"20 W 34th St", // street
"", // street 2 (ex. Suite 1234)
"New York", // city
"NY", // state
"10001", // zip
{
// Marker icon config object
url: "https://maps.google.com/mapfiles/ms/micons/blue.png",
size: new google.maps.Size(markerWidth, markerHeight),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(markerWidth * (markerScale / 2), markerHeight * markerScale),
scaledSize: new google.maps.Size(markerWidth * markerScale, markerHeight * markerScale)
},
new google.maps.Size(markerWidth * (markerScale / 4) * -1, markerHeight * markerScale), // marker offset
new google.maps.LatLng(40.7484995, -73.9882267)
],
[
"Independence Hall", // name
"520 Chestnut St", // street
"", // street 2 (ex. Suite 1234)
"Philadelphia", // city
"PA", // state
"19106", // zip
{
// Marker icon config object
url: "https://maps.google.com/mapfiles/ms/micons/red.png",
size: new google.maps.Size(markerWidth, markerHeight),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(markerWidth * (markerScale / 2), markerHeight * markerScale),
scaledSize: new google.maps.Size(markerWidth * markerScale, markerHeight * markerScale)
},
new google.maps.Size(markerWidth * (markerScale / 4) * -1, markerHeight * markerScale), // marker offset
new google.maps.LatLng(39.949140, -75.149730)
]
];
// Init map on Google 'load' event
google.maps.event.addDomListener(window, "load", init);
// Init the map
function init() {
// Customize look of the map.
// https://www.mapbuildr.com/
mapOptions = {
zoom: defaultZoom,
zoomControl: true,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL
},
disableDoubleClickZoom: false,
mapTypeControl: false,
panControl: false,
scaleControl: false,
scrollwheel: false,
streetViewControl: false,
draggable: true,
overviewMapControl: false,
mapTypeId: google.maps.MapTypeId.ROADMAP,
styles: [
{
featureType: "all",
stylers: [{ saturation: -100 }, { gamma: 0.8 }]
},
{
featureType: "poi",
stylers: [{ visibility: "off" }]
},
{
featureType: "transit",
stylers: [{ visibility: "off" }]
}
]
};
// Create new map object
map = new google.maps.Map(mapElement, mapOptions);
// OPTIONAL: Set listener to tell when map is idle
// Can be useful during dev
// google.maps.event.addListener(map, "idle", function() {
// console.log("map is idle");
// });
if (useGeocoding) {
var geocoder = new google.maps.Geocoder();
}
bounds = new google.maps.LatLngBounds();
locationCount = 0;
// Init InfoWindow and leave it
// for use when user clicks marker
infoWindow = new google.maps.InfoWindow({ content: "Loading content..." });
// Loop through locations and set markers
for (i = 0; i < locations.length; i++) {
if (useGeocoding) {
// street+city,state+zip
var address = locations[i][1] + "+" + locations[i][2] + "," + locations[i][3] + "+" + locations[i][4];
//Get latitude and longitude from address
geocoder.geocode({ address: address }, onGeocodeComplete(i));
} else {
placeLatLngMarker(i);
}
}
// Re-center map on window resize
google.maps.event.addDomListener(window, "resize", function() {
var center = map.getCenter();
google.maps.event.trigger(map, "resize");
map.setCenter(center);
});
} // END init()
// Triggered as the geocode callback
function onGeocodeComplete(i) {
// Callback function for geocode on response from Google.
// We wrap it in 'onGeocodeComplete' so we can send the
// location index through to the marker to establish
// content.
var geocodeCallBack = function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
// Create the marker for the location
// We use 'html' key to attach the
// InfoWindow content to the marker.
var marker = new google.maps.Marker({
icon: locations[i][6],
position: results[0].geometry.location,
map: map,
window_offset: locations[i][7],
html: getInfoWindowContent(i)
});
// Set event to display the InfoWindow anchored
// to the marker when the marker is clicked.
google.maps.event.addListener(marker, "click", function() {
showInfoWindow(this);
});
// Add this marker to the map bounds
extendBounds(results[0].geometry.location);
} else {
console.log("Location geocoding has failed: " + google.maps.GeocoderStatus);
// Hide empty map element on error
mapElement.style.display = "none";
}
}; // END geocodeCallBack()
return geocodeCallBack;
} // END onGeocodeComplete()
function placeLatLngMarker(i) {
// Create the marker for the location
// We use 'html' key to attach the
// InfoWindow content to the marker.
var marker = new google.maps.Marker({
icon: locations[i][6],
position: locations[i][8],
map: map,
window_offset: locations[i][7],
html: getInfoWindowContent(i)
});
// Set event to display the InfoWindow anchored
// to the marker when the marker is clicked.
google.maps.event.addListener(marker, "click", function() {
showInfoWindow(this);
});
// Add this marker to the map bounds
extendBounds(locations[i][8]);
}
// The HTML content for the InfoWindow.
// Includes a form to allow the user to
// get directions.
function getInfoWindowContent(i) {
var windowContent = '<form id="form-directions" action="http://maps.google.com/maps" method="get" target="_blank">\
<p><strong>' + locations[i][0] + '</strong><br>\
' + locations[i][1] + ', ' + locations[i][2] + '<br>\
' + locations[i][3] + ', ' + locations[i][4] + ' ' + locations[i][5] + '</p>\
<input type="hidden" name="daddr" value="' + locations[i][1] + ', ' + locations[i][3] + ', ' + locations[i][4] + ' ' + locations[i][5] + '" />\
<label for="saddr" class="alt-italic">Need directions?</label>\
<div class="input-group input-group--inline input-group--sm">\
<input name="saddr" type="text" class="input-group__input" placeholder="Your Address...">\
<button class="input-group__btn button" type="submit">Go!</button>\
</div><!-- /input-group -->\
</form>';
return windowContent;
}
function showInfoWindow(marker) {
// Updates the InfoWindow content with
// the HTML held in the marker ('this').
infoWindow.setOptions({
content: marker.html,
pixelOffset: marker.window_offset
});
infoWindow.open(map, marker);
}
// Establishes the bounds for all the markers
// then centers and zooms the map to show all.
function extendBounds(latlng) {
++locationCount;
bounds.extend(latlng);
if (locationCount == locations.length) {
map.fitBounds(bounds);
var currentZoom = map.getZoom();
if (currentZoom > mapOptions.zoom) {
map.setZoom(mapOptions.zoom);
}
}
} // END extendBounds()
</script>
<style>
.google-map {
background-color: #e0e0e0;
height: 40rem;
max-height: 100vh;
}
</style>
<div id="myGoogleMap" class="google-map"></div>
@EnjoyMyHate
Copy link

This was very useful and helped me solve dealing with geocoding's asynchronous ways.

@gitChriss
Copy link

Sadly this does not work anymore.

"cation geocoding has failed: ' + google.maps.GeocoderStatus)', 'window.log' is undefined)"
After i changed window.log to console.log it outputs just a "Location geocoding has failed: [object Object]".

:-(

@all4pages
Copy link

all4pages commented Feb 28, 2018

For me it´s working perfect. Don't forget to set the url to your marker PNG's.
You don't need "sensor=false" anymore.

@pwil30
Copy link

pwil30 commented Jun 12, 2019

This worked perfectly. Saved me from quite a headache I was encountering with the Geocoder, particularly with the info windows. Thanks for sharing this.

@bootsified
Copy link
Author

bootsified commented Jun 12, 2019

This worked perfectly. Saved me from quite a headache I was encountering with the Geocoder, particularly with the info windows. Thanks for sharing this.

@pwil30 Awesome! Glad I could help. This one took a while to put together, but it's been so handy. 🙌

@bootsified
Copy link
Author

I realized I'd tweaked this out some over the years in my projects, so I updated it here. Adds the ability to scale the marker icon, if needed (if you can't control the source image). Also sets a minimum zoom level, so the map won't zoom in too far if there is only one marker.

@harrync
Copy link

harrync commented Feb 20, 2020

Fantastic, thanks so much

@untiedshoes
Copy link

Love this, and has so far proved extremely helpful, so thank you very much for that :)

One thing that would be very helpful however, would be the addition of using lat/long, rather than the address. One issue with using addresses for markers is that we seem to be limited to a max number of 10. Using lat / long be all accounts does not have limits.

I'm currently trying to work my way through this adding in the addition of lat / long for the markers.

Craig

@bootsified
Copy link
Author

@untiedshoes It's actually much simpler to set markers by lat/lng. I've updated the gist with that option, as well as cleaning things up a bit more. You can also now see a working demo here: https://codepen.io/bootsified/details/XWbgwNr.

I never thought about adding that option. Makes total sense. Thanks for the suggestion. 🙌

@untiedshoes
Copy link

Awesome man!!
I've literally (in just the last 10 mins), also done my own solution.

Be interesting to see how you've done it too :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment