In QGIS Punkte in einer bestimmten Entfernung zufällig neu positionieren

Nov 27 2020

Ich habe GPS-Koordinaten, die Hilfsprojekte in Afrika identifizieren. Jetzt möchte ich diese Projekte neu lokalisieren, sodass jedes Projekt 100 km in zufälliger Richtung von seinem ursprünglichen Standort entfernt ist.

(Extra: Ich habe Länderebenen. Kann ich QGIS zwingen, nur innerhalb des ursprünglichen Landes umzuziehen?)

Ich habe den Virtual Layer & Geometry Generator ausprobiert, beide haben bisher nicht funktioniert. Irgendwelche Hinweise?

Ich benutze QGIS 3.14.15.

Antworten

14 babel Nov 27 2020 at 22:42

Diese Antwort wurde mehrmals aktualisiert, um eine Lösung zu erhalten, die dem Ausdruck allmählich Komplexität verleiht. Für die endgültige Lösung sollten Sie direkt ganz nach unten gehen (Schritt 5). Der Verlauf der Aktualisierung der ursprünglichen Antwort führt jedoch zu einer schrittweisen Anleitung zur Funktionsweise dieser Lösung, beginnend mit einer sehr einfachen Methode. Es gibt einen Einblick, wie komplexere QGIS-Ausdrücke funktionieren und was damit erreicht werden kann. Jeder Komplexitätsschritt wird durch eine horizontale Regel getrennt und mit Schritt 1, Schritt 2 ... bis Schritt 5 bezeichnet, damit Sie problemlos zu diesen Schritten springen können. Schritt 4 enthält eine detaillierte Erklärung, wie die verschiedenen Teile der Ausdrücke zusammenkommen.


Schritt 1

Sie können dies tun, indem Sie den folgenden Ausdruck verwenden (siehe unten für die zusätzlichen Informationen zum Umsiedeln innerhalb von Ländern):

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

Wenn Sie es nur zu Visualisierungszwecken benötigen, fügen Sie eine Symbolebene hinzu, definieren Sie sie als Geoemtry-Generator und fügen Sie den obigen Ausdruck ein. Ändern Sie 100000, wenn die Einheiten Ihres CRS nicht in Metern angegeben sind. Beachten Sie, dass jedes Mal, wenn Sie schwenken, zoomen oder eine andere Aktivität ausführen, die Ebene aktualisiert wird und sich die zufälligen Punkte an einer neuen Position befinden. Wenn Sie auch eine zufällige Entfernung von beispielsweise 50 bis 100 km wünschen, ersetzen Sie 100000 durch rand ( 50000, 100000).

Wenn Sie die tatsächliche Geometrie möchten, verwenden Sie Menü processing / toolbox / geoemtry by expression, um eine neue Ebene zu generieren. Verwenden Sie denselben Ausdruck.

Ergebnis: rote Punkte: Original; blaue Punkte: neu positionierte Punkte.

Es besteht die Möglichkeit, x- und y-Koordinaten für die zufälligen Punkte in Ihrer ursprünglichen Punktebene zu erstellen, ohne eine neue Geometrie zu erstellen. Verwenden Sie dazu den Feldrechner und erstellen Sie ein neues Feld mit dem Namen "Sagen wir x_coordinateund" y_coordinate. Verwenden Sie den Ausdruck x(geometry)und y(geometry)erstellen Sie für jedes Feld ein separates Feld, das die Koordinaten der zufällig neu platzierten Punkte enthält, während Geometrie der Ausdruck zum Erstellen der neu platzierten Punkte ist (im folgenden Screenshot habe ich den komplexeren Ausdruck von unten verwendet). Sie erhalten zwei neue Felder mit Werten für neu platzierte x- / y-Koordinaten. Sie werden parmanent gespeichert und nicht mehr geändert / aktualisiert (Sie können virtuelle Felder verwenden, um den Inhalt zu ändern).

Mit diesen Feldern können Sie Ihre Punkte an der neu platzierten Position mithilfe einer Symbolebene mit dem Ausdruck visualisieren:

make_point(
   "x_coordinate" ,  
   "y_coordiante" 
)

Dieser Ausdruck generiert die weißen, neu platzierten Punkte im folgenden Screenshot. Wenn Sie möchten, können Sie die ursprüngliche Darstellung der ursprünglichen roten Punkte entfernen, um nur die neu platzierten Punkte beizubehalten:


Schritt 2

Eine verfeinerte Version, um die Punkte nur innerhalb des Landes neu zu lokalisieren. Dieselbe Lösung, aber anstelle des obigen Ausdrucks verwenden Sie diese hier. Achten Sie darauf 'countries', den Namen der Ebene zu ändern , die Ihre Länderpolygone enthält. Beide Ebenen sowie das Projekt sollten sich im selben CRS befinden. Verwenden Sie am besten ein projiziertes CRS (bei einem geografischen CRS sind Entfernungsmessungen nicht sinnvoll).

Auch hier soll diese Schicht ein Feld hat , 'fid'die eine eindeutige ganze Zahl enthält (eine nicht , wie in einer früheren Version, ein eindeutiger Feldwert wie Ländernamen angegeben). Sie können 'fid'den folgenden Ausdruck durch einen beliebigen Feldnamen Ihrer Länderebene ersetzen , der eine eindeutige Ganzzahl enthält:

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)
      )
   )
)

Schritt 3

Und eine weitere Perfektion des Ausdrucks: Wenn sich in der obigen Version die ursprünglichen Punkte nahe der Grenze befinden (<100 km), befinden sich die neu platzierten Punkte manchmal an der Grenze selbst und somit weniger als 100 km vom ursprünglichen Punkt entfernt . Der obige Ausdruck erzeugt einen Kreis um den ursprünglichen Punkt und schneidet mit dem aktuellen Land. Es nimmt dann die äußere Linie dieser Kreuzung und platziert einen zufälligen Punkt an dieser äußeren Grenze - und diese Grenze enthält manchmal Segmente der Ländergrenzen, wenn der ursprüngliche Punkt innerhalb einer Entfernung von 100 km liegt.

Um dies zu verhindern, habe ich den Ausdruck entsprechend geändert und eine Differenzoperation hinzugefügt: Von der gerade beschriebenen Grenze werden alle Ländergrenzen abgeschnitten (extrahiert). Auf den verbleibenden Linien (nur die Teile des ursprünglichen Pufferkreises innerhalb des aktuellen Landes des Punkts bleiben übrig) werden die neuen Punkte zufällig positioniert. (Eine überarbeitete Version des Ausdrucks finden Sie am Ende dieser Antwort, da nicht alle Rahmensegmente vollständig abgeschnitten sind.)

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)
      )
   )
)

Schritt 4

Folgen Sie einer Beschreibung mit Screenshots, um besser zu verstehen, wie der Ausdruck funktioniert:

  1. Erstellen Sie einen Puffer von 100 km um die Punkte: buffer ( $geometry, 100000)(Polygon)

  1. Holen Sie sich die Polygonformen der Länder aus der anderen Ebene:

Ausdruck:

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

(Polygon)

  1. Schnittpunkt von 1 und 2: nur die Teile der Puffer (1), die im aktuellen Land liegen (2)

intersection ( a, b), während a der Ausdruck von 1 ist, b der Ausdruck von 2

  1. Erstellen Sie die äußere Grenze von 3 (Teil des Puffers innerhalb des Landes) : boundary (c), während c der Ausdruck von 3 ist (Zeile)

  1. Holen Sie sich die Ländergrenzen als Linie von den Polygongrenzen der Länderebene:

Ausdruck:

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

(Linie)

  1. Clip (Extrakt) der Ländergrenzen (5) von 4 (äußere Grenze des Puffers innerhalb des Landes): difference(d, e)während d der Ausdruck von 4 und e der Ausdruck von 5 ist

  1. Setzen Sie nun irgendwo auf der Linie einen zufälligen Punkt von Nr. 6 mit dem Ausdruck line_interpolate_point(geometry,distance)während
  • Geometrie ist der Ausdruck von 6 (also: der 100-km-Kreis mit Ausnahme der Teile, die außerhalb des Landes und ohne Landesgrenzen liegen)
  • Der Abstand auf dieser Linie wird zufällig mit dem Ausdruck rand(min,max)mit min = 0 und max = Gesamtlänge dieser geometryLinie (ab Nr. 6) erstellt. Der Punkt wird also zufällig irgendwo zwischen dem Anfang und dem Ende der Linie zugewiesen.
  1. Der letzte (oder allererste) Teil des Ausdrucks lautet with_variable(name,value,expression): Dies setzt eine Variable und innerhalb dieser Funktion haben wir alle Elemente, die wir von 1 bis 7 erstellt haben, zusammen.
  • A: Die Variable hat den Namen (1. Argument) 'boundary'(auf den in C verwiesen wird, siehe unten) - der Name ist willkürlich und kann beliebig sein.

  • B: Der Wert (2. Argument) ist der gesamte Ausdruck, der in Nr. 1 erstellt wurde. 6 (mit dem Element von 1 bis 5), also die Kreislinie ohne Ländergrenzen und

  • C: Der Ausdruck (3. Argument) ist der line_interpolate_point Ausdruck von Nr. 7, die die Eingabegeometrie (den Wert von B) als Variable verwendet @boundary, den oben definierten Namen (siehe A) (das @ -Zeichen vor dem Namen der Variablen ist die QGIS-Konvention zur Adressierung einer Variablen).

Das Ergebnis sieht folgendermaßen aus: blau: ursprünglicher Punkt mit ID-Label, weiß: neu positionierter Punkt mit ID-Label:


Schritt 5

Und eine (hoffentlich allerletzte) Verbesserung: Wie ich erst jetzt erkannte, können in einigen Fällen noch Punkte an der Grenze liegen, weniger als 100 km vom ursprünglichen Punkt entfernt. Die vom Kreis gekreuzten Rahmensegmente werden nicht abgeschnitten, siehe:

Dies ist der Hintergrund, warum ich diese Frage gestellt habe: Abrufen des Liniensegments, das von einem anderen mit QGIS-Ausdruck gekreuzt wird

Der Vollständigkeit halber gibt es hier jedoch eine einfachere Lösung, um eine "endgültige" Antwort zu erhalten: In Schritt 4, Teil 5 (Ermitteln der Ländergrenzen) erstelle ich anstelle der Ländergrenzen einen Puffer von beispielsweise 1 Meter um den Rand herum, so dass dieser Teil des Ausdrucks etwas anders aussieht:

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

Beachten Sie, dass das Rendern des Kartenbilds jetzt einige Zeit in Anspruch nimmt, da QGIS den Puffer um die Ränder berechnen muss. Diese letzte Lösung ist besser zu verwenden geometry by expression tool, um ein für alle Mal neue Geometrien zu erstellen, nicht für Symbolebenen, die bei jedem Schwenken oder Zoomen gerendert werden.

Wenn Sie die Zahl 1 im Ausdruck erhöhen, können Sie steuern, wie weit die zufälligen Punkte vom Rand entfernt sein sollen. Wenn Sie sie auf 20000 [Meter, wenn Sie ein projiziertes CRS mit Metern als Einheiten verwenden] setzen, werden die Punkte neu positioniert sollte mindestens 20 km von der Grenze entfernt sein und dennoch 100 km vom ursprünglichen Punkt entfernt sein.

Hier ist also der letzte Ausdruck, der diese zufälligen Punkte erzeugt:

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)
      )
   )
)

Das war's Leute!