Select to view content in your preferred language

Geometry union at antimeridian

358
1
Jump to solution
11-23-2023 02:10 AM
ChristopheS
New Contributor II

Hi all,

I have to union many extents all over the globe, including antimeridian (180th meridian) on both sides and crossing.

I cannot get to have extents on both sides of 180th meridian to merge :

https://codepen.io/Chris_Siveco/pen/JjxvpvJ?editors=1000

Only one side extents are merging. The one that is crossing 180th meridian is merging ok, and the one on the left also. But not the one on the right.

Is there a way to achieve this ?

Regards

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
JoelBennett
MVP Regular Contributor

It is because on line 73. ext1 and ext3 do not intersect numerically speaking.  That is, before the call to union, here is the state of things:

ext1: (150, 50) -> (210, 80)

ext3: (-175, 55) -> (-120, 65)

As you can see, if you put this on a simple cartesian plane, they won't intersect.   You can add some functionality to adjust the geometries one rotation around the world to the east or west to get what you're looking for like so:

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>
      Intro to graphics | Sample | ArcGIS Maps SDK for JavaScript 4.28
    </title>
    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.28/esri/themes/light/main.css"
    />
    <script src="https://js.arcgis.com/4.28/"></script>
    <style>
      html,
      body,
      #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
      }
    </style>
    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/Graphic",
        "esri/geometry/Polygon",
        "esri/geometry/Extent",
        "esri/geometry/geometryEngine"
      ], (Map, MapView, Graphic, Polygon, Extent, GeometryEngine) => {
        const map = new Map({
          basemap: "hybrid"
        });

        const view = new MapView({
          center: [-180, 60],
          container: "viewDiv",
          map: map,
          zoom: 3
        });

        var ext1 = new Extent({
          xmin: 170.0,
          ymin: 56.0,
          xmax: -170.0 + 360,
          ymax: 70.0
        });

        drawAThing(ext1);
        //-----------------
        var ext2 = new Extent({
          xmin: 150.0,
          ymin: 50.0,
          xmax: -150.0 + 360,
          ymax: 80.0
        });
        drawAThing(ext2);

        var ext3 = new Extent({
          xmin: -175.0,
          ymin: 55.0,
          xmax: -120.0,
          ymax: 65.0
        });
        drawAThing(ext3);

        var ext4 = new Extent({
          xmin: 140.0,
          ymin: 54.0,
          xmax: 175,
          ymax: 57.0
        });
        drawAThing(ext4);

        view.on("click", function (evt) {
          //-------------------
          alert("union ready ?");
          view.graphics.removeAll();
          var geom = unionGeometries(ext1, ext2);
          geom = unionGeometries(geom, ext3);
          geom = unionGeometries(geom, ext4);

          drawAThing(geom);
          //-------------------
        });

        function drawAThing(geo) {
          // Create a symbol for rendering the graphic
          var 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
            }
          };

          // Add the geometry and symbol to a new graphic
          var graphic = new Graphic({
            geometry: geo,
            symbol: fillSymbol
          });
          view.graphics.addMany([graphic]);
        }

        function unionGeometries(geometry, extent) {
          if (!extent.intersects(geometry)) {
            var extent2 = extent.clone();
            extent2.xmax += 360;
            extent2.xmin += 360;

            if (extent2.intersects(geometry))
              return GeometryEngine.union([geometry, extent2]);
            else {
              extent2.xmin -= 720;
              extent2.xmax -= 720;

              if (extent2.intersects(geometry))
                return GeometryEngine.union([geometry, extent2]);
            }
          }

          return GeometryEngine.union([geometry, extent]);
        }
      });
    </script>
  </head>
  <body>
    <div id="viewDiv"></div>
  </body>
</html>

 

Basically, the unionGeometries function was added, and the "extN" objects were created as full-on Extent objects to make this work.  Note this only works if geometries are offset by no more than one rotation.

View solution in original post

0 Kudos
1 Reply
JoelBennett
MVP Regular Contributor

It is because on line 73. ext1 and ext3 do not intersect numerically speaking.  That is, before the call to union, here is the state of things:

ext1: (150, 50) -> (210, 80)

ext3: (-175, 55) -> (-120, 65)

As you can see, if you put this on a simple cartesian plane, they won't intersect.   You can add some functionality to adjust the geometries one rotation around the world to the east or west to get what you're looking for like so:

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>
      Intro to graphics | Sample | ArcGIS Maps SDK for JavaScript 4.28
    </title>
    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.28/esri/themes/light/main.css"
    />
    <script src="https://js.arcgis.com/4.28/"></script>
    <style>
      html,
      body,
      #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
      }
    </style>
    <script>
      require([
        "esri/Map",
        "esri/views/MapView",
        "esri/Graphic",
        "esri/geometry/Polygon",
        "esri/geometry/Extent",
        "esri/geometry/geometryEngine"
      ], (Map, MapView, Graphic, Polygon, Extent, GeometryEngine) => {
        const map = new Map({
          basemap: "hybrid"
        });

        const view = new MapView({
          center: [-180, 60],
          container: "viewDiv",
          map: map,
          zoom: 3
        });

        var ext1 = new Extent({
          xmin: 170.0,
          ymin: 56.0,
          xmax: -170.0 + 360,
          ymax: 70.0
        });

        drawAThing(ext1);
        //-----------------
        var ext2 = new Extent({
          xmin: 150.0,
          ymin: 50.0,
          xmax: -150.0 + 360,
          ymax: 80.0
        });
        drawAThing(ext2);

        var ext3 = new Extent({
          xmin: -175.0,
          ymin: 55.0,
          xmax: -120.0,
          ymax: 65.0
        });
        drawAThing(ext3);

        var ext4 = new Extent({
          xmin: 140.0,
          ymin: 54.0,
          xmax: 175,
          ymax: 57.0
        });
        drawAThing(ext4);

        view.on("click", function (evt) {
          //-------------------
          alert("union ready ?");
          view.graphics.removeAll();
          var geom = unionGeometries(ext1, ext2);
          geom = unionGeometries(geom, ext3);
          geom = unionGeometries(geom, ext4);

          drawAThing(geom);
          //-------------------
        });

        function drawAThing(geo) {
          // Create a symbol for rendering the graphic
          var 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
            }
          };

          // Add the geometry and symbol to a new graphic
          var graphic = new Graphic({
            geometry: geo,
            symbol: fillSymbol
          });
          view.graphics.addMany([graphic]);
        }

        function unionGeometries(geometry, extent) {
          if (!extent.intersects(geometry)) {
            var extent2 = extent.clone();
            extent2.xmax += 360;
            extent2.xmin += 360;

            if (extent2.intersects(geometry))
              return GeometryEngine.union([geometry, extent2]);
            else {
              extent2.xmin -= 720;
              extent2.xmax -= 720;

              if (extent2.intersects(geometry))
                return GeometryEngine.union([geometry, extent2]);
            }
          }

          return GeometryEngine.union([geometry, extent]);
        }
      });
    </script>
  </head>
  <body>
    <div id="viewDiv"></div>
  </body>
</html>

 

Basically, the unionGeometries function was added, and the "extN" objects were created as full-on Extent objects to make this work.  Note this only works if geometries are offset by no more than one rotation.

0 Kudos