24

I've been having trouble getting Google Maps API v3 to update correctly. I've got a javascript timer running that should be refreshing the traffic layer periodically but I'm not seeing it happening.

As I understand the documentation, I should be able to say something like "layer.setMap(null);" followed by "layer.setMap(map);" to refresh the layer (source: https://developers.google.com/maps/documentation/javascript/reference#TrafficLayer).

I know the new map tiles are being downloaded (for example, I can see them in the Resources section of Chrome's dev tools), but the browser isn't rendering them. There is probably something fundamental I'm missing.

I've tried several things, to include:

Is there a way to ensure the browser will render the new images without forcing a full page reload?

Below is a simplified version of the page (based off of the answer from Google Maps refresh traffic layer).

<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>
Community
  • 1
  • 1
manderson
  • 504
  • 1
  • 4
  • 16

8 Answers8

23

Ok, what i've found, and what is mentioned above is that trafficLayer.setMap(null) and trafficLayer.setMap(map) - just switches tiles with drawn traffic to tiles without traffic. Also map.setZoom(map.getZoom()) (and any other variaties of zoom) doesn't work because tiles are already in cache and google scripts don't even try to download fresh ones from server.

Also, seems that if you just open google maps and turn on traffic layer it's not refreshing! Such a lame, google!

So we have to find a different way to solve it.

First thought is to use window.location.reload(true) wich will flush image cache - and we see it works. Not a good way to go though - reloading whole page taking too long. How about reloading images? Let's try!

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);                                                   
        }               
    }
}   

And we call this function every N seconds: setInterval(reloadTiles, 5000)

some comments:

$("#map-canvas").find("img") - will grab all images from your map container (map-canvas in my case). Not all are tiles so we need to filter them out - i've noticed that tiles are loaded from domains like mts(digit).googleapis.com/vt?pb=(hella long param). Other map images are loaded from maps.gstatic.com.

So we get tile images, add bogus parameter and change their src. Profit!

Btw, i've found that traffic really changes in real time - tiles may be different each second.

Edit

Oh, sorry, here's working sample. And it's a snowy Moscow with a huge traffic :)

Sergio
  • 6,762
  • 5
  • 27
  • 54
1

I use the traffic layer quite a bit. I have added a button to toggle the layer but found to hide it I had to set the layer itself as null.

My code to show the layer:

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

Then to go and hide the layer again:

MapManager.trafficLayer.setMap(null);
MapManager.trafficLayer = null;

In an ideal way what you suggested above would be better but for me this seems to work just fine.

loan.burger
  • 3,578
  • 6
  • 39
  • 59
  • So I did try a complete tear down and rebuild similar to what you suggested, and while it acts like it's doing something (I can see the map flash) the actual traffic doesn't update (verified by pulling the page in a different browser where nothing has been cached yet). – manderson Nov 14 '14 at 13:10
1

The way you are refreshing the layer is working fine.

Here is your code edited to prove this:

<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>

I added a visibility property to toggle the layer every 2 seconds. You will notice how it turns on and off. Because the way you called it I think it did not have enough time to refresh.

Also I added the interval in the initMap function after the layer's map was first set.

Hope that helps.

EDIT. Just to add to this, where the traffic is real time where supported, in some cases its based on historic data and will not refresh over a short period. But the traffic data is fetched at the time of each request as specified in the docs: https://developers.google.com/maps/documentation/javascript/trafficlayer#traffic_layer

loan.burger
  • 3,578
  • 6
  • 39
  • 59
  • I appreciate your taking the time to look at this. I may need to clarify a bit more. The layer renders just fine, but the actual traffic data is out of date. I can let it run for an hour or more and still see no change (this includes modifying the code to match what you've done). It is entirely possible that there is something environmental prohibiting me from getting this to work, yet I've had a couple other people look at it with the same results. – manderson Nov 18 '14 at 13:16
  • Hi @manderson, no worries mate, I do think that you may have a similar issue as I have in NZ in that the traffic data does not get updated in real-time. I'd recommend you monitor the traffic over a few days, take screen-shots and compare it over a week or say and see if the data has changed. otherwise it may be worth logging a support ticket with Google. – loan.burger Nov 18 '14 at 23:22
  • I will check it out and post back later then. Thanks for sticking with me on this. – manderson Nov 19 '14 at 20:26
  • Sorry for taking so long to get back to this. I did let the page run for a lengthy duration without noticing any changes. I did inadvertently panned the view manually which forced a refresh of the tiles, so I tried programmatically changing the latitude and longitude slightly but that didn't work either. Will update again when I have something to share. – manderson Dec 29 '14 at 15:36
  • A SImilar idea works for me. 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 Mar 22 '19 at 10:05
1

try this code snippet, may be this help you out

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>
Eko Junaidi Salam
  • 1,643
  • 1
  • 17
  • 26
1

Google documentation says:

The Google Maps API allows you to add real-time traffic information (where supported) to your maps using the TrafficLayer object. Traffic information is provided for the time at which the request is made.

I believe your refreshMap() function will only hide and show the traffic layer (via the .setMap() method) and will not actually fetch updated traffic data (if there is).

So the only efficient way would be to renew your request by re-initiating the TrafficLayer() object.

EDIT: I've monitored the network traffic and saw no new requests are made. The view has to be changed to force new data to be fetched (for example by re-setting the zoom)...

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);
}

So with the updated method above, the JS file below is re-fetched from 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

that contains traffic data such as:

...,["traffic",[[40.74725696280421,-74.102783203125],[40.75557964275589,-73.916015625]]]...
Onur Yıldırım
  • 27,841
  • 11
  • 78
  • 96
  • FYI as far as I can see, the traffic data is actually embedded in the tiles, not drawn on top. Check in Chrome, network, preview. Also, this didn't work any better than already proposed solutions, i.e. setZoom() is ignored I believe by the API. – RichardTheKiwi Jan 11 '15 at 21:41
  • @RichardTheKiwi You're right. I've removed that claim. But `setZoom()` is definitely not ignored. Without that line, `ViewportInfoService.GetViewportInfo` file (traffinc data) is never re-fetched. Just check the Network tab in Dev Tools. – Onur Yıldırım Jan 12 '15 at 00:48
1

I haven't work with traffic layers yet, so I'm not sure if this will work for you. I read the setZoom(getZoom()) trick but when I need to refresh the tiles (the classic one. The map itself) it didn't work either. I need to show/hide a grid in Gmaps (without reloading the page)

After searching and testing I got it working with these three lines:

        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 :/

I repeat. I don't know if this works with traffic layers but test it anyways. It kept me awake 2 days.

Esselans
  • 1,484
  • 2
  • 24
  • 43
  • I went one better - I did the setZoom(-1) and in a setInterval callback, the setZoom(+1). The screen flickered and all and STILL no reloading of tiles. – RichardTheKiwi Jan 12 '15 at 02:55
1

Sergio's answer is still the correct one but I believe Google updated their API and URLs (a risk mentioned by a commenter).

The correct test would be now:

....
if (/googleapis.com\/maps\/vt\?pb=/.test(src)) {
...
Unheilig
  • 15,690
  • 193
  • 65
  • 96
renambot
  • 11
  • 2
0

This might work for you. enter image description here

    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);
        }

I Added traffic layer on the map initially, using the below code to toggle the traffic based on the user requirement

    // 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;
        }
      }
Nagendra Badiganti
  • 1,566
  • 2
  • 18
  • 28