├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── dist ├── leaflet.timedimension.control.css ├── leaflet.timedimension.control.min.css ├── leaflet.timedimension.min.js ├── leaflet.timedimension.src.js └── leaflet.timedimension.src.withlog.js ├── examples ├── css │ └── style.css ├── data │ ├── easy_currents_track.kml │ ├── running_mallorca.gpx │ ├── spill.json │ └── track_bus699.geojson ├── example1.html ├── example10.html ├── example11.html ├── example12.html ├── example13.html ├── example14.html ├── example15.html ├── example16.html ├── example17.html ├── example18.html ├── example2.html ├── example3.html ├── example4.html ├── example5.html ├── example6.html ├── example7.html ├── example8.html ├── example9.html ├── img │ ├── .directory │ ├── GitHub-Mark-32px.png │ ├── black-arrow.png │ ├── bus.png │ ├── grey-arrow.png │ ├── logo-leaflettimedimension-32.png │ ├── logo-leaflettimedimension.png │ ├── logo-socib.png │ ├── running.png │ ├── screenshot │ │ ├── .directory │ │ ├── 1-aviso.png │ │ ├── 10-imageoverlay-customcontrol.png │ │ ├── 11-noaa-cdr-sst.png │ │ ├── 12-nyc-opendata.png │ │ ├── 13-wms-timeseries.png │ │ ├── 14-noaa-nowcast.png │ │ ├── 15-oil-spill.png │ │ ├── 16-portus-tilelayers.png │ │ ├── 17-basic-geojson.png │ │ ├── 18-velocity-integration.png │ │ ├── 2-temperature-forecast.png │ │ ├── 3-hfradar.png │ │ ├── 4-rain-radar.png │ │ ├── 5-climate-projections.png │ │ ├── 6-sapoib.png │ │ ├── 7-ocean-forecast.png │ │ ├── 8-hfradar-with-drifters.png │ │ ├── 9-gxp-kml-tracks.png │ │ └── screenshot-leaflet-timedimension.png │ └── surface-drifter.png ├── index.html ├── js │ ├── baselayers.js │ ├── example1.js │ ├── example10-data.json │ ├── example10.js │ ├── example11.js │ ├── example12.js │ ├── example13.js │ ├── example14.js │ ├── example15.js │ ├── example16.js │ ├── example17.js │ ├── example18.js │ ├── example2.js │ ├── example3.js │ ├── example4.js │ ├── example5.js │ ├── example6.js │ ├── example7.js │ ├── example8.js │ ├── example9.js │ └── extras │ │ ├── leaflet.timedimension.circlelabelmarker.js │ │ ├── leaflet.timedimension.layer.wms.timeseries.js │ │ ├── leaflet.timedimension.tilelayer.portus.js │ │ └── leaflet.timedimension.velocitylayer.js └── server │ ├── proxy-datadiscovery.php │ └── proxy.php ├── package.json └── src ├── leaflet.timedimension.control.css ├── leaflet.timedimension.control.js ├── leaflet.timedimension.js ├── leaflet.timedimension.layer.geojson.js ├── leaflet.timedimension.layer.js ├── leaflet.timedimension.layer.wms.js ├── leaflet.timedimension.player.js └── leaflet.timedimension.util.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | 5 | grunt.loadNpmTasks('grunt-contrib-uglify'); 6 | grunt.loadNpmTasks('grunt-contrib-concat'); 7 | grunt.loadNpmTasks('grunt-contrib-clean'); 8 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 9 | grunt.loadNpmTasks('grunt-contrib-jshint'); 10 | grunt.loadNpmTasks('grunt-contrib-watch'); 11 | grunt.loadNpmTasks("grunt-remove-logging"); 12 | 13 | grunt.initConfig({ 14 | pkg: grunt.file.readJSON('package.json'), 15 | umd: { 16 | prefix: '(function (factory, window) {\n' + 17 | ' if (typeof define === \'function\' && define.amd) {\n' + 18 | ' // define an AMD module that relies on leaflet\n' + 19 | ' define([\'leaflet\', \'iso8601-js-period\'], factory);\n' + 20 | ' } else if (typeof exports === \'object\') {\n' + 21 | ' // define a Common JS module that relies on leaflet\n' + 22 | ' module.exports = factory(require(\'leaflet\'), require(\'iso8601-js-period\'));\n' + 23 | ' } else if (typeof window !== \'undefined\' && window.L && typeof L !== \'undefined\') {\n' + 24 | ' // get the iso8601 from the expected to be global nezasa scope\n' + 25 | ' var iso8601 = nezasa.iso8601;\n' + 26 | ' // attach your plugin to the global L variable\n' + 27 | ' window.L.TimeDimension = factory(L, iso8601);\n' + 28 | ' }\n' + 29 | ' }(function (L, iso8601) {\n'+ 30 | ' // make sure iso8601 module js period module is available under the nezasa scope\n'+ 31 | ' if (typeof nezasa === \'undefined\') {\n'+ 32 | ' var nezasa = { iso8601: iso8601 };\n'+ 33 | ' }\n'+ 34 | ' // TimeDimension plugin implementation\n', 35 | postfix: ' \n'+ 36 | ' return L.TimeDimension;\n'+ 37 | ' }, window)\n'+ 38 | ');' 39 | }, 40 | meta: { 41 | banner: '/* \n' + 42 | ' * Leaflet TimeDimension v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> \n' + 43 | ' * \n' + 44 | ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> \n' + 45 | ' * <%= pkg.author.email %> \n' + 46 | ' * <%= pkg.author.url %> \n' + 47 | ' * \n' + 48 | ' * Licensed under the <%= pkg.license %> license. \n' + 49 | ' * \n' + 50 | ' * Demos: \n' + 51 | ' * <%= pkg.homepage %> \n' + 52 | ' * \n' + 53 | ' * Source: \n' + 54 | ' * <%= pkg.repository.url %> \n' + 55 | ' * \n' + 56 | ' */\n' 57 | }, 58 | clean: { 59 | dist: { 60 | src: ['dist/*'] 61 | } 62 | }, 63 | jshint: { 64 | options: { 65 | globals: { 66 | console: true, 67 | module: true 68 | }, 69 | "-W099": true, //ignora tabs e space warning 70 | "-W033": true, 71 | "-W041": true, 72 | "-W004": true, 73 | "-W044": true //ignore regexp 74 | }, 75 | files: ['src/*.js'] 76 | }, 77 | concat: { 78 | js: { 79 | options: { 80 | banner: '<%= meta.banner %>\n'+ 81 | '<%= umd.prefix %>', 82 | footer: '<%= umd.postfix %>' 83 | }, 84 | src: [ 85 | 'src/leaflet.timedimension.js', 86 | 'src/leaflet.timedimension.util.js', 87 | 'src/leaflet.timedimension.layer.js', 88 | 'src/leaflet.timedimension.layer.wms.js', 89 | 'src/leaflet.timedimension.layer.geojson.js', 90 | 'src/leaflet.timedimension.player.js', 91 | 'src/leaflet.timedimension.control.js' 92 | ], 93 | dest: 'dist/leaflet.timedimension.src.withlog.js' 94 | }, 95 | css: { 96 | options: { 97 | banner: '<%= meta.banner %>' 98 | }, 99 | src: 'src/leaflet.timedimension.control.css', 100 | dest: 'dist/leaflet.timedimension.control.css' 101 | }, 102 | }, 103 | removelogging: { 104 | dist: { 105 | src: "dist/leaflet.timedimension.src.withlog.js", 106 | dest: "dist/leaflet.timedimension.src.js" 107 | } 108 | }, 109 | uglify: { 110 | options: { 111 | banner: '<%= meta.banner %>' 112 | }, 113 | dist: { 114 | files: { 115 | 'dist/leaflet.timedimension.min.js': ['dist/leaflet.timedimension.src.js'] 116 | } 117 | } 118 | }, 119 | cssmin: { 120 | combine: { 121 | files: { 122 | 'dist/leaflet.timedimension.control.min.css': ['dist/leaflet.timedimension.control.css'] 123 | } 124 | }, 125 | options: { 126 | banner: '<%= meta.banner %>' 127 | }, 128 | minify: { 129 | expand: true, 130 | cwd: 'dist/', 131 | files: { 132 | 'dist/leaflet.timedimension.control.min.css': ['dist/leaflet.timedimension.control.css'] 133 | } 134 | } 135 | }, 136 | watch: { 137 | dist: { 138 | options: { 139 | livereload: true 140 | }, 141 | files: ['src/*', 'examples/*'], 142 | tasks: ['clean', 'concat', 'removelogging', 'cssmin', 'jshint'] 143 | } 144 | } 145 | }); 146 | 147 | grunt.registerTask('default', [ 148 | 'clean', 149 | 'concat', 150 | 'removelogging', 151 | 'cssmin', 152 | 'jshint', 153 | 'uglify' 154 | ]); 155 | 156 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 ICTS SOCIB - Servei d'observació i predicció costaner de les Illes Balears. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet-timedimension", 3 | "description": "Add time dimension capabilities on a Leaflet map", 4 | "main": [ 5 | "dist/leaflet.timedimension.src.js", 6 | "dist/leaflet.timedimension.control.css" 7 | ], 8 | "license": "MIT", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "test", 14 | "tests", 15 | "examples", 16 | "Gruntfile.js", 17 | "package.json" 18 | ], 19 | "keywords": [ 20 | "gis", 21 | "map", 22 | "timedimension", 23 | "leaflet" 24 | ], 25 | "authors": [ 26 | { 27 | "name": "Biel Frontera (ICTS SOCIB)", 28 | "email": "datacenter@socib.es", 29 | "homepage": "https://www.socib.es/" 30 | }, 31 | { 32 | "name": "Sylvain Marcadal (Ram)", 33 | "email":"sylvain@marcadal.me" 34 | } 35 | ], 36 | "homepage": "https://apps.socib.es/Leaflet.TimeDimension/", 37 | "repository": { 38 | "type": "git", 39 | "url": "git://github.com/socib/Leaflet.TimeDimension.git" 40 | }, 41 | "dependencies": { 42 | "iso8601-js-period": "*", 43 | "leaflet": "~0.7.4 || ~1.0.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /dist/leaflet.timedimension.control.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:'Glyphicons Halflings';src:url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.eot);src:url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.woff) format('woff'),url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular) format('svg')}.leaflet-bar-timecontrol{background-color:#fff;color:#000}.leaflet-bar-timecontrol *{box-sizing:border-box}.leaflet-bar-timecontrol .leaflet-control-timecontrol{float:left;height:26px;line-height:26px;border:solid #a5a5a5;background-color:#fff;border-width:0 1px 0 0}.leaflet-bar-timecontrol .leaflet-control-timecontrol:first-child{border-radius:4px 0 0 4px}.leaflet-bar-timecontrol .leaflet-control-timecontrol:last-child{border-radius:0 4px 4px 0}.leaflet-bar-timecontrol .leaflet-control-timecontrol:before{font-family:"Glyphicons Halflings";display:block}.leaflet-bar-timecontrol .timecontrol-slider{position:relative;width:auto;cursor:auto}.leaflet-bar-timecontrol a.timecontrol-date,.leaflet-bar-timecontrol a.timecontrol-date:hover{position:relative;min-width:150px;width:auto;padding:0 10px 0 20px;white-space:nowrap}.leaflet-bar-timecontrol a.timecontrol-date.utc,.leaflet-bar-timecontrol a.timecontrol-date.utc:hover{min-width:185px}.leaflet-bar-timecontrol a.timecontrol-date.loading,.leaflet-bar-timecontrol a.timecontrol-date.loading:hover{background-color:#ffefa4}.leaflet-bar-timecontrol .timecontrol-dateslider .slider{width:200px}.leaflet-bar-timecontrol .timecontrol-speed{white-space:nowrap;cursor:auto}.leaflet-bar-timecontrol .timecontrol-speed .slider{width:55px;display:inline-block}.leaflet-bar-timecontrol .timecontrol-speed .speed{width:55px;display:inline-block;float:left;text-align:right}.leaflet-bar-timecontrol .timecontrol-play,.leaflet-bar-timecontrol .timecontrol-play:hover{position:relative}.leaflet-bar-timecontrol .timecontrol-play span{font-size:10px}.leaflet-bar-timecontrol a.timecontrol-play.loading{background-color:#ffefa4}.timecontrol-slider .slider{position:relative;height:12px;margin:6px;border:1px solid #a5a5a5;cursor:pointer}.timecontrol-slider .slider.has-limits{margin-left:15px;margin-right:15px;background-color:#ddd}.timecontrol-slider .slider.has-limits .range{position:absolute;height:10px;background-color:#fff}.timecontrol-slider .knob{position:absolute;width:8px;height:22px;background-color:#ddd;border-radius:2px;border:1px solid #a5a5a5;margin-top:-6px;margin-left:-4px;cursor:ew-resize;cursor:-webkit-grab;cursor:-moz-grab}.timecontrol-slider .knob:after{content:' ';display:block;position:absolute;width:20px;top:-5px;height:32px;left:-7px}.timecontrol-slider .knob.lower,.timecontrol-slider .knob.upper{width:11px;height:20px;border:none;background-color:transparent}.timecontrol-slider .knob.upper{margin-top:-5px;margin-left:-1px}.timecontrol-slider .knob.lower{margin-top:-5px;margin-left:-10px}.timecontrol-slider .knob.lower:after{right:0;left:initial}.timecontrol-slider .knob.upper:after{left:0}.timecontrol-slider .knob.lower:before,.timecontrol-slider .knob.upper:before{display:block;content:'';position:relative;top:2px;width:0;height:0;border-style:solid}.timecontrol-slider .knob.upper:before{border-width:16px 0 0 10px;border-color:transparent transparent transparent #a5a5a5}.timecontrol-slider .knob.lower:before{border-width:0 0 16px 10px;border-color:transparent transparent #a5a5a5}.timecontrol-slider .dragging .knob,.timecontrol-slider .knob.leaflet-drag-target,.timecontrol-slider .slider.dragging{cursor:ew-resize;cursor:grabbing;cursor:-webkit-grabbing;cursor:-moz-grabbing}@-webkit-keyframes icon-rotation{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes icon-rotation{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.timecontrol-loop.looped,.timecontrol-loop.looped:hover{background-color:#ddd;color:#094f8e}.timecontrol-backward:before,.timecontrol-forward:before,.timecontrol-loop:before,.timecontrol-play:before,.timecontrol-stop:before{width:100%;text-align:center}.timecontrol-play:before{position:absolute;content:"\e072"}.timecontrol-play.reverse:before{content:"\e072";-ms-transform:scaleX(-1);-webkit-transform:scaleX(-1);transform:scaleX(-1)}.timecontrol-play.pause:before{content:"\e073"}.timecontrol-play.reverse.pause:before{-ms-transform:none;-webkit-transform:none;transform:none}a.timecontrol-play.loading:before{content:"\e031";opacity:.2;-webkit-animation:icon-rotation 6s infinite linear;animation:icon-rotation 6s infinite linear}.timecontrol-date.loading:before{content:"\e031";left:5px;position:absolute;-webkit-animation:icon-rotation 6s infinite linear;animation:icon-rotation 6s infinite linear}.timecontrol-speed:before{content:"\e141";position:absolute;left:7px}.timecontrol-stop:before{content:"\e074"}.timecontrol-forward:before{content:"\e075"}.timecontrol-backward:before{content:"\e071"}.timecontrol-loop:before{content:"\e030"}@media (max-width:767px){.leaflet-bar-timecontrol .timecontrol-date,.leaflet-bar-timecontrol .timecontrol-slider{clear:both;float:none;border-right:none}}.leaflet-touch .leaflet-bar-timecontrol .leaflet-control-timecontrol{height:30px;line-height:30px}.leaflet-touch .timecontrol-slider .slider{margin-top:10px} -------------------------------------------------------------------------------- /examples/data/track_bus699.geojson: -------------------------------------------------------------------------------- 1 | {"type": "Feature", "properties": {"name": "Track of bus 699", "times": ["2019-11-23 10:51:06", "2019-11-23 10:52:05", "2019-11-23 10:53:05", "2019-11-23 10:54:04", "2019-11-23 10:55:05", "2019-11-23 10:56:05", "2019-11-23 10:57:05", "2019-11-23 10:58:05", "2019-11-23 10:59:05", "2019-11-23 11:00:06", "2019-11-23 11:01:06", "2019-11-23 11:02:06", "2019-11-23 11:03:05", "2019-11-23 11:04:04", "2019-11-23 11:05:06", "2019-11-23 11:06:05", "2019-11-23 11:07:05", "2019-11-23 11:08:05", "2019-11-23 11:09:05", "2019-11-23 11:10:06", "2019-11-23 11:11:05", "2019-11-23 11:12:05", "2019-11-23 11:13:05", "2019-11-23 11:14:06", "2019-11-23 11:15:05", "2019-11-23 11:16:05", "2019-11-23 11:17:05", "2019-11-23 11:18:05", "2019-11-23 11:19:05", "2019-11-23 11:20:06", "2019-11-23 11:21:05", "2019-11-23 11:22:05", "2019-11-23 11:23:05", "2019-11-23 11:24:06", "2019-11-23 11:25:06", "2019-11-23 11:26:05", "2019-11-23 11:27:05", "2019-11-23 11:28:04", "2019-11-23 11:29:06"]}, "geometry": {"type": "LineString", "coordinates": [[-4.4214296, 36.73835], [-4.422104, 36.737865], [-4.4229302, 36.73773], [-4.4235334, 36.735817], [-4.4222927, 36.73413], [-4.4218254, 36.732475], [-4.4213734, 36.72983], [-4.420156, 36.73], [-4.419239, 36.730686], [-4.417272, 36.732136], [-4.4155564, 36.732613], [-4.4155564, 36.732613], [-4.4147606, 36.729523], [-4.4143534, 36.728085], [-4.414023, 36.727142], [-4.414023, 36.727142], [-4.4145956, 36.726017], [-4.4163203, 36.722366], [-4.4163203, 36.722366], [-4.4142747, 36.72012], [-4.4162464, 36.71957], [-4.418931, 36.71882], [-4.421059, 36.718254], [-4.421595, 36.718174], [-4.424712, 36.717197], [-4.4268923, 36.717003], [-4.427205, 36.717583], [-4.426953, 36.717876], [-4.4264026, 36.715973], [-4.4267263, 36.71531], [-4.4270782, 36.714962], [-4.4300385, 36.71217], [-4.4314194, 36.71117], [-4.4344425, 36.70879], [-4.437068, 36.706684], [-4.4393854, 36.70489], [-4.440271, 36.704346], [-4.4454684, 36.702717], [-4.4454684, 36.702717]]}} -------------------------------------------------------------------------------- /examples/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 | 22 |

Code

23 |
24 |
25 | Index 26 |
27 |
28 | Example 2: Temperature from IBL Software Engineering 29 |
30 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/example10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 |
22 |
23 | Beach Monitoring at Cala Millor 24 |   25 |   26 |
27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 | 37 |

Description

38 |

This is a proof-of-concept. We have implemented a new L.TimeDimension.Layer class to manage ImageOverlays and a basic custom control to manage the TimeDimension.

39 |

The chart shows the daily wind average observed at the weather station installed in Cala Millor by SOCIB. If you click in any point of the chart, it will show the image of the selected date at 12:00h.

40 |

Code

41 |
42 | 43 |
44 | Example 9: GeoJSON layers with times property 45 |
46 |
47 | Example 11: NOAA Operational Climate Data Records (SST) 48 |
49 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /examples/example11.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 |
23 | 28 |
29 | 30 | 31 |

Code

32 |
33 | 34 |
35 | Example 10: ImageOverlay and custom control 36 |
37 |
38 | Example 12: Noise-Commercial complaints from NYC OpenData 39 |
40 | 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /examples/example12.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 37 | 38 | 39 |
40 | 44 |
45 | 46 | 47 |

Code

48 |
49 | 50 |
51 | Example 11: NOAA Operational Climate Data Records (SST) 52 |
53 |
54 | Example 13: Demo of L.TimeDimension.WMS.timeseries Layer 55 |
56 | 60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /examples/example13.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 |
22 | 23 |

Information

24 |
25 | This example use two extra Leaflet.TimeDimension classes: 26 | 30 | 31 |

These two classes are specific for WMS layers provided by a THREDDS Data Server, as they use 33 | getFeatureInfo method to get an XML with the values of the layer along time (see ncWMS 35 | documentation). We 36 | recommend to use at least version 4.6.1 of TDS, which includes a significant improvement in the response 37 | time of GetFeatureInfo requests (see the issue at 38 | github).

39 |

By default, only 7 days of data are requested for each marker. When visible time range in the chart is changed, additional data is requested to the THREDDS Server.

40 |

The vertical red line represents current selected time on the map. You can change the current time by clicking on the chart (so, the chart can be used as time control as well).

41 |

You can add more timeseries to the chart double clicking on any point on the map.

42 |
43 | 44 | 45 |

Code

46 |
47 | 48 |
49 | Example 12: Noise-Commercial complaints from NYC OpenData 50 |
51 |
52 | Example 14: NOAA's nowCOAST weather radar 53 |
54 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /examples/example14.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 |
22 | 23 |

Information

24 |
25 |

Example using NOAA's nowCOAST radar layer, used by @eflowbeach in his project MobileWeather. 28 |

29 |
30 | 31 | 32 |

Code

33 |
34 | 35 |
36 | Example 13: Demo of L.TimeDimension.WMS.timeseries Layer 37 |
38 |
39 | Example 15: Oil Spill simulation 40 |
41 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/example15.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 33 | 34 | 35 |
36 | 40 |
41 |
42 |
43 |

Information

44 |
45 |

Simulation of the trajectory of a hypothetical oil spill. The contours correspond to the cumulative probability of all particles (50% green, yellow 75% and red 90%).

46 |

This simulation was made with a tool developed by SOCIB and IMEDEA.

47 |
48 | 49 |

Code

50 |
51 | 52 |
53 | Example 14: NOAA's nowCOAST weather radar 54 |
55 |
56 | Example 16: Portus wave forecast 57 |
58 | 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /examples/example16.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Leaflet TimeDimension 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 19 |
20 | 21 |

Information

22 |
23 |

This example use an extra Leaflet.TimeDimension class: L.TimeDimension.Layer.TileLayer.Portus.

24 | 25 |

Portus wave forecast is served as different TileLayers with datetime as a part of the URL (in the format yyyymmddhh). The class is constructing a new TileLayer taking the URL of the base layer as a template (note the {d} and {t} placeholder).

26 |
27 | 28 |

Code

29 |
30 |

Code of L.TimeDimension.Layer.TileLayer.Portus

31 |
32 | 33 |
34 | Example 15: Oil Spill simulation 35 |
36 |
37 | Example 17: Basic GeoJSON usage 38 |
39 | 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /examples/example17.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Leaflet TimeDimension 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 |
20 | 24 |
25 | 26 | 27 |

Code

28 |
29 | 30 |
31 | Example 16: Portus Wave forecast 32 |
33 |
34 | Example 18: Integration with leaflet-velocity 35 |
36 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/example18.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Leaflet TimeDimension 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 |
21 | 25 |
26 |

Information

27 |
28 |

leaflet-velocity is a plugin for Leaflet that creates a 29 | canvas visualisation layer for direction and intensity of arbitrary velocities. 30 | In this example, we are using SOCIB Data API to get 31 | the values of sea water velocity measured by a High Frequency Radar (HFR) to feed a 32 | leaflet-velocity layer that is updated on every time change. 33 |

34 |

This example use an extra Leaflet.TimeDimension class: L.TimeDimension.Layer.VelocityLayer.

36 |
37 | 38 |

Code

39 |
40 | 41 |
42 | Example 17: Basic GeoJSON usage 43 |
44 |
45 | Index 46 |
47 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /examples/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 |

Code

22 |
23 |
24 | Example 1: Daily altimetry from satellite 25 |
26 |
27 | Example 3: SOCIB HF Radar 28 |
29 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 21 |
22 |

23 |

Select start time and end time of the animation loop

24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |

32 | 33 |
34 |

35 |
36 | 37 |

Code

38 |
39 |
40 | Example 2: Temperature from IBL Software Engineering 41 |
42 |
43 | Example 4: Radar precipitation measurements 44 |
45 | 49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/example4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 | 22 | 23 |

Code

24 |
25 | 26 |
27 | Example 3: SOCIB HF Radar 28 |
29 |
30 | Example 5: Climate projections 31 |
32 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /examples/example5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 |

Disclaimer

22 |

This example is not working because the dataset of this climate projections is no longer online. It will be replaced soon. If you know a similar dataset, please open an issue or send a Pull Request.

23 | 24 |

Code

25 |
26 | 27 |
28 | Example 4: Radar precipitation measurements 29 |
30 |
31 | Example 6: SOCIB Wave Forecast (SAPO IB) 32 |
33 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/example6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 | 22 | 23 |

Code

24 |
25 | 26 |
27 | Example 5: Climate projections 28 |
29 |
30 | Example 7: SOCIB WMOP Ocean Forecast 31 |
32 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/example7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 | 22 | 23 |

Code

24 |
25 | 26 |
27 | Example 6: SOCIB Wave Forecast (SAPO IB) 28 |
29 |
30 | Example 8: SOCIB HF Radar with surface drifters trajectories 31 |
32 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/example8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 | 22 | 23 |

Code

24 |
25 | 26 |
27 | Example 7: SOCIB WMOP Ocean Forecast 28 |
29 |
30 | Example 9: GeoJSON layers with times property 31 |
32 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /examples/example9.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 20 |
21 | 22 | 23 |

Code

24 |
25 |
26 | Running icon made by Freepik from www.flaticon.com is licensed under CC BY 3.0 27 |
28 | 29 |
30 | Example 8: SOCIB HF Radar with surface drifters trajectories 31 |
32 |
33 | Example 10: ImageOverlay and custom control 34 |
35 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/img/.directory: -------------------------------------------------------------------------------- 1 | [Dolphin] 2 | PreviewsShown=true 3 | Timestamp=2014,12,18,17,21,52 4 | Version=3 5 | -------------------------------------------------------------------------------- /examples/img/GitHub-Mark-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/GitHub-Mark-32px.png -------------------------------------------------------------------------------- /examples/img/black-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/black-arrow.png -------------------------------------------------------------------------------- /examples/img/bus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/bus.png -------------------------------------------------------------------------------- /examples/img/grey-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/grey-arrow.png -------------------------------------------------------------------------------- /examples/img/logo-leaflettimedimension-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/logo-leaflettimedimension-32.png -------------------------------------------------------------------------------- /examples/img/logo-leaflettimedimension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/logo-leaflettimedimension.png -------------------------------------------------------------------------------- /examples/img/logo-socib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/logo-socib.png -------------------------------------------------------------------------------- /examples/img/running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/running.png -------------------------------------------------------------------------------- /examples/img/screenshot/.directory: -------------------------------------------------------------------------------- 1 | [Dolphin] 2 | PreviewsShown=true 3 | Timestamp=2014,12,18,17,49,4 4 | Version=3 5 | -------------------------------------------------------------------------------- /examples/img/screenshot/1-aviso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/1-aviso.png -------------------------------------------------------------------------------- /examples/img/screenshot/10-imageoverlay-customcontrol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/10-imageoverlay-customcontrol.png -------------------------------------------------------------------------------- /examples/img/screenshot/11-noaa-cdr-sst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/11-noaa-cdr-sst.png -------------------------------------------------------------------------------- /examples/img/screenshot/12-nyc-opendata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/12-nyc-opendata.png -------------------------------------------------------------------------------- /examples/img/screenshot/13-wms-timeseries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/13-wms-timeseries.png -------------------------------------------------------------------------------- /examples/img/screenshot/14-noaa-nowcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/14-noaa-nowcast.png -------------------------------------------------------------------------------- /examples/img/screenshot/15-oil-spill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/15-oil-spill.png -------------------------------------------------------------------------------- /examples/img/screenshot/16-portus-tilelayers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/16-portus-tilelayers.png -------------------------------------------------------------------------------- /examples/img/screenshot/17-basic-geojson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/17-basic-geojson.png -------------------------------------------------------------------------------- /examples/img/screenshot/18-velocity-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/18-velocity-integration.png -------------------------------------------------------------------------------- /examples/img/screenshot/2-temperature-forecast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/2-temperature-forecast.png -------------------------------------------------------------------------------- /examples/img/screenshot/3-hfradar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/3-hfradar.png -------------------------------------------------------------------------------- /examples/img/screenshot/4-rain-radar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/4-rain-radar.png -------------------------------------------------------------------------------- /examples/img/screenshot/5-climate-projections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/5-climate-projections.png -------------------------------------------------------------------------------- /examples/img/screenshot/6-sapoib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/6-sapoib.png -------------------------------------------------------------------------------- /examples/img/screenshot/7-ocean-forecast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/7-ocean-forecast.png -------------------------------------------------------------------------------- /examples/img/screenshot/8-hfradar-with-drifters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/8-hfradar-with-drifters.png -------------------------------------------------------------------------------- /examples/img/screenshot/9-gxp-kml-tracks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/9-gxp-kml-tracks.png -------------------------------------------------------------------------------- /examples/img/screenshot/screenshot-leaflet-timedimension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/screenshot/screenshot-leaflet-timedimension.png -------------------------------------------------------------------------------- /examples/img/surface-drifter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socib/Leaflet.TimeDimension/fa36ffee7f6390aadce8c055ca7c60ede7c2134c/examples/img/surface-drifter.png -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Leaflet TimeDimension 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 16 | 36 | 40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/js/baselayers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Return common layers used in different examples 3 | */ 4 | function getCommonBaseLayers(map){ 5 | var osmLayer = L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { 6 | attribution: '© OpenStreetMap contributors' 7 | }); 8 | var bathymetryLayer = L.tileLayer.wms("https://ows.emodnet-bathymetry.eu/wms", { 9 | layers: 'emodnet:mean_atlas_land', 10 | format: 'image/png', 11 | transparent: true, 12 | attribution: "EMODnet Bathymetry", 13 | opacity: 0.8 14 | }); 15 | var coastlinesLayer = L.tileLayer.wms("https://ows.emodnet-bathymetry.eu/wms", { 16 | layers: 'coastlines', 17 | format: 'image/png', 18 | transparent: true, 19 | attribution: "EMODnet Bathymetry", 20 | opacity: 0.8 21 | }); 22 | var bathymetryGroupLayer = L.layerGroup([bathymetryLayer, coastlinesLayer]); 23 | bathymetryGroupLayer.addTo(map); 24 | return { 25 | "EMODnet Bathymetry": bathymetryGroupLayer, 26 | "OSM": osmLayer 27 | }; 28 | } -------------------------------------------------------------------------------- /examples/js/example1.js: -------------------------------------------------------------------------------- 1 | var map = L.map('map', { 2 | zoom: 5, 3 | fullscreenControl: true, 4 | timeDimension: true, 5 | timeDimensionControl: true, 6 | center: [38.0, 15.0] 7 | }); 8 | 9 | var avisoWMS = "https://thredds.socib.es/thredds/wms/observational/satellite/altimetry/aviso/madt/sealevel_med_phy_nrt_L4_agg/sealevel_med_phy_nrt_L4_agg_best.ncd"; 10 | 11 | var heigthLayer = L.tileLayer.wms(avisoWMS, { 12 | layers: 'adt', 13 | format: 'image/png', 14 | transparent: true, 15 | colorscalerange: '-0.4,0.4', 16 | abovemaxcolor: "extend", 17 | belowmincolor: "extend", 18 | numcolorbands: 100, 19 | styles: 'boxfill/rainbow' 20 | }); 21 | var heigthContourLayer = L.tileLayer.wms(avisoWMS, { 22 | layers: 'adt', 23 | format: 'image/png', 24 | transparent: true, 25 | colorscalerange: '-0.5,0.5', 26 | numcontours: 11, 27 | styles: 'contour/rainbow' 28 | }); 29 | 30 | var velocityLayer = L.nonTiledLayer.wms(avisoWMS, { 31 | layers: 'surface_geostrophic_sea_water_velocity', 32 | format: 'image/png', 33 | transparent: true, 34 | colorscalerange: '-20,100', 35 | markerscale: 10, 36 | markerspacing: 8, 37 | abovemaxcolor: "extend", 38 | belowmincolor: "extend", 39 | numcolorbands: 100, 40 | styles: 'prettyvec/greyscale' 41 | }); 42 | 43 | var proxy = 'server/proxy.php'; 44 | var heightTimeLayer = L.timeDimension.layer.wms(heigthLayer, { 45 | proxy: proxy, 46 | updateTimeDimension: true, 47 | }); 48 | var heightContourTimeLayer = L.timeDimension.layer.wms(heigthContourLayer, { 49 | proxy: proxy, 50 | updateTimeDimension: false, 51 | }); 52 | var velocityTimeLayer = L.timeDimension.layer.wms(velocityLayer, { 53 | proxy: proxy, 54 | updateTimeDimension: false, 55 | }); 56 | 57 | var overlayMaps = { 58 | "AVISO - Sea surface height above geoid": heightTimeLayer, 59 | "AVISO - Sea surface height above geoid (Contour)": heightContourTimeLayer, 60 | "AVISO - Surface geostrophic sea water velocity": velocityTimeLayer 61 | }; 62 | 63 | // Legends 64 | var heigthLegend = L.control({ 65 | position: 'bottomright' 66 | }); 67 | heigthLegend.onAdd = function(map) { 68 | var src = avisoWMS + "?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&LAYER=adt&colorscalerange=-0.4,0.4&PALETTE=rainbow&transparent=TRUE"; 69 | var div = L.DomUtil.create('div', 'info legend'); 70 | div.innerHTML += 71 | 'legend'; 72 | return div; 73 | }; 74 | 75 | var velocityLegend = L.control({ 76 | position: 'bottomright' 77 | }); 78 | velocityLegend.onAdd = function(map) { 79 | var div = L.DomUtil.create('div', 'info legend'); 80 | div.innerHTML += ' Surface geostrophic
sea water velocity'; 81 | return div; 82 | }; 83 | 84 | 85 | map.on('overlayadd', function(eventLayer) { 86 | if (eventLayer.name == 'AVISO - Sea surface height above geoid') { 87 | heigthLegend.addTo(this); 88 | } else if (eventLayer.name == 'AVISO - Surface geostrophic sea water velocity') { 89 | velocityLegend.addTo(this); 90 | } 91 | }); 92 | 93 | map.on('overlayremove', function(eventLayer) { 94 | if (eventLayer.name == 'AVISO - Sea surface height above geoid') { 95 | map.removeControl(heigthLegend); 96 | } else if (eventLayer.name == 'AVISO - Surface geostrophic sea water velocity') { 97 | map.removeControl(velocityLegend); 98 | } 99 | }); 100 | 101 | var baseLayers = getCommonBaseLayers(map); // see baselayers.js 102 | L.control.layers(baseLayers, overlayMaps).addTo(map); 103 | 104 | heightTimeLayer.addTo(map); 105 | heightContourTimeLayer.addTo(map); 106 | velocityTimeLayer.addTo(map); -------------------------------------------------------------------------------- /examples/js/example11.js: -------------------------------------------------------------------------------- 1 | Date.prototype.format = function (mask, utc) { 2 | return dateFormat(this, mask, utc); 3 | }; 4 | 5 | var currentMonth = new Date(); 6 | currentMonth.setUTCDate(1); 7 | currentMonth.setUTCHours(12, 0, 0, 0); 8 | 9 | var map = L.map('map', { 10 | zoom: 2, 11 | fullscreenControl: true, 12 | timeDimension: true, 13 | timeDimensionOptions:{ 14 | timeInterval: "P6M/" + currentMonth.format("yyyy-mm-dd\'T\'HH:MM:ss"), 15 | period: "P1M", 16 | currentTime: currentMonth 17 | }, 18 | center: [20.0, 0.0], 19 | }); 20 | 21 | L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { 22 | attribution: '© OpenStreetMap contributors' 23 | }).addTo(map); 24 | 25 | var proxy = 'server/proxy.php'; 26 | var testWMS = "https://www.ncei.noaa.gov/thredds/wms/ncFC/fc-oisst-daily-avhrr-only-dly/OISST_Daily_AVHRR-only_Feature_Collection_best.ncd" 27 | var testLayer = L.tileLayer.wms(testWMS, { 28 | layers: 'sst', 29 | format: 'image/png', 30 | transparent: true, 31 | style: 'boxfill/sst_36', 32 | colorscalerange: '-3,35', 33 | abovemaxcolor: "extend", 34 | belowmincolor: "extend", 35 | attribution: 'NOAAs National Climatic Data Center' 36 | }); 37 | 38 | var testTimeLayer = L.timeDimension.layer.wms(testLayer, { 39 | proxy: proxy, 40 | updateTimeDimension: false, 41 | }); 42 | testTimeLayer.addTo(map); 43 | 44 | var testLegend = L.control({ 45 | position: 'topright' 46 | }); 47 | testLegend.onAdd = function(map) { 48 | var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=sst&PALETTE=sst_36&COLORSCALERANGE=-3,35"; 49 | var div = L.DomUtil.create('div', 'info legend'); 50 | div.innerHTML += 51 | 'legend'; 52 | return div; 53 | }; 54 | testLegend.addTo(map); 55 | 56 | L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({ 57 | _getDisplayDateFormat: function(date){ 58 | return date.format("dS mmmm yyyy"); 59 | } 60 | }); 61 | var timeDimensionControl = new L.Control.TimeDimensionCustom({ 62 | playerOptions: { 63 | buffer: 1, 64 | minBufferReady: -1 65 | } 66 | }); 67 | map.addControl(this.timeDimensionControl); 68 | -------------------------------------------------------------------------------- /examples/js/example12.js: -------------------------------------------------------------------------------- 1 | Date.prototype.format = function (mask, utc) { 2 | return dateFormat(this, mask, utc); 3 | }; 4 | 5 | 6 | 7 | // Attibution: SODA API requests based on this example: https://github.com/chriswhong/soda-leaflet 8 | L.TimeDimension.Layer.SODAHeatMap = L.TimeDimension.Layer.extend({ 9 | 10 | initialize: function(options) { 11 | var heatmapCfg = this._getHeatmapOptions(options.heatmatOptions || {}); 12 | var layer = new HeatmapOverlay(heatmapCfg); 13 | L.TimeDimension.Layer.prototype.initialize.call(this, layer, options); 14 | this._currentLoadedTime = 0; 15 | this._currentTimeData = { 16 | max: this.options.heatmapMax || 10, 17 | data: [] 18 | }; 19 | this._baseURL = this.options.baseURL || null; 20 | this._period = this.options.period || "P1M"; 21 | }, 22 | 23 | _getHeatmapOptions: function(options) { 24 | var config = {}; 25 | var defaultConfig = { 26 | radius: 15, 27 | maxOpacity: .8, 28 | scaleRadius: false, 29 | useLocalExtrema: false, 30 | latField: 'lat', 31 | lngField: 'lng', 32 | valueField: 'count' 33 | }; 34 | for (var attrname in defaultConfig) { 35 | config[attrname] = defaultConfig[attrname]; 36 | } 37 | for (var attrname in options) { 38 | config[attrname] = options[attrname]; 39 | } 40 | return config; 41 | }, 42 | 43 | onAdd: function(map) { 44 | L.TimeDimension.Layer.prototype.onAdd.call(this, map); 45 | map.addLayer(this._baseLayer); 46 | if (this._timeDimension) { 47 | this._getDataForTime(this._timeDimension.getCurrentTime()); 48 | } 49 | }, 50 | 51 | _onNewTimeLoading: function(ev) { 52 | this._getDataForTime(ev.time); 53 | return; 54 | }, 55 | 56 | isReady: function(time) { 57 | return (this._currentLoadedTime == time); 58 | }, 59 | 60 | _update: function() { 61 | this._baseLayer.setData(this._currentTimeData); 62 | return true; 63 | }, 64 | 65 | _getDataForTime: function(time) { 66 | if (!this._baseURL || !this._map) { 67 | return; 68 | } 69 | var url = this._constructQuery(time); 70 | var oReq = new XMLHttpRequest(); 71 | oReq.addEventListener("load", (function(xhr) { 72 | var response = xhr.currentTarget.response; 73 | var data = JSON.parse(response); 74 | delete this._currentTimeData.data; 75 | this._currentTimeData.data = []; 76 | for (var i = 0; i < data.length; i++) { 77 | var marker = data[i]; 78 | if (marker.location) { 79 | this._currentTimeData.data.push({ 80 | lat: marker.location.latitude, 81 | lng: marker.location.longitude, 82 | count: 1 83 | }); 84 | } 85 | } 86 | this._currentLoadedTime = time; 87 | if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) { 88 | this._update(); 89 | } 90 | this.fire('timeload', { 91 | time: time 92 | }); 93 | }).bind(this)); 94 | 95 | oReq.open("GET", url); 96 | oReq.send(); 97 | 98 | }, 99 | 100 | _constructQuery: function(time) { 101 | var bbox = this._map.getBounds(); 102 | var sodaQueryBox = [bbox._northEast.lat, bbox._southWest.lng, bbox._southWest.lat, bbox._northEast.lng]; 103 | 104 | var startDate = new Date(time); 105 | var endDate = new Date(startDate.getTime()); 106 | L.TimeDimension.Util.addTimeDuration(endDate, this._period, false); 107 | 108 | var where = "&$where=created_date > '" + 109 | startDate.format('yyyy-mm-dd') + 110 | "' AND created_date < '" + 111 | endDate.format('yyyy-mm-dd') + 112 | "' AND within_box(location," + 113 | sodaQueryBox + 114 | ")&$order=created_date desc"; 115 | 116 | var url = this._baseURL + where; 117 | return url; 118 | } 119 | 120 | }); 121 | 122 | L.timeDimension.layer.sodaHeatMap = function(options) { 123 | return new L.TimeDimension.Layer.SODAHeatMap(options); 124 | }; 125 | 126 | 127 | 128 | var currentTime = new Date(); 129 | currentTime.setUTCDate(1, 0, 0, 0, 0); 130 | 131 | var map = L.map('map', { 132 | zoom: 12, 133 | fullscreenControl: true, 134 | timeDimension: true, 135 | timeDimensionOptions: { 136 | timeInterval: "2010-01-01/" + currentTime.toISOString(), 137 | period: "P1M", 138 | currentTime: currentTime 139 | }, 140 | center: [40.74, -73.9], 141 | }); 142 | 143 | var layer = new L.StamenTileLayer("toner-lite"); 144 | map.addLayer(layer); 145 | 146 | var testSODALayer = L.timeDimension.layer.sodaHeatMap({ 147 | baseURL: 'https://data.cityofnewyork.us/resource/erm2-nwe9.json?$select=location,closed_date,complaint_type,street_name,created_date,status,unique_key,agency_name,due_date,descriptor,location_type,agency,incident_address&complaint_type=Noise - Commercial', 148 | }); 149 | testSODALayer.addTo(map); 150 | map.attributionControl.addAttribution('NYC OpenData'); 151 | 152 | L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({ 153 | _getDisplayDateFormat: function(date){ 154 | return date.format("mmmm yyyy"); 155 | } 156 | }); 157 | var timeDimensionControl = new L.Control.TimeDimensionCustom({ 158 | playerOptions: { 159 | buffer: 1, 160 | minBufferReady: -1 161 | } 162 | }); 163 | map.addControl(this.timeDimensionControl); 164 | -------------------------------------------------------------------------------- /examples/js/example13.js: -------------------------------------------------------------------------------- 1 | var startDate = new Date(); 2 | startDate.setUTCHours(0, 0, 0, 0); 3 | 4 | var map = L.map('map', { 5 | zoom: 8, 6 | fullscreenControl: true, 7 | timeDimensionControl: true, 8 | timeDimensionControlOptions: { 9 | position: 'bottomleft', 10 | playerOptions: { 11 | transitionTime: 1000, 12 | } 13 | }, 14 | timeDimension: true, 15 | center: [39.3, 2.9] 16 | }); 17 | 18 | var sapoWMS = "https://thredds.socib.es/thredds/wms/operational_models/oceanographical/wave/model_run_aggregation/sapo_ib/sapo_ib_best.ncd"; 19 | var sapoHeightLayer = L.tileLayer.wms(sapoWMS, { 20 | layers: 'significant_wave_height', 21 | format: 'image/png', 22 | transparent: true, 23 | colorscalerange: '0,3', 24 | abovemaxcolor: "extend", 25 | belowmincolor: "extend", 26 | numcolorbands: 100, 27 | styles: 'areafill/scb_bugnylorrd' 28 | }); 29 | 30 | var sapoMeanDirectionLayer = L.nonTiledLayer.wms(sapoWMS, { 31 | layers: 'average_wave_direction', 32 | format: 'image/png', 33 | transparent: true, 34 | colorscalerange: '1,1', 35 | abovemaxcolor: "extend", 36 | belowmincolor: "extend", 37 | markerscale: 15, 38 | markerspacing: 12, 39 | markerclipping: true, 40 | styles: 'prettyvec/greyscale' 41 | }); 42 | 43 | var sapoPeakDirectionLayer = L.nonTiledLayer.wms(sapoWMS, { 44 | layers: 'direction_of_the_peak_of_the_spectrum', 45 | format: 'image/png', 46 | transparent: true, 47 | colorscalerange: '0,2', 48 | abovemaxcolor: "extend", 49 | belowmincolor: "extend", 50 | markerscale: 15, 51 | markerspacing: 12, 52 | markerclipping: true, 53 | styles: 'prettyvec/greyscale' 54 | }); 55 | 56 | var markers = [{ 57 | name: 'Sa Dragonera', 58 | position: [39.555, 2.102], 59 | platformName: 'Buoy at Sa Dragonera', 60 | platform: 18, 61 | instrument: 68, 62 | variable: 17 63 | }, { 64 | name: 'Alcúdia', 65 | position: [39.8, 3.216] 66 | }, { 67 | name: 'Palma', 68 | position: [39.492847, 2.700405], 69 | platformName: 'Buoy at Palma Bay', 70 | platform: 143, 71 | instrument: 296, 72 | variable: 90047 73 | }, { 74 | name: 'Ciutadella', 75 | position: [39.976, 3.761] 76 | }, { 77 | name: 'Ibiza Channel', 78 | platformName: 'Buoy at Ibiza Channel', 79 | position: [38.82445, 0.783667], 80 | platform: 146, 81 | instrument: 314, 82 | variable: 90047 83 | }]; 84 | 85 | var proxy = 'server/proxy.php'; 86 | var sapoHeightTimeLayer = L.timeDimension.layer.wms.timeseries(sapoHeightLayer, { 87 | proxy: proxy, 88 | updateTimeDimension: true, 89 | markers: markers, 90 | name: "Significant wave height", 91 | units: "m", 92 | enableNewMarkers: true 93 | }); 94 | 95 | var sapoMeanDirectionTimeLayer = L.timeDimension.layer.wms(sapoMeanDirectionLayer, { 96 | proxy: proxy 97 | }); 98 | var sapoPeakDirectionTimeLayer = L.timeDimension.layer.wms(sapoPeakDirectionLayer, { 99 | proxy: proxy 100 | }); 101 | 102 | var sapoLegend = L.control({ 103 | position: 'bottomright' 104 | }); 105 | sapoLegend.onAdd = function(map) { 106 | var src = sapoWMS + "?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&LAYER=significant_wave_height&colorscalerange=0,3&PALETTE=scb_bugnylorrd&numcolorbands=100&transparent=TRUE"; 107 | var div = L.DomUtil.create('div', 'info legend'); 108 | div.innerHTML += 109 | 'legend'; 110 | return div; 111 | }; 112 | 113 | var sapoMeanDirectionLegend = L.control({ 114 | position: 'bottomright' 115 | }); 116 | sapoMeanDirectionLegend.onAdd = function(map) { 117 | var div = L.DomUtil.create('div', 'info legend'); 118 | div.innerHTML += ' mean direction'; 119 | return div; 120 | }; 121 | 122 | var sapoPeakDirectionLegend = L.control({ 123 | position: 'bottomright' 124 | }); 125 | sapoPeakDirectionLegend.onAdd = function(map) { 126 | var div = L.DomUtil.create('div', 'info legend'); 127 | div.innerHTML += ' peak direction'; 128 | return div; 129 | }; 130 | 131 | var overlayMaps = { 132 | "SAPO - significant wave height": sapoHeightTimeLayer, 133 | "SAPO - average wave direction": sapoMeanDirectionTimeLayer, 134 | "SAPO - direction of the peak": sapoPeakDirectionTimeLayer 135 | }; 136 | 137 | map.on('overlayadd', function(eventLayer) { 138 | if (eventLayer.name == 'SAPO - significant wave height') { 139 | sapoLegend.addTo(this); 140 | } else if (eventLayer.name == 'SAPO - average wave direction') { 141 | sapoMeanDirectionLegend.addTo(this); 142 | } else if (eventLayer.name == 'SAPO - direction of the peak') { 143 | sapoPeakDirectionLegend.addTo(this); 144 | } 145 | }); 146 | 147 | map.on('overlayremove', function(eventLayer) { 148 | if (eventLayer.name == 'SAPO - significant wave height') { 149 | map.removeControl(sapoLegend); 150 | } else if (eventLayer.name == 'SAPO - average wave direction') { 151 | map.removeControl(sapoMeanDirectionLegend); 152 | } else if (eventLayer.name == 'SAPO - direction of the peak') { 153 | map.removeControl(sapoPeakDirectionLegend); 154 | } 155 | }); 156 | 157 | var baseLayers = getCommonBaseLayers(map); // see baselayers.js 158 | L.control.layers(baseLayers, overlayMaps).addTo(map); 159 | 160 | sapoHeightTimeLayer.addTo(map); 161 | sapoPeakDirectionTimeLayer.addTo(map); 162 | sapoMeanDirectionTimeLayer.addTo(map); -------------------------------------------------------------------------------- /examples/js/example14.js: -------------------------------------------------------------------------------- 1 | var endDate = new Date(); 2 | endDate.setUTCMinutes(0, 0, 0); 3 | 4 | var map = L.map('map', { 5 | zoom: 4, 6 | fullscreenControl: true, 7 | timeDimension: true, 8 | timeDimensionControl: true, 9 | timeDimensionOptions:{ 10 | timeInterval: "PT4H/" + endDate.toISOString(), 11 | period: "PT4M", 12 | currentTime: endDate 13 | }, 14 | 15 | timeDimensionControlOptions: { 16 | autoPlay: false, 17 | playerOptions: { 18 | buffer: 10, 19 | transitionTime: 250, 20 | loop: true, 21 | } 22 | }, 23 | center: [38.0, -90.50], 24 | }); 25 | 26 | L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { 27 | attribution: '© OpenStreetMap contributors' 28 | }).addTo(map); 29 | 30 | var wmsUrl = "https://nowcoast.noaa.gov/arcgis/services/nowcoast/radar_meteo_imagery_nexrad_time/MapServer/WMSServer" 31 | var radarWMS = L.nonTiledLayer.wms(wmsUrl, { 32 | layers: '1', 33 | format: 'image/png', 34 | transparent: true, 35 | opacity: 0.8, 36 | attribution: 'nowCOAST' 37 | }); 38 | 39 | var testTimeLayer = L.timeDimension.layer.wms(radarWMS, { 40 | updateTimeDimension: false, 41 | wmsVersion: '1.3.0' 42 | }); 43 | testTimeLayer.addTo(map); 44 | 45 | var theLegend = L.control({ 46 | position: 'topright' 47 | }); 48 | 49 | theLegend.onAdd = function(map) { 50 | var src = "https://nowcoast.noaa.gov/images/legends/radar.png"; 51 | var div = L.DomUtil.create('div', 'info legend'); 52 | div.style.width = '270px'; 53 | div.style.height = '50px'; 54 | div.innerHTML += 'Legend
legend'; 55 | return div; 56 | }; 57 | theLegend.addTo(map); -------------------------------------------------------------------------------- /examples/js/example15.js: -------------------------------------------------------------------------------- 1 | L.TimeDimension.Layer.CDrift = L.TimeDimension.Layer.GeoJson.extend({ 2 | 3 | // CDrift data has property time in seconds, not in millis. 4 | _getFeatureTimes: function(feature) { 5 | if (!feature.properties) { 6 | return []; 7 | } 8 | if (feature.properties.hasOwnProperty('coordTimes')) { 9 | return feature.properties.coordTimes; 10 | } 11 | if (feature.properties.hasOwnProperty('times')) { 12 | return feature.properties.times; 13 | } 14 | if (feature.properties.hasOwnProperty('linestringTimestamps')) { 15 | return feature.properties.linestringTimestamps; 16 | } 17 | if (feature.properties.hasOwnProperty('time')) { 18 | return [feature.properties.time * 1000]; 19 | } 20 | return []; 21 | }, 22 | 23 | // Do not modify features. Just return the feature if it intersects 24 | // the time interval 25 | _getFeatureBetweenDates: function(feature, minTime, maxTime) { 26 | var featureStringTimes = this._getFeatureTimes(feature); 27 | if (featureStringTimes.length == 0) { 28 | return feature; 29 | } 30 | var featureTimes = []; 31 | for (var i = 0, l = featureStringTimes.length; i < l; i++) { 32 | var time = featureStringTimes[i] 33 | if (typeof time == 'string' || time instanceof String) { 34 | time = Date.parse(time.trim()); 35 | } 36 | featureTimes.push(time); 37 | } 38 | 39 | if (featureTimes[0] > maxTime || featureTimes[l - 1] < minTime) { 40 | return null; 41 | } 42 | return feature; 43 | }, 44 | 45 | }); 46 | 47 | L.timeDimension.layer.cDrift = function(layer, options) { 48 | return new L.TimeDimension.Layer.CDrift(layer, options); 49 | }; 50 | 51 | var startDate = new Date(); 52 | startDate.setUTCHours(0, 0, 0, 0); 53 | 54 | var map = L.map('map', { 55 | zoom: 8, 56 | fullscreenControl: true, 57 | timeDimensionControl: true, 58 | timeDimensionControlOptions: { 59 | position: 'bottomleft', 60 | autoPlay: true, 61 | timeSlider: false, 62 | loopButton: true, 63 | playerOptions: { 64 | transitionTime: 125, 65 | loop: true, 66 | } 67 | }, 68 | timeDimension: true, 69 | center: [39.6145, 1.99363] 70 | }); 71 | 72 | var oReq = new XMLHttpRequest(); 73 | oReq.addEventListener("load", (function(xhr) { 74 | var response = xhr.currentTarget.response; 75 | var data = JSON.parse(response); 76 | var cdriftLayer = L.geoJson(data, { 77 | style: function(feature) { 78 | var color = "#FFF"; 79 | if (feature.properties.confidence == '0.9') { 80 | color = "#FF0000"; 81 | } else if (feature.properties.confidence == '0.75') { 82 | color = "#FFFF00"; 83 | } else if (feature.properties.confidence == '0.5') { 84 | color = "#00FF00"; 85 | } 86 | return { 87 | "color": color, 88 | "weight": 2, 89 | "opacity": 0.4 90 | }; 91 | } 92 | }); 93 | 94 | var cdriftTimeLayer = L.timeDimension.layer.cDrift(cdriftLayer, { 95 | updateTimeDimension: true, 96 | updateTimeDimensionMode: 'replace', 97 | addlastPoint: false, 98 | duration: 'PT20M', 99 | }); 100 | cdriftTimeLayer.addTo(map); 101 | map.fitBounds(cdriftLayer.getBounds()); 102 | 103 | var cDriftLegend = L.control({ 104 | position: 'bottomright' 105 | }); 106 | cDriftLegend.onAdd = function(map) { 107 | var div = L.DomUtil.create('div', 'info legend'); 108 | div.innerHTML += ''; 109 | return div; 110 | }; 111 | cDriftLegend.addTo(map); 112 | 113 | map.timeDimension.on('timeload', function(data) { 114 | var date = new Date(map.timeDimension.getCurrentTime()); 115 | if (data.time == map.timeDimension.getCurrentTime()) { 116 | var totalTimes = map.timeDimension.getAvailableTimes().length; 117 | var position = map.timeDimension.getAvailableTimes().indexOf(data.time); 118 | map.getContainer().querySelector('.animation-progress-bar').style.width = ((position*100)/totalTimes + "%"); 119 | // update map bounding box 120 | map.fitBounds(cdriftTimeLayer.getBounds()); 121 | } 122 | }); 123 | })); 124 | oReq.open("GET", 'data/spill.json'); 125 | oReq.send(); 126 | 127 | 128 | 129 | 130 | var sorrento = L.circleMarker([39.6145, 1.99363], { 131 | color: '#FFFFFF', 132 | fillColor: "#f28f43", 133 | fillOpacity: 1, 134 | radius: 5, 135 | weight: 2 136 | }).addTo(map); 137 | 138 | var baseLayers = getCommonBaseLayers(map); // see baselayers.js 139 | L.control.layers(baseLayers, {}).addTo(map); 140 | -------------------------------------------------------------------------------- /examples/js/example16.js: -------------------------------------------------------------------------------- 1 | var startDate = new Date(); 2 | startDate.setUTCHours(0, 0, 0, 0); 3 | 4 | var map = L.map('map', { 5 | zoom: 7, 6 | fullscreenControl: true, 7 | timeDimensionControl: true, 8 | timeDimensionControlOptions: { 9 | position: 'bottomleft', 10 | playerOptions: { 11 | transitionTime: 1000, 12 | } 13 | }, 14 | timeDimension: true, 15 | timeDimensionOptions: { 16 | timeInterval: startDate.toISOString() + "/PT72H", 17 | period: "PT3H" 18 | }, 19 | center: [39.3, 2.9] 20 | }); 21 | 22 | var portusLayer = L.tileLayer('https://portus.puertos.es/Portus//pathtiles/wave/MED/VHM0/{d}{h}/map//{z}/{x}/{y}.png', { 23 | attribution: '© Agencia Estatal de Meteorología (AEMET) y Puertos del Estado (OPPE)', 24 | tms: true, 25 | maxZoom: 7, 26 | }); 27 | var portusTimeLayer = L.timeDimension.layer.tileLayer.portus(portusLayer, {}); 28 | 29 | var portusBalLayer = L.tileLayer('https://portus.puertos.es/Portus//pathtiles/wave/S12B/VHM0/{d}{h}/map//{z}/{x}/{y}.png', { 30 | attribution: '© Agencia Estatal de Meteorología (AEMET) y Puertos del Estado (OPPE)', 31 | tms: true, 32 | minZoom: 8 33 | }); 34 | var portusBalTimeLayer = L.timeDimension.layer.tileLayer.portus(portusBalLayer, {}); 35 | 36 | var overlayMaps = { 37 | "Mediterranean wave": portusTimeLayer, 38 | "Balearic wave": portusBalTimeLayer, 39 | }; 40 | 41 | var baseLayers = getCommonBaseLayers(map); // see baselayers.js 42 | L.control.layers(baseLayers, overlayMaps).addTo(map); 43 | 44 | portusTimeLayer.addTo(map); 45 | portusBalTimeLayer.addTo(map); 46 | -------------------------------------------------------------------------------- /examples/js/example17.js: -------------------------------------------------------------------------------- 1 | function addGeoJSONLayer(map, data) { 2 | var icon = L.icon({ 3 | iconUrl: 'img/bus.png', 4 | iconSize: [22, 22], 5 | iconAnchor: [11, 11] 6 | }); 7 | 8 | var geoJSONLayer = L.geoJSON(data, { 9 | pointToLayer: function (feature, latLng) { 10 | if (feature.properties.hasOwnProperty('last')) { 11 | return new L.Marker(latLng, { 12 | icon: icon 13 | }); 14 | } 15 | return L.circleMarker(latLng); 16 | } 17 | }); 18 | 19 | var geoJSONTDLayer = L.timeDimension.layer.geoJson(geoJSONLayer, { 20 | updateTimeDimension: true, 21 | duration: 'PT2M', 22 | updateTimeDimensionMode: 'replace', 23 | addlastPoint: true 24 | }); 25 | 26 | // Show both layers: the geoJSON layer to show the whole track 27 | // and the timedimension layer to show the movement of the bus 28 | geoJSONLayer.addTo(map); 29 | geoJSONTDLayer.addTo(map); 30 | } 31 | 32 | var map = L.map('map', { 33 | zoom: 14, 34 | fullscreenControl: true, 35 | timeDimensionControl: true, 36 | timeDimensionControlOptions: { 37 | timeSliderDragUpdate: true, 38 | loopButton: true, 39 | autoPlay: true, 40 | playerOptions: { 41 | transitionTime: 1000, 42 | loop: true 43 | } 44 | }, 45 | timeDimension: true, 46 | center: [36.72, -4.43] 47 | }); 48 | 49 | var osmLayer = L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { 50 | attribution: '© OpenStreetMap contributors' 51 | }); 52 | osmLayer.addTo(map); 53 | 54 | var oReq = new XMLHttpRequest(); 55 | oReq.addEventListener("load", (function (xhr) { 56 | var response = xhr.currentTarget.response; 57 | var data = JSON.parse(response); 58 | addGeoJSONLayer(map, data); 59 | })); 60 | oReq.open('GET', 'data/track_bus699.geojson'); 61 | oReq.send(); 62 | -------------------------------------------------------------------------------- /examples/js/example18.js: -------------------------------------------------------------------------------- 1 | // Set 1 hour earlier 2 | var endDate = new Date(); 3 | endDate.setUTCHours(endDate.getUTCHours() - 1); 4 | endDate.setUTCMinutes(0, 0, 0); 5 | 6 | L.TimeDimension.Layer.VelocityLayer.Radar = L.TimeDimension.Layer.VelocityLayer.extend({ 7 | 8 | initialize: function(options) { 9 | L.TimeDimension.Layer.VelocityLayer.prototype.initialize.call(this, options); 10 | this._socibDataSource = this.options.socibDataSource || null; 11 | this._apiKey = this.options.apiKey || null; 12 | this._proxy = this.options.proxy || null; 13 | 14 | // Update availableTimes from datasource metadata 15 | this._updateTimeDimension = this.options.updateTimeDimension || false; 16 | this._updateTimeDimensionMode = this.options.updateTimeDimensionMode || 'intersect'; // 'union' or 'replace' 17 | this._period = this.options.period || 'PT1H'; 18 | this._setDefaultTime = this.options.setDefaultTime || false; 19 | this._endTime = this.options.endTime || null; 20 | this._metadataRequested = false; 21 | if (this._updateTimeDimension) { 22 | this._requestDataSourceMetadata(); 23 | } 24 | }, 25 | 26 | onAdd: function(map) { 27 | L.TimeDimension.Layer.VelocityLayer.prototype.onAdd.call(this, map); 28 | if (this._updateTimeDimension) { 29 | if (this._endTime === null) { 30 | this._requestDataSourceMetadata(); 31 | } else { 32 | this._updateTimeDimensionAvailableTimes(); 33 | } 34 | } 35 | }, 36 | 37 | _constructQuery: function(time) { 38 | var startDate = new Date(time); 39 | var endDate = new Date(startDate.getTime()); 40 | L.TimeDimension.Util.addTimeDuration(endDate, "PT30M", false); 41 | var timeParams = 42 | "&initial_datetime=" + 43 | startDate.toISOString().substring(0, 19) + 44 | "&end_datetime=" + 45 | endDate.toISOString().substring(0, 19); 46 | var url = this._baseURL + this._socibDataSource + 47 | "/data/?max_qc_value=3&standard_name=northward_sea_water_velocity&standard_name=eastward_sea_water_velocity&processing_level=L1&format=json" + 48 | timeParams; 49 | if (this._apiKey) { 50 | url = url + "&api_key=" + this._apiKey; 51 | } 52 | if (this._proxy) { 53 | url = this._proxy + "?url=" + encodeURIComponent(url); 54 | } 55 | return url; 56 | }, 57 | 58 | _processLoadedData: function(data) { 59 | if (data.length === 0) { 60 | return []; 61 | } 62 | findVariable = function(standard_name, variable) { 63 | return variable.standard_name === standard_name; 64 | }; 65 | var u = data[0].variables.find(findVariable.bind(this, "eastward_sea_water_velocity")); 66 | var v = data[0].variables.find(findVariable.bind(this, "northward_sea_water_velocity")); 67 | var lat = data[0].coordinates.latitude; 68 | var lon = data[0].coordinates.longitude; 69 | var time = data[0].coordinates.time.data; 70 | 71 | var u_header = { 72 | parameterUnit: u.units, 73 | parameterNumberName: u.standard_name, 74 | parameterNumber: 2, 75 | parameterCategory: 2, 76 | la1: lat.data[0], 77 | la2: lat.data[lat.data.length - 1], 78 | lo1: lon.data[0], 79 | lo2: lon.data[lon.data.length - 1], 80 | nx: lon.data.length, 81 | ny: lat.data.length, 82 | refTime: time 83 | }; 84 | u_header["dx"] = (u_header["lo2"] - u_header["lo1"]) / u_header["nx"]; 85 | u_header["dy"] = (u_header["la1"] - u_header["la2"]) / u_header["ny"]; 86 | 87 | var v_header = { 88 | parameterUnit: v.units, 89 | parameterNumberName: v.standard_name, 90 | parameterNumber: 3, // northward-v 91 | parameterCategory: 2, 92 | la1: lat.data[0], 93 | la2: lat.data[lat.data.length - 1], 94 | lo1: lon.data[0], 95 | lo2: lon.data[lon.data.length - 1], 96 | nx: lon.data.length, 97 | ny: lat.data.length, 98 | refTime: time 99 | }; 100 | v_header["dx"] = (v_header["lo2"] - v_header["lo1"]) / v_header["nx"]; 101 | v_header["dy"] = (v_header["la1"] - v_header["la2"]) / v_header["ny"]; 102 | 103 | return [ 104 | { 105 | header: u_header, 106 | data: _.flatten(u.data[0]) 107 | }, 108 | { 109 | header: v_header, 110 | data: _.flatten(v.data[0]) 111 | } 112 | ]; 113 | }, 114 | 115 | _setEndTime: function(endTime) { 116 | this._endTime = endTime; 117 | }, 118 | 119 | _getDefaultTime: function() { 120 | return this._endTime; 121 | }, 122 | 123 | _updateTimeDimensionAvailableTimes: function() { 124 | if (this._timeDimension && this._updateTimeDimension) { 125 | var startTime = new Date(this._timeDimension.getAvailableTimes()[0]); 126 | var times = L.TimeDimension.Util.explodeTimeRange(startTime, this._endTime, this._period); 127 | this._timeDimension.setAvailableTimes(times, this._updateTimeDimensionMode); 128 | if (this._setDefaultTime) { 129 | var defaultTime = this._getDefaultTime(); 130 | if (defaultTime !== this._timeDimension.getCurrentTime()){ 131 | this._timeDimension.setCurrentTime(this._getDefaultTime()); 132 | } 133 | } 134 | } 135 | }, 136 | 137 | _requestDataSourceMetadata: function() { 138 | if (this._metadataRequested) { 139 | return; 140 | } 141 | this._metadataRequested = true; 142 | var url = this._baseURL + this._socibDataSource + "/?"; 143 | if (this._apiKey) { 144 | url = url + "&api_key=" + this._apiKey; 145 | } 146 | if (this._proxy) { 147 | url = this._proxy + "?url=" + encodeURIComponent(url); 148 | } 149 | var oReq = new XMLHttpRequest(); 150 | oReq.addEventListener("load", (function(xhr) { 151 | var data = null; 152 | try { 153 | var response = xhr.currentTarget.response; 154 | data = JSON.parse(response); 155 | } catch(e) { 156 | console.log("Error parsing API response", e); 157 | } 158 | if (data !== null){ 159 | var end_datetime = new Date(Date.parse(data['end_datetime'])); 160 | this._setEndTime(end_datetime); 161 | this._updateTimeDimensionAvailableTimes(); 162 | } 163 | }).bind(this)); 164 | oReq.open("GET", url); 165 | oReq.send(); 166 | }, 167 | 168 | }); 169 | 170 | L.timeDimension.layer.velocityLayer.radar = function(options) { 171 | return new L.TimeDimension.Layer.VelocityLayer.Radar(options); 172 | }; 173 | 174 | var map = L.map("map", { 175 | zoom: 10, 176 | center: [38.65, 1.15], 177 | fullscreenControl: true, 178 | timeDimension: true, 179 | timeDimensionControl: true, 180 | timeDimensionOptions: { 181 | timeInterval: "P1M/" + endDate.toISOString(), 182 | period: "PT1H", 183 | currentTime: endDate.getTime() 184 | }, 185 | timeDimensionControlOptions: { 186 | autoPlay: false, 187 | playerOptions: { 188 | minBufferReady: -1, 189 | transitionTime: 2000, 190 | loop: true 191 | } 192 | } 193 | }); 194 | 195 | 196 | var GSHHS_h_L1 = L.tileLayer.wms( 197 | "https://gis.socib.es/geoserver/gwc/service/wms?", { 198 | layers: 'gshhs%3AGSHHS_h_L1', 199 | transparent: true, 200 | format: 'image/png' 201 | } 202 | ); 203 | 204 | var baseLayers = { 205 | "Shoreline from GSHHG": GSHHS_h_L1 206 | }; 207 | 208 | L.control.layers(baseLayers, {}).addTo(map); 209 | GSHHS_h_L1.addTo(map); 210 | 211 | var testVelocityRadarLayer = L.timeDimension.layer.velocityLayer.radar({ 212 | baseURL: "https://api.socib.es/data-sources/", 213 | socibDataSource: "f8e2429729", 214 | updateTimeDimension: true, 215 | updateTimeDimensionMode: 'intersect', 216 | setDefaultTime: true, 217 | proxy: "server/proxy-api.php", 218 | velocityLayerOptions: { 219 | particleAge: 30 220 | } 221 | }); 222 | testVelocityRadarLayer.addTo(map); 223 | -------------------------------------------------------------------------------- /examples/js/example2.js: -------------------------------------------------------------------------------- 1 | var map = L.map('map', { 2 | zoom: 4.5, 3 | zoomDelta: 0.5, 4 | zoomSnap: 0.5, 5 | fullscreenControl: true, 6 | timeDimension: true, 7 | timeDimensionControl: true, 8 | timeDimensionOptions: { 9 | // times: "2014-12-01T06:00:00Z,2014-12-01T09:00:00Z,2014-12-01T12:00:00Z,2014-12-01T15:00:00Z,2014-12-01T18:00:00Z,2014-12-01T21:00:00Z,2014-12-02T00:00:00Z,2014-12-02T03:00:00Z,2014-12-02T06:00:00Z,2014-12-02T09:00:00Z,2014-12-02T12:00:00Z,2014-12-02T15:00:00Z,2014-12-02T18:00:00Z,2014-12-02T21:00:00Z,2014-12-03T00:00:00Z,2014-12-03T03:00:00Z,2014-12-03T06:00:00Z,2014-12-03T09:00:00Z,2014-12-03T12:00:00Z,2014-12-03T15:00:00Z,2014-12-03T18:00:00Z,2014-12-03T21:00:00Z,2014-12-04T00:00:00Z,2014-12-04T03:00:00Z,2014-12-04T06:00:00Z,2014-12-04T12:00:00Z,2014-12-04T18:00:00Z,2014-12-05T00:00:00Z,2014-12-05T06:00:00Z,2014-12-05T12:00:00Z,2014-12-05T18:00:00Z,2014-12-06T00:00:00Z,2014-12-06T06:00:00Z,2014-12-06T12:00:00Z,2014-12-06T18:00:00Z,2014-12-07T00:00:00Z,2014-12-07T06:00:00Z,2014-12-07T12:00:00Z,2014-12-07T18:00:00Z,2014-12-08T00:00:00Z,2014-12-08T06:00:00Z,2014-12-08T12:00:00Z,2014-12-08T18:00:00Z,2014-12-09T00:00:00Z,2014-12-09T06:00:00Z,2014-12-09T12:00:00Z,2014-12-09T18:00:00Z,2014-12-10T00:00:00Z,2014-12-10T06:00:00Z" 10 | }, 11 | center: [45.3, 0.9], 12 | }); 13 | 14 | 15 | // https://ogcie.iblsoft.com/metocean/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities 16 | var testWMS = "https://ogcie.iblsoft.com/metocean/wms" 17 | L.tileLayer.wms(testWMS, { 18 | layers: 'foreground-lines', 19 | format: 'image/png', 20 | transparent: true, 21 | crs: L.CRS.EPSG4326 22 | }).addTo(map); 23 | 24 | var testLayer = L.tileLayer.wms(testWMS, { 25 | layers: 'gfs-temperature-isbl', // isobaric levels, or -agl for above ground levels 26 | format: 'image/png', 27 | transparent: true, 28 | opacity: 0.3, 29 | crs: L.CRS.EPSG4326, 30 | attribution: 'OGC MetOcean DWG Best Practice Example, IBL Software Engineering' 31 | }); 32 | var proxy = 'server/proxy.php'; 33 | var testTimeLayer = L.timeDimension.layer.wms(testLayer, { 34 | proxy: proxy, 35 | updateTimeDimension: true, 36 | }); 37 | testTimeLayer.addTo(map); 38 | 39 | var testLegend = L.control({ 40 | position: 'topright' 41 | }); 42 | testLegend.onAdd = function(map) { 43 | var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=gfs-temperature-isbl&STYLE=default"; 44 | var div = L.DomUtil.create('div', 'info legend'); 45 | div.innerHTML += 46 | 'legend'; 47 | return div; 48 | }; 49 | testLegend.addTo(map); 50 | -------------------------------------------------------------------------------- /examples/js/example3.js: -------------------------------------------------------------------------------- 1 | var map = L.map('map', { 2 | zoom: 10, 3 | fullscreenControl: true, 4 | timeDimension: true, 5 | timeDimensionOptions: { 6 | timeInterval: "2015-09-01/2015-09-03", 7 | period: "PT1H", 8 | currentTime: Date.parse("2015-09-01T00:00:00Z") 9 | }, 10 | timeDimensionControl: true, 11 | timeDimensionControlOptions: { 12 | autoPlay: true, 13 | loopButton: true, 14 | timeSteps: 1, 15 | playReverseButton: true, 16 | limitSliders: true, 17 | playerOptions: { 18 | buffer: 0, 19 | transitionTime: 250, 20 | loop: true, 21 | } 22 | }, 23 | center: [38.705, 1.15], 24 | }); 25 | 26 | L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { 27 | attribution: '© OpenStreetMap contributors' 28 | }).addTo(map); 29 | 30 | var testWMS = "https://thredds.socib.es/thredds/wms/observational/hf_radar/hf_radar_ibiza-scb_codarssproc001_L1_agg/hf_radar_ibiza-scb_codarssproc001_L1_agg_best.ncd" 31 | var testLayer = L.tileLayer.wms(testWMS, { 32 | layers: 'sea_water_velocity', 33 | version: '1.3.0', 34 | format: 'image/png', 35 | transparent: true, 36 | styles: 'prettyvec/rainbow', 37 | markerscale: 15, 38 | markerspacing: 10, 39 | abovemaxcolor: "extend", 40 | belowmincolor: "extend", 41 | colorscalerange: "0,0.4", 42 | attribution: 'SOCIB HF RADAR | sea_water_velocity' 43 | }); 44 | var proxy = 'server/proxy.php'; 45 | var testTimeLayer = L.timeDimension.layer.wms(testLayer, { 46 | proxy: proxy, 47 | updateTimeDimension: true 48 | }); 49 | testTimeLayer.addTo(map); 50 | 51 | var testLegend = L.control({ 52 | position: 'topright' 53 | }); 54 | testLegend.onAdd = function(map) { 55 | var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=sea_water_velocity&PALETTE=rainbow&colorscalerange=0,0.4"; 56 | var div = L.DomUtil.create('div', 'info legend'); 57 | div.innerHTML += 58 | 'legend'; 59 | return div; 60 | }; 61 | testLegend.addTo(map); 62 | 63 | $('#dtp_start').datetimepicker({ 64 | inline: true, 65 | value: new Date('2015-09-01'), 66 | format: "c" 67 | }); 68 | $('#dtp_end').datetimepicker({ 69 | inline: true, 70 | value: new Date('2015-09-03'), 71 | format: "c" 72 | }); 73 | 74 | $("#btn_timerange").click(function(){ 75 | var startTime = new Date($('#dtp_start').val()); 76 | var endTime = new Date($('#dtp_end').val()); 77 | var newAvailableTimes = L.TimeDimension.Util.explodeTimeRange(startTime, endTime, 'PT1H'); 78 | map.timeDimension.setAvailableTimes(newAvailableTimes, 'replace'); 79 | map.timeDimension.setCurrentTime(startTime); 80 | }); 81 | 82 | $("#btn_limitrange").click(function(){ 83 | var startTime = new Date($('#dtp_start').val()); 84 | var endTime = new Date($('#dtp_end').val()); 85 | map.timeDimension.setLowerLimit(startTime); 86 | map.timeDimension.setUpperLimit(endTime); 87 | map.timeDimension.setCurrentTime(startTime); 88 | }); -------------------------------------------------------------------------------- /examples/js/example4.js: -------------------------------------------------------------------------------- 1 | var endDate = new Date(); 2 | endDate.setUTCMinutes(0, 0, 0); 3 | 4 | var map = L.map('map', { 5 | zoom: 7, 6 | fullscreenControl: true, 7 | center: [52.0, 3.50], 8 | timeDimension: true, 9 | timeDimensionControl: true, 10 | timeDimensionOptions: { 11 | timeInterval: "P2W/" + endDate.toISOString(), 12 | period: "PT5M" 13 | }, 14 | timeDimensionControlOptions: { 15 | playerOptions: { 16 | transitionTime: 250, 17 | } 18 | } 19 | }); 20 | 21 | L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { 22 | attribution: '© OpenStreetMap contributors' 23 | }).addTo(map); 24 | 25 | 26 | // http://geoservices.knmi.nl/adaguc_portal/?srs=EPSG%3A28992&bbox=-47780.898876404506,300000,342780.8988764045,630000&service=http%253A%252F%252Fgeoservices.knmi.nl%252Fcgi-bin%252FRADNL_OPER_R___25PCPRR_L3.cgi%253F&layer=RADNL_OPER_R___25PCPRR_L3_COLOR%2524image%252Fpng%2524true%2524default%25241%25240&selected=0&dims=time$current&baselayers=world_raster$nl_world_line 27 | var testWMS = "https://geoservices.knmi.nl/cgi-bin/RADNL_OPER_R___25PCPRR_L3.cgi" 28 | var testLayer = L.nonTiledLayer.wms(testWMS, { 29 | layers: 'RADNL_OPER_R___25PCPRR_L3_COLOR', 30 | format: 'image/png', 31 | transparent: true, 32 | attribution: 'KNMI' 33 | }); 34 | var testTimeLayer = L.timeDimension.layer.wms(testLayer); 35 | testTimeLayer.addTo(map); 36 | 37 | var testLegend = L.control({ 38 | position: 'topright' 39 | }); 40 | testLegend.onAdd = function(map) { 41 | var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=RADNL_OPER_R___25PCPRR_L3_COLOR&format=image/png&STYLE=default"; 42 | var div = L.DomUtil.create('div', 'info legend'); 43 | div.style.width = '65px'; 44 | div.style.height = '280px'; 45 | div.style['background-image'] = 'url(' + src + ')'; 46 | return div; 47 | }; 48 | testLegend.addTo(map); 49 | -------------------------------------------------------------------------------- /examples/js/example5.js: -------------------------------------------------------------------------------- 1 | 2 | var map = L.map('map', { 3 | zoom: 2, 4 | fullscreenControl: true, 5 | timeDimension: true, 6 | timeDimensionOptions:{ 7 | timeInterval: "2006-01/2099-12", 8 | period: "P1M" 9 | }, 10 | timeDimensionControl: true, 11 | timeDimensionControlOptions:{ 12 | timeSteps: 12 13 | }, 14 | center: [20.0, 0.0], 15 | }); 16 | 17 | L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { 18 | attribution: '© OpenStreetMap contributors' 19 | }).addTo(map); 20 | 21 | /* 22 | var proxy = 'server/proxy.php'; 23 | var testWMS = "http://data2-glues.ufz.de/thredds/wms/pik_wcrp_cmip3/ncar_pcm1_sresb1_2006-2099_tmp.nc" 24 | var testLayer = L.tileLayer.wms(testWMS, { 25 | layers: 'tmp', 26 | format: 'image/png', 27 | transparent: true, 28 | attribution: 'PIK' 29 | }); 30 | var testTimeLayer = L.timeDimension.layer.wms(testLayer, {proxy: proxy}); 31 | testTimeLayer.addTo(map); 32 | 33 | var testLegend = L.control({ 34 | position: 'topright' 35 | }); 36 | testLegend.onAdd = function(map) { 37 | var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=tmp&PALETTE=tmp"; 38 | var div = L.DomUtil.create('div', 'info legend'); 39 | div.innerHTML += 40 | 'legend'; 41 | return div; 42 | }; 43 | testLegend.addTo(map); 44 | */ 45 | 46 | -------------------------------------------------------------------------------- /examples/js/example6.js: -------------------------------------------------------------------------------- 1 | var startDate = new Date(); 2 | startDate.setUTCHours(0, 0, 0, 0); 3 | 4 | var map = L.map('map', { 5 | zoom: 8, 6 | fullscreenControl: true, 7 | timeDimensionControl: true, 8 | timeDimensionControlOptions: { 9 | position: 'bottomleft', 10 | playerOptions: { 11 | transitionTime: 1000, 12 | } 13 | }, 14 | timeDimension: true, 15 | timeDimensionOptions: { 16 | timeInterval: "2020-09-25/2020-09-30", 17 | period: "PT1H", 18 | currentTime: Date.parse("2020-09-25T12:00:00Z") 19 | }, 20 | center: [39.3, 2.9] 21 | }); 22 | 23 | var sapoWMS = "https://thredds.socib.es/thredds/wms/operational_models/oceanographical/wave/model_run_aggregation/sapo_ib/sapo_ib_best.ncd"; 24 | var sapoHeightLayer = L.tileLayer.wms(sapoWMS, { 25 | layers: 'significant_wave_height', 26 | format: 'image/png', 27 | transparent: true, 28 | colorscalerange: '0,3', 29 | abovemaxcolor: "extend", 30 | belowmincolor: "extend", 31 | numcolorbands: 100, 32 | styles: 'areafill/scb_bugnylorrd' 33 | // styles: 'areafill/scb_greens' 34 | }); 35 | 36 | var sapoMeanDirectionLayer = L.nonTiledLayer.wms(sapoWMS, { 37 | layers: 'average_wave_direction', 38 | format: 'image/png', 39 | transparent: true, 40 | colorscalerange: '1,1', 41 | abovemaxcolor: "extend", 42 | belowmincolor: "extend", 43 | markerscale: 15, 44 | markerspacing: 12, 45 | markerclipping: true, 46 | styles: 'prettyvec/greyscale' 47 | }); 48 | 49 | var sapoPeakDirectionLayer = L.nonTiledLayer.wms(sapoWMS, { 50 | layers: 'direction_of_the_peak_of_the_spectrum', 51 | format: 'image/png', 52 | transparent: true, 53 | colorscalerange: '0,2', 54 | abovemaxcolor: "extend", 55 | belowmincolor: "extend", 56 | markerscale: 15, 57 | markerspacing: 12, 58 | markerclipping: true, 59 | styles: 'prettyvec/greyscale' 60 | }); 61 | 62 | var proxy = 'server/proxy.php'; 63 | var sapoHeightTimeLayer = L.timeDimension.layer.wms(sapoHeightLayer, { 64 | proxy: proxy, 65 | updateTimeDimension: false 66 | }); 67 | 68 | var sapoMeanDirectionTimeLayer = L.timeDimension.layer.wms(sapoMeanDirectionLayer, { 69 | proxy: proxy 70 | }); 71 | var sapoPeakDirectionTimeLayer = L.timeDimension.layer.wms(sapoPeakDirectionLayer, { 72 | proxy: proxy 73 | }); 74 | 75 | var sapoLegend = L.control({ 76 | position: 'bottomright' 77 | }); 78 | sapoLegend.onAdd = function(map) { 79 | var src = sapoWMS + "?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&LAYER=significant_wave_height&colorscalerange=0,3&PALETTE=scb_bugnylorrd&numcolorbands=100&transparent=TRUE"; 80 | var div = L.DomUtil.create('div', 'info legend'); 81 | div.innerHTML += 82 | 'legend'; 83 | return div; 84 | }; 85 | 86 | var sapoMeanDirectionLegend = L.control({ 87 | position: 'bottomright' 88 | }); 89 | sapoMeanDirectionLegend.onAdd = function(map) { 90 | var div = L.DomUtil.create('div', 'info legend'); 91 | div.innerHTML += ' mean direction'; 92 | return div; 93 | }; 94 | 95 | var sapoPeakDirectionLegend = L.control({ 96 | position: 'bottomright' 97 | }); 98 | sapoPeakDirectionLegend.onAdd = function(map) { 99 | var div = L.DomUtil.create('div', 'info legend'); 100 | div.innerHTML += ' peak direction'; 101 | return div; 102 | }; 103 | 104 | var overlayMaps = { 105 | "SAPO - significant wave height": sapoHeightTimeLayer, 106 | "SAPO - average wave direction": sapoMeanDirectionTimeLayer, 107 | "SAPO - direction of the peak": sapoPeakDirectionTimeLayer 108 | }; 109 | 110 | map.on('overlayadd', function(eventLayer) { 111 | if (eventLayer.name == 'SAPO - significant wave height') { 112 | sapoLegend.addTo(this); 113 | } else if (eventLayer.name == 'SAPO - average wave direction') { 114 | sapoMeanDirectionLegend.addTo(this); 115 | } else if (eventLayer.name == 'SAPO - direction of the peak') { 116 | sapoPeakDirectionLegend.addTo(this); 117 | } 118 | }); 119 | 120 | map.on('overlayremove', function(eventLayer) { 121 | if (eventLayer.name == 'SAPO - significant wave height') { 122 | map.removeControl(sapoLegend); 123 | } else if (eventLayer.name == 'SAPO - average wave direction') { 124 | map.removeControl(sapoMeanDirectionLegend); 125 | } else if (eventLayer.name == 'SAPO - direction of the peak') { 126 | map.removeControl(sapoPeakDirectionLegend); 127 | } 128 | }); 129 | 130 | var baseLayers = getCommonBaseLayers(map); // see baselayers.js 131 | L.control.layers(baseLayers, overlayMaps).addTo(map); 132 | 133 | sapoHeightTimeLayer.addTo(map); 134 | sapoPeakDirectionTimeLayer.addTo(map); 135 | sapoMeanDirectionTimeLayer.addTo(map); -------------------------------------------------------------------------------- /examples/js/example7.js: -------------------------------------------------------------------------------- 1 | var currentTime = new Date(); 2 | currentTime.setUTCHours(0, 0, 0, 0); 3 | var endDate = new Date(currentTime.getTime()); 4 | L.TimeDimension.Util.addTimeDuration(endDate, "P3D", true); 5 | 6 | var map = L.map('map', { 7 | zoom: 8, 8 | center: [39.4, 2.5], 9 | fullscreenControl: true, 10 | timeDimensionControl: true, 11 | timeDimension: true, 12 | timeDimensionOptions: { 13 | timeInterval: "P1M/" + endDate.toISOString(), 14 | period: "PT6H", 15 | currentTime: currentTime.getTime() 16 | }, 17 | timeDimensionControlOptions: { 18 | playerOptions: { 19 | loop: true, 20 | transitionTime: 1500, 21 | buffer: 10 22 | } 23 | } 24 | }); 25 | 26 | var wmopWMS = "https://thredds.socib.es/thredds/wms/operational_models/oceanographical/hydrodynamics/model_run_aggregation/wmop_surface/wmop_surface_best.ncd"; 27 | var wmopTemperatureLayer = L.tileLayer.wms(wmopWMS, { 28 | layers: 'temp', 29 | format: 'image/png', 30 | transparent: true, 31 | abovemaxcolor: "extend", 32 | belowmincolor: "extend", 33 | numcolorbands: 40, 34 | styles: 'boxfill/sst_36', 35 | zIndex: 1, 36 | }); 37 | 38 | var wmopTemperatureContourLayer = L.tileLayer.wms(wmopWMS, { 39 | layers: 'temp', 40 | format: 'image/png', 41 | transparent: true, 42 | numcontours: 11, 43 | styles: 'contour/sst_36', 44 | zIndex: 10, 45 | }); 46 | 47 | var wmopSalinityLayer = L.tileLayer.wms(wmopWMS, { 48 | layers: 'salt', 49 | format: 'image/png', 50 | transparent: true, 51 | abovemaxcolor: "extend", 52 | belowmincolor: "extend", 53 | numcolorbands: 40, 54 | styles: 'boxfill/mpl_rdbu_r' 55 | }); 56 | 57 | var wmopSalinityContourLayer = L.tileLayer.wms(wmopWMS, { 58 | layers: 'salt', 59 | format: 'image/png', 60 | transparent: true, 61 | numcontours: 11, 62 | styles: 'contour/sst_36' 63 | }); 64 | 65 | var wmopVelocityLayer = L.nonTiledLayer.wms(wmopWMS, { 66 | layers: 'sea_surface_velocity', 67 | format: 'image/png', 68 | transparent: true, 69 | colorscalerange: '0,3', 70 | abovemaxcolor: "extend", 71 | belowmincolor: "extend", 72 | markerscale: 10, 73 | markerspacing: 8, 74 | styles: 'prettyvec/greyscale' 75 | }); 76 | 77 | var proxy = 'server/proxy.php'; 78 | var wmopVelocityTimeLayer = L.timeDimension.layer.wms(wmopVelocityLayer, {proxy: proxy, updateTimeDimension: true}); 79 | var overlayMaps = { 80 | "WMOP - Velocity": wmopVelocityTimeLayer 81 | }; 82 | 83 | var baseLayers = getCommonBaseLayers(map); // see baselayers.js 84 | var layersControl = L.control.layers(baseLayers, overlayMaps); 85 | layersControl.addTo(map); 86 | 87 | wmopVelocityTimeLayer.addTo(map); 88 | 89 | var getLayerMinMax = function(layer, callback) { 90 | var url = wmopWMS + '?service=WMS&version=1.1.1&request=GetMetadata&item=minmax'; 91 | url = url + '&layers=' + layer.options.layers; 92 | url = url + '&srs=EPSG:4326'; 93 | var size = map.getSize(); 94 | url = url + '&BBox=' + map.getBounds().toBBoxString(); 95 | url = url + '&height=' + size.y; 96 | url = url + '&width=' + size.x; 97 | url = proxy + '?url=' + encodeURIComponent(url); 98 | 99 | var oReq = new XMLHttpRequest(); 100 | oReq.addEventListener("load", (function(xhr) { 101 | var response = xhr.currentTarget.response; 102 | var data = JSON.parse(response); 103 | var range = data.max - data.min; 104 | var min = Math.floor(data.min) - 1; 105 | var max = Math.floor(data.max + 2); 106 | layer.options.colorscalerange = min + "," + max; 107 | layer.wmsParams.colorscalerange = min + "," + max; 108 | if (callback !== undefined) { 109 | callback(); 110 | } 111 | })); 112 | oReq.open("GET", url); 113 | oReq.send(); 114 | }; 115 | 116 | var addTimeDimensionLayer = function(layer, name, addToMap){ 117 | var timeDimensionLayer = L.timeDimension.layer.wms(layer, {proxy: proxy}); 118 | layersControl.addOverlay(timeDimensionLayer, name); 119 | if (addToMap) 120 | timeDimensionLayer.addTo(map); 121 | } 122 | 123 | getLayerMinMax(wmopTemperatureLayer, function(){ 124 | addTimeDimensionLayer(wmopTemperatureLayer, 'WMOP - Temperature', true); 125 | wmopTemperatureContourLayer.wmsParams.colorscalerange = wmopTemperatureLayer.wmsParams.colorscalerange; 126 | wmopTemperatureContourLayer.options.colorscalerange = wmopTemperatureLayer.wmsParams.colorscalerange; 127 | addTimeDimensionLayer(wmopTemperatureContourLayer, 'WMOP - Temperature (Contour)', true); 128 | }); 129 | getLayerMinMax(wmopSalinityLayer, function(){ 130 | addTimeDimensionLayer(wmopSalinityLayer, 'WMOP - Salinity', false); 131 | wmopSalinityContourLayer.wmsParams.colorscalerange = wmopSalinityLayer.wmsParams.colorscalerange; 132 | wmopSalinityContourLayer.options.colorscalerange = wmopSalinityLayer.wmsParams.colorscalerange; 133 | addTimeDimensionLayer(wmopSalinityContourLayer, 'WMOP - Salinity (Contour)', false); 134 | }); 135 | -------------------------------------------------------------------------------- /examples/js/example8.js: -------------------------------------------------------------------------------- 1 | L.TimeDimension.Layer.DrifterDeployment = L.TimeDimension.Layer.GeoJson.extend({ 2 | 3 | initialize: function(layer, options) { 4 | layer = L.geoJson(); 5 | L.TimeDimension.Layer.GeoJson.prototype.initialize.call(this, layer, options); 6 | this._id_platform = this.options.id_platform; 7 | this._id_deployment = this.options.id_deployment; 8 | }, 9 | 10 | onAdd: function(map) { 11 | L.TimeDimension.Layer.prototype.onAdd.call(this, map); 12 | var proxy = "server/proxy-datadiscovery.php"; 13 | var url = "https://apps.socib.es/DataDiscovery/deployment-info?" + 14 | "id_platform=" + this._id_platform + "&id_deployment=" + this._id_deployment + 15 | "&sample=50"; 16 | url = proxy + '?url=' + encodeURIComponent(url); 17 | var oReq = new XMLHttpRequest(); 18 | oReq.addEventListener("load", (function(xhr) { 19 | var response = xhr.currentTarget.response; 20 | var data = JSON.parse(response); 21 | this._baseLayer = this._createLayer(data); 22 | this._onReadyBaseLayer(); 23 | }.bind(this))); 24 | oReq.open("GET", url); 25 | oReq.send(); 26 | }, 27 | 28 | _createLayer: function(featurecollection) { 29 | // lastPosition 30 | this._color = this._pickRandomColor(); 31 | 32 | 33 | this._icon = L.icon({ 34 | iconUrl: 'img/surface-drifter.png', 35 | iconSize: [20, 20], 36 | iconAnchor: [10, 20] 37 | }); 38 | 39 | var layer = L.geoJson(null, { 40 | pointToLayer: (function(feature, latLng) { 41 | if (feature.properties.hasOwnProperty('last')) { 42 | return new L.Marker(latLng, { 43 | icon: this._icon 44 | }); 45 | } 46 | return L.circleMarker(latLng, { 47 | fillColor: this._color, 48 | fillOpacity: 0.5, 49 | stroke: false, 50 | radius: 3 51 | }); 52 | }).bind(this), 53 | style: (function(feature) { 54 | return { 55 | "color": this._color, 56 | "weight": 2, 57 | "opacity": 1 58 | }; 59 | }).bind(this) 60 | }); 61 | if (!featurecollection.features) { 62 | return layer; 63 | } 64 | layer.addData(featurecollection.features[0]); 65 | for (var i = 1, l = featurecollection.features.length; i < l; i++) { 66 | var point = featurecollection.features[i]; 67 | // fix Point 68 | if (point.geometry.type == 'point') { 69 | point.geometry.type = 'Point'; 70 | } 71 | layer.addData(point); 72 | } 73 | // save last point 74 | this._lastPoint = featurecollection.features[featurecollection.features.length - 1]; 75 | return layer; 76 | }, 77 | 78 | _pickRandomColor: function() { 79 | var colors = ["#00aaff", "#ffaa00", "#ff00aa", "#ff0000", "#00ffaa", "#00ff00", "#0000ff", "#aa00ff", "#aaff00"]; 80 | var index = Math.floor(Math.random() * colors.length); 81 | return colors[index]; 82 | }, 83 | 84 | _addDeploymentTrajectory: function(layer, trajectory_feature) { 85 | // remove the old one 86 | if (this._deploymentTrajectory) { 87 | layer.removeLayer(this._deploymentTrajectory); 88 | } 89 | var getStyle = (function(feature) { 90 | return { 91 | "color": this._color, 92 | "weight": 2, 93 | "opacity": 1 94 | }; 95 | }).bind(this); 96 | var deploymentTrajectory = L.geoJson(trajectory_feature, { 97 | style: getStyle 98 | }); 99 | // deploymentTrajectory.on('click', deployment.popupFunction.bind(this, deployment, undefined)); 100 | deploymentTrajectory.addTo(layer); 101 | // save for later 102 | this._deploymentTrajectory = deploymentTrajectory; 103 | }, 104 | 105 | _addDeploymenPoint: function(layer, point, isLastPoint) { 106 | var deploymentPoint = L.geoJson(point, { 107 | pointToLayer: (function(feature, latLng) { 108 | if (isLastPoint) { 109 | return new L.Marker(latLng, { 110 | icon: this._icon 111 | }); 112 | } else { 113 | return L.circleMarker(latLng, { 114 | fillColor: this._color, 115 | fillOpacity: 0.5, 116 | stroke: false, 117 | radius: 3 118 | }); 119 | } 120 | }).bind(this) 121 | }); 122 | // deploymentPoint.on('click', deployment.popupFunction.bind(this, deployment, point)); 123 | deploymentPoint.addTo(layer); 124 | if (isLastPoint) 125 | this._lastPoint = deploymentPoint; 126 | } 127 | 128 | 129 | }); 130 | 131 | L.timeDimension.layer.drifterDeployment = function(options) { 132 | return new L.TimeDimension.Layer.DrifterDeployment(null, options); 133 | }; 134 | 135 | 136 | 137 | var map = L.map('map', { 138 | zoom: 10, 139 | fullscreenControl: true, 140 | timeDimension: true, 141 | timeDimensionOptions: { 142 | timeInterval: "2014-09-30/2014-10-30", 143 | period: "PT1H", 144 | currentTime: Date.parse("2014-09-30T09:00:00Z") 145 | }, 146 | timeDimensionControl: true, 147 | timeDimensionControlOptions: { 148 | autoPlay: true, 149 | playerOptions: { 150 | buffer: 10, 151 | transitionTime: 500, 152 | loop: true, 153 | } 154 | }, 155 | center: [38.705, 1.15], 156 | }); 157 | 158 | var Esri_WorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { 159 | attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, ' + 160 | 'AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community' 161 | }); 162 | 163 | var baseLayers = { 164 | "ESRI Satellite": Esri_WorldImagery, 165 | }; 166 | 167 | L.control.layers(baseLayers, {}).addTo(map); 168 | Esri_WorldImagery.addTo(map); 169 | 170 | var testWMS = "https://thredds.socib.es/thredds/wms/observational/hf_radar/hf_radar_ibiza-scb_codarssproc001_L1_agg/hf_radar_ibiza-scb_codarssproc001_L1_agg_best.ncd" 171 | var testLayer = L.nonTiledLayer.wms(testWMS, { 172 | layers: 'sea_water_velocity', 173 | format: 'image/png', 174 | transparent: true, 175 | styles: 'prettyvec/mpl_reds', 176 | markerscale: 15, 177 | markerspacing: 6, 178 | numcolorbands: 10, 179 | abovemaxcolor: "extend", 180 | belowmincolor: "extend", 181 | colorscalerange: "0,0.4", 182 | attribution: 'SOCIB HF RADAR' 183 | }); 184 | var proxy = 'server/proxy.php'; 185 | var testTimeLayer = L.timeDimension.layer.wms(testLayer, { 186 | proxy: proxy, 187 | updateTimeDimension: true, 188 | }); 189 | testTimeLayer.addTo(map); 190 | 191 | var testLegend = L.control({ 192 | position: 'topright' 193 | }); 194 | testLegend.onAdd = function(map) { 195 | var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=sea_water_velocity&PALETTE=mpl_reds&numcolorbands=10&colorscalerange=0,0.4"; 196 | var div = L.DomUtil.create('div', 'info legend'); 197 | div.innerHTML += 198 | 'legend'; 199 | return div; 200 | }; 201 | testLegend.addTo(map); 202 | 203 | var drifters = [ 204 | [243, 416], 205 | [244, 417], 206 | [245, 418], 207 | [246, 419], 208 | [248, 421], 209 | [249, 422], 210 | [162, 423], 211 | [161, 424], 212 | [250, 425], 213 | [253, 427], 214 | [252, 428] 215 | ]; 216 | 217 | for (var i = 0, l = drifters.length; i < l; i++) { 218 | var drifterLayer = L.timeDimension.layer.drifterDeployment({ 219 | id_platform: drifters[i][0], 220 | id_deployment: drifters[i][1], 221 | duration: "P1D", 222 | addlastPoint: true 223 | }); 224 | drifterLayer.addTo(map); 225 | } -------------------------------------------------------------------------------- /examples/js/example9.js: -------------------------------------------------------------------------------- 1 | var startDate = new Date(); 2 | startDate.setUTCHours(0, 0, 0, 0); 3 | 4 | var map = L.map('map', { 5 | zoom: 12, 6 | fullscreenControl: true, 7 | center: [39.3, 4] 8 | }); 9 | 10 | // start of TimeDimension manual instantiation 11 | var timeDimension = new L.TimeDimension({ 12 | period: "PT5M", 13 | }); 14 | // helper to share the timeDimension object between all layers 15 | map.timeDimension = timeDimension; 16 | // otherwise you have to set the 'timeDimension' option on all layers. 17 | 18 | var player = new L.TimeDimension.Player({ 19 | transitionTime: 100, 20 | loop: false, 21 | startOver:true 22 | }, timeDimension); 23 | 24 | var timeDimensionControlOptions = { 25 | player: player, 26 | timeDimension: timeDimension, 27 | position: 'bottomleft', 28 | autoPlay: true, 29 | minSpeed: 1, 30 | speedStep: 0.5, 31 | maxSpeed: 15, 32 | timeSliderDragUpdate: true 33 | }; 34 | 35 | var timeDimensionControl = new L.Control.TimeDimension(timeDimensionControlOptions); 36 | map.addControl(timeDimensionControl); 37 | 38 | var icon = L.icon({ 39 | iconUrl: 'img/running.png', 40 | iconSize: [22, 22], 41 | iconAnchor: [5, 25] 42 | }); 43 | 44 | var customLayer = L.geoJson(null, { 45 | pointToLayer: function (feature, latLng) { 46 | if (feature.properties.hasOwnProperty('last')) { 47 | return new L.Marker(latLng, { 48 | icon: icon 49 | }); 50 | } 51 | return L.circleMarker(latLng); 52 | } 53 | }); 54 | 55 | var gpxLayer = omnivore.gpx('data/running_mallorca.gpx', null, customLayer).on('ready', function() { 56 | map.fitBounds(gpxLayer.getBounds(), { 57 | paddingBottomRight: [40, 40] 58 | }); 59 | }); 60 | 61 | var gpxTimeLayer = L.timeDimension.layer.geoJson(gpxLayer, { 62 | updateTimeDimension: true, 63 | addlastPoint: true, 64 | waitForReady: true 65 | }); 66 | 67 | var kmlLayer = omnivore.kml('data/easy_currents_track.kml'); 68 | var kmlTimeLayer = L.timeDimension.layer.geoJson(kmlLayer, { 69 | updateTimeDimension: true, 70 | addlastPoint: true, 71 | waitForReady: true 72 | }); 73 | 74 | 75 | var overlayMaps = { 76 | "GPX Layer": gpxTimeLayer, 77 | "KML Layer": kmlTimeLayer 78 | }; 79 | var baseLayers = getCommonBaseLayers(map); // see baselayers.js 80 | L.control.layers(baseLayers, overlayMaps).addTo(map); 81 | gpxTimeLayer.addTo(map); -------------------------------------------------------------------------------- /examples/js/extras/leaflet.timedimension.circlelabelmarker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.TimeDimension.CircleLabelMarker: circleMarker + divIcon containing 3 | * the numerical value of the baseLayer (from a THREDDS server). 4 | */ 5 | 6 | L.TimeDimension.Layer.CircleLabelMarker = L.TimeDimension.Layer.extend({ 7 | 8 | initialize: function(layer, options) { 9 | L.TimeDimension.Layer.prototype.initialize.call(this, layer, options); 10 | this._serieId = this.options.serieId; 11 | this._dataLayer = this.options.dataLayer; 12 | this._labelMarker = null; 13 | this._position = layer.getLatLng(); 14 | this._proxy = this.options.proxy || null; 15 | this._data = []; 16 | }, 17 | 18 | addTo: function(map) { 19 | map.addLayer(this); 20 | map.addLayer(this._baseLayer); 21 | var time = this._timeDimension.getCurrentTime(); 22 | if (!this._existsValueForTime(time)){ 23 | this._loadDataForTime(time, (function() { 24 | this._update(); 25 | }).bind(this)); 26 | }else{ 27 | this._update(); 28 | } 29 | 30 | return this; 31 | }, 32 | 33 | eachLayer: function(method, context) { 34 | if (this._labelMarker) { 35 | method.call(context, this._labelMarker); 36 | } 37 | return L.TimeDimension.Layer.prototype.eachLayer.call(this, method, context); 38 | }, 39 | 40 | _onNewTimeLoading: function(ev) { 41 | if (this._existsValueForTime(ev.time)) { 42 | this.fire('timeload', { 43 | time: ev.time 44 | }); 45 | } else { 46 | this._loadDataForTime(ev.time, (function() { 47 | this.fire('timeload', { 48 | time: ev.time 49 | }); 50 | }).bind(this)); 51 | } 52 | return; 53 | }, 54 | 55 | isReady: function(time) { 56 | return this._existsValueForTime(time); 57 | }, 58 | 59 | _update: function() { 60 | if (!this._map) 61 | return; 62 | var time = this._timeDimension.getCurrentTime(); 63 | var value = this._getValueForTime(time); 64 | if (value && !isNaN(value)) { 65 | value = value.toFixed(2); 66 | }else{ 67 | value = ''; 68 | } 69 | if (this._labelMarker) { 70 | this._map.removeLayer(this._labelMarker); 71 | delete this._labelMarker; 72 | } 73 | var icon = L.divIcon({ 74 | className: 'marker-label-icon', 75 | html: value, 76 | iconSize: [40, 15], 77 | iconAnchor: [-8, 12], 78 | }); 79 | this._labelMarker = L.marker(this._position, { 80 | icon: icon 81 | }); 82 | this._labelMarker.addTo(this._map); 83 | return true; 84 | }, 85 | 86 | _getValueForTime: function(time) { 87 | if (this._dataLayer && this._dataLayer.chart && this._serieId) { 88 | var data = this._dataLayer.chart.get(this._serieId).options.data; 89 | for (var i = 0, l = data.length; i < l; i++) { 90 | if (data[i][0] == time) { 91 | return data[i][1]; 92 | } else if (data[i][0] < time) { 93 | break; 94 | } 95 | } 96 | } 97 | for (var i = 0, l = this._data.length; i < l; i++) { 98 | if (this._data[i][0] == time) { 99 | return this._data[i][1]; 100 | } 101 | } 102 | return null; 103 | }, 104 | 105 | _existsValueForTime: function(time) { 106 | if (this._dataLayer && this._dataLayer.chart && this._serieId) { 107 | var data = this._dataLayer.chart.get(this._serieId).options.data; 108 | for (var i = 0, l = data.length; i < l; i++) { 109 | if (data[i][0] == time) { 110 | return true; 111 | } else if (data[i][0] < time) { 112 | break; 113 | } 114 | } 115 | } 116 | for (var i = 0, l = this._data.length; i < l; i++) { 117 | if (this._data[i][0] == time) { 118 | return true; 119 | } 120 | } 121 | return false; 122 | }, 123 | 124 | _loadDataForTime: function(time, callback) { 125 | if (!this._dataLayer || !this._map || !this._map.getBounds().contains(this._position)){ 126 | if (callback !== undefined) { 127 | callback(null); 128 | } 129 | return; 130 | } 131 | var point = this._map.latLngToContainerPoint(this._position); 132 | var url = this._dataLayer.getURL() + '?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetFeatureInfo&SRS=EPSG:4326'; 133 | url = url + '&LAYER=' + this._dataLayer.wmsParams.layers; 134 | url = url + '&QUERY_LAYERS=' + this._dataLayer.wmsParams.layers; 135 | url = url + '&X=' + point.x + '&Y=' + point.y + '&I=' + point.x + '&J=' + point.y; 136 | var size = this._map.getSize(); 137 | url = url + '&BBox=' + this._map.getBounds().toBBoxString(); 138 | url = url + '&WIDTH=' + size.x + '&HEIGHT=' + size.y; 139 | url = url + '&INFO_FORMAT=text/xml'; 140 | var url_without_time = url; 141 | url = url + '&TIME=' + new Date(time).toISOString(); 142 | 143 | if (this._proxy) url = this._proxy + '?url=' + encodeURIComponent(url); 144 | 145 | var oReq = new XMLHttpRequest(); 146 | oReq.addEventListener("load", (function(xhr) { 147 | var data = xhr.currentTarget.responseXML; 148 | var result = null; 149 | data.querySelectorAll('FeatureInfo').forEach(function(fi) { 150 | var this_data = fi.querySelector('value').textContent; 151 | try { 152 | this_data = parseFloat(this_data); 153 | } catch (e) { 154 | this_data = null; 155 | } 156 | result = this_data; 157 | }); 158 | this._data.push([time, result]); 159 | if (callback !== undefined) { 160 | callback(result); 161 | } 162 | }).bind(this)); 163 | oReq.overrideMimeType('application/xml'); 164 | oReq.open("GET", url); 165 | oReq.send(); 166 | } 167 | }); 168 | 169 | L.timeDimension.layer.circleLabelMarker = function(layer, options) { 170 | return new L.TimeDimension.Layer.CircleLabelMarker(layer, options); 171 | }; -------------------------------------------------------------------------------- /examples/js/extras/leaflet.timedimension.velocitylayer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.TimeDimension.Layer.VelocityLayer: TimeDimension for VelocityLayer 3 | */ 4 | Date.prototype.format = function(mask, utc) { 5 | return dateFormat(this, mask, utc); 6 | }; 7 | 8 | if (!Array.prototype.find) { 9 | Array.prototype.find = function(predicate) { 10 | if (this === null) { 11 | throw new TypeError("Array.prototype.find called on null or undefined"); 12 | } 13 | if (typeof predicate !== "function") { 14 | throw new TypeError("predicate must be a function"); 15 | } 16 | var list = Object(this); 17 | var length = list.length >>> 0; 18 | var thisArg = arguments[1]; 19 | var value; 20 | 21 | for (var i = 0; i < length; i++) { 22 | value = list[i]; 23 | if (predicate.call(thisArg, value, i, list)) { 24 | return value; 25 | } 26 | } 27 | return undefined; 28 | }; 29 | } 30 | 31 | L.CustomVelocityLayer = L.VelocityLayer.extend({ 32 | options: { 33 | displayValues: true, 34 | displayOptions: { 35 | velocityType: 'GBR Water', 36 | displayPosition: 'bottomleft', 37 | displayEmptyString: 'No currents data' 38 | }, 39 | data: [], 40 | maxVelocity: 0.3, 41 | velocityScale: 0.1 // arbitrary default 0.005 42 | }, 43 | 44 | initialize: function(options) { 45 | L.setOptions(this, options); 46 | L.VelocityLayer.prototype.initialize.call(this, this.options); 47 | } 48 | }); 49 | 50 | L.customVelocityLayer = function(options) { 51 | return new L.CustomVelocityLayer(options); 52 | }; 53 | 54 | L.TimeDimension.Layer.VelocityLayer = L.TimeDimension.Layer.extend({ 55 | 56 | initialize: function(options) { 57 | var layer = new L.customVelocityLayer( 58 | options.velocityLayerOptions || {} 59 | ); 60 | L.TimeDimension.Layer.prototype.initialize.call(this, layer, options); 61 | this._currentLoadedTime = 0; 62 | this._currentTimeData = []; 63 | this._baseURL = this.options.baseURL || null; 64 | }, 65 | 66 | onAdd: function(map) { 67 | L.TimeDimension.Layer.prototype.onAdd.call(this, map); 68 | if (this._timeDimension) { 69 | this._getDataForTime(this._timeDimension.getCurrentTime()); 70 | } 71 | }, 72 | 73 | _onNewTimeLoading: function(ev) { 74 | this._getDataForTime(ev.time); 75 | return; 76 | }, 77 | 78 | isReady: function(time) { 79 | return (this._currentLoadedTime == time); 80 | }, 81 | 82 | _update: function() { 83 | if (this._currentTimeData && this._currentTimeData.length > 0) { 84 | this._map.addLayer(this._baseLayer); 85 | this._baseLayer.setData(this._currentTimeData); 86 | } else { 87 | this._map.removeLayer(this._baseLayer); 88 | } 89 | 90 | return true; 91 | }, 92 | 93 | _getDataForTime: function(time) { 94 | if (!this._baseURL || !this._map) { 95 | return; 96 | } 97 | var url = this._constructQuery(time); 98 | var oReq = new XMLHttpRequest(); 99 | oReq.addEventListener("load", (function(xhr) { 100 | var data = []; 101 | try { 102 | var response = xhr.currentTarget.response; 103 | data = JSON.parse(response); 104 | } catch(e) { 105 | console.log("Error parsing API response", e); 106 | } 107 | delete this._currentTimeData; 108 | this._currentTimeData = this._processLoadedData(data); 109 | this._currentLoadedTime = time; 110 | if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) { 111 | this._update(); 112 | } 113 | this.fire('timeload', { 114 | time: time 115 | }); 116 | }).bind(this)); 117 | 118 | oReq.open("GET", url); 119 | oReq.send(); 120 | }, 121 | 122 | _constructQuery: function(time) { 123 | var time = new Date(time); 124 | var timeParams = "&time=" + 125 | time.format('isoDateTime'); 126 | var url = this._baseURL + timeParams; 127 | return url; 128 | }, 129 | 130 | _processLoadedData: function(data) { 131 | return data; 132 | } 133 | }); 134 | 135 | L.timeDimension.layer.velocityLayer = function(options) { 136 | return new L.TimeDimension.Layer.VelocityLayer(options); 137 | }; 138 | -------------------------------------------------------------------------------- /examples/server/proxy-datadiscovery.php: -------------------------------------------------------------------------------- 1 | = 0; $i--) { 9 | $line = $http_response_header[$i]; 10 | if (substr_compare($line, 'Content-Type', 0, 12, true) == 0) { 11 | $content_type = $line; 12 | break; 13 | } 14 | } 15 | header($content_type); 16 | echo $result; 17 | } else{ 18 | header('HTTP/1.0 400 Bad Request'); 19 | echo 'Request not valid'; 20 | } 21 | ?> 22 | -------------------------------------------------------------------------------- /examples/server/proxy.php: -------------------------------------------------------------------------------- 1 | = 0){ 6 | $result = file_get_contents($url); 7 | $nlines = count($http_response_header); 8 | for ($i = $nlines-1; $i >= 0; $i--) { 9 | $line = $http_response_header[$i]; 10 | if (substr_compare($line, 'Content-Type', 0, 12, true) == 0) { 11 | $content_type = $line; 12 | break; 13 | } 14 | } 15 | header($content_type); 16 | echo $result; 17 | } else{ 18 | header('HTTP/1.0 400 Bad Request'); 19 | echo 'Request not valid'; 20 | } 21 | ?> 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet-timedimension", 3 | "version": "1.1.1", 4 | "description": "Add time dimension capabilities on a Leaflet map", 5 | "keywords": [ 6 | "gis", 7 | "map", 8 | "timedimension", 9 | "leaflet" 10 | ], 11 | "homepage": "https://apps.socib.es/Leaflet.TimeDimension/", 12 | "bugs": { 13 | "url": "https://github.com/socib/Leaflet.TimeDimension/issues" 14 | }, 15 | "license": "MIT", 16 | "author": { 17 | "name": "Biel Frontera (ICTS SOCIB)", 18 | "email": "datacenter@socib.es", 19 | "url": "https://www.socib.es/" 20 | }, 21 | "contributors": [ 22 | { 23 | "name": "Sylvain Marcadal (Ram)", 24 | "email": "sylvain@marcadal.me", 25 | "url": "https://github.com/ram-one" 26 | } 27 | ], 28 | "repository": { 29 | "type": "git", 30 | "url": "git://github.com/socib/Leaflet.TimeDimension.git" 31 | }, 32 | "dependencies": { 33 | "iso8601-js-period": "^0.2.1", 34 | "leaflet": "~0.7.4 || ~1" 35 | }, 36 | "devDependencies": { 37 | "grunt": "~1", 38 | "grunt-contrib-clean": "~0.7.0", 39 | "grunt-contrib-concat": "~1", 40 | "grunt-contrib-cssmin": "~2.2", 41 | "grunt-contrib-jshint": "~1.1", 42 | "grunt-contrib-uglify": "~3.1.0", 43 | "grunt-contrib-watch": "~1", 44 | "grunt-remove-logging": "~0.2.0" 45 | }, 46 | "main": "dist/leaflet.timedimension.src.js", 47 | "style": "dist/leaflet.timedimension.control.css" 48 | } 49 | -------------------------------------------------------------------------------- /src/leaflet.timedimension.control.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Glyphicons Halflings'; 3 | src: url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.eot'); 4 | src: url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.woff') format('woff'), url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); 5 | } 6 | .leaflet-bar-timecontrol{ 7 | background-color: #fff; 8 | color: black; 9 | } 10 | .leaflet-bar-timecontrol * { 11 | box-sizing: border-box; 12 | } 13 | .leaflet-bar-timecontrol .leaflet-control-timecontrol { 14 | float: left; 15 | height: 26px; 16 | line-height: 26px; 17 | border: solid #a5a5a5; 18 | background-color: #fff; 19 | border-width: 0 1px 0 0; 20 | } 21 | .leaflet-bar-timecontrol .leaflet-control-timecontrol:first-child { 22 | border-radius: 4px 0 0 4px; 23 | } 24 | .leaflet-bar-timecontrol .leaflet-control-timecontrol:last-child { 25 | border-radius: 0 4px 4px 0; 26 | } 27 | .leaflet-bar-timecontrol .leaflet-control-timecontrol:before { 28 | font-family: "Glyphicons Halflings"; 29 | display: block; 30 | } 31 | .leaflet-bar-timecontrol .timecontrol-slider { 32 | position: relative; 33 | width: auto; 34 | cursor: auto; 35 | } 36 | .leaflet-bar-timecontrol a.timecontrol-date, 37 | .leaflet-bar-timecontrol a.timecontrol-date:hover { 38 | position: relative; 39 | min-width: 150px; 40 | width: auto; 41 | padding: 0 10px 0 20px; 42 | white-space: nowrap; 43 | } 44 | .leaflet-bar-timecontrol a.timecontrol-date.utc, 45 | .leaflet-bar-timecontrol a.timecontrol-date.utc:hover { 46 | min-width: 185px; 47 | } 48 | .leaflet-bar-timecontrol a.timecontrol-date.loading, 49 | .leaflet-bar-timecontrol a.timecontrol-date.loading:hover { 50 | background-color: #ffefa4; 51 | } 52 | .leaflet-bar-timecontrol .timecontrol-dateslider .slider { 53 | width: 200px; 54 | } 55 | .leaflet-bar-timecontrol .timecontrol-speed { 56 | white-space: nowrap; 57 | cursor: auto; 58 | } 59 | .leaflet-bar-timecontrol .timecontrol-speed .slider { 60 | width: 55px; 61 | display: inline-block; 62 | } 63 | .leaflet-bar-timecontrol .timecontrol-speed .speed { 64 | width: 55px; 65 | display: inline-block; 66 | float: left; 67 | text-align: right; 68 | } 69 | .leaflet-bar-timecontrol .timecontrol-play, 70 | .leaflet-bar-timecontrol .timecontrol-play:hover { 71 | position: relative; 72 | } 73 | .leaflet-bar-timecontrol .timecontrol-play span { 74 | font-size: 10px; 75 | } 76 | .leaflet-bar-timecontrol a.timecontrol-play.loading { 77 | background-color: #ffefa4; 78 | } 79 | 80 | /** 81 | * Slider/Knobs styles 82 | */ 83 | 84 | .timecontrol-slider .slider { 85 | position: relative; 86 | height: 12px; 87 | margin: 6px; 88 | border: 1px solid #a5a5a5; 89 | cursor: pointer; 90 | } 91 | .timecontrol-slider .slider.has-limits { 92 | margin-left: 15px; 93 | margin-right: 15px; 94 | background-color: #ddd; 95 | } 96 | .timecontrol-slider .slider.has-limits .range { 97 | position: absolute; 98 | height: 10px; 99 | background-color: #fff; 100 | /*opacity: 0.5;*/ 101 | } 102 | .timecontrol-slider .knob { 103 | position: absolute; 104 | width: 8px; 105 | height: 22px; 106 | background-color: #ddd; 107 | border-radius: 2px; 108 | border: 1px solid #a5a5a5; 109 | /*use margins because on ie,leaflet will use top/left for positionning*/ 110 | margin-top: -6px; 111 | margin-left: -4px; 112 | cursor: ew-resize; 113 | cursor: -webkit-grab; 114 | cursor: -moz-grab; 115 | } 116 | .timecontrol-slider .knob:after { 117 | /** Big transparent block on top of the knob for easier grabbing on touch device*/ 118 | content: ' '; 119 | display: block; 120 | position: absolute; 121 | width: 20px; 122 | top:-5px; 123 | height: 32px; 124 | left: -7px; 125 | /* opacity: 0.5; 126 | background: red;*/ 127 | 128 | } 129 | .timecontrol-slider .knob.upper, 130 | .timecontrol-slider .knob.lower { 131 | width: 11px; 132 | height: 20px; 133 | border: none; 134 | background-color: transparent; 135 | } 136 | .timecontrol-slider .knob.upper { 137 | margin-top: -5px; 138 | margin-left: -1px; 139 | } 140 | .timecontrol-slider .knob.lower { 141 | margin-top: -5px; 142 | margin-left: -10px; 143 | } 144 | .timecontrol-slider .knob.lower:after { 145 | right:0px; 146 | left: initial; 147 | } 148 | .timecontrol-slider .knob.upper:after { 149 | left:0px; 150 | } 151 | .timecontrol-slider .knob.upper:before, 152 | .timecontrol-slider .knob.lower:before { 153 | display: block; 154 | content: ''; 155 | position: relative; 156 | top: 2px; 157 | width: 0; 158 | height: 0; 159 | border-style: solid; 160 | } 161 | .timecontrol-slider .knob.upper:before { 162 | border-width: 16px 0 0 10px; 163 | border-color: transparent transparent transparent #a5a5a5; 164 | } 165 | .timecontrol-slider .knob.lower:before { 166 | border-width: 0 0 16px 10px; 167 | border-color: transparent transparent #a5a5a5; 168 | } 169 | 170 | .timecontrol-slider .slider.dragging, 171 | .timecontrol-slider .dragging .knob, 172 | .timecontrol-slider .knob.leaflet-drag-target { 173 | cursor: ew-resize; 174 | cursor: grabbing; 175 | cursor: -webkit-grabbing; 176 | cursor: -moz-grabbing; 177 | } 178 | 179 | /** 180 | * Icons definitions 181 | */ 182 | 183 | @-webkit-keyframes icon-rotation { 184 | from { 185 | -webkit-transform: rotate(0deg); 186 | transform: rotate(0deg); 187 | } 188 | to { 189 | -webkit-transform: rotate(360deg); 190 | transform: rotate(360deg); 191 | } 192 | } 193 | @keyframes icon-rotation { 194 | from { 195 | -webkit-transform: rotate(0deg); 196 | transform: rotate(0deg); 197 | } 198 | to { 199 | -webkit-transform: rotate(360deg); 200 | transform: rotate(360deg); 201 | } 202 | } 203 | .timecontrol-loop.looped, 204 | .timecontrol-loop.looped:hover { 205 | background-color: #ddd; 206 | color: #094F8E; 207 | } 208 | 209 | .timecontrol-backward:before, 210 | .timecontrol-forward:before, 211 | .timecontrol-stop:before, 212 | .timecontrol-play:before, 213 | .timecontrol-loop:before { 214 | width: 100%; 215 | text-align: center; 216 | } 217 | 218 | .timecontrol-play:before { 219 | position: absolute; 220 | content: "\e072"; 221 | } 222 | /*.timecontrol-play.play:before { 223 | content: "\e072"; 224 | }*/ 225 | .timecontrol-play.reverse:before { 226 | content: "\e072"; 227 | -ms-transform: scaleX(-1); 228 | -webkit-transform: scaleX(-1); 229 | transform: scaleX(-1); 230 | } 231 | .timecontrol-play.pause:before { 232 | content: "\e073"; 233 | } 234 | .timecontrol-play.reverse.pause:before { 235 | -ms-transform: none; 236 | -webkit-transform: none; 237 | transform: none; 238 | } 239 | 240 | a.timecontrol-play.loading:before { 241 | content: "\e031"; 242 | opacity: 0.2; 243 | -webkit-animation: icon-rotation 6s infinite linear; 244 | animation: icon-rotation 6s infinite linear; 245 | } 246 | .timecontrol-date.loading:before { 247 | content: "\e031"; 248 | left: 5px; 249 | position: absolute; 250 | -webkit-animation: icon-rotation 6s infinite linear; 251 | animation: icon-rotation 6s infinite linear; 252 | } 253 | .timecontrol-speed:before { 254 | content: "\e141"; 255 | position: absolute; 256 | left: 7px; 257 | } 258 | .timecontrol-stop:before { 259 | content: "\e074"; 260 | } 261 | .timecontrol-forward:before { 262 | content: "\e075"; 263 | } 264 | .timecontrol-backward:before { 265 | content: "\e071"; 266 | } 267 | .timecontrol-loop:before { 268 | content: "\e030"; 269 | } 270 | 271 | @media (max-width: 767px){ 272 | .leaflet-bar-timecontrol .timecontrol-date, 273 | .leaflet-bar-timecontrol .timecontrol-slider{ 274 | clear: both; 275 | float: none; 276 | border-right: none; 277 | } 278 | } 279 | .leaflet-touch .leaflet-bar-timecontrol .leaflet-control-timecontrol{ 280 | height: 30px; 281 | line-height: 30px; 282 | } 283 | .leaflet-touch .timecontrol-slider .slider{ 284 | margin-top: 10px; 285 | } -------------------------------------------------------------------------------- /src/leaflet.timedimension.layer.geojson.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.TimeDimension.Layer.GeoJson: 3 | */ 4 | 5 | L.TimeDimension.Layer.GeoJson = L.TimeDimension.Layer.extend({ 6 | 7 | initialize: function(layer, options) { 8 | L.TimeDimension.Layer.prototype.initialize.call(this, layer, options); 9 | this._updateTimeDimension = this.options.updateTimeDimension || false; 10 | this._updateTimeDimensionMode = this.options.updateTimeDimensionMode || 'extremes'; // 'union', 'replace' or extremes 11 | this._duration = this.options.duration || null; 12 | this._addlastPoint = this.options.addlastPoint || false; 13 | this._waitForReady = this.options.waitForReady || false; 14 | this._updateCurrentTime = this.options.updateCurrentTime || this._updateTimeDimension; 15 | this._availableTimes = []; 16 | this._loaded = false; 17 | if (this._baseLayer.getLayers().length == 0) { 18 | if (this._waitForReady){ 19 | this._baseLayer.on("ready", this._onReadyBaseLayer, this); 20 | }else{ 21 | this._loaded = true; 22 | } 23 | } else { 24 | this._loaded = true; 25 | this._setAvailableTimes(); 26 | } 27 | // reload available times if data is added to the base layer 28 | this._baseLayer.on('layeradd', (function () { 29 | if (this._loaded) { 30 | this._setAvailableTimes(); 31 | } 32 | }).bind(this)); 33 | }, 34 | 35 | onAdd: function(map) { 36 | L.TimeDimension.Layer.prototype.onAdd.call(this, map); 37 | if (this._loaded) { 38 | this._setAvailableTimes(); 39 | } 40 | }, 41 | 42 | eachLayer: function(method, context) { 43 | if (this._currentLayer) { 44 | method.call(context, this._currentLayer); 45 | } 46 | return L.TimeDimension.Layer.prototype.eachLayer.call(this, method, context); 47 | }, 48 | 49 | isReady: function(time) { 50 | return this._loaded; 51 | }, 52 | 53 | _update: function() { 54 | if (!this._map) 55 | return; 56 | if (!this._loaded) { 57 | return; 58 | } 59 | 60 | var time = this._timeDimension.getCurrentTime(); 61 | 62 | var maxTime = this._timeDimension.getCurrentTime(), 63 | minTime = 0; 64 | if (this._duration) { 65 | var date = new Date(maxTime); 66 | L.TimeDimension.Util.subtractTimeDuration(date, this._duration, true); 67 | minTime = date.getTime(); 68 | } 69 | 70 | // new coordinates: 71 | var layer = L.geoJson(null, this._baseLayer.options); 72 | var layers = this._baseLayer.getLayers(); 73 | for (var i = 0, l = layers.length; i < l; i++) { 74 | var feature = this._getFeatureBetweenDates(layers[i].feature, minTime, maxTime); 75 | if (feature) { 76 | layer.addData(feature); 77 | if (this._addlastPoint && feature.geometry.type == "LineString") { 78 | if (feature.geometry.coordinates.length > 0) { 79 | var properties = feature.properties; 80 | properties.last = true; 81 | layer.addData({ 82 | type: 'Feature', 83 | properties: properties, 84 | geometry: { 85 | type: 'Point', 86 | coordinates: feature.geometry.coordinates[feature.geometry.coordinates.length - 1] 87 | } 88 | }); 89 | } 90 | } 91 | } 92 | } 93 | 94 | if (this._currentLayer) { 95 | this._map.removeLayer(this._currentLayer); 96 | } 97 | if (layer.getLayers().length) { 98 | layer.addTo(this._map); 99 | this._currentLayer = layer; 100 | } 101 | }, 102 | 103 | _setAvailableTimes: function() { 104 | var times = []; 105 | var layers = this._baseLayer.getLayers(); 106 | for (var i = 0, l = layers.length; i < l; i++) { 107 | if (layers[i].feature) { 108 | var featureTimes = this._getFeatureTimes(layers[i].feature); 109 | for (var j = 0, m = featureTimes.length; j < m; j++) { 110 | times.push(featureTimes[j]); 111 | } 112 | } 113 | } 114 | this._availableTimes = L.TimeDimension.Util.sort_and_deduplicate(times); 115 | this._updateCurrentTime = this._updateCurrentTime || (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0); 116 | if (this._timeDimension && (this._updateTimeDimension || this._timeDimension.getAvailableTimes().length == 0)) { 117 | this._timeDimension.setAvailableTimes(this._availableTimes, this._updateTimeDimensionMode); 118 | } 119 | if (this._updateCurrentTime && this._timeDimension && this._availableTimes.length) { 120 | this._timeDimension.setCurrentTime(this._availableTimes[0]); 121 | } 122 | }, 123 | 124 | _getFeatureTimes: function(feature) { 125 | if (!feature.featureTimes) { 126 | if (!feature.properties) { 127 | feature.featureTimes = []; 128 | } else if (feature.properties.hasOwnProperty('coordTimes')) { 129 | feature.featureTimes = feature.properties.coordTimes; 130 | } else if (feature.properties.hasOwnProperty('times')) { 131 | feature.featureTimes = feature.properties.times; 132 | } else if (feature.properties.hasOwnProperty('linestringTimestamps')) { 133 | feature.featureTimes = feature.properties.linestringTimestamps; 134 | } else if (feature.properties.hasOwnProperty('time')) { 135 | feature.featureTimes = [feature.properties.time]; 136 | } else { 137 | feature.featureTimes = []; 138 | } 139 | // String dates to ms 140 | for (var i = 0, l = feature.featureTimes.length; i < l; i++) { 141 | var time = feature.featureTimes[i]; 142 | if (typeof time == 'string' || time instanceof String) { 143 | time = Date.parse(time.trim()); 144 | feature.featureTimes[i] = time; 145 | } 146 | } 147 | } 148 | return feature.featureTimes; 149 | }, 150 | 151 | _getFeatureBetweenDates: function(feature, minTime, maxTime) { 152 | var featureTimes = this._getFeatureTimes(feature); 153 | if (featureTimes.length == 0) { 154 | return feature; 155 | } 156 | 157 | var index_min = null, 158 | index_max = null, 159 | l = featureTimes.length; 160 | 161 | if (featureTimes[0] > maxTime || featureTimes[l - 1] < minTime) { 162 | return null; 163 | } 164 | 165 | if (featureTimes[l - 1] >= minTime) { 166 | for (var i = 0; i < l; i++) { 167 | if (index_min === null && featureTimes[i] >= minTime) { 168 | // set index_min the first time that current time is greater or equal to the minTime 169 | index_min = i; 170 | } 171 | if (featureTimes[i] > maxTime) { 172 | index_max = i; 173 | break; 174 | } 175 | } 176 | } 177 | if (index_min === null) { 178 | index_min = 0; 179 | } 180 | if (index_max === null) { 181 | index_max = l; 182 | } 183 | var new_coordinates = []; 184 | if (feature.geometry.coordinates[0].length) { 185 | new_coordinates = feature.geometry.coordinates.slice(index_min, index_max); 186 | } else { 187 | new_coordinates = feature.geometry.coordinates; 188 | } 189 | return { 190 | type: 'Feature', 191 | properties: feature.properties, 192 | geometry: { 193 | type: feature.geometry.type, 194 | coordinates: new_coordinates 195 | } 196 | }; 197 | }, 198 | 199 | _onReadyBaseLayer: function() { 200 | this._loaded = true; 201 | this._setAvailableTimes(); 202 | this._update(); 203 | }, 204 | 205 | }); 206 | 207 | L.timeDimension.layer.geoJson = function(layer, options) { 208 | return new L.TimeDimension.Layer.GeoJson(layer, options); 209 | }; 210 | -------------------------------------------------------------------------------- /src/leaflet.timedimension.layer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.TimeDimension.Layer: an abstract Layer that can be managed/synchronized with a TimeDimension. 3 | * The constructor recieves a layer (of any kind) and options. 4 | * Any children class should implement `_onNewTimeLoading`, `isReady` and `_update` functions 5 | * to react to time changes. 6 | */ 7 | 8 | L.TimeDimension.Layer = (L.Layer || L.Class).extend({ 9 | 10 | includes: (L.Evented || L.Mixin.Events), 11 | options: { 12 | opacity: 1, 13 | zIndex: 1 14 | }, 15 | 16 | initialize: function(layer, options) { 17 | L.setOptions(this, options || {}); 18 | this._map = null; 19 | this._baseLayer = layer; 20 | this._currentLayer = null; 21 | this._timeDimension = this.options.timeDimension || null; 22 | }, 23 | 24 | addTo: function(map) { 25 | map.addLayer(this); 26 | return this; 27 | }, 28 | 29 | onAdd: function(map) { 30 | this._map = map; 31 | if (!this._timeDimension && map.timeDimension) { 32 | this._timeDimension = map.timeDimension; 33 | } 34 | this._timeDimension.on("timeloading", this._onNewTimeLoading, this); 35 | this._timeDimension.on("timeload", this._update, this); 36 | this._timeDimension.registerSyncedLayer(this); 37 | this._update(); 38 | }, 39 | 40 | onRemove: function(map) { 41 | this._timeDimension.unregisterSyncedLayer(this); 42 | this._timeDimension.off("timeloading", this._onNewTimeLoading, this); 43 | this._timeDimension.off("timeload", this._update, this); 44 | this.eachLayer(map.removeLayer, map); 45 | this._map = null; 46 | }, 47 | 48 | eachLayer: function(method, context) { 49 | method.call(context, this._baseLayer); 50 | return this; 51 | }, 52 | 53 | setZIndex: function(zIndex) { 54 | this.options.zIndex = zIndex; 55 | if (this._baseLayer.setZIndex) { 56 | this._baseLayer.setZIndex(zIndex); 57 | } 58 | if (this._currentLayer && this._currentLayer.setZIndex) { 59 | this._currentLayer.setZIndex(zIndex); 60 | } 61 | return this; 62 | }, 63 | 64 | setOpacity: function(opacity) { 65 | this.options.opacity = opacity; 66 | if (this._baseLayer.setOpacity) { 67 | this._baseLayer.setOpacity(opacity); 68 | } 69 | if (this._currentLayer && this._currentLayer.setOpacity) { 70 | this._currentLayer.setOpacity(opacity); 71 | } 72 | return this; 73 | }, 74 | 75 | bringToBack: function() { 76 | if (!this._currentLayer) { 77 | return; 78 | } 79 | this._currentLayer.bringToBack(); 80 | return this; 81 | }, 82 | 83 | bringToFront: function() { 84 | if (!this._currentLayer) { 85 | return; 86 | } 87 | this._currentLayer.bringToFront(); 88 | return this; 89 | }, 90 | 91 | _onNewTimeLoading: function(ev) { 92 | // to be implemented for each type of layer 93 | this.fire('timeload', { 94 | time: ev.time 95 | }); 96 | return; 97 | }, 98 | 99 | isReady: function(time) { 100 | // to be implemented for each type of layer 101 | return true; 102 | }, 103 | 104 | _update: function() { 105 | // to be implemented for each type of layer 106 | return true; 107 | }, 108 | 109 | getBaseLayer: function() { 110 | return this._baseLayer; 111 | }, 112 | 113 | getBounds: function() { 114 | var bounds = new L.LatLngBounds(); 115 | if (this._currentLayer) { 116 | bounds.extend(this._currentLayer.getBounds ? this._currentLayer.getBounds() : this._currentLayer.getLatLng()); 117 | } 118 | return bounds; 119 | } 120 | 121 | }); 122 | 123 | L.timeDimension.layer = function(layer, options) { 124 | return new L.TimeDimension.Layer(layer, options); 125 | }; -------------------------------------------------------------------------------- /src/leaflet.timedimension.player.js: -------------------------------------------------------------------------------- 1 | /*jshint indent: 4, browser:true*/ 2 | /*global L*/ 3 | 4 | 5 | /* 6 | * L.TimeDimension.Player 7 | */ 8 | //'use strict'; 9 | L.TimeDimension.Player = (L.Layer || L.Class).extend({ 10 | 11 | includes: (L.Evented || L.Mixin.Events), 12 | initialize: function(options, timeDimension) { 13 | L.setOptions(this, options); 14 | this._timeDimension = timeDimension; 15 | this._paused = false; 16 | this._buffer = this.options.buffer || 5; 17 | this._minBufferReady = this.options.minBufferReady || 1; 18 | this._waitingForBuffer = false; 19 | this._loop = this.options.loop || false; 20 | this._steps = 1; 21 | this._timeDimension.on('timeload', (function(data) { 22 | this.release(); // free clock 23 | this._waitingForBuffer = false; // reset buffer 24 | }).bind(this)); 25 | this.setTransitionTime(this.options.transitionTime || 1000); 26 | 27 | this._timeDimension.on('limitschanged availabletimeschanged timeload', (function(data) { 28 | this._timeDimension.prepareNextTimes(this._steps, this._minBufferReady, this._loop); 29 | }).bind(this)); 30 | }, 31 | 32 | 33 | _tick: function() { 34 | var maxIndex = this._getMaxIndex(); 35 | var maxForward = (this._timeDimension.getCurrentTimeIndex() >= maxIndex) && (this._steps > 0); 36 | var maxBackward = (this._timeDimension.getCurrentTimeIndex() == 0) && (this._steps < 0); 37 | if (maxForward || maxBackward) { 38 | // we reached the last step 39 | if (!this._loop) { 40 | this.pause(); 41 | this.stop(); 42 | this.fire('animationfinished'); 43 | return; 44 | } 45 | } 46 | 47 | if (this._paused) { 48 | return; 49 | } 50 | var numberNextTimesReady = 0, 51 | buffer = this._bufferSize; 52 | 53 | if (this._minBufferReady > 0) { 54 | numberNextTimesReady = this._timeDimension.getNumberNextTimesReady(this._steps, buffer, this._loop); 55 | // If the player was waiting, check if all times are loaded 56 | if (this._waitingForBuffer) { 57 | if (numberNextTimesReady < buffer) { 58 | console.log('Waiting until buffer is loaded. ' + numberNextTimesReady + ' of ' + buffer + ' loaded'); 59 | this.fire('waiting', { 60 | buffer: buffer, 61 | available: numberNextTimesReady 62 | }); 63 | return; 64 | } else { 65 | // all times loaded 66 | console.log('Buffer is fully loaded!'); 67 | this.fire('running'); 68 | this._waitingForBuffer = false; 69 | } 70 | } else { 71 | // check if player has to stop to wait and force to full all the buffer 72 | if (numberNextTimesReady < this._minBufferReady) { 73 | console.log('Force wait for load buffer. ' + numberNextTimesReady + ' of ' + buffer + ' loaded'); 74 | this._waitingForBuffer = true; 75 | this._timeDimension.prepareNextTimes(this._steps, buffer, this._loop); 76 | this.fire('waiting', { 77 | buffer: buffer, 78 | available: numberNextTimesReady 79 | }); 80 | return; 81 | } 82 | } 83 | } 84 | this.pause(); 85 | this._timeDimension.nextTime(this._steps, this._loop); 86 | if (buffer > 0) { 87 | this._timeDimension.prepareNextTimes(this._steps, buffer, this._loop); 88 | } 89 | }, 90 | 91 | _getMaxIndex: function(){ 92 | return Math.min(this._timeDimension.getAvailableTimes().length - 1, 93 | this._timeDimension.getUpperLimitIndex() || Infinity); 94 | }, 95 | 96 | start: function(numSteps) { 97 | if (this._intervalID) return; 98 | this._steps = numSteps || 1; 99 | this._waitingForBuffer = false; 100 | var startedOver = false; 101 | if (this.options.startOver){ 102 | if (this._timeDimension.getCurrentTimeIndex() === this._getMaxIndex()){ 103 | this._timeDimension.setCurrentTimeIndex(this._timeDimension.getLowerLimitIndex() || 0); 104 | startedOver = true; 105 | } 106 | } 107 | this.release(); 108 | this._intervalID = window.setInterval( 109 | L.bind(this._tick, this), 110 | this._transitionTime); 111 | if (!startedOver) 112 | this._tick(); 113 | this.fire('play'); 114 | this.fire('running'); 115 | }, 116 | 117 | stop: function() { 118 | if (!this._intervalID) return; 119 | clearInterval(this._intervalID); 120 | this._intervalID = null; 121 | this._waitingForBuffer = false; 122 | this.fire('stop'); 123 | }, 124 | 125 | pause: function() { 126 | this._paused = true; 127 | }, 128 | 129 | release: function () { 130 | this._paused = false; 131 | }, 132 | 133 | getTransitionTime: function() { 134 | return this._transitionTime; 135 | }, 136 | 137 | isPlaying: function() { 138 | return this._intervalID ? true : false; 139 | }, 140 | 141 | isWaiting: function() { 142 | return this._waitingForBuffer; 143 | }, 144 | isLooped: function() { 145 | return this._loop; 146 | }, 147 | 148 | setLooped: function(looped) { 149 | this._loop = looped; 150 | this.fire('loopchange', { 151 | loop: looped 152 | }); 153 | }, 154 | 155 | setTransitionTime: function(transitionTime) { 156 | this._transitionTime = transitionTime; 157 | if (typeof this._buffer === 'function') { 158 | this._bufferSize = this._buffer.call(this, this._transitionTime, this._minBufferReady, this._loop); 159 | console.log('Buffer size changed to ' + this._bufferSize); 160 | } else { 161 | this._bufferSize = this._buffer; 162 | } 163 | if (this._intervalID) { 164 | this.stop(); 165 | this.start(this._steps); 166 | } 167 | this.fire('speedchange', { 168 | transitionTime: transitionTime, 169 | buffer: this._bufferSize 170 | }); 171 | }, 172 | 173 | getSteps: function() { 174 | return this._steps; 175 | } 176 | }); 177 | -------------------------------------------------------------------------------- /src/leaflet.timedimension.util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.TimeDimension.Util 3 | */ 4 | 5 | L.TimeDimension.Util = { 6 | getTimeDuration: function(ISODuration) { 7 | if (typeof nezasa === 'undefined') { 8 | throw "iso8601-js-period library is required for Leatlet.TimeDimension: https://www.npmjs.com/package/iso8601-js-period"; 9 | } 10 | return nezasa.iso8601.Period.parse(ISODuration, true); 11 | }, 12 | 13 | addTimeDuration: function(date, duration, utc) { 14 | if (typeof utc === 'undefined') { 15 | utc = true; 16 | } 17 | if (typeof duration == 'string' || duration instanceof String) { 18 | duration = this.getTimeDuration(duration); 19 | } 20 | var l = duration.length; 21 | var get = utc ? "getUTC" : "get"; 22 | var set = utc ? "setUTC" : "set"; 23 | 24 | if (l > 0 && duration[0] != 0) { 25 | date[set + "FullYear"](date[get + "FullYear"]() + duration[0]); 26 | } 27 | if (l > 1 && duration[1] != 0) { 28 | date[set + "Month"](date[get + "Month"]() + duration[1]); 29 | } 30 | if (l > 2 && duration[2] != 0) { 31 | // weeks 32 | date[set + "Date"](date[get + "Date"]() + (duration[2] * 7)); 33 | } 34 | if (l > 3 && duration[3] != 0) { 35 | date[set + "Date"](date[get + "Date"]() + duration[3]); 36 | } 37 | if (l > 4 && duration[4] != 0) { 38 | date[set + "Hours"](date[get + "Hours"]() + duration[4]); 39 | } 40 | if (l > 5 && duration[5] != 0) { 41 | date[set + "Minutes"](date[get + "Minutes"]() + duration[5]); 42 | } 43 | if (l > 6 && duration[6] != 0) { 44 | date[set + "Seconds"](date[get + "Seconds"]() + duration[6]); 45 | } 46 | }, 47 | 48 | subtractTimeDuration: function(date, duration, utc) { 49 | if (typeof duration == 'string' || duration instanceof String) { 50 | duration = this.getTimeDuration(duration); 51 | } 52 | var subDuration = []; 53 | for (var i = 0, l = duration.length; i < l; i++) { 54 | subDuration.push(-duration[i]); 55 | } 56 | this.addTimeDuration(date, subDuration, utc); 57 | }, 58 | 59 | parseAndExplodeTimeRange: function(timeRange, overwritePeriod) { 60 | var tr = timeRange.split('/'); 61 | var startTime = new Date(Date.parse(tr[0])); 62 | var endTime = new Date(Date.parse(tr[1])); 63 | var period = (tr.length > 2 && tr[2].length) ? tr[2] : "P1D"; 64 | if (overwritePeriod !== undefined && overwritePeriod !== null){ 65 | period = overwritePeriod; 66 | } 67 | return this.explodeTimeRange(startTime, endTime, period); 68 | }, 69 | 70 | explodeTimeRange: function(startTime, endTime, ISODuration, validTimeRange) { 71 | var duration = this.getTimeDuration(ISODuration); 72 | var result = []; 73 | var currentTime = new Date(startTime.getTime()); 74 | var minHour = null, 75 | minMinutes = null, 76 | maxHour = null, 77 | maxMinutes = null; 78 | if (validTimeRange !== undefined) { 79 | var validTimeRangeArray = validTimeRange.split('/'); 80 | minHour = validTimeRangeArray[0].split(':')[0]; 81 | minMinutes = validTimeRangeArray[0].split(':')[1]; 82 | maxHour = validTimeRangeArray[1].split(':')[0]; 83 | maxMinutes = validTimeRangeArray[1].split(':')[1]; 84 | } 85 | while (currentTime < endTime) { 86 | if (validTimeRange === undefined || 87 | (currentTime.getUTCHours() >= minHour && currentTime.getUTCHours() <= maxHour) 88 | ) { 89 | if ((currentTime.getUTCHours() != minHour || currentTime.getUTCMinutes() >= minMinutes) && 90 | (currentTime.getUTCHours() != maxHour || currentTime.getUTCMinutes() <= maxMinutes)) { 91 | result.push(currentTime.getTime()); 92 | } 93 | } 94 | this.addTimeDuration(currentTime, duration); 95 | } 96 | if (currentTime >= endTime){ 97 | result.push(endTime.getTime()); 98 | } 99 | return result; 100 | }, 101 | 102 | parseTimeInterval: function(timeInterval) { 103 | var parts = timeInterval.split("/"); 104 | if (parts.length != 2) { 105 | throw "Incorrect ISO9601 TimeInterval: " + timeInterval; 106 | } 107 | var startTime = Date.parse(parts[0]); 108 | var endTime = null; 109 | var duration = null; 110 | if (isNaN(startTime)) { 111 | // -> format duration/endTime 112 | duration = this.getTimeDuration(parts[0]); 113 | endTime = Date.parse(parts[1]); 114 | startTime = new Date(endTime); 115 | this.subtractTimeDuration(startTime, duration, true); 116 | endTime = new Date(endTime); 117 | } else { 118 | endTime = Date.parse(parts[1]); 119 | if (isNaN(endTime)) { 120 | // -> format startTime/duration 121 | duration = this.getTimeDuration(parts[1]); 122 | endTime = new Date(startTime); 123 | this.addTimeDuration(endTime, duration, true); 124 | } else { 125 | // -> format startTime/endTime 126 | endTime = new Date(endTime); 127 | } 128 | startTime = new Date(startTime); 129 | } 130 | return [startTime, endTime]; 131 | }, 132 | 133 | parseTimesExpression: function(times, overwritePeriod) { 134 | var result = []; 135 | if (!times) { 136 | return result; 137 | } 138 | if (typeof times == 'string' || times instanceof String) { 139 | var timeRanges = times.split(","); 140 | var timeRange; 141 | var timeValue; 142 | for (var i=0, l=timeRanges.length; i 0 && b.length > 0) { 166 | if (a[0] < b[0]) { 167 | a.shift(); 168 | } else if (a[0] > b[0]) { 169 | b.shift(); 170 | } else /* they're equal */ { 171 | result.push(a.shift()); 172 | b.shift(); 173 | } 174 | } 175 | return result; 176 | }, 177 | 178 | union_arrays: function(arrayA, arrayB) { 179 | var a = arrayA.slice(0); 180 | var b = arrayB.slice(0); 181 | var result = []; 182 | while (a.length > 0 && b.length > 0) { 183 | if (a[0] < b[0]) { 184 | result.push(a.shift()); 185 | } else if (a[0] > b[0]) { 186 | result.push(b.shift()); 187 | } else /* they're equal */ { 188 | result.push(a.shift()); 189 | b.shift(); 190 | } 191 | } 192 | if (a.length > 0) { 193 | result = result.concat(a); 194 | } else if (b.length > 0) { 195 | result = result.concat(b); 196 | } 197 | return result; 198 | }, 199 | 200 | sort_and_deduplicate: function(arr) { 201 | arr = arr.slice(0).sort(function (a, b) { 202 | return a - b; 203 | }); 204 | var result = []; 205 | var last = null; 206 | for (var i = 0, l = arr.length; i < l; i++) { 207 | if (arr[i] !== last){ 208 | result.push(arr[i]); 209 | last = arr[i]; 210 | } 211 | } 212 | return result; 213 | } 214 | 215 | }; 216 | --------------------------------------------------------------------------------