Projekt

Obecné

Profil

Stáhnout (21.7 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 ac154afa Martin Sebela
  const page = currentPageInPopup
408 c892003d Martin Sebela
409 1a1d8f64 Martin Sebela
  currentPageInPopup = (currentPageInPopup + countPagesInPopup - 1) % countPagesInPopup
410 ac154afa Martin Sebela
  setPageContentInPopup(page)
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 ac154afa Martin Sebela
  const page = currentPageInPopup
417 c892003d Martin Sebela
418 1a1d8f64 Martin Sebela
  currentPageInPopup = (currentPageInPopup + 1) % countPagesInPopup
419 ac154afa Martin Sebela
  setPageContentInPopup(page)
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 ac154afa Martin Sebela
const formatTime = (hours, twoDigitsHours = false) => {
514
  return ((twoDigitsHours && hours < 10) ? '0' : '') + hours + ':00';
515
}
516
517 1a1d8f64 Martin Sebela
const updateHeaderControls = () => {
518 1cf1413d ballakt
  $(`#time_${currentTime}`).prop('checked', true)
519 ac154afa Martin Sebela
  $('#dropdownMenuButtonTime').html(formatTime(currentTime, true))
520 a48642fb vastja
}
521 351696d5 Martin Sebela
522 1a1d8f64 Martin Sebela
const setTimeline = () => {
523 ac154afa Martin Sebela
  $('#player-time > span').text(formatTime(currentTime))
524 863ca316 Martin Sebela
  $('#player-time').attr('class', 'time hour-' + currentTime)
525 351696d5 Martin Sebela
}
526 fdba469a Martin Sebela
527 d51166e8 ballakt
const loadingTimeline = (isLoading) => {
528
  if (isLoading) {
529
    loadingYTimeline()
530
  } else {
531
    loadingNTimeline()
532
  }
533
}
534 1a1d8f64 Martin Sebela
535 d51166e8 ballakt
const loadingYTimeline = () => {
536
  $('#player-time > .spinner-border').removeClass('d-none')
537
  $('#player-time > span').text('')
538
}
539 1a1d8f64 Martin Sebela
540 d51166e8 ballakt
const loadingNTimeline = () => {
541
  $('#player-time > .spinner-border').addClass('d-none')
542
  setTimeline()
543
}
544 1a1d8f64 Martin Sebela
545 1774c06d Tomáš Ballák
const onChangeHour = (hour) => {
546
  changeHour(hour)
547
  drawHeatmap(data[currentTime])
548
}
549 863ca316 Martin Sebela
550 1774c06d Tomáš Ballák
const changeHour = (hour) => {
551 815159f3 Tomáš Ballák
  $('#player-time').removeAttr('style')
552 1774c06d Tomáš Ballák
  changeCurrentTime(hour)
553 c892003d Martin Sebela
  updateHeaderControls()
554
  setTimeline()
555 1a1d8f64 Martin Sebela
  changeUrlParameters()
556 e6097215 ballakt
  updatePopup()
557 c892003d Martin Sebela
}
558
559 e6097215 ballakt
const updatePopup = () => {
560
  const { _popup } = mymap
561 1a1d8f64 Martin Sebela
562 e6097215 ballakt
  if (_popup) {
563
    showInfo({
564
      latlng: _popup.getLatLng()
565 1a1d8f64 Martin Sebela
    })
566 e6097215 ballakt
  }
567
}
568 1a1d8f64 Martin Sebela
569 70a3df53 vastja
/**
570
 * Load and display heatmap layer for current data
571
 * @param {string} opendataRoute route to dataset source
572
 * @param {string} positionsRoute  route to dataset postitions source
573
 */
574 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
575 d51166e8 ballakt
async function loadCurrentTimeHeatmap (opendataRoute, positionsRoute, loaderDelay = defaultLoaderDelay) {
576 1cf1413d ballakt
  loadCheckboxDatasetNameData()
577 c892003d Martin Sebela
578 8feb1753 ballakt
  dataSourceRoute = opendataRoute
579 1cf1413d ballakt
  positionsSourceRoute = positionsRoute
580 2f227a6c ballakt
  const allPromises = []
581
  data[currentTime] = {}
582 1774c06d Tomáš Ballák
583 2f227a6c ballakt
  const dataSelectedHandler = async (datasetName) => {
584 1774c06d Tomáš Ballák
    if (!(datasetName in dataSourceMarks)) {
585
      dataSourceMarks[datasetName] = await fetchDataSourceMarks(positionsRoute, datasetName)
586
    }
587 1a1d8f64 Martin Sebela
588 1774c06d Tomáš Ballák
    const datasetData = await fetchByNameDate(dataSourceRoute, datasetName, currentDateToString(), currentTime)
589 2f227a6c ballakt
    data[currentTime][datasetName] = datasetData
590
  }
591 1774c06d Tomáš Ballák
  datasetSelected.forEach((datasetName) => {
592 2f227a6c ballakt
    allPromises.push(dataSelectedHandler(datasetName))
593 8feb1753 ballakt
  })
594 c892003d Martin Sebela
595 d51166e8 ballakt
  loadingY(loaderDelay)
596 1a1d8f64 Martin Sebela
597 e6097215 ballakt
  await Promise.all(allPromises).then(
598 2f227a6c ballakt
    () => {
599 d51166e8 ballakt
      loadingN(0)
600 2f227a6c ballakt
      drawDataSourceMarks(dataSourceMarks)
601
      drawHeatmap(data[currentTime])
602 1774c06d Tomáš Ballák
      preload(currentTime, 1, currentDateToString())
603
      preload(currentTime, -1, currentDateToString())
604 2f227a6c ballakt
    }
605
  )
606 a48642fb vastja
}
607
608 8feb1753 ballakt
function drawDataSourceMarks (data) {
609 61ff7718 vastja
  if (marksLayer != null) {
610 1cf1413d ballakt
    mymap.removeLayer(marksLayer)
611 61ff7718 vastja
  }
612 c892003d Martin Sebela
613 8feb1753 ballakt
  marksLayer = L.layerGroup()
614 c892003d Martin Sebela
615 2f227a6c ballakt
  Object.keys(data).forEach((key_) => {
616
    for (var key in data[key_]) {
617
      const { x, y, name } = data[key_][key]
618
      const pop =
619
          prepareLayerPopUp(x, y, 1, `popup-${key_}`)
620 1a1d8f64 Martin Sebela
            .setContent(getPopupContent(datasetDictNameDisplayName[key_], name, 0, 0, 1, 1))
621 2f227a6c ballakt
      const newCircle =
622
        L.circle([x, y], { radius: 2, fillOpacity: 0.8, color: '#004fb3', fillColor: '#004fb3', bubblingMouseEvents: true })
623
          .bindPopup(pop)
624
      globalMarkersHolder[x + '' + y] = [newCircle, pop] // add new marker to global holders
625
      marksLayer.addLayer(
626
        newCircle
627
      )
628
    }
629
  })
630 61ff7718 vastja
631 8feb1753 ballakt
  marksLayer.setZIndex(-1).addTo(mymap)
632 61ff7718 vastja
}
633
634 2f227a6c ballakt
async function preload (time, change, date) {
635 1774c06d Tomáš Ballák
  loadingY()
636 1a1d8f64 Martin Sebela
637 2f227a6c ballakt
  for (let nTime = time + change; nTime >= 0 && nTime <= 23; nTime = nTime + change) {
638
    if (!data[nTime]) {
639
      data[nTime] = {}
640
    }
641 c892003d Martin Sebela
642 1cf1413d ballakt
    datasetSelected.forEach(async (datasetName) => {
643
      if (!data[nTime][datasetName]) {
644
        data[nTime][datasetName] = await fetchByNameDate(dataSourceRoute, datasetName, date, nTime)
645
      }
646
    })
647 a48642fb vastja
  }
648 1a1d8f64 Martin Sebela
649 1774c06d Tomáš Ballák
  loadingN()
650 3fc08f2d vastja
}
651
652 2f227a6c ballakt
function drawHeatmap (dataRaw) {
653 03c02899 vastja
  // Todo still switched
654 2f227a6c ballakt
  const dataDict = dataRaw
655
  const mergedPoints = []
656
  let max = 0
657 1774c06d Tomáš Ballák
658 d5a88af0 Tomáš Ballák
  if (Object.keys(globalMarkersChanged).length) {
659
    Object.keys(globalMarkersChanged).forEach(function (key) {
660
      globalMarkersChanged[key][0].bindPopup(globalMarkersChanged[key][1])
661
    })
662
    globalMarkersChanged = {}
663
  }
664 c892003d Martin Sebela
665 2f227a6c ballakt
  Object.keys(dataDict).forEach((key) => {
666
    const data = dataDict[key]
667
    max = Math.max(max, data.max)
668 c892003d Martin Sebela
669 2f227a6c ballakt
    if (data != null) {
670 8feb1753 ballakt
    // Bind back popups for markers (we dont know if there is any data for this marker or not)
671 2f227a6c ballakt
      const points = data.items.map((point) => {
672
        const { x, y, number } = point
673
        const key = x + '' + y
674
        const holder = globalMarkersHolder[key]
675
        if (!globalMarkersChanged[key] && number) {
676 e6097215 ballakt
          // There is data for this marker => unbind popup with zero value
677 2f227a6c ballakt
          holder[0] = holder[0].unbindPopup()
678
          globalMarkersChanged[key] = holder
679
        }
680 c892003d Martin Sebela
681 2f227a6c ballakt
        return [x, y, number]
682
      })
683
      mergedPoints.push(...points)
684 1774c06d Tomáš Ballák
    } else {
685 2f227a6c ballakt
      if (heatmapLayer != null) {
686
        mymap.removeLayer(heatmapLayer)
687 084a5972 ballakt
      }
688 a48642fb vastja
    }
689 2f227a6c ballakt
  })
690 c892003d Martin Sebela
691 2f227a6c ballakt
  if (heatmapLayer != null) {
692
    mymap.removeLayer(heatmapLayer)
693
  }
694 c892003d Martin Sebela
695 2f227a6c ballakt
  if (mergedPoints.length) {
696
    heatmapLayer = L.heatLayer(mergedPoints, { max: max, minOpacity: 0.5, radius: 35, blur: 30 }).addTo(mymap)
697 a48642fb vastja
  }
698 ac154afa Martin Sebela
699
  // timto vyresen bug #8191 - TODO: znamena to, ze muzeme smazat volani updatePopup() ve funkcich, kde se nejdriv vola drawHeatmap() a pak updatePopup()?
700
  updatePopup()
701 03c02899 vastja
}
702 3fc08f2d vastja
703 70a3df53 vastja
/**
704
 * Checks dataset availibility
705 81980e82 ballakt
 * @param {string} route authority for datasets availibility checks
706 70a3df53 vastja
 */
707 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
708
function checkDataSetsAvailability (route) {
709 03c02899 vastja
  $.ajax({
710 8feb1753 ballakt
    type: 'POST',
711 03c02899 vastja
    // Todo it might be good idea to change db collections format
712 1774c06d Tomáš Ballák
    url: route + '/' + currentDateToString(),
713 8feb1753 ballakt
    success: function (result) {
714
      updateAvailableDataSets(result)
715 03c02899 vastja
    }
716 8feb1753 ballakt
  })
717 03c02899 vastja
}
718
719 8feb1753 ballakt
function updateAvailableDataSets (available) {
720 2f227a6c ballakt
  let leastOneOptionEnabled = false
721 c892003d Martin Sebela
722
  $('#dropdown-dataset .dropdown-item').each(function () {
723 2f227a6c ballakt
    const input = $(this).find('input')
724
    const inputVal = input[0].value
725 c892003d Martin Sebela
726 2f227a6c ballakt
    if (!(inputVal in available)) {
727
      $(this).addClass('disabled')
728
      $(input).prop('checked', false)
729 1774c06d Tomáš Ballák
    } else {
730 2f227a6c ballakt
      leastOneOptionEnabled = true
731
      $(this).removeClass('disabled')
732 5d599617 vastja
    }
733 8feb1753 ballakt
  })
734 dfe43218 vastja
735 94c6eb49 Martin Sebela
  $('#btn-update-heatmap').prop('disabled', !leastOneOptionEnabled)
736 03c02899 vastja
}
737 0a828a5a Martin Sebela
738 8feb1753 ballakt
function formatDate (date) {
739
  var day = String(date.getDate())
740
  var month = String(date.getMonth() + 1)
741 0a828a5a Martin Sebela
742
  if (day.length === 1) {
743 8feb1753 ballakt
    day = '0' + day
744 0a828a5a Martin Sebela
  }
745
746
  if (month.length === 1) {
747 8feb1753 ballakt
    month = '0' + month
748 0a828a5a Martin Sebela
  }
749
750 8feb1753 ballakt
  return date.getFullYear() + '-' + month + '-' + day
751 0a828a5a Martin Sebela
}
752
753 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
754
function initDatepicker (availableDatesSource) {
755
  var availableDates = ''
756 0a828a5a Martin Sebela
757
  $.ajax({
758
    type: 'GET',
759
    url: availableDatesSource,
760 8feb1753 ballakt
    success: function (result) {
761
      availableDates = String(result).split(',')
762 0a828a5a Martin Sebela
    }
763 a7e04778 Martin Sebela
  }).then(function () {
764
    $('#date').datepicker({
765
      format: 'yyyy-mm-dd',
766
      language: 'cs',
767 8feb1753 ballakt
      beforeShowDay: function (date) {
768 a7e04778 Martin Sebela
        if (availableDates.indexOf(formatDate(date)) < 0) {
769 8feb1753 ballakt
          return { enabled: false, tooltip: 'Žádná data' }
770 1774c06d Tomáš Ballák
        } else {
771 8feb1753 ballakt
          return { enabled: true }
772 a7e04778 Martin Sebela
        }
773
      },
774
      autoclose: true
775 8feb1753 ballakt
    })
776
  })
777
}
778 dd652e61 Martin Sebela
779 81980e82 ballakt
function initLocationsMenu () {
780 1a1d8f64 Martin Sebela
  const elmLocationsList = $('.locations')
781
  const locationsDisplayClass = 'show'
782 dd652e61 Martin Sebela
783 81980e82 ballakt
  if ($(window).width() <= 480) {
784 1a1d8f64 Martin Sebela
    elmLocationsList.removeClass(locationsDisplayClass)
785 1774c06d Tomáš Ballák
  } else {
786 1a1d8f64 Martin Sebela
    elmLocationsList.addClass(locationsDisplayClass)
787 dd652e61 Martin Sebela
  }
788 4e003182 Martin Sebela
}
789
790 2f227a6c ballakt
function onDocumentReady () {
791 c892003d Martin Sebela
  $('#dropdown-dataset').on('click', function (e) {
792 2f227a6c ballakt
    e.stopPropagation()
793
  })
794 1cf1413d ballakt
795 94c6eb49 Martin Sebela
  $('#btn-update-heatmap').prop('name', '')
796 1774c06d Tomáš Ballák
  changeCurrentTime()
797
  changeCurrentDate()
798 1cf1413d ballakt
  onValueChangeRegister()
799 815159f3 Tomáš Ballák
  onArrowLeftRightKeysDownRegister()
800 1cf1413d ballakt
}
801 c892003d Martin Sebela
802 1cf1413d ballakt
const loadCheckboxDatasetNameData = () => {
803 2f227a6c ballakt
  datasetSelected = []
804 1a1d8f64 Martin Sebela
805 c892003d Martin Sebela
  $('#dropdown-dataset .dropdown-item').each(function () {
806 2f227a6c ballakt
    const input = $(this).find('input')
807
    const inputVal = input[0].value
808 c892003d Martin Sebela
809 2f227a6c ballakt
    if (input[0].checked) {
810
      datasetSelected.push(inputVal)
811
    }
812 c892003d Martin Sebela
813 883a423e Tomáš Ballák
    datasetDictNameDisplayName[inputVal] = $(input).data('dataset-display-name')
814 2f227a6c ballakt
  })
815
}
816 ec5e3220 Martin Sebela
817 1a1d8f64 Martin Sebela
const dragTimeline = () => {
818 815159f3 Tomáš Ballák
  const hourElemWidthPx = 26
819 ec5e3220 Martin Sebela
820 815159f3 Tomáš Ballák
  const elem = $('#player-time')
821
  const offset = elem.offset().left - elem.parent().offset().left
822 ec5e3220 Martin Sebela
823
  if (offset >= 0 && offset <= elem.parent().width()) {
824 815159f3 Tomáš Ballák
    const hour = Math.round(offset / hourElemWidthPx)
825
826 1a1d8f64 Martin Sebela
    if (hour !== currentTime) {
827 fdba469a Martin Sebela
      elem.attr('class', 'time hour-' + hour)
828 ac154afa Martin Sebela
      $('#player-time span').html(formatTime(hour))
829 ec5e3220 Martin Sebela
830 fdba469a Martin Sebela
      onChangeHour(hour)
831
    }
832 ec5e3220 Martin Sebela
  }
833 815159f3 Tomáš Ballák
}