├── .gitignore ├── LICENSE ├── README.md ├── css ├── bootstrap.min.css ├── icons-classic │ ├── label.png │ ├── resultset_next.png │ └── resultset_previous.png ├── jquery.slider.css ├── jquery.slider.ithing.css ├── leaflet.css ├── main.css └── slider_bar.png ├── csv ├── 10-all-5min.csv ├── 182-29out-5min.csv └── 50-29out-5min.csv ├── index.html ├── js ├── bootstrap.min.js ├── crossfilter.min.js ├── jquery.mousewheel.min.js ├── jquery.slider.min.js ├── knockout.min.js ├── leaflet.min.js ├── main.js ├── moment.min.js └── moment.timezone.min.js ├── parse.py └── sigr_63925_project_report.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipr 2 | *.iws 3 | *.iml 4 | var/* 5 | target/* 6 | build/* 7 | */target/* 8 | .gradle/* 9 | out/* 10 | dependency.txt 11 | .tablesawcache 12 | .idea 13 | log/* 14 | /bin 15 | /kairosdb.jar 16 | 17 | # Sublime Files 18 | *.sublime-project 19 | *.sublime-workspace 20 | 21 | # Eclipse 22 | .classpath 23 | .project 24 | .settings/ 25 | 26 | # Intellij 27 | .idea/ 28 | *.iml 29 | *.iws 30 | 31 | # Maven 32 | log/ 33 | target/ 34 | 35 | # OSX 36 | .DS_Store 37 | 38 | # Linux 39 | .* 40 | !.gitignore 41 | *~ 42 | 43 | # KDE 44 | .directory 45 | 46 | # Windows 47 | .Spotlight-V100 48 | .Trashes 49 | Thumbs.db 50 | ._* 51 | Desktop.ini 52 | $RECYCLE.BIN/ 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Hugo Sequeira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | spatiotemporal-visualizer 2 | ========================= 3 | 4 | An online spatio-temporal utility to visualize trajectory data using D3.js, Leaflet.js and Crossfilter. 5 | 6 | View demo at http://hugocore.github.io/spatiotemporal-visualizer/ 7 | 8 | Read [documentation](https://www.researchgate.net/publication/266734204_Spatio-Temporal_Visualizer_Online_tool_to_visualize_spatiotemporal_data_with_an_interactive_time-window_slider_using_D3js_Leafletjs_and_Crossfilter) for more information. 9 | -------------------------------------------------------------------------------- /css/icons-classic/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugocore/spatiotemporal-visualizer/8fed56f2463d797fe7c438bc51364b1758374fdd/css/icons-classic/label.png -------------------------------------------------------------------------------- /css/icons-classic/resultset_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugocore/spatiotemporal-visualizer/8fed56f2463d797fe7c438bc51364b1758374fdd/css/icons-classic/resultset_next.png -------------------------------------------------------------------------------- /css/icons-classic/resultset_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugocore/spatiotemporal-visualizer/8fed56f2463d797fe7c438bc51364b1758374fdd/css/icons-classic/resultset_previous.png -------------------------------------------------------------------------------- /css/jquery.slider.css: -------------------------------------------------------------------------------- 1 | .ui-rangeSlider{height:22px}.ui-rangeSlider .ui-rangeSlider-innerBar{height:16px;margin:3px 6px;background:#DDD}.ui-rangeSlider .ui-rangeSlider-handle{width:6px;height:22px;background:#AAA;background:rgba(100,100,100,.3);cursor:col-resize}.ui-rangeSlider .ui-rangeSlider-bar{margin:1px 0;background:#CCC;background:rgba(100,100,150,.2);height:20px;cursor:move;cursor:grab;cursor:-moz-grab}.ui-rangeSlider .ui-rangeSlider-bar.ui-draggable-dragging{cursor:-moz-grabbing;cursor:grabbing}.ui-rangeSlider-arrow{height:16px;margin:2px 0;width:16px;background-repeat:no-repeat}.ui-rangeSlider-arrow.ui-rangeSlider-leftArrow{background-image:url(icons-classic/resultset_previous.png);background-position:center left}.ui-rangeSlider-arrow.ui-rangeSlider-rightArrow{background-image:url(icons-classic/resultset_next.png);background-position:center right}.ui-rangeSlider-arrow-inner{display:none}.ui-rangeSlider-container{height:22px}.ui-rangeSlider-withArrows .ui-rangeSlider-container{margin:0 11px}.ui-rangeSlider-noArrow .ui-rangeSlider-container{margin:0}.ui-rangeSlider-label{margin:0 2px 2px;background-image:url(icons-classic/label.png);background-position:bottom center;background-repeat:no-repeat;white-space:nowrap;bottom:20px;padding:3px 6px 7px;cursor:col-resize}.ui-rangeSlider-label-inner{display:none}input.ui-editRangeSlider-inputValue{width:3em;vertical-align:middle;text-align:center} -------------------------------------------------------------------------------- /css/jquery.slider.ithing.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Theme for jQRangeSlider 3 | * Inspired by http://cssdeck.com/item/381/itunes-10-storage-bar 4 | * and http://cssdeck.com/item/276/pure-css-arrow-with-border-tooltip 5 | */ 6 | 7 | .ui-rangeSlider{ 8 | height: 30px; 9 | padding-top: 40px; 10 | } 11 | 12 | .ui-rangeSlider, 13 | .ui-rangeSlider-container, 14 | .ui-rangeSlider-arrow{ 15 | -webkit-box-sizing:content-box; 16 | -moz-box-sizing:content-box; 17 | box-sizing:content-box; 18 | } 19 | 20 | .ui-rangeSlider-withArrows .ui-rangeSlider-container{ 21 | margin: 0 15px; 22 | } 23 | 24 | .ui-rangeSlider-withArrows .ui-rangeSlider-container, 25 | .ui-rangeSlider-noArrow .ui-rangeSlider-container, 26 | .ui-rangeSlider-arrow{ 27 | -webkit-box-shadow: inset 0px 4px 6px -2px RGBA(0,0,0,0.5); 28 | -moz-box-shadow: inset 0px 4px 6px -2px RGBA(0,0,0,0.5); 29 | box-shadow: inset 0px 4px 6px -2px RGBA(0,0,0,0.5); 30 | } 31 | 32 | .ui-rangeSlider-disabled.ui-rangeSlider-withArrows .ui-rangeSlider-container, 33 | .ui-rangeSlider-disabled.ui-rangeSlider-noArrow .ui-rangeSlider-container, 34 | .ui-rangeSlider-disabled .ui-rangeSlider-arrow{ 35 | -webkit-box-shadow: inset 0px 4px 6px -2px RGBA(0,0,0,0.3); 36 | -moz-box-shadow: inset 0px 4px 6px -2px RGBA(0,0,0,0.3); 37 | box-shadow: inset 0px 4px 6px -2px RGBA(0,0,0,0.3); 38 | } 39 | 40 | .ui-rangeSlider-noArrow .ui-rangeSlider-container{ 41 | -moz-border-radius: 4px; 42 | border-radius: 4px; 43 | border-left: solid 1px #515862; 44 | border-right: solid 1px #515862; 45 | } 46 | 47 | .ui-rangeSlider-disabled.ui-rangeSlider-noArrow .ui-rangeSlider-container{ 48 | border-color: #8490a3; 49 | } 50 | 51 | .ui-rangeSlider-container, 52 | .ui-rangeSlider-arrow{ 53 | height: 30px; 54 | 55 | border-top: solid 1px #232a32; 56 | border-bottom: solid 1px #6a7179; 57 | } 58 | 59 | .ui-rangeSlider-disabled .ui-rangeSlider-container, 60 | .ui-rangeSlider-disabled .ui-rangeSlider-arrow{ 61 | border-top-color: #49576b; 62 | border-bottom-color: #9ca7b3; 63 | } 64 | 65 | .ui-rangeSlider-container, 66 | .ui-rangeSlider-arrow, 67 | .ui-rangeSlider-label{ 68 | background: #67707F; 69 | background: -moz-linear-gradient(top, #67707F 0%, #888DA0 100%); 70 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#67707F), color-stop(100%,#888DA0)); 71 | } 72 | 73 | .ui-rangeSlider-disabled .ui-rangeSlider-container, 74 | .ui-rangeSlider-disabled .ui-rangeSlider-arrow, 75 | .ui-rangeSlider-disabled .ui-rangeSlider-label{ 76 | background: #95a4bd; 77 | background: -moz-linear-gradient(top, #95a4bd 0%, #b2bbd8 100%); 78 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#95a4bd), color-stop(100%,#b2bbd8)); 79 | } 80 | 81 | .ui-rangeSlider-arrow{ 82 | width:14px; 83 | cursor:pointer; 84 | } 85 | 86 | .ui-rangeSlider-leftArrow{ 87 | border-radius:4px 0 0 4px; 88 | border-left: solid 1px #515862; 89 | } 90 | 91 | .ui-rangeSlider-disabled .ui-rangeSlider-leftArrow{ 92 | border-left-color: #8792a2; 93 | } 94 | 95 | .ui-rangeSlider-rightArrow{ 96 | border-radius:0 4px 4px 0; 97 | border-right: solid 1px #515862; 98 | } 99 | 100 | .ui-rangeSlider-disabled .ui-rangeSlider-rightArrow{ 101 | border-right-color: #8792a2; 102 | } 103 | 104 | .ui-rangeSlider-arrow-inner{ 105 | position: absolute; 106 | top: 50%; 107 | border: 10px solid transparent; 108 | width:0; 109 | height:0; 110 | 111 | margin-top: -10px; 112 | } 113 | 114 | .ui-rangeSlider-leftArrow .ui-rangeSlider-arrow-inner{ 115 | border-right:10px solid #a4a8b7; 116 | left: 0; 117 | margin-left: -8px; 118 | } 119 | 120 | .ui-rangeSlider-leftArrow:hover .ui-rangeSlider-arrow-inner{ 121 | border-right:10px solid #b3b6c2; 122 | } 123 | 124 | .ui-rangeSlider-disabled .ui-rangeSlider-leftArrow .ui-rangeSlider-arrow-inner, 125 | .ui-rangeSlider-disabled .ui-rangeSlider-leftArrow:hover .ui-rangeSlider-arrow-inner{ 126 | border-right-color: #bbc0cf; 127 | } 128 | 129 | .ui-rangeSlider-rightArrow .ui-rangeSlider-arrow-inner{ 130 | border-left:10px solid #a4a8b7; 131 | right: 0; 132 | margin-right: -8px; 133 | } 134 | 135 | .ui-rangeSlider-rightArrow:hover .ui-rangeSlider-arrow-inner{ 136 | border-left:10px solid #b3b6c2; 137 | } 138 | 139 | .ui-rangeSlider-disabled .ui-rangeSlider-rightArrow .ui-rangeSlider-arrow-inner, 140 | .ui-rangeSlider-disabled .ui-rangeSlider-rightArrow:hover .ui-rangeSlider-arrow-inner{ 141 | border-left-color: #bbc0cf; 142 | } 143 | 144 | .ui-rangeSlider-innerBar{ 145 | width: 110%; 146 | height: 100%; 147 | left: -10px; 148 | overflow: hidden; 149 | } 150 | 151 | .ui-rangeSlider-bar{ 152 | background: #842DCE url("slider_bar.png") no-repeat right center; 153 | height: 29px; 154 | margin:1px 0; 155 | -moz-border-radius: 4px; 156 | border-radius: 4px; 157 | cursor:move; 158 | cursor:grab; 159 | cursor: -moz-grab; 160 | 161 | -webkit-box-shadow: inset 0 2px 6px RGBA(0,0,0,0.5); 162 | -moz-box-shadow: inset 0 2px 6px RGBA(0,0,0,0.5); 163 | box-shadow: inset 0 2px 6px RGBA(0,0,0,0.5); 164 | } 165 | 166 | .ui-rangeSlider-disabled .ui-rangeSlider-bar{ 167 | background: #93aeca; 168 | 169 | -webkit-box-shadow: inset 0 2px 6px RGBA(0,0,0,0.3); 170 | -moz-box-shadow: inset 0 2px 6px RGBA(0,0,0,0.3); 171 | box-shadow: inset 0 2px 6px RGBA(0,0,0,0.3); 172 | } 173 | 174 | .ui-rangeSlider-handle{ 175 | width:10px; 176 | height:30px; 177 | background: transparent; 178 | cursor:col-resize; 179 | } 180 | 181 | .ui-rangeSlider-label{ 182 | padding: 5px 10px; 183 | bottom:40px; 184 | 185 | -moz-border-radius: 4px; 186 | border-radius: 4px; 187 | 188 | -webkit-box-shadow: 0px 1px 0px #c2c5d6; 189 | -moz-box-shadow: 0px 1px 0px #c2c5d6; 190 | box-shadow: 0px 1px 0px #c2c5d6; 191 | 192 | color:white; 193 | font-size:15px; 194 | 195 | cursor:col-resize; 196 | } 197 | 198 | .ui-rangeSlider-label-inner{ 199 | position: absolute; 200 | top: 100%; 201 | left: 50%; 202 | display: block; 203 | z-index:99; 204 | border-left: 10px solid transparent; 205 | border-right: 10px solid transparent; 206 | 207 | margin-left: -10px; 208 | border-top: 10px solid #888DA0; 209 | } 210 | 211 | .ui-rangeSlider-disabled .ui-rangeSlider-label-inner{ 212 | border-top-color: #b2bbd8; 213 | } 214 | 215 | .ui-editRangeSlider-inputValue{ 216 | width:2em; 217 | text-align:center; 218 | font-size:15px; 219 | } 220 | 221 | .ui-rangeSlider .ui-ruler-scale{ 222 | position:absolute; 223 | top:0; 224 | left:0; 225 | bottom:0; 226 | right:0; 227 | } 228 | 229 | .ui-rangeSlider .ui-ruler-tick { 230 | float: left; 231 | } 232 | 233 | .ui-rangeSlider .ui-ruler-scale0 .ui-ruler-tick-inner{ 234 | color:white; 235 | margin-top:1px; 236 | border-left: 1px solid white; 237 | height:29px; 238 | padding-left:2px; 239 | position:relative; 240 | } 241 | 242 | .ui-rangeSlider .ui-ruler-scale0 .ui-ruler-tick-label{ 243 | position:absolute; 244 | bottom: 6px; 245 | } 246 | 247 | .ui-rangeSlider .ui-ruler-scale1 .ui-ruler-tick-inner{ 248 | border-left:1px solid white; 249 | margin-top: 25px; 250 | height: 5px; 251 | } 252 | -------------------------------------------------------------------------------- /css/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles */ 2 | 3 | .leaflet-map-pane, 4 | .leaflet-tile, 5 | .leaflet-marker-icon, 6 | .leaflet-marker-shadow, 7 | .leaflet-tile-pane, 8 | .leaflet-tile-container, 9 | .leaflet-overlay-pane, 10 | .leaflet-shadow-pane, 11 | .leaflet-marker-pane, 12 | .leaflet-popup-pane, 13 | .leaflet-overlay-pane svg, 14 | .leaflet-zoom-box, 15 | .leaflet-image-layer, 16 | .leaflet-layer { 17 | position: absolute; 18 | left: 0; 19 | top: 0; 20 | } 21 | .leaflet-container { 22 | overflow: hidden; 23 | -ms-touch-action: none; 24 | } 25 | .leaflet-tile, 26 | .leaflet-marker-icon, 27 | .leaflet-marker-shadow { 28 | -webkit-user-select: none; 29 | -moz-user-select: none; 30 | user-select: none; 31 | -webkit-user-drag: none; 32 | } 33 | .leaflet-marker-icon, 34 | .leaflet-marker-shadow { 35 | display: block; 36 | } 37 | /* map is broken in FF if you have max-width: 100% on tiles */ 38 | .leaflet-container img { 39 | max-width: none !important; 40 | } 41 | /* stupid Android 2 doesn't understand "max-width: none" properly */ 42 | .leaflet-container img.leaflet-image-layer { 43 | max-width: 15000px !important; 44 | } 45 | .leaflet-tile { 46 | filter: inherit; 47 | visibility: hidden; 48 | } 49 | .leaflet-tile-loaded { 50 | visibility: inherit; 51 | } 52 | .leaflet-zoom-box { 53 | width: 0; 54 | height: 0; 55 | } 56 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 57 | .leaflet-overlay-pane svg { 58 | -moz-user-select: none; 59 | } 60 | 61 | .leaflet-tile-pane { z-index: 2; } 62 | .leaflet-objects-pane { z-index: 3; } 63 | .leaflet-overlay-pane { z-index: 4; } 64 | .leaflet-shadow-pane { z-index: 5; } 65 | .leaflet-marker-pane { z-index: 6; } 66 | .leaflet-popup-pane { z-index: 7; } 67 | 68 | .leaflet-vml-shape { 69 | width: 1px; 70 | height: 1px; 71 | } 72 | .lvml { 73 | behavior: url(#default#VML); 74 | display: inline-block; 75 | position: absolute; 76 | } 77 | 78 | 79 | /* control positioning */ 80 | 81 | .leaflet-control { 82 | position: relative; 83 | z-index: 7; 84 | pointer-events: auto; 85 | } 86 | .leaflet-top, 87 | .leaflet-bottom { 88 | position: absolute; 89 | z-index: 1000; 90 | pointer-events: none; 91 | } 92 | .leaflet-top { 93 | top: 0; 94 | } 95 | .leaflet-right { 96 | right: 0; 97 | } 98 | .leaflet-bottom { 99 | bottom: 0; 100 | } 101 | .leaflet-left { 102 | left: 0; 103 | } 104 | .leaflet-control { 105 | float: left; 106 | clear: both; 107 | } 108 | .leaflet-right .leaflet-control { 109 | float: right; 110 | } 111 | .leaflet-top .leaflet-control { 112 | margin-top: 10px; 113 | } 114 | .leaflet-bottom .leaflet-control { 115 | margin-bottom: 10px; 116 | } 117 | .leaflet-left .leaflet-control { 118 | margin-left: 10px; 119 | } 120 | .leaflet-right .leaflet-control { 121 | margin-right: 10px; 122 | } 123 | 124 | 125 | /* zoom and fade animations */ 126 | 127 | .leaflet-fade-anim .leaflet-tile, 128 | .leaflet-fade-anim .leaflet-popup { 129 | opacity: 0; 130 | -webkit-transition: opacity 0.2s linear; 131 | -moz-transition: opacity 0.2s linear; 132 | -o-transition: opacity 0.2s linear; 133 | transition: opacity 0.2s linear; 134 | } 135 | .leaflet-fade-anim .leaflet-tile-loaded, 136 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 137 | opacity: 1; 138 | } 139 | 140 | .leaflet-zoom-anim .leaflet-zoom-animated { 141 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 142 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 143 | -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); 144 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 145 | } 146 | .leaflet-zoom-anim .leaflet-tile, 147 | .leaflet-pan-anim .leaflet-tile, 148 | .leaflet-touching .leaflet-zoom-animated { 149 | -webkit-transition: none; 150 | -moz-transition: none; 151 | -o-transition: none; 152 | transition: none; 153 | } 154 | 155 | .leaflet-zoom-anim .leaflet-zoom-hide { 156 | visibility: hidden; 157 | } 158 | 159 | 160 | /* cursors */ 161 | 162 | .leaflet-clickable { 163 | cursor: pointer; 164 | } 165 | .leaflet-container { 166 | cursor: -webkit-grab; 167 | cursor: -moz-grab; 168 | } 169 | .leaflet-popup-pane, 170 | .leaflet-control { 171 | cursor: auto; 172 | } 173 | .leaflet-dragging .leaflet-container, 174 | .leaflet-dragging .leaflet-clickable { 175 | cursor: move; 176 | cursor: -webkit-grabbing; 177 | cursor: -moz-grabbing; 178 | } 179 | 180 | 181 | /* visual tweaks */ 182 | 183 | .leaflet-container { 184 | background: #ddd; 185 | outline: 0; 186 | } 187 | .leaflet-container a { 188 | color: #0078A8; 189 | } 190 | .leaflet-container a.leaflet-active { 191 | outline: 2px solid orange; 192 | } 193 | .leaflet-zoom-box { 194 | border: 2px dotted #38f; 195 | background: rgba(255,255,255,0.5); 196 | } 197 | 198 | 199 | /* general typography */ 200 | .leaflet-container { 201 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 202 | } 203 | 204 | 205 | /* general toolbar styles */ 206 | 207 | .leaflet-bar { 208 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 209 | border-radius: 4px; 210 | } 211 | .leaflet-bar a, 212 | .leaflet-bar a:hover { 213 | background-color: #fff; 214 | border-bottom: 1px solid #ccc; 215 | width: 26px; 216 | height: 26px; 217 | line-height: 26px; 218 | display: block; 219 | text-align: center; 220 | text-decoration: none; 221 | color: black; 222 | } 223 | .leaflet-bar a, 224 | .leaflet-control-layers-toggle { 225 | background-position: 50% 50%; 226 | background-repeat: no-repeat; 227 | display: block; 228 | } 229 | .leaflet-bar a:hover { 230 | background-color: #f4f4f4; 231 | } 232 | .leaflet-bar a:first-child { 233 | border-top-left-radius: 4px; 234 | border-top-right-radius: 4px; 235 | } 236 | .leaflet-bar a:last-child { 237 | border-bottom-left-radius: 4px; 238 | border-bottom-right-radius: 4px; 239 | border-bottom: none; 240 | } 241 | .leaflet-bar a.leaflet-disabled { 242 | cursor: default; 243 | background-color: #f4f4f4; 244 | color: #bbb; 245 | } 246 | 247 | .leaflet-touch .leaflet-bar a { 248 | width: 30px; 249 | height: 30px; 250 | line-height: 30px; 251 | } 252 | 253 | 254 | /* zoom control */ 255 | 256 | .leaflet-control-zoom-in, 257 | .leaflet-control-zoom-out { 258 | font: bold 18px 'Lucida Console', Monaco, monospace; 259 | text-indent: 1px; 260 | } 261 | .leaflet-control-zoom-out { 262 | font-size: 20px; 263 | } 264 | 265 | .leaflet-touch .leaflet-control-zoom-in { 266 | font-size: 22px; 267 | } 268 | .leaflet-touch .leaflet-control-zoom-out { 269 | font-size: 24px; 270 | } 271 | 272 | 273 | /* layers control */ 274 | 275 | .leaflet-control-layers { 276 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 277 | background: #fff; 278 | border-radius: 5px; 279 | } 280 | .leaflet-control-layers-toggle { 281 | background-image: url(images/layers.png); 282 | width: 36px; 283 | height: 36px; 284 | } 285 | .leaflet-retina .leaflet-control-layers-toggle { 286 | background-image: url(images/layers-2x.png); 287 | background-size: 26px 26px; 288 | } 289 | .leaflet-touch .leaflet-control-layers-toggle { 290 | width: 44px; 291 | height: 44px; 292 | } 293 | .leaflet-control-layers .leaflet-control-layers-list, 294 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 295 | display: none; 296 | } 297 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 298 | display: block; 299 | position: relative; 300 | } 301 | .leaflet-control-layers-expanded { 302 | padding: 6px 10px 6px 6px; 303 | color: #333; 304 | background: #fff; 305 | } 306 | .leaflet-control-layers-selector { 307 | margin-top: 2px; 308 | position: relative; 309 | top: 1px; 310 | } 311 | .leaflet-control-layers label { 312 | display: block; 313 | } 314 | .leaflet-control-layers-separator { 315 | height: 0; 316 | border-top: 1px solid #ddd; 317 | margin: 5px -10px 5px -6px; 318 | } 319 | 320 | 321 | /* attribution and scale controls */ 322 | 323 | .leaflet-container .leaflet-control-attribution { 324 | background: #fff; 325 | background: rgba(255, 255, 255, 0.7); 326 | margin: 0; 327 | } 328 | .leaflet-control-attribution, 329 | .leaflet-control-scale-line { 330 | padding: 0 5px; 331 | color: #333; 332 | } 333 | .leaflet-control-attribution a { 334 | text-decoration: none; 335 | } 336 | .leaflet-control-attribution a:hover { 337 | text-decoration: underline; 338 | } 339 | .leaflet-container .leaflet-control-attribution, 340 | .leaflet-container .leaflet-control-scale { 341 | font-size: 11px; 342 | } 343 | .leaflet-left .leaflet-control-scale { 344 | margin-left: 5px; 345 | } 346 | .leaflet-bottom .leaflet-control-scale { 347 | margin-bottom: 5px; 348 | } 349 | .leaflet-control-scale-line { 350 | border: 2px solid #777; 351 | border-top: none; 352 | line-height: 1.1; 353 | padding: 2px 5px 1px; 354 | font-size: 11px; 355 | white-space: nowrap; 356 | overflow: hidden; 357 | -moz-box-sizing: content-box; 358 | box-sizing: content-box; 359 | 360 | background: #fff; 361 | background: rgba(255, 255, 255, 0.5); 362 | } 363 | .leaflet-control-scale-line:not(:first-child) { 364 | border-top: 2px solid #777; 365 | border-bottom: none; 366 | margin-top: -2px; 367 | } 368 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 369 | border-bottom: 2px solid #777; 370 | } 371 | 372 | .leaflet-touch .leaflet-control-attribution, 373 | .leaflet-touch .leaflet-control-layers, 374 | .leaflet-touch .leaflet-bar { 375 | box-shadow: none; 376 | } 377 | .leaflet-touch .leaflet-control-layers, 378 | .leaflet-touch .leaflet-bar { 379 | border: 2px solid rgba(0,0,0,0.2); 380 | background-clip: padding-box; 381 | } 382 | 383 | 384 | /* popup */ 385 | 386 | .leaflet-popup { 387 | position: absolute; 388 | text-align: center; 389 | } 390 | .leaflet-popup-content-wrapper { 391 | padding: 1px; 392 | text-align: left; 393 | border-radius: 12px; 394 | } 395 | .leaflet-popup-content { 396 | margin: 13px 19px; 397 | line-height: 1.4; 398 | } 399 | .leaflet-popup-content p { 400 | margin: 18px 0; 401 | } 402 | .leaflet-popup-tip-container { 403 | margin: 0 auto; 404 | width: 40px; 405 | height: 20px; 406 | position: relative; 407 | overflow: hidden; 408 | } 409 | .leaflet-popup-tip { 410 | width: 17px; 411 | height: 17px; 412 | padding: 1px; 413 | 414 | margin: -10px auto 0; 415 | 416 | -webkit-transform: rotate(45deg); 417 | -moz-transform: rotate(45deg); 418 | -ms-transform: rotate(45deg); 419 | -o-transform: rotate(45deg); 420 | transform: rotate(45deg); 421 | } 422 | .leaflet-popup-content-wrapper, 423 | .leaflet-popup-tip { 424 | background: white; 425 | 426 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 427 | } 428 | .leaflet-container a.leaflet-popup-close-button { 429 | position: absolute; 430 | top: 0; 431 | right: 0; 432 | padding: 4px 4px 0 0; 433 | text-align: center; 434 | width: 18px; 435 | height: 14px; 436 | font: 16px/14px Tahoma, Verdana, sans-serif; 437 | color: #c3c3c3; 438 | text-decoration: none; 439 | font-weight: bold; 440 | background: transparent; 441 | } 442 | .leaflet-container a.leaflet-popup-close-button:hover { 443 | color: #999; 444 | } 445 | .leaflet-popup-scrolled { 446 | overflow: auto; 447 | border-bottom: 1px solid #ddd; 448 | border-top: 1px solid #ddd; 449 | } 450 | 451 | .leaflet-oldie .leaflet-popup-content-wrapper { 452 | zoom: 1; 453 | } 454 | .leaflet-oldie .leaflet-popup-tip { 455 | width: 24px; 456 | margin: 0 auto; 457 | 458 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 459 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 460 | } 461 | .leaflet-oldie .leaflet-popup-tip-container { 462 | margin-top: -1px; 463 | } 464 | 465 | .leaflet-oldie .leaflet-control-zoom, 466 | .leaflet-oldie .leaflet-control-layers, 467 | .leaflet-oldie .leaflet-popup-content-wrapper, 468 | .leaflet-oldie .leaflet-popup-tip { 469 | border: 1px solid #999; 470 | } 471 | 472 | 473 | /* div icon */ 474 | 475 | .leaflet-div-icon { 476 | background: #fff; 477 | border: 1px solid #666; 478 | } 479 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Custom 3 | */ 4 | 5 | html, 6 | body { 7 | height: 100%; 8 | background-color: white; 9 | -webkit-touch-callout: none; 10 | -webkit-user-select: none; 11 | -khtml-user-select: none; 12 | -moz-user-select: none; 13 | -ms-user-select: none; 14 | user-select: none; 15 | } 16 | body { 17 | color: #333; 18 | text-align: left; 19 | text-shadow: 0 1px 3px rgba(0,0,0,.5); 20 | } 21 | 22 | html, body, .container, #map { 23 | height: 100%; 24 | width: 100%; 25 | padding: 0px; 26 | margin: 0px; 27 | } 28 | 29 | #map { 30 | z-index: 0; 31 | width: 100%; 32 | height: 100%; 33 | position: absolute; 34 | top: 0px; 35 | left: 0px; 36 | } 37 | 38 | #slider { 39 | width: 100%; 40 | } 41 | 42 | .description { 43 | display: none; 44 | } 45 | 46 | /* 47 | * Globals 48 | */ 49 | 50 | /* Links */ 51 | a { 52 | color: purple; 53 | padding: 5px; 54 | } 55 | a:focus, 56 | a:hover { 57 | color: white; 58 | background-color: purple; 59 | padding: 5px; 60 | text-decoration: none; 61 | } 62 | 63 | /* Custom default button */ 64 | .btn-default, 65 | .btn-default:hover, 66 | .btn-default:focus { 67 | color: #333; 68 | text-shadow: none; /* Prevent inheritence from `body` */ 69 | background-color: #fff; 70 | border: 1px solid #00FFFF; 71 | } 72 | 73 | /* Extra markup and styles for table-esque vertical and horizontal centering */ 74 | .site-wrapper { 75 | z-index: 999; 76 | display: table; 77 | min-height: 100%; 78 | height: auto !important; 79 | height: 100%; 80 | /* Negative indent footer by its height */ 81 | margin: 0 auto -60px; 82 | /* Pad bottom by footer height */ 83 | padding: 0 0 60px; 84 | } 85 | 86 | .site-wrapper-inner { 87 | display: table-cell; 88 | vertical-align: top; 89 | } 90 | .cover-container { 91 | margin-right: auto; 92 | margin-left: auto; 93 | z-index: 999; 94 | } 95 | 96 | /* Padding for spacing */ 97 | .inner { 98 | padding: 30px; 99 | } 100 | 101 | 102 | /* 103 | * Header 104 | */ 105 | .masthead-brand { 106 | margin-top: 10px; 107 | margin-bottom: 10px; 108 | } 109 | 110 | .masthead-nav > li { 111 | display: inline-block; 112 | } 113 | .masthead-nav > li + li { 114 | margin-left: 20px; 115 | } 116 | .masthead-nav > li > a { 117 | padding-right: 0; 118 | padding-left: 0; 119 | font-size: 16px; 120 | font-weight: bold; 121 | color: #00FFFF; /* IE8 proofing */ 122 | color: rgba(255,255,255,.75); 123 | border-bottom: 2px solid transparent; 124 | } 125 | .masthead-nav > li > a:hover, 126 | .masthead-nav > li > a:focus { 127 | background-color: transparent; 128 | border-bottom-color: #a9a9a9; 129 | border-bottom-color: rgba(255,255,255,.25); 130 | } 131 | .masthead-nav > .active > a, 132 | .masthead-nav > .active > a:hover, 133 | .masthead-nav > .active > a:focus { 134 | color: #00FFFF; 135 | border-bottom-color: #00FFFF; 136 | } 137 | 138 | @media (min-width: 768px) { 139 | .masthead-brand { 140 | float: left; 141 | } 142 | .masthead-nav { 143 | float: right; 144 | } 145 | } 146 | 147 | /* 148 | * Cover 149 | */ 150 | 151 | .cover { 152 | z-index: 999; 153 | position: fixed; 154 | padding: 0 20px; 155 | } 156 | .cover .btn-lg { 157 | padding: 10px 20px; 158 | font-weight: bold; 159 | } 160 | 161 | /* 162 | * Footer 163 | */ 164 | 165 | .mastfoot { 166 | color: #333; /* IE8 proofing */ 167 | text-align: center; 168 | } 169 | 170 | 171 | /* 172 | * Affix and center 173 | */ 174 | 175 | @media (min-width: 768px) { 176 | /* Pull out the header and footer */ 177 | .masthead { 178 | position: fixed; 179 | top: 0; 180 | } 181 | .mastfoot { 182 | position: fixed; 183 | bottom: 0; 184 | } 185 | /* Start the vertical centering */ 186 | .site-wrapper-inner { 187 | vertical-align: middle; 188 | } 189 | /* Handle the widths */ 190 | .masthead, 191 | .mastfoot, 192 | .cover-container { 193 | width: 100%; /* Must be percentage or pixels for horizontal alignment */ 194 | } 195 | } 196 | 197 | @media (min-width: 992px) { 198 | .masthead, 199 | .mastfoot, 200 | .cover-container { 201 | width: 700px; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /css/slider_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugocore/spatiotemporal-visualizer/8fed56f2463d797fe7c438bc51364b1758374fdd/css/slider_bar.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SIGR 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |
52 | 53 |
54 | 55 |
56 | 57 |
58 |
59 |

Spatio temporal visualizer

60 |
61 |
62 |
63 |

Loading data, please wait ...

64 |
65 |
66 |

Following ... users in Beijing over the period of ... hours. More info.

67 | 68 | 69 |
70 |
71 |
72 | 73 |
74 | 75 |
76 | 77 |
78 |
79 |
80 |

Spatio temporal data visualizer using D3.js, Leaflet.js and Crossfilter by @hugocore

81 |
82 |
83 | 84 |
85 | 86 |
87 | 88 |
89 | 90 |
91 | 92 | 93 |