Projekt

Obecné

Profil

Stáhnout (10.7 KB) Statistiky
| Větev: | Revize:
1 8feb1753 ballakt
/* global L */
2
/* global $ */
3
var mymap
4
var heatmapLayer = null
5
var marksLayer = null
6 03c02899 vastja
7 8feb1753 ballakt
var startX = 49.7248
8
var startY = 13.3521
9
var startZoom = 17
10 3fc08f2d vastja
11 8feb1753 ballakt
var dataSourceRoute
12
var currentTime
13
var name
14
var date
15 a48642fb vastja
16 8feb1753 ballakt
var timer
17
var isAnimationRunning = false
18
var data = []
19 a48642fb vastja
20 70a3df53 vastja
var info = []
21
var currenInfo = 0
22
23 084a5972 ballakt
// holds all instances of markers for bind/unbind popup purpose
24
// contains: {key:[L.circle,L.pupup]}
25
// key: x and y, x + '' + y string
26 8feb1753 ballakt
const globalMarkersHolder = {}
27 084a5972 ballakt
28 8feb1753 ballakt
// all marker from which popup was removed
29 084a5972 ballakt
// contains: {key:[L.circle,L.pupup]}
30
// key: x and y, x + '' + y string
31 8feb1753 ballakt
let globalMarkersChanged = {}
32 084a5972 ballakt
33
const genPopUpControls = (controls) => {
34
  return `<div class="popup-controls">${controls.reduce((sum, item) => sum + item, '')}</div>`
35
}
36
const genPopUp = (place, number, sum, currentPos, maxPos) => {
37 8feb1753 ballakt
  const header = `<strong>Zařízení a počet:</strong><div id="place-info">${place}</div>`
38
  const currentNum = `<span id="digit-info">${number}</span>`
39 90d3db28 Tomáš Ballák
  // eslint-disable-next-line eqeqeq
40
  const sumNum = `<span id="total-info" style="font-size: large">${(sum && (sum != number)) ? '/' + sum : ''}</span>`
41 8feb1753 ballakt
  const digitInfo = `<div id="number-info">${currentNum}${sumNum}</div>`
42
  let previousButton = '<button id="previous-info-btn" class="circle-button" onclick="previousInfo()"></button>'
43
  let nextButton = '<button id="next-info-btn" onclick="nextInfo()" class="circle-button next"></button>'
44
  let posInfo = `<div id="count-info">${currentPos} z ${maxPos}</div>`
45 084a5972 ballakt
46
  if (!sum) {
47 8feb1753 ballakt
    previousButton = ''
48
    nextButton = ''
49
    posInfo = ''
50 084a5972 ballakt
  }
51
  return `
52
  ${header}
53
  ${digitInfo}
54
  ${genPopUpControls([previousButton, posInfo, nextButton])}
55
  `
56
}
57 70a3df53 vastja
/**
58
 * Initialize leaflet map on start position which can be default or set based on user action
59
 */
60 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
61
function initMap () {
62 90d3db28 Tomáš Ballák
  startX = localStorage.getItem('lat') || startX
63
  startY = localStorage.getItem('lng') || startY
64
  startZoom = localStorage.getItem('zoom') || startZoom
65 72a438f3 vastja
66 8feb1753 ballakt
  mymap = L.map('heatmap').setView([startX, startY], startZoom)
67 3fc08f2d vastja
68 c236b33a msebela
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
69
    attribution: '',
70
    maxZoom: 19
71 8feb1753 ballakt
  }).addTo(mymap)
72 3ae59f75 vastja
73 8feb1753 ballakt
  mymap.on('click', showInfo)
74 c236b33a msebela
}
75 3fc08f2d vastja
76 8feb1753 ballakt
function showInfo (e) {
77 3ae59f75 vastja
  info = []
78 8feb1753 ballakt
  currenInfo = 0
79 3ae59f75 vastja
80
  // https://wiki.openstreetmap.org/wiki/Zoom_levels
81
  // Todo change to variable - it is used in heatmap init
82 8feb1753 ballakt
  var stile = 40075016.686 * Math.cos(startX) / Math.pow(2, mymap.getZoom())
83
  var radius = 25 * stile / 256
84 3ae59f75 vastja
85 8feb1753 ballakt
  var i = 0
86
  var lat = 0
87
  var lng = 0
88 3ae59f75 vastja
89 8feb1753 ballakt
  var total = 0
90
  data[currentTime].items.forEach(element => {
91 3ae59f75 vastja
    if (e.latlng.distanceTo(new L.LatLng(element.x, element.y)) < radius) {
92
      lat += element.x
93 8feb1753 ballakt
      lng += element.y
94
      info[i] = { place: element.place, number: element.number }
95
      total += parseInt(element.number)
96
      i++
97 3ae59f75 vastja
    }
98 8feb1753 ballakt
  })
99 3ae59f75 vastja
100
  if (info.length > 0) {
101 8feb1753 ballakt
    const { place, number } = info[currenInfo]
102
    L.popup({
103 dfcface9 Tomáš Ballák
      autoPan: false
104 8feb1753 ballakt
    })
105
      .setLatLng([lat / i, lng / i])
106
      .setContent(genPopUp(place, number, total, currenInfo + 1, info.length))
107
      .openOn(mymap)
108
109
    if (info.length === 1) {
110
      $('#previous-info-btn').prop('disabled', true)
111
      $('#next-info-btn').prop('disabled', true)
112
      $('.popup-controls').hide()
113 3ae59f75 vastja
    }
114
  }
115
}
116
117 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
118
function previousInfo () {
119
  currenInfo = (currenInfo + info.length - 1) % info.length
120
  displayInfoText()
121 3ae59f75 vastja
}
122
123 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
124
function nextInfo () {
125 3ae59f75 vastja
  currenInfo = (currenInfo + 1) % info.length
126 8feb1753 ballakt
  displayInfoText()
127 3ae59f75 vastja
}
128
129 8feb1753 ballakt
function displayInfoText () {
130 bc7738cd Martin Sebela
  $('#place-info').html(info[currenInfo].place)
131 61ff7718 vastja
  $('#digit-info').html(info[currenInfo].number)
132 8feb1753 ballakt
  $('#count-info').html(currenInfo + 1 + ' z ' + info.length)
133 3ae59f75 vastja
}
134 351696d5 Martin Sebela
135 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
136 72a438f3 vastja
function setMapView (latitude, longitude, zoom) {
137 90d3db28 Tomáš Ballák
  localStorage.setItem('lat', latitude)
138
  localStorage.setItem('lng', longitude)
139
  localStorage.setItem('zoom', zoom)
140 8feb1753 ballakt
  mymap.setView([latitude, longitude], zoom)
141 3fc08f2d vastja
}
142
143 70a3df53 vastja
/**
144
 * Change animation start from playing to stopped or the other way round
145
 */
146 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
147
function changeAnimationState () {
148 a48642fb vastja
  isAnimationRunning = !isAnimationRunning
149
  if (isAnimationRunning) {
150 8feb1753 ballakt
    $('#play-pause').attr('class', 'pause')
151 a48642fb vastja
    timer = setInterval(
152 8feb1753 ballakt
      function () {
153
        next()
154 a48642fb vastja
      },
155
      800
156 8feb1753 ballakt
    )
157
  } else {
158
    clearTimeout(timer)
159
    $('#play-pause').attr('class', 'play')
160 351696d5 Martin Sebela
  }
161
}
162
163 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
164
function previous () {
165
  currentTime = (currentTime + 23) % 24
166
  drawHeatmap(data[currentTime])
167
  setTimeline()
168
  mymap.closePopup()
169
  updateHeaderControls()
170
  changeUrl()
171 a48642fb vastja
}
172
173 8feb1753 ballakt
function next () {
174
  currentTime = (currentTime + 1) % 24
175
  drawHeatmap(data[currentTime])
176
  setTimeline()
177
  mymap.closePopup()
178
  updateHeaderControls()
179
  changeUrl()
180 8b840eb7 vastja
}
181
182 70a3df53 vastja
/**
183
 * Change browser url based on animation step
184
 */
185 8feb1753 ballakt
function changeUrl () {
186 8b840eb7 vastja
  window.history.pushState(
187 8feb1753 ballakt
    '',
188 8b840eb7 vastja
    document.title,
189 8feb1753 ballakt
    window.location.origin + window.location.pathname + `?data_set[date]=${$('#date').val()}&data_set[time]=${currentTime}&data_set[type]=${$('#type').children('option:selected').val()}`
190
  )
191 4e8c0e5b Martin Sebela
}
192
193 8feb1753 ballakt
function updateHeaderControls () {
194
  document.getElementById('time').value = currentTime
195 a48642fb vastja
}
196 351696d5 Martin Sebela
197 8feb1753 ballakt
function setTimeline () {
198
  $('#timeline').text(currentTime + ':00')
199
  $('#timeline').attr('class', 'time hour-' + currentTime)
200 351696d5 Martin Sebela
}
201
202 70a3df53 vastja
/**
203
 * Load and display heatmap layer for current data
204
 * @param {string} opendataRoute route to dataset source
205
 * @param {string} positionsRoute  route to dataset postitions source
206
 */
207 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
208
function loadCurrentTimeHeatmap (opendataRoute, positionsRoute) {
209
  dataSourceRoute = opendataRoute
210
  data = []
211 64bc2934 vastja
212 8feb1753 ballakt
  name = $('#type').children('option:selected').val()
213 03ccdd65 vastja
  date = $('#date').val()
214 8feb1753 ballakt
  currentTime = parseInt($('#time').children('option:selected').val())
215
  setTimeline()
216 61ff7718 vastja
  $.ajax({
217
    type: 'POST',
218
    url: positionsRoute + '/' + name,
219 8feb1753 ballakt
    success: function (result) {
220
      drawDataSourceMarks(result)
221 084a5972 ballakt
      $.ajax({
222 8feb1753 ballakt
        type: 'POST',
223
        url: dataSourceRoute + '/' + name + '/' + date + '/' + currentTime,
224
        success: function (result) {
225
          data[currentTime] = result
226
          drawHeatmap(data[currentTime])
227 084a5972 ballakt
        }
228
      })
229 61ff7718 vastja
    }
230 8feb1753 ballakt
  })
231 a48642fb vastja
232 8feb1753 ballakt
  preload(currentTime, 1)
233
  preload(currentTime, -1)
234 a48642fb vastja
}
235
236 8feb1753 ballakt
function drawDataSourceMarks (data) {
237 61ff7718 vastja
  if (marksLayer != null) {
238 8feb1753 ballakt
    L.removeLayer(marksLayer)
239 61ff7718 vastja
  }
240 8feb1753 ballakt
  marksLayer = L.layerGroup()
241 61ff7718 vastja
  for (var key in data) {
242 8feb1753 ballakt
    const { x, y, name } = data[key]
243
    const pop =
244
      L.popup({ autoPan: false })
245 084a5972 ballakt
        .setLatLng([x, y])
246 8feb1753 ballakt
        .setContent(genPopUp(name, 0, 0, 1, 1))
247
    const newCircle =
248
      L.circle([x, y], { radius: 2, fillOpacity: 0.8, color: '#004fb3', fillColor: '#004fb3', bubblingMouseEvents: true })
249
        .bindPopup(pop)
250
    globalMarkersHolder[x + '' + y] = [newCircle, pop] // add new marker to global holders
251 084a5972 ballakt
    marksLayer.addLayer(
252
      newCircle
253 8feb1753 ballakt
    )
254 61ff7718 vastja
  }
255
256 8feb1753 ballakt
  marksLayer.setZIndex(-1).addTo(mymap)
257 61ff7718 vastja
}
258
259 8feb1753 ballakt
function preload (time, change) {
260
  var ntime = time + change
261
  if (ntime >= 0 && ntime <= 23) {
262
    $.ajax({
263
      type: 'POST',
264
      url: dataSourceRoute + '/' + name + '/' + date + '/' + ntime,
265
      success: function (result) {
266
        data[ntime] = result
267
        preload(ntime, change)
268
      }
269
    })
270 a48642fb vastja
  }
271 3fc08f2d vastja
}
272
273 8feb1753 ballakt
function drawHeatmap (data) {
274 03c02899 vastja
  // Todo still switched
275 8feb1753 ballakt
  if (data.items != null) {
276
    // Bind back popups for markers (we dont know if there is any data for this marker or not)
277
    if (Object.keys(globalMarkersChanged).length) {
278
      Object.keys(globalMarkersChanged).forEach(function (key) {
279
        globalMarkersChanged[key][0].bindPopup(globalMarkersChanged[key][1])
280
      })
281
      globalMarkersChanged = {}
282 084a5972 ballakt
    }
283 8feb1753 ballakt
    const points = data.items.map((point) => {
284
      const { x, y, number } = point
285
      const key = x + '' + y
286
      const holder = globalMarkersHolder[key]
287
      if (!globalMarkersChanged[key] && number) {
288
        // There is data for this marker => unbind popup with zero value
289
        holder[0] = holder[0].unbindPopup()
290
        globalMarkersChanged[key] = holder
291 084a5972 ballakt
      }
292 8feb1753 ballakt
      return [x, y, number]
293
    })
294 a48642fb vastja
    if (heatmapLayer != null) {
295 8feb1753 ballakt
      mymap.removeLayer(heatmapLayer)
296 a48642fb vastja
    }
297 8feb1753 ballakt
    heatmapLayer = L.heatLayer(points, { max: data.max, minOpacity: 0.5, radius: 35, blur: 30 }).addTo(mymap)
298
  } else {
299 a48642fb vastja
    if (heatmapLayer != null) {
300 8feb1753 ballakt
      mymap.removeLayer(heatmapLayer)
301 a48642fb vastja
    }
302
  }
303
304 03c02899 vastja
  // var heat_01 = ...
305
  // on background map.addLayer(heat_01) -> map.removeLayer(heat_01);
306 64bc2934 vastja
  // $(.leaflet-heatmap-layer).css('opacity', 'value');
307 03c02899 vastja
}
308 3fc08f2d vastja
309 70a3df53 vastja
/**
310
 * Checks dataset availibility
311 81980e82 ballakt
 * @param {string} route authority for datasets availibility checks
312 70a3df53 vastja
 */
313 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
314
function checkDataSetsAvailability (route) {
315 03c02899 vastja
  $.ajax({
316 8feb1753 ballakt
    type: 'POST',
317 03c02899 vastja
    // Todo it might be good idea to change db collections format
318 03ccdd65 vastja
    url: route + '/' + $('#date').val(),
319 8feb1753 ballakt
    success: function (result) {
320
      updateAvailableDataSets(result)
321 03c02899 vastja
    }
322 8feb1753 ballakt
  })
323 03c02899 vastja
}
324
325 8feb1753 ballakt
var allOptionsDisabled = false
326 2dd5d57f vastja
327 8feb1753 ballakt
function updateAvailableDataSets (available) {
328
  var isOptionEnabled = true
329
  $('#type > option').each(function () {
330
    if ((this.value in available) === false) {
331
      $(this).prop('disabled', true)
332
      $(this).prop('selected', false)
333
    } else {
334 5d599617 vastja
      $(this).prop('disabled', false)
335 2dd5d57f vastja
      if (allOptionsDisabled) {
336 8feb1753 ballakt
        $(this).prop('selected', true)
337
        allOptionsDisabled = false
338 2dd5d57f vastja
      }
339 8feb1753 ballakt
      isOptionEnabled = false
340 5d599617 vastja
    }
341 8feb1753 ballakt
  })
342
  allOptionsDisabled = isOptionEnabled
343 dfe43218 vastja
344 8feb1753 ballakt
  $('#submit-btn').prop('disabled', isOptionEnabled)
345 03c02899 vastja
}
346 0a828a5a Martin Sebela
347 8feb1753 ballakt
function formatDate (date) {
348
  var day = String(date.getDate())
349
  var month = String(date.getMonth() + 1)
350 0a828a5a Martin Sebela
351
  if (day.length === 1) {
352 8feb1753 ballakt
    day = '0' + day
353 0a828a5a Martin Sebela
  }
354
355
  if (month.length === 1) {
356 8feb1753 ballakt
    month = '0' + month
357 0a828a5a Martin Sebela
  }
358
359 8feb1753 ballakt
  return date.getFullYear() + '-' + month + '-' + day
360 0a828a5a Martin Sebela
}
361
362 8feb1753 ballakt
// eslint-disable-next-line no-unused-vars
363
function initDatepicker (availableDatesSource) {
364
  var availableDates = ''
365 0a828a5a Martin Sebela
366
  $.ajax({
367
    type: 'GET',
368
    url: availableDatesSource,
369 8feb1753 ballakt
    success: function (result) {
370
      availableDates = String(result).split(',')
371 0a828a5a Martin Sebela
    }
372 a7e04778 Martin Sebela
  }).then(function () {
373
    $('#date').datepicker({
374
      format: 'yyyy-mm-dd',
375
      language: 'cs',
376 8feb1753 ballakt
      beforeShowDay: function (date) {
377 a7e04778 Martin Sebela
        if (availableDates.indexOf(formatDate(date)) < 0) {
378 8feb1753 ballakt
          return { enabled: false, tooltip: 'Žádná data' }
379
        } else {
380
          return { enabled: true }
381 a7e04778 Martin Sebela
        }
382
      },
383
      autoclose: true
384 8feb1753 ballakt
    })
385
  })
386
}
387 dd652e61 Martin Sebela
388 81980e82 ballakt
function initLocationsMenu () {
389
  var locationsWrapper = '.locations'
390
  var locationsDisplayClass = 'show'
391 dd652e61 Martin Sebela
392 81980e82 ballakt
  if ($(window).width() <= 480) {
393
    $(locationsWrapper).removeClass(locationsDisplayClass)
394
  } else {
395
    $(locationsWrapper).addClass(locationsDisplayClass)
396 dd652e61 Martin Sebela
  }
397 4e003182 Martin Sebela
}
398
399 81980e82 ballakt
function openDatepicker () {
400 4e003182 Martin Sebela
  if ($(window).width() <= 990) {
401 81980e82 ballakt
    $('.navbar-collapse').collapse()
402 4e003182 Martin Sebela
  }
403
404
  $('#date').datepicker('show')
405 81980e82 ballakt
}