This recipe will allow you to add a Google Maps element to a Dynamic Report block. This allows you to display the individuals returned by a Dynamic Report on a map.

Screenshot_2021-06-18_145405.png


To get started, first create a DataView and Report to power your Dynamic Report block. In the example above, we use a data view with just one Distance From filter:
DataView.png


The report you create must have the following 4 columns in the following order as the first 4 columns, and they must all have the option "Show In Grid" enabled. After adding these 4 columns in this order, you can add any other columns you would like to the report.

ReportColumns.png


The Latitude and Longitude columns are achieved by using an Address field type using the appropriate Address Part:

LatitudeField.png


Once you create the DataView and Report, you'll need to create a Dynamic Report block and configure it to use your new Report:

DynamicReportBlock.png


In order to display the Google Map, you'll need to edit the Post HTML under the Dynamic Report block's Advanced Settings and paste the following Lava code. This code takes the columns from your report and plots them on a map:



    <style>
    table[id$=gReport] tr td:nth-child(2), table[id$=gReport] tr th:nth-child(2)
    ,table[id$=gReport] tr td:nth-child(3), table[id$=gReport] tr th:nth-child(3) {
        display: none;
    }

    #map {
        width: 100%;
        height: 500px;
    }
</style>

<script>
    function initMap() {


        let people = document.querySelectorAll('table[id$=gReport] tbody tr');

        if (people.length > 0 && people[0].textContent.trim() != "No Results") {
            $('<div id="map"></div>').insertAfter('div[id$=pnlFilter]');

            // Init lat and longitude zoom options
            var latlngbounds = new google.maps.LatLngBounds();
            const infowindow = new google.maps.InfoWindow();

            const map = new google.maps.Map(document.getElementById("map"), {
                zoom: 4
            });


            function getPopupContent(row) {
                let wrapper = $('<div></div>')
                let link = $('<a></a>');
                link.on('click', function() { window.scrollTo({ top: $(row).offset().top-80, behavior: 'auto' }); });
                link.append('<i class="fa fa-angle-double-down fa-lg" style="margin-right: 10px;"></i>');
                wrapper.append(link);
                let checkbox;

                if (row.childNodes[1].querySelector('input').checked == true)
                {
                    checkbox = $('<i class="far fa-check-square fa-lg" style="margin-right: 10px;"></i>');
                } else {
                    checkbox = $('<i class="far fa-square fa-lg" style="margin-right: 10px;"></i>');
                }
                checkbox.on('click', function () {
                    row.childNodes[1].querySelector('input').click();
                    if (row.childNodes[1].querySelector('input').checked == true) {
                        $(this).attr('class', 'far fa-check-square fa-lg');
                    } else {
                        $(this).attr('class', 'far fa-square fa-lg');
                    }
                });
                wrapper.append(checkbox);

                wrapper.append(`<b style="font-size: 1.2em;">${row.childNodes[4].childNodes[1].outerHTML}</b>`);
                wrapper.append(`<div style="margin-top: 10px;">${row.childNodes[5].innerHTML}</div>`);

                return wrapper[0];
            }

            function markerClicked(marker) {
                infowindow.setContent(getPopupContent(marker.rowEl));
                infowindow.open(map, marker);
            }

            for (let person of people) {
                // Get lat and lon from grid.
                const lat = parseFloat(person.childNodes[2].textContent);
                const lng = parseFloat(person.childNodes[3].textContent);

                if (lat && lng) {
                    const latLng = { lat: lat, lng: lng };
                    let marker = new google.maps.Marker({
                        position: latLng,
                        map,
                        title: person.childNodes[4].textContent,
                        rowEl: person
                    });
                    marker.addListener("click", () => {
                        markerClicked(marker);
                    });
                    // Add info window
                    latlngbounds.extend(marker.position);

                    let markerEl = $('<img src="http://maps.google.com/mapfiles/ms/icons/red-dot.png" style="margin-right: 10px;">');
                    markerEl.on('click', function() {
                        window.scrollTo({ top: $('#map').offset().top-80, behavior: 'auto' });
                        map.setCenter(marker.position);
                        markerClicked(marker);
                    });
                    $(person.childNodes[4]).prepend(markerEl);
                }
            }

            //Center map and adjust Zoom based on the position of all markers.
            map.setCenter({lat: latlngbounds.getCenter().lat(), lng: latlngbounds.getCenter().lng()})

            google.maps.event.addListenerOnce(map, 'idle', function() {
                map.fitBounds(latlngbounds);
            });
        }

    }

    // Select the node that will be observed for mutations
    const targetNode = document.querySelector('form');

    // Options for the observer (which mutations to observe)
    const config = { attributes: false, childList: true, subtree: false };

    // Callback function to execute when mutations are observed
    let debounceTimer;
    const callback = function(mutationsList, observer) {
        if (debounceTimer) clearTimeout(debounceTimer);
        debounceTimer = setTimeout(initMap, 500);
    };

    // Create an observer instance linked to the callback function
    const observer = new MutationObserver(callback);

    // Start observing the target node for configured mutations
    observer.observe(targetNode, config);
</script>

That's it! After adding that code to PostHTML under your block settings, any results returned by your dynamic dataview will be plotted on a map.

You can click the map icon on any row to center and open that person's info window on the map. You can click the arrows on the map info window to return to that person's row on the grid, or check the box on the map info window to select that row in the grid below:

OpenMap.png