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 API for JavaScript Tutorials: Buffer and intersect geometry.
Add modules
In this step you will load the required modules for the application.
In the
requirestatement, 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 afeatureLayervariable 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
activeGraphicvariable and a function calledfindNearestGraphic. Use the viewhitTestfunction to find the closest graphic to the screenpoint and only return new trails found. Use theOBJECTIDattribute 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
bufferGraphicvariable and a function calleddrawBufferto 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-moveevent 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 theactiveGraphicvariable and then use thegeometryEnginegeodesicBufferfunction to buffer the trail. Set the distance to0.25and the units tomiles. Add the buffer to the view with thedrawBufferfunction.view.on("pointer-move", function (event) { findNearestGraphic(event).then(function (graphic) { if (graphic) { activeGraphic = graphic; var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, 0.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
bufferGraphicexists and get the current cursor position. Use the geometry engineintersectsfunction 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, 0.25, "miles"); drawBuffer(buffer); } }); //*** ADD ***// 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)"; // 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
lineGraphicvariable and a function calleddrawLineto 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-movefunction, use the geometry enginenearestVertexfunction 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, 0.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
textGraphicvariable and a function calleddrawTextto 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-movefunction, use the geometry enginegeodesicLengthfunction to determine the length of the line. Use thecursorPointanddistanceto calldrawText.view.on("pointer-move", function (event) { findNearestGraphic(event).then(function (graphic) { if (graphic) { activeGraphic = graphic; var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, 0.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);
}
});
...
});