Libove Blog

Personal Blog about anything - mostly programming, cooking and random thoughts

Moved owl-blogs to GitHub

Owl-blogs has reached a certain level of maturity and I think it is stable enough to be used by other people. As I have a lot of articles on my blog any future changes have to be compatible or need an automated adjustment, even if I'm the only person using the software.

If you like to try out owl-blogs or even contribute to the project, the main location of the code is now on GitHub.

H4kor/owl-blogs


Rotkehlchen

Rotkehlchen Ein Rotkehlchen sitzt auf einer verzierten Stange

Frog 2

Frog 2 A frog sitting in a pond and croaking

Frog 1

Frog 1

Crescent Moon

Crescent Moon

Calculating the complementary color in CSS

I'm currently rebuilding the design of my blog and want to use a complementary color palette. As I'm not yet sure which colors I will use it would be ideal to automatically derive the secondary color from the primary color.

In the future this can be done using the relative color feature coming to CSS, but this isn't yet widely supported.

    --primary: hsl(170, 66%, 28%);
    --secondary: hsl(from var(--primary) calc(h + 180) s l);

I figured out that you can achieve the same effect using the color-mix. This is already supported by all major browsers.

    --primary: hsl(170, 66%, 28%);
    --secondary: color-mix(in hsl longer hue, var(--primary), var(--primary) 50%);

The command mixes the primary color with itself in the HSL color space. Additionally it is specified that it should use the longer pass along the hue axis. As this will always be the 360° path, a mix of 50% will end up at the 180° complementary color to the primary color. The saturation and lightness will stay the same.

This allows for fast testing of colors using the developer console:

Choosing a primary color automatically adjusts the secondary color.

This can also be used to create other color palettes by changing the percentage accordingly.


Jukebox

Jukebox

Visualizing Data on Maps using matplotlib and geopandas

For visualizing the heating and cooling degree days of Germany I had to learn how to plot maps.

First Look

My data source was a bunch of CSV files, which I combined into a single DataFrame, including latitude and longitude columns. I've used geopandas to plot this data for a first visualization. The DataFrame can be turned into a GeoDataFrame to create geometry information from the longitude and latitude columns. It's important to specify the right coordinate reference system (CRS) of your data.

With this conversion the data can be plotted using the plot function of geopandas

df = load_my_dataframe(...)
gdf = gpd.GeoDataFrame(
    df, geometry=gpd.points_from_xy(df["Longitude"], df["Latitude"]), crs="EPSG:4326"
)
ax = gdf.plot(
    column="ValueToVisualize",
)

Simple visualization of geography data using points

Adding Boundaries

The shape of Germany is already recognizable, but some more context would be beneficial. For this reason I've added the borders of the German states to the plot. The shape files of all European countries can be downloaded from eurostat in various formats. I've used the "Polygon (RG)" version and the .shp format. The files contain the borders on different levels (country, state and regional borders). With a few lines the borders can be added to the plot.

border_file = "NUTS_RG_01M_2021_3035.shp/NUTS_RG_01M_2021_3035.shp"
eu_gdf = gpd.read_file(border_file)
eu_gdf.crs = "EPSG:3035"
gdf_de = eu_gdf[(eu_gdf.CNTR_CODE == "DE") & (eu_gdf.LEVL_CODE == 1)]
gdf_de.to_crs("EPSG:4326").boundary.plot(ax=ax, color="black")

visualization of geography data using points and state borders

Filling the Map

Using points to visualize this data is not the best approach. Instead I want to fill the entire map where each pixel is colored according to the nearest data point. This can be achieved by computing a Voronoi Diagram, where each data point is turned into a face. I've tried to use the geoplot library to compute the voronoi diagram, but it got stuck on my data. Luckily scipy also has a voronoi implementation, but it's a bit more work to use.

# create a box around all data
min_lat = df["Latitude"].min() - 1
max_lat = df["Latitude"].max() + 1
min_lon = df["Longitude"].min() - 1
max_lon = df["Longitude"].max() + 1
boundarycoords = np.array(
    [
        [min_lat, min_lon],
        [min_lat, max_lon],
        [max_lat, min_lon],
        [max_lat, max_lon],
    ]
)
# convert our data point coordinates to a numpy array
coords = df[["Latitude", "Longitude"]].to_numpy()
all_coords = np.concatenate((coords, boundarycoords))
# compute voronoi
vor = scipy.spatial.Voronoi(points=all_coords)
# construct geometry from voronoi
polygons = [
    shapely.geometry.Polygon(vor.vertices[vor.regions[line]])
    for line in vor.point_region
    if -1 not in vor.regions[line]
]
voronois = gpd.GeoDataFrame(geometry=gpd.GeoSeries(polygons), crs="EPSG:4326")
# create dataframe
gdf = gpd.GeoDataFrame(
    df.reset_index(),
    geometry=voronois.geometry,
    crs="EPSG:4326",
)

# plot borders
gdf_de.to_crs("EPSG:4326").boundary.plot(ax=ax, color="black")
# clip geometry to inside of map
clip = eu_gdf[(eu_gdf.CNTR_CODE == "DE") & (eu_gdf.LEVL_CODE == 0)]
gdf = gdf.clip(clip.to_crs("EPSG:4326"))
# plot data
gdf.plot(
    ax=ax,
    column="Monatsgradtage",
    cmap="Blues",
    legend=True,
)

Visualization using Voronoi diagram

Making it beautiful

The plot is almost done, I only want to clean it up a bit. As the axis don't serve any purpose here, I removed them. Additionally I've added a title and a small text to the bottom to include the source in the graphic.

ax.set_title(f"Heizgradtage 2020", fontsize=20)
ax.axis("off")
ax.text(
    0.01,
    0.01,
    "Quelle: https://www.dwd.de/DE/leistungen/gtz_kostenfrei/gtz_kostenfrei.html\nVisualisierung: Niko Abeler CC-BY-SA 4.0",
    ha="left",
    va="top",
    transform=ax.transAxes,
    alpha=0.5,
    fontsize=8,
)

Final Visualization


Visualisierung: Heizgradtage und Kühlgradstunden Deutschland

Heizgradtage werden im Energiemanagement benutzt. Um den Heizbedarf über mehrere Jahre zu vergleichen muss der Effekt des Wetters herausgerechnet werden. Ansonsten vergleicht man jediglich das Wetter, da in kalten Wintern mehr geheizt werden muss. Dazu zählt man die Grad Celsius unter einem Schwellwert (meist 15°C in Deutschland).

Als Beispiel: Ein Tag mit konstanter Temperatur von 5°C ergibt 10 Heizgradtage ((15°C-5°C) * 1 Tag). Ein Monat mit 10°C ergibt 300 Heizgradtage ((15°C-5°C) * 30 Tage)

Die Visualisierung zeigt die Heizgradtage der Jahre 2010 - 2023 in Deutschland. Je kälter (mehr Heizgradtage) es in einer Region ist, desto dunkler wird diese dargestellt. Die Visualisierung basiert auf den Daten des DWD.

Parallel zu den Heizgradtagen gibt es auch die Kühlgradstunden. Hier werden die Stunden über einem Schwellwert (hier 18°C) gezählt. Eine Stunde mit 30°C ergibt somit 12 Kühlgradstunden. In dieser Visualisierung werden heiße Regionen in dunklerot dargestellt.


Valley of Dunes

Valley of Dunes