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