Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 1774c06d

Přidáno uživatelem Tomáš Ballák před více než 4 roky(ů)

done

Zobrazit rozdíly:

website/public/css/style.css
888 888
  padding: 3px 6px 4px 12px;
889 889
}
890 890

  
891
.player .date.lock {
892
  background: #0b155a;
893
}
894

  
895
.player .date.lock .lock-date {
896
  border: 0px;
897
  box-shadow: none;
898
}
899

  
900
.player .date.lock .lock-date:after {
901
  background-position: 0 0;
902
}
903

  
904
.player .date.lock .lock-date:hover:after {
905
  background-position: -8px 0;
906
}
907

  
891 908
.player .date .lock-date {
892 909
  width: 26px;
893 910
  height: 26px;
894 911
  margin-right: 2px;
895 912
  opacity: 1;
896 913
  border: 1px solid #ffffff;
897
  box-shadow: 0 0 0 2px #0b155a;
914
  /* box-shadow: 0 0 0 2px $secondary-bg-color; */
915
}
916

  
917
.player .date .lock-date:after {
918
  background-position: -8px 0;
898 919
}
899 920

  
900 921
.player .date .lock-date:hover {
901
  box-shadow: 0 0 0 2px rgba(11, 21, 90, 0.2);
922
  /* box-shadow: 0 0 0 2px rgba(11, 21, 90, .2); */
902 923
  background: rgba(11, 21, 90, 0.2);
903 924
}
904 925

  
905 926
.player .date .lock-date:hover:after {
906
  background-position: -8px 0;
927
  background-position: 0 0;
907 928
}
908 929

  
909 930
.player .date .lock-date:after {
......
911 932
  display: inline-block;
912 933
  width: 8px;
913 934
  height: 11px;
914
  background: url("../img/btn-lock.svg") no-repeat 0 0;
935
  background: url("../img/btn-lock.svg") no-repeat -8px 0;
915 936
  background-size: cover;
916 937
}
917 938

  
918
.player .date .lock-date.unlock:after {
919
  background-position: -8px 0;
920
}
921

  
922
.player .date .lock-date.unlock:hover:after {
923
  background-position: 0 0;
924
}
925

  
926 939
.player .timeline {
927 940
  display: flex;
928 941
  height: 30px;
website/public/css/style.scss
836 836
    border-radius: 15px;
837 837
    cursor: pointer;
838 838
    background: rgba(11, 21, 90, .7);
839

  
839
    //box-shadow: 0 0 0 2px rgba(11, 21, 90, .7);
840
    //border: 1px solid transparent;
840 841
    @media (max-width: 390px) {
841 842
      margin-right: 0;
842 843
    }
......
844 845
    span {
845 846
      padding: 3px 6px 4px 12px;
846 847
    }
848
    &.lock {
849
      background: rgba(11, 21, 90, 1);
850
      //border-color: $primary-text-color;
851
      //box-shadow: 0 0 0 2px $secondary-bg-color;
852
      .lock-date {
853
        border: 0px;
854
        box-shadow: none;
855
        &:after {
856
          background-position: 0 0;
857
        }
847 858

  
859
        &:hover:after {
860
          background-position: -8px 0;
861
        }
862
      }
863
    }
848 864
    .lock-date {
849 865
      width: 26px;
850 866
      height: 26px;
851 867
      margin-right: 2px;
852 868
      opacity: 1;
853 869
      border: 1px solid $primary-text-color;
854
      box-shadow: 0 0 0 2px $secondary-bg-color;
855

  
870
      /* box-shadow: 0 0 0 2px $secondary-bg-color; */
871
      &:after{
872
        background-position: -8px 0;
873
      }
856 874
      &:hover {
857
        box-shadow: 0 0 0 2px rgba(11, 21, 90, .2);
875
        /* box-shadow: 0 0 0 2px rgba(11, 21, 90, .2); */
858 876
        background: rgba(11, 21, 90, .2);
859 877

  
860 878
        &:after {
861
          background-position: -8px 0;
879
          background-position: 0 0;
862 880
        }
863 881
      }
864 882

  
......
867 885
        display: inline-block;
868 886
        width: 8px;
869 887
        height: 11px;
870
        background: url($images-dir + 'btn-lock.svg') no-repeat 0 0;
888
        background: url($images-dir + 'btn-lock.svg') no-repeat -8px 0;
871 889
        background-size: cover;
872 890
      }
873

  
874
      &.unlock {
875
        &:after {
876
          background-position: -8px 0;
877
        }
878

  
879
        &:hover:after {
880
          background-position: 0 0;
881
        }
882
      }
883 891
    }
884 892
  }
885 893

  
website/public/js/zcu-heatmap.js
11 11

  
12 12
var dataSourceRoute
13 13
let positionsSourceRoute
14
var currentTime
14

  
15
let currentTime
16

  
17
let currentDate
15 18

  
16 19
var timer
17 20
var isAnimationRunning = false
......
23 26
const datasetDictNameDisplayName = {}
24 27
var datasetSelected = []
25 28

  
29
// data only for one day
30
let lockedDay = false
31

  
32
let loading = 0
33

  
34
// marks for all datasets
35
const dataSourceMarks = {}
36

  
26 37
const globalMarkersHolder = {}
27 38
// all marker from which popup was removed
28 39
// contains: {key:[L.circle,L.pupup]}
29 40
// key: x and y, x + '' + y string
30 41
let globalMarkersChanged = {}
31 42

  
43
const loadingY = () => {
44
  loading++
45
}
46
const loadingN = () => {
47
  loading--
48
}
49

  
50
const changeCurrentTime = (time = null) => {
51
  if (time !== null) {
52
    currentTime = time
53
  } else {
54
    $('#dropdown-time input[type="radio"]:checked').each(function () {
55
      currentTime = parseInt($(this).val())
56
    })
57
  }
58
}
59

  
60
const changeCurrentDate = (date = null) => {
61
  if (date) {
62
    currentDate = new Date(date)
63
  } else {
64
    currentDate = new Date($('#date').val())
65
  }
66
  $('#player-date span').html(`${currentDate.getDate()}. ${currentDate.getMonth() + 1}. ${currentDate.getFullYear()}`)
67
  $('#date').val(currentDateToString())
68
  data = []
69
}
70
const currentDayToString = () => {
71
  const day = currentDate.getDate()
72
  return day > 9 ? `${day}` : `0${day}`
73
}
74
const currentMonthToString = () => {
75
  const month = currentDate.getMonth() + 1
76
  return month > 9 ? `${month}` : `0${month}`
77
}
78
const currentDateToString = () => `${currentDate.getFullYear()}-${currentMonthToString()}-${currentDayToString()}`
79
const addDayToCurrentDate = (day) => {
80
  currentDate.setDate(currentDate.getDate() + day)
81
  changeCurrentDate(currentDate)
82
}
83
const toggleDayLock = () => {
84
  lockedDay = !lockedDay
85
  $('#player-date').toggleClass('lock')
86
}
32 87

  
33 88
const fetchByNameDate = async (baseRoute, name, date, currentTime) => {
34 89
  const headers = new Headers()
......
40 95
  return beforeJson.json()
41 96
}
42 97

  
43

  
44 98
const fetchDataSourceMarks = async (positionRoute, datasetName) => {
45 99
  const headers = new Headers()
46 100
  const myRequest = new Request(positionRoute + '/' + datasetName, {
......
51 105
  return beforeJson.json()
52 106
}
53 107

  
54

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

  
61

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

  
66

  
67 118
const genMultipleDatasetsPopUp = (sum, currentPos, maxPos, datasetName) => {
68 119
  const popupHeader = `<strong id="dataset-info">${datasetName}</strong>`
69 120
  const popupData = `<div id="number-info"><span id="digit-info">${sum}</span></div>`
......
76 127
  `
77 128
}
78 129

  
79

  
80 130
const prepareLayerPopUp = (lat, lng, num, className) => L.popup({
81 131
  autoPan: false,
82 132
  className: className
83 133
}).setLatLng([lat / num, lng / num])
84 134

  
85

  
86 135
const genPopUp = (datasetName, place, count, sum, currentPos, maxPos) => {
87 136
  const popupHeader = `
88 137
    <strong>${datasetName}</strong>
......
101 150
  `
102 151
}
103 152

  
104

  
105 153
const onCheckboxClicked = async (checkbox) => {
106 154
  if ($(checkbox).prop('checked')) {
107 155
    loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
108 156
    changeUrl()
109
  }
110
  else {
157
  } else {
111 158
    loadCheckboxDatasetNameData()
112 159

  
113 160
    data.forEach((item, index) => {
......
123 170
  }
124 171
}
125 172

  
126

  
127 173
const debounce = (func, delay) => {
128 174
  let inDebounce
129 175
  return function () {
......
134 180
  }
135 181
}
136 182

  
137

  
138 183
const onValueChangeRegister = () => {
139 184
  $('#date').change(function () {
140
    data = []
185
    changeCurrentDate($(this).val())
141 186
    loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
142
    const date = new Date($(this).val())
143
    $('#player-date').html(`${date.getDate()}. ${date.getMonth() + 1}. ${date.getFullYear()}`)
144 187
    changeUrl()
145 188
  })
146 189

  
147 190
  $('#dropdown-time input[type="radio"]').each(function () {
148 191
    $(this).change(function () {
149
      currentTime = parseInt($(this).val())
150
      updateHeaderControls()
151
      setTimeline()
192
      changeHour(parseInt($(this).val()))
152 193
      drawHeatmap(data[currentTime])
153
      changeUrl()
154 194
    })
155 195
  })
156 196

  
......
161 201
  })
162 202
}
163 203

  
164

  
165 204
/**
166 205
 * Initialize leaflet map on start position which can be default or set based on user action
167 206
 */
......
181 220
  mymap.on('click', showInfo)
182 221
}
183 222

  
184

  
185 223
const getInfoLength = () => {
186 224
  const infoKeys = Object.keys(info)
187 225
  if (infoKeys.length === 1) {
......
192 230
  return infoKeys.length
193 231
}
194 232

  
195

  
196 233
const getElFromObjectInfo = (position) => {
197 234
  const keys = Object.keys(info)
198 235
  return info[keys[position]]
199 236
}
200 237

  
201

  
202 238
const hasInfoMultipleDatasets = () => {
203 239
  return Object.keys(info).length > 1
204 240
}
205 241

  
206

  
207 242
function showInfo (e) {
208 243
  info = []
209 244
  currentInfo = 0
......
272 307
      $('#next-info-btn').prop('disabled', true)
273 308
      $('.popup-controls').hide()
274 309
    }
275
  }
276
  else {
310
  } else {
277 311
    const { datasetName, number } = getElFromObjectInfo(currentInfo)
278 312

  
279 313
    prepareLayerPopUp(lat, lng, i, `popup-${datasetName}`)
......
282 316
  }
283 317
}
284 318

  
285

  
286 319
// eslint-disable-next-line no-unused-vars
287 320
function previousInfo () {
288 321
  const infoLength = getInfoLength()
......
292 325
  displayInfoText(previousCurrentInfo)
293 326
}
294 327

  
295

  
296 328
// eslint-disable-next-line no-unused-vars
297 329
function nextInfo () {
298 330
  const infoLength = getInfoLength()
......
302 334
  displayInfoText(previousCurrentInfo)
303 335
}
304 336

  
305

  
306 337
function displayInfoText (previousInfoNum) {
307 338
  const previousInfo = hasInfoMultipleDatasets() ? getElFromObjectInfo(previousInfoNum) : getElFromObjectInfo(0).items[previousInfoNum]
308 339
  const info_ = hasInfoMultipleDatasets() ? getElFromObjectInfo(currentInfo) : getElFromObjectInfo(0).items[currentInfo]
......
312 343
  if (datasetInfo) {
313 344
    $(datasetInfo).html(datasetDictNameDisplayName[info_.datasetName])
314 345
  }
315
  
346

  
316 347
  $('#place-info').html(info_.place ? info_.place : info_.datasetName)
317 348
  $('#digit-info').html(info_.number)
318 349
  $('#count-info').html(currentInfo + 1 + ' z ' + infoLength)
......
321 352
  $('.leaflet-popup').addClass(`popup-${info_.datasetName}`)
322 353
}
323 354

  
324

  
325 355
// eslint-disable-next-line no-unused-vars
326 356
function setMapView (latitude, longitude, zoom) {
327 357
  localStorage.setItem('lat', latitude)
......
330 360
  mymap.setView([latitude, longitude], zoom)
331 361
}
332 362

  
333

  
334 363
/**
335 364
 * Change animation start from playing to stopped or the other way round
336 365
 */
......
340 369

  
341 370
  if (isAnimationRunning) {
342 371
    $('#play-pause').attr('class', 'pause')
343
    timer = setInterval(function() { next() }, 800)
344
  }
345
  else {
372
    timer = setInterval(function () { next() }, 800)
373
  } else {
346 374
    clearTimeout(timer)
347 375
    $('#play-pause').attr('class', 'play')
348 376
  }
349 377
}
350 378

  
351

  
352 379
// eslint-disable-next-line no-unused-vars
353
function previous () {
380
async function previous () {
381
  if (loading) {
382
    return
383
  }
354 384
  currentTime = (currentTime + 23) % 24
355
  drawHeatmap(data[currentTime])
356
  setTimeline()
385
  changeHour(currentTime)
357 386
  mymap.closePopup()
358
  updateHeaderControls()
359
  changeUrl()
387
  if (!lockedDay && (currentTime === 23)) {
388
    addDayToCurrentDate(-1)
389
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
390
  } else {
391
    drawHeatmap(data[currentTime])
392
  }
360 393
}
361 394

  
362

  
363
function next () {
395
async function next () {
396
  if (loading) {
397
    return
398
  }
364 399
  currentTime = (currentTime + 1) % 24
365
  drawHeatmap(data[currentTime])
366
  setTimeline()
400
  changeHour(currentTime)
367 401
  mymap.closePopup()
368
  updateHeaderControls()
369
  changeUrl()
402
  if (!lockedDay && (currentTime === 0)) {
403
    addDayToCurrentDate(1)
404
    await loadCurrentTimeHeatmap(dataSourceRoute, positionsSourceRoute)
405
  } else {
406
    drawHeatmap(data[currentTime])
407
  }
370 408
}
371 409

  
372

  
373 410
/**
374 411
 * Change browser url based on animation step.
375 412
 */
......
377 414
  window.history.pushState(
378 415
    '',
379 416
    document.title,
380
    window.location.origin + window.location.pathname + `?date=${$('#date').val()}&time=${currentTime}${datasetSelected.reduce((acc, current) => acc + '&type=' + current, '')}`
417
    window.location.origin + window.location.pathname + `?date=${currentDateToString()}&time=${currentTime}${datasetSelected.reduce((acc, current) => acc + '&type=' + current, '')}`
381 418
  )
382 419
}
383 420

  
384

  
385 421
function updateHeaderControls () {
386 422
  $(`#time_${currentTime}`).prop('checked', true)
387 423
  $('#dropdownMenuButtonTime').html((currentTime < 10 ? '0' : '') + `${currentTime}:00`)
388 424
}
389 425

  
390

  
391 426
function setTimeline () {
392 427
  $('#timeline').text(currentTime + ':00')
393 428
  $('#timeline').attr('class', 'time hour-' + currentTime)
394 429
}
395

  
396

  
397
function changeHour(hour) {
398
  currentTime = hour
430
const onChangeHour = (hour) => {
431
  changeHour(hour)
432
  drawHeatmap(data[currentTime])
433
}
434
const changeHour = (hour) => {
435
  changeCurrentTime(hour)
399 436
  updateHeaderControls()
400 437
  setTimeline()
401
  drawHeatmap(data[currentTime])
402 438
  changeUrl()
403 439
}
404 440

  
405

  
406 441
/**
407 442
 * Load and display heatmap layer for current data
408 443
 * @param {string} opendataRoute route to dataset source
......
414 449

  
415 450
  dataSourceRoute = opendataRoute
416 451
  positionsSourceRoute = positionsRoute
417
  const dataSourceMarks = {}
418 452
  const allPromises = []
419
  const date = $('#date').val()
420
  currentTime = parseInt($('#dropdown-time input[type="radio"]:checked').val())
421

  
422
  setTimeline()
423 453
  data[currentTime] = {}
454

  
424 455
  const dataSelectedHandler = async (datasetName) => {
425
    const marks = await fetchDataSourceMarks(positionsRoute, datasetName)
426
    const datasetData = await fetchByNameDate(dataSourceRoute, datasetName, date, currentTime)
427
    dataSourceMarks[datasetName] = marks
456
    if (!(datasetName in dataSourceMarks)) {
457
      dataSourceMarks[datasetName] = await fetchDataSourceMarks(positionsRoute, datasetName)
458
    }
459
    const datasetData = await fetchByNameDate(dataSourceRoute, datasetName, currentDateToString(), currentTime)
428 460
    data[currentTime][datasetName] = datasetData
429 461
  }
430

  
431
  await datasetSelected.forEach((datasetName) => {
462
  datasetSelected.forEach((datasetName) => {
432 463
    allPromises.push(dataSelectedHandler(datasetName))
433 464
  })
434 465

  
466
  loadingY()
435 467
  Promise.all(allPromises).then(
436 468
    () => {
469
      loadingN()
437 470
      drawDataSourceMarks(dataSourceMarks)
438 471
      drawHeatmap(data[currentTime])
439
      preload(currentTime, 1, date)
440
      preload(currentTime, -1, date)
472
      preload(currentTime, 1, currentDateToString())
473
      preload(currentTime, -1, currentDateToString())
441 474
    }
442 475
  )
443 476
}
444 477

  
445

  
446 478
function drawDataSourceMarks (data) {
447 479
  if (marksLayer != null) {
448 480
    mymap.removeLayer(marksLayer)
......
469 501
  marksLayer.setZIndex(-1).addTo(mymap)
470 502
}
471 503

  
472

  
473 504
async function preload (time, change, date) {
505
  loadingY()
474 506
  for (let nTime = time + change; nTime >= 0 && nTime <= 23; nTime = nTime + change) {
475 507
    if (!data[nTime]) {
476 508
      data[nTime] = {}
......
482 514
      }
483 515
    })
484 516
  }
517
  loadingN()
485 518
}
486 519

  
487

  
488 520
function drawHeatmap (dataRaw) {
489 521
  // Todo still switched
490 522
  const dataDict = dataRaw
491 523
  const mergedPoints = []
492 524
  let max = 0
493
  
525

  
494 526
  if (Object.keys(globalMarkersChanged).length) {
495 527
    Object.keys(globalMarkersChanged).forEach(function (key) {
496 528
      globalMarkersChanged[key][0].bindPopup(globalMarkersChanged[key][1])
......
518 550
        return [x, y, number]
519 551
      })
520 552
      mergedPoints.push(...points)
521
    }
522
    else {
553
    } else {
523 554
      if (heatmapLayer != null) {
524 555
        mymap.removeLayer(heatmapLayer)
525 556
      }
......
535 566
  }
536 567
}
537 568

  
538

  
539 569
/**
540 570
 * Checks dataset availibility
541 571
 * @param {string} route authority for datasets availibility checks
......
545 575
  $.ajax({
546 576
    type: 'POST',
547 577
    // Todo it might be good idea to change db collections format
548
    url: route + '/' + $('#date').val(),
578
    url: route + '/' + currentDateToString(),
549 579
    success: function (result) {
550 580
      updateAvailableDataSets(result)
551 581
    }
552 582
  })
553 583
}
554 584

  
555

  
556 585
function updateAvailableDataSets (available) {
557 586
  let leastOneOptionEnabled = false
558
  // datasetSelected = []
559 587

  
560 588
  $('#dropdown-dataset .dropdown-item').each(function () {
561 589
    const input = $(this).find('input')
......
564 592
    if (!(inputVal in available)) {
565 593
      $(this).addClass('disabled')
566 594
      $(input).prop('checked', false)
567
    }
568
    else {
595
    } else {
569 596
      leastOneOptionEnabled = true
570 597
      $(this).removeClass('disabled')
571 598
    }
......
574 601
  $('#btn-update-heatmap').prop('disabled', !leastOneOptionEnabled)
575 602
}
576 603

  
577

  
578 604
function formatDate (date) {
579 605
  var day = String(date.getDate())
580 606
  var month = String(date.getMonth() + 1)
......
590 616
  return date.getFullYear() + '-' + month + '-' + day
591 617
}
592 618

  
593

  
594 619
// eslint-disable-next-line no-unused-vars
595 620
function initDatepicker (availableDatesSource) {
596 621
  var availableDates = ''
......
608 633
      beforeShowDay: function (date) {
609 634
        if (availableDates.indexOf(formatDate(date)) < 0) {
610 635
          return { enabled: false, tooltip: 'Žádná data' }
611
        }
612
        else {
636
        } else {
613 637
          return { enabled: true }
614 638
        }
615 639
      },
......
618 642
  })
619 643
}
620 644

  
621

  
622 645
function initLocationsMenu () {
623 646
  var locationsWrapper = '.locations'
624 647
  var locationsDisplayClass = 'show'
625 648

  
626 649
  if ($(window).width() <= 480) {
627 650
    $(locationsWrapper).removeClass(locationsDisplayClass)
628
  }
629
  else {
651
  } else {
630 652
    $(locationsWrapper).addClass(locationsDisplayClass)
631 653
  }
632 654
}
633 655

  
634

  
635
function openDatepicker () {
636
  if ($(window).width() <= 990) {
637
    $('.navbar-collapse').collapse()
638
  }
639

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

  
643

  
644 656
function onDocumentReady () {
645 657
  $('#dropdown-dataset').on('click', function (e) {
646 658
    e.stopPropagation()
647 659
  })
648 660

  
649 661
  $('#btn-update-heatmap').prop('name', '')
662
  changeCurrentTime()
663
  setTimeline()
664
  changeCurrentDate()
650 665
  onValueChangeRegister()
651 666
}
652 667

  
653

  
654 668
const loadCheckboxDatasetNameData = () => {
655 669
  datasetSelected = []
656 670
  $('#dropdown-dataset .dropdown-item').each(function () {
website/templates/heatmap.html.twig
117 117

  
118 118
    <div class="datetime">
119 119
      {% if form.vars.value.date %}
120
      <div class="date" id="player-date">
121
        <span onclick="openDatepicker()">{{ form.vars.value.date|date('j. n. Y') }}</span>
120
      <div class="date" id="player-date" onclick="toggleDayLock()">
121
        <span>{{ form.vars.value.date|date('j. n. Y') }}</span>
122 122
        <div class="lock-date"></div>
123 123
      </div>
124 124
      {% endif %}
......
127 127
        <div class="time hour-0" id="timeline">0:00</div>
128 128

  
129 129
        {% for i in 0..23 %}
130
        <div class="hour" title="{{ i }}:00" onclick="changeHour('{{ i }}')"></div>
130
        <div class="hour" title="{{ i }}:00" onclick="onChangeHour('{{ i }}')"></div>
131 131
        {% endfor %}
132 132
        <div class="end-dot"></div>
133 133
      </div>

Také k dispozici: Unified diff