This is the final post in a three-part series exploring the client-side GeometryEngine in the ArcGIS API for JavaScript. This series covers the following topics:
- Part 1: Testing spatial relationships and editing
- Part 2: Measurement
- Part 3: Overlay Analysis
In the first post, we reviewed testing functions GeometryEngine provides that are useful for editing geometries in web applications. In the second post, we explored measurement and proximity functions, such as buffer, area, and length and established when to use the planar and geodesic versions of each.
In this final post, we’ll take a look at GeometryEngine’s overlay methods that can be leveraged to perform simple spatial analysis for data exploration purposes. These overlay functions include clip, difference, intersect, union, and symmetricDifference.
The following sample demonstrates how three of these overlay functions can work together to create a simple application with the purpose of exploring land ownership. Specifically, this app allows users to click or drag the mouse anywhere in the state of Utah to view privately-owned land and compare it to public land within 10 miles of the mouse location. It then generates a pie chart, which compares the areas of each land type to each other.
Whether you want to know the area affected by fire or flood within a certain distance of a point, or the habitat of sensitive or endangered species within the buffer of a project area, applications such as these are useful because they can explore spatial relationships and perform preliminary calculations prior to doing hefty GIS analysis.
Open sample: Overlay in GeometryEngine
Notice how quickly it takes for this whole process to run. The performance of this app varies depending on the complexity of features involved on any given mouse click or drag. But the experience would be much slower if this were attempted with GeometryService or a custom geoprocessing task.
This app uses geometries from the graphics of a layer representing privately owned land in Utah as a basis for the analysis. The following functions are used to produce the results: geodesicBuffer, intersect, union, difference, and geodesicArea. In addition, intersects (note the ‘s’), within, and overlaps are used to test the spatial relationship of features to ensure the analysis is done within the state of Utah and that proper inputs are in place before any overlay operations are performed.
Let’s take a look at the code to see how these methods all work together in generating our results.
Overlay sample workflow
First, the buffer is created using geodesicBuffer with the mouse location as the center point. The evt
object containing the mapPoint is retrieved on each mouse-click and mouse-drag.
var centerPt = evt.mapPoint;
//Generate buffer of mouse location
var buffGeom = geometryEngine.geodesicBuffer(centerPt, 10, "miles");
Now we have a 10-mile buffer. Prior to performing any overlay, it is good practice to test the spatial relationship of the buffer with bounding geometries. In this case we don’t want to attempt an overlay if the buffer exists outside the Utah state boundary since there won’t be any features to overlay outside the state. Also, if the buffer is partially within the state, we only want to keep the portion of the buffer’s geometry that overlaps the state. If there is overlap, we’ll use intersect to only get the intersecting portion of the geometries.
//check if buffer is completely within Utah -- UtahBoundary is a polygon geometry var within = geometryEngine.within(bufferGeom, UtahBoundary); //check if buffer overlaps Utah var overlaps = geometryEngine.overlaps(bufferGeom, UtahBoundary); if(!within && overlaps){ //If buffer is not within Utah, but overlaps it, then only keep the portion within Utah bufferGeom = geometryEngine.intersect(bufferGeom, UtahBoundary); } if(!within && !overlaps){ //If buffer is completely outside Utah, then don't attempt any overlay console.log("outside of utah!"); return; } //If the buffer is completely within Utah, then keep the original buffer geometry
We didn’t use clip to keep the area common to both geometries because clip uses the envelope of one of the input geometries to find the common area, whereas intersect uses the geometry itself.
Once we know the buffer’s geometry is located within Utah’s boundary (or overlaps it), we can proceed with the overlay to determine the area of private and public land inside the buffer. In this case, the ownership layer only contains features representing privately owned land. We are presuming that all other land is owned or managed by some government entity. To make the area calculation easier, we want to represent all private land as one polygon. To do this we need to merge the private geometries together using union.
//To simplify the merge, we only care about the geometries that intersect the buffer //so we'll loop through the geometries to essentially preform a "select by location" //using 'intersects' and keep the private land geometries that intersect the buffer. var intersectingPvtLand = array.filter(privateLandGeoms, function(pvtGeom){ if(geometryEngine.intersects(bufferGeom, pvtGeom)){ return pvtGeom; } });
Now that we have an array of geometries that intersect the buffer we can proceed with the overlay and area calculation.
//merge all the private land features that intersect buffer into one geometry var privateUnion = geometryEngine.union(intersectingPvtLand); //get intersection of buffer and union of private land (cookie cutter) var privateIntersect = geometryEngine.intersect(privateUnion, bufferGeom); //calculate area of private land within buffer var privateArea = geometryEngine.geodesicArea(privateIntersect, "square-miles");
The geometry representing private land within the buffer can now be added to the map as a graphic. The area can be used for further analysis or printed to the user.
Once we have the private land, the area of the public land is easy to calculate. There are a number of ways to do it, but since we’re exploring GeometryEngine, we’ll see how difference can help us easily find this result.
//most land that isn't private is public (city, county, state, or federally owned) var publicLand = geometryEngine.difference(bufferGeom, privateIntersect); //calculate area of public land var publicArea = geometryEngine.geodesicArea(publicLand, "square-miles");
While the area calculations are good, they don’t take into account other features that affect the result, such as water bodies within the buffer. To improve the analysis, we could use difference to remove the geometries of water bodies within the buffer.
GeometryEngine overlay and analysis widgets
Note there are similar overlay functions available in the ArcGIS JavaScript API as analysis widgets, such as OverlayLayers, CreateBuffers, MergeLayers, etc. These widgets are very different from the overlay functions in GeometryEngine. GeometryEngine methods, for example, only execute processes in the browser; that means developers can create apps focused on data exploration with an enhanced interactive experience for users. In addition, GeometryEngine’s methods work only with geometries, not attributes or layers.
While GeometryEngine’s overlay methods can provide a rich user experience in web applications, they are limited to generating results within a single browser session. The analysis widgets are different because they perform analysis for layers, which include both geometries and attributes, and process the analyses server-side. Therefore developers can use the analysis widgets to generate overlay results as feature services within the ArcGIS platform. These results can then be perpetuated and consumed inside custom web applications and ArcGIS Online web maps.
In summary, GeometryEngine is a powerful client-side engine that provides developers with the ability to test geometric relationships; calculate proximity, areas and distances; and perform simple overlays all client-side. These are fundamental methods that make GIS applications a popular way to explore, analyze, and summarize data.
Article Discussion: