def transformedCornerCoordinates(self, center, rotation, xScale, yScale):
        # scale
        topLeft = QgsPointXY(-self.image.width() / 2.0 * xScale,
                             self.image.height() / 2.0 * yScale)
        topRight = QgsPointXY(self.image.width() / 2.0 * xScale,
                              self.image.height() / 2.0 * yScale)
        bottomLeft = QgsPointXY(-self.image.width() / 2.0 * xScale,
                                - self.image.height() / 2.0 * yScale)
        bottomRight = QgsPointXY(self.image.width() / 2.0 * xScale,
                                 - self.image.height() / 2.0 * yScale)

        # rotate
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)

        topLeft = self._rotate(topLeft, cosRot, sinRot)
        topRight = self._rotate(topRight, cosRot, sinRot)
        bottomRight = self._rotate(bottomRight, cosRot, sinRot)
        bottomLeft = self._rotate(bottomLeft, cosRot, sinRot)

        topLeft.set(topLeft.x() + center.x(), topLeft.y() + center.y())
        topRight.set(topRight.x() + center.x(), topRight.y() + center.y())
        bottomRight.set(bottomRight.x() + center.x(),
                        bottomRight.y() + center.y())
        bottomLeft.set(bottomLeft.x() + center.x(),
                       bottomLeft.y() + center.y())

        return (topLeft, topRight, bottomRight, bottomLeft)
Ejemplo n.º 2
0
class TestQgsPointXY(unittest.TestCase):

    def __init__(self, methodName):
        """Run once on class initialization."""
        unittest.TestCase.__init__(self, methodName)

    def setUp(self):
        self.mPoint = QgsPointXY(10.0, 10.0)

    def test_Point(self):
        myExpectedValue = 10.0
        myActualValue = self.mPoint.x()
        myMessage = 'Expected: %s Got: %s' % (myExpectedValue, myActualValue)
        assert myExpectedValue == myActualValue, myMessage

    def test_pointToString(self):
        myExpectedValue = '10, 10'
        myActualValue = self.mPoint.toString()
        myMessage = 'Expected: %s Got: %s' % (myExpectedValue, myActualValue)
        assert myExpectedValue == myActualValue, myMessage

    def test_hash(self):
        a = QgsPointXY(2.0, 1.0)
        b = QgsPointXY(2.0, 2.0)
        c = QgsPointXY(1.0, 2.0)
        d = QgsPointXY(1.0, 1.0)
        e = QgsPointXY(2.0, 1.0)
        assert a.__hash__() != b.__hash__()
        assert e.__hash__() == a.__hash__()

        mySet = set([a, b, c, d, e])
        assert len(mySet) == 4
    def transformedCornerCoordinatesFromPoint(self, startPoint, rotation,
                                              xScale, yScale):
        # startPoint is a fixed point for this new movement (rotation and
        # scale)
        # rotation is the global rotation of the image
        # xScale is the new xScale factor to be multiplied by self.xScale
        # idem for yScale
        # Calculate the coordinate of the center in a startPoint origin
        # coordinate system and apply scales
        dX = (self.center.x() - startPoint.x()) * xScale
        dY = (self.center.y() - startPoint.y()) * yScale
        # Half width and half height in the current transformation
        hW = (self.image.width() / 2.0) * self.xScale * xScale
        hH = (self.image.height() / 2.0) * self.yScale * yScale
        # Actual rectangle coordinates :
        pt1 = QgsPointXY(-hW, hH)
        pt2 = QgsPointXY(hW, hH)
        pt3 = QgsPointXY(hW, -hH)
        pt4 = QgsPointXY(-hW, -hH)
        # Actual rotation from the center
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -self.rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)
        pt1 = self._rotate(pt1, cosRot, sinRot)
        pt2 = self._rotate(pt2, cosRot, sinRot)
        pt3 = self._rotate(pt3, cosRot, sinRot)
        pt4 = self._rotate(pt4, cosRot, sinRot)
        # Second transformation
        # displacement of the origin
        pt1 = QgsPointXY(pt1.x() + dX, pt1.y() + dY)
        pt2 = QgsPointXY(pt2.x() + dX, pt2.y() + dY)
        pt3 = QgsPointXY(pt3.x() + dX, pt3.y() + dY)
        pt4 = QgsPointXY(pt4.x() + dX, pt4.y() + dY)
        # Rotation
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)
        pt1 = self._rotate(pt1, cosRot, sinRot)
        pt2 = self._rotate(pt2, cosRot, sinRot)
        pt3 = self._rotate(pt3, cosRot, sinRot)
        pt4 = self._rotate(pt4, cosRot, sinRot)
        # translate to startPoint
        pt1 = QgsPointXY(pt1.x() + startPoint.x(), pt1.y() + startPoint.y())
        pt2 = QgsPointXY(pt2.x() + startPoint.x(), pt2.y() + startPoint.y())
        pt3 = QgsPointXY(pt3.x() + startPoint.x(), pt3.y() + startPoint.y())
        pt4 = QgsPointXY(pt4.x() + startPoint.x(), pt4.y() + startPoint.y())

        return (pt1, pt2, pt3, pt4)
Ejemplo n.º 4
0
    def testPoint(self):
        point = QgsReferencedPointXY(QgsPointXY(1.0, 2.0), QgsCoordinateReferenceSystem('epsg:3111'))
        self.assertEqual(point.x(), 1.0)
        self.assertEqual(point.y(), 2.0)
        self.assertEqual(point.crs().authid(), 'EPSG:3111')

        point.setCrs(QgsCoordinateReferenceSystem('epsg:28356'))
        self.assertEqual(point.crs().authid(), 'EPSG:28356')

        # in variant
        v = QVariant(QgsReferencedPointXY(QgsPointXY(3.0, 4.0), QgsCoordinateReferenceSystem('epsg:3111')))
        self.assertEqual(v.value().x(), 3.0)
        self.assertEqual(v.value().y(), 4.0)
        self.assertEqual(v.value().crs().authid(), 'EPSG:3111')

        # to QgsPointXY
        p = QgsPointXY(point)
        self.assertEqual(p.x(), 1.0)
        self.assertEqual(p.y(), 2.0)
Ejemplo n.º 5
0
    def checkPoint(self, point):
        state = 'yellow'

        if type(point) != QgsPointXY:
            try:
                if type(point) == list and len(point) == 2:
                    point = QgsPointXY(point[0], point[1])
                else:
                    point = QgsPointXY(point)
            except TypeError:
                return state, None
            
        if self.dhm != {}:
            extent = self.dhm['extent']
            [extLx, extHy, extHx, extLy] = extent
            
            if extLx <= float(point.x()) <= extHx \
                    and extLy <= float(point.y()) <= extHy:
                state = 'green'
            else:
                state = 'red'

        return state, point
Ejemplo n.º 6
0
class QadMOVECommandClass(QadCommandClass):
    def instantiateNewCmd(self):
        """ istanzia un nuovo comando dello stesso tipo """
        return QadMOVECommandClass(self.plugIn)

    def getName(self):
        return QadMsg.translate("Command_list", "MOVE")

    def getEnglishName(self):
        return "MOVE"

    def connectQAction(self, action):
        action.triggered.connect(self.plugIn.runMOVECommand)

    def getIcon(self):
        return QIcon(":/plugins/qad/icons/move.png")

    def getNote(self):
        # impostare le note esplicative del comando
        return QadMsg.translate("Command_MOVE", "Moves the selected objects.")

    def __init__(self, plugIn):
        QadCommandClass.__init__(self, plugIn)
        self.SSGetClass = QadSSGetClass(plugIn)
        self.SSGetClass.onlyEditableLayers = True
        self.cacheEntitySet = QadCacheEntitySet()
        self.basePt = QgsPointXY()

    def __del__(self):
        QadCommandClass.__del__(self)
        del self.SSGetClass

    def getPointMapTool(self, drawMode=QadGetPointDrawModeEnum.NONE):
        if self.step == 0:  # quando si é in fase di selezione entità
            return self.SSGetClass.getPointMapTool()
        else:
            if (self.plugIn is not None):
                if self.PointMapTool is None:
                    self.PointMapTool = Qad_move_maptool(self.plugIn)
                return self.PointMapTool
            else:
                return None

    def getCurrentContextualMenu(self):
        if self.step == 0:  # quando si é in fase di selezione entità
            return None  # return self.SSGetClass.getCurrentContextualMenu()
        else:
            return self.contextualMenu

    #============================================================================
    # move
    #============================================================================
    def move(self, entity, offsetX, offsetY):
        # verifico se l'entità appartiene ad uno stile di quotatura
        if entity.whatIs() == "ENTITY":
            # sposto la geometria dell'entità
            qadGeom = entity.getQadGeom().copy()  # la copio
            qadGeom.move(offsetX, offsetY)
            f = entity.getFeature()
            f.setGeometry(fromQadGeomToQgsGeom(qadGeom, entity.crs()))
            # plugIn, layer, feature, refresh, check_validity
            if qad_layer.updateFeatureToLayer(self.plugIn, entity.layer, f,
                                              False, False) == False:
                return False
        elif entity.whatIs() == "DIMENTITY":
            if entity.deleteToLayers(self.plugIn) == False:
                return False  # cancello vecchia quota
            newDimEntity = QadDimEntity(entity)  # la copio
            if newDimEntity.move(offsetX, offsetY) == False:
                return False  # sposto la quota
            if newDimEntity.addToLayers(
                    self.plugIn) == False:  # ricreo nuva quota
                return False

        return True

    #============================================================================
    # moveGeoms
    #============================================================================
    def moveGeoms(self, newPt):
        offsetX = newPt.x() - self.basePt.x()
        offsetY = newPt.y() - self.basePt.y()

        self.plugIn.beginEditCommand("Feature moved",
                                     self.cacheEntitySet.getLayerList())

        dimElaboratedList = []  # lista delle quotature già elaborate
        entityIterator = QadCacheEntitySetIterator(self.cacheEntitySet)
        for entity in entityIterator:
            qadGeom = entity.getQadGeom()  # così inizializzo le info qad
            # verifico se l'entità appartiene ad uno stile di quotatura
            dimEntity = QadDimStyles.getDimEntity(entity)
            if dimEntity is not None:
                if appendDimEntityIfNotExisting(
                        dimElaboratedList,
                        dimEntity) == False:  # quota già elaborata
                    continue
                entity = dimEntity

            if self.move(entity, offsetX, offsetY) == False:
                self.plugIn.destroyEditCommand()
                return

        self.plugIn.endEditCommand()

    def run(self, msgMapTool=False, msg=None):
        if self.plugIn.canvas.mapSettings().destinationCrs().isGeographic():
            self.showMsg(
                QadMsg.translate(
                    "QAD",
                    "\nThe coordinate reference system of the project must be a projected coordinate system.\n"
                ))
            return True  # fine comando

        #=========================================================================
        # RICHIESTA SELEZIONE OGGETTI
        if self.step == 0:  # inizio del comando
            if self.SSGetClass.run(msgMapTool, msg) == True:
                # selezione terminata
                self.step = 1
                self.getPointMapTool().refreshSnapType(
                )  # aggiorno lo snapType che può essere variato dal maptool di selezione entità
                return self.run(msgMapTool, msg)

        #=========================================================================
        # SPOSTA OGGETTI
        elif self.step == 1:
            if self.SSGetClass.entitySet.count() == 0:
                return True  # fine comando
            self.cacheEntitySet.appendEntitySet(self.SSGetClass.entitySet)

            # imposto il map tool
            self.getPointMapTool().cacheEntitySet = self.cacheEntitySet
            self.getPointMapTool().setMode(
                Qad_move_maptool_ModeEnum.NONE_KNOWN_ASK_FOR_BASE_PT)

            keyWords = QadMsg.translate("Command_MOVE", "Displacement")
            prompt = QadMsg.translate(
                "Command_MOVE",
                "Specify base point or [{0}] <{0}>: ").format(keyWords)

            englishKeyWords = "Displacement"
            keyWords += "_" + englishKeyWords
            # si appresta ad attendere un punto o enter o una parola chiave
            # msg, inputType, default, keyWords, nessun controllo
            self.waitFor(prompt, \
                         QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                         None, \
                         keyWords, QadInputModeEnum.NONE)
            self.step = 2
            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA PUNTO BASE (da step = 1)
        elif self.step == 2:  # dopo aver atteso un punto o un numero reale si riavvia il comando
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        pass  # opzione di default "spostamento"
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            if value is None or type(value) == unicode:
                self.basePt.set(0, 0)
                self.getPointMapTool().basePt = self.basePt
                self.getPointMapTool().setMode(
                    Qad_move_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_MOVE_PT)
                # si appresta ad attendere un punto
                msg = QadMsg.translate(
                    "Command_MOVE",
                    "Specify the displacement fom the origin point 0,0 <{0}, {1}>: "
                )
                # msg, inputType, default, keyWords, nessun controllo
                self.waitFor(msg.format(str(self.plugIn.lastOffsetPt.x()), str(self.plugIn.lastOffsetPt.y())), \
                             QadInputTypeEnum.POINT2D, \
                             self.plugIn.lastOffsetPt, \
                             "", QadInputModeEnum.NONE)
                self.step = 4
            elif type(
                    value) == QgsPointXY:  # se é stato inserito il punto base
                self.basePt.set(value.x(), value.y())

                # imposto il map tool
                self.getPointMapTool().basePt = self.basePt
                self.getPointMapTool().setMode(
                    Qad_move_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_MOVE_PT)

                # si appresta ad attendere un punto o enter o una parola chiave
                # msg, inputType, default, keyWords, nessun controllo
                self.waitFor(QadMsg.translate("Command_MOVE", "Specify the second point or <use first point as displacement from the origin point 0,0>: "), \
                             QadInputTypeEnum.POINT2D, \
                             None, \
                             "", QadInputModeEnum.NONE)
                self.step = 3

            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA SECONDO PUNTO PER SPOSTAMENTO (da step = 2)
        elif self.step == 3:  # dopo aver atteso un punto o un numero reale si riavvia il comando
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        return True  # fine comando
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            if value is None:
                newPt = QgsPointXY(self.basePt.x() * 2, self.basePt.y() * 2)
                self.moveGeoms(newPt)
            elif type(
                    value
            ) == QgsPointXY:  # se é stato inserito lo spostamento con un punto
                self.moveGeoms(value)

            return True  # fine comando

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA DEL PUNTO DI SPOSTAMENTO (da step = 2)
        elif self.step == 4:  # dopo aver atteso un punto o un numero reale si riavvia il comando
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        return True  # fine comando
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            self.plugIn.setLastOffsetPt(value)
            self.moveGeoms(value)
            return True
Ejemplo n.º 7
0
def physiocap_filtrer(self,
                      src,
                      csv_sans_0,
                      csv_avec_0,
                      csv_0_seul,
                      nom_dir_segment,
                      nom_session,
                      chemin_session,
                      diametre_filtre,
                      nom_fichier_synthese,
                      err,
                      mindiam,
                      maxdiam,
                      max_sarments_metre,
                      segment_mini_vitesse,
                      segment_maxi_vitesse,
                      segment_mini_point,
                      segment_max_pdop,
                      segment_max_derive,
                      segment_pas_de_derive,
                      details,
                      eer,
                      eec,
                      d,
                      hv,
                      laProjectionCRS,
                      laProjectionTXT,
                      version_3="NO"):
    """Fonction de traitement.
    Filtre ligne brute par ligne brute les données de source (src) pour les valeurs 
    comprises entre mindiam et maxdiam et verifie si on n'a pas atteint le max_sarments_metre.
    Le résultat est écrit au fur et à mesure dans les fichiers 
    csv_sans_0, csv_avec_0 et depuis v3 dans csv_0_seul mais aussi diametre_filtre 
    La synthese est allongé
    "details" pilote l'ecriture de 5 parametres ou de la totalité des 10 parametres 
    """
    leModeDeTrace = self.fieldComboModeTrace.currentText()
    # S'il n'existe pas de données parcellaire, le script travaille avec les données brutes
    titre = ""
    titre_partie_details = " ; NBSARMM2 ; NBSARCEP ; BIOMMM2 ; BIOMGM2 ; BIOMGCEP "
    if version_3 == "NO":
        titre_sans_detail = "X ; Y ; XL93 ; YL93 ; NBSARM ; DIAM ; BIOM ; DATE ; VITESSE"
    else:  # Ajout en version 3 de l'altitude
        titre_sans_detail = "ID;X ; Y ; XL93 ; YL93 ; ALTITUDE; PDOP ; DISTANCE; DERIVE; AZIMUTH; NBSART; NBSARM ; DIAM ; BIOM ; DATE ; VITESSE"

    if details == "NO":
        titre = titre_sans_detail
    else:
        #S'il existe des données parcellaire, le script travaille avec les données brutes et les données calculées
        titre = titre_sans_detail + titre_partie_details

    # Ecriture de l'entete pour tous les cas
    csv_sans_0.write("{0}\n".format(titre))
    csv_avec_0.write("{0}\n".format(titre))
    csv_0_seul.write("{0}\n".format(titre))

    # Pour progress bar entre 15 et 40
    lignes_brutes = src.readlines()
    max_lignes = len(lignes_brutes)
    progress_step = int(max_lignes / 25)
    #physiocap_log("Bar step: " + str( progress_step), leModeDeTrace)
    progress_bar = 15
    barre = 1
    precedent = []
    on_coupe = "PREMIER"
    segment_en_cours = []
    gid_en_cours = []
    gid_sans_mesure = []
    manquant_en_cours = []
    info_en_cours = {}
    derive_en_cours = []
    mes_lignes_sans_coupure = []
    info_lignes_sans_coupure = []
    nombre_segments_sans_coupure = 0

    # Récuperer le CRS choisi, les extensions et le calculateur de distance
    distancearea, EXT_CRS_SHP, EXT_CRS_PRJ, EXT_CRS_RASTER, \
    laProjectionCRS, laProjectionTXT, EPSG_NUMBER = \
            physiocap_quelle_projection_et_lib_demandee( self)

    for numero_point, ligne_brute in enumerate(lignes_brutes):
        if not ligne_brute: break

        # Progress BAR de 15 à 40 %
        if (numero_point > barre * progress_step):
            progress_bar = progress_bar + 1
            barre = barre + 1
            self.progressBar.setValue(progress_bar)

        comptage = ligne_brute.count(",")  # compte le nombre de virgules
        result = ligne_brute.split(",")  # split en fonction des virgules

        try:  # Transform GPS en L93
            # on extrait les Colonnnes 1 à 8 (XY, puis GPS jusqu'à vitesse)
            # en on les transforme en float
            ### On utilise XY[0 et 1] puis Altitude XY[2] Pdop XY[5] et vitesse XY[7]
            XY = [float(x) for x in result[1:9]]

            # Puis on transforme les WGS84 (du capteur) en L93 (probablement utile)
            # TODO: ?V3.x autres EPSG ? et eviter cet appel dans la boucle
            crsDest = QgsCoordinateReferenceSystem.fromEpsgId(
                EPSG_NUMBER_L93)  # Lambert 93
            crsSrc = QgsCoordinateReferenceSystem.fromEpsgId(
                EPSG_NUMBER_GPS)  # WGS 84
            transformer = QgsCoordinateTransform()
            transformer.setSourceCrs(crsSrc)
            transformer.setDestinationCrs(crsDest)
            if not transformer.isValid():
                raise physiocap_exception_no_transform(numero_point)

            # On assure la tranformation par compatibilité du CVS en GPS et L93
            point_L93 = transformer.transform(QgsPointXY(XY[0], XY[1]))
            XY_L93 = [point_L93.x(), point_L93.y()]
            # aMsg = "Transformation faite X {0} et Y {1}". \
            #           format( XY_L93[0], XY_L93[1])
            # physiocap_log( aMsg , leModeDeTrace)
            # physiocap_log( "La projection {0}". format( laProjectionTXT), leModeDeTrace)
            if (laProjectionTXT == "GPS"):
                le_point_projete = QgsPointXY(XY[0], XY[1])
            else:  # Pour le moment seulement L93
                le_point_projete = QgsPointXY(XY_L93[0], XY_L93[1])
            XY_projete = [le_point_projete.x(), le_point_projete.y()]

        except:
            aMsg = "{0} Erreur bloquante durant tranformation SCR : pour la ligne brute numéro {1}". \
                format ( PHYSIOCAP_STOP,  numero_point)
            physiocap_error(self, aMsg)
            err.write(aMsg)  # on écrit la ligne dans le fichier ERREUR
            # monter directemenr exception
            raise

        # TODO: ?V3.x marquer les points à conserver (non filtré et dans un segment)
        # pour creer un 4eme csv POINTS_VALIDES
        # ce qui reste compliqué pour les segments courts que je ne connais pas encore

        try:  # SEGMENT si V3
            # On regarde les points sans mesure avant SEGMENT
            diams = [float(x) for x in result[9:NB_VIRGULES + 1]
                     ]  # on extrait les diams et on les transforme en float
            diamsF = [
                i for i in diams if i > mindiam and i < maxdiam
            ]  # on filtre les diams avec les paramètres entrés ci-dessus
            derive = 0.0
            ma_distance = 0.0
            mon_azimuth = 0.0
            # SEGMENT si V3
            if version_3 == "NO":
                pass
            elif precedent == [] or on_coupe == "PREMIER":
                #physiocap_log( "SEGMENT ==>> point {0} PREMIER".format( numero_point), TRACE_SEGMENT + "_DEBUG")
                # Stocker le premier point pour comparer au prochain tour
                # et la Date début
                precedent = XY_projete
                # TODO: ?V3.y passage en 3D mettre en Z la dérive
                info_en_cours[DATE_DEBUT] = result[0]
                if len(diamsF) == 0:
                    # On ne STOCKE pas les points sans MESURE
                    gid_sans_mesure.append(numero_point)
                else:
                    gid_en_cours.append(numero_point)
                derive_en_cours.append(0)
                segment_en_cours.append(QgsPointXY(le_point_projete))
                on_coupe = "NON"
            else:
                # On vérifie qualité de mesure
                # ################################################
                # Filtre des points pour découpage en SEGMENT ou
                # pour montrer les limites de la capture
                # On cherche si le point est dans la zone attendue
                # calcul basé sur la vitesse annoncé par GPS sur
                # le point en cours et PDOP
                # #################################################

                # Quand vitesse plus de 2.5 et moins de 8 et pdop reste cohérent segment_max_pdop
                if XY[7] >= segment_mini_vitesse and XY[
                        7] < segment_maxi_vitesse and XY[5] < segment_max_pdop:
                    # on est en vitesse de croisière
                    # Calcul de la distance théorique par rapport au precedent
                    # Introduire un calcul de distance length et l'azimuth
                    le_point_precedent = QgsPointXY(precedent[0], precedent[1])
                    ma_distance = distancearea.measureLine(
                        le_point_projete, le_point_precedent)
                    mon_azimuth = le_point_projete.azimuth(le_point_precedent)
                    # TODO: ?V3.y Traiter l'azimuth depuis le début du segment

                    distance_theorique = XY[
                        7] * 1000 / 3600  # On suppose une seconde d'avancement
                    derive = (ma_distance -
                              distance_theorique) / distance_theorique * 100
                    #                    physiocap_log( "Vitesse {3} Distance théorique {1:.2f} et ma distance {0:.2f}  \
                    #                        sont distantes de \n  {2:.1f} soit une derive de {4:.1f}".\
                    #                            format(ma_distance,  distance_theorique, \
                    #                            ( ma_distance - distance_theorique),  XY[7],  derive ), \
                    #                            TRACE_SEGMENT)
                    #remplacer le precedent par l'actuel
                    precedent = XY_projete

                    # Vérification de dérive
                    if abs(derive) > (segment_max_derive +
                                      (2 * segment_pas_de_derive)):
                        physiocap_log( "{0} DECOUPAGE point {1} : l'avancée dérive GRAVE ===> {2:.1f} ! ".\
                            format(PHYSIOCAP_WARNING,   numero_point,   derive ), \
                            TRACE_SEGMENT_DECOUPES)
                        on_coupe = "OUI"
                    elif abs(derive) > (segment_max_derive +
                                        segment_pas_de_derive):
                        physiocap_log( "{0} DECOUPAGE point {1} : l'avancée dérive de PLUS d'un PAS ==> {2:.1f} ! ".\
                            format(PHYSIOCAP_WARNING,   numero_point,   derive ), \
                            TRACE_SEGMENT_DECOUPES)
                        on_coupe = "OUI"
                    elif abs(derive) > segment_max_derive:
                        physiocap_log("{0} DECOUPAGE point {1} : l'avancée dérive => {2:.1f} ! ".\
                            format(PHYSIOCAP_WARNING,   numero_point,   derive ), \
                            TRACE_SEGMENT_DECOUPES)
                        on_coupe = "OUI"
                    else:
                        # La derive < segment_max_derive en % :
                        # Stocker ligne "droite" = orientation et sens d'avancement
                        # Créer un flux des avancement stables pour identifier l'écartement problable
                        # Ajouter un point à la ligne
                        segment_en_cours.append(QgsPointXY(le_point_projete))
                        info_en_cours[DATE_FIN] = result[0]
                        if len(diamsF) == 0:
                            # On ne STOCKE pas les points sans MESURE
                            gid_sans_mesure.append(numero_point)
                        else:
                            gid_en_cours.append(numero_point)
                        derive_en_cours.append(derive)
                        on_coupe = "NON"

                else:  # Cas d'arret (fin de rang) ou pdop
                    on_coupe = "OUI"
                    # Tracer cas decoupe vitessse
                    if XY[7] < segment_mini_vitesse:
                        if len(segment_en_cours) > 0:
                            physiocap_log("{0} DECOUPAGE point {1} : vitesse {2:.1f} alors que min est {3:.1f}! ".\
                            format(PHYSIOCAP_WARNING, numero_point, XY[7],  segment_mini_vitesse), \
                            TRACE_SEGMENT_DECOUPES)
                    if XY[7] > segment_maxi_vitesse:
                        if len(segment_en_cours) > 0:
                            physiocap_log("{0} DECOUPAGE point {1} : vitesse {2:.1f} que max est {3:.1f}! ".\
                            format(PHYSIOCAP_WARNING, numero_point, XY[7],  segment_maxi_vitesse), \
                            TRACE_SEGMENT_DECOUPES)
                    # Tracer cas decoupe pdop
                    if XY[5] >= segment_max_pdop:
                        physiocap_log("{0} DECOUPAGE point {1} : pdop {2:.1f} max est  {3:.1f}! ".\
                            format(PHYSIOCAP_WARNING, numero_point, XY[5], segment_max_pdop ), \
                            TRACE_SEGMENT_DECOUPES)

                if on_coupe == "OUI":  # Cas de fin de ligne
                    if len(segment_en_cours) > segment_mini_point:
                        # Le segment est à garder
                        manquant_en_cours.append(numero_point)
                        # Mémoriser la ligne des points cohérents
                        mes_lignes_sans_coupure.append(segment_en_cours)
                        info_en_cours[NUM_SEG] = nombre_segments_sans_coupure
                        info_en_cours[DATE_FIN] = result[0]
                        info_en_cours[NOMBRE] = len(segment_en_cours)
                        info_en_cours[GID_GARDE] = gid_en_cours
                        info_en_cours[GID_SANS_MESURE] = gid_sans_mesure
                        info_en_cours[GID_TROU] = manquant_en_cours
                        info_en_cours[DERIVE] = np.mean(derive_en_cours)
                        # stocker jour_heure début et fin et derive moyenne ...
                        info_lignes_sans_coupure.append(info_en_cours)
                        nombre_segments_sans_coupure = nombre_segments_sans_coupure + 1
                        manquant_en_cours = []

                    else:
                        # Vérifier les gid_sans_mesure
                        # On ne perd pas les points manquants qui seront ajouter dans GID_TROU pour le segment suivant
                        # On aditionne des gid en cours avec les manquants...
                        for gid_perdu in gid_en_cours:
                            manquant_en_cours.append(gid_perdu)
                        manquant_en_cours.append(numero_point)

                        if len(segment_en_cours) > 0:
                            physiocap_log("{0} SEGMENT {1} IGNORE : trop cours == {2} points, le mini est {3} ".\
                                format(PHYSIOCAP_WARNING, nombre_segments_sans_coupure,
                                len(segment_en_cours),  segment_mini_point ),
                                TRACE_SEGMENT_DECOUPES)

                    info_en_cours = {}
                    gid_en_cours = []
                    gid_sans_mesure = []
                    precedent = []
                    on_coupe = "PREMIER"
                    segment_en_cours = []

        except:
            aMsg = "{0} Erreur bloquante durant extraction des segments : pour la ligne brute numéro {1}". \
                format ( PHYSIOCAP_STOP,  numero_point)
            physiocap_error(self, aMsg)
            err.write(aMsg)  # on écrit la ligne dans le fichier ERREUR
            # monter directemenr exception
            raise

        try:  # On filtre vraiement
            if details == "NO":
                if len(
                        diamsF
                ) == 0:  # si le nombre de diamètre après filtrage = 0 alors pas de mesures
                    nbsarm = 0
                    nbsart = 0
                    diam = 0
                    biom = 0
                    # Ecrire les seuls_0 et aussi les points avec 0
                    if version_3 == "NO":
                        csv_0_seul.write("%.7f%s%.7f%s%.7f%s%.7f%s%i%s%i%s%i%s%s%s%0.2f\n" \
                            %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam ,";",biom,";",result[0],";",XY[7]))  # on écrit la ligne dans le csv avec ZERO SEUL
                        csv_avec_0.write("%.7f%s%.7f%s%.7f%s%.7f%s%i%s%i%s%i%s%s%s%0.2f\n" \
                            %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam ,";",biom,";",result[0],";",XY[7]))  # on écrit la ligne dans le fcsv avec ZERO
                    else:  # V3 on ajoute altitude, pdop, distance au point precedent et la dérive
                        # puis AZIMUTH et NBSART = 0
                        a_ecrire = "{0};{1:.7f};{2:.7f};{3:.7f};{4:.7f}; \
                                    {5:.2f};{6:.2f};{7:.2f};{8:.2f};{9:.2f};0;0;0;0;{10};{11:.7f}\n"                                                                                                    . \
                                format(numero_point, XY[0],XY[1],XY_L93[0],XY_L93[1], \
                                    XY[2],XY[5],ma_distance,derive,mon_azimuth,       result[0],XY[7])
                        csv_0_seul.write(a_ecrire)
                        csv_avec_0.write(a_ecrire)

                elif comptage == NB_VIRGULES and len(
                        diamsF
                ) > 0:  # si le nombre de diamètre après filtrage != 0 alors mesures
                    # Nombre sarment total
                    nbsart = len(diamsF)
                    if XY[7] != 0:  # Si vitesse non nulle
                        nbsarm = len(diamsF) / (XY[7] * 1000 / 3600)
                    else:
                        nbsarm = 0
                    if nbsarm > 1 and nbsarm < max_sarments_metre:
                        diam = sum(diamsF) / len(diamsF)
                        biom = 3.1416 * (diam / 2) * (diam / 2) * nbsarm
                        if version_3 == "NO":
                            csv_avec_0.write("%.7f%s%.7f%s%.7f%s%.7f%s%0.2f%s%.2f%s%.2f%s%s%s%0.2f\n" \
                                %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam,";",biom,";",result[0],";",XY[7])) # on écrit la ligne dans le csv avec ZERO
                            csv_sans_0.write("%.7f%s%.7f%s%.7f%s%.7f%s%0.2f%s%.2f%s%.2f%s%s%s%0.2f\n" \
                                %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam,";",biom,";",result[0],";",XY[7])) # on écrit la ligne dans le csv sans ZERO
                        else:  # V3 on ajoute altitude, pdop,distance au point precedent et risque de dérive
                            # puis AZIMUTH et NBSART
                            a_ecrire = "{0};{1:.7f};{2:.7f};{3:.7f};{4:.7f}; \
                                {5:.2f};{6:.2f};{7:.2f};{8:.2f};{9:.2f};{10}; \
                                {11:.2f}; {12:.2f};{13:.2f};{14};{15:.7f}\n"                                                                            . \
                                format( numero_point, XY[0],  XY[1], XY_L93[0] ,XY_L93[1], \
                                    XY[2],XY[5],ma_distance,derive,mon_azimuth,nbsart, \
                                    nbsarm,diam,biom,result[0],XY[7])
                            csv_avec_0.write(a_ecrire)
                            csv_sans_0.write(a_ecrire)

                        for n in range(len(diamsF)):
                            diametre_filtre.write("%f%s" % (diamsF[n], ";"))
            elif details == "YES":
                if len(
                        diamsF
                ) == 0:  # si le nombre de diamètre après filtrage = 0 alors pas de mesures
                    nbsart = 0
                    nbsarm = 0
                    diam = 0
                    biom = 0
                    nbsarmm2 = 0
                    nbsarcep = 0
                    biommm2 = 0
                    biomgm2 = 0
                    biomgcep = 0
                    if version_3 == "NO":
                        csv_0_seul.write("%.7f%s%.7f%s%.7f%s%.7f%s%i%s%i%s%i%s%s%s%0.2f%s%i%s%i%s%i%s%i%s%i\n" \
                        %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam ,";",biom,";",result[0],";",XY[7],";",nbsarmm2,";",nbsarcep,";",biommm2,";",biomgm2,";",biomgcep))
                        csv_avec_0.write("%.7f%s%.7f%s%.7f%s%.7f%s%i%s%i%s%i%s%s%s%0.2f%s%i%s%i%s%i%s%i%s%i\n" \
                        %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam ,";",biom,";",result[0],";",XY[7],";",nbsarmm2,";",nbsarcep,";",biommm2,";",biomgm2,";",biomgcep))
                    else:  # Q3 on ajoute altitude, pdop, distance au point precedent et la dérive
                        # puis AZIMUTH et NBSART = 0
                        a_ecrire = "{0};{1:.7f};{2:.7f};{3:.7f};{4:.7f}; \
                                    {5:.2f};{6:.2f};{7:.2f};{8:.2f};{9:.2f};0;0;0;0;{10};{11:.7f}"                                                                                                  . \
                                format(numero_point, XY[0],XY[1],XY_L93[0],XY_L93[1],
                                    XY[2],XY[5],ma_distance,derive,mon_azimuth,       result[0],XY[7])
                        a_ecrire_detail = ";0;0;0;0;0\n"
                        a_ecrire_complet = a_ecrire + a_ecrire_detail
                        csv_0_seul.write(a_ecrire_complet)
                        csv_avec_0.write(a_ecrire_complet)

                elif comptage == NB_VIRGULES and len(
                        diamsF
                ) > 0:  # si le nombre de diamètre après filtrage != 0 alors mesures
                    nbsart = len(diamsF)
                    if XY[7] != 0:
                        nbsarm = len(diamsF) / (XY[7] * 1000 / 3600)
                    else:
                        nbsarm = 0
                    if nbsarm > 1 and nbsarm < max_sarments_metre:
                        diam = sum(diamsF) / len(diamsF)
                        biom = 3.1416 * (diam / 2) * (diam / 2) * nbsarm
                        nbsarmm2 = nbsarm / eer * 100
                        nbsarcep = nbsarm * eec / 100
                        biommm2 = biom / eer * 100
                        biomgm2 = biom * d * hv / eer
                        biomgcep = biom * d * hv * eec / 100 / 100
                        if version_3 == "NO":
                            csv_avec_0.write("%.7f%s%.7f%s%.7f%s%.7f%s%.2f%s%.2f%s%.2f%s%s%s%.2f%s%.2f%s%.2f%s%.2f%s%.2f%s%.2f\n" \
                            %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam ,";",biom,";",result[0],";",XY[7],";",nbsarmm2,";",nbsarcep,";",biommm2,";",biomgm2,";",biomgcep))
                            csv_sans_0.write("%.7f%s%.7f%s%.7f%s%.7f%s%.2f%s%.2f%s%.2f%s%s%s%.2f%s%.2f%s%.2f%s%.2f%s%.2f%s%.2f\n" \
                            %(XY[0],";",XY[1],";",XY_L93[0],";",XY_L93[1],";",nbsarm,";",diam ,";",biom,";",result[0],";",XY[7],";",nbsarmm2,";",nbsarcep,";",biommm2,";",biomgm2,";",biomgcep))
                        else:  # Q3 on ajoute altitude, pdop,distance au point precedent et risque de dérive
                            # puis AZIMUTH et NBSART
                            a_ecrire = "{0};{1:.7f};{2:.7f};{3:.7f};{4:.7f}; \
                                {5:.2f};{6:.2f};{7:.2f};{8:.2f};{9:.2f};{10}; \
                                {11:.2f}; {12:.2f};{13:.2f};{14};{15:.7f}"                                                                          . \
                                format( numero_point, XY[0],  XY[1], XY_L93[0] ,XY_L93[1],
                                    XY[2],XY[5],ma_distance,derive,mon_azimuth,nbsart,
                                    nbsarm,diam,biom,result[0],XY[7])
                            a_ecrire_detail = ";{0:.7f};{1:.7f};{2:.7f};{3:.7f};{4:.7f}\n". \
                                format( nbsarmm2, nbsarcep,biommm2,biomgm2,biomgcep)
                            a_ecrire_complet = a_ecrire + a_ecrire_detail
                            csv_avec_0.write(a_ecrire_complet)
                            csv_sans_0.write(a_ecrire_complet)

                        # Memorise diametre filtré pour histo
                        for n in range(len(diamsF)):
                            diametre_filtre.write("%f%s" % (diamsF[n], ";"))

        except:
            aMsg = "{0} Erreur bloquante durant filtrage : pour la ligne brute numéro {1}". \
                format ( PHYSIOCAP_STOP,  numero_point)
            physiocap_error(self, aMsg)
            err.write(aMsg)  # on écrit la ligne dans le fichier ERREUR
            # Pour monter directement exception
            raise physiocap_exception_err_csv(nom_court_csv_concat)

    if version_3 == "NO":
        vecteur_segment = None
        vecteur_segment_brise = None
    else:
        if len(info_lignes_sans_coupure) != nombre_segments_sans_coupure:
            physiocap_error( self, "{0} on a trouvé {1} segments et {2} infos". \
            format( PHYSIOCAP_INFO,  nombre_segments_sans_coupure, len( info_lignes_sans_coupure)))
            raise physiocap_exception_calcul_segment_invalid(
                "Segment et leurs infos sont différents")
        i = 0
        for info_segment in info_lignes_sans_coupure:
            i = i + 1
            try:
                physiocap_log( "{0} Segment {1} contient {2} points et une dérive moyenne de {3:.1f}". \
                    format( PHYSIOCAP_INFO,  i,  info_segment[NOMBRE],  info_segment[DERIVE]),  TRACE_SEGMENT)
                physiocap_log( "gid des points :{0} \net les sans mesure\n{1}". \
                    format(info_segment[GID_GARDE],  info_segment[GID_SANS_MESURE]), TRACE_SEGMENT)
            except:
                physiocap_error(
                    self, "Problème : manque attribut dans info segment")
                raise physiocap_exception_calcul_segment_invalid(
                    "Un  attribut n'est pas présent")
#            try:
#                physiocap_log( "Date début {0} et fin {1}". \
#                format( info_segment[DATE_DEBUT], info_segment[DATE_FIN]),
#                TRACE_SEGMENT)
#            except:
#                physiocap_error( self, "Problème : pas de date dans le segment")
#                raise physiocap_exception_calcul_segment_invalid( "Date non présente")

# Creer les lignes simplifiés ou brisés de ces segments et infos
        vecteur_segment = physiocap_segment_vers_vecteur(
            self, chemin_session, nom_dir_segment, nom_session,
            mes_lignes_sans_coupure, info_lignes_sans_coupure, version_3)
        vecteur_segment_brise = physiocap_segment_vers_vecteur(
            self, chemin_session, nom_dir_segment, nom_session,
            mes_lignes_sans_coupure, info_lignes_sans_coupure, version_3,
            "BRISE")

    physiocap_log( "{0} {1} Fin du filtrage OK des {2} lignes.". \
        format( PHYSIOCAP_INFO, PHYSIOCAP_UNI, str(numero_point - 1)), leModeDeTrace)
    return vecteur_segment, vecteur_segment_brise
Ejemplo n.º 8
0
    def result(self, result):
        # See if OK was pressed
        if result:
            project = QgsProject.instance()
            # First get all the values of the GUI items
            crs_input = self.dlg.crs_input.crs()
            crs_out = QgsCoordinateReferenceSystem(
                'EPSG:4326')  # we need this to be WGS84 for Nominatim
            lineedit_text = self.dlg.lineedit_xy.value()

            # Protect the free text field for coordinates from generic user failure
            try:
                lineedit_yx = [
                    float(coord.strip()) for coord in lineedit_text.split(',')
                ]
            except:
                QMessageBox.critical(
                    self.iface.mainWindow(), 'QuickAPI error',
                    "Did you really specify a coordinate in comma-separated Lat/Long?\nExiting..."
                )
                return

            # Create a Point and transform if necessary
            point = QgsPointXY(*reversed(lineedit_yx))
            if crs_input.authid() != 'EPSG:4326':
                xform = QgsCoordinateTransform(crs_input, crs_out, project)
                point_transform = xform.transform(point)
                point = point_transform

            # Set up the GET Request to Nominatim
            query = QUrlQuery()
            query.addQueryItem('lat', str(point.y()))
            query.addQueryItem('lon', str(point.x()))
            query.addQueryItem('format', 'json')

            url = QUrl('https://nominatim.openstreetmap.org/reverse')
            url.setQuery(query)

            request = QNetworkRequest(url)
            request.setHeader(QNetworkRequest.UserAgentHeader,
                              '*****@*****.**')

            nam = QgsNetworkAccessManager()
            response: QgsNetworkReplyContent = nam.blockingGet(request)

            # Only process if HTTP status code is 200
            status_code = response.attribute(
                QNetworkRequest.HttpStatusCodeAttribute)
            if status_code == 200:
                # Get the content of the response and process it
                response_json = json.loads(bytes(response.content()))
                if response_json.get('error'):
                    QMessageBox.critical(
                        self.iface.mainWindow(), "Quick API error",
                        "The request was not processed succesfully!\n\n"
                        "Message:\n"
                        "{}".format(response_json['error']))
                    return

                x = float(response_json['lon'])
                y = float(response_json['lat'])
                address = response_json['display_name']
                license = response_json['licence']

                # Create the output memory layer
                layer_out = QgsVectorLayer(
                    "Point?crs=EPSG:4326&field=address:string&field=license:string",
                    "Nominatim Reverse Geocoding", "memory")

                # Create the output feature (only one here)
                point_out = QgsPointXY(x, y)
                feature = QgsFeature()
                feature.setGeometry(QgsGeometry.fromPointXY(point_out))
                feature.setAttributes([address, license])

                # Add feature to layer and layer to map
                layer_out.dataProvider().addFeature(feature)
                layer_out.updateExtents()
                project.addMapLayer(layer_out)

                # build bbox for auto-zoom feature
                bbox = [float(coord) for coord in response_json['boundingbox']]
                min_y, max_y, min_x, max_x = bbox
                bbox_geom = QgsGeometry.fromRect(
                    QgsRectangle(min_x, min_y, max_x, max_y))

                # Transform bbox if map canvas has a different CRS
                if project.crs().authid() != 'EPSG:4326':
                    xform = QgsCoordinateTransform(crs_out, project.crs(),
                                                   project)
                    bbox_geom.transform(xform)

                self.iface.mapCanvas().zoomToFeatureExtent(
                    QgsRectangle.fromWkt(bbox_geom.asWkt()))
class FreehandRasterGeoreferencerLayer(QgsPluginLayer):

    LAYER_TYPE = "FreehandRasterGeoreferencerLayer"
    transformParametersChanged = pyqtSignal(tuple)

    def __init__(self, plugin, filepath, title, screenExtent):
        QgsPluginLayer.__init__(
            self, FreehandRasterGeoreferencerLayer.LAYER_TYPE, title
        )
        self.plugin = plugin
        self.iface = plugin.iface

        self.title = title
        self.filepath = filepath
        self.screenExtent = screenExtent
        self.history = []
        # set custom properties
        self.setCustomProperty("title", title)
        self.setCustomProperty("filepath", self.filepath)

        self.setValid(True)

        self.setTransparency(LayerDefaultSettings.TRANSPARENCY)
        self.setBlendModeByName(LayerDefaultSettings.BLEND_MODE)

        # dummy data: real init is done in intializeLayer
        self.center = QgsPointXY(0, 0)
        self.rotation = 0.0
        self.xScale = 1.0
        self.yScale = 1.0

        self.error = False
        self.initializing = False
        self.initialized = False
        self.initializeLayer(screenExtent)
        self._extent = None

        self.provider = FreehandRasterGeoreferencerLayerProvider(self)

    def dataProvider(self):
        # issue with DBManager if the dataProvider of the QgsLayerPlugin
        # returns None
        return self.provider

    def setScale(self, xScale, yScale):
        self.xScale = xScale
        self.yScale = yScale

    def setRotation(self, rotation):
        # 3 decimals ought to be enough for everybody
        rotation = round(rotation, 3)
        # keep in -180,180 interval
        if rotation < -180:
            rotation += 360
        if rotation > 180:
            rotation -= 360
        self.rotation = rotation

    def setCenter(self, center):
        self.center = center

    def commitTransformParameters(self):
        QgsProject.instance().setDirty(True)
        self._extent = None
        self.setCustomProperty("xScale", self.xScale)
        self.setCustomProperty("yScale", self.yScale)
        self.setCustomProperty("rotation", self.rotation)
        self.setCustomProperty("xCenter", self.center.x())
        self.setCustomProperty("yCenter", self.center.y())
        self.transformParametersChanged.emit(
            (self.xScale, self.yScale, self.rotation, self.center)
        )

    def reprojectTransformParameters(self, oldCrs, newCrs):
        transform = QgsCoordinateTransform(oldCrs, newCrs, QgsProject.instance())

        newCenter = transform.transform(self.center)
        newExtent = transform.transform(self.extent())

        # transform the parameters except rotation
        # TODO rotation could be better handled (maybe check rotation between
        # old and new extent)
        # but not really worth the effort ?
        self.setCrs(newCrs)
        self.setCenter(newCenter)
        self.resetScale(newExtent.width(), newExtent.height())

    def resetTransformParametersToNewCrs(self):
        """
        Attempts to keep the layer on the same region of the map when
        the map CRS is changed
        """
        oldCrs = self.crs()
        newCrs = self.iface.mapCanvas().mapSettings().destinationCrs()
        self.reprojectTransformParameters(oldCrs, newCrs)
        self.commitTransformParameters()

    def setupCrsEvents(self):
        layerId = self.id()

        def removeCrsChangeHandler(layerIds):
            if layerId in layerIds:
                try:
                    self.iface.mapCanvas().destinationCrsChanged.disconnect(
                        self.resetTransformParametersToNewCrs
                    )
                except Exception:
                    pass
                try:
                    QgsProject.instance().disconnect(removeCrsChangeHandler)
                except Exception:
                    pass

        self.iface.mapCanvas().destinationCrsChanged.connect(
            self.resetTransformParametersToNewCrs
        )
        QgsProject.instance().layersRemoved.connect(removeCrsChangeHandler)

    def setupCrs(self):
        mapCrs = self.iface.mapCanvas().mapSettings().destinationCrs()
        self.setCrs(mapCrs)

        self.setupCrsEvents()

    def repaint(self):
        self.repaintRequested.emit()

    def transformParameters(self):
        return (self.center, self.rotation, self.xScale, self.yScale)

    def initializeLayer(self, screenExtent=None):
        if self.error or self.initialized or self.initializing:
            return

        if self.filepath is not None:
            # not safe...
            self.initializing = True
            absPath = self.getAbsoluteFilepath()

            if not os.path.exists(absPath):
                # TODO integrate with BadLayerHandler ?
                loadErrorDialog = LoadErrorDialog(absPath)
                result = loadErrorDialog.exec_()
                if result == 1:
                    # absolute
                    absPath = loadErrorDialog.lineEditImagePath.text()
                    # to relative if needed
                    self.filepath = utils.toRelativeToQGS(absPath)
                    self.setCustomProperty("filepath", self.filepath)
                    QgsProject.instance().setDirty(True)
                else:
                    self.error = True

                del loadErrorDialog

            imageFormat = utils.imageFormat(absPath)
            if imageFormat == "pdf":
                s = QSettings()
                oldValidation = s.value("/Projections/defaultBehavior")
                s.setValue(
                    "/Projections/defaultBehavior", "useGlobal"
                )  # for not asking about crs
                layer = QgsRasterLayer(absPath, os.path.basename(absPath))
                self.image = layer.previewAsImage(QSize(layer.width(), layer.height()))
                s.setValue("/Projections/defaultBehavior", oldValidation)
            else:
                has_corrected = False
                if imageFormat == "tif":
                    # other than TIFF => assumes can be loaded by Qt
                    has_corrected = self.preCheckImage(absPath)
                if has_corrected:
                    # image already loaded by preCheckImage
                    self.showBarMessage(
                        "Raster changed",
                        "Raster content has been transformed for display in the "
                        "plugin. "
                        "When exporting, select the 'Only export world file' checkbox.",
                        Qgis.Warning,
                        10,
                    )
                else:
                    reader = QImageReader(absPath)
                    self.image = reader.read()

            self.initialized = True
            self.initializing = False

            self.setupCrs()

            if screenExtent:
                # constructor called from AddLayer action
                # if not, layer loaded from QGS project file

                # check if image already has georef info
                # use GDAL
                dataset = gdal.Open(absPath, gdal.GA_ReadOnly)
                georef = None
                if dataset:
                    georef = dataset.GetGeoTransform()

                if georef and not self.is_default_geotransform(georef):
                    self.initializeExistingGeoreferencing(dataset, georef)
                else:
                    # init to default params
                    self.setCenter(screenExtent.center())
                    self.setRotation(0.0)

                    sw = screenExtent.width()
                    sh = screenExtent.height()

                    self.resetScale(sw, sh)

                    self.commitTransformParameters()

    def preCheckImage(self, filepath):
        nbands, datatype, width, height = gdal_utils.format(filepath)

        pixels = None
        if nbands not in (1, 3):
            pixels = gdal_utils.pixels(filepath)
            if nbands > 3:
                # first 3
                pixels = pixels[:3]
                nbands = 3

            if nbands == 2:
                # remove band 2
                pixels = pixels[0][np.newaxis, ...]
                nbands = 1

        if datatype != "Byte":
            pixels = pixels if pixels is not None else gdal_utils.pixels(filepath)

            bands = np.empty(np.shape(pixels), dtype=np.uint8)
            for i in range(nbands):
                band_pixels = pixels[i]
                bands[i] = gdal_utils.to_byte(band_pixels)
            pixels = bands

        if pixels is not None:
            # some transformation done# band at the end
            pixels = np.transpose(pixels, [1, 2, 0])
            pixels = pixels.ravel()

            if nbands == 1:
                # monochrome
                format = QImage.Format_Grayscale8
                bytesPerLine = width
            else:
                format = QImage.Format_RGB888
                bytesPerLine = 3 * width

            # Byte
            qImg = QImage(pixels, width, height, bytesPerLine, format)
            self.image = qImg

            return True

        return False

    def initializeExistingGeoreferencing(self, dataset, georef):
        # georef can have scaling, rotation or translation
        rotation = 180 / math.pi * -math.atan2(georef[4], georef[1])
        sx = math.sqrt(georef[1] ** 2 + georef[4] ** 2)
        sy = math.sqrt(georef[2] ** 2 + georef[5] ** 2)
        i_center_x = self.image.width() / 2
        i_center_y = self.image.height() / 2
        center = QgsPointXY(
            georef[0] + georef[1] * i_center_x + georef[2] * i_center_y,
            georef[3] + georef[4] * i_center_x + georef[5] * i_center_y,
        )

        qDebug(repr(rotation) + " " + repr((sx, sy)) + " " + repr(center))

        self.setRotation(rotation)
        self.setCenter(center)
        # keep yScale positive
        self.setScale(sx, sy)
        self.commitTransformParameters()

        crs_wkt = dataset.GetProjection()
        message_shown = False
        if crs_wkt:
            qcrs = QgsCoordinateReferenceSystem(crs_wkt)
            # TODO check change
            if qcrs.description() != self.crs().description():
                # reproject
                try:
                    self.reprojectTransformParameters(qcrs, self.crs())
                    self.commitTransformParameters()
                    self.showBarMessage(
                        "Transform parameters changed: ",
                        "Found existing georeferencing in raster but "
                        "its CRS does not match the CRS of the map. "
                        "Reprojected the extent.",
                        Qgis.Warning,
                        25,
                    )
                    message_shown = True
                except Exception as ex:
                    QgsMessageLog.logMessage(repr(ex))
                    self.showBarMessage(
                        "CRS does not match",
                        "Found existing georeferencing in raster but "
                        "its CRS does not match the CRS of the map. "
                        "Unable to reproject.",
                        Qgis.Warning,
                        5,
                    )
                    message_shown = True
        # if no projection info, assume it is the same CRS
        # as the map and no warning
        if not message_shown:
            self.showBarMessage(
                "Georeferencing loaded",
                "Found existing georeferencing in raster",
                Qgis.Info,
                3,
            )

        # zoom (assume the user wants to work on the image)
        self.iface.mapCanvas().setExtent(self.extent())

    def is_default_geotransform(self, georef):
        """
        Check if there is really a transform or if it is just the default
        made up by GDAL
        """
        return georef[0] == 0 and georef[3] == 0 and georef[1] == 1 and georef[5] == 1

    def resetScale(self, sw, sh):
        iw = self.image.width()
        ih = self.image.height()
        wratio = sw / iw
        hratio = sh / ih

        if wratio > hratio:
            # takes all height of current extent
            self.setScale(hratio, hratio)
        else:
            # all width
            self.setScale(wratio, wratio)

    def replaceImage(self, filepath, title):
        self.title = title
        self.filepath = filepath

        # set custom properties
        self.setCustomProperty("title", title)
        self.setCustomProperty("filepath", self.filepath)
        self.setName(title)

        fileInfo = QFileInfo(filepath)
        ext = fileInfo.suffix()
        if ext == "pdf":
            s = QSettings()
            oldValidation = s.value("/Projections/defaultBehavior")
            s.setValue(
                "/Projections/defaultBehavior", "useGlobal"
            )  # for not asking about crs
            path = fileInfo.filePath()
            baseName = fileInfo.baseName()
            layer = QgsRasterLayer(path, baseName)
            self.image = layer.previewAsImage(QSize(layer.width(), layer.height()))
            s.setValue("/Projections/defaultBehavior", oldValidation)
        else:
            reader = QImageReader(filepath)
            self.image = reader.read()
        self.repaint()

    def clone(self):
        layer = FreehandRasterGeoreferencerLayer(
            self.plugin, self.filepath, self.title, self.screenExtent
        )
        layer.center = self.center
        layer.rotation = self.rotation
        layer.xScale = self.xScale
        layer.yScale = self.yScale
        layer.commitTransformParameters()
        return layer

    def getAbsoluteFilepath(self):
        if not os.path.isabs(self.filepath):
            # relative to QGS file
            qgsPath = QgsProject.instance().fileName()
            qgsFolder, _ = os.path.split(qgsPath)
            filepath = os.path.join(qgsFolder, self.filepath)
        else:
            filepath = self.filepath

        return filepath

    def extent(self):
        self.initializeLayer()
        if not self.initialized:
            qDebug("Not Initialized")
            return QgsRectangle(0, 0, 1, 1)

        if self._extent:
            return self._extent

        topLeft, topRight, bottomRight, bottomLeft = self.cornerCoordinates()

        left = min(topLeft.x(), topRight.x(), bottomRight.x(), bottomLeft.x())
        right = max(topLeft.x(), topRight.x(), bottomRight.x(), bottomLeft.x())
        top = max(topLeft.y(), topRight.y(), bottomRight.y(), bottomLeft.y())
        bottom = min(topLeft.y(), topRight.y(), bottomRight.y(), bottomLeft.y())

        # recenter + create rectangle
        self._extent = QgsRectangle(left, bottom, right, top)
        return self._extent

    def cornerCoordinates(self):
        return self.transformedCornerCoordinates(
            self.center, self.rotation, self.xScale, self.yScale
        )

    def transformedCornerCoordinates(self, center, rotation, xScale, yScale):
        # scale
        topLeft = QgsPointXY(
            -self.image.width() / 2.0 * xScale, self.image.height() / 2.0 * yScale
        )
        topRight = QgsPointXY(
            self.image.width() / 2.0 * xScale, self.image.height() / 2.0 * yScale
        )
        bottomLeft = QgsPointXY(
            -self.image.width() / 2.0 * xScale, -self.image.height() / 2.0 * yScale
        )
        bottomRight = QgsPointXY(
            self.image.width() / 2.0 * xScale, -self.image.height() / 2.0 * yScale
        )

        # rotate
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)

        topLeft = self._rotate(topLeft, cosRot, sinRot)
        topRight = self._rotate(topRight, cosRot, sinRot)
        bottomRight = self._rotate(bottomRight, cosRot, sinRot)
        bottomLeft = self._rotate(bottomLeft, cosRot, sinRot)

        topLeft.set(topLeft.x() + center.x(), topLeft.y() + center.y())
        topRight.set(topRight.x() + center.x(), topRight.y() + center.y())
        bottomRight.set(bottomRight.x() + center.x(), bottomRight.y() + center.y())
        bottomLeft.set(bottomLeft.x() + center.x(), bottomLeft.y() + center.y())

        return (topLeft, topRight, bottomRight, bottomLeft)

    def transformedCornerCoordinatesFromPoint(
        self, startPoint, rotation, xScale, yScale
    ):
        # startPoint is a fixed point for this new movement (rotation and
        # scale)
        # rotation is the global rotation of the image
        # xScale is the new xScale factor to be multiplied by self.xScale
        # idem for yScale
        # Calculate the coordinate of the center in a startPoint origin
        # coordinate system and apply scales
        dX = (self.center.x() - startPoint.x()) * xScale
        dY = (self.center.y() - startPoint.y()) * yScale
        # Half width and half height in the current transformation
        hW = (self.image.width() / 2.0) * self.xScale * xScale
        hH = (self.image.height() / 2.0) * self.yScale * yScale
        # Actual rectangle coordinates :
        pt1 = QgsPointXY(-hW, hH)
        pt2 = QgsPointXY(hW, hH)
        pt3 = QgsPointXY(hW, -hH)
        pt4 = QgsPointXY(-hW, -hH)
        # Actual rotation from the center
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -self.rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)
        pt1 = self._rotate(pt1, cosRot, sinRot)
        pt2 = self._rotate(pt2, cosRot, sinRot)
        pt3 = self._rotate(pt3, cosRot, sinRot)
        pt4 = self._rotate(pt4, cosRot, sinRot)
        # Second transformation
        # displacement of the origin
        pt1 = QgsPointXY(pt1.x() + dX, pt1.y() + dY)
        pt2 = QgsPointXY(pt2.x() + dX, pt2.y() + dY)
        pt3 = QgsPointXY(pt3.x() + dX, pt3.y() + dY)
        pt4 = QgsPointXY(pt4.x() + dX, pt4.y() + dY)
        # Rotation
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)
        pt1 = self._rotate(pt1, cosRot, sinRot)
        pt2 = self._rotate(pt2, cosRot, sinRot)
        pt3 = self._rotate(pt3, cosRot, sinRot)
        pt4 = self._rotate(pt4, cosRot, sinRot)
        # translate to startPoint
        pt1 = QgsPointXY(pt1.x() + startPoint.x(), pt1.y() + startPoint.y())
        pt2 = QgsPointXY(pt2.x() + startPoint.x(), pt2.y() + startPoint.y())
        pt3 = QgsPointXY(pt3.x() + startPoint.x(), pt3.y() + startPoint.y())
        pt4 = QgsPointXY(pt4.x() + startPoint.x(), pt4.y() + startPoint.y())

        return (pt1, pt2, pt3, pt4)

    def moveCenterFromPointRotate(self, startPoint, rotation, xScale, yScale):
        cornerPoints = self.transformedCornerCoordinatesFromPoint(
            startPoint, rotation, xScale, yScale
        )
        self.center = QgsPointXY(
            (cornerPoints[0].x() + cornerPoints[2].x()) / 2,
            (cornerPoints[0].y() + cornerPoints[2].y()) / 2,
        )

    def _rotate(self, point, cosRot, sinRot):
        return QgsPointXY(
            point.x() * cosRot - point.y() * sinRot,
            point.x() * sinRot + point.y() * cosRot,
        )

    def createMapRenderer(self, rendererContext):
        return FreehandRasterGeoreferencerLayerRenderer(self, rendererContext)

    def setBlendModeByName(self, modeName):
        self.blendModeName = modeName
        blendMode = getattr(QPainter, "CompositionMode_" + modeName, 0)
        self.setBlendMode(blendMode)
        self.setCustomProperty("blendMode", modeName)

    def setTransparency(self, transparency):
        self.transparency = transparency
        self.setCustomProperty("transparency", transparency)

    def draw(self, renderContext):
        if renderContext.extent().isEmpty():
            qDebug("Drawing is skipped because map extent is empty.")
            return True

        self.initializeLayer()
        if not self.initialized:
            qDebug("Drawing is skipped because nothing to draw.")
            return True

        painter = renderContext.painter()
        painter.save()
        self.prepareStyle(painter)
        self.drawRaster(renderContext)
        painter.restore()

        return True

    def drawRaster(self, renderContext):
        painter = renderContext.painter()
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)

        self.map2pixel = renderContext.mapToPixel()

        scaleX = self.xScale / self.map2pixel.mapUnitsPerPixel()
        scaleY = self.yScale / self.map2pixel.mapUnitsPerPixel()

        rect = QRectF(
            QPointF(-self.image.width() / 2.0, -self.image.height() / 2.0),
            QPointF(self.image.width() / 2.0, self.image.height() / 2.0),
        )
        mapCenter = self.map2pixel.transform(self.center)

        # draw the image on the map canvas
        painter.translate(QPointF(mapCenter.x(), mapCenter.y()))
        painter.rotate(self.rotation)
        painter.scale(scaleX, scaleY)
        painter.drawImage(rect, self.image)

        painter.setOpacity(1.0)
        painter.setBrush(Qt.NoBrush)
        pen = QPen()
        pen.setColor(QColor(0, 0, 0))
        pen.setWidth(3)
        pen.setCosmetic(True)
        painter.setPen(pen)
        painter.drawRect(rect)

    def prepareStyle(self, painter):
        painter.setOpacity(1.0 - self.transparency / 100.0)

    def readXml(self, node, context):
        self.readCustomProperties(node)
        self.title = self.customProperty("title", "")
        self.filepath = self.customProperty("filepath", "")
        self.xScale = float(self.customProperty("xScale", 1.0))
        self.yScale = float(self.customProperty("yScale", 1.0))
        self.rotation = float(self.customProperty("rotation", 0.0))
        xCenter = float(self.customProperty("xCenter", 0.0))
        yCenter = float(self.customProperty("yCenter", 0.0))
        self.center = QgsPointXY(xCenter, yCenter)
        self.setTransparency(
            int(self.customProperty("transparency", LayerDefaultSettings.TRANSPARENCY))
        )
        self.setBlendModeByName(
            self.customProperty("blendMode", LayerDefaultSettings.BLEND_MODE)
        )
        return True

    def writeXml(self, node, doc, context):
        element = node.toElement()
        self.writeCustomProperties(node, doc)
        element.setAttribute("type", "plugin")
        element.setAttribute("name", FreehandRasterGeoreferencerLayer.LAYER_TYPE)
        return True

    def metadata(self):
        lines = []
        fmt = "%s:\t%s"
        lines.append(fmt % (self.tr("Title"), self.title))
        filepath = self.getAbsoluteFilepath()
        filepath = os.path.normpath(filepath)
        lines.append(fmt % (self.tr("Path"), filepath))
        lines.append(fmt % (self.tr("Image Width"), str(self.image.width())))
        lines.append(fmt % (self.tr("Image Height"), str(self.image.height())))
        lines.append(fmt % (self.tr("Rotation (CW)"), str(self.rotation)))
        lines.append(fmt % (self.tr("X center"), str(self.center.x())))
        lines.append(fmt % (self.tr("Y center"), str(self.center.y())))
        lines.append(fmt % (self.tr("X scale"), str(self.xScale)))
        lines.append(fmt % (self.tr("Y scale"), str(self.yScale)))

        return "\n".join(lines)

    def log(self, msg):
        qDebug(msg)

    def dump(self, detail=False, bbox=None):
        pass

    def showStatusMessage(self, msg, timeout):
        self.iface.mainWindow().statusBar().showMessage(msg, timeout)

    def showBarMessage(self, title, text, level, duration):
        self.iface.messageBar().pushMessage(title, text, level, duration)

    def transparencyChanged(self, val):
        QgsProject.instance().setDirty(True)
        self.setTransparency(val)
        self.repaintRequested.emit()

    def setTransformContext(self, transformContext):
        pass
Ejemplo n.º 10
0
class QadCOPYCommandClass(QadCommandClass):

   def instantiateNewCmd(self):
      """ istanzia un nuovo comando dello stesso tipo """
      return QadCOPYCommandClass(self.plugIn)
   
   def getName(self):
      return QadMsg.translate("Command_list", "COPY")

   def getEnglishName(self):
      return "COPY"

   def connectQAction(self, action):
      action.triggered.connect(self.plugIn.runCOPYCommand)

   def getIcon(self):
      return QIcon(":/plugins/qad/icons/copyEnt.png")

   def getNote(self):
      # impostare le note esplicative del comando
      return QadMsg.translate("Command_COPY", "Copies selected objects a specified distance in a specified direction.")
   
   def __init__(self, plugIn):
      QadCommandClass.__init__(self, plugIn)
      self.SSGetClass = QadSSGetClass(plugIn)
      self.SSGetClass.onlyEditableLayers = True
      self.cacheEntitySet = QadCacheEntitySet()
      self.basePt = QgsPointXY()
      self.series = False
      self.seriesLen = 2
      self.adjust = False
      self.copyMode = QadVariables.get(QadMsg.translate("Environment variables", "COPYMODE"))
      
      self.nOperationsToUndo = 0

   def __del__(self):
      QadCommandClass.__del__(self)
      del self.SSGetClass


   def getPointMapTool(self, drawMode = QadGetPointDrawModeEnum.NONE):
      if self.step == 0: # quando si é in fase di selezione entità
         return self.SSGetClass.getPointMapTool()
      else:
         if (self.plugIn is not None):
            if self.PointMapTool is None:
               self.PointMapTool = Qad_copy_maptool(self.plugIn)
            return self.PointMapTool
         else:
            return None


   def getCurrentContextualMenu(self):
      if self.step == 0: # quando si é in fase di selezione entità
         return None # return self.SSGetClass.getCurrentContextualMenu()
      else:
         return self.contextualMenu


   #============================================================================
   # move
   #============================================================================
   def move(self, entity, offsetX, offsetY):
      # verifico se l'entità appartiene ad uno stile di quotatura
      if entity.whatIs() == "ENTITY":
         # sposto la geometria dell'entità
         qadGeom = entity.getQadGeom().copy() # la copio
         qadGeom.move(offsetX, offsetY)
         f = entity.getFeature()
         f.setGeometry(fromQadGeomToQgsGeom(qadGeom, entity.crs()))
         # plugIn, layer, feature, coordTransform, refresh, check_validity
         if qad_layer.addFeatureToLayer(self.plugIn, entity.layer, f, None, False, False) == False:  
            return False
      elif entity.whatIs() == "DIMENTITY":
         newDimEntity = QadDimEntity(entity) # la copio
         # sposto la quota
         newDimEntity.move(offsetX, offsetY)
         if newDimEntity.addToLayers(self.plugIn) == False:
            return False             
            
      return True


   #============================================================================
   # copyGeoms
   #============================================================================
   def copyGeoms(self, newPt):
      offsetX = newPt.x() - self.basePt.x()
      offsetY = newPt.y() - self.basePt.y()

      self.plugIn.beginEditCommand("Feature copied", self.cacheEntitySet.getLayerList())

      dimElaboratedList = [] # lista delle quotature già elaborate
      entityIterator = QadCacheEntitySetIterator(self.cacheEntitySet)
      for entity in entityIterator:
         qadGeom = entity.getQadGeom() # così inizializzo le info qad
         # verifico se l'entità appartiene ad uno stile di quotatura
         dimEntity = QadDimStyles.getDimEntity(entity)
         if dimEntity is not None:
            if appendDimEntityIfNotExisting(dimElaboratedList, dimEntity) == False: # quota già elaborata
               continue
            entity = dimEntity
         
         if self.seriesLen > 0: # devo fare una serie
            if self.adjust == True:
               offsetX = offsetX / (self.seriesLen - 1)
               offsetY = offsetY / (self.seriesLen - 1)

            deltaX = offsetX
            deltaY = offsetY
               
            for i in range(1, self.seriesLen, 1):
               if self.move(entity, deltaX, deltaY) == False:
                  self.plugIn.destroyEditCommand()
                  return
               deltaX = deltaX + offsetX
               deltaY = deltaY + offsetY     
         else:
            if self.move(entity, offsetX, offsetY) == False:
               self.plugIn.destroyEditCommand()
               return
               
      self.plugIn.endEditCommand()
      self.nOperationsToUndo = self.nOperationsToUndo + 1


   #============================================================================
   # waitForBasePt
   #============================================================================
   def waitForBasePt(self):
      # imposto il map tool
      self.getPointMapTool().setMode(Qad_copy_maptool_ModeEnum.NONE_KNOWN_ASK_FOR_BASE_PT)                                

      if self.copyMode == 0: # Imposta il comando COPIA in modo che venga ripetuto automaticamente
         keyWords = QadMsg.translate("Command_COPY", "Displacement") + "/" + \
                    QadMsg.translate("Command_COPY", "mOde")
         englishKeyWords = "Displacement" + "/" + "mOde"
                    
      else:
         # l'opzione Multiple viene tradotta in italiano in "MUltiplo" nel contesto "waitForBasePt"
         # l'opzione Multiple viene tradotta in italiano in "Multipla" nel contesto "waitForMode"
         # e "Multipla" nel caso di modalità di copia
         keyWords = QadMsg.translate("Command_COPY", "Displacement") + "/" + \
                    QadMsg.translate("Command_COPY", "mOde") + "/" + \
                    QadMsg.translate("Command_COPY", "Multiple", "waitForBasePt")
         englishKeyWords = "Displacement" + "/" + "mOde" + "/" + "Multiple"

      default = QadMsg.translate("Command_COPY", "Displacement")                   
      prompt = QadMsg.translate("Command_COPY", "Specify base point or [{0}] <{1}>: ").format(keyWords, default)
      
      keyWords += "_" + englishKeyWords
      # si appresta ad attendere un punto o enter o una parola chiave         
      # msg, inputType, default, keyWords, nessun controllo
      self.waitFor(prompt, \
                   QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                   default, \
                   keyWords, QadInputModeEnum.NONE)      
      self.step = 2      
   
   #============================================================================
   # waitForSeries
   #============================================================================
   def waitForSeries(self):
      # si appresta ad attendere un numero intero
      msg = QadMsg.translate("Command_COPY", "Number of Items to Array <{0}>: ")
      # msg, inputType, default, keyWords, valori positivi
      self.waitFor(msg.format(str(self.seriesLen)), \
                   QadInputTypeEnum.INT, \
                   self.seriesLen, \
                   "", \
                   QadInputModeEnum.NOT_ZERO | QadInputModeEnum.NOT_NEGATIVE)                                      
      self.step = 6        
      
   #============================================================================
   # waitForSecondPt
   #============================================================================
   def waitForSecondPt(self):
      self.series = False
      self.adjust = False
      self.getPointMapTool().seriesLen = 0
      self.getPointMapTool().setMode(Qad_copy_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_COPY_PT)
                                      
      if self.nOperationsToUndo > 0:
         keyWords = QadMsg.translate("Command_COPY", "Array") + "/" + \
                    QadMsg.translate("Command_COPY", "Exit") + "/" + \
                    QadMsg.translate("Command_COPY", "Undo")
         default = QadMsg.translate("Command_COPY", "Exit")
         prompt = QadMsg.translate("Command_COPY", "Specify second point or [{0}] <{1}>: ").format(keyWords, default)
   
         englishKeyWords = "Array" + "/" + "Exit" + "/" + "Undo" + "/" + "Exit"
         keyWords += "_" + englishKeyWords
         # si appresta ad attendere un punto o enter o una parola chiave         
         # msg, inputType, default, keyWords, nessun controllo
         self.waitFor(prompt, \
                      QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                      default, \
                      keyWords, QadInputModeEnum.NONE)
      else:
         keyWords = QadMsg.translate("Command_COPY", "Array")
         prompt = QadMsg.translate("Command_COPY", "Specify second point or [{0}] <use first point as displacement from origin point 0,0>: ").format(keyWords)
                   
         englishKeyWords = "Array"
         keyWords += "_" + englishKeyWords
         # si appresta ad attendere un punto o enter o una parola chiave         
         # msg, inputType, default, keyWords, nessun controllo
         self.waitFor(prompt, \
                      QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                      None, \
                      keyWords, QadInputModeEnum.NONE)      
            
      self.step = 3           

   #============================================================================
   # waitForSecondPtBySeries
   #============================================================================
   def waitForSecondPtBySeries(self):
      if self.adjust == False:
         keyWords = QadMsg.translate("Command_COPY", "Fit")
         englishKeyWords = "Fit"        
      else:
         keyWords = QadMsg.translate("Command_COPY", "Array")
         englishKeyWords = "Array"        
      prompt = QadMsg.translate("Command_COPY", "Specify second point or [{0}]: ").format(keyWords)

      keyWords += "_" + englishKeyWords
      # si appresta ad attendere un punto o enter o una parola chiave         
      # msg, inputType, default, keyWords, valore nullo non permesso
      self.waitFor(prompt, \
                   QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                   "", \
                   keyWords, QadInputModeEnum.NOT_NULL)      
      self.step = 7

   #============================================================================
   # run
   #============================================================================
   def run(self, msgMapTool = False, msg = None):
      if self.plugIn.canvas.mapSettings().destinationCrs().isGeographic():
         self.showMsg(QadMsg.translate("QAD", "\nThe coordinate reference system of the project must be a projected coordinate system.\n"))
         return True # fine comando
            
      #=========================================================================
      # RICHIESTA SELEZIONE OGGETTI
      if self.step == 0: # inizio del comando
         if self.SSGetClass.run(msgMapTool, msg) == True:
            # selezione terminata
            self.step = 1
            return self.run(msgMapTool, msg)
      
      #=========================================================================
      # COPIA OGGETTI
      elif self.step == 1:
         if self.SSGetClass.entitySet.count() == 0:
            return True # fine comando
         self.cacheEntitySet.appendEntitySet(self.SSGetClass.entitySet)

         CurrSettingsMsg = QadMsg.translate("QAD", "\nCurrent settings: ")
         if self.copyMode == 0: # 0 = multipla 
            CurrSettingsMsg = CurrSettingsMsg + QadMsg.translate("Command_COPY", "Copy mode = Multiple")         
         else: # 1 = singola
            CurrSettingsMsg = CurrSettingsMsg + QadMsg.translate("Command_COPY", "Copy mode = Single")         
         self.showMsg(CurrSettingsMsg)         

         self.getPointMapTool().cacheEntitySet = self.cacheEntitySet
         self.waitForBasePt()
         self.getPointMapTool().refreshSnapType() # riagggiorno lo snapType che può essere variato dal maptool di selezione entità                    
         return False
         
      #=========================================================================
      # RISPOSTA ALLA RICHIESTA PUNTO BASE (da step = 1)
      elif self.step == 2: # dopo aver atteso un punto o un numero reale si riavvia il comando
         if msgMapTool == True: # il punto arriva da una selezione grafica
            # la condizione seguente si verifica se durante la selezione di un punto
            # é stato attivato un altro plugin che ha disattivato Qad
            # quindi stato riattivato il comando che torna qui senza che il maptool
            # abbia selezionato un punto            
            if self.getPointMapTool().point is None: # il maptool é stato attivato senza un punto
               if self.getPointMapTool().rightButton == True: # se usato il tasto destro del mouse
                  pass # opzione di default "spostamento"
               else:
                  self.setMapTool(self.getPointMapTool()) # riattivo il maptool
                  return False

            value = self.getPointMapTool().point
         else: # il punto arriva come parametro della funzione
            value = msg

         if value is None:
            value = QadMsg.translate("Command_COPY", "Displacement")

         if type(value) == unicode:
            if value == QadMsg.translate("Command_COPY", "Displacement") or value == "Displacement":
               self.basePt.set(0, 0)
               self.getPointMapTool().basePt = self.basePt
               self.getPointMapTool().setMode(Qad_copy_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_COPY_PT)                                
               # si appresta ad attendere un punto
               msg = QadMsg.translate("Command_COPY", "Specify the displacement from the origin point 0,0 <{0}, {1}>: ")
               # msg, inputType, default, keyWords, nessun controllo
               self.waitFor(msg.format(str(self.plugIn.lastOffsetPt.x()), str(self.plugIn.lastOffsetPt.y())), \
                            QadInputTypeEnum.POINT2D, \
                            self.plugIn.lastOffsetPt, \
                            "", QadInputModeEnum.NONE)                                      
               self.step = 4
            elif value == QadMsg.translate("Command_COPY", "mOde") or value == "mOde":
               # l'opzione Multiple viene tradotta in italiano in "Multipla" nel contesto "waitForMode"
               keyWords = QadMsg.translate("Command_COPY", "Single") + "/" + \
                          QadMsg.translate("Command_COPY", "Multiple", "waitForMode")
               englishKeyWords = "Single" + "/" + "Multiple"
                          
               if self.copyMode == 0: # Imposta il comando COPIA in modo che venga ripetuto automaticamente
                  # l'opzione Multiple viene tradotta in italiano in "Multipla" nel contesto "waitForMode"
                  default = QadMsg.translate("Command_COPY", "Multiple", "waitForMode")
               else:
                  default = QadMsg.translate("Command_COPY", "Single")               
               prompt = QadMsg.translate("Command_COPY", "Enter a copy mode option [{0}] <{1}>: ").format(keyWords, default)

               keyWords += "_" + englishKeyWords
               # si appresta ad attendere enter o una parola chiave         
               # msg, inputType, default, keyWords, nessun controllo
               self.waitFor(prompt, \
                            QadInputTypeEnum.KEYWORDS, \
                            default, \
                            keyWords, QadInputModeEnum.NONE)
               self.step = 5
            # l'opzione Multiple viene tradotta in italiano in "MUltiplo" nel contesto "waitForBasePt"
            elif value == QadMsg.translate("Command_COPY", "Multiple", "waitForBasePt") or value == "Multiple":
               self.copyMode = 0 # Imposta il comando COPIA in modo che venga ripetuto automaticamente
               self.waitForBasePt()                         
         elif type(value) == QgsPointXY: # se é stato inserito il punto base
            self.basePt.set(value.x(), value.y())

            # imposto il map tool
            self.getPointMapTool().basePt = self.basePt           
            self.waitForSecondPt()
         
         return False 
         
      #=========================================================================
      # RISPOSTA ALLA RICHIESTA SECONDO PUNTO PER COPIA (da step = 2)
      elif self.step == 3: # dopo aver atteso un punto o un numero reale si riavvia il comando
         if msgMapTool == True: # il punto arriva da una selezione grafica
            # la condizione seguente si verifica se durante la selezione di un punto
            # é stato attivato un altro plugin che ha disattivato Qad
            # quindi stato riattivato il comando che torna qui senza che il maptool
            # abbia selezionato un punto            
            if self.getPointMapTool().point is None: # il maptool é stato attivato senza un punto
               if self.getPointMapTool().rightButton == True: # se usato il tasto destro del mouse
                  if self.nOperationsToUndo > 0:
                     value = QadMsg.translate("Command_COPY", "Exit")
                  else:
                     value = None
               else:
                  self.setMapTool(self.getPointMapTool()) # riattivo il maptool
                  return False
            else:
               value = self.getPointMapTool().point
         else: # il punto arriva come parametro della funzione
            value = msg

         if value is None:
            if self.nOperationsToUndo > 0:
               value = QadMsg.translate("Command_COPY", "Exit")
            else:               
               # utilizzare il primo punto come spostamento
               value = QgsPointXY(self.basePt)
               self.basePt.set(0, 0)
               self.copyGeoms(value)
               return True # fine comando
         
         if type(value) == unicode:
            if value == QadMsg.translate("Command_COPY", "Array") or value == "Array":
               self.waitForSeries()               
            elif value == QadMsg.translate("Command_COPY", "Exit") or value == "Exit":
               return True # fine comando
            elif value == QadMsg.translate("Command_COPY", "Undo") or value == "Undo":
               if self.nOperationsToUndo > 0: 
                  self.nOperationsToUndo = self.nOperationsToUndo - 1
                  self.plugIn.undoEditCommand()
               else:
                  self.showMsg(QadMsg.translate("QAD", "\nThe command has been canceled."))                  
               self.waitForSecondPt()
         elif type(value) == QgsPointXY: # se é stato inserito lo spostamento con un punto
            self.copyGeoms(value)
            if self.copyMode == 1: # "Singola" 
               return True # fine comando
            self.waitForSecondPt()
         
         return False
               
      #=========================================================================
      # RISPOSTA ALLA RICHIESTA DEL PUNTO DI SPOSTAMENTO (da step = 2)
      elif self.step == 4: # dopo aver atteso un punto o un numero reale si riavvia il comando
         if msgMapTool == True: # il punto arriva da una selezione grafica
            # la condizione seguente si verifica se durante la selezione di un punto
            # é stato attivato un altro plugin che ha disattivato Qad
            # quindi stato riattivato il comando che torna qui senza che il maptool
            # abbia selezionato un punto            
            if self.getPointMapTool().point is None: # il maptool é stato attivato senza un punto
               if self.getPointMapTool().rightButton == True: # se usato il tasto destro del mouse
                  return True # fine comando
               else:
                  self.setMapTool(self.getPointMapTool()) # riattivo il maptool
                  return False

            value = self.getPointMapTool().point
         else: # il punto arriva come parametro della funzione
            value = msg

         self.plugIn.setLastOffsetPt(value)
         self.copyGeoms(value)
         return True # fine comando


      #=========================================================================
      # RISPOSTA ALLA RICHIESTA DELLA MODALITA' (SINGOLA / MULTIPLA) (da step = 2)
      elif self.step == 5: # dopo aver atteso un punto o un numero reale si riavvia il comando
         if msgMapTool == True: # il punto arriva da una selezione grafica
            # la condizione seguente si verifica se durante la selezione di un punto
            # é stato attivato un altro plugin che ha disattivato Qad
            # quindi stato riattivato il comando che torna qui senza che il maptool
            # abbia selezionato un punto            
            if self.getPointMapTool().point is None: # il maptool é stato attivato senza un punto
               if self.getPointMapTool().rightButton == True: # se usato il tasto destro del mouse
                  return True # fine comando
               else:
                  self.setMapTool(self.getPointMapTool()) # riattivo il maptool
                  return False

            value = self.getPointMapTool().point
         else: # la parola chiave arriva come parametro della funzione
            value = msg

         if type(value) == unicode:
            if value == QadMsg.translate("Command_COPY", "Single") or value == "Single":
               self.copyMode = 1
               QadVariables.set(QadMsg.translate("Environment variables", "COPYMODE"), 1)
               QadVariables.save()
            # l'opzione Multiple viene tradotta in italiano in "Multipla" nel contesto "waitForMode"
            elif value == QadMsg.translate("Command_COPY", "Multiple", "waitForMode") or value == "Multiple":
               self.copyMode = 0
               QadVariables.set(QadMsg.translate("Environment variables", "COPYMODE"), 0)
               QadVariables.save()
            
         self.waitForBasePt()
         return False

      #=========================================================================
      # RISPOSTA ALLA RICHIESTA DELLA SERIE (da step = 3)
      elif self.step == 6: # dopo aver atteso un numero intero si riavvia il comando
         if msgMapTool == True: # il punto arriva da una selezione grafica
            if self.getPointMapTool().rightButton == True: # se usato il tasto destro del mouse
               value = self.seriesLen
            else:
               value = self.getPointMapTool().point
         else: # il punto arriva come parametro della funzione
            value = msg

         if value < 2:
            self.showMsg(QadMsg.translate("Command_COPY", "\nThe value must be between 2 and 32767."))
            self.waitForSeries()
         else:
            self.series = True
            self.seriesLen = value
            self.getPointMapTool().seriesLen = self.seriesLen

            self.waitForSecondPtBySeries()
            
         return False

      #=========================================================================
      # RISPOSTA ALLA RICHIESTA SECONDO PUNTO PER COPIA DA SERIE (da step = 6)
      elif self.step == 7: # dopo aver atteso un punto o una parola chiave
         if msgMapTool == True: # il punto arriva da una selezione grafica
            # la condizione seguente si verifica se durante la selezione di un punto
            # é stato attivato un altro plugin che ha disattivato Qad
            # quindi stato riattivato il comando che torna qui senza che il maptool
            # abbia selezionato un punto            
            if self.getPointMapTool().point is None: # il maptool é stato attivato senza un punto
               if self.getPointMapTool().rightButton == True: # se usato il tasto destro del mouse
                  return True # fine comando
               else:
                  self.setMapTool(self.getPointMapTool()) # riattivo il maptool
                  return False

            value = self.getPointMapTool().point
         else: # il punto arriva come parametro della funzione
            value = msg

         if type(value) == unicode:
            if value == QadMsg.translate("Command_COPY", "Array") or value == "Array":
               self.adjust = False
               self.getPointMapTool().adjust = self.adjust
               self.waitForSecondPtBySeries()
            elif value == QadMsg.translate("Command_COPY", "Fit") or value == "Fit":
               self.adjust = True
               self.getPointMapTool().adjust = self.adjust
               self.waitForSecondPtBySeries()
         elif type(value) == QgsPointXY: # se é stato inserito lo spostamento con un punto
            self.copyGeoms(value)
            if self.copyMode == 1: # "Singola" 
               return True # fine comando            
            self.waitForSecondPt()
          
         return False
Ejemplo n.º 11
0
class MobileItem(QObject):
    '''
    A Mobile Item that reveives its position from a dataprovider
    and is displayed on the canvas
    Could be everything liek vehicles or simple beacons
    '''

    mobileItemCount = 0

    newPosition = pyqtSignal(float, QgsPointXY, float, float)
    newAttitude = pyqtSignal(float, float, float)  # heading, pitch, roll
    timeout = pyqtSignal()

    def __init__(self, iface, params={}, parent=None):
        '''
        Constructor
        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        :param params: A dictionary defining all the properties of the item
        :type params: dictionary
        :param parent: Parent object for the new item. Defaults None.
        :type parent: QObject
        '''
        super(MobileItem, self).__init__(parent)

        self.iface = iface
        self.canvas = iface.mapCanvas()
        MobileItem.mobileItemCount += 1
        self.name = params.setdefault(
            'Name', 'MobileItem_' + str(MobileItem.mobileItemCount))
        self.marker = PositionMarker(self.canvas, params)
        self.marker.setToolTip(self.name)
        self.dataProvider = params.get('provider', dict())
        self.messageFilter = dict()
        self.extData = dict()
        self.coordinates = None
        self.position = None
        self.heading = 0.0
        self.depth = 0.0
        self.altitude = 0.0
        self.lastFix = 0.0
        self.crsXform = QgsCoordinateTransform()
        self.crsXform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
        self.onCrsChange()
        self.canvas.destinationCrsChanged.connect(self.onCrsChange)
        if hasattr(self.canvas, 'magnificationChanged'):
            self.canvas.magnificationChanged.connect(
                self.onMagnificationChanged)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.timeout)
        self.notifyCount = int(params.get('nofixNotify', 0))
        self.fadeOut = bool(params.get('fadeOut', False))
        if self.notifyCount or self.fadeOut:
            self.timer.timeout.connect(self.notifyTimeout)
        self.timeoutCount = 0
        self.timeoutTime = int(params.get('timeout', 3000))
        self.notifyDuration = int(params.get('NotifyDuration', 0))
        self.timedOut = False
        self.enabled = True

    def removeFromCanvas(self):
        '''
        Remove the item and its track from the canvas
        '''
        self.marker.removeFromCanvas()

    def properties(self):
        '''
        Return the items properties as dictionary
        :returns: Items properties
        :rtype: dict
        '''
        d = {
            'Name': self.name,
            'timeout': self.timeoutTime,
            'nofixNotify': self.notifyCount,
            'fadeOut': self.fadeOut,
            'enabled': self.enabled,
            'provider': self.dataProvider
        }
        d.update(self.marker.properties())
        return d

    def subscribePositionProvider(self, provider, filterId=None):
        '''
        Subscribe the provider for this item
        by connecting to the providers signals
        :param provider: Provider to connect to
        :type provider: DataProvider
        :param filterId: Filter Id for this item
        :type filterId:
        '''
        provider.newDataReceived.connect(self.processNewData)
        try:
            if filterId['id'] not in (None, 'None') or filterId['flags']:
                self.messageFilter[provider.name] = filterId
            elif provider.name in self.messageFilter:
                self.messageFilter.pop(provider.name, None)
        except (KeyError, TypeError):
            self.messageFilter.pop(provider.name, None)

    def unsubscribePositionProvider(self, provider):
        '''
        Unsubscribe provider by disconnecting the providers signals
        :param provider: Provider to diconnect from
        :type provider: DataProvider
        '''
        try:
            provider.newDataReceived.disconnect(self.processData)
            self.messageFilter.pop(provider.name, None)
        except KeyError:
            pass

    @pyqtSlot(dict)
    def processNewData(self, data):
        '''
        Process incoming data from the data provider
        :param data: Positon or attitude data
        :type data: dict
        '''
        if not self.enabled:
            return

        flags = list()
        try:
            pname = data['name']
            flags = self.messageFilter[pname]['flags']
            if not self.messageFilter[pname]['id'] in (None, 'None'):
                if not data['id'] in (self.messageFilter[pname]['id'],
                                      str(self.messageFilter[pname]['id'])):
                    return
        except Exception:
            pass

        self.extData.update(data)

        if '-pos' not in flags:
            if 'lat' in data and 'lon' in data:
                if self.fadeOut and self.timedOut:
                    self.marker.setVisible(True)
                    self.timedOut = False
                self.position = QgsPointXY(data['lon'], data['lat'])
                self.heading = data.get('heading', -9999.9)
                self.depth = data.get('depth', -9999.9)
                self.altitude = data.get('altitude', -9999.9)
                try:
                    self.coordinates = self.crsXform.transform(self.position)
                    self.marker.setMapPosition(self.coordinates)
                    if 'time' in data:
                        self.lastFix = data['time']
                        self.newPosition.emit(
                            self.lastFix, self.position,
                            self.extData.get('depth', -9999.9),
                            self.extData.get('altitude', -9999.9))
                        self.timer.start(self.timeoutTime)
                        self.timeoutCount = 0
                except QgsCsException:
                    pass

            elif self.position is not None:
                if 'depth' in data or 'altitude' in data:
                    self.newPosition.emit(
                        self.lastFix, self.position,
                        self.extData.get('depth', -9999.9),
                        self.extData.get('altitude', -9999.9))

        if 'heading' in data and '-head' not in flags:
            self.newAttitude.emit(data['heading'], data.get('pitch', 0.0),
                                  data.get('roll', 0.0))
            self.marker.newHeading(data['heading'])
            self.heading = data['heading']
        elif 'course' in data and '+course' in flags:
            self.newAttitude.emit(data['course'], data.get('pitch', 0.0),
                                  data.get('roll', 0.0))
            self.marker.newHeading(data['course'])
            self.heading = data['course']

    @pyqtSlot(float)
    def onScaleChange(self, ):
        '''
        Slot called when the map is zoomed
        :param scale: New scale
        :type scale: float
        '''
        self.marker.updatePosition()

    @pyqtSlot()
    def onCrsChange(self):
        '''
        SLot called when the mapcanvas CRS is changed
        '''
        crsDst = self.canvas.mapSettings().destinationCrs()
        self.crsXform.setDestinationCrs(crsDst)
        self.marker.updatePosition()

    @pyqtSlot(float)
    def onMagnificationChanged(self, ):
        '''
        Slot called when the map magnification has changed
        :param scale: New scale
        :type scale: float
        '''
        self.marker.updateMapMagnification()

    @pyqtSlot(bool)
    def setEnabled(self, enabled):
        '''
        Hide or display the item and its track on the map
        :param enabled: what to do
        :type enabled: bool
        '''
        self.enabled = enabled
        self.marker.setVisible(self.enabled)
        self.marker.resetPosition()
        self.extData.clear()
        if self.enabled:
            self.timer.start(self.timeoutTime)
            self.timeoutCount = 0
        else:
            self.timer.stop()

    @pyqtSlot()
    def deleteTrack(self):
        '''
        Delete the track all points
        '''
        self.marker.deleteTrack()

    @pyqtSlot()
    def centerOnMap(self):
        '''
        Center the item on the map
        '''
        if self.coordinates is not None:
            self.canvas.setCenter(self.coordinates)
            self.canvas.refresh()

    def reportPosition(self):
        '''
        Report the position of the item. Used for logging
        :returns: geographic postion, depth and altitude
        :rtype: float, float, float, float, float
        '''
        if self.position is None:
            return -9999.9, -9999.9, -9999.9, 0.0, -9999.9
        return self.position.y(), self.position.x(
        ), self.depth, self.heading, self.altitude

    @pyqtSlot()
    def notifyTimeout(self):
        if self.fadeOut and not self.timedOut:
            self.marker.setVisible(False)
            self.timedOut = True
        if self.notifyCount:
            self.timeoutCount += 1
            if self.timeoutCount == self.notifyCount:
                msg = self.tr(u'No fix for %s since more than %d seconds!') % (
                    self.name, self.timeoutTime * self.timeoutCount / 1000)
                w = self.iface.messageBar().createMessage(
                    self.tr(u'PosiView Attention'), msg)
                label = QLabel(w)
                m = QMovie(':/plugins/PosiView/hand.gif')
                m.setSpeed(75)
                label.setMovie(m)
                m.setParent(label)
                m.start()
                w.layout().addWidget(label)
                self.iface.messageBar().pushWidget(
                    w, level=Qgis.Critical, duration=self.notifyDuration)

    def getTrack(self):
        tr = [e[1] for e in self.marker.track]
        return tr

    def applyTrack(self, track):
        self.marker.setTrack(track)
Ejemplo n.º 12
0
class TestQgsPointXY(unittest.TestCase):

    def __init__(self, methodName):
        """Run once on class initialization."""
        unittest.TestCase.__init__(self, methodName)

    def setUp(self):
        self.mPoint = QgsPointXY(10.0, 10.0)

    def test_Point(self):
        myExpectedValue = 10.0
        myActualValue = self.mPoint.x()
        myMessage = 'Expected: %s Got: %s' % (myExpectedValue, myActualValue)
        assert myExpectedValue == myActualValue, myMessage

    def test_pointToString(self):
        myExpectedValue = '10, 10'
        myActualValue = self.mPoint.toString()
        myMessage = 'Expected: %s Got: %s' % (myExpectedValue, myActualValue)
        assert myExpectedValue == myActualValue, myMessage

    def test_hash(self):
        a = QgsPointXY(2.0, 1.0)
        b = QgsPointXY(2.0, 2.0)
        c = QgsPointXY(1.0, 2.0)
        d = QgsPointXY(1.0, 1.0)
        e = QgsPointXY(2.0, 1.0)
        assert a.__hash__() != b.__hash__()
        assert e.__hash__() == a.__hash__()

        mySet = set([a, b, c, d, e])
        assert len(mySet) == 4

    def test_issue_32443(self):
        p = QgsPoint()
        assert p.wkbType() == QgsWkbTypes.Point and p.x() != p.x() and p.y() != p.y()

        # ctor from QgsPointXY should be available
        p = QgsPoint(QgsPointXY(1, 2))
        assert p.wkbType() == QgsWkbTypes.Point and p.x() == 1 and p.y() == 2

        # ctor from QPointF should be available
        p = QgsPoint(QPointF(1, 2))
        assert p.wkbType() == QgsWkbTypes.Point and p.x() == 1 and p.y() == 2

        p = QgsPoint(1, 2)
        assert p.wkbType() == QgsWkbTypes.Point and p.x() == 1 and p.y() == 2

        p = QgsPoint(1, 2, 3)
        assert p.wkbType() == QgsWkbTypes.PointZ and p.x() == 1 and p.y() == 2 and p.z() == 3

        p = QgsPoint(1, 2, z=3)
        assert p.wkbType() == QgsWkbTypes.PointZ and p.x() == 1 and p.y() == 2 and p.z() == 3

        p = QgsPoint(1, 2, m=3)
        assert p.wkbType() == QgsWkbTypes.PointM and p.x() == 1 and p.y() == 2 and p.m() == 3

        p = QgsPoint(1, 2, wkbType=QgsWkbTypes.PointM)
        assert p.wkbType() == QgsWkbTypes.PointM and p.x() == 1 and p.y() == 2 and p.m() != p.m()

        p = QgsPoint(1, 2, 3, 4)
        assert p.wkbType() == QgsWkbTypes.PointZM and p.x() == 1 and p.y() == 2 and p.z() == 3 and p.m() == 4

        p = QgsPoint(1, 2, m=4, z=3)
        assert p.wkbType() == QgsWkbTypes.PointZM and p.x() == 1 and p.y() == 2 and p.z() == 3 and p.m() == 4
Ejemplo n.º 13
0
def processLine(layer, writerLines, discardVertices, isProcessing):
    layercrs = layer.crs()    
    if layercrs != epsg4326:
        transto4326 = QgsCoordinateTransform(layercrs, epsg4326, QgsProject.instance())
        transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs, QgsProject.instance())
    
    iterator = layer.getFeatures()
    num_features = 0
    num_bad = 0
    maxseglen = settings.maxSegLength*1000.0
    maxSegments = settings.maxSegments
    for feature in iterator:
        num_features += 1
        try:
            wkbtype = feature.geometry().wkbType()
            if wkbtype == QgsWkbTypes.LineString:
                seg = [feature.geometry().asPolyline()]
            else:
                seg = feature.geometry().asMultiPolyline()
            numseg = len(seg)
            if numseg < 1 or len(seg[0]) < 2:
                continue
            # Create a new Line Feature
            fline = QgsFeature()
            # If the input is not 4326 we need to convert it to that and then back to the output CRS
            if discardVertices:
                ptStart = QgsPointXY(seg[0][0][0], seg[0][0][1])
                if layercrs != epsg4326: # Convert to 4326
                    ptStart = transto4326.transform(ptStart)
                pts = [ptStart]
                numpoints = len(seg[numseg-1])
                ptEnd = QgsPointXY(seg[numseg-1][numpoints-1][0], seg[numseg-1][numpoints-1][1])
                if layercrs != epsg4326: # Convert to 4326
                    ptEnd = transto4326.transform(ptEnd)
                l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                if l.s13 > maxseglen:
                    n = int(math.ceil(l.s13 / maxseglen))
                    if n > maxSegments:
                        n = maxSegments
                    seglen = l.s13 / n
                    for i in range(1,n):
                        s = seglen * i
                        g = l.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                        pts.append( QgsPointXY(g['lon2'], g['lat2']) )
                pts.append(ptEnd)
                
                if layercrs != epsg4326: # Convert each point back to the output CRS
                    for x, pt in enumerate(pts):
                        pts[x] = transfrom4326.transform(pt)
                fline.setGeometry(QgsGeometry.fromPolylineXY(pts))
            else:
                if wkbtype == QgsWkbTypes.LineString:
                    line = seg[0]
                    numpoints = len(line)
                    ptStart = QgsPointXY(line[0][0], line[0][1])
                    if layercrs != epsg4326: # Convert to 4326
                        ptStart = transto4326.transform(ptStart)
                    pts = [ptStart]
                    for x in range(1,numpoints):
                        ptEnd = QgsPointXY(line[x][0], line[x][1])
                        if layercrs != epsg4326: # Convert to 4326
                            ptEnd = transto4326.transform(ptEnd)
                        l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                        n = int(math.ceil(l.s13 / maxseglen))
                        if l.s13 > maxseglen:
                            if n > maxSegments:
                                n = maxSegments
                            seglen = l.s13 / n
                            for i in range(1,n):
                                s = seglen * i
                                g = l.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                pts.append( QgsPointXY(g['lon2'], g['lat2']) )
                        pts.append(ptEnd)
                        ptStart = ptEnd
                
                    if layercrs != epsg4326: # Convert each point back to the output CRS
                        for x, pt in enumerate(pts):
                            pts[x] = transfrom4326.transform(pt)
                    fline.setGeometry(QgsGeometry.fromPolylineXY(pts))
                else: # MultiLineString
                    outseg = []
                    for line in seg:
                        numpoints = len(line)
                        ptStart = QgsPointXY(line[0][0], line[0][1])
                        if layercrs != epsg4326: # Convert to 4326
                            ptStart = transto4326.transform(ptStart)
                        pts = [ptStart]
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(line[x][0], line[x][1])
                            if layercrs != epsg4326: # Convert to 4326
                                ptEnd = transto4326.transform(ptEnd)
                            l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                            n = int(math.ceil(l.s13 / maxseglen))
                            if l.s13 > maxseglen:
                                if n > maxSegments:
                                    n = maxSegments
                                seglen = l.s13 / n
                                for i in range(1,n):
                                    s = seglen * i
                                    g = l.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                    pts.append( QgsPointXY(g['lon2'], g['lat2']) )
                            pts.append(ptEnd)
                            ptStart = ptEnd
                            
                        if layercrs != epsg4326: # Convert each point back to the output CRS
                            for x, pt in enumerate(pts):
                                pts[x] = transfrom4326.transform(pt)
                        outseg.append(pts)
                
                    fline.setGeometry(QgsGeometry.fromMultiPolylineXY(outseg))
                    
            fline.setAttributes(feature.attributes())
            if isProcessing:
                writerLines.addFeature(fline)
            else:
                writerLines.addFeatures([fline])
        except:
            num_bad += 1
            #traceback.print_exc()
            pass
            
    return num_bad
Ejemplo n.º 14
0
def processPoly(layer, writerLines, isProcessing):
    layercrs = layer.crs()
    if layercrs != epsg4326:
        transto4326 = QgsCoordinateTransform(layercrs, epsg4326, QgsProject.instance())
        transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs, QgsProject.instance())
    
    iterator = layer.getFeatures()
    num_features = 0
    num_bad = 0
    maxseglen = settings.maxSegLength*1000.0
    maxSegments = settings.maxSegments
    for feature in iterator:
        num_features += 1
        try:
            wkbtype = feature.geometry().wkbType()
            if wkbtype == QgsWkbTypes.Polygon:
                poly = feature.geometry().asPolygon()
                numpolygons = len(poly)
                if numpolygons < 1:
                    continue
                
                ptset = []
                for points in poly:
                    numpoints = len(points)
                    if numpoints < 2:
                        continue
                    # If the input is not 4326 we need to convert it to that and then back to the output CRS
                    ptStart = QgsPointXY(points[0][0], points[0][1])
                    if layercrs != epsg4326: # Convert to 4326
                        ptStart = transto4326.transform(ptStart)
                    pts = [ptStart]
                    for x in range(1,numpoints):
                        ptEnd = QgsPointXY(points[x][0], points[x][1])
                        if layercrs != epsg4326: # Convert to 4326
                            ptEnd = transto4326.transform(ptEnd)
                        l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                        n = int(math.ceil(l.s13 / maxseglen))
                        if n > maxSegments:
                            n = maxSegments
                            
                        seglen = l.s13 / n
                        for i in range(1,n):
                            s = seglen * i
                            g = l.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                            pts.append( QgsPointXY(g['lon2'], g['lat2']) )
                        pts.append(ptEnd)
                        ptStart = ptEnd
     
                    if layercrs != epsg4326: # Convert each point to the output CRS
                        for x, pt in enumerate(pts):
                            pts[x] = transfrom4326.transform(pt)
                    ptset.append(pts)
                        
                if len(ptset) > 0:
                    featureout = QgsFeature()
                    featureout.setGeometry(QgsGeometry.fromPolygonXY(ptset))
                                
                    featureout.setAttributes(feature.attributes())
                    if isProcessing:
                        writerLines.addFeature(featureout)
                    else:
                        writerLines.addFeatures([featureout])
            else:
                multipoly = feature.geometry().asMultiPolygon()
                multiset = []
                for poly in multipoly:
                    ptset = []
                    for points in poly:
                        numpoints = len(points)
                        if numpoints < 2:
                            continue
                        # If the input is not 4326 we need to convert it to that and then back to the output CRS
                        ptStart = QgsPointXY(points[0][0], points[0][1])
                        if layercrs != epsg4326: # Convert to 4326
                            ptStart = transto4326.transform(ptStart)
                        pts = [ptStart]
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(points[x][0], points[x][1])
                            if layercrs != epsg4326: # Convert to 4326
                                ptEnd = transto4326.transform(ptEnd)
                            l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                            n = int(math.ceil(l.s13 / maxseglen))
                            if n > maxSegments:
                                n = maxSegments
                                
                            seglen = l.s13 / n
                            for i in range(1,n):
                                s = seglen * i
                                g = l.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                pts.append( QgsPointXY(g['lon2'], g['lat2']) )
                            pts.append(ptEnd)
                            ptStart = ptEnd
         
                        if layercrs != epsg4326: # Convert each point to the output CRS
                            for x, pt in enumerate(pts):
                                pts[x] = transfrom4326.transform(pt)
                        ptset.append(pts)
                    multiset.append(ptset)
                        
                if len(multiset) > 0:
                    featureout = QgsFeature()
                    featureout.setGeometry(QgsGeometry.fromMultiPolygonXY(multiset))
                                
                    featureout.setAttributes(feature.attributes())
                    if isProcessing:
                        writerLines.addFeature(featureout)
                    else:
                        writerLines.addFeatures([featureout])
        except:
            num_bad += 1
            #traceback.print_exc()
            pass
                
    return num_bad
Ejemplo n.º 15
0
    def result(self, result):
        if result:

            # save a project reference
            project = QgsProject.instance()

            # Save the user input
            lineedit_text = self.dlg.lineedit_xy.value()
            crs_input = self.dlg.crs_input.crs()

            crs_out = QgsCoordinateReferenceSystem(
                4326)  # we need this to be WGS84 for Nominatim

            # Protect the free text field for coordinates from generic user failure:)
            try:
                lineedit_yx = [
                    float(coord.strip()) for coord in lineedit_text.split(',')
                ]
            except:
                QMessageBox.critical(
                    self.iface.mainWindow(), 'QuickAPI error',
                    "Did you really specify a coordinate in comma-separated Lat/Long?\nExiting..."
                )
                return

            # Create a Point and transform if necessary
            point = QgsPointXY(*reversed(lineedit_yx))

            if crs_input.authid() != 'EPSG:4326':
                xform = QgsCoordinateTransform(crs_input, crs_out, project)
                point_transform = xform.transform(point)
                point = point_transform

            # Set up and fire Nominatim GET request
            user_agent = '*****@*****.**'
            base_url = 'https://nominatim.openstreetmap.org/reverse'
            params = {'lat': point.y(), 'lon': point.x(), 'format': 'json'}

            response = requests.get(url=base_url,
                                    params=params,
                                    headers={'User-Agent': user_agent})
            response_json = response.json()

            # Only process response if HTTP 200
            if response.status_code == 200:
                # Unfortunately Nominatim goes against protocol and responds with 200 even if an error occurred
                if response_json.get('error'):
                    QMessageBox.critical(
                        self.iface.mainWindow(), "Quick API error",
                        "The request was not processed succesfully!\n\n"
                        "Message:\n"
                        "{}".format(response.json()))
                    return

                # Capture relevant response fields
                x = float(response_json['lon'])
                y = float(response_json['lat'])
                address = response_json['display_name']
                license = response_json['licence']

                # Create the output memory layer
                layer_out = QgsVectorLayer(
                    "Point?crs=EPSG:4326&field=address:string&field=license:string",
                    "Nominatim Reverse Geocoding", "memory")

                # Create the output feature (only one here)
                point_out = QgsPointXY(x, y)
                feature = QgsFeature()
                feature.setGeometry(QgsGeometry.fromPointXY(point_out))
                feature.setAttributes([address, license])

                # Add feature to layer and layer to map
                layer_out.dataProvider().addFeature(feature)
                layer_out.updateExtents()
                project.addMapLayer(layer_out)

                # build bbox for auto-zoom feature
                bbox = [float(coord) for coord in response_json['boundingbox']]
                min_y, max_y, min_x, max_x = bbox
                bbox_geom = QgsGeometry.fromPolygonXY([[
                    QgsPointXY(min_x, min_y),
                    QgsPointXY(min_x, max_y),
                    QgsPointXY(max_x, max_y),
                    QgsPointXY(max_x, min_y),
                ]])

                # Transform bbox if map canvas has a different CRS
                if project.crs().authid() != 'EPSG:4326':
                    xform = QgsCoordinateTransform(crs_out, project.crs(),
                                                   project)
                    bbox_geom.transform(xform)

                self.iface.mapCanvas().zoomToFeatureExtent(
                    QgsRectangle.fromWkt(bbox_geom.asWkt()))
    def processAlgorithm(self, parameters, context, feedback):
        try:
            # read out parameters
            input_layer = self.parameterAsVectorLayer(parameters, self.INPUT,
                                                      context)
            in_crs = input_layer.crs()
            attribute_name = parameters[self.ATTRIBUTE_NAME]
            coverage_id = [
                self.coverages[i] for i in self.parameterAsEnums(
                    parameters, self.COVERAGE_ID, context)
            ][0]
            # start processing
            fields = input_layer.fields()
            fields.append(QgsField(attribute_name, QVariant.Double))
            field_names = [field.name() for field in fields]
            (sink, dest_id) = self.parameterAsSink(
                parameters,
                self.OUTPUT,
                context,
                fields,
                input_layer.wkbType(),
                in_crs,
            )
            if feedback.isCanceled():
                return {}

            wcs_proj_authid = self.get_native_proj_authid(coverage_id)

            if in_crs.authid() != wcs_proj_authid:
                wcs_crs = QgsCoordinateReferenceSystem(wcs_proj_authid)
                transform_input = QgsCoordinateTransform(
                    in_crs, wcs_crs, QgsProject.instance())
            for feature in input_layer.getFeatures():
                geom = feature.geometry()
                if in_crs.authid() != wcs_proj_authid:
                    geom.transform(transform_input)
                point_geom = QgsGeometry.asPoint(geom)
                point_xy = QgsPointXY(point_geom)
                x = point_xy.x()
                y = point_xy.y()
                attrs = feature.attributes()
                new_ft = QgsFeature(fields)
                for i in range(len(attrs)):
                    attr = attrs[i]
                    field_name = field_names[i]
                    new_ft.setAttribute(field_name, attr)
                ds = self.get_gdal_ds_from_wcs(x, y, coverage_id, feedback)
                nodata = self.get_nodata_from_gdal_ds(ds)
                ahn_val = self.get_val_from_gdal_ds(x, y, ds)
                ds = None

                if ahn_val == nodata:
                    fid = feature.id()
                    feedback.pushWarning(
                        f"NODATA value found for feature with id: {fid}, geom: POINT({x},{y})"
                    )
                    ahn_val = None
                new_ft.setAttribute(attribute_name, ahn_val)
                new_ft.setGeometry(geom)
                sink.addFeature(new_ft, QgsFeatureSink.FastInsert)
                if feedback.isCanceled():
                    return {}
            results = {}
            results[self.OUTPUT] = dest_id
            return results
        except Exception as e:
            traceback_str = traceback.format_exc()
            toolname = self.displayName()
            message = f"Unexpected error occured while running {toolname}: {e} - traceback: {traceback_str}"
            self.log_message(message, loglevel=2)
            raise QgsProcessingException(message)
Ejemplo n.º 17
0
    def accept(self):
        layer = self.inputMapLayerComboBox.currentLayer()
        if not layer:
            self.iface.messageBar().pushMessage("",
                                                "No Valid Layer",
                                                level=Qgis.Warning,
                                                duration=4)
        pointname = self.pointsNameLineEdit.text()
        linename = self.lineNameLineEdit.text()
        startXcol = self.startXFieldComboBox.currentIndex(
        )  # Returns -1 if none selected
        startYcol = self.startYFieldComboBox.currentIndex()
        endXcol = self.endXFieldComboBox.currentIndex()
        endYcol = self.endYFieldComboBox.currentIndex()
        startUseGeom = self.startCheckBox.isChecked()
        endUseGeom = self.endCheckBox.isChecked()
        inCRS = self.inputQgsProjectionSelectionWidget.crs()
        outCRS = self.outputQgsProjectionSelectionWidget.crs()
        lineType = self.lineTypeComboBox.currentIndex()
        showStart = self.showStartCheckBox.isChecked()
        showEnd = self.showEndCheckBox.isChecked()

        if (startUseGeom == False) and (startXcol == -1 or startYcol == -1):
            self.iface.messageBar().pushMessage(
                "",
                "Must specify valid starting point columns",
                level=Qgis.Warning,
                duration=4)
            return
        if (endUseGeom == False) and (endXcol == -1 or endYcol == -1):
            self.iface.messageBar().pushMessage(
                "",
                "Must specify valid ending point columns",
                level=Qgis.Warning,
                duration=4)
            return

        # If we are using the layer geometry then we ignore the selected input and output CRS
        if startUseGeom:
            inCRS = layer.crs()
        if endUseGeom:
            outCRS = layer.crs()

        # Get the field names for the input layer. The will be copied to the output layers
        fields = layer.fields()

        # Create the points and line output layers
        lineLayer = QgsVectorLayer("LineString?crs={}".format(outCRS.authid()),
                                   linename, "memory")
        pline = lineLayer.dataProvider()
        pline.addAttributes(fields)
        lineLayer.updateFields()

        if showStart or showEnd:
            pointLayer = QgsVectorLayer("Point?crs={}".format(outCRS.authid()),
                                        pointname, "memory")
            ppoint = pointLayer.dataProvider()
            ppoint.addAttributes(fields)
            pointLayer.updateFields()

        transform = QgsCoordinateTransform(inCRS, outCRS,
                                           QgsProject.instance())
        if inCRS != epsg4326:
            transto4326 = QgsCoordinateTransform(inCRS, epsg4326,
                                                 QgsProject.instance())
        if outCRS != epsg4326:
            transfrom4326 = QgsCoordinateTransform(epsg4326, outCRS,
                                                   QgsProject.instance())

        iter = layer.getFeatures()
        num_features = 0
        num_bad = 0
        maxseglen = settings.maxSegLength * 1000.0
        maxSegments = settings.maxSegments
        for feature in iter:
            num_features += 1
            try:
                if startUseGeom == True:
                    ptStart = feature.geometry().asPoint()
                else:
                    ptStart = QgsPointXY(float(feature[startXcol]),
                                         float(feature[startYcol]))
                if endUseGeom == True:
                    ptEnd = feature.geometry().asPoint()
                else:
                    ptEnd = QgsPointXY(float(feature[endXcol]),
                                       float(feature[endYcol]))
                # Create a new Line Feature
                fline = QgsFeature()
                if lineType == 0:  # Geodesic
                    # If the input is not 4326 we need to convert it to that and then back to the output CRS
                    if inCRS != epsg4326:  # Convert to 4326
                        ptStart = transto4326.transform(ptStart)
                        ptEnd = transto4326.transform(ptEnd)
                    pts = [ptStart]
                    l = self.geod.InverseLine(ptStart.y(), ptStart.x(),
                                              ptEnd.y(), ptEnd.x())
                    if l.s13 > maxseglen:
                        n = int(math.ceil(l.s13 / maxseglen))
                        if n > maxSegments:
                            n = maxSegments

                        seglen = l.s13 / n
                        for i in range(1, n):
                            s = seglen * i
                            g = l.Position(
                                s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                                | Geodesic.LONG_UNROLL)
                            pts.append(QgsPointXY(g['lon2'], g['lat2']))
                    pts.append(ptEnd)
                    if outCRS != epsg4326:  # Convert each point to the output CRS
                        for x, pt in enumerate(pts):
                            pts[x] = transfrom4326.transform(pt)
                    fline.setGeometry(QgsGeometry.fromPolylineXY(pts))
                    ptStart = pts[0]
                    ptEnd = pts[len(pts) - 1]
                elif lineType == 1:  # Great Circle
                    # If the input is not 4326 we need to convert it to that and then back to the output CRS
                    if inCRS != epsg4326:  # Convert to 4326
                        ptStart = transto4326.transform(ptStart)
                        ptEnd = transto4326.transform(ptEnd)
                    pts = LatLon.getPointsOnLine(
                        ptStart.y(),
                        ptStart.x(),
                        ptEnd.y(),
                        ptEnd.x(),
                        settings.maxSegLength * 1000.0,  # Put it in meters
                        settings.maxSegments + 1)
                    if outCRS != epsg4326:  # Convert each point to the output CRS
                        for x, pt in enumerate(pts):
                            pts[x] = transfrom4326.transform(pt)
                    fline.setGeometry(QgsGeometry.fromPolylineXY(pts))
                    ptStart = pts[0]
                    ptEnd = pts[len(pts) - 1]
                else:  # Simple line
                    '''Transform the starting and end points if the input CRS
                       and the output CRS are not the same and then create a 
                       2 point polyline'''
                    if inCRS != outCRS:
                        ptStart = transform.transform(ptStart)
                        ptEnd = transform.transform(ptEnd)
                    fline.setGeometry(
                        QgsGeometry.fromPolylineXY([ptStart, ptEnd]))
                fline.setAttributes(feature.attributes())
                pline.addFeatures([fline])
                # Add two point features
                if showStart:
                    fpoint = QgsFeature()
                    fpoint.setGeometry(QgsGeometry.fromPointXY(ptStart))
                    fpoint.setAttributes(feature.attributes())
                    ppoint.addFeatures([fpoint])
                if showEnd:
                    fpoint = QgsFeature()
                    fpoint.setGeometry(QgsGeometry.fromPointXY(ptEnd))
                    fpoint.setAttributes(feature.attributes())
                    ppoint.addFeatures([fpoint])
            except:
                num_bad += 1
                pass

        lineLayer.updateExtents()
        QgsProject.instance().addMapLayer(lineLayer)
        if showStart or showEnd:
            pointLayer.updateExtents()
            QgsProject.instance().addMapLayer(pointLayer)
        if num_bad != 0:
            self.iface.messageBar().pushMessage(
                "",
                "{} out of {} features failed".format(num_bad, num_features),
                level=Qgis.Warning,
                duration=3)

        self.close()
Ejemplo n.º 18
0
    def putKPPointsAlongLine(self, source, maxseglen):
        "We travel along the line and test if each consecutive segment should contain KPs and how many"
        layercrs = source.sourceCrs()
        if layercrs != KPTool.epsg4326:
            transto4326 = QgsCoordinateTransform(layercrs, KPTool.epsg4326,
                                                 QgsProject.instance())
            transfrom4326 = QgsCoordinateTransform(KPTool.epsg4326, layercrs,
                                                   QgsProject.instance())

        new_pt_layer = QgsVectorLayer(
            'Point', 'KP_points_' + str(source.name()),
            'memory')  # define new layer we will return
        new_pt_layer.setCrs(source.crs())  # get crs from input layer
        new_pt_layer.startEditing()
        provider_ptLayer = new_pt_layer.dataProvider(
        )  # provider for new layer to add the features to
        provider_ptLayer.addAttributes([QgsField('KP', QVariant.Double)])
        iterator = source.getFeatures()
        KP_label_count = 0

        for cnt, feature in enumerate(iterator):
            if feature.geometry().isMultipart():
                seg = feature.geometry().asMultiPolyline()
            else:
                seg = [feature.geometry().asPolyline()]
            numseg = len(seg)
            if numseg < 1 or len(seg[0]) < 2:
                self.iface.messageBar().pushMessage(
                    'Less than one segment in line layer',
                    level=Qgis.Critical,
                    duration=2)
                continue
            for line in seg:
                numpoints = len(line)
                if self.Reverse_KP_points == 2:  #reverse point order for reverse KP
                    _ = line.reverse()
                ptStart = QgsPointXY(line[0][0], line[0][1])
                new_kp_point = self.createFeatureFromPoint(
                    ptStart, KP_label_count)
                provider_ptLayer.addFeatures([new_kp_point])
                remaining_dist = 0.0  #remaining distance to next point
                if layercrs != KPTool.epsg4326:  # Convert to 4326
                    ptStart = transto4326.transform(ptStart)
                for x in range(1, numpoints):
                    ptEnd = QgsPointXY(line[x][0], line[x][1])
                    if layercrs != KPTool.epsg4326:  # Convert to 4326
                        ptEnd = transto4326.transform(ptEnd)
                    gline = KPTool.geod.InverseLine(ptStart.y(), ptStart.x(),
                                                    ptEnd.y(), ptEnd.x())
                    if remaining_dist + gline.s13 > maxseglen:  #we have to place at least one KP
                        s = maxseglen - remaining_dist
                        g = gline.Position(
                            s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                            | Geodesic.LONG_UNROLL)
                        ptKP = QgsPointXY(g['lon2'], g['lat2'])
                        if layercrs != KPTool.epsg4326:
                            ptKP = transfrom4326.transform(ptKP)
                        KP_label_count = KP_label_count + maxseglen
                        remaining_dist = remaining_dist + gline.s13 - maxseglen
                        new_kp_point = self.createFeatureFromPoint(
                            ptKP, KP_label_count)
                        provider_ptLayer.addFeatures([new_kp_point])
                        if remaining_dist > maxseglen:  #we need to place more KP pts
                            extra_from_start = s
                            n = int(remaining_dist / maxseglen)
                            for i in range(0, n):
                                s = maxseglen * (i + 1) + extra_from_start
                                g = gline.Position(
                                    s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                                    | Geodesic.LONG_UNROLL)
                                ptKP = QgsPointXY(g['lon2'], g['lat2'])
                                if layercrs != KPTool.epsg4326:  # Convert each point back to the output CRS
                                    ptKP = transfrom4326.transform(ptKP)
                                KP_label_count = KP_label_count + maxseglen
                                remaining_dist = remaining_dist - maxseglen
                                new_kp_point = self.createFeatureFromPoint(
                                    ptKP, KP_label_count)
                                provider_ptLayer.addFeatures([new_kp_point])
                    else:  #no KPs placed in this segment, keep the cumulative distance
                        remaining_dist = remaining_dist + gline.s13
                    ptStart = ptEnd
        new_pt_layer.commitChanges()
        return new_pt_layer
class SelectFeaturesTool(QgsMapTool):
    selection_clicked = pyqtSignal(list)

    def __init__(self, mission_track, canvas):
        QgsMapTool.__init__(self, canvas)
        self.setCursor(Qt.ArrowCursor)
        self.mission_track = mission_track
        self.layer = self.mission_track.get_mission_layer()
        self.rubber_band = None
        self.rubber_band_points = None
        self.selection_polygon = []
        self.indexes_within_list = []
        self.band_finished = True
        self.mCtrl = False
        self.p0, self.p1, self.p2, self.p3 = None, None, None, None
        self.mission_track.mission_changed.connect(self.update_rubber_band)
        self.mission_track.step_removed.connect(self.remove_rubber_band)
        self.wp = self.mission_track.find_waypoints_in_mission()
        self.layer.startEditing()
        self.rubber_band_vs_track_indexes = {}
        self.rubber_band_points = QgsRubberBand(self.canvas(),
                                                QgsWkbTypes.PointGeometry)
        self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rubber_band_points.setIconSize(10)
        self.rubber_band_points.setColor(QColor("green"))
        self.rubber_band_vertex_counter = 0

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Control:
            self.mCtrl = True

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Control:
            self.mCtrl = False
        if event.key() == Qt.Key_Escape:
            self.p0, self.p1, self.p2, self.p3 = None, None, None, None
            if self.rubber_band:
                self.rubber_band.reset(True)
            self.close_polygon_band()
            self.band_finished = True
            self.canvas().refresh()
            return

    def canvasPressEvent(self, event):
        if event.button() == Qt.LeftButton:
            point = self.toMapCoordinates(event.pos())
            # check if we have clicked on a vertex
            tolerance = self.calc_tolerance()
            vertex = self.find_vertex_at(event.pos(), tolerance)
            if self.mCtrl and vertex is not None:
                # if we have clicked on a vertex, identify which one
                # check if was already in the selection list
                if vertex not in self.indexes_within_list:
                    # add it
                    self.indexes_within_list.append(vertex)
                    self.update_rubber_band()
                else:
                    # remove it
                    self.indexes_within_list.remove(vertex)
                    self.update_rubber_band()

                self.band_finished = True
            elif vertex is None:
                # if we have not clicked on a vertex and there's no polygon band, start it
                if not len(self.selection_polygon
                           ) and self.band_finished and not self.mCtrl:
                    self.selection_clicked.emit(list())
                    self.band_finished = False
                    self.rubber_band = QgsRubberBand(
                        self.canvas(), QgsWkbTypes.PolygonGeometry)
                    self.rubber_band.setWidth(2)
                    select_green = QColor("green")
                    select_green.setAlpha(128)
                    self.rubber_band.setColor(select_green)

                    if event.button() == Qt.LeftButton:
                        # Left click -> add vertex
                        self.p0 = QgsPointXY(point.x(), point.y())
                        self.selection_polygon.append(self.p0)
                elif len(self.selection_polygon) == 1 and not self.mCtrl:
                    if event.button() == Qt.LeftButton:
                        # Left click -> add vertex
                        self.p2 = QgsPointXY(point.x(), point.y())
                        self.p1 = QgsPointXY(self.p2.x(), self.p0.y())
                        self.p3 = QgsPointXY(self.p0.x(), self.p2.y())
                        self.selection_polygon.append(self.p1)
                        self.selection_polygon.append(self.p2)
                        self.selection_polygon.append(self.p3)
                        self.band_finished = True
                        self.set_selection()
                        self.close_polygon_band()

            self.selection_clicked.emit(self.indexes_within_list)

    def find_vertex_at(self, pos, tolerance):
        """
        get the vertex that is closer to the clicked point


        :param pos: The point that we've clicked
        :param tolerance: The tolerance of pos
        :return: vertex or None
        """
        if len(self.wp) > 0:
            dist_to_vertex = []
            for v in range(0, len(self.wp)):
                a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v]))
                dist_to_vertex.append(
                    math.sqrt((pos.x() - a1.x())**2 + (pos.y() - a1.y())**2))

            vertex = dist_to_vertex.index(min(dist_to_vertex))
            if min(dist_to_vertex) > tolerance:
                return None
            else:
                return vertex
        else:
            return None

    def calc_tolerance(self):
        """
        Compute the tolerance on canvas

        :return: tolerance
        """
        # 2% of tolerance
        width_tolerance = 0.02 * self.canvas().width()
        height_tolerance = 0.02 * self.canvas().height()
        if width_tolerance < height_tolerance:
            tolerance = width_tolerance
        else:
            tolerance = height_tolerance
        return tolerance

    def canvasMoveEvent(self, event):

        if not self.band_finished and not self.mCtrl:
            self.p2 = self.toMapCoordinates(event.pos())
            self.p1 = QgsPointXY(self.p2.x(), self.p0.y())
            self.p3 = QgsPointXY(self.p0.x(), self.p2.y())

            self.selection_polygon.append(self.p1)
            self.selection_polygon.append(self.p2)
            self.selection_polygon.append(self.p3)
            self.rubber_band.setToGeometry(
                QgsGeometry.fromPolygonXY([self.selection_polygon]), None)
            self.selection_polygon.pop()
            self.selection_polygon.pop()
            self.selection_polygon.pop()

    def set_selection(self):
        """
        Set vertices highlight according to polygon
        """
        # Check which features are within the polygon
        mission_track = self.layer.getFeatures()  # get mission track feature

        for f in mission_track:  # loop although mission layer only has one feature
            vertices_it = f.geometry().vertices()
        polygon_geom = QgsGeometry.fromPolygonXY([self.selection_polygon])
        vertices_within_list = []
        # self.indexes_within_list = []
        vertex_index = 0

        # Highlight them using a point rubber band
        self.rubber_band_vertex_counter = 0
        for v in vertices_it:
            point_geom = QgsGeometry.fromPointXY(QgsPointXY(v.x(), v.y()))
            if point_geom.within(polygon_geom):
                vertices_within_list.append(v)
                if not (vertex_index in self.indexes_within_list
                        ):  # only add if not already present
                    self.indexes_within_list.append(vertex_index)
                    self.rubber_band_points.addPoint(QgsPointXY(v.x(), v.y()))
                    self.rubber_band_vertex_counter = self.rubber_band_vertex_counter + 1
                    self.rubber_band_vs_track_indexes[
                        vertex_index] = self.rubber_band_vertex_counter - 1
            vertex_index = vertex_index + 1

    def update_rubber_band(self):
        if self.rubber_band_points:
            self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
            self.rubber_band_vs_track_indexes = {}
            self.rubber_band_vertex_counter = 0
        self.wp = self.mission_track.find_waypoints_in_mission()
        if len(self.indexes_within_list) > 0:

            selected_vertices = self.mission_track.find_waypoints_in_mission(
                self.indexes_within_list)
            for v in selected_vertices:
                vertex_index = 0
                for point in self.wp:
                    if v == point:
                        pc = self.toLayerCoordinates(self.layer, QgsPointXY(v))
                        self.rubber_band_points.addPoint(pc)
                        self.rubber_band_vertex_counter = self.rubber_band_vertex_counter + 1
                        self.rubber_band_vs_track_indexes[
                            vertex_index] = self.rubber_band_vertex_counter - 1
                    vertex_index = vertex_index + 1

        self.set_geometry()

    def remove_rubber_band(self, wp):
        # if self.rubber_band_points:
        #    self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
        self.indexes_within_list.remove(wp)
        self.update_rubber_band()

    def set_geometry(self):
        """
        Save rubber band to geometry of the layer
        """
        if self.layer.featureCount() == 0:
            # no feature yet created
            f = QgsFeature()
            if len(self.wp) == 1:
                f.setGeometry(
                    QgsGeometry.fromPointXY(
                        QgsPointXY(self.wp[0].x(), self.wp[0].y())))
            else:
                f.setGeometry(QgsGeometry.fromPolyline(self.wp))
            # self.layer.dataProvider().addFeatures([f])
            self.layer.addFeatures([f])
        else:
            # mission feature present, edit geometry
            feats = self.layer.getFeatures()
            for f in feats:
                if len(self.wp) == 1:
                    self.layer.changeGeometry(
                        f.id(),
                        QgsGeometry.fromPointXY(
                            QgsPointXY(self.wp[0].x(), self.wp[0].y())))
                else:
                    self.layer.changeGeometry(
                        f.id(), QgsGeometry.fromPolyline(self.wp))
        self.layer.commitChanges()
        self.layer.startEditing()

    def close_polygon_band(self):
        self.selection_polygon = []
        if self.rubber_band is not None:
            self.rubber_band.reset()
            self.canvas().scene().removeItem(self.rubber_band)
        self.rubber_band = None

    def close_highlight_band(self):
        self.rubber_band_points.reset()
        self.canvas().scene().removeItem(self.rubber_band_points)
        self.rubber_band_points = None

    def deactivate(self):
        if self.rubber_band:
            self.close_polygon_band()
        if self.rubber_band_points:
            self.close_highlight_band()

        try:
            self.mission_track.mission_changed.disconnect(
                self.update_rubber_band)
            self.mission_track.step_removed.disconnect(self.remove_rubber_band)
        except:
            logger.info("no connected to signal")
        self.layer.commitChanges()
Ejemplo n.º 20
0
class ShowExtent(QgsMapTool):
    """Show an extent in the canvas"""

    ShowEnded = pyqtSignal()

    def __init__(self, canvas):
        """Constructor"""
        QgsMapTool.__init__(self, canvas)

        self.canvas = canvas
        self.rubberBand = QgsRubberBand(self.canvas,
                                        QgsWkbTypes.PolygonGeometry)
        color = QColor(30, 230, 30, 65)
        self.rubberBand.setColor(color)
        self.rubberBand.setWidth(1)

        self.start_point = self.end_point = None

    def canvasPressEvent(self, event):
        """Change the outcome of the click event to end  the ongoing process."""
        _ = event
        self.rubberBand.hide()
        self.ShowEnded.emit()

    def show_extent(self, extent: QgsRectangle):
        """Display the extent on the canvas"""
        self.start_point = QgsPointXY(extent.xMinimum(), extent.yMinimum())
        self.end_point = QgsPointXY(extent.xMaximum(), extent.yMaximum())
        self.transform_coordinates()

        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)

        point1 = QgsPointXY(self.start_point.x(), self.start_point.y())
        point2 = QgsPointXY(self.start_point.x(), self.end_point.y())
        point3 = QgsPointXY(self.end_point.x(), self.end_point.y())
        point4 = QgsPointXY(self.end_point.x(), self.start_point.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        self.rubberBand.addPoint(point4, True)
        self.rubberBand.show()

        rect = QgsRectangle(self.start_point, self.end_point)
        self.canvas.setExtent(rect)

    def transform_coordinates(self):
        """Transform the coordinates in 4326."""
        if self.start_point is None or self.end_point is None:
            return None
        if self.start_point.x() == self.end_point.x() or self.start_point.y(
        ) == self.end_point.y():
            return None

        # Defining the crs from src and destiny
        epsg = self.canvas.mapSettings().destinationCrs().authid()
        crs_dest = QgsCoordinateReferenceSystem(epsg)
        crs_src = QgsCoordinateReferenceSystem('EPSG:4326')

        # Creating a transformer
        transformer = QgsCoordinateTransform(crs_src, crs_dest,
                                             QgsProject.instance())

        # Transforming the points
        self.start_point = transformer.transform(self.start_point)
        self.end_point = transformer.transform(self.end_point)
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.PrmInputLayer, context)
        measureTotal = self.parameterAsBool(parameters, self.PrmMeasureTotalLength, context)
        units = self.parameterAsInt(parameters, self.PrmUnitsOfMeasure, context)
        autoStyle = self.parameterAsBool(parameters, self.PrmAutomaticStyline, context)

        srcCRS = source.sourceCrs()

        f = QgsFields()
        f.append(QgsField("label", QVariant.String))
        f.append(QgsField("distance", QVariant.Double))
        f.append(QgsField("units", QVariant.String))
        if not measureTotal:
            f.append(QgsField("heading_to", QVariant.Double))
            f.append(QgsField("total_distance", QVariant.Double))

        (sink, dest_id) = self.parameterAsSink(
            parameters, self.PrmOutputLayer, context, f, QgsWkbTypes.LineString, srcCRS)

        if srcCRS != epsg4326:
            geomTo4326 = QgsCoordinateTransform(srcCRS, epsg4326, QgsProject.instance())
            toSinkCrs = QgsCoordinateTransform(epsg4326, srcCRS, QgsProject.instance())

        wkbtype = source.wkbType()
        geomtype = QgsWkbTypes.geometryType(wkbtype)

        featureCount = source.featureCount()
        total = 100.0 / featureCount if featureCount else 0

        iterator = source.getFeatures()
        for cnt, feature in enumerate(iterator):
            if feedback.isCanceled():
                break

            if geomtype == QgsWkbTypes.LineGeometry:
                if feature.geometry().isMultipart():
                    ptdata = [feature.geometry().asMultiPolyline()]
                else:
                    ptdata = [[feature.geometry().asPolyline()]]
            else: #polygon
                if feature.geometry().isMultipart():
                    ptdata = feature.geometry().asMultiPolygon()
                else:
                    ptdata = [feature.geometry().asPolygon()]
            if len(ptdata) < 1:
                continue

            for seg in ptdata:
                if len(seg) < 1:
                    continue
                if measureTotal:
                    for pts in seg:
                        numpoints = len(pts)
                        if numpoints < 2:
                            continue
                        f = QgsFeature()
                        f.setGeometry(QgsGeometry.fromPolylineXY(pts))
                        ptStart = QgsPointXY(pts[0].x(), pts[0].y())
                        if srcCRS != epsg4326: # Convert to 4326
                            ptStart = geomTo4326.transform(ptStart)
                        # Calculate the total distance of this line segment
                        distance = 0.0
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(pts[x].x(), pts[x].y())
                            if srcCRS != epsg4326: # Convert to 4326
                                ptEnd = geomTo4326.transform(ptEnd)
                            l = geod.Inverse(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                            distance += l['s12']
                            ptStart = ptEnd

                        distance = self.unitDistance(units, distance) # Distance converted to the selected unit of measure
                        attr = ["{:.2f} {}".format(distance, unitsAbbr[units]), distance, unitsAbbr[units] ]
                        f.setAttributes(attr)
                        sink.addFeature(f)
                else:
                    for pts in seg:
                        numpoints = len(pts)
                        if numpoints < 2:
                            continue
                        ptStart = QgsPointXY(pts[0].x(), pts[0].y())
                        if srcCRS != epsg4326: # Convert to 4326
                            ptStart = geomTo4326.transform(ptStart)
                        # Calculate the total distance of this line segment
                        totalDistance = 0.0
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(pts[x].x(), pts[x].y())
                            if srcCRS != epsg4326: # Convert to 4326
                                ptEnd = geomTo4326.transform(ptEnd)
                            l = geod.Inverse(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                            totalDistance += l['s12']
                            ptStart = ptEnd

                        totalDistance = self.unitDistance(units, totalDistance) # Distance converted to the selected unit of measure

                        ptStart = QgsPointXY(pts[0].x(), pts[0].y())
                        if srcCRS != epsg4326: # Convert to 4326
                            pt1 = geomTo4326.transform(ptStart)
                        else:
                            pt1 = ptStart
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(pts[x].x(), pts[x].y())
                            f = QgsFeature()
                            f.setGeometry(QgsGeometry.fromPolylineXY([ptStart, ptEnd]))
                            if srcCRS != epsg4326: # Convert to 4326
                                pt2 = geomTo4326.transform(ptEnd)
                            else:
                                pt2 = ptEnd
                            l = geod.Inverse(pt1.y(), pt1.x(), pt2.y(), pt2.x())
                            ptStart = ptEnd
                            pt1 = pt2
                            distance = self.unitDistance(units, l['s12'])
                            attr = ["{:.2f} {}".format(distance, unitsAbbr[units]), distance, unitsAbbr[units], l['azi1'],totalDistance ]
                            f.setAttributes(attr)
                            sink.addFeature(f)

            if cnt % 100 == 0:
                feedback.setProgress(int(cnt * total))
        if autoStyle and context.willLoadLayerOnCompletion(dest_id):
            context.layerToLoadOnCompletionDetails(dest_id).setPostProcessor(StylePostProcessor.create())

        return {self.PrmOutputLayer: dest_id}
Ejemplo n.º 22
0
    def processAlgorithm(self, parameters, context, feedback):

        layer = self.parameterAsSource(parameters, self.Centerline, context)
        layer2 = self.parameterAsVectorLayer(parameters, self.Polygons, context)
        samples = parameters[self.Samples]
        distance = parameters[self.Distance]
        FC = parameters[self.FC]

        context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)

        if layer.sourceCrs() != layer2.sourceCrs():
            feedback.reportError(QCoreApplication.translate('Error','WARNING: Centerline and Polygon input do not have the same projection'))

        Precision=5
        if FC:
            field_names = ['Distance','SP_Dist','Width','Deviation','DWidthL','DWidthR']
        else:
            field_names = ['Distance','SP_Dist','Width','Deviation','DWidthL','DWidthR','Diff']

        fields = QgsFields()
        fields.append( QgsField('ID', QVariant.Int ))

        for name in field_names:
            fields.append( QgsField(name, QVariant.Double ))

        (writer, dest_id) = self.parameterAsSink(parameters, self.Output, context,
                                               fields, QgsWkbTypes.LineString, layer.sourceCrs())

        fet = QgsFeature()

        field_check =layer.fields().indexFromName('ID')
        field_check2 =layer2.fields().indexFromName('ID')
        if field_check == -1 or field_check2 == -1:
            feedback.reportError(QCoreApplication.translate('Error','Centerline and Polygon input feature require a matching ID field!'))
            return {}

        total = 0
        counts = {}
        if FC:
            vertices = st.run("native:extractvertices", {'INPUT':layer2,'OUTPUT':'memory:'})
            index = QgsSpatialIndex(vertices['OUTPUT'].getFeatures())
            data = {feature.id():feature for feature in vertices['OUTPUT'].getFeatures()}
        SPS = {}
        SPE = {}
        values = {}
        values2 = {}
        feats = {f["ID"]:f for f in layer2.getFeatures()}
        feedback.pushInfo(QCoreApplication.translate('Update','Defining Centerline Paths'))
        for enum,feature in enumerate(layer.getFeatures()):
            total += 1
            try:
                pnt = feature.geometry()
                if pnt.isMultipart():
                    pnt = pnt.asMultiPolyline()[0]
                else:
                    pnt = pnt.asPolyline()

                startx,starty = round(pnt[0][0],Precision),round(pnt[0][1],Precision)
                endx,endy = round(pnt[-1][0],Precision),round(pnt[-1][1],Precision)
                ID = feature['ID']
                c =  feature['Distance']
                if ID in SPS: #Get start and endpoint of each centerline
                    v = values[ID]
                    v2 = values2[ID]
                    v3 = counts[ID] + 1
                    if c > v:
                        SPS[ID] = [(startx,starty),(endx,endy)]
                        values[ID] = c
                    if c < v2:
                        SPE[ID] = [(startx,starty),(endx,endy)]
                        values2[ID] = c
                    counts[ID] = v3
                else:
                    SPS[ID] = [(startx,starty),(endx,endy)]
                    values[ID] = c
                    SPE[ID] = [(startx,starty),(endx,endy)]
                    values2[ID] = c
                    counts[ID] = 1

            except Exception as e:
                feedback.reportError(QCoreApplication.translate('Error','%s'%(e)))
                continue ##Possible Collapsed Polyline?

        del values,values2
        total = 100.0/float(total)
        ID = None
        feedback.pushInfo(QCoreApplication.translate('Update','Creating Width Measurements'))
        report = True
        for enum,feature in enumerate(layer.getFeatures()):
            try:
                if total != -1:
                    feedback.setProgress(int(enum*total))
                pnt = feature.geometry()
                L = pnt.length()
                if pnt.isMultipart():
                    pnt = pnt.asMultiPolyline()[0]
                else:
                    pnt = pnt.asPolyline()

                curID = feature["ID"]
                if ID != curID:
                    startx,starty = round(pnt[0][0],Precision),round(pnt[0][1],Precision)
                    midx,midy = round(pnt[-1][0],Precision),round(pnt[-1][1],Precision)

                    ID = curID
                    if samples > 0:
                        if distance:
                            Counter = L
                            Limit = float(samples)
                        else:
                            Counter = 1
                            Limit = round((counts[ID]/float(samples)),0)

                    continue

                endx,endy = round(pnt[-1][0],Precision),round(pnt[-1][1],Precision)

                if samples > 0:
                    if distance:
                        Counter += L
                    else:
                        Counter += 1
                    if Counter < Limit:
                        startx,starty = midx,midy
                        midx,midy = endx,endy
                        continue
                if FC:
                    startx,starty = round(pnt[0][0],Precision),round(pnt[0][1],Precision)
                    near = index.nearestNeighbor(QgsPointXY(startx,starty), 1)
                    SPv = 1e12

                    midx,midy = data[near[0]].geometry().asPoint()

                    dx,dy = startx-midx,starty-midy
                    shortestPath = sqrt((dx**2)+(dy**2))
                    if shortestPath < SPv:
                        SPv = shortestPath

                    near = index.nearestNeighbor(QgsPointXY(endx,endy), 1)
                    midx,midy = data[near[0]].geometry().asPoint()
                    dx,dy = endx-midx,endy-midy

                    shortestPath = sqrt((dx**2)+(dy**2))
                    if shortestPath < SPv:
                        SP = shortestPath

                else:

                    m = ((starty - endy)/(startx - endx)) #Slope
                    inter = feats[curID]
                    Distance = inter.geometry().boundingBox().width()/2

                    if startx==endx: #if vertical
                        x1,y1 = midx+Distance,midy
                        x2,y2 = midx - Distance,midy
                    else:
                        m = ((starty - endy)/(startx - endx)) #Slope
                        angle = degrees(atan(m)) + 90

                        m = tan(radians(angle)) #Angle to Slope
                        c,s = (1/sqrt(1+m**2),m/sqrt(1+m**2)) #cosine and sin
                        x1,y1 = (midx + Distance*(c),midy + Distance*(s))
                        x2,y2 = (midx - Distance*(c),midy - Distance*(s))

                    geom = QgsGeometry.fromPolylineXY([QgsPointXY(x1,y1),QgsPointXY(midx,midy),QgsPointXY(x2,y2)])

                    geom = geom.intersection(inter.geometry())

                    if geom.isMultipart():
                        polyline = geom.asMultiPolyline()
                        if len(polyline) == 0:
                            startx,starty = midx,midy
                            midx,midy = endx,endy
                            continue

                        for line in polyline:
                            if len(line)==3:
                                t=1
                                start,mid,end = line
                                geom1 = QgsGeometry.fromPolylineXY([QgsPointXY(start[0],start[1]),QgsPointXY(mid[0],mid[1])])
                                geom2 = QgsGeometry.fromPolylineXY([QgsPointXY(mid[0],mid[1]),QgsPointXY(end[0],end[1])])
                                geom = QgsGeometry.fromPolylineXY([QgsPointXY(start[0],start[1]),QgsPointXY(mid[0],mid[1]),QgsPointXY(end[0],end[1])])
                                break

                    else:
                        try:
                            line = geom.asPolyline()
                        except Exception as e:
                            startx,starty = midx,midy
                            midx,midy = endx,endy
                            if report:
                                report = False
                                feedback.reportError(QCoreApplication.translate('Error','Width measurement along centerline does not intersect with input polygons. Check 1. ID fields corresponds between centerline and polygons 2. Geometry of centerline and polygon inputs by using the "Fix Geometries" tool'))
                            continue
                        geom1 = QgsGeometry.fromPolylineXY([QgsPointXY(line[0][0],line[0][1]),QgsPointXY(line[1][0],line[1][1])])
                        geom2 = QgsGeometry.fromPolylineXY([QgsPointXY(line[1][0],line[1][1]),QgsPointXY(line[2][0],line[2][1])])
                    Widths = [geom1.length(),geom2.length()]


                SP = list(SPS[curID])
                SP.extend(list(SPE[curID]))
                D = 0

                for start,end in combinations(SP,2):
                    dx = start[0] - end[0]
                    dy =  start[1] - end[1]
                    shortestPath = sqrt((dx**2)+(dy**2))
                    if shortestPath > D:
                        D = shortestPath
                        s = QgsPointXY(start[0],start[1])
                        e = QgsPointXY(end[0],end[1])

                m = s.sqrDist(e)

                u = ((midx - s.x()) * (e.x() - s.x()) + (midy - s.y()) * (e.y() - s.y()))/(m)
                x = s.x() + u * (e.x() - s.x())
                y = s.y() + u * (e.y() - s.y())
                d = ((e.x()-s.x())*(midy-s.y()) - (e.y() - s.y())*(midx - s.x())) #Determine which side of the SP the symmetry occurs

                dx = s.x() - e.x()
                dy =  s.y() - e.y()
                shortestPath = sqrt((dx**2)+(dy**2))

                dx = s.x() - x
                dy =  s.y() - y
                shortestPath1 = sqrt((dx**2)+(dy**2))

                if shortestPath < shortestPath1:
                    sym = QgsGeometry.fromPolylineXY([QgsPointXY(e.x(),e.y()),QgsPointXY(midx,midy)])
                else:
                    sym = QgsGeometry.fromPolylineXY([QgsPointXY(x,y),QgsPointXY(midx,midy)])

                if d < 0:
                    DW = -(sym.length())
                else:
                    DW = sym.length()

                if FC:
                    W = SPv*2
                    rows = [curID,feature['Distance'],feature['SP_Dist'],W,DW,(W/2)+DW,-(W/2)+DW]
                    geom = feature.geometry()
                else:
                    W = geom.length()

                    rows = [curID,feature['Distance'],feature['SP_Dist'],W,DW,(W/2)+DW,-(W/2)+DW,(min(Widths)/max(Widths))*100]

                startx,starty = midx,midy
                midx,midy = endx,endy

                fet.setGeometry(geom)
                fet.setAttributes(rows)
                writer.addFeature(fet)

                if distance:
                    Counter -= samples
                else:
                    Counter = 0

            except Exception as e:
                #feedback.reportError(QCoreApplication.translate('Error','%s'%(e)))
                startx,starty = midx,midy
                midx,midy = endx,endy
                continue
        del writer
        if FC:
            del data

        del SPS,SPE

        return {self.Output:dest_id}
Ejemplo n.º 23
0
 def dxf_ok_param(self, dic_param):
     self.nw_params = dic_param
     idx_prec = 1
     # Find the creator
     if "createur" in self.nw_params:
         ge_createur = self.nw_params["createur"]
     else:
         ge_createur = self.user
     # Determine the precision class attribute
     for (idx, prec_val) in enumerate(self.precision_class):
         if self.precision_class[idx][1] == self.nw_params["prec_class"]:
             idx_prec = idx
     idx_prec = int(self.precision_class[idx_prec][0])
     # Find delim_pub
     if "delim_pub" in self.nw_params:
         delim_pub = self.nw_params["delim_pub"]
         
     # Create the list of all blk_typo_natures
     blk_lst = []
     if "blk_corrs" in self.nw_params:
         for k in list(self.nw_params["blk_corrs"].keys()):
             blk_lst.append(k)
             
     # Create the list of all lim_typo_natures
     lim_lst = []
     if "lim_lyrs" in self.nw_params:
         for k in list(self.nw_params["lim_lyrs"].keys()):
             lim_lst.append(k)
     
     # Transformations to obtain the WGS84 or the CC coordinates
     coords_tr_wgs, coords_tr_cc = crs_trans_params(self.canvas, self.project)
     
     # Creation of the vertices        
     self.iface.setActiveLayer(self.l_edge)
     self.iface.setActiveLayer(self.l_vertex)    
     elim_pts = []
     vtx_blk_ents = [entity for entity in self.dwg_ents if entity.layer == self.nw_params["vtx_lyr"] and entity.dxftype == "INSERT"]
     for pt_type in blk_lst:
         if self.nw_params["blk_corrs"][pt_type] == all_blks:
             vtx_curtypeblks = vtx_blk_ents
         else:
             vtx_curtypeblks = [entity for entity in vtx_blk_ents if entity.name == self.nw_params["blk_corrs"][pt_type]]
         for blk in vtx_curtypeblks:
             blk_pt = blk.insert
             nw_pt = QgsPointXY(float(blk_pt[0]), float(blk_pt[1]))
             nw_pt_wgs = coords_tr_wgs.transform(nw_pt)
             # Check if the new vertex is in the tolerance of an existing vertex in the RFU
             to_create = True
             id_ptintol = NULL
             for vtx_feat in self.original_l_vtx.getFeatures():
                 vtx_feat_g = vtx_feat.geometry()
                 vtx_tol = vtx_feat['som_tolerance']
                 if vtx_feat_g.type() == QgsWkbTypes.PointGeometry:
                     vtx_feat_pt = vtx_feat_g.asPoint()
                     vtx_feat_pt_cc = coords_tr_cc.transform(vtx_feat_pt)
                     # if find_near(vtx_feat_pt_cc, nw_pt, self.tol_spt):
                     pt_in_tol = find_near(vtx_feat_pt_cc, nw_pt, vtx_tol)
                     if pt_in_tol[0]:
                         # Case of existing RFU point in the tolerance distance
                         if vtx_feat['@id_noeud']:
                             id_ptintol = vtx_feat['@id_noeud']
                             if pt_in_tol[1] > 0:
                                 m_box = mbox_w_params(tl_pt_exst_rfu, txt_pt_exst_rfu, 
                                                         inftxt_pt_exst_rfu.format(nw_pt.x(), nw_pt.y(),
                                                         float(vtx_tol), id_ptintol))
                             # Case of strictly identical point
                             else:
                                 elim_dbpt = []
                                 elim_dbpt.append(nw_pt)
                                 elim_dbpt.append(vtx_feat_pt_cc)
                                 elim_pts.append(elim_dbpt)
                                 to_create = False
                                 m_box = mbox_w_params(tl_ptrfu_dbl, txt_ptrfu_dbl, 
                                                         inftxt_ptrfu_dbl.format(nw_pt.x(), nw_pt.y(), id_ptintol))
                         # Case of double point in the file imported
                         else:
                             m_box = mbox_w_params(tl_pt_dbl, txt_pt_dbl,
                                                     inftxt_pt_dbl.format(nw_pt.x(), nw_pt.y()))
                             # Add the list of new point eliminated and corresponding point 
                             # in the RFU to the list of eliminated points 
                             # (list of 2 point lists)
                             elim_dbpt = []
                             elim_dbpt.append(nw_pt)
                             elim_dbpt.append(vtx_feat_pt_cc)
                             elim_pts.append(elim_dbpt)
                             to_create = False
                         m_box.exec_()
             # Creation of the RFU objects in the layers
             if to_create:
                 create_nw_feat( self.l_vertex, 
                                 QgsGeometry.fromPointXY(nw_pt_wgs), 
                                 [NULL, NULL, ge_createur, delim_pub, pt_type, pt_type, idx_prec, float("{0:.02f}".format(nw_pt.x())), float("{0:.02f}".format(nw_pt.y())), self.cc, 0.0, "false", id_ptintol]
                                 )
     
     # Creation of the limits
     self.iface.setActiveLayer(self.l_vertex)
     self.iface.setActiveLayer(self.l_edge)
     for lim_type in lim_lst:            
         edge_ents = [entity for entity in self.dwg_ents if entity.layer == self.nw_params["lim_lyrs"][lim_type] and \
                         (entity.dxftype == "LWPOLYLINE" or entity.dxftype == "LINE")]
         for lwp_ent in edge_ents:
             lwp_ent_pts = []
             if lwp_ent.dxftype == "LWPOLYLINE":
                 lwp_ent_pts = lwp_ent.points
                 if lwp_ent.is_closed:
                     lwp_ent_pts.append(lwp_ent_pts[0])
             if lwp_ent.dxftype == "LINE":
                 lwp_ent_pts.append(lwp_ent.start)
                 lwp_ent_pts.append(lwp_ent.end)
             for idpt, lwp_pt in enumerate(lwp_ent_pts):
                 if idpt < (len(lwp_ent_pts) - 1):
                     start_pt_cc = QgsPointXY(float(lwp_pt[0]), float(lwp_pt[1]))
                     end_pt_cc = QgsPointXY(float(lwp_ent_pts[idpt + 1][0]), float(lwp_ent_pts[idpt + 1][1]))
                     # Creation only if no double point
                     if check_no_dblpt(start_pt_cc, end_pt_cc):
                         # Check if the point is an eliminated point
                         # If yes, use the corresponding RFU point instead
                         for elim_pt in elim_pts:
                             if start_pt_cc == elim_pt[0]:
                                 start_pt_cc = elim_pt[1]
                             if end_pt_cc == elim_pt[0]:
                                 end_pt_cc = elim_pt[1]
                         start_pt = coords_tr_wgs.transform(start_pt_cc)
                         end_pt = coords_tr_wgs.transform(end_pt_cc)
                         # Creation of the new RFU objects in the layers
                         # Create line geometry
                         line = QgsGeometry.fromPolylineXY([start_pt, end_pt])
                         # Check if the lines intersects
                         to_create = check_limit_cross(line, self.original_l_edge, ge_createur, delim_pub, lim_type, self.canvas, True)
                         # Creation of the RFU objects in the layer
                         if to_create:
                             # Create the feature
                             create_nw_feat(self.l_edge, line, [NULL, NULL, ge_createur, delim_pub, lim_type])
     # Refresh the canvas
     self.canvas.refresh()
Ejemplo n.º 24
0
 def processAlgorithm(self, parameters, context, feedback):
     source = self.parameterAsSource(parameters, self.PrmInputLayer, context)
     sourceCrs = self.parameterAsCrs(parameters, self.PrmInputCRS, context)
     sinkCrs = self.parameterAsCrs(parameters, self.PrmOutputCRS, context)
     lineType = self.parameterAsInt(parameters, self.PrmLineType, context)
     startUseGeom =  self.parameterAsBool(parameters, self.PrmStartUseLayerGeom, context)
     startXcol = self.parameterAsString(parameters, self.PrmStartXField, context)
     startYcol = self.parameterAsString(parameters, self.PrmStartYField, context)
     endUseGeom =  self.parameterAsBool(parameters, self.PrmEndUseLayerGeom, context)
     endXcol = self.parameterAsString(parameters, self.PrmEndXField, context)
     endYcol = self.parameterAsString(parameters, self.PrmEndYField, context)
     showStart =  self.parameterAsBool(parameters, self.PrmShowStartPoint, context)
     showEnd =  self.parameterAsBool(parameters, self.PrmShowEndPoint, context)
     dateLine =  self.parameterAsBool(parameters, self.PrmDateLineBreak, context)
     
     if dateLine and lineType <= 1:
         isMultiPart = True
     else:
         isMultiPart = False
     
     if isMultiPart:
         (lineSink, lineDest_id) = self.parameterAsSink(parameters,
             self.PrmOutputLineLayer, context, source.fields(),
             QgsWkbTypes.MultiLineString, sinkCrs)
     else:
         (lineSink, lineDest_id) = self.parameterAsSink(parameters,
             self.PrmOutputLineLayer, context, source.fields(),
             QgsWkbTypes.LineString, sinkCrs)
     (ptSink, ptDest_id) = self.parameterAsSink(parameters,
         self.PrmOutputPointLayer, context, source.fields(),
         QgsWkbTypes.Point, sinkCrs)
         
     if not ptSink:
         if showStart or showEnd:
             feedback.pushInfo(tr('Output point layer was set to [skip output]. No point layer will be generated.'))
         showStart = False
         showEnd = False
     if (startUseGeom or endUseGeom) and (source.wkbType() != QgsWkbTypes.Point):
         msg = tr('In order to use the layer geometry for the start or ending points, the input layer must be of type Point')
         feedback.reportError(msg)
         raise QgsProcessingException(msg)
         
     if (not startUseGeom and (not startXcol or not startYcol)) or (not endUseGeom and (not endXcol or not endYcol)):
         msg = tr('Please select valid starting and ending point columns')
         feedback.reportError(msg)
         raise QgsProcessingException(msg)
     if source.wkbType() != QgsWkbTypes.Point and (startUseGeom or endUseGeom):
         msg = tr("In order to select the input layer's geometry as a beginning or ending point it must be a Point vector layer.")
         feedback.reportError(msg)
         raise QgsProcessingException(msg)
         
     # Set up CRS transformations
     geomCrs = source.sourceCrs()
     if (startUseGeom or endUseGeom) and (geomCrs != epsg4326):
         geomTo4326 = QgsCoordinateTransform(geomCrs, epsg4326, QgsProject.instance())
     if sourceCrs != epsg4326:
         sourceTo4326 = QgsCoordinateTransform(sourceCrs, epsg4326, QgsProject.instance())
     if sinkCrs != epsg4326:
         toSinkCrs = QgsCoordinateTransform(epsg4326, sinkCrs, QgsProject.instance())
         
         
     featureCount = source.featureCount()
     total = 100.0 / featureCount if featureCount else 0
     numBad = 0
     maxseglen = settings.maxSegLength*1000.0
     maxSegments = settings.maxSegments
     
     iterator = source.getFeatures()
     for cnt, feature in enumerate(iterator):
         if (cnt % 100 == 0) and feedback.isCanceled():
             break
         try:
             if startUseGeom:
                 ptStart = feature.geometry().asPoint()
                 if geomCrs != epsg4326:
                     ptStart = geomTo4326.transform(ptStart)
             else:
                 ptStart = QgsPointXY(float(feature[startXcol]), float(feature[startYcol]))
                 if sourceCrs != epsg4326:
                     ptStart = sourceTo4326.transform(ptStart)
             if endUseGeom:
                 ptEnd = feature.geometry().asPoint()
                 if geomCrs != epsg4326:
                     ptEnd = geomTo4326.transform(ptEnd)
             else:
                 ptEnd = QgsPointXY(float(feature[endXcol]), float(feature[endYcol]))
                 if sourceCrs != epsg4326:
                     ptEnd = sourceTo4326.transform(ptEnd)
             pts = [ptStart]
             if ptStart == ptEnd: # We cannot have a line that begins and ends at the same point
                 numBad += 1
                 continue
                 
             if lineType == 0: # Geodesic
                 l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                 if l.s13 > maxseglen:
                     n = int(math.ceil(l.s13 / maxseglen))
                     if n > maxSegments:
                         n = maxSegments
                     seglen = l.s13 / n
                     for i in range(1,n+1):
                         s = seglen * i
                         g = l.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE)
                         pts.append( QgsPointXY(g['lon2'], g['lat2']) )
                 else: # The line segment is too short so it is from ptStart to ptEnd
                     pts.append(ptEnd)
             elif lineType == 1: # Great circle
                 pts = GCgetPointsOnLine(ptStart.y(), ptStart.x(),
                     ptEnd.y(), ptEnd.x(),
                     settings.maxSegLength*1000.0, # Put it in meters
                     settings.maxSegments+1)
             else: # Simple line
                 pts.append(ptEnd)
             f = QgsFeature()
             if isMultiPart:
                 outseg = checkIdlCrossings(pts)
                 if sinkCrs != epsg4326: # Convert each point to the output CRS
                     for y in range(len(outseg)):
                         for x, pt in enumerate(outseg[y]):
                             outseg[y][x] = toSinkCrs.transform(pt)
                 f.setGeometry(QgsGeometry.fromMultiPolylineXY(outseg))
             else:
                 if sinkCrs != epsg4326: # Convert each point to the output CRS
                     for x, pt in enumerate(pts):
                         pts[x] = toSinkCrs.transform(pt)
                 f.setGeometry(QgsGeometry.fromPolylineXY(pts))
             f.setAttributes(feature.attributes())
             lineSink.addFeature(f)
             
             if showStart:
                 f = QgsFeature()
                 if sinkCrs != epsg4326:
                     f.setGeometry(QgsGeometry.fromPointXY(toSinkCrs.transform(ptStart)))
                 else:
                     f.setGeometry(QgsGeometry.fromPointXY(ptStart))
                 f.setAttributes(feature.attributes())
                 ptSink.addFeature(f)
             if showEnd:
                 f = QgsFeature()
                 if sinkCrs != epsg4326:
                     f.setGeometry(QgsGeometry.fromPointXY(toSinkCrs.transform(ptEnd)))
                 else:
                     f.setGeometry(QgsGeometry.fromPointXY(ptEnd))
                 f.setAttributes(feature.attributes())
                 ptSink.addFeature(f)
         except:
             numBad += 1
             '''s = traceback.format_exc()
             feedback.pushInfo(s)'''
         
         if cnt % 100 == 0: # Set the progress after every 100 entries
             feedback.setProgress(int(cnt * total))
         
     if numBad > 0:
         feedback.pushInfo(tr("{} out of {} features from the input layer were invalid and were ignored.".format(numBad, featureCount)))
     
         
     return {self.PrmOutputLineLayer: lineDest_id, self.PrmOutputPointLayer: ptDest_id}
Ejemplo n.º 25
0
class QadVirtualGripCommandsClass(QadCommandClass):

    #============================================================================
    # instantiateNewCmd
    #============================================================================
    def instantiateNewCmd(self):
        """ istanzia un nuovo comando dello stesso tipo """
        return QadVirtualGripCommandsClass(self.plugIn)

    #============================================================================
    # getName
    #============================================================================
    def getName(self):
        return "QadVirtualGripCommandsClass"

    #============================================================================
    # __init__
    #============================================================================
    def __init__(self, plugIn):
        QadCommandClass.__init__(self, plugIn)
        self.commandNum = QadVirtualGripCommandsEnum.NONE
        self.currentCommand = None
        self.entitySetGripPoints = None
        self.basePt = QgsPointXY()

    #============================================================================
    # __del__
    #============================================================================
    def __del__(self):
        QadCommandClass.__del__(self)
        del self.currentCommand

    #============================================================================
    # getPointMapTool
    #============================================================================
    def getPointMapTool(self, drawMode=QadGetPointDrawModeEnum.NONE):
        if self.currentCommand is not None:
            return self.currentCommand.getPointMapTool(drawMode)
        else:
            return None

    #============================================================================
    # getCurrentContextualMenu
    #============================================================================
    def getCurrentContextualMenu(self):
        if self.currentCommand is not None:
            return self.currentCommand.getCurrentContextualMenu()
        else:
            return None

    #============================================================================
    # getCommand
    #============================================================================
    def getCommand(self):
        if self.commandNum == QadVirtualGripCommandsEnum.STRECTH:
            return QadGRIPSTRETCHCommandClass(self.plugIn)
        elif self.commandNum == QadVirtualGripCommandsEnum.MOVE:
            return QadGRIPMOVECommandClass(self.plugIn)
        elif self.commandNum == QadVirtualGripCommandsEnum.ROTATE:
            return QadGRIPROTATECommandClass(self.plugIn)
        elif self.commandNum == QadVirtualGripCommandsEnum.SCALE:
            return QadGRIPSCALECommandClass(self.plugIn)
        elif self.commandNum == QadVirtualGripCommandsEnum.MIRROR:
            return QadGRIPMIRRORCommandClass(self.plugIn)
        elif self.commandNum == QadVirtualGripCommandsEnum.CHANGE_RADIUS:
            return QadGRIPCHANGEARCRADIUSCommandClass(self.plugIn)
        elif self.commandNum == QadVirtualGripCommandsEnum.LENGTHEN:
            return QadGRIPLENGTHENCommandClass(self.plugIn)
        elif self.commandNum == QadVirtualGripCommandsEnum.ADD_VERTEX:
            cmd = QadGRIPINSERTREMOVEVERTEXCommandClass(self.plugIn)
            cmd.setInsertVertexAfter_Mode()
            return cmd
        elif self.commandNum == QadVirtualGripCommandsEnum.ADD_VERTEX_BEFORE:
            cmd = QadGRIPINSERTREMOVEVERTEXCommandClass(self.plugIn)
            cmd.setInsertVertexBefore_Mode()
            return cmd
        elif self.commandNum == QadVirtualGripCommandsEnum.REMOVE_VERTEX:
            cmd = QadGRIPINSERTREMOVEVERTEXCommandClass(self.plugIn)
            cmd.setRemoveVertex_mode()
            return cmd
        elif self.commandNum == QadVirtualGripCommandsEnum.LINE_TO_ARC:
            cmd = QadGRIPARCLINECONVERTCommandClass(self.plugIn)
            cmd.setLineToArcConvert_Mode()
            return cmd
        elif self.commandNum == QadVirtualGripCommandsEnum.ARC_TO_LINE:
            cmd = QadGRIPARCLINECONVERTCommandClass(self.plugIn)
            cmd.setArcToLineConvert_Mode()
            return cmd

        return None

    #============================================================================
    # initStartCommand
    #============================================================================
    def initStartCommand(self, commandNum):
        if self.currentCommand is not None:
            del self.currentCommand
            self.currentCommand = None

        self.commandNum = commandNum
        self.currentCommand = self.getCommand()

        if self.currentCommand is not None:
            self.currentCommand.basePt.set(self.basePt.x(), self.basePt.y())
            self.currentCommand.setSelectedEntityGripPoints(
                self.entitySetGripPoints)
            return True
        else:
            return False

    #============================================================================
    # initNextCommand
    #============================================================================
    def initNextCommand(self):
        if self.currentCommand is not None:
            del self.currentCommand
            self.currentCommand = None

        if self.commandNum == QadVirtualGripCommandsEnum.STRECTH or \
           self.commandNum == QadVirtualGripCommandsEnum.LENGTHEN or \
           self.commandNum == QadVirtualGripCommandsEnum.ADD_VERTEX or \
           self.commandNum == QadVirtualGripCommandsEnum.ADD_VERTEX_BEFORE or \
           self.commandNum == QadVirtualGripCommandsEnum.REMOVE_VERTEX or \
           self.commandNum == QadVirtualGripCommandsEnum.LINE_TO_ARC or \
           self.commandNum == QadVirtualGripCommandsEnum.ARC_TO_LINE or \
           self.commandNum == QadVirtualGripCommandsEnum.CHANGE_RADIUS:
            self.commandNum = QadVirtualGripCommandsEnum.MOVE
        elif self.commandNum == QadVirtualGripCommandsEnum.MOVE:
            self.commandNum = QadVirtualGripCommandsEnum.ROTATE
        elif self.commandNum == QadVirtualGripCommandsEnum.ROTATE:
            self.commandNum = QadVirtualGripCommandsEnum.SCALE
        elif self.commandNum == QadVirtualGripCommandsEnum.SCALE:
            self.commandNum = QadVirtualGripCommandsEnum.MIRROR
        elif self.commandNum == QadVirtualGripCommandsEnum.MIRROR:
            self.commandNum = QadVirtualGripCommandsEnum.MOVE

        self.currentCommand = self.getCommand()

        if self.currentCommand is not None:
            self.currentCommand.basePt.set(self.basePt.x(), self.basePt.y())
            self.currentCommand.setSelectedEntityGripPoints(
                self.entitySetGripPoints)
            return True
        else:
            return False

    #============================================================================
    # run
    #============================================================================
    def run(self, msgMapTool=False, msg=None):
        if self.currentCommand is None:
            return True
        res = self.currentCommand.run(msgMapTool, msg)
        if res == True:
            if self.currentCommand.skipToNextGripCommand == True:
                if self.initNextCommand():  # attivo comando successivo
                    return self.currentCommand.run(msgMapTool, msg)
            else:
                # ridisegno i grip point nelle nuove posizioni resettando quelli selezionati
                self.plugIn.tool.clearEntityGripPoints()
                self.plugIn.tool.refreshEntityGripPoints()

        return res
Ejemplo n.º 26
0
class PointFeatureDlg(QDialog, Ui_PointFeature):
    map_tool_change_signal = pyqtSignal()
    landmark_added = pyqtSignal(QgsMapLayer)
    landmark_removed = pyqtSignal(QgsMapLayer)
    finish_add_landmark_signal = pyqtSignal()

    def __init__(self, canvas, proj, parent=None):
        super(PointFeatureDlg, self).__init__(parent)
        self.setupUi(self)

        self.point = None
        self.point_layer = None
        self.feat = None
        self.num = 1
        self.canvas = canvas
        self.proj = proj

        self.double_validator = get_custom_double_validator()
        self.int_validator = get_custom_int_validator()

        self.getCoordinatesButton.setIcon(QIcon(":/resources/pickPointInMap.svg"))
        self.getCoordinatesButton.clicked.connect(self.set_point_tool)
        self.getCoordinatesButton.setToolTip("Pick point in map")

        self.copy_to_clipboardButton.setIcon(QIcon(":/resources/mActionCopyClipboard.svg"))
        self.copy_to_clipboardButton.setToolTip("Copy to clipboard")
        self.copy_to_clipboardButton.clicked.connect(self.copy_to_clipboard)

        self.tool_add_landmark = addlandmarktool.AddLandmarkTool(canvas)
        self.tool_add_landmark.point_signal.connect(self.add_new_landmark)
        self.previous_coordinates_format = self.comboBox.currentText()
        self.comboBox.currentIndexChanged.connect(self.set_format)

    def get_coordinates(self):
        """ Return the coordinates (latitude, longitude)
        :return : latitude, longitude
        """
        return self.point.y(), self.point.x()

    def set_point_tool(self):
        """Set the maptool to pointTool"""
        self.map_tool_change_signal.emit() #Emit signal to warn mainwindow that we are changing a maptool
        self.canvas.setMapTool(self.tool_add_landmark)

    def add_new_landmark(self, point):
        """
        create a landmark point
        :param point: Point to add on the canvas
        """
        if self.feat is not None:
            self.point_layer.dataProvider().deleteFeatures([self.feat.id()])
            self.landmark_removed.emit(self.point_layer)

        self.point = point
        name = "LandmarkPoint_" + str(self.num)
        restart_search = True
        # This loops tries to find a free name for the LandmarkPoint_
        while restart_search:
            restart_search = False
            # for every layer
            for layer in self.proj.mapLayers().values():
                # check name
                if layer.name() == name:
                    self.num += 1
                    name = "LandmarkPoint_" + str(self.num)
                    # Set to True to find the next LandmarkPoint
                    restart_search = True

        self.point_layer = QgsVectorLayer(
            "Point?crs=epsg:4326",
            name,
            "memory")

        self.feat = QgsFeature()
        self.feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(self.point.x(), self.point.y())))
        self.point_layer.dataProvider().addFeatures([self.feat])
        self.landmark_added.emit(self.point_layer)

        if self.comboBox.currentText() == "Decimal degrees":
            self.set_degrees_from_point()

        if self.comboBox.currentText() == "Degrees, minutes":
            # convert degree to degreeminute
            self.set_degrees_minutes_from_point()

        if self.comboBox.currentText() == "Degrees, minutes, seconds":
            # convert degree to degminsec
            self.set_degrees_minutes_seconds_from_point()

    def set_format(self):
        """ Set widgets on dialog according with combobox coordinates format"""

        if self.previous_coordinates_format == "Decimal degrees":

            if validate_custom_double(self.lon_degrees_lineedit.text()) == QValidator.Acceptable and \
                    validate_custom_double(self.lat_degrees_lineedit.text()) == QValidator.Acceptable:

                self.point = QgsPointXY(float(self.lon_degrees_lineedit.text()),
                                        float(self.lat_degrees_lineedit.text()))
            else:
                self.point = None

        elif self.previous_coordinates_format == "Degrees, minutes":
            lat_d = self.lat_degrees_lineedit.text()
            lat_m = self.lat_minutes_lineedit.text()
            lon_d = self.lon_degrees_lineedit.text()
            lon_m = self.lon_minutes_lineedit.text()

            if validate_custom_int(lat_d) == QValidator.Acceptable \
                and validate_custom_double(lat_m) == QValidator.Acceptable \
                and validate_custom_int(lon_d) == QValidator.Acceptable \
                and validate_custom_double(lon_m) == QValidator.Acceptable:

                lat_dm = [lat_d, lat_m]
                lon_dm = [lon_d, lon_m]

                # convert degree_minute to degree
                lat_degree, lon_degree = degree_minute_to_degree(lat_dm, lon_dm)
                self.point = QgsPointXY(float(lon_degree),
                                        float(lat_degree))
            else:

                self.point = None

        elif self.previous_coordinates_format == "Degrees, minutes, seconds":
            if validate_custom_int(self.lat_degrees_lineedit.text()) == QValidator.Acceptable \
                    and validate_custom_int(self.lat_minutes_lineedit.text()) == QValidator.Acceptable \
                    and validate_custom_double(self.lat_seconds_lineedit.text()) == QValidator.Acceptable \
                    and validate_custom_int(self.lon_degrees_lineedit.text()) == QValidator.Acceptable \
                    and validate_custom_int(self.lon_minutes_lineedit.text()) == QValidator.Acceptable \
                    and validate_custom_double(self.lon_seconds_lineedit.text()) == QValidator.Acceptable:

                lat = self.lat_degrees_lineedit.text() + 'º' + self.lat_minutes_lineedit.text() + '\'' + self.lat_seconds_lineedit.text() + '\'\''
                lon = self.lon_degrees_lineedit.text() + 'º' + self.lon_minutes_lineedit.text() + '\'' + self.lon_seconds_lineedit.text() + '\'\''

                lat_degree, lon_degree = degree_minute_second_to_degree(lat, lon)
                self.point = QgsPointXY(float(lon_degree),
                                        float(lat_degree))

            else:
                self.point = None

        # delete previous widgets
        self.delete_all(self.latitude_layout)
        self.delete_all(self.longitude_layout)

        lat_label = QLabel(self)
        lat_label.setText("Latitude: ")
        lat_label.setFixedWidth(75)
        lon_label = QLabel(self)
        lon_label.setText("Longitude: ")
        lon_label.setFixedWidth(75)

        self.latitude_layout.addWidget(lat_label)
        self.longitude_layout.addWidget(lon_label)

        if self.comboBox.currentText() == "Decimal degrees" or self.previous_coordinates_format is None:
            self.lat_degrees_lineedit = QLineEdit(self)
            self.lon_degrees_lineedit = QLineEdit(self)

            self.lat_degrees_lineedit.textChanged.connect(self.validate_coordinates_double)
            self.lon_degrees_lineedit.textChanged.connect(self.validate_coordinates_double)

            self.latitude_layout.addWidget(self.lat_degrees_lineedit)
            self.longitude_layout.addWidget(self.lon_degrees_lineedit)

            self.set_degrees_from_point()


        if self.comboBox.currentText() == "Degrees, minutes":

            lat_deg_label = QLabel(self)
            lat_deg_label.setText("º")
            lon_deg_label = QLabel(self)
            lon_deg_label.setText("º")
            lat_min_label = QLabel(self)
            lat_min_label.setText("\'")
            lon_min_label = QLabel(self)
            lon_min_label.setText("\'")
            self.lat_degrees_lineedit = QLineEdit(self)
            self.lon_degrees_lineedit = QLineEdit(self)
            self.lat_minutes_lineedit = QLineEdit(self)
            self.lon_minutes_lineedit = QLineEdit(self)

            self.lat_degrees_lineedit.textChanged.connect(self.validate_coordinates_int)
            self.lon_degrees_lineedit.textChanged.connect(self.validate_coordinates_int)
            self.lat_minutes_lineedit.textChanged.connect(self.validate_coordinates_double)
            self.lon_minutes_lineedit.textChanged.connect(self.validate_coordinates_double)

            self.latitude_layout.addWidget(self.lat_degrees_lineedit)
            self.latitude_layout.addWidget(lat_deg_label)
            self.latitude_layout.addWidget(self.lat_minutes_lineedit)
            self.latitude_layout.addWidget(lat_min_label)

            self.longitude_layout.addWidget(self.lon_degrees_lineedit)
            self.longitude_layout.addWidget(lon_deg_label)
            self.longitude_layout.addWidget(self.lon_minutes_lineedit)
            self.longitude_layout.addWidget(lon_min_label)

            self.set_degrees_minutes_from_point()


        if self.comboBox.currentText() == "Degrees, minutes, seconds":

            lat_deg_label = QLabel(self)
            lat_deg_label.setText("º")
            lon_deg_label = QLabel(self)
            lon_deg_label.setText("º")
            lat_min_label = QLabel(self)
            lat_min_label.setText("\'")
            lon_min_label = QLabel(self)
            lon_min_label.setText("\'")
            lat_sec_label = QLabel(self)
            lat_sec_label.setText("\'\'")
            lon_sec_label = QLabel(self)
            lon_sec_label.setText("\'\'")
            self.lat_degrees_lineedit = QLineEdit(self)
            self.lon_degrees_lineedit = QLineEdit(self)
            self.lat_minutes_lineedit = QLineEdit(self)
            self.lon_minutes_lineedit = QLineEdit(self)
            self.lat_seconds_lineedit = QLineEdit(self)
            self.lon_seconds_lineedit = QLineEdit(self)

            self.lat_degrees_lineedit.textChanged.connect(self.validate_coordinates_int)
            self.lon_degrees_lineedit.textChanged.connect(self.validate_coordinates_int)
            self.lat_minutes_lineedit.textChanged.connect(self.validate_coordinates_int)
            self.lon_minutes_lineedit.textChanged.connect(self.validate_coordinates_int)
            self.lat_seconds_lineedit.textChanged.connect(self.validate_coordinates_double)
            self.lon_seconds_lineedit.textChanged.connect(self.validate_coordinates_double)

            self.latitude_layout.addWidget(self.lat_degrees_lineedit)
            self.latitude_layout.addWidget(lat_deg_label)
            self.latitude_layout.addWidget(self.lat_minutes_lineedit)
            self.latitude_layout.addWidget(lat_min_label)
            self.latitude_layout.addWidget(self.lat_seconds_lineedit)
            self.latitude_layout.addWidget(lat_sec_label)

            self.longitude_layout.addWidget(self.lon_degrees_lineedit)
            self.longitude_layout.addWidget(lon_deg_label)
            self.longitude_layout.addWidget(self.lon_minutes_lineedit)
            self.longitude_layout.addWidget(lon_min_label)
            self.longitude_layout.addWidget(self.lon_seconds_lineedit)
            self.longitude_layout.addWidget(lon_sec_label)

            self.set_degrees_minutes_seconds_from_point()


        self.previous_coordinates_format = self.comboBox.currentText()

    def set_degrees_from_point(self):
        """ From point in degrees, fill degrees QLineEdits"""
        if self.point is not None:

            lat_degrees_text = str(self.point.y())
            lon_degrees_text = str(self.point.x())

            if (validate_custom_double(lat_degrees_text) == QValidator.Acceptable and
                    validate_custom_double(lon_degrees_text) == QValidator.Acceptable):

                self.lat_degrees_lineedit.setText("{:.8F}".format(float(lat_degrees_text)))
                self.lon_degrees_lineedit.setText("{:.8F}".format(float(lon_degrees_text)))

    def set_degrees_minutes_from_point(self):
        """ From point in degrees, fill degrees and minutesQLineEdits"""

        if self.point is not None:

            lat_degrees_text = str(self.point.y())
            lon_degrees_text = str(self.point.x())

            if (validate_custom_double(lat_degrees_text) == QValidator.Acceptable and
                    validate_custom_double(lon_degrees_text) == QValidator.Acceptable):

                lat_dm, lon_dm = degree_to_degree_minute(float(lat_degrees_text), float(lon_degrees_text))

                lat_d, lat_m = str(lat_dm).split('º')
                lat_m, unused = lat_m.split('\'')

                lon_d, lon_m = str(lon_dm).split('º')
                lon_m, unused = lon_m.split('\'')

                self.lat_degrees_lineedit.setText("{:d}".format(int(lat_d)))
                self.lat_minutes_lineedit.setText("{:.8F}".format(float(lat_m)))
                self.lon_degrees_lineedit.setText("{:d}".format(int(lon_d)))
                self.lon_minutes_lineedit.setText("{:.8F}".format(float(lon_m)))

    def set_degrees_minutes_seconds_from_point(self):
        """ From point in degrees, fill degrees, minutes and seconds QLineEdits"""

        if self.point is not None:

            lat_degrees_text = str(self.point.y())
            lon_degrees_text = str(self.point.x())

            if (validate_custom_double(lat_degrees_text) == QValidator.Acceptable and
                    validate_custom_double(lon_degrees_text) == QValidator.Acceptable):

                lat_dms, lon_dms = degree_to_degree_minute_second(float(lat_degrees_text), float(lon_degrees_text))

                lat_deg, lat_ms = str(lat_dms).split('º')
                lat_ms, unused = lat_ms.split('\'\'')
                lat_min, lat_sec = lat_ms.split('\'')

                lon_deg, lon_ms = str(lon_dms).split('º')
                lon_ms, unused = lon_ms.split('\'\'')
                lon_min, lon_sec = lon_ms.split('\'')

                self.lat_degrees_lineedit.setText("{:d}".format(int(lat_deg)))
                self.lat_minutes_lineedit.setText("{:d}".format(int(lat_min)))
                self.lat_seconds_lineedit.setText("{:.8F}".format(float(lat_sec)))
                self.lon_degrees_lineedit.setText("{:d}".format(int(lon_deg)))
                self.lon_minutes_lineedit.setText("{:d}".format(int(lon_min)))
                self.lon_seconds_lineedit.setText("{:.8F}".format(float(lon_sec)))

    def validate_coordinates_double(self):
        """validate line edit coordinates with double"""
        sender = self.sender()
        state = validate_custom_double(sender.text())
        color = get_color(state)
        sender.setStyleSheet('QLineEdit { background-color: %s }' % color)

    def validate_coordinates_int(self):
        """validate line edit coordinates with int"""
        sender = self.sender()
        state = validate_custom_int(sender.text())
        color = get_color(state)
        sender.setStyleSheet('QLineEdit { background-color: %s }' % color)

    def reset(self):
        """ Reset params"""
        self.comboBox.setCurrentIndex(0)
        self.point = None
        self.point_layer = None
        self.feat = None
        self.previous_coordinates_format = None

        self.set_format()

    def copy_to_clipboard(self):
        """Copy coordinates to clipboard"""
        cb = QApplication.clipboard()
        cb.clear()
        if self.comboBox.currentText() == "Decimal degrees" :
            cb.setText("{}, {}".format(self.lat_degrees_lineedit.text(),self.lon_degrees_lineedit.text()))
        if self.comboBox.currentText() == "Degrees, minutes":
            cb.setText("{}º {}', {}º {}\'".format(self.lat_degrees_lineedit.text(), self.lat_minutes_lineedit.text(),
                                            self.lon_degrees_lineedit.text(), self.lon_minutes_lineedit.text()))
        if self.comboBox.currentText() == "Degrees, minutes, seconds":
            cb.setText("{}º {}' {}\'\', {}º {}\' {}\'\'".format(self.lat_degrees_lineedit.text(),
                                                  self.lat_minutes_lineedit.text(),
                                                  self.lat_seconds_lineedit.text(),
                                                  self.lon_degrees_lineedit.text(),
                                                  self.lon_minutes_lineedit.text(),
                                                  self.lon_seconds_lineedit.text()))

    def accept(self):
        """ Insert self.pont in the canvas"""
        self.set_format()
        if self.point is not None:
            new_point = QgsPointXY(float(self.get_coordinates()[1]),
                                   float(self.get_coordinates()[0]))
            if self.point_layer is None:
                self.add_new_landmark(self.point)

            self.point_layer.startEditing()
            self.point_layer.beginEditCommand("Move Point")  # for undo
            self.point_layer.moveVertex(new_point.x(), new_point.y(), self.feat.id() + 1, 0)
            self.point_layer.endEditCommand()
            self.point_layer.commitChanges()

            self.finish_add_landmark_signal.emit()
            super(PointFeatureDlg, self).accept()
        else:
            QMessageBox.warning(self,
                                "Invalid Point",
                                "Invalid point, make sure the coordinates are correct.",
                                QMessageBox.Close)

    def reject(self):
        """ reject point insertion"""
        if self.feat is not None:
            # if we are aborting, point is deleted
            self.point_layer.dataProvider().deleteFeatures([self.feat.id()])
            self.landmark_removed.emit(self.point_layer)

        self.finish_add_landmark_signal.emit()
        super(PointFeatureDlg, self).reject()

    def delete_all(self, layout):
        """
        delete all widget from layout
        :param layout: layout is a qt layout
        """
        if layout is not None:
            for i in reversed(range(layout.count())):
                item = layout.takeAt(i)
                widget = item.widget()
                if widget is not None:
                    widget.deleteLater()
                else:
                    self.delete_all(item.layout())
Ejemplo n.º 27
0
class QadSTRETCHCommandClass(QadCommandClass):
    def instantiateNewCmd(self):
        """ istanzia un nuovo comando dello stesso tipo """
        return QadSTRETCHCommandClass(self.plugIn)

    def getName(self):
        return QadMsg.translate("Command_list", "STRETCH")

    def getEnglishName(self):
        return "STRETCH"

    def connectQAction(self, action):
        action.triggered.connect(self.plugIn.runSTRETCHCommand)

    def getIcon(self):
        return QIcon(":/plugins/qad/icons/stretch.png")

    def getNote(self):
        # impostare le note esplicative del comando
        return QadMsg.translate("Command_STRETCH", "Stretches objects.")

    def __init__(self, plugIn):
        QadCommandClass.__init__(self, plugIn)
        self.AddOnSelection = True  # se = False significa remove
        self.points = []
        self.MPOLYGONCommand = None
        self.SSGeomList = [
        ]  # lista di entità da stirare con geom di selezione
        self.basePt = QgsPointXY()

    def __del__(self):
        QadCommandClass.__del__(self)
        if self.MPOLYGONCommand is not None:
            del self.MPOLYGONCommand
        for SSGeom in self.SSGeomList:
            SSGeom[0].deselectOnLayer()

    def getPointMapTool(self, drawMode=QadGetPointDrawModeEnum.NONE):
        if self.step == 2:  # quando si é in fase di disegno linea
            return self.MPOLYGONCommand.getPointMapTool(drawMode)
        else:
            if (self.plugIn is not None):
                if self.PointMapTool is None:
                    self.PointMapTool = Qad_stretch_maptool(self.plugIn)
                return self.PointMapTool
            else:
                return None

    def getCurrentContextualMenu(self):
        if self.step == 2:  # quando si é in fase di disegno linea
            return self.MPOLYGONCommand.getCurrentContextualMenu()
        else:
            return self.contextualMenu

    def stretch(self, entity, containerGeom, offsetX, offsetY,
                tolerance2ApproxCurve):
        # entity = entità da stirare
        # ptList = lista dei punti da stirare
        # offsetX, offsetY = spostamento da applicare
        # tolerance2ApproxCurve = tolleranza per ricreare le curve

        if entity.whatIs() == "DIMENTITY":
            dimEntity = entity
        else:
            # verifico se l'entità appartiene ad uno stile di quotatura
            dimEntity = QadDimStyles.getDimEntity(entity)

        if dimEntity is None:
            stretchedGeom = entity.getQadGeom()
            # controllo inserito perchè con le quote, questa viene cancellata e ricreata quindi alcuni oggetti potrebbero non esistere più
            if stretchedGeom is None:  # se non c'è lo salto senza errore
                return True
            # stiro la feature
            stretchedGeom = qad_stretch_fun.stretchQadGeometry(stretchedGeom, containerGeom, \
                                                               offsetX, offsetY)

            if stretchedGeom is not None:
                # trasformo la geometria nel crs del layer
                f = entity.getFeature()
                f.setGeometry(fromQadGeomToQgsGeom(stretchedGeom,
                                                   entity.crs()))
                # plugIn, layer, feature, refresh, check_validity
                if qad_layer.updateFeatureToLayer(self.plugIn, entity.layer, f,
                                                  False, False) == False:
                    return False

        else:
            # stiro la quota
            if dimEntity.deleteToLayers(self.plugIn) == False:
                return False
            newDimEntity = QadDimEntity(dimEntity)  # la copio
            newDimEntity.stretch(containerGeom, offsetX, offsetY)
            if newDimEntity.addToLayers(self.plugIn) == False:
                return False

        return True

    #============================================================================
    # stretchFeatures
    #============================================================================
    def stretchFeatures(self, newPt):
        # mi ricavo un unico QadEntitySet con le entità selezionate
        entitySet = QadEntitySet()
        for SSGeom in self.SSGeomList:
            entitySet.unite(SSGeom[0])
        self.plugIn.beginEditCommand("Feature stretched",
                                     entitySet.getLayerList())

        dimElaboratedList = []  # lista delle quotature già elaborate

        tolerance2ApproxCurve = QadVariables.get(
            QadMsg.translate("Environment variables", "TOLERANCE2APPROXCURVE"))
        offsetX = newPt.x() - self.basePt.x()
        offsetY = newPt.y() - self.basePt.y()

        entity = QadEntity()
        for SSGeom in self.SSGeomList:
            # copio entitySet
            entitySet = QadEntitySet(SSGeom[0])
            geomSel = SSGeom[1]

            for layerEntitySet in entitySet.layerEntitySetList:
                layer = layerEntitySet.layer

                for featureId in layerEntitySet.featureIds:
                    entity.set(layer, featureId)

                    # verifico se l'entità appartiene ad uno stile di quotatura
                    dimEntity = QadDimStyles.getDimEntity(entity)
                    if dimEntity is None:
                        if self.stretch(entity, geomSel, offsetX, offsetY,
                                        tolerance2ApproxCurve) == False:
                            self.plugIn.destroyEditCommand()
                            return
                    else:
                        found = False
                        for dimElaborated in dimElaboratedList:
                            if dimElaborated == dimEntity:
                                found = True

                        if found == False:  # quota non ancora elaborata
                            # aggiungo i layer dei componenti della quota
                            self.plugIn.addLayerListToLastEditCommand(
                                "Feature stretched", [
                                    dimEntity.getSymbolLayer(),
                                    dimEntity.getLinearLayer(),
                                    dimEntity.getTextualLayer()
                                ])

                            dimElaboratedList.append(dimEntity)
                            if self.stretch(dimEntity, geomSel, offsetX,
                                            offsetY,
                                            tolerance2ApproxCurve) == False:
                                self.plugIn.destroyEditCommand()
                                return

        self.plugIn.endEditCommand()

    #============================================================================
    # setEntitySetGeom
    #============================================================================
    def setEntitySetGeom(self, entitySet, selGeom):
        for SSGeom in self.SSGeomList:
            SSGeom[0].deselectOnLayer()
        del self.SSGeomList[:]  # svuoto la lista
        # aggiuge il gruppo di selezione con la geometria usata per la selezione
        self.SSGeomList.append([entitySet, selGeom])
        entitySet.selectOnLayer(False)  # incremental = False

    #============================================================================
    # addEntitySetGeom
    #============================================================================
    def addEntitySetGeom(self, entitySet, selGeom):
        # elimino dai gruppi precedenti gli oggetti presenti in entitySet
        self.removeEntitySet(entitySet)
        # aggiuge il gruppo di selezione con la geometria usata per la selezione
        self.SSGeomList.append([entitySet, selGeom])
        entitySet.selectOnLayer(True)  # incremental = True

    #============================================================================
    # removeEntitySet
    #============================================================================
    def removeEntitySet(self, entitySet):
        # elimino dai gruppi precedenti gli oggetti presenti in entitySet
        for SSGeom in self.SSGeomList:
            SSGeom[0].subtract(entitySet)
        for SSGeom in self.SSGeomList:
            SSGeom[0].selectOnLayer(False)  # incremental = False

    #============================================================================
    # SSGeomListIsEmpty
    #============================================================================
    def SSGeomListIsEmpty(self):
        if len(self.SSGeomList) == 0:
            return True
        for SSGeom in self.SSGeomList:
            if SSGeom[0].isEmpty() == False:
                return False
        return True

    #============================================================================
    # waitForObjectSel
    #============================================================================
    def waitForObjectSel(self):
        self.step = 1
        # imposto il map tool
        self.getPointMapTool().setMode(
            Qad_stretch_maptool_ModeEnum.ASK_FOR_FIRST_PT_RECTANGLE)

        keyWords = QadMsg.translate("Command_STRETCH", "Polygon") + "/" + \
                   QadMsg.translate("Command_STRETCH", "Add") + "/" + \
                   QadMsg.translate("Command_STRETCH", "Remove")

        if self.AddOnSelection == True:
            prompt = QadMsg.translate("Command_STRETCH", "Select vertices")
        else:
            prompt = QadMsg.translate("Command_STRETCH", "Remove vertices")
        prompt = prompt + QadMsg.translate(
            "Command_STRETCH",
            " to stretch crossed by a selection window or [{0}]: ").format(
                keyWords)

        englishKeyWords = "Polygon" + "/" + "Add" + "/" + "Remove"
        keyWords += "_" + englishKeyWords
        # si appresta ad attendere un punto o enter o una parola chiave
        # msg, inputType, default, keyWords, nessun controllo
        self.waitFor(prompt, QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                     None, \
                     keyWords, QadInputModeEnum.NONE)

    #============================================================================
    # waitForBasePt
    #============================================================================
    def waitForBasePt(self):
        self.step = 4
        # imposto il map tool
        self.getPointMapTool().setMode(
            Qad_stretch_maptool_ModeEnum.NONE_KNOWN_ASK_FOR_BASE_PT)

        keyWords = QadMsg.translate("Command_STRETCH", "Displacement")
        prompt = QadMsg.translate(
            "Command_STRETCH",
            "Specify base point or [{0}] <{0}>: ").format(keyWords)

        englishKeyWords = "Displacement"
        keyWords += "_" + englishKeyWords
        # si appresta ad attendere un punto o enter o una parola chiave
        # msg, inputType, default, keyWords, nessun controllo
        self.waitFor(prompt, \
                     QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                     None, \
                     keyWords, QadInputModeEnum.NONE)

    #============================================================================
    # run
    #============================================================================
    def run(self, msgMapTool=False, msg=None):
        if self.plugIn.canvas.mapSettings().destinationCrs().isGeographic():
            self.showMsg(
                QadMsg.translate(
                    "QAD",
                    "\nThe coordinate reference system of the project must be a projected coordinate system.\n"
                ))
            return True  # fine comando

        #=========================================================================
        # RICHIESTA SELEZIONE OGGETTI
        if self.step == 0:  # inizio del comando
            # si appresta ad attendere la selezione degli oggetti da stirare
            self.waitForObjectSel()
            return False

        #=========================================================================
        # RISPOSTA ALLA SELEZIONE OGGETTI DA STIRARE
        elif self.step == 1:
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        value = None
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False
                else:
                    value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            if type(value) == unicode:
                if value == QadMsg.translate("Command_STRETCH",
                                             "Polygon") or value == "Polygon":
                    # Seleziona tutti gli oggetti che sono interni al poligono
                    self.MPOLYGONCommand = QadMPOLYGONCommandClass(self.plugIn)
                    # se questo flag = True il comando serve all'interno di un altro comando per disegnare una linea
                    # che non verrà salvata su un layer
                    self.MPOLYGONCommand.virtualCmd = True
                    self.MPOLYGONCommand.run(msgMapTool, msg)
                    self.step = 2
                    return False
                elif value == QadMsg.translate("Command_SSGET",
                                               "Add") or value == "Add":
                    # Passa al metodo Aggiungi: gli oggetti selezionati possono essere aggiunti al gruppo di selezione
                    self.AddOnSelection = True
                elif value == QadMsg.translate("Command_SSGET",
                                               "Remove") or value == "Remove":
                    # Passa al metodo Rimuovi: gli oggetti possono essere rimossi dal gruppo di selezione
                    self.AddOnSelection = False
            elif type(value) == QgsPointXY:  # se é stato selezionato un punto
                del self.points[:]  # svuoto la lista
                self.points.append(value)
                # imposto il map tool
                self.getPointMapTool().setMode(
                    Qad_stretch_maptool_ModeEnum.
                    FIRST_PT_KNOWN_ASK_FOR_SECOND_PT_RECTANGLE)
                self.getPointMapTool().setStartPoint(value)

                # si appresta ad attendere un punto
                self.waitForPoint(
                    QadMsg.translate("Command_STRETCH",
                                     "Specify opposite corner: "))
                self.step = 3
                return False
            else:
                if self.SSGeomListIsEmpty():
                    return True
                # si appresta ad attendere il punto base o lo spostamento
                self.waitForBasePt()
                return False

            # si appresta ad attendere la selezione degli oggetti da stirare
            self.waitForObjectSel()

            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA PUNTO PER MODALITA' POLIGONO (da step = 1)
        elif self.step == 2:  # dopo aver atteso un punto si riavvia il comando
            if self.MPOLYGONCommand.run(msgMapTool, msg) == True:
                if self.MPOLYGONCommand.polyline.qty() > 0:
                    # cerco tutte le geometrie intersecanti il poligono
                    # e considerando solo layer editabili
                    selSet = getSelSet("CP", self.getPointMapTool(), self.MPOLYGONCommand.PLINECommand.polyline.asPolyline(), \
                                       None, True, True, True, \
                                       True)
                    # se la selezione é avvenuta con shift premuto o se si deve rimuovere il gruppo selSet dal gruppo
                    if self.AddOnSelection == False:
                        self.removeEntitySet(selSet)
                    else:
                        self.setEntitySetGeom(
                            selSet,
                            QgsGeometry.fromPolygonXY([
                                self.MPOLYGONCommand.PLINECommand.polyline.
                                asPolyline()
                            ]))

                del self.MPOLYGONCommand
                self.MPOLYGONCommand = None

                # si appresta ad attendere la selezione degli oggetti da stirare
                self.waitForObjectSel()
                self.getPointMapTool().refreshSnapType(
                )  # aggiorno lo snapType che può essere variato dal maptool di mpolygon
            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA PUNTO PER MODALITA' FINESTRA (da step = 1)
        elif self.step == 3:  # dopo aver atteso un punto si riavvia il comando
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        self.showMsg(
                            QadMsg.translate("Command_STRETCH",
                                             "Window not correct."))
                        # si appresta ad attendere un punto
                        self.waitForPoint(
                            QadMsg.translate("Command_STRETCH",
                                             "Specify opposite corner: "))
                        return False
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                shiftKey = self.getPointMapTool().shiftKey
                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                shiftKey = False
                value = msg

            if type(value) == QgsPointXY:
                self.points.append(value)
                # cerco tutte le geometrie intersecanti il rettangolo
                # e considerando solo layer editabili
                selSet = getSelSet("C", self.getPointMapTool(), self.points, \
                                   None, True, True, True, \
                                   True)
                # se si deve rimuovere il gruppo entitySet dal gruppo
                if self.AddOnSelection == False:
                    self.removeEntitySet(selSet)
                else:
                    if shiftKey:  # se la selezione é avvenuta con shift premuto
                        self.addEntitySetGeom(
                            selSet,
                            QgsGeometry.fromRect(
                                QgsRectangle(self.points[0], self.points[1])))
                    else:
                        self.setEntitySetGeom(
                            selSet,
                            QgsGeometry.fromRect(
                                QgsRectangle(self.points[0], self.points[1])))
                # si appresta ad attendere la selezione degli oggetti da stirare
                self.waitForObjectSel()
            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA PUNTO BASE (da step = 1)
        elif self.step == 4:  # dopo aver atteso un punto o un numero reale si riavvia il comando
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        pass  # opzione di default "spostamento"
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            # imposto il map tool
            self.getPointMapTool().SSGeomList = self.SSGeomList

            if value is None or type(value) == unicode:
                self.basePt.set(0, 0)
                self.getPointMapTool().basePt = self.basePt
                self.getPointMapTool().setMode(
                    Qad_stretch_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_MOVE_PT)
                # si appresta ad attendere un punto
                msg = QadMsg.translate(
                    "Command_STRETCH",
                    "Specify the displacement from the origin point 0,0 <{0}, {1}>: "
                )
                # msg, inputType, default, keyWords, nessun controllo
                self.waitFor(msg.format(str(self.plugIn.lastOffsetPt.x()), str(self.plugIn.lastOffsetPt.y())), \
                             QadInputTypeEnum.POINT2D, \
                             self.plugIn.lastOffsetPt, \
                             "", QadInputModeEnum.NONE)
                self.step = 5
            elif type(
                    value) == QgsPointXY:  # se é stato inserito il punto base
                self.basePt.set(value.x(), value.y())

                # imposto il map tool
                self.getPointMapTool().basePt = self.basePt
                self.getPointMapTool().setMode(
                    Qad_stretch_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_MOVE_PT)

                # si appresta ad attendere un punto o enter o una parola chiave
                # msg, inputType, default, keyWords, nessun controllo
                self.waitFor(QadMsg.translate("Command_STRETCH", "Specify second point or <use first point as displacement from origin point 0,0>: "), \
                             QadInputTypeEnum.POINT2D, \
                             None, \
                             "", QadInputModeEnum.NONE)
                self.step = 6

            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA DEL PUNTO DI SPOSTAMENTO (da step = 2)
        elif self.step == 5:  # dopo aver atteso un punto o un numero reale si riavvia il comando
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        return True  # fine comando
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            self.plugIn.setLastOffsetPt(value)
            self.stretchFeatures(value)
            return True  # fine comando

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA SECONDO PUNTO PER SPOSTAMENTO (da step = 2)
        elif self.step == 6:  # dopo aver atteso un punto o un numero reale si riavvia il comando
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        return True  # fine comando
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            if value is None:
                newPt = QgsPointXY(self.basePt.x() * 2, self.basePt.y() * 2)
                self.stretchFeatures(newPt)
            elif type(
                    value
            ) == QgsPointXY:  # se é stato inserito lo spostamento con un punto
                self.stretchFeatures(value)

            return True  # fine comando
Ejemplo n.º 28
0
    def processAlgorithm(self, parameters, context, feedback):
        # gather parameters
        extent = self.parameterAsExtent(parameters, self.PrmExtent, context,
                                        epsg4326)
        lineDistance = self.parameterAsDouble(parameters, self.PrmLineDistance,
                                              context)
        traceInterval = self.parameterAsDouble(parameters,
                                               self.PrmTraceInterval, context)
        distanceTolerance = self.parameterAsDouble(parameters,
                                                   self.PrmDistanceTolerance,
                                                   context)
        variationTolerance = self.parameterAsDouble(parameters,
                                                    self.PrmVariationTolerance,
                                                    context)
        units = self.parameterAsInt(parameters, self.PrmUnitsOfMeasure,
                                    context)
        measureFactor = conversionToMeters(units)

        # adjust linear units
        lineDistance *= measureFactor
        traceInterval *= measureFactor
        distanceTolerance *= measureFactor

        f = QgsFields()
        f.append(QgsField("trace", QVariant.Int))
        f.append(QgsField("variation", QVariant.Double))

        # obtain our output sink
        (sink, dest_id) = self.parameterAsSink(parameters, self.PrmOutputLayer,
                                               context, f,
                                               QgsWkbTypes.LineString,
                                               epsg4326)

        # Determine the longitude step direction based on sign of the variation in the extent's center,
        # so that we start on the side of the extent such that we will fill in partial lines at the corner.
        # TODO: this is not ideal when the extent intersects an agonic line.
        centerVar = geomag.declination(extent.center().y(),
                                       extent.center().x(), 0, date.today())
        start = QgsPointXY(extent.xMinimum(), extent.yMinimum())
        if centerVar > 0:
            lineDistance = -lineDistance
            start.setX(extent.xMaximum())

        # We trace between points at a fixed longitude interval, so that the distance-preservation logic below
        # is straightforward and we can compare points in successive traces easily.
        traceIntervalDeg = traceInterval * metersToDeg
        numTraces = math.ceil(extent.height() / traceIntervalDeg)
        traceY = extent.height() / numTraces

        lineCount = 0
        lastStartPoints = []

        # Our major (longitude) loop starts here
        while not feedback.isCanceled():
            # Initialize our line at the start
            empty = True
            line = []
            startPoints = []

            # Add the first point
            p1 = start
            if p1.x() >= extent.xMinimum() and p1.x() <= extent.xMaximum():
                line.append(p1)
                empty = False

            # Now we will trace the line in the field direction until we are out of the rectangle's Y range,
            # restarting a new line whenever the horizontal step gets out of whack due to latitude change.

            for t in range(0, numTraces + 1):
                if feedback.isCanceled():
                    break

                y = extent.yMinimum() + (t * traceY)

                # get the variation at this point
                variation = geomag.declination(p1.y(), p1.x(), 0, date.today())
                if t == 0:
                    lastVariation = variation  # record last used variation in this polyline for error computation

                # determine a 1-meter vector in the direction of magnetic north
                # and scale this to find a vector taking us from p1 to the next latitude step
                magN = projectBearing(p1, 1, variation)
                magN.multiply(
                    (y - p1.y()) / magN.y()
                )  # note that Y magnitude will be nonzero for reasonable variations
                p2 = addPoints(p1, magN)

                if p2.x() >= extent.xMinimum() and p2.x() <= extent.xMaximum():
                    line.append(p2)
                    empty = False

                # start a new, longitudinally adjusted line if distance exceeds tolerance
                if distanceTolerance > 0 and len(lastStartPoints) > t:
                    lastP2 = lastStartPoints[t]
                    (factor, nextX) = self.adjustLongForVariation(
                        lastP2, lineDistance, variation)
                    if abs(p2.x() - nextX) > distanceTolerance * factor:
                        # Flush any line that is in progress
                        if len(line) >= 2:
                            feature = QgsFeature()
                            feature.setAttributes([lineCount, variation])
                            feature.setGeometry(
                                QgsGeometry.fromPolylineXY(line))
                            sink.addFeature(feature)

                        # Now start a new line, properly spaced from the previous trace
                        p2 = QgsPointXY(nextX, p2.y())
                        line = []
                        if p2.x() >= extent.xMinimum() and p2.x(
                        ) <= extent.xMaximum():
                            line.append(p2)
                            empty = False

                # if we didn't do that, and variation error exceeds tolerance, then start a new line in the same spot
                elif variationTolerance > 0 and abs(
                        variation - lastVariation) > variationTolerance:
                    # Flush any line that is in progress and start a new line in the same spot
                    if len(line) >= 2:
                        feature = QgsFeature()
                        feature.setAttributes([lineCount, variation])
                        feature.setGeometry(QgsGeometry.fromPolylineXY(line))
                        sink.addFeature(feature)
                    line = [p2]
                    lastVariation = variation

                startPoints.append(p2)
                p1 = p2

            # end field tracing loop

            # Flush any accumulated points to a polyline
            if len(line) >= 2:
                feature = QgsFeature()
                feature.setAttributes([lineCount, variation])
                feature.setGeometry(QgsGeometry.fromPolylineXY(line))
                sink.addFeature(feature)

            # If we did not manage to find any points inside the extent on this trace, we're done
            if empty:
                break

            # hold onto our point list for spacing check on the next trace
            lastStartPoints = startPoints

            # now advance the start point longitude, correcting for the variation angle
            variation = geomag.declination(start.y(), start.x(), 0,
                                           date.today())
            (factor,
             nextX) = self.adjustLongForVariation(start, lineDistance,
                                                  variation)
            start.setX(nextX)

            lineCount += 1
            if lineDistance > 0:
                feedback.setProgress(100 * (start.x() - extent.xMinimum()) /
                                     extent.width())
            else:
                feedback.setProgress(100 * (extent.xMaximum() - start.x()) /
                                     extent.width())

        # end longitude loop

        if context.willLoadLayerOnCompletion(dest_id):
            context.layerToLoadOnCompletionDetails(dest_id).setPostProcessor(
                StylePostProcessor.create(self))

        return {self.PrmOutputLayer: dest_id}
Ejemplo n.º 29
0
    def point_clicked(self, point=None, button=None):
        # check if active layer is raster
        if self.raster is None:
            self.uc.bar_warn("Choose a raster to work with...", dur=3)
            return

        # check if coordinates trasformation is required
        canvas_srs = self.iface.mapCanvas().mapSettings().destinationCrs()
        if point is None:
            pos = self.last_point
        elif not canvas_srs == self.raster.crs():
            project = QgsProject.instance()
            srs_transform = QgsCoordinateTransform(canvas_srs,
                                                   self.raster.crs(), project)
            try:
                pos = srs_transform.transform(point)
            except QgsCsException as err:
                self.uc.bar_warn(
                    "Point coordinates transformation failed! Check the raster projection:\n\n{}"
                    .format(repr(err)),
                    dur=5)
                return
        else:
            pos = QgsPointXY(point.x(), point.y())

        # keep last clicked point
        self.last_point = pos

        # check if the point is within active raster bounds
        if self.rbounds[0] <= pos.x() <= self.rbounds[2]:
            self.px = int((pos.x() - self.rbounds[0]) /
                          self.raster.rasterUnitsPerPixelX()
                          )  # - self.gt[0]) / self.gt[1])
        else:
            self.uc.bar_info("Out of x bounds", dur=2)
            return

        if self.rbounds[1] <= pos.y() <= self.rbounds[3]:
            self.py = int((self.rbounds[3] - pos.y()) /
                          self.raster.rasterUnitsPerPixelY()
                          )  #  - self.gt[3]) / self.gt[5])
        else:
            self.uc.bar_info("Out of y bounds", dur=2)
            return

        # probe current raster value, dict: band_nr -> value
        vals = self.rdp.identify(pos, QgsRaster.IdentifyFormatValue).results()

        # for rasters having more that 3 bands, ignore other than 1-3
        bands_to_ignore = [i for i in vals.keys() if i > 3]
        for band_nr in bands_to_ignore:
            del vals[band_nr]

        # data types for each band
        dtypes = []

        for nr in range(1, min(4, self.band_count + 1)):
            # bands data type
            dtypes.append(self.bands[nr]['qtype'])

            # check if nodata is defined
            if self.mode == 'gom' and self.bands[nr]['nodata'] is None:
                msg = 'NODATA value is not defined for one of the raster\'s bands.\n'
                msg += 'Please define it in raster properties dialog!'
                self.uc.show_warn(msg)
                return

            # if in probing mode, set band's spinbox value
            if self.mode == 'probe':
                val = vals[nr] if is_number(
                    vals[nr]) else self.bands[nr]['nodata']
                self.bands[nr]['sbox'].setValue(val)
                self.bands[nr]['sbox'].setFocus()
                self.bands[nr]['sbox'].selectAll()

        if not self.mode == 'probe':

            old_vals = [
                v if v is not None else self.bands[k]['nodata']
                for k, v in sorted(vals.items())
            ]
            if self.mode == 'gom':
                temp_vals = [
                    self.bands[nr]['nodata'] for nr in sorted(vals.keys())
                ]
                new_vals = [
                    int(v) if dtypes[i] < 6 else float(v)
                    for i, v in enumerate(temp_vals)
                ]
            else:
                temp_vals = [
                    self.bands[nr]['sbox'].value()
                    for nr in sorted(vals.keys())
                ]
                new_vals = [
                    int(v) if dtypes[i] < 6 else float(v)
                    for i, v in enumerate(temp_vals)
                ]

            # store all bands' changes to undo list
            self.undos[self.raster.id()].append(
                [old_vals, new_vals, self.px, self.py, pos])

            # write the new cell value(s)
            self.change_cell_value(new_vals)

        if self.band_count > 2:
            self.mColorButton.setColor(
                QColor(self.bands[1]['sbox'].value(),
                       self.bands[2]['sbox'].value(),
                       self.bands[3]['sbox'].value()))
class FreehandRasterGeoreferencerLayer(QgsPluginLayer):

    LAYER_TYPE = "FreehandRasterGeoreferencerLayer"
    transformParametersChanged = pyqtSignal(tuple)

    def __init__(self, plugin, filepath, title, screenExtent):
        QgsPluginLayer.__init__(
            self, FreehandRasterGeoreferencerLayer.LAYER_TYPE, title)
        self.plugin = plugin
        self.iface = plugin.iface

        self.title = title
        self.filepath = filepath
        self.screenExtent = screenExtent
        self.history = []
        # set custom properties
        self.setCustomProperty("title", title)
        self.setCustomProperty("filepath", self.filepath)

        self.setValid(True)

        self.setTransparency(LayerDefaultSettings.TRANSPARENCY)
        self.setBlendModeByName(LayerDefaultSettings.BLEND_MODE)

        # dummy data: real init is done in intializeLayer
        self.center = QgsPointXY(0, 0)
        self.rotation = 0.0
        self.xScale = 1.0
        self.yScale = 1.0

        self.error = False
        self.initializing = False
        self.initialized = False
        self.initializeLayer(screenExtent)
        self._extent = None

        self.provider = FreehandRasterGeoreferencerLayerProvider(self)

    def dataProvider(self):
        # issue with DBManager if the dataProvider of the QgsLayerPlugin
        # returns None
        return self.provider

    def setScale(self, xScale, yScale):
        self.xScale = xScale
        self.yScale = yScale

    def setRotation(self, rotation):
        rotation = round(rotation, 1)
        # keep in -180,180 interval
        if rotation < -180:
            rotation += 360
        if rotation > 180:
            rotation -= 360
        self.rotation = rotation

    def setCenter(self, center):
        self.center = center

    def commitTransformParameters(self):
        QgsProject.instance().setDirty(True)
        self._extent = None
        self.setCustomProperty("xScale", self.xScale)
        self.setCustomProperty("yScale", self.yScale)
        self.setCustomProperty("rotation", self.rotation)
        self.setCustomProperty("xCenter", self.center.x())
        self.setCustomProperty("yCenter", self.center.y())
        self.transformParametersChanged.emit(
            (self.xScale, self.yScale, self.rotation, self.center))

    def reprojectTransformParameters(self, oldCrs, newCrs):
        transform = QgsCoordinateTransform(oldCrs, newCrs,
                                           QgsProject.instance())

        newCenter = transform.transform(self.center)
        newExtent = transform.transform(self.extent())

        # transform the parameters except rotation
        # TODO rotation could be better handled (maybe check rotation between
        # old and new extent)
        # but not really worth the effort ?
        self.setCrs(newCrs)
        self.setCenter(newCenter)
        self.resetScale(newExtent.width(), newExtent.height())

    def resetTransformParametersToNewCrs(self):
        """
        Attempts to keep the layer on the same region of the map when
        the map CRS is changed
        """
        oldCrs = self.crs()
        newCrs = self.iface.mapCanvas().mapSettings().destinationCrs()
        self.reprojectTransformParameters(oldCrs, newCrs)
        self.commitTransformParameters()

    def setupCrsEvents(self):
        layerId = self.id()

        def removeCrsChangeHandler(layerIds):
            if layerId in layerIds:
                try:
                    self.iface.mapCanvas().destinationCrsChanged.disconnect(
                        self.resetTransformParametersToNewCrs)
                except Exception:
                    pass
                try:
                    QgsProject.instance().disconnect(
                        removeCrsChangeHandler)
                except Exception:
                    pass

        self.iface.mapCanvas().destinationCrsChanged.connect(
            self.resetTransformParametersToNewCrs)
        QgsProject.instance().layersRemoved.connect(
            removeCrsChangeHandler)

    def setupCrs(self):
        mapCrs = self.iface.mapCanvas().mapSettings().destinationCrs()
        self.setCrs(mapCrs)

        self.setupCrsEvents()

    def repaint(self):
        self.repaintRequested.emit()

    def transformParameters(self):
        return (self.center, self.rotation, self.xScale, self.yScale)

    def initializeLayer(self, screenExtent=None):
        if self.error or self.initialized or self.initializing:
            return

        if self.filepath is not None:
            # not safe...
            self.initializing = True
            filepath = self.getAbsoluteFilepath()

            if not os.path.exists(filepath):
                # TODO integrate with BadLayerHandler ?
                loadErrorDialog = LoadErrorDialog(filepath)
                result = loadErrorDialog.exec_()
                if result == 1:
                    # absolute
                    filepath = loadErrorDialog.lineEditImagePath.text()
                    # to relative if needed
                    self.filepath = utils.toRelativeToQGS(filepath)
                    self.setCustomProperty("filepath", self.filepath)
                    QgsProject.instance().setDirty(True)
                else:
                    self.error = True

                del loadErrorDialog

            fileInfo = QFileInfo(filepath)
            ext = fileInfo.suffix()
            if ext == "pdf":
                s = QSettings()
                oldValidation = s.value("/Projections/defaultBehavior")
                s.setValue("/Projections/defaultBehavior", "useGlobal") # for not asking about crs
                path = fileInfo.filePath()
                baseName = fileInfo.baseName()
                layer = QgsRasterLayer(path, baseName)
                self.image = layer.previewAsImage(QSize(layer.width(),layer.height()))
                s.setValue("/Projections/defaultBehavior", oldValidation)
            else:
                reader = QImageReader(filepath)
                self.image = reader.read()

            self.initialized = True
            self.initializing = False

            self.setupCrs()

            if screenExtent:
                # constructor called from AddLayer action
                # if not, layer loaded from QGS project file

                # check if image already has georef info
                # use GDAL
                dataset = gdal.Open(filepath, gdal.GA_ReadOnly)
                georef = None
                if dataset:
                    georef = dataset.GetGeoTransform()

                if georef and not self.is_default_geotransform(georef):
                    self.initializeExistingGeoreferencing(dataset, georef)
                else:
                    # init to default params
                    self.setCenter(screenExtent.center())
                    self.setRotation(0.0)

                    sw = screenExtent.width()
                    sh = screenExtent.height()

                    self.resetScale(sw, sh)

                    self.commitTransformParameters()

    def initializeExistingGeoreferencing(self, dataset, georef):
        # georef can have scaling, rotation or translation
        rotation = 180 / math.pi * -math.atan2(georef[4], georef[1])
        sx = math.sqrt(georef[1] ** 2 + georef[4] ** 2)
        sy = math.sqrt(georef[2] ** 2 + georef[5] ** 2)
        i_center_x = self.image.width() / 2
        i_center_y = self.image.height() / 2
        center = QgsPointXY(georef[0] + georef[1] * i_center_x +
                            georef[2] * i_center_y,
                            georef[3] + georef[4] * i_center_x +
                            georef[5] * i_center_y)

        qDebug(repr(rotation) + " " + repr((sx, sy)) + " " +
               repr(center))

        self.setRotation(rotation)
        self.setCenter(center)
        # keep yScale positive
        self.setScale(sx, sy)
        self.commitTransformParameters()

        crs_wkt = dataset.GetProjection()
        message_shown = False
        if crs_wkt:
            qcrs = QgsCoordinateReferenceSystem(crs_wkt)
            if qcrs != self.crs():
                # reproject
                try:
                    self.reprojectTransformParameters(qcrs, self.crs())
                    self.commitTransformParameters()
                    self.showBarMessage(
                        "Transform parameters changed",
                        "Found existing georeferencing in raster but "
                        "its CRS does not match the CRS of the map. "
                        "Reprojected the extent.",
                        Qgis.Warning,
                        5)
                    message_shown = True
                except Exception as ex:
                    QgsMessageLog.logMessage(repr(ex))
                    self.showBarMessage(
                        "CRS does not match",
                        "Found existing georeferencing in raster but "
                        "its CRS does not match the CRS of the map. "
                        "Unable to reproject.",
                        Qgis.Warning,
                        5)
                    message_shown = True
        # if no projection info, assume it is the same CRS
        # as the map and no warning
        if not message_shown:
            self.showBarMessage(
                "Georeferencing loaded",
                "Found existing georeferencing in raster",
                Qgis.Info,
                3)

        # zoom (assume the user wants to work on the image)
        self.iface.mapCanvas().setExtent(self.extent())

    def is_default_geotransform(self, georef):
        """
        Check if there is really a transform or if it is just the default
        made up by GDAL
        """
        return (georef[0] == 0 and georef[3] == 0 and georef[1] == 1 and
                georef[5] == 1)

    def resetScale(self, sw, sh):
        iw = self.image.width()
        ih = self.image.height()
        wratio = sw / iw
        hratio = sh / ih

        if wratio > hratio:
            # takes all height of current extent
            self.setScale(hratio, hratio)
        else:
            # all width
            self.setScale(wratio, wratio)

    def replaceImage(self, filepath, title):
        self.title = title
        self.filepath = filepath

         # set custom properties
        self.setCustomProperty("title", title)
        self.setCustomProperty("filepath", self.filepath)
        self.setName(title)

        fileInfo = QFileInfo(filepath)
        ext = fileInfo.suffix()
        if ext == "pdf":
            s = QSettings()
            oldValidation = s.value("/Projections/defaultBehavior")
            s.setValue("/Projections/defaultBehavior", "useGlobal")  # for not asking about crs
            path = fileInfo.filePath()
            baseName = fileInfo.baseName()
            layer = QgsRasterLayer(path, baseName)
            self.image = layer.previewAsImage(QSize(layer.width(), layer.height()))
            s.setValue("/Projections/defaultBehavior", oldValidation)
        else:
            reader = QImageReader(filepath)
            self.image = reader.read()
        self.repaint()

    def clone(self):
        layer = FreehandRasterGeoreferencerLayer(self.plugin, self.filepath, self.title, self.screenExtent)
        layer.center = self.center
        layer.rotation = self.rotation
        layer.xScale = self.xScale
        layer.yScale = self.yScale
        layer.commitTransformParameters()
        return layer

    def getAbsoluteFilepath(self):
        if not os.path.isabs(self.filepath):
            # relative to QGS file
            qgsPath = QgsProject.instance().fileName()
            qgsFolder, _ = os.path.split(qgsPath)
            filepath = os.path.join(qgsFolder, self.filepath)
        else:
            filepath = self.filepath

        return filepath

    def extent(self):
        self.initializeLayer()
        if not self.initialized:
            qDebug("Not Initialized")
            return QgsRectangle(0, 0, 1, 1)

        if self._extent:
            return self._extent

        topLeft, topRight, bottomRight, bottomLeft = self.cornerCoordinates()

        left = min(topLeft.x(), topRight.x(), bottomRight.x(), bottomLeft.x())
        right = max(topLeft.x(), topRight.x(), bottomRight.x(), bottomLeft.x())
        top = max(topLeft.y(), topRight.y(), bottomRight.y(), bottomLeft.y())
        bottom = min(topLeft.y(), topRight.y(),
                     bottomRight.y(), bottomLeft.y())

        # recenter + create rectangle
        self._extent = QgsRectangle(left, bottom, right, top)
        return self._extent

    def cornerCoordinates(self):
        return self.transformedCornerCoordinates(self.center,
                                                 self.rotation,
                                                 self.xScale,
                                                 self.yScale)

    def transformedCornerCoordinates(self, center, rotation, xScale, yScale):
        # scale
        topLeft = QgsPointXY(-self.image.width() / 2.0 * xScale,
                             self.image.height() / 2.0 * yScale)
        topRight = QgsPointXY(self.image.width() / 2.0 * xScale,
                              self.image.height() / 2.0 * yScale)
        bottomLeft = QgsPointXY(-self.image.width() / 2.0 * xScale,
                                - self.image.height() / 2.0 * yScale)
        bottomRight = QgsPointXY(self.image.width() / 2.0 * xScale,
                                 - self.image.height() / 2.0 * yScale)

        # rotate
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)

        topLeft = self._rotate(topLeft, cosRot, sinRot)
        topRight = self._rotate(topRight, cosRot, sinRot)
        bottomRight = self._rotate(bottomRight, cosRot, sinRot)
        bottomLeft = self._rotate(bottomLeft, cosRot, sinRot)

        topLeft.set(topLeft.x() + center.x(), topLeft.y() + center.y())
        topRight.set(topRight.x() + center.x(), topRight.y() + center.y())
        bottomRight.set(bottomRight.x() + center.x(),
                        bottomRight.y() + center.y())
        bottomLeft.set(bottomLeft.x() + center.x(),
                       bottomLeft.y() + center.y())

        return (topLeft, topRight, bottomRight, bottomLeft)

    def transformedCornerCoordinatesFromPoint(self, startPoint, rotation,
                                              xScale, yScale):
        # startPoint is a fixed point for this new movement (rotation and
        # scale)
        # rotation is the global rotation of the image
        # xScale is the new xScale factor to be multiplied by self.xScale
        # idem for yScale
        # Calculate the coordinate of the center in a startPoint origin
        # coordinate system and apply scales
        dX = (self.center.x() - startPoint.x()) * xScale
        dY = (self.center.y() - startPoint.y()) * yScale
        # Half width and half height in the current transformation
        hW = (self.image.width() / 2.0) * self.xScale * xScale
        hH = (self.image.height() / 2.0) * self.yScale * yScale
        # Actual rectangle coordinates :
        pt1 = QgsPointXY(-hW, hH)
        pt2 = QgsPointXY(hW, hH)
        pt3 = QgsPointXY(hW, -hH)
        pt4 = QgsPointXY(-hW, -hH)
        # Actual rotation from the center
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -self.rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)
        pt1 = self._rotate(pt1, cosRot, sinRot)
        pt2 = self._rotate(pt2, cosRot, sinRot)
        pt3 = self._rotate(pt3, cosRot, sinRot)
        pt4 = self._rotate(pt4, cosRot, sinRot)
        # Second transformation
        # displacement of the origin
        pt1 = QgsPointXY(pt1.x() + dX, pt1.y() + dY)
        pt2 = QgsPointXY(pt2.x() + dX, pt2.y() + dY)
        pt3 = QgsPointXY(pt3.x() + dX, pt3.y() + dY)
        pt4 = QgsPointXY(pt4.x() + dX, pt4.y() + dY)
        # Rotation
        # minus sign because rotation is CW in this class and Qt)
        rotationRad = -rotation * math.pi / 180
        cosRot = math.cos(rotationRad)
        sinRot = math.sin(rotationRad)
        pt1 = self._rotate(pt1, cosRot, sinRot)
        pt2 = self._rotate(pt2, cosRot, sinRot)
        pt3 = self._rotate(pt3, cosRot, sinRot)
        pt4 = self._rotate(pt4, cosRot, sinRot)
        # translate to startPoint
        pt1 = QgsPointXY(pt1.x() + startPoint.x(), pt1.y() + startPoint.y())
        pt2 = QgsPointXY(pt2.x() + startPoint.x(), pt2.y() + startPoint.y())
        pt3 = QgsPointXY(pt3.x() + startPoint.x(), pt3.y() + startPoint.y())
        pt4 = QgsPointXY(pt4.x() + startPoint.x(), pt4.y() + startPoint.y())

        return (pt1, pt2, pt3, pt4)

    def moveCenterFromPointRotate(self, startPoint, rotation, xScale, yScale):
        cornerPoints = self.transformedCornerCoordinatesFromPoint(
            startPoint, rotation, xScale, yScale)
        self.center = QgsPointXY((cornerPoints[0].x(
        ) + cornerPoints[2].x()) / 2, (cornerPoints[0].y() +
                                       cornerPoints[2].y()) / 2)

    def _rotate(self, point, cosRot, sinRot):
        return QgsPointXY(point.x() * cosRot - point.y() * sinRot,
                          point.x() * sinRot + point.y() * cosRot)

    def createMapRenderer(self, rendererContext):
        return FreehandRasterGeoreferencerLayerRenderer(self, rendererContext)

    def setBlendModeByName(self, modeName):
        self.blendModeName = modeName
        blendMode = getattr(QPainter, "CompositionMode_" + modeName, 0)
        self.setBlendMode(blendMode)
        self.setCustomProperty("blendMode", modeName)

    def setTransparency(self, transparency):
        self.transparency = transparency
        self.setCustomProperty("transparency", transparency)

    def draw(self, renderContext):
        if renderContext.extent().isEmpty():
            qDebug("Drawing is skipped because map extent is empty.")
            return True

        self.initializeLayer()
        if not self.initialized:
            qDebug("Drawing is skipped because nothing to draw.")
            return True

        painter = renderContext.painter()
        painter.save()
        self.prepareStyle(painter)
        self.drawRaster(renderContext)
        painter.restore()

        return True

    def drawRaster(self, renderContext):
        painter = renderContext.painter()
        self.map2pixel = renderContext.mapToPixel()

        scaleX = self.xScale / self.map2pixel.mapUnitsPerPixel()
        scaleY = self.yScale / self.map2pixel.mapUnitsPerPixel()

        rect = QRectF(QPointF(-self.image.width() / 2.0,
                              - self.image.height() / 2.0),
                      QPointF(self.image.width() / 2.0,
                              self.image.height() / 2.0))
        mapCenter = self.map2pixel.transform(self.center)

        # draw the image on the map canvas
        painter.translate(QPoint(round(mapCenter.x()), round(mapCenter.y())))
        painter.rotate(self.rotation)
        painter.scale(scaleX, scaleY)
        painter.drawImage(rect, self.image)

        painter.setBrush(Qt.NoBrush)
        painter.setPen(QColor(0, 0, 0))
        painter.drawRect(rect)

    def prepareStyle(self, painter):
        painter.setOpacity(1.0 - self.transparency / 100.0)

    def readXml(self, node, context):
        self.readCustomProperties(node)
        self.title = self.customProperty("title", "")
        self.filepath = self.customProperty("filepath", "")
        self.xScale = float(self.customProperty("xScale", 1.0))
        self.yScale = float(self.customProperty("yScale", 1.0))
        self.rotation = float(self.customProperty("rotation", 0.0))
        xCenter = float(self.customProperty("xCenter", 0.0))
        yCenter = float(self.customProperty("yCenter", 0.0))
        self.center = QgsPointXY(xCenter, yCenter)
        self.setTransparency(int(self.customProperty(
            "transparency", LayerDefaultSettings.TRANSPARENCY)))
        self.setBlendModeByName(self.customProperty(
            "blendMode", LayerDefaultSettings.BLEND_MODE))
        return True

    def writeXml(self, node, doc, context):
        element = node.toElement()
        self.writeCustomProperties(node, doc)
        element.setAttribute("type", "plugin")
        element.setAttribute(
            "name", FreehandRasterGeoreferencerLayer.LAYER_TYPE)
        return True

    def metadata(self):
        lines = []
        fmt = "%s:\t%s"
        lines.append(fmt % (self.tr("Title"), self.title))
        filepath = self.getAbsoluteFilepath()
        filepath = os.path.normpath(filepath)
        lines.append(fmt % (self.tr("Path"), filepath))
        lines.append(fmt % (self.tr("Image Width"), str(self.image.width())))
        lines.append(fmt % (self.tr("Image Height"), str(self.image.height())))
        lines.append(fmt % (self.tr("Rotation (CW)"), str(self.rotation)))
        lines.append(fmt % (self.tr("X center"), str(self.center.x())))
        lines.append(fmt % (self.tr("Y center"), str(self.center.y())))
        lines.append(fmt % (self.tr("X scale"), str(self.xScale)))
        lines.append(fmt % (self.tr("Y scale"), str(self.yScale)))

        return "\n".join(lines)

    def log(self, msg):
        qDebug(msg)

    def dump(self, detail=False, bbox=None):
        pass

    def showStatusMessage(self, msg, timeout):
        self.iface.mainWindow().statusBar().showMessage(msg, timeout)

    def showBarMessage(self, title, text, level, duration):
        self.iface.messageBar().pushMessage(title, text, level, duration)

    def transparencyChanged(self, val):
        QgsProject.instance().setDirty(True)
        self.setTransparency(val)
        self.repaintRequested.emit()
Ejemplo n.º 31
0
class Qad_pedit_maptool(QadGetPoint):
    
   def __init__(self, plugIn):
      QadGetPoint.__init__(self, plugIn)

      self.firstPt = None
      self.mode = None
                       
      self.layer = None
      self.polyline = QadPolyline()
      self.tolerance2ApproxCurve = None
      self.vertexAt = 0
      self.vertexPt = None
      self.after = True 
      self.basePt = None
      self.__highlight = QadHighlight(self.canvas)


   def hidePointMapToolMarkers(self):
      QadGetPoint.hidePointMapToolMarkers(self)
      self.__highlight.hide()

   def showPointMapToolMarkers(self):
      QadGetPoint.showPointMapToolMarkers(self)
      self.__highlight.show()
                             
   def clear(self):
      QadGetPoint.clear(self)
      self.__highlight.reset()
      self.mode = None
      if self.basePt is not None:
         del(self.basePt)
         self.basePt = None

   def setPolyline(self, polyline, layer):
      self.polyline.set(polyline)
      self.layer = layer
      self.tolerance2ApproxCurve = QadVariables.get(QadMsg.translate("Environment variables", "TOLERANCE2APPROXCURVE"))


   def setVertexAt(self, vertexAt, after = None):
      if vertexAt == self.polyline.qty():
         self.firstPt = self.polyline.getLinearObjectAt(-1).getEndPt()
      else:
         self.firstPt = self.polyline.getLinearObjectAt(vertexAt).getStartPt()
      
      self.vertexPt = QgsPointXY(self.firstPt)
      self.vertexAt = vertexAt
      self.after = after      
    
      
   def canvasMoveEvent(self, event):
      QadGetPoint.canvasMoveEvent(self, event)
      
      self.__highlight.reset()
      tmpPolyline = None
       
      # si richiede un nuovo vertice da inserire
      if self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_NEW_VERTEX:
         if self.basePt is not None:
            offsetX = self.tmpPoint.x() - self.basePt.x()
            offsetY = self.tmpPoint.y() - self.basePt.y()
            newPt = QgsPointXY(self.vertexPt.x() + offsetX, self.vertexPt.y() + offsetY)
         else:
            newPt = QgsPointXY(self.tmpPoint)
            
         tmpPolyline = self.polyline.copy()
         
         if self.after: # dopo
            if self.vertexAt == tmpPolyline.qty() and tmpPolyline.isClosed():
               tmpPolyline.insertPoint(0, newPt)
            else:
               tmpPolyline.insertPoint(self.vertexAt, newPt)
         else: # prima
            if self.vertexAt == 0 and tmpPolyline.isClosed():
               tmpPolyline.insertPoint(tmpPolyline.qty() - 1, newPt)
            else:
               tmpPolyline.insertPoint(self.vertexAt - 1, newPt)
               
      elif self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_MOVE_VERTEX:
         newPt = QgsPointXY(self.tmpPoint)
         tmpPolyline = self.polyline.copy()
         tmpPolyline.movePoint(self.vertexAt, newPt)
      
      if tmpPolyline is not None:
         pts = tmpPolyline.asPolyline(self.tolerance2ApproxCurve) 
         if self.layer.geometryType() == QgsWkbTypes.PolygonGeometry:
            geom = QgsGeometry.fromPolygonXY([pts])
         else:
            geom = QgsGeometry.fromPolylineXY(pts)
            
         # trasformo la geometria nel crs del layer
         self.__highlight.addGeometry(self.mapToLayerCoordinates(self.layer, geom), self.layer)
      
    
   def activate(self):
      QadGetPoint.activate(self)
      self.__highlight.show()          

   def deactivate(self):
      try: # necessario perché se si chiude QGIS parte questo evento nonostante non ci sia più l'oggetto maptool !
         QadGetPoint.deactivate(self)
         self.__highlight.hide()
      except:
         pass

   def setMode(self, mode):
      self.mode = mode
            
      # si richiede la selezione di un'entità
      if self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_ENTITY_SEL:
         self.setSelectionMode(QadGetPointSelectionModeEnum.ENTITY_SELECTION)
         
         # solo layer lineari o poligono editabili che non appartengano a quote
         layerList = []
         for layer in qad_utils.getVisibleVectorLayers(self.plugIn.canvas): # Tutti i layer vettoriali visibili
            if (layer.geometryType() == QgsWkbTypes.LineGeometry or layer.geometryType() == QgsWkbTypes.PolygonGeometry) and \
               layer.isEditable():
               if len(QadDimStyles.getDimListByLayer(layer)) == 0:
                  layerList.append(layer)
         
         self.layersToCheck = layerList
         self.setSnapType(QadSnapTypeEnum.DISABLE)
      # non si richiede niente
      elif self.mode == Qad_pedit_maptool_ModeEnum.NONE:
         self.setSelectionMode(QadGetPointSelectionModeEnum.POINT_SELECTION)   
         self.setDrawMode(QadGetPointDrawModeEnum.NONE)
      # si richiede il primo punto per calcolo distanza di approssimazione
      # si richiede la posizione più vicina ad un vertice
      elif self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_FIRST_TOLERANCE_PT:
         self.onlyEditableLayers = False
         self.checkPointLayer = True
         self.checkLineLayer = True
         self.checkPolygonLayer = True
         self.setSnapType()
         self.setSelectionMode(QadGetPointSelectionModeEnum.POINT_SELECTION)   
         self.setDrawMode(QadGetPointDrawModeEnum.NONE)
      # noto il primo punto per calcolo distanza di approssimazione si richiede il secondo punto
      elif self.mode == Qad_pedit_maptool_ModeEnum.FIRST_TOLERANCE_PT_KNOWN_ASK_FOR_SECOND_PT or \
           self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_NEW_VERTEX or \
           self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_MOVE_VERTEX:         
         self.onlyEditableLayers = False
         self.checkPointLayer = True
         self.checkLineLayer = True
         self.checkPolygonLayer = True
         self.setSnapType()
         self.setSelectionMode(QadGetPointSelectionModeEnum.POINT_SELECTION)   
         self.setDrawMode(QadGetPointDrawModeEnum.ELASTIC_LINE)
         self.setStartPoint(self.firstPt)
      # si richiede la posizione più vicina ad un vertice
      elif self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_VERTEX:
         self.setSnapType(QadSnapTypeEnum.DISABLE)
         self.setSelectionMode(QadGetPointSelectionModeEnum.POINT_SELECTION)   
         self.setDrawMode(QadGetPointDrawModeEnum.NONE)
      # si richede il punto base (grip mode)
      elif self.mode == Qad_pedit_maptool_ModeEnum.ASK_FOR_BASE_PT:
         self.setSnapType()
         self.setSelectionMode(QadGetPointSelectionModeEnum.POINT_SELECTION)   
         self.setDrawMode(QadGetPointDrawModeEnum.NONE)
def processPoly(source, sink, feedback, maxseglen):
    layercrs = source.sourceCrs()
    if layercrs != epsg4326:
        transto4326 = QgsCoordinateTransform(layercrs, epsg4326,
                                             QgsProject.instance())
        transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs,
                                               QgsProject.instance())

    total = 100.0 / source.featureCount() if source.featureCount() else 0
    iterator = source.getFeatures()
    num_bad = 0
    for cnt, feature in enumerate(iterator):
        if feedback.isCanceled():
            break
        try:
            if not feature.geometry().isMultipart():
                poly = feature.geometry().asPolygon()
                numpolygons = len(poly)
                if numpolygons < 1:
                    continue

                ptset = []
                # Iterate through all points in the polygon and if the distance
                # is greater than the maxseglen, then add additional points.
                for points in poly:
                    numpoints = len(points)
                    if numpoints < 2:
                        continue
                    # If the input is not 4326 we need to convert it to that and then back to the output CRS
                    ptStart = QgsPointXY(points[0][0], points[0][1])
                    if layercrs != epsg4326:  # Convert to 4326
                        ptStart = transto4326.transform(ptStart)
                    pts = [ptStart]
                    for x in range(1, numpoints):
                        ptEnd = QgsPointXY(points[x][0], points[x][1])
                        if layercrs != epsg4326:  # Convert to 4326
                            ptEnd = transto4326.transform(ptEnd)
                        l = geod.InverseLine(ptStart.y(), ptStart.x(),
                                             ptEnd.y(), ptEnd.x())
                        # Check to see if the distance is greater than the maximum
                        # segment length and if so lets add additional points.
                        if l.s13 > maxseglen:
                            n = int(math.ceil(l.s13 / maxseglen))
                            seglen = l.s13 / n
                            for i in range(1, n):
                                s = seglen * i
                                g = l.Position(
                                    s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                                    | Geodesic.LONG_UNROLL)
                                pts.append(QgsPointXY(g['lon2'], g['lat2']))
                        pts.append(ptEnd)
                        ptStart = ptEnd

                    if layercrs != epsg4326:  # Convert each point to the output CRS
                        for x, pt in enumerate(pts):
                            pts[x] = transfrom4326.transform(pt)
                    ptset.append(pts)

                if len(ptset) > 0:
                    featureout = QgsFeature()
                    featureout.setGeometry(QgsGeometry.fromPolygonXY(ptset))

                    featureout.setAttributes(feature.attributes())
                    sink.addFeature(featureout)
            else:
                multipoly = feature.geometry().asMultiPolygon()
                multiset = []
                for poly in multipoly:
                    ptset = []
                    for points in poly:
                        numpoints = len(points)
                        if numpoints < 2:
                            continue
                        # If the input is not 4326 we need to convert it to that and then back to the output CRS
                        ptStart = QgsPointXY(points[0][0], points[0][1])
                        if layercrs != epsg4326:  # Convert to 4326
                            ptStart = transto4326.transform(ptStart)
                        pts = [ptStart]
                        for x in range(1, numpoints):
                            ptEnd = QgsPointXY(points[x][0], points[x][1])
                            if layercrs != epsg4326:  # Convert to 4326
                                ptEnd = transto4326.transform(ptEnd)
                            l = geod.InverseLine(ptStart.y(), ptStart.x(),
                                                 ptEnd.y(), ptEnd.x())
                            if l.s13 > maxseglen:
                                n = int(math.ceil(l.s13 / maxseglen))
                                seglen = l.s13 / n
                                for i in range(1, n):
                                    s = seglen * i
                                    g = l.Position(
                                        s,
                                        Geodesic.LATITUDE | Geodesic.LONGITUDE
                                        | Geodesic.LONG_UNROLL)
                                    pts.append(QgsPointXY(
                                        g['lon2'], g['lat2']))
                            pts.append(ptEnd)
                            ptStart = ptEnd

                        if layercrs != epsg4326:  # Convert each point to the output CRS
                            for x, pt in enumerate(pts):
                                pts[x] = transfrom4326.transform(pt)
                        ptset.append(pts)
                    multiset.append(ptset)

                if len(multiset) > 0:
                    featureout = QgsFeature()
                    featureout.setGeometry(
                        QgsGeometry.fromMultiPolygonXY(multiset))

                    featureout.setAttributes(feature.attributes())
                    sink.addFeature(featureout)
        except:
            num_bad += 1
            '''s = traceback.format_exc()
            feedback.pushInfo(s)'''

        feedback.setProgress(int(cnt * total))
    return num_bad
Ejemplo n.º 33
0
class QadGRIPMOVECommandClass(QadCommandClass):
    def instantiateNewCmd(self):
        """ istanzia un nuovo comando dello stesso tipo """
        return QadGRIPMOVECommandClass(self.plugIn)

    def __init__(self, plugIn):
        QadCommandClass.__init__(self, plugIn)
        self.cacheEntitySet = QadCacheEntitySet()
        self.basePt = QgsPointXY()
        self.skipToNextGripCommand = False
        self.copyEntities = False
        self.nOperationsToUndo = 0

    def __del__(self):
        QadCommandClass.__del__(self)

    def getPointMapTool(self, drawMode=QadGetPointDrawModeEnum.NONE):
        if (self.plugIn is not None):
            if self.PointMapTool is None:
                self.PointMapTool = Qad_move_maptool(self.plugIn)
            return self.PointMapTool
        else:
            return None

    #============================================================================
    # setSelectedEntityGripPoints
    #============================================================================
    def setSelectedEntityGripPoints(self, entitySetGripPoints):
        # lista delle entityGripPoint con dei grip point selezionati
        self.cacheEntitySet.clear()

        for entityGripPoints in entitySetGripPoints.entityGripPoints:
            self.cacheEntitySet.appendEntity(entityGripPoints.entity)

        self.getPointMapTool().cacheEntitySet = self.cacheEntitySet

    #============================================================================
    # move
    #============================================================================
    def move(self, entity, offsetX, offsetY):
        # verifico se l'entità appartiene ad uno stile di quotatura
        if entity.whatIs() == "ENTITY":
            # sposto la geometria dell'entità
            qadGeom = entity.getQadGeom().copy()  # la copio
            qadGeom.move(offsetX, offsetY)
            f = entity.getFeature()
            f.setGeometry(fromQadGeomToQgsGeom(qadGeom, entity.crs()))
            if self.copyEntities == False:
                # plugIn, layer, feature, refresh, check_validity
                if qad_layer.updateFeatureToLayer(self.plugIn, entity.layer, f,
                                                  False, False) == False:
                    return False
            else:
                # plugIn, layer, features, coordTransform, refresh, check_validity
                if qad_layer.addFeatureToLayer(self.plugIn, entity.layer, f,
                                               None, False, False) == False:
                    return False
        elif entity.whatIs() == "DIMENTITY":
            # stiro la quota
            if self.copyEntities == False:
                if entity.deleteToLayers(self.plugIn) == False:
                    return False
            newDimEntity = QadDimEntity(entity)  # la copio
            newDimEntity.move(offsetX, offsetY)
            if newDimEntity.addToLayers(self.plugIn) == False:
                return False

        return True

    #============================================================================
    # moveFeatures
    #============================================================================
    def moveFeatures(self, newPt):
        offsetX = newPt.x() - self.basePt.x()
        offsetY = newPt.y() - self.basePt.y()

        self.plugIn.beginEditCommand("Feature moved",
                                     self.cacheEntitySet.getLayerList())

        dimElaboratedList = []  # lista delle quotature già elaborate
        entityIterator = QadCacheEntitySetIterator(self.cacheEntitySet)
        for entity in entityIterator:
            qadGeom = entity.getQadGeom()  # così inizializzo le info qad
            # verifico se l'entità appartiene ad uno stile di quotatura
            dimEntity = QadDimStyles.getDimEntity(entity)
            if dimEntity is not None:
                if appendDimEntityIfNotExisting(
                        dimElaboratedList,
                        dimEntity) == False:  # quota già elaborata
                    continue
                entity = dimEntity

            if self.move(entity, offsetX, offsetY) == False:
                self.plugIn.destroyEditCommand()
                return

        self.plugIn.endEditCommand()
        self.nOperationsToUndo = self.nOperationsToUndo + 1

    #============================================================================
    # waitForMovePoint
    #============================================================================
    def waitForMovePoint(self):
        self.step = 1
        self.plugIn.setLastPoint(self.basePt)
        # imposto il map tool
        self.getPointMapTool().basePt = self.basePt
        self.getPointMapTool().setMode(
            Qad_move_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_MOVE_PT)

        keyWords = QadMsg.translate("Command_GRIPMOVE", "Base point") + "/" + \
                   QadMsg.translate("Command_GRIPMOVE", "Copy") + "/" + \
                   QadMsg.translate("Command_GRIPMOVE", "Undo") + "/" + \
                   QadMsg.translate("Command_GRIPMOVE", "eXit")

        prompt = QadMsg.translate(
            "Command_GRIPMOVE",
            "Specify move point or [{0}]: ").format(keyWords)

        englishKeyWords = "Base point" + "/" + "Copy" + "/" + "Undo" + "/" + "eXit"
        keyWords += "_" + englishKeyWords
        # si appresta ad attendere un punto o enter o una parola chiave
        # msg, inputType, default, keyWords, nessun controllo
        self.waitFor(prompt, QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                     None, \
                     keyWords, QadInputModeEnum.NONE)

    #============================================================================
    # waitForBasePt
    #============================================================================
    def waitForBasePt(self):
        self.step = 2
        # imposto il map tool
        self.getPointMapTool().setMode(
            Qad_move_maptool_ModeEnum.NONE_KNOWN_ASK_FOR_BASE_PT)

        # si appresta ad attendere un punto
        self.waitForPoint(
            QadMsg.translate("Command_GRIPMOVE", "Specify base point: "))

    #============================================================================
    # run
    #============================================================================
    def run(self, msgMapTool=False, msg=None):
        if self.plugIn.canvas.mapSettings().destinationCrs().isGeographic():
            self.showMsg(
                QadMsg.translate(
                    "QAD",
                    "\nThe coordinate reference system of the project must be a projected coordinate system.\n"
                ))
            return True  # fine comando

        #=========================================================================
        # RICHIESTA SELEZIONE OGGETTI
        if self.step == 0:  # inizio del comando
            if self.cacheEntitySet.isEmpty(
            ):  # non ci sono oggetti da spostare
                return True
            self.showMsg(QadMsg.translate("Command_GRIPMOVE",
                                          "\n** MOVE **\n"))
            # si appresta ad attendere un punto di spostamento
            self.waitForMovePoint()
            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA DI UN PUNTO DI SPOSTAMENTO
        elif self.step == 1:
            ctrlKey = False
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        value = None
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False
                else:
                    value = self.getPointMapTool().point

                ctrlKey = self.getPointMapTool().ctrlKey
            else:  # il punto arriva come parametro della funzione
                value = msg

            if type(value) == unicode:
                if value == QadMsg.translate(
                        "Command_GRIPMOVE",
                        "Base point") or value == "Base point":
                    # si appresta ad attendere il punto base
                    self.waitForBasePt()
                elif value == QadMsg.translate("Command_GRIPMOVE",
                                               "Copy") or value == "Copy":
                    # Copia entità lasciando inalterate le originali
                    self.copyEntities = True
                    # si appresta ad attendere un punto di spostamento
                    self.waitForMovePoint()
                elif value == QadMsg.translate("Command_GRIPMOVE",
                                               "Undo") or value == "Undo":
                    if self.nOperationsToUndo > 0:
                        self.nOperationsToUndo = self.nOperationsToUndo - 1
                        self.plugIn.undoEditCommand()
                    else:
                        self.showMsg(
                            QadMsg.translate(
                                "QAD", "\nThe command has been canceled."))
                    # si appresta ad attendere un punto di spostamento
                    self.waitForMovePoint()
                elif value == QadMsg.translate("Command_GRIPMOVE",
                                               "eXit") or value == "eXit":
                    return True  # fine comando
            elif type(value) == QgsPointXY:  # se é stato selezionato un punto
                if ctrlKey:
                    self.copyEntities = True

                self.moveFeatures(value)

                if self.copyEntities == False:
                    return True
                # si appresta ad attendere un punto di stiramento
                self.waitForMovePoint()

            else:
                if self.copyEntities == False:
                    self.skipToNextGripCommand = True
                return True  # fine comando

            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA PUNTO BASE (da step = 1)
        elif self.step == 2:  # dopo aver atteso un punto
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        pass  # opzione di default "spostamento"
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            if type(value) == QgsPointXY:  # se é stato inserito il punto base
                self.basePt.set(value.x(), value.y())
                # imposto il map tool
                self.getPointMapTool().basePt = self.basePt

            # si appresta ad attendere un punto di spostamento
            self.waitForMovePoint()

            return False
Ejemplo n.º 34
0
class QadGRIPSTRETCHCommandClass(QadCommandClass):
    def instantiateNewCmd(self):
        """ istanzia un nuovo comando dello stesso tipo """
        return QadGRIPSTRETCHCommandClass(self.plugIn)

    def __init__(self, plugIn):
        QadCommandClass.__init__(self, plugIn)
        self.selectedEntityGripPoints = [
        ]  # lista in cui ogni elemento è una entità + una lista di punti da stirare
        self.basePt = QgsPointXY()
        self.skipToNextGripCommand = False
        self.copyEntities = False
        self.nOperationsToUndo = 0

    def __del__(self):
        QadCommandClass.__del__(self)

    def getPointMapTool(self, drawMode=QadGetPointDrawModeEnum.NONE):
        if (self.plugIn is not None):
            if self.PointMapTool is None:
                self.PointMapTool = Qad_gripStretch_maptool(self.plugIn)
            return self.PointMapTool
        else:
            return None

    #============================================================================
    # addToSelectedEntityGripPoints
    #============================================================================
    def addToSelectedEntityGripPoints(self, entityGripPoints):
        # entità con lista dei grip point
        i = 0
        gripPoints = entityGripPoints.gripPoints
        gripPointsLen = len(gripPoints)
        ptList = []
        while i < gripPointsLen:
            gripPoint = gripPoints[i]
            # grip point selezionato
            if gripPoint.getStatus() == qad_grip.QadGripStatusEnum.SELECTED:
                if gripPoint.gripType == qad_grip.QadGripPointTypeEnum.CENTER:
                    ptList.append(gripPoint.getPoint())
                elif gripPoint.gripType == qad_grip.QadGripPointTypeEnum.LINE_MID_POINT:
                    # aggiungo il vertice precedente e successivo di quello intermedio
                    if i > 0:
                        ptList.append(gripPoints[i - 1].getPoint())
                    if i < gripPointsLen - 1:
                        ptList.append(gripPoints[i + 1].getPoint())
                elif gripPoint.gripType == qad_grip.QadGripPointTypeEnum.QUA_POINT:
                    ptList.append(gripPoint.getPoint())
                elif gripPoint.gripType == qad_grip.QadGripPointTypeEnum.VERTEX or \
                     gripPoint.gripType == qad_grip.QadGripPointTypeEnum.END_VERTEX:
                    ptList.append(gripPoint.getPoint())
                elif gripPoint.gripType == qad_grip.QadGripPointTypeEnum.ARC_MID_POINT:
                    ptList.append(gripPoint.getPoint())
            i = i + 1

        if len(ptList) > 0:
            self.selectedEntityGripPoints.append(
                [entityGripPoints.entity, ptList])

    #============================================================================
    # setSelectedEntityGripPoints
    #============================================================================
    def setSelectedEntityGripPoints(self, entitySetGripPoints):
        # lista delle entityGripPoint con dei grip point selezionati
        # ritorna una lista in cui ogni elemento è una entità + una lista di punti da stirare
        del self.selectedEntityGripPoints[:]  # svuoto la lista

        for entityGripPoints in entitySetGripPoints.entityGripPoints:
            self.addToSelectedEntityGripPoints(entityGripPoints)
        self.getPointMapTool().setSelectedEntityGripPoints(
            self.selectedEntityGripPoints)

        # input : self.basePt e entitySetGripPoints
        # cerco in entitySetGripPoints l'entità che ha un solo grip selezionato corrispondente a basePt
        entityGripPoints, entityGripPoint = entitySetGripPoints.isIntersecting(
            self.basePt)
        if entityGripPoint.getStatus() == qad_grip.QadGripStatusEnum.SELECTED and \
           len(entityGripPoints.getSelectedGripPoints()) == 1:

            entity = entityGripPoints.entity
            # verifico se l'entità appartiene ad uno stile di quotatura
            if QadDimStyles.isDimEntity(entity):
                pass
            else:
                qadGeom = entity.getQadGeom(entityGripPoint.atGeom,
                                            entityGripPoint.atSubGeom)
                qadGeomType = qadGeom.whatIs()
                if qadGeomType == "POLYLINE":
                    self.getPointMapTool().prevPart, self.getPointMapTool(
                    ).nextPart = qadGeom.getPrevNextLinearObjectsAtVertex(
                        entityGripPoint.nVertex)
                elif qadGeomType == "CIRCLE":
                    if qadGeom.isPtOnCircle(entityGripPoint.getPoint()):
                        line = QadLine()
                        line.set(qadGeom.center, entityGripPoint.getPoint())
                        self.getPointMapTool().prevPart = line
                elif qadGeomType == "ELLIPSE":
                    if qadGeom.containsPt(entityGripPoint.getPoint()):
                        line = QadLine()
                        line.set(qadGeom.center, entityGripPoint.getPoint())
                        self.getPointMapTool().prevPart = line

    #============================================================================
    # getSelectedEntityGripPointNdx
    #============================================================================
    def getSelectedEntityGripPointNdx(self, entity):
        # lista delle entityGripPoint con dei grip point selezionati
        # cerca la posizione di un'entità nella lista in cui ogni elemento è una entità + una lista di punti da stirare
        i = 0
        tot = len(self.selectedEntityGripPoints)
        while i < tot:
            selectedEntityGripPoint = self.selectedEntityGripPoints[i]
            if selectedEntityGripPoint[0] == entity:
                return i
            i = i + 1
        return -1

    #============================================================================
    # stretch
    #============================================================================
    def stretch(self, entity, ptList, offsetX, offsetY, tolerance2ApproxCurve):
        # entity = entità da stirare
        # ptList = lista dei punti da stirare
        # offsetX, offsetY = spostamento da applicare
        # tolerance2ApproxCurve = tolleranza per ricreare le curve

        if entity.whatIs() == "DIMENTITY":
            dimEntity = entity
        else:
            # verifico se l'entità appartiene ad uno stile di quotatura
            dimEntity = QadDimStyles.getDimEntity(entity)

        if dimEntity is None:
            stretchedGeom = entity.getQadGeom()
            # controllo inserito perchè con le quote, questa viene cancellata e ricreata quindi alcuni oggetti potrebbero non esistere più
            if stretchedGeom is None:  # se non c'è lo salto senza errore
                return True

            # stiro la feature
            stretchedGeom = qad_stretch_fun.stretchQadGeometry(stretchedGeom, ptList, \
                                                               offsetX, offsetY)

            if stretchedGeom is not None:
                # trasformo la geometria QAD in geometria GSIS nel crs del layer
                f = entity.getFeature()
                f.setGeometry(fromQadGeomToQgsGeom(stretchedGeom,
                                                   entity.crs()))
                if self.copyEntities == False:
                    # plugIn, layer, feature, refresh, check_validity
                    if qad_layer.updateFeatureToLayer(self.plugIn,
                                                      entity.layer, f, False,
                                                      False) == False:
                        return False
                else:
                    # plugIn, layer, features, coordTransform, refresh, check_validity
                    if qad_layer.addFeatureToLayer(self.plugIn, entity.layer,
                                                   f, None, False,
                                                   False) == False:
                        return False

        else:
            # stiro la quota
            if self.copyEntities == False:
                if dimEntity.deleteToLayers(self.plugIn) == False:
                    return False
            newDimEntity = QadDimEntity(dimEntity)  # la copio
            newDimEntity.stretch(ptList, offsetX, offsetY)
            if newDimEntity.addToLayers(self.plugIn) == False:
                return False
            # non so per quale motivo a volte non si aggiorna la mappa quindi forzo l'aggiornamento
            self.plugIn.canvas.refresh()

        return True

    #============================================================================
    # stretchFeatures
    #============================================================================
    def stretchFeatures(self, newPt):
        # mi ricavo un unico QadEntitySet con le entità selezionate
        entitySet = QadEntitySet()
        for selectedEntity in self.selectedEntityGripPoints:
            entitySet.addEntity(selectedEntity[0])
        self.plugIn.beginEditCommand("Feature stretched",
                                     entitySet.getLayerList())

        dimElaboratedList = []  # lista delle quotature già elaborate

        for selectedEntity in self.selectedEntityGripPoints:
            entity = selectedEntity[0]
            ptList = selectedEntity[1]
            layer = entity.layer

            tolerance2ApproxCurve = QadVariables.get(
                QadMsg.translate("Environment variables",
                                 "TOLERANCE2APPROXCURVE"))
            offsetX = newPt.x() - self.basePt.x()
            offsetY = newPt.y() - self.basePt.y()

            # verifico se l'entità appartiene ad uno stile di quotatura
            dimEntity = QadDimStyles.getDimEntity(entity)
            if dimEntity is None:
                if self.stretch(entity, ptList, offsetX, offsetY,
                                tolerance2ApproxCurve) == False:
                    self.plugIn.destroyEditCommand()
                    return
            else:
                found = False
                for dimElaborated in dimElaboratedList:
                    if dimElaborated == dimEntity:
                        found = True

                if found == False:  # quota non ancora elaborata
                    # aggiungo i layer dei componenti della quota
                    self.plugIn.addLayerListToLastEditCommand(
                        "Feature stretched", [
                            dimEntity.getSymbolLayer(),
                            dimEntity.getLinearLayer(),
                            dimEntity.getTextualLayer()
                        ])

                    dimEntitySet = dimEntity.getEntitySet()
                    # creo un'unica lista contenente i grip points di tutti i componenti della quota
                    dimPtlist = []
                    for layerEntitySet in dimEntitySet.layerEntitySetList:
                        for featureId in layerEntitySet.featureIds:
                            componentDim = QadEntity()
                            componentDim.set(layerEntitySet.layer, featureId)
                            i = self.getSelectedEntityGripPointNdx(
                                componentDim)
                            if i >= 0:
                                dimPtlist.extend(
                                    self.selectedEntityGripPoints[i][1])

                    dimElaboratedList.append(dimEntity)
                    if self.stretch(dimEntity, dimPtlist, offsetX, offsetY,
                                    tolerance2ApproxCurve) == False:
                        self.plugIn.destroyEditCommand()
                        return

        self.plugIn.endEditCommand()
        self.nOperationsToUndo = self.nOperationsToUndo + 1

    #============================================================================
    # waitForStretchPoint
    #============================================================================
    def waitForStretchPoint(self):
        self.step = 1
        self.plugIn.setLastPoint(self.basePt)
        # imposto il map tool
        self.getPointMapTool().basePt = self.basePt
        self.getPointMapTool().setMode(
            Qad_stretch_maptool_ModeEnum.BASE_PT_KNOWN_ASK_FOR_MOVE_PT)

        keyWords = QadMsg.translate("Command_GRIP", "Base point") + "/" + \
                   QadMsg.translate("Command_GRIP", "Copy") + "/" + \
                   QadMsg.translate("Command_GRIP", "Undo") + "/" + \
                   QadMsg.translate("Command_GRIP", "eXit")

        prompt = QadMsg.translate(
            "Command_GRIPSTRETCH",
            "Specify stretch point or [{0}]: ").format(keyWords)

        englishKeyWords = "Base point" + "/" + "Copy" + "/" + "Undo" + "/" + "eXit"
        keyWords += "_" + englishKeyWords
        # si appresta ad attendere un punto o enter o una parola chiave
        # msg, inputType, default, keyWords, nessun controllo
        self.waitFor(prompt, QadInputTypeEnum.POINT2D | QadInputTypeEnum.KEYWORDS, \
                     None, \
                     keyWords, QadInputModeEnum.NONE)

    #============================================================================
    # waitForBasePt
    #============================================================================
    def waitForBasePt(self):
        self.step = 2
        # imposto il map tool
        self.getPointMapTool().setMode(
            Qad_stretch_maptool_ModeEnum.NONE_KNOWN_ASK_FOR_BASE_PT)

        # si appresta ad attendere un punto
        self.waitForPoint(
            QadMsg.translate("Command_GRIPSTRETCH", "Specify base point: "))

    #============================================================================
    # run
    #============================================================================
    def run(self, msgMapTool=False, msg=None):
        if self.plugIn.canvas.mapSettings().destinationCrs().isGeographic():
            self.showMsg(
                QadMsg.translate(
                    "QAD",
                    "\nThe coordinate reference system of the project must be a projected coordinate system.\n"
                ))
            return True  # fine comando

        #=========================================================================
        # RICHIESTA SELEZIONE OGGETTI
        if self.step == 0:  # inizio del comando
            if len(self.selectedEntityGripPoints
                   ) == 0:  # non ci sono oggetti da stirare
                return True
            self.showMsg(
                QadMsg.translate("Command_GRIPSTRETCH", "\n** STRETCH **\n"))
            # si appresta ad attendere un punto di stiramento
            self.waitForStretchPoint()
            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA DI UN PUNTO DI STIRAMENTO
        elif self.step == 1:
            ctrlKey = False
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        value = None
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False
                else:
                    value = self.getPointMapTool().point

                ctrlKey = self.getPointMapTool().ctrlKey
            else:  # il punto arriva come parametro della funzione
                value = msg

            if type(value) == unicode:
                if value == QadMsg.translate(
                        "Command_GRIP", "Base point") or value == "Base point":
                    # si appresta ad attendere il punto base
                    self.waitForBasePt()
                elif value == QadMsg.translate("Command_GRIP",
                                               "Copy") or value == "Copy":
                    # Copia entità lasciando inalterate le originali
                    self.copyEntities = True
                    # si appresta ad attendere un punto di stiramento
                    self.waitForStretchPoint()
                elif value == QadMsg.translate("Command_GRIP",
                                               "Undo") or value == "Undo":
                    if self.nOperationsToUndo > 0:
                        self.nOperationsToUndo = self.nOperationsToUndo - 1
                        self.plugIn.undoEditCommand()
                    else:
                        self.showMsg(
                            QadMsg.translate(
                                "QAD", "\nThe command has been canceled."))
                    # si appresta ad attendere un punto di stiramento
                    self.waitForStretchPoint()
                elif value == QadMsg.translate("Command_GRIP",
                                               "eXit") or value == "eXit":
                    return True  # fine comando
            elif type(value) == QgsPointXY:  # se é stato selezionato un punto
                if ctrlKey:
                    self.copyEntities = True

                self.stretchFeatures(value)

                if self.copyEntities == False:
                    return True
                # si appresta ad attendere un punto di stiramento
                self.waitForStretchPoint()

            else:
                if self.copyEntities == False:
                    self.skipToNextGripCommand = True
                return True  # fine comando

            return False

        #=========================================================================
        # RISPOSTA ALLA RICHIESTA PUNTO BASE (da step = 1)
        elif self.step == 2:  # dopo aver atteso un punto
            if msgMapTool == True:  # il punto arriva da una selezione grafica
                # la condizione seguente si verifica se durante la selezione di un punto
                # é stato attivato un altro plugin che ha disattivato Qad
                # quindi stato riattivato il comando che torna qui senza che il maptool
                # abbia selezionato un punto
                if self.getPointMapTool(
                ).point is None:  # il maptool é stato attivato senza un punto
                    if self.getPointMapTool(
                    ).rightButton == True:  # se usato il tasto destro del mouse
                        pass  # opzione di default "spostamento"
                    else:
                        self.setMapTool(
                            self.getPointMapTool())  # riattivo il maptool
                        return False

                value = self.getPointMapTool().point
            else:  # il punto arriva come parametro della funzione
                value = msg

            if type(value) == QgsPointXY:  # se é stato inserito il punto base
                self.basePt.set(value.x(), value.y())
                # imposto il map tool
                self.getPointMapTool().basePt = self.basePt

            # si appresta ad attendere un punto di stiramento
            self.waitForStretchPoint()

            return False
def processLine(source, sink, feedback, discardVertices, maxseglen):
    layercrs = source.sourceCrs()
    if layercrs != epsg4326:
        transto4326 = QgsCoordinateTransform(layercrs, epsg4326,
                                             QgsProject.instance())
        transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs,
                                               QgsProject.instance())

    total = 100.0 / source.featureCount() if source.featureCount() else 0
    iterator = source.getFeatures()
    num_bad = 0
    for cnt, feature in enumerate(iterator):
        if feedback.isCanceled():
            break
        try:
            if feature.geometry().isMultipart():
                seg = feature.geometry().asMultiPolyline()
            else:
                seg = [feature.geometry().asPolyline()]
            numseg = len(seg)
            if numseg < 1 or len(seg[0]) < 2:
                continue
            # Create a new Line Feature
            fline = QgsFeature()
            # If the input is not 4326 we need to convert it to that and then back to the output CRS
            if discardVertices:
                ptStart = QgsPointXY(seg[0][0][0], seg[0][0][1])
                if layercrs != epsg4326:  # Convert to 4326
                    ptStart = transto4326.transform(ptStart)
                pts = [ptStart]
                numpoints = len(seg[numseg - 1])
                ptEnd = QgsPointXY(seg[numseg - 1][numpoints - 1][0],
                                   seg[numseg - 1][numpoints - 1][1])
                if layercrs != epsg4326:  # Convert to 4326
                    ptEnd = transto4326.transform(ptEnd)
                l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(),
                                     ptEnd.x())
                if l.s13 > maxseglen:
                    n = int(math.ceil(l.s13 / maxseglen))
                    seglen = l.s13 / n
                    for i in range(1, n):
                        s = seglen * i
                        g = l.Position(
                            s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                            | Geodesic.LONG_UNROLL)
                        pts.append(QgsPointXY(g['lon2'], g['lat2']))
                pts.append(ptEnd)

                if layercrs != epsg4326:  # Convert each point back to the output CRS
                    for x, pt in enumerate(pts):
                        pts[x] = transfrom4326.transform(pt)
                fline.setGeometry(QgsGeometry.fromPolylineXY(pts))
            else:
                if not feature.geometry().isMultipart():
                    line = seg[0]
                    numpoints = len(line)
                    ptStart = QgsPointXY(line[0][0], line[0][1])
                    if layercrs != epsg4326:  # Convert to 4326
                        ptStart = transto4326.transform(ptStart)
                    pts = [ptStart]
                    for x in range(1, numpoints):
                        ptEnd = QgsPointXY(line[x][0], line[x][1])
                        if layercrs != epsg4326:  # Convert to 4326
                            ptEnd = transto4326.transform(ptEnd)
                        l = geod.InverseLine(ptStart.y(), ptStart.x(),
                                             ptEnd.y(), ptEnd.x())
                        if l.s13 > maxseglen:
                            n = int(math.ceil(l.s13 / maxseglen))
                            seglen = l.s13 / n
                            for i in range(1, n):
                                s = seglen * i
                                g = l.Position(
                                    s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                                    | Geodesic.LONG_UNROLL)
                                pts.append(QgsPointXY(g['lon2'], g['lat2']))
                        pts.append(ptEnd)
                        ptStart = ptEnd

                    if layercrs != epsg4326:  # Convert each point back to the output CRS
                        for x, pt in enumerate(pts):
                            pts[x] = transfrom4326.transform(pt)
                    fline.setGeometry(QgsGeometry.fromPolylineXY(pts))
                else:  # MultiLineString
                    outseg = []
                    for line in seg:
                        numpoints = len(line)
                        ptStart = QgsPointXY(line[0][0], line[0][1])
                        if layercrs != epsg4326:  # Convert to 4326
                            ptStart = transto4326.transform(ptStart)
                        pts = [ptStart]
                        for x in range(1, numpoints):
                            ptEnd = QgsPointXY(line[x][0], line[x][1])
                            if layercrs != epsg4326:  # Convert to 4326
                                ptEnd = transto4326.transform(ptEnd)
                            l = geod.InverseLine(ptStart.y(), ptStart.x(),
                                                 ptEnd.y(), ptEnd.x())
                            if l.s13 > maxseglen:
                                n = int(math.ceil(l.s13 / maxseglen))
                                seglen = l.s13 / n
                                for i in range(1, n):
                                    s = seglen * i
                                    g = l.Position(
                                        s,
                                        Geodesic.LATITUDE | Geodesic.LONGITUDE
                                        | Geodesic.LONG_UNROLL)
                                    pts.append(QgsPointXY(
                                        g['lon2'], g['lat2']))
                            pts.append(ptEnd)
                            ptStart = ptEnd

                        if layercrs != epsg4326:  # Convert each point back to the output CRS
                            for x, pt in enumerate(pts):
                                pts[x] = transfrom4326.transform(pt)
                        outseg.append(pts)

                    fline.setGeometry(QgsGeometry.fromMultiPolylineXY(outseg))

            fline.setAttributes(feature.attributes())
            sink.addFeature(fline)
        except:
            num_bad += 1

        feedback.setProgress(int(cnt * total))

    return num_bad
Ejemplo n.º 36
0
    def measureLine(self, linetoMeasure, clicked_pt, geodetic_measure=True):
        """This will take a line segment and the clicked point and return the
        length up till that point for the segment in km. Inspired by shape tools and closest point plugins"""
        srcCRS = linetoMeasure.sourceCrs()  #get CRS from line
        feature = linetoMeasure.getFeature(0)  #get first feature from line
        wgs84 = KPTool.epsg4326  #define EPSG 4326
        projected_point_raw, dist_ext = self.closestPt(
            linetoMeasure, clicked_pt, True
        )  #we'll need that point later to compare with projected points of subsegments
        projected_point_on_line_raw, dist_nonext = self.closestPt(
            linetoMeasure, clicked_pt,
            False)  #get proj point on non-extended line
        if srcCRS != wgs84:
            geomTo4326 = QgsCoordinateTransform(
                srcCRS, wgs84, QgsProject.instance())  #convert if needed
        if feature.geometry().isMultipart():
            # get nodes out of data
            ptdata = [feature.geometry().asMultiPolyline()]
        else:
            feature = linetoMeasure.getFeature(
                1)  #for some reason singleline first feature is NULL
            ptdata = [[feature.geometry().asPolyline()]]
        for seg in ptdata:
            if len(seg) < 1:
                # should never happen
                self.iface.messageBar().pushMessage(
                    'Something is strange with your line file',
                    level=Qgis.Critical,
                    duration=2)
                continue
            for pts in seg:
                # now we get all nodes from start to end of segment line
                numpoints = len(pts)
                if numpoints < 2:
                    # should never happen
                    self.iface.messageBar().pushMessage(
                        'Something is strange with your line file',
                        level=Qgis.Critical,
                        duration=2)
                    continue
                ptStart_raw = QgsPointXY(pts[0].x(),
                                         pts[0].y())  #get initial point coords
                # Calculate the total distance of this line segment
                distance = 0.0
                for x in range(1, numpoints):
                    # from point one (since we preextend segment) start measuring distance cumulative
                    ptEnd_raw = QgsPointXY(
                        pts[x].x(), pts[x].y())  # coordinates of next point
                    P1 = QgsPoint(ptStart_raw.x(), ptStart_raw.y())
                    P2 = QgsPoint(ptEnd_raw.x(), ptEnd_raw.y())
                    seg_geom = QgsGeometry.fromPolyline(
                        (P1, P2))  # generate geometry for current subsegment
                    _, mindistpt, _, _ = seg_geom.closestSegmentWithContext(
                        projected_point_raw)
                    TestPoint = QgsPointXY(
                        mindistpt[0],
                        mindistpt[1])  #create projected point on subsegment
                    ptStart = ptStart_raw
                    ptEnd = ptEnd_raw
                    projected_point = projected_point_raw
                    projected_point_on_line = projected_point_on_line_raw
                    if geodetic_measure == True:
                        if srcCRS != wgs84:
                            # Convert to 4326 - just to be safe when using geodetic measure
                            ptStart = geomTo4326.transform(ptStart_raw)
                            ptEnd = geomTo4326.transform(ptEnd_raw)
                            projected_point = geomTo4326.transform(
                                projected_point_raw)
                            projected_point_on_line = geomTo4326.transform(
                                projected_point_on_line_raw)
                            TestPoint = geomTo4326.transform(TestPoint)
                        d_to_click = KPTool.geod.Inverse(
                            ptStart.y(), ptStart.x(),
                            projected_point_on_line.y(),
                            projected_point_on_line.x())['s12']
                        len_subsegment = KPTool.geod.Inverse(
                            ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()
                        )['s12']  # geodetic distance between begin and end of subsegment
                        test_distance = KPTool.geod.Inverse(
                            projected_point_on_line.y(),
                            projected_point_on_line.x(), TestPoint.y(),
                            TestPoint.x()
                        )['s12']  # check distance between two on-subsegment-points

                    else:
                        d_to_click = QgsDistanceArea().measureLine(
                            ptStart, projected_point_on_line)
                        len_subsegment = QgsDistanceArea().measureLine(
                            ptStart, ptEnd
                        )  # cartesian distance between begin and end point
                        test_distance = QgsDistanceArea().measureLine(
                            projected_point_on_line, TestPoint
                        )  # check distance between two on-subsegment-points
                    round_test_distance = round(
                        test_distance, 6
                    )  # round so we can get zero, otherwise it will be a small fraction
                    if round_test_distance != 0:
                        # not yet the last segment to be measured
                        distance += len_subsegment  #add to comulative
                    else:
                        # this is the segment, where we have to stop measuring
                        distance += d_to_click
                        break  # break loop and give distance so far
                    ptStart_raw = ptEnd_raw  # make ready for next pair of points
                if QgsPoint(projected_point) != QgsPoint(
                        projected_point_on_line):
                    # if our point on the extended line is different, the projected point is before start or after end of line layer
                    if geodetic_measure == True:
                        extra_dist = KPTool.geod.Inverse(
                            projected_point.y(), projected_point.x(),
                            projected_point_on_line.y(),
                            projected_point_on_line.x())['s12']
                    else:
                        extra_dist = QgsDistanceArea().measureLine(
                            projected_point, projected_point_on_line)
                    if round(distance, 6) == 0:
                        # we are at the start of the line layer, the for loops hasn't done anything, so negative distance to starting node
                        distance = -extra_dist
                    else:
                        # we are at the end of the line segment, so need to add more distance
                        distance = distance + extra_dist
                out = distance / 1000  # Distance converted to KM
        return out
def run(sources_layer_path, receivers_layer_path, emission_pts_layer_path, research_ray):
    sources_layer = QgsVectorLayer(sources_layer_path, "input layer", "ogr")
    receivers_layer = QgsVectorLayer(receivers_layer_path, "output layer", "ogr")

    sources_feat_all = sources_layer.dataProvider().getFeatures()

    receivers_feat_all_dict = {}
    receivers_feat_all = receivers_layer.dataProvider().getFeatures()
    receivers_spIndex = QgsSpatialIndex()
    for receivers_feat in receivers_feat_all:
        receivers_spIndex.insertFeature(receivers_feat)
        receivers_feat_all_dict[receivers_feat.id()] = receivers_feat

    emission_pts_fields = QgsFields()
    emission_pts_fields.append(QgsField("id_emi", QVariant.Int))
    emission_pts_fields.append(QgsField("id_emi_source", QVariant.Int))
    emission_pts_fields.append(QgsField("id_source", QVariant.Int))
    emission_pts_fields.append(QgsField("d_rTOe", QVariant.Double, len=10, prec=2))
    # update for QGIS 3 converting VectorWriter to QgsVectorFileWriter
    # emission_pts_writer = VectorWriter(emission_pts_layer_path, None, emission_pts_fields, 0, sources_layer.crs())

    emission_pts_writer = QgsVectorFileWriter(emission_pts_layer_path, "System",
                                              emission_pts_fields, QgsWkbTypes.Point, sources_layer.crs(),
                                              "ESRI Shapefile")

    # initializes ray and emission point id
    emission_pt_id = 0

    for sources_feat in sources_feat_all:

        # researches the receiver points in a rectangle created by the research_ray
        # creates the search rectangle
        rect = QgsRectangle()
        rect.setXMinimum(sources_feat.geometry().boundingBox().xMinimum() - research_ray)
        rect.setXMaximum(sources_feat.geometry().boundingBox().xMaximum() + research_ray)
        rect.setYMinimum(sources_feat.geometry().boundingBox().yMinimum() - research_ray)
        rect.setYMaximum(sources_feat.geometry().boundingBox().yMaximum() + research_ray)

        receiver_pts_request = receivers_spIndex.intersects(rect)

        distance_min = []
        for receiver_pts_id in receiver_pts_request:
            receiver_pts_feat = receivers_feat_all_dict[receiver_pts_id]
            result = sources_feat.geometry().closestSegmentWithContext(receiver_pts_feat.geometry().asPoint())
            distance_min_tmp = sqrt(result[0])

            if distance_min_tmp <= research_ray:
                distance_min.append(distance_min_tmp)

        # defines segment max length
        if len(distance_min) >= 1:
            segment_max = min(distance_min) / 2
            if segment_max < 2:
                segment_max = 2
        else:
            continue

        # splits the sources line in emission points at a fix distance (minimum distance/2) and create the emission point layer
        # gets vertex
        sources_geom = sources_feat.geometry()
        if sources_geom.isMultipart():
            sources_geom.convertToSingleType()
        sources_feat_vertex_pt_all = sources_geom.asPolyline()

        emission_pt_id_road = 0

        for i in range(0, len(sources_feat_vertex_pt_all)):

            pt1 = QgsPointXY(sources_feat_vertex_pt_all[i])

            add_point_to_layer(emission_pts_writer, pt1,
                               [emission_pt_id, emission_pt_id_road, sources_feat.id(), segment_max])

            emission_pt_id = emission_pt_id + 1
            emission_pt_id_road = emission_pt_id_road + 1

            if i < len(sources_feat_vertex_pt_all) - 1:

                pt2 = QgsPoint(sources_feat_vertex_pt_all[i + 1])

                x1 = pt1.x()
                y1 = pt1.y()
                x2 = pt2.x()
                y2 = pt2.y()

                if y2 == y1:
                    dx = segment_max
                    dy = 0
                    m = 0
                elif x2 == x1:
                    dx = 0
                    dy = segment_max
                else:
                    m = (y2 - y1) / (x2 - x1)
                    dx = sqrt((segment_max ** 2) / (1 + m ** 2))
                    dy = sqrt(((segment_max ** 2) * (m ** 2)) / (1 + m ** 2))

                pt = pt1

                while compute_distance(pt, pt2) > segment_max:
                    x_temp = pt.x()
                    y_temp = pt.y()
                    if x_temp < x2:
                        if m > 0:
                            pt = QgsPointXY(x_temp + dx, y_temp + dy)
                        elif m < 0:
                            pt = QgsPointXY(x_temp + dx, y_temp - dy)
                        elif m == 0:
                            pt = QgsPointXY(x_temp + dx, y_temp)
                    elif x_temp > x2:
                        if m > 0:
                            pt = QgsPointXY(x_temp - dx, y_temp - dy)
                        elif m < 0:
                            pt = QgsPointXY(x_temp - dx, y_temp + dy)
                        elif m == 0:
                            pt = QgsPointXY(x_temp - dx, y_temp)
                    elif x_temp == x2:
                        if y2 > y_temp:
                            pt = QgsPointXY(x_temp, y_temp + dy)
                        else:
                            pt = QgsPointXY(x_temp, y_temp - dy)

                    add_point_to_layer(emission_pts_writer, pt,
                                       [emission_pt_id, emission_pt_id_road, sources_feat.id(), segment_max])

                    emission_pt_id = emission_pt_id + 1
                    emission_pt_id_road = emission_pt_id_road + 1

    del emission_pts_writer