DEV Community

Samuel Earl
Samuel Earl

Posted on

A Basic Chart - Building a Geospacial App with SvelteKit, Deck.gl, and Mapbox - Part 4

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;
}
Enter fullscreen mode Exit fullscreen mode

Now find the pointsArray declaration and replace this:

let pointsArray = [];
Enter fullscreen mode Exit fullscreen mode

...with this:

let taxiDataset = {
  points: [],
  pickups: [],
  pickupObj: {},
  dropoffs: [],
  dropoffObj: {}
};
Enter fullscreen mode Exit fullscreen mode

Now find the renderLayers reactive property:

$: renderLayers({ data: pointsArray, settings: settings });
Enter fullscreen mode Exit fullscreen mode

...and update it to look like this:

$: renderLayers({ data: taxiDataset.points, settings: settings });
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Now in your index.svelte file, import that BarChart component:

import BarChart from "$/components/BarChart.svelte";
Enter fullscreen mode Exit fullscreen mode

...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>
Enter fullscreen mode Exit fullscreen mode

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.


Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
sheecegardezi profile image
Syed Sheece Raza Gardezi

Hi @samuelearl code blocks are still blank files!