Chapitre 7 Les opérations géométriques

Nous allons voir dans ce chapitre comment opérer des opérations géométriques sur nos vecteurs.

Dans ce chapitre, nous allons utiliser les packages suivants.

library(sf)
library(tidyverse)
library(mapview)
library(ggplot2)
library(rmapshaper)

On distingue deux types d’opérations : les opérations unaires et binaires.

7.1 Opérations unaires

7.1.1 Simplification

La simplification revient comme son nom l’indique à simplifier une couche vectorielle. Le cas d’usage d’un tel procédé peut être un changement d’échelle et plus généralement le besoin de réduire la taille de stockage de notre objet (par exemple pour une publication ou une carte interactive).

Le package sf contient une fonction st_simplify qui implémente l’algorithme de Douglas-Peucker9 de GEOS.

La fonction utilise le paramètre dTolerance pour controler le niveau de simplification.

load("data/admin_express.RData")

departement_56 <- departements_geo %>%
  filter(INSEE_DEP == "56")

departement_56_simplifie <- departement_56 %>%
  st_simplify(dTolerance = 900)

departement_56_super_simplifie <- departement_56 %>%
  st_simplify(dTolerance = 2000)
p1 <- ggplot() + 
  geom_sf(data = departement_56) + 
  theme_void() + 
  theme(panel.grid = element_blank(), panel.border = element_blank())

p2 <- ggplot() + 
  geom_sf(data = departement_56_simplifie) +
  theme_void()

p3 <- ggplot() + 
  geom_sf(data = departement_56_super_simplifie) + 
  theme_void()

p1 + p2 + p3 + plot_layout(nrow = 1)

On peut mesurer le gain réalisé par chaque opération.

  • Une simplification avec un dTolerance de 900 permet d’économiser 96.9 % du stockage.

  • Une simplification avec un dTolerance de 2000 permet d’économiser 97.7 % du stockage.

object.size(departement_56)
505744 bytes
object.size(departement_56_simplifie)
15720 bytes
object.size(departement_56_super_simplifie)
11768 bytes

Le problème de l’algorithme Douglas-Peucker est qu’il simplifie les géométries objet par objet. Cela conduit à perdre la topologie, et à des trous ou des chevauchements. L’option preserveTopology = T de st_simplify() doit permettre en théorie d’éviter ce problème, mais ne marche pas au delà d’un certain seuil.

Par exemple, prenons 2 départements autour du Morbihan.

departements_35_44_56 <- departements_geo %>%
  filter(INSEE_DEP %in% c("35", "44", "56"))

departements_35_44_56_super_simplifie <- departements_35_44_56 %>%
  st_simplify(dTolerance = 3000)
p1 <- ggplot() + 
  geom_sf(data = departements_35_44_56) + 
  theme_void() + 
  theme(panel.grid = element_blank(), panel.border = element_blank())

p3 <- ggplot() + 
  geom_sf(data = departements_35_44_56_super_simplifie) + 
  theme_void()

p1 + p3 + plot_layout(nrow = 1)

On constate clairement des trous à la frontière des 3 départements.

Un autre algorithme peut être utilisé qui n’a pas les mêmes limitations, l’algorithme de Visvalingam10.

Le package rmapshaper contient une fonction ms_simplify() qui implémente cet algorithme. Ce package est une interface vers Mapshaper11, un site en ligne d’édition de données cartographiques.

departements_35_44_56 <- departements_35_44_56 %>%
  mutate(AREA = as.numeric(AREA))
departements_35_44_56_ms_simplifie <- ms_simplify(departements_35_44_56, method = "vis", keep = 0.01)
p1 <- ggplot() + 
  geom_sf(data = departements_35_44_56) + 
  theme_void() + 
  theme(panel.grid = element_blank(), panel.border = element_blank())

p3 <- ggplot() + 
  geom_sf(data = departements_35_44_56_ms_simplifie) + 
  theme_void()

p1 + p3 + plot_layout(nrow = 1)

7.1.2 Centroïde

Le centroïde permet d’identifier le centre d’un objet géométrique. Il y a plusieurs façons de définir un centroïde. La plus usuelle est le centroïde géographique, qui peut être défini comme le point d’équilibre d’un objet (celui en dessous duquel votre doigt peut faire tenir en équilibre cet objet). La fonction permettant de définir un centroïde dans sf est st_centroid().

centres_departements <- st_centroid(departements_geo)
ggplot() +
  geom_sf(data = departements_geo) +
  geom_sf(data = centres_departements, color = "dark green", size = .5) +
  theme_void() +
  theme(panel.grid = element_blank(), panel.border = element_blank()) +
  labs(title = "les départements et leur centroïdes")

Parfois, le centroïde peut se placer en dehors de l’objet lui même. Par exemple pensez à un atoll.

Dans ce cas on peut utiliser st_point_on_surface() qui garantit que le point est sur la surface de l’objet de départ.

7.1.3 Buffer

Comme déjà vu, à partir d’une couche de départ de type ponctuel, linéaire ou polygonal, le buffer va créer une nouvelle couche vectorielle. La géométrie de cette couche représente des objets surfaciques dont les frontières sont positionnées à une distance euclidienne, définie par l’utilisateur, des limites des objets vectoriels de la couche de départ.

departement_44_buffer <- departement_44 %>%
  st_buffer(dist = 5000)

mapview(list(departement_44_buffer, departement_44), layer.name = c("Loire-Atlantique avec un buffer de 5 km", "Loire-Atlantique"), zcol = c("NOM_DEP", "NOM_DEP"), col.regions = list("#440154FF", "#FDE725FF"))

7.2 Opérations binaires

7.2.1 Transformation affine

Les tranformations affines regrouppent les transformations qui préservent les lignes et le parallélisme. A l’inverse les angles et les tailles ne le sont pas forcément.

Les transformations affines intègrent notamment les translations, les rotations et les changements d’échelle.

Le package sf implémente ces transformations pour les objets de classe sfg et sfc.

7.2.1.1 Translation

departement_44_sfc <- st_geometry(departement_44)

departement_44_sfc_trans <- departement_44_sfc + c(10000, 10000)

departement_44_trans <- st_set_geometry(departement_44, departement_44_sfc_trans)

st_crs(departement_44_trans) <- st_crs(departement_44)

mapview(list(departement_44_trans, departement_44), layer.name = c("Loire-Atlantique avec déplacement affine de 10 km vers le nord et 10 km vers le sud", "Loire-Atlantique"), zcol = c("NOM_DEP", "NOM_DEP"), col.regions = list("#440154FF", "#FDE725FF"))

7.2.1.2 Changement d’échelle

Dans l’exemple suivant on va réduire par deux la surface de chacun des epci du département. Pour cela on va recentrer les epci pour que les coodonnées des centroids soient à l’origine avant de diviser par deux les cordonnées des contours et réappliquer la translation au centroid.

epci_d44_sfc <- st_geometry(epci_d44)

epci_d44_centroid_sfc <- st_centroid(epci_d44_sfc)

epci_d44_centroid_sfc_scale <- (epci_d44_sfc - epci_d44_centroid_sfc) * 0.5 + epci_d44_centroid_sfc

epci_d44_centroid_scale <- st_set_geometry(epci_d44, epci_d44_centroid_sfc_scale)

st_crs(epci_d44_centroid_scale) <- st_crs(epci_d44)


mapview(list(epci_d44, epci_d44_centroid_scale), layer.name = c("Epci de Loire-Atlantique", "Epci de Loire-Atlantique avec surface divisées par deux"), zcol = c("NOM_EPCI", "NOM_EPCI"), col.regions = list("#440154FF", "#FDE725FF"), legend = F)

7.2.2 Découpage

Le découpage spatiale est une forme de filtre sur les géographies.

Le découpage peut seulement s’appliquer à des formes plus complexes que des points : (multi-)lignes, (multi-)polygones.

Nous allons illustrer le découpage à partir des epci à cheval sur plusieurs départements.

epci_redon <- epci_d44 %>%
  filter(CODE_EPCI == "243500741")

st_intersection() permet de ne garder que la partie commune des deux géométries

redon_et_departement_44 <- st_intersection(epci_redon, departement_44) %>%
  mutate(NOM_EPCI = "Communes du 44 de la CA Redon Agglomération")

st_difference(x,y) permet de ne garder que la partie de x non présente dans y

redon_hors_departement_44 <- st_difference(epci_redon, departement_44) %>%
  mutate(NOM_EPCI = "Communes du 44 et du CA Redon Agglomération hors communes en commun")

st_sym_difference(x,y) permet de ne garder que les partie de x et de y non communes.

redon_et_departement_44_sans_partie_communes <- st_sym_difference(epci_redon, departement_44) %>%
  mutate(NOM_EPCI = "Communes du 44 et du CA Redon Agglomération hors communes en commun")

7.2.3 Union

Union permet de fusionner les géométries de plusieurs objets. On va regarder comment reconsituer la carte des communes à partir de celle des départements

regions <- departements_geo %>%
  group_by(INSEE_REG) %>%
  summarise(do_union = T)
mapview(regions, legend = F)

Derrière cette opération, summarise() utilise st_union() du package sf pour dissourdre les polygones des départements en un seul polygone régional.

regions_52 <- departements_geo %>%
  filter(INSEE_REG == "52")

regions_52 <- st_union(regions_52)

  1. Douglas, David H, and Thomas K Peucker. 1973. “Algorithms for the Reduction of the Number of Points Required to Represent a Digitized Line or Its Caricature.” Cartographica: The International Journal for Geographic Information and Geovisualization 10 (2): 112–22.↩︎

  2. Visvalingam, M., and J. D. Whyatt. 1993. “Line Generalisation by Repeated Elimination of Points.” The Cartographic Journal 30 (1): 46–51. https://doi.org/10.1179/000870493786962263.↩︎

  3. https://mapshaper.org/↩︎