-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple GeoJSON timeline layers, with a single timeline control #23
Comments
To see if I understand this correctly: add multiple Sounds very reasonable, and would probably help clean up the code in reworking it. Right now there's a lot of interdependency between the control and the layer; implementing this would sort of force a separation. Could get hairy on edge cases, e.g. the timeline is currently playing and then the user toggles a layer. |
You have it exactly. The cleanest from the user API viewpoint. Would be if be to have the first L.Timeline work as normal, and if you chose to add a second, it would only add the GeoJSON layer, and would "connect" to the original Timeline control. If you are refactoring, then I would make each layer subscribe to the Timeline control, and seperate the code modularly. But an alternative is to make the TimeLineControl (TLC) the master and have the layers register with it, and it loops through the layers to "animate" them. There probably are other ways to tackle this also. Our use case is a small area of land where we have lots of sensors (internet of things) and we are trying to compare what features they pick up and which they don't and what footprints they have and which they do not. We want to see all the A/B choices. My worry right now is about the Date() resolution. Sensor data is coming in at 100Hz in some cases and so I need the start and stop to be about 10ms apart on the timeline. |
RE: edge conditions If the master is the layer (it does the animating) and just subscribes to time events, you can handle the layer invisible issue cleanly. You could also have the TLC as master and simply update the GeoJSON layer if it was invisible. I would leave the tick marks as they were even if a layer was turned off, it seems too much work to keep separate bags for each set of tic marks. |
Regarding your last point, I think that shouldn't matter. The timeline automatically adjusts to the endpoints of the data given to it, and calculates a step amount based on that. I don't remember anything forcing the step to be an integer. It does raise a point about the multiple-layers thing. I guess there could be two modes for merging, offset and absolute. Basically if you have one layer which is 10ms of data at noon on day 1, then another layer which is 10ms of data at noon on day 2, then the timeline will show two blips one day apart for absolute mode, or one 10ms span from 0 to 10ms in offset mode |
Well, most use cases I can think of where you have 2+ layers is where the data actually overlaps in time. Not blips widely scattered. You want to do A & B comparison of data at the same time. If data is for a few seconds each day (with lots of data at each time). Then it is going to be really hard to get the duration and steps with the right granularity. You would have to go to a different time model, where gaps are fine and the timeline moves by time-varying intervals based on data presence. |
Offset mode would basically make the layers behave as if the first So...
(note: would've been better to show dataset 1 and 2 as starting at non-zero times to show that offset would always start at 0 regardless of the datasets.. but I already typed this up as it is 😓) Because the timeline just jumps by a certain step each frame, if the data you have is in tiny clusters relative to the overall time span, the animation would show it all happening in one frame. (Though using the next/previous would still work, because those just find the next point in the data, they don't move stepwise) The same thing could be achieved by just preprocessing the data to align them all to the same start point, but this way might be easier for some people. |
I would not be fond of the offset mode. I do not want the analysis framework shifting the times of my data to indicate overlap when they really are separate. Of course, I might be looking at this all through my "soda straw" :-( But what I have is a lot of sensors (LIDAR, Stereo Vision, Doppler Radar, etc) all reporting of features. What I want to do is FUSION of the sensors. Each sample is time-stamped with an absolute GPS clock value. This is key for the FUSION, since I need to know what sensors see simultaneously. I don't want an shift of timeline. I want to see in Leaflet the raw overlay and figure out what I need for Fusion algorithms. Also I really like what you did with intervalFromFeature, function, since I don't get the data stream tagged with start/stop. Instead I get a time stamp for each sample. Knowing the sample rate (4Hz, 50 Hz, 1000Hz) I can then use the intervalFromFeature to compute start/stop. Yes, there might be need to overlap separate runs, but I have pretty good tools for bulk processing the JSON if I want to (SAFE FME). So if I want to align separate runs on separate days I just have to add an offset to the intervalFromFeature function. That strikes me as the best way to get offsets. BTW, I assume you know about the trade-offs of continuous time vs. discrete time: https://en.wikipedia.org/wiki/Discrete_time_and_continuous_time |
The offset vs absolute mode would be a toggleable option, defaulting to absolute. Kind of in keeping with the "minimal external processing needed" course this has been taking (e.g. with the Also, while I don't think it's quite possible right now, the amount of work to shift it away from |
Now that is a fascinating generalization, and I applaud it. |
Perhaps this should be a different issue, but I noticed that the "change" event does not return much useful info. While you are refactoring could you have it return the current time on the timeline, and the list of GeoJSON features that are active? Why do I ask? I have a GeoJSON file that has point features for the location of a automobile and its heading. I am using a RotableMarker (https://github.com/bbecquet/Leaflet.RotatedMarker) that shows that marker. I just allow the GeoJSON layer to default for pointToLayer, it would have a blue marker. I want it to set the makers as invisible on load, and then to render it manual on each timeline change event (translate and rotate). I think you could stick the active features for the timeline into a jagged array, with a bin for each bucket in the duration/step. Then put the active features for that bucket of time in the list. It should be easy to return that pointer with the event object. What do you think? |
You can (I believe) already access the time with var layer = L.timeline(...);
layer.on('change', function() {
var currentTime = layer.time;
var currentLayers = layer.displayedLayers;
}); It's easy enough to throw that into the |
I was hoping for convenience values attached to the event, because what I need is less the actual time, but more the features that are active at CurrentTime. But both would be good: e.time = Timeline.Date
e.features = [ {featureA, featureB, ...]; // across all GeoJson Timeline objects controlled by the timeline. |
Right, that's what's in Could perhaps better be called |
I was actually planning on adding something in the readme asking for anyone who uses it to let me know what sorts of things they're doing with it, and maybe including links to examples "in the wild" (with permission of course). I've only seen a few, but they've all been really neat. |
I've published a beta of 1.0.0, which (among other things) allows multiple Please try it out, and let me know if anything's awry, either with the code or the docs. Thanks! |
I will, but one quick question, that is not covered in the documentation. What we call a klutz question: If you want multiple GeoJSON layers controlled by a single Timeline, what do you do? Do I guess right that you just keep adding Leaflet.Timelines()? What if they have different time ranges, durations, steps, time, formoutOutput, enablePlayback? And if not, then how does one add a second GeoJSON layer to the existing timeline? |
Yeah, each GeoJSON layer would be a separate The layers themselves no longer have a concept of duration, steps, etc. All of that is managed by the control. The only options for the layers is The layers expose the start (x) and end (y) of their datasets and the control figures out the range it should have based on all of the layers it manages (min of xs, max of ys). You could also specify a The steps and duration aren't really meant to have anything to do with the data. They're a means of mucking with the playback speed. Individual points are accessible if necessary by dragging the slider and/or using the next/prev buttons |
Ok, I got it sorry for being dense. I am working on other parts of the problem this morning, and the brains cells for javascript are being used for UDP, Python, WebSockets, and a DotNet C# gateway that is translating from binary UDP sockets to GeoJSON for this layer. I want to get this tested this afternoon. |
In addition to Polyline and Polygon layers, I have GeoJSON layer that has point data (the location of a car). It seemed to me a waste to instantiate over 5,000 marker objects for a GeoJSON timeline for that layer. So what I tried to do is set a GeoJSON.filter function that checks to see if the GeoJSON has GPS property, and not "show" that GeoJSON layer. this.show = function (feature, layer) {
var type = feature.properties.type;
switch (type) {
case "Oxford.GPS":
return false;
default:
return true;
}
} Then I catch the change event and get the list of objects, (and i was going to the properties to update a single marker (which is actually has a rotate with it) Now, it is true that I get the change event for each object on the timeline (including these point objects) But the list of changed objects is 0, when I filter. But it is fine if I don't. this.newTime = function (e) {
var currentTime = timeLayer.time;
var currentLayers = timeLayer.displayedLayers;
console.log("Timeline Change: " + JSON.stringify(e.type));
$.each(currentLayers, function (index, layer) {
var bag = layer.geoJSON.properties;
var type = bag.type;
var loc = layer.geoJSON.geometry.coordinates;
switch (type) {
case "Oxford.GPS":
Overlays.doGPS(loc, bag);
break;
default:
console.log("overlays.newTime: unknown GeoJSON type: " + type);
}
});
}
this.doGPS = function (loc, props) {
CarState.lat = loc[1];
CarState.lon = loc[0];
CarState.alt = loc[2];
CarState.heading = props.heading;
CarState.time = props.start;
CarState.valid = props.valid;
switch (props.correction) {
case 0:
CarState.correction = "None";
break;
case 1:
CarState.correction = "WAAS";
break;
case 2:
CarState.correction = "Differential";
break;
case 3:
CarState.correction = "RTK";
break;
case 4:
CarState.correction = "Other";
break;
}
Mapper.update();
} |
If you're filtering out features of type "Oxford.GPS", they never get added to the But I think if I switch |
Right, I suspected that. But the odd thing was the change events were happing based on where the "invisible" items were. So they seemed to be added to the GeoJSON layer. So I was hoping it would be possible to get even the filtered ones back with the getDisplayed() without too much hassle. getAllCurrent() ? (better than getDisplayedAndNotDisplayed() ) |
I know I am going to have to learn coffeeScript so I can help you directly (and figure out how to get gitHub source control to work with our firewall, I can do local git repositories, and our local server, but I.T. is nasty here, and we have real problems with going through the firewall. So I hope you don't mind and I will post the issues I have below. |
FWIW it's no longer written in Coffeescript; it's ES2015 (aka ES6) now. Still transpiled, but it's Javascript -- just from the future. |
This could be all fixed for me if I could specify the GeoJSON filter: function, and stilll gett the getDisplayed() function to return all of them. Right now I have a kludge to sett zIndexOffset for the car icon marker to 10,000. So that it stays on top of all the junk in the marker pane. I don't know if this is going to be an issue with the PolyLine and Polygon GeoJSON paths.
Multiple issues;
Minor documentation issues:
I probably need functions for setStart() and setEnd() since different logs will have different time ranges. BTW, I did something slightly illegal, and extended the GeoJSON spec, At the end of each GeoJSON file I load I put a bunch of metadata, which is ignored in all the programs I have tried. .... {"type":"Point","coordinates":[-83.697503617676588,42.302661024805644,278.33480834960938]},"type":"Feature"},
{"properties":{"heading":275.377625,"correction":3,"valid":true,"type":"Oxford.GPS","start":"2015-11-19T15:52:57.3530000Z","end":"2015-11-19T15:52:57.3630000Z"},
"geometry":{"type":"Point","coordinates":[-83.697504531266375,42.302661093646158,278.33636474609375]},"type":"Feature"}
],
"version": "1.0",
"samples": 6152,
"start": "2015-11-19T15:51:56.2660000Z",
"end": "2015-11-19T15:52:57.3530000Z",
"duration": 61087.00,
"description": "demo"
} |
I found this undocumented method in your source: addTimelines() and did the call, but it did not update the TimelineSlider. Hey, thanks for switching to ES6! |
The name was actually changed specifically because it doesn't refer only to dates anymore. The default behavior does treat everything like a date, but it's all overridable. |
I think I got you. You are saying that begin/end pairs in the GeoJSON as well as the axis of the "timeline" can be any continuous monotonically increasing value. E.G. instead of time, one could have luminence, chrominance, or pressure, Any comment on my other findings? Would some of the "geoJSOn files that I am using help". I want to help contribute to this. |
I really like the simplicity and speed of your plug in. There is a much more complex one for leaflet: https://github.com/socib/Leaflet.TimeDimension but I see no advantages for GeoJSON with it. Except for one feature I could really use (unless I am missing something, and it is already possible).
How can I have multiple GeoJSON (actually your sub-class timeline) all controlled synchronounsly from one timeline control? Yes, I could manage the visibility within the layer via properties, but then I lose out on all the layer control plugins (tree, select) and and the other features that come with leveraging the native Leaflet layer model.
I have not dived into the source, but it seems like this would be a great feature for those who need to compare event streams and look for conjunctions of events.
The text was updated successfully, but these errors were encountered: