Projekt

Obecné

Profil

Stáhnout (21 KB) Statistiky
| Větev: | Revize:
1
let app = angular.module('pvpk', ['ngRoute', 'ngResource', 'ngSanitize']);
2

    
3
app.constant('config', {
4
    APP_NAME: 'PVPK',
5
    APP_VERSION: '1.2.0',
6
    API_URL: API_URL,
7
    API_TOKEN: API_TOKEN,
8
    DEFAULT_POSITION: {LAT: 49.53, LNG: 13.3},
9
    DEFAULT_ZOOM: 10,
10
    DEFAULT_ZOOM_MAX: 7,
11
});
12

    
13
app.controller('mainController', function ($rootScope, $scope, $location, $window) {
14

    
15
    this.$onInit = function () {
16
        $scope.showLoadingScreen = true;
17
    };
18

    
19
    $window.onload = function () {
20
        let params = $location.search();
21
        if (params.deviceId) {
22
            $rootScope.$emit('activeMarker', {id: params.deviceId});
23
        }
24

    
25
        $scope.$apply(function () {
26
            $scope.showLoadingScreen = false;
27
        });
28
    };
29

    
30
    $rootScope.$on('$locationChangeSuccess', function (event, newUrl, oldUrl) {
31
        let params = $location.search();
32

    
33
        if (newUrl !== oldUrl && $scope.historyUrl) {
34
            if ($scope.historyUrl.q !== $scope.historyUrl.q || $scope.historyUrl.isDirection != params.isDirection) {
35
                $rootScope.$emit('setSearchFromUrl', null);
36
            }
37

    
38
            if ($scope.historyUrl.fromDate !== params.fromDate || $scope.historyUrl.toDate !== params.toDate ||
39
                $scope.historyUrl.fromTime !== params.fromTime || $scope.historyUrl.toTime !== params.toTime) {
40
                $rootScope.$emit('setRangeFromUrl', null);
41
                if (params.deviceId) {
42
                    $rootScope.$emit('infoLocation', {id: params.deviceId, direction: params.direction});
43
                }
44
            } else if (params.deviceId && ($scope.historyUrl.deviceId !== params.deviceId || $scope.historyUrl.direction !== params.direction)) {
45
                $rootScope.$emit('infoLocation', {id: params.deviceId, direction: params.direction});
46
                $rootScope.$emit('activeMarker', {id: params.deviceId});
47
            } else if (!params.deviceId && $scope.historyUrl.deviceId) {
48
                $rootScope.selectDevice = null;
49
                $rootScope.$emit('setDefaultMap', null);
50
            }
51
        } else if (params.deviceId) {
52
            $rootScope.$emit('infoLocation', {id: params.deviceId, direction: params.direction});
53
        }
54

    
55
        $scope.historyUrl = $location.search();
56
    });
57

    
58
    $rootScope.handleErrorResponse = function (response) {
59

    
60
        let modalError = jQuery('#modalError');
61
        switch (response.status) {
62
            case 400:
63
                console.log('API ERROR 400');
64
                $scope.modalError = {
65
                    title: 'Neplatný požadavek',
66
                    body: 'Požadavek nemůže být vyřízen, poněvadž byl syntakticky nesprávně zapsán.',
67
                    button: 'OK'
68
                };
69
                modalError.modal('show');
70
                break;
71
            case 401:
72
                $scope.modalError = {
73
                    title: 'Platnost webové aplikace vypršela',
74
                    body: 'Pro obnovení platnosti stačí stisknout tlačítko <strong>Obnovit</strong>.',
75
                    button: 'Obnovit',
76
                    clickButton: $scope.reloadApp
77
                };
78
                modalError.modal({backdrop: 'static', keyboard: false});
79
                break;
80
            case 404:
81
                console.log('API ERROR 404');
82
                $scope.modalError = {title: 'Nenalezen', body: 'Záznam nebyl nalezen.', button: 'OK'};
83
                modalError.modal('show');
84
                break;
85
            case 500:
86
                console.log('API ERROR 500');
87
                $scope.modalError = {title: 'Chyba', body: 'Chyba serveru. Zopakujte akci později.', button: 'OK'};
88
                modalError.modal('show');
89
                break;
90
            case -1:
91
                console.log('API NOT CONNECTED');
92
                $scope.modalError = {
93
                    title: 'Připojení k internetu',
94
                    body: 'Nejste připojeni k internetu. Zkontrolujte připojení.',
95
                    button: 'OK'
96
                };
97
                modalError.modal('show');
98
                break;
99
            default:
100
                console.log('API UNKNOWN ERROR');
101
                $scope.modalError = {title: 'Neočekávaná chyba', body: 'Nastala neočekávaná chyba.', button: 'OK'};
102
                modalError.modal('show');
103
                break;
104
        }
105
    };
106

    
107
    $scope.reloadApp = function () {
108
        $window.location.reload();
109
    }
110
});
111

    
112

    
113
app.controller('searchController', function ($rootScope, $scope, $location, config, Device) {
114

    
115
    this.$onInit = function () {
116
        $scope.config = config;
117
        $scope.locations = [];
118
        $scope.showSearchLoading = false;
119

    
120
        $rootScope.$emit('setSearchFromUrl', null);
121
    };
122

    
123
    $scope.searchLocations = function () {
124
        let params = $location.search();
125
        params.q = $scope.search.q;
126
        params.isDirection = $scope.search.isDirection ? 1 : null;
127
        $location.search(params);
128

    
129
        if (!$scope.search.q || $scope.search.q.length <= 1) {
130
            $scope.locations = [];
131
            return;
132
        }
133

    
134
        $scope.showSearchLoading = true;
135

    
136
        Device.query({
137
            address: $scope.search.q,
138
            showDirection: $scope.search.isDirection ? 1 : 0
139
        }, function (data) {
140
            $scope.locations = data;
141
            $scope.showSearchLoading = false;
142
        }, function (response) {
143
            $scope.showSearchLoading = false;
144
            console.log('Error api all Devices');
145
            $rootScope.handleErrorResponse(response);
146
        });
147
    };
148

    
149
    $rootScope.$on('setSearchFromUrl', function (event, args) {
150
        let params = $location.search();
151
        $scope.search = {
152
            q: params.q,
153
            isDirection: params.isDirection ? !!+params.isDirection : false
154
        };
155
        $scope.searchLocations();
156
    });
157

    
158
    $scope.selectDevice = function (id, direction) {
159
        $rootScope.$emit('activeMarker', {id: id});
160
        $rootScope.$emit('infoLocation', {id: id, direction: direction});
161
    };
162

    
163
});
164

    
165

    
166
app.controller('infoController', function ($rootScope, $scope, $location, config, Device, Vehicle) {
167

    
168
    this.$onInit = function () {
169
        $rootScope.selectDevice = null;
170
        $scope.showInfoLoading = false;
171
        $scope.vehicles = [];
172
        $scope.filterVehicles = [];
173
        $scope.urlExportCsv = null;
174

    
175
        Vehicle.query(null, function (data) {
176
            $scope.vehicles = data;
177
        }, function (response) {
178
            $rootScope.graphShow = false;
179
            console.log('Error api all Vehicles');
180
            $rootScope.handleErrorResponse(response);
181
        });
182

    
183
        $rootScope.$emit('setRangeFromUrl', null);
184
    };
185

    
186
    $rootScope.$on('setRangeFromUrl', function (event, args) {
187
        let params = $location.search();
188
        let defaultRange = $scope.defaultRange();
189

    
190
        $scope.range = {
191
            fromDate: moment(params.fromDate, 'YYYY-MM-DD').isValid() ? moment(params.fromDate).toDate() : defaultRange.fromDate.toDate(),
192
            toDate: moment(params.toDate, 'YYYY-MM-DD').isValid() ? moment(params.toDate).toDate() : defaultRange.toDate.toDate(),
193
            fromTime: moment(params.fromTime, 'HH:mm').isValid() ? moment(params.fromTime, 'HH:mm').toDate() : defaultRange.fromTime.toDate(),
194
            toTime: moment(params.toTime, 'HH:mm').isValid() ? moment(params.toTime, 'HH:mm').toDate() : defaultRange.toTime.toDate(),
195
            isTime: params.isTime == 0 ? false : defaultRange.isTime
196
        };
197

    
198
    });
199

    
200
    $rootScope.$on('infoLocation', function (event, args) {
201
        $scope.showInfoLoading = true;
202

    
203
        let params = $location.search();
204
        params.deviceId = args.id;
205
        params.direction = args.direction;
206
        $location.search(params);
207

    
208
        let range = $scope.getRange();
209

    
210
        let query = {
211
            period: range.isTime ? 'time-period' : 'day-period',
212
            id: args.id,
213
            direction: args.direction,
214
            dateFrom: range.fromDate.format('YYYY-MM-DD'),
215
            dateTo: range.toDate.format('YYYY-MM-DD'),
216
            timeFrom: range.isTime ? range.fromTime.format('HH:mm') : null,
217
            timeTo: range.isTime ? range.toTime.format('HH:mm') : null
218
        };
219

    
220
        Device.get(query, function (data) {
221
            $rootScope.selectDevice = data;
222

    
223
            $scope.renderGraphAverageSpeed();
224
            $scope.renderGraphNumberVehicles();
225

    
226
            $scope.urlExportCsv = $scope.generateUrlExportCsv(query);
227

    
228
            $scope.showInfoLoading = false;
229
        }, function (response) {
230
            $rootScope.selectDevice = null;
231
            $scope.showInfoLoading = false;
232
            console.log('Error api get Devices');
233
            $rootScope.handleErrorResponse(response);
234
        });
235

    
236
    });
237

    
238
    $scope.generateUrlExportCsv = function (query) {
239
        let relativeUrl = '/devices/:id/:period/csv?'.replace(':id', query.id).replace(':period', query.period);
240
        delete query.id;
241
        delete query.period;
242

    
243
        let paramsUrl = jQuery.param(query);
244
        return config.API_URL + relativeUrl + paramsUrl;
245
    };
246

    
247
    $scope.changeRange = function () {
248
        if ($scope.range.fromDate >= $scope.range.toDate || ($scope.range.isTime && $scope.range.fromTime >= $scope.range.toTime)) {
249
            $rootScope.selectDevice.traffics = [];
250
            return;
251
        }
252

    
253
        let range = $scope.getRange();
254

    
255
        let params = $location.search();
256
        params.fromDate = range.fromDate.format('YYYY-MM-DD');
257
        params.toDate = range.toDate.format('YYYY-MM-DD');
258
        params.fromTime = range.isTime ? range.fromTime.format('HH:mm') : null;
259
        params.toTime = range.isTime ? range.toTime.format('HH:mm') : null;
260
        params.isTime = range.isTime ? null : 0;
261
        $location.search(params);
262

    
263
        if ($rootScope.selectDevice)
264
            $rootScope.$emit('infoLocation', {
265
                id: $rootScope.selectDevice.id,
266
                direction: $rootScope.selectDevice.direction
267
            });
268
    };
269

    
270
    $scope.getRange = function () {
271
        let defaultRange = $scope.defaultRange();
272

    
273
        return {
274
            fromDate: moment($scope.range.fromDate).isValid() ? moment($scope.range.fromDate) : defaultRange.fromDate,
275
            toDate: moment($scope.range.toDate).isValid() ? moment($scope.range.toDate) : defaultRange.toDate,
276
            fromTime: moment($scope.range.fromTime).isValid() ? moment($scope.range.fromTime) : defaultRange.fromTime,
277
            toTime: moment($scope.range.toTime).isValid() ? moment($scope.range.toTime) : defaultRange.toTime,
278
            isTime: $scope.range.isTime ? true : false
279
        };
280
    };
281

    
282
    $scope.defaultRange = function () {
283
        return {
284
            fromDate: moment().day(-30),
285
            toDate: moment().day(-1),
286
            fromTime: moment({hour: 7}),
287
            toTime: moment({hour: 16}),
288
            isTime: true
289
        };
290
    };
291

    
292
    $scope.renderGraphAverageSpeed = function () {
293

    
294
        let t = $rootScope.selectDevice.traffics.reduce(function (l, r) {
295
            let key = $scope.range.isTime ? r.timeFrom : r.date;
296
            if (typeof l[key] === 'undefined') {
297
                l[key] = {
298
                    numberVehicle: 0,
299
                    speedSum: 0
300
                };
301
            }
302

    
303
            if (r.speedAverage > 0) {
304
                l[key].numberVehicle += r.numberVehicle;
305
                l[key].speedSum += r.speedAverage * r.numberVehicle;
306
            }
307
            return l;
308
        }, {});
309

    
310
        let labels = jQuery.unique($rootScope.selectDevice.traffics.map(function (d) {
311
            return $scope.range.isTime ? d.timeFrom : moment(d.date, 'YYYY-MM-DD').format('D.M.YYYY');
312
        }));
313
        let data = Object.values(t).map(function (d) {
314
            return Math.round(d.speedSum / d.numberVehicle);
315
        });
316

    
317
        let canvasGraphAverageSpeed = document.getElementById('graphAverageSpeed').getContext('2d');
318

    
319
        if ($scope.graphAverageSpeed)
320
            $scope.graphAverageSpeed.destroy();
321

    
322
        $scope.graphAverageSpeed = new Chart(canvasGraphAverageSpeed, {
323
            type: 'line',
324
            data: {
325
                labels: labels,
326
                datasets: [{
327
                    data: data,
328
                    borderWidth: 2,
329
                    label: "Rychlost",
330
                    fill: 'start',
331
                    backgroundColor: 'rgba(0, 123, 255, 0.3)',
332
                    borderColor: 'rgba(0, 123, 255,1)',
333
                    cubicInterpolationMode: 'monotone',
334
                    pointRadius: 0
335
                }]
336
            },
337
            options: {
338
                responsive: true,
339
                pointDot: false,
340
                legend: {
341
                    display: false
342
                },
343
                scales: {
344
                    xAxes: [{
345
                        ticks: {
346
                            autoSkip: true,
347
                            maxTicksLimit: 15
348
                        }
349
                    }],
350
                    yAxes: [{
351
                        scaleLabel: {
352
                            display: true,
353
                            labelString: 'km/h'
354
                        },
355
                        ticks: {
356
                            beginAtZero: true,
357
                            max: Math.max(Math.round((Math.max.apply(null, data) + 10) / 10) * 10, 70)
358
                        }
359
                    }]
360
                },
361
                tooltips: {
362
                    mode: 'index',
363
                    intersect: false,
364
                    callbacks: {
365
                        label: function (tooltipItems) {
366
                            return tooltipItems.yLabel + ' km/h';
367
                        }
368
                    }
369
                }
370
            }
371
        });
372
    };
373

    
374

    
375
    $scope.renderGraphNumberVehicles = function () {
376
        let color = ['rgba(158, 158, 158, #alpha)', 'rgba(213, 0, 0, #alpha)', 'rgba(0, 123, 255, #alpha)', 'rgba(170, 0, 255, #alpha)',
377
            'rgba(0, 200, 83, #alpha)', 'rgba(255, 214, 0, #alpha)', 'rgba(255, 109, 0, #alpha)',
378
            'rgba(174, 234, 0, #alpha)', 'rgba(98, 0, 234, #alpha)', 'rgba(255, 171, 0, #alpha)', 'rgba(100, 221, 23, #alpha)', 'rgba(0, 184, 212, #alpha)'];
379

    
380

    
381
        let labels = jQuery.unique($rootScope.selectDevice.traffics.map(function (d) {
382
            return $scope.range.isTime ? d.timeFrom : moment(d.date, 'YYYY-MM-DD').format('D.M.YYYY');
383
        }));
384

    
385
        let useVehiclesIds = jQuery.unique($rootScope.selectDevice.traffics.map(function (d) {
386
            return d.typeVehicleId;
387
        }));
388

    
389
        $scope.filterVehicles = jQuery.grep($scope.vehicles, function (n) {
390
            return useVehiclesIds.indexOf(n.id) >= 0;
391
        });
392

    
393
        let datasets = [];
394
        for (let i = 0, vehicle; vehicle = $scope.filterVehicles[i]; i++) {
395
            let dataset = {
396
                label: vehicle.name,
397
                backgroundColor: color[vehicle.id].replace("#alpha", "0.3"),
398
                borderColor: color[vehicle.id].replace("#alpha", "1"),
399
                borderWidth: 2,
400
                data: []
401
            };
402

    
403
            let l = 0;
404
            for (let j = 0, traffic; traffic = $rootScope.selectDevice.traffics[j]; j++) {
405
                if (($scope.range.isTime && labels[l] !== traffic.timeFrom) || (!$scope.range.isTime && labels[l] !== moment(traffic.date, 'YYYY-MM-DD').format('D.M.YYYY'))) {
406
                    l++;
407
                    if (dataset.data.length < l) {
408
                        dataset.data.push(0);
409
                    }
410
                }
411
                if (traffic.typeVehicleId === vehicle.id) {
412
                    dataset.data.push($scope.range.isTime ? traffic.numberVehicleAverage : traffic.numberVehicle);
413
                }
414
            }
415
            datasets.push(dataset);
416
        }
417

    
418
        let canvasGraphNumberVehicles = document.getElementById('graphNumberVehicles').getContext('2d');
419

    
420
        if ($scope.graphNumberVehicles)
421
            $scope.graphNumberVehicles.destroy();
422

    
423
        $scope.graphNumberVehicles = new Chart(canvasGraphNumberVehicles, {
424
            type: 'bar',
425
            data: {
426
                labels: labels,
427
                datasets: datasets
428
            },
429
            options: {
430
                responsive: true,
431
                onResize: function (chart, size) {
432
                    chart.options.legend.display = size.height > 240;
433
                    chart.update();
434
                },
435
                legend: {
436
                    position: 'bottom',
437
                },
438
                scales: {
439
                    xAxes: [{
440
                        stacked: true,
441
                        ticks: {
442
                            autoSkip: true,
443
                            maxTicksLimit: 15
444
                        }
445
                    }],
446
                    yAxes: [{
447
                        scaleLabel: {
448
                            display: true,
449
                            labelString: "počet vozidel"
450
                        },
451
                        stacked: true
452
                    }]
453
                },
454
                tooltips: {
455
                    mode: 'index',
456
                    intersect: false
457
                }
458
            }
459
        });
460
    };
461

    
462
    $scope.infoClose = function () {
463
        $rootScope.selectDevice = null;
464

    
465
        let params = $location.search();
466
        params.deviceId = null;
467
        params.direction = null;
468
        $location.search(params);
469

    
470
        $rootScope.$emit('setDefaultMap', null);
471
    };
472
});
473

    
474

    
475
app.controller('mapController', function ($rootScope, $scope, config, Device) {
476

    
477
    this.$onInit = function () {
478
        $scope.markers = [];
479

    
480
        $scope.map = new google.maps.Map(document.getElementById('map'), {
481
            center: {lat: config.DEFAULT_POSITION.LAT, lng: config.DEFAULT_POSITION.LNG},
482
            zoom: config.DEFAULT_ZOOM,
483
            minZoom: config.DEFAULT_ZOOM_MAX,
484
            zoomControl: true,
485
            mapTypeControl: false,
486
            scaleControl: false,
487
            streetViewControl: false,
488
            rotateControl: false,
489
            fullscreenControl: false,
490
            mapTypeId: google.maps.MapTypeId.ROADMAP
491
        });
492

    
493
        Device.query({showDirection: 0}, function (data) {
494
            for (let i = 0, lctn; lctn = data[i]; i++) {
495
                $scope.createMarker(lctn);
496
            }
497
        }, function (response) {
498
            console.log('Error api all Devices');
499
            $rootScope.handleErrorResponse(response);
500
        });
501
    };
502

    
503
    $scope.createMarker = function (lctn) {
504
        if (lctn.lat && lctn.lng) {
505
            let marker = new google.maps.Marker({
506
                map: $scope.map,
507
                position: {lat: lctn.lat, lng: lctn.lng},
508
                title: lctn.name,
509
                infoWindow: new google.maps.InfoWindow({
510
                    content: '<h6 class="mb-1">' + lctn.name + '</h6>'
511
                    + '<address>' + lctn.street + ', ' + lctn.town + '</address>'
512
                }),
513
                id: lctn.id
514
            });
515

    
516
            marker.addListener('click', function () {
517
                $scope.closeInfoWindows();
518
                marker.infoWindow.open($scope.map, marker);
519
                $rootScope.$emit('infoLocation', {id: lctn.id});
520
            });
521

    
522
            $scope.markers.push(marker);
523
        }
524
    };
525

    
526
    $rootScope.$on('activeMarker', function (event, args) {
527
        let id = args.id;
528
        for (let i = 0, marker; marker = $scope.markers[i]; i++) {
529
            if (marker.id && marker.id === id && marker.infoWindow) {
530
                $scope.map.setCenter(marker.getPosition());
531
                $scope.map.setZoom(12);
532
                marker.infoWindow.open($scope.map, marker);
533
            } else {
534
                marker.infoWindow.close();
535
            }
536
        }
537
    });
538

    
539
    $rootScope.$on('setDefaultMap', function (event, args) {
540
        $scope.map.setCenter({lat: config.DEFAULT_POSITION.LAT, lng: config.DEFAULT_POSITION.LNG});
541
        $scope.map.setZoom(config.DEFAULT_ZOOM);
542
        $scope.closeInfoWindows();
543
    });
544

    
545
    $scope.closeInfoWindows = function () {
546
        for (let i = 0, marker; marker = $scope.markers[i]; i++) {
547
            marker.infoWindow.close();
548
        }
549
    };
550
});
551

    
552

    
553
app.factory('Device', function ($resource, config) {
554
    return $resource(config.API_URL + '/devices/:id', {id: '@id', period: '@period'}, {
555
        'get': {
556
            url: config.API_URL + '/devices/:id/:period',
557
            method: 'GET',
558
            headers: {
559
                'Content-Type': 'application/json',
560
                'Accept': 'application/json',
561
                'jwt': config.API_TOKEN
562
            }
563
        },
564
        'query': {
565
            url: config.API_URL + '/devices',
566
            method: 'GET',
567
            isArray: true,
568
            headers: {
569
                'Content-Type': 'application/json',
570
                'Accept': 'application/json',
571
                'jwt': config.API_TOKEN
572
            }
573
        }
574
    });
575
});
576

    
577
app.factory('Vehicle', function ($resource, config) {
578
    return $resource(config.API_URL + '/vehicles', null, {
579
        'query': {
580
            url: config.API_URL + '/vehicles',
581
            method: 'GET',
582
            isArray: true,
583
            headers: {
584
                'Content-Type': 'application/json',
585
                'Accept': 'application/json',
586
                'jwt': config.API_TOKEN
587
            }
588
        }
589
    });
590
});
(2-2/6)