From 1fdd4e71b509a9a0cd93f35adbf6e4c1740fe260 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Fri, 16 Feb 2024 11:14:32 +0100 Subject: [PATCH 1/6] departure point can be null --- background.js | 72 ++++++++++++++++++++++++++++----------------------- manifest.json | 14 ++++------ 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/background.js b/background.js index 95ccd76..e03772c 100644 --- a/background.js +++ b/background.js @@ -17,8 +17,8 @@ let gpxTrack = null; */ function toWGS84(point) { // convert LV95 into the civilian system - let y_aux = (point[0] - 2600000) / 1000000; - let x_aux = (point[1] - 1200000) / 1000000; + const y_aux = (point[0] - 2600000) / 1000000; + const x_aux = (point[1] - 1200000) / 1000000; // calculate longitude and latitude in the unit 10000" let lat = 16.9023892 + @@ -26,7 +26,7 @@ function toWGS84(point) { 0.270978 * Math.pow(y_aux, 2) - 0.002528 * Math.pow(x_aux, 2) - 0.0447 * Math.pow(y_aux, 2) * x_aux - - 0.0140 * Math.pow(x_aux, 3); + 0.014 * Math.pow(x_aux, 3); let lon = 2.6779094 + 4.728982 * y_aux + @@ -35,8 +35,8 @@ function toWGS84(point) { 0.0436 * Math.pow(y_aux, 3); // unit 10000" to 1" and seconds to degrees (dec) - lat = lat * 100 / 36; - lon = lon * 100 / 36; + lat = (lat * 100) / 36; + lon = (lon * 100) / 36; return { lat: lat, lon: lon }; } @@ -48,7 +48,7 @@ function toWGS84(point) { * @returns Track point xml node for gpx. */ function toTrackPoint(point) { - let wgs84Point = toWGS84(point); + const wgs84Point = toWGS84(point); return ``; } @@ -58,7 +58,7 @@ function toTrackPoint(point) { * @returns Way point xml node for gpx. */ function toWayPoint(point) { - let wgs84Point = toWGS84(point.geom.coordinates); + const wgs84Point = toWGS84(point.geom.coordinates); return ` ${point.altitude} @@ -73,7 +73,6 @@ function toWayPoint(point) { * @returns Combined route number, id and route title. */ function trackTitle(geoJson) { - const route = geoJson.segments[0]; const book = geoJson.book_route_number ? `${geoJson.book_route_number} - ` : ""; @@ -88,18 +87,25 @@ function trackTitle(geoJson) { * @returns Simple gpx string. */ function toGpx(geoJson) { - let trackSegments = geoJson.segments.map((segment) => { - if (segment.geom == null) return ""; - return ` + const trackSegments = geoJson.segments + .map((segment) => { + if (segment.geom == null) return ""; + return ` ${segment.geom.coordinates.map(toTrackPoint).join("")} `; - }).join(""); + }) + .join(""); - let endPoint = geoJson.end_point ? toWayPoint(geoJson.end_point) : ""; - let waypoints = geoJson.waypoints - ? geoJson.waypoints.map((wp) => { - return toWayPoint(wp.reference_poi); - }).join("") + const departurePoint = geoJson.departure_point + ? toWayPoint(geoJson.departure_point) + : ""; + const endPoint = geoJson.end_point ? toWayPoint(geoJson.end_point) : ""; + const waypoints = geoJson.waypoints + ? geoJson.waypoints + .map((wp) => { + return toWayPoint(wp.reference_poi); + }) + .join("") : ""; const routeTitle = trackTitle(geoJson); @@ -111,7 +117,7 @@ function toGpx(geoJson) { xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd" version="1.0" creator="SAC-Tourenportal GPX Downloader"> - ${toWayPoint(geoJson.departure_point)} + ${departurePoint} ${toWayPoint(geoJson.destination_poi)} ${waypoints} ${endPoint} @@ -123,7 +129,7 @@ function toGpx(geoJson) { `; const parser = new DOMParser(); - let xmlDoc = parser.parseFromString(xmlString, "text/xml"); + const xmlDoc = parser.parseFromString(xmlString, "text/xml"); return new XMLSerializer().serializeToString(xmlDoc.documentElement); } @@ -132,26 +138,26 @@ function toGpx(geoJson) { * Intercept the download of GeoJSON data and save it for the background script. */ function listener(details) { - let filter = browser.webRequest.filterResponseData(details.requestId); - let decoder = new TextDecoder("utf-8"); - let encoder = new TextEncoder(); + const filter = browser.webRequest.filterResponseData(details.requestId); + const decoder = new TextDecoder("utf-8"); + const encoder = new TextEncoder(); - let data = []; + const data = []; filter.ondata = (event) => { data.push(event.data); }; - filter.onstop = async (event) => { - let blob = new Blob(data, { type: "text/html" }); - let buffer = await blob.arrayBuffer(); - let str = decoder.decode(buffer); + filter.onstop = async (_event) => { + const blob = new Blob(data, { type: "text/html" }); + const buffer = await blob.arrayBuffer(); + const str = decoder.decode(buffer); updateActiveTab(browser.tabs); filter.write(encoder.encode(str)); filter.close(); - let geoJson = JSON.parse(str); + const geoJson = JSON.parse(str); const routeTitle = trackTitle(geoJson); gpxTrack = { title: routeTitle, data: toGpx(geoJson) }; }; @@ -182,10 +188,10 @@ function handleClick(tab) { return; } - let blob = new Blob([gpxTrack.data], { type: "application/gpx+xml" }); - let objectURL = URL.createObjectURL(blob); + const blob = new Blob([gpxTrack.data], { type: "application/gpx+xml" }); + const objectURL = URL.createObjectURL(blob); - let downloading = browser.downloads.download({ + const downloading = browser.downloads.download({ url: objectURL, filename: `${gpxTrack.title}.gpx`, saveAs: true, @@ -202,14 +208,14 @@ function handleClick(tab) { /** * Update the download icon and text. */ -function updateActiveTab(tabs) { +function updateActiveTab(_tabs) { function updateTab(tabs) { if (tabs[0]) { updateIcon(tabs[0]); } } - let gettingActiveTab = browser.tabs.query({ + const gettingActiveTab = browser.tabs.query({ active: true, currentWindow: true, }); diff --git a/manifest.json b/manifest.json index b738a29..8c464a2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,19 +1,15 @@ { - "manifest_version": 2, "name": "SAC Route Portal GPX Downloader", - "version": "0.7", + "version": "0.8", "developer": { "name": "Sebastian Hugentobler", "url": "https://code.vanwa.ch/sebastian/sac-route-portal-gpx-fx" }, - "description": "Download gpx tracks from the sac route portal.", - "icons": { "48": "icons/map.png" }, - "permissions": [ "activeTab", "downloads", @@ -21,13 +17,13 @@ "webRequestBlocking", "https://www.sac-cas.ch/*" ], - "background": { - "scripts": ["background.js"] + "scripts": [ + "background.js" + ] }, - "browser_action": { "default_icon": "icons/map.png", "default_title": "To GPX" - } + } } From 59d0f01f874d612607779a3ded5c052791a410ea Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Mon, 24 Jun 2024 10:25:07 +0200 Subject: [PATCH 2/6] add simple nix flake for development --- flake.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 19 +++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f1b9252 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1719075281, + "narHash": "sha256-CyyxvOwFf12I91PBWz43iGT1kjsf5oi6ax7CrvaMyAo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a71e967ef3694799d0c418c98332f7ff4cc5f6af", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9751f05 --- /dev/null +++ b/flake.nix @@ -0,0 +1,19 @@ +{ + description = "little-hesinde project"; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + devShells.default = with pkgs; mkShell { buildInputs = [ deno ]; }; + } + ); +} From 6e91a15ca71f3c0303ee0b9608ab1d28ce9a8e8a Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Mon, 24 Jun 2024 11:07:53 +0200 Subject: [PATCH 3/6] Sanitize filenames of downloads. Some routes have characters like ":" in them which can be illegal on certain filesystems. Remove the known common ones and replace them with an underscore. --- README.md | 29 ++++++++++++++----------- background.js | 60 +++++++++++++++++++++++++++++++++++++++++---------- manifest.json | 2 +- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 5e58206..bc253fb 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,28 @@ # SAC Route Portal GPX Downloader -The [Swiss Alpine Club](https://www.sac-cas.ch/en/) has a great -[route portal](https://www.sac-cas.ch/en/huts-and-tours/sac-route-portal/) -for finding interesting hiking routes. -However you can not download the tracks as gpx files, as they argue that the exact -paths are subject to change and it could be dangerous to adhere too closely to them. +The [Swiss Alpine Club](https://www.sac-cas.ch/en/) has a great +[route portal](https://www.sac-cas.ch/en/huts-and-tours/sac-route-portal/) for +finding interesting hiking routes. -I do agree with that, but you can draw your own routes -and download those, mainly taking some time to draw after the route they are -already showing on the map. +However you can not download the tracks as gpx files, as they argue that the +exact paths are subject to change and it could be dangerous to adhere too +closely to them. -With this extension you can select any track and then download it as a gpx file +I do agree with that, but you can draw your own routes and download those, +mainly taking some time to draw after the route they are already showing on the +map. + +With this extension you can select any track and then download it as a gpx file (an active subscription is needed). ## Be Cautious! -Heed their warning and do not blindly follow the gps track! Use your brain and -plan your route beforehand to be aware of dangerous parts and alternatives. -Take a look at their [safety instructions](https://www.sac-cas.ch/en/training-and-safety/safety/). +Heed their warning and do not blindly follow the gps track! Use your brain and +plan your route beforehand to be aware of dangerous parts and alternatives. + +Take a look at their +[safety instructions](https://www.sac-cas.ch/en/training-and-safety/safety/). ## Contributors + - wizche diff --git a/background.js b/background.js index e03772c..e69a4e3 100644 --- a/background.js +++ b/background.js @@ -21,14 +21,16 @@ function toWGS84(point) { const x_aux = (point[1] - 1200000) / 1000000; // calculate longitude and latitude in the unit 10000" - let lat = 16.9023892 + + let lat = + 16.9023892 + 3.238272 * x_aux - 0.270978 * Math.pow(y_aux, 2) - 0.002528 * Math.pow(x_aux, 2) - 0.0447 * Math.pow(y_aux, 2) * x_aux - 0.014 * Math.pow(x_aux, 3); - let lon = 2.6779094 + + let lon = + 2.6779094 + 4.728982 * y_aux + 0.791484 * y_aux * x_aux + 0.1306 * y_aux * Math.pow(x_aux, 2) - @@ -102,10 +104,10 @@ function toGpx(geoJson) { const endPoint = geoJson.end_point ? toWayPoint(geoJson.end_point) : ""; const waypoints = geoJson.waypoints ? geoJson.waypoints - .map((wp) => { - return toWayPoint(wp.reference_poi); - }) - .join("") + .map((wp) => { + return toWayPoint(wp.reference_poi); + }) + .join("") : ""; const routeTitle = trackTitle(geoJson); @@ -179,6 +181,41 @@ function checkTrack(tab) { return tab.url.match("https://www.sac-cas.ch/.*") && gpxTrack; } +/** + * Remove characters that lead to problems in common filesystems. + * + * Windows (NTFS) seems to have the most restrictions, so it is mostly sourced from there. + * + * Reserved names on windows: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file + * + * Code comes from https://github.com/parshap/node-sanitize-filename/blob/master/index.js + */ +function sanitizeFilename(input) { + const illegal = /[\/\?<>\\:\*\|"]/g; + // deno-lint-ignore no-control-regex + const control = /[\x00-\x1f\x80-\x9f]/g; + const reserved = /^\.+$/; + const windowsReserved = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; + const windowsTrailing = /[\. ]+$/; + const replacement = "_"; + + const sanitized = input + .replace(illegal, replacement) + .replace(control, replacement) + .replace(reserved, replacement) + .replace(windowsReserved, replacement) + .replace(windowsTrailing, replacement); + + if (sanitized.length === 0) { + sanitized = "unnamed"; + } + + const maxLength = 250; + const uint8Array = new TextEncoder().encode(sanitized); + const truncated = uint8Array.slice(0, maxLength); + return new TextDecoder().decode(truncated); +} + /** * If a valid tack was selected, download it as a gpx file. */ @@ -191,9 +228,10 @@ function handleClick(tab) { const blob = new Blob([gpxTrack.data], { type: "application/gpx+xml" }); const objectURL = URL.createObjectURL(blob); + const filename = sanitizeFilename(gpxTrack.title); const downloading = browser.downloads.download({ url: objectURL, - filename: `${gpxTrack.title}.gpx`, + filename: `${filename}.gpx`, saveAs: true, conflictAction: "uniquify", }); @@ -231,11 +269,11 @@ function updateIcon(tab) { browser.browserAction.setIcon({ path: hasTrack ? { - 48: "icons/map.png", - } + 48: "icons/map.png", + } : { - 48: "icons/map-disabled.png", - }, + 48: "icons/map-disabled.png", + }, tabId: tab.id, }); browser.browserAction.setTitle({ diff --git a/manifest.json b/manifest.json index 8c464a2..4f2230d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "SAC Route Portal GPX Downloader", - "version": "0.8", + "version": "0.9", "developer": { "name": "Sebastian Hugentobler", "url": "https://code.vanwa.ch/sebastian/sac-route-portal-gpx-fx" From bc82090b9b868034ebb6fa788d7897568030ed75 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Mon, 24 Jun 2024 11:18:44 +0200 Subject: [PATCH 4/6] add zip to flake --- flake.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 9751f05..6c8eac7 100644 --- a/flake.nix +++ b/flake.nix @@ -13,7 +13,14 @@ pkgs = import nixpkgs { inherit system; }; in { - devShells.default = with pkgs; mkShell { buildInputs = [ deno ]; }; + devShells.default = + with pkgs; + mkShell { + buildInputs = [ + deno + zip + ]; + }; } ); } From 51d1f5c4ccc400802d1443f445a1df755aa7377f Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Tue, 25 Jun 2024 10:46:19 +0200 Subject: [PATCH 5/6] =?UTF-8?q?add=20michal=20bryx=C3=AD=20as=20contributo?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bc253fb..fc8159b 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,4 @@ Take a look at their ## Contributors - wizche +- [Michal Bryxí](mailto:michal.bryxi@gmail.com) From 777b14641fcecaf1df3fcc7c1fde77b576a2a3ff Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Tue, 25 Jun 2024 10:48:31 +0200 Subject: [PATCH 6/6] add contact address --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index fc8159b..00ac779 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,7 @@ Take a look at their - wizche - [Michal Bryxí](mailto:michal.bryxi@gmail.com) + +## Contact + +For issue reporting contact me at [shu+gpx@vanwa.ch](mailto:shu+gpx@vanwa.ch)