/// <summary> /// Gets the Kml of all the features in the plug-in /// </summary> /// <param name="ge">The plug-in</param> /// <returns>String of all the Kml from the plug-in - or an empty string</returns> public static string GetAllFeaturesKml(dynamic ge) { if (!IsGE(ge)) { throw new ArgumentException("ge is not of the type GEPlugin"); } StringBuilder kml = new StringBuilder(); try { dynamic children = KmlHelpers.GetChildNodes(ge); for (int i = 0; i < children.getLength(); i++) { dynamic child = children.item(i); if (child != null) { kml.Append(child.getKml()); } } } catch (RuntimeBinderException rbex) { Debug.WriteLine("GetAllFeaturesKml: " + rbex.Message, "GEHelpers"); } return(kml.ToString()); }
/// <summary> /// Called when the 'go' navigation button is clicked /// </summary> /// <param name="sender">The object that raised the event.</param> /// <param name="e">Event arguments.</param> private void NavigationButton_Click(object sender, EventArgs e) { string input = this.navigationTextBox.Text; if (input.Length > 1) { if (this.NavigationAutoCompleteMode == AutoCompleteMode.Append || this.NavigationAutoCompleteMode == AutoCompleteMode.SuggestAppend) { // add the user input to the custom 'per-session' string collection this.navigationTextBoxStringCollection.Add(input); } if (File.Exists(input)) { this.browser.FetchKmlLocal(input); } else if (GEHelpers.IsUri(input, UriKind.Absolute)) { // input is a remote file... this.browser.FetchKml(input); } else if (input.Contains(",")) { // input is possibly decimal coordinates string[] parts = input.Split(','); if (parts.Length == 2) { double latitude; double longitude; if (double.TryParse(parts[0], out latitude) && double.TryParse(parts[1], out longitude)) { KmlHelpers.CreateLookAt(this.browser.Plugin, latitude, longitude); } } } else { // finally attempt to geocode the input // fly to the point here or in javascript? this.browser.InvokeDoGeocode(input); } } }
/// <summary> /// Opens the balloon for the given feature in the plug-in using OpenFeatureBalloon() /// </summary> /// <param name="ge">the plug-in instance</param> /// <param name="feature">the feature to open a balloon for</param> /// <param name="useUnsafeHtml">Optional setting to use getBalloonHtmlUnsafe, default is false</param> /// <param name="minWidth">Optional minimum balloon width, default is 100</param> /// <param name="minHeight">Optional minimum balloon height, default is 100</param> /// <param name="maxWidth">Optional maximum balloon width, default is 800</param> /// <param name="maxHeight">Optional maximum balloon height, default is 600</param> /// <param name="setBalloon">Optionally set the balloon to be the current in the plug-in</param> /// <returns>The feature balloon or null</returns> public static dynamic OpenFeatureBalloon( dynamic ge, dynamic feature, bool useUnsafeHtml = false, int minWidth = 100, int minHeight = 100, int maxWidth = 800, int maxHeight = 600, bool setBalloon = true) { if (!IsGE(ge)) { throw new ArgumentException("ge is not of the type GEPlugin"); } string content = string.Empty; try { ge.setBalloon(null); content = useUnsafeHtml ? feature.getBalloonHtmlUnsafe() : feature.getBalloonHtml(); } catch (COMException cex) { Debug.WriteLine("OpenFeatureBalloon: " + cex.Message, "GEHelpers"); } // Scrubbing string... // see: http://code.google.com/apis/earth/documentation/balloons.html if (string.IsNullOrEmpty(content) || content == "<!--\nContent-type: mhtml-die-die-die\n\n-->") { return(null); } dynamic balloon = KmlHelpers.CreateHtmlStringBalloon( ge, content, minWidth, minHeight, maxWidth, maxHeight, setBalloon); balloon.setFeature(feature); return(balloon); }
/// <summary> /// Sets a Bounds object to the current plugin view /// </summary> /// <param name="ge">the plugin</param> /// <param name="bounds">the bounds object to create the view of</param> /// <param name="aspectRatio">Optional aspect ratio</param> /// <param name="defaultRange">Optional default range</param> /// <param name="scaleRange">Optional scale range</param> public static void SetBoundsView( dynamic ge, Bounds bounds, double aspectRatio = 1.0, double defaultRange = 1000, double scaleRange = 1.5) { try { ge.getView().setAbstractView( KmlHelpers.CreateBoundsView( ge, bounds, aspectRatio, defaultRange, scaleRange)); } catch (RuntimeBinderException rbex) { Debug.WriteLine("SetBoundsView: " + rbex.Message, "KmlHelpers"); } }
/// <summary> /// Attempts to set the view of the plug-in to the given API object /// </summary> /// <param name="ge">the plug-in</param> /// <param name="feature">the API object</param> /// <param name="boundsFallback">Optionally set whether to fallback to the bounds method</param> /// <param name="aspectRatio">Optional aspect ratio</param> /// <param name="defaultRange">Optional default range</param> /// <param name="scaleRange">Optional scale range</param> public static void FlyToObject( dynamic ge, dynamic feature, bool boundsFallback = true, double aspectRatio = 1.0, double defaultRange = 1000, double scaleRange = 1.5) { if (!IsGE(ge)) { throw new ArgumentException("ge is not of the type GEPlugin"); } try { if (feature.getAbstractView() != null) { ge.getView().setAbstractView(feature.getAbstractView()); } else if (boundsFallback) { Bounds bounds = KmlHelpers.ComputeBounds(feature); if (!bounds.IsEmpty) { KmlHelpers.SetBoundsView( ge, bounds, aspectRatio, defaultRange, scaleRange); } } } catch (COMException) { } }
/// <summary> /// Creates a KmlTreeViewNode from an KML feature /// </summary> /// <param name="feature">The KML feature to base the node on</param> /// <returns>A KmlTreeViewNode based on the feature</returns> public static KmlTreeViewNode CreateNode(dynamic feature) { // create the node from the feature KmlTreeViewNode treeNode = new KmlTreeViewNode(feature); // if the children are hidden just return the empty node... if (treeNode.KmlListStyle == ListItemStyle.CheckHideChildren) { return(treeNode); } // if the node is a network link or a document or folder // and it has children... if (treeNode.ApiType == ApiType.KmlNetworkLink || ((treeNode.ApiType == ApiType.KmlDocument || treeNode.ApiType == ApiType.KmlFolder) && KmlHelpers.HasChildNodes(feature))) { // add a place holder for the children... treeNode.Nodes.Add(new KmlTreeViewNode()); } return(treeNode); }
/// <summary> /// Asynchronous method for building a list of nodes /// </summary> /// <param name="sender">The object that raised the event.</param> /// <param name="e">Event arguments.</param> private void NodeBuilderDoWork(object sender, DoWorkEventArgs e) { // node passed in from RunWorkerAsync KmlTreeViewNode baseNode = (KmlTreeViewNode)e.Argument; baseNode.IsLoading = true; // create a stack to hold the children we will create Stack <KmlTreeViewNode> children = new Stack <KmlTreeViewNode>(); bool linkFailed = false; if (baseNode.ApiType == ApiType.KmlNetworkLink) { // fetch the network link data string url = KmlHelpers.GetUrl(baseNode.ApiObject).ToString(); dynamic data; bool rebuild = false; // can't use the new FetchAndParse with archive files... if (url.EndsWith("kmz", StringComparison.OrdinalIgnoreCase) || url.EndsWith("dae", StringComparison.OrdinalIgnoreCase)) { data = this.browser.FetchKmlSynchronous(url); } else { data = this.browser.FetchAndParse(url); rebuild = true; } if (data != null) { KmlTreeViewNode link = CreateNode(data); if (rebuild) { // The KmlObjects are created via parseKml so they need their id's need to be rebuilt // so that the Url is still present (e.g. http://foo.com/#id vs. #id) // the `baseurl` of the node is set so that any child nodes can also have there id's rebuilt // when they are created. link.Name = GenerateId(url, data.getId()); link.BaseUrl = url; } // create a new tree node from the data and push it on to the stack children.Push(link); } else { // no data, so push a new place holder node in and set the loadError flag baseNode.IsLoading = false; children.Push(new KmlTreeViewNode()); linkFailed = true; } } else { // the feature must be a KmlFolder or a KmlDocument (KmlContainer) // ...so get the child nodes from it dynamic kmlChildNodes = KmlHelpers.GetChildNodes(baseNode.ApiObject); if (kmlChildNodes != null) { int count = kmlChildNodes.getLength(); for (int i = 0; i < count; i++) { // create a new KmlTreeViewNode from each feature in the KmlContainer // and push it on to the stack. try { children.Push(CreateNode(kmlChildNodes.item(i))); } catch (COMException) { children.Clear(); return; } } } } // pass the base node, child stack and error flag as the result. e.Result = new object[] { baseNode, children, linkFailed }; }
/// <summary> /// Based on kmldomwalk.js /// see: http://code.google.com/p/earth-api-samples/source/browse/trunk/lib/kmldomwalk.js /// </summary> /// <param name="feature">The KML object to parse</param> /// <param name="callback">A delegate action, each node visited will be passed to this as the single parameter</param> /// <param name="walkFeatures">Optionally walk features, default is true</param> /// <param name="walkGeometries">Optionally walk geometries, default is false</param> /// <remarks>This method is used by <see cref="KmlTreeView"/> to build the nodes</remarks> public static void WalkKmlDom( dynamic feature, Action <dynamic> callback, bool walkFeatures = true, bool walkGeometries = false) { if (feature == null) { return; } dynamic objectContainer = null; ApiType type = GEHelpers.GetApiType(feature); switch (type) { // objects that support getFeatures case ApiType.KmlDocument: case ApiType.KmlFolder: case ApiType.KmlLayer: case ApiType.KmlLayerRoot: { if (walkFeatures) { objectContainer = feature.getFeatures(); // GESchemaObjectContainer } } break; // objects that support getGeometry case ApiType.KmlPlacemark: { if (walkGeometries) { WalkKmlDom(feature.getGeometry(), callback, walkFeatures, true); } } break; // object that support getInnerBoundaries case ApiType.KmlPolygon: { if (walkGeometries) { WalkKmlDom(feature.getOuterBoundary(), callback, walkFeatures, true); } } break; // objects that support getGeometries case ApiType.KmlMultiGeometry: { if (walkGeometries) { objectContainer = feature.getGeometries(); // GESchemaObjectContainer ////WalkKmlDom(feature.getOuterBoundary(), callback, walkFeatures, walkGeometries); } } break; } callback(feature); if (objectContainer != null && HasChildNodes(objectContainer)) { // 'GetChildNodes' returns null in some circumstances. // see: Issue 96 dynamic childNodes = KmlHelpers.GetChildNodes(objectContainer); int count = childNodes == null ? 0 : childNodes.getLength(); for (int i = 0; i < count; i++) { dynamic node = childNodes.item(i); WalkKmlDom(node, callback, walkFeatures, walkGeometries); callback(node); } } }
/// <summary> /// Computes the bounding box for the given object. /// Note that this method walks the object's DOM, so may have poor performance for large objects. /// </summary> /// <param name="kmlFeature">{KmlFeature|KmlGeometry} object The feature or geometry whose bounds should be computed</param> /// <returns>A bounds object based on the <paramref name="kmlFeature"/> (or an empty bounds object)</returns> /// <remarks> /// Based on the methods at: /// http://code.google.com/p/earth-api-utility-library/source/browse/trunk/extensions/src/dom/utils.js /// </remarks> public static Bounds ComputeBounds(dynamic kmlFeature) { Bounds bounds = new Bounds(); Action <dynamic> eachNode = feature => { ApiType type = GEHelpers.GetApiType(feature); switch (type) { case ApiType.KmlGroundOverlay: dynamic llb = feature.getLatLonBox(); if (llb != null) { double alt = feature.getAltitude(); bounds.Extend(new Coordinate(llb.getNorth(), llb.getEast(), alt)); bounds.Extend(new Coordinate(llb.getNorth(), llb.getWest(), alt)); bounds.Extend(new Coordinate(llb.getSouth(), llb.getEast(), alt)); bounds.Extend(new Coordinate(llb.getSouth(), llb.getWest(), alt)); } break; case ApiType.KmlModel: bounds.Extend(new Coordinate(feature.getLocation())); break; case ApiType.KmlLinearRing: case ApiType.KmlLineString: dynamic coords = feature.getCoordinates(); if (coords != null) { int count = coords.getLength(); for (int i = 0; i < count; i++) { bounds.Extend(new Coordinate(coords.get(i))); } } break; case ApiType.KmlCoord: case ApiType.KmlLocation: case ApiType.KmlPoint: bounds.Extend(new Coordinate(feature)); break; case ApiType.KmlPlacemark: dynamic geometry = feature.getGeometry(); if (GEHelpers.IsApiType(geometry, ApiType.KmlPoint)) { bounds.Extend(new Coordinate(geometry)); } break; } }; KmlHelpers.WalkKmlDom(kmlFeature, eachNode, true, true); return(bounds); }