Link Google Earth Plugin to OpenLayers

I just pushed out GeoPortal v1.3 with some new features - moved to jQuery Templates for reporting, put a lot of config code in one file (contributed by Mike Wilson - thanks!), added search help, and some general bolt tightening.

One of the changes for this release was adding a Google Earth widget to the Google Maps module. I’ve been frowning at the Google Earth plugin for a while now due to it’s lack of Linux support (Google Earth runs fine on Linux for pete’s sake), but since it gives a fairly polite “We don’t support Linux yet” message and it isn’t central to the apps core functionality, I figured I’d toss it in.

On to the code. First, we need a routine to initialize the map. Fortunately I was in decent coding form when I made this routine for Streetview, so it adding Google Earth wasn’t so bad.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* Handle widget map creation and panning
* @param {string} maptype
* @param {Object} longlat
* @param {boolean} reset
*/
function widgetMaps(maptype, longlat, reset) {
var lonlatGCS = OpenLayers.Layer.SphericalMercator.inverseMercator(longlat.lon, longlat.lat);
if (maptype == "Google Street View") {
if (streetview == null || reset ) {
streetview = new google.maps.StreetViewPanorama(document.getElementById("widget-streetview"));
}

var theloc = new google.maps.LatLng(lonlatGCS.lat,lonlatGCS.lon);
streetview.setPosition(theloc);

google.maps.event.addListener(streetview, 'position_changed', function() {
var angle = computeAngle(theloc, streetview.getPosition());
streetview.setPov({heading:angle, pitch:0, zoom:1});
});
}
else if (maptype == "Google Earth") {
if (ge == null || reset) {
$("#widget-earth").empty();
google.earth.createInstance('widget-earth', function(instance){
ge = instance;
instance.getWindow().setVisibility(true);

ge.getLayerRoot().enableLayerById(ge.LAYER_BUILDINGS, true);
ge.getLayerRoot().enableLayerById(ge.LAYER_TERRAIN, true);
geZoom(longlat);

});
}
else {
geZoom(longlat);
}
}
else {
return false;
}
}

Basically you’re passing the function the type of map you want to control (in this case “Google Earth”), a longlat that represents the OpenLayer’s map center, and an optional reset parameter. The reset parameter is handy - I’ve found hiding streetview or Google Earth widgets and then making them visible again in a jQuery UI Accordion can have unexpected consequences.

So we initialize the Google Earth instance if it either doesn’t exist or we pass the reset parameter, and then hit another function to zoom. If it does exist and reset is false, we just zoom. The reason it’s set up that way is Google Earth loads asynchronously, and it’ll blow right past your createInstance and keep on trucking.

The zoom function is pretty straight forward:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Function to zoom for Google Earth. External because GE loads asynchronously.
* Note map.getScale()/40 for range and the tilt angle. Tweak those to taste.
*/
function geZoom(longlat) {
// Update the view in Google Earth
var lonlatGCS = OpenLayers.Layer.SphericalMercator.inverseMercator(longlat.lon, longlat.lat);
var lookAt = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
lookAt.setLatitude(lonlatGCS.lat);
lookAt.setLongitude(lonlatGCS.lon);
lookAt.setRange(map.getScale()/40);
lookAt.setTilt(45);
ge.getView().setAbstractView(lookAt);
}

The things to note here are setTilt and the setRange. SetTilt you should set to whatever makes you happy. I’m not thrilled with setRange. Basically I take the OpenLayers map scale and divide it by 40 (arbitrary number) to get a GE range value I liked, so it zooms in and out with the OpenLayers map. You’ll probably want to tweak that too.

We have to tell OpenLayers to call widgetMaps when the map is done with a move event.

1
2
3
4
5
6
7
8
9
10
       // Hook streetview to map move event
map.events.register("moveend", map, function(e) {
mapactive = $("#accordion-map h3").eq($('#accordion-map').accordion('option', 'active')).attr("id");
if (mapactive == "STREETVIEW") {
widgetMaps("Google Street View", map.getCenter(), false);
}
if (mapactive == "EARTH") {
widgetMaps("Google Earth", map.getCenter(), false);
}
});

The mapactive bit is checking to see if the particular widget map is visible.

In this case I’m initializing the widget map when it’s jQuery UI accordion is activated, which is as easy as:

1
2
3
4
5
6
7
8
9
10
11
/*  Run Code When Active Map Accordion Changes  */
function processAccordionMapChange(accordionValue) {
switch (accordionValue) {
case "STREETVIEW":
widgetMaps("Google Street View", map.getCenter(), true);
break;
case "EARTH":
widgetMaps("Google Earth", map.getCenter(), true);
break;
}
}

And…done. Our little Google Earth plugin widget will follow the OpenLayers map around the screen. I haven’t integrated this functionality with any live sites yet, so here’s a quick video showing how it works.

You can download the latest GeoPortal release here.