Wie ermittle ich benachbarte Kachel-IDs in QGIS?

11

Ich wurde kürzlich in einem Schulungskurs gefragt, ob QGIS die nächsten / vorherigen und oberen / unteren Seitenzahlen für ein mit dem Atlasgenerator erstelltes Kartenbuch automatisch berechnen kann. Ich habe es geschafft, einen ziemlich vernünftigen Beschriftungsausdruck für ein reguläres Raster zu erarbeiten, wenn Sie die Breite und Höhe des Rasters kennen.

Aber dann haben wir uns realistische Beispiele ausgedacht, bei denen wir keine Seiten zeichnen möchten, die nicht unseren interessierenden Bezirk enthalten, wie diesen aus meinem Heimatland:

Geben Sie hier die Bildbeschreibung ein

Also habe ich heute Nachmittag ein Python-Skript gespielt, um die 4 Nachbarn, an denen ich interessiert war, für jede Gitterzelle herauszufinden und diese Werte zu meinem Gitter hinzuzufügen (dies basiert stark auf Ujaval Gandhis Tutorial ):

for f in feature_dict.values():
    print 'Working on %s' % f[_NAME_FIELD]
    geom = f.geometry()
    # Find all features that intersect the bounding box of the current feature.
    # We use spatial index to find the features intersecting the bounding box
    # of the current feature. This will narrow down the features that we need
    # to check neighboring features.
    intersecting_ids = index.intersects(geom.boundingBox())
    # Initalize neighbors list and sum
    neighbors = []
    neighbors_sum = 0
    for intersecting_id in intersecting_ids:
        # Look up the feature from the dictionary
        intersecting_f = feature_dict[intersecting_id]
        int_geom = intersecting_f.geometry()
        centroid = geom.centroid()
        height = geom.boundingBox().height()
        width = geom.boundingBox().width()
        # For our purpose we consider a feature as 'neighbor' if it touches or
        # intersects a feature. We use the 'disjoint' predicate to satisfy
        # these conditions. So if a feature is not disjoint, it is a neighbor.
        if (f != intersecting_f and
            not int_geom.disjoint(geom)):
            above_point = QgsGeometry.fromPoint(QgsPoint(centroid.asPoint().x(),
               centroid.asPoint().y()+height))
            below_point = QgsGeometry.fromPoint(QgsPoint(centroid.asPoint().x(),
               centroid.asPoint().y()-height))
            left_point = QgsGeometry.fromPoint(QgsPoint(centroid.asPoint().x()-width,
               centroid.asPoint().y()))
            right_point = QgsGeometry.fromPoint(QgsPoint(centroid.asPoint().x()+width,
               centroid.asPoint().y()))
            above = int_geom.contains(above_point)   
            below = int_geom.contains(below_point)   
            left = int_geom.contains(left_point)
            right = int_geom.contains(right_point)
            if above:
                print "setting %d as above %d"%(intersecting_f['id'],f['id'])
                f['above']=intersecting_f['id']

            if below:
                print "setting %d as below %d"%(intersecting_f['id'],f['id'])
                f['below']=intersecting_f['id']

            if left:
                print "setting %d as left of %d"%(intersecting_f['id'],f['id'])
                f['left']=intersecting_f['id']

            if right:
                print "setting %d as right of %d"%(intersecting_f['id'],f['id'])
                f['right']=intersecting_f['id']

    # Update the layer with new attribute values.
    layer.updateFeature(f)

layer.commitChanges()

Das funktioniert gut.

Geben Sie hier die Bildbeschreibung ein

Aber um ehrlich zu sein, scheint es falsch, das Ganze einen Testpunkt nach Norden zu schaffen und dann alle möglichen Nachbarn zu testen. Nach einem Nachmittag, an dem ich mir das Gehirn zerbrochen habe, kann ich mir keinen besseren Weg vorstellen, um festzustellen, was der nördliche Nachbar einer bestimmten Gitterzelle ist.

Idealerweise hätte ich gerne etwas, das einfach genug ist, um es in ein Textfeld für Druckkomponisten einzufügen, aber ich vermute, das ist zu viel verlangt.

Ian Turton
quelle
Was ist, wenn es auf einer Seite keinen Nachbarn gibt? Möchten Sie den Wert der Closeset-Zelle in eine Richtung oder möchten Sie eine Lücke hinterlassen?
Radouxju
Ich freue mich über eine Null in diesem Fall. Ich kann die Beschriftung einfach so einstellen, dass sie nur angezeigt wird, wenn sie nicht null oder leer ist.
Ian Turton

Antworten:

3

Wenn Sie nicht jede Seitenausdehnung (von der Indexebene) genau in den Composer einpassen, sondern überlappende Ränder mit benachbarten Seiten haben (wie in Ihrem zweiten Screenshot gezeigt), können Sie Beschriftungen aus der Indexebene verwenden, mit dem Nachteil, dass Sie würden sich innerhalb der Kartengrenze befinden.

Wenn es keine Überlappung gibt, können Sie eine Technik, die ich in der Vergangenheit erfolgreich angewendet habe (zufällig in E & W Sussex!), In MapInfo replizieren, wo ich ein kleines Skript geschrieben habe, das für jedes Index-Feature einen Satz von vier Punkten generiert , in die angrenzenden Features versetzt, mit Attributen sowohl der Blattnummer als auch der Versatzrichtung. Die Punktebene wurde dann verwendet, um erneut Beschriftungen zu erzeugen, wobei die Richtung des Versatzes es ermöglichte, die Ausrichtung der Beschriftungen für einen schöneren Effekt anzupassen.

Ich habe dies nicht versucht, aber Sie können möglicherweise vermeiden, eine separate Datenschicht in QGIS zu generieren, indem Sie die neue Geometriegenerator-Styling-Funktion verwenden. Dies würde zu einer eleganteren und dynamischeren Lösung führen, die in MapInfo nicht erreichbar war!

Andy Harfoot
quelle
Ich hätte wirklich daran denken sollen, nur die Beschriftungen der anderen Polygone zu verwenden! :-) Nach einem kurzen Experiment mit dem Geometrie-Generator kann ich einen Begrenzungsrahmen zeichnen, aber es ist schwieriger, ein Gitter zu erstellen
Ian Turton
Ich dachte an die Erzeugung von Beschriftungspunkten, die in die benachbarten Polygone versetzt sind, anstatt in Gitter. Eine andere Möglichkeit wäre, den MBR des Index-Features in die angrenzenden Features zu erweitern, damit Beschriftungen gezeichnet werden können.
Andy Harfoot
Hatte gerade ein Spiel und es scheint, dass die vom Geometriegenerator-Styling erzeugte Geometrie nicht beschriftet wird, also nicht die elegantere Lösung, auf die ich gehofft hatte.
Andy Harfoot
8

Tatsächlich haben Sie bereits den größten Teil der erforderlichen Arbeit geleistet, um die Kacheln zu bestimmen, die Sie mit Atlas drucken möchten. Der Punkt ist jedoch, wie Sie alles zusammen anpassen, um nur die Kachel-IDs anzuzeigen, die Sie benötigen. Um meine Idee zu demonstrieren, werde ich in diesem Beispiel ein DEM-Bild und eine Gittervektordatei verwenden, wie Sie unten sehen können:

Geben Sie hier die Bildbeschreibung ein

Zuerst müssen wir die Beschriftung jedes Gitters zeigen.

In der Layoutansicht habe ich das Raster als Abdeckungsebene im Atlas verwendet und zwei Karten erstellt: die Hauptfensterkarte und eine Indexkarte, die nur das Raster anzeigt, wie Sie unten sehen können:

Geben Sie hier die Bildbeschreibung ein

Dann habe ich folgendes gemacht:

  1. Ich habe den Maßstab der Indexkarte angepasst, um die gesamte Gitterausdehnung anzuzeigen, und dann den Maßstab festgelegt
  2. Ich habe die Ansichtsausdehnung korrigiert, um zu verhindern, dass die Karte bei Verwendung von und verschoben Preview atlaswird
  3. Ich habe das aktiviert Overview, um den Umfang und die Position der Hauptansichtskarte zu sehen, wie Sie unten sehen können:

Geben Sie hier die Bildbeschreibung ein

Für die Hauptfensterkarte habe ich den Maßstab auf die Ausdehnung jedes Rasterblocks festgelegt, um sicherzustellen, dass der Maßstab nicht geändert wird, wenn etwas passiert, wie Sie unten sehen können.

Geben Sie hier die Bildbeschreibung ein

Mithilfe einer Indexkarte können Sie die ID und Position jeder Kachel in Bezug auf eine andere Kachel leicht anzeigen, selbst wenn Sie das Raster im Hauptfenster der Ansichtskarte ausschalten. Die folgende Karte hat beispielsweise eine Kachel-ID = 14, und Sie können die umgebenden Kachel-IDs sehen.

Geben Sie hier die Bildbeschreibung ein

Update :

Ich werde meine Antwort aktualisieren, da mir klar wurde, dass Sie den Seitenzahlenindex der umgebenden Layouts und nicht die IDs der umgebenden Layouts anzeigen möchten.

Um das Verständnis des Prozesses zu erleichtern, aktualisiere ich die ID-Nummern in der Indexzuordnung, um die Nummer der Layoutseite anzuzeigen, wie unten gezeigt:

Geben Sie hier die Bildbeschreibung ein

Da die IDs, die ich habe, bei 0 (Null) beginnen, beginnt die ID des ersten auf der Indexkarte angezeigten Rasters bei 3. Daher möchte ich die Seitenzahl so ändern, dass sie bei 1 beginnt, indem ich 2 von der ID-Nummer in Atlas subtrahiere: Page number: ID -2Dann verwende ich die aktuelle Seitenzahl als Referenz im Ausdruck, um Beschriftungen für die aktuelle Seite, die vorherige Seite, die nächste Seite, die obere Seite und die untere Seite wie folgt zu erstellen:

Geben Sie hier die Bildbeschreibung ein

  • Die aktuelle Seite enthält diesen Ausdruck im Beschriftungstextfeld: Current Page Number: [%@atlas_pagename%]

  • Vorheriger [%if((@atlas_pagename = 1), Null, '↑ Page Number: ' || (@atlas_pagename - 1))%]Seitenausdruck : da vor 1 keine Seiten vorhanden sind

  • Ausdruck der nächsten Seite: [%if( (@atlas_pagename = 25), Null, '↓ Page Number: ' || (@atlas_pagename + 1))%]da nach 25 keine Seiten mehr vorhanden sind

  • Up Page Ausdruck: [%if((@atlas_pagename <= 6),NULL,'↑ Page Number: ' || (@atlas_pagename -6))%]da in oberer Richtung keine Seiten vor 6 vorhanden sind

  • Ausdruck unter der Seite: [%if((@atlas_pagename >= 20), Null, '↓ Page Number: ' || (@atlas_pagename + 6))%]da nach 20 keine Seiten in der unteren Richtung vorhanden sind

Einige Ausgabeergebnisse:

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

ahmadhanb
quelle
Obwohl nützlich, beantwortet dies seine Frage nicht.
Victor
@ Victor Danke für deinen Kommentar, ich habe meine Antwort aktualisiert.
Ahmadad
Dies funktioniert in Ihrem (und seinem) Beispiel, da die Seiten der Keymap / des Rasters regelmäßig sind. Wenn sie nicht gerade wären, würde es nicht funktionieren, da die Zahl zum Addieren oder Subtrahieren (6 in Ihrem Beispiel) abhängig von der Atlas-Seite, auf der Sie sich befinden, variieren würde.
Victor
2
Ich stimme mit Ihnen ein. Wenn das Raster nicht regelmäßig ist, ist der Vorgang komplizierter. Da er es jedoch auf ein reguläres Raster anwenden möchte, funktioniert die in meiner Lösungsvorschlag angewandte Methode.
Ahmadhanb
Ich beachte nur die Tatsache, falls Sie eine andere gute Idee haben! Zumal mein Gitter nicht regelmäßig ist!
Victor
2

Diese Lösung funktioniert für rechteckige Gitter und ist automatisch (sollte für jedes Szenario funktionieren, ohne etwas manuell anzupassen).

Nehmen wir an, Sie haben ein Raster mit Seitenzahlen. Sie können mein Verarbeitungsskript ausführen, indem Sie die Rasterebene und ihr Seitenzahlfeld als Parameter auswählen. Das Skript erstellt vier Felder ( right, left, above, below) in der Rasterebene und berechnet die entsprechende Nachbarseiten-ID für jede Rasterzelle. Dann können Sie Ihre Ausdrücke (z. B. [% if( "left" is not NULL, 'to page' || "left", "" ) %]) verwenden, um Beschriftungen der Nachbarseiten anzuzeigen.

Fügen Sie einfach mein Repository ( https://github.com/gacarrillor/QGIS-Resources.git ) vom QGIS Resource Sharing-Plugin hinzu und installieren Sie das Skript: Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Wie es funktioniert

Das Skript ermittelt die Beziehung (rechts, links, oben oder unten) durch Vergleichen der Koordinaten der Begrenzungsrahmen sowohl von der aktuellen Gitterzelle als auch von jeder sich überschneidenden Zelle. Es stellt sich heraus, dass für jede Beziehung eine der Koordinaten fehlt.

Wenn die Beziehung ist above, ist die fehlende Koordinate yMin, dh alle anderen 3 Koordinaten aus dem Begrenzungsrahmen der aktuellen Gitterzelle sind im Begrenzungsrahmen der obigen Zelle vorhanden. Beachten Sie, dass die QGIS-Begrenzungsrahmen in dieser Reihenfolge definiert sind : [xMin, yMin, xMax, yMax].

Nehmen wir für ein numerisches Beispiel Rechtecke mit Seiten der Länge 1. Angenommen, der Begrenzungsrahmen der aktuellen Zelle ist definiert als bbox1=[0,0,1,1]. Der Begrenzungsrahmen der obigen Zelle wäre definiert als bbox2=[0,1,1,2]. X-Koordinaten von bbox1 sind in bbox2 vorhanden, während bbox1 yMinin Y-Koordinaten von bbox2 fehlen.

Wir können unsere 4 Beziehungen auf diese Weise definieren (o: vorhanden, #: fehlt):

right: [#,o,o,o]
above: [o,#,o,o]
left:  [o,o,#,o]
below: [o,o,o,#]

Wie Sie sehen können, gibt uns der fehlende Index alle Informationen, die wir benötigen.

Germán Carrillo
quelle