Jak przerwać linie konturu pod etykietami wysokości (zamiast używać buforów etykiet)?

32

Czy istnieje sposób na przerwanie linii konturu pod etykietą wysokości?

wprowadź opis zdjęcia tutaj

MAPA
źródło
ArcGIS? QGIS? Zwyczaj?
Ragi Yaser Burhum
1
Używam qgis do opisywania konturów
MAP
Czy zaakceptowałbyś odpowiedź, która wymaga PostGIS?
Scro
3
niestety: nie :) Ale czy istnieje sposób na rozwiązanie problemu z PostGIS?
MAP

Odpowiedzi:

22

Tak, wykonalne. Zwykle sugerowałbym częściowo przezroczysty bufor, ale rozumiem, dlaczego chcesz to zrobić kartograficznie.

Może to być powolne i musisz ręcznie zdecydować, dokąd mają iść etykiety - ale kartograficznie rzecz biorąc, to nie jest złe!

Oto zrzut ekranu ...

Jak widać, brak buforów. Raster pod spodem pozostaje niezmieniony. Uwzględniłem cieńsze pośrednie linie konturu i nadałem im styl, aby były wyświetlane tylko, gdy ELEV% 50 <> 0

przykład przerwanych linii konturu

Zrobiłem to w QGIS 2.12 ... Twój przebieg może się różnić w zależności od wcześniejszych wersji.

Zakładam, że masz pole „ELEV” na każdej linii konturu.

Podziel kontury na segmenty

  1. Użyj przetwarzania i algorytmu GRASS v.split.length, aby podzielić kontury na segmenty o równej długości. Musisz wybrać długość, która będzie zbliżona do rozmiaru etykiety w jednostkach mapy, przy założeniu, że używasz mierników. Tutaj wykorzystałem 200m.

    Uważaj na to, ponieważ spowoduje to, że plik będzie znacznie, znacznie większy (zwróć uwagę na tę funkcję na zrzucie ekranu).

    Aby obejść ten problem, możesz wygenerować tylko te linie konturu, które chcesz stylizować (np. Co 50 lub 100 metrów), aby uniknąć przetwarzania wszystkich pośrednich linii konturu.

  2. Do tej warstwy dodaj 1-cyfrowe pole liczby całkowitej o nazwie showLabel . Domyślnie 0 lub NULL.

  3. Zmień etykietę, aby wyświetlała się tylko w segmencie, w którym to pole jest ustawione na 1. Użyj tego wyrażenia tekstu etykiety ...

    if ( "showlabel" is not null, "ELEV", "")
    

    Myślę, że jeśli (wyrażenie, wartość prawdziwa, wartość fałszywa) jest dość nowe; jeśli używasz starszej wersji, możesz użyć CASE-ELSE

  4. Zmień styl linii, aby wszystkie odcinki o stałej długości były rysowane, z wyjątkiem odcinków, w których wyświetlana jest etykieta. Używaj więc renderowania opartego na regułach z dwiema regułami

    Rule 1: "showLabel" is null
    Black, 0% transparent
    
    Rule 2: "showLabel" is not null
    Any colour, 100% transparent
    

    Teraz wszystkie kontury będą wyświetlane domyślnie, ale nie będzie etykiet.

    Ręcznie edytuj segmenty, w których chcesz wyświetlać etykiety

    Przejdź do trybu edycji i ręcznie wybierz segmenty, w których chcesz wyświetlić wartości konturu, i ustaw wartość showLabel1 dla wybranych operacji. Możesz użyć Ctrl+ select (w Ubuntu / Win, Cmd+ Ctrl+ Click / na Mac?), Aby wybrać wiele segmentów, aby przyspieszyć.

    Powinno to teraz „przyciąć” kontury tam, gdzie etykiety mają być pokazywane, a etykiety pojawią się w przerwach.

W tym przypadku ustawienia mojej etykiety były następujące:

CRS: EPSG 27700 (Local UTM for UK, in meters)
Text size: 50 map units
Placement: Parallel, On Line

Mam nadzieję, że to pomaga!

Steven Kay
źródło
5
To jedyne w pełni działające rozwiązanie, jakie mogę sobie wyobrazić. Bardzo bolesne, choć przy wielu etykietach, nie wyobrażam sobie wykonywania wszystkich moich map wód podziemnych (tysiące rocznie) w ten sposób. Byłoby wspaniale, gdyby w przyszłości było to możliwe dzięki stylowi - ponieważ najlepszym byłoby niestandardowy wzór linii i powtarzanie etykiety + offset.
Miro,
2
Kilka pomysłów ułatwiających zadanie: Aby wybrać wiele segmentów do narysowania etykiety, przydatne może być Wybierz według wielokąta lub Wybierz odręcznie. Kolejnym podejściem byłoby utworzenie warstwy linii rysującej w celu narysowania linii przechwytujących kontury, a następnie wybranie opcji Wybierz według lokalizacji.
Alexandre Neto,
7

Korzystam z opcji „Bufor” w zakładce „Ustawienie etykiety”. (Za pomocą przycisku etykiet, a nie starej opcji etykiet w oknie dialogowym właściwości warstwy). Nie usuwa to linii konturu, jak sądzę, że chcesz, ale czyni czytelną etykietę.

Scro
źródło
4
Nigdy wcześniej o tym nie myślałem, ale byłoby wygodniej, gdyby zamiast przypisywać kolor do bufora, możesz zastosować go jako „nokaut” dla wybranych warstw.
Scro,
5
Najnowsza wersja QGIS ma przezroczyste bufory, dzięki czemu możesz zmniejszyć wpływ na inne części mapy.
Nathan W
1
@MAP Knockout usuwa znajdujące się pod nim piksele. Gdyby to była opcja, w tym przypadku wybrałbyś wybicie warstwy konturu.
Scro
1
nokaut - esri to „maskowanie” resources.arcgis.com/en/help/main/10.1/index.html#//…
mike
1
@MAP - sponsoruj programistę lub prześlij prośbę o funkcję i poczekaj na życzliwość innych. :)
Scro
5

Myślę, że najbliższe, jakie może uzyskać przy obecnych zdolnościach QGIS, jest użycie efektu halo (lub tła) z kolorem pochodzącym z tabeli, który będzie oparty na wartości elewacji i schemacie kolorów takim samym, jak w przypadku siatki bazowej. Oczywiście nie uwzględniłoby to cienia wzgórza i wszystkiego innego poniżej aureoli na mapie. Przykład losowego koloru: losowy kolor efektu halo etykiet Przy odrobinie kodu można to przepisać jako funkcję odzwierciedlającą kolor siatki.

Teoretycznie powinno być możliwe użycie niestandardowego wzoru linii i powtórzenie etykiety + przesunięcie . Niestety nie ma ustawienia przesunięcia etykiety.

  • po niektórych testach nie można zmusić QGIS do ścisłego umieszczania etykiet w dokładnych odstępach czasu i nigdzie indziej (+ i tak brak przesunięcia początkowego)
  • niemożliwe jest utworzenie niestandardowego wzoru linii z zerowym mm, aby spacja miała początkowe przesunięcie, takie jak 20 linii - 10 spacji - 70 linii - 0 spacji - więc etykieta byłaby umieszczana co 100 mm z przesunięciem 30 mm na początku - co oznacza, że ​​etykieta byłaby w środek każdego otworu 10 mm.

wprowadź opis zdjęcia tutaj

Miro
źródło
2

Po tym, jak ostatnio napotkałem ten sam problem, przygotowałem skrypt QGIS Python, aby przeprowadzić ciężkie podnoszenie. Skrypt zawierający niektóre dane testowe (Wielka Brytania), plik Readme (przewodnik) i użyte arkusze stylów można znaleźć na stronie https://github.com/pjgeng/Contour-Labels

Krótko mówiąc, skrypt używa dwóch warstw wektorowych jako danych wejściowych - z adnotowaną warstwą konturową i warstwą „prowadzącą”. Ten ostatni składa się z polilinii przecinających kontury w pożądanych lokalizacjach etykiet.

Skrypt następnie działa na podstawie odległości między konturami i indeksem konturu, które etykiety mają zostać zastosowane, dodaje wartość obrotu do punktów etykiety i ostatecznie przycina oryginalną warstwę konturu, aby utworzyć przerwy.

Zbliżenie na wynik końcowy.

Podejście to działa szczególnie dobrze, gdy użytkownik musi tworzyć mapy konturowe w różnych odstępach czasu w tym samym obszarze (tzn. Prowadnice się nie zmieniają). Wadą jest niemożność zmiany pozycji etykiety po zakończeniu skryptu. W tym celu użytkownik musiałby dostosować linie pomocnicze i ponownie uruchomić skrypt względem oryginalnego wejścia. Wcześniej często pracowałem z buforami wokół etykiet, aby stworzyć efekt przerwania, ale okazało się to nieprzyjemne estetycznie na mapach opartych na danych wektorowych.

Niestety nie mogę teraz dodać więcej zdjęć, aby udokumentować lub zilustrować proces.

PS: W przypadku korzystania z warstw stylów zawartych w repozytorium użytkownicy mogą potrzebować „aktywować” niestandardowe pola „Obrót”, „Pokaż etykietę” i „Zawsze pokazuj” w menu etykietowania. W niektórych instalacjach QGIS są one stosowane automatycznie z arkusza stylów - jeszcze nie dowiedziałem się, co to powoduje.

Phil G.
źródło
2

Oto inne rozwiązanie problemu maskowania etykiet konturowych QGIS, w którym wykorzystuję funkcjonalność QGIS Spatialite (obecnie QGIS 3.x) wraz z generatorem geometrii do umieszczania etykiet.

To bardzo dynamiczne rozwiązanie pozwala nam natychmiast zmieniać wszystkie rozmiary i położenia etykiet, a nawet przetrwać eksport plików PDF!

wprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutaj

Aby było to możliwe, potrzebujemy następujących części:

  1. Warstwa wektorowa „LINESTRING” lub „MULTILINESTRING” o nazwie „kontury” z 2 polami: „fid” (Interger64 - klucz podstawowy), „elev” (ciąg znaków)
  2. Warstwa wektorowa „LINESTRING” o nazwie „scratch_lines” (patrz czerwone linie na zdjęciu)
  3. Tabela bez geometrii zwana „ustawieniami” do przechowywania globalnego rozmiaru etykiety konturu (jest to proste obejście, ponieważ QGIS nie może do tej pory używać zmiennych projektu w zapytaniach SQL): „fid” (Integer64 - klucz podstawowy), „zmienna” (Ciąg), „wartość” (ciąg)

wprowadź opis zdjęcia tutaj

  1. Warstwa wirtualna o nazwie np. „Contours_with_labels” ze stylem opartym na regułach:

    • Zasada 1: „etykieta” = 1… prosta linia, krycie 0%
    • Zasada 2: ELSE… prosta linia

    • oraz tekst etykiety warunkowej dla reguły 1:

      SPRAWA, GDY etykieta = 1 NASTĘPNIE POZOSTAŁA KONIEC

    • z generatorem geometrii do umieszczania tekstu:

      make_line (punkt_początkowy (geometria $), punkt_końcowy (geometria $))

    • oraz ciąg wyrażeń dla zmiennej wielkości tekstu:

      atrybut (get_feature („ustawienia”, „zmienna”, „contourlabel_size”), „wartość”)

i na koniec, oto zapytanie SQL dla warstwy wirtualnej:

------------------------------------------------------------------------
-- select all contour lines that do not intersect any scratch lines
------------------------------------------------------------------------
select c.geometry,c.elev,0 as label 
from contours c,
       (select st_union(geometry) as geom from scratch_lines) as scr 
where not st_intersects(c.geometry,scr.geom)
------------------------------------------------------------------------

UNION

--------------------------------------------------------------------------------------------------------
-- create buffers around all scratch lines (bufferwidth = length(elevation_text) * txtsize/3),
-- get st_difference between contour lines and buffers
-- and set attribute "label" to 0
--------------------------------------------------------------------------------------------------------
select st_difference(c.geometry,buf.geom) as geom,c.elev,0 as label 
from 
  (select c.fid,st_union(st_buffer(scr.geometry,length(c.elev) * txtsize.value / 3)) as geom 
      from scratch_lines scr, 
              contours c, 
              (select cast(value as integer) as value from settings where variable = 'contourlabel_size') txtsize 
      where st_intersects(scr.geometry,c.geometry) 
      group by c.fid) as buf,
  contours c 
where c.fid = buf.fid
group by c.fid
--------------------------------------------------------------------------------------------------------

UNION

--------------------------------------------------------------------------------------------------------
-- create buffers around all scratch lines (bufferwidth = length(elevation_text) * txtsize/3),
-- get st_intersection between contour lines and buffers
-- and set attribute "label" to 1
--------------------------------------------------------------------------------------------------------
select st_intersection(st_buffer(scr.geometry,length(c.elev) * txtsize.value / 3),c.geometry) as geom,c.elev,1 as label 
from scratch_lines scr,
        contours c,
        (select cast(value as integer) as value from settings where variable = 'contourlabel_size') txtsize 
where st_intersects(c.geometry,scr.geometry)

to jest to!

Ogromne podziękowania dla wszystkich entuzjastycznych ludzi, którzy sprawiają, że jest to możliwe!

Christoph
źródło
Muszę powiedzieć, że jestem pod wrażeniem wyniku. Dopóki nie otrzymamy wbudowanej opcji (jeśli w ogóle), jest to zdecydowanie najczystszy sposób. Ponownie wirtualne warstwy na ratunek.
Gabriel C.
Byłem pod wrażeniem samego siebie. Ale czy może wytrzymać naprawdę duże warstwy konturu?
Christoph
I bardzo się cieszę, po przetestowaniu, że działa również z danymi w GeoPackages! Chciałem zweryfikować, ponieważ jest oparty na Spatialite. Chciałbym móc głosować więcej niż raz ...
Gabriel C.
IMHO, jest niezależny od jakiegokolwiek formatu pliku, ponieważ zapytanie działa w QGIS. Więc powinno nawet działać z CSV.
Christoph
Wygląda na to, że to nie koniec historii. Po dzisiejszych badaniach, zdałem sobie sprawę, że możemy korzystać z QGIS funkcje ekspresyjne wewnątrz zapytania wirtualnego warstwa SQL: gis.stackexchange.com/questions/320991/... . Przed nami jeszcze wiele innych rzeczy, które przyspieszą maskowanie etykiet (myślę o zapytaniach zależnych od skali lub lepszym wykorzystaniu indeksów przestrzennych).
christoph
1

Czy pamiętasz ten wątek Martin? Jedynym sposobem, w jaki mogę podejść do rozwiązania problemu, jest nałożenie warstwy konturowej na przyciętą warstwę konturową, użycie tego do etykietowania i zmiana koloru linii na neutralny, który zamaskowałby kontury pod etykietami, ma się nadzieję, nie będąc zbyt nachalnym. N.

Dodano później: warto spojrzeć na ten wątek , drugą odpowiedź. Być może łamanie linii konturu może być odpowiedzią, być może przy użyciu warstwy buforowej używanej do obcinania konturów?

nhopton
źródło
1

Aby etykiety były bardziej doskonałe, zmieniłem zapytanie SQL warstwy wirtualnej, aby uwzględnić linie rysujące biegnące równolegle do linii konturowych (patrz rozwiązanie poniżej):

Stara wersja wprowadź opis zdjęcia tutaj

Nowa wersja wprowadź opis zdjęcia tutaj

A oto nowy SQL dla warstwy wirtualnej:

------------------------------------------------------------------------
-- select all contour lines that do not intersect any scratch lines
------------------------------------------------------------------------
select c.geometry,c.elev,0 as label 
from contours c,
   (select st_union(geometry) as geom from scratch_lines) as scr 
where not st_intersects(c.geometry,scr.geom)
------------------------------------------------------------------------

UNION

--------------------------------------------------------------------------------------------------------
-- create buffers around all intersection points (bufferwidth = length(elevation_text) * txtsize/2.5),
-- get st_difference between contour lines and buffers
-- and set attribute "label" to 0
--------------------------------------------------------------------------------------------------------
select st_difference(c.geometry,buf.geom) as geom,c.elev,0 as label 
from contours c,
(select c.fid,st_union(st_buffer(st_intersection(c.geometry,scr.geometry),length(c.elev) * txtsize.value / 3)) as geom
from contours c, scratch_lines scr, (select cast(value as integer) as value from settings where variable = 'contourlabel_size') txtsize
where st_intersects(c.geometry,scr.geometry)
group by c.fid) as buf
where c.fid = buf.fid
--------------------------------------------------------------------------------------------------------

UNION

--------------------------------------------------------------------------------------------------------
-- create buffers around all intersection points (bufferwidth = length(elevation_text) * txtsize/2.5),
-- get st_intersection between contour lines and buffers
-- and set attribute "label" to 1
--------------------------------------------------------------------------------------------------------
select st_intersection(c.geometry,st_buffer(st_intersection(c.geometry,scr.geometry),length(c.elev) * txtsize.value / 3)) as geom,c.elev,1 as label 
from contours c, 
 scratch_lines scr,
 (select cast(value as integer) as value from settings where variable = 'contourlabel_size') txtsize 
where st_intersects(c.geometry,scr.geometry)
Christoph
źródło
1

Mały dodatek związany z pierwotnym pytaniem.

Dla wszystkich tych, którzy nie są świadomi faktu, że możemy użyć stylizacji „Generatora geometrii”, aby uprościć i wygładzić nasze linie konturu w QGIS 3.10:

gładka (uproszczenie ($ geometria, 2), 2)

wprowadź opis zdjęcia tutaj

Christoph
źródło
0

Wpis na blogu ESRI: http://blogs.esri.com/esri/arcgis/2011/11/28/variable-depth-masking-contour-label-example/

Maskowanie o zmiennej głębokości dla etykiet konturów obejmuje trzy etapy:

1 tworzenie adnotacji z etykiet, 2 korzystanie z narzędzia Maski konspektu do tworzenia masek oraz 3 korzystanie z Zaawansowanych opcji rysowania> Ustawienia maskowania, aby określić, które warstwy będą maskowane.

Przędzarka
źródło
To pokazuje, jak można to zaimplementować, ale tak naprawdę nie odpowiada na pytanie.
podmrok