DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

3 2

Open Source Adventures: Episode 32: Better Russian Tank Losses Graphs with D3 and Svelte

Before we answer the question when Russian will run out of tanks, let's refactor and improve our code a bit.

We want to achieve the following:

  • put awkward axis code in separate component
  • extend tank axis slightly so it ends up on a round number
  • change ticks on date axis, so they start on Feb 24
  • add trend line for overall data
  • reduce dead space around the graph

src/Axis.svelte

This component doesn't do any logic, it just bridges D3 DOM manipulation with Svelte, so we have less awkward code in the main component:

<script>
import * as d3 from "d3"
export let axis

let axisNode

$: {
  d3.select(axisNode).selectAll("*").remove()
  d3.select(axisNode).call(axis)
}
</script>

<g bind:this={axisNode}></g>
Enter fullscreen mode Exit fullscreen mode

Rounded numbers on Y scale

We don't need any special code for this, as D3 already comes with a convenient .nice() function that pads domain on both sides to nearest round number. As 0 is already as round as it gets, it will only pad a bit on the top, in this case to 700:

let yScale = d3.scaleLinear()
  .domain(d3.extent(data, d => d.tank))
  .nice()
  .range([500, 0])
Enter fullscreen mode Exit fullscreen mode

Date formatting on X scale

This is a bit more complicated. The scale is fine, and we do not want to pad it with extra days. It should start Feb 24, and end at the last day we have data for.

We can solve that by manually telling D3 where to place ticks (with .ticks(d3.timeThursday) - as Feb 24 is Thursday), and then how to format them (with .tickFormat(d3.timeFormat("%d %b")))

This isn't perfect, as today doesn't have its own tick, but it will do for now.

let xAxis = d3.axisBottom()
  .scale(xScale)
  .ticks(d3.timeThursday)
  .tickFormat(d3.timeFormat("%b %d"))
Enter fullscreen mode Exit fullscreen mode

Trend line

Nothing fancy here, just connect first and last datapoint with dashed line.

let trendPathData = d3.line()
  .x(d => xScale(d.date))
  .y(d => yScale(d.tank))
  ([data[0], data.at(-1)])
Enter fullscreen mode Exit fullscreen mode

src/Graph.svelte

And here's the complete src/Graph.svelte. All other files are unchanged from previous episode:

<script>
import * as d3 from "d3"
import Axis from "./Axis.svelte"

export let data

let xScale = d3.scaleTime()
  .domain(d3.extent(data, d => d.date))
  .range([0, 700])

let yScale = d3.scaleLinear()
  .domain(d3.extent(data, d => d.tank))
  .nice()
  .range([500, 0])

let pathData = d3.line()
  .x(d => xScale(d.date))
  .y(d => yScale(d.tank))
  (data)

let trendPathData = d3.line()
  .x(d => xScale(d.date))
  .y(d => yScale(d.tank))
  ([data[0], data.at(-1)])

let xAxis = d3.axisBottom()
  .scale(xScale)
  .ticks(d3.timeThursday)
  .tickFormat(d3.timeFormat("%b %d"))
let yAxis = d3
  .axisLeft()
  .scale(yScale)
</script>

<h1>Russian Tank Losses</h1>
<svg>
  <g class="graph"><path d={pathData}/></g>
  <g class="trendline"><path d={trendPathData}/></g>
  <g class="x-axis"><Axis axis={xAxis}/></g>
  <g class="y-axis"><Axis axis={yAxis}/></g>
</svg>

<style>
svg {
  height: 600px;
  width: 800px;
}
.graph {
  transform: translate(50px, 20px);
}
.graph path {
  fill: none;
  stroke: red;
  stroke-width: 1.5;
}
.trendline {
  transform: translate(50px, 20px);
}
.trendline path {
  fill: none;
  stroke: red;
  stroke-width: 1.5;
  stroke-dasharray: 3px;
}
.x-axis {
  transform: translate(50px, 520px);
}
.y-axis {
  transform: translate(50px, 20px);
}
</style>
Enter fullscreen mode Exit fullscreen mode

Story so far

All the code is on GitHub.

I deployed this on GitHub Pages, you can see it here.

Coming next

In the next episode, we'll add some functionality to the app. The end goal is to try to figure out how long until Russia runs out of tanks, but that might take longer than an episode.

Image of AssemblyAI tool

Challenge Submission: SpeechCraft - AI-Powered Speech Analysis for Better Communication

SpeechCraft is an advanced real-time speech analytics platform that transforms spoken words into actionable insights. Using cutting-edge AI technology from AssemblyAI, it provides instant transcription while analyzing multiple dimensions of speech performance.

Read full post

Top comments (0)

Image of Docusign

πŸ› οΈ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

πŸ‘‹ Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay