Hide Table of Contents
View Simplify a polygon sample in sandbox
Simplify a polygon

Description

This example shows how to simplify a topologically illegal feature so that it can be used in an application. The sample shows a polygon graphic whose outline crosses itself. The simplify operation breaks up the polygon into a three-part feature. The sample proves that the feature is legal by performing a query based on the simplified polygon.

In this example, the illegal polygon was created programmatically, but a more likely scenario is that a user draws an illegal polygon on the screen and attempts to use it. The sample allows you to explore what happens when you perform the query with the illegal polygon.

Simplifying geometries requires an ArcGIS Server geometry service. This type of service is new at ArcGIS Server 9.3 and you can use it for buffering, projecting, and geometry simplification in JavaScript applications. This line creates the geometry service:

gsvc = new esri.tasks.GeometryService("http://sampleserver1.arcgisonline.com/arcgis/rest/services/Geometry/GeometryServer");

You can use the Services Directory to discover the URL to your own geometry service. Since the service name is required to be Geometry, the URL will be very similar to the one above.

A number of functions work together in this example:

  • initialize- Creates the map and adds the necessary layers and tasks.
  • drawPolygon- Draws the self-intersecting polygon on the map.
  • doSimplify- Calls GeometryService.simplify() to break the polygon into three separate rings.
  • simplifyCallback- Reports the number of rings returned by the simplify operation.
  • doQuery- Queries all the parcels that are intersected or contained by the polygon.
  • queryCallback- Draws the query results on the map. For performance, the results are displayed as one large multipart polygon.

Note: At version 2.2, a new utility method was added that checks to see if a polygon is self-intersecting. You can use the esri.geometry.polygonSelfIntersecting utility method to determine if the polygons are self-intersecting, if not there is no need to call simplify.

if(esri.geometry.polygonSelfIntersecting(polygonGraphic.geometry)){
gsvc
.simplify([ polygonGraphic.geometry ], simplifyCallback);
}

Code

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
    <title>Simplify a polygon</title>

    <link rel="stylesheet" href="https://js.arcgis.com/3.22/dijit/themes/claro/claro.css">
    <link rel="stylesheet" href="https://js.arcgis.com/3.22/esri/css/esri.css"> 

    <script src="https://js.arcgis.com/3.22/"></script>
    <script>
    dojo.require("esri.map");
    dojo.require("esri.tasks.geometry");
    dojo.require("esri.geometry");

    var map = null;
    var gsvc = null;
    var qtask = null;
    var polygonGraphic = null;
    var queryGraphic = null;

    function initialize() {
      map = new esri.Map("map", { 
        basemap: "streets", 
        center: [-83.273, 42.572],
        zoom: 15
      });
      console.log("created map");
      dojo.connect(map, "onLoad", drawPolygon);
      
      gsvc = new esri.tasks.GeometryService("https://utility.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
      qtask = new esri.tasks.QueryTask("https://sampleserver3.arcgisonline.com/ArcGIS/rest/services/BloomfieldHillsMichigan/Parcels/MapServer/2");
    }

    function drawPolygon() {
      console.log("draw poly");
      var latOffset, lonOffset, center, lat, lon, points;

      var center = map.extent.getCenter();
      var lat = center.y;
      var lon = center.x + 500;

      // Create a Green Polygon
      var latOffset = 500;
      var lonOffset = 500;
      var points = [
        new esri.geometry.Point(lon - lonOffset, lat, map.spatialReference),
        new esri.geometry.Point(lon, lat + latOffset, map.spatialReference),
        new esri.geometry.Point(lon + lonOffset, lat, map.spatialReference),
        new esri.geometry.Point(lon, lat - latOffset, map.spatialReference),
        new esri.geometry.Point(lon - lonOffset, lat, map.spatialReference),
        new esri.geometry.Point(lon - 2 * lonOffset, lat + latOffset, map.spatialReference),
        new esri.geometry.Point(lon - 3 * lonOffset, lat, map.spatialReference),
        new esri.geometry.Point(lon - 2 * lonOffset, lat - latOffset, map.spatialReference),
        new esri.geometry.Point(lon - 1.5 * lonOffset, lat + latOffset, map.spatialReference),
        new esri.geometry.Point(lon - lonOffset, lat, map.spatialReference)
      ];
      var polygon = new esri.geometry.Polygon();
      polygon.addRing(points);
      polygon.spatialReference = map.spatialReference;

      console.log("created poly geom");

      // Add the polygon to map
      var symbol = new esri.symbol.SimpleFillSymbol().setStyle(esri.symbol.SimpleFillSymbol.STYLE_SOLID);
      polygonGraphic = new esri.Graphic(polygon, symbol, { keeper: true });
      var polyLayer = new esri.layers.GraphicsLayer({ id: "poly" });
      map.addLayer(polyLayer);
      polyLayer.add(polygonGraphic);
      console.log("added poly graphic");
    }

    function doSimplify() {
      //simplify the polygon if self-intersecting
      if (esri.geometry.polygonSelfIntersecting(polygonGraphic.geometry)) {
        console.log("doing simplify");
        gsvc.simplify([ polygonGraphic.geometry ], simplifyCallback);
      }
    }

    function simplifyCallback(geometries) {
      alert("Number of Rings returned by Simplify operation = " + geometries[0].rings.length);
      polygonGraphic.setGeometry(geometries[0]);
      doQuery(polygonGraphic); // select a ring to do query
    }

    function doQuery(polygonGraphic) {
      map.graphics.clear();

      var query = new esri.tasks.Query();
      //query.spatialRelationship = esri.tasks.Query.SPATIAL_REL_INTERSECTS;
      query.spatialRelationship = esri.tasks.Query.SPATIAL_REL_CONTAINS;
      query.geometry = polygonGraphic.geometry;
      query.returnGeometry = true;
      qtask.execute(query, queryCallback);
    }

    function queryCallback(featureSet) {
      var symbol = new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([0,0,255,0.65]), 2), new dojo.Color([0,0,255,0.35]));
      var features = featureSet.features;
      dojo.forEach(features, function(f) {
        f.setSymbol(symbol);
        map.graphics.add(f);
      });
    }

    dojo.ready(initialize);
  </script>
</head>

<body class="claro">
  Many spatial operations require topologically correct geometry.
  <br>
  <input type="button" value="Simplify the polygon and Do a Query with simplified polygon" onclick="doSimplify();" />
  <br>If you try to use the self-intersecting geometry above without simplifying it you will get incorrect results.
  <br>
  <input type="button" value="Execute query task without simplifying polygon" onclick="doQuery(polygonGraphic);" />
  <div id="map" style="width:600px; height:400px; border:1px solid #000;"></div>
</body>
</html>