I have many large KML data-sets, which are served using a hierarchy of region-based network-links; as described in the KML reference:
Using Regions in conjunction with NetworkLinks, you can create a hierarchy of pointers, each of which points to a specific sub-Region. The <viewRefreshMode>, as shown in the following KML file, has an onRegion option, which specifies to load the Region data only when the Region is active. If you provide nested Regions with multiple levels of detail, larger amounts of data are loaded only when the user's viewpoint triggers the next load.
This works nicely when loaded in Google Earth.
I now wish to load these in an application using the Google Earth plug-in. And I need to access the loaded content via the Google Earth API; (i.e. attach click events, alter styles) to integrate the content into the application.
The issue is, I haven't found any reference to an 'on-load' event for network links. In my mind, the way this would work is:
Load top-level network link via the API, attaching a call-back function which will be invoked when the network-link is loaded.
In the call-back function, parse the KML returned by network link. For intermediate levels in the regionation hierarchy, this KML will contain only network links to the next regionation level. Load these into the plug-in via the API, again specifying the same call-back function, which will be invoked when these are loaded (i.e. when their region becomes visible).
Eventually, the KML returned will contain the actual 'content'. At this stage we load the actual content (i.e. placemarks) into the plug-in, after performing any desired modifications (e.g. attaching event-listeners, setting styles, etc).
I'm thinking the javascript would look something like the following.
Please note: this is just a rough sketch to perhaps aid in understanding my question. I am NOT asking why this code doesn't work.
//create network link
var networkLink = ge.createNetworkLink("");
networkLink.setName("Regionated hierarchy root");
// create a Link object
//the network-links contained in the kml that will be returned in this file
//are region-based; they will only be loaded when the user zooms into the relevant
//region.
var link = ge.createLink("");
link.setHref("http://foo.com/regionatedRoot.kml");
// attach the Link to the NetworkLink
networkLink.setLink(link);
//specify the callback function to be invoked when the network link is loaded
//this is is the part that doesn't actually exist; pure fiction...
networkLink.onLoad = networkLinkLoaded;
// add the NetworkLink feature to Earth
ge.getFeatures().appendChild(networkLink);
// function which will be invoked when a network-link is loaded
// i.e. when its region becomes active
function networkLinkLoaded(kml) {
//parse the kml returned for child network links,
//this will create the network link KmlObject, with a
//region specified on it.
for (childNetworkLink in parseNetworkLinks(kml)) {
//and append them, again hooking up the call-back
childNetworkLink.onLoad = networkLinkLoaded;
ge.getFeatures().appendChild(childNetworkLink);
}
//if the user has zoomed in far enough, then the kml returned will
//contain the actual content (i.e. placemarks).
//parse the kml returned for content (in this case placemarks)
for (placemark in parsePlacemarks(kml)) {
//here we would attach event-listeners to the placemark
ge.getFeatures().appendChild(placemark);
}
}
Is this possible?
Have I taken a wrong turn in my thinking? I believe I have followed recommended practices for managing large KML datasets, but I am unsure how to use these via the API.
Addendum:
As an example of the type of problem I am trying to solve:
Imagine you are building a web application using the Google Earth Plugin, and you want to display a placemark for every set of traffic-lights in the world. The placemarks should only display at an appropriate level-of-detail (e.g. when the camera is at 5km altitude). When a user clicks on a placemark, we want the web app to load statistics for that set of traffic-lights, and display them in a sidebar.
How would you engineer this?
You wouldn't need access to the object data directly to provide the functionality you require. You would handle the data load exactly like you have done, using a hierarchy of region-based network-links.
Then if your usage scenario is like the one you set out in your addendum then you would simply use the target data from the click event to load your statistical data based on the placemarks as required.
For example, you could simply set up a generic mousedown event handler on the window object and then test to see if the target is a placemark. You can add this generic listener before you load any data and it will still be fired when you click on your dynamically loaded placemarks. There is no need to attach individual event-listeners to the placemarks at all.
e.g.
window.google.earth.addEventListener(ge.getWindow(), 'mousedown', onWindowMouseDown);
var onWindowMouseDown = function(event) {
if (event.getTarget().getType() == 'KmlPlacemark') {
// get the placemark that was clicked
var placemark = event.getTarget();
// do something with it, or one of its relative objects...
var document = placemark.getOwnerDocument();
var parent = placemark.getParentNode();
// etc...
}
}
Not sure if this is quite what you want but there is a kmltree api that will:
build out the kml tree for you based on the kml given
allow you to have a 'kmlloaded' event handler
http://code.google.com/p/kmltree/
function initCB(instance){
ge = instance;
ge.getWindow().setVisibility(true);
var gex = gex = new GEarthExtensions(ge);
var tree = kmltree({
url: 'http://foo.com/regionatedRoot.kml',
gex: gex,
mapElement: $('#map3d'),
element: $('#tree'),
});
$(tree).bind('kmlLoaded', function(event, kmlObject){ //do something here });
tree.load();
}
it does require you to bring in another js api but it works pretty good and gives you some good built in functionality.
So far I haven't found anything just from the plug-in that will fire an event when the kml is loaded...
you might be able to try using fetchKml() especially if you are hardcoding that url for the link in there?
google.earth.fetchKml(ge, 'http://foo.com/regionatedRoot.kml', function(kmlObject){
//do logic here
});
Related
I would like to ask if there is a way on how to display the nameAlt of a unit instead of the unit label in displaying the indoor map.
https://learn.microsoft.com/en-us/azure/azure-maps/drawing-package-guide
In the drawing package guide, it shows the unit label but how can we also display the nameAlt?
I have not found any documentation about it. However, I have found that Azure Indoor Maps is using Mapbox GL JS as a rendering engine. For this reason, it seems that some documentation of Mapbox GL JS can be helpful until Azure Indoor Maps have more documentation. https://www.mapbox.com/blog/azure-maps-adds-data-driven-styling-powered-by-mapbox-gl
In this case, we have used the setLayoutProperty (https://docs.mapbox.com/mapbox-gl-js/api/map/#map#setlayoutproperty and https://docs.mapbox.com/mapbox-gl-js/example/language-switch/) method to change it. However, we are not sure if the property name will change in the future, Azure Indoor Maps is on preview, and this kind of change can be risky.
This is the code snippet that we have used in our project to display the nameAlt property.
map.events.add('render', function (e) {
//Used the render function to get all the layers, including the layers for indoor maps.
//Set the nameAlt in the text-field instead of name in indoor_global_unit_label
map.map.setLayoutProperty('microsoft.maps.indoor.labels_indoor.indoor_global_unit_label', 'text-field', '{nameAlt}');
});
The display name is handled by the SDK and currently there is no way to override that.
I'm trying to render the HTML for a content item to a string from within a controller action. Technically I just want to get the "body" part of it without any header/footer stuff. I want to do this so I can get a content item rendering the way I want once, and then display it as a normal orchard page OR by requesting the HTML for the content item via ajax to display it in a div in a JavaScript app. I don't want to have to manually render everything in the JavaScript as that would be duplicating the layout logic I already did. I want to re-use the bulk of the server side rendering so any changes are reflected in my normal orchard page and my JavaScript page. I've been digging into the code and searching everywhere and have gotten close but not all the way there.
I found these:
How to render shape to string?
Using FindView in Orchard
In my controller I have:
var shape = _contentManager.BuildDisplay(contentItem);
Using either of the two methods above, I can render that shape to an HTML string in my controller. All was golden. I was getting the body of that page and using it in JS. Then, I changed a placement file:
<Place Parts_Common_Body="Content:1" />
was changed to:
<Place Parts_Common_Body="/AsideFirst:1" />
The body moved where I wanted it (AsideFirst) in my normal Orchard page but disappeared from the HTML retrieved using the two methods above.
If I look at shape.Content.Items after the BuildDisplay call, I can see the item for the body is no longer there... why is it not rendering all the zones? Or, I guess a more specific question is why is the BuildDisplay method not building the complete shape? Is there a way I can make this work?
I tried a million different things and eventually got this working. Not sure I totally get it yet, but I think the problem had to do with the fact that I was using shape.Content and I'd moved stuff out of the Content zone. And maybe when I was looking at what the BuildDisplay method was returning I was just not looking at some newly created zone that actually did had the stuff I thought was missing. Clearly I need to learn more about zones and shapes... Anyway, I have a new zone called "MainInfo" now that I created in a placement file. I get a MainInfo property on the main shape returned form BuildDisplay and pass shape.MainInfo to the view rendering code and all seems to be working well now.
I am trying to build out a harness for a page so that we can write tests against it. What I would like to be able to do is use a CSS selector to find the given element or elements instead of manually modifying the SearchProperties or FilterProperties.For a web test the CSS Selector seems far more intuitive then the SearchProperties do. Is there some mechanism for doing this that I am simply not seeing?
Try this...
https://github.com/rpearsondev/CodedUI.jQueryExtensions/
It adds extension methods to the BrowserWindow object...
var example1 = browser.JQuerySelect<HtmlHyperlink>('a.class1');
var example2 = browser.JQuerySelect<HtmlListItem>('li.class2');
However, I will let you know I'm having issues with it complaining about casting errors regularly.
Try browserWindow.executeJavascript if you return a control you found via css/xpath it returns the relevant uiControl object
const string javascript = "document.querySelector('{0}');";
var bw = BrowserWindow.Launch(new Uri("http://rawstack.azurewebsites.net"));
string selector = "[ng-model='filterOptions.filterText']";
var control = bw.ExecuteScript(string.Format(javascript,selector));
HtmlEdit filter= control as HtmlEdit;
filter.Text = "Alien";
As sjdirect noted, the jQuery extensions are probably the way to go if you want to use those type of selectors.
However, it seems that you may be interested in some abstraction that doesn't require directly setting search / filter properties on the UITestControl objects.
There are good abstractions that do not use the same selectors as jQuery, but provide a readable, consistent approach for finding elements in the page and interacting with them.
I would recommend also looking into Code First and CodedUI Fluent (I wrote the fluent extensions) or even CodedUI Enhanced (CUITe).
These provide query support for that looks like (from CUITe):
// Launch the web browser and navigate to the homepage
BrowserWindowUnderTest browserWindow = BrowserWindowUnderTest.Launch("https://website.com");
// Enter the first name
browserWindow.Find<HtmlEdit>(By.Id("FirstName")).Text = "John";
// Enter the last name
browserWindow.Find<HtmlPassword>(By.Id("LastName")).Text ="Doe";
// Click the Save button
browserWindow.Find<HtmlInputButton>(By.Id("Save")).Click();
we are developing a tour with GEPlugin and I have some questions..
The tour is similar to Tour.
It starts and navigates to a Placemark where its stop and shows a balloon with a specific information for the Placemark.
I have defined the balloon using an iframe inside the description in kml, that points to a php script.
I can load the css and javascript if I click the placemark (via click event attached) however I canĀ“t make it show up when the tour stops setting
<Change><Placemark><gx:balloonVisibility>1</gx:balloonVisibility></Placemark></Change>
in the kml file.
Is there a way to make It show up the way I want?
Thanks!
Ok, I have found the solution.
I attached the event balloonopening (looked all over around the documentation) to the instance of the google earth plugin, get the feature and set the specific balloon.
Here goes some code example:
function showBalloon(placemark){
var content = placemark.getBalloonHtmlUnsafe();
var balloon = ge.createHtmlStringBalloon('');
balloon.setFeature(placemark);
balloon.setContentString(content);
balloon.setCloseButtonEnabled(false);
ge.setBalloon(balloon);
}
google.earth.addEventListener(ge, 'balloonopening', function(){
var placemark = ge.getBalloon().getFeature();
showBalloon(placemark);
});
Thanks!
Google Earth defaults to the "Borders and Labels" (i.e, showing country outlines) on. I have KMZ/KML files where I want the borders turned off. I've searched documents describing the KML and can't find anything related to layers.
I also tried saving KMZ files with and without the borders turned on, and they are identical.
Anyone know how to access the layers through the KML API?
You can't yet turn on/off layers in Google Earth directly via KML. Currently it's a manual action done by user of Google Earth.
However, there is currently a directive to turn on/off historical imagery, streetview, and sun light modes.
https://developers.google.com/kml/documentation/kmlreference#gxvieweroptions
But you can turn the layers on/off easily via the GE API:
To enable a specific layer:
ge.getLayerRoot().enableLayerById(ge.LAYER_NAME, true)
To disable a layer:
ge.getLayerRoot().enableLayerById(ge.LAYER_NAME, false);
Reference: https://developers.google.com/earth/documentation/layers#layers
The API doesn't have constants defined for the individual layers within ge.LAYER_BORDERS but if you look in the KML file, each "Folder" node has an ID, so, as JasonM1 says, you can use enableLayerById for each one you want to switch off, having switched on the top containing node (ge.LAYER_BORDERS in this case).
Here's the code i used to clean up the "Borders and Labels" layer via the API:
ge.getLayerRoot().enableLayerById(ge.LAYER_BORDERS, true);
ge.getLayerRoot().enableLayerById("530286de-c7b3-11dc-938c-dd553d8c9902", false); // internation borders
ge.getLayerRoot().enableLayerById("cfdff130-394a-11e0-98fa-dd5743f1dfd8", false); // coastline
ge.getLayerRoot().enableLayerById("533444c6-c7b3-11dc-b02e-dd553d8c9902", false); // 1st level borders too
ge.getLayerRoot().enableLayerById("534ab67a-c7b3-11dc-a2a7-dd553d8c9902", false); // 2nd level borders too
ge.getLayerRoot().enableLayerById("e2334aaa-e853-11df-9192-77880e18aa7d", false); // geographic features
p.s. I can't seem to find where i got the "borders and labels.kml" file now, but it was probably here somewhere, will continue looking...