สร้างส่วนโค้งของเส้นจากบรรทัดและค่า


9

ฉันพยายามสร้างพล็อตแหล่งกำเนิดปลายทางเช่นนี้

ป้อนคำอธิบายรูปภาพที่นี่

ฉันจัดการเพื่อบดบังข้อมูลลงในตาราง MSOA ถึง LAD และสามารถวาดแผนที่เช่นนี้สำหรับหนึ่งใน MSOA ต้นทาง

ป้อนคำอธิบายรูปภาพที่นี่

ซึ่งเมื่อคุณอนุญาตให้สำหรับ (ตอนนี้ไร้สาระ) ระยะทางผู้คนในการเดินทางในเขต Peak District ในการทำงานอยู่ใกล้

แต่ฉันค่อนข้างชอบเอฟเฟกต์ที่ผู้เขียนประสบความสำเร็จด้วยการ "แยก" ออกจากเส้น เห็นได้ชัดว่าด้วยกระแส 522 และ 371 ฉันไม่สามารถไปหนึ่งบรรทัดต่อผู้โดยสารหนึ่งคนได้ แต่มันก็ดีที่จะสร้างส่วนโค้งของเส้นสัดส่วนเพื่อแสดงจำนวนผู้คนที่เดินทาง

ฉันคิดว่าฉันจะสามารถใช้เครื่องสร้างรูปทรงเรขาคณิตได้ แต่หากไม่มีโครงสร้างวนรอบฉันไม่สามารถสร้างความก้าวหน้าได้


เครื่องมือ ESRIนี้อาจเป็นที่สนใจของคุณหรืออย่างน้อยกระดานกระโดดน้ำสำหรับแนวคิดรหัสเกี่ยวกับการสร้าง "ลิ่ม" ของบรรทัด
Hornbydd

และเมื่อเส้นเป็นสัญลักษณ์สมมติว่าผู้โดยสาร 50 (100, 200) ตัวต่อบรรทัด? ด้วยรหัสไพ ธ อนบางตัว (หรือตัวสร้างรูปทรงเรขาคณิตฉันไม่แน่ใจ) คุณสามารถหมุนเส้น (x / 50) ด้วยจำนวนที่แตกต่างกัน
สเตฟาน

คำตอบ:


5

ความท้าทายที่ยิ่งใหญ่!

คำตอบนี้ใช้ตัวสร้างเรขาคณิตเป็นหลักและเขียนด้วย QGIS 3.2 QGIS crashed (ไม่มีฉันบันทึกไว้!) หลังจากที่ฉันสร้างบรรทัดแรกและฉันเกือบยอมแพ้ แต่รายการ expression ที่ใช้ล่าสุดบันทึกวันนั้น - โบนัสอีกอย่างสำหรับการใช้เครื่องกำเนิดเรขาคณิต

ฉันเริ่มต้นด้วยสองชุดจุดหนึ่งต้นทางและสามปลายทาง จุดหมายจะถูกระบุด้วยจำนวน:

จุดเริ่มต้น

ฉันสร้างบรรทัดที่เชื่อมต่อจุดต้นทางไปยังปลายทางทั้งหมดโดยใช้เลเยอร์เสมือนโดยใช้รหัสต่อไปนี้:

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

จุดเชื่อมต่อ

จากนั้นฉันใช้นิพจน์ตัวสร้างเรขาคณิตต่อไปนี้เพื่อจัดรูปแบบเส้น:

 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))

สิ่งนี้ใช้แต่ละบรรทัดและใช้ขั้นตอนต่อไปนี้:

  1. สร้างบัฟเฟอร์เรียวจากความกว้างเป็นศูนย์ที่ต้นทางถึงความกว้างที่ปรับตามจำนวนปลายทางที่ปลายปลายทาง ความหนาแน่นของจุดบัฟเฟอร์จะถูกลดขนาดด้วยแอตทริบิวต์จำนวนปลายทาง
  2. จุดยอดของรูปหลายเหลี่ยมบัฟเฟอร์จะถูกแปลงเป็นจุด (ซึ่งอาจเป็นฟุ่มเฟือย) จากนั้นส่งออกไปยัง WKT และวงเล็บจะถูกลบออกโดยใช้ regex ก่อนที่จะแปลงเป็นอาร์เรย์
  3. จากนั้นอาร์เรย์จะขยายออกกลับไปเป็นสตริง WKT สำหรับมัลติริงก์ชั่นโดยแทรกในพิกัดของจุดต้นทางพร้อมกับการจัดรูปแบบที่เกี่ยวข้องซึ่งจะสร้างบรรทัดแยกสำหรับแต่ละจุดยอดที่แยกซึ่งเชื่อมต่อกับจุดต้นทาง
  4. WKT จะถูกแปลงกลับไปเป็นวัตถุรูปทรงเรขาคณิตและในที่สุดก็ตัดกันด้วยบัฟเฟอร์ของจุดต้นทางเพื่อตัดกลับไปเป็นวงกลมที่จุดปลายทางตั้งอยู่ (ดูผลลัพธ์ของ a tapered_bufferเพื่อทำความเข้าใจว่าทำไมจึงมีความจำเป็น)

แฟน ๆ

ในการเขียนขั้นตอนฉันรู้ว่าการแปลงไปยังและจากอาเรย์นั้นไม่จำเป็นและการจัดการ WKT ทั้งหมดสามารถทำได้ด้วย regexes นิพจน์นี้อยู่ด้านล่างและหากtapered_arrayฟังก์ชันสามารถถูกแทนที่ด้วยฟังก์ชันอื่นก็จะสามารถใช้งานได้ใน 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))

6

คำถามของคุณทำให้ฉันอยากรู้

วิธีนี้ใช้ได้กับ QGIS 2.x ใน Python Console เท่านั้น

เช่นเดียวกับที่กล่าวถึงในความคิดเห็นของฉันที่นี่เป็นความคิดของฉันในการสร้างส่วนโค้งของเส้นด้วย Python

ฉันมีเลเยอร์สองจุด:

ผม. หนึ่งถือครองเมืองหลวง (id, capital)

ii หนึ่งถือครองเมือง (id, เมือง, ผู้โดยสาร)

จำนวนผู้โดยสารที่ "แยกเป็นธนบัตร" และสิ่งเหล่านี้จะเป็นเส้นที่สร้างส่วนโค้ง ดังนั้นผู้ใช้บริการ 371 รายจึงรวมกันเป็น 3x100, 1x50, 2x10 และ 1x1 รวมเป็นธนบัตร 7 ใบ หลังจากนั้นเส้นจะถูกจัดสไตล์ด้วยการออกแบบตามกฎ

นี่คือรหัส:

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()

ผลลัพธ์อาจมีลักษณะเช่นนี้:

ป้อนคำอธิบายรูปภาพที่นี่

UPDATE: ความแตกต่างชาย / หญิง

ผลลัพธ์เป็น 4 Memory Layer

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()

ผลลัพธ์อาจมีลักษณะเช่นนี้:ป้อนคำอธิบายรูปภาพที่นี่

สิ่งหนึ่งที่ไม่เหมาะในมุมมองของการทำแผนที่:

ขนาดของส่วนโค้งของเส้นสามารถทำให้เกิดการระคายเคืองได้อย่างรวดเร็วในวิธีที่เส้นโค้งที่ใหญ่กว่าสามารถแสดงถึงจำนวนผู้โดยสารที่เพิ่มขึ้น อาร์คอาจใหญ่กว่าโดยมีผู้สัญจรน้อยกว่า (289 คน / 11 คน) กว่าคนที่มีจำนวนผู้โดยสารมากกว่า (311 คน / 5 คน)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.