//KML reference
//
//URL of a
//http://someserver.com/somestylefile.xml#restaurant
//eateries.kml#my-lunch-spot
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Xml;
using System.Net;
using System.Web;
namespace ThinkGeo.MapSuite.Core
{
public class KmlFeatureSourceExtension : KmlFeatureSource
{
private const string presetXml = "";
private XmlDocument kmlDocument;
private InMemoryFeatureLayer inMemoryFeatureLayer;
private Collection tempFeatures;
private Dictionary> globalStyles;
protected KmlFeatureSourceExtension()
: this(string.Empty)
{
}
//public KmlFeatureSourceExtension(string kmlPathFileName)
// : this(kmlPathFileName, KmlStringType.File)
//{
//}
public KmlFeatureSourceExtension(string kmlString) //, KmlStringType stringType)
: base()
{
//switch (stringType)
//{
// case KmlStringType.String:
// kmlDocument = new XmlDocument();
// if (string.IsNullOrEmpty(kmlString))
// {
// kmlDocument.LoadXml(presetXml);
// }
// else
// {
// kmlDocument.LoadXml(kmlString);
// }
// break;
// case KmlStringType.File:
// default:
// kmlDocument = new XmlDocument();
// kmlDocument.Load(kmlString);
// break;
//}
kmlDocument = new XmlDocument();
kmlDocument.Load(kmlString);
this.inMemoryFeatureLayer = new InMemoryFeatureLayer();
inMemoryFeatureLayer.Open();
inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn("linecolor"));
inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn("linewidth"));
inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn("polycolor"));
inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn("polyfill"));
inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn("polyoutline"));
inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn("labelcolor"));
inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn("labelscale"));
inMemoryFeatureLayer.Close();
globalStyles = new Dictionary>();
}
protected override void OpenCore()
{
if (inMemoryFeatureLayer.InternalFeatures.Count == 0)
{
tempFeatures = new Collection();
XmlNamespaceManager manager = new XmlNamespaceManager(kmlDocument.NameTable);
manager.AddNamespace("kk", kmlDocument.DocumentElement.NamespaceURI);
if (kmlDocument.DocumentElement.HasChildNodes)
{
XmlNodeList styleList = kmlDocument.DocumentElement.ChildNodes[0].SelectNodes("kk:Style", manager);
foreach (XmlNode item in styleList)
{
if (null != item.Attributes && null != item.Attributes["id"])
{
string styleId = item.Attributes["id"].Value;
Dictionary columnValues = GetColumnValues(item, manager);
globalStyles.Add(styleId, columnValues);
}
}
XmlNodeList placemarks = kmlDocument.SelectNodes("//kk:Placemark", manager);
foreach (XmlNode placemark in placemarks)
{
ProcessPlacemark(placemark, manager);
}
// There is no Placemark tag, so it may use NetworkLink
if (placemarks.Count == 0)
{
XmlNodeList networkLinks = kmlDocument.SelectNodes("//kk:NetworkLink", manager);
foreach (XmlNode networkLink in networkLinks)
{
ProcessNetworkLink(networkLink, manager);
}
}
foreach (Feature item in tempFeatures)
{
WellKnownType wellKnownType = item.GetWellKnownType();
if (wellKnownType == WellKnownType.Polygon || wellKnownType == WellKnownType.Multipolygon)
{
inMemoryFeatureLayer.InternalFeatures.Add(item);
}
}
foreach (Feature item in tempFeatures)
{
WellKnownType wellKnownType = item.GetWellKnownType();
if (wellKnownType == WellKnownType.Line || wellKnownType == WellKnownType.Multiline)
{
inMemoryFeatureLayer.InternalFeatures.Add(item);
}
}
foreach (Feature item in tempFeatures)
{
WellKnownType wellKnownType = item.GetWellKnownType();
if (wellKnownType == WellKnownType.Point || wellKnownType == WellKnownType.Multipoint)
{
inMemoryFeatureLayer.InternalFeatures.Add(item);
}
}
}
}
}
private void ProcessNetworkLink(XmlNode networkLink, XmlNamespaceManager manager)
{
XmlNode hrefNode = networkLink.SelectSingleNode("kk:Url/kk:href", manager);
string fileUrl = hrefNode.InnerText;
WebRequest request = WebRequest.Create(fileUrl);
request.Timeout = 10000;
WebResponse response = null;
try
{
response = request.GetResponse();
string disposition = response.Headers["Content-Disposition"];
if (!string.IsNullOrEmpty(disposition))
{
string filename = @"C:\temp\" + disposition.Split('=')[1].Trim('\"');
WebClient webClient = new WebClient();
webClient.DownloadFile(fileUrl, filename);
if (filename.EndsWith(".kml", StringComparison.InvariantCultureIgnoreCase))
{
}
else if (filename.EndsWith(".kmz", StringComparison.InvariantCultureIgnoreCase))
{
filename = new KmlHelper().ProcessUnZip(filename);
}
else
{
return;
}
KmlFeatureSourceExtension source = new KmlFeatureSourceExtension(filename);
source.Open();
foreach (Feature item in source.inMemoryFeatureLayer.InternalFeatures)
{
tempFeatures.Add(item);
}
source.Close();
}
}
catch
{
}
finally
{
if (response != null)
{
response.Close();
}
}
}
private void ProcessPlacemark(XmlNode placemark, XmlNamespaceManager manager)
{
XmlNode stylesNode = placemark.SelectSingleNode("kk:Style", manager);
Dictionary columnValues = GetColumnValues(stylesNode, manager);
XmlNode descriptionNode = placemark.SelectSingleNode("kk:description", manager);
if (descriptionNode != null)
{
columnValues.Add("popupHTML", descriptionNode.InnerXml);
}
XmlNodeList linearRingList = placemark.SelectNodes(".//kk:LinearRing", manager);
foreach (XmlNode node in linearRingList)
{
string value = node.SelectSingleNode("kk:coordinates", manager).InnerText.Trim();
//TODO: the document says use " ", not "\r\n"
string[] values = value.Split(new string[] { " ", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
Collection Vertices = new Collection();
foreach (string item in values)
{
string[] items = item.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Vertex vertex = new Vertex(double.Parse(items[0]), double.Parse(items[1]));
Vertices.Add(vertex);
}
RingShape ringShape = new RingShape(Vertices);
tempFeatures.Add(new Feature(ringShape, columnValues));
}
XmlNodeList lineStringList = placemark.SelectNodes(".//kk:LineString", manager);
foreach (XmlNode node in lineStringList)
{
string value = node.SelectSingleNode("kk:coordinates", manager).InnerText.Trim();
//TODO: the document says use " ", not "\r\n"
string[] values = value.Split(new string[] { " ", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
Collection Vertices = new Collection();
foreach (string item in values)
{
string[] items = item.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Vertex vertex = new Vertex(double.Parse(items[0]), double.Parse(items[1]));
Vertices.Add(vertex);
}
LineShape lineShape = new LineShape(Vertices);
if (columnValues.Count == 0 || (columnValues.Count == 1 && columnValues.ContainsKey("popupHTML")))
{
columnValues.Add("linecolor", "ffffffff");
columnValues.Add("linewidth", "1");
if (placemark.SelectSingleNode("kk:styleUrl", manager) != null)
{
string styleUrl = placemark.SelectSingleNode("kk:styleUrl", manager).InnerText;
if (styleUrl.StartsWith("#"))
{
columnValues = globalStyles[styleUrl.TrimStart('#')];
}
}
}
tempFeatures.Add(new Feature(lineShape, columnValues));
}
XmlNodeList pointList = placemark.SelectNodes(".//kk:Point", manager);
foreach (XmlNode item in pointList)
{
string value = item.SelectSingleNode("kk:coordinates", manager).InnerText.Trim();
string[] values = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
PointShape point = new PointShape(double.Parse(values[0]), double.Parse(values[1]));
tempFeatures.Add(new Feature(point, columnValues));
}
}
private Dictionary GetColumnValues(XmlNode stylesNode, XmlNamespaceManager manager)
{
Dictionary columnValues = new Dictionary();
if (stylesNode != null)
{
foreach (XmlNode styleNode in stylesNode.ChildNodes)
{
if (styleNode.Name == "LineStyle")
{
string linecolor = "ffffffff";
if (styleNode.SelectSingleNode("kk:color", manager) != null)
{
linecolor = styleNode.SelectSingleNode("kk:color", manager).InnerText;
}
string linewidth = "1";
if (styleNode.SelectSingleNode("kk:width", manager) != null)
{
linewidth = styleNode.SelectSingleNode("kk:width", manager).InnerText;
}
columnValues.Add("linecolor", linecolor);
columnValues.Add("linewidth", linewidth);
}
else if (styleNode.Name == "PolyStyle")
{
string polycolor = "ffffffff";
if (styleNode.SelectSingleNode("kk:color", manager) != null)
{
polycolor = styleNode.SelectSingleNode("kk:color", manager).InnerText;
}
string polyfill = "1";
if (styleNode.SelectSingleNode("kk:fill", manager) != null)
{
polyfill = styleNode.SelectSingleNode("kk:fill", manager).InnerText;
}
string polyoutline = "1";
if (styleNode.SelectSingleNode("kk:outline", manager) != null)
{
polyoutline = styleNode.SelectSingleNode("kk:outline", manager).InnerText;
}
columnValues.Add("polycolor", polycolor);
columnValues.Add("polyfill", polyfill);
columnValues.Add("polyoutline", polyoutline);
}
else if (styleNode.Name == "LabelStyle")
{
string labelcolor = "ffffffff";
if (styleNode.SelectSingleNode("kk:color", manager) != null)
{
labelcolor = styleNode.SelectSingleNode("kk:color", manager).InnerText;
}
string labelscale = "1";
if (styleNode.SelectSingleNode("kk:scale", manager) != null)
{
labelscale = styleNode.SelectSingleNode("kk:scale", manager).InnerText;
}
columnValues.Add("labelcolor", labelcolor);
columnValues.Add("labelscale", labelscale);
}
else if (styleNode.Name == "IconStyle")
{
string iconcolor = "ffffffff";
if (styleNode.SelectSingleNode("kk:color", manager) != null)
{
iconcolor = styleNode.SelectSingleNode("kk:color", manager).InnerText;
}
string iconscale = "1";
if (styleNode.SelectSingleNode("kk:scale", manager) != null)
{
iconscale = styleNode.SelectSingleNode("kk:scale", manager).InnerText;
}
string iconheading = "0";
if (styleNode.SelectSingleNode("kk:heading", manager) != null)
{
iconheading = styleNode.SelectSingleNode("kk:heading", manager).InnerText;
}
string iconhref = "";
if (styleNode.SelectSingleNode("kk:href", manager) != null)
{
iconhref = styleNode.SelectSingleNode("kk:href", manager).InnerText;
}
string iconHotspot = "";
if (styleNode.SelectSingleNode("kk:hotSpot", manager) != null)
{
iconHotspot = styleNode.SelectSingleNode("kk:hotSpot", manager).InnerText;
}
columnValues.Add("iconcolor", iconcolor);
columnValues.Add("iconscale", iconscale);
columnValues.Add("iconheading", iconheading);
columnValues.Add("iconhref", iconhref);
columnValues.Add("iconHotspot", iconHotspot);
}
}
}
return columnValues;
}
protected override void CloseCore()
{
//inMemoryFeatureLayer.InternalFeatures.Clear();
//globalStyles.Clear();
}
protected override Collection GetAllFeaturesCore(IEnumerable returningColumnNames)
{
return inMemoryFeatureLayer.InternalFeatures;
}
///
/// Get the first KML Style if there is one in globalStyles.
///
///
public string GetStyleKml()
{
if (null != globalStyles && globalStyles.Count > 0)
{
return globalStyles.Keys.ElementAt(0);
}
return string.Empty;
}
///
/// Create a new LineStyle for the KML linecolor and linewidth attributes.
///
///
///
public LineStyle GetLineStyleKml(string id)
{
GeoColor linecolor = GeoColor.SimpleColors.White;
float linewidth = 1;
if (globalStyles.ContainsKey(id))
{
Dictionary lineStyle = globalStyles[id];
if (lineStyle.ContainsKey("linecolor"))
{
Color color = ColorTranslator.FromHtml("#" + lineStyle["linecolor"]);
linecolor = new GeoColor(color.A,color.B,color.G,color.R);
}
if (lineStyle.ContainsKey("linewidth"))
{
float.TryParse(lineStyle["linewidth"], out linewidth);
}
}
return new LineStyle(new GeoPen(linecolor, linewidth));
}
///
/// Create a new AreaStyle for the polycolor, polyfill, polyoutline attributes.
///
///
///
public AreaStyle GetPolyStyleKml(string id)
{
GeoColor polycolor = GeoColor.SimpleColors.White;
bool polyfill = true;
bool polyoutline = true;
GeoColor linecolor = GeoColor.SimpleColors.White;
float linewidth = 1;
if (globalStyles.ContainsKey(id))
{
Dictionary polyStyle = globalStyles[id];
if (polyStyle.ContainsKey("polycolor"))
{
Color color = ColorTranslator.FromHtml("#" + polyStyle["polycolor"]);
polycolor = new GeoColor(color.A, color.B, color.G, color.R);
}
if (polyStyle.ContainsKey("polyfill"))
{
polyfill = (polyStyle["polyfill"] == "1") ? true : false;
}
if (polyStyle.ContainsKey("polyoutline"))
{
polyoutline = (polyStyle["polyoutline"] == "1") ? true : false;
}
if (!polyfill)
{
polycolor = GeoColor.SimpleColors.Transparent;
}
if (polyoutline)
{
if (polyStyle.ContainsKey("linecolor"))
{
Color color = ColorTranslator.FromHtml("#" + polyStyle["linecolor"]);
linecolor = new GeoColor(color.A, color.B, color.G, color.R);
}
if (polyStyle.ContainsKey("linewidth"))
{
float.TryParse(polyStyle["linewidth"], out linewidth);
}
}
}
return new AreaStyle(new GeoPen(linecolor, linewidth),
new GeoSolidBrush(polycolor));
}
///
/// Create a new PointStyle for the iconcolor, iconheading, iconhref, iconhotspot attributes.
///
///
///
public PointStyle GetIconStyleKml(string id)
{
GeoColor iconcolor = GeoColor.SimpleColors.Black;
int iconscale = 5;
if (globalStyles.ContainsKey(id))
{
Dictionary labelStyle = globalStyles[id];
if (labelStyle.ContainsKey("iconcolor"))
{
Color color = ColorTranslator.FromHtml("#" + labelStyle["iconcolor"]);
iconcolor = new GeoColor(color.A, color.B, color.G, color.R);
}
if (labelStyle.ContainsKey("iconscale"))
{
int.TryParse(labelStyle["iconscale"], out iconscale);
}
}
return new PointStyle(PointSymbolType.Star, new GeoSolidBrush(iconcolor), iconscale);
}
///
/// Create a new TextStyle for the labelcolor and labelscale attributes.
///
///
///
public TextStyle GetLabelStyleKml(string id)
{
GeoColor labelcolor = GeoColor.SimpleColors.Black;
float labelscale = 5;
if (globalStyles.ContainsKey(id))
{
Dictionary labelStyle = globalStyles[id];
if (labelStyle.ContainsKey("labelcolor"))
{
Color color = ColorTranslator.FromHtml("#" + labelStyle["labelcolor"]);
labelcolor = new GeoColor(color.A,color.B,color.G,color.R);
}
if (labelStyle.ContainsKey("labelscale"))
{
float.TryParse(labelStyle["labelscale"], out labelscale);
}
}
return new TextStyle("KmlText", new GeoFont("Arial",
labelscale,
DrawingFontStyles.Regular),
new GeoSolidBrush(labelcolor));
}
}
}