Projekt

Obecné

Profil

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