geojson-vt and leaflet
Geographical data is amazing, if you have a decent dataset you can find out a lot of things by running a diverse range of analysis. Most of the times you will be using shapefiles in order to analyze geographical data but the standard filetype to display geographical data in web browsers is GeoJSON. We will create a simple layer using a complex GeoJSON file.
In this tutorial we are going to be using GeoJSON which is a format for encoding a variety of geographic data structures, as you may already be guessing GeoJSON is based on JavaScript Object Notation. If you want a better understanding of how GeoJSON works you should check the GeoJSON Format Specification. You can find the code at github but you will have to create your own GeoJSON (shown in this tutorial).
The dataset
First of all we need to find a nice dataset, we will be using geojson-vt a highly efficient JavaScript library for slicing GeoJSON data into vector tiles on the fly, primarily designed to enable rendering and interacting with large geospatial datasets on the browser side. I live in Mexico and one nice dataset is the land use for our country, you can find the file of Mexico’s land use at the INEGI’s website.
Lets download the file and unzip it:
|
|
One issue with this dataset is that the current projection is not the one we need to use in order to render in leaflet. In order to change the projection we may use ArcGIS or a similar geospatial tool, in this case we want to avoid as much as possible using tools that are an overkill for the job we want to get done. A great tool that helps with the transformation of data is mapshaper.
We can install mapshaper by running npm install -g mapshaper
. This will install the command line tool and the mapshaper gui, lets use the mapshaper-gui so we can explore the data we just downloaded. Run mapshaper-gui
and load the shapefile. We should see something like this:
Thats Mexico and the land use layer which INEGI created, thats perfect but we still need to know if the projection used in this shapefile is compatible with the approach we are going to use to render the data in the browser. If we use mapshaper’s command line tool we can get a little bit of information of the projection and a sneek peak of the data we have:
|
|
We find that the projection is not the one we’ll be needing, we need our data to be projected using WGS84. We can export the file to a GeoJSON format and simpify while we are changing the projection. We should also just store the attributes we care for. The attribute TIPO is the one that we are interested in so lets keep that in mind. If you are interested in getting to know the options mapshaper offers you should go check the documentation.
|
|
We are changing the projection to WGS84, simplyfying to a 30% (Percentage of removable points to retain. Accepts values in the range 0%-100% or 0-1), selecting the attribute data TIPO so it can be stored in the properties of our features and finally exporting to the GeoJSON format. If we load the newly projected data in the mapshaper-gui we should see how the projection changed:
Our projection is now correct and we have our GeoJSON ready, we still need to find all the values of the attribute data TIPO and create a set with those. Lets use the each
flag provided by mapshaper and some shell utilities to find the unique values present in this attribute:
|
|
Keep the files we generated (GeoJSON and types.txt), we can now go and start building our GeoJSON explorer with geojson-vt.
geojson-vt
If we want to use geojson-vt we need to create a javascript application. Use this package.json to start our project. The important parts here are the scripts and dependencies, the start script will run the webpack-dev-server without webpack-dev-server or webpack being installed globally. Don’t forget to run npm install
to install all the dependencies in our package.json
Lets create a folder structure like the following:
- dist: Ths folder will contain our bundle and the files needed to run the application.
- node_modules: The folder in which all our dependencies will be installed
- shapefiles: The folder containing the shapefiles (This is optional)
- src: The folder contains all the files for the development of our application.
- static: The content of this folder will be copied automatically to our dist folder on
npm start
ornpm run build
. - index.js: The entrypoint of the application.
- static: The content of this folder will be copied automatically to our dist folder on
- package.json: The package.json we previously created.
- webpack.config.js: webpack configuration for development.
Copy the GeoJSON previously created into the static folder.
Create an index.html file in the static folder.
Use the following template to populate our index.html:
|
|
Our html has some important parts:
- Requiring leaflet.css: We are calling the leaflet CSS so our maps can be nicely rendered.
- Adding simple CSS: We will include a little bit of CSS in our header in order to avoid the creation of another file just to make simple style modifications.
- Create container div: We create a div with id ‘geojson__container’ which will be the container of our map.
- Requiring bundle.js: We are calling the bundle we will create in a bit.
Lets create a webpack.config.js
file so we can create our bundle (I assume you have a little webpack knowledge):
|
|
Lets add the following into our index.js file:
|
|
The previous snippet should render a map with tiles from Carto.
Now lets call our GeoJSON file with axios, in our webpack configuration we stated that our static files will be at the folder dist. So our bundle.js will be able to find our GeoJSON at ‘./uso.json’
We have our GeoJSON loaded, we can now use geojson-vt! Lets declare a function expression which will handle the creation of our index of tiles, lets import the geojsonvt module at the top of our file and create our tile index in the function expression body. In order to create the tile index you will only need to give as firt argument the GeoJSON and as second argument a options object. You can tweak the options object, but the defaults should work with the dataset we are using.
How To: Bing Maps Custom Tile Overlay – Google Maps, viewed 27 December 2016, http://blogs.microsoft.co.il/shair/2012/06/24/how-to-bing-maps-custom-tile-overlay-google-maps.
A layer is composed of a group of tiles, each tile will be a square of 256 pixels x 256 pixels. When we zoom a map each succeeding zoom level will divide the map into 4 N tiles, where N refers to the zoom level. The leaflet docs explain that in order to create a custom layer, we must extend GridLayer and implement the createTile() method, which will be passed a Point object with the x, y, and z (zoom level) coordinates to draw our tile.
If you inspect the html you can find how our tiles are being rendered, navigate to the div with class leaflet-tile-pane
. You will see that this node has two child nodes with class leaflet-layer
, the first child will be the container of the tiles from Carto. The second child will be our tiles, navigate the child nodes and try to understand whats happening, just remember that at the moment all the canvas we created are empty.
We will use the getTile
method of the geojson-vt tileIndex object so we can get the features that are within the tile we are fetching. Lets understand this a little bit better:
Remember that every tile we are creating is a canvas, which will allow us to draw on them with the Canvas API. Explaining how the canvas API works is not covered in this tutorial, you can check this tutorial if you want to deepen your knowledge in this subject. Lets create a simple example on how to draw a feature in our canvas:
Lets explain a bit of the code found in the jsfiddle. Each feature has a geometry, which is an array of arrays. Each element of the array has an array with two values a x and a y. We are using a sample geometry to illustrate how the canvas rendering will work. We then need to get the context of the canvas.
The canvas is initially blank. To display something, a script first needs to access the rendering context and draw on it. The
<canvas>
element has a method called getContext(), used to obtain the rendering context and its drawing functions. getContext() takes one parameter, the type of context. For 2D graphics, such as those covered by this tutorial, you specify “2d” to get a CanvasRenderingContext2D.
When we have the context of the canvas we can start modifying it:
Dobot: Robotic Arm for Everyone! Arduino & Open Source, viewed 27 December 2016, https://www.kickstarter.com/projects/dobot/dobot-robotic-arm-for-everyone-arduino-and-open-so.
In order to keep it simple we will create a mapping for our variables and colors, the logic behind the color will be your choice.
|
|
Now lets really dig into the tile creation!
|
|
You should get something like this:
#Bundling
Ok, so we have everything we need, we just need to bundle our project. Run npm run build
|
|
That’s it, you now can watch your dataset in your browser. I hope you find this useful, if you have any doubts you can ask anything at my twitter account @torresomar