diff --git a/get_nib.py b/get_nib.py deleted file mode 100644 index d5371389a2ff48016093971a68b4087c8ffd7d43..0000000000000000000000000000000000000000 --- a/get_nib.py +++ /dev/null @@ -1,423 +0,0 @@ -# -*- coding: utf-8 -*- -""" -/*************************************************************************** - getnib - A QGIS plugin - - Plugin "NIB-ortofoto-prosjekt" - Hent alle of-prosjekt i et utsnitt fra Norge i bilder (WMS) - Utsnittet kan være bounding boksen til - - et kartlag i Layers panel - - en fil man laster opp - - aktuelt map canvas - -Ressures brukt: - - Plugin Builder for å få riktig oppsett og nødvendige filer - - https://www.qgistutorials.com/en/docs/3/building_a_python_plugin.html - NB! husk å lage og å kopiere over compile.bat (trengs for å kompilere resources.py slik at f.eks. eget icon skal bli synlig) - Se også https://gis.stackexchange.com/questions/136861/getting-layer-by-name-in-pyqgis - - https://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/cheat_sheet.html# - - https://docs.qgis.org/3.16/en/docs/pyqgis_developer_cookbook/plugins/index.html - - https://norgeibilder.no/dok/webtjenester.pdf for å få tilgang til Metadata og se påkrevd format på input-verdier - - installert QGIS plugin "Releod plugin" for å oppdatere endringer gjort i plugin-en - - OSGeo4W-installasjon av QGIS med Qt Designer inkludert mtp. utforming av plugin - - logo hentet fra https://www.flaticon.com - Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ - ------------------- - begin : 2021-12-22 - git sha : $Format:%H$ - copyright : (C) 2021 by ban, NIBIO - email : ban@nibio.no - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -""" -#Default modules -from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication -from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtWidgets import QAction, QFileDialog - -#Import additional modules -from qgis.core import QgsProject, Qgis, QgsMessageLog, QgsRasterLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsPointXY -from qgis.gui import QgsMapCanvas -from qgis.utils import iface -from urllib import request, parse # for å lese url, gå gjennom streng -import os - -# Initialize Qt resources from file resources.py -from .resources import * -# Import the code for the dialog -from .get_nib_dialog import getnibDialog -import os.path - - -class getnib: - """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 - self.iface = iface - # initialize plugin directory - self.plugin_dir = os.path.dirname(__file__) - # initialize locale - locale = QSettings().value('locale/userLocale')[0:2] - locale_path = os.path.join( - self.plugin_dir, - 'i18n', - 'getnib_{}.qm'.format(locale)) - - if os.path.exists(locale_path): - self.translator = QTranslator() - self.translator.load(locale_path) - QCoreApplication.installTranslator(self.translator) - - # Declare instance attributes - self.actions = [] - self.menu = self.tr(u'&NIB of-prosjekt') - - # Check if plugin was started the first time in current QGIS session - # Must be set in initGui() to survive plugin reloads - self.first_start = None - - # noinspection PyMethodMayBeStatic - 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 - """ - # noinspection PyTypeChecker,PyArgumentList,PyCallByClass - return QCoreApplication.translate('getnib', message) - - - def add_action( - self, - 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 - """ - - 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: - # Adds plugin icon to Plugins toolbar - self.iface.addToolBarIcon(action) - - if add_to_menu: - self.iface.addPluginToMenu( - self.menu, - action) - - self.actions.append(action) - - return action - - def initGui(self): - """Create the menu entries and toolbar icons inside the QGIS GUI.""" - - icon_path = ':/plugins/get_nib/icon.png' - self.add_action( - icon_path, - text=self.tr(u'Hent of-prosjekt'), - callback=self.run, - parent=self.iface.mainWindow()) - - # will be set False in run() - self.first_start = True - - - def unload(self): - """Removes the plugin menu item and icon from QGIS GUI.""" - for action in self.actions: - self.iface.removePluginMenu( - self.tr(u'&NIB of-prosjekt'), - action) - self.iface.removeToolBarIcon(action) - - """ Additional methods """ - def select_input_file(self): - """ Får å velge fil som skal tjene som bounding box """ - filename, _filter = QFileDialog.getOpenFileName( - self.dlg, "Select a file ","", 'geo-files (*.shp *.geojson *.gpkg *.gml *.jpg *.tif);; All files (*.*)') - self.dlg.lineEdit.setText(filename) - - def check_bbsize(self, srid, xmin, xmax, ymin, ymax): - """ Check length and height of bounding box. Limit is set to 50 km """ - sourceCrs = QgsCoordinateReferenceSystem(srid) # Input project or layer crs - destCrs = QgsCoordinateReferenceSystem('EPSG:3035') # ETRS89-extended / LAEA Europe - transformContext = QgsProject.instance().transformContext() - xform = QgsCoordinateTransform(sourceCrs, destCrs, transformContext) - # Forward transformation: src -> dest - # Computes length and height in LAE regardless input crs - ll = xform.transform(QgsPointXY(xmin,ymin)) # Bounding box's lower left corner - ur = xform.transform(QgsPointXY(xmax,ymax)) # Bounding box's upper right corner - # Get the maximum height and length of the bounding box (limit is set to 50 km) - xdist = (ur.x()-ll.x())/1000 # Get lenght in km (xmax-xmin) - ydist = (ur.y()-ll.y())/1000 # Get height in km (ymax-ymin) - if xdist > 50 or ydist > 50: - self.iface.messageBar().pushMessage("Error", "Height or length of bounding box can't be > 50 km",level=Qgis.Critical, duration=3) - ok = False - else: - ok = True # Bounding box is small enough - return ok - """ end additional methods """ - - def run(self): - """Run method that performs all the real work""" - - # Create the dialog with elements (after translation) and keep reference - # Only create GUI ONCE in callback, so that it will only load when the plugin is started - if self.first_start == True: - self.first_start = False - self.dlg = getnibDialog() - """ Additional code """ - #connect the select_input_file method to the clicked signal of the push button widget - self.dlg.pushButton.clicked.connect(self.select_input_file) - - # Fetch the currently loaded layers - layers = QgsProject.instance().mapLayers() - # Clear the contents of the combobox from previous runs - self.dlg.comboBox.clear() - # Populate the combobox with names of all the loaded layers - layer_list = [] - unique_lyr = [] - for layer in layers.values(): - item = layer.name() - layer_list.append(item) # Includes duplicates - unique_lyr = list(set(layer_list)) # No duplicates - self.dlg.comboBox.addItems(unique_lyr) # Populate combobox - self.dlg.comboBox.model().sort(0) # Sort layer names (includes filepath) alfabetically - # Clear the contents of the lineEdit from previous runs - self.dlg.lineEdit.clear() - # As default: Remember user input from previous run - """ If this should not be the default, remove comment # from next line """ - # self.dlg.checkBoxNib.setChecked(False) # Unchecked - # As default: Set map canvas checkbox to checked - """ If this should not be the default, comment # next line """ - self.dlg.checkBox.setChecked(True) # Checked - - # Get projects epsg-code # e.g. - crs_proj_str = iface.mapCanvas().mapSettings().destinationCrs().authid() # EPSG:25832 - crs_proj_int = int(crs_proj_str[5:]) # 25832 - """ end additional code """ - - # show the dialog - self.dlg.show() - # Run the dialog event loop - result = self.dlg.exec_() - # See if OK was pressed - if result: - # Do something useful here - delete the line containing pass and - # substitute with your code. - # pass - """" Additional code""" - selectedLayer = '' # Initialise - reset = False - if self.dlg.checkBoxNib.isChecked(): - reset = True - # If checked, use the bounding box (= extent of current map canvas) - if self.dlg.checkBox.isChecked(): - # Get the extent of current map canvas (coordinates in the project's crs) - e = iface.mapCanvas().extent() - xmin = e.xMinimum() - xmax = e.xMaximum() - ymin = e.yMinimum() - ymax = e.yMaximum() - ok = self.check_bbsize(crs_proj_str, xmin, xmax, ymin, ymax) # Check if bb is small enough - else: # If not checked, use existing layer or open new from file - fname = self.dlg.lineEdit.text() # get the text (path and filename) - lname = os.path.splitext(os.path.basename(fname))[0] #get only the filename without the extension, will be used as layer name in the Layers panel in QGIS - # No file is chosen - if fname == "": # Use selected layer from combobox - lyr_name = self.dlg.comboBox.currentText() # Get the layer name - if lyr_name != "": # If a layer is present/chosen - selectedLayer = QgsProject.instance().mapLayersByName(lyr_name)[0] # Get this vector layer - # Get the extent of the file (coordinates in the selected layer's crs) - e = selectedLayer.extent() - else: # If no file or layer is selcted, throw an error mesage and end plugin - self.iface.messageBar().pushMessage("Error", "Nothing selected!", level=Qgis.Critical, duration=3) - return # Return from (end) plugin - else: # Use selected file - file = r""+fname+"" # Reads the file - fLayer = QgsVectorLayer(file, lname, "ogr") # If vector fie - if not fLayer.isValid(): # If not valid vector laayer - fLayer = QgsRasterLayer(file, lname, "gdal") # If raster file - if not fLayer.isValid(): # If not valid raster layer either, show error message and end plugin - self.iface.messageBar().pushMessage("Error", "Not a valid geo-file: "+ str(lname),level=Qgis.Critical, duration=3) - return # Return from (end) plugin - QgsProject.instance().addMapLayer(fLayer, True) # Add the layer and show it (True) - selectedLayer = QgsProject.instance().mapLayersByName(lname)[0] # Get this layer - # Get the extent of the active layer (coordinates in the selected file's crs) - e = selectedLayer.extent() - - # Activate (select) the selected layer in the combobox or in the lineEdit - iface.setActiveLayer(selectedLayer) - # Zoom to activated layer - iface.zoomToActiveLayer() - - # Get selected layer's epsg:code - crs_lyr = selectedLayer.crs() # example: - crs_lyr_str = crs_lyr.authid() # EPSG:4258 - try: - crs_lyr_int = int(crs_lyr_str[5:]) # 4258 - except: # In case a non-geo-layer (e.g. csv-file) is selected from the combobox - self.iface.messageBar().pushMessage("Error", "Layer is missing crs", level=Qgis.Critical, duration=3) - layers = [] # Empty layers to get a fresh start when rerunning the plugin - return # Return from (end) plugin - - # Get the extent of the active layer (coordinates in file or layer crs) - xmin = e.xMinimum() - xmax = e.xMaximum() - ymin = e.yMinimum() - ymax = e.yMaximum() - # If selected file or layer and project have different crs, the layer's - # bounding box coordinates must be transformed into the project's crs - if crs_lyr_int != crs_proj_int: - sourceCrs = QgsCoordinateReferenceSystem(crs_lyr_str) - destCrs = QgsCoordinateReferenceSystem(crs_proj_str) - transformContext = QgsProject.instance().transformContext() - xform = QgsCoordinateTransform(sourceCrs, destCrs, transformContext) - # forward transformation: src -> dest - pt1 = xform.transform(QgsPointXY(xmin,ymin)) - pt2 = xform.transform(QgsPointXY(xmax,ymax)) - xmin = pt1.x() - xmax = pt2.x() - ymin = pt1.y() - ymax = pt2.y() - # Check if bb is small enough - input crs and transformed bb coordinates are in project crs - ok = self.check_bbsize(crs_proj_str, xmin, xmax, ymin, ymax) - else: # Selected file or layer and project have the same crs - # Check if bb is small enough - input crs and bb coordinates are in layer crs = project crs - ok = self.check_bbsize(crs_lyr_str, xmin, xmax, ymin, ymax) - - if ok: # If bb small enough, get the corner coordinates (to be used in url-request) - # Set bounding box corner coordinates as geojson (x1,y1;x2,y2;...) - coords = "%f,%f;%f,%f;%f,%f;%f,%f" %(xmin,ymin,xmin,ymax,xmax,ymax,xmax,ymin) - else: # If bb too large - return # Return from (end) plugin - - # Accessing layers' tree root - root = QgsProject.instance().layerTreeRoot() - - # If reset box is checked, group "Nib-prosjekt" is deleted before recreated - # and filled with layers - if reset: # The reset box is checked - group = root.findGroup('Nib-prosjekt') # Find the group - root.removeChildNode(group) # Remove the group - # Recreate Nib-prosjekt group - # Add a layer group to be used for all orthophoto-projects within the active layer's extent - if not root.findGroup("Nib-prosjekt"): # If the group don't exist - group = root.addGroup('Nib-prosjekt') # add the group and name it "Nib-prosjekt" - # group.setExpanded(False) # Collapse the layer group - - # Load Norge i bilder-project based on active layer's bounding box - # (geojson-format x1,y1;x2,y2;x3,y3;...) - # https://stackoverflow.com/questions/50337388/how-to-use-special-character-%c3%a6-%c3%b8-or-%c3%a5-in-a-urllib-request-urlopen-in-python-3 - para=parse.quote('{Filter:"ortofototype in (1,2,3,4,8,9,12)",Coordinates:"'+coords+'",InputWKID:'+str(crs_proj_int)+',StopOnCover:false}') - inn = request.urlopen('https://tjenester.norgeibilder.no/rest/projectMetadata.ashx?request='+para).read() #list with of-projects - ut = inn.decode() # Convert from bytes to string - ut = ut.replace('"','') # Remove quotes, i.e. replace " with nothing - a = ut.split("[") # Split string in two at bracket [ - b = a[1].split("]") # Split 2nd. substring at each bracket [ gives 2 elements since there is only one occurence of [ (counting starts at 0) - nib_liste = b[0].split(",") - lyr = '' # Initiate - for nibprosjwms in nib_liste: # Loads WMS (raster layer) for each of-project within the bounding box in question - urlWithParams = 'contextualWMSLegend=0&crs=EPSG:'+str(crs_proj_int)+'&dpiMode=7&featureCount=10&format=image/png&layers='+nibprosjwms+'&styles&url=https://wms.geonorge.no/skwms1/wms.nib-prosjekter' - rlayer = QgsRasterLayer(urlWithParams, nibprosjwms, 'wms') # Get the raster layer - if rlayer.isValid(): # Valid raster layer - layers = QgsProject.instance().mapLayersByName(nibprosjwms) # Check if loaded layer already exists - if layers: - lyr = layers[0] - tree_layer = root.findLayer(lyr.id()) - if tree_layer: # True if layer exists, otherwise False - layer_parent = tree_layer.parent() - if str(layer_parent.name()) == 'Nib-prosjekt': # If layer exists in group Nib-prosjekt, it will not be added - self.iface.messageBar().pushMessage("Info", "WMS layer exists: "+ str(nibprosjwms),level=Qgis.Info, duration=1) - continue # Return the control to the beginning of the for loop - else: # The layer exists but not in the Nib-prsjekt group - QgsProject.instance().addMapLayer(rlayer, False) # Add the raster layer without showing it (False) - mygroup = root.findGroup("Nib-prosjekt") # Get the group named "Nib-prosjekt" - mygroup.addLayer(rlayer) # Add the layer to this group - # Uncheck the raster layer - QgsProject.instance().layerTreeRoot().findLayer(rlayer.id()).setItemVisibilityChecked(False) - self.iface.messageBar().pushMessage("Success", "WMS reloaded", level=Qgis.Success, duration=3) - else: # layer does not exist, thus it will be addedd - QgsProject.instance().addMapLayer(rlayer, False) # Add the raster layer without showing it (False) - mygroup = root.findGroup("Nib-prosjekt") # Get the group named "Nib-prosjekt" - mygroup.addLayer(rlayer) # Add the layer to this group - # Uncheck the raster layer - QgsProject.instance().layerTreeRoot().findLayer(rlayer.id()).setItemVisibilityChecked(False) - self.iface.messageBar().pushMessage("Success", "WMS added in Nib-prosjekt", level=Qgis.Success, duration=3) - else: # If not valid raster layer, an error message will appear - self.iface.messageBar().pushMessage("Warning", "Can't load: "+ str(nibprosjwms),level=Qgis.Warning, duration=3) diff --git a/get_nib_dialog.py b/get_nib_dialog.py deleted file mode 100644 index 36d9def7fc4501b232cb1e032d50ccd21f0b2495..0000000000000000000000000000000000000000 --- a/get_nib_dialog.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -""" -/*************************************************************************** - getnibDialog - A QGIS plugin - Plugin "NIB of-prosjekt" - Hent alle of-prosjekt i et utsnitt fra Norge i bilder (WMS) - Utsnittet kan være bounding boksen til - - et kartlag i Layers panel - - en fil man laster opp - - aktuelt map canvas - - Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ - ------------------- - begin : 2021-12-22 - git sha : $Format:%H$ - copyright : (C) 2021 by ban, NIBIO - email : ban@nibio.no - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -""" - -import os - -from qgis.PyQt import uic -from qgis.PyQt import QtWidgets - -# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer -FORM_CLASS, _ = uic.loadUiType(os.path.join( - os.path.dirname(__file__), 'get_nib_dialog_base.ui')) - - -class getnibDialog(QtWidgets.QDialog, FORM_CLASS): - def __init__(self, parent=None): - """Constructor.""" - super(getnibDialog, self).__init__(parent) - # Set up the user interface from Designer through FORM_CLASS. - # After self.setupUi() you can access any designer object by doing - # self.<objectname>, and you can use autoconnect slots - see - # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html - # #widgets-and-dialogs-with-auto-connect - self.setupUi(self) diff --git a/get_nib_dialog_base.ui b/get_nib_dialog_base.ui deleted file mode 100644 index 2675efc6ff1c4a4f086d1f77981da2c5349f0aca..0000000000000000000000000000000000000000 --- a/get_nib_dialog_base.ui +++ /dev/null @@ -1,173 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>getnibDialogBase</class> - <widget class="QDialog" name="getnibDialogBase"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>331</width> - <height>180</height> - </rect> - </property> - <property name="windowTitle"> - <string>NIB of-prosjekt</string> - </property> - <widget class="QDialogButtonBox" name="button_box"> - <property name="geometry"> - <rect> - <x>-20</x> - <y>140</y> - <width>341</width> - <height>32</height> - </rect> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - <widget class="QPushButton" name="pushButton"> - <property name="geometry"> - <rect> - <x>280</x> - <y>110</y> - <width>40</width> - <height>23</height> - </rect> - </property> - <property name="text"> - <string>...</string> - </property> - </widget> - <widget class="QComboBox" name="comboBox"> - <property name="geometry"> - <rect> - <x>88</x> - <y>70</y> - <width>231</width> - <height>22</height> - </rect> - </property> - </widget> - <widget class="QCheckBox" name="checkBox"> - <property name="geometry"> - <rect> - <x>10</x> - <y>40</y> - <width>311</width> - <height>17</height> - </rect> - </property> - <property name="font"> - <font> - <pointsize>10</pointsize> - </font> - </property> - <property name="text"> - <string>Use current map canvas</string> - </property> - </widget> - <widget class="QLineEdit" name="lineEdit"> - <property name="geometry"> - <rect> - <x>90</x> - <y>110</y> - <width>181</width> - <height>22</height> - </rect> - </property> - </widget> - <widget class="QLabel" name="label_lyr"> - <property name="geometry"> - <rect> - <x>10</x> - <y>70</y> - <width>81</width> - <height>16</height> - </rect> - </property> - <property name="font"> - <font> - <pointsize>10</pointsize> - </font> - </property> - <property name="text"> - <string>Select layer</string> - </property> - </widget> - <widget class="QLabel" name="label_file"> - <property name="geometry"> - <rect> - <x>10</x> - <y>110</y> - <width>71</width> - <height>16</height> - </rect> - </property> - <property name="font"> - <font> - <pointsize>10</pointsize> - </font> - </property> - <property name="text"> - <string>Select file</string> - </property> - </widget> - <widget class="QCheckBox" name="checkBoxNib"> - <property name="geometry"> - <rect> - <x>10</x> - <y>10</y> - <width>271</width> - <height>17</height> - </rect> - </property> - <property name="font"> - <font> - <pointsize>10</pointsize> - </font> - </property> - <property name="text"> - <string>Reset (empty) "Nib-prosjekt"-group</string> - </property> - </widget> - </widget> - <resources/> - <connections> - <connection> - <sender>button_box</sender> - <signal>accepted()</signal> - <receiver>getnibDialogBase</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>20</x> - <y>20</y> - </hint> - <hint type="destinationlabel"> - <x>20</x> - <y>20</y> - </hint> - </hints> - </connection> - <connection> - <sender>button_box</sender> - <signal>rejected()</signal> - <receiver>getnibDialogBase</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>20</x> - <y>20</y> - </hint> - <hint type="destinationlabel"> - <x>20</x> - <y>20</y> - </hint> - </hints> - </connection> - </connections> -</ui>