<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>mlthlschr</title>
    <link>https://hlschr.de/</link>
    <description>mlthlschr</description>
    
    <language>en-gb</language>
    
    
    
    
    <copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright>
    <lastBuildDate>Sat, 19 Apr 2025 19:33:11 +0000</lastBuildDate>
    
    <atom:link href="https://hlschr.de/" rel="self" type="application/rss+xml" />



    <item>
      <title>Integrating own map tiles into the blog</title>
      <link>https://hlschr.de/posts/tech/pmtiles-blog-integration/</link>
      <pubDate>Sat, 19 Apr 2025 19:33:11 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/tech/pmtiles-blog-integration/</guid>
      <description>&lt;p&gt;I added the &amp;ldquo;outdoor&amp;rdquo; category to this blog a few months ago. The main feature
is the possibility to read GPX files I recorded on outdoor trips. After reading
the GPX file, it is displayed on a map (via &lt;a href=&#34;https://leafletjs.com&#34;&gt;leaflet&lt;/a&gt;)
and some metadata is displayed. This is how the first try looked like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;before.png&#34; alt=&#34;The raster tile based map from an external source&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;maps-in-the-browser&#34;&gt;Maps in the browser&lt;/h3&gt;
&lt;p&gt;Maps in the browser usually work like this: a mapping library loads tiles for
specific geo areas and zoom levels on demand, normally from an external server,
and the library then renders/places the tiles on the map. A well-known example
is &lt;a href=&#34;https://openstreetmap.org&#34;&gt;openstreetmap&lt;/a&gt;, which uses tiles hosted by
openstreetmap.&lt;/p&gt;
&lt;p&gt;Hosting map tiles can lead to serious traffic. Imagine providing the possibility
to download a sheer infinite amount of PNGs, where a new download is triggered
on each pan or zoom on the map. That&amp;rsquo;s why tile hosters typically require
registration or have non-free or restricted usage policies. See
&lt;a href=&#34;https://wiki.openstreetmap.org/wiki/Raster_tile_providers&#34;&gt;here in the openstreetmap wiki&lt;/a&gt;
for examples.&lt;/p&gt;
&lt;h3 id=&#34;what-do-i-require&#34;&gt;What do I require?&lt;/h3&gt;
&lt;p&gt;One could argue that I could just select one of the &amp;lsquo;free&amp;rsquo; services, but those
normally fall into the &amp;lsquo;usage policy&amp;rsquo; category. I simply do not want to bother
great open-source projects with traffic from my personal blog so that they can
use their funds for better purposes. Another point is the possibility of
styling - the free tiles are usually raster tiles, which are prerendered PNGs
already styled for you. If you do not like the style provided, you have to look
elsewhere. Typically, that leads to another, non-free service.&lt;/p&gt;
&lt;p&gt;In software engineering, the
&lt;a href=&#34;https://en.wikipedia.org/wiki/Software_requirements&#34;&gt;requirements&lt;/a&gt; play an
important role in the success of a project. They basically define how the system
should behave, which in turn should be based on the user requirements for this
application.&lt;/p&gt;
&lt;p&gt;So what are my requirements?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I want a cool style for the map.&lt;/li&gt;
&lt;li&gt;I want to host the map without much hassle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That could not be too difficult, could it?&lt;/p&gt;
&lt;h3 id=&#34;protomaps-to-the-rescue&#34;&gt;Protomaps to the rescue&lt;/h3&gt;
&lt;p&gt;Some time ago, I read about a mapping file format that boils down a vector map
dataset into a single file. Doing some research, I found it again - its name was
pmtiles.&lt;/p&gt;
&lt;p&gt;This file type is part of the &lt;a href=&#34;https://protomaps.com/&#34;&gt;protomaps&lt;/a&gt; ecosystem. It
can integrate the whole world into a single file, which can directly be queried
from the browser. That means you can put the file onto your own server and query
it from your own page without much hassle (except using some additional
client-side JS library)! Also, as it supports vector format, I can define my own
styles for my map. This fulfills all my requirements!&lt;/p&gt;
&lt;h4 id=&#34;refining-the-dataset&#34;&gt;Refining the Dataset&lt;/h4&gt;
&lt;p&gt;Protomaps provides the daily (?) updated dataset for me to download, which I can
then use for my maps. There is one catch though - the file is almost 120GB. It
contains the whole world, of course it will be large&amp;hellip; But the way the data is
stored within the file makes it easy to split it up into geo-specific areas,
e.g., defined by a bounding box. You will still have a world outline view, but
as you zoom in, you recognize missing data at some places. But that&amp;rsquo;s okay, as I
exactly know where my outdoor tours are happening. Also, I do not need to
provide more data for anybody else.&lt;/p&gt;
&lt;p&gt;I found a great blog post by
&lt;a href=&#34;https://til.simonwillison.net/gis/pmtiles&#34;&gt;Simon Willison&lt;/a&gt; on his blog about
this. He provides a great description of his tinkering with the file format,
including where to get the data and how to crop it. Check it out for an in-depth
description!&lt;/p&gt;
&lt;p&gt;TLDR: use the &lt;a href=&#34;https://docs.protomaps.com/pmtiles/cli#extract&#34;&gt;pmtiles cli&lt;/a&gt; to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;load dataset from &lt;a href=&#34;https://maps.protomaps.com/builds/&#34;&gt;protomaps.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;limit the downloaded dataset to the bbox you know or found out on
&lt;a href=&#34;http://bboxfinder.com/&#34;&gt;bboxfinder.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;displaying-and-styling-the-dataset&#34;&gt;Displaying and styling the Dataset&lt;/h4&gt;
&lt;p&gt;Having downloaded the dataset, I needed to find out how to display it on the
leaflet map. That was not difficult; it just required integrating another
library for leaflet, called
&lt;a href=&#34;https://github.com/protomaps/protomaps-leaflet&#34;&gt;protomaps-leaflet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a very simple integration where you can select one of a few flavors for
the style of the basemap. For more info, check
&lt;a href=&#34;https://protomaps.com/blog/what-is-a-basemap/&#34;&gt;this blogpost&lt;/a&gt;. The cool thing
is you can choose one of the default flavors as your starting point and adapt
the styling of &lt;a href=&#34;https://docs.protomaps.com/basemaps/layers&#34;&gt;specific layers&lt;/a&gt; to
your liking.&lt;/p&gt;
&lt;p&gt;I changed some of the settings but didn&amp;rsquo;t delve too deep into it. This is how
the end result looks like now:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;after.png&#34; alt=&#34;The pmtiles-based map, purely self-hosted&#34;&gt;&lt;/p&gt;
&lt;p&gt;For now, the result is fine, but I will surely continue to improve the style.
When directly comparing it to the state before, I see tweaking the city labels,
changing zoom ranges for smaller streets, or adapting the map colors to the
blog&amp;rsquo;s color scheme, etc.&lt;/p&gt;
&lt;h3 id=&#34;deploying-the-tiles&#34;&gt;Deploying the tiles&lt;/h3&gt;
&lt;p&gt;The blog is basically a git repo with some files that are fed into hugo, which
then generates the pages per my theme and settings. Large binary files are not
great in git repos, and the pmtiles can be very large and very binary.&lt;/p&gt;
&lt;p&gt;I let an LLM write me a small script that does that for me so I can use it
locally to get the tile data. However, the main reason why I have the script is
so that it can by run in the CI, as I do not want to pollute the blogs git repo
with massive binaries. Now every time I push a change the CI will load and crop
the latest maps (I defined in a single json file) and deploy them together with
the rest of the blog. I just have to make sure that they are copied in the right
location on the server. Pretty neat!&lt;/p&gt;

&lt;details&gt;
  &lt;summary&gt;Show code&lt;/summary&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;encoding/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;flag&amp;#34;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Import the flag package for command line arguments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;s&#34;&gt;&amp;#34;os/exec&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;//todo: add a flag to specify the path of the pmtiles binary&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// Define command line flags&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;helpFlag&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;h&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Display help message&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;outputDir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;o&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;./static/tiles&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Output directory for .pmtiles files&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;pmtilesBinary&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;pmtiles&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Path to the pmtiles binary&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Parse the command line arguments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;helpFlag&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Usage: go run your_program_name.go [options] &amp;lt;path_to_json_file&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;\nOptions:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;PrintDefaults&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Print default flag usage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// Check if the JSON file path is provided&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NArg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Please provide the path to the JSON file as a command line argument.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Usage: go run your_program_name.go &amp;lt;path_to_json_file&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Use -h for help.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;filePath&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Arg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Get the first non-flag argument as the file path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// Read the content of the file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;jsonData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ReadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Error reading file: %v\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// Create a map to store the parsed JSON data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dataMap&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// Unmarshal the JSON data into the map&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Unmarshal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;jsonData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dataMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Error parsing JSON:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;// Use the output directory specified by the command line flag&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bbox&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dataMap&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Fetching %v.pmtiles with bbox %v\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bbox&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;outputFilePath&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%s/%s.pmtiles&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;outputDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;c1&#34;&gt;// Use the pmtilesBinary flag value to specify the command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pmtilesBinary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;extract&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;https://data.source.coop/protomaps/openstreetmap/v4.pmtiles&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;outputFilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;--bbox=&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bbox&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Error loading %v: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;

</description>
    </item>
    
    <item>
      <title>Another one bites the dust</title>
      <link>https://hlschr.de/posts/tech/another-one-bites-the-dust/</link>
      <pubDate>Sun, 13 Apr 2025 18:14:12 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/tech/another-one-bites-the-dust/</guid>
      <description>&lt;p&gt;Ever since I was doing longer bike tours I always had a very good companion with me.
Not only there, also on hiking tours they were accompanying me like a trusted old
friend. In the last years they have changed a bit and got more annoying than I knew
them, but as every good friend has their quirks, in the end the pros always outweighed
the cons.&lt;/p&gt;
&lt;p&gt;Once in a while I was wondering what would happen if the companion changed so drastically
that I could not bear the quirks anymore.
In the end I always told myself that they have been standing on the right side
all along and they assured me of that by promising not to change.&lt;/p&gt;
&lt;p&gt;Actually on one of my last trips I was wondering about that.
I wish I hadn&amp;rsquo;t.
The trusted companion decided to &lt;a href=&#34;https://secure.businesswire.com/news/home/20250320568940/en/Bending-Spoons-Acquires-komoot&#34;&gt;sell&lt;/a&gt;
&lt;a href=&#34;https://www.reddit.com/r/wandern/comments/1jg3c1m/komoot_wurde_verkauft&#34;&gt;their&lt;/a&gt; &lt;a href=&#34;https://escapecollective.com/how-komoot-lost-its-way/&#34;&gt;soul&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Komoot has been acquired by an Italian investor having a reputation for not necessarily
improving their products.
Do you still remember evernote? Yeah, exactly.
One of the first things they like to do is to lay off people.
Apparently that &lt;a href=&#34;https://escapecollective.com/how-komoot-lost-its-way/&#34;&gt;has already happened&lt;/a&gt;
for Komoot as well.
I am sorry for the people that created that great product.&lt;/p&gt;
&lt;p&gt;For the app, that most probably means it will only get worse from now on.
For me, it seems like that I have to prepare for a breakup.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Trip to North Sea</title>
      <link>https://hlschr.de/posts/outdoor/20250322-trip-to-north-sea/</link>
      <pubDate>Sun, 23 Mar 2025 18:00:00 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/outdoor/20250322-trip-to-north-sea/</guid>
      <description>&lt;h3 id=&#34;general&#34;&gt;General&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;22.03.25 (Saturday)
&lt;ul&gt;
&lt;li&gt;sunny - cloudy, 12C .. 13C, 25-40kn E&amp;rsquo;ly&lt;/li&gt;
&lt;li&gt;during night still windy, 3C-5C&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;23.03.25 (Sunday)
&lt;ul&gt;
&lt;li&gt;cloudy - overcast, 10C .. 11C, 20-30kn, NE&amp;rsquo;ly&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;saturday&#34;&gt;Saturday&lt;/h3&gt;
&lt;p&gt;The weekend before I wanted to go to Plön but due to a cold forecasted night I
postponed it to this weekend. As you can see, again this trip was not towards
Plön but in the opposite direction. What was the excuse this time?&lt;/p&gt;
&lt;p&gt;Again, the weather. Not the weather in the night but rather the weather at daytime.
Strong winds were forecasted from easterly directions, even a wind warning was
posted by the german met office. As Plön is to the east of Kiel, that would have
meant having beautiful gusts of 40kn directly in my face.&lt;/p&gt;
&lt;p&gt;I am not quite there yet to &amp;ldquo;weather&amp;rdquo; such situations by simply going, but I also
wanted to go out. So I did, but rather in the other direction, which also meant
having those winds to my favour now. At least Saturday&amp;hellip;&lt;/p&gt;
&lt;p&gt;So I went westward. After 2.5km I already decided to turn around, as I forgot
some equipment that was essential for the night. That mistake handsomely increased
my planned distance by about 7km. But as it was early in the day I simply went back
and proceeded after getting my stuff.&lt;/p&gt;
&lt;p&gt;The first part was through rural Kiel, then the more natural paths began near
Westensee. Almost the whole way to the beautiful wild moor south of Rendsburg was
in nature. After that, Passing through Jevenstedt, I reached the Kiel Canal (is
it still called like that in English, even near Rendsburg?), which led me 10km
westwards. With the wind in my back that was one hell of a ride, the speed making
up for the less interesting nature compared to before.&lt;/p&gt;
&lt;p&gt;The last part leading to Albersdorf was again dominated by agricultural planes,
just until right before the sleeping spot, which was on the edge of a forest.&lt;/p&gt;
&lt;p&gt;I checked the spot, planned for the night, bought some rations at Lidl in the
town of Albersdorf and started to set up the tent.&lt;/p&gt;
&lt;p&gt;At some point another hiker came along, an interesting guy from Passau who was
just five days into hiking the German part of E1 from Flensburg to Konstanz.
What a trip! We talked a bit about his tour, his gear, his life and his plans.
He had some interesting and inspiring things to tell.&lt;/p&gt;
&lt;h4 id=&#34;sleeping-spot&#34;&gt;Sleeping Spot&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&#34;https://www.naturschutz-sh.de/wildes-sh/trekkingplatz-gieselautal?embed=1&#34;&gt;sleeping spot&lt;/a&gt; was okay&lt;/li&gt;
&lt;li&gt;One tent can have a good spot, the others have to live with more slopy parts&lt;/li&gt;
&lt;li&gt;No table or bench available&lt;/li&gt;
&lt;li&gt;Lidl and Edeka available in town&lt;/li&gt;
&lt;li&gt;Toilets available in stone age museum, open from tuesdays to sundays&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;night&#34;&gt;Night&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A bit windy but not too cold&lt;/li&gt;
&lt;li&gt;Took some time to get warm feet but in the end everything was snuggly&lt;/li&gt;
&lt;li&gt;Had to turn around in the tent, which i set up in the direction of the wind (but
not in the direction of the slope&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;sunday&#34;&gt;Sunday&lt;/h3&gt;
&lt;p&gt;Lying in the tent I checked out where I could go to take the train back to Kiel.
Before departing, I though about going to Büsum. But after the exhausting but
fulfilling day I decided that I could make some more way on this Sunday. So in the
end I decided going to Husum, which is to the North of Albersdorf.&lt;/p&gt;
&lt;p&gt;The wind has turned a bit in northerly directions, which meant that I had the wind
from a less favourable direction than yesterday. Anyway, the trip was really nice,
mainly through agricultural areas in the beginning but later on more the classical
north sea marsh lands. It was quite cool to see how the landscape changes, even if
you only go 100km from Kiel to the West.&lt;/p&gt;
&lt;p&gt;Arrived in Husum I took the chance to see the north sea, but when I arrived it was
the time where the moon decided to retreat the north sea. So only intertidal mudflat&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;
and tidal creeks&lt;sup id=&#34;fnref1:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; as far as you could see, but that&amp;rsquo;s also what makes this part special!&lt;/p&gt;

&lt;details&gt;
  &lt;summary&gt;Notes to self/Stuff for nerds&lt;/summary&gt;
  &lt;h3 id=&#34;gear&#34;&gt;Gear&lt;/h3&gt;
&lt;p&gt;Almost same as the previous overnighter, only that&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vans shoes&lt;/li&gt;
&lt;li&gt;Thermal shirt instead of the trekking shirt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and I did not forget anything important (after going back once, haha).&lt;/p&gt;
&lt;h4 id=&#34;to-be-improved&#34;&gt;To be improved&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Down jacket was used throughout Sunday, as the wind was really chilly; so maybe a wind breaker would be nice to have&lt;/li&gt;
&lt;li&gt;next time take some tissues for the runny nose&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;

&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I like the German words &amp;ldquo;Watt&amp;rdquo; and &amp;ldquo;Priel&amp;rdquo; more as they sound less generic
and somehow more special for what they describe. It&amp;rsquo;s not just a mudflat! But
maybe thats comparable to languages where you have 18 different words for
Snow or something&amp;hellip;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref1:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Git workflow - fixing messed up commits</title>
      <link>https://hlschr.de/posts/git/fix-messed-up-commits/</link>
      <pubDate>Sun, 16 Mar 2025 15:00:49 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/git/fix-messed-up-commits/</guid>
      <description>&lt;p&gt;Sometimes you recognize that your change history is a bit of a mess.
One commit contains multiple different types of changes, possibly even some refactoring.&lt;/p&gt;
&lt;p&gt;In this post I describe my (current) workflow to handle that.
Git being git, this might not be the only one, possibly not even the best way to do it!&lt;/p&gt;
&lt;p&gt;First you need to know which commit is affected.&lt;/p&gt;
&lt;h3 id=&#34;last-commit&#34;&gt;Last commit&lt;/h3&gt;
&lt;p&gt;If you happen to remember that the disastrous commit was the one you just did, you are in luck, as the process is quite easy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git reset HEAD^&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;add the changes &lt;a href=&#34;https://hlschr.de/posts/git/git-clean-commits/&#34;&gt;cleaner&lt;/a&gt; this time&lt;/li&gt;
&lt;li&gt;commit as required&lt;/li&gt;
&lt;li&gt;optional: stash other changes to run build/run tests&lt;/li&gt;
&lt;li&gt;repeat until working tree clean&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;last-commit1&#34;&gt;Last commit~{&amp;gt;=1}&lt;/h3&gt;
&lt;p&gt;Old commits are a bit more tricky, as they require a rebase (you change the history).
If you have not pushed the changes or are able to force push, that should not be a problem.&lt;/p&gt;
&lt;h4 id=&#34;finding-out-the-commit&#34;&gt;Finding out the commit&lt;/h4&gt;
&lt;p&gt;First of all we need to find out which commit is affected and how we can reference it exactly.
Usually it is sufficient to simply &lt;code&gt;git log&lt;/code&gt; and find the hash, or at least an approximation of how many commits have happened since your critical change.
The three functions below are sufficient for the majority of the cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git log&lt;/code&gt; or &lt;code&gt;git log --oneline&lt;/code&gt; to find the commit by commit message only&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git log --stat&lt;/code&gt; to find the commit by additionally checking the files affected&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git log -p -- ${FILENAME}&lt;/code&gt; to find all commits with changes of &lt;code&gt;${FILENAME}&lt;/code&gt; (incl. diff)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, there are various different ways to describe a commit in git, but this is content for another post.&lt;/p&gt;
&lt;h4 id=&#34;changing-the-commit&#34;&gt;Changing the commit&lt;/h4&gt;
&lt;p&gt;Besides the rebase, we have the same workflow as for the last commit: &lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git rebase -i ${COMMIT_HASH}^&lt;/code&gt;, rebases from HEAD until the parent commit of &lt;code&gt;${COMMIT_HASH}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;vim/your editor opens showing all the commits since &lt;code&gt;${COMMIT_HASH}^&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;mark &lt;code&gt;${COMMIT_HASH}&lt;/code&gt; as &lt;code&gt;edit&lt;/code&gt;/&lt;code&gt;e&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;apply same steps as if it were the last commit&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git rebase --continue&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the first step you do not have to rebase all commits since the affected commit hash.
As long as you define a range of commits in which the affected critical commit is in, you should be fine.
Remember that the way rebasing works the history (incl. hashes) of all commits from your change on is going to be changed.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://git-scm.com/docs/git-rebase#_splitting_commits&#34;&gt;Git reference: rebase&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Southeast Kiel Ride</title>
      <link>https://hlschr.de/posts/outdoor/20250315-southeast-kiel/</link>
      <pubDate>Sat, 15 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/outdoor/20250315-southeast-kiel/</guid>
      <description>&lt;p&gt;I actually wanted to do another overnighter but the forecast was too dire (-5C in the night).
That plan is not canceled but instead postponed to next weekend.&lt;/p&gt;
&lt;p&gt;This alternative ride was quite beautiful.
As usual, I tried to stay in nature as much as possible.
The previous ride I already went in a south-westerly direction.
This time I took the same start but after Veloroute 10 ended, I proceeded more south-easterly in the direction of Meimersdorf, Moorsee and Rönne.
I have never been there before, so it was exciting to see that more rural part of Kiel as well.&lt;/p&gt;
&lt;p&gt;In Wellsee there are some hiking paths which are also quite nice to ride.
Of course, here you always have to yield to pedestrians but today there were not too many to care about.&lt;/p&gt;
&lt;p&gt;Weather was a bit cold, about 4-5C. Again, my feet were the first ones that disliked those temperatures.
Definitely gonna need some proper shoes for the future.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Overnighter in Olendiekssau</title>
      <link>https://hlschr.de/posts/outdoor/20250308-kiel-olendiekssau-kiel/</link>
      <pubDate>Sat, 08 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/outdoor/20250308-kiel-olendiekssau-kiel/</guid>
      <description>&lt;h3 id=&#34;general&#34;&gt;General&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;08.03.25 (Saturday)
&lt;ul&gt;
&lt;li&gt;evening ride to sleeping spot @ 54.196389, 9.920639&lt;/li&gt;
&lt;li&gt;sunny, 12C .. 15C, calm&lt;/li&gt;
&lt;li&gt;during night 3C-5C&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;09.03. (Sunday)
&lt;ul&gt;
&lt;li&gt;return home in the morning&lt;/li&gt;
&lt;li&gt;sunny, 8C .. 12C, calm&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;misc&#34;&gt;Misc&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;First really spring like weekend, took the chance to have a nice overnighter&lt;/li&gt;
&lt;li&gt;I almost forgot how easy it is in Kiel to get out into nature within a couple of minutes&lt;/li&gt;
&lt;li&gt;The final part of the first leg was very nice, through the forest&lt;/li&gt;
&lt;li&gt;forgot lots of stuff: plate, cutlery, TP etc, so had to eat with hands&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;sleeping-spot&#34;&gt;Sleeping Spot&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&#34;https://www.naturschutz-sh.de/wildes-sh/trekkingplatz-olendieksau?embed=1&#34;&gt;sleeping spot&lt;/a&gt; was okayish, a bit small but sufficient for my small tent; could be too small in summer&lt;/li&gt;
&lt;li&gt;Two picnic tables and some space to pitch small tents&lt;/li&gt;
&lt;li&gt;I was quite tired from the previous days and thus started to sleep at 20:00&lt;/li&gt;
&lt;li&gt;at around 21:30 a car came, and a couple of minutes later another one&lt;/li&gt;
&lt;li&gt;second car was cop car checking the other, which were some finns on their voyage to France&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;night&#34;&gt;Night&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;a bit cold on the back in the night, could have been the sleeping pad which lost a bit of air&lt;/li&gt;
&lt;li&gt;feet very warm, had to remove socks at one point&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;day-2&#34;&gt;Day 2&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;breakfast was prepared sandwich, so didnt need to much preparation&lt;/li&gt;
&lt;li&gt;was hoping to have a talk with the finns but they were still snoring in their hastily pitched tent&lt;/li&gt;
&lt;li&gt;time from wake up til departure ~1:10&lt;/li&gt;
&lt;li&gt;way home started again with very nice forests, later on more agricultural&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;clothing&#34;&gt;Clothing&lt;/h3&gt;
&lt;h4 id=&#34;riding-clothes&#34;&gt;Riding Clothes&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Fjallraven/specialized pants&lt;/li&gt;
&lt;li&gt;Trekking T-shirt&lt;/li&gt;
&lt;li&gt;Fleece pullover&lt;/li&gt;
&lt;li&gt;fleece mittens&lt;/li&gt;
&lt;li&gt;nike runners&lt;/li&gt;
&lt;li&gt;normal socks&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;sleepwear&#34;&gt;Sleepwear&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Sleeping gear (joggers, long sleeve)&lt;/li&gt;
&lt;li&gt;Beanie&lt;/li&gt;
&lt;li&gt;extra pair of socks, underwear&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;misc-1&#34;&gt;Misc&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;down jacket (used in the morning)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;camping-gear&#34;&gt;Camping Gear&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Decathlon Quickhike 2 Person tent&lt;/li&gt;
&lt;li&gt;Iso mat (R=2)&lt;/li&gt;
&lt;li&gt;Inflatable mat (R=1)&lt;/li&gt;
&lt;li&gt;down sleeping bag (0C comfort)&lt;/li&gt;
&lt;li&gt;Inflatable pillow&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;to-be-improved&#34;&gt;To be improved&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;take some appropriate entertainment, e.g. book&lt;/li&gt;
&lt;li&gt;dont forget TP, cutlery etc&amp;hellip;&lt;/li&gt;
&lt;li&gt;shovel for feces&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Git workflow - how to keep commits clean</title>
      <link>https://hlschr.de/posts/git/git-clean-commits/</link>
      <pubDate>Sun, 28 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/git/git-clean-commits/</guid>
      <description>&lt;p&gt;When commiting my changes, I like to keep the commits as simple and atomic as possible.
My basic workflow for that is to check all the changed files, choose the respective lines which have been changed, add them to the commit and commit all of my selected changes.&lt;/p&gt;
&lt;p&gt;Jetbrains IDEs incorporate a beautiful commit GUI, where you can do all of that.
But as there are days where I am not using a Jetbrains IDE, I asked myself how such a workflow might be possible using the tools at hand - the tools offered by the &lt;code&gt;git&lt;/code&gt; command.&lt;/p&gt;
&lt;h2 id=&#34;file-lifecycle&#34;&gt;File lifecycle&lt;/h2&gt;
&lt;p&gt;In order to be able to follow the mentioned workflow, it is good to have a better understanding of the git file lifecycle.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Untracked    Unmodified     Modified        Staged
|= add file ===============================&amp;gt;|
|            |= edit file =&amp;gt;|               |
|            |              |= stage file =&amp;gt;|
|            |&amp;lt;==================== commit =|
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pro Git, Chapter 2.2, Figure 8.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Edited files are in the state of &amp;lsquo;modified&amp;rsquo;, but are part of a commit only when they have been added to the staging area.&lt;/p&gt;
&lt;h2 id=&#34;adding-files-to-staging-area&#34;&gt;Adding files to staging area&lt;/h2&gt;
&lt;p&gt;There are several ways to add files and their content to the staging area.&lt;/p&gt;
&lt;h3 id=&#34;shotgun&#34;&gt;Shotgun&lt;/h3&gt;
&lt;p&gt;The easiest and fastest way to add files to the staging area is via&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git add file
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;or even easier&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git add .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is the shotgun method - you hit your target but might also hit something else.
You have to make sure that your files contain only the information you also want to have in your commit.&lt;/p&gt;
&lt;h3 id=&#34;more-specific&#34;&gt;More specific&lt;/h3&gt;
&lt;p&gt;Most of the times, you need to select single lines for a certain commit.&lt;/p&gt;
&lt;p&gt;You can also do that via CLI&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git add --patch file
git add -p file
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Consecutive hunks&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; of your changes whithin that file will be shown to you.
You can select &lt;code&gt;y&lt;/code&gt; or &lt;code&gt;n&lt;/code&gt;  to add the specific hunk to the staging area or leave it in the modified area.&lt;/p&gt;
&lt;p&gt;In case the given hunk is too big, you can split it up by choosing &lt;code&gt;s&lt;/code&gt;.
Depending on the hunk, the given hunks could contain a single line or a few.
The workflow with these smaller hunks is the same as before.&lt;/p&gt;
&lt;h3 id=&#34;line-by-line&#34;&gt;Line by line&lt;/h3&gt;
&lt;p&gt;If the given choice is still too broad, e.g. you have multiple consecutive lines changed together, an additional split might not be possible.
What if you still want to split these lines into different commits?
This is where the the manual edit comes into play, by choosing &lt;code&gt;e&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This opens your favorite text editor with the contents of the hunk, which looks more or less like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-deleted line
+added instead
 unchanged line
+we want to keep this addition here
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s assume you want to add the last line to the commit, but not the deletion and the addition of the first two lines.
We have to bring the document to the state it has been before - thus replacing the &lt;code&gt;-&lt;/code&gt; of the deleted line with a space &lt;code&gt; &lt;/code&gt; and removing the added line completely.
Why a space?
Space means unchanged (see the unchanged line, it also has a space in front), and exactly thats what we want.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s how it looks now:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; deleted line
 unchanged line
+we want to keep this addition here
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can now save and close the editor to apply our custom hunk.
There will be a validity check when you save, e.g. you deleted a deletion or added some text which has not been there before.
In case there was a problem, you get another chance to edit your selections.&lt;/p&gt;
&lt;h2 id=&#34;removing-files-from-staging-area&#34;&gt;Removing files from staging area&lt;/h2&gt;
&lt;p&gt;In case you noticed that you added too many files to the staging area, you can simply&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git reset filename
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will remove the file from the staging area, but not revert your local changes (as it just changes the file&amp;rsquo;s lifecycle state from staged back to modified).
Now you can start to add it to staging area again or continue committing the rest.&lt;/p&gt;
&lt;h2 id=&#34;committing&#34;&gt;Committing&lt;/h2&gt;
&lt;p&gt;Before committing, I always like to check what I am about to commit.
The best way to do that, is of course via &lt;code&gt;git status&lt;/code&gt;, connected with &lt;code&gt;git diff&lt;/code&gt; for specific files in the staging area.
If you keep your commits light and atomic, then you even might want to try the verbose status&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git status -v
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;to skip the diff part.
This will show you all textual changes of all files.&lt;/p&gt;
&lt;p&gt;Too much text?
Maybe your commit is too big :-)&lt;/p&gt;
&lt;p&gt;If you are happy, go ahead and commit!&lt;/p&gt;
&lt;h2 id=&#34;losing-overview&#34;&gt;Losing overview?&lt;/h2&gt;
&lt;p&gt;I have to admit, you can quickly lose overview over your hunks and you find yourself in a loop of &lt;code&gt;git status&lt;/code&gt; and &lt;code&gt;git add -p&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is where &lt;code&gt;git gui&lt;/code&gt; comes in handy.
What, leaving CLI? Using GUI? Only noobs use GUI?!
Come on, in this case a good visualisation comes in handy.&lt;/p&gt;
&lt;p&gt;You can do all the above mentioned steps in the gui by either adding whole files with &lt;code&gt;CTRL+T&lt;/code&gt;, adding single hunks by right clicking and adding them, or by marking single lines and adding/removing them.&lt;/p&gt;
&lt;p&gt;When you are done with that, you can check the staged items and commit them conveniently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Commit, push, run!&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://git-scm.com/book/en/v2/&#34;&gt;Pro Git&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://www.gnu.org/software/diffutils/manual/html_node/Hunks.html&#34;&gt;Gnu diffutils:&lt;/a&gt; hunks are &lt;em&gt;groups of differing lines&lt;/em&gt; in between &lt;em&gt;sequences of lines common to two files&lt;/em&gt;.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Contributing to Nextcloud with Docker</title>
      <link>https://hlschr.de/posts/nextcloud/contributing-to-nextcloud/</link>
      <pubDate>Thu, 07 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://hlschr.de/posts/nextcloud/contributing-to-nextcloud/</guid>
      <description>&lt;p&gt;The other day I wanted to debug a Nextcloud app which I am using quite frequently.
To have a local environment would be a good start, however I do not like to install everything on my system (as it is described in the Nextcloud docs).
So what can we do? Docker, of course.
I found two repos on github which were promoting docker Nextcloud development environments, &lt;a href=&#34;https://github.com/David-Development/nextcloud-dev-docker&#34;&gt;this one&lt;/a&gt; and &lt;a href=&#34;https://github.com/juliushaertl/nextcloud-docker-dev&#34;&gt;this one&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To make it short: the first one seemed a bit outdated and didn&amp;rsquo;t work anyway. The second one was working after a few hiccups.&lt;/p&gt;
&lt;p&gt;What you need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Have it already? Then let&amp;rsquo;s go.&lt;/p&gt;
&lt;h2 id=&#34;1-clone-the-project&#34;&gt;1. Clone &lt;a href=&#34;good&#34;&gt;the project&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/juliushaertl/nextcloud-docker-dev.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;2-clone-nextcloud-server&#34;&gt;2. Clone Nextcloud server&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; nextcloud-docker-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/nextcloud/server.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; server
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git submodule update --init
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; ..
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;3-set-up-env-for-nextcloud-server&#34;&gt;3. Set up env for Nextcloud server&lt;/h2&gt;
&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; file in the root folder of &lt;code&gt;nextcloud-docker-dev&lt;/code&gt; containing some specific variables:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;COMPOSE_PROJECT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;master
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;REPO_PATH_SERVER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/jus/repos/nextcloud/server
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ADDITIONAL_APPS_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/jus/repos/nextcloud/server/apps-extra
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NEXTCLOUD_AUTOINSTALL_APPS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;viewer activity&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# can be used to run separate setups besides each other&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DOCKER_SUBNET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;192.168.15.0/24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PORTBASE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;815&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Main dns names for ssl proxy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# This can be used to append a custom domain name to the container names&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DOMAIN_SUFFIX&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Enter your &lt;code&gt;REPO_PATH_SERVER&lt;/code&gt; as per the &lt;code&gt;pwd&lt;/code&gt; from step 2. The &lt;code&gt;ADDITIONAL_APPS_PATH&lt;/code&gt; is the place where you can put the apps you want to work on.&lt;/p&gt;
&lt;h2 id=&#34;4-set-up-hosts&#34;&gt;4. Set up hosts&lt;/h2&gt;
&lt;p&gt;The reverse proxy is resolving the domain &lt;code&gt;nextcloud.local&lt;/code&gt;, add it to your &lt;code&gt;/etc/hosts/&lt;/code&gt; to reach it&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1 nextcloud.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you forget this step, you will not be able to open Nextcloud from your browser.&lt;/p&gt;
&lt;h2 id=&#34;5-spin-up-the-containers&#34;&gt;5. Spin up the containers&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose up proxy nextcloud
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command starts the revproxy (nginx), nextcloud, the database (postgres) and mailhog (whatever that is for, lol). After a small installation process you are ready to go.&lt;/p&gt;
&lt;p&gt;There are some more containers in the &lt;code&gt;docker-compose&lt;/code&gt;, which I do not need for developing. That&amp;rsquo;s why I am ok with the command mentioned above.&lt;/p&gt;
&lt;h2 id=&#34;6-open-nextcloud-in-browser&#34;&gt;6. Open Nextcloud in browser&lt;/h2&gt;
&lt;p&gt;As set up in step 4, you can simply open &lt;code&gt;nextcloud.local&lt;/code&gt; and you are good to go.
Log in with one of the user names mentioned in the set up process. I do not remember all of them, but some are these:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;jane
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;admin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;alice
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bob
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;user1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Password is the username.&lt;/p&gt;
&lt;h2 id=&#34;7-start-developing&#34;&gt;7. Start developing&lt;/h2&gt;
&lt;p&gt;Pull the apps you want to develop to the folder described in &lt;code&gt;ADDITIONAL_APPS_PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Make sure you enable them in the Nexctloud app menu using the admin login.&lt;/p&gt;
&lt;p&gt;Happy contributing!&lt;/p&gt;
</description>
    </item>
    

  </channel>
</rss>
