Files
2024-06-28 12:23:19 +02:00

186 lines
6.9 KiB
Python

from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing,
QgsFeatureSink,
QgsProcessingException,
QgsProcessingAlgorithm,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterString,
QgsProcessingParameterEnum,
QgsFeature,
QgsField,
QgsGeometry,
QgsVectorLayer,
QgsProject,
QgsProcessingContext)
from qgis import processing
import requests
import geopandas as gpd
class FetchGeoBoundaryAlgorithm(QgsProcessingAlgorithm):
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
RELEASE_TYPE = 'RELEASE_TYPE'
COUNTRY_CODE = 'COUNTRY_CODE'
BOUNDARY_TYPE = 'BOUNDARY_TYPE'
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return FetchGeoBoundaryAlgorithm()
def name(self):
return 'fetchgeoboundary'
def displayName(self):
return self.tr('Fetch geoBoundaries')
def group(self):
return self.tr('geoBoundaries')
def groupId(self):
return 'geoboundaryscripts'
def shortHelpString(self):
return self.tr("Fetches geoBoundaries and adds them. This is an unofficial tool by Joaquin Gottlebe. \n More information: https://www.geoboundaries.org/index.html")
def initAlgorithm(self, config=None):
self.addParameter(
QgsProcessingParameterString(
self.COUNTRY_CODE,
self.tr('Country Code ISO-3'),
defaultValue='DEU'
)
)
self.addParameter(
QgsProcessingParameterEnum(
self.BOUNDARY_TYPE,
self.tr('Boundary Type'),
options=['ADM0','ADM1','ADM2','ADM3','ADM4','ADM5'],
defaultValue=0
)
)
self.addParameter(
QgsProcessingParameterEnum(
self.RELEASE_TYPE,
self.tr('Release Type'),
options=['gbOpen','gbHumanitarian','gbAuthorative'],
defaultValue='gbOpen'
)
)
self.addParameter(
QgsProcessingParameterFeatureSink(
self.OUTPUT,
self.tr('Output layer')
)
)
def processAlgorithm(self, parameters, context, feedback):
release_type_index = self.parameterAsEnum(parameters, self.RELEASE_TYPE, context)
release_types = ['gbOpen', 'gbHumanitarian', 'gbAuthorative']
release_type = release_types[release_type_index]
country_code = self.parameterAsString(parameters, self.COUNTRY_CODE, context)
boundary_type_index = self.parameterAsEnum(parameters, self.BOUNDARY_TYPE, context)
boundary_types = ['ADM0','ADM1','ADM2','ADM3','ADM4','ADM5']
boundary_type = boundary_types[boundary_type_index]
results = self.fetch_geoboundary(release_type, country_code, boundary_type, feedback)
if not results:
raise QgsProcessingException('Failed to fetch geoBoundary')
total_features = sum(len(result['gdf']) for result in results)
processed_features = 0
layer_name = f"{country_code}_{boundary_type}_{release_type}_geoBoundaries"
vector_layer = QgsVectorLayer("Polygon?crs=epsg:4326", layer_name, "memory")
pr = vector_layer.dataProvider()
for result in results:
gdf = result['gdf']
metadata = result['metadata']
if gdf.empty:
feedback.reportError("Loaded GeoDataFrame is empty.")
continue
for index, row in gdf.iterrows():
if feedback.isCanceled():
break
# Debugging type and attribute
if not hasattr(row['geometry'], 'wkt'):
feedback.reportError(f"Unexpected type for geometry: {type(row['geometry'])}. Expected shapely geometry object.")
continue # Skip this iteration if the geometry type is unexpected
# Assuming row['geometry'] is a shapely.geometry object as expected
feat = QgsFeature()
try:
feat.setGeometry(QgsGeometry.fromWkt(row['geometry'].wkt))
except Exception as e:
feedback.reportError(f"Error setting geometry from WKT: {e}")
continue # Skip this iteration if there was an error setting the geometry
# Add additional feature settings and add feature to the provider as necessary
pr.addFeature(feat)
vector_layer.updateExtents()
QgsProject.instance().addMapLayer(vector_layer)
feedback.pushInfo("GeoBoundary layer added to the project.")
return {self.OUTPUT: vector_layer.id()}
def fetch_geoboundary(self, release_type, country_code, boundary_type, feedback):
api_url = f"https://www.geoboundaries.org/api/current/{release_type}/{country_code}/{boundary_type}/"
try:
response = requests.get(api_url)
if response.status_code != 200:
print(f"Failed to fetch data: HTTP Status Code {response.status_code}")
return None
data = response.json()
results = []
if not isinstance(data, list):
data = [data]
total_countries = len(data)
processed_countries = 0
for country_data in data:
if feedback.isCanceled():
return None
if 'gjDownloadURL' not in country_data:
feedback.reportError("'gjDownloadURL' not found in the response for" + country_data.get('boundaryISO', 'an unknown country'))
continue
geojson_url = country_data['gjDownloadURL']
gdf = gpd.read_file(geojson_url)
if gdf.empty:
feedback.reportError("Loaded GeoDataFrame is empty.")
continue
metadata = {key: country_data.get(key, '') for key in country_data}
results.append({'gdf': gdf, 'metadata' : metadata})
processed_countries += 1
feedback.setProgress(int((processed_countries / total_countries) * 100))
return results
except requests.RequestException as e:
feedback.reportError(f"Request error: {e}")
except Exception as e:
feedback.reportError(f"An unexpected error occurred: {e}")
return None