2012-10-18 95 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