Closed fox91 closed 8 years ago
Hi @fox91, yes, you can show GeoJSON layers with data obtained independently for each time.
This is the idea in example12. It doesn't use a GeoJSON layer (its a Heatmap), but at the end it's the same.
Here you have the code adapted to GeoJSON. I haven't tested it, and it doesn't include any cache.
L.TimeDimension.Layer.AjaxGeoJSON = L.TimeDimension.Layer.extend({
initialize: function(layer, options) {
L.TimeDimension.Layer.prototype.initialize.call(this, layer, options);
this._currentLoadedTime = 0;
this._currentTimeData = null;
},
onAdd: function(map) {
L.TimeDimension.Layer.prototype.onAdd.call(this, map);
if (this._timeDimension) {
this._getDataForTime(this._timeDimension.getCurrentTime());
}
},
_onNewTimeLoading: function(ev) {
this._getDataForTime(ev.time);
return;
},
isReady: function(time) {
return (this._currentLoadedTime == time);
},
_update: function() {
if (!this._map)
return;
var layer = L.geoJson(this._currentTimeData, this._baseLayer.options);
if (this._currentLayer) {
this._map.removeLayer(this._currentLayer);
}
layer.addTo(this._map);
this._currentLayer = layer;
},
_getDataForTime: function(time) {
if (!this._map) {
return;
}
var d = new Date(time);
var url = d.format("yyyymmdd'T'HHMMss'Z'", true) + '.geojson';
var callback = function(status, data) {
if (status == 'ok'){
this._currentTimeData = data;
} else{
this._currentTimeData = [];
}
this._currentLoadedTime = time;
if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
this._update();
}
this.fire('timeload', {
time: time
});
};
$.getJSON(url, callback.bind(this, 'ok')).fail(callback.bind(this, 'error'));
},
});
L.timeDimension.layer.ajaxGeoJSON = function(layer, options) {
return new L.TimeDimension.Layer.AjaxGeoJSON(layer, options);
};
Something similar might be added to the plugin as new kind of timedimension layer. It should include as an option a function to construct the URL of the data from a time.
Hi @bielfrontera ,
Any chance we can get clear comments on the above code: i.e for better understanding? Currently i want to use this library for updating the leaflet map : every month will have a geo json (not wms based). I'm working on a data science project with the geo political data set(GDELT): and I would love to use this library for viz.
Cheers
Thanks I figured it out :) :+1:
Hi @rsethur.
Ok, perfect.
Basically, it loads json data via ajax from a dynamic url (constructed with time as parameter). This data is stored at _currentTimeData
. Then, when the layer is updated (this is called from timedimension, when a new time is applied), this class removes the old layer and creates a new layer with current data.
You might change the code to add an object to store the data for each time, and therefore, you can have cached times and create a buffer (like the cache for WMS layers. See leaflet.timedimension.layer.wms.js ).
I think you can take a look of the code of this example, which loads a json with tweets location for each time to make a heatmap: http://elpais.com/elpais/2015/09/10/ciencia/1441884739_007223.html
@bielfrontera Have you done similar but to get the Weather API metadata (Open Weather Map API) to be precise when you drag the player button?
I tried to mimic this behavior with some weather data in 24 JSON files. I changed your code in order do link heatmap plugin with data of each JSON, here is the full code of javascript:
Date.prototype.format = function (mask, utc) {
return dateFormat(this, mask, utc);
};
L.TimeDimension.Layer.AjaxGeoJSON = L.TimeDimension.Layer.extend({
initialize: function() {
var heatmapCfg = this._getHeatmapOptions();
console.log(heatmapCfg)
var layer = new HeatmapOverlay(heatmapCfg);
L.TimeDimension.Layer.prototype.initialize.call(this, layer);
this._currentLoadedTime = 0;
this._currentTimeData = null
;
},
_getHeatmapOptions: function() {
var config = {};
var defaultConfig = {
radius: 0.3,
maxOpacity: .8,
scaleRadius: false,
useLocalExtrema: false,
latField: 'lat',
lngField: 'lng',
valueField: 'temp'
};
for (var attrname in defaultConfig) {
config[attrname] = defaultConfig[attrname];
}
return config;
},
onAdd: function(map) {
L.TimeDimension.Layer.prototype.onAdd.call(this, map);
if (this._timeDimension) {
this._getDataForTime(this._timeDimension.getCurrentTime());
}
},
_onNewTimeLoading: function(ev) {
this._getDataForTime(ev.time);
return;
},
isReady: function(time) {
return (this._currentLoadedTime == time);
},
_update: function() {
if (!this._map)
return;
var layer = L.geoJson(this._currentTimeData, this._baseLayer.options);
if (this._currentLayer) {
this._map.removeLayer(this._currentLayer);
}
layer.addTo(this._map);
this._currentLayer = layer;
},
_getDataForTime: function(time) {
if (!this._map) {
return;
}
var d = new Date(time);
url0="https://raw.githubusercontent.com/tekija/LCd/master/"
var url = url0+ d.format("yyyy-mm-dd'T'HH", true) + '.geojson';
var callback = function(status, data) {
console.log(status);
// console.log(data.features.length);
// console.log(data.features[0].geometry.coordinates[1]);
this._currentTimeData = [];
if (status == 'ok'){
for (var i = 0; i < data.features.length; i++) {
// console.log(this._currentTimeData);
this._currentTimeData.push({
lat: data.features[i].geometry.coordinates[1],
lng: data.features[i].geometry.coordinates[0],
temp: data.features[i].properties.t2m
});
}
//console.log(this._currentTimeData);
// this._currentTimeData = data;
} else{
this._currentTimeData = [];
}
this._currentLoadedTime = time;
if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
this._update();
}
this.fire('timeload', {
time: time
});
};
$.getJSON(url, callback.bind(this, 'ok')).fail(callback.bind(this, 'error'));
},
});
L.timeDimension.layer.ajaxGeoJSON = function( ) {
return new L.TimeDimension.Layer.AjaxGeoJSON( );
};
var currentTime = new Date();
currentTime.setUTCDate(1, 0, 0, 0, 0);
var map = L.map('map', {
zoom: 9,
fullscreenControl: true,
timeDimension: true,
timeDimensionOptions: {
timeInterval: "2020-01-01/2020-01-02",
period: "PT1H",
currentTime: currentTime
},
center: [35, 25],
});
var layer = new L.StamenTileLayer("toner-lite");
map.addLayer(layer);
var geojsonLayer = L.timeDimension.layer.ajaxGeoJSON(
{
}
);
geojsonLayer.addTo(map);
L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({
_getDisplayDateFormat: function(date){
return date.format("mm dd hh");
}
});
var timeDimensionControl = new L.Control.TimeDimensionCustom({
playerOptions: {
buffer: 1,
minBufferReady: -1
}
});
map.addControl(this.timeDimensionControl);
This is not working due to this._currentTimeData is set to null. Can you please provide some guidelines for achieving this behaviour.
Hi @tekija,
change this._currentTimeData.data = [];
by this._currentTimeData = [];
and this._currentTimeData.data.push
by this._currentTimeData.push
.
You were trying to add a property (data
) to an object that has been initialised with null (_currentTimeData
).
Hope this helps!
@bielfrontera Great hint. Helped with correcting error. TimeDimension is running fine and loads data in __currentTimeData
in each iteration, but i still cant get heatmap animated.
I guess I am not feeding heatmap layer properly, but still cant see how to fix this.
Edit: It looks like module _update
is not converting layer properly in order to be read with heatmap.
Hi @bielfrontera, you can at the time, load the various steps of a GeoJSON, as with a WMS layer?
I'll explain: I have a GeoJSON that contains a lot of data (tens of MB). You can load a file (eg
index.json
) containing an array of dates (eg["20151202T220000Z","20151202T230000Z"]
), and then load the various GeoJSON necessary (eg20151202T220000Z.geojson
,20151202T230000Z.geojson
, etc.)?