Select to view content in your preferred language

Help using NormalizeCentralMeridian (or spanning the antimeridian)

708
7
08-18-2022 02:53 PM
MattH
by
Emerging Contributor

 

I'm trying to display some geometries that span the antimeridian in a WPF app. Ultimately, the geometries will be pulled from a map service, but I have a simple rectangle polygon that I'm testing with. This seems to be a pretty common issue, and I've read through all of the examples that I could find. Most of the examples I've seen are using the JS API, and I'm having trouble getting the same results in the .Net SDK.

The working JS example here (https://codepen.io/andygup/pen/QeyerP) appears to call geodesicUtils.geodesicDensify() to resolve the issue. I didn't see any reference to NormalizeCentralMeridian() in this example, like I've seen in other examples. Porting that to .Net didn't work, though.

This example converts to WebMercator then uses NormalizeCentralMeridian() (https://jsbin.com/xegicih/2/edit?html,output), but again is in JS.

This post (https://community.esri.com/t5/arcgis-runtime-sdk-for-net-questions/polyline-not-crossing-internation...) is in .Net and suggested adding 360 to the negative western longitudes. Doing this will put a box in the correct place, but with an extra line at 180 degrees. The JS examples don't have this issue.

Can the .Net SDK display a geometry the same way as the JS examples?

Here is a sample of the code I've been working with:

 

private void addGraphic(Geometry.Geometry geometry)
  {
    SimpleLineSymbol outlineSymbol = 
      new SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.Green, 2);
    Geometry.PointCollection points = 
      new Geometry.PointCollection(SpatialReferences.Wgs84)
      {
        new MapPoint(-178, -5),
        new MapPoint(-178, 5),
        new MapPoint(178, 5),
        new MapPoint(178, -5)
      };

      Polygon polygon = new Polygon(points);
      Geometry.Geometry g = GeometryEngine.NormalizeCentralMeridian(polygon);
      esriMap.SetViewpointGeometryAsync(g, 50);

      Graphic graphic = new Graphic(g, outlineSymbol);
      _overlay.Graphics.Add(graphic);
 }

 

 

Here's how I tried adding 360 to the longitudes. That got me a bit closer, but still not what I was looking for:

private void addGraphic(Geometry.Geometry geometry)
  {
    SimpleLineSymbol outlineSymbol = 
      new SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.Green, 2);
    Geometry.PointCollection points = 
      new Geometry.PointCollection(SpatialReferences.Wgs84)
      {
        new MapPoint(-178, -5),
        new MapPoint(-178, 5),
        new MapPoint(178, 5),
        new MapPoint(178, -5)
      };

      Polygon polygon = new Polygon(points);
      Polygon newPoly;
      Geometry.PointCollection newPoints = 
        new Geometry.PointCollection(SpatialReferences.Wgs84);
      
      foreach (ReadOnlyPart gp in polygon.Parts)
      {
        foreach (MapPoint pt in gp.Points)
          {
            double x = pt.X;
            if(x<0){ x += 360; }
            newPoints.Add(new MapPoint(x, pt.Y));
          } 
       }
            newPoly = new Polygon(newPoints);
            //this puts a box in the correct place, 
            //but has an extra vertical line at 180
            esriMap.SetViewpointGeometryAsync(newPoly.Extent, 50);
            Graphic graphic = new Graphic(newPoly, outlineSymbol);
            _overlay.Graphics.Add(graphic);
}

 

0 Kudos
7 Replies
MarcHillman
Regular Contributor

I have EXACTLY the same problem (although I'm coding in VB.NET). Did you ever get an answer? When I apply NormalizeCentralMeridian to my polygon, nothing happens. I don't get two polygons returned.

0 Kudos
MattH
by
Emerging Contributor

Would you believe that I can't remember? I usually post updates if I find answers, so it's possible that I just had to move on.

I'll dig through my old project this afternoon, and see if I can find it.

 

0 Kudos
MarcHillman
Regular Contributor

Thanks - really appreciate that. I'm having 2 difficult problems, and NormalizeCentralMeridian seems to be doing nothing.

1. I have a simple rectangular box spanning the antimeridian. Can't split it in two with NormalizeCentralMeridian.

2. And then I have the mother of all meridian problems. I have a polygon enclosing Antarctica. Need to split that in two as well.

0 Kudos
MarcHillman
Regular Contributor

This problem is REALLY bugging me. I can't see what I'm doing wrong. I have written a minimal code example. It creates a linestring (using a polylinebuilder) that crosses the Antimeridian (it's a box around Fiji). It is 1 part with 5 points. I then apply NormalizeCentralMeridian to it to produce "normal".  normal is identical to linestring. 

        Dim plb As New PolylineBuilder(SpatialReferences.Wgs84)       ' drawn bounding box
        plb.AddPoint(New MapPoint(177.17, -19.25))       ' first point
        plb.AddPoint(New MapPoint(-179.65, -19.25))
        plb.AddPoint(New MapPoint(-179.65, -15.67))
        plb.AddPoint(New MapPoint(177.17, -15.67))
        plb.AddPoint(New MapPoint(177.17, -19.25))       ' last point

        Dim linestring = plb.ToGeometry
        AppendText(TextBox1, $"Before {linestring.ToString}{vbCrLf}")
        Dim normal = linestring.NormalizeCentralMeridian
        AppendText(TextBox1, $"After {normal.ToString}{vbCrLf}")

 

Output of test program shows NormalizeCentralMeridian is having no effect

Before Polyline[Parts=1, Points=5, Wkid=4326]
After Polyline[Parts=1, Points=5, Wkid=4326]

 

 

0 Kudos
MarcHillman
Regular Contributor

I have a fix, but I'm not why it's valid. If I have a polygon that spans the antimeridian, I simply add 360 to any -ve X value. NormalizeCentralMeridian then does what you'd expect. I'm not sure I fully understand why, but at least I have a solution.

0 Kudos
MattH
by
Emerging Contributor

Do you have a snippet of code that you could post? That sounds like what I was trying, but I'm still getting an additional line along the antimeridian.

snip.PNG

 

0 Kudos
MarcHillman
Regular Contributor

Below is the code I currently use. It works in almost all cases.

1. If you have a polygon spanning the antimeridian, it produces the desired result - the polygon is split into pieces. I have a box around Fiji, and I can render it no problem.

2. I also have a polygon that encircles the South Pole. This code is splitting the polygon into 3 pieces, but it is not rendering properly. All the signs on the X's in the 3 pieces are the same, so they look sensible.

The code works by testing for antimeridian crossing (CrossesAntiMeridian) and if it does then

1. deconstruct the polygon into a polygonbuilder so we can modify it

2. Add 360 to every X that is less than 0 in every part

3. Rebuild the polygon from the polygonbuilder

4. Use NormalizeCentralMeridian

5. Use Simplify as results of NormalizeCentralMeridian seem to leave polygons that are not Simple

As I said, it seems to be working for 'standard' cases where a pole is not involved, but it seems to breakdown at the poles.

    Function CrossesAntiMeridian(g As Geometry) As Boolean
        ' returns true if geometry crosses anti-meridian
        Return Math.Abs(g.Extent.XMax - g.Extent.XMin) > 300
    End Function
    Function NormalizeCentralMeridian(PolyBox As Polygon) As Polygon
        ' Normalize a polygon if it crosses the anti-meridian
        If CrossesAntiMeridian(PolyBox) Then       '  crosses anti-meridian
            With PolyBox.Extent
                Dim plb As New PolygonBuilder(PolyBox)        ' deconstruct polygon to polygon builder
                ' Add 360 to any negative X value in any part
                For prt = 0 To plb.Parts.Count - 1
                    For pnt = 0 To plb.Parts(prt).Points.Count - 1
                        If plb.Parts(prt).Points(pnt).X < 0 Then
                            plb.Parts(prt).SetPoint(pnt, New MapPoint(plb.Parts(prt).Points(pnt).X + 360, plb.Parts(prt).Points(pnt).Y, plb.SpatialReference))   ' add 360 to X
                        End If
                    Next
                Next
                Dim poly = plb.ToGeometry               ' reconstruct geometry
                Dim Normalized As Polygon = poly.NormalizeCentralMeridian   ' split geometry into parts that don't cross the antimeridian
                Normalized = Normalized.Simplify        ' Normalizing seems to produce a non simple result
                Return Normalized
            End With
        Else
            Return PolyBox       ' doesn't cross anti-meridian
        End If
    End Function

Below 

0 Kudos