2012-10-18 94 views
1

我試圖鼠標事件附加到使用此代碼的GroundOverlay功能:附加事件在谷歌地球的地面疊加層插件API

 var groundOverlay = ge.createGroundOverlay(''); 
     var icon = ge.createIcon(''); 
     icon.setHref("http://www.google.com/logos/earthday08.gif"); 
     groundOverlay.setIcon(icon); 
     var latLonBox = ge.createLatLonBox(''); 
     latLonBox.setBox(48.80, 48.75, -121.77, -121.85, 0); 
     groundOverlay.setLatLonBox(latLonBox); 
     ge.getFeatures().appendChild(groundOverlay);  

      google.earth.addEventListener(groundOverlay, 'click', function(e) { 
        e.preventDefault(); 
        console.log("hello"); 
        }); 

但點擊顯示沒有結果。任何想法爲什麼?

謝謝! Bill

+0

我添加了一個遲到的答案,因爲我剛剛將問題標記爲與您的副本相同。目前的答案有幾個問題,所以我添加了一個有效的例子。 – Fraser

回答

1

由於KmlGroundOverlays還沒有產生鼠標事件,另一個解決辦法是從世界各地收到的鼠標事件,然後確定是否鼠標是在覆蓋層中(一個「點擊測試」) 。以下是使用mouseMove事件執行此操作的代碼的起點。它會生成兩個重疊的疊加層(一個旋轉),並將鼠標下的疊加層引入表面(通過操作drawingOrder)。

它在自己的結構中維護關於疊加層的信息(而不是走KML,這可能是可能的)。

我無法找到一種方法從GE獲取旋轉覆蓋圖的頂點的位置,因此旋轉是使用笛卡爾逼近法在代碼中完成的。此代碼可能會在極點或邊界或大型多邊形上打破。

一旦頂點是已知的,是在鼠標移動運行命中測試在How can I determine whether a 2D Point is within a Polygon?

基於一個很好的討論,讓這個代碼說明了開始的想法:

  • 如何合成鼠標從事件覆蓋(解決方案直到KML覆蓋可以生成鼠標事件)
  • 如何近似旋轉覆蓋圖的頂點(查找覆蓋圖的位置)
  • 對o verlay(從上述的參考)
  • 使用DRAWORDER帶來的重疊覆蓋在表面上的鼠標懸停

有在http://jsfiddle.net/pudkg/

這裏,演示的代碼& HTML:

<!DOCTYPE html> 

<html> 
<head> 

<script type="text/javascript" src="https://www.google.com/jsapi"> </script> 

<script type="text/javascript"> 
var ge; 
var overlays = []; // record information about overlays (filled by 'addOverlay') 
var drawOrder = 0; // drawOrder value of topmost overlay 
google.load("earth", "1"); 

function Point (lat, lon) { 
    this.lat = lat; 
    this.lon = lon; 
} 

function Overlay (groundOverlay, points, drawOrder) { 
    this.overlay = groundOverlay; // KML object 
    this.points = points;   // array of Points (vertices of overlay) 
    this.drawOrder = drawOrder; // integer, higest displayed topmost 
} 
Overlay.prototype.hitTest = function (lat, lon) { // return true if lat/lon is within overlay 
    // Based upon https://stackoverflow.com/questions/217578/point-in-polygon-aka-hit-test 
    var isInside = false; 
    var minLon = this.points[0].lon, maxLon = this.points[0].lon; 
    var minLat = this.points[0].lat, maxLat = this.points[0].lat; 
    for (var n = 1; n < this.points.length; n++) { 
     var q = this.points[n]; 
     minLon = Math.min(q.lon, minLon); 
     maxLon = Math.max(q.lon, maxLon); 
     minLat = Math.min(q.lat, minLat); 
     maxLat = Math.max(q.lat, maxLat); 
    } 
    if (lon < minLon || lon > maxLon || lat < minLat || lat > maxLat) 
     return false; 

    var i = 0, j = this.points.length - 1; 
    for (i, j; i < this.points.length; j = i++) 
     if ((this.points[i].lat > lat) != (this.points[j].lat > lat) && 
      lon < (this.points[j].lon - this.points[i].lon) * (lat - this.points[i].lat)/
      (this.points[j].lat - this.points[i].lat) + this.points[i].lon) 
     isInside = !isInside; 
    return isInside; 
    } 

function init() { 
    google.earth.createInstance('map3d', initCB, failureCB); 
} 

function initCB(e) { 
    ge = e; 
    ge.getWindow().setVisibility(true); 

    var lat = 37.204193; 
    var lon = -112.934429; 
    var dlat = 0.003; 
    var dlon = 0.005; 
    var offset = 0.004; 

    var la = ge.createLookAt(''); // position camera 
    la.set(lat, lon, 0, ge.ALTITUDE_RELATIVE_TO_GROUND, 0, 30, 2000); 
    ge.getView().setAbstractView(la); 

    for (var i = 0; i < 2; i++) // generate two overlays, overlapping; second one rotated 
    addOverlay('http://www.google.com/logos/earthday08.gif', 
     lat + dlat + offset*i, lat - dlat + offset*i, 
     lon + dlon + offset*i, lon - dlon + offset*i, 30*i); 
    // KML overlays can't (yet) generate mouse events, so look for events from globe 
    google.earth.addEventListener(ge.getGlobe(), 'mousemove', function(event) { 
    var lat = event.getLatitude(); 
    var lon = event.getLongitude(); 
    // show that a move event was received: 
    document.getElementById('logMove').innerHTML = event.getLatitude(); 
    topmost = -1, zMax = 0; // find topmost overlay 
    for (var i = overlays.length - 1; i >= 0; i--) 
     if (overlays[i].hitTest(lat, lon)) { // if mouse is within overlays[i] 
     document.getElementById('logHit').innerHTML = i + '; ' + overlays[i].drawOrder; 
     if (overlays[i].drawOrder > zMax) { // if this overlay is higher than any previous 
      topmost = i; 
      zMax = overlays[i].drawOrder; 
     } 
     } 
    if ((topmost >= 0) && (overlays[topmost].drawOrder < drawOrder)) { 
     // if in an overlay and it is buried, make it top-most 
     overlays[topmost].overlay.setDrawOrder(++drawOrder); 
     overlays[topmost].drawOrder = drawOrder; // update local structure 
    } 
    document.getElementById('logOver').innerHTML = topmost + '; ' + zMax; 
    }); 
} 

function addOverlay(url, north, south, east, west, rotation) { 
    var groundOverlay = ge.createGroundOverlay(''); // create overlay 
    var icon = ge.createIcon(''); 
    icon.setHref(url); 
    groundOverlay.setIcon(icon); 
    var latLonBox = ge.createLatLonBox(''); 
    latLonBox.setBox(north, south, east, west, rotation); 
    groundOverlay.setLatLonBox(latLonBox); 
    groundOverlay.setDrawOrder(++drawOrder); 
    ge.getFeatures().appendChild(groundOverlay); 
    var points = []; // figure out lat/lon of the corners of the overlay 
    var sinTheta = Math.sin(rotation * Math.PI/180.0); 
    var cosTheta = Math.cos(rotation * Math.PI/180.0); 
    // rotation is about the center of the overlay; find midpoint: 
    var midPoint = new Point((north + south)/2, (west + east)/2); 
    // To do cartesian rotation, need to consider that the distance between 
    // units of longitude diminish as one goes north, to zero at pole: 
    var cosLat = Math.cos(midPoint.lat * Math.PI/180.0); // longitude compression factor 
    west = (west - midPoint.lon) * cosLat, east = (east - midPoint.lon) * cosLat; 
    north -= midPoint.lat, south -= midPoint.lat; 
    // use cartesian rotation (good enough approximation for UI away from pole, boundaries) 
    // after rotation, restore (expand) longitudes by compression factor 
    points.push(new Point(midPoint.lat + west * sinTheta + north * cosTheta, 
     midPoint.lon + (west * cosTheta - north * sinTheta)/cosLat)); 
    points.push(new Point(midPoint.lat + east * sinTheta + north * cosTheta, 
     midPoint.lon + (east * cosTheta - north * sinTheta)/cosLat)); 
    points.push(new Point(midPoint.lat + east * sinTheta + south * cosTheta, 
     midPoint.lon + (east * cosTheta - south * sinTheta)/cosLat)); 
    points.push(new Point(midPoint.lat + west * sinTheta + south * cosTheta, 
     midPoint.lon + (west * cosTheta - south * sinTheta)/cosLat)); 
    overlays.push(new Overlay(groundOverlay, points, drawOrder)); 
} 

function failureCB(errorCode) { 
    alert("GE init fail"); 
} 

google.setOnLoadCallback(init); 
</script> 
</head> 

<body> 

<div id=map3d style='height: 400px; width: 600px'></div> 
<p>Mouse over the two overlays. The one under the mouse should come to surface.</p> 
<p>Latitude of mouse: <span id=logMove></span></p> 
<p>Index of last overlay hit; its drawOrder: <span id=logHit></span></p> 
<p>Index of topmost overlay; max drawOrder: <span id=logOver></span></p> 

</body> 
</html> 
0

您正試圖做得太快。 首先,您需要將事件監聽器添加到GE插件

所以更換

google.earth.addEventListener(groundOverlay, 'click', function(e) { 

google.earth.addEventListener(ge.getGlobe(), 'click', function(e) { 

有時它是更好地使用ge.getWindow()ge.getView()這取決於你在做什麼編輯:在評論後指出錯誤

而不是檢測點擊GroundOverlay,創建一個Polygon覆蓋完全相同的地形,並檢測點擊它。

創建Polygon時,我會將其設置爲「不可見」,但將不透明度設置爲零。然後在我前面的回答,請確定何時,如果它被點擊

設定一個UNIQUE_ID爲多邊形的UNIQUE_ID:在KML它看起來像這樣

<Placemark id="unique_id"> 
<name>Polygon Name</name> 
<styleUrl>....</styleUrl> 
<Polygon> 
    ..... 
</Polygon> 
</Placemark> 

然後使用這種功能內事件監聽

var obj = e.getTarget(); 
     if (obj.getType() == 'KmlPlacemark') { 
     e.preventDefault(); 
     var placemark = obj; 
     var placemark_id = placemark.getId(); 
       if (placemark_id == 'unique_id') { 
       console.log("hello"); 
       } 
    } 

我知道你是能夠檢測一個Polygon點擊這種方式,所以這個想法應該GroundOverlays工作。我還沒有測試過它。如果您有問題,我建議Polygon<drawOrder>設置爲數字比GroundOverlay

希望這個作品的<drawOrder>更高!

+0

感謝您的回覆。不幸的是,getType()調用總是返回「GEGlobe」,如果它覆蓋或不在。如果我點擊地標,它會返回「KmlPlacemark」。 – Bill

+0

啊,對不起,你是對的。我現在記得在談論疊加層(地面和屏幕)時是不同的。如果我找出答案,我會編輯我的並讓你知道。 – lifeIsGood

+0

我會很感激。順便說一句:我看了你的3DWhistler網站。幹得不錯! – Bill

1

這是一個錯誤,您發佈的代碼是完全正確的。

KmlGroundOverlay對象確實繼承自GEEventEmitter,所以它具有標準KmlMouseEvents;鼠標按下,鼠標鬆開,鼠標移動等

你可以看到,這裏明確:https://developers.google.com/earth/documentation/reference/interface_kml_ground_overlay-members

出於某種原因,該事件只是不火,雖然。沒有此這裏的bug報告(疊加問題被合併到它,我相信...) https://code.google.com/p/earth-api-samples/issues/detail?id=123

正如@lifeIsGood說,在他的答案是最好的解決方法是將一個透明的多邊形下相同的幾何地面疊加層。但是,至少以編程方式將不透明度設置爲0並不是一個好主意。Opacity is experimental,並且此時如果您將其設置爲0,則對於該對象,KmlMouseEvents通常不會觸發。我相信當插件完全透明時,該插件可以有效地從事件鏈中移除該功能。無論如何,爲了防止這種情況,將不透明度設置爲.1而不是0

處理多邊形上事件的更好方法是將GroundOverlay傳遞迴處理程序。通過這種方式,在引發事件時,您可以在處理程序中引用GroundOverlay和事件數據,而不需要任何全局變量或唯一ID。例如

// attach the event to the transparent polygon discussed 
google.earth.addEventListener(polygon, 'click', function(e) { 
    handler(overlay, e) 
}); 

// handle the event. 
// sender is the overlay 
// event is the real KmlMouseEvent from the polygon. 
function handler(sender, event) { 
    console.log(sender.getType()); //KmlGroundOverlay 
    console.log(event.getTarget().getType()); //KmlPolygon 
}; 

I made a working example of this here.

+1

謝謝。我很感激! – Bill

+0

不用擔心。注意我也意識到,與錯誤報告的鏈接並不真正適用,因此我[已正確提出問題](https://code.google.com/p/earth-api-samples/issues/detail? ID = 960)。 – Fraser