Controlling a Map with Timeline in Exhibit

248 views
Skip to first unread message

Jon Crump

unread,
Sep 1, 2017, 2:07:24 PM9/1/17
to SIMILE Widgets
Dear all,

I thought I'd take another stab at recapitulating the functionality of my Simile Timeline/Google Maps mashup using Exhibit3. Way back when I first developed The Itinerary of John website, David Huynh very generously pointed me in the right direction to get the Timeline to control the appearance of markers on the map. I've manage to succeed (partially) in doing the same thing within Exhibit, but I'm not very happy with the solution. First of all GMaps breaks it, as described below, but more importantly, I can't help suspecting that there's a more direct way to associate Timeline Events with GMap Markers in the custom Timeline, or GMap constructors.

In the Exhibit example linked above (drawing its data from a Google Spreadsheet), I've had to use nested `for` loops to compare the LatLng of Events with the LatLng of the map markers in order  to find a match. This seems clumsy and inefficient. Is there any way to associate, when it's created, a Map Marker with an Exhibit database Item? This would obviate the need to match them up in the `OnScrollListener` function. I will fork the github Repo and hack on the source if I need to, but I thought I would first ask the assembled wise here if there is some more straightforward way to do this in either my Timeline or Map constructors.

To make my solution even less satisfactory, it appears that GMaps breaks it anyway, and in a way that I've yet to find an explanation for. (Maybe this is some sort of JavaScript artifact?) There appears to be a float rounding error somewhere. After hunting through the code a lot, it appears that the place where an item's LatLng is plotted on the map and a map icon is generated is in map-view.js (line 459):

Exhibit.MapView.prototype._rePlotItems

When this function calls
currentSet.visit(function(itemID)

A new LatLng is created by `GLatLng()` and is added to `locationData` object.

Then, in a function:
var addMarkerAtLocation = function(locationData)

when a new marker is made:
var marker = new GMarker(point, icon);

it takes its data from a `point` generated thus:
var point = new GLatLng(locationData.latlng.lat, locationData.latlng.lng)

When the GLatLng constructor is invoked, for some reason, in half of my locations the latlng float data is rounded so that the coordinates of the marker no longer matches the coordinates for the exhibit item.

This is, therefore not an Exhibit problem, but rather a Google Maps problem, but if anybody has some insight into why this happens, I'd love to hear about it.

Thanks,
Jon

Jon Crump

unread,
Sep 4, 2017, 1:30:46 PM9/4/17
to SIMILE Widgets
Well, I "fixed" the rounding error using the unsatisfactory expedient of 
position.lng().toFixed(7)
(unsatisfactory because I still don't know why a given longitude might be rounded to 14 decimals where another wouldn't).

But a much more satisfactory solution would be to discover a way to access the map markers programmatically using the event data so that I can setVisible() on them in the ScrollListener.

Jon Crump

unread,
Sep 9, 2017, 1:00:45 PM9/9/17
to SIMILE Widgets
For anyone who's interested

http://neolography.com/staging/itinExhibitSketch.html will remain up for the time being, illustrating the problem of the `date` property of the BandInfo Object. Exhibit does not honor any value set there in a custom Timeline configuration.

I did make some progress, however, on displaying map markers sequentially in response to timeline scroll. The example site at http://neolography.com/staging/itinExhibitSketchII.html represents my progress to date on this idea.

Any insight or comment is welcome,
Jon

jjon

unread,
Oct 28, 2018, 2:38:16 AM10/28/18
to SIMILE Widgets
In the unlikely event that anyone notices what goes on in this group: An Update,

It's taken a year to return to this experiment, but I spent a day with it today and worked out the main bug in my effort to show/hide map markers in response to timeline scroll. This toy example is self contained except for the Exhibit3 libraries (which I'm hosting locally), and the Google Map libraries.

In my first attempt, it worked just fine until I used the facets to filter the data, then when I cleared the filters, all my map markers showed and no longer responded to the scroll handler. My solution was to update an object containing the map markers as derived from the Map:

        b0.addOnScrollListener(function(){
            markerObj
= deriveMarkersObj();
            visibleEvents
= {};
           
var dmax = b0.getMaxVisibleDate();
           
var dmin = b0.getMinVisibleDate();
           
var iterator = b0.getEventSource().getEventIterator(dmin,dmax);
           
while (iterator.hasNext()) {
               
var e = iterator.next();
                visibleEvents
[e.getProperty('LatLong')] = true;
           
};
                       
           
for (var m in markerObj){
                visibleEvents
[m] ? markerObj[m].setVisible(true) : markerObj[m].setVisible(false);
           
}

       
});

I'm very pleased to say that this worked in my little toy example; however, I worry that rebuilding the `markerObj` every time the scroll handler fires will not scale. If anybody has any ideas about how to do this more efficiently, I'd sure like to hear about it.

Thanks,
Jon


Luis Miguel Morillas

unread,
Oct 28, 2018, 8:15:44 AM10/28/18
to simile-...@googlegroups.com
It's very interesting, Jon.

I'll take a look when I'll be less busy.

Saludos,

-- luismiguel (@lmorillas)
> --
> You received this message because you are subscribed to the Google Groups "SIMILE Widgets" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to simile-widget...@googlegroups.com.
> To post to this group, send email to simile-...@googlegroups.com.
> Visit this group at https://groups.google.com/group/simile-widgets.
> For more options, visit https://groups.google.com/d/optout.

Contemplative

unread,
Oct 29, 2018, 8:33:45 PM10/29/18
to SIMILE Widgets
I notice... am very interested in continuing and extending my use of Exhibit.  So you are not talking in a void... :-)
wjw

Drew

unread,
Oct 31, 2018, 3:50:27 PM10/31/18
to SIMILE Widgets
Likewise. I am very interested in this function.

Thanks for the work!
Drew

jjon

unread,
Nov 2, 2018, 3:49:02 PM11/2/18
to SIMILE Widgets
Update:

Thank you Drew, Contemplative, and luismiguel  (@lmorillas) for your interest. I've done more fiddling with this than I thought I would, and I've posted a new version. This is very clumsy. If I do any more hacking on this, I'll have to get it under version control, at github I suppose. But this new version is just 'musing out loud'. In SketchII I was worried that the markerObj that contained all the map markers was being regenerated with each iteration of scroll. It seemed too expensive to me (though, I didn't ... like ... test it. It just offended my sensibilities). So I went poring over the Exhibit source code and found in `exhibit-master3.1.2-rc3/scripted/src/extensions/map/scripts/map-view.js` this function: `Exhibit.MapView.prototype._rePlotItems`

This function is called every time there is some change to the `currentSet`, say, changing the facet filters and the map has to be updated by `Exhibit.MapView.prototype._reconstruct`. `_rePlotItems` 'visits' each item of the currentSet, determines if it should be on the map, and sets properties for it, including creating and plotting a map marker.

In the process, the `_rePlotItems` function generates a useful object for internal use: `locationToData`. This is a hash keyed by the latlong string of each of the map markers, just as my `markerObj` did in SketchII. So SketchIII is my very clumsy attempt to hijack `_rePlotItems` and add a line to populate a global variable (yes, I know, I know. I did say it was clumsy) which gets updated whenever there is a change in the currentSet, rather than every time onscroll fires.

If somebody more expert with JS, and/or more familiar with the Exhibit code base, could suggest a less clumsy way to do this I'd sure like to hear about it.

thanks,
Jon

Contemplative

unread,
Nov 2, 2018, 6:27:10 PM11/2/18
to SIMILE Widgets
This is nice work!  I am impressed.

I wonder if the extra digits in the latitude is caused by the way the data is stored in the google spreadsheet(?). Being that it is just the latitude, it might be the latitude column which is set for some length digits, while the longitude column may be set for a limited number of digits?  Just a guess.

I tend to always source my data out of a local database, usually SQLite3 for smaller data sets and MySQL or MariaDB for larger data sets, so never go to google.  So when I try out what you have done, touching google maps will be a first for me.  It has been a while since I have built a new exhibit and I have a few things I want to try based on what you have made work here.  I look forward to it, and thanks again for your work and willingness to share. I wish I was good enough with the level of java-script used in exhibit to help out: Unfortunately, I am not.... 



Wes (Contemplative)

Luis Miguel Morillas

unread,
Nov 3, 2018, 5:44:48 PM11/3/18
to simile-...@googlegroups.com
Hi, Jon,

You are mixing timeline2 and exhibit 3 timeline. Use only the second better.
This is your example rewrote
https://lmorillas.github.io/timeline_maps/ I must review come typos,
but I have no time now.



Saludos,

-- luismiguel (@lmorillas)

jjon

unread,
Nov 3, 2018, 6:33:40 PM11/3/18
to SIMILE Widgets
Nice of you to say, but I know this example is naive and broken. You should not take it as authoritative in any way. For example, I just realized that I've been using an onLoad() function because of my earlier experience with Timeline. With Exhibit's event driven API, this is no longer necessary. Sketch III has been updated to reflect this.

jjon

unread,
Nov 3, 2018, 6:50:26 PM11/3/18
to SIMILE Widgets
Thanks Luis! I appreciate you taking an interest. Note that SketchIII has already been updated to reflect my realization that the old onLoad() function isn't necessary. I will study your rewrite with interest. It's a little embarrassing to expose my naive self education in this way. I hope it may prove instructive to others. When you can, I hope you can offer some insight into the way I've implemented the onScroll handler

best,
Jon
Reply all
Reply to author
Forward
0 new messages