@furkot/garmin-data 1.0.2 → 2.0.0
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/index.js +1 -1
- package/lib/index.js +233 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = require('./lib
|
|
1
|
+
module.exports = require('./lib');
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
const {
|
|
2
|
+
toGarmin,
|
|
3
|
+
toFurkot,
|
|
4
|
+
colors
|
|
5
|
+
} = require('./garmin');
|
|
6
|
+
|
|
7
|
+
const schema = [
|
|
8
|
+
[
|
|
9
|
+
'xmlns:gpxx', 'http://www.garmin.com/xmlschemas/GpxExtensions/v3',
|
|
10
|
+
'http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd'
|
|
11
|
+
],
|
|
12
|
+
[
|
|
13
|
+
'xmlns:trp', 'http://www.garmin.com/xmlschemas/TripExtensions/v1',
|
|
14
|
+
'http://www.garmin.com/xmlschemas/TripExtensionsv1.xsd'
|
|
15
|
+
],
|
|
16
|
+
[
|
|
17
|
+
'xmlns:tmd', 'http://www.garmin.com/xmlschemas/TripMetaDataExtensions/v1',
|
|
18
|
+
'http://www.garmin.com/xmlschemas/TripMetaDataExtensionsv1.xsd'
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
'xmlns:vptm', 'http://www.garmin.com/xmlschemas/ViaPointTransportationModeExtensions/v1',
|
|
22
|
+
'http://www.garmin.com/xmlschemas/ViaPointTransportationModeExtensionsv1.xsd'
|
|
23
|
+
]
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const modeMap = [
|
|
27
|
+
'Motorcycling',
|
|
28
|
+
'Automotive',
|
|
29
|
+
'Bicycling',
|
|
30
|
+
'Walking',
|
|
31
|
+
'Direct',
|
|
32
|
+
'Direct',
|
|
33
|
+
'RV',
|
|
34
|
+
'Direct'
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function getMode(rMode, mMode) {
|
|
38
|
+
if (rMode !== undefined) {
|
|
39
|
+
return modeMap[rMode + 1] || 'Unknown';
|
|
40
|
+
}
|
|
41
|
+
return modeMap[(mMode || 0) + 1] || 'Unknown';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const millisInMinute = 60 * 1000;
|
|
45
|
+
const millisInHour = 60 * millisInMinute;
|
|
46
|
+
|
|
47
|
+
function formatDuration(step) {
|
|
48
|
+
const duration = ['P'];
|
|
49
|
+
if (step.nights) {
|
|
50
|
+
duration.push(step.nights, 'D');
|
|
51
|
+
}
|
|
52
|
+
if (step.visit_duration) {
|
|
53
|
+
duration.push('T');
|
|
54
|
+
let dur = Math.floor(step.visit_duration / millisInHour);
|
|
55
|
+
if (dur) {
|
|
56
|
+
duration.push(dur, 'H');
|
|
57
|
+
}
|
|
58
|
+
dur = Math.floor((step.visit_duration % millisInHour) / millisInMinute);
|
|
59
|
+
if (dur) {
|
|
60
|
+
duration.push(dur, 'M');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (duration.length > 2) {
|
|
64
|
+
return duration.join('');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getWptExt({ start, end, el, elIfText, each }) {
|
|
69
|
+
return function* (st) {
|
|
70
|
+
yield* start('gpxx:WaypointExtension');
|
|
71
|
+
if (st.tags && st.tags.length) {
|
|
72
|
+
yield* start('gpxx:Categories');
|
|
73
|
+
yield* each(st.tags, function* (cat) {
|
|
74
|
+
yield* el('gpxx:Category', null, cat);
|
|
75
|
+
});
|
|
76
|
+
yield* end();
|
|
77
|
+
}
|
|
78
|
+
if (st.address) {
|
|
79
|
+
yield* start('gpxx:Address');
|
|
80
|
+
yield* elIfText('gpxx:StreetAddress', st.stopStreetAddress);
|
|
81
|
+
if (st.locality) {
|
|
82
|
+
yield* elIfText('gpxx:City', st.locality.town);
|
|
83
|
+
yield* elIfText('gpxx:State', st.locality.province);
|
|
84
|
+
yield* elIfText('gpxx:Country', st.locality.country_long);
|
|
85
|
+
}
|
|
86
|
+
yield* end();
|
|
87
|
+
}
|
|
88
|
+
yield* elIfText('gpxx:PhoneNumber', st.phone);
|
|
89
|
+
yield* end();
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getRteExt({ ctx, start, end, el, elIfText }) {
|
|
94
|
+
return function* (rt) {
|
|
95
|
+
yield* start('gpxx:RouteExtension');
|
|
96
|
+
yield* elIfText('gpxx:IsAutoNamed', 'false');
|
|
97
|
+
yield* elIfText('gpxx:DisplayColor', colors[rt.color] || 'Blue');
|
|
98
|
+
yield* end();
|
|
99
|
+
|
|
100
|
+
const { mode, name } = ctx?.metadata || {};
|
|
101
|
+
|
|
102
|
+
if (rt.mode !== undefined || mode !== undefined) {
|
|
103
|
+
yield* start('trp:Trip');
|
|
104
|
+
//- The <trp:TransportationMode> extension tells the devices Trip Planner algorithm what calculation type to use:
|
|
105
|
+
//- Automotive, Motorcycling, Walking, Bicycling, Direct.
|
|
106
|
+
yield* elIfText('trp:TransportationMode', getMode(rt.mode, mode));
|
|
107
|
+
yield* end();
|
|
108
|
+
|
|
109
|
+
if (name || rt.routeTimestamp || rt.day) {
|
|
110
|
+
yield* start('tmd:TripMetaData');
|
|
111
|
+
yield* el('tmd:TripName', null, name || '');
|
|
112
|
+
if (rt.routeTimestamp) {
|
|
113
|
+
yield* elIfText('tmd:Date', rt.routeTimestamp);
|
|
114
|
+
}
|
|
115
|
+
yield* elIfText('tmd:DayNumber', rt.day);
|
|
116
|
+
yield* end();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getRtePtExt({ ctx, precision, start, end, each, el, elIfText }) {
|
|
123
|
+
|
|
124
|
+
function* $gpxxRpt(p, subclass) {
|
|
125
|
+
const attribs = {
|
|
126
|
+
lat: p[1].toFixed(precision),
|
|
127
|
+
lon: p[0].toFixed(precision)
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
if (subclass) {
|
|
131
|
+
yield* start('gpxx:rpt', attribs);
|
|
132
|
+
yield* el('gpxx:Subclass', null, subclass);
|
|
133
|
+
yield* end();
|
|
134
|
+
} else {
|
|
135
|
+
yield* el('gpxx:rpt', attribs);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return function* (rt, st, index, next) {
|
|
140
|
+
let shapingPoint;
|
|
141
|
+
|
|
142
|
+
if (st.stopIsActualStop || index % 124 === 0 || !next) {
|
|
143
|
+
//- The <trp:ViaPoint> is equivalent to a Furkot Stop.
|
|
144
|
+
yield* start('trp:ViaPoint');
|
|
145
|
+
if (st.stopDeparture && next) {
|
|
146
|
+
yield* elIfText('trp:DepartureTime', st.stopDeparture);
|
|
147
|
+
}
|
|
148
|
+
if (index && next) {
|
|
149
|
+
yield* elIfText('trp:StopDuration', formatDuration(st));
|
|
150
|
+
}
|
|
151
|
+
if (st.stopTimestamp && index > 0) {
|
|
152
|
+
yield* elIfText('trp:ArrivalTime', st.stopTimestamp);
|
|
153
|
+
}
|
|
154
|
+
yield* elIfText('trp:CalculationMode', 'FasterTime');
|
|
155
|
+
yield* elIfText('trp:ElevationMode', 'Standard');
|
|
156
|
+
yield* end();
|
|
157
|
+
} else {
|
|
158
|
+
shapingPoint = true;
|
|
159
|
+
//- The <trp:ShapingPoint> is equivalent to Furkot Pass-through Points.
|
|
160
|
+
//- These points are displayed in the Route point list on the GPS but are not announced during navigation.
|
|
161
|
+
//- There can be up to 124 ShapingPoints between each ViaPoint.
|
|
162
|
+
yield* el('trp:ShapingPoint');
|
|
163
|
+
}
|
|
164
|
+
if (next && next.mode !== undefined) {
|
|
165
|
+
|
|
166
|
+
//- The <vptm:ViaPointTransportationMode> extension tells the devices Trip Planner algorithm
|
|
167
|
+
//- what calculation type to use between this and next point:
|
|
168
|
+
//- Automotive, Motorcycling, Walking, Bicycling, Direct.
|
|
169
|
+
yield* start('vptm:ViaPointTransportationMode');
|
|
170
|
+
yield* elIfText('vptm:TransportationMode', getMode(next.mode));
|
|
171
|
+
yield* end();
|
|
172
|
+
}
|
|
173
|
+
if (ctx.RoutePointExtension) {
|
|
174
|
+
yield* start('gpxx:RoutePointExtension');
|
|
175
|
+
|
|
176
|
+
//- The <gpxx:Subclass> is a special Garmin code for older Garmin devices that do not use
|
|
177
|
+
//- the Trip Planner algorithm. This specific code says to use the above RoutePoint as a waypoint.
|
|
178
|
+
//- Older Garmin devices don't use the Trip Planner algorithm.
|
|
179
|
+
yield* el('gpxx:Subclass', null, '000000000000FFFFFFFFFFFFFFFFFFFFFFFF');
|
|
180
|
+
const expanded = next && next.track;
|
|
181
|
+
|
|
182
|
+
if (expanded) {
|
|
183
|
+
yield* each(expanded, function* (p) {
|
|
184
|
+
let subclass;
|
|
185
|
+
|
|
186
|
+
//- All of the <gpxx:rpt> are equivalent to Furkot Track Points generated between Stops.
|
|
187
|
+
//- These points are not displayed on the GPS or announced during navigation.
|
|
188
|
+
if (next.custom || next.mode === 3) {
|
|
189
|
+
//- This Subclass code denotes a route point that is not on a mapped road (off-road).
|
|
190
|
+
//- There are usually many of these in succession to define the route path
|
|
191
|
+
//- and each off-road point needs to have this Subclass.
|
|
192
|
+
subclass = 'FFFF00000000FFFFFFFF2117000000000000';
|
|
193
|
+
} else if (shapingPoint) {
|
|
194
|
+
shapingPoint = false;
|
|
195
|
+
//- A Subclass other than 000000000000FFFFFFFFFFFFFFFFFFFFFFFF must be added after a ShapingPoint.
|
|
196
|
+
//- Any other ‘valid’ Subclass code works.
|
|
197
|
+
subclass = '0300F2F7C403DD6E00002116000025490404';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
yield* $gpxxRpt(p, subclass);
|
|
201
|
+
});
|
|
202
|
+
yield* $gpxxRpt(expanded[expanded.length - 1], '000000000000FFFFFFFFFFFFFFFFFFFFFFFF');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
yield* end();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function getTrkExt({ start, end, elIfText }) {
|
|
211
|
+
return function* (tr) {
|
|
212
|
+
yield* start('gpxx:TrackExtension');
|
|
213
|
+
yield* elIfText('gpxx:DisplayColor', colors[tr.color]);
|
|
214
|
+
yield* end();
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
module.exports = {
|
|
219
|
+
getWptCmt: st => st.notes,
|
|
220
|
+
getWptSym: st => toGarmin[st.sym],
|
|
221
|
+
hasWptExt: st => st.address || st.phone || st.tags?.length,
|
|
222
|
+
getWptExt,
|
|
223
|
+
hasRteExt: () => true,
|
|
224
|
+
getRteExt,
|
|
225
|
+
hasRtePtExt: () => true,
|
|
226
|
+
getRtePtExt,
|
|
227
|
+
hasTrkExt: tr => tr.color,
|
|
228
|
+
getTrkExt,
|
|
229
|
+
schema,
|
|
230
|
+
toGarmin,
|
|
231
|
+
toFurkot,
|
|
232
|
+
colors
|
|
233
|
+
};
|