Projekt

Obecné

Profil

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