Створіть дугу з рядків і значення


9

Я намагаюся відтворити сюжет "Origin-Destination" так:

введіть тут опис зображення

Мені вдалося об'єднати дані в таблицю MSOA до LAD і можу намалювати подібну карту для одного з вихідних MSOA.

введіть тут опис зображення

Що колись ви дозволяєте на (зараз смішних) відстанях людей у ​​районі Пік їздити на роботу, це близько.

Але мені дуже подобається той ефект, якого автор досяг, "виплеснувши" рядки. Очевидно, що при потоках 522 і 371 я не можу їхати по одній лінії на один маршрут, але було б непогано створити пропорційну дугу ліній, щоб показати кількість людей, які здійснюють подорож.

Я думав, що мені вдасться скористатися Генератором геометрії, але без циклічної конструкції я не можу просунутися вперед.


Цей інструмент ESRI може зацікавити вас або хоча б пружинну дошку для кодових ідей щодо створення "клину" рядків.
Хорнбідд

І коли рядок символізує, скажімо, 50 (100, 200) пасажирів на кожен рядок? З деяким кодом пітона (або з генератором геометрії, я не впевнений у цьому) ви могли б обертати лінії (x / 50) з чіткою сумою.
Стефан

Відповіді:


5

Великий виклик!

У цій відповіді передусім використовується генератор Геометрії та був написаний у QGIS 3.2. QGIS вийшов з ладу (не врятувавши мене!) Відразу після того, як я вперше створив лінії, і я майже відмовився, але нещодавно використаний список виразів врятував день - ще один бонус до використання генератора Geometry

Я почав з двох точкових наборів, одного джерела та трьох напрямків. Цілі призначення позначені цифрами:

Початкові пункти

Потім я генерував лінії, що з'єднують вихідну точку з усіма напрямками, використовуючи віртуальний шар, використовуючи наступний код:

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, а дужки видаляються за допомогою регулярного вираження, перш ніж перетворюватися в масив
  3. Потім масив знову розширюється на рядок WKT для багаторядкового рядка, вставляючи координати вихідної точки плюс відповідне форматування - це створює окремий рядок для кожної витягнутої вершини, підключеної до точки джерела
  4. WKT перетворюється назад в геометричний об’єкт і, нарешті, перетинається з буфером вихідної точки, щоб відключити їх назад до кола, на якому сидить точка призначення (див. Висновок a, tapered_bufferщоб зрозуміти, чому це потрібно)

Вболівальники

Під час написання кроків я розумію, що перетворення в масив та з нього не є необхідним, і всі маніпуляції з WKT можуть бути виконані з регулярними виразами. Цей вираз знаходиться нижче, і якщо 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

Як згадується в коментарі, тут є моя ідея створити дугу ліній з Python.

У мене два точкових шару:

i. Один володіє капіталом (ідентифікатор, капітал)

ii. Один тримає міста (ідентифікатор, місто, маршрутки)

Сума переїздів "розділена на банкноти", і це будуть лінії, що створюють дугу. Тож 371 пасажирський транспорт - це комбінація 3х100, 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()

Результат може виглядати приблизно так:

введіть тут опис зображення

ОНОВЛЕННЯ: відмінність чоловіка / жінки

Результати за 4 рівня пам'яті.

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 банкнот).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.