Visualizzazione variabile delle linee sovrapposte in QGIS?


10

Quando i punti si sovrappongono, esiste questa proprietà che consente di visualizzarli automaticamente separatamente attorno a dove si trovano, chiamati "Spostamento dei punti". Ma non funziona per le linee, anche se mi sembra abbastanza concettualmente realizzabile per ottenere qualcosa del genere:

inserisci qui la descrizione dell'immagine

Ho assolutamente bisogno di vedere le diverse linee che in realtà sono tutte nello stesso posto (sto lavorando in reti di telecomunicazione). L'unico modo in cui vedo per ora è creare davvero linee diverse come nella foto sopra, creando così errori spaziali.

Sto usando QGIS 2.14.


Penso che si possa fare qualcosa ricorrendo allo styling. La linea nel mezzo è la linea di partenza? Quindi, vedo che hai creato ognuna delle altre linee usando tre diverse geometrie, quindi la mia domanda è se ci sono alcune regole aggiuntive specifiche nel renderle?
mgri,

@mgri Non sono sicuro di capire la tua domanda. L'immagine fornita è un esempio in cui ho tracciato cinque diverse linee a scopo dimostrativo. In realtà sarebbe più che queste 5 linee si trovino effettivamente sul punto di quella centrale (sono dei fili, quindi tutti bloccati nella stessa guaina).
GuiOm Clair,

1
È anche possibile eseguire il rendering di linee con uno spostamento ("offset"), ma non si incontrerebbero nei punti iniziale e finale.
AndreJ,

@AndreJ Sì, e un altro problema sarebbe che sarebbe un'operazione abbastanza manuale in cui avrei bisogno di qualcosa di più automatico poiché sarebbe usato da molti utenti.
GuiOm Clair,

1
@GuiOmClair Seguendo l'immagine allegata, ho assunto che tu partissi da una linea che si sovrappone (ad esempio) ad altre quattro linee e che devi trovare un modo per visualizzarle separatamente, anche se si sovrappongono. Ho appena detto che potrebbe essere possibile riprodurre ciò che viene visualizzato nell'immagine allegata senza la necessità di creare nuove geometrie (ma ricorrendo solo alle proprietà di stile del livello iniziale). Un altro modo sarebbe quello proposto da AndreJ, ma sembra che non si adatti alle tue esigenze.
mgri,

Risposte:


12

Propongo un approccio che ricorre solo a un generatore di geometria e una funzione personalizzata.

Prima di iniziare, voglio sottolineare che focalizzerò l'attenzione sulla spiegazione delle cose minime da fare per riprodurre il risultato desiderato: questo significa che alcuni altri parametri minori (come dimensioni, larghezze e così via) dovrebbero essere facilmente regolati da te per adattarsi meglio alle tue esigenze.

Pertanto, questa soluzione funziona sia per i sistemi geografici sia per i sistemi di riferimento proiettati: di seguito, ho assunto di utilizzare un CRS proiettato (ovvero le unità di misura sono metri), ma è possibile modificarle in base al proprio CRS.


Contesto

Supponiamo di iniziare da questo livello di vettore lineare che rappresenta i fili (le etichette rappresentano il numero di fili sovrapposti (coincidenti)):

inserisci qui la descrizione dell'immagine


Soluzione

Innanzitutto, vai a Layer Properties | Stylee quindi scegli il Single symbolrenderer.

Dalla Symbol selectorfinestra di dialogo, scegliere un Geometry generatortipo di livello come simbolo e Linestring / MultiLinestringcome tipo di geometria. Quindi, fai clic sulla Function Editorscheda:

inserisci qui la descrizione dell'immagine

Quindi, fai clic su New filee digita draw_wirescome nome della nuova funzione:

inserisci qui la descrizione dell'immagine

Vedrai che una nuova funzione è stata creata ed è elencata sul lato sinistro della finestra di dialogo. Ora, fai clic sul nome della funzione e sostituisci il valore predefinito @qgsfunctioncon il seguente codice (non dimenticare di aggiungere tutte le librerie allegate qui):

from qgis.core import *
from qgis.gui import *
from math import sin, cos, radians

@qgsfunction(args='auto', group='Custom')
def draw_wires(angle, percentage, curr_feat, layer_name, feature, parent):

    def wires(polyline, new_angle, percentage):
        for x in range(0, len(polyline)-1):
            vertices = []
            first_point = polyline[x]
            second_point = polyline[x +1]
            seg = QgsGeometry.fromPolyline([first_point, second_point])
            len_feat = seg.length()
            frac_len = percentage * len_feat
            limb = frac_len/cos(radians(new_angle))
            tmp_azim = first_point.azimuth(second_point)
            angle_1 = radians(90 - (tmp_azim+new_angle))
            dist_x, dist_y = (limb * cos(angle_1), limb * sin(angle_1))
            point_1 = QgsPoint(first_point[0] + dist_x, first_point[1] + dist_y)
            angle_2 = radians(90 - (tmp_azim-new_angle))
            dist_x, dist_y = (limb * cos(angle_2), limb * sin(angle_2))
            point_2 = QgsPoint(second_point[0] - dist_x, second_point[1] - dist_y)
            tmp_azim = second_point.azimuth(first_point)
            angle_3 = radians(90 - (tmp_azim+new_angle))
            dist_x, dist_y = (limb * cos(angle_3), limb * sin(angle_3))
            point_3 = QgsPoint(second_point[0] + dist_x, second_point[1] + dist_y)
            angle_4 = radians(90 - (tmp_azim-new_angle))
            dist_x, dist_y = (limb * cos(angle_4), limb * sin(angle_4))
            point_4 = QgsPoint(first_point[0] - dist_x, first_point[1] - dist_y)
            vertices.extend([first_point, point_1, point_2, second_point, point_3, point_4, first_point])
            tempGeom = QgsGeometry.fromPolyline(vertices)
            num.append(tempGeom)
        return num


    layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0]

    all_feats = {}
    index = QgsSpatialIndex()
    for ft in layer.getFeatures():
        index.insertFeature(ft)
        all_feats[ft.id()] = ft

    first = True

    tmp_geom = curr_feat.geometry()
    polyline = tmp_geom.asPolyline()
    idsList = index.intersects(tmp_geom.boundingBox())
    occurrences = 0
    for id in idsList:
        test_feat = all_feats[id]
        test_geom = test_feat.geometry()
        if tmp_geom.equals(test_geom):
            occurrences += 1
    if occurrences & 0x1:
        num = [tmp_geom]
    else:
        num = []

    rapp = occurrences/2
    i=2
    new_angle = angle

    while i <= occurrences:
        draw=wires(polyline, new_angle, percentage)
        i += 2
        new_angle -= new_angle/rapp
    first = True
    for h in num:
        if first:
            geom = QgsGeometry(h)
            first = False
        else:
            geom = geom.combine(h)
    return geom

Una volta fatto questo, fai clic sul Loadpulsante e sarai in grado di vedere la funzione dal Custommenu della Expressionfinestra di dialogo.

Ora, digita questa espressione (vedi l'immagine sotto come riferimento):

draw_wires(40, 0.3, $currentfeature, @layer_name)

inserisci qui la descrizione dell'immagine

Hai appena eseguito una funzione che sta dicendo, in modo immaginario:

"Per il layer corrente ( @layer_name ) e la funzione corrente ( $ currentfeature ), mostra i fili insieme usando un'apertura massima iniziale di 40 gradi e con un cambio di direzione a una distanza di 0,3 volte la lunghezza del segmento corrente."

L'unica cosa che devi cambiare è il valore dei primi due parametri come vuoi, ma ovviamente in modo ragionevole (lascia gli altri parametri di funzione come previsto).

Infine, fai clic sul Applypulsante per applicare le modifiche.

Vedrai qualcosa del genere:

inserisci qui la descrizione dell'immagine

come previsto.


MODIFICARE

Secondo una specifica richiesta sollevata dal PO in un commento:

"Sarebbe possibile creare questo modello solo tra l'inizio e la fine di ogni polilinea anziché tra ciascun vertice?"

Ho leggermente modificato il codice. La seguente funzione dovrebbe restituire il risultato atteso:

from qgis.core import *
from qgis.gui import *
from math import sin, cos, radians

@qgsfunction(args='auto', group='Custom')
def draw_wires(angle, percentage, curr_feat, layer_name, feature, parent):

    def wires(polyline, new_angle, percentage):
        vertices = []
        len_feat = polyline.length()
        frac_len = percentage * len_feat
        limb = frac_len/cos(radians(new_angle))
        tmp_azim = first_point.azimuth(second_point)
        angle_1 = radians(90 - (tmp_azim+new_angle))
        dist_x, dist_y = (limb * cos(angle_1), limb * sin(angle_1))
        point_1 = QgsPoint(first_point[0] + dist_x, first_point[1] + dist_y)
        angle_2 = radians(90 - (tmp_azim-new_angle))
        dist_x, dist_y = (limb * cos(angle_2), limb * sin(angle_2))
        point_2 = QgsPoint(second_point[0] - dist_x, second_point[1] - dist_y)
        tmp_azim = second_point.azimuth(first_point)
        angle_3 = radians(90 - (tmp_azim+new_angle))
        dist_x, dist_y = (limb * cos(angle_3), limb * sin(angle_3))
        point_3 = QgsPoint(second_point[0] + dist_x, second_point[1] + dist_y)
        angle_4 = radians(90 - (tmp_azim-new_angle))
        dist_x, dist_y = (limb * cos(angle_4), limb * sin(angle_4))
        point_4 = QgsPoint(first_point[0] - dist_x, first_point[1] - dist_y)
        vertices.extend([first_point, point_1, point_2, second_point, point_3, point_4, first_point])
        tempGeom = QgsGeometry.fromPolyline(vertices)
        num.append(tempGeom)

    layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0]

    all_feats = {}
    index = QgsSpatialIndex()
    for ft in layer.getFeatures():
        index.insertFeature(ft)
        all_feats[ft.id()] = ft
    first = True
    tmp_geom = curr_feat.geometry()
    coords = tmp_geom.asMultiPolyline()
    if coords:
        new_coords = [QgsPoint(x, y) for x, y in z for z in coords]
    else:
        coords = tmp_geom.asPolyline()
        new_coords = [QgsPoint(x, y) for x, y in coords]
    first_point = new_coords[0]
    second_point = new_coords[-1]
    polyline=QgsGeometry.fromPolyline([first_point, second_point])
    idsList = index.intersects(tmp_geom.boundingBox())
    occurrences = 0
    for id in idsList:
        test_feat = all_feats[id]
        test_geom = test_feat.geometry()
        if tmp_geom.equals(test_geom):
            occurrences += 1
    if occurrences & 0x1:
        num = [polyline]
    else:
        num = []

    rapp = occurrences/2
    i=2
    new_angle = angle

    while i <= occurrences:
        draw=wires(polyline, new_angle, percentage)
        i += 2
        new_angle -= new_angle/rapp
    first = True
    for h in num:
        if first:
            geom = QgsGeometry(h)
            first = False
        else:
            geom = geom.combine(h)
    return geom

Wow! Questa è una risposta impressionante! Grazie mille per aver dedicato del tempo a trovarlo e condividerlo. Tuttavia: 1. Ho problemi ad applicarlo ai miei dati (quando applico la funzione, le linee scompaiono), ma immagino che il problema provenga dai miei dati poiché funziona su un livello temporaneo e 2. sarebbe possibile creare questo modello solo tra l'inizio e la fine di ogni polilinea anziché tra ogni vertice?
GuiOm Clair,

@GuiOmClair le linee scompaiono perché qualcosa non va nella funzione. Il problema non deriva dall'utilizzo del layer temporaneo, ma potrebbe essere correlato all'utilizzo delle geometrie MultiLine anziché delle geometrie Line. Caricare il layer in QGIS e quindi digitare queste due righe in Python Console: layer=iface.activeLayer()e quindi print layer.wkbType(). Clic Run: qual è il valore del numero stampato?
mgri,

Il numero è 5 (cosa significa?)
GuiOm Clair,

@GuiOmClair Significa che il tuo livello è un livello MultiLineString, mentre supponevo che fosse un livello LineString (dal momento che non lo hai specificato). Questo non sarebbe un problema e modificherò correttamente il codice il prima possibile (forse domani). Inoltre, dovrei essere in grado di eseguire il rendering dei fili solo tra il primo e l'ultimo punto di ciascuna funzione (multi) linea.
mgri,

1
Sì, le caratteristiche sono linee rette (poiché sono generalmente più facili da gestire ed esportare), quindi sarebbe meglio considerare la lunghezza reale dei fili.
GuiOm Clair,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.