Last active
November 28, 2016 10:24
-
-
Save pelson/eadc049527cc173cc886b7ff11e5edf7 to your computer and use it in GitHub Desktop.
Visualising the last 250 days of mslp using leaflet
This gist exceeds the recommended number of files (~10).
To access all files, please clone this gist.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
L.TimeDimension.Layer.ImageOverlay = L.TimeDimension.Layer.extend({ | |
initialize: function(layer, options) { | |
L.TimeDimension.Layer.prototype.initialize.call(this, layer, options); | |
this._layers = {}; | |
this._defaultTime = 0; | |
this._timeCacheBackward = this.options.cacheBackward || this.options.cache || 0; | |
this._timeCacheForward = this.options.cacheForward || this.options.cache || 0; | |
this._getUrlFunction = this.options.getUrlFunction; | |
this._baseLayer.on('load', (function() { | |
this._baseLayer.setLoaded(true); | |
this.fire('timeload', { | |
time: this._defaultTime | |
}); | |
}).bind(this)); | |
}, | |
eachLayer: function(method, context) { | |
for (var prop in this._layers) { | |
if (this._layers.hasOwnProperty(prop)) { | |
method.call(context, this._layers[prop]); | |
} | |
} | |
return L.TimeDimension.Layer.prototype.eachLayer.call(this, method, context); | |
}, | |
_onNewTimeLoading: function(ev) { | |
var layer = this._getLayerForTime(ev.time); | |
if (!this._map.hasLayer(layer)) { | |
this._map.addLayer(layer); | |
} | |
}, | |
isReady: function(time) { | |
var layer = this._getLayerForTime(time); | |
return layer.isLoaded(); | |
}, | |
_update: function() { | |
if (!this._map) | |
return; | |
var time = this._map.timeDimension.getCurrentTime(); | |
var layer = this._getLayerForTime(time); | |
if (this._currentLayer == null) { | |
this._currentLayer = layer; | |
} | |
if (!this._map.hasLayer(layer)) { | |
this._map.addLayer(layer); | |
} else { | |
this._showLayer(layer, time); | |
} | |
}, | |
_showLayer: function(layer, time) { | |
if (this._currentLayer && this._currentLayer !== layer) { | |
this._currentLayer.hide(); | |
this._map.removeLayer(this._currentLayer); | |
} | |
layer.show(); | |
if (this._currentLayer && this._currentLayer === layer) { | |
return; | |
} | |
this._currentLayer = layer; | |
// Cache management | |
var times = this._getLoadedTimes(); | |
var strTime = String(time); | |
var index = times.indexOf(strTime); | |
var remove = []; | |
// remove times before current time | |
if (this._timeCacheBackward > -1) { | |
var objectsToRemove = index - this._timeCacheBackward; | |
if (objectsToRemove > 0) { | |
remove = times.splice(0, objectsToRemove); | |
this._removeLayers(remove); | |
} | |
} | |
if (this._timeCacheForward > -1) { | |
index = times.indexOf(strTime); | |
var objectsToRemove = times.length - index - this._timeCacheForward - 1; | |
if (objectsToRemove > 0) { | |
remove = times.splice(index + this._timeCacheForward + 1, objectsToRemove); | |
this._removeLayers(remove); | |
} | |
} | |
}, | |
_getLayerForTime: function(time) { | |
if (time == 0 || time == this._defaultTime) { | |
return this._baseLayer; | |
} | |
if (this._layers.hasOwnProperty(time)) { | |
return this._layers[time]; | |
} | |
var url = this._getUrlFunction(this._baseLayer.getURL(), time); | |
imageBounds = this._baseLayer._bounds; | |
var newLayer = L.imageOverlay(url, imageBounds, this._baseLayer.options); | |
this._layers[time] = newLayer; | |
newLayer.on('load', (function(layer, time) { | |
layer.setLoaded(true); | |
if (this._map.timeDimension && time == this._map.timeDimension.getCurrentTime() && !this._map.timeDimension.isLoading()) { | |
this._showLayer(layer, time); | |
} | |
this.fire('timeload', { | |
time: time | |
}); | |
}).bind(this, newLayer, time)); | |
// Hack to hide the layer when added to the map. | |
// It will be shown when timeload event is fired from the map (after all layers are loaded) | |
newLayer.onAdd = (function(map) { | |
Object.getPrototypeOf(this).onAdd.call(this, map); | |
this.hide(); | |
}).bind(newLayer); | |
return newLayer; | |
}, | |
_getLoadedTimes: function() { | |
var result = []; | |
for (var prop in this._layers) { | |
if (this._layers.hasOwnProperty(prop)) { | |
result.push(prop); | |
} | |
} | |
return result.sort(); | |
}, | |
_removeLayers: function(times) { | |
for (var i = 0, l = times.length; i < l; i++) { | |
this._map.removeLayer(this._layers[times[i]]); | |
delete this._layers[times[i]]; | |
} | |
}, | |
}); | |
L.timeDimension.layer.imageOverlay = function(layer, options) { | |
return new L.TimeDimension.Layer.ImageOverlay(layer, options); | |
}; | |
L.ImageOverlay.include({ | |
_visible: true, | |
_loaded: false, | |
_originalUpdate: L.imageOverlay.prototype._update, | |
_update: function() { | |
if (!this._visible && this._loaded) { | |
return; | |
} | |
this._originalUpdate(); | |
}, | |
setLoaded: function(loaded) { | |
this._loaded = loaded; | |
}, | |
isLoaded: function() { | |
return this._loaded; | |
}, | |
hide: function() { | |
this._visible = false; | |
if (this._image && this._image.style) | |
this._image.style.display = 'none'; | |
}, | |
show: function() { | |
this._visible = true; | |
if (this._image && this._image.style) | |
this._image.style.display = 'block'; | |
}, | |
getURL: function() { | |
return this._url; | |
}, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Animated data</title> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.2/dist/leaflet.css" /> | |
<script src="https://unpkg.com/leaflet@1.0.2/dist/leaflet.js"></script> | |
<script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/Leaflet.fullscreen.min.js'></script> | |
<link href='https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/leaflet.fullscreen.css' rel='stylesheet' /> | |
<!-- Get the slider in. TODO: Avoid using rawgit as a CDN. --> | |
<script src='https://code.jquery.com/jquery-3.1.1.min.js'></script> | |
<script src='https://rawgit.com/nezasa/iso8601-js-period/master/iso8601.min.js'></script> | |
<link href='https://rawgit.com/socib/Leaflet.TimeDimension/master/dist/leaflet.timedimension.control.min.css' rel='stylesheet' /> | |
<script src='https://rawgit.com/socib/Leaflet.TimeDimension/master/dist/leaflet.timedimension.min.js'></script> | |
<!-- Animatable image overlay --> | |
<script src='image_overlay.js'></script> | |
<style> | |
body { | |
padding: 0; | |
margin: 0; | |
} | |
html, body { | |
height: 100%; | |
width: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="mapid" style="width: 100%; height: 100%;"></div> | |
<script> | |
var mymap = L.map('mapid', {crs: L.CRS.EPSG4326, fullscreenControl: true, | |
timeDimension: true, | |
timeDimensionOptions: { | |
timeInterval: "2016-03-23/2016-11-24", | |
period: "PT12H" | |
}, | |
timeDimensionControlOptions: { | |
loopButton: true, | |
autoPlay: true, | |
limitSliders: true, | |
timeSliderDragUpdate: true, | |
playerOptions: { | |
loop: true, | |
transitionTime: 333, | |
} | |
}, | |
timeDimensionControl: true, | |
}).setView([0, 180], 1); | |
// Our images are global images, so setup the bounds here. | |
var imageBounds = [[-90, 0], [90, 360]]; | |
// create the title | |
var title = L.control({position: 'topright'}); | |
title.onAdd = function (map) { | |
var div = L.DomUtil.create('div', 'command'); | |
div.innerHTML = '<h1 id="map_title"></h1>'; | |
return div; | |
}; | |
title.addTo(mymap); | |
var mslpLayer = L.imageOverlay('mslp_201603230000.png', imageBounds, { | |
opacity: 1.0, | |
}); | |
var coastlineLayer = L.imageOverlay('coastlines.png', imageBounds, { | |
opacity: 1.0, | |
}); | |
function pad(n, width, z) { | |
z = z || '0'; | |
n = n + ''; | |
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; | |
} | |
var getMSPLUrl = function(baseUrl, time) { | |
var beginUrl = baseUrl.substring(0, baseUrl.lastIndexOf("/")); | |
var dt = new Date(time) | |
var url = (beginUrl + 'mslp_' + | |
+ dt.getUTCFullYear() | |
+ pad((dt.getUTCMonth() + 1), 2) | |
+ pad(dt.getUTCDate(), 2) | |
+ pad(dt.getUTCHours(), 2) | |
+ pad(dt.getUTCMinutes(), 2) + '.png'); | |
return url; | |
}; | |
L.TimeDimension.Layer.BaseImageOverlay = L.TimeDimension.Layer.ImageOverlay.extend({ | |
_onNewTimeLoading: function(ev) { | |
var layer = this._getLayerForTime(ev.time); | |
if (!this._map.hasLayer(layer)) { | |
this._map.addLayer(layer); | |
layer.bringToBack(); | |
} | |
}, | |
}); | |
var mslpLayer_t = new L.TimeDimension.Layer.BaseImageOverlay(mslpLayer, { | |
getUrlFunction: getMSPLUrl, | |
}); | |
mslpLayer_t.addTo(mymap); | |
coastlineLayer.addTo(mymap); | |
// mymap.on('click', function(e) { | |
// console.log(e.latlng.lat, e.latlng.lng) | |
// }); | |
L.TimeDimension.Layer.TrackGeoJson = L.TimeDimension.Layer.GeoJson.extend({ | |
_getFeatureBetweenDates: function(feature, minTime, maxTime) { | |
// An identical copy to the superclass, except that the index (where necessary) always includes the lowest index when slicing. | |
// This gives the effect of the track growing during its validity time, and not being visible when there is no validity time. | |
var featureStringTimes = this._getFeatureTimes(feature); | |
if (featureStringTimes.length == 0) { | |
return feature; | |
} | |
var featureTimes = []; | |
for (var i = 0, l = featureStringTimes.length; i < l; i++) { | |
var time = featureStringTimes[i] | |
if (typeof time == 'string' || time instanceof String) { | |
time = Date.parse(time.trim()); | |
} | |
featureTimes.push(time); | |
} | |
if (featureTimes[0] > maxTime || featureTimes[l - 1] < minTime) { | |
return null; | |
} | |
var index_min = null, | |
index_max = null, | |
l = featureTimes.length; | |
if (featureTimes[l - 1] > minTime) { | |
for (var i = 0; i < l; i++) { | |
if (index_min === null && featureTimes[i] > minTime) { | |
// set index_min the first time that current time is greater the minTime | |
index_min = i; | |
} | |
if (featureTimes[i] > maxTime) { | |
index_max = i; | |
break; | |
} | |
} | |
} | |
if (index_min === null) { | |
index_min = 0; | |
} | |
if (index_max === null) { | |
index_max = l; | |
} | |
var new_coordinates = []; | |
if (feature.geometry.coordinates[0].length) { | |
new_coordinates = feature.geometry.coordinates.slice(index_min, index_max); | |
// This is the new line. | |
new_coordinates = feature.geometry.coordinates.slice(0, index_max); | |
} else { | |
new_coordinates = feature.geometry.coordinates; | |
} | |
return { | |
type: 'Feature', | |
properties: feature.properties, | |
geometry: { | |
type: feature.geometry.type, | |
coordinates: new_coordinates | |
} | |
}; | |
}, | |
}); | |
var myStyle = { | |
"color": "blue", | |
"weight": 5, | |
"opacity": 0.65 | |
}; | |
var myLayer = L.geoJSON([], myStyle); | |
var td_myLayer = new L.TimeDimension.Layer.TrackGeoJson(myLayer, | |
{ | |
// Duration: The amount of time that a feature should remain after it is out of time. | |
duration: 'PT0M', | |
addLastPoint: true, | |
}); | |
td_myLayer.addTo(mymap); | |
function addDataToMap(data) { | |
myLayer.addData(data); | |
}; | |
$.getJSON("tracks.json", addDataToMap); | |
mymap.timeDimension.on('timeload', function(data) { | |
var date = new Date(mymap.timeDimension.getCurrentTime()); | |
$('#map_title').text(date.toISOString()); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment