Overview

You will learn: how to build an app to buffer, intersect, and preform distance calculations with the geometry engine.

Applications can use the geometry engine and the geometry service to perform sophisticated spatial operations such as buffering, projecting, calculating length and area, and determining the spatial relationship between two or more geometries. The geometry engine can perform a wide range of geometry operations and calculations on the client-side and is generally used for fast calculations and drawing on the map. The geometry service can perform similar operations to the geometry engine, but all calculations happen on the server-side. The geometry service has the additional capability of projecting geometries and performing spatial operations on geometries that have different spatial references. To learn more about all of the different types of geometric operations possible, please visit the documentation.

In this tutorial, you will use the geometry engine to buffer, intersect, and calculate the distance between the cursor and the geometries related to the Trails feature layer.

Move your mouse around the map below to see how it works.

Steps

Create a starter app

  1. Open the JavaScript Starter App on CodePen.

  2. In CodePen, click Fork and save the pen as ArcGIS JavaScript Tutorials: Buffer and intersect geometry.

Add modules

In this step you will load the required modules for the application.

  1. In the require statement, add the FeatureLayer, Graphic, and geometryEngine modules.

        require([
          "esri/Map",
          "esri/views/MapView",
          "esri/layers/FeatureLayer",
          "esri/Graphic",
          "esri/geometry/geometryEngine"
        ], function(Map, MapView, FeatureLayer, Graphic, geometryEngine) {
    

Add the Trails feature layer

  1. At the end of the code in the main function, create a featureLayer variable that references the Trails layer and then add it to the map. Learn more about feature layers in the Add layers to a map tutorial.

        var featureLayer = new FeatureLayer({
          url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails_Styled/FeatureServer/0"
        });
    
        map.add(featureLayer);
    

Create a buffer

In this step you will use the geometry engine to find the nearest trail to the cursor and then create a buffer around each feature geometry. You will first need to implement a mechanism to find trail features as the cursor moves on the map.

  1. Define an activeGraphic variable and a function called findNearestGraphic. Use the view hitTest function to find the closest graphic to the screenpoint and only return new trails found. Use the OBJECTID attribute of the graphic to identify when a new graphic has been found.

        var activeGraphic;
    
        function findNearestGraphic(event) {
          return view.hitTest(event).then(function (response) {
            var graphic;
            // Get the Trail graphics only
            if (response.results.length) {
              graphic = response.results.filter(function (result) {
                return (result.graphic.layer === featureLayer);
              })[0].graphic;
            }
            // Only return new graphics are found
            if (graphic) {
              if (!activeGraphic || (activeGraphic.attributes.OBJECTID !== graphic.attributes.OBJECTID)) {
                return graphic;
              } else {
                return null;
              }
            } else {
              return null;
            }
          });
        }
    
  2. Define a bufferGraphic variable and a function called drawBuffer to display the buffer graphic after it is created. Learn more about creating graphics in the Display point, line, and polygon graphics tutorial.

        var bufferGraphic;
    
        function drawBuffer(bufferGeometry) {
          view.graphics.remove(bufferGraphic);
          bufferGraphic = new Graphic({
            geometry: bufferGeometry,
            symbol: {
              type: "simple-fill",
              color: "rgba(0,0,0,0)",
              outline: {
                color: "rgba(0,0,0,.5)",
                width: 1
              }
            }
          });
          view.graphics.add(bufferGraphic);
        }
    
  3. Use the view pointer-move event to listen to when the cursor moves and call the findNearestGraphic. Pass in the event (screen coordinates) to find the closest graphic. Once a graphic is found, set the activeGraphic variable and then use the geometryEngine geodesicBuffer function to buffer the trail. Set the distance to 0.25 and the units to miles. Add the buffer to the view with the drawBuffer function.

        view.on("pointer-move", function(event){
          findNearestGraphic(event).then(function(graphic){
            if (graphic) {
              activeGraphic = graphic;
              var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, .25, "miles");
              drawBuffer(buffer);
            }
          });
        });
    
  4. Run the app and move your cursor on the map. You should see a buffer appear whenever you move over trail features.

Intersect a point with a polygon

In this step you will use the geometry engine to determine if the cursor point intersects with the buffer (polygon), and if it does, change the color.

  1. Add code to check if a bufferGraphic exists and get the current cursor position. Use the geometry engine intersects function to determine if the cursor point intersects with the buffer geometry. If they do, change the symbol color to highlight the buffer.

        view.on("pointer-move", function(event){
          findNearestGraphic(event).then(function(graphic){
            if (graphic) {
              activeGraphic = graphic;
              var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, .25, "miles");
              drawBuffer(buffer);
            }
          });
          //*** ADD ***//
          if (bufferGraphic) {
            var currentPoint = view.toMap(event);
            var intersects = geometryEngine.intersects(bufferGraphic.geometry, currentPoint);
            var symbol = bufferGraphic.symbol.clone();
            if (intersects) {
              symbol.color = "rgba(0,0,0,.15)"; // Highlight
            } else {
              symbol.color = "rgba(0,0,0,0)"; // Transparent
            }
            bufferGraphic.symbol = symbol;
          }
        });
    
  2. Run the app and move your cursor over some trails. You should see the buffer outline change color when the cursor is inside of the buffer.

Find the closest point

In this step you will use the geometry engine to find the closest point in the buffer to the cursor and create a line to show the shortest distance between the two points.

  1. Define a lineGraphic variable and a function called drawLine to create a line from two points and display it as a graphic. Learn more about creating graphics in the Display point, line, and polygon graphics tutorial.

          var lineGraphic;
    
          function drawLine(point, point2) {
            view.graphics.remove(lineGraphic);
            lineGraphic = new Graphic({
              geometry: {
                type: "polyline",
                paths: [
                  [point.longitude, point.latitude],
                  [point2.longitude, point2.latitude]
                ]
              },
              symbol: {
                type: "simple-line",
                color: "#333",
                width: 1
              }
            });
            view.graphics.add(lineGraphic);
          }
    
  2. In the pointer-move function, use the geometry engine nearestVertex function to find the closest point in the buffer to the cursor point. Use the coordinate returned and the cursor point to call drawLine.

          view.on("pointer-move", function(event){
            findNearestGraphic(event).then(function(graphic){
              if (graphic) {
                activeGraphic = graphic;
                var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, .25, "miles");
                drawBuffer(buffer);
              }
            });
            if (bufferGraphic) {
              var cursorPoint = view.toMap(event);
              var intersects = geometryEngine.intersects(bufferGraphic.geometry, cursorPoint);
              var symbol = bufferGraphic.symbol.clone();
              if (intersects) {
                symbol.color = "rgba(0,0,0,.15)";
              } else {
                symbol.color = "rgba(0,0,0,0)";
              }
              bufferGraphic.symbol = symbol;
              //*** ADD ***//
              var vertexResult = geometryEngine.nearestVertex(bufferGraphic.geometry, cursorPoint);
              var closestPoint = vertexResult.coordinate;
              drawLine(cursorPoint, closestPoint)
            }
          });
    
  3. Run the app and move your cursor around the map. You should see a line and the distance draw on the map.

Calculate length

Now you will use the geometry engine to calculate the length of the line and display the value in miles.

  1. Define a textGraphic variable and a function called drawText to create a text graphic to display the length of the line. Learn more about creating graphics in the Display point, line, and polygon graphics tutorial.

          var textGraphic;
    
          function drawText(point, distance) {
            view.graphics.remove(textGraphic);
            textGraphic = new Graphic({
              geometry: point,
              symbol: {
                type: "text",
                text: distance.toFixed(2) + " miles",
                color: "black",
                font: {
                  size: 12
                },
                haloColor: "white",
                haloSize: 1
              }
            })
            view.graphics.add(textGraphic)
          }
    
  2. In the pointer-move function, use the geometry engine geodesicLength function to determine the length of the line. Use the cursorPoint and distance to call drawText.

          view.on("pointer-move", function(event){
            findNearestGraphic(event).then(function(graphic){
              if (graphic) {
                activeGraphic = graphic;
                var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, .25, "miles");
                drawBuffer(buffer);
              }
            });
            if (bufferGraphic) {
              var cursorPoint = view.toMap(event);
              var intersects = geometryEngine.intersects(bufferGraphic.geometry, cursorPoint);
              var symbol = bufferGraphic.symbol.clone();
              if (intersects) {
                symbol.color = "rgba(0,0,0,.15)";
              } else {
                symbol.color = "rgba(0,0,0,0)";
              }
              bufferGraphic.symbol = symbol;
              var vertexResult = geometryEngine.nearestVertex(bufferGraphic.geometry, cursorPoint);
              var closestPoint = vertexResult.coordinate;
              drawLine(cursorPoint, closestPoint)
              //*** ADD ***//
              var distance = geometryEngine.geodesicLength(lineGraphic.geometry, "miles");
              drawText(cursorPoint, distance);
            }
          });
    
  3. Run the app and move your cursor around the map. You should see a line and the length draw at the end of it.

Congratulations, you're done!

Your app should look something like this.

Challenge

Use a click event

You can use a number of other view pointer events to interact with the view and perform geometry operations. Update the code by replacing pointer-move with the click event so the code only executes when you click on the map.

      //*** UPDATE ***//
      // view.on("pointer-move", function(event){
      view.on("click", function(event){

        ...

      });

Densify the buffer

You can perform many other types of operations with the geometry engine. Try adding points to the buffer by using the densify function. Add points every 250 meters around the buffer.

      view.on("click", function(event){
        findNearestGraphic(event).then(function(graphic){
          if (graphic) {
            activeGraphic = graphic;
            var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, .25, "miles");
            //*** ADD ***//
            buffer = geometryEngine.densify(buffer, 250, "meters");
            drawBuffer(buffer);
          }
        });

        ...

      });
Content