Losowe ponowne lokalizowanie punktów w określonej odległości w QGIS

Nov 27 2020

Mam współrzędne GPS, które identyfikują projekty pomocowe w Afryce. Teraz chcę ponownie zlokalizować te projekty, tak aby każdy projekt był ponownie zlokalizowany o 100 km w losowym kierunku od pierwotnej lokalizacji.

(Dodatkowo: mam warstwy krajowe; czy mogę zmusić QGIS do zmiany lokalizacji tylko w oryginalnym kraju?)

Wypróbowałem Wirtualny generator warstw i geometrii, oba do tej pory nie działały. Jakieś wskazówki?

Używam QGIS 3.14.15.

Odpowiedzi

14 babel Nov 27 2020 at 22:42

Ta odpowiedź była kilkakrotnie aktualizowana, aby uzyskać rozwiązanie, które stopniowo zwiększa złożoność wyrażenia. Aby uzyskać ostateczne rozwiązanie, należy przejść bezpośrednio na sam dół (krok 5). Historia aktualizacji oryginalnej odpowiedzi składa się jednak na przewodnik krok po kroku, jak działa to rozwiązanie, zaczynając od bardzo prostego sposobu. Daje pewien wgląd w to, jak działają bardziej złożone wyrażenia QGIS i co można dzięki nim osiągnąć. Każdy stopień złożoności jest oddzielony poziomą linią i nazwany Krok 1, Krok 2 ... do Krok 5, aby można było łatwo przejść do tych kroków. Krok 4 to szczegółowe wyjaśnienie, w jaki sposób łączą się różne części wyrażeń.


Krok 1

Możesz to zrobić, używając następującego wyrażenia (patrz poniżej, aby uzyskać dodatkowe informacje dotyczące zmiany lokalizacji w krajach):

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

Jeśli potrzebujesz go tylko do wizualizacji, dodaj warstwę symboli, zdefiniuj ją jako generator geoemtry i wklej powyższe wyrażenie. Zmień 100000, jeśli jednostki twojego CRS nie są w metrach. Należy pamiętać, że za każdym razem, gdy przesuwasz, powiększasz lub wykonujesz inną czynność, warstwa jest aktualizowana, a losowe punkty będą znajdować się w nowej lokalizacji. Jeśli chcesz mieć losową odległość, powiedzmy od 50 do 100 km, zamień 100000 na rand ( 50000, 100000).

Jeśli chcesz uzyskać rzeczywistą geometrię, użyj Menu, processing / toolbox / geoemtry by expressionaby wygenerować nową warstwę, użyj tego samego wyrażenia.

Wynik: czerwone kropki: oryginał; niebieskie kropki: ponownie zlokalizowane punkty.

Istnieje możliwość tworzenia współrzędnych xiy dla losowych punktów w oryginalnej warstwie punktów bez tworzenia nowej geometrii. Aby to zrobić, użyj kalkulatora pola i utwórz nowe pole o nazwie powiedzmy x_coordinatei y_coordinate. Użyj wyrażenia x(geometry)i y(geometry)utwórz osobne pole dla każdego, które zawiera współrzędne losowo zmienionych punktów, podczas gdy geometria jest wyrażeniem do tworzenia ponownie umieszczonych punktów (na poniższym zrzucie ekranu użyłem bardziej wyrafinowanego wyrażenia z dołu). Otrzymasz dwa nowe pola z wartościami zmienionych współrzędnych x- / y-. Będą przechowywane w sposób bezpieczny i nie będą się już zmieniać / aktualizować (możesz użyć pól wirtualnych, aby zmienić zawartość).

Dzięki temu możesz użyć tych pól do wizualizacji punktów w zmienionej pozycji, używając warstwy symboli z wyrażeniem:

make_point(
   "x_coordinate" ,  
   "y_coordiante" 
)

To wyrażenie generuje białe, ponownie umieszczone, punkty w następnym rzucie ekranu. Jeśli chcesz, możesz usunąć oryginalną reprezentację oryginalnych czerwonych punktów, aby zachować tylko ponownie umieszczone punkty:


Krok 2

Udoskonalona wersja umożliwiająca ponowne zlokalizowanie punktów tylko w kraju. To samo rozwiązanie, ale zamiast powyższego wyrażenia użyj tego tutaj. Pamiętaj, aby zmienić 'countries'nazwę warstwy zawierającej wielokąty Twojego kraju. Obie warstwy i projekt powinny znajdować się w tym samym CRS, najlepiej korzystać z rzutowanego CRS (przy geograficznym CRS pomiary odległości nie mają sensu).

Ta warstwa powinna mieć pole 'fid'zawierające unikalną liczbę całkowitą ( nie , jak stwierdzono we wcześniejszej wersji, jakąkolwiek unikalną wartość pola, taką jak nazwa kraju). W 'fid'poniższym wyrażeniu możesz zastąpić dowolną nazwę pola warstwy Twoich krajów, która zawiera unikatową liczbę całkowitą:

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

Krok 3

I dalsza doskonałość wyrażenia: w powyższej wersji, jeśli oryginalne punkty znajdują się blisko granicy (<100 km), ponownie umieszczone punkty znajdują się czasami na samej granicy, a zatem mniej niż 100 km od punktu oryginalnego . Powyższe wyrażenie tworzy okrąg wokół pierwotnego punktu i przecina z bieżącym krajem. Następnie zajmuje zewnętrzną linię tego przecięcia i umieszcza losowy punkt na tej zewnętrznej granicy - i ta granica obejmuje czasami fragmenty granic kraju, jeśli pierwotny punkt znajduje się w odległości 100 km.

Aby temu zapobiec, odpowiednio zmieniłem wyrażenie, dodając operację różnicy: z właśnie opisanej granicy obcina (wyodrębnia) wszystkie granice państw. Na pozostałych liniach (pozostają tylko te części oryginalnego okręgu buforowego wewnątrz bieżącego kraju punktu), nowe punkty są rozmieszczane losowo. (zobacz na dole tej odpowiedzi poprawioną wersję wyrażenia, ponieważ nie wszystkie segmenty obramowania są całkowicie obcięte):

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

Krok 4

Po opisie ze zrzutami ekranu, aby lepiej zrozumieć, jak działa wyrażenie:

  1. utwórz bufor 100 km wokół punktów: buffer ( $geometry, 100000)(wielokąt)

  1. pobierz kształty wielokątów krajów z drugiej warstwy:

wyrażenie:

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

(wielokąt)

  1. Przecięcie 1 i 2: tylko te części buforów (1), które znajdują się w obecnym kraju (2)

intersection ( a, b), podczas gdy a jest wyrażeniem z 1, b wyrażeniem z 2

  1. stwórz zewnętrzną granicę 3 (część bufora wewnątrz kraju): gdzie boundary (c)c to wyrażenie z 3 (linia)

  1. pobierz granice kraju jako linię z granic wielokątów warstwy krajów:

wyrażenie:

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

(linia)

  1. clip (wyciąg) granice kraju (5) z 4 (zewnętrzna granica bufora wewnątrz kraju) difference(d, e):, gdzie d to wyrażenie z 4, a e to wyrażenie z 5

  1. Teraz ustaw losowy punkt gdzieś na linii od nie. 6 z ekspresją line_interpolate_point(geometry,distance)natomiast
  • geometria jest wyrażeniem z 6 (stąd: koło 100 km z wyjątkiem części leżących poza granicami kraju i bez granic)
  • odległość na tej linii jest tworzona losowo za pomocą wyrażenia rand(min,max)min = 0 i max = cała długość tej geometrylinii (od nr 6) - tak więc punkt zostanie losowo przydzielony gdzieś pomiędzy początkiem a końcem linii.
  1. Ostatnia (lub pierwsza część) wyrażenia to with_variable(name,value,expression): to ustawia zmienną iw ramach tej funkcji mamy wszystkie elementy, które stworzyliśmy od 1 do 7 razem.
  • Odp .: Zmienna ma nazwę (pierwszy argument) 'boundary'(do odniesienia w C, patrz poniżej) - nazwa jest dowolna i może być dowolna.

  • B: jego wartość (drugi argument) to całe wyrażenie utworzone w nr. 6 (z elementem od 1 do 5), czyli linia bez granic kraju i

  • C: wyrażenie (trzeci argument) jest line_interpolate_point wyrażeniem z nie. 7, która przyjmuje geometrię wejściową (wartość z B) jako zmienną @boundary, nazwa zdefiniowana powyżej (patrz A) (znak @ przed nazwą zmiennej to konwencja QGIS dotycząca adresowania zmiennej).

Wynik wygląda następująco - niebieski: oryginalny punkt z etykietą identyfikatora, biały: ponownie zlokalizowany punkt z etykietą identyfikatora:


Krok 5

I (mam nadzieję, że ostatnia) poprawa: jak zdałem sobie sprawę dopiero teraz, w niektórych przypadkach punkty mogą nadal leżeć na granicy, mniej niż 100 km od pierwotnego punktu. Segmenty graniczne przecinane przez okrąg nie są przycinane, zobacz:

To jest tło, dla którego zadałem to pytanie: Uzyskiwanie odcinka prostej, przecinanej przez inną z wyrażeniem QGIS

Ale jest łatwiejsze rozwiązanie, które przytaczam tutaj dla kompletności, aby mieć „ostateczną” odpowiedź: w kroku 4, część 5 (uzyskać granice kraju), zamiast korzystać z granic kraju, tworzę bufor o wielkości powiedzmy 1 metra wokół granicy, więc ta część wyrażenia wygląda nieco inaczej:

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

Należy pamiętać, że teraz renderowanie obrazu mapy zajmuje trochę czasu, ponieważ QGIS musi obliczyć bufor wokół granic. To ostatnie rozwiązanie jest lepsze w przypadku geometry by expression tooltworzenia raz i dla wszystkich nowych geometrii, a nie dla warstw symboli, które zaczynają się renderować za każdym razem, gdy przesuwasz lub powiększasz.

Zwiększając liczbę 1 w wyrażeniu, masz kontrolę nad tym, jak daleko losowe punkty powinny znajdować się od granicy - ustawienie go na 20000 [metry, w przypadku korzystania z rzutowanego CRS z metrami jako jednostkami] oznacza, że ​​punkty są ponownie zlokalizowane powinien znajdować się w odległości co najmniej 20 km od granicy, jednocześnie będąc w odległości 100 km od pierwotnego punktu.

Oto końcowe wyrażenie generujące te losowe punkty:

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

To wszystko ludzie!