pyQGIS를 이용한 벡터 데이터 처리 3 : 속성 및 좌표 얻기

벡터 데이터는 피쳐(Feature)라는 항목들로 구성되며 피쳐는 속성과 좌표로 정의됩니다. 벡터 데이터를 구성하는 피쳐에 대한 속성과 좌표를 얻는 코드를 살펴보겠습니다.

먼저 벡터 데이터의 속성값에 대한 필드 정보는 얻는 코드입니다.

QgsProject.instance().removeAllMapLayers()
layer = QgsVectorLayer("D:/__Data__/세종특별자치시_36000/TL_SPRD_MANAGE.shp", "TL_SPRD_MANAGE", "ogr")
QgsProject.instance().addMapLayers([layer])

for field in layer.fields():
    print(field.name(), field.typeName(), field.length(), field.precision())

실행 결과는 다음과 같습니다.

그리고 모든 피쳐의 각 필드의 값을 얻어오는 코드는 다음과 같습니다.

QgsProject.instance().removeAllMapLayers()

layer = QgsVectorLayer("D:/__Data__/세종특별자치시_36000/TL_SPRD_MANAGE.shp", "TL_SPRD_MANAGE")
QgsProject.instance().addMapLayers([layer])

fieldNames = []
for field in layer.fields():
    fieldNames.append(field.name())

fc = layer.featureCount()
for i in range(0, fc):
    print("\nFID: ", i)
    feature = layer.getFeature(i)
    for fn in fieldNames:
        print(fn, feature[fn])
    
    if i == 5: break

먼저 필드명을 배열에 저장해 두고 각 피쳐에 대해 필드명으로 그 필드값을 얻어올 수 있습니다. 필드명이 아닌 필드의 인덱스 번호로도 필드값을 얻을 수 있습니다. 필드의 인덱스의 시작은 0입니다. 위의 코드에 대한 실행결과는 다음과 같습니다.

다음은 피쳐의 좌표를 얻어오는 코드입니다.

QgsProject.instance().removeAllMapLayers()

layer = QgsVectorLayer("D:/__Data__/세종특별자치시_36000/TL_SPRD_MANAGE.shp", "TL_SPRD_MANAGE")
QgsProject.instance().addMapLayers([layer])

fc = layer.featureCount()
for i in range(0, fc):
    feature = layer.getFeature(i)
    geometry = feature.geometry()
    # print(geometry.asWkt())
    geomSingleType = QgsWkbTypes.isSingleType(geometry.wkbType())
    
    if geometry.type() == QgsWkbTypes.LineGeometry:
        if geomSingleType:
            polyline = geometry.asPolyline()
            print("line:", geom)
        else:
            polyline = geometry.asMultiPolyline()
            print("multiline:", geom)
    
    if i == 5: break

실행결과는 다음과 같습니다.

지오메트리의 좌표를 뽑아내기 위해서는 Multi 여부를 확인해서 형변환(asPolyline, asMultiPolyline 등)이 필요하며 형변환된 객체에 지오메트리의 구성 좌표가 QgsPointXY 타입의 객체들이 배열로 저장됩니다.

pyQGIS를 이용한 벡터 데이터 처리 2 : 레이어 추가 및 삭제

SHP 파일 등과 같은 벡터 데이터를 레이어로 추가하는 코드는 다음과 같습니다.

layer = QgsVectorLayer("D:/__Data__/세종특별자치시_36000/TL_SPRD_MANAGE.shp", "TL_SPRD_MANAGE")
QgsProject.instance().addMapLayers([layer])

실행 결과는 다음과 같습니다. 레이어를 추가할 때 추가된 레이어가 바로 지도뷰에 꽉 차게 표시하기 위한 코드는 다음과 같습니다.

canvas = iface.mapCanvas()
extent = layer.extent()
canvas.setExtent(extent)

추가한 레이어에 대한 객체를 얻는 코드는 다음과 같습니다.

layers = QgsProject.instance().mapLayersByName("TL_SPRD_MANAGE")
print(len(layers), layers[0].name())

레이어의 이름으로 객체를 얻는 것인데, 레이어의 이름은 중복될 수 있으므로 동일한 이름이라도 여러개의 레이어 객체가 참조 될 수 있습니다.

추가한 레이어를 삭제하는 코드는 다음과 같습니다.

layers = QgsProject.instance().mapLayersByName("TL_SPRD_MANAGE")
QgsProject.instance().removeMapLayer(layers[0].id())

레이어의 이름은 중복될 수 있으므로 실제 삭제 시에는 레이어의 ID 값을 지정하여 삭제해야 합니다.

추가된 모든 레이어를 삭제하는 코드는 다음과 같습니다.

QgsProject.instance().removeAllMapLayers()

pyQGIS를 이용한 벡터 데이터 처리 1 : Message, Progress 출력

QGIS 프로그램 안에서 사용자에게 어떤 메세지를 전달하고자 할 때의 코드의 예는 다음과 같습니다.

iface.messageBar().pushMessage("안녕하세요, pyQGIS")

실행 결과는 QGIS의 맵영역 상단에 표시되며 다음과 같습니다.

iface의 messageBar를 통한 pushMessage 매서드는 더 많은 인자를 받을 수 있습니다. 다음 코드를 보면요.

iface.messageBar().pushMessage(
    "환영", 
    "안녕하세요, pyQGIS",
    level=Qgis.Success,
    duration=5
)

아래의 실행 결과를 통해 각 인자의 의미를 유추할 수 있습니다.

세번째와 네번째 인자에 대해 언급하면 먼저 level 인자는 4가지 값을 가질 수 있습니다. Qgis.Critical, Qgis.Warning, Qgis.Info, Qgis.Success이며 각각 에러 메세지, 경고 메세지, 정보 메세지, 성공 메세지에 대한 아이콘을 표시해 줍니다. 그리고 duration은 메세지를 몇초간 표시하고 사라지게 할지 지정할 수 있습니다.

QGIS에서는 시간이 오래 걸리는 연산이 많이 수행됩니다. 이때 수행되는 연산의 진행율을 사용자에게 알려주는 코드는 다음과 같습니다.

import time

progressMessageBar = iface.messageBar().createMessage("처리 메세지")

progress = QProgressBar()
progress.setMaximum(100)
progressMessageBar.layout().addWidget(progress)

iface.messageBar().pushWidget(progressMessageBar, Qgis.Info)

for i in range(100):
    time.sleep(0.01)
    progress.setValue(i + 1)

time.sleep(0.01)을 100번 호출하므로 1초에 걸쳐 최대 100까지 값을 증가시켜 진행율을 표시합니다. 실행결과는 다음과 같습니다.

pyQGIS Tip

특정 Feature를 확대하기

아래의 코드는 선택된 Feature를 확대합니다.

canvas = iface.mapCanvas()
id = 0
#canvas.setSelectionColor(QColor("red"))
layer.select(id)
canvas.zoomToSelected(layer)

아래의 코드는 Feature를 선택하지 않고 FID가 0인 Feature를 확대합니다.

canvas = iface.mapCanvas()
id = 0
feat = layer.getFeature(id)
geom = feat.geometry()
bbox = geom.boundingBox()
canvas.setExtent(bbox)
canvas.refresh()

특정 Feature를 깜박이게 하기

canvas = iface.mapCanvas()
id = 0
canvas.flashFeatureIds(layer, [id])

화면 좌표와 지도 좌표 간의 변환

아래의 코드는 화면 좌표 (100, 20)을 지도 좌표로 변환하고 다시 변환된 지도 좌표를 화면 좌표로 변환하는 코드입니다.

ptView = QPoint(100, 20)
ptMap = canvas.getCoordinateTransform().toMapCoordinates(ptView)
print(ptMap.x(), ptMap.y())

ptMap2 = QgsMapTool(canvas).toCanvasCoordinates(ptMap)
print(ptMap2.x(), ptMap2.y())

다양한 형태의 지오메트리 생성

좌표를 지정해서 다양한 형태의 지오메트리를 생성하는 코드입니다.

geomPoint = QgsGeometry.fromPointXY(QgsPointXY(10000, 15000))

geomMultiPoint = QgsGeometry.fromMultiPointXY(
    [
        QgsPointXY(10000, 15000),
        QgsPointXY(11000, 16000),
    ]
)

geomPolyline = QgsGeometry.fromPolylineXY(
    [
        QgsPointXY(10000, 15000),
        QgsPointXY(11000, 16000),
        QgsPointXY(12000, 17000),
        QgsPointXY(13000, 18000),
    ]
)

geomPolyline = QgsGeometry.fromMultiPolylineXY(
    [
        [
            QgsPointXY(10000, 15000),
            QgsPointXY(11000, 16000),
            QgsPointXY(12000, 17000),
            QgsPointXY(13000, 18000),
        ]
    ]
)

pX = 971195
pY = 1841488

geomPolygon = QgsGeometry.fromPolygonXY(
    [
        [
            QgsPointXY(pX, pY),
            QgsPointXY(pX+1000, pY),
            QgsPointXY(pX+1000, pY+1000),
            QgsPointXY(pX, pY+1000),
        ],
        [
            QgsPointXY(pX+100, pY+100),
            QgsPointXY(pX+900, pY+100),
            QgsPointXY(pX+900, pY+900),
            QgsPointXY(pX+100, pY+900),
        ]
    ]
)

geomPolygon = QgsGeometry.fromMultiPolygonXY(
    [
        [
            [
                QgsPointXY(pX, pY),
                QgsPointXY(pX+1000, pY),
                QgsPointXY(pX+1000, pY+1000),
                QgsPointXY(pX, pY+1000),
            ],
            [
                QgsPointXY(pX+100, pY+100),
                QgsPointXY(pX+900, pY+100),
                QgsPointXY(pX+900, pY+900),
                QgsPointXY(pX+100, pY+900),
            ]
        ]
    ]
)

아래는 Python 언어의 특징을 이용한 효율적인 코드 작성입니다.

pts = [
    (583899.8, 8177297.19), 
    (583900.23, 8177262.52), 
    (583896.49, 8177262.47), 
    (583895.96, 8177297.29), 
    (583899.8, 8177297.19)
]

geomPolygon = QgsGeometry.fromPolygonXY( [[ QgsPointXY( xy[0], xy[1] ) for xy in pts ]] ) 

GeoService-Xr의 플러그인(PlugIn) 설정

GeoService-Xr을 기반으로 구축된 프로젝트마다 특화된 기능의 개발을 위해 PlugIn 방식으로 기능 확장을 하기 위한 설정입니다. 이 방식을 통해 GeoService-Xr의 유지보수 관리와 개별 프로젝트에 대한 특화된 기능에 대한 유지보수 관리가 효과적으로 완전히 분리됩니다.

기본적으로 GeoService-Xr의 설정 파일은 XrConfig.xml이며 아래의 예와 같습니다.


    ./SQL.json
    ./key.json
    ./session.json
    ./webHome
    ./geodata.xml
    777
    256
    10
    5485760
    0
    0
    ./plugin.json
    false

위의 설정 값 중 plugin 값으로 지정된 값인 plugin.json이 플러그인 기능에 대한 설정 파일입니다. 이 설정 파일을 보면 다음과 같습니다.

{
    "initializer": "kr.co.geoservice.geoserviceweb.PlugInInitializer",

    "services": [
        "kr.co.geoservice.geoserviceweb.request.ChangeProfile",
        "kr.co.geoservice.geoserviceweb.request.CheckAuthorizationCode",

        ....
    ],

    "schedulers": [
        "kr.co.geoservice.geoserviceweb.scheduler.NonauthorizedUserRemoverScheduler"

        ....
    ]
}

initializer에 지정된 Java 클래스가 해당 플러그인에 대한 초기화를 시켜주며, services에 지정된 Java 클래스들이 각기 기능 단위들입니다. schedulers는 일정 시간마다 특정한 작업을 실행하는 Java 클래스들입니다.

아울러 GeoService-Xr에 대한 jar 파일을 생성할 때 manifest 파일에 플러그인에 대한 jar 파일을 포함해줘야 합니다.