pyQGIS를 이용한 벡터 데이터 처리 6 : 피쳐의 속성 및 좌표 편집

이미 저장되어 있는 피쳐의 속성과 좌표를 변경하는 코드를 살펴보겠습니다. 먼저 피쳐의 속성값을 변경하는 코드는 다음과 같습니다.

QgsProject.instance().removeAllMapLayers()

layer = QgsVectorLayer("D:/__Data__/세종특별자치시_36000/TL_SPRD_MANAGE.shp", "TL_SPRD_MANAGE", "ogr")
QgsProject.instance().addMapLayers([layer])
layer.selectByExpression('"RN_CD" = \'3251036\'')
idxField = layer.fields().indexOf('RN')

features = layer.selectedFeatures()
with edit(layer):
    for f in features:
        layer.changeAttributeValue(f.id(), idxField, "짱기로")

위의 코드는 먼저 RN_CD의 값이 3251036인 피쳐를 선택하고 선택된 피쳐의 RN 필드의 값을 “짱기로”로 변경하는 코드입니다.

다음은 위와 동일한 조건으로 피쳐를 선택하고 선택된 피쳐의 좌표를 편집하는 코드입니다.

QgsProject.instance().removeAllMapLayers()

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

features = layer.selectedFeatures()
with edit(layer):
    for f in features:
        geom = QgsGeometry.fromWkt("MULTILINESTRING((968963 1820821, 993152 1819742, 993567 1844387))")
        layer.changeGeometry(f.id(),geom)

새롭게 정의할 도형의 좌표를 WKT 형식으로 지정하였습니다.

pyQGIS를 이용한 벡터 데이터 처리 5 : 피쳐 선택하고 저장하기

가장 먼저 피쳐의 ID 값으로 선택하는 코드입니다.

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

selectid = [0, 1, 2, 3, 4]
layer.select(selectid)

아래는 선택된 피쳐를 순회하는 코드입니다.

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

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

selectid = [0, 1, 2, 3, 4]
layer.select(selectid)

selection = layer.selectedFeatures()
for feat in selection:
    print(feat['RN'])

다음은 SQL의 Where 조건 형태의 조건으로 피쳐를 선택하고 선택된 피쳐만을 새로운 SHP 파일로 저장하는 코드입니다.

QgsProject.instance().removeAllMapLayers()

layer = QgsVectorLayer("D:/__Data__/세종특별자치시_36000/TL_SPRD_MANAGE.shp", "TL_SPRD_MANAGE", "ogr")
QgsProject.instance().addMapLayers([layer])
layer.selectByExpression('"RN" like \'%로\'')

fn = "D:/__Data__/aa.shp"
writer = QgsVectorFileWriter.writeAsVectorFormat(layer, fn, 
    "utf-8", 
    driverName="ESRI ShapeFile", 
    onlySelected=True
)

print("DONE")

newLayer = QgsVectorLayer(fn, "aa")
QgsProject.instance().addMapLayers([newLayer])

다음은 사각형 영역을 지정해서 이 영역과 교차하는 피쳐를 선택하는 코드입니다.

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

areaOfInterest = QgsRectangle(982413, 1835226, 987413, 1840226)
request = QgsFeatureRequest().setFilterRect(areaOfInterest)

selected = []

for feature in layer.getFeatures(request):
    selected.append(feature.id())
    
layer.select(selected)

pyQGIS를 이용한 벡터 데이터 처리 4 : 스타일 심벌 및 라벨 지정하기

추가한 벡터 레이어에 대한 그리기 스타일을 심벌이라고 하는데, 이 심벌은 매우 다양합니다. 먼저 전체 도형을 하나의 심벌로 지정하는 코드는 다음과 같습니다.

QgsProject.instance().removeAllMapLayers()

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

symbol = QgsLineSymbol.createSimple({"line_style":"solid", "color": "red", "width": "0.5"})
layer.renderer().setSymbol(symbol)
layer.triggerRepaint()

선 스타일은 Solid이고 색상은 red, 굵기는 0.5로 지정했으며 결과는 다음과 같습니다.

다음은 특정 필드의 값에 대한 범위에 따라 심벌을 다르게 설정하는 코드입니다.

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 = "ROAD_BT"

def createRendererRange(layer, label, minV, maxV, color, opacity, width):
    color = QColor(color)
    symbol = QgsSymbol.defaultSymbol(layer.geometryType())
    symbol.setColor(color)
    symbol.setOpacity(1)
    symbol.setWidth(width)
    range = QgsRendererRange(minV, maxV, symbol, label)
    return range

rangeList = [
    createRendererRange(layer, "Group1", 0.0, 10, "#aaaaaa", 1, 0.2),
    createRendererRange(layer, "Group2", 10, 20, "#00ff00", 0.5, 0.5),
    createRendererRange(layer, "Group3", 20, 30, "#ffff00", 1, 0.5),
    createRendererRange(layer, "Group4", 30, 50, "#ff0000", 0.5, 0.5)
]

groupRenderer = QgsGraduatedSymbolRenderer("", rangeList)
groupRenderer.setMode(QgsGraduatedSymbolRenderer.EqualInterval)
groupRenderer.setClassAttribute(field)
layer.setRenderer(groupRenderer)

ROAD_BT에 대한 필드값을 4개의 구간에 대해 다른 심벌을 지정하고 있으며 그 결과는 다음과 같습니다.

다음은 라벨을 설정하는 코드입니다.

text_format = QgsTextFormat()
text_format.setFont(QFont("Arial"))
text_format.setSize(12)
text_format.setColor(QColor("white"))
buffer_settings = QgsTextBufferSettings()
buffer_settings.setEnabled(True)
buffer_settings.setSize(1)
buffer_settings.setColor(QColor("gray"))
text_format.setBuffer(buffer_settings)

layer_settings = QgsPalLayerSettings()
layer_settings.setFormat(text_format)
layer_settings.fieldName = "name"
layer_settings.placement = QgsPalLayerSettings.Line

label_settings = QgsVectorLayerSimpleLabeling(layer_settings)
layer.setLabelsEnabled(True)
layer.setLabeling(label_settings)
layer.triggerRepaint()

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

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 타입의 객체들이 배열로 저장됩니다.