【问题标题】:How to draw openlayers features using a google earth basemap如何使用谷歌地球底图绘制 openlayers 功能
【发布时间】:2026-02-06 12:00:02
【问题描述】:

尝试使用OpenLayers(最终)在谷歌地球插件基础地图上加载标记、矢量和 WMS。 Google 地球似乎“与他人相处得不好”。

如果我以“谷歌方式”实例化地图: google.earth.createInstance('map', initCB, failCB);

我得到了一个可以添加谷歌地标的谷歌地图,但我无法将该实例传递给 OpenLayers。

使用以下内容:

map = new OpenLayers.Map('ol-map');
map.addControl(new OpenLayers.Control.LayerSwitcher());

var gphy = new OpenLayers.Layer.Google(
    "Google Physical",
    {type: G_PHYSICAL_MAP}
);
var gmap = new OpenLayers.Layer.Google(
    "Google Streets", // the default
    {numZoomLevels: 20}
);
var ghyb = new OpenLayers.Layer.Google(
    "Google Hybrid",
    {type: G_HYBRID_MAP, numZoomLevels: 20}
);
var gsat = new OpenLayers.Layer.Google(
    "Google Satellite",
    {type: G_SATELLITE_MAP, numZoomLevels: 22}
);
var gearth = new OpenLayers.Layer.Google(
    "Google Earth",
    {type: G_SATELLITE_3D_MAP}
);

map.addLayers([gphy, gmap, ghyb, gsat, gearth]);

map.setCenter(new OpenLayers.LonLat(-120, 32), 5)
addMarker();

这将创建一个包含 5 个 googly 层的基本 OL 地图。 I can see the marker I add when any of the map layers except when gearth is selected.一旦我加载了谷歌地球地图,它就会“接管”整个 div。 LayerSwitcher 等所有 OL 控件都消失了,我一直无法弄清楚如何从 OL 访问 google earth 实例。

我假设标记仍然存在,但在底图后面。在底图上设置不透明度无效。

问题:

  1. 这是记录在案的限制吗?我找不到任何表明它不可能的东西。
  2. 是否有一种解决方法可以将 OpenLayers 地图/图层实例传递给 google earth 的 getEarthInstance 调用?或相反亦然?这似乎是两全其美,让我可以在需要时访问 GE API,但我可以将 OL 中的所有 WFS 处理用于大多数任务。

欢迎不成熟的想法。

【问题讨论】:

    标签: openlayers google-earth-plugin


    【解决方案1】:

    一般来说,OpenLayers 完全不支持 Google 地球 3D 模式。 (我是 OpenLayers 的开发人员,我只是通过这个问题才意识到这是可能。)由于 3D 显示代码的工作方式(通过 3rd 方插件),我可以无法想象默认的 OpenLayers 工具(用于绘图等)会以任何方式工作:OpenLayers 需要与之集成的部分根本不存在于 3D API 中。

    OpenLayers - 主要是一个 2D 绘图客户端 - 不太可能实现将所有各种调用转换为 Google Maps/Earth API 调用的东西。

    OpenLayers 确实支持协议和格式:理论上,这将允许您在与 Google Earth API 等第三方 API 交互时在某种程度上使用 OpenLayers WFS 处理工具链。但是,我希望在这种情况下,您不会从中获得足够的收益来解决您的问题。

    简而言之:这是行不通的,也没有简单的解决方案。如果您想要 3D,您可能需要自己构建更多内容。

    【讨论】:

    • 感谢您的反馈。我还与几个谷歌工程师保持联系,所以如果我学到任何东西,我会及时通知你。
    • 你是对的。我与谷歌工程师的谈话对此没有帮助,但确实指出,如果你想将谷歌地球插件实现到定制的网络应用程序中,GE 插件 API (earth_loader_plugin.js) 的本地副本必须是加载并且必须设置一些环境变量来配置GEE服务器的API密钥和错误页面的位置。加载 Google 地球插件并连接到私有 GEE 服务器的示例网页可在 gmdemo.keyhole.com/geplugin/gee-basic-example.html 查看以供参考。
    【解决方案2】:

    我认为这个问题与 Google Maps v2 有关,因为我认为 v3 中没有类型:G_SATELLITE_3D_MAP。

    用于 Google 地图 v3 的 Google 地球插件(测试版)通过 script from the google-maps-utility-library-v3 (googleearth.js) 提供支持

    作者@jlivni采取的方法是监听Google Maps v3 add/remove事件,添加对应的Google Earth Api对象。 我相信类似的方法可以用于 OpenLayers。我是 OpenLayers 的新手,刚刚开始研究这个问题,但我会在这里发布更新。

    我现在唯一能补充的是关于谷歌地球插件的初始化,这在 v3 中有所不同:

    function GoogleEarth( olmap ) {
    this.olmap_ = olmap;
    this.layer_ = new OpenLayers.Layer.Google(
        "Google Earth",
        {type: google.maps.MapTypeId.SATELLITE, numZoomLevels: 22, visibility: false}
    );
    this.olmap_.addLayers([ this.layer_ ]);
    this.map_ = this.layer_.mapObject;
    this.mapDiv_ = this.map_.getDiv();
    
    ...
    var earthMapType = /** @type {google.maps.MapType} */({
    tileSize: new google.maps.Size(256, 256),
    maxZoom: 19,
    name: this.earthTitle_,
    // The alt helps the findMapTypeControlDiv work.
    alt: this.earthTitle_,
    getTile:
      /**
       * @param {google.maps.Point} tileCoord the tile coordinate.
       * @param {number} zoom the zoom level.
       * @param {Node} ownerDocument n/a.
       * @return {Node} the overlay.
       */
      function(tileCoord, zoom, ownerDocument) {
        var div = ownerDocument.createElement('DIV');
        return div;
      }
    });
    
    this.map_.mapTypes.set(GoogleEarth.MAP_TYPE_ID, earthMapType);
    
    this.layer_.type = GoogleEarth.MAP_TYPE_ID;
    
    var that = this;
    google.maps.event.addListener(map, 'maptypeid_changed', function() {
      that.mapTypeChanged_();
      }); 
    }
    

    令人费解的部分是我们需要为 Google Earth 定义一个新的地图类型,并将该类型添加到 Google Map 对象中。但是,如果我们不创建具有我最初设置为 google.maps.MapTypeId.SATELLITE 的类型的图层,则 Google 地图对象不存在。 不是很干净,但至少它让我使用 Google Maps v3 达到与本文作者相同的状态。 最后可能有一种方法可以通过修改函数 findMapTypeControlDiv_() 使 OpenLayers 控件可见:

    var mapTypeControlDiv = this.findMapTypeControlDiv_();
    if (mapTypeControlDiv) {
      this.setZIndexes_(mapTypeControlDiv);
      this.addShim_(mapTypeControlDiv);
    }
    

    [更新]

    我已经修改了 findMapTypeControlDiv_() 函数,现在我寻找的是 OpenLayers LayerSwitcher:

    GoogleEarth.prototype.findLayerSwitcherDiv_ = function() {
    //  var title = 'title="' + this.earthTitle_ + '"';
      var id = 'LayerSwitcher';
    
      var siblings = this.controlDiv_.parentNode.childNodes;
      for (var i = 0, sibling; sibling = siblings[i]; i++) {
        if (sibling.id.indexOf(id) != -1) {
          return sibling;
        }
      }
    };
    

    LayerSwitcher 的 z 索引设置正确,div 显示在顶部,直到调用 google.earth.createInstance(),然后它消失。我会花更多的时间来解决它,它看起来不难解决。

    [更新 2]

    我已经解决了 LayerSwitcher 面板问题。诀窍是将 Google Earth Plugin div 附加到正确的 div(GMaps 的原始代码将其附加到 Map Controls 数组)。另一个问题是设置 zIndex 以确保 LayerSwitcher 位于顶部。 当 GE 插件运行时我仍然遇到问题,我尝试最小化 LayerSwitcher,调用 OpenLayers.Event.stop() 会使 GE 插件崩溃(Chrome)或 LayerSwitcher 消失(IE8)。 我想从谷歌分叉原始代码并制作一个适用于 OpenLayers 的 GE 插件层。 有人可以建议怎么做吗?谢谢。

    无论如何,这是我最新的更改:

    GoogleEarth.prototype.findLayerSwitcherDiv_ = function() {
      var id = 'LayerSwitcher';
    
      var siblings = this.mapDiv_.parentNode.childNodes;
      for (var i = 0, sibling; sibling = siblings[i]; i++) {
        if (sibling.id.indexOf(id) != -1) {
          return sibling;
        }
      }
    };
    
    GoogleEarth.prototype.addEarthControl_ = function() {
    ...
    inner.appendChild(earthDiv);
    var parentNode = this.findLayerSwitcherDiv_().parentNode;
    parentNode.appendChild( control );
    
    ...
    GoogleEarth.prototype.setZIndexes_ = function(mapTypeControlDiv) {
      var oldIndex = mapTypeControlDiv.style.zIndex;
      var siblings = this.controlDiv_.parentNode.childNodes;
      for (var i = 0, sibling; sibling = siblings[i]; i++) {
        sibling['__gme_ozi'] = sibling.style.zIndex;
        // Sets the zIndex of all controls to be behind Earth.
        sibling.style.zIndex = 0;
      }
    
      mapTypeControlDiv['__gme_ozi'] = oldIndex;
      this.controlDiv_.style.zIndex = 2000;
      mapTypeControlDiv.style.zIndex = 2001;
    };
    

    [更新 3] 我在这里建立了一个github项目:https://github.com/ZiglioUK/GoogleEarth-for-OpenLayers

    【讨论】:

    • 嗨,更新 4 的 URL 正在返回 404,还尝试过“更新 3”似乎已过时。你有可用的工作集吗?
    【解决方案3】:

    我不再熟悉 OpenLayers 中的不同图层类型,但关键是 Google Earth API 和 Google Maps API 确实是不同的野兽。乍一看,我不明白为什么需要将它添加到 Google Maps 类型(假设我认为是这样)。

    正如 Chris 已经暗示的那样,只需将您的 Earth API 集成视为一个全新的、独立于任何现有 Google 层的东西;这些 API 中的语法和概念并不十分相似,至少据我所知,您无法从 OpenLayers 中的现有 Google 地图集成中获得任何优势。

    也就是说,我希望 Maps V3/Earth 集成的代码会有所帮助,并且类似于将其与 OpenLayers 集成所需的操作。同时,我看到其他一些人也尝试过,例如这个 geoExt 示例,您可能会发现它也很有用:http://dev.geoext.org/sandbox/cmoullet/ux/GoogleEarthPanel/examples/GoogleEarthPanelExample.html

    【讨论】: