Last active
April 5, 2021 12:57
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
* 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> |
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
@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. 🙌