안녕하세요? 이번 글은 "QGIS 실전 플러그인 개발" 시리즈의 다섯 번째로, "플러그인의 핵심 로직 구현"에 대해 다룹니다. 이번에는 geoquery.py 파일을 변경하여 플러그인의 핵심 로직을 구현합니다.
핵심 로직 구현
두 번째 글에서 작성한 Python 코드를 적용하되, 일부 코드만 변경해주시면 되겠습니다.
import requests
import json
from qgis.core import *
from qgis.utils import iface
from PyQt5.QtCore import QVariant
def get_bbox_epsg5179():
"""
현재 QGIS 맵 캔버스의 BBox를 EPSG:5179 좌표계로 변환하여 반환
"""
canvas = iface.mapCanvas()
extent = canvas.extent()
canvas_crs = canvas.mapSettings().destinationCrs()
target_crs = QgsCoordinateReferenceSystem("EPSG:5179")
transform = QgsCoordinateTransform(canvas_crs, target_crs, QgsProject.instance())
min_point = transform.transform(QgsPointXY(extent.xMinimum(), extent.yMinimum()))
max_point = transform.transform(QgsPointXY(extent.xMaximum(), extent.yMaximum()))
return f"{min_point.x()},{min_point.y()},{max_point.x()},{max_point.y()}"
def fetch_vworld_data(api_key, bbox, query, page, page_size):
"""
Vworld API 요청을 보내고 응답 데이터를 반환
Args:
api_key (str): Vworld API 키
bbox (str): EPSG:5179 기준 BBox
query (str): 검색 키워드
page (int): 페이지 번호
page_size (int): 페이지 크기
Returns:
dict: API 응답 데이터
"""
url = (
f"https://api.vworld.kr/req/search?service=search&request=search"
f"&version=2.0&crs=EPSG:5179&bbox={bbox}&type=place&format=json"
f"&errorformat=json&key={api_key}&query={query}&size={page_size}&page={page}"
)
response = requests.get(url)
if response.status_code == 200:
return response.json()
else:
print(f"Error: {response.status_code}")
return None
def add_data_to_qgis_layer(all_items):
"""
Vworld API에서 가져온 데이터를 QGIS 레이어에 추가
Args:
all_items (list): 검색된 데이터 리스트
"""
try:
features = []
for item in all_items:
x = float(item["point"]["x"])
y = float(item["point"]["y"])
road = item.get("address", {}).get("road", "")
parcel = item.get("address", {}).get("parcel", "")
point = QgsPointXY(x, y)
feature = QgsFeature()
feature.setGeometry(QgsGeometry.fromPointXY(point))
feature.setAttributes(
[
item.get("id", ""),
item.get("title", ""),
item.get("category", ""),
road,
parcel,
]
)
features.append(feature)
layer = QgsVectorLayer("Point?crs=EPSG:5179", "Vworld Search Results", "memory")
provider = layer.dataProvider()
provider.addAttributes(
[
QgsField("ID", QVariant.String),
QgsField("Title", QVariant.String),
QgsField("Category", QVariant.String),
QgsField("Road", QVariant.String),
QgsField("Parcel", QVariant.String),
]
)
layer.updateFields()
provider.addFeatures(features)
QgsProject.instance().addMapLayer(layer)
iface.messageBar().pushMessage(
"Vworld Search", f"Added {len(all_items)} items to QGIS.", level=1
)
except KeyError as e:
print(f"KeyError: {e} - Check the response data structure.")
api_key = "3A99F800-2E60-3397-9F0E-2746BC4169F6"
query = "국립공원"
bbox = get_bbox_epsg5179()
page = 1
page_size = 1000
all_items = []
while True:
data = fetch_vworld_data(api_key, bbox, query, page, page_size)
if data and "response" in data and "result" in data["response"]:
items = data["response"]["result"]["items"]
all_items.extend(items)
total_results = int(data["response"]["record"]["total"])
print(
f"Total Results: {total_results}, Current Page: {page}, Items Retrieved: {len(items)}"
)
if len(items) < page_size:
break
page += 1
else:
print("Failed to fetch or parse data.")
break
# 수집한 데이터를 QGIS에 추가
add_data_to_qgis_layer(all_items)
먼저 다음 코드는 geoquery.py에 그대로 붙여넣기 합니다.
import requests
import json
from qgis.core import *
from qgis.utils import iface
from PyQt5.QtCore import QVariant
def get_bbox_epsg5179():
"""
현재 QGIS 맵 캔버스의 BBox를 EPSG:5179 좌표계로 변환하여 반환
"""
canvas = iface.mapCanvas()
extent = canvas.extent()
canvas_crs = canvas.mapSettings().destinationCrs()
target_crs = QgsCoordinateReferenceSystem("EPSG:5179")
transform = QgsCoordinateTransform(canvas_crs, target_crs, QgsProject.instance())
min_point = transform.transform(QgsPointXY(extent.xMinimum(), extent.yMinimum()))
max_point = transform.transform(QgsPointXY(extent.xMaximum(), extent.yMaximum()))
return f"{min_point.x()},{min_point.y()},{max_point.x()},{max_point.y()}"
def fetch_vworld_data(api_key, bbox, query, page, page_size):
"""
Vworld API 요청을 보내고 응답 데이터를 반환
Args:
api_key (str): Vworld API 키
bbox (str): EPSG:5179 기준 BBox
query (str): 검색 키워드
page (int): 페이지 번호
page_size (int): 페이지 크기
Returns:
dict: API 응답 데이터
"""
url = (
f"https://api.vworld.kr/req/search?service=search&request=search"
f"&version=2.0&crs=EPSG:5179&bbox={bbox}&type=place&format=json"
f"&errorformat=json&key={api_key}&query={query}&size={page_size}&page={page}"
)
response = requests.get(url)
if response.status_code == 200:
return response.json()
else:
print(f"Error: {response.status_code}")
return None
def add_data_to_qgis_layer(all_items):
"""
Vworld API에서 가져온 데이터를 QGIS 레이어에 추가
Args:
all_items (list): 검색된 데이터 리스트
"""
try:
features = []
for item in all_items:
x = float(item["point"]["x"])
y = float(item["point"]["y"])
road = item.get("address", {}).get("road", "")
parcel = item.get("address", {}).get("parcel", "")
point = QgsPointXY(x, y)
feature = QgsFeature()
feature.setGeometry(QgsGeometry.fromPointXY(point))
feature.setAttributes(
[
item.get("id", ""),
item.get("title", ""),
item.get("category", ""),
road,
parcel,
]
)
features.append(feature)
layer = QgsVectorLayer("Point?crs=EPSG:5179", "Vworld Search Results", "memory")
provider = layer.dataProvider()
provider.addAttributes(
[
QgsField("ID", QVariant.String),
QgsField("Title", QVariant.String),
QgsField("Category", QVariant.String),
QgsField("Road", QVariant.String),
QgsField("Parcel", QVariant.String),
]
)
layer.updateFields()
provider.addFeatures(features)
QgsProject.instance().addMapLayer(layer)
iface.messageBar().pushMessage(
"Vworld Search", f"Added {len(all_items)} items to QGIS.", level=1
)
except KeyError as e:
print(f"KeyError: {e} - Check the response data structure.")
이제 남은 코드는 아래와 같습니다.
api_key = "3A99F800-2E60-3397-9F0E-2746BC4169F6"
query = "국립공원"
bbox = get_bbox_epsg5179()
page = 1
page_size = 1000
all_items = []
while True:
data = fetch_vworld_data(api_key, bbox, query, page, page_size)
if data and "response" in data and "result" in data["response"]:
items = data["response"]["result"]["items"]
all_items.extend(items)
total_results = int(data["response"]["record"]["total"])
print(
f"Total Results: {total_results}, Current Page: {page}, Items Retrieved: {len(items)}"
)
if len(items) < page_size:
break
page += 1
else:
print("Failed to fetch or parse data.")
break
# 수집한 데이터를 QGIS에 추가
add_data_to_qgis_layer(all_items)
query 변수는 대화 상자의 텍스트 입력으로 변경해 줍니다.
query = "국립공원"
query = self.dlg.lineEdit.text()
geoquery.py의 run() 메서드는 "확인" 버튼을 클릭했을 때 코드를 추가할 수 있도록 빈 조건문( if result: ... )을 제공하고 있습니다.
def run(self):
"""Run method that performs all the real work"""
# Create the dialog with elements (after translation) and keep reference
# Only create GUI ONCE in callback, so that it will only load when the plugin is started
if self.first_start == True:
self.first_start = False
self.dlg = GeoQueryPluginDialog()
# show the dialog
self.dlg.show()
# Run the dialog event loop
result = self.dlg.exec_()
# See if OK was pressed
if result:
# Do something useful here - delete the line containing pass and
# substitute with your code.
pass
여기에 query 변수를 변경한 남은 코드를 추가해 줍니다.
if result:
api_key = "3A99F800-2E60-3397-9F0E-2746BC4169F6"
query = self.dlg.lineEdit.text()
bbox = get_bbox_epsg5179()
page = 1
page_size = 1000
all_items = []
while True:
data = fetch_vworld_data(api_key, bbox, query, page, page_size)
if data and "response" in data and "result" in data["response"]:
items = data["response"]["result"]["items"]
all_items.extend(items)
total_results = int(data["response"]["record"]["total"])
print(
f"Total Results: {total_results}, Current Page: {page}, Items Retrieved: {len(items)}"
)
if len(items) < page_size:
break
page += 1
else:
print("Failed to fetch or parse data.")
break
# 수집한 데이터를 QGIS에 추가
add_data_to_qgis_layer(all_items)
핵심 로직 구현 확인
이제 OpenStreetMap에서 북한산국립공원 영역을 선택한 후, "GeoQuery for QGIS" 플러그인을 실행하겠습니다. "국립공원"을 검색한 뒤 확인을 클릭합니다!
아래와 같이 124개의 지점이 QGIS 레이어에 자동으로 추가되었습니다! 다음 글에서는 마지막으로 아이콘 변경을 위한 리소스 파일 컴파일 방법을 소개하겠습니다.