{"id":13606958,"url":"https://github.com/mpetazzoni/leaflet-gpx","last_synced_at":"2025-05-13T22:03:32.557Z","repository":{"id":6398298,"uuid":"7636316","full_name":"mpetazzoni/leaflet-gpx","owner":"mpetazzoni","description":"A GPX track plugin for Leaflet.js","archived":false,"fork":false,"pushed_at":"2025-04-24T15:02:40.000Z","size":377,"stargazers_count":576,"open_issues_count":8,"forks_count":124,"subscribers_count":33,"default_branch":"main","last_synced_at":"2025-04-29T13:46:00.749Z","etag":null,"topics":["gps-track","gpx","leaflet-gpx","leaflet-plugin"],"latest_commit_sha":null,"homepage":"http://mpetazzoni.github.io/leaflet-gpx","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mpetazzoni.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2013-01-16T00:40:29.000Z","updated_at":"2025-04-27T09:47:14.000Z","dependencies_parsed_at":"2025-04-13T19:37:16.031Z","dependency_job_id":"006871f8-2252-4973-8a11-9b6d99eee259","html_url":"https://github.com/mpetazzoni/leaflet-gpx","commit_stats":{"total_commits":91,"total_committers":38,"mean_commits":"2.3947368421052633","dds":0.5934065934065934,"last_synced_commit":"dfe4b07f87e16d95033aa407c71bc4444f7f9ef7"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpetazzoni%2Fleaflet-gpx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpetazzoni%2Fleaflet-gpx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpetazzoni%2Fleaflet-gpx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpetazzoni%2Fleaflet-gpx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mpetazzoni","download_url":"https://codeload.github.com/mpetazzoni/leaflet-gpx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254036811,"owners_count":22003653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gps-track","gpx","leaflet-gpx","leaflet-plugin"],"created_at":"2024-08-01T19:01:14.179Z","updated_at":"2025-05-13T22:03:32.519Z","avatar_url":"https://github.com/mpetazzoni.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# GPX plugin for Leaflet\n\n[![CDNJS](https://img.shields.io/cdnjs/v/leaflet-gpx.svg)](https://cdnjs.com/libraries/leaflet-gpx)\n\n[Leaflet](http://www.leafletjs.com) is a Javascript library for displaying\ninteractive maps. This plugin, based on the work of [Pavel\nShramov](http://github.com/shramov) and his\n[leaflet-plugins](http://github.com/shramov/leaflet-plugins), allows for\ndisplaying and analyzing GPX tracks and their waypoints so they can be\ndisplayed on a Leaflet map as a new layer.\n\nAs it parses the GPX data, `leaflet-gpx` records information about the\nGPX track, including total time, moving time, total distance, elevation\nstats and heart-rate, and makes it accessible through an exhaustive set\nof accessor methods.\n\nGPX parsing will automatically handle pauses in the track with a default\ntolerance interval of 15 seconds between points. You can configure this\ninterval by setting `max_point_interval`, in milliseconds, in the options\npassed to the `GPX` constructor.\n\nI've put together a complete example as a\n[demo](http://mpetazzoni.github.io/leaflet-gpx/).\n\n## License\n\n`leaflet-gpx` is under the *BSD 2-clause license*. Please refer to the\nattached LICENSE file and/or to the copyright header in gpx.js for more\ninformation.\n\n## Usage\n\nUsage is very simple:\n\n* Include the Leaflet stylesheet and script, and the leaflet-gpx script,\n  in your HTML page;\n* Create your Leaflet map, with your choice of base layer(s);\n* Create the `L.GPX` layer to display your GPX track.\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003clink rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css\" /\u003e\n    \u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js\" defer\u003e\u003c/script\u003e\n    \u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/2.1.2/gpx.min.js\" defer\u003e\u003c/script\u003e\n    \u003cstyle\u003e\n      #map { height: 500px; }\n    \u003c/style\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"map\"\u003e\u003c/div\u003e\n    \u003c!-- ... --\u003e\n    \u003cscript type=\"module\"\u003e\n      const map = L.map('map');\n      L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n        attribution: 'Map data \u0026copy; \u003ca href=\"http://www.osm.org\"\u003eOpenStreetMap\u003c/a\u003e'\n      }).addTo(map);\n\n      // URL to your GPX file or the GPX itself as a XML string.\n      const url = 'https://mpetazzoni.github.io/leaflet-gpx/demo.gpx';\n      const options = {\n        async: true,\n        polyline_options: { color: 'red' },\n      };\n\n      const gpx = new L.GPX(url, options).on('loaded', (e) =\u003e {\n        map.fitBounds(e.target.getBounds());\n      }).addTo(map);\n     \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Importing from a non-module context\n\n```javascript\nconst map = L.map('map');\nL.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n  attribution: 'Map data \u0026copy; \u003ca href=\"http://www.osm.org\"\u003eOpenStreetMap\u003c/a\u003e'\n}).addTo(map);\n\nawait import('gpx.js').then((module) =\u003e {\n  new L.GPX('https://...').on('loaded', (e) =\u003e {\n    map.fitBounds(e.target.getBounds());\n  }).addTo(map);\n});\n```\n\n## Functions\n\nIf you want to display additional information about the GPX track, you can do\nso in the 'loaded' event handler, calling one of the following methods on the\n`GPX` object `e.target`:\n\n* `get_name()`: returns the name of the GPX track\n* `get_distance()`: returns the total track distance, in meters\n* `get_start_time()`: returns a Javascript `Date` object representing the\n  starting time\n* `get_end_time()`: returns a Javascript `Date` object representing when the\n  last point was recorded\n* `get_moving_time()`: returns the moving time, in milliseconds\n* `get_total_time()`: returns the total track time, in milliseconds\n* `get_moving_pace()`: returns the average moving pace in milliseconds per km\n* `get_moving_speed()`: returns the average moving speed in km per hour\n* `get_total_speed()`: returns the average total speed in km per hour\n* `get_elevation_min()`: returns the lowest elevation, in meters\n* `get_elevation_max()`: returns the highest elevation, in meters\n* `get_elevation_gain()`: returns the cumulative elevation gain, in meters\n* `get_elevation_loss()`: returns the cumulative elevation loss, in meters\n* `get_speed_max()`: returns the maximum speed in km per hour\n* `get_average_hr()`: returns the average heart rate (if available)\n* `get_average_cadence()`: returns the average cadence (if available)\n* `get_average_temp()`: returns the average of the temperature (if available)\n\nIf you're not a fan of the metric system, you also have the following methods\nat your disposal:\n\n* `get_distance_imp()`: returns the total track distance in miles\n* `get_moving_pace_imp()`: returns the average moving pace in milliseconds per\n  mile\n* `get_moving_speed_imp()`: returns the average moving speed in miles per hour\n* `get_total_speed_imp()`: returns the average total speed in miles per hour\n* `get_elevation_min_imp()`: returns the lowest elevation, in feet\n* `get_elevation_max_imp()`: returns the highest elevation, in feet\n* `get_elevation_gain_imp()`: returns the cumulative elevation gain, in feet\n* `get_elevation_loss_imp()`: returns the cumulative elevation loss, in feet\n* `get_speed_max_imp()`: returns the maximum speed in miles per hour\n\nThe reason why these methods return milliseconds is that you have at your\ndisposal nice helper methods to format a duration in milliseconds into a cool\nstring:\n\n* `get_duration_string(duration, hidems)` format to a string like `3:07'48\"`\n  or `59'32.431`, where `duration` is in\n  milliseconds and `hidems` is an optional boolean you can use to request never\n  to display millisecond precision.\n* `get_duration_string_iso(duration, hidems)` formats to an ISO like\n  representation like `3:07:48` or `59:32.431`, where `duration` is in\n  milliseconds and `hidems` is an optional boolean you can use to request never\n  to display millisecond precision.\n\nYou can also get full elevation, heartrate, cadence and temperature data with:\n\n* `get_elevation_data()` and `get_elevation_data_imp()`\n* `get_speed_data()` and `get_speed_data_imp()`\n* `get_heartrate_data()` and `get_heartrate_data_imp()`\n* `get_cadence_data()` and `get_cadence_data_imp()`\n* `get_temp_data()` and `get_temp_data_imp()`\n\nThese methods all return an array of points `[distance, value, tooltip]` where\nthe distance is either in kilometers or in miles and the elevation in meters or\nfeet, depending on whether you use the `_imp` variant or not. Heart rate,\nobviously, doesn't change.\n\n## Reloading\n\nYou can make `leaflet-gpx` reload the source GPX file by calling the\n`reload()` method. For example, to trigger a reload every 5 seconds, you\ncan do:\n\n```javascript\nvar gpx = new L.GPX(url);\nsetInterval(function() {\n  gpx.reload();\n}, 5000);\n```\n\n## About marker icons\n\n### Configuring markers\n\nBy default, `leaflet-gpx` uses Leaflet's default icon image for all\nmarkers. You can override this behavior by providing a Leaflet `Icon`\nobject, or the path or URL to an image to use as the marker, for any of\nthe markers supported by this plugin as part of the `markers` parameter:\n\n```javascript\nnew L.GPX(url, {\n  async: true,\n  markers: {\n    startIcon: ...,\n    endIcon: ...\n    wptIcons: { ... },\n    wptTypeIcons: { ... },\n    pointMatchers: [ ... ],\n  }\n}).on('loaded', function(e) {\n  map.fitBounds(e.target.getBounds());\n}).addTo(map);\n```\n\n* `startIcon` is used for the marker at the beginning of the GPX track;\n* `endIcon` is used for the marker at the end of the GPX track;\n* `wptIcons` and `wptTypeIcons` are mappings of waypoint symbols and\n  types to the icon you want to use for each;\n* `pointMatchers` is an array of custom point matchers and their\n  respective icon (see below);\n\nYou can also override any of those to `null` to disable the\ncorresponding marker altogether.\n\nHere is how you would override the URL of the provided icons for start\nand end markers, but none of the other types of markers:\n\n```javascript\nnew L.GPX(url, {\n  async: true,\n  markers: {\n    startIcon: 'images/pin-icon-start.png',\n    endIcon: 'images/pin-icon-end.png',\n  }\n}).on('loaded', function(e) {\n  map.fitBounds(e.target.getBounds());\n}).addTo(map);\n```\n\nIt's usually preferrable and more flexible to provide a Leaflet `Icon`\ninstance directly, for example from\n[leaflet-awesome-markers](https://github.com/lennardv2/Leaflet.awesome-markers). See\n\u003chttps://leafletjs.com/examples/custom-icons/\u003e for more information.\n\n```javascript\nnew L.GPX(url, {\n  async: true,\n  markers: {\n    wptIcons: {\n      'Coffee shop': new L.AwesomeMarkers.icon({\n        icon: 'coffee',\n        prefix: 'fa',\n        markerColor: 'blue',\n        iconColor: 'white'\n      })\n    }\n  }\n}).on('loaded', function (e) {\n  map.fitBounds(e.target.getBounds());\n}).addTo(map);\n```\n\n### Marker options\n\nYou can fine tune marker options using any of the parameters expected by\n[Leaflet's base L.Icon class](https://leafletjs.com/reference.html#icon)\nusing the `marker_options` parameters:\n\n```javascript\nnew L.GPX(url, {\n  async: true,\n  marker_options: {\n    iconSize: [38, 95],\n    iconAnchor: [22, 94],\n  }\n}).on('loaded', function(e) {\n  map.fitBounds(e.target.getBounds());\n}).addTo(map);\n```\n\n### Sensible defaults\n\nNote that you do not need to override all the marker definitions, or\nmarker options, when providing the `markers` and `marker_options`\nparameters to the GPX constructor as this plugin will use sensible\ndefaults for all of those settings.\n\n## About waypoints\n\nBy default, this plugin will parse all Waypoints from a GPX file. This\ncan be controlled via the value `waypoint` in `gpx_options`, e.g.\n`parseElements: ['track', 'route', 'waypoint']`.\n\nThe icon used in the marker representing each track waypoint is\ndetermined based on the waypoint's properties, in this order:\n\n* If the waypoint has a `sym` attribute, the `markers.wptIcons[sym]`\n  icon is used;\n* If the waypoint has a `type` attribute, the `markers.wptTypeIcons[type]`\n  icon is used;\n* Point matchers are evaluated in order, if one matches the waypoint's\n  `name` attribute, its icon is used (see _Named markers_ below);\n* If none of the above rules match, the default `''` (empty string) icon\n  entry in `wptIcons` is used.\n\n```javascript\nnew L.GPX(url, {\n  async: true,\n  markers: {\n    wptIcons: {\n      '': new L.Icon.Default,\n      'Geocache Found': 'img/gpx/geocache.png',\n      'Park': 'img/gpx/tree.png'\n    },\n  }\n}).on('loaded', function (e) {\n  map.fitBounds(e.target.getBounds());\n}).addTo(map);\n```\n\n## Named points\n\nGPX points can be named, for example to denote certain POIs (points of\ninterest). You can setup rules to match point names to create labeled\nmarkers for those points by providing a `pointMatchers` array in the\n`markers` constructor parameter.\n\nEach element in this array must define a `regex` to match the point's\nname and an `icon` definition (a `L.Icon` or subclass object, or the URL\nto an icon image).\n\nEach named point in the GPX track is evaluated against those rules and\na marker is created with the point's name as label from the first\nmatching rule. This also applies to named waypoints, but keep in mind\nthat waypoint icons rules take precedence over point matchers.\n\n```javascript\nnew L.GPX(url, {\n  async: true,\n  markers: {\n    pointMatchers: [\n      {\n        regex: /Coffee/,\n        icon: new L.AwesomeMarkers.icon({\n          icon: 'coffee',\n          markerColor: 'blue',\n          iconColor: 'white'\n        }),\n      },\n      {\n        regex: /Home/,\n        icon: new L.AwesomeMarkers.icon({\n          icon: 'home',\n          markerColor: 'green',\n          iconColor: 'white'\n        }),\n      }\n    ]\n  }\n}).on('loaded', function(e) {\n  map.fitToBounds(e.target.getBounds());\n}).addTo(map);\n```\n\n## Events\n\nEvents are fired on the `L.GPX` object as the GPX data is being parsed\nand the map layers generated. You can listen for those events by\nattaching the corresponding event listener on the `L.GPX` object:\n\n```javascript\nnew L.GPX(url, async: true, {\n  // options\n}).on('addpoint', function(e) {\n  console.log('Added ' + e.point_type + ' point: ' + e.point);\n}).on('loaded', function(e) {\n  var gpx = e.target;\n  map.fitToBounds(gpx.getBounds());\n}).on('error', function(e) {\n  console.log('Error loading file: ' + e.err);\n}).addTo(map);\n```\n\nNote that for your event listeners to be correctly triggered, you need\nto pass `async: true` to the `L.GPX` constructor; otherwise the parsing\nof the GPX happens synchronously in the constructor before you your\nevent listeners get registered!\n\n`addpoint` events are fired for every marker added to the map, in\nparticular for the start and end points, all the waypoints, and all the\nnamed points that matched `pointMatchers` rules. Each `addpoint` event\ncontains the following properties:\n\n- `point`: the marker object itself, from which you can get or modify\n  the latitude and longitude of the point and any other attribute of the\n  marker.\n- `point_type`: one of `start`, `end`, `waypoint` or `label`, allowing\n  you to identify what type of point the marker is for.\n- `element`: the track point element the marker was created for.\n\nOne use case for those events is for example to attach additional\ncontent or behavior to the markers that were generated (popups, etc).\n\n`error` events are fired when no layers of the type(s) specified in\n`options.gpx_options.parseElements` can be parsed out of the given\nfile. For instance, `error` would be fired if a file with no waypoints\nwas attempted to be loaded with `parseElements` set to `['waypoint']`.\nEach `error` event contains the following property:\n\n- `err`: a message with details about the error that occurred.\n\n## Line styling\n\n`leaflet-gpx` understands the [GPX\nStyle](http://www.topografix.com/GPX/gpx_style/0/2) extension, and will\nextract styling information defined on routes and track segments to use\nfor drawing the corresponding polyline.\n\n```xml\n\u003ctrkseg\u003e\n  \u003cextensions\u003e\n    \u003cline xmlns=\"http://www.topografix.com/GPX/gpx_style/0/2\"\u003e\n      \u003ccolor\u003eFF0000\u003c/color\u003e\n      \u003copacity\u003e0.5\u003c/opacity\u003e\n      \u003cweight\u003e1\u003c/weight\u003e\n      \u003clinecap\u003esquare\u003c/linecap\u003e\n      \u003clinejoin\u003esquare\u003c/linejoin\u003e\n      \u003cdasharray\u003e0,10\u003c/dasharray\u003e\n      \u003cdashoffset\u003e3\u003c/dashoffset\u003e\n    \u003c/line\u003e\n  \u003c/extensions\u003e\n  \u003ctrkpt lat=\"...\" lon=\"...\"\u003e\u003c/trkpt\u003e\n\u003c/trkseg\u003e\n```\n\nYou can override the style of the lines by passing a `polyline_options`\narray into the `options` argument of the `L.GPX` constructor, each\nelement of the array defines the style for the corresponding route\nand/or track in the file (in the same order).\n\n```javascript\nnew L.GPX(url, {\n  polyline_options: [{\n    color: 'green',\n    opacity: 0.75,\n    weight: 3,\n    lineCap: 'round'\n  },{\n    color: 'blue',\n    opacity: 0.75,\n    weight: 1\n  }]\n}).on('loaded', function(e) {\n  var gpx = e.target;\n  map.fitToBounds(gpx.getBounds());\n}).addTo(map);\n```\n\nIf you have many routes or tracks in your GPX file and you want them to\nshare the same styling, you can pass `polyline_options` as a single\nobject rather than an array (this is also how `leaflet-gpx` worked\nbefore the introduction of the array):\n\n```javascript\nnew L.GPX(url, {\n  polyline_options: {\n    color: 'green',\n    opacity: 0.75,\n    weight: 3,\n    lineCap: 'round'\n  }\n}).on('loaded', function(e) {\n  var gpx = e.target;\n  map.fitToBounds(gpx.getBounds());\n}).addTo(map);\n```\n\nFor more information on the available polyline styling options, refer to\nthe [Leaflet documentation on\nPolyline](https://leafletjs.com/reference.html#polyline). By\ndefault, if no styling is available, the line will be drawn in _blue_.\n\n## GPX parsing options\n\n### Selecting which elements define the track\n\nSome GPX tracks contain the actual route/track twice, both the `\u003ctrk\u003e`\nand `\u003crte\u003e` elements are used. You can tell `leaflet-gpx` which tag to\nuse or to use both (which is the default setting for backwards\ncompatibility). The `parseElements` field of `gpx_options` controls this\nbehavior. It should be an array that contains `'route'` and/or `'track'`\nand/or `'waypoint'`.\n\n### Multiple track segments within each track\n\nGPX file may contain multiple tracks represented by `\u003ctrk\u003e` elements,\neach track possibly composed of multiple segments with `\u003ctrkseg\u003e`\nelements. Although this plugin will always represent each GPX route and\neach GPX track as distinct entities with their own start and end\nmarkers, track segments will by default be joined into a single line.\n\nYou can disable this behavior by setting the `joinTrackSegments` flag to\n`false` in the `gpx_options`:\n\n```javascript\nnew L.GPX(url, {\n  gpx_options: {\n    joinTrackSegments: false\n  }\n}).on('loaded', function(e) {\n  map.fitBounds(e.target.getBounds());\n}).addTo(map);\n```\n\n## Caveats\n\n* Distance calculation is relatively accurate, but elevation change\n  calculation is not topographically adjusted, so the total elevation\n  gain/loss/change might appear inaccurate in some situations.\n* Currently doesn't seem to work in IE8/9. See #9 and #11 for\n  discussion.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpetazzoni%2Fleaflet-gpx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmpetazzoni%2Fleaflet-gpx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpetazzoni%2Fleaflet-gpx/lists"}