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.
Open the JavaScript Starter App on CodePen.
In CodePen, click Fork and save the pen as ArcGIS JavaScript Tutorials: Buffer and intersect geometry
.
In this step you will load the required modules for the application.
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) {
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);
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 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;
}
});
}
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);
}
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);
}
});
});
Run the app and move your cursor on the map. You should see a buffer appear whenever you move over trail features.
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 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;
}
});
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.
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 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);
}
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)
}
});
Run the app and move your cursor around the map. You should see a line and the distance draw on the map.
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 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)
}
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);
}
});
Run the app and move your cursor around the map. You should see a line and the length draw at the end of it.
Your app should look something like this.
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){
...
});
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);
}
});
...
});