Projekt

Obecné

Profil

Stáhnout (20.1 KB) Statistiky
| Větev: | Revize:
1 8feb1753 ballakt
/* global L */
2
/* global $ */
3 2f227a6c ballakt
4 8feb1753 ballakt
var mymap
5
var heatmapLayer = null
6
var marksLayer = null
7 03c02899 vastja
8 815159f3 Tomáš Ballák
// values for arrow keys
9
const arrowKeyLEFT = 37
10
const arrowKeyRIGHT = 39
11
12 8feb1753 ballakt
var startX = 49.7248
13
var startY = 13.3521
14
var startZoom = 17
15 3fc08f2d vastja
16 8feb1753 ballakt
var dataSourceRoute
17 1cf1413d ballakt
let positionsSourceRoute
18 1774c06d Tomáš Ballák
19
let currentTime
20
21
let currentDate
22 a48642fb vastja
23 8feb1753 ballakt
var timer
24
var isAnimationRunning = false
25
var data = []
26 a48642fb vastja
27 70a3df53 vastja
var info = []
28 2f227a6c ballakt
let currentInfo = 0
29
30 d51166e8 ballakt
// dictionary for names of datasets
31 883a423e Tomáš Ballák
const datasetDictNameDisplayName = {}
32 2f227a6c ballakt
var datasetSelected = []
33 70a3df53 vastja
34 1774c06d Tomáš Ballák
// data only for one day
35
let lockedDay = false
36
37 d51166e8 ballakt
// loading information for async operations
38 1774c06d Tomáš Ballák
let loading = 0
39
40 d51166e8 ballakt
// default loader showup delay
41
const defaultLoaderDelay = 1000
42
43 1774c06d Tomáš Ballák
// marks for all datasets
44
const dataSourceMarks = {}
45
46 8feb1753 ballakt
const globalMarkersHolder = {}
47 d5a88af0 Tomáš Ballák
// all marker from which popup was removed
48
// contains: {key:[L.circle,L.pupup]}
49
// key: x and y, x + '' + y string
50
let globalMarkersChanged = {}
51 084a5972 ballakt
52 d51166e8 ballakt
const loadingCallbackNested = (func, delay) => {
53
  setTimeout(() => {
54
    func(loading)
55
    if (loading) {
56
      loadingCallbackNested(func, delay)
57
    }
58
  }, delay)
59
}
60
const loadingY = (delay = defaultLoaderDelay) => {
61 1774c06d Tomáš Ballák
  loading++
62 d51166e8 ballakt
  // check after nms if there is something that is loading
63
  loadingCallbackNested(() => loadingCallbackNested((isLoading) => loadingTimeline(isLoading), delay))
64 1774c06d Tomáš Ballák
}
65 d51166e8 ballakt
const loadingN = (delay = defaultLoaderDelay) => {
66 1774c06d Tomáš Ballák
  loading--
67 d51166e8 ballakt
  loadingCallbackNested(() => loadingCallbackNested((isLoading) => loadingTimeline(isLoading)), delay)
68 1774c06d Tomáš Ballák
}
69
70
const changeCurrentTime = (time = null) => {
71
  if (time !== null) {
72
    currentTime = time
73
  } else {
74 863ca316 Martin Sebela
    currentTime = parseInt($('#dropdown-time input[type="radio"]:checked').val())
75 1774c06d Tomáš Ballák
  }
76
}
77
78
const changeCurrentDate = (date = null) => {
79
  if (date) {
80
    currentDate = new Date(date)
81
  } else {
82
    currentDate = new Date($('#date').val())
83
  }
84 863ca316 Martin Sebela
85 1774c06d Tomáš Ballák
  $('#date').val(currentDateToString())
86 863ca316 Martin Sebela
  $('#player-date span').html(`${currentDate.getDate()}. ${currentDate.getMonth() + 1}. ${currentDate.getFullYear()}`)
87
88 1774c06d Tomáš Ballák
  data = []
89
}
90
const currentDayToString = () => {
91
  const day = currentDate.getDate()
92
  return day > 9 ? `${day}` : `0${day}`
93
}
94
const currentMonthToString = () => {
95
  const month = currentDate.getMonth() + 1
96
  return month > 9 ? `${month}` : `0${month}`
97
}
98
const currentDateToString = () => `${currentDate.getFullYear()}-${currentMonthToString()}-${currentDayToString()}`
99
const addDayToCurrentDate = (day) => {
100
  currentDate.setDate(currentDate.getDate() + day)
101
  changeCurrentDate(currentDate)
102
}
103
const toggleDayLock = () => {
104
  lockedDay = !lockedDay
105
  $('#player-date').toggleClass('lock')
106
}
107 c892003d Martin Sebela
108 2f227a6c ballakt
const fetchByNameDate = async (baseRoute, name, date, currentTime) => {
109
  const headers = new Headers()
110
  const myRequest = new Request(baseRoute + '/' + name + '/' + date + '/' + currentTime, {
111
    method: 'GET',
112
    headers: headers
113
  })
114
  const beforeJson = await fetch(myRequest)
115
  return beforeJson.json()
116
}
117 c892003d Martin Sebela
118 2f227a6c ballakt
const fetchDataSourceMarks = async (positionRoute, datasetName) => {
119
  const headers = new Headers()
120
  const myRequest = new Request(positionRoute + '/' + datasetName, {
121
    method: 'GET',
122
    headers: headers
123
  })
124
  const beforeJson = await fetch(myRequest)
125
  return beforeJson.json()
126
}
127 084a5972 ballakt
128 2f227a6c ballakt
const genPopUpControlButtons = (currentPage, numPages, onNextClick, onPreviousClick) => ({
129
  previousButton: '<button id="previous-info-btn" class="circle-button" onclick="previousInfo()"></button>',
130 c892003d Martin Sebela
  nextButton: '<button id="next-info-btn" class="circle-button next" onclick="nextInfo()"></button>',
131 2f227a6c ballakt
  posInfo: `<div id="count-info">${currentPage} z ${numPages}</div>`
132
})
133 c892003d Martin Sebela
134 084a5972 ballakt
const genPopUpControls = (controls) => {
135 2f227a6c ballakt
  return `<div class="popup-controls">${controls ? controls.reduce((sum, item) => sum + item, '') : ''}</div>`
136 084a5972 ballakt
}
137 c892003d Martin Sebela
138 883a423e Tomáš Ballák
const genMultipleDatasetsPopUp = (sum, currentPos, maxPos, datasetName) => {
139 c892003d Martin Sebela
  const popupHeader = `<strong id="dataset-info">${datasetName}</strong>`
140
  const popupData = `<div id="number-info"><span id="digit-info">${sum}</span></div>`
141 2f227a6c ballakt
  const { previousButton, nextButton, posInfo } = genPopUpControlButtons(currentPos, maxPos)
142 c892003d Martin Sebela
143 2f227a6c ballakt
  return `
144 c892003d Martin Sebela
  ${popupHeader}
145
  ${popupData}
146 2f227a6c ballakt
  ${genPopUpControls([previousButton, posInfo, nextButton])}
147
  `
148
}
149 c892003d Martin Sebela
150 2f227a6c ballakt
const prepareLayerPopUp = (lat, lng, num, className) => L.popup({
151
  autoPan: false,
152
  className: className
153
}).setLatLng([lat / num, lng / num])
154
155 bb2d43b5 Martin Sebela
const genPopUp = (datasetName, place, count, sum, currentPos, maxPos) => {
156
  const popupHeader = `
157
    <strong>${datasetName}</strong>
158
    <div id="place-info">${place}</div>`
159
  const popupData = `
160
    <div id="number-info">
161
      <span id="digit-info">${count}</span>
162
      <span id="total-info">${(sum && (sum != count)) ? '/' + sum : ''}</span>
163
    </div>`
164 2f227a6c ballakt
  const { previousButton, nextButton, posInfo } = genPopUpControlButtons(currentPos, maxPos)
165 bb2d43b5 Martin Sebela
166 084a5972 ballakt
  return `
167 bb2d43b5 Martin Sebela
  ${popupHeader}
168
  ${popupData}
169 2f227a6c ballakt
  ${genPopUpControls(maxPos > 1 ? [previousButton, posInfo, nextButton] : null)}
170 084a5972 ballakt
  `
171
}
172 c892003d Martin Sebela
173 1cf1413d ballakt
const onCheckboxClicked = async (checkbox) => {
174
  if ($(checkbox).prop('checked')) {
175 d51166e8 ballakt
    loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute, 0)
176 1cf1413d ballakt
    changeUrl()
177 1774c06d Tomáš Ballák
  } else {
178 1cf1413d ballakt
    loadCheckboxDatasetNameData()
179 c892003d Martin Sebela
180 1cf1413d ballakt
    data.forEach((item, index) => {
181
      Object.keys(item).forEach((datasetName) => {
182
        if (datasetName === $(checkbox).val()) {
183
          delete data[index][datasetName]
184
        }
185
      })
186
      drawHeatmap(data[currentTime])
187
    })
188 c892003d Martin Sebela
189 1cf1413d ballakt
    changeUrl()
190
  }
191
}
192 c892003d Martin Sebela
193 815159f3 Tomáš Ballák
const onArrowLeftRightKeysDownRegister = () => {
194
  $(document).keydown(function (e) {
195
    const { which } = e
196 fdba469a Martin Sebela
    
197 815159f3 Tomáš Ballák
    if (which === arrowKeyLEFT) {
198
      previous()
199 fdba469a Martin Sebela
      e.preventDefault()
200 815159f3 Tomáš Ballák
    } else if (which === arrowKeyRIGHT) {
201
      next()
202 fdba469a Martin Sebela
      e.preventDefault()
203 815159f3 Tomáš Ballák
    }
204
  })
205
}
206
207 1cf1413d ballakt
const debounce = (func, delay) => {
208
  let inDebounce
209
  return function () {
210
    const context = this
211
    const args = arguments
212
    clearTimeout(inDebounce)
213
    inDebounce = setTimeout(() => func.apply(context, args), delay)
214
  }
215
}
216 bb2d43b5 Martin Sebela
217 1cf1413d ballakt
const onValueChangeRegister = () => {
218
  $('#date').change(function () {
219 1774c06d Tomáš Ballák
    changeCurrentDate($(this).val())
220 d51166e8 ballakt
    loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute, 0)
221 bb2d43b5 Martin Sebela
    changeUrl()
222 1cf1413d ballakt
  })
223 bb2d43b5 Martin Sebela
224 c892003d Martin Sebela
  $('#dropdown-time input[type="radio"]').each(function () {
225 1cf1413d ballakt
    $(this).change(function () {
226 1774c06d Tomáš Ballák
      changeHour(parseInt($(this).val()))
227 1cf1413d ballakt
      drawHeatmap(data[currentTime])
228
    })
229
  })
230 bb2d43b5 Martin Sebela
231 c892003d Martin Sebela
  $('#dropdown-dataset input[type="checkbox"]').each(function () {
232 1cf1413d ballakt
    $(this).change(
233
      debounce(() => onCheckboxClicked(this), 1000)
234
    )
235
  })
236
}
237 bb2d43b5 Martin Sebela
238 70a3df53 vastja
/**
239
 * Initialize leaflet map on start position which can be default or set based on user action
240
 */
241 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
242
function initMap () {
243 90d3db28 Tomáš Ballák
  startX = localStorage.getItem('lat') || startX
244
  startY = localStorage.getItem('lng') || startY
245
  startZoom = localStorage.getItem('zoom') || startZoom
246 72a438f3 vastja
247 8feb1753 ballakt
  mymap = L.map('heatmap').setView([startX, startY], startZoom)
248 3fc08f2d vastja
249 c236b33a msebela
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
250
    attribution: '',
251
    maxZoom: 19
252 8feb1753 ballakt
  }).addTo(mymap)
253 3ae59f75 vastja
254 8feb1753 ballakt
  mymap.on('click', showInfo)
255 c236b33a msebela
}
256 c892003d Martin Sebela
257 2f227a6c ballakt
const getInfoLength = () => {
258
  const infoKeys = Object.keys(info)
259
  if (infoKeys.length === 1) {
260
    // return number of records in one dataset (one dataset in area)
261
    return info[infoKeys[0]].items.length
262
  }
263
  // return number of datasets (agregation of all datasets in area)
264
  return infoKeys.length
265
}
266 c892003d Martin Sebela
267 2f227a6c ballakt
const getElFromObjectInfo = (position) => {
268
  const keys = Object.keys(info)
269
  return info[keys[position]]
270
}
271 c892003d Martin Sebela
272 2f227a6c ballakt
const hasInfoMultipleDatasets = () => {
273
  return Object.keys(info).length > 1
274
}
275 c892003d Martin Sebela
276 8feb1753 ballakt
function showInfo (e) {
277 3ae59f75 vastja
  info = []
278 2f227a6c ballakt
  currentInfo = 0
279 3ae59f75 vastja
280
  // https://wiki.openstreetmap.org/wiki/Zoom_levels
281
  // Todo change to variable - it is used in heatmap init
282 2f227a6c ballakt
  const stile = 40075016.686 * Math.cos(startX) / Math.pow(2, mymap.getZoom())
283
  const radius = 25 * stile / 256
284
285
  let i = 0
286
  let lat = 0
287
  let lng = 0
288
289
  let total = 0
290
291
  const datasetsInRadius = {}
292
293
  Object.keys(data[currentTime]).forEach((key) => {
294
    const namedData = data[currentTime][key]
295
    namedData.items.forEach(element => {
296
      if (e.latlng.distanceTo(new L.LatLng(element.x, element.y)) < radius) {
297
        lat += element.x
298
        lng += element.y
299
        info[i] = { place: element.place, number: element.number, datasetName: key }
300
        total += parseInt(element.number)
301
        i++
302
        datasetsInRadius[key] = true
303
      }
304
    })
305 8feb1753 ballakt
  })
306 3ae59f75 vastja
307 2f227a6c ballakt
  // Process info for more then one dataset
308 8feb1753 ballakt
309 2f227a6c ballakt
  info = info.reduce((acc, item) => {
310
    if (!acc[item.datasetName]) {
311
      acc[item.datasetName] = {
312
        items: [],
313
        number: 0,
314
        datasetName: item.datasetName
315
      }
316
    }
317 c892003d Martin Sebela
318 2f227a6c ballakt
    acc[item.datasetName].items.push(item)
319
    acc[item.datasetName].number += Number(item.number)
320
    return acc
321
  }, {})
322
323
  // There is one dataset
324
325
  const numDatasets = Object.keys(datasetsInRadius).length
326
327 d5a88af0 Tomáš Ballák
  if (!numDatasets) {
328
    return
329
  }
330 2f227a6c ballakt
331
  if (numDatasets === 1) {
332
    const infoDict = getElFromObjectInfo(0)
333
    const info_ = infoDict.items
334
    const { place, number } = info_[currentInfo]
335
    prepareLayerPopUp(lat, lng, i, `popup-${infoDict.datasetName}`)
336 883a423e Tomáš Ballák
      .setContent(genPopUp(datasetDictNameDisplayName[infoDict.datasetName], place, number, total, currentInfo + 1, info_.length))
337 2f227a6c ballakt
      .openOn(mymap)
338 c892003d Martin Sebela
339 2f227a6c ballakt
    if (info_.length === 1) {
340 8feb1753 ballakt
      $('#previous-info-btn').prop('disabled', true)
341
      $('#next-info-btn').prop('disabled', true)
342
      $('.popup-controls').hide()
343 3ae59f75 vastja
    }
344 1774c06d Tomáš Ballák
  } else {
345 2f227a6c ballakt
    const { datasetName, number } = getElFromObjectInfo(currentInfo)
346 c892003d Martin Sebela
347 2f227a6c ballakt
    prepareLayerPopUp(lat, lng, i, `popup-${datasetName}`)
348 883a423e Tomáš Ballák
      .setContent(genMultipleDatasetsPopUp(number, currentInfo + 1, getInfoLength(), datasetDictNameDisplayName[datasetName]))
349 2f227a6c ballakt
      .openOn(mymap)
350 3ae59f75 vastja
  }
351
}
352
353 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
354
function previousInfo () {
355 2f227a6c ballakt
  const infoLength = getInfoLength()
356
  const previousCurrentInfo = currentInfo
357 c892003d Martin Sebela
358 2f227a6c ballakt
  currentInfo = (currentInfo + infoLength - 1) % infoLength
359
  displayInfoText(previousCurrentInfo)
360 3ae59f75 vastja
}
361
362 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
363
function nextInfo () {
364 2f227a6c ballakt
  const infoLength = getInfoLength()
365
  const previousCurrentInfo = currentInfo
366 c892003d Martin Sebela
367 2f227a6c ballakt
  currentInfo = (currentInfo + 1) % infoLength
368
  displayInfoText(previousCurrentInfo)
369 3ae59f75 vastja
}
370 c892003d Martin Sebela
371 2f227a6c ballakt
function displayInfoText (previousInfoNum) {
372
  const previousInfo = hasInfoMultipleDatasets() ? getElFromObjectInfo(previousInfoNum) : getElFromObjectInfo(0).items[previousInfoNum]
373
  const info_ = hasInfoMultipleDatasets() ? getElFromObjectInfo(currentInfo) : getElFromObjectInfo(0).items[currentInfo]
374
  const infoLength = getInfoLength()
375 883a423e Tomáš Ballák
  const datasetInfo = $('#dataset-info')
376 c892003d Martin Sebela
377 883a423e Tomáš Ballák
  if (datasetInfo) {
378
    $(datasetInfo).html(datasetDictNameDisplayName[info_.datasetName])
379
  }
380 1774c06d Tomáš Ballák
381 2f227a6c ballakt
  $('#place-info').html(info_.place ? info_.place : info_.datasetName)
382
  $('#digit-info').html(info_.number)
383
  $('#count-info').html(currentInfo + 1 + ' z ' + infoLength)
384 c892003d Martin Sebela
385 2f227a6c ballakt
  $('.leaflet-popup').removeClass(`popup-${previousInfo.datasetName}`)
386
  $('.leaflet-popup').addClass(`popup-${info_.datasetName}`)
387 3ae59f75 vastja
}
388 351696d5 Martin Sebela
389 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
390 72a438f3 vastja
function setMapView (latitude, longitude, zoom) {
391 90d3db28 Tomáš Ballák
  localStorage.setItem('lat', latitude)
392
  localStorage.setItem('lng', longitude)
393
  localStorage.setItem('zoom', zoom)
394 8feb1753 ballakt
  mymap.setView([latitude, longitude], zoom)
395 3fc08f2d vastja
}
396
397 70a3df53 vastja
/**
398
 * Change animation start from playing to stopped or the other way round
399
 */
400 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
401
function changeAnimationState () {
402 a48642fb vastja
  isAnimationRunning = !isAnimationRunning
403 c892003d Martin Sebela
404 a48642fb vastja
  if (isAnimationRunning) {
405 863ca316 Martin Sebela
    $('#animate-btn').removeClass('play').addClass('pause')
406 1774c06d Tomáš Ballák
    timer = setInterval(function () { next() }, 800)
407
  } else {
408 8feb1753 ballakt
    clearTimeout(timer)
409 863ca316 Martin Sebela
    $('#animate-btn').removeClass('pause').addClass('play')
410 351696d5 Martin Sebela
  }
411
}
412
413 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
414 1774c06d Tomáš Ballák
async function previous () {
415
  if (loading) {
416
    return
417
  }
418 ec5e3220 Martin Sebela
419 8feb1753 ballakt
  currentTime = (currentTime + 23) % 24
420 1774c06d Tomáš Ballák
  changeHour(currentTime)
421 8feb1753 ballakt
  mymap.closePopup()
422 1774c06d Tomáš Ballák
  if (!lockedDay && (currentTime === 23)) {
423
    addDayToCurrentDate(-1)
424
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
425
  } else {
426
    drawHeatmap(data[currentTime])
427
  }
428 a48642fb vastja
}
429
430 1774c06d Tomáš Ballák
async function next () {
431
  if (loading) {
432
    return
433
  }
434 ec5e3220 Martin Sebela
435 8feb1753 ballakt
  currentTime = (currentTime + 1) % 24
436 1774c06d Tomáš Ballák
  changeHour(currentTime)
437 8feb1753 ballakt
  mymap.closePopup()
438 1774c06d Tomáš Ballák
  if (!lockedDay && (currentTime === 0)) {
439
    addDayToCurrentDate(1)
440
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
441
  } else {
442
    drawHeatmap(data[currentTime])
443
  }
444 8b840eb7 vastja
}
445 c892003d Martin Sebela
446 70a3df53 vastja
/**
447 c892003d Martin Sebela
 * Change browser url based on animation step.
448 70a3df53 vastja
 */
449 8feb1753 ballakt
function changeUrl () {
450 8b840eb7 vastja
  window.history.pushState(
451 8feb1753 ballakt
    '',
452 8b840eb7 vastja
    document.title,
453 1774c06d Tomáš Ballák
    window.location.origin + window.location.pathname + `?date=${currentDateToString()}&time=${currentTime}${datasetSelected.reduce((acc, current) => acc + '&type=' + current, '')}`
454 8feb1753 ballakt
  )
455 4e8c0e5b Martin Sebela
}
456
457 8feb1753 ballakt
function updateHeaderControls () {
458 1cf1413d ballakt
  $(`#time_${currentTime}`).prop('checked', true)
459 bb2d43b5 Martin Sebela
  $('#dropdownMenuButtonTime').html((currentTime < 10 ? '0' : '') + `${currentTime}:00`)
460 a48642fb vastja
}
461 351696d5 Martin Sebela
462 8feb1753 ballakt
function setTimeline () {
463 d51166e8 ballakt
  $('#player-time > span').text(currentTime + ':00')
464 863ca316 Martin Sebela
  $('#player-time').attr('class', 'time hour-' + currentTime)
465 351696d5 Martin Sebela
}
466 fdba469a Martin Sebela
467 d51166e8 ballakt
const loadingTimeline = (isLoading) => {
468
  if (isLoading) {
469
    loadingYTimeline()
470
  } else {
471
    loadingNTimeline()
472
  }
473
}
474
const loadingYTimeline = () => {
475
  $('#player-time > .spinner-border').removeClass('d-none')
476
  $('#player-time > span').text('')
477
}
478
const loadingNTimeline = () => {
479
  $('#player-time > .spinner-border').addClass('d-none')
480
  setTimeline()
481
}
482 1774c06d Tomáš Ballák
const onChangeHour = (hour) => {
483
  changeHour(hour)
484
  drawHeatmap(data[currentTime])
485
}
486 863ca316 Martin Sebela
487 1774c06d Tomáš Ballák
const changeHour = (hour) => {
488 815159f3 Tomáš Ballák
  $('#player-time').removeAttr('style')
489 1774c06d Tomáš Ballák
  changeCurrentTime(hour)
490 c892003d Martin Sebela
  updateHeaderControls()
491
  setTimeline()
492
  changeUrl()
493
}
494
495 70a3df53 vastja
/**
496
 * Load and display heatmap layer for current data
497
 * @param {string} opendataRoute route to dataset source
498
 * @param {string} positionsRoute  route to dataset postitions source
499
 */
500 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
501 d51166e8 ballakt
async function loadCurrentTimeHeatmap (opendataRoute, positionsRoute, loaderDelay = defaultLoaderDelay) {
502 1cf1413d ballakt
  loadCheckboxDatasetNameData()
503 c892003d Martin Sebela
504 8feb1753 ballakt
  dataSourceRoute = opendataRoute
505 1cf1413d ballakt
  positionsSourceRoute = positionsRoute
506 2f227a6c ballakt
  const allPromises = []
507
  data[currentTime] = {}
508 1774c06d Tomáš Ballák
509 2f227a6c ballakt
  const dataSelectedHandler = async (datasetName) => {
510 1774c06d Tomáš Ballák
    if (!(datasetName in dataSourceMarks)) {
511
      dataSourceMarks[datasetName] = await fetchDataSourceMarks(positionsRoute, datasetName)
512
    }
513
    const datasetData = await fetchByNameDate(dataSourceRoute, datasetName, currentDateToString(), currentTime)
514 2f227a6c ballakt
    data[currentTime][datasetName] = datasetData
515
  }
516 1774c06d Tomáš Ballák
  datasetSelected.forEach((datasetName) => {
517 2f227a6c ballakt
    allPromises.push(dataSelectedHandler(datasetName))
518 8feb1753 ballakt
  })
519 c892003d Martin Sebela
520 d51166e8 ballakt
  loadingY(loaderDelay)
521 2f227a6c ballakt
  Promise.all(allPromises).then(
522
    () => {
523 d51166e8 ballakt
      loadingN(0)
524 2f227a6c ballakt
      drawDataSourceMarks(dataSourceMarks)
525
      drawHeatmap(data[currentTime])
526 1774c06d Tomáš Ballák
      preload(currentTime, 1, currentDateToString())
527
      preload(currentTime, -1, currentDateToString())
528 2f227a6c ballakt
    }
529
  )
530 a48642fb vastja
}
531
532 8feb1753 ballakt
function drawDataSourceMarks (data) {
533 61ff7718 vastja
  if (marksLayer != null) {
534 1cf1413d ballakt
    mymap.removeLayer(marksLayer)
535 61ff7718 vastja
  }
536 c892003d Martin Sebela
537 8feb1753 ballakt
  marksLayer = L.layerGroup()
538 c892003d Martin Sebela
539 2f227a6c ballakt
  Object.keys(data).forEach((key_) => {
540
    for (var key in data[key_]) {
541
      const { x, y, name } = data[key_][key]
542
      const pop =
543
          prepareLayerPopUp(x, y, 1, `popup-${key_}`)
544 883a423e Tomáš Ballák
            .setContent(genPopUp(datasetDictNameDisplayName[key_], name, 0, 0, 1, 1))
545 2f227a6c ballakt
      const newCircle =
546
        L.circle([x, y], { radius: 2, fillOpacity: 0.8, color: '#004fb3', fillColor: '#004fb3', bubblingMouseEvents: true })
547
          .bindPopup(pop)
548
      globalMarkersHolder[x + '' + y] = [newCircle, pop] // add new marker to global holders
549
      marksLayer.addLayer(
550
        newCircle
551
      )
552
    }
553
  })
554 61ff7718 vastja
555 8feb1753 ballakt
  marksLayer.setZIndex(-1).addTo(mymap)
556 61ff7718 vastja
}
557
558 2f227a6c ballakt
async function preload (time, change, date) {
559 1774c06d Tomáš Ballák
  loadingY()
560 2f227a6c ballakt
  for (let nTime = time + change; nTime >= 0 && nTime <= 23; nTime = nTime + change) {
561
    if (!data[nTime]) {
562
      data[nTime] = {}
563
    }
564 c892003d Martin Sebela
565 1cf1413d ballakt
    datasetSelected.forEach(async (datasetName) => {
566
      if (!data[nTime][datasetName]) {
567
        data[nTime][datasetName] = await fetchByNameDate(dataSourceRoute, datasetName, date, nTime)
568
      }
569
    })
570 a48642fb vastja
  }
571 1774c06d Tomáš Ballák
  loadingN()
572 3fc08f2d vastja
}
573
574 2f227a6c ballakt
function drawHeatmap (dataRaw) {
575 03c02899 vastja
  // Todo still switched
576 2f227a6c ballakt
  const dataDict = dataRaw
577
  const mergedPoints = []
578
  let max = 0
579 1774c06d Tomáš Ballák
580 d5a88af0 Tomáš Ballák
  if (Object.keys(globalMarkersChanged).length) {
581
    Object.keys(globalMarkersChanged).forEach(function (key) {
582
      globalMarkersChanged[key][0].bindPopup(globalMarkersChanged[key][1])
583
    })
584
    globalMarkersChanged = {}
585
  }
586 c892003d Martin Sebela
587 2f227a6c ballakt
  Object.keys(dataDict).forEach((key) => {
588
    const data = dataDict[key]
589
    max = Math.max(max, data.max)
590 c892003d Martin Sebela
591 2f227a6c ballakt
    if (data != null) {
592 8feb1753 ballakt
    // Bind back popups for markers (we dont know if there is any data for this marker or not)
593 2f227a6c ballakt
      const points = data.items.map((point) => {
594
        const { x, y, number } = point
595
        const key = x + '' + y
596
        const holder = globalMarkersHolder[key]
597 c892003d Martin Sebela
598 2f227a6c ballakt
        if (!globalMarkersChanged[key] && number) {
599 8feb1753 ballakt
        // There is data for this marker => unbind popup with zero value
600 2f227a6c ballakt
          holder[0] = holder[0].unbindPopup()
601
          globalMarkersChanged[key] = holder
602
        }
603 c892003d Martin Sebela
604 2f227a6c ballakt
        return [x, y, number]
605
      })
606
      mergedPoints.push(...points)
607 1774c06d Tomáš Ballák
    } else {
608 2f227a6c ballakt
      if (heatmapLayer != null) {
609
        mymap.removeLayer(heatmapLayer)
610 084a5972 ballakt
      }
611 a48642fb vastja
    }
612 2f227a6c ballakt
  })
613 c892003d Martin Sebela
614 2f227a6c ballakt
  if (heatmapLayer != null) {
615
    mymap.removeLayer(heatmapLayer)
616
  }
617 c892003d Martin Sebela
618 2f227a6c ballakt
  if (mergedPoints.length) {
619
    heatmapLayer = L.heatLayer(mergedPoints, { max: max, minOpacity: 0.5, radius: 35, blur: 30 }).addTo(mymap)
620 a48642fb vastja
  }
621 03c02899 vastja
}
622 3fc08f2d vastja
623 70a3df53 vastja
/**
624
 * Checks dataset availibility
625 81980e82 ballakt
 * @param {string} route authority for datasets availibility checks
626 70a3df53 vastja
 */
627 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
628
function checkDataSetsAvailability (route) {
629 03c02899 vastja
  $.ajax({
630 8feb1753 ballakt
    type: 'POST',
631 03c02899 vastja
    // Todo it might be good idea to change db collections format
632 1774c06d Tomáš Ballák
    url: route + '/' + currentDateToString(),
633 8feb1753 ballakt
    success: function (result) {
634
      updateAvailableDataSets(result)
635 03c02899 vastja
    }
636 8feb1753 ballakt
  })
637 03c02899 vastja
}
638
639 8feb1753 ballakt
function updateAvailableDataSets (available) {
640 2f227a6c ballakt
  let leastOneOptionEnabled = false
641 c892003d Martin Sebela
642
  $('#dropdown-dataset .dropdown-item').each(function () {
643 2f227a6c ballakt
    const input = $(this).find('input')
644
    const inputVal = input[0].value
645 c892003d Martin Sebela
646 2f227a6c ballakt
    if (!(inputVal in available)) {
647
      $(this).addClass('disabled')
648
      $(input).prop('checked', false)
649 1774c06d Tomáš Ballák
    } else {
650 2f227a6c ballakt
      leastOneOptionEnabled = true
651
      $(this).removeClass('disabled')
652 5d599617 vastja
    }
653 8feb1753 ballakt
  })
654 dfe43218 vastja
655 94c6eb49 Martin Sebela
  $('#btn-update-heatmap').prop('disabled', !leastOneOptionEnabled)
656 03c02899 vastja
}
657 0a828a5a Martin Sebela
658 8feb1753 ballakt
function formatDate (date) {
659
  var day = String(date.getDate())
660
  var month = String(date.getMonth() + 1)
661 0a828a5a Martin Sebela
662
  if (day.length === 1) {
663 8feb1753 ballakt
    day = '0' + day
664 0a828a5a Martin Sebela
  }
665
666
  if (month.length === 1) {
667 8feb1753 ballakt
    month = '0' + month
668 0a828a5a Martin Sebela
  }
669
670 8feb1753 ballakt
  return date.getFullYear() + '-' + month + '-' + day
671 0a828a5a Martin Sebela
}
672
673 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
674
function initDatepicker (availableDatesSource) {
675
  var availableDates = ''
676 0a828a5a Martin Sebela
677
  $.ajax({
678
    type: 'GET',
679
    url: availableDatesSource,
680 8feb1753 ballakt
    success: function (result) {
681
      availableDates = String(result).split(',')
682 0a828a5a Martin Sebela
    }
683 a7e04778 Martin Sebela
  }).then(function () {
684
    $('#date').datepicker({
685
      format: 'yyyy-mm-dd',
686
      language: 'cs',
687 8feb1753 ballakt
      beforeShowDay: function (date) {
688 a7e04778 Martin Sebela
        if (availableDates.indexOf(formatDate(date)) < 0) {
689 8feb1753 ballakt
          return { enabled: false, tooltip: 'Žádná data' }
690 1774c06d Tomáš Ballák
        } else {
691 8feb1753 ballakt
          return { enabled: true }
692 a7e04778 Martin Sebela
        }
693
      },
694
      autoclose: true
695 8feb1753 ballakt
    })
696
  })
697
}
698 dd652e61 Martin Sebela
699 81980e82 ballakt
function initLocationsMenu () {
700
  var locationsWrapper = '.locations'
701
  var locationsDisplayClass = 'show'
702 dd652e61 Martin Sebela
703 81980e82 ballakt
  if ($(window).width() <= 480) {
704
    $(locationsWrapper).removeClass(locationsDisplayClass)
705 1774c06d Tomáš Ballák
  } else {
706 81980e82 ballakt
    $(locationsWrapper).addClass(locationsDisplayClass)
707 dd652e61 Martin Sebela
  }
708 4e003182 Martin Sebela
}
709
710 2f227a6c ballakt
function onDocumentReady () {
711 c892003d Martin Sebela
  $('#dropdown-dataset').on('click', function (e) {
712 2f227a6c ballakt
    e.stopPropagation()
713
  })
714 1cf1413d ballakt
715 94c6eb49 Martin Sebela
  $('#btn-update-heatmap').prop('name', '')
716 1774c06d Tomáš Ballák
  changeCurrentTime()
717
  changeCurrentDate()
718 1cf1413d ballakt
  onValueChangeRegister()
719 815159f3 Tomáš Ballák
  onArrowLeftRightKeysDownRegister()
720 1cf1413d ballakt
}
721 c892003d Martin Sebela
722 1cf1413d ballakt
const loadCheckboxDatasetNameData = () => {
723 2f227a6c ballakt
  datasetSelected = []
724 c892003d Martin Sebela
  $('#dropdown-dataset .dropdown-item').each(function () {
725 2f227a6c ballakt
    const input = $(this).find('input')
726
    const inputVal = input[0].value
727 c892003d Martin Sebela
728 2f227a6c ballakt
    if (input[0].checked) {
729
      datasetSelected.push(inputVal)
730
    }
731 c892003d Martin Sebela
732 883a423e Tomáš Ballák
    datasetDictNameDisplayName[inputVal] = $(input).data('dataset-display-name')
733 2f227a6c ballakt
  })
734
}
735 ec5e3220 Martin Sebela
736 815159f3 Tomáš Ballák
function dragTimeline () {
737
  const hourElemWidthPx = 26
738 ec5e3220 Martin Sebela
739 815159f3 Tomáš Ballák
  const elem = $('#player-time')
740
  const offset = elem.offset().left - elem.parent().offset().left
741 ec5e3220 Martin Sebela
742
  if (offset >= 0 && offset <= elem.parent().width()) {
743 815159f3 Tomáš Ballák
    const hour = Math.round(offset / hourElemWidthPx)
744
745 fdba469a Martin Sebela
    if (hour != currentTime) {
746
      elem.attr('class', 'time hour-' + hour)
747
      $('#player-time span').html(hour + ':00')
748 ec5e3220 Martin Sebela
749 fdba469a Martin Sebela
      onChangeHour(hour)
750
    }
751 ec5e3220 Martin Sebela
  }
752 815159f3 Tomáš Ballák
}