Add files via upload
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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 {}
|
||||
@@ -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 {}
|
||||
@@ -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 {}
|
||||
@@ -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 {}
|
||||
@@ -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
|
||||
@@ -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 <file_path>')
|
||||
sys.exit(1)
|
||||
else:
|
||||
input_path = sys.argv[1]
|
||||
clean_file(input_path)
|
||||
Reference in New Issue
Block a user