We use here: transitive joins, geoJSON data, granularity.
This example is about data visualization on maps. I used Mapbox API as a map tool, Cube.JS to retrieve data and PostgreSQL as a database. If you’re not familiar with Cube.JS yet, please visit Cube JS Sample React app tutorial before continuing this tutorial.
As the start point I used a sample Cube.JS React app.
In this example there are two independent parts:
- How to render a choropleth layer based on pandemic outbreak data.
- How to render heatmap and cluster layers based on points of interest and accommodation sites in Paris.
To create the mapbox__example
database, please, use the following commands.
$ createdb mapbox__example
$ psql --dbname mapbox__example -f mapbox-dump.sql
Next, install Cube.js CLI if you don’t have it already and generate a new application.
$ npm install -g cubejs-cli
$ cubejs create mapbox-data -d postgres
Cube.js uses environment variables for configuration. To configure the connection to our database, we need to specify the DB type and name. In the Cube.js project folder, replace the contents of the .env file with the following:
CUBEJS_API_SECRET = SECRET;
CUBEJS_DB_TYPE = postgres;
CUBEJS_DB_NAME = mapbox__example;
CUBEJS_DB_USER = postgres;
CUBEJS_DB_PASS = postgres;
Now, start the development server and open the localhost:4000 in your browser.
$ npm run dev
I used this React Mapbox wrapper. You can find some other Mapbox plugins for React, Angular and other frameworks at Mapbox Documentation.
To visualize those data I used three tables:
stats
– table with outbreak statistics: daily cases by countrymapbox
– service table, that contains country names, shortened names, iso codes etc. To join it withstats
, we usedstats.countryandterritorycode
andmapbox.iso_a3
mapbox__coords
– service table that contains polygon coordinates. Some countries have continues borders, others like USA with the region of Alaska and Hawaii or the islands of Japan have two or many separate polygons. To joinmapbox
withmapbox__coordinates
we used columniso_a3
.
I implemented dynamic data visualization: data rendering on the map depends on the date that can be changed using the slider under the map.
For performance reasons and to render the map faster I used only three database requests on page loading:
- one to define the start and the end date from our dataset.
- one to load data and to create an object with geoJSONs by date at component state
- one to load total cases data by date
Here is the query for the second request:
measures: ['stats.total'],
dimensions: [
'stats.countryterritorycode',
'mapbox.name',
'mapbox__coords.coordinates'
],
timeDimensions: [
{
dimension: 'stats.date',
granularity: 'day'
}
],
limit: 20000,
In this query we used Transitive Joins: so we didn’t need to join stats
table with mapbox__coords
table directly.
We joined stats
table with mapbox
table and thenmapbox
table with mapbox__coords
table.
To get data for every day between the start and the end date we set granularity as ‘day’.
Due to large amount of data rows: amount of days, countries and polygons we needed to set up the limit field, because I expected more rows that are provided by default.
After we got results we created an object with a date as a key and a geojson as a value.
Depending on the chosen date we used a geoJSON as a Source for Fill Layer at the map.
We used the same process to display a total cases amount at the top right corner of the map.
Here is the query to get total cases by day:
measures: ['stats.total'],
timeDimensions: [
{
dimension: 'stats.date',
granularity: 'day'
}
]
- Documentation for fill layer, its parameters and settings
- Choropleth layer example at Mapbox.Documentation
We needed two tables to implement this example:
paris__poi
– table that containts data about Paris sightseeing points with rating. Heatmap layer in this example based on rating data.paris__accomodation
– table that contains data about Paris hotels etc.
Here is the query to get data for the heatmap layer:
{
dimensions: [
'paris__poi.name',
'paris__poi.rating',
'paris__poi.lat',
'paris__poi.lng'
],
}
We needed to set up the heatmap layer:
- at
heatmap-weight
we defined the fieldproperty.rating
as a base for the heatmap layer - at
heatmap-color
we created a palette for our heatmap
Here you can find more information about heatmap layers:
- What does mean expressions [‘interpolate’,[‘linear’] …]
- Heatmap layer example at Mapbox.Documentation
- Heatmap layers params descriptions
- Some theory about heatmap layers settings, palettes
We needed to create three layers to implement clusters:
- one for circle rendering
- one for digits rendering
- one for unclustered points
Similar to previous examples we created a geoJSON with points and set it as a source data for these layers.
Here you can find more information about cluster layers:
If you have any feedback or questions about this tutorial or about Cube.js in general — feel free to use our Slack Cube.js community or drop an email to [email protected]