Looking for iNaturalist observation map visualisation suggestions

Read these two posts and see if one or both are what you’re looking for?



1 Like

Welcome to the forum, @vojczech!

This is all I’m doing. But it is very cumbersome, and not really what I need. My ideal solution would be a heatmap of different layers (with each layer representing whatever species I select)

hmm… if you’re trying to do heatmaps, then you should try the tilesets served up by iNat and GBIF, if you haven’t already.

iNaturalist’s tiles are a little bit uglier in most cases (in my opinion), but they can be set to any color using the color parameter. GBIF’s tiles look a little better (in my opinion), and the polygon tilesets in particular offer lots of options for formatting, though there are fewer color options available.

here are 4 quick examples of observation tilesets for Diloma concameratum (Wavy Top) that i pulled up in ArcGIS Online over a dark basemap (each snapshot is preceded by the tileset URL i used for that layer):






Please pardon my ignorance…I get a “400” error when I click on the GBIF links. I get a parsing error when I click on the api.inaturalist links.

Is there something I should be doing to set both of them up before being able to access the links?

Thanks in advance

1 Like

the api.inat.org and api.gbif.org URLs above won’t actually take you anywhere. but they do provide a GIS tool – in this case, ArcGIS Online – the instructions needed to get map tiles from iNat and GBIF.

you may have noticed that when you load many online maps, they get rendered a section or a tile at a time, not all at once. as you pan beyond the original extent of the map, new sections or tiles get loaded and added to the map. as you zoom in and out, new tiles get loaded to provide a better representation of the map at the given zoom level.

you’ll notice in the tile URLs i provided above, they all contain a {level}, a {col}, and a {row}. ArcGIS will dynamically replace these with a zoom level, an x value (or column index), and a y value (or row index). that tells the API to return a map tile that represents a particular section of the Earth at a given zoom level. when the API provides ArcGIS that tile (usually a picture file – in these cases, all *.png files), ArcGIS puts that tile on the appropriate place in the map, along with all the other tiles needed to fully display the map.

here’s how you can create a map with one of these tilesets in ArcGIS Online:

  1. go to https://www.arcgis.com
  2. at the top of the screen, find Maps, and click on it. (you don’t have to sign in.)
  3. that should get you to a screen with a blank map. at the top right of that screen, click Modify Map.
  4. that wll add some editing options to the toolbar below. find Add in that tool bar (on the left side of the screen), and click on that, then select “Add Layer from Web” from the drop list.
  5. that will give you a pop up. in that pop-up, select “A Tile Layer”, then fill in the URL (using one of the URLs i provided before), a title for it (which is what show up in the left Contents pane in the screenshots), and credits (which is what shows up in the bottom right corner of the map in the screenshots). then click Add Layer.
  6. finally, you may want to use a basemap that is less busy than the default topo basemap. you can either select one by clicking the Basemap button in the map toolbar, or searching for one via Add > Search for Layer. (ArcGIS has lots of layers available.)

if you don’t like ArcGIS, you can also do something similar in other GIS tools. the specific steps will differ, of course, but the concept is the same. (the URLs may differ in the other tools, too. for example, QGIS and Leaflet.js expect {z}, {x}, and {y} in the URL instead of {level}, {col}, and {row}.)

hope that helps. if you have other questions, feel free to ask.

also note there are a few more advanced things that can be done. for example, you can use different tilesets for different zoom level ranges. so for example, the hexagons that GBIF provides may look okay at that size in my example at a continent level, but they might look too much like points at a higher (more granular zoom level). so you might choose to use relatively bigger hexagons at higher zoom levels by changing the hexPerTile value in the URL (smaller values result in bigger hexagons in this case). you can also modify the transparency of the tiles within your particular GIS tool.


it occurred to me last night / this morning that i could probably take the results from the UTFGrid tiles that the iNaturalist API serves up and use them to create my own custom observation maps. so when i got some time this evening, i tried to create my own custom map based on UTFGrid data, and it actually worked out better than i expected. i think UTFGrids might be a little less precise, but they are much more customizable than either iNat’s standard observation tiles or GBIF’s tiles, and they offer all the query options that iNat provides. below is an example of all observations from iNat. i compared it against another map i created from GBIF (https://forum.inaturalist.org/t/observation-density/6305/6), and it looks similar (which is good from a data quality perspective). i’ll probably work on it a little bit more when i get time just for fun, but if anyone is interested in it, i’ll clean it up a little bit more and can share the code when it’s more functional.

UPDATE: i was doing some more sanity checks on the data before i got too far down the road of coding something that turned out later to be flawed – so far, things look ok – and i just wanted to share some more maps that i thought were interesting.

here’s a map that shows observations over the last 4 years (r=2019, y=2018, g=2017, b=2016, brightest dots represent at least 20k observations). you can sort of see how iNat usage is spreading across the world:

this map shows observations over the last 4 months (r=Oct, y=Sept, g=Aug, b=July, brightest dots represent at least 2500 observations):

this map shows observations Monarchs over the last 4 months (r=Oct, y=Sept, g=Aug, b=July, brightest dots represent at least 25 observations). you can sort of see a shift in observations over these months related to migration:


ok… i’ve been coding a bit, and i’m at a point of diminishing returns for further coding, i think. i didn’t get to the point of producing a mapping interface, but the code is here (https://github.com/jumear/stirfry/blob/gh-pages/iNat_UTFgrid_based_custom_density_map.html), and hopefully it’s relatively easy to understand and tinker with. you’re welcome to adapt it as you please.

here are some examples of different custom maps i created using the UTFgrids:


one last contribution here: https://jumear.github.io/stirfry/iNat_UTFgrid_based_density_map_for_Leaflet.html
(code: https://github.com/jumear/stirfry/blob/gh-pages/iNat_UTFgrid_based_density_map_for_Leaflet.html)

the previous examples were built in a static map viewer that i cobbled together. but this new example is built as an extension of Leaflet.js. so it might be easier to tinker with for those who are familiar with Leaflet.js, and this example is also easier for exploration since you can pan and zoom. the markers are also created a little differently here, in a way that is less resource intensive but is a little less flexible (tradeoffs).

that’s it for now.


This is really impressive. Is there a way that non-coders can use this to produce their own maps, without having to manipulate things at the ‘code’ level?

because maps can be so variable, it would take a lot more code than i’m willing to do to create a proper front-end to allow people to really configure their own maps.

that said, the Leaflet-integrated version of the map does allow you to specify parameters in the URL that will filter down the results (just like you can in the iNaturalist Explore and Identify pages), and i just added the ability to pass in custom scale factors, too (which will allow you to customize scaling ranges).

for example, this gives you Rudbeckia amplexicaulis observations scaled from 0-5 observations per cell at 0 zoom, down to just 1 observation per cell at the highest zoom levels:

or this will apply scaling for 0-5 observations per cell across all zoom levels:

if you need advice on how to tinker with either of my examples, let me know what you’re trying to achieve.

they’re both just HTML files. so you can begin tinkering by doing the following:

  1. click on the Github code link of the example that you’re interested in
  2. just above the code on that page, there’s a Raw button. click that.
  3. select and copy all the text.
  4. open a code or plain text editor. (i just use Notepad in Windows.)
  5. paste the code into the code/text editor, and then save the file as an .html file.
  6. now find the newly saved file, and open it in your favorite internet browser just to make sure it’s working.
  7. if the browser opened up the file okay, you can go back to your code/text editor and start editing. when you’re ready to see changes. just save the changes, and then reload the page in your browser.

i’ve been thinking about a 3rd version of this that could take filter parameters in a URL and generate a static map that might scale automatically, automatically set the map extent, and provide the option to generate a proper heatmap, based on parameters entered, but i probably won’t get to that any time soon.


at some point, i’m planning to also add the ability to pass in parameters to set the following:

  1. default map center point (lat / long)
  2. default map zoom
  3. default layer = opacity markers or gradient markers
  4. hide instructions (the pane on the left side of the screen)
  5. make these layers active by default: taxon place, taxon range, place. (these layers currently appear as options – though inactive by default – if you pass in a taxon_id or place_id)

are there any options that might be useful in that example?

1 Like

Thanks for testing it Friel! Rinat package which is running the app seems to be slower in downloading higher number of observations per user. So app looks frozen but actually is still downloading. I will try to make some workaround.

1 Like

in case you haven’t seen the news, they’re now testing a new style of iNat density map. see https://forum.inaturalist.org/t/open-test-of-map-tile-improvements/7833.


Thank you for your work here pisum. I’ll keep an eye on this thread to see how the project progresses.

Thank you also for mentioning the iNat density map, I hadn’t seen the announcement.

Warm regards,


1 Like

i added these today, if anyone is still following this.


This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.

i made one more visualization that compares observation density from one time period to observation density from another time period, just to see what it would look like. not sure how useful it is, but i thought i’d share since it’s coded at this point.

as an example, this (https://jumear.github.io/stirfry/iNat_UTFgrid_based_density_comparison_map.html?d1=2019-05-18&d2=2020-05-17&d1b=2018-05-18&d2b=2019-05-17) compares all observations from the trailing year to the previous year. the map is explorable (you can pan and zoom), but here’s a static screenshot of the above example at a worldwide view zoom level:

(red means a lot more observations, blue means a lot fewer observations, and green means no or less significant change.)

code is here: https://github.com/jumear/stirfry/blob/gh-pages/iNat_UTFgrid_based_density_comparison_map.html

UPDATE: originally, i included only a relative comparison (A/B), but now i’ve added an optional layer for absolute comparison (A-B).


You’ve put a lot of effort into this. Very sophisticated

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.