Projekt

Obecné

Profil

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