Merging Polygons with JavaScript 4.24

1289
6
Jump to solution
10-06-2022 07:13 AM
PaulAnderson2k
New Contributor II

I am having trouble trying to understand why my polygons do not merge.  I've tried to following posts: 

 

https://community.esri.com/t5/arcgis-api-for-javascript-questions/how-can-i-split-a-polygon-and-how-...

https://community.esri.com/t5/arcgis-api-for-javascript-questions/merge-polygons-in-javascript-api-4...

https://community.esri.com/t5/arcgis-api-for-javascript-questions/merge-several-polygon-in-javascrip...

 

I click on the Quick Add button and then overlap the 2 polygons.  Then I click on the Merge button - hoping that the two overlapped polygons will join.

Sometimes I am left with one.  Sometimes all Polygons are removed.

The coordinates that comeback from the GeometryEngine are spurious (line 121):

PaulAnderson2k_0-1665065532725.png

 

HTML

 

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ArcGIS - Art of the Possible Demo</title>

    <link rel="stylesheet" href="index.css">
    <link rel="stylesheet" href="https://js.arcgis.com/4.24/esri/themes/light/main.css">
</head>

<body>
    <div id="control-toolbar">
        <button type="button" id="quick-add">Quick Add</button>
        <button type="button" id="merge">Merge</button>
    </div>

    <div id="viewDiv"></div>

    <script src="https://js.arcgis.com/4.24/"></script>
    <script src="index.js"></script>
</body>

</html>

 

 

 

CSS

 

 

html, body, #viewDiv {
    padding: 0;
    margin: 0;
    height: 100%;
    width: 100%;
  }

  .esri-view .esri-ui-corner .esri-component.esri-widget--panel-height-only {
    width: 250px;
    max-height: calc(100% - 45px) !important;
    position: fixed;
  }

 

 

 

Javascript

 

 

require([
  "esri/config",
  "esri/Map",
  "esri/views/MapView",

  "esri/widgets/Sketch/SketchViewModel",
  "esri/layers/GraphicsLayer",
  "esri/Graphic",
  "esri/geometry/geometryEngine",
  "esri/geometry/Polygon"
], (
  esriConfig,
  Map,
  MapView,
  SketchViewModel,
  GraphicsLayer,
  Graphic,
  geometryEngine,
  Polygon
) => {
  const graphicsLayer = new GraphicsLayer();

  const fillSymbol = {
    type: "simple-fill", // autocasts as new SimpleFillSymbol()
    color: [227, 139, 79, 0.8],
    outline: {
      // autocasts as new SimpleLineSymbol()
      color: [255, 255, 255],
      width: 1
    }
  };

  const map = new Map({
    basemap: {
      portalItem: {
        id: "8d91bd39e873417ea21673e0fee87604" // nova basemap
      }
    },
    layers: [graphicsLayer]
  });

  const view = new MapView({
    map: map,
    center: [-1.9, 53.9], //Longitude, latitude
    zoom: 5,
    container: "viewDiv",
    constraints: {
      snapToZoom: false,
    },
  });

  // try and get the polygon back out of ui
  const sketchVM = new SketchViewModel({
    view: view,
    layer: graphicsLayer
  });
  sketchVM.on("update", (event) => {
    currentGraphic = event.graphics[0]; // set the currentGraphic to the graphic being updated
    currentSymbol = currentGraphic.symbol; // set the current symbol based on the graphic's symbol
    if (event.state == "start") {
      // configureProperties(currentSymbol); // update the UI based on the current symbol
    }
    if (event.state == "complete") {
      currentGraphic = null; // set the current graphic to null once update is complete
    }
  });
  sketchVM.on("create", (event) => {
    currentGraphic = event.graphic; // set the currentGraphic to the graphic being created
    if (event.state == "complete" || event.state == "cancel") {
      currentGraphic = null; // set graphic to null once create is complete or cancelled
    }
  });

  document.getElementById("quick-add").addEventListener("click", function (event) {
    const polygons = [
      {
        type: "polygon", // autocasts as new Polygon()
        rings: [
          [-2.778906249999767, 54.16455520570663],
          [2.296777343748883, 54.650508751294815],
          [1.7694335937490233, 52.69855676850477],
          [-3.064550781249691, 52.73848533714189],
          [-3.7896484374994994, 53.18845854138924],
          [-2.778906249999767, 54.16455520570663]
        ]
      },
      {
        type: "polygon",
        rings: [
          [1.4837890624990875, 54.91028142199062],
          [2.274804687498878, 55.424835143449705],
          [4.362207031248323, 54.87237008287549],
          [3.3954101562485794, 53.92588439579337],
          [2.692285156248766, 54.82176611083718],
          [1.4837890624990875, 54.91028142199062]
        ]
      }
    ];

    polygons.forEach(polygon => {
      const polygonGraphic = new Graphic({
        geometry: polygon,
        symbol: clone(fillSymbol)
      });

      graphicsLayer.add(polygonGraphic);
    });
  });

  function clone(obj) {
    return JSON.parse(JSON.stringify(obj))
  }

  document.getElementById("merge").addEventListener('click', function () {
    debugger;
    var updatedGeometry = new Array();
    graphicsLayer.graphics.map(function (gra) {
      updatedGeometry.push(new Polygon(gra.geometry));
    });

    var joinedPolygons = geometryEngine.union(updatedGeometry);
    graphicsLayer.removeAll();

    joinedPolygons.rings.forEach(function (ring) {
      let resultpolygon = new Polygon();
      resultpolygon.rings = [ring];
      resultpolygon.spatialReference.wkid = 4326;

      let resultgraphic = new Graphic({
        geometry: resultpolygon,
        symbol: clone(fillSymbol)
      });

      graphicsLayer.add(resultgraphic);
    });
  });
});

 

 

 

0 Kudos
1 Solution

Accepted Solutions
ReneRubalcava
Frequent Contributor

Interesting, I didn't know that sketch would default to the map SR, but that makes sense I think. Just something to keep in mind when using geometry engine methods. I should be a good practice anyway since it does require all geometries to be the same spatial reference.

 

You could use the webMercatorUtils module to help convert geographic and webMercator pretty easily.

graphicsLayer.graphics.map(function (gra) {
    if (gra.geometry.spatialReference.wkid === 4326) {
        updatedGeometry.push(
            webMercatorUtils.geographicToWebMercator(
                gra.geometry.clone()
            )
        );
    }
    else {
        updatedGeometry.push(gra.geometry.clone());
    }
});
const joinedPolygon = geometryEngine.union(updatedGeometry);

That should do it.

https://codepen.io/odoe/pen/oNdPKNv?editors=0010

View solution in original post

6 Replies
ReneRubalcava
Frequent Contributor

This is expected.

You might see some oddities because you are using a class instance to new up a new class, which you don't have to do

// don't need this
updatedGeometry.push(new Polygon(gra.geometry));

// can do this
updatedGeometry.push(gra.geometry);

// or this
updatedGeometry.push(gra.geometry.clone());

 

But you have two polygons that do not overlap. So when they union, it becomes a polygon with multiple rings.

ReneRubalcava_0-1665070300401.png

You don't need to iterate over the rings, the result of the geometryEngine union is a geometry, so you can use it directly.

const joinedPolygon = geometryEngine.union(updatedGeometry);
graphicsLayer.removeAll();
let resultgraphic = new Graphic({
    geometry: joinedPolygon,
    symbol: mergeSymbol
});
graphicsLayer.add(resultgraphic);

demo: https://codepen.io/odoe/pen/RwyYKxN?editors=0010

 

If you want the polygons to become a single polygon with a single ring, you can do something like convexHull

const [result] = geometryEngine.convexHull(updatedGeometry, true);
graphicsLayer.removeAll();
let resultgraphic = new Graphic({
    geometry: result,
    symbol: mergeSymbol
});
graphicsLayer.add(resultgraphic);

ReneRubalcava_1-1665070640146.png

demo: https://codepen.io/odoe/pen/ZEoMLmj

PaulAnderson2k
New Contributor II

Thanks for the detailed answer.

I should have been more clear in the question.  I am moving one of the polygons to intersect with the other:

PaulAnderson2k_0-1665131311662.png

Then clicking the Merge button.  This is where the problem is.

However, if I change the Quick Add to put the polygon into that position, it does intersect fine.

Polygon 0
[-2.778906249999767, 54.16455520570663]
[2.296777343748883, 54.650508751294815]
[1.7694335937490233, 52.69855676850477]
[-3.064550781249691, 52.73848533714189]
[-3.7896484374994994, 53.18845854138924]
[-2.778906249999767, 54.16455520570663]

Polygon 1
[0.8685546874990959, 53.79630190975718]
[1.659570312498886, 54.32504414504308]
[3.746972656248331, 53.757348875329235]
[2.780175781248588, 52.78502248089292]
[2.0770507812487744, 53.705355243112635]
[0.8685546874990959, 53.79630190975718]

PaulAnderson2k_1-1665132619403.png

Updated quick add code:

 

document.getElementById("quick-add").addEventListener("click", function (event) {
    const polygons = [
      {
        type: "polygon", // autocasts as new Polygon()
        rings: [
          [-2.778906249999767, 54.16455520570663],
          [2.296777343748883, 54.650508751294815],
          [1.7694335937490233, 52.69855676850477],
          [-3.064550781249691, 52.73848533714189],
          [-3.7896484374994994, 53.18845854138924],
          [-2.778906249999767, 54.16455520570663]
        ]
      },
      {
        type: "polygon",
        rings: [
          // [1.4837890624990875, 54.91028142199062],
          // [2.274804687498878, 55.424835143449705],
          // [4.362207031248323, 54.87237008287549],
          // [3.3954101562485794, 53.92588439579337],
          // [2.692285156248766, 54.82176611083718],
          // [1.4837890624990875, 54.91028142199062]
          [0.8685546874990959, 53.79630190975718],
          [1.659570312498886, 54.32504414504308],
          [3.746972656248331, 53.757348875329235],
          [2.780175781248588, 52.78502248089292],
          [2.0770507812487744, 53.705355243112635],
          [0.8685546874990959, 53.79630190975718]
        ]
      }
    ];

 

 

Something is suggesting (perhaps) the issue is when I drag the smaller polygon.

I have added to a new code pen here Merge with ArcGIS (codepen.io)

0 Kudos
PaulAnderson2k
New Contributor II

It's definitely a problem when moving a polygon using the SketchViewModel.  This is added in the code sample with:

const sketchVM = new SketchViewModel({
    view: view,
    layer: graphicsLayer
  });

 

I've removed the code with the sketchVM.on event handlers as they didn't do anything.  Updated demo here:

Merge with ArcGIS (codepen.io)

0 Kudos
PaulAnderson2k
New Contributor II

After further debugging...

When one of the polygons is clicked, I noticed the following changes in the graphicsLayer.graphics.items.

Here the wkid changes for Polygon 1 after clicking on it using the mouse.  The ring coordinates change along with the spatial reference.

 After Quick Add Intersect Button ClickAfter Click On Polygon x
Polygon 0{
"aggregateGeometries": null,
"geometry": {
"spatialReference": {
"wkid": 4326
},
"rings": [
[
[
-2.778906249999767,
54.16455520570663
],
[
2.296777343748883,
54.650508751294815
],
[
1.7694335937490233,
52.69855676850477
],
[
-3.064550781249691,
52.73848533714189
],
[
-3.7896484374994994,
53.18845854138924
],
[
-2.778906249999767,
54.16455520570663
]
]
]
},
"symbol": {
"type": "esriSFS",
"color": [
227,
139,
79,
204
],
"outline": {
"type": "esriSLS",
"color": [
255,
255,
255,
255
],
"width": 1,
"style": "esriSLSSolid"
},
"style": "esriSFSSolid"
},
"attributes": {},
"popupTemplate": null
}
{
"aggregateGeometries": null,
"geometry": {
"spatialReference": {
"wkid": 4326
},
"rings": [
[
[
-2.778906249999767,
54.16455520570663
],
[
2.296777343748883,
54.650508751294815
],
[
1.7694335937490233,
52.69855676850477
],
[
-3.064550781249691,
52.73848533714189
],
[
-3.7896484374994994,
53.18845854138924
],
[
-2.778906249999767,
54.16455520570663
]
]
]
},
"symbol": {
"type": "esriSFS",
"color": [
227,
139,
79,
204
],
"outline": {
"type": "esriSLS",
"color": [
255,
255,
255,
255
],
"width": 1,
"style": "esriSLSSolid"
},
"style": "esriSFSSolid"
},
"attributes": {},
"popupTemplate": null
}
Polygon 1{
"aggregateGeometries": null,
"geometry": {
"spatialReference": {
"wkid": 4326
},
"rings": [
[
[
0.8685546874990959,
53.79630190975718
],
[
1.659570312498886,
54.32504414504308
],
[
3.746972656248331,
53.757348875329235
],
[
2.780175781248588,
52.78502248089292
],
[
2.0770507812487744,
53.705355243112635
],
[
0.8685546874990959,
53.79630190975718
]
]
]
},
"symbol": {
"type": "esriSFS",
"color": [
227,
139,
79,
204
],
"outline": {
"type": "esriSLS",
"color": [
255,
255,
255,
255
],
"width": 1,
"style": "esriSLSSolid"
},
"style": "esriSFSSolid"
},
"attributes": {},
"popupTemplate": null
}
{
"aggregateGeometries": null,
"geometry": {
"spatialReference": {
"latestWkid": 3857,
"wkid": 102100
},
"rings": [
[
[
96687.06553851021,
7131672.313613452
],
[
184742.5221230099,
7231957.69472358
],
[
417111.08810988395,
7124334.358898078
],
[
309487.75228438433,
6943331.475918833
],
[
231216.2353203846,
7114550.419277586
],
[
96687.06553851021,
7131672.313613452
]
]
]
},
"symbol": {
"type": "esriSFS",
"color": [
227,
139,
79,
204
],
"outline": {
"type": "esriSLS",
"color": [
255,
255,
255,
255
],
"width": 1,
"style": "esriSLSSolid"
},
"style": "esriSFSSolid"
},
"attributes": {},
"popupTemplate": null
}
0 Kudos
PaulAnderson2k
New Contributor II

I am thinking the problem is the base map spatial references.  I am not sure what the correct solution is here.  Help!

I am believe I am using WGS84 coordinates.  On closer inspection the base map has a spaitalReference.wkid of 102100 which refers to WGS_1984_Web_Mercator_Auxiliary_Sphere (WKID 3857).

Spatial references | Documentation | ArcGIS Developers

SpatialReference | API Reference | ArcGIS API for JavaScript 4.24 | ArcGIS Developers

FAQ: Why does ArcGIS Online use a deprecated spatial reference (102100) for hosted services? (esri.c...

Using spatial references—ArcGIS REST APIs | ArcGIS Developers

Solved: SketchViewModel New Point Bug? - Esri Community

I was hoping to use coordinates using  standard latitude/longitude with 4326 where we would store coordinates outside of arcis.

From Spatial references | Documentation | ArcGIS Developers

4326 - GPS

4326 is the most common spatial reference for storing a referencing data across the entire world. It serves as the default for both the PostGIS spatial database and the GeoJSON standard. It is also used by default in most web mapping libraries.

Because of its use in GPS, 4326 is generally assumed to be the spatial reference when talking about "latitude" or "longitude".

3857 - Web Mercator

The Web Mercator coordinate reference system is the default in most web mapping libraries. Web Mercator preserves a consistent direction and North is always "up" on a Web Mercator map. Angles are also depicted accurately, so a 90-degree turn on a Web Mercator map will look like a true right angle. That said, the projection can distort both shape and size as distance from the equator increases.

Web Mercator is ideal for generating map tiles, since it projects the world onto a square tile that can be subdivided evenly across all zoom levels. For example, one tile at zoom level 0, four tiles at zoom level 1, and so on.

 

 

0 Kudos
ReneRubalcava
Frequent Contributor

Interesting, I didn't know that sketch would default to the map SR, but that makes sense I think. Just something to keep in mind when using geometry engine methods. I should be a good practice anyway since it does require all geometries to be the same spatial reference.

 

You could use the webMercatorUtils module to help convert geographic and webMercator pretty easily.

graphicsLayer.graphics.map(function (gra) {
    if (gra.geometry.spatialReference.wkid === 4326) {
        updatedGeometry.push(
            webMercatorUtils.geographicToWebMercator(
                gra.geometry.clone()
            )
        );
    }
    else {
        updatedGeometry.push(gra.geometry.clone());
    }
});
const joinedPolygon = geometryEngine.union(updatedGeometry);

That should do it.

https://codepen.io/odoe/pen/oNdPKNv?editors=0010