Tạo một vòng cung của dòng từ một dòng và một giá trị


9

Tôi đang cố gắng tạo lại một cốt truyện Origin-Destination như thế này:

nhập mô tả hình ảnh ở đây

Tôi đã quản lý để chuyển dữ liệu vào bảng MSOA sang LAD và có thể vẽ bản đồ như thế này cho một trong những MSOA gốc.

nhập mô tả hình ảnh ở đây

Mà một khi bạn cho phép khoảng cách (bây giờ thật lố bịch) mọi người trong Khu vực đi làm ở Quận Đỉnh đi làm là gần.

Nhưng tôi khá thích hiệu ứng mà tác giả đã đạt được bằng cách "chơi" các dòng. Rõ ràng, với các dòng 522 và 371, tôi không thể đi một dòng trên mỗi lần đi lại, nhưng thật tuyệt khi tạo ra một vòng cung tỷ lệ để hiển thị số người thực hiện chuyến đi.

Tôi nghĩ rằng tôi sẽ có thể sử dụng Trình tạo hình học nhưng không có cấu trúc vòng lặp, tôi dường như không thể thực hiện được.


Công cụ ESRI này có thể được bạn quan tâm hoặc ít nhất là một bảng mùa xuân cho các ý tưởng mã về việc tạo ra một "nêm" các dòng.
Hornbydd

Và khi một dòng tượng trưng, ​​giả sử 50 (100, 200) hành khách, mỗi dòng? Với một số mã python (hoặc trình tạo hình học, tôi không chắc chắn về điều đó), bạn có thể xoay các dòng (x / 50) với một số lượng riêng biệt.
Stefan

Câu trả lời:


5

Một thử thách lớn!

Câu trả lời này chủ yếu sử dụng trình tạo Hình học và được viết trong QGIS 3.2. QGIS đã bị sập (không có tôi đã lưu!) Ngay sau khi tôi xây dựng các dòng đầu tiên và tôi gần như đã bỏ cuộc, nhưng danh sách biểu thức được sử dụng gần đây đã lưu lại ngày - một phần thưởng khác cho việc sử dụng trình tạo Hình học

Tôi bắt đầu với hai bộ điểm, một nguồn và ba điểm đến. Các điểm đến được dán nhãn với số lượng:

Điểm ban đầu

Sau đó, tôi đã tạo các đường kết nối điểm nguồn với tất cả các đích bằng một lớp ảo bằng mã sau:

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

Điểm kết nối

Sau đó, tôi đã sử dụng biểu thức trình tạo Hình học sau đây để định kiểu các dòng:

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

Điều này có từng dòng và áp dụng các bước sau:

  1. Tạo một bộ đệm giảm dần đi từ độ rộng 0 tại nguồn thành chiều rộng được chia tỷ lệ theo số đích ở cuối đích. Mật độ điểm đệm cũng được chia tỷ lệ theo thuộc tính đếm đích.
  2. Các đỉnh của đa giác bộ đệm được chuyển đổi thành các điểm (điều này có thể là thừa), sau đó được xuất sang WKT và dấu ngoặc được loại bỏ bằng regex, trước khi chuyển đổi thành một mảng
  3. Sau đó, mảng được mở rộng trở lại thành chuỗi WKT cho đa tuyến, chèn vào tọa độ của điểm nguồn cộng với định dạng có liên quan - điều này tạo ra một dòng riêng cho mỗi đỉnh được trích xuất được kết nối với điểm nguồn
  4. WKT được chuyển đổi trở lại thành một đối tượng hình học và cuối cùng được giao với bộ đệm của điểm nguồn để đưa chúng trở lại vòng tròn mà điểm đích nằm trên (xem đầu ra của a tapered_bufferđể hiểu tại sao điều này là cần thiết)

Người hâm mộ

Khi viết lên các bước, tôi nhận ra rằng việc chuyển đổi sang và từ một mảng là không cần thiết và tất cả các thao tác WKT có thể được thực hiện với các biểu thức chính quy. Biểu thức này ở bên dưới và nếu tapered_arraychức năng có thể được thay thế bằng một biểu thức khác thì điều này cũng có thể được sử dụng trong 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

Câu hỏi của bạn làm tôi tò mò.

Giải pháp này chỉ hoạt động cho QGIS 2.x trong Bảng điều khiển Python

Giống như được đề cập trong bình luận của tôi ở đây là ý tưởng của tôi để tạo ra vòng cung của dòng với Python.

Tôi có hai lớp điểm:

Tôi. Một người nắm giữ vốn (id, capital)

ii. Một người giữ thị trấn (id, thị trấn, đi lại)

Số lượng hành khách được "tách thành tiền giấy" và đây sẽ là những dòng tạo nên vòng cung. Vì vậy, 371 hành khách là sự kết hợp của 3x100, 1x50, 2x10 và 1x1 và trong tổng số 7 tiền giấy. Sau đó, các dòng được tạo kiểu theo kiểu dựa trên quy tắc.

Đây là mã:

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

Kết quả có thể như thế này:

nhập mô tả hình ảnh ở đây

CẬP NHẬT: phân biệt nam / nữ

Kết quả trong 4 lớp bộ nhớ.

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

Kết quả có thể như thế này:nhập mô tả hình ảnh ở đây

Một điều không lý tưởng theo quan điểm bản đồ:

Kích thước của một vòng cung có thể gây khó chịu ngay từ cái nhìn đầu tiên, theo cách mà một vòng cung lớn hơn có thể đại diện cho nhiều hành khách hơn. Một vòng cung có thể lớn hơn với ít người đi lại hơn (289 người đi làm / 11 tờ tiền) so với người khác có nhiều người đi lại hơn (311 người đi làm / 5 tờ tiền).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.