def progressWidget(self): """ Retrieves a progress widget bar. """ bar = QProgressBar() bar.setTextVisible(True) bar.setValue(0) return bar
def progressdialog(self, progress): pdialog = QProgressDialog() pdialog.setWindowTitle("Progress") pdialog.setLabelText("Voortgang van importeren:") pbar = QProgressBar(pdialog) pbar.setTextVisible(True) pbar.setValue(progress) pdialog.setBar(pbar) pdialog.setMinimumWidth(300) pdialog.show() return pdialog, pbar
class WaitDialog(IgnoreKeyPressesDialog): def __init__(self, parent, title, progress=False): super().__init__(parent, Qt.WindowTitleHint) vbox = QVBoxLayout() self.progress_bar = QProgressBar() max_val = PROGRESS_BAR_MAX if progress else 0 self.progress_bar.setRange(0, max_val) self.progress_bar.setTextVisible(False) vbox.addWidget(self.progress_bar) self.setModal(True) self.setWindowTitle(title) self.setLayout(vbox) self.setMaximumHeight(0) self.setFixedWidth(parent.width() * 0.25) self.show() def update_progress(self, progress: float) -> None: self.progress_bar.setValue(int(progress * PROGRESS_BAR_MAX)) self.progress_bar.repaint() # otherwise just updates in 1% steps
class Create_model: def __init__(self, dlg, current_layer): """This constructor need Qt Dialog class as dlg and need current layer to execute the algorithm in current_layer parameter""" self.layers = current_layer self.bar = QProgressBar() self.dlg = dlg #self.list = [] self.border = [] self.X = [] self.Y = [] self.Z = [] self.faces = [] self.points = [] #self.list_of_all=[] self.NoData = -3.4028234663852886e+38 '''def iterator(self): """Main algorithm to iterate through all the pixels and also to create boundaries""" self.layer = self.dlg.cmbSelectLayer.currentLayer() #get the extent of layer provider = self.layer.dataProvider() extent = provider.extent() xmin = extent.xMinimum() ymax = extent.yMaximum() rows = self.layer.height() cols = self.layer.width() xsize = self.layer.rasterUnitsPerPixelX() ysize = self.layer.rasterUnitsPerPixelY() xinit = xmin + xsize / 2 yinit = ymax - ysize / 2 block = provider.block(1, extent, cols, rows) #iterate the raster to get the values of pixels k=1 for i in range(rows): for j in range(cols): x = xinit + j * xsize y = yinit k += 1 if block.value(i, j) == self.NoData: self.list_of_all.append([i,j,x,y,block.value(i,j)]) elif block.value(i,j)>=self.dlg.dsbDatum.value(): self.list.append([x, y, block.value(i, j)]) self.list_of_all.append([i,j,x,y,block.value(i,j)]) else: self.list_of_all.append([i,j,x,y,self.NoData]) xinit = xmin + xsize / 2 yinit -= ysize #get minimal value to stretching method print(len(self.list_of_all)) height=[] for searching in self.list: height.append(searching[2]) self.minimal=min(height) #iterate the raster to get the boundaries colrow=[] rowcol=[] for pixelo in self.list_of_all: rowcol.append(pixelo) if pixelo[1]==j: colrow.append(rowcol) rowcol=[] for pixel in self.list_of_all: if pixel[4] != self.NoData: if pixel[0]==0 or pixel[1]==0 or pixel[0]==i or pixel[1]==j: pixel[4] = float(self.dlg.dsbDatum.value()) self.border.append([pixel[2],pixel[3],pixel[4]]) #self.list = self.border + self.list else: wii=pixel[0] kol=pixel[1] pixel[4]=float(self.dlg.dsbDatum.value()) condition1 = colrow[wii-1][kol][4] condition2 = colrow[wii][kol-1][4] condition3 = colrow[wii+1][kol][4] condition4 = colrow[wii][kol+1][4] if condition1> self.NoData or condition2> self.NoData or condition3> self.NoData or condition4 > self.NoData: if condition1 == self.NoData or condition2== self.NoData or condition3== self.NoData or condition4 == self.NoData: self.border.append([pixel[2],pixel[3],pixel[4]]) #self.list=self.border+self.list self.list += self.border print(len(self.border)) #print(self.list) print(len(self.list)) return self.list, self.minimal''' def iterator(self): #path = iface.activeLayer().source() path = self.dlg.cmbSelectLayer.currentLayer().source() data_source = gdal.Open(path) #extract one band, because we don't need 3d matrix band = data_source.GetRasterBand(1) #read matrix as numpy array raster = band.ReadAsArray().astype(np.float) #threshold = 222 #poziom odniesienia - wyzsze od 222 threshold = self.dlg.dsbDatum.value() #change no data to nan value raster[raster == band.GetNoDataValue()] = np.nan raster2 = raster[np.logical_not( np.isnan(raster) )] #w raster2 nie mam nanow i sa to tylko wartosci wysokosci (y_index, x_index) = np.nonzero(raster >= threshold) #get the minimal value to stretching method self.minimal = np.nanmin(raster) #To demonstate this compare a.shape to band.XSize and band.YSize (upper_left_x, x_size, x_rotation, upper_left_y, y_rotation, y_size) = data_source.GetGeoTransform() x_coords = x_index * x_size + upper_left_x + ( x_size / 2) #add half the cell size y_coords = y_index * y_size + upper_left_y + (y_size / 2 ) #to centre the point raster3 = raster2[raster2 >= threshold] z_coords = np.asarray(raster3).reshape(-1) entire_matrix = np.stack((x_coords, y_coords, z_coords), axis=-1) #add outer bounder = np.pad(raster, pad_width=1, mode='constant', constant_values=np.nan) bounder_inner = (np.roll(bounder, 1, axis=0) * np.roll(bounder, -1, axis=0) * np.roll(bounder, 1, axis=1) * np.roll(bounder, -1, axis=1) * np.roll(np.roll(bounder, 1, axis=0), 1, axis=1) * np.roll(np.roll(bounder, 1, axis=0), -1, axis=1) * np.roll(np.roll(bounder, -1, axis=0), 1, axis=1) * np.roll(np.roll(bounder, -1, axis=0), -1, axis=1)) is_inner = (np.isnan(bounder_inner) == False) b = bounder b[is_inner] = np.nan b[~np.isnan(b)] = threshold boundary_real = b[1:-1, 1:-1] boundary_real_2 = boundary_real[np.logical_not( np.isnan(boundary_real))] #create boundary coordinates (y_index_boundary, x_index_boundary) = np.nonzero(boundary_real == threshold) #print(len(boundary_real_2)) x_coords_boundary = x_index_boundary * x_size + upper_left_x + ( x_size / 2) #add half the cell size y_coords_boundary = y_index_boundary * y_size + upper_left_y + ( y_size / 2) #to centre the point z_coords_boundary = np.asarray(boundary_real_2).reshape(-1) boundary_the_end = np.stack( (x_coords_boundary, y_coords_boundary, z_coords_boundary), axis=-1) boundary_the_end = np.repeat(boundary_the_end, 10, axis=0) self.entire_mat_with_heights = np.concatenate( (entire_matrix, boundary_the_end)) #entire_mat_with_heights[entire_mat_with_heights[:, [0,1,2]].argsort()] self.entire_mat_with_heights = self.entire_mat_with_heights[np.argsort( self.entire_mat_with_heights[:, 2])] return self.entire_mat_with_heights, self.minimal def delaunay(self): """This is Delaunay algorithm from Scipy lib This is needed to create vertices and faces which will be going to executed in creating STL model""" self.x = self.entire_mat_with_heights[:, 0] self.y = self.entire_mat_with_heights[:, 1] self.z = self.entire_mat_with_heights[:, 2] #print(self.x.shape) self.tri = Delaunay(np.array([self.x, self.y]).T) for vert in self.tri.simplices: self.faces.append([vert[0], vert[1], vert[2]]) for i in range(self.x.shape[0]): self.points.append([self.x[i], self.y[i], self.z[i]]) return self.faces, self.points, self.x, self.y, self.z, self.tri def saver(self): """ Create STL model """ # Define the vertices vertices = np.array(self.points) # Define the triangles facess = np.array(self.faces) # Create the mesh self.figure = mesh.Mesh( np.zeros(facess.shape[0], dtype=mesh.Mesh.dtype)) all_percentage = len(facess) value = self.bar.value() for i, f in enumerate(facess): if value < all_percentage: value = value + 1 self.bar.setValue(value) else: self.timer.stop() for j in range(3): self.figure.vectors[i][j] = vertices[f[j], :] filename = self.dlg.lineEdit.text() self.figure.save(filename) return self.figure def create_graph(self): """ Visualize model by Matplotlib lib""" fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='3d') ax.plot_trisurf(self.x, self.y, self.z, triangles=self.tri.simplices, cmap=plt.cm.Spectral) ax.set_title('3D_Graph') ax.set_xlabel('X Label') ax.set_ylabel('Y Label') ax.set_zlabel('Z Label') plt.show() def stretching(self): """ This method stretching the entire model to the given Datum level""" for cords in self.entire_mat_with_heights: if cords[2] > self.minimal: height_stretched = cords[2] - float(self.dlg.dsbDatum.value()) height_stretched = height_stretched * self.dlg.sbStretch.value( ) height_stretched += float(self.dlg.dsbDatum.value()) cords[2] = height_stretched return self.entire_mat_with_heights def loading(self): """ Loading progress bar """ self.dialog = QProgressDialog() self.dialog.setWindowTitle("Loading") self.dialog.setLabelText("That's your progress") self.bar = QProgressBar() self.bar.setTextVisible(True) self.dialog.setBar(self.bar) self.dialog.setMinimumWidth(300) self.dialog.show() self.timer = QTimer() self.timer.timeout.connect(self.saver) self.timer.start(1000) def shape(self, direction): """ This algorithm convert ShapeFile to the .tif file. To created that process I used a lot of GDAL processing algorithms and this gave me possibility to convert the shape to .tif file, drape them to the correct elevation and merge this new raster to the current main tif """ self.plugin_dir = direction buffer_distance = self.dlg.dsbBuffer.value() output_data_type = self.dlg.sbOutputData.value() output_raster_size_unit = 0 no_data_value = 0 layer2 = self.dlg.cmbSelectShape.currentLayer() shape_dir = os.path.join(self.plugin_dir, 'TRASH') layer_ext_cor = self.dlg.cmbSelectLayer.currentLayer() provider = layer_ext_cor.dataProvider() extent = provider.extent() xmin = extent.xMinimum() ymax = extent.yMaximum() xmax = extent.xMaximum() ymin = extent.yMinimum() cord_sys = layer_ext_cor.crs().authid() coords = "%f,%f,%f,%f " % (xmin, xmax, ymin, ymax) + '[' + str(cord_sys) + ']' rows = layer_ext_cor.height() cols = layer_ext_cor.width() set_shp_height = self.dlg.sbHeight.value() processing.run( "native:buffer", { 'INPUT': layer2, 'DISTANCE': buffer_distance, 'SEGMENTS': 5, 'END_CAP_STYLE': 0, 'JOIN_STYLE': 0, 'MITER_LIMIT': 2, 'DISSOLVE': False, 'OUTPUT': os.path.join(shape_dir, 'shape_bufor.shp') }) #### tu poprawic ten dissolve \/ zredukowac ten na dole processing.run( "native:dissolve", { 'INPUT': os.path.join(shape_dir, 'shape_bufor.shp'), 'FIELD': [], 'OUTPUT': os.path.join(shape_dir, 'shape_dissolve.shp') }) processing.run( "qgis:generatepointspixelcentroidsinsidepolygons", { 'INPUT_RASTER': self.dlg.cmbSelectLayer.currentLayer().dataProvider(). dataSourceUri(), 'INPUT_VECTOR': os.path.join(shape_dir, 'shape_dissolve.shp'), 'OUTPUT': os.path.join(shape_dir, 'shape_points.shp') }) processing.run( "native:setzfromraster", { 'INPUT': os.path.join(shape_dir, 'shape_points.shp'), 'RASTER': self.dlg.cmbSelectLayer.currentLayer().dataProvider(). dataSourceUri(), 'BAND': 1, 'NODATA': 0, 'SCALE': 1, 'OUTPUT': os.path.join(shape_dir, 'shape_drape.shp') }) layer3 = iface.addVectorLayer( os.path.join(shape_dir, 'shape_drape.shp'), "Shape_Drape", "ogr") if not layer3: print("Layer failed to load!") field_name = "height" field_namess = [field.name() for field in layer3.fields()] i = 0 for l in range(100): i += 1 if field_name in field_namess: print('Exist') field_name = field_name + str(i) continue else: print('Doesn\'t Exist') break processing.run( "qgis:fieldcalculator", { 'INPUT': os.path.join(shape_dir, 'shape_drape.shp'), 'FIELD_NAME': field_name, 'FIELD_TYPE': 0, 'FIELD_LENGTH': 10, 'FIELD_PRECISION': 3, 'NEW_FIELD': True, 'FORMULA': 'z($geometry)+' + str(set_shp_height), 'OUTPUT': os.path.join(shape_dir, 'shape_drape_c.shp') }) processing.run( "gdal:rasterize", { 'INPUT': os.path.join(shape_dir, 'shape_drape_c.shp'), 'FIELD': field_name, 'BURN': 0, 'UNITS': output_raster_size_unit, 'WIDTH': cols, 'HEIGHT': rows, 'EXTENT': coords, 'NODATA': no_data_value, 'OPTIONS': '', 'DATA_TYPE': output_data_type, 'INIT': None, 'INVERT': False, 'OUTPUT': os.path.join(shape_dir, 'shape_to_raster.tif') }) iface.addRasterLayer(os.path.join(shape_dir, 'shape_to_raster.tif'), "Shape_to_Raster") QgsProject.instance().removeMapLayers([layer3.id()]) processing.run( "gdal:merge", { 'INPUT': [ self.dlg.cmbSelectLayer.currentLayer().dataProvider(). dataSourceUri(), os.path.join(shape_dir, 'shape_to_raster.tif') ], 'PCT': False, 'SEPARATE': False, 'NODATA_INPUT': None, 'NODATA_OUTPUT': None, 'OPTIONS': '', 'DATA_TYPE': 5, 'OUTPUT': os.path.join(shape_dir, 'merged.tif') }) iface.addRasterLayer(os.path.join(shape_dir, 'merged.tif'), "Raster_With_Shape") shape_to_raster = QgsProject.instance().mapLayersByName( 'Shape_to_Raster') QgsProject.instance().removeMapLayers([shape_to_raster[0].id()])
class ViewSTRWidget(WIDGET, BASE): """ Search and browse the social tenure relationship of all participating entities. """ def __init__(self, plugin): QMainWindow.__init__(self, plugin.iface.mainWindow()) self.setupUi(self) self.btnSearch.setIcon(GuiUtils.get_icon('search.png')) self.btnClearSearch.setIcon(GuiUtils.get_icon('reset.png')) self._plugin = plugin self.search_done = False # self.tbPropertyPreview.set_iface(self._plugin.iface) QTimer.singleShot( 100, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface)) self.curr_profile = current_profile() self.spatial_units = self.curr_profile.social_tenure.spatial_units # Center me self.move(QDesktopWidget().availableGeometry().center() - self.frameGeometry().center()) self.sp_unit_manager = SpatialUnitManagerDockWidget( self._plugin.iface, self._plugin) self.geom_cols = [] for spatial_unit in self.spatial_units: each_geom_col = self.sp_unit_manager.geom_columns(spatial_unit) self.geom_cols.extend(each_geom_col) # Configure notification bar self._notif_search_config = NotificationBar(self.vl_notification) # set whether currently logged in user has # permissions to edit existing STR records self._can_edit = self._plugin.STRCntGroup.canUpdate() self._can_delete = self._plugin.STRCntGroup.canDelete() self._can_create = self._plugin.STRCntGroup.canCreate() # Variable used to store a reference to the # currently selected social tenure relationship # when displaying documents in the supporting documents tab window. # This ensures that there are no duplicates # when the same item is selected over and over again. self._strID = None self.removed_docs = None # Used to store the root hash of the currently selected node. self._curr_rootnode_hash = "" self.str_model, self.str_doc_model = entity_model( self.curr_profile.social_tenure, False, True) self._source_doc_manager = SourceDocumentManager( self.curr_profile.social_tenure.supporting_doc, self.str_doc_model, self) self._source_doc_manager.documentRemoved.connect( self.onSourceDocumentRemoved) self._source_doc_manager.setEditPermissions(False) self.initGui() self.add_spatial_unit_layer() self.details_tree_view = DetailsTreeView(iface, self._plugin, self) # else: # self.details_tree_view = self._plugin.details_tree_view self.details_tree_view.activate_feature_details(True) self.details_tree_view.add_tree_view() self.details_tree_view.model.clear() count = pg_table_count(self.curr_profile.social_tenure.name) self.setWindowTitle( self.tr('{}{}'.format(self.windowTitle(), '- ' + str(count) + ' rows'))) self.active_spu_id = -1 self.toolBox.setStyleSheet(''' QToolBox::tab { background: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #EDEDED, stop: 0.4 #EDEDED, stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3 ); border-radius: 2px; border-style: outset; border-width: 2px; height: 100px; border-color: #C3C3C3; } QToolBox::tab:selected { font: italic; } ''') self.details_tree_view.view.setStyleSheet(''' QTreeView:!active { selection-background-color: #72a6d9; } ''') def add_tool_buttons(self): """ Add toolbar buttons of add, edit and delete buttons. :return: None :rtype: NoneType """ tool_buttons = QToolBar() tool_buttons.setObjectName('form_toolbar') tool_buttons.setIconSize(QSize(16, 16)) self.addSTR = QAction(GuiUtils.get_icon('add.png'), QApplication.translate('ViewSTRWidget', 'Add'), self) self.editSTR = QAction(GuiUtils.get_icon('edit.png'), QApplication.translate('ViewSTRWidget', 'Edit'), self) self.deleteSTR = QAction( GuiUtils.get_icon('remove.png'), QApplication.translate('ViewSTRWidget', 'Remove'), self) tool_buttons.addAction(self.addSTR) tool_buttons.addAction(self.editSTR) tool_buttons.addAction(self.deleteSTR) self.toolbarVBox.addWidget(tool_buttons) def initGui(self): """ Initialize widget """ self.tb_actions.setVisible(False) self._load_entity_configurations() self.add_tool_buttons() # Connect signals self.tbSTREntity.currentChanged.connect(self.entityTabIndexChanged) self.btnSearch.clicked.connect(self.searchEntityRelations) self.btnClearSearch.clicked.connect(self.clearSearch) # self.tvSTRResults.expanded.connect(self.onTreeViewItemExpanded) # Set the results treeview to accept requests for context menus # self.tvSTRResults.setContextMenuPolicy(Qt.CustomContextMenu) # self.tvSTRResults.customContextMenuRequested.connect( # self.onResultsContextMenuRequested # ) if not self._can_create: self.addSTR.hide() if not self._can_edit: self.editSTR.hide() else: self.editSTR.setDisabled(True) if not self._can_delete: self.deleteSTR.hide() else: self.deleteSTR.setDisabled(True) self.addSTR.triggered.connect(self.load_new_str_editor) self.deleteSTR.triggered.connect(self.delete_str) self.editSTR.triggered.connect(self.load_edit_str_editor) # Load async for the current widget self.entityTabIndexChanged(0) def init_progress_dialog(self): """ Initializes the progress dialog. """ self.progress = QProgressBar(self) self.progress.resize(self.width(), 10) self.progress.setTextVisible(False) def add_spatial_unit_layer(self): """ Add the spatial unit layer into the map canvas for later use. """ # Used for startup of view STR, just add the first geom layer. if len(self.geom_cols) > 0: for spatial_unit in self.spatial_units: layer_name_item = self.sp_unit_manager.geom_col_layer_name( spatial_unit.name, self.geom_cols[0]) self.sp_unit_manager.add_layer_by_name(layer_name_item) def _check_permissions(self): """ Enable/disable actions based on the permissions defined in the content group. """ if self._can_edit: self.tb_actions.addAction(self._new_str_action) else: self.tb_actions.removeAction(self._new_str_action) if len(self.tb_actions.actions()) == 0: self.tb_actions.setVisible(False) else: self.tb_actions.setVisible(True) def _load_entity_configurations(self): """ Specify the entity configurations. """ try: self.parties = self.curr_profile.social_tenure.parties tb_str_entities = self.parties + self.spatial_units for i, t in enumerate(tb_str_entities): QApplication.processEvents() entity_cfg = self._entity_config_from_profile( str(t.name), t.short_name) if not entity_cfg is None: entity_widget = self.add_entity_config(entity_cfg) # entity_widget.setNodeFormatter( # EntityNodeFormatter( # entity_cfg, self.tvSTRResults, self # ) # ) except DummyException as pe: self._notif_search_config.clear() self._notif_search_config.insertErrorNotification(str(pe)) def _entity_config_from_profile(self, table_name, short_name): """ Creates an EntityConfig object from the table name. :param table_name: Name of the database table. :type table_name: str :return: Entity configuration object. :rtype: EntityConfig """ table_display_name = format_name(short_name) entity = self.curr_profile.entity_by_name(table_name) model = entity_model(entity) if model is not None: # Entity configuration entity_cfg = EntityConfiguration() entity_cfg.Title = table_display_name entity_cfg.STRModel = model entity_cfg.data_source_name = table_name for col, factory in self._get_widget_factory(entity): entity_cfg.LookupFormatters[col.name] = factory # Load filter and display columns # using only those which are of # numeric/varchar type searchable_columns = entity_searchable_columns(entity) display_columns = entity_display_columns(entity) for c in searchable_columns: if c != 'id': entity_cfg.filterColumns[c] = format_name(c) for c in display_columns: if c != 'id': entity_cfg.displayColumns[c] = format_name(c) return entity_cfg else: return None def _get_widget_factory(self, entity): """ Get widget factory for specific column type :param entity: Current column entity object :type entity: Entity :return c: Column object corresponding to the widget factory :rtype c: BaseColumn :return col_factory: Widget factory corresponding to the column type :rtype col_factory: ColumnWidgetRegistry """ for c in entity.columns.values(): col_factory = ColumnWidgetRegistry.factory(c.TYPE_INFO) if col_factory is not None: yield c, col_factory(c) def add_entity_config(self, config): """ Set an entity configuration option and add it to the 'Search Entity' tab. """ entityWidg = STRViewEntityWidget(config) entityWidg.asyncStarted.connect(self._progressStart) entityWidg.asyncFinished.connect(self._progressFinish) tabIndex = self.tbSTREntity.addTab(entityWidg, config.Title) return entityWidg def entityTabIndexChanged(self, index): """ Raised when the tab index of the entity search tab widget changes. """ # Get the current widget in the tab container entityWidget = self.tbSTREntity.currentWidget() if isinstance(entityWidget, EntitySearchItem): entityWidget.loadAsync() def searchEntityRelations(self): """ Slot that searches for matching items for the specified entity and corresponding STR entities. """ entityWidget = self.tbSTREntity.currentWidget() entity_name = entityWidget.config.data_source_name self._reset_controls() if isinstance(entityWidget, EntitySearchItem): valid, msg = entityWidget.validate() if not valid: self._notif_search_config.clear() self._notif_search_config.insertErrorNotification(msg) return results, searchWord = entityWidget.executeSearch() # Show error message if len(results) == 0: noResultsMsg = QApplication.translate( 'ViewSTR', 'No results found for "{}"'.format(searchWord)) self._notif_search_config.clear() self._notif_search_config.insertErrorNotification(noResultsMsg) return party_names = [ e.name for e in self.curr_profile.social_tenure.parties ] entity = self.curr_profile.entity_by_name(entity_name) result_ids = [r.id for r in results] if entity_name in party_names: self.active_spu_id = self.details_tree_view.search_party( entity, result_ids) else: self.details_tree_view.search_spatial_unit(entity, result_ids) # self.tbPropertyPreview._iface.activeLayer().selectByExpression("id={}".format(self.active_spu_id)) # self.details_tree_view._selected_features = self.tbPropertyPreview._iface.activeLayer().selectedFeatures() # self._load_root_node(entity_name, formattedNode) def clearSearch(self): """ Clear search input parameters (for current widget) and results. """ entityWidget = self.tbSTREntity.currentWidget() if isinstance(entityWidget, EntitySearchItem): entityWidget.reset() self._reset_controls() def _reset_controls(self): # Clear tree view self._resetTreeView() # Clear document listings self._deleteSourceDocTabs() # Remove spatial unit memory layer self.tbPropertyPreview.remove_layer() def on_select_results(self): """ Slot which is raised when the selection is changed in the tree view selection model. """ if len(self.details_tree_view.view.selectedIndexes()) < 1: self.disable_buttons() return self.search_done = True index = self.details_tree_view.view.selectedIndexes()[0] item = self.details_tree_view.model.itemFromIndex(index) QApplication.processEvents() # STR node - edit social tenure relationship if item.text() == self.details_tree_view.str_text: entity = self.curr_profile.social_tenure str_model = self.details_tree_view.str_models[item.data()] self.details_tree_view.selected_model = str_model self.details_tree_view.selected_item = SelectedItem(item) documents = self.details_tree_view._supporting_doc_models( entity.name, str_model) self._load_source_documents(documents) # if there is supporting document, # expand supporting document tab if len(documents) > 0: self.toolBox.setCurrentIndex(1) self.disable_buttons(False) # party node - edit party elif item.data() in self.details_tree_view.spatial_unit_items.keys(): self.toolBox.setCurrentIndex(0) entity = self.details_tree_view.spatial_unit_items[item.data()] model = self.details_tree_view.feature_model(entity, item.data()) self.draw_spatial_unit(entity.name, model) self.disable_buttons() canvas = iface.mapCanvas() if canvas: canvas.zoomToFullExtent() else: self.disable_buttons() def disable_buttons(self, status=True): if self._can_edit: self.deleteSTR.setDisabled(status) if self._can_delete: self.editSTR.setDisabled(status) def str_party_column_obj(self, record): """ Gets the current party column name in STR table by finding party column with value other than None. :param record: The STR record or result. :type record: Dictionary :return: The party column name with value. :rtype: String """ for party in self.parties: party_name = party.short_name.lower() party_id = '{}_id'.format(party_name) if party_id not in record.__dict__: return None if record.__dict__[party_id] != None: party_id_obj = getattr(self.str_model, party_id) return party_id_obj def load_edit_str_editor(self): self.details_tree_view.edit_selected_node(self.details_tree_view) self.btnSearch.click() self.disable_buttons() def load_new_str_editor(self): try: # Check type of node and perform corresponding action add_str = STREditor() add_str.exec_() except DummyException as ex: QMessageBox.critical( self._plugin.iface.mainWindow(), QApplication.translate("STDMPlugin", "Loading Error"), str(ex)) def delete_str(self): self.details_tree_view.delete_selected_item() self.btnSearch.click() self.disable_buttons() def onSourceDocumentRemoved(self, container_id, doc_uuid, removed_doc): """ Slot raised when a source document is removed from the container. If there are no documents in the specified container then remove the tab. """ curr_container = self.tbSupportingDocs.currentWidget() curr_doc_widget = curr_container.findChildren(DocumentWidget) for doc in curr_doc_widget: if doc.fileUUID == doc_uuid: doc.deleteLater() self.removed_docs = removed_doc def draw_spatial_unit(self, entity_name, model): """ Render the geometry of the given spatial unit in the spatial view. :param row_id: Sqlalchemy object representing a feature. """ entity = self.curr_profile.entity_by_name(entity_name) self.tbPropertyPreview.draw_spatial_unit(entity, model) def showEvent(self, event): """ (Re)load map layers in the viewer and main canvas. :param event: Window event :type event: QShowEvent """ self.setEnabled(True) if QTimer is not None: QTimer.singleShot(200, self.init_mirror_map) return QMainWindow.showEvent(self, event) def init_mirror_map(self): self._notify_no_base_layers() # Add spatial unit layer if it doesn't exist self.tbPropertyPreview.refresh_canvas_layers() self.tbPropertyPreview.load_web_map() def _notify_no_base_layers(self): """ Checks if there are any base layers that will be used when visualizing the spatial units. If there are no base layers then insert warning message. """ self._notif_search_config.clear() num_layers = len(QgsProject.instance().mapLayers()) if num_layers == 0: msg = QApplication.translate( "ViewSTR", "No basemap layers are loaded in the " "current project. Basemap layers " "enhance the visualization of spatial units.") self._notif_search_config.insertWarningNotification(msg) def _deleteSourceDocTabs(self): """ Removes all source document tabs and deletes their references. """ tabCount = self.tbSupportingDocs.count() while tabCount != 0: srcDocWidget = self.tbSupportingDocs.widget(tabCount - 1) self.tbSupportingDocs.removeTab(tabCount - 1) del srcDocWidget tabCount -= 1 self._strID = None self._source_doc_manager.reset() def _resetTreeView(self): """ Clears the results tree view. """ # Reset tree view strModel = self.details_tree_view.view.model() resultsSelModel = self.details_tree_view.view.selectionModel() if strModel: strModel.clear() if resultsSelModel: if self.search_done: resultsSelModel.selectionChanged.disconnect( self.on_select_results) resultsSelModel.selectionChanged.connect(self.on_select_results) def _load_source_documents(self, source_docs): """ Load source documents into document listing widget. """ # Configure progress dialog progress_msg = QApplication.translate( "ViewSTR", "Loading supporting documents...") progress_dialog = QProgressDialog(self) if len(source_docs) > 0: progress_dialog.setWindowTitle(progress_msg) progress_dialog.setRange(0, len(source_docs)) progress_dialog.setWindowModality(Qt.WindowModal) progress_dialog.setFixedWidth(380) progress_dialog.show() progress_dialog.setValue(0) self._notif_search_config.clear() self.tbSupportingDocs.clear() self._source_doc_manager.reset() if len(source_docs) < 1: empty_msg = QApplication.translate( 'ViewSTR', 'No supporting document is uploaded ' 'for this social tenure relationship.') self._notif_search_config.clear() self._notif_search_config.insertWarningNotification(empty_msg) for i, (doc_type_id, doc_obj) in enumerate(source_docs.items()): # add tabs, and container and widget for each tab tab_title = self._source_doc_manager.doc_type_mapping[doc_type_id] tab_widget = QWidget() tab_widget.setObjectName(tab_title) cont_layout = QVBoxLayout(tab_widget) cont_layout.setObjectName('widget_layout_' + tab_title) scrollArea = QScrollArea(tab_widget) scrollArea.setFrameShape(QFrame.NoFrame) scrollArea_contents = QWidget() scrollArea_contents.setObjectName('tab_scroll_area_' + tab_title) tab_layout = QVBoxLayout(scrollArea_contents) tab_layout.setObjectName('layout_' + tab_title) scrollArea.setWidgetResizable(True) scrollArea.setWidget(scrollArea_contents) cont_layout.addWidget(scrollArea) self._source_doc_manager.registerContainer(tab_layout, doc_type_id) for doc in doc_obj: try: # add doc widgets self._source_doc_manager.insertDocFromModel( doc, doc_type_id) except DummyException as ex: LOGGER.debug(str(ex)) self.tbSupportingDocs.addTab(tab_widget, tab_title) progress_dialog.setValue(i + 1) # def _on_node_reference_changed(self, rootHash): # """ # Method for resetting document listing and map preview # if another root node and its children # are selected then the documents are reset as # well as the map preview control. # """ # if rootHash != self._curr_rootnode_hash: # self._deleteSourceDocTabs() # self._curr_rootnode_hash = rootHash def _progressStart(self): """ Load progress dialog window. For items whose durations is unknown, 'isindefinite' = True by default. If 'isindefinite' is False, then 'rangeitems' has to be specified. """ pass def _progressFinish(self): """ Hide progress dialog window. """ pass def _edit_permissions(self): """ Returns True/False whether the current logged in user has permissions to create new social tenure relationships. If true, then the system assumes that they can also edit STR records. """ canEdit = False userName = globals.APP_DBCONN.User.UserName authorizer = Authorizer(userName) newSTRCode = "9576A88D-C434-40A6-A318-F830216CA15A" # Get the name of the content from the code cnt = Content() createSTRCnt = cnt.queryObject().filter( Content.code == newSTRCode).first() if createSTRCnt: name = createSTRCnt.name canEdit = authorizer.CheckAccess(name) return canEdit
class DocumentGeneratorDialog(WIDGET, BASE): """ Dialog that enables a user to generate documents by using configuration information for different entities. """ def __init__(self, iface, access_templates, parent=None, plugin=None): QDialog.__init__(self, parent) self.setupUi(self) self.btnSelectTemplate.setIcon(GuiUtils.get_icon('document.png')) self._iface = iface self.plugin = plugin self._docTemplatePath = "" self._outputFilePath = "" self.curr_profile = current_profile() self.last_data_source = None self._config_mapping = OrderedDict() self._notif_bar = NotificationBar(self.vlNotification) self._doc_generator = DocumentGenerator(self._iface, self) self._data_source = "" self.access_templates = access_templates enable_drag_sort(self.lstDocNaming) # Configure generate button generateBtn = self.buttonBox.button(QDialogButtonBox.Ok) if not generateBtn is None: generateBtn.setText( QApplication.translate("DocumentGeneratorDialog", "Generate")) # Load supported image types supportedImageTypes = QImageWriter.supportedImageFormats() for imageType in supportedImageTypes: imageTypeStr = imageType.data().decode() self.cboImageType.addItem(imageTypeStr) self._init_progress_dialog() # Connect signals self.btnSelectTemplate.clicked.connect(self.onSelectTemplate) self.buttonBox.accepted.connect(self.onGenerate) self.chkUseOutputFolder.stateChanged.connect( self.onToggledOutputFolder) self.rbExpImage.toggled.connect(self.onToggleExportImage) self.tabWidget.currentChanged.connect(self.on_tab_index_changed) self.chk_template_datasource.stateChanged.connect( self.on_use_template_datasource) self.btnShowOutputFolder.clicked.connect(self.onShowOutputFolder) def _init_progress_dialog(self): """ Initializes the progress dialog. """ self.progress = QProgressBar(self) self.progress.resize(self.width(), 10) self.progress.setTextVisible(False) def add_entity_configuration(self, **kwargs): ent_config = EntityConfig(**kwargs) self.add_entity_config(ent_config) def add_entity_config(self, ent_config, progress_value=0): QApplication.processEvents() if not self._config_mapping.get(ent_config.title(), ""): fk_mapper = self._create_fk_mapper(ent_config) self.tabWidget.addTab(fk_mapper, ent_config.title()) self._config_mapping[ent_config.title()] = ent_config # Force list of column names to be loaded if self.tabWidget.currentIndex() != 0: self.tabWidget.setCurrentIndex(0) else: self.on_tab_index_changed(0) self.progress.setValue(progress_value) def on_tab_index_changed(self, index): if index == -1: return config = self.config(index) if not config is None: # Set data source name self._data_source = config.data_source() def on_use_template_datasource(self, state): if state == Qt.Checked: self.tabWidget.setEnabled(False) self.chkUseOutputFolder.setEnabled(False) self.chkUseOutputFolder.setChecked(False) elif state == Qt.Unchecked: self.tabWidget.setEnabled(True) self.chkUseOutputFolder.setEnabled(True) self.chkUseOutputFolder.setChecked(False) self.on_tab_index_changed(self.tabWidget.currentIndex()) def onShowOutputFolder(self): reg_config = RegistryConfig() path = reg_config.read([COMPOSER_OUTPUT]) output_path = path.get(COMPOSER_OUTPUT, '') # windows if sys.platform.startswith('win32'): os.startfile(output_path) # *nix systems if sys.platform.startswith('linux'): subprocess.Popen(['xdg-open', output_path]) # macOS if sys.platform.startswith('darwin'): subprocess.Popen(['open', output_path]) def notification_bar(self): """ :return: Returns an instance of the notification bar. :rtype: NotificationBar """ return self._notif_bar def config(self, index): """ Returns the configuration for the current mapper in the tab widget. """ tab_key = self.tabWidget.tabText(index) return self._config_mapping.get(tab_key, None) def current_config(self): """ Returns the configuration corresponding to the current widget in the tab. """ return self.config(self.tabWidget.currentIndex()) def _load_model_columns(self, config): """ Load model columns into the view for specifying file output name. Only those columns of display type variants will be used. """ model_attr_mapping = OrderedDict() for c in config.data_source_columns(): model_attr_mapping[c] = format_name(c) self.lstDocNaming.load_mapping(model_attr_mapping) def _load_data_source_columns(self, entity): """ Load the columns of a data source for use in the file naming. """ table_cols = entity_display_columns(entity, True) attr_mapping = OrderedDict() for c, header in table_cols.items(): attr_mapping[c] = header self.lstDocNaming.load_mapping(attr_mapping) def _create_fk_mapper(self, config): fk_mapper = ForeignKeyMapper(config.ds_entity, self.tabWidget, self._notif_bar, enable_list=True, can_filter=True, plugin=self.plugin) fk_mapper.setDatabaseModel(config.model()) fk_mapper.setSupportsList(True) fk_mapper.setDeleteonRemove(False) fk_mapper.setNotificationBar(self._notif_bar) return fk_mapper def onSelectTemplate(self): """ Slot raised to load the template selector dialog. """ current_config = self.current_config() if current_config is None: msg = QApplication.translate( 'DocumentGeneratorDialog', 'An error occured while trying to determine the data source ' 'for the current entity.\nPlease check your current profile ' 'settings.') QMessageBox.critical( self, QApplication.translate('DocumentGeneratorDialog', 'Template Selector'), msg) return # Set the template selector to only load those templates that # reference the current data source. filter_table = current_config.data_source() templateSelector = TemplateDocumentSelector( self, filter_data_source=filter_table, access_templates=self.access_templates) if templateSelector.exec_() == QDialog.Accepted: docName, docPath = templateSelector.documentMapping() self.lblTemplateName.setText(docName) self._docTemplatePath = docPath if filter_table != self.last_data_source: # Load template data source fields self._load_template_datasource_fields() def _load_template_datasource_fields(self): # If using template data source template_doc, err_msg = self._doc_generator.template_document( self._docTemplatePath) if template_doc is None: QMessageBox.critical( self, "Error Generating documents", QApplication.translate( "DocumentGeneratorDialog", "Error Generating documents - %s" % (err_msg))) return composer_ds, err_msg = self._doc_generator.composer_data_source( template_doc) if composer_ds is None: QMessageBox.critical( self, "Error Generating documents", QApplication.translate( "DocumentGeneratorDialog", "Error Generating documents - %s" % (err_msg))) return # Load data source columns self._data_source = self.current_config().data_source() self.ds_entity = self.curr_profile.entity_by_name(self._data_source) self._load_data_source_columns(self.ds_entity) def onToggledOutputFolder(self, state): """ Slot raised to enable/disable the generated output documents to be written to the plugin composer output folder using the specified naming convention. """ if state == Qt.Checked: self.gbDocNaming.setEnabled(True) elif state == Qt.Unchecked: self.gbDocNaming.setEnabled(False) def reset(self, success_status=False): """ Clears/resets the dialog from user-defined options. """ self._docTemplatePath = "" self._data_source = "" self.lblTemplateName.setText("") # reset form only if generation is successful if success_status: fk_table_view = self.tabWidget.currentWidget(). \ findChild(QTableView) while fk_table_view.model().rowCount() > 0: fk_table_view.model().rowCount(0) fk_table_view.model().removeRow(0) if self.tabWidget.count() > 0 and \ not self.chk_template_datasource.isChecked(): self.on_tab_index_changed(0) if self.cboImageType.count() > 0: self.cboImageType.setCurrentIndex(0) def onToggleExportImage(self, state): """ Slot raised to enable/disable the image formats combobox. """ if state: self.cboImageType.setEnabled(True) else: self.cboImageType.setEnabled(False) def onGenerate(self): """ Slot raised to initiate the certificate generation process. """ self._notif_bar.clear() success_status = True config = self.current_config() self.last_data_source = config.data_source() if config is None: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "The entity configuration could not be extracted.")) return # Get selected records and validate records = self.tabWidget.currentWidget().entities() if self.chk_template_datasource.isChecked(): records = self._dummy_template_records() if len(records) == 0: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "Please load at least one entity record")) return if not self._docTemplatePath: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "Please select a document template to use")) return documentNamingAttrs = self.lstDocNaming.selectedMappings() if self.chkUseOutputFolder.checkState() == Qt.Checked and len( documentNamingAttrs) == 0: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "Please select at least one field for naming the output document")) return # Set output file properties if self.rbExpImage.isChecked(): outputMode = DocumentGenerator.Image fileExtension = self.cboImageType.currentText() saveAsText = "Image File" else: outputMode = DocumentGenerator.PDF fileExtension = "pdf" saveAsText = "PDF File" # Show save file dialog if not using output folder if self.chkUseOutputFolder.checkState() == Qt.Unchecked: docDir = source_document_location() if self._outputFilePath: fileInfo = QFileInfo(self._outputFilePath) docDir = fileInfo.dir().path() self._outputFilePath, _ = QFileDialog.getSaveFileName( self, QApplication.translate("DocumentGeneratorDialog", "Save Document"), docDir, "{0} (*.{1})".format( QApplication.translate("DocumentGeneratorDialog", saveAsText), fileExtension)) if not self._outputFilePath: self._notif_bar.insertErrorNotification( QApplication.translate( "DocumentGeneratorDialog", "Process aborted. No output file was specified.")) return # Include extension in file name self._outputFilePath = self._outputFilePath # + "." + fileExtension # else: # Multiple files to be generated. # pass self._doc_generator.set_link_field(config.link_field()) self._doc_generator.clear_attr_value_formatters() if not self.chk_template_datasource.isChecked(): # Apply cell formatters for naming output files self._doc_generator.set_attr_value_formatters(config.formatters()) entity_field_name = "id" # Iterate through the selected records progressDlg = QProgressDialog(self) progressDlg.setMaximum(len(records)) try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) for i, record in enumerate(records): progressDlg.setValue(i) if progressDlg.wasCanceled(): success_status = False break # User-defined location if self.chkUseOutputFolder.checkState() == Qt.Unchecked: status, msg = self._doc_generator.run( self._docTemplatePath, entity_field_name, record.id, outputMode, data_source=self.ds_entity.name, filePath=self._outputFilePath) self._doc_generator.clear_temporary_layers() # Output folder location using custom naming else: status, msg = self._doc_generator.run( self._docTemplatePath, entity_field_name, record.id, outputMode, dataFields=documentNamingAttrs, fileExtension=fileExtension, data_source=self.ds_entity.name) self._doc_generator.clear_temporary_layers() if not status: result = QMessageBox.warning( self, QApplication.translate("DocumentGeneratorDialog", "Document Generate Error"), msg, QMessageBox.Ignore | QMessageBox.Abort) if result == QMessageBox.Abort: progressDlg.close() success_status = False # Restore cursor QApplication.restoreOverrideCursor() return # If its the last record and user has selected to ignore if i + 1 == len(records): progressDlg.close() success_status = False # Restore cursor QApplication.restoreOverrideCursor() return else: progressDlg.setValue(len(records)) QApplication.restoreOverrideCursor() QMessageBox.information( self, QApplication.translate("DocumentGeneratorDialog", "Document Generation Complete"), QApplication.translate( "DocumentGeneratorDialog", "Document generation has successfully completed.")) except DummyException as ex: LOGGER.debug(str(ex)) err_msg = sys.exc_info()[1] QApplication.restoreOverrideCursor() QMessageBox.critical( self, "STDM", QApplication.translate( "DocumentGeneratorDialog", "Error Generating documents - %s" % (err_msg))) success_status = False # Reset UI self.reset(success_status) def _dummy_template_records(self): """ This is applied when records from a template data source are to be used to generate the documents where no related entity will be used to filter matching records in the data source. The iteration of the data source records will be done internally within the DocumentGenerator class. """ class _DummyRecord: id = 1 return [_DummyRecord()] def showEvent(self, event): """ Notifies if there are not entity configuration objects defined. :param event: Window event :type event: QShowEvent """ QTimer.singleShot(500, self.check_entity_config) return QDialog.showEvent(self, event) def check_entity_config(self): if len(self._config_mapping) == 0: self._notif_bar.clear() msg = QApplication.translate( "DocumentGeneratorDialog", "Table " "configurations do not exist or have not been configured properly" ) self._notif_bar.insertErrorNotification(msg)
class Ui_Dialog(object): """ def __init__(self, iface): self.iface = iface """ def setupUi(self, Dialog): self.iface = iface Dialog.setObjectName("Dialog") Dialog.resize( QtCore.QSize(QtCore.QRect(0, 0, 350, 250).size()).expandedTo( Dialog.minimumSizeHint())) Dialog.setWindowTitle("GroupPointsWithinDistance") # QLabel lancer recherche self.label10 = QLabel(Dialog) self.label10.setGeometry(QtCore.QRect(15, 15, 320, 18)) self.label10.setObjectName("label10") self.label10.setText("Select a layer with points to regroup: ") ListeCouchesPoint = [""] NbCouches = self.iface.mapCanvas().layerCount() if NbCouches == 0: QMessageBox.information(None, "information:", "No layers ! ") else: for i in range(0, NbCouches): couche = self.iface.mapCanvas().layer(i) # 0 pour point if couche.geometryType() == 0 or couche.geometryType() == 3: if couche.isValid(): ListeCouchesPoint.append(couche.name()) else: QMessageBox.information(None, "information:", "No layers with points ! ") return None self.ComboBoxPoints = QComboBox(Dialog) self.ComboBoxPoints.setMinimumSize(QtCore.QSize(320, 25)) self.ComboBoxPoints.setMaximumSize(QtCore.QSize(320, 25)) self.ComboBoxPoints.setGeometry(QtCore.QRect(10, 35, 320, 25)) self.ComboBoxPoints.setObjectName("ComboBoxPoints") for i in range(len(ListeCouchesPoint)): self.ComboBoxPoints.addItem(ListeCouchesPoint[i]) # QLabel entrer Enter distance of recherch self.labelResearchDistance = QLabel(Dialog) self.labelResearchDistance.setGeometry(QtCore.QRect(15, 80, 240, 23)) self.labelResearchDistance.setObjectName(" ResearchDistance") self.labelResearchDistance.setText("Enter distance of research :") #Exemple de QDoubleSpinBox self.dsbResearchDistance = QDoubleSpinBox(Dialog) self.dsbResearchDistance.setMinimumSize(QtCore.QSize(70, 23)) self.dsbResearchDistance.setMaximumSize(QtCore.QSize(70, 23)) self.dsbResearchDistance.setGeometry(QtCore.QRect(180, 80, 70, 23)) self.dsbResearchDistance.setObjectName("dsb") #self.dsbResearchDistance.setValue(10.0) self.dsbResearchDistance.setDecimals(1) self.dsbResearchDistance.setSingleStep(10.0) self.dsbResearchDistance.setRange(0, 1000000) self.dsbResearchDistance.setProperty("value", 100.0) #self.dsbResearchDistance.valueChanged.connect(self.onValueChanged) #Exemple de QPushButton self.DoButton = QPushButton(Dialog) self.DoButton.setMinimumSize(QtCore.QSize(280, 20)) self.DoButton.setMaximumSize(QtCore.QSize(280, 20)) self.DoButton.setGeometry(QtCore.QRect(15, 120, 280, 20)) self.DoButton.setObjectName("DoButton") self.DoButton.setText(" Let's make aggregates - being patient !") #Exemple de QLCDNumber self.progressBar = QProgressBar(Dialog) self.progressBar.setProperty("value", 0) self.progressBar.setMinimumSize(QtCore.QSize(260, 15)) self.progressBar.setMaximumSize(QtCore.QSize(260, 15)) self.progressBar.setGeometry(QtCore.QRect(30, 155, 260, 15)) self.progressBar.setAlignment(QtCore.Qt.AlignCenter) self.progressBar.setTextVisible(True) self.progressBar.setObjectName("progressBar") self.progressBar.setStyleSheet( """QProgressBar {border: 2px solid grey; border-radius: 5px; text-align: center;}""" """QProgressBar::chunk {background-color: #6C96C6; width: 20px;}""" ) #Pose a minima une valeur de la barre de progression / slide contrôle self.progressBar.setValue(0) #Exemple de QPushButton self.aboutButton = QPushButton(Dialog) self.aboutButton.setMinimumSize(QtCore.QSize(70, 20)) self.aboutButton.setMaximumSize(QtCore.QSize(70, 20)) self.aboutButton.setGeometry(QtCore.QRect(30, 195, 70, 23)) self.aboutButton.setObjectName("aboutButton") self.aboutButton.setText(" Read me ") self.PushButton = QPushButton(Dialog) self.PushButton.setMinimumSize(QtCore.QSize(100, 20)) self.PushButton.setMaximumSize(QtCore.QSize(100, 20)) self.PushButton.setGeometry(QtCore.QRect(185, 195, 100, 20)) self.PushButton.setObjectName("PushButton") self.PushButton.setText("Close") self.PushButton.clicked.connect(Dialog.reject) self.ComboBoxPoints.activated[str].connect(self.onComboP) self.aboutButton.clicked.connect(self.doAbout) self.DoButton.clicked.connect(self.Run) QtCore.QMetaObject.connectSlotsByName(Dialog) def onComboP(self): global SelectionP SelectionP = self.ComboBoxPoints.currentText() def doAbout(self): d = doAboutGroupPointsWithinDistance.Dialog() d.exec_() def Run(self): D = 0.0 D = self.dsbResearchDistance.value() #QMessageBox.information(None,"information:"," D : "+str(D)) DicoP1 = {} DicoA = {} counterProgess = 0 compteur_pt = 0 layerP = fonctionsGPWD.getVectorLayerByName(SelectionP) # on parcourt la couche de points et on stocke dans les dictionnaire DicoP1 les points # the points of the point laye are put into a dictionnary for featP in layerP.getFeatures(): Point_id = featP.id() geomP1 = featP.geometry() Point1 = geomP1.asPoint() DicoP1[Point_id] = [Point1] compteur_pt += 1 if D == 0: QMessageBox.information(None, "information:", "Zero is not a value for D !") #zdim est le compteur de la progress bar zDim = compteur_pt counterProgess = 0 cpt_agg = 1 nb = 0 liste_id = [] liste_pt = [] liste_aggreg_pt = [] DicoSegments = {} firstDicoSegments = True T = True while len(DicoP1) != 0: first = True zPercent = int(100 * counterProgess / zDim) self.progressBar.setValue(zPercent) nb = 0 for keyD1 in list(DicoP1.keys()): P1 = DicoP1[keyD1][0] if first: # we pick a first point and delete it from the dictionnary we point are stored T = True first = False nb += 1 liste_id = [keyD1] liste_pt = [P1] counterProgess += 1 del DicoP1[keyD1] while T: # We are generating an aggregates and making it grows # by adding points at distance from the point it contains # and repeating the research all over again as soon a poitn is added # untill none are added for pt in liste_pt: compteur_corresP = 0 for keyD1 in list(DicoP1.keys()): P1 = DicoP1[keyD1][0] if fonctionsGPWD.mag(fonctionsGPWD.vect( pt, P1)) < D: # one point at distance found compteur_corresId = 0 for idp in liste_id: if keyD1 == idp: # is this point already added in the aggregate compteur_corresId += 1 if compteur_corresId == 0: # if not let s add it nb += 1 liste_id.append(keyD1) liste_pt.append(P1) compteur_corresP += 1 counterProgess += 1 # boucle des segments # interpoint line loop idseg = '' # a segment as an id made of the points id order ordered id: smallerid-biggerid idseg = str(keyD1) + '-' + str(idp) idseg_reverse = str(idp) + '-' + str(keyD1) if firstDicoSegments: firstDicoSegments = False if int(keyD1) > int(idp): idseg = idseg_reverse DicoSegments[idseg] = [[pt, P1], idp, keyD1, cpt_agg] else: DicoSegments[idseg] = [[P1, pt], keyD1, idp, cpt_agg] else: for idseg_cheack in list( DicoSegments.keys()): if idseg == idseg_cheack or idseg_reverse == idseg_cheack: pass else: if int(keyD1) > int(idp): idseg = idseg_reverse DicoSegments[idseg] = [[ pt, P1 ], idp, keyD1, cpt_agg] else: DicoSegments[idseg] = [[ P1, pt ], keyD1, idp, cpt_agg] if compteur_corresP == 0: # if no more points are find then we are over with the previous aggregate T = False DicoA[cpt_agg] = [nb, liste_id, liste_pt, DicoSegments] cpt_agg += 1 for id in liste_id: for keyD1 in list(DicoP1.keys()): if id == keyD1: del DicoP1[keyD1] # on fabrique un polygone buffer de D/100 # du convexHull de tous les points de l'agregat # pour les operateur Pyqgis # voir http://www.qgis.org/api/classQgsGeometry.html#a1699b205d01c365a50ead2d0bf2bbcfb DicoP4 = {} for key2 in list(DicoA.keys()): list_pt = [] list_pt = DicoA[key2][2] nb_pt = 0 nb_pt = DicoA[key2][0] Liste_id = [] Liste_id = DicoA[key2][1] buff = 0.0 first = True for pt in list_pt: if first: first = False #https://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/geometry.html g0 = QgsGeometry().fromPointXY(pt) else: g0 = QgsGeometry().fromPointXY(pt).combine( g0) # combine pour union car union reserve C++ buff = D / 100 P = g0.convexHull() B = P.buffer(buff, 5) DicoP4[key2] = [B, Liste_id, nb_pt] zPercent = int(100 * counterProgess / zDim) self.progressBar.setValue(zPercent) self.iface.mapCanvas().refresh() STATIONS = QgsVectorLayer( "MultiPolygon", "Polygons_under_AGGREGATES_D" + str(D) + "_OF_" + str(layerP.name()), "memory") QgsProject.instance().addMapLayer(STATIONS) prSTATIONS = STATIONS.dataProvider() listFieldsS = [] listFieldsS.append(QgsField("NumAggreg", QVariant.String)) listFieldsS.append(QgsField("List_Pts", QVariant.String)) listFieldsS.append(QgsField("Nb_Pts", QVariant.Int)) prSTATIONS.addAttributes(listFieldsS) STATIONS.startEditing() newfeatSTATIONS = QgsFeature() for keyP4 in DicoP4.keys(): GeomPoly = DicoP4[keyP4][0] newfeatSTATIONS = QgsFeature() newfeatSTATIONS.setGeometry(GeomPoly) toto = '' first = True for t in DicoP4[keyP4][1]: if first: first = False toto = str(t) else: toto = toto + ' - ' + str(t) NbObs = DicoP4[keyP4][2] ValuesSTATIONS = [keyP4] ValuesSTATIONS.append(toto) ValuesSTATIONS.append(NbObs) newfeatSTATIONS.setAttributes(ValuesSTATIONS) prSTATIONS.addFeatures([newfeatSTATIONS]) STATIONS.commitChanges() iface.mapCanvas().refresh() SEGMENTS = QgsVectorLayer( "MultiLineString", "Lines_from_" + str(layerP.name()) + "_Aggregates_with_D" + str(D), "memory") QgsProject.instance().addMapLayer(SEGMENTS) prSEGMENTS = SEGMENTS.dataProvider() listFields = [] listFields.append(QgsField("NumAgregat", QVariant.String)) listFields.append(QgsField("Nb_Pts", QVariant.Int)) prSEGMENTS.addAttributes(listFields) SEGMENTS.startEditing() newfeatSEGMENTS = QgsFeature() attributs = [] for keyA in DicoA.keys(): DicoSeg = DicoA[keyA][3] NbObs = DicoA[keyA][0] firstSEG = True MultiLine = [] GeomLine = QgsGeometry for keyPair in DicoSeg.keys(): if DicoSeg[keyPair][3] == keyA: if firstSEG: firstSEG = False MultiLine = [] MultiLine = [DicoSeg[keyPair][0]] else: MultiLine.append(DicoSeg[keyPair][0]) GeomLine = QgsGeometry.fromMultiPolylineXY(MultiLine) NumAg = keyA newfeatSEGMENTS = QgsFeature() newfeatSEGMENTS.setGeometry(GeomLine) ValuesSEGMENTS = [NumAg] ValuesSEGMENTS.append(NbObs) newfeatSEGMENTS.setAttributes(ValuesSEGMENTS) prSEGMENTS.addFeatures([newfeatSEGMENTS]) SEGMENTS.commitChanges() iface.mapCanvas().refresh() # modification de la table de point initiale pour ajout d un numero d agregat # making of the modified point layer with aggregates code AGGREGATS = QgsVectorLayer( "Point", str(layerP.name()) + "_aggregated_with_D" + str(D), "memory") QgsProject.instance().addMapLayer(AGGREGATS) prAGGREGATS = AGGREGATS.dataProvider() fieldsP = layerP.fields() listFields = [] for f in fieldsP: znameField = f.name() Type = str(f.typeName()) if Type == 'Integer': listFields.append(QgsField(znameField, QVariant.Int)) if Type == 'Real': listFields.append(QgsField(znameField, QVariant.Double)) if Type == 'String': listFields.append(QgsField(znameField, QVariant.String)) else: listFields.append(QgsField(znameField, QVariant.String)) listFields.append(QgsField("Point_id", QVariant.String)) listFields.append(QgsField("NumAggreg", QVariant.String)) listFields.append(QgsField("Nb_Pts", QVariant.Int)) listFields.append(QgsField("List_Pts", QVariant.String)) prAGGREGATS.addAttributes(listFields) AGGREGATS.startEditing() newfeatAGGREGATS = QgsFeature() attributs = [] for featP in layerP.getFeatures(): attributs = featP.attributes() Point_id = featP.id() geomP1 = featP.geometry() NbObs = 1 NumAgregat = 0 for keyP4 in DicoP4.keys(): #GeomPoly=DicoP4[keyP4][0] #if geomP1.intersects(GeomPoly): for ptid in DicoP4[keyP4][1]: if Point_id == ptid: NbObs = DicoP4[keyP4][2] toto = '' first = True for t in DicoP4[keyP4][1]: if first: first = False toto = str(t) else: toto = toto + ' - ' + str(t) list_id = toto NumAgregat = keyP4 newfeatAGGREGATS = QgsFeature() newfeatAGGREGATS.setGeometry(geomP1) ValuesAGGREGATS = attributs ValuesAGGREGATS.append(Point_id) ValuesAGGREGATS.append(NumAgregat) ValuesAGGREGATS.append(NbObs) ValuesAGGREGATS.append(list_id) newfeatAGGREGATS.setAttributes(ValuesAGGREGATS) prAGGREGATS.addFeatures([newfeatAGGREGATS]) AGGREGATS.commitChanges() iface.mapCanvas().refresh()