AnsweredAssumed Answered

How to convert a KmlDataset to KmlGeometry in order to apply GeometryEngine.NormalizeCentralMeridian and avoid distortion of a KML line graphic across the International Date Line (IDL).

Question asked by cjmtk_rick on Jun 19, 2019
Latest reply on Jun 19, 2019 by MBranscomb-esristaff

ArcGIS Runtime .NET 100.5:

I'm reading KML file and displaying the features on a map where the features are crossing the International Date Line and displaying a distortion (gap) going East to West.  I would like to use GeometryEngine.NormalizeCentralMeridian to normalize and eliminate distortion but do not know how to convert KML to geometries in order to apply the normalization.

 

Current Map Showing Distortion around IDL:

Current Map Showing Distortion around IDL

 

Same Map at Larger Extent:Current Map Showing Distortion around IDL at Larger Extent

 

What Map Should Look Like (Same KML in Google Earth):

What Map Should Look Like (Same KML in Google Earth):

 

KML File:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>180.kml</name>
<StyleMap id="msn_ylw-pushpin">
<Pair>
<key>normal</key>
<styleUrl>#sn_ylw-pushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_ylw-pushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="sn_ylw-pushpin">
<IconStyle>
<scale>1.1</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
</IconStyle>
<BalloonStyle>
</BalloonStyle>

</Style>
<Style id="sh_ylw-pushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
</IconStyle>
<BalloonStyle>
</BalloonStyle>
</Style>
<Folder>
<name>Stuff</name>
<open>1</open>
<Placemark>
<name>W through to E</name>
<styleUrl>#msn_ylw-pushpin</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>
-170.7165137933902,-12.72314064901935,0 -171.9436023467433,-12.51404613754339,0 -174.374588380541,-12.08339726717282,0 -175.9101196672809,-11.93816028528418,0 -177.2904287103858,-12.03766229098746,0 -177.3590937452626,-12.03461491513858,0 -178.6808846722653,-11.83780449826281,0 179.9359714705273,-11.83309460788605,0 178.9859656598996,-11.80199020678887,0 178.3767822713525,-11.83359709194731,0 176.8696276607265,-11.78398138903656,0 175.3854454699889,-11.70542531712627,0
</coordinates>
</LineString>
</Placemark>
<Placemark>
<name>E through to W</name>
<styleUrl>#msn_ylw-pushpin</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>
174.6270601842228,-4.501663671814118,0 176.1590448925327,-4.422251738284245,0 178.100574912019,-4.578527601047368,0 179.4766586699339,-4.568867585220675,0 180.8258819908834,-4.923148462603215,0 182.1637144504404,-4.715933508068548,0 184.263983662899,-4.808692819330977,0 185.7987594538308,-4.853789918218695,0 187.4844411973654,-4.90505862616004,0 187.5077006237957,-4.905276871333621,0 188.8130455251189,-5.006035746717257,0 190.0134699143226,-5.166620198566529,0
</coordinates>
</LineString>
</Placemark>
</Folder>
</Document>
</kml>

 

 

.NET Code:

 

Solution Explorer:

Code Solution Explorer

 

MainWindow.xaml:

<Window x:Class="ArcGISDevLabsApp_NET01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
xmlns:local="clr-namespace:ArcGISDevLabsApp_NET01"
mc:Ignorable="d"
Title="MainWindow" Height="525" Width="790">
<Window.Resources>
<local:MapViewModel x:Key="MapViewModel" />
</Window.Resources>
<Grid>
<esri:MapView Map="{Binding Map, Source={StaticResource MapViewModel}}" />
</Grid>
</Window>

 

MapViewModel.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Location;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Security;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.Tasks;
using Esri.ArcGISRuntime.UI;
using Esri.ArcGISRuntime.Ogc;
using Esri.ArcGISRuntime.UI.Controls;

namespace ArcGISDevLabsApp_NET01
{
/// <summary>
/// Provides map data to an application
/// </summary>
public class MapViewModel : INotifyPropertyChanged
{
public MapViewModel()
{
// calling CreateNewMap function
CreateNewMap();
}

private Map _map = new Map(Basemap.CreateStreets());

/// <summary>
/// Gets or sets the map
/// </summary>
public Map Map
{
get { return _map; }
set { _map = value; OnPropertyChanged(); }
}

/// <summary>
/// Raises the <see cref="MapViewModel.PropertyChanged" /> event
/// </summary>
/// <param name="propertyName">The name of the property that has changed</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var propertyChangedHandler = PropertyChanged;
if (propertyChangedHandler != null)
propertyChangedHandler(this, new PropertyChangedEventArgs(propertyName));
}

public event PropertyChangedEventHandler PropertyChanged;

private async void CreateNewMap()
{
// create a new map with an imagery basemap
Map newMap = new Map(Basemap.CreateImageryWithLabels());

// create KML Data Source and Operational Layer
Uri kmlFilePathUri = new Uri("K:/2 - Backups/GIS/CJMTK/HelpDesk Tickets/HD1057/HD1057_LineCrossingIDL/HD1057_LineCrossingIDL/kml/180.kml");

KmlDataset kmlDataSource = new KmlDataset(kmlFilePathUri);
KmlLayer kmlOperationalLayer = new KmlLayer(kmlDataSource);

// tried to use GeometryEngine.NormalizeCentralMeridian
//KmlDataset kmLGeoNormal = (KmlDataset) GeometryEngine.NormalizeCentralMeridian(KmlGeometry.Geometry(kmlDataSource));

// load the layer asynchronously and await it's completion
await kmlOperationalLayer.LoadAsync();

// add the layer to the map's collection of operational layers
newMap.OperationalLayers.Add(kmlOperationalLayer);

newMap.InitialViewpoint = new Viewpoint(kmlOperationalLayer.FullExtent);

// explore the KML content tree
ExploreKml(kmlDataSource.RootNodes);

// set the MapViewModel.Map property with the new map
Map = newMap;

// get the current map extent
//var extent = MyMapView.GetCurrentViewpoint(ViewpointType.BoundingGeometry).TargetGeometry;

}

private void ExploreKml(IEnumerable<KmlNode> nodes)
{

// Iterate all nodes.
foreach (KmlNode node in nodes)
{
// Toggle node visibility for any screen overlays found.
if (node.GetType() == typeof(KmlScreenOverlay))
{
node.IsVisible = !node.IsVisible;
}

// See if this node has child nodes; store them in a list.
List<KmlNode> children = new List<KmlNode>();
if (node is KmlContainer containerNode)
{
children.AddRange(containerNode.ChildNodes);
}
if (node is KmlNetworkLink networkLinkNode)
{
children.AddRange(networkLinkNode.ChildNodes);
}

// Recursively call this function with the child node list.
ExploreKml(children);
}
}
}
}

 

Thanks in advance

Outcomes