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).

889
3
06-19-2019 11:55 AM
RickSanders1
New Contributor

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

0 Kudos
3 Replies
MichaelBranscomb
Esri Frequent Contributor

Hi,

It's great to get some feedback on our KML support so far... 

After talking to the KML team, we're going to try to reproduce this on the current development build if still reproducible we can investigate this as a possible bug.

But in the meantime, to answer your question above you can get the KmlPlacemark from the ChildNodes collection which you are already traversing in your code snippet above, and then use the:

KmlPlacemark.Geometries Property https://developers.arcgis.com/net/latest/wpf/api-reference/html/P_Esri_ArcGISRuntime_Ogc_KmlPlacemar...

...and...

KmlGeometry.Geometry Property https://developers.arcgis.com/net/latest/wpf/api-reference/html/P_Esri_ArcGISRuntime_Ogc_KmlGeometry...

... to get the specific Polyline geometry then pass that into the GeometryEngine. 

Note currently you won't be able to set that geometry back on the KmlPlacemark (we're currently working on supporting editing of KML content for a future release).

Cheers

Mike

0 Kudos
Jimmy
by
New Contributor

Hello.  I see that the capability to modify existing KML content has been added according to release 100.6
https://developers.arcgis.com/net/reference/release-notes/prior-releases/100.6/#create-and-modify-km...

However, I do not see this capability for the KmlPlacemark class.  It seems that all properties are read only and there exists no methods for the class; other than the one constructor, which seems to only take a single geometry.

https://developers.arcgis.com/net/api-reference/api/netwin/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Ogc...

Have I overlooked something?

0 Kudos
MatveiStefarov
Esri Contributor

@Jimmy To modify an existing KmlPlacemark, use the collection returned by its Geometries property.  Prior to 100.6 this was a read-only collection, but now you can add/remove/replace KmlGeometry objects.

0 Kudos