Closed PennyBeames closed 9 years ago
No worries; while it might not be an issue with L.timeline itself on the surface, it could reveal some flaws or worthwhile features. Also, Github has way nicer code presentation abilities. :smile:
Class inheritance of L.timeline
is
L.layerGroup > L.featureGroup > L.geoJson > L.timeline
So in effect, anything you can do with a layerGroup, you should be able to do with a timeline.
I found this StackOverflow answer which could work for your case? There's also a filter
option available (see: the bottom of this page), but I'm not sure how you could change it after the timeline was created.
FWIW in the code you posted, it's saying feature
is not defined because, in the for (prop in permits)
block, feature
is not in scope. Perhaps it should have been permits[prop].properties.purp
?
Hey there. You're very generous. :) Finally had a chance to get back to this.
The full site can be found here.
So, the kids at Mapbox support suggested I use this markercluster template as a basis for my structure. It's a huge help, because it basically sets out what I had been thinking but didn't really know how to write.
You said in your message "but I'm not sure how you could change it after the timeline was created." That's also what I was thinking.
To get around that, I figured I could add a new layerGroup to the map and then add the timeline to the layerGroup. That way, every time someone clicked on one of the checkboxes, the function called could include the clearLayers method and would clear the timeline completely. Once the timeline had been cleared, the function would build a new one and add the new one to the layerGroup. It's probably a resource-heavy approach, but I really couldn't think of anything else.
So here's my problem. NOTE: this could just be because I don't really know what I'm doing.
I set up my check boxes:
<div class="panel-body" id="damTypes">
<div><input type='checkbox' name='filters' onclick='showDams();' value='hydroelectricity' checked> Hydroelectricity</div>
<div><input type='checkbox' name='filters' onclick='showDams();' value='irrigation' checked> Irrigation</div>
<div><input type='checkbox' name='filters' onclick='showDams();' value='watersupply' checked> Water Supply</div>
<div><input type='checkbox' name='filters' onclick='showDams();' value='recreation' checked> Recreation</div>
<div><input type='checkbox' name='filters' onclick='showDams();' value='other' checked> Other</div>
<div><input type='checkbox' name='filters' onclick='showDams();' value=null checked> No data</div>
</div>
The showDams function lives in the onclick and everything is listed as checked, which presumably means that everything included in the original geojson object will display.
Once in the script I create my new layerGroup and add it to the map. I also create my layers variable as that's going to act as my target later on.
//overlay that will contain dams data, cleared using clearLayers method later on
var overlay = L.layerGroup().addTo(map);
//future target
var layers;
Cool. So far so good.
The rest of the action is in a $.getJson callback:
$.getJSON("js/dams.json", function(dams) {
Rather than sending all of the geojson features straight to timeline, though, what I figure I can do is create a new L.geoJson object, send all of my newly filtered features into that L.geoJson object, and then pass THAT through L.timeline. Is this making any sense?
So I set up my target and call the initial showDams function:
map.on('ready', function(e) {
layers = e.target;
//call showDams function from checkbox onclick event
showDams();
});
set up my filters:
//filters, which for some reason I can't get right -- something about ('damTypes').filters is not working
var filters = document.getElementById('damTypes').filters;
create my new L.geoJson:
var damsLayer = new L.geoJson();
and then run the showDams function (that isn't working):
function showDams() {
//create an array to house the value strings from the inputs above, ie 'hydroelectricity', 'irrigation', etc
var list = [];
//console log keeps throwing an error: Cannot read property 'length' of undefined
for (var i = 0; i < filters.length; i++) {
if (filters[i].checked) list.push(filters[i].value);
}
//clears previous timelines from the overlay
overlay.clearLayers();
//add newly filtered data to the damsLayer geojson object, code based on clusterMarkerGroup example
layers.eachLayer(function(layer) {
if (list.indexOf(layer.feature.properties.line) !== -1) {
//addData method called on damsLayer
damsLayer.addData(layer);
}
});
}
Once I've got all the new data added to the damsLayer geojson object, I figure I can just run it through timeline like so:
var timeline = L.timeline(damsLayer, {
steps: 4000,
duration: 20000,
waitToUpdateMap: true,
onEachFeature: onEachFeature,
formatDate: function(date){
return moment(date).format("YYYY");
},
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, damsStyle(feature));
}
});
And then add the timeline to the overlay layerGroup:
timeline.addTo(overlay).on('load', finishedLoading);
But I keep getting errors, either directly in my code or in leaflet.timeline.js (because of my code).
Any insight?
Sorry it's taken me a while to respond.
That plan would work, but there were some scope issues in your code. I've shuffled some things around, but the code's a bit long so I've put it on a pastebin: http://pastebin.com/BRaw9H0x
It did reveal an issue with Leaflet.timeline -- specifically, it does not remove the slider control when it is removed from the map. I will be pushing a fix for that shortly.
Hey!
This was really generous. Way more comprehensive than what I've been experimenting with (none of which was working). Thank you x a million for fixing the scoping issues.
Also, this:
// in the data, start and end are integers, so the dates are
// getting parsed wrong
Thank you! That one was bugging me.
I look forward to the fix. In the meantime, do you have a tip jar?
Me again,
I ran the code you wrote without updating to timeline 0.4.1 just to see what would happen. Aside from the old timeline controller not being removed on click, everything was working great.
But when I updated to 0.4.1, I started getting an error from the leaflet-src.js:
It broke other maps I built that were relying on the same leaflet-timeline.js file once I'd updated it. Other maps that were only using timeline and weren't relying on any click events or filtered data.
Thoughts?
Ah, whoops. 0.4.1 adds a version check (in another codebase where I'm using this, it's on Leaflet 0.8, and there was a slight API change between 0.7 and 0.8), and I forgot to convert the string to an integer. Guess I'm making 0.4.2 :|
All good, thank you!
Seriously, though -- where is your "buy me a beer" button?
It works!
Hey!
I wasn't sure if I should submit this as an issue or not. If I follow instructions and my stuff runs backwards, that's an issue. If I can't get something to work because I just don't know enough, well, that's a different story.
Here's my deal.
For the most part, when I want to implement toggleable layers, I add my data to layerGroups or set a filter function on a Mapbox featureLayer. But because L.timeline works akin to L.geojson, those options seem out of reach.
I have a dataset that I can divide into 11 different categories based on the value in features.properties.purp. I was thinking maybe I could set up 11 arrays and then loop through the json object and push each element to its corresponding array?
Then use an unordered list with id'd links and an onClick that would allow the user to choose which arrays are active and which aren't?
I tried this but kept getting Reference error: feature is not defined.
I feel like I'm reaching. It doesn't help that my geojson is huge (40 000 records), which is the biggest reason why I want users to be able to toggle layers on and off.
Thoughts?