Projekt

Obecné

Profil

« Předchozí | Další » 

Revize c892003d

Přidáno uživatelem Martin Sebela před více než 3 roky(ů)

Re #8159 - CSS improvements, timeline clickable, code refactoring

Zobrazit rozdíly:

website/public/js/zcu-heatmap.js
29 29
// key: x and y, x + '' + y string
30 30
let globalMarkersChanged = {}
31 31

  
32

  
32 33
const fetchByNameDate = async (baseRoute, name, date, currentTime) => {
33 34
  const headers = new Headers()
34 35
  const myRequest = new Request(baseRoute + '/' + name + '/' + date + '/' + currentTime, {
......
38 39
  const beforeJson = await fetch(myRequest)
39 40
  return beforeJson.json()
40 41
}
42

  
43

  
41 44
const fetchDataSourceMarks = async (positionRoute, datasetName) => {
42 45
  const headers = new Headers()
43 46
  const myRequest = new Request(positionRoute + '/' + datasetName, {
......
48 51
  return beforeJson.json()
49 52
}
50 53

  
54

  
51 55
const genPopUpControlButtons = (currentPage, numPages, onNextClick, onPreviousClick) => ({
52 56
  previousButton: '<button id="previous-info-btn" class="circle-button" onclick="previousInfo()"></button>',
53
  nextButton: '<button id="next-info-btn" onclick="nextInfo()" class="circle-button next"></button>',
57
  nextButton: '<button id="next-info-btn" class="circle-button next" onclick="nextInfo()"></button>',
54 58
  posInfo: `<div id="count-info">${currentPage} z ${numPages}</div>`
55 59
})
60

  
61

  
56 62
const genPopUpControls = (controls) => {
57 63
  return `<div class="popup-controls">${controls ? controls.reduce((sum, item) => sum + item, '') : ''}</div>`
58 64
}
65

  
66

  
59 67
const genMultipleDatasetsPopUp = (sum, currentPos, maxPos, datasetName) => {
60
  const header = `<strong id="dataset-info">${datasetName}</strong>`
61
  const digitInfo = `<div id="number-info"><span id="digit-info">${sum}</span></div>`
68
  const popupHeader = `<strong id="dataset-info">${datasetName}</strong>`
69
  const popupData = `<div id="number-info"><span id="digit-info">${sum}</span></div>`
62 70
  const { previousButton, nextButton, posInfo } = genPopUpControlButtons(currentPos, maxPos)
71

  
63 72
  return `
64
  ${header}
65
  ${digitInfo}
73
  ${popupHeader}
74
  ${popupData}
66 75
  ${genPopUpControls([previousButton, posInfo, nextButton])}
67 76
  `
68 77
}
78

  
79

  
69 80
const prepareLayerPopUp = (lat, lng, num, className) => L.popup({
70 81
  autoPan: false,
71 82
  className: className
72 83
}).setLatLng([lat / num, lng / num])
73 84

  
85

  
74 86
const genPopUp = (datasetName, place, count, sum, currentPos, maxPos) => {
75 87
  const popupHeader = `
76 88
    <strong>${datasetName}</strong>
......
88 100
  ${genPopUpControls(maxPos > 1 ? [previousButton, posInfo, nextButton] : null)}
89 101
  `
90 102
}
103

  
104

  
91 105
const onCheckboxClicked = async (checkbox) => {
92 106
  if ($(checkbox).prop('checked')) {
93 107
    loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
94 108
    changeUrl()
95
  } else {
109
  }
110
  else {
96 111
    loadCheckboxDatasetNameData()
112

  
97 113
    data.forEach((item, index) => {
98 114
      Object.keys(item).forEach((datasetName) => {
99 115
        if (datasetName === $(checkbox).val()) {
......
102 118
      })
103 119
      drawHeatmap(data[currentTime])
104 120
    })
121

  
105 122
    changeUrl()
106 123
  }
107 124
}
125

  
126

  
108 127
const debounce = (func, delay) => {
109 128
  let inDebounce
110 129
  return function () {
......
115 134
  }
116 135
}
117 136

  
137

  
118 138
const onValueChangeRegister = () => {
119 139
  $('#date').change(function () {
120 140
    data = []
......
124 144
    changeUrl()
125 145
  })
126 146

  
127
  $('#dataset-dropdown-time input[type="radio"]').each(function () {
147
  $('#dropdown-time input[type="radio"]').each(function () {
128 148
    $(this).change(function () {
129 149
      currentTime = parseInt($(this).val())
130 150
      updateHeaderControls()
......
134 154
    })
135 155
  })
136 156

  
137
  $('input[type=checkbox]').each(function () {
157
  $('#dropdown-dataset input[type="checkbox"]').each(function () {
138 158
    $(this).change(
139 159
      debounce(() => onCheckboxClicked(this), 1000)
140 160
    )
141 161
  })
142 162
}
143 163

  
164

  
144 165
/**
145 166
 * Initialize leaflet map on start position which can be default or set based on user action
146 167
 */
......
159 180

  
160 181
  mymap.on('click', showInfo)
161 182
}
183

  
184

  
162 185
const getInfoLength = () => {
163 186
  const infoKeys = Object.keys(info)
164 187
  if (infoKeys.length === 1) {
......
168 191
  // return number of datasets (agregation of all datasets in area)
169 192
  return infoKeys.length
170 193
}
194

  
195

  
171 196
const getElFromObjectInfo = (position) => {
172 197
  const keys = Object.keys(info)
173 198
  return info[keys[position]]
174 199
}
200

  
201

  
175 202
const hasInfoMultipleDatasets = () => {
176 203
  return Object.keys(info).length > 1
177 204
}
205

  
206

  
178 207
function showInfo (e) {
179 208
  info = []
180 209
  currentInfo = 0
......
216 245
        datasetName: item.datasetName
217 246
      }
218 247
    }
248

  
219 249
    acc[item.datasetName].items.push(item)
220 250
    acc[item.datasetName].number += Number(item.number)
221 251
    return acc
......
236 266
    prepareLayerPopUp(lat, lng, i, `popup-${infoDict.datasetName}`)
237 267
      .setContent(genPopUp(datasetDictNameDisplayName[infoDict.datasetName], place, number, total, currentInfo + 1, info_.length))
238 268
      .openOn(mymap)
269

  
239 270
    if (info_.length === 1) {
240 271
      $('#previous-info-btn').prop('disabled', true)
241 272
      $('#next-info-btn').prop('disabled', true)
242 273
      $('.popup-controls').hide()
243 274
    }
244
  } else {
275
  }
276
  else {
245 277
    const { datasetName, number } = getElFromObjectInfo(currentInfo)
278

  
246 279
    prepareLayerPopUp(lat, lng, i, `popup-${datasetName}`)
247 280
      .setContent(genMultipleDatasetsPopUp(number, currentInfo + 1, getInfoLength(), datasetDictNameDisplayName[datasetName]))
248 281
      .openOn(mymap)
249 282
  }
250 283
}
251 284

  
285

  
252 286
// eslint-disable-next-line no-unused-vars
253 287
function previousInfo () {
254 288
  const infoLength = getInfoLength()
255 289
  const previousCurrentInfo = currentInfo
290

  
256 291
  currentInfo = (currentInfo + infoLength - 1) % infoLength
257 292
  displayInfoText(previousCurrentInfo)
258 293
}
259 294

  
295

  
260 296
// eslint-disable-next-line no-unused-vars
261 297
function nextInfo () {
262 298
  const infoLength = getInfoLength()
263 299
  const previousCurrentInfo = currentInfo
300

  
264 301
  currentInfo = (currentInfo + 1) % infoLength
265 302
  displayInfoText(previousCurrentInfo)
266 303
}
304

  
305

  
267 306
function displayInfoText (previousInfoNum) {
268 307
  const previousInfo = hasInfoMultipleDatasets() ? getElFromObjectInfo(previousInfoNum) : getElFromObjectInfo(0).items[previousInfoNum]
269 308
  const info_ = hasInfoMultipleDatasets() ? getElFromObjectInfo(currentInfo) : getElFromObjectInfo(0).items[currentInfo]
270 309
  const infoLength = getInfoLength()
271 310
  const datasetInfo = $('#dataset-info')
311

  
272 312
  if (datasetInfo) {
273 313
    $(datasetInfo).html(datasetDictNameDisplayName[info_.datasetName])
274 314
  }
315
  
275 316
  $('#place-info').html(info_.place ? info_.place : info_.datasetName)
276 317
  $('#digit-info').html(info_.number)
277 318
  $('#count-info').html(currentInfo + 1 + ' z ' + infoLength)
319

  
278 320
  $('.leaflet-popup').removeClass(`popup-${previousInfo.datasetName}`)
279 321
  $('.leaflet-popup').addClass(`popup-${info_.datasetName}`)
280 322
}
281 323

  
324

  
282 325
// eslint-disable-next-line no-unused-vars
283 326
function setMapView (latitude, longitude, zoom) {
284 327
  localStorage.setItem('lat', latitude)
......
287 330
  mymap.setView([latitude, longitude], zoom)
288 331
}
289 332

  
333

  
290 334
/**
291 335
 * Change animation start from playing to stopped or the other way round
292 336
 */
293 337
// eslint-disable-next-line no-unused-vars
294 338
function changeAnimationState () {
295 339
  isAnimationRunning = !isAnimationRunning
340

  
296 341
  if (isAnimationRunning) {
297 342
    $('#play-pause').attr('class', 'pause')
298
    timer = setInterval(
299
      function () {
300
        next()
301
      },
302
      800
303
    )
304
  } else {
343
    timer = setInterval(function() { next() }, 800)
344
  }
345
  else {
305 346
    clearTimeout(timer)
306 347
    $('#play-pause').attr('class', 'play')
307 348
  }
308 349
}
309 350

  
351

  
310 352
// eslint-disable-next-line no-unused-vars
311 353
function previous () {
312 354
  currentTime = (currentTime + 23) % 24
......
317 359
  changeUrl()
318 360
}
319 361

  
362

  
320 363
function next () {
321 364
  currentTime = (currentTime + 1) % 24
322 365
  drawHeatmap(data[currentTime])
......
325 368
  updateHeaderControls()
326 369
  changeUrl()
327 370
}
328
const typeUrlReducer = (accumulator, currentValue) => accumulator + currentValue
371

  
372

  
329 373
/**
330
 * Change browser url based on animation step
374
 * Change browser url based on animation step.
331 375
 */
332 376
function changeUrl () {
333 377
  window.history.pushState(
334 378
    '',
335 379
    document.title,
336
    window.location.origin + window.location.pathname + `?date=${$('#date').val()}&time=${currentTime}${datasetSelected.reduce((acc, current) => acc + '&type[]=' + current, '')}`
380
    window.location.origin + window.location.pathname + `?date=${$('#date').val()}&time=${currentTime}${datasetSelected.reduce((acc, current) => acc + '&type=' + current, '')}`
337 381
  )
338 382
}
339 383

  
384

  
340 385
function updateHeaderControls () {
341 386
  $(`#time_${currentTime}`).prop('checked', true)
342 387
  $('#dropdownMenuButtonTime').html((currentTime < 10 ? '0' : '') + `${currentTime}:00`)
343 388
}
344 389

  
390

  
345 391
function setTimeline () {
346 392
  $('#timeline').text(currentTime + ':00')
347 393
  $('#timeline').attr('class', 'time hour-' + currentTime)
348 394
}
349 395

  
396

  
397
function changeHour(hour) {
398
  currentTime = hour
399
  updateHeaderControls()
400
  setTimeline()
401
  drawHeatmap(data[currentTime])
402
  changeUrl()
403
}
404

  
405

  
350 406
/**
351 407
 * Load and display heatmap layer for current data
352 408
 * @param {string} opendataRoute route to dataset source
......
355 411
// eslint-disable-next-line no-unused-vars
356 412
async function loadCurrentTimeHeatmap (opendataRoute, positionsRoute) {
357 413
  loadCheckboxDatasetNameData()
414

  
358 415
  dataSourceRoute = opendataRoute
359 416
  positionsSourceRoute = positionsRoute
360 417
  const dataSourceMarks = {}
361 418
  const allPromises = []
362 419
  const date = $('#date').val()
363
  currentTime = parseInt($('#dataset-dropdown-time input[type="radio"]:checked').val())
420
  currentTime = parseInt($('#dropdown-time input[type="radio"]:checked').val())
364 421

  
365 422
  setTimeline()
366 423
  data[currentTime] = {}
......
370 427
    dataSourceMarks[datasetName] = marks
371 428
    data[currentTime][datasetName] = datasetData
372 429
  }
430

  
373 431
  await datasetSelected.forEach((datasetName) => {
374 432
    allPromises.push(dataSelectedHandler(datasetName))
375 433
  })
434

  
376 435
  Promise.all(allPromises).then(
377 436
    () => {
378 437
      drawDataSourceMarks(dataSourceMarks)
......
383 442
  )
384 443
}
385 444

  
445

  
386 446
function drawDataSourceMarks (data) {
387 447
  if (marksLayer != null) {
388 448
    mymap.removeLayer(marksLayer)
389 449
  }
450

  
390 451
  marksLayer = L.layerGroup()
452

  
391 453
  Object.keys(data).forEach((key_) => {
392 454
    for (var key in data[key_]) {
393 455
      const { x, y, name } = data[key_][key]
......
407 469
  marksLayer.setZIndex(-1).addTo(mymap)
408 470
}
409 471

  
472

  
410 473
async function preload (time, change, date) {
411 474
  for (let nTime = time + change; nTime >= 0 && nTime <= 23; nTime = nTime + change) {
412 475
    if (!data[nTime]) {
413 476
      data[nTime] = {}
414 477
    }
478

  
415 479
    datasetSelected.forEach(async (datasetName) => {
416 480
      if (!data[nTime][datasetName]) {
417 481
        data[nTime][datasetName] = await fetchByNameDate(dataSourceRoute, datasetName, date, nTime)
......
420 484
  }
421 485
}
422 486

  
487

  
423 488
function drawHeatmap (dataRaw) {
424 489
  // Todo still switched
425 490
  const dataDict = dataRaw
426 491
  const mergedPoints = []
427 492
  let max = 0
493
  
428 494
  if (Object.keys(globalMarkersChanged).length) {
429 495
    Object.keys(globalMarkersChanged).forEach(function (key) {
430 496
      globalMarkersChanged[key][0].bindPopup(globalMarkersChanged[key][1])
431 497
    })
432 498
    globalMarkersChanged = {}
433 499
  }
500

  
434 501
  Object.keys(dataDict).forEach((key) => {
435 502
    const data = dataDict[key]
436 503
    max = Math.max(max, data.max)
504

  
437 505
    if (data != null) {
438 506
    // Bind back popups for markers (we dont know if there is any data for this marker or not)
439 507
      const points = data.items.map((point) => {
440 508
        const { x, y, number } = point
441 509
        const key = x + '' + y
442 510
        const holder = globalMarkersHolder[key]
511

  
443 512
        if (!globalMarkersChanged[key] && number) {
444 513
        // There is data for this marker => unbind popup with zero value
445 514
          holder[0] = holder[0].unbindPopup()
446 515
          globalMarkersChanged[key] = holder
447 516
        }
517

  
448 518
        return [x, y, number]
449 519
      })
450 520
      mergedPoints.push(...points)
451
    } else {
521
    }
522
    else {
452 523
      if (heatmapLayer != null) {
453 524
        mymap.removeLayer(heatmapLayer)
454 525
      }
455 526
    }
456 527
  })
528

  
457 529
  if (heatmapLayer != null) {
458 530
    mymap.removeLayer(heatmapLayer)
459 531
  }
532

  
460 533
  if (mergedPoints.length) {
461 534
    heatmapLayer = L.heatLayer(mergedPoints, { max: max, minOpacity: 0.5, radius: 35, blur: 30 }).addTo(mymap)
462 535
  }
463 536
}
464 537

  
538

  
465 539
/**
466 540
 * Checks dataset availibility
467 541
 * @param {string} route authority for datasets availibility checks
......
478 552
  })
479 553
}
480 554

  
555

  
481 556
function updateAvailableDataSets (available) {
482 557
  let leastOneOptionEnabled = false
483 558
  // datasetSelected = []
484
  $('#dataset-dropdown .dropdown-item').each(function () {
559

  
560
  $('#dropdown-dataset .dropdown-item').each(function () {
485 561
    const input = $(this).find('input')
486 562
    const inputVal = input[0].value
563

  
487 564
    if (!(inputVal in available)) {
488 565
      $(this).addClass('disabled')
489 566
      $(input).prop('checked', false)
490
    } else {
567
    }
568
    else {
491 569
      leastOneOptionEnabled = true
492 570
      $(this).removeClass('disabled')
493 571
    }
......
496 574
  $('#btn-update-heatmap').prop('disabled', !leastOneOptionEnabled)
497 575
}
498 576

  
577

  
499 578
function formatDate (date) {
500 579
  var day = String(date.getDate())
501 580
  var month = String(date.getMonth() + 1)
......
511 590
  return date.getFullYear() + '-' + month + '-' + day
512 591
}
513 592

  
593

  
514 594
// eslint-disable-next-line no-unused-vars
515 595
function initDatepicker (availableDatesSource) {
516 596
  var availableDates = ''
......
528 608
      beforeShowDay: function (date) {
529 609
        if (availableDates.indexOf(formatDate(date)) < 0) {
530 610
          return { enabled: false, tooltip: 'Žádná data' }
531
        } else {
611
        }
612
        else {
532 613
          return { enabled: true }
533 614
        }
534 615
      },
......
537 618
  })
538 619
}
539 620

  
621

  
540 622
function initLocationsMenu () {
541 623
  var locationsWrapper = '.locations'
542 624
  var locationsDisplayClass = 'show'
543 625

  
544 626
  if ($(window).width() <= 480) {
545 627
    $(locationsWrapper).removeClass(locationsDisplayClass)
546
  } else {
628
  }
629
  else {
547 630
    $(locationsWrapper).addClass(locationsDisplayClass)
548 631
  }
549 632
}
550 633

  
634

  
551 635
function openDatepicker () {
552 636
  if ($(window).width() <= 990) {
553 637
    $('.navbar-collapse').collapse()
......
555 639

  
556 640
  $('#date').datepicker('show')
557 641
}
642

  
643

  
558 644
function onDocumentReady () {
559
  $('#dataset-dropdown').on('click', function (e) {
645
  $('#dropdown-dataset').on('click', function (e) {
560 646
    e.stopPropagation()
561 647
  })
562 648

  
563 649
  $('#btn-update-heatmap').prop('name', '')
564 650
  onValueChangeRegister()
565 651
}
652

  
653

  
566 654
const loadCheckboxDatasetNameData = () => {
567 655
  datasetSelected = []
568
  $('#dataset-dropdown .dropdown-item').each(function () {
656
  $('#dropdown-dataset .dropdown-item').each(function () {
569 657
    const input = $(this).find('input')
570 658
    const inputVal = input[0].value
659

  
571 660
    if (input[0].checked) {
572 661
      datasetSelected.push(inputVal)
573 662
    }
663

  
574 664
    datasetDictNameDisplayName[inputVal] = $(input).data('dataset-display-name')
575 665
  })
576 666
}

Také k dispozici: Unified diff