private static void ComputeIconTranslationValues(KMLStyle style, KmlPlaceMarkerSymbol ms, BitmapImage bi) { if (bi.PixelWidth > 0) { ms.Width = bi.PixelWidth; } if (bi.PixelHeight > 0) { ms.Height = bi.PixelHeight; } switch (style.IconHotspotUnitsX) { case HotSpotUnitType.Pixels: ms.TranslateX = style.IconHotspotX * -1; break; case HotSpotUnitType.Fraction: ms.TranslateX = (bi.PixelWidth * style.IconHotspotX) * -1; break; } switch (style.IconHotspotUnitsY) { case HotSpotUnitType.Pixels: ms.TranslateY = (ms.Height - style.IconHotspotY) * -1; break; case HotSpotUnitType.Fraction: ms.TranslateY = (bi.PixelHeight * style.IconHotspotY) * -1; break; } }
private static void ComputeIconTranslationValues(KMLStyle style, KmlPlaceMarkerSymbol ms, BitmapImage bi) { // To match sizing of Google Earth, default size of point images is 40x40 // Note: the iconScale will be applied later globally to the symbol ms.Height = 40; ms.Width = 40; switch (style.IconHotspotUnitsX) { case HotSpotUnitType.Pixels: ms.TranslateX = style.IconHotspotX * -1; break; case HotSpotUnitType.Fraction: ms.TranslateX = (ms.Width * style.IconHotspotX) * -1; break; } switch (style.IconHotspotUnitsY) { case HotSpotUnitType.Pixels: ms.TranslateY = (ms.Height - style.IconHotspotY) * -1; break; case HotSpotUnitType.Fraction: ms.TranslateY = (ms.Height * style.IconHotspotY) * -1; break; } }
/// <summary> /// Adds a KML style to the list. /// </summary> /// <param name="key">Unique key associated with the style, typically an id, filename or GUID.</param> /// <param name="style">Style description.</param> public void AddStyle(string key, KMLStyle style) { if (styles.ContainsKey(key)) { styles[key] = style; } else { styles.Add(key, style); } }
/// <summary> /// Copies a KML style object contents to another KML object. /// </summary> /// <param name="from">The source of the copy (contents copied to instance invoking this method).</param> public void CopyFrom(KMLStyle from) { this.StyleId = from.StyleId; this.IconHref = from.IconHref; this.IconHotspotX = from.IconHotspotX; this.IconHotspotY = from.IconHotspotY; this.IconHotspotUnitsX = from.IconHotspotUnitsX; this.IconHotspotUnitsY = from.IconHotspotUnitsY; this.IconHeading = from.IconHeading; this.IconScale = from.IconScale; this.IconImage = from.IconImage; this.LineWidth = from.LineWidth; this.LineColor = from.LineColor; this.PolyFill = from.PolyFill; this.PolyOutline = from.PolyOutline; this.PolyFillColor = from.PolyFillColor; this.BalloonText = from.BalloonText; }
private static void GetRendererInfo(PlacemarkDescriptor feature, KMLStyle style, out string label, out string description) { label = null; description = null; if (string.IsNullOrEmpty(style.StyleId)) { //if the feature is not using a shared style -> create an entry by graphic with the name as ident if (feature.Attributes.ContainsKey("name")) { label = feature.Attributes["name"].ToString(); } if (feature.Attributes.ContainsKey("description")) { description = feature.Attributes["description"].ToString(); } } else { //if the feature is using a shared style -> create an entry for the style label = style.StyleId; } }
private void GetStyleUrlAsync(string styleUrl, XDocument xDoc, System.Net.ICredentials credentials, Action<KMLStyle> callback) { KMLStyle kmlStyle = new KMLStyle(); if (!String.IsNullOrEmpty(styleUrl)) { // If the style url begins with a # symbol, then it is a reference to a style // defined within the current KML file. Otherwise it is a reference to a style // in an external file which must be downloaded and processed. if (styleUrl.Substring(0, 1) == "#") { // Remove first character (which is "#") string styleId = styleUrl.Substring(1); if (xDoc == null) { if (featureDefs.styles.ContainsKey(styleId)) kmlStyle.CopyFrom(featureDefs.styles[styleId]); } else { XElement style = xDoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "Style" && (string)e.Attribute("id") == styleId); // Make sure the style was found and use first element if (style != null) { GetStyle(style, kmlStyle); } else { // See if the styleURL value is associated with a StyleMap node style = xDoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "StyleMap" && (string)e.Attribute("id") == styleId); // Make sure the style map was found and use first element if (style != null) { GetStyleMapAsync(style, xDoc, credentials, callback); return; } } } } else { DownloadStyleAsync(styleUrl, credentials, callback); return; } } callback(kmlStyle); }
/// <summary> /// Gets the 'normal' style of a style map. /// Getting this style may need recursive download. /// When the style is ready -> execute the callback. /// </summary> /// <remarks> /// The 'highlight' style is not used by the KmlLayer. /// </remarks> /// <param name="styleMap">The style map element to parse.</param> /// <param name="xDoc">The xDocument the style map is part of.</param> /// <param name="credentials">The credentials.</param> /// <param name="callback">The callback to call when the style is downloaded (if needed).</param> private void GetStyleMapAsync(XElement styleMap, XDocument xDoc, ICredentials credentials, Action<KMLStyle> callback) { XNamespace kmlNS = styleMap.Name.Namespace; KMLStyle kmlStyle = null; foreach (XElement pair in styleMap.Descendants(kmlNS + "Pair")) { XElement key = pair.Element(kmlNS + "key"); if (key != null) { if (key.Value == "normal") { XElement style = pair.Element(kmlNS + "Style"); if (style != null) { kmlStyle = new KMLStyle(); GetStyle(style, kmlStyle); } else { XElement styleUrl = pair.Element(kmlNS + "styleUrl"); if (styleUrl != null) { XAttribute styleIdAttribute = styleMap.Attribute("id"); string styleId = styleIdAttribute == null ? null : styleIdAttribute.Value; // Get the style from the styleUrl. This may need to downloading an external KML file GetStyleUrlAsync(styleUrl.Value, xDoc, credentials , kmlstyle => { //// After obtaining the style (which may have involved recursion and downloading external KML files //// to resolve style URLs) be sure to always overwrite the styleId with the name given to this StyleMap. if (styleId != null && kmlstyle != null) kmlstyle.StyleId = styleId; callback(kmlstyle); }); return; } } } } } // execute the callback with the found style (or null if not found) callback(kmlStyle); }
/// <summary> /// Extracts a point from the input element and applies style information to the placemark descriptor. /// </summary> /// <param name="kmlStyle">KML Style information.</param> /// <param name="point">Point geometry information.</param> /// <returns>A PlacemarkDescriptor object representing the feature.</returns> private static PlacemarkDescriptor ExtractPoint(KMLStyle kmlStyle, XElement point) { XNamespace kmlNS = point.Name.Namespace; XElement coord = point.Element(kmlNS + "coordinates"); if (coord != null) { // Extract geometry ESRI.ArcGIS.Client.Geometry.Geometry geom = ExtractCoordinate(coord.Value); if (geom != null) { // Create symbol and use style information PointSymbolDescriptor sym = new PointSymbolDescriptor(); sym.style = kmlStyle; // Create feature descriptor from geometry and other information PlacemarkDescriptor fd = new PlacemarkDescriptor() { Geometry = geom, Symbol = sym }; return fd; } } return null; }
/// <summary> /// Extracts a polygon from the input element and applies style information to the placemark descriptor. /// </summary> /// <param name="kmlStyle">KML Style information.</param> /// <param name="geomElement">Polygon geometry information.</param> /// <returns>A PlacemarkDescriptor object representing the feature.</returns> private static PlacemarkDescriptor ExtractPolygon(KMLStyle kmlStyle, XElement geomElement) { XNamespace kmlNS = geomElement.Name.Namespace; ESRI.ArcGIS.Client.Geometry.Polygon polygon = new Polygon(); // Extract outer polygon boundary XElement boundary; boundary = geomElement.Element(kmlNS + "outerBoundaryIs"); if (boundary != null) { ESRI.ArcGIS.Client.Geometry.PointCollection pts = ExtractRing(boundary); if (pts != null && pts.Count > 0) { polygon.Rings.Add(pts); } } // Extract holes (if any) IEnumerable<XElement> holes = from e in geomElement.Descendants(kmlNS + "innerBoundaryIs") select e; foreach (XElement hole in holes) { ESRI.ArcGIS.Client.Geometry.PointCollection pts = ExtractRing(hole); if (pts != null && pts.Count > 0) { polygon.Rings.Add(pts); } } // Create symbol and use style information PolygonSymbolDescriptor sym = new PolygonSymbolDescriptor(); sym.style = kmlStyle; if (polygon.Rings.Count > 0) { // Create feature descriptor from geometry and other information return new PlacemarkDescriptor() { Geometry = polygon, Symbol = sym }; } return null; }
private static void ExtractFeatureStyleInfo(KMLStyle kmlStyle, XElement placemark) { XNamespace kmlNS = placemark.Name.Namespace; XElement colorElement = placemark.Element(kmlNS + "color"); if (colorElement != null) kmlStyle.PolyFillColor = GetColorFromHexString(colorElement.Value); XElement iconElement = placemark.Element(kmlNS + "Icon"); if (iconElement != null) kmlStyle.IconHref = iconElement.Value.Trim(); }
private static void StoreZipfileAndCallback(KMLStyle kmlStyle, Action<KMLStyle> callback, ZipFile zipFile) { if (zipFile != null) { if (kmlStyle.ZipFile == null && !String.IsNullOrEmpty(kmlStyle.IconHref)) { kmlStyle.ZipFile = zipFile; } else { zipFile.Dispose(); } } callback(kmlStyle); }
/// <summary> /// Constructs a KMLStyle object that represents KML style contained in the input XElement. /// </summary> /// <param name="style">XElement containing KML style definition.</param> /// <param name="kmlStyle">KMLStyle object representing input style.</param> private static void GetStyle(XElement style, KMLStyle kmlStyle) { XNamespace kmlNS = style.Name.Namespace; XAttribute styleId = style.Attribute("id"); if (styleId != null) { kmlStyle.StyleId = styleId.Value; } // If style contains an BalloonStyle, then extract that information XElement balloonStyle = style.Element(kmlNS + "BalloonStyle"); if (balloonStyle != null) { XElement text = balloonStyle.Element(kmlNS + "text"); if (text != null) { kmlStyle.BalloonText = text.Value; } } // If style contains an IconStyle, then extract that information XElement iconStyle = style.Element(kmlNS + "IconStyle"); if (iconStyle != null) { XElement icon = iconStyle.Element(kmlNS + "Icon"); if (icon != null) { XElement href = icon.Element(kmlNS + "href"); if (href != null) { string iconUrl = href.Value; const string googlePal = "root://icons/palette-"; if(iconUrl.StartsWith(googlePal, StringComparison.OrdinalIgnoreCase)) { // Replace Google earth built-in palette URL by the real URL int x = 0; int y = 0; int numPalette = 0; XElement xElement = icon.Element(kmlNS + "x"); if (xElement != null) int.TryParse(xElement.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out x); XElement yElement = icon.Element(kmlNS + "y"); if (yElement != null) int.TryParse(yElement.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out y); string pal = iconUrl.Substring(googlePal.Length, 1); int.TryParse(pal, NumberStyles.Integer, CultureInfo.InvariantCulture, out numPalette); if (numPalette > 0) { int numIcon = 8 * (7 - y/32) + x/32; iconUrl = string.Format("http://maps.google.com/mapfiles/kml/pal{0}/icon{1}.png", numPalette, numIcon); } } kmlStyle.IconHref = iconUrl; } } // Extract IconColor XElement iconColor = iconStyle.Element(kmlNS + "color"); if (iconColor != null) { kmlStyle.IconColor = GetColorFromHexString(iconColor.Value); } // If the hotspot element is present, make use of it XElement hotspot = iconStyle.Element(kmlNS + "hotSpot"); if (hotspot != null) { XAttribute units; XAttribute val; units = hotspot.Attribute("xunits"); if (units != null) { try { kmlStyle.IconHotspotUnitsX = (HotSpotUnitType)Enum.Parse(typeof(HotSpotUnitType), units.Value, true); val = hotspot.Attribute("x"); if (val != null) { double x; if (double.TryParse(val.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out x)) kmlStyle.IconHotspotX = x; } } catch { } } units = hotspot.Attribute("yunits"); if (units != null) { try { kmlStyle.IconHotspotUnitsY = (HotSpotUnitType)Enum.Parse(typeof(HotSpotUnitType), units.Value, true); val = hotspot.Attribute("y"); if (val != null) { double y; if (double.TryParse(val.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out y)) kmlStyle.IconHotspotY = y; } } catch { } } } // If the heading element is present, make use of it XElement heading = iconStyle.Element(kmlNS + "heading"); if (heading != null) { double degrees; if (double.TryParse(heading.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out degrees)) kmlStyle.IconHeading = degrees; } // If the scale element is present, make use of it XElement scale = iconStyle.Element(kmlNS + "scale"); if (scale != null) { double scaleAmount; if (double.TryParse(scale.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out scaleAmount)) kmlStyle.IconScale = scaleAmount; } } // If style contains a LineStyle, then extract that information XElement lineStyle = style.Element(kmlNS + "LineStyle"); if (lineStyle != null) { XElement color = lineStyle.Element(kmlNS + "color"); if (color != null) { kmlStyle.LineColor = GetColorFromHexString(color.Value); } XElement width = lineStyle.Element(kmlNS + "width"); if (width != null) { double widthVal; if (double.TryParse(width.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out widthVal)) kmlStyle.LineWidth = widthVal; } } // If style contains a PolyStyle, then extract that information XElement polyStyle = style.Element(kmlNS + "PolyStyle"); if (polyStyle != null) { XElement color = polyStyle.Element(kmlNS + "color"); if (color != null) { kmlStyle.PolyFillColor = GetColorFromHexString(color.Value); } XElement fill = polyStyle.Element(kmlNS + "fill"); if (fill != null) { kmlStyle.PolyFill = StringToBool(fill.Value); } XElement outline = polyStyle.Element(kmlNS + "outline"); if (outline != null) { kmlStyle.PolyOutline = StringToBool(outline.Value); } } }
/// <summary> /// Creates graphic elements and adds them to the graphics layer. /// </summary> /// <param name="layer">Graphics layer that will have features added to it.</param> /// <param name="images">Dictionary of images coming from kmz content or from previous parsing</param> public void CreateGraphics(KmlGraphicsLayer layer, IDictionary <string, ImageBrush> images) { if (layer == null) { return; } GraphicCollection graphics = new GraphicCollection(); UniqueValueRenderer renderer = new UniqueValueRenderer(); // dummy renderer used to create the legend items (since creation of the swatches from the symbol is not that obvious) // Process each metadata feature in the list foreach (PlacemarkDescriptor feature in placemarks) { KMLStyle style = feature.Symbol.style; if (style.ZipFile != null) { // Look for the image in the zip file if (style.IconImage == null && !String.IsNullOrEmpty(style.IconHref)) { style.IconImage = GetIconImage(style.ZipFile, style.IconHref.ToLower()); } style.ZipFile.Dispose(); style.ZipFile = null; } //Define handlers upfront so we can unhook from them #if SILVERLIGHT EventHandler <RoutedEventArgs> #else EventHandler #endif imageCompleted = null; #if SILVERLIGHT EventHandler <ExceptionRoutedEventArgs> #else EventHandler <ExceptionEventArgs> #endif imageFailed = null; // If the style has an HREF then it is associated with an image if (style.IconImage == null && !String.IsNullOrEmpty(style.IconHref)) { // If the image is already loaded in the image dictionary, use it if (images.ContainsKey(style.IconHref.ToLower())) { style.IconImage = images[style.IconHref.ToLower()]; } else { // Get the image using the HREF and store the image in the images dictionary so that if // other features reference it, it is cached style.IconImage = GetIconImage(style.IconHref); if (style.IconImage != null && (style.IconImage as ImageBrush).ImageSource != null) { var bi = (style.IconImage as ImageBrush).ImageSource as BitmapImage; if (bi != null) { imageFailed = (s, e) => { var b = s as BitmapImage; #if SILVERLIGHT if (imageCompleted != null) { b.ImageOpened -= imageCompleted; } if (imageFailed != null) { b.ImageFailed -= imageFailed; } #else if (imageCompleted != null) { b.DownloadCompleted -= imageCompleted; } if (imageFailed != null) { b.DownloadFailed -= imageFailed; } #endif var key = b.GetValue(BitmapImageKeyProperty) as string; layer.Dispatcher.BeginInvoke((Action) delegate { UpdateGraphicsAndRenderer(layer, renderer, key); }); }; #if SILVERLIGHT bi.ImageFailed += imageFailed; #else bi.DownloadFailed += imageFailed; #endif } } images.Add(style.IconHref.ToLower(), style.IconImage); } } // Create a new graphic from the metadata and construct the symbol using polymorphism Graphic g = new Graphic() { Geometry = feature.Geometry, Symbol = feature.Symbol.CreateSymbol(), TimeExtent = feature.TimeExtent }; g.SetValue(FeaturePlacemarkerDescriptorProperty, feature); // Create legend entry string label; string description; GetRendererInfo(feature, style, out label, out description); if (!string.IsNullOrEmpty(label) && !renderer.Infos.Any(info => info.Label == label)) { renderer.Infos.Add(new UniqueValueInfo { Label = label, Description = description, Symbol = g.Symbol }); } // Adjust and assign picture marker symbol properties if (g.Geometry is ESRI.ArcGIS.Client.Geometry.MapPoint && g.Symbol is KmlPlaceMarkerSymbol) { try { KmlPlaceMarkerSymbol ms = g.Symbol as KmlPlaceMarkerSymbol; // To match sizing of Google Earth, default size of point images is 40x40 ms.Height = 40; ms.Width = 40; ms.Fill = style.IconImage; ms.IconColor = style.IconColor; // Default to half the pixel size (width and height) if symbol offsets are 0 (supported in wpf and sl3) ImageBrush ib = ms.Fill; BitmapImage bi = ib.ImageSource as BitmapImage; #if SILVERLIGHT if (bi.PixelHeight == 0 || bi.PixelWidth == 0) #else if (bi.IsDownloading) #endif { imageCompleted = (s, e) => { var b = s as BitmapImage; #if SILVERLIGHT if (imageCompleted != null) { b.ImageOpened -= imageCompleted; } if (imageFailed != null) { b.ImageFailed -= imageFailed; } #else if (imageCompleted != null) { b.DownloadCompleted -= imageCompleted; } if (imageFailed != null) { b.DownloadFailed -= imageFailed; } #endif ComputeIconTranslationValues(style, ms, b); }; #if SILVERLIGHT bi.ImageOpened += imageCompleted; #else bi.DownloadCompleted += imageCompleted; #endif } else { ComputeIconTranslationValues(style, ms, bi); } } catch { g.Symbol = PointSymbolDescriptor.GetDefaultSymbol(); ComputeIconTranslationValues(style, g.Symbol as KmlPlaceMarkerSymbol, ((g.Symbol as KmlPlaceMarkerSymbol).Fill as ImageBrush).ImageSource as BitmapImage); var info = renderer.Infos.FirstOrDefault(i => i.Label == label); if (info != null) { info.Symbol = g.Symbol; } } } // Copy attributes values from metadata to graphic foreach (var attribute in feature.Attributes) { g.Attributes.Add(attribute.Key, attribute.Value); } // If the balloontext property has been assigned a value in the style associated with this // graphic feature, then add it to the attributes collection. if (!String.IsNullOrEmpty(style.BalloonText)) { g.Attributes.Add("balloonText", style.BalloonText); } // Add graphic to graphics layer graphics.Add(g); } layer.Graphics = graphics; // keep the renderer for further usage (when QueryLegendInfos is called) layer.RendererBasedOnStyle = renderer; }
private static void ComputeIconTranslationValues(KMLStyle style, KmlPlaceMarkerSymbol ms, BitmapImage bi) { if (bi.PixelWidth > 0) ms.Width = bi.PixelWidth; if (bi.PixelHeight > 0) ms.Height = bi.PixelHeight; switch (style.IconHotspotUnitsX) { case HotSpotUnitType.Pixels: ms.TranslateX = style.IconHotspotX * -1; break; case HotSpotUnitType.Fraction: ms.TranslateX = (bi.PixelWidth * style.IconHotspotX) * -1; break; } switch (style.IconHotspotUnitsY) { case HotSpotUnitType.Pixels: ms.TranslateY = (ms.Height - style.IconHotspotY) * -1; break; case HotSpotUnitType.Fraction: ms.TranslateY = (bi.PixelHeight * style.IconHotspotY) * -1; break; } }
/// <summary> /// Constructs a KMLStyle object that represents KML style contained in the input XElement. /// </summary> /// <param name="style">XElement containing KML style definition.</param> /// <param name="kmlStyle">KMLStyle object representing input style.</param> private static void GetStyle(XElement style, KMLStyle kmlStyle) { XNamespace kmlNS = style.Name.Namespace; XAttribute styleId = style.Attribute("id"); if (styleId != null) { kmlStyle.StyleId = styleId.Value; } // If style contains an BalloonStyle, then extract that information XElement balloonStyle = style.Element(kmlNS + "BalloonStyle"); if (balloonStyle != null) { XElement text = balloonStyle.Element(kmlNS + "text"); if (text != null) { kmlStyle.BalloonText = text.Value; } } // If style contains an IconStyle, then extract that information XElement iconStyle = style.Element(kmlNS + "IconStyle"); if (iconStyle != null) { XElement icon = iconStyle.Element(kmlNS + "Icon"); if (icon != null) { XElement href = icon.Element(kmlNS + "href"); if (href != null) { kmlStyle.IconHref = href.Value; } } // If the hotspot element is present, make use of it XElement hotspot = iconStyle.Element(kmlNS + "hotSpot"); if (hotspot != null) { XAttribute units; XAttribute val; units = hotspot.Attribute("xunits"); if (units != null) { try { kmlStyle.IconHotspotUnitsX = (HotSpotUnitType)Enum.Parse(typeof(HotSpotUnitType), units.Value, true); val = hotspot.Attribute("x"); if (val != null) { double x; if (double.TryParse(val.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out x)) kmlStyle.IconHotspotX = x; } } catch { } } units = hotspot.Attribute("yunits"); if (units != null) { try { kmlStyle.IconHotspotUnitsY = (HotSpotUnitType)Enum.Parse(typeof(HotSpotUnitType), units.Value, true); val = hotspot.Attribute("y"); if (val != null) { double y; if (double.TryParse(val.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out y)) kmlStyle.IconHotspotY = y; } } catch { } } } // If the heading element is present, make use of it XElement heading = iconStyle.Element(kmlNS + "heading"); if (heading != null) { double degrees; if (double.TryParse(heading.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out degrees)) kmlStyle.IconHeading = degrees; } // If the scale element is present, make use of it XElement scale = iconStyle.Element(kmlNS + "scale"); if (scale != null) { double scaleAmount; if (double.TryParse(scale.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out scaleAmount)) kmlStyle.IconScale = scaleAmount; } } // If style contains a LineStyle, then extract that information XElement lineStyle = style.Element(kmlNS + "LineStyle"); if (lineStyle != null) { XElement color = lineStyle.Element(kmlNS + "color"); if (color != null) { kmlStyle.LineColor = GetColorFromHexString(color.Value); } XElement width = lineStyle.Element(kmlNS + "width"); if (width != null) { double widthVal; if (double.TryParse(width.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out widthVal)) kmlStyle.LineWidth = widthVal; } } // If style contains a PolyStyle, then extract that information XElement polyStyle = style.Element(kmlNS + "PolyStyle"); if (polyStyle != null) { XElement color = polyStyle.Element(kmlNS + "color"); if (color != null) { kmlStyle.PolyFillColor = GetColorFromHexString(color.Value); } XElement fill = polyStyle.Element(kmlNS + "fill"); if (fill != null) { kmlStyle.PolyFill = StringToBool(fill.Value); } XElement outline = polyStyle.Element(kmlNS + "outline"); if (outline != null) { kmlStyle.PolyOutline = StringToBool(outline.Value); } } }
/// <summary> /// Takes features in the KML element and converts them into equivalent features /// and adds them to the FeatureDefinition. /// Only the direct children of the KML element are converted. /// </summary> /// <param name="context">Context containing the XElement with the KML definition to be converted.</param> /// <param name="credentials">The credentials.</param> /// <returns></returns> public FeatureDefinition Convert(KmlLayerContext context, System.Net.ICredentials credentials = null) { XElement xElement = context.Element; XNamespace kmlNS = xElement.Name.Namespace; _waitHelper.Reset(); // Remove any existing features so only those contained in the input KML element file are stored featureDefs.Clear(); // Process the styles if they are not already known (the styles are shared by all folders/documents, so process them only once) if (context.Styles == null) { featureDefs.styles = new Dictionary<string, KMLStyle>(); // Find all Style elements that have an ID and can thus be referenced by other styleURLs. IEnumerable<XElement> styles = xElement.Descendants().Where(e => e.Name.LocalName == "Style" && (string)e.Attribute("id") != null); foreach (XElement style in styles) { KMLStyle kmlStyle = new KMLStyle(); GetStyle(style, kmlStyle); featureDefs.AddStyle(kmlStyle.StyleId, kmlStyle); } // Find all StyleMap elements that have an ID and can thus be referenced by other styleURLs. IEnumerable<XElement> styleMaps = xElement.Descendants().Where(e => e.Name.LocalName == "StyleMap" && (string)e.Attribute("id") != null); foreach (XElement map in styleMaps) { // A style map may need to download styles in other documents. // Need to use asynchronous pattern. GetStyleMapAsync(map, null, credentials , kmlStyle => { if (kmlStyle != null) featureDefs.AddStyle(kmlStyle.StyleId, kmlStyle); }); } // Wait for getting all styles before creating the feature definition _waitHelper.Wait(); } else { foreach (var style in context.Styles) featureDefs.AddStyle(style.Key, style.Value); } // Process the optional NetworkLinkControl XElement networkLinkControl = xElement.Element(kmlNS + "NetworkLinkControl"); if (networkLinkControl != null) { featureDefs.networkLinkControl = new NetworkLinkControl(); XElement minRefreshPeriod = networkLinkControl.Element(kmlNS + "minRefreshPeriod"); if (minRefreshPeriod != null) featureDefs.networkLinkControl.MinRefreshPeriod = GetDoubleValue(minRefreshPeriod); } // Find the containers which will be represented by a sublayer i.e Document/Folder/NetworkLink foreach (XElement container in xElement.Elements().Where(element => element.Name.LocalName == "Folder" || element.Name.LocalName == "Document" || element.Name.LocalName == "NetworkLink")) { ContainerInfo containerInfo = new ContainerInfo { Element = container, Url = null, // only for networklink Visible = true, AtomAuthor = context.AtomAuthor, // Use parent value by default AtomHref = context.AtomHref // Use parent value by default }; XNamespace kmlContainerNS = container.Name.Namespace; if (container.Name.LocalName == "NetworkLink") { string hrefValue = ""; string composite = ""; string layerids = ""; // Link takes precedence over Url from KML version 2.1 and later: XElement url = container.Element(kmlContainerNS + "Link") ?? container.Element(kmlContainerNS + "Url"); if (url != null) { XElement href = url.Element(kmlContainerNS + "href"); if (href != null) { hrefValue = href.Value; } // This next section is to parse special elements that only occur when an ArcGIS Server KML // is to be processed. XElement view = url.Element(kmlContainerNS + "viewFormat"); if (view != null) { int begIdx = view.Value.IndexOf("Composite"); if (begIdx != -1) { int endIdx = view.Value.IndexOf("&", begIdx); if (endIdx != -1) composite = view.Value.Substring(begIdx, endIdx - begIdx); } begIdx = view.Value.IndexOf("LayerIDs"); if (begIdx != -1) { int endIdx = view.Value.IndexOf("&", begIdx); if (endIdx != -1) layerids = view.Value.Substring(begIdx, endIdx - begIdx); } } // If network link URL is successfully extracted, then add to container list if (!String.IsNullOrEmpty(hrefValue)) { // extract refreshInterval XElement refreshMode = url.Element(kmlContainerNS + "refreshMode"); if (refreshMode != null && refreshMode.Value == "onInterval") { XElement refreshInterval = url.Element(kmlContainerNS + "refreshInterval"); if (refreshInterval != null) containerInfo.RefreshInterval = GetDoubleValue(refreshInterval); else containerInfo.RefreshInterval = 4; // default value } // the following values are for processing specialized ArcGIS Server KML links // generated from REST endpoints. if (!String.IsNullOrEmpty(composite)) hrefValue += "?" + composite; if (!String.IsNullOrEmpty(layerids)) { if (!String.IsNullOrEmpty(hrefValue)) hrefValue += "&" + layerids; else hrefValue += "?" + layerids; } containerInfo.Url = hrefValue; } else containerInfo = null; // Link without href. Should not happen. Skip it. } else containerInfo = null; // NetworkLink without Link/Url. Should not happen. Skip it. } else { // Folder or Document XElement XElement linkElement = container.Elements(atomNS + "link").Where(element => element.HasAttributes).FirstOrDefault(); if (linkElement != null) { // Overwrite global default value only upon successful extraction from element string tempHref = GetAtomHref(linkElement); if (!String.IsNullOrEmpty(tempHref)) containerInfo.AtomHref = new Uri(tempHref); } XElement authorElement = container.Element(atomNS + "author"); if (authorElement != null) { // Overwrite global default value only upon successful extraction from element string tempAuthor = GetAtomAuthor(authorElement); if (!String.IsNullOrEmpty(tempAuthor)) containerInfo.AtomAuthor = tempAuthor; } } if (containerInfo != null) { XElement visibilityElement = container.Element(kmlContainerNS + "visibility"); if (visibilityElement != null) { containerInfo.Visible = GetBooleanValue(visibilityElement); } XElement nameElement = container.Element(kmlContainerNS + "name"); if (nameElement != null) { containerInfo.Name = nameElement.Value; } if (container.HasAttributes && container.Attribute(KmlLayer.FolderIdAttributeName) != null) { containerInfo.FolderId = (int)container.Attribute(KmlLayer.FolderIdAttributeName); } featureDefs.AddContainer(containerInfo); } } // Process all children placemarks or groundoverlays foreach (XElement element in xElement.Elements().Where(element => element.Name == kmlNS + "Placemark" || element.Name == kmlNS + "GroundOverlay" )) { // Establish baseline style if a "styleUrl" setting is present XElement styleElement = element.Element(kmlNS + "styleUrl"); if (styleElement != null) { // get the style asynchronously and create the feature definition as soon as the style is there XElement featureElement = element; GetStyleUrlAsync(styleElement.Value, null, credentials, kmlStyle => CreateFeatureDefinition(kmlStyle, featureElement, null, context)); } else { // Create feature definition synchronously using default KML style, meta data and placemark information CreateFeatureDefinition(null, element, null, context); } } // Get the name of the XElement XElement nameXElement = xElement.Element(kmlNS + "name"); if (nameXElement != null && string.IsNullOrEmpty(featureDefs.name)) { featureDefs.name = nameXElement.Value; } // At this point, some inner styles are possibly on the way to being downloaded and so the feature definitions are not created yet // Wait for all downloads to be sure all feature definitions are created before terminating the background worker _waitHelper.Wait(); int folderId = 0; if (xElement.HasAttributes && xElement.Attribute(KmlLayer.FolderIdAttributeName) != null) { folderId = (int)xElement.Attribute(KmlLayer.FolderIdAttributeName); } if (!featureDefs.groundOverlays.Any() && !featureDefs.placemarks.Any() && featureDefs.containers.Count() == 1 && folderId == 0 && string.IsNullOrEmpty(featureDefs.containers.First().Url)) { // Avoid useless level when there is no groundoverlay, no placemark and only one folder or document at the root level ContainerInfo container = featureDefs.containers.First(); Dictionary<string, KMLStyle> styles = featureDefs.styles.ToDictionary(style => style.Key, style => style.Value); KmlLayerContext childContext = new KmlLayerContext { Element = container.Element, // The XElement that the KML layer has to process Styles = styles, Images = context.Images, AtomAuthor = container.AtomAuthor, AtomHref = container.AtomHref }; featureDefs.hasRootContainer = true; return Convert(childContext, credentials); } return featureDefs; }
/// <summary> /// Copies a KML style object contents to another KML object. /// </summary> /// <param name="from">The source of the copy (contents copied to instance invoking this method).</param> public void CopyFrom(KMLStyle from) { this.StyleId = from.StyleId; this.IconHref = from.IconHref; this.IconHotspotX = from.IconHotspotX; this.IconHotspotY = from.IconHotspotY; this.IconHotspotUnitsX = from.IconHotspotUnitsX; this.IconHotspotUnitsY = from.IconHotspotUnitsY; this.IconHeading = from.IconHeading; this.IconScale = from.IconScale; this.IconImage = from.IconImage; this.LineWidth = from.LineWidth; this.LineColor = from.LineColor; this.PolyFill = from.PolyFill; this.PolyOutline = from.PolyOutline; this.PolyFillColor = from.PolyFillColor; this.BalloonText = from.BalloonText; this.IconColor = from.IconColor; }
private void CreateFeatureDefinition(KMLStyle kmlStyle, XElement feature, XElement geometry, KmlLayerContext context) { if (feature == null) return; // should not happen XNamespace kmlNS = feature.Name.Namespace; if (feature.Name.LocalName == "Placemark") { // kmlStyle is null when the placemark doesn't reference any shared style (or a shared style that we are not able to download) // in this case, use a default style if (kmlStyle == null) kmlStyle = new KMLStyle(); // Determine what kind of feature is present in the placemark. If an input geometry is present, then the // style has already been determined and this method is being called recursively for each child element // of a multi-geometry placemarker. XElement geomElement = null; if (geometry != null) { geomElement = geometry; } else { geomElement = GetFeatureType(feature); // Override any settings from the inline style "Style" node XElement styleElement = feature.Element(kmlNS + "Style"); if (styleElement != null) { GetStyle(styleElement, kmlStyle); } } PlacemarkDescriptor fd = null; if (geomElement != null && geomElement.Name != null) { switch (geomElement.Name.LocalName) { case "Point": fd = ExtractPoint(kmlStyle, geomElement); break; case "LineString": fd = ExtractPolyLine(kmlStyle, geomElement); break; case "LinearRing": fd = ExtractLinearRing(kmlStyle, geomElement); break; case "Polygon": fd = ExtractPolygon(kmlStyle, geomElement); break; case "MultiGeometry": foreach (XElement item in geomElement.Elements()) { // Use recursion to walk the hierarchy of embedded definitions CreateFeatureDefinition(kmlStyle, feature, item, context); } break; case "LatLonBox": ExtractFeatureStyleInfo(kmlStyle, feature); fd = ExtractLatLonBox(kmlStyle, geomElement); break; } // If a feature definition was created, then assign attributes and add to collection if (fd != null) { if (fd.Geometry != null) fd.Geometry.SpatialReference = new SpatialReference(4326); XElement descElement = feature.Element(kmlNS + "description"); if (descElement != null) fd.Attributes.Add("description", descElement.Value); XElement nameElement = feature.Element(kmlNS + "name"); if (nameElement != null) fd.Attributes.Add("name", nameElement.Value); if (atomNS != null) { // Initialize to parent value Uri atomHrefValue = context.AtomHref; // If node exists, has attributes, and can be successfully extracted, then extract // this value. XElement atomHrefElement = feature.Element(atomNS + "link"); if (atomHrefElement != null && atomHrefElement.HasAttributes) { string tempHref = GetAtomHref(atomHrefElement); if (!String.IsNullOrEmpty(tempHref)) atomHrefValue = new Uri(tempHref); } // If a value was extracted or assigned from a parent, then add to attributes if (atomHrefValue != null) fd.Attributes.Add("atomHref", atomHrefValue); // AtomAuthor : Initialize to parent value string atomValue = context.AtomAuthor; // If node exists, has attributes, and can be successfully extracted, then extract // this value. XElement atomAuthorElement = feature.Element(atomNS + "author"); if (atomAuthorElement != null) { string tempAuthor = GetAtomAuthor(atomAuthorElement); if (!String.IsNullOrEmpty(tempAuthor)) atomValue = tempAuthor; } // If a value was extracted or assigned from a parent, then add to attributes if (!String.IsNullOrEmpty(atomValue)) fd.Attributes.Add("atomAuthor", atomValue); } // Extract extended information XElement extendedDataElement = feature.Element(kmlNS + "ExtendedData"); if (extendedDataElement != null) { List<KmlExtendedData> extendedList = new List<KmlExtendedData>(); IEnumerable<XElement> dataElements = from e in extendedDataElement.Descendants(kmlNS + "Data") select e; foreach (XElement data in dataElements) { XAttribute name = data.Attribute("name"); if (name != null) { KmlExtendedData listItem = new KmlExtendedData(); listItem.Name = name.Value; foreach (XElement dataChild in data.Descendants()) { if (dataChild.Name == kmlNS + "displayName") listItem.DisplayName = dataChild.Value; else if (dataChild.Name == kmlNS + "value") listItem.Value = dataChild.Value; } extendedList.Add(listItem); } } if (extendedList.Count > 0) fd.Attributes.Add("extendedData", extendedList); } featureDefs.AddPlacemark(fd); } } } else if (feature.Name.LocalName == "GroundOverlay") { XElement latLonBoxElement = feature.Element(kmlNS + "LatLonBox"); if (latLonBoxElement != null) { GroundOverlayDescriptor fd = new GroundOverlayDescriptor(); fd.Envelope = ExtractEnvelope(latLonBoxElement); XElement rotationElement = latLonBoxElement.Element(kmlNS + "rotation"); if (rotationElement != null) fd.Rotation = GetDoubleValue(rotationElement); XElement colorElement = feature.Element(kmlNS + "color"); if (colorElement != null) fd.Color = GetColorFromHexString(colorElement.Value); else fd.Color = System.Windows.Media.Colors.White; // Default = white XElement iconElement = feature.Element(kmlNS + "Icon"); if (iconElement != null) { XElement href = iconElement.Element(kmlNS + "href"); if (href != null) { fd.IconHref = href.Value; } } featureDefs.AddGroundOverlay(fd); } } }
/// <summary> /// Adds a KML style to the list. /// </summary> /// <param name="key">Unique key associated with the style, typically an id, filename or GUID.</param> /// <param name="style">Style description.</param> public void AddStyle(string key, KMLStyle style) { if (styles.ContainsKey(key)) styles[key] = style; else styles.Add(key, style); }
/// <summary> /// Extracts a polygon from the input element and applies style information to the placemark descriptor. /// </summary> /// <param name="kmlStyle">KML Style information.</param> /// <param name="geomElement">Polygon geometry information.</param> /// <returns>A PlacemarkDescriptor object representing the feature.</returns> private static PlacemarkDescriptor ExtractLatLonBox(KMLStyle kmlStyle, XElement geomElement) { XNamespace kmlNS = geomElement.Name.Namespace; ESRI.ArcGIS.Client.Geometry.Polygon polygon = new Polygon(); double? north = null, south = null, east = null, west = null; double temp; XElement boundary; // Extract box values boundary = geomElement.Element(kmlNS + "north"); if (boundary != null) { if (double.TryParse(boundary.Value, System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out temp)) north = temp; } boundary = geomElement.Element(kmlNS + "south"); if (boundary != null) { if (double.TryParse(boundary.Value, System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out temp)) south = temp; } boundary = geomElement.Element(kmlNS + "east"); if (boundary != null) { if (double.TryParse(boundary.Value, System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out temp)) east = temp; } boundary = geomElement.Element(kmlNS + "west"); if (boundary != null) { if (double.TryParse(boundary.Value, System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out temp)) west = temp; } if (north.HasValue && south.HasValue && east.HasValue && west.HasValue) { ESRI.ArcGIS.Client.Geometry.PointCollection pts = new PointCollection(); MapPoint mp1 = new MapPoint(west.Value, north.Value); pts.Add(mp1); MapPoint mp2 = new MapPoint(east.Value, north.Value); pts.Add(mp2); MapPoint mp3 = new MapPoint(east.Value, south.Value); pts.Add(mp3); MapPoint mp4 = new MapPoint(west.Value, south.Value); pts.Add(mp4); polygon.Rings.Add(pts); // Create symbol and use style information PolygonSymbolDescriptor sym = new PolygonSymbolDescriptor(); sym.style = kmlStyle; // Create feature descriptor from geometry and other information return new PlacemarkDescriptor() { Geometry = polygon, Symbol = sym }; } return null; }
private static void GetRendererInfo(PlacemarkDescriptor feature, KMLStyle style, out string label, out string description) { label = null; description = null; if (string.IsNullOrEmpty(style.StyleId)) { //if the feature is not using a shared style -> create an entry by graphic with the name as ident if (feature.Attributes.ContainsKey("name")) label = feature.Attributes["name"].ToString(); if (feature.Attributes.ContainsKey("description")) description = feature.Attributes["description"].ToString(); } else { //if the feature is using a shared style -> create an entry for the style label = style.StyleId; } }
/// <summary> /// Extracts a polyline from the input element and applies style information to the placemark descriptor. /// </summary> /// <param name="kmlStyle">KML Style information.</param> /// <param name="line">Polyline geometry information.</param> /// <returns>A PlacemarkDescriptor object representing the feature.</returns> private static PlacemarkDescriptor ExtractPolyLine(KMLStyle kmlStyle, XElement line) { XNamespace kmlNS = line.Name.Namespace; XElement coord = line.Element(kmlNS + "coordinates"); if (coord != null) { // Extract coordinates and build geometry ESRI.ArcGIS.Client.Geometry.PointCollection pts = ExtractCoordinates(coord); if (pts != null && pts.Count > 0) { ESRI.ArcGIS.Client.Geometry.Polyline polyline = new ESRI.ArcGIS.Client.Geometry.Polyline(); polyline.Paths.Add(pts); // Create symbol and use style information LineSymbolDescriptor sym = new LineSymbolDescriptor(); sym.style = kmlStyle; // Create feature descriptor from geometry and other information return new PlacemarkDescriptor() { Geometry = polyline, Symbol = sym }; } } return null; }