Buat busur garis dari garis dan nilai

9

Saya mencoba membuat ulang plot Asal-Tujuan seperti ini:

masukkan deskripsi gambar di sini

Saya telah berhasil memasukkan data ke dalam tabel MSOA ke LAD dan dapat menggambar peta seperti ini untuk salah satu MSOA asal.

masukkan deskripsi gambar di sini

Yang pernah Anda izinkan untuk jarak (sekarang konyol) orang-orang di Peak District pergi bekerja dekat.

Tapi saya cukup suka efek yang penulis capai dengan "melebarkan" garis. Jelas, dengan aliran 522 dan 371, saya tidak bisa menggunakan satu baris per komuter tetapi akan lebih baik untuk menghasilkan garis garis proporsional untuk menunjukkan jumlah orang yang melakukan perjalanan.

Saya berpikir bahwa saya akan dapat menggunakan Generator Geometri tetapi tanpa konstruksi lingkaran, saya tidak bisa membuat kemajuan.

Ian Turton
sumber
Alat ESRI ini mungkin menarik bagi Anda, atau setidaknya papan pijakan untuk gagasan kode tentang membuat "irisan" garis.
Hornbydd
Dan ketika sebuah garis melambangkan, katakanlah 50 (100, 200) penumpang, per baris? Dengan beberapa kode python (atau generator geometri, saya tidak yakin tentang) Anda dapat memutar garis (x / 50) dengan jumlah yang berbeda.
Stefan

Jawaban:

5

Tantangan besar!

Jawaban ini terutama menggunakan generator Geometri dan ditulis dalam QGIS 3.2. QGIS mogok (tanpa saya menyimpan!) Tepat setelah saya pertama kali membangun garis dan saya hampir menyerah, tetapi daftar ekspresi yang baru digunakan menyelamatkan hari - bonus lain untuk menggunakan generator Geometri

Saya mulai dengan dua set poin, satu sumber dan tiga tujuan. Tujuan diberi label dengan jumlah:

Poin awal

Saya kemudian membuat garis yang menghubungkan titik sumber ke semua tujuan menggunakan lapisan virtual menggunakan kode berikut:

SELECT d.Count_MF, Makeline( s.geometry, d.geometry) 'geometry' 
  FROM Source AS s JOIN Destinations AS d

Poin yang terhubung

Lalu saya menggunakan ekspresi generator Geometri berikut untuk memberi style pada baris:

 intersection(
   geom_from_wkt( 
     'MULTILINESTRING ((' ||  $x_at( 0)  || ' ' || $y_at( 0)  || ', ' || 
     array_to_string(
       array_remove_at( string_to_array( regexp_replace(
             geom_to_wkt(nodes_to_points( tapered_buffer(  $geometry ,0, "Count_MF" * 200, floor("Count_MF" / 10)),true)),
             '[\\(\\)]','')),0)
     , ') , ('  ||  $x_at( 0)  || ' ' || $y_at( 0)  || ', ' )
    || '))')
    ,buffer( point_n(  $geometry ,1), $length))

Ini mengambil setiap baris dan menerapkan langkah-langkah berikut:

  1. Menghasilkan penyangga tirus dari lebar nol di sumber ke lebar yang diukur dengan jumlah tujuan di ujung tujuan. Kepadatan titik buffer juga diskalakan oleh atribut jumlah tujuan.
  2. Vertikal penyangga poligon dikonversi ke titik (ini mungkin berlebihan), dan kemudian diekspor ke WKT, dan tanda kurung dihapus menggunakan regex, sebelum dikonversi menjadi array
  3. Array kemudian diperluas kembali ke string WKT untuk multilinestring, menyisipkan dalam koordinat titik sumber ditambah pemformatan yang relevan - ini menciptakan garis terpisah untuk setiap simpul yang diekstraksi yang terhubung ke titik sumber
  4. WKT dikonversi kembali ke objek geometri dan akhirnya berpotongan dengan buffer dari titik sumber untuk klip mereka kembali ke lingkaran di mana titik tujuan duduk (lihat output dari tapered_bufferuntuk memahami mengapa ini diperlukan)

Penggemar

Dalam menulis langkah-langkahnya, saya menyadari bahwa konversi ke dan dari array tidak diperlukan, dan semua manipulasi WKT dapat dilakukan dengan regex. Ungkapan ini di bawah, dan jika tapered_arrayfungsi dapat diganti dengan yang lain maka ini juga dapat digunakan dalam QGIS 2.18.

intersection(
   geom_from_wkt(
    'MULTILINESTRING ((' ||  $x_at( 0)  || ' ' || $y_at( 0)  || ', ' ||
  replace(
    regexp_replace(
      regexp_replace(
        geom_to_wkt(tapered_buffer(  $geometry ,0, "Count_MF" * 200, floor("Count_MF" / 10))),
      '^[^,]*,',''),
    ',[^,]*$',''),
  ',',') , ('  ||  $x_at( 0)  || ' ' || $y_at( 0)  || ', ')
  || '))')
,buffer( point_n(  $geometry ,1), $length))
Andy Harfoot
sumber
6

Pertanyaan Anda membuat saya penasaran.

Solusi ini hanya berfungsi untuk QGIS 2.x di Python Console

Seperti disebutkan dalam komentar saya di sini adalah ide saya untuk membuat garis lengkung dengan Python.

Saya memiliki dua lapisan titik:

saya. Satu memegang modal (id, modal)

ii. Satu memegang kota (id, kota, komuter)

Jumlah komuter "dipisahkan menjadi uang kertas" dan ini akan menjadi garis yang membangun busur. Jadi 371 komuter adalah kombinasi 3x100, 1x50, 2x10 dan 1x1 dan total 7 uang kertas. Setelah itu, garis ditata dengan gaya berdasarkan aturan.

Ini kodenya:

from qgis.gui import *
from qgis.utils import *
from qgis.core import *
from PyQt4 import QtGui, uic
from PyQt4.QtGui import *
from PyQt4.QtCore import *

for lyr in QgsMapLayerRegistry.instance().mapLayers().values():
    if lyr.name() == "capital":
        capital_layer = lyr

for lyr in QgsMapLayerRegistry.instance().mapLayers().values():
    if lyr.name() == "town":
        town_layer = lyr

    # creating the memory layer
d_lyr = QgsVectorLayer('LineString', 'distance', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(d_lyr)
prov = d_lyr.dataProvider()
prov.addAttributes( [ QgsField("id", QVariant.Int), QgsField("banknote",QVariant.Int)])

    # function to create the banknotes
def banknoteOutput(number):
    number_list = []
    number_list.append(number)
    banknote_count = []
    temp_list = []
    banknote_list = []
    for n in number_list:
        total_sum = 0
        total = int(n/100)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 100])
        n = n-(total*100)
        total = int(n/50)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 50])
        n = n-(total*50)
        total = int(n/10)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 10])
        n = n-(total*10)
        total = int(n/5)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 5])
        n = n-(total*5)
        total = int(n/1)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 1])
        for i in banknote_count:
            temp_list.append(i*i[0])
        banknote_list = [item for sublist in temp_list for item in sublist][1::2]
        return banknote_list

        # creating lines with the amount of banknotes
for capital in capital_layer.getFeatures():
    for town in town_layer.getFeatures():
        commuter_splitting = banknoteOutput(town['commuters'])
        for i,banknote in enumerate(commuter_splitting):
            angle = 2
            distance = QgsDistanceArea()
            distance.measureLine(capital.geometry().asPoint(), town.geometry().asPoint())
            vect = QgsFeature()
            vect.setGeometry(QgsGeometry.fromPolyline([capital.geometry().asPoint(), town.geometry().asPoint()]))
            vect.geometry().rotate(0+(i*angle), capital.geometry().asPoint())
            vect.setAttributes([int(town["id"]), int(banknote)])
            prov.addFeatures([vect])

d_lyr.updateExtents()
d_lyr.triggerRepaint()
d_lyr.updateFields()

Hasilnya bisa seperti ini:

masukkan deskripsi gambar di sini

UPDATE: perbedaan pria / wanita

Hasil dalam 4 Lapisan Memori.

from qgis.gui import *
from qgis.utils import *
from qgis.core import *
from PyQt4 import QtGui, uic
from PyQt4.QtGui import *
from PyQt4.QtCore import *

for lyr in QgsMapLayerRegistry.instance().mapLayers().values():
    if lyr.name() == "capital":
        capital_layer = lyr

for lyr in QgsMapLayerRegistry.instance().mapLayers().values():
    if lyr.name() == "town":
        town_layer = lyr

    # function to create the banknotes
def banknoteOutput(number):
    number_list = []
    number_list.append(number)
    banknote_count = []
    temp_list = []
    banknote_list = []
    for n in number_list:
        total_sum = 0
        total = int(n/100)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 100])
        n = n-(total*100)
        total = int(n/50)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 50])
        n = n-(total*50)
        total = int(n/10)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 10])
        n = n-(total*10)
        total = int(n/5)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 5])
        n = n-(total*5)
        total = int(n/1)
        total_sum = total_sum + total
        if total > 0:
            banknote_count.append([total, 1])
        for i in banknote_count:
            temp_list.append(i*i[0])
        banknote_list = [item for sublist in temp_list for item in sublist][1::2]
        return banknote_list

    # creating the male memory layer
cmt_male = QgsVectorLayer('LineString', 'Commuters_Male', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(cmt_male)
prov_male = cmt_male.dataProvider()
prov_male.addAttributes( [ QgsField("id", QVariant.Int), QgsField("banknote",QVariant.Int)])

    # creating the male polygon memory layer
cmt_male_polygon = QgsVectorLayer('Polygon', 'Commuters_Male_Poly', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(cmt_male_polygon)
prov_cmt_male_polygon = cmt_male_polygon.dataProvider()
prov_cmt_male_polygon.addAttributes( [ QgsField("id", QVariant.Int), QgsField("banknote",QVariant.Int)])

    # creating lines with the amount of banknotes
for capital in capital_layer.getFeatures():
    for town in town_layer.getFeatures():
        commuter_splitting = banknoteOutput(town['cmt_male'])
        points = []
        for i,banknote in enumerate(reversed(commuter_splitting)):
            angle = 2
            distance = QgsDistanceArea()
            distance.measureLine(capital.geometry().asPoint(), town.geometry().asPoint())
            vect = QgsFeature()
            vect.setGeometry(QgsGeometry.fromPolyline([capital.geometry().asPoint(), town.geometry().asPoint()]))
            vect.geometry().rotate(0+(i*angle), capital.geometry().asPoint())
            vect.setAttributes([int(town["id"]), int(banknote)])
            points.append(vect.geometry().asPolyline()[1])
            prov_male.addFeatures([vect])
        polygon = QgsFeature()
        points.insert(0,capital.geometry().asPoint())
        points.insert(len(points),capital.geometry().asPoint())
        polygon.setGeometry(QgsGeometry.fromPolygon([points]))
        polygon.setAttributes([1, 2])
        prov_cmt_male_polygon.addFeatures([polygon])

cmt_male.updateExtents()
cmt_male.triggerRepaint()
cmt_male.updateFields()
cmt_male_polygon.updateExtents()
cmt_male_polygon.triggerRepaint()
cmt_male_polygon.updateFields()

    # creating the female memory layer
cmt_female = QgsVectorLayer('LineString', 'Commuters_Female', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(cmt_female)
prov_female = cmt_female.dataProvider()
prov_female.addAttributes( [ QgsField("id", QVariant.Int), QgsField("banknote",QVariant.Int)])

    # creating the female polygon memory layer
cmt_female_polygon = QgsVectorLayer('Polygon', 'Commuters_Female_Poly', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(cmt_female_polygon)
prov_cmt_female_polygon = cmt_female_polygon.dataProvider()
prov_cmt_female_polygon.addAttributes( [ QgsField("id", QVariant.Int), QgsField("banknote",QVariant.Int)])

    # creating lines with the amount of banknotes
for capital in capital_layer.getFeatures():
    for town in town_layer.getFeatures():
        commuter_splitting = banknoteOutput(town['cmt_female'])
        points = []
        for i,banknote in enumerate(commuter_splitting):
            angle = 2
            distance = QgsDistanceArea()
            distance.measureLine(capital.geometry().asPoint(), town.geometry().asPoint())
            vect = QgsFeature()
            vect.setGeometry(QgsGeometry.fromPolyline([capital.geometry().asPoint(), town.geometry().asPoint()]))
            vect.geometry().rotate(-angle-(i*angle), capital.geometry().asPoint())
            vect.setAttributes([int(town["id"]), int(banknote)])
            points.append(vect.geometry().asPolyline()[1])
            prov_female.addFeatures([vect])
        polygon = QgsFeature()
        points.insert(0,capital.geometry().asPoint())
        points.insert(len(points),capital.geometry().asPoint())
        polygon.setGeometry(QgsGeometry.fromPolygon([points]))
        polygon.setAttributes([1, 2])
        prov_cmt_female_polygon.addFeatures([polygon])

cmt_female.updateExtents()
cmt_female.triggerRepaint()
cmt_female.updateFields()
cmt_female_polygon.updateExtents()
cmt_female_polygon.triggerRepaint()
cmt_female_polygon.updateFields()

Hasilnya bisa seperti ini:masukkan deskripsi gambar di sini

Satu hal yang tidak ideal dari sudut pandang kartografi:

Ukuran busur garis dapat mengganggu pada pandangan pertama, dalam cara busur yang lebih besar dapat mewakili lebih banyak penumpang. Busur bisa lebih besar dengan lebih sedikit komuter (289 komuter / 11 uang kertas) daripada yang lain dengan lebih banyak komuter (311 komuter / 5 uang kertas).

Stefan
sumber