<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://blog.geothings.ch/feed_style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <tabi:metadata xmlns:tabi="https://github.com/welpo/tabi">
        <tabi:base_url>https://blog.geothings.ch</tabi:base_url>
        <tabi:separator>
            •
        </tabi:separator>
        <tabi:about_feeds>
            This is a web feed, also known as an Atom feed. Subscribe by copying the URL from the address bar into your newsreader. Visit About Feeds to learn more and get started. It's free.
        </tabi:about_feeds>
        <tabi:visit_the_site>
            Visit website
        </tabi:visit_the_site>
        <tabi:recent_posts>
            Recent posts
        </tabi:recent_posts>
        <tabi:last_updated_on>
            Updated on $DATE
        </tabi:last_updated_on>
        <tabi:default_theme></tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>opensource</tabi:current_section>
    </tabi:metadata><link rel="extra-stylesheet" href="https://blog.geothings.ch/skins/mint.css?h=504215cf6bc10586b487" /><title>geothings - opensource</title>
        <subtitle>spatial data | open source | oversharing</subtitle>
    <link href="https://blog.geothings.ch/tags/opensource/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://blog.geothings.ch/tags/opensource/" rel="alternate" type="text/html"/>
    <generator uri="https://www.getzola.org/">Zola</generator><updated>2025-05-30T00:00:00+00:00</updated><id>https://blog.geothings.ch/tags/opensource/atom.xml</id><entry xml:lang="en">
        <title>Serve PDF and DXF files using qgis-server</title>
        <published>2025-05-30T00:00:00+00:00</published>
        <updated>2025-05-30T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/pdf-dxf-qgis-server/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/pdf-dxf-qgis-server/</id>
        
            <content type="html">&lt;p&gt;You might be faced with the need to distribute “plan” files. For some businesses, paper maps (pdf) and CAD drawings (dxf) are still the primary source of information, and sometimes you can’t propose anything else. If you’re working with QGIS there are simple ways to serve those files without much to do, and all with opensource solutions using &lt;code&gt;qgis&lt;/code&gt; and &lt;code&gt;qgis-server&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;You’ll need… Well, &lt;code&gt;qgis&lt;/code&gt; and &lt;code&gt;qgis-server&lt;/code&gt;, and that’s it. First install &lt;code&gt;qgis-server&lt;/code&gt; using instructions for you environment, you might have a look at the &lt;a rel=&quot;external&quot; href=&quot;https://docs.qgis.org/3.40/en/docs/server_manual/getting_started.html&quot;&gt;official doc&lt;/a&gt; or this &lt;a rel=&quot;external&quot; href=&quot;https://geothings.ch/blog/geoservices-with-qgis-server/#install-and-configure-qgis-server-and-nginx&quot;&gt;short tuto&lt;/a&gt;. While working on this article, I followed the &lt;a rel=&quot;external&quot; href=&quot;https://docs.qgis.org/3.40/en/docs/server_manual/containerized_deployment.html#&quot;&gt;containerized deployment instruction&lt;/a&gt; from the QGIS documentation. I’ll link a &lt;a rel=&quot;external&quot; href=&quot;https://codeberg.org/jujube/pdf-dxf-qgis-server&quot;&gt;repository&lt;/a&gt; with the resources for you to follow along.&lt;/p&gt;
&lt;p&gt;The next step is the preparation of your exports using &lt;code&gt;qgis&lt;/code&gt;. Your projects will serve as configuration, where you’ll define symbologies, renderings, services, etc. We’ll show some possibilities to configure pdf and dxf exports.&lt;/p&gt;
&lt;h2 id=&quot;export-pdf-files&quot;&gt;Export PDF files&lt;/h2&gt;
&lt;p&gt;For simple cases it’s the easiest setup, you’ll just need to configure a &lt;code&gt;layout&lt;/code&gt; in your project. Define a map that suits your taste (or better your objectives) and save everything in a location where your server has the right to read your project and the linked data. And that’s it for the most part. You can then have a look at a resulting map by querying an url of the like:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;http://localhost:8080/qgis-server?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SERVICE=WMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;VERSION=1.3.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;REQUEST=GetPrint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;MAP=project.qgz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;TEMPLATE=layout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;CRS=EPSG:3857&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;map0:extent=-591992,-1689591,4318167,3574909&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;FORMAT=application/pdf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we divide the query we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://localhost:8080/qgis-server&lt;/code&gt; the url of your qgis-server.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SERVICE=WMS&lt;/code&gt; tells the server to use its WMS service.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VERSION=1.3.0&lt;/code&gt; version of the service.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REQUEST=GetPrint&lt;/code&gt; run the GetPrint request.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAP=project.qgz&lt;/code&gt; path to the qgis project to read.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TEMPLATE=layout&lt;/code&gt; the name of a template defined within the project that describes a &lt;code&gt;layout&lt;/code&gt;, where you’ll define your map.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CRS=3857&lt;/code&gt; the coordinate system to use, usually the one defined in your project.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;map0:extent=xmin,ymin,xmax,ymax&lt;/code&gt; the bounding box defining the extent to print. In the &lt;code&gt;CRS&lt;/code&gt; system.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FORMAT=application/pdf&lt;/code&gt; specify the output format, here pdf.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your browser will show you something like the following:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/pdf-dxf-qgis-server/export_pdf.png&quot; alt=&quot;export-pdf&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this simple case, we could imagine a service where you define an extraction extent, which then will be passed to your printing service in the form of a &lt;code&gt;mapx:extent&lt;/code&gt; parameter. You’d receive your pdf as a response.&lt;/p&gt;
&lt;h2 id=&quot;export-pdf-files-atlas-version&quot;&gt;Export PDF files: atlas version&lt;/h2&gt;
&lt;p&gt;You can create &lt;a rel=&quot;external&quot; href=&quot;https://docs.qgis.org/3.40/en/docs/training_manual/forestry/forest_maps.html&quot;&gt;&lt;code&gt;atlases&lt;/code&gt; in QGIS&lt;/a&gt; which allows you to produce maps using predefined extents. Your &lt;code&gt;qgis-server&lt;/code&gt; also supports those and you can request a specific map from an atlas by using the &lt;code&gt;ATLAS_PK&lt;/code&gt; parameter in you query.&lt;/p&gt;
&lt;p&gt;If we create a &lt;code&gt;layout_atlas&lt;/code&gt; in our project, using a grid as overlay (see the &lt;a rel=&quot;external&quot; href=&quot;https://codeberg.org/jujube/pdf-dxf-qgis-server&quot;&gt;example project&lt;/a&gt;), we could then query our atlas pages using the following:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;http://localhost:8080/qgis-server?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SERVICE=WMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;VERSION=1.3.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;REQUEST=GetPrint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;MAP=project.qgz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;TEMPLATE=layout_atlas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;CRS=EPSG:3857&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;ATLAS_PK=1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which would then send back something like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/pdf-dxf-qgis-server/export_atlas.png&quot; alt=&quot;export-atlas&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here you’d need to get the atlas elements that intersect your region of interest and pass their &lt;code&gt;id&lt;/code&gt; as &lt;code&gt;ATLAS_PK&lt;/code&gt;. A query like the following could get you this information:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;curl -X POST -H &amp;quot;Content-Type: application/xml&amp;quot; -d @BODY.xml localhost:8080/qgis-server?SERVICE=WFS&amp;amp;VERSION=1.3.0&amp;amp;REQUEST=GetFeature&amp;amp;MAP=project.qgs  &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;BODY.xml&lt;/code&gt; contains the following body:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;yes&amp;quot;?&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;wfs:GetFeature maxFeatures=&amp;quot;5000&amp;quot; version=&amp;quot;1.1.0&amp;quot; service=&amp;quot;WFS&amp;quot; xsi:schemaLocation=&amp;quot;http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd&amp;quot; xmlns:wfs=&amp;quot;http://www.opengis.net/wfs&amp;quot; xmlns:gml=&amp;quot;http://www.opengis.net/gml&amp;quot; xmlns:ogc=&amp;quot;http://www.opengis.net/ogc&amp;quot; xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;wfs:Query typeName=&amp;quot;grid&amp;quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;ogc:Filter&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;ogc:Intersects&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;ogc:PropertyName&amp;gt;geom&amp;lt;/ogc:PropertyName&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;Polygon xmlns=&amp;quot;http://www.opengis.net/gml&amp;quot; srsName=&amp;quot;EPSG:3857&amp;quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;exterior&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;LinearRing&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;lt;posList srsDimension=&amp;quot;2&amp;quot;&amp;gt;1471131.73 1257643.13 2471131.7 1257643.13 2471131.7 257643.1 1471131.7 257643.1 1471131.73 1257643.1&amp;lt;/posList&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;/LinearRing&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;/exterior&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;/Polygon&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;/ogc:Intersects&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;/ogc:Filter&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;/wfs:Query&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;/wfs:GetFeature&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Extracting the ids from the response you could then query you atlas on the server with:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;http://localhost:8080/qgis-server/?atlas_pk=44,45,46,52,53,54&amp;amp;SERVICE=WMS&amp;amp;VERSION=1.3.0&amp;amp;REQUEST=GetPrint&amp;amp;MAP=project.qgz&amp;amp;TEMPLATE=layout_atlas&amp;amp;CRS=EPSG:3857&amp;amp;FORMAT=application/pdf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;admonition tip&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-tip&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;TIP&lt;/strong&gt;
&lt;p&gt;You’ll need to allow your server to print multiple pages. This can be done in &lt;code&gt;Project &amp;gt; Properties &amp;gt; QGIS Server &amp;gt; WMS &amp;gt; Maximum page number for atlas queries&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;export-dxf&quot;&gt;Export DXF&lt;/h2&gt;
&lt;p&gt;At this point you have everything you need to export dxf files. Make sure you expose the layers you want to export through WFS (&lt;code&gt;Project &amp;gt; Properties &amp;gt; QGIS Server &amp;gt; WFS &amp;gt; Tick Publish for the layers you want&lt;/code&gt;). You can then get your dxf using something like:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;localhost:8080/qgis-server?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SERVICE=WMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;VERSION=1.3.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;REQUEST=GetMap&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;MAP=project.qgz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;TEMPLATE=layout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;CRS=EPSG:3857&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;map0:extent=-591992,-1689591,4318167,3574909&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;LAYERS=point,line,polygon&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;FORMAT=application/dxf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;amp;FILE_NAME=output.dxf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;admonition tip&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-tip&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;TIP&lt;/strong&gt;
&lt;p&gt;For dxf exports, the symbology is a bit trickier to get right in my opinion. You might have to play with units to get a configuration and a rendering that suits your needs. Also, &lt;code&gt;Polylines3D&lt;/code&gt; don’t support linetypes and weight, so you might need to use the &lt;code&gt;FORCE_2D&lt;/code&gt; parameter.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;to-conclude&quot;&gt;To conclude&lt;/h2&gt;
&lt;p&gt;PDF and DXF are, after all, rather easy to create with a qgis-server. No need for any other piece of software, &lt;a rel=&quot;external&quot; href=&quot;https://geothings.ch/blog/stop-using-fme/&quot;&gt;potentially expensive&lt;/a&gt;. Make sure to play with the &lt;code&gt;QGIS Server&lt;/code&gt; properties of your project and layers within QGIS to see how it influences the exports. Again, all the material used in this article is accessible in this &lt;a rel=&quot;external&quot; href=&quot;https://codeberg.org/jujube/pdf-dxf-qgis-server&quot;&gt;repo&lt;/a&gt;.&lt;/p&gt;
</content>
        <summary type="html">Ditch that ugly FME workspace for a simple server installation.</summary>
        </entry><entry xml:lang="en">
        <title>How to build a single file QGIS project?</title>
        <published>2024-09-28T00:00:00+00:00</published>
        <updated>2024-09-28T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/single-file-qgis/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/single-file-qgis/</id>
        
            <content type="html">&lt;p&gt;We talked about &lt;a rel=&quot;external&quot; href=&quot;https://www.geothings.ch/posts/managing-shared-qgis-projects/&quot;&gt;how to collaborate on QGIS project&lt;/a&gt; at a “project management” level, but in practice, how do you make things easy for you to easily share a project. Well, one way is to minimize the amount of files by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Storing your project in database&lt;/li&gt;
&lt;li&gt;Storing your data in database&lt;/li&gt;
&lt;li&gt;Storing your styles in database&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;introducing-geopackage&quot;&gt;Introducing Geopackage&lt;/h2&gt;
&lt;p&gt;Geopackage is basically a SQLite database with well defined rules for storing geospatial data. You get a single file that can contain all your data, stored in different layers. The standard has been extended to allow &lt;a rel=&quot;external&quot; href=&quot;https://www.geopackage.org/extensions.html&quot;&gt;storage of QGIS projects&lt;/a&gt;, which allows to bundle a lot into a single file.&lt;/p&gt;
&lt;h2 id=&quot;storing-your-project-in-geopackage&quot;&gt;Storing your project in Geopackage&lt;/h2&gt;
&lt;p&gt;The first step would be to store your new project into a GPKG file. Open QGIS and create a new Geopackage database:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Layer &amp;gt; Create a new layer &amp;gt; New Geopackage layer&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then you can save your project in this newly created Geopackage:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Project &amp;gt; Save To &amp;gt; GeoPackage...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And that’s it, you can then open this project from QGIS:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Project &amp;gt; Open from &amp;gt; GeoPackage...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You now have a Geopackage containing your project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;GeoPackage&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Project&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;storing-your-data-in-geopackage&quot;&gt;Storing your data in Geopackage&lt;/h2&gt;
&lt;p&gt;It’s rather self-explanatory, your can store all the data used in your project in the same Geopackage. If you use shapefile, &lt;a rel=&quot;external&quot; href=&quot;https://switchfromshapefile.org/&quot;&gt;please stop&lt;/a&gt;, it been making no sense for a while now.&lt;/p&gt;
&lt;p&gt;Convert your data into Geopackage layer by right clicking on your layer of interest:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Export &amp;gt; Save features as... &amp;gt; GeoPackage&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Select the Geopackage database containing your project as &lt;code&gt;File name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point you should already have your project and data in a single file, which is already way easier than managing multiple data sources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;GeoPackage&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Project&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;layer 1&lt;/li&gt;
&lt;li&gt;layer 2&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;storing-styles&quot;&gt;Storing styles&lt;/h2&gt;
&lt;p&gt;Having your data stored is nice, but styles are also important to add symbologies and capabilities to your layers. Geopackage also allow to store your style directly.&lt;/p&gt;
&lt;p&gt;Define your layers styles (forms, symbologies, actions etc.) and then right click on your layer:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Properties... &amp;gt; Symbology &amp;gt; Style &amp;gt; Save Style... &amp;gt; In Database&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And hey you can even go further! If you have .svg symbols in your style, you can load the symbol definition in the Geopackage. Like this you avoid broken links and unfound .svg file.&lt;/p&gt;
&lt;p&gt;To do this, first create a new table in your Geopackage:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Layer &amp;gt; Create Layer &amp;gt; New GeoPackage Layer...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Select your Geopackage and fill the layer name (&lt;code&gt;symbols&lt;/code&gt; or whatever you like). Then under &lt;code&gt;New Field&lt;/code&gt;, add a field of type &lt;code&gt;Binary Object (BLOB)&lt;/code&gt; and create the layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/single-file-qgis/add_svg_definition.PNG&quot; alt=&quot;alt text&quot; /&gt;
&lt;em&gt;Add svg to Geopackage&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Then add a new feature in your &lt;code&gt;symbols&lt;/code&gt; and embed your .svg file in the BLOB column.&lt;/p&gt;
&lt;p&gt;Finally, from your data layer symbology, select the &lt;code&gt;SVG Marker&lt;/code&gt; symbol type and &lt;code&gt;Edit&lt;/code&gt; the definition of the SVG with the rule of your choice, for example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&#39;base64:&#39;|| to_base64(attribute(get_feature( &#39;symbols&#39;, &#39;fid&#39;, 1), &#39;svg&#39;))&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here we’ll use the symbol with the &lt;code&gt;fid&lt;/code&gt; value 1. You can create the rule that wou want, but make sure to encode the binary definition into base64.&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-tip&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;TIP&lt;/strong&gt;
&lt;p&gt;You can also directly embed the file instead of creating the rule, you’ll just lose some flexibility in the choice of you symbol. A rule-based choice would allow you to use different SVG definitions based on a attribute for example.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;At this point you should be in a situation like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;GeoPackage&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Project&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;layer 1&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Style 1&lt;/li&gt;
&lt;li&gt;Style 2&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;layer 2&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Style 1&lt;/li&gt;
&lt;li&gt;Style 2&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which should be enough to cover the needs of most projects, all of this in a single file! Now it’s way easier to share, and there is no risk of breaking a link.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Easy right? And the best in all that, is that you can do exactly the same with a PostgreSQL database: storing your QGIS project, storing data (obviously) and storing your styles. It just take some “pre-procesing” by saving your data into your GPKG/PostgreSQL database, but you’ll want to do that anyway, and if you don’t, I assure you you want to.&lt;/p&gt;
</content>
        </entry><entry xml:lang="en">
        <title>Managing shared QGIS projects</title>
        <published>2024-04-19T00:00:00+00:00</published>
        <updated>2024-04-19T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/managing-shared-qgis-projects/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/managing-shared-qgis-projects/</id>
        
            <content type="html">&lt;p&gt;QGIS is an amazing tool. Being spatial analysis or digitizing data, it’s efficient, highly customable and extensible. There is so much you can do with it.&lt;/p&gt;
&lt;p&gt;So it’s only natural that we want to share a project that we are working on and propose to other people to join our effort on our amazing data digitizing/spatial statistics calculating masterpiece. Well yes of course, things are better when they are shared (most of them at least), and we go further together than alone (wow very profound), but this is something that has to be given some thoughts beforehand to ensure that everything goes smoothly within the collaboration.&lt;/p&gt;
&lt;p&gt;We’ll briefly have a look at what can help make collaborating with QGIS successful. Also, we’ll be talking a lot about &lt;strong&gt;projects&lt;/strong&gt;, we are referring here to the literal QGIS file used to store the work environment (data, visualizations, etc.). It’s not &lt;strong&gt;project&lt;/strong&gt; in the sense of a &lt;em&gt;project management&lt;/em&gt;, but some things might be useful to apply from the project management world. And in any case, working together requires tools outside of the technical aspects.&lt;/p&gt;
&lt;h2 id=&quot;when-does-this-apply&quot;&gt;When does this apply?&lt;/h2&gt;
&lt;p&gt;Obviously if you’re alone on your project or working with a handful of people, some points might feel over-the-top or even counter productive, but some questions are always useful to be asked and it can be helpful to use some “good” practice, even when working for ourselves.&lt;/p&gt;
&lt;p&gt;For the last years I had to pick up some projects that were shared by tens of people, create new ones, and I can safely say that there need to be rules when more people are getting on board.&lt;/p&gt;
&lt;p&gt;So if you work with spatial data and you need to have people collaborating using QGIS you might be interested&lt;/p&gt;
&lt;h2 id=&quot;ok-let-s-go-how-to-collaborate-with-qgis&quot;&gt;Ok let’s go, how to collaborate with QGIS?&lt;/h2&gt;
&lt;p&gt;This is obviously from my personal experience. It’s not exhaustive, or maybe not accurate, but if it can help..&lt;/p&gt;
&lt;h3 id=&quot;tl-dr&quot;&gt;tl;dr&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scope your project&lt;/strong&gt;: have objectives for the project and keep it within controlled boundaries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Have a data structure&lt;/strong&gt;: define which data will be included and model them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Give roles and responsibilities&lt;/strong&gt;: have a project management in place.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Version control&lt;/strong&gt;: track changes and decompose your project if it becomes too big.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Share it efficiently&lt;/strong&gt;: have everything in a single place, keep it as simple as possible, save it in database.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;scope-your-project&quot;&gt;Scope your project&lt;/h3&gt;
&lt;p&gt;It sometimes feels like it’s a bad thing to say in an Agile world, but giving some boundaries to the project helps staying on track. Maybe this is something that I feel due to unstructured work environments, but when working on a (QGIS) project, if there is no beginning and no end there is just no way to succeed.&lt;/p&gt;
&lt;p&gt;Back to QGIS. When starting, think about what the project will be about: will it be used to digitize data? Should data processing and spatial statistics be in the project? Should this data X be included? QGIS is a powerful tool, and maps are powerful visualizations, when people unfamiliar with these notions start to see what can be done they tend to want to add a lot of information in a project. Hell you can do your taxes in QGIS if you want, should you? Probably not.&lt;/p&gt;
&lt;p&gt;Let’s say you design your project for a certain team, to help them digitize certain data. Obviously, since they are the target of the project, their demands should be considered, analyzed and implemented. But let’s also imagine that the project has been shared with another team interested in visualizing the data. Should you take their demands into account as well? Well, has the work induced by these demands been considered when designing the project? If not then these demands should be redirected somewhere else.&lt;/p&gt;
&lt;p&gt;Maybe try to have some ground rules: one project per data structure (see &lt;a href=&quot;https://blog.geothings.ch/blog/managing-shared-qgis-projects/#structure-your-data-before-thinking-about-qgis&quot;&gt;below&lt;/a&gt;), keep separate the digitization, processing and statistics, one project per team, etc.&lt;/p&gt;
&lt;p&gt;Of course if your time is bound to be spent on a couple of project you can fit a lot of things and optimize them, having everything working together. But don’t forget that the more people start to use these projects, the more impactful any change becomes, and separation of concerns can help a lot to prevent that.&lt;/p&gt;
&lt;h3 id=&quot;structure-your-data-before-thinking-about-qgis&quot;&gt;Structure your data before thinking about QGIS&lt;/h3&gt;
&lt;p&gt;Have a coherent data model to work with. If you have people around specialized in modeling data and structuring a database, listen to them. If they tell you that you should &lt;a rel=&quot;external&quot; href=&quot;https://en.wikipedia.org/wiki/Database_normalization&quot;&gt;normalize your data&lt;/a&gt; because you’ll otherwise have problems when scaling up, please do it. I think that’s the most important advice, and it doesn’t even concern QGIS.&lt;/p&gt;
&lt;p&gt;If you’re wondering what this is about, data modeling is, very simply put, how to structure/divide your data into blocks that make sense, allowing you to add, remove, update data in the most efficient way. For example, let’s imagine that I want to have information about trees in my city and the population of birds they are housing. I could have everything in a single table like below:&lt;/p&gt;
&lt;noscript&gt;
    &lt;strong&gt;⚠️ JavaScript is required to render the diagram.&lt;/strong&gt;
&lt;/noscript&gt;
&lt;pre class=&quot;mermaid invertible-image&quot;&gt;
erDiagram
    tree {
        serial id_tree
        int age
        geometry geom
        string bird_species
        bool is_bird_cute
    }
&lt;/pre&gt;
&lt;p&gt;Ok cool I can now create one tree that has a family of pigeons:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;id_tree&lt;/th&gt;&lt;th&gt;age&lt;/th&gt;&lt;th&gt;geom&lt;/th&gt;&lt;th&gt;bird_species&lt;/th&gt;&lt;th&gt;is_bird_cute&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;125&lt;/td&gt;&lt;td&gt;POINT(x,y)&lt;/td&gt;&lt;td&gt;pigeon&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;But wait, there is also a family of crow next branch:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;id_tree&lt;/th&gt;&lt;th&gt;age&lt;/th&gt;&lt;th&gt;geom&lt;/th&gt;&lt;th&gt;bird_species&lt;/th&gt;&lt;th&gt;is_bird_cute&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;125&lt;/td&gt;&lt;td&gt;POINT(x,y)&lt;/td&gt;&lt;td&gt;pigeon&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;125&lt;/td&gt;&lt;td&gt;POINT(x,y)&lt;/td&gt;&lt;td&gt;crow&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;You quickly see the problem appearing, we duplicate as much data as we create new one. That’s where a little bit of modeling helps. A potential alternative could look like this:&lt;/p&gt;
&lt;noscript&gt;
    &lt;strong&gt;⚠️ JavaScript is required to render the diagram.&lt;/strong&gt;
&lt;/noscript&gt;
&lt;pre class=&quot;mermaid invertible-image&quot;&gt;
erDiagram
    tree ||--|| location : is_located
    tree {
        serial id PK
        int age
    }
    location {
        serial id_tree PK,FK
        geometry geom
    }
    tree ||--o{ bird : host
    bird {
        serial id_bird PK
        string species
        bool is_cute
        int id_tree FK
    }
&lt;/pre&gt;
&lt;p&gt;Where &lt;em&gt;trees&lt;/em&gt; have their own table, &lt;em&gt;birds&lt;/em&gt; as well and they are connected using a foreign key (FK). We create a table for trees &lt;em&gt;location&lt;/em&gt; as well; it’s not really necessary in that case since it is a 1:1 relationship, but it often appears that separating the information about an object and its geometries simplify many operations. It also shows that you can treat geometries like any other column in a data structure.&lt;/p&gt;
&lt;p&gt;The above tables should now look something like:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;tree&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;id&lt;/th&gt;&lt;th&gt;age&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;125&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;location&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;id_tree&lt;/th&gt;&lt;th&gt;geom&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;POINT(x,y)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;bird&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;id_bird&lt;/th&gt;&lt;th&gt;bird_species&lt;/th&gt;&lt;th&gt;is_cute&lt;/th&gt;&lt;th&gt;id_tree&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;pigeon&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;crow&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;If migration season comes and 200 bird families choose this tree as their home, we just need to add elements in the &lt;em&gt;bird&lt;/em&gt; table without duplicating any data regarding &lt;em&gt;trees&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Your data model should be independent from the tools you’ll be using to manipulate it. A client limitation shouldn’t question a well structured data; if the tool is limited, update it, or maybe it’s not the one you need for the job. How many time have I seen weird database designs (Postgres triggers I see you) because it was not possible to work with certain situations in QGIS or because of performance issues with certain designs.&lt;/p&gt;
&lt;p&gt;It’s a step for which you’ll be grateful when people ask to add some values, some attributes etc. Because if you’re data is well structured, you need to bring the change to a single place. But if the data structure didn’t follow some rules, you’ll probably have to change things in multiple locations, introducing much more space for error.&lt;/p&gt;
&lt;h3 id=&quot;give-roles-and-responsibilities&quot;&gt;Give roles and responsibilities&lt;/h3&gt;
&lt;p&gt;Who is going to modify the project? Who is going to modify the data? Should you give read-only access? Who is going to do the long term maintenance? Who should be able to propose changes? Some questions should be answered before starting to build the project.&lt;/p&gt;
&lt;p&gt;If we know who is responsible for what, decisions can be more efficient and implementation becomes most of the time a matter of minutes.&lt;/p&gt;
&lt;p&gt;You should know who is allowed to ask for modifications in a certain project, who should implement them and who is allowed to reject the modifications.&lt;/p&gt;
&lt;p&gt;But honestly, in short, have a project management.&lt;/p&gt;
&lt;h3 id=&quot;decompose-centralize-and-version-control-you-project&quot;&gt;Decompose, centralize and version control you project&lt;/h3&gt;
&lt;p&gt;Hun? Decompose and centralize? Sounds like there is a useless step here, but let me explain.&lt;/p&gt;
&lt;p&gt;A QGIS project is basically a big xml file bringing many elements together. It’s nice because everything (almost) is in the same place, but it also means that any little change influences everything, which could provoke side effects. And when it comes to modifications brought to layers it can become cumbersome to track.&lt;/p&gt;
&lt;p&gt;To mitigate that, QGIS allows to export many of its layer elements into separate files: symbols, forms, rendering, etc. Think about what will be modified regularly. You can export these elements as separate files to make sure that you focus on their changes and keep a specific history for them. Imagine you often have to modify the colors of certain layers; export the symbologies in .sld or .qml. Like this you will have the history of these files outside of the project and will be able to access older symbologies without changing the whole project.&lt;/p&gt;
&lt;p&gt;Regarding &lt;em&gt;version control&lt;/em&gt;, it seems obvious now but it’s not that easy when working with QGIS projects since the file is completely rewritten if you change anything and then save. But as said, a project is a combination of many things; there can be plugins, symbologies, profiles, etc. Making the effort of having everything in a single repository (even the result of your decomposition process) helps to understand what is needed and where, and by using a version control tool with it you can always come back to an earlier version in case of mistakes or breaking changes.&lt;/p&gt;
&lt;p&gt;When working with a version control platform (such as GitLab or GitHub), you can also bundle your versions into &lt;em&gt;releases&lt;/em&gt;, allowing to have everything needed in the project into a single archive, and accessible directly from an URL, which can help with the &lt;a href=&quot;https://blog.geothings.ch/blog/managing-shared-qgis-projects/#find-an-efficient-way-to-share-your-project&quot;&gt;sharing process&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;find-an-efficient-way-to-share-your-project&quot;&gt;Find an efficient way to share your project&lt;/h3&gt;
&lt;p&gt;I find it bizarrely difficult to share a project with people. As said right &lt;a href=&quot;https://blog.geothings.ch/blog/managing-shared-qgis-projects/#decompose-centralize-and-version-control-you-project&quot;&gt;above&lt;/a&gt;, a project is composed of multiple elements that work together: data, symbols, plugins, etc.&lt;/p&gt;
&lt;p&gt;There are some ways of avoiding loosing information when sharing a project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep all data in a single place: store your data in a database or a database file (e.g. Geopackage), it helps having everything in the same place to avoid &lt;em&gt;Non valid layers&lt;/em&gt; or losing information.&lt;/li&gt;
&lt;li&gt;Keep the number of plugins low: plugins are nice and they can help a lot, but any plugin added introduce uncertainty. They might not be updated, be usable with certain QGIS versions etc. In a more general sense, keep your project as simple and as clean as possible.&lt;/li&gt;
&lt;li&gt;Save the project file in database or database file. QGIS allows you to store the project in database, which makes it so much easier to share. You theoretically can have a single Geopackage holding all the data AND the project. You then just need to share the file to others and they have basically everything they need to work with the project.&lt;/li&gt;
&lt;li&gt;Maybe don’t share the project? Think outside of QGIS. If you start to do something in QGIS, you don’t have to stay in it. Do you really have to share the project or do you want to share the data inside it? Should the people you share with have a write access or not? There are so many tools available, maybe the task you’re trying to accomplish should not be done by sharing a QGIS project. Look into web-clients, file formats or any type of software that could reach the objective.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now after rereading, I realize that I wrote a weird mix of technical aspects and project management. There might be something that I need to learn from that. But the main message remains that the tool shouldn’t guide your objectives and the underlying structure. Find a way to articulate your work that makes sense to you and your team, and that allows you to be efficient in order to answer request as well as possible.&lt;/p&gt;
&lt;p&gt;Anyhow, QGIS is a very powerful tool: it’s easy to use, allows to accomplish a lot of things with spatial data and makes you feel like a genius. But the tool in itself shouldn’t replace a proper project management. Think about the objectives, the stakeholders, the responsibilities and make sure that everybody is on-board with the rules. It’s not because you can do anything with it that a project should host everything. Keep it clean and simple, one objective at a time, and find the method for sharing information that suits your need. With QGIS, a monolith project with a lot of people asking for modifications is a bad idea, at least from my experience.&lt;/p&gt;
&lt;p&gt;But in any case, it’s a amazing tool, support it.&lt;/p&gt;
&lt;p&gt;Cheers.&lt;/p&gt;
</content>
        <summary type="html">Thoughts and experience about how to collaborate using QGIS.</summary>
        </entry><entry xml:lang="en">
        <title>Geoservices with qgis-server</title>
        <published>2024-03-22T00:00:00+00:00</published>
        <updated>2024-03-22T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/geoservices-with-qgis-server/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/geoservices-with-qgis-server/</id>
        
            <content type="html">&lt;p&gt;While the geospatial world has its own particularities that sometimes make it difficult to get on-board or communicate with (see &lt;a rel=&quot;external&quot; href=&quot;https://www.geothings.ch/posts/on-gis/#the-spatial-exception&quot;&gt;the spatial exception&lt;/a&gt;), we have to admit that some tools and standards developed are both amazing and essential. Geoservices are crucial for the diffusion of georeferenced data, and they drastically changed the way we apprehend our localization; when was the last time you asked for direction?&lt;/p&gt;
&lt;p&gt;Being WMS, WMTS, WFS, WCS or any W*S, geoservices are everywhere and sometimes are a bit mysterious. But even without understanding the fundamentals of their definition, it’s rather easy to get started.&lt;/p&gt;
&lt;p&gt;We’ll see how to install a (geo)server that will be responsible for serving our data, quickly prepare some data, and even see how you can use these services to update your data safely. We’ll be using &lt;a rel=&quot;external&quot; href=&quot;https://docs.qgis.org/3.34/en/docs/user_manual/index.html&quot;&gt;qgis-server&lt;/a&gt; here, because it’s the most easy to use and the quickest way to get started.&lt;/p&gt;
&lt;h2 id=&quot;some-quick-context&quot;&gt;Some quick context&lt;/h2&gt;
&lt;p&gt;You’ve probably heard about the &lt;a rel=&quot;external&quot; href=&quot;https://www.ogc.org/&quot;&gt;Open Geospatial Consortium&lt;/a&gt; (OGC), but to make it really short, if you hear about a standard regarding geospatial, the OGC is probably behind. At least it’s the case for the geoservices we’ll be diving into here.&lt;/p&gt;
&lt;p&gt;And what are those geoservices? Well most of the time when we talk about “geoservice” we’re referring to WMS, WMTS, WFS, WCS. They are a standardized way of accessing georeferenced data through the internet. The same way you reach a website or an API using an URL, you can access some geographic data if you know some URL (and have the tools that go with it). You can then use the data for your use-case: displaying a background map in your app, showing queryable data for an interactive webmap etc. The most common are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web Map Service (&lt;strong&gt;WMS&lt;/strong&gt;) –&amp;gt; probably the initial service. Used to display an image as .jpg or .png (see it as a background map)&lt;/li&gt;
&lt;li&gt;Web Map Tiled Service (&lt;strong&gt;WMTS&lt;/strong&gt;) –&amp;gt; the more efficient WMS. It’s the same, but images are tiled to restricted the amount of data to be transferred to the client based on the display localization and the zoom level.&lt;/li&gt;
&lt;li&gt;Web Feature Service (&lt;strong&gt;WFS&lt;/strong&gt;) –&amp;gt; the vector service. You can work on the data as vector, request features on the map, and even add, modify or delete features.&lt;/li&gt;
&lt;li&gt;OGC API-Feature (&lt;strong&gt;OGC-APIF&lt;/strong&gt;) –&amp;gt; the more “web standard” WFS, also known as &lt;strong&gt;WFS3&lt;/strong&gt;. Features can be accessed individually through an URL. The OGC states that
&lt;blockquote&gt;
&lt;p&gt;The API building blocks specified in this standard are consistent with the architecture of the Web.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Web Coverage Service (&lt;strong&gt;WCS&lt;/strong&gt;) –&amp;gt; at the time of writing I’ve never used this standard so I won’t go into details, but from my reading it’s a service close to the WMS standard but providing more information than just an image, allowing more complex interactions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are other existing services: vector tiles, WSC, WMC etc. But I either never encountered them or they usually are implemented by other tools than the common geoservers.&lt;/p&gt;
&lt;h2 id=&quot;ok-let-s-get-started&quot;&gt;Ok let’s get started&lt;/h2&gt;
&lt;p&gt;It’s nice and all, but how do we serve a W*S. Well for starters we need a server. Here we’ll be using &lt;a rel=&quot;external&quot; href=&quot;https://docs.qgis.org/3.34/en/docs/server_manual/index.html&quot;&gt;qgis-server&lt;/a&gt; with &lt;a rel=&quot;external&quot; href=&quot;https://nginx.org/en/&quot;&gt;nginx&lt;/a&gt;. The choice for qgis-server is motivated by the simplicity of use, you serve a qgis project. The choice for nginx, well the doc mentions it and it seems very lightweigth. I’m by no mean a system administrator so bear with me as I’ll read the doc with you.&lt;/p&gt;
&lt;h3 id=&quot;install-and-configure-qgis-server-and-nginx&quot;&gt;Install and configure qgis-server and nginx&lt;/h3&gt;
&lt;p&gt;I’m on Ubuntu so I’ll be using &lt;code&gt;apt&lt;/code&gt;, and the installation includes the use of &lt;code&gt;systemd&lt;/code&gt;. I’ll also be working with the &lt;a rel=&quot;external&quot; href=&quot;https://ppa.launchpadcontent.net/ubuntugis/ppa/ubuntu/&quot;&gt;ubuntugis ppa&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First add the ppa:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;add-apt-repository&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; ppa:ubuntugis&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install qgis-server:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install qgis-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install nginx if not already present:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;configure-qgis-server&quot;&gt;Configure qgis-server&lt;/h4&gt;
&lt;p&gt;We’ll then follow instructions from the &lt;a rel=&quot;external&quot; href=&quot;https://docs.qgis.org/3.34/en/docs/server_manual/getting_started.html#systemd&quot;&gt;QGIS documentation&lt;/a&gt; for a qgis-server installation using systemd and nginx. The method defines a socket unit and a service unit as follow. The sockets will be the communication channel between our http server (nginx) and our application server (qgis-server). Create the socket file &lt;code&gt;/etc/systemd/system/qgis-server@.socket&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Description=QGIS Server Listen Socket (instance %i)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Socket]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Accept=false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ListenStream=/var/run/qgis-server-%i.sock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SocketUser=www-data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SocketGroup=www-data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SocketMode=0600&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;WantedBy=sockets.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can start these sockets with the following command:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;for&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; in&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; 1 2 3 4&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; do&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; systemctl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; enable --now qgis-server@&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;.socket&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; done&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which will start 4 sockets, through which our http server will be able to pass requests to our qgis-server.&lt;/p&gt;
&lt;p&gt;Now for the services. Services are managed by systemd and in our case are responsible for the execution of the qgis-server. Create the file &lt;code&gt;/etc/systemd/system/qgis-server@.service&lt;/code&gt; and add:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Description=QGIS Server Service (instance %i)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;User=www-data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Group=www-data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;StandardOutput=null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;StandardError=journal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;StandardInput=socket&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ExecStart=/usr/lib/cgi-bin/qgis_mapserv.fcgi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;EnvironmentFile=/etc/qgis-server/env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;WantedBy=multi-user.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that the server is configured using a &lt;code&gt;env&lt;/code&gt; file, here &lt;code&gt;/etc/qgis-server/env&lt;/code&gt;. Finally, in the same manner as the sockets, start the 4 services with the following:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;for&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; in&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; 1 2 3 4&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; do&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; systemctl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; enable --now qgis-server@&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;.service&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; done&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Services are depending on sockets, you’ll need to start the sockets first then the services.
{: .prompt-warning }&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;configure-nginx&quot;&gt;Configure nginx&lt;/h4&gt;
&lt;p&gt;We’ll then configure nginx for our future qgis-server using &lt;a rel=&quot;external&quot; href=&quot;https://en.wikipedia.org/wiki/FastCGI&quot;&gt;fastcgi&lt;/a&gt; by creating the file &lt;code&gt;/etc/nginx/sites-enabled/qgis-server&lt;/code&gt; and adding the following:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;nginx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;upstream&lt;/span&gt;&lt;span&gt; qgis-server_backend {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   server&lt;/span&gt;&lt;span&gt; unix:/var/run/qgis-server-1.sock;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   server&lt;/span&gt;&lt;span&gt; unix:/var/run/qgis-server-2.sock;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   server&lt;/span&gt;&lt;span&gt; unix:/var/run/qgis-server-3.sock;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   server&lt;/span&gt;&lt;span&gt; unix:/var/run/qgis-server-4.sock;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   listen&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 8080&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   access_log&lt;/span&gt;&lt;span&gt;  /var/log/nginx/qgis-server/access.log&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   error_log&lt;/span&gt;&lt;span&gt;  /var/log/nginx/qgis-server/error.log&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;   location&lt;/span&gt;&lt;span&gt; /qgis-server {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;       gzip&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; off&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;       include&lt;/span&gt;&lt;span&gt; fastcgi_params&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;       fastcgi_pass&lt;/span&gt;&lt;span&gt; qgis-server_backend&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration will then be included by the nginx.conf file using the &lt;code&gt;include /etc/nginx/sites-enabled/*;&lt;/code&gt; statement.&lt;/p&gt;
&lt;h3 id=&quot;create-a-qgis-project-and-configure-it&quot;&gt;Create a QGIS project and configure it&lt;/h3&gt;
&lt;p&gt;Now it’s time to decide what we want to expose. I have some OSM data in a PostgreSQL database (see &lt;a rel=&quot;external&quot; href=&quot;https://www.geothings.ch/posts/vector-tiles-with-martin/#downloading-some-data&quot;&gt;Serving vector tiles with Martin&lt;/a&gt;) so I’ll use that.&lt;/p&gt;
&lt;p&gt;Create a project, add some data and apply the desired style. A quick example is shown below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/geoservices-with-qgis-server/qgis-data-prepared.png&quot; alt=&quot;data-with-style&quot; /&gt;
&lt;em&gt;Data with some style&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you’re satisfied with the look of your data, you’ll need to configure the project so it will tell qgis-server to proceed with the geoservice you want to serve.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;em&gt;Project -&amp;gt; Properties -&amp;gt; QGIS Server&lt;/em&gt; and complete the configuration as you wish.&lt;/li&gt;
&lt;li&gt;Under &lt;em&gt;Service Capabilities&lt;/em&gt;, check the &lt;em&gt;Enable Service Capabilities&lt;/em&gt; box.&lt;/li&gt;
&lt;li&gt;Select the different services you want to enable under the different tabs.&lt;/li&gt;
&lt;li&gt;Save your project under a folder accessible by the server user (&lt;code&gt;www-data&lt;/code&gt;). If you’re using file data in your project, such as a geopackage, save them in the same place.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you setup a WFS, the tab should look like something like below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/geoservices-with-qgis-server/setup-wfs.png&quot; alt=&quot;wfs-setup&quot; /&gt;
&lt;em&gt;Data with some style&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;admonition warning&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-warning&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;WARNING&lt;/strong&gt;
&lt;p&gt;Make sure that your tables have a primary key. Otherwise the &lt;em&gt;update&lt;/em&gt; and &lt;em&gt;delete&lt;/em&gt; operations won’t be possible.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;admonition warning&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-warning&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;WARNING&lt;/strong&gt;
&lt;p&gt;And also, be sure that you don’t have special characters in you column or table names. It messes up the WFS 1.0 and 1.1 requests.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can then edit the &lt;code&gt;env&lt;/code&gt; file with the corresponding environment variables. For me it looks like:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;QGIS_PROJECT_FILE=/etc/qgis-server/my_project.qgz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;QGIS_SERVER_LOG_STDERR=1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;QGIS_SERVER_LOG_LEVEL=3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use PostgreSQL data and have had troubles getting the server to reach the data, even on localhost but finally managed to have everything working with the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make the project read the data using a &lt;a rel=&quot;external&quot; href=&quot;https://www.postgresql.org/docs/current/libpq-pgservice.html&quot;&gt;PostgreSQL service&lt;/a&gt; (create a pg_service.conf if not existing).&lt;/li&gt;
&lt;li&gt;Copy the pg_service.conf file somewhere accessible for the server.&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;/etc/nginx/fastcgi_params&lt;/code&gt; and add &lt;code&gt;fastcgi_param  PGSERVICEFILE /path/to/service/pg_service.conf;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Restart everything.&lt;/li&gt;
&lt;li&gt;Bonus: &lt;code&gt;touch&lt;/code&gt; the project file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If everything works correctly, you should be able to reach &lt;a rel=&quot;external&quot; href=&quot;http://localhost:8080/qgis-server/qgis_mapserv.fcgi?SERVICE=WMS&amp;amp;VERSION=1.3.0&amp;amp;REQUEST=GetCapabilities&quot;&gt;localhost:8080/qgis-server/qgis_mapserv.fcgi?SERVICE=WMS&amp;amp;VERSION=1.3.0&amp;amp;REQUEST=GetCapabilities&lt;/a&gt; for WMS capabilities or &lt;a rel=&quot;external&quot; href=&quot;http://localhost:8080/qgis-server/wfs3/&quot;&gt;http://localhost:8080/qgis-server/wfs3/&lt;/a&gt; for the OGC-APIF description and navigate the different endpoints defined in the OGC-APIF standard.&lt;/p&gt;
&lt;p&gt;You should now be able to also call these URL from QGIS using the correct url. Try to add a WFS/OGC-APIF using the http://localhost:8080/qgis-server/wfs3/ url (qgis-server in this example, but you can setup your server with a different url).&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going further&lt;/h2&gt;
&lt;p&gt;We have the basics setup, now what can we do? Let’s start by alter data.&lt;/p&gt;
&lt;h3 id=&quot;updating-data-through-a-wfs&quot;&gt;Updating data through a WFS&lt;/h3&gt;
&lt;p&gt;If at this point you have everything working, there shouldn’t be much to do to update data through your newly build WFS. Working with the OGC-APIF you’ll be able to add, delete and update data using URL like you would do with any REST API.&lt;/p&gt;
&lt;p&gt;You’ll find the description of your API at the URL &lt;code&gt;http://localhost:8080/qgis-server/wfs3/api&lt;/code&gt; (for this particular setup) and it should look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/geoservices-with-qgis-server/api-description.png&quot; alt=&quot;api-description&quot; /&gt;
&lt;em&gt;OpenAPI description of an OGC-APIF&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This gives you an idea of the available endpoints to manipulate data.&lt;/p&gt;
&lt;p&gt;To try this, let’s try to &lt;code&gt;GET&lt;/code&gt; one of our feature. You can access a single feature through a &lt;code&gt;collection&lt;/code&gt; and an &lt;code&gt;item&lt;/code&gt;. For me, the URL &lt;a rel=&quot;external&quot; href=&quot;http://localhost:8080/qgis-server/wfs3/collections/planet_osm_polygon/items/2102736&quot;&gt;http://localhost:8080/qgis-server/wfs3/collections/planet_osm_polygon/items/2102736&lt;/a&gt; will point to the Lausanne train station:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/geoservices-with-qgis-server/lausanne-gare.png&quot; alt=&quot;api-description&quot; /&gt;
&lt;em&gt;Getting an item using its dedicated URL&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And the same can be done using &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt; or any network utility you wish:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --request &amp;quot;GET&amp;quot; &amp;quot;localhost:8080/qgis-server/wfs3/collections/planet_osm_polygon/items/2102736&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; jq&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will print something like:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;bbox&amp;quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    6.627779,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    46.51671,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    6.630578,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    46.517298&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;geometry&amp;quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;quot;coordinates&amp;quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          6.627779,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          46.517099&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          6.628225,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          46.517038&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          6.628344,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          46.51702&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;admonition tip&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-tip&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;TIP&lt;/strong&gt;
&lt;p&gt;The piping into &lt;code&gt;jq&lt;/code&gt; (&lt;code&gt;| jq .&lt;/code&gt;) is just for printing purpose, it doesn’t participate in the query.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now let’s delete this object:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --request &amp;quot;DELETE&amp;quot; &amp;quot;localhost:8080/qgis-server/wfs3/collections/planet_osm_polygon/items/2102736&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it!&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --request &amp;quot;GET&amp;quot; &amp;quot;localhost:8080/qgis-server/wfs3/collections/planet_osm_polygon/items/2102736&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;now returns:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Internal server error&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Invalid feature [2102736]&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can do exactly the same using QGIS. Add your WFS as a layer and work on it like any vector data: add, update ,delete features and then save.&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-tip&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;TIP&lt;/strong&gt;
&lt;p&gt;I intended to use a PATCH request to update a single attribute, but couldn’t make it work. QGIS seems to systematically call a POST request, even for a single attribute change. If you now how to work with PATCH in this environment let me know.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&quot;protect-our-service-with-authentication&quot;&gt;Protect our service with authentication&lt;/h3&gt;
&lt;p&gt;Accessing information through an URL means that many client might have access to it as well. It’s important to keep in mind that authentication would be necessary if you expect to update information through this API.&lt;/p&gt;
&lt;p&gt;Again, I’m not a system administration or a security expert, but I’ll try to show how to setup a very basic authentication directly using &lt;code&gt;nginx&lt;/code&gt;. &lt;strong&gt;This is not how you secure an API and it won’t replace a modern and secure authentication setup&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I’ll follow the &lt;a rel=&quot;external&quot; href=&quot;https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/&quot;&gt;nginx documention&lt;/a&gt; for that.&lt;/p&gt;
&lt;p&gt;First install &lt;code&gt;apache2-utils&lt;/code&gt; if your on a Debian based distribution:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; apt install apache2-utils&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a password file and a user:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; htpasswd -c /etc/apache2/.htpasswd user1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;nginx&lt;/code&gt; configuration, under the location of your API, add a name and specify that you’ll be using a &lt;code&gt;auth_basic_user_file&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;location /qgis-server {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    auth_basic           &amp;quot;Protected Area&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    auth_basic_user_file /etc/apache2/.htpasswd;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart &lt;code&gt;nginx&lt;/code&gt; for good measure and that it, try to reach your WFS using an URL or with QGIS and you should see a connection popup like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/geoservices-with-qgis-server/connexion.png&quot; alt=&quot;connection&quot; /&gt;
&lt;em&gt;The route now needs basic authentication to be reached&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Services are a powerful tool to be aware of when working with geographical data. They allow to efficiently share them at a larger scale through the web and even, as we saw, update them. There are many ways to build these services, and &lt;code&gt;qgis-server&lt;/code&gt; is one solution among others, but it has the advantage to work (almost) out of the box with potential QGIS projects that you could already have: a few tweaks and you can be going.&lt;/p&gt;
&lt;p&gt;The possibility to give access to your data through an URL makes sharing them so easy; it reduces the amount of manipulation and tools required, you don’t need to maintain a data extractor or repositories. Just some data and a server.&lt;/p&gt;
</content>
        <summary type="html">Use QGIS as a configuration for your services.</summary>
        </entry><entry xml:lang="en">
        <title>Opening and writing raster dataset with GDAL Rust</title>
        <published>2024-03-08T00:00:00+00:00</published>
        <updated>2024-03-08T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/rust-gdal/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/rust-gdal/</id>
        
            <content type="html">&lt;p&gt;I’ve been trying to learn Rust lately. For some reason there is something that I find very appealing in this language. From all I could read, it’s very efficient in resources and memory, it’s fast, and it doesn’t let you mess around with memory. Regarding performances, it places itself next to C and C++, and when looking into the world of language performances, I learned that Python was kind of slow, around 100x slower according to some benchmarks. Obviously this value in itself doesn’t mean much, and many more aspects of a language must be taken account when choosing one for the task, but this information kind of planted its seed. It might be because I have a problem with inefficient and overly complicated process, but I find the idea to work with a system language very elegant.&lt;/p&gt;
&lt;p&gt;But we’re not here to talk about the Rust language in itself. We’ll see how to read and write raster data using the Rust binding for GDAL. Also, this is about how I tried to compare the Rust and Python bindings for GDAL and how I learned that it doesn’t change a thing in terms of performance.&lt;/p&gt;
&lt;h2 id=&quot;wait-what&quot;&gt;Wait what?&lt;/h2&gt;
&lt;p&gt;Well I started by thinking “Oh I can’t wait to see how faster GDAL in Rust is compared to Python”. So I started by reading a raster file and print some basic information. I use a band from a &lt;a rel=&quot;external&quot; href=&quot;https://browser.dataspace.copernicus.eu/&quot;&gt;Sentinel-2 image&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;how-to-open-a-raster-file&quot;&gt;How to open a raster file?&lt;/h3&gt;
&lt;p&gt;Let’s do it. We’ll be working with the GDAL crate from &lt;a rel=&quot;external&quot; href=&quot;https://crates.io/crates/gdal&quot;&gt;crates.io&lt;/a&gt; (&lt;code&gt;cargo add gdal&lt;/code&gt;) and the GDAL package prom &lt;a rel=&quot;external&quot; href=&quot;https://pypi.org/project/GDAL/&quot;&gt;PyPi&lt;/a&gt; (&lt;code&gt;pip install GDAL&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;In Rust:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; gdal&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt;Dataset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Open dataset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt; Dataset&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;IMG.jp2&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;This raster has &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; bands.&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;raster_count&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Read band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; band&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;rasterband&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Print statistics&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if let&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt; Some&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;stats&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; band&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;get_statistics&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-11&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    println!&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;[ STATS ] =  Minimum=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;, Maximum=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;, Mean=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;, StdDev=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        stats&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; stats&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; stats&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span&gt;mean&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; stats&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span&gt;std_dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;admonition warning&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-warning&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;WARNING&lt;/strong&gt;
&lt;p&gt;I’m using a lot of .unwrap(), which in very short will cause the code to panic if a problem occurs, this is for a quick and dirty test. Within a “production” environment you would handle errors and stuff.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And in Python:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;from&lt;/span&gt;&lt;span&gt; osgeo&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; import&lt;/span&gt;&lt;span&gt; gdal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gdal&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;UseExceptions&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Open dataset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dataset&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; gdal&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;IMG.jp2&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nband&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; dataset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;RasterCount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;This raster contains &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;nband&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; bands.&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Read band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srcband&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; dataset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetRasterBand&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Print statistics&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;stats&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; srcband&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetStatistics&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; True&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; True&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;[ STATS ] =  Minimum=%.3f, Maximum=%.3f, Mean=%.3f, StdDev=%.3f&amp;#39;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    %&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; stats&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; stats&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; stats&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; stats&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;] ))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;File opened and information read. We can compare their runtime.&lt;/p&gt;
&lt;h3 id=&quot;comparing-performances&quot;&gt;Comparing performances&lt;/h3&gt;
&lt;p&gt;Building the Rust program, then running and timing these two codes multiple times, we get results like follows:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rust&lt;/th&gt;&lt;th&gt;Python&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;real    &lt;strong&gt;0m0.072s&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;real    &lt;strong&gt;0m0.156s&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;user    0m0.154s&lt;/td&gt;&lt;td&gt;user    0m0.752s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;sys     0m0.275s&lt;/td&gt;&lt;td&gt;sys     0m1.689s&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;admonition warning&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-warning&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;WARNING&lt;/strong&gt;
&lt;p&gt;This is a naive approach on testing and comparing performances, not a full-fledged benchmark.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Twice as fast without any processing or multi-threading, at that point I though “Oh it’s gonna be good”.&lt;/p&gt;
&lt;h2 id=&quot;let-s-add-some-writing-then&quot;&gt;Let’s add some writing then&lt;/h2&gt;
&lt;p&gt;It’s only the start, we’ll now read the data in our raster dataset and, since my source is in .jp2, write it in a new raster dataset in the GeoTiff format.&lt;/p&gt;
&lt;h3 id=&quot;how-to-write-a-raster-dataset-with-gdal-bindings&quot;&gt;How to write a raster dataset with GDAL bindings?&lt;/h3&gt;
&lt;p&gt;We will be using the Create() approach rather than the CreateCopy() (see the nuance in the &lt;a rel=&quot;external&quot; href=&quot;https://gdal.org/tutorials/raster_api_tut.html#using-createcopy&quot;&gt;doc&lt;/a&gt;). Geospatialized rasters come with series of metadata and overviews. The CreateCopy() automatically takes those from the source dataset and copy them into the destination. While this obviously simplifies the work if we don’t need to bring any change to these information, using Create() helps to better understand the structure of a raster. Also I have a quite important performance difference between the two and I’ve found Create() more efficient for my use case.&lt;/p&gt;
&lt;p&gt;So the code now becomes as follow.&lt;/p&gt;
&lt;p&gt;In Rust:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; gdal&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt;Dataset&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt; DriverManager&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Open dataset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt; Dataset&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;IMG.jp2&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;This raster has &lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; bands.&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;raster_count&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Get source dataset metadata&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; projection&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;projection&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; spatial_ref&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;spatial_ref&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; geo_transform&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;geo_transform&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; (&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;raster_size&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Read band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; band&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;rasterband&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Get GeoTiff driver  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; d&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt; DriverManager&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;get_driver_by_name&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;GTiff&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Create the destination dataset as mutable with 1 band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let mut&lt;/span&gt;&lt;span&gt; dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; d&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;create_with_band_type&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; _&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;IMG_as_tiff.gtiff&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    x&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; as isize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; as isize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Set metadata from source dataset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;set_geo_transform&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;geo_transform&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;set_projection&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;projection&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;set_spatial_ref&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;spatial_ref&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Get the destination band as mutable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let mut&lt;/span&gt;&lt;span&gt; dst_band&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;rasterband&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Read the source data as Buffer&amp;lt;u16&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;let&lt;/span&gt;&lt;span&gt; buf&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; band&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;read_as&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;z-5&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;), (&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;), (&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt; None&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Write the data in the destination band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_band&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;), (&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;buf&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// Close dataset for good conscience&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in Python:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Open dataset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; gdal&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;IMG.jp2&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nband&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;RasterCount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;This raster has &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;nband&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; bands.&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Get dataset metadata&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srcprojection&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetProjection&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srcspatialref&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetSpatialRef&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srcgeotransform&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetGeoTransform&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;xsize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; ysize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; (&lt;/span&gt;&lt;span&gt;ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;RasterXSize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;RasterYSize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Get GeoTiff driver&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fileformat&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;GTiff&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;driver&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; gdal&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetDriverByName&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;fileformat&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Create the destination dataset with 1 band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; driver&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;IMG_as_tiff.gtiff&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; xsize&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;xsize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; ysize&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;ysize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;                    bands&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;nband&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; eType&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;gdal&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;GDT_UInt16&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Set metadata from source dataset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;SetProjection&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;srcprojection&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;SetGeoTransform&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;srcgeotransform&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;SetSpatialRef&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;srcspatialref&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Get the destination band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srcband&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetRasterBand&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Read the source data as Array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srcdata&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; srcband&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;ReadAsArray&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Write the data in the destination band&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;GetRasterBand&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;WriteArray&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;srcdata&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Close dataset for good conscience&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dst_ds&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; None&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both codes should create a new raster file that resembles our source, but in GeoTiff. And that’s it, you can convert a raster file into any format. We’re just scratching the surface, you can subset data, reproject, transpose it if you want etc.&lt;/p&gt;
&lt;h3 id=&quot;but-let-s-compare-performances-again&quot;&gt;But let’s compare performances again&lt;/h3&gt;
&lt;p&gt;Now as “la cerise sur le gâteau”, let’s see how powerful Rust can be.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rust&lt;/th&gt;&lt;th&gt;Python&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;real    &lt;strong&gt;0m1.024s&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;real    &lt;strong&gt;0m1.133s&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;user    0m17.150s&lt;/td&gt;&lt;td&gt;user    0m17.335s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;sys     0m1.629s&lt;/td&gt;&lt;td&gt;sys     0m2.089s&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Wait let me rerun.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rust&lt;/th&gt;&lt;th&gt;Python&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;real    &lt;strong&gt;0m1.053s&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;real    &lt;strong&gt;0m1.141s&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;user    0m17.076s&lt;/td&gt;&lt;td&gt;user    0m17.324s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;sys     0m1.608s&lt;/td&gt;&lt;td&gt;sys     0m2.134s&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Hmm.&lt;/p&gt;
&lt;h2 id=&quot;wait-what-bis&quot;&gt;Wait what? (bis)&lt;/h2&gt;
&lt;p&gt;Well that’s not what I expected.&lt;/p&gt;
&lt;p&gt;In the process I forgot something important, we are working with &lt;strong&gt;bindings&lt;/strong&gt;. Asking &lt;a rel=&quot;external&quot; href=&quot;https://en.wikipedia.org/wiki/Language_binding&quot;&gt;Wikipedia&lt;/a&gt; what a binding is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In programming and software design, binding is an application programming interface (API) that provides glue code specifically made to allow a programming language to use a foreign library or operating system service (one that is not native to that language).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So from my understanding, working with Rust or Python or any other language bindings is just working with the API that let us call the underlying code, for GDAL written in C and C++. Thus the overhead that we generate is only the interaction with the library, not during the process itself, since it’s running C/C++ code. The difference that we observe is probably the same that we were observing when we just opened a file.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;GDAL raster processing is &lt;strong&gt;very&lt;/strong&gt; similar in Rust and Python. When working with its bindings, it doesn’t change much which language you use &lt;strong&gt;to perform the GDAL operations in themself&lt;/strong&gt;, since in any case you’re running C/C++ code from your program. But everything that is around need to be considered when choosing the right tool. If your program needs multi-threading then you’l probably chose Rust, if you need quick development without much optimization then Python might be a best fit. But it’s comfortable to see that the GDAL processing time won’t change significantly.&lt;/p&gt;
&lt;p&gt;I’m not at all trying to say that Rust is not worth it, I’m excited to go further into it and work on projects, but it remains a tool and there should be no injunction to use it if it doesn’t bring much to the table for your use case.&lt;/p&gt;
&lt;p&gt;Also it’s totally possible that I totally misunderstood something and that those comparisons are meaningless, happy to learn more about it.&lt;/p&gt;
</content>
        <summary type="html">Some first tries at using the GDAL/OGR binding in Rust 🦀</summary>
        </entry><entry xml:lang="en">
        <title>PMTiles exploration</title>
        <published>2024-02-02T00:00:00+00:00</published>
        <updated>2024-02-02T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/pmtiles-introduction/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/pmtiles-introduction/</id>
        
            <content type="html">&lt;p&gt;It’s a technology that I came across rather recently. Tile generation when maintaining a geographic oriented website can be a hassle: it can take time, generates a lot of files, and can be a critical process. PMTiles might have the potential to facilitate some of this work and we’ll try to see how.&lt;/p&gt;
&lt;h2 id=&quot;what-are-pmtiles&quot;&gt;What are PMTiles?&lt;/h2&gt;
&lt;p&gt;In itself, PMTiles is a specification for building single-file tile pyramids. It’s open source and part of a larger project called &lt;a rel=&quot;external&quot; href=&quot;https://protomaps.com/&quot;&gt;Protomaps&lt;/a&gt;, and it aims at mapping the World, just that. The major difference with the kind of tiles that I know is that they are embedded as a single file, like the MBTile format, which make the interaction very intuitive, I mean we all get the idea of what a file is. But what does it bring compared to MBTiles? Well from the Protomaps website:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A MBTiles file needs to be accessed as a SQLite database on disk, so requires a running server and disk storage for cloud hosting. &lt;strong&gt;PMTiles is designed to be cloud-native&lt;/strong&gt; and readable in fine-grained parts from remote storage like Amazon S3.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It aims at simplifying the manipulation of tiles in a cloud native approach, for worldwide coverage.&lt;/p&gt;
&lt;h2 id=&quot;how-to-create-pmtiles&quot;&gt;How to create PMTiles?&lt;/h2&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https://docs.protomaps.com/pmtiles/create&quot;&gt;Protomaps website&lt;/a&gt; gives us some tools to create these tiles. Let’s try some possibilities.&lt;/p&gt;
&lt;h3 id=&quot;generate-pmtiles-using-gdal&quot;&gt;Generate PMTiles using GDAL&lt;/h3&gt;
&lt;p&gt;Our &lt;a rel=&quot;external&quot; href=&quot;https://www.geothings.ch/posts/gdal-appreciation/&quot;&gt;Swiss Army Knife&lt;/a&gt; is back, starting version 3.8.0 GDAL has native support for PMTiles, so let’s try that.&lt;/p&gt;
&lt;p&gt;First check the GDAL version you’re using, you’ll want 3.8.0 or higher:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gdalinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --version&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;--&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; GDAL 3.8.3, released 2024/01/04&lt;/span&gt;&lt;span&gt; (debug&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; build&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s start from an OSM &lt;code&gt;.pbf&lt;/code&gt; file (see &lt;a rel=&quot;external&quot; href=&quot;https://www.geothings.ch/posts/vector-tiles-with-martin/#downloading-some-data&quot;&gt;Serving vector tiles with Martin&lt;/a&gt;). To convert this file into a PMTiles file you can enter:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogr2ogr&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -f PMTiles out.pmtiles switzerland-latest.osm.pbf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The process might take some time if you’re working on a large dataset. By default the MAXZOOM is 5, which is rather low, so the run shouldn’t take to long, but if you start to increase the parameter to around 15, which allows to represent data from much closer, processing time increases.&lt;/p&gt;
&lt;p&gt;But once the command is done, well that’s it!&lt;/p&gt;
&lt;p&gt;Alternatively we can use &lt;a rel=&quot;external&quot; href=&quot;https://github.com/felt/tippecanoe&quot;&gt;tippecanoe&lt;/a&gt; to achieve the same result.&lt;/p&gt;
&lt;h3 id=&quot;generate-pmtiles-using-tippecanoe&quot;&gt;Generate PMTiles using tippecanoe&lt;/h3&gt;
&lt;div class=&quot;admonition warning&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-warning&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;WARNING&lt;/strong&gt;
&lt;p&gt;Under construction&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;visualize-pmtiles&quot;&gt;Visualize PMTiles&lt;/h2&gt;
&lt;p&gt;The easiest way is to go to the &lt;a rel=&quot;external&quot; href=&quot;https://protomaps.github.io/PMTiles/&quot;&gt;PMTiles Viewer Page&lt;/a&gt; and upload your newly created PMTiles file.&lt;/p&gt;
&lt;p&gt;Obviously you can add it to your custom project. The easiest way of doing is by using the Leaflet plugin &lt;a rel=&quot;external&quot; href=&quot;https://github.com/protomaps/protomaps-leaflet&quot;&gt;protomaps-leaflet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Starting from complete scratch you can create an &lt;code&gt;.html&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;&amp;lt;!DOCTYPE&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; html&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; charset&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; name&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; content&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;width=device-width, initial-scale=1&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; rel&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; href&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;https://unpkg.com/leaflet@1.7.1/dist/leaflet.css&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; src&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;https://unpkg.com/leaflet@1.7.1/dist/leaflet.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; src&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;https://unpkg.com/protomaps-leaflet@latest/dist/protomaps-leaflet.min.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;        body&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        #&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;            height&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 100&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;vh&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;            margin&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;px&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Hey I&amp;#39;m a custom page&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Let&amp;#39;s add some Leaflet and PMTiles.&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;z-10&quot;&gt; id&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;map&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        let&lt;/span&gt;&lt;span&gt; PAINT_RULES&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                dataLayer&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;multipolygons&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt; //here we render only one layer, check your layers names &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                symbolizer&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-9 z-b&quot;&gt; new&lt;/span&gt;&lt;span&gt; protomapsL&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;PolygonSymbolizer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;span&gt; fill&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;steelblue&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ]&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        const&lt;/span&gt;&lt;span&gt; map&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; L&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;map&amp;#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;setView&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;46.5&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 6.6&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 13&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt; //chose a view that suits you&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        var&lt;/span&gt;&lt;span&gt; osm&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; L&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;tileLayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;https://tile.openstreetmap.org/{z}/{x}/{y}.png&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            maxZoom&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 19&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            attribution&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;&amp;amp;copy; &amp;lt;a href=&amp;quot;http://www.openstreetmap.org/copyright&amp;quot;&amp;gt;OpenStreetMap&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;addTo&lt;/span&gt;&lt;span&gt;(map)&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        var&lt;/span&gt;&lt;span&gt; layer&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; protomapsL&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;leafletLayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                url&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;http://127.0.0.1:8080/your_pmtile_file.pmtiles&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                paint_rules&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span&gt; PAINT_RULES&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;addTo&lt;/span&gt;&lt;span&gt;(map)&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For testing purposes you might have to serve you PMTiles through http. To do so you can do as the Protomaps website suggests:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;npm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install -g http-server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Then from the folder containing your PMTiles.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;http-server&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; . --cors&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open your &lt;code&gt;.html&lt;/code&gt; file on the browser of your choice and you should see the map with your PMTiles displayed.&lt;/p&gt;
&lt;h2 id=&quot;when-should-pmtiles-be-used&quot;&gt;When should PMTiles be used?&lt;/h2&gt;
&lt;p&gt;Again, the documentation on &lt;a rel=&quot;external&quot; href=&quot;https://protomaps.com/faq&quot;&gt;Protomaps website&lt;/a&gt; is quite complete and gives us some insights.&lt;/p&gt;
&lt;h3 id=&quot;cloud-hosting&quot;&gt;Cloud hosting&lt;/h3&gt;
&lt;p&gt;PMTiles are stored as a single file, and compression is so efficient that the size is ridiculous compared to classic png tiles. Number of requests necessary is drastically reduced and thus the cost of operation as well.&lt;/p&gt;
&lt;h3 id=&quot;static-data&quot;&gt;Static data&lt;/h3&gt;
&lt;p&gt;PMTiles don’t support localized updates so each time you’ll need to apply an update, the whole file needs to be rewritten. If your data don’t change that much PMTiles are a wise choice, but If you need to have a more dynamic rendering the documentation recommends PostGIS with the &lt;a rel=&quot;external&quot; href=&quot;https://postgis.net/docs/ST_AsMVT.html&quot;&gt;ST_As_MVT()&lt;/a&gt; function.&lt;/p&gt;
&lt;h3 id=&quot;simplicity&quot;&gt;Simplicity&lt;/h3&gt;
&lt;p&gt;Here it’s more like a feeling about the philosophy of the specification. You have a single file, accessible without introduced complexity, directly from a library like Leaflet, which itself has &lt;em&gt;simplicity&lt;/em&gt; as one of its core objectives. It looks like one of those project that allows you to make complicated-looking things so easily. I guess it’s a sign of a good project.&lt;/p&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final words&lt;/h2&gt;
&lt;p&gt;Well it was rather easy to discover. I mean we create a file, we basically load it within a project and we’re good to go. Obviously there are some customizations to do with styling and labels etc. But once you have the starting point and your tools, it takes minutes to create what you desire.&lt;/p&gt;
&lt;p&gt;Here we’re just scratching the surface, remains to explore filtering, styling etc. But the concept in itself and the unreasonable performance of the compression make it very interesting if you’re in the situation where PMTiles could give you an advantage; the file created for OSM data Switzerland, with zooms from 0 to 14, is around 1GB, it costs pennies to upload and host on cloud services.&lt;/p&gt;
&lt;p&gt;It’s a project to support and look for, I guess?&lt;/p&gt;
</content>
        <summary type="html">Messing around with something I never used, have some pmtiles.</summary>
        </entry><entry xml:lang="en">
        <title>A little bit of GDAL/OGR appreciation</title>
        <published>2024-01-19T00:00:00+00:00</published>
        <updated>2024-01-19T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/gdal-appreciation/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/gdal-appreciation/</id>
        
            <content type="html">&lt;p&gt;If there had to be one single tool to work with spatial data, I think GDAL/OGR would be the first name to come to mind. Its usages range from raster to vector, it can be used as command line, imported as a library in many languages through bindings developed by the community, many applications provide UI to parameterize and run GDAL etc. Its versatility makes it an amazing tool to work with spatial data in a lot of different contexts, but it’s sometimes underestimated or even misunderstood.&lt;/p&gt;
&lt;p&gt;We will quickly introduce GDAL/OGR, then give some examples of usage to help for some quick starts.&lt;/p&gt;
&lt;h2 id=&quot;what-is-gdal-ogr&quot;&gt;What is GDAL/OGR?&lt;/h2&gt;
&lt;p&gt;GDAL/OGR (&lt;strong&gt;G&lt;/strong&gt;eospatial &lt;strong&gt;D&lt;/strong&gt;ata &lt;strong&gt;A&lt;/strong&gt;bstraction &lt;strong&gt;L&lt;/strong&gt;ibrary/&lt;strong&gt;O&lt;/strong&gt;pen&lt;strong&gt;G&lt;/strong&gt;IS Simple Features &lt;strong&gt;R&lt;/strong&gt;eference Implementation) is a library for working with raster and vector geospatial data formats, coded mainly in C++, C and Python. The first release dates from 2000, and the project has now more than &lt;a rel=&quot;external&quot; href=&quot;https://github.com/OSGeo/gdal/graphs/contributors&quot;&gt;450 contributors&lt;/a&gt; at the time of writing. This is a very active project with regular releases.&lt;/p&gt;
&lt;p&gt;The project is composed of two parts: the raster processing library GDAL and the feature processing library OGR, both merged during the release of GDAL 2.0. When working with raster data you will use programs prefixed with &lt;em&gt;gdal&lt;/em&gt; (gdal_translate, gdalwarp etc.) while vector data will use &lt;em&gt;ogr&lt;/em&gt; prefixed scripts (ogrinfo, ogr2ogr etc.). When talking aboutt &lt;em&gt;GDAL&lt;/em&gt;, both libraries are included, and we’ll do the same from here.&lt;/p&gt;
&lt;p&gt;As its name suggests, GDAL abstracts data in order to standardize its way of interacting with it. The driver creates the interface between the data source and the tools, allowing to focus on the logic of the process rather than how to read or write the data. This makes it a very extensible project; including a new format of data source requires to write a new driver, nothing more. You could see that the list of supported formats is quite something, either &lt;a rel=&quot;external&quot; href=&quot;https://gdal.org/drivers/raster/index.html#&quot;&gt;raster&lt;/a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https://gdal.org/drivers/vector/index.html&quot;&gt;vector&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;GDAL describes itself as a translator, meaning that data come from a source and goes to its destination. Translations can occur at different moment during the process, during reading or by the script called. These translations can occur on multiple axis as well, we could for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extract a subset of source data depending on a region of interest.&lt;/li&gt;
&lt;li&gt;Extract a subset of source data based on an attribute.&lt;/li&gt;
&lt;li&gt;Reproject data into a different coordinate system.&lt;/li&gt;
&lt;li&gt;Merge a series of data files into a single one.&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/gdal-appreciation/gdal.jpeg&quot; alt=&quot;gdal-appreciation&quot; /&gt;
&lt;em&gt;Source unknown, if it’s your image and you want to be cited please leave a comment&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As an open source project with a permissive MIT license, GDAL is used by many projects, open source or proprietary, to process geospatial data. If you’ve used QGIS, ArcGIS, FME or PostGIS, you’ve used GDAL under the hood.&lt;/p&gt;
&lt;h2 id=&quot;why-would-i-use-it&quot;&gt;Why would I use it?&lt;/h2&gt;
&lt;p&gt;Weirdly, even though GDAL is basically everywhere there is geospatial data to be moved, it’s not so much acknowledged by GIS people, or at least it’s not really used on its own. If you read this, you definitely use GDAL, but maybe without noticing.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ok but why would I complicate my life with some command lines when the same tool is being used by a software that provides me a user interface easier to use?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While it’s much intuitive to explore geospatial data through a user interface such as QGIS, where you can interactively load your data, chose the process you want to apply, its parameters, and visualize the results, things get a bit more complicated once you’ve done the exploratory part and want to reapply your newly defined process to new data. Repeating operations becomes quickly tedious and error-prone. You could use a tool such as the &lt;a rel=&quot;external&quot; href=&quot;https://docs.qgis.org/3.28/en/docs/user_manual/processing/modeler.html&quot;&gt;Model Designer&lt;/a&gt; from QGIS (amazing little tool, check it if you repeat a lot of operations in QGIS), but how do you schedule your process to be started when you’re not able to launch it manually?&lt;/p&gt;
&lt;p&gt;If you use a heavy client ETL such as FME, try once to compare the difference in speed between a simple table transfer done by launching a FME workspace (using a command line) and a simple &lt;em&gt;ogr2ogr&lt;/em&gt; command, there is at least an order of magnitude of difference.&lt;/p&gt;
&lt;p&gt;It’s about using a tool designed for a purpose as efficiently as possible.&lt;/p&gt;
&lt;h2 id=&quot;how-to-install&quot;&gt;How to install?&lt;/h2&gt;
&lt;p&gt;We’ll assume you have no installation of GDAL yet, but just to make sure you can try:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gdalinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which gives:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;GDAL 3.4.1, released 2021/12/27&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it’s not installed you can refer to the installation instructions from &lt;a rel=&quot;external&quot; href=&quot;https://gdal.org/download.html#&quot;&gt;GDAL website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, I’m working on Ubuntu 22.04, for which the GDAL package is the &lt;a rel=&quot;external&quot; href=&quot;https://launchpad.net/ubuntu/+source/gdal&quot;&gt;3.4.1&lt;/a&gt;. The latest version can be installed by downloading the latest release, but I usually stick with the provided version for my platform, unless I need a specific update.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install gdal-bin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you should be good!&lt;/p&gt;
&lt;div class=&quot;admonition warning&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-warning&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;WARNING&lt;/strong&gt;
&lt;p&gt;You might already have GDAL installed through another software like QGIS, be careful during the installation, multiple installations might interfere and create warnings or errors. On Windows you can use the &lt;em&gt;OSGeo Shell&lt;/em&gt; to access any tool from the OSGeo ecosystem.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;and-how-do-we-use-it&quot;&gt;And how do we use it?&lt;/h2&gt;
&lt;p&gt;It’s easy, you just need to be comfortable with command lines. If you’ve mostly worked with user interfaces it might look scary, but it really is not complicated.&lt;/p&gt;
&lt;h3 id=&quot;access-metadata&quot;&gt;Access metadata&lt;/h3&gt;
&lt;p&gt;The first thing we usually do with data is to have some information about it. Let’s try to see the metadata of a random .jpeg:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gdalinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; gdal.jpeg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Driver: JPEG/JPEG JFIF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Files: gdal.jpeg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Size is 711, 882&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Metadata:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  EXIF_Orientation=1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Image Structure Metadata:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  COMPRESSION=JPEG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  INTERLEAVE=PIXEL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  SOURCE_COLOR_SPACE=YCbCr&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Corner Coordinates:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Upper Left  (    0.0,    0.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Lower Left  (    0.0,  882.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Upper Right (  711.0,    0.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Lower Right (  711.0,  882.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Center      (  355.5,  441.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Band 1 Block=711x1 Type=Byte, ColorInterp=Red&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Overviews: 356x441, 178x221&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Image Structure Metadata:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    COMPRESSION=JPEG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Band 2 Block=711x1 Type=Byte, ColorInterp=Green&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Overviews: 356x441, 178x221&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Image Structure Metadata:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    COMPRESSION=JPEG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Band 3 Block=711x1 Type=Byte, ColorInterp=Blue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Overviews: 356x441, 178x221&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Image Structure Metadata:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    COMPRESSION=JPEG&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Easy right? And it’s not even georeferenced, it’s a .jpeg, a driver exists, it opens.&lt;/p&gt;
&lt;p&gt;We can try the same with some vector data. We’ll have a look at what is going on in one of the PostgreSQL database hosted on my computer.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogrinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -ro PG:dbname=osm_suisse&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;INFO: Open of `PG:dbname=osm_suisse&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      using driver `PostgreSQL&amp;#39; successful.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1: planet_osm_line (Line String)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2: planet_osm_polygon&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3: planet_osm_roads (Line String)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4: planet_osm_point (Point)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;5: natural_polygons&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And as we can see I have 5 layers in my database. Notice how we used the &lt;em&gt;ogrinfo&lt;/em&gt; command for questioning vector data. Here &lt;em&gt;ogrinfo&lt;/em&gt; (like &lt;em&gt;gdalinfo&lt;/em&gt;) requires a dataset source as input, the syntax will depend on each driver, make sure to have a look at the &lt;a rel=&quot;external&quot; href=&quot;https://gdal.org/drivers/vector/index.html&quot;&gt;documentation&lt;/a&gt;. For a PostgreSQL connection, the line could have looked like:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogrinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -ro PG:&amp;quot;dbname=&amp;#39;osm_suisse&amp;#39; host=&amp;#39;localhost&amp;#39; port=&amp;#39;5432&amp;#39; user=&amp;#39;x&amp;#39; password=&amp;#39;y&amp;#39;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can extend the information that we want to see for each layer by adding the &lt;code&gt;-al&lt;/code&gt; flag to the line. The option &lt;code&gt;-so&lt;/code&gt; will summarize the output.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogrinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -ro -so -al PG:dbname=osm_suisse&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;INFO: Open of `PG:dbname=osm_suisse&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      using driver `PostgreSQL&amp;#39; successful.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer name: planet_osm_line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Geometry: Line String&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feature Count: 2137192&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Extent: (626211.331305, 5706647.213604) - (1253686.695428, 6117386.570796)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer SRS WKT:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;PROJCRS[&amp;quot;WGS 84 / Pseudo-Mercator&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BASEGEOGCRS[&amp;quot;WGS 84&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ENSEMBLE[&amp;quot;World Geodetic System 1984 ensemble&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (Transit)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G730)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G873)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G1150)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G1674)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G1762)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G2139)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ELLIPSOID[&amp;quot;WGS 84&amp;quot;,6378137,298.257223563,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                LENGTHUNIT[&amp;quot;metre&amp;quot;,1]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ENSEMBLEACCURACY[2.0]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        PRIMEM[&amp;quot;Greenwich&amp;quot;,0,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ANGLEUNIT[&amp;quot;degree&amp;quot;,0.0174532925199433]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ID[&amp;quot;EPSG&amp;quot;,4326]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    CONVERSION[&amp;quot;Popular Visualisation Pseudo-Mercator&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        METHOD[&amp;quot;Popular Visualisation Pseudo Mercator&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ID[&amp;quot;EPSG&amp;quot;,1024]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        PARAMETER[&amp;quot;Latitude of natural origin&amp;quot;,0,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ANGLEUNIT[&amp;quot;degree&amp;quot;,0.0174532925199433],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ID[&amp;quot;EPSG&amp;quot;,8801]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        PARAMETER[&amp;quot;Longitude of natural origin&amp;quot;,0,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ANGLEUNIT[&amp;quot;degree&amp;quot;,0.0174532925199433],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ID[&amp;quot;EPSG&amp;quot;,8802]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        PARAMETER[&amp;quot;False easting&amp;quot;,0,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            LENGTHUNIT[&amp;quot;metre&amp;quot;,1],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ID[&amp;quot;EPSG&amp;quot;,8806]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        PARAMETER[&amp;quot;False northing&amp;quot;,0,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            LENGTHUNIT[&amp;quot;metre&amp;quot;,1],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ID[&amp;quot;EPSG&amp;quot;,8807]]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    CS[Cartesian,2],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        AXIS[&amp;quot;easting (X)&amp;quot;,east,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ORDER[1],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            LENGTHUNIT[&amp;quot;metre&amp;quot;,1]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        AXIS[&amp;quot;northing (Y)&amp;quot;,north,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ORDER[2],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            LENGTHUNIT[&amp;quot;metre&amp;quot;,1]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    USAGE[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        SCOPE[&amp;quot;Web mapping and visualisation.&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        AREA[&amp;quot;World between 85.06°S and 85.06°N.&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        BBOX[-85.06,-180,85.06,180]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ID[&amp;quot;EPSG&amp;quot;,3857]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Data axis to CRS axis mapping: 1,2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Geometry Column = way&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;osm_id: Integer64 (0.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;access: String (0.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;addr:housename: String (0.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;addr:housenumber: String (0.0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we have the full detail of our layers; name, type, feature count, etc.&lt;/p&gt;
&lt;h3 id=&quot;basic-translations&quot;&gt;Basic translations&lt;/h3&gt;
&lt;p&gt;Now that we know what were working, with we can try to move some things around. Let’s start by exporting our vector data into a geopackage.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogr2ogr&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -f GPKG exported_data.gpkg &amp;quot;PG:dbname=osm_suisse tables=planet_osm_point&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we’re exporting the table &lt;em&gt;planet_osm_point&lt;/em&gt; into the &lt;em&gt;exported_data.gpkg&lt;/em&gt; geopackage. The format can be recognized by the extension of the output file, but it’s safer to ensure the format by passing the argument &lt;code&gt;-f&lt;/code&gt;. Again, each driver documentation will have its driver name to be used.&lt;/p&gt;
&lt;h3 id=&quot;add-a-filter&quot;&gt;Add a filter&lt;/h3&gt;
&lt;p&gt;We can export a subset of our data, based on an attribute for example. Let’s say we want to export the points where the attribute &lt;em&gt;addr:housenumber&lt;/em&gt; is not null, and we want to save it in another table in our newly created geopackage. We’d do something like:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogr2ogr&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -update -f GPKG -where &amp;#39;&amp;quot;addr:housenumber&amp;quot; is not null&amp;#39; -nln only_housenumbers exported_data.gpkg &amp;quot;PG:dbname=osm_suisse tables=planet_osm_point&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to update our dataset (our geopackage) and not create a new one, so we use the &lt;code&gt;-update&lt;/code&gt; option. The syntax of the &lt;code&gt;-where&lt;/code&gt; parameter is a bit convoluted here since there is a special character in the column name, but with a simple name you’d just need a pair of quotes &lt;code&gt;-where &quot;housenumber&quot; is not null&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s have a look at our geopackage:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogrinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -ro -al -so exported_data.gpkg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;INFO: Open of `exported_data.gpkg&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      using driver `GPKG&amp;#39; successful.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer name: planet_osm_point&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Geometry: Point&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feature Count: 2146246&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Extent: (626415.435591, 5736097.599850) - (1189428.532375, 6117386.570796)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer SRS WKT:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;PROJCRS[&amp;quot;WGS 84 / Pseudo-Mercator&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BASEGEOGCRS[&amp;quot;WGS 84&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ENSEMBLE[&amp;quot;World Geodetic System 1984 ensemble&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (Transit)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G730)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G873)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G1150)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer name: only_housenumbers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Geometry: Point&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feature Count: 366561&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Extent: (663719.309401, 5751784.044673) - (1167785.106794, 6072459.331387)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer SRS WKT:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;PROJCRS[&amp;quot;WGS 84 / Pseudo-Mercator&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BASEGEOGCRS[&amp;quot;WGS 84&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ENSEMBLE[&amp;quot;World Geodetic System 1984 ensemble&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (Transit)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G730)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G873)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G1150)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            MEMBER[&amp;quot;World Geodetic System 1984 (G1674)&amp;quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new layer has been added to the geopackage. Has the query been respected?&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogrinfo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -ro -al -so -sql &amp;#39;select * from only_housenumbers where &amp;quot;addr:housenumber&amp;quot; is null&amp;#39; exported_data.gpkg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;INFO: Open of `exported_data.gpkg&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      using driver `GPKG&amp;#39; successful.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer name: SELECT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Geometry: Point&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Feature Count: 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Layer SRS WKT:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;PROJCRS[&amp;quot;WGS 84 / Pseudo-Mercator&amp;quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes! No data without &lt;em&gt;addr:housenumber&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-tip&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;TIP&lt;/strong&gt;
&lt;p&gt;The &lt;code&gt;-sql&lt;/code&gt; option is another way of filtering data, it allows you to pass a sql query in any of the sql flavor supported by the driver, you can even save your queries in a text file and pass the file to the &lt;code&gt;-sql&lt;/code&gt; parameter as &lt;code&gt;-sql @path/to/my_file&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&quot;geometric-operations&quot;&gt;Geometric operations?&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;-sql&lt;/code&gt; parameter can also be used to apply transformations to the data. We’ll request the same points, but this time we’ll create a buffer around them.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogr2ogr&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -update -f GPKG -sql &amp;#39;select ST_Buffer(way, 50) from planet_osm_point where &amp;quot;addr:housenumber&amp;quot; is not null&amp;#39; -nln buffered_only_housenumbers exported_data.gpkg &amp;quot;PG:dbname=osm_suisse tables=planet_osm_point&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it, we have our buffer around the points of interest. Here GDAL makes use of the internal capabilities of PostgreSQL and PostGIS by passing directly the request to the database, but the same command using the &lt;em&gt;only_housenumbers&lt;/em&gt; layer from our geopackage would also be valid.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ogr2ogr&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -update -f GPKG -sql &amp;quot;select ST_Buffer(way, 50) from only_housenumbers&amp;quot; -nln buffered_only_housenumbers exported_data.gpkg exported_data.gpkg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;admonition tip&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-tip&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;TIP&lt;/strong&gt;
&lt;p&gt;In the case of PostgreSQL/PostGIS you can write the &lt;code&gt;-sql&lt;/code&gt; parameter like you would write your queries directly into the database. They are passed directly to the database by GDAL.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This should give you a small idea of the things you can do with GDAL and how to start experimenting.&lt;/p&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final words&lt;/h2&gt;
&lt;p&gt;Well that was a lot more than expected. But GDAL really is a very strong and important tool. Diving into the details of all the available options, both for the command AND the drivers, is not a small task, but understanding how to navigate around and find the information that you need to process your data as you wish can go a long way. Manual and repeated tasks can be replaced to help to focus on more pertinent work, process time can be (by experience) reduced drastically depending on the tools you’re initially using. And if you’re working on data hosted on a server without graphic interface, well you have a tool to get the info you need!&lt;/p&gt;
&lt;p&gt;Obviously GDAL alone can’t do everything and has its imperfections; I’ve really struggled with installations in the past (Python binding might have bested me at some point), and the number of parameters is a bit overwhelming. But once you have it working it does the job, and really well.&lt;/p&gt;
&lt;p&gt;If you can, use GDAL.&lt;/p&gt;
</content>
        <summary type="html">To one of the OGs of geographical information, it&#39;s everywhere and you should knwo about it.</summary>
        </entry><entry xml:lang="en">
        <title>Serving vector tiles with Martin</title>
        <published>2023-12-20T00:00:00+00:00</published>
        <updated>2023-12-20T00:00:00+00:00</updated>
        <author>
            <name>jujube</name>
        </author>
        <link rel="alternate" href="https://blog.geothings.ch/blog/vector-tiles-with-martin/" type="text/html"/>
        <id>https://blog.geothings.ch/blog/vector-tiles-with-martin/</id>
        
            <content type="html">&lt;p&gt;There are some amazing tools out there, and they are getting build fast. I haven’t had the opportunity to work with vector tiles during my day-to-day jobs, but I wanted to explore what it is and how to set some things up, just for the fun of it. We’ll see here how to start using &lt;a rel=&quot;external&quot; href=&quot;https://github.com/maplibre/martin&quot;&gt;Martin&lt;/a&gt;, a vector tile server built in Rust serving PostGIS data.&lt;/p&gt;
&lt;h2 id=&quot;vector-tiles&quot;&gt;Vector tiles&lt;/h2&gt;
&lt;p&gt;The standard has been set by &lt;a rel=&quot;external&quot; href=&quot;https://www.mapbox.com/&quot;&gt;Mapbox&lt;/a&gt;. In the same manner a WMTS serves raster tiles, vector tiles allow for the visualization of large volumes of data over the web, but as their name suggests, they are vectors.&lt;/p&gt;
&lt;p&gt;It comes with some advantages compared to raster formats:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It’s significantly lighter, about 20 to 50% of its raster equivalent according to &lt;a rel=&quot;external&quot; href=&quot;https://documentation.maptiler.com/hc/en-us/articles/4411234458385-Raster-vs-Vector-Map-Tiles-What-Is-the-Difference-Between-the-Two-Data-Types-&quot;&gt;MapTiler&lt;/a&gt;. Vector data are series of points and each pixel of the map doesn’t have to have information declared; a rectangle polygon covering thousands of square kilometers can still be defined with 4 points.&lt;/li&gt;
&lt;li&gt;It is stylized dynamically. While raster tiles need to be generated with their style embedded, vector tiles style can just be modified on the fly, without going through the laborious process of tiles generation that can take easily several hours/days.&lt;/li&gt;
&lt;li&gt;It’s fast. Moving through a vector tile map feels smooth, there is no apparent tile loading.&lt;/li&gt;
&lt;li&gt;What you request is what you get. You directly query the tiles, not some sort of other service along your tiles.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s an amazing standard to visualize spatial data in a web map. Obviously it’s not suitable for all kinds of data, you have to have discrete data that make sense to be visualized as vector; I doubt that raw satellites images as vector tiles are a good idea.&lt;/p&gt;
&lt;h2 id=&quot;installing-postgresql-and-postgis&quot;&gt;Installing PostgreSQL and PostGIS&lt;/h2&gt;
&lt;p&gt;First thing first, we need to store data somewhere. Martin is designed to be used with PostGIS, PMTiles and MBTiles, we’ll explore the PostGIS alternative.&lt;/p&gt;
&lt;p&gt;The following commands were used on an Ubuntu distribution.&lt;/p&gt;
&lt;p&gt;Let’s start by installing PostgreSQL:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install postgresql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the PostGIS extension:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install postgis&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll already set things up in the database by creating a dedicated user, creating a database and creating the PostGIS extension in there. From psql, create a user:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;create user&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; martin_usr&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; with password&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;my_password&amp;#39;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a database:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;create database&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; martin_db&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go to your new database (&lt;code&gt;\c martin_db&lt;/code&gt;) and create the extension:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;create&lt;/span&gt;&lt;span&gt; extension postgis;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We won’t cover here the more extensive tuning of the database. You should be set on the database side for this example.&lt;/p&gt;
&lt;h2 id=&quot;downloading-some-data&quot;&gt;Downloading some data&lt;/h2&gt;
&lt;p&gt;Obviously you’ll want some data to serve as vector tiles. In a production environment you’ll probably want to serve your own data, so this step would probably not be necessary. For the example we don’t have any data yet, so we’ll use some from osm through &lt;a rel=&quot;external&quot; href=&quot;https://download.geofabrik.de/&quot;&gt;GeoFabrik&lt;/a&gt;. Since I’m in Switzerland, let’s download data over the country:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; https://download.geofabrik.de/europe/switzerland-latest.osm.pbf -O&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using &lt;code&gt;osm2pgsql&lt;/code&gt; to import data into the database, so the &lt;code&gt;.pbf&lt;/code&gt; format is ideal.&lt;/p&gt;
&lt;p&gt;You can then load your data (&lt;code&gt;apt install osm2pgsql&lt;/code&gt; if you don’t have the tool):&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;osm2pgsql&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -d martin_db -U martin_usr switzerland-latest.osm.pbf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have osm data over a whole country, easy right?&lt;/p&gt;
&lt;h2 id=&quot;now-install-martin&quot;&gt;Now install Martin&lt;/h2&gt;
&lt;p&gt;Here comes the best part, the vector tile server itself. There are multiple ways to start working with Martin, we’ll use rust and its package manager &lt;a rel=&quot;external&quot; href=&quot;https://doc.rust-lang.org/book/ch01-03-hello-cargo.html&quot;&gt;cargo&lt;/a&gt;. If you don’t want to install those on your server you might prefer the &lt;a rel=&quot;external&quot; href=&quot;https://maplibre.org/martin/run-with-docker.html&quot;&gt;Docker installation&lt;/a&gt;, or simply using binaries.&lt;/p&gt;
&lt;p&gt;As said earlier, Martin is build in rust, and it’s accessible as a crate using cargo. You can first &lt;a rel=&quot;external&quot; href=&quot;https://www.rust-lang.org/tools/install&quot;&gt;install rust&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --proto &amp;#39;=https&amp;#39; --tlsv1.2 -sSf https://sh.rustup.rs&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install Martin:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;cargo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install martin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;martin&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it! Martin’s here.&lt;/p&gt;
&lt;h2 id=&quot;can-t-be-that-simple-right&quot;&gt;Can’t be that simple right?&lt;/h2&gt;
&lt;p&gt;Finally, you can run your new server:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;martin&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;postgresql://martin_usr:my_password@localhost:5432/martin_db&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Martin can discover geometric tables in the database from the given url, so by default everything is served. So yes it can be that simple! Obviously environment variables need to be set, passwords hidden etc. But you have an idea of the process.&lt;/p&gt;
&lt;p&gt;You can try by yourself reading the catalog:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; http://0.0.0.0:3000/catalog&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; json_pp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;   &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;fonts&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; : {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;   &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;sprites&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; : {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;   &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;tiles&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; : {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;planet_osm_line&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; : {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;application/x-protobuf&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;public.planet_osm_line.way&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;planet_osm_point&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; : {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;application/x-protobuf&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;public.planet_osm_point.way&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;planet_osm_polygon&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; : {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;application/x-protobuf&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;public.planet_osm_polygon.way&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;planet_osm_roads&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; : {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;application/x-protobuf&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;         &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;&amp;quot; :&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;public.planet_osm_roads.way&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;   }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;visualize-data-using-qgis&quot;&gt;Visualize data using QGIS&lt;/h2&gt;
&lt;p&gt;Once the server is running we can easily visualize our data in a client. Starting QGIS 3.14,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Layer&lt;/em&gt; &amp;gt; &lt;em&gt;Add Layer&lt;/em&gt; &amp;gt; &lt;em&gt;Add Vector Tile Layer&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And enter the url then &lt;em&gt;OK&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;http://localhost:3000/planet_osm_line,planet_osm_point,planet_osm_polygon/{z}/{x}/{y}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/vector-tiles-with-martin/add-vector-tiles.png&quot; alt=&quot;add-vector-tiles&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You should see a rather absurd amount of data.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.geothings.ch/blog/vector-tiles-with-martin/visualization.png&quot; alt=&quot;visualization&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;to-conclude&quot;&gt;To conclude&lt;/h2&gt;
&lt;p&gt;Here is how to get started with Martin, a rust vector tile server. It’s just the start, we don’t cover here the creation of symbology and more in depth configurations and optimization, but it gives an idea of how easy and powerful such a tool can be.&lt;/p&gt;
</content>
        <summary type="html">Vector tiles seems nice, let&#39;s see who&#39;s Martin.</summary>
        </entry>
</feed>
