Display markers reactivly on a map (noUislider / googlemap API)

Hello,

I try to display projects according to a slider (range, with noUislider). The names of the projects are displayed reactively, depending on the cursor.
Now… i want to do the same thing with a bunch of googlemap markers. When I move the cursor , I want to see the markers change on the map (googlemapJs API)

Reactive list of projets :

Template.body.helpers({
  projects: function() {
    var slider = Session.get("slider"); //get the range values
    return Projects.find({
      endDate: {
        $gte: slider[0],
        $lt: slider[1]
      }
    });
  }
});

Way I currently display markers after loading the map :

Template.body.onCreated(function() {
    GoogleMaps.ready('atlasArchi', function(map) {
        var projectsArray = Projects.find().fetch();
        var marker, i;
        for (i = 0; i < projectsArray.Length; i++) {
            marker = new google.maps.Marker({
                position: new google.maps.LatLng(projectsArray[i]['lat'], projectsArray[i]['lng']),
                map: map.instance
            });
        }
    });
});

So, i guess i can’t use Template.body.onCreated for displaying the markers.
How can i use Template.body.helpers to make my list of markers reactive ?

Thanks !

screenshot :
http://img11.hostingpics.net/pics/374357atlasArchi.jpg

1 Like

You can make something inside onCreated reactive with autorun like this (not tested):

Template.body.onCreated(function() { var templ = this;
GoogleMaps.ready('atlasArchi', function(map) {
    templ.autorun(function() {
        var slider = Session.get("slider"); //get the range values
        var projectsArray = Projects.find({
            endDate: {
                $gte: slider[0],
                $lt: slider[1]
            }
        });
    
        var marker, i;
        for (i = 0; i < projectsArray.Length; i++) {
            marker = new google.maps.Marker({
                position: new google.maps.LatLng(projectsArray[i]['lat'], projectsArray[i]['lng']),
                map: map.instance
            });
        }
    });
});

});

It Seems to work after some changes.
The markers still don’t appear in real time. But it works in the console, the array is updated . So I just have to figure out how to remove markers on the map, when that are not in the array.
Thank you very much !

code :

Tracker.autorun(function() {
  var slider = Session.get("slider"); //get the range values
  projectsArray = Projects.find({
    endDate: {
      $gte: slider[0],
      $lt: slider[1]
    }
  }).fetch(); //must be an array ? i think
  console.log(projectsArray);
  var projNumber = projectsArray.length;
  var marker, i;
  for (i = 0; i < projNumber; i++) {
    marker = new google.maps.Marker({
      position: new google.maps.LatLng(projectsArray[i]['lat'], projectsArray[i]['lng']),
      map: map.instance
    });
  }
});

Use observe instead of fetch, see http://docs.meteor.com/#/full/observe
Just add a marker on added and store it in an array. Remove it on removed event.

1 Like

I vote for observing and using maps API to add and remove instead of recreating it.

1 Like

Thanks, good idea !

It kind of works… exept for the “removed” event.

Template.body.onCreated(function() {
  GoogleMaps.ready('atlasArchi', function(map) {
    Tracker.autorun(function() {
      var slider = Session.get("slider"); //get the range values
      var projectsList = Projects.find({
        endDate: {
          $gte: slider[0],
          $lt: slider[1]
        }
      });
      
      var markers = {};
      
      projectsList.observe({
        added: function(document) {
          var marker = new google.maps.Marker({
            animation: google.maps.Animation.DROP,
            position: new google.maps.LatLng(document.lat, document.lng),
            map: map.instance,
            id: document._id
          });
          markers[document._id] = marker;
        },
        removed: function(oldDocument) {
          markers[oldDocument._id].setMap(null);
          google.maps.event.clearInstanceListeners(markers[oldDocument._id]);
          delete markers[oldDocument._id];
        }
      });
    });
  });
});

I can see that it works through animation of markers. When i move noUislider, new markers are falling down.
But, the wrong markers are staying on the map. I’ll try to figure it out.
When i write a console.log(oldDocument); in the removed event, nothing happens. The “removed” event is not called when projectsList is changing.

1 Like

Maybe place the var markers = {} outsitde the autorun. I guess it is always reinitialized.

1 Like

I tried… same result.
The “removed” event is never called. But the “called” event works every time I move the slider.
I don’t now why… i’m trying to find examples. People seem to use observe() and removed…
Maybe my query does not change the right way ? ^^
Or maybe the removed event is never called because I didn’t specified something in the query.

1 Like

Maybe an idea… but i’m not sure.
When i move the slider, session values are changing a lot. So, my query is changing a lot. Then maybe the callback “removed” is not called.
http://img15.hostingpics.net/pics/431978added.jpg

So I tried to modify the slider function :

Template.slider.rendered = function() {
        this.$("#slider").noUiSlider({
            start: Session.get("slider"),
            connect: true,
            animate: true,
            step: 1,
            range: {
                'min': rangeStartDate,
                'max': rangeEndDate
            },
            format: wNumb({
                decimals: 0
            })
        }).on('slide', function(ev, val) {
            // set real values on 'slide' event
            //Session.set('slider', val); I DELETED THIS LINE

        }).on('change', function(ev, val) {
            // round off values on 'change' event
            Session.set('slider', [Math.round(val[0]), Math.round(val[1])]);
        });
    };

After removing the line above…
Nothing changes , I don’t understand why the " observe" function does not see changes on the query. Even if i have only one change in my query when i move the slider.

The same scenario worked for me, but I used pub/sub, ie. when changing a parameter I autorun the subscription. You can see the effect here

1 Like

Thank you,
I guess publish and subscribe are my last chance… I’m stuck on this problem. What do you mean by “ie” ?

I tried a lot of things today, but nothing works (old markers are staying on the map).
When i move the slider, it is as if a new group of projects were included in my query. But the “old documents” are not accessible.
I would like to understand exactly how observe() works, but i can’t find this kind of documentation.
Maybe the problem is changing values in the query like i do, with $gte and $lt.

ie = that is (latin id est).

As for you old markers, when you check your collection in the browser after changing the slider, does it have the same number of documents? or just more documents?

Even though you can observe a cursor at any time, I believe for what you want to accomplish you need to use the callback from your subscribe and do something like documented in the docs.

You can also check out my code for blaze-maps

2 Likes

When i change the slider, i can see some markers falling down on the map (googlemap marker’s animation).
The markers that fall on the map correspond to the projects that are part of the range. So if i change the slider, i can see 2 projects falling, 10 projects, 4 projects… etc It’s good.
The only problem is that the markers don’t fade when projects are no longer part of the range. And I saw on the console that the callback “removed” was never called.

If I initialize the map with a small slider, and gradually increases the range , the projects appear gradually. There is a problem for the other way.

It works ! Thank you very much.

What I’ve done so far :

Server side :

Meteor.publish('projects', function(slider) {
    return Projects.find({
        endDate: {
            $gte: slider[0],
            $lt: slider[1]
        }
    }, {
        sort: {
            name: 1
        }
    });
});

Client side :

Tracker.autorun(function() {
    Meteor.subscribe('projects', Session.get('slider'));
});

and

Template.body.onCreated(function() {
    GoogleMaps.ready('atlasArchi', function(map) {
        var markers = {};
        Tracker.autorun(function() {
            var query = Projects.find();

            query.observe({
                added: function(document) {
                    if (document.lat && document.lng) {
                        var marker = new google.maps.Marker({
                            animation: google.maps.Animation.DROP,
                            position: {
                                lat: document.lat,
                                lng: document.lng
                            },
                            map: GoogleMaps.maps.atlasArchi.instance,
                            id: document._id
                        });
                        markers[document._id] = marker;
                        console.log('observe - added - ' + document._id);
                    }
                },
                removed: function(oldDocument) {
                    markers[oldDocument._id].setMap(null);
                    google.maps.event.clearInstanceListeners(markers[oldDocument._id]);
                    delete markers[oldDocument._id];
                    console.log('observe - removed - ' + oldDocument._id);
                }
            });
        });
    });
});

Not sure i understand the original problem. I guess something did not work because it was the client side.

1 Like

‘Object.observe’ is deprecated and will be removed in M50, around April 2016. See https://www.chromestatus.com/f… for more details.

so is query.observe still going to work in your reactive google map? I dont know if its the same api

1 Like

Sorry, I’m rewriting the app with node.js, socket.io and maybe react to interact with googlemap. I did not start the front-end…
I was tired of several problems with Meteor (outdated dependencies , no npm, hidden complexity, front end, deployment on a VPS!!). I want something transparent that is not “magic”. :slight_smile:

1 Like

if you will use react, take a look at this plugin:


(pretty cool integration i think)

1 Like

Yes, I already did :wink:

It should not be too difficult to send data to the client (markers) with socket.io.

Some people advised me to adopt redux or similar technology in order to “prerender” react on the server, but it gets complicated for me… Can I just use React with the node router or this is incoherent ?

1 Like

Hi, I interest with this code, can I show the demo? thanks