Eliminate overlaps and gaps between polygons in a layer (with QGis and Postgis)

For those unfamiliar with the joys of working with the French cadastre, you should know that if you have the opportunity to work with cadastral data corresponding to “Lieux-dits” (a subdivision of communes) you’ll find that spatial coherence (topology) isn’t really respected:

In fact, the boundary of each locality is the edge of the adjoining road, leaving empty spaces where the roads are. While this may not be a problem in some cases, it becomes one when you add the “Communes” layer (from the same cadastre) to your map:

At this scale it doesn’t look very clean, but if you zoom in it’s even worse.

Et si on And if we look at the locality layer itself, we find numerous topological anomalies:

Yes, it’s the official land registry…

Here’s how to solve both problems:

  • How to correct the topology of the lieux-dits layer by removing overlapping polygons and empty spaces between them;
  • How to recreate a common layer consistent with the new locality layer and superimposed exactly on the locality boundaries.

The following example was created using data from the official cadastre of the 71 department, downloaded from the following link https://cadastre.data.gouv.fr/data/etalab-cadastre/2023-01-01/shp/departements/71/

How to correct the topology of the locality layer

The solution proposed here requires a PostgreSQL with Postgis database. It is possible to adapt it to SQLite to use a geopackage file and the QGis DB Manager. But you have to expect very long processing times and work around a few shortcomings and bugs. We’ll look at the best solution here, but we’ll present the solution with Geopackage in another article.

1-Create a layer with a buffer around localities

When it comes to managing the spaces between polygons, even if some snapping tools can be used to deal with certain problems, the simplest and most radical solution is to build a buffer around the polygons, so as to transform empty spaces into superimpositions. In the case of localities, empty spaces are of the order of 10m. We build a 6m buffer around the localities, which will give an overlay of the order of 2-3 meters minimum.

Choose a permanent output file in Geopackage format, which will allow you to follow the procedure in the next step without modification.

The result of the buffer layer is as follows:

We no longer have to manage spaces between polygons, we only have overlays.

2- Load the lieux-dits layer into PostgreSQL

You can read more about how to load the layer in the article How to load a geopackage in Postgis with QGis . Use the same procedure to load shapefiles.

And you can find your table in PostreSQL with pgAdmin

3- Running the SQL script

We’re going to use a single script to do all the work:

  • We create a view with two collections of geometries. Initially, these are the polygons of the localities in duplicate.
  • The first collection contains those parts of the polygons that don’t overlap with any others.
  • the overlapping parts are kept in the second collection
  • We add the superimposed part to the smaller of the two polygons concerned by the superimposition.

Here’s the script:

DROP TABLE IF EXISTS table_corrigee ; 

CREATE OR REPLACE VIEW auto_jointure AS SELECT

    tampon.id as id,
    tampon.nom as "nom",
    tampon.commune as "commune",
-- we retrieve all the polygons from the buffer table in two collections: one containing the complete geometries and another containing the superimposed parts. For the moment, both collections contain the complete geometries.
    ST_Union(tampon.geom) AS full_geom,
    ST_Union(tampon_bis.geom) AS shared_geom
    FROM  tampon ,tampon AS tampon_bis
    WHERE
      --check polygon validity as follows
      ST_IsValid(tampon.geom) AND ST_IsValid(tampon_bis.geom)
      --filter to retain intersecting polygons
      AND ST_intersects(tampon.geom,tampon_bis.geom)   
      --eliminate intersecting polygons
      AND tampon.id <> tampon_bis.id          
      --for an intersection of 2 polygons, keep only the smaller one
      AND ST_Area(tampon_bis.geom) < ST_Area(tampon.geom)
      --since we're making "unions", we need to perform a grouping on the other attributes
    GROUP BY tampon.id,tampon."nom" , tampon.commune ;
/*
We'll create a table containing the final result: the first step is to remove all overlapping areas. The second step consists in adding the overlapping areas to the smaller of the two polygons. The holes created by the first step are filled by the second step. The final polygons are seamless, with no overlaps or gaps.
*/

    CREATE TABLE table_corrigee AS SELECT
    id,
    "nom",
    commune,
/* Intersections are subtracted from the layer containing all polygons, thus removing conflicting parts*/
    ST_Multi(ST_Difference(full_geom,shared_geom)) as geom,
    ST_Area(full_geom) as area
    FROM auto_jointure
    WHERE ST_IsValid(full_geom) AND ST_IsValid(shared_geom)
/*The intersections just subtracted must then be added to fill the spaces created*/
    UNION 
      SELECT
      id,
      "nom",
      commune,
      geom,
      ST_Area(geom)
      FROM tampon
      WHERE id NOT IN (SELECT id FROM auto_jointure);
/*A primary key is then redefined so that QGIS can load the layer.*/

ALTER TABLE table_corrigee ADD CONSTRAINT pk_table_corrigee PRIMARY KEY (id);

To adapt the script to your case, you need to replace the following elements with a word processor:

  • all occurrences of tampon by the name of your table containing the buffered edit_locations,
  • all occurrences of table_corrigee with the name of your desired result table,
  • and check that the identifier of your buffered table is id. By default, it can be named fid. In this case, replace all occurrences of id with fid.

Load the new layer into QGis:

Left: corrected layer, right: original locality layer

We’ve solved the problem of the topological consistency of localities.

How to recreate a communes layer consistent with the new lieux-dits layer

If we superimpose the cadastral commune boundaries on our new layer, we can see that they are not consistent with our new lieux-dits, just as they are not consistent with the original lieux-dits.

To obtain coherent communal boundaries, it would be sufficient to group the lieux-dits by commune. The problem is that not all communal areas are divided into lieux-dits. We therefore have “holes” in the lieux-dits layer, which are perfectly normal.

To build complete communes, first create the missing locality polygons, assigning the corresponding commune code, then group the localities together using the commune attribute.

1-Creating the missing polygons

First of all, we’ll create a new polygon layer, emprise, with a single polygon that completely surrounds our lieux-dits layer.

We then use the “Difference” treatment to keep only the emprise zones that don’t correspond to the existing lieux-dits in our corrected_table:

You can leave the output of the treatments as temporary layers, as you won’t have any further use for them after the final result.

The result of the difference is:

The result is a single multipolygon… Before continuing, we need to change this multipolygon layer to single polygons, so that we can remove the area outline.

We use single-piece-to-multi-piece processing

Once the processing is complete, switch the result to edit mode and select the outer polygon.

And it’s being removed:

So now we have a Simple Geometries layer containing all the places where there are no localities, and a layer of corrected localities. We merge the two layers into one, which will no longer have any holes:

We still need to resolve the problem of attributes for polygons that are not localities. They must have an attribute containing the commune code, so that grouping by commune can take place.

We will use the communes layer of the cadastre and perform a join by location with the new merged layer.

We now have the joined table:

In this image, the first five records correspond to areas that are not lieux-dits. The Commune attribute, which is of interest to us in the next step, is not filled in. However, the id2 attribute, which comes from the location-based join with the communes layer, is filled in.

Open the table in edit mode. Use an expression to select the records that do not have the commune attribute filled in, then open the field calculator and update the commune field with the contents of id2.

Now we can group the lieux-dits by commune to obtain our communes layer consistent with the lieux-dits.

We use the Group treatment, with the layer resulting from the join we’ve just modified.

So we have our new communes. We now have one last operation to perform. As with all mergers, there are a few small drosses that need to be eliminated:

To do this, we’ll use the Remove Holes treatment.

Don’t forget to save the result in a permanent file – it would be hard to have to redo everything!

We can now compare the result of our new common layer with the original layer

On the left is the original cadastral layer, on the right the layer we’ve built.

Si cet article vous a intéressé et que vous pensez qu'il pourrait bénéficier à d'autres personnes, n'hésitez pas à le partager sur vos réseaux sociaux en utilisant les boutons ci-dessous. Votre partage est apprécié !

Leave a Reply

Your email address will not be published. Required fields are marked *