Tutorials and hands-on lessons for learning

Selecting within a distance (one to many)


Another common approach to proximity analysis is to determine which points fall within a specified distance or radius. This type of analysis, also known as a buffer analysis, does not take into account travel time across a network. Buffer analysis may be used to determine how many healthy food options are within a 5-mile radius, for visualizing how many migrant services are within 0.5, 1, 3, and 5-mile distances from a reference location, or for determining how many potential customers live within 10-miles of a store. Buffer analysis is an example of a one-to-many relationship.


There are multiple ways to do a 'buffer' analysis including:

Your Turn

In this exercise, we will be using Mapbox and turf.js to locate ‘confirmed’ sightings of alien activity in Area 51. To do this we will:

  1. Add data on potential alien sightings to a map.
  2. Create a search radius (buffer) around any clicked events
  3. Join potential alien sightings data to buffered polygon and return ‘confirmed’ alien sightings within the selected search radius.

Tools we'll use:

  • Text Editor (Atom, VSCode, JSFiddle) to write and edit your code.‍
  • Mapbox account
  • Turf.js. Turf is the JavaScript library you'll be using today to add analysis to your map.

Step 1. Initialize you map

Open a text editor and create a file called index.html. Set up the document by copying and pasting this template code below into your new HTML file.

The variable aliens stores a feature collection with information about the location of each alien sighting in Roswell, NM. The data also contains a field ‘isAlien’, which records whether the sighting was “confirmed” .

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>Alien Map</title>
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <script src='https://api.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.js'></script>
  <link href='https://api.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.css' rel='stylesheet'/>
  <script src="https://cdn.jsdelivr.net/npm/@turf/turf@5/turf.min.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }
    #instructions {
      position: absolute;
      top: 20px;
      left: 20px;
      background-color: #fff;
      padding: 5px 20px 5px 20px;
      border-radius: 10px;
    }

  </style>
</head>

<body>
  <div id="map"></div>
  <div id="instructions">
    <h2>👽 Instructions</h2>
    <p>Click anywhere to check for confirmed alien landing sites.</p>
  </div>
  <script>
       // Data showing potential alien landing sites
    var aliens = { "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": { "ID": 1, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.4580078125, 33.687781758439364] } }, { "type": "Feature", "properties": { "ID": 2, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.117431640625, 33.57000543108403] } }, { "type": "Feature", "properties": { "ID": 3, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.09957885742188, 33.91373381431625] } }, { "type": "Feature", "properties": { "ID": 4, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.26162719726562, 33.67292566628718] } }, { "type": "Feature", "properties": { "ID": 5, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.83016967773438, 33.58602331802259] } }, { "type": "Feature", "properties": { "ID": 6, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.86724853515625, 33.277731642555224] } }, { "type": "Feature", "properties": { "ID": 7, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.33990478515625, 33.53338195763831] } }, { "type": "Feature", "properties": { "ID": 8, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.23004150390625, 33.32593850874471] } }, { "type": "Feature", "properties": { "ID": 9, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.63516235351562, 33.578014746143985] } }, { "type": "Feature", "properties": { "ID": 0, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.7821044921875, 33.43717151666947] } }, { "type": "Feature", "properties": { "ID": 11, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.71755981445312, 33.20652045176062] } }, { "type": "Feature", "properties": { "ID": 12, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.22454833984374, 33.12950124445052] } }, { "type": "Feature", "properties": { "ID": 13, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.09683227539062, 33.47841764867342] } }, { "type": "Feature", "properties": { "ID": 14, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.315185546875, 33.897777013859475] } }, { "type": "Feature", "properties": { "ID": 15, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.82742309570312, 33.7825716472443] } }, { "type": "Feature", "properties": { "ID": 16, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-103.82354736328125, 33.710632271492095] } }, { "type": "Feature", "properties": { "ID": 17, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.58297729492188, 33.19388015067254] } }, { "type": "Feature", "properties": { "ID": 18, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.74639892578125, 33.073130945006625] } }, { "type": "Feature", "properties": { "ID": 19, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.2547607421875, 33.01326987686983] } }, { "type": "Feature", "properties": { "ID": 20, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.08172607421875, 33.22375428474926] } }, { "type": "Feature", "properties": { "ID": 21, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.98123168945311, 33.46696235807553] } }, { "type": "Feature", "properties": { "ID": 22, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.45526123046875, 33.90689555128866] } }, { "type": "Feature", "properties": { "ID": 23, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.28497314453125, 33.457797035354766] } }, { "type": "Feature", "properties": { "ID": 24, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.52255249023438, 33.384439582098224] } }, { "type": "Feature", "properties": { "ID": 25, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.61868286132812, 33.00060174664655] } }, { "type": "Feature", "properties": { "ID": 26, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-105.14465332031249, 32.778037985363675] } }, { "type": "Feature", "properties": { "ID": 27, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-103.81393432617188, 32.858825196463854] } }, { "type": "Feature", "properties": { "ID": 28, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-105.23941040039062, 34.028762179464465] } }, { "type": "Feature", "properties": { "ID": 29, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-103.68484497070312, 34.07199987534163] } }, { "type": "Feature", "properties": { "ID": 30, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.12155151367188, 33.884097379274905] } }, { "type": "Feature", "properties": { "ID": 31, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.08172607421875, 33.89207743274474] } }, { "type": "Feature", "properties": { "ID": 32, "isAlien": "yes" }, "geometry": { "type": "Point", "coordinates": [-104.13940429687499, 33.920571528675076] } }, { "type": "Feature", "properties": { "ID": 33, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.05975341796875, 33.93652406150093] } }, { "type": "Feature", "properties": { "ID": 34, "isAlien": "" }, "geometry": { "type": "Point", "coordinates": [-104.10232543945312, 33.959308210392024] } }] };

   
    mapboxgl.accessToken = 'Access Token Goes Here';
    var map = new mapboxgl.Map({
      container: 'map',
      style: 'mapbox://styles/mapbox/light-v10', // stylesheet location
      center: [-104.4395, 33.4085], // starting position near Roswell, NM
      zoom: 9 // starting zoom
    });

    map.on('load', function () {

      //Map layers go in here!
    });

    //Click event goes here!

    //makeRadius function goes here!

    //spatialJoin function goes here!

  </script>

</body>

</html>

Step 2. Add your Mapbox access token

Without an access token, the rest of the code will not work.‍ Login or create a free Mapbox account. Find your access token on your Access tokens page or the main page you sign into your Mapbox account.

Copy and paste your access token into the code, here:

  mapboxgl.accessToken = 'Access Token Goes Here';

Save your code and open your HTML document in a browser.

Step 3. Add layers to the map

We need to add three layers to the map - one to display all the alien sightings in Roswell, NM, one to store confirmed alien landing sites, and one for drawing the search radius. We will use map.addLayer() to add each layer to the map and to define how the data is styled. All the layers will be initialized inside of a load event to make sure that the map has finished loading before the layers are added.

First, add a layer that displays all the reported alien sightings in Roswell, NM. Remember this goes inside of the 'load' event.

      //Add layer to show all alien sighting locations
      map.addLayer({
        id: 'aliens',
        source: {
          type: 'geojson',
          data: aliens
        },
        type: 'circle',
        paint: {
          'circle-color': '#5555f6',
          'circle-radius': 8,
          'circle-opacity': .5
        }
      });

Next, add a layer to store the information on confirmed sightings. This layer’s source data is set to an empty feature collection. When a user clicks on a region, any confirmed alien sightings will be added to this layer. For this layer, set the layer 'type' to 'symbol' and use the Mapbox Maki 'rocket-11' to display any confirmed sightings. Remember this goes inside of the 'load' event.

    // When the map has finished loading, add a new layer that will be empty 
    // at first, but will eventually house our confirmed alien landing sites.
      map.addLayer({
        id: 'alien-truth',
        source: {
          type: 'geojson',
          data: { "type": "FeatureCollection", "features": [] }
        },
        type: 'symbol',
        layout: {
           'icon-image': 'rocket-11',
           'icon-size': 1,
           'icon-allow-overlap': true
           }
      });

Next, add a layer to draw a search radius on the map (still within the load event). This layer’s source data is also set to an empty feature collection but will soon be filled with information about our search radius. Remember this goes inside of the 'load' event.

      // Draw the alien search radius on the map
      map.addLayer({
        id: 'search-radius',
        source: {
          type: 'geojson',
          data: { "type": "FeatureCollection", "features": [] }
        },
        type: 'fill',
        paint: {
          'fill-color': '#F1CF65',
          'fill-opacity': 0.1
        }
      });

Step 4. Add a click event

We need to create search radius around any point on the map that the user clicks. To do this, we need the coordinate information for each click event. Create a click event and store the information about the clicked coordinates in a variable called eventLngLat. Console.log(eventLngLat) to view the clicked coordinates in your browser. This event should go outside of the load event.

    map.on('click', function(e) {
      var eventLngLat = [e.lngLat.lng, e.lngLat.lat];
      console.log(eventLngLat)
    });

Delete or comment out console.log(eventLngLat) when finished.

Step 5. Create a buffer with turf.js

In this next step, we will use the turf.js library to transform our event coordinates into point features and to create a buffer polygon around each point.

Initialize a function called makeRadius() and give it two parameters: lngLatArray and radiusInMeters

function makeRadius(lngLatArray, radiusInMeters){}

Before we create a buffer, we need to turn our clicked location coordinates into a point feature. Inside of the function add a new variable called point and set it to turf.point(lngLatArray).

  var point = turf.point(lngLatArray)

Now that our coordinates are stored as a point feature, we can use turf's buffer function to make a polygon around the clicked point. Add the new buffered variable below the point variable and return it.

  var buffered = turf.buffer(point, radiusInMeters, { units: 'meters' });
  return buffered;

The makeRadius() function should look like:

  function makeRadius(lngLatArray, radiusInMeters) {
      var point = turf.point(lngLatArray);
      var buffered = turf.buffer(point, radiusInMeters, { units: 'meters' });
      return buffered;
    }

Step 6. Call the makeRadius() function

Use the makeRadius() function to generate the search radius as a GeoJSON polygon by calling it within your 'click' event. Remember that the function takes two arguements: a longitude and latitude array (eventLngLat) and a search radius. For this tutorial let's use a radius of 15000 meters.

  var searchRadius = makeRadius(eventLngLat, 15000);

Step 7. Set the search-radius source layer

Set the search-radius source layer to our newly made radius feature. Below var = searchRadius, add the following:

  map.getSource('search-radius').setData(searchRadius);

Save your code and open the HTML document in a browser. Click anywhere on the map to see the new search radius.

Step 8. Identify confirmed alien sightings within the buffered radius

In this step we are going to create a function that loops through all the features in our original GeoJSON data. Next, the function we will use turf.booleanPointInPolygon to filter features that are both inside the buffered region and are confirmed landing sites.

    function spatialJoin(sourceGeoJSON, filterFeature) {
      // Loop through all the features in the source geojson and return the ones that 
      // are inside the filter feature (buffered radius) and are confirmed landing sites
      var joined = sourceGeoJSON.features.filter(function (feature) {
        return turf.booleanPointInPolygon(feature, filterFeature) && feature.properties.isAlien === 'yes';
      });
    
      return joined;
    } 
  

Step 8. Call the spatialJoin() function

Within the click function, create a new variable called 'featuresInBuffer' and assign it the value of spatialJoin(). Remember that the function takes two arguements: a GeoJSON and a filter feature. For this exercise, we are using the searchRadius as the filter feature.

  var featuresInBuffer = spatialJoin(aliens, searchRadius);

Step 9. Set the alien-truth source layer

Set the alien-truth source layer to our newly made featuresInBuffer variable. Below var = featuresInBuffer, add the following:

   map.getSource('alien-truth').setData(turf.featureCollection(featuresInBuffer));

Save your code and open the HTML document in a browser. Click anywhere on the map and see if you can spot any confirmed alien sightings in Roswell!

Additional Tutorials

Review questions

  1. Name two tools that can be used to do a 'buffer' analysis.
  2. In the exercises above, what does the makeRadius() function do? What does the spatialJoin() function do?
  3. In the exercises above, what does the spatialJoin() function do?
Was this page helpful?