Library / SDK
mapbox/leaflet-omnivore avatar
mapbox/leaflet-omnivore

leaflet-omnivore turns GPX, KML, CSV and WKT into Leaflet layers

universal format parser for Leaflet & Mapbox.js

680 stars127 forksJavaScriptNOASSERTION

At a glance

What is it?
Omnivore is a browser library that gives Leaflet and Mapbox.js one call per geospatial format, so a GPX track, a KML file, a CSV of points or a WKT string becomes a map layer without you writing the conversion. It is old, small and built on browserify, and its README tells you when you should skip it and use the underlying parsers directly.
Who is it for?
Adopt leaflet-omnivore when you already use Leaflet or Mapbox.js in the browser and you have data in a format other than GeoJSON, and when you want the same layer and event contract across those formats. Do not adopt it if you only need one parser, because the README itself points you at the individual libraries, and do not adopt it for a build pipeline, since the package is a browserify bundle with a script tag installation path.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 121 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 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One call per format, all returning a layer

The premise is stated in the second line of the README. Leaflet supports the GeoJSON format by default, so what do you do if you have something else? That is where omnivore comes in.

It currently supports CSV, via csv2geojson, GPX and KML, both via toGeoJSON, WKT, via wellknown, TopoJSON, and encoded polylines, via polyline. The library also includes an AJAX library, corslite, so you can specify what you want to add to the map with just a URL, which is the part that removes the usual CORS friction when your data file lives somewhere else.

The API is one method per format, each taking a URL and returning a layer:

js
var map = L.mapbox.map('map', 'mapbox.streets')
    .setView([38, -102.0], 5);

omnivore.csv('a.csv').addTo(map);
omnivore.gpx('a.gpx').addTo(map);
omnivore.kml('a.kml').addTo(map);
omnivore.wkt('a.wkt').addTo(map);
omnivore.topojson('a.topojson').addTo(map);
omnivore.geojson('a.geojson').addTo(map);
omnivore.polyline('a.txt').addTo(map);

Each format also has a `.parse` counterpart that takes a string or an object rather than a URL, and the README is precise about the difference in what they accept: `.kml.parse` parses KML from a string of XML or an XML DOM, `.gpx.parse` the same for GPX, `.wkt.parse` takes a WKT string, `.topojson.parse` takes a TopoJSON given as a string or object, and `.polyline.parse` takes the text.

The optional arguments have a rule that is easy to get wrong. The parser options are options sent to the parser library, not to the layer, so if you want to pass options to the layer you use the custom layer argument instead.

customLayer is where you keep control of the styling

By default the library constructs an `L.geoJson()` layer internally and calls `.addData(geojson)` on it in order to load it full of GeoJSON. If you want a different kind of layer, like an `L.mapbox.featureLayer()`, you can, by passing it as `customLayer`, as long as it supports events and `addData()`.

That last condition is the contract. Your layer has to accept GeoJSON through `addData()` and be able to emit events, because omnivore uses both: `addData()` to load the parsed features, and the event system to tell you when loading finished.

Passing custom options works the same way:

js
var customLayer = L.geoJson(null, {
    filter: function() {
        // my custom filter function
        return true;
    }
});

var myLayer = omnivore.csv('foo', null, customLayer);

And adding custom styles to a GeoJSON layer:

js
var customLayer = L.geoJson(null, {
    // http://leafletjs.com/reference.html#geojson-style
    style: function(feature) {
        return { color: '#f00' };
    }
});
// this can be any kind of omnivore layer
var runLayer = omnivore.kml('line.kml', null, customLayer)

The third argument is the same slot for every format, and the README notes that you can also use the API to pass custom options to an `L.geoJson()` instance. So the same three-argument shape, parser options then custom layer, works whether you are loading a CSV or a KML.

URL loading is asynchronous, so you wait for ready

This is the part of the API that causes the most confusion, and the README handles it head on.

Each function returns an `L.geoJson` object, and functions that load from URLs are asynchronous, so they will not immediately expose accurate `.setGeoJSON()` functions. For this reason the library fires events. `ready` is fired when all data is loaded into the layer, and `error` is fired if data can't be loaded or parsed.

js
var layer = omnivore.gpx('a.gpx')
    .on('ready', function() {
        // when this is fired, the layer
        // is done being initialized
    })
    .on('error', function() {
        // fired if the layer can't be loaded over AJAX
        // or can't be parsed
    })
    .addTo(map);

So a layer can be added to the map immediately, before it contains anything, and the data appears when the request completes. The error case is explicit too, and the comment names the two failure modes: the layer can't be loaded over AJAX, or it can't be parsed.

There is one asymmetry worth remembering. `ready` does not fire if you don't use an asynchronous form of the function, like `.topojson.parse()`, because you don't need an event. In that case you just run your code after the call, since the parse is synchronous and the layer is already populated when it returns.

Installing it is a script tag or a downloaded file

The README gives two routes. The first is the Mapbox Plugins CDN:

html
<script src='//api.tiles.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.3.1/leaflet-omnivore.min.js'></script>

The second is to download `leaflet-omnivore.min.js` from the repository.

Both point at the same built artefact, and the version in the CDN tag is v0.3.1 while the package manifest records 0.3.4. That gap is small but it is the kind of thing that produces a bug report, because the script tag and the package are two different delivery routes that can drift.

For development, the project is a browserify project:

sh
git clone [email protected]:mapbox/leaflet-omnivore.git
sh
cd leaflet-omnivore
sh
# to run tests
npm install
sh
# to build leaflet-omnivore.js
npm run prepublish

And the README is explicit about which files are generated. `leaflet-omnivore.js` and `leaflet-omnivore.min.js` are built files generated from `index.js` by browserify. If you find an issue, it either needs to be fixed in `index.js`, or in one of the libraries leaflet-omnivore uses to parse formats.

That last sentence is the maintenance rule for the project, and it is the part to remember if you are tempted to patch the built file.

The dependency list is the architecture

The package manifest is short and shows exactly what the library is made of:

json
{
  "name": "@mapbox/leaflet-omnivore",
  "version": "0.3.4",
  "description": "a geospatial format parser for Leaflet",
  "main": "index.js",
  "scripts": {
    "test": "zuul --local -- test/test.js",
    "test-remote": "zuul -- test/test.js",
    "test-headless": "zuul --phantom -- test/test.js",
    "prepublish": "browserify -s omnivore index.js > leaflet-omnivore.js && uglifyjs leaflet-omnivore.js -c -m > leaflet-omnivore.min.js"
  },

The runtime dependencies are the parsers themselves: `csv2geojson` at about 5.0.0, `togeojson` at 0.13.0, `corslite` at 0.0.7, `wellknown` at 0.4.2, `topojson` at 1.6.26 and `polyline` at 0.2.0, plus `brfs` as a browserify transform so that file contents can be inlined at build time.

The build is two commands, browserify to produce the global `omnivore` object and then uglifyjs to minify it, and the published files are `index.js`, `leaflet-omnivore.js` and `leaflet-omnivore.min.js`.

The test setup shows the era. Zuul runs the test file, in local mode, in remote mode and in headless mode, with mocha, tape, jshint, the `st` server and `phantomjs-prebuilt` in the development dependencies, and `mapbox.js` at 2.4.0 pinned alongside it. The author is recorded as Tom MacWright, and the licence in the manifest is BSD-3-Clause, while the repository metadata for the project lists the licence as one GitHub cannot categorise, so the LICENSE file is the document to read.

The FAQ tells you when to skip omnivore entirely

The most useful paragraph in the README is the first FAQ entry, and it is a warning rather than a feature.

The question is what if I just want one format, and the answer is that each format is specified in a different module, so you can just use TopoJSON, csv2geojson, wellknown or toGeoJSON individually. The whole library is a convenience over libraries that are perfectly usable on their own.

That is the honest framing of the value proposition. What omnivore adds on top is the layer contract: the same `L.geoJson()` construction, the same optional `customLayer` slot, the same `ready` and `error` events, and the same URL or string entry point for every format. If you only need one format, none of that is worth a bundle.

The second FAQ entry is about the other common failure. If an AJAX request fails for a cross-domain request, read up on the same origin restriction, and note that by default the library uses corslite, so cross-domain requests will try to use CORS.

The pattern across the two answers is the same. Omnivore is a convenience layer with opinions about how a map application should look, and it will help you if your application already looks like that, and it will cost you a dependency and a debugging session if it does not.

Editorial conclusion

Adopt leaflet-omnivore when you already use Leaflet or Mapbox.js in the browser and you have data in a format other than GeoJSON, and when you want the same layer and event contract across those formats. Do not adopt it if you only need one parser, because the README itself points you at the individual libraries, and do not adopt it for a build pipeline, since the package is a browserify bundle with a script tag installation path. Verify first that the version you load matches the one you test against, since the CDN tag in the README pins v0.3.1 while the package manifest reads 0.3.4, that the `customLayer` you pass supports events and `addData()`, and that the licence in the package manifest, BSD-3-Clause, matches what the LICENSE file in the repository actually says.

Frequently asked questions

Which formats does leaflet-omnivore support?

CSV via csv2geojson, GPX and KML via toGeoJSON, WKT via wellknown, TopoJSON via topojson, and encoded polylines via polyline, on top of GeoJSON which Leaflet handles natively. It also bundles corslite so a URL can be passed directly for the AJAX request.

How do I know when a layer loaded in leaflet-omnivore?

Functions that load from URLs are asynchronous, so the layer is not populated when the call returns. Listen for the `ready` event, which fires when all data is loaded into the layer, and for `error`, which fires if the data cannot be loaded or parsed. The synchronous `.parse` forms do not fire `ready`.

Can I use my own layer style with leaflet-omnivore?

Yes. Pass a custom layer as the third argument, and it must support events and `addData()`. The README shows filtering with a custom `filter` function, styling through a `style` function, and using `L.mapbox.featureLayer()` in the same slot.

Do I need leaflet-omnivore if I only support one format?

The README's own FAQ says you do not, because each format is specified in a separate module and you can use TopoJSON, csv2geojson, wellknown or toGeoJSON individually. Omnivore is worth it when you want the same layer contract and the same ready and error events across several formats.

Official sources

  1. Issues
  2. mapbox/leaflet-omnivore on GitHub
  3. Project website
  4. README
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-leaflet-omnivore.svg)](https://hysenlabs.com/projects/mapbox-leaflet-omnivore)

Was this useful?