Desplazamiento de visualización de líneas superpuestas en QGIS?

10

Cuando los puntos se superponen, existe esta propiedad que permite mostrar automáticamente el lote de ellos por separado alrededor de donde están, llamado 'desplazamiento de punto'. Pero no funciona para líneas, aun así me parece bastante factible conceptualmente para lograr algo así:

ingrese la descripción de la imagen aquí

Necesito absolutamente ver las diferentes líneas que en realidad están todas en el mismo lugar (estoy trabajando en redes de telecomunicaciones). La única forma en que veo por ahora es crear realmente diferentes líneas como en la imagen de arriba, creando así errores espaciales.

Estoy usando QGIS 2.14.

GuiOm Clair
fuente
Creo que se podría hacer algo recurriendo al estilo. ¿Es la línea del medio la línea de partida? Entonces, veo que creaste cada una de las otras líneas usando tres geometrías diferentes, así que mi pregunta es si hay algunas reglas adicionales específicas para representarlas.
mgri
@mgri No estoy seguro de entender tu pregunta. La imagen proporcionada es un ejemplo en el que dibujé cinco líneas diferentes en aras de la demostración. En realidad, sería más que estas 5 líneas de hecho están en el lugar de la del medio (son cables, por lo que todos están atrapados en la misma vaina).
GuiOm Clair
1
También puede procesar líneas con un desplazamiento ("desplazamiento"), pero no se encontrarían en los puntos inicial y final.
AndreJ
@AndreJ Sí, y otro problema sería que sería una operación bastante manual donde necesitaría algo más automático, ya que sería utilizado por muchos usuarios.
GuiOm Clair
1
@GuiOmClair Siguiendo la imagen adjunta, asumí que comienza desde una línea que se superpone (por ejemplo) a otras cuatro líneas y que necesita encontrar una manera de mostrarlas por separado, incluso si se superponen. Acabo de decir que podría ser posible reproducir lo que se muestra en la imagen adjunta sin la necesidad de crear nuevas geometrías (pero solo recurriendo a las propiedades de estilo de la capa inicial). Otra forma sería la propuesta por AndreJ, pero parece que no se ajusta a sus necesidades.
mgri

Respuestas:

12

Propongo un enfoque que solo recurre a un generador de geometría y una función personalizada.

Antes de comenzar, quiero subrayar que enfocaré la atención en la explicación de las cosas mínimas para reproducir el resultado deseado: esto significa que usted debe ajustar fácilmente otros parámetros menores (como tamaños, anchos, etc.) para satisfacer mejor sus necesidades.

Por lo tanto, esta solución funciona tanto para los sistemas de referencia geográficos como proyectados: en lo siguiente, supuse que usaría un CRS proyectado (es decir, las unidades de medida son metros), pero puede cambiarlos de acuerdo con su CRS.


Contexto

Asumamos comenzar desde esta capa de vector de cadena lineal que representa los cables (las etiquetas representan el número de cables superpuestos (coincidentes)):

ingrese la descripción de la imagen aquí


Solución

En primer lugar, vaya a Layer Properties | Styley luego elija el Single symbolrenderizador.

En el Symbol selectorcuadro de diálogo, elija un Geometry generatortipo de capa de símbolo y un Linestring / MultiLinestringtipo de geometría. Luego, haga clic en la Function Editorpestaña:

ingrese la descripción de la imagen aquí

Luego, haga clic en New filey escriba draw_wirescomo el nombre de la nueva función:

ingrese la descripción de la imagen aquí

Verá que se ha creado una nueva función y está listada en el lado izquierdo del cuadro de diálogo. Ahora, haga clic en el nombre de la función y reemplace el predeterminado @qgsfunctioncon el siguiente código (no olvide agregar todas las bibliotecas adjuntas aquí):

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 vez que haya hecho esto, haga clic en el Loadbotón y podrá ver la función desde el Custommenú del Expressioncuadro de diálogo.

Ahora, escriba esta expresión (vea la imagen a continuación como referencia):

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

ingrese la descripción de la imagen aquí

Acaba de ejecutar una función que dice, de forma imaginaria:

"Para la capa actual ( @layer_name ) y la característica actual ( $ currentfeature ), muestre los cables juntos usando una apertura máxima inicial de 40 grados y con un cambio de dirección a una distancia de 0.3 veces la longitud del segmento actual".

Lo único que necesita cambiar es el valor de los dos primeros parámetros que desee, pero obviamente de manera razonable (deje los otros parámetros de función como se proporcionan).

Finalmente, haga clic en el Applybotón para aplicar los cambios.

Verás algo como esto:

ingrese la descripción de la imagen aquí

como se esperaba.


EDITAR

Según una solicitud específica planteada por el OP en un comentario:

"¿Sería posible crear este patrón solo entre el principio y el final de cada polilínea en lugar de entre cada vértice?"

Edité ligeramente el código. La siguiente función debería devolver el resultado esperado:

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
mgri
fuente
¡Guauu! ¡Esa es una respuesta impresionante! Muchas gracias por tomarse este tiempo para encontrarlo y compartirlo. Sin embargo: 1. Tengo problemas para aplicarlo a mis datos (cuando aplico la función, las líneas desaparecen), pero supongo que el problema proviene de mis datos ya que funciona en una capa temporal y 2. sería posible crear este patrón solo entre el principio y el final de cada polilínea en lugar de entre cada vértice?
GuiOm Clair
@GuiOmClair las líneas desaparecen porque algo sale mal con la función. El problema no proviene del uso de la capa temporal, pero podría estar relacionado con el uso de geometrías multilínea en lugar de geometrías de línea. Por favor, cargue la capa en QGIS y luego escriba estas dos líneas en la Consola Python: layer=iface.activeLayer()y luego print layer.wkbType(). Haga clic Run: ¿cuál es el valor del número impreso?
mgri
El número es 5 (¿qué significa?)
GuiOm Clair
@GuiOmClair Significa que su capa es una capa MultiLineString, mientras que supuse que era una capa LineString (ya que no la especificó). Esto no sería un problema y editaré el código correctamente tan pronto como pueda (tal vez mañana). Además, debería poder renderizar los cables solo entre el primer y el último punto de cada característica de (múltiples) líneas.
mgri
1
Sí, las características son líneas rectas (ya que generalmente son más fáciles de administrar y exportar), por lo que sería mejor teniendo en cuenta la longitud real de los cables.
GuiOm Clair