private void ProcessMuiltipleGeometry(MultipleGeometry f) { f.Geometry.ToList().ForEach(g => { if (g is Polygon) { ProcessPolygonGeometry((Polygon)g); } if (g is LineString) { ProcessLineStringGeometry((LineString)g); } if (g is Point) { ProcessPointGeometry((Point)g); } if (g is LinearRing) { ProcessLinearRingGeometry((LinearRing)g); } if (g is MultipleGeometry) { ProcessMuiltipleGeometry((MultipleGeometry)g); } }); }
private static Placemark CreateFresnelPlacemark(IDirectionVector directionVector, string name, double frequency, Uri styleReferenceUri, string description) { var fresnelCreator = new FresnelPolygonsCreator(directionVector); IReadOnlyList <IReadOnlyList <ILocation> > fresnelRingLocations = fresnelCreator.CalculateRingLocations(frequency, 20); var linearRings = CreateKmlRings(fresnelRingLocations); var multiGeometry = new MultipleGeometry(); foreach (var linearRing in linearRings) { multiGeometry.AddGeometry(new Polygon { AltitudeMode = AltitudeMode.Absolute, OuterBoundary = new OuterBoundary { LinearRing = linearRing } }); } var linePlacemark = new Placemark { Name = name, StyleUrl = styleReferenceUri, Geometry = multiGeometry, Snippet = new Snippet(), Description = new Description { Text = description } }; return(linePlacemark); }
/// <summary> /// Loads track from given MultiGeometry object, containing multiple geometry objects. /// </summary> /// <param name="multiGeometry">multi geometry object</param> /// <returns>loaded track </returns> private Track LoadTrackFromMultipleGeometry(MultipleGeometry multiGeometry) { var track = CreateTrackFromPlacemark(multiGeometry.Parent as Placemark); foreach (var geometry in multiGeometry.Geometry) { if (geometry is LineString lineString) { foreach (var vector in lineString.Coordinates) { TrackPoint trackPoint = GetTrackPointFromVector(vector); track.TrackPoints.Add(trackPoint); } } else if (geometry is SharpKml.Dom.GX.Track gxtrack) { AddGxTrackPointsToTrack(track, gxtrack); } else if (geometry is MultipleTrack multiTrack) { foreach (var gxtrack2 in multiTrack.Tracks) { AddGxTrackPointsToTrack(track, gxtrack2); } } else { Debug.WriteLine($"unhandled geometry type: {geometry.GetType().FullName}"); } } return(track); }
public static Placemark ClonePlacemark(this Placemark placemark) { var clonedPlacemark = new Placemark(); clonedPlacemark.Id = placemark.Id; clonedPlacemark.Name = placemark.Name; var clonedGeometry = new MultipleGeometry(); if (placemark.Geometry is MultipleGeometry) { foreach (var geometry in ((MultipleGeometry)placemark.Geometry).Geometry) { clonedGeometry.AddGeometry(new Polygon { OuterBoundary = new OuterBoundary { LinearRing = new LinearRing { Coordinates = new CoordinateCollection(((Polygon)geometry).OuterBoundary.LinearRing.Coordinates) } } }); } } else if (placemark.Geometry is Polygon geometry) { clonedGeometry.AddGeometry(new Polygon { OuterBoundary = new OuterBoundary { LinearRing = new LinearRing { Coordinates = new CoordinateCollection(((Polygon)geometry).OuterBoundary.LinearRing.Coordinates) } } }); } clonedPlacemark.Geometry = clonedGeometry; return(clonedPlacemark); }
private MultipleGeometry GetKml(TGeometryCollection geom) { var kml = new MultipleGeometry(); foreach (var sub in geom.Geometries) { kml.AddGeometry(GetKmlGeometry(sub)); } return(kml); }
/// <summary> /// Expands the BoundingBox to include the specified Geometry. /// </summary> /// <param name="geometry">The Geometry to expand the box to include.</param> /// <param name="box">The BoundingBox to expand.</param> internal static void ExpandBox(Geometry geometry, BoundingBox box) { MultipleGeometry multiple = geometry as MultipleGeometry; if (multiple != null) { foreach (var geo in multiple.Geometry) { ExpandBox(geo, box); } } else { // Try it as an IBoundsInformation, doesn't matter if the conversion fails ExpandBox(geometry as IBoundsInformation, box); } }
/** * @param args */ public static void createKml(GpxFileSetModel fileSetModel, KmlOptions kmlOptions) { // Generate the KML Kml kml = new Kml(); // Create the Document for this file Document document = new Document() { Name = "GPXViewer", Open = true }; kml.Feature = document; // Make the Styles for this Document // Trk Colors switch (kmlOptions.TrkColorMode) { case KmlColorMode.COLOR: createTrkColorColors(kmlOptions); break; case KmlColorMode.COLORSET: createTrkColorSetColors(kmlOptions); break; case KmlColorMode.RAINBOW: createTrkRainbowColors(kmlOptions, fileSetModel); break; } // Rte Colors switch (kmlOptions.RteColorMode) { case KmlColorMode.COLOR: createRteColorColors(kmlOptions); break; case KmlColorMode.COLORSET: createRteColorSetColors(kmlOptions); break; case KmlColorMode.RAINBOW: createRteRainbowColors(kmlOptions, fileSetModel); break; } // Wpt Colors switch (kmlOptions.WptColorMode) { case KmlColorMode.COLOR: createWptColorColors(kmlOptions); break; case KmlColorMode.COLORSET: createWptColorSetColors(kmlOptions); break; case KmlColorMode.RAINBOW: createWptRainbowColors(kmlOptions, fileSetModel); break; } // Create the color styles int nTrkColors = trkColors.Length; foreach (String color in trkColors) { document.AddStyle(new Style() { Id = "trk" + color, Line = new LineStyle() { Color = Color32.Parse(color), Width = kmlOptions.TrkLineWidth, }, Icon = new SharpKml.Dom.IconStyle() { Color = Color32.Parse(color), ColorMode = ColorMode.Normal, Scale = kmlOptions.IconScale, Icon = new IconStyle.IconLink(new Uri(kmlOptions.TrkIconUrl)) } }); } int nRteColors = rteColors.Length; foreach (String color in rteColors) { document.AddStyle(new Style() { Id = "rte" + color, Line = new LineStyle() { Color = Color32.Parse(color), Width = kmlOptions.TrkLineWidth, }, Icon = new SharpKml.Dom.IconStyle() { Color = Color32.Parse(color), ColorMode = ColorMode.Normal, Scale = kmlOptions.IconScale, Icon = new IconStyle.IconLink(new Uri(kmlOptions.RteIconUrl)) } }); } int nWptColors = wptColors.Length; foreach (String color in wptColors) { document.AddStyle(new Style() { Id = "wpt" + color, Line = new LineStyle() { Color = Color32.Parse(color), Width = kmlOptions.TrkLineWidth, }, Icon = new SharpKml.Dom.IconStyle() { Color = Color32.Parse(color), ColorMode = ColorMode.Normal, Scale = kmlOptions.IconScale, Icon = new IconStyle.IconLink(new Uri(kmlOptions.WptIconUrl)) } }); } // Loop over GPX files int nTrack = 0; int nRoute = 0; int nWaypoint = 0; List <GpxWaypointModel> waypointModels; wptType waypoint; Folder fileFolder; Folder waypointFolder; Folder trackFolder; Folder routeFolder; Folder folder; MultipleGeometry mg; Placemark placemark; Placemark trackPlacemark; MultipleTrack mt; Track track; LineString ls = null; double lat, lon, alt; String fileName; DateTime? when; List <GpxFileModel> fileModels = fileSetModel.Files; foreach (GpxFileModel fileModel in fileModels) { if (!fileModel.Checked) { continue; } fileName = fileModel.FileName; if (!File.Exists(fileName)) { Utils.errMsg("File does not exist: " + fileName); continue; } fileFolder = new Folder() { //Name = fileName, Name = Path.GetFileName(fileName), Open = false }; document.AddFeature(fileFolder); // Loop over waypoints waypointFolder = new Folder() { Name = "Waypoints", Open = false }; waypointModels = fileModel.Waypoints; if (waypointModels.Count > 0) { fileFolder.AddFeature(waypointFolder); } foreach (GpxWaypointModel waypointModel in waypointModels) { if (!waypointModel.Checked) { continue; } waypoint = waypointModel.Waypoint; lat = decimal.ToDouble(waypoint.lat); lon = decimal.ToDouble(waypoint.lon); if (waypoint.ele.HasValue) { alt = decimal.ToDouble((decimal)waypoint.ele); } else { alt = 0; } // Make a Placemark waypointFolder.AddFeature(new Placemark() { Name = waypointModel.Label, StyleUrl = new Uri("#wpt" + wptColors[nWaypoint % nWptColors], UriKind.Relative), Geometry = new SharpKml.Dom.Point() { Coordinate = new Vector(lat, lon, alt) } }); nWaypoint++; } // Loop over tracks trackFolder = new Folder() { Name = "Tracks", Open = false }; List <GpxTrackModel> trackModels; trackModels = fileModel.Tracks; bool useTrackIconFirst; bool useTrackTrackFirst; if (trackModels.Count > 0) { fileFolder.AddFeature(trackFolder); } trackPlacemark = null; mt = null; track = null; foreach (GpxTrackModel trackModel in trackModels) { if (!trackModel.Checked) { continue; } // Make a Placemark for the track if (kmlOptions.UseTrkTrack) { trackPlacemark = new Placemark() { Name = trackModel.Label, Open = false, StyleUrl = new Uri("#trk" + trkColors[nTrack % nTrkColors], UriKind.Relative), Snippet = new Snippet() }; trackFolder.AddFeature(trackPlacemark); mt = new MultipleTrack(); trackPlacemark.Geometry = mt; } // Make a Placemark with MultiGeometry placemark = new Placemark() { Name = trackModel.Label + " Lines", Visibility = kmlOptions.UseTrkTrack && !kmlOptions.UseTrkLines ? false:true, StyleUrl = new Uri("#trk" + trkColors[nTrack % nTrkColors], UriKind.Relative) }; trackFolder.AddFeature(placemark); // Need MultiGeometry to handle non-connected segments mg = new MultipleGeometry(); placemark.Geometry = mg; useTrackIconFirst = kmlOptions.UseTrkIcon ? true : false; useTrackTrackFirst = kmlOptions.UseTrkTrack ? true : false; foreach (trksegType trackSegment in trackModel.Track.trkseg) { // Add a LineString to the MultiGeometry ls = new LineString() { Extrude = false, Tessellate = true, Coordinates = new CoordinateCollection() }; mg.AddGeometry(ls); if (mt != null) { track = new Track(); mt.AddTrack(track); } foreach (wptType trackPoint in trackSegment.trkpt) { lat = decimal.ToDouble(trackPoint.lat); lon = decimal.ToDouble(trackPoint.lon); if (trackPoint.ele.HasValue) { alt = decimal.ToDouble((decimal)trackPoint.ele); } else { alt = 0; } if (trackPoint.time != null) { when = (DateTime)trackPoint.time; } else { when = null; } // Add coordinates to the LineString ls.Coordinates.Add(new Vector(lat, lon, alt)); if (track != null) { // Add coordinates (and when to the track ?) track.AddCoordinate(new Vector(lat, lon, alt)); if (when != null) { track.AddWhen((DateTime)when); } } // Make a Placemark with an icon at the first point // on the track if (useTrackIconFirst) { useTrackIconFirst = false; trackFolder.AddFeature(new Placemark() { Name = trackModel.Label, // The Track has its own icon Visibility = !kmlOptions.UseTrkTrack, StyleUrl = new Uri("#trk" + trkColors[nTrack % nTrkColors], UriKind.Relative), Geometry = new SharpKml.Dom.Point() { Coordinate = new Vector(lat, lon) } }); } if (trackPlacemark != null && useTrackTrackFirst) { useTrackTrackFirst = false; trackPlacemark.Description = new Description() { Text = trackPoint.time.ToString() }; } } } nTrack++; } // Loop over routes folder = new Folder() { Name = "Routes", Open = false }; List <GpxRouteModel> routeModels; routeModels = fileModel.Routes; bool useRteIconFirst; if (routeModels.Count > 0) { fileFolder.AddFeature(folder); } foreach (GpxRouteModel routeModel in routeModels) { if (!routeModel.Checked) { continue; } routeFolder = (new Folder() { Name = routeModel.Label, Open = false, }); folder.AddFeature(routeFolder); // Make a Placemark with MultiGeometry placemark = new Placemark() { Name = routeModel.Label + " Lines", StyleUrl = new Uri("#rte" + rteColors[nRoute % nRteColors], UriKind.Relative), }; routeFolder.AddFeature(placemark); // Need MultiGeometry to handle non-connected segments mg = new MultipleGeometry(); placemark.Geometry = mg; useRteIconFirst = kmlOptions.UseRteIcon ? true : false; // Add a LineString to the MultiGeometry ls = new LineString() { Extrude = false, Tessellate = true }; mg.AddGeometry(ls); foreach (wptType rtePoint in routeModel.Route.rtept) { lat = decimal.ToDouble(rtePoint.lat); lon = decimal.ToDouble(rtePoint.lon); if (rtePoint.ele.HasValue) { alt = decimal.ToDouble((decimal)rtePoint.ele); } else { alt = 0; } if (useRteIconFirst) { // Make a Placemark with an Icon at the first point // on the route useRteIconFirst = false; routeFolder.AddFeature(new Placemark() { Name = routeModel.Label, StyleUrl = new Uri("#rte" + rteColors[nRoute % nRteColors], UriKind.Relative), Geometry = new SharpKml.Dom.Point() { Coordinate = new Vector(lat, lon, alt) } }); } // Make a Placemark routeFolder.AddFeature(new Placemark() { Name = rtePoint.name, StyleUrl = new Uri("#rte" + rteColors[nRoute % nRteColors], UriKind.Relative), Geometry = new SharpKml.Dom.Point() { Coordinate = new Vector(lat, lon, alt) } }); } } nRoute++; } // Create the file String kmlFileName = kmlOptions.KmlFileName; string outFile = Environment.ExpandEnvironmentVariables(kmlFileName); if (kmlOptions.PromptToOverwrite && File.Exists(outFile)) { DialogResult dr = MessageBox.Show("File exists: " + outFile + "\nOK to overwrite?", "File Exists", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information); if (dr != DialogResult.Yes) { return; } } try { Serializer serializer = new Serializer(); serializer.Serialize(kml); File.WriteAllText(outFile, serializer.Xml); } catch (Exception ex) { Utils.excMsg("Error creating KML file: " + outFile, ex); } // Send it to Google Earth if (kmlOptions.SendToGoogleEarth) { Process result; try { result = Process.Start(outFile); if (result == null) { Utils.errMsg("Failed to send to Google Earth:" + NL + " " + outFile); } } catch (Exception ex) { Utils.excMsg("Failed to send to Google Earth:" + NL + " " + outFile, ex); } } }
private static void processKML(SharpKml.Dom.Element Element) { try { // log.Info(Element.ToString() + " " + Element.Parent); } catch { } SharpKml.Dom.Document doc = Element as SharpKml.Dom.Document; SharpKml.Dom.Placemark pm = Element as SharpKml.Dom.Placemark; SharpKml.Dom.Folder folder = Element as SharpKml.Dom.Folder; SharpKml.Dom.Polygon polygon = Element as SharpKml.Dom.Polygon; SharpKml.Dom.LineString ls = Element as SharpKml.Dom.LineString; MultipleGeometry geom = Element as MultipleGeometry; if (doc != null) { foreach (var feat in doc.Features) { //Console.WriteLine("feat " + feat.GetType()); //processKML((Element)feat); } } else if (folder != null) { foreach (SharpKml.Dom.Feature feat in folder.Features) { //Console.WriteLine("feat "+feat.GetType()); //processKML(feat); } } else if (pm != null) { } else if (polygon != null) { GMapPolygon kmlpolygon = new GMapPolygon(new List <PointLatLng>(), polygon.Id); kmlpolygon.Stroke.Color = Color.Purple; kmlpolygon.Fill = new SolidBrush(Color.FromArgb(30, Color.Blue)); foreach (var loc in polygon.OuterBoundary.LinearRing.Coordinates) { kmlpolygon.Points.Add(new PointLatLng(loc.Latitude, loc.Longitude)); } kmlpolygonsoverlay.Polygons.Add(kmlpolygon); } else if (ls != null) { GMapRoute kmlroute = new GMapRoute(new List <PointLatLng>(), "kmlroute"); kmlroute.Stroke.Color = Color.Purple; foreach (var loc in ls.Coordinates) { kmlroute.Points.Add(new PointLatLng(loc.Latitude, loc.Longitude)); } kmlpolygonsoverlay.Routes.Add(kmlroute); } else if (geom != null) { foreach (var geometry in geom.Geometry) { processKML(geometry); } } }
protected virtual Feature CreateKmlFeature(FeatureDataRow feature, StyleSelector style) { var geometry = feature.Geometry; geometry = ToTarget(geometry); switch (geometry.OgcGeometryType) { case OgcGeometryType.Point: { var location = geometry.Coordinate; var p = new Point { Coordinate = new Vector(location.Y, location.X) }; return(WrapPlacemark(p, style, feature)); } case OgcGeometryType.MultiPoint: { var multiGeometry = new MultipleGeometry(); foreach (var coordinate in geometry.Coordinates) { var p = new Point { Coordinate = new Vector(coordinate.Y, coordinate.X) }; multiGeometry.AddGeometry(p); } return(WrapPlacemark(multiGeometry, style, feature)); } case OgcGeometryType.LineString: { var lineString = CreateLineString(geometry); return(WrapPlacemark(lineString, style, feature)); } case OgcGeometryType.Polygon: { var polygon = (IPolygon)geometry; var kmlPolygon = CreateKmlPolygon(polygon); return(WrapPlacemark(kmlPolygon, style, feature)); } case OgcGeometryType.MultiLineString: { var multiGeometry = new MultipleGeometry(); var multiLineString = (IMultiLineString)geometry; foreach (var innerGeometry in multiLineString.Geometries) { var lineString = CreateLineString(innerGeometry); multiGeometry.AddGeometry(lineString); } return(WrapPlacemark(multiGeometry, style, feature)); } case OgcGeometryType.MultiPolygon: { var multiGeometry = new MultipleGeometry(); var multiPoly = (IMultiPolygon)geometry; foreach (var innerGeometry in multiPoly.Geometries.Cast <IPolygon>()) { var polygon = CreateKmlPolygon(innerGeometry); multiGeometry.AddGeometry(polygon); } return(WrapPlacemark(multiGeometry, style, feature)); } default: throw new InvalidOperationException("Geometry not supported"); } }
public static List <KmlPlaceMark> GetPlaceMarksFromKmlFile(string kmlFile) { string compatibleXml = GetCompatibleXml(new StreamReader(kmlFile).ReadToEnd()); using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(compatibleXml))) { KmlFile file = KmlFile.Load(stream); List <KmlPlaceMark> list = new List <KmlPlaceMark>(); foreach (Placemark placemark in file.Root.Flatten().OfType <Placemark>()) { try { Geometry geo = placemark.Geometry; if (geo != null) { if (geo is MultipleGeometry) { MultipleGeometry geometry2 = geo as MultipleGeometry; if (geometry2.Geometry != null) { foreach (Geometry geometry3 in geometry2.Geometry) { KmlPlaceMark placeMark = new KmlPlaceMark { Name = placemark.Name, StyleUrl = placemark.StyleUrl.ToString() }; if (placemark.Description != null) { placeMark.Description = placemark.Description.Text; } Geometry geometry4 = geometry3; FillPlacemarkWithGeometry(ref placeMark, ref geometry4); list.Add(placeMark); } } } else { KmlPlaceMark mark2 = new KmlPlaceMark { Name = placemark.Name }; if (placemark.Description != null) { mark2.Description = placemark.Description.Text; } FillPlacemarkWithGeometry(ref mark2, ref geo); list.Add(mark2); } } continue; } catch { continue; } } return(list); } }
public PlacemarkExtensionsTests() { _polygon = new Polygon { OuterBoundary = new OuterBoundary { LinearRing = new LinearRing { Coordinates = new CoordinateCollection { new Vector { Longitude = 30, Latitude = 10 }, new Vector { Longitude = 40.5, Latitude = 40 }, new Vector { Longitude = 20, Latitude = 40 }, new Vector { Longitude = 10, Latitude = 20 }, new Vector { Longitude = 30, Latitude = 10 } } } } }; var polygonWithHole = new Polygon { OuterBoundary = new OuterBoundary { LinearRing = new LinearRing { Coordinates = new CoordinateCollection { new Vector { Longitude = 35, Latitude = 10 }, new Vector { Longitude = 45, Latitude = 45 }, new Vector { Longitude = 15, Latitude = 40 }, new Vector { Longitude = 10, Latitude = 20 }, new Vector { Longitude = 35, Latitude = 10 } } } } }; polygonWithHole.AddInnerBoundary(new InnerBoundary { LinearRing = new LinearRing { Coordinates = new CoordinateCollection { new Vector { Longitude = 20, Latitude = 30 }, new Vector { Longitude = 35, Latitude = 35 }, new Vector { Longitude = 30, Latitude = 20 }, new Vector { Longitude = 20, Latitude = 30 } } } }); _multipleGeometry = new MultipleGeometry(); _multipleGeometry.AddGeometry(new Polygon { OuterBoundary = new OuterBoundary { LinearRing = new LinearRing { Coordinates = new CoordinateCollection { new Vector { Longitude = 30, Latitude = 20 }, new Vector { Longitude = 45, Latitude = 40 }, new Vector { Longitude = 10, Latitude = 40 }, new Vector { Longitude = 30, Latitude = 20 } } } } }); _multipleGeometry.AddGeometry(new Polygon { OuterBoundary = new OuterBoundary { LinearRing = new LinearRing { Coordinates = new CoordinateCollection { new Vector { Longitude = 15, Latitude = 5 }, new Vector { Longitude = 40, Latitude = 10 }, new Vector { Longitude = 10, Latitude = 20 }, new Vector { Longitude = 5, Latitude = 10 }, new Vector { Longitude = 15, Latitude = 5 } } } } }); var linestring = new LineString { Coordinates = new CoordinateCollection(new List <Vector> { new Vector { Longitude = 30, Latitude = 10 }, new Vector { Longitude = 10, Latitude = 30 }, new Vector { Longitude = 40, Latitude = 40 } }) }; _placemark = new Placemark { Geometry = _polygon }; _placemarkWithHole = new Placemark { Geometry = polygonWithHole }; _multiplePlacemark = new Placemark { Geometry = _multipleGeometry }; _placemarkWithPoint = new Placemark { Geometry = new Point { Coordinate = new Vector(15, 5) } }; _placemarkWithLinestring = new Placemark { Geometry = linestring }; }
/// <summary> /// Converts JSON geometry to KML geometry. /// </summary> /// <param name="geometry">ArcGIS JSON geometry</param> /// <returns><see cref="Geometry"/></returns> public static Geometry JsonToKmlGeometry(Dictionary <string, object> geometry) { Geometry placemarkGeometry; if (geometry.Keys.Contains("x")) { // Create point geometry // x and y are decimals; var x = Convert.ToDouble(geometry["x"]); var y = Convert.ToDouble(geometry["y"]); placemarkGeometry = new Point { Coordinate = new Vector(y, x) }; } else if (geometry.Keys.Contains("rings")) { // Create polygon geometry var rings = (ArrayList)geometry["rings"]; var linearRings = from ArrayList ring in rings select new LinearRing { Coordinates = new CoordinateCollection(from ArrayList point in ring select new Vector( Convert.ToDouble(point[1]), Convert.ToDouble(point[0]) )) }; if (linearRings.Count() > 1) { var polygon = new Polygon(); polygon.OuterBoundary = new OuterBoundary { LinearRing = linearRings.ElementAt(0) }; var innerBoundaries = from lr in linearRings.Skip(1) select new InnerBoundary { LinearRing = lr }; foreach (var ib in innerBoundaries) { polygon.AddInnerBoundary(ib); } placemarkGeometry = polygon; } else { placemarkGeometry = linearRings.First(); } } else if (geometry.Keys.Contains("paths")) { // Create line geometry var paths = (ArrayList)geometry["paths"]; var lineStrings = from ArrayList path in paths select new LineString { Coordinates = new CoordinateCollection(from ArrayList point in path select new Vector( Convert.ToDouble(point[1]), Convert.ToDouble(point[0]) )) }; if (lineStrings.Count() > 1) { var multiGeo = new MultipleGeometry(); foreach (var ls in lineStrings) { multiGeo.AddGeometry(ls); } placemarkGeometry = multiGeo; } else { placemarkGeometry = lineStrings.First(); } } else { placemarkGeometry = null; } return(placemarkGeometry); }
public static void WriteNewKml(string filename, List <Placemark> features, List <Style> styles) { Document doc = new Document(); foreach (Style style in styles) { doc.AddStyle(style); } features.Sort((p1, p2) => p1.Name.CompareTo(p2.Name)); foreach (Placemark feature in features) { MultipleGeometry newMulti = new MultipleGeometry(); switch (feature.Geometry) { case MultipleGeometry multi: { foreach (Geometry geom in multi.Geometry) { if (geom is Polygon p) { newMulti.AddGeometry(new Polygon { OuterBoundary = p.OuterBoundary }); } } break; } case Polygon p: { newMulti.AddGeometry(new Polygon { OuterBoundary = p.OuterBoundary }); break; } default: { Console.WriteLine("Encountered unexpected geometry"); break; } } Placemark newPlacemark = new Placemark { Name = GetFeatureName(feature), Geometry = newMulti, StyleUrl = feature.StyleUrl }; doc.AddFeature(newPlacemark); } KmlFile file = KmlFile.Create(new Kml { Feature = doc }, false); SaveKml(file, filename); }