-1

I am currently working on a project that has about 20 markers scattered across the US. Each pin has a corresponding infobubble (Different from infowindow, https://github.com/googlemaps/js-info-bubble) that is always showing (no need to click the marker to get the infobubble to appear).

My issue is that the entire point of the project is to get the most up to date data (coming in from an external site that spits out XML every 15 seconds). What I want ideally is for the content of the infobubbles to "update" appropriately without having to refresh the entire page. This allows you to move about the map with it resetting to the default position and is just less jarring overall. Is there any way to do so?

Update: I have found this solution : (Google Map v3 auto refresh Markers only) but there are a couple of issues:

1). How does he continually get the markers to refresh? They only update once after 10 seconds.
2.) Can I apply similar logic to just infobubble refresh? Remember, my markers are static, its only the contents of the infobubble that need to be updated.

Community
  • 1
  • 1
John
  • 265
  • 2
  • 5
  • 16
  • 1
    Please post an example for the XML – Dr.Molle Feb 18 '15 at 22:40
  • The XML I'm currently using is tied to sporting events, so the data wont be auto refreshing until games are playing. – John Feb 18 '15 at 22:47
  • Look for `ticker: null,//setTimeout reference` and `setTimeout` in the answer you linked. and for `delay: 10000,//(milliseconds) the interval between successive gets` – Daniel W. Feb 18 '15 at 22:51
  • I still don't understand. Would I set ticker to be X where X is the number of times I want the page to refresh? – John Feb 19 '15 at 00:41

2 Answers2

2
  1. to request the XML continiously start a new delayed request via setTimeout

  2. You'll need some distinct property for the markers. As it seems this distinct property could be the position, because they are static.

Create an object where you store the markers, and use the string-representation of the marker-position as keys.

To get the XML you may of course use AJAX(when the XML comes from a different domain they must either send an appropriate Access-Control-Allow-Origin-header or you must use a serverside proxy-script on your own server which forwards the XML).

To update the content use the setContent()-method of the infoBubble(use the desired content as first function-argument).

Note: when a infoBubble is already open you must call the method updateContent_() of the infoBubble, otherwise the content will be updated when the infoBubble will be opened the next time. You may use the method isOpen() to check if a infoBubble is already open.


A sample-implementation:

Function which requests the XML:

  function downloadUrl(url,//URL of the XML-file
                      callback,//function 2 execute
                      map//the map
                      ) {

      var request = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') 
                                         : new XMLHttpRequest;

      request.onreadystatechange = function () {
          if (request.readyState == 4) {
              request.onreadystatechange = function () {};
              callback(request, request.status, url, map, callback);
          }
      };

      request.open('GET', url, true);
      request.send(null);
  }

Function which handles the response(used as callback-argument of the function above). It's not clear how the xml looks like, the function requires the following(you probably must modify it based on the given XML):

  • the items which contain the properties for each marker/bubble have the nodeName event
  • latitude is stored in the items attribute lat
  • longitude is stored in the items attribute lng
  • title/caption/whateve of the event is stored in the items attribute name
  • the content will be the textContent of the item e.g.

Sample-XML:

<events>
   <event lat="41.8781136" lng="-87.629798" name="Bulls vs. Mavericks">1:0</event>
</events>

  function xhrCallback(r, //the request
                       s, //request-status
                       u, //request-url
                       m, //map
                       c //callback
  ) {
      var events = r.responseXML.getElementsByTagName('event');
      if (!m.markers) {
          //here we store the markers
          m.markers = {};

      }
      //will be used later to remove markers which will not exist in the XML
      var keys=Object.keys(m.markers);

      for (var i = 0; i < events.length; ++i) {
          //the current XML-node
          var event = events[i],
              position = new google.maps.LatLng(event.getAttribute('lat'),
                                                event.getAttribute('lng')),
              hash = position.toString(),
              content = '<h2>' + event.getAttribute('name') + '</h2>' +
                         event.firstChild.data;

          //when the hash doesn't exist in m.markers 
          if (!m.markers[hash]) {
              //create a new marker & infobubble
              m.markers[hash] = new google.maps.Marker({
                  map: m,
                  position: position,
                  bubble: new InfoBubble({
                      content: content
                  })
              });

              google.maps.event.addListener(m.markers[hash], 'click', function () {

                  this.bubble.open(this.getMap(), this)
              });
              google.maps.event.trigger(m.markers[hash], 'click');

          } else {
            m.markers[hash].bubble.setContent(content);
              if (m.markers[hash].bubble.isOpen()) {
                  m.markers[hash].bubble.updateContent_();
              }
              //remove the current hash from the keys-array 
              (function(h){
                var index=keys.indexOf(h);
                  if(index>=0){
                      keys.splice(index,1);
                  }
              }(hash));
          }
      }

      //remove markers and bubbles which doesn't exist in the XML
      keys.forEach(function(k){        
          m.markers[k].setMap(null);
          m.markers[k].bubble.close();
          delete m.markers[k];
      });

      //new request
      setTimeout(function () {
          downloadUrl(u, c, m)
      }, 
      10000//delay in milliseconds 
      );

  }

To execute it call 1 time:

downloadUrl('path/to/file.xml',
             xhrCallback,//function from above 
             map//your google.maps.Map
            );

Demo: http://jsfiddle.net/doktormolle/fwk5e1nq/

  • The demo simply set's the content to the current time, but the content will be retrieved via AJAX.

  • you'll see that the markers/bubbles sometimes disappear, this will happen when there are markers/bubbles in the map which will not be present in the updated XML(the script will remove them)


Example for a proxy-script:

<?php
 $url='http://external.service.com/path/to/the.xml';
 header('Content-Type:text/xml');
 die(file_get_contents($url));
?>
Dr.Molle
  • 116,463
  • 16
  • 195
  • 201
  • Could you possibly go into some more detail on what it would take to use a serverside proxy-script on my own server which forwards the XML? For instance, would I have to have potentially another computer setup that's sole purpose is to fetch the XML data from a public website (that won't set the appropirate Access-Control-Allow-Origin-header) so that I could grab it from there? – John Feb 19 '15 at 05:27
  • You'll need to be able to run a serverside script, that's the only requirement. This script should 1. send a correct Content-Type-header, e.g. `text/xml` 2. read the external ressource 3. print the contents of the external ressource – Dr.Molle Feb 19 '15 at 06:46
  • I'm still a little confused on how I can implement such a script. The project that I'm currently working on is in ASP.NET MVC. How would a php script (I'm assuming php is the way to go) integrate with the rest of my project? How would it run? Or is an MVC project not a good idea for something like this? Sorry for my lack of understanding, this is all quiet new to me. I really appreciate your help. – John Feb 19 '15 at 15:01
  • It should be possible with any language available on serverside, I've added a PHP-sample above, as you see it's really simple. Set the `$url`-variable to the path of the external XML, and call this PHP-file by using it's path as first argument for `downloadUrl()` – Dr.Molle Feb 19 '15 at 16:09
0

I would definitely think about caching. Especially if you get sets with a lot more users, making an AJAX request every 10 seconds could easily overload your server. However, if you want to keep it simple, make a request every few minutes instead, to update. Cache the users, have them generated into the javascript code, say users = new Array(user1, user2, ...). You don't really have to keep updating the page if it's not that important, as most users would navigate away within a minute or two anyways. If you have a long list that changes every few seconds, that gives you enough time to not ever have to update using AJAX, and just rely on the server generated list of users.

If not, store the last time you updated the list in a variable, and send the time as an argument to your server when you're updating via AJAX, and then have the server quickly check what new users were added, and send just those. Then, just merge the new array of new servers with the old array. I highly suggest not making a call every 10 seconds for a new name though. Not only will you run up more bandwidth on your server, you will increase the CPU usage when it has to find the next user in the list for you, and then send you that one. For good practice, always let the client do as much of the work as possible, without having lag. There is only one server, but many more clients. Each operation you shift to the clients will save your server hundreds, if not thousands, of operations.

As for long polling vs setInterval, I would recommend setInterval in that case. You can at least send a request with a time argument, specifying the last update time, and thus only needing to send that small portion, instead of the entire data array.

AniV
  • 3,997
  • 1
  • 12
  • 17