Revize c892003d
Přidáno uživatelem Martin Sebela před více než 4 roky(ů)
website/public/css/style.css | ||
---|---|---|
893 | 893 |
position: relative; |
894 | 894 |
padding: 0 10px; |
895 | 895 |
border-radius: 100px; |
896 |
cursor: pointer; |
|
896 | 897 |
background-color: rgba(11, 21, 90, 0.7); |
897 | 898 |
} |
898 | 899 |
|
... | ... | |
1101 | 1102 |
.leaflet-popup-content-wrapper, .leaflet-popup-tip { |
1102 | 1103 |
font-family: 'Be Vietnam', sans-serif; |
1103 | 1104 |
text-align: center; |
1104 |
color: white;
|
|
1105 |
color: #ffffff;
|
|
1105 | 1106 |
} |
1106 | 1107 |
|
1107 | 1108 |
.leaflet-popup-content-wrapper .leaflet-popup-content strong { |
... | ... | |
1166 | 1167 |
} |
1167 | 1168 |
|
1168 | 1169 |
header.map .nav-item .btn-secondary { |
1170 |
display: flex; |
|
1171 |
justify-content: center; |
|
1172 |
align-items: center; |
|
1169 | 1173 |
outline: none; |
1170 | 1174 |
border-radius: 50px; |
1171 | 1175 |
height: 40px; |
... | ... | |
1177 | 1181 |
-webkit-box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.17); |
1178 | 1182 |
-moz-box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.17); |
1179 | 1183 |
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.17); |
1180 |
text-align: center; |
|
1181 |
display: flex; |
|
1182 |
justify-content: center; |
|
1183 |
align-items: center; |
|
1184 | 1184 |
} |
1185 | 1185 |
|
1186 | 1186 |
@media (max-width: 991.98px) { |
... | ... | |
1229 | 1229 |
color: #000; |
1230 | 1230 |
} |
1231 | 1231 |
|
1232 |
header.map .nav-item select:hover { |
|
1233 |
cursor: pointer; |
|
1234 |
background: rgba(255, 255, 255, 0.2); |
|
1235 |
} |
|
1236 |
|
|
1237 |
header.map .nav-item select:hover option { |
|
1238 |
background: #0048A9; |
|
1239 |
} |
|
1240 |
|
|
1241 |
header.map .nav-item select:focus { |
|
1242 |
background: #0048A9; |
|
1243 |
color: white; |
|
1244 |
} |
|
1245 |
|
|
1246 |
.custom-dropdown-item { |
|
1247 |
margin: 0px !important; |
|
1248 |
} |
|
1249 |
|
|
1250 |
.custom-dropdown-item .disabled { |
|
1251 |
cursor: not-allowed !important; |
|
1252 |
} |
|
1253 |
|
|
1254 |
.custom-control-label { |
|
1255 |
font-weight: 300 !important; |
|
1256 |
margin: 0px !important; |
|
1232 |
.dropdown { |
|
1233 |
min-width: 200px; |
|
1257 | 1234 |
} |
1258 | 1235 |
|
1259 |
.custom-dropdown { |
|
1260 |
cursor: not-allowed; |
|
1261 |
} |
|
1262 |
|
|
1263 |
input[type=radio]:checked ~ label { |
|
1236 |
.dropdown input[type="radio"]:checked ~ label { |
|
1264 | 1237 |
font-weight: 800; |
1265 | 1238 |
} |
1266 | 1239 |
|
... | ... | |
1280 | 1253 |
width: 100%; |
1281 | 1254 |
max-height: 300px; |
1282 | 1255 |
overflow-y: auto; |
1256 |
z-index: 1001; |
|
1257 |
} |
|
1258 |
|
|
1259 |
.dropdown .dropdown-item:active { |
|
1260 |
background: #0048A9; |
|
1283 | 1261 |
} |
1284 | 1262 |
|
1285 | 1263 |
.dropdown .dropdown-item label { |
1286 | 1264 |
width: 100% !important; |
1265 |
margin-left: 0; |
|
1287 | 1266 |
cursor: pointer; |
1288 | 1267 |
} |
1289 | 1268 |
|
1290 |
.dropdown #dataset-dropdown-time .dropdown-item:nth-of-type(even) {
|
|
1269 |
.dropdown #dropdown-time .dropdown-item:nth-of-type(even) { |
|
1291 | 1270 |
background: rgba(0, 0, 0, 0.05); |
1292 | 1271 |
} |
1293 | 1272 |
|
1294 |
.dropdown #dataset-dropdown-time .dropdown-item:nth-of-type(even):active {
|
|
1295 |
background: #007bff;
|
|
1273 |
.dropdown #dropdown-time .dropdown-item:nth-of-type(even):active { |
|
1274 |
background: #0048A9;
|
|
1296 | 1275 |
} |
website/public/css/style.scss | ||
---|---|---|
6 | 6 |
|
7 | 7 |
$primary-bg-color: #0048A9; |
8 | 8 |
$primary-bg-color-darker: #004fb3; |
9 |
$input-bg-hover: #336dba; |
|
9 | 10 |
$secondary-bg-color: #0b155a; |
10 | 11 |
|
11 | 12 |
$primary-text-color: #ffffff; |
... | ... | |
717 | 718 |
} |
718 | 719 |
|
719 | 720 |
|
720 |
// PŘEHRÁVAČ HEATMAPY
|
|
721 |
// DOLNÍ OVLÁDACÍ PANEL HEATMAPY
|
|
721 | 722 |
.player { |
722 | 723 |
width: 100%; |
723 | 724 |
display: flex; |
... | ... | |
850 | 851 |
position: relative; |
851 | 852 |
padding: 0 10px; |
852 | 853 |
border-radius: 100px; |
854 |
cursor: pointer; |
|
853 | 855 |
background-color: rgba(11, 21, 90, .7); |
854 | 856 |
|
855 | 857 |
@include media-breakpoint-down(md) { |
... | ... | |
955 | 957 |
background: url($images-dir + 'footer-bg.svg') no-repeat; |
956 | 958 |
|
957 | 959 |
@include media-breakpoint-down(md) { |
958 |
|
|
959 | 960 |
width: 320px; |
960 | 961 |
height: 143px; |
961 | 962 |
background-size: 100%; |
... | ... | |
1003 | 1004 |
} |
1004 | 1005 |
} |
1005 | 1006 |
|
1006 |
// INFORMACE O KONKRÉTNÍM BODĚ V HEATMAPĚ |
|
1007 |
|
|
1008 |
// POPUP O KONKRÉTNÍM BODĚ V HEATMAPĚ |
|
1007 | 1009 |
.leaflet-popup-content-wrapper, .leaflet-popup-tip { |
1008 | 1010 |
font-family: 'Be Vietnam', sans-serif; |
1009 | 1011 |
text-align: center; |
1010 |
color: white;
|
|
1012 |
color: $primary-text-color;
|
|
1011 | 1013 |
} |
1012 | 1014 |
|
1013 | 1015 |
.leaflet-popup-content-wrapper { |
... | ... | |
1074 | 1076 |
} |
1075 | 1077 |
|
1076 | 1078 |
.btn-secondary { |
1079 |
display: flex; |
|
1080 |
justify-content: center; |
|
1081 |
align-items: center; |
|
1077 | 1082 |
outline: none; |
1078 | 1083 |
border-radius: 50px; |
1079 | 1084 |
height: 40px; |
... | ... | |
1085 | 1090 |
-webkit-box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.17); |
1086 | 1091 |
-moz-box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.17); |
1087 | 1092 |
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.17); |
1088 |
text-align: center; |
|
1089 |
display: flex; |
|
1090 |
justify-content: center; |
|
1091 |
align-items: center; |
|
1092 | 1093 |
|
1093 | 1094 |
@include media-breakpoint-down(md) { |
1094 | 1095 |
width: 100%; |
... | ... | |
1098 | 1099 |
margin-bottom: -10px; |
1099 | 1100 |
} |
1100 | 1101 |
|
1101 |
&:after{ |
|
1102 |
&:after {
|
|
1102 | 1103 |
padding-bottom: 4px; |
1103 | 1104 |
content: '↺'; |
1104 | 1105 |
font-weight: 400; |
... | ... | |
1136 | 1137 |
color: #000; |
1137 | 1138 |
} |
1138 | 1139 |
} |
1140 |
} |
|
1139 | 1141 |
|
1140 |
select { |
|
1141 |
&:hover { |
|
1142 |
cursor: pointer; |
|
1143 |
background: rgba(255, 255, 255, .2); |
|
1144 | 1142 |
|
1145 |
option { |
|
1146 |
background: $primary-bg-color; |
|
1147 |
} |
|
1148 |
} |
|
1143 |
// MENU PRO VÝBĚR ČASU/DATOVÝCH SAD |
|
1144 |
.dropdown { |
|
1145 |
min-width: 200px; |
|
1149 | 1146 |
|
1150 |
&:focus { |
|
1151 |
background: $primary-bg-color; |
|
1152 |
color: white; |
|
1153 |
} |
|
1147 |
input[type="radio"]:checked ~ label { |
|
1148 |
font-weight: 800; |
|
1154 | 1149 |
} |
1155 |
} |
|
1156 |
.custom-dropdown-item { |
|
1157 |
margin: 0px !important; |
|
1158 |
} |
|
1159 |
.custom-dropdown-item .disabled { |
|
1160 |
cursor: not-allowed !important; |
|
1161 |
} |
|
1162 |
.custom-control-label { |
|
1163 |
font-weight: 300 !important; |
|
1164 |
margin: 0px !important; |
|
1165 |
} |
|
1166 |
.custom-dropdown { |
|
1167 |
cursor: not-allowed; |
|
1168 |
} |
|
1169 | 1150 |
|
1170 |
input[type=radio]:checked~label { |
|
1171 |
font-weight: 800; |
|
1172 |
} |
|
1173 |
|
|
1174 |
.dropdown { |
|
1175 | 1151 |
.btn { |
1176 | 1152 |
width: 100%; |
1177 | 1153 |
text-align: left; |
... | ... | |
1180 | 1156 |
background: $primary-bg-color; |
1181 | 1157 |
|
1182 | 1158 |
&:hover { |
1183 |
background: #336dba;
|
|
1159 |
background: $input-bg-hover;
|
|
1184 | 1160 |
} |
1185 | 1161 |
} |
1186 | 1162 |
|
... | ... | |
1188 | 1164 |
width: 100%; |
1189 | 1165 |
max-height: 300px; |
1190 | 1166 |
overflow-y: auto; |
1167 |
z-index: 1001; |
|
1191 | 1168 |
} |
1192 | 1169 |
|
1193 |
.dropdown-item label { |
|
1194 |
width: 100% !important; |
|
1195 |
cursor: pointer; |
|
1170 |
.dropdown-item { |
|
1171 |
&:active { |
|
1172 |
background: $primary-bg-color; |
|
1173 |
} |
|
1174 |
|
|
1175 |
label { |
|
1176 |
width: 100% !important; |
|
1177 |
margin-left: 0; |
|
1178 |
cursor: pointer; |
|
1179 |
} |
|
1196 | 1180 |
} |
1197 | 1181 |
|
1198 |
#dataset-dropdown-time .dropdown-item:nth-of-type(even) {
|
|
1182 |
#dropdown-time .dropdown-item:nth-of-type(even) { |
|
1199 | 1183 |
background: rgba(0,0,0,.05); |
1200 | 1184 |
|
1201 | 1185 |
&:active { |
1202 |
background: #007bff;
|
|
1186 |
background: $primary-bg-color;
|
|
1203 | 1187 |
} |
1204 | 1188 |
} |
1205 | 1189 |
} |
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 |
} |
website/templates/base.html.twig | ||
---|---|---|
1 | 1 |
<!DOCTYPE html> |
2 | 2 |
<html lang="cs"> |
3 |
<head>
|
|
4 |
<meta charset="utf-8">
|
|
3 |
<head> |
|
4 |
<meta charset="utf-8"> |
|
5 | 5 |
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
7 |
<meta name="description" content="Heatmapa kampusu Západočeské univerzity v Plzni">
|
|
8 |
<meta name="keywords" content="heatmap, opendata, západočeská univerzita, zču, plzeň">
|
|
9 |
<meta name="author" content="Tým BHVS – Tomáš Ballák, Petr Hlaváč, Jakub Vašta, Martin Šebela">
|
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
|
7 |
<meta name="description" content="Heatmapa kampusu Západočeské univerzity v Plzni"> |
|
8 |
<meta name="keywords" content="heatmap, opendata, otevřená data, západočeská univerzita, zču, plzeň">
|
|
9 |
<meta name="author" content="Tým BHVS – Tomáš Ballák, Petr Hlaváč, Jakub Vašta, Martin Šebela"> |
|
10 | 10 |
|
11 |
<meta property="og:url" content="https://heatmap.zcu.cz">
|
|
12 |
<meta property="og:type" content="website">
|
|
13 |
<meta property="og:title" content="Heatmapa kampusu Západočeské univerzity v Plzni">
|
|
14 |
<meta property="og:description" content="Hodina po hodině, den po dni.">
|
|
15 |
<meta property="og:image" content="https://heatmap.zcu.cz/img/banner.jpg">
|
|
11 |
<meta property="og:url" content="https://heatmap.zcu.cz"> |
|
12 |
<meta property="og:type" content="website"> |
|
13 |
<meta property="og:title" content="Heatmapa kampusu Západočeské univerzity v Plzni"> |
|
14 |
<meta property="og:description" content="Hodina po hodině, den po dni."> |
|
15 |
<meta property="og:image" content="https://heatmap.zcu.cz/img/banner.jpg"> |
|
16 | 16 |
|
17 |
<link rel="shortcut icon" href="favicon.ico">
|
|
17 |
<link rel="shortcut icon" href="favicon.ico"> |
|
18 | 18 |
|
19 |
<title>{% block title %}{% endblock %}</title> |
|
20 |
{% block stylesheets %}{% endblock %} |
|
21 |
</head> |
|
22 |
<body{% block bodyClass %}{% endblock %}> |
|
23 |
{% block body %}{% endblock %} |
|
24 |
{% block javascripts %}{% endblock %} |
|
25 |
{% block style %}{% endblock %} |
|
26 |
</body> |
|
19 |
<title>{% block title %}{% endblock %}</title> |
|
20 |
|
|
21 |
{% block stylesheets %}{% endblock %} |
|
22 |
{% block style %}{% endblock %} |
|
23 |
</head> |
|
24 |
<body{% block bodyClass %}{% endblock %}> |
|
25 |
{% block body %}{% endblock %} |
|
26 |
{% block javascripts %}{% endblock %} |
|
27 |
</body> |
|
27 | 28 |
</html> |
website/templates/heatmap.html.twig | ||
---|---|---|
2 | 2 |
|
3 | 3 |
{% block title %}Heatmap.ZČU{% endblock %} |
4 | 4 |
{% block stylesheets %} |
5 |
<link rel="stylesheet" href="{{ asset('css/leaflet.css') }}">
|
|
6 |
<link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}"> |
|
7 |
<link rel="stylesheet" href="{{ asset('css/style.css') }}"> |
|
8 |
<link rel="stylesheet" href="{{ asset('css/bootstrap-datepicker3.css') }}"> |
|
5 |
<link rel="stylesheet" href="{{ asset('css/leaflet.css') }}"> |
|
6 |
<link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}">
|
|
7 |
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
|
|
8 |
<link rel="stylesheet" href="{{ asset('css/bootstrap-datepicker3.css') }}">
|
|
9 | 9 |
{% endblock %} |
10 | 10 |
|
11 | 11 |
{% block body %} |
... | ... | |
47 | 47 |
<button type="button" class="btn dropdown-toggle" id="dropdownMenuButtonTime" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
48 | 48 |
{{ current_time }} |
49 | 49 |
</button> |
50 |
<div id="dataset-dropdown-time" class="dropdown-menu" aria-labelledby="dropdownMenuButtonTime">
|
|
50 |
<div id="dropdown-time" class="dropdown-menu" aria-labelledby="dropdownMenuButtonTime"> |
|
51 | 51 |
{% for hour in form.time.children %} |
52 | 52 |
<div class="dropdown-item custom-dropdown-item"> |
53 | 53 |
{{ form_widget(hour, {attr: {class: 'd-none'}}) }} |
... | ... | |
62 | 62 |
<button type="button" class="btn dropdown-toggle font-weight-bold" id="dropdownMenuButtonDataset" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
63 | 63 |
Vyberte datovou sadu |
64 | 64 |
</button> |
65 |
<div id="dataset-dropdown" class="dropdown-menu" aria-labelledby="dropdownMenuButtonDataset">
|
|
65 |
<div id="dropdown-dataset" class="dropdown-menu" aria-labelledby="dropdownMenuButtonDataset">
|
|
66 | 66 |
{% for dataset in form.type.children %} |
67 | 67 |
<div class="dropdown-item custom-dropdown-item"> |
68 | 68 |
<div class="custom-control custom-checkbox custom-checkbox-{{dataset.vars.value}}"> |
... | ... | |
84 | 84 |
<li class="nav-item"> |
85 | 85 |
{{ form_widget(form.submit, |
86 | 86 |
{ |
87 |
'name' : '', |
|
88 |
'label' : '', |
|
89 | 87 |
'type' : 'submit', |
88 |
'name' : '', |
|
90 | 89 |
'id' : 'btn-update-heatmap', |
91 | 90 |
'attr' : { |
92 | 91 |
'class': 'btn btn-secondary' |
... | ... | |
126 | 125 |
<div class="time hour-0" id="timeline">0:00</div> |
127 | 126 |
|
128 | 127 |
{% for i in 0..23 %} |
129 |
<div class="hour"></div> |
|
128 |
<div class="hour" title="{{ i }}:00" onclick="changeHour('{{ i }}')"></div>
|
|
130 | 129 |
{% endfor %} |
131 | 130 |
<div class="end-dot"></div> |
132 | 131 |
</div> |
... | ... | |
138 | 137 |
{% endblock %} |
139 | 138 |
|
140 | 139 |
{% block javascripts %} |
141 |
|
|
142 | 140 |
<script src="{{ asset('js/vendor/jquery-3.4.1.min.js') }}"></script> |
143 | 141 |
<script src="{{ asset('js/vendor/bootstrap.min.js') }}"></script> |
144 | 142 |
<script src="{{ asset('js/vendor/bootstrap-datepicker.min.js') }}"></script> |
... | ... | |
147 | 145 |
<script src="{{ asset('js/vendor/leaflet-heat.js') }}"></script> |
148 | 146 |
<script src="{{ asset('js/zcu-heatmap.js') }}"></script> |
149 | 147 |
<script> |
150 |
initDatepicker("{{ path('dates') }}"); |
|
148 |
initDatepicker("{{ path('dates') }}"); |
|
149 |
initLocationsMenu(); |
|
150 |
initMap(); |
|
151 |
|
|
152 |
$(document).ready(function() { |
|
153 |
onDocumentReady(); |
|
154 |
checkDataSetsAvailability("{{ path('available') }}") |
|
155 |
{% if submitted %} |
|
156 |
loadCurrentTimeHeatmap("{{ path('opendata') }}", "{{ path('positions') }}"); |
|
157 |
{% endif %} |
|
158 |
}); |
|
159 |
|
|
160 |
$(window).resize(function() { |
|
151 | 161 |
initLocationsMenu(); |
152 |
initMap(); |
|
153 |
|
|
154 |
$(document).ready(function() { |
|
155 |
onDocumentReady(); |
|
156 |
checkDataSetsAvailability("{{ path('available') }}") |
|
157 |
{% if submitted %} |
|
158 |
loadCurrentTimeHeatmap("{{ path('opendata') }}", "{{ path('positions') }}"); |
|
159 |
{% endif %} |
|
160 |
}); |
|
161 |
|
|
162 |
$(window).resize(function() { |
|
163 |
initLocationsMenu(); |
|
164 |
}); |
|
162 |
}); |
|
165 | 163 |
</script> |
166 |
|
|
167 | 164 |
{% endblock %} |
168 | 165 |
|
169 | 166 |
{% block style %} |
170 |
<style type="text/css">
|
|
171 |
{% for dataset_name, dataset_color in dataset_colors %}
|
|
172 |
.custom-control-input-{{ dataset_name }}:focus~.custom-control-label::before {
|
|
173 |
border-color: {{ dataset_color }} !important;
|
|
174 |
box-shadow: 0 0 0 0.2rem rgba(192,192,192, 0.4) !important;
|
|
175 |
}
|
|
176 |
|
|
177 |
.custom-control-input-{{ dataset_name }}:checked~.custom-control-label::before {
|
|
178 |
border-color: {{ dataset_color }} !important;
|
|
179 |
background-color: {{ dataset_color }} !important;
|
|
180 |
}
|
|
181 |
|
|
182 |
.custom-control-input-{{ dataset_name }}:focus:not(:checked)~.custom-control-label::before {
|
|
183 |
border-color: {{ dataset_color }} !important;
|
|
184 |
}
|
|
185 |
|
|
186 |
.custom-control-input-{{ dataset_name }}:not(:disabled):active~.custom-control-label::before {
|
|
187 |
background-color: {{ dataset_color }} !important;
|
|
188 |
border-color: {{ dataset_color }} !important;
|
|
189 |
}
|
|
190 |
|
|
191 |
.popup-{{ dataset_name }} > .leaflet-popup-content-wrapper, .popup-{{ dataset_name }} .leaflet-popup-tip {
|
|
192 |
background-color: {{ dataset_color }} !important;
|
|
193 |
}
|
|
194 |
{% endfor %}
|
|
195 |
</style>
|
|
167 |
<style type="text/css"> |
|
168 |
{% for dataset_name, dataset_color in dataset_colors %} |
|
169 |
.custom-control-input-{{ dataset_name }}:focus~.custom-control-label::before { |
|
170 |
border-color: {{ dataset_color }} !important; |
|
171 |
box-shadow: 0 0 0 0.2rem rgba(192,192,192, 0.4) !important; |
|
172 |
} |
|
173 |
|
|
174 |
.custom-control-input-{{ dataset_name }}:checked~.custom-control-label::before { |
|
175 |
border-color: {{ dataset_color }} !important; |
|
176 |
background-color: {{ dataset_color }} !important; |
|
177 |
} |
|
178 |
|
|
179 |
.custom-control-input-{{ dataset_name }}:focus:not(:checked)~.custom-control-label::before { |
|
180 |
border-color: {{ dataset_color }} !important; |
|
181 |
} |
|
182 |
|
|
183 |
.custom-control-input-{{ dataset_name }}:not(:disabled):active~.custom-control-label::before { |
|
184 |
background-color: {{ dataset_color }} !important; |
|
185 |
border-color: {{ dataset_color }} !important; |
|
186 |
} |
|
187 |
|
|
188 |
.popup-{{ dataset_name }} > .leaflet-popup-content-wrapper, .popup-{{ dataset_name }} .leaflet-popup-tip { |
|
189 |
background-color: {{ dataset_color }} !important; |
|
190 |
} |
|
191 |
{% endfor %} |
|
192 |
</style> |
|
196 | 193 |
{% endblock %} |
Také k dispozici: Unified diff
Re #8159 - CSS improvements, timeline clickable, code refactoring