Greasy Fork is available in English.

GeoGPXer

GeoGPXer is a JavaScript library designed to convert GPX data into GeoJSON format efficiently. It supports the conversion of waypoints, tracks, and routes, with additional handling for GPX extensions.

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.org/scripts/523870/1534525/GeoGPXer.js

  1. // ==UserScript==
  2. // @name GeoGPXer
  3. // @namespace https://github.com/JS55CT
  4. // @description GeoGPXer is a JavaScript library designed to convert GPX data into GeoJSON format efficiently. It supports the conversion of waypoints, tracks, and routes, with additional handling for GPX extensions.
  5. // @version 2.0.0
  6. // @author JS55CT
  7. // @license MIT
  8. // @match *://this-library-is-not-supposed-to-run.com/*
  9. // ==/UserScript==
  10. /***********************************************************
  11. * ## Project Home < https://github.com/JS55CT/GeoGPXer >
  12. * MIT License
  13. * Copyright (c) 2022 hu de yi
  14. * Permission is hereby granted, free of charge, to any person obtaining a copy
  15. * of this software and associated documentation files (the "Software"), to deal
  16. * in the Software without restriction, including without limitation the rights
  17. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  18. * copies of the Software, and to permit persons to whom the Software is
  19. * furnished to do so, subject to the following conditions:
  20. *
  21. * The above copyright notice and this permission notice shall be included in all
  22. * copies or substantial portions of the Software.
  23. *
  24. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  27. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30. * SOFTWARE.
  31. *
  32. * Derived from logic of https://github.com/M-Reimer/gpx2geojson/tree/master (LGPL-3.0 license)
  33. **************************************************************/
  34. /**
  35. * @desc The GeoGPXer namespace.
  36. * @namespace
  37. * @global
  38. */
  39. var GeoGPXer = (function () {
  40. // Define the GeoGPXer constructor
  41. function GeoGPXer(obj) {
  42. if (obj instanceof GeoGPXer) return obj;
  43. if (!(this instanceof GeoGPXer)) return new GeoGPXer(obj);
  44. this._wrapped = obj;
  45. }
  46. /**
  47. * @desc Parses GPX text and returns an XML Document.
  48. * @param {String} gpxText - The GPX data as a string.
  49. * @return {Document} Parsed XML Document.
  50. */
  51. GeoGPXer.prototype.read = function (gpxText) {
  52. const parser = new DOMParser();
  53. const xmlDoc = parser.parseFromString(gpxText, "application/xml");
  54. // Check for parsing errors by looking for parser error tags
  55. const parseErrors = xmlDoc.getElementsByTagName("parsererror");
  56. if (parseErrors.length > 0) {
  57. // If there are parsing errors, log them and throw an error
  58. const errorMessages = Array.from(parseErrors)
  59. .map((errorElement, index) => {
  60. return `Parsing Error ${index + 1}: ${errorElement.textContent}`;
  61. })
  62. .join("\n");
  63. console.error(errorMessages);
  64. throw new Error("Failed to parse GPX. See console for details.");
  65. }
  66. // If parsing is successful, return the parsed XML document
  67. return xmlDoc;
  68. };
  69. /**
  70. * @desc Converts an XML Document to GeoJSON FeatureCollection.
  71. * @param {Document} document - Parsed XML document of GPX data.
  72. * @param {Boolean} includeElevation - Whether to include elevation data in coordinates.
  73. * @return {Object} GeoJSON FeatureCollection.
  74. */
  75. GeoGPXer.prototype.toGeoJSON = function (document, includeElevation = false) {
  76. const features = [];
  77. for (const n of document.firstChild.childNodes) {
  78. switch (n.tagName) {
  79. case "wpt":
  80. features.push(this.wptToPoint(n, includeElevation));
  81. break;
  82. case "trk":
  83. features.push(this.trkToMultiLineString(n, includeElevation));
  84. break;
  85. case "rte":
  86. features.push(this.rteToLineString(n, includeElevation));
  87. break;
  88. }
  89. }
  90. return {
  91. type: "FeatureCollection",
  92. features: features,
  93. };
  94. };
  95. /**
  96. * @desc Extracts coordinates from a node.
  97. * @param {Node} node - GPX node containing coordinates.
  98. * @param {Boolean} includeElevation - Whether to include elevation data.
  99. * @return {Array} Array of coordinates [longitude, latitude, elevation].
  100. */
  101. GeoGPXer.prototype.coordFromNode = function (node, includeElevation = false) {
  102. const coords = [parseFloat(node.getAttribute("lon")), parseFloat(node.getAttribute("lat"))];
  103. if (includeElevation) {
  104. const eleNode = node.getElementsByTagName("ele")[0];
  105. const elevation = eleNode ? parseFloat(eleNode.textContent) : 0;
  106. coords.push(elevation);
  107. }
  108. return coords;
  109. };
  110. /**
  111. * @desc Creates a GeoJSON feature.
  112. * @param {String} type - Type of geometry (Point, LineString, etc.).
  113. * @param {Array} coords - Coordinates for the geometry.
  114. * @param {Object} props - Properties of the feature.
  115. * @return {Object} GeoJSON feature.
  116. */
  117. GeoGPXer.prototype.makeFeature = function (type, coords, props) {
  118. return {
  119. type: "Feature",
  120. geometry: {
  121. type: type,
  122. coordinates: coords,
  123. },
  124. properties: props,
  125. };
  126. };
  127. /**
  128. * @desc Converts a waypoint node to a GeoJSON Point feature.
  129. * @param {Node} node - GPX waypoint node.
  130. * @param {Boolean} includeElevation - Whether to include elevation data.
  131. * @return {Object} GeoJSON Point feature.
  132. */
  133. GeoGPXer.prototype.wptToPoint = function (node, includeElevation = false) {
  134. const coord = this.coordFromNode(node, includeElevation);
  135. const props = this.extractProperties(node);
  136. return this.makeFeature("Point", coord, props);
  137. };
  138. /**
  139. * @desc Converts a track node to a GeoJSON MultiLineString feature.
  140. * @param {Node} node - GPX track node.
  141. * @param {Boolean} includeElevation - Whether to include elevation data.
  142. * @return {Object} GeoJSON MultiLineString feature.
  143. */
  144. GeoGPXer.prototype.trkToMultiLineString = function (node, includeElevation = false) {
  145. const coordslst = [];
  146. const props = this.extractProperties(node);
  147. for (const n of node.childNodes) {
  148. if (n.tagName === "trkseg") {
  149. const coords = [];
  150. coordslst.push(coords);
  151. for (const trkpt of n.getElementsByTagName("trkpt")) {
  152. coords.push(this.coordFromNode(trkpt, includeElevation));
  153. }
  154. }
  155. }
  156. return this.makeFeature("MultiLineString", coordslst, props);
  157. };
  158. /**
  159. * @desc Converts a route node to a GeoJSON LineString feature.
  160. * @param {Node} node - GPX route node.
  161. * @param {Boolean} includeElevation - Whether to include elevation data.
  162. * @return {Object} GeoJSON LineString feature.
  163. */
  164. GeoGPXer.prototype.rteToLineString = function (node, includeElevation = false) {
  165. const coords = [];
  166. const props = this.extractProperties(node);
  167. for (const n of node.childNodes) {
  168. if (n.tagName === "rtept") {
  169. coords.push(this.coordFromNode(n, includeElevation));
  170. }
  171. }
  172. return this.makeFeature("LineString", coords, props);
  173. };
  174. /**
  175. * @desc Extracts properties from a GPX node.
  176. * @param {Node} node - GPX node.
  177. * @return {Object} Properties extracted from the node.
  178. */
  179. GeoGPXer.prototype.extractProperties = function (node) {
  180. const props = {};
  181. for (const n of node.childNodes) {
  182. if (n.nodeType === Node.ELEMENT_NODE && n.tagName !== "extensions") {
  183. props[n.tagName] = n.textContent;
  184. }
  185. }
  186. const extensions = node.getElementsByTagName("extensions");
  187. if (extensions.length > 0) {
  188. for (const ext of extensions[0].childNodes) {
  189. if (ext.nodeType === Node.ELEMENT_NODE) {
  190. props[`ex_${ext.tagName}`] = ext.textContent;
  191. }
  192. }
  193. }
  194. return props;
  195. };
  196. return GeoGPXer; // Return the GeoGPXer constructor
  197. })();