Exemple #1
0
class LoginSTDialog(QDialog, DIALOG_UI):
    active_role_changed = pyqtSignal()

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.session = STSession()

        self.logger = Logger()
        self.help_strings = HelpStrings()

        self.should_emit_role_changed = False

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.login)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.txt_login_user.setFocus()

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def login(self):
        if not self.txt_login_user.text().strip(
        ) or not self.txt_login_password.text().strip():
            msg = QCoreApplication.translate(
                "LoginSTDialog", "First enter user and password data.")
            self.show_message(msg, Qgis.Warning)
            return

        msg = self.logger.status(
            QCoreApplication.translate("LoginSTDialog",
                                       "Connecting to login service..."))
        with ProcessWithStatus(msg):
            res, msg, change_role = self.session.login(
                self.txt_login_user.text(), self.txt_login_password.text())

        if res:
            self.should_emit_role_changed = change_role
            self.logger.info(__name__, msg, EnumLogHandler.MESSAGE_BAR, 15)
            self.close()
        else:
            self.show_message(msg, Qgis.Warning, 0)

    def show_message(self, message, level, duration=15):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, duration)

    def show_help(self):
        show_plugin_help('transitional_system_login')

    def reject(self):
        if self.should_emit_role_changed:
            self.logger.info(__name__, "Emit active_role_changed.")
            self.active_role_changed.emit()

        self.logger.info(__name__, "Dialog closed.")
        self.done(QDialog.Accepted)  # Any code, we don't use it anyways
class LoginSTDialog(QDialog, DIALOG_UI):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.session = STSession()

        self.logger = Logger()
        self.help_strings = HelpStrings()

        #self.txt_help_page.setHtml(self.help_strings.DLG_WELCOME_SCREEN)
        #self.txt_help_page.anchorClicked.connect(self.save_template)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.login)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def login(self):
        if not self.txt_login_user.text().strip(
        ) or not self.txt_login_password.text().strip():
            msg = QCoreApplication.translate(
                "LoginSTDialog", "First enter user and password data.")
            self.show_message(msg, Qgis.Warning)
            return

        msg = self.logger.status(
            QCoreApplication.translate("LoginSTDialog",
                                       "Connecting to login service..."))
        with ProcessWithStatus(msg):
            res, msg = self.session.login(self.txt_login_user.text(),
                                          self.txt_login_password.text())

        if res:
            self.logger.info(__name__, msg, LogHandlerEnum.MESSAGE_BAR, 15)
            self.close()
        else:
            self.show_message(msg, Qgis.Warning, 0)

    def show_message(self, message, level, duration=15):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, duration)

    def show_help(self):
        self.qgis_utils.show_help("import_from_excel")
class STCancelTaskDialog(QDialog, DIALOG_TRANSITION_SYSTEM_UI):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)

        self.comments = ''

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.validate_and_accept)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def validate_and_accept(self):
        self.bar.clearWidgets()

        if not self.txt_comments.toPlainText():
            res = False
            res_msg = QCoreApplication.translate(
                "STUploadFileDialog",
                "Comments are required to cancel a task.")

            self.show_message(res_msg, Qgis.Success if res else Qgis.Warning)

            return  # Do not close the dialog

        self.comments = self.txt_comments.toPlainText()

        QDialog.accept(self)

    def show_message(self, message, level):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, 0)

    def show_help(self):
        # self.qgis_utils.show_help("settings")
        pass
class geopunt4QgisPoidialog(QDialog):
    def __init__(self, iface):
        QDialog.__init__(self, None)
        self.setWindowFlags( self.windowFlags() & ~Qt.WindowContextHelpButtonHint )
        #self.setWindowFlags( self.windowFlags() |Qt.WindowStaysOnTopHint)
        self.iface = iface

        # initialize locale
        locale = QSettings().value("locale/userLocale", "nl")
        if not locale: locale == 'nl' 
        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):
        'Set up the user interface from Designer.'
        self.ui = Ui_geopunt4QgisPoiDlg()
        self.ui.setupUi(self)	
    
        #get settings
        self.s = QSettings()
        self.loadSettings()

        #setup geopunt and geometryHelper objects
        self.gh = geometryHelper(self.iface)
        self.ph = poiHelper( self.iface)
        
        #create the graphicsLayer
        self.graphicsLayer = []
    
        #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)
            
        #table ui
        self.ui.resultLijst.hideColumn(0)
       
        # filters
        self.firstShow = True
        self.poiTypes = {}
        self.poiCategories = {}
        self.poiThemes = {}
    
        #actions
        self.ui.resultLijst.addAction( self.ui.actionZoomtoSelection )
        self.ui.actionZoomtoSelection.triggered.connect( self.onZoomSelClicked)
        self.ui.resultLijst.addAction( self.ui.actionAddTSeltoMap )
        self.ui.actionAddTSeltoMap.triggered.connect( self.onAddSelClicked)
    
        #event handlers 
        self.ui.zoekKnop.clicked.connect(self.onZoekActivated)
        self.ui.zoomSelKnop.clicked.connect(self.onZoomSelClicked)
        self.ui.resultLijst.itemDoubleClicked.connect(self.onZoomSelClicked )
        self.ui.resultLijst.itemSelectionChanged.connect(self.onSelectionChanged)
        self.ui.addToMapKnop.clicked.connect(self.onAddSelClicked)
        self.ui.addMinModelBtn.clicked.connect( self.addMinModel )
        self.ui.poiText.textChanged.connect( self.searchTxtChanged )
        self.ui.buttonBox.helpRequested.connect(self.openHelp)
        
        self.finished.connect(self.clean )
    
    def loadSettings(self):
        self.saveToFile = int( self.s.value("geopunt4qgis/poiSavetoFile" , 1))
        layerName =  self.s.value("geopunt4qgis/poilayerText", "")
        if layerName: self.layerName= layerName
        self.timeout =  int( self.s.value("geopunt4qgis/timeout" ,15))
        if settings().proxyUrl:
            self.proxy = settings().proxyUrl
        else:
            self.proxy = ""
        self.startDir = self.s.value("geopunt4qgis/startDir", os.path.expanduser("~") )
        self.poi = Poi(self.timeout, self.proxy)
    
    def show(self):
        QDialog.show(self)
        self.setWindowModality(0)
        if self.firstShow:
             inet = internet_on(proxyUrl=self.proxy, timeout=self.timeout)
             #filters
             if inet:
                self.poiThemes = dict( self.poi.listPoiThemes() )
                poiThemes = [""] + list(self.poiThemes.keys())
                poiThemes.sort()
                self.ui.filterPoiThemeCombo.addItems( poiThemes )
                self.poiCategories = dict( self.poi.listPoiCategories() )
                poiCategories = [""] + list(self.poiCategories.keys())
                poiCategories.sort()
                self.ui.filterPoiCategoryCombo.addItems( poiCategories )
                self.poiTypes = dict( self.poi.listPoitypes() )
                poiTypes = [""] + list(self.poiTypes.keys())
                poiTypes.sort()
                self.ui.filterPoiTypeCombo.addItems(  poiTypes )
                gemeentes = json.load( open(os.path.join(os.path.dirname(__file__), "data/gemeentenVL.json")) )
                self.NIScodes= { n["Naam"] : n["Niscode"] for n in gemeentes }
                gemeenteNamen = [n["Naam"] for n in gemeentes]
                gemeenteNamen.sort()
                self.ui.filterPoiNIS.addItems( gemeenteNamen )            
                #connect when inet on
                self.ui.filterPoiThemeCombo.activated.connect(self.onThemeFilterChange)
                self.ui.filterPoiCategoryCombo.activated.connect(self.onCategorieFilterChange)
                
                self.firstShow = False      
             else:
                self.bar.pushMessage(
                  QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), 
                  QCoreApplication.translate("geopunt4QgisPoidialog", "Kan geen verbing maken met het internet."), 
                  level=Qgis.Warning, duration=3)
      
    def openHelp(self):
        webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/poi")
    
    def onZoekActivated(self):
        txt = self.ui.poiText.text()
        self.ui.resultLijst.clearContents()
        self.ui.resultLijst.setRowCount(0)
        self.ui.msgLbl.setText("")
        
        ##filters:
        poithemeText = self.ui.filterPoiThemeCombo.currentText()
        if poithemeText != "": poitheme = self.poiThemes[ poithemeText ]
        else: poitheme = ""
        poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() 
        if poiCategorieText != "": poiCategorie = self.poiCategories[ poiCategorieText ]
        else: poiCategorie = ""
        poiTypeText =  self.ui.filterPoiTypeCombo.currentText() 
        if poiTypeText!= "": poiType = self.poiTypes[ poiTypeText ]
        else: poiType = ""
        NISText= self.ui.filterPoiNIS.currentText()
        if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText]
        else: Niscode = ""
        
        if self.ui.currentBoundsVink.isChecked():
            bbox = self.iface.mapCanvas().extent()
            minX, minY = self.gh.prjPtFromMapCrs([bbox.xMinimum(),bbox.yMinimum()], 4326)
            maxX, maxY = self.gh.prjPtFromMapCrs([bbox.xMaximum(),bbox.yMaximum()], 4326)
            xyBox = [minX, minY, maxX, maxY]
            self.poi.fetchPoi( txt, c=32, srs=4326 , maxModel=True, updateResults=True, bbox=xyBox, 
                               theme=poitheme , category=poiCategorie, POItype=poiType )
        else:
            self.poi.fetchPoi( txt, c=32, srs=4326 , maxModel=True, updateResults=True, bbox=None, 
                               theme=poitheme , category=poiCategorie, POItype=poiType, region=Niscode )
      
        suggesties = self.poi.poiSuggestion()
    
        if type(suggesties) is list:
          #prevent sorting every time an item is added
          self.ui.resultLijst.setSortingEnabled(False)
          row =0
          for sug in suggesties:
              id = QTableWidgetItem( sug[0], 0 )
              theme = QTableWidgetItem( sug[1], 0 )
              categorie = QTableWidgetItem( sug[2], 0 )
              PoiType = QTableWidgetItem( sug[3], 0 )
              naam = QTableWidgetItem( sug[4], 0 )
              straat = QTableWidgetItem( sug[5], 0)
              huisnr = QTableWidgetItem( sug[6], 0)
              busnr = QTableWidgetItem( sug[7], 0)
              postcode = QTableWidgetItem( sug[8], 0)
              gemeente = QTableWidgetItem( sug[9], 0)
              self.ui.resultLijst.insertRow(row)
              self.ui.resultLijst.setItem(row, 0, id)
              self.ui.resultLijst.setItem(row, 1, theme)
              self.ui.resultLijst.setItem(row, 2, categorie)
              self.ui.resultLijst.setItem(row, 3, PoiType)
              self.ui.resultLijst.setItem(row, 4, naam)
              self.ui.resultLijst.setItem(row, 5, straat)
              self.ui.resultLijst.setItem(row, 6, huisnr)
              self.ui.resultLijst.setItem(row, 7, busnr)
              self.ui.resultLijst.setItem(row, 8, postcode)
              self.ui.resultLijst.setItem(row, 9, gemeente)
              row += 1
          self.ui.resultLijst.setSortingEnabled(True)
          
          if self.poi.resultCount > 0:
            self.ui.msgLbl.setText(QCoreApplication.translate("geopunt4QgisPoidialog", 
            "Aantal getoond: %s gevonden: %s" % ( self.ui.resultLijst.rowCount() , self.poi.resultCount ) ))
          elif self.poi.resultCount == 0:
            self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", 
            "Geen resultaten gevonden voor deze zoekopdracht"), "", level=Qgis.Info, duration=10)
          elif self.poi.resultCount < 0:
            self.bar.pushMessage(QCoreApplication.translate("geopunt4QgisPoidialog", 
            "Het aantal gevonden kon niet worden bepaald, te complexe zoekopdracht"), 
            "", level=Qgis.Info, duration=10)
            self.ui.msgLbl.setText(QCoreApplication.translate("geopunt4QgisPoidialog", 
            "Aantal getoond: %s, aantal gevonden niet bepaald" % self.ui.resultLijst.rowCount() ) )

        elif type( suggesties ) is str:
          self.bar.pushMessage(
            QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"), 
            suggesties, level=Qgis.Warning)
        else:
          self.bar.pushMessage("Error",
            QCoreApplication.translate("geopunt4QgisPoidialog","onbekende fout"),
            level=Qgis.Critical)
    
    def onZoomSelClicked(self):
        if not len( self.ui.resultLijst.selectedIndexes() ):
            self.bar.pushMessage("",
               QCoreApplication.translate("geopunt4QgisPoidialog", 
               "Er zijn geen records geselecteerd"), level=Qgis.Warning )
            return
        
        selPois = self._getSelectedPois()
        if len(selPois) <= 0 :
          self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Merk op"), 
                QCoreApplication.translate("geopunt4QgisPoidialog", "Er niets om naar te zoomen"),
                level=Qgis.Info, duration=3)
        elif len(selPois) >= 2:
            pts = [n['location']['points'][0]['Point']['coordinates'] for n in selPois ] 
            bounds = self.gh.getBoundsOfPointArray(pts)
            self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326)
        elif len(selPois) == 1:
            x,  y = selPois[0]['location']['points'][0]['Point']['coordinates']
            bounds = self.gh.getBoundsOfPoint(x,y)
            self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326)
    
    def onSelectionChanged(self):
        selPois = self._getSelectedPois()
        canvas = self.iface.mapCanvas()
        self.clearGraphicsLayer()
    
        pts = [ self.gh.prjPtToMapCrs( n['location']['points'][0]['Point']['coordinates'], 4326)
                      for n in selPois ] 
        for pt in pts:
            x,y = pt
            m = QgsVertexMarker(canvas)
            self.graphicsLayer.append(m)
            m.setCenter(QgsPointXY(x,y))
            m.setColor(QColor(255,255,0))
            m.setIconSize(1)
            m.setIconType(QgsVertexMarker.ICON_BOX) 
            m.setPenWidth(10)

    def onAddSelClicked(self):
        if not len( self.ui.resultLijst.selectedIndexes() ):
            self.bar.pushMessage("",
               QCoreApplication.translate("geopunt4QgisPoidialog", "Er zijn geen records geselecteerd"), level=Qgis.Warning )
            return
        
        if not self.layernameValid(): return        
        self.clearGraphicsLayer()
        pts = self._getSelectedPois()
        self.ph.save_pois_points( pts ,  layername=self.layerName, 
                                    startFolder= os.path.join(self.startDir, self.layerName),
                  saveToFile=self.saveToFile, sender=self )

    def onThemeFilterChange(self): 
        poithemeText = self.ui.filterPoiThemeCombo.currentText()
        
        if poithemeText != "": 
           poithemeID = self.poiThemes[ poithemeText ]
           poiCategories = [""] + [n[0] for n in self.poi.listPoiCategories(poithemeID)]
           poiCategories.sort()
           self.ui.filterPoiCategoryCombo.clear()
           self.ui.filterPoiTypeCombo.clear()
           self.ui.filterPoiCategoryCombo.addItems( poiCategories )
        else:
          self.ui.filterPoiCategoryCombo.addItems([""] + list(self.poiCategories.keys()))
          self.ui.filterPoiTypeCombo.addItems([""] + list(self.poiTypes.keys()))

    def onCategorieFilterChange(self):
        poithemeText = self.ui.filterPoiThemeCombo.currentText()
        poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() 
        
        if poiCategorieText != "" and poithemeText != "": 
           poiCategorieID = self.poiCategories[ poiCategorieText ]
           poithemeID = self.poiThemes[ poithemeText ]
           poiTypes = [""] + [n[0] for n in self.poi.listPoitypes(poithemeID, poiCategorieID)]
           poiTypes.sort()          
           self.ui.filterPoiTypeCombo.clear()
           self.ui.filterPoiTypeCombo.addItems( poiTypes )

    def addMinModel(self):
        if not self.layernameValid(): return
        self.clearGraphicsLayer()
        txt = self.ui.poiText.text()

        poithemeText = self.ui.filterPoiThemeCombo.currentText()
        if poithemeText != "": poitheme = self.poiThemes[ poithemeText ]
        else: poitheme = ""
        poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() 
        if poiCategorieText != "": poiCategorie = self.poiCategories[ poiCategorieText ]
        else: poiCategorie = ""
        poiTypeText =  self.ui.filterPoiTypeCombo.currentText() 
        if poiTypeText!= "": poiType = self.poiTypes[ poiTypeText ]
        else: poiType = ""
        NISText= self.ui.filterPoiNIS.currentText()
        if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText]
        else: Niscode = ""
        
        cluster = self.ui.clusterCheck.isChecked()
      
        if self.ui.currentBoundsVink.isChecked():
            bbox = self.iface.mapCanvas().extent()
            minX, minY = self.gh.prjPtFromMapCrs([bbox.xMinimum(),bbox.yMinimum()], 4326)
            maxX, maxY = self.gh.prjPtFromMapCrs([bbox.xMaximum(),bbox.yMaximum()], 4326)
            xyBox = [minX, minY, maxX, maxY]
            pts= self.poi.fetchPoi( txt, c=1024, srs=4326 , maxModel=0, updateResults=0,
                                   bbox= xyBox, theme= poitheme , category= poiCategorie,
                                   POItype= poiType, clustering= cluster )
        else:
            pts= self.poi.fetchPoi( txt, c=1024, srs=4326 , maxModel=0, updateResults=0,
                                   bbox=None,  theme= poitheme , category= poiCategorie,
                                   POItype= poiType, region= Niscode, clustering= cluster )

        if type( pts ) == str:
            self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"),
                                  pts, level=Qgis.Warning, duration=5)
        elif type( pts ) == list or type( pts )  == dict:            
            self.ph.save_minPois_points(pts, layername=self.layerName, startFolder= os.path.join(self.startDir, self.layerName), saveToFile=self.saveToFile, sender=self )
            self.close()

    def searchTxtChanged(self):
        txt= self.ui.poiText.text()
        if txt != "":
           msg = QCoreApplication.translate("geopunt4QgisPoidialog",
                                                   "Voeg meer punten toe")
        else:
           msg = QCoreApplication.translate("geopunt4QgisPoidialog",
                                                   "Voeg alle punten toe" )
        self.ui.addMinModelBtn.setText(msg)   

    def _getSelectedPois(self):
        pois =  self.poi.PoiResult
        selPois = []
        selRows = set( [sel.row() for sel in self.ui.resultLijst.selectedIndexes()] )
        for row in  selRows:
            itemID = self.ui.resultLijst.item(row,0).text()
            selPois += [i for i in pois if i["id"] == itemID]
        return selPois
      
    def clearGraphicsLayer(self):
      for graphic in  self.graphicsLayer: 
          self.iface.mapCanvas().scene().removeItem(graphic)
      self.graphicsLayer = []

    def layernameValid(self):   
        if not hasattr(self, 'layerName'):
          layerName, accept = QInputDialog.getText(None,
              QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'),
              QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') )
          if accept == False: 
             return False
          else: 
             self.layerName = layerName
        return True
      
    def clean(self):
        self.bar.clearWidgets()
        self.ui.poiText.setText("")
        self.ui.msgLbl.setText("")
        self.ui.resultLijst.clearContents()
        self.ui.resultLijst.setRowCount(0)
        self.clearGraphicsLayer()
class ResourceSharingDialog(QDialog, FORM_CLASS):
    TAB_ALL = 0
    TAB_INSTALLED = 1
    TAB_SETTINGS = 2

    def __init__(self, parent=None, iface=None):
        """Constructor.

        :param parent: Optional widget to use as parent
        :type parent: QWidget

        :param iface: An instance of QGisInterface
        :type iface: QGisInterface
        """
        super(ResourceSharingDialog, self).__init__(parent)
        self.setupUi(self)
        self.iface = iface

        # Reconfigure UI
        self.setModal(True)
        self.button_edit.setEnabled(False)
        self.button_delete.setEnabled(False)
        self.button_install.setEnabled(False)
        self.button_open.setEnabled(False)
        self.button_uninstall.setEnabled(False)

        # Set up the "main menu" - QListWidgetItem
        # All collections
        icon_all = QIcon()
        icon_all.addFile(str(resources_path('img', 'plugin.svg')), QSize(),
                         QIcon.Normal, QIcon.Off)
        item_all = QListWidgetItem()
        item_all.setIcon(icon_all)
        item_all.setText(self.tr('All collections'))
        # Installed collections
        icon_installed = QIcon()
        icon_installed.addFile(
            str(resources_path('img', 'plugin-installed.svg')), QSize(),
            QIcon.Normal, QIcon.Off)
        item_installed = QListWidgetItem()
        item_installed.setIcon(icon_installed)
        item_installed.setText(self.tr('Installed collections'))
        item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        # Settings / repositories
        icon_settings = QIcon()
        icon_settings.addFile(str(resources_path('img', 'settings.svg')),
                              QSize(), QIcon.Normal, QIcon.Off)
        item_settings = QListWidgetItem()
        item_settings.setIcon(icon_settings)
        item_settings.setText(self.tr('Settings'))
        item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)

        # Add the items to the list widget
        self.menu_list_widget.addItem(item_all)
        self.menu_list_widget.addItem(item_installed)
        self.menu_list_widget.addItem(item_settings)

        # Init the message bar
        self.message_bar = QgsMessageBar(self)
        self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.vlayoutRightColumn.insertWidget(0, self.message_bar)

        # Progress dialog for long running processes
        self.progress_dialog = None

        # Init the repository manager dialog
        self.repository_manager = RepositoryManager()
        self.collection_manager = CollectionManager()
        # Collections list view
        self.collections_model = QStandardItemModel(0, 1)
        self.collections_model.sort(0, Qt.AscendingOrder)
        self.collection_proxy = CustomSortFilterProxyModel(self)
        self.collection_proxy.setSourceModel(self.collections_model)
        self.list_view_collections.setModel(self.collection_proxy)
        # Active selected collection
        self._selected_collection_id = None

        # Slots
        self.button_add.clicked.connect(self.add_repository)
        self.button_edit.clicked.connect(self.edit_repository)
        self.button_delete.clicked.connect(self.delete_repository)
        self.button_reload.clicked.connect(self.reload_repositories)
        self.menu_list_widget.currentRowChanged.connect(self.set_current_tab)
        self.list_view_collections.selectionModel().currentChanged.connect(
            self.on_list_view_collections_clicked)
        self.line_edit_filter.textChanged.connect(self.filter_collections)
        self.button_install.clicked.connect(self.install_collection)
        self.button_open.clicked.connect(self.open_collection)
        self.button_uninstall.clicked.connect(self.uninstall_collection)
        self.button_box.button(QDialogButtonBox.Help).clicked.connect(
            self.open_help)

        # Populate the repositories widget and collections list view
        self.populate_repositories_widget()
        self.reload_collections_model()

    def set_current_tab(self, index):
        """Set stacked widget based on the active tab.

        :param index: The index of the active widget (in the list widget).
        :type index: int
        """
        # Clear message bar
        self.message_bar.clearWidgets()
        if index == (self.menu_list_widget.count() - 1):
            # Last menu entry - Settings
            self.stacked_menu_widget.setCurrentIndex(1)
        else:
            # Not settings, must be Collections (all or installed)
            if index == 1:
                # Installed collections
                self.collection_proxy.accepted_status = \
                    COLLECTION_INSTALLED_STATUS
                # Set the web view
                title = self.tr('Installed Collections')
                description = self.tr(
                    'On the left you see the list of all the '
                    'installed collections.')
            else:
                # All collections (0)
                self.collection_proxy.accepted_status = COLLECTION_ALL_STATUS
                # Set the web view
                title = self.tr('All Collections')
                description = self.tr(
                    'On the left you see a list of all the collections '
                    'that are available from the registered repositories.<br> '
                    'Installed collections are emphasized (in <b>bold</b>).')

            context = {
                'resources_path': str(resources_path()),
                'title': title,
                'description': description
            }
            self.web_view_details.setHtml(
                render_template('tab_description.html', context))
            self.stacked_menu_widget.setCurrentIndex(0)

    def add_repository(self):
        """Open add repository dialog."""
        dlg = ManageRepositoryDialog(self)
        if not dlg.exec_():
            return

        for repoName, repo in self.repository_manager.directories.items():
            if dlg.line_edit_url.text().strip() == repo['url']:
                self.message_bar.pushMessage(
                    self.tr(
                        'Unable to add another repository with the same URL!'),
                    Qgis.Warning, 5)
                return
            if dlg.line_edit_name.text().strip() == repoName:
                self.message_bar.pushMessage(
                    self.tr('Repositories must have unique names!'),
                    Qgis.Warning, 5)
                return

        repo_name = dlg.line_edit_name.text()
        repo_url = dlg.line_edit_url.text().strip()
        repo_auth_cfg = dlg.line_edit_auth_id.text().strip()
        if repo_name in self.repository_manager.directories:
            repo_name += '(2)'

        # Show progress dialog
        self.show_progress_dialog("Fetching repository's metadata")

        # Add repository
        try:
            status, adderror = self.repository_manager.add_directory(
                repo_name, repo_url, repo_auth_cfg)
            if status:
                self.message_bar.pushMessage(
                    self.tr('Repository was successfully added'), Qgis.Success,
                    5)
            else:
                self.message_bar.pushMessage(
                    self.tr('Unable to add repository: %s') % adderror,
                    Qgis.Warning, 5)
        except Exception as e:
            self.message_bar.pushMessage(self.tr('%s') % e, Qgis.Warning, 5)
        finally:
            self.progress_dialog.hide()

        # Reload data and widget
        self.reload_data_and_widget()

        # Deactivate edit and delete button
        self.button_edit.setEnabled(False)
        self.button_delete.setEnabled(False)

    def edit_repository(self):
        """Open edit repository dialog."""
        selected_item = self.tree_repositories.currentItem()
        if selected_item:
            repo_name = selected_item.text(0)

        if not repo_name:
            return

        # Check if it is among the officially approved QGIS repositories
        settings = QgsSettings()
        settings.beginGroup(repo_settings_group())
        if settings.value(repo_name + '/url') in \
                self.repository_manager._online_directories.values():
            self.message_bar.pushMessage(
                self.tr('You can not edit the official repositories!'),
                Qgis.Warning, 5)
            return

        dlg = ManageRepositoryDialog(self)
        dlg.line_edit_name.setText(repo_name)
        dlg.line_edit_url.setText(
            self.repository_manager.directories[repo_name]['url'])
        dlg.line_edit_auth_id.setText(
            self.repository_manager.directories[repo_name]['auth_cfg'])

        if not dlg.exec_():
            return

        # Check if the changed URL is already present and that
        # the new repository name is unique
        new_url = dlg.line_edit_url.text().strip()
        old_url = self.repository_manager.directories[repo_name]['url']
        new_name = dlg.line_edit_name.text().strip()
        for repoName, repo in self.repository_manager.directories.items():
            if new_url == repo['url'] and (old_url != new_url):
                self.message_bar.pushMessage(
                    self.tr('Unable to add another repository with the same '
                            'URL!'), Qgis.Warning, 5)
                return
            if new_name == repoName and (repo_name != new_name):
                self.message_bar.pushMessage(
                    self.tr('Repositories must have unique names!'),
                    Qgis.Warning, 5)
                return

        # Redundant
        if (new_name in self.repository_manager.directories) and (new_name !=
                                                                  repo_name):
            new_name += '(2)'

        new_auth_cfg = dlg.line_edit_auth_id.text()

        # Show progress dialog
        self.show_progress_dialog("Fetching repository's metadata")

        # Edit repository
        try:
            status, editerror = self.repository_manager.edit_directory(
                repo_name, new_name, old_url, new_url, new_auth_cfg)
            if status:
                self.message_bar.pushMessage(
                    self.tr('Repository is successfully updated'),
                    Qgis.Success, 5)
            else:
                self.message_bar.pushMessage(
                    self.tr('Unable to edit repository: %s') % editerror,
                    Qgis.Warning, 5)
        except Exception as e:
            self.message_bar.pushMessage(self.tr('%s') % e, Qgis.Warning, 5)
        finally:
            self.progress_dialog.hide()

        # Reload data and widget
        self.reload_data_and_widget()

        # Deactivate the edit and delete buttons
        self.button_edit.setEnabled(False)
        self.button_delete.setEnabled(False)

    def delete_repository(self):
        """Delete a repository in the tree widget."""
        selected_item = self.tree_repositories.currentItem()
        if selected_item:
            repo_name = selected_item.text(0)

        if not repo_name:
            return
        # Check if it is among the offical repositories
        repo_url = self.repository_manager.directories[repo_name]['url']
        if repo_url in self.repository_manager._online_directories.values():
            self.message_bar.pushMessage(
                self.tr('You can not remove official repositories!'),
                Qgis.Warning, 5)
            return

        warning = self.tr('Are you sure you want to remove the following '
                          'repository?') + '\n' + repo_name
        if QMessageBox.warning(self, self.tr('QGIS Resource Sharing'), warning,
                               QMessageBox.Yes,
                               QMessageBox.No) == QMessageBox.No:
            return

        # Remove repository
        installed_collections = \
            self.collection_manager.get_installed_collections(repo_url)
        if installed_collections:
            message = ('You have installed collections from this '
                       'repository. Please uninstall them first!')
            self.message_bar.pushMessage(message, Qgis.Warning, 5)
        else:
            self.repository_manager.remove_directory(repo_name)
            # Reload data and widget
            self.reload_data_and_widget()
            # Deactivate the edit and delete buttons
            self.button_edit.setEnabled(False)
            self.button_delete.setEnabled(False)

    def reload_repositories(self):
        """Slot for when user clicks reload repositories button."""
        # Show progress dialog
        self.show_progress_dialog('Reloading all repositories')

        for repo_name in self.repository_manager.directories:
            directory = self.repository_manager.directories[repo_name]
            url = directory['url']
            auth_cfg = directory['auth_cfg']
            try:
                status, reloaderror = self.repository_manager.reload_directory(
                    repo_name, url, auth_cfg)
                if status:
                    self.message_bar.pushMessage(
                        self.tr('Repository %s is successfully reloaded') %
                        repo_name, Qgis.Info, 5)
                else:
                    self.message_bar.pushMessage(
                        self.tr('Unable to reload %s: %s') %
                        (repo_name, reloaderror), Qgis.Warning, 5)
            except Exception as e:
                self.message_bar.pushMessage(
                    self.tr('%s') % e, Qgis.Warning, 5)

        self.progress_dialog.hide()
        # Reload data and widget
        self.reload_data_and_widget()

    def install_collection(self):
        """Slot for when the user clicks the install/reinstall button."""
        # Save the current index to enable selection after installation
        self.current_index = self.list_view_collections.currentIndex()
        self.show_progress_dialog('Starting installation...')
        self.progress_dialog.canceled.connect(self.install_canceled)

        self.installer_thread = QThread()
        self.installer_worker = CollectionInstaller(
            self.collection_manager, self._selected_collection_id)
        self.installer_worker.moveToThread(self.installer_thread)
        self.installer_worker.finished.connect(self.install_finished)
        self.installer_worker.aborted.connect(self.install_aborted)
        self.installer_worker.progress.connect(self.install_progress)
        self.installer_thread.started.connect(self.installer_worker.run)
        self.installer_thread.start()

    def install_finished(self):
        # Process the result
        self.progress_dialog.hide()
        installStatus = self.installer_worker.install_status
        if not installStatus:
            message = self.installer_worker.error_message
        # Clean up the worker and thread
        self.installer_worker.deleteLater()
        self.installer_thread.quit()
        self.installer_thread.wait()
        self.installer_thread.deleteLater()

        if installStatus:
            self.reload_collections_model()
            # Report what has been installed
            message = '<b>%s</b> was successfully installed, containing:\n<ul>' % (
                config.COLLECTIONS[self._selected_collection_id]['name'])
            number = 0
            if 'style' in config.COLLECTIONS[
                    self._selected_collection_id].keys():
                number = config.COLLECTIONS[
                    self._selected_collection_id]['style']
                message = message + '\n<li> ' + str(
                    number) + ' Layer style (QML) file'
                if number > 1:
                    message = message + 's'
            if 'symbol' in config.COLLECTIONS[
                    self._selected_collection_id].keys():
                number = config.COLLECTIONS[
                    self._selected_collection_id]['symbol']
                message = message + '\n<li> ' + str(
                    number) + ' XML symbol file'
                if number > 1:
                    message = message + 's'
            if 'svg' in config.COLLECTIONS[
                    self._selected_collection_id].keys():
                number = config.COLLECTIONS[
                    self._selected_collection_id]['svg']
                message = message + '\n<li> ' + str(number) + ' SVG file'
                if number > 1:
                    message = message + 's'
            if 'models' in config.COLLECTIONS[
                    self._selected_collection_id].keys():
                number = config.COLLECTIONS[
                    self._selected_collection_id]['models']
                message = message + '\n<li> ' + str(number) + ' model'
                if number > 1:
                    message = message + 's'
            if 'expressions' in config.COLLECTIONS[
                    self._selected_collection_id].keys():
                number = config.COLLECTIONS[
                    self._selected_collection_id]['expressions']
                message = message + '\n<li> ' + str(
                    number) + ' expression file'
                if number > 1:
                    message = message + 's'
            if 'processing' in config.COLLECTIONS[
                    self._selected_collection_id].keys():
                number = config.COLLECTIONS[
                    self._selected_collection_id]['processing']
                message = message + '\n<li> ' + str(
                    number) + ' processing script'
                if number > 1:
                    message = message + 's'
            if 'rscripts' in config.COLLECTIONS[
                    self._selected_collection_id].keys():
                number = config.COLLECTIONS[
                    self._selected_collection_id]['rscripts']
                message = message + '\n<li> ' + str(number) + ' R script'
                if number > 1:
                    message = message + 's'
            message = message + '\n</ul>'
        QMessageBox.information(self, 'Resource Sharing', message)
        self.populate_repositories_widget()
        # Set the selection
        oldRow = self.current_index.row()
        newIndex = self.collections_model.createIndex(oldRow, 0)
        selection_model = self.list_view_collections.selectionModel()
        selection_model.setCurrentIndex(newIndex,
                                        selection_model.ClearAndSelect)
        selection_model.select(newIndex, selection_model.ClearAndSelect)
        # Update the buttons
        self.button_install.setEnabled(True)
        self.button_install.setText('Reinstall')
        self.button_open.setEnabled(True)
        self.button_uninstall.setEnabled(True)

        self.show_collection_metadata(self._selected_collection_id)

    def install_canceled(self):
        self.progress_dialog.hide()
        self.show_progress_dialog('Cancelling installation...')
        self.installer_worker.abort()

    def install_aborted(self):
        if self.installer_thread.isRunning():
            self.installer_thread.quit()
        self.installer_thread.finished.connect(self.progress_dialog.hide)

    def install_progress(self, text):
        self.progress_dialog.setLabelText(text)

    def uninstall_collection(self):
        """Slot called when user clicks the uninstall button."""
        # get the QModelIndex for the item to be uninstalled
        uninstall_index = self.list_view_collections.currentIndex()
        coll_id = self._selected_collection_id
        try:
            self.collection_manager.uninstall(coll_id)
        except Exception as e:
            LOGGER.error('Could not uninstall collection ' +
                         config.COLLECTIONS[coll_id]['name'] + ':\n' + str(e))
        else:
            QMessageBox.information(
                self, 'Resource Sharing',
                'The collection was successfully uninstalled!')
            self.reload_collections_model()
            # Fix the GUI
            currentMenuRow = self.menu_list_widget.currentRow()
            self.set_current_tab(currentMenuRow)
            self.populate_repositories_widget()

            rowCount = self.collection_proxy.rowCount()
            if rowCount > 0:
                # Set the current (and selected) row in the listview
                newRow = uninstall_index.row()
                # Check if this was the last element
                rowCount = self.collection_proxy.rowCount()
                if newRow == rowCount:
                    newRow = newRow - 1
                # Select the new current element
                newIndex = self.collections_model.createIndex(newRow, 0)
                selection_model = self.list_view_collections.selectionModel()
                selection_model.setCurrentIndex(newIndex,
                                                selection_model.ClearAndSelect)
                # Get the id of the current collection
                proxyModel = self.list_view_collections.model()
                proxyIndex = proxyModel.index(newRow, 0)
                current_coll_id = proxyIndex.data(COLLECTION_ID_ROLE)
                self._selected_collection_id = current_coll_id
                # Update buttons
                status = config.COLLECTIONS[current_coll_id]['status']
                if status == COLLECTION_INSTALLED_STATUS:
                    self.button_install.setEnabled(True)
                    self.button_install.setText('Reinstall')
                    self.button_open.setEnabled(True)
                    self.button_uninstall.setEnabled(True)
                else:
                    self.button_install.setEnabled(True)
                    self.button_install.setText('Install')
                    self.button_open.setEnabled(False)
                    self.button_uninstall.setEnabled(False)
                # Update the web_view_details frame
                self.show_collection_metadata(current_coll_id)
            else:
                self.button_install.setEnabled(False)
                self.button_install.setText('Install')
                self.button_open.setEnabled(False)
                self.button_uninstall.setEnabled(False)

    def open_collection(self):
        """Slot for when user clicks 'Open' button."""
        collection_path = local_collection_path(self._selected_collection_id)
        directory_url = QUrl.fromLocalFile(str(collection_path))
        QDesktopServices.openUrl(directory_url)

    def reload_data_and_widget(self):
        """Reload repositories and collections and update widgets related."""
        self.reload_repositories_widget()
        self.reload_collections_model()

    def reload_repositories_widget(self):
        """Refresh tree repositories using new repositories data."""
        self.repository_manager.load_directories()
        self.populate_repositories_widget()

    def populate_repositories_widget(self):
        """Populate the current dictionary repositories to the tree widget."""
        # Clear the current tree widget
        self.tree_repositories.clear()
        installed_collections = \
            self.collection_manager.get_installed_collections()
        # Export the updated ones from the repository manager
        for repo_name in self.repository_manager.directories:
            url = self.repository_manager.directories[repo_name]['url']
            item = QTreeWidgetItem(self.tree_repositories, REPOSITORY_ITEM)
            item.setText(0, repo_name)
            item.setText(1, url)
            for coll_id in config.COLLECTIONS:
                if ('repository_name' in config.COLLECTIONS[coll_id].keys()
                        and config.COLLECTIONS[coll_id]['repository_name']
                        == repo_name):
                    coll_name = config.COLLECTIONS[coll_id]['name']
                    coll_tags = config.COLLECTIONS[coll_id]['tags']
                    collectionItem = QTreeWidgetItem(item, COLLECTION_ITEM)
                    collitemtext = coll_name
                    if installed_collections and coll_id in installed_collections.keys(
                    ):
                        collitemtext = coll_name + ' (installed)'
                        collectionFont = QFont()
                        collectionFont.setWeight(60)
                        collectionItem.setFont(0, collectionFont)
                    collectionItem.setText(0, collitemtext)
                    collectionItem.setText(1, coll_tags)
        self.tree_repositories.resizeColumnToContents(0)
        self.tree_repositories.resizeColumnToContents(1)
        self.tree_repositories.sortItems(1, Qt.AscendingOrder)

    def reload_collections_model(self):
        """Reload the collections model with the current collections."""
        self.collections_model.clear()
        installed_collections = \
            self.collection_manager.get_installed_collections()
        for id in config.COLLECTIONS:
            collection_name = config.COLLECTIONS[id]['name']
            collection_author = config.COLLECTIONS[id]['author']
            collection_tags = config.COLLECTIONS[id]['tags']
            collection_description = config.COLLECTIONS[id]['description']
            collection_status = config.COLLECTIONS[id]['status']
            repository_name = ''
            if 'repository_name' in config.COLLECTIONS[id].keys():
                repository_name = config.COLLECTIONS[id]['repository_name']
            item = QStandardItem(collection_name + ' (' + repository_name +
                                 ')')
            item.setEditable(False)
            item.setData(id, COLLECTION_ID_ROLE)
            item.setData(collection_name, COLLECTION_NAME_ROLE)
            item.setData(collection_description, COLLECTION_DESCRIPTION_ROLE)
            item.setData(collection_author, COLLECTION_AUTHOR_ROLE)
            item.setData(collection_tags, COLLECTION_TAGS_ROLE)
            item.setData(collection_status, COLLECTION_STATUS_ROLE)
            # Make installed collections stand out
            if installed_collections and id in installed_collections.keys():
                collectionFont = QFont()
                collectionFont.setWeight(60)
                item.setFont(collectionFont)
            self.collections_model.appendRow(item)
        self.collections_model.sort(0, Qt.AscendingOrder)

    def on_tree_repositories_itemSelectionChanged(self):
        """Slot for the itemSelectionChanged signal of tree_repositories."""
        selected_item = self.tree_repositories.currentItem()
        if selected_item and selected_item.type() == REPOSITORY_ITEM:
            if selected_item:
                repo_name = selected_item.text(0)
            if not repo_name:
                return
            if not repo_name in self.repository_manager.directories.keys():
                return
            repo_url = self.repository_manager.directories[repo_name]['url']
            # Disable the edit and delete buttons for "official" repositories
            if repo_url in self.repository_manager._online_directories.values(
            ):
                self.button_edit.setEnabled(False)
                self.button_delete.setEnabled(False)
            else:
                # Activate the edit and delete buttons
                self.button_edit.setEnabled(True)
                self.button_delete.setEnabled(True)
        elif selected_item and selected_item.type() == COLLECTION_ITEM:
            self.button_edit.setEnabled(False)
            self.button_delete.setEnabled(False)
        else:
            self.button_edit.setEnabled(False)
            self.button_delete.setEnabled(False)

    def on_list_view_collections_clicked(self, index):
        """Slot for when the list_view_collections is clicked."""
        real_index = self.collection_proxy.mapToSource(index)
        if real_index.row() != -1:
            collection_item = self.collections_model.itemFromIndex(real_index)
            collection_id = collection_item.data(COLLECTION_ID_ROLE)
            self._selected_collection_id = collection_id

            # Enable / disable buttons
            status = config.COLLECTIONS[self._selected_collection_id]['status']
            is_installed = status == COLLECTION_INSTALLED_STATUS
            if is_installed:
                self.button_install.setEnabled(True)
                self.button_install.setText('Reinstall')
                self.button_open.setEnabled(True)
                self.button_uninstall.setEnabled(True)
            else:
                self.button_install.setEnabled(True)
                self.button_install.setText('Install')
                self.button_open.setEnabled(False)
                self.button_uninstall.setEnabled(False)

            # Show  metadata
            self.show_collection_metadata(collection_id)

    @pyqtSlot(str)
    def filter_collections(self, text):
        search = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp)
        self.collection_proxy.setFilterRegExp(search)

    def show_collection_metadata(self, id):
        """Show the collection metadata given the ID."""
        html = self.collection_manager.get_html(id)
        self.web_view_details.setHtml(html)

    def reject(self):
        """Slot when the dialog is closed."""
        # Serialize collections to settings
        self.repository_manager.serialize_repositories()
        self.done(0)

    def open_help(self):
        """Open help."""
        doc_url = QUrl('http://qgis-contribution.github.io/' +
                       'QGIS-ResourceSharing/')
        QDesktopServices.openUrl(doc_url)

    def show_progress_dialog(self, text):
        """Show infinite progress dialog with given text.

        :param text: Text as the label of the progress dialog
        :type text: str
        """
        if self.progress_dialog is None:
            self.progress_dialog = QProgressDialog(self)
            self.progress_dialog.setWindowModality(Qt.WindowModal)
            self.progress_dialog.setAutoClose(False)
            title = self.tr('Resource Sharing')
            self.progress_dialog.setWindowTitle(title)
            # Just use an infinite progress bar here
            self.progress_dialog.setMaximum(0)
            self.progress_dialog.setMinimum(0)
            self.progress_dialog.setValue(0)
            self.progress_dialog.setLabelText(text)

        self.progress_dialog.show()
Exemple #6
0
class AlgorithmDialog(AlgorithmDialogBase):
    def __init__(self, alg):
        AlgorithmDialogBase.__init__(self, alg)

        self.alg = alg

        self.setMainWidget(self.getParametersPanel(alg, self))

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().insertWidget(0, self.bar)

        self.runAsBatchButton = QPushButton(
            QCoreApplication.translate("AlgorithmDialog",
                                       "Run as Batch Process…"))
        self.runAsBatchButton.clicked.connect(self.runAsBatch)
        self.buttonBox.addButton(
            self.runAsBatchButton,
            QDialogButtonBox.ResetRole)  # reset role to ensure left alignment

    def getParametersPanel(self, alg, parent):
        return ParametersPanel(parent, alg)

    def runAsBatch(self):
        self.close()
        dlg = BatchAlgorithmDialog(self.alg)
        dlg.show()
        dlg.exec_()

    def getParamValues(self):
        parameters = {}

        if not hasattr(self, 'mainWidget') or self.mainWidget is None:
            return parameters

        for param in self.alg.parameterDefinitions():
            if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
                continue
            if not param.isDestination():
                wrapper = self.mainWidget.wrappers[param.name()]
                value = None
                if wrapper.widget:
                    value = wrapper.value()
                    parameters[param.name()] = value

                    if not param.checkValueIsAcceptable(value):
                        raise AlgorithmDialogBase.InvalidParameterValue(
                            param, wrapper.widget)
            else:
                dest_project = None
                if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
                        isinstance(param, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)):
                    if self.mainWidget.checkBoxes[param.name()].isChecked():
                        dest_project = QgsProject.instance()

                value = self.mainWidget.outputWidgets[param.name()].getValue()
                if value and isinstance(value,
                                        QgsProcessingOutputLayerDefinition):
                    value.destinationProject = dest_project
                if value:
                    parameters[param.name()] = value

        return parameters

    def checkExtentCRS(self):
        unmatchingCRS = False
        hasExtent = False
        context = dataobjects.createContext()
        projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
        layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance())
        for param in self.alg.parameterDefinitions():
            if isinstance(
                    param,
                (ParameterRaster, ParameterVector, ParameterMultipleInput)):
                if param.value:
                    if isinstance(param, ParameterMultipleInput):
                        inputlayers = param.value.split(';')
                    else:
                        inputlayers = [param.value]
                    for inputlayer in inputlayers:
                        for layer in layers:
                            if layer.source() == inputlayer:
                                if layer.crs() != projectCRS:
                                    unmatchingCRS = True

                        p = QgsProcessingUtils.mapLayerFromString(
                            inputlayer, context)
                        if p is not None:
                            if p.crs() != projectCRS:
                                unmatchingCRS = True
            if isinstance(param, ParameterExtent):
                if param.skip_crs_check:
                    continue

                value = self.mainWidget.wrappers[
                    param.name()].widget.leText.text().strip()
                if value:
                    hasExtent = True

        return hasExtent and unmatchingCRS

    def accept(self):
        super(AlgorithmDialog, self)._saveGeometry()

        feedback = self.createFeedback()
        context = dataobjects.createContext(feedback)

        checkCRS = ProcessingConfig.getSetting(
            ProcessingConfig.WARN_UNMATCHING_CRS)
        try:
            parameters = self.getParamValues()

            if checkCRS and not self.alg.validateInputCrs(parameters, context):
                reply = QMessageBox.question(
                    self, self.tr("Unmatching CRS's"),
                    self.tr('Layers do not all use the same CRS. This can '
                            'cause unexpected results.\nDo you want to '
                            'continue?'), QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            checkExtentCRS = ProcessingConfig.getSetting(
                ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS)
            # TODO
            if False and checkExtentCRS and self.checkExtentCRS():
                reply = QMessageBox.question(
                    self, self.tr("Extent CRS"),
                    self.
                    tr('Extent parameters must use the same CRS as the input layers.\n'
                       'Your input layers do not have the same extent as the project, '
                       'so the extent might be in a wrong CRS if you have selected it from the canvas.\n'
                       'Do you want to continue?'),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            ok, msg = self.alg.checkParameterValues(parameters, context)
            if msg:
                QMessageBox.warning(self,
                                    self.tr('Unable to execute algorithm'),
                                    msg)
                return
            self.btnRun.setEnabled(False)
            self.btnClose.setEnabled(False)
            buttons = self.mainWidget.iterateButtons
            self.iterateParam = None

            for i in range(len(list(buttons.values()))):
                button = list(buttons.values())[i]
                if button.isChecked():
                    self.iterateParam = list(buttons.keys())[i]
                    break

            self.progressBar.setMaximum(0)
            self.lblProgress.setText(self.tr('Processing algorithm...'))
            # Make sure the Log tab is visible before executing the algorithm
            try:
                self.tabWidget.setCurrentIndex(1)
                self.repaint()
            except:
                pass

            self.setInfo(
                self.tr('<b>Algorithm \'{0}\' starting...</b>').format(
                    self.alg.displayName()),
                escape_html=False)

            feedback.pushInfo(self.tr('Input parameters:'))
            feedback.pushCommandInfo(pformat(parameters))
            feedback.pushInfo('')
            start_time = time.time()

            if self.iterateParam:
                self.buttonCancel.setEnabled(
                    self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
                if executeIterating(self.alg, parameters, self.iterateParam,
                                    context, feedback):
                    feedback.pushInfo(
                        self.tr(
                            'Execution completed in {0:0.2f} seconds'.format(
                                time.time() - start_time)))
                    self.buttonCancel.setEnabled(False)
                    self.finish(True, parameters, context, feedback)
                else:
                    self.buttonCancel.setEnabled(False)
                    self.resetGUI()
            else:
                command = self.alg.asPythonCommand(parameters, context)
                if command:
                    ProcessingLog.addToLog(command)
                self.buttonCancel.setEnabled(
                    self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)

                def on_complete(ok, results):
                    if ok:
                        feedback.pushInfo(
                            self.tr('Execution completed in {0:0.2f} seconds'.
                                    format(time.time() - start_time)))
                        feedback.pushInfo(self.tr('Results:'))
                        feedback.pushCommandInfo(pformat(results))
                    else:
                        feedback.reportError(
                            self.tr('Execution failed after {0:0.2f} seconds'.
                                    format(time.time() - start_time)))
                    feedback.pushInfo('')

                    self.buttonCancel.setEnabled(False)
                    self.finish(ok, results, context, feedback)

                task = QgsProcessingAlgRunnerTask(self.alg, parameters,
                                                  context, feedback)
                task.executed.connect(on_complete)
                QgsApplication.taskManager().addTask(task)

        except AlgorithmDialogBase.InvalidParameterValue as e:
            try:
                self.buttonBox.accepted.connect(
                    lambda e=e: e.widget.setPalette(QPalette()))
                palette = e.widget.palette()
                palette.setColor(QPalette.Base, QColor(255, 255, 0))
                e.widget.setPalette(palette)
            except:
                pass
            self.bar.clearWidgets()
            self.bar.pushMessage(
                "",
                self.tr("Wrong or missing parameter value: {0}").format(
                    e.parameter.description()),
                level=QgsMessageBar.WARNING,
                duration=5)

    def finish(self, successful, result, context, feedback):
        keepOpen = not successful or ProcessingConfig.getSetting(
            ProcessingConfig.KEEP_DIALOG_OPEN)

        if self.iterateParam is None:

            # add html results to results dock
            for out in self.alg.outputDefinitions():
                if isinstance(out, QgsProcessingOutputHtml) and out.name(
                ) in result and result[out.name()]:
                    resultsList.addResult(icon=self.alg.icon(),
                                          name=out.description(),
                                          result=result[out.name()])

            if not handleAlgorithmResults(self.alg, context, feedback,
                                          not keepOpen):
                self.resetGUI()
                return

        self.executed = True
        self.setInfo(self.tr('Algorithm \'{0}\' finished').format(
            self.alg.displayName()),
                     escape_html=False)

        if not keepOpen:
            self.close()
        else:
            self.resetGUI()
            if self.alg.hasHtmlOutputs():
                self.setInfo(self.tr(
                    'HTML output has been generated by this algorithm.'
                    '\nOpen the results dialog to check it.'),
                             escape_html=False)
Exemple #7
0
class geopunt4QgisAdresDialog(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", "nl")
        if not locale: locale == 'nl'
        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_geopunt4Qgis()
        self.ui.setupUi(self)

        #get settings
        self.s = QSettings()
        self.loadSettings()

        #setup geometryHelper object
        self.gh = geometryHelper(self.iface)

        #create graphicsLayer
        self.graphicsLayer = []

        self.firstShow = True

        self.completer = QCompleter(self)
        self.completerModel = QStringListModel(self)
        self.ui.gemeenteBox.setCompleter(self.completer)
        self.completer.setModel(self.completerModel)
        self.completer.setCaseSensitivity(False)

        #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)

        #event handlers
        if self.adresSearchOnEnter:
            self.ui.zoekText.returnPressed.connect(self.onZoekActivated)
        else:
            self.ui.zoekText.textEdited.connect(self.onZoekActivated)
        self.ui.gemeenteBox.currentIndexChanged.connect(self.onZoekActivated)
        self.ui.resultLijst.itemDoubleClicked.connect(self.onItemActivated)
        self.ui.resultLijst.itemClicked.connect(self.onItemClick)
        self.ui.ZoomKnop.clicked.connect(self.onZoomKnopClick)
        self.ui.Add2mapKnop.clicked.connect(self.onAdd2mapKnopClick)
        self.ui.buttonBox.helpRequested.connect(self.openHelp)
        self.finished.connect(self.clean)

    def loadSettings(self):
        self.saveToFile = int(self.s.value("geopunt4qgis/adresSavetoFile", 1))
        layerName = self.s.value("geopunt4qgis/adreslayerText", "")
        if layerName:
            self.layerName = layerName
        self.adresSearchOnEnter = int(
            self.s.value("geopunt4qgis/adresSearchOnEnter", 0))
        self.timeout = int(self.s.value("geopunt4qgis/timeout", 15))
        if settings().proxyUrl:
            self.proxy = settings().proxyUrl
        else:
            self.proxy = ""
        self.startDir = self.s.value("geopunt4qgis/startDir",
                                     os.path.expanduser("~"))
        self.gp = Adres(self.timeout, self.proxy)

    # overwrite
    def show(self):
        QDialog.show(self)
        self.setWindowModality(0)
        arUrl = "http://loc.api.geopunt.be/"
        inet = internet_on(proxyUrl=self.proxy,
                           timeout=self.timeout,
                           testSite=arUrl)

        if not inet:
            msg = "Kan geen verbing maken met de site 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(
                arUrl)
            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.am = basisregisters.adresMatch(self.timeout, self.proxy)
            gemeenteNamen = [n["Naam"] for n in self.am.gemeenten()]

            self.ui.gemeenteBox.addItems(gemeenteNamen)
            self.completerModel.setStringList(gemeenteNamen)
            self.ui.gemeenteBox.setEditText(
                QCoreApplication.translate("geopunt4QgisAdresDialog",
                                           "gemeente"))
            self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}')
            self.ui.gemeenteBox.setFocus()
            self.firstShow = False

    def openHelp(self):
        webbrowser.open_new_tab(
            "http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/zoek-een-adres"
        )

    def onZoekActivated(self):
        self._clearGraphicsLayer()
        self.bar.clearWidgets()

        gemeente = self.ui.gemeenteBox.currentText()
        if gemeente != QCoreApplication.translate("geopunt4QgisAdresDialog",
                                                  "gemeente"):
            self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #000000}')

            txt = self.ui.zoekText.text() + ", " + gemeente

            suggesties = self.gp.fetchSuggestion(txt, 25)

            self.ui.resultLijst.clear()
            if type(suggesties) is list and len(suggesties) != 0:
                self.ui.resultLijst.addItems(suggesties)
                if len(suggesties) == 1:
                    self.ui.resultLijst.setCurrentRow(0)

    def onItemActivated(self, item):
        txt = item.text()
        self._zoomLoc(txt)

    def onItemClick(self, item):
        txt = item.text()
        streetNr = txt.split(",")[:-1]
        self.ui.zoekText.setText(",".join(streetNr))

    def onZoomKnopClick(self):
        item = self.ui.resultLijst.currentItem()
        if item:
            self._zoomLoc(item.text())

    def onAdd2mapKnopClick(self):
        item = self.ui.resultLijst.currentItem()
        if item:
            self._addToMap(item.text())

    def _clearGraphicsLayer(self):
        for graphic in self.graphicsLayer:
            self.iface.mapCanvas().scene().removeItem(graphic)
        self.graphicsLayer = []

    def _zoomLoc(self, txt):
        self._clearGraphicsLayer()
        locations = self.gp.fetchLocation(txt)
        if type(locations) is list and len(locations):
            loc = locations[0]

            LowerLeftX = loc['BoundingBox']['LowerLeft']['X_Lambert72']
            LowerLeftY = loc['BoundingBox']['LowerLeft']['Y_Lambert72']
            UpperRightX = loc['BoundingBox']['UpperRight']['X_Lambert72']
            UpperRightY = loc['BoundingBox']['UpperRight']['Y_Lambert72']

            self.gh.zoomtoRec(QgsPointXY(LowerLeftX, LowerLeftY),
                              QgsPointXY(UpperRightX, UpperRightY), 31370)

            xlb, ylb = loc["Location"]["X_Lambert72"], loc["Location"][
                "Y_Lambert72"]
            x, y = self.gh.prjPtToMapCrs(QgsPointXY(xlb, ylb), 31370)

            m = QgsVertexMarker(self.iface.mapCanvas())
            self.graphicsLayer.append(m)
            m.setCenter(QgsPointXY(x, y))
            m.setColor(QColor(255, 255, 0))
            m.setIconSize(1)
            m.setIconType(QgsVertexMarker.ICON_BOX)
            m.setPenWidth(9)

        elif type(locations) is str:
            self.bar.pushMessage(QCoreApplication.translate(
                "geopunt4QgisAdresDialog", "Waarschuwing"),
                                 locations,
                                 level=Qgis.Warning,
                                 duration=3)
        else:
            self.bar.pushMessage("Error",
                                 QCoreApplication.translate(
                                     "geopunt4QgisAdresDialog",
                                     "onbekende fout"),
                                 level=Qgis.Critical,
                                 duration=3)

    def _addToMap(self, txt):
        if not self.layernameValid(): return
        locations = self.gp.fetchLocation(txt)
        if type(locations) is list and len(locations):
            loc = locations[0]
            x, y = loc["Location"]["X_Lambert72"], loc["Location"][
                "Y_Lambert72"]
            adres = loc["FormattedAddress"]
            LocationType = loc["LocationType"]

            pt = self.gh.prjPtToMapCrs(QgsPointXY(x, y), 31370)

            self.gh.save_adres_point(pt,
                                     adres,
                                     typeAddress=LocationType,
                                     layername=self.layerName,
                                     saveToFile=self.saveToFile,
                                     sender=self,
                                     startFolder=os.path.join(
                                         self.startDir, self.layerName))

    def layernameValid(self):
        if not hasattr(self, 'layerName'):
            layerName, accept = QInputDialog.getText(
                None,
                QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'),
                QCoreApplication.translate("geopunt4Qgis",
                                           'Geef een naam voor de laag op:'))
            if accept == False:
                return False
            else:
                self.layerName = layerName
        return True

    def clean(self):
        self.bar.clearWidgets()
        self.ui.resultLijst.clear()
        self.ui.zoekText.setText("")
        self.ui.gemeenteBox.setEditText(
            QCoreApplication.translate("geopunt4QgisAdresDialog", "gemeente"))
        self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}')
        self._clearGraphicsLayer()
Exemple #8
0
class S3Manager(S3Connection):
    def __init__(self,
                 access_key_id,
                 secret_access_key,
                 bucket_name,
                 filenames,
                 upload_options,
                 wizard_page,
                 parent=None):

        S3Connection.__init__(self, access_key_id, secret_access_key)
        self.upload_options = upload_options
        self.bucket_name = bucket_name
        self.bucket = None
        self.filenames = filenames
        self.s3Uploaders = []
        self.threads = []

        self.count_uploaded_images = 0
        self.num_uploading_images = 0

        #For GUI (messages and progress bars)
        self.wizard_page = wizard_page

        self.uploader_widget = QtGui.QWidget()
        self.uploader_widget.setWindowTitle("Upload Progress Bars")
        self.uploader_widget.setWindowFlags(Qt.WindowStaysOnTopHint)

        self.uploader_v_box = QtGui.QVBoxLayout()
        self.uploader_widget.setLayout(self.uploader_v_box)

        self.msg_bar_main = None
        self.msg_bar_main_content = None
        self.cancel_button_main = None

        self.msg_bars = []
        self.msg_bars_content = []
        self.progress_bars = []
        self.cancel_buttons = []

    def getBucket(self):

        for trial in xrange(3):
            if self.bucket: break
            try:
                self.bucket = super(S3Manager,
                                    self).get_bucket(self.bucket_name)
                QgsMessageLog.logMessage('Connection established' % trial,
                                         'OAM',
                                         level=QgsMessageLog.INFO)
            except:
                if trial == 2:
                    QgsMessageLog.logMessage(
                        'Failed to connect after 3 attempts',
                        'OAM',
                        level=QgsMessageLog.CRITICAL)

        return self.bucket
        """for testing purpose"""
        """
        rsKeys = []
        for key in self.bucket.list():
            rsKeys.append(repr(key))
        return rsKeys
        """

    #functions for threading purpose
    def uploadFiles(self):
        """ Testing purpose only """
        if "notify_oam" in self.upload_options:
            print "notify_oam"
        if "trigger_tiling" in self.upload_options:
            print "trigger_tiling"

        # configure the msg_bar_main (including Cancel button and its container)
        self.msg_bar_main = QgsMessageBar()
        self.msg_bar_main.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.wizard_page.layout().addWidget(self.msg_bar_main)

        self.msg_bar_main_content = self.msg_bar_main.createMessage(
            'Performing upload...', )
        self.cancel_button_main = QPushButton()
        self.cancel_button_main.setText('Cancel')
        self.cancel_button_main.clicked.connect(self.cancelAllUploads)
        self.msg_bar_main_content.layout().addWidget(self.cancel_button_main)
        self.msg_bar_main.clearWidgets()
        self.msg_bar_main.pushWidget(self.msg_bar_main_content,
                                     level=QgsMessageBar.INFO)

        self.num_uploading_images = len(self.filenames)

        for i in range(0, self.num_uploading_images):
            filename = self.filenames[i]

            # create a new S3Uploader instance
            self.s3Uploaders.append(
                S3Uploader(filename, self.bucket, self.upload_options, i))

            try:
                # start the worker in a new thread
                self.threads.append(QThread())
                self.s3Uploaders[i].moveToThread(self.threads[i])
                self.s3Uploaders[i].finished.connect(self.finishUpload)
                self.s3Uploaders[i].error.connect(self.displayUploadError)
                self.threads[i].started.connect(self.s3Uploaders[i].run)
                self.threads[i].start()

                print repr(self.threads[i])

                # configure the msg_bars for progress bar
                self.msg_bars.append(QgsMessageBar())
                self.msg_bars[i].setSizePolicy(QSizePolicy.Minimum,
                                               QSizePolicy.Fixed)
                #self.wizard_page.layout().addWidget(self.msg_bars[i])
                self.uploader_v_box.addWidget(self.msg_bars[i])

                #set the texts of uploading files
                file_basename = str(os.path.basename(str(filename)))
                self.msg_bars_content.append(self.msg_bars[i].createMessage(
                    file_basename, ))
                self.msg_bars[i].clearWidgets()
                self.msg_bars[i].pushWidget(self.msg_bars_content[i],
                                            level=QgsMessageBar.INFO)

                #add progress bars in the message bar
                self.progress_bars.append(QProgressBar())
                self.progress_bars[i].setAlignment(Qt.AlignLeft
                                                   | Qt.AlignVCenter)
                self.msg_bars_content[i].layout().addWidget(
                    self.progress_bars[i])
                self.s3Uploaders[i].progress.connect(self.updateProgressBar)

                #set cancel button in the message bar for each upload file
                """
                self.cancel_buttons.append(QPushButton())
                self.cancel_buttons[i].setText('Cancel')
                self.cancel_buttons[i].clicked.connect(self.cancelAllUploads)
                self.msg_bars_content[i].layout().addWidget(self.cancel_buttons[i])
                #self.cancel_buttons[i].clicked.connect(self.cancelUpload)
                """

            except Exception, e:
                return repr(e)

        #Display upload progress bars in a separate widget
        self.uploader_widget.show()
        screenShape = QtGui.QDesktopWidget().screenGeometry()
        width, height = screenShape.width(), screenShape.height()
        winW, winH = self.uploader_widget.frameGeometry().width(
        ), self.uploader_widget.frameGeometry().height()
        left = width - (winW + 10)
        top = height - (winH + 50)
        self.uploader_widget.move(left, top)
        #self.uploader_widget.activateWindow()
        return True
Exemple #9
0
class QQuakeDialog(QDialog, FORM_CLASS):
    """
    The main plugin dialog
    """
    def __init__(self, iface, parent=None):  # pylint:disable=too-many-statements
        """Constructor."""
        super().__init__(parent)

        self.setupUi(self)

        self.setObjectName('QQuakeDialog')
        QgsGui.enableAutoGeometryRestore(self)

        self.scrollArea.setStyleSheet("""
            QScrollArea { background: transparent; }
            QScrollArea > QWidget > QWidget { background: transparent; }
            QScrollArea > QWidget > QScrollBar { background: 1; }
        """)
        self.scrollArea_2.setStyleSheet(self.scrollArea.styleSheet())
        self.scrollArea_3.setStyleSheet(self.scrollArea.styleSheet())
        self.scrollArea_4.setStyleSheet(self.scrollArea.styleSheet())

        self.splitter.setStretchFactor(0, 0)
        self.splitter_2.setStretchFactor(0, 0)
        self.splitter_3.setStretchFactor(0, 0)
        self.splitter_4.setStretchFactor(0, 0)
        self.splitter.setStretchFactor(1, 1)
        self.splitter_2.setStretchFactor(1, 1)
        self.splitter_3.setStretchFactor(1, 1)
        self.splitter_4.setStretchFactor(1, 1)

        self.fdsn_event_filter = FilterParameterWidget(
            iface, SERVICE_MANAGER.FDSNEVENT)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.fdsn_event_filter)
        self.fdsn_event_filter_container.setLayout(vl)
        self.earthquake_service_info_widget = ServiceInformationWidget(iface)
        self.fdsn_by_id_filter = FilterByIdWidget(iface,
                                                  SERVICE_MANAGER.FDSNEVENT)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.fdsn_by_id_filter)
        self.fdsn_by_id_container.setLayout(vl)
        self.fdsn_by_url_widget = FetchByUrlWidget(iface,
                                                   SERVICE_MANAGER.FDSNEVENT)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.fdsn_by_url_widget)
        self.fdsn_by_url_container.setLayout(vl)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.earthquake_service_info_widget)
        self.earthquake_service_info_container.setLayout(vl)

        self.macro_filter = FilterParameterWidget(iface,
                                                  SERVICE_MANAGER.MACROSEISMIC)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.macro_filter)
        self.macro_filter_container.setLayout(vl)
        self.macro_by_id_filter = FilterByIdWidget(
            iface, SERVICE_MANAGER.MACROSEISMIC)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.macro_by_id_filter)
        self.macro_by_id_container.setLayout(vl)
        self.macro_by_url_widget = FetchByUrlWidget(
            iface, SERVICE_MANAGER.MACROSEISMIC)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.macro_by_url_widget)
        self.macro_by_url_container.setLayout(vl)
        self.macro_service_info_widget = ServiceInformationWidget(iface)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.macro_service_info_widget)
        self.macro_service_info_container.setLayout(vl)

        self.station_filter = FilterParameterWidget(
            iface, SERVICE_MANAGER.FDSNSTATION)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.station_filter)
        self.station_filter_container.setLayout(vl)
        self.station_by_id_filter = FilterStationByIdWidget(
            iface, SERVICE_MANAGER.FDSNSTATION)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.station_by_id_filter)
        self.station_by_id_container.setLayout(vl)
        self.station_service_info_widget = ServiceInformationWidget(iface)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.station_service_info_widget)
        self.station_service_info_container.setLayout(vl)

        self.station_by_url_widget = FetchByUrlWidget(
            iface, SERVICE_MANAGER.FDSNSTATION)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.station_by_url_widget)
        self.station_by_url_container.setLayout(vl)

        self.ogc_service_widget = OgcServiceWidget(iface)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.ogc_service_widget)
        self.ogc_widget_container.setLayout(vl)
        self.ogc_service_info_widget = ServiceInformationWidget(iface)
        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)
        vl.addWidget(self.ogc_service_info_widget)
        self.ogc_service_info_container.setLayout(vl)

        self.message_bar = QgsMessageBar()
        self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.verticalLayout.insertWidget(0, self.message_bar)

        self.fdsn_event_url_text_browser.viewport().setAutoFillBackground(
            False)
        self.fdsn_macro_url_text_browser.viewport().setAutoFillBackground(
            False)
        self.fdsn_station_url_text_browser.viewport().setAutoFillBackground(
            False)

        self.button_box.button(QDialogButtonBox.Ok).setText(
            self.tr('Fetch Data'))
        self.button_box.rejected.connect(self._save_settings)

        self.iface = iface

        # OGC
        self.ogc_combo.addItem(self.tr('Web Map Services (WMS)'),
                               SERVICE_MANAGER.WMS)
        self.ogc_combo.addItem(self.tr('Web Feature Services (WFS)'),
                               SERVICE_MANAGER.WFS)
        self.ogc_combo.currentIndexChanged.connect(self.refreshOgcWidgets)

        self.ogc_list_model = QStandardItemModel(self.ogc_list)
        self.ogc_list.setModel(self.ogc_list_model)
        self.ogc_list.selectionModel().selectionChanged.connect(
            self._ogc_service_changed)

        self._refresh_services()
        SERVICE_MANAGER.refreshed.connect(self._refresh_services)

        # connect to refreshing function to refresh the UI depending on the WS
        self._refresh_fdsnevent_widgets()
        self.refreshFdsnMacroseismicWidgets()
        self.refreshFdsnStationWidgets()

        # change the UI parameter according to the web service chosen
        self.fdsn_event_list.currentRowChanged.connect(
            self._refresh_fdsnevent_widgets)
        self.fdsn_macro_list.currentRowChanged.connect(
            self.refreshFdsnMacroseismicWidgets)
        self.fdsn_station_list.currentRowChanged.connect(
            self.refreshFdsnStationWidgets)

        self.fdsn_event_filter.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT))
        self.fdsn_by_id_filter.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT))
        self.fdsn_by_url_widget.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT))
        self.fdsn_event_list.currentRowChanged.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT))
        self.macro_filter.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC))
        self.macro_by_id_filter.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC))
        self.macro_by_url_widget.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC))
        self.fdsn_macro_list.currentRowChanged.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC))
        self.station_filter.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION))
        self.station_by_id_filter.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION))
        self.fdsn_station_list.currentRowChanged.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION))
        self.station_by_url_widget.changed.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION))

        self.button_box.accepted.connect(self._getEventList)

        self.service_tab_widget.currentChanged.connect(
            lambda: self._refresh_url(None))

        self.fetcher = None

        QgsGui.enableAutoGeometryRestore(self)

        self.fdsn_tab_widget.currentChanged.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT))
        self.macro_tab_widget.currentChanged.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC))
        self.fdsnstation_tab_widget.currentChanged.connect(
            lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION))

        for b in [
                self.button_fdsn_new_service, self.button_macro_new_service,
                self.button_station_new_service, self.button_ogc_new_service
        ]:
            self._build_add_service_menu(b)

        for b in [
                self.button_fdsn_edit_service, self.button_macro_edit_service,
                self.button_station_edit_service, self.button_ogc_edit_service
        ]:
            b.clicked.connect(self._edit_service)

        for b in [
                self.button_fdsn_rename_service,
                self.button_macro_rename_service,
                self.button_station_rename_service,
                self.button_ogc_rename_service
        ]:
            b.clicked.connect(self._rename_service)

        for b in [
                self.button_fdsn_remove_service,
                self.button_macro_remove_service,
                self.button_station_remove_service,
                self.button_ogc_remove_service
        ]:
            b.clicked.connect(self._remove_service)

        for b in [
                self.button_fdsn_export_service,
                self.button_macro_export_service,
                self.button_station_export_service,
                self.button_ogc_export_service
        ]:
            b.clicked.connect(self._export_service)

        self._restore_settings()
        self._refresh_url(SERVICE_MANAGER.FDSNEVENT)
        self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)
        self._refresh_url(SERVICE_MANAGER.FDSNSTATION)

    def closeEvent(self, e):  # pylint: disable=missing-function-docstring
        self._save_settings()
        super().closeEvent(e)

    def _build_add_service_menu(self, widget):
        """
        Builds the add service menu for a specific widget
        """
        menu = QMenu()
        save_action = QAction(self.tr('Save Current Configuration As…'),
                              parent=menu)
        save_action.setObjectName('save_action')
        menu.addAction(save_action)
        save_action.triggered.connect(self._save_configuration)

        import_action = QAction(self.tr('Import from File…'), parent=menu)
        menu.addAction(import_action)
        import_action.triggered.connect(self._import_configuration)

        create_new_action = QAction(self.tr('Create New Service…'),
                                    parent=menu)
        menu.addAction(create_new_action)
        create_new_action.triggered.connect(self._create_configuration)

        menu.aboutToShow.connect(lambda: self._menu_about_to_show(menu))
        widget.setMenu(menu)

    def _menu_about_to_show(self, menu):
        """
        Triggered when the Add Service menu is about to show
        """
        save_current_action = menu.findChild(QAction, 'save_action')

        service_type = self.get_current_service_type()
        filter_widget = self.get_service_filter_widget(service_type)

        save_current_action.setEnabled(
            hasattr(filter_widget, 'to_service_definition'))

    def _refresh_services(self):
        """
        Refreshes the list of available services
        """

        # fill the FDSN listWidget with the dictionary keys
        self.fdsn_event_list.clear()
        self.fdsn_event_list.addItems(
            SERVICE_MANAGER.available_services(SERVICE_MANAGER.FDSNEVENT))
        self.fdsn_event_list.setCurrentRow(0)

        # fill the FDSN listWidget with the dictionary keys
        self.fdsn_macro_list.clear()
        self.fdsn_macro_list.addItems(
            SERVICE_MANAGER.available_services(SERVICE_MANAGER.MACROSEISMIC))
        self.fdsn_macro_list.setCurrentRow(0)

        # fill the FDSN listWidget with the dictionary keys
        self.fdsn_station_list.clear()
        self.fdsn_station_list.addItems(
            SERVICE_MANAGER.available_services(SERVICE_MANAGER.FDSNSTATION))
        self.fdsn_station_list.setCurrentRow(0)

        self.refreshOgcWidgets()

    def _save_configuration(self):
        """
        Triggers saving the current service configuration
        """
        service_type = self.get_current_service_type()
        name, ok = QInputDialog.getText(
            self, self.tr('Save Service Configuration'),
            self.tr('Save the current service configuration as'))
        if not name or not ok:
            return

        filter_widget = self.get_service_filter_widget(service_type)
        SERVICE_MANAGER.save_service(service_type, name,
                                     filter_widget.to_service_definition())
        self.set_current_service(service_type, name)

    def _save_settings(self):
        """
        Saves all settings currently defined in the dialog
        """
        s = QgsSettings()
        if self.service_tab_widget.currentIndex(
        ) != self.service_tab_widget.count() - 1:
            s.setValue('/plugins/qquake/last_tab',
                       self.service_tab_widget.currentIndex())
        s.setValue('/plugins/qquake/fdsn_event_last_event_service',
                   self.fdsn_event_list.currentItem().text())
        s.setValue('/plugins/qquake/macro_last_event_service',
                   self.fdsn_macro_list.currentItem().text())

        s.setValue('/plugins/qquake/fdsnevent_last_tab',
                   self.fdsn_tab_widget.currentIndex())
        s.setValue('/plugins/qquake/macro_last_tab',
                   self.macro_tab_widget.currentIndex())
        s.setValue('/plugins/qquake/station_last_tab',
                   self.fdsnstation_tab_widget.currentIndex())

        self.fdsn_event_filter.save_settings('fdsn_event')
        self.fdsn_by_id_filter.save_settings('fdsn_event')
        self.fdsn_by_url_widget.save_settings('fdsn_event')
        self.macro_filter.save_settings('macro')
        self.macro_by_id_filter.save_settings('macro')
        self.macro_by_url_widget.save_settings('macro')
        self.station_filter.save_settings('stations')
        self.station_by_id_filter.save_settings('stations')
        self.station_by_url_widget.save_settings('stations')

    def _restore_settings(self):
        """
        Restores dialog settings
        """
        s = QgsSettings()

        last_tab = s.value('/plugins/qquake/last_tab')
        if last_tab is not None:
            self.service_tab_widget.setCurrentIndex(int(last_tab))

        last_service = s.value('/plugins/qquake/fdsn_event_last_event_service')
        if last_service is not None:
            try:
                self.fdsn_event_list.setCurrentItem(
                    self.fdsn_event_list.findItems(last_service,
                                                   Qt.MatchContains)[0])
            except IndexError:
                pass

        last_service = s.value('/plugins/qquake/macro_last_event_service')
        if last_service is not None:
            self.fdsn_macro_list.setCurrentItem(
                self.fdsn_macro_list.findItems(last_service,
                                               Qt.MatchContains)[0])

        self.fdsn_event_filter.restore_settings('fdsn_event')
        self.fdsn_by_id_filter.restore_settings('fdsn_event')
        self.fdsn_by_url_widget.restore_settings('fdsn_event')
        self.macro_filter.restore_settings('macro')
        self.macro_by_id_filter.restore_settings('macro')
        self.macro_by_url_widget.restore_settings('fdsn_event')
        self.station_filter.restore_settings('stations')
        self.station_by_id_filter.restore_settings('stations')
        self.station_by_url_widget.restore_settings('stations')

        self.fdsn_tab_widget.setCurrentIndex(
            s.value('/plugins/qquake/fdsnevent_last_tab', 0, int))
        self.macro_tab_widget.setCurrentIndex(
            s.value('/plugins/qquake/macro_last_tab', 0, int))
        self.fdsnstation_tab_widget.setCurrentIndex(
            s.value('/plugins/qquake/station_last_tab', 0, int))

    def get_current_service_id(self, service_type: str) -> Optional[str]:
        """
        Returns the current selected service id
        """
        if service_type == SERVICE_MANAGER.FDSNEVENT:
            service_id = self.fdsn_event_list.currentItem().text(
            ) if self.fdsn_event_list.currentItem() else None
        elif service_type == SERVICE_MANAGER.MACROSEISMIC:
            service_id = self.fdsn_macro_list.currentItem().text(
            ) if self.fdsn_macro_list.currentItem() else None
        elif service_type == SERVICE_MANAGER.FDSNSTATION:
            service_id = self.fdsn_station_list.currentItem().text(
            ) if self.fdsn_station_list.currentItem() else None
        elif service_type in (SERVICE_MANAGER.WMS, SERVICE_MANAGER.WFS):
            service_id = self.ogc_list.selectionModel().selectedIndexes(
            )[0].data() if self.ogc_list.selectionModel().selectedIndexes(
            ) else None
        else:
            service_id = None

        return service_id

    def get_current_service_type(self) -> Optional[str]:
        """
        Returns the current service type
        """
        if self.service_tab_widget.currentIndex() == 0:
            service_type = SERVICE_MANAGER.FDSNEVENT
        elif self.service_tab_widget.currentIndex() == 1:
            service_type = SERVICE_MANAGER.MACROSEISMIC
        elif self.service_tab_widget.currentIndex() == 2:
            service_type = SERVICE_MANAGER.FDSNSTATION
        elif self.service_tab_widget.currentIndex() == 3:
            return self.ogc_combo.currentData()
        else:
            service_type = None
        return service_type

    def get_service_filter_widget(
        self,  # pylint:disable=too-many-branches
        service_type: str
    ) -> Optional[
            Union[FilterParameterWidget, FilterByIdWidget, FetchByUrlWidget,
                  FilterStationByIdWidget, OgcServiceWidget]]:
        """
        Returns the service filter widget for a specific service type
        """
        widget = None
        if service_type == SERVICE_MANAGER.FDSNEVENT:
            if self.fdsn_tab_widget.currentIndex() in (0, 3):
                widget = self.fdsn_event_filter
            elif self.fdsn_tab_widget.currentIndex() == 1:
                widget = self.fdsn_by_id_filter
            elif self.fdsn_tab_widget.currentIndex() == 2:
                widget = self.fdsn_by_url_widget
        elif service_type == SERVICE_MANAGER.MACROSEISMIC:
            if self.macro_tab_widget.currentIndex() in (0, 3):
                widget = self.macro_filter
            elif self.macro_tab_widget.currentIndex() == 1:
                widget = self.macro_by_id_filter
            elif self.macro_tab_widget.currentIndex() == 2:
                widget = self.macro_by_url_widget
        elif service_type == SERVICE_MANAGER.FDSNSTATION:
            if self.fdsnstation_tab_widget.currentIndex() in (0, 3):
                widget = self.station_filter
            elif self.fdsnstation_tab_widget.currentIndex() == 1:
                widget = self.station_by_id_filter
            elif self.fdsnstation_tab_widget.currentIndex() == 2:
                widget = self.station_by_url_widget
        elif service_type in (SERVICE_MANAGER.WMS, SERVICE_MANAGER.WFS):
            widget = self.ogc_service_widget
        return widget

    def get_fetcher(self, service_type: Optional[str] = None):
        """
        Returns a quake fetcher corresponding to the current dialog settings
        """

        if service_type is None:
            service_type = self.get_current_service_type()

        service = self.get_current_service_id(service_type)
        if not service:
            return None

        filter_widget = self.get_service_filter_widget(service_type)

        service_config = SERVICE_MANAGER.service_details(service_type, service)

        if isinstance(filter_widget, FilterParameterWidget):
            fetcher = Fetcher(
                service_type=service_type,
                event_service=service,
                event_start_date=filter_widget.start_date(),
                event_end_date=filter_widget.end_date(),
                event_min_magnitude=filter_widget.min_magnitude(),
                event_max_magnitude=filter_widget.max_magnitude(),
                limit_extent_rect=filter_widget.extent_rect(),
                min_latitude=filter_widget.min_latitude(),
                max_latitude=filter_widget.max_latitude(),
                min_longitude=filter_widget.min_longitude(),
                max_longitude=filter_widget.max_longitude(),
                limit_extent_circle=filter_widget.limit_extent_circle(),
                circle_latitude=filter_widget.circle_latitude(),
                circle_longitude=filter_widget.circle_longitude(),
                circle_min_radius=filter_widget.circle_min_radius(),
                circle_max_radius=filter_widget.circle_max_radius(),
                circle_radius_unit=filter_widget.circle_radius_unit(),
                earthquake_number_mdps_greater=filter_widget.
                earthquake_number_mdps_greater(),
                earthquake_max_intensity_greater=filter_widget.
                earthquake_max_intensity_greater(),
                output_fields=filter_widget.output_fields,
                output_type=filter_widget.output_type(),
                convert_negative_depths=filter_widget.convert_negative_depths(
                ),
                depth_unit=filter_widget.depth_unit(),
                event_type=filter_widget.event_type(),
                updated_after=filter_widget.updated_after())
        elif isinstance(filter_widget, FilterByIdWidget):
            if not service_config['settings'].get('queryeventid'):
                fetcher = None
            else:
                fetcher = Fetcher(
                    service_type=service_type,
                    event_service=service,
                    event_ids=filter_widget.ids(),
                    contributor_id=filter_widget.contributor_id(),
                    output_fields=filter_widget.output_fields,
                    output_type=filter_widget.output_type(),
                    convert_negative_depths=filter_widget.
                    convert_negative_depths(),
                    depth_unit=filter_widget.depth_unit())
        elif isinstance(filter_widget, FetchByUrlWidget):
            fetcher = Fetcher(service_type=service_type,
                              event_service=service,
                              url=filter_widget.url(),
                              output_fields=filter_widget.output_fields,
                              output_type=filter_widget.output_type(),
                              convert_negative_depths=filter_widget.
                              convert_negative_depths(),
                              depth_unit=filter_widget.depth_unit())
        elif isinstance(filter_widget, FilterStationByIdWidget):
            fetcher = Fetcher(service_type=service_type,
                              event_service=service,
                              network_codes=filter_widget.network_codes(),
                              station_codes=filter_widget.station_codes(),
                              locations=filter_widget.locations(),
                              output_fields=filter_widget.output_fields,
                              output_type=filter_widget.output_type(),
                              convert_negative_depths=filter_widget.
                              convert_negative_depths(),
                              depth_unit=filter_widget.depth_unit())
        return fetcher

    def _refresh_url(self, service_type: Optional[str] = None):
        """
        Updates the service URL
        """
        if not service_type:
            service_type = self.get_current_service_type()
            if service_type is None:
                self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
                return

            if service_type not in (SERVICE_MANAGER.FDSNEVENT,
                                    SERVICE_MANAGER.MACROSEISMIC,
                                    SERVICE_MANAGER.FDSNSTATION):
                self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
                return

        fetcher = self.get_fetcher(service_type)
        if not fetcher:
            self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
            return

        self._valid_changed()

        if service_type == SERVICE_MANAGER.FDSNEVENT:
            self.fdsn_event_url_text_browser.setText(
                '<a href="{0}">{0}</a>'.format(fetcher.generate_url()))
        elif service_type == SERVICE_MANAGER.MACROSEISMIC:
            self.fdsn_macro_url_text_browser.setText(
                '<a href="{0}">{0}</a>'.format(fetcher.generate_url()))
        elif service_type == SERVICE_MANAGER.FDSNSTATION:
            self.fdsn_station_url_text_browser.setText(
                '<a href="{0}">{0}</a>'.format(fetcher.generate_url()))

    def _valid_changed(self):
        """
        Called when dialog entry validation should occur
        """
        service_type = self.get_current_service_type()
        if service_type not in (SERVICE_MANAGER.FDSNEVENT,
                                SERVICE_MANAGER.MACROSEISMIC,
                                SERVICE_MANAGER.FDSNSTATION):
            self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
            return

        filter_widget = self.get_service_filter_widget(service_type)
        self.button_box.button(QDialogButtonBox.Ok).setEnabled(
            filter_widget.is_valid())

    def _update_service_widgets(
            self,  # pylint: disable=too-many-locals,too-many-branches
            service_type,
            service_id,
            filter_widget,
            filter_by_id_widget,
            fetch_by_url_widget,
            info_widget,
            remove_service_button,
            edit_service_button,
            rename_service_button,
            tab_widget):
        """
        Updates all widgets to reflect the current service details
        """
        service_config = SERVICE_MANAGER.service_details(
            service_type, service_id)

        date_start = QDateTime.fromString(service_config['datestart'],
                                          Qt.ISODate)
        default_date_start = QDateTime.fromString(
            service_config['default']['datestart'],
            Qt.ISODate) if service_config['default'].get('datestart') else None

        # if the dateend is not set in the config.json set the date to NOW
        date_end = QDateTime.fromString(
            service_config['dateend'],
            Qt.ISODate) if 'dateend' in service_config and service_config[
                'dateend'] else None

        default_date_end = QDateTime.fromString(
            service_config['default']['dateend'],
            Qt.ISODate) if service_config['default'].get('dateend') else None

        filter_widget.set_date_range_limits(date_start, date_end)
        filter_widget.set_current_date_range(default_date_start,
                                             default_date_end)

        if service_config['default'].get('boundingboxpredefined'):
            filter_widget.set_predefined_bounding_box(
                service_config['default'].get('boundingboxpredefined'))
        if service_config['default'].get('minimumlatitude'):
            filter_widget.set_min_latitude(
                service_config['default'].get('minimumlatitude'))
        if service_config['default'].get('maximumlatitude'):
            filter_widget.set_max_latitude(
                service_config['default'].get('maximumlatitude'))
        if service_config['default'].get('minimumlongitude'):
            filter_widget.set_min_longitude(
                service_config['default'].get('minimumlongitude'))
        if service_config['default'].get('maximumlongitude'):
            filter_widget.set_max_longitude(
                service_config['default'].get('maximumlongitude'))
        if service_config['default'].get('circlelatitude'):
            filter_widget.set_circle_latitude(
                service_config['default'].get('circlelatitude'))
        if service_config['default'].get('circlelongitude'):
            filter_widget.set_circle_longitude(
                service_config['default'].get('circlelongitude'))
        if service_config['default'].get('minimumcircleradius'):
            filter_widget.set_min_circle_radius(
                service_config['default'].get('minimumcircleradius'))
        if service_config['default'].get('maximumcircleradius'):
            filter_widget.set_max_circle_radius(
                service_config['default'].get('maximumcircleradius'))
        if service_config['default'].get('minimummagnitude'):
            filter_widget.set_min_magnitude(
                service_config['default'].get('minimummagnitude'))
        if service_config['default'].get('maximummagnitude'):
            filter_widget.set_max_magnitude(
                service_config['default'].get('maximummagnitude'))
        if service_config['default'].get('macromaxintensitygreater'):
            filter_widget.set_max_intensity_greater(
                service_config['default'].get('macromaxintensitygreater'))
        if service_config['default'].get('macromdpsgreaterthan'):
            filter_widget.set_mdps_greater_than(
                service_config['default'].get('macromdpsgreaterthan'))
        if service_config['default'].get('eventtype'):
            filter_widget.set_event_type(
                service_config['default'].get('eventtype'))
        updated_after = QDateTime.fromString(
            service_config['default']['updatedafter'], Qt.ISODate
        ) if service_config['default'].get('updatedafter') else None
        if updated_after:
            filter_widget.set_updated_after(updated_after)

        filter_widget.set_extent_limit(
            service_config.get('boundingbox', [-180, -90, 180, 90]))

        if service_type in [
                SERVICE_MANAGER.FDSNEVENT, SERVICE_MANAGER.MACROSEISMIC
        ]:
            tab_widget.widget(1).setEnabled(service_config['settings'].get(
                'queryeventid', False))

        info_widget.set_service(service_type=service_type,
                                service_id=service_id)

        filter_widget.set_service_id(service_id)
        filter_by_id_widget.set_service_id(service_id)
        if fetch_by_url_widget is not None:
            fetch_by_url_widget.set_service_id(service_id)

        remove_service_button.setEnabled(not service_config['read_only'])
        edit_service_button.setEnabled(not service_config['read_only'])
        rename_service_button.setEnabled(not service_config['read_only'])

    def _refresh_fdsnevent_widgets(self):
        """
        Refreshing the FDSN-Event UI depending on the WS chosen
        """
        if not self.fdsn_event_list.currentItem():
            return

        service_id = self.fdsn_event_list.currentItem().text()
        self._update_service_widgets(
            service_type=SERVICE_MANAGER.FDSNEVENT,
            service_id=service_id,
            filter_widget=self.fdsn_event_filter,
            filter_by_id_widget=self.fdsn_by_id_filter,
            fetch_by_url_widget=self.fdsn_by_url_widget,
            info_widget=self.earthquake_service_info_widget,
            remove_service_button=self.button_fdsn_remove_service,
            edit_service_button=self.button_fdsn_edit_service,
            rename_service_button=self.button_fdsn_rename_service,
            tab_widget=self.fdsn_tab_widget)

    def refreshFdsnMacroseismicWidgets(self):
        """
        Refreshing the FDSN-Macroseismic UI depending on the WS chosen
        """
        if not self.fdsn_macro_list.currentItem():
            return

        service_id = self.fdsn_macro_list.currentItem().text()
        self._update_service_widgets(
            service_type=SERVICE_MANAGER.MACROSEISMIC,
            service_id=service_id,
            filter_widget=self.macro_filter,
            filter_by_id_widget=self.macro_by_id_filter,
            fetch_by_url_widget=self.macro_by_url_widget,
            info_widget=self.macro_service_info_widget,
            remove_service_button=self.button_macro_remove_service,
            edit_service_button=self.button_macro_edit_service,
            rename_service_button=self.button_macro_rename_service,
            tab_widget=self.macro_tab_widget)

    def refreshFdsnStationWidgets(self):
        """
        Refreshing the FDSN-Macroseismic UI depending on the WS chosen
        """
        if not self.fdsn_station_list.currentItem():
            return

        service_id = self.fdsn_station_list.currentItem().text()
        self._update_service_widgets(
            service_type=SERVICE_MANAGER.FDSNSTATION,
            service_id=service_id,
            filter_by_id_widget=self.station_by_id_filter,
            fetch_by_url_widget=self.station_by_url_widget,
            filter_widget=self.station_filter,
            info_widget=self.station_service_info_widget,
            remove_service_button=self.button_station_remove_service,
            edit_service_button=self.button_station_edit_service,
            rename_service_button=self.button_station_rename_service,
            tab_widget=self.fdsnstation_tab_widget)

    def refreshOgcWidgets(self):
        """
        read the ogc_combo and fill it with the services
        """
        self.ogc_list_model.clear()
        ogc_selection = self.ogc_combo.currentData()

        services = SERVICE_MANAGER.available_services(ogc_selection)

        group_items = {}

        for service in services:
            service_config = SERVICE_MANAGER.service_details(
                ogc_selection, service)
            group = service_config.get('group')
            if not group or group in group_items:
                continue

            group_item = QStandardItem(group)
            group_item.setFlags(Qt.ItemIsEnabled)
            self.ogc_list_model.appendRow([group_item])
            group_items[group] = group_item

        first_item = None
        for service in services:
            item = QStandardItem(service)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            item.setData(service, role=Qt.UserRole)
            if not first_item:
                first_item = item

            service_config = SERVICE_MANAGER.service_details(
                ogc_selection, service)
            group = service_config.get('group')
            if group:
                parent = group_items[group]
                parent.appendRow([item])
            else:
                self.ogc_list_model.appendRow([item])

        self.ogc_list.expandAll()
        first_item_index = self.ogc_list_model.indexFromItem(first_item)
        self.ogc_list.selectionModel().select(
            first_item_index, QItemSelectionModel.ClearAndSelect)

        service_config = SERVICE_MANAGER.service_details(
            ogc_selection, self.get_current_service_id(ogc_selection))
        self.button_ogc_edit_service.setEnabled(
            not service_config['read_only'])
        self.button_ogc_rename_service.setEnabled(
            not service_config['read_only'])
        self.button_ogc_remove_service.setEnabled(
            not service_config['read_only'])

    def _ogc_service_changed(self, _, __):
        """
        Triggered when the current OGC service changes
        """
        if not self.ogc_list.selectionModel().selectedIndexes():
            return

        current_service = self.ogc_list.selectionModel().selectedIndexes(
        )[0].data(Qt.UserRole)
        if not current_service:
            return

        self.ogc_service_widget.set_service(
            service_type=self.ogc_combo.currentData(),
            service_id=current_service)

        self.ogc_service_info_widget.set_service(
            service_type=self.ogc_combo.currentData(),
            service_id=current_service)

        service_config = SERVICE_MANAGER.service_details(
            self.ogc_combo.currentData(), current_service)
        self.button_ogc_edit_service.setEnabled(
            not service_config['read_only'])
        self.button_ogc_rename_service.setEnabled(
            not service_config['read_only'])
        self.button_ogc_remove_service.setEnabled(
            not service_config['read_only'])

    def _remove_service(self):
        """
        Removes the current service
        """
        service_type = self.get_current_service_type()
        service_id = self.get_current_service_id(service_type)
        if QMessageBox.question(
                self, self.tr('Remove Service'),
                self.tr('Are you sure you want to remove "{}"?'.format(
                    service_id))) != QMessageBox.Yes:
            return

        SERVICE_MANAGER.remove_service(service_type, service_id)

    def _edit_service(self):
        """
        Edits the current service
        """
        service_type = self.get_current_service_type()
        service_id = self.get_current_service_id(service_type)

        config_dialog = ServiceConfigurationDialog(self.iface, service_type,
                                                   service_id, self)
        if config_dialog.exec_():
            self.set_current_service(service_type, service_id)

    def _rename_service(self):
        """
        Renames the current service
        """
        service_type = self.get_current_service_type()
        service_id = self.get_current_service_id(service_type)

        dlg = QgsNewNameDialog(
            service_id,
            service_id, [],
            existing=SERVICE_MANAGER.available_services(service_type))
        dlg.setHintString(self.tr('Rename service configuration to'))
        dlg.setWindowTitle(self.tr('Rename Service Configuration'))
        dlg.setOverwriteEnabled(False)
        dlg.setConflictingNameWarning(
            self.tr('A configuration with this name already exists'))
        if not dlg.exec_():
            return

        new_name = dlg.name()
        SERVICE_MANAGER.rename_service(service_type, service_id, new_name)
        self.set_current_service(service_type, new_name)

    def set_current_service(self, service_type: str, service_id: str):
        """
        Sets the current service
        """
        if service_type == SERVICE_MANAGER.FDSNEVENT:
            self.fdsn_event_list.setCurrentItem(
                self.fdsn_event_list.findItems(service_id,
                                               Qt.MatchContains)[0])
        elif service_type == SERVICE_MANAGER.MACROSEISMIC:
            self.fdsn_macro_list.setCurrentItem(
                self.fdsn_macro_list.findItems(service_id,
                                               Qt.MatchContains)[0])
        elif service_type == SERVICE_MANAGER.FDSNSTATION:
            self.fdsn_station_list.setCurrentItem(
                self.fdsn_station_list.findItems(service_id,
                                                 Qt.MatchContains)[0])
        elif service_type in (SERVICE_MANAGER.WMS, SERVICE_MANAGER.WFS):
            self.ogc_combo.setCurrentIndex(
                self.ogc_combo.findData(service_type))

            indexes = self.ogc_list_model.match(
                self.ogc_list_model.index(0, 0),
                Qt.UserRole,
                service_id,
                flags=Qt.MatchExactly | Qt.MatchRecursive)
            if len(indexes) > 0:
                self.ogc_list.selectionModel().select(
                    indexes[0], QItemSelectionModel.ClearAndSelect)

    def _create_configuration(self):
        """
        Creates a new service configuration
        """
        service_type = self.get_current_service_type()
        dlg = QgsNewNameDialog(
            '',
            '', [],
            existing=SERVICE_MANAGER.available_services(service_type))
        dlg.setHintString(self.tr('Create a new service configuration named'))
        dlg.setWindowTitle(self.tr('New Service Configuration'))
        dlg.setOverwriteEnabled(False)
        dlg.setConflictingNameWarning(
            self.tr('A configuration with this name already exists'))
        if not dlg.exec_():
            return

        name = dlg.name()
        config_dialog = ServiceConfigurationDialog(self.iface, service_type,
                                                   name, self)
        if config_dialog.exec_():
            self.set_current_service(service_type, name)

    def _export_service(self):
        """
        Triggers exporting a service configuration
        """
        service_type = self.get_current_service_type()
        service_id = self.get_current_service_id(service_type)
        file, _ = QFileDialog.getSaveFileName(
            self, self.tr('Export Service'),
            QDir.homePath() + '/{}.json'.format(service_id),
            'JSON Files (*.json)')
        if not file:
            return

        file = QgsFileUtils.ensureFileNameHasExtension(file, ['json'])

        if SERVICE_MANAGER.export_service(service_type, service_id, file):
            self.message_bar.pushMessage(self.tr("Service exported"),
                                         Qgis.Success, 5)
        else:
            self.message_bar.pushMessage(
                self.tr("An error occurred while exporting service"),
                Qgis.Critical, 5)

    def _import_configuration(self):
        """
        Triggers importing a configuration
        """
        file, _ = QFileDialog.getOpenFileName(self, self.tr('Import Service'),
                                              QDir.homePath(),
                                              'JSON Files (*.json)')
        if not file:
            return

        res, err = SERVICE_MANAGER.import_service(file)
        if res:
            self.message_bar.pushMessage(self.tr("Service imported"),
                                         Qgis.Success, 5)
        else:
            self.message_bar.pushMessage(err, Qgis.Critical, 5)

    def _getEventList(self):
        """
        read the event URL and convert the response in a list
        """
        if self.get_current_service_type() in (SERVICE_MANAGER.WMS,
                                               SERVICE_MANAGER.WFS):
            self.ogc_service_widget.add_selected_layers()
            return

        if self.fetcher:
            # TODO - cancel current request
            return

        self.fetcher = self.get_fetcher()

        def on_started():
            self.progressBar.setValue(0)
            self.progressBar.setRange(0, 0)

        def on_progress(progress: float):
            self.progressBar.setRange(0, 100)
            self.progressBar.setValue(progress)

        self.fetcher.started.connect(on_started)
        self.fetcher.progress.connect(on_progress)
        self.fetcher.finished.connect(self._fetcher_finished)
        self.fetcher.message.connect(self._fetcher_message)
        self.button_box.button(QDialogButtonBox.Ok).setText(
            self.tr('Fetching'))
        self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)

        self.fetcher.fetch_data()

    def _fetcher_message(self, message, level):
        """
        Handles message feedback from a fetcher
        """
        self.message_bar.clearWidgets()
        self.message_bar.pushMessage(message, level, 0)

    def _fetcher_finished(self, res):  # pylint: disable=too-many-branches
        """
        Triggered when a fetcher is finished
        """
        self.progressBar.setRange(0, 100)
        self.progressBar.reset()
        self.button_box.button(QDialogButtonBox.Ok).setText(
            self.tr('Fetch Data'))
        self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)

        if not res:
            self.fetcher.deleteLater()
            self.fetcher = None
            return

        found_results = False

        layers = []
        if self.fetcher.service_type in (SERVICE_MANAGER.FDSNEVENT,
                                         SERVICE_MANAGER.MACROSEISMIC):
            layer = self.fetcher.create_event_layer()
            if layer:
                layers.append(layer)
            if self.fetcher.service_type == SERVICE_MANAGER.MACROSEISMIC:
                layer = self.fetcher.create_mdp_layer()
                if layer:
                    layers.append(layer)

            if layers:
                events_count = layers[0].featureCount()
                found_results = bool(events_count)

                service_limit = self.fetcher.service_config['settings'].get(
                    'querylimitmaxentries', None)
                self.message_bar.clearWidgets()
                if service_limit is not None and events_count >= service_limit:
                    self.message_bar.pushMessage(
                        self.tr("Query exceeded the service's result limit"),
                        Qgis.Critical, 0)
                elif events_count > 500:
                    self.message_bar.pushMessage(
                        self.tr(
                            "Query returned a large number of results ({})".
                            format(events_count)), Qgis.Warning, 0)
                elif events_count == 0:
                    self.message_bar.pushMessage(
                        self.
                        tr("Query returned no results - possibly parameters are invalid for this service"
                           ), Qgis.Critical, 0)
                else:
                    self.message_bar.pushMessage(
                        self.tr("Query returned {} records").format(
                            events_count), Qgis.Success, 0)
        elif self.fetcher.service_type == SERVICE_MANAGER.FDSNSTATION:
            layers.append(self.fetcher.create_stations_layer())
            stations_count = layers[0].featureCount()
            found_results = bool(stations_count)

            if stations_count == 0:
                self.message_bar.pushMessage(
                    self.
                    tr("Query returned no results - possibly parameters are invalid for this service"
                       ), Qgis.Critical, 0)
            else:
                self.message_bar.pushMessage(
                    self.tr("Query returned {} stations").format(
                        stations_count), Qgis.Info, 0)
        else:
            assert False

        self.fetcher.deleteLater()
        self.fetcher = None

        if found_results:
            QgsProject.instance().addMapLayers(layers)
class S3Manager(S3Connection):

    def __init__(self,
                 access_key_id,
                 secret_access_key,
                 bucket_name,
                 filenames,
                 upload_options,
                 wizard_page,
                 parent=None):

        S3Connection.__init__(self, access_key_id, secret_access_key)
        self.upload_options = upload_options
        self.bucket_name = bucket_name
        self.bucket = None
        self.filenames = filenames
        self.s3Uploaders = []
        self.threads = []

        self.count_uploaded_images = 0
        self.num_uploading_images = 0

        #For GUI (messages and progress bars)
        self.wizard_page = wizard_page

        self.uploader_widget = QtGui.QWidget()
        self.uploader_widget.setWindowTitle("Upload Progress Bars")
        self.uploader_widget.setWindowFlags(Qt.WindowStaysOnTopHint)

        self.uploader_v_box = QtGui.QVBoxLayout()
        self.uploader_widget.setLayout(self.uploader_v_box)

        self.msg_bar_main = None
        self.msg_bar_main_content = None
        self.cancel_button_main = None

        self.msg_bars = []
        self.msg_bars_content = []
        self.progress_bars = []
        self.cancel_buttons = []

    def getBucket(self):

        for trial in xrange(3):
            if self.bucket: break
            try:
                self.bucket = super(S3Manager,self).get_bucket(self.bucket_name)
                QgsMessageLog.logMessage(
                    'Connection established' % trial,
                    'OAM',
                    level=QgsMessageLog.INFO)
            except:
                if trial == 2:
                   QgsMessageLog.logMessage(
                    'Failed to connect after 3 attempts',
                    'OAM',
                    level=QgsMessageLog.CRITICAL)

        return self.bucket

        """for testing purpose"""
        """
        rsKeys = []
        for key in self.bucket.list():
            rsKeys.append(repr(key))
        return rsKeys
        """

    #functions for threading purpose
    def uploadFiles(self):

        """ Testing purpose only """
        if "notify_oam" in self.upload_options:
            print "notify_oam"
        if "trigger_tiling" in self.upload_options:
            print "trigger_tiling"

        # configure the msg_bar_main (including Cancel button and its container)
        self.msg_bar_main = QgsMessageBar()
        self.msg_bar_main.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.wizard_page.layout().addWidget(self.msg_bar_main)

        self.msg_bar_main_content = self.msg_bar_main.createMessage('Performing upload...', )
        self.cancel_button_main = QPushButton()
        self.cancel_button_main.setText('Cancel')
        self.cancel_button_main.clicked.connect(self.cancelAllUploads)
        self.msg_bar_main_content.layout().addWidget(self.cancel_button_main)
        self.msg_bar_main.clearWidgets()
        self.msg_bar_main.pushWidget(self.msg_bar_main_content, level=QgsMessageBar.INFO)

        self.num_uploading_images = len(self.filenames)

        for i in range(0, self.num_uploading_images):
            filename = self.filenames[i]

            # create a new S3Uploader instance
            self.s3Uploaders.append(S3Uploader(filename, self.bucket, self.upload_options, i))

            try:
                # start the worker in a new thread
                self.threads.append(QThread())
                self.s3Uploaders[i].moveToThread(self.threads[i])
                self.s3Uploaders[i].finished.connect(self.finishUpload)
                self.s3Uploaders[i].error.connect(self.displayUploadError)
                self.threads[i].started.connect(self.s3Uploaders[i].run)
                self.threads[i].start()

                print repr(self.threads[i])

                # configure the msg_bars for progress bar
                self.msg_bars.append(QgsMessageBar())
                self.msg_bars[i].setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
                #self.wizard_page.layout().addWidget(self.msg_bars[i])
                self.uploader_v_box.addWidget(self.msg_bars[i])

                #set the texts of uploading files
                file_basename = str(os.path.basename(str(filename)))
                self.msg_bars_content.append(self.msg_bars[i].createMessage(file_basename, ))
                self.msg_bars[i].clearWidgets()
                self.msg_bars[i].pushWidget(self.msg_bars_content[i], level=QgsMessageBar.INFO)

                #add progress bars in the message bar
                self.progress_bars.append(QProgressBar())
                self.progress_bars[i].setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
                self.msg_bars_content[i].layout().addWidget(self.progress_bars[i])
                self.s3Uploaders[i].progress.connect(self.updateProgressBar)

                #set cancel button in the message bar for each upload file
                """
                self.cancel_buttons.append(QPushButton())
                self.cancel_buttons[i].setText('Cancel')
                self.cancel_buttons[i].clicked.connect(self.cancelAllUploads)
                self.msg_bars_content[i].layout().addWidget(self.cancel_buttons[i])
                #self.cancel_buttons[i].clicked.connect(self.cancelUpload)
                """

            except Exception, e:
                return repr(e)

        #Display upload progress bars in a separate widget
        self.uploader_widget.show()
        screenShape = QtGui.QDesktopWidget().screenGeometry()
        width, height = screenShape.width(), screenShape.height()
        winW, winH = self.uploader_widget.frameGeometry().width(), self.uploader_widget.frameGeometry().height()
        left = width - (winW + 10)
        top = height - (winH + 50)
        self.uploader_widget.move(left,top)
        #self.uploader_widget.activateWindow()
        return True
Exemple #11
0
class geopunt4QgisPoidialog(QtGui.QDialog):
    def __init__(self, iface):
        QtGui.QDialog.__init__(self, None)
        self.setWindowFlags(self.windowFlags()
                            & ~QtCore.Qt.WindowContextHelpButtonHint)
        #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
        self.iface = iface

        # initialize locale
        locale = QtCore.QSettings().value("locale/userLocale", "nl")
        if not locale: locale == 'nl'
        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 = QtCore.QTranslator()
            self.translator.load(localePath)
            if QtCore.qVersion() > '4.3.3':
                QtCore.QCoreApplication.installTranslator(self.translator)
        self._initGui()

    def _initGui(self):
        'Set up the user interface from Designer.'
        self.ui = Ui_geopunt4QgisPoiDlg()
        self.ui.setupUi(self)

        #get settings
        self.s = QtCore.QSettings()
        self.loadSettings()

        #setup geopunt and geometryHelper objects
        self.gh = gh.geometryHelper(self.iface)
        self.ph = poiHelper(self.iface)

        #create the graphicsLayer
        self.graphicsLayer = []

        #setup a message bar
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QtGui.QSizePolicy.Minimum,
                               QtGui.QSizePolicy.Fixed)
        self.ui.verticalLayout.addWidget(self.bar)

        self.ui.buttonBox.addButton(QtGui.QPushButton("Sluiten"),
                                    QtGui.QDialogButtonBox.RejectRole)
        for btn in self.ui.buttonBox.buttons():
            btn.setAutoDefault(0)

        #table ui
        self.ui.resultLijst.hideColumn(0)

        # filters
        self.firstShow = True
        self.poiTypes = {}
        self.poiCategories = {}
        self.poiThemes = {}

        #actions
        self.ui.resultLijst.addAction(self.ui.actionZoomtoSelection)
        self.ui.actionZoomtoSelection.triggered.connect(self.onZoomSelClicked)
        self.ui.resultLijst.addAction(self.ui.actionAddTSeltoMap)
        self.ui.actionAddTSeltoMap.triggered.connect(self.onAddSelClicked)

        #event handlers
        self.ui.zoekKnop.clicked.connect(self.onZoekActivated)
        self.ui.zoomSelKnop.clicked.connect(self.onZoomSelClicked)
        self.ui.resultLijst.itemDoubleClicked.connect(self.onZoomSelClicked)
        self.ui.resultLijst.itemSelectionChanged.connect(
            self.onSelectionChanged)
        self.ui.addToMapKnop.clicked.connect(self.onAddSelClicked)
        self.ui.addMinModelBtn.clicked.connect(self.addMinModel)
        self.ui.poiText.textChanged.connect(self.searchTxtChanged)
        self.ui.buttonBox.helpRequested.connect(self.openHelp)

        self.finished.connect(self.clean)

    def loadSettings(self):
        self.saveToFile = int(self.s.value("geopunt4qgis/poiSavetoFile", 1))
        layerName = self.s.value("geopunt4qgis/poilayerText", "")
        if layerName: self.layerName = layerName
        self.timeout = int(self.s.value("geopunt4qgis/timeout", 15))
        if int(self.s.value("geopunt4qgis/useProxy", 0)):
            self.proxy = self.s.value("geopunt4qgis/proxyHost", "")
            self.port = self.s.value("geopunt4qgis/proxyPort", "")
        else:
            self.proxy = ""
            self.port = ""
        self.startDir = self.s.value("geopunt4qgis/startDir",
                                     os.path.dirname(__file__))
        self.poi = geopunt.Poi(self.timeout, self.proxy, self.port)

    def show(self):
        QtGui.QDialog.show(self)
        self.setWindowModality(0)
        if self.firstShow:
            inet = geopunt.internet_on(proxyUrl=self.proxy,
                                       port=self.port,
                                       timeout=self.timeout)
            #filters
            if inet:
                self.poiThemes = dict(self.poi.listPoiThemes())
                poiThemes = [""] + self.poiThemes.keys()
                poiThemes.sort()
                self.ui.filterPoiThemeCombo.addItems(poiThemes)
                self.poiCategories = dict(self.poi.listPoiCategories())
                poiCategories = [""] + self.poiCategories.keys()
                poiCategories.sort()
                self.ui.filterPoiCategoryCombo.addItems(poiCategories)
                self.poiTypes = dict(self.poi.listPoitypes())
                poiTypes = [""] + self.poiTypes.keys()
                poiTypes.sort()
                self.ui.filterPoiTypeCombo.addItems(poiTypes)
                gemeentes = json.load(
                    open(
                        os.path.join(os.path.dirname(__file__),
                                     "data/gemeentenVL.json")))
                self.NIScodes = {n["Naam"]: n["Niscode"] for n in gemeentes}
                gemeenteNamen = [n["Naam"] for n in gemeentes]
                gemeenteNamen.sort()
                self.ui.filterPoiNIS.addItems(gemeenteNamen)
                #connect when inet on
                self.ui.filterPoiThemeCombo.activated.connect(
                    self.onThemeFilterChange)
                self.ui.filterPoiCategoryCombo.activated.connect(
                    self.onCategorieFilterChange)

                self.firstShow = False
            else:
                self.bar.pushMessage(
                    QtCore.QCoreApplication.translate("geopunt4QgisPoidialog",
                                                      "Waarschuwing "),
                    QtCore.QCoreApplication.translate(
                        "geopunt4QgisPoidialog",
                        "Kan geen verbing maken met het internet."),
                    level=QgsMessageBar.WARNING,
                    duration=3)

    def openHelp(self):
        webbrowser.open_new_tab(
            "http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/poi"
        )

    def onZoekActivated(self):
        txt = self.ui.poiText.text()
        self.ui.resultLijst.clearContents()
        self.ui.resultLijst.setRowCount(0)
        self.ui.msgLbl.setText("")

        ##filters:
        poithemeText = self.ui.filterPoiThemeCombo.currentText()
        if poithemeText != "": poitheme = self.poiThemes[poithemeText]
        else: poitheme = ""
        poiCategorieText = self.ui.filterPoiCategoryCombo.currentText()
        if poiCategorieText != "":
            poiCategorie = self.poiCategories[poiCategorieText]
        else:
            poiCategorie = ""
        poiTypeText = self.ui.filterPoiTypeCombo.currentText()
        if poiTypeText != "": poiType = self.poiTypes[poiTypeText]
        else: poiType = ""
        NISText = self.ui.filterPoiNIS.currentText()
        if NISText != "" and not self.ui.currentBoundsVink.isChecked():
            Niscode = self.NIScodes[NISText]
        else:
            Niscode = ""

        if self.ui.currentBoundsVink.isChecked():
            bbox = self.iface.mapCanvas().extent()
            minX, minY = self.gh.prjPtFromMapCrs(
                [bbox.xMinimum(), bbox.yMinimum()], 4326)
            maxX, maxY = self.gh.prjPtFromMapCrs(
                [bbox.xMaximum(), bbox.yMaximum()], 4326)
            xyBox = [minX, minY, maxX, maxY]
            self.poi.fetchPoi(txt,
                              c=32,
                              srs=4326,
                              maxModel=True,
                              updateResults=True,
                              bbox=xyBox,
                              theme=poitheme,
                              category=poiCategorie,
                              POItype=poiType)
        else:
            self.poi.fetchPoi(txt,
                              c=32,
                              srs=4326,
                              maxModel=True,
                              updateResults=True,
                              bbox=None,
                              theme=poitheme,
                              category=poiCategorie,
                              POItype=poiType,
                              region=Niscode)

        suggesties = self.poi.poiSuggestion()

        if type(suggesties) is list and len(suggesties) > 0:
            #prevent sorting every time an item is added
            self.ui.resultLijst.setSortingEnabled(False)
            row = 0
            for sug in suggesties:
                id = QtGui.QTableWidgetItem(sug[0], 0)
                theme = QtGui.QTableWidgetItem(sug[1], 0)
                categorie = QtGui.QTableWidgetItem(sug[2], 0)
                PoiType = QtGui.QTableWidgetItem(sug[3], 0)
                naam = QtGui.QTableWidgetItem(sug[4], 0)
                adres = QtGui.QTableWidgetItem(
                    sug[5].replace("<br />", ", ").replace("<br/>", ", "), 0)
                self.ui.resultLijst.insertRow(row)
                self.ui.resultLijst.setItem(row, 0, id)
                self.ui.resultLijst.setItem(row, 1, theme)
                self.ui.resultLijst.setItem(row, 2, categorie)
                self.ui.resultLijst.setItem(row, 3, PoiType)
                self.ui.resultLijst.setItem(row, 4, naam)
                self.ui.resultLijst.setItem(row, 5, adres)
                row += 1
            self.ui.resultLijst.setSortingEnabled(True)

            if txt == "":
                self.ui.msgLbl.setText(
                    QtCore.QCoreApplication.translate(
                        "geopunt4QgisPoidialog",
                        "Aantal getoond: %s gevonden: %s" %
                        (self.ui.resultLijst.rowCount(),
                         self.poi.resultCount)))
            else:
                self.ui.msgLbl.setText(
                    QtCore.QCoreApplication.translate(
                        "geopunt4QgisPoidialog", "Aantal getoond: %s" %
                        (self.ui.resultLijst.rowCount())))

        elif len(suggesties) == 0:
            self.bar.pushMessage(QtCore.QCoreApplication.translate(
                "geopunt4QgisPoidialog", "Geen resultaten gevonden"),
                                 txt,
                                 level=QgsMessageBar.INFO,
                                 duration=5)
        elif type(suggesties) is str:
            self.bar.pushMessage(QtCore.QCoreApplication.translate(
                "geopunt4QgisPoidialog", "Waarschuwing"),
                                 suggesties,
                                 level=QgsMessageBar.WARNING)
        else:
            self.bar.pushMessage("Error",
                                 QtCore.QCoreApplication.translate(
                                     "geopunt4QgisPoidialog",
                                     "onbekende fout"),
                                 level=QgsMessageBar.CRITICAL)

    def onZoomSelClicked(self):
        selPois = self._getSelectedPois()
        if len(selPois) <= 0:
            self.bar.pushMessage(QtCore.QCoreApplication.translate(
                "geopunt4QgisPoidialog", "Merk op"),
                                 QtCore.QCoreApplication.translate(
                                     "geopunt4QgisPoidialog",
                                     "Er niets om naar te zoomen"),
                                 level=QgsMessageBar.INFO,
                                 duration=3)
        elif len(selPois) >= 2:
            pts = [
                n['location']['points'][0]['Point']['coordinates']
                for n in selPois
            ]
            bounds = self.gh.getBoundsOfPointArray(pts)
            self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326)
        elif len(selPois) == 1:
            x, y = selPois[0]['location']['points'][0]['Point']['coordinates']
            bounds = self.gh.getBoundsOfPoint(x, y)
            self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326)

    def onSelectionChanged(self):
        selPois = self._getSelectedPois()
        canvas = self.iface.mapCanvas()
        self.clearGraphicsLayer()

        pts = [
            self.gh.prjPtToMapCrs(
                n['location']['points'][0]['Point']['coordinates'], 4326)
            for n in selPois
        ]
        for pt in pts:
            x, y = pt
            m = QgsVertexMarker(canvas)
            self.graphicsLayer.append(m)
            m.setCenter(QgsPoint(x, y))
            m.setColor(QtGui.QColor(255, 255, 0))
            m.setIconSize(1)
            m.setIconType(QgsVertexMarker.ICON_BOX)
            m.setPenWidth(10)

    def onAddSelClicked(self):
        if not self.layernameValid(): return
        self.clearGraphicsLayer()
        pts = self._getSelectedPois()
        self.ph.save_pois_points(pts,
                                 layername=self.layerName,
                                 startFolder=os.path.join(
                                     self.startDir, self.layerName),
                                 saveToFile=self.saveToFile,
                                 sender=self)

    def onThemeFilterChange(self):
        poithemeText = self.ui.filterPoiThemeCombo.currentText()

        if poithemeText != "":
            poithemeID = self.poiThemes[poithemeText]
            poiCategories = [""] + [
                n[0] for n in self.poi.listPoiCategories(poithemeID)
            ]
            poiCategories.sort()
            self.ui.filterPoiCategoryCombo.clear()
            self.ui.filterPoiTypeCombo.clear()
            self.ui.filterPoiCategoryCombo.addItems(poiCategories)
        else:
            self.ui.filterPoiCategoryCombo.addItems([""] +
                                                    self.poiCategories.keys())
            self.ui.filterPoiTypeCombo.addItems([""] + self.poiTypes.keys())

    def onCategorieFilterChange(self):
        poithemeText = self.ui.filterPoiThemeCombo.currentText()
        poiCategorieText = self.ui.filterPoiCategoryCombo.currentText()

        if poiCategorieText != "" and poithemeText != "":
            poiCategorieID = self.poiCategories[poiCategorieText]
            poithemeID = self.poiThemes[poithemeText]
            poiTypes = [""] + [
                n[0] for n in self.poi.listPoitypes(poithemeID, poiCategorieID)
            ]
            poiTypes.sort()
            self.ui.filterPoiTypeCombo.clear()
            self.ui.filterPoiTypeCombo.addItems(poiTypes)

    def addMinModel(self):
        if not self.layernameValid(): return
        self.clearGraphicsLayer()
        txt = self.ui.poiText.text()

        poithemeText = self.ui.filterPoiThemeCombo.currentText()
        if poithemeText != "": poitheme = self.poiThemes[poithemeText]
        else: poitheme = ""
        poiCategorieText = self.ui.filterPoiCategoryCombo.currentText()
        if poiCategorieText != "":
            poiCategorie = self.poiCategories[poiCategorieText]
        else:
            poiCategorie = ""
        poiTypeText = self.ui.filterPoiTypeCombo.currentText()
        if poiTypeText != "": poiType = self.poiTypes[poiTypeText]
        else: poiType = ""
        NISText = self.ui.filterPoiNIS.currentText()
        if NISText != "" and not self.ui.currentBoundsVink.isChecked():
            Niscode = self.NIScodes[NISText]
        else:
            Niscode = ""

        if self.ui.currentBoundsVink.isChecked():
            bbox = self.iface.mapCanvas().extent()
            minX, minY = self.gh.prjPtFromMapCrs(
                [bbox.xMinimum(), bbox.yMinimum()], 4326)
            maxX, maxY = self.gh.prjPtFromMapCrs(
                [bbox.xMaximum(), bbox.yMaximum()], 4326)
            xyBox = [minX, minY, maxX, maxY]
            pts = self.poi.fetchPoi(txt,
                                    c=1024,
                                    srs=4326,
                                    maxModel=0,
                                    updateResults=0,
                                    bbox=xyBox,
                                    theme=poitheme,
                                    category=poiCategorie,
                                    POItype=poiType)
        else:
            pts = self.poi.fetchPoi(txt,
                                    c=1024,
                                    srs=4326,
                                    maxModel=0,
                                    updateResults=0,
                                    bbox=None,
                                    theme=poitheme,
                                    category=poiCategorie,
                                    POItype=poiType,
                                    region=Niscode)

        if type(pts) == str:
            self.bar.pushMessage(QtCore.QCoreApplication.translate(
                "geopunt4QgisPoidialog", "Waarschuwing"),
                                 pts,
                                 level=QgsMessageBar.WARNING,
                                 duration=5)
        elif type(pts) == list or type(pts) == dict:
            self.ph.save_minPois_points(pts,
                                        layername=self.layerName,
                                        startFolder=os.path.join(
                                            self.startDir, self.layerName),
                                        saveToFile=self.saveToFile,
                                        sender=self)
            self.close()

    def searchTxtChanged(self):
        txt = self.ui.poiText.text()
        if txt != "":
            msg = QtCore.QCoreApplication.translate("geopunt4QgisPoidialog",
                                                    "Voeg meer punten toe")
        else:
            msg = QtCore.QCoreApplication.translate("geopunt4QgisPoidialog",
                                                    "Voeg alle punten toe")
        self.ui.addMinModelBtn.setText(msg)

    def _getSelectedPois(self):
        pois = self.poi.PoiResult
        selPois = []
        selRows = set(
            [sel.row() for sel in self.ui.resultLijst.selectedIndexes()])
        for row in selRows:
            itemID = self.ui.resultLijst.item(row, 0).text()
            selPois += [i for i in pois if i["id"] == itemID]
        return selPois

    def clearGraphicsLayer(self):
        for graphic in self.graphicsLayer:
            self.iface.mapCanvas().scene().removeItem(graphic)
        self.graphicsLayer = []

    def layernameValid(self):
        if not hasattr(self, 'layerName'):
            layerName, accept = QtGui.QInputDialog.getText(
                None,
                QtCore.QCoreApplication.translate("geopunt4Qgis",
                                                  'Laag toevoegen'),
                QtCore.QCoreApplication.translate(
                    "geopunt4Qgis", 'Geef een naam voor de laag op:'))
            if accept == False:
                return False
            else:
                self.layerName = layerName
        return True

    def clean(self):
        self.bar.clearWidgets()
        self.ui.poiText.setText("")
        self.ui.msgLbl.setText("")
        self.ui.resultLijst.clearContents()
        self.ui.resultLijst.setRowCount(0)
        self.clearGraphicsLayer()
class tTestAnalysisDialog(QDialog, FORM_CLASS):
    """Extract statistics from a list of rasters at set locations."""
    toolKey = 'tTestAnalysisDialog'

    def __init__(self, iface, parent=None):

        super(tTestAnalysisDialog, self).__init__(parent)

        # Set up the user interface from Designer.
        self.setupUi(self)

        self.iface = iface
        self.DISP_TEMP_LAYERS = read_setting(PLUGIN_NAME + '/DISP_TEMP_LAYERS',
                                             bool)
        self.DEBUG = config.get_debug_mode()

        # Catch and redirect python errors directed at the log messages python error tab.
        QgsApplication.messageLog().messageReceived.connect(errorCatcher)

        if not os.path.exists(TEMPDIR):
            os.mkdir(TEMPDIR)

        # Setup for validation messagebar on gui-----------------------------
        self.messageBar = QgsMessageBar(
            self)  # leave this message bar for bailouts
        self.validationLayout = QtWidgets.QFormLayout(
            self)  # new layout to gui

        self.raster_filter_message = self.lblLayerFilter.text()
        self.pixel_size = ['0', 'm', '']
        self.layer_table = build_layer_table()

        if isinstance(self.layout(),
                      (QtWidgets.QFormLayout, QtWidgets.QGridLayout)):
            # create a validation layout so multiple messages can be added and cleaned up.
            self.layout().insertRow(0, self.validationLayout)
            self.layout().insertRow(0, self.messageBar)
        else:
            # for use with Vertical/horizontal layout box
            self.layout().insertWidget(0, self.messageBar)

        for cbo in [
                self.mcboRasterLayer, self.mcboCtrlRasterLayer,
                self.mcboZoneRasterLyr
        ]:
            cbo.setFilters(QgsMapLayerProxyModel.RasterLayer)
            cbo.setExcludedProviders(['wms'])
            cbo.setAllowEmptyLayer(True)
            cbo.setCurrentIndex(0)

        self.mcboPointsLayer.setFilters(QgsMapLayerProxyModel.PointLayer)
        self.mcboPointsLayer.setExcludedProviders(['wms'])
        self.mcboPointsLayer.setAllowEmptyLayer(True)
        self.mcboPointsLayer.setCurrentIndex(0)

        self.setMapLayers()
        self.updateUseSelected()

        # GUI Runtime Customisation -----------------------------------------------
        self.setWindowIcon(QtGui.QIcon(':/plugins/pat/icons/icon_t-test.svg'))

    def cleanMessageBars(self, AllBars=True):
        """Clean Messages from the validation layout.
        Args:
            AllBars (bool): Remove All bars including those which haven't timed-out. Default - True
        """
        layout = self.validationLayout
        for i in reversed(list(range(layout.count()))):
            # when it timed out the row becomes empty....
            if layout.itemAt(i).isEmpty():
                # .removeItem doesn't always work. so takeAt(pop) it instead
                item = layout.takeAt(i)
            elif AllBars:  # ie remove all
                item = layout.takeAt(i)
                # also have to remove any widgets associated with it.
                if item.widget() is not None:
                    item.widget().deleteLater()

    def send_to_messagebar(self,
                           message,
                           title='',
                           level=Qgis.Info,
                           duration=5,
                           exc_info=None,
                           core_QGIS=False,
                           addToLog=False,
                           showLogPanel=False):
        """ Add a message to the forms message bar.

        Args:
            message (str): Message to display
            title (str): Title of message. Will appear in bold. Defaults to ''
            level (QgsMessageBarLevel): The level of message to log. Defaults to Qgis.Info
            duration (int): Number of seconds to display message for. 0 is no timeout. Defaults to 5
            core_QGIS (bool): Add to QGIS interface rather than the dialog
            addToLog (bool): Also add message to Log. Defaults to False
            showLogPanel (bool): Display the log panel
            exc_info () : Information to be used as a traceback if required

        """

        if core_QGIS:
            newMessageBar = self.iface.messageBar()
        else:
            newMessageBar = QgsMessageBar(self)

        widget = newMessageBar.createMessage(title, message)

        if showLogPanel:
            """check out C:\data\GIS_Tools\Reference_Code\QGIS_Reference_Plugins\QGIS-master\python\plugins\db_manager\db_tree.py
            to try and hyperlink"""

            button = QPushButton(widget)
            button.setText('View')
            button.setContentsMargins(0, 0, 0, 0)
            button.setFixedWidth(35)
            button.pressed.connect(openLogPanel)
            widget.layout().addWidget(button)

        newMessageBar.pushWidget(widget, level, duration=duration)

        if not core_QGIS:
            rowCount = self.validationLayout.count()
            self.validationLayout.insertRow(rowCount + 1, newMessageBar)

        if addToLog:
            if level == 1:  # 'WARNING':
                LOGGER.warning(message)
            elif level == 2:  # 'CRITICAL':
                # Add a traceback to log only for bailouts only
                if exc_info is not None:
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    mess = str(traceback.format_exc())
                    message = message + '\n' + mess

                LOGGER.critical(message)
            else:  # INFO = 0
                LOGGER.info(message)

    def setMapLayers(self):
        """ Run through all loaded layers to find ones which should be excluded.
            In this case exclude services."""

        if len(self.layer_table) == 0:
            return

        cbo_list = [
            self.mcboRasterLayer, self.mcboCtrlRasterLayer,
            self.mcboZoneRasterLyr
        ]

        if self.mcboPointsLayer.currentLayer() is None:
            for cbo in cbo_list:
                cbo.setExceptedLayerList([])
            return

        df_pts = self.layer_table[self.layer_table['layer_id'] ==
                                  self.mcboPointsLayer.currentLayer().id()]

        used_layers = [
            cbo.currentLayer().id() for cbo in cbo_list
            if cbo.currentLayer() is not None
        ]

        df_rastlyrs = self.layer_table[
            (self.layer_table['provider'] == 'gdal')
            & (self.layer_table['layer_type'] == 'RasterLayer')]

        if self.chkUseSelected.isChecked():
            layer = self.mcboPointsLayer.currentLayer()

            transform = QgsCoordinateTransform(
                QgsCoordinateReferenceSystem(df_pts['epsg'].values[0]),
                QgsProject.instance().crs(), QgsProject.instance())

            prj_ext = transform.transformBoundingBox(
                layer.boundingBoxOfSelected())
            df_pts['geometry'] = wkt.loads(prj_ext.asWktPolygon())

            # recreate the geodataframe or unary_union wont work
            df_pts = gpd.GeoDataFrame(df_pts,
                                      geometry='geometry',
                                      crs=df_pts.crs)

        if self.pixel_size[0] == '0':
            # Find layers that overlap.
            if len(df_pts) > 0:
                df_keep = df_rastlyrs[df_rastlyrs.intersects(
                    df_pts.unary_union)]

        else:
            # Find layers that overlap and have the same pixel size.
            if len(df_pts) > 0:
                df_keep = df_rastlyrs[
                    (df_rastlyrs['pixel_size'] == self.pixel_size[0])
                    & (df_rastlyrs.intersects(df_pts.unary_union))]

        # process for each raster layer cbo
        for cbo in cbo_list:
            df_cbo = df_keep.copy()

            if cbo.currentLayer() is None:
                loop_used_layers = used_layers
            else:
                loop_used_layers = [
                    ea for ea in used_layers if cbo.currentLayer().id() != ea
                ]

            # add it back the current one.
            df_cbo = df_cbo[~df_cbo['layer_id'].isin(loop_used_layers)]

            # find those we don't want to keep;
            df_remove = pd.concat([df_rastlyrs,
                                   df_cbo]).drop_duplicates(keep=False)

            cbo.setExceptedLayerList(df_remove['layer'].tolist())

    def updateUseSelected(self):
        """Update use selected checkbox if active layer has a feature selection"""

        self.chkUseSelected.setChecked(False)

        if self.mcboPointsLayer.count(
        ) == 0 or self.mcboPointsLayer.currentLayer() is None:
            return

        point_lyr = self.mcboPointsLayer.currentLayer()
        if point_lyr.selectedFeatureCount() > 0:
            self.chkUseSelected.setText(
                'Use the {} selected feature(s) ?'.format(
                    point_lyr.selectedFeatureCount()))
            self.chkUseSelected.setEnabled(True)
        else:
            self.chkUseSelected.setText('No features selected')
            self.chkUseSelected.setEnabled(False)

    def on_mcboRasterLayer_layerChanged(self):
        if self.mcboRasterLayer.currentLayer() is None:
            self.pixel_size = ['0', 'm', '']
            self.lblLayerFilter.setText(self.raster_filter_message)
            return

        else:
            self.pixel_size = get_pixel_size(
                self.mcboRasterLayer.currentLayer())
            self.lblLayerFilter.setText(
                'Raster lists below have been <b>filtered</b> to only show rasters with <br/>'
                + '- a pixelsize of <b>{} {}</b><br/>'.format(
                    self.pixel_size[0], self.pixel_size[1]) +
                '- and <b>overlaps</b> with the points layer')

        self.mcboCtrlRasterLayer.setEnabled(
            self.mcboCtrlRasterLayer.count() > 1)
        self.mcboZoneRasterLyr.setEnabled(self.mcboCtrlRasterLayer.count() > 1)

        self.setMapLayers()

    def on_chkUseSelected_stateChanged(self, state):
        self.setMapLayers()

    def on_mcboCtrlRasterLayer_layerChanged(self):
        self.setMapLayers()

    def on_mcboZoneRasterLyr_layerChanged(self):
        self.setMapLayers()

    def on_mcboPointsLayer_layerChanged(self):
        self.updateUseSelected()
        self.setMapLayers()

    @QtCore.pyqtSlot(name='on_cmdOutputFolder_clicked')
    def on_cmdOutputFolder_clicked(self):
        self.messageBar.clearWidgets()
        if self.lneOutputFolder.text() is None:
            outFolder = ''
        else:
            outFolder = self.lneOutputFolder.text()

        if outFolder == '':
            outFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey +
                                     "/LastOutFolder")
            if outFolder is None or not os.path.exists(outFolder):
                outFolder = read_setting(PLUGIN_NAME + '/BASE_OUT_FOLDER')

        s = QFileDialog.getExistingDirectory(
            self,
            self.
            tr("Save output files to a folder. A sub-folder will be created from the image name"
               ), outFolder, QFileDialog.ShowDirsOnly)

        self.cleanMessageBars(self)
        if s == '' or s is None:
            return

        s = os.path.normpath(s)

        self.lblOutputFolder.setStyleSheet('color:black')
        self.lneOutputFolder.setStyleSheet('color:black')
        self.lneOutputFolder.setText(s)
        write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastOutFolder", s)

    def validate(self):
        """Check to see that all required gui elements have been entered and are valid."""
        self.messageBar.clearWidgets()
        self.cleanMessageBars(AllBars=True)
        try:
            errorList = []

            if self.mcboPointsLayer.currentLayer() is None:
                self.lblPointsLayer.setStyleSheet('color:red')
                errorList.append(self.tr("Input points layer required."))
            else:
                self.lblPointsLayer.setStyleSheet('color:black')

            if self.mcboRasterLayer.currentLayer() is None:
                self.lblRasterLayer.setStyleSheet('color:red')
                errorList.append(
                    self.tr("Input strips raster layer required."))
            else:
                self.lblRasterLayer.setStyleSheet('color:black')

            if self.dsbMovingWinSize.value() <= 0:
                self.lblMovingWinSize.setStyleSheet('color:red')
                errorList.append(self.tr("Pixel size must be greater than 0."))
            else:
                self.lblMovingWinSize.setStyleSheet('color:black')

            if self.lneOutputFolder.text() == '':
                self.lblOutputFolder.setStyleSheet('color:red')
                errorList.append(self.tr("Select output data folder"))
            elif not os.path.exists(self.lneOutputFolder.text()):
                self.lneOutputFolder.setStyleSheet('color:red')
                self.lblOutputFolder.setStyleSheet('color:red')
                errorList.append(self.tr("Output data folder does not exist"))
            else:
                self.lblOutputFolder.setStyleSheet('color:black')
                self.lneOutputFolder.setStyleSheet('color:black')

            if len(errorList) > 0:
                raise ValueError(errorList)

        except ValueError as e:
            self.cleanMessageBars(True)
            if len(errorList) > 0:
                for i, ea in enumerate(errorList):
                    self.send_to_messagebar(str(ea),
                                            level=Qgis.Warning,
                                            duration=(i + 1) * 5)
                return False

        return True

    def accept(self, *args, **kwargs):
        try:

            if not self.validate():
                return False

            # disable form via a frame, this will still allow interaction with the message bar
            # self.fraMain.setDisabled(True)

            # clean gui and Qgis messagebars
            self.cleanMessageBars(True)

            # Change cursor to Wait cursor
            QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
            self.iface.mainWindow().statusBar().showMessage(
                'Processing {}'.format(self.windowTitle()))

            LOGGER.info('{st}\nProcessing {}'.format(self.windowTitle(),
                                                     st='*' * 50))

            self.send_to_messagebar(
                "Please wait. QGIS will be locked. See log panel for progress.",
                level=Qgis.Warning,
                duration=0,
                addToLog=False,
                core_QGIS=False,
                showLogPanel=True)

            # Add settings to log
            settingsStr = 'Parameters:---------------------------------------'

            if self.chkUseSelected.isChecked():
                settingsStr += '\n    {:20}\t{} with {} selected features'.format(
                    'Strip points layer:',
                    self.mcboPointsLayer.currentLayer().name(),
                    self.mcboPointsLayer.currentLayer().selectedFeatureCount())
            else:
                settingsStr += '\n    {:20}\t{}'.format(
                    'Strip points layer:',
                    self.mcboPointsLayer.currentLayer().name())

            settingsStr += '\n    {:20}\t{}'.format(
                'Strip values raster:',
                self.mcboRasterLayer.currentLayer().name())

            control_file, zone_file = ['', '']
            if self.mcboCtrlRasterLayer.currentLayer() is not None:
                settingsStr += '\n    {:20}\t{}'.format(
                    'Control values raster:',
                    self.mcboCtrlRasterLayer.currentLayer().name())
                control_file = get_layer_source(
                    self.mcboCtrlRasterLayer.currentLayer())

            if self.mcboZoneRasterLyr.currentLayer() is not None:
                settingsStr += '\n    {:20}\t{}'.format(
                    'Control values raster:',
                    self.mcboZoneRasterLyr.currentLayer().name())
                zone_file = get_layer_source(
                    self.mcboZoneRasterLyr.currentLayer())

            settingsStr += '\n    {:20}\t{}'.format(
                'Moving window size: ', self.dsbMovingWinSize.value())
            settingsStr += '\n    {:30}\t{}\n'.format(
                'Output folder:', self.lneOutputFolder.text())

            LOGGER.info(settingsStr)

            lyrPoints = self.mcboPointsLayer.currentLayer()

            if self.chkUseSelected.isChecked() or lyrPoints.providerType(
            ) == 'delimitedtext':
                savePtsName = lyrPoints.name() + '_strippts.shp'
                fileStripPts = os.path.join(TEMPDIR, savePtsName)

                if os.path.exists(fileStripPts):
                    removeFileFromQGIS(fileStripPts)

                QgsVectorFileWriter.writeAsVectorFormat(
                    lyrPoints,
                    fileStripPts,
                    "utf-8",
                    lyrPoints.crs(),
                    driverName="ESRI Shapefile",
                    onlySelected=self.chkUseSelected.isChecked())

                if self.DISP_TEMP_LAYERS:
                    addVectorFileToQGIS(fileStripPts,
                                        layer_name=os.path.splitext(
                                            os.path.basename(fileStripPts))[0],
                                        group_layer_name='DEBUG',
                                        atTop=True)
            else:
                fileStripPts = get_layer_source(lyrPoints)

            points_desc = describe.VectorDescribe(fileStripPts)
            gdf_pts = points_desc.open_geo_dataframe()

            df_table = ttest_analysis(gdf_pts,
                                      points_desc.crs,
                                      get_layer_source(
                                          self.mcboRasterLayer.currentLayer()),
                                      self.lneOutputFolder.text(),
                                      zone_file,
                                      control_file,
                                      size=self.dsbMovingWinSize.value())

            self.cleanMessageBars(True)
            self.fraMain.setDisabled(False)

            self.iface.mainWindow().statusBar().clearMessage()
            self.iface.messageBar().popWidget()

            QApplication.restoreOverrideCursor()
            return super(tTestAnalysisDialog, self).accept(*args, **kwargs)

        except Exception as err:

            QApplication.restoreOverrideCursor()
            self.iface.mainWindow().statusBar().clearMessage()
            self.cleanMessageBars(True)
            self.fraMain.setDisabled(False)

            self.send_to_messagebar(str(err),
                                    level=Qgis.Critical,
                                    duration=0,
                                    addToLog=True,
                                    core_QGIS=False,
                                    showLogPanel=True,
                                    exc_info=sys.exc_info())

            return False  # leave dialog open
Exemple #13
0
class PreVesperDialog(QtGui.QDialog, FORM_CLASS):
    """Dialog to prepare data and run vesper kriging"""
    toolKey = 'PreVesperDialog'

    def __init__(self, iface, parent=None):

        super(PreVesperDialog, self).__init__(iface.mainWindow())

        # Set up the user interface from Designer.
        self.setupUi(self)

        # The qgis interface
        self.iface = iface
        self.DISP_TEMP_LAYERS = read_setting(
            PLUGIN_NAME + '/DISP_TEMP_LAYERS', bool)
        self.DEBUG = config.get_debug_mode()

        # Catch and redirect python errors directed at the log messages python
        # error tab.
        QgsMessageLog.instance().messageReceived.connect(errorCatcher)

        if not os.path.exists(TEMPDIR):
            os.mkdir(TEMPDIR)

        # Setup for validation messagebar on gui-----------------------------
        self.setWindowIcon(QtGui.QIcon(
            ':/plugins/pat/icons/icon_vesperKriging.svg'))

        self.validationLayout = QtGui.QFormLayout(self)
        # source: https://nathanw.net/2013/08/02/death-to-the-message-box-use-the-qgis-messagebar/
        # Add the error messages to top of form via a message bar.
        # leave this message bar for bailouts
        self.messageBar = QgsMessageBar(self)

        if isinstance(self.layout(), (QtGui.QFormLayout, QtGui.QGridLayout)):
            # create a validation layout so multiple messages can be added and
            # cleaned up.
            self.layout().insertRow(0, self.validationLayout)
            self.layout().insertRow(0, self.messageBar)
        else:
            # for use with Vertical/horizontal layout box
            self.layout().insertWidget(0, self.messageBar)

        # Set Class default variables -------------------------------------
        self.vesp_dict = None
        self.in_qgscrs = None
        self.dfCSV = None

        # this is a validation flag
        self.OverwriteCtrlFile = False
        self.cboMethod.addItems(
            ['High Density Kriging', 'Low Density Kriging (Advanced)'])

        # To allow only integers for the min number of pts.
        self.onlyInt = QIntValidator()
        self.lneMinPoint.setValidator(self.onlyInt)

        self.vesper_exe = check_vesper_dependency()
        if self.vesper_exe is None or self.vesper_exe == '':
            self.gbRunVesper.setTitle(
                'WARNING:Vesper not found please configure using the about dialog.')
            self.gbRunVesper.setChecked(False)
            self.gbRunVesper.setCheckable(False)
            self.gbRunVesper.setEnabled(False)

    def cleanMessageBars(self, AllBars=True):
        """Clean Messages from the validation layout.
        Args:
            AllBars (bool): Remove All bars including. Defaults to True
        """
        layout = self.validationLayout
        for i in reversed(range(layout.count())):
            # when it timed out the row becomes empty....
            if layout.itemAt(i).isEmpty():
                # .removeItem doesn't always work. so takeAt(pop) it instead
                item = layout.takeAt(i)
            elif AllBars:  # ie remove all
                item = layout.takeAt(i)
                # also have to remove any widgets associated with it.
                if item.widget() is not None:
                    item.widget().deleteLater()

    def send_to_messagebar(self, message, title='', level=QgsMessageBar.INFO, duration=5,
                           exc_info=None, core_QGIS=False, addToLog=False, showLogPanel=False):
        """ Add a message to the forms message bar.

        Args:
            message (str): Message to display
            title (str): Title of message. Will appear in bold. Defaults to ''
            level (QgsMessageBarLevel): The level of message to log. Defaults to QgsMessageBar.INFO
            duration (int): Number of seconds to display message for. 0 is no timeout. Defaults to 5
            core_QGIS (bool): Add to QGIS interface rather than the dialog
            addToLog (bool): Also add message to Log. Defaults to False
            showLogPanel (bool): Display the log panel
            exc_info () : Information to be used as a traceback if required

        """

        if core_QGIS:
            newMessageBar = self.iface.messageBar()
        else:
            newMessageBar = QgsMessageBar(self)

        widget = newMessageBar.createMessage(title, message)

        if showLogPanel:
            button = QPushButton(widget)
            button.setText('View')
            button.setContentsMargins(0, 0, 0, 0)
            button.setFixedWidth(35)
            button.pressed.connect(openLogPanel)
            widget.layout().addWidget(button)

        newMessageBar.pushWidget(widget, level, duration=duration)

        if not core_QGIS:
            rowCount = self.validationLayout.count()
            self.validationLayout.insertRow(rowCount + 1, newMessageBar)

        if addToLog:
            if level == 1:  # 'WARNING':
                LOGGER.warning(message)
            elif level == 2:  # 'CRITICAL':
                # Add a traceback to log only for bailouts only
                if exc_info is not None:
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    mess = str(traceback.format_exc())
                    message = message + '\n' + mess

                LOGGER.critical(message)
            else:  # INFO = 0
                LOGGER.info(message)

    def updateCtrlFileName(self):
        if self.chkAutoCtrlFileName.isChecked():
            ctrl_name = os.path.splitext(
                os.path.basename(self.lneInCSVFile.text()))[0]
            ctrl_name = ctrl_name.replace('_normtrimmed', '')
            fld = ''

            if self.cboKrigColumn.currentText() != '':
                # convert field name to something meaningful if it contains
                # invalid chars, ie degC
                fld = unidecode(self.cboKrigColumn.currentText())

                # remove field from filename, then add it according to the naming
                #  convention to avoid duplications.
                # flags=re.I is for a case insensitive find and replace
                ctrl_name = re.sub(fld, '', ctrl_name, flags=re.I)

                # and again with invalid characters removed. Only allow
                # alpha-numeric Underscores and hyphens
                fld = re.sub('[^A-Za-z0-9_-]+', '', fld)
                ctrl_name = re.sub(fld, '', ctrl_name, flags=re.I)

                # and again with the field truncated to 10 chars
                fld = fld[:10]
                ctrl_name = re.sub(fld, '', ctrl_name, flags=re.I)

            if self.cboMethod.currentText() == 'High Density Kriging':
                krig_type = 'HighDensity'
            else:
                krig_type = 'LowDensity'

            # add the chosen field name to the control filename
            ctrl_name = '{}_{}_{}_control'.format(ctrl_name[:20], krig_type, fld)

            # only allow alpha-numeric Underscores and hyphens
            ctrl_name = re.sub('[^A-Za-z0-9_-]+', '', ctrl_name)

            # replace more than one instance of underscore with a single one.
            # ie'file____norm__control___yield_h__' to
            # 'file_norm_control_yield_h_'
            ctrl_name = re.sub(r"_+", "_", ctrl_name)
            self.lneCtrlFile.setText(ctrl_name + '.txt')

    @QtCore.pyqtSlot(int)
    def on_cboKrigColumn_currentIndexChanged(self, index):
        if self.cboKrigColumn.currentText() != '':
            self.lblKrigColumn.setStyleSheet('color:black')
        if self.chkAutoCtrlFileName.isChecked():
            self.updateCtrlFileName()

    @QtCore.pyqtSlot(name='on_cmdInCSVFile_clicked')
    def on_cmdInCSVFile_clicked(self):
        self.lneInCSVFile.clear()
        self.messageBar.clearWidgets()
        self.cboMethod.setCurrentIndex(0)
        self.dfCSV = None

        inFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastCSVFolder")
        if inFolder is None or not os.path.exists(inFolder):
            inFolder = read_setting(PLUGIN_NAME + '/BASE_IN_FOLDER')

        s = QtGui.QFileDialog.getOpenFileName(self,
                                              caption=self.tr("Select a CSV file to krige"),
                                              directory=inFolder,
                                              filter='{}  (*.csv);;{}  (*.*);;'.format(
                                                  self.tr("Comma delimited files"),
                                                  self.tr("All Files"))
                                              )

        self.cleanMessageBars(self)
        self.lneInCSVFile.clear()

        # validate files first
        overlaps, message = self.validate_csv_grid_files(s, self.lneInGridFile.text())

        if not overlaps or message is not None:
            self.lblInCSVFile.setStyleSheet('color:red')
            self.lneInCSVFile.setStyleSheet('color:red')

            self.send_to_messagebar(message,
                                    level=QgsMessageBar.CRITICAL,
                                    duration=0, addToLog=True, showLogPanel=True,
                                    exc_info=sys.exc_info())
            return

        s = os.path.normpath(s)
        self.lblInCSVFile.setStyleSheet('color:black')
        self.lneInCSVFile.setStyleSheet('color:black')
        self.lneInCSVFile.setText(s)

        descCSV = describe.CsvDescribe(s)
        self.dfCSV = descCSV.open_pandas_dataframe(nrows=150)

        if len(self.dfCSV) <= 100:
            self.lneMinPoint.clear()
            QMessageBox.warning(self, 'Cannot Krige', 'Kriging is not advised for less '
                                                      'than 100 points')

        self.lneMinPoint.clear()
        self.cboKrigColumn.clear()

        coord_cols = predictCoordinateColumnNames(self.dfCSV.columns)
        epsgcols = [col for col in self.dfCSV.columns if 'EPSG' in col.upper()]

        field_names = list(self.dfCSV.drop(coord_cols + epsgcols, axis=1)
                           .select_dtypes(include=[np.number]).columns.values)

        self.cboKrigColumn.addItems([''] + field_names)

        # To set a coordinate system for vesper2raster, try and get it from
        # a column in the data.
        epsg = 0
        if len(epsgcols) > 0:
            for col in epsgcols:
                if self.dfCSV.iloc[0][col] > 0:
                    epsg = int(self.dfCSV.iloc[0][col])
                    break

        if epsg > 0:
            self.in_qgscrs = QgsCoordinateReferenceSystem("EPSG:{}".format(epsg))
            self.lblInCRS.setText('{}  -  {}'.format(self.in_qgscrs.description(),
                                                     self.in_qgscrs.authid()))
        else:
            self.lblInCRS.setText('Unspecified')

        write_setting(PLUGIN_NAME + "/" + self.toolKey +
                      "/LastCSVFolder", os.path.dirname(s))
        del descCSV
        self.updateCtrlFileName()

    @QtCore.pyqtSlot(name='on_cmdInGridFile_clicked')
    def on_cmdInGridFile_clicked(self):
        self.lneInGridFile.clear()
        self.messageBar.clearWidgets()

        inFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastVesperGridFolder")

        if inFolder is None or not os.path.exists(inFolder):

            inFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastCSVFolder")

            if inFolder is None or not os.path.exists(inFolder):
                inFolder = read_setting(PLUGIN_NAME + '/BASE_IN_FOLDER')

        s = QtGui.QFileDialog.getOpenFileName(self, caption=self.tr("Choose the Vesper Grid File"),
                                              directory=inFolder,
                                              filter='{}  (*_v.txt);;{}  (*.*);;'.format(
                                                  self.tr("Vesper Grid File(s)"),
                                                  self.tr("All Files"))
                                              )

        self.cleanMessageBars(self)
        s = os.path.normpath(s)

        self.lneInGridFile.clear()
        self.lneInGridFile.setStyleSheet('color:red')
        self.lblInGridFile.setStyleSheet('color:red')

        overlaps, message = self.validate_csv_grid_files(self.lneInCSVFile.text(), s)

        if overlaps or message is None:
            self.lneInGridFile.setStyleSheet('color:black')
            self.lblInGridFile.setStyleSheet('color:black')
            if overlaps:
                self.lneInGridFile.setText(s)
                write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastVesperGridFolder",
                              os.path.dirname(s))
        else:
            self.send_to_messagebar(message,
                                    level=QgsMessageBar.CRITICAL,
                                    duration=0, addToLog=True, showLogPanel=True,
                                    exc_info=sys.exc_info())

    @QtCore.pyqtSlot(name='on_cmdVariogramFile_clicked')
    def on_cmdVariogramFile_clicked(self):
        self.lneVariogramFile.clear()
        self.messageBar.clearWidgets()

        inFolder = read_setting(
            PLUGIN_NAME + "/" + self.toolKey + "/LastVariogramFolder")
        if inFolder is None or not os.path.exists(inFolder):
            inFolder = read_setting(
                PLUGIN_NAME + "/" + self.toolKey + "/LastVariogramFolder")
            if inFolder is None or not os.path.exists(inFolder):
                inFolder = read_setting(PLUGIN_NAME + '/BASE_IN_FOLDER')

        s = QtGui.QFileDialog.getOpenFileName(self,
                                              caption=self.tr("Choose the Vesper Variogram File"),
                                              directory=inFolder,
                                              filter='{}  (*.txt);;{}  (*.*);;'.format(
                                                  self.tr("Variogram Text File(s)"),
                                                  self.tr("All Files"))
                                              )

        self.cleanMessageBars(self)
        if s == '':
            self.lneVariogramFile.setStyleSheet('color:red')
            self.lblVariogramFile.setStyleSheet('color:red')
            return

        if 'Variogram Model' not in open(s).read():
            self.lneVariogramFile.setStyleSheet('color:red')
            self.lblVariogramFile.setStyleSheet('color:red')
            self.send_to_messagebar("Invalid Variogram File", level=QgsMessageBar.CRITICAL,
                                    duration=0, addToLog=True, showLogPanel=True,
                                    exc_info=sys.exc_info())
            # self.lneVariogramFile.clear()
            return

        s = os.path.normpath(s)
        self.lblVariogramFile.setStyleSheet('color:black')
        self.lneVariogramFile.setStyleSheet('color:black')
        self.lneVariogramFile.setText(s)
        write_setting(PLUGIN_NAME + "/" + self.toolKey +
                      "/LastVariogramFolder", os.path.dirname(s))

    @QtCore.pyqtSlot(name='on_cmdVesperFold_clicked')
    def on_cmdVesperFold_clicked(self):
        self.messageBar.clearWidgets()

        if self.lneVesperFold.text() is None:
            outFolder = ''
        else:
            outFolder = self.lneVesperFold.text()

        if outFolder == '':
            outFolder = read_setting(
                PLUGIN_NAME + "/" + self.toolKey + "/LastVesperOutFolder")
            if outFolder is None or not os.path.exists(outFolder):
                outFolder = read_setting(PLUGIN_NAME + '/BASE_OUT_FOLDER')

        s = QtGui.QFileDialog.getExistingDirectory(self, self.tr(
            "Vesper processing folder. A Vesper sub-folder will be created."), outFolder,
                                                   QtGui.QFileDialog.ShowDirsOnly)

        self.cleanMessageBars(self)
        if s == '' or s is None:
            return

        s = os.path.normpath(s)
        self.lblVesperFold.setStyleSheet('color:black')
        self.lneVesperFold.setStyleSheet('color:black')
        self.lneVesperFold.setText(s)
        write_setting(PLUGIN_NAME + "/" + self.toolKey +
                      "/LastVesperOutFolder", s)

    @QtCore.pyqtSlot(int)
    def on_chkAutoCtrlFileName_stateChanged(self, state):
        self.updateCtrlFileName()

    @QtCore.pyqtSlot(name='on_cmdInCRS_clicked')
    def on_cmdInCRS_clicked(self):
        self.messageBar.clearWidgets()

        dlg = QgsGenericProjectionSelector(self)
        dlg.setMessage('Select coordinate system for the Vesper raster files')
        if dlg.exec_():
            if dlg.selectedAuthId() != '':
                self.in_qgscrs = QgsCoordinateReferenceSystem(dlg.selectedAuthId())
                if self.in_qgscrs == 'Unspecified' or self.in_qgscrs == '':
                    self.lblInCRS.setText('Unspecified')
                    self.lblOutCRS.setText('Unspecified')
                else:
                    self.lblInCRS.setText('{}  -  {}'.format(self.in_qgscrs.description(),
                                                             self.in_qgscrs.authid()))
                    self.lblInCRS.setStyleSheet('color:black;background:transparent;')
                    self.lblInCRSTitle.setStyleSheet('color:black')

        self.cleanMessageBars(self)

    @QtCore.pyqtSlot(int)
    def on_cboMethod_currentIndexChanged(self, index):
        self.stackedWidget.setCurrentIndex(index)
        self.updateCtrlFileName()
        if self.dfCSV is None:
            return

        if len(self.dfCSV) <= 100:
            self.cleanMessageBars(self)
            self.lneMinPoint.clear()
            QMessageBox.warning(
                self, 'Cannot Krige', 'Kriging is not advised for less than 100 points')

        if 'Low Density Kriging' in self.cboMethod.currentText():
            if len(self.dfCSV) <= 150:  # only partial file opened so open it all
                descCSV = describe.CsvDescribe(self.lneInCSVFile.text())
                self.dfCSV = descCSV.open_pandas_dataframe()
            self.lblRowCount.setText("The maximum number of points is {}.".format(len(self.dfCSV)))
            self.lneMinPoint.setText(str(len(self.dfCSV) - 2))
        else:
            self.lneMinPoint.clear()
            self.lblRowCount.setText('')

    def parse_variogram_file(self):

        vario_values = {}

        for line in open(self.lneVariogramFile.text()):
            # reset some text to control file tags
            line = line.replace('C0', 'CO')
            line = line.replace('Variogram Model', 'modtyp').strip()

            if set(':=.').intersection(set(line)):
                for ea in ['=', ':', ' ']:
                    if ea in line:
                        key, val = line.split(ea, 1)
                        break

                # sort out the numerics from the strings
                try:
                    key = int(float(key)) if int(
                        float(key)) == float(key) else float(key)
                except ValueError:
                    key = key.strip()

                try:
                    val = int(float(val)) if int(
                        float(val)) == float(val) else float(val)
                except ValueError:
                    val = val.strip()

                # only return keys required for the control file.
                # and key in VESPER_OPTIONS.keys():
                if isinstance(key, basestring):
                    vario_values[key] = val

        return vario_values

    def validate_csv_grid_files(self, csv_file, grid_file, show_msgbox=True):
        """ validate the csv and grid files and check for overlap assuming that they are
        of the same coordinate system if True then message will be blank else a message
        will be generated
        """

        overlaps = False

        if csv_file != '':
            if not os.path.exists(csv_file):
                return False, 'CSV file does not exist'
            else:
                try:
                    df_csv = pd.read_csv(csv_file)
                    csvX, csvY = predictCoordinateColumnNames(df_csv.columns)
                    csv_bbox = box(df_csv[csvX].min(), df_csv[csvY].min(),
                                   df_csv[csvX].max(), df_csv[csvY].max())

                except Exception as err:
                    self.lblInCSVFile.setStyleSheet('color:red')
                    self.lneInCSVFile.setStyleSheet('color:red')
                    return False, 'Invalid CSV file'

        if grid_file != '':
            if not os.path.exists(grid_file):
                return False, 'Grid file does not exist'
            else:
                try:
                    df_grid = pd.read_table(grid_file, names=['X', 'Y'],
                                            delimiter=' ', skipinitialspace=True)

                    grid_bbox = box(df_grid['X'].min(), df_grid['Y'].min(),
                                    df_grid['X'].max(), df_grid['Y'].max())

                except Exception as err:
                    self.lblInGridFile.setStyleSheet('color:red')
                    self.lneInGridFile.setStyleSheet('color:red')
                    return False, 'Invalid VESPER grid file'

        # only continue if both inputs aren't blank
        if csv_file == '' or grid_file == '':
            # if one or the other is blank keep validate as true or it wont write to the GUI
            return True, None

        if csv_file == grid_file:
            return False, "VESPER grid file and CSV file cannot be the same file"

        # now we can check for overlap

        if self.in_qgscrs:
            epsg = self.in_qgscrs.authid()
        else:
            epsg = ''

        overlaps = check_for_overlap(csv_bbox.to_wkt(), grid_bbox.to_wkt(), epsg, epsg)
        if not overlaps:
            message = 'There is no overlap between the VESPER Grid file and the CSV file.\n' \
                      'Please check input files and coordinate systems'

            if show_msgbox:
                QMessageBox.warning(self, 'No Overlap', message)

            self.lblInGridFile.setStyleSheet('color:red')
            self.lneInGridFile.setStyleSheet('color:red')
            self.lblInCSVFile.setStyleSheet('color:red')
            self.lneInCSVFile.setStyleSheet('color:red')
        else:
            message = None
            self.lblInGridFile.setStyleSheet('color:black')
            self.lneInGridFile.setStyleSheet('color:black')
            self.lblInCSVFile.setStyleSheet('color:black')
            self.lneInCSVFile.setStyleSheet('color:black')

        return overlaps, message

    def validate(self):
        """Check to see that all required gui elements have been entered and are valid."""
        try:
            self.messageBar.clearWidgets()
            self.cleanMessageBars(AllBars=True)
            errorList = []

            if self.lneInCSVFile.text() is None or self.lneInCSVFile.text() == '':
                self.lblInCSVFile.setStyleSheet('color:red')
                self.lneInCSVFile.setStyleSheet('color:red')
                errorList.append(self.tr("Select an input csv data file"))
            elif not os.path.exists(self.lneInCSVFile.text()):
                self.lblInCSVFile.setStyleSheet('color:red')
                self.lneInCSVFile.setStyleSheet('color:red')
                errorList.append(self.tr("Input csv data file does not exist"))
            else:
                self.lblInCSVFile.setStyleSheet('color:black')
                self.lneInCSVFile.setStyleSheet('color:black')

            if self.lneInGridFile.text() is None or self.lneInGridFile.text() == '':
                self.lblInGridFile.setStyleSheet('color:red')
                self.lneInGridFile.setStyleSheet('color:red')
                errorList.append(self.tr("Select a Vesper grid file"))
            elif not os.path.exists(self.lneInGridFile.text()):
                self.lblInGridFile.setStyleSheet('color:red')
                self.lneInGridFile.setStyleSheet('color:red')
                errorList.append(self.tr("Vesper grid file does not exists."))
            else:
                self.lblInGridFile.setStyleSheet('color:black')
                self.lneInGridFile.setStyleSheet('color:black')

            if self.in_qgscrs is None:
                self.lblInCRSTitle.setStyleSheet('color:red')
                self.lblInCRS.setStyleSheet('color:red;background:transparent;')
                errorList.append(self.tr("Select a coordinate system"))
            else:
                self.lblInCRSTitle.setStyleSheet('color:black')
                self.lblInCRS.setStyleSheet('color:black;background:transparent;')

            pass_check, message = self.validate_csv_grid_files(self.lneInCSVFile.text(),
                                                               self.lneInGridFile.text())
            if not pass_check:
                errorList.append(self.tr(message))
                # errorList.append(self.tr("Input csv file and grid file do not overlap. Could be "
                #                          "due to differing coordinate systems or invalid files"))

            if self.cboKrigColumn.currentText() == '':
                self.lblKrigColumn.setStyleSheet('color:red')
                errorList.append(self.tr("Select a column to krige"))
            else:
                self.lblKrigColumn.setStyleSheet('color:black')

            if self.cboMethod.currentText() != 'High Density Kriging':

                if self.lneVariogramFile.text() is None or self.lneVariogramFile.text() == '':
                    self.lblVariogramFile.setStyleSheet('color:red')
                    self.lneVariogramFile.setStyleSheet('color:red')
                    errorList.append(self.tr("Select a variogram text file"))
                elif not os.path.exists(self.lneVariogramFile.text()):
                    self.lblVariogramFile.setStyleSheet('color:red')
                    self.lneVariogramFile.setStyleSheet('color:red')
                    errorList.append(self.tr("Variogram text file does not exists."))
                else:
                    self.lblVariogramFile.setStyleSheet('color:black')
                    self.lneVariogramFile.setStyleSheet('color:black')

                if int(self.lneMinPoint.text()) >= len(self.dfCSV):
                    self.lneMinPoint.setStyleSheet('color:red')
                    self.lneMinPoint.setStyleSheet('color:red')
                    errorList.append(
                        self.tr("Minimum number of points should be at least "
                                "2 less than the dataset count"))
                else:
                    self.lneMinPoint.setStyleSheet('color:black')
                    self.lneMinPoint.setStyleSheet('color:black')

            if self.lneVesperFold.text() == '':
                self.lblVesperFold.setStyleSheet('color:red')
                errorList.append(self.tr("Select output Vesper data folder"))
            elif not os.path.exists(self.lneVesperFold.text()):
                self.lneVesperFold.setStyleSheet('color:red')
                errorList.append(
                    self.tr("Output Vesper data folder does not exist"))
            else:
                self.lblVesperFold.setStyleSheet('color:black')
                self.lneVesperFold.setStyleSheet('color:black')

            ctrl_file = os.path.join(self.lneVesperFold.text(), self.lneCtrlFile.text())

            if os.path.exists(ctrl_file):
                message = 'Vesper Control File {} already exists. Do you want to' \
                          ' overwrite?'.format(self.lneCtrlFile.text())

                reply = QMessageBox.question(
                    self, 'Control File', message, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
                if reply == QtGui.QMessageBox.Yes:
                    self.overwrite_ctrl_file = True
                    self.lblVesperFold.setStyleSheet('color:black')
                else:
                    self.overwrite_ctrl_file = False
                    self.lblCtrlFile.setStyleSheet('color:red')
                    errorList.append(
                        self.tr("Output control file exists please choose a different name"))

            if len(errorList) > 0:
                raise ValueError(errorList)

        except ValueError as e:
            self.cleanMessageBars(True)
            if len(errorList) > 0:
                for i, ea in enumerate(errorList):
                    self.send_to_messagebar(
                        unicode(ea), level=QgsMessageBar.WARNING, duration=(i + 1) * 5)
                return False

        return True

    def accept(self, *args, **kwargs):
        if not self.validate():
            return False

        try:
            self.cleanMessageBars(True)

            message = '- and run VESPER' if self.gbRunVesper.isChecked() else ''
            LOGGER.info('{st}\nProcessing {} {}'.format(
                self.windowTitle(), message, st='*' * 50))

            # Add settings to log
            settingsStr = 'Parameters:---------------------------------------'
            settingsStr += '\n    {:30}\t{}'.format('Data File:', self.lneInCSVFile.text())
            settingsStr += '\n    {:30}\t{}'.format('Krige Column:',
                                                    self.cboKrigColumn.currentText())
            settingsStr += '\n    {:30}\t{}'.format('Grid File:', self.lneInGridFile.text())
            settingsStr += '\n    {:30}\t{}'.format('Output Vesper Folder:',
                                                    self.lneVesperFold.text())
            settingsStr += '\n    {:30}\t{}'.format(self.cboMethod.currentText(), '')

            if self.cboMethod.currentText() == 'High Density Kriging':
                settingsStr += '\n    {:30}\t{}'.format('Block Kriging Size:',
                                                        int(self.dsbBlockKrigSize.value()))

            else:
                settingsStr += '\n    {:30}\t{}'.format('Variogram File:',
                                                        self.lneVariogramFile.text())
                settingsStr += '\n    {:30}\t{}'.format('Min Point Number:',
                                                        self.lneMinPoint.text())

            settingsStr += '\n    {:30}\t{}'.format(
                'Display Vesper Graphics:', self.chkDisplayGraphics.isChecked())

            settingsStr += '\n    {:30}\t{}'.format(
                'Run Vesper Now:', self.gbRunVesper.isChecked())

            if self.gbRunVesper.isChecked():
                settingsStr += '\n    {:30}\t{}'.format('Import Vesper Files to Rasters:',
                                                        self.chkVesper2Raster.isChecked())
                if self.chkVesper2Raster.isChecked():
                    settingsStr += '\n    {:30}\t{}'.format(
                        'Vesper Files Coordinate System:', self.lblInCRS.text())

            LOGGER.info(settingsStr)

            # get a fresh dataframe for the input csv file
            self.dfCSV = pd.read_csv(self.lneInCSVFile.text())

            vc = VesperControl()

            if self.cboMethod.currentText() == 'High Density Kriging':
                vc.update(xside=int(self.dsbBlockKrigSize.value()),
                          yside=int(self.dsbBlockKrigSize.value()))
            else:
                # from the variogram text file find and update the control file keys
                vario = self.parse_variogram_file()

                vesp_keys = {key: val for key, val in vario.items() if key in vc}
                vc.update(vesp_keys)

                # apply the other keys.
                vc.update({'jpntkrg': 1,
                           'jlockrg': 0,
                           'minpts': int(self.lneMinPoint.text()),
                           'maxpts': len(self.dfCSV),
                           'jcomvar': 0,
                           })
            epsg = int(self.in_qgscrs.authid().replace('EPSG:', ''))
            bat_file, ctrl_file = prepare_for_vesper_krige(self.dfCSV,
                                                           self.cboKrigColumn.currentText(),
                                                           self.lneInGridFile.text(),
                                                           self.lneVesperFold.text(),
                                                           control_textfile=self.lneCtrlFile.text(),
                                                           coord_columns=[],
                                                           epsg=epsg,
                                                           display_graphics=self.chkDisplayGraphics.isChecked(),
                                                           control_options=vc)

            epsg = 0
            if self.in_qgscrs is not None and self.chkVesper2Raster.isChecked():
                epsg = int(self.in_qgscrs.authid().replace('EPSG:', ''))

            if self.gbRunVesper.isChecked():
                # Add to vesper queue
                self.vesp_dict = {'control_file': ctrl_file, 'epsg': epsg}

            else:
                message = 'Successfully created files for Vesper kriging. ' \
                          'The control file is {}'.format(ctrl_file)
                self.send_to_messagebar(message, level=QgsMessageBar.SUCCESS, duration=0,
                                        addToLog=True, core_QGIS=True)
                LOGGER.info('Successfully created files for Vesper kriging')

            QtGui.qApp.restoreOverrideCursor()
            return super(PreVesperDialog, self).accept(*args, **kwargs)

        except Exception as err:
            QtGui.qApp.restoreOverrideCursor()
            self.cleanMessageBars(True)
            self.send_to_messagebar(str(err), level=QgsMessageBar.CRITICAL, duration=0,
                                    addToLog=True, showLogPanel=True, exc_info=sys.exc_info())
            return False
Exemple #14
0
class ImgUploaderWizard(QtGui.QWizard, FORM_CLASS):

    def __init__(self, iface, settings, parent=None):
        """Constructor."""
        super(ImgUploaderWizard, self).__init__(parent)
        # Set up the user interface from Designer.
        # After 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.iface = iface
        self.setupUi(self)

        self.setWindowFlags(Qt.WindowCloseButtonHint |
                            Qt.WindowMinimizeButtonHint)

        # Message bars need to be attached to pages, since the wizard object
        # does not have a layout. It doesn't work to attach the same bar
        # object to all pages (it is only shown in the last one). The only way
        # I could make it work was to create different QgsMessageBar objects,
        # one per page, but it is very to keep track of those references
        # along the code. It is messy, there should be a better solution.

        self.bar0 = QgsMessageBar()
        self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(0).layout().addWidget(self.bar0)

        self.bar1 = QgsMessageBar()
        self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(1).layout().addWidget(self.bar1)

        self.bar2 = QgsMessageBar()
        self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(2).layout().addWidget(self.bar2)

        self.setButtonText(QtGui.QWizard.CustomButton1,
                           self.tr("&Start upload"))
        self.setOption(QtGui.QWizard.HaveCustomButton1, True)
        self.button(QWizard.CustomButton1).setVisible(False)

        self.settings = settings

        # Initialize the upload progressbar
        self.upPrgWin = None

        # Initialize layers and default settings
        self.loadLayers()
        self.loadMetadataSettings()
        self.loadStorageSettings()
        self.loadOptionsSettings()
        self.setStorageType()

        # register event handlers
        self.button(QWizard.BackButton).clicked.connect(self.previousPage)
        self.button(QWizard.NextButton).clicked.connect(self.nextPage)
        self.button(QWizard.FinishButton).clicked.connect(self.finishWizard)
        self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard)

        # Imagery connections (wizard page 1)
        self.layers_tool_button.clicked.connect(self.loadLayers)
        self.file_tool_button.clicked.connect(self.selectFile)
        self.add_source_button.clicked.connect(self.addSources)
        self.remove_source_button.clicked.connect(self.removeSources)
        self.up_source_button.clicked.connect(self.upSource)
        self.down_source_button.clicked.connect(self.downSource)

        # Metadata connections (wizard page 2)
        self.sense_start_edit.setCalendarPopup(1)
        self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm')
        self.sense_end_edit.setCalendarPopup(1)
        self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm')
        self.default_button.clicked.connect(self.loadMetadataSettings)
        self.clean_button.clicked.connect(self.cleanMetadataSettings)
        self.save_button.clicked.connect(self.saveMetadata)

        # Temporarily hide the reload button
        self.reload_button.hide()
        self.reload_button.clicked.connect(self.loadSavedMetadata)

        # temporarily disable convert_format_check_box and combobox
        self.convert_format_check_box.setEnabled(False)
        self.format_combo_box.addItem('n.a.')
        self.format_combo_box.setEnabled(False)

        # Upload tab connections (wizard page 3)
        # self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify)
        self.storage_type_combo_box.currentIndexChanged.connect(self.setStorageType)
        self.customButtonClicked.connect(self.startUpload)
        # self.button(QWizard.CustomButton1).clicked.connect(self.startUpload)

        self.toggleTokenRequestForm()
        self.notify_oam_check.stateChanged.connect(self.toggleTokenRequestForm)

        # temporarily disable trigger_tiling_check
        self.notify_oam_check.setEnabled(False)


    # handlers for navigation
    def nextPage(self):
        if self.currentId() == 1:
            # print "Page ID: " + str(self.currentId())
            # self.bar1.clearWidgets()
            pass
        elif self.currentId() == 2:
            # print "Page ID: " + str(self.currentId())
            # self.bar2.clearWidgets()
            self.loadMetadataReviewBox()
            self.button(QWizard.CustomButton1).setVisible(True)
        else:
            pass

    def previousPage(self):
        if self.currentId() == 0:
            # print "Page ID: " + str(self.currentId())
            # self.bar0.clearWidgets()
            pass
        elif self.currentId() == 1:
            # print "Page ID: " + str(self.currentId())
            # self.bar1.clearWidgets()
            self.button(QWizard.CustomButton1).setVisible(False)
        else:
            pass

    def finishWizard(self):
        # print "finish wizard button was clicked."
        pass

    def cancelWizard(self):
        # print "cancel wizard button was clicked."
        # need to display QMessageBox
        if self.upPrgWin is not None:
            self.upPrgWin.cancelAllUploads()

    # event handling for wizard page 1
    def loadLayers(self):
        all_layers = self.iface.mapCanvas().layers()
        for layer in all_layers:
            if not self.layers_list_widget.findItems(
                layer.name(), Qt.MatchExactly):
                if not self.sources_list_widget.findItems(
                    layer.name(), Qt.MatchExactly):
                    item = QListWidgetItem()
                    item.setText(layer.name())
                    item.setData(
                        Qt.UserRole, os.path.abspath(layer.dataProvider().dataSourceUri()))
                    self.layers_list_widget.addItem(item)
                    # print(item.data(Qt.UserRole))

    def selectFile(self):
        selected_file = QFileDialog.getOpenFileName(
            self,
            'Select imagery file',
            os.path.expanduser("~"))
        self.source_file_edit.setText(selected_file)

    def addSources(self):
        filename = self.source_file_edit.text()
        selected_layers = self.layers_list_widget.selectedItems()
        if not filename and not selected_layers:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                'WARNING',
                'Either a layer or file should be selected to be added',
                level=QgsMessageBar.WARNING)
        else:
            added = False
            if filename:
                result_val = validate_file(filename)
                if result_val["val"]:
                    if not self.sources_list_widget.findItems(
                        filename, Qt.MatchExactly):
                        item = QListWidgetItem()
                        item.setText(os.path.basename(filename))
                        item.setData(Qt.UserRole, os.path.abspath(filename))
                        print(item.data(Qt.UserRole))
                        self.sources_list_widget.addItem(item)
                        self.added_sources_list_widget.addItem(item.clone())
                        self.source_file_edit.setText('')
                        added = True
                else:
                    msg = result_val["msg"]
                    self.bar0.clearWidgets()
                    self.bar0.pushMessage(
                        "CRITICAL",
                        msg,
                        level=QgsMessageBar.CRITICAL)
                    QgsMessageLog.logMessage(
                        "CRITICAL",
                        msg,
                        level=QgsMessageLog.INFO)

            if selected_layers:
                for item in selected_layers:
                    if validate_layer(item.text(), self.iface):
                        if not self.sources_list_widget.findItems(
                            item.text(), Qt.MatchExactly):
                            self.layers_list_widget.takeItem(
                                self.layers_list_widget.row(item))
                            self.sources_list_widget.addItem(item)
                            self.added_sources_list_widget.addItem(item.clone())
                            added = True
                    else:
                        self.bar0.clearWidgets()
                        self.bar0.pushMessage(
                            "CRITICAL",
                            "Vector layers cannot be selected for upload",
                            level=QgsMessageBar.CRITICAL)
            if added:
                self.bar0.clearWidgets()
                self.bar0.pushMessage(
                    'INFO',
                    'Source(s) added to the upload queue',
                    level=QgsMessageBar.INFO)

    def removeSources(self):
        selected_sources = self.sources_list_widget.selectedItems()
        added_sources = self.added_sources_list_widget.selectedItems()
        if selected_sources:
            for item in selected_sources:
                self.sources_list_widget.takeItem(
                    self.sources_list_widget.row(item))
                added_item = self.added_sources_list_widget.findItems(
                    item.text(), Qt.MatchExactly)
                if added_item:
                    self.added_sources_list_widget.takeItem(
                        self.added_sources_list_widget.row(added_item[0]))
                all_layers = self.iface.mapCanvas().layers()
                for layer in all_layers:
                    if item.text() == layer.name():
                        if not self.layers_list_widget.findItems(
                            item.text(), Qt.MatchExactly):
                            self.layers_list_widget.addItem(item)
        else:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                'WARNING',
                'An imagery source must be selected to be removed',
                level=QgsMessageBar.WARNING)

    def upSource(self):
        selected_layers = self.sources_list_widget.selectedItems()
        if selected_layers:
            position = self.sources_list_widget.row(selected_layers[0])
            if position > 0:
                item = self.sources_list_widget.takeItem(position)
                self.sources_list_widget.insertItem(position - 1, item)
                item.setSelected(1)

    def downSource(self):
        selected_layers = self.sources_list_widget.selectedItems()
        if selected_layers:
            position = self.sources_list_widget.row(selected_layers[0])
            if position < self.sources_list_widget.count() - 1:
                item = self.sources_list_widget.takeItem(position)
                self.sources_list_widget.insertItem(position + 1, item)
                item.setSelected(1)

    # event handling for wizard page 2
    # load default values
    def loadMetadataSettings(self):
        self.settings.beginGroup("Metadata")
        self.base_uuid_edit.setText(self.settings.value('BASE_UUID'))
        self.title_edit.setText(self.settings.value('TITLE'))
        if self.settings.value('PLATFORM') is None:
            self.platform_combo_box.setCurrentIndex(0)
        else:
            self.platform_combo_box.setCurrentIndex(
                int(self.settings.value('PLATFORM')))
        self.sensor_edit.setText(self.settings.value('SENSOR'))
        self.sensor_edit.setCursorPosition(0)
        self.sense_start_edit.setDate(QDateTime.fromString(
                                      self.settings.value('SENSE_START'),
                                      Qt.ISODate).date())
        self.sense_start_edit.setTime(
            QDateTime.fromString(self.settings.value('SENSE_START'),
                                 Qt.ISODate).time())
        self.sense_end_edit.setDate(
            QDateTime.fromString(self.settings.value('SENSE_END'),
                                 Qt.ISODate).date())
        self.sense_end_edit.setTime(
            QDateTime.fromString(self.settings.value('SENSE_END'),
                                 Qt.ISODate).time())
        self.provider_edit.setText(self.settings.value('PROVIDER'))
        self.provider_edit.setCursorPosition(0)
        self.contact_edit.setText(self.settings.value('CONTACT'))
        self.contact_edit.setCursorPosition(0)
        self.settings.endGroup()

    def cleanMetadataSettings(self):
        self.base_uuid_edit.setText('')
        self.title_edit.setText('')
        self.platform_combo_box.setCurrentIndex(0)
        self.sensor_edit.setText('')
        self.sense_start_edit.setDate(
            QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).date())
        self.sense_start_edit.setTime(
            QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time())
        self.sense_end_edit.setDate(
            QDateTime().fromString('1970-01-01T00:00:00',
                                   Qt.ISODate).date())
        self.sense_end_edit.setTime(
            QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time())
        self.provider_edit.setText('')
        self.contact_edit.setText('')
        self.license_check_box.setCheckState(0)
        self.reproject_check_box.setCheckState(0)

    def saveMetadata(self):

        selected_layers = self.added_sources_list_widget.selectedItems()
        if not selected_layers:
            self.bar1.clearWidgets()
            self.bar1.pushMessage(
                'WARNING',
                'One or more source imagery should be selected to have ' +
                'the metadata saved',
                level=QgsMessageBar.WARNING)
        else:
            if not self.reproject_check_box.isChecked():

                self.bar1.clearWidgets()
                self.bar1.pushMessage(
                    'INFO',
                    'Metadata extraction is being processed...',
                    level=QgsMessageBar.INFO)

                # num_selected_layers = len(selected_layers)
                for each_layer in selected_layers:
                    file_abspath = each_layer.data(Qt.UserRole)
                    # print("file name: " + str(file_abspath))
                    self.exportMetaAsTextFile(file_abspath)

                self.bar1.clearWidgets()
                self.bar1.pushMessage(
                    'INFO',
                    'Metadata for the selected sources were saved',
                    level=QgsMessageBar.INFO)
            else:
                qMsgBox = QMessageBox()
                qMsgBox.setWindowTitle("Confirmation")
                qMsgBox.setText("You checked the reprojection option, " +
                                "which can require significant amount " +
                                "of time. Are you sure to continue?")
                qMsgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
                # qMsgBox.setDefaultButton(QMessageBox.Cancel)

                if qMsgBox.exec_() == QMessageBox.Ok:
                    """Open python console"""
                    """
                    pluginMenu = self.iface.pluginMenu()
                    #print(repr(pluginMenu))
                    for action in pluginMenu.actions():
                        #print(action.text())
                        if 'Python Console' in action.text():
                            action.trigger()
                    """

                    # num_selected_layers = len(selected_layers)
                    self.reprojectionCmdWindows = []
                    self.numReprojectionCmdWindows = len(selected_layers)
                    self.numReprojectionFinished = 0
                    index = 0

                    self.bar1.clearWidgets()
                    self.bar1.pushMessage(
                        'INFO',
                        'Reprojecting files: {0}th image '.format(
                            self.numReprojectionFinished + 1
                        ) +
                        'out of {0} is being processed...'.format(
                            self.numReprojectionCmdWindows
                        ),
                        level=QgsMessageBar.INFO)

                    for each_layer in selected_layers:
                        file_abspath = each_layer.data(Qt.UserRole)

                        if "EPSG3857" not in file_abspath:

                            reprojected_file_abspath = os.path.splitext(
                                file_abspath)[0] + '_EPSG3857.tif'
                            layerName = each_layer.text()

                            cmd = "gdalwarp"
                            optionsInList = ["-of", "GTiff", "-overwrite", "-t_srs", "epsg:3857"]

                            self.reprojectionCmdWindows.append(
                                ReprojectionCmdWindow('Reprojection',
                                                      cmd,
                                                      optionsInList,
                                                      file_abspath,
                                                      reprojected_file_abspath,
                                                      index,
                                                      layerName))
                            self.reprojectionCmdWindows[index].finished.connect(
                                self.updateReprojectionProgress)
                            # self.reprojectionCmdWindows[
                            #    index].cancelled.connect(
                            #    self.cancelSingleReprojectionProcess)
                            self.reprojectionCmdWindows[index].show()
                            self.reprojectionCmdWindows[index].move(
                                20 + index * 20, 20 + index * 20)
                            # self.reprojectionCmdWindows[
                            #    index].startCommandThread()
                            self.reprojectionCmdWindows[index].run()
                            self.reprojectionCmdWindows[index].activateWindow()

                            index += 1

                        else:
                            self.bar1.clearWidgets()
                            self.bar1.pushMessage(
                                'WARNING',
                                'Suffix _EPSG3857 is already included in ' +
                                'one (some) of the selected files...',
                                level=QgsMessageBar.WARNING)
                            # break

    def cancelSingleReprojectionProcess(self, index):
        # print("Reprojection was cancelled. Index: {0}".format(str(index)))
        pass

    def updateReprojectionProgress(self, index):

        """ consider the use of callback function here. """
        reprojectedFileAbsPath = self.reprojectionCmdWindows[
            index].getReprojectedFileAbsPath()
        reprojectedLayerName = self.reprojectionCmdWindows[
            index].getReprojectedLayerName()
        fileAbsPath = self.reprojectionCmdWindows[
            index].getFileAbsPath()
        layerName = self.reprojectionCmdWindows[
            index].getLayerName()
        # print('Reprojection completed for:')
        # print('File Path:  {0}'.format(fileAbsPath))
        # print('Layer Name: {0}'.format(layerName))

        """
        # rlayer = QgsRasterLayer(reprojectedFileAbsPath,
        #    reprojectedLayerName)
        # QgsMapLayerRegistry.instance().addMapLayer(rlayer)
        # print(str(rlayer.isValid()))
        """

        self.iface.addRasterLayer(
            reprojectedFileAbsPath, reprojectedLayerName)
        # print('Insert reprojected raster layer. Layer name:')
        # print('File Path:  {0}'.format(reprojectedFileAbsPath))
        # print('Layer Name: {0}'.format(reprojectedLayerName))

        self.exportMetaAsTextFile(reprojectedFileAbsPath)
        # print('Export Metadata into text file...')

        # self.activateWindow()

        items_for_remove = self.added_sources_list_widget.findItems(
            layerName, Qt.MatchExactly)
        self.added_sources_list_widget.takeItem(
            self.added_sources_list_widget.row(items_for_remove[0]))

        items_for_remove = self.sources_list_widget.findItems(
            layerName, Qt.MatchExactly)
        self.sources_list_widget.takeItem(
            self.sources_list_widget.row(items_for_remove[0]))
        # self.layers_list_widget.addItem(items_for_remove[0])

        item = QListWidgetItem()
        item.setText(reprojectedLayerName)
        item.setData(Qt.UserRole, os.path.abspath(reprojectedFileAbsPath))
        self.sources_list_widget.addItem(item)
        self.added_sources_list_widget.addItem(item.clone())

        self.loadLayers()

        self.numReprojectionFinished += 1

        if self.numReprojectionFinished < self.numReprojectionCmdWindows:
            self.bar1.clearWidgets()
            self.bar1.pushMessage(
                'INFO',
                'Reprojecting files: {0}th image '.format(
                    self.numReprojectionFinished + 1) +
                'out of {0} is being processed...'.format(
                    self.numReprojectionCmdWindows),
                level=QgsMessageBar.INFO)
        else:
            self.bar1.clearWidgets()
            self.bar1.pushMessage(
                'INFO',
                'Metadata for the selected sources were saved',
                level=QgsMessageBar.INFO)

    def createFootPrint(self):
        # probably need to insert the codes to create and insert
        # the detailed footprint layer here
        pass

    def exportMetaAsTextFile(self, file_abspath):

        # get metadata from GUI, and store them in a dictionary
        metaInputInDict = {}
        temp_filename = os.path.basename(file_abspath)
        strUuid = '{0}{1}'.format(self.base_uuid_edit.text(), temp_filename)
        metaInputInDict['uuid'] = strUuid
        metaInputInDict['title'] = self.title_edit.text()
        metaInputInDict['platform'] = \
            self.platform_combo_box.currentText()
        metaInputInDict['acquisition_start'] = \
            self.sense_start_edit.dateTime().toString(Qt.ISODate)
        metaInputInDict['acquisition_end'] = \
            self.sense_end_edit.dateTime().toString(Qt.ISODate)
        metaInputInDict['provider'] = \
            self.provider_edit.text()
        metaInputInDict['contact'] = self.contact_edit.text()

        properties = {}
        properties['sensor'] = self.sensor_edit.text()
        properties['thumbnail'] = strUuid + ".thumb.png"
        metaInputInDict['properties'] = properties

        # extract metadata from GeoTiff,
        # and merge with the metadata from textbox
        imgMetaHdlr = ImgMetadataHandler(file_abspath)
        imgMetaHdlr.extractMetaInImagery()
        metaForUpload = dict(
            imgMetaHdlr.getMetaInImagery().items() + metaInputInDict.items())
        strMetaForUpload = str(json.dumps(metaForUpload))

        json_file_abspath = file_abspath + '_meta.json'
        # print json_file_abspath
        f = open(json_file_abspath, 'w')
        f.write(strMetaForUpload)
        f.close()

        return(True)

    def loadSavedMetadata(self):

        qMsgBox = QMessageBox()
        qMsgBox.setText('Under construction:\nMessage from ' +
                        'loadSavedMetadata function.')
        qMsgBox.exec_()

    # event handling for wizard page 3
    # please also see startUpload function
    # for event handling of Start Upload button
    """
    def enableSpecify(self):
        if self.storage_combo_box.currentIndex() == 1:
            self.specify_label.setEnabled(1)
            self.specify_edit.setEnabled(1)
        else:
            self.specify_label.setEnabled(0)
            self.specify_edit.setText('')
            self.specify_edit.setEnabled(0)
    """


    """ need to simplify this part later """
    def setAWSS3Edit(self):
            self.aws_bucket_name_edit.setVisible(True)
            self.aws_bucket_name_label.setVisible(True)
            self.aws_key_id_edit.setVisible(True)
            self.aws_key_id_label.setVisible(True)
            self.aws_secret_key_edit.setVisible(True)
            self.aws_secret_key_label.setVisible(True)
            self.google_client_secret_file_edit.setVisible(False)
            self.google_client_secret_file_label.setVisible(False)
            self.google_application_name_edit.setVisible(False)
            self.google_application_name_label.setVisible(False)
            self.dropbox_access_token_edit.setVisible(False)
            self.dropbox_access_token_label.setVisible(False)

    def setGoogleDriveEdit(self):
            self.aws_bucket_name_edit.setVisible(False)
            self.aws_bucket_name_label.setVisible(False)
            self.aws_key_id_edit.setVisible(False)
            self.aws_key_id_label.setVisible(False)
            self.aws_secret_key_edit.setVisible(False)
            self.aws_secret_key_label.setVisible(False)
            self.google_client_secret_file_edit.setVisible(True)
            self.google_client_secret_file_label.setVisible(True)
            self.google_application_name_edit.setVisible(True)
            self.google_application_name_label.setVisible(True)
            self.dropbox_access_token_edit.setVisible(False)
            self.dropbox_access_token_label.setVisible(False)

    def setDroboxEdit(self):
            self.aws_bucket_name_edit.setVisible(False)
            self.aws_bucket_name_label.setVisible(False)
            self.aws_key_id_edit.setVisible(False)
            self.aws_key_id_label.setVisible(False)
            self.aws_secret_key_edit.setVisible(False)
            self.aws_secret_key_label.setVisible(False)
            self.google_client_secret_file_edit.setVisible(False)
            self.google_client_secret_file_label.setVisible(False)
            self.google_application_name_edit.setVisible(False)
            self.google_application_name_label.setVisible(False)
            self.dropbox_access_token_edit.setVisible(True)
            self.dropbox_access_token_label.setVisible(True)

    def setStorageType(self):
        if self.storage_type_combo_box.currentIndex() == 0:
            self.setAWSS3Edit()

        elif self.storage_type_combo_box.currentIndex() == 1:
            self.setGoogleDriveEdit()

        elif self.storage_type_combo_box.currentIndex() == 2:
            self.setDroboxEdit()

    def toggleTokenRequestForm(self):

        state = False
        if self.notify_oam_check.isChecked():
            state = True

        self.btn_request_token.setEnabled(state)
        self.token_edit.setEnabled(state)
        self.token_label.setEnabled(state)
        self.uploader_name_edit.setEnabled(state)
        self.uploader_name_label.setEnabled(state)
        self.uploader_email_edit.setEnabled(state)
        self.uploader_email_label.setEnabled(state)

        # temporarily disable textboxes
        self.token_edit.setText('n.a.')
        self.uploader_name_edit.setText('n.a.')
        self.uploader_email_edit.setText('n.a.')

    def loadStorageSettings(self):
        self.settings.beginGroup("Storage")
        """
        bucket = self.settings.value('AWS_BUCKET_NAME')
        storage_index = self.storage_combo_box.findText(
            bucket, Qt.MatchExactly)
        if not storage_index == -1:
            self.storage_combo_box.setCurrentIndex(storage_index)
        else:
            self.storage_combo_box.setCurrentIndex(
                self.storage_combo_box.findText(self.tr('other...')))
            self.specify_label.setEnabled(1)
            self.specify_edit.setEnabled(1)
            self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME'))
        """

        if self.settings.value('DEFAULT_STORAGE') != None:
            self.storage_type_combo_box.setCurrentIndex(
                int(self.settings.value('DEFAULT_STORAGE')))
        else:
            self.storage_type_combo_box.setCurrentIndex(0)

        self.aws_bucket_name_edit.setText(
            self.settings.value('AWS_BUCKET_NAME'))
        self.aws_bucket_name_edit.setCursorPosition(0)
        self.aws_key_id_edit.setText(
            self.settings.value('AWS_ACCESS_KEY_ID'))
        self.aws_key_id_edit.setCursorPosition(0)
        self.aws_secret_key_edit.setText(
            self.settings.value('AWS_SECRET_ACCESS_KEY'))
        self.aws_secret_key_edit.setCursorPosition(0)

        self.google_client_secret_file_edit.setText(
            self.settings.value('GOOGLE_CLIENT_SECRET_FILE'))
        self.google_client_secret_file_edit.setCursorPosition(0)
        self.google_application_name_edit.setText(
            self.settings.value('GOOGLE_APPLICATION_NAME'))
        self.google_application_name_edit.setCursorPosition(0)

        self.dropbox_access_token_edit.setText(
            self.settings.value('DROPBOX_ACCESS_TOKEN'))
        self.dropbox_access_token_edit.setCursorPosition(0)

        self.settings.endGroup()

        # temporarily disable combobox
        self.storage_type_combo_box.setCurrentIndex(0)
        self.storage_type_combo_box.setEnabled(False)

    def loadOptionsSettings(self):
        self.settings.beginGroup("Options")
        """
        Boolean values are converted into string and lower case for
        'if' statement, since PyQt sometimes returns 'true', just like C++,
        instead of 'True', Python style.
        Maybe we can use integer values (0 or 1), instead of using string.
        """
        if str(self.settings.value('LICENSE')).lower() == 'true':
            self.license_check_box.setCheckState(2)
        if str(self.settings.value('REPROJECT')).lower() == 'true':
            self.reproject_check_box.setCheckState(2)
        if str(self.settings.value('NOTIFY_OAM')).lower() == 'true':
            self.notify_oam_check.setCheckState(2)

        self.settings.endGroup()

        # temporarily disable notify_oam_check box
        self.notify_oam_check.setCheckState(0)
        self.notify_oam_check.setEnabled(False)

    def loadMetadataReviewBox(self):
        json_file_abspaths = []
        for index in xrange(self.sources_list_widget.count()):
            file_abspath = str(
                self.sources_list_widget.item(index).data(Qt.UserRole))
            json_file_abspath = ''
            json_file_abspath = file_abspath + '_meta.json'
            # print str(json_file_abspath)
            json_file_abspaths.append(json_file_abspath)

        temp = QTemporaryFile()
        temp.open()
        for json_file_abspath in json_file_abspaths:
            if os.path.exists(json_file_abspath):
                with open(json_file_abspath) as infile:
                    temp.write(infile.read() + '\n\n')
            else:
                temp.write('%s was not found. Please save it in advance.\n\n'
                           % str(json_file_abspath))
        temp.flush()
        temp.seek(0)

        stream = QTextStream(temp)
        self.review_metadata_box.setText(stream.readAll())

    # event handler for upload button
    def startUpload(self):

        upload_options = []
        upload_file_abspaths = []

        # get the information of upload options
        if self.notify_oam_check.isChecked():
            upload_options.append("notify_oam")

        if not self.license_check_box.isChecked():
            self.bar2.clearWidgets()
            self.bar2.pushMessage(
                'CRITICAL',
                'Please check the lisence term.',
                level=QgsMessageBar.WARNING)
        else:
            for index in xrange(self.sources_list_widget.count()):
                upload_file_abspath = str(
                    self.added_sources_list_widget.item(index).data(Qt.UserRole))
                # create thumbnail
                ThumbnailCreation.createThumbnail(upload_file_abspath)
                upload_file_abspaths.append(upload_file_abspath)

            if self.upPrgWin is None:
                self.upPrgWin = UploadProgressWindow()
                self.upPrgWin.connected.connect(self.displayConnectionResult)
                # self.upPrgWin.progress.connect(self.updateProgress)
                self.upPrgWin.started.connect(self.updateListWidgets)
                self.upPrgWin.finished.connect(self.finishUpload)

            if self.storage_type_combo_box.currentIndex() == 0:

                # get login information for bucket
                bucket_name = None
                bucket_key = None
                bucket_secret = None

                #if self.storage_combo_box.currentIndex() == 0:
                #    bucket_name = 'oam-qgis-plugin-test'
                #else:
                bucket_name = str(self.aws_bucket_name_edit.text())
                #if not bucket_name:
                #    self.bar2.clearWidgets()
                #    self.bar2.pushMessage(
                #        'WARNING',
                #        'The bucket for upload must be provided',
                #        level=QgsMessageBar.WARNING)

                bucket_key = str(self.aws_key_id_edit.text())
                bucket_secret = str(self.aws_secret_key_edit.text())

                self.upPrgWin.startUpload('aws',
                                            upload_file_abspaths,
                                            upload_options,
                                            awsBucketKey=bucket_key,
                                            awsBucketSecret=bucket_secret,
                                            awsBucketName=bucket_name)

            elif self.storage_type_combo_box.currentIndex() == 1:
                qMsgBox = QMessageBox()
                qMsgBox.setText('Under construction:\nMessage from ' +
                                'Google drive module.')
                qMsgBox.exec_()

            elif self.storage_type_combo_box.currentIndex() == 2:
                qMsgBox = QMessageBox()
                qMsgBox.setText('Under construction:\nMessage from ' +
                                'Dropbox module.')
                qMsgBox.exec_()

            self.button(QWizard.FinishButton).setVisible(False)
            # print(self.isTopLevel())

    def displayConnectionResult(self, didStart):
        if didStart:
            self.bar2.clearWidgets()
            self.bar2.pushMessage(
                'INFO',
                'Uploading imagery and metadata...',
                level=QgsMessageBar.INFO)
        else:
            self.bar2.clearWidgets()
            self.bar2.pushMessage(
                'CRITICAL',
                'Connection to S3 server failed.',
                level=QgsMessageBar.CRITICAL)

    def updateListWidgets(self, fileAbsPath):
        # print('fileAbsPath: ' + fileAbsPath)

        # print(str(self.added_sources_list_widget.count()))
        for index in xrange(0, self.added_sources_list_widget.count()):
            refFileAbsPath = str(
                self.added_sources_list_widget.item(index).data(Qt.UserRole))
            # print('refFileAbsPath: ' + refFileAbsPath)
            if fileAbsPath == refFileAbsPath:
                self.added_sources_list_widget.takeItem(index)
                break

        # print(str(self.sources_list_widget.count()))
        for index in xrange(0, self.sources_list_widget.count()):
            refFileAbsPath = str(
                self.sources_list_widget.item(index).data(Qt.UserRole))
            # print('refFileAbsPath: ' + refFileAbsPath)
            if fileAbsPath == refFileAbsPath:
                self.sources_list_widget.takeItem(index)
                break

        self.loadMetadataReviewBox()
        self.loadLayers()

    def finishUpload(self, numSuccess, numCancelled, numFailed):
        self.button(QWizard.FinishButton).setVisible(True)

        self.bar2.clearWidgets()
        self.bar2.pushMessage(
            'Upload Result',
            'Success:{0} Cancel:{1} Fail:{2}'.format(
                numSuccess, numCancelled, numFailed),
            level=QgsMessageBar.INFO)
class DialogExportData(QDialog, DIALOG_UI):
    on_result = pyqtSignal(
        bool)  # whether the tool was run successfully or not

    ValidExtensions = ['xtf', 'itf', 'gml', 'xml']
    current_row_schema = 0

    def __init__(self, iface, qgis_utils, conn_manager, context):
        QDialog.__init__(self)
        self.setupUi(self)

        QgsGui.instance().enableAutoGeometryRestore(self)
        self.iface = iface
        self.conn_manager = conn_manager
        self.db_source = context.get_db_sources()[0]
        self.db = self.conn_manager.get_db_connector_from_source(
            self.db_source)
        self.qgis_utils = qgis_utils
        self.logger = Logger()

        self.java_utils = JavaUtils()
        self.java_utils.download_java_completed.connect(
            self.download_java_complete)
        self.java_utils.download_java_progress_changed.connect(
            self.download_java_progress_change)

        self.base_configuration = BaseConfiguration()
        self.ilicache = IliCache(self.base_configuration)
        self.ilicache.refresh()

        self._dbs_supported = ConfigDbSupported()
        self._running_tool = False

        # There may be 1 case where we need to emit a db_connection_changed from the Export Data dialog:
        #   1) Connection Settings was opened and the DB conn was changed.
        self._db_was_changed = False  # To postpone calling refresh gui until we close this dialog instead of settings

        # Similarly, we could call a refresh on layers and relations cache in 1 case:
        #   1) If the ED dialog was called for the COLLECTED source: opening Connection Settings and changing the DB
        #      connection.
        self._schedule_layers_and_relations_refresh = False

        # We need bar definition above calling clear_messages
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.xtf_file_browse_button.clicked.connect(
            make_save_file_selector(
                self.xtf_file_line_edit,
                title=QCoreApplication.translate("DialogExportData",
                                                 "Save in XTF Transfer File"),
                file_filter=QCoreApplication.translate(
                    "DialogExportData",
                    "XTF Transfer File (*.xtf);;Interlis 1 Transfer File (*.itf);;XML (*.xml);;GML (*.gml)"
                ),
                extension='.xtf',
                extensions=['.' + ext for ext in self.ValidExtensions]))
        self.xtf_file_browse_button.clicked.connect(
            self.xtf_browser_opened_to_true)
        self.xtf_browser_was_opened = False

        self.validators = Validators()
        fileValidator = FileValidator(
            pattern=['*.' + ext for ext in self.ValidExtensions],
            allow_non_existing=True)
        self.xtf_file_line_edit.setPlaceholderText(
            QCoreApplication.translate("DialogExportData",
                                       "[Name of the XTF to be created]"))
        self.xtf_file_line_edit.setValidator(fileValidator)
        self.xtf_file_line_edit.textChanged.connect(
            self.validators.validate_line_edits)
        self.xtf_file_line_edit.textChanged.connect(
            self.xtf_browser_opened_to_false)
        self.xtf_file_line_edit.textChanged.emit(
            self.xtf_file_line_edit.text())

        self.connection_setting_button.clicked.connect(self.show_settings)

        self.connection_setting_button.setText(
            QCoreApplication.translate("DialogExportData",
                                       "Connection Settings"))

        # LOG
        self.log_config.setTitle(
            QCoreApplication.translate("DialogExportData", "Show log"))
        self.log_config.setFlat(True)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self._accept_button = self.buttonBox.addButton(
            QCoreApplication.translate("DialogExportData", "Export data"),
            QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(QDialogButtonBox.Help)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.update_connection_info()
        self.update_model_names()
        self.restore_configuration()

    def update_connection_info(self):
        db_description = self.db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(self.db.get_display_conn_string())
            self._accept_button.setEnabled(True)
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("DialogExportData",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')
            self._accept_button.setEnabled(False)

    def update_model_names(self):
        self.export_models_qmodel = QStandardItemModel()

        model_names = self.db.get_models()

        if model_names:
            for model_name in model_names:
                if model_name not in LADMNames.DEFAULT_HIDDEN_MODELS:
                    item = QStandardItem(model_name)
                    item.setCheckable(False)
                    item.setEditable(False)
                    self.export_models_qmodel.appendRow(item)

        self.export_models_list_view.setModel(self.export_models_qmodel)

    def reject(self):
        if self._running_tool:
            QMessageBox.information(
                self, QCoreApplication.translate("DialogExportData",
                                                 "Warning"),
                QCoreApplication.translate(
                    "DialogExportData",
                    "The Export Data tool is still running. Please wait until it finishes."
                ))
        else:
            self.close_dialog()

    def close_dialog(self):
        """
        We use this method to be safe when emitting the db_connection_changed, otherwise we could trigger slots that
        unload the plugin, destroying dialogs and thus, leading to crashes.
        """
        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.connect(
                self.qgis_utils.cache_layers_and_relations)

        if self._db_was_changed:
            # If the db was changed, it implies it complies with ladm_col, hence the second parameter
            self.conn_manager.db_connection_changed.emit(
                self.db, True, self.db_source)

        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.disconnect(
                self.qgis_utils.cache_layers_and_relations)

        self.logger.info(__name__, "Dialog closed.")
        self.done(QDialog.Accepted)

    def get_ili_models(self):
        ili_models = list()
        for index in range(self.export_models_qmodel.rowCount()):
            item = self.export_models_qmodel.item(index)
            ili_models.append(item.text())
        return ili_models

    def show_settings(self):
        # We only need those tabs related to Model Baker/ili2db operations
        dlg = SettingsDialog(qgis_utils=self.qgis_utils,
                             conn_manager=self.conn_manager)
        dlg.set_db_source(self.db_source)
        dlg.set_tab_pages_list(
            [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX])

        # Connect signals (DBUtils, QgisUtils)
        dlg.db_connection_changed.connect(self.db_connection_changed)
        if self.db_source == COLLECTED_DB_SOURCE:
            self._schedule_layers_and_relations_refresh = True

        dlg.set_action_type(EnumDbActionType.EXPORT)

        if dlg.exec_():
            self.db = dlg.get_db_connection()
            self.update_model_names()
            self.update_connection_info()

    def db_connection_changed(self, db, ladm_col_db, db_source):
        self._db_was_changed = True
        self.clear_messages()

    def accepted(self):
        self._running_tool = True
        self.bar.clearWidgets()

        java_home_set = self.java_utils.set_java_home()
        if not java_home_set:
            message_java = QCoreApplication.translate(
                "DialogExportData",
                """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION)
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()
            self.txtStdout.setText(message_java)
            self.java_utils.get_java_on_demand()
            self.disable()
            return

        configuration = self.update_configuration()

        if not self.xtf_file_line_edit.validator().validate(
                configuration.xtffile, 0)[0] == QValidator.Acceptable:
            self._running_tool = False
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid XTF file before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.get_ili_models():
            self._running_tool = False
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid schema to export. This schema does not have information to export."
            )
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        if not configuration.iliexportmodels:
            self._running_tool = False
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a model before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        # If xtf browser was opened and the file exists, the user already chose
        # to overwrite the file
        if os.path.isfile(self.xtf_file_line_edit.text().strip()
                          ) and not self.xtf_browser_was_opened:
            self.msg = QMessageBox()
            self.msg.setIcon(QMessageBox.Warning)
            self.msg.setText(
                QCoreApplication.translate(
                    "DialogExportData",
                    "{filename} already exists.\nDo you want to replace it?").
                format(filename=os.path.basename(
                    self.xtf_file_line_edit.text().strip())))
            self.msg.setWindowTitle(
                QCoreApplication.translate("DialogExportData",
                                           "Save in XTF Transfer File"))
            self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            msg_box = self.msg.exec_()
            if msg_box == QMessageBox.No:
                self._running_tool = False
                return

        with OverrideCursor(Qt.WaitCursor):
            self.progress_bar.show()

            self.disable()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()

            exporter = iliexporter.Exporter()

            db_factory = self._dbs_supported.get_db_factory(self.db.engine)

            exporter.tool = db_factory.get_mbaker_db_ili_mode()
            exporter.configuration = configuration

            self.save_configuration(configuration)

            exporter.stdout.connect(self.print_info)
            exporter.stderr.connect(self.on_stderr)
            exporter.process_started.connect(self.on_process_started)
            exporter.process_finished.connect(self.on_process_finished)

            self.progress_bar.setValue(25)

            try:
                if exporter.run() != iliexporter.Exporter.SUCCESS:
                    self._running_tool = False
                    self.show_message(
                        QCoreApplication.translate(
                            "DialogExportData",
                            "An error occurred when exporting the data. For more information see the log..."
                        ), Qgis.Warning)
                    self.on_result.emit(
                        False
                    )  # Inform other classes that the execution was not successful
                    return
            except JavaNotFoundError:
                self._running_tool = False
                message_error_java = QCoreApplication.translate(
                    "DialogExportData",
                    "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
                ).format(JAVA_REQUIRED_VERSION)
                self.txtStdout.setTextColor(QColor('#000000'))
                self.txtStdout.clear()
                self.txtStdout.setText(message_error_java)
                self.show_message(message_error_java, Qgis.Warning)
                return

            self._running_tool = False
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.progress_bar.setValue(100)
            self.show_message(
                QCoreApplication.translate(
                    "DialogExportData",
                    "Export of the data was successfully completed."),
                Qgis.Success)
            self.on_result.emit(
                True)  # Inform other classes that the execution was successful

    def download_java_complete(self):
        self.accepted()

    def download_java_progress_change(self, progress):
        self.progress_bar.setValue(progress / 2)
        if (progress % 20) == 0:
            self.txtStdout.append('...')

    def save_configuration(self, configuration):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export',
            configuration.xtffile)
        settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log',
                          not self.log_config.isCollapsed())

    def restore_configuration(self):
        settings = QSettings()
        self.xtf_file_line_edit.setText(
            settings.value(
                'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export'))

        # Show log
        value_show_log = settings.value(
            'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool)
        self.log_config.setCollapsed(not value_show_log)

        # set model repository
        # if there is no option by default use online model repository
        custom_model_is_checked = settings.value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        if custom_model_is_checked:
            self.custom_model_directories = settings.value(
                'Asistente-LADM_COL/models/custom_models', DEFAULT_MODELS_DIR)

    def update_configuration(self):
        """
        Get the configuration that is updated with the user configuration changes on the dialog.
        :return: Configuration
        """
        db_factory = self._dbs_supported.get_db_factory(self.db.engine)

        configuration = ExportConfiguration()
        db_factory.set_ili2db_configuration_params(self.db.dict_conn_params,
                                                   configuration)

        configuration.xtffile = self.xtf_file_line_edit.text().strip()
        full_java_exe_path = JavaUtils.get_full_java_exe_path()
        if full_java_exe_path:
            self.base_configuration.java_path = full_java_exe_path

        # User could have changed the default values
        self.use_local_models = QSettings().value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        self.custom_model_directories = QSettings().value(
            'Asistente-LADM_COL/models/custom_models', DEFAULT_MODELS_DIR)

        # Check custom model directories
        if self.use_local_models:
            if not self.custom_model_directories:
                self.base_configuration.custom_model_directories_enabled = False
            else:
                self.base_configuration.custom_model_directories = self.custom_model_directories
                self.base_configuration.custom_model_directories_enabled = True

        configuration.base_configuration = self.base_configuration
        if self.get_ili_models():
            configuration.iliexportmodels = ';'.join(self.get_ili_models())
            configuration.ilimodels = ';'.join(self.get_ili_models())

        configuration.disable_validation = not QSettings().value(
            'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting',
            True, bool)

        return configuration

    def print_info(self, text, text_color='#000000', clear=False):
        self.txtStdout.setTextColor(QColor(text_color))
        self.txtStdout.append(text)
        QCoreApplication.processEvents()

    def on_stderr(self, text):
        color_log_text(text, self.txtStdout)
        self.advance_progress_bar_by_text(text)

    def on_process_started(self, command):
        self.disable()
        self.txtStdout.setTextColor(QColor('#000000'))
        self.txtStdout.clear()
        self.txtStdout.setText(command)
        QCoreApplication.processEvents()

    def on_process_finished(self, exit_code, result):
        color = '#004905' if exit_code == 0 else '#aa2222'
        self.txtStdout.setTextColor(QColor(color))
        self.txtStdout.append(
            QCoreApplication.translate("DialogExportData",
                                       "Finished ({})").format(exit_code))
        if result == iliexporter.Exporter.SUCCESS:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.enable()

            # Open log
            if self.log_config.isCollapsed():
                self.log_config.setCollapsed(False)

    def advance_progress_bar_by_text(self, text):
        if text.strip() == 'Info: compile models...':
            self.progress_bar.setValue(50)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: process data...':
            self.progress_bar.setValue(55)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: first validation pass...':
            self.progress_bar.setValue(70)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: second validation pass...':
            self.progress_bar.setValue(85)
            QCoreApplication.processEvents()

    def clear_messages(self):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.txtStdout.clear()  # Clear previous log messages
        self.progress_bar.setValue(0)  # Initialize progress bar

    def show_help(self):
        self.qgis_utils.show_help("export_data")

    def disable(self):
        self.db_config.setEnabled(False)
        self.ili_config.setEnabled(False)
        self.buttonBox.setEnabled(False)

    def enable(self):
        self.db_config.setEnabled(True)
        self.ili_config.setEnabled(True)
        self.buttonBox.setEnabled(True)

    def show_message(self, message, level):
        if level == Qgis.Warning:
            self.enable()

        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)

    def xtf_browser_opened_to_true(self):
        """
        Slot. Sets a flag to true to eventually avoid asking a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = True

    def xtf_browser_opened_to_false(self):
        """
        Slot. Sets a flag to false to eventually ask a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = False
        self.clear_messages()
Exemple #16
0
class NmapsEngine:
    """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: QgisInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        self.visible = False
        # 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',
                                   'NmapsEngine_{}.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'&Nmaps Engine')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'NmapsEngine')
        self.toolbar.setObjectName(u'NmapsEngine')

    # 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('NmapsEngine', 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
        """

        # Create the dialog (after translation) and keep reference
        self.dlg = NmapsEngineDialog()

        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.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/NmapsEngine/icon.png'
        self.add_action(icon_path,
                        text=self.tr(u'NMap'),
                        callback=self.run,
                        parent=self.iface.mainWindow())

        self.dlg.pushButton.clicked.connect(self.create_nmap)
        self.dlg.listWidget.itemDoubleClicked.connect(self.item_click)
        self.bar = QgsMessageBar(self.dlg)
        QObject.connect(self.iface.mapCanvas(), SIGNAL("layersChanged()"),
                        self.nmap_connect)
        QgsMapLayerRegistry.instance(
        ).layersWillBeRemoved["QStringList"].connect(self.remove_layer)
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.bar.move(20, 1)
        self.bar.setFixedSize(692, 40)

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(self.tr(u'&Nmaps Engine'), action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def run(self):
        """Run method that performs all the real work"""
        # show the dialog
        self.iface.mainWindow().statusBar().showMessage("...")
        self.dlg.listWidget.clear()
        self.dlg.show()
        self.visible = True
        # Run the dialog event loop
        self.nmap_connect()
        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.
            self.visible = False
            pass

    def remove_layer(self, lyr_id):
        if len(lyr_id) == 1:
            lyr = QgsMapLayerRegistry.instance().mapLayer(
                lyr_id[0])  # returns QgsMapLayer pointer
            lyr_name = lyr.name()
            tl = pickle_db.load_obj()
            for k, v in tl.items():
                if v == lyr_name:
                    del tl[k]
            pickle_db.save_obj(tl)

    def nmap_connect(self):
        if self.visible:
            self.iface.mainWindow().statusBar().clear()
            self.bar.pushInfo("Serwer NMap",
                              "Łączenie z serwerem NMap".decode("utf-8"))
            try:
                token = Authentication(ApiKey(), 'qgis-plugin').authenticate()
                self.nm_token = Token(token)
            except:
                self.bar.pushCritical("Serwer NMap",
                                      "Nie udało się połączyć".decode("utf-8"))
                tkd = TokenDialog(self)
                tkd.dlg()
                return False

            try:
                tilesets = Tilesets(self.nm_token).list()
            except:
                self.bar.pushCritical("Serwer NMap",
                                      "Nie mogę pobrać danych".decode("utf-8"))
                return False

            self.bar.clearWidgets()
            self.bar.pushSuccess("Serwer NMap", "Połączono".decode('utf-8'))
            self.iface.mainWindow().statusBar().showMessage(
                "Połączono".decode("utf-8"))

            tl = pickle_db.load_obj()

            avialble_layers = [
                layer.name()
                for layer in self.iface.legendInterface().layers()
            ]

            self.dlg.listWidget.clear()
            for tileset in tilesets:
                tileset_list_item = TilesetListItem()
                tileset_list_item.setTextUp(tileset.get('id'))
                tileset_list_item.setTextDown(tileset.get('name'))
                if tileset.get('name') in tl and tl[tileset.get(
                        'name')] in avialble_layers:
                    tileset_list_item.setTextMiddle(tl[tileset.get('name')])
                if tileset.get('id') in tl and tl[tileset.get(
                        'id')] in avialble_layers:
                    tileset_list_item.setTextMiddle(tl[tileset.get('id')])
                tileset_list_item.setIcon(None)
                tileset_list_item_widget = QListWidgetItem(self.dlg.listWidget)
                tileset_list_item_widget.setSizeHint(
                    tileset_list_item.sizeHint())
                self.dlg.listWidget.addItem(tileset_list_item_widget)
                self.dlg.listWidget.setItemWidget(tileset_list_item_widget,
                                                  tileset_list_item)

            self.bar.clearWidgets()
            self.bar.pushSuccess(
                "Serwer NMap",
                "Połączono i pobrano listę zestawów".decode('utf-8'))

            self.timer = QTimer()
            self.timer.timeout.connect(self.add_notif)
            self.timer.start(4000)

    def item_click(self, item):
        current = item.listWidget().itemWidget(item)
        item_id = current.getTextUp()
        item_label = current.getTextDown()
        TilesetCommonDialog(item_id, self).dlg(item_label)

    def create_nmap(self):
        TilesetCreateDialog(self).dlg()

    def add_notif(self):
        tileset_req = Tilesets(self.nm_token)
        notifications = tileset_req.jobs()
        self.bar.clearWidgets()
        for notification in notifications:
            if notification.get('status') == 'waiting':
                self.bar.pushInfo(
                    "Serwer NMap", "Oczekiwanie  %s %d %%".decode('utf-8') %
                    (notification.get('originalname'),
                     notification.get('progress')))
            if notification.get('status') == 'processing':
                self.bar.pushInfo(
                    "Serwer NMap", "Kafelkowanie  %s %d %%".decode('utf-8') %
                    (notification.get('originalname'),
                     notification.get('progress')))
            if notification.get('status') == 'success':
                self.bar.pushSuccess(
                    "Serwer NMap", "Ukończono %s".decode('utf-8') %
                    (notification.get('originalname')))
                self.nmap_connect()
                tileset_req.hide(notification.get('job_id'))
            if notification.get('status') == 'error':
                self.bar.pushCritical(
                    "Serwer NMap", "Błąd %s".decode('utf-8') %
                    (notification.get('originalname')))
                tileset_req.hide(notification.get('job_id'))
class ResourceSharingDialog(QDialog, FORM_CLASS):
    TAB_ALL = 0
    TAB_INSTALLED = 1
    TAB_SETTINGS = 2

    def __init__(self, parent=None, iface=None):
        """Constructor.

        :param parent: Optional widget to use as parent
        :type parent: QWidget

        :param iface: An instance of QGisInterface
        :type iface: QGisInterface
        """
        super(ResourceSharingDialog, self).__init__(parent)
        self.setupUi(self)
        self.iface = iface

        # Reconfigure UI
        self.setModal(True)
        self.button_edit.setEnabled(False)
        self.button_delete.setEnabled(False)
        self.button_install.setEnabled(False)
        self.button_open.setEnabled(False)
        self.button_uninstall.setEnabled(False)

        # Set QListWidgetItem
        # All
        icon_all = QIcon()
        icon_all.addFile(
            resources_path('img', 'plugin.svg'),
            QSize(),
            QIcon.Normal,
            QIcon.Off)
        item_all = QListWidgetItem()
        item_all.setIcon(icon_all)
        item_all.setText(self.tr('All'))
        # Installed
        icon_installed = QIcon()
        icon_installed.addFile(
            resources_path('img', 'plugin-installed.svg'),
            QSize(),
            QIcon.Normal,
            QIcon.Off)
        item_installed = QListWidgetItem()
        item_installed.setIcon(icon_installed)
        item_installed.setText(self.tr('Installed'))
        item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        # Settings
        icon_settings = QIcon()
        icon_settings.addFile(
            resources_path('img', 'settings.svg'),
            QSize(),
            QIcon.Normal,
            QIcon.Off)
        item_settings = QListWidgetItem()
        item_settings.setIcon(icon_settings)
        item_settings.setText(self.tr('Settings'))
        item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)

        # Add the list widget item to the widget
        self.menu_list_widget.addItem(item_all)
        self.menu_list_widget.addItem(item_installed)
        self.menu_list_widget.addItem(item_settings)

        # Init the message bar
        self.message_bar = QgsMessageBar(self)
        self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.vlayoutRightColumn.insertWidget(0, self.message_bar)

        # Progress dialog for any long running process
        self.progress_dialog = None

        # Init repository manager
        self.repository_manager = RepositoryManager()
        self.collection_manager = CollectionManager()
        # Collections list view
        self.collections_model = QStandardItemModel(0, 1)
        self.collections_model.sort(0, Qt.AscendingOrder)
        self.collection_proxy = CustomSortFilterProxyModel(self)
        self.collection_proxy.setSourceModel(self.collections_model)
        self.list_view_collections.setModel(self.collection_proxy)
        # Active selected collection
        self._selected_collection_id = None

        # Slots
        self.button_add.clicked.connect(self.add_repository)
        self.button_edit.clicked.connect(self.edit_repository)
        self.button_delete.clicked.connect(self.delete_repository)
        self.button_reload.clicked.connect(self.reload_repositories)
        self.menu_list_widget.currentRowChanged.connect(self.set_current_tab)
        self.list_view_collections.selectionModel().currentChanged.connect(
            self.on_list_view_collections_clicked)
        self.line_edit_filter.textChanged.connect(self.filter_collections)
        self.button_install.clicked.connect(self.install_collection)
        self.button_open.clicked.connect(self.open_collection)
        self.button_uninstall.clicked.connect(self.uninstall_collection)
        self.button_box.button(QDialogButtonBox.Help).clicked.connect(
            self.open_help)

        # Populate repositories widget and collections list view
        self.populate_repositories_widget()
        self.reload_collections_model()

    def set_current_tab(self, index):
        """Set stacked widget based on active tab.

        :param index: The index of the active list widget item.
        :type index: int
        """
        # Clear message bar first
        self.message_bar.clearWidgets()
        if index == (self.menu_list_widget.count() - 1):
            # Switch to settings tab
            self.stacked_menu_widget.setCurrentIndex(1)
        else:
            # Switch to plugins tab
            if index == 1:
                # Installed
                self.collection_proxy.accepted_status = \
                    COLLECTION_INSTALLED_STATUS
                # Set the web view
                title = self.tr('Installed Collections')
                description = self.tr(
                    'On the left you see the list of all collections '
                    'installed on your QGIS')
            else:
                # All
                self.collection_proxy.accepted_status = COLLECTION_ALL_STATUS
                # Set the web view
                title = self.tr('All Collections')
                description = self.tr(
                    'On the left you see the list of all collections '
                    'available from the repositories registered in the '
                    'settings.')

            context = {
                'resources_path': resources_path(),
                'title': title,
                'description': description
            }
            self.web_view_details.setHtml(
                render_template('tab_description.html', context))
            self.stacked_menu_widget.setCurrentIndex(0)

    def add_repository(self):
        """Open add repository dialog."""
        dlg = ManageRepositoryDialog(self)
        if not dlg.exec_():
            return

        for repo in self.repository_manager.directories.values():
            if dlg.line_edit_url.text().strip() == repo['url']:
                self.message_bar.pushMessage(
                    self.tr(
                        'Unable to add another repository with the same URL!'),
                    Qgis.Critical, 5)
                return

        repo_name = dlg.line_edit_name.text()
        repo_url = dlg.line_edit_url.text().strip()
        repo_auth_cfg = dlg.line_edit_auth_id.text().strip()
        if repo_name in self.repository_manager.directories:
            repo_name += '(2)'

        # Show progress dialog
        self.show_progress_dialog("Fetching repository's metadata")

        # Add repository
        try:
            status, description = self.repository_manager.add_directory(
                repo_name, repo_url, repo_auth_cfg)
            if status:
                self.message_bar.pushMessage(
                    self.tr(
                        'Repository is successfully added'),
                    Qgis.Success, 5)
            else:
                self.message_bar.pushMessage(
                    self.tr(
                        'Unable to add repository: %s') % description,
                    Qgis.Critical, 5)
        except Exception as e:
            self.message_bar.pushMessage(
                self.tr('%s') % e,
                Qgis.Critical, 5)
        finally:
            self.progress_dialog.hide()

        # Reload data and widget
        self.reload_data_and_widget()

        # Deactivate edit and delete button
        self.button_edit.setEnabled(False)
        self.button_delete.setEnabled(False)

    def edit_repository(self):
        """Open edit repository dialog."""
        selected_item = self.tree_repositories.currentItem()
        if selected_item:
            repo_name = selected_item.text(0)

        if not repo_name:
            return

        # Check if it's the approved online dir repository
        settings = QSettings()
        settings.beginGroup(repo_settings_group())
        if settings.value(repo_name + '/url') in \
                self.repository_manager._online_directories.values():
            self.message_bar.pushMessage(
                self.tr(
                    'You can not edit the official repositories!'),
                Qgis.Warning, 5)
            return

        dlg = ManageRepositoryDialog(self)
        dlg.line_edit_name.setText(repo_name)
        dlg.line_edit_url.setText(
            self.repository_manager.directories[repo_name]['url'])
        dlg.line_edit_auth_id.setText(
            self.repository_manager.directories[repo_name]['auth_cfg'])

        if not dlg.exec_():
            return

        # Check if the changed URL is already there in the repo
        new_url = dlg.line_edit_url.text().strip()
        old_url = self.repository_manager.directories[repo_name]['url']
        for repo in self.repository_manager.directories.values():
            if new_url == repo['url'] and (old_url != new_url):
                self.message_bar.pushMessage(
                    self.tr('Unable to add another repository with the same '
                            'URL!'),
                    Qgis.Critical, 5)
                return

        new_name = dlg.line_edit_name.text()
        if (new_name in self.repository_manager.directories) and (
                    new_name != repo_name):
            new_name += '(2)'

        new_auth_cfg = dlg.line_edit_auth_id.text()

        # Show progress dialog
        self.show_progress_dialog("Fetching repository's metadata")

        # Edit repository
        try:
            status, description = self.repository_manager.edit_directory(
                repo_name,
                new_name,
                old_url,
                new_url,
                new_auth_cfg
            )
            if status:
                self.message_bar.pushMessage(
                    self.tr('Repository is successfully updated'),
                    Qgis.Success, 5)
            else:
                self.message_bar.pushMessage(
                    self.tr('Unable to add repository: %s') % description,
                    Qgis.Critical, 5)
        except Exception as e:
            self.message_bar.pushMessage(
                self.tr('%s') % e, Qgis.Critical, 5)
        finally:
            self.progress_dialog.hide()

        # Reload data and widget
        self.reload_data_and_widget()

        # Deactivate edit and delete button
        self.button_edit.setEnabled(False)
        self.button_delete.setEnabled(False)

    def delete_repository(self):
        """Delete a repository in the tree widget."""
        selected_item = self.tree_repositories.currentItem()
        if selected_item:
            repo_name = selected_item.text(0)

        if not repo_name:
            return
        # Check if it's the approved online dir repository
        repo_url = self.repository_manager.directories[repo_name]['url']
        if repo_url in self.repository_manager._online_directories.values():
            self.message_bar.pushMessage(
                self.tr(
                    'You can not remove the official repositories!'),
                Qgis.Warning, 5)
            return

        warning = self.tr('Are you sure you want to remove the following '
                          'repository?') + '\n' + repo_name
        if QMessageBox.warning(
                self,
                self.tr('QGIS Resource Sharing'),
                warning,
                QMessageBox.Yes,
                QMessageBox.No) == QMessageBox.No:
            return

        # Remove repository
        installed_collections = \
            self.collection_manager.get_installed_collections(repo_url)
        if installed_collections:
            message = ('You have some installed collections from this '
                       'repository. Please uninstall them first!')
            self.message_bar.pushMessage(message, Qgis.Warning, 5)
        else:
            self.repository_manager.remove_directory(repo_name)
            # Reload data and widget
            self.reload_data_and_widget()
            # Deactivate edit and delete button
            self.button_edit.setEnabled(False)
            self.button_delete.setEnabled(False)

    def reload_repositories(self):
        """Slot for when user clicks reload repositories button."""
        # Show progress dialog
        self.show_progress_dialog('Reloading all repositories')

        for repo_name in self.repository_manager.directories:
            directory = self.repository_manager.directories[repo_name]
            url = directory['url']
            auth_cfg = directory['auth_cfg']
            try:
                status, description = self.repository_manager.reload_directory(
                    repo_name, url, auth_cfg)
                if status:
                    self.message_bar.pushMessage(
                        self.tr(
                            'Repository %s is successfully reloaded') %
                        repo_name, Qgis.Info, 5)
                else:
                    self.message_bar.pushMessage(
                        self.tr(
                            'Unable to reload %s: %s') % (
                            repo_name, description),
                        Qgis.Critical, 5)
            except Exception as e:
                self.message_bar.pushMessage(
                    self.tr('%s') % e,
                    Qgis.Critical, 5)

        self.progress_dialog.hide()
        # Reload data and widget
        self.reload_data_and_widget()

    def install_collection(self):
        """Slot for when user clicks download button."""
        self.show_progress_dialog('Starting installation process...')
        self.progress_dialog.canceled.connect(self.install_canceled)

        self.installer_thread = QThread()
        self.installer_worker = CollectionInstaller(
            self.collection_manager, self._selected_collection_id)
        self.installer_worker.moveToThread(self.installer_thread)
        self.installer_worker.finished.connect(self.install_finished)
        self.installer_worker.aborted.connect(self.install_aborted)
        self.installer_worker.progress.connect(self.install_progress)
        self.installer_thread.started.connect(self.installer_worker.run)
        self.installer_thread.start()

    def install_finished(self):
        # Process the result
        self.progress_dialog.hide()
        if self.installer_worker.install_status:
            self.reload_collections_model()
            message = '%s is installed successfully' % (
                config.COLLECTIONS[self._selected_collection_id]['name'])
        else:
            message = self.installer_worker.error_message
        QMessageBox.information(self, 'Resource Sharing', message)
        # Clean up the worker and thread
        self.installer_worker.deleteLater()
        self.installer_thread.quit()
        self.installer_thread.wait()
        self.installer_thread.deleteLater()

    def install_canceled(self):
        self.progress_dialog.hide()
        self.show_progress_dialog('Cancelling installation...')
        self.installer_worker.abort()

    def install_aborted(self):
        if self.installer_thread.isRunning():
            self.installer_thread.quit()
        self.installer_thread.finished.connect(self.progress_dialog.hide)

    def install_progress(self, text):
        self.progress_dialog.setLabelText(text)

    def uninstall_collection(self):
        """Slot called when user clicks uninstall button."""
        try:
            self.collection_manager.uninstall(self._selected_collection_id)
        except Exception as e:
            raise
        self.reload_collections_model()
        QMessageBox.information(
            self,
            'Resource Sharing',
            'The collection is uninstalled succesfully!')

    def open_collection(self):
        """Slot for when user clicks 'Open' button."""
        collection_path = local_collection_path(self._selected_collection_id)
        directory_url = QUrl.fromLocalFile(collection_path)
        QDesktopServices.openUrl(directory_url)

    def reload_data_and_widget(self):
        """Reload repositories and collections and update widgets related."""
        self.reload_repositories_widget()
        self.reload_collections_model()

    def reload_repositories_widget(self):
        """Refresh tree repositories using new repositories data."""
        self.repository_manager.load_directories()
        self.populate_repositories_widget()

    def populate_repositories_widget(self):
        """Populate the current dictionary repositories to the tree widget."""
        # Clear the current tree widget
        self.tree_repositories.clear()

        # Export the updated ones from the repository manager
        for repo_name in self.repository_manager.directories:
            url = self.repository_manager.directories[repo_name]['url']
            item = QTreeWidgetItem(self.tree_repositories)
            item.setText(0, repo_name)
            item.setText(1, url)
        self.tree_repositories.resizeColumnToContents(0)
        self.tree_repositories.resizeColumnToContents(1)
        self.tree_repositories.sortItems(1, Qt.AscendingOrder)

    def reload_collections_model(self):
        """Reload the collections model with the current collections."""
        self.collections_model.clear()
        for id in config.COLLECTIONS:
            collection_name = config.COLLECTIONS[id]['name']
            collection_author = config.COLLECTIONS[id]['author']
            collection_tags = config.COLLECTIONS[id]['tags']
            collection_description = config.COLLECTIONS[id]['description']
            collection_status = config.COLLECTIONS[id]['status']
            item = QStandardItem(collection_name)
            item.setEditable(False)
            item.setData(id, COLLECTION_ID_ROLE)
            item.setData(collection_name, COLLECTION_NAME_ROLE)
            item.setData(collection_description, COLLECTION_DESCRIPTION_ROLE)
            item.setData(collection_author, COLLECTION_AUTHOR_ROLE)
            item.setData(collection_tags, COLLECTION_TAGS_ROLE)
            item.setData(collection_status, COLLECTION_STATUS_ROLE)
            self.collections_model.appendRow(item)
        self.collections_model.sort(0, Qt.AscendingOrder)

    def on_tree_repositories_itemSelectionChanged(self):
        """Slot for when the itemSelectionChanged signal emitted."""
        # Activate edit and delete button
        self.button_edit.setEnabled(True)
        self.button_delete.setEnabled(True)

    def on_list_view_collections_clicked(self, index):
        """Slot for when the list_view_collections is clicked."""
        real_index = self.collection_proxy.mapToSource(index)
        if real_index.row() != -1:
            collection_item = self.collections_model.itemFromIndex(real_index)
            collection_id = collection_item.data(COLLECTION_ID_ROLE)
            self._selected_collection_id = collection_id

            # Enable/disable button
            status = config.COLLECTIONS[self._selected_collection_id]['status']
            is_installed = status == COLLECTION_INSTALLED_STATUS
            if is_installed:
                self.button_install.setEnabled(True)
                self.button_install.setText('Reinstall')
                self.button_open.setEnabled(True)
                self.button_uninstall.setEnabled(True)
            else:
                self.button_install.setEnabled(True)
                self.button_install.setText('Install')
                self.button_open.setEnabled(False)
                self.button_uninstall.setEnabled(False)

            # Show  metadata
            self.show_collection_metadata(collection_id)

    @pyqtSlot(str)
    def filter_collections(self, text):
        search = QRegExp(
            text,
            Qt.CaseInsensitive,
            QRegExp.RegExp)
        self.collection_proxy.setFilterRegExp(search)

    def show_collection_metadata(self, id):
        """Show the collection metadata given the id."""
        html = self.collection_manager.get_html(id)
        self.web_view_details.setHtml(html)

    def reject(self):
        """Slot when the dialog is closed."""
        # Serialize collections to settings
        self.repository_manager.serialize_repositories()
        self.done(0)

    def open_help(self):
        """Open help."""
        doc_url = QUrl('http://www.akbargumbira.com/qgis_resources_sharing')
        QDesktopServices.openUrl(doc_url)

    def show_progress_dialog(self, text):
        """Show infinite progress dialog with given text.

        :param text: Text as the label of the progress dialog
        :type text: str
        """
        if self.progress_dialog is None:
            self.progress_dialog = QProgressDialog(self)
            self.progress_dialog.setWindowModality(Qt.WindowModal)
            self.progress_dialog.setAutoClose(False)
            title = self.tr('Resource Sharing')
            self.progress_dialog.setWindowTitle(title)
            # Just use infinite progress bar here
            self.progress_dialog.setMaximum(0)
            self.progress_dialog.setMinimum(0)
            self.progress_dialog.setValue(0)
            self.progress_dialog.setLabelText(text)

        self.progress_dialog.show()
class GetDBOrSchemaNameDialog(QDialog, DIALOG_UI):
    db_or_schema_created = pyqtSignal(str)

    def __init__(self, db_connector, uri, type, parent=None):
        """
        Constructor
        :param db: database connection instance
        :param type: type of parameter to capture (database or schema)
        :param parent: parent of dialog
        """
        QDialog.__init__(self, parent)

        self.type = type
        self.db_connector = db_connector
        self.uri = uri
        self.parent = parent
        self.setupUi(self)

        if self.type == 'database':
            self.message_label.setText(
                QCoreApplication.translate("GetDBOrSchemaNameDialog",
                                           "Enter the name of the database:"))
            self.parameter_line_edit.setPlaceholderText(
                QCoreApplication.translate(
                    "GetDBOrSchemaNameDialog",
                    "[Name of the database to be created]"))
        else:
            self.message_label.setText(
                QCoreApplication.translate("GetDBOrSchemaNameDialog",
                                           "Enter the name of the schema:"))
            self.parameter_line_edit.setPlaceholderText(
                QCoreApplication.translate(
                    "GetDBOrSchemaNameDialog",
                    "[Name of the schema to be created]"))

        self.setWindowTitle(
            QCoreApplication.translate("GetDBOrSchemaNameDialog",
                                       "Create {type}").format(type=self.type))
        self.validators = Validators()

        # schema name mustn't have special characters
        regex = QRegExp("[a-zA-Z0-9_]+")
        validator = QRegExpValidator(regex)
        self.parameter_line_edit.setValidator(validator)
        self.parameter_line_edit.setMaxLength(63)
        self.parameter_line_edit.textChanged.connect(
            self.validators.validate_line_edits_lower_case)
        self.parameter_line_edit.textChanged.emit(
            self.parameter_line_edit.text())

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self.buttonBox.addButton(
            QCoreApplication.translate("GetDBOrSchemaNameDialog",
                                       "Create {type}").format(type=self.type),
            QDialogButtonBox.AcceptRole)

    def accepted(self):
        parameter_value = self.parameter_line_edit.text().strip()
        if not parameter_value:
            if self.type == 'database':
                self.show_message(
                    QCoreApplication.translate(
                        "GetDBOrSchemaNameDialog",
                        "The name of the database cannot be empty."),
                    Qgis.Warning)
            else:
                self.show_message(
                    QCoreApplication.translate(
                        "GetDBOrSchemaNameDialog",
                        "The name of the schema cannot be empty."),
                    Qgis.Warning)
            return

        tmp_db_conn = self.db_connector
        self.buttonBox.setEnabled(False)
        self.parameter_line_edit.setEnabled(False)

        with OverrideCursor(Qt.WaitCursor):
            if self.type == 'database':
                db_name = parameter_value
                result = tmp_db_conn.create_database(self.uri, db_name)
            elif self.type == 'schema':
                schema_name = parameter_value
                result = tmp_db_conn.create_schema(self.uri, schema_name)

        if result[0]:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.show_message(result[1], Qgis.Success)

            # signal updating the list of databases or schemas
            self.db_or_schema_created.emit(parameter_value)
        else:
            self.show_message(result[1], Qgis.Warning)
            self.buttonBox.setEnabled(True)
            self.parameter_line_edit.setEnabled(True)

    def show_message(self, message, level):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)
Exemple #19
0
class STUploadFileDialog(QDialog, DIALOG_TRANSITION_SYSTEM_UI):
    on_result = pyqtSignal(
        bool)  # whether the tool was run successfully or not

    def __init__(self, request_id, supply_type, parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)

        self.request_id = request_id
        self.supply_type = supply_type
        self.st_utils = STUtils()

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.upload_file)
        self.buttonBox.helpRequested.connect(self.show_help)
        self.btn_browse_file.clicked.connect(
            make_file_selector(
                self.txt_file_path,
                QCoreApplication.translate(
                    "STUploadFileDialog",
                    "Select the file you want to upload to the transitional system"
                ),
                QCoreApplication.translate(
                    "STUploadFileDialog",
                    'INTERLIS 2 transfer format (*.xtf)')))

        self.initialize_progress()

        self.restore_settings()

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def upload_file(self):
        self.bar.clearWidgets()
        self.start_progress()
        self.enable_controls(False)

        file_path = self.txt_file_path.text().strip()
        if not self.txt_comments.toPlainText():
            res = False
            res_msg = QCoreApplication.translate(
                "STUploadFileDialog",
                "File was not uploaded! Details: Comments are required.")
        elif os.path.isfile(file_path):
            zip_file_path = Utils.compress_file(file_path)
            with ProcessWithStatus(
                    QCoreApplication.translate(
                        "STUploadFileDialog",
                        "Uploading file to ST server...")):
                res, res_msg = self.st_utils.upload_file(
                    self.request_id, self.supply_type, zip_file_path,
                    self.txt_comments.toPlainText())
        else:
            res = False
            res_msg = QCoreApplication.translate(
                "STUploadFileDialog",
                "The file '{}' does not exist!").format(file_path)

        self.show_message(res_msg, Qgis.Success if res else Qgis.Warning)

        self.initialize_progress()

        if res:
            self.store_settings()
        else:
            self.enable_controls(True)  # Prepare next run

        self.on_result.emit(
            res)  # Inform other classes if the execution was successful

        return  # Do not close dialog

    def show_message(self, message, level):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, 0)

    def store_settings(self):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_export',
            self.txt_file_path.text().strip())

    def restore_settings(self):
        settings = QSettings()
        self.txt_file_path.setText(
            settings.value(
                'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_export'))

    def start_progress(self):
        self.progress.setVisible(True)
        self.progress.setRange(0, 0)

    def initialize_progress(self):
        self.progress.setVisible(False)

    def enable_controls(self, enable):
        self.gbx_page_1.setEnabled(enable)
        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable)

    def show_help(self):
        show_plugin_help('transitional_system_upload_file')
class ImgUploaderWizard(QtGui.QWizard, FORM_CLASS):

    def __init__(self, iface, settings, parent=None):
        """Constructor."""
        super(ImgUploaderWizard, self).__init__(parent)
        # Set up the user interface from Designer.
        # After 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.iface = iface
        self.setupUi(self)

        # Message bars need to be attached to pages, since the wizard object
        # does not have a layout. It doesn't work to attach the same bar
        # object to all pages (it is only shown in the last one). The only way
        # I could make it work was to create different QgsMessageBar objects,
        # one per page, but it is very to keep track of those references
        # along the code. It is messy, there should be a better solution.

        self.bar0 = QgsMessageBar()
        self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(0).layout().addWidget(self.bar0)

        self.bar1 = QgsMessageBar()
        self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(1).layout().addWidget(self.bar1)

        self.bar2 = QgsMessageBar()
        self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(2).layout().addWidget(self.bar2)


        self.setButtonText(QtGui.QWizard.CustomButton1, self.tr("&Start upload"));
        self.setOption(QtGui.QWizard.HaveCustomButton1, True);
        self.button(QWizard.CustomButton1).setVisible(False)

        self.settings = settings

        # Initialize the object for S3Manager and upload options and filenames
        #self.s3Mgr = None
        #self.upload_options = []
        #self.upload_filenames = []
        #self.upload_file_abspaths = []

        self.s3UpPrgWin = None

        # Initialize layers and default settings
        self.loadLayers()
        self.loadMetadataSettings()
        self.loadStorageSettings()
        self.loadOptionsSettings()

        # register event handlers
        self.button(QWizard.BackButton).clicked.connect(self.previousPage)
        self.button(QWizard.NextButton).clicked.connect(self.nextPage)
        self.button(QWizard.FinishButton).clicked.connect(self.finishWizard)
        self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard)

        # Imagery connections (wizard page 1)
        self.layers_tool_button.clicked.connect(self.loadLayers)
        self.file_tool_button.clicked.connect(self.selectFile)
        self.add_source_button.clicked.connect(self.addSources)
        self.remove_source_button.clicked.connect(self.removeSources)
        self.up_source_button.clicked.connect(self.upSource)
        self.down_source_button.clicked.connect(self.downSource)

        # Metadata connections (wizard page 2)
        self.sense_start_edit.setCalendarPopup(1)
        self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm')
        self.sense_end_edit.setCalendarPopup(1)
        self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm')
        self.default_button.clicked.connect(self.loadMetadataSettings)
        self.clean_button.clicked.connect(self.cleanMetadataSettings)
        self.save_button.clicked.connect(self.saveMetadata)

        # Temporarily hide the reload button
        self.reload_button.hide()
        self.reload_button.clicked.connect(self.loadSavedMetadata)

        # temporarily disable notify_oam_check and trigger_tiling_check
        self.notify_oam_check.setEnabled(False)
        self.trigger_tiling_check.setEnabled(False)

        # temporarily disable textEdit for website and tags
        # probably make textEdit for thumbnail later
        self.website_edit.setEnabled(False)
        self.tags_edit.setEnabled(False)

        # Upload tab connections (wizard page 3)
        self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify)
        self.customButtonClicked.connect(self.startUpload)
        #self.button(QWizard.CustomButton1).clicked.connect(self.startUpload)

    # handlers for navigation
    def nextPage(self):
        if self.currentId() == 1:
            #print "Page ID: " + str(self.currentId())
            #self.bar1.clearWidgets()
            pass
        elif self.currentId() == 2:
            #print "Page ID: " + str(self.currentId())
            #self.bar2.clearWidgets()
            self.loadMetadataReviewBox()
            self.button(QWizard.CustomButton1).setVisible(True)
        else:
            pass

    def previousPage(self):
        if self.currentId() == 0:
            #print "Page ID: " + str(self.currentId())
            #self.bar0.clearWidgets()
            pass
        elif self.currentId() == 1:
            #print "Page ID: " + str(self.currentId())
            #self.bar1.clearWidgets()
            self.button(QWizard.CustomButton1).setVisible(False)
        else:
            pass

    def finishWizard(self):
        #print "finish wizard button was clicked."
        pass

    def cancelWizard(self):
        #print "cancel wizard button was clicked."
        # need to display QMessageBox
        if self.s3UpPrgWin != None:
            self.s3UpPrgWin.cancelAllUploads()

    # event handling for wizard page 1
    def loadLayers(self):
        all_layers = self.iface.mapCanvas().layers()
        for layer in all_layers:
            if not self.layers_list_widget.findItems(layer.name(),Qt.MatchExactly):
                if not self.sources_list_widget.findItems(layer.name(),Qt.MatchExactly):
                    item = QListWidgetItem()
                    item.setText(layer.name())
                    item.setData(Qt.UserRole, layer.dataProvider().dataSourceUri())
                    self.layers_list_widget.addItem(item)

    def selectFile(self):
        selected_file = QFileDialog.getOpenFileName(
            self,
            'Select imagery file',
            os.path.expanduser("~"))
        self.source_file_edit.setText(selected_file)

    def addSources(self):
        filename = self.source_file_edit.text()
        selected_layers = self.layers_list_widget.selectedItems()
        if not filename and not selected_layers:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                'WARNING',
                'Either a layer or file should be selected to be added',
                level=QgsMessageBar.WARNING)
        else:
            added = False
            if filename:
                result_val = validate_file(filename)
                if result_val["val"]:
                    if not self.sources_list_widget.findItems(filename,Qt.MatchExactly):
                        item = QListWidgetItem()
                        item.setText(os.path.basename(filename))
                        item.setData(Qt.UserRole, filename)
                        self.sources_list_widget.addItem(item)
                        self.added_sources_list_widget.addItem(item.clone())
                        self.source_file_edit.setText('')
                        added = True
                else:
                    msg = result_val["msg"]
                    self.bar0.clearWidgets()
                    self.bar0.pushMessage("CRITICAL", msg, level=QgsMessageBar.CRITICAL)
                    QgsMessageLog.logMessage("CRITICAL", msg, level=QgsMessageLog.INFO)

            if selected_layers:
                for item in selected_layers:
                    if validate_layer(item.text(), self.iface):
                        if not self.sources_list_widget.findItems(item.text(),Qt.MatchExactly):
                            self.layers_list_widget.takeItem(self.layers_list_widget.row(item))
                            self.sources_list_widget.addItem(item)
                            self.added_sources_list_widget.addItem(item.clone())
                            added = True
                    else:
                        self.bar0.clearWidgets()
                        self.bar0.pushMessage(
                            "CRITICAL",
                            "Vector layers cannot be selected for upload",
                            level=QgsMessageBar.CRITICAL)
            if added:
                self.bar0.clearWidgets()
                self.bar0.pushMessage(
                    'INFO',
                    'Source(s) added to the upload queue',
                    level=QgsMessageBar.INFO)

    def removeSources(self):
        selected_sources = self.sources_list_widget.selectedItems()
        added_sources = self.added_sources_list_widget.selectedItems()
        if selected_sources:
            for item in selected_sources:
                self.sources_list_widget.takeItem(self.sources_list_widget.row(item))
                added_item = self.added_sources_list_widget.findItems(item.text(),Qt.MatchExactly)
                if added_item:
                    self.added_sources_list_widget.takeItem(self.added_sources_list_widget.row(added_item[0]))
                all_layers = self.iface.mapCanvas().layers()
                for layer in all_layers:
                    if item.text() == layer.name():
                        if not self.layers_list_widget.findItems(item.text(),Qt.MatchExactly):
                            self.layers_list_widget.addItem(item)
        else:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                'WARNING',
                'An imagery source must be selected to be removed',
                level=QgsMessageBar.WARNING)

    def upSource(self):
        selected_layers = self.sources_list_widget.selectedItems()
        if selected_layers:
            position = self.sources_list_widget.row(selected_layers[0])
            if position > 0:
                item = self.sources_list_widget.takeItem(position)
                self.sources_list_widget.insertItem(position-1,item)
                item.setSelected(1)

    def downSource(self):
        selected_layers = self.sources_list_widget.selectedItems()
        if selected_layers:
            position = self.sources_list_widget.row(selected_layers[0])
            if position < self.sources_list_widget.count()-1:
                item = self.sources_list_widget.takeItem(position)
                self.sources_list_widget.insertItem(position+1,item)
                item.setSelected(1)

    # event handling for wizard page 2
    # load default values
    def loadMetadataSettings(self):
        self.settings.beginGroup("Metadata")
        self.base_uuid_edit.setText(self.settings.value('BASE_UUID'))
        self.title_edit.setText(self.settings.value('TITLE'))
        if self.settings.value('PLATFORM') == None:
            self.platform_combo_box.setCurrentIndex(0)
        else:
            self.platform_combo_box.setCurrentIndex(
                int(self.settings.value('PLATFORM')))
        self.sensor_edit.setText(self.settings.value('SENSOR'))
        self.sensor_edit.setCursorPosition(0)
        self.sense_start_edit.setDate(QDateTime.fromString(
            self.settings.value('SENSE_START'),
            Qt.ISODate).date())
        self.sense_start_edit.setTime(
            QDateTime.fromString(self.settings.value('SENSE_START'),
            Qt.ISODate).time())
        self.sense_end_edit.setDate(
            QDateTime.fromString(self.settings.value('SENSE_END'),
            Qt.ISODate).date())
        self.sense_end_edit.setTime(
            QDateTime.fromString(self.settings.value('SENSE_END'),
            Qt.ISODate).time())
        self.provider_edit.setText(self.settings.value('PROVIDER'))
        self.provider_edit.setCursorPosition(0)
        self.contact_edit.setText(self.settings.value('CONTACT'))
        self.contact_edit.setCursorPosition(0)
        #self.website_edit.setText(self.settings.value('WEBSITE'))
        #self.website_edit.setCursorPosition(0)
        #self.tags_edit.setText(self.settings.value('TAGS'))
        #self.tags_edit.setCursorPosition(0)
        self.settings.endGroup()

    def cleanMetadataSettings(self):
        self.base_uuid_edit.setText('')
        self.title_edit.setText('')
        self.platform_combo_box.setCurrentIndex(0)
        self.sensor_edit.setText('')
        self.sense_start_edit.setDate(
            QDateTime().fromString('1970-01-01T00:00:00',
            Qt.ISODate).date())
        self.sense_start_edit.setTime(
            QDateTime().fromString('1970-01-01T00:00:00',
            Qt.ISODate).time())
        self.sense_end_edit.setDate(
            QDateTime().fromString('1970-01-01T00:00:00',
            Qt.ISODate).date())
        self.sense_end_edit.setTime(
            QDateTime().fromString('1970-01-01T00:00:00',
            Qt.ISODate).time())
        self.tags_edit.setText('')
        self.provider_edit.setText('')
        self.contact_edit.setText('')
        self.website_edit.setText('')
        self.license_check_box.setCheckState(0)
        self.reproject_check_box.setCheckState(0)

    def saveMetadata(self):
        flag = False
        if self.reproject_check_box.isChecked():
            qMsgBox = QMessageBox()
            qMsgBox.setWindowTitle("Confirmation")
            qMsgBox.setText("You checked the reprojection option, which can require significant \
amount of time. Are you sure to continue?")
            qMsgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
            #qMsgBox.setDefaultButton(QMessageBox.Cancel)

            if qMsgBox.exec_() == QMessageBox.Ok:
                flag = True
        else:
            flag = True

        if flag:
            num_selected_layers = 0
            count = 0
            """Open python console. I haven't indentified the exact reason, but
            messagebar doesn't work properly without opening python console
            and some print statements"""
            pluginMenu = self.iface.pluginMenu()
            #print(repr(pluginMenu))
            for action in pluginMenu.actions():
                if 'Python Console' in action.text():
                    action.trigger()

            self.activateWindow()

            selected_layers = self.added_sources_list_widget.selectedItems()
            if selected_layers:
                num_selected_layers = len(selected_layers)
                for each_layer in selected_layers:
                    file_abspath = each_layer.data(Qt.UserRole)

                    if self.reproject_check_box.isChecked():
                        self.bar1.clearWidgets()
                        # this message doesn't show up without opening python console
                        # need to identify the reason
                        self.bar1.pushMessage(
                            'INFO',
                            'Reprojecting files: %sth image out of %s is being processed...'\
                             % (str(count+1), str(num_selected_layers)),
                            level=QgsMessageBar.INFO)
                        # Isn't it better to use thread?
                        print('Reprojecting {0}'.format(str(os.path.basename(file_abspath))))

                        """ probably, it is better to create a class for reprojection """
                        file_abspath = reproject(file_abspath)

                        print('Add the image as a raster layer...')
                        original_file_name = each_layer.text()
                        reprojected_file_name = '(EPSG3857) ' + original_file_name
                        self.iface.addRasterLayer(file_abspath, reprojected_file_name)

                    else:
                        self.bar1.clearWidgets()
                        self.bar1.pushMessage(
                            'INFO',
                            '%sth image out of %s is processed.'\
                             % (str(count+1), str(num_selected_layers)),
                            level=QgsMessageBar.INFO)


                    """ probably need to insert the codes to create and insert
                    the detailed footprint layer here"""


                    # get metadata from GUI, and store them in a dictionary
                    metaInputInDict = {}
                    temp_filename = file_abspath.split('/')[-1]
                    strUuid = '{0}{1}'.format(self.base_uuid_edit.text(), temp_filename)
                    metaInputInDict['uuid'] = strUuid
                    metaInputInDict['title'] = self.title_edit.text()
                    metaInputInDict['platform'] = self.platform_combo_box.currentText()
                    metaInputInDict['acquisition_start'] = self.sense_start_edit.dateTime().toString(Qt.ISODate)
                    metaInputInDict['acquisition_end'] = self.sense_end_edit.dateTime().toString(Qt.ISODate)
                    metaInputInDict['provider'] = self.provider_edit.text()
                    metaInputInDict['contact'] = self.contact_edit.text()
                    # temporarily disable two keys (website and tags)
                    #metaInputInDict['website'] = self.website_edit.text()
                    #metaInputInDict['tags'] = self.tags_edit.text()

                    properties = {}
                    properties['sensor'] = self.sensor_edit.text()
                    """need to implement thumbnail creation, etc."""
                    properties['thumbnail'] = "currently not available"
                    metaInputInDict['properties'] = properties

                    # extract metadata from GeoTiff, and merge with the metadata from textbox
                    imgMetaHdlr = ImgMetadataHandler(file_abspath)
                    imgMetaHdlr.extractMetaInImagery()
                    metaForUpload = dict(imgMetaHdlr.getMetaInImagery().items() + metaInputInDict.items())
                    strMetaForUpload = str(json.dumps(metaForUpload))

                    #json_file_abspath = os.path.splitext(file_abspath)[0] + '.tif_meta.json'
                    json_file_abspath = file_abspath + '_meta.json'
                    #print json_file_abspath
                    f = open(json_file_abspath,'w')
                    f.write(strMetaForUpload)
                    f.close()
                    count += 1

                    # refresh the list widget and selected items, if reprojection is done
                    if self.reproject_check_box.isChecked():
                        print('update the listWidget to reflect the change...')

                        items_for_remove = self.added_sources_list_widget.findItems(original_file_name, Qt.MatchExactly)
                        self.added_sources_list_widget.takeItem(self.added_sources_list_widget.row(items_for_remove[0]))

                        items_for_remove = self.sources_list_widget.findItems(original_file_name, Qt.MatchExactly)
                        self.sources_list_widget.takeItem(self.sources_list_widget.row(items_for_remove[0]))
                        #self.layers_list_widget.addItem(items_for_remove[0])

                        self.loadLayers()
                        items_to_add = self.layers_list_widget.findItems(reprojected_file_name, Qt.MatchExactly)
                        items_to_add[0].setSelected(True)
                        self.addSources()


                self.bar1.clearWidgets()
                self.bar1.pushMessage(
                    'INFO',
                    'Metadata for the selected sources were saved',
                    level=QgsMessageBar.INFO)

            else:
                self.bar1.clearWidgets()
                self.bar1.pushMessage(
                    'WARNING',
                    'One or more source imagery should be selected to have the metadata saved',
                    level=QgsMessageBar.WARNING)

    def loadSavedMetadata(self):

        qMsgBox = QMessageBox()
        qMsgBox.setText('Under construction:\nMessage from loadSavedMetadata function.')
        qMsgBox.exec_()

    # event handling for wizard page 3
    # please also see startUpload function for event handling of Start Upload button
    def enableSpecify(self):
        if self.storage_combo_box.currentIndex() == 1:
            self.specify_label.setEnabled(1)
            self.specify_edit.setEnabled(1)
        else:
            self.specify_label.setEnabled(0)
            self.specify_edit.setText('')
            self.specify_edit.setEnabled(0)

    def loadStorageSettings(self):
        self.settings.beginGroup("Storage")
        bucket = self.settings.value('S3_BUCKET_NAME')
        storage_index = self.storage_combo_box.findText(bucket,Qt.MatchExactly)
        if not storage_index == -1:
            self.storage_combo_box.setCurrentIndex(storage_index)
        else:
            self.storage_combo_box.setCurrentIndex(self.storage_combo_box.findText(self.tr('other...')))
            self.specify_label.setEnabled(1)
            self.specify_edit.setEnabled(1)
            self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME'))
        self.key_id_edit.setText(self.settings.value('AWS_ACCESS_KEY_ID'))
        self.key_id_edit.setCursorPosition(0)
        self.secret_key_edit.setText(self.settings.value('AWS_SECRET_ACCESS_KEY'))
        self.secret_key_edit.setCursorPosition(0)
        self.settings.endGroup()

    def loadOptionsSettings(self):
        self.settings.beginGroup("Options")
        """
        Boolean values are converted into string and lower case for
        'if' statement, since PyQt sometimes returns 'true', just like C++,
        instead of 'True', Python style.
        Maybe we can use integer values (0 or 1), instead of using string.
        """
        if str(self.settings.value('LICENSE')).lower() == 'true':
            self.license_check_box.setCheckState(2)
        if str(self.settings.value('REPROJECT')).lower() == 'true':
            self.reproject_check_box.setCheckState(2)
        """
        if str(self.settings.value('NOTIFY_OAM')).lower() == 'true':
            self.notify_oam_check.setCheckState(2)
        if str(self.settings.value('TRIGGER_OAM_TS')).lower() == 'true':
            self.trigger_tiling_check.setCheckState(2)
        """
        # This part is for temporal use.
        self.notify_oam_check.setCheckState(0)
        self.trigger_tiling_check.setCheckState(0)
        self.settings.endGroup()

    def loadMetadataReviewBox(self):
        json_file_abspaths = []
        for index in xrange(self.sources_list_widget.count()):
            file_abspath = str(self.sources_list_widget.item(index).data(Qt.UserRole))
            json_file_abspath = ''
            #if self.reproject_check_box.isChecked():
            #    json_file_abspath = os.path.splitext(file_abspath)[0]+'_EPSG3857.json'
            #else:
            #json_file_abspath = os.path.splitext(file_abspath)[0]+'.tif_meta.json'
            json_file_abspath = file_abspath + '_meta.json'

            #print str(json_file_abspath)
            json_file_abspaths.append(json_file_abspath)

        temp = QTemporaryFile()
        temp.open()
        for json_file_abspath in json_file_abspaths:
            if os.path.exists(json_file_abspath):
                with open(json_file_abspath) as infile:
                    temp.write(infile.read() + '\n\n')
            else:
                temp.write('%s was not found. Please save it in advance.\n\n'
                    % str(json_file_abspath))
        temp.flush()
        temp.seek(0)

        stream = QTextStream(temp)
        self.review_metadata_box.setText(stream.readAll())


    #event handler for upload button
    def startUpload(self):

        upload_options = []
        upload_file_abspaths = []

        #get the information of upload options
        if self.notify_oam_check.isChecked():
            #self.upload_options.append("notify_oam")
            upload_options.append("notify_oam")
        if self.trigger_tiling_check.isChecked():
            #self.upload_options.append("trigger_tiling")
            upload_options.append("trigger_tiling")

        if not self.license_check_box.isChecked():
            self.bar2.clearWidgets()
            self.bar2.pushMessage(
                'CRITICAL',
                'Please check the lisence term.',
                level=QgsMessageBar.WARNING)
        else:
            for index in xrange(self.sources_list_widget.count()):
                upload_file_abspath = str(self.added_sources_list_widget.item(index).data(Qt.UserRole))
                upload_file_abspaths.append(upload_file_abspath)

            # get login information for bucket
            bucket_name = None
            bucket_key = None
            bucket_secret = None

            if self.storage_combo_box.currentIndex() == 0:
                bucket_name = 'oam-qgis-plugin-test'
            else:
                bucket_name = str(self.specify_edit.text())
                if not bucket_name:
                    self.bar2.clearWidgets()
                    self.bar2.pushMessage(
                        'WARNING',
                        'The bucket for upload must be provided',
                        level=QgsMessageBar.WARNING)

            bucket_key = str(self.key_id_edit.text())
            bucket_secret = str(self.secret_key_edit.text())

            if self.s3UpPrgWin == None:
                self.s3UpPrgWin = S3UploadProgressWindow()
                self.s3UpPrgWin.started.connect(self.displayConnectionResult)
                #self.s3UpPrgWin.progress.connect(self.updateProgress)
                self.s3UpPrgWin.startConfirmed.connect(self.updateListWidgets)
                self.s3UpPrgWin.finished.connect(self.finishUpload)

            self.s3UpPrgWin.startUpload(bucket_key,
                                        bucket_secret,
                                        bucket_name,
                                        upload_options,
                                        upload_file_abspaths)

            self.button(QWizard.FinishButton).setVisible(False)
            #print(self.isTopLevel())


    def displayConnectionResult(self, didStart):
        if didStart:
            self.bar2.clearWidgets()
            self.bar2.pushMessage(
                'INFO',
                'Uploading imagery and metadata...',
                level=QgsMessageBar.INFO)
        else:
            self.bar2.clearWidgets()
            self.bar2.pushMessage(
                'CRITICAL',
                'Connection to S3 server failed.',
                level=QgsMessageBar.CRITICAL)


    def updateListWidgets(self, fileAbsPath):
        #print('fileAbsPath: ' + fileAbsPath)

        #print(str(self.added_sources_list_widget.count()))
        for index in xrange(0, self.added_sources_list_widget.count()):
            refFileAbsPath = str(self.added_sources_list_widget.item(index).data(Qt.UserRole))
            #print('refFileAbsPath: ' + refFileAbsPath)
            if fileAbsPath == refFileAbsPath:
                self.added_sources_list_widget.takeItem(index)
                break

        #print(str(self.sources_list_widget.count()))
        for index in xrange(0, self.sources_list_widget.count()):
            refFileAbsPath = str(self.sources_list_widget.item(index).data(Qt.UserRole))
            #print('refFileAbsPath: ' + refFileAbsPath)
            if fileAbsPath == refFileAbsPath:
                self.sources_list_widget.takeItem(index)
                break

        self.loadMetadataReviewBox()
        self.loadLayers()


    def finishUpload(self, numSuccess, numCancelled, numFailed):
        self.button(QWizard.FinishButton).setVisible(True)

        self.bar2.clearWidgets()
        self.bar2.pushMessage(
            'Upload Result',
            'Success:{0} Cancel:{1} Fail:{2}'.format(numSuccess, numCancelled, numFailed),
            level=QgsMessageBar.INFO)

        # Probably, it is better to change this part into log file.
        print('')
        print('------------------------------------------------')
        print('Success:{0} Cancel:{1} Fail:{2}'.format(numSuccess, numCancelled, numFailed))
        print('------------------------------------------------')
        print('')
class SuppliesETLWizard(QWizard, WIZARD_UI):
    on_result = pyqtSignal(
        bool)  # whether the tool was run successfully or not

    def __init__(self, db, conn_manager, parent=None):
        QWizard.__init__(self, parent)
        self.setupUi(self)
        self._db = db
        self.conn_manager = conn_manager
        self.parent = parent

        self.logger = Logger()
        self.app = AppInterface()

        self.names = self._db.names
        self.help_strings = HelpStrings()
        self._data_source_widget = None
        self.db_source = SUPPLIES_DB_SOURCE
        self.tool_name = ""
        self._running_tool = False
        self._db_was_changed = False  # To postpone calling refresh gui until we close this dialog instead of settings
        self.progress_configuration(0, 1)  # start from: 0, number of steps: 1

        self.wizardPage2.setButtonText(
            QWizard.CustomButton1,
            QCoreApplication.translate("SuppliesETLWizard", "Run ETL"))
        self.wizardPage1.setButtonText(
            QWizard.CancelButton,
            QCoreApplication.translate("SuppliesETLWizard", "Close"))
        self.wizardPage2.setButtonText(
            QWizard.CancelButton,
            QCoreApplication.translate("SuppliesETLWizard", "Close"))

        # Auxiliary data to set nonlinear next pages
        self.pages = [self.wizardPage1, self.wizardPage2, self.wizardPage3]
        self.dict_pages_ids = {
            self.pages[idx]: pid
            for idx, pid in enumerate(self.pageIds())
        }

        # Set connections
        self.rad_snc_data.toggled.connect(self.etl_option_changed)
        self.etl_option_changed()  # Initialize it
        self.button(QWizard.CustomButton1).clicked.connect(
            self.import_button_clicked)
        self.button(QWizard.HelpButton).clicked.connect(self.show_help)
        self.currentIdChanged.connect(self.current_page_changed)
        self.finished.connect(self.finished_slot)
        self.btn_browse_connection.clicked.connect(self.show_settings)

        # Initialize
        self.current_page_changed(1)
        self.update_connection_info()
        self.restore_settings()
        self.initialize_feedback()

        # Set MessageBar for QWizard
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.setLayout(QGridLayout())
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def current_page_changed(self, id):
        """
        Reset the Next button. Needed because Next might have been disabled by a
        condition in a another SLOT.
        """
        #enable_next_wizard(self)
        button_list = [
            QWizard.HelpButton, QWizard.Stretch, QWizard.BackButton,
            QWizard.CustomButton1, QWizard.NextButton, QWizard.FinishButton,
            QWizard.CancelButton
        ]
        not_visible = []

        if id == self.dict_pages_ids[self.wizardPage1]:
            self.setWindowTitle(
                QCoreApplication.translate("SuppliesETLWizard",
                                           "Run supplies ETL"))
            button_list.remove(QWizard.BackButton)
            button_list.remove(QWizard.CustomButton1)
            button_list.remove(QWizard.FinishButton)
        elif id == self.dict_pages_ids[self.wizardPage2]:
            button_list.remove(QWizard.FinishButton)
            not_visible.append(self.NextButton)
            self.load_data_source_controls()
            if self.rad_snc_data.isChecked():
                self.setWindowTitle(
                    QCoreApplication.translate("SuppliesETLWizard",
                                               "ETL: SNC to Supplies model"))
            else:
                self.setWindowTitle(
                    QCoreApplication.translate("SuppliesETLWizard",
                                               "ETL: Cobol to Supplies model"))
        elif id == self.dict_pages_ids[self.wizardPage3]:
            self.bar.clearWidgets()
            button_list.remove(QWizard.CustomButton1)
            button_list.remove(QWizard.NextButton)
            button_list.remove(QWizard.BackButton)
            self.wizardPage3.setFinalPage(True)

        self.setButtonLayout(button_list)
        for button in not_visible:
            self.button(button).setVisible(False)

    def etl_option_changed(self):
        """
        Adjust help, names and titles according to the selected option
        """
        if self.rad_snc_data.isChecked():
            self.tool_name = QCoreApplication.translate(
                "SuppliesETLWizard", "ETL-SNC")
            self.txt_help_page_2.setHtml(
                self.help_strings.WIZ_SUPPLIES_ETL_PAGE_2.format("del SNC"))
        elif self.rad_cobol_data.isChecked():  # self.rad_cobol_data is checked
            self.tool_name = QCoreApplication.translate(
                "SuppliesETLWizard", "ETL-Cobol")
            self.txt_help_page_2.setHtml(
                self.help_strings.WIZ_SUPPLIES_ETL_PAGE_2.format("de Cobol"))

    def load_data_source_controls(self):
        self.clear_data_source_widget()
        if self.rad_snc_data.isChecked():
            self._data_source_widget = SNCDataSourceWidget()
        else:  # Cobol
            self._data_source_widget = CobolDataSourceWidget()

        self._data_source_widget.input_data_changed.connect(
            self.set_import_button_enabled)
        self._data_source_widget.emit_input_data_changed(
        )  # Initialize input validation
        self.data_source_layout.addWidget(self._data_source_widget)
        self._data_source_widget.setVisible(True)

    def clear_data_source_widget(self):
        while self.data_source_layout.count():
            child = self.data_source_layout.takeAt(0)
            if child.widget():
                child.widget().setVisible(False)

    def initialize_feedback(self):
        self.progress.setValue(0)
        self.progress.setVisible(False)
        self.custom_feedback = CustomFeedback()
        self.custom_feedback.progressChanged.connect(self.progress_changed)
        self.set_gui_controls_enabled(True)

    def progress_configuration(self, base, num_process):
        """
        :param base: Where to start counting from
        :param num_process: Number of steps
        """
        self.progress_base = base
        self.progress_maximum = 100 * num_process
        self.progress.setMaximum(self.progress_maximum)

    def progress_changed(self):
        QCoreApplication.processEvents()  # Listen to cancel from the user
        self.progress.setValue(self.progress_base +
                               self.custom_feedback.progress())

    def set_gui_controls_enabled(self, enable):
        self.gbx_data_source.setEnabled(enable)
        self.target_data.setEnabled(enable)
        self.set_import_button_enabled(enable)

    def set_import_button_enabled(self, enable):
        self.button(QWizard.CustomButton1).setEnabled(enable)

    def import_button_clicked(self):
        self.bar.clearWidgets()
        self.save_settings()
        etl_result = False

        if self.rad_snc_data.isChecked():
            etl = ETLSNC(self.names, self._data_source_widget)
        else:  # Cobol
            etl = ETLCobol(self.names, self._data_source_widget)

        if self._db.test_connection()[0]:
            reply = QMessageBox.question(
                self, QCoreApplication.translate("SuppliesETLWizard",
                                                 "Warning"),
                QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "The database <i>{}</i> already has a valid LADM-COL structure.<br/><br/>If such database has any data, loading data into it might cause invalid data.<br/><br/>Do you still want to continue?"
                ).format(self._db.get_description_conn_string()),
                QMessageBox.Yes, QMessageBox.No)

            if reply == QMessageBox.Yes:
                self.set_gui_controls_enabled(False)
                self.button(self.BackButton).setEnabled(False)
                self.button(self.CustomButton1).setEnabled(False)
                self.button(self.CancelButton).setText(
                    QCoreApplication.translate("SuppliesETLWizard", "Cancel"))

                with OverrideCursor(Qt.WaitCursor):
                    res_alpha, msg_alpha = etl.load_alphanumeric_layers()

                    if res_alpha:
                        res_spatial, msg_spatial = etl.load_spatial_layers()

                        if res_spatial:
                            res_model, msg_model = self.load_model_layers(
                                etl.layers)

                            if res_model:
                                layers_feature_count_before = {
                                    name: layer.featureCount()
                                    for name, layer in etl.layers.items()
                                }
                                self._running_tool = True
                                self.progress.setVisible(True)
                                res_etl_model = etl.run_etl_model(
                                    self.custom_feedback)
                                if not self.custom_feedback.isCanceled(
                                ) and res_etl_model:
                                    self.progress.setValue(100)

                                    self.button(
                                        self.NextButton).setVisible(True)
                                    self.button(
                                        self.CustomButton1).setVisible(False)
                                    self.button(self.CancelButton).setText(
                                        QCoreApplication.translate(
                                            "SuppliesETLWizard", "Close"))
                                    self.show_message(
                                        QCoreApplication.translate(
                                            "SuppliesETLWizard",
                                            "The {} has finished successfully!"
                                        ).format(self.tool_name), Qgis.Success,
                                        0)

                                    self.logger.clear_status()
                                    self.fill_summary(
                                        layers_feature_count_before,
                                        etl.layers)
                                    etl_result = True
                                else:
                                    self.initialize_feedback(
                                    )  # Get ready for an eventual new execution
                                    self.logger.clear_status()
                                self._running_tool = False
                            else:
                                self.show_message(msg_model, Qgis.Warning)
                        else:
                            self.show_message(msg_spatial, Qgis.Warning)
                    else:
                        self.show_message(msg_alpha, Qgis.Warning)
        else:
            with OverrideCursor(Qt.WaitCursor):
                # TODO: if an empty schema was selected, do the magic under the hood
                # self.create_model_into_database()
                # Now execute "accepted()"
                msg = QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "To run the ETL, the database (schema) should have the Supplies LADM-COL structure. Choose a proper database (schema) and try again."
                )
                self.show_message(msg, Qgis.Warning)
                self.logger.warning(__name__, msg)

        self.on_result.emit(
            etl_result)  # Inform other classes if the execution was successful

    def reject(self):
        if self._running_tool:
            reply = QMessageBox.question(
                self, QCoreApplication.translate("SuppliesETLWizard",
                                                 "Warning"),
                QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "The '{}' tool is still running. Do you want to cancel it? If you cancel, the data might be incomplete in the target database."
                ).format(self.tool_name), QMessageBox.Yes, QMessageBox.No)

            if reply == QMessageBox.Yes:
                self.custom_feedback.cancel()
                self._running_tool = False
                msg = QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "The '{}' tool was cancelled.").format(self.tool_name)
                self.logger.info(__name__, msg)
                self.show_message(msg, Qgis.Info)
        else:
            if self._db_was_changed:
                self.conn_manager.db_connection_changed.emit(
                    self._db,
                    self._db.test_connection()[0], self.db_source)
            self.logger.info(__name__, "Dialog closed.")
            self.app.settings.set_setting(
                self.app.settings.COBOL_FILES_DIR_KEY, '')
            self.app.settings.set_setting(self.app.settings.SNC_FILES_DIR_KEY,
                                          '')
            self.done(1)

    def finished_slot(self, result):
        self.bar.clearWidgets()

    def show_message(self, message, level, duration=10):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, duration)

    def fill_summary(self, layers_feature_count_before, etl_layers):
        layers_feature_count_after = {
            name: layer.featureCount()
            for name, layer in etl_layers.items()
        }
        summary = """<html><head/><body><p>"""
        summary += QCoreApplication.translate(
            "SuppliesETLWizard", "<h4>{} report</h4>").format(self.tool_name)
        summary += QCoreApplication.translate(
            "SuppliesETLWizard",
            "Number of features loaded to the LADM-COL cadastral supplies model:<br/>"
        )

        for name, before_count in layers_feature_count_before.items():
            summary += QCoreApplication.translate(
                "SuppliesETLWizard", '<br/><b>{}</b> : {}'.format(
                    name, layers_feature_count_after[name] - before_count))

        summary += """<hr>"""
        summary += """</body></html>"""
        self.txt_log.setText(summary)

    def save_settings(self):
        settings = QSettings()
        etl_source = "snc"
        if self.rad_snc_data.isChecked():
            etl_source = "snc"
        elif self.rad_cobol_data.isChecked():
            etl_source = "cobol"

        settings.setValue('Asistente-LADM-COL/supplies/etl_source', etl_source)
        self._data_source_widget.save_settings()

        # In the main page (source-target configuration), save if splitter is closed
        self.app.settings.etl_splitter_collapsed = self.splitter_2.sizes(
        )[1] == 0

    def restore_settings(self):
        settings = QSettings()
        etl_source = settings.value(
            'Asistente-LADM-COL/supplies/etl_source') or 'snc'
        if etl_source == 'snc':
            self.rad_snc_data.setChecked(True)
        elif etl_source == 'cobol':
            self.rad_cobol_data.setChecked(True)

        # If splitter in the main page was closed before, set it as closed again
        if self.app.settings.etl_splitter_collapsed:
            sizes = self.splitter_2.sizes()
            self.splitter_2.setSizes([sizes[0], 0])

    def show_help(self):
        show_plugin_help('supplies')

    def show_settings(self):
        dlg = SettingsDialog(self.conn_manager, parent=self)
        dlg.setWindowTitle(
            QCoreApplication.translate("SuppliesETLWizard",
                                       "Target DB Connection Settings"))
        dlg.show_tip(
            QCoreApplication.translate(
                "SuppliesETLWizard",
                "Configure where do you want the data to be imported."))
        dlg.set_db_source(self.db_source)

        dlg.set_required_models([LADMNames.SUPPLIES_MODEL_KEY])
        dlg.set_tab_pages_list(
            [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX])
        dlg.set_action_type(EnumDbActionType.IMPORT_FROM_ETL)

        dlg.db_connection_changed.connect(self.db_connection_changed)
        if self.db_source == COLLECTED_DB_SOURCE:
            dlg.db_connection_changed.connect(
                self.app.core.cache_layers_and_relations)

        if dlg.exec_():
            self._db = dlg.get_db_connection()
            self.update_connection_info()

    def update_connection_info(self):
        db_description = self._db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(
                self._db.get_display_conn_string())
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("SuppliesETLWizard",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')

    def db_connection_changed(self, db, ladm_col_db, db_source):
        # We dismiss parameters here, after all, we already have the db, and the ladm_col_db may change from this moment
        # until we close the supplies dialog (e.g., we might run an import schema before under the hood)
        self._db_was_changed = True

    def load_model_layers(self, layers):
        self.app.core.get_layers(self._db, layers, load=True)
        if not layers:
            return False, QCoreApplication.translate(
                "SuppliesETLWizard",
                "There was a problem loading layers from the 'Supplies' model!"
            )

        return True, ''
class ResampleImageToBlockDialog(QtGui.QDialog, FORM_CLASS):
    """Extract statistics from a list of rasters at set locations."""
    toolKey = 'ResampleImageBandDialog'

    def __init__(self, iface, parent=None):

        super(ResampleImageToBlockDialog, self).__init__(parent)

        # Set up the user interface from Designer.
        self.setupUi(self)

        self.iface = iface
        self.DISP_TEMP_LAYERS = read_setting(PLUGIN_NAME + '/DISP_TEMP_LAYERS',
                                             bool)
        self.DEBUG = config.get_debug_mode()

        # Catch and redirect python errors directed at the log messages python error tab.
        QgsMessageLog.instance().messageReceived.connect(errorCatcher)

        if not os.path.exists(TEMPDIR):
            os.mkdir(TEMPDIR)

        # Setup for validation messagebar on gui-----------------------------
        self.messageBar = QgsMessageBar(
            self)  # leave this message bar for bailouts
        self.validationLayout = QtGui.QFormLayout(self)  # new layout to gui

        if isinstance(self.layout(), (QtGui.QFormLayout, QtGui.QGridLayout)):
            # create a validation layout so multiple messages can be added and cleaned up.
            self.layout().insertRow(0, self.validationLayout)
            self.layout().insertRow(0, self.messageBar)
        else:
            self.layout().insertWidget(
                0,
                self.messageBar)  # for use with Vertical/horizontal layout box

        self.outQgsCRS = None

        self.exclude_map_layers()
        self.updateRaster()
        self.updateUseSelected()
        self.autoSetCoordinateSystem()

        # GUI Runtime Customisation -----------------------------------------------
        self.setWindowIcon(
            QtGui.QIcon(':/plugins/pat/icons/icon_resampleToBlock.svg'))
        self.chkAddToDisplay.setChecked(False)
        self.add_blank_field_to_cbo()
        # self.chkAddToDisplay.hide()

    def cleanMessageBars(self, AllBars=True):
        """Clean Messages from the validation layout.
        Args:
            AllBars (bool): Remove All bars including those which haven't timed-out. Defaults to True
        """
        layout = self.validationLayout
        for i in reversed(range(layout.count())):
            # when it timed out the row becomes empty....
            if layout.itemAt(i).isEmpty():
                # .removeItem doesn't always work. so takeAt(pop) it instead
                item = layout.takeAt(i)
            elif AllBars:  # ie remove all
                item = layout.takeAt(i)
                # also have to remove any widgets associated with it.
                if item.widget() is not None:
                    item.widget().deleteLater()

    def send_to_messagebar(self,
                           message,
                           title='',
                           level=QgsMessageBar.INFO,
                           duration=5,
                           exc_info=None,
                           core_QGIS=False,
                           addToLog=False,
                           showLogPanel=False):
        """ Add a message to the forms message bar.

        Args:
            message (str): Message to display
            title (str): Title of message. Will appear in bold. Defaults to ''
            level (QgsMessageBarLevel): The level of message to log. Defaults to QgsMessageBar.INFO
            duration (int): Number of seconds to display message for. 0 is no timeout. Defaults to 5
            core_QGIS (bool): Add to QGIS interface rather than the dialog
            addToLog (bool): Also add message to Log. Defaults to False
            showLogPanel (bool): Display the log panel
            exc_info () : Information to be used as a traceback if required

        """

        if core_QGIS:
            newMessageBar = self.iface.messageBar()
        else:
            newMessageBar = QgsMessageBar(self)

        widget = newMessageBar.createMessage(title, message)

        if showLogPanel:
            button = QPushButton(widget)
            button.setText('View')
            button.setContentsMargins(0, 0, 0, 0)
            button.setFixedWidth(35)
            button.pressed.connect(openLogPanel)
            widget.layout().addWidget(button)

        newMessageBar.pushWidget(widget, level, duration=duration)

        if not core_QGIS:
            rowCount = self.validationLayout.count()
            self.validationLayout.insertRow(rowCount + 1, newMessageBar)

        if addToLog:
            if level == 1:  # 'WARNING':
                LOGGER.warning(message)
            elif level == 2:  # 'CRITICAL':
                # Add a traceback to log only for bailouts only
                if exc_info is not None:
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    mess = str(traceback.format_exc())
                    message = message + '\n' + mess

                LOGGER.critical(message)
            else:  # INFO = 0
                LOGGER.info(message)

    def exclude_map_layers(self):
        """ Run through all loaded layers to find ones which should be excluded. In this case exclude services."""

        exVlayer_list = []
        exRlayer_list = []
        for layer in self.iface.legendInterface().layers():
            if layer.type() == QgsMapLayer.RasterLayer:
                if layer.providerType() != 'gdal':
                    exRlayer_list.append(layer)

            elif layer.type() == QgsMapLayer.VectorLayer:
                if layer.providerType() != 'ogr':
                    exVlayer_list.append(layer)

        self.mcboRasterLayer.setExceptedLayerList(exRlayer_list)
        if len(exRlayer_list) > 0:
            pass

        if len(exVlayer_list) > 0:
            self.mcboPolygonLayer.setExceptedLayerList(exVlayer_list)

    def updateRaster(self):
        if self.mcboRasterLayer.currentLayer() is None: return

        layer = self.mcboRasterLayer.currentLayer()
        provider = layer.dataProvider()

        if provider.srcHasNoDataValue(1):
            self.spnNoDataVal.setValue(provider.srcNoDataValue(1))
        elif len(provider.userNoDataValues(1)) > 0:
            self.spnNoDataVal.setValue(provider.userNoDataValues(1)[0].min())
        else:
            self.spnNoDataVal.setValue(0)

        # add a band list to the drop down box
        bandCount = self.mcboRasterLayer.currentLayer().bandCount()
        band_list = ['Band {: >2}'.format(i) for i in range(1, bandCount + 1)]
        self.cboBand.setMaxCount(bandCount + 1)
        self.cboBand.clear()
        self.cboBand.addItems([u''] + sorted(band_list))

    def updateUseSelected(self):
        """Update use selected checkbox if active layer has a feature selection"""

        self.chkUseSelected.setChecked(False)

        if self.mcboPolygonLayer.count() == 0:
            return

        polygon_lyr = self.mcboPolygonLayer.currentLayer()
        self.mFieldComboBox.setLayer(polygon_lyr)
        if len(polygon_lyr.selectedFeatures()) > 0:
            self.chkUseSelected.setText(
                'Use the {} selected feature(s) ?'.format(
                    len(polygon_lyr.selectedFeatures())))
            self.chkUseSelected.setEnabled(True)
        else:
            self.chkUseSelected.setText('No features selected')
            self.chkUseSelected.setEnabled(False)

    def autoSetCoordinateSystem(self):
        if self.mcboRasterLayer.count() == 0:
            return
        self.cleanMessageBars()
        raster_lyr = self.mcboRasterLayer.currentLayer()

        raster_utm_crs = crs.getProjectedCRSForXY(
            raster_lyr.extent().xMinimum(),
            raster_lyr.extent().yMinimum(),
            int(raster_lyr.crs().authid().replace('EPSG:', '')))
        self.outQgsCRS = None

        if raster_utm_crs is not None:
            raster_crs = QgsCoordinateReferenceSystem('EPSG:{}'.format(
                raster_utm_crs.epsg_number))
            self.outQgsCRS = raster_crs

        if self.outQgsCRS is not None:
            self.lblOutCRS.setText('{}  -  {}'.format(
                self.outQgsCRS.description(), self.outQgsCRS.authid()))
            self.lblOutCRSTitle.setStyleSheet('color:black')
            self.lblOutCRS.setStyleSheet('color:black')
        else:
            self.lblOutCRSTitle.setStyleSheet('color:red')
            self.lblOutCRS.setStyleSheet('color:red')
            self.lblOutCRS.setText('Unspecified')
            self.send_to_messagebar(
                'Auto detect coordinate system Failed. Check coordinate system of input raster layer',
                level=QgsMessageBar.CRITICAL,
                duration=5)
        return

    def add_blank_field_to_cbo(self, set=True):
        """ Add a blank string to the field combo box.  Fixed in qgis 3"""

        if self.mFieldComboBox.findText('', QtCore.Qt.MatchFixedString) == -1:
            self.mFieldComboBox.addItem(u'')
            if set == True:
                self.mFieldComboBox.setField(u'')

    def on_mcboRasterLayer_layerChanged(self):
        self.updateRaster()
        self.autoSetCoordinateSystem()

    def on_mcboPolygonLayer_layerChanged(self):
        self.updateUseSelected()
        self.autoSetCoordinateSystem()

        # ToDo: QGIS 3 implement QgsMapLayerComboBox.allowEmptyLayer() instead of chkUsePoly checkbox
        self.chkUsePoly.setChecked(True)

        self.add_blank_field_to_cbo()

    @QtCore.pyqtSlot(int)
    def on_chkUsePoly_stateChanged(self, state):
        self.add_blank_field_to_cbo()

        self.mFieldComboBox.setEnabled(state)
        self.lblGroupByField.setEnabled(state)

#     def on_mFieldComboBox_fieldChanged(self, field):
#         # Problem : after selecting a field, the blank is removed from the pick list.
#         # Solution: Add it again.... but this doesn't work.
#         if self.mFieldComboBox.findText('', QtCore.Qt.MatchFixedString) == -1:
#             # work around for not having a physical blank in the list. Fixed in qgis 3
#             self.mFieldComboBox.addItem(u'')

    def on_chkUseSelected_stateChanged(self, state):
        if self.chkUseSelected.isChecked():
            self.chkUsePoly.setChecked(True)

    @QtCore.pyqtSlot(name='on_cmdOutCRS_clicked')
    def on_cmdOutCRS_clicked(self):
        dlg = QgsGenericProjectionSelector(self)
        dlg.setMessage(self.tr('Select coordinate system'))
        if dlg.exec_():
            if dlg.selectedAuthId() != '':
                self.outQgsCRS = QgsCoordinateReferenceSystem(
                    dlg.selectedAuthId())

                if self.outQgsCRS.geographicFlag():
                    self.outQgsCRS = None
                    self.send_to_messagebar(unicode(
                        self.
                        tr("Geographic coordinate systems are not allowed. Resetting to default.."
                           )),
                                            level=QgsMessageBar.WARNING,
                                            duration=5)
            else:
                self.outQgsCRS = None

            if self.outQgsCRS is None:
                self.autoSetCoordinateSystem()

            self.lblOutCRSTitle.setStyleSheet('color:black')
            self.lblOutCRS.setStyleSheet('color:black')
            self.lblOutCRS.setText(
                self.tr('{}  -  {}'.format(self.outQgsCRS.description(),
                                           self.outQgsCRS.authid())))

    @QtCore.pyqtSlot(name='on_cmdOutputFolder_clicked')
    def on_cmdOutputFolder_clicked(self):
        self.messageBar.clearWidgets()
        if self.lneOutputFolder.text() is None:
            outFolder = ''
        else:
            outFolder = self.lneOutputFolder.text()

        if outFolder == '':
            outFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey +
                                     "/LastOutFolder")
            if outFolder is None or not os.path.exists(outFolder):
                outFolder = read_setting(PLUGIN_NAME + '/BASE_OUT_FOLDER')

        s = QtGui.QFileDialog.getExistingDirectory(
            self,
            self.
            tr("Save output files to a folder. A sub-folder will be created from the image name"
               ), outFolder, QtGui.QFileDialog.ShowDirsOnly)

        self.cleanMessageBars(self)
        if s == '' or s is None:
            return

        s = os.path.normpath(s)

        self.lblOutputFolder.setStyleSheet('color:black')
        self.lneOutputFolder.setStyleSheet('color:black')
        self.lneOutputFolder.setText(s)
        write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastOutFolder", s)

    def validate(self):
        """Check to see that all required gui elements have been entered and are valid."""
        self.messageBar.clearWidgets()
        self.cleanMessageBars(AllBars=True)
        try:
            errorList = []

            if self.mcboRasterLayer.currentLayer() is None:
                self.lblRasterLayer.setStyleSheet('color:red')
                errorList.append(self.tr("Input image layer required."))
            else:
                self.lblRasterLayer.setStyleSheet('color:black')

            if self.cboBand.currentText() == '':
                self.lblBand.setStyleSheet('color:red')
                errorList.append(self.tr("Input band selection required."))
            else:
                self.lblBand.setStyleSheet('color:black')

            if self.dsbPixelSize.value() <= 0:
                self.lblPixelSize.setStyleSheet('color:red')
                errorList.append(self.tr("Pixel size must be greater than 0."))
            else:
                self.lblPixelSize.setStyleSheet('color:black')

            if self.outQgsCRS is None:
                self.lblOutCRSTitle.setStyleSheet('color:red')
                self.lblOutCRS.setStyleSheet('color:red')
                errorList.append(
                    self.tr("Select output projected coordinate system"))
            else:
                if self.outQgsCRS.geographicFlag():
                    self.lblOutCRSTitle.setStyleSheet('color:red')
                    self.lblOutCRS.setStyleSheet('color:red')
                    errorList.append(
                        self.
                        tr("Output projected coordinate system (not geographic) required"
                           ))
                else:
                    self.lblOutCRSTitle.setStyleSheet('color:black')
                    self.lblOutCRS.setStyleSheet('color:black')

            if self.lneOutputFolder.text() == '':
                self.lblOutputFolder.setStyleSheet('color:red')
                errorList.append(self.tr("Select output data folder"))
            elif not os.path.exists(self.lneOutputFolder.text()):
                self.lneOutputFolder.setStyleSheet('color:red')
                self.lblOutputFolder.setStyleSheet('color:red')
                errorList.append(self.tr("Output data folder does not exist"))
            else:
                self.lblOutputFolder.setStyleSheet('color:black')
                self.lneOutputFolder.setStyleSheet('color:black')

            if len(errorList) > 0:
                raise ValueError(errorList)

        except ValueError as e:
            self.cleanMessageBars(True)
            if len(errorList) > 0:
                for i, ea in enumerate(errorList):
                    self.send_to_messagebar(unicode(ea),
                                            level=QgsMessageBar.WARNING,
                                            duration=(i + 1) * 5)
                return False

        return True

    def accept(self, *args, **kwargs):
        try:

            if not self.validate():
                return False

            # disable form via a frame, this will still allow interaction with the message bar
            self.fraMain.setDisabled(True)

            # clean gui and Qgis messagebars
            self.cleanMessageBars(True)

            # Change cursor to Wait cursor
            QtGui.qApp.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
            self.iface.mainWindow().statusBar().showMessage(
                'Processing {}'.format(self.windowTitle()))
            LOGGER.info('{st}\nProcessing {}'.format(self.windowTitle(),
                                                     st='*' * 50))

            self.send_to_messagebar(
                "Please wait.. QGIS will be locked... See log panel for progress.",
                level=QgsMessageBar.WARNING,
                duration=0,
                addToLog=False,
                core_QGIS=False,
                showLogPanel=True)

            # Add settings to log
            settingsStr = 'Parameters:---------------------------------------'
            settingsStr += '\n    {:20}\t{}'.format(
                'Image layer:',
                self.mcboRasterLayer.currentLayer().name())
            settingsStr += '\n    {:20}\t{}'.format('Image Band:',
                                                    self.cboBand.currentText())
            settingsStr += '\n    {:20}\t{}'.format('Image nodata value:',
                                                    self.spnNoDataVal.value())

            if self.chkUsePoly.isChecked():
                if self.chkUseSelected.isChecked():
                    settingsStr += '\n    {:20}\t{} with {} selected features'.format(
                        'Layer:',
                        self.mcboPolygonLayer.currentLayer().name(),
                        len(self.mcboPolygonLayer.currentLayer().
                            selectedFeatures()))
                else:
                    settingsStr += '\n    {:20}\t{}'.format(
                        'Boundary layer:',
                        self.mcboPolygonLayer.currentLayer().name())

                if self.mFieldComboBox.currentField():
                    settingsStr += '\n    {:20}\t{}'.format(
                        'Block ID field:', self.mFieldComboBox.currentField())
            else:
                settingsStr += '\n    {:20}\t{}'.format('Boundary layer:', '')
                settingsStr += '\n    {:20}\t{}'.format('Block ID field:', '')

            settingsStr += '\n    {:20}\t{}'.format('Resample pixel size: ',
                                                    self.dsbPixelSize.value())

            settingsStr += '\n    {:30}\t{}'.format(
                'Output Coordinate System:', self.lblOutCRS.text())
            settingsStr += '\n    {:30}\t{}\n'.format(
                'Output Folder:', self.lneOutputFolder.text())

            LOGGER.info(settingsStr)

            lyrRaster = self.mcboRasterLayer.currentLayer()

            if self.chkUsePoly.isChecked():
                lyrBoundary = self.mcboPolygonLayer.currentLayer()

                if self.chkUseSelected.isChecked():
                    savePlyName = lyrBoundary.name() + '_poly.shp'
                    filePoly = os.path.join(TEMPDIR, savePlyName)
                    if os.path.exists(filePoly): removeFileFromQGIS(filePoly)

                    QgsVectorFileWriter.writeAsVectorFormat(lyrBoundary,
                                                            filePoly,
                                                            "utf-8",
                                                            lyrBoundary.crs(),
                                                            "ESRI Shapefile",
                                                            onlySelected=True)

                    if self.DISP_TEMP_LAYERS:
                        addVectorFileToQGIS(filePoly,
                                            layer_name=os.path.splitext(
                                                os.path.basename(filePoly))[0],
                                            group_layer_name='DEBUG',
                                            atTop=True)
                else:
                    filePoly = lyrBoundary.source()

            band_num = [int(self.cboBand.currentText().replace('Band ', ''))]
            files = resample_bands_to_block(
                lyrRaster.source(),
                self.dsbPixelSize.value(),
                self.lneOutputFolder.text(),
                band_nums=band_num,
                image_epsg=int(lyrRaster.crs().authid().replace('EPSG:', '')),
                image_nodata=self.spnNoDataVal.value(),
                polygon_shapefile=filePoly
                if self.chkUsePoly.isChecked() else None,
                groupby=self.mFieldComboBox.currentField()
                if self.mFieldComboBox.currentField() else None,
                out_epsg=int(self.outQgsCRS.authid().replace('EPSG:', '')))

            if self.chkAddToDisplay.isChecked():
                for ea_file in files:
                    removeFileFromQGIS(ea_file)
                    addRasterFileToQGIS(ea_file,
                                        group_layer_name=os.path.basename(
                                            os.path.dirname(ea_file)),
                                        atTop=False)

            self.cleanMessageBars(True)
            self.fraMain.setDisabled(False)

            self.iface.mainWindow().statusBar().clearMessage()
            self.iface.messageBar().popWidget()
            QtGui.qApp.restoreOverrideCursor()
            return super(ResampleImageToBlockDialog,
                         self).accept(*args, **kwargs)

        except Exception as err:

            QtGui.qApp.restoreOverrideCursor()
            self.iface.mainWindow().statusBar().clearMessage()
            self.cleanMessageBars(True)
            self.fraMain.setDisabled(False)

            self.send_to_messagebar(str(err),
                                    level=QgsMessageBar.CRITICAL,
                                    duration=0,
                                    addToLog=True,
                                    core_QGIS=False,
                                    showLogPanel=True,
                                    exc_info=sys.exc_info())

            return False  # leave dialog open
Exemple #23
0
class DialogImportData(QDialog, DIALOG_UI):
    open_dlg_import_schema = pyqtSignal(dict)  # dict with key-value params
    BUTTON_NAME_IMPORT_DATA = QCoreApplication.translate(
        "DialogImportData", "Import data")
    BUTTON_NAME_GO_TO_CREATE_STRUCTURE = QCoreApplication.translate(
        "DialogImportData", "Go to Create Structure...")

    def __init__(self,
                 iface,
                 conn_manager,
                 context,
                 link_to_import_schema=True,
                 parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)

        QgsGui.instance().enableAutoGeometryRestore(self)
        self.iface = iface
        self.conn_manager = conn_manager
        self.db_source = context.get_db_sources()[0]
        self.link_to_import_schema = link_to_import_schema
        self.db = self.conn_manager.get_db_connector_from_source(
            self.db_source)
        self.base_configuration = BaseConfiguration()
        self.logger = Logger()
        self.app = AppInterface()
        self.__ladmcol_models = LADMColModelRegistry()

        self.java_dependency = JavaDependency()
        self.java_dependency.download_dependency_completed.connect(
            self.download_java_complete)
        self.java_dependency.download_dependency_progress_changed.connect(
            self.download_java_progress_change)

        self.ilicache = IliCache(self.base_configuration)
        self.ilicache.refresh()

        self._dbs_supported = ConfigDBsSupported()
        self._running_tool = False

        # There may be 1 case where we need to emit a db_connection_changed from the Import Data dialog:
        #   1) Connection Settings was opened and the DB conn was changed.
        self._db_was_changed = False  # To postpone calling refresh gui until we close this dialog instead of settings

        # Similarly, we could call a refresh on layers and relations cache in 1 case:
        #   1) If the ID dialog was called for the COLLECTED source: opening Connection Settings and changing the DB
        #      connection.
        self._schedule_layers_and_relations_refresh = False

        # We need bar definition above calling clear_messages
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.xtf_file_browse_button.clicked.connect(
            make_file_selector(
                self.xtf_file_line_edit,
                title=QCoreApplication.translate(
                    "DialogImportData", "Open Transfer or Catalog File"),
                file_filter=QCoreApplication.translate(
                    "DialogImportData",
                    'Transfer File (*.xtf *.itf);;Catalogue File (*.xml *.xls *.xlsx)'
                )))

        self.validators = Validators()
        self.xtf_file_line_edit.setPlaceholderText(
            QCoreApplication.translate("DialogImportData",
                                       "[Name of the XTF to be imported]"))
        fileValidator = FileValidator(pattern=['*.xtf', '*.itf', '*.xml'])
        self.xtf_file_line_edit.setValidator(fileValidator)
        self.xtf_file_line_edit.textChanged.connect(self.update_import_models)
        self.xtf_file_line_edit.textChanged.emit(
            self.xtf_file_line_edit.text())

        # db
        self.connection_setting_button.clicked.connect(self.show_settings)
        self.connection_setting_button.setText(
            QCoreApplication.translate("DialogImportData",
                                       "Connection Settings"))

        # LOG
        self.log_config.setTitle(
            QCoreApplication.translate("DialogImportData", "Show log"))

        self.buttonBox.accepted.disconnect()
        self.buttonBox.clicked.connect(self.accepted_import_data)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self._accept_button = self.buttonBox.addButton(
            self.BUTTON_NAME_IMPORT_DATA, QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(QDialogButtonBox.Help)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.update_connection_info()
        self.restore_configuration()

    def accepted_import_data(self, button):
        if self.buttonBox.buttonRole(button) == QDialogButtonBox.AcceptRole:
            if button.text() == self.BUTTON_NAME_IMPORT_DATA:
                self.accepted()
            elif button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE:
                self.close()  # Close import data dialog
                self.open_dlg_import_schema.emit({
                    'selected_models':
                    self.get_ili_models()
                })  # Emit signal to open import schema dialog

    def reject(self):
        if self._running_tool:
            QMessageBox.information(
                self, QCoreApplication.translate("DialogImportData",
                                                 "Warning"),
                QCoreApplication.translate(
                    "DialogImportData",
                    "The Import Data tool is still running. Please wait until it finishes."
                ))
        else:
            self.close_dialog()

    def close_dialog(self):
        """
        We use this method to be safe when emitting the db_connection_changed, otherwise we could trigger slots that
        unload the plugin, destroying dialogs and thus, leading to crashes.
        """
        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.connect(
                self.app.core.cache_layers_and_relations)

        if self._db_was_changed:
            # If the db was changed, it implies it complies with ladm_col, hence the second parameter
            self.conn_manager.db_connection_changed.emit(
                self.db, True, self.db_source)

        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.disconnect(
                self.app.core.cache_layers_and_relations)

        self.logger.info(__name__, "Dialog closed.")
        self.done(QDialog.Accepted)

    def update_connection_info(self):
        db_description = self.db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(self.db.get_display_conn_string())
            self._accept_button.setEnabled(True)
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("DialogImportData",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')
            self._accept_button.setEnabled(False)

    def update_import_models(self):
        self.clear_messages()
        error_msg = None

        if not self.xtf_file_line_edit.text().strip():
            color = '#ffd356'  # Light orange
            self.import_models_qmodel = QStandardItemModel()
            self.import_models_list_view.setModel(self.import_models_qmodel)
        else:
            if os.path.isfile(self.xtf_file_line_edit.text().strip()):
                color = '#fff'  # White

                self.import_models_qmodel = QStandardItemModel()
                model_names = get_models_from_xtf(
                    self.xtf_file_line_edit.text().strip())

                for model in self.__ladmcol_models.supported_models():
                    if not model.hidden() and model.full_name() in model_names:
                        item = QStandardItem(model.full_alias())
                        item.setData(model.full_name(), Qt.UserRole)
                        item.setCheckable(False)
                        item.setEditable(False)
                        self.import_models_qmodel.appendRow(item)

                if self.import_models_qmodel.rowCount() > 0:
                    self.import_models_list_view.setModel(
                        self.import_models_qmodel)
                else:
                    error_msg = QCoreApplication.translate(
                        "DialogImportData",
                        "No models were found in the XTF. Is it a valid file?")
                    color = '#ffd356'  # Light orange
                    self.import_models_qmodel = QStandardItemModel()
                    self.import_models_list_view.setModel(
                        self.import_models_qmodel)
            else:
                error_msg = QCoreApplication.translate(
                    "DialogImportData", "Please set a valid XTF file")
                color = '#ffd356'  # Light orange
                self.import_models_qmodel = QStandardItemModel()
                self.import_models_list_view.setModel(
                    self.import_models_qmodel)
        self.xtf_file_line_edit.setStyleSheet(
            'QLineEdit {{ background-color: {} }}'.format(color))

        if error_msg:
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

    def get_ili_models(self):
        ili_models = list()
        for index in range(self.import_models_qmodel.rowCount()):
            item = self.import_models_qmodel.item(index)
            ili_models.append(item.data(Qt.UserRole))
        return ili_models

    def show_settings(self):
        # We only need those tabs related to Model Baker/ili2db operations
        dlg = SettingsDialog(self.conn_manager, parent=self)
        dlg.setWindowTitle(
            QCoreApplication.translate("DialogImportData",
                                       "Target DB Connection Settings"))
        dlg.show_tip(
            QCoreApplication.translate(
                "DialogImportData",
                "Configure where do you want the XTF data to be imported."))
        dlg.set_db_source(self.db_source)
        dlg.set_tab_pages_list(
            [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX])

        # Connect signals (DBUtils, Core)
        dlg.db_connection_changed.connect(self.db_connection_changed)
        if self.db_source == COLLECTED_DB_SOURCE:
            self._schedule_layers_and_relations_refresh = True

        dlg.set_action_type(EnumDbActionType.IMPORT)

        if dlg.exec_():
            self.db = dlg.get_db_connection()
            self.update_connection_info()

    def db_connection_changed(self, db, ladm_col_db, db_source):
        self._db_was_changed = True
        self.clear_messages()

    def accepted(self):
        self._running_tool = True
        self.txtStdout.clear()
        self.progress_bar.setValue(0)
        self.bar.clearWidgets()

        if not os.path.isfile(self.xtf_file_line_edit.text().strip()):
            self._running_tool = False
            error_msg = QCoreApplication.translate(
                "DialogImportData",
                "Please set a valid XTF file before importing data. XTF file does not exist."
            )
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        java_home_set = self.java_dependency.set_java_home()
        if not java_home_set:
            message_java = QCoreApplication.translate(
                "DialogImportData",
                """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION)
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()
            self.txtStdout.setText(message_java)
            self.java_dependency.get_java_on_demand()
            self.disable()
            return

        configuration = self.update_configuration()

        if configuration.disable_validation:  # If data validation at import is disabled, we ask for confirmation
            self.msg = QMessageBox()
            self.msg.setIcon(QMessageBox.Question)
            self.msg.setText(
                QCoreApplication.translate(
                    "DialogImportData",
                    "Are you sure you want to import your data without validation?"
                ))
            self.msg.setWindowTitle(
                QCoreApplication.translate("DialogImportData",
                                           "Import XTF without validation?"))
            self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            res = self.msg.exec_()
            if res == QMessageBox.No:
                self._running_tool = False
                return

        if not self.xtf_file_line_edit.validator().validate(
                configuration.xtffile, 0)[0] == QValidator.Acceptable:
            self._running_tool = False
            error_msg = QCoreApplication.translate(
                "DialogImportData",
                "Please set a valid XTF before importing data.")
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.get_ili_models():
            self._running_tool = False
            error_msg = QCoreApplication.translate(
                "DialogImportData",
                "The selected XTF file does not have information according to the LADM-COL model to import."
            )
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

        # Get list of models present in the XTF file, in the DB and in the list of required models (by the plugin)
        ili_models = set([ili_model for ili_model in self.get_ili_models()])

        supported_models_in_ili = set([
            m.full_name() for m in self.__ladmcol_models.supported_models()
        ]).intersection(ili_models)

        if not supported_models_in_ili:
            self._running_tool = False
            error_msg = QCoreApplication.translate("DialogImportData",
                                                   "The selected XTF file does not have data from any LADM-COL model supported by the LADM-COL Assistant. " \
                                                   "Therefore, you cannot import it! These are the models supported:\n\n * {}").format(" \n * ".join([m.full_alias() for m in self.__ladmcol_models.supported_models()]))
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

        db_models = set(self.db.get_models())
        suggested_models = sorted(ili_models.difference(db_models))

        if not ili_models.issubset(db_models):
            self._running_tool = False
            error_msg = QCoreApplication.translate("DialogImportData",
                                                   "IMPORT ERROR: The XTF file to import does not have the same models as the target database schema. " \
                                                   "Please create a schema that also includes the following missing modules:\n\n * {}").format(
                " \n * ".join(suggested_models))
            self.txtStdout.clear()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()

            # button is removed to define order in GUI
            for button in self.buttonBox.buttons():
                if button.text() == self.BUTTON_NAME_IMPORT_DATA:
                    self.buttonBox.removeButton(button)

            # Check if button was previously added
            self.remove_create_structure_button()

            if self.link_to_import_schema:
                self.buttonBox.addButton(
                    self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE,
                    QDialogButtonBox.AcceptRole).setStyleSheet(
                        "color: #aa2222;")
            self.buttonBox.addButton(self.BUTTON_NAME_IMPORT_DATA,
                                     QDialogButtonBox.AcceptRole)

            return

        with OverrideCursor(Qt.WaitCursor):
            self.progress_bar.show()

            self.disable()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()

            dataImporter = iliimporter.Importer(dataImport=True)

            db_factory = self._dbs_supported.get_db_factory(self.db.engine)

            dataImporter.tool = db_factory.get_model_baker_db_ili_mode()
            dataImporter.configuration = configuration

            self.save_configuration(configuration)

            dataImporter.stdout.connect(self.print_info)
            dataImporter.stderr.connect(self.on_stderr)
            dataImporter.process_started.connect(self.on_process_started)
            dataImporter.process_finished.connect(self.on_process_finished)

            self.progress_bar.setValue(25)

            try:
                if dataImporter.run() != iliimporter.Importer.SUCCESS:
                    self._running_tool = False
                    self.show_message(
                        QCoreApplication.translate(
                            "DialogImportData",
                            "An error occurred when importing the data. For more information see the log..."
                        ), Qgis.Warning)
                    return
            except JavaNotFoundError:
                self._running_tool = False
                error_msg_java = QCoreApplication.translate(
                    "DialogImportData",
                    "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
                ).format(JAVA_REQUIRED_VERSION)
                self.txtStdout.setTextColor(QColor('#000000'))
                self.txtStdout.clear()
                self.txtStdout.setText(error_msg_java)
                self.show_message(error_msg_java, Qgis.Warning)
                return

            self._running_tool = False
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.progress_bar.setValue(100)
            self.show_message(
                QCoreApplication.translate(
                    "DialogImportData",
                    "Import of the data was successfully completed"),
                Qgis.Success)

    def download_java_complete(self):
        self.accepted()

    def download_java_progress_change(self, progress):
        self.progress_bar.setValue(progress / 2)
        if (progress % 20) == 0:
            self.txtStdout.append('...')

    def remove_create_structure_button(self):
        for button in self.buttonBox.buttons():
            if button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE:
                self.buttonBox.removeButton(button)

    def save_configuration(self, configuration):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import',
            configuration.xtffile)
        settings.setValue('Asistente-LADM-COL/QgisModelBaker/show_log',
                          not self.log_config.isCollapsed())

    def restore_configuration(self):
        settings = QSettings()
        self.xtf_file_line_edit.setText(
            settings.value(
                'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import'))

        # Show log
        value_show_log = settings.value(
            'Asistente-LADM-COL/QgisModelBaker/show_log', False, type=bool)
        self.log_config.setCollapsed(not value_show_log)

        # set model repository
        # if there is no option  by default use online model repository
        self.use_local_models = settings.value(
            'Asistente-LADM-COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        if self.use_local_models:
            self.custom_model_directories = settings.value(
                'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR)

    def update_configuration(self):
        """
        Get the configuration that is updated with the user configuration changes on the dialog.
        :return: Configuration
        """
        db_factory = self._dbs_supported.get_db_factory(self.db.engine)

        configuration = ImportDataConfiguration()
        db_factory.set_ili2db_configuration_params(self.db.dict_conn_params,
                                                   configuration)

        configuration.xtffile = self.xtf_file_line_edit.text().strip()
        configuration.delete_data = False

        configuration.srs_auth = QSettings().value(
            'Asistente-LADM-COL/QgisModelBaker/srs_auth', DEFAULT_SRS_AUTH,
            str)
        configuration.srs_code = QSettings().value(
            'Asistente-LADM-COL/QgisModelBaker/srs_code',
            int(DEFAULT_SRS_CODE), int)
        configuration.inheritance = ILI2DBNames.DEFAULT_INHERITANCE
        configuration.create_basket_col = ILI2DBNames.CREATE_BASKET_COL
        configuration.create_import_tid = ILI2DBNames.CREATE_IMPORT_TID
        configuration.stroke_arcs = ILI2DBNames.STROKE_ARCS
        configuration.with_importtid = True

        full_java_exe_path = JavaDependency.get_full_java_exe_path()
        if full_java_exe_path:
            self.base_configuration.java_path = full_java_exe_path

        # User could have changed the default values
        self.use_local_models = QSettings().value(
            'Asistente-LADM-COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        self.custom_model_directories = QSettings().value(
            'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR)

        # Check custom model directories
        if self.use_local_models:
            if not self.custom_model_directories:
                self.base_configuration.custom_model_directories_enabled = False
            else:
                self.base_configuration.custom_model_directories = self.custom_model_directories
                self.base_configuration.custom_model_directories_enabled = True

        configuration.base_configuration = self.base_configuration
        if self.get_ili_models():
            configuration.ilimodels = ';'.join(self.get_ili_models())

        configuration.disable_validation = not QSettings().value(
            'Asistente-LADM-COL/models/validate_data_importing_exporting',
            True, bool)

        return configuration

    def print_info(self, text, text_color='#000000', clear=False):
        self.txtStdout.setTextColor(QColor(text_color))
        self.txtStdout.append(text)
        QCoreApplication.processEvents()

    def on_stderr(self, text):
        color_log_text(text, self.txtStdout)
        self.advance_progress_bar_by_text(text)

    def on_process_started(self, command):
        self.disable()
        self.txtStdout.setTextColor(QColor('#000000'))
        self.txtStdout.clear()
        self.txtStdout.setText(command)
        QCoreApplication.processEvents()

    def on_process_finished(self, exit_code, result):
        color = '#004905' if exit_code == 0 else '#aa2222'
        self.txtStdout.setTextColor(QColor(color))
        self.txtStdout.append('Finished ({})'.format(exit_code))
        if result == iliimporter.Importer.SUCCESS:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.show_message(
                QCoreApplication.translate("DialogImportData",
                                           "Error when importing data"),
                Qgis.Warning)

            # Open log
            if self.log_config.isCollapsed():
                self.log_config.setCollapsed(False)

    def advance_progress_bar_by_text(self, text):
        if text.strip() == 'Info: compile models...':
            self.progress_bar.setValue(50)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: create table structure...':
            self.progress_bar.setValue(55)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: first validation pass...':
            self.progress_bar.setValue(60)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: second validation pass...':
            self.progress_bar.setValue(80)
            QCoreApplication.processEvents()

    def clear_messages(self):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.txtStdout.clear()  # Clear previous log messages
        self.progress_bar.setValue(0)  # Initialize progress bar

    def show_help(self):
        show_plugin_help("import_data")

    def disable(self):
        self.source_config.setEnabled(False)
        self.target_config.setEnabled(False)
        self.buttonBox.setEnabled(False)

    def enable(self):
        self.source_config.setEnabled(True)
        self.target_config.setEnabled(True)
        self.buttonBox.setEnabled(True)

    def show_message(self, message, level):
        if level == Qgis.Warning:
            self.enable()

        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage("Asistente LADM-COL", message, level, duration=0)
Exemple #24
0
class geopunt4QgisAdresDialog(QtGui.QDialog):
    def __init__(self, iface):
        QtGui.QDialog.__init__(self, None)
        self.setWindowFlags(self.windowFlags()
                            & ~QtCore.Qt.WindowContextHelpButtonHint)
        #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
        self.iface = iface

        # initialize locale
        locale = QtCore.QSettings().value("locale/userLocale")[0:2]
        localePath = os.path.join(os.path.dirname(__file__), 'i18n',
                                  'geopunt4qgis_{}.qm'.format(locale))
        if os.path.exists(localePath):
            self.translator = QtCore.QTranslator()
            self.translator.load(localePath)
            if QtCore.qVersion() > '4.3.3':
                QtCore.QCoreApplication.installTranslator(self.translator)

        self._initGui()

    def _initGui(self):
        """setup the user interface"""
        self.ui = Ui_geopunt4Qgis()
        self.ui.setupUi(self)

        #get settings
        self.s = QtCore.QSettings()
        self.loadSettings()

        #setup geometryHelper object
        self.gh = gh.geometryHelper(self.iface)

        #create graphicsLayer
        self.graphicsLayer = []

        #populate gemeenteBox
        gemeentes = json.load(
            open(
                os.path.join(os.path.dirname(__file__),
                             "data/gemeentenVL.json")))
        gemeenteNamen = [str(n["Naam"]) for n in gemeentes]
        self.ui.gemeenteBox.addItems(gemeenteNamen)
        self.ui.gemeenteBox.setEditText(
            QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog",
                                              "gemeente"))
        self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}')
        self.ui.gemeenteBox.setFocus()

        self.completer = QtGui.QCompleter(self)
        self.completerModel = QtGui.QStringListModel(self)
        self.ui.gemeenteBox.setCompleter(self.completer)
        self.completer.setModel(self.completerModel)
        self.completer.setCaseSensitivity(False)
        self.completerModel.setStringList(gemeenteNamen)

        #setup a message bar
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QtGui.QSizePolicy.Minimum,
                               QtGui.QSizePolicy.Fixed)
        self.ui.verticalLayout.addWidget(self.bar)

        self.ui.buttonBox.addButton(QtGui.QPushButton("Sluiten"),
                                    QtGui.QDialogButtonBox.RejectRole)

        #event handlers
        if self.adresSearchOnEnter:
            self.ui.zoekText.returnPressed.connect(self.onZoekActivated)
        else:
            self.ui.zoekText.textEdited.connect(self.onZoekActivated)
        self.ui.gemeenteBox.currentIndexChanged.connect(self.onZoekActivated)
        self.ui.resultLijst.itemDoubleClicked.connect(self.onItemActivated)
        self.ui.resultLijst.itemClicked.connect(self.onItemClick)
        self.ui.ZoomKnop.clicked.connect(self.onZoomKnopClick)
        self.ui.Add2mapKnop.clicked.connect(self.onAdd2mapKnopClick)
        self.ui.buttonBox.helpRequested.connect(self.openHelp)
        self.finished.connect(self.clean)

    def loadSettings(self):
        self.saveToFile = int(self.s.value("geopunt4qgis/adresSavetoFile", 1))
        layerName = self.s.value("geopunt4qgis/adreslayerText", "")
        if layerName:
            self.layerName = layerName
        self.adresSearchOnEnter = int(
            self.s.value("geopunt4qgis/adresSearchOnEnter", 0))
        self.timeout = int(self.s.value("geopunt4qgis/timeout", 15))
        if int(self.s.value("geopunt4qgis/useProxy", 0)):
            self.proxy = self.s.value("geopunt4qgis/proxyHost", "")
            self.port = self.s.value("geopunt4qgis/proxyPort", "")
        else:
            self.proxy = ""
            self.port = ""
        self.startDir = self.s.value("geopunt4qgis/startDir",
                                     os.path.dirname(__file__))
        self.gp = geopunt.Adres(self.timeout, self.proxy, self.port)

    def openHelp(self):
        webbrowser.open_new_tab("http://kgis.be/index.html#!geopuntAddress.md")

    def onZoekActivated(self):
        self._clearGraphicsLayer()
        self.bar.clearWidgets()

        gemeente = self.ui.gemeenteBox.currentText()
        if gemeente <> QtCore.QCoreApplication.translate(
                "geopunt4QgisAdresDialog", "gemeente"):
            self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #000000}')

            txt = self.ui.zoekText.text() + ", " + gemeente

            suggesties = self.gp.fetchSuggestion(txt, 25)

            self.ui.resultLijst.clear()
            if type(suggesties) is list and len(suggesties) <> 0:
                self.ui.resultLijst.addItems(suggesties)
                if len(suggesties) == 1:
                    self.ui.resultLijst.setCurrentRow(0)
            elif type(suggesties) is str:
                self.bar.pushMessage(QtCore.QCoreApplication.translate(
                    "geopunt4QgisAdresDialog", "Waarschuwing"),
                                     suggesties,
                                     level=QgsMessageBar.WARNING)

    def onItemActivated(self, item):
        txt = item.text()
        self._zoomLoc(txt)

    def onItemClick(self, item):
        txt = item.text()
        streetNr = txt.split(",")[:-1]
        self.ui.zoekText.setText(",".join(streetNr))

    def onZoomKnopClick(self):
        item = self.ui.resultLijst.currentItem()
        if item:
            self._zoomLoc(item.text())

    def onAdd2mapKnopClick(self):
        item = self.ui.resultLijst.currentItem()
        if item:
            self._addToMap(item.text())

    def _clearGraphicsLayer(self):
        for graphic in self.graphicsLayer:
            self.iface.mapCanvas().scene().removeItem(graphic)
        self.graphicsLayer = []

    def _zoomLoc(self, txt):
        self._clearGraphicsLayer()
        locations = self.gp.fetchLocation(txt)
        if type(locations) is list and len(locations):
            loc = locations[0]

            LowerLeftX = loc['BoundingBox']['LowerLeft']['X_Lambert72']
            LowerLeftY = loc['BoundingBox']['LowerLeft']['Y_Lambert72']
            UpperRightX = loc['BoundingBox']['UpperRight']['X_Lambert72']
            UpperRightY = loc['BoundingBox']['UpperRight']['Y_Lambert72']

            self.gh.zoomtoRec(QgsPoint(LowerLeftX, LowerLeftY),
                              QgsPoint(UpperRightX, UpperRightY), 31370)

            xlb, ylb = loc["Location"]["X_Lambert72"], loc["Location"][
                "Y_Lambert72"]
            x, y = self.gh.prjPtToMapCrs(QgsPoint(xlb, ylb), 31370)

            m = QgsVertexMarker(self.iface.mapCanvas())
            self.graphicsLayer.append(m)
            m.setCenter(QgsPoint(x, y))
            m.setColor(QtGui.QColor(255, 255, 0))
            m.setIconSize(1)
            m.setIconType(QgsVertexMarker.ICON_BOX)
            m.setPenWidth(9)

        elif type(locations) is str:
            self.bar.pushMessage(QtCore.QCoreApplication.translate(
                "geopunt4QgisAdresDialog", "Waarschuwing"),
                                 locations,
                                 level=QgsMessageBar.WARNING,
                                 duration=3)
        else:
            self.bar.pushMessage("Error",
                                 QtCore.QCoreApplication.translate(
                                     "geopunt4QgisAdresDialog",
                                     "onbekende fout"),
                                 level=QgsMessageBar.CRITICAL,
                                 duration=3)

    def _addToMap(self, txt):
        if not self.layernameValid(): return
        locations = self.gp.fetchLocation(txt)
        if type(locations) is list and len(locations):
            loc = locations[0]
            x, y = loc["Location"]["X_Lambert72"], loc["Location"][
                "Y_Lambert72"]
            adres = loc["FormattedAddress"]
            LocationType = loc["LocationType"]

            pt = self.gh.prjPtToMapCrs(QgsPoint(x, y), 31370)

            self.gh.save_adres_point(pt,
                                     adres,
                                     typeAddress=LocationType,
                                     layername=self.layerName,
                                     saveToFile=self.saveToFile,
                                     sender=self,
                                     startFolder=os.path.join(
                                         self.startDir, self.layerName))

        elif type(locations) is str:
            self.bar.pushMessage(QtCore.QCoreApplication.translate(
                "geopunt4QgisAdresDialog", "Waarschuwing"),
                                 locations,
                                 level=QgsMessageBar.WARNING)
        else:
            self.bar.pushMessage("Error",
                                 QtCore.QCoreApplication.translate(
                                     "geopunt4QgisAdresDialog",
                                     "onbekende fout"),
                                 level=QgsMessageBar.CRITICAL)

    def layernameValid(self):
        if not hasattr(self, 'layerName'):
            layerName, accept = QtGui.QInputDialog.getText(
                None,
                QtCore.QCoreApplication.translate("geopunt4Qgis",
                                                  'Laag toevoegen'),
                QtCore.QCoreApplication.translate(
                    "geopunt4Qgis", 'Geef een naam voor de laag op:'))
            if accept == False:
                return False
            else:
                self.layerName = layerName
        return True

    def clean(self):
        self.bar.clearWidgets()
        self.ui.resultLijst.clear()
        self.ui.zoekText.setText("")
        self.ui.gemeenteBox.setEditText(
            QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog",
                                              "gemeente"))
        self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}')
        self._clearGraphicsLayer()
Exemple #25
0
class AlgorithmDialog(AlgorithmDialogBase):

    def __init__(self, alg):
        AlgorithmDialogBase.__init__(self, alg)

        self.alg = alg

        self.setMainWidget(self.getParametersPanel(alg, self))

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().insertWidget(0, self.bar)

        self.cornerWidget = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 5)
        self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }")
        self.runAsBatchButton = QPushButton(self.tr("Run as batch process..."))
        self.runAsBatchButton.clicked.connect(self.runAsBatch)
        layout.addWidget(self.runAsBatchButton)
        self.cornerWidget.setLayout(layout)
        self.tabWidget.setCornerWidget(self.cornerWidget)

    def getParametersPanel(self, alg, parent):
        return ParametersPanel(parent, alg)

    def runAsBatch(self):
        self.close()
        dlg = BatchAlgorithmDialog(self.alg)
        dlg.show()
        dlg.exec_()

    def getParamValues(self):
        parameters = {}

        for param in self.alg.parameterDefinitions():
            if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
                continue
            if not param.isDestination():
                wrapper = self.mainWidget.wrappers[param.name()]
                value = None
                if wrapper.widget:
                    value = wrapper.value()
                    parameters[param.name()] = value

                    if not param.checkValueIsAcceptable(value):
                        raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget)
            else:
                dest_project = None
                if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
                        isinstance(param, (QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink, OutputTable)):
                    if self.mainWidget.checkBoxes[param.name()].isChecked():
                        dest_project = QgsProject.instance()

                value = self.mainWidget.outputWidgets[param.name()].getValue()
                if value:
                    value.destinationProject = dest_project
                    parameters[param.name()] = value

        return parameters

    def checkExtentCRS(self):
        unmatchingCRS = False
        hasExtent = False
        context = dataobjects.createContext()
        projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
        layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance())
        for param in self.alg.parameterDefinitions():
            if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)):
                if param.value:
                    if isinstance(param, ParameterMultipleInput):
                        inputlayers = param.value.split(';')
                    else:
                        inputlayers = [param.value]
                    for inputlayer in inputlayers:
                        for layer in layers:
                            if layer.source() == inputlayer:
                                if layer.crs() != projectCRS:
                                    unmatchingCRS = True

                        p = QgsProcessingUtils.mapLayerFromString(inputlayer, context)
                        if p is not None:
                            if p.crs() != projectCRS:
                                unmatchingCRS = True
            if isinstance(param, ParameterExtent):
                if param.skip_crs_check:
                    continue

                value = self.mainWidget.wrappers[param.name()].widget.leText.text().strip()
                if value:
                    hasExtent = True

        return hasExtent and unmatchingCRS

    def accept(self):
        super(AlgorithmDialog, self)._saveGeometry()

        context = dataobjects.createContext()

        checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS)
        try:
            feedback = self.createFeedback()

            parameters = self.getParamValues()

            if checkCRS and not self.alg.validateInputCrs(parameters, context):
                reply = QMessageBox.question(self, self.tr("Unmatching CRS's"),
                                             self.tr('Layers do not all use the same CRS. This can '
                                                     'cause unexpected results.\nDo you want to '
                                                     'continue?'),
                                             QMessageBox.Yes | QMessageBox.No,
                                             QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            checkExtentCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS)
            #TODO
            if False and checkExtentCRS and self.checkExtentCRS():
                reply = QMessageBox.question(self, self.tr("Extent CRS"),
                                             self.tr('Extent parameters must use the same CRS as the input layers.\n'
                                                     'Your input layers do not have the same extent as the project, '
                                                     'so the extent might be in a wrong CRS if you have selected it from the canvas.\n'
                                                     'Do you want to continue?'),
                                             QMessageBox.Yes | QMessageBox.No,
                                             QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            ok, msg = self.alg.checkParameterValues(parameters, context)
            if msg:
                QMessageBox.warning(
                    self, self.tr('Unable to execute algorithm'), msg)
                return
            self.btnRun.setEnabled(False)
            self.btnClose.setEnabled(False)
            buttons = self.mainWidget.iterateButtons
            self.iterateParam = None

            for i in range(len(list(buttons.values()))):
                button = list(buttons.values())[i]
                if button.isChecked():
                    self.iterateParam = list(buttons.keys())[i]
                    break

            self.progressBar.setMaximum(0)
            self.lblProgress.setText(self.tr('Processing algorithm...'))
            # Make sure the Log tab is visible before executing the algorithm
            try:
                self.tabWidget.setCurrentIndex(1)
                self.repaint()
            except:
                pass

            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

            self.setInfo(
                self.tr('<b>Algorithm \'{0}\' starting...</b>').format(self.alg.displayName()), escape_html=False)

            feedback.pushInfo(self.tr('Input parameters:'))
            feedback.pushCommandInfo(pformat(parameters))
            feedback.pushInfo('')
            start_time = time.time()

            if self.iterateParam:
                self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
                if executeIterating(self.alg, parameters, self.iterateParam, context, feedback):
                    feedback.pushInfo(
                        self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time)))
                    self.buttonCancel.setEnabled(False)
                    self.finish(parameters, context, feedback)
                else:
                    self.buttonCancel.setEnabled(False)
                    QApplication.restoreOverrideCursor()
                    self.resetGUI()
            else:
                command = self.alg.asPythonCommand(parameters, context)
                if command:
                    ProcessingLog.addToLog(command)
                self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
                result = executeAlgorithm(self.alg, parameters, context, feedback)
                feedback.pushInfo(self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time)))
                feedback.pushInfo(self.tr('Results:'))
                feedback.pushCommandInfo(pformat(result))
                feedback.pushInfo('')

                self.buttonCancel.setEnabled(False)
                self.finish(result, context, feedback)
        except AlgorithmDialogBase.InvalidParameterValue as e:
            try:
                self.buttonBox.accepted.connect(lambda e=e:
                                                e.widget.setPalette(QPalette()))
                palette = e.widget.palette()
                palette.setColor(QPalette.Base, QColor(255, 255, 0))
                e.widget.setPalette(palette)
            except:
                pass
            self.bar.clearWidgets()
            self.bar.pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()),
                                 level=QgsMessageBar.WARNING, duration=5)

    def finish(self, result, context, feedback):
        keepOpen = ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN)

        if self.iterateParam is None:

            if not handleAlgorithmResults(self.alg, context, feedback, not keepOpen):
                self.resetGUI()
                return

        self.executed = True
        self.setInfo(self.tr('Algorithm \'{0}\' finished').format(self.alg.displayName()), escape_html=False)
        QApplication.restoreOverrideCursor()

        if not keepOpen:
            self.close()
        else:
            self.resetGUI()
            if self.alg.hasHtmlOutputs():
                self.setInfo(
                    self.tr('HTML output has been generated by this algorithm.'
                            '\nOpen the results dialog to check it.'), escape_html=False)
Exemple #26
0
class SettingsDialog(QDialog, DIALOG_UI):

    db_connection_changed = pyqtSignal(DBConnector,
                                       bool)  # dbconn, ladm_col_db
    organization_tools_changed = pyqtSignal(str)

    def __init__(self, iface=None, parent=None, qgis_utils=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.iface = iface
        self.log = QgsApplication.messageLog()
        self._db = None
        self.qgis_utils = qgis_utils

        self.ant_tools_initial_chk_value = None

        self._action_type = None
        self.conf_db = ConfigDbSupported()

        self.online_models_radio_button.setChecked(True)
        self.online_models_radio_button.toggled.connect(
            self.model_provider_toggle)
        self.custom_model_directories_line_edit.setText("")
        self.custom_models_dir_button.clicked.connect(
            self.show_custom_model_dir)
        self.custom_model_directories_line_edit.setVisible(False)
        self.custom_models_dir_button.setVisible(False)

        # Set connections
        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.helpRequested.connect(self.show_help)
        self.finished.connect(self.finished_slot)
        self.btn_test_connection.clicked.connect(self.test_connection)
        self.btn_test_ladm_col_structure.clicked.connect(
            self.test_ladm_col_structure)

        self.btn_test_service.clicked.connect(self.test_service)
        self.chk_use_roads.toggled.connect(self.update_images_state)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.setLayout(QGridLayout())
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.cbo_db_source.clear()

        self._lst_db = self.conf_db.get_db_items()
        self._lst_panel = dict()

        for key, value in self._lst_db.items():
            self.cbo_db_source.addItem(value.get_name(), key)
            self._lst_panel[key] = value.get_config_panel()
            self._lst_panel[key].notify_message_requested.connect(
                self.show_message)
            self.db_layout.addWidget(self._lst_panel[key])

        self.db_source_changed()

        # Trigger some default behaviours
        self.restore_settings()

        self.cbo_db_source.currentIndexChanged.connect(self.db_source_changed)

    def showEvent(self, event):
        # It is necessary to reload the variables
        # to load the database and schema name
        self.restore_settings()

        self.btn_test_ladm_col_structure.setVisible(
            self._action_type != EnumDbActionType.SCHEMA_IMPORT)

    def model_provider_toggle(self):
        if self.offline_models_radio_button.isChecked():
            self.custom_model_directories_line_edit.setVisible(True)
            self.custom_models_dir_button.setVisible(True)
        else:
            self.custom_model_directories_line_edit.setVisible(False)
            self.custom_models_dir_button.setVisible(False)
            self.custom_model_directories_line_edit.setText("")

    def _get_db_connector_from_gui(self):
        current_db = self.cbo_db_source.currentData()
        params = self._lst_panel[current_db].read_connection_parameters()
        db = self._lst_db[current_db].get_db_connector(params)

        return db

    def get_db_connection(self):
        if self._db is not None:
            self.log.logMessage("Returning existing db connection...",
                                PLUGIN_NAME, Qgis.Info)
        else:
            self.log.logMessage("Getting new db connection...", PLUGIN_NAME,
                                Qgis.Info)
            self._db = self._get_db_connector_from_gui()

        return self._db

    def show_custom_model_dir(self):
        dlg = CustomModelDirDialog(
            self.custom_model_directories_line_edit.text(), self)
        dlg.exec_()

    def accepted(self):
        current_db = self.cbo_db_source.currentData()
        if self._lst_panel[current_db].state_changed():
            valid_connection = True
            ladm_col_schema = False

            db = self._get_db_connector_from_gui()

            test_level = EnumTestLevel.DB_SCHEMA

            if self._action_type == EnumDbActionType.SCHEMA_IMPORT:
                # Limit the validation (used in GeoPackage)
                test_level |= EnumTestLevel.CREATE_SCHEMA

            res, msg = db.test_connection(test_level=test_level)

            if res:
                if self._action_type != EnumDbActionType.SCHEMA_IMPORT:
                    # Don't check if it's a LADM schema, we expect it to be after the schema import
                    ladm_col_schema, msg = db.test_connection(
                        test_level=EnumTestLevel.LADM)
            else:
                self.show_message(msg, Qgis.Warning)
                valid_connection = False

            if valid_connection:
                if self._db is not None:
                    self._db.close_connection()

                # FIXME is it overwriting itself?
                self._db = None
                self._db = self.get_db_connection()

                self.db_connection_changed.emit(self._db, ladm_col_schema)

                self.save_settings()
                QDialog.accept(self)
            else:
                return  # Do not close the dialog
        else:
            # Save settings from tabs other than database connection
            self.save_settings()
            QDialog.accept(self)

        if self.chk_ant_tools.isChecked() != self.ant_tools_initial_chk_value:
            self.organization_tools_changed.emit(NATIONAL_LAND_AGENCY)

    def reject(self):
        self.done(0)

    def finished_slot(self, result):
        self.bar.clearWidgets()

    def set_db_connection(self, mode, dict_conn):
        """
        To be used by external scripts and unit tests
        """
        self.cbo_db_source.setCurrentIndex(self.cbo_db_source.findData(mode))
        self.db_source_changed()

        current_db = self.cbo_db_source.currentData()

        self._lst_panel[current_db].write_connection_parameters(dict_conn)

        self.accepted()  # Create/update the db object

    def save_settings(self):
        settings = QSettings()
        settings.setValue('Asistente-LADM_COL/db_connection_source',
                          self.cbo_db_source.currentData())

        # Save QSettings
        current_db = self.cbo_db_source.currentData()
        dict_conn = self._lst_panel[current_db].read_connection_parameters()

        for key, value in dict_conn.items():
            settings.setValue('Asistente-LADM_COL/' + current_db + '/' + key,
                              value)

        settings.setValue(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            self.offline_models_radio_button.isChecked())
        if self.offline_models_radio_button.isChecked():
            settings.setValue('Asistente-LADM_COL/models/custom_models',
                              self.custom_model_directories_line_edit.text())

        settings.setValue(
            'Asistente-LADM_COL/quality/too_long_tolerance',
            int(self.txt_too_long_tolerance.text())
            or DEFAULT_TOO_LONG_BOUNDARY_SEGMENTS_TOLERANCE)
        settings.setValue('Asistente-LADM_COL/quality/use_roads',
                          self.chk_use_roads.isChecked())

        settings.setValue(
            'Asistente-LADM_COL/automatic_values/automatic_values_in_batch_mode',
            self.chk_automatic_values_in_batch_mode.isChecked())
        settings.setValue('Asistente-LADM_COL/sources/document_repository',
                          self.connection_box.isChecked())

        settings.setValue('Asistente-LADM_COL/advanced_settings/ant_tools',
                          self.chk_ant_tools.isChecked())
        settings.setValue(
            'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting',
            self.chk_validate_data_importing_exporting.isChecked())

        endpoint = self.txt_service_endpoint.text().strip()
        settings.setValue(
            'Asistente-LADM_COL/sources/service_endpoint',
            (endpoint[:-1] if endpoint.endswith('/') else endpoint)
            or DEFAULT_ENDPOINT_SOURCE_SERVICE)

        # Changes in automatic namespace or local_id configuration?
        current_namespace_enabled = settings.value(
            'Asistente-LADM_COL/automatic_values/namespace_enabled', True,
            bool)
        current_namespace_prefix = settings.value(
            'Asistente-LADM_COL/automatic_values/namespace_prefix', "")
        current_local_id_enabled = settings.value(
            'Asistente-LADM_COL/automatic_values/local_id_enabled', True, bool)

        settings.setValue(
            'Asistente-LADM_COL/automatic_values/namespace_enabled',
            self.namespace_collapsible_group_box.isChecked())
        if self.namespace_collapsible_group_box.isChecked():
            settings.setValue(
                'Asistente-LADM_COL/automatic_values/namespace_prefix',
                self.txt_namespace.text())

        settings.setValue(
            'Asistente-LADM_COL/automatic_values/local_id_enabled',
            self.chk_local_id.isChecked())

        if current_namespace_enabled != self.namespace_collapsible_group_box.isChecked() or \
           current_namespace_prefix != self.txt_namespace.text() or \
           current_local_id_enabled != self.chk_local_id.isChecked():

            self.qgis_utils.automatic_namespace_local_id_configuration_changed(
                self._db)

    def _restore_settings_db(self):
        settings = QSettings()
        # reload all panels
        for index_db, item_db in self._lst_panel.items():
            dict_conn = dict()
            keys = item_db.get_keys_connection_parameters()
            for key in keys:
                dict_conn[key] = settings.value('Asistente-LADM_COL/' +
                                                index_db + '/' + key)

            item_db.write_connection_parameters(dict_conn)
            item_db.save_state()

    def restore_settings(self):
        # Restore QSettings
        settings = QSettings()
        default_db = self.conf_db.id_default_db

        index_db = self.cbo_db_source.findData(
            settings.value('Asistente-LADM_COL/db_connection_source',
                           default_db))

        if index_db == -1:
            index_db = self.cbo_db_source.findData(default_db)

        self.cbo_db_source.setCurrentIndex(index_db)
        self.db_source_changed()

        self._restore_settings_db()

        custom_model_directories_is_checked = settings.value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            type=bool)
        if custom_model_directories_is_checked:
            self.offline_models_radio_button.setChecked(True)
            self.custom_model_directories_line_edit.setText(
                settings.value('Asistente-LADM_COL/models/custom_models'))
            self.custom_model_directories_line_edit.setVisible(True)
            self.custom_models_dir_button.setVisible(True)
        else:
            self.online_models_radio_button.setChecked(True)
            self.custom_model_directories_line_edit.setText("")
            self.custom_model_directories_line_edit.setVisible(False)
            self.custom_models_dir_button.setVisible(False)

        self.txt_too_long_tolerance.setText(
            str(
                settings.value('Asistente-LADM_COL/quality/too_long_tolerance',
                               DEFAULT_TOO_LONG_BOUNDARY_SEGMENTS_TOLERANCE)))
        use_roads = settings.value('Asistente-LADM_COL/quality/use_roads',
                                   True, bool)
        self.chk_use_roads.setChecked(use_roads)
        self.update_images_state(use_roads)

        self.chk_automatic_values_in_batch_mode.setChecked(
            settings.value(
                'Asistente-LADM_COL/automatic_values/automatic_values_in_batch_mode',
                True, bool))
        self.connection_box.setChecked(
            settings.value('Asistente-LADM_COL/sources/document_repository',
                           True, bool))
        self.namespace_collapsible_group_box.setChecked(
            settings.value(
                'Asistente-LADM_COL/automatic_values/namespace_enabled', True,
                bool))
        self.chk_local_id.setChecked(
            settings.value(
                'Asistente-LADM_COL/automatic_values/local_id_enabled', True,
                bool))
        self.txt_namespace.setText(
            str(
                settings.value(
                    'Asistente-LADM_COL/automatic_values/namespace_prefix',
                    "")))

        self.ant_tools_initial_chk_value = settings.value(
            'Asistente-LADM_COL/advanced_settings/ant_tools', False, bool)
        self.chk_ant_tools.setChecked(self.ant_tools_initial_chk_value)

        self.chk_validate_data_importing_exporting.setChecked(
            settings.value(
                'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting',
                True, bool))

        self.txt_service_endpoint.setText(
            settings.value('Asistente-LADM_COL/sources/service_endpoint',
                           DEFAULT_ENDPOINT_SOURCE_SERVICE))

    def db_source_changed(self):
        if self._db is not None:
            self._db.close_connection()

        self._db = None  # Reset db connection

        for key, value in self._lst_panel.items():
            value.setVisible(False)

        current_db = self.cbo_db_source.currentData()

        self._lst_panel[current_db].setVisible(True)

    def test_connection(self):
        db = self._get_db_connector_from_gui()

        test_level = EnumTestLevel.DB_SCHEMA

        if self._action_type == EnumDbActionType.SCHEMA_IMPORT:
            test_level |= EnumTestLevel.CREATE_SCHEMA

        res, msg = db.test_connection(test_level=test_level)

        if db is not None:
            db.close_connection()

        self.show_message(msg, Qgis.Info if res else Qgis.Warning)
        self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info)

    def test_ladm_col_structure(self):
        db = self._get_db_connector_from_gui()
        test_level = EnumTestLevel.LADM

        res, msg = db.test_connection(test_level=test_level)

        if db is not None:
            db.close_connection()

        self.show_message(msg, Qgis.Info if res else Qgis.Warning)
        self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info)

    def test_service(self):
        self.setEnabled(False)
        QCoreApplication.processEvents()
        res, msg = self.is_source_service_valid()
        self.setEnabled(True)
        self.show_message(msg['text'], msg['level'])

    def is_source_service_valid(self):
        res = False
        msg = {'text': '', 'level': Qgis.Warning}
        url = self.txt_service_endpoint.text().strip()
        if url:
            with OverrideCursor(Qt.WaitCursor):
                self.qgis_utils.status_bar_message_emitted.emit(
                    "Checking source service availability (this might take a while)...",
                    0)
                QCoreApplication.processEvents()
                if self.qgis_utils.is_connected(TEST_SERVER):

                    nam = QNetworkAccessManager()
                    request = QNetworkRequest(QUrl(url))
                    reply = nam.get(request)

                    loop = QEventLoop()
                    reply.finished.connect(loop.quit)
                    loop.exec_()

                    allData = reply.readAll()
                    response = QTextStream(allData, QIODevice.ReadOnly)
                    status = reply.attribute(
                        QNetworkRequest.HttpStatusCodeAttribute)
                    if status == 200:
                        try:
                            data = json.loads(response.readAll())
                            if 'id' in data and data[
                                    'id'] == SOURCE_SERVICE_EXPECTED_ID:
                                res = True
                                msg['text'] = QCoreApplication.translate(
                                    "SettingsDialog",
                                    "The tested service is valid to upload files!"
                                )
                                msg['level'] = Qgis.Info
                            else:
                                res = False
                                msg['text'] = QCoreApplication.translate(
                                    "SettingsDialog",
                                    "The tested upload service is not compatible: no valid 'id' found in response."
                                )
                        except json.decoder.JSONDecodeError as e:
                            res = False
                            msg['text'] = QCoreApplication.translate(
                                "SettingsDialog",
                                "Response from the tested service is not compatible: not valid JSON found."
                            )
                    else:
                        res = False
                        msg['text'] = QCoreApplication.translate(
                            "SettingsDialog",
                            "There was a problem connecting to the server. The server might be down or the service cannot be reached at the given URL."
                        )
                else:
                    res = False
                    msg['text'] = QCoreApplication.translate(
                        "SettingsDialog",
                        "There was a problem connecting to Internet.")

                self.qgis_utils.clear_status_bar_emitted.emit()
        else:
            res = False
            msg['text'] = QCoreApplication.translate(
                "SettingsDialog", "Not valid service URL to test!")

        return (res, msg)

    def show_message(self, message, level):
        self.bar.pushMessage(message, level, 10)

    def update_images_state(self, checked):
        self.img_with_roads.setEnabled(checked)
        self.img_with_roads.setToolTip(
            QCoreApplication.translate(
                "SettingsDialog", "Missing roads will be marked as errors."
            ) if checked else '')
        self.img_without_roads.setEnabled(not checked)
        self.img_without_roads.setToolTip(
            '' if checked else QCoreApplication.translate(
                "SettingsDialog", "Missing roads will not be marked as errors."
            ))

    def show_help(self):
        self.qgis_utils.show_help("settings")

    def set_action_type(self, action_type):
        self._action_type = action_type

        for key, value in self._lst_panel.items():
            value.set_action(action_type)
Exemple #27
0
class AlgorithmDialog(AlgorithmDialogBase):
    def __init__(self, alg):
        AlgorithmDialogBase.__init__(self, alg)

        self.alg = alg

        self.setMainWidget(self.getParametersPanel(alg, self))

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().insertWidget(0, self.bar)

        self.cornerWidget = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 5)
        self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }")
        self.runAsBatchButton = QPushButton(self.tr("Run as batch process..."))
        self.runAsBatchButton.clicked.connect(self.runAsBatch)
        layout.addWidget(self.runAsBatchButton)
        self.cornerWidget.setLayout(layout)
        self.tabWidget.setCornerWidget(self.cornerWidget)

    def getParametersPanel(self, alg, parent):
        return ParametersPanel(parent, alg)

    def runAsBatch(self):
        self.close()
        dlg = BatchAlgorithmDialog(self.alg)
        dlg.show()
        dlg.exec_()

    def getParamValues(self):
        parameters = {}

        for param in self.alg.parameterDefinitions():
            if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
                continue
            if not param.isDestination():
                wrapper = self.mainWidget.wrappers[param.name()]
                value = None
                if wrapper.widget:
                    value = wrapper.value()
                    parameters[param.name()] = value

                    if not param.checkValueIsAcceptable(value):
                        raise AlgorithmDialogBase.InvalidParameterValue(
                            param, wrapper.widget)
            else:
                dest_project = None
                if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
                        isinstance(param, (QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink, OutputTable)):
                    if self.mainWidget.checkBoxes[param.name()].isChecked():
                        dest_project = QgsProject.instance()

                value = self.mainWidget.outputWidgets[param.name()].getValue()
                if value:
                    value.destinationProject = dest_project
                    parameters[param.name()] = value

        return parameters

    def checkExtentCRS(self):
        unmatchingCRS = False
        hasExtent = False
        context = dataobjects.createContext()
        projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
        layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance())
        for param in self.alg.parameterDefinitions():
            if isinstance(
                    param,
                (ParameterRaster, ParameterVector, ParameterMultipleInput)):
                if param.value:
                    if isinstance(param, ParameterMultipleInput):
                        inputlayers = param.value.split(';')
                    else:
                        inputlayers = [param.value]
                    for inputlayer in inputlayers:
                        for layer in layers:
                            if layer.source() == inputlayer:
                                if layer.crs() != projectCRS:
                                    unmatchingCRS = True

                        p = QgsProcessingUtils.mapLayerFromString(
                            inputlayer, context)
                        if p is not None:
                            if p.crs() != projectCRS:
                                unmatchingCRS = True
            if isinstance(param, ParameterExtent):
                if param.skip_crs_check:
                    continue

                value = self.mainWidget.wrappers[
                    param.name()].widget.leText.text().strip()
                if value:
                    hasExtent = True

        return hasExtent and unmatchingCRS

    def accept(self):
        super(AlgorithmDialog, self)._saveGeometry()

        context = dataobjects.createContext()

        checkCRS = ProcessingConfig.getSetting(
            ProcessingConfig.WARN_UNMATCHING_CRS)
        try:
            parameters = self.getParamValues()

            QgsMessageLog.logMessage(str(parameters), 'Processing',
                                     QgsMessageLog.CRITICAL)

            # TODO
            if False and checkCRS and not self.alg.checkInputCRS():
                reply = QMessageBox.question(
                    self, self.tr("Unmatching CRS's"),
                    self.tr('Layers do not all use the same CRS. This can '
                            'cause unexpected results.\nDo you want to '
                            'continue?'), QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            checkExtentCRS = ProcessingConfig.getSetting(
                ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS)
            #TODO
            if False and checkExtentCRS and self.checkExtentCRS():
                reply = QMessageBox.question(
                    self, self.tr("Extent CRS"),
                    self.
                    tr('Extent parameters must use the same CRS as the input layers.\n'
                       'Your input layers do not have the same extent as the project, '
                       'so the extent might be in a wrong CRS if you have selected it from the canvas.\n'
                       'Do you want to continue?'),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            ok, msg = self.alg.checkParameterValues(parameters, context)
            if msg:
                QMessageBox.warning(self,
                                    self.tr('Unable to execute algorithm'),
                                    msg)
                return
            self.btnRun.setEnabled(False)
            self.btnClose.setEnabled(False)
            buttons = self.mainWidget.iterateButtons
            self.iterateParam = None

            for i in range(len(list(buttons.values()))):
                button = list(buttons.values())[i]
                if button.isChecked():
                    self.iterateParam = list(buttons.keys())[i]
                    break

            self.progressBar.setMaximum(0)
            self.lblProgress.setText(self.tr('Processing algorithm...'))
            # Make sure the Log tab is visible before executing the algorithm
            try:
                self.tabWidget.setCurrentIndex(1)
                self.repaint()
            except:
                pass

            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

            self.setInfo(
                self.tr('<b>Algorithm {0} starting...</b>').format(
                    self.alg.displayName()))

            if self.iterateParam:
                if executeIterating(self.alg, parameters, self.iterateParam,
                                    context, self.feedback):
                    self.finish(parameters, context)
                else:
                    QApplication.restoreOverrideCursor()
                    self.resetGUI()
            else:
                # TODO
                #command = self.alg.getAsCommand()
                #if command:
                #    ProcessingLog.addToLog(command)
                self.buttonCancel.setEnabled(
                    self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
                result = executeAlgorithm(self.alg, parameters, context,
                                          self.feedback)
                self.buttonCancel.setEnabled(False)
                self.finish(result, context)
                #TODO
                #else:
                #    QApplication.restoreOverrideCursor()
                #    self.resetGUI()
        except AlgorithmDialogBase.InvalidParameterValue as e:
            try:
                self.buttonBox.accepted.connect(
                    lambda e=e: e.widget.setPalette(QPalette()))
                palette = e.widget.palette()
                palette.setColor(QPalette.Base, QColor(255, 255, 0))
                e.widget.setPalette(palette)
            except:
                pass
            self.bar.clearWidgets()
            self.bar.pushMessage(
                "",
                self.tr("Wrong or missing parameter value: {0}").format(
                    e.parameter.description()),
                level=QgsMessageBar.WARNING,
                duration=5)

    def finish(self, result, context):
        keepOpen = ProcessingConfig.getSetting(
            ProcessingConfig.KEEP_DIALOG_OPEN)

        if self.iterateParam is None:

            if not handleAlgorithmResults(self.alg, context, self.feedback,
                                          not keepOpen):
                self.resetGUI()
                return

        self.executed = True
        self.setInfo(
            self.tr('Algorithm {0} finished').format(self.alg.displayName()))
        QApplication.restoreOverrideCursor()

        if not keepOpen:
            self.close()
        else:
            self.resetGUI()
            if self.alg.hasHtmlOutputs():
                self.setInfo(
                    self.tr('HTML output has been generated by this algorithm.'
                            '\nOpen the results dialog to check it.'))

    def closeEvent(self, event):
        QgsProject.instance().layerWasAdded.disconnect(
            self.mainWidget.layerRegistryChanged)
        QgsProject.instance().layersWillBeRemoved.disconnect(
            self.mainWidget.layerRegistryChanged)
        super(AlgorithmDialog, self).closeEvent(event)
Exemple #28
0
class AssociateExtAddressWizard(QWizard, WIZARD_UI):

    def __init__(self, iface, db, qgis_utils, parent=None):
        QWizard.__init__(self, parent)
        self.setupUi(self)
        self.iface = iface
        self.log = QgsApplication.messageLog()
        self._db = db
        self.qgis_utils = qgis_utils
        self.canvas = self.iface.mapCanvas()
        self.maptool = self.canvas.mapTool()
        self.select_maptool = None
        self.help_strings = HelpStrings()
        self.translatable_config_strings = TranslatableConfigStrings()
        self._extaddress_layer = None
        self._plot_layer = None
        self._building_layer = None
        self._building_unit_layer = None
        self._current_layer = None

        self._feature_tid = None
        self._extaddress_tid = None

        self.restore_settings()

        self.rad_to_plot.toggled.connect(self.adjust_page_1_controls)
        self.rad_to_building.toggled.connect(self.adjust_page_1_controls)
        self.rad_to_building_unit.toggled.connect(self.adjust_page_1_controls)
        self.adjust_page_1_controls()
        self.button(QWizard.NextButton).clicked.connect(self.prepare_selection)
        self.button(QWizard.FinishButton).clicked.connect(self.finished_dialog)
        self.button(QWizard.HelpButton).clicked.connect(self.show_help)

        self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PolygonLayer)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.setLayout(QGridLayout())
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def adjust_page_1_controls(self):
        self.cbo_mapping.clear()
        self.cbo_mapping.addItem("")
        self.cbo_mapping.addItems(self.qgis_utils.get_field_mappings_file_names(EXTADDRESS_TABLE))

        if self.rad_refactor.isChecked():
            self.lbl_refactor_source.setEnabled(True)
            self.mMapLayerComboBox.setEnabled(True)
            self.lbl_field_mapping.setEnabled(True)
            self.cbo_mapping.setEnabled(True)
            disable_next_wizard(self)
            FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard", "Import")
            self.txt_help_page_1.setHtml(self.help_strings.get_refactor_help_string(EXTADDRESS_TABLE, True))
            self.wizardPage1.setFinalPage(True)
            self.wizardPage1.setButtonText(QWizard.FinishButton,
                                           QCoreApplication.translate("AssociateExtAddressWizard",
                                                                      FinishButton_text))

        elif self.rad_to_plot.isChecked():
            self.lbl_refactor_source.setEnabled(False)
            self.mMapLayerComboBox.setEnabled(False)
            self.lbl_field_mapping.setEnabled(False)
            self.cbo_mapping.setEnabled(False)
            self.wizardPage1.setFinalPage(False)
            enable_next_wizard(self)
            FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard", "Associate Plot ExtAddress")
            self.txt_help_page_1.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_1)

        elif self.rad_to_building.isChecked():
            self.lbl_refactor_source.setEnabled(False)
            self.mMapLayerComboBox.setEnabled(False)
            self.lbl_field_mapping.setEnabled(False)
            self.cbo_mapping.setEnabled(False)
            self.wizardPage1.setFinalPage(False)
            enable_next_wizard(self)
            FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard", "Associate Building ExtAddress")
            self.txt_help_page_1.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_2)

        else:  # self.rad_to_building_unit.isChecked():
            self.lbl_refactor_source.setEnabled(False)
            self.mMapLayerComboBox.setEnabled(False)
            self.lbl_field_mapping.setEnabled(False)
            self.cbo_mapping.setEnabled(False)
            self.wizardPage1.setFinalPage(False)
            enable_next_wizard(self)
            FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard",
                                                           "Associate Building Unit ExtAddress")
            self.txt_help_page_1.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_3)

        self.wizardPage2.setButtonText(QWizard.FinishButton,
                                       QCoreApplication.translate('AssociateExtAddressWizard',
                                                                  FinishButton_text))

    def prepare_selection(self):
        self.button(self.FinishButton).setDisabled(True)
        if self.rad_to_plot.isChecked():
            self.btn_select.setText(QCoreApplication.translate("AssociateExtAddressWizard",
                                                               "Select Plot"))
            self.txt_help_page_2.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_1)
            # Load layers
            res_layers = self.qgis_utils.get_layers(self._db, {
                EXTADDRESS_TABLE: {'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry},
                OID_TABLE: {'name': OID_TABLE, 'geometry': None},
                PLOT_TABLE: {'name': PLOT_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry}
            }, load=True)

            self._extaddress_layer = res_layers[EXTADDRESS_TABLE]
            self._oid_layer = res_layers[OID_TABLE]
            self._plot_layer = res_layers[PLOT_TABLE]
            self._current_layer = self._plot_layer

        elif self.rad_to_building.isChecked():
            self.btn_select.setText(QCoreApplication.translate("AssociateExtAddressWizard",
                                                               "Select Building"))
            self.txt_help_page_2.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_2)

            # Load layers
            res_layers = self.qgis_utils.get_layers(self._db, {
                EXTADDRESS_TABLE: {'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry},
                OID_TABLE: {'name': OID_TABLE, 'geometry': None},
                BUILDING_TABLE: {'name': BUILDING_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry}
            }, load=True)

            self._extaddress_layer = res_layers[EXTADDRESS_TABLE]
            self._building_layer = res_layers[BUILDING_TABLE]
            self._oid_layer = res_layers[OID_TABLE]
            self._current_layer = self._building_layer

        else:  # self.rad_to_building_unit.isChecked():
            self.btn_select.setText(QCoreApplication.translate("AssociateExtAddressWizard",
                                                               "Select Building Unit"))
            self.txt_help_page_2.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_3)

            # Load layers
            res_layers = self.qgis_utils.get_layers(self._db, {
                EXTADDRESS_TABLE: {'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry},
                OID_TABLE: {'name': OID_TABLE, 'geometry': None},
                BUILDING_UNIT_TABLE: {'name': BUILDING_UNIT_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry}
            }, load=True)

            self._extaddress_layer = res_layers[EXTADDRESS_TABLE]
            self._building_unit_layer = res_layers[BUILDING_UNIT_TABLE]
            self._oid_layer = res_layers[OID_TABLE]
            self._current_layer = self._building_unit_layer

        self.iface.setActiveLayer(self._current_layer)
        self.check_selected_features()
        self.btn_select.clicked.connect(self.select_feature)
        self.btn_select_by_expression.clicked.connect(self.select_feature_by_expression)

    def check_selected_features(self):
        self.bar.clearWidgets()

        if self._current_layer.selectedFeatureCount() == 1:
            self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard",
                                                                 "1 Feature Selected"))
            self.button(self.FinishButton).setDisabled(False)
            self._feature_tid = self._current_layer.selectedFeatures()[0][ID_FIELD]
            self.canvas.zoomToSelected(self._current_layer)
        elif self._current_layer.selectedFeatureCount() > 1:
            self.show_message(QCoreApplication.translate("AssociateExtAddressWizard",
                                                         "Please select just one feature"), Qgis.Warning)
            self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard",
                                                                 "{} Feature(s) Selected".format(
                                                                     self._current_layer.selectedFeatureCount())))
            self.button(self.FinishButton).setDisabled(True)
        else:
            self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard",
                                                                 "0 Features Selected"))
            self.button(self.FinishButton).setDisabled(True)

    def select_feature_by_expression(self):
        Dlg_expression_selection = QgsExpressionSelectionDialog(self._current_layer)
        self._current_layer.selectionChanged.connect(self.check_selected_features)
        Dlg_expression_selection.exec()
        self._current_layer.selectionChanged.disconnect(self.check_selected_features)

    def select_feature(self):
        self.setVisible(False)  # Make wizard disappear

        # Create maptool
        self.select_maptool = SelectMapTool(self.canvas, self._current_layer, multi=False)
        self.canvas.setMapTool(self.select_maptool)
        self.select_maptool.features_selected_signal.connect(self.feature_selected)

        # TODO: Take into account that a user can select another tool

    def feature_selected(self):
        self.setVisible(True)  # Make wizard appear
        feature = self._current_layer.selectedFeatures()[0]  # Get selected feature

        if feature:
            self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard",
                                                                 "1 Feature Selected"))
            self._current_layer.selectByIds([feature.id()])

        self.canvas.setMapTool(self.maptool)
        self.check_selected_features()

        self.select_maptool.features_selected_signal.disconnect(self.feature_selected)
        self.log.logMessage("Spatial Unit's featureIdentified SIGNAL disconnected", PLUGIN_NAME, Qgis.Info)

    def finished_dialog(self):
        self.save_settings()

        if self.rad_refactor.isChecked():
            if self.mMapLayerComboBox.currentLayer() is not None:
                field_mapping = self.cbo_mapping.currentText()
                res_etl_model = self.qgis_utils.show_etl_model(self._db,
                                                               self.mMapLayerComboBox.currentLayer(),
                                                               EXTADDRESS_TABLE,
                                                               field_mapping=field_mapping)

                if res_etl_model:
                    if field_mapping:
                        self.qgis_utils.delete_old_field_mapping(field_mapping)

                    self.qgis_utils.save_field_mapping(EXTADDRESS_TABLE)

            else:
                self.iface.messageBar().pushMessage('Asistente LADM_COL',
                                                    QCoreApplication.translate("AssociateExtAddressWizard",
                                                                               "Select a source layer to set the field mapping to '{}'.").format(
                                                        EXTADDRESS_TABLE),
                                                    Qgis.Warning)

        else:
            self.prepare_extaddress_creation()

    def prepare_extaddress_creation(self):
        # Don't suppress (i.e., show) feature form
        form_config = self._extaddress_layer.editFormConfig()
        form_config.setSuppress(QgsEditFormConfig.SuppressOff)
        self._extaddress_layer.setEditFormConfig(form_config)

        # Suppress (i.e., hide) feature form
        form_config = self._oid_layer.editFormConfig()
        form_config.setSuppress(QgsEditFormConfig.SuppressOn)
        self._oid_layer.setEditFormConfig(form_config)

        self.edit_extaddress()

    def edit_extaddress(self):
        if self._current_layer.selectedFeatureCount() == 1:
            # Open Form
            self.iface.layerTreeView().setCurrentLayer(self._extaddress_layer)
            self._extaddress_layer.startEditing()
            self.iface.actionAddFeature().trigger()

            # Create connections to react when a feature is added to buffer and
            # when it gets stored into the DB
            self._extaddress_layer.featureAdded.connect(self.call_extaddress_commit)

        else:
            self.iface.messageBar().pushMessage("Asistente LADM_COL",
                                                QCoreApplication.translate("AssociateExtAddressWizard",
                                                                           "Please select a feature"),
                                                Qgis.Warning)

    def call_extaddress_commit(self, fid):
        plot_field_idx = self._extaddress_layer.getFeature(fid).fieldNameIndex(EXTADDRESS_PLOT_FIELD)
        building_field_idx = self._extaddress_layer.getFeature(fid).fieldNameIndex(EXTADDRESS_BUILDING_FIELD)
        building_unit_field_idx = self._extaddress_layer.getFeature(fid).fieldNameIndex(EXTADDRESS_BUILDING_UNIT_FIELD)
        self._extaddress_tid = self._extaddress_layer.getFeature(fid)[ID_FIELD]

        if self._current_layer.name() == PLOT_TABLE:
            self._extaddress_layer.changeAttributeValue(fid, plot_field_idx, self._feature_tid)
        elif self._current_layer.name() == BUILDING_TABLE:
            self._extaddress_layer.changeAttributeValue(fid, building_field_idx, self._feature_tid)
        else:  # self._current_layer.name() == BUILDING_UNIT_TABLE:
            self._extaddress_layer.changeAttributeValue(fid, building_unit_field_idx, self._feature_tid)

        self._extaddress_layer.featureAdded.disconnect(self.call_extaddress_commit)
        self.log.logMessage("Extaddress's featureAdded SIGNAL disconnected", PLUGIN_NAME, Qgis.Info)
        res = self._extaddress_layer.commitChanges()
        self._current_layer.removeSelection()
        self.add_oid_feature()

    def add_oid_feature(self):
        # Add OID record
        self._oid_layer.startEditing()
        feature = QgsVectorLayerUtils().createFeature(self._oid_layer)
        feature.setAttribute(OID_EXTADDRESS_ID_FIELD, self._extaddress_tid)
        self._oid_layer.addFeature(feature)
        self._oid_layer.commitChanges()

    def show_message(self, message, level):
        self.bar.pushMessage(message, level, 0)

    def save_settings(self):
        settings = QSettings()

        load_data_type = 'refactor'
        if self.rad_to_plot.isChecked():
            load_data_type = 'to_plot'
        elif self.rad_to_building.isChecked():
            load_data_type = 'to_building'
        else:  # self.rad_to_building_unit.isChecked():
            load_data_type = 'to_building_unit'

        settings.setValue('Asistente-LADM_COL/wizards/ext_address_load_data_type', load_data_type)

    def restore_settings(self):
        settings = QSettings()

        load_data_type = settings.value('Asistente-LADM_COL/wizards/ext_address_load_data_type', 'to_plot')
        if load_data_type == 'refactor':
            self.rad_refactor.setChecked(True)
        elif load_data_type == 'to_plot':
            self.rad_to_plot.setChecked(True)
        elif load_data_type == 'to_building':
            self.rad_to_building.setChecked(True)
        else:  # load_data_type == 'to_building_unit':
            self.rad_to_building_unit.setChecked(True)

    def show_help(self):
        self.qgis_utils.show_help("associate_ext_address")
class BackupedImgUploaderWizard(QtGui.QWizard, FORM_CLASS):

    def __init__(self, iface, settings, parent=None):
        """Constructor."""
        super(BackupedImgUploaderWizard, self).__init__(parent)
        # Set up the user interface from Designer.
        # After 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.iface = iface
        self.setupUi(self)

        # Message bars need to be attached to pages, since the wizard object
        # does not have a layout. It doesn't work to attach the same bar
        # object to all pages (it is only shown in the last one). The only way
        # I could make it work was to create different QgsMessageBar objects,
        # one per page, but it is very to keep track of those references
        # along the code. It is messy, there should be a better solution.

        self.bar0 = QgsMessageBar()
        self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(0).layout().addWidget(self.bar0)

        self.bar1 = QgsMessageBar()
        self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(1).layout().addWidget(self.bar1)

        self.bar2 = QgsMessageBar()
        self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.page(2).layout().addWidget(self.bar2)

        self.setButtonText(QtGui.QWizard.CustomButton1, self.tr("&Start upload"));
        self.setOption(QtGui.QWizard.HaveCustomButton1, True);

        self.settings = settings

        # Dictionaries to save imagery info (todo: defined as a classes in the future)
        self.metadata = {}
        self.reprojected = []
        self.licensed = []

        # Initialize layers and default settings
        self.loadLayers()
        self.loadMetadataSettings()
        self.loadStorageSettings()
        self.loadOptionsSettings()

        # register event handlers
        """ List of page navigation buttons in QWizard, for reference.
        Please comment out and implement following functions if necessary."""
        #self.button(QWizard.BackButton).clicked.connect(self.previousPage)
        #self.button(QWizard.NextButton).clicked.connect(self.nextPage)
        #self.button(QWizard.FinishButton).clicked.connect(self.finishWizard)
        #self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard)

        # Imagery connections (wizard page 1)
        self.layers_tool_button.clicked.connect(self.loadLayers)
        self.file_tool_button.clicked.connect(self.selectFile)
        self.add_source_button.clicked.connect(self.addSources)
        self.remove_source_button.clicked.connect(self.removeSources)
        self.up_source_button.clicked.connect(self.upSource)
        self.down_source_button.clicked.connect(self.downSource)

        # Metadata connections (wizard page 2)
        self.sense_start_edit.setCalendarPopup(1)
        self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm')
        self.sense_end_edit.setCalendarPopup(1)
        self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm')
        self.default_button.clicked.connect(self.loadMetadataSettings)
        self.clean_button.clicked.connect(self.cleanMetadataSettings)
        self.save_button.clicked.connect(self.saveMetadata)

        # Upload tab connections (wizard page 3)
        self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify)
        self.customButtonClicked.connect(self.startUploader)

    # event handling for wizard page 1
    def loadLayers(self):
        all_layers = self.iface.mapCanvas().layers()
        for layer in all_layers:
            if not self.layers_list_widget.findItems(layer.name(),Qt.MatchExactly):
                item = QListWidgetItem()
                item.setText(layer.name())
                item.setData(Qt.UserRole, layer.dataProvider().dataSourceUri())
                self.layers_list_widget.addItem(item)

    def selectFile(self):
        selected_file = QFileDialog.getOpenFileName(
            self,
            'Select imagery file',
            os.path.expanduser("~"))
        self.source_file_edit.setText(selected_file)

    def addSources(self):
        filename = self.source_file_edit.text()
        selected_layers = self.layers_list_widget.selectedItems()
        if not filename and not selected_layers:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                'WARNING',
                'Either a layer or file should be selected to be added',
                level=QgsMessageBar.WARNING)
        else:
            added = False
            if filename:
                if self.validateFile(filename):
                    if not self.sources_list_widget.findItems(filename,Qt.MatchExactly):
                        item = QListWidgetItem()
                        item.setText(os.path.basename(filename))
                        item.setData(Qt.UserRole, filename)
                        self.sources_list_widget.addItem(item)
                        self.added_sources_list_widget.addItem(item.clone())
                        self.source_file_edit.setText('')
                        added = True
            if selected_layers:
                for item in selected_layers:
                    if self.validateLayer(item.text()):
                        if not self.sources_list_widget.findItems(item.text(),Qt.MatchExactly):
                            self.layers_list_widget.takeItem(self.layers_list_widget.row(item))
                            self.sources_list_widget.addItem(item)
                            self.added_sources_list_widget.addItem(item.clone())
                            added = True
            if added:
                self.bar0.clearWidgets()
                self.bar0.pushMessage(
                    'INFO',
                    'Source(s) added to the upload queue',
                    level=QgsMessageBar.INFO)
                self.loadMetadataReviewBox()

    def removeSources(self):
        selected_sources = self.sources_list_widget.selectedItems()
        added_sources = self.added_sources_list_widget.selectedItems()
        if selected_sources:
            for item in selected_sources:
                self.sources_list_widget.takeItem(self.sources_list_widget.row(item))
                added_item = self.added_sources_list_widget.findItems(item.text(),Qt.MatchExactly)
                if added_item:
                    self.added_sources_list_widget.takeItem(self.added_sources_list_widget.row(added_item[0]))
                all_layers = self.iface.mapCanvas().layers()
                for layer in all_layers:
                    if item.text() == layer.name():
                        if not self.layers_list_widget.findItems(item.text(),Qt.MatchExactly):
                            self.layers_list_widget.addItem(item)
        else:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                'WARNING',
                'An imagery source must be selected to be removed',
                level=QgsMessageBar.WARNING)

    def upSource(self):
        selected_layers = self.sources_list_widget.selectedItems()
        if selected_layers:
            position = self.sources_list_widget.row(selected_layers[0])
            if position > 0:
                item = self.sources_list_widget.takeItem(position)
                self.sources_list_widget.insertItem(position-1,item)
                item.setSelected(1)

    def downSource(self):
        selected_layers = self.sources_list_widget.selectedItems()
        if selected_layers:
            position = self.sources_list_widget.row(selected_layers[0])
            if position < self.sources_list_widget.count()-1:
                item = self.sources_list_widget.takeItem(position)
                self.sources_list_widget.insertItem(position+1,item)
                item.setSelected(1)

    # event handling for wizard page 2
    def loadMetadataSettings(self):
        self.settings.beginGroup("Metadata")
        self.title_edit.setText(self.settings.value('TITLE'))

        if self.settings.value('PLATFORM') == None:
            self.platform_combo_box.setCurrentIndex(0)
        else:
            self.platform_combo_box.setCurrentIndex(int(self.settings.value('PLATFORM')))

        self.sensor_edit.setText(self.settings.value('SENSOR'))
        self.sensor_edit.setCursorPosition(0)

        """
        self.sense_start_edit.setDateTime(QDateTime(self.settings.value('SENSE_START')))
        self.sense_end_edit.setDateTime(QDateTime(self.settings.value('SENSE_END')))

        """
        self.sense_start_edit.setDate(QDateTime.fromString(
            self.settings.value('SENSE_START'),
            Qt.ISODate).date())
        self.sense_start_edit.setTime(
            QDateTime.fromString(self.settings.value('SENSE_START'),
            Qt.ISODate).time())
        self.sense_end_edit.setDate(
            QDateTime.fromString(self.settings.value('SENSE_END'),
            Qt.ISODate).date())
        self.sense_end_edit.setTime(
            QDateTime.fromString(self.settings.value('SENSE_END'),
            Qt.ISODate).time())

        self.tags_edit.setText(self.settings.value('TAGS'))
        self.tags_edit.setCursorPosition(0)
        self.provider_edit.setText(self.settings.value('PROVIDER'))
        self.provider_edit.setCursorPosition(0)
        self.contact_edit.setText(self.settings.value('CONTACT'))
        self.contact_edit.setCursorPosition(0)
        self.website_edit.setText(self.settings.value('WEBSITE'))
        self.website_edit.setCursorPosition(0)

        """
        Boolean values are converted into string and lower case for
        'if' statement, since PyQt sometimes returns 'true', just like C++,
        instead of 'True', Python style.
        Maybe we can use integer values (0 or 1), instead of using string.
        """
        if str(self.settings.value('LICENSE')).lower() == 'true':
            self.license_check_box.setCheckState(2)
        if str(self.settings.value('REPROJECT')).lower() == 'true':
            self.reproject_check_box.setCheckState(2)

        self.settings.endGroup()

    def cleanMetadataSettings(self):
        self.title_edit.setText('')
        self.platform_combo_box.setCurrentIndex(0)
        self.sensor_edit.setText('')
        self.sense_start_edit.setDate(
            QDateTime().fromString('1970-01-01T00:00:00',
            Qt.ISODate).date())
        self.sense_start_edit.setTime(
            QDateTime().fromString('1970-01-01T00:00:00',
            Qt.ISODate).time())
        self.sense_end_edit.setDate(
            QDateTime().fromString('1970-01-01T00:00:00',Qt.ISODate).date())
        self.sense_end_edit.setTime(
            QDateTime().fromString('1970-01-01T00:00:00',Qt.ISODate).time())
        self.tags_edit.setText('')
        self.provider_edit.setText('')
        self.contact_edit.setText('')
        self.website_edit.setText('')
        self.license_check_box.setCheckState(0)
        self.reproject_check_box.setCheckState(0)

    def saveMetadata(self):
        selected_layers = self.added_sources_list_widget.selectedItems()
        if selected_layers:
            for item in selected_layers:
                filename = item.data(Qt.UserRole)
                self.loadImageryInfo(filename)
                json_string = json.dumps(self.metadata[filename],indent=4,separators=(',', ': '))
                if filename not in self.reprojected:
                    json_filename = os.path.splitext(filename)[0]+'.json'
                else:
                    # to avoid repetition of "EPSG3857" in filename:
                    if not "EPSG3857" in filename:
                        json_filename = os.path.splitext(filename)[0]+'_EPSG3857.json'
                json_file = open(json_filename, 'w')
                print >> json_file, json_string
                json_file.close()

            self.loadMetadataReviewBox()
            self.bar1.clearWidgets()
            self.bar1.pushMessage(
                'INFO',
                'Metadata for the selected sources were saved',
                level=QgsMessageBar.INFO)
        else:
            self.bar1.clearWidgets()
            self.bar1.pushMessage(
                'WARNING',
                'One or more source imagery should be selected to have the metadata saved',
                level=QgsMessageBar.WARNING)

    # event handling for wizard page 3
    # also see multi-thread for startUploader function
    def enableSpecify(self):
        if self.storage_combo_box.currentIndex() == 1:
            self.specify_label.setEnabled(1)
            self.specify_edit.setEnabled(1)
        else:
            self.specify_label.setEnabled(0)
            self.specify_edit.setText('')
            self.specify_edit.setEnabled(0)

    def loadStorageSettings(self):
        self.settings.beginGroup("Storage")
        bucket = self.settings.value('S3_BUCKET_NAME')
        storage_index = self.storage_combo_box.findText(bucket,Qt.MatchExactly)
        if not storage_index == -1:
            self.storage_combo_box.setCurrentIndex(storage_index)
        else:
            self.storage_combo_box.setCurrentIndex(self.storage_combo_box.findText(self.tr('other...')))
            self.specify_label.setEnabled(1)
            self.specify_edit.setEnabled(1)
            self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME'))
        self.key_id_edit.setText(self.settings.value('AWS_ACCESS_KEY_ID'))
        self.key_id_edit.setCursorPosition(0)
        self.secret_key_edit.setText(self.settings.value('AWS_SECRET_ACCESS_KEY'))
        self.secret_key_edit.setCursorPosition(0)
        self.settings.endGroup()

    def loadOptionsSettings(self):
        self.settings.beginGroup("Options")

        """
        Boolean values are converted into string and lower case for
        'if' statement, since PyQt sometimes returns 'true', just like C++,
        instead of 'True', Python style.
        Maybe we can use integer values (0 or 1), instead of using string.
        """
        if str(self.settings.value('NOTIFY_OAM')).lower() == 'true':
            self.notify_oam_check.setCheckState(2)
        if str(self.settings.value('TRIGGER_OAM_TS')).lower() == 'true':
            self.trigger_tiling_check.setCheckState(2)

        self.settings.endGroup()

    # other functions
    def validateFile(self,filename):
        # check that file exists
        if not os.path.exists(filename):
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                "CRITICAL",
                "The file %s does not exist" % filename,
                level=QgsMessageBar.CRITICAL)
            return False
        # check that file is an image
        if imghdr.what(filename) is None:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                "CRITICAL",
                "The file %s is not a supported data source" % filename,
                level=QgsMessageBar.CRITICAL)
            return False
        # check if gdal can read file
        try:
            raster = gdal.Open(filename,gdal.GA_ReadOnly)
        except:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                "CRITICAL",
                "GDAL could not read file %s" % filename,
                level=QgsMessageBar.CRITICAL)
            return False
        # check if there is an object raster
        if not raster:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                "CRITICAL",
                "GDAL could not read file %s" % filename,
                level=QgsMessageBar.CRITICAL)
            return False
        # check that image has at least 3 bands
        if raster.RasterCount < 3:
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                "CRITICAL",
                "The file %s has less than 3 raster bands" % filename,
                level=QgsMessageBar.CRITICAL)
            return False
        # check if projection is set
        if raster.GetProjection() is '':
            self.bar0.clearWidgets()
            self.bar0.pushMessage(
                "CRITICAL",
                "Could not extract projection from file %s" % filename,
                level=QgsMessageBar.CRITICAL)
            return False
        # finally, check if bbox has valid data
        xy_points = [(0.0,0.0),(0.0,raster.RasterYSize),(raster.RasterXSize,0.0),(raster.RasterXSize,raster.RasterYSize)]
        for point in xy_points:
            if point != self.GDALInfoReportCorner(raster,point[0],point[1]):
                QgsMessageLog.logMessage(
                    'File %s is a valid data source' % filename,
                    'OAM',
                    level=QgsMessageLog.INFO)
                return True

    def validateLayer(self,layer_name):
        all_layers = self.iface.mapCanvas().layers()
        for layer in all_layers:
            if layer_name == layer.name():
                if layer.type() == QgsMapLayer.VectorLayer:
                    self.bar0.clearWidgets()
                    self.bar0.pushMessage(
                        "CRITICAL",
                        "Vector layers cannot be selected for upload",
                        level=QgsMessageBar.CRITICAL)
                    return 0
                else:
                    return 1

    def extractMetadata(self,filename):
        """Extract filesize, projection, bbox and gsd from image file"""

        self.metadata[filename]['File size'] = os.stat(filename).st_size

        datafile = gdal.Open(filename,gdal.GA_ReadOnly)
        if datafile is None:
            self.bar1.clearWidgets()
            self.bar1.pushMessage(
                'CRITICAL',
                'Extraction of raster metadata failed',
                level=QgsMessageBar.CRITICAL)
            QgsMessageLog.logMessage(
                'Failed to extract metadata',
                'OAM',
                level=QgsMessageLog.CRITICAL)
        else:
            # extract projection
            projInfo = datafile.GetProjection()
            # WKT format
            spatialRef = osr.SpatialReference()
            spatialRef.ImportFromWkt(projInfo)
            # Proj4 format
            spatialRefProj = spatialRef.ExportToProj4()

            self.metadata[filename]['Projection'] = str(spatialRefProj)

            # original bbox
            upper_left = self.GDALInfoReportCorner(datafile,0.0,0.0 );
            lower_left = self.GDALInfoReportCorner(datafile,0.0,datafile.RasterYSize);
            upper_right = self.GDALInfoReportCorner(datafile,datafile.RasterXSize,0.0 );
            lower_right = self.GDALInfoReportCorner(datafile,datafile.RasterXSize,datafile.RasterYSize );
            center = self.GDALInfoReportCorner(datafile,datafile.RasterXSize/2.0,datafile.RasterYSize/2.0 );

            # get new bbox values if reprojection will happen
            try:
                if filename in self.reprojected:
                    self.metadata[filename]['Projection'] = "EPSG:3857"
                    target = osr.SpatialReference()
                    target.ImportFromEPSG(3857)
                    transform = osr.CoordinateTransformation(spatialRef,target)

                    point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (upper_left[0],upper_left[1]))
                    point.Transform(transform)
                    upper_left = json.loads(point.ExportToJson())['coordinates']

                    point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (lower_left[0],lower_left[1]))
                    point.Transform(transform)
                    lower_left = json.loads(point.ExportToJson())['coordinates']

                    point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (upper_right[0],upper_right[1]))
                    point.Transform(transform)
                    upper_right = json.loads(point.ExportToJson())['coordinates']

                    point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (lower_right[0],lower_right[1]))
                    point.Transform(transform)
                    lower_right = json.loads(point.ExportToJson())['coordinates']
            except (RuntimeError, TypeError, NameError) as error:
                print error
            except:
                print "Unexpected error:", sys.exc_info()[0]

            self.metadata[filename]['BBOX'] = (upper_left,lower_left,upper_right,lower_right)

    def GDALInfoReportCorner(self,hDataset,x,y):
        """GDALInfoReportCorner: extracted and adapted from the python port of gdalinfo"""

        # Transform the point into georeferenced coordinates
        adfGeoTransform = hDataset.GetGeoTransform(can_return_null = True)
        if adfGeoTransform is not None:
            dfGeoX = adfGeoTransform[0] + adfGeoTransform[1] * x + adfGeoTransform[2] * y
            dfGeoY = adfGeoTransform[3] + adfGeoTransform[4] * x + adfGeoTransform[5] * y
        else:
            self.bar1.clearWidgets()
            self.bar1.pushMessage(
                'WARNING',
                'BBOX might be wrong. Transformation coefficient could not be fetched from raster',
                level=QgsMessageBar.WARNING)
            return (x,y)

        # Report the georeferenced coordinates
        if abs(dfGeoX) < 181 and abs(dfGeoY) < 91:
            return literal_eval(("(%12.7f,%12.7f) " % (dfGeoX, dfGeoY )))
        else:
            return literal_eval(("(%12.3f,%12.3f) " % (dfGeoX, dfGeoY )))

    def loadImageryInfoForm(self, filename):
        pass

    def loadImageryInfo(self, filename):
        self.metadata[filename] = {}
        self.metadata[filename]['Title'] = self.title_edit.text()
        self.metadata[filename]['Platform'] = self.platform_combo_box.currentIndex()
        self.metadata[filename]['Sensor'] = self.sensor_edit.text()
        start = QDateTime()
        start.setDate(self.sense_start_edit.date())
        start.setTime(self.sense_start_edit.time())
        self.metadata[filename]['Sensor start'] = start.toString(Qt.ISODate)
        end = QDateTime()
        end.setDate(self.sense_end_edit.date())
        end.setTime(self.sense_end_edit.time())
        self.metadata[filename]['Sensor end'] = end.toString(Qt.ISODate)
        self.metadata[filename]['Tags'] = self.tags_edit.text()
        self.metadata[filename]['Provider'] = self.provider_edit.text()
        self.metadata[filename]['Contact'] = self.contact_edit.text()
        self.metadata[filename]['Website'] = self.website_edit.text()

        if self.reproject_check_box.isChecked():
            if filename not in self.reprojected:
                self.reprojected.append(filename)
        else:
            while filename in self.reprojected:
                self.reprojected.remove(filename)

        if self.license_check_box.isChecked():
            self.metadata[filename]['License'] = "Licensed under CC-BY 4.0 and allow tracing in OSM"
            if filename not in self.licensed:
                self.licensed.append(filename)
        else:
            while filename in self.licensed:
                self.licensed.remove(filename)

        self.extractMetadata(filename)

    def loadMetadataReviewBox(self):
        json_filenames = []
        for index in xrange(self.sources_list_widget.count()):
            filename = str(self.sources_list_widget.item(index).data(Qt.UserRole))
            if filename not in self.reprojected:
                f = os.path.splitext(filename)[0]+'.json'
            else:
                f = os.path.splitext(filename)[0]+'_EPSG3857.json'
            json_filenames.append(f)

        temp = QTemporaryFile()
        temp.open()
        for f in json_filenames:
            if os.path.exists(f):
                with open(f) as infile:
                    temp.write(infile.read())
        temp.flush()
        temp.seek(0)

        stream = QTextStream(temp)
        self.review_metadata_box.setText(stream.readAll())

    #functions for threading purpose
    def startConnection(self):
        if self.storage_combo_box.currentIndex() == 0:
            bucket_name = 'oam-qgis-plugin-test'
        else:
            bucket_name = str(self.specify_edit.text())
            if not bucket_name:
                self.bar2.clearWidgets()
                self.bar2.pushMessage(
                    'WARNING',
                    'The bucket for upload must be provided',
                    level=QgsMessageBar.WARNING)

        bucket_key = str(self.key_id_edit.text())
        bucket_secret = str(self.secret_key_edit.text())

        self.bucket = None
        for trial in xrange(3):
            if self.bucket: break
            try:
                connection = S3Connection(bucket_key,bucket_secret)
                self.bucket = connection.get_bucket(bucket_name)
                QgsMessageLog.logMessage(
                    'Connection established' % trial,
                    'OAM',
                    level=QgsMessageLog.INFO)
            except:
                if trial == 2:
                   QgsMessageLog.logMessage(
                    'Failed to connect after 3 attempts',
                    'OAM',
                    level=QgsMessageLog.CRITICAL)
        return self.bucket

    def reproject(self,filename):
        # to avoid repetition of "EPSG3857" in filename:
        if not "EPSG3857" in filename:
            reproject_filename = os.path.splitext(filename)[0]+'_EPSG3857.tif'
        os.system("gdalwarp -of GTiff -t_srs epsg:3857 %s %s" % (filename,reproject_filename))
        QgsMessageLog.logMessage(
            'Reprojected to EPSG:3857',
            'OAM',
            level=QgsMessageLog.INFO)
        return reproject_filename

    def convert(self,filename):
        tif_filename = os.path.splitext(filename)[0]+".tif"
        #Open existing dataset
        src_ds = gdal.Open(filename)
        driver = gdal.GetDriverByName("GTiff")
        dst_ds = driver.CreateCopy(tif_filename, src_ds, 0 )
        #Properly close the datasets to flush to disk
        dst_ds = None
        src_ds = None

    def startUploader(self):
        # initialize options
        self.upload_options = []
        if self.notify_oam_check.isChecked():
            self.upload_options.append("notify_oam")
        if self.trigger_tiling_check.isChecked():
            self.upload_options.append("trigger_tiling")

        if self.startConnection():
            for index in xrange(self.sources_list_widget.count()):
                filename = str(self.sources_list_widget.item(index).data(Qt.UserRole))

                self.bar2.clearWidgets()
                self.bar2.pushMessage(
                    'INFO',
                    'Pre-upload image processing...',
                    level=QgsMessageBar.INFO)

                # Perfom reprojection
                if filename in self.reprojected:
                    filename = self.reproject(filename)
                    QgsMessageLog.logMessage(
                        'Created reprojected file: %s' % filename,
                        'OAM',
                        level=QgsMessageLog.INFO)

                # Convert file format
                if not (imghdr.what(filename) == 'tiff'):
                    filename = self.convert(filename)
                    QgsMessageLog.logMessage(
                        'Converted file to tiff: %s' % filename,
                        'OAM',
                        level=QgsMessageLog.INFO)

                # create a new uploader instance
                uploader = Uploader(filename,self.bucket,self.upload_options)
                QgsMessageLog.logMessage(
                    'Uploader started\n',
                    'OAM',
                    level=QgsMessageLog.INFO)
                # configure the QgsMessageBar
                messageBar = self.bar2.createMessage('INFO: Performing upload...', )
                progressBar = QProgressBar()
                progressBar.setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
                messageBar.layout().addWidget(progressBar)
                cancelButton = QPushButton()
                cancelButton.setText('Cancel')
                cancelButton.clicked.connect(self.cancelUpload)
                messageBar.layout().addWidget(cancelButton)
                self.bar2.clearWidgets()
                self.bar2.pushWidget(messageBar, level=QgsMessageBar.INFO)
                self.messageBar = messageBar

                # start the worker in a new thread
                thread = QThread(self)
                uploader.moveToThread(thread)
                uploader.finished.connect(self.uploaderFinished)
                uploader.error.connect(self.uploaderError)
                uploader.progress.connect(progressBar.setValue)
                thread.started.connect(uploader.run)
                thread.start()
                self.thread = thread
                self.uploader = uploader
        else:
                QgsMessageLog.logMessage(
                    'No connection to the server\n',
                    'OAM',
                    level=QgsMessageLog.CRITICAL)

    def cancelUpload(self):
        self.uploader.kill()
        self.bar2.clearWidgets()
        self.bar2.pushMessage(
            'WARNING',
            'Canceling upload...',
            level=QgsMessageBar.WARNING)

    def uploaderFinished(self, success):
        # clean up the uploader and thread
        try:
            self.uploader.deleteLater()
        except:
            QgsMessageLog.logMessage(
                'Exception on deleting uploader\n',
                'OAM',
                level=QgsMessageLog.CRITICAL)
        self.thread.quit()
        self.thread.wait()
        try:
            self.thread.deleteLater()
        except:
            QgsMessageLog.logMessage(
                'Exception on deleting thread\n',
                'OAM',
                level=QgsMessageLog.CRITICAL)
        # remove widget from message bar
        self.bar2.popWidget(self.messageBar)
        if success:
            # report the result
            self.bar2.clearWidgets()
            self.bar2.pushMessage(
                'INFO',
                'Upload completed with success',
                level=QgsMessageBar.INFO)
            QgsMessageLog.logMessage(
                'Upload succeeded',
                'OAM',
                level=QgsMessageLog.INFO)
        else:
            # notify the user that something went wrong
            self.bar2.pushMessage(
                'CRITICAL',
                'Upload was interrupted',
                level=QgsMessageBar.CRITICAL)
            QgsMessageLog.logMessage(
                'Upload was interrupted',
                'OAM',
                level=QgsMessageLog.CRITICAL)

    def uploaderError(self, e, exception_string):
        QgsMessageLog.logMessage(
            'Uploader thread raised an exception:\n'.format(exception_string),
            'OAM',
            level=QgsMessageLog.CRITICAL)
Exemple #30
0
class ImgSearchDialog(QtGui.QDialog, FORM_CLASS):

    def __init__(self, iface, settings, parent=None):
        """Constructor."""
        super(ImgSearchDialog, self).__init__(parent)
        # Set up the user interface from Designer.
        # After 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.iface = iface
        self.setupUi(self)

        self.settings = settings

        self.setWindowFlags(Qt.WindowCloseButtonHint |
                            Qt.WindowMinimizeButtonHint)

        # initialize GUI
        self.initGui()

        # self.buttonBox.button(QDialogButtonBox.Ok).setText('Save')
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.verticalLayoutListWidget.layout().addWidget(self.bar)

        # event handling
        self.pushButtonSearch.clicked.connect(self.startSearch)
        # self.returnPressed.connect(self.startSearch)
        # self.pushButtonSearchLatest.clicked.connect(self.searchLatest)
        # self.pushButtonBrowseLocation.clicked.connect(self.browseLocation)
        self.connect(self.listWidget, QtCore.SIGNAL(
            "itemClicked(QListWidgetItem *)"),
            self.browseThumbnailAndMeta)

        # self.buttonBox.clicked.connect(lambda: self.test(self.buttonBox))
        self.connect(self.buttonBox,
                     QtCore.SIGNAL('accepted()'),
                     self.execOk)
        self.connect(self.buttonBox,
                     QtCore.SIGNAL('rejected()'),
                     self.execCancel)

        # disable some GUIs
        # self.pushButtonBrowseLocation.hide()
        # self.pushButtonSearchLatest.hide()

        # add objects for catalog access
        self.settings.beginGroup("Storage")

        if self.settings.value('CATALOG_URL') is None or \
            str(self.settings.value('CATALOG_URL')) == '':
            # self.catalogUrl = "https://oam-catalog.herokuapp.com"
            self.catalogUrl = "http://api.openaerialmap.org"
        else:
            self.catalogUrl = str(self.settings.value('CATALOG_URL'))

        self.settings.endGroup()

        self.oamCatalogAccess = OAMCatalogAccess(self.catalogUrl)
        catalogUrlLabel = self.catalog_url_label.text() + self.catalogUrl
        self.catalog_url_label.setText(catalogUrlLabel)

        self.imgBrowser = None


    def closeEvent(self, evnt):
        pass

    def keyPressEvent(self, e):
        if e.key() == QtCore.Qt.Key_Return:
            self.startSearch()

    def test(self, *argv):
        print(str(argv))

    def createQueriesSettings(self):
        self.settings.setValue('CHECKBOX_LOCATION', True)
        self.settings.setValue('CHECKBOX_ACQUISITION_FROM', True)
        self.settings.setValue('CHECKBOX_ACQUISITION_TO', True)
        self.settings.setValue('CHECKBOX_GSD_FROM', True)
        self.settings.setValue('CHECKBOX_GSD_TO', True)

        self.settings.setValue('LOCATION', '')
        self.settings.setValue('ACQUISITION_FROM',
            QDate.currentDate().addMonths(-12).toString(Qt.ISODate))
        self.settings.setValue('ACQUISITION_TO',
            QDate.currentDate().toString(Qt.ISODate))
        self.settings.setValue('GSD_FROM', '')
        self.settings.setValue('GSD_TO', '')
        self.settings.setValue('LIMIT', 20)
        self.settings.setValue('ORDER_BY', 0)
        self.settings.setValue('SORT', 'desc')

    def loadQueriesSettings(self):
        self.settings.beginGroup("ImageSearch")

        if str(self.settings.value('CHECKBOX_LOCATION')).lower() == 'true':
            self.checkBoxLocation.setCheckState(2)
        else:
            self.checkBoxLocation.setCheckState(0)
        if str(self.settings.value('CHECKBOX_ACQUISITION_FROM')).lower() == 'true':
            self.checkBoxAcquisitionFrom.setCheckState(2)
        else:
            self.checkBoxAcquisitionFrom.setCheckState(0)
        if str(self.settings.value('CHECKBOX_ACQUISITION_TO')).lower() == 'true':
            self.checkBoxAcquisitionTo.setCheckState(2)
        else:
            self.checkBoxAcquisitionTo.setCheckState(0)
        if str(self.settings.value('CHECKBOX_GSD_FROM')).lower() == 'true':
            self.checkBoxResolutionFrom.setCheckState(2)
        else:
            self.checkBoxResolutionFrom.setCheckState(0)
        if str(self.settings.value('CHECKBOX_GSD_TO')).lower() == 'true':
            self.checkBoxResolutionTo.setCheckState(2)
        else:
            self.checkBoxResolutionTo.setCheckState(0)

        self.lineEditLocation.setText(
            self.settings.value('LOCATION'))
        self.dateEditAcquisitionFrom.setDate(QDate.fromString(
            self.settings.value('ACQUISITION_FROM'), Qt.ISODate))
        self.dateEditAcquisitionTo.setDate(QDate.fromString(
            self.settings.value('ACQUISITION_TO'), Qt.ISODate))
        self.lineEditResolutionFrom.setText(
            str(self.settings.value('GSD_FROM')))
        self.lineEditResolutionTo.setText(
            str(self.settings.value('GSD_TO')))
        self.lineEditNumImages.setText(
            str(self.settings.value('LIMIT')))
        self.comboBoxOrderBy.setCurrentIndex(
            int(self.settings.value('ORDER_BY')))
        if self.settings.value('SORT') == 'desc':
            self.radioButtonDesc.setChecked(True)
        else:
            self.radioButtonAsc.setChecked(True)

        self.settings.endGroup()

    def saveQueriesSettings(self):
        self.settings.beginGroup("ImageSearch")

        self.settings.setValue('CHECKBOX_LOCATION',
            self.checkBoxLocation.isChecked())
        self.settings.setValue('CHECKBOX_ACQUISITION_FROM',
            self.checkBoxAcquisitionFrom.isChecked())
        self.settings.setValue('CHECKBOX_ACQUISITION_TO',
            self.checkBoxAcquisitionTo.isChecked())
        self.settings.setValue('CHECKBOX_GSD_FROM',
            self.checkBoxResolutionFrom.isChecked())
        self.settings.setValue('CHECKBOX_GSD_TO',
            self.checkBoxResolutionTo.isChecked())

        self.settings.setValue('LOCATION',
            self.lineEditLocation.text())
        self.settings.setValue('ACQUISITION_FROM',
            self.dateEditAcquisitionFrom.date().toString(Qt.ISODate))
        self.settings.setValue('ACQUISITION_TO',
            self.dateEditAcquisitionTo.date().toString(Qt.ISODate))
        if (self.lineEditResolutionFrom.text() != ''
            and self.lineEditResolutionFrom.text() is not None):
            self.settings.setValue('GSD_FROM',
                float(self.lineEditResolutionFrom.text()))
        if (self.lineEditResolutionTo.text() != ''
            and self.lineEditResolutionTo.text() is not None):
            self.settings.setValue('GSD_TO',
                float(self.lineEditResolutionTo.text()))
        if (self.lineEditNumImages.text() != ''
            and self.lineEditNumImages.text() is not None):
            self.settings.setValue('LIMIT',
                int(self.lineEditNumImages.text()))
        self.settings.setValue('ORDER_BY',
            self.comboBoxOrderBy.currentIndex())
        if self.radioButtonDesc.isChecked():
            self.settings.setValue('SORT', 'desc')
        else:
            self.settings.setValue('SORT', 'asc')

        self.settings.endGroup()

    def initGui(self):

        item = QListWidgetItem()
        item.setText("Please set the conditions and press 'Search' button.")
        item.setData(Qt.UserRole, "Sample Data")
        self.listWidget.addItem(item)

        # load default queries
        self.settings.beginGroup("ImageSearch")
        if self.settings.value('CHECKBOX_LOCATION') is None:
            print('create new queries settings')
            self.createQueriesSettings()
        self.settings.endGroup()

        self.loadQueriesSettings()

    def refreshListWidget(self, metadataInList):

        self.listWidget.clear()

        for singleMetaInDict in metadataInList:
            item = QListWidgetItem()
            item.setText(singleMetaInDict[u'title'])
            item.setData(Qt.UserRole, singleMetaInDict)
            self.listWidget.addItem(item)

    def startSearch(self):
        action = "meta"
        dictQueries = {}

        try:
            if self.checkBoxLocation.isChecked():
                location = self.lineEditLocation.text()
                strBboxForOAM = nominatim_search(location)
                print(strBboxForOAM)
                if strBboxForOAM != 'failed':
                    dictQueries['bbox'] = strBboxForOAM
                else:
                    qMsgBox = QMessageBox()
                    qMsgBox.setWindowTitle('Message')
                    qMsgBox.setText("The 'location' won't be used as a " +
                                    "query, because Geocoder could " +
                                    "not find the location.")
                    qMsgBox.exec_()

            if self.checkBoxAcquisitionFrom.isChecked():
                dictQueries['acquisition_from'] = \
                    self.dateEditAcquisitionFrom.date().toString(Qt.ISODate)

            if self.checkBoxAcquisitionTo.isChecked():
                dictQueries['acquisition_to'] = \
                    self.dateEditAcquisitionTo.date().toString(Qt.ISODate)

            if self.checkBoxResolutionFrom.isChecked():
                if (self.lineEditResolutionFrom.text() != '' and
                        self.lineEditResolutionFrom.text() is not None):
                    dictQueries['gsd_from'] = \
                        float(self.lineEditResolutionFrom.text())

            if self.checkBoxResolutionTo.isChecked():
                if (self.lineEditResolutionTo.text() != '' and
                        self.lineEditResolutionTo.text() is not None):
                    dictQueries['gsd_to'] = \
                        float(self.lineEditResolutionTo.text())

            if (self.lineEditNumImages.text() != '' and
                    self.lineEditNumImages.text() is not None):
                dictQueries['limit'] = int(self.lineEditNumImages.text())

            if self.comboBoxOrderBy.currentText() == 'Acquisition Date':
                dictQueries['order_by'] = "acquisition_end"
            elif self.comboBoxOrderBy.currentText() == 'GSD':
                dictQueries['order_by'] = "gsd"

            print(self.comboBoxOrderBy.currentText())


            if self.radioButtonAsc.isChecked():
                dictQueries['sort'] = "asc"
            elif self.radioButtonDesc.isChecked():
                dictQueries['sort'] = "desc"

            self.oamCatalogAccess.setAction(action)
            self.oamCatalogAccess.setDictQueries(dictQueries)
            metadataInList = self.oamCatalogAccess.getMetadataInList()

            self.refreshListWidget(metadataInList)

        except Exception as e:
            print(repr(e))
            qMsgBox = QMessageBox()
            qMsgBox.setWindowTitle('Message')
            qMsgBox.setText("Please make sure if you entered valid data" +
                            "/internet connection, and try again.")
            qMsgBox.exec_()

    """ This function is not in use. """
    """
    def searchLatest(self):
        action = "meta"
        dictQueries = {}

        try:
            dictQueries['sort'] = "desc"
            dictQueries['order_by'] = "acquisition_end"
            if (self.lineEditResolutionFrom.text() != '' and
                    self.lineEditResolutionFrom.text() is not None):
                dictQueries['gsd_from'] = float(
                    self.lineEditResolutionFrom.text())
            if (self.lineEditResolutionTo.text() != '' and
                    self.lineEditResolutionTo.text() is not None):
                dictQueries['gsd_to'] = float(
                    self.lineEditResolutionTo.text())
            if (self.lineEditNumImages.text() != '' and
                    self.lineEditNumImages.text() is not None):
                dictQueries['limit'] = int(self.lineEditNumImages.text())

            self.oamCatalogAccess.setAction(action)
            self.oamCatalogAccess.setDictQueries(dictQueries)
            metadataInList = self.oamCatalogAccess.getMetadataInList()

            self.refreshListWidget(metadataInList)

        except Exception as e:
            qMsgBox = QMessageBox()
            qMsgBox.setWindowTitle('Message')
            qMsgBox.setText("Please make sure if you entered " +
                            "valid data/internet connection, and try again.")
            qMsgBox.exec_()
    """

    """ This function is not in use. """
    """
    def browseLocation(self):
        print("Browse location of loaded layer...")
    """

    def browseThumbnailAndMeta(self, item):

        singleMetaInDict = item.data(Qt.UserRole)
        # print(str(singleMetaInDict))

        if type(singleMetaInDict) is dict:

            if self.imgBrowser is None:
                self.imgBrowser = ImgBrowser(self.iface)
                self.imgBrowser.thumbnailManager.statusChanged.connect(
                    self.changeThumbnailStatus)
                self.imgBrowser.thumbnailManager.error.connect(
                    self.displayThumnailDownloadError)

                pos = self.pos()
                print(pos.x())
                print(pos.y())
                pos.setX(pos.x() + 400)
                pos.setY(pos.y() + 20)
                self.imgBrowser.move(pos)

            self.imgBrowser.setSingleMetaInDic(singleMetaInDict)
            self.imgBrowser.displayMetadata()
            self.imgBrowser.displayThumbnail()

            if not self.imgBrowser.isVisible():
                self.imgBrowser.show()

            self.imgBrowser.activateWindow()

    def changeThumbnailStatus(self, status):
        # print(str(status))
        if status == 0:
            pass
            """
            self.bar.clearWidgets()
            self.bar.pushMessage(
                'INFO',
                'Downloading thumbnail...',
                level=QgsMessageBar.INFO,
                duration=8)
            """
        if status == 1:
            self.bar.clearWidgets()

    def displayThumnailDownloadError(self, e):
        self.bar.clearWidgets()
        self.bar.pushMessage(
            'Failed to download thumbnail.\n{}'.format(str(e)),
            level=QgsMessageBar.WARNING,
            duration=8)

    def execOk(self):
        # save self.defaultQueriesInDict into self.settings
        self.saveQueriesSettings()
        self.close()

    def execCancel(self):
        self.close()
Exemple #31
0
class AlgorithmDialog(AlgorithmDialogBase):

    def __init__(self, alg):
        AlgorithmDialogBase.__init__(self, alg)

        self.alg = alg

        self.setMainWidget(alg.getParametersPanel(self))

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().insertWidget(0, self.bar)

        self.cornerWidget = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 5)
        self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }")
        self.runAsBatchButton = QPushButton(self.tr("Run as batch process..."))
        self.runAsBatchButton.clicked.connect(self.runAsBatch)
        layout.addWidget(self.runAsBatchButton)
        self.cornerWidget.setLayout(layout)
        self.tabWidget.setCornerWidget(self.cornerWidget)

    def runAsBatch(self):
        self.close()
        dlg = BatchAlgorithmDialog(self.alg)
        dlg.show()
        dlg.exec_()

    def setParamValues(self):
        params = self.alg.parameters
        outputs = self.alg.outputs

        for param in params:
            if param.hidden:
                continue
            wrapper = self.mainWidget.wrappers[param.name]
            if not self.setParamValue(param, wrapper):
                raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget)

        for output in outputs:
            if output.hidden:
                continue
            output.value = self.mainWidget.outputWidgets[output.name].getValue()
            if isinstance(output, (OutputRaster, OutputVector, OutputTable)):
                output.open = self.mainWidget.checkBoxes[output.name].isChecked()

        return True

    def setParamValue(self, param, wrapper):
        if wrapper.widget:
            return param.setValue(wrapper.value())
        else:
            return True

    def checkExtentCRS(self):
        unmatchingCRS = False
        hasExtent = False
        projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
        layers = dataobjects.getAllLayers()
        for param in self.alg.parameters:
            if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)):
                if param.value:
                    if isinstance(param, ParameterMultipleInput):
                        inputlayers = param.value.split(';')
                    else:
                        inputlayers = [param.value]
                    for inputlayer in inputlayers:
                        for layer in layers:
                            if layer.source() == inputlayer:
                                if layer.crs() != projectCRS:
                                    unmatchingCRS = True

                        p = dataobjects.getObjectFromUri(inputlayer)
                        if p is not None:
                            if p.crs() != projectCRS:
                                unmatchingCRS = True
            if isinstance(param, ParameterExtent):
                value = self.mainWidget.wrappers[param.name].widget.leText.text().strip()
                if value:
                    hasExtent = True

        return hasExtent and unmatchingCRS

    def accept(self):
        self.settings.setValue("/Processing/dialogBase", self.saveGeometry())

        checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS)
        try:
            self.setParamValues()
            if checkCRS and not self.alg.checkInputCRS():
                reply = QMessageBox.question(self, self.tr("Unmatching CRS's"),
                                             self.tr('Layers do not all use the same CRS. This can '
                                                     'cause unexpected results.\nDo you want to '
                                                     'continue?'),
                                             QMessageBox.Yes | QMessageBox.No,
                                             QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            checkExtentCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS)
            if checkExtentCRS and self.checkExtentCRS():
                reply = QMessageBox.question(self, self.tr("Extent CRS"),
                                             self.tr('Extent parameters must use the same CRS as the input layers.\n'
                                                     'Your input layers do not have the same extent as the project, '
                                                     'so the extent might be in a wrong CRS if you have selected it from the canvas.\n'
                                                     'Do you want to continue?'),
                                             QMessageBox.Yes | QMessageBox.No,
                                             QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            msg = self.alg._checkParameterValuesBeforeExecuting()
            if msg:
                QMessageBox.warning(
                    self, self.tr('Unable to execute algorithm'), msg)
                return
            self.btnRun.setEnabled(False)
            self.btnClose.setEnabled(False)
            buttons = self.mainWidget.iterateButtons
            self.iterateParam = None

            for i in range(len(list(buttons.values()))):
                button = list(buttons.values())[i]
                if button.isChecked():
                    self.iterateParam = list(buttons.keys())[i]
                    break

            self.progressBar.setMaximum(0)
            self.lblProgress.setText(self.tr('Processing algorithm...'))
            # Make sure the Log tab is visible before executing the algorithm
            try:
                self.tabWidget.setCurrentIndex(1)
                self.repaint()
            except:
                pass

            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

            self.setInfo(
                self.tr('<b>Algorithm %s starting...</b>') % self.alg.name)

            if self.iterateParam:
                if runalgIterating(self.alg, self.iterateParam, self.feedback):
                    self.finish()
                else:
                    QApplication.restoreOverrideCursor()
                    self.resetGUI()
            else:
                command = self.alg.getAsCommand()
                if command:
                    ProcessingLog.addToLog(
                        ProcessingLog.LOG_ALGORITHM, command)
                if runalg(self.alg, self.feedback):
                    self.finish()
                else:
                    QApplication.restoreOverrideCursor()
                    self.resetGUI()
        except AlgorithmDialogBase.InvalidParameterValue as e:
            try:
                self.buttonBox.accepted.connect(lambda e=e:
                                                e.widget.setPalette(QPalette()))
                palette = e.widget.palette()
                palette.setColor(QPalette.Base, QColor(255, 255, 0))
                e.widget.setPalette(palette)
            except:
                pass
            self.bar.clearWidgets()
            self.bar.pushMessage("", "Wrong or missing parameter value: %s" % e.parameter.description,
                                 level=QgsMessageBar.WARNING, duration=5)

    def finish(self):
        keepOpen = ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN)

        if self.iterateParam is None:
            if not handleAlgorithmResults(self.alg, self.feedback, not keepOpen):
                self.resetGUI()
                return

        self.executed = True
        self.setInfo('Algorithm %s finished' % self.alg.name)
        QApplication.restoreOverrideCursor()

        if not keepOpen:
            self.close()
        else:
            self.resetGUI()
            if self.alg.getHTMLOutputsCount() > 0:
                self.setInfo(
                    self.tr('HTML output has been generated by this algorithm.'
                            '\nOpen the results dialog to check it.'))

    def closeEvent(self, evt):
        QgsProject.instance().layerWasAdded.disconnect(self.mainWidget.layerRegistryChanged)
        QgsProject.instance().layersWillBeRemoved.disconnect(self.mainWidget.layerRegistryChanged)
        super(AlgorithmDialog, self).closeEvent(evt)
class OfficialDataSettingsDialog(QDialog, DIALOG_UI):
    official_db_connection_changed = pyqtSignal(DBConnector,
                                                bool)  # dbconn, ladm_col_db

    def __init__(self, qgis_utils=None, conn_manager=None, parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.log = QgsApplication.messageLog()
        self.conn_manager = conn_manager
        self._db = None
        self.qgis_utils = qgis_utils
        self.db_source = OFFICIAL_DB_SOURCE
        self.conf_db = ConfigDbSupported()

        # Set connections
        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.helpRequested.connect(self.show_help)
        self.finished.connect(self.finished_slot)
        self.btn_test_connection.clicked.connect(self.test_connection)
        self.btn_test_ladm_col_structure.clicked.connect(
            self.test_ladm_col_structure)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.cbo_db_source.clear()

        self._lst_db = self.conf_db.get_db_items()
        self._lst_panel = dict()

        for key, value in self._lst_db.items():
            self.cbo_db_source.addItem(value.get_name(), key)
            self._lst_panel[key] = value.get_config_panel(self)
            self._lst_panel[key].notify_message_requested.connect(
                self.show_message)
            self.db_layout.addWidget(self._lst_panel[key])

        self.db_source_changed()

        # Trigger some default behaviours
        self.restore_settings()

        self.cbo_db_source.currentIndexChanged.connect(self.db_source_changed)
        self.rejected.connect(self.close_dialog)

    def close_dialog(self):
        self.close()

    def showEvent(self, event):
        # It is necessary to reload the variables
        # to load the database and schema name
        self.restore_settings()

    def _get_db_connector_from_gui(self):
        current_db = self.cbo_db_source.currentData()
        params = self._lst_panel[current_db].read_connection_parameters()
        db = self._lst_db[current_db].get_db_connector(params)
        db.open_connection()  # Open connection using gui parameters
        return db

    def get_db_connection(self):
        if self._db is not None:
            self.log.logMessage("Returning existing db connection...",
                                PLUGIN_NAME, Qgis.Info)
        else:
            self.log.logMessage("Getting new db connection...", PLUGIN_NAME,
                                Qgis.Info)
            self._db = self._get_db_connector_from_gui()
            self._db.open_connection()

        return self._db

    def accepted(self):
        current_db = self.cbo_db_source.currentData()
        if self._lst_panel[current_db].state_changed():
            valid_connection = True
            ladm_col_schema = False

            db = self._get_db_connector_from_gui()
            res, msg = db.test_connection(EnumTestLevel.DB_SCHEMA)

            if res:
                ladm_col_schema, msg = db.test_connection(EnumTestLevel.LADM)
            else:
                self.show_message(msg, Qgis.Warning)
                valid_connection = False

            if not ladm_col_schema:
                self.show_message(msg, Qgis.Warning)
                return  # Do not close the dialog

            if valid_connection:
                if self._db is not None:
                    self._db.close_connection()

                self._db = db

                # Update db connect with new db conn
                self.conn_manager.set_db_connector_for_source(
                    self._db, self.db_source)

                # Emmit signal when change db source
                self.official_db_connection_changed.emit(
                    self._db, ladm_col_schema)

                self.save_settings()
                QDialog.accept(self)  # TODO remove?
            else:
                return  # Do not close the dialog

        else:
            QDialog.accept(self)  # TODO remove?

    def finished_slot(self, result):
        self.bar.clearWidgets()

    def set_db_connection(self, mode, dict_conn):
        """
        To be used by external scripts and unit tests
        """
        self.cbo_db_source.setCurrentIndex(self.cbo_db_source.findData(mode))
        self.db_source_changed()

        current_db = self.cbo_db_source.currentData()

        self._lst_panel[current_db].write_connection_parameters(dict_conn)

        self.accepted()  # Create/update the db object

    def save_settings(self):
        settings = QSettings()
        current_db = self.cbo_db_source.currentData()
        settings.setValue(
            'Asistente-LADM_COL/db/{db_source}/db_connection_source'.format(
                db_source=self.db_source), current_db)
        dict_conn = self._lst_panel[current_db].read_connection_parameters()

        self._lst_db[current_db].save_parameters_conn(dict_conn=dict_conn,
                                                      db_source=self.db_source)

    def restore_settings(self):
        # Restore QSettings
        settings = QSettings()
        default_db = self.conf_db.id_default_db

        index_db = self.cbo_db_source.findData(
            settings.value(
                'Asistente-LADM_COL/db/{db_source}/db_connection_source'.
                format(db_source=self.db_source), default_db))

        if index_db == -1:
            index_db = self.cbo_db_source.findData(default_db)

        self.cbo_db_source.setCurrentIndex(index_db)
        self.db_source_changed()

        # restore db settings for all panels
        for id_db, db_factory in self._lst_db.items():
            dict_conn = db_factory.get_parameters_conn(self.db_source)
            self._lst_panel[id_db].write_connection_parameters(dict_conn)
            self._lst_panel[id_db].save_state()

    def db_source_changed(self):
        if self._db is not None:
            self._db.close_connection()

        self._db = None  # Reset db connection

        for key, value in self._lst_panel.items():
            value.setVisible(False)

        current_db = self.cbo_db_source.currentData()

        self._lst_panel[current_db].setVisible(True)

    def test_connection(self):
        db = self._get_db_connector_from_gui()
        res, msg = db.test_connection(test_level=EnumTestLevel.DB_SCHEMA)

        if db is not None:
            db.close_connection()

        self.show_message(msg, Qgis.Info if res else Qgis.Warning)
        self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info)

    def test_ladm_col_structure(self):
        db = self._get_db_connector_from_gui()
        res, msg = db.test_connection(test_level=EnumTestLevel.LADM)

        if db is not None:
            db.close_connection()

        self.show_message(msg, Qgis.Info if res else Qgis.Warning)
        self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info)

    def show_message(self, message, level):
        self.bar.pushMessage(message, level, 10)

    def show_help(self):
        self.qgis_utils.show_help("settings")
class CreateGroupPartyOperation(QDialog, DIALOG_UI):
    WIZARD_NAME = "CreateGroupPartyOperationWizard"
    WIZARD_TOOL_NAME = QCoreApplication.translate(WIZARD_NAME,
                                                  "Create group party")

    def __init__(self, iface, db, qgis_utils, parent=None):
        QDialog.__init__(self)
        self.setupUi(self)
        self.iface = iface
        self._db = db
        self.qgis_utils = qgis_utils
        self.logger = Logger()
        self.names = self._db.names
        self.help_strings = HelpStrings()

        self.data = {}  # {t_id: [display_text, denominator, numerator]}
        self.current_selected_parties = []  #  [t_ids]
        self.parties_to_group = {}  # {t_id: [denominator, numerator]}

        self._layers = {
            self.names.OP_GROUP_PARTY_T: {
                'name': self.names.OP_GROUP_PARTY_T,
                'geometry': None,
                LAYER: None
            },
            self.names.OP_PARTY_T: {
                'name': self.names.OP_PARTY_T,
                'geometry': None,
                LAYER: None
            },
            self.names.MEMBERS_T: {
                'name': self.names.MEMBERS_T,
                'geometry': None,
                LAYER: None
            },
            self.names.FRACTION_S: {
                'name': self.names.FRACTION_S,
                'geometry': None,
                LAYER: None
            },
            self.names.COL_GROUP_PARTY_TYPE_D: {
                'name': self.names.COL_GROUP_PARTY_TYPE_D,
                'geometry': None,
                LAYER: None
            }
        }

        # Fill combo of types
        col_group_party_type_table = self.qgis_utils.get_layer(
            self._db, self.names.COL_GROUP_PARTY_TYPE_D, None, True)
        if not col_group_party_type_table:
            return

        for feature in col_group_party_type_table.getFeatures():
            self.cbo_group_type.addItem(feature[self.names.DISPLAY_NAME_F],
                                        feature[self.names.T_ID_F])

        self.txt_search_party.setText("")
        self.btn_select.setEnabled(False)
        self.btn_deselect.setEnabled(False)

        self.tbl_selected_parties.setColumnCount(3)
        self.tbl_selected_parties.setColumnWidth(0, 140)
        self.tbl_selected_parties.setColumnWidth(1, 90)
        self.tbl_selected_parties.setColumnWidth(2, 90)
        self.tbl_selected_parties.sortItems(0, Qt.AscendingOrder)

        self.txt_search_party.textEdited.connect(self.search)
        self.lst_all_parties.itemSelectionChanged.connect(
            self.selection_changed_all)
        self.tbl_selected_parties.itemSelectionChanged.connect(
            self.selection_changed_selected)
        self.tbl_selected_parties.cellChanged.connect(self.valueEdited)
        self.btn_select_all.clicked.connect(self.select_all)
        self.btn_deselect_all.clicked.connect(self.deselect_all)
        self.btn_select.clicked.connect(self.select)
        self.btn_deselect.clicked.connect(self.deselect)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)
        self.rejected.connect(self.close_wizard)

    def closeEvent(self, e):
        # It's necessary to prevent message bar alert
        pass

    def required_layers_are_available(self):
        layers_are_available = self.qgis_utils.required_layers_are_available(
            self._db, self._layers, self.WIZARD_TOOL_NAME)
        return layers_are_available

    def load_parties_data(self):
        expression = QgsExpression(
            LayerConfig.get_dict_display_expressions(
                self.names)[self.names.OP_PARTY_T])
        context = QgsExpressionContext()
        data = dict()
        for feature in self._layers[
                self.names.OP_PARTY_T][LAYER].getFeatures():
            context.setFeature(feature)
            expression.prepare(context)
            value = expression.evaluate(context)
            data[feature[self.names.T_ID_F]] = [
                value if value != NULL else None, 0, 0
            ]
        self.set_parties_data(data)

    def set_parties_data(self, parties_data):
        """
        Initialize parties data.

        :param parties_data: Dictionary {t_id: [display_text, denominator, numerator]}
        :type parties_data: dict
        """
        self.data = parties_data
        self.update_lists()

    def search(self, text):
        self.update_lists(True)

    def selection_changed_all(self):
        self.btn_select.setEnabled(len(self.lst_all_parties.selectedItems()))

    def selection_changed_selected(self):
        self.btn_deselect.setEnabled(
            len(self.tbl_selected_parties.selectedItems()))

    def select_all(self):
        """
        SLOT. Select all parties listed from left list widget.
        """
        items_ids = []
        for index in range(self.lst_all_parties.count()):
            items_ids.append(
                self.lst_all_parties.item(index).data(Qt.UserRole))
        self.add_parties_to_selected(items_ids)

    def deselect_all(self):
        """
        SLOT. Remove all parties from left list widget.
        """
        items_ids = []
        for index in range(self.tbl_selected_parties.rowCount()):
            items_ids.append(
                self.tbl_selected_parties.item(index, 0).data(Qt.UserRole))
        self.remove_parties_from_selected(items_ids)

    def select(self):
        """
        SLOT. Select all parties highlighted in left list widget.
        """
        self.add_parties_to_selected([
            item.data(Qt.UserRole)
            for item in self.lst_all_parties.selectedItems()
        ])

    def deselect(self):
        """
        SLOT. Remove all parties highlighted in right list widget.
        """
        self.remove_parties_from_selected([
            item.data(Qt.UserRole)
            for item in self.tbl_selected_parties.selectedItems()
            if item.column() == 0
        ])

    def add_parties_to_selected(self, parties_ids):
        self.current_selected_parties.extend(parties_ids)
        self.update_lists()

    def remove_parties_from_selected(self, parties_ids):
        for party_id in parties_ids:
            self.current_selected_parties.remove(party_id)
            if party_id in self.parties_to_group:
                del self.parties_to_group[party_id]
        self.update_lists()

    def update_lists(self, only_update_all_list=False):
        """
        Update left list widget and optionally the right one.

        :param only_update_all_list: Only update left list widget.
        :type only_update_all_list: bool
        """
        # All parties
        self.lst_all_parties.clear()
        if self.txt_search_party.text():
            tmp_parties = {
                i: d
                for i, d in self.data.items()
                if self.txt_search_party.text().lower() in d[0].lower()
            }
        else:
            tmp_parties = copy.deepcopy(self.data)  # Copy all!

        for party_id in self.current_selected_parties:
            if party_id in tmp_parties:
                del tmp_parties[party_id]

        for i, d in tmp_parties.items():
            item = QListWidgetItem(d[0])
            item.setData(Qt.UserRole, i)
            self.lst_all_parties.addItem(item)

        if not only_update_all_list:
            # Selected parties
            self.tbl_selected_parties.clearContents()
            self.tbl_selected_parties.setRowCount(
                len(self.current_selected_parties))
            self.tbl_selected_parties.setColumnCount(3)
            self.tbl_selected_parties.setSortingEnabled(False)

            for row, party_id in enumerate(self.current_selected_parties):
                item = QTableWidgetItem(self.data[party_id][0])
                item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                item.setData(Qt.UserRole, party_id)
                self.tbl_selected_parties.setItem(row, 0, item)
                value_denominator = self.parties_to_group[party_id][
                    0] if party_id in self.parties_to_group else self.data[
                        party_id][1]
                self.tbl_selected_parties.setItem(
                    row, 1, QTableWidgetItem(str(value_denominator)))
                value_numerator = self.parties_to_group[party_id][
                    1] if party_id in self.parties_to_group else self.data[
                        party_id][2]
                self.tbl_selected_parties.setItem(
                    row, 2, QTableWidgetItem(str(value_numerator)))

            self.tbl_selected_parties.setSortingEnabled(True)

    def valueEdited(self, row, column):
        """
        SLOT. Update either the denominator or the numerator for given row.

        :param row: Edited row
        :type row: int
        :param column: Edited column
        :type column: int
        """
        if column != 0:
            party_id = self.tbl_selected_parties.item(row, 0).data(Qt.UserRole)
            value_denominator = self.tbl_selected_parties.item(row, 1).text()

            # While creating a row and the second column is created, the third
            # one doesn't exist, so use the value already stored for that case
            value_numerator = self.parties_to_group[party_id][
                1] if party_id in self.parties_to_group else 0
            if self.tbl_selected_parties.item(row, 2) is not None:
                value_numerator = self.tbl_selected_parties.item(row, 2).text()

            self.parties_to_group[party_id] = [
                value_denominator, value_numerator
            ]

    def accept(self):
        """ Overwrite the dialog's `accept
        <https://doc.qt.io/qt-5/qdialog.html#accept>`_ SLOT to store
        selected parties and numerator-denominator before closing the dialog.
        """
        self.parties_to_group = {}
        for index in range(self.tbl_selected_parties.rowCount()):
            k = self.tbl_selected_parties.item(index, 0).data(Qt.UserRole)
            try:
                v_n = int(self.tbl_selected_parties.item(index, 1).text())
            except ValueError as e:
                self.show_message(
                    QCoreApplication.translate(
                        "WizardTranslations",
                        "There are some invalid values in the numerator column. Fix them before continuing..."
                    ), Qgis.Warning)
                return
            try:
                v_d = int(self.tbl_selected_parties.item(index, 2).text())
            except ValueError as e:
                self.show_message(
                    QCoreApplication.translate(
                        "WizardTranslations",
                        "There are some invalid values in the denominator column. Fix them before continuing..."
                    ), Qgis.Warning)
                return

            self.parties_to_group[k] = [v_n, v_d]

        name = self.txt_group_name.text()
        group_party_type = self.cbo_group_type.itemData(
            self.cbo_group_type.currentIndex())
        dict_params = {
            self.names.COL_PARTY_T_NAME_F: name,
            self.names.COL_GROUP_PARTY_T_TYPE_F: group_party_type,
            'porcentajes': self.parties_to_group
        }

        res, msg = self.validate_group_party(dict_params)

        if not res:
            self.show_message(msg, Qgis.Warning)
            return

        self.save_group_party(self._db, [dict_params])

    def validate_group_party(self, params):
        name = params[self.names.COL_PARTY_T_NAME_F]
        group_party_type = params[self.names.COL_GROUP_PARTY_T_TYPE_F]
        porcentajes = params['porcentajes']

        if not porcentajes:
            return (False,
                    QCoreApplication.translate(
                        "CreateGroupParty",
                        "You need to select some parties to create a group."))
        elif len(porcentajes) == 1:
            return (
                False,
                QCoreApplication.translate(
                    "CreateGroupParty",
                    "There is just one party, you need to add at least two parties to a group."
                ))

        there_percents = False
        fraction = Fraction()
        for t, nd in porcentajes.items():
            if porcentajes[t] != [0, 0]:
                there_percents = True
                break

        if there_percents:
            for t, nd in porcentajes.items():
                if porcentajes[t][1] == 0:
                    return (
                        False,
                        QCoreApplication.translate(
                            "CreateGroupParty",
                            "There are denominators equal to zero. You need to change those values."
                        ))
                elif porcentajes[t][1] < porcentajes[t][0]:
                    return (
                        False,
                        QCoreApplication.translate(
                            "CreateGroupParty",
                            "The denominator cannot be less than the numerator."
                        ))
                else:
                    fraction = Fraction(porcentajes[t][0],
                                        porcentajes[t][1]) + fraction
            if fraction != 1.0:
                return (False,
                        QCoreApplication.translate(
                            "CreateGroupParty",
                            "The sum of the fractions must be equal to one."))

        return (True,
                QCoreApplication.translate("CreateGroupParty",
                                           "Validation passed!"))

    def show_message(self, message, level):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, 10)

    def save_group_party(self, db, params):
        """
        Save group party data into associated tables: self.names.OP_GROUP_PARTY_T,
        self.names.MEMBERS_T and self.names.FRACTION_S.

        params: List of dicts, where each dict is an independent group party:
            {
                self.names.COL_PARTY_T_NAME_F: '',
                self.names.COL_GROUP_PARTY_T_TYPE_F: '',
                'porcentajes': {
                    't_id_miembro': [20, 100], # numerador/denominador
                    't_id_miembro2': [40, 100]
                }
            }
        """
        # Disconnect from previous runs
        self.disconnect_signals()

        for group in params:
            # Create connections to react when a group party is stored to the DB
            self._layers[self.names.OP_GROUP_PARTY_T][
                LAYER].committedFeaturesAdded.connect(
                    partial(self.finish_group_party_saving,
                            group['porcentajes']))

            # First save the group party
            new_feature = QgsVectorLayerUtils().createFeature(
                self._layers[self.names.OP_GROUP_PARTY_T][LAYER])
            new_feature.setAttribute(
                self.names.COL_GROUP_PARTY_T_TYPE_F,
                group[self.names.COL_GROUP_PARTY_T_TYPE_F])
            new_feature.setAttribute(self.names.COL_PARTY_T_NAME_F,
                                     group[self.names.COL_PARTY_T_NAME_F])

            # TODO: Remove when local id and working space are defined
            new_feature.setAttribute(self.names.OID_T_LOCAL_ID_F, 1)
            new_feature.setAttribute(self.names.OID_T_NAMESPACE_F,
                                     self.names.OP_GROUP_PARTY_T)

            # TODO: Gui should allow users to ented namespace, local_id and date values
            #new_feature.setAttribute("p_espacio_de_nombres", self.names.OP_GROUP_PARTY_T)
            #new_feature.setAttribute("p_local_id", '0')
            #new_feature.setAttribute("comienzo_vida_util_version", 'now()')

            self.logger.info(__name__, "Saving Group Party: {}".format(group))
            with edit(self._layers[self.names.OP_GROUP_PARTY_T][LAYER]):
                self._layers[self.names.OP_GROUP_PARTY_T][LAYER].addFeature(
                    new_feature)

    def finish_group_party_saving(self, members, layer_id, features):
        try:
            self._layers[self.names.OP_GROUP_PARTY_T][
                LAYER].committedFeaturesAdded.disconnect()
        except TypeError as e:
            pass

        message = QCoreApplication.translate(
            "WizardTranslations",
            "'{}' tool has been closed because an error occurred while trying to save the data."
        ).format(self.WIZARD_TOOL_NAME)
        if len(features) != 1:
            message = QCoreApplication.translate(
                "WizardTranslations",
                "'{}' tool has been closed. We should have got only one group party... We cannot do anything with {} group parties"
            ).format(self.WIZARD_TOOL_NAME, len(features))
            self.logger.warning(
                __name__,
                "We should have got only one group party... We cannot do anything with {} group parties"
                .format(len(features)))
        else:
            fid = features[0].id()
            if not self._layers[self.names.OP_GROUP_PARTY_T][LAYER].getFeature(
                    fid).isValid():
                self.logger.warning(
                    __name__, "Feature not found in table Group Party...")
            else:
                group_party_id = self._layers[self.names.OP_GROUP_PARTY_T][
                    LAYER].getFeature(fid)[self.names.T_ID_F]

                # Now save members
                party_ids = list()
                for party_id, fraction in members.items():
                    # Create connections to react when a group party is stored to the DB
                    self._layers[self.names.MEMBERS_T][
                        LAYER].committedFeaturesAdded.connect(
                            partial(self.finish_member_saving, fraction))

                    new_feature = QgsVectorLayerUtils().createFeature(
                        self._layers[self.names.MEMBERS_T][LAYER])
                    new_feature.setAttribute(
                        self.names.MEMBERS_T_GROUP_PARTY_F, group_party_id)
                    new_feature.setAttribute(self.names.MEMBERS_T_PARTY_F,
                                             party_id)
                    self.logger.info(
                        __name__,
                        "Saving group party's member ({}: {}).".format(
                            group_party_id, party_id))
                    with edit(self._layers[self.names.MEMBERS_T][LAYER]):
                        self._layers[self.names.MEMBERS_T][LAYER].addFeature(
                            new_feature)
                        party_ids.append(party_id)

                if len(party_ids):
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new group party (t_id={}) was successfully created and associated with its corresponding party(ies) (t_id={})!"
                    ).format(group_party_id,
                             ", ".join([str(b) for b in party_ids]))
                else:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new group party (t_id={}) was successfully created but this one wasn't associated with a party(ies)"
                    ).format(group_party_id)
        self.close_wizard(message)

    def finish_member_saving(self, fraction, layer_id, features):
        try:
            self._layers[self.names.
                         MEMBERS_T][LAYER].committedFeaturesAdded.disconnect()
        except TypeError as e:
            pass

        if len(features) != 1:
            self.logger.warning(
                __name__,
                "We should have got only one member... We cannot do anything with {} members"
                .format(len(features)))
        else:
            fid = features[0].id()
            if not self._layers[self.names.MEMBERS_T][LAYER].getFeature(
                    fid).isValid():
                self.logger.warning(__name__,
                                    "Feature not found in table Members...")
            else:
                member_id = self._layers[self.names.MEMBERS_T][
                    LAYER].getFeature(fid)[self.names.T_ID_F]

                if fraction == [0, 0]:
                    return

                # And finally save fractions
                new_feature = QgsVectorLayerUtils().createFeature(
                    self._layers[self.names.FRACTION_S][LAYER])
                new_feature.setAttribute(self.names.FRACTION_S_MEMBER_F,
                                         member_id)
                new_feature.setAttribute(self.names.FRACTION_S_NUMERATOR_F,
                                         fraction[0])
                new_feature.setAttribute(self.names.FRACTION_S_DENOMINATOR_F,
                                         fraction[1])
                with edit(self._layers[self.names.FRACTION_S][LAYER]):
                    self.logger.info(
                        __name__, "Saving member's fraction ({}: {}).".format(
                            member_id, fraction))
                    self._layers[self.names.FRACTION_S][LAYER].addFeature(
                        new_feature)

    def close_wizard(self, message=None, show_message=True):
        if message is None:
            message = QCoreApplication.translate(
                "WizardTranslations",
                "'{}' tool has been closed.").format(self.WIZARD_TOOL_NAME)
        if show_message:
            self.logger.info_msg(__name__, message)
        self.disconnect_signals()
        self.close()

    def disconnect_signals(self):
        try:
            self._layers[self.names.OP_GROUP_PARTY_T][
                LAYER].committedFeaturesAdded.disconnect()
        except TypeError as e:
            pass
        try:
            self._layers[self.names.
                         MEMBERS_T][LAYER].committedFeaturesAdded.disconnect()
        except TypeError as e:
            pass

    def show_help(self):
        self.qgis_utils.show_help("group_party")
class PublishWidget(BASE, WIDGET):
    def __init__(self, parent):
        super(PublishWidget, self).__init__()
        self.isMetadataPublished = {}
        self.isDataPublished = {}
        self.currentRow = None
        self.currentLayer = None
        self.parent = parent

        self.fieldsToPublish = {}
        self.metadata = {}
        execute(self._setupUi)

    def _setupUi(self):
        self.setupUi(self)
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().insertWidget(0, self.bar)

        self.txtNoLayers.setVisible(False)

        self.populateComboBoxes()
        self.populateLayers()
        self.listLayers.setContextMenuPolicy(Qt.CustomContextMenu)
        self.listLayers.customContextMenuRequested.connect(
            self.showContextMenu)
        self.listLayers.currentRowChanged.connect(self.currentRowChanged)
        self.comboGeodataServer.currentIndexChanged.connect(
            self.geodataServerChanged)
        self.comboMetadataServer.currentIndexChanged.connect(
            self.metadataServerChanged)
        self.btnPublish.clicked.connect(self.publish)
        self.btnPublishOnBackground.clicked.connect(self.publishOnBackground)
        self.btnOpenQgisMetadataEditor.clicked.connect(self.openMetadataEditor)
        self.labelSelect.linkActivated.connect(self.selectLabelClicked)
        self.btnRemoveAll.clicked.connect(self.unpublishAll)
        self.btnRemoveAll.setIcon(REMOVE_ICON)
        self.btnValidate.setIcon(VALIDATE_ICON)
        self.btnPreview.clicked.connect(self.previewMetadata)
        self.btnPreview.setIcon(PREVIEW_ICON)
        self.btnImport.setIcon(IMPORT_ICON)
        #self.btnImport.setVisible(False)
        self.btnImport.clicked.connect(self.importMetadata)
        self.btnValidate.clicked.connect(self.validateMetadata)
        self.btnUseConstraints.clicked.connect(
            lambda: self.openMetadataEditor(ACCESS))
        self.btnAccessConstraints.clicked.connect(
            lambda: self.openMetadataEditor(ACCESS))
        self.btnIsoTopic.clicked.connect(
            lambda: self.openMetadataEditor(CATEGORIES))
        self.btnKeywords.clicked.connect(
            lambda: self.openMetadataEditor(KEYWORDS))
        self.btnDataContact.clicked.connect(
            lambda: self.openMetadataEditor(CONTACT))
        self.btnMetadataContact.clicked.connect(
            lambda: self.openMetadataEditor(CONTACT))
        self.btnExportFolder.clicked.connect(self.selectExportFolder)

        if self.listLayers.count():
            item = self.listLayers.item(0)
            self.listLayers.setCurrentItem(item)
            self.currentRowChanged(0)
        else:
            self.txtNoLayers.setVisible(True)
            self.listLayers.setVisible(False)
            self.labelSelect.setVisible(False)
            #self.spacerLayerSelect.setVisible(False)
            self.btnRemoveAll.setVisible(False)

        self.metadataServerChanged()
        self.selectLabelClicked("all")

    def selectExportFolder(self):
        folder = QFileDialog.getExistingDirectory(self,
                                                  self.tr("Export to folder"))
        if folder:
            self.txtExportFolder.setText(folder)

    def geodataServerChanged(self):
        self.updateLayersPublicationStatus(True, False)

    def metadataServerChanged(self):
        self.updateLayersPublicationStatus(False, True)
        try:
            profile = metadataServers()[
                self.comboMetadataServer.currentText()].profile
        except KeyError:
            profile = GeonetworkServer.PROFILE_DEFAULT
        if profile == GeonetworkServer.PROFILE_DEFAULT:
            if self.tabWidgetMetadata.count() == 3:
                self.tabWidgetMetadata.removeTab(1)
                self.tabWidgetMetadata.removeTab(1)
        else:
            if self.tabWidgetMetadata.count() == 1:
                title = "Dutch geography" if profile == GeonetworkServer.PROFILE_DUTCH else "INSPIRE"
                self.tabWidgetMetadata.addTab(self.tabInspire, title)
                self.tabWidgetMetadata.addTab(self.tabTemporal,
                                              self.tr("Temporal"))
            self.comboStatus.setVisible(
                profile == GeonetworkServer.PROFILE_DUTCH)

    def selectLabelClicked(self, url):
        state = Qt.Unchecked if url == "none" else Qt.Checked
        for i in range(self.listLayers.count()):
            item = self.listLayers.item(i)
            widget = self.listLayers.itemWidget(item).setCheckState(state)

    def currentRowChanged(self, currentRow):
        if self.currentRow == currentRow:
            return
        self.currentRow = currentRow
        self.storeFieldsToPublish()
        self.storeMetadata()
        layers = self.publishableLayers()
        layer = layers[currentRow]
        self.currentLayer = layer
        self.populateLayerMetadata()
        self.populateLayerFields()
        if layer.type() != layer.VectorLayer:
            self.tabLayerInfo.setCurrentWidget(self.tabMetadata)

    def populateLayerMetadata(self):
        metadata = self.metadata[self.currentLayer]
        self.txtMetadataTitle.setText(metadata.title())
        self.txtAbstract.setPlainText(metadata.abstract())
        isoTopics = ",".join(metadata.keywords().get("gmd:topicCategory", []))
        self.txtIsoTopic.setText(isoTopics)
        keywords = []
        for group in metadata.keywords().values():
            keywords.extend(group)
        self.txtKeywords.setText(",".join(keywords))
        if metadata.contacts():
            self.txtDataContact.setText(metadata.contacts()[0].name)
            self.txtMetadataContact.setText(metadata.contacts()[0].name)
        self.txtUseConstraints.setText(metadata.fees())
        licenses = metadata.licenses()
        if licenses:
            self.txtAccessConstraints.setText(licenses[0])
        else:
            self.txtAccessConstraints.setText("")
        self.comboLanguage.setCurrentText(metadata.language())
        #TODO: Use default values if no values in QGIS metadata object

    def populateLayerFields(self):
        if self.currentLayer.type() == self.currentLayer.VectorLayer:
            fields = [f.name() for f in self.currentLayer.fields()]
            self.tabLayerInfo.setTabEnabled(1, True)
            self.tableFields.setRowCount(len(fields))
            for i, field in enumerate(fields):
                item = QTableWidgetItem()
                item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                check = Qt.Checked if self.fieldsToPublish[
                    self.currentLayer][field] else Qt.Unchecked
                item.setCheckState(check)
                self.tableFields.setItem(i, 0, item)
                item = QTableWidgetItem(field)
                item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                self.tableFields.setItem(i, 1, item)
        else:
            self.tabLayerInfo.setTabEnabled(1, False)

    def storeMetadata(self):
        if self.currentLayer is not None:
            metadata = self.metadata[self.currentLayer]
            metadata.setTitle(self.txtMetadataTitle.text())
            metadata.setAbstract(self.txtAbstract.toPlainText())
            metadata.setLanguage(self.comboLanguage.currentText())
            self.currentLayer.setMetadata(metadata)

    def storeFieldsToPublish(self):
        if self.currentLayer is not None:
            if self.currentLayer.type() == self.currentLayer.VectorLayer:
                fieldsToPublish = {}
                fields = self.currentLayer.fields()
                for i in range(fields.count()):
                    chkItem = self.tableFields.item(i, 0)
                    nameItem = self.tableFields.item(i, 1)
                    fieldsToPublish[
                        nameItem.text()] = chkItem.checkState() == Qt.Checked
                self.fieldsToPublish[self.currentLayer] = fieldsToPublish

    def showContextMenu(self, pos):
        item = self.listLayers.itemAt(pos)
        if item is None:
            return
        row = self.listLayers.row(item)
        name = self.listLayers.itemWidget(item).name()
        menu = QMenu()
        if self.isDataPublished.get(name):
            menu.addAction(self.tr("View WMS layer"),
                           lambda: self.viewWms(name))
            menu.addAction(self.tr("Unpublish data"),
                           lambda: self.unpublishData(name))
        if self.isMetadataPublished.get(name):
            menu.addAction(self.tr("View metadata"),
                           lambda: self.viewMetadata(name))
            menu.addAction(self.tr("Unpublish metadata"),
                           lambda: self.unpublishMetadata(name))
        if any(self.isDataPublished.values()):
            menu.addAction(self.tr("View all WMS layers"), self.viewAllWms)
        menu.exec_(self.listLayers.mapToGlobal(pos))

    def publishableLayers(self):
        def _layersFromTree(layerTreeGroup):
            _layers = []
            for child in layerTreeGroup.children():
                if isinstance(child, QgsLayerTreeLayer):
                    _layers.append(child.layer())
                elif isinstance(child, QgsLayerTreeGroup):
                    _layers.extend(_layersFromTree(child))

            return _layers

        root = QgsProject.instance().layerTreeRoot()
        layers = [
            layer for layer in _layersFromTree(root) if
            layer.type() in [QgsMapLayer.VectorLayer, QgsMapLayer.RasterLayer]
            and layer.dataProvider().name() != "wms"
        ]
        return layers

    def populateLayers(self):
        layers = self.publishableLayers()
        for i, layer in enumerate(layers):
            fields = [f.name() for f in layer.fields()
                      ] if layer.type() == layer.VectorLayer else []
            self.fieldsToPublish[layer] = {f: True for f in fields}
            self.metadata[layer] = layer.metadata().clone()
            self.addLayerListItem(layer)
        self.updateLayersPublicationStatus()

    def addLayerListItem(self, layer):
        widget = LayerItemWidget(layer)
        item = QListWidgetItem(self.listLayers)
        item.setSizeHint(widget.sizeHint())
        self.listLayers.addItem(item)
        self.listLayers.setItemWidget(item, widget)
        return item

    def populateComboBoxes(self):
        self.populatecomboMetadataServer()
        self.populatecomboGeodataServer()
        self.comboLanguage.clear()
        for lang in QgsMetadataWidget.parseLanguages():
            self.comboLanguage.addItem(lang)

    def populatecomboGeodataServer(self):
        self.comboGeodataServer.clear()
        self.comboGeodataServer.addItem(self.tr("Do not publish data"))
        self.comboGeodataServer.addItems(geodataServers().keys())

    def populatecomboMetadataServer(self):
        self.comboMetadataServer.clear()
        self.comboMetadataServer.addItem(self.tr("Do not publish metadata"))
        self.comboMetadataServer.addItems(metadataServers().keys())

    def updateServers(self):
        #TODO: do not call updateLayersPublicationStatus if not really needed
        self.comboGeodataServer.currentIndexChanged.disconnect(
            self.geodataServerChanged)
        self.comboMetadataServer.currentIndexChanged.disconnect(
            self.metadataServerChanged)
        self.populatecomboMetadataServer()
        current = self.comboGeodataServer.currentText()
        self.populatecomboGeodataServer()
        if current in geodataServers().keys():
            self.comboGeodataServer.setCurrentText(current)
        current = self.comboMetadataServer.currentText()
        self.populatecomboMetadataServer()
        if current in metadataServers().keys():
            self.comboMetadataServer.setCurrentText(current)
        self.updateLayersPublicationStatus()
        self.comboGeodataServer.currentIndexChanged.connect(
            self.geodataServerChanged)
        self.comboMetadataServer.currentIndexChanged.connect(
            self.metadataServerChanged)

    def isMetadataOnServer(self, layer):
        try:
            server = metadataServers()[self.comboMetadataServer.currentText()]
            uuid = uuidForLayer(self.layerFromName(layer))
            return server.metadataExists(uuid)
        except KeyError:
            return False
        except:
            return False

    def isDataOnServer(self, layer):
        try:
            server = geodataServers()[self.comboGeodataServer.currentText()]
            return server.layerExists(layer)
        except KeyError:
            return False
        except Exception as e:
            return False

    def importMetadata(self):
        if self.currentLayer is None:
            return
        metadataFile = os.path.splitext(self.currentLayer.source())[0] + ".xml"
        if not os.path.exists(metadataFile):
            metadataFile = self.currentLayer.source() + ".xml"
            if not os.path.exists(metadataFile):
                metadataFile = None
        if metadataFile is not None:
            try:
                loadMetadataFromIsoXml(self.currentLayer, metadataFile)
            except:
                self.bar.pushMessage(
                    self.tr("Error importing metadata"),
                    self.
                    tr("Cannot convert the metadata file. Maybe not ISO format?"
                       ),
                    level=Qgis.Warning,
                    duration=5)
                return

            self.metadata[
                self.currentLayer] = self.currentLayer.metadata().clone()
            self.populateLayerMetadata()
            self.bar.pushMessage("",
                                 self.tr("Metadata correctly imported"),
                                 level=Qgis.Success,
                                 duration=5)
        else:
            self.bar.pushMessage(
                self.tr("Error importing metadata"),
                self.tr("Cannot find ISO metadata file for the current layer"),
                level=Qgis.Warning,
                duration=5)

    def validateMetadata(self):
        if self.currentLayer is None:
            return
        self.storeMetadata()
        validator = QgsNativeMetadataValidator()
        result, errors = validator.validate(self.metadata[self.currentLayer])
        if result:
            txt = self.tr("No validation errors")
        else:
            txt = (self.tr("The following issues were found:") + "<br>" +
                   "<br>".join([
                       "<b>%s</b>:%s" % (err.section, err.note)
                       for err in errors
                   ]))
        dlg = QgsMessageOutput.createMessageOutput()
        dlg.setTitle(self.tr("Metadata validation"))
        dlg.setMessage(txt, QgsMessageOutput.MessageHtml)
        dlg.showMessage()

    def openMetadataEditor(self, tab):
        if self.currentLayer is None:
            return
        self.storeMetadata()
        metadata = self.metadata[self.currentLayer].clone()
        w = MetadataDialog(metadata, tab, self)
        w.exec_()
        if w.metadata is not None:
            self.metadata[self.currentLayer] = w.metadata
            self.populateLayerMetadata()

    def unpublishData(self, name):
        server = geodataServers()[self.comboGeodataServer.currentText()]
        server.deleteLayer(name)
        server.deleteStyle(name)
        self.updateLayerIsDataPublished(name, False)

    def unpublishMetadata(self, name):
        server = metadataServers()[self.comboMetadataServer.currentText()]
        uuid = uuidForLayer(self.layerFromName(name))
        server.deleteMetadata(uuid)
        self.updateLayerIsMetadataPublished(name, False)

    def updateLayerIsMetadataPublished(self, name, value):
        self.isMetadataPublished[name] = value
        for i in range(self.listLayers.count()):
            item = self.listLayers.item(i)
            widget = self.listLayers.itemWidget(item)
            if widget.name() == name:
                server = metadataServers()[
                    self.comboMetadataServer.currentText()] if value else None
                widget.setMetadataPublished(server)

    def updateLayerIsDataPublished(self, name, value):
        self.isDataPublished[name] = value
        for i in range(self.listLayers.count()):
            item = self.listLayers.item(i)
            widget = self.listLayers.itemWidget(item)
            if widget.name() == name:
                server = geodataServers()[
                    self.comboGeodataServer.currentText()] if value else None
                widget.setDataPublished(server)

    def updateLayersPublicationStatus(self, data=True, metadata=True):
        canPublish = True
        if data:
            try:
                dataServer = geodataServers()[
                    self.comboGeodataServer.currentText()]
                if dataServer.testConnection():
                    self.comboGeodataServer.setStyleSheet("QComboBox { }")
                else:
                    self.comboGeodataServer.setStyleSheet(
                        "QComboBox { border: 2px solid red; }")
                    dataServer = None
                    canPublish = False
            except KeyError:
                self.comboGeodataServer.setStyleSheet("QComboBox { }")
                dataServer = None

        if metadata:
            try:
                metadataServer = metadataServers()[
                    self.comboMetadataServer.currentText()]
                if metadataServer.testConnection():
                    self.comboMetadataServer.setStyleSheet("QComboBox { }")
                else:
                    self.comboMetadataServer.setStyleSheet(
                        "QComboBox { border: 2px solid red; }")
                    metadataServer = None
                    canPublish = False
            except KeyError:
                self.comboMetadataServer.setStyleSheet("QComboBox { }")
                metadataServer = None

        for i in range(self.listLayers.count()):
            item = self.listLayers.item(i)
            widget = self.listLayers.itemWidget(item)
            name = widget.name()
            if data:
                server = None
                if dataServer:
                    self.isDataPublished[name] = self.isDataOnServer(name)
                    server = dataServer if self.isDataPublished[name] else None
                widget.setDataPublished(server)
            if metadata:
                server = None
                if metadataServer:
                    self.isMetadataPublished[name] = self.isMetadataOnServer(
                        name)
                    server = metadataServer if self.isMetadataPublished[
                        name] else None
                widget.setMetadataPublished(server)

        canPublish = canPublish and self.listLayers.count()
        self.btnPublish.setEnabled(canPublish)
        self.btnPublishOnBackground.setEnabled(canPublish)

    def unpublishAll(self):
        for name in self.isDataPublished:
            if self.isDataPublished.get(name, False):
                self.unpublishData(name)
            if self.isMetadataPublished.get(name, False):
                self.unpublishMetadata(name)

    def viewWms(self, name):
        layer = self.layerFromName(name)
        names = [layer.name()]
        bbox = layer.extent()
        if bbox.isEmpty():
            bbox.grow(1)
        sbbox = ",".join([
            str(v) for v in [
                bbox.xMinimum(),
                bbox.yMinimum(),
                bbox.xMaximum(),
                bbox.yMaximum()
            ]
        ])
        server = geodataServers()[self.comboGeodataServer.currentText()]
        server.openPreview(names, sbbox, layer.crs().authid())

    def viewAllWms(self):
        server = geodataServers()[self.comboGeodataServer.currentText()]
        layers = self.publishableLayers()
        bbox = QgsRectangle()
        canvasCrs = iface.mapCanvas().mapSettings().destinationCrs()
        names = []
        for layer in layers:
            if self.isDataPublished[layer.name()]:
                names.append(layer.name())
                xform = QgsCoordinateTransform(layer.crs(), canvasCrs,
                                               QgsProject.instance())
                extent = xform.transform(layer.extent())
                bbox.combineExtentWith(extent)
        sbbox = ",".join([
            str(v) for v in [
                bbox.xMinimum(),
                bbox.yMinimum(),
                bbox.xMaximum(),
                bbox.yMaximum()
            ]
        ])
        server.openPreview(names, sbbox, canvasCrs.authid())

    def previewMetadata(self):
        if self.currentLayer is None:
            return
        html = self.currentLayer.htmlMetadata()
        dlg = QgsMessageOutput.createMessageOutput()
        dlg.setTitle("Layer metadata")
        dlg.setMessage(html, QgsMessageOutput.MessageHtml)
        dlg.showMessage()

    def viewMetadata(self, name):
        server = metadataServers()[self.comboMetadataServer.currentText()]
        layer = self.layerFromName(name)
        uuid = uuidForLayer(layer)
        server.openMetadata(uuid)

    def publish(self):
        if self.validateBeforePublication():
            toPublish = self._toPublish()
            progressDialog = ProgressDialog(toPublish, self.parent)
            task = self.getPublishTask(self.parent)
            task.stepStarted.connect(progressDialog.setInProgress)
            task.stepSkipped.connect(progressDialog.setSkipped)
            task.stepFinished.connect(progressDialog.setFinished)
            progressDialog.show()
            #task.progressChanged.connect(progress.setValue)
            ret = execute(task.run)
            progressDialog.close()
            #self.bar.clearWidgets()
            task.finished(ret)
            if task.exception is not None:
                if task.exceptiontype == requests.exceptions.ConnectionError:
                    QMessageBox.warning(
                        self, self.tr("Error while publishing"),
                        self.
                        tr("Connection error. Server unavailable.\nSee QGIS log for details"
                           ))
                else:
                    self.bar.clearWidgets()
                    self.bar.pushMessage(self.tr("Error while publishing"),
                                         self.tr("See QGIS log for details"),
                                         level=Qgis.Warning,
                                         duration=5)
                QgsMessageLog.logMessage(task.exception,
                                         'GeoCat Bridge',
                                         level=Qgis.Critical)
            if isinstance(task, PublishTask):
                self.updateLayersPublicationStatus(
                    task.geodataServer is not None, task.metadataServer
                    is not None)

    def publishOnBackground(self):
        if self.validateBeforePublication():
            self.parent.close()
            task = self.getPublishTask(iface.mainWindow())

            def _finished():
                if task.exception is not None:
                    iface.messageBar().pushMessage(
                        self.tr("Error while publishing"),
                        self.tr("See QGIS log for details"),
                        level=Qgis.Warning,
                        duration=5)
                    QgsMessageLog.logMessage(task.exception,
                                             'GeoCat Bridge',
                                             level=Qgis.Critical)

            task.taskTerminated.connect(_finished)
            QgsApplication.taskManager().addTask(task)
            QCoreApplication.processEvents()

    def validateBeforePublication(self):
        names = []
        errors = set()
        for i in range(self.listLayers.count()):
            item = self.listLayers.item(i)
            widget = self.listLayers.itemWidget(item)
            if widget.checked():
                name = widget.name()
                for c in "?&=#":
                    if c in name:
                        errors.add("Unsupported character in layer name: " + c)
                if name in names:
                    errors.add("Several layers with the same name")
                names.append(name)

        if self.comboGeodataServer.currentIndex() != 0:
            geodataServer = geodataServers()[
                self.comboGeodataServer.currentText()]
            geodataServer.validateBeforePublication(errors)

        if self.comboMetadataServer.currentIndex() != 0:
            metadataServer = metadataServers()[
                self.comboMetadataServer.currentText()]
            metadataServer.validateBeforePublication(errors)

        if errors:
            txt = '''<p><b>Cannot publish data.</b></p>
                    <p>The following issues were found:<p><ul><li>%s</li></ul>
                    ''' % "</li><li>".join(errors)
            dlg = QgsMessageOutput.createMessageOutput()
            dlg.setTitle("Publish")
            dlg.setMessage(txt, QgsMessageOutput.MessageHtml)
            dlg.showMessage()
            return False
        else:
            return True

    def _toPublish(self):
        toPublish = []
        for i in range(self.listLayers.count()):
            item = self.listLayers.item(i)
            widget = self.listLayers.itemWidget(item)
            if widget.checked():
                name = widget.name()
                toPublish.append(name)
        return toPublish

    def getPublishTask(self, parent):
        self.storeMetadata()
        self.storeFieldsToPublish()

        toPublish = self._toPublish()

        if self.tabOnOffline.currentIndex() == 0:
            if self.comboGeodataServer.currentIndex() != 0:
                geodataServer = geodataServers()[
                    self.comboGeodataServer.currentText()]
            else:
                geodataServer = None

            if self.comboMetadataServer.currentIndex() != 0:
                metadataServer = metadataServers()[
                    self.comboMetadataServer.currentText()]
            else:
                metadataServer = None

            onlySymbology = self.chkOnlySymbology.checkState() == Qt.Checked

            return PublishTask(toPublish, self.fieldsToPublish, onlySymbology,
                               geodataServer, metadataServer, parent)
        else:
            return ExportTask(self.txtExportFolder.text(), toPublish,
                              self.fieldsToPublish,
                              self.chkExportData.isChecked(),
                              self.chkExportMetadata.isChecked(),
                              self.chkExportSymbology.isChecked())

    def layerFromName(self, name):
        layers = self.publishableLayers()
        for layer in layers:
            if layer.name() == name:
                return layer
class CreatePointsSurveyWizard(QWizard, WIZARD_UI):
    WIZARD_NAME = "CreatePointsSurveyWizard"
    WIZARD_TOOL_NAME = QCoreApplication.translate(WIZARD_NAME, "Create Point")

    def __init__(self, iface, db):
        QWizard.__init__(self)
        self.setupUi(self)
        self.iface = iface
        self._db = db
        self.logger = Logger()
        self.app = AppInterface()

        self.names = self._db.names
        self.help_strings = HelpStrings()

        self._layers = {
            self.names.LC_BOUNDARY_POINT_T: None,
            self.names.LC_SURVEY_POINT_T: None,
            self.names.LC_CONTROL_POINT_T: None
        }

        self.target_layer = None

        # Auxiliary data to set nonlinear next pages
        self.pages = [self.wizardPage1, self.wizardPage2, self.wizardPage3]
        self.dict_pages_ids = {self.pages[idx] : pid for idx, pid in enumerate(self.pageIds())}

        self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PointLayer)

        # Set connections
        self.btn_browse_file.clicked.connect(
            make_file_selector(self.txt_file_path,
                               file_filter=QCoreApplication.translate("WizardTranslations",'CSV File (*.csv *.txt)')))
        self.txt_file_path.textChanged.connect(self.file_path_changed)
        self.crsSelector.crsChanged.connect(self.crs_changed)
        self.crs = ""  # SRS auth id
        self.txt_delimiter.textChanged.connect(self.fill_long_lat_combos)
        self.mMapLayerComboBox.layerChanged.connect(self.import_layer_changed)

        self.known_delimiters = [
            {'name': ';', 'value': ';'},
            {'name': ',', 'value': ','},
            {'name': 'tab', 'value': '\t'},
            {'name': 'space', 'value': ' '},
            {'name': '|', 'value': '|'},
            {'name': '~', 'value': '~'},
            {'name': 'Other', 'value': ''}
        ]
        self.cbo_delimiter.addItems([ item['name'] for item in self.known_delimiters ])
        self.cbo_delimiter.currentTextChanged.connect(self.separator_changed)

        self.restore_settings()

        self.txt_file_path.textChanged.emit(self.txt_file_path.text())

        self.rad_boundary_point.toggled.connect(self.point_option_changed)
        self.rad_control_point.toggled.connect(self.point_option_changed)
        self.rad_csv.toggled.connect(self.adjust_page_2_controls)
        self.point_option_changed() # Initialize it
        self.button(QWizard.FinishButton).clicked.connect(self.finished_dialog)
        self.currentIdChanged.connect(self.current_page_changed)

        self.txt_help_page_2.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_2_OPTION_CSV)

        self.wizardPage2.setButtonText(QWizard.FinishButton,
                                       QCoreApplication.translate("WizardTranslations",
                                            "Import"))
        self.txt_help_page_3.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_3_OPTION_CSV)
        self.txt_help_page_3.anchorClicked.connect(self.save_template)
        self.button(QWizard.HelpButton).clicked.connect(self.show_help)
        self.rejected.connect(self.close_wizard)

        # Set MessageBar for QWizard
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.setLayout(QGridLayout())
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def nextId(self):
        """
        Set navigation order. Should return an integer. -1 is Finish.
        """
        if self.currentId() == self.dict_pages_ids[self.wizardPage1]:
            return self.dict_pages_ids[self.wizardPage2]
        elif self.currentId() == self.dict_pages_ids[self.wizardPage2]:
            if self.rad_csv.isChecked():
                return self.dict_pages_ids[self.wizardPage3]
            elif self.rad_refactor.isChecked():
                return -1
        elif self.currentId() == self.dict_pages_ids[self.wizardPage3]:
            return -1
        else:
            return -1

    def current_page_changed(self, id):
        """
        Reset the Next button. Needed because Next might have been disabled by a
        condition in a another SLOT.
        """
        enable_next_wizard(self)

        if id == self.dict_pages_ids[self.wizardPage2]:
            self.adjust_page_2_controls()
        elif id == self.dict_pages_ids[self.wizardPage3]:
            self.set_buttons_visible(False)
            self.set_buttons_enabled(False)

            QCoreApplication.processEvents()
            self.check_z_in_geometry()
            QCoreApplication.processEvents()
            self.fill_long_lat_combos("")
            QCoreApplication.processEvents()

            self.set_buttons_visible(True)
            self.set_buttons_enabled(True)

    def set_buttons_visible(self, visible):
        self.button(self.BackButton).setVisible(visible)
        self.button(self.FinishButton).setVisible(visible)
        self.button(self.CancelButton).setVisible(visible)

    def set_buttons_enabled(self, enabled):
        self.wizardPage3.setEnabled(enabled)
        self.button(self.BackButton).setEnabled(enabled)
        self.button(self.FinishButton).setEnabled(enabled)
        self.button(self.CancelButton).setEnabled(enabled)

    def check_z_in_geometry(self):
        self.target_layer = self.app.core.get_layer(self._db, self.current_point_name(), load=True)
        if not self.target_layer:
            return

        if not QgsWkbTypes().hasZ(self.target_layer.wkbType()):
            self.labelZ.setEnabled(False)
            self.cbo_elevation.setEnabled(False)
            msg = QCoreApplication.translate("WizardTranslations",
                                             "The current model does not support 3D geometries")
            self.cbo_elevation.setToolTip(msg)
            self.labelZ.setToolTip(msg)
        else:
            self.labelZ.setEnabled(True)
            self.cbo_elevation.setEnabled(True)
            self.labelZ.setToolTip("")
            self.cbo_elevation.setToolTip("")

    def adjust_page_2_controls(self):
        self.cbo_mapping.clear()
        self.cbo_mapping.addItem("")
        self.cbo_mapping.addItems(self.app.core.get_field_mappings_file_names(self.current_point_name()))

        if self.rad_refactor.isChecked():
            self.lbl_refactor_source.setEnabled(True)
            self.mMapLayerComboBox.setEnabled(True)
            self.lbl_field_mapping.setEnabled(True)
            self.cbo_mapping.setEnabled(True)
            self.import_layer_changed(self.mMapLayerComboBox.currentLayer())

            disable_next_wizard(self)
            self.wizardPage2.setFinalPage(True)
            self.txt_help_page_2.setHtml(self.help_strings.get_refactor_help_string(self._db, self._layers[self.current_point_name()]))

        elif self.rad_csv.isChecked():
            self.lbl_refactor_source.setEnabled(False)
            self.mMapLayerComboBox.setEnabled(False)
            self.lbl_field_mapping.setEnabled(False)
            self.cbo_mapping.setEnabled(False)
            self.lbl_refactor_source.setStyleSheet('')

            enable_next_wizard(self)
            self.wizardPage2.setFinalPage(False)
            self.txt_help_page_2.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_2_OPTION_CSV)

    def point_option_changed(self):
        if self.rad_boundary_point.isChecked():
            self.gbx_page_2.setTitle(QCoreApplication.translate("WizardTranslations", "Load data to Boundary Points..."))
            self.gbx_page_3.setTitle(QCoreApplication.translate("WizardTranslations", "Configure CSV data source for Boundary Points..."))
            self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_1_OPTION_BP)
        elif self.rad_survey_point.isChecked(): # self.rad_survey_point is checked
            self.gbx_page_2.setTitle(QCoreApplication.translate("WizardTranslations", "Load data to Survey Points..."))
            self.gbx_page_3.setTitle(QCoreApplication.translate("WizardTranslations", "Configure CSV data source for Survey Points..."))
            self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_1_OPTION_SP)
        else: # self.rad_control_point is checked
            self.gbx_page_2.setTitle(QCoreApplication.translate("WizardTranslations", "Load data to Control Points..."))
            self.gbx_page_3.setTitle(QCoreApplication.translate("WizardTranslations", "Configure CSV data source for Control Points..."))
            self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_1_OPTION_CP)

    def finished_dialog(self):
        self.save_settings()

        if self.rad_refactor.isChecked():
            output_layer_name = self.current_point_name()

            if self.mMapLayerComboBox.currentLayer() is not None:
                field_mapping = self.cbo_mapping.currentText()
                res_etl_model = self.app.core.show_etl_model(self._db,
                                                               self.mMapLayerComboBox.currentLayer(),
                                                               output_layer_name,
                                                               field_mapping=field_mapping)

                if res_etl_model:
                    self.app.gui.redraw_all_layers()  # Redraw all layers to show imported data

                    # If the result of the etl_model is successful and we used a stored recent mapping, we delete the
                    # previous mapping used (we give preference to the latest used mapping)
                    if field_mapping:
                        self.app.core.delete_old_field_mapping(field_mapping)

                    self.app.core.save_field_mapping(output_layer_name)

            else:
                self.logger.warning_msg(__name__, QCoreApplication.translate("WizardTranslations",
                    "Select a source layer to set the field mapping to '{}'.").format(output_layer_name))

            self.close_wizard()

        elif self.rad_csv.isChecked():
            self.prepare_copy_csv_points_to_db()

    def close_wizard(self, message=None, show_message=True):
        if message is None:
            message = QCoreApplication.translate("WizardTranslations", "'{}' tool has been closed.").format(self.WIZARD_TOOL_NAME)
        if show_message:
            self.logger.info_msg(__name__, message)
        self.close()

    def current_point_name(self):
        if self.rad_boundary_point.isChecked():
            return self.names.LC_BOUNDARY_POINT_T
        elif self.rad_survey_point.isChecked():
            return self.names.LC_SURVEY_POINT_T
        else:
            return self.names.LC_CONTROL_POINT_T

    def prepare_copy_csv_points_to_db(self):
        csv_path = self.txt_file_path.text().strip()

        if not csv_path or not os.path.exists(csv_path):
            self.logger.warning_msg(__name__, QCoreApplication.translate("WizardTranslations",
                                                                         "No CSV file given or file doesn't exist."))
            return

        target_layer_name = self.current_point_name()

        with OverrideCursor(Qt.WaitCursor):
            csv_layer = self.app.core.csv_to_layer(csv_path,
                                                   self.txt_delimiter.text(),
                                                   self.cbo_longitude.currentText(),
                                                   self.cbo_latitude.currentText(),
                                                   self.crs,
                                                   self.cbo_elevation.currentText() or None,
                                                   self.detect_decimal_point(csv_path))

            self.app.core.copy_csv_to_db(csv_layer, self._db, target_layer_name)

    def required_layers_are_available(self):
        layers_are_available = self.app.core.required_layers_are_available(self._db, self._layers, self.WIZARD_TOOL_NAME)
        return layers_are_available

    def file_path_changed(self):
        self.autodetect_separator()
        self.fill_long_lat_combos("")
        self.cbo_delimiter.currentTextChanged.connect(self.separator_changed)

    def detect_decimal_point(self, csv_path):
        if os.path.exists(csv_path):
            with open(csv_path) as file:
                file.readline() # headers
                data = file.readline().strip() # 1st line with data

            if data:
                fields = self.get_fields_from_csv_file(csv_path)
                if self.cbo_latitude.currentText() in fields:
                    num_col = data.split(self.cbo_delimiter.currentText())[fields.index(self.cbo_latitude.currentText())]
                    for decimal_point in ['.', ',']:
                        if decimal_point in num_col:
                            return decimal_point

        return '.' # just use the default one

    def autodetect_separator(self):
        csv_path = self.txt_file_path.text().strip()
        if os.path.exists(csv_path):
            with open(csv_path) as file:
                first_line = file.readline()
                for delimiter in self.known_delimiters:
                    if delimiter['value'] == '':
                        continue
                    # if separator works like a column separator in header
                    # number of cols is greater than 1
                    if len(first_line.split(delimiter['value'])) > 1:
                        self.cbo_delimiter.setCurrentText(delimiter['name'])
                        return

    def update_crs_info(self):
        self.crsSelector.setCrs(QgsCoordinateReferenceSystem(self.crs))

    def crs_changed(self):
        self.crs = get_crs_authid(self.crsSelector.crs())
        if self.crs != DEFAULT_SRS_AUTHID:
            self.lbl_crs.setStyleSheet('color: orange')
            self.lbl_crs.setToolTip(QCoreApplication.translate("WizardTranslations",
                "Your CSV data will be reprojected for you to '{}' (Colombian National Origin),<br>before attempting to import it into LADM-COL.").format(DEFAULT_SRS_AUTHID))
        else:
            self.lbl_crs.setStyleSheet('')
            self.lbl_crs.setToolTip(QCoreApplication.translate("WizardTranslations", "Coordinate Reference System"))

    def fill_long_lat_combos(self, text):
        csv_path = self.txt_file_path.text().strip()
        self.cbo_longitude.clear()
        self.cbo_latitude.clear()
        self.cbo_elevation.clear()
        if os.path.exists(csv_path):
            self.button(QWizard.FinishButton).setEnabled(True)

            fields = self.get_fields_from_csv_file(csv_path)
            fields_dict = {field: field.lower() for field in fields}
            if not fields:
                self.button(QWizard.FinishButton).setEnabled(False)
                return

            self.cbo_longitude.addItems(fields)
            self.cbo_latitude.addItems(fields)
            self.cbo_elevation.addItems([""] + fields)

            # Heuristics to suggest values for x, y and z
            x_potential_names = ['x', 'lon', 'long', 'longitud', 'longitude', 'este', 'east', 'oeste', 'west']
            y_potential_names = ['y', 'lat', 'latitud', 'latitude', 'norte', 'north']
            z_potential_names = ['z', 'altura', 'elevacion', 'elevation', 'elevación', 'height']
            for x_potential_name in x_potential_names:
                for k,v in fields_dict.items():
                    if x_potential_name == v:
                        self.cbo_longitude.setCurrentText(k)
                        break
            for y_potential_name in y_potential_names:
                for k, v in fields_dict.items():
                    if y_potential_name == v:
                        self.cbo_latitude.setCurrentText(k)
                        break
            if self.cbo_elevation.isEnabled():
                for z_potential_name in z_potential_names:
                    for k, v in fields_dict.items():
                        if z_potential_name == v:
                            self.cbo_elevation.setCurrentText(k)
                            break

        else:
            self.button(QWizard.FinishButton).setEnabled(False)

    def get_fields_from_csv_file(self, csv_path):
        if not self.txt_delimiter.text():
            return []

        error_reading = False
        try:
            reader  = open(csv_path, "r")
        except IOError:
            error_reading = True
        line = reader.readline().replace("\n", "")
        reader.close()
        if not line:
            error_reading = True
        else:
            return line.split(self.txt_delimiter.text())

        if error_reading:
            self.logger.warning_msg(__name__, QCoreApplication.translate("WizardTranslations",
                "It was not possible to read field names from the CSV. Check the file and try again."))
        return []

    def separator_changed(self, text):
        # first ocurrence
        value = next((x['value'] for x in self.known_delimiters if x['name'] == text), '')
        self.txt_delimiter.setText(value)
        if value == '':
            self.txt_delimiter.setEnabled(True)
        else:
            self.txt_delimiter.setEnabled(False)

    def save_template(self, url):
        link = url.url()
        if self.rad_boundary_point.isChecked():
            if link == '#template':
                self.download_csv_file('template_boundary_points.csv')
            elif link == '#data':
                self.download_csv_file('sample_boundary_points.csv')
        elif self.rad_survey_point.isChecked():
            if link == '#template':
                self.download_csv_file('template_survey_points.csv')
            elif link == '#data':
                self.download_csv_file('sample_survey_points.csv')
        elif self.rad_control_point.isChecked():
            if link == '#template':
                self.download_csv_file('template_control_points.csv')
            elif link == '#data':
                self.download_csv_file('sample_control_points.csv')

    def download_csv_file(self, filename):
        settings = QSettings()
        settings.setValue('Asistente-LADM-COL/wizards/points_csv_file_delimiter', self.txt_delimiter.text().strip())

        new_filename, filter = QFileDialog.getSaveFileName(self,
                                   QCoreApplication.translate("WizardTranslations",
                                                              "Save File"),
                                   os.path.join(settings.value('Asistente-LADM-COL/wizards/points_download_csv_path', '.'), filename),
                                   QCoreApplication.translate("WizardTranslations",
                                                              "CSV File (*.csv *.txt)"))

        if new_filename:
            settings.setValue('Asistente-LADM-COL/wizards/points_download_csv_path', os.path.dirname(new_filename))
            template_file = QFile(":/Asistente-LADM-COL/resources/csv/" + filename)

            if not template_file.exists():
                self.logger.critical(__name__, "CSV doesn't exist! Probably due to a missing 'make' execution to generate resources...")
                msg = QCoreApplication.translate("WizardTranslations", "CSV file not found. Update your plugin. For details see log.")
                self.show_message(msg, Qgis.Warning)
                return

            if os.path.isfile(new_filename):
                self.logger.info(__name__, 'Removing existing file {}...'.format(new_filename))
                os.chmod(new_filename, 0o777)
                os.remove(new_filename)

            if template_file.copy(new_filename):
                os.chmod(new_filename, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
                msg = QCoreApplication.translate("WizardTranslations", """The file <a href="file:///{}">{}</a> was successfully saved!""").format(normalize_local_url(new_filename), os.path.basename(new_filename))
                self.show_message(msg, Qgis.Info)
            else:
                self.logger.warning(__name__, 'There was an error copying the CSV file {}!'.format(new_filename))
                msg = QCoreApplication.translate("WizardTranslations", "The file couldn\'t be saved.")
                self.show_message(msg, Qgis.Warning)

    def import_layer_changed(self, layer):
        if layer:
            crs = get_crs_authid(layer.crs())
            if crs != DEFAULT_SRS_AUTHID:
                self.lbl_refactor_source.setStyleSheet('color: orange')
                self.lbl_refactor_source.setToolTip(QCoreApplication.translate("WizardTranslations",
                                                                   "This layer will be reprojected for you to '{}' (Colombian National Origin),<br>before attempting to import it into LADM-COL.").format(
                    DEFAULT_SRS_AUTHID))
            else:
                self.lbl_refactor_source.setStyleSheet('')
                self.lbl_refactor_source.setToolTip('')

    def show_message(self, message, level):
        self.bar.clearWidgets()  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, 10)

    def save_settings(self):
        settings = QSettings()
        point_type = None
        if self.rad_boundary_point.isChecked():
            point_type = 'boundary_point'
        elif self.rad_survey_point.isChecked():
            point_type = 'survey_point'
        else:
            point_type = 'control_point'

        settings.setValue('Asistente-LADM-COL/wizards/points_add_points_type', point_type)
        settings.setValue('Asistente-LADM-COL/wizards/points_load_data_type', 'csv' if self.rad_csv.isChecked() else 'refactor')
        settings.setValue('Asistente-LADM-COL/wizards/points_add_points_csv_file', self.txt_file_path.text().strip())
        settings.setValue('Asistente-LADM-COL/wizards/points_csv_file_delimiter', self.txt_delimiter.text().strip())
        settings.setValue('Asistente-LADM-COL/wizards/points_csv_crs', self.crs)

    def restore_settings(self):
        settings = QSettings()
        point_type = settings.value('Asistente-LADM-COL/wizards/points_add_points_type') or 'boundary_point'
        if point_type == 'boundary_point':
            self.rad_boundary_point.setChecked(True)
        elif point_type == 'survey_point':
            self.rad_survey_point.setChecked(True)
        else: # 'control_point'
            self.rad_control_point.setChecked(True)

        load_data_type = settings.value('Asistente-LADM-COL/wizards/points_load_data_type') or 'csv'
        if load_data_type == 'refactor':
            self.rad_refactor.setChecked(True)
        else:
            self.rad_csv.setChecked(True)

        self.txt_file_path.setText(settings.value('Asistente-LADM-COL/wizards/points_add_points_csv_file'))
        self.txt_delimiter.setText(settings.value('Asistente-LADM-COL/wizards/points_csv_file_delimiter'))

        self.crs = settings.value('Asistente-LADM-COL/wizards/points_csv_crs', DEFAULT_SRS_AUTHID, str)
        self.update_crs_info()

    def show_help(self):
        show_plugin_help("create_points")
Exemple #36
0
class ImportFromExcelDialog(QDialog, DIALOG_UI):
    log_excel_show_message_emitted = pyqtSignal(str)

    def __init__(self, iface, db, qgis_utils, parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.iface = iface
        self._db = db
        self.qgis_utils = qgis_utils
        self.logger = Logger()
        self.help_strings = HelpStrings()
        self.log_dialog_excel_text_content = ""
        self.group_parties_exists = False
        self.names = self._db.names
        self._running_tool = False
        self.tool_name = QCoreApplication.translate(
            "ImportFromExcelDialog", "Import intermediate structure")

        self.fields = {
            EXCEL_SHEET_NAME_PLOT: [
                EXCEL_SHEET_TITLE_DEPARTMENT, EXCEL_SHEET_TITLE_MUNICIPALITY,
                EXCEL_SHEET_TITLE_ZONE, EXCEL_SHEET_TITLE_REGISTRATION_PLOT,
                EXCEL_SHEET_TITLE_NPN, EXCEL_SHEET_TITLE_NPV,
                EXCEL_SHEET_TITLE_PLOT_NAME, EXCEL_SHEET_TITLE_VALUATION,
                EXCEL_SHEET_TITLE_PLOT_CONDITION, EXCEL_SHEET_TITLE_PLOT_TYPE,
                EXCEL_SHEET_TITLE_ADDRESS
            ],
            EXCEL_SHEET_NAME_PARTY: [
                EXCEL_SHEET_TITLE_FIRST_NAME, EXCEL_SHEET_TITLE_MIDDLE,
                EXCEL_SHEET_TITLE_FIRST_SURNAME,
                EXCEL_SHEET_TITLE_SECOND_SURNAME,
                EXCEL_SHEET_TITLE_BUSINESS_NAME, EXCEL_SHEET_TITLE_SEX,
                EXCEL_SHEET_TITLE_DOCUMENT_TYPE,
                EXCEL_SHEET_TITLE_DOCUMENT_NUMBER,
                EXCEL_SHEET_TITLE_KIND_PERSON,
                EXCEL_SHEET_TITLE_ISSUING_ENTITY, EXCEL_SHEET_TITLE_DATE_ISSUE,
                EXCEL_SHEET_TITLE_NPN
            ],
            EXCEL_SHEET_NAME_GROUP: [
                EXCEL_SHEET_TITLE_NPN, EXCEL_SHEET_TITLE_DOCUMENT_TYPE,
                EXCEL_SHEET_TITLE_DOCUMENT_NUMBER, EXCEL_SHEET_TITLE_ID_GROUP
            ],
            EXCEL_SHEET_NAME_RIGHT: [
                EXCEL_SHEET_TITLE_TYPE,
                EXCEL_SHEET_TITLE_PARTY_DOCUMENT_NUMBER,
                EXCEL_SHEET_TITLE_GROUP, EXCEL_SHEET_TITLE_NPN,
                EXCEL_SHEET_TITLE_SOURCE_TYPE,
                EXCEL_SHEET_TITLE_DESCRIPTION_SOURCE,
                EXCEL_SHEET_TITLE_STATE_SOURCE,
                EXCEL_SHEET_TITLE_OFFICIALITY_SOURCE,
                EXCEL_SHEET_TITLE_STORAGE_PATH
            ]
        }

        self.txt_help_page.setHtml(self.help_strings.DLG_IMPORT_FROM_EXCEL)
        self.txt_help_page.anchorClicked.connect(self.save_template)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        #self.buttonBox.rejected.connect(self.rejected)
        self.buttonBox.helpRequested.connect(self.show_help)
        self.btn_browse_file.clicked.connect(
            make_file_selector(
                self.txt_excel_path,
                QCoreApplication.translate(
                    "ImportFromExcelDialog",
                    "Select the Excel file with data in the intermediate structure"
                ),
                QCoreApplication.translate("ImportFromExcelDialog",
                                           'Excel File (*.xlsx *.xls)')))
        self.buttonBox.button(QDialogButtonBox.Ok).setText(
            QCoreApplication.translate("ImportFromExcelDialog", "Import"))

        self.initialize_feedback()
        self.restore_settings()

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        # self.tabWidget.currentWidget().layout().setContentsMargins(0, 0, 0, 0)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def accepted(self):
        self.save_settings()
        self.import_from_excel()

    def import_from_excel(self):
        self._running_tool = True
        steps = 18
        step = 0
        self.progress.setVisible(True)
        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)

        # Where to store the reports?
        excel_path = self.txt_excel_path.text()

        if not excel_path:
            self.show_message(
                QCoreApplication.translate(
                    "ImportFromExcelDialog",
                    "You need to select an Excel file before continuing with the import."
                ), Qgis.Warning)
            self.progress.setVisible(False)
            self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True)
            return

        if not os.path.exists(excel_path):
            self.show_message(
                QCoreApplication.translate(
                    "ImportFromExcelDialog",
                    "The specified Excel file does not exist!"), Qgis.Warning)
            self.progress.setVisible(False)
            self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True)
            return

        self.progress.setVisible(True)
        self.txt_log.setText(
            QCoreApplication.translate(
                "ImportFromExcelDialog",
                "Loading tables from the Excel file..."))

        # Now that we have the Excel file, build vrts to load its sheets appropriately
        # Also validate each layer against a number of rules
        layer_parcel = self.check_layer_from_excel_sheet(
            excel_path, EXCEL_SHEET_NAME_PLOT)
        layer_party = self.check_layer_from_excel_sheet(
            excel_path, EXCEL_SHEET_NAME_PARTY)
        layer_group_party = self.check_layer_from_excel_sheet(
            excel_path, EXCEL_SHEET_NAME_GROUP)
        layer_right = self.check_layer_from_excel_sheet(
            excel_path, EXCEL_SHEET_NAME_RIGHT)

        if layer_parcel is None or layer_party is None or layer_group_party is None or layer_right is None:
            # A layer is None if at least an error was found
            self.group_parties_exists = False
            self.log_excel_show_message_emitted.emit(
                self.log_dialog_excel_text_content)
            self.done(0)
            return

        if not layer_group_party.isValid() or not layer_party.isValid(
        ) or not layer_parcel.isValid() or not layer_right.isValid():
            self.show_message(
                QCoreApplication.translate(
                    "ImportFromExcelDialog",
                    "One of the sheets of the Excel file couldn't be loaded! Check the format again."
                ), Qgis.Warning)
            self.progress.setVisible(False)
            self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True)
            return

        QgsProject.instance().addMapLayers(
            [layer_group_party, layer_party, layer_parcel, layer_right])

        # GET LADM LAYERS
        layers = {
            self.names.OP_PARTY_T: {
                'name': self.names.OP_PARTY_T,
                'geometry': None,
                LAYER: None
            },
            self.names.OP_PARCEL_T: {
                'name': self.names.OP_PARCEL_T,
                'geometry': None,
                LAYER: None
            },
            self.names.OP_RIGHT_T: {
                'name': self.names.OP_RIGHT_T,
                'geometry': None,
                LAYER: None
            },
            self.names.EXT_ARCHIVE_S: {
                'name': self.names.EXT_ARCHIVE_S,
                'geometry': None,
                LAYER: None
            },
            self.names.COL_RRR_SOURCE_T: {
                'name': self.names.COL_RRR_SOURCE_T,
                'geometry': None,
                LAYER: None
            },
            self.names.OP_GROUP_PARTY_T: {
                'name': self.names.OP_GROUP_PARTY_T,
                'geometry': None,
                LAYER: None
            },
            self.names.MEMBERS_T: {
                'name': self.names.MEMBERS_T,
                'geometry': None,
                LAYER: None
            },
            self.names.OP_ADMINISTRATIVE_SOURCE_T: {
                'name': self.names.OP_ADMINISTRATIVE_SOURCE_T,
                'geometry': None,
                LAYER: None
            }
        }

        self.qgis_utils.get_layers(self._db, layers, load=True)
        if not layers:
            return None

        # Get feature counts to compare after the ETL and know how many records were imported to each ladm_col table
        ladm_tables = [
            layers[self.names.OP_PARCEL_T][LAYER],
            layers[self.names.OP_PARTY_T][LAYER],
            layers[self.names.OP_RIGHT_T][LAYER],
            layers[self.names.OP_ADMINISTRATIVE_SOURCE_T][LAYER],
            layers[self.names.COL_RRR_SOURCE_T][LAYER],
            layers[self.names.OP_GROUP_PARTY_T][LAYER],
            layers[self.names.MEMBERS_T][LAYER]
        ]
        ladm_tables_feature_count_before = {
            t.name(): t.featureCount()
            for t in ladm_tables
        }

        # Run the ETL
        params = {
            'agrupacion':
            layers[self.names.OP_GROUP_PARTY_T][LAYER],
            'colmiembros':
            layers[self.names.MEMBERS_T][LAYER],
            'colrrrsourcet':
            layers[self.names.COL_RRR_SOURCE_T][LAYER],
            'extarchivo':
            layers[self.names.EXT_ARCHIVE_S][LAYER],
            'interesado':
            layers[self.names.OP_PARTY_T][LAYER],
            'layergroupparty':
            layer_group_party,
            'layerparcel':
            layer_parcel,
            'layerparty':
            layer_party,
            'layerright':
            layer_right,
            'opderecho':
            layers[self.names.OP_RIGHT_T][LAYER],
            'opfuenteadministrativatipo':
            layers[self.names.OP_ADMINISTRATIVE_SOURCE_T][LAYER],
            'parcel':
            layers[self.names.OP_PARCEL_T][LAYER]
        }

        self.qgis_utils.disable_automatic_fields(self._db,
                                                 self.names.OP_GROUP_PARTY_T)
        self.qgis_utils.disable_automatic_fields(self._db,
                                                 self.names.OP_RIGHT_T)
        self.qgis_utils.disable_automatic_fields(
            self._db, self.names.OP_ADMINISTRATIVE_SOURCE_T)

        processing.run("model:ETL_intermediate_structure",
                       params,
                       feedback=self.feedback)

        if not self.feedback.isCanceled():
            self.progress.setValue(100)
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.initialize_feedback()

        # Print summary getting feature count in involved LADM_COL tables...
        summary = """<html><head/><body><p>"""
        summary += QCoreApplication.translate("ImportFromExcelDialog",
                                              "Import done!!!<br/>")
        for table in ladm_tables:
            summary += QCoreApplication.translate(
                "ImportFromExcelDialog",
                "<br/><b>{count}</b> records loaded into table <b>{table}</b>"
            ).format(count=table.featureCount() -
                     ladm_tables_feature_count_before[table.name()],
                     table=table.name())

        summary += """</body></html>"""
        self.txt_log.setText(summary)
        self.logger.success_msg(
            __name__,
            QCoreApplication.translate(
                "QGISUtils",
                "Data successfully imported to LADM_COL from intermediate structure (Excel file: '{}')!!!"
            ).format(excel_path))
        self._running_tool = False

    def check_layer_from_excel_sheet(self, excel_path, sheetname):
        layer = self.get_layer_from_excel_sheet(excel_path, sheetname)
        error_counter = 0

        if layer is None and sheetname != EXCEL_SHEET_NAME_GROUP:  # optional sheet
            self.generate_message_excel_error(
                QCoreApplication.translate(
                    "ImportFromExcelDialog",
                    "The {} sheet has not information or has another name.").
                format(sheetname))
            error_counter += 1
        else:
            title_validator = layer.fields().toList()

        if sheetname == EXCEL_SHEET_NAME_PLOT and layer is not None:
            if not title_validator:
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The title does not match the format in the sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"numero predial nuevo" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column numero predial nuevo has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if not self.check_field_numeric_layer(layer, 'departamento'):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column departamento has non-numeric values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if not self.check_field_numeric_layer(layer, 'municipio'):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column municipio has non-numeric values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if not self.check_field_numeric_layer(layer,
                                                  'numero predial nuevo'):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column numero predial nuevo has non-numeric values in sheet {}."
                    ).format(sheetname))
                error_counter += 1

        if sheetname == EXCEL_SHEET_NAME_PARTY and layer is not None:
            if not title_validator:
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The title does not match the format in sheet {}.").
                    format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"tipo documento" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column tipo documento has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"numero de documento" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column numero de documento has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if not self.check_length_attribute_value(
                    layer, 'numero de documento', 12):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column numero de documento has more characters than expected in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"tipo persona" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column tipo persona has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1

        if sheetname == EXCEL_SHEET_NAME_GROUP and layer is not None:
            if not title_validator:
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The title does not match the format in the sheet {}."
                    ).format(sheetname))
                error_counter += 1
            self.group_parties_exists = True
            if list(layer.getFeatures('"numero predial nuevo" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column numero predial nuevo has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"tipo documento" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column tipo documento has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"numero de documento" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column numero de documento has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"id agrupación" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column id agrupación has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if not self.check_length_attribute_value(
                    layer, 'numero de documento', 12):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column numero de documento has more characters of the permitted in sheet {}."
                    ).format(sheetname))
                error_counter += 1

        if sheetname == EXCEL_SHEET_NAME_RIGHT and layer is not None:
            if not title_validator:
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The title does not match the format in sheet {}.").
                    format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"tipo" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column tipo has empty values in sheet {}.").
                    format(sheetname))
                error_counter += 1
            if list(layer.getFeatures('"tipo de fuente" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column tipo de fuente has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            if list(
                    layer.getFeatures(
                        '"estado_disponibilidad de la fuente" is Null')):
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "The column estado_disponibilidad de la fuente has empty values in sheet {}."
                    ).format(sheetname))
                error_counter += 1
            #if list(layer.getFeatures('"Ruta de Almacenamiento de la fuente" is Null')):
            #    self.generate_message_excel_error(QCoreApplication.translate("ImportFromExcelDialog",
            #            "The column Ruta de Almacenamiento de la fuente has empty values in sheet {}.").format(sheetname))
            #    error_counter += 1
            if len(
                    list(
                        layer.getFeatures(
                            '"número documento Interesado" is Null'))) + len(
                                list(layer.getFeatures('"agrupación" is Null'))
                            ) != layer.featureCount():
                self.generate_message_excel_error(
                    QCoreApplication.translate(
                        "ImportFromExcelDialog",
                        "Number of non-null parties plus number of non-null group parties is not equal to number of records in sheet {}. There might be rights without party or group party associated."
                    ).format(sheetname))
                error_counter += 1
            if not self.group_parties_exists:
                if list(
                        layer.getFeatures(
                            '"número documento Interesado" is Null')):
                    self.generate_message_excel_error(
                        QCoreApplication.translate(
                            "ImportFromExcelDialog",
                            "The column número documento Interesado has empty values in sheet {}."
                        ).format(sheetname))
                    error_counter += 1
                if len(list(layer.getFeatures(
                        '"agrupacion" is Null'))) != layer.featureCount():
                    self.generate_message_excel_error(
                        QCoreApplication.translate(
                            "ImportFromExcelDialog",
                            "The column agrupacion has data but the sheet does not exist in sheet {}."
                        ).format(sheetname))
                    error_counter += 1

        return layer if error_counter == 0 else None

    def check_field_numeric_layer(self, layer, name):
        id_field_idx = layer.fields().indexFromName(name)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        features = layer.getFeatures(request)
        is_numeric = True

        for feature in features:
            try:
                int(feature[name])
            except:
                is_numeric = False
                break

        return is_numeric

    def check_length_attribute_value(self, layer, name, size):
        id_field_idx = layer.fields().indexFromName(name)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        features = layer.getFeatures(request)
        right_length = True

        for feature in features:
            if len(str(feature[name])) > size:
                right_length = False
                break

        return right_length

    def generate_message_excel_error(self, msg):
        self.log_dialog_excel_text_content += "{}{}{}{}{}{}".format(
            LOG_QUALITY_LIST_CONTAINER_OPEN, LOG_QUALITY_LIST_ITEM_ERROR_OPEN,
            msg, LOG_QUALITY_LIST_ITEM_ERROR_CLOSE,
            LOG_QUALITY_LIST_CONTAINER_CLOSE, LOG_QUALITY_CONTENT_SEPARATOR)

    def get_layer_from_excel_sheet(self, excel_path, sheetname):
        basename = os.path.basename(excel_path)
        filename = os.path.splitext(basename)[0]
        dirname = os.path.dirname(excel_path)

        header_in_first_row, count = self.get_excel_info(excel_path, sheetname)
        if header_in_first_row is None and count is None:
            return None

        layer_definition = "<SrcLayer>{sheetname}</SrcLayer>".format(
            sheetname=sheetname)
        if header_in_first_row:
            layer_definition = """<SrcSql dialect="sqlite">SELECT * FROM '{sheetname}' LIMIT {count} OFFSET 1</SrcSql>""".format(
                sheetname=sheetname, count=count)
        xml_text_group_party = """<?xml version="1.0" encoding="UTF-8"?>
                    <OGRVRTDataSource>
                        <OGRVRTLayer name="{filename}-{sheetname}">
                            <SrcDataSource relativeToVRT="1">{basename}</SrcDataSource>
                            <!--Header={header}-->
                            {layer_definition}
                            {fields}
                        </OGRVRTLayer>            
                    </OGRVRTDataSource>
                """.format(filename=filename,
                           basename=basename,
                           header=header_in_first_row,
                           layer_definition=layer_definition,
                           sheetname=sheetname,
                           fields=self.get_vrt_fields(sheetname,
                                                      header_in_first_row))

        group_party_file_path = os.path.join(
            dirname, '{}.{}.vrt'.format(basename, sheetname))
        with open(group_party_file_path, 'w') as sheet:
            sheet.write(xml_text_group_party)

        uri = '{vrtfilepath}|layername={filename}-{sheetname}'.format(
            vrtfilepath=group_party_file_path,
            sheetname=sheetname,
            filename=filename)

        self.logger.info(__name__,
                         "Loading layer from excel with uri='{}'".format(uri))
        layer = QgsVectorLayer(uri, '{}-{}'.format('excel', sheetname), 'ogr')
        layer.setProviderEncoding('UTF-8')
        return layer

    def get_excel_info(self, path, sheetname):
        data_source = ogr.Open(path, 0)
        layer = data_source.GetLayerByName(sheetname)

        if layer is None:
            # A sheetname couldn't be found
            return None, None

        feature = layer.GetNextFeature()

        # If ogr recognizes the header, the first row will contain data, otherwise it'll contain field names
        header_in_first_row = True
        for field in self.fields[sheetname]:
            if feature.GetField(self.fields[sheetname].index(field)) != field:
                header_in_first_row = False

        num_rows = layer.GetFeatureCount()
        return header_in_first_row, num_rows - 1 if header_in_first_row else num_rows

    def get_vrt_fields(self, sheetname, header_in_first_row):
        vrt_fields = ""
        for index, field in enumerate(self.fields[sheetname]):
            vrt_fields += """<Field name="{field}" src="{src}" type="String"/>\n""".format(
                field=field,
                src='Field{}'.format(index +
                                     1) if header_in_first_row else field)

        return vrt_fields.strip()

    def save_template(self, url):
        link = url.url()
        if link == '#template':
            self.download_excel_file('plantilla_estructura_excel.xlsx')
        elif link == '#data':
            self.download_excel_file('datos_estructura_excel.xlsx')

    def download_excel_file(self, filename):
        settings = QSettings()

        new_filename, filter = QFileDialog.getSaveFileName(
            self,
            QCoreApplication.translate("ImportFromExcelDialog", "Save File"),
            os.path.join(
                settings.value(
                    'Asistente-LADM_COL/import_from_excel_dialog/template_save_path',
                    '.'), filename),
            QCoreApplication.translate("ImportFromExcelDialog",
                                       "Excel File (*.xlsx *.xls)"))

        if new_filename:
            settings.setValue(
                'Asistente-LADM_COL/import_from_excel_dialog/template_save_path',
                os.path.dirname(new_filename))
            template_file = QFile(":/Asistente-LADM_COL/resources/excel/" +
                                  filename)

            if not template_file.exists():
                self.logger.critical(
                    __name__,
                    "Excel doesn't exist! Probably due to a missing 'make' execution to generate resources..."
                )
                msg = QCoreApplication.translate(
                    "ImportFromExcelDialog",
                    "Excel file not found. Update your plugin. For details see log."
                )
                self.show_message(msg, Qgis.Warning)
                return

            if os.path.isfile(new_filename):
                self.logger.info(
                    __name__,
                    'Removing existing file {}...'.format(new_filename))
                os.chmod(new_filename, 0o777)
                os.remove(new_filename)

            if template_file.copy(new_filename):
                os.chmod(
                    new_filename,
                    stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
                msg = QCoreApplication.translate(
                    "ImportFromExcelDialog",
                    """The file <a href="file:///{}">{}</a> was successfully saved!"""
                ).format(normalize_local_url(new_filename),
                         os.path.basename(new_filename))
                self.show_message(msg, Qgis.Info)
            else:
                self.logger.info(
                    __name__,
                    'There was an error copying the CSV file {}!'.format(
                        new_filename))
                msg = QCoreApplication.translate(
                    "ImportFromExcelDialog", "The file couldn\'t be saved.")
                self.show_message(msg, Qgis.Warning)

    def reject(self):
        self.selected_items_dict = dict()

        if self._running_tool:
            reply = QMessageBox.question(
                self, QCoreApplication.translate("import_from_excel",
                                                 "Warning"),
                QCoreApplication.translate(
                    "import_from_excel",
                    "The '{}' tool is still running. Do you want to cancel it? If you cancel, the data might be incomplete in the target database."
                ).format(self.tool_name), QMessageBox.Yes, QMessageBox.No)

            if reply == QMessageBox.Yes:
                self.feedback.cancel()
                self._running_tool = False
                msg = QCoreApplication.translate(
                    "import_from_excel",
                    "The '{}' tool was cancelled.").format(self.tool_name)
                self.logger.info(__name__, msg)
                self.show_message(msg, Qgis.Info)
        else:
            self.logger.info(__name__, "Dialog closed.")
            self.done(1)

    def save_settings(self):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM_COL/import_from_excel_dialog/excel_path',
            self.txt_excel_path.text())

    def restore_settings(self):
        settings = QSettings()
        self.txt_excel_path.setText(
            settings.value(
                'Asistente-LADM_COL/import_from_excel_dialog/excel_path', ''))

    def show_message(self, message, level):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, 10)

    def show_help(self):
        self.qgis_utils.show_help("import_from_excel")

    def progress_changed(self):
        QCoreApplication.processEvents()  # Listen to cancel from the user
        self.progress.setValue(self.feedback.progress())

    def initialize_feedback(self):
        self.progress.setValue(0)
        self.progress.setVisible(False)
        self.feedback = QgsProcessingFeedback()
        self.feedback.progressChanged.connect(self.progress_changed)
        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True)
Exemple #37
0
class AlgorithmDialog(AlgorithmDialogBase):
    def __init__(self, alg):
        AlgorithmDialogBase.__init__(self, alg)

        self.alg = alg

        self.setMainWidget(alg.getParametersPanel(self))

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().insertWidget(0, self.bar)

        self.cornerWidget = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 5)
        self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }")
        self.runAsBatchButton = QPushButton(self.tr("Run as batch process..."))
        self.runAsBatchButton.clicked.connect(self.runAsBatch)
        layout.addWidget(self.runAsBatchButton)
        self.cornerWidget.setLayout(layout)
        self.tabWidget.setCornerWidget(self.cornerWidget)

    def runAsBatch(self):
        self.close()
        dlg = BatchAlgorithmDialog(self.alg)
        dlg.show()
        dlg.exec_()

    def setParamValues(self):
        params = self.alg.parameters
        outputs = self.alg.outputs

        for param in params:
            if param.hidden:
                continue
            wrapper = self.mainWidget.wrappers[param.name]
            if not self.setParamValue(param, wrapper):
                raise AlgorithmDialogBase.InvalidParameterValue(
                    param, wrapper.widget)

        for output in outputs:
            if output.hidden:
                continue
            output.value = self.mainWidget.outputWidgets[
                output.name].getValue()
            if isinstance(output, (OutputRaster, OutputVector, OutputTable)):
                output.open = self.mainWidget.checkBoxes[
                    output.name].isChecked()

        return True

    def setParamValue(self, param, wrapper):
        if wrapper.widget:
            return param.setValue(wrapper.value())
        else:
            return True

    def checkExtentCRS(self):
        unmatchingCRS = False
        hasExtent = False
        projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
        layers = dataobjects.getAllLayers()
        for param in self.alg.parameters:
            if isinstance(
                    param,
                (ParameterRaster, ParameterVector, ParameterMultipleInput)):
                if param.value:
                    if isinstance(param, ParameterMultipleInput):
                        inputlayers = param.value.split(';')
                    else:
                        inputlayers = [param.value]
                    for inputlayer in inputlayers:
                        for layer in layers:
                            if layer.source() == inputlayer:
                                if layer.crs() != projectCRS:
                                    unmatchingCRS = True

                        p = dataobjects.getObjectFromUri(inputlayer)
                        if p is not None:
                            if p.crs() != projectCRS:
                                unmatchingCRS = True
            if isinstance(param, ParameterExtent):
                if param.skip_crs_check:
                    continue

                value = self.mainWidget.wrappers[
                    param.name].widget.leText.text().strip()
                if value:
                    hasExtent = True

        return hasExtent and unmatchingCRS

    def accept(self):
        self.settings.setValue("/Processing/dialogBase", self.saveGeometry())

        checkCRS = ProcessingConfig.getSetting(
            ProcessingConfig.WARN_UNMATCHING_CRS)
        try:
            self.setParamValues()
            if checkCRS and not self.alg.checkInputCRS():
                reply = QMessageBox.question(
                    self, self.tr("Unmatching CRS's"),
                    self.tr('Layers do not all use the same CRS. This can '
                            'cause unexpected results.\nDo you want to '
                            'continue?'), QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            checkExtentCRS = ProcessingConfig.getSetting(
                ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS)
            if checkExtentCRS and self.checkExtentCRS():
                reply = QMessageBox.question(
                    self, self.tr("Extent CRS"),
                    self.
                    tr('Extent parameters must use the same CRS as the input layers.\n'
                       'Your input layers do not have the same extent as the project, '
                       'so the extent might be in a wrong CRS if you have selected it from the canvas.\n'
                       'Do you want to continue?'),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if reply == QMessageBox.No:
                    return
            msg = self.alg._checkParameterValuesBeforeExecuting()
            if msg:
                QMessageBox.warning(self,
                                    self.tr('Unable to execute algorithm'),
                                    msg)
                return
            self.btnRun.setEnabled(False)
            self.btnClose.setEnabled(False)
            buttons = self.mainWidget.iterateButtons
            self.iterateParam = None

            for i in range(len(list(buttons.values()))):
                button = list(buttons.values())[i]
                if button.isChecked():
                    self.iterateParam = list(buttons.keys())[i]
                    break

            self.progressBar.setMaximum(0)
            self.lblProgress.setText(self.tr('Processing algorithm...'))
            # Make sure the Log tab is visible before executing the algorithm
            try:
                self.tabWidget.setCurrentIndex(1)
                self.repaint()
            except:
                pass

            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

            self.setInfo(
                self.tr('<b>Algorithm %s starting...</b>') % self.alg.name)

            if self.iterateParam:
                if runalgIterating(self.alg, self.iterateParam, self.feedback):
                    self.finish()
                else:
                    QApplication.restoreOverrideCursor()
                    self.resetGUI()
            else:
                command = self.alg.getAsCommand()
                if command:
                    ProcessingLog.addToLog(ProcessingLog.LOG_ALGORITHM,
                                           command)
                if runalg(self.alg, self.feedback):
                    self.finish()
                else:
                    QApplication.restoreOverrideCursor()
                    self.resetGUI()
        except AlgorithmDialogBase.InvalidParameterValue as e:
            try:
                self.buttonBox.accepted.connect(
                    lambda e=e: e.widget.setPalette(QPalette()))
                palette = e.widget.palette()
                palette.setColor(QPalette.Base, QColor(255, 255, 0))
                e.widget.setPalette(palette)
            except:
                pass
            self.bar.clearWidgets()
            self.bar.pushMessage("",
                                 "Wrong or missing parameter value: %s" %
                                 e.parameter.description,
                                 level=QgsMessageBar.WARNING,
                                 duration=5)

    def finish(self):
        keepOpen = ProcessingConfig.getSetting(
            ProcessingConfig.KEEP_DIALOG_OPEN)

        if self.iterateParam is None:
            if not handleAlgorithmResults(self.alg, self.feedback,
                                          not keepOpen):
                self.resetGUI()
                return

        self.executed = True
        self.setInfo('Algorithm %s finished' % self.alg.name)
        QApplication.restoreOverrideCursor()

        if not keepOpen:
            self.close()
        else:
            self.resetGUI()
            if self.alg.getHTMLOutputsCount() > 0:
                self.setInfo(
                    self.tr('HTML output has been generated by this algorithm.'
                            '\nOpen the results dialog to check it.'))

    def closeEvent(self, evt):
        QgsProject.instance().layerWasAdded.disconnect(
            self.mainWidget.layerRegistryChanged)
        QgsProject.instance().layersWillBeRemoved.disconnect(
            self.mainWidget.layerRegistryChanged)
        super(AlgorithmDialog, self).closeEvent(evt)
class geopunt4QgisAdresDialog(QtGui.QDialog):
    def __init__(self, iface):
        QtGui.QDialog.__init__(self, None)
        self.setWindowFlags( self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint )
        #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
        self.iface = iface
    
        # initialize locale
        locale = QtCore.QSettings().value("locale/userLocale", "nl")
        if not locale: locale == 'nl' 
        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 = QtCore.QTranslator()
            self.translator.load(localePath)
            if QtCore.qVersion() > '4.3.3': QtCore.QCoreApplication.installTranslator(self.translator)
    
        self._initGui()

    def _initGui(self):
        """setup the user interface"""
        self.ui = Ui_geopunt4Qgis()
        self.ui.setupUi(self)
        
        #get settings
        self.s = QtCore.QSettings()
        self.loadSettings()
        
        #setup geometryHelper object
        self.gh = gh.geometryHelper(self.iface)
        
        #create graphicsLayer
        self.graphicsLayer = []

        #populate gemeenteBox
        gemeentes = json.load( open(os.path.join(os.path.dirname(__file__),
                  "data/gemeentenVL.json")) )
        gemeenteNamen =  [str( n["Naam"]) for n in gemeentes]
        self.ui.gemeenteBox.addItems( gemeenteNamen )
        self.ui.gemeenteBox.setEditText(QtCore.QCoreApplication.translate(
                  "geopunt4QgisAdresDialog", "gemeente"))
        self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}')
        self.ui.gemeenteBox.setFocus()
        
        self.completer = QtGui.QCompleter( self )
        self.completerModel = QtGui.QStringListModel( self )
        self.ui.gemeenteBox.setCompleter( self.completer )
        self.completer.setModel( self.completerModel )
        self.completer.setCaseSensitivity(False)
        self.completerModel.setStringList( gemeenteNamen )
        
        #setup a message bar
        self.bar = QgsMessageBar() 
        self.bar.setSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed )
        self.ui.verticalLayout.addWidget(self.bar)
        
        self.ui.buttonBox.addButton( QtGui.QPushButton("Sluiten"), QtGui.QDialogButtonBox.RejectRole )
        for btn in self.ui.buttonBox.buttons():
            btn.setAutoDefault(0)
            
        #event handlers 
        if self.adresSearchOnEnter:
          self.ui.zoekText.returnPressed.connect(self.onZoekActivated)
        else:
          self.ui.zoekText.textEdited.connect(self.onZoekActivated)
        self.ui.gemeenteBox.currentIndexChanged.connect(self.onZoekActivated)
        self.ui.resultLijst.itemDoubleClicked.connect(self.onItemActivated)
        self.ui.resultLijst.itemClicked.connect(self.onItemClick)
        self.ui.ZoomKnop.clicked.connect(self.onZoomKnopClick)
        self.ui.Add2mapKnop.clicked.connect(self.onAdd2mapKnopClick)
        self.ui.buttonBox.helpRequested.connect(self.openHelp)
        self.finished.connect(self.clean )
        
    def loadSettings(self):
        self.saveToFile = int( self.s.value("geopunt4qgis/adresSavetoFile" , 1))
        layerName =  self.s.value("geopunt4qgis/adreslayerText", "")
        if layerName :
           self.layerName= layerName
        self.adresSearchOnEnter = int( self.s.value("geopunt4qgis/adresSearchOnEnter" , 0))
        self.timeout =  int(  self.s.value("geopunt4qgis/timeout" ,15))
        if int( self.s.value("geopunt4qgis/useProxy" , 0)):
            self.proxy = self.s.value("geopunt4qgis/proxyHost" ,"")
            self.port = self.s.value("geopunt4qgis/proxyPort" ,"")
        else:
            self.proxy = ""
            self.port = ""
        self.startDir = self.s.value("geopunt4qgis/startDir", os.path.dirname(__file__))
        self.gp = geopunt.Adres(self.timeout, self.proxy, self.port)
        
    def openHelp(self):
        webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/zoek-een-adres")
    
    def onZoekActivated(self):
        self._clearGraphicsLayer()
        self.bar.clearWidgets()
    
        gemeente = self.ui.gemeenteBox.currentText() 
        if gemeente <> QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","gemeente"):
          self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #000000}')
    
          txt = self.ui.zoekText.text() +", "+ gemeente
    
          suggesties = self.gp.fetchSuggestion( txt , 25 )
      
          self.ui.resultLijst.clear()
          if type( suggesties ) is list and len(suggesties) <> 0:
            self.ui.resultLijst.addItems(suggesties)
            if len(suggesties) == 1:
              self.ui.resultLijst.setCurrentRow(0)
          elif type( suggesties ) is str:
            self.bar.pushMessage(
              QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","Waarschuwing"),
              suggesties, level=QgsMessageBar.WARNING)
      
    def onItemActivated( self, item):
        txt = item.text()
        self._zoomLoc(txt)
    
    def onItemClick(self, item):
        txt = item.text()
        streetNr = txt.split(",")[:-1]
        self.ui.zoekText.setText( ",".join(streetNr)  )
    
    def onZoomKnopClick(self):
        item = self.ui.resultLijst.currentItem()
        if item:
          self._zoomLoc(item.text())

    def onAdd2mapKnopClick(self):
        item = self.ui.resultLijst.currentItem()
        if item:
          self._addToMap(item.text())
      
    def _clearGraphicsLayer(self):
        for graphic in  self.graphicsLayer: 
          self.iface.mapCanvas().scene().removeItem(graphic)
        self.graphicsLayer = []
    
    def _zoomLoc(self, txt):
        self._clearGraphicsLayer()
        locations = self.gp.fetchLocation(txt)
        if type( locations ) is list and len(locations):
            loc = locations[0]
        
            LowerLeftX = loc['BoundingBox']['LowerLeft']['X_Lambert72']
            LowerLeftY = loc['BoundingBox']['LowerLeft']['Y_Lambert72']
            UpperRightX = loc['BoundingBox']['UpperRight']['X_Lambert72']
            UpperRightY = loc['BoundingBox']['UpperRight']['Y_Lambert72']
        
            self.gh.zoomtoRec(QgsPoint(LowerLeftX,LowerLeftY),QgsPoint(UpperRightX, UpperRightY), 31370)
        
            xlb, ylb = loc["Location"]["X_Lambert72"], loc["Location"]["Y_Lambert72"]
            x, y = self.gh.prjPtToMapCrs(QgsPoint( xlb , ylb), 31370)
        
            m = QgsVertexMarker(self.iface.mapCanvas())
            self.graphicsLayer.append(m)
            m.setCenter(QgsPoint(x,y))
            m.setColor(QtGui.QColor(255,255,0))
            m.setIconSize(1)
            m.setIconType(QgsVertexMarker.ICON_BOX) 
            m.setPenWidth(9)
        
        elif type( locations ) is str:
          self.bar.pushMessage(
            QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","Waarschuwing"), 
                locations, level=QgsMessageBar.WARNING, duration=3)
        else:
          self.bar.pushMessage("Error", 
            QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","onbekende fout"),
                level=QgsMessageBar.CRITICAL, duration=3)
        
    def _addToMap(self, txt):
        if not self.layernameValid(): return
        locations = self.gp.fetchLocation(txt)
        if type( locations ) is list and len(locations):
            loc = locations[0]
            x, y = loc["Location"]["X_Lambert72"], loc["Location"]["Y_Lambert72"]
            adres = loc["FormattedAddress"]
            LocationType = loc["LocationType"]
        
            pt = self.gh.prjPtToMapCrs(QgsPoint( x, y), 31370)
        
            self.gh.save_adres_point( pt, adres, typeAddress=LocationType, 
              layername=self.layerName, saveToFile=self.saveToFile, sender=self, 
              startFolder= os.path.join(self.startDir, self.layerName))
        
        elif type( locations ) is str:
          self.bar.pushMessage(
            QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","Waarschuwing"), 
                locations, level=QgsMessageBar.WARNING)	
        else:
          self.bar.pushMessage("Error", 
            QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","onbekende fout"),
                level=QgsMessageBar.CRITICAL)
      
    def layernameValid(self):   
        if not hasattr(self, 'layerName'):
          layerName, accept = QtGui.QInputDialog.getText(None,
              QtCore.QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'),
              QtCore.QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') )
          if accept == False: 
             return False
          else: 
             self.layerName = layerName
        return True
      
    def clean(self):
        self.bar.clearWidgets()
        self.ui.resultLijst.clear()
        self.ui.zoekText.setText("")
        self.ui.gemeenteBox.setEditText(QtCore.QCoreApplication.translate(
                "geopunt4QgisAdresDialog","gemeente"))
        self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}')
        self._clearGraphicsLayer()