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
Open the JavaScript Starter App on CodePen.
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.
In the
require
statement, add theFeatureLayer
,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
At the end of the code in the main
function
, create afeatureLayer
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.
Define an
activeGraphic
variable and a function calledfindNearestGraphic
. Use the viewhitTest
function to find the closest graphic to the screenpoint and only return new trails found. Use theOBJECTID
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; } }); }
Define a
bufferGraphic
variable and a function calleddrawBuffer
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); }
Use the view
pointer-move
event to listen to when the cursor moves and call thefindNearestGraphic
. Pass in theevent
(screen coordinates) to find the closest graphic. Once a graphic is found, set theactiveGraphic
variable and then use thegeometryEngine
geodesicBuffer
function to buffer the trail. Set the distance to0.25
and the units tomiles
. Add the buffer to the view with thedrawBuffer
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); } }); });
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.
Add code to check if a
bufferGraphic
exists and get the current cursor position. Use the geometry engineintersects
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; } });
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.
Define a
lineGraphic
variable and a function calleddrawLine
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); }
In the
pointer-move
function, use the geometry enginenearestVertex
function to find the closest point in the buffer to the cursor point. Use the coordinate returned and the cursor point to calldrawLine
.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) } });
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.
Define a
textGraphic
variable and a function calleddrawText
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) }
In the
pointer-move
function, use the geometry enginegeodesicLength
function to determine the length of the line. Use thecursorPoint
anddistance
to calldrawText
.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); } });
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);
}
});
...
});