선과 값으로 선호 만들기


9

다음 과 같이 Origin-Destination 플롯 을 다시 만들려고합니다 .

여기에 이미지 설명을 입력하십시오

데이터를 MSOA에서 LAD 테이블로 병합하고 원본 MSOA 중 하나에 대해 이와 같은 맵을 그릴 수 있습니다.

여기에 이미지 설명을 입력하십시오

피크 디스트릭트 (Peak District) 출퇴근 거리에있는 사람들이 (현재 말도 안되는) 거리를두고 나면 어느 것이 가까이 있을까요?

그러나 나는 저자가 그 선을 "재생"함으로써 달성 한 효과를 매우 좋아한다. 분명히 522와 371의 흐름으로 통근자 당 한 줄을 갈 수는 없지만 여행하는 사람들의 수를 보여주기 위해 비례하는 호를 만드는 것이 좋습니다.

Geometry Generator를 사용할 수 있다고 생각했지만 루프 구조가 없으면 앞을 내다 볼 수 없습니다.


ESRI 툴 은 여러분에게 관심이 있거나 최소한 "쐐기"라인을 만드는 코드 아이디어를위한 스프링 보드입니다.
Hornbydd

그리고 한 줄이 상징 될 때 한 줄에 50 (100, 200) 통근자를 가정 해 봅시다. 일부 파이썬 코드 (또는 지오메트리 생성기, 확실하지 않음)를 사용하면 선 (x / 50)을 뚜렷한 양으로 회전시킬 수 있습니다.
Stefan

답변:


5

큰 도전!

이 답변은 주로 Geometry generator를 사용하며 QGIS 3.2로 작성되었습니다. QGIS는 라인을 처음 구축하고 거의 포기한 직후에 (저장하지 않고!) 추락했지만 최근에 사용한 표현식 목록이 하루를 절약했습니다. 지오메트리 생성기를 사용하는 또 다른 보너스

두 개의 포인트 세트 (하나의 소스와 세 개의 목적지)로 시작했습니다. 목적지는 개수로 표시됩니다.

초기 포인트

그런 다음 다음 코드를 사용하여 가상 계층을 사용하여 소스 지점을 모든 대상에 연결하는 선을 생성했습니다.

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. 소스에서 너비가 0에서 대상 끝의 대상 수에 따라 크기가 조정되는 테이퍼 버퍼를 생성합니다. 버퍼 포인트 밀도는 대상 개수 속성에 따라 조정됩니다.
  2. 버퍼 다각형의 꼭짓점은 점으로 변환 된 후 (불필요 할 수 있음) WKT로 내보내고, 정규식을 사용하여 대괄호를 배열로 변환하기 전에 제거합니다.
  3. 그런 다음 소스 포인트의 좌표와 관련 형식을 삽입하여 여러 줄 문자열에 대해 배열을 WKT 문자열로 다시 확장합니다. 이렇게하면 소스 포인트에 연결된 추출 된 정점 각각에 대해 별도의 선이 만들어집니다
  4. WKT는 지오메트리 객체로 다시 변환되고 마지막으로 소스 포인트의 버퍼와 교차하여 대상 포인트가있는 원으로 다시 클립합니다 ( tapered_buffer이 결과가 필요한 이유를 이해하려면 a의 출력 참조 ).

팬들

단계를 작성하면서 배열과의 변환은 불필요하며 모든 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

당신의 질문은 나를 궁금하게 만들었습니다.

이 솔루션은 Python 콘솔의 QGIS 2.x에서만 작동합니다.

내 의견에서 언급했듯이 여기에 파이썬으로 호를 만드는 아이디어가 있습니다.

두 개의 포인트 레이어가 있습니다.

나는. 수도를 보유하고있는 분 (ID, 수도)

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

결과는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

업데이트 : 구별 남성 / 여성

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

결과는 다음과 같습니다.여기에 이미지 설명을 입력하십시오

지도 제작상의 관점에서 이상적이지 않은 것 :

원호의 크기는 큰 원호가 더 많은 통근자를 나타낼 수있는 방식으로 언뜻보기에 자극적 일 수 있습니다. 더 많은 통근자를 가진 다른 통근자 (311 통근자 / 5 지폐)보다 적은 통근자 (289 통근자 / 11 지폐)로 아크가 클 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.