<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Nicolas Abbal]]></title><description><![CDATA[Technology, sport, and experiments...]]></description><link>https://nicolas-abbal.com/</link><image><url>https://nicolas-abbal.com/favicon.png</url><title>Nicolas Abbal</title><link>https://nicolas-abbal.com/</link></image><generator>Ghost 3.39</generator><lastBuildDate>Mon, 20 Oct 2025 05:14:39 GMT</lastBuildDate><atom:link href="https://nicolas-abbal.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Creating a map routing application for outdoor activities]]></title><description><![CDATA[How and why I've built my own map routing app for outdoor activities, with Vue.js, Leaflet and the Open Route Service API...]]></description><link>https://nicolas-abbal.com/creating-a-map-routing-application-for-outdoor-activities/</link><guid isPermaLink="false">5f45210ff600cb1653240115</guid><category><![CDATA[outdoor]]></category><category><![CDATA[vue.js]]></category><category><![CDATA[map]]></category><category><![CDATA[routing]]></category><dc:creator><![CDATA[Nicolas Abbal]]></dc:creator><pubDate>Sat, 29 Aug 2020 07:52:15 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1519885277449-12eee5564d68?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<h2 id="why">Why?</h2><img src="https://images.unsplash.com/photo-1519885277449-12eee5564d68?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Creating a map routing application for outdoor activities"><p>Before going for a hike, a ride or any outdoor activity on roads or trails, it can be useful to plan your outing in order to know where you are going, and to estimate how much time it could take you.</p><p>Moreover, in this Covid period, where most of the trail running races were cancelled, a lot of runners (myself included) were looking for off-races, long distance challenges. But before attempting to complete a challenge, it must be defined and planned, and this usually includes drawing some lines on a map. So my initial motivation was to create a tool that allows me to do that in a quick an efficient way.</p><h2 id="what-do-i-need">What do I need?</h2><p>The essential features I personally need are:<br>- the tool should be able to automatically follow roads and trails between two points<br>- it should be possible to add off-trail points with a straight line from the previous point (manual routing)<br>- it should be possible to switch the routing mode between road and trail<br>- the tool should calculate the distance, ascent and descent<br>- the track should be exportable to a GPX file.<br>Some other features are nice to have but non essential for me:<br>- a visualization of the slopes on a graph<br>- having some Point of Interests displayed on a map, like refuges and huts, drinking water location, shops, etc...<br>- being able to estimate a schedule based on a pace.</p><p>There are a lot of existing tools which cover more or less these features, but some of them (Strava, OpenRunner, TraceDeTrail...) require a premium subscription to have all the features listed above, and some others are linked to the watch or device you bought (Garmin, Suunto...). Moreover, I'm often not very satisfied with the user experience of these applications.</p><h2 id="some-technical-details-">Some technical details...</h2><p>So I decided to try building my own app, with the following components:<br>- Vue.js for the front-end framework<br>- Leaflet as the map library<br>- the <a href="https://openrouteservice.org/">OpenRouteService</a> API via its <a href="https://www.npmjs.com/package/openrouteservice-js">openrouteservice-js NPM module</a><br>- Thunderforest for its <a href="https://www.thunderforest.com/maps/outdoors/">outdoor map</a></p><p>The ORS API is nicely designed an is quite fast. The main issue I faced was the limitation of maximum 50 points for the routing API. To overcome this, I'm now doing requests to the API with only two points, and concatenating the route inside the application. </p><p>I now have a first working version which includes all the essential features. The code is available on <a href="https://github.com/niabb/off-planner">Github</a> with the instructions to run it.</p><p>An online version is available at <a href="https://map.nicolas-abbal.com">https://map.nicolas-abbal.com</a>.</p><h2 id="how-to-use-it">How to use it?</h2><p>I did not spend much time on the UI/UX part, as it's mainly a tool for my own usage. Here are some hints on how to use it.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://nicolas-abbal.com/content/images/2020/08/image.png" class="kg-image" alt="Creating a map routing application for outdoor activities"><figcaption>You can zoom in and out, and navigate to a different place with a drag &amp; drop, or by using the location text box with a city name, and using the enter key</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://nicolas-abbal.com/content/images/2020/08/image-2.png" class="kg-image" alt="Creating a map routing application for outdoor activities"><figcaption>The first layer icon allows to switch between the Thunderforest Outdoor map and the Open TopoMap. The second icon cancels the last point added on the map (ctrl+Z works as well). On the third line, you can switch between the hiking/trails (by default) and biking/roads routing mode.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://nicolas-abbal.com/content/images/2020/08/image-1.png" class="kg-image" alt="Creating a map routing application for outdoor activities"><figcaption>By left clicking on the map, a starting point is inserted. By left clicking again, the track is calculated following roads or trails depending on the routing mode. A right click will add a point without routing with a straight line.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://nicolas-abbal.com/content/images/2020/08/image-3.png" class="kg-image" alt="Creating a map routing application for outdoor activities"><figcaption>By clicking on the gear icon, track informations are displayed. You can also export the track in the GPX format, and import/export the track in a JSON format so that you can continue editing your route later.</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[How to install Ghost on Debian 9, with NGINX and SQLite]]></title><description><![CDATA[Quick walkthrough to install Ghost on a fresh Debian 9 install, using NGINX as a reverse proxy, and SQLite as the database to store the blog content.]]></description><link>https://nicolas-abbal.com/how-to-install-ghost-on-debian-9-with-nginx-and-sqlite/</link><guid isPermaLink="false">5ddedabbc02f1379e72c6451</guid><category><![CDATA[ghost]]></category><category><![CDATA[nginx]]></category><category><![CDATA[debian]]></category><category><![CDATA[node.js]]></category><dc:creator><![CDATA[Nicolas Abbal]]></dc:creator><pubDate>Wed, 12 Dec 2018 18:59:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="How to install Ghost on Debian 9, with NGINX and SQLite"><p>The official Ghost installation guide recommends Ubuntu 16.04 and MySQL. Here is a quick walkthrough to install Ghost on a fresh Debian 9 install, with a sudoer user, using NGINX as a reverse proxy, and SQLite as the database to store the blog content.</p><p>The goal is to have all the files needed to run the blog in the same folder, so that it can be easily transfered to another server, with the minimum amount of work.</p><h2 id="1-install-node-js-and-nginx">1/ Install node.js and NGINX</h2><!--kg-card-begin: markdown--><p><a href="https://github.com/nodesource/distributions/blob/master/README.md">Instructions here.<br>
</a>For node.js 8.x:</p>
<pre><code>sudo apt-get update
sudo apt-get install -y curl nginx
curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
sudo apt-get install -y nodejs
</code></pre>
<!--kg-card-end: markdown--><h2 id="2-install-ghost-cli">2/ Install ghost-cli</h2><!--kg-card-begin: markdown--><p>We will use ghost-cli to create the local Ghost instance.</p>
<pre><code>sudo npm install ghost-cli@latest -g
</code></pre>
<!--kg-card-end: markdown--><h2 id="3-install-a-local-instance-of-ghost">3/ Install a local instance of Ghost</h2><!--kg-card-begin: markdown--><p>First, we create a new directory in /var/www, and we give our user its ownership:</p>
<pre><code>cd /var/www
sudo mkdir my-ghost-instance
sudo chown myuser:myuser my-ghost-instance
</code></pre>
<p>Then we install the local ghost instance:</p>
<pre><code>cd my-ghost-instance
ghost install local
</code></pre>
<p>At the end of the installation, you should have the following message:</p>
<pre><code>Ghost was installed successfully! To complete setup of your publication, visit:

    http://localhost:2368/ghost/
</code></pre>
<p>And you should have an instance running:</p>
<pre><code>nico@myserver:/var/www/my-ghost-instance$ ps -ef |grep node
myuser      5694  5686  3 16:10 ?        00:00:04 /usr/bin/node current/index.js
myuser      5714  2479  0 16:13 pts/0    00:00:00 grep node
</code></pre>
<p>The command <code>ghost ls</code> should also display the running instance.<br>
Now, we will create the production configuration file:</p>
<pre><code>cp config.development.json config.production.json
</code></pre>
<p>Let's Edit the file config.production.json, and change the url field to match our server IP or DNS (the one which will be used to access our blog). The file should look like:</p>
<pre><code>{
  &quot;url&quot;: &quot;http://myDNSorIP&quot;,
  &quot;server&quot;: {
    &quot;port&quot;: 2368,
    &quot;host&quot;: &quot;127.0.0.1&quot;
  },
  &quot;database&quot;: {
    &quot;client&quot;: &quot;sqlite3&quot;,
    &quot;connection&quot;: {
      &quot;filename&quot;: &quot;/var/www/my-ghost-instance/content/data/ghost-local.db&quot;
    }
  },
  &quot;mail&quot;: {
    &quot;transport&quot;: &quot;Direct&quot;
  },
  &quot;logging&quot;: {
    &quot;transports&quot;: [
      &quot;file&quot;,
      &quot;stdout&quot;
    ]
  },
  &quot;process&quot;: &quot;local&quot;,
  &quot;paths&quot;: {
    &quot;contentPath&quot;: &quot;/var/www/my-ghost-instance/content&quot;
  }
}
</code></pre>
<p>Now we stop our ghost instance and start it in production mode:</p>
<pre><code>ghost stop
ghost start production
</code></pre>
<!--kg-card-end: markdown--><h2 id="4-configure-nginx">4/ Configure NGINX</h2><!--kg-card-begin: markdown--><p>Let's create some folders and files to configure NGINX to act as a reverse proxy for the blog.</p>
<pre><code>cd /var/www/my-ghost-install
mkdir -p system/files system/nginx-root
cd system/files
</code></pre>
<p>Then, we create a file named my-ghost-instance.conf, with the following content:</p>
<pre><code>server {
    listen 80;
    listen [::]:80;

    server_name myDNSorIP;
    root /var/www/my-ghost-instance/system/nginx-root;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:2368;

    }

    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}
</code></pre>
<p>Then we will link this configuration file into the NGINX configuration folder, and then make NGINX reload the configuration files:</p>
<pre><code>sudo ln -s /var/www/my-ghost-instance/system/files/my-ghost-instance.conf /etc/nginx/sites-enabled/my-ghost-instance.conf
sudo systemctl reload nginx.service
</code></pre>
<!--kg-card-end: markdown--><h2 id="5-make-the-blog-resilient-with-systemd">5/ Make the blog resilient with systemd</h2><!--kg-card-begin: markdown--><p>Now we need to ensure that the ghost instance will be automatically restarted if the server reboots. Create a new file /var/www/my-ghost-instance/system/files/my-ghost-instance.service with the following content:</p>
<pre><code>[Unit]
Description=Ghost systemd service for my-ghost-instance

[Service]
Type=simple
WorkingDirectory=/var/www/my-ghost-instance
User=myuser
ExecStart=/usr/bin/node /var/www/my-ghost-instance/current/index.js
Restart=always
Environment=&quot;NODE_ENV=production&quot;

[Install]
WantedBy=multi-user.target
</code></pre>
<p>Then we need to link this configuration file to the systemd folder, and then activate the service:</p>
<pre><code>ghost stop
sudo ln -s /var/www/my-ghost-instance/system/files/my-ghost-instance.service /etc/systemd/system/my-ghost-instance.service
sudo systemctl daemon-reload
sudo systemctl enable my-ghost-instance.service
sudo systemctl start my-ghost-instance.service
</code></pre>
<!--kg-card-end: markdown--><h2 id="6-bonus-install-a-ssl-certificate-to-use-https">6/ Bonus: install a SSL certificate to use HTTPS</h2><!--kg-card-begin: markdown--><p>It's very easy to use HTTPS, generating a SSL certificate with Let's Encrypt free service. <a href="https://certbot.eff.org/lets-encrypt/debianstretch-nginx">The instructions are described here</a>, or we can follow the following steps:</p>
<pre><code>sudo su
echo 'deb http://ftp.debian.org/debian stretch-backports main' &gt; /etc/apt/sources.list.d/stretch-backports.list
exit
sudo apt-get update
sudo apt-get install certbot python-certbot-nginx -t stretch-backports
sudo certbot --nginx
</code></pre>
<p>Once the question have been answered, the NGINX configuration should be automatically updated to use the new certificate.</p>
<!--kg-card-end: markdown--><p></p><p>We now have a new Ghost instance running! Using this process, we can easily add more Ghost instances on the same server (adapting the folder names, port numbers etc...), and backup an instance with all its configuration file.</p>]]></content:encoded></item></channel></rss>