186 lines
6.9 KiB
Python
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 |