def update_jobs_table(self): if self.jobs: table_model = JobsTableModel(self.jobs, self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.jobs_view.setModel(proxy_model) # Add "Details" buttons in cell for row in range(0, len(self.jobs)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) btn.setMinimumSize(QSize(75, 0)) btn.setMaximumSize(QSize(150, 16777215)) self.jobs_view.setIndexWidget(proxy_model.index(row, 5), btn) #self.jobs_view.widget(proxy_model.index(row, 2)).setTextAlignment(Qt.AlignCenter) # self.jobs_view.setMinimumSize(QSize(0, 100)) # self.jobs_view.setColumnWidth(1, 400) # self.jobs_view.setColumnWidth(2, 200) # self.jobs_view.setColumnWidth(3, 200) # self.jobs_view.setColumnWidth(4, 200) # self.jobs_view.setColumnWidth(5, 200) self.jobs_view.selectionModel().selectionChanged.connect( self.selection_changed)
class BigList(Ui_BigList, QWidget): itemselected = pyqtSignal(QModelIndex) closewidget = pyqtSignal() savewidget = pyqtSignal() def __init__(self, parent=None, centeronparent=False, showsave=True): super(BigList, self).__init__(parent) self.setupUi(self) self.centeronparent = centeronparent self.listView.clicked.connect(self.selected) self.saveButton.pressed.connect(self.savewidget.emit) self.closebutton.pressed.connect(self.closewidget.emit) self._index = None self.search.textEdited.connect(self.set_filter) self.filtermodel = QSortFilterProxyModel() self.filtermodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.listView.setModel(self.filtermodel) self.listView.setWordWrap(True) self.saveButton.setVisible(showsave) utils.install_touch_scroll(self.listView) def set_filter(self, text): self.filtermodel.setFilterRegExp(text + ".*") def selected(self, index): self._index = index self._index = self.filtermodel.mapToSource(index) self.itemselected.emit(self._index) def setmodel(self, model): self.filtermodel.setSourceModel(model) def setlabel(self, fieldname): self.fieldnameLabel.setText(fieldname) def currentindex(self): return self._index def setcurrentindex(self, index): if index is None: index = QModelIndex() if isinstance(index, int): index = self.listView.model().index(index, 0) self.listView.setCurrentIndex(index) def show(self): super(BigList, self).show() if self.centeronparent: width = self.parent().width() height = self.parent().height() self.move(width / 4, 0) self.resize(QSize(width / 2, height))
def setup_class_table(self, classes): default_codes = sorted( [c['Initial_Code'] for c in self.default_classes]) input_codes = sorted([c['Initial_Code'] for c in classes]) new_codes = [c for c in input_codes if c not in default_codes] missing_codes = [c for c in default_codes if c not in input_codes] if len(new_codes) > 0: QtWidgets.QMessageBox.warning( None, self.tr("Warning"), self. tr(u"Some of the class codes ({}) in the definition file do not appear in the chosen data file." .format(', '.join([str(c) for c in new_codes]), None))) if len(missing_codes) > 0: QtWidgets.QMessageBox.warning( None, self.tr("Warning"), self. tr(u"Some of the class codes ({}) in the data file do not appear in the chosen definition file." .format(', '.join([str(c) for c in missing_codes]), None))) # Setup a new classes list with the new class codes for all classes # included in default calsses, and and any other class codes that are # missing added from the default class list classes = [c for c in classes if c['Initial_Code'] in default_codes] classes.extend([ c for c in self.default_classes if c['Initial_Code'] not in input_codes ]) table_model = AggTableModel(classes, parent=self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.remap_view.setModel(proxy_model) # Add selector in cell for row in range(0, len(classes)): lc_classes = QtWidgets.QComboBox() lc_classes.currentIndexChanged.connect(self.lc_class_combo_changed) # Add the classes in order of their codes lc_classes.addItems( sorted(list(self.final_classes.keys()), key=lambda k: self.final_classes[k])) ind = lc_classes.findText(classes[row]['Final_Label']) if ind != -1: lc_classes.setCurrentIndex(ind) self.remap_view.setIndexWidget( proxy_model.index(row, self.remap_view.model().columnCount() - 1), lc_classes) self.remap_view.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows) self.remap_view.setColumnWidth(1, 450) self.remap_view.horizontalHeader().setStretchLastSection(True) return True
class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) self.completer = QCompleter(self.pFilterModel, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.popup().setStyleSheet("min-height: 150px") self.completer.popup().setAlternatingRowColors(True) self.setCompleter(self.completer) self.lineEdit().textEdited[str].connect(self.pFilterModel.setFilterFixedString)
class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) self.completer = QCompleter(self.pFilterModel, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.popup().setStyleSheet('min-height: 150px') self.completer.popup().setAlternatingRowColors(True) self.setCompleter(self.completer) self.lineEdit().textEdited[unicode].connect( self.pFilterModel.setFilterFixedString)
def update_data_table(self): table_model = DataTableModel(self.datasets, self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.data_view.setModel(proxy_model) # Add "Notes" buttons in cell for row in range(0, len(self.datasets)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.data_view.setIndexWidget(proxy_model.index(row, 7), btn) self.data_view.horizontalHeader().setResizeMode( QtWidgets.QHeaderView.ResizeToContents) self.data_view.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows)
def set_model_by_list(string_list, widget, proxy_model): """ Set the model according to the list """ model = QStringListModel() model.setStringList(string_list) proxy_model.setSourceModel(model) proxy_model.setFilterKeyColumn(0) proxy_model_aux = QSortFilterProxyModel() proxy_model_aux.setSourceModel(model) proxy_model_aux.setFilterKeyColumn(0) widget.setModel(proxy_model_aux) widget.setModelColumn(0) completer = QCompleter() completer.setModel(proxy_model) completer.setCompletionColumn(0) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) widget.setCompleter(completer)
def config(self, token_model): space_model = XYZSpaceModel(self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(space_model) self.tableView_space.setModel(proxy_model) self.tableView_space.setSelectionMode( self.tableView_space.SingleSelection) self.tableView_space.setSelectionBehavior( self.tableView_space.SelectRows) self.tableView_space.setSortingEnabled(True) ############# connect gui self.tableView_space.pressed.connect(self.cb_table_row_selected) self.btn_use.clicked.connect(self._get_space_model().reset) TokenUX.config(self, token_model)
class ExtendedComboBox(QComboBox): """Extended class of QComboBox so we can perform a filtering of items. """ def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited[str].connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # on selection of an item from the completer, # select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of # the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column)
class FilteredComboBox(QComboBox): """Custom QComboBox with filtering option.""" def __init__(self, parent=None): super(FilteredComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) self.setEditable(True) self.filter_proxy_model = QSortFilterProxyModel(self) self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSortCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSourceModel(self.model()) self.completer = QCompleter(self.filter_proxy_model, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) self.setMinimumSize(150, 25) self.setFont(QFont("Segoe UI", 10)) self.setStyleSheet("QComboBox {background-color: white;}") self.setMaxVisibleItems(10) self.completer.activated.connect(self.on_completer_activated) self.lineEdit().textEdited.connect( self.filter_proxy_model.setFilterFixedString) def on_completer_activated(self, text): """Set active combobox item when a completer item is picked.""" if not text: return idx = self.findText(text) self.setCurrentIndex(idx) self.activated[str].emit(self.itemText(idx)) def setModel(self, model): """Set completer model after the combobox model.""" super(FilteredComboBox, self).setModel(model) self.filter_proxy_model.setSourceModel(model) self.completer.setModel(self.filter_proxy_model) def setModelColumn(self, column_idx): """Set the correct column for completer and combobox model using column index.""" self.completer.setCompletionColumn(column_idx) self.filter_proxy_model.setFilterKeyColumn(column_idx) super(FilteredComboBox, self).setModelColumn(column_idx)
def searchModel(self, columnIndex, columnValue): ''' Searches for 'columnValue' in the column whose index is specified by 'columnIndex' in all rows contained in the model. ''' if isinstance(columnValue, QVariant): columnValue = str(columnValue.toString()) if not isinstance(columnValue, str): columnValue = str(columnValue) columnValue = columnValue.strip() proxy = QSortFilterProxyModel(self) proxy.setSourceModel(self._tableModel) proxy.setFilterKeyColumn(columnIndex) proxy.setFilterFixedString(columnValue) # Will return model index containing the primary key. matchingIndex = proxy.mapToSource(proxy.index(0, 0)) return matchingIndex
def __init__( self, table_data, headers, parent=None, *args ): """ Creates two QTableViews one of which is a frozen table while the other one can scroll behind it. :param table_data: The data that goes into the tables :type table_data: List :param headers: The header data of the tables. :type headers: List :param parent: The parent of the QTableView :type parent: QWidget :param args: :type args: """ QTableView.__init__(self, parent) # set the table model self.table_model = BaseSTDMTableModel( table_data, headers, parent ) # set the proxy model proxy_model = QSortFilterProxyModel(self) proxy_model.setSourceModel(self.table_model) # Assign a data model for TableView self.setModel(self.table_model) # frozen_table_view - first column self.frozen_table_view = QTableView(self) # Set the model for the widget, fixed column self.frozen_table_view.setModel(self.table_model) # Hide row headers self.frozen_table_view.verticalHeader().hide() # Widget does not accept focus self.frozen_table_view.setFocusPolicy( Qt.StrongFocus | Qt.TabFocus | Qt.ClickFocus ) # The user can not resize columns self.frozen_table_view.horizontalHeader(). \ setSectionResizeMode(QHeaderView.Fixed) self.frozen_table_view.setObjectName('frozen_table') self.setSelectionMode(QAbstractItemView.NoSelection) # Remove the scroll bar self.frozen_table_view.setHorizontalScrollBarPolicy( Qt.ScrollBarAlwaysOff ) self.frozen_table_view.setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff ) # Puts more widgets to the foreground self.viewport().stackUnder(self.frozen_table_view) # # Log in to edit mode - even with one click # Set the properties of the column headings hh = self.horizontalHeader() # Text alignment centered hh.setDefaultAlignment(Qt.AlignCenter) self.set_column_width() # Set properties header lines vh = self.verticalHeader() vh.setDefaultSectionSize(25) # height lines # text alignment centered vh.setDefaultAlignment(Qt.AlignCenter) vh.setVisible(True) # Height of rows - as in the main widget self.frozen_table_view.verticalHeader(). \ setDefaultSectionSize( vh.defaultSectionSize() ) # Show frozen table view self.frozen_table_view.show() # Set the size of him like the main self.setHorizontalScrollMode( QAbstractItemView.ScrollPerPixel ) self.setVerticalScrollMode( QAbstractItemView.ScrollPerPixel ) self.frozen_table_view.setVerticalScrollMode( QAbstractItemView.ScrollPerPixel ) ## select the first column (STR Type) self.frozen_table_view.selectColumn(0) self.frozen_table_view.setEditTriggers( QAbstractItemView.AllEditTriggers ) self.set_size() self.signals()
class NafiDockWidget(QtWidgets.QDockWidget, Ui_NafiDockWidgetBase): closingPlugin = pyqtSignal() def __init__(self, parent=None): """Constructor.""" super(NafiDockWidget, self).__init__(parent) self.setupUi(self) # set up QTreeView self.treeView.setHeaderHidden(True) self.treeView.setSortingEnabled(True) self.treeView.setFocusPolicy(Qt.NoFocus) self.treeView.pressed.connect(self.treeViewPressed) # set up search signal self.lineEdit.textChanged.connect(self.searchTextChanged) self.searchText = "" # set up clear search self.clearSearchButton.clicked.connect(self.clearSearch) # set up About … dialog self.aboutButton.clicked.connect(self.showAboutDialog) # set up Download NAFI data … link button self.dataButton.clicked.connect( lambda: webbrowser.open(getNafiDataUrl())) # set up base model self.treeViewModel = NafiTreeViewModel(getNafiUrl()) # set up proxy model for filtering self.proxyModel = QSortFilterProxyModel(self.treeView) self.proxyModel.setSourceModel(self.treeViewModel) self.proxyModel.setRecursiveFilteringEnabled(True) self.treeView.setModel(self.proxyModel) self.reader = NafiCapabilitiesReader() self.reader.capabilitiesDownloaded.connect( lambda xml: self.initModel(xml)) # restore the view from source whenever this dock widget is made visible again self.visibilityChanged.connect( lambda visible: visible and self.loadNafiWms()) # initialise proxied tree view model from WMS contents # self.loadNafiWms() def loadNafiWms(self): """Load the NAFI WMS and additional layers.""" self.wmsUrl = getNafiUrl() self.reader.downloadCapabilities(self.wmsUrl) def initModel(self, wmsXml): """Initialise a QStandardItemModel from the NAFI WMS.""" googSat = GoogleXyzItem() googHyb = GoogleXyzItem("y") googStr = GoogleXyzItem("m") # ibraWms = IbraWmsItem() ozTopoWmts = OzTopoWmtsItem() self.treeViewModel.loadWms( self.wmsUrl, wmsXml, additionalItems=[googSat, googHyb, googStr, ozTopoWmts]) # set default sort and expansion self.proxyModel.sort(0, Qt.AscendingOrder) self.expandTopLevel() def expandTopLevel(self): # expand the top level items for row in range(self.proxyModel.rowCount()): self.treeView.expand(self.proxyModel.index(row, 0)) def treeViewPressed(self, index): """Load a NAFI WMS layer given an index in the tree view.""" assert isinstance( index, QModelIndex), "Supplied parameter is not a QModelIndex" realIndex = self.proxyModel.mapToSource(index) modelNode = self.treeViewModel.itemFromIndex(realIndex) # if we've got a layer and not a layer group, add to map if modelNode is not None: if isinstance( modelNode, (GoogleXyzItem, IbraWmsItem, OzTopoWmtsItem, WmsItem)): modelNode.addLayer() def searchTextChanged(self, text): """Process a change in the search filter text.""" # user adding characters and has exceeded 3 or more, or is removing characters if len(text) >= 3 or len(self.searchText) > len(text): regex = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.proxyModel.setFilterRegExp(regex) self.treeView.expandAll() # update last search text state self.searchText = text def clearSearch(self): """Clear search data.""" self.lineEdit.setText(None) self.treeView.collapseAll() def sizeHint(self): return QtCore.QSize(150, 400) def showAboutDialog(self): """Show an About … dialog.""" aboutDialog = NafiAboutDialog() aboutDialog.exec_() def closeEvent(self, event): """Handle plug-in close.""" self.closingPlugin.emit() event.accept()
class RuimtelijkePlannen(object): """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface and the mapCanvas self.iface = iface self.mapcanvas = iface.mapCanvas() # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale if not QSettings().value('locale/userLocale'): locale = QSettings().value('locale/globalLocale')[0:2] else: locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'RuimtelijkePlannen_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Ruimtelijke Plannen') self.toolbar = self.iface.addToolBar(u'RuimtelijkePlannen') self.toolbar.setObjectName(u'RuimtelijkePlannen') self.nam = networkaccessmanager.NetworkAccessManager() self.project = QgsProject.instance() # initialize styles folder self.settings = QSettings() self.available_styles_folder = os.path.join(self.plugin_dir, 'styles') self.map_style = self.settings.value("RuimtelijkePlannen/map_style", "print") self.map_style_folder = os.path.join(self.available_styles_folder, self.map_style) if not os.path.isdir(self.map_style_folder): self.map_style = "print" self.map_style_folder = os.path.join(self.available_styles_folder, self.map_style) def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ return QCoreApplication.translate('RuimtelijkePlannen', message) def add_action(self, dialog, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ if dialog: self.dlg = dialog icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToWebMenu(self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" self.dlg = RuimtelijkePlannenDialog() self.settings_dlg = RuimtelijkePlannenSettingsDialog() self.add_action(dialog=None, icon_path=':/plugins/RuimtelijkePlannen/help.png', text=self.tr(u'Help'), callback=self.open_help, parent=self.iface.mainWindow(), add_to_toolbar=False) self.add_action(dialog=None, icon_path=':/plugins/RuimtelijkePlannen/settings.png', text=self.tr(u'Settings'), callback=self.run_settings, parent=self.iface.mainWindow(), add_to_toolbar=False) # add a button to click in map and find bestemmingsplannen self.action = self.add_action( dialog=None, icon_path=':/plugins/RuimtelijkePlannen/icon.png', text=self.tr(u'Click map query RuimtelijkePlannen'), callback=self.activate_tool, add_to_menu=True, status_tip=self.tr(u'Click map query RuimtelijkePlannen'), parent=self.iface.mainWindow()) self.action.setCheckable(True) # add it to the same group as the pan/zoom tools self.iface.actionPan().actionGroup().addAction(self.action) self.xytool = GetPointTool(self.mapcanvas, self.getRPplannenByPoint) self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(QtCore.Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("NL.IMRO.") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.addRPplanFromToolbar) self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) self.dlg.treeView_results.setModel(self.proxyModel) self.dlg.treeView_results.setEditTriggers( QAbstractItemView.NoEditTriggers) self.dlg.treeView_results.setSortingEnabled(True) self.dlg.treeView_results.doubleClicked.connect(self.loadRPplan) # have an info icon at hand for keuzehulp self.infoIcon = QIcon(':/plugins/RuimtelijkePlannen/info.png') # have the right crs at hand self.rp_crs = QgsCoordinateReferenceSystem(28992) ## urls to RuimtelijkePlannen ## this one is the traditional one # self.rp_search_url = "https://www.ruimtelijkeplannen.nl/web-roo/rest/search" # self.rp_search_id_resource = "/plan/id/" # self.rp_search_xy_resource = "/plannen/xy/" ## this one is new and has more metadata with the "keuzehulp"! self.rp_search_url = "https://www.ruimtelijkeplannen.nl/plannenservice" self.rp_search_id_resource = "/plannen/identificatie/" self.rp_search_xy_resource = "/plannen/xy/" self.rp_wfs_url = "https://afnemers.ruimtelijkeplannen.nl/afnemers2012/services" # the following lists of plantypes comes from: # https://www.ruimtelijkeplannen.nl/viewer/planoverzicht self.rp_supported_planTypes = [\ 'aanwijzingsbesluit', 'beheersverordening', 'bestemmingsplan', 'exploitatieplan', 'gemeentelijk plan; bestemmingsplan artikel 10', 'gemeentelijk plan; uitwerkingsplan artikel 11', 'gemeentelijk plan; voorbereidingsbesluit', 'gemeentelijk plan; wijzigingsplan artikel 11', 'gemeentelijke visie; overig', 'gerechtelijke uitspraak', 'inpassingsplan', 'omgevingsvergunning', 'projectbesluit', 'reactieve aanwijzing', 'tijdelijke ontheffing buitenplans', 'uitwerkingsplan', 'voorbereidingsbesluit', 'wijzigingsplan' ] self.rp_not_supported_plantypes = [\ # these are the plan types which can have multiple maps 'amvb', 'provinciale verordening', 'regeling', 'structuurvisie' ] # layer action on RuimtelijkePlannen layers to show links to text self.rp_layer_action = QgsAction(QgsAction.GenericPython, 'Open text link(s) in browser ', layer_action, "", False, "", {'Feature', 'Canvas'}) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginWebMenu(self.tr(u'&Ruimtelijke Plannen'), action) self.iface.removeToolBarIcon(action) del self.toolbar def open_help(self): ''' Method to open the help pages ''' QDesktopServices().openUrl( QUrl.fromLocalFile( os.path.join("file://", self.plugin_dir, 'help/build/html', 'index.html'))) def run_settings(self): ''' method showing the settings dialog and act on the results ''' # we do this on opening this setting, so the user can easily add # new styles wthout reloading the plugin. available_styles = next(os.walk(self.available_styles_folder))[1] self.settings_dlg.styleComboBox.clear() self.settings_dlg.styleComboBox.addItems(available_styles) index = self.settings_dlg.styleComboBox.findText(self.map_style) self.settings_dlg.styleComboBox.setCurrentIndex(index) self.settings_dlg.show() result = self.settings_dlg.exec_() if result: map_style_folder = os.path.join( self.available_styles_folder, self.settings_dlg.styleComboBox.currentText()) if os.path.isdir(map_style_folder): self.map_style = self.settings_dlg.styleComboBox.currentText() self.map_style_folder = os.path.join( self.available_styles_folder, self.map_style) self.settings.setValue("RuimtelijkePlannen/map_style", self.map_style) else: self.iface.messageBar().pushMessage( 'Warning', self. tr(u"Selected style folder not found. See message log for details." ), level=Qgis.Warning) QgsMessageLog.logMessage(u'Style folder not found: ' + \ os.path.join(self.available_styles_folder, self.map_style), 'RuimtelijkePlannen') def activate_tool(self): self.mapcanvas.setMapTool(self.xytool) def addRPplanFromToolbar(self): '''slot for toolbarSearch''' self.addFromIdn(self.toolbarSearch.text()) def addFromIdn(self, search_string): '''adds plan from idn''' self.toolbarSearch.clear() url = self.rp_search_url + self.rp_search_id_resource + search_string QgsMessageLog.logMessage(u'Query: ' + str(url), 'RuimtelijkePlannen') QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) (response, content) = self.nam.request(url) self.rp = json.loads(content.decode("utf-8")) QApplication.restoreOverrideCursor() if "ErrorType" in self.rp \ or ('aantal' in self.rp and self.rp['aantal'] == 0): self.iface.messageBar().pushMessage("Error", self.tr(u'Plan not found.'), level=Qgis.Critical) return if 'resultaat' in self.rp: # we are using the keuzeHulp self.rp = self.rp['plannen'][0] if not self.rp["typePlan"] in self.rp_supported_planTypes: self.iface.messageBar().pushMessage( "Error", self.tr(u'Plan type not supported.'), level=Qgis.Critical) else: self.addRPplan_WFS(plangebied=search_string, plantype=self.rp["typePlan"]) def getWfsLayer(self, service, typename, wfs_filter): params = { 'service': 'WFS', 'version': '1.1.0', 'request': 'GetFeature', 'typename': typename, 'filter': wfs_filter, 'srsname': 'EPSG:28992', } if not service[-1] == '?': service += '?' uri = service + urllib.parse.unquote(urllib.parse.urlencode(params)) layername = typename vlayer = QgsVectorLayer(uri, layername, "WFS") return vlayer def addRPplan_WFS(self, plangebied, plantype): '''adds a plan as a WFS layer''' QgsMessageLog.logMessage(u'Adding plan with IDN "%s" and plantype "%s"'\ % (plangebied,plantype), 'RuimtelijkePlannen') # add a layergroup to insert the layers in bpGroup = QgsProject.instance().layerTreeRoot().insertGroup( 0, plangebied) service = self.rp_wfs_url wfs_filter = '"plangebied"=\'' + plangebied + '\'' plan = rp_plan(plantype) for layer in plan.layers: vlayer = self.getWfsLayer(service, layer['name'], wfs_filter) if vlayer.isValid(): if 'qml' in layer: layerQml = os.path.join(self.map_style_folder, layer['qml']) if os.path.exists(layerQml): vlayer.loadNamedStyle(layerQml) else: self.iface.messageBar().pushMessage( 'Warning', self. tr(u"Style not found. See message log for details." ), level=Qgis.Warning) QgsMessageLog.logMessage(u'Style file not found: ' + \ str(layerQml), 'RuimtelijkePlannen') if layer['text_action']: vlayer.actions().addAction(self.rp_layer_action) self.project.addMapLayer(vlayer, False) bpGroup.insertChildNode(-1, QgsLayerTreeLayer(vlayer)) else: self.iface.messageBar().pushMessage( 'Warning', self.tr(u"Invalid layer: ") + layer['name'], level=Qgis.Warning) vlayer.selectAll() canvas = self.iface.mapCanvas() canvas.zoomToSelected(vlayer) vlayer.removeSelection() def loadRPplan(self, index): '''slot for row in search results widget''' self.dlg.hide() self.addRPplan_WFS(plangebied=index.sibling(index.row(), 1).data(), plantype=index.sibling(index.row(), 3).data()) def addSourceRow(self, nr, plan, keuze_hulp=None): '''adds plan to search results widget''' if plan["typePlan"] in self.rp_supported_planTypes: nr = QStandardItem(str(nr + 1)) planId = QStandardItem(plan["identificatie"]) if keuze_hulp: planStatus = QStandardItem(self.infoIcon, plan["planStatus"]) f = planStatus.font() f.setUnderline(True) planStatus.setFont(f) planStatus.setToolTip( '%s: %s' % (keuze_hulp["positieTekst"], keuze_hulp["keuzeHulpTekst"])) else: planStatus = QStandardItem(plan["planStatus"]) planNaam = QStandardItem(plan["naam"]) planType = QStandardItem(plan["typePlan"]) #planGebiedType = QStandardItem(plan["planGebiedType"]) # not yet(?) in use by this plugin self.sourceModel.appendRow( [nr, planId, planStatus, planType, planNaam]) def addSourceRowFromKeuzeHulp(self, nr, plan_en_keuze): '''adds plan to search results widget''' keuze_hulp = plan_en_keuze['keuzeHulp'] plan = plan_en_keuze['planDto'] self.addSourceRow(nr, plan, keuze_hulp) def getRPplannenByPoint(self, event): '''Queries ruimtelijkeplannen by point and shows results on widget.''' xform = QgsCoordinateTransform( self.mapcanvas.mapSettings().destinationCrs(), self.rp_crs, QgsProject.instance()) xy = xform.transform( QgsPointXY( self.mapcanvas.getCoordinateTransform().toMapCoordinates( event.pos().x(), event.pos().y()))) self.sourceModel.clear() url = self.rp_search_url + self.rp_search_xy_resource + str( xy.x()) + "/" + str(xy.y()) QgsMessageLog.logMessage(u'Query: ' + str(url), 'RuimtelijkePlannen') QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) (response, content) = self.nam.request(url) self.rp = json.loads(content.decode("utf-8")) if "ErrorType" in self.rp: self.iface.messageBar().pushMessage("Error", self.tr(u'No Plans found.') + self.rp["ErrorDescription"], level=Qgis.Critical) QApplication.restoreOverrideCursor() return if 'plannen' in self.rp: for nr, plan in enumerate(self.rp["plannen"]): self.addSourceRow(nr, plan) else: for nr, plan_en_keuze in enumerate(self.rp["keuzeHulpPlannen"]): self.addSourceRowFromKeuzeHulp(nr, plan_en_keuze) if not self.sourceModel.rowCount(): QApplication.restoreOverrideCursor() return self.sourceModel.setHeaderData(0, QtCore.Qt.Horizontal, "Nr") self.sourceModel.setHeaderData(1, QtCore.Qt.Horizontal, "Identification") self.sourceModel.horizontalHeaderItem(1).setTextAlignment( QtCore.Qt.AlignLeft) self.sourceModel.setHeaderData(2, QtCore.Qt.Horizontal, "Status") self.sourceModel.setHeaderData(3, QtCore.Qt.Horizontal, "Type") self.sourceModel.setHeaderData(4, QtCore.Qt.Horizontal, "Name") self.dlg.treeView_results.setColumnHidden(0, True) self.dlg.treeView_results.resizeColumnsToContents() self.dlg.treeView_results.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.dlg.treeView_results.setSelectionMode(1) self.dlg.treeView_results.selectRow(0) # sort on order of the keuzehulp, instead of status. #self.dlg.treeView_results.sortByColumn(2, QtCore.Qt.AscendingOrder) self.dlg.treeView_results.sortByColumn(0, QtCore.Qt.AscendingOrder) QApplication.restoreOverrideCursor() self.dlg.show()
class STRViewEntityWidget(WIDGET2, BASE2, EntitySearchItem): """ A widget that represents options for searching through an entity. """ asyncStarted = pyqtSignal() asyncFinished = pyqtSignal() def __init__(self, config, formatter=None, parent=None): QWidget.__init__(self, parent) EntitySearchItem.__init__(self, formatter) self.setupUi(self) self.tbSTRViewEntity.setTabIcon(0, GuiUtils.get_icon('filter.png')) self.tbSTRViewEntity.setTabIcon(1, GuiUtils.get_icon('period_blue.png')) self.config = config self.setConfigOptions() self.curr_profile = current_profile() self.social_tenure = self.curr_profile.social_tenure self.str_model = entity_model(self.social_tenure) # Model for storing display and actual mapping values self._completer_model = None self._proxy_completer_model = None # Hook up signals self.cboFilterCol.currentIndexChanged.connect( self._on_column_index_changed) self.init_validity_dates() self.validity_from_date.dateChanged.connect(self.set_minimum_to_date) self.validity.setDisabled(True) self.init_validity_checkbox() def init_validity_checkbox(self): self.check_box_list = [] self.validity_checkbox = QCheckBox() self.check_box_list.append(self.validity_checkbox) self.tbSTRViewEntity.tabBar().setTabButton( self.tbSTRViewEntity.tabBar().count() - 1, QTabBar.LeftSide, self.validity_checkbox) self.validity_checkbox.stateChanged.connect( self.toggle_validity_period) def toggle_validity_period(self, state): if state == Qt.Checked: self.validity.setDisabled(False) else: self.validity.setDisabled(True) def set_minimum_to_date(self): """ Set the minimum to date based on the change in value of from date. :return: :rtype: """ self.validity_to_date.setMinimumDate(self.validity_from_date.date()) def init_validity_dates(self): """ Initialize the dates by setting the current date. :return: :rtype: """ self.validity_from_date.setDate(date.today()) self.validity_to_date.setDate(date.today()) def setConfigOptions(self): """ Apply configuration options. """ # Set filter columns and remove id column for col_name, display_name in self.config.filterColumns.items(): if col_name != "id": self.cboFilterCol.addItem(display_name, col_name) def loadAsync(self): """ Asynchronously loads an entity's attribute values. """ self.asyncStarted.emit() # Create model worker workerThread = QThread(self) modelWorker = ModelWorker() modelWorker.moveToThread(workerThread) # Connect signals modelWorker.error.connect(self.errorHandler) workerThread.started.connect(lambda: modelWorker.fetch( self.config.STRModel, self.currentFieldName())) modelWorker.retrieved.connect(self._asyncFinished) modelWorker.retrieved.connect(workerThread.quit) workerThread.finished.connect(modelWorker.deleteLater) workerThread.finished.connect(workerThread.deleteLater) # Start thread workerThread.start() def validate(self): """ Validate entity search widget """ is_valid = True message = "" if self.txtFilterPattern.text() == "": message = QApplication.translate("ViewSTR", "Search word cannot be empty.") is_valid = False return is_valid, message def executeSearch(self): """ Base class override. Search for matching items for the specified entity and column. """ model_root_node = None prog_dialog = QProgressDialog(self) prog_dialog.setFixedWidth(380) prog_dialog.setWindowTitle( QApplication.translate("STRViewEntityWidget", "Searching for STR...")) prog_dialog.show() prog_dialog.setRange(0, 10) search_term = self._searchTerm() prog_dialog.setValue(2) # Try to get the corresponding search term value from the completer model if not self._completer_model is None: reg_exp = QRegExp("^%s$" % (search_term), Qt.CaseInsensitive, QRegExp.RegExp2) self._proxy_completer_model.setFilterRegExp(reg_exp) if self._proxy_completer_model.rowCount() > 0: # Get corresponding actual value from the first matching item value_model_idx = self._proxy_completer_model.index(0, 1) source_model_idx = self._proxy_completer_model.mapToSource( value_model_idx) prog_dialog.setValue(4) search_term = self._completer_model.data( source_model_idx, Qt.DisplayRole) modelInstance = self.config.STRModel() modelQueryObj = modelInstance.queryObject() queryObjProperty = getattr(self.config.STRModel, self.currentFieldName()) entity_name = modelQueryObj._primary_entity._label_name entity = self.curr_profile.entity_by_name(entity_name) prog_dialog.setValue(6) # Get property type so that the filter can # be applied according to the appropriate type propType = queryObjProperty.property.columns[0].type results = [] try: if not isinstance(propType, String): col_name = self.currentFieldName() col = entity.columns[self.currentFieldName()] if col.TYPE_INFO == 'LOOKUP': lookup_entity = lookup_parent_entity( self.curr_profile, col_name) lkp_model = entity_model(lookup_entity) lkp_obj = lkp_model() value_obj = getattr(lkp_model, 'value') result = lkp_obj.queryObject().filter( func.lower(value_obj) == func.lower( search_term)).first() if result is None: result = lkp_obj.queryObject().filter( func.lower(value_obj).like(search_term + '%')).first() if not result is None: results = modelQueryObj.filter( queryObjProperty == result.id).all() else: results = [] else: results = modelQueryObj.filter( func.lower(queryObjProperty) == func.lower( search_term)).all() if self.validity.isEnabled(): valid_str_ids = self.str_validity_period_filter(results) else: valid_str_ids = None prog_dialog.setValue(7) except exc.StatementError: return model_root_node, [], search_term # if self.formatter is not None: # self.formatter.setData(results) # model_root_node = self.formatter.root(valid_str_ids) prog_dialog.setValue(10) prog_dialog.hide() return results, search_term def str_validity_period_filter(self, results): """ Filter the entity results using validity period in STR table. :param results: Entity result :type results: SQLAlchemy result proxy :return: Valid list of STR ids :rtype: List """ self.str_model_obj = self.str_model() valid_str_ids = [] for result in results: from_date = self.validity_from_date.date().toPyDate() to_date = self.validity_to_date.date().toPyDate() entity_id = '{}_id'.format(result.__table__.name[3:]) str_column_obj = getattr(self.str_model, entity_id) str_result = self.str_model_obj.queryObject().filter( self.str_model.validity_start >= from_date).filter( self.str_model.validity_end <= to_date).filter( str_column_obj == result.id).all() for res in str_result: valid_str_ids.append(res.id) return valid_str_ids def reset(self): """ Clear search input parameters. """ self.txtFilterPattern.clear() if self.cboFilterCol.count() > 0: self.cboFilterCol.setCurrentIndex(0) def currentFieldName(self): """ Returns the name of the database field from the current item in the combo box. """ curr_index = self.cboFilterCol.currentIndex() field_name = self.cboFilterCol.itemData(curr_index) if field_name is None: return else: return field_name def _searchTerm(self): """ Returns the search term specified by the user. """ return self.txtFilterPattern.text() def _asyncFinished(self, model_values): """ Slot raised when worker has finished retrieving items. """ # Create QCompleter and add values to it. self._update_completer(model_values) self.asyncFinished.emit() def _update_completer(self, values): # Get the items in a tuple and put them in a list # Store display and actual values in a # model for easier mapping and # retrieval when carrying out searches model_attr_mapping = [] # Check if there are formaters specified # for the current field name for mv in values: f_model_values = [] m_val = mv[0] if m_val is not None: col_label = self.currentFieldName() if col_label in self.config.LookupFormatters: formatter = self.config.LookupFormatters[col_label] if formatter.column.TYPE_INFO == 'LOOKUP': m_val = formatter.code_value(m_val)[0] else: m_val = formatter.format_column_value(m_val) f_model_values.extend([m_val, m_val]) model_attr_mapping.append(f_model_values) self._completer_model = BaseSTDMTableModel(model_attr_mapping, ["", ""], self) # We will use the QSortFilterProxyModel for filtering purposes self._proxy_completer_model = QSortFilterProxyModel() self._proxy_completer_model.setDynamicSortFilter(True) self._proxy_completer_model.setSourceModel(self._completer_model) self._proxy_completer_model.setSortCaseSensitivity(Qt.CaseInsensitive) self._proxy_completer_model.setFilterKeyColumn(0) # Configure completer mod_completer = QCompleter(self._completer_model, self) mod_completer.setCaseSensitivity(Qt.CaseInsensitive) mod_completer.setCompletionMode(QCompleter.PopupCompletion) mod_completer.setCompletionColumn(0) mod_completer.setCompletionRole(Qt.DisplayRole) self.txtFilterPattern.setCompleter(mod_completer) def _on_column_index_changed(self, int): """ Slot raised when the user selects a different filter column. """ self.txtFilterPattern.clear() self.loadAsync()
class DlgDownload(DlgCalculateBase, Ui_DlgDownload): def __init__(self, parent=None): """Constructor.""" super(DlgDownload, self).__init__(parent) # Allow the download tool to support data downloads of any size (in # terms of area) self._max_area = 1e10 self.settings = QSettings() self.setupUi(self) with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'gee_datasets.json')) as f: data_dict = json.load(f) self.datasets = [] for cat in list(data_dict.keys()): for title in list(data_dict[cat].keys()): item = data_dict[cat][title] item.update({'category': cat, 'title': title}) min_x = item.get('Min Longitude', None) max_x = item.get('Max Longitude', None) min_y = item.get('Min Latitude', None) max_y = item.get('Max Latitude', None) if not None in (min_x, max_x, min_y, max_y): extent_lat = '{} - {}'.format(min_y, max_y) extent_lon = '{} - {}'.format(min_x, max_x) item.update({'extent_lat': extent_lat, 'extent_lon': extent_lon}) self.datasets.append(item) self.update_data_table() self.data_view.selectionModel().selectionChanged.connect(self.selection_changed) self.data_view.viewport().installEventFilter(tool_tipper(self.data_view)) def selection_changed(self): if self.data_view.selectedIndexes(): # Note there can only be one row selected at a time by default row = list(set(index.row() for index in self.data_view.selectedIndexes()))[0] first_year = self.datasets[row]['Start year'] last_year = self.datasets[row]['End year'] if (first_year == 'NA') or (last_year == 'NA'): self.first_year.setEnabled(False) self.last_year.setEnabled(False) else: self.first_year.setEnabled(True) self.last_year.setEnabled(True) first_year = QDate(first_year, 12, 31) last_year = QDate(last_year, 12, 31) self.first_year.setMinimumDate(first_year) self.first_year.setMaximumDate(last_year) self.last_year.setMinimumDate(first_year) self.last_year.setMaximumDate(last_year) def tab_changed(self): super(DlgDownload, self).tab_changed() if (self.TabBox.currentIndex() == (self.TabBox.count() - 1)) \ and not self.data_view.selectedIndexes(): # Only enable download if a dataset is selected self.button_calculate.setEnabled(False) def firstShow(self): super(DlgDownload, self).firstShow() # Don't show the time selector for now self.TabBox.removeTab(1) def showEvent(self, event): super(DlgDownload, self).showEvent(event) # Don't local/cloud selector for this dialog self.options_tab.toggle_show_where_to_run(False) def update_data_table(self): table_model = DataTableModel(self.datasets, self) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(table_model) self.data_view.setModel(self.proxy_model) # Add "Notes" buttons in cell for row in range(0, len(self.datasets)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.data_view.setIndexWidget(self.proxy_model.index(row, 8), btn) self.data_view.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) self.data_view.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) self.data_view.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch) self.data_view.horizontalHeader().setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(4, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(5, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(6, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(7, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(8, QtWidgets.QHeaderView.ResizeToContents) self.data_view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) def btn_details(self): button = self.sender() index = self.data_view.indexAt(button.pos()) #TODO: Code the details view def btn_calculate(self): # Note that the super class has several tests in it - if they fail it # returns False, which would mean this function should stop execution # as well. ret = super(DlgDownload, self).btn_calculate() if not ret: return rows = list(set(index.row() for index in self.data_view.selectedIndexes())) # Construct unique dataset names as the concatenation of the category # and the title selected_names = [self.proxy_model.index(row, 0).data() + self.proxy_model.index(row, 1).data()for row in rows] selected_datasets = [d for d in self.datasets if d['category'] + d['title'] in selected_names] self.close() crosses_180th, geojsons = self.gee_bounding_box for dataset in selected_datasets: payload = {'geojsons': json.dumps(geojsons), 'crs': self.aoi.get_crs_dst_wkt(), 'year_start': self.first_year.date().year(), 'year_end': self.last_year.date().year(), 'crosses_180th': crosses_180th, 'asset': dataset['GEE Dataset'], 'name': dataset['title'], 'temporal_resolution': dataset['Temporal resolution'], 'task_name': self.options_tab.task_name.text(), 'task_notes': self.options_tab.task_notes.toPlainText()} resp = run_script(get_script_slug('download-data'), payload) if resp: mb.pushMessage(self.tr("Success"), self.tr("Download request submitted to Google Earth Engine."), level=0, duration=5) else: mb.pushMessage(self.tr("Error"), self.tr("Unable to submit download request to Google Earth Engine."), level=0, duration=5)
def makeProxyModel(self, model): proxyModel = QSortFilterProxyModel() proxyModel.setDynamicSortFilter(True) proxyModel.setSortCaseSensitivity(Qt.CaseInsensitive) proxyModel.setSourceModel(model) return proxyModel
class PdokServicesPlugin(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # docked or dialog, defaults to dialog # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if isinstance(QSettings().value("/pdokservicesplugin/docked"), QVariant): # self.docked = QSettings().value("/pdokservicesplugin/docked", QVariant(False)) # else: # self.docked = QSettings().value("/pdokservicesplugin/docked", False) # # # Create the dialog and keep reference # if "True" == self.docked or "true" == self.docked or True is self.docked: # self.dlg = PdokServicesPluginDockWidget() # self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dlg) # else: # self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDatabaseFilePath()).path() + "/python/plugins/pdokservicesplugin" # initialize locale localePath = "" if isinstance(QSettings().value("locale/userLocale"), QVariant): locale = QSettings().value("locale/userLocale").value()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/pdokservicesplugin_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.currentLayer = None self.SETTINGS_SECTION = '/pdokservicesplugin/' self.pointer = None self.pdokgeocoder = PDOKGeoLocator(self.iface) self.geocoderSourceModel = None def getSettingsValue(self, key, default=''): if QSettings().contains(self.SETTINGS_SECTION + key): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 return str(QSettings().value(key).toString()) else: return str(QSettings().value(key)) else: return default def setSettingsValue(self, key, value): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue(key, QVariant(value)) else: QSettings().setValue(key, value) def initGui(self): # Create action that will start plugin configuration self.run_action = QAction(QIcon(":/plugins/pdokservicesplugin/icon.png"), \ u"Pdok Services Plugin", self.iface.mainWindow()) self.servicesLoaded = False # connect the action to the run method # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if "True" == self.docked or "true" == self.docked or True == self.docked: # self.run_action.triggered.connect(self.showAndRaise) # self.dlg.radioDocked.setChecked(True) # # docked the dialog is immidiately visible, so should run NOW # else: # self.run_action.triggered.connect(self.run) # self.dlg.radioDocked.setChecked(False) # self.setupfq() self.run_action.triggered.connect(self.run) #self.dlg.radioDocked.setChecked(False) self.setupfq() # Add toolbar button and menu item #self.iface.addToolBarIcon(self.action) self.toolbar = self.iface.addToolBar("PDOK services plugin") self.toolbar.setObjectName("PDOK services plugin") self.toolbar.addAction(self.run_action) self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("PDOK Locatieserver zoek") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.searchAddressFromToolbar) # address/point cleanup self.clean_action = QAction(QIcon(":/plugins/pdokservicesplugin/eraser.png"), \ u"Cleanup", self.eraseAddress()) self.toolbar.addAction(self.clean_action) self.clean_action.triggered.connect(self.eraseAddress) self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.run_action) # about self.aboutAction = QAction(QIcon(":/plugins/pdokservicesplugin/help.png"), \ "About", self.iface.mainWindow()) self.aboutAction.setWhatsThis("Pdok Services Plugin About") self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.aboutAction) self.aboutAction.triggered.connect(self.about) self.dlg.ui.btnLoadLayer.clicked.connect(self.loadService) self.dlg.geocoderSearch.returnPressed.connect(self.searchAddress) self.dlg.geocoderSearch.textEdited.connect(self.searchAddress) self.dlg.geocoderSearch.setPlaceholderText("PDOK Locatieserver zoek, bv postcode of postcode huisnummer") self.dlg.geocoderResultSearch.textChanged.connect(self.filterGeocoderResult) self.dlg.geocoderResultSearch.setPlaceholderText("een of meer zoekwoorden uit resultaat") #self.dlg.radioDocked.toggled.connect(self.set_docked) self.dlg.btnCheckPdokJson.clicked.connect(self.checkPdokJson) #self.iface.mapCanvas().renderStarting.connect(self.extentsChanged) ui = self.dlg.ui cbxs = [ui.cbx_gem, ui.cbx_wpl, ui.cbx_weg, ui.cbx_pcd, ui.cbx_adr, ui.cbx_pcl, ui.cbx_hmp] # connect all fq checkboxes with suggest, so upon a change in fq filter we re-search for cbx in cbxs: cbx.stateChanged.connect(self.searchAddress) self.run(True) # for now hiding the pointer as soon as the extent changes #def extentsChanged(self): # self.removePointer() def checkPdokJson(self): myversion = self.getSettingsValue('pdokversion', '1') msgtxt = '' msglvl = 0 # QgsMessageBar.INFO try: response = urllib.request.urlopen('http://www.qgis.nl/pdok.version') str_response = response.read().decode('utf-8') pdokversion = json.loads(str_response) if pdokversion > int(myversion): response = urllib.request.urlopen('http://www.qgis.nl/pdok.json') str_response = response.read().decode('utf-8') pdokjson = json.loads(str_response) with open(self.plugin_dir +'/pdok.json', 'w') as outfile: json.dump(pdokjson, outfile) msgtxt = "De laatste versie is opgehaald en zal worden gebruikt " + \ str(pdokversion) + ' (was ' + myversion +')' self.servicesLoaded = False # reset reading of json self.run() self.setSettingsValue('pdokversion', pdokversion) else: msgtxt = "Geen nieuwere versie beschikbaar dan " + str(pdokversion) except Exception as e: #print e msgtxt = "Fout bij ophalen van service info. Netwerk probleem?" msglvl = 2 # QgsMessageBar.CRITICAL # msg if hasattr(self.iface, 'messageBar'): self.iface.messageBar().pushMessage("PDOK services update", msgtxt, level=msglvl, duration=10) else: # 1.8 QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin", msgtxt) # def set_docked(self, foo): # self.setSettingsValue('docked', self.dlg.radioDocked.isChecked()) # #if Qgis.QGIS_VERSION_INT < 10900: # # # qgis <= 1.8 # # QSettings().setValue("/pdokservicesplugin/docked", QVariant(self.dlg.radioDocked.isChecked())) # #else: # # QSettings().setValue("/pdokservicesplugin/docked", self.dlg.radioDocked.isChecked()) def showAndRaise(self): self.dlg.show() self.dlg.raise_() # also remove the pointer self.removePointer() def about(self): infoString = "Written by Richard Duivenvoorde\nEmail - [email protected]\n" infoString += "Company - Zuidt - http://www.zuidt.nl\n" infoString += "Source: https://github.com/rduivenvoorde/pdokservicesplugin" QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin About", infoString) def unload(self): self.removePointer() # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Pdok Services Plugin",self.run_action) del self.toolbarSearch def showService(self, selectedIndexes): if len(selectedIndexes)==0: self.currentLayer = None self.dlg.ui.layerInfo.setHtml('') self.dlg.ui.comboSelectProj.clear() return # needed to scroll To the selected row incase of using the keyboard / arrows self.dlg.servicesView.scrollTo(self.dlg.servicesView.selectedIndexes()[0]) # itemType holds the data (== column 1) self.currentLayer = self.dlg.servicesView.selectedIndexes()[1].data(Qt.UserRole) if isinstance(self.currentLayer, QVariant): self.currentLayer = self.currentLayer.toMap() # QGIS 1.8: QVariants currentLayer = {} for key in list(self.currentLayer.keys()): val = self.currentLayer[key] currentLayer[str(key)]=str(val.toString()) self.currentLayer = currentLayer url = self.currentLayer['url'] title = self.currentLayer['title'] style = '' if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' servicetitle = self.currentLayer['servicetitle'] layername = self.currentLayer['layers'] abstract = self.currentLayer['abstract'] stype = self.currentLayer['type'].upper() minscale ='' if 'minscale' in self.currentLayer and self.currentLayer['minscale'] != None and self.currentLayer['minscale'] != '': minscale = "min. schaal 1:"+self.currentLayer['minscale'] maxscale = '' if 'maxscale' in self.currentLayer and self.currentLayer['maxscale'] != None and self.currentLayer['maxscale'] != '': maxscale = "max. schaal 1:"+self.currentLayer['maxscale'] self.dlg.ui.layerInfo.setText('') self.dlg.ui.btnLoadLayer.setEnabled(True) self.dlg.ui.layerInfo.setHtml('<h4>%s</h4><h3>%s</h3><lu><li>%s</li><li> </li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li></lu>' % (servicetitle, title, abstract, stype, url, layername, style, minscale, maxscale)) self.dlg.ui.comboSelectProj.clear() if stype=="WMS": try: crs = self.currentLayer['crs'] except KeyError: crs = 'EPSG:28992' crs = crs.split(',') self.dlg.ui.comboSelectProj.addItems(crs) for i in range(len(crs)): if crs[i] == 'EPSG:28992': self.dlg.ui.comboSelectProj.setCurrentIndex(i) if stype=="WMTS": tilematrixsets = self.currentLayer['tilematrixsets'].split(',') self.dlg.ui.comboSelectProj.addItems(tilematrixsets) for i in range(len(tilematrixsets)): if tilematrixsets[i].startswith('EPSG:28992'): self.dlg.ui.comboSelectProj.setCurrentIndex(i) def loadService(self): if self.currentLayer == None: return servicetype = self.currentLayer['type'] url = self.currentLayer['url'] # some services have an url with query parameters in it, we have to urlencode those: location,query = urllib.parse.splitquery(url) url = location if query != None and query != '': url +=('?'+urllib.parse.quote_plus(query)) title = self.currentLayer['title'] if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' else: style = '' # == default for this service layers = self.currentLayer['layers'] # mmm, tricky: we take the first one while we can actually want png/gif or jpeg if servicetype == "wms": imgformat = self.currentLayer['imgformats'].split(',')[0] if self.dlg.ui.comboSelectProj.currentIndex() == -1: crs = 'EPSG:28992' else: crs = self.dlg.ui.comboSelectProj.currentText() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 uri = url self.iface.addRasterLayer( uri, # service uri title, # name for layer (as seen in QGIS) "wms", # dataprovider key [layers], # array of layername(s) for provider (id's) [""], # array of stylename(s) NOTE: ignoring styles here!!! imgformat, # image format searchstring crs) # crs code searchstring else: # qgis > 1.8 uri = "crs="+crs+"&layers="+layers+"&styles="+style+"&format="+imgformat+"&url="+url; self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wmts": if Qgis.QGIS_VERSION_INT < 10900: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen in deze versie van QGIS.\nMisschien kunt u QGIS 2.0 installeren (die kan het WEL)?\nOf is de laag niet ook beschikbaar als wms of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return if self.dlg.ui.comboSelectProj.currentIndex() == -1: tilematrixset = 'EPSG:28992' else: tilematrixset = self.dlg.ui.comboSelectProj.currentText() imgformat = self.currentLayer['imgformats'].split(',')[0] # special case for luchtfoto #if layers=="luchtfoto": # # tileMatrixSet=nltilingschema&crs=EPSG:28992&layers=luchtfoto&styles=&format=image/jpeg&url=http://geodata1.nationaalgeoregister.nl/luchtfoto/wmts/1.0.0/WMTSCapabilities.xml # # {u'layers': u'luchtfoto', u'imgformats': u'image/jpeg', u'title': u'PDOK-achtergrond luchtfoto', u'url': u'http://geodata1.nationaalgeoregister.nl/luchtfoto/wms', u'abstract': u'', u'tilematrixsets': u'nltilingschema', u'type': u'wmts'} # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url #else: # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url; #uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; if tilematrixset.startswith('EPSG:'): crs=tilematrixset i = crs.find(':', 5) if i > -1: crs=crs[:i] elif tilematrixset.startswith('OGC:1.0'): crs='EPSG:3857' uri = "tileMatrixSet="+tilematrixset+"&crs="+crs+"&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; #print "############ PDOK URI #################" #print uri self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wfs": location, query = urllib.parse.splitquery(url) #uri = location+"?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME="+layers+"&SRSNAME=EPSG:28992" #uri = location + "?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=" + layers + "&SRSNAME=EPSG:28992" # adding a bbox paramater forces QGIS to NOT cache features but retrieve new features all the time # QGIS will update the BBOX to the right value #uri += "&BBOX=-10000,310000,290000,650000" uri = " pagingEnabled='true' restrictToRequestBBOX='1' srsname='EPSG:28992' typename='"+layers+"' url='"+url+"' version='2.0.0' " self.iface.addVectorLayer(uri, title, "WFS") elif servicetype == "wcs": # cache=AlwaysCache&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs uri = '' # cache=AlwaysCache # cache=PreferNetwork # cache=AlwaysNetwork # cache=AlwaysNetwork&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.1&identifier="+layers+"&url="+url # working for ahn1 ahn2 and ahn3: GEOTIFF_FLOAT32 format = 'GEOTIFF_FLOAT32' # working for ahn25m is only image/tiff if layers=='ahn25m': format = 'image/tiff' # we handcrated some wcs layers with 2 different image formats: tiff (RGB) and tiff (float32): if 'imgformats' in self.currentLayer: format = self.currentLayer['imgformats'].split(',')[0] uri = "cache=AlwaysNetwork&crs=EPSG:28992&format="+format+"&version=1.1.2&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.2&identifier=" + layers + "&url=" + url self.iface.addRasterLayer(uri, title, "wcs") else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen door de plugin of door QGIS.\nIs het niet beschikbaar als wms, wmts of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return def filterGeocoderResult(self, string): #print "filtering geocoder results: %s" % string self.dlg.geocoderResultView.selectRow(0) self.geocoderProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.geocoderProxyModel.setFilterFixedString(string) def searchAddressFromToolbar(self): self.removePointer() self.geocoderSourceModel.clear() self.geocode() def searchAddress(self): self.removePointer() #print "search geocoder for: %s" % self.dlg.geocoderSearch.text() self.geocoderSourceModel.clear() #self.geocode(self.dlg.geocoderSearch.text()) self.suggest() def eraseAddress(self): """ clean the input and remove the pointer """ self.removePointer() if self.geocoderSourceModel is not None: self.geocoderSourceModel.clear() if self.dlg.geocoderSearch is not None: self.dlg.geocoderSearch.clear() if self.toolbarSearch is not None: self.toolbarSearch.clear() def filterLayers(self, string): # remove selection if one row is selected self.dlg.servicesView.selectRow(0) #self.currentLayer = None self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterFixedString(string) #def addSourceRow(self, service, layer): def addSourceRow(self, serviceLayer): # you can attache different "data's" to to an QStandarditem # default one is the visible one: itemType = QStandardItem("%s" % (serviceLayer["type"].upper()) ) # userrole is a free form one: # only attach the data to the first item # service layer = a dict/object with all props of the layer itemType.setData( serviceLayer, Qt.UserRole ) itemType.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) # only wms services have styles (sometimes) layername = serviceLayer["title"] if 'style' in serviceLayer: itemLayername = QStandardItem("%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) ) layername = "%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) else: itemLayername = QStandardItem("%s" % (serviceLayer["title"])) itemLayername.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["servicetitle"] )) # itemFilter is the item used to search filter in. That is why layername is a combi of layername + filter here itemFilter = QStandardItem("%s %s %s %s" % (serviceLayer["type"], layername, serviceLayer["servicetitle"], serviceLayer["abstract"]) ) itemServicetitle = QStandardItem("%s" % (serviceLayer["servicetitle"])) itemServicetitle.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) self.sourceModel.appendRow( [ itemLayername, itemType, itemServicetitle, itemFilter ] ) # run method that performs all the real work def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value("/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget(int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers(QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText("woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect(self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable(False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel().selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True; self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth(0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer() def setupfq(self): """ Setup the fq checkboxes in the gui, by looking into the settings for the 'pdokservicesplugin/checkedfqs' key, which contains a list of type strings like ['weg','adres'] """ checked_fqs = self.getSettingsValue('checkedfqs', []) #self.info('setup fq: {}'.format(checked_fqs)) if len(checked_fqs) > 0: # else there is not saved state... take gui defaults self.dlg.ui.cbx_gem.setChecked('gemeente' in checked_fqs) self.dlg.ui.cbx_wpl.setChecked('woonplaats' in checked_fqs) self.dlg.ui.cbx_weg.setChecked('weg' in checked_fqs) self.dlg.ui.cbx_pcd.setChecked('postcode' in checked_fqs) self.dlg.ui.cbx_adr.setChecked('adres' in checked_fqs) self.dlg.ui.cbx_pcl.setChecked('perceel' in checked_fqs) self.dlg.ui.cbx_hmp.setChecked('hectometerpaal' in checked_fqs) def createfq(self): """ This creates a fq-string (Filter Query, see https://github.com/PDOK/locatieserver/wiki/Zoekvoorbeelden-Locatieserver) Based on the checkboxes in the dialog. Defaults to '' Example: 'fq=+type:adres+type:gemeente' (only gemeente AND addresses) :return: """ fqlist = [] if self.dlg.ui.cbx_gem.isChecked(): fqlist.append('gemeente') if self.dlg.ui.cbx_wpl.isChecked(): fqlist.append('woonplaats') if self.dlg.ui.cbx_weg.isChecked(): fqlist.append('weg') if self.dlg.ui.cbx_pcd.isChecked(): fqlist.append('postcode') if self.dlg.ui.cbx_adr.isChecked(): fqlist.append('adres') if self.dlg.ui.cbx_pcl.isChecked(): fqlist.append('perceel') if self.dlg.ui.cbx_hmp.isChecked(): fqlist.append('hectometerpaal') self.setSettingsValue('checkedfqs', fqlist) #self.info(self.getSettingsValue('checkedfqs', ['leeg?'])) fq = '' if len(fqlist) > 0: fq = '&fq=+type:' + '+type:'.join(fqlist) return fq def suggest(self): self.dlg.ui.lookupinfo.setHtml('') search_text = self.dlg.geocoderSearch.text() if len(search_text) <= 1: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "meer input aub: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) return # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "zoeken: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) results = self.pdokgeocoder.suggest(search_text, self.createfq()) if len(results) == 0: # ignore, as we are suggesting, maybe more characters will reveal something... return for result in results: #print address adrestekst = QStandardItem("%s" % (result["adrestekst"])) adrestekst.setData(result, Qt.UserRole) type = QStandardItem("%s" % (result["type"])) id = QStandardItem("%s" % (result["id"])) score = QStandardItem("%s" % (result["score"])) adrestekst.setData(result, Qt.UserRole) self.geocoderSourceModel.appendRow([adrestekst, type]) self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) self.dlg.geocoderResultView.resizeColumnsToContents() self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def geocode(self): self.dlg.geocoderSearch.setText(self.toolbarSearch.text()) self.suggest() if self.dlg.geocoderResultView.model().rowCount()>0: self.dlg.geocoderResultView.selectRow(0) self.zoomToAddress() else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ "Niets gevonden.\nProbeer een andere spelling, of alleen postcode/huisnummer?\n\nSelecteer meer (Locatieserver) 'typen' in de PdokServicesPlugin dialoog.\n\nOf gebruik de 'PDOK geocoder'-tab in de PdokServicesPlugin dialoog." ), QMessageBox.Ok, QMessageBox.Ok) # def geocode(self): # self.dlg.ui.lookupinfo.setHtml('') # search_text = self.toolbarSearch.text() # addresses = self.pdokgeocoder.search(search_text) # if len(addresses) == 0: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "Niets gevonden. Probeer een andere spelling of alleen postcode/huisnummer." # ), QMessageBox.Ok, QMessageBox.Ok) # return # for address in addresses: # #print address # adrestekst = QStandardItem("%s" % (address["adrestekst"])) # adrestekst.setData(address, Qt.UserRole) # straat = QStandardItem("%s" % (address["straat"])) # nummer = QStandardItem("%s" % (address["nummer"])) # postcode = QStandardItem("%s" % (address["postcode"])) # plaats = QStandardItem("%s" % (address["plaats"])) # gemeente = QStandardItem("%s" % (address["gemeente"])) # provincie = QStandardItem("%s" % (address["provincie"])) # self.geocoderSourceModel.appendRow([adrestekst, straat, nummer, postcode, plaats, gemeente, provincie]) # # self.dlg.geocoderResultView.selectRow(0) # self.zoomToAddress() # # self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") # self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Straat") # self.geocoderSourceModel.setHeaderData(2, Qt.Horizontal, "Nr") # self.geocoderSourceModel.setHeaderData(3, Qt.Horizontal, "Postcode") # self.geocoderSourceModel.setHeaderData(4, Qt.Horizontal, "Plaats") # self.geocoderSourceModel.setHeaderData(5, Qt.Horizontal, "Gemeente") # self.geocoderSourceModel.setHeaderData(6, Qt.Horizontal, "Provincie") # # self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(3).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(4).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(5).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(6).setTextAlignment(Qt.AlignLeft) # # self.dlg.geocoderResultView.resizeColumnsToContents() # self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data(Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data= data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml( '<h4>{}</h4><lu>{}</lu>'.format(adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh() def setPointer(self, point): self.removePointer() self.pointer = QgsVertexMarker(self.iface.mapCanvas()) self.pointer.setColor(QColor(255, 255, 0)) self.pointer.setIconSize(10) self.pointer.setPenWidth(5) self.pointer.setCenter(point) def removePointer(self): if self.pointer is not None: self.iface.mapCanvas().scene().removeItem(self.pointer) def info(self, msg=""): QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin', Qgis.Info)
class ExtendedCombobox(QComboBox): """ Overwrite combobox to provide text filtering of combobox list. """ def __init__(self, parent): """ Initialise ExtendedCombobox :param parent: Parent of combobox :type parent: PyQt5.QtWidgets.QWidget """ super().__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.p_filter_model = QSortFilterProxyModel(self) self.p_filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) self.lineEdit().textEdited.connect(self.p_filter_model.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked) def setModel(self, model): # pylint:disable=invalid-name """ Set the model to use the Filter model :param model: The model to be used by the combobox :type model: PyQt5.QtGui.QStandardItemModel """ super().setModel(model) self.p_filter_model.setSourceModel(model) self.completer.setModel(self.p_filter_model) def setModelColumn(self, column): # pylint:disable=invalid-name """ :param model: The model to be used by the combobox :type model: PyQt5.QtGui.QStandardItemModel """ self.completer.setCompletionColumn(column) self.p_filter_model.setFilterKeyColumn(column) super().setModelColumn(column) def view(self): """ A QListView of items stored in the model :return: items stored in the model :rtype: PyQt5.QtWidgets.QListView """ return self.completer.popup() def index(self): """ Index of the current item in the combobox. :return: index of the current item :rtype: int """ return self.currentIndex() def setTextIfCompleterIsClicked(self, text): # pylint:disable=invalid-name """ :param text: The current text of the qlineedit :type text: str If the combobx lineedit is clicked, set the lineedits current item as the combobox's current item """ if text: index = self.findText(text) self.setCurrentIndex(index)
class DlgJobs(QtWidgets.QDialog, Ui_DlgJobs): # When a connection to the api starts, emit true. When it ends, emit False connectionEvent = pyqtSignal(bool) def __init__(self, parent=None): """Constructor.""" super(DlgJobs, self).__init__(parent) self.setupUi(self) self.connection_in_progress = False self.bar = QgsMessageBar() self.bar.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.refresh.clicked.connect(self.btn_refresh) self.download.clicked.connect(self.btn_download) self.connectionEvent.connect(self.connection_event_changed) # Only enable download button if a job is selected self.download.setEnabled(False) self.jobs_view.viewport().installEventFilter( tool_tipper(self.jobs_view)) def showEvent(self, event): super(DlgJobs, self).showEvent(event) ####################################################################### ####################################################################### # Hack to download multiple countries at once for workshop preparation ####################################################################### ####################################################################### # from qgis.PyQt.QtCore import QTimer, Qt # from qgis.PyQt.QtWidgets import QMessageBox, QApplication # from qgis.PyQt.QtTest import QTest # from time import sleep # # self.btn_refresh() # # search_string = '_TE_Land_Productivity' # # # Ensure any message boxes that open are closed within 1 second # def close_msg_boxes(): # for w in QApplication.topLevelWidgets(): # if isinstance(w, QMessageBox): # print('Closing message box') # QTest.keyClick(w, Qt.Key_Enter) # timer = QTimer() # timer.timeout.connect(close_msg_boxes) # timer.start(1000) # jobs = [job for job in self.jobs if job['status'] == 'FINISHED' and \ # job['results']['type'] == 'CloudResults' and \ # re.search(search_string, job['task_name'])] # for job in jobs: # name = job['task_name'] # country = name.replace(search_string, '') # out_file = os.path.join(u'H:/Data/Trends.Earth/USB Stick Data/Trends.Earth_Data', country, u'{}.json'.format(name)) # #out_file = os.path.join(u'C:/Users/azvol/Desktop/TE_Indicators_For_USB', country, u'{}.json'.format(name)) # if not os.path.exists(out_file): # if not os.path.exists(os.path.dirname(out_file)): # os.makedirs(os.path.dirname(out_file)) # log(u'Downloading {} to {}'.format(name, out_file)) # download_cloud_results(job, # os.path.splitext(out_file)[0], # self.tr, # add_to_map=False) # sleep(2) ####################################################################### ####################################################################### # End hack ####################################################################### ####################################################################### try: jobs_cache = json.loads(QSettings().value("LDMP/jobs_cache", '{}')) except TypeError: # For backward compatibility need to handle case of jobs caches # that were stored inappropriately in past version of Trends.Earth jobs_cache = {} if jobs_cache is not {}: self.jobs = jobs_cache self.update_jobs_table() def connection_event_changed(self, flag): if flag: self.connection_in_progress = True self.download.setEnabled(False) self.refresh.setEnabled(False) else: self.connection_in_progress = False # Enable the download button if there is a selection self.selection_changed() self.refresh.setEnabled(True) def selection_changed(self): if self.connection_in_progress: return elif not self.jobs_view.selectedIndexes(): self.download.setEnabled(False) else: rows = set( list(index.row() for index in self.jobs_view.selectedIndexes())) job_ids = [self.proxy_model.index(row, 4).data() for row in rows] statuses = [ job.get('status', None) for job in self.jobs if job.get('id', None) in job_ids ] if len(statuses) > 0: for status in statuses: # Don't set button to enabled if any of the tasks aren't # yet finished, or if any are invalid if status != 'FINISHED': self.download.setEnabled(False) return self.download.setEnabled(True) def btn_refresh(self): self.connectionEvent.emit(True) email = get_user_email() if email: start_date = datetime.datetime.now() + datetime.timedelta(-14) jobs = get_execution(date=start_date.strftime('%Y-%m-%d')) if jobs: self.jobs = jobs # Add script names and descriptions to jobs list for job in self.jobs: # self.jobs will have prettified data for usage in table, # so save a backup of the original data under key 'raw' job['raw'] = job.copy() script = job.get('script_id', None) if script: job['script_name'] = job['script']['name'] # Clean up the script name so the version tag doesn't # look so odd job['script_name'] = re.sub(r'([0-9]+(_[0-9]+)+)$', r'(v\g<1>)', job['script_name']) job['script_name'] = job['script_name'].replace( '_', '.') job['script_description'] = job['script'][ 'description'] else: # Handle case of scripts that have been removed or that are # no longer supported job['script_name'] = self.tr('Script not found') job['script_description'] = self.tr('Script not found') # Pretty print dates and pull the metadata sent as input params for job in self.jobs: job['start_date'] = datetime.datetime.strftime( job['start_date'], '%Y/%m/%d (%H:%M)') job['end_date'] = datetime.datetime.strftime( job['end_date'], '%Y/%m/%d (%H:%M)') job['task_name'] = job['params'].get('task_name', '') job['task_notes'] = job['params'].get('task_notes', '') job['params'] = job['params'] # Cache jobs for later reuse QSettings().setValue( "LDMP/jobs_cache", json.dumps(self.jobs, default=json_serial)) self.update_jobs_table() self.connectionEvent.emit(False) return True self.connectionEvent.emit(False) return False def update_jobs_table(self): if self.jobs: table_model = JobsTableModel(self.jobs, self) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(table_model) self.jobs_view.setModel(self.proxy_model) # Add "Details" buttons in cell for row in range(0, len(self.jobs)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.jobs_view.setIndexWidget(self.proxy_model.index(row, 6), btn) self.jobs_view.horizontalHeader().setSectionResizeMode( 0, QtWidgets.QHeaderView.Stretch) self.jobs_view.horizontalHeader().setSectionResizeMode( 1, QtWidgets.QHeaderView.Stretch) self.jobs_view.horizontalHeader().setSectionResizeMode( 2, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 3, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 4, QtWidgets.QHeaderView.Fixed) self.jobs_view.horizontalHeader().setSectionResizeMode( 5, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 6, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.selectionModel().selectionChanged.connect( self.selection_changed) def btn_details(self): button = self.sender() index = self.jobs_view.indexAt(button.pos()) details_dlg = DlgJobsDetails(self) job = self.jobs[index.row()] details_dlg.task_name.setText(job.get('task_name', '')) details_dlg.task_status.setText(job.get('status', '')) details_dlg.comments.setText(job.get('task_notes', '')) details_dlg.input.setText( json.dumps(job.get('params', ''), indent=4, sort_keys=True)) details_dlg.output.setText( json.dumps(job.get('results', ''), indent=4, sort_keys=True)) details_dlg.show() details_dlg.exec_() def btn_download(self): # Use set below in case multiple cells within the same row are selected rows = set( list(index.row() for index in self.jobs_view.selectedIndexes())) job_ids = [self.proxy_model.index(row, 4).data() for row in rows] jobs = [job for job in self.jobs if job['id'] in job_ids] filenames = [] for job in jobs: # Check if we need a download filename - some tasks don't need to # save data, but if any of the chosen tasks do, then we need to # choose a folder. Right now only TimeSeriesTable doesn't need a # filename. if job['results'].get('type') != 'TimeSeriesTable': f = None while not f: # Setup a string to use in filename window if job['task_name']: job_info = u'{} ({})'.format(job['script_name'], job['task_name']) else: job_info = job['script_name'] f, _ = QtWidgets.QFileDialog.getSaveFileName( self, self.tr( u'Choose a filename. Downloading results of: {}'. format(job_info)), QSettings().value("LDMP/output_dir", None), self.tr('Base filename (*.json)')) # Strip the extension so that it is a basename f = os.path.splitext(f)[0] if f: if os.access(os.path.dirname(f), os.W_OK): QSettings().setValue("LDMP/output_dir", os.path.dirname(f)) log(u"Downloading results to {} with basename {}". format(os.path.dirname(f), os.path.basename(f))) else: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr(u"Cannot write to {}. Choose a different base filename." .format(f))) else: return False filenames.append(f) else: filenames.append(None) self.close() for row, f in zip(rows, filenames): job = self.jobs[row] log(u"Processing job {}.".format(job.get('id', None))) result_type = job['results'].get('type') if result_type == 'CloudResults': download_cloud_results(job, f, self.tr) elif result_type == 'TimeSeriesTable': download_timeseries(job, self.tr) else: raise ValueError( "Unrecognized result type in download results: {}".format( result_type))
class PdokServicesPlugin(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # docked or dialog, defaults to dialog # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if isinstance(QSettings().value("/pdokservicesplugin/docked"), QVariant): # self.docked = QSettings().value("/pdokservicesplugin/docked", QVariant(False)) # else: # self.docked = QSettings().value("/pdokservicesplugin/docked", False) # # # Create the dialog and keep reference # if "True" == self.docked or "true" == self.docked or True is self.docked: # self.dlg = PdokServicesPluginDockWidget() # self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dlg) # else: # self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDatabaseFilePath( )).path() + "/python/plugins/pdokservicesplugin" # initialize locale localePath = "" if isinstance(QSettings().value("locale/userLocale"), QVariant): locale = QSettings().value("locale/userLocale").value()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/pdokservicesplugin_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.currentLayer = None self.SETTINGS_SECTION = '/pdokservicesplugin/' self.pointer = None self.pdokgeocoder = PDOKGeoLocator(self.iface) self.geocoderSourceModel = None def getSettingsValue(self, key, default=''): if QSettings().contains(self.SETTINGS_SECTION + key): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 return str(QSettings().value(key).toString()) else: return str(QSettings().value(key)) else: return default def setSettingsValue(self, key, value): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue(key, QVariant(value)) else: QSettings().setValue(key, value) def initGui(self): # Create action that will start plugin configuration runIcon = QIcon(os.path.join(self.plugin_dir, 'icon_add_service.svg')) self.run_action = QAction(runIcon, \ "PDOK Services plugin", self.iface.mainWindow()) self.servicesLoaded = False # connect the action to the run method # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if "True" == self.docked or "true" == self.docked or True == self.docked: # self.run_action.triggered.connect(self.showAndRaise) # self.dlg.radioDocked.setChecked(True) # # docked the dialog is immidiately visible, so should run NOW # else: # self.run_action.triggered.connect(self.run) # self.dlg.radioDocked.setChecked(False) # self.setupfq() self.run_action.triggered.connect(self.run) #self.dlg.radioDocked.setChecked(False) self.setupfq() # Add toolbar button and menu item #self.iface.addToolBarIcon(self.action) self.toolbar = self.iface.addToolBar("PDOK services plugin") self.toolbar.setObjectName("PDOK services plugin") self.toolbar.addAction(self.run_action) self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("PDOK Locatieserver zoek") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.searchAddressFromToolbar) # address/point cleanup eraserIcon = QIcon( os.path.join(self.plugin_dir, 'icon_remove_cross.svg')) self.clean_action = QAction(eraserIcon, \ "Cleanup", self.eraseAddress()) self.toolbar.addAction(self.clean_action) self.clean_action.triggered.connect(self.eraseAddress) self.clean_action.setEnabled(False) self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.run_action) # about self.aboutAction = QAction(QIcon(":/plugins/pdokservicesplugin/icon_help.png"), \ "About", self.iface.mainWindow()) self.aboutAction.setWhatsThis("Pdok Services Plugin About") self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.aboutAction) self.aboutAction.triggered.connect(self.about) self.dlg.ui.btnLoadLayer.clicked.connect(self.loadService) self.dlg.geocoderSearch.returnPressed.connect(self.searchAddress) self.dlg.geocoderSearch.textEdited.connect(self.searchAddress) self.dlg.geocoderSearch.setPlaceholderText( "PDOK Locatieserver zoek, bv postcode of postcode huisnummer") self.dlg.geocoderResultSearch.textChanged.connect( self.filterGeocoderResult) self.dlg.geocoderResultSearch.setPlaceholderText( "een of meer zoekwoorden uit resultaat") #self.dlg.radioDocked.toggled.connect(self.set_docked) self.dlg.btnCheckPdokJson.clicked.connect(self.checkPdokJson) #self.iface.mapCanvas().renderStarting.connect(self.extentsChanged) ui = self.dlg.ui cbxs = [ ui.cbx_gem, ui.cbx_wpl, ui.cbx_weg, ui.cbx_pcd, ui.cbx_adr, ui.cbx_pcl, ui.cbx_hmp ] # connect all fq checkboxes with suggest, so upon a change in fq filter we re-search for cbx in cbxs: cbx.stateChanged.connect(self.searchAddress) self.run(True) # for now hiding the pointer as soon as the extent changes #def extentsChanged(self): # self.removePointer() def checkPdokJson(self): myversion = self.getSettingsValue('pdokversion', '1') msgtxt = '' msglvl = 0 # QgsMessageBar.INFO try: response = urllib.request.urlopen( 'http://www.qgis.nl/pdok.version') str_response = response.read().decode('utf-8') pdokversion = json.loads(str_response) if pdokversion > int(myversion): response = urllib.request.urlopen( 'http://www.qgis.nl/pdok.json') str_response = response.read().decode('utf-8') pdokjson = json.loads(str_response) with open(self.plugin_dir + '/pdok.json', 'w') as outfile: json.dump(pdokjson, outfile) msgtxt = "De laatste versie is opgehaald en zal worden gebruikt " + \ str(pdokversion) + ' (was ' + myversion +')' self.servicesLoaded = False # reset reading of json self.run() self.setSettingsValue('pdokversion', pdokversion) else: msgtxt = "Geen nieuwere versie beschikbaar dan " + str( pdokversion) except Exception as e: #print e msgtxt = "Fout bij ophalen van service info. Netwerk probleem?" msglvl = 2 # QgsMessageBar.CRITICAL # msg if hasattr(self.iface, 'messageBar'): self.iface.messageBar().pushMessage("PDOK services update", msgtxt, level=msglvl, duration=10) else: # 1.8 QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin", msgtxt) # def set_docked(self, foo): # self.setSettingsValue('docked', self.dlg.radioDocked.isChecked()) # #if Qgis.QGIS_VERSION_INT < 10900: # # # qgis <= 1.8 # # QSettings().setValue("/pdokservicesplugin/docked", QVariant(self.dlg.radioDocked.isChecked())) # #else: # # QSettings().setValue("/pdokservicesplugin/docked", self.dlg.radioDocked.isChecked()) def showAndRaise(self): self.dlg.show() self.dlg.raise_() # also remove the pointer self.removePointer() def about(self): infoString = "Written by Richard Duivenvoorde\nEmail - [email protected]\n" infoString += "Company - Zuidt - http://www.zuidt.nl\n" infoString += "Source: https://github.com/rduivenvoorde/pdokservicesplugin" QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin About", infoString) def unload(self): self.removePointer() # Remove the plugin menu item and icon self.iface.removePluginMenu("&Pdok Services Plugin", self.run_action) self.iface.removePluginMenu("&Pdok Services Plugin", self.aboutAction) del self.toolbarSearch del self.run_action del self.aboutAction def showService(self, selectedIndexes): if len(selectedIndexes) == 0: self.currentLayer = None self.dlg.ui.layerInfo.setHtml('') self.dlg.ui.comboSelectProj.clear() return # needed to scroll To the selected row incase of using the keyboard / arrows self.dlg.servicesView.scrollTo( self.dlg.servicesView.selectedIndexes()[0]) # itemType holds the data (== column 1) self.currentLayer = self.dlg.servicesView.selectedIndexes()[1].data( Qt.UserRole) if isinstance(self.currentLayer, QVariant): self.currentLayer = self.currentLayer.toMap() # QGIS 1.8: QVariants currentLayer = {} for key in list(self.currentLayer.keys()): val = self.currentLayer[key] currentLayer[str(key)] = str(val.toString()) self.currentLayer = currentLayer url = self.currentLayer['url'] title = self.currentLayer['title'] style = '' if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' servicetitle = self.currentLayer['servicetitle'] layername = self.currentLayer['layers'] abstract = self.currentLayer['abstract'] stype = self.currentLayer['type'].upper() minscale = '' if 'minscale' in self.currentLayer and self.currentLayer[ 'minscale'] != None and self.currentLayer['minscale'] != '': minscale = "min. schaal 1:" + self.currentLayer['minscale'] maxscale = '' if 'maxscale' in self.currentLayer and self.currentLayer[ 'maxscale'] != None and self.currentLayer['maxscale'] != '': maxscale = "max. schaal 1:" + self.currentLayer['maxscale'] self.dlg.ui.layerInfo.setText('') self.dlg.ui.btnLoadLayer.setEnabled(True) self.dlg.ui.layerInfo.setHtml( '<h4>%s</h4><h3>%s</h3><lu><li>%s</li><li> </li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li></lu>' % (servicetitle, title, abstract, stype, url, layername, style, minscale, maxscale)) self.dlg.ui.comboSelectProj.clear() if stype == "WMS": try: crs = self.currentLayer['crs'] except KeyError: crs = 'EPSG:28992' crs = crs.split(',') self.dlg.ui.comboSelectProj.addItems(crs) for i in range(len(crs)): if crs[i] == 'EPSG:28992': self.dlg.ui.comboSelectProj.setCurrentIndex(i) if stype == "WMTS": tilematrixsets = self.currentLayer['tilematrixsets'].split(',') self.dlg.ui.comboSelectProj.addItems(tilematrixsets) for i in range(len(tilematrixsets)): if tilematrixsets[i].startswith('EPSG:28992'): self.dlg.ui.comboSelectProj.setCurrentIndex(i) def loadService(self): if self.currentLayer == None: return servicetype = self.currentLayer['type'] url = self.currentLayer['url'] # some services have an url with query parameters in it, we have to urlencode those: location, query = urllib.parse.splitquery(url) url = location if query != None and query != '': url += ('?' + urllib.parse.quote_plus(query)) title = self.currentLayer['title'] if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' else: style = '' # == default for this service layers = self.currentLayer['layers'] # mmm, tricky: we take the first one while we can actually want png/gif or jpeg if servicetype == "wms": imgformat = self.currentLayer['imgformats'].split(',')[0] if self.dlg.ui.comboSelectProj.currentIndex() == -1: crs = 'EPSG:28992' else: crs = self.dlg.ui.comboSelectProj.currentText() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 uri = url self.iface.addRasterLayer( uri, # service uri title, # name for layer (as seen in QGIS) "wms", # dataprovider key [layers], # array of layername(s) for provider (id's) ["" ], # array of stylename(s) NOTE: ignoring styles here!!! imgformat, # image format searchstring crs) # crs code searchstring else: # qgis > 1.8 uri = "crs=" + crs + "&layers=" + layers + "&styles=" + style + "&format=" + imgformat + "&url=" + url self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wmts": if Qgis.QGIS_VERSION_INT < 10900: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( "Sorry, dit type layer: '" + servicetype.upper() + "' \nkan niet worden geladen in deze versie van QGIS.\nMisschien kunt u QGIS 2.0 installeren (die kan het WEL)?\nOf is de laag niet ook beschikbaar als wms of wfs?" ), QMessageBox.Ok, QMessageBox.Ok) return if self.dlg.ui.comboSelectProj.currentIndex() == -1: tilematrixset = 'EPSG:28992' else: tilematrixset = self.dlg.ui.comboSelectProj.currentText() imgformat = self.currentLayer['imgformats'].split(',')[0] # special case for luchtfoto #if layers=="luchtfoto": # # tileMatrixSet=nltilingschema&crs=EPSG:28992&layers=luchtfoto&styles=&format=image/jpeg&url=http://geodata1.nationaalgeoregister.nl/luchtfoto/wmts/1.0.0/WMTSCapabilities.xml # # {u'layers': u'luchtfoto', u'imgformats': u'image/jpeg', u'title': u'PDOK-achtergrond luchtfoto', u'url': u'http://geodata1.nationaalgeoregister.nl/luchtfoto/wms', u'abstract': u'', u'tilematrixsets': u'nltilingschema', u'type': u'wmts'} # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url #else: # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url; #uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; if tilematrixset.startswith('EPSG:'): crs = tilematrixset i = crs.find(':', 5) if i > -1: crs = crs[:i] elif tilematrixset.startswith('OGC:1.0'): crs = 'EPSG:3857' uri = "tileMatrixSet=" + tilematrixset + "&crs=" + crs + "&layers=" + layers + "&styles=default&format=" + imgformat + "&url=" + url #print "############ PDOK URI #################" #print uri self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wfs": location, query = urllib.parse.splitquery(url) #uri = location+"?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME="+layers+"&SRSNAME=EPSG:28992" #uri = location + "?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=" + layers + "&SRSNAME=EPSG:28992" # adding a bbox paramater forces QGIS to NOT cache features but retrieve new features all the time # QGIS will update the BBOX to the right value #uri += "&BBOX=-10000,310000,290000,650000" uri = " pagingEnabled='true' restrictToRequestBBOX='1' srsname='EPSG:28992' typename='" + layers + "' url='" + url + "' version='2.0.0' " self.iface.addVectorLayer(uri, title, "WFS") elif servicetype == "wcs": # cache=AlwaysCache&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs uri = '' # cache=AlwaysCache # cache=PreferNetwork # cache=AlwaysNetwork # cache=AlwaysNetwork&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.1&identifier="+layers+"&url="+url # working for ahn1 ahn2 and ahn3: GEOTIFF_FLOAT32 format = 'GEOTIFF_FLOAT32' # working for ahn25m is only image/tiff if layers == 'ahn25m': format = 'image/tiff' # we handcrated some wcs layers with 2 different image formats: tiff (RGB) and tiff (float32): if 'imgformats' in self.currentLayer: format = self.currentLayer['imgformats'].split(',')[0] uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=" + format + "&version=1.1.2&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.2&identifier=" + layers + "&url=" + url self.iface.addRasterLayer(uri, title, "wcs") else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( "Sorry, dit type layer: '" + servicetype.upper() + "' \nkan niet worden geladen door de plugin of door QGIS.\nIs het niet beschikbaar als wms, wmts of wfs?" ), QMessageBox.Ok, QMessageBox.Ok) return def filterGeocoderResult(self, string): #print "filtering geocoder results: %s" % string self.dlg.geocoderResultView.selectRow(0) self.geocoderProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.geocoderProxyModel.setFilterFixedString(string) def searchAddressFromToolbar(self): self.removePointer() self.geocoderSourceModel.clear() self.geocode() def searchAddress(self): self.removePointer() #print "search geocoder for: %s" % self.dlg.geocoderSearch.text() self.geocoderSourceModel.clear() #self.geocode(self.dlg.geocoderSearch.text()) self.suggest() def eraseAddress(self): """ clean the input and remove the pointer """ self.removePointer() if self.geocoderSourceModel is not None: self.geocoderSourceModel.clear() if self.dlg.geocoderSearch is not None: self.dlg.geocoderSearch.clear() if self.toolbarSearch is not None: self.toolbarSearch.clear() def filterLayers(self, string): # remove selection if one row is selected self.dlg.servicesView.selectRow(0) #self.currentLayer = None self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterFixedString(string) #def addSourceRow(self, service, layer): def addSourceRow(self, serviceLayer): # you can attache different "data's" to to an QStandarditem # default one is the visible one: itemType = QStandardItem("%s" % (serviceLayer["type"].upper())) # userrole is a free form one: # only attach the data to the first item # service layer = a dict/object with all props of the layer itemType.setData(serviceLayer, Qt.UserRole) itemType.setToolTip( "%s - %s" % (serviceLayer["type"].upper(), serviceLayer["title"])) # only wms services have styles (sometimes) layername = serviceLayer["title"] if 'style' in serviceLayer: itemLayername = QStandardItem( "%s [%s]" % (serviceLayer["title"], serviceLayer["style"])) layername = "%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) else: itemLayername = QStandardItem("%s" % (serviceLayer["title"])) itemLayername.setToolTip( "%s - %s" % (serviceLayer["type"].upper(), serviceLayer["servicetitle"])) # itemFilter is the item used to search filter in. That is why layername is a combi of layername + filter here itemFilter = QStandardItem( "%s %s %s %s" % (serviceLayer["type"], layername, serviceLayer["servicetitle"], serviceLayer["abstract"])) itemServicetitle = QStandardItem("%s" % (serviceLayer["servicetitle"])) itemServicetitle.setToolTip( "%s - %s" % (serviceLayer["type"].upper(), serviceLayer["title"])) self.sourceModel.appendRow( [itemLayername, itemType, itemServicetitle, itemFilter]) # run method that performs all the real work def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value( "/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget( int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers( QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers( QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance( service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText( "woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect( self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable( False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel( ).selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth( 0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer() def setupfq(self): """ Setup the fq checkboxes in the gui, by looking into the settings for the 'pdokservicesplugin/checkedfqs' key, which contains a list of type strings like ['weg','adres'] """ checked_fqs = self.getSettingsValue('checkedfqs', []) #self.info('setup fq: {}'.format(checked_fqs)) if len(checked_fqs ) > 0: # else there is not saved state... take gui defaults self.dlg.ui.cbx_gem.setChecked('gemeente' in checked_fqs) self.dlg.ui.cbx_wpl.setChecked('woonplaats' in checked_fqs) self.dlg.ui.cbx_weg.setChecked('weg' in checked_fqs) self.dlg.ui.cbx_pcd.setChecked('postcode' in checked_fqs) self.dlg.ui.cbx_adr.setChecked('adres' in checked_fqs) self.dlg.ui.cbx_pcl.setChecked('perceel' in checked_fqs) self.dlg.ui.cbx_hmp.setChecked('hectometerpaal' in checked_fqs) def createfq(self): """ This creates a fq-string (Filter Query, see https://github.com/PDOK/locatieserver/wiki/Zoekvoorbeelden-Locatieserver) Based on the checkboxes in the dialog. Defaults to '' Example: 'fq=+type:adres+type:gemeente' (only gemeente AND addresses) :return: """ fqlist = [] if self.dlg.ui.cbx_gem.isChecked(): fqlist.append('gemeente') if self.dlg.ui.cbx_wpl.isChecked(): fqlist.append('woonplaats') if self.dlg.ui.cbx_weg.isChecked(): fqlist.append('weg') if self.dlg.ui.cbx_pcd.isChecked(): fqlist.append('postcode') if self.dlg.ui.cbx_adr.isChecked(): fqlist.append('adres') if self.dlg.ui.cbx_pcl.isChecked(): fqlist.append('perceel') if self.dlg.ui.cbx_hmp.isChecked(): fqlist.append('hectometerpaal') self.setSettingsValue('checkedfqs', fqlist) #self.info(self.getSettingsValue('checkedfqs', ['leeg?'])) fq = '' if len(fqlist) > 0: fq = '&fq=+type:' + '+type:'.join(fqlist) return fq def suggest(self): self.dlg.ui.lookupinfo.setHtml('') search_text = self.dlg.geocoderSearch.text() if len(search_text) <= 1: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "meer input aub: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) return # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "zoeken: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) results = self.pdokgeocoder.suggest(search_text, self.createfq()) if len(results) == 0: # ignore, as we are suggesting, maybe more characters will reveal something... return for result in results: #print address adrestekst = QStandardItem("%s" % (result["adrestekst"])) adrestekst.setData(result, Qt.UserRole) type = QStandardItem("%s" % (result["type"])) id = QStandardItem("%s" % (result["id"])) score = QStandardItem("%s" % (result["score"])) adrestekst.setData(result, Qt.UserRole) self.geocoderSourceModel.appendRow([adrestekst, type]) self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment( Qt.AlignLeft) self.dlg.geocoderResultView.resizeColumnsToContents() self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection( True) def geocode(self): self.dlg.geocoderSearch.setText(self.toolbarSearch.text()) self.suggest() if self.dlg.geocoderResultView.model().rowCount() > 0: self.dlg.geocoderResultView.selectRow(0) self.zoomToAddress() else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ "Niets gevonden.\nProbeer een andere spelling, of alleen postcode/huisnummer?\n\nSelecteer meer (Locatieserver) 'typen' in de PdokServicesPlugin dialoog.\n\nOf gebruik de 'PDOK geocoder'-tab in de PdokServicesPlugin dialoog." ), QMessageBox.Ok, QMessageBox.Ok) # def geocode(self): # self.dlg.ui.lookupinfo.setHtml('') # search_text = self.toolbarSearch.text() # addresses = self.pdokgeocoder.search(search_text) # if len(addresses) == 0: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "Niets gevonden. Probeer een andere spelling of alleen postcode/huisnummer." # ), QMessageBox.Ok, QMessageBox.Ok) # return # for address in addresses: # #print address # adrestekst = QStandardItem("%s" % (address["adrestekst"])) # adrestekst.setData(address, Qt.UserRole) # straat = QStandardItem("%s" % (address["straat"])) # nummer = QStandardItem("%s" % (address["nummer"])) # postcode = QStandardItem("%s" % (address["postcode"])) # plaats = QStandardItem("%s" % (address["plaats"])) # gemeente = QStandardItem("%s" % (address["gemeente"])) # provincie = QStandardItem("%s" % (address["provincie"])) # self.geocoderSourceModel.appendRow([adrestekst, straat, nummer, postcode, plaats, gemeente, provincie]) # # self.dlg.geocoderResultView.selectRow(0) # self.zoomToAddress() # # self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") # self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Straat") # self.geocoderSourceModel.setHeaderData(2, Qt.Horizontal, "Nr") # self.geocoderSourceModel.setHeaderData(3, Qt.Horizontal, "Postcode") # self.geocoderSourceModel.setHeaderData(4, Qt.Horizontal, "Plaats") # self.geocoderSourceModel.setHeaderData(5, Qt.Horizontal, "Gemeente") # self.geocoderSourceModel.setHeaderData(6, Qt.Horizontal, "Provincie") # # self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(3).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(4).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(5).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(6).setTextAlignment(Qt.AlignLeft) # # self.dlg.geocoderResultView.resizeColumnsToContents() # self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data( Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data = data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml('<h4>{}</h4><lu>{}</lu>'.format( adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh() def setPointer(self, point): self.removePointer() self.pointer = QgsVertexMarker(self.iface.mapCanvas()) self.pointer.setColor(QColor(255, 255, 0)) self.pointer.setIconSize(10) self.pointer.setPenWidth(5) self.pointer.setCenter(point) self.clean_action.setEnabled(True) def removePointer(self): if self.pointer is not None: self.iface.mapCanvas().scene().removeItem(self.pointer) self.clean_action.setEnabled(False) def info(self, msg=""): QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin', Qgis.Info)
class SPAQLunicornDialog(QtWidgets.QDialog, FORM_CLASS): ## The triple store configuration file triplestoreconf = None ## Prefix map prefixes = None enrichtab = None interlinktab = None conceptList = None completerClassList = None columnvars = {} def __init__(self, triplestoreconf={}, prefixes=[], addVocabConf={}, autocomplete={}, prefixstore={ "normal": {}, "reversed": {} }, savedQueriesJSON={}, maindlg=None, parent=None): """Constructor.""" super(SPAQLunicornDialog, self).__init__(parent) self.setupUi(self) self.prefixes = prefixes self.maindlg = maindlg self.savedQueriesJSON = savedQueriesJSON self.enrichtab = EnrichmentTab(self) self.interlinktab = InterlinkingTab(self) self.addVocabConf = addVocabConf self.autocomplete = autocomplete self.prefixstore = prefixstore self.triplestoreconf = triplestoreconf self.searchTripleStoreDialog = TripleStoreDialog( self.triplestoreconf, self.prefixes, self.prefixstore, self.comboBox) self.geoClassList.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geoClassList.setAlternatingRowColors(True) self.geoClassList.setViewMode(QListView.ListMode) self.geoClassList.setContextMenuPolicy(Qt.CustomContextMenu) self.geoClassList.customContextMenuRequested.connect(self.onContext) self.geoClassListModel = QStandardItemModel() self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.sort(0) self.proxyModel.setSourceModel(self.geoClassListModel) self.geoClassList.setModel(self.proxyModel) self.geoClassListModel.clear() self.queryLimit.setValidator(QRegExpValidator(QRegExp("[0-9]*"))) self.filterConcepts.textChanged.connect(self.setFilterFromText) self.inp_sparql2 = ToolTipPlainText(self.tab, self.triplestoreconf, self.comboBox, self.columnvars, self.prefixes, self.autocomplete) self.inp_sparql2.move(10, 130) self.inp_sparql2.setMinimumSize(780, 401) self.inp_sparql2.document().defaultFont().setPointSize(16) self.inp_sparql2.setPlainText( "SELECT ?item ?lat ?lon WHERE {\n ?item ?b ?c .\n ?item <http://www.wikidata.org/prop:P123> ?def .\n}" ) self.inp_sparql2.columnvars = {} self.inp_sparql2.textChanged.connect(self.validateSPARQL) self.sparqlhighlight = SPARQLHighlighter(self.inp_sparql2) self.areaconcepts.hide() self.areas.hide() self.label_8.hide() self.label_9.hide() self.savedQueries.hide() self.loadQuery.hide() self.saveQueryButton.hide() self.saveQueryName.hide() self.savedQueryLabel.hide() self.saveQueryName_2.hide() self.enrichTableResult.hide() self.queryTemplates.currentIndexChanged.connect(self.viewselectaction) self.bboxButton.clicked.connect(self.getPointFromCanvas) self.interlinkTable.cellClicked.connect( self.createInterlinkSearchDialog) self.enrichTable.cellClicked.connect(self.createEnrichSearchDialog) self.chooseLayerInterlink.clear() self.searchClass.clicked.connect(self.createInterlinkSearchDialog) urlregex = QRegExp( "http[s]?://(?:[a-zA-Z#]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+" ) urlvalidator = QRegExpValidator(urlregex, self) self.interlinkNameSpace.setValidator(urlvalidator) self.interlinkNameSpace.textChanged.connect(self.check_state3) self.interlinkNameSpace.textChanged.emit( self.interlinkNameSpace.text()) self.addEnrichedLayerButton.clicked.connect( self.enrichtab.addEnrichedLayer) self.startEnrichment.clicked.connect(self.enrichtab.enrichLayerProcess) self.exportInterlink.clicked.connect( self.enrichtab.exportEnrichedLayer) self.loadQuery.clicked.connect(self.loadQueryFunc) self.saveQueryButton.clicked.connect(self.saveQueryFunc) self.exportMappingButton.clicked.connect( self.interlinktab.exportMapping) self.importMappingButton.clicked.connect(self.interlinktab.loadMapping) self.loadLayerInterlink.clicked.connect(self.loadLayerForInterlink) self.loadLayerEnrich.clicked.connect(self.loadLayerForEnrichment) self.addEnrichedLayerRowButton.clicked.connect(self.addEnrichRow) self.geoClassList.selectionModel().selectionChanged.connect( self.viewselectaction) self.loadFileButton.clicked.connect(self.buildLoadGraphDialog) self.refreshLayersInterlink.clicked.connect(self.loadUnicornLayers) self.btn_loadunicornlayers.clicked.connect(self.loadUnicornLayers) self.whattoenrich.clicked.connect(self.createWhatToEnrich) self.quickAddTripleStore.clicked.connect(self.buildQuickAddTripleStore) self.loadTripleStoreButton.clicked.connect( self.buildCustomTripleStoreDialog) self.loadUnicornLayers() def loadQueryFunc(self): if self.triplestoreconf[self.comboBox.currentIndex( )]["endpoint"] in self.savedQueriesJSON: self.inp_sparql2.setPlainText(self.savedQueriesJSON[ self.triplestoreconf[self.comboBox.currentIndex()] ["endpoint"]][self.savedQueries.currentIndex()]["query"]) def saveQueryFunc(self): queryName = self.saveQueryName.text() if queryName != None and queryName != "": __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) self.savedQueriesJSON[self.triplestoreconf[ self.comboBox.currentIndex()]["endpoint"]].append({ "label": queryName, "query": self.inp_sparql2.toPlainText() }) self.savedQueries.addItem(queryName) f = open(os.path.join(__location__, 'savedqueries.json'), "w") f.write(json.dumps(self.savedQueriesJSON)) f.close() def onContext(self): menu = QMenu("Menu", self.geoClassList) action = QAction("Open in Webbrowser") menu.addAction(action) action.triggered.connect(self.openURL) def openURL(self): curindex = self.proxyModel.mapToSource( self.geoClassList.selectionModel().currentIndex()) concept = self.geoClassListModel.itemFromIndex(curindex).data(1) url = QUrl(concept) QDesktopServices.openUrl(url) def setFilterFromText(self): self.proxyModel.setFilterRegExp(self.filterConcepts.text()) ## # @brief Creates a What To Enrich dialog with parameters given. # # @param self The object pointer def buildLoadGraphDialog(self): self.searchTripleStoreDialog = LoadGraphDialog(self.triplestoreconf, self.maindlg, self) self.searchTripleStoreDialog.setWindowTitle("Load Graph") self.searchTripleStoreDialog.exec_() ## # @brief Creates a What To Enrich dialog with parameters given. # # @param self The object pointer def buildQuickAddTripleStore(self): self.searchTripleStoreDialog = TripleStoreQuickAddDialog( self.triplestoreconf, self.prefixes, self.prefixstore, self.comboBox) self.searchTripleStoreDialog.setMinimumSize(580, 186) self.searchTripleStoreDialog.setWindowTitle( "Configure Own Triple Store") self.searchTripleStoreDialog.exec_() ## # @brief Creates a What To Enrich dialog with parameters given. # # @param self The object pointer def buildCustomTripleStoreDialog(self): self.searchTripleStoreDialog = TripleStoreDialog( self.triplestoreconf, self.prefixes, self.prefixstore, self.comboBox) self.searchTripleStoreDialog.setMinimumSize(700, 500) self.searchTripleStoreDialog.setWindowTitle( "Configure Own Triple Store") self.searchTripleStoreDialog.exec_() ## # @brief Creates a What To Enrich dialog with parameters given. # # @param self The object pointer def createWhatToEnrich(self): if self.enrichTable.rowCount() == 0: return layers = QgsProject.instance().layerTreeRoot().children() selectedLayerIndex = self.chooseLayerEnrich.currentIndex() layer = layers[selectedLayerIndex].layer() self.searchTripleStoreDialog = EnrichmentDialog( self.triplestoreconf, self.prefixes, self.enrichTable, layer, None, None) self.searchTripleStoreDialog.setMinimumSize(700, 500) self.searchTripleStoreDialog.setWindowTitle("Enrichment Search") self.searchTripleStoreDialog.exec_() def check_state3(self): self.searchTripleStoreDialog.check_state(self.interlinkNameSpace) def createEnrichSearchDialog(self, row=-1, column=-1): if column == 1: self.buildSearchDialog(row, column, False, self.enrichTable, False, False, None, self.addVocabConf) if column == 6: self.buildSearchDialog(row, column, False, self.enrichTable, False, False, None, self.addVocabConf) def createEnrichSearchDialogProp(self, row=-1, column=-1): self.buildSearchDialog(row, column, False, self.findIDPropertyEdit, True, False, None, self.addVocabConf) ## # @brief Creates a search dialog with parameters for interlinking. # # @param self The object pointer # @param row The row of the table for which to map the search result # @param column The column of the table for which to map the search result def createInterlinkSearchDialog(self, row=-1, column=-1): if column > 3 and column < 7: self.buildSearchDialog(row, column, True, self.interlinkTable, True, False, None, self.addVocabConf) elif column >= 7: layers = QgsProject.instance().layerTreeRoot().children() selectedLayerIndex = self.chooseLayerInterlink.currentIndex() layer = layers[selectedLayerIndex].layer() self.buildValueMappingDialog(row, column, True, self.interlinkTable, layer) elif column == -1: self.buildSearchDialog(row, column, -1, self.interlinkOwlClassInput, False, False, None, self.addVocabConf) ## # @brief Shows the configuration table after creating an enrichment result. # # @param self The object pointer # def showConfigTable(self): self.enrichTableResult.hide() self.enrichTable.show() self.startEnrichment.setText("Start Enrichment") self.startEnrichment.clicked.disconnect() self.startEnrichment.clicked.connect(self.enrichtab.enrichLayerProcess) ## # @brief Executes a GUI event when a new SPARQL endpoint is selected. # Usually loads the list of concepts related to the SPARQL endpoint # @param send The sender of the request # def viewselectaction(self): endpointIndex = self.comboBox.currentIndex() if endpointIndex == 0: self.justloadingfromfile = False return concept = "" curindex = self.proxyModel.mapToSource( self.geoClassList.selectionModel().currentIndex()) if self.geoClassList.selectionModel().currentIndex( ) != None and self.geoClassListModel.itemFromIndex( curindex) != None and re.match( r'.*Q[0-9]+.*', self.geoClassListModel.itemFromIndex(curindex).text() ) and not self.geoClassListModel.itemFromIndex( curindex).text().startswith("http"): self.inp_label.setText( self.geoClassListModel.itemFromIndex(curindex).text().split( "(")[0].lower().replace(" ", "_")) concept = "Q" + self.geoClassListModel.itemFromIndex( curindex).text().split("Q")[1].replace(")", "") elif self.geoClassListModel.itemFromIndex(curindex) != None: concept = self.geoClassListModel.itemFromIndex(curindex).data(1) if "querytemplate" in self.triplestoreconf[endpointIndex]: if "wd:Q%%concept%% ." in self.triplestoreconf[endpointIndex][ "querytemplate"][ self.queryTemplates.currentIndex()]["query"]: querytext = "" if concept != None and concept.startswith("http"): querytext = self.triplestoreconf[endpointIndex][ "querytemplate"][self.queryTemplates.currentIndex( )]["query"].replace( "wd:Q%%concept%% .", "wd:" + concept[concept.rfind('/') + 1:] + " .") elif concept != None: querytext = self.triplestoreconf[endpointIndex][ "querytemplate"][self.queryTemplates.currentIndex( )]["query"].replace("wd:Q%%concept%% .", "wd:" + concept + " .") else: querytext = self.triplestoreconf[endpointIndex][ "querytemplate"][ self.queryTemplates.currentIndex()]["query"].replace( "%%concept%%", concept) if self.queryLimit.text().isnumeric( ) and querytext.rfind("LIMIT") != -1: querytext = querytext[0:querytext.rfind( "LIMIT")] + "LIMIT " + self.queryLimit.text() elif self.queryLimit.text().isnumeric() and querytext.rfind( "LIMIT") == -1: querytext = querytext + " LIMIT " + self.queryLimit.text() self.inp_sparql2.setPlainText(querytext) self.inp_sparql2.columnvars = {} if self.geoClassList.selectionModel().currentIndex( ) != None and self.geoClassListModel.itemFromIndex( curindex ) != None and "#" in self.geoClassListModel.itemFromIndex( curindex).text(): self.inp_label.setText( self.geoClassListModel.itemFromIndex(curindex).text() [self.geoClassListModel.itemFromIndex(curindex).text(). rfind('#') + 1:].lower().replace(" ", "_")) elif self.geoClassList.selectionModel().currentIndex( ) != None and self.geoClassListModel.itemFromIndex(curindex) != None: self.inp_label.setText( self.geoClassListModel.itemFromIndex(curindex).text() [self.geoClassListModel.itemFromIndex(curindex).text(). rfind('/') + 1:].lower().replace(" ", "_")) def itemModelToMap(self, model): resdict = {} for row in range(model.rowCount()): index = model.index(row, 0, self) resdict[model.itemFromIndex(index).text()] = model.itemFromIndex( index).data(1) return resdict ## # @brief Deletes a row from the table in the enrichment dialog. # # @param send The sender of the request # def deleteEnrichRow(send): w = send.sender().parent() row = self.enrichTable.indexAt(w.pos()).row() self.enrichTable.removeRow(row) self.enrichTable.setCurrentCell(0, 0) ## # @brief Adds a new row to the table in the enrichment dialog. # # @param self The object pointer # def addEnrichRow(self): layers = QgsProject.instance().layerTreeRoot().children() selectedLayerIndex = self.chooseLayerEnrich.currentIndex() layer = layers[selectedLayerIndex].layer() self.enrichTableResult.hide() fieldnames = [field.name() for field in layer.fields()] item = QTableWidgetItem("new_column") #item.setFlags(QtCore.Qt.ItemIsEnabled) row = self.enrichTable.rowCount() self.enrichTable.insertRow(row) self.enrichTable.setItem(row, 0, item) cbox = QComboBox() cbox.addItem("Get Remote") cbox.addItem("No Enrichment") cbox.addItem("Exclude") self.enrichTable.setCellWidget(row, 3, cbox) cbox = QComboBox() cbox.addItem("Enrich Value") cbox.addItem("Enrich URI") cbox.addItem("Enrich Both") self.enrichTable.setCellWidget(row, 4, cbox) cbox = QComboBox() for fieldd in fieldnames: cbox.addItem(fieldd) self.enrichTable.setCellWidget(row, 5, cbox) itemm = QTableWidgetItem("http://www.w3.org/2000/01/rdf-schema#label") self.enrichTable.setItem(row, 6, itemm) itemm = QTableWidgetItem("") self.enrichTable.setItem(row, 7, itemm) itemm = QTableWidgetItem("") self.enrichTable.setItem(row, 8, itemm) ## Validates the SPARQL query in the input field and outputs errors in a label. # @param self The object pointer. def validateSPARQL(self): if self.prefixes != None and self.comboBox != None and self.comboBox.currentIndex( ) != None and self.prefixes[self.comboBox.currentIndex( )] != None and self.inp_sparql2.toPlainText( ) != None and self.inp_sparql2.toPlainText() != "": try: if self.prefixes[self.comboBox.currentIndex()] != "": prepareQuery( "".join(self.prefixes[self.comboBox.currentIndex()]) + "\n" + self.inp_sparql2.toPlainText()) self.errorLabel.setText("Valid Query") self.errorline = -1 self.sparqlhighlight.errorhighlightline = self.errorline self.sparqlhighlight.currentline = 0 self.inp_sparql2.errorline = None except Exception as e: match = re.search(r'line:([0-9]+),', str(e)) match2 = re.search(r'col:([0-9]+),', str(e)) start = int(match.group(1)) - len(self.triplestoreconf[ self.comboBox.currentIndex()]["prefixes"]) - 1 self.errorLabel.setText( re.sub("line:([0-9]+),", "line: " + str(start) + ",", str(e))) self.inp_sparql2.errorline = start - 1 if "line" in str(e): ex = str(e) start = ex.find('line:') + 5 end = ex.find(',', start) start2 = ex.find('col:') + 4 end2 = ex.find(')', start2) self.errorline = ex[start:end] self.sparqlhighlight.errorhighlightcol = ex[start2:end2] self.sparqlhighlight.errorhighlightline = self.errorline self.sparqlhighlight.currentline = 0 ## # @brief Builds the search dialog to search for a concept or class. # @param self The object pointer # @param row the row to insert the result # @param column the column to insert the result # @param interlinkOrEnrich indicates if the dialog is meant for interlinking or enrichment # @param table the GUI element to display the result def buildSearchDialog(self, row, column, interlinkOrEnrich, table, propOrClass, bothOptions=False, currentprefixes=None, addVocabConf=None): self.currentcol = column self.currentrow = row self.interlinkdialog = SearchDialog(column, row, self.triplestoreconf, self.prefixes, interlinkOrEnrich, table, propOrClass, bothOptions, currentprefixes, addVocabConf) self.interlinkdialog.setMinimumSize(650, 400) self.interlinkdialog.setWindowTitle("Search Interlink Concept") self.interlinkdialog.exec_() ## # @brief Builds a boundingbox dialog allows to pick a bounding box for a SPARQL query. # # @param self The object pointer def getPointFromCanvas(self): self.d = BBOXDialog(self.inp_sparql2, self.triplestoreconf, self.comboBox.currentIndex()) self.d.setWindowTitle("Choose BoundingBox") self.d.exec_() ## # @brief Builds a value mapping dialog window for ther interlinking dialog. # # @param self The object pointer # @param row The row of the table for which to map the value # @param column The column of the table for which to map the value # @param table The table in which to save the value mapping result # @param layer The layer which is concerned by the enrichment oder interlinking def buildValueMappingDialog(self, row, column, interlinkOrEnrich, table, layer): self.currentcol = column self.currentrow = row valuemap = None if table.item(row, column) != None and table.item(row, column).text() != "": valuemap = table.item(row, column).data(1) self.interlinkdialog = ValueMappingDialog(column, row, self.triplestoreconf, interlinkOrEnrich, table, table.item(row, 3).text(), layer, valuemap) self.interlinkdialog.setMinimumSize(650, 400) self.interlinkdialog.setWindowTitle("Get Value Mappings for column " + table.item(row, 3).text()) self.interlinkdialog.exec_() ## # @brief Loads a QGIS layer for interlinking into the interlinking dialog. # # @param self The object pointer def loadLayerForInterlink(self): layers = QgsProject.instance().layerTreeRoot().children() selectedLayerIndex = self.chooseLayerInterlink.currentIndex() if len(layers) == 0: return layer = layers[selectedLayerIndex].layer() fieldnames = [field.name() for field in layer.fields()] while self.interlinkTable.rowCount() > 0: self.interlinkTable.removeRow(0) row = 0 self.interlinkTable.setHorizontalHeaderLabels([ "Export?", "IDColumn?", "GeoColumn?", "Column", "ColumnProperty", "PropertyType", "ColumnConcept", "ValueConcepts" ]) self.interlinkTable.setColumnCount(8) for field in fieldnames: item = QTableWidgetItem(field) item.setFlags(QtCore.Qt.ItemIsEnabled) item2 = QTableWidgetItem() item2.setCheckState(True) item3 = QTableWidgetItem() item3.setCheckState(False) item4 = QTableWidgetItem() item4.setCheckState(False) self.interlinkTable.insertRow(row) self.interlinkTable.setItem(row, 3, item) self.interlinkTable.setItem(row, 0, item2) self.interlinkTable.setItem(row, 1, item3) self.interlinkTable.setItem(row, 2, item4) cbox = QComboBox() cbox.addItem("Automatic") cbox.addItem("AnnotationProperty") cbox.addItem("DataProperty") cbox.addItem("ObjectProperty") cbox.addItem("SubClass") self.interlinkTable.setCellWidget(row, 5, cbox) currentRowCount = self.interlinkTable.rowCount() row += 1 ## # @brief Loads a QGIS layer for enrichment into the enrichment dialog. # # @param self The object pointer def loadLayerForEnrichment(self): layers = QgsProject.instance().layerTreeRoot().children() selectedLayerIndex = self.chooseLayerEnrich.currentIndex() if len(layers) == 0: return layer = layers[selectedLayerIndex].layer() self.enrichTableResult.hide() while self.enrichTableResult.rowCount() > 0: self.enrichTableResult.removeRow(0) self.enrichTable.show() self.addEnrichedLayerRowButton.setEnabled(True) fieldnames = [field.name() for field in layer.fields()] while self.enrichTable.rowCount() > 0: self.enrichTable.removeRow(0) row = 0 self.enrichTable.setColumnCount(9) self.enrichTable.setHorizontalHeaderLabels([ "Column", "EnrichmentConcept", "TripleStore", "Strategy", "content", "ID Column", "ID Property", "ID Domain", "Language" ]) for field in fieldnames: item = QTableWidgetItem(field) item.setFlags(QtCore.Qt.ItemIsEnabled) currentRowCount = self.enrichTable.rowCount() self.enrichTable.insertRow(row) self.enrichTable.setItem(row, 0, item) cbox = QComboBox() cbox.addItem("No Enrichment") cbox.addItem("Keep Local") cbox.addItem("Keep Remote") cbox.addItem("Replace Local") cbox.addItem("Merge") cbox.addItem("Ask User") cbox.addItem("Exclude") self.enrichTable.setCellWidget(row, 3, cbox) cbox = QComboBox() cbox.addItem("Enrich Value") cbox.addItem("Enrich URI") cbox.addItem("Enrich Both") self.enrichTable.setCellWidget(row, 4, cbox) cbox = QComboBox() for fieldd in fieldnames: cbox.addItem(fieldd) self.enrichTable.setCellWidget(row, 5, cbox) itemm = QTableWidgetItem( "http://www.w3.org/2000/01/rdf-schema#label") self.enrichTable.setItem(row, 6, itemm) itemm = QTableWidgetItem("") self.enrichTable.setItem(row, 7, itemm) itemm = QTableWidgetItem("") self.enrichTable.setItem(row, 8, itemm) celllayout = QHBoxLayout() upbutton = QPushButton("Up") removebutton = QPushButton("Remove", self) removebutton.clicked.connect(self.deleteEnrichRow) downbutton = QPushButton("Down") celllayout.addWidget(upbutton) celllayout.addWidget(downbutton) celllayout.addWidget(removebutton) w = QWidget() w.setLayout(celllayout) optitem = QTableWidgetItem() #self.enrichTable.setCellWidget(row,4,w) #self.enrichTable.setItem(row,3,cbox) row += 1 self.originalRowCount = row ## Fetch the currently loaded layers. # @param self The object pointer. def loadUnicornLayers(self): layers = QgsProject.instance().layerTreeRoot().children() # Populate the comboBox with names of all the loaded unicorn layers self.loadedLayers.clear() self.chooseLayerInterlink.clear() self.chooseLayerEnrich.clear() for layer in layers: ucl = layer.name() #if type(layer) == QgsMapLayer.VectorLayer: self.loadedLayers.addItem(layer.name()) self.chooseLayerInterlink.addItem(layer.name()) self.chooseLayerEnrich.addItem(layer.name())
class geopunt4QgisDataCatalog(QDialog): def __init__(self, iface): QDialog.__init__(self, None) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.iface = iface # initialize locale locale = QSettings().value("locale/userLocale", "en") if not locale: locale = 'en' else: locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_geopunt4QgisDataCatalogDlg() self.ui.setupUi(self) # get settings self.s = QSettings() self.loadSettings() self.gh = geometryHelper(self.iface) # setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton(QPushButton("Sluiten"), QDialogButtonBox.RejectRole) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) # vars self.firstShow = True self.wms = None self.wfs = None self.dl = None self.zoek = '' self.bronnen = None self.model = QStandardItemModel(self) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel(self.proxyModel) self.completer = QCompleter(self) self.completerModel = QStringListModel(self) self.ui.zoekTxt.setCompleter(self.completer) self.completer.setModel(self.completerModel) # eventhandlers self.ui.zoekBtn.clicked.connect(self.onZoekClicked) self.ui.addWMSbtn.clicked.connect(self.addWMS) self.ui.addWFSbtn.clicked.connect(self.addWFS) self.ui.DLbtn.clicked.connect(lambda: self.openUrl(self.dl)) self.ui.resultView.clicked.connect(self.resultViewClicked) self.ui.modelFilterCbx.currentIndexChanged.connect(self.modelFilterCbxIndexChanged) self.ui.filterWgt.setHidden(1) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean) def loadSettings(self): self.timeout = int(self.s.value("geopunt4qgis/timeout", 15)) if settings().proxyUrl: self.proxy = settings().proxyUrl else: self.proxy = "" self.md = MDReader(self.timeout, self.proxy) def openHelp(self): webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/catalogus") def _setModel(self, records): self.model.clear() records = sorted(records, key=lambda k: k['title']) for rec in records: title = QStandardItem(rec['title']) # 0 wms = QStandardItem(rec['wms']) # 1 downloadLink = QStandardItem(rec['download']) # 2 id = QStandardItem(rec['uuid']) # 3 abstract = QStandardItem(rec['abstract']) # 4 wfs = QStandardItem(rec['wfs']) # 5 self.model.appendRow([title, wms, downloadLink, id, abstract, wfs]) # overwrite def show(self): QDialog.show(self) self.setWindowModality(0) metadataUrl = "https://metadata.geopunt.be" inet = internet_on(proxyUrl=self.proxy, timeout=self.timeout, testSite= metadataUrl) if not inet: msg = "Kan geen verbing maken met de metadata van Geopunt: {} \nMogelijke is deze site niet bereikbaar, dan zal deze tool ook niet werken.\nProbeer later opnieuw. Indien dit probleem zich blijft voordoen contacteer informatie Vlaanderen.".format(metadataUrl) QMessageBox.warning(self.iface.mainWindow(), "Waarschuwing: kan geen verbinding maken", msg) self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"), msg, level=Qgis.Warning, duration=3) return if self.firstShow: self.ui.GDIThemaCbx.addItems([''] + self.md.list_GDI_theme()) self.ui.organisatiesCbx.addItems([''] + self.md.list_organisations()) keywords = sorted(self.md.list_suggestionKeyword()) self.completerModel.setStringList(keywords) self.bronnen = self.md.list_bronnen() self.ui.bronCbx.addItems([''] + [n[1] for n in self.bronnen]) self.ui.typeCbx.addItems([''] + [n[0] for n in self.md.dataTypes]) self.ui.INSPIREannexCbx.addItems([''] + self.md.inspireannex) self.ui.INSPIREserviceCbx.addItems([''] + self.md.inspireServiceTypes) self.ui.INSPIREthemaCbx.addItems([''] + self.md.list_inspire_theme()) self.firstShow = False # eventhandlers def resultViewClicked(self): if self.ui.resultView.selectedIndexes(): row = self.ui.resultView.selectedIndexes()[0].row() title = self.proxyModel.data(self.proxyModel.index(row, 0)) self.wms = self.proxyModel.data(self.proxyModel.index(row, 1)) self.dl = self.proxyModel.data(self.proxyModel.index(row, 2)) self.wfs = self.proxyModel.data(self.proxyModel.index(row, 5)) uuid = self.proxyModel.data(self.proxyModel.index(row, 3)) abstract = self.proxyModel.data(self.proxyModel.index(row, 4)) self.ui.descriptionText.setText( """<h3>%s</h3><div>%s</div><br/><div> <a href='https://metadata.geopunt.be/zoekdienst/apps/tabsearch/index.html?uuid=%s'> Ga naar fiche</a></div>""" % (title, abstract, uuid)) if self.wms: self.ui.addWMSbtn.setEnabled(1) else: self.ui.addWMSbtn.setEnabled(0) if self.wfs: self.ui.addWFSbtn.setEnabled(1) else: self.ui.addWFSbtn.setEnabled(0) if self.dl: self.ui.DLbtn.setEnabled(1) else: self.ui.DLbtn.setEnabled(0) def onZoekClicked(self): self.zoek = self.ui.zoekTxt.currentText() self.search() def modelFilterCbxIndexChanged(self): value = self.ui.modelFilterCbx.currentIndex() if value == 1: self.filterModel(1) elif value == 2: self.filterModel(5) elif value == 3: self.filterModel(2) else: self.filterModel() def filterModel(self, col=None): if col != None: self.proxyModel.setFilterKeyColumn(col) expr = QRegExp("?*", Qt.CaseInsensitive, QRegExp.Wildcard) self.proxyModel.setFilterRegExp(expr) else: self.proxyModel.setFilterRegExp(None) def search(self): try: if self.ui.filterBox.isChecked(): themekey = self.ui.GDIThemaCbx.currentText() orgName = self.ui.organisatiesCbx.currentText() dataTypes = [n[1] for n in self.md.dataTypes if n[0] == self.ui.typeCbx.currentText()] if dataTypes != []: dataType = dataTypes[0] else: dataType = '' siteIds = [n[0] for n in self.bronnen if n[1] == self.ui.bronCbx.currentText()] if siteIds != []: siteId = siteIds[0] else: siteId = '' inspiretheme = self.ui.INSPIREthemaCbx.currentText() inspireannex = self.ui.INSPIREannexCbx.currentText() inspireServiceType = self.ui.INSPIREserviceCbx.currentText() searchResult = MDdata(self.md.searchAll( self.zoek, themekey, orgName, dataType, siteId, inspiretheme, inspireannex, inspireServiceType)) else: searchResult = MDdata(self.md.searchAll(self.zoek)) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=3) return self.ui.countLbl.setText("Aantal gevonden: %s" % searchResult.count) self.ui.descriptionText.setText('') self._setModel(searchResult.records) if searchResult.count == 0: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QCoreApplication.translate("geopunt4QgisPoidialog", "Er werden geen resultaten gevonde voor deze zoekopdracht"), duration=5) def openUrl(self, url): if url: webbrowser.open_new_tab(url.encode("utf-8")) def addWMS(self): if self.wms == None: return crs = self.gh.getGetMapCrs(self.iface).authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' try: lyrs = getWmsLayerNames(self.wms, self.proxy) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMS", QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wms), level=Qgis.Warning, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QInputDialog.getItem(self, "WMS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle][0] style = [n[2] for n in lyrs if n[1] == layerTitle][0] if not style: style = "" url = self.wms.split('?')[0] if crs != 'EPSG:31370' or crs != 'EPSG:3857': crs = 'EPSG:31370' wmsUrl = "contextualWMSLegend=0&dpiMode=7&url=%s&layers=%s&format=image/png&styles=%s&crs=%s" % ( url, layerName, style, crs) try: rlayer = QgsRasterLayer(wmsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsProject.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan WMS niet laden"), level=Qgis.Critical, duration=10) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return def addWFS(self): if self.wfs == None: return try: lyrs = getWFSLayerNames(self.wfs, self.proxy) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WFS", QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wfs), level=Qgis.Warning, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QInputDialog.getItem(self, "WFS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle][0] crs = [n[2] for n in lyrs if n[1] == layerTitle][0] url = self.wfs.split('?')[0] wfsUri = makeWFSuri(url, layerName, crs ) try: vlayer = QgsVectorLayer(wfsUri, layerTitle, "WFS") QgsProject.instance().addMapLayer(vlayer) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return def clean(self): self.model.clear() self.wms = None self.wfs = None self.dl = None self.ui.zoekTxt.setCurrentIndex(0) self.ui.descriptionText.setText('') self.ui.countLbl.setText("") self.ui.msgLbl.setText("") self.ui.DLbtn.setEnabled(0) self.ui.addWFSbtn.setEnabled(0) self.ui.addWMSbtn.setEnabled(0) self.ui.modelFilterCbx.setCurrentIndex(0)
def refresh_model(self, source_model, db_connector=None, silent=False): filtered_source_model = QSortFilterProxyModel() filtered_source_model.setSourceModel(source_model) filtered_source_model.setFilterRole(int(SourceModel.Roles.TYPE)) self.print_info.emit(self.tr("Refresh available models:")) self.clear() previously_checked_models = self._checked_models self._checked_models = {} # models from db db_modelnames = self._db_modelnames(db_connector) # models from the repos filtered_source_model.setFilterFixedString("model") for r in range(0, filtered_source_model.rowCount()): filtered_source_model_index = filtered_source_model.index( r, SourceModel.Columns.SOURCE) modelname = filtered_source_model_index.data( int(SourceModel.Roles.NAME)) if modelname: enabled = modelname not in db_modelnames self.add_source( modelname, filtered_source_model_index.data( int(SourceModel.Roles.TYPE)), filtered_source_model_index.data( int(SourceModel.Roles.PATH)), filtered_source_model_index.data( int(SourceModel.Roles.ORIGIN_INFO)), previously_checked_models.get( ( modelname, filtered_source_model_index.data( int(SourceModel.Roles.PATH)), ), Qt.Checked, ) if enabled and modelname not in self.checked_models() else Qt.Unchecked, enabled, ) if not silent: self.print_info.emit( self.tr("- Append (repository) model {}{}").format( modelname, " (inactive because it already exists in the database)" if not enabled else "", )) # models from the files filtered_source_model.setFilterFixedString("ili") for r in range(0, filtered_source_model.rowCount()): filtered_source_model_index = filtered_source_model.index( r, SourceModel.Columns.SOURCE) ili_file_path = filtered_source_model_index.data( int(SourceModel.Roles.PATH)) self.ilicache = IliCache(None, ili_file_path) models = self.ilicache.process_ili_file(ili_file_path) for model in models: if model["name"]: enabled = model["name"] not in db_modelnames self.add_source( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.TYPE)), filtered_source_model_index.data( int(SourceModel.Roles.PATH)), filtered_source_model_index.data( int(SourceModel.Roles.ORIGIN_INFO)), previously_checked_models.get( ( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.PATH)), ), Qt.Checked if model is models[-1] and enabled and model["name"] not in self.checked_models() else Qt.Unchecked, ), enabled, ) if not silent: self.print_info.emit( self.tr("- Append (file) model {}{} from {}"). format( model["name"], " (inactive because it already exists in the database)" if not enabled else "", ili_file_path, )) # models from the transfer files filtered_source_model.setFilterRegExp("|".join(TransferExtensions)) for r in range(0, filtered_source_model.rowCount()): filtered_source_model_index = filtered_source_model.index( r, SourceModel.Columns.SOURCE) xtf_file_path = filtered_source_model_index.data( int(SourceModel.Roles.PATH)) models = self._transfer_file_models(xtf_file_path) for model in models: if model["name"]: enabled = model["name"] not in db_modelnames self.add_source( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.TYPE)), filtered_source_model_index.data( int(SourceModel.Roles.PATH)), filtered_source_model_index.data( int(SourceModel.Roles.ORIGIN_INFO)), previously_checked_models.get( ( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.PATH)), ), Qt.Checked if enabled and model["name"] not in self.checked_models() else Qt.Unchecked, ), enabled, ) if not silent: self.print_info.emit( self.tr("- Append (xtf file) model {}{} from {}"). format( model["name"], " (inactive because it already exists in the database)" if not enabled else "", xtf_file_path, )) return self.rowCount()
def setup_class_table(self, classes=[]): # Load the codes each class will be recoded to. # # The "classes" parameter will include any mappings derived from a # class definition file, or, in the case or reading in user land cover # files, classes from the file itself. # # The default codes stored in self.default_classes are derived either # from the data/land_cover_classes_ESA_to_IPCC.json file when this class is # instantiated from the LCSetupWidget, or from the values within a # custom user data file when this class is instantiated from the # DlgDataIOImportLC class. if len(classes) > 0: input_codes = sorted([c['Initial_Code'] for c in classes]) default_codes = sorted([c['Initial_Code'] for c in self.default_classes]) new_codes = [c for c in input_codes if c not in default_codes] missing_codes = [c for c in default_codes if c not in input_codes] if len(new_codes) > 0: QtWidgets.QMessageBox.warning(None, self.tr("Warning"), self.tr(u"Some of the class codes ({}) in the definition file do not appear in the chosen data file.".format(', '.join([str(c) for c in new_codes])))) if len(missing_codes) > 0: QtWidgets.QMessageBox.warning(None, self.tr("Warning"), self.tr(u"Some of the class codes ({}) in the data file do not appear in the chosen definition file.".format(', '.join([str(c) for c in missing_codes])))) # Setup a new classes list with the new class codes for all classes # included in default classes, and any other class codes that are # missing added from the default class list classes = [c for c in classes if c['Initial_Code'] in default_codes] classes.extend([c for c in self.default_classes if c['Initial_Code'] not in input_codes]) else: classes = self.default_classes table_model = LCAggTableModel(classes, parent=self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.remap_view.setModel(proxy_model) # Add selector in cell for row in range(0, len(classes)): lc_class_combo = LCClassComboBox() # Now Set the default final codes for each row. Note that the # QComboBox entries are potentially translated, so need to link the # translated names back to a particular code. # Get the input code for this row and the final label it should map # to by default input_code = table_model.index(row, 0).data() final_label = [c['Final_Label'] for c in classes if c['Initial_Code'] == input_code][0] # Figure out which label translation this Final_Label (in English) # is equivalent to label_to_label_tr = {final_classes[key]['label']: key for key in final_classes.keys()} final_label_tr = label_to_label_tr[final_label] # Now find the index in the combo box of this translated final # label ind = lc_class_combo.findText(final_label_tr) if ind != -1: lc_class_combo.setCurrentIndex(ind) self.remap_view.setIndexWidget(proxy_model.index(row, 2), lc_class_combo) self.remap_view.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) self.remap_view.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) self.remap_view.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) self.remap_view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) return True
class ThreeDiResultSelectionWidget(QWidget, FORM_CLASS): """Dialog for selecting model (spatialite and result files netCDFs) TODO: actually, it is two dialogs in one: a selector of results and a separate downloader of results. They are not really connected. So splitting them makes the code simpler. And, more importantly, it makes the UI clearer. """ closingDialog = pyqtSignal() def __init__( self, parent=None, iface=None, ts_datasources=None, downloadable_results=None, tool=None, ): """Constructor :parent: Qt parent Widget :iface: QGiS interface :ts_datasources: TimeseriesDatasourceModel instance :downloadable_results: DownloadableResultModel instance :tool: the tool class which instantiated this widget. Is used here for storing volatile information """ super().__init__(parent) self.tool = tool self.iface = iface self.setupUi(self) # login administration self.login_dialog = LoginDialog() # NOTE: autoDefault was set on ``log_in_button`` (via Qt Designer), # which makes pressing Enter work for logging in. self.login_dialog.log_in_button.clicked.connect(self.handle_log_in) # set models on table views and update view columns self.ts_datasources = ts_datasources self.resultTableView.setModel(self.ts_datasources) self.ts_datasources.set_column_sizes_on_view(self.resultTableView) self.downloadable_results = downloadable_results self.download_proxy_model = QSortFilterProxyModel() self.download_proxy_model.setSourceModel(self.downloadable_results) self.download_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filterLineEdit.textChanged.connect( self.download_proxy_model.setFilterFixedString) self.downloadResultTableView.setModel(self.download_proxy_model) self.toggle_login_interface() # connect signals self.selectTsDatasourceButton.clicked.connect( self.select_ts_datasource) self.closeButton.clicked.connect(self.close) self.removeTsDatasourceButton.clicked.connect( self.remove_selected_ts_datasource) self.selectModelSpatialiteButton.clicked.connect( self.select_model_spatialite_file) self.loginButton.clicked.connect(self.on_login_button_clicked) # set combobox list combo_list = [ datasource for datasource in self.get_3di_spatialites_legendlist() ] if (self.ts_datasources.model_spatialite_filepath and self.ts_datasources.model_spatialite_filepath not in combo_list): combo_list.append(self.ts_datasources.model_spatialite_filepath) if not self.ts_datasources.model_spatialite_filepath: combo_list.append("") self.modelSpatialiteComboBox.addItems(combo_list) if self.ts_datasources.model_spatialite_filepath: current_index = self.modelSpatialiteComboBox.findText( self.ts_datasources.model_spatialite_filepath) self.modelSpatialiteComboBox.setCurrentIndex(current_index) else: current_index = self.modelSpatialiteComboBox.findText("") self.modelSpatialiteComboBox.setCurrentIndex(current_index) self.modelSpatialiteComboBox.currentIndexChanged.connect( self.model_spatialite_change) self.thread = None def on_close(self): """ Clean object on close """ self.selectTsDatasourceButton.clicked.disconnect( self.select_ts_datasource) self.closeButton.clicked.disconnect(self.close) self.removeTsDatasourceButton.clicked.disconnect( self.remove_selected_ts_datasource) self.selectModelSpatialiteButton.clicked.disconnect( self.select_model_spatialite_file) self.loginButton.clicked.disconnect(self.on_login_button_clicked) # stop the thread when we close the widget if self.thread: self.thread.output.disconnect(self.update_download_result_model) self.thread.stop() def closeEvent(self, event): """ Close widget, called by Qt on close :param event: QEvent, close event """ self.closingDialog.emit() self.on_close() event.accept() def keyPressEvent(self, event): """Handle key press events on the widget.""" # Close window if the Escape key is pressed if event.key() == Qt.Key_Escape: self.close() def select_ts_datasource(self): """ Open File dialog for selecting netCDF result files, triggered by button :return: boolean, if file is selected """ settings = QSettings("3di", "qgisplugin") try: init_path = settings.value("last_used_datasource_path", type=str) except TypeError: logger.debug( "Last used datasource path is no string, setting it to our home dir." ) init_path = os.path.expanduser("~") filename, __ = QFileDialog.getOpenFileName( self, "Open resultaten file", init_path, "NetCDF (subgrid_map.nc results_3di.nc)", ) if filename: # Little test for checking if there is an id mapping file available # If not we check if an .h5 file is available # If not we're not going to proceed datasource_type = detect_netcdf_version(filename) logger.info("Netcdf result file selected: %s, type is %s", filename, datasource_type) if datasource_type == "netcdf-groundwater": try: find_h5_file(filename) except FileNotFoundError: logger.warning( "Groundwater h5 not found (%s), warning the user.", filename) pop_up_info( "You selected a netcdf that was created " "(after May 2018) with a 3Di calculation" "core that is able to include groundwater" " calculations. The ThreeDiToolbox reads " "this netcdf together with an .h5 file, we " "could however not find this .h5 file. Please " "add this file next to the netcdf and try " "again", title="Error", ) return False elif datasource_type == "netcdf": logger.warning( "Result file (%s) version is too old. Warning the user.", filename) pop_up_info( "The selected result data is too old and no longer " "supported in this version of ThreediToolbox. Please " "recalculate the results with a newer version of the " "threedicore or use the ThreediToolbox plugin for QGIS 2", title="Error", ) # TODO: the below "return False" was previously missing. Check # if Reinout was right in adding it :-) return False items = [{ "type": datasource_type, "name": os.path.basename(filename).lower().rstrip(".nc"), "file_path": filename, }] self.ts_datasources.insertRows(items) settings.setValue("last_used_datasource_path", os.path.dirname(filename)) return True return False def remove_selected_ts_datasource(self): """ Remove selected result files from model, called by 'remove' button """ selection_model = self.resultTableView.selectionModel() # get unique rows in selected fields rows = set( [index.row() for index in selection_model.selectedIndexes()]) for row in reversed(sorted(rows)): self.ts_datasources.removeRows(row, 1) def get_3di_spatialites_legendlist(self): """ Get list of spatialite data sources currently active in canvas :return: list of strings, unique spatialite paths """ tdi_spatialites = [] for layer in self.iface.layerTreeView().selectedLayers(): if (layer.name() in list(LAYER_QH_TYPE_MAPPING.keys()) and layer.dataProvider().name() == "spatialite"): source = layer.dataProvider().dataSourceUri().split("'")[1] if source not in tdi_spatialites: tdi_spatialites.append(source) return tdi_spatialites def model_spatialite_change(self, nr): """ Change active modelsource. Called by combobox when selected spatialite changed :param nr: integer, nr of item selected in combobox """ filepath = self.modelSpatialiteComboBox.currentText() logger.info("Different spatialite 3di model selected: %s", filepath) self.ts_datasources.model_spatialite_filepath = filepath # Just emitting some dummy model indices cuz what else can we do, there # is no corresponding rows/columns that's been changed self.ts_datasources.dataChanged.emit(QModelIndex(), QModelIndex()) def select_model_spatialite_file(self): """ Open file dialog on click on button 'load model' :return: Boolean, if file is selected """ settings = QSettings("3di", "qgisplugin") try: init_path = settings.value("last_used_spatialite_path", type=str) except TypeError: logger.debug( "Last used datasource path is no string, setting it to our home dir." ) init_path = os.path.expanduser("~") filepath, __ = QFileDialog.getOpenFileName( self, "Open 3Di model spatialite file", init_path, "Spatialite (*.sqlite)") if filepath == "": return False self.ts_datasources.spatialite_filepath = filepath index_nr = self.modelSpatialiteComboBox.findText(filepath) if index_nr < 0: self.modelSpatialiteComboBox.addItem(filepath) index_nr = self.modelSpatialiteComboBox.findText(filepath) self.modelSpatialiteComboBox.setCurrentIndex(index_nr) add_spatialite_connection(filepath, self.iface) settings.setValue("last_used_spatialite_path", os.path.dirname(filepath)) return True def on_login_button_clicked(self): """Handle log in and out.""" if self.logged_in: self.handle_log_out() else: self.login_dialog.user_name_input.setFocus() self.login_dialog.show() def handle_log_out(self): self.set_logged_out_status() if self.thread: self.thread.stop() num_rows = len(self.downloadable_results.rows) self.downloadable_results.removeRows(0, num_rows) self.toggle_login_interface() def toggle_login_interface(self): """Enable/disable aspects of the interface based on login status.""" # TODO: better to use signals maybe? if self.logged_in: self.loginButton.setText("Log out") self.downloadResultTableView.setEnabled(True) self.downloadResultButton.setEnabled(True) else: self.loginButton.setText("Log in") self.downloadResultTableView.setEnabled(False) self.downloadResultButton.setEnabled(False) def handle_log_in(self): """Handle logging in and populating DownloadableResultModel.""" # Get the username and password username = self.login_dialog.user_name_input.text() password = self.login_dialog.user_password_input.text() if username == "" or password == "": pop_up_info("Username or password cannot be empty.") return try: scenarios_endpoint = Endpoint(username=username, password=password, endpoint="scenarios") endpoint = scenarios_endpoint.get_paginated(page_size=10) except HTTPError as e: logger.exception("Error trying to log in") if e.code == 401: pop_up_info("Incorrect username and/or password.") else: pop_up_info(str(e)) return self.set_logged_in_status(username, password) self.toggle_login_interface() # don't persist info in the dialog: useful when logged out self.login_dialog.user_name_input.clear() self.login_dialog.user_password_input.clear() # start thread self.thread = DownloadWorker(endpoint=endpoint, username=username, password=password) self.thread.connection_failure.connect(self.handle_connection_failure) self.thread.output.connect(self.update_download_result_model) self.thread.start() # return to widget self.login_dialog.close() def update_download_result_model(self, items): self.downloadable_results.insertRows(items) def handle_connection_failure(self, status, reason): pop_up_info("Something went wrong trying to connect to " "lizard: {0} {1}".format(status, reason)) self.handle_log_out() @property def username(self): return self.tool.username @username.setter def username(self, username): self.tool.username = username @property def password(self): return self.tool.password @password.setter def password(self, password): self.tool.password = password @property def logged_in(self): """Return the logged in status.""" return self.tool.logged_in def set_logged_in_status(self, username, password): """Set logged in status to True.""" # TODO: it doesn't really do that. I *suspect* that the parent class # has a logged_in() method that checks if username and password are # set there. It's all quite indirect. self.username = username self.password = password def set_logged_out_status(self): """Set logged in status to False.""" self.username = None self.password = None
class RasterAttributeTableDialog(QDialog): def __init__(self, raster_layer, iface=None): QDialog.__init__(self) # Set up the user interface from Designer. ui_path = os.path.join(os.path.dirname(__file__), 'Ui_RasterAttributeTableDialog.ui') uic.loadUi(ui_path, self) self.raster_layer = raster_layer self.iface = iface self.editable = False self.is_dirty = False self.mRasterBandsComboBox.addItems([ raster_layer.bandName(bn) for bn in range(1, raster_layer.bandCount() + 1) ]) self.mToggleEditingToolButton.setIcon( QgsApplication.getThemeIcon("/mActionEditTable.svg")) self.mAddColumnToolButton.setIcon( QgsApplication.getThemeIcon("/mActionNewAttribute.svg")) self.mAddRowToolButton.setIcon( QgsApplication.getThemeIcon("/mActionNewTableRow.svg")) self.mRemoveRowToolButton.setIcon( QgsApplication.getThemeIcon("/mActionRemoveSelectedFeature.svg")) self.mRemoveColumnToolButton.setIcon( QgsApplication.getThemeIcon("/mActionDeleteAttribute.svg")) self.mSaveChangesToolButton.setIcon( QgsApplication.getThemeIcon("/mActionSaveAllEdits.svg")) stylesheet = "QToolButton {padding: 1px;}" self.mToggleEditingToolButton.setStyleSheet(stylesheet) self.mAddColumnToolButton.setStyleSheet(stylesheet) self.mRemoveColumnToolButton.setStyleSheet(stylesheet) self.mSaveChangesToolButton.setStyleSheet(stylesheet) assert self.loadRat(0) self.setEditable(False) # Connections self.mClassifyButton.clicked.connect(self.classify) self.mRasterBandsComboBox.currentIndexChanged.connect(self.loadRat) self.mButtonBox.accepted.connect(self.accept) self.mButtonBox.rejected.connect(self.reject) self.mToggleEditingToolButton.toggled.connect(self.setEditable) self.mSaveChangesToolButton.clicked.connect(self.saveChanges) self.mAddColumnToolButton.clicked.connect(self.addColumn) self.mRemoveColumnToolButton.clicked.connect(self.removeColumn) self.mAddRowToolButton.clicked.connect(self.addRow) self.mRemoveRowToolButton.clicked.connect(self.removeRow) try: self.restoreGeometry(QgsSettings().value( "RasterAttributeTable/geometry", None, QByteArray, QgsSettings.Plugins)) except: pass self.updateButtons() def addRow(self): current_row = self.proxyModel.mapToSource( self.mRATView.selectionModel().currentIndex()).row() dlg = AddRowDialog(current_row) if dlg.exec_() == QDialog.Accepted: if not self.model.insert_row( current_row + (0 if dlg.mBefore.isChecked() else 1)): QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Row"), QCoreApplication.translate( 'RAT', "An error occourred while adding a new row to the RAT!" )) def removeRow(self): if QMessageBox.question( None, QCoreApplication.translate('RAT', "Remove Row"), QCoreApplication.translate( 'RAT', "Removing a row will remove the value from the RAT, do you want to continue?" )) == QMessageBox.Yes: current_row = self.proxyModel.mapToSource( self.mRATView.selectionModel().currentIndex()).row() if not self.model.remove_row(current_row): QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Removing Row"), QCoreApplication.translate( 'RAT', "An error occourred while removing a row from the RAT!" )) def allowedAddedUsages(self) -> list: """Return the list of not-color usages that can be added :return: allowed usages that can be added :rtype: list """ allowed_usages = [] usages = self.model.rat.field_usages for usage, info in rat_supported_column_info().items(): if not info['is_color'] and (not info['unique'] or usage not in usages): allowed_usages.append(usage) return allowed_usages def canAddAnyColumn(self) -> bool: """Check if any column can be added""" return not self.model.has_color or len(self.allowedAddedUsages()) > 0 def addColumn(self): dlg = AddColumnDialog(self.model, self.iface) if self.model.has_color: dlg.mColumnType.hide() # List columns where insertion is allowed: skip value and count for field in self.model.rat.fields.values(): if field.usage not in {gdal.GFU_MinMax, gdal.GFU_PixelCount}: dlg.mColumn.addItem(field.name) # List allowed usages allowed_usages = self.allowedAddedUsages() # Set insertion point col = self.proxyModel.mapToSource( self.mRATView.selectionModel().currentIndex()).column() header = self.model.headers[col] dlg.mColumn.setCurrentIndex(dlg.mColumn.findText(header)) if not allowed_usages: if not self.model.has_color: dlg.mColor.setChecked(True) dlg.mStandardColumn.setEnabled(False) else: # We cannot add any field, we should never get here! rat_log( 'Cannot add any column: this should have been checked before getting so far!', Qgis.Critical) else: usages_info = rat_column_info() for usage in allowed_usages: dlg.mUsage.addItem(usages_info[usage]['name'], usage) if dlg.exec_() == QDialog.Accepted: position = dlg.mColumn.currentText() after = dlg.mAfter.isChecked() insertion_point = self.model.headers.index(position) + (1 if after else 0) if dlg.mColor.isChecked(): if not self.model.insert_color(insertion_point): QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Colors"), QCoreApplication.translate( 'RAT', "An error occourred while adding colors to the RAT!" )) else: # Try to update colors from current raster self.model.rat.update_colors_from_raster(self.raster_layer) self.is_dirty = True else: data_type = dlg.mDataType.currentData() usage = dlg.mUsage.currentData() name = dlg.mName.text() field = RATField(name, usage, data_type) result, error_message = self.model.insert_column( insertion_point, field) if not result: QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Column"), QCoreApplication.translate( 'RAT', "An error occourred while adding a column to the RAT: %s" % error_message)) else: self.is_dirty = True def columnIsColor(self, column_name) -> bool: return (column_name == RAT_COLOR_HEADER_NAME ) or self.model.rat.fields[column_name].is_color def selectedColumnCanBeRemoved(self) -> bool: selected_column = self.mRATView.selectionModel().currentIndex().column( ) column_can_be_removed = False try: column_name = self.model.headers[selected_column] usage = self.model.rat.fields[column_name].usage if self.columnIsColor(column_name) or usage in {gdal.GFU_Generic}: column_can_be_removed = True elif usage == gdal.GFU_Name: # Check if it's not the only one names_count = len([ field for field in self.model.rat.fields.values() if field.usage == gdal.GFU_Name ]) if names_count > 1: column_can_be_removed = True except Exception as ex: rat_log('Could not get selected column type: %s' % ex) return column_can_be_removed def removeColumn(self): if not self.selectedColumnCanBeRemoved(): rat_log('Selected column cannot be removed!') else: selected_column = self.mRATView.selectionModel().currentIndex( ).column() column_name = self.model.headers[selected_column] # Handle color columns if self.columnIsColor(column_name): if QMessageBox.question( None, QCoreApplication.translate('RAT', "Remove All Colors"), QCoreApplication.translate( 'RAT', "<p>All color information (Red, Green, Blue and Alpha) will be removed from the RAT.</p><p>Do you want continue?</p>" )) == QMessageBox.Yes: # Remove all colors if not self.model.remove_color(): QMessageBox.warning( None, QCoreApplication.translate( 'RAT', "Error Removing Colors"), QCoreApplication.translate( 'RAT', "An error occourred while removing colors from the RAT!" )) else: self.is_dirty = True # Normal columns elif QMessageBox.question( None, QCoreApplication.translate('RAT', "Remove Column"), QCoreApplication.translate( 'RAT', "Column <b>%s</b> will be removed from the RAT. Do you want continue?" % column_name)) == QMessageBox.Yes: result, error_message = self.model.remove_column( selected_column) if not result: QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Removing Column"), QCoreApplication.translate( 'RAT', "An error occourred while removing colum <b>%s</b> from the RAT: %s!" % (column_name, error_message))) else: self.is_dirty = True def updateButtons(self): enable_editing_buttons = self.mRATView.selectionModel().currentIndex( ).isValid() and self.editable self.mAddColumnToolButton.setEnabled(enable_editing_buttons and self.canAddAnyColumn()) self.mRemoveColumnToolButton.setEnabled(enable_editing_buttons) self.mAddRowToolButton.setEnabled(enable_editing_buttons) self.mRemoveRowToolButton.setEnabled(enable_editing_buttons) self.mSaveChangesToolButton.setEnabled(self.is_dirty) def setEditable(self, editable): if not editable and self.is_dirty: if QMessageBox.question( None, QCoreApplication.translate('RAT', "Save RAT changes"), QCoreApplication.translate( 'RAT', "RAT has been modified, Do you want to save the changes?" )) == QMessageBox.Yes: self.saveChanges() else: self.loadRat(self.mRasterBandsComboBox.currentIndex()) self.editable = editable self.model.setEditable(editable) self.updateButtons() def saveChanges(self): """Store changes back into the RAT""" rat = self.model.rat band = self.mRasterBandsComboBox.currentIndex() + 1 if rat.save(band): self.is_dirty = False def accept(self): QgsSettings().setValue("RasterAttributeTable/geometry", self.saveGeometry(), QgsSettings.Plugins) super().accept() def reject(self): if not self.is_dirty or QMessageBox.question( None, QCoreApplication.translate('RAT', "Save RAT changes"), QCoreApplication.translate( 'RAT', "<p>RAT has been modified, if you do not save the changes now or export the RAT they will be lost.</p><p>Exit without saving?<p>" )) == QMessageBox.Yes: self.accept() def classify(self): """Create classification on the selected criteria""" if QMessageBox.question( None, QCoreApplication.translate('RAT', "Overwrite classification"), QCoreApplication.translate( 'RAT', "The existing classification will be overwritten, do you want to continue?" )) == QMessageBox.Yes: band = self.mRasterBandsComboBox.currentIndex() + 1 criteria = self.mClassifyComboBox.currentText() # TODO: ramp & feedback unique_class_row_indexes = rat_classify(self.raster_layer, band, self.rat, criteria) unique_class_row_indexes.insert(0, 0) if self.iface is not None: deduplicate_legend_entries(self.iface, self.raster_layer, criteria, unique_class_row_indexes, expand=True) # Adopt the layer self.raster_layer.setCustomProperty( RAT_CUSTOM_PROPERTY_CLASSIFICATION_CRITERIA, criteria) def updateClassify(self): current_index = self.mClassifyComboBox.currentIndex() self.mClassifyComboBox.clear() self.mClassifyComboBox.addItems([ field.name for field in self.rat.fields.values() if field.usage in {gdal.GFU_Name, gdal.GFU_Generic} ]) if current_index > 0: self.mClassifyComboBox.setCurrentIndex(current_index) else: headers = self.rat.keys criteria = self.raster_layer.customProperty( RAT_CUSTOM_PROPERTY_CLASSIFICATION_CRITERIA) if criteria in headers: self.mClassifyComboBox.setCurrentIndex( self.mClassifyComboBox.findText(criteria)) def dirty(self, *args): self.is_dirty = True rat_log('Model is dirty') self.updateButtons() def loadRat(self, band_0_based) -> bool: """Load RAT for raster band 0-based""" if type(band_0_based) != int: rat_log( QCoreApplication.translate( 'RAT', 'Invalid band number for the selected raster.'), Qgis.Critical) return False self.mClassifyComboBox.clear() self.rat = get_rat(self.raster_layer, band_0_based + 1) if self.rat.keys: self.model = RATModel(self.rat) if os.environ.get('CI'): self.tester = QAbstractItemModelTester(self.model) self.model.dataChanged.connect(self.dirty) self.model.rowsInserted.connect(self.dirty) self.model.rowsRemoved.connect(self.dirty) self.model.columnsInserted.connect(self.dirty) self.model.columnsRemoved.connect(self.dirty) self.model.columnsInserted.connect(self.updateClassify) self.model.columnsRemoved.connect(self.updateClassify) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.mRATView.setModel(self.proxyModel) self.mRATView.selectionModel().selectionChanged.connect( self.updateButtons) # Color picker if self.rat.has_color: if gdal.GFU_Alpha in self.rat.field_usages: colorDelegate = ColorAlphaDelegate(self.mRATView) else: colorDelegate = ColorDelegate(self.mRATView) self.mRATView.setItemDelegateForColumn(0, colorDelegate) self.updateClassify() self.mRATView.sortByColumn( self.model.headers.index(self.rat.value_columns[0]), Qt.AscendingOrder) return True else: rat_log( QCoreApplication.translate( 'RAT', 'There is no Raster Attribute Table for the selected raster.' ), Qgis.Critical) return False
class ConfigurationDialog(QDialog, Ui_Configuration, SettingDialog): def __init__(self): QDialog.__init__(self) self.setupUi(self) self.settings = MySettings() SettingDialog.__init__(self, self.settings) # new declaration of ProjectFinder since changes can be cancelled self.project_finder = ProjectFinder(self) # table model self.project_search_model = ProjectSearchModel(self.project_finder) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.project_search_model) self.projectSearchTable.setModel(self.proxyModel) header = self.projectSearchTable.horizontalHeader() # header.setResizeMode(QHeaderView.ResizeToContents) header.setSectionResizeMode(QHeaderView.ResizeToContents) # open/create QuickFinder file self.createFileButton.clicked.connect(self.create_QFTS_file) self.openFileButton.clicked.connect(self.open_QFTS_file) self.read_QFTS_file() # project search self.addSearchButton.clicked.connect(self.add_project_search) self.removeSearchButton.clicked.connect(self.remove_project_search) self.editSearchButton.clicked.connect(self.edit_project_search) self.refreshButton.clicked.connect(self.refresh_project_search) self.projectSearchTable.selectionModel().selectionChanged.connect( self.enableButtons) self.enableButtons() def reject(self): if self.close_and_control(): QDialog.reject(self) def accept(self): if self.close_and_control(): QDialog.accept(self) def close_and_control(self): self.project_finder.close() for search in list(self.project_finder.searches.values()): if search.dateEvaluated is None: box = QMessageBox( QMessageBox.Warning, "Quick Finder", QCoreApplication.translate( "Configuration dialog", "Some searches are still not recorded to the file. Do you want to record them now ? " ), QMessageBox.Cancel | QMessageBox.Yes | QMessageBox.Close, self) ret = box.exec_() if ret == QMessageBox.Cancel: return False elif ret == QMessageBox.Yes: self.refresh_project_search() return False return True def read_QFTS_file(self): filepath = self.qftsfilepath.text() self.project_finder.setFile(filepath) self.projectSearchTable.setEnabled(self.project_finder.isValid) self.projectSearchButtonsWidget.setEnabled(self.project_finder.isValid) def create_QFTS_file(self): prjPath = QgsProject.instance().homePath() filepath, __ = QFileDialog.getSaveFileName( self, "Create Quickfinder index file", prjPath, "Quickfinder file (*.qfts)") if filepath: if filepath[-5:] != ".qfts": filepath += ".qfts" if path.isfile(filepath): remove(filepath) create_FTS_file(filepath) self.qftsfilepath.setText(filepath) self.read_QFTS_file() def open_QFTS_file(self): prjPath = QgsProject.instance().homePath() filepath, __ = QFileDialog.getOpenFileName( self, "Open Quickfinder index file", prjPath, "Quickfinder file (*.qfts)") if filepath: self.qftsfilepath.setText(filepath) self.read_QFTS_file() def add_project_search(self): ProjectSearchDialog(self.project_finder, self.project_search_model).exec_() def remove_project_search(self): sel = self.selected_search_ids() if len(sel) == 0: return box = QMessageBox( QMessageBox.Warning, "Quick Finder", QCoreApplication.translate( "Configuration dialog", "Are you sure to remove {0} search(es) ? ").format(len(sel)), QMessageBox.Yes | QMessageBox.Cancel, self) ret = box.exec_() if ret == QMessageBox.Cancel: return self.project_search_model.removeSearches(sel) def edit_project_search(self): sel = self.selected_search_ids() if len(sel) != 1: return if sel[0] not in self.project_search_model.searches: return search = self.project_search_model.searches[sel[0]] if search: ProjectSearchDialog(self.project_finder, self.project_search_model, search).exec_() def refresh_project_search(self): RefreshDialog(self.project_finder, self.project_search_model, self.selected_search_ids()).exec_() def selected_search_ids(self): selectedSearchId = [] for idx in self.projectSearchTable.selectionModel().selectedRows(): selectedSearchId.append(self.proxyModel.data(idx, SearchIdRole)) return selectedSearchId def remove_postgis_search(self): sel = self.selected_postgis_search_ids() if len(sel) == 0: return box = QMessageBox( QMessageBox.Warning, "Quick Finder", QCoreApplication.translate( "Configuration dialog", "Are you sure to remove {0} search(es) ? ").format(len(sel)), QMessageBox.Yes | QMessageBox.Cancel, self) ret = box.exec_() if ret == QMessageBox.Cancel: return self.postgis_search_model.removeSearches(sel) def dbConnectionList(self): connection_names = [] settings = QSettings() settings.beginGroup(u"/PostgreSQL/connections") for name in settings.childGroups(): connection_names.append(name) settings.endGroup() return connection_names def enableButtons(self): n = len(self.selected_search_ids()) self.removeSearchButton.setEnabled(n > 0) self.editSearchButton.setEnabled(n == 1) self.projectSearchButtonsWidget.setEnabled(self.project_finder.isValid) def geomapfishCrsButtonClicked(self): dlg = QgsProjectionSelectionTreeWidget(self) dlg.setMessage('Select GeoMapFish CRS') dlg.setSelectedAuthId(self.geomapfishCrs.text()) if dlg.exec_(): self.geomapfishCrs.setText(dlg.selectedAuthId())