diff --git a/drawio-diagramms/Workflow.drawio b/drawio-diagramms/Workflow.drawio
new file mode 100644
index 0000000..3cb36c0
--- /dev/null
+++ b/drawio-diagramms/Workflow.drawio
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/analysisworkflow.drawio b/drawio-diagramms/analysisworkflow.drawio
new file mode 100644
index 0000000..fff3177
--- /dev/null
+++ b/drawio-diagramms/analysisworkflow.drawio
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/bordersworkflow.drawio b/drawio-diagramms/bordersworkflow.drawio
new file mode 100644
index 0000000..9b9c856
--- /dev/null
+++ b/drawio-diagramms/bordersworkflow.drawio
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/facilitiesworkflow.drawio b/drawio-diagramms/facilitiesworkflow.drawio
new file mode 100644
index 0000000..c85c6e5
--- /dev/null
+++ b/drawio-diagramms/facilitiesworkflow.drawio
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/frictionsurfaceworkflow.drawio b/drawio-diagramms/frictionsurfaceworkflow.drawio
new file mode 100644
index 0000000..5c32f15
--- /dev/null
+++ b/drawio-diagramms/frictionsurfaceworkflow.drawio
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/icanalysisworkflow.drawio b/drawio-diagramms/icanalysisworkflow.drawio
new file mode 100644
index 0000000..0829e2d
--- /dev/null
+++ b/drawio-diagramms/icanalysisworkflow.drawio
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/indcomworkflow.drawio b/drawio-diagramms/indcomworkflow.drawio
new file mode 100644
index 0000000..54140b9
--- /dev/null
+++ b/drawio-diagramms/indcomworkflow.drawio
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/overviewworkflow.drawio b/drawio-diagramms/overviewworkflow.drawio
new file mode 100644
index 0000000..61fe55f
--- /dev/null
+++ b/drawio-diagramms/overviewworkflow.drawio
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/roadsworkflow.drawio b/drawio-diagramms/roadsworkflow.drawio
new file mode 100644
index 0000000..bb9e1bb
--- /dev/null
+++ b/drawio-diagramms/roadsworkflow.drawio
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/studyareaworkflow.drawio b/drawio-diagramms/studyareaworkflow.drawio
new file mode 100644
index 0000000..2295e14
--- /dev/null
+++ b/drawio-diagramms/studyareaworkflow.drawio
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/traveltimeworkflow 2.drawio b/drawio-diagramms/traveltimeworkflow 2.drawio
new file mode 100644
index 0000000..c056e6a
--- /dev/null
+++ b/drawio-diagramms/traveltimeworkflow 2.drawio
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drawio-diagramms/traveltimeworkflow.drawio b/drawio-diagramms/traveltimeworkflow.drawio
new file mode 100644
index 0000000..c056e6a
--- /dev/null
+++ b/drawio-diagramms/traveltimeworkflow.drawio
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python-scripts/correlation.py b/python-scripts/correlation.py
new file mode 100644
index 0000000..5c26200
--- /dev/null
+++ b/python-scripts/correlation.py
@@ -0,0 +1,51 @@
+import rasterio
+import numpy as np
+from scipy.stats import pearsonr
+import matplotlib.pyplot as plt
+
+def read_raster_data(raster_path, target_width, target_height):
+ with rasterio.open(raster_path) as raster:
+ data = raster.read(1, masked=True) # Reads the first band
+ if data.shape != (target_height, target_width):
+ data = data[:target_height, :target_width]
+ if raster.nodata is not None:
+ data = data.filled(np.nan) # Fill masked values with NaN
+ return data
+
+def flatten_data(data):
+ return data.flatten()
+
+def calculate_correlation(data1, data2):
+ mask = ~np.isnan(data1) & ~np.isnan(data2)
+
+ filtered_data1 = data1[mask]
+ filtered_data2 = data2[mask]
+
+ correlation, _ = pearsonr(filtered_data1, filtered_data2)
+ return correlation, filtered_data1, filtered_data2 # Return the filtered data for plotting
+
+def plot_correlation(data1, data2, correlation, file_path):
+ plt.scatter(data1, data2, alpha=0.5)
+ plt.xlim(0,7)
+ #plt.title(f'Correlation: {correlation:.2f}')
+ plt.xlabel('Raster 1 Values')
+ plt.ylabel('Raster 2 Values')
+ plt.savefig(file_path, dpi=300)
+ plt.close()
+
+
+if __name__ == "__main__":
+ raster_path1 = 'friction surface.tif'
+ raster_path2 = 'ghsl.tif'
+ save_path = 'plot.png'
+
+ # Common dimensions
+ target_width = 275 # Choose based on your requirements
+ target_height = 254
+
+ data1 = flatten_data(read_raster_data(raster_path1, target_width, target_height))
+ data2 = flatten_data(read_raster_data(raster_path2, target_width, target_height))
+
+ correlation, filtered_data1, filtered_data2 = calculate_correlation(data1, data2)
+ plot_correlation(filtered_data1, filtered_data2, correlation, save_path)
+
diff --git a/python-scripts/correlation_grouped.py b/python-scripts/correlation_grouped.py
new file mode 100644
index 0000000..c714932
--- /dev/null
+++ b/python-scripts/correlation_grouped.py
@@ -0,0 +1,94 @@
+import rasterio
+import numpy as np
+from scipy.stats import pearsonr
+from scipy.optimize import curve_fit
+import matplotlib.pyplot as plt
+
+def read_raster_data(raster_path, target_width, target_height):
+ with rasterio.open(raster_path) as raster:
+ data = raster.read(1, masked=True) # Reads the first band
+ if data.shape != (target_height, target_width):
+ data = data[:target_height, :target_width]
+ if raster.nodata is not None:
+ data = data.filled(np.nan) # Fill masked values with NaN
+ return data
+
+def flatten_data(data):
+ return data.flatten()
+
+def calculate_correlation(data1, data2):
+ mask = ~np.isnan(data1) & ~np.isnan(data2)
+
+ filtered_data1 = data1[mask]
+ filtered_data2 = data2[mask]
+
+ correlation, _ = pearsonr(filtered_data1, filtered_data2)
+ return correlation, filtered_data1, filtered_data2
+
+def exponential_func(x, a, b):
+ return a * np.exp(-b * x)
+
+def plot_max_values_scatter(data1, data2, file_path):
+ # Group data by 0.01 increments
+ bins = np.arange(0, np.nanmax(data1) + 0.001, 0.001)
+ digitized = np.digitize(data1, bins)
+
+ # Calculate the maximum of raster 2 values for each bin
+ max_values_per_bin = []
+ for i in range(1, len(bins)):
+ filtered_data2 = data2[digitized == i]
+ if filtered_data2.size > 0: # Check if the array is not empty
+ max_value = np.nanmax(filtered_data2)
+ else:
+ max_value = np.nan # Set to NaN if no data is present in the bin
+ max_values_per_bin.append(max_value)
+
+ # Compute the indices of non-NaN values for max_values_per_bin
+ non_nan_indices = ~np.isnan(max_values_per_bin)
+
+ # Use these indices to filter both bins and max_values_per_bin
+ max_values_per_bin = np.array(max_values_per_bin)[non_nan_indices]
+ # Adjust the bin values to be the center of each bin for plotting
+ bin_centers = bins[:-1] + 0.0005
+ bin_centers = bin_centers[non_nan_indices]
+
+ # Remove outliers based on a threshold (e.g., 3 standard deviations from the mean)
+ threshold = np.nanmean(max_values_per_bin) + 3 * np.nanstd(max_values_per_bin)
+ outliers_mask = max_values_per_bin <= threshold
+ max_values_per_bin = max_values_per_bin[outliers_mask]
+ bin_centers = bin_centers[outliers_mask]
+
+ # Fit an exponential decrease to the scatter plot
+ popt, pcov = curve_fit(exponential_func, bin_centers, max_values_per_bin)
+
+ # Plot
+ plt.scatter(bin_centers, max_values_per_bin, alpha=0.5, label='Data')
+ plt.plot(bin_centers, exponential_func(bin_centers, *popt), 'r-', label='Exponential Fit')
+ # Update labels to reflect the new bin size
+ plt.xlabel('Population density in [%]')
+ plt.ylabel('Friction surface in [min/km]')
+ """plt.xlim(0,0.1)
+ plt.ylim(0,0.1)"""
+ plt.legend()
+ plt.savefig(file_path, dpi=300)
+ plt.close()
+
+
+if __name__ == "__main__":
+ raster_path2 = 'friction surface.tif'
+ raster_path1 = 'ghsl.tif'
+ save_path = 'plot_grouped_scatter_with_exponential_fit.png'
+
+ # Common dimensions
+ target_width = 275
+ target_height = 254
+
+ # Read and process the raster data
+ data1 = flatten_data(read_raster_data(raster_path1, target_width, target_height))
+ data2 = flatten_data(read_raster_data(raster_path2, target_width, target_height))
+
+ # Calculate correlation
+ correlation, filtered_data1, filtered_data2 = calculate_correlation(data1, data2)
+
+ # Plot the maximum values of raster 2 for each group in raster 1
+ plot_max_values_scatter(filtered_data1, filtered_data2, save_path)
diff --git a/python-scripts/geoBoundariesAPI.py b/python-scripts/geoBoundariesAPI.py
new file mode 100644
index 0000000..fa8b730
--- /dev/null
+++ b/python-scripts/geoBoundariesAPI.py
@@ -0,0 +1,186 @@
+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
\ No newline at end of file
diff --git a/python-scripts/matplotlibBar.py b/python-scripts/matplotlibBar.py
new file mode 100644
index 0000000..5421e6c
--- /dev/null
+++ b/python-scripts/matplotlibBar.py
@@ -0,0 +1,316 @@
+from matplotlib.colors import Normalize
+import matplotlib.pyplot as plt
+from qgis.PyQt.QtCore import QCoreApplication
+from qgis.core import (QgsProcessing,
+ QgsProcessingException,
+ QgsProcessingAlgorithm,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterField,
+ QgsProcessingParameterFileDestination,
+ QgsProcessingParameterNumber,
+ QgsProcessingParameterString,
+ QgsProcessingParameterBoolean,
+ QgsProcessingParameterEnum)
+from qgis import processing
+
+import matplotlib
+matplotlib.use('Agg')
+import matplotlib.pyplot as plt
+
+class matplotlibBar(QgsProcessingAlgorithm):
+
+ INPUT = 'INPUT'
+ ATTRIBUTE_CAT = 'ATTRIBUTE_CAT'
+ ATTRIBUTE_VAL = 'ATTRIBUTE_VAL'
+ SORTING_OPTION = 'SORTING_OPTION'
+ ATTRIBUTE_COLOR = 'ATTRIBUTE_COLOR'
+ COLOR_MAP = 'COLOR_MAP'
+ SHOW_LEGEND = 'SHOW_LEGEND'
+ LEGEND_TITLE = 'LEGEND_TITLE'
+ PLOT_OUTPUT = 'PLOT_OUTPUT'
+ FIG_WIDTH = 'FIG_WIDTH'
+ FIG_HEIGHT = 'FIG_HEIGHT'
+ ALPHA = 'ALPHA'
+ COLOR = 'COLOR'
+ SHOW_GRID = 'SHOW_GRID'
+ PLOT_TITLE = 'PLOT_TITLE'
+ X_LABEL = 'X_LABEL'
+ Y_LABEL = 'Y_LABEL'
+ X_TICK_ROTATION = 'X_TICK_ROTATION'
+ X_TICK_ALIGNMENT = 'X_TICK_ALIGNMENT'
+
+ def tr(self, string):
+ return QCoreApplication.translate('Processing', string)
+
+ def createInstance(self):
+ return matplotlibBar()
+
+ def name(self):
+ return 'Bar Plot'
+
+ def displayName(self):
+ return self.tr('Bar Plot')
+
+ def group(self):
+ return self.tr('matplotlib vector')
+
+ def groupId(self):
+ return 'matplotlib vector'
+
+ def shortHelpString(self):
+ return self.tr("Generates a Bar Plot for a category attribute and a value attribute and colors them depending another attribute from an input layer.")
+
+ def initAlgorithm(self, config=None):
+ self.addParameter(QgsProcessingParameterFeatureSource(
+ self.INPUT,
+ self.tr('Input layer'),
+ [QgsProcessing.TypeVectorAnyGeometry]
+ )
+ )
+ self.addParameter(QgsProcessingParameterField(
+ self.ATTRIBUTE_CAT,
+ self.tr('Category Attribute'),
+ None,
+ self.INPUT,
+ QgsProcessingParameterField.Any
+ )
+ )
+ self.addParameter(QgsProcessingParameterField(
+ self.ATTRIBUTE_VAL,
+ self.tr('Value Attribute'),
+ None,
+ self.INPUT,
+ QgsProcessingParameterField.Numeric
+ )
+ )
+
+ sorting_options = [
+ ('NO_SORTING', ('No Sorting')),
+ ('ASCENDING', ('Ascending')),
+ ('DESCENDING', ('Descending'))
+ ]
+
+ self.addParameter(QgsProcessingParameterEnum(
+ self.SORTING_OPTION,
+ self.tr('Sorting Option'),
+ options=[option[1] for option in sorting_options],
+ defaultValue=0,
+ allowMultiple=False
+ )
+ )
+
+ self.addParameter(QgsProcessingParameterFileDestination(
+ 'PLOT_OUTPUT',
+ self.tr('Plot Output File'),
+ 'PNG Files (*.png)'
+ )
+ )
+ self.addParameter(QgsProcessingParameterNumber(
+ self.FIG_WIDTH,
+ self.tr('Figure Width'),
+ QgsProcessingParameterNumber.Double,
+ 10
+ )
+ )
+ self.addParameter(QgsProcessingParameterNumber(
+ self.FIG_HEIGHT,
+ self.tr('Figure Height'),
+ QgsProcessingParameterNumber.Double,
+ 6
+ )
+ )
+ self.addParameter(QgsProcessingParameterNumber(
+ self.ALPHA,
+ self.tr('Alpha Transparency'),
+ QgsProcessingParameterNumber.Double,
+ 0.7
+ )
+ )
+ self.addParameter(QgsProcessingParameterString(
+ self.COLOR,
+ self.tr('Color'),
+ defaultValue='blue'
+ )
+ )
+ self.addParameter(QgsProcessingParameterBoolean(
+ self.SHOW_GRID,
+ self.tr('Show Grid'),
+ defaultValue=True
+ )
+ )
+ self.addParameter(QgsProcessingParameterString(
+ self.PLOT_TITLE,
+ self.tr('Plot Title'),
+ defaultValue=' '
+ )
+ )
+ self.addParameter(QgsProcessingParameterString(
+ self.X_LABEL,
+ self.tr('X-axis Label (Categories)'),
+ defaultValue=' '
+ )
+ )
+ self.addParameter(QgsProcessingParameterString(
+ self.Y_LABEL,
+ self.tr('Y-axis Label (Values)'),
+ defaultValue=' '
+ )
+ )
+ self.addParameter(QgsProcessingParameterNumber(
+ self.X_TICK_ROTATION,
+ self.tr('X-axis Tick Label Rotation'),
+ QgsProcessingParameterNumber.Integer,
+ defaultValue=45
+ )
+ )
+ self.addParameter(QgsProcessingParameterString(
+ self.X_TICK_ALIGNMENT,
+ self.tr('X-axis Tick Label Alignment'),
+ defaultValue='right'
+ )
+ )
+ self.addParameter(QgsProcessingParameterField(
+ self.ATTRIBUTE_COLOR,
+ self.tr('Color Attribute'),
+ None,
+ self.INPUT,
+ QgsProcessingParameterField.Any,
+ optional=True
+ )
+ )
+
+ self.addParameter(QgsProcessingParameterString(
+ self.COLOR_MAP,
+ self.tr('Color Map'),
+ defaultValue='viridis',
+ )
+ )
+
+ self.addParameter(QgsProcessingParameterBoolean(
+ 'SHOW_LEGEND',
+ self.tr('Show Legend'),
+ defaultValue=True,
+ )
+ )
+
+ self.addParameter(QgsProcessingParameterString(
+ self.LEGEND_TITLE,
+ self.tr('Legend Title'),
+ optional=True,
+ defaultValue=' '
+ )
+ )
+
+ def processAlgorithm(self, parameters, context, feedback):
+ source = self.parameterAsSource(parameters, self.INPUT, context)
+
+ cat_attribute_name = self.parameterAsString(
+ parameters, self.ATTRIBUTE_CAT, context)
+ val_attribute_name = self.parameterAsString(
+ parameters, self.ATTRIBUTE_VAL, context)
+ color_attribute_name = self.parameterAsString(
+ parameters, self.ATTRIBUTE_COLOR, context)
+ color_map_name = self.parameterAsString(
+ parameters, self.COLOR_MAP, context)
+ plot_output_path = self.parameterAsFileOutput(
+ parameters, 'PLOT_OUTPUT', context)
+ fig_width = self.parameterAsDouble(parameters, self.FIG_WIDTH, context)
+ fig_height = self.parameterAsDouble(
+ parameters, self.FIG_HEIGHT, context)
+ alpha = self.parameterAsDouble(parameters, self.ALPHA, context)
+ color = self.parameterAsString(parameters, self.COLOR, context)
+ show_grid = self.parameterAsBool(parameters, self.SHOW_GRID, context)
+ plot_title = self.parameterAsString(
+ parameters, self.PLOT_TITLE, context)
+ x_label = self.parameterAsString(parameters, self.X_LABEL, context)
+ y_label = self.parameterAsString(parameters, self.Y_LABEL, context)
+ x_tick_rotation = self.parameterAsInt(
+ parameters, self.X_TICK_ROTATION, context)
+ x_tick_alignment = self.parameterAsString(
+ parameters, self.X_TICK_ALIGNMENT, context)
+ show_legend = self.parameterAsBool(parameters, self.SHOW_LEGEND, context)
+ legend_title = self.parameterAsString(
+ parameters, self.LEGEND_TITLE, context)
+ sorting_option = self.parameterAsEnum(
+ parameters, self.SORTING_OPTION, context)
+
+ categories, values, color_values = [], [], []
+
+ for feature in source.getFeatures():
+ cat_value = feature[cat_attribute_name]
+ val_value = feature[val_attribute_name]
+
+ if cat_value is not None and val_value is not None:
+ categories.append(str(cat_value))
+ values.append(float(val_value))
+
+ if color_attribute_name:
+ color_value = feature[color_attribute_name] if feature[color_attribute_name] is not None else "Default"
+ color_values.append(color_value)
+
+ if sorting_option == 1:
+ sorted_indices = sorted(
+ range(len(values)), key=lambda i: values[i])
+ elif sorting_option == 2:
+ sorted_indices = sorted(
+ range(len(values)), key=lambda i: values[i], reverse=True)
+ else:
+ sorted_indices = range(len(values))
+
+ sorted_categories = [categories[i] for i in sorted_indices]
+ sorted_values = [values[i] for i in sorted_indices]
+
+ if color_attribute_name:
+ color_values = [color_values[i] for i in sorted_indices]
+
+ plt.figure(figsize=(fig_width, fig_height))
+
+ if color_attribute_name:
+ unique_colors = list(set(color_values))
+ colormap = plt.cm.get_cmap(color_map_name, len(unique_colors))
+ norm = Normalize(vmin=0, vmax=len(unique_colors)-1)
+ color_map = {color: colormap(norm(i))
+ for i, color in enumerate(unique_colors)}
+ bar_colors = [color_map[value] for value in color_values]
+
+ for i, (cat, val) in enumerate(zip(sorted_categories, sorted_values)):
+ plt.bar(cat, val, color=bar_colors[i], label=color_values[i]
+ if i == 0 or color_values[i] != color_values[i-1] else "")
+ if show_legend:
+ handles, labels = plt.gca().get_legend_handles_labels()
+ by_label = dict(zip(labels, handles))
+ plt.legend(by_label.values(), by_label.keys(), title=legend_title)
+
+ else:
+ plt.bar(sorted_categories, sorted_values, color=color, alpha=alpha)
+
+ if color_attribute_name and len(color_values) != len(categories):
+ raise QgsProcessingException(
+ self.tr('Mismatch in the number of categories and color values.'))
+
+ if not categories or not values:
+ raise QgsProcessingException(
+ self.tr("No valid data found. Please check the selected attributes."))
+
+ if not legend_title.strip():
+ legend_title = color_attribute_name if color_attribute_name else 'Legend'
+
+ plt.title(plot_title if plot_title else 'Value Distribution by Category')
+ plt.xlabel(x_label if x_label else 'Category')
+ plt.ylabel(y_label if y_label else 'Value')
+
+ if show_grid:
+ plt.grid(True)
+ else:
+ plt.grid(False)
+
+ plt.xticks(rotation=x_tick_rotation, ha=x_tick_alignment)
+ plt.tight_layout()
+
+ try:
+ plt.savefig(plot_output_path)
+ plt.close()
+ return {}
+ except Exception as e:
+ feedback.reportError(str(e))
+ return {}
diff --git a/python-scripts/matplotlibHist.py b/python-scripts/matplotlibHist.py
new file mode 100644
index 0000000..48573af
--- /dev/null
+++ b/python-scripts/matplotlibHist.py
@@ -0,0 +1,113 @@
+from qgis.PyQt.QtCore import QCoreApplication
+from qgis.core import (QgsProcessing,
+ QgsFeatureSink,
+ QgsProcessingException,
+ QgsProcessingAlgorithm,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterFeatureSink,
+ QgsProcessingParameterField,
+ QgsProcessingParameterFileDestination,
+ QgsProcessingParameterNumber,
+ QgsProcessingParameterString,
+ QgsProcessingParameterBoolean)
+from qgis import processing
+
+import matplotlib
+matplotlib.use('Agg')
+import matplotlib.pyplot as plt
+
+class matplotlibHist(QgsProcessingAlgorithm):
+
+ INPUT = 'INPUT'
+ ATTRIBUTE = 'ATTRIBUTE'
+ MAX_VALUE = 'MAX_VALUE'
+ PLOT_OUTPUT = 'PLOT_OUTPUT'
+ FIG_WIDTH = 'FIG_WIDTH'
+ FIG_HEIGHT = 'FIG_HEIGHT'
+ BINS = 'BINS'
+ ALPHA = 'ALPHA'
+ COLOR = 'COLOR'
+ SHOW_GRID = 'SHOW_GRID'
+ PLOT_TITLE = 'PLOT_TITLE'
+ X_LABEL = 'X_LABEL'
+ Y_LABEL = 'Y_LABEL'
+
+ def tr(self, string):
+ return QCoreApplication.translate('Processing', string)
+
+ def createInstance(self):
+ return matplotlibHist()
+
+ def name(self):
+ return 'Histogram'
+
+ def displayName(self):
+ return self.tr('Histogram')
+
+ def group(self):
+ return self.tr('matplotlib vector')
+
+ def groupId(self):
+ return 'matplotlib vector'
+
+ def shortHelpString(self):
+ return self.tr("Generates a histogram plot for a selected numeric attribute from an input layer.")
+
+ def initAlgorithm(self, config=None):
+ self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,self.tr('Input layer'),[QgsProcessing.TypeVectorAnyGeometry]))
+ self.addParameter(QgsProcessingParameterField(self.ATTRIBUTE,self.tr('Attribute'),None,self.INPUT,QgsProcessingParameterField.Any))
+ self.addParameter(QgsProcessingParameterNumber(self.MAX_VALUE, self.tr('Maximum Value'), QgsProcessingParameterNumber.Double, None, optional=True))
+ self.addParameter(QgsProcessingParameterFileDestination('PLOT_OUTPUT',self.tr('Plot Output File'),'PNG Files (*.png)'))
+ self.addParameter(QgsProcessingParameterNumber(self.FIG_WIDTH,self.tr('Figure Width'), QgsProcessingParameterNumber.Double, 10))
+ self.addParameter(QgsProcessingParameterNumber(self.FIG_HEIGHT,self.tr('Figure Height'), QgsProcessingParameterNumber.Double, 6))
+ self.addParameter(QgsProcessingParameterNumber(self.BINS, self.tr('Number of Bins'), QgsProcessingParameterNumber.Integer, 30 ))
+ self.addParameter(QgsProcessingParameterNumber(self.ALPHA,self.tr('Alpha Transparency'), QgsProcessingParameterNumber.Double, 0.7))
+ self.addParameter(QgsProcessingParameterString(self.COLOR,self.tr('Color'), defaultValue='blue'))
+ self.addParameter(QgsProcessingParameterBoolean(self.SHOW_GRID,self.tr('Show Grid'), defaultValue=True))
+ self.addParameter(QgsProcessingParameterString(self.PLOT_TITLE, self.tr('Plot Title'), defaultValue=' '))
+ self.addParameter(QgsProcessingParameterString(self.X_LABEL, self.tr('X-axis Label'), defaultValue=' '))
+ self.addParameter(QgsProcessingParameterString(self.Y_LABEL, self.tr('Y-axis Label'), defaultValue='Frequency'))
+
+ def processAlgorithm(self, parameters, context, feedback):
+ source = self.parameterAsSource(parameters, self.INPUT, context)
+
+ attribute_name = self.parameterAsString(parameters, self.ATTRIBUTE, context)
+
+ attribute_values = []
+
+ max_value = self.parameterAsDouble(parameters, self.MAX_VALUE, context) if parameters[self.MAX_VALUE] is not None else max(attribute_values)
+ plot_output_path = self.parameterAsFileOutput(parameters, 'PLOT_OUTPUT', context)
+ fig_width = self.parameterAsDouble(parameters, self.FIG_WIDTH, context)
+ fig_height = self.parameterAsDouble(parameters, self.FIG_HEIGHT, context)
+ bins = self.parameterAsInt(parameters, self.BINS, context)
+ alpha = self.parameterAsDouble(parameters, self.ALPHA, context)
+ color = self.parameterAsString(parameters, self.COLOR, context)
+ show_grid = self.parameterAsBool(parameters, self.SHOW_GRID, context)
+ plot_title = self.parameterAsString(parameters, self.PLOT_TITLE, context)
+ x_label = self.parameterAsString(parameters, self.X_LABEL, context)
+ y_label = self.parameterAsString(parameters, self.Y_LABEL, context)
+
+ for feature in source.getFeatures():
+ attribute_value = feature[attribute_name]
+ if attribute_value is not None:
+ attribute_values.append(attribute_value)
+
+ if not attribute_values:
+ raise QgsProcessingException(self.tr("No attribute values found. Please check the selected attribute."))
+
+ try:
+ plt.figure(figsize=(fig_width,fig_height))
+ plt.hist(attribute_values, bins=bins,alpha=alpha, color=color, range=(min(attribute_values), max_value))
+ plt.title(plot_title if plot_title else 'Value Distribution by Category')
+ plt.xlabel(x_label if x_label else ' ')
+ plt.ylabel(y_label if y_label else 'Frequence')
+ if show_grid:
+ plt.grid(True)
+ else:
+ plt.grid(False)
+ plt.savefig(plot_output_path)
+ plt.close()
+ return {}
+ except Exception as e:
+ feedback.reportError(str(e))
+ return {}
\ No newline at end of file
diff --git a/python-scripts/matplotlibLin.py b/python-scripts/matplotlibLin.py
new file mode 100644
index 0000000..dd17c79
--- /dev/null
+++ b/python-scripts/matplotlibLin.py
@@ -0,0 +1,256 @@
+import math
+import matplotlib.pyplot as plt
+from qgis.PyQt.QtCore import QCoreApplication
+from qgis.core import (QgsProcessing,
+ QgsProcessingAlgorithm,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterFileDestination,
+ QgsProcessingParameterField,
+ QgsProcessingParameterString,
+ QgsProcessingParameterNumber,
+ QgsProcessingParameterBoolean)
+
+import numpy as np
+from scipy.optimize import curve_fit
+import matplotlib
+matplotlib.use('Agg')
+
+
+class matplotlibExp(QgsProcessingAlgorithm):
+
+ INPUT = 'INPUT'
+ OUTPUT = 'OUTPUT'
+ X_VALUES = 'X_VALUES'
+ Y_VALUES = 'Y_VALUES'
+ X_MAX = 'X_MAX'
+ Y_MAX = 'Y_MAX'
+ B = 'B'
+ COLOR = 'COLOR'
+ TITLE = 'TITLE'
+ X_LABEL = 'X_LABEL'
+ Y_LABEL = 'Y_LABEL'
+
+ def tr(self, string):
+ return QCoreApplication.translate('Processing', string)
+
+ def createInstance(self):
+ return matplotlibExp()
+
+ def name(self):
+ return 'Exponential fit'
+
+ def displayName(self):
+ return self.tr('Exponential fit')
+
+ def group(self):
+ return self.tr('matplotlib vector')
+
+ def groupId(self):
+ return 'matplotlib vector'
+
+ def shortHelpString(self):
+ return self.tr("Generates a exponential fit for two selected numeric attributes from an input layer.")
+
+ def initAlgorithm(self, config=None):
+
+ self.addParameter(
+ QgsProcessingParameterFeatureSource(
+ self.INPUT,
+ self.tr('Input layer'),
+ [QgsProcessing.TypeVectorAnyGeometry]
+ )
+ )
+
+ self.addParameter(QgsProcessingParameterFileDestination(
+ 'OUTPUT',
+ self.tr('Plot Output File'),
+ 'PNG Files (*.png)'
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterField(
+ self.X_VALUES,
+ self.tr('X Values'),
+ None,
+ self.INPUT,
+ QgsProcessingParameterField.Numeric
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterField(
+ self.Y_VALUES,
+ self.tr('Y Values'),
+ None,
+ self.INPUT,
+ QgsProcessingParameterField.Numeric
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterNumber(
+ self.X_MAX,
+ self.tr('X Max'),
+ type=QgsProcessingParameterNumber.Double,
+ optional=True
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterNumber(
+ self.Y_MAX,
+ self.tr('Y Max'),
+ type=QgsProcessingParameterNumber.Double,
+ optional=True
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterNumber(
+ self.B,
+ self.tr('b'),
+ type = QgsProcessingParameterNumber.Double,
+ optional=True
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.COLOR,
+ self.tr('Color'),
+ defaultValue='blue'
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.TITLE,
+ self.tr('Title'),
+ defaultValue=' '
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.X_LABEL,
+ self.tr('X Label'),
+ defaultValue=' '
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.Y_LABEL,
+ self.tr('Y Label'),
+ defaultValue=' '
+ )
+ )
+
+ @staticmethod
+ def exponential_decay(x, a, b, c):
+ return a * np.exp(b * x) + c
+
+ @staticmethod
+ def exponential_decay_no_offset(x,a,b):
+ return a * np.exp(b * x)
+
+ def processAlgorithm(self, parameters, context, feedback):
+
+ source = self.parameterAsSource(parameters, self.INPUT, context)
+ x_field = self.parameterAsString(parameters, self.X_VALUES, context)
+ y_field = self.parameterAsString(parameters, self.Y_VALUES, context)
+ x_max = self.parameterAsDouble(parameters, self.X_MAX, context)
+ y_max = self.parameterAsDouble(parameters, self.Y_MAX, context)
+ b = self.parameterAsDouble(parameters, self.B, context)
+ color = self.parameterAsString(parameters, self.COLOR, context)
+ title = self.parameterAsString(parameters, self.TITLE, context)
+ x_label = self.parameterAsString(parameters, self.X_LABEL, context)
+ y_label = self.parameterAsString(parameters, self.Y_LABEL, context)
+ output = self.parameterAsFileOutput(parameters, self.OUTPUT, context)
+
+ feedback.pushInfo(f"Source: {source}")
+ feedback.pushInfo(f"x_field: {x_field}")
+ feedback.pushInfo(f"y_field: {y_field}")
+ feedback.pushInfo(f"x_max: {x_max}")
+ feedback.pushInfo(f"y_max: {y_max}")
+ feedback.pushInfo(f"title: {title}")
+ feedback.pushInfo(f"x_label: {x_label}")
+ feedback.pushInfo(f"y_label: {y_label}")
+ feedback.pushInfo(f"output: {output}")
+
+ x_values = []
+ y_values = []
+
+ for feature in source.getFeatures():
+ try:
+ x = float(feature[x_field])
+ y = float(feature[y_field])
+ if not (math.isnan(x) or math.isnan(y)):
+ x_values.append(x)
+ y_values.append(y)
+ except Exception as e:
+ continue
+
+ feedback.pushInfo(f"x_values: {x_values}")
+ feedback.pushInfo(f"y_values: {y_values}")
+
+ grouped_data = {}
+
+ for x, y in zip(x_values, y_values):
+ if x not in grouped_data:
+ grouped_data[x] = [y]
+ else:
+ grouped_data[x].append(y)
+
+ max_y_values = {x: max(y_list) for x, y_list in grouped_data.items()}
+
+ x_values_np = np.array(sorted(max_y_values.keys()))
+ y_values_np = np.array([max_y_values[x] for x in x_values_np])
+
+ positive_filter = y_values_np > 0
+
+ x_values_positive = x_values_np[positive_filter]
+ y_values_positive = y_values_np[positive_filter]
+
+ if np.any(y_values_positive.min() <= 0):
+ feedback.reportError("Negative values in y field")
+ return {}
+
+ initial_guess = [np.max(y_values_positive), b] # Assuming a starts at the max values, b is negative, c is zero
+
+ try:
+ popt, pcov = curve_fit(
+ matplotlibExp.exponential_decay_no_offset,
+ x_values_positive,
+ y_values_positive,
+ p0=initial_guess,
+ )
+ a_fit, b_fit = popt
+ x_axis = np.linspace(x_values_positive.min(), x_values_positive.max(),500)
+ y_fit = matplotlibExp.exponential_decay_no_offset(x_axis, a_fit, b_fit)
+
+ feedback.pushInfo(f"Inital guess: {initial_guess}")
+ feedback.pushInfo(f"Fit parameters: {popt}")
+
+
+ except RuntimeError as e:
+ feedback.reportError(f"Error during curve fitting: {e}")
+ return{}
+
+ try:
+ plt.scatter(x_values_positive, y_values_positive)
+ plt.plot(x_axis, y_fit, color=color, label='Fit Line')
+ plt.xlim(x_values_positive.min(), x_max if x_max is not None else x_values_positive.max())
+ plt.ylim(y_values_positive.min(), y_max if y_max is not None else y_values_positive.max())
+ plt.title(title)
+ plt.xlabel(x_label)
+ plt.ylabel(y_label)
+ plt.legend()
+ plt.savefig(output)
+ plt.close()
+ feedback.pushInfo(f"Scatter plot saved to {output}")
+ return {self.OUTPUT: output}
+ except Exception as e:
+ feedback.reportError(f"Error: {e}")
+ return {}
diff --git a/python-scripts/matplotlibScat.py b/python-scripts/matplotlibScat.py
new file mode 100644
index 0000000..02343e3
--- /dev/null
+++ b/python-scripts/matplotlibScat.py
@@ -0,0 +1,216 @@
+from qgis.PyQt.QtCore import QCoreApplication
+from qgis.core import (QgsProcessing,
+ QgsProcessingAlgorithm,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterFileDestination,
+ QgsProcessingParameterField,
+ QgsProcessingParameterString,
+ QgsProcessingParameterNumber,
+ QgsProcessingParameterBoolean)
+
+import matplotlib
+matplotlib.use('Agg')
+import matplotlib.pyplot as plt
+import math
+
+class matplotlibScat(QgsProcessingAlgorithm):
+
+ INPUT = 'INPUT'
+ OUTPUT = 'OUTPUT'
+ X_VALUES = 'X_VALUES'
+ Y_VALUES = 'Y_VALUES'
+ X_MAX = 'X_MAX'
+ Y_MAX = 'Y_MAX'
+ FLIP_X = 'FLIP_X'
+ FLIP_Y = 'FLIP_Y'
+ COLOR = 'COLOR'
+ TITLE = 'TITLE'
+ X_LABEL = 'X_LABEL'
+ Y_LABEL = 'Y_LABEL'
+
+
+ def tr(self, string):
+ return QCoreApplication.translate('Processing', string)
+
+ def createInstance(self):
+ return matplotlibScat()
+
+ def name(self):
+ return 'Scatter Plot'
+
+ def displayName(self):
+ return self.tr('Scatter Plot')
+
+ def group(self):
+ return self.tr('matplotlib vector')
+
+ def groupId(self):
+ return 'matplotlib vector'
+
+ def shortHelpString(self):
+ return self.tr("Generates a scatter plot for two selected numeric attributes from an input layer.")
+
+ def initAlgorithm(self, config=None):
+
+ self.addParameter(
+ QgsProcessingParameterFeatureSource(
+ self.INPUT,
+ self.tr('Input layer'),
+ [QgsProcessing.TypeVectorAnyGeometry]
+ )
+ )
+
+ self.addParameter(QgsProcessingParameterFileDestination(
+ 'OUTPUT',
+ self.tr('Plot Output File'),
+ 'PNG Files (*.png)'
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterField(
+ self.X_VALUES,
+ self.tr('X Values'),
+ None,
+ self.INPUT,
+ QgsProcessingParameterField.Numeric
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterField(
+ self.Y_VALUES,
+ self.tr('Y Values'),
+ None,
+ self.INPUT,
+ QgsProcessingParameterField.Numeric
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterNumber(
+ self.X_MAX,
+ self.tr('X Max'),
+ type=QgsProcessingParameterNumber.Double,
+ optional=True
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterNumber(
+ self.Y_MAX,
+ self.tr('Y Max'),
+ type=QgsProcessingParameterNumber.Double,
+ optional=True
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterBoolean(
+ self.FLIP_X,
+ self.tr('Flip X Axis'),
+ defaultValue=False
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterBoolean(
+ self.FLIP_Y,
+ self.tr('Flip Y Axis'),
+ defaultValue=False
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.COLOR,
+ self.tr('Color'),
+ defaultValue='blue'
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.TITLE,
+ self.tr('Title'),
+ defaultValue=' '
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.X_LABEL,
+ self.tr('X Label'),
+ defaultValue=' '
+ )
+ )
+
+ self.addParameter(
+ QgsProcessingParameterString(
+ self.Y_LABEL,
+ self.tr('Y Label'),
+ defaultValue=' '
+ )
+ )
+
+ def processAlgorithm(self, parameters, context, feedback):
+
+ source = self.parameterAsSource(parameters, self.INPUT, context)
+ x_field = self.parameterAsString(parameters, self.X_VALUES, context)
+ y_field = self.parameterAsString(parameters, self.Y_VALUES, context)
+ x_max = self.parameterAsDouble(parameters, self.X_MAX, context)
+ y_max = self.parameterAsDouble(parameters, self.Y_MAX, context)
+ flip_x = self.parameterAsBool(parameters, self.FLIP_X, context)
+ flip_y = self.parameterAsBool(parameters, self.FLIP_Y, context)
+ color = self.parameterAsString(parameters, self.COLOR, context)
+ title = self.parameterAsString(parameters, self.TITLE, context)
+ x_label = self.parameterAsString(parameters, self.X_LABEL, context)
+ y_label = self.parameterAsString(parameters, self.Y_LABEL, context)
+ output = self.parameterAsFileOutput(parameters, self.OUTPUT, context)
+
+ feedback.pushInfo(f"Source: {source}")
+ feedback.pushInfo(f"x_field: {x_field}")
+ feedback.pushInfo(f"y_field: {y_field}")
+ feedback.pushInfo(f"x_max: {x_max}")
+ feedback.pushInfo(f"y_max: {y_max}")
+ feedback.pushInfo(f"flip_x: {flip_x}")
+ feedback.pushInfo(f"flip_y: {flip_y}")
+ feedback.pushInfo(f"title: {title}")
+ feedback.pushInfo(f"x_label: {x_label}")
+ feedback.pushInfo(f"y_label: {y_label}")
+ feedback.pushInfo(f"output: {output}")
+
+ x_values = []
+ y_values = []
+
+ for feature in source.getFeatures():
+ try:
+ x = float(feature[x_field])
+ y = float(feature[y_field])
+ if not (math.isnan(x) or math.isnan(y)):
+ x_values.append(x)
+ y_values.append(y)
+ except Exception as e:
+ continue
+
+ feedback.pushInfo(f"x_values: {x_values}")
+ feedback.pushInfo(f"y_values: {y_values}")
+
+ try:
+ plt.scatter(x_values, y_values, color=color)
+ plt.xlim(0, x_max)
+ plt.ylim(0, y_max)
+ if flip_x:
+ plt.gca().invert_xaxis()
+ if flip_y:
+ plt.gca().invert_yaxis()
+ plt.title(title)
+ plt.xlabel(x_label)
+ plt.ylabel(y_label)
+ plt.savefig(output)
+ plt.close()
+ feedback.pushInfo(f"Scatter plot saved to {output}")
+ return {self.OUTPUT: output}
+ except Exception as e:
+ feedback.reportError(f"Error: {e}")
+ return {}
\ No newline at end of file
diff --git a/python-scripts/normal.py b/python-scripts/normal.py
new file mode 100644
index 0000000..4562244
--- /dev/null
+++ b/python-scripts/normal.py
@@ -0,0 +1,39 @@
+import rasterio
+import numpy as np
+import matplotlib.pyplot as plt
+from scipy.stats import norm
+
+def read_raster_data(raster_path):
+ with rasterio.open(raster_path) as raster:
+ data = raster.read(1, masked=True)
+ if raster.nodata is not None:
+ data = data.filled(np.nan)
+ return data
+
+def flatten_data(data):
+ return data.flatten()
+
+# Replace 'traveltime.tif' with the path to your actual raster file
+x_axis = flatten_data(read_raster_data('traveltime.tif'))
+
+# Filter out NaN values
+x_axis = x_axis[~np.isnan(x_axis)]
+
+mean = np.mean(x_axis)
+sd = np.std(x_axis)
+
+# Plot histogram
+plt.hist(x_axis, bins=50, density=True, alpha=0.6, color='g', range=(mean-3*sd, mean+3*sd))
+xmin, xmax = plt.xlim()
+plt.xlim(0,5)
+plt.xlabel("Travel time in [h]")
+plt.ylabel("Quantaty in percentage")
+x = np.linspace(xmin, xmax, 100)
+
+
+# Calculate and plot the 0.85 quantile
+quantile_90 = np.quantile(x_axis, 0.90)
+plt.axvline(x=quantile_90, color='r', linestyle='--', label='90%')
+plt.legend()
+
+plt.savefig("traveltimedistribution.png", dpi=300) # Save the plot
diff --git a/python-scripts/removenullbytes.py b/python-scripts/removenullbytes.py
new file mode 100644
index 0000000..a8ce5be
--- /dev/null
+++ b/python-scripts/removenullbytes.py
@@ -0,0 +1,26 @@
+import sys
+
+
+def clean_file(input_path):
+ try:
+ output_path = input_path.replace('.', '_clean.')
+ with open(input_path, 'r', encoding='utf-8', errors='ignore') as file:
+ file_content = file.read()
+
+ clean_content = file_content.replace('\x00', '')
+
+ with open(output_path, 'w', encoding='utf-8') as clean_file:
+ clean_file.write(clean_content)
+ print('Null bytes removed from file:', input_path)
+ except Exception as e:
+ print(f'Error processing file {input_path}: {e}')
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ print('Usage: python removenullbytes.py ')
+ sys.exit(1)
+ else:
+ input_path = sys.argv[1]
+ clean_file(input_path)
diff --git a/qgis-models/Analysis.model3 b/qgis-models/Analysis.model3
new file mode 100644
index 0000000..bb393ab
--- /dev/null
+++ b/qgis-models/Analysis.model3
@@ -0,0 +1,2838 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qgis-models/facilities.model3 b/qgis-models/facilities.model3
new file mode 100644
index 0000000..36b0b54
--- /dev/null
+++ b/qgis-models/facilities.model3
@@ -0,0 +1,3137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qgis-models/friction.model3 b/qgis-models/friction.model3
new file mode 100644
index 0000000..1eec0e8
--- /dev/null
+++ b/qgis-models/friction.model3
@@ -0,0 +1,500 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qgis-models/ghsl.model3 b/qgis-models/ghsl.model3
new file mode 100644
index 0000000..af0c09b
--- /dev/null
+++ b/qgis-models/ghsl.model3
@@ -0,0 +1,695 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qgis-models/mapbiomas.model3 b/qgis-models/mapbiomas.model3
new file mode 100644
index 0000000..03e5381
--- /dev/null
+++ b/qgis-models/mapbiomas.model3
@@ -0,0 +1,445 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qgis-models/region.model3 b/qgis-models/region.model3
new file mode 100644
index 0000000..27d6ade
--- /dev/null
+++ b/qgis-models/region.model3
@@ -0,0 +1,3086 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qgis-models/roads.model3 b/qgis-models/roads.model3
new file mode 100644
index 0000000..929b8c7
--- /dev/null
+++ b/qgis-models/roads.model3
@@ -0,0 +1,747 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qgis-models/traveltime.model3 b/qgis-models/traveltime.model3
new file mode 100644
index 0000000..4ced480
--- /dev/null
+++ b/qgis-models/traveltime.model3
@@ -0,0 +1,475 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+