如何使 google 地图中的重叠 POI 易于访问

How to make overlaid POIs in google maps accessible

g 地图中的常见 POI 固定显示在图块上,但似乎可以通过重叠链接点击。当我制作透明地图覆盖时,POI 链接仍然可见但不再可点击。
截图可能会更清楚问题,正确的 POI 我只能在较暗的叠加层旁边的右上角区域单击:

我已经尝试将叠加层绑定到具有最低 z-index 的图层,但没有成功:

mapPane z-index:100
overlayLayer z-index:101
floatPane z-index:102
markerLayer z-index:103
overlayImage z-index:104
floatShadow z-index:105
overlayMouseTarget z-index:106
floatPane z-index:107

const panes = this.getPanes();
panes.mapPane.appendChild(this.div_);

我知道与标记相反,API-对 POI 的支持是有限的,并且至少部分绑定到地点 API。不过,我更喜欢有关 z-index 的方法或其他一些方法来处理更通用的 POI,这样我就不必使用额外的 APIs 并且该解决方案适用于所有显示的 POI。

对于标记,方法 setZIndex 存在,但这似乎对这里没有帮助:Marker API

我知道在更高的分辨率下显示更多 POI 的情况,但这对问题没有影响。
那么如何使 g-maps 中叠加层下方的 POI 链接可点击?

编辑:
根据回答

我想问题差不多解决了。由于@Brett Donald 的回答和评论,我在自己的 SVG 代码中发现了一些错误,并意识到标记并非完全由 g-map 创建,但 SVG 确实是由我创建/提供的。 在个人 class class USGSOverlay extends google.maps.OverlayView 中,我可以选择添加实现我的目标所需的代码。我也意识到这个 class 是解决问题必不可少的。 在 example which is mentioned in the one linked answer 中,并非覆盖层后面的所有点击事件都是可能的,因此可以通过单击 POI 打开 windows 但不能在图层后面再次关闭它们。

我会悬赏给可以通过关闭叠加层后面的信息 windows 的选项来丰富链接示例的人。

您是否在叠加层上尝试过此设置?

.my-overlay {
    pointer-events: none;
}

关于 Google 地图的基础知识

Google 地图(可能)包含许多难以在浏览器控制台中验证的图层。我在控制台中标记了外部地图层,并在编辑器中复制了 JavaScript 生成的整个 html,因此更容易验证和搜索预期的元素,如标记或覆盖。
然而,console.log().

可以很好地调试地图、标记或叠加层等对象。

关于 z-index 的层,如问题中所述,结构清晰。但是 z-index 以及每一层的内容都可以调整(或不使用),我怀疑在没有具体 use-case 的情况下详细显示一般结构是否有用。因此,任何相关细节都与问题直接相关。

叠加基础知识

可以在多个级别上进行叠加,还有一个特殊的 class google.maps.GroundOverlay 直接在地图上方排序。要了解与覆盖相关的对象和方法的概述,一个好的开始就是在 listing with classes and properties.

中搜索单词 overlay

将叠加层绑定到窗格

实现叠加层的常用方法是根据google.maps.OverlayView创建定义并将叠加层绑定到窗格:


  class USGSOverlay extends google.maps.OverlayView {
        bounds_;
        image_;
        div_;
        constructor(bounds, image) { ... }
        onAdd() {
            this._div = document.createElement('div');
            ...
            const panes = this.getPanes();
            // inding overlay to a pane,
            // `mapPane` can be exchanged by a different existing pane:
            panes.mapPane.appendChild(this.div_); 
            ...
        }
        draw() { ... }
        onRemove() { ... }
  }
  overlay = new USGSOverlay(map);
  overlay.setMap(map);

此过程会将地图绑定到预定义的窗格,该窗格通常具有问题中列出的 z-index(默认范围 z-index 从 100 到 107)。

将 Overlay 直接绑定到地图

也可以将独立于窗格的叠加层绑定到地图,如果没有定义不同的z-index,它将直接位于地图上方。 linked example中使用了此方法。 该示例使用原型,因此它有点复杂,但基本上是这样完成的:

class MyOverlay extends google.maps.OverlayView {
 ...
 onAdd() {
     // NO binding like `panes.mapPane.appendChild(this.div_);`
 }
 ...
}
overlay = new MyOverlay(map);
overlay.setMap(map);

选择使用哪种方法可能会影响地图中生成的 html,但无论如何都会影响 z-index。因此,无论任何操纵的目标超出此选择范围,都会对进一步的程序产生影响。

兴趣点和标记的基础知识

google 地图中的 POI(兴趣点)的处理方式完全不同,并且在大多数情况下也有不同的 z-indexes。不过,用户的功能可能是相同的。

POI(兴趣点)

可以使用以下设置启用或禁用 POI:

        const styles = {
          default: [],
          hide: [
            {featureType: "poi.attraction", stylers: [{ visibility: "off" }]},
            {featureType: "poi.business", stylers: [{ visibility: "off" }]},
            {featureType: "poi.government", stylers: [{ visibility: "off" }]},
            {featureType: "poi.medical",stylers: [{ visibility: "off" }]},
            {featureType: "poi.park",stylers: [{ visibility: "off" }]},
            {featureType: "poi.place_of_worship", stylers: [{ visibility: "off" }]},
            {featureType: "poi.school", stylers: [{ visibility: "off" }]},
            {featureType: "poi.sports_complex", stylers: [{ visibility: "off" }]},
          ],
        };
        map.setOptions({ styles: styles["hide"] });

POI 的图像直接“打印”在 png-images 的地图图块上。 POI 无法移动,只能通过单击功能打开 info-window 它们通常在地图上没有直接功能(它们可能通过高级地图选项连接)。
这是直接从 g-map 复制的图像,包括一个兴趣点:

有关功能的 POI 是图像和 HTML-Markup 的组合,它们都没有直接组合在 HTML 源中,而是按地图上的位置逻辑组合。

标记

标记可以有单独的功能,单独的设计,如果地图支持,有些标记还可以拖到其他位置。它们具有不同的标准设计,并且可以具有与 POI 不同的 z-index,此外它们具有 own API,还允许更改 z-index。
这是标准标记:

虽然 POI 的图像始终位于任何叠加层下方,但标记可以显示在叠加层上方,相比之下,这对处理 z-indexes 也有一些影响。

到目前为止的结论

有许多具有不同 z-indexes 的图层,而且可能很容易创建更多图层。 google 地图的许多挑战都与 z-index 和 html 源中的排序有关,因此将元素绑定到正确的图层可能是许多情况下的解决方案。

挑战

挑战的主要部分是创建叠加层并启用对 POI 及其在这些叠加层下方的 info-window 的点击。在叠加层上方显示 info-window 可能会很重要。

挑战的第二部分是在叠加层上方显示标记及其 info-window。这似乎比第一部分容易得多。

存在哪些信息/经验?

This question 是如何禁用叠加层下方的鼠标事件,目前我的印象是它的答案太复杂了,因为可能只是将叠加层的 z-index 提升到通过将其绑定到窗格来避免鼠标事件。尽管如此,我还是很高兴除了答案之外还有问题,因为该网站揭示了几个细节。
另外 the short example 对于查看实际情况和验证一些细节非常有用。

挑战详情

该示例表明,与任何窗格相比,叠加层从不阻止直接绑定到地图的叠加层下方的 POI 上的点击事件。
尽管如此,信息 windows 无法关闭,因此信息 windows 本身就是一个挑战。
将标记放在叠加层的顶部应该是通过将它们绑定到窗格很容易。
因此叠加层应直接绑定到地图,标记绑定到窗格。
关于 POI 信息的选项 windows 还不清楚,它们应该显示在叠加层上方,否则至少可以关闭。

如果它是由 svg、html 标记或路径(即使用多边形选项)构建的,可能仍然需要验证叠加层的行为和与之相关的行为是否始终相同。

关于 jsfiddle.net

上示例的评论

本地代码和 jsfiddle.net 上的代码表现有些不同,并且在行为上并非 100% 一致。所以 jsfiddle.net 很适合显示 运行 示例,但代码可能必须更改或仅用于另一个变体。 如果某些东西在 jsfiddle.net 上不工作,请先在您自己的服务器上尝试,然后再发表评论。

第 1 步

As the linked question 是为了阻止我想要实现的目标,首先必须停用示例/答案中的各个事件处理程序。
此外,可以停用定义 this._div.style.zIndex = 1000; 以获得关闭打开 info-window 的选项。 奇怪的是 info-window 并不总是叠加,有时在叠加层之上,有时在叠加层之下。这应该是一致的,最多在叠加层之上。另一个问题是信息 windows 并不总是可关闭的,但在大多数情况下,当我尝试时(在 jsfiddle.net 上这不起作用)。

可以看出细微的变化here in action

这里是在自己的服务器上测试的完整代码,在文件底部的变量“googleApiKey”中添加自己的API密钥:

<!doctype html>
<html>
<head>
    <title>Example for clickevents</title>
    <style>
      html, body {height: 100%;margin: 0;padding: 0;}
      #googleMap {height: 70%; width:100%}
    </style>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script
    type="application/javascript"
    src="//code.jquery.com/jquery-2.1.3.js"
></script>
</head>
<body>
<div id="googleMap"></div>
<div id="message"></div>

<script>
    let map;

    function log(msg) {
        //console.log(msg); 
        document.getElementById('message').innerHTML += msg + '<br>';
    }

    function initMap() {
        var mapProp = {
            center: new google.maps.LatLng(51.508742, -0.120850),
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map(document.getElementById("googleMap"), mapProp);

        let myMarkers = [];
        myMarkers['marker1'] = new google.maps.Marker({
            position: new google.maps.LatLng(51.506742, -0.120850),
            map: map,
            title: "MARKER 1",
        });
        myMarkers['marker2'] = new google.maps.Marker({
            position: new google.maps.LatLng(51.510742, -0.120850),
            map: map,
            title: "MARKER 2",
        });
        for (currentMarker in myMarkers) {
          var marker = new google.maps.Marker({
            position: myMarkers[currentMarker].position,
            map: map,
            title: myMarkers[currentMarker].title,
            // icon: icon,
          });
        }

        var infowindow = new google.maps.InfoWindow({
          content: 'Welcome to Google! Thanks for using our products and services (“Services”). The Services are provided by Google Inc. (“Google”), located at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.By using our Services, you are agreeing to these terms. Please read them carefully.Our Services are very diverse, so sometimes additional terms or product requirements (including age requirements) may apply. Additional terms will be available with the relevant Services, and those additional terms become part of your agreement with us if you use those Services.'
        });
        
        myMarkers['marker1'].addListener('click', function () { infowindow.open(map, myMarkers['marker1']); });
        myMarkers['marker2'].addListener('click', function () { log('marker2 clicked'); });

        MyOverlay.prototype = new google.maps.OverlayView; //extends google.maps.OverlayView {
        function MyOverlay(map) {
          this._div = document.createElement('div');
          this._div.style.background = 'rgba(0, 0, 60, 0.2)';
          this._div.style.position   = 'absolute';
          // this._div.style.zIndex     = 1000;
          this._div.style.width      = '100%';
          this._div.style.height     = '200px';
          this.listeners = [];
          this.setMap(map);
        }
        const overlay = new MyOverlay(map);
        // const overlay = new MyOverlay;

        MyOverlay.events = [
          'mousedown', 'mousemove', 'mouseover', 
          'mouseout', 'mouseup', 'mousewheel', 
          'DOMMouseScroll', 'touchstart', 'touchend', 
          'touchmove', 'dblclick', 'contextmenu'
        ];
        MyOverlay.prototype.onAdd = function () {
            var self = this;
            this.getPanes().floatPane.appendChild(this._div);
            this.listeners = MyOverlay.events.map(function (event) {
                console.log({map:map,event:event});
                myMarkers['marker1'].addListener('mousedown', function () { log('marker1 clicked'); });
            });
        };
        MyOverlay.prototype.onRemove = function () {
            this.getPanes().floatPane.removeChild(this._div);
        };
        MyOverlay.prototype.draw = function () {
                myMarkers['marker1'].addListener('mousedown', function () { log('marker1 clicked'); });
        };
        overlay.setMap(map);
        console.log(overlay);
    }
    window.initMap = initMap;
    
    
    googleApiKey = '';
</script>
<script
  src="https://maps.googleapis.com/maps/api/js?key=" + googleApiKey + "&callback=initMap&v=weekly&channel=2&region=DE&language=de"
  async defer
></script>
</body>
</html>

所以实际上我自己最初的问题如何使 POI 易于访问得到了回答,我将通过扩展此答案回答我在上面“挑战”中提到的更多细节。