mtl_gis_oo/scrub_layer_class.py

219 lines
7.9 KiB
Python
Raw Normal View History

2024-04-25 11:13:46 -04:00
"""
2024-08-05 17:55:55 -04:00
scrub_layer_class module
PyQGIS functionalities that are needed in the cleaning and updating
Montreal Buildings dataset project, gathered in one class.
Project Developer: Alireza Adli alireza.adli@concordia.ca
2024-04-25 11:13:46 -04:00
"""
2024-03-15 16:43:27 -04:00
from qgis.core import QgsApplication, QgsField, QgsProject, \
QgsProcessingFeedback, QgsVectorLayer, QgsVectorDataProvider, \
QgsExpressionContext, QgsExpressionContextUtils, edit, QgsFeatureRequest, \
QgsExpression
2024-03-26 16:04:43 -04:00
from qgis.PyQt.QtCore import QVariant
2024-03-15 12:08:52 -04:00
from qgis.analysis import QgsNativeAlgorithms
from basic_functions import *
2024-03-15 16:43:27 -04:00
import processing
2024-03-15 11:26:23 -04:00
class ScrubLayer:
2024-03-15 12:08:52 -04:00
def __init__(self, qgis_path, layer_path, layer_name):
self.qgis_path = qgis_path
2024-03-15 12:08:52 -04:00
# Set the path to QGIS installation
QgsApplication.setPrefixPath(self.qgis_path, True)
2024-03-15 12:08:52 -04:00
2024-03-15 11:26:23 -04:00
self.layer_path = layer_path
self.layer_name = layer_name
2024-03-15 12:08:52 -04:00
self.layer = self.load_layer()
2024-03-16 16:33:47 -04:00
self.data_count = self.layer.featureCount()
2024-03-15 11:26:23 -04:00
2024-08-16 09:58:22 -04:00
def get_cell(self, fid, field_name):
2024-08-15 19:10:44 -04:00
return self.layer.getFeature(fid)[field_name]
2024-08-16 09:58:56 -04:00
def select_cells(
self,
2024-08-16 09:58:22 -04:00
field_name, field_value, required_field,
return_one_value=False):
2024-08-16 09:58:22 -04:00
"""Returns the value of a field
based on the value of another field in the same record"""
expression = QgsExpression(f'{field_name} = {field_value}')
request = QgsFeatureRequest(expression)
features = self.layer.getFeatures(request)
field_field_values = []
for feature in features:
2024-08-16 09:58:22 -04:00
field_field_values.append(feature[required_field])
if return_one_value and field_field_values:
return field_field_values[0]
return field_field_values
2024-03-15 12:08:52 -04:00
def load_layer(self):
the_layer = QgsVectorLayer(self.layer_path, self.layer_name, 'ogr')
2024-03-15 11:26:23 -04:00
if not the_layer.isValid():
2024-03-15 12:08:52 -04:00
raise ValueError(f'Failed to load layer {self.layer_name} from {self.layer_path}')
2024-03-15 11:26:23 -04:00
else:
2024-03-15 12:08:52 -04:00
QgsProject.instance().addMapLayer(the_layer)
return the_layer
def fix_geometries(self, fixed_layer):
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
fix_geometries_params = {
'INPUT': self.layer,
'METHOD': 0,
'OUTPUT': fixed_layer
}
processing.run("native:fixgeometries", fix_geometries_params)
2024-03-15 16:43:27 -04:00
def create_spatial_index(self):
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
create_spatial_index_params = {
'INPUT': self.layer,
2024-03-15 16:43:27 -04:00
'OUTPUT': 'Output'
}
processing.run("native:createspatialindex", create_spatial_index_params)
2024-03-26 16:04:43 -04:00
print(f'Creating Spatial index for {self.layer_name} is completed.')
def spatial_join(self, joining_layer_path, joined_layer_path):
"""In QGIS, it is called 'Join attributes by Location'"""
params = {'INPUT': self.layer,
'PREDICATE': [0],
'JOIN': joining_layer_path,
'JOIN_FIELDS': [],
'METHOD': 0,
'DISCARD_NONMATCHING': False,
'PREFIX': '',
'OUTPUT': joined_layer_path}
feedback = QgsProcessingFeedback()
processing.run('native:joinattributesbylocation', params, feedback=feedback)
print(f'Spatial Join with input layer {self.layer_name} is completed.')
def clip_layer(self, overlay_layer, clipped_layer):
2024-03-26 16:04:43 -04:00
"""This must be tested"""
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
clip_layer_params = {
'INPUT': self.layer_path,
'OVERLAY': overlay_layer,
'FILTER_EXPRESSION': '',
'FILTER_EXTENT': None,
'OUTPUT': clipped_layer
}
processing.run("native:clip", clip_layer_params)
print(f'Clipping of {self.layer_name} is completed.')
2024-03-15 16:43:27 -04:00
def clip_by_multiple(self, number_of_partitions, overlay_layers_dir, clipped_layers_dir):
create_folders(clipped_layers_dir, number_of_partitions)
for layer in range(number_of_partitions):
overlay = overlay_layers_dir + f'/layer_{layer}/layer_{layer}.shp'
clipped = clipped_layers_dir + f'/layer_{layer}/layer_{layer}.shp'
self.clip_layer(overlay, clipped)
clipped_layer = ScrubLayer(self.qgis_path, clipped, 'Temp Layer')
clipped_layer.create_spatial_index()
def split_layer(self, number_of_layers, splitted_layers_dir):
2024-03-26 19:21:15 -04:00
number_of_layers -= 1
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
create_folders(splitted_layers_dir, number_of_layers)
intervals = self.data_count // number_of_layers
for part in range(number_of_layers):
output_layer_path = \
splitted_layers_dir + f'/layer_{part}/layer_{part}.shp'
params = {'INPUT': self.layer,
'EXPRESSION': f'$id >= {part * intervals} '
f'AND $id < {(part + 1) * intervals}\r\n',
'OUTPUT': output_layer_path}
processing.run("native:extractbyexpression", params)
new_layer = ScrubLayer(self.qgis_path, output_layer_path, 'Temp Layer')
2024-03-26 19:21:15 -04:00
new_layer.create_spatial_index()
# Adding a folder for the remaining features
os.makedirs(splitted_layers_dir + f'/layer_{number_of_layers}')
2024-03-26 19:21:15 -04:00
output_layer_path = splitted_layers_dir + \
f'/layer_{number_of_layers}/layer_{number_of_layers}.shp'
2024-03-26 19:21:15 -04:00
params = {'INPUT': self.layer,
'EXPRESSION': f'$id >= {number_of_layers * intervals}\r\n',
'OUTPUT': output_layer_path}
processing.run("native:extractbyexpression", params)
new_layer = ScrubLayer(self.qgis_path, output_layer_path, 'Temp Layer')
2024-03-26 19:21:15 -04:00
new_layer.create_spatial_index()
2024-03-26 16:04:43 -04:00
@staticmethod
def merge_layers(layers_path, mergeded_layer_path):
merging_layers = find_shp_files(layers_path)
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
params = {'LAYERS': merging_layers,
'CRS': None,
'OUTPUT': mergeded_layer_path}
processing.run("native:mergevectorlayers", params)
def multipart_to_singleparts(self, singleparts_layer_path):
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
params = {'INPUT': self.layer,
'OUTPUT': singleparts_layer_path}
processing.run("native:multiparttosingleparts", params)
2024-03-26 16:04:43 -04:00
def delete_duplicates(self, deleted_duplicates_layer):
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
params = {'INPUT': self.layer_path,
'OUTPUT': deleted_duplicates_layer}
processing.run("native:deleteduplicategeometries", params)
def delete_field(self, field_name):
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
with edit(self.layer):
# Get the index of the column to delete
idx = self.layer.fields().indexFromName(field_name)
# Delete the field
self.layer.deleteAttribute(idx)
# Update layer fields
self.layer.updateFields()
def conditional_delete_record(self, field_name, operator, condition):
request = QgsFeatureRequest().setFilterExpression(
f'{field_name} {operator} {str(condition)}')
with edit(self.layer):
for feature in self.layer.getFeatures(request):
self.layer.deleteFeature(feature.id())
2024-08-07 15:44:38 -04:00
self.layer.commitChanges()
2024-03-26 16:04:43 -04:00
def add_field(self, new_field_name):
functionalities = self.layer.dataProvider().capabilities()
if functionalities & QgsVectorDataProvider.AddAttributes:
new_field = QgsField(new_field_name, QVariant.Double)
self.layer.dataProvider().addAttributes([new_field])
self.layer.updateFields()
def assign_area(self, field_name):
self.layer.startEditing()
idx = self.layer.fields().indexFromName(field_name)
context = QgsExpressionContext()
2024-08-07 15:44:38 -04:00
context.appendScopes(
QgsExpressionContextUtils.globalProjectLayerScopes(self.layer))
2024-03-26 16:04:43 -04:00
for feature in self.layer.getFeatures():
area = feature.geometry().area()
feature[idx] = area
self.layer.updateFeature(feature)
self.layer.commitChanges()
2024-03-16 16:33:47 -04:00
def __str__(self):
return f'The {self.layer_name} has {self.data_count} records.'
2024-03-15 12:08:52 -04:00
@staticmethod
def cleanup():
QgsApplication.exitQgis()
2024-03-15 11:26:23 -04:00
2024-03-15 12:08:52 -04:00