CLI tool
mapbox/togeojson avatar
mapbox/togeojson

togeojson: a small, deliberately incomplete KML and GPX to GeoJSON converter

convert KML and GPX to GeoJSON, without the fuss

1,326 stars320 forksJavaScriptBSD-2-Clause

At a glance

What is it?
togeojson converts KML and GPX documents to GeoJSON in a browser or under Node.js, exposing two functions that take an XML DOM and return a GeoJSON object. Its most interesting property is its restraint: NetworkLinks, GroundOverlays, and folders are left out on purpose, and the project explains the reasoning.
Who is it for?
togeojson fits a project that needs a quick, dependable conversion of KML or GPX into GeoJSON, in a browser or a script, without pulling in a geospatial toolchain. It is the wrong choice for files that rely on NetworkLinks, GroundOverlays, or folder structure, since the project rules those out as having no GeoJSON equivalent, and it is the wrong choice for a GeoJSON round trip, since it only converts in one direction.
Can I use it commercially?
Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 116 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The API is two functions and one input contract

togeojson converts KML and GPX to GeoJSON, in a browser or with Node.js, and the whole public API is two functions.

`toGeoJSON.kml(doc)` converts a KML document to GeoJSON, and `toGeoJSON.gpx(doc)` converts a GPX document to GeoJSON. The output is a JavaScript object of GeoJSON data, and you can convert it to a string with JSON.stringify or use it directly in libraries like mapbox.js.

The input contract is where most integration friction comes from, and the project is explicit about it. The first argument, `doc`, must be a KML or GPX document as an XML DOM, not as a string. You can get this using jQuery's default `.ajax` function, or using a bare XMLHttpRequest with the `.response` property holding an XML DOM.

That is a deliberate design choice rather than an oversight. A converter that parsed strings would need to bundle or depend on an XML parser, and in the browser the parser is already there. The project's own protip section shows the one-liner for when you do have a string:

js
var dom = (new DOMParser()).parseFromString(xmlStr, 'text/xml');

The `text/xml` mime type is not a detail either, since it is what selects the parser mode.

The package is named `@mapbox/togeojson` and the description in the metadata is simply convert KML and GPX to GeoJSON. The project advertises three properties in its summary, tiny, tested, and working in both Node.js and browsers, and that short list is the whole pitch: a converter that does one thing, does it in both environments, and does not drag a dependency tree with it.

Three ways to use it, and each one assumes something different

The three usage paths in the project summary are worth comparing, because each makes a different assumption about what you have.

The CLI is the simplest. You install it into your path with `npm install -g @mapbox/togeojson` and then:

code
~> togeojson file.kml > file.geojson

That is the whole interface. One input file, one output on standard output, which redirects to a file. The `bin` entry in package.json points at the `togeojson` file at the repository root, and the runtime dependencies are `@xmldom/xmldom` and `concat-stream` for the streaming and `minimist` for the argument parsing.

The Node.js path installs into your project with `npm install --save @mapbox/togeojson`, and the example in the summary shows the whole pattern:

javascript
// using togeojson in nodejs

var tj = require('@mapbox/togeojson'),
    fs = require('fs'),
    // node doesn't have xml parsing or a dom. use xmldom
    DOMParser = require('xmldom').DOMParser;

var kml = new DOMParser().parseFromString(fs.readFileSync('foo.kml', 'utf8'));

var converted = tj.kml(kml);

var convertedWithStyles = tj.kml(kml, { styles: true });

The comment about Node not having XML parsing or a DOM, and using xmldom, is the important line. In Node you have to supply the parser, and the example uses the `xmldom` package. The second call shows the styles option, which is the only option the API has.

The browser path is the third, and it is the most old-fashioned. You download the file into your project:

code
wget https://raw.githubusercontent.com/mapbox/togeojson/master/togeojson.js

and then load it with a script tag:

html
<script src='jquery.js'></script>
<script src='togeojson.js'></script>
<script>
$.ajax('test/data/linestring.kml').done(function(xml) {
    console.log(toGeoJSON.kml(xml));
});
</script>

The project notes that toGeoJSON doesn't include AJAX, you can use jQuery for just AJAX. The `.done` callback receives the parsed XML document, which is exactly the DOM the converter wants, so the jQuery path is a natural fit rather than an awkward one.

The package.json also has a browser field setting `xmldom` to false, so the XML parser is excluded from browser bundles, and a browserify transform of brfs, which is what lets the test fixtures be read from disk at build time.

What KML features survive, and what the two gaps are

The KML feature support list is a checklist with two unchecked boxes, and the checked ones describe a converter that handles the shapes of KML people actually draw.

Supported for KML are Point, Polygon, and LineString geometries, name and description, ExtendedData, SimpleData, MultiGeometry mapping to a GeoJSON GeometryCollection, styles with hashing, Tracks and MultiTracks with `gx:coords` including altitude, TimeSpan, and TimeStamp.

The two unsupported entries are NetworkLinks and GroundOverlays.

MultiGeometry becoming a GeometryCollection is the right call and shows the thinking behind the project. A KML MultiGeometry is a container of geometries, and a GeoJSON GeometryCollection is a container of geometries, so the mapping is natural. TimeSpan and TimeStamp are also handled, which matters because KML is a time-aware format and GeoJSON has no first-class time, so the times end up as properties on the feature.

For GPX, the support list is shorter. Line paths and line styles are supported. Properties are partially supported, with name, cmt, desc, link, time, keywords, sym, and type tags handled, while author and copyright tags are not.

The project gives a principle for the boundaries. This module should support converting all KML and GPX features that have commonplace equivalents in GeoJSON. KML is a very complex format with many features, and some of them, like NetworkLinks, folders, and GroundOverlays, don't have a GeoJSON equivalent. In those cases, toGeoJSON doesn't convert the features. It also doesn't crash on these constructs: toGeoJSON should be able to run on all valid KML and GPX files without crashing, but for some files it may have no output.

That last sentence is the important contract. The failure mode is an empty result, not an exception. A caller that assumes a non-empty FeatureCollection will get a silent surprise rather than an error, which is worth designing around.

Why NetworkLinks are out, and what to do instead

The FAQ section explains the NetworkLinks decision in more detail than any other part of the project, and the reasoning is the most useful thing in the repository.

The NetworkLink KML construct allows KML files to refer to other online or local KML files for their content. It is often used to let people pass around files but keep the actual content on servers.

To support it, toGeoJSON would need to be asynchronous and perform network requests. The project's judgement is that these changes would make it more complex and less reliable in order to hit a limited use case, and they would rather keep it simple and not require users to think about network connectivity and bandwidth in order to convert files.

That is a coherent position, and it generalises. A converter that can fetch a remote document has to deal with timeouts, with partial failures, with the network being unavailable in a batch job, with ordering when a document references several others, and with what to do when a referenced URL returns an error. A converter that only ever reads a document you hand it has none of that, and the output is a pure function of the input, which is the property that makes a converter testable.

The project also says the alternative rather than leaving it as a gap. NetworkLink support could be implemented in a separate library as a pre-processing step if desired. So the shape of the answer is: resolve the links first, then convert.

The same logic applies to the wider question of whether a given KML feature should be supported, and the project's answer to that is the principle quoted in the previous section, with an invitation to other libraries to pick up the unsupported features. Support for them is out of scope for toGeoJSON.

The style hashing is a pragmatic answer to a broken format

The FAQ has one entry that explains a design decision, and it is about KML's style system.

KML's style system isn't semantic: a typical document made through official tools has hundreds of identical styles. So, togeojson does its best to make this into something usable, by taking a quick hash of each style and exposing `styleUrl` and `styleHash` to users. This lets you work backwards from the awful representation and build your own styles or derive data based on the classes chosen.

And the implied caveat: this does not try to represent all data contained in KML styles.

That is a good example of engineering judgement applied to a badly designed format. KML styles are addressed by reference, and a document produced by a tool will have a style per shape, so the same visual style is repeated hundreds of times under different identifiers. Mapping those faithfully into GeoJSON would produce a feature collection with a `style` property per feature and no way to group them. Hashing collapses the duplication into a stable identifier, which means the consumer can group by hash and apply one style per group, which is what you want to do anyway.

The `styles: true` option in the API exists to turn this on, and it is off by default. The default matters: without the option the output is clean GeoJSON with no style noise, and with it you get the grouping keys. That is the right default for a converter whose job is to produce data rather than presentation.

The styles feature is listed as supported in the KML feature checklist, with hashing named explicitly, so this is a deliberate part of the design rather than an accident of implementation.

The project is small, old, and shows its age in the tooling

The repository layout and the package metadata describe a project that has been stable for a long time, and the tooling tells you how old.

The version is 0.16.2, and the release history shows 0.16.1 from 2023-09-11 and 0.16.2 from 2023-09-27, with v0.16.0 from 2016 whose release note is Support for waypoint, link, and type tags in GPX. The last push to the repository was on 2026-06-29, and the project is not archived, so the code is still being touched. The homepage is mapbox.github.io/togeojson.

The dev dependencies are from a different era. `eslint` is at `^3.3.0`, `istanbul` is at `~0.4.4`, `tape` is at `~4.6.0`, `brfs` at `~1.4.3`, and `glob` at `^7.0.5`. The test script is `eslint togeojson.js && tape test/*.js` and the coverage script is `istanbul cover test/index.js && coveralls <./coverage/lcov.info`. That is a pre-2017 toolchain, and it is what a project that has not needed new features looks like.

The runtime side is more current. The XML parser is `@xmldom/xmldom` at `^0.8.10`, and `minimist` is at 1.2.8. The `concat-stream` dependency at `~2.0.0` is the old major.

The other files in the tree tell the rest of the story. There is a `component.json`, which is the manifest format for the old Browserify component ecosystem, and a `togeojson` file at the root which is the CLI, and a `togeojson.js` which is the browserified bundle, and an `index.html` with a `css/` directory, which is the demo page hosted at the project homepage. There is a `CODEOWNERS` file, an `.eslintrc`, a `.travis.yml` for the continuous integration, and a `CHANGELOG.md`.

The licence is BSD-2-Clause, and the FOSSA badge at the bottom of the project summary is the licence and security scanning service, which is how the licence is presented to a consumer rather than buried in a file.

Editorial conclusion

togeojson fits a project that needs a quick, dependable conversion of KML or GPX into GeoJSON, in a browser or a script, without pulling in a geospatial toolchain. It is the wrong choice for files that rely on NetworkLinks, GroundOverlays, or folder structure, since the project rules those out as having no GeoJSON equivalent, and it is the wrong choice for a GeoJSON round trip, since it only converts in one direction. Check three things before you rely on it: that your input is an XML DOM rather than a string, because the functions will not parse for you, that you use the styles option only when you want the style hashing, and that you read the feature support list, because two KML features and two GPX property tags are explicitly unsupported.

Frequently asked questions

How do I convert a KML or GPX file to GeoJSON with togeojson?

From the command line, install it with `npm install -g @mapbox/togeojson` and run `togeojson file.kml > file.geojson`. In Node, install it into the project and parse the file yourself with an XML DOM, because the `kml` and `gpx` functions take an XML DOM and not a string.

Why does toGeoJSON not support NetworkLinks in KML?

Supporting them would require the library to be asynchronous and to perform network requests, which the project says would make it more complex and less reliable to hit a limited use case, and would require users to think about network connectivity and bandwidth to convert files. NetworkLink support could be implemented in a separate library as a pre-processing step.

What does the styles option do in togeojson?

KML's style system is not semantic, and a typical document has hundreds of identical styles, so togeojson takes a quick hash of each style and exposes `styleUrl` and `styleHash`. That lets you group features by style and build your own styles. It does not represent all the data contained in KML styles.

Which KML and GPX features does togeojson not convert?

For KML, NetworkLinks and GroundOverlays are not converted, and folders are not converted either, because these have no GeoJSON equivalent. For GPX, the author and copyright property tags are not supported, while name, cmt, desc, link, time, keywords, sym, and type are. The library does not crash on unsupported constructs, but may produce no output for some files.

Official sources

  1. License: BSD-2-Clause
  2. mapbox/togeojson on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mapbox-togeojson.svg)](https://hysenlabs.com/projects/mapbox-togeojson)

Was this useful?