Skip to content

Instantly share code, notes, and snippets.

@signedav
Last active March 23, 2022 08:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save signedav/c14a819b9ef8883ae829506d181f4e36 to your computer and use it in GitHub Desktop.
Save signedav/c14a819b9ef8883ae829506d181f4e36 to your computer and use it in GitHub Desktop.
UsabILIty Hub Layertree concept

Note that those are only notes made by @signedav and not a clearly defined concept. See for the implemented part opengisch/QgisModelBaker#648

Layertree, internal Id and Relations

Super Layertree

First of all the layer tree file gets powerfull.

The layer tree file will contain in future:

  • Optional internal id's in the layers (and the groups) - not integrated
  • qml link (or path) - integrated
  • qlr link (or path) - integrated
  • source - integrated

And relations. Not sure yet about the syntax. - not integrated

The internal identifactor not integrated

Every layer can have an internal id (optional).

The "internal" ID (means the ID we use in the Model Baker context) needs to be defined in the layer tree. I see no other place that is better for it. Since the identity of the layer can divert according to it's position.

Format

Format can be open. The user can enter an id manually and is responsible for it.

When generated with the exporter, it can be technical. Slug of layer name + a uuid is nice but it's the same like the real one so it could be confused with. Maybe better usability_layernameslug_uuid.

Backwards compatibility still supported

We can still have the current QML mapping in the metaconfiguration file (INI).

It could be possible to map there with the id as well (but most possibly the mapping there will be not needed anymore because it's in the layer tree). not integrated

Possible that it's still needed: For layers not contained in the layertree (invisible layers) to append there QMLs. not integrated

Why replacing a concept?

The linking in the layer tree is more readable. Still usually a layer tree file was not needed sometimes. But because the id needs to be configured there, it's mostly needed. So it's nicer to put the mapping readable as possible there.

Concerns

Paths instead of ilidata: links

Be aware because of this https://opengisch.github.io/QgisModelBaker/background_info/usabilityhub/technical_concept/#static-file-path

And generally spocken: When using local file paths they are relative to where? Currently the plugin dir (in the QGIS profile folder).

Handling of relation reference / editor widgets

[Resolved] Do they work when I export / import the qml? Yes. The Ids are kind of stable. Only the manually created ones contain uuids etc.

Implementation

IST Zustand:

Aktuell erstellen wir ein Project. Dort fügen wir die Layers (available_layers) hinzu. Unabhängig davon fügen wir eine Legend hinzu, welche die Layers (available_layers) berücksichtigt (anhand von layer.alias). QMLs werden dann zu den Layers des Projects hinzugefügt.

Approach 1:

In Legenden-Erstellung vom Generator

  • Layer mit Source erstellen
  • Layer mit QLR erstellen
  • Layer von available_layer laden (falls kein QLR oder Source gegeben)
  • QML für Layer laden

Fragen:

  • Sollen available_layer zum Projekt gefügt werden, wenn auch nicht im layertree? Ich denke ja, da oft Kataloglayer etc. wohl nicht in den Layertree kommen und schnell vergessen werden.
  • Wenn QML für available_layer, die nicht im layertree sind, wohin damit? Könnten momentan noch wie gehabt vom Ilidata auf den Layername geparsed hinzugefügt werden.
  • Möglich custom_layers (nicht available_layers) laden, die nicht im layertree sind? Entweder genauso wie bisher im Metaconfig-File (einfach auch mit Ids und QLR) oder mit einem Keyword hidden.
  • Relativer Pfad wohin? Plugin dir? Eigentlich am besten relativ zum File. Also müsste viellecht noch ein Pfad mitgegeben werden.
  • Wann werden die Toppings heruntergeladen? Erst beim erstellen? Why not, aber dafür einzelne Requests, parsing dauert länger. Falls Problem könnte man ilidata: parsen - vorgängig herunterladen und als PathResolver dann nur das Mapping angeben...

Wie spielts mit Relation mit?

in Relation_Info stehen die Tabellennamen. Es wird dann die darauf lautende Layer Objekt genommen und ins Relation Objekt geschrieben. Beim Relation.create wird dann die id() des Layers genommen für die erstellung der QGIS Relation.

Man müsste also dem Layer nicht nur den name mitgeben sondern auch seine "interne" id *. Beim erstellen der relations soll dann im mapping nicht namelayer sondern interne_idlayer gemacht werden.

* Obacht: da haben wir nun Layer.name (was wohl der tabellenname ist) Layer.alias (schöner Name, der zBs. im Layertree genutzt wird) und Layer.internal_id...

Schema thoughts

https://asdf-standard.readthedocs.io/en/1.5.0/schemas/yaml_schema.html

http://json-schema.org/draft-04/json-schema-validation.html

Implementation

IST ZUSTAND

Man stellt die einzelnen Teile zusammen:

Generator generiert anhand von DB Daten und Metainformationen.

  • generator.layers -> Layer[]
  • generator.relations -> Relation[]
  • generator.bags_of_enum -> {}
  • generator.legend -> Legend (mit evtl. Layertree-Struktur)
  • custom_layer_order_structure -> [] (würde evtl. Sinn machen im generator zu haben)

Dann macht man:

project = Project()
project.layers = available_layers
project.relations = relations
project.bags_of_enum = bags_of_enum
project.legend = legend
project.custom_layer_order_structure = custom_layer_order_structure

und dann:

project.post_generate()
qgis_project = QgsProject.instance()
project.create(None, qgis_project, group)

QMLs werden im Nachhinein geladen. Auf die qgis-layer:

layer.layer.loadNamedStyle(style_file_path)

SOLL ZUSTAND "Straigh-Foreward"

Id Implementierung

  • Ids werden in die einzelnen Layer[] geschrieben im generator.legend.

QML Implementierung

Pfade im layertree

  • QML Pfade werden im generator.legend dem Layer hinzugefügt.
  • QMLs werden im Layer.create in den qgis-layer geladen.

Weird oder auch hässlich, da Layer im legend() noch bearbeitet werden?

Pfade im metaconfig

  • QMLs werden nicht nur Anhand des Namens sondern auch der Id im nachhinein geladen (oder einfach Pfade hinzugefügt zu den Layern nach der generate.legend um sie dann genau gleich im Layer.create zu laden.)

QLR Implementierung

Pfade im layertree

  • Layer wird erstellt.
  • QLR Pfade werden im generator.legend dem Layer hinzugefügt.
  • Werden im Layer.create dann erstellt.

Pfade im metaconfig

  • Müsste vor wie generator.layers vor dem Erstellen von Project kommen. Dort wird dann die Id gespeichert.
  • Werden im Layer.create dann erstellt.

Source Implementierung

Pfade im layertree

  • Layer wird erstellt.
  • Source Info werden im generator.legend dem Layer hinzugefügt.
  • Werden im Layer.create dann erstellt.

Pfade im metaconfig

  • Müsste vor wie generator.layers vor dem Erstellen von Project kommen. Dort wird dann die Id gespeichert.
  • Werden im Layer.create dann erstellt.

Laden der Layers

https://docs.qgis.org/3.22/en/docs/pyqgis_developer_cookbook/loadlayer.html

SHP und GPKG mit ogr

Postgres URI needs to contain connection parameters

Ablauf für alle Fälle

  • Datenbank Layers werden erstellt mit generator.layers (haben keine Id)
  • Metaconfig QLR Layers werden erstellt (Pfade in Layer() geschrieben zusammen mit deren Id.
  • Metaconfig Source Layers werden erstellt (Daten in Layer() geschrieben zusammen mit deren Id.
  • Legend wird erstellt mit generator.legend, dabei wird folgendes gemacht:
    • Wenn Id in Layer (weil QLR oder Source) vorhanden, wird dieser existierende Layer in die Legend geschrieben.
    • Wenn QLR Source in Legend: Layer erstellen und in Legend schreiben.
    • Wenn Source in Legend: Layer erstellen und in Legend schreiben.
    • Wenn Ili-Name (Source) in Legend: Layer anhand des Ili-Names holen, wenn kein Ili-Name gefunden anhand des Namens holen, Id rein schreiben und in Legend schreiben.
    • Wenn weder Source noch QLR noch Ili-Name in Legend: Layer anhand des Namens holen, Id rein schreiben und in Legend schreiben.
    • Wenn QML in Legend: QML Pfad in Layer schreiben.
  • Metaconfig QML Pfade in Layer() schreiben.
  • Mit layer.create (im project.create) die Layers mit Source oder QLR erstellen und QMLs laden.

Betr ili_name etc.

Layer mit anderem Name dann irgendwie so erstellen:

        if "ili_name" in item_properties:
            found_layer = [layer for layer in layers if layer.ili_name == item_properties["ili_name"] and layer.geometry_column == item_properties.get("geometry_column")][0]
            layer = found_layer
            layer.alias = node_name

Means:

Creating Layers

class Layer(object):
    def __init__(
        self,
        [...],
        identificator,
        definitionfile,
        stylefile,

while source will be we taken over:

  • provider
  • uri
  • srid
  • extent
  • geometry_column
  • and name is taken

Creation is like creation is, but additionally all overwritten by definitionfile. In the end the stylefile is applied to it.

The legend

generator.legend should be called right after generator.layers. Maybe it even could be bundled. Should it be renamed (to layertree? no.)

layertree_structure will be passed here. name is fine, since it's the layertree name. Maybe we could rename in the yaml "legend" with "layertree".

layertree:

layerorder:

relations:

Sollen projectlayers von legend gesplittet werden um "unsichtbare" layers zu erzeugen?

metaconfig definition (should be "bad practice")

[qgis.modelbaker.qml]
<identificator> = file oder ilidata
[qgis.modelbaker.qlr]
<identificator> = file oder ilidata
qlr

Evtl. erstellen von DefinedLayer. Section wird identificator (kann auch name sein) werden generator.definedlayers gepasst. Oder mal schauen. Auf jeden fall sollen nacher die Layers im available_layers drin sein.

qml

Evtl. appendStylefiles die allen available_layers angehängt werden. Dies aber erst nach dem generate.legend.

layertree definition

definitionfile = file oder ilidata
stylefile = file oder ilidata

Dann im erstellen der Nodes DefinedLayer und layer.stylefile = ...

Aber dann muss available_layers zurück gegeben werden irgendwie.

Path resolver

Ein Path Resolver soll übergeben werden. Dieser ist default = pfad, absolut oder relativ zum Model Baker (oder whatever). und ansonsten macht er ein file download. Mal schaun wie das geht...

Relation thoughts

  • Wenn generator.legend erst die id's hinzufügt, muss generator.relations erst nachher aufgerufen werden um die Relations mit den ids zu erstellen.
  • Relations müssen evtl. anhand der Ids und nicht der Namen erstellt werden. Aber das können wir noch alles schauen. Wichtig ist, dass die Layer[] alle mit Id erstellt sind, bevor wir die Relations laden...

To do

Renaming of existing function (providing backwards compatibility of course)

Should it be named qgis_toppingfile instead? Or qgis_project_topping? Instead of qgis.modelbaker.layertree and the folder layertree?

Ideas:

  • qgis.modelbaker.projecttopping (evtl.)
  • qgis.modelbaker.projectstructure (<- -1 because it's not only the structure)
  • qgis.modelbaker.projectfile (<- -1 because it can be confused with a file)

And the folder? Should it be yaml in future or not? And if not should it be style and layerdefinition (instead of qml etc) and metaattributes (instead of TOML) -> this is more something for the exporter...

In the YAML is the legend - since it gets more powerfull it could be layertree, layerstructure or layers?

checked vs. visible

In QGS file we have the layers checked or not and we have there a <legend> visible or not. But this legend structure is obsolete and visible with it. So forget about that. The used ones are <layer-tree-group> and <layer-tree-layer>.

The datasource in the QLR file is relative

Means we cannot move the file around.

The group from QLR is added at the end

I dunno know why

Make Paths default relative to the YAML file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment