diff --git a/.prettierignore b/.prettierignore
index 6644623..cc6dedb 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -4,8 +4,14 @@ dist/
yarn.lock
.idea
.gitignore
+.eslintignore
.prettierignore
.tx/
layers/
locales/*.json
resources/boundaries/
+*.sh
+profiles2/
+Dockerfile
+tests/**/*.json
+tests/**/*.gpx
diff --git a/gulpfile.js b/gulpfile.js
index 6c50707..11f844b 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -71,6 +71,7 @@ var paths = {
'js/LayersConfig.js',
'js/router/BRouter.js',
'js/util/*.js',
+ 'js/format/*.js',
'js/plugin/*.js',
'js/control/*.js',
'js/index.js',
diff --git a/js/format/Gpx.js b/js/format/Gpx.js
new file mode 100644
index 0000000..e70a3f2
--- /dev/null
+++ b/js/format/Gpx.js
@@ -0,0 +1,76 @@
+BR.Gpx = {
+ format: function (geoJson) {
+ const gpx = togpx(geoJson, {
+ featureDescription: function () {},
+ });
+ const statsComment = BR.Gpx._statsComment(geoJson);
+ const xml = '' + statsComment + gpx;
+ return BR.Gpx.pretty(xml, 1);
+ },
+
+ //
+ _statsComment: function (geoJson) {
+ const props = geoJson.features?.[0].properties;
+ if (!props) return '';
+
+ let comment = '';
+ return comment;
+ },
+
+ // modified version of
+ // https://gist.github.com/sente/1083506#gistcomment-2254622
+ // MIT License, Copyright (c) 2016 Stuart Powers, ES6 version by Jonathan Gruber
+ pretty: function (xml, indentSize = 2) {
+ const PADDING = ' '.repeat(indentSize);
+ const newline = '\n';
+ // break into lines, keep trkpt with subelement ele in single line
+ const reg = /(>)(<)(?!ele|\/trkpt)(\/?)/g;
+ let pad = 0;
+
+ xml = xml.replace('', '');
+ xml = xml.replace(reg, `$1${newline}$2$3`);
+
+ let lines = xml.split(newline);
+ lines = lines.map((node, index) => {
+ let indent = 0;
+ if (node.match(/.+<\/\w[^>]*>$/)) {
+ indent = 0;
+ } else if (node.match(/^<\/\w/) && pad > 0) {
+ pad -= 1;
+ } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
+ indent = 1;
+ } else {
+ indent = 0;
+ }
+
+ pad += indent;
+
+ return PADDING.repeat(pad - indent) + node;
+ });
+
+ for (const [i, line] of lines.entries()) {
+ // break gpx attributes into separate lines
+ if (line.includes('\n [^<]*<\/name>/, `\n ${name}`);
+ gpx = gpx
+ .split(newline)
+ .map((line) => line.replace(/lon="([^"]*)" lat="([^"]*)"/, 'lat="$2" lon="$1"'))
+ .join(newline);
+ gpx = gpx.replace(/0">/g, '">');
+ gpx = gpx.replace('\n', '');
+
+ return gpx;
+}
+
+function read(fileName) {
+ return adoptGpx(fs.readFileSync(path + fileName, 'utf8'));
+}
+
+test('simple track', () => {
+ const brouterGpx = read('track.gpx');
+ const gpx = BR.Gpx.format(geoJson);
+ expect(gpx).toEqual(brouterGpx);
+});
diff --git a/tests/format/data/track.gpx b/tests/format/data/track.gpx
new file mode 100644
index 0000000..8091802
--- /dev/null
+++ b/tests/format/data/track.gpx
@@ -0,0 +1,20 @@
+
+
+
+
+ Track
+
+ 101.5
+ 101.5
+ 101.5
+ 101.5
+ 101.75
+ 103.5
+ 99.75
+
+
+
diff --git a/tests/format/data/track.json b/tests/format/data/track.json
new file mode 100644
index 0000000..6c8902f
--- /dev/null
+++ b/tests/format/data/track.json
@@ -0,0 +1,40 @@
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "creator": "BRouter-1.1",
+ "name": "Track",
+ "track-length": "319",
+ "filtered ascend": "2",
+ "plain-ascend": "-1",
+ "total-time": "44",
+ "total-energy": "4412",
+ "cost": "533",
+ "voicehints": [
+ [1,5,0],
+ [5,2,0]
+ ],
+ "messages": [
+ ["Longitude", "Latitude", "Elevation", "Distance", "CostPerKm", "ElevCost", "TurnCost", "NodeCost", "InitialCost", "WayTags", "NodeTags"],
+ ["8468340", "49488794", "101", "89", "1000", "0", "0", "0", "0", "highway=residential surface=asphalt cycleway=lane oneway=yes lcn=yes smoothness=good route_bicycle_icn=yes route_bicycle_ncn=yes route_bicycle_rcn=yes", ""],
+ ["8470671", "49488909", "99", "230", "1150", "0", "180", "0", "0", "highway=residential surface=asphalt oneway=yes smoothness=good", ""]
+ ]
+ },
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [8.467714, 49.488115, 101.5],
+ [8.468340, 49.488794, 101.5],
+ [8.468586, 49.488698, 101.5],
+ [8.468743, 49.488636, 101.5],
+ [8.469161, 49.488473, 101.75],
+ [8.469971, 49.488151, 103.5],
+ [8.470610, 49.488842, 99.75]
+ ]
+ }
+ }
+ ]
+}
+