Compare commits

..

No commits in common. "main" and "0.7" have entirely different histories.
main ... 0.7

5 changed files with 61 additions and 198 deletions

View file

@ -1,33 +1,23 @@
# 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.
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.
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.
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.
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
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.
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/).
Take a look at their [safety instructions](https://www.sac-cas.ch/en/training-and-safety/safety/).
## Contributors
- wizche
- [Michal Bryxí](mailto:michal.bryxi@gmail.com)
## Contact
For issue reporting contact me at [shu+gpx@vanwa.ch](mailto:shu+gpx@vanwa.ch)

View file

@ -17,28 +17,26 @@ let gpxTrack = null;
*/
function toWGS84(point) {
// convert LV95 into the civilian system
const y_aux = (point[0] - 2600000) / 1000000;
const x_aux = (point[1] - 1200000) / 1000000;
let y_aux = (point[0] - 2600000) / 1000000;
let 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);
0.0140 * 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) -
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 };
}
@ -50,7 +48,7 @@ function toWGS84(point) {
* @returns Track point xml node for gpx.
*/
function toTrackPoint(point) {
const wgs84Point = toWGS84(point);
let wgs84Point = toWGS84(point);
return `<trkpt lat="${wgs84Point.lat}" lon="${wgs84Point.lon}"/>`;
}
@ -60,7 +58,7 @@ function toTrackPoint(point) {
* @returns Way point xml node for gpx.
*/
function toWayPoint(point) {
const wgs84Point = toWGS84(point.geom.coordinates);
let wgs84Point = toWGS84(point.geom.coordinates);
return `
<wpt lat="${wgs84Point.lat}" lon="${wgs84Point.lon}">
<ele>${point.altitude}</ele>
@ -75,6 +73,7 @@ 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} - `
: "";
@ -89,25 +88,18 @@ function trackTitle(geoJson) {
* @returns Simple gpx string.
*/
function toGpx(geoJson) {
const trackSegments = geoJson.segments
.map((segment) => {
if (segment.geom == null) return "";
return `<trkseg>
let trackSegments = geoJson.segments.map((segment) => {
if (segment.geom == null) return "";
return `<trkseg>
${segment.geom.coordinates.map(toTrackPoint).join("")}
</trkseg>`;
})
.join("");
}).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("")
let endPoint = geoJson.end_point ? toWayPoint(geoJson.end_point) : "";
let waypoints = geoJson.waypoints
? geoJson.waypoints.map((wp) => {
return toWayPoint(wp.reference_poi);
}).join("")
: "";
const routeTitle = trackTitle(geoJson);
@ -119,7 +111,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">
${departurePoint}
${toWayPoint(geoJson.departure_point)}
${toWayPoint(geoJson.destination_poi)}
${waypoints}
${endPoint}
@ -131,7 +123,7 @@ function toGpx(geoJson) {
`;
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
let xmlDoc = parser.parseFromString(xmlString, "text/xml");
return new XMLSerializer().serializeToString(xmlDoc.documentElement);
}
@ -140,26 +132,26 @@ function toGpx(geoJson) {
* Intercept the download of GeoJSON data and save it for the background script.
*/
function listener(details) {
const filter = browser.webRequest.filterResponseData(details.requestId);
const decoder = new TextDecoder("utf-8");
const encoder = new TextEncoder();
let filter = browser.webRequest.filterResponseData(details.requestId);
let decoder = new TextDecoder("utf-8");
let encoder = new TextEncoder();
const data = [];
let data = [];
filter.ondata = (event) => {
data.push(event.data);
};
filter.onstop = async (_event) => {
const blob = new Blob(data, { type: "text/html" });
const buffer = await blob.arrayBuffer();
const str = decoder.decode(buffer);
filter.onstop = async (event) => {
let blob = new Blob(data, { type: "text/html" });
let buffer = await blob.arrayBuffer();
let str = decoder.decode(buffer);
updateActiveTab(browser.tabs);
filter.write(encoder.encode(str));
filter.close();
const geoJson = JSON.parse(str);
let geoJson = JSON.parse(str);
const routeTitle = trackTitle(geoJson);
gpxTrack = { title: routeTitle, data: toGpx(geoJson) };
};
@ -181,41 +173,6 @@ 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.
*/
@ -225,13 +182,12 @@ function handleClick(tab) {
return;
}
const blob = new Blob([gpxTrack.data], { type: "application/gpx+xml" });
const objectURL = URL.createObjectURL(blob);
let blob = new Blob([gpxTrack.data], { type: "application/gpx+xml" });
let objectURL = URL.createObjectURL(blob);
const filename = sanitizeFilename(gpxTrack.title);
const downloading = browser.downloads.download({
let downloading = browser.downloads.download({
url: objectURL,
filename: `${filename}.gpx`,
filename: `${gpxTrack.title}.gpx`,
saveAs: true,
conflictAction: "uniquify",
});
@ -246,14 +202,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]);
}
}
const gettingActiveTab = browser.tabs.query({
let gettingActiveTab = browser.tabs.query({
active: true,
currentWindow: true,
});
@ -269,11 +225,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({

61
flake.lock generated
View file

@ -1,61 +0,0 @@
{
"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
}

View file

@ -1,26 +0,0 @@
{
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
zip
];
};
}
);
}

View file

@ -1,15 +1,19 @@
{
"manifest_version": 2,
"name": "SAC Route Portal GPX Downloader",
"version": "0.9",
"version": "0.7",
"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",
@ -17,13 +21,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"
}
}
}