fleetmap-reports 1.0.391 → 1.0.398
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -4
- package/src/index.test.js +340 -328
- package/src/refueling-report.js +149 -147
- package/src/speeding-report.js +112 -111
- package/src/tests/index.js +14 -16
- package/src/util/utils.js +152 -135
- package/src/zone-report.js +34 -30
package/src/util/utils.js
CHANGED
|
@@ -1,179 +1,196 @@
|
|
|
1
1
|
const axios = require('axios')
|
|
2
|
-
const turf = require('turf')
|
|
3
|
-
const {getUserPartner} = require(
|
|
4
|
-
const messages = require(
|
|
2
|
+
const turf = require('@turf/helpers')
|
|
3
|
+
const { getUserPartner } = require('fleetmap-partners')
|
|
4
|
+
const messages = require('../../lang')
|
|
5
|
+
const distance = require('@turf/distance')
|
|
5
6
|
|
|
6
7
|
exports.getTranslations = (userData) => {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const lang = userData.user.attributes.lang || (navigator && navigator.language)
|
|
9
|
+
return messages[lang] ? messages[lang] : messages['en-GB']
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
function convertMS(duration, withSeconds) {
|
|
12
|
+
function convertMS (duration, withSeconds) {
|
|
13
|
+
if (!duration || duration < 0) {
|
|
14
|
+
return '00:00'
|
|
15
|
+
}
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let seconds = s % 60;
|
|
20
|
-
s = (s - seconds) / 60;
|
|
21
|
-
let minutes = s % 60;
|
|
22
|
-
let hours = (s - minutes) / 60;
|
|
17
|
+
const ms = duration % 1000
|
|
18
|
+
let s = (duration - ms) / 1000
|
|
19
|
+
let seconds = s % 60
|
|
20
|
+
s = (s - seconds) / 60
|
|
21
|
+
let minutes = s % 60
|
|
22
|
+
let hours = (s - minutes) / 60
|
|
23
23
|
|
|
24
|
+
hours = (hours < 10) ? '0' + hours : hours
|
|
25
|
+
minutes = (minutes < 10) ? '0' + minutes : minutes
|
|
26
|
+
seconds = (seconds < 10) ? '0' + seconds : seconds
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
minutes = (minutes < 10) ? "0" + minutes : minutes
|
|
27
|
-
seconds = (seconds < 10) ? "0" + seconds : seconds
|
|
28
|
-
|
|
29
|
-
return withSeconds ? hours + ":" + minutes + ":" + seconds : hours + ":" + minutes
|
|
28
|
+
return withSeconds ? hours + ':' + minutes + ':' + seconds : hours + ':' + minutes
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
function coordsDistance(lon1, lat1, lon2, lat2) {
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
function coordsDistance (lon1, lat1, lon2, lat2) {
|
|
32
|
+
const from = turf.point([lon1, lat1])
|
|
33
|
+
const to = turf.point([lon2, lat2])
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
return distance.default(from, to, 'meters')
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
function convertPositionToFeature(position) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
function convertPositionToFeature (position) {
|
|
39
|
+
const point = turf.point([position.longitude, position.latitude])
|
|
40
|
+
point.properties.position = position
|
|
41
|
+
return point
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
function convertToFeature(geofence) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
const str = geofence.area.substring('POLYGON(('.length, geofence.area.length - 2)
|
|
58
|
-
const coord_list = str.split(',')
|
|
59
|
-
for (const i in coord_list) {
|
|
60
|
-
const coord = coord_list[i].trim().split(' ')
|
|
61
|
-
feature.geometry.coordinates[0].push([parseFloat(coord[1]), parseFloat(coord[0])])
|
|
62
|
-
}
|
|
63
|
-
return feature
|
|
44
|
+
function convertToFeature (geofence) {
|
|
45
|
+
if (geofence.area.startsWith('POLYGON')) {
|
|
46
|
+
const feature = {
|
|
47
|
+
type: 'Feature',
|
|
48
|
+
properties: {
|
|
49
|
+
geofence
|
|
50
|
+
},
|
|
51
|
+
geometry: {
|
|
52
|
+
type: 'Polygon',
|
|
53
|
+
coordinates: [[]]
|
|
54
|
+
}
|
|
64
55
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const point = turf.point([parseFloat(coord[1]), parseFloat(coord[0])])
|
|
71
|
-
point.properties.geofence = geofence
|
|
72
|
-
point.properties.distance = coord_list[1].trim()
|
|
73
|
-
console.log(point)
|
|
74
|
-
return point
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function getDates(startDate, endDate) {
|
|
79
|
-
const dates = []
|
|
80
|
-
let currentDate = new Date(startDate.toISOString().split('T')[0] + ' 12:00:00.000 PM')
|
|
81
|
-
const addDays = function (days) {
|
|
82
|
-
const date = new Date(this.valueOf())
|
|
83
|
-
date.setDate(date.getDate() + days)
|
|
84
|
-
return date
|
|
56
|
+
const str = geofence.area.substring('POLYGON(('.length, geofence.area.length - 2)
|
|
57
|
+
const coord_list = str.split(',')
|
|
58
|
+
for (const i in coord_list) {
|
|
59
|
+
const coord = coord_list[i].trim().split(' ')
|
|
60
|
+
feature.geometry.coordinates[0].push([parseFloat(coord[1]), parseFloat(coord[0])])
|
|
85
61
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
62
|
+
return feature
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (geofence.area.startsWith('CIRCLE')) {
|
|
66
|
+
const str = geofence.area.substring('CIRCLE ('.length, geofence.area.length - 1)
|
|
67
|
+
const coord_list = str.split(',')
|
|
68
|
+
const coord = coord_list[0].trim().split(' ')
|
|
69
|
+
const point = turf.point([parseFloat(coord[1]), parseFloat(coord[0])])
|
|
70
|
+
point.properties.geofence = geofence
|
|
71
|
+
point.properties.distance = coord_list[1].trim()
|
|
72
|
+
console.log(point)
|
|
73
|
+
return point
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (geofence.area.startsWith('LINESTRING')) {
|
|
77
|
+
const str = geofence.area.substring('LINESTRING ('.length, geofence.area.length - 1)
|
|
78
|
+
const coord_list = str.split(',')
|
|
79
|
+
const coordinates = coord_list.map(c => {
|
|
80
|
+
const coord = c.trim().split(' ')
|
|
81
|
+
return [parseFloat(coord[1]), parseFloat(coord[0])]
|
|
82
|
+
})
|
|
83
|
+
return {
|
|
84
|
+
type: 'Feature',
|
|
85
|
+
properties: {
|
|
86
|
+
geofence
|
|
87
|
+
},
|
|
88
|
+
geometry: {
|
|
89
|
+
type: 'LineString',
|
|
90
|
+
coordinates
|
|
91
|
+
}
|
|
89
92
|
}
|
|
90
|
-
|
|
93
|
+
}
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
function
|
|
94
|
-
|
|
96
|
+
function getDates (startDate, endDate) {
|
|
97
|
+
const dates = []
|
|
98
|
+
let currentDate = new Date(startDate.toISOString().split('T')[0] + ' 12:00:00.000 PM')
|
|
99
|
+
const addDays = function (days) {
|
|
100
|
+
const date = new Date(this.valueOf())
|
|
101
|
+
date.setDate(date.getDate() + days)
|
|
102
|
+
return date
|
|
103
|
+
}
|
|
104
|
+
while (currentDate <= endDate) {
|
|
105
|
+
dates.push(currentDate)
|
|
106
|
+
currentDate = addDays.call(currentDate, 1)
|
|
107
|
+
}
|
|
108
|
+
return dates
|
|
95
109
|
}
|
|
96
110
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return Buffer.from(response, 'binary').toString('base64')
|
|
111
|
+
function getImage (url) {
|
|
112
|
+
return axios.get(url, { responseType: 'arraybuffer' }).then(d => d.data)
|
|
100
113
|
}
|
|
101
114
|
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
async function getImageBase64 (url) {
|
|
116
|
+
const response = await getImage(url)
|
|
117
|
+
return Buffer.from(response, 'binary').toString('base64')
|
|
105
118
|
}
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const img = new Image()
|
|
111
|
-
img.src = url
|
|
112
|
-
return img
|
|
113
|
-
} catch (e) {
|
|
114
|
-
return await getImageBase64(url);
|
|
115
|
-
}
|
|
120
|
+
function getLogoUrl (hostname) {
|
|
121
|
+
const path = `/img/logos/${hostname}.png`
|
|
122
|
+
return window ? path : `https://${hostname}${path}`
|
|
116
123
|
}
|
|
117
124
|
|
|
118
|
-
function
|
|
125
|
+
async function getImgFromUrl (hostname) {
|
|
126
|
+
const url = getLogoUrl(hostname)
|
|
127
|
+
try {
|
|
128
|
+
const img = new Image()
|
|
129
|
+
img.src = url
|
|
130
|
+
return img
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return await getImageBase64(url)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
119
135
|
|
|
136
|
+
function isClientSide () { return (typeof window !== 'undefined') }
|
|
120
137
|
|
|
121
|
-
function convertToLocaleString(value, lang, timezone){
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
function convertToLocaleString (value, lang, timezone) {
|
|
139
|
+
if (isClientSide()) {
|
|
140
|
+
return new Date(value).toLocaleString()
|
|
141
|
+
} else {
|
|
142
|
+
return new Date(value).toLocaleString(lang, {
|
|
143
|
+
timeZone: timezone,
|
|
144
|
+
hour12: false
|
|
145
|
+
})
|
|
146
|
+
}
|
|
130
147
|
}
|
|
131
148
|
|
|
132
|
-
function convertToLocaleDateString(value, lang, timezone){
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
149
|
+
function convertToLocaleDateString (value, lang, timezone) {
|
|
150
|
+
if (isClientSide()) {
|
|
151
|
+
return new Date(value).toLocaleDateString()
|
|
152
|
+
} else {
|
|
153
|
+
return new Date(value).toLocaleDateString(lang, {
|
|
154
|
+
timeZone: timezone,
|
|
155
|
+
hour12: false
|
|
156
|
+
})
|
|
157
|
+
}
|
|
141
158
|
}
|
|
142
159
|
|
|
143
|
-
function convertToLocaleTimeString(value, lang, timezone){
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
160
|
+
function convertToLocaleTimeString (value, lang, timezone) {
|
|
161
|
+
if (isClientSide()) {
|
|
162
|
+
return new Date(value).toLocaleTimeString()
|
|
163
|
+
} else {
|
|
164
|
+
return new Date(value).toLocaleTimeString(lang, {
|
|
165
|
+
timeZone: timezone,
|
|
166
|
+
hour12: false
|
|
167
|
+
})
|
|
168
|
+
}
|
|
152
169
|
}
|
|
153
170
|
|
|
154
|
-
function getTimezoneOffset(timezone){
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
171
|
+
function getTimezoneOffset (timezone) {
|
|
172
|
+
if (isClientSide()) {
|
|
173
|
+
return new Date().getTimezoneOffset()
|
|
174
|
+
} else {
|
|
175
|
+
const utcDate = new Date(new Date().toLocaleString('en-US', { timeZone: 'UTC' }))
|
|
176
|
+
const tzDate = new Date(new Date().toLocaleString('en-US', { timeZone: timezone }))
|
|
177
|
+
return (utcDate.getTime() - tzDate.getTime()) / 6e4
|
|
178
|
+
}
|
|
162
179
|
}
|
|
163
180
|
|
|
164
|
-
function convertFromUTC(value, timezone) {
|
|
165
|
-
|
|
181
|
+
function convertFromUTC (value, timezone) {
|
|
182
|
+
const offset = getTimezoneOffset(timezone)
|
|
166
183
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
184
|
+
const valueDate = new Date(value)
|
|
185
|
+
valueDate.setTime(valueDate.getTime() - (offset * 60 * 1000))
|
|
186
|
+
return valueDate
|
|
170
187
|
}
|
|
171
188
|
|
|
172
|
-
function weekDaySelected(date, weekDays){
|
|
173
|
-
|
|
189
|
+
function weekDaySelected (date, weekDays) {
|
|
190
|
+
return (date.getDay() === 0 && weekDays.sunday) ||
|
|
174
191
|
(date.getDay() === 1 && weekDays.monday) ||
|
|
175
192
|
(date.getDay() === 2 && weekDays.tuesday) ||
|
|
176
|
-
(date.getDay() === 3 && weekDays.wednesday)
|
|
193
|
+
(date.getDay() === 3 && weekDays.wednesday) ||
|
|
177
194
|
(date.getDay() === 4 && weekDays.thursday) ||
|
|
178
195
|
(date.getDay() === 5 && weekDays.friday) ||
|
|
179
196
|
(date.getDay() === 6 && weekDays.saturday)
|
|
@@ -191,8 +208,8 @@ exports.getTimezoneOffset = getTimezoneOffset
|
|
|
191
208
|
exports.convertFromUTC = convertFromUTC
|
|
192
209
|
exports.weekDaySelected = weekDaySelected
|
|
193
210
|
exports.getLogo = (user) => {
|
|
194
|
-
|
|
195
|
-
|
|
211
|
+
const host = window ? window.location.hostname : getUserPartner(user).host
|
|
212
|
+
return getImage(getLogoUrl(host))
|
|
196
213
|
}
|
|
197
214
|
exports.convertToFeature = convertToFeature
|
|
198
215
|
exports.convertPositionToFeature = convertPositionToFeature
|
package/src/zone-report.js
CHANGED
|
@@ -8,7 +8,9 @@ const {getStyle} = require("./reportStyle")
|
|
|
8
8
|
const {getUserPartner} = require("fleetmap-partners")
|
|
9
9
|
const {devicesToProcess} = require("./util/device")
|
|
10
10
|
const traccarHelper = require("./util/traccar")
|
|
11
|
-
const
|
|
11
|
+
const pointToLineDistance = require("@turf/point-to-line-distance")
|
|
12
|
+
const pointOnFeature = require("@turf/point-on-feature")
|
|
13
|
+
const distance = require("@turf/distance")
|
|
12
14
|
|
|
13
15
|
const fileName = 'ZoneReport'
|
|
14
16
|
|
|
@@ -74,7 +76,7 @@ async function createZoneReport(from, to, userData, traccar) {
|
|
|
74
76
|
return reportData
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
async function processDevices(from, to, devices, drivers, geofences, data
|
|
79
|
+
async function processDevices(from, to, devices, drivers, geofences, data) {
|
|
78
80
|
const devicesResult = []
|
|
79
81
|
|
|
80
82
|
for (const d of devices) {
|
|
@@ -147,61 +149,54 @@ async function processDevices(from, to, devices, drivers, geofences, data, tracc
|
|
|
147
149
|
return devicesResult
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
|
|
151
152
|
function getInAndOutEvents(devices, route, userData) {
|
|
152
153
|
const events = []
|
|
153
154
|
devices.forEach(d => {
|
|
154
155
|
const deviceRoute = route.filter(p => p.deviceId === d.id)
|
|
155
156
|
|
|
156
157
|
const routePoints = deviceRoute.map(p => convertPositionToFeature(p))
|
|
157
|
-
const geofencesFeatures = userData.geofences.
|
|
158
|
+
const geofencesFeatures = userData.geofences.map(g => convertToFeature(g))
|
|
158
159
|
|
|
159
160
|
const entryMap = []
|
|
160
161
|
routePoints.forEach(p => {
|
|
161
162
|
geofencesFeatures.forEach(g => {
|
|
162
163
|
if(g.geometry.type === 'Polygon') {
|
|
163
|
-
if (
|
|
164
|
+
if (pointOnFeature(p, g)) {
|
|
164
165
|
if (!entryMap.includes(g)) {
|
|
165
|
-
events.push(
|
|
166
|
-
type: "geofenceEnter",
|
|
167
|
-
position: p.properties.position,
|
|
168
|
-
geofenceId: g.properties.geofence.id,
|
|
169
|
-
deviceId: d.id
|
|
170
|
-
})
|
|
166
|
+
events.push(createEvent('geofenceEnter',d.id,p,g))
|
|
171
167
|
entryMap.push(g)
|
|
172
168
|
}
|
|
173
169
|
} else {
|
|
174
170
|
if (entryMap.includes(g)) {
|
|
175
|
-
events.push(
|
|
176
|
-
type: "geofenceExit",
|
|
177
|
-
position: p.properties.position,
|
|
178
|
-
geofenceId: g.properties.geofence.id,
|
|
179
|
-
deviceId: d.id
|
|
180
|
-
})
|
|
171
|
+
events.push(createEvent('geofenceExit',d.id,p,g))
|
|
181
172
|
const index = entryMap.indexOf(g)
|
|
182
173
|
entryMap.splice(index, 1)
|
|
183
174
|
}
|
|
184
175
|
}
|
|
185
176
|
}
|
|
186
177
|
if(g.geometry.type === 'Point') {
|
|
187
|
-
if (
|
|
178
|
+
if (distance(p, g, 'meters') < g.properties.distance) {
|
|
179
|
+
if (!entryMap.includes(g)) {
|
|
180
|
+
events.push(createEvent('geofenceEnter',d.id,p,g))
|
|
181
|
+
entryMap.push(g)
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
if (entryMap.includes(g)) {
|
|
185
|
+
events.push(createEvent('geofenceExit',d.id,p,g))
|
|
186
|
+
const index = entryMap.indexOf(g)
|
|
187
|
+
entryMap.splice(index, 1)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if(g.geometry.type === 'LineString') {
|
|
192
|
+
if (pointToLineDistance(p, g,{units: 'meters'}) < (g.properties.geofence.attributes.polylineDistance || 25)) {
|
|
188
193
|
if (!entryMap.includes(g)) {
|
|
189
|
-
events.push(
|
|
190
|
-
type: "geofenceEnter",
|
|
191
|
-
position: p.properties.position,
|
|
192
|
-
geofenceId: g.properties.geofence.id,
|
|
193
|
-
deviceId: d.id
|
|
194
|
-
})
|
|
194
|
+
events.push(createEvent('geofenceEnter',d.id,p,g))
|
|
195
195
|
entryMap.push(g)
|
|
196
196
|
}
|
|
197
197
|
} else {
|
|
198
198
|
if (entryMap.includes(g)) {
|
|
199
|
-
events.push(
|
|
200
|
-
type: "geofenceExit",
|
|
201
|
-
position: p.properties.position,
|
|
202
|
-
geofenceId: g.properties.geofence.id,
|
|
203
|
-
deviceId: d.id
|
|
204
|
-
})
|
|
199
|
+
events.push(createEvent('geofenceExit',d.id,p,g))
|
|
205
200
|
const index = entryMap.indexOf(g)
|
|
206
201
|
entryMap.splice(index, 1)
|
|
207
202
|
}
|
|
@@ -214,6 +209,15 @@ function getInAndOutEvents(devices, route, userData) {
|
|
|
214
209
|
return events
|
|
215
210
|
}
|
|
216
211
|
|
|
212
|
+
function createEvent(type, deviceId, p, g) {
|
|
213
|
+
return {
|
|
214
|
+
type: type,
|
|
215
|
+
position: p.properties.position,
|
|
216
|
+
geofenceId: g.properties.geofence.id,
|
|
217
|
+
deviceId: deviceId
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
217
221
|
async function exportZoneReportToPDF(userData, reportData) {
|
|
218
222
|
console.log('Export to PDF')
|
|
219
223
|
|