Esempio n. 1
0
class AboutBox(QDialog):
    """
    About box of the plugin
    """
    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        mainLayout = QVBoxLayout()

        logo_file_path = PluginGlobals.instance().logo_file_path
        self.logo = QLabel()
        self.logo.setPixmap(QPixmap(logo_file_path))
        mainLayout.addWidget(self.logo)

        title = u"À propos de l'extension GéoGrandEst…"
        description = u"""Extension pour QGIS donnant un accès simplifié aux ressources géographiques utiles aux 
partenaires de GéoGrandEst
Version {0}
Plus d'informations à l'adresse suivante :
{1}
        """.format(PluginGlobals.instance().PLUGIN_VERSION,
                   PluginGlobals.instance().PLUGIN_SOURCE_REPOSITORY)

        self.textArea = QTextEdit()
        self.textArea.setReadOnly(True)
        self.textArea.setText(description)
        self.textArea.setFrameShape(QFrame.NoFrame)
        mainLayout.addWidget(self.textArea)

        self.setModal(True)
        self.setSizeGripEnabled(False)

        self.setLayout(mainLayout)

        self.setFixedSize(400, 250)
        self.setWindowTitle(title)
Esempio n. 2
0
class ConsoleWidget(QWidget):
    def __init__(self, exc_info, parent=None):
        QWidget.__init__(self, parent)

        self.compiler = code.CommandCompiler()  # for console

        self.tb = exc_info[2]
        self.entries = traceback.extract_tb(self.tb)

        self.console = QLineEdit()
        self.console.setPlaceholderText(">>> Python Console")
        self.console.returnPressed.connect(self.exec_console)
        self.console.setFont(QFont("Courier"))

        self.console_out = QTextEdit()
        self.console_out.setReadOnly(True)
        self.console_out.setFont(QFont("Courier"))
        self.console_out.setVisible(False)  # initially hidden

        self.console_outs = [''] * len(self.entries)

        self.frame_vars = [None] * len(self.entries)

        l = QVBoxLayout()
        l.addWidget(self.console_out)
        l.addWidget(self.console)
        l.setContentsMargins(0, 0, 0, 0)
        self.setLayout(l)

    def go_to_frame(self, index):
        self.console_out.setPlainText(self.console_outs[index])
        self.current_frame_index = index

    def exec_console(self):

        index = self.current_frame_index
        if index < 0: return

        # cache frame variables (globals and locals)
        # because every time we ask for frame.f_locals, a new dict instance
        # is created - we keep our local cache that may contain some changes
        if self.frame_vars[index] is None:
            #print "init", index
            frame = frame_from_traceback(self.tb, index)
            self.frame_vars[index] = (dict(frame.f_globals),
                                      dict(frame.f_locals))

        frame_vars = self.frame_vars[index]
        #print frame_vars[1]

        line = self.console.text()
        try:
            c = self.compiler(line, "<console>", "single")
        except (OverflowError, SyntaxError, ValueError) as e:
            QMessageBox.critical(self, "Error", str(e))
            return

        if c is None:
            QMessageBox.critical(self, "Error", "Code not complete")
            return

        import io
        io = io.StringIO() if sys.version_info.major >= 3 else io.BytesIO()
        try:
            with stdout_redirected(io):
                exec(c, frame_vars[0], frame_vars[1])
        except:
            etype, value, tb = sys.exc_info()
            QMessageBox.critical(self, "Error",
                                 etype.__name__ + "\n" + str(value))
            return

        stuff = self.console_outs[index]
        stuff += ">>> " + line + "\n"
        stuff += io.getvalue()
        self.console_outs[index] = stuff

        self.console_out.setPlainText(stuff)
        self.console_out.setVisible(True)
        # make sure we are at the end
        c = self.console_out.textCursor()
        c.movePosition(QTextCursor.End)
        self.console_out.setTextCursor(c)
        self.console_out.ensureCursorVisible()

        self.console.setText('')
Esempio n. 3
0
class TreeSettingItem(QTreeWidgetItem):

    comboStyle = '''QComboBox {
                 border: 1px solid gray;
                 border-radius: 3px;
                 padding: 1px 18px 1px 3px;
                 min-width: 6em;
             }

             QComboBox::drop-down {
                 subcontrol-origin: padding;
                 subcontrol-position: top right;
                 width: 15px;
                 border-left-width: 1px;
                 border-left-color: darkgray;
                 border-left-style: solid;
                 border-top-right-radius: 3px;
                 border-bottom-right-radius: 3px;
             }
            '''

    def _addTextEdit(self, editable=True):
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.textEdit = QTextEdit()
        if not editable:
            self.textEdit.setReadOnly(True)
        self.textEdit.setPlainText(self._value)
        layout.addWidget(self.textEdit)
        w = QWidget()
        w.setLayout(layout)
        self.tree.setItemWidget(self, 1, w)

    def _addTextBoxWithLink(self, text, func, editable=True):
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.lineEdit = QLineEdit()
        if not editable:
            self.lineEdit.setReadOnly(True)
        self.lineEdit.setText(self._value)
        layout.addWidget(self.lineEdit)
        if text:
            self.linkLabel = QLabel()
            self.linkLabel.setText("<a href='#'> %s</a>" % text)
            self.linkLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            layout.addWidget(self.linkLabel)
            self.linkLabel.linkActivated.connect(func)
        w = QWidget()
        w.setLayout(layout)
        self.tree.setItemWidget(self, 1, w)

    def __init__(self, parent, tree, setting, namespace, value):
        QTreeWidgetItem.__init__(self, parent)
        self.parent = parent
        self.namespace = namespace
        self.tree = tree
        self._value = value
        self.setting = setting
        self.name = setting["name"]
        self.labelText = setting["label"]
        self.settingType = setting["type"]
        self.setText(0, self.labelText)
        if self.settingType == CRS:
            def edit():
                selector = QgsProjectionSelectionDialog()
                selector.setCrs(value);
                if selector.exec_():
                    crs = selector.crs()
                    if crs.upper().startswith("EPSG:"):
                        self.lineEdit.setText(crs)
            self._addTextBoxWithLink("Edit", edit, False)
        elif self.settingType == FILES:
            def edit():
                f = QFileDialog.getOpenFileNames(parent.treeWidget(), "Select file", "", "*.*")
                if f:
                    self.lineEdit.setText(",".join(f))
            self._addTextBoxWithLink("Browse", edit, True)
        elif self.settingType == FILE:
            def edit():
                f = QFileDialog.getOpenFileName(parent.treeWidget(), "Select file", "", "*.*")
                if f:
                    self.lineEdit.setText(f)
            self._addTextBoxWithLink("Browse", edit, True)
        elif self.settingType == FOLDER:
            def edit():
                f = QFileDialog.getExistingDirectory(parent.treeWidget(), "Select folder", "")
                if f:
                    self.lineEdit.setText(f)
            self._addTextBoxWithLink("Browse", edit, True)
        elif self.settingType == BOOL:
            if value:
                self.setCheckState(1, Qt.Checked)
            else:
                self.setCheckState(1, Qt.Unchecked)
        elif self.settingType == CHOICE:
            self.combo = QComboBox()
            self.combo.setStyleSheet(self.comboStyle)
            for option in setting["options"]:
                self.combo.addItem(option)
            self.tree.setItemWidget(self, 1, self.combo)
            idx = self.combo.findText(str(value))
            self.combo.setCurrentIndex(idx)
        elif self.settingType == TEXT:
            self._addTextEdit()
        elif self.settingType == STRING:
            self._addTextBoxWithLink(None, None)
        elif self.settingType == AUTHCFG:
            def edit():
                currentAuthCfg = self.value()
                dlg = AuthConfigSelectDialog(parent.treeWidget(), authcfg=currentAuthCfg)
                ret = dlg.exec_()
                if ret:
                    self.lineEdit.setText(dlg.authcfg)
            self._addTextBoxWithLink("Select", edit, True)
        else:
            self.setFlags(self.flags() | Qt.ItemIsEditable)
            self.setText(1, unicode(value))

    def saveValue(self):
        value = self.value()
        setPluginSetting(self.name, value, self.namespace)

    def value(self):
        self.setBackgroundColor(0, Qt.white)
        self.setBackgroundColor(1, Qt.white)
        try:
            if self.settingType == BOOL:
                return self.checkState(1) == Qt.Checked
            elif self.settingType == NUMBER:
                v = float(self.text(1))
                return v
            elif self.settingType == CHOICE:
                return self.combo.currentText()
            elif self.settingType in [TEXT]:
                return self.textEdit.toPlainText()
            elif self.settingType in [CRS, STRING, FILES, FOLDER, AUTHCFG]:
                return self.lineEdit.text()
            else:
                return self.text(1)
        except:
            self.setBackgroundColor(0, Qt.yellow)
            self.setBackgroundColor(1, Qt.yellow)
            raise WrongValueException()

    def setValue(self, value):
        if self.settingType == BOOL:
            if value:
                self.setCheckState(1, Qt.Checked)
            else:
                self.setCheckState(1, Qt.Unchecked)
        elif self.settingType == CHOICE:
            idx = self.combo.findText(str(value))
            self.combo.setCurrentIndex(idx)
        elif self.settingType in [TEXT, CRS, STRING, FILES, FOLDER, AUTHCFG]:
            self.lineEdit.setText(value)
        else:
            self.setText(1, unicode(value))

    def resetDefault(self):
        self.setValue(self.setting["default"])
Esempio n. 4
0
class QadPreview(QWidget):
    def __init__(self,
                 plugIn,
                 parent,
                 size,
                 transparency,
                 windowFlags=Qt.Widget):
        self.plugIn = plugIn
        self.size = size
        self.transparency = transparency
        QWidget.__init__(self, parent, windowFlags)

        self.edit1 = QTextEdit(self)
        self.edit1.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.edit1.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.edit1.insertPlainText("12.3456")
        self.edit1.setReadOnly(True)

        self.edit2 = QTextEdit(self)
        self.edit2.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.edit2.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.edit2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.edit2.insertPlainText("78.9012")
        self.edit2.setReadOnly(True)

    def refresh(self, size, transparency):
        self.size = size
        self.transparency = transparency
        self.update()  # forzo il disegno del preview

    def paintEvent(self, event):
        self.paint_preview()


    def setEdit(self, editWidget, foregroundColor, backGroundColor, borderColor, \
                selectionColor, selectionBackGroundColor, opacity):
        # se i colori sono None allora non vengono alterati
        # caso particolare per borderColor = "" non viene disegnato
        # opacity = 0-100
        oldFmt = self.styleSheet().split(";")
        fmt = "rgba({0},{1},{2},{3}%)"

        c = QColor(foregroundColor)
        rgbStrForeColor = "color: " + fmt.format(str(c.red()), str(
            c.green()), str(c.blue()), str(opacity)) + ";"

        c = QColor(backGroundColor)
        rgbStrBackColor = "background-color: " + fmt.format(
            str(c.red()), str(c.green()), str(c.blue()), str(opacity)) + ";"

        c = QColor(borderColor)
        rgbStrBorderColor = "border-color: " + fmt.format(
            str(c.red()), str(c.green()), str(c.blue()), str(opacity)) + ";"
        fmtBorder = "border:1px;border-style:solid;"

        c = QColor(selectionColor)
        rgbStrSelectionColor = "selection-color: " + fmt.format(
            str(c.red()), str(c.green()), str(c.blue()), str(opacity)) + ";"

        c = QColor(selectionBackGroundColor)
        rgbStrSelectionBackColor = "selection-background-color: " + fmt.format(
            str(c.red()), str(c.green()), str(c.blue()), str(opacity)) + ";"

        fontSize = 8 + self.size

        fmt = rgbStrForeColor + \
              rgbStrBackColor + \
              fmtBorder + \
              rgbStrBorderColor + \
              rgbStrSelectionColor + \
              rgbStrSelectionBackColor + \
              "font-size: " + str(fontSize) + "pt;"

        editWidget.setStyleSheet(fmt)

    def paint_preview(self):
        rect = self.rect()
        painter = QPainter(self)
        painter.fillRect(rect, self.plugIn.canvas.canvasColor())
        painter.setRenderHint(QPainter.Antialiasing)

        foregroundColor = QColor(
            QadVariables.get(
                QadMsg.translate("Environment variables", "DYNEDITFORECOLOR")))
        backGroundColor = QColor(
            QadVariables.get(
                QadMsg.translate("Environment variables", "DYNEDITBACKCOLOR")))
        borderColor = QColor(
            QadVariables.get(
                QadMsg.translate("Environment variables",
                                 "DYNEDITBORDERCOLOR")))
        opacity = 100 - self.transparency
        font_size = 8 + self.size
        height = font_size + 15

        selectionColor = QColor(Qt.white)
        selectionBackGroundColor = QColor(51, 153,
                                          255)  # azzurro (R=51 G=153 B=255)
        self.setEdit(self.edit1, foregroundColor, backGroundColor, borderColor,
                     selectionColor, selectionBackGroundColor, opacity)
        fm = QFontMetrics(self.edit1.currentFont())
        width1 = fm.width(self.edit1.toPlainText() + "__") + 2

        self.edit1.resize(width1, height)
        self.edit1.selectAll()  # seleziono tutto il testo

        self.setEdit(self.edit2, foregroundColor, backGroundColor, borderColor,
                     backGroundColor, foregroundColor, opacity)
        fm = QFontMetrics(self.edit2.currentFont())
        width2 = fm.width(self.edit2.toPlainText() + "__") + 2
        self.edit2.resize(width2, height)

        offset = height / 3
        x = (rect.width() - (width1 + offset + width2)) / 2
        y = (rect.height() - height) / 2
        self.edit1.move(x, y)
        self.edit2.move(x + width1 + offset, y)
Esempio n. 5
0
class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(
            QtCore.QSize(QtCore.QRect(0, 0, 440, 660).size()).expandedTo(
                Dialog.minimumSizeHint()))

        self.gridlayout = QGridLayout(Dialog)
        self.gridlayout.setObjectName("gridlayout")

        font = QFont()
        font.setPointSize(15)
        font.setWeight(50)
        font.setBold(True)

        self.label_2 = QLabel(Dialog)
        self.label_2.setFont(font)
        self.label_2.setTextFormat(QtCore.Qt.RichText)
        self.label_2.setObjectName("label_2")
        self.gridlayout.addWidget(self.label_2, 1, 1, 1, 2)

        self.textEdit = QTextEdit(Dialog)

        palette = QPalette()

        brush = QBrush(QColor(0, 0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QPalette.Active, QPalette.Base, brush)

        brush = QBrush(QColor(0, 0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QPalette.Inactive, QPalette.Base, brush)

        brush = QBrush(QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QPalette.Disabled, QPalette.Base, brush)
        self.textEdit.setPalette(palette)
        self.textEdit.setAutoFillBackground(True)
        self.textEdit.width = 320
        self.textEdit.height = 360
        self.textEdit.setFrameShape(QFrame.NoFrame)
        self.textEdit.setFrameShadow(QFrame.Plain)
        self.textEdit.setReadOnly(True)
        self.textEdit.setObjectName("textEdit")
        self.textEdit.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)

        self.gridlayout.addWidget(self.textEdit, 2, 1, 5, 2)

        self.pushButton = QPushButton(Dialog)
        self.pushButton.setObjectName("pushButton")
        self.gridlayout.addWidget(self.pushButton, 4, 2, 1, 1)

        spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.gridlayout.addItem(spacerItem, 3, 1, 1, 1)

        self.retranslateUi(Dialog)
        self.pushButton.clicked.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(
            QApplication.translate("Dialog", "GroupPointsWithinDistance",
                                   None))
        self.label_2.setText(
            QApplication.translate("Dialog", "GroupPointsWithinDistance 0.2",
                                   None))
        self.textEdit.setHtml(
            QApplication.translate(
                "Dialog",
                "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
                "p, li { white-space: pre-wrap; }\n"
                "</style></head><body style=\" font-family:\'Sans Serif\'; font-size:8pt; font-weight:400; font-style:normal;\">\n"
                "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'MS Shell Dlg 2\'; font-size:8pt;\"><span style=\" font-weight:600;\">"
                " GroupPointsWithinDistance 0.2 :</span>"
                "  A little QGIS plugin to find and group/aggregate points of a points' layer if they are at a given distance of one another.</b>\n"
                +
                "                                                                                                                                                                                           \n"
                +
                " <br><b>WARNING:</b><br><b> Work with projected datas only, in other words do not use geographical (long-lat type) reference systems !</b>\n "
                +
                "                                                                                                                                             \n"
                + "             \n" +
                "This plugin is meant to deal with points datas (not multipoints, convert first to points) (for instance faunistic or floristic observations), nearby from one another, that the user wants to be regrouped/aggregated as a station.\n"
                + "\n" +
                "                                                                                                                                                                                                                    \n"
                +
                "The user should appreciate and cheack which distance to use, and - may be - keep the distance chosen as small as possible in order to avoid '"
                'chains effect'
                "' and producing too widespread groups .\n" +
                "                                                                                                                                          \n"
                + "The plugin produces a layer of point called '"
                'input_layer_name_aggregated_with_Distance'
                "' with 3 mores attributes:                                                          \n"
                +
                "                                                                                                                                                                                                                    \n"
                +
                " - Point_id: the point id,                                                                                                                                                              \n"
                +
                "                                                                                                                                                                                                                    \n"
                + " - NumAggreg: the '"
                'aggregates id number'
                "'                                                                                                                                                              \n"
                +
                "                                                                                                                                                                                                                    \n"
                +
                " - Nb_Pts: the number of regrouped points,                                              \n "
                + "\n" +
                "                                                                                                                                                                                                                    \n"
                +
                " - List_Pts: the list of the points ids in the aggregate.                                                                                                                     \n"
                + "\n" +
                "                                                                                                                                          \n"
                +
                "                                                                                                                                                          \n"
                + "\n" +
                " The plugin also produce a line layer, with not all lines connecting points of a same aggregate but showing only the very one of the connections that were made to link the points as part of a same aggregate."
                " the plugin also produces a layer of polygons overlaping the regrouped points within distance with an attribute table with also NumAggreg,Nb_Pts,List_Pts attributes.    \n "
                + "\n" +
                "                                                                                                                                                                \n"
                +
                "  The polygons are buffers (D/100) of the convex hull polygons of the aggregated points \n "
                +
                "                                                                                                                                                          \n"
                + "\n" +
                "<br><b>WARNING 2:</b><br> The polygons are meant - as the lines - only to show the aggregates but they overlap often points that are not part of the aggregate. The lines can be used for that matter  \n"
                +
                "<br><b>In order to select point of an aggregate only consider the point table attribute !</b><br>"
                "                                                                                                                                                          \n"
                + "\n" +
                " <br><b><i>NOTA BENE: all rasters should be unchecked in layer panel or the plugin won't work !</i></b></br>"
                " This plugin is not a part of Qgis engine and any problems should be reported only to the author. </p></td></tr></table>"
                "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"></p>\n"
                "<p style=\"margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
                "                   "
                "<br><b>[email protected]</b><br>"
                "<br><br><i>code 0.2 (26 march 2019).</i></p></body></html>",
                None))
        self.pushButton.setText(QApplication.translate("Dialog", "OK", None))
Esempio n. 6
0
class ExploreMapWindow(QMainWindow):
    """This class offers a canvas and tools to preview and explore data 
        provided by Geocubes. Preview raster layers are fetched from the Geocubes
        cached WMTS server. The user can simply view the data or get legend info
        on a single point."""

    # the window is initiated with the Geocubes url base defined on the main plugin
    # this means that the base doesn't have to be manually changed here if it changes
    def __init__(self, url_base):
        QMainWindow.__init__(self)

        # creating map canvas, which draws the maplayers
        # setting up features like canvas color
        self.canvas = QgsMapCanvas()
        self.canvas.setMinimumSize(550, 700)
        self.canvas.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.canvas.setCanvasColor(Qt.white)
        #self.canvas.enableAntiAliasing(True)

        self.url_base = url_base

        # Qmainwindow requires a central widget. Canvas is placed
        self.setCentralWidget(self.canvas)
        """'tile widths' refer to the Map proxy WMTS server's settings for displaying
        data of different resolutions. If I understood correctly, these values 
        (got by examining properties of a layer from that server in QGIS) are
        the thresholds at which a different resolution is loaded on the GRIDI-FIN
        tileset. The values represent the tile size in map units (meters). 
        
        Each tile widths is tied to the corresponding resolution, which is used
        to get the correct resolution legend info. The method is only an estimation,
        but ought to produce good enough results for this purpose.
        Smallest resolutions (1, 2, 5) are omitted since only some layers 
        have them. """
        self.tile_widths = {
            2560: 10,
            5120: 20,
            12800: 50,
            25600: 100,
            51200: 200,
            128000: 500,
            256000: 1000
        }

        # get all keys i.e. widths
        self.all_widths = [i for i in self.tile_widths]

        # creating background layer box and housing it with the hardcoded options
        self.bg_layer_box = QComboBox()
        # if ortokuva ever updates to newer versions, just change the year here
        bg_layers = ['Taustakartta', 'Ortokuva_2018', 'No reference layer']

        # set 'No reference layer' as the default option
        self.bg_layer_box.addItems(layer for layer in bg_layers)
        self.bg_layer_box.setCurrentIndex(2)
        self.bg_layer_box.currentIndexChanged.connect(self.addBackgroundLayer)

        # initialize the slider that will control BG layer opacity/transparency
        self.opacity_slider = QSlider(Qt.Horizontal)
        self.opacity_slider.setMinimum(0)
        self.opacity_slider.setMaximum(100)
        self.opacity_slider.setSingleStep(1)
        self.opacity_slider.setMaximumWidth(100)
        self.opacity_slider.valueChanged.connect(self.setBackgroundMapOpacity)

        self.legend_checkbox = QCheckBox("Get attribute info on all layers")

        # explanatory texts for the different widgets are stored as label widgets
        bg_layer_label = QLabel(" Background: ")
        bg_opacity_label = QLabel(" BG opacity: ")
        data_label = QLabel("Data: ")
        spacing = QLabel(" ")

        # all of the data layers are housed in this combobox
        self.layer_box = QComboBox()
        self.layer_box.currentIndexChanged.connect(self.addLayer)

        # creating each desired action
        self.actionPan = QAction("Pan tool", self)
        self.actionLegend = QAction("Attribute info tool", self)
        self.actionCancel = QAction("Close window", self)
        self.actionZoom = QAction("Zoom to full extent", self)

        # these two work as on/off. the rest are clickable
        self.actionPan.setCheckable(True)
        self.actionLegend.setCheckable(True)

        # when actions are clicked, do corresponding function
        self.actionPan.triggered.connect(self.pan)
        self.actionLegend.triggered.connect(self.info)
        self.actionCancel.triggered.connect(self.cancel)
        self.actionZoom.triggered.connect(self.zoomToExtent)

        # defining two toolbars: first one houses layer and opacity selection
        # the other has all the tools and functions
        self.layers_toolbar = self.addToolBar("Select layers")
        self.layers_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        self.layers_toolbar.setMovable(False)
        self.addToolBarBreak()
        self.tools_toolbar = self.addToolBar("Tools")
        self.tools_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        self.tools_toolbar.setMovable(False)

        # change order here to change their placement on window
        # starting with the layer widgets and the corresponding label texts
        self.layers_toolbar.addWidget(data_label)
        self.layers_toolbar.addWidget(self.layer_box)
        self.layers_toolbar.addWidget(bg_layer_label)
        self.layers_toolbar.addWidget(self.bg_layer_box)
        self.layers_toolbar.addWidget(bg_opacity_label)
        self.layers_toolbar.addWidget(self.opacity_slider)
        self.layers_toolbar.addWidget(spacing)
        self.layers_toolbar.addWidget(self.legend_checkbox)

        # then setting all the canvas tools on the second toolbar
        self.tools_toolbar.addAction(self.actionLegend)
        self.tools_toolbar.addAction(self.actionPan)
        self.tools_toolbar.addAction(self.actionZoom)
        self.tools_toolbar.addAction(self.actionCancel)

        # a large text box that will house the legend info
        self.text_browser = QTextEdit("Legend will be shown here")
        self.text_browser.setReadOnly(True)

        # a dock widget is required for the text browser. Docked to main window
        dock_widget = QDockWidget()
        dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures)
        dock_widget.setWindowTitle("Legend")
        dock_widget.setWidget(self.text_browser)

        self.addDockWidget(Qt.RightDockWidgetArea, dock_widget)

        # link actions to premade map tools
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolPan.setAction(self.actionPan)

        self.toolClick = QgsMapToolEmitPoint(self.canvas)
        self.toolClick.canvasClicked.connect(self.getLegendInfo)

        # this is to ensure that the map isn't zoomed out everytime the layer changes
        self.first_start = True

        # this boolean is true while there is no active background layer
        # needed to ensure that e.g. opacity isn't attempted to be set on a nonexisting layer
        self.no_bg_layer_flag = True

        # set pantool as default
        self.pan()

    def pan(self):
        """Simply activates the tool and deactivates the other tool if active"""
        self.canvas.setMapTool(self.toolPan)
        # make sure the other button isn't checked to avoid confusion
        self.actionLegend.setChecked(False)

    def info(self):
        self.canvas.setMapTool(self.toolClick)
        self.actionLegend.setChecked(True)
        self.actionPan.setChecked(False)

    def zoomToExtent(self):
        """zooms out/in so that the raster layer is centered"""
        self.canvas.setExtent(self.layer.extent())
        self.canvas.refresh()

    def setBackgroundMapOpacity(self):
        if self.no_bg_layer_flag:
            return
        else:
            self.bg_layer.renderer().setOpacity(self.getBackgroundMapOpacity())
            self.canvas.refresh()

    def getBackgroundMapOpacity(self):
        """Returns the current BG layer opacity as a double [0, 1]. Slider only
            accepts integers, therefore the initial value is divided by hundred."""
        return (self.opacity_slider.value() / 100)

    def showCanvas(self, all_datasets):
        """Called to activate the the window. Input is all of the datasets on 
            the Geocubes server as a dictionary (see main plugin py-file). First a
            default layer (background map, which is on the WMTS server
            but not on Geocubes files) is inserted to the combobox. Then the 
            keys of the dictionary (which are in format layer_name;year) are inserted."""

        # empty box on restart
        self.layer_box.clear()
        self.all_datasets = all_datasets
        self.no_bg_layer_flag = True

        for key in self.all_datasets:
            self.layer_box.addItem(key)

        # zoom to the full extent of the current map
        self.zoomToExtent()

        # default values set
        self.text_browser.setText("Legend will be shown here")
        self.bg_layer_box.setCurrentIndex(2)
        self.opacity_slider.setValue(50)
        self.show()

    def getLegendInfo(self, point):
        """Activated when the canvas is clicked. The click returns a point, which
            is parsed to a string of X and Y coordinates separated by a comma.
            An url to get legend info on this point is formed and used.
            If the request is succesful, the response string is decoded and passed
            to be inserted to the text browser."""
        formatted_point = str(int(point.x())) + "," + str(int(point.y()))

        url = self.formLegendUrl(formatted_point)

        if not url:
            return

        response = requests.get(url, timeout=6)

        # 200 = succesful request
        # the field won't be updated in case of a failed request
        if response.status_code == 200:
            legend_string = response.content.decode("utf-8")
            self.setTextToBrowser(legend_string)

    def setTextToBrowser(self, string):
        """Formats and inserts legend text to the browser. Input is string of 
        raw text data. This is split at semicolons if there are multiple features."""

        # empty on multiple clicks
        self.text_browser.clear()
        strings = string.split(';')

        # no need for a loop if there's only one line
        if len(strings) == 1:
            self.text_browser.setText(string)
        else:
            for text_string in strings:
                # appending allows to insert multi-line texts
                self.text_browser.append(text_string)

    def formLegendUrl(self, formatted_point):
        """Forms an url for querying legend data on a specific coordinate point.
            Data is queried either from the currently selected layer or, if selected
            by the user, from all available layers."""
        key = self.layer_box.currentText()
        resolution = self.getResolutionFromExtent()

        if not key:
            return

        if not resolution:
            resolution = 100

        if self.legend_checkbox.isChecked():
            layer_name = "all"
            year = "2015"
        else:
            value = self.all_datasets[key]
            layer_name, year = value[0], value[3]

        url = (self.url_base + "/legend/" + str(resolution) + "/" +
               layer_name + "/" + formatted_point + "/" + year)

        return url

    def getResolutionFromExtent(self):
        """Estimates the resolution of the imagery currently viewed by user
         based on the width of the canvas. Returns said resolution. Used by
         the legend tool to get info of the correct dataset."""
        # extent as a QgsRectangle
        canvas_extent = self.canvas.extent()

        # width (in meters, since CRS is EPSG:3067) of the current canvas view
        width = canvas_extent.xMaximum() - canvas_extent.xMinimum()

        # find the width threshold closest to the current one
        try:
            closest_width = min(self.all_widths, key=lambda x: abs(x - width))
        except Exception:
            return

        # use the width key to get the corrensponding resolution
        closest_resolution = self.tile_widths[closest_width]

        return closest_resolution

    def addLayer(self):
        """Adds a new layer on the map canvas based on the selection on the combobox.
            Everything else is hardcoded, but the layer name of course changes.
            Layers are identified by name and year (i.e. km2_2018). These type
            of strings are formed first, then the whole url"""
        # often a layer already exists. If so, remove
        try:
            QgsProject.instance().removeMapLayer(self.layer)
        except Exception:
            pass

        key = self.layer_box.currentText()

        if not key:
            return
        # background map doesn't have a specific year attached to it
        if key == "Taustakartta":
            layer_name = key
        else:
            # the desired parameters are housed in the dictionary. Luckily the
            # combobox houses the keys to it. gets a tuple with four values
            value = self.all_datasets[key]
            # name is first value, year last. separated with an underscore
            layer_name = value[0] + "_" + value[3]

        url = ("https://vm0160.kaj.pouta.csc.fi/ogiir_cache/wmts/1.0.0/" +
               "WMTSCapabilities.xml&crs=EPSG:3067&dpiMode=7&format=image/" +
               "png&layers=" + layer_name +
               "&styles=default&tileMatrixSet=GRIDI-FIN")

        self.layer = QgsRasterLayer("url=" + url,
                                    'GEOCUBES DATALAYER - TEMPORARY', 'wms')

        if self.layer.isValid():
            QgsProject.instance().addMapLayer(self.layer, False)
            # if layer is valid and added to the instance, insert it to the canvas
            self.setMapLayers()
            # zoom to the full extent of the map if canvas is started for the first time
            if self.first_start:
                self.zoomToExtent()
                self.first_start = False

    def addBackgroundLayer(self):
        """Adds a background layer to help user locating what they want.
            This layer will be either background map (taustakarta), ortographic
            imagery or nothing at all. BG layer has an opacity value that set by
            the user. Function is called when user selects a layer on the
            combobox."""
        layer_name = self.bg_layer_box.currentText()

        # remove the old background layer, if one exists
        try:
            QgsProject.instance().removeMapLayer(self.bg_layer)
        except Exception:
            pass

        # if user wants no background layer, return without setting a new layer
        if not layer_name or layer_name == 'No reference layer':
            self.no_bg_layer_flag = True
            self.canvas.refresh()
            return
        else:
            self.bg_layer = QgsRasterLayer(
                "url=https://vm0160.kaj.pouta.csc.fi/ogiir_cache/wmts/1.0.0/" +
                "WMTSCapabilities.xml&crs=EPSG:3067&dpiMode=7&format=image/" +
                "png&layers=" + layer_name.lower() +
                "&styles=default&tileMatrixSet=GRIDI-FIN",
                'GEOCUBES BG-LAYER - TEMPORARY', 'wms')

            if self.bg_layer.isValid():
                self.no_bg_layer_flag = False
                QgsProject.instance().addMapLayer(self.bg_layer, False)
                self.bg_layer.renderer().setOpacity(
                    self.getBackgroundMapOpacity())
                self.setMapLayers()

    def setMapLayers(self):
        """Called anytime a new layer is added to the project instance.
        Setting layers to canvas decides what's shown to the user and in which
        order. If there's a background layer, it must be set before the data layer."""
        if self.no_bg_layer_flag:
            self.canvas.setLayers([self.layer])
        else:
            self.canvas.setLayers([self.bg_layer, self.layer])

        self.canvas.refresh()

    def cancel(self):
        self.close()

    def closeEvent(self, event):
        """Activated anytime Mapwindow is closed either by buttons given or
            if the user finds some other way to close the window. 
            Deletes scrap maplayers."""
        try:
            QgsProject.instance().removeMapLayer(self.layer)
            QgsProject.instance().removeMapLayer(self.bg_layer)
        except Exception:
            pass
        QMainWindow.closeEvent(self, event)