//----------------------------------------------------------------------------
// NAME: KMLImporter
// DESCRIPTION: KMLImporter allows you to import Placemarks from KML and KMZ files
// DEVELOPER: ShockFire
// WEBSITE: http://shockfire.blogger.com
// VERSION: 1.08
//----------------------------------------------------------------------------
using System;
using System.IO;
using System.Net;
using System.Xml;
using System.Drawing;
using System.Threading;
using System.Reflection;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Forms;
using System.ComponentModel;
using System.Security.Cryptography;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using WorldWind;
using WorldWind.Net;
using WorldWind.Renderable;
using System.Text.RegularExpressions;
namespace KMLPlugin
{
///
/// Main plugin class
///
public class KMLImporter : WorldWind.PluginEngine.Plugin
{
private const string version = "1.08"; // The version of this plugin
private const int IconSizeConstant = 32; // The default icon size used for scaling
private RIcons KMLIcons; // Main Icon container
private MenuItem tempMenu = new MenuItem(); // Temp menu item for storing file MenuItems
private MenuItem aboutMenuItem = new MenuItem(); // About menu item
private MenuItem pluginMenuItem = new MenuItem(); // Plugin menu item (for children)
private MenuItem napalmMenuItem = new MenuItem(); // Napalm enable/disable menu item
private MenuItem labelMenuItem = new MenuItem(); // drawAllLabels enable/disable menu item
private Hashtable iconStyles = new Hashtable(); // The main Style storage
private Hashtable bitmapCache = new Hashtable(); // Hashtable to cache Bitmaps from various sources
internal string KMLPath; // Temp internal argument passing variable
private ArrayList networkLinks = new ArrayList(); // Stores created NetworkLinks
private World m_world; // The world this KMLImporter is associated with
static string KmlDirectory = Path.Combine(Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath), "kml");
#region Plugin methods
///
/// Loads this plugin. Initializes variables and adds layers and menu items
///
public override void Load()
{
// Load settings
Settings.LoadSettings(Path.Combine(KmlDirectory, "KMLImporter.xml"));
// Initialize the main Icons layer
KMLIcons = new RIcons("KML Icons");
KMLIcons.IsOn = false;
// Setup Drag&Drop functionality
m_Application.WorldWindow.DragEnter += new DragEventHandler(WorldWindow_DragEnter);
m_Application.WorldWindow.DragDrop += new DragEventHandler(WorldWindow_DragDrop);
// Add a menu item to the File menu and the Help menu
MenuItem loadMenuItem = new MenuItem();
loadMenuItem.Text = "Load KML/KMZ file...";
loadMenuItem.Click += new EventHandler(loadMenu_Click);
aboutMenuItem.Text = "About KMLImporter";
aboutMenuItem.Click += new EventHandler(aboutMenu_Click);
int mergeOrder = 0;
foreach (MenuItem menuItem in m_Application.MainMenu.MenuItems)
{
if (menuItem.Text.Replace("&", "") == "File")
{
foreach (MenuItem subMenuItem in menuItem.MenuItems)
{
subMenuItem.MergeOrder = mergeOrder;
if (subMenuItem.Text == "-")
mergeOrder = 2; // Everything after this should come after our new items
}
tempMenu.Text = menuItem.Text;
tempMenu.MergeOrder = 1; // MergeOrder 1 will have 0 before it and 2 after it
tempMenu.MenuItems.Add(loadMenuItem);
tempMenu.MenuItems.Add(new MenuItem("-"));
menuItem.MergeMenu(tempMenu);
}
if (menuItem.Text.Replace("&", "") == "Help")
menuItem.MenuItems.Add(aboutMenuItem);
}
// Napalm enable/disable menu item
bool bEnabled = Napalm.NapalmIsEnabled(KmlDirectory);
if (bEnabled)
napalmMenuItem.Text = "Disable KMLImporter autoupdate";
else
napalmMenuItem.Text = "Enable KMLImporter autoupdate";
napalmMenuItem.Click += new EventHandler(napalmMenu_Click);
pluginMenuItem.MenuItems.Add(napalmMenuItem);
// Allways show labels enable/disable menu item
labelMenuItem.Text = "Show all labels";
labelMenuItem.Checked = Settings.ShowAllLabels;
labelMenuItem.Click += new EventHandler(labelMenuItem_Click);
pluginMenuItem.MenuItems.Add(labelMenuItem);
// Add a menu item to the Plugins menu
pluginMenuItem.Text = "KMLImporter";
m_Application.PluginsMenu.MenuItems.Add(pluginMenuItem);
// Some magic to provide backward compability
Type typecontroller = typeof(MainApplication);
System.Reflection.PropertyInfo finfo = typecontroller.GetProperty("CmdArgs", BindingFlags.Static|BindingFlags.Public|BindingFlags.GetProperty);
string[] temp = null;
if(finfo != null)
{
temp = (string[])finfo.GetValue(null, null);
// If command line arguments are available, try to find one pointing to a kml/kmz file
if (temp != null)
{
foreach (string arg in temp)
{
if (!File.Exists(arg))
continue;
string fExt = Path.GetExtension(arg);
if (fExt != ".kml" && fExt != ".kmz")
continue;
LoadDiskKM(arg);
break;
}
}
}
// Add the main Icons layer to the globe
m_Application.WorldWindow.CurrentWorld.RenderableObjects.Add(KMLIcons);
//Set the currentworld
m_world = m_Application.WorldWindow.CurrentWorld;
base.Load();
}
///
/// Unloads this plugin. Removes layers and menu items
///
public override void Unload()
{
// Cleanup
Cleanup();
// Save settings
Settings.SaveSettings(Path.Combine(KmlDirectory, "KMLImporter.xml"));
// Remove the icon layer
m_Application.WorldWindow.CurrentWorld.RenderableObjects.Remove(KMLIcons);
// Disable Drag&Drop functionality
this.Application.WorldWindow.DragEnter -= new DragEventHandler(WorldWindow_DragEnter);
this.Application.WorldWindow.DragDrop -= new DragEventHandler(WorldWindow_DragDrop);
// Remove the menu items
foreach (MenuItem menuItem in m_Application.MainMenu.MenuItems)
{
if (menuItem.Text.Replace("&", "") == "File")
{
foreach (MenuItem subMenuItem in menuItem.MenuItems)
{
if (subMenuItem.Text == tempMenu.MenuItems[0].Text)
{
menuItem.MenuItems.RemoveAt(subMenuItem.Index+1);
menuItem.MenuItems.RemoveAt(subMenuItem.Index);
break;
}
}
}
if (menuItem.Text.Replace("&", "") == "Help")
menuItem.MenuItems.Remove(aboutMenuItem);
}
tempMenu.MenuItems.Clear();
m_Application.PluginsMenu.MenuItems.Remove(napalmMenuItem);
try
{
// Delete the temp kmz extract directory
if (Directory.Exists(Path.Combine(KmlDirectory, "kmz")))
Directory.Delete(Path.Combine(KmlDirectory, "kmz"), true);
foreach (string kmlfile in Directory.GetFiles(KmlDirectory, "*.kml"))
{
try
{
File.Delete(kmlfile);
}
catch (System.IO.IOException)
{ }
}
foreach (string kmzfile in Directory.GetFiles(KmlDirectory, "*.kmz"))
{
try
{
File.Delete(kmzfile);
}
catch (System.IO.IOException)
{ }
}
}
catch (Exception) {}
base.Unload();
}
#endregion
#region KMx loading methods
///
/// Loads either a KML or KMZ file from disk
///
///
private void LoadDiskKM(string filename)
{
if (Path.GetExtension(filename) == ".kmz")
{
bool shouldReturn;
string ExtractedKMLPath = ExtractKMZ(filename, out shouldReturn);
if (shouldReturn)
return;
Spawn_LoadKML(ExtractedKMLPath);
}
else
{
Spawn_LoadKML(filename);
}
KMLIcons.IsOn = true;
}
///
/// Loads a KML file in a new thread
///
/// The path to the KML file to load
private void Spawn_LoadKML(string path)
{
KMLPath = path;
ThreadStart threadStart = new ThreadStart(LoadKMLFile);
Thread kmlThread = new System.Threading.Thread(threadStart);
kmlThread.Name = "KMLImporter worker thread";
kmlThread.IsBackground = true;
kmlThread.Start();
Napalm.Update(KmlDirectory, version);
}
///
/// Loads a KML file
///
private void LoadKMLFile()
{
if (KMLPath == null || KMLIcons == null)
return;
Cleanup();
WaitMessage waitMessage = new WaitMessage();
KMLIcons.ChildObjects.Add(waitMessage);
// Create a reader to read the file
try
{
System.IO.StreamReader sr = new StreamReader(KMLPath);
// Read all data from the reader
string kml = sr.ReadToEnd();
try
{
// Load the actual kml data
LoadKML(kml, KMLIcons);
}
catch (Exception ex)
{
Utility.Log.Write("KMLImporter: " + ex.ToString());
MessageBox.Show(
String.Format(CultureInfo.InvariantCulture, "Error loading KML file '{0}':\n\n{1}", KMLPath, ex.ToString()),
"KMLImporter error",
MessageBoxButtons.OK,
MessageBoxIcon.Error,
MessageBoxDefaultButton.Button1,
base.Application.RightToLeft == RightToLeft.Yes ? MessageBoxOptions.RtlReading : MessageBoxOptions.ServiceNotification);
}
// Close the reader
sr.Close();
}
catch(Exception ex) // Catch error if stream reader failed
{
Utility.Log.Write("KMLImporter: " + ex.ToString());
MessageBox.Show(
String.Format(CultureInfo.InvariantCulture, "Error opening KML file '{0}':\n\n{1}", KMLPath, ex.ToString()),
"KMLImporter error",
MessageBoxButtons.OK,
MessageBoxIcon.Error,
MessageBoxDefaultButton.Button1,
base.Application.RightToLeft == RightToLeft.Yes ? MessageBoxOptions.RtlReading : MessageBoxOptions.ServiceNotification);
}
// Cleanup
KMLIcons.ChildObjects.Remove(waitMessage);
KMLPath = null;
}
///
/// Load given KML data
///
/// The KML data to load
internal void LoadKML(string kml, RIcons layer)
{
kml = kml.Replace("xmlns=\"http://earth.google.com/kml/2.0\"", ""); // HACK
kml = kml.Replace("xmlns='http://earth.google.com/kml/2.0'", ""); // DOUBLE HACK
kml = kml.Replace("xmlns=\"http://earth.google.com/kml/2.1\"", ""); // MULTI HACK!
kml = kml.Replace("xmlns='http://earth.google.com/kml/2.1'", ""); // M-M-M-M-M-M-M-MONSTER HACK!!!!
// Open the downloaded xml in an XmlDocument to allow for XPath searching
XmlDocument doc = new XmlDocument();
doc.LoadXml(kml);
// Try to find some sort of name for this kml from various places
XmlNode node = doc.SelectSingleNode("//Document[name]/name");
if(layer.Name == null || layer.Name.Length == 0 || layer.Name.Equals("KML Icons"))
{
if (node != null)
layer.Name = node.InnerText;
}
// Parse Style and StyleMap nodes and store them
ParseStyles(doc, KMLPath);
// Load Placemarks recursively and put them in folders
XmlNode inNode = doc.SelectSingleNode("/kml/Document");
if (inNode == null)
inNode = doc.SelectSingleNode("/kml");
/// if(inNode == null)
// inNode = doc.SelectSingleNode("/Document");
if(inNode != null)
ParseRenderables(inNode, layer, KMLPath);
}
#endregion
#region Parsing methods
///
/// Parses Styles and StyleMaps and stores them
///
/// The document to load styles from
/// The path to the KML file that is being loaded
private void ParseStyles(XmlDocument doc, string KmlPath)
{
// Load IconStyle elements and extract the images
XmlNodeList styles = doc.SelectNodes("//Style[@id]");
foreach (XmlNode xstyle in styles)
{
string name = xstyle.Attributes.GetNamedItem("id").InnerText;
if (iconStyles.ContainsKey(name))
continue;
Style style = GetStyle(xstyle, new Style(), KmlPath);
if (style != null)
iconStyles.Add(name, style);
}
// Load StyleMaps and extract the images linked
XmlNodeList stylemaps = doc.SelectNodes("//StyleMap[@id]");
foreach (XmlNode stylemap in stylemaps)
{
string name = stylemap.Attributes.GetNamedItem("id").InnerText;
if (iconStyles.ContainsKey(name))
continue;
System.Xml.XmlNode stylemapNode = stylemap.SelectSingleNode("Pair[key=\"normal\"]/styleUrl");
if (stylemapNode == null)
continue;
string normalName = stylemapNode.InnerText.Replace("#", "");
XmlNode normalNode = doc.SelectSingleNode("//Style[@id='"+normalName+"']");
Style style = GetStyle(normalNode, new Style(), KmlPath);
if (style != null)
iconStyles.Add(name, style);
}
}
///
/// Parses everything that is not a style
///
/// The node containing renderables
/// The layer to add the resulting renderables to
/// The path to the KML file that is being loaded
private void ParseRenderables(XmlNode inNode, RIcons layer, string KmlPath)
{
// Extract and set layer visibility for the current layer
XmlNode visible = inNode.SelectSingleNode("visibility");
if (visible != null)
{
if (visible.InnerText == "0")
layer.IsOn = false;
}
// Parse all Folders
ParseFolders(inNode, layer, KmlPath);
// Parse NetworkLinks
ParseNetworkLinks(inNode, layer);
// Parse GroundOverlays
ParseGroundOverlays(inNode, layer);
//Parse ScreenOverlays
ParseScreenOverlays(inNode, layer);
// Parse Placemarks
ParsePlacemarks(inNode, layer, KmlPath);
// Parse LineStrings
ParseLineStrings(inNode, layer);
// Parse Polygons
ParsePolygons(inNode, layer);
// Parse MultiGeometry
ParseMultiGeometry(inNode, layer);
// Update metadata for this layer
layer.MetaData["Child count"] = layer.ChildObjects.Count;
}
///
/// Locates Folders and parses them recursively
///
/// The XmlNode to extract the Folders from
/// The layer to add the folders to
/// The path to the KML file that is being loaded
private void ParseFolders(XmlNode inNode, RIcons layer, string KmlPath)
{
// Find Folders and initialize them recursively
XmlNodeList folders = inNode.SelectNodes("Folder");
foreach (XmlNode node in folders)
{
try
{
// Find the name of the folder
string foldername = "Folder";
XmlNode nameNode = node.SelectSingleNode("name");
if (nameNode != null)
foldername = nameNode.InnerText;
// See if the folder already exists
RIcons folder = null;
foreach (RenderableObject ro in layer.ChildObjects)
{
RIcons ricons = ro as RIcons;
if ((ricons != null) && (ro.Name == foldername))
{
folder = ricons;
}
}
// Create a new folder if it doesn't exist yet
if (folder == null)
{
folder = new RIcons(foldername);
layer.Add(folder);
}
XmlNode visibilityNode = node.SelectSingleNode("visibility");
if(visibilityNode != null)
layer.IsOn = (visibilityNode.InnerText == "1" ? true : false);
// Parse placemarks into the folder
ParseRenderables(node, folder, KmlPath);
}
catch (Exception ex)
{ Utility.Log.Write("KMLImporter: " + ex.ToString()); }
}
}
///
/// Parse NetworkLinks
///
/// The XmlNode to load NetworkLinks from
/// The layer to add NetworkLinks to
private void ParseNetworkLinks(XmlNode inNode, RIcons layer)
{
// Find NetworkLinks, initialize them and download them for the first time
XmlNodeList networklinks = inNode.SelectNodes("NetworkLink");
foreach (XmlNode node in networklinks)
{
try
{
// Find out the name for this NetworkLink
string nlName = "NetworkLink";
XmlNode nameNode = node.SelectSingleNode("name");
if (nameNode != null)
nlName = nameNode.InnerText;
// See if a folder for this NetworkLink already exists
RIcons folder = null;
foreach (RenderableObject ro in layer.ChildObjects)
{
RIcons ricons = ro as RIcons;
if ((ricons != null) && (ro.Name == nlName))
{
folder = ricons;
}
}
// Create a new folder if none is available
if (folder == null)
{
folder = new RIcons(nlName);
layer.Add(folder);
}
XmlNode visibilityNode = node.SelectSingleNode("visibility");
if(visibilityNode != null)
folder.IsOn = (visibilityNode.InnerText == "1" ? true : false);
// Find the URL to download the file from
string loadFile = null;
XmlNode hrefNode = node.SelectSingleNode("Url/href");
if ((hrefNode != null) && (hrefNode.InnerText.Length > 0))
loadFile = hrefNode.InnerText;
// Give up if no URL can be found
if (loadFile == null)
continue;
int tickSeconds = -1;
int viewSeconds = -1;
bool fired = false;
if (node.SelectSingleNode("Url/refreshMode") != null)
{
if (node.SelectSingleNode("Url/refreshMode").InnerText == "onInterval")
{
string refreshText = node.SelectSingleNode("Url/refreshInterval").InnerText;
tickSeconds = Convert.ToInt32(refreshText, CultureInfo.InvariantCulture);
}
if (node.SelectSingleNode("Url/refreshMode").InnerText == "once")
{
NetworkLink netLink = new NetworkLink(this, folder, loadFile, -1, -1);
netLink.Fire();
netLink.Dispose();
fired = true;
}
}
if ((node.SelectSingleNode("Url/viewRefreshMode") != null) && (node.SelectSingleNode("Url/viewRefreshMode").InnerText == "onStop"))
{
string refreshText = node.SelectSingleNode("Url/viewRefreshTime").InnerText;
viewSeconds = Convert.ToInt32(refreshText, CultureInfo.InvariantCulture);
}
// Initialize the actual NetworkLink object to handle updates for us
if (tickSeconds != -1 || viewSeconds != -1)
{
NetworkLink netLink = new NetworkLink(this, folder, loadFile, tickSeconds*1000, viewSeconds*1000);
netLink.Fire();
networkLinks.Add(netLink);
}
else if(!fired)
{
NetworkLink netLink = new NetworkLink(this, folder, loadFile, -1, -1);
netLink.Fire();
netLink.Dispose();
}
}
catch (Exception ex)
{ Utility.Log.Write("KMLImporter: " + ex.ToString()); }
}
}
///
/// Parses Placemarks
///
/// The node containing Placemarks
/// The layer to add the resulting icons or folders to
/// The path to the KML file that is being loaded
private void ParsePlacemarks(XmlNode inNode, RIcons layer, string KmlPath)
{
foreach (WorldWind.Renderable.RenderableObject ro in layer.ChildObjects)
{
RIcon ricon = ro as RIcon;
if (ricon != null)
ricon.HasBeenUpdated = false;
}
// Parse all Placemarks that have a name and location
XmlNodeList placemarks = inNode.SelectNodes("Placemark[name and Point]");
foreach (XmlNode node in placemarks)
{
try
{
string name = node.SelectSingleNode("name").InnerText;
RIcon update = null;
// Extract the location string for this Placemark node and split it up
string loc = node.SelectSingleNode("Point/coordinates").InnerText.Trim();
LLA lla = ParseCoordinate(loc);
string desc = null;
string uri = null;
// Extract a description and make sure it's not too long
XmlNode xnode = node.SelectSingleNode("description");
if (xnode != null)
{
string descRaw = xnode.InnerText;
uri = SearchUri(descRaw);
desc = RemoveTags(descRaw.Replace("
", "\n").Replace("
", "\n").Replace("
", "\n"));
if (desc.Length > 505)
desc = desc.Substring(0, 500) + "...";
}
float rotation = 0;
bool bRotated = false;
string rotRaw = null;
// Locate a node containing rotation
XmlNode rotNode1 = node.SelectSingleNode("Point/rotation");
if (rotNode1 != null)
rotRaw = rotNode1.InnerText;
else
{
XmlNode rotNode2 = node.SelectSingleNode("IconStyle/heading");
if (rotNode2 != null)
rotRaw = rotNode2.InnerText;
else
{
XmlNode rotNode3 = node.SelectSingleNode("Style/IconStyle/heading");
if (rotNode3 != null)
rotRaw = rotNode3.InnerText;
}
}
// If rotation was found parse it
if (rotRaw != null)
{
rotation = Convert.ToSingle(rotRaw, CultureInfo.InvariantCulture);
bRotated = true;
}
// Find a style for this icon
Style style = LocateStyle(node, KmlPath);
// Check if this icon has to be extruded
bool bExtrude = false;
XmlNode extrudeNode = node.SelectSingleNode("Point/extrude");
if (extrudeNode != null)
{
if (extrudeNode.InnerText == "1")
bExtrude = true;
}
// See if this icon already exists, and store it if it does
foreach (WorldWind.Renderable.RenderableObject ro in layer.ChildObjects)
{
RIcon ricon = ro as RIcon;
if (ricon != null)
{
if ((ro.Name == name) && ((style == null) || ((ricon.NormalIcon == style.NormalIcon) && (!ricon.HasBeenUpdated))))
{
update = ricon;
update.HasBeenUpdated = true;
break;
}
}
}
// If a previous icons has been found update it's location
if (update != null)
{
update.IsRotated = bRotated;
if (bRotated)
{
update.Rotation = Angle.FromDegrees(rotation);
}
update.Height = Double.IsNaN(style.NormalScale) ? IconSizeConstant : (int)(style.NormalScale * Math.Min(((Bitmap)bitmapCache[style.NormalIcon]).Height, IconSizeConstant));
update.Width = Double.IsNaN(style.NormalScale) ? IconSizeConstant : (int)(style.NormalScale * Math.Min(((Bitmap)bitmapCache[style.NormalIcon]).Width, IconSizeConstant));
update.Description = desc;
update.SetPosition(lla.lat, lla.lon, lla.alt);
}
else
{
// Create the icon with either the generated bitmap or the default dot
if (style != null)
{
CreateIcon(layer, name, desc, uri, lla.lat, lla.lon, lla.alt, style, bRotated, rotation, bExtrude);
}
else
{
// Use the default 'tack' icon if no style was found
string pal3Path = Path.Combine(KmlDirectory, "icons/palette-3.png");
if (File.Exists(pal3Path))
{
if (!bitmapCache.Contains(pal3Path))
bitmapCache.Add(pal3Path, (Bitmap)Bitmap.FromFile(pal3Path));
Style pinStyle = new Style(GetSubImage(new Style(pal3Path), 448, 64, 64, 64));
CreateIcon(layer, name, desc, uri, lla.lat, lla.lon, lla.alt, pinStyle, bRotated, rotation, bExtrude);
}
}
}
}
catch (Exception ex)
{ Utility.Log.Write("KMLImporter: " + ex.ToString()); }
}
// Cleanup icons that have not been updated
RemoveUnusedIcons(layer);
}
///
/// Parse Ground Overlays
///
/// The node containing Ground Overlays
/// The layer to add the resulting Ground Overlays to
private void ParseGroundOverlays(XmlNode inNode, RIcons layer)
{
// Parse all Placemarks that have a name and LineString
XmlNodeList groundOverlays = inNode.SelectNodes("GroundOverlay[name and LatLonBox]");
foreach (XmlNode node in groundOverlays)
{
// Extract the name from this node
XmlNode nameNode = node.SelectSingleNode("name");
string name = nameNode.InnerText;
XmlNode latLonBoxNode = node.SelectSingleNode("LatLonBox");
//Parse Coordinates
if(latLonBoxNode != null)
{
XmlNode northNode = latLonBoxNode.SelectSingleNode("north");
XmlNode southNode = latLonBoxNode.SelectSingleNode("south");
XmlNode westNode = latLonBoxNode.SelectSingleNode("west");
XmlNode eastNode = latLonBoxNode.SelectSingleNode("east");
double north = ConfigurationLoader.ParseDouble(northNode.InnerText);
double south = ConfigurationLoader.ParseDouble(southNode.InnerText);
double west = ConfigurationLoader.ParseDouble(westNode.InnerText);
double east = ConfigurationLoader.ParseDouble(eastNode.InnerText);
// Create GroundOverlay
WorldWind.Renderable.ImageLayer imageLayer = new ImageLayer(
name,
ParentApplication.WorldWindow.CurrentWorld,
0,
null,
south,
north,
west,
east,
1.0,
ParentApplication.WorldWindow.CurrentWorld.TerrainAccessor
);
imageLayer.DisableZBuffer = true;
imageLayer.ImageUrl = node.SelectSingleNode("Icon/href").InnerText;
XmlNode visibilityNode = node.SelectSingleNode("visibility");
if(visibilityNode != null)
imageLayer.IsOn = (visibilityNode.InnerText == "1" ? true : false);
layer.Add(imageLayer);
}
}
}
///
/// This Method parses screen overlays and adds to renderables
/// using ScreenOverlay Object
///
/// The node containing the Screen Overlay
/// The layer to add the resulting Screen Overlay to
private void ParseScreenOverlays(XmlNode inNode, RIcons layer)
{
XmlNodeList screenOverlays = inNode.SelectNodes("ScreenOverlay");
if(screenOverlays!=null)
{
foreach(XmlNode screenOverlayNode in screenOverlays)
{
XmlNode nameNode = screenOverlayNode.SelectSingleNode("name");
String name = "";
if(nameNode != null)
name = nameNode.InnerText;
XmlNode uriNode = screenOverlayNode.SelectSingleNode("Icon/href");
String uri = "http://www.apogee.com.au/logo_topleft.gif";
if(uriNode != null)
uri = uriNode.InnerText;
ScreenOverlay scoverlay = new ScreenOverlay(name,0,0,uri);
layer.Add(scoverlay);
}
}
}
///
/// Parses LineStrings
///
/// The node containing LineStrings
/// The layer to add the resulting lines to
/// The path to the KML file that is being loaded
private void ParseLineStrings(XmlNode inNode, RIcons layer)
{
// Parse all Placemarks that have a name and LineString
XmlNodeList lineStrings = inNode.SelectNodes("Placemark[name and LineString]");
foreach (XmlNode node in lineStrings)
{
// Extract the name from this node
XmlNode nameNode = node.SelectSingleNode("name");
string name = nameNode.InnerText;
Style style = null;
// get StyleUrl
XmlNode styleUrlNode = node.SelectSingleNode("styleUrl");
if(styleUrlNode != null)
{
string styleUrlKey = styleUrlNode.InnerText.Trim();
if(styleUrlKey.StartsWith("#"))
styleUrlKey = styleUrlKey.Substring(1, styleUrlKey.Length - 1);
style = (Style)iconStyles[styleUrlKey];
}
else
{
XmlNode styleNode = node.SelectSingleNode("Style");
if(styleNode != null)
style = GetStyle( styleNode, new Style(), "");
}
if(style == null)
style = new Style();
if(style.LineStyle == null)
style.LineStyle = new LineStyle();
if(style.PolyStyle == null)
style.PolyStyle = new PolyStyle();
// See if this LineString has to be extruded to the ground
bool extrude = false;
XmlNode extrudeNode = node.SelectSingleNode("LineString/extrude");
if (extrudeNode != null)
extrude = Convert.ToBoolean(Convert.ToInt16(extrudeNode.InnerText));
//Parse Coordinates
XmlNode outerRingNode = node.SelectSingleNode("LineString/coordinates");
if(outerRingNode != null)
{
// Parse the list of line coordinates
Point3d[] points = ParseCoordinates(outerRingNode);
// Create PathLine Object
LineFeature line = new LineFeature(name, m_world, points, System.Drawing.Color.FromArgb(style.LineStyle.Color.Color));
if (extrude)
{
line.Extrude = true;
XmlNode altitudeModeNode = node.SelectSingleNode("LineString/altitudeMode");
line.AltitudeMode = GetAltitudeMode(altitudeModeNode);
line.LineWidth = (float)style.LineStyle.Width.Value;
if(style.PolyStyle.Color != null)
{
line.PolygonColor = System.Drawing.Color.FromArgb(style.PolyStyle.Color.Color);
}
}
XmlNode visibilityNode = node.SelectSingleNode("visibility");
if(visibilityNode != null)
line.IsOn = (visibilityNode.InnerText == "1" ? true : false);
layer.Add(line);
}
}
}
///
/// Parses Multi Polygons
///
/// The node containing Polygons
/// The layer to add the resulting Polygons to
private void ParseMultiGeometry(XmlNode inNode, RIcons layer)
{
// Parse all Placemarks that have a name and Polygon
XmlNodeList placemarkNodes = inNode.SelectNodes("Placemark[name and MultiGeometry]");
Random rand = new Random((int)DateTime.Now.Ticks);
foreach(XmlNode placemarkNode in placemarkNodes)
{
XmlNode nameNode = placemarkNode.SelectSingleNode("name");
string name = nameNode.InnerText;
// change this to something that unifies the geometry into a single object instead of a user-accessible list
RIcons multiGeometryList = new RIcons(name);
Style style = null;
// get StyleUrl
XmlNode styleUrlNode = placemarkNode.SelectSingleNode("styleUrl");
if(styleUrlNode != null)
{
string styleUrlKey = styleUrlNode.InnerText.Trim();
if(styleUrlKey.StartsWith("#"))
styleUrlKey = styleUrlKey.Substring(1, styleUrlKey.Length - 1);
style = (Style)iconStyles[styleUrlKey];
}
else
{
XmlNode styleNode = placemarkNode.SelectSingleNode("Style");
if(styleNode != null)
style = GetStyle( styleNode, new Style(), "");
}
if(style == null)
style = new Style();
if(style.LineStyle == null)
style.LineStyle = new LineStyle();
if(style.PolyStyle == null)
style.PolyStyle = new PolyStyle();
XmlNodeList lineStringNodes = placemarkNode.SelectNodes("MultiGeometry/LineString");
foreach(XmlNode lineStringNode in lineStringNodes)
{
bool extrude = false;
XmlNode extrudeNode = lineStringNode.SelectSingleNode("extrude");
if (extrudeNode != null)
extrude = Convert.ToBoolean(Convert.ToInt16(extrudeNode.InnerText));
XmlNode coordinateNode = lineStringNode.SelectSingleNode("coordinates");
Point3d[] points = ParseCoordinates(coordinateNode);
XmlNode altitudeModeNode = lineStringNode.SelectSingleNode("altitudeMode");
AltitudeMode altitudeMode = GetAltitudeMode(altitudeModeNode);
if(points != null && points.Length > 0)
{
LineFeature line = new LineFeature(
name,
m_world,
points,
System.Drawing.Color.FromArgb(style.LineStyle.Color.Color)
);
line.AltitudeMode = altitudeMode;
if(style.PolyStyle.Color != null)
line.PolygonColor = System.Drawing.Color.FromArgb(style.PolyStyle.Color.Color);
line.LineWidth = (float)style.LineStyle.Width.Value;
line.Extrude = extrude;
multiGeometryList.Add(line);
}
}
XmlNodeList polygonNodes = placemarkNode.SelectNodes("MultiGeometry/Polygon");
foreach(XmlNode polygonNode in polygonNodes)
{
bool extrude = false;
XmlNode extrudeNode = polygonNode.SelectSingleNode("extrude");
if (extrudeNode != null)
extrude = Convert.ToBoolean(Convert.ToInt16(extrudeNode.InnerText));
XmlNode altitudeModeNode = polygonNode.SelectSingleNode("altitudeMode");
AltitudeMode altitudeMode = GetAltitudeMode(altitudeModeNode);
LinearRing outerRing = null;
LinearRing[] innerRings = null;
// Parse Outer Ring
XmlNode outerRingNode = polygonNode.SelectSingleNode("outerBoundaryIs/LinearRing/coordinates");
if (outerRingNode != null)
{
Point3d[] points = ParseCoordinates(outerRingNode);
outerRing = new LinearRing();
outerRing.Points = points;
}
// Parse Inner Ring
XmlNodeList innerRingNodes = polygonNode.SelectNodes("innerBoundaryIs");
if (innerRingNodes != null)
{
innerRings = new LinearRing[innerRingNodes.Count];
for(int i = 0; i < innerRingNodes.Count; i++)
{
Point3d[] points = ParseCoordinates(innerRingNodes[i]);
innerRings[i] = new LinearRing();
innerRings[i].Points = points;
}
}
if(outerRing != null)
{
PolygonFeature polygonFeature = new PolygonFeature(
name,
m_world,
outerRing,
innerRings,
(style.PolyStyle.Color != null ? System.Drawing.Color.FromArgb(style.PolyStyle.Color.Color) : System.Drawing.Color.Yellow)
);
polygonFeature.Extrude = extrude;
polygonFeature.AltitudeMode = altitudeMode;
polygonFeature.Outline = style.PolyStyle.Outline;
if(style.LineStyle.Color != null)
polygonFeature.OutlineColor = System.Drawing.Color.FromArgb(style.LineStyle.Color.Color);
multiGeometryList.Add(polygonFeature);
}
}
XmlNode visibilityNode = placemarkNode.SelectSingleNode("visibility");
if(visibilityNode != null)
multiGeometryList.IsOn = (visibilityNode.InnerText == "1" ? true : false);
layer.Add(multiGeometryList);
}
}
private AltitudeMode GetAltitudeMode(XmlNode altitudeModeNode)
{
if(altitudeModeNode == null || altitudeModeNode.InnerText == null || altitudeModeNode.InnerText.Length == 0)
return AltitudeMode.ClampedToGround;
if(altitudeModeNode != null && altitudeModeNode.InnerText.Length > 0)
{
switch(altitudeModeNode.InnerText)
{
case "clampedToGround":
return AltitudeMode.ClampedToGround;
case "relativeToGround":
return AltitudeMode.RelativeToGround;
case "absolute":
return AltitudeMode.Absolute;
}
}
return AltitudeMode.ClampedToGround;
}
///
/// Parses Polygons
///
/// The node containing Polygons
/// The layer to add the resulting Polygons to
private void ParsePolygons(XmlNode inNode, RIcons layer)
{
// Parse all Placemarks that have a name and Polygon
XmlNodeList polygons = inNode.SelectNodes("Placemark[name and Polygon]");
Random rand = new Random((int)DateTime.Now.Ticks);
foreach (XmlNode node in polygons)
{
// Extract the name from this node
XmlNode nameNode = node.SelectSingleNode("name");
string name = nameNode.InnerText;
Style style = null;
// get StyleUrl
XmlNode styleUrlNode = node.SelectSingleNode("styleUrl");
if(styleUrlNode != null)
{
string styleUrlKey = styleUrlNode.InnerText.Trim();
if(styleUrlKey.StartsWith("#"))
styleUrlKey = styleUrlKey.Substring(1, styleUrlKey.Length - 1);
style = (Style)iconStyles[styleUrlKey];
}
else
{
XmlNode styleNode = node.SelectSingleNode("Style");
if(styleNode != null)
style = GetStyle( styleNode, new Style(), "");
}
if(style == null)
style = new Style();
if(style.LineStyle == null)
style.LineStyle = new LineStyle();
if(style.PolyStyle == null)
style.PolyStyle = new PolyStyle();
// See if this LineString has to be extruded to the ground
bool extrude = false;
XmlNode extrudeNode = node.SelectSingleNode("Polygon/extrude");
if (extrudeNode != null)
extrude = Convert.ToBoolean(Convert.ToInt16(extrudeNode.InnerText));
XmlNode altitudeModeNode = node.SelectSingleNode("Polygon/altitudeMode");
AltitudeMode altitudeMode = GetAltitudeMode(altitudeModeNode);
LinearRing outerRing = null;
LinearRing[] innerRings = null;
// Parse Outer Ring
XmlNode outerRingNode = node.SelectSingleNode("Polygon/outerBoundaryIs/LinearRing/coordinates");
if (outerRingNode != null)
{
Point3d[] points = ParseCoordinates(outerRingNode);
Console.WriteLine(points.Length);
outerRing = new LinearRing();
outerRing.Points = points;
}
// Parse Inner Ring
XmlNodeList innerRingNodes = node.SelectNodes("Polygon/innerBoundaryIs");
if (innerRingNodes != null)
{
innerRings = new LinearRing[innerRingNodes.Count];
for(int i = 0; i < innerRingNodes.Count; i++)
{
Point3d[] points = ParseCoordinates(innerRingNodes[i]);
innerRings[i] = new LinearRing();
innerRings[i].Points = points;
}
}
if(outerRing != null)
{
PolygonFeature polygonFeature = new PolygonFeature(
name, m_world,
outerRing,
innerRings,
System.Drawing.Color.FromArgb(style.PolyStyle.Color.Color));
polygonFeature.Extrude = extrude;
polygonFeature.AltitudeMode = altitudeMode;
polygonFeature.Outline = style.PolyStyle.Outline;
if(style.LineStyle.Color != null)
polygonFeature.OutlineColor = System.Drawing.Color.FromArgb(style.LineStyle.Color.Color);
XmlNode visibilityNode = node.SelectSingleNode("visibility");
if(visibilityNode != null)
polygonFeature.IsOn = (visibilityNode.InnerText == "1" ? true : false);
layer.Add(polygonFeature);
}
}
}
///
/// Parse a list of coordinates
///
/// The node containing coordinates to parse
private Point3d[] ParseCoordinates(XmlNode coordinatesNode)
{
string coordlist = coordinatesNode.InnerText.Trim();
char[] splitters = {'\n', ' ', '\t', ','};
string[] lines = coordlist.Split(splitters);
ArrayList tokenList = new ArrayList();
ArrayList points = new ArrayList();
int tokenCount = 0;
for(int i = 0; i < lines.Length; i++)
{
string token = lines[i].Trim();
if(token.Length == 0 || token == String.Empty)
continue;
tokenCount++;
tokenList.Add(token);
if(tokenCount == 3)
{
double lon = double.Parse((string)tokenList[tokenList.Count - 3], CultureInfo.InvariantCulture);
double lat = double.Parse((string)tokenList[tokenList.Count - 2], CultureInfo.InvariantCulture);
double alt = double.Parse((string)tokenList[tokenList.Count - 1], CultureInfo.InvariantCulture);
points.Add(new Point3d(lon, lat, alt));
tokenCount = 0;
}
}
/*
for (int i = 0; i < lines.Length; i++)
{
string loc = lines[i].Trim();
if (loc == String.Empty)
continue;
LLA lla = ParseCoordinate(loc);
points.Add(new Point3d(lla.lon, lla.lat, lla.alt));
}
*/
return (Point3d[])points.ToArray(typeof(Point3d));
}
///
/// Parses a string containing a coordinate
///
/// The string containing a coordinate
/// The parsed coordinate
private LLA ParseCoordinate(string loc)
{
// get rid of a leading comma from bad user input
if(loc.StartsWith(","))
loc = loc.Substring(1, loc.Length - 1);
// get rid of a trailing comma from bad user input
if(loc.EndsWith(","))
loc = loc.Substring(0, loc.Length - 1);
string sLon="0", sLat="0", sAlt="0";
if (loc.Split(',').Length == 3) // Includes altitude
{
sLon = loc.Substring(0, loc.IndexOf(",")).Trim();
sLat = loc.Substring(loc.IndexOf(",") + 1, loc.LastIndexOf(",") - loc.IndexOf(",") - 1).Trim();
sAlt = loc.Substring(loc.LastIndexOf(",") + 1, loc.Length - loc.LastIndexOf(",") - 1).Trim();
}
else // Lat and Lon only (assume 0 altitude)
{
sLon = loc.Substring(0, loc.IndexOf(",")).Trim();
sLat = loc.Substring(loc.LastIndexOf(",") + 1, loc.Length - loc.LastIndexOf(",") - 1).Trim();
}
// Convert extracted positions to numbers
float lat = Convert.ToSingle(sLat, CultureInfo.InvariantCulture);
float lon = Convert.ToSingle(sLon, CultureInfo.InvariantCulture);
float alt = Convert.ToSingle(sAlt, CultureInfo.InvariantCulture);
LLA lla = new LLA(lat, lon, alt);
return lla;
}
#endregion
#region Style handling methods
///
/// Modifies a style with Style tags loaded from an XmlNode
///
/// The XmlNode containing override information
/// The style to override
/// The path to the KML file that is being loaded
/// The style with overridden values
private Style GetStyle(XmlNode style, Style oldStyle, string KmlPath)
{
try
{
Style overStyle = oldStyle;
bool bPalette = false;
// Determine the scale, if any, for this style
XmlNode scaleNode = style.SelectSingleNode("IconStyle/scale");
if (scaleNode != null)
overStyle.NormalScale = Convert.ToDouble(scaleNode.InnerText, CultureInfo.InvariantCulture);
// Search for style tags in location 1
XmlNode hrefNode = style.SelectSingleNode("IconStyle/Icon/href");
if (hrefNode != null)
{
string filename = hrefNode.InnerText;
if (filename.StartsWith("root://")) // Use palette bitmap
{
filename = Path.Combine(KmlDirectory, filename.Remove(0, 7));
if (File.Exists(filename))
{
bPalette = true;
overStyle.NormalIcon = GetDiskImage(filename);
}
}
else if (filename.StartsWith("http://")) // Use bitmap from the internet
{
overStyle.NormalIcon = GetWebImage(filename);
}
else if (File.Exists(Path.Combine(Path.GetDirectoryName(KmlPath), filename))) // Use a file from disk
{
overStyle.NormalIcon = GetDiskImage(Path.Combine(Path.GetDirectoryName(KmlPath), filename));
}
else if (File.Exists(Path.Combine(KmlDirectory, filename))) // Use a file from disk
{
overStyle.NormalIcon = GetDiskImage(Path.Combine(KmlDirectory, filename));
}
}
// See if we need to cut this style to a substyle
XmlNode wNode = style.SelectSingleNode("IconStyle/Icon/w");
XmlNode hNode = style.SelectSingleNode("IconStyle/Icon/h");
if (wNode != null && hNode != null)
{
int w = Convert.ToInt32(wNode.InnerText, CultureInfo.InvariantCulture);
int h = Convert.ToInt32(hNode.InnerText, CultureInfo.InvariantCulture);
int x = 0, y = 0;
XmlNode xNode = style.SelectSingleNode("IconStyle/Icon/x");
if (xNode != null)
x = Convert.ToInt32(xNode.InnerText, CultureInfo.InvariantCulture);
XmlNode yNode = style.SelectSingleNode("IconStyle/Icon/y");
if (yNode != null)
y = Convert.ToInt32(yNode.InnerText, CultureInfo.InvariantCulture);
if (bPalette)
overStyle.NormalIcon = GetSubImage(overStyle, x*2, y*2, w*2, h*2);
else
overStyle.NormalIcon = GetSubImage(overStyle, x, y, w, h);
}
// Search for style tags in a possible secondary location
XmlNode iconNode = style.SelectSingleNode("icon");
if (iconNode != null)
{
string filename = iconNode.InnerText;
if (!filename.StartsWith("http://"))
return null;
overStyle.NormalIcon = GetWebImage(filename);
}
XmlNode balloonStyleNode = style.SelectSingleNode("BalloonStyle");
if(balloonStyleNode != null)
{
BalloonStyle balloonStyle = new BalloonStyle();
XmlNode balloonTextNode = balloonStyleNode.SelectSingleNode("text");
if(balloonTextNode != null)
{
TextElement textElement = new TextElement();
textElement.Text = balloonTextNode.InnerText;
XmlNode textNodeColor = balloonTextNode.SelectSingleNode("textColor");
if(textNodeColor != null)
textElement.TextColor = new ColorElement(ParseColor(textNodeColor.InnerText));
balloonStyle.Text = textElement;
}
XmlNode balloonTextColorNode = balloonStyleNode.SelectSingleNode("textColor");
if(balloonTextColorNode != null)
balloonStyle.TextColor = new ColorElement(ParseColor(balloonTextColorNode.InnerText));
XmlNode balloonColorNode = balloonStyleNode.SelectSingleNode("color");
if(balloonColorNode != null)
balloonStyle.Color = new ColorElement(ParseColor(balloonColorNode.InnerText));
overStyle.BalloonStyle = balloonStyle;
}
XmlNode iconStyleNode = style.SelectSingleNode("IconStyle");
if(iconStyleNode != null)
{
XmlNode iconElementNode = iconStyleNode.SelectSingleNode("Icon");
IconElement iconElement = new IconElement();
if(iconElementNode != null)
{
XmlNode iconElementHrefNode = iconElementNode.SelectSingleNode("href");
if (iconElementHrefNode != null)
{
string filename = iconElementHrefNode.InnerText;
if (filename.StartsWith("root://")) // Use palette bitmap
{
filename = Path.Combine(KmlDirectory, filename.Remove(0, 7));
if (File.Exists(filename))
{
bPalette = true;
iconElement.href = GetDiskImage(filename);
}
}
else if (filename.StartsWith("http://")) // Use bitmap from the internet
{
iconElement.href = GetWebImage(filename);
}
else if (File.Exists(Path.Combine(Path.GetDirectoryName(KmlPath), filename))) // Use a file from disk
{
iconElement.href = GetDiskImage(Path.Combine(Path.GetDirectoryName(KmlPath), filename));
}
else if (File.Exists(Path.Combine(KmlDirectory, filename))) // Use a file from disk
{
iconElement.href = GetDiskImage(Path.Combine(KmlDirectory, filename));
}
}
// See if we need to cut this style to a substyle
XmlNode iconElementWNode = iconElementNode.SelectSingleNode("w");
XmlNode iconElementHNode = iconElementNode.SelectSingleNode("h");
if (iconElementWNode != null && iconElementHNode != null)
{
int w = Convert.ToInt32(wNode.InnerText, CultureInfo.InvariantCulture);
int h = Convert.ToInt32(hNode.InnerText, CultureInfo.InvariantCulture);
int x = 0, y = 0;
XmlNode xNode = iconElementNode.SelectSingleNode("x");
if (xNode != null)
x = Convert.ToInt32(xNode.InnerText, CultureInfo.InvariantCulture);
XmlNode yNode = iconElementNode.SelectSingleNode("y");
if (yNode != null)
y = Convert.ToInt32(yNode.InnerText, CultureInfo.InvariantCulture);
if (bPalette)
iconElement.href = GetSubImage(overStyle, x*2, y*2, w*2, h*2);
else
iconElement.href = GetSubImage(overStyle, x, y, w, h);
}
IconStyle iconStyle = new IconStyle(iconElement);
XmlNode iconStyleColorNode = iconStyleNode.SelectSingleNode("color");
if(iconStyleColorNode != null)
iconStyle.Color = new ColorElement(ParseColor(iconStyleColorNode.InnerText));
XmlNode iconStyleColorModeNode = iconStyleNode.SelectSingleNode("colorMode");
if(iconStyleColorModeNode != null)
{
iconStyle.ColorMode = (iconStyleColorModeNode.InnerText.ToLower() == "random" ? ColorMode.Random : ColorMode.Normal);
}
XmlNode iconStyleHeadingNode = iconStyleNode.SelectSingleNode("heading");
if(iconStyleHeadingNode != null)
iconStyle.Heading = new DecimalElement(double.Parse(iconStyleHeadingNode.InnerText, CultureInfo.InvariantCulture));
XmlNode iconStyleScaleNode = iconStyleNode.SelectSingleNode("scale");
if(iconStyleScaleNode != null)
iconStyle.Scale = new DecimalElement(double.Parse(iconStyleScaleNode.InnerText, CultureInfo.InvariantCulture));
overStyle.IconStyle = iconStyle;
}
}
XmlNode labelStyleNode = style.SelectSingleNode("LabelStyle");
if(labelStyleNode != null)
{
LabelStyle labelStyle = new LabelStyle();
XmlNode labelColorNode = labelStyleNode.SelectSingleNode("color");
if(labelColorNode != null)
labelStyle.Color = new ColorElement(ParseColor(labelColorNode.InnerText));
XmlNode labelColorModeNode = labelStyleNode.SelectSingleNode("colorMode");
if(labelColorModeNode != null)
labelStyle.ColorMode = (labelColorModeNode.InnerText.ToLower() == "random" ? ColorMode.Random : ColorMode.Normal);
XmlNode labelScaleNode = labelStyleNode.SelectSingleNode("scale");
if(labelScaleNode != null)
labelStyle.Scale = new DecimalElement(double.Parse(labelScaleNode.InnerText, CultureInfo.InvariantCulture));
overStyle.LabelStyle = labelStyle;
}
XmlNode lineStyleNode = style.SelectSingleNode("LineStyle");
if(lineStyleNode != null)
{
LineStyle lineStyle = new LineStyle();
XmlNode lineColorNode = lineStyleNode.SelectSingleNode("color");
if(lineColorNode != null)
lineStyle.Color = new ColorElement(ParseColor(lineColorNode.InnerText));
XmlNode lineColorModeNode = lineStyleNode.SelectSingleNode("colorMode");
if(lineColorModeNode != null)
lineStyle.ColorMode = (lineColorModeNode.InnerText.ToLower() == "random" ? ColorMode.Random : ColorMode.Normal);
XmlNode lineWidthNode = lineStyleNode.SelectSingleNode("width");
if(lineWidthNode != null)
lineStyle.Width = new DecimalElement(double.Parse(lineWidthNode.InnerText, CultureInfo.InvariantCulture));
overStyle.LineStyle = lineStyle;
}
XmlNode polyStyleNode = style.SelectSingleNode("PolyStyle");
if(polyStyleNode != null)
{
PolyStyle polyStyle = new PolyStyle();
XmlNode polyColorNode = polyStyleNode.SelectSingleNode("color");
if(polyColorNode != null)
polyStyle.Color = new ColorElement(ParseColor(polyColorNode.InnerText));
XmlNode polyColorModeNode = polyStyleNode.SelectSingleNode("colorMode");
if(polyColorModeNode != null)
polyStyle.ColorMode = (polyColorModeNode.InnerText.ToLower() == "random" ? ColorMode.Random : ColorMode.Normal);
XmlNode polyFillNode = polyStyleNode.SelectSingleNode("fill");
if(polyFillNode != null)
polyStyle.Fill = (polyFillNode.InnerText == "1" ? true : false);
XmlNode polyOutlineNode = polyStyleNode.SelectSingleNode("outline");
if(polyOutlineNode != null)
polyStyle.Outline = (polyOutlineNode.InnerText == "1" ? true : false);
overStyle.PolyStyle = polyStyle;
}
return overStyle;
}
catch (System.Net.WebException ex)
{
Utility.Log.Write("KMLImporter: " + ex.ToString());
}
return null;
}
private int ParseColor(string s)
{
string a = s.Substring(0,2);
string b = s.Substring(2,2);
string g = s.Substring(4,2);
string r = s.Substring(6,2);
return System.Drawing.Color.FromArgb(
int.Parse(a, System.Globalization.NumberStyles.HexNumber),
int.Parse(r, System.Globalization.NumberStyles.HexNumber),
int.Parse(g, System.Globalization.NumberStyles.HexNumber),
int.Parse(b, System.Globalization.NumberStyles.HexNumber)).ToArgb();
}
///
/// Extracts a rectangular selection from a given style
///
/// The file to extract the SubBitmap from
/// x
/// y
/// width
/// height
/// The generated bitmap
private string GetSubImage(Style style, int x, int y, int w, int h)
{
// Try using a cached version
string key = style.NormalIcon+ "|"
+x.ToString("D5", CultureInfo.InvariantCulture)
+y.ToString("D5", CultureInfo.InvariantCulture)
+w.ToString("D5", CultureInfo.InvariantCulture)
+h.ToString("D5", CultureInfo.InvariantCulture);
if (bitmapCache.ContainsKey(key))
return key;
// Create a new bitmap to draw into
Bitmap outImage = new Bitmap(w, h);
Graphics graphics = Graphics.FromImage(outImage);
// Draw a region into the newly created bitmap
RectangleF destinationRect = new RectangleF(0, 0, w, h);
System.Drawing.Bitmap bit = ((Bitmap)bitmapCache[style.NormalIcon]);
RectangleF sourceRect = new RectangleF(x, bit.Height - y - h, w, h);
graphics.DrawImage((Bitmap)bitmapCache[style.NormalIcon], destinationRect, sourceRect, GraphicsUnit.Pixel);
graphics.Flush();
// Cache the generated bitmap
bitmapCache.Add(key, outImage);
return key;
}
///
/// Retrieves a bitmap from cache or a file on disk
///
/// The filename to open the bitmap from
/// The found bitmap
private string GetDiskImage(string filename)
{
// Try using a cached version
if (bitmapCache.ContainsKey(filename))
return filename;
// Load the bitmap from disk
Bitmap bit = (Bitmap)Bitmap.FromFile(filename);
bitmapCache.Add(filename, bit);
return filename;
}
///
/// Retrieves a bitmap from cache or the web
///
/// The URI to download the bitmap from
/// The downloaded bitmap
private string GetWebImage(string filename)
{
// Try using a cached version
if (bitmapCache.ContainsKey(filename))
return filename;
// Download the file from the web
WebDownload myDownload = new WebDownload(filename);
myDownload.DownloadMemory();
// Load it into a Bitmap
Bitmap bit = (Bitmap)System.Drawing.Bitmap.FromStream(myDownload.ContentStream);
// Cache the downloaded bitmap
myDownload.Dispose();
bitmapCache.Add(filename, bit);
return filename;
}
///
/// Locates a referenced style
///
///
///
/// The located style, or null if none was found
private Style LocateStyle(XmlNode node, string KmlPath)
{
Style style = null;
// Takes care of 'normal' IconStyles
XmlNode styleNode = node.SelectSingleNode("styleUrl");
if (styleNode != null)
{
string styleUrl = styleNode.InnerText.Replace("#", "");
style = (Style)iconStyles[styleUrl];
}
// Check if there's an inline Style node
styleNode = node.SelectSingleNode("Style");
if (styleNode != null)
{
if (style != null)
style = GetStyle(styleNode, style, KmlPath);
else
style = GetStyle(styleNode, new Style(), KmlPath);
}
return style;
}
#endregion
#region Drag&Drop handling methods
///
/// Checks if the object being dropped is a kml or kmz file
///
private void WorldWindow_DragEnter(object sender, DragEventArgs e)
{
if (DragDropIsValid(e))
e.Effect = DragDropEffects.All;
}
///
/// Handles dropping of a kml/kmz file (by loading that file)
///
private void WorldWindow_DragDrop(object sender, DragEventArgs e)
{
if (DragDropIsValid(e))
{
// transfer the filenames to a string array
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (files.Length > 0 && File.Exists(files[0]))
{
LoadDiskKM(files[0]);
}
}
}
///
/// Determines if this plugin can handle the dropped item
///
private static bool DragDropIsValid(DragEventArgs e)
{
if( e.Data.GetDataPresent(DataFormats.FileDrop, false))
{
if (((string[])e.Data.GetData(DataFormats.FileDrop)).Length == 1)
{
string extension = Path.GetExtension(((string[])e.Data.GetData(DataFormats.FileDrop))[0]).ToLower(CultureInfo.InvariantCulture);
if ((extension == ".kml") || (extension == ".kmz"))
return true;
}
}
return false;
}
#endregion
#region Misc. methods
internal string ExtractKMZ(string filename, out bool bError)
{
bError = false;
FileInfo fileInfo = new FileInfo(filename);
// Create a folder 'kmz' to extract the kmz file to
string ExtractPath = Path.Combine(KmlDirectory, "kmz\\" + fileInfo.Name);
if (!Directory.Exists(ExtractPath))
Directory.CreateDirectory(ExtractPath);
// Extract the kmz file
FastZip fz = new FastZip();
fz.ExtractZip(filename, ExtractPath, "");
// Try to find the extracted kml file to load
string ExtractedKMLPath = null;
if (File.Exists(Path.Combine(ExtractPath, "doc.kml")))
ExtractedKMLPath = Path.Combine(ExtractPath, "doc.kml");
else
{
ExtractedKMLPath = GetKMLFromDirectory(ExtractPath);
if (ExtractedKMLPath == null)
bError = true;
}
return ExtractedKMLPath;
}
private string GetKMLFromDirectory(string ExtractPath)
{
string[] folders = Directory.GetDirectories(ExtractPath);
foreach (string folder in folders)
{
string tempPath = GetKMLFromDirectory(folder);
if (tempPath != null)
return tempPath;
}
string[] kmlfiles = Directory.GetFiles(ExtractPath, "*.kml");
if (kmlfiles.Length > 0)
return kmlfiles[0];
else
return null;
}
///
/// Creates a scaled icon on the globe
///
/// The name of the item
/// The description
/// The latitude for the icon
/// The longitude for the icon
/// The altitude to draw the icon at
/// The path to the bitmap to show
private void CreateIcon(RIcons layer, string Name, string Desc, string Uri, float Lat, float Lon, float Alt,
Style style, bool bRotated, float rotation, bool bExtrude)
{
// Create the icon and set initial settings
RIcon ic = new RIcon(
Name, // name
Lat, // latitude
Lon, // longitude
style.NormalIcon, // helper
Alt); // altitude
// Set optional icon settings
if (Desc != null)
ic.Description = Desc;
if (Uri != null)
ic.ClickableActionURL = Uri;
if (bRotated)
{
ic.Rotation = Angle.FromDegrees(rotation);
ic.IsRotated = true;
}
ic.m_drawGroundStick = bExtrude;
if(style.NormalIcon != null && bitmapCache.Contains(style.NormalIcon))
ic.Image = (Bitmap)bitmapCache[style.NormalIcon];
ic.Height = Double.IsNaN(style.NormalScale) ? IconSizeConstant : (int)(style.NormalScale * Math.Min(((Bitmap)bitmapCache[style.NormalIcon]).Height, IconSizeConstant));
ic.Width = Double.IsNaN(style.NormalScale) ? IconSizeConstant : (int)(style.NormalScale * Math.Min(((Bitmap)bitmapCache[style.NormalIcon]).Width, IconSizeConstant));
// Add the icon to the layer
layer.Add(ic);
}
///
/// Handles selecting and loading the selected KML/KMZ file
///
private void loadMenu_Click(object sender, EventArgs e)
{
System.Windows.Forms.OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.CheckFileExists = true;
fileDialog.Filter = "KML/KMZ files (*.kml *.kmz)|*.kml;*.kmz";
fileDialog.Multiselect = false;
fileDialog.RestoreDirectory = true;
DialogResult result = fileDialog.ShowDialog();
if (result == DialogResult.OK)
{
LoadDiskKM(fileDialog.FileName);
}
}
///
/// Cleans up used resources
///
private void Cleanup()
{
foreach (RenderableObject ro in KMLIcons.ChildObjects)
{
ro.Dispose();
}
KMLIcons.ChildObjects.Clear();
foreach (Object bit in bitmapCache)
{
Bitmap bitmap = bit as Bitmap;
if (bitmap != null)
bitmap.Dispose();
}
bitmapCache.Clear();
foreach (NetworkLink netLink in networkLinks)
{
netLink.Dispose();
}
networkLinks.Clear();
iconStyles.Clear();
}
///
/// Shows information about KMLImporter on a Form
///
private void aboutMenu_Click(object sender, EventArgs e)
{
AboutForm aboutForm = new AboutForm();
aboutForm.ShowDialog();
}
///
/// Toggles the Napalm enabled state
///
private void napalmMenu_Click(object sender, EventArgs e)
{
bool bEnabled = Napalm.NapalmChangeStatus(KmlDirectory, napalmMenuItem.Text.StartsWith("Enable"));
if (bEnabled)
napalmMenuItem.Text = "Disable KMLImporter autoupdate";
else
napalmMenuItem.Text = "Enable KMLImporter autoupdate";
}
///
/// Toggles the 'drawAllLabels' state
///
private void labelMenuItem_Click(object sender, EventArgs e)
{
labelMenuItem.Checked = Settings.ShowAllLabels = !labelMenuItem.Checked;
}
///
/// Returns the URI found in the first href tag
///
/// The string to search
/// The found URI, or null if no URI was found
private static string SearchUri(string source)
{
int i = source.IndexOf("");
uri = uri.Replace(@"'", @"'");
uri = uri.Replace(@""", "\"");
return uri;
}
return null;
}
///
/// Removes the tags from a string
///
/// The string to remove the tags from
/// The input string without tags
private static string RemoveTags(string source)
{
while (true)
{
if ((source.IndexOf("<") == -1) || (source.IndexOf(">") == -1))
break;
int start = source.IndexOf("<");
int stop = source.IndexOf(">") + 1;
int count = stop - start;
if (count < 0)
break;
source = source.Remove(start, count);
}
source = source.Replace(" ", "");
return source.Trim();
}
///
/// Removes unused icons from a given layer
///
/// The layer to remove icons from
private static void RemoveUnusedIcons(RIcons layer)
{
// Stores removed icons
ArrayList VFD = new ArrayList();
// Search for removed icons
foreach (WorldWind.Renderable.RenderableObject ro in layer.ChildObjects)
{
RIcon ricon = ro as RIcon;
if ((ricon != null) && (!ricon.HasBeenUpdated))
{
VFD.Add(ro);
}
}
// Remove all icons that were found to be removed
foreach (WorldWind.Renderable.RenderableObject ro in VFD)
{
layer.Remove(ro);
ro.Dispose();
}
}
#endregion
}
///
/// Napalm autoupdater code
///
class Napalm
{
private const string plugName = "KMLImporter";
private const string baseUrl = "http://worldwind.arc.nasa.gov";
private delegate void UpdateDelegate (string PluginDirectory, string version);
///
/// Empty private constructor, because this class only contains static methods
///
private Napalm()
{
}
///
/// Starts an async update
///
///
///
internal static void Update(string PluginDirectory, string version)
{
UpdateDelegate udel = new UpdateDelegate(WebUpdate);
udel.BeginInvoke(PluginDirectory, version, null, null);
}
///
/// Updates this plugin (and supporting files) from the internet
///
private static void WebUpdate(string PluginDirectory, string version)
{
CultureInfo icy = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
// Now go check for a new version (in the background)
try
{
if (!NapalmIsEnabled(PluginDirectory))
return;
if (!File.Exists(Path.Combine(PluginDirectory, plugName+".cs")))
return;
// Download the versioning file
string URL = String.Format(icy, "{0}/{1}/{1}_ver.txt", baseUrl, plugName);
WebClient verDownloader = new WebClient();
Stream response = new MemoryStream(verDownloader.DownloadData(URL));
// Create a reader to read the response from the server
System.IO.StreamReader sr = new StreamReader(response);
string ver = sr.ReadLine();
// Try to update if this version appears to be out of date
if (ver != version)
{
try
{
if (Convert.ToSingle(ver, CultureInfo.InvariantCulture) < Convert.ToSingle(version, CultureInfo.InvariantCulture))
return;
}
catch (Exception)
{
return;
}
// Download the new .cs file
string CsURL = String.Format(icy, "{0}/{1}/{1}.cs", baseUrl, plugName);
string CsPath = Path.Combine(PluginDirectory, String.Format(icy, "{0}.cs_", plugName));
WebClient csDownloader = new WebClient();
csDownloader.DownloadFile(CsURL, CsPath);
// Napalm v2.0 secure autoupdater block
try
{
// Create a hash of the file that was downloaded
System.IO.StreamReader streamreader = new StreamReader(CsPath);
byte[] testStringBytes = GetHashBytes(streamreader.ReadToEnd());
RSAParameters RSAKeyInfo = new RSAParameters();
System.IO.StreamReader keyreader = new System.IO.StreamReader(Path.Combine(PluginDirectory, "key"));
RSAKeyInfo.Modulus = Convert.FromBase64String(keyreader.ReadLine());
RSAKeyInfo.Exponent = Convert.FromBase64String(keyreader.ReadLine());
byte[] SignedHashValue = Convert.FromBase64String(sr.ReadLine());
RSACryptoServiceProvider RSAdecr = new RSACryptoServiceProvider();
RSAdecr.ImportParameters(RSAKeyInfo);
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSAdecr);
RSADeformatter.SetHashAlgorithm("SHA1");
if (!RSADeformatter.VerifySignature(testStringBytes, SignedHashValue))
{
Utility.Log.Write(String.Format(icy, "{0}: The file signature is not valid!", plugName));
return;
}
}
catch (Exception ex) // General signature checking / cryptography error
{
Utility.Log.Write("Signature checking error:\n" + ex);
return;
}
// Backup the current file, and replace it with the file that was just downloaded
if ((File.Exists(CsPath)) && (new FileInfo(CsPath).Length > 2))
{
// Just delete a backup of the same version if it already exists
string bcpPath = Path.Combine(PluginDirectory, plugName+"_v"+version+".cs_");
if (File.Exists(bcpPath))
File.Delete(bcpPath);
string tempPath = Path.Combine(PluginDirectory, plugName+".cs_");
string plugPath = Path.Combine(PluginDirectory, plugName+".cs");
// Move the old file out of the way and replace it with the new version
File.Move(plugPath, bcpPath);
for (int i = 0; (i < 5) && (File.Exists(tempPath)); i++)
{
try
{
File.Move(tempPath, plugPath);
}
catch (Exception)
{
System.Threading.Thread.Sleep(800);
}
}
}
// Notify the user, because we love users
string message = String.Format(icy, "The {0} plugin has been updated.\n", plugName);
message += "If you experience any problems it is recommended that you reload the plugin.\n";
MessageBox.Show(message,
plugName+" updated",
MessageBoxButtons.OK,
MessageBoxIcon.Information,
MessageBoxDefaultButton.Button1,
MessageBoxOptions.ServiceNotification);
}
else if (ver != version) // This means this is probably an internal plugin, so we can't autoupdate it
{
if (MessageBox.Show("A new version of the "+plugName+" plugin is available.\nWould you like to go to the website to download the latest version?",
"PlaneTracker update available",
MessageBoxButtons.YesNo,
MessageBoxIcon.Information,
MessageBoxDefaultButton.Button1,
MessageBoxOptions.ServiceNotification) == DialogResult.Yes)
System.Diagnostics.Process.Start("http://www.worldwindcentral.com/wiki/Add-on:KMLImporter");
}
}
catch (Exception) // We don't really care if this failed
{ }
}
///
/// Gets a byte array representing the hash of a given string
///
/// The string to hash
/// A byte array containing the hash
private static byte[] GetHashBytes(string s)
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(s);
byte[] key = Convert.FromBase64String("szLIWrCoPJ3DSWInZx5Ye7sRz0MKBG3JpmgP2KgzlcWGvuJMqNiD77DVQuIRFvgbc5UCEFRhS5Ii5khitfOXhg=="); // A random key that needs to be kept constant
byte[] hash = new HMACSHA1(key).ComputeHash(data);
return hash;
}
///
/// Checks whether Napalm is enabled
///
/// The directory that contains the key file
/// true if Napalm is enabled, false if Napalm is not enabled
internal static bool NapalmIsEnabled(string PluginDirectory)
{
string keyPath = Path.Combine(PluginDirectory, "key");
if (!File.Exists(keyPath))
return false;
StreamReader reader = new StreamReader(keyPath);
string keyline1 = reader.ReadLine();
string keyline2 = reader.ReadLine();
string keyline3 = reader.ReadLine();
reader.Close();
if ((keyline1 != null) && (keyline2 != null) && (keyline3 != null) && (keyline3.Length > 0))
return false;
else
return true;
}
///
/// Sets Napalm's enabled state
///
/// The directory that contains the key file
/// Whether to enable Napalm
/// Whether Napalm was enabled
internal static bool NapalmChangeStatus(string PluginDirectory, bool bEnabled)
{
string keyPath = Path.Combine(PluginDirectory, "key");
if (!File.Exists(keyPath))
return false;
StreamReader reader = new StreamReader(keyPath);
string keyline1 = reader.ReadLine();
string keyline2 = reader.ReadLine();
reader.Close();
if ((keyline1 == null) || (keyline2 == null))
return false;
StreamWriter writer = new StreamWriter(keyPath);
writer.WriteLine(keyline1);
writer.WriteLine(keyline2);
if (!bEnabled)
{
string[] possibleText = new string[] {
"DisableNapalm",
"You see; random characters",
"WARNING: Do not try to read the above lines out loud",
"\"Sharks with frickin' laser beams attached to their heads!\"",
"\"Oh, my, yes.\"",
"\"Windmills do not work that way! Good night!\"",
"\"I am Holly, the ship's computer, with an IQ of 6000, the same IQ as 6000 PE teachers.\"",
"\"Spoon!\"",
"\"Contrary to popular opinion, cats cannot see in the dark. They just know where you are.\""};
Random rand = new Random();
string disabledString = possibleText[rand.Next(possibleText.Length-1)];
writer.WriteLine(disabledString);
}
writer.Flush();
writer.Close();
return bEnabled;
}
}
///
/// Stores settings and has methods to save/load these settings to/from a file
///
class Settings
{
internal static bool ShowAllLabels; // Whether to draw all labels for icons
///
/// Empty private constructor, because this class only contains static methods
///
private Settings()
{
}
///
/// Loads settings from an XML file
///
/// The file to load from
internal static void LoadSettings(string file)
{
try
{
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(file);
XmlNode node;
// ShowAllLabels
node = xmldoc.SelectSingleNode("KMLImporter/ShowAllLabels");
if (node != null)
{
ShowAllLabels = System.Convert.ToBoolean(node.InnerText, CultureInfo.InvariantCulture);
}
}
catch (System.IO.IOException)
{ }
catch (System.Xml.XmlException)
{ }
}
///
/// Saves settings to an XML file
///
/// The file to save to
internal static void SaveSettings(string file)
{
try
{
// Open writer
System.Xml.XmlTextWriter xmlwriter = new System.Xml.XmlTextWriter(file, System.Text.Encoding.Default);
xmlwriter.Formatting = System.Xml.Formatting.Indented;
// Start document
xmlwriter.WriteStartDocument();
xmlwriter.WriteStartElement("KMLImporter");
// ShowAllLabels
xmlwriter.WriteStartElement("ShowAllLabels");
xmlwriter.WriteString(ShowAllLabels.ToString(CultureInfo.InvariantCulture));
xmlwriter.WriteEndElement();
// End document
xmlwriter.WriteEndElement();
xmlwriter.WriteEndDocument();
// Close writer
xmlwriter.Flush();
xmlwriter.Close();
}
catch (System.IO.IOException)
{ }
catch (System.Xml.XmlException)
{ }
}
}
///
/// Represents a NetworkLink. Updates a layer from a remote source periodically.
///
class NetworkLink
{
private string url;
private RIcons layer;
private System.Timers.Timer tickTimer = new System.Timers.Timer();
private System.Timers.Timer viewTimer = new System.Timers.Timer();
private KMLImporter owner;
private bool bUpdating = false;
bool m_firedStartup = false;
private Matrix lastView = Matrix.Identity;
private bool bViewStopped = false;
///
/// Creates and initializes a new NetworkLink
///
/// The owner of this NetworkLink
/// The layer to update the NetworkLink to
/// The URL to update the NetworkLink from
/// The interval, in milliseconds, at which the NetworkLink should update
/// The time, in milliseconds, after the view stops moving which the NetworkLink should update
internal NetworkLink(KMLImporter owner, RIcons layer, string url, int tickTime, int viewTime)
{
this.owner = owner;
this.url = url;
this.layer = layer;
if (tickTime > 0)
{
tickTimer.Interval = (double)tickTime;
tickTimer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
tickTimer.Start();
}
if (viewTime > 0)
{
viewTimer.Interval = (double)viewTime;
viewTimer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
viewTimer.Start();
}
}
///
/// Gets the visible bounding box for the application in lat/lon degrees.
///
/// An array of Angles in minx.miny,maxx, maxy order
private static string GetBBox()
{
CultureInfo ic = CultureInfo.InvariantCulture;
// TODO: Correct the ViewRange for non-square windows.
// Is is accurate horizontally but not vertically.
Angle lat = DrawArgs.Camera.Latitude;
Angle lon = DrawArgs.Camera.Longitude;
Angle vr = DrawArgs.Camera.ViewRange;
Angle North = lat + (0.5 * vr);
Angle South = lat - (0.5 * vr);
Angle East = lon + (0.5 * vr);
Angle West = lon - (0.5 * vr);
//minX(West), minY(South), maxX(East), MaxY(North)
return "BBOX=" + West.Degrees.ToString(ic) +" "+ South.Degrees.ToString(ic) +" "+ East.Degrees.ToString(ic) +" "+ North.Degrees.ToString(ic);
}
///
/// Fires off a download
///
internal void Fire()
{
if (viewTimer.Enabled)
timer_Elapsed(viewTimer, null);
else
timer_Elapsed(null, null);
}
///
/// Downloads a KML/KMZ file from the given URL
///
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (bUpdating)
return;
bUpdating = true;
try
{
if (!m_firedStartup || (layer != null && layer.IsOn))
{
string fullurl = url;
if (sender == viewTimer)
{
WorldWind.DrawArgs drawArgs = owner.ParentApplication.WorldWindow.DrawArgs;
if (!bViewStopped)
{
if (drawArgs.WorldCamera.ViewMatrix != lastView)
{
lastView = drawArgs.WorldCamera.ViewMatrix;
bUpdating = false;
return;
}
bViewStopped = true;
}
else
{
if (drawArgs.WorldCamera.ViewMatrix != lastView)
{
lastView = drawArgs.WorldCamera.ViewMatrix;
bViewStopped = false;
}
bUpdating = false;
return;
}
fullurl += (fullurl.IndexOf('?') == -1 ? "?" : "&") + GetBBox();
}
string saveFile = Path.GetFileName(url);
if(saveFile == null || saveFile.Length == 0)
saveFile = "temp.kml";
saveFile = Path.Combine(owner.PluginDirectory,saveFile);
WebDownload myClient = new WebDownload(fullurl);
myClient.DownloadFile(saveFile);
// Extract the file if it is a kmz file
string kmlFile = saveFile;
if (Path.GetExtension(saveFile) == ".kmz")
{
bool bError = false;
kmlFile = owner.ExtractKMZ(saveFile, out bError);
if (bError)
{
return;
}
}
owner.KMLPath = kmlFile;
// Create a reader to read the file
StreamReader sr = new StreamReader(kmlFile);
// Read all data from the reader
string kml = sr.ReadToEnd();
sr.Close();
try
{
// Load the actual kml data
owner.LoadKML(kml, layer);
}
catch (Exception ex)
{
Utility.Log.Write("KMLImporter: " + ex.ToString());
MessageBox.Show(
String.Format(CultureInfo.InvariantCulture, "Error loading KML file '{0}':\n\n{1}", kmlFile, ex.ToString()),
"KMLImporter error",
MessageBoxButtons.OK,
MessageBoxIcon.Error,
MessageBoxDefaultButton.Button1,
MessageBoxOptions.ServiceNotification);
}
m_firedStartup = true;
}
}
catch (Exception ex)
{
Utility.Log.Write("KMLImporter: " + ex.ToString());
}
bUpdating = false;
}
///
/// Stops the timer
///
internal void Dispose()
{
tickTimer.Stop();
viewTimer.Stop();
}
}
///
/// Helper class. Represents a KML Style or StyleMap
///
class Style
{
#region Public members
public BalloonStyle BalloonStyle = null;
public IconStyle IconStyle = null;
public LabelStyle LabelStyle = null;
public LineStyle LineStyle = null;
public PolyStyle PolyStyle = null;
internal string NormalIcon
{
get
{
return normalIcon;
}
set
{
normalIcon = value;
}
}
internal double NormalScale
{
get
{
return normalScale;
}
set
{
normalScale = value;
}
}
#endregion
#region Private members
private string normalIcon;
private double normalScale = Double.NaN;
#endregion
///
/// Creates a new Style
///
internal Style()
{
this.normalIcon = null;
}
///
/// Creates a new Style
///
/// The normal Bitmap to use for this Style
internal Style(string normalIcon)
{
this.normalIcon = normalIcon;
}
///
/// Convert a hex string to a .NET Color object.
///
/// a hex string: "FFFFFF", "#000000"
public static Color HexToColor(string hexColor)
{
string hc = ExtractHexDigits(hexColor);
if (hc.Length != 8)
{
// you can choose whether to throw an exception
//throw new ArgumentException("hexColor is not exactly 6 digits.");
return Color.White;
}
string a = hc.Substring(0, 2);
string r = hc.Substring(2, 2);
string g = hc.Substring(4, 2);
string b = hc.Substring(6, 2);
Color color = Color.White;
try
{
int ai
= Int32.Parse(a, System.Globalization.NumberStyles.HexNumber);
int ri
= Int32.Parse(r, System.Globalization.NumberStyles.HexNumber);
int gi
= Int32.Parse(g, System.Globalization.NumberStyles.HexNumber);
int bi
= Int32.Parse(b, System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(ri, gi, bi);
}
catch
{
// you can choose whether to throw an exception
//throw new ArgumentException("Conversion failed.");
return Color.White;
}
return color;
}
///
/// Extract only the hex digits from a string.
///
private static string ExtractHexDigits(string input)
{
// remove any characters that are not digits (like #)
Regex isHexDigit
= new Regex("[abcdefABCDEF\\d]+", RegexOptions.Compiled);
string newnum = "";
foreach (char c in input)
{
if (isHexDigit.IsMatch(c.ToString()))
newnum += c.ToString();
}
return newnum;
}
}
///
/// LatLonAlt
///
class LLA
{
internal float lat;
internal float lon;
internal float alt;
///
/// Creates a new instance of LLA
///
/// Latitude
/// Longitude
/// Altitude
public LLA(float lat, float lon, float alt)
{
this.lat = lat;
this.lon = lon;
this.alt = alt;
}
}
class BalloonStyle
{
public string id = null;
public TextElement Text = null;
public ColorElement TextColor = null;
public ColorElement Color = null;
}
class IconStyle
{
public string id = null;
public ColorElement Color = new ColorElement(System.Drawing.Color.White.ToArgb());
public ColorMode ColorMode = ColorMode.Normal;
public DecimalElement Heading = null;
public DecimalElement Scale = new DecimalElement(1.0);
public IconElement Icon = null;
public IconStyle(IconElement icon)
{
Icon = icon;
}
}
class LabelStyle
{
public string id = null;
public ColorElement Color = new ColorElement(System.Drawing.Color.White.ToArgb());
public ColorMode ColorMode = ColorMode.Normal;
public DecimalElement Scale = new DecimalElement(1.0);
}
class LineStyle
{
public string id = null;
public ColorElement Color = new ColorElement(System.Drawing.Color.Gray.ToArgb());
public ColorMode ColorMode = ColorMode.Normal;
public DecimalElement Width = new DecimalElement(1);
}
class PolyStyle
{
public string id = null;
public ColorElement Color = new ColorElement(System.Drawing.Color.DarkGray.ToArgb());
public ColorMode ColorMode = ColorMode.Normal;
public bool Fill = true;
public bool Outline = true;
}
class IconElement
{
public string href = null;
public IntegerElement x = null;
public IntegerElement y = null;
public IntegerElement w = null;
public IntegerElement h = null;
}
class TextElement
{
public string Text = null;
public ColorElement TextColor = null;
}
class ColorElement
{
public int Color = System.Drawing.Color.Black.ToArgb();
public ColorElement(int color)
{
Color = color;
}
}
class IntegerElement
{
public int Value = 0;
public IntegerElement(int v)
{
Value = v;
}
}
class DecimalElement
{
public double Value = 0;
public DecimalElement(double d)
{
Value = d;
}
}
enum ColorMode
{
Normal,
Random
}
///
/// Holds a collection of icons
///
class RIcons : RenderableObjectList
{
#region Private members
private Hashtable m_textures = new Hashtable(); // Texture cache
private Sprite m_sprite;
private Line m_groundStick;
private Vector2[] groundStick = new Vector2[2];
private RIcon mouseOverIcon; // The closest icon the mouse is currently over
private const int minIconZoomAltitude = 5000000;
private static int hotColor = Color.White.ToArgb();
private static int normalColor = Color.FromArgb(150,255,255,255).ToArgb();
private static int descriptionColor = Color.White.ToArgb();
private ArrayList labelRectangles = new ArrayList();
#endregion
///
/// Initializes a new instance of the class
///
/// The name to give the layer
internal RIcons(string name) : base(name)
{
this.MetaData.Add("Child count", 0);
}
///
/// Adds an icon to this layer. Deprecated.
///
internal void AddIcon(RIcon icon)
{
icon.ParentList = this;
Add(icon);
}
///
/// Draw the icon
///
protected virtual void Render(DrawArgs drawArgs, RIcon icon, Vector3 projectedPoint)
{
if (!icon.isInitialized)
icon.Initialize(drawArgs);
if(!drawArgs.WorldCamera.ViewFrustum.ContainsPoint(icon.Position))
return;
Vector3 referenceCenter = new Vector3(
(float)drawArgs.WorldCamera.ReferenceCenter.X,
(float)drawArgs.WorldCamera.ReferenceCenter.Y,
(float)drawArgs.WorldCamera.ReferenceCenter.Z);
IconTexture iconTexture = GetTexture(icon);
bool isMouseOver = icon == mouseOverIcon;
if(isMouseOver)
{
// Mouse is over
isMouseOver = true;
if(icon.isSelectable)
DrawArgs.MouseCursor = CursorType.Hand;
string description = icon.Description;
if(description==null)
description = icon.ClickableActionURL;
if(description!=null)
{
// Render description field
DrawTextFormat format = DrawTextFormat.NoClip | DrawTextFormat.WordBreak | DrawTextFormat.Bottom;
int left = 10;
if(World.Settings.ShowLayerManager)
left += World.Settings.LayerManagerWidth;
Rectangle rect = Rectangle.FromLTRB(left, 10, drawArgs.screenWidth - 10, drawArgs.screenHeight - 10 );
// Draw outline
drawArgs.defaultDrawingFont.DrawText(
m_sprite, description,
rect,
format, 0xb0 << 24 );
rect.Offset(2,0);
drawArgs.defaultDrawingFont.DrawText(
m_sprite, description,
rect,
format, 0xb0 << 24 );
rect.Offset(0,2);
drawArgs.defaultDrawingFont.DrawText(
m_sprite, description,
rect,
format, 0xb0 << 24 );
rect.Offset(-2,0);
drawArgs.defaultDrawingFont.DrawText(
m_sprite, description,
rect,
format, 0xb0 << 24 );
// Draw description
rect.Offset(1,-1);
drawArgs.defaultDrawingFont.DrawText(
m_sprite, description,
rect,
format, descriptionColor );
}
}
int color = isMouseOver ? hotColor : normalColor;
if(iconTexture==null || isMouseOver || Settings.ShowAllLabels)
{
// Render label
if(icon.Name != null)
{
// Render name field
const int labelWidth = 1000; // Dummy value needed for centering the text
if(iconTexture==null)
{
// Center over target as we have no bitmap
Rectangle realrect = drawArgs.defaultDrawingFont.MeasureString(m_sprite, icon.Name, DrawTextFormat.WordBreak, color);
realrect.X = (int)projectedPoint.X - (labelWidth>>1);
realrect.Y = (int)(projectedPoint.Y - (drawArgs.defaultDrawingFont.Description.Height >> 1));
bool bDraw = true;
foreach (Rectangle drawnrect in labelRectangles)
{
if (realrect.IntersectsWith(drawnrect))
{
bDraw = false;
break;
}
}
if (bDraw)
{
labelRectangles.Add(realrect);
drawArgs.defaultDrawingFont.DrawText(m_sprite, icon.Name, realrect, DrawTextFormat.Center, color);
}
}
else
{
// Adjust text to make room for icon
int spacing = (int)(icon.Width * 0.3f);
if(spacing>10)
spacing = 10;
int offsetForIcon = (icon.Width>>1) + spacing;
// Text to the right
Rectangle rightrect = drawArgs.defaultDrawingFont.MeasureString(m_sprite, icon.Name, DrawTextFormat.WordBreak, color);
rightrect.X = (int)projectedPoint.X + offsetForIcon;
rightrect.Y = (int)(projectedPoint.Y - (drawArgs.defaultDrawingFont.Description.Height >> 1));
// Text to the left
Rectangle leftrect = drawArgs.defaultDrawingFont.MeasureString(m_sprite, icon.Name, DrawTextFormat.WordBreak, color);
leftrect.X = (int)projectedPoint.X - offsetForIcon - rightrect.Width;
leftrect.Y = (int)(projectedPoint.Y - (drawArgs.defaultDrawingFont.Description.Height >> 1));
bool bDrawRight = true;
bool bDrawLeft = true;
foreach (Rectangle drawnrect in labelRectangles)
{
if (rightrect.IntersectsWith(drawnrect))
{
bDrawRight = false;
}
if (leftrect.IntersectsWith(drawnrect))
{
bDrawLeft = false;
}
if (!bDrawRight && !bDrawLeft)
{
break;
}
}
if (bDrawRight)
{
labelRectangles.Add(rightrect);
drawArgs.defaultDrawingFont.DrawText(m_sprite, icon.Name, rightrect, DrawTextFormat.WordBreak, color);
}
else if (bDrawLeft)
{
labelRectangles.Add(leftrect);
drawArgs.defaultDrawingFont.DrawText(m_sprite, icon.Name, leftrect, DrawTextFormat.WordBreak, color);
}
}
}
}
if (icon.m_drawGroundStick)
{
Vector3 projectedGroundPoint = drawArgs.WorldCamera.Project(icon.m_groundPoint - referenceCenter);
m_groundStick.Begin();
groundStick[0].X = projectedPoint.X;
groundStick[0].Y = projectedPoint.Y;
groundStick[1].X = projectedGroundPoint.X;
groundStick[1].Y = projectedGroundPoint.Y;
m_groundStick.Draw(groundStick, Color.Red.ToArgb());
m_groundStick.End();
}
if(iconTexture!=null)
{
float factor = 1;
if (drawArgs.WorldCamera.Altitude > minIconZoomAltitude)
factor -= (float)((drawArgs.WorldCamera.Altitude - minIconZoomAltitude) / drawArgs.WorldCamera.Altitude);
// Render icon
float xscale = factor * ((float)icon.Width / iconTexture.Width);
float yscale = factor * ((float)icon.Height / iconTexture.Height);
m_sprite.Transform = Matrix.Scaling(xscale, yscale, 0);
if (icon.IsRotated) m_sprite.Transform *= Matrix.RotationZ((float)icon.Rotation.Radians - (float) drawArgs.WorldCamera.Heading.Radians);
m_sprite.Transform *= Matrix.Translation(projectedPoint.X, projectedPoint.Y, 0);
m_sprite.Draw( iconTexture.Texture,
new Vector3(iconTexture.Width>>1, iconTexture.Height>>1,0),
Vector3.Empty,
color);
// Reset transform to prepare for text rendering later
m_sprite.Transform = Matrix.Identity;
}
}
///
/// Retrieve an icon's texture
///
private IconTexture GetTexture(RIcon icon)
{
object key = icon.Image;
if(key==null)
return null;
IconTexture res = (IconTexture)m_textures[key];
return res;
}
#region RenderableObject methods
///
/// Add a child object to this layer.
///
public override void Add(RenderableObject ro)
{
ro.ParentList = this;
m_children.Add(ro);
isInitialized = false;
}
public override void Initialize(DrawArgs drawArgs)
{
if(!isOn)
return;
if(m_sprite != null)
{
m_sprite.Dispose();
m_sprite = null;
}
if(m_groundStick != null)
{
m_groundStick.Dispose();
m_groundStick = null;
}
m_sprite = new Sprite(drawArgs.device);
m_groundStick = new Line(drawArgs.device);
// Load all textures
foreach(RenderableObject ro in m_children)
{
RIcon icon = ro as RIcon;
if(icon==null)
{
// Child is not an icon
if(ro.IsOn)
ro.Initialize(drawArgs);
continue;
}
// Child is an icon
icon.Initialize(drawArgs);
object key = null;
IconTexture iconTexture = null;
// Icon image from bitmap
if(icon.Image != null)
{
iconTexture = (IconTexture)m_textures[icon.Image];
if(iconTexture==null)
{
// Create new texture from image
key = icon.Image;
iconTexture = new IconTexture( drawArgs.device, icon.Image);
}
}
if(iconTexture==null)
// No texture set
continue;
if(key!=null)
{
// New texture, cache it
m_textures.Add(key, iconTexture);
// Use default dimensions if not set
if(icon.Width==0)
icon.Width = iconTexture.Width;
if(icon.Height==0)
icon.Height = iconTexture.Height;
}
}
// Compute mouse over bounding boxes
foreach(RenderableObject ro in m_children)
{
RIcon icon = ro as RIcon;
if(icon==null)
// Child is not an icon
continue;
if(GetTexture(icon)==null)
{
// Label only
icon.SelectionRectangle = drawArgs.defaultDrawingFont.MeasureString(null, icon.Name, DrawTextFormat.None, 0);
}
else
{
// Icon only
icon.SelectionRectangle = new Rectangle(0, 0, icon.Width, icon.Height);
}
// Center the box at (0,0)
icon.SelectionRectangle.Offset(-icon.SelectionRectangle.Width/2, -icon.SelectionRectangle.Height/2 );
}
isInitialized = true;
}
public override void Dispose()
{
base.Dispose();
if(m_textures != null)
{
foreach(IconTexture iconTexture in m_textures.Values)
iconTexture.Texture.Dispose();
m_textures.Clear();
}
if(m_sprite != null)
{
m_sprite.Dispose();
m_sprite = null;
}
if (m_groundStick != null)
{
m_groundStick.Dispose();
m_groundStick = null;
}
}
public override bool PerformSelectionAction(DrawArgs drawArgs)
{
foreach(RenderableObject ro in m_children)
{
if(!ro.IsOn)
continue;
if(!ro.isSelectable)
continue;
RIcon icon = ro as RIcon;
if(icon==null)
{
// Child is not an icon
if (ro.PerformSelectionAction(drawArgs))
return true;
continue;
}
if(!drawArgs.WorldCamera.ViewFrustum.ContainsPoint(icon.Position))
continue;
Vector3 projectedPoint = drawArgs.WorldCamera.Project(icon.Position);
if(!icon.SelectionRectangle.Contains(
DrawArgs.LastMousePosition.X - (int)projectedPoint.X,
DrawArgs.LastMousePosition.Y - (int)projectedPoint.Y ) )
continue;
try
{
Process.Start(icon.ClickableActionURL);
return true;
}
catch
{
}
}
return false;
}
public override void Render(DrawArgs drawArgs)
{
if(!isOn)
return;
if(!isInitialized)
return;
Vector3 referenceCenter = new Vector3(
(float)drawArgs.WorldCamera.ReferenceCenter.X,
(float)drawArgs.WorldCamera.ReferenceCenter.Y,
(float)drawArgs.WorldCamera.ReferenceCenter.Z);
// First render everything except icons
foreach(RenderableObject ro in m_children)
{
if(ro is RIcon)
continue;
if(!ro.IsOn)
continue;
// Child is not an icon
ro.Render(drawArgs);
}
labelRectangles.Clear();
int closestIconDistanceSquared = int.MaxValue;
RIcon closestIcon = null;
// Now render just the icons
m_sprite.Begin(SpriteFlags.AlphaBlend);
foreach(RenderableObject ro in m_children)
{
if(!ro.IsOn)
continue;
RIcon icon = ro as RIcon;
if(icon == null)
continue;
// Find closest mouse-over icon
Vector3 projectedPoint = drawArgs.WorldCamera.Project(icon.Position - referenceCenter);
int dx = DrawArgs.LastMousePosition.X - (int)projectedPoint.X;
int dy = DrawArgs.LastMousePosition.Y - (int)projectedPoint.Y;
if( icon.SelectionRectangle.Contains( dx, dy ) )
{
// Mouse is over, check whether this icon is closest
int distanceSquared = dx*dx + dy*dy;
if(distanceSquared < closestIconDistanceSquared)
{
closestIconDistanceSquared = distanceSquared;
closestIcon = icon;
}
}
if(icon != mouseOverIcon)
Render(drawArgs, icon, projectedPoint);
}
labelRectangles.Clear();
// Render the mouse over icon last (on top)
if(mouseOverIcon != null)
Render(drawArgs, mouseOverIcon, drawArgs.WorldCamera.Project(mouseOverIcon.Position - referenceCenter));
mouseOverIcon = closestIcon;
m_sprite.End();
}
#endregion
}
///
/// One icon in an RIcons layer
///
class RIcon : RenderableObject
{
#region Public members
//internal string Description; // Longer description of icon (addition to name)
internal Bitmap Image; // Icon image
internal int Width; // Icon on-screen rendered width (pixels)
internal int Height; // Icon on-screen rendered height (pixels)
internal string ClickableActionURL // On-Click browse to location
{
get
{
return m_clickableActionURL;
}
set
{
isSelectable = value != null;
m_clickableActionURL = value;
}
}
internal Rectangle SelectionRectangle; // Bounding box centered at (0,0) used to calculate whether mouse is over icon/label
internal Angle Rotation
{
get
{
return m_rotation;
}
set
{
m_rotation = value;
}
}
internal bool IsRotated
{
get
{
return m_isRotated;
}
set
{
m_isRotated = value;
}
}
internal bool m_drawGroundStick = true;
internal Vector3 m_groundPoint = new Vector3();
internal string NormalIcon;
internal bool HasBeenUpdated = true;
#endregion
#region Private members
private Angle m_rotation;
private bool m_isRotated;
private string m_clickableActionURL;
private float m_latitude;
private float m_longitude;
private float m_altitude; // The icon altitude above sea level
Matrix lastView = Matrix.Identity;
#endregion
///
/// Initializes a new instance of the class
///
/// Name of the icon
/// Latitude in decimal degrees.
/// Longitude in decimal degrees.
/// Icon height (meters) above sea level.
internal RIcon(string name, float latitude, float longitude, string normalicon, float heightAboveSurface) : base( name )
{
m_latitude = latitude;
m_longitude = longitude;
NormalIcon = normalicon;
this.m_altitude = heightAboveSurface;
}
///
/// Sets the geographic position of the icon.
///
/// Latitude in decimal degrees.
/// Longitude in decimal degrees.
/// The icon altitude above sea level.
internal void SetPosition(float latitude, float longitude, float altitude)
{
m_latitude = latitude;
m_longitude = longitude;
this.m_altitude = altitude;
// Recalculate XYZ coordinates
isInitialized = false;
}
#region RenderableObject methods
public override void Initialize(DrawArgs drawArgs)
{
float samplesPerDegree = 50.0f / ((float)drawArgs.WorldCamera.ViewRange.Degrees);
float elevation = (float)drawArgs.CurrentWorld.TerrainAccessor.GetElevationAt(m_latitude, m_longitude, samplesPerDegree);
float altitude = (float)(World.Settings.VerticalExaggeration * m_altitude + World.Settings.VerticalExaggeration * elevation);
Position = MathEngine.SphericalToCartesian(m_latitude, m_longitude,
altitude + drawArgs.WorldCamera.WorldRadius);
if (m_drawGroundStick)
{
// compute the ground point
double gselevation = drawArgs.WorldCamera.WorldRadius;
if((((WorldWindow)drawArgs.parentControl).CurrentWorld.TerrainAccessor != null) && (drawArgs.WorldCamera.Altitude < 300000))
{
gselevation += elevation * World.Settings.VerticalExaggeration;
}
m_groundPoint = MathEngine.SphericalToCartesian(m_latitude, m_longitude, gselevation);
}
isInitialized = true;
}
///
/// Disposes the icon (when disabled)
///
public override void Dispose()
{
// Nothing to dispose
}
public override bool PerformSelectionAction(DrawArgs drawArgs)
{
// Handled by parent
return false;
}
public override void Update(DrawArgs drawArgs)
{
// Handled by parent
if(drawArgs.WorldCamera.ViewMatrix != lastView && drawArgs.CurrentWorld.TerrainAccessor != null && drawArgs.WorldCamera.Altitude < 300000)
{
float samplesPerDegree = 50.0f / ((float)drawArgs.WorldCamera.ViewRange.Degrees);
float elevation = (float)drawArgs.CurrentWorld.TerrainAccessor.GetElevationAt(m_latitude, m_longitude, samplesPerDegree);
float altitude = (float)(World.Settings.VerticalExaggeration * m_altitude + World.Settings.VerticalExaggeration * elevation);
Position = MathEngine.SphericalToCartesian(m_latitude, m_longitude,
altitude + drawArgs.WorldCamera.WorldRadius);
lastView = drawArgs.WorldCamera.ViewMatrix;
}
}
public override void Render(DrawArgs drawArgs)
{
// Handled by parent
}
#endregion
}
///
/// Renders a message to the lower right corner
///
class WaitMessage : RenderableObject
{
#region Private members
private string _Text = "Please wait, loading KML file.";
private int color = Color.White.ToArgb();
private int distanceFromCorner = 25;
#endregion
///
/// Creates a new WaitMessage
///
internal WaitMessage() : base("KML WaitMessage", Vector3.Empty, Quaternion.Identity)
{
// We want to be drawn on top of everything else
this.RenderPriority = RenderPriority.Icons;
// true to make this layer active on startup, this is equal to the checked state in layer manager
this.IsOn = true;
}
#region RenderableObject methods
///
/// This is where we do our rendering
/// Called from UI thread = UI code safe in this function
///
public override void Render(DrawArgs drawArgs)
{
// Draw the current text using default font in lower right corner
Rectangle bounds = drawArgs.defaultDrawingFont.MeasureString(null, _Text, DrawTextFormat.None, 0);
drawArgs.defaultDrawingFont.DrawText(null, _Text,
drawArgs.screenWidth-bounds.Width-distanceFromCorner, drawArgs.screenHeight-bounds.Height-distanceFromCorner,
color );
}
///
/// RenderableObject abstract member (needed)
/// OBS: Worker thread (don't update UI directly from this thread)
///
public override void Initialize(DrawArgs drawArgs)
{
}
///
/// RenderableObject abstract member (needed)
/// OBS: Worker thread (don't update UI directly from this thread)
///
public override void Update(DrawArgs drawArgs)
{
}
///
/// RenderableObject abstract member (needed)
/// OBS: Worker thread (don't update UI directly from this thread)
///
public override void Dispose()
{
}
///
/// RenderableObject abstract member (needed)
/// Called from UI thread = UI code safe in this function
///
public override bool PerformSelectionAction(DrawArgs drawArgs)
{
return false;
}
#endregion
}
///
/// A Form with information about KMLImporter
///
class AboutForm : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.LinkLabel linkLabel1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label3;
private System.ComponentModel.Container components = null;
internal AboutForm()
{
InitializeComponent();
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private void linkLabel1_Click(object sender, EventArgs e)
{
try
{
System.Diagnostics.Process.Start("http://shockfire.blogspot.com/");
}
catch (Exception) {}
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.linkLabel1 = new System.Windows.Forms.LinkLabel();
this.button1 = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.label1.Location = new System.Drawing.Point(120, 8);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(160, 48);
this.label1.TabIndex = 0;
this.label1.Text = "KMLImporter";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label2
//
this.label2.Location = new System.Drawing.Point(96, 144);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(224, 24);
this.label2.TabIndex = 1;
this.label2.Text = "Created by Tim van den Hamer (ShockFire)";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// linkLabel1
//
this.linkLabel1.Location = new System.Drawing.Point(96, 176);
this.linkLabel1.Name = "linkLabel1";
this.linkLabel1.Size = new System.Drawing.Size(224, 16);
this.linkLabel1.TabIndex = 2;
this.linkLabel1.TabStop = true;
this.linkLabel1.Text = "http://shockfire.blogspot.com/";
this.linkLabel1.Click += new EventHandler(linkLabel1_Click);
this.linkLabel1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// button1
//
this.button1.Location = new System.Drawing.Point(168, 216);
this.button1.Name = "button1";
this.button1.TabIndex = 3;
this.button1.Text = "OK";
this.button1.Click += new EventHandler(button1_Click);
//
// label3
//
this.label3.Location = new System.Drawing.Point(96, 64);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(224, 64);
this.label3.TabIndex = 4;
this.label3.Text = "KMLImporter - A placemark importer for NASA World Wind allowing easy importing of kml/kmz files.";
//
// AboutForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(416, 265);
this.ControlBox = false;
this.Controls.Add(this.label3);
this.Controls.Add(this.button1);
this.Controls.Add(this.linkLabel1);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.ShowInTaskbar = false;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AboutForm";
this.Text = "About KMLImporter";
this.ResumeLayout(false);
}
#endregion
}
}