OpenStreetMap GPX Overlay

Overlay GPX files on the OpenStreetMap website

  1. // ==UserScript==
  2. // @name OpenStreetMap GPX Overlay
  3. // @namespace Violentmonkey Scripts
  4. // @match *://www.openstreetmap.org/*
  5. // @version 1.1
  6. // @author CyrilSLi
  7. // @description Overlay GPX files on the OpenStreetMap website
  8. // @license MIT
  9. // @require https://update.greasyfork.org/scripts/533461/1574689/Get%20OpenStreetMap%20Leaflet%20object.js
  10. // @require https://cdn.jsdelivr.net/npm/@jitbit/htmlsanitizer@2/HtmlSanitizer.min.js
  11. // @require https://cdn.jsdelivr.net/npm/gpxparser@3/dist/GPXParser.min.js
  12. // ==/UserScript==
  13.  
  14. // https://github.com/gpxstudio/gpx.studio/blob/306ed2ae0e2ab46f32eadcc9d38c441c219cf428/website/src/lib/components/gpx-layer/GPXLayer.ts#L27
  15. const colors = [
  16. '#ff0000',
  17. '#0000ff',
  18. '#46e646',
  19. '#00ccff',
  20. '#ff9900',
  21. '#ff00ff',
  22. '#ffff32',
  23. '#288228',
  24. '#9933ff',
  25. '#50f0be',
  26. '#8c645a',
  27. ];
  28. var colorIndex = 0;
  29.  
  30. function addFiles(ev) {
  31. const input = document.createElement("input");
  32. input.setAttribute("type", "file");
  33. input.setAttribute("accept", ".gpx");
  34. input.setAttribute("multiple", "");
  35. input.addEventListener("change", (ev) => {
  36. [...ev.target.files].forEach((file) => {
  37. if (!file.name.toLowerCase().endsWith(".gpx")) {
  38. return;
  39. }
  40. const reader = new FileReader();
  41. reader.addEventListener("load", (ev) => {
  42. const gpx = new gpxParser();
  43. gpx.parse(ev.target.result);
  44. gpx.tracks.concat(gpx.routes).forEach((line) => {
  45. L.polyline(line.points.map((point) => [point.lat, point.lon]), {
  46. weight: 5,
  47. color: colors[colorIndex++ % colors.length]
  48. }).addTo(unsafeWindow.userscriptMap);
  49. });
  50. gpx.waypoints.forEach((wpt) => {
  51. txt = document.createElement("textarea");
  52. txt.innerHTML = HtmlSanitizer.SanitizeHtml(wpt.name ?? "") +
  53. ((wpt.name != null && wpt.desc != null) ? "<br>" : "") +
  54. HtmlSanitizer.SanitizeHtml(wpt.desc ?? "");
  55. L.marker([wpt.lat, wpt.lon], {title: wpt.name ?? ""}).addTo(unsafeWindow.userscriptMap).bindPopup(txt.value);
  56. });
  57. });
  58. reader.readAsText(file, "UTF-8");
  59. });
  60. });
  61. input.click();
  62. }
  63.  
  64. window.addEventListener("load", () => {
  65. if (!unsafeWindow.userscriptMap) {
  66. return;
  67. }
  68. keyButton = document.getElementsByClassName("control-key")[0];
  69. gpxButton = keyButton.cloneNode(true);
  70. keyButton.parentNode.insertBefore(gpxButton, keyButton.nextSibling);
  71. keyButton.children[0].click();
  72. keyButton.children[0].click();
  73. gpxButton.classList.remove("control-key");
  74. gpxButton.children[0].setAttribute("data-bs-original-title", "Overlay GPX");
  75. gpxButton.children[0].classList.remove("disabled");
  76. gpxButton.children[0].style.padding = "10px";
  77. gpxButton.children[0].innerHTML = `<svg width="20px" height="20px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none"><path fill="#ffffff" fill-rule="evenodd" d="M11.291 21.706 12 21l-.709.706zM12 21l.708.706a1 1 0 0 1-1.417 0l-.006-.007-.017-.017-.062-.063a47.708 47.708 0 0 1-1.04-1.106 49.562 49.562 0 0 1-2.456-2.908c-.892-1.15-1.804-2.45-2.497-3.734C4.535 12.612 4 11.248 4 10c0-4.539 3.592-8 8-8 4.408 0 8 3.461 8 8 0 1.248-.535 2.612-1.213 3.87-.693 1.286-1.604 2.585-2.497 3.735a49.583 49.583 0 0 1-3.496 4.014l-.062.063-.017.017-.006.006L12 21zm0-8a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" clip-rule="evenodd"/></svg>`;
  78. gpxButton.children[0].addEventListener("click", addFiles);
  79. });