How to Populate OpenStreetMap with Foursquare Venues/Amenities

First of all, we need a Foursquare app and a client ID and secret. Head over to developer.foursquare.com and get your free account.

Follow the steps to verify your account and create an app.

Create the HTML structure which will feed the map and the markers. Don’t forget to feed in your Foursquare details, your coordinates (latitude and longitude) and property title (note the variables between {curly brackets}).

<div id="amenities" data-fscid="{foursquare_client_id}" data-fscs="{foursquare_client_secret}" data-latitude="53.3032329" data-longitude="-6.1792373" data-property-title="{title}"></div>
<div id="osm-map-amenities"></div>

Next, here’s the JavaScript function that does the heavy lifting:

/**
 * Generate amenity map based on Foursquare
 *
 * Generates an amenity map based on Foursquare and populates it with closest venues
 *
 * @docs https://developer.foursquare.com/docs/api/venues/search
 * @docs https://foursquare.com/developers/apps/5M2CNASPDYYJ304R1BCDPLAP4ENIJCMRYF5CIMCGC1VNY2L3/settings
 * @docs https://foursquare.com/developers/apps/5M2CNASPDYYJ304R1BCDPLAP4ENIJCMRYF5CIMCGC1VNY2L3/places/usage
 *
 * @type function
 */
function get_amenity_map() {
    var amenitiesElement = document.getElementById('amenities');

    var fsClientId = amenitiesElement.dataset.fscid,
        fsClientSecret = amenitiesElement.dataset.fscs,
        latitude = amenitiesElement.dataset.latitude,
        longitude = amenitiesElement.dataset.longitude,
        currentPropertyTitle = amenitiesElement.dataset.propertyTitle;

    var data = null,
        xhr = new XMLHttpRequest();

    xhr.withCredentials = false;

    xhr.addEventListener("readystatechange", function () {
        if (this.readyState === 4) {
            var latLngArray = [],
                venues = JSON.parse(this.response),
                venueCategory;

            venues.response.venues.forEach(function (venue) {
                venueCategory = '';

                if (venue.categories[0]) {
                    venueCategory = venue.categories[0].name;
                }
                if (!isNaN(venue.location.lat) && !isNaN(venue.location.lng)) {
                    latLngArray.push([parseFloat(venue.location.lat), parseFloat(venue.location.lng), '<h3>' + venue.name + '</h3>' + venueCategory + '<br><small>' + venue.location.address + '</small>']);
                }
            });

            var osmMapAmenities = L.map("osm-map-amenities").setView([0, 0], 16);

            L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
                attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            }).addTo(osmMapAmenities);
            L.control.scale().addTo(osmMapAmenities);

            var markers = L.markerClusterGroup({
                maxClusterRadius: 0,
                spiderfyOnMaxZoom: false,
                showCoverageOnHover: false,
                zoomToBoundsOnClick: false
            });

            // Add current property marker
            var propertyIcon = L.icon({
                iconUrl: "map-marker-red.png"
            });

            var title = currentPropertyTitle;
            var marker = L.marker(new L.LatLng(latitude, longitude), {
                title: title,
                icon: propertyIcon
            });
            marker.bindPopup(title);
            markers.addLayer(marker);

            var myIcon;

            for (var i = 0; i < latLngArray.length; i++) {
                var a = latLngArray[i];
                var title = a[2];

                myIcon = L.icon({
                    iconUrl: "map-marker-green.png"
                });

                var marker = L.marker(new L.LatLng(a[0], a[1]), {
                    title: title,
                    icon: myIcon
                });
                marker.bindPopup(title);
                markers.addLayer(marker);
            }

            osmMapAmenities.addLayer(markers);
            osmMapAmenities.setView(new L.LatLng(latitude, longitude), 17);
        }
    });

    xhr.open("GET", "https://api.foursquare.com/v2/venues/search?ll=" + latitude + "," + longitude + "&client_id=" + fsClientId + "&client_secret=" + fsClientSecret + "&v=20190801&radius=500&limit=20&intent=browse");
    xhr.send(data);
}

document.addEventListener('DOMContentLoaded', function () {
    if (document.getElementById('amenities') && document.getElementById('amenities').dataset.fscid !== '' && document.getElementById('amenities').dataset.fscs !== '') {
        get_amenity_map();
    }
});

And finally, here’s your dependencies. Note that I have included the CSS inline for better performance, as I save two requests for relatively small code.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css">

<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script>

<script src="https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/Leaflet.fullscreen.min.js"></script>

<style>
/**
 * Leaflet.js Fullscreen Module
 *
 * @source  https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/leaflet.fullscreen.css
 * @version 1.0.1
 */
.leaflet-control-fullscreen a {
    background:#fff url(../images/fullscreen.png) no-repeat 0 0;
    background-size: 26px 52px;
}
.leaflet-touch .leaflet-control-fullscreen a {
    background-position: 2px 2px;
}
.leaflet-fullscreen-on .leaflet-control-fullscreen a {
    background-position:0 -26px;
}
.leaflet-touch.leaflet-fullscreen-on .leaflet-control-fullscreen a {
    background-position: 2px -24px;
}

/* Do not combine these two rules; IE will break. */
.leaflet-container:-webkit-full-screen {
    width: 100% !important;
    height: 100% !important;
}
.leaflet-container.leaflet-fullscreen-on {
    width: 100% !important;
    height: 100% !important;
}

.leaflet-pseudo-fullscreen {
    position: fixed !important;
    width: 100% !important;
    height: 100% !important;
    top: 0 !important;
    left: 0 !important;
    z-index: 99999;
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
    .leaflet-control-fullscreen a {
        background-image:url(../images/fullscreen@2x.png);
    }
}



/**
 * Leaflet.js Marker Cluster Module
 *
 * @source  https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/MarkerCluster.css
 * @version 1.4.1
 */
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
    -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
    -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
    -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
    transition: transform 0.3s ease-out, opacity 0.3s ease-in;
}
.leaflet-cluster-spider-leg {
    /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
    -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
    -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
    -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
    transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
}

.leaflet-popup-content {
    font-family: var(--body_font);
}



/**
 * Leaflet.js Marker Cluster Default Theme
 *
 * @source  https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css
 * @version 1.4.1
 */
.marker-cluster-small {
    background-color: rgba(181, 226, 140, 0.6);
}
.marker-cluster-small div {
    background-color: rgba(110, 204, 57, 0.6);
}
.marker-cluster-medium {
    background-color: rgba(241, 211, 87, 0.6);
}
.marker-cluster-medium div {
    background-color: rgba(240, 194, 12, 0.6);
}
.marker-cluster-large {
    background-color: rgba(253, 156, 115, 0.6);
}
.marker-cluster-large div {
    background-color: rgba(241, 128, 23, 0.6);
}

.marker-cluster {
    background-clip: padding-box;
    border-radius: 20px;
}
.marker-cluster div {
    width: 30px;
    height: 30px;
    margin-left: 5px;
    margin-top: 5px;

    text-align: center;
    border-radius: 15px;
    font: 12px -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
}
.marker-cluster span {
    line-height: 30px;
}
</style>

Let me know how it goes for you!

Find more JavaScript tutorials, code snippets and samples here or more jQuery tutorials, code snippets and samples here.

Added by Ciprian on Wednesday, August 28, 2019 in JavaScript

Unlimited Automated Page Speed Monitoring & Tracking. Completely free.
Use SpeedFactor to track your website. It’s simple, reliable, and best of all, it’s free forever.
See how real people experience the speed of your website. Then find (and fix) your web performance problems.
Get Started

Related Articles


Privacy Policy