/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ let gpxTrack = null; /** * Convert the Swiss projection coordinates to WGS84 (lat/lon). * * Calculations from "Approximate formulas for the transformation between Swiss * projection coordinates and WGS84" by the Swiss Federal Office of Topography, * swisstopo * (https://www.swisstopo.admin.ch/en/knowledge-facts/surveying-geodesy/reference-systems/map-projections.html). * * @param {*} point Array of Swiss projection coordinates, position 0 is E and 1 is N. * @returns Calculated lat and lon. */ function toWGS84(point) { // convert LV95 into the civilian system 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 + 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.0140 * Math.pow(x_aux, 3); 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; return {lat: lat, lon: lon}; } /** * Get the gpx trackpoint representation of a Swiss projection coordinate point. * * @param {*} point Array of Swiss projection coordinates, position 0 is E and 1 is N. * @returns Track point xml node for gpx. */ function toTrackPoint(point) { let wgs84Point = toWGS84(point); return ``; } /** * Get the gpx waypoint representation of a route portal point. * * @returns Way point xml node for gpx. */ function toWayPoint(point) { let wgs84Point = toWGS84(point.geom.coordinates); return ` ${point.altitude} ${point.display_name} `; } /** * Create track title. * * @param {*} geoJson * @returns Combined route number, id and route title. */ function trackTitle(geoJson) { const route = geoJson.segments[0]; return `${geoJson.book_route_number}-${route.route_id} ${route.title}`; } /** * Create a gpx representation from the sac GeoJSON data. * * @param {*} geoJson * @returns Simple gpx string. */ function toGpx(geoJson) { const route = geoJson.segments[0].geom; const routeTitle = trackTitle(geoJson); const xmlString = ` ${toWayPoint(geoJson.departure_point)} ${toWayPoint(geoJson.destination_poi)} Track ${routeTitle} ${route.coordinates.map(toTrackPoint).join("")} `; const parser = new DOMParser(); let xmlDoc = parser.parseFromString(xmlString, "text/xml"); return new XMLSerializer().serializeToString(xmlDoc.documentElement); } /** * 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(); let 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); updateActiveTab(browser.tabs); filter.write(encoder.encode(str)); filter.close(); let geoJson = JSON.parse(str); const routeTitle = trackTitle(geoJson); gpxTrack = {title: routeTitle, data: toGpx(geoJson)}; }; return {}; } /** * Check if a url should be intercepted. * * @param {*} tab * @returns True if the active tab is an sac website, false otherwise. */ function checkTrack(tab) { if (!tab.url) { return false; } return tab.url.match("https://www.sac-cas.ch/.*") && gpxTrack; } /** * If a valid tack was selected, download it as a gpx file. */ function handleClick(tab) { const hasTrack = checkTrack(tab); if (!hasTrack) { return; } let blob = new Blob([gpxTrack.data], {type: "application/gpx+xml"}); let objectURL = URL.createObjectURL(blob); let downloading = browser.downloads.download({ url : objectURL, filename : `track-${gpxTrack.title}.gpx`, saveAs: true, conflictAction : 'uniquify' }); downloading.then( (id) => console.log(`Started downloading: ${id}`), (error) => console.log(`Download failed: ${error}`)); gpxTrack = null; } /** * Update the download icon and text. */ function updateActiveTab(tabs) { function updateTab(tabs) { if (tabs[0]) { updateIcon(tabs[0]); } } let gettingActiveTab = browser.tabs.query({active: true, currentWindow: true}); gettingActiveTab.then(updateTab); } /** * Update the download icon. */ function updateIcon(tab) { const hasTrack = checkTrack(tab); browser.browserAction.setIcon({ path: hasTrack ? { 48: "icons/map.png", } : { 48: "icons/map-disabled.png", }, tabId: tab.id }); browser.browserAction.setTitle({ title: hasTrack ? `Download track ${gpxTrack.title}` : 'No track selected', tabId: tab.id }); } browser.webRequest.onBeforeRequest.addListener( listener, {urls: ["https://www.sac-cas.ch/*[routeId]*"]}, ["blocking"] ); browser.browserAction.onClicked.addListener(handleClick); browser.tabs.onUpdated.addListener(updateActiveTab); browser.tabs.onActivated.addListener(updateActiveTab); browser.windows.onFocusChanged.addListener(updateActiveTab); updateActiveTab();