Beispiel #1
0
class SetPointMapTool(QgsMapToolEmitPoint):
    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker.setColor(Qt.red)
        self.vertexMarker.setCursor(Qt.CrossCursor)
        self.reset()

    def reset(self):
        self.centerPoint = None
        self.vertexMarker.hide()
        #self.isEmittingPoint = False
        #self.rubberBand.reset(QGis.Polygon)

    def canvasPressEvent(self, e):
        self.centerPoint = self.toMapCoordinates(e.pos())
        self.vertexMarker.setCenter(self.centerPoint)
        if not self.vertexMarker.isVisible():
            self.vertexMarker.show()
        #self.endPoint = self.startPoint
        #self.isEmittingPoint = True
        #self.showRect(self.startPoint, self.endPoint)

    def canvasReleaseEvent(self, e):
        return
        #self.isEmittingPoint = False
        # r = self.rectangle()
        #if r is not None:
        #   print "Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum()

    def canvasMoveEvent(self, e):
        return
        #if not self.isEmittingPoint:
        #return
        #self.endPoint = self.toMapCoordinates(e.pos())
        #self.showRect(self.startPoint, self.endPoint)

    def rectangle(self):
        if self.startPoint is None or self.endPoint is None:
            return None
        elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y(
        ) == self.endPoint.y():
            return None
        return QgsRectangle(self.startPoint, self.endPoint)

    def deactivate(self):
        super(SetPointMapTool, self).deactivate()
        #self.emit(SIGNAL("deactivated()"))
        self.deactivated.emit()
Beispiel #2
0
class APISImageMapping(QDockWidget, FORM_CLASS):
    def __init__(self, iface, dbm, apisLayer, parent=None):
        """Constructor."""
        super(APISImageMapping, self).__init__(parent)

        self.iface = iface
        self.dbm = dbm
        self.apisLayer = apisLayer
        self.setupUi(self)
        self.settings = QSettings(QSettings().value("APIS/config_ini"),
                                  QSettings.IniFormat)

        self.iface.addDockWidget(Qt.RightDockWidgetArea, self)
        self.hide()
        self.canvas = self.iface.mapCanvas()

        self.imageCenterPoint = None
        self.cpLayerId = None
        self.fpLayerId = None
        self.currentFilmNumber = None
        self.isOblique = True

        self.uiEditProjectTableObliqueBtn.clicked.connect(
            lambda: self.openSystemTableEditorDialog(
                "projekt", self.uiProjectObliqueCombo))
        self.uiEditProjectTableVerticalBtn.clicked.connect(
            lambda: self.openSystemTableEditorDialog(
                "projekt", self.uiProjectVerticalCombo))

        self.visibilityChanged.connect(self.onVisibilityChanged)

        self.setPointMapTool = QgsMapToolEmitPoint(
            self.canvas)  # etPointMapTool(self.canvas)
        self.setPointMapTool.canvasClicked.connect(self.updatePoint)
        #self.setPointMapTool.canvasDoubleClicked.connect(self.handleDoubleClick)
        self.setPointMapTool.setButton(self.uiSetCenterPointBtn)
        self.uiSetCenterPointBtn.setCheckable(True)
        self.uiSetCenterPointBtn.toggled.connect(self.toggleSetCenterPoint)
        #self.uiSetCenterPointBtn.clicked.connect(self.activateSetCenterPoint)

        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker2 = QgsVertexMarker(self.canvas)
        self.vertexMarker.setIconType(3)
        self.vertexMarker2.setIconType(1)
        self.vertexMarker.setColor(QColor(255, 153, 0))
        self.vertexMarker2.setColor(QColor(255, 153, 0))
        self.vertexMarker.setIconSize(12)
        self.vertexMarker2.setIconSize(20)
        self.vertexMarker.hide()
        self.vertexMarker2.hide()

        self.filmSelectionDlg = APISFilmNumberSelection()
        self.uiFilmSelectionBtn.clicked.connect(self.openFilmSelectionDialog)

        self.uiAutoImportBtn.clicked.connect(self.openAutoImportDialog)

        self.uiAddCenterPointBtn.clicked.connect(self.startAddingDetails)

        self.uiCancelCenterPointBtn.clicked.connect(
            self.onCancelAddCenterPoint)
        self.uiSaveCenterPointBtn.clicked.connect(self.onSaveAddCenterPoint)

        self.uiGenerateFootprintsBtn.clicked.connect(self.generateFootprints)

        self.uiAddProjectObliqueBtn.clicked.connect(self.addProjectOblique)
        self.uiAddProjectVerticalBtn.clicked.connect(self.addProjectVertical)

        self.uiRemoveProjectObliqueBtn.clicked.connect(
            self.removeProjectOblique)
        self.uiRemoveProjectVerticalBtn.clicked.connect(
            self.removeProjectVertical)

        self.resetCurrentFilmNumber()

        self.mappingMode = False

    def onVisibilityChanged(self, isVisible):
        if not isVisible:
            self.vertexMarker.hide()
            self.vertexMarker2.hide()

            if self.uiSetCenterPointBtn.isChecked():
                self.uiSetCenterPointBtn.toggle()

            self.removeCenterPointLayer()
            self.removeFootPrintLayer()
            self.apisLayer.removeLayerGroupIfEmpty("Bildkartierung")

        else:
            if self.currentFilmNumber:
                self.reloadFpLayer()
                self.reloadCpLayer()

            if self.mappingMode:
                self.vertexMarker.show()
                self.vertexMarker2.show()

    def openFilmSelectionDialog(self):
        """Run method that performs all the real work"""
        self.filmSelectionDlg.show()
        self.filmSelectionDlg.uiFilmNumberEdit.setFocus()
        if self.filmSelectionDlg.exec_():
            if not self.checkFilmNumber(self.filmSelectionDlg.filmNumber()):
                self.resetCurrentFilmNumber()
                #self.openFilmSelectionDialog()
            else:
                self.setCurrentFilmNumber(self.filmSelectionDlg.filmNumber())

    def openSystemTableEditorDialog(self, table, editor):
        if self.dbm:
            self.systemTableEditorDlg = APISAdvancedInputDialog(
                self.dbm,
                table,
                True,
                modelColumnName="bezeichnung",
                excludeEntries=[
                    editor.itemText(i) for i in range(editor.count())
                ],
                parent=self)

            if self.systemTableEditorDlg.tableExists:
                if self.systemTableEditorDlg.exec_():
                    # QMessageBox.information(self, "info", "{}".format(self.systemTableEditorDlg.getValueToBeAdded()))
                    self.updateProjectListOfFilm(
                        self.systemTableEditorDlg.getValueToBeAdded(), editor)
            else:
                QMessageBox.warning(
                    self, "Tabelle nicht vorhanden",
                    "Die Tabelle {0} ist in der APIS Datenbank nicht vorhanden"
                    .format(table))

        else:
            QMessageBox.warning(
                self, "Warning Database",
                "Die APIS Datenbank konnte nicht gefunden werden.")

    def openAutoImportDialog(self):
        self.reloadFpLayer()
        self.reloadCpLayer()

        autoImportDlg = APISDigitalImageAutoImport(self.iface,
                                                   self.dbm,
                                                   self.apisLayer,
                                                   self.cpLayer,
                                                   self.fpLayer,
                                                   self.currentFilmNumber,
                                                   parent=self)
        autoImportDlg.show()
        if autoImportDlg.exec_():
            self.cpLayer.updateExtents()
            self.fpLayer.updateExtents()

    def setCurrentFilmNumber(self, filmNumber):
        self.currentFilmNumber = filmNumber
        self.uiCurrentFilmNumberEdit.setText(self.currentFilmNumber)

        # Get Information & Stats: schräg/senk, bildanzahl, ...
        filmStatus = self.getFilmInfo(
        )  # filmStatus true/false depending if problems wiht film infos
        if not filmStatus:
            self.resetCurrentFilmNumber()
            return
        self.getMappingStats()
        self.updateMappingDetails()

        # Enable Controls
        self.setCurrentLayout(True, True, True, False)
        if not self.imageCenterPoint:
            self.uiAddCenterPointBtn.setEnabled(False)
        self.mappingMode = False
        #if not self.uiSetCenterPointBtn.isChecked():
        #    self.uiSetCenterPointBtn.toggle()

        # Remove old Layer & Load New Layer
        self.removeCenterPointLayer()
        self.removeFootPrintLayer()
        self.loadFootPrintLayerForFilm()
        self.loadCenterPointLayerForFilm()

    def resetCurrentFilmNumber(self):
        self.uiCurrentFilmNumberEdit.clear()
        self.currentFilmNumber = None
        self.setCurrentLayout(True, False, False, False)

        #self.canvas.unsetMapTool(self.setPointMapTool)
        self.mappingMode = False
        if self.uiSetCenterPointBtn.isChecked():
            self.uiSetCenterPointBtn.toggle()

        self.vertexMarker.hide()
        self.vertexMarker2.hide()

        # Reset & Remove Layer!!!
        self.removeCenterPointLayer()
        self.removeFootPrintLayer()

    def enableItemsInLayout(self, layout, enable):
        for i in range(layout.count()):
            if layout.itemAt(i).widget():
                layout.itemAt(i).widget().setEnabled(enable)

    def visibleItemsInLayout(self, layout, visible):
        for i in range(layout.count()):
            if layout.itemAt(i).widget():
                layout.itemAt(i).widget().setVisible(visible)

    def checkFilmNumber(self, filmNumber):
        if IsFilm(self.dbm.db, filmNumber):
            # Film exists
            return True
        else:
            # Film does not exist
            QMessageBox.warning(
                None, u"Film Nummer",
                u"Der Film mit der Nummer {0} existiert nicht!".format(
                    filmNumber))
            return False

    def getFilmInfo(self):
        filmFields = [
            "filmnummer_hh_jjjj_mm", "filmnummer_nn", "filmnummer",
            "anzahl_bilder", "form1", "form2", "weise", "kammerkonstante",
            "datum_ersteintrag", "datum_aenderung", "projekt", "copyright"
        ]
        self.currentFilmInfoDict = {}
        query = QSqlQuery(self.dbm.db)
        qryStr = "SELECT {0} FROM film WHERE filmnummer = '{1}'".format(
            ", ".join(filmFields), self.currentFilmNumber)
        query.exec_(qryStr)
        query.first()
        if query.value(0) is not None:
            for key in filmFields:
                value = query.value(query.record().indexOf(key))
                self.currentFilmInfoDict[key] = value

            if self.currentFilmInfoDict["weise"] == u"schräg":
                self.orientation = "schraeg"
                self.isOblique = True
            else:
                self.orientation = "senk"
                self.isOblique = False
                # QMessageBox.information(None, "kkInfo0", f'type: {type(self.currentFilmInfoDict["kammerkonstante"])}')
                if isinstance(
                        self.currentFilmInfoDict["kammerkonstante"], str
                ) and not self.currentFilmInfoDict["kammerkonstante"]:
                    QMessageBox.warning(
                        None, u"Film Nummer",
                        u"Der senkrecht Film mit der Nummer {0} hat keine Kammerkonstante (Wird für die Berechnung des Bildmaßstabs und in Folge des Footprints benötigt.) Geben Sie im Film Dialog die Kammerkonstante an, um korrekte Footprints zu erhalten."
                        .format(self.currentFilmNumber))
                    return False
                else:
                    if not self.currentFilmInfoDict["kammerkonstante"] > 0:
                        QMessageBox.warning(
                            None, u"Film Nummer",
                            u"Der senkrecht Film mit der Nummer {0} hat eine Kammerkonstante (={1}), die für die Berechnung des Bildmaßstabs und in Folge des Footprints ungeeignet ist! Geben Sie im Film Dialog die Kammerkonstante an, um korrekte Footprints zu erhalten. Falls Bildmittelpunkte bereits gesetzt sind (mit einem Maßstab = 0) müssen auch diese neu kartiert werden!"
                            .format(
                                self.currentFilmNumber,
                                self.currentFilmInfoDict["kammerkonstante"]))
                        return False
            return True
        else:
            QMessageBox.warning(
                None, u"Film Nummer",
                u"Der Film mit der Nummer {0} existiert nicht!".format(
                    self.currentFilmNumber))
            return False

    def getMappingStats(self):
        self.imageStatsDict = {}
        query = QSqlQuery(self.dbm.db)
        qryStr = "SELECT COUNT(*) AS anzahl_kartiert, MAX(bildnummer_nn) AS letzte_bildnummer, MIN(bildnummer_nn) AS erste_bildnummer FROM luftbild_{0}_cp WHERE filmnummer = '{1}'".format(
            self.orientation, self.currentFilmNumber)
        query.exec_(qryStr)
        query.first()
        statFields = [
            "anzahl_kartiert", "letzte_bildnummer", "erste_bildnummer"
        ]
        if query.value(0) is not None:
            for key in statFields:
                idx = query.record().indexOf(key)
                if query.isNull(idx):
                    self.imageStatsDict[key] = 0
                else:
                    self.imageStatsDict[key] = query.value(idx)

            #QMessageBox.warning(None, u"Film Nummer", u"{0}, {1}, {2}".format(self.imageStatsDict["anzahl_kartiert"],self.imageStatsDict["letzte_bildnummer"],self.imageStatsDict["erste_bildnummer"]))
        else:
            self.imageStatsDict["anzahl_kartiert"] = 0
            self.imageStatsDict["letzte_bildnummer"] = 0
            self.imageStatsDict["erste_bildnummer"] = 0
            QMessageBox.warning(
                None, u"Film Nummer",
                u"Für den Film mit der Nummer {0} sind noch keine Bilder kartiert!"
                .format(self.currentFilmNumber))

    def updateMappingDetails(self):
        if self.isOblique:
            self.uiMappingDetailsTBox.setCurrentIndex(0)
            self.uiImageNumberFromSpn.setValue(
                self.imageStatsDict["letzte_bildnummer"] + 1)
            self.uiImageNumberToSpn.setValue(
                self.imageStatsDict["letzte_bildnummer"] + 1)

            # Projekte
            # DropDown Reset
            # von Film in DropDown hinzufügen
            self.uiProjectObliqueCombo.clear()
            if self.currentFilmInfoDict["projekt"]:
                self.uiProjectObliqueCombo.addItems(
                    str.split(self.currentFilmInfoDict["projekt"], ";"))
            else:
                #deactivate buttons
                pass

        else:
            self.uiMappingDetailsTBox.setCurrentIndex(1)
            self.uiImageNumberSpn.setValue(
                self.imageStatsDict["letzte_bildnummer"] + 1)
            self.uiProjectVerticalCombo.clear()
            if self.currentFilmInfoDict["projekt"]:
                self.uiProjectVerticalCombo.addItems(
                    str.split(self.currentFilmInfoDict["projekt"], ";"))
            else:
                #deactivate buttons
                pass

    def updateProjectListOfFilm(self, newValue, editor):
        # QMessageBox.information(self, "Info", "'{0};{1}'".format(self.currentFilmInfoDict["projekt"], newValue))
        if self.currentFilmInfoDict["projekt"]:
            qryStr = "UPDATE film SET projekt = '{0}' WHERE filmnummer = '{1}'".format(
                "{0};{1}".format(self.currentFilmInfoDict["projekt"],
                                 newValue), self.currentFilmNumber)
        else:
            qryStr = "UPDATE film SET projekt = '{0}' WHERE filmnummer = '{1}'".format(
                newValue, self.currentFilmNumber)

        query = QSqlQuery(self.dbm.db)
        res = query.exec_(qryStr)

        if res:
            # update Film info and comboBox
            if self.currentFilmInfoDict["projekt"]:
                self.currentFilmInfoDict["projekt"] = "{0};{1}".format(
                    self.currentFilmInfoDict["projekt"], newValue)
            else:
                self.currentFilmInfoDict["projekt"] = "{0}".format(newValue)

            editor.clear()
            if self.currentFilmInfoDict["projekt"]:
                editor.addItems(
                    str.split(self.currentFilmInfoDict["projekt"], ";"))
                editor.setCurrentIndex(editor.count() - 1)

            self.dbm.dbRequiresUpdate = True
        else:
            QMessageBox.warning(
                self, "DB Fehler",
                "Der folgende Feheler ist aufgetreten: {}".format(
                    query.lastError().text()))

    def toggleSetCenterPoint(self, isChecked):
        if isChecked:
            self.canvas.setMapTool(self.setPointMapTool)
            self.iface.messageBar().pushMessage(
                u"APIS Bild Kartierung",
                u"Positionieren Sie den Bildmittelpunkt mit der linken Maustaste und klicken Sie auf das Plus Symbol (oder verwenden Sie die reche Maustaste)",
                level=Qgis.Info)
            self.vertexMarker.show()
            self.vertexMarker2.show()
        else:
            self.canvas.unsetMapTool(self.setPointMapTool)
            self.iface.actionPan().trigger()
            if not self.mappingMode:
                self.vertexMarker.hide()
                self.vertexMarker2.hide()
            self.iface.messageBar().clearWidgets()

    def updatePoint(self, point, button):
        self.reloadCpLayer()
        self.imageCenterPoint = self.canvas.mapSettings(
        ).mapToLayerCoordinates(self.cpLayer, QgsPointXY(point))
        #self.gl.setLocation(self.imageCenterPoint)
        self.vertexMarker.setCenter(point)
        self.vertexMarker2.setCenter(point)
        self.uiXCoordinateLbl.setText("{}".format(point.x()))
        self.uiYCoordinateLbl.setText("{}".format(point.y()))
        self.uiAddCenterPointBtn.setEnabled(True)
        if not self.vertexMarker.isVisible():
            self.vertexMarker.show()
            self.vertexMarker2.show()
        if button == Qt.RightButton:
            self.startAddingDetails()

    def startAddingDetails(self):
        # Disable Layouts
        self.mappingMode = True
        if self.uiSetCenterPointBtn.isChecked():
            self.uiSetCenterPointBtn.toggle()
        self.setCurrentLayout(False, False, False, True)

        self.uiMappingDetailsTBox.setItemEnabled(int(self.isOblique), False)

    def loadCenterPointLayerForFilm(self):
        self.cpLayer = self.apisLayer.requestSpatialiteTable(
            self.dbm.db.databaseName(),
            "luftbild_{0}_cp".format(self.orientation),
            displayName="Kartierung {0} Mittelpunkt".format(
                self.currentFilmNumber),
            groupName="Bildkartierung",
            subsetString='"filmnummer" = "{0}"'.format(self.currentFilmNumber),
            useLayerFromTree=False,
            addToCanvas=True,
            stylePath=self.apisLayer.getStylePath("image_mapping_cp"))
        self.cpLayerId = self.cpLayer.id()

    def loadFootPrintLayerForFilm(self):
        self.fpLayer = self.apisLayer.requestSpatialiteTable(
            self.dbm.db.databaseName(),
            "luftbild_{0}_fp".format(self.orientation),
            displayName="Kartierung {0} Footprint".format(
                self.currentFilmNumber),
            groupName="Bildkartierung",
            subsetString='"filmnummer" = "{0}"'.format(self.currentFilmNumber),
            useLayerFromTree=False,
            addToCanvas=True,
            stylePath=self.apisLayer.getStylePath("image_mapping_fp"))
        self.fpLayerId = self.fpLayer.id()

    def removeCenterPointLayer(self):
        if self.cpLayerId in QgsProject.instance().mapLayers():
            QgsProject.instance().removeMapLayer(self.cpLayer.id())

    def removeFootPrintLayer(self):
        if self.fpLayerId in QgsProject.instance().mapLayers():
            QgsProject.instance().removeMapLayer(self.fpLayer.id())

    def onCancelAddCenterPoint(self):
        self.setCurrentLayout(True, True, True, False)
        self.getMappingStats()
        self.updateMappingDetails()
        #self.canvas.setMapTool(self.setPointMapTool)
        self.mappingMode = False
        if not self.uiSetCenterPointBtn.isChecked():
            self.uiSetCenterPointBtn.toggle()

    def onSaveAddCenterPoint(self):
        #QMessageBox.warning(None, u"Film Nummer", u"{0},{1},{2}".format(self.imageCenterPoint.x(), self.imageCenterPoint.y(), type(self.imageCenterPoint)))
        self.reloadCpLayer()

        #Prepare Image Numbers
        if self.isOblique:
            fromImageNumber = self.uiImageNumberFromSpn.value()
            toImageNumber = self.uiImageNumberToSpn.value()
            if fromImageNumber > toImageNumber:
                QMessageBox.warning(
                    None, u"Bild Nummern",
                    u"Die erste Bildnummer darf nicht größer als die zweite sein."
                )
                return
            else:
                imageNumbers = range(fromImageNumber, toImageNumber + 1)
        else:
            imageNumbers = [self.uiImageNumberSpn.value()]

        # filmImageNumbers = []
        # for imageNumber in imageNumbers:
        #     filmImageNumbers.append('{0}.{1:03d}'.format(self.currentFilmNumber, imageNumber))

        # QMessageBox.warning(None, u"Bild Nummern", ",".join(imageNumbers))

        # for filmImageNumber in self.cpLayer.getValues("BILD"):
        #    QMessageBox.warning(None, u"Bild Nummern", "{0}".format(filmImageNumber))

        # Check if Image Number in Table
        for imageNumber in imageNumbers:
            # QMessageBox.warning(None, u"Bild Nummern", u"{0}".format(QgsVectorLayerUtils.getValues(self.cpLayer, "bildnummer_nn")))
            if imageNumber in QgsVectorLayerUtils.getValues(
                    self.cpLayer, "bildnummer_nn")[0]:
                QMessageBox.warning(
                    None, u"Bild Nummern",
                    u"Ein Bild mit der Nummer {0} wurde bereits kartiert".
                    format(imageNumber))
                return

        caps = self.cpLayer.dataProvider().capabilities()
        if caps & QgsVectorDataProvider.AddFeatures:
            features = []

            feat = QgsFeature(self.cpLayer.fields())
            feat.setGeometry(QgsGeometry.fromPointXY(self.imageCenterPoint))

            # From Film Table
            # filmFields = ["form1", "form2", "weise", "kammerkonstante"]
            feat.setAttribute(
                'filmnummer_hh_jjjj_mm',
                self.currentFilmInfoDict["filmnummer_hh_jjjj_mm"])
            feat.setAttribute('filmnummer_nn',
                              self.currentFilmInfoDict["filmnummer_nn"])
            feat.setAttribute('filmnummer', self.currentFilmNumber)

            #Date TODAY
            now = QDate.currentDate()
            feat.setAttribute('datum_ersteintrag', now.toString("yyyy-MM-dd"))
            feat.setAttribute('datum_aenderung', now.toString("yyyy-MM-dd"))

            # Iterate over Project Selection List und String mit ; trennung generieren
            feat.setAttribute('copyright',
                              self.currentFilmInfoDict["copyright"])

            # By Default Fix Value
            feat.setAttribute('etikett', 0)

            # Get Projects from Projekte Liste
            items = []
            # From Input (Radius, Höhe, Schlüsslewort, Beschreibung)
            if self.isOblique:
                feat.setAttribute('radius',
                                  float(self.uiImageDiameterSpn.value() / 2))
                feat.setAttribute('beschreibung',
                                  self.uiImageDescriptionEdit.text())
                h = self.uiFlightHeightObliqueSpn.value()
                for j in range(self.uiProjectObliqueList.count()):
                    items.append(self.uiProjectObliqueList.item(j))
            else:
                h = self.uiFlightHeightVerticalSpn.value()
                feat.setAttribute('fokus',
                                  self.currentFilmInfoDict["kammerkonstante"])
                if not self.currentFilmInfoDict[
                        "kammerkonstante"] or not self.currentFilmInfoDict[
                            "kammerkonstante"] > 0:
                    feat.setAttribute('massstab', 0)
                else:
                    feat.setAttribute(
                        'massstab',
                        h / self.currentFilmInfoDict["kammerkonstante"] * 1000)
                for j in range(self.uiProjectVerticalList.count()):
                    items.append(self.uiProjectVerticalList.item(j))

            feat.setAttribute('projekt', ";".join([i.text() for i in items]))
            feat.setAttribute('hoehe', h)

            # Calculated/Derived
            feat.setAttribute('longitude', self.imageCenterPoint.x())
            feat.setAttribute('latitude', self.imageCenterPoint.y())

            countryCode = self.getCountryCode()
            feat.setAttribute('land', countryCode)

            if countryCode == 'AUT':
                # get meridian and epsg Code
                meridian, epsgGK = GetMeridianAndEpsgGK(
                    self.imageCenterPoint.x())

                # get KG Coordinates
                gk = TransformGeometry(
                    QgsGeometry().fromPointXY(self.imageCenterPoint),
                    self.cpLayer.crs(),
                    QgsCoordinateReferenceSystem(f"EPSG:{epsgGK}"))
                gkx = gk.asPoint().y()  # Hochwert
                gky = gk.asPoint().x()  # Rechtswert
            else:
                meridian = None
                gkx = None
                gky = None

            feat.setAttribute('meridian', meridian)
            feat.setAttribute('gkx', gkx)  # Hochwert
            feat.setAttribute('gky', gky)  # Rechtswert

            for imageNumber in imageNumbers:
                f = QgsFeature(feat)
                f.setAttribute('bildnummer_nn', imageNumber)
                bn = '{0}.{1:03d}'.format(self.currentFilmNumber, imageNumber)
                f.setAttribute('bildnummer', bn)

                if self.isOblique:
                    image = os.path.normpath(
                        self.settings.value("APIS/image_dir") + '\\' +
                        self.currentFilmNumber + '\\' + bn.replace('.', '_') +
                        '.jpg')
                    exif = GetExifForImage(image,
                                           altitude=True,
                                           longitude=True,
                                           latitude=True,
                                           exposure_time=True,
                                           focal_length=True,
                                           fnumber=True)

                    if "altitude" in exif and exif["altitude"]:
                        f.setAttribute('hoehe', exif["altitude"])
                    f.setAttribute(
                        'gps_longitude',
                        exif["longitude"] if "longitude" in exif else None)
                    f.setAttribute(
                        'gps_latitude',
                        exif["latitude"] if "latitude" in exif else None)

                    if "longitude" in exif and "latitude" in exif and exif[
                            "longitude"] and exif["latitude"]:
                        capturePoint = QgsPointXY(exif["longitude"],
                                                  exif["latitude"])
                        kappa = capturePoint.azimuth(self.imageCenterPoint)
                    else:
                        kappa = None
                    f.setAttribute('kappa', kappa)

                    f.setAttribute(
                        'belichtungszeit', exif["exposure_time"]
                        if "exposure_time" in exif else None)
                    f.setAttribute('fokus',
                                   exif["focal_length"] if "focal_length"
                                   in exif else None)  # FocalLength
                    if "focal_length" in exif and "fnumber" in exif and exif[
                            "focal_length"] and exif["fnumber"]:
                        blende = exif["focal_length"] / exif[
                            "fnumber"]  # effecitve aperture (diameter of entrance pupil) = focalLength / fNumber
                    else:
                        blende = None
                    f.setAttribute('blende', blende)

                features.append(f)

            (res, outFeats) = self.cpLayer.dataProvider().addFeatures(features)
            self.cpLayer.updateExtents()

            if res and self.isOblique:
                self.generateFootprintsForFilmOblique()

            #QMessageBox.warning(None, u"Film Nummer", u"{0},{1}".format(res, outFeats))
        else:
            QMessageBox.warning(None, u"Layer Capabilities!")

        if self.canvas.isCachingEnabled():
            self.cpLayer.triggerRepaint()
        else:
            self.canvas.refresh()

        self.onCancelAddCenterPoint()

    def getCountryCode(self):
        query = QSqlQuery(self.dbm.db)
        #qryStr = "SELECT code FROM osm_boundaries WHERE within(MakePoint({0}, {1}, 4312), geometry)".format(self.imageCenterPoint.x(), self.imageCenterPoint.y())
        qryStr = "SELECT code FROM osm_boundaries WHERE intersects(Transform(MakePoint({0}, {1}, 4312), 4326), geometry)  AND ROWID IN (SELECT ROWID FROM SpatialIndex WHERE f_table_name = 'osm_boundaries' AND search_frame = Transform(MakePoint({0}, {1}, 4312), 4326))".format(
            self.imageCenterPoint.x(), self.imageCenterPoint.y())
        query.exec_(qryStr)
        query.first()
        if query.value(0) is None:
            return 'INT'
        else:
            return query.value(0)

    def reloadCpLayer(self):
        if self.cpLayerId not in QgsProject.instance().mapLayers():
            self.loadCenterPointLayerForFilm()

    def reloadFpLayer(self):
        if self.fpLayerId not in QgsProject.instance().mapLayers():
            self.loadFootPrintLayerForFilm()

    def generateFootprints(self):
        if self.isOblique:
            self.generateFootprintsForFilmOblique()
        else:
            self.generateFootprintsForFilmVertical()

    def generateFootprintsForFilmVertical(self):
        self.reloadFpLayer()
        self.reloadCpLayer()

        # Error wenn nur ein punkt vorhanden
        if self.cpLayer.featureCount() > 1:
            caps = self.fpLayer.dataProvider().capabilities()
            if caps & QgsVectorDataProvider.AddFeatures:
                #Get FORM1 from FilmInfoDict
                f1 = self.currentFilmInfoDict["form1"]  # Image height
                f2 = self.currentFilmInfoDict["form2"]  # Image width

                iterFeatures = self.cpLayer.getFeatures()
                iterNext = self.cpLayer.getFeatures()
                existingFootpints = QgsVectorLayerUtils.getValues(
                    self.fpLayer, "bildnummer")[0]
                ft = QgsFeature()
                ftNext = QgsFeature()
                iterNext.nextFeature(ftNext)
                fpFeats = []
                kappasToUpdate = {}
                # iterate over points from CP Layer > LON, LAT
                i = 0
                while iterFeatures.nextFeature(ft):
                    i += 1
                    iterNext.nextFeature(ftNext)
                    p = QgsPointXY(ft.geometry().asPoint())
                    if ft['bildnummer'] in existingFootpints:
                        pPrevGeom = QgsGeometry(ft.geometry())
                        #QMessageBox.warning(None, u"Bild Nummern", u"Footprint für das Bild mit der Nummer {0} wurde bereits erstellt.".format(ft['BILD']))
                        continue
                    if i == 1:
                        pPrevGeom = QgsGeometry(ftNext.geometry())
                    #if iterNext.isClosed():
                    #    #use pPrev as pNext
                    #    pNext = QgsPoint(pPrev)
                    #else:
                    #    pNext = QgsPoint(ftNext.geometry().asPoint())

                    #kappa = p.azimuth(pPrev)

                    #kappa = p.azimuth(pNext)

                    # d = math.sqrt(2*((f1/2 * ft['MASS']/1000)**2))
                    d1 = f1 / 2 * ft['massstab'] / 1000
                    d2 = f2 / 2 * ft['massstab'] / 1000
                    #QMessageBox.warning(None, u"Bild Nummern", "{0}".format(d))

                    calcCrs = QgsCoordinateReferenceSystem()
                    calcCrs.createFromProj4(self.Proj4Utm(p))
                    ctF = QgsCoordinateTransform(self.cpLayer.crs(), calcCrs,
                                                 QgsProject.instance())

                    cpMetric = QgsGeometry(ft.geometry())
                    cpMetric.transform(ctF)
                    pPrevGeom.transform(ctF)
                    pMetric = QgsPointXY(cpMetric.asPoint())
                    pPrevMetric = QgsPointXY(pPrevGeom.asPoint())
                    kappaMetric = pMetric.azimuth(pPrevMetric)
                    pPrevGeom = QgsGeometry(ft.geometry())
                    left = pMetric.x() - d2
                    bottom = pMetric.y() - d1
                    right = pMetric.x() + d2
                    top = pMetric.y() + d1

                    #R = 6371
                    #D = (d/1000)
                    #cpLat = math.radians(p.y())
                    #cpLon = math.radians(p.x())
                    #urLat = math.asin( math.sin(cpLat)*math.cos(D/R) + math.cos(cpLat)*math.sin(D/R)*math.cos(urBrng) )
                    #urLon = cpLon + math.atan2(math.sin(urBrng)*math.sin(D/R)*math.cos(cpLat), math.cos(D/R)-math.sin(cpLat)*math.sin(urLat))

                    #top = math.asin( math.sin(cpLat)*math.cos(D/R) + math.cos(cpLat)*math.sin(D/R) )
                    #bottom = math.asin( math.sin(cpLat)*math.cos(D/R) + math.cos(cpLat)*math.sin(D/R)*-1 )

                    #lat = math.asin( math.sin(cpLat)*math.cos(D/R) )
                    #right = cpLon + math.atan2(math.sin(D/R)*math.cos(cpLat), math.cos(D/R)-math.sin(cpLat)*math.sin(lat))
                    #left = cpLon + math.atan2(-1*math.sin(D/R)*math.cos(cpLat), math.cos(D/R)-math.sin(cpLat)*math.sin(lat))

                    #QMessageBox.warning(None, u"Bild Nummern", "{0}, {1}, {2}, {3}".format(math.degrees(top), math.degrees(bottom), math.degrees(left), math.degrees(right)))

                    #rect = QgsRectangle(math.degrees(left), math.degrees(bottom), math.degrees(right), math.degrees(top))
                    #l = math.degrees(left)
                    #b = math.degrees(bottom)
                    #r = math.degrees(right)
                    #t = math.degrees(top)
                    p1 = QgsGeometry.fromPointXY(QgsPointXY(left, bottom))
                    p2 = QgsGeometry.fromPointXY(QgsPointXY(right, bottom))
                    p3 = QgsGeometry.fromPointXY(QgsPointXY(right, top))
                    p4 = QgsGeometry.fromPointXY(QgsPointXY(left, top))
                    #p1.rotate(kappa+90, p)
                    #p2.rotate(kappa+90, p)
                    #p3.rotate(kappa+90, p)
                    #p4.rotate(kappa+90, p)
                    pol = [[
                        p1.asPoint(),
                        p2.asPoint(),
                        p3.asPoint(),
                        p4.asPoint()
                    ]]
                    geom = QgsGeometry.fromPolygonXY(pol)
                    geom.rotate(kappaMetric, pMetric)
                    #Transform to DestinationCRS
                    ctB = QgsCoordinateTransform(calcCrs, self.fpLayer.crs(),
                                                 QgsProject.instance())
                    geom.transform(ctB)

                    feat = QgsFeature(self.fpLayer.fields())
                    feat.setGeometry(geom)
                    feat.setAttribute('filmnummer', self.currentFilmNumber)
                    feat.setAttribute('bildnummer', ft['bildnummer'])
                    da = QgsDistanceArea()
                    da.setEllipsoid(self.fpLayer.crs().ellipsoidAcronym())
                    feat.setAttribute('shape_length',
                                      da.measurePerimeter(geom))
                    feat.setAttribute('shape_area', da.measureArea(geom))
                    fpFeats.append(feat)

                    # update Kappa in cpLayer
                    kappasToUpdate[ft.id()] = {
                        ft.fieldNameIndex('kappa'): kappaMetric
                    }

                iterFeatures.close()
                iterNext.close()

                resCAVs = self.cpLayer.dataProvider().changeAttributeValues(
                    kappasToUpdate)
                QgsMessageLog.logMessage(
                    f"Kappa Update for {kappasToUpdate}, Success: {resCAVs}",
                    tag="APIS",
                    level=Qgis.Success if resCAVs else Qgis.Critical)

                (res,
                 outFeats) = self.fpLayer.dataProvider().addFeatures(fpFeats)

                self.fpLayer.updateExtents()
                if self.canvas.isCachingEnabled():
                    self.fpLayer.triggerRepaint()
                else:
                    self.canvas.refresh()
            else:
                #Caps
                QMessageBox.warning(None, "Layer Capabilities!",
                                    "Layer Capabilities!")
        else:
            #small feature count
            QMessageBox.warning(
                None, "Footprints",
                "Zum Berechnen der senkrecht Footprint müssen mindestens zwei Bilder kartiert werden!"
            )

    def generateFootprintsForFilmOblique(self):
        self.reloadFpLayer()
        self.reloadCpLayer()

        caps = self.fpLayer.dataProvider().capabilities()
        if caps & QgsVectorDataProvider.AddFeatures:
            if self.cpLayer.dataProvider().featureCount() > 0:
                iter = self.cpLayer.getFeatures()
                existingFootpints = QgsVectorLayerUtils.getValues(
                    self.fpLayer, "bildnummer")[0]
                cpFt = QgsFeature()
                fpFts = []
                #iterate over points from CP Layer > LON, LAT
                while iter.nextFeature(cpFt):
                    if cpFt['bildnummer'] in existingFootpints:
                        #QMessageBox.warning(None, u"Bild Nummern", u"Footprint für das Bild mit der Nummer {0} wurde bereits erstellt.".format(ft['BILD']))
                        continue
                    cp = cpFt.geometry()
                    cpMetric = QgsGeometry(cp)
                    destCrs = QgsCoordinateReferenceSystem()
                    destCrs.createFromProj4(self.Proj4Utm(cp.asPoint()))
                    coordTransformF = QgsCoordinateTransform(
                        self.cpLayer.crs(), destCrs, QgsProject.instance())
                    coordTransformB = QgsCoordinateTransform(
                        destCrs, self.cpLayer.crs(), QgsProject.instance())
                    cpMetric.transform(coordTransformF)
                    if cpFt['radius'] == '':
                        r = 175
                    else:
                        r = float(cpFt['radius'])
                    fpMetric = QgsGeometry(cpMetric.buffer(r, 18))
                    fp = QgsGeometry(fpMetric)
                    fp.transform(coordTransformB)

                    fpFt = QgsFeature(self.fpLayer.fields())
                    fpFt.setGeometry(fp)
                    fpFt.setAttribute("bildnummer", cpFt["bildnummer"])
                    fpFt.setAttribute("filmnummer", cpFt["filmnummer"])
                    da = QgsDistanceArea()
                    da.setEllipsoid(self.fpLayer.crs().ellipsoidAcronym())
                    fpFt.setAttribute('shape_length', da.measurePerimeter(fp))
                    fpFt.setAttribute('shape_area', da.measureArea(fp))
                    fpFts.append(fpFt)

                (res,
                 outFeats) = self.fpLayer.dataProvider().addFeatures(fpFts)
                self.fpLayer.updateExtents()
                if self.canvas.isCachingEnabled():
                    self.fpLayer.triggerRepaint()
                else:
                    self.canvas.refresh()
            else:
                QMessageBox.warning(
                    None, "Keine Bildmittelpunkte",
                    "Keine Bildmittelpunkte für den Film {0} vorhanden.".
                    format(self.currentFilmNumber))
        else:
            QMessageBox.warning(
                None, "Layer Capabilities",
                "AddFeature is not enabled ({0})".format(
                    self.fpLayer.dataProvider().capabilitiesString()))

    def Proj4Utm(self, p):
        x = p.x()
        y = p.y()
        z = math.floor((x + 180) / 6) + 1

        if y >= 56.0 and y < 64.0 and x >= 3.0 and x < 12.0:
            z = 32

        #Special zones for Svalbard
        if y >= 72.0 and y < 84.0:
            if y >= 0.0 and y < 9.0:
                z = 31
            elif y >= 9.0 and y < 21.0:
                z = 33
            elif y >= 21.0 and y < 33.0:
                z = 35
            elif y >= 33.0 and y < 42.0:
                z = 37

        return "+proj=utm +zone={0} +datum=WGS84 +units=m +no_defs".format(
            int(z))

    def setCurrentLayout(self,
                         film=True,
                         mapping=False,
                         autoimport=False,
                         details=False):
        self.enableItemsInLayout(self.uiFilmSelectionHorizontalLayout, film)
        self.enableItemsInLayout(self.uiMappingGridLayout, mapping)
        self.enableItemsInLayout(self.uiFootprintsVerticalLayout, mapping)
        self.enableItemsInLayout(self.uiAutoImportHorizontalLayout, autoimport)
        self.visibleItemsInLayout(self.uiMappingDetailsGridLayout, details)

    def addProjectOblique(self):
        self.addProject(self.uiProjectObliqueList,
                        self.uiProjectObliqueCombo.currentText())

    def addProjectVertical(self):
        self.addProject(self.uiProjectVerticalList,
                        self.uiProjectVerticalCombo.currentText())

    def removeProjectOblique(self):
        self.removeProject(self.uiProjectObliqueList)

    def removeProjectVertical(self):
        self.removeProject(self.uiProjectVerticalList)

    def addProject(self, editor, value):
        notInList = True
        for row in range(editor.count()):
            if value == editor.item(row).data(0):
                notInList = False
                break
        if notInList:
            editor.addItem(value)
            editor.sortItems()

    def removeProject(self, editor):
        editor.takeItem(editor.currentRow())