Re-localiser des points au hasard à une certaine distance dans QGIS

Nov 27 2020

J'ai des coordonnées GPS qui identifient les projets d'aide en Afrique. Maintenant, je veux relocaliser ces projets, de sorte que chaque projet soit relocalisé de 100 km dans une direction aléatoire à partir de son emplacement d'origine.

(Extra: j'ai des couches de pays; puis-je forcer QGIS à se déplacer uniquement dans le pays d'origine?)

J'ai essayé le Virtual Layer & Geometry Generator, les deux n'ont pas fonctionné jusqu'à présent. Des pistes?

J'utilise QGIS 3.14.15.

Réponses

14 babel Nov 27 2020 at 22:42

Cette réponse a été mise à jour plusieurs fois pour obtenir une solution qui ajoute progressivement de la complexité à l'expression. Pour la solution finale, vous devez aller directement tout en bas (étape 5). L'historique de la mise à jour de la réponse originale s'ajoute cependant à un guide étape par étape du fonctionnement de cette solution, en commençant par un moyen très simple. Il donne un aperçu du fonctionnement d'expressions QGIS plus complexes et de ce qui peut être réalisé avec. Chaque étape de complexité est séparée par une règle horizontale et nommée Étape 1, Étape 2 ... à Étape 5 afin que vous puissiez passer facilement à ces étapes. L'étape 4 est une explication détaillée de la façon dont toutes les différentes parties des expressions se réunissent.


Étape 1

Vous pouvez le faire en utilisant l'expression suivante (voir ci-dessous pour le supplément pour re-localiser à l'intérieur des pays):

project ( $geometry , 100000 , radians ( rand ( 0,360) ) ) 

Si vous en avez besoin uniquement à des fins de visualisation, ajoutez une couche de symboles, définissez-la comme générateur de géoémétrie et collez l'expression ci-dessus. Changez 100000 si les unités de votre CRS ne sont pas en mètres. Sachez que chaque fois que vous effectuez un panoramique, un zoom ou une autre activité, la couche est mise à jour et les points aléatoires se trouveront à un nouvel emplacement. Si vous souhaitez également une distance aléatoire comprise entre 50 et 100 km, remplacez 100000 par rand ( 50000, 100000).

Si vous voulez la géométrie réelle, utilisez Menu processing / toolbox / geoemtry by expressionpour générer un nouveau calque, utilisez la même expression.

Résultat: points rouges: original; points bleus: points repositionnés.

Il est possible de créer des coordonnées x et y pour les points aléatoires dans votre couche de points d'origine sans créer de nouvelle géométrie. Pour ce faire, utilisez la calculatrice de champ et créez un nouveau champ, nommé disons x_coordinateet y_coordinate. Utilisez l'expression x(geometry)et y(geometry)pour créer un champ séparé pour chacun contenant les coordonnées des points replacés aléatoirement, tandis que la géométrie est l'expression pour créer les points replacés (dans la capture d'écran ci-dessous, j'ai utilisé l'expression plus sophistiquée ci-dessous). Vous obtiendrez deux nouveaux champs avec des valeurs de coordonnées x / y replacées. Ils seront stockés parmanemment et ne seront plus modifiés / mis à jour (vous pouvez utiliser des champs virtuels pour modifier le contenu).

Avec cela, vous pouvez utiliser ces champs pour visualiser vos points dans la position replacée, en utilisant une couche de symboles avec l'expression:

make_point(
   "x_coordinate" ,  
   "y_coordiante" 
)

Cette expression génère les points blancs replacés dans la capture d'écran suivante. Si vous le souhaitez, vous pouvez supprimer la représentation d'origine des points d'origine, rouges, pour ne conserver que les points replacés:


Étape 2

Une version raffinée pour relocaliser les points uniquement à l'intérieur du pays. Même solution, mais au lieu de l'expression ci-dessus, utilisez celle-ci ici. N'oubliez pas de changer 'countries'le nom de la couche contenant les polygones de votre pays. Les deux couches ainsi que le projet doivent être dans le même CRS, mieux utiliser un CRS projeté (avec un CRS géographique, les mesures de distance n'ont pas de sens).

Encore une fois, cette couche doit avoir un champ 'fid'qui contient un entier unique (et non , comme indiqué dans une version précédente, une valeur de champ unique comme le nom du pays). Vous pouvez remplacer 'fid'dans l'expression suivante par n'importe quel nom de champ de la couche de votre pays qui contient un entier unique:

with_variable ( 
   'boundary',
    boundary ( 
      intersection (
         buffer ( $geometry, 100000),
         geometry(
            get_feature( 
               'countries',
                'fid',
                to_int ( array_to_string (
                   overlay_within (
                      'countries', 
                      fid)
                 ))
            )
          )
       )
   ), 
   line_interpolate_point (
      @boundary,
      rand (
         0,
         length(@boundary)
      )
   )
)

Étape 3

Et une perfection supplémentaire de l'expression: dans la version ci-dessus, si les points d'origine sont proches de la frontière (<100 km), les points replacés sont parfois situés sur la frontière elle-même et donc à moins de 100 km du point d'origine . L'expression ci-dessus crée un cercle autour du point d'origine et fait une intersection avec le pays actuel. Il prend ensuite la ligne extérieure de cette intersection et place un point aléatoire sur cette frontière extérieure - et cette frontière comprend parfois des segments des frontières du pays si le point d'origine est à moins de 100 km.

Pour éviter cela, j'ai changé l'expression en conséquence, en ajoutant une opération de différence: à partir de la limite que je viens de décrire, elle coupe (extrait) toutes les frontières du pays. Sur les lignes restantes (il ne reste que les parties du cercle-tampon d'origine à l'intérieur du pays actuel du point), les nouveaux points sont positionnés de manière aléatoire. (voir au bas de cette réponse pour une version révisée de l'expression, car tous les segments de bordure ne sont pas complètement coupés):

with_variable ( 
   'boundary',
    
    difference(
        boundary ( 
          intersection (
             buffer ( $geometry, 100000),
             geometry(
                get_feature( 
                   'countries',
                    'fid',
                    to_int ( array_to_string (
                       overlay_within (
                          'countries', 
                          fid)
                     ))
                )
              )
           )
      ), 
      boundary ( 
         aggregate ( 
            'countries', 
            'collect',
            $geometry)
      )),
   line_interpolate_point (
      @boundary,
      rand (
         0,
         length(@boundary)
      )
   )
)

Étape 4

Suite à une description avec des captures d'écran pour mieux comprendre le fonctionnement de l'expression:

  1. créer un tampon de 100 km autour des points: buffer ( $geometry, 100000)(polygone)

  1. récupérez les formes polygonales des pays de l'autre couche:

expression:

geometry(
   get_feature(
         'countries',
      'fid',
      to_int ( 
         array_to_string (
            overlay_within (
           'countries', 
                   fid)
         )
      )
   )
)

(polygone)

  1. Intersection de 1 et 2: uniquement les parties des tampons (1) qui se trouvent dans le pays actuel (2)

intersection ( a, b), alors que a est l'expression de 1, b l'expression de 2

  1. créer la limite extérieure de 3 (partie du tampon qui se trouve à l'intérieur du pays) boundary (c):, alors que c est l'expression de 3 (ligne)

  1. obtenir les frontières du pays sous forme de ligne à partir des limites du polygone de la couche de pays:

expression:

boundary ( 
       aggregate ( 
          'countries', 
          'collect',
          $geometry)
    )

(ligne)

  1. clip (extraire) les frontières du pays (5) à partir de 4 (limite extérieure du tampon à l'intérieur du pays) difference(d, e):, alors que d est l'expression de 4 et e est l'expression de 5

  1. Maintenant, définissez un point aléatoire quelque part sur la ligne à partir de non. 6 avec l'expression line_interpolate_point(geometry,distance)alors que
  • la géométrie est l'expression de 6 (donc: le cercle de 100 km sauf les parties situées à l'extérieur du pays et sans les frontières du pays)
  • la distance sur cette ligne est créée aléatoirement avec l'expression rand(min,max)avec min = 0 et max = longueur totale de cette geometryligne (à partir du n ° 6) - ainsi le point sera alloué de manière aléatoire quelque part entre le début et la fin de la ligne.
  1. La dernière (ou la toute première partie) de l'expression est with_variable(name,value,expression): cela définit une variable et dans cette fonction, nous avons tous les éléments que nous avons créés de 1 à 7 ensemble.
  • R: La variable a le nom (1er argument) 'boundary'(à référencer en C, voir ci-dessous) - le nom est arbitraire et pourrait être n'importe quoi d'autre.

  • B: sa valeur (2ème argument) est l'expression entière créée en no. 6 (avec l'élément de 1 à 5), donc la ligne circulaire sans frontières de pays et

  • C: l'expression (3ème argument) est l' line_interpolate_point expression de no. 7 qui prend la géométrie d'entrée (la valeur de B) comme variable @boundary, le nom défini ci-dessus (voir A) (le caractère @ devant le nom de la variable est la convention QGIS pour adresser une variable).

Le résultat ressemble à ceci - bleu: point d'origine avec id-label, blanc: point re-localisé avec id-label:


Étape 5

Et une amélioration (probablement la toute dernière): comme je l'ai réalisé seulement maintenant, dans certains cas, des points peuvent encore se trouver à la frontière, à moins de 100 km du point d'origine. Les segments de bordure traversés par le cercle ne sont pas tronqués, voir:

C'est le contexte pour lequel j'ai posé cette question: Obtenir le segment d'une ligne, traversé par un autre avec l'expression QGIS

Mais il y a une solution plus simple que j'inclus ici par souci d'exhaustivité, pour avoir une réponse "finale": à l'étape 4, partie 5 (obtenir les frontières du pays), au lieu d'utiliser les frontières du pays, je crée un tampon de disons 1 mètre autour de la bordure, donc cette partie de l'expression est légèrement différente:

buffer (
   boundary ( 
      aggregate ( 
         'countries', 
         'collect',
          $geometry)
       ) ,
    1 
)

Sachez que maintenant, le rendu de l'image de la carte prend un certain temps car QGIS doit calculer le tampon autour des bordures. Cette dernière solution est préférable à utiliser avec le geometry by expression toolpour créer une fois pour toutes de nouvelles géométries, et non pour les couches de symboles qui commencent à s'afficher à chaque fois que vous effectuez un panoramique ou un zoom.

En augmentant le nombre 1 dans l'expression, vous avez le contrôle de la distance entre les points aléatoires et la frontière - en le définissant sur 20000 [mètres, au cas où vous utilisez un CRS projeté avec des mètres comme unités] signifie que les points ont été déplacés doit avoir au moins une distance de 20 km de la frontière, tout en restant à 100 km du point d'origine.

Voici donc l'expression finale pour générer ces points aléatoires:

with_variable ( 
   'boundary',

    difference(
        boundary ( 
          intersection (
             buffer ( $geometry, 100000),
             geometry(
                get_feature( 
                   'countries',
                    'fid',
                    to_int ( array_to_string (
                       overlay_within (
                          'countries', 
                          fid)
                     ))
                )
              )
           )
      ), 
      buffer (boundary ( 
         aggregate ( 
            'countries', 
            'collect',
            $geometry)
      ),1)),
   line_interpolate_point (
      @boundary,
      rand (
         0,
         length(@boundary)
      )
   )
)

C'est tout les gens!