V tomto blogu si ukážeme příklad použití tvorby atlasu, který vychází z reálného dotazu účastníka našeho kurzu QGIS pro pokročilé. Princip použití atlasu spočívá v automatické tvorbě sady mapových výstupů na základě jednotlivých prvků ve vrstvě. Jak se takový atlas dá vytvořit popisujeme v materiálech pro školení (kapitola Tvorba atlasu). To je ale jen zlomek možností, které atlas nabízí. Díky možnosti provázání hodnot s různými parametry, využití vzorců nebo malých Python skriptů, máme při generování vícelistých výstupů téměř neomezené možnosti.
Požadavek účastníka kurzu spočíval v tom, že potřeboval vytvořit sérii mapových výstupů, které by zobrazovaly jedno území s podkladovými daty a každá jednotlivá stránka by navíc zobrazovala jednu zájmovou liniovou vrstvu. Tedy atlas, který nebude iterovat nad prvky v jedné vrstvě, ale and konkrétními vrstvami v projektu. Vrstev pro jednotlivé zobrazení bylo řádově desítky, data měla podobnou strukturu, jen zobrazovala jiný jev.
Princip navrhovaného řešení je použití tabulky, kde budou v řádcích vypsány unikátní identifikátory vrstev (layer_id
), pro které budeme atlas vytvářet. Tuto tabulku použijeme jako vrstvu pokrytí v atlasu a nastavíme pojmenování stran podle sloupce s identifikátory. Tím zajistíme vytvoření strany atlasu pro každý identifikátor vrstvy.
U těchto vrstev potom nastavíme symbologii podle pravidla, kdy necháme zobrazit prvky ve vrstvě pouze pokud se jejich id shoduje s názvem stránky atlasu. Použitím právě layer_id
docílíme toho, že se atlas vygeneruje správně i pokud budou mezi vrstvami některé se shodným jménem. Zároveň necháme zobrazit ostatní podkladové vrstvy, které budou statické – zobrazené na každé straně atlasu.
Navrhované řešení se záměrně vyhýbá editaci nebo slučování vstupních dat. Řešení je poměrně přímočaré, ale provedení může pokulhávat, zejména v případě kdy máme k zobrazení desítky nebo stovky vrstev. Takže si proces musíme trochu zautomatizovat. Jak na to?
V první řadě musíme vytvořit tabulku se sloupcem s unikátními identifikátory vrstev v projektu (proměnná layer_id
). Identifikátor lze zjistit ve vlastnostech vrstvy v záložce proměnné, a můžeme začít kopíro… řekli jsme automatizovat! :). Při tvorbě tabulky si tedy pomůžeme malým Python skriptem, který v základu výtahne layer_id
všech vrstev v konkrétní skupině a vytvoří csv tabulku s jedním sloupcem, ve kterém budou získané hodnoty zapsané. Dáme všechny požadované vrstvy do jedné skupiny, zapneme python konzoli (Zásuvné moduly –> Python console) a z vestavěného textového editoru spustíme skript pomocí tlačítka (je nutné zadat název skupiny group_name
a cestu pro uložení tabulky csv_path
):
goup_name = "group1" csv_path = "/cesta/pro/tabulku.csv" root = QgsProject.instance().layerTreeRoot() group_layers = [] for i in root.findGroup(group_name).findLayers(): group_layers.append(i.layerId()) with open(csv_path, "w") as f: for item in group_layers: f.write("%s\n" % item)
Nyní nahrajeme tabulku pomocí Přidat vrstvu s odděleným textem…, a nebo rozšíříme náš skript a necháme ho nahrát tabulku za nás. Na konec skriptu stačí přidat:
uri = ( 'file://{}?type=csv&delimiter=%20&useHeader=No&detectTypes=yes' '&geomType=none&subsetIndex=no&watchFile=no' ).format(csv_path) layer = iface.addVectorLayer( uri, group_name + "_list", "delimitedtext" )
V první řadě vložíme do mapového výstupu mapové okno, které nastavíme podle potřeb. Potom přejdeme k nastavení atlasu. Jako vrstvu pokrytí nastavíme naší tabulku a pro generování názvů stran zvolíme sloupec (máme pouze jeden – field_1
). Nyní když spustíme náhled atlasu, vidíme, že se nám vygenerovala stránka pro každé layer_id
. Teď zbývá pouze zařídit, aby se na stránkách zobrazovali jen ty vrstvy, které chceme. Tedy vrstva, která má hodnotu layer_id
shodnou s názvem strany atlasu. Toho docílíme nastavením pravidla v symbologii vrstev. V takovém případě budeme ignorovat standardní ovládání atlasu pro mapovým oknem, které bude neaktivní.
Otevřeme vlastnosti jedné z vrstev ve skupině a zvolíme symbologii podle pravidla. Otevřeme jedinou položku, nastylujeme podle potřeb a nastavíme filtr:
@layer_id = @atlas_pagename
Díky tomuto pravidlu se symbologie použije pouze, pokud výsledek filtru bude pravdivý, v našem případě – id vrstvy se shoduje s názvem strany v atlasu.
Posledním krokem je nastylování všech ostatních vrstev stejným způsobem. Toho docílíme jednoduše tak, že zkopírujeme styl vrstvy, ve které jsme nastavili symbologii, a tento styl použijeme pro všechny vrstvy ve skupině (pravým tlačítkem klikneme na položku skupiny a vybereme vložit styl). Nevýhodou by mohlo být, že všechny vrstvy budou mít ve výstupech stejný styl. Pokud by to byl problém, dá se to ošetřit například generováním náhodných barev u symbolů, kdy jako hodnotu barvy vložíme vzorec, kdy položky RGB budou náhodná čísla ve zvoleném rozmezí.
Nicméně celý krok 3 si můžeme usnadnit “vytuněním” našeho skriptu. Do smyčky, která iteruje nad vrstvami, přidáme nastavení symbologie podle pravidla s našim filtrem (inspirováno příspěvkem na gis.stackexchange.com):
group_layers = [] for i in root.findGroup(group_name).findLayers(): group_layers.append(i.layerId()) layer = i.layer() symbol = QgsSymbol.defaultSymbol(layer.geometryType()) renderer = QgsRuleBasedRenderer(symbol) root_rule = renderer.rootRule() rule = root_rule.children()[0] rule.setLabel("Atlas") rule.setFilterExpression('@layer_id = @atlas_pagename') layer.setRenderer(renderer)
Nyní nezbývá nic než doplnit mapový výstup o další potřebné prvky. Prvky lze také nastavit tak, aby se generovaly na základě proměnných hodnot (např. texty). V případě použití proměnných vrstvy na straně atlasu však budeme muset tuto vrstvu specifikovat (nepoužíváme ovládání atlasem). To zajistíme funkcí layer_property(@atlas_pagename)
, pro název vrstvy např.:
[% layer_property(@atlas_pagename,'name') %]
Jakmile jsme se vzhledem výstupu spokojení, můžeme atlas exportovat do jednoho pdf dokumentu nebo do samostatných výstupů.
– vytvoří (a nahraje do projektu) csv tabulku se seznamem id vrstev (layer_id
) v konkrétní skupině
– u vrstev ve skupině nastaví styl podle pravidla tak, aby se pro každou stranu atlasu zobrazila pouze jedna vrstva
(Aktuální verzi skriptu lze nalázet na GitHub)
group_name = "nazev_skupiny" csv_path = "cesta/pro/vytvoreni/tabulky.csv" root = QgsProject.instance().layerTreeRoot() group_layers = [] for i in root.findGroup(group_name).findLayers(): group_layers.append(i.layerId()) layer = i.layer() symbol = QgsSymbol.defaultSymbol(layer.geometryType()) renderer = QgsRuleBasedRenderer(symbol) root_rule = renderer.rootRule() rule = root_rule.children()[0] rule.setLabel("Atlas") rule.setFilterExpression('@layer_id = @atlas_pagename') layer.setRenderer(renderer) with open(csv_path, "w") as f: for item in group_layers: f.write("%s\n" % item) uri = ( 'file://{}?type=csv&delimiter=%20&useHeader=No&detectTypes=yes' '&geomType=none&subsetIndex=no&watchFile=no' ).format(csv_path) layer = iface.addVectorLayer( uri, group_name + "_list", "delimitedtext" )