Tag Archives: julia,visualization

COVID-19 Visualization

COVID-19 Visualization

This is a special blog post but after having a major decision about how to compute bounds yesterday for the ConstraintSolver I promise the next post will be
one about the ConstraintSolver again.

Based on the recent events I wanted to create a visualization about the COVID-19 virus to play around with the publicly available data a little bit.
I’m a huge fan of data visualization especially on maps.

First of all we need to get the data and then I want to visualize the number of total cases over time. It can be easily modified to only show active cases or what I’m also interested in is the number of cases per 100,000 people. I’m planning on updating the visualization at the end of the post regularly when new data arrives.

Getting the data

Most people probably look at this map to check the current status but I’m not a huge fan of the circles and wanted to create an actual overlay over the country as it’s quite easy to get the shapefiles of the countries but the ones of the provinces will take more time/effort πŸ˜€

They use the data from this repo which I also use.

Corona cases

The first step after downloading the data:

download("https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Confirmed.csv", "covid.csv")

is to combine all cases from Mainland China into one row and the same for all other countries of course.

The first step is to remove some columns we don’t need and a simple renaming of the Country/Region column.

using DataFrames, CSV
function summarize_data()
    df = CSV.read("covid.csv"; copycols=true)
    select!(df, Not(Symbol("Province/State")))
    select!(df, Not(:Lat))
    select!(df, Not(:Long))
    rename!(df, Symbol("Country/Region") => :Country)
end

Later I also don’t have a shapefile for some countries one of them is Hong Kong which I count as China here. Hopefully that’s okay for this visualization…

for row in eachrow(df)
    if row[:Country] == "Hong Kong"
        row[:Country] = "Mainland China"
    end
end

Next step is to sum up the cases grouped by :Country which can be done using:

adf = aggregate(df, :Country, sum)

I also renamed some country names to work with the shapefiles later and renamed the dates to my preferred format πŸ˜€

dates = names(adf)[2:end]
for date in dates
    col_name = string(date)[1:end-4]
    parts = split(col_name, "/")
    col_name = "$(parts[2]).$(parts[1]).$(parts[3])"
    rename!(adf, date => Symbol(col_name))
end
for row in eachrow(adf)
    if row[:Country] == "Mainland China"
        row[:Country] = "China"
    elseif row[:Country] == "US"
        row[:Country] = "United States"
    elseif row[:Country] == "UK"
        row[:Country] = "United Kingdom"
    end
end
CSV.write("summarized.csv", adf)

Now we have a csv file with a column for each date and the number of cases for that date per country.

Visualization

I’m using another Julia plotting library after using Plots.jl and tried out Makie.jl for others. This time I use Luxor.jl as they have an example of a world map πŸ˜‰

Let’s include all libraries we need first

using Shapefile, Luxor
using DataFrames, CSV
using ColorSchemes
using Dates

include(joinpath(dirname(pathof(Luxor)), "readshapefiles.jl"))

I’m not a huge fan of the include() but that’s what was used in the documentation of Luxor so maybe…

B-splines

About three month ago I published my post about Bézier curves and mentioned a follow up post about B-splines. Here it is πŸ˜‰
I finished the course on Geometric Modelling and Animation in university so I thought it’s time now. Maybe there will be also a post on surfaces.

The blog post contains:

  • An explanation of B-splines
  • How to draw B-splines with different techniques
    • Normal way
    • De Boor Algorithm
  • Animations using Julia

What are B-splines?

The idea of splines in general is to combine several curves together to obtain one single curve. The curve itself should have some nice properties like continuity and it shouldn’t behave in a strange way like making weird curves as a polynomial function of degree 15 might do. Another important point is local control which means if we move one control point a little bit we don’t want to change the whole curve but only the small area around the point. For Bézier curves for example the whole curve can be changed even though the curve is changed mostly around the point that moved.

Additionally having a convex hull property is quite useful for collision detection as checking directly against the curve is quite complex which means it takes time whereas checking whether an object is inside a convex hull is easy. Later the tests can be refined if it at least has the possibility of being part of a collision with the actual curve.

In comparison to Bézier curves we gain local control, a better convex hull property and we can control the degree of the curve by combining curves together. Compared to Bézier splines we don’t have to worry about A-frames anymore which was the last part of the previous post.

Let’s have a look at a specific example:

Bezier spline vs B-spline

I have drawn this using my Bézier curve code of the last post and simple \(C^1\) continuity but for \(C^2\) this would be harder. In this example you can see the normal bezier points \(b_i\) and the so called de Boor points \(d_i\) which given the knot vector which I roughly explained last time (and the continuity) perfectly define the curve and the inner points \(b_2,b_4, b_6\) are defined implicitly by this.

I’ll come back to the Knot vector later which defines the continuity and whether it is interpolating or approximating the point. For the comparison to Bézier splines it should be end point interpolating and approximating everywhere else which means that the curve touches \(d_0\) and \(d_{end}\) but not the ones in between.

I think we need a bit more definitions for the next steps:

We have \(m+1\) de Boor points, \(d_0,\dots,d_m\) which means in the example above \(m=5\)
and degree \(n\) and the Knot Vector \(\mathbf{T} = (u_0, \dots, u_{n+m+1})\)

The spline is defined by:

\[
s(t) = \sum_{i=0}^{m} \mathbf{d}_iN_i^n(t)\]

where \(N_i^n(t)\) are the B-spline basis functions which are defined by:

\[
N_i^0(t) = \begin{cases}
1 & \text{if } u_i \leq t

\[
N_i^r(t) = \frac{t-u_i}{u_{i+r}-u_i}N_i^{r-1}(t) +
\frac{u_{i+1+r}-t}{u_{i+1+r}-u_{i+1}} N_{i+1}^{r-1}(t) \quad 1…

BΓ©zier curves in Julia with animations

I think many heard about Bézier curves but maybe some of you didn’t and I heard about it but wasn’t really sure what they are and how they work.
During my Geometric Modelling and Animations course in university we had some lectures on it and I did some coding for homeworks and also to understand a bit more about it. During the last days I published my animations on Twitter and asked whether you’re interested in a post. There was quite a bit feedback on that so here it is!

Let us start with a basic Bézier curve:

simple Bézier curve

Before I show you what Bézier curves are we should probably have a short look what a basic curve is. A curve can be described by a parameterized description as:

\[
\mathbf{b}(t) = (x(t),y(t))^T \quad t_1 \leq t \leq t_2\]

whereas \(\mathbf{b}\) is a vector and \(x, y\) are polynomial functions like

\[
x(t) = a_0 + a_1t + a_2t^2 + \dots + a_nt^n\]

Now the idea is to compute the values on the curve from \(t_1 = 0\) to \(t_2 = 1\) using a different basis not simply \(t^0, \dots, t^n\).
For Bézier curves this basis are defined as:

\[
B_{i}^{n}(t) :=\left( \begin{array}{c}{n} \\ {i}\end{array}\right) t^{i}(1-t)^{n-i}, \quad 0 \leq i \leq n\]

and is called Bernstein basis.

Which looks a bit random for now but they have some interesting properties:

  • \(B_{0}^{n}(t)+B_{1}^{n}(t)+\cdots+B_{n}^{n}(t)=1\)
  • $B_0^n(0) = 1, B_n^n(1)=1

which means when we write down the complete formula to compute \(\mathbf{b}(t)\):

\[
\mathbf{b}(t) = \sum_{i=0}^n B_i^n \mathbf{b}_i\]

where \(\mathbf{b}_i\) are our control points. Now the second property just means that \(\mathbf{b}(0) = \mathbf{b}_0\) and \(\mathbf{b}(0) = \mathbf{b}_n\)

I think it would be nice to actually see the basis functions:

Bernstein basis

Now we can combine it with our control points to obtain:

Bézier using Bernstein basis

Besides the change in color you probably don’t see a difference to the first plot even though that was plotted differently.

I know some of you are here for code so I’ll show you the bernstein code:

using Plots
# for the LaTeX labels in the legend
using LaTeXStrings 

function compute_bernstein(i,n; steps=100)
    return [binomial(n,i)*t^i*(1-t)^(n-i) for t in LinRange(0,1,steps)]
end

function compute_bernstein_poly(px,py; steps=100)
    n = length(px)-1
    bernsteins = [compute_bernstein(i,n) for i=0:n]
    x_vals = [sum(px[k]*bernsteins[k][t] for k=1:n+1) for t=1:steps]
    y_vals = [sum(py[k]*bernsteins[k][t] for k=1:n+1) for t=1:steps]
    return x_vals, y_vals
end

function plot_with_bernstein(px,py; steps=100, subplot=1)
    x_vals, y_vals = compute_bernstein_poly(px,py; steps=steps)
    plot!(x_vals, y_vals, color=:blue, label="",subplot=subplot)
end

function main()
    px = [0, 3, 7]
    py = [2, 9, 3]

    plot(;size=(700,500), axisratio=:equal, legendfont=font(13))
    plot!(px, py, linetype=:scatter, label="control points")
    plot_with_bernstein(px,py)
    png("using_bernstein")
end

Pretty basic so far. Now combining the two and animate:

Bernstein gif

Actually I’m not too sure about how to interpret the colored animating part on the lower plot with the different bernstein polynomials but I think it looks interesting and I never saw that before. The three dots red, green and blue some up to the black in both ways.

Anyway I think the interesting things are still missing. First of all we only have 3 control points at…