Menggeser tampilan garis yang tumpang tindih di QGIS?

10

Ketika poin tumpang tindih, ada properti ini yang memungkinkan untuk secara otomatis menampilkan banyak dari mereka secara terpisah di sekitar tempat mereka berada, yang disebut 'Point displacement'. Tapi itu tidak bekerja untuk garis, meskipun begitu menurut saya cukup layak secara konseptual untuk mencapai sesuatu seperti itu:

masukkan deskripsi gambar di sini

Saya benar-benar perlu melihat garis differents yang pada kenyataannya semua berada di tempat yang sama (saya bekerja di jaringan telekomunikasi). Satu-satunya cara saya melihat untuk saat ini adalah benar-benar membuat garis yang berbeda seperti pada gambar di atas, sehingga menciptakan kesalahan spasial.

Saya menggunakan QGIS 2.14.

GuiOm Clair
sumber
Saya pikir sesuatu bisa dilakukan berulang-ulang untuk styling. Apakah garis di tengah adalah garis awal? Lalu, saya melihat bahwa Anda membuat masing-masing dari baris lain dengan menggunakan tiga geometri yang berbeda, jadi pertanyaan saya adalah apakah ada beberapa aturan tambahan khusus untuk menerjemahkannya?
mgri
@ Magri Saya tidak yakin untuk memahami pertanyaan Anda. Gambar yang diberikan adalah contoh di mana saya menggambar lima garis yang berbeda untuk demonstrasi. Pada kenyataannya akan lebih bahwa 5 baris ini sebenarnya memang di tempat yang tengah (mereka adalah kabel, jadi semuanya terjebak dalam sarung yang sama).
GuiOm Clair
1
Anda dapat membuat garis dengan perpindahan ("offset") juga, tetapi mereka tidak akan bertemu di titik awal dan akhir.
AndreJ
@ AndreJ Ya, dan masalah lain adalah bahwa itu akan menjadi operasi yang cukup manual di mana saya akan membutuhkan sesuatu yang lebih otomatis karena akan digunakan oleh banyak pengguna.
GuiOm Clair
1
@GuiOmClair Mengikuti gambar terlampir, saya berasumsi bahwa Anda mulai dari satu baris yang tumpang tindih (misalnya) empat baris lainnya dan Anda perlu menemukan cara untuk menampilkannya secara terpisah, bahkan jika tumpang tindih. Saya hanya mengatakan bahwa itu mungkin untuk mereproduksi apa yang ditampilkan pada gambar terlampir tanpa perlu membuat geometri baru (tetapi hanya berulang pada properti style dari layer awal). Cara lain akan menjadi yang diusulkan oleh AndreJ, tetapi tampaknya itu tidak sesuai dengan kebutuhan Anda.
mgri

Jawaban:

12

Saya mengusulkan pendekatan yang hanya berulang ke generator geometri dan fungsi kustom.

Sebelum memulai, saya ingin menggarisbawahi bahwa saya akan memusatkan perhatian pada penjelasan tentang hal-hal minimal yang harus dilakukan untuk mereproduksi hasil yang diinginkan: ini berarti bahwa beberapa parameter kecil lainnya (seperti ukuran, lebar dan sebagainya) harus mudah disesuaikan oleh Anda untuk menyesuaikan kebutuhan Anda dengan lebih baik.

Oleh karena itu, solusi ini berfungsi baik untuk Sistem Referensi Geografis dan Proyeksi: sebagai berikut, saya berasumsi menggunakan CRS yang diproyeksikan (yaitu satuan pengukuran adalah meter), tetapi Anda dapat mengubahnya sesuai dengan CRS Anda.


Konteks

Mari kita asumsikan mulai dari layer vektor linestring ini yang mewakili kabel (label mewakili jumlah kabel yang tumpang tindih (bertepatan)):

masukkan deskripsi gambar di sini


Larutan

Pertama, pergi ke Layer Properties | Styledan kemudian pilih Single symbolpenyaji.

Dari Symbol selectordialog, pilih Geometry generatortipe layer simbol dan Linestring / MultiLinestringtipe geometri. Kemudian, klik pada Function Editortab:

masukkan deskripsi gambar di sini

Kemudian, klik New filedan ketik draw_wiressebagai nama fungsi baru:

masukkan deskripsi gambar di sini

Anda akan melihat bahwa fungsi baru telah dibuat dan terdaftar di sisi kiri dialog. Sekarang, klik pada nama fungsi dan ganti default @qgsfunctiondengan kode berikut (jangan lupa untuk menambahkan semua perpustakaan yang dilampirkan di sini):

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

Setelah Anda selesai melakukannya, klik Loadtombol dan Anda akan dapat melihat fungsi dari CustomMenu Expressiondialog.

Sekarang, ketikkan ungkapan ini (lihat gambar di bawah ini sebagai referensi):

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

masukkan deskripsi gambar di sini

Anda baru saja menjalankan fungsi yang mengatakan, dengan cara imajiner:

"Untuk layer saat ini ( @layer_name ) dan fitur saat ini ( $ currentfeature ), tampilkan kabel bersama-sama menggunakan pembukaan maksimum awal 40 derajat dan dengan perubahan arah pada jarak 0,3 kali panjang segmen saat ini."

Satu-satunya hal yang perlu Anda ubah adalah nilai dari dua parameter pertama seperti yang Anda inginkan, tetapi jelas dengan cara yang masuk akal (biarkan parameter fungsi lainnya sebagaimana disediakan).

Terakhir, klik Applytombol untuk menerapkan perubahan.

Anda akan melihat sesuatu seperti ini:

masukkan deskripsi gambar di sini

seperti yang diharapkan.


EDIT

Menurut permintaan spesifik yang diajukan oleh OP dalam komentar:

"Apakah mungkin untuk membuat pola ini hanya antara awal dan akhir setiap polyline alih-alih antara setiap vertex?"

Saya sedikit mengedit kode. Fungsi berikut harus mengembalikan hasil yang diharapkan:

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
sumber
Wow! Itu jawaban yang mengesankan! Terima kasih banyak telah meluangkan waktu ini untuk menemukan dan membagikannya. Namun: 1. Saya mengalami kesulitan menerapkannya ke data saya (ketika saya menerapkan fungsi, garis-garisnya hilang), tapi saya kira masalahnya berasal dari data saya karena ia bekerja pada lapisan sementara dan 2. apakah mungkin untuk membuat pola ini hanya antara awal dan akhir setiap polyline, bukan antara setiap vertice?
GuiOm Clair
@GuiOmClair garisnya hilang karena ada yang salah dengan fungsi. Masalahnya bukan berasal dari penggunaan lapisan sementara, tetapi bisa terkait dengan penggunaan geometri MultiLine bukan geometri Garis. Tolong, muat layer di QGIS dan kemudian ketik dua baris ini di Python Console: layer=iface.activeLayer()dan kemudian print layer.wkbType(). Klik Run: yang merupakan nilai dari nomor yang dicetak?
mgri
Jumlahnya 5 (apa artinya?)
GuiOm Clair
@ GuiOmClair Ini berarti bahwa layer Anda adalah layer MultiLineString, sementara saya berasumsi itu adalah layer LineString (karena Anda tidak menentukannya). Ini tidak akan menjadi masalah dan saya akan mengedit kode dengan benar sesegera mungkin (mungkin besok). Selain itu, saya harus dapat membuat kabel hanya antara titik pertama dan terakhir dari setiap fitur baris (multi).
mgri
1
Ya, fitur-fiturnya adalah garis lurus (karena umumnya lebih mudah dikelola dan diekspor), jadi akan lebih baik mengingat panjang kabel yang sebenarnya.
GuiOm Clair