Hello Everyone!
It took me a while to get this project working, but once I did, I wanted to share it with the community, and I hope someone might find this useful.
The Goal: I am making a personal portfolio website and I have a CMS collection with over 300 images and each of them has a set of coordinates of where the photo was taken. I wanted to create a Map page where I have all the markers for every photo, and that each marker on click displays a thumbnail and some information about that particular photo. On click it take you to the individual CMS page for that image.
The Problem â the initial google maps setup was not too bad, it took some doing but was possible, but I had two issues:
- I had a number of images that had exact same coordinates and since the markers were overlaying each other I would only get the top one visible and clickable.
- The CMS collection is limited only to 100 items
NOW I present the solution to the problem that involves Google Maps JS API, and Overlapping Marker Spiderfier script (for overlapping markers â MAD PROPS TO jawj), and multiple collections on the same page so that all 300 markers are visible.
(This presumes that you have setup you Google Cloud Console and have the Google Maps API Key ready)
Step 1. SETUP THE MAP CANVAS
Create a page where you intend to use the google maps. I used a div with âfixed fullâ positioning, 100VH height and 5vh/5vw padding, I named it âgoogle-maps-wrapperâ.
Create a child div âgoogle-maps-canvasâ with static positioning, and 100% width and height.
Click on the gear icon (next the brush icon) and in the Div Block Settings give the âgoogle-maps-wrapperâ ID as âmap_wrapperâ and to âgoogle-maps-canvasâ give an ID âmap_elementâ (of course without the quotes)
Step 2. SETUP THE COLLECTIONS
Create your CMS collection(s), and make sure they are all named the same. (mine were âdynamicGallerySectionâ. Inside the âgalleryItemâ div, place an âHTML Embedâ.
The HTML Embed should have all the information that you may want to use on the map.
Use the following script:
<div class="imgMap"
<script>
imgs.push ({
'name' : â[title]â,
'slug' : â[slug]â,
'url' : 'https://[YOUR PATH TO PUBLISHED INDIVIDUAL CMS PAGE]/â[slug]â,
'description' : â[description]â,
'photo' : â[photo]â,
'lat' : â[lat]â,
'lng' : â[lng]â,
});
</script>
Replace the items [XXX] with the dynamic fields as needed (click the purple â+Add Fieldâ button in the HTML Embed Code Editor) and donât forget your path to the CMS collection individual item page - it is formed with the last part being the âslugâ of your CMS item - this is important so that the InfoWindow popups link to the CMS items.
If you have multiple CMS Collections on the same page (if you need to have more than 100 markers on the map) then make sure that each CMS collection is named the same and has an identical HTML embed in the âgalleryItemâ div.
Set the collection âlayoutâ as hidden.
Step 3. SETUP THE CUSTOM CODE (THE BIG STEP )
Open the custom code section of your page. In the âinside tagâ add the following code:
<script>
var imgs = [];
</script>
<style>
#map_element .imgMap {
min-height: 0;
height: 100px;
margin-bottom: 5px;
}
</style>
In the âBefore tagâ add the following code:
<script async defer src="https://maps.google.com/maps/api/js?v=3&callback=mapLibReadyHandler&key=[YOUR API KEY] "></script>
<script async defer src="https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier/1.0.3/oms.min.js?spiderfier_callback=mapLibReadyHandler"></script>
<div id="map_element"></div>
<script>
var isIE = false;
var mapLibsReady = 0;
function mapLibReadyHandler() {
if (++mapLibsReady < 2) return;
var mapElement = document.getElementById('map_element');
var map = new google.maps.Map(mapElement, {
center: new google.maps.LatLng(0, 0),
zoom: 4,
minZoom: 3,
mapTypeId: 'terrain',
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
mapTypeIds: ['terrain', 'satellite'],
},
streetViewControl: false,
});
var iw = new google.maps.InfoWindow();
function iwClose() {
iw.close();
}
google.maps.event.addListener(map, 'click', iwClose);
var oms = new OverlappingMarkerSpiderfier(map, {
markersWontMove: true,
markersWontHide: true
});
oms.addListener('format', function(marker, status) {
var iconURL = status == OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED ? 'https://uploads-ssl.webflow.com/5d38e6641d65b655aa18d423/5d629dcd45d893fadf546bd7_marker-highlight%20N.svg' :
status == OverlappingMarkerSpiderfier.markerStatus.SPIDERFIABLE ? 'https://uploads-ssl.webflow.com/5d38e6641d65b655aa18d423/5d629dcda93ad9effccff365_marker-plus%20N.svg' :
status == OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIABLE ? 'https://uploads-ssl.webflow.com/5d38e6641d65b655aa18d423/5d629dcd45d893fadf546bd7_marker-highlight%20N.svg' : null;
var iconSize = new google.maps.Size(23, 32);
marker.setIcon({
url: iconURL,
size: iconSize,
scaledSize: iconSize // makes SVG icons work in IE
});
});
for (var i = 0, len = window.mapData.length; i < len; i++) {
(function() { // make a closure over the marker and marker data
var markerData = window.mapData[i]; // e.g. { lat: 50.123, lng: 0.123, text: 'XYZ' }
var marker = new google.maps.Marker({
position: markerData,
optimized: !isIE // makes SVG icons work in IE
});
google.maps.event.addListener(marker, 'click', iwClose);
oms.addMarker(marker, function(e) {
iw.setContent(markerData.text);
iw.open(map, marker);
});
})();
}
window.map = map;
window.oms = oms;
}
var data = [];
for (i = 0; i < imgs.length; i++) {
var imgMap = imgs[i];
data.push({
lng: parseFloat(imgMap.lng),
lat: parseFloat(imgMap.lat),
text: '<a href="' + imgMap.url + '"><div class="imgMap" style="background:url(' + imgMap.photo + ') center/contain no-repeat"></div></a>' + '<h3>' + imgMap.name + '</h3> <i><small>' + imgMap.classification + '</small></i><br/><b>' + imgMap.description + ' </b>'
});
}
window.mapData = data;
</script>
Please pay attention to the following sections of the script:
-
<div id="map_element"></div>
This connects to the map_element div ID setup in Step 1. -
var mapElement = document.getElementById('map_element');
This part connects the google maps API to the div ID you made in Step 1 -
var map = new google.maps.Map(mapElement, { center: new google.maps.LatLng(0, 0), zoom: 4, minZoom: 3, mapTypeId: 'terrain', mapTypeControl: true, mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.DROPDOWN_MENU, mapTypeIds: ['terrain', 'satellite'], }, streetViewControl: false, });
This part styles the map how you like it â e.g. if you want to have street view button visible, or at what zoom level it starts and the max/min zoom you can use, type of map, etc⌠for more details read this article
-
var iconURL = status == OverlappingMarkerSpiderfier.markerStatus.SPIDERFIED ? 'https://uploads-ssl.webflow.com/5d38e6641d65b655aa18d423/5d629dcd45d893fadf546bd7_marker-highlight%20N.svg' : status == OverlappingMarkerSpiderfier.markerStatus.SPIDERFIABLE ? 'https://uploads-ssl.webflow.com/5d38e6641d65b655aa18d423/5d629dcda93ad9effccff365_marker-plus%20N.svg' : status == OverlappingMarkerSpiderfier.markerStatus.UNSPIDERFIABLE ? 'https://uploads-ssl.webflow.com/5d38e6641d65b655aa18d423/5d629dcd45d893fadf546bd7_marker-highlight%20N.svg'
those are links to marker icons â I have uploaded SVGâ of markers to my webflow site and got the links to them and stuck them in the script.
-
lng: parseFloat(imgMap.lng), lat: parseFloat(imgMap.lat),
I have stored the coordinates as a text field in my CMS collection so in that case I had to turn them into numbers, hence the parseFloat command â if yours are in number format you do not need to use that.
-
text: '<a href="' + imgMap.url + '"><div class="imgMap" style="background:url(' + imgMap.photo + ') center/contain no-repeat"></div></a>' + '<h3>' + imgMap.name + '</h3> <i><small>' + imgMap.classification + '</small></i><br/><b>' + imgMap.description + ' </b>' });
This is HTML styling for the info window that will show up when a marker is clicked on the map â style as you need â it references the dynamic information that you set up in the HTML Embed in Step 2
Thatâs it folks, publish and enjoy your google map with CMS custom markers!
Here is the link to my map