Projekt

Obecné

Profil

Stáhnout (22.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 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 894e3339 Martin Sebela
const areCoordsIdentical = (first, second) => {
81 e6097215 ballakt
  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 894e3339 Martin Sebela
  const popupData = `<div id="place-intesity"><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 894e3339 Martin Sebela
    <div id="place-intesity">
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 894e3339 Martin Sebela
      Object.keys(item).forEach(datasetName => {
219 1cf1413d ballakt
        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 894e3339 Martin Sebela
299 2f227a6c ballakt
  if (infoKeys.length === 1) {
300
    // return number of records in one dataset (one dataset in area)
301
    return info[infoKeys[0]].items.length
302
  }
303
  // return number of datasets (agregation of all datasets in area)
304
  return infoKeys.length
305
}
306 c892003d Martin Sebela
307 894e3339 Martin Sebela
const getPopupDataOnPage = (pageInPopup) => {
308 2f227a6c ballakt
  const keys = Object.keys(info)
309 894e3339 Martin Sebela
  return info[keys[pageInPopup]]
310 2f227a6c ballakt
}
311 c892003d Martin Sebela
312 894e3339 Martin Sebela
const areMultipleDatasetsInRadius = () => {
313 2f227a6c ballakt
  return Object.keys(info).length > 1
314
}
315 c892003d Martin Sebela
316 894e3339 Martin Sebela
const setPopupDatasetClassName = (datasetName) => {
317 2c2cad8d Martin Sebela
  const popup = $('.leaflet-popup')
318
319
  popup.removeClass(function (index, css) {
320 894e3339 Martin Sebela
    return (css.match(/(^|\s)popup-\S+/g) || []).join(' ');
321 2c2cad8d Martin Sebela
  })
322 894e3339 Martin Sebela
  popup.addClass('popup-' + datasetName)
323 2c2cad8d Martin Sebela
}
324
325 8feb1753 ballakt
function showInfo (e) {
326 3ae59f75 vastja
  info = []
327 1a1d8f64 Martin Sebela
  currentPageInPopup = 0
328 3ae59f75 vastja
329
  // https://wiki.openstreetmap.org/wiki/Zoom_levels
330
  // Todo change to variable - it is used in heatmap init
331 2f227a6c ballakt
  const stile = 40075016.686 * Math.cos(startX) / Math.pow(2, mymap.getZoom())
332
  const radius = 25 * stile / 256
333
334
  let i = 0
335
  let lat = 0
336
  let lng = 0
337
338
  let total = 0
339
340
  const datasetsInRadius = {}
341 e6097215 ballakt
  const eventCoord = {
342
    lng: e.latlng.lng,
343
    lat: e.latlng.lat
344
  }
345 894e3339 Martin Sebela
346
  Object.keys(data[currentTime]).forEach(key => {
347 2f227a6c ballakt
    const namedData = data[currentTime][key]
348 894e3339 Martin Sebela
349 2f227a6c ballakt
    namedData.items.forEach(element => {
350
      if (e.latlng.distanceTo(new L.LatLng(element.x, element.y)) < radius) {
351
        lat += element.x
352
        lng += element.y
353
        info[i] = { place: element.place, number: element.number, datasetName: key }
354
        total += parseInt(element.number)
355
        i++
356
        datasetsInRadius[key] = true
357
      }
358
    })
359 8feb1753 ballakt
  })
360 1a1d8f64 Martin Sebela
361 2f227a6c ballakt
  // Process info for more then one dataset
362
  info = info.reduce((acc, item) => {
363
    if (!acc[item.datasetName]) {
364
      acc[item.datasetName] = {
365
        items: [],
366
        number: 0,
367
        datasetName: item.datasetName
368
      }
369
    }
370 c892003d Martin Sebela
371 2f227a6c ballakt
    acc[item.datasetName].items.push(item)
372
    acc[item.datasetName].number += Number(item.number)
373
    return acc
374
  }, {})
375
376 894e3339 Martin Sebela
  const countDatasets = Object.keys(datasetsInRadius).length
377 1a1d8f64 Martin Sebela
378 894e3339 Martin Sebela
  if (!countDatasets) {
379 e6097215 ballakt
    if (mymap._popup) {
380
      $('#part-info').text('')
381
      $('#current-number').html(0)
382
      disablePopupControls()
383
    }
384
385 d5a88af0 Tomáš Ballák
    return
386
  }
387 1a1d8f64 Martin Sebela
388 894e3339 Martin Sebela
  if (countDatasets === 1) {
389
    const markersInRadius = getPopupDataOnPage(0)
390
    const popupPagesData = markersInRadius.items
391
    const { place, number } = popupPagesData[currentPageInPopup]
392 e6097215 ballakt
393 894e3339 Martin Sebela
    if (!globalPopup._popup || !areCoordsIdentical(globalPopup.coord, eventCoord)) {
394
      globalPopup._popup = prepareLayerPopUp(lat, lng, i, `popup-${markersInRadius.datasetName}`)
395 e6097215 ballakt
      globalPopup.coord = eventCoord
396
    }
397 2c2cad8d Martin Sebela
    else {
398 894e3339 Martin Sebela
      setPopupDatasetClassName(markersInRadius.datasetName)
399 2c2cad8d Martin Sebela
    }
400 e6097215 ballakt
401 894e3339 Martin Sebela
    setGlobalPopupContent(getPopupContent(datasetDictNameDisplayName[markersInRadius.datasetName], place, number, total, 1, popupPagesData.length))
402 c892003d Martin Sebela
403 894e3339 Martin Sebela
    if (popupPagesData.length === 1) {
404 e6097215 ballakt
      disablePopupControls()
405 3ae59f75 vastja
    }
406 1774c06d Tomáš Ballák
  } else {
407 894e3339 Martin Sebela
    const { datasetName, number } = getPopupDataOnPage(currentPageInPopup)
408 c892003d Martin Sebela
409 894e3339 Martin Sebela
    if (!globalPopup._popup || !areCoordsIdentical(globalPopup.coord, eventCoord)) {
410 e6097215 ballakt
      globalPopup._popup = prepareLayerPopUp(lat, lng, i, `popup-${datasetName}`)
411
      globalPopup.coord = eventCoord
412
    }
413 2c2cad8d Martin Sebela
    else {
414 894e3339 Martin Sebela
      setPopupDatasetClassName(datasetName)
415 2c2cad8d Martin Sebela
    }
416 e6097215 ballakt
417 1a1d8f64 Martin Sebela
    setGlobalPopupContent(genMultipleDatasetsPopUp(number, 1, getCountPagesInPopup(), datasetDictNameDisplayName[datasetName]))
418 3ae59f75 vastja
  }
419
}
420
421 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
422 1a1d8f64 Martin Sebela
function setPreviousPageInPopup () {
423
  const countPagesInPopup = getCountPagesInPopup()
424 ac154afa Martin Sebela
  const page = currentPageInPopup
425 c892003d Martin Sebela
426 1a1d8f64 Martin Sebela
  currentPageInPopup = (currentPageInPopup + countPagesInPopup - 1) % countPagesInPopup
427 ac154afa Martin Sebela
  setPageContentInPopup(page)
428 3ae59f75 vastja
}
429
430 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
431 1a1d8f64 Martin Sebela
function setNextPageInPopup () {
432
  const countPagesInPopup = getCountPagesInPopup()
433 ac154afa Martin Sebela
  const page = currentPageInPopup
434 c892003d Martin Sebela
435 1a1d8f64 Martin Sebela
  currentPageInPopup = (currentPageInPopup + 1) % countPagesInPopup
436 ac154afa Martin Sebela
  setPageContentInPopup(page)
437 3ae59f75 vastja
}
438 c892003d Martin Sebela
439 1a1d8f64 Martin Sebela
function setPageContentInPopup (page) {
440 894e3339 Martin Sebela
  const previousPageData = areMultipleDatasetsInRadius() ? getPopupDataOnPage(page) : getPopupDataOnPage(0).items[page]
441
  const currentPageData = areMultipleDatasetsInRadius() ? getPopupDataOnPage(currentPageInPopup) : getPopupDataOnPage(0).items[currentPageInPopup]
442 1a1d8f64 Martin Sebela
  const datasetName = $('#dataset-name')
443 c892003d Martin Sebela
444 1a1d8f64 Martin Sebela
  if (datasetName) {
445
    datasetName.html(datasetDictNameDisplayName[currentPageData.datasetName])
446 883a423e Tomáš Ballák
  }
447 1774c06d Tomáš Ballák
448 1a1d8f64 Martin Sebela
  $('#place-name').html(currentPageData.place ? currentPageData.place : currentPageData.datasetName)
449
  $('#current-number').html(currentPageData.number)
450
  $('#pages').html(currentPageInPopup + 1 + ' z ' + getCountPagesInPopup())
451 c892003d Martin Sebela
452 1a1d8f64 Martin Sebela
  $('.leaflet-popup').removeClass(`popup-${previousPageData.datasetName}`).addClass(`popup-${currentPageData.datasetName}`)
453 3ae59f75 vastja
}
454 351696d5 Martin Sebela
455 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
456 72a438f3 vastja
function setMapView (latitude, longitude, zoom) {
457 90d3db28 Tomáš Ballák
  localStorage.setItem('lat', latitude)
458
  localStorage.setItem('lng', longitude)
459
  localStorage.setItem('zoom', zoom)
460 1a1d8f64 Martin Sebela
461 8feb1753 ballakt
  mymap.setView([latitude, longitude], zoom)
462 3fc08f2d vastja
}
463
464 70a3df53 vastja
/**
465
 * Change animation start from playing to stopped or the other way round
466
 */
467 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
468
function changeAnimationState () {
469 1a1d8f64 Martin Sebela
  const btnAnimate = $('#animate-btn')
470
471 a48642fb vastja
  isAnimationRunning = !isAnimationRunning
472 c892003d Martin Sebela
473 a48642fb vastja
  if (isAnimationRunning) {
474 1a1d8f64 Martin Sebela
    btnAnimate.removeClass('play').addClass('pause')
475 1774c06d Tomáš Ballák
    timer = setInterval(function () { next() }, 800)
476
  } else {
477 8feb1753 ballakt
    clearTimeout(timer)
478 1a1d8f64 Martin Sebela
    btnAnimate.removeClass('pause').addClass('play')
479 351696d5 Martin Sebela
  }
480
}
481
482 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
483 1774c06d Tomáš Ballák
async function previous () {
484
  if (loading) {
485
    return
486
  }
487 ec5e3220 Martin Sebela
488 8feb1753 ballakt
  currentTime = (currentTime + 23) % 24
489 1774c06d Tomáš Ballák
  changeHour(currentTime)
490 1a1d8f64 Martin Sebela
  
491
  if (!lockedDay && currentTime === 23) {
492 1774c06d Tomáš Ballák
    addDayToCurrentDate(-1)
493
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
494
  } else {
495
    drawHeatmap(data[currentTime])
496
  }
497 1a1d8f64 Martin Sebela
498 e6097215 ballakt
  updatePopup()
499 a48642fb vastja
}
500
501 1774c06d Tomáš Ballák
async function next () {
502
  if (loading) {
503
    return
504
  }
505 ec5e3220 Martin Sebela
506 8feb1753 ballakt
  currentTime = (currentTime + 1) % 24
507 1774c06d Tomáš Ballák
  changeHour(currentTime)
508 1a1d8f64 Martin Sebela
509
  if (!lockedDay && currentTime === 0) {
510 1774c06d Tomáš Ballák
    addDayToCurrentDate(1)
511
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
512
  } else {
513
    drawHeatmap(data[currentTime])
514
  }
515 1a1d8f64 Martin Sebela
516 e6097215 ballakt
  updatePopup()
517 8b840eb7 vastja
}
518 c892003d Martin Sebela
519 70a3df53 vastja
/**
520 c892003d Martin Sebela
 * Change browser url based on animation step.
521 70a3df53 vastja
 */
522 1a1d8f64 Martin Sebela
const changeUrlParameters = () => {
523 8b840eb7 vastja
  window.history.pushState(
524 8feb1753 ballakt
    '',
525 8b840eb7 vastja
    document.title,
526 ca88914a Martin Sebela
    window.location.origin + window.location.pathname + `?date=${currentDateToString()}&time=${currentTime}${datasetSelected.reduce((acc, current) => acc + '&type[]=' + current, '')}`
527 8feb1753 ballakt
  )
528 4e8c0e5b Martin Sebela
}
529
530 ac154afa Martin Sebela
const formatTime = (hours, twoDigitsHours = false) => {
531
  return ((twoDigitsHours && hours < 10) ? '0' : '') + hours + ':00';
532
}
533
534 1a1d8f64 Martin Sebela
const updateHeaderControls = () => {
535 1cf1413d ballakt
  $(`#time_${currentTime}`).prop('checked', true)
536 ac154afa Martin Sebela
  $('#dropdownMenuButtonTime').html(formatTime(currentTime, true))
537 a48642fb vastja
}
538 351696d5 Martin Sebela
539 1a1d8f64 Martin Sebela
const setTimeline = () => {
540 ac154afa Martin Sebela
  $('#player-time > span').text(formatTime(currentTime))
541 863ca316 Martin Sebela
  $('#player-time').attr('class', 'time hour-' + currentTime)
542 351696d5 Martin Sebela
}
543 fdba469a Martin Sebela
544 d51166e8 ballakt
const loadingTimeline = (isLoading) => {
545
  if (isLoading) {
546
    loadingYTimeline()
547
  } else {
548
    loadingNTimeline()
549
  }
550
}
551 1a1d8f64 Martin Sebela
552 d51166e8 ballakt
const loadingYTimeline = () => {
553
  $('#player-time > .spinner-border').removeClass('d-none')
554
  $('#player-time > span').text('')
555
}
556 1a1d8f64 Martin Sebela
557 d51166e8 ballakt
const loadingNTimeline = () => {
558
  $('#player-time > .spinner-border').addClass('d-none')
559
  setTimeline()
560
}
561 1a1d8f64 Martin Sebela
562 1774c06d Tomáš Ballák
const onChangeHour = (hour) => {
563
  changeHour(hour)
564
  drawHeatmap(data[currentTime])
565
}
566 863ca316 Martin Sebela
567 1774c06d Tomáš Ballák
const changeHour = (hour) => {
568 815159f3 Tomáš Ballák
  $('#player-time').removeAttr('style')
569 1774c06d Tomáš Ballák
  changeCurrentTime(hour)
570 c892003d Martin Sebela
  updateHeaderControls()
571
  setTimeline()
572 1a1d8f64 Martin Sebela
  changeUrlParameters()
573 e6097215 ballakt
  updatePopup()
574 c892003d Martin Sebela
}
575
576 e6097215 ballakt
const updatePopup = () => {
577
  const { _popup } = mymap
578 1a1d8f64 Martin Sebela
579 e6097215 ballakt
  if (_popup) {
580
    showInfo({
581
      latlng: _popup.getLatLng()
582 1a1d8f64 Martin Sebela
    })
583 e6097215 ballakt
  }
584
}
585 1a1d8f64 Martin Sebela
586 70a3df53 vastja
/**
587
 * Load and display heatmap layer for current data
588
 * @param {string} opendataRoute route to dataset source
589
 * @param {string} positionsRoute  route to dataset postitions source
590
 */
591 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
592 d51166e8 ballakt
async function loadCurrentTimeHeatmap (opendataRoute, positionsRoute, loaderDelay = defaultLoaderDelay) {
593 1cf1413d ballakt
  loadCheckboxDatasetNameData()
594 c892003d Martin Sebela
595 8feb1753 ballakt
  dataSourceRoute = opendataRoute
596 1cf1413d ballakt
  positionsSourceRoute = positionsRoute
597 2f227a6c ballakt
  const allPromises = []
598
  data[currentTime] = {}
599 1774c06d Tomáš Ballák
600 2f227a6c ballakt
  const dataSelectedHandler = async (datasetName) => {
601 1774c06d Tomáš Ballák
    if (!(datasetName in dataSourceMarks)) {
602
      dataSourceMarks[datasetName] = await fetchDataSourceMarks(positionsRoute, datasetName)
603
    }
604 1a1d8f64 Martin Sebela
605 1774c06d Tomáš Ballák
    const datasetData = await fetchByNameDate(dataSourceRoute, datasetName, currentDateToString(), currentTime)
606 2f227a6c ballakt
    data[currentTime][datasetName] = datasetData
607
  }
608 1774c06d Tomáš Ballák
  datasetSelected.forEach((datasetName) => {
609 2f227a6c ballakt
    allPromises.push(dataSelectedHandler(datasetName))
610 8feb1753 ballakt
  })
611 c892003d Martin Sebela
612 d51166e8 ballakt
  loadingY(loaderDelay)
613 1a1d8f64 Martin Sebela
614 e6097215 ballakt
  await Promise.all(allPromises).then(
615 2f227a6c ballakt
    () => {
616 d51166e8 ballakt
      loadingN(0)
617 2f227a6c ballakt
      drawDataSourceMarks(dataSourceMarks)
618
      drawHeatmap(data[currentTime])
619 1774c06d Tomáš Ballák
      preload(currentTime, 1, currentDateToString())
620
      preload(currentTime, -1, currentDateToString())
621 2f227a6c ballakt
    }
622
  )
623 a48642fb vastja
}
624
625 8feb1753 ballakt
function drawDataSourceMarks (data) {
626 61ff7718 vastja
  if (marksLayer != null) {
627 1cf1413d ballakt
    mymap.removeLayer(marksLayer)
628 61ff7718 vastja
  }
629 c892003d Martin Sebela
630 8feb1753 ballakt
  marksLayer = L.layerGroup()
631 c892003d Martin Sebela
632 2f227a6c ballakt
  Object.keys(data).forEach((key_) => {
633
    for (var key in data[key_]) {
634
      const { x, y, name } = data[key_][key]
635
      const pop =
636
          prepareLayerPopUp(x, y, 1, `popup-${key_}`)
637 1a1d8f64 Martin Sebela
            .setContent(getPopupContent(datasetDictNameDisplayName[key_], name, 0, 0, 1, 1))
638 2f227a6c ballakt
      const newCircle =
639
        L.circle([x, y], { radius: 2, fillOpacity: 0.8, color: '#004fb3', fillColor: '#004fb3', bubblingMouseEvents: true })
640
          .bindPopup(pop)
641
      globalMarkersHolder[x + '' + y] = [newCircle, pop] // add new marker to global holders
642
      marksLayer.addLayer(
643
        newCircle
644
      )
645
    }
646
  })
647 61ff7718 vastja
648 8feb1753 ballakt
  marksLayer.setZIndex(-1).addTo(mymap)
649 61ff7718 vastja
}
650
651 2f227a6c ballakt
async function preload (time, change, date) {
652 1774c06d Tomáš Ballák
  loadingY()
653 1a1d8f64 Martin Sebela
654 2f227a6c ballakt
  for (let nTime = time + change; nTime >= 0 && nTime <= 23; nTime = nTime + change) {
655
    if (!data[nTime]) {
656
      data[nTime] = {}
657
    }
658 c892003d Martin Sebela
659 1cf1413d ballakt
    datasetSelected.forEach(async (datasetName) => {
660
      if (!data[nTime][datasetName]) {
661
        data[nTime][datasetName] = await fetchByNameDate(dataSourceRoute, datasetName, date, nTime)
662
      }
663
    })
664 a48642fb vastja
  }
665 1a1d8f64 Martin Sebela
666 1774c06d Tomáš Ballák
  loadingN()
667 3fc08f2d vastja
}
668
669 2f227a6c ballakt
function drawHeatmap (dataRaw) {
670 03c02899 vastja
  // Todo still switched
671 2f227a6c ballakt
  const dataDict = dataRaw
672
  const mergedPoints = []
673
  let max = 0
674 1774c06d Tomáš Ballák
675 d5a88af0 Tomáš Ballák
  if (Object.keys(globalMarkersChanged).length) {
676
    Object.keys(globalMarkersChanged).forEach(function (key) {
677
      globalMarkersChanged[key][0].bindPopup(globalMarkersChanged[key][1])
678
    })
679
    globalMarkersChanged = {}
680
  }
681 c892003d Martin Sebela
682 2f227a6c ballakt
  Object.keys(dataDict).forEach((key) => {
683
    const data = dataDict[key]
684
    max = Math.max(max, data.max)
685 c892003d Martin Sebela
686 2f227a6c ballakt
    if (data != null) {
687 8feb1753 ballakt
    // Bind back popups for markers (we dont know if there is any data for this marker or not)
688 2f227a6c ballakt
      const points = data.items.map((point) => {
689
        const { x, y, number } = point
690
        const key = x + '' + y
691
        const holder = globalMarkersHolder[key]
692
        if (!globalMarkersChanged[key] && number) {
693 e6097215 ballakt
          // There is data for this marker => unbind popup with zero value
694 2f227a6c ballakt
          holder[0] = holder[0].unbindPopup()
695
          globalMarkersChanged[key] = holder
696
        }
697 c892003d Martin Sebela
698 2f227a6c ballakt
        return [x, y, number]
699
      })
700
      mergedPoints.push(...points)
701 1774c06d Tomáš Ballák
    } else {
702 2f227a6c ballakt
      if (heatmapLayer != null) {
703
        mymap.removeLayer(heatmapLayer)
704 084a5972 ballakt
      }
705 a48642fb vastja
    }
706 2f227a6c ballakt
  })
707 c892003d Martin Sebela
708 2f227a6c ballakt
  if (heatmapLayer != null) {
709
    mymap.removeLayer(heatmapLayer)
710
  }
711 c892003d Martin Sebela
712 2f227a6c ballakt
  if (mergedPoints.length) {
713
    heatmapLayer = L.heatLayer(mergedPoints, { max: max, minOpacity: 0.5, radius: 35, blur: 30 }).addTo(mymap)
714 a48642fb vastja
  }
715 ac154afa Martin Sebela
716
  // timto vyresen bug #8191 - TODO: znamena to, ze muzeme smazat volani updatePopup() ve funkcich, kde se nejdriv vola drawHeatmap() a pak updatePopup()?
717
  updatePopup()
718 03c02899 vastja
}
719 3fc08f2d vastja
720 70a3df53 vastja
/**
721
 * Checks dataset availibility
722 81980e82 ballakt
 * @param {string} route authority for datasets availibility checks
723 70a3df53 vastja
 */
724 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
725
function checkDataSetsAvailability (route) {
726 03c02899 vastja
  $.ajax({
727 8feb1753 ballakt
    type: 'POST',
728 03c02899 vastja
    // Todo it might be good idea to change db collections format
729 1774c06d Tomáš Ballák
    url: route + '/' + currentDateToString(),
730 8feb1753 ballakt
    success: function (result) {
731
      updateAvailableDataSets(result)
732 03c02899 vastja
    }
733 8feb1753 ballakt
  })
734 03c02899 vastja
}
735
736 8feb1753 ballakt
function updateAvailableDataSets (available) {
737 2f227a6c ballakt
  let leastOneOptionEnabled = false
738 c892003d Martin Sebela
739
  $('#dropdown-dataset .dropdown-item').each(function () {
740 2f227a6c ballakt
    const input = $(this).find('input')
741
    const inputVal = input[0].value
742 c892003d Martin Sebela
743 2f227a6c ballakt
    if (!(inputVal in available)) {
744
      $(this).addClass('disabled')
745
      $(input).prop('checked', false)
746 1774c06d Tomáš Ballák
    } else {
747 2f227a6c ballakt
      leastOneOptionEnabled = true
748
      $(this).removeClass('disabled')
749 5d599617 vastja
    }
750 8feb1753 ballakt
  })
751 dfe43218 vastja
752 94c6eb49 Martin Sebela
  $('#btn-update-heatmap').prop('disabled', !leastOneOptionEnabled)
753 03c02899 vastja
}
754 0a828a5a Martin Sebela
755 8feb1753 ballakt
function formatDate (date) {
756
  var day = String(date.getDate())
757
  var month = String(date.getMonth() + 1)
758 0a828a5a Martin Sebela
759
  if (day.length === 1) {
760 8feb1753 ballakt
    day = '0' + day
761 0a828a5a Martin Sebela
  }
762
763
  if (month.length === 1) {
764 8feb1753 ballakt
    month = '0' + month
765 0a828a5a Martin Sebela
  }
766
767 8feb1753 ballakt
  return date.getFullYear() + '-' + month + '-' + day
768 0a828a5a Martin Sebela
}
769
770 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
771
function initDatepicker (availableDatesSource) {
772
  var availableDates = ''
773 0a828a5a Martin Sebela
774
  $.ajax({
775
    type: 'GET',
776
    url: availableDatesSource,
777 8feb1753 ballakt
    success: function (result) {
778
      availableDates = String(result).split(',')
779 0a828a5a Martin Sebela
    }
780 a7e04778 Martin Sebela
  }).then(function () {
781
    $('#date').datepicker({
782
      format: 'yyyy-mm-dd',
783
      language: 'cs',
784 8feb1753 ballakt
      beforeShowDay: function (date) {
785 a7e04778 Martin Sebela
        if (availableDates.indexOf(formatDate(date)) < 0) {
786 8feb1753 ballakt
          return { enabled: false, tooltip: 'Žádná data' }
787 1774c06d Tomáš Ballák
        } else {
788 8feb1753 ballakt
          return { enabled: true }
789 a7e04778 Martin Sebela
        }
790
      },
791
      autoclose: true
792 8feb1753 ballakt
    })
793
  })
794
}
795 dd652e61 Martin Sebela
796 81980e82 ballakt
function initLocationsMenu () {
797 1a1d8f64 Martin Sebela
  const elmLocationsList = $('.locations')
798
  const locationsDisplayClass = 'show'
799 dd652e61 Martin Sebela
800 81980e82 ballakt
  if ($(window).width() <= 480) {
801 1a1d8f64 Martin Sebela
    elmLocationsList.removeClass(locationsDisplayClass)
802 1774c06d Tomáš Ballák
  } else {
803 1a1d8f64 Martin Sebela
    elmLocationsList.addClass(locationsDisplayClass)
804 dd652e61 Martin Sebela
  }
805 4e003182 Martin Sebela
}
806
807 2f227a6c ballakt
function onDocumentReady () {
808 c892003d Martin Sebela
  $('#dropdown-dataset').on('click', function (e) {
809 2f227a6c ballakt
    e.stopPropagation()
810
  })
811 1cf1413d ballakt
812 94c6eb49 Martin Sebela
  $('#btn-update-heatmap').prop('name', '')
813 1774c06d Tomáš Ballák
  changeCurrentTime()
814
  changeCurrentDate()
815 1cf1413d ballakt
  onValueChangeRegister()
816 815159f3 Tomáš Ballák
  onArrowLeftRightKeysDownRegister()
817 1cf1413d ballakt
}
818 c892003d Martin Sebela
819 1cf1413d ballakt
const loadCheckboxDatasetNameData = () => {
820 2f227a6c ballakt
  datasetSelected = []
821 1a1d8f64 Martin Sebela
822 c892003d Martin Sebela
  $('#dropdown-dataset .dropdown-item').each(function () {
823 2f227a6c ballakt
    const input = $(this).find('input')
824
    const inputVal = input[0].value
825 c892003d Martin Sebela
826 2f227a6c ballakt
    if (input[0].checked) {
827
      datasetSelected.push(inputVal)
828
    }
829 c892003d Martin Sebela
830 883a423e Tomáš Ballák
    datasetDictNameDisplayName[inputVal] = $(input).data('dataset-display-name')
831 2f227a6c ballakt
  })
832
}
833 ec5e3220 Martin Sebela
834 1a1d8f64 Martin Sebela
const dragTimeline = () => {
835 815159f3 Tomáš Ballák
  const hourElemWidthPx = 26
836 ec5e3220 Martin Sebela
837 815159f3 Tomáš Ballák
  const elem = $('#player-time')
838
  const offset = elem.offset().left - elem.parent().offset().left
839 ec5e3220 Martin Sebela
840
  if (offset >= 0 && offset <= elem.parent().width()) {
841 815159f3 Tomáš Ballák
    const hour = Math.round(offset / hourElemWidthPx)
842
843 1a1d8f64 Martin Sebela
    if (hour !== currentTime) {
844 fdba469a Martin Sebela
      elem.attr('class', 'time hour-' + hour)
845 ac154afa Martin Sebela
      $('#player-time span').html(formatTime(hour))
846 ec5e3220 Martin Sebela
847 fdba469a Martin Sebela
      onChangeHour(hour)
848
    }
849 ec5e3220 Martin Sebela
  }
850 815159f3 Tomáš Ballák
}