刷新谷歌地图API V3图层

24

我一直在尝试让Google Maps API v3正确更新,我运行了一个Javascript计时器来定期刷新交通图层,但我没有看到它实际发生。

根据文档,我应该能够像"layer.setMap(null);",然后是"layer.setMap(map);"这样做来刷新图层(来源:https://developers.google.com/maps/documentation/javascript/reference#TrafficLayer)。

我知道新地图瓦片正在下载(例如,我可以在Chrome的开发工具的资源部分中看到它们),但浏览器没有呈现它们。我可能错过了一些基本的东西。

我尝试了几件事情,包括:

是否有一种方法可以确保浏览器渲染新的图像而不强制重新加载整个页面?

下面是页面的简化版本(基于Google Maps刷新交通图层的答案)。

<html>
    <head>
        <title>Map Testing</title>
        <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=weather"></script>
        <script src="http://code.jquery.com/jquery-latest.min.js"></script>

        <script type="text/javascript">
            var map,
                trafficLayer,
                mapTimerHandle;

            $(function() {
                initMap();
                mapTimerHandle = setInterval(refreshMap, 15000);
            });

            function refreshMap() {
                trafficLayer.setMap(null);
                trafficLayer.setMap(map);
            }

            function initMap() {
                var mapDiv = document.getElementById('map');

                map = new google.maps.Map(mapDiv, {zoom: 15, center: new google.maps.LatLng(40.7127, -74.0059)});

                trafficLayer = new google.maps.TrafficLayer();

                trafficLayer.setMap(map);
            }
        </script>
    </head>
    <body style="margin:0px;">
        <div id="map" style="width:100%; height:100%;"></div>
    </body>
</html>

谢谢!我确实查看了那篇文章(事实上,它是我在帖子中引用的文章之一)。不幸的是,那对我没有起作用。 - manderson
你的浏览器上游是否有任何代理或缓存? - James Westgate
8个回答

23

好的,根据上面提到的内容,trafficLayer.setMap(null)trafficLayer.setMap(map) 只是将有交通流量的图块切换为没有交通流量的图块。此外,map.setZoom(map.getZoom())(以及其他缩放变化)无法工作,因为图块已经在缓存中,并且 Google 脚本甚至不会尝试从服务器下载新的图块。

此外,似乎如果只是打开 Google 地图并开启交通流量图层,它并不会刷新!太糟糕了,Google!

因此我们必须找到另一种解决方法。

首先想到的是使用 window.location.reload(true) ,它会清除图像缓存 - 我们看到它确实起作用了。虽然这不是一个好主意 - 重新加载整个页面需要太长时间。那么重新加载图像呢?让我们试试!

function reloadTiles() {
    var tiles = $("#map-canvas").find("img");
    for (var i = 0; i < tiles.length; i++) {
        var src = $(tiles[i]).attr("src");
        if (/googleapis.com\/vt\?pb=/.test(src)) {              
            var new_src = src.split("&ts")[0] + '&ts=' + (new Date()).getTime();
            $(tiles[i]).attr("src", new_src);                                                   
        }               
    }
}   

每N秒我们调用这个函数: setInterval(reloadTiles, 5000)

一些注释:

$("#map-canvas").find("img") - 将从您的地图容器(map-canvas是我的情况)中获取所有图像。并非所有图片都是瓷砖,因此我们需要将它们过滤出来——我注意到瓷砖是从像mts(digit).googleapis.com/vt?pb=(hella long param)这样的域加载的。其他地图图像是从maps.gstatic.com加载的。

因此,我们获取瓷砖图像,添加虚假参数并更改它们的src。利润!

顺便说一句,我发现交通情况确实随着时间而变化-瓷砖可能每秒都不同。

编辑

哦,对不起,这里有一个工作示例。那是一个下着大雪的莫斯科,交通量很大 :)


2
您,先生,非常有创意!很好的超越传统思维。 - RichardTheKiwi
这个完美地运行了。抱歉我回复慢了。最近太忙了。 - manderson
只是一个提醒,googleapis.com 可能会随着时间的推移发生变化,现在略有不同,如 mts1.google.com。因此,删除域名,只保留 /vt\?pb 应该可以解决问题。 - alpakyol
1
现在需要过滤的URL是"/map/vt?pb="。 - lg.
有任何想法如何在Angular 2+中完成吗? - Amrit

1

我经常使用交通图层。我添加了一个按钮来切换图层,但发现如果要隐藏它,我必须将图层本身设置为null。

显示图层的代码:

if (MapManager.trafficLayer == null)
{
      MapManager.trafficLayer = new google.maps.TrafficLayer();
}
MapManager.trafficLayer.setMap(MapManager.map);

然后再隐藏该层:
MapManager.trafficLayer.setMap(null);
MapManager.trafficLayer = null;

你上面提出的方法是理想的,但对我来说,这种方法似乎已经很好地解决了问题。


所以我尝试了完全的拆除和重建,类似于你建议的方式,虽然它看起来像是在做某些事情(我可以看到地图闪烁),但实际上交通情况并没有更新(通过在另一个浏览器中拉取页面进行验证,在那里还没有缓存任何内容)。 - manderson

1

您刷新图层的方式运行良好。

这是您编辑过的代码,以证明这一点:

<html>
<head>
    <title>Map Testing</title>
    <script type="text/javascript" src="https://maps.googleapis.com/maps/api   /js?v=3.exp&libraries=weather"></script>
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>

    <script type="text/javascript">
        var map, trafficLayer;          
        var vis = false;

        $(function ()
        {
            initMap();            
        });

        function refreshMap()
        {               
            if (vis)            
            {
                trafficLayer.setMap(null)
                vis = false;
            }
            else
            {
                trafficLayer.setMap(map);
                vis = true;
            }            
        }

        function initMap()
        {
            var mapDiv = document.getElementById('map');
            map = new google.maps.Map(mapDiv, {zoom: 15, center: new google.maps.LatLng(40.7127, -74.0059)});

            trafficLayer = new google.maps.TrafficLayer();

            trafficLayer.setMap(map);
            vis = true;
            setInterval(function ()
            {
                refreshMap();
            }, 3000);
        }
    </script>
</head>
<body style="margin:0px;">
    <div id="map" style="width:100%; height:100%;"></div>
</body>

我在每2秒钟添加了一个可见性属性来切换图层。您会注意到它如何开启和关闭。因为您的调用方式,我认为它没有足够的时间来刷新。
此外,我在图层地图首次设置后的initMap函数中添加了间隔。
希望这有所帮助。
编辑。只是补充一下,在支持实时交通的地方,有些情况下基于历史数据,不会在短时间内刷新。但是,交通数据将根据文档中指定的每个请求的时间获取:https://developers.google.com/maps/documentation/javascript/trafficlayer#traffic_layer

感谢您抽出时间查看此内容。我可能需要再澄清一下。图层渲染得很好,但实际的交通数据已经过时了。我可以让它运行一个小时甚至更长时间,仍然看不到任何变化(包括修改代码以匹配您所做的)。完全有可能存在某些环境因素阻止我使其正常工作,但我已经让其他几个人也看了一下,结果都是一样的。 - manderson
嗨@manderson,别担心,伙计,我认为你可能和我在新西兰一样存在相似的问题,即交通数据不会实时更新。我建议你在几天内监视交通情况,截屏并在一周左右进行比较,看看数据是否有变化。否则,可能值得向Google提交支持工单。 - TResponse
我会去检查一下,稍后再回复。感谢您一直陪伴着我。 - manderson
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - manderson
trafficLayerOnMap() { if (this.isTrafficEnabled) { this.trafficLayer.setMap(null); this.isTrafficEnabled = false; } else { this.trafficLayer = new google.maps.TrafficLayer(); this.trafficLayer.setMap(this.map); this.isTrafficEnabled = true; } }类似的想法对我来说可行。 - Nagendra Badiganti

1

塞尔吉奥的回答仍然是正确的,但我相信谷歌已经更新了他们的API和URL(一位评论者提到的风险)。

现在的正确测试应该是:

....
if (/googleapis.com\/maps\/vt\?pb=/.test(src)) {
...

1
尝试使用这段代码片段,可能会对您有所帮助。

function init() {
    var map,
    trafficLayer,
    mapTimerHandle;

    var mapDiv = document.getElementById('map');

    map = new google.maps.Map(mapDiv, {
        zoom: 14,
        center: new google.maps.LatLng(40.7127, -74.0059)
    });

    trafficLayer = new google.maps.TrafficLayer();
    trafficLayer.setMap(map);
}

$(document).ready(function () {
    init();
    setInterval(function () {
        google.maps.event.trigger(map, 'resize');
        map.fitBounds();
    }, 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false"></script>

<div id="map" style="width:600px; height:450px"></div>


1

谷歌文档说:

使用TrafficLayer对象,谷歌地图API允许您向地图添加实时交通信息(如果支持)。提供的交通信息是请求时的时间

我认为你的refreshMap()函数只会通过.setMap()方法隐藏和显示交通层,并不会获取更新的交通数据(如果有的话)。

所以唯一有效的方法是通过重新初始化TrafficLayer()对象来更新你的请求

编辑: 我监视了网络流量,发现没有新请求被发出。必须更改视图才能强制获取新数据(例如通过重新设置缩放)...

function refreshMap() {
    trafficLayer.setMap(null);
    trafficLayer = null;
    map.setZoom(map.getZoom()); // this renews the view and forces new data to be requested
    setTimeout(function () {
        trafficLayer = new google.maps.TrafficLayer();
        trafficLayer.setMap(map);
    }, 100);
}

因此,使用上述更新的方法,下面的JS文件将从https://maps.googleapis.com/maps/api/js/重新获取:

ViewportInfoService.GetViewportInfo?1m6&1m2&1d40.67711350268867&2d-74.09477919619036&2m2&1d40.747903965754034&2d-73.91666125686464&2u15&4sen-US&5e0&6sm%40285000000&7b0&8e0&9b0&callback=_xdc_._e0qzs3&token=110512

包含交通数据,例如:

...,["traffic",[[40.74725696280421,-74.102783203125],[40.75557964275589,-73.916015625]]]...

据我所见,流量数据实际上是嵌入在瓦片中的,而不是绘制在其上。请在Chrome中检查网络预览。此外,这并没有比已经提出的解决方案更好,即通过API设置缩放(<相同缩放>)被忽略了。 - RichardTheKiwi
@RichardTheKiwi 你说得对。我已经删除了那个声明。但是 setZoom() 绝对不会被忽略。如果没有这行代码,ViewportInfoService.GetViewportInfo 文件(交通数据)将永远不会被重新获取。只需在 Dev 工具的网络选项卡中检查即可。 - Onur Yıldırım

1
我还没有使用过交通图层,所以不确定这是否适用于您。我阅读了“setZoom(getZoom())”技巧,但当我需要刷新瓦片(经典的地图本身)时,它也无法工作。我需要在Gmaps中显示/隐藏网格(而不重新加载页面)
经过搜索和测试,我通过以下三行使其工作:
        google.maps.event.trigger(map, 'resize'); // Can't remember if really helps
        map.setZoom( map.getZoom() -1 );    
        map.setZoom( map.getZoom() +1 ); // It won't flicker or make the transition between zoom levels. GMap just ignore the zoom change but redraw the tiles :/

我重复一遍。我不知道这是否适用于交通图层,但无论如何都要测试它。它让我保持清醒了两天。

我更进一步了 - 我执行了setZoom(-1),并在一个setInterval回调函数中执行了setZoom(+1)。屏幕闪烁了一下,但仍然没有重新加载瓦片。 - RichardTheKiwi

0

这可能对您有用。 在此输入图像描述

    constructor(){
         this.mapOptions =  {
            zoom: 11,
            mapTypeId: google.maps.MapTypeId.HYBRID,
            mapTypeControl: false,
            streetViewControl: false,
            fullscreenControl: false
          }

         this.map = new google.maps.Map(
           this.mapElement.nativeElement,
           this.mapOptions
         );
         this.map.setCenter(this.latLng);
         this.map.setZoom(7);

         this.trafficLayer = new google.maps.TrafficLayer();
         // console.log(trafficLayer);
         this.trafficLayer.setMap(this.map);
        }

我最初在地图上添加了交通图层,使用以下代码根据用户需求切换交通图层

    // use this method to toggle traffic layer on the map. 
    trafficLayerOnMap() {
        if (this.isTrafficEnabled) {
          this.trafficLayer.setMap(null);
          this.isTrafficEnabled = false;
        } else {
          this.trafficLayer = new google.maps.TrafficLayer();
          this.trafficLayer.setMap(this.map);
          this.isTrafficEnabled = true;
        }
      }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接