We are going to add a D3.js bar chart that will interact with our geospacial data.
The Building a Geospacial App tutorial uses a data visualization library called React-Vis. Apparently React and D3 do not work together very easily, so you will see some data visualization libraries that combine React and D3 to make it easier for developers to incorporate D3 into a React app. Since we are using Svelte we don't have that problem and we can use D3 (along with all of its power and customizability) directly.
Svelte and D3 work really well together. However, to optimize the relationship between Svelte and D3 it is important to understand some basic concepts about these two great pieces of software. Please read this (short) post titled Making Visualizations Literally with Svelte & D3 and then come back here.
Update the processData()
function
In your index.svelte
file, find the processData()
function and update it to look like this:
function processData() {
let data = taxiData.reduce(
(accu, curr) => {
let pickupHour = new Date(curr.pickup_datetime).getUTCHours();
let dropoffHour = new Date(curr.dropoff_datetime).getUTCHours();
let pickupLongitude = Number(curr.pickup_longitude);
let pickupLatitude = Number(curr.pickup_latitude);
if (!isNaN(pickupLongitude) && !isNaN(pickupLatitude)) {
accu.points.push({
position: [pickupLongitude, pickupLatitude],
hour: pickupHour,
pickup: true
});
}
let dropoffLongitude = Number(curr.dropoff_longitude);
let dropoffLatitude = Number(curr.dropoff_latitude);
if (!isNaN(dropoffLongitude) && !isNaN(dropoffLatitude)) {
accu.points.push({
position: [dropoffLongitude, dropoffLatitude],
hour: dropoffHour,
pickup: false
});
}
let prevPickups = accu.pickupObj[pickupHour] || 0;
let prevDropoffs = accu.dropoffObj[dropoffHour] || 0;
accu.pickupObj[pickupHour] = prevPickups + 1;
accu.dropoffObj[dropoffHour] = prevDropoffs + 1;
return accu;
},
{
points: [],
pickupObj: {},
dropoffObj: {}
}
);
data.pickups = Object.entries(data.pickupObj).map(([hour, count]) => {
return { hour: Number(hour), x: Number(hour) + 0.5, y: count };
});
data.dropoffs = Object.entries(data.dropoffObj).map(([hour, count]) => {
return { hour: Number(hour), x: Number(hour) + 0.5, y: count };
});
taxiDataset = data;
}
Now find the pointsArray
declaration and replace this:
let pointsArray = [];
...with this:
let taxiDataset = {
points: [],
pickups: [],
pickupObj: {},
dropoffs: [],
dropoffObj: {}
};
Now find the renderLayers
reactive property:
$: renderLayers({ data: pointsArray, settings: settings });
...and update it to look like this:
$: renderLayers({ data: taxiDataset.points, settings: settings });
It looks like there is a lot going on in the processData()
function. It is just formatting the taxiData
into an object that is shaped like the taxiDataset
object that you used to replace the pointsArray
variable.
- The
points
property holds the scatterplot data, which will show how the distance and time of the trips are correlated. - The
pickupObj
property holds the number of pickups by hour. - The
pickups
property holds the x and y values of the pickup bars in the bar chart. - The
dropoffObj
property holds the number of dropoffs by hour. - The
dropoffs
property holds the x and y values of the dropoff bars in the bar chart.
Create the bar chart container
Create a new file inside your /src/components
directory and name it BarChart.svelte
. Paste the following code inside:
<div class="bar-chart">
<h2>Pickups by hours</h2>
</div>
<style>
.bar-chart {
position: absolute;
left: 10px;
bottom: 10px;
z-index: 100;
width: 500px;
height: 210px;
padding: 10px;
background-color: white;
border-radius: 3;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
font-size: 12px;
line-height: 1.833;
}
</style>
Now in your index.svelte
file, import that BarChart
component:
import BarChart from "$/components/BarChart.svelte";
...and include it inside the <div>
tag in your HTML as the last element/component in the <div>
tag, like this:
<div class="deck-container">
{#if hover.hoveredObject}
<div class="tooltip" style:transform={`translate(${hover.x}px, ${hover.y}px)`}>
<div>{hover.label}</div>
</div>
{/if}
<MapStylePicker currentStyle={mapStyle} on:change={handleStyleChange}/>
<LayerControls {settings} controls={CONTROLS} on:layerSettingsChange={updateLayerSettings} />
<div id="map" bind:this={mapElement}></div>
<canvas id="deck-canvas" bind:this={canvasElement}></canvas>
<BarChart />
</div>
Make sure to save your files and refresh your browser, if necessary, to see the box for your bar chart.
Create the bar chart
First, we are going to create a simple bar chart of pickups by hour.
To do this, we are going to use the taxiDataset.pickups
variable we prepared above. This is an array of objects of the form {hour: 0, x: 0.5, y: 246}
. Each x
is going to be the x-coordinate of the corresponding bar and each y
is going to be y-coordinate of the corresponding bar for the pickups we want to plot.
We are going to create the bar chart by coding SVG elements, which are similar to HTML elements, directly into the HTML portion of our BarChart.svelte
component.
In your BarChart.svelte
file, update the HTML as follows:
TODO: Finish the rest of this tutorial.
Top comments (1)
Hi @samuelearl code blocks are still blank files!