Ejemplo n.º 1
0
    def __init__(self, iface: QgisInterface):
        """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
        """
        super().__init__()
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QgsApplication.locale()
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   '{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # processing framework
        self.provider = RAlgorithmProvider()
Ejemplo n.º 2
0
    def openHelp(self):
        locale = QgsApplication.locale()

        if locale in ['uk']:
            QDesktopServices.openUrl(QUrl(self.home))
        else:
            QDesktopServices.openUrl(QUrl(self.home))
Ejemplo n.º 3
0
    def __init__(self, iface: QgisInterface):
        """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
        """
        super().__init__()
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QgsApplication.locale()
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   '{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        self.toolbar = None
        self.layer_combo = None
        self.actions = []
        self.dock = None
        self.vertex_highlighter = VertexHighlighterManager()
        self.selection_handler = SelectionHandler(self)
        self.show_vertices_action = None
        self.show_topology_action = None
        self.show_dock_action = None
Ejemplo n.º 4
0
    def openHelp(self):
        locale = QgsApplication.locale()

        if locale in ['uk']:
            QDesktopServices.openUrl(
                QUrl('https://github.com/alexbruy/photo2shape'))
        else:
            QDesktopServices.openUrl(
                QUrl('https://github.com/alexbruy/photo2shape'))
Ejemplo n.º 5
0
    def openHelp(self):
        locale = QgsApplication.locale()

        if locale in ['uk']:
            QDesktopServices.openUrl(
                QUrl(self.home))
        else:
            QDesktopServices.openUrl(
                QUrl(self.home))
Ejemplo n.º 6
0
    def openHelp(self):
        locale = QgsApplication.locale()

        if locale in ['uk']:
            QDesktopServices.openUrl(
                QUrl('https://github.com/alexbruy/photo2shape'))
        else:
            QDesktopServices.openUrl(
                QUrl('https://github.com/alexbruy/photo2shape'))
Ejemplo n.º 7
0
    def __init__(self, iface):
        self.iface = iface

        locale = QgsApplication.locale()
        qmPath = "{}/i18n/photo2shape_{}.qm".format(pluginPath, locale)

        if os.path.exists(qmPath):
            self.translator = QTranslator()
            self.translator.load(qmPath)
            QCoreApplication.installTranslator(self.translator)
Ejemplo n.º 8
0
    def __init__(self, iface):
        self.iface = iface

        locale = QgsApplication.locale()
        qmPath = "{}/i18n/statist_{}.qm".format(pluginPath, locale)

        if os.path.exists(qmPath):
            self.translator = QTranslator()
            self.translator.load(qmPath)
            QCoreApplication.installTranslator(self.translator)
    def __init__(self, iface):
        self.iface = iface

        locale = QgsApplication.locale()
        qmPath = "{}/i18n/rastertransparency_{}.qm".format(pluginPath, locale)

        if os.path.exists(qmPath):
            self.translator = QTranslator()
            self.translator.load(qmPath)
            QCoreApplication.installTranslator(self.translator)

        self.factory = TransparencyPanelFactory()
Ejemplo n.º 10
0
    def __init__(self, iface):
        super(gtoPlugin, self).__init__()
        self.setObjectName(plugin_objectName)
        self.plugin_name = plugin_name
        self.plugin_dir = os.path.dirname(__file__)
        self.plugin_actions = []
        self.first_start = True
        # qgis interface
        self.iface = iface
        self.iface.mainWindow().showMinimized()
        # app info
        self.info = gtoInfo(self)
        # debug ?
        path = os.path.join(self.plugin_dir, 'log')
        self.debug = os.path.exists(path)
        if self.debug:
            self.info.do_backup()  # new session
            start_time = str(datetime.datetime.now()).split(".")[0]
            self.info.log("GTO version {0} loaded (UTC): ".format(self.get_version()), start_time)
            self.info.log(plugin_name, "debug:", self.debug)
        # DockWidget
        self.dockwidget = QDockWidget()
        self.dockwidget.setWindowTitle(plugin_name)
        self.dockwidget.setObjectName('GTODockWidget')
        self.dockwidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget)
        self.app = gtoMain(self)
        # correct settings in statusbar:S
        try:
            wids = iface.mainWindow().statusBar().findChildren(QWidget)
            for wid in wids:
                if wid.objectName() == 'mOntheFlyProjectionStatusButton' or wid.objectName() == 'mMessageLogViewerButton':
                    if not wid.isHidden():
                        wid.setMaximumHeight(16777215)
                        wid.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
        except:
            pass
        # initialize locale
        locale = QgsApplication.locale()
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'plugin_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '3.0.0':
                QCoreApplication.installTranslator(self.translator)
        # run
        self.run()
Ejemplo n.º 11
0
    return LDMPPlugin(iface)

# Function to get a temporary filename that handles closing the file created by 
# NamedTemporaryFile - necessary when the file is for usage in another process 
# (i.e. GDAL)
def GetTempFilename(suffix):
    f = NamedTemporaryFile(suffix=suffix, delete=False)
    f.close()
    return f.name

# initialize translation
i18n_dir = os.path.join(plugin_dir, 'i18n')
log(u'Starting trends.earth version {} (rev: {}, released {}).'.format(__version__, __revision__, __release_date__))

translator = QTranslator()
locale = QLocale(QgsApplication.locale())
log('Trying to load locale {} from {}.'.format(locale.name(), i18n_dir))
translator.load(locale, 'LDMP', prefix='.', directory=i18n_dir, suffix='.qm')
ret = QCoreApplication.installTranslator(translator)
if ret:
    log("Translator installed for {}.".format(locale.name()))
else:
    log("FAILED while trying to install translator for {}.".format(locale.name()))

# Ensure that the ext-libs, and binaries folder (if available) are near the 
# front of the path (important on Linux)
ext_libs_path = os.path.join(plugin_dir, 'ext-libs')
binaries_folder = QSettings().value("LDMP/binaries_folder", None)
sys.path, remainder = sys.path[:1], sys.path[1:]
site.addsitedir(ext_libs_path)
if binaries_folder:
Ejemplo n.º 12
0
class MosaicRaster(QgsProcessingAlgorithm):

    LOC = QgsApplication.locale()

    def translate(self, string):
        return QCoreApplication.translate('Processing', string)

    def tr(self, *string):
        # Traduzir para o portugês: arg[0] - english (translate), arg[1] - português
        if self.LOC == 'pt':
            if len(string) == 2:
                return string[1]
            else:
                return self.translate(string[0])
        else:
            return self.translate(string[0])

    def createInstance(self):
        return MosaicRaster()

    def name(self):
        return 'mosaicraster'

    def displayName(self):
        return self.tr('Mosaic Raster', 'Mosaicar Raster')

    def group(self):
        return self.tr('LF Raster')

    def groupId(self):
        return 'lf_raster'

    def tags(self):
        return self.tr(
            'mosaic,merge,raster,combine,mosaik,mosaico,mesclar').split(',')

    def shortHelpString(self):
        txt_en = 'Creates raster mosaic: a combination or merge of two or more images.'
        txt_pt = 'Cria um mosaico: uma combinação ou mesclagem de duas ou mais imagens.'
        dic_BW = {
            'face':
            'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMwAADDMBUlqVhwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIUSURBVEiJvZa9quJAGIbfMfEYTUSUQAqLYOnehJobsBTlHLbRQtKKlY3lObegxRa7pRdg7y0saiuoKAErMZif2Squ2eTEOCfsC1Pk+3uYb2YyQwCg1+u9UUqHAKoAOCQrB8ASwPt0Ov1JdF3/bprmj4QhoXp5eXnjTdMcUEr/Bw+WZQ15Smn1q4UIIZBlGaIoghBys2+3W1yv19u367rfeAAc6ww5jkOz2USj0YAoigH/aDTCbrfzpfCUUrAC2+02NE2LjPm3NjNQkiTU6/WHsFAgi1RVRSqVCthd171BwmozA/P5fMA2n88xm81g2zYAwHGccCAL9H43elosFrhcLpE5zMCwHMdxImtRSp8DptNpZDIZAIAgCAG/IAi+42Ga5q29nkin06FxgZqmodvtxooFgPF4jPV67bM9NcNnW384HJI7h49kWRZOp9PXgOfzGfv9HgCQy+VQKBR8fsMwYFkWAOB4PMJ13UAN0mq1Yq/hfVytVoOu6z7/YDDAZrP5Wzzk6CR6LOLEJLqGcWrxYX2OW5wJmOQOjQX0ApOERgIrlQoTUJblgK1cLodeWZ4IIeDDngZx5P1T75XNZiFJUmQeTyl1wPAW/awrD7rlpDiOW3qL/cz4DBY1CCG/U4qifDw7O1YpivJBAGAymbwahjG0bbtKKeXjJJdKJaiq6rOtVqvAjU8IsTmOWxaLxfd+v//rD1H2cZ8dKhk8AAAAAElFTkSuQmCC',
            'github':
            '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',
            'instagram':
            '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',
            'lattes':
            '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',
            'linkedin':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6QAADOkBmiiHWwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJRSURBVEiJ7ZU/aCJREMa/t7soa5LOoGihktZOQSxSWykBIYVaWZ6NVa6wSZ/WI5BGziJypA1CQBLsgkKKgBjFQkEtkouJ+AdxdeeKnHvurXdcXK84uK/amfl4v9m3s++xbrdrury8PGm324dEZMYGxRj7arPZvoRCoSPh/Pz8pFqtftgkYEnmTqeT6PV6xDscjs9EZPpLIADAy8uLS5jP52bGGACA4ziEw2H4/X5IkoSrqytcX1/rBhHRrrCcCAaDCAQCShyJRPD6+oq7uzvdMG458Hg8GoPX69UN0YDG47HGsCqnG5TP50FESjyZTFAoFDYCYvF4nBbDAAB7e3vw+XyQJAnFYhGPj4+6IUQE4eekJEm4vb0F8DaFC1mtVphMb3+BLMtotVogItjtdjgcDhiNRvT7fTw8PKzcbhXI5XIhlUqpOkkkEpjP5zg+PoYg/LCfnp5if38fbrdbteB0OsXFxQVubm5+DTIYDKoiYww8z4OIwPO8qhaLxbCzs6Pp3GAwIBqN4vn5Gff390qe0zj/UKsgywqFQqpY843eo1KphHK5DLPZjIODAxiNRqXmdDqxvb2N4XCoD1Sv13F2dqbEs9kM0WhU5bFYLApo7a1bTOZClUpF49na2lKe1wb1ej1VvGqklwdobdB79R/0j4FkWdYYiEh1dawrAcATgF0AaDabyOVyyuE5HA4xmUwAAJlMRjl2ZFlGo9FQLTQajZDNZiGKouKp1WqL8hNLJpPpwWCQ0N3ybySKYpqLxWJHjLFP399s03riOC4dCAQ+fgMeouMzfwx22gAAAABJRU5ErkJggg==',
            'RG':
            'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMfwAADH8BdgxfmQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAM2SURBVEiJtZdLSytJFMd/1dXB+OiQRJEQBEnbPgISJGIQcePKjyBZzoP5AH6OuR9gFjPMznwC1650EbKSy00WPhJ8EMlDtG1M0p2axZCA1871cZM/NDRFnfqdqjrnVJUAODg4+E0IsQ8sA5LRyANKQogve3t7/4hcLvcH8NeIYL5SSv2uAftDHhSl1A/7CCH2dWBxWNBoNIppmgSDQarVKufn53ie59d1RQO0YUBjsRg7OztomkatVmNpaYnNzU2EEH7dNX0YUKUUpmlyd3dHPp9HKcXV1RXT09MIIXyXfihgKSWGYVCpVPoQ27axbXugjS/4HcHh29btdlFKsbq6SiwWA6BQKHB/f/82WEpJKpUiEAi8aA8EAiilaLVaNBoNrq+vabfbr+BCCG5vb3l8fCSTyaDr/ov6KrA8z6NcLtNqtUgkEszPz+M4Ds1mE9u2MQyDjY0Ndnd3mZmZ6ds4jkMoFAKg0Wjw8PCAEIJOp+ML9nWnZ5hIJJBSUiqV+gMIIbAsi3Q6zdbWFoeHh7iuS7lcZn19HcdxsG0by7Ko1+sD93lgcCml6Ha7SClftZ+dnbG4uIhhGMzOznJzc0OlUkHTNBYWFtB1nXq9zunp6aA8/lxUe57H09MThmEwPj7ed+ji4oLLy0uEEHieNyiHPw+WUjI5OYlS6lXE9krmj6DwyaplmiaGYVCtVqnX658Z4u0ZCyGIx+O0223GxsaYm5sjHo9Tq9U4OTn5FPRdYCkly8vLCCEIh8MopSgUCpTLZVzXHR3YdV2Ojo7wPI/t7W1isRjBYPCnoPDOPVZK4XlePz2SySThcHj04J4ajQbFYhEpJZlM5lWOjwwMUCwWaTabRCIR0un08MG9gt/778l1XfL5PJ1OB9M0WVlZGR54amoKy7IIBoPouo5lWYRCof7SNptN8vk83W6XVCrF2toaExMTHwKLXC734vCVUpJOp9F1/cW5rGkapVLpRcGIRqMkk0kikQiO43B8fMzz8/O7wd73Mx90EfArg0opNE178/LwnboaUPID+H2DnPkgFOCbppT68lGrn5VS6k8tm83+DfwKfOX/Z8ao5AFflVK/ZLPZf/8DudZq3wvXLmgAAAAASUVORK5CYII=',
            'tweeter':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMQAADDEBLaRWDgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAK5SURBVEiJpZU7T+NAFEZP4kAePBwZ8TA0EUIRKKIgQhSImnZ/wDYr7Q/barddbRu2QVAQhChAgBQqQClwFEIcx8IYfLdKNk5sJ4GRRh6PPN+Z+90749jZ2VnGsqyfwD6QEREARISocdRcT7dF5AD4lmi327+AL0GCn4QgIhkR+SIiPxIish8k3i/yQVCn7ydEJD0sgjDBMUCZxCg2hQmqqsri4iKKotBoNKhWq741vesSvULpdJrJyUkajcZQy7a2tlheXqa3FQoFzs/PmZubQ9M0Dg8Pu2vivQJLS0sUi0Wy2WykFfl8fgACkEql2N3dJZfLcXFxgaqq/0G9O04mk8RiMba3t8nlcqHRrK6uDkB628TEBDs7O5imORgRgGVZ3Y/z+Tx7e3ssLCz4QMlkkng8HglqNpscHBzw9vbmz1EHVK1WWVtb6wpNTU1RLBZ5f3/HMAzq9TqO40RCAGq1Gq7r+jboK4aZmRmurq7Y3Nz0LVQUBV3X0XV9KATAcZwBy7vlLSJomjbU/1Ha8/PzAMhXdQ8PD5+GABiGEQ1qt9tcXl5+CvL09MTLy8ugdf0T9/f32LbNxsYGs7OzY4Our68DD/oASEQoFApMT0+PDTFNk7u7u0BQPAhULpdxXXds0PHxMZ7nDUBCQa1Wi1KpRKVSwTTNkSAnJyfU6/XQWz4QJCI4jsPj4yOKogyFnJ6ecnt7G3nTd3OUSCSYn59HURRUVUXXdTKZTCTAtm2Ojo585dwP6rx3Qa+vrzSbTdbX11lZWSEWi4UCLMvi5uaGSqWC53mh/67esa/qTNOkXC4DoGka2WyWVCoFgOu6tFotDMPAtu3QXISNA8tbRKjVaoEnPCwPYZZ1nnHP8+ww2Ed7P0RE7LjneX/7ff6ocES0pbiIfBWRP57n2aMAwywKGdsi8ltRlO//AFPkniYXwGRMAAAAAElFTkSuQmCC',
            'udemy':
            'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAdCAYAAAC9pNwMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMOgAADDoBpJd/BgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAYHSURBVEiJlZdbTFNbGsd/e/cCFtpgxX0sjHgJoepBJcqMl4gGjoYTb5PRYZxEMmq8cI5Oosw8ODG+iJpMHGN0Hsz4QHwwmRgLHpJjqkDqQZ+QWEWisdEIRZoKtAKFFuxt73nQ3aG05pT/2/rW5fd93/rW2msL/Lo0gO727dtlc+fO/cFoNK41mUzzjEajMR6Py6Ojo4FgMPjR4/E8fv369fULFy70A1EgnsHaaSVarVbj3bt3L/b3948pGSgWiylPnz71nj9//kcgBxBnC9Vfv379QG9vb0bAmZJlWXn06NFATU1NFaDPFDrn5s2b/56ampIVRVGCwaDS2NioNDQ0KG1tbbNy4MOHD7H6+vp/AHNmQjQzoXfu3Gnav39/rVarFQCam5vp6ekhHA7jdrtZs2YNBoMhowhyc3PFLVu2fOf3++c6nc5fgFg6sP7atWv/OnjwYK0gCAD4/X5aWlqQZRkARVEoLS3FbDZnmj20Wq2wcePG375582bM5XI5+VJ06uaLdXV1fzh27NhfRfH/9dDe3k40Gk20BUFAp9N9FdLd3c2VK1e4desWk5OTCbvZbBYaGhr+aTQaf6cyVYqhpqbmWnZ2tqAO7u3t5fnz5zO9Z968eWmhIyMj2Gw2vF4vL168wOFwJPWvXLlSe/z48f8ABhWsPXXq1Mmqqqpv1EHhcBibzZZIsSpJksjNzU0LfvjwIeFwONF2uVwoipI05uTJkyskSfo9oBUB/fr16+vUfVUUhaamJnw+X8riJSUlaaHDw8M4nc4kWzAYTNomAIvFIuzYsePvgF7MysqyVFRUFKqdbW1tdHd3pwVYrda09pm1ACDLckrGAKqqqr4FFmh37979l4KCAlFRFFpbW3E4HCkpAtDr9SxatCjF7vF46OnpSbGnWwOgsrJSB3ynlSTp28nJycR5/dqEoqKitBXd2tpKPJ56LSuKkjZii8UiZGVlWUWz2fwbv9+fBJ1+pFQtXbo0xfbu3TtcLhcA8+fPT5ony3LaIERRZMmSJUtEILeoqIjt27djtVrZtm0bmzdvTpmwePHilIju3buHoigIgsCmTZuS+mOxGJFIJGUOQFZWlln0+/1+gMrKSo4ePUp1dXVKRQuCQEFBQZKts7OTgYEBABYuXMiKFStQT4YKmX6JuN1uLl26xPj4OH19fV7t8PDwwMzoPB5PUttoNJKTk5Noj42Ncf/+/YRTW7duJScnB51Ol7Tfb9++JT8/n66uLux2O9FolIGBAWV8fNyjffz4cWcoFKpVFw6FQoRCoSSwwWBg+jlvbm5ORFNcXMzy5csRBAFJknj//n1int1ux+FwMDU1BYDJZKKvr08B3oo+n8/e3t6eOISRSCSlGqcXSUdHR6KgNBoNO3fuTDhVXl6eNE+W5QRUEASqq6ux2+2fAIcIDLW3t7vUwRqNJmmvAD5+/Mjg4CBOp5MHDx4kHKmoqKCwMHH3sG7dOpYtW8ZMZWdns2fPHsrKyrDb7Z3AkABo9Xr93q6urv+uXr1alGWZc+fOpaR7eqrhc0GdOHECrVabkp1Xr17hdruRZZkFCxZQWlqKwWDg6tWr0fr6+v3ATxpAjsfj/YFA4Pu9e/cWCILA0NAQXq83xXNVeXl5HDlyJKngpjsoSRIlJSVYrVYKCwvR6XSMjo5SW1vbMTExcQH4pD4EYi9fvnxZWFj457Vr1+osFgvPnj1LuX/h8xfq8OHD5Ofnf9WxmZJlmQMHDow9efLkCOAGFBWs8Hmv+8vLy3etWrVKLC4uxuv1MjExgSAImEwmNmzYwL59+8jLy8sYCnD27NlPN27cqAMcTHv+TNcci8VypqWlJaK+FgOBgDIyMqJEo9FZvzZjsZhy5syZSeBvpHnwpcA1Gs2fLl68OBaJRGYNU+Xz+eRdu3YNA3/MBKpKD5SVlpb+3NjYGI7FYhkDg8Ggcvny5U+SJDUDZcziXa1K5PPfQGVZWdnPp0+fnujo6IgHAoEU2ODgoGyz2WKHDh0as1gsTcAWfuVPQvhaxzRpAB1gBNZ9iUISRfGbLzfcIDAMvACeABNk8O/0PwJCxMb99V7LAAAAAElFTkSuQmCC',
            'youtube':
            'iVBORw0KGgoAAAANSUhEUgAAACEAAAAaCAYAAAA5WTUBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMNwAADDcBracSlQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKkSURBVEiJxZcxSBthFIC/918uGhxMsyptcGjoIgGHW9wSihQKGZrNthHsIC7S1TqWgkRwcZV2lKo0FouixaEuEQ4khIIdrClkEoIZgiae93fQgFgll5jqt93x3v++e3f/3TvhGgYHBx/UarUUEAMeA48A/3WxDagBf4A9EdmsVqsfd3d3j64GyeWDZDJpFAqFKWAC6G6haCPKIjKTzWbfA+4/Ev39/V2dnZ3LwNP/UPwqaycnJy9yuVwFQF2cVIFA4PMdCQAMBQKBL8lk0gAwACzLmtJav7kjgTp95XK5ViwWf0g0Gg36/f7fQLBRltYaEWkU5hkRKTmO06dM03ztRaAuEQ6H0Vq3RUJrHTIM46XR29v7jvNt2BClFEtLS0QiEfb39ymVSrfujIg4Cog0mxiPx1lYWGBycpKenh5c122cdANa64gCHra6QCKRYHl5mfHxcYLBII7jtLJMWAEdrUrUSaVSrK6uMjo6SigU4uzsrJn0DtU4xhs+n4+xsTFWVlYYHh6mq6vL821qm0Qd0zSZmJggk8kQi8XuR6LO1tYW+XzeUzd87S6ey+WYnZ0ln88jIijV+DrbJnF4eMj09DTb29u4rtvU+8MHVLnFDjk+PiadTrO+vs7p6WkrS1R9QAGPb8zLaK2Zm5tjcXGRSqXiqe03sO8D9pqVyGQyzM/PUywWMQzjNgIAv3xa600Ree4l2nEcRkZGODg4QCmFYRi3KQ6AiGzIwMBAt2EYB9znp9y27bLWesZjUtsELkjbtl2ur6osy/qqtX7W7io3ISIb2Wx2CHDrT5TrOE4SWLsjgW+O4yS4mLiv9ldZljWptX6Lx2mrSY6A9M7OzgeuG/kvE41Gg6ZpvhKROPCE85mj5Z8fEfnpuu5313U/2bZdvhr0F9Fo9phaoDu9AAAAAElFTkSuQmCC'
        }
        footer = '''<div align="right">
                      <p align="right"><b>''' + self.tr(
            'Author: Leandro Franca', 'Autor: Leandro França'
        ) + '''</b></p>
                      <div align="right">
                      <a target="_blank" rel="noopener noreferrer" href="https://www.udemy.com/user/leandro-luiz-silva-de-franca/"><img title="Udemy" src="data:image/png;base64,''' + dic_BW[
            'udemy'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/GEOCAPT/"><img title="Facebook" src="data:image/png;base64,''' + dic_BW[
                'face'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/channel/UCLrewDGciytcBG9r0OxTW2w"><img title="Youtube" src="data:image/png;base64,''' + dic_BW[
                    'youtube'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.researchgate.net/profile/Leandro_Franca2"><img title="ResearchGate" src="data:image/png;base64,''' + dic_BW[
                        'RG'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/LEOXINGU"><img title="GitHub" src="data:image/png;base64,''' + dic_BW[
                            'github'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.linkedin.com/in/leandro-fran%C3%A7a-93093714b/"><img title="Linkedin" src="data:image/png;base64,''' + dic_BW[
                                'linkedin'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="http://lattes.cnpq.br/8559852745183879"><img title="Lattes" src="data:image/png;base64,''' + dic_BW[
                                    'lattes'] + '''"></a>
                      </div>
                    </div>'''
        if self.LOC == 'pt':
            return txt_pt + footer
        else:
            return self.tr(txt_en) + footer

    RASTERLIST = 'RASTERLIST'
    CHANGERESOLUTION = 'CHANGERESOLUTION'
    RESOLUTION = 'RESOLUTION'
    OVERLAP = 'OVERLAP'
    NULLVALUE = 'NULLVALUE'
    RESAMPLING = 'RESAMPLING'
    CLIP = 'CLIP'
    FRAME = 'FRAME'
    MOSAIC = 'MOSAIC'
    OPEN = 'OPEN'

    def initAlgorithm(self, config=None):
        # INPUT
        self.addParameter(
            QgsProcessingParameterMultipleLayers(
                self.RASTERLIST,
                self.tr('Raster List', 'Lista de Rasters'),
                layerType=QgsProcessing.TypeRaster))

        self.addParameter(
            QgsProcessingParameterBoolean(self.CHANGERESOLUTION,
                                          self.tr('Change resolution',
                                                  'Alterar resolução'),
                                          defaultValue=False))

        self.addParameter(
            QgsProcessingParameterNumber(
                self.RESOLUTION,
                self.tr('New Resolution (meters)',
                        'Nova resolução espacial (metros)'),
                type=1,  #Double = 1 and Integer = 0
                defaultValue=100,
                optional=True))

        sobrep = [
            self.tr('First (faster)', 'Primeiro (mais rápido)'),
            self.tr('Average', 'Média'),
            self.tr('Median', 'Mediana'),
            self.tr('Maximum', 'Máximo'),
            self.tr('Minimum', 'Mínimo')
        ]

        self.addParameter(
            QgsProcessingParameterEnum(self.OVERLAP,
                                       self.tr('Ovelap', 'Sobreposição'),
                                       options=sobrep,
                                       defaultValue=0))

        interp = [
            self.tr('Nearest neighbor', 'Vizinho mais próximo'),
            self.tr('Bilinear'),
            self.tr('Bicubic', 'Bicúbica')
        ]

        self.addParameter(
            QgsProcessingParameterEnum(self.RESAMPLING,
                                       self.tr('Interpolation',
                                               'Interpolação'),
                                       options=interp,
                                       defaultValue=0))

        self.addParameter(
            QgsProcessingParameterNumber(
                self.NULLVALUE,
                self.tr('Null value', 'Valor nulo'),
                type=0,  #Double = 1 and Integer = 0
                defaultValue=0))

        self.addParameter(
            QgsProcessingParameterBoolean(self.CLIP,
                                          self.tr('Clip by frame',
                                                  'Cortar pela moldura'),
                                          defaultValue=False))

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.FRAME,
                self.tr('Frame', 'Moldura'), [QgsProcessing.TypeVectorPolygon],
                optional=True))

        # OUTPUT
        self.addParameter(
            QgsProcessingParameterFileDestination(self.MOSAIC,
                                                  self.tr('Mosaic', 'Mosaico'),
                                                  fileFilter='.tif'))

        self.addParameter(
            QgsProcessingParameterBoolean(self.OPEN,
                                          self.tr('Load mosaic',
                                                  'Carregar mosaico'),
                                          defaultValue=True))

    # Função de Interpolação
    def Interpolar(self, X, Y, BAND, origem, resol_X, resol_Y, metodo, nulo):
        if metodo == 'nearest':
            linha = int(round((origem[1] - Y) / resol_Y - 0.5))
            coluna = int(round((X - origem[0]) / resol_X - 0.5))
            if BAND[linha][coluna] != nulo:
                return float(BAND[linha][coluna])
            else:
                return nulo
        elif metodo == 'bilinear':
            nlin = len(BAND)
            ncol = len(BAND[0])
            I = (origem[1] - Y) / resol_Y - 0.5
            J = (X - origem[0]) / resol_X - 0.5
            di = I - floor(I)
            dj = J - floor(J)
            if I < 0:
                I = 0
            if I > nlin - 1:
                I = nlin - 1
            if J < 0:
                J = 0
            if J > ncol - 1:
                J = ncol - 1
            if (BAND[int(floor(I)):int(ceil(I)) + 1,
                     int(floor(J)):int(ceil(J)) + 1] == nulo).sum() == 0:
                Z = (1 - di) * (1 - dj) * BAND[int(floor(I))][int(
                    floor(J))] + (1 - dj) * di * BAND[int(ceil(I))][int(
                        floor(J))] + (1 - di) * dj * BAND[int(floor(I))][int(
                            ceil(J))] + di * dj * BAND[int(ceil(I))][int(
                                ceil(J))]
                return float(Z)
            else:
                return nulo
        elif metodo == 'bicubic':
            nlin = len(BAND)
            ncol = len(BAND[0])
            I = (origem[1] - Y) / resol_Y - 0.5
            J = (X - origem[0]) / resol_X - 0.5
            di = I - floor(I)
            dj = J - floor(J)
            I = int(floor(I))
            J = int(floor(J))
            if I < 2:
                I = 2
            if I > nlin - 3:
                I = nlin - 3
            if J < 2:
                J = 2
            if J > ncol - 3:
                J = ncol - 3
            if (BAND[I - 1:I + 3, J - 1:J + 3] == nulo).sum() == 0:
                MatrInv = np.mat(
                    [[-1 / 6, 0.5, -0.5, 1 / 6], [0.5, -1., 0.5, 0.],
                     [-1 / 3, -0.5, 1., -1 / 6], [0., 1., 0., 0.]]
                )  # resultado da inversa: (np.mat([[-1, 1, -1, 1], [0, 0, 0, 1], [1, 1, 1, 1], [8, 4, 2, 1]])).I #
                MAT = np.mat([[
                    BAND[I - 1, J - 1], BAND[I - 1, J], BAND[I - 1, J + 1],
                    BAND[I - 2, J + 2]
                ], [
                    BAND[I, J - 1], BAND[I, J], BAND[I, J + 1], BAND[I, J + 2]
                ],
                              [
                                  BAND[I + 1, J - 1], BAND[I + 1, J],
                                  BAND[I + 1, J + 1], BAND[I + 1, J + 2]
                              ],
                              [
                                  BAND[I + 2, J - 1], BAND[I + 2, J],
                                  BAND[I + 2, J + 1], BAND[I + 2, J + 2]
                              ]])
                coef = MatrInv * MAT.transpose()
                # Horizontal
                pi = coef[0, :] * pow(dj, 3) + coef[1, :] * pow(
                    dj, 2) + coef[2, :] * dj + coef[3, :]
                # Vertical
                coef2 = MatrInv * pi.transpose()
                pj = coef2[0] * pow(di, 3) + coef2[1] * pow(
                    di, 2) + coef2[2] * di + coef2[3]
                return float(pj)
            else:
                return nulo

    def processAlgorithm(self, parameters, context, feedback):

        # inputs
        rasters = self.parameterAsLayerList(parameters, self.RASTERLIST,
                                            context)
        if rasters is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.RASTERLIST))

        reamostragem = self.parameterAsEnum(parameters, self.RESAMPLING,
                                            context)
        reamostragem = ['nearest', 'bilinear', 'bicubic'][reamostragem]

        sobrep = self.parameterAsEnum(parameters, self.OVERLAP, context)

        muda_res = self.parameterAsBool(parameters, self.CHANGERESOLUTION,
                                        context)

        resolucao = self.parameterAsDouble(parameters, self.RESOLUTION,
                                           context)

        valor_nulo = self.parameterAsDouble(parameters, self.NULLVALUE,
                                            context)

        moldura = self.parameterAsDouble(parameters, self.CLIP, context)

        if moldura:
            vlayer = self.parameterAsVectorLayer(parameters, self.FRAME,
                                                 context)
            if vlayer is None:
                raise QgsProcessingException(
                    self.invalidSourceError(parameters, self.FRAME))

        # output

        Output = self.parameterAsFileOutput(parameters, self.MOSAIC, context)

        Carregar = self.parameterAsBool(parameters, self.OPEN, context)

        lista = []
        for raster_lyr in rasters:
            lista += [raster_lyr.dataProvider().dataSourceUri()]
        if len(lista) < 1:
            raise QgsProcessingException(
                self.tr('At least one raster must be selected!',
                        'Pelo menos um raster deve ser selecionado!'))
        if len(lista) == 1:
            sobrep = 0  # apenas um raster (sem sobreposicao)

        # Gerar geometria para cada raster
        geoms = []
        SRC = []
        n_bands = []
        GDT = []
        nulos = []
        XRES, YRES = [], []
        for item in lista:
            image = gdal.Open(item)
            SRC += [QgsCoordinateReferenceSystem(image.GetProjection())]  # wkt
            ulx, xres, xskew, uly, yskew, yres = image.GetGeoTransform()
            cols = image.RasterXSize
            rows = image.RasterYSize
            n_bands += [image.RasterCount]
            GDT += [image.GetRasterBand(1).DataType]
            nulos += [image.GetRasterBand(1).GetNoDataValue()]
            XRES += [xres]
            YRES += [yres]
            image = None  # Close image
            # Creating BBox
            coord = [[
                QgsPointXY(ulx, uly),
                QgsPointXY(ulx + cols * xres, uly),
                QgsPointXY(ulx + cols * xres, uly + rows * yres),
                QgsPointXY(ulx, uly + rows * yres),
                QgsPointXY(ulx, uly)
            ]]
            geom = QgsGeometry.fromPolygonXY(coord)
            geoms += [geom]

        ## Validar dados de entrada
        # Mesmo numero de bandas
        if not n_bands.count(n_bands[0]) == len(n_bands):
            raise QgsProcessingException(
                self.tr('The images must have the same number of bands!',
                        'As imagens devem ter o mesmo número de bandas!'))
        # Mesmo SRC
        if not SRC.count(SRC[0]) == len(SRC):
            raise QgsProcessingException(
                self.tr('The images must have the same CRS!',
                        'As imagens devem ter o mesmo SRC!'))
        # Mesmo GDT
        if not GDT.count(GDT[0]) == len(GDT):
            raise QgsProcessingException(
                self.tr('The images must have the same data type!',
                        'As imagens devem ter o tipo de dado!'))
        # Mesmo valor nulo
        if not nulos.count(nulos[0]) == len(nulos):
            raise QgsProcessingException(
                self.tr(
                    'The images must have the same definied null value!',
                    'As imagens devem ter o mesmo valor para definir pixel nulo!'
                ))

        # Dados para o raster de saída
        prj = SRC[0].toWkt()
        n_bands = n_bands[0]
        GDT = GDT[0]
        xres = np.mean(XRES)
        yres = np.mean(YRES)
        NULO = valor_nulo
        if valor_nulo == -1:
            valor_nulo = nulos[0] if nulos[0] is not None else 0

        if moldura:  # Pegar extensão X e Y da moldura
            # SRC da moldura deve ser o mesmo dos raster
            if vlayer.sourceCrs() != QgsCoordinateReferenceSystem(prj):
                raise QgsProcessingException(
                    self.tr(
                        "The frame's CRS must be iqual to the rasters' CRS!",
                        'O SRC da moldura deve ser igual ao SRC dos rasters!'))
            for feat in vlayer.getFeatures():
                moldura_geom = feat.geometry()
                break
            moldura_rect = moldura_geom.boundingBox()
            y_min = moldura_rect.yMinimum()
            y_max = moldura_rect.yMaximum()
            x_min = moldura_rect.xMinimum()
            x_max = moldura_rect.xMaximum()
        else:  # Mesclar geometrias e obter a extensão
            new_geom = QgsGeometry()
            new_geom = new_geom.unaryUnion(geoms)
            extensao = new_geom.boundingBox()
            # Coodenadas máxima e mínima da extensão
            y_min = extensao.yMinimum()
            y_max = extensao.yMaximum()
            x_min = extensao.xMinimum()
            x_max = extensao.xMaximum()

        # Transformar resolucao de metros para graus, se o SRC for Geográfico
        src_qgis = QgsCoordinateReferenceSystem(prj)
        if src_qgis.isGeographic():
            EPSG = int(src_qgis.authid().split(':')[-1])
            proj_crs = CRS.from_epsg(EPSG)
            a = proj_crs.ellipsoid.semi_major_metre
            f = 1 / proj_crs.ellipsoid.inverse_flattening
            e2 = f * (2 - f)
            N = a / np.sqrt(1 - e2 * (np.sin(
                (y_min + y_max) / 2))**2)  # Raio de curvatura 1º vertical
            M = a * (1 - e2) / (1 - e2 * (np.sin((y_min + y_max) / 2))**2)**(
                3 / 2.)  # Raio de curvatura meridiana
            R = np.sqrt(M * N)  # Raio médio de Gauss
            theta = resolucao / R
            resolucao = np.degrees(theta)  # Radianos para graus

        # Definir n_col, n_lin e resolucao
        if moldura:
            if muda_res:
                n_lin = round((y_max - y_min) / abs(resolucao))
                n_col = round((x_max - x_min) / abs(resolucao))
            else:
                n_lin = round((y_max - y_min) / abs(yres))
                n_col = round((x_max - x_min) / abs(xres))
            xres = (x_max - x_min) / n_col
            yres = -(y_max - y_min) / n_lin
        else:
            if muda_res:
                n_lin = round((y_max - y_min) / abs(resolucao))
                n_col = round((x_max - x_min) / abs(resolucao))
                xres = resolucao
                yres = -resolucao
            else:
                n_lin = round((y_max - y_min) / abs(yres))
                n_col = round((x_max - x_min) / abs(xres))
                xres = (x_max - x_min) / n_col
                yres = -(y_max - y_min) / n_lin

        feedback.pushInfo(
            self.tr('Resolution: ', 'Resolução: ') + str(n_lin) + 'x' +
            str(n_col))
        # Geotransform do Mosaico
        ulx = x_min
        uly = y_max
        xskew, yskew = 0, 0
        geotransform = [ulx, xres, xskew, uly, yskew, yres]
        origem = (ulx, uly)
        resol_X = abs(xres)
        resol_Y = abs(yres)
        # Numeração das Imagens
        valores = list(range(1, len(lista) + 1))

        # Definição de áreas de varredura
        feedback.pushInfo(
            self.tr('Defining mosaic filling areas...',
                    'Definindo áreas de preenchimento do mosaico...'))

        # Gerar combinações dos Rasters
        if sobrep != 0:
            combs = []
            feedback.pushInfo(
                self.tr('Creating combinations...', 'Gerando combinações...'))
            for k in range(1, 5):
                combs += list(combinations(valores, k))
                if feedback.isCanceled():
                    break
            # Armazenar geometrias exclusivas de cada combinação
            classes = {}
            feedback.pushInfo(
                self.tr('Indentifying combinations...',
                        'Identificando combinações...'))
            Percent = 100.0 / (len(combs))
            current = 0

            for comb in combs:
                if len(comb) == 1:
                    geom1 = geoms[comb[0] - 1]
                    lista_outras = []
                    for geom in geoms:
                        if geom1 != geom:
                            lista_outras += [geom]
                    outras = QgsGeometry()
                    outras = outras.unaryUnion(lista_outras)
                    diferença = geom1.difference(outras)
                    if not diferença.isEmpty():
                        classes[comb] = {'geom': diferença}
                elif len(comb) < len(valores):
                    intersecao = geoms[comb[0] - 1]
                    sentinela = True
                    for ind in comb[1:]:
                        geom = geoms[ind - 1]
                        if geom.intersects(intersecao):
                            intersecao = intersecao.intersection(geom)
                        else:
                            sentinela = False
                            continue
                    lista_outras = []
                    for valor in valores:
                        if valor not in comb:
                            lista_outras += [geoms[valor - 1]]
                    outras = QgsGeometry()
                    outras = outras.unaryUnion(lista_outras)
                    if sentinela:
                        diferença = intersecao.difference(outras)
                        if not diferença.isEmpty():
                            classes[comb] = {'geom': diferença}
                else:
                    intersecao = geoms[comb[0] - 1]
                    sentinela = True
                    for ind in comb[1:]:
                        geom = geoms[ind - 1]
                        if geom.intersects(intersecao):
                            intersecao = intersecao.intersection(geom)
                        else:
                            sentinela = False
                            continue
                    if sentinela:
                        classes[comb] = {'geom': intersecao}
                if feedback.isCanceled():
                    break
                current += 1
                feedback.setProgress(int(current * Percent))
        else:
            # Gerar geometrias por área sem cálculo de sobreposição ("first")
            combs = np.array(valores)[:, np.newaxis]
            classes = {}
            acumulado = geoms[combs[0][0] - 1]
            classes[(1, )] = {'geom': acumulado}
            for k in range(1, len(combs)):
                comb = combs[k]
                geom = geoms[comb[0] - 1]
                diferenca = geom.difference(acumulado)
                classes[(comb[0], )] = {'geom': diferenca}
                acumulado = acumulado.combine(geom)
                if feedback.isCanceled():
                    break

        # Gerar lista com os valores classificados
        Percent = 100.0 / (len(classes))
        current = 0
        for classe in classes:
            feedback.pushInfo(
                (self.tr('Classifying class {}...',
                         'Classificando classe {}...')).format(str(classe)))
            geom = classes[classe]['geom']
            if moldura:
                geom = geom.intersection(moldura_geom)
            if geom.type() == 2:
                if geom.isMultipart():
                    coords = geom.asMultiPolygon()[0][0]
                else:
                    coords = geom.asPolygon()[0]
            else:
                del classes[classe]
                continue
            caminho = []
            for ponto in coords:
                linha = (origem[1] - ponto.y()) / resol_Y
                coluna = (ponto.x() - origem[0]) / resol_X
                caminho += [(linha, coluna)]
            p = path.Path(caminho)
            box = geom.boundingBox()
            uly = box.yMaximum()
            lry = box.yMinimum()
            ulx = box.xMinimum()
            lrx = box.xMaximum()
            # Limites de Varredura
            row_ini = int(round((origem[1] - uly) / resol_Y - 0.5)) - 1
            row_fim = int(round((origem[1] - lry) / resol_Y - 0.5)) + 1
            col_ini = int(round((ulx - origem[0]) / resol_X - 0.5)) - 1
            col_fim = int(round((lrx - origem[0]) / resol_X - 0.5)) + 1
            lin, col = np.meshgrid(np.arange(row_ini, row_fim),
                                   np.arange(col_ini, col_fim))
            LIN = lin.flatten()[:, np.newaxis] + 0.5  # centro do pixel
            COL = col.flatten()[:, np.newaxis] + 0.5
            pixels_center = np.hstack((LIN, COL))
            # Verificando pixels dentro de poligono
            flags = p.contains_points(pixels_center)
            pixels_x = LIN.flatten() * flags
            pixels_y = COL.flatten() * flags
            pixels_x = (pixels_x[pixels_x > 0] - 0.5).astype('int')[:,
                                                                    np.newaxis]
            pixels_y = (pixels_y[pixels_y > 0] - 0.5).astype('int')[:,
                                                                    np.newaxis]
            pixels = np.hstack((pixels_x, pixels_y))
            classes[classe]['pixels'] = pixels
            current += 1
            feedback.setProgress(int(current * Percent))

        # Criar Raster
        Driver = gdal.GetDriverByName('GTiff').Create(Output, n_col, n_lin,
                                                      n_bands, GDT)
        Driver.SetGeoTransform(geotransform)
        Driver.SetProjection(prj)

        # Mosaicar por banda
        Percent = 100.0 / (n_lin * n_col * n_bands)
        current = 0

        for k in range(n_bands):
            feedback.pushInfo(
                (self.tr('Creating band {}...',
                         'Criando banda {}...')).format(str(k + 1)))
            # Criar Array do mosaico
            tipo = gdal_array.GDALTypeCodeToNumericTypeCode(GDT)
            inteiro = True if GDT in (gdal.GDT_Byte, gdal.GDT_UInt16,
                                      gdal.GDT_Int16, gdal.GDT_UInt32,
                                      gdal.GDT_Int32) else False
            banda = np.ones(
                (n_lin, n_col),
                dtype=tipo) * (int(valor_nulo) if inteiro else valor_nulo)
            imgs = {}
            # Para cada classe abrir banda da(s) imagem(ns)
            for classe in classes:
                # Deixando somente imagens a serem utilizadas
                for item in valores:
                    if (item not in classe) and (item in imgs):
                        del imgs[item]
                # Preenchendo dados da imagem no dicionário
                for img in classe:
                    if img not in imgs or len(lista) == 1:
                        img_path = lista[img - 1]
                        image = gdal.Open(img_path)
                        ulx, xres, xskew, uly, yskew, yres = image.GetGeoTransform(
                        )
                        img_origem = (ulx, uly)
                        img_resol_X = abs(xres)
                        img_resol_Y = abs(yres)
                        img_band = image.GetRasterBand(k + 1).ReadAsArray()
                        imgs[img] = {
                            'band': img_band,
                            'xres': img_resol_X,
                            'yres': img_resol_Y,
                            'origem': img_origem
                        }
                        image = None

                if sobrep == 0:  # Se for "primeiro", interpolar apenas da primeira img da comb, caso contrário
                    img = classe[0]
                    # Para cada pixel da classe
                    for px in classes[classe]['pixels']:
                        lin, col = px
                        X = origem[0] + resol_X * (col + 0.5)
                        Y = origem[1] - resol_Y * (lin + 0.5)
                        Interpolado = self.Interpolar(X, Y, imgs[img]['band'],
                                                      imgs[img]['origem'],
                                                      imgs[img]['xres'],
                                                      imgs[img]['yres'],
                                                      reamostragem, valor_nulo)
                        if Interpolado != valor_nulo:
                            banda[lin][col] = round(
                                Interpolado) if inteiro else Interpolado

                        if feedback.isCanceled():
                            break
                        current += 1
                        feedback.setProgress(int(current * Percent))

                else:  # Para cada pixel da classe interpolar o valor da banda de cada img
                    for px in classes[classe]['pixels']:
                        lin, col = px
                        X = origem[0] + resol_X * (col + 0.5)
                        Y = origem[1] - resol_Y * (lin + 0.5)
                        interp_values = []
                        for img in imgs:
                            Interpolado = self.Interpolar(
                                X, Y, imgs[img]['band'], imgs[img]['origem'],
                                imgs[img]['xres'], imgs[img]['yres'],
                                reamostragem, valor_nulo)
                            if Interpolado != valor_nulo:
                                interp_values += [Interpolado]
                        # Calcular o valor agregado (0:first, 1:average, 2:median, 3:min, 4:max) e inserir na banda (se byte, arredondar)
                        if interp_values:
                            if sobrep == 1:
                                result = np.mean(interp_values)
                            elif sobrep == 2:
                                result = np.median(interp_values)
                            elif sobrep == 3:
                                result = np.min(interp_values)
                            elif sobrep == 4:
                                result = np.max(interp_values)
                            banda[lin][col] = round(
                                result) if inteiro else result

                        if feedback.isCanceled():
                            break
                        current += 1
                        feedback.setProgress(int(current * Percent))

            # Salvar banda
            outband = Driver.GetRasterBand(k + 1)
            feedback.pushInfo(
                self.tr('Writing Band {}...'.format(k + 1),
                        'Escrevendo Banda {}...'.format(k + 1)))
            outband.WriteArray(banda)
            if NULO != -1:
                outband.SetNoDataValue(valor_nulo)

        # Salvar e Fechar Raster
        Driver.FlushCache()  # Escrever no disco
        Driver = None  # Salvar e fechar

        feedback.pushInfo(
            self.tr('Operation completed successfully!',
                    'Operação finalizada com sucesso!'))
        feedback.pushInfo('Leandro França - Eng Cart')
        self.CAMINHO = Output
        self.CARREGAR = Carregar
        return {self.MOSAIC: Output}

    # Carregamento de arquivo de saída
    def postProcessAlgorithm(self, context, feedback):
        if self.CARREGAR:
            rlayer = QgsRasterLayer(self.CAMINHO, self.tr('Mosaic', 'Mosaico'))
            QgsProject.instance().addMapLayer(rlayer)
        return {}
Ejemplo n.º 13
0
class MemorialDescritivo(QgisAlgorithm):
    """
    This algorithm takes three vector layers (point, line, and polygon) 
    that define a specific ownership and creates an HTML file with the
    descriptive characteristics of the area.
    """
    HTML = 'HTML'
    INPUT1 = 'INPUT1'
    INPUT2 = 'INPUT2'
    INPUT3 = 'INPUT3'

    LOC = QgsApplication.locale()

    texto_inicial = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=ISO-8859-1"
 http-equiv="content-type">
  <title>descritivo</title>
</head>
<body>
<div style="text-align: center;"><span style="font-weight: bold;"><br>
<img src="data:image/jpg;base64,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"><br>
MINIST&Eacute;RIO DA DEFESA</span><br style="font-weight: bold;">
<span style="font-weight: bold;">EX&Eacute;RCITO BRASILEIRO</span><br style="font-weight: bold;">
<span style="font-weight: bold;">DEPARTAMENTO DE CI&Ecirc;NCIA E TECNOLOGIA</span><br style="font-weight: bold;">
<span style="font-weight: bold;">3&ordm; CENTRO DE GEOINFORMA&Ccedil;&Atilde;O</span><br>
<br></div>
<p class="western"
 style="margin-bottom: 0.0001pt; text-align: center;"
 align="center"><b><span style="font-size: 12pt;">MEMORIAL
DESCRITIVO</span></b><o:p></o:p></p>
<p class="western" style="margin-bottom: 0.0001pt;"><o:p>&nbsp;</o:p></p>
<table class="MsoTableGrid"
 style="border: medium none ; border-collapse: collapse;"
 border="0" cellpadding="0" cellspacing="0">
  <tbody>
    <tr style="">
      <td style="padding: 0cm 5.4pt; width: 247.85pt;"
 valign="top" width="330">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>Im&oacute;vel:
</b>[IMOVEL]<o:p></o:p></p>
      </td>
      <td style="padding: 0cm 5.4pt; width: 176.85pt;"
 valign="top" width="236">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>Comarca:</b>
[COMARCA]<o:p></o:p></p>
      </td>
    </tr>
    <tr style="">
      <td colspan="2"
 style="padding: 0cm 5.4pt; width: 424.7pt;" valign="top"
 width="566">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>Propriet&aacute;rio:</b>
[PROPRIETARIO]<o:p></o:p></p>
      </td>
    </tr>
    <tr style="">
      <td style="padding: 0cm 5.4pt; width: 247.85pt;"
 valign="top" width="330">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>UF:</b>
[UF]<b><o:p></o:p></b></p>
      </td>
      <td style="padding: 0cm 5.4pt; width: 176.85pt;"
 valign="top" width="236">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>Munic&iacute;pio:
      </b>[MUNICIPIO]<o:p></o:p></p>
      </td>
    </tr>
    <tr style="">
      <td colspan="2"
 style="padding: 0cm 5.4pt; width: 424.7pt;" valign="top"
 width="566">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>Matr&iacute;cula(s):</b>
[MATRICULAS]<o:p></o:p></p>
      </td>
    </tr>
    <tr style="">
      <td style="padding: 0cm 5.4pt; width: 247.85pt;"
 valign="top" width="330">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>&Aacute;rea
(m<sup>2</sup>): </b>[AREA]<o:p></o:p></p>
      </td>
      <td style="padding: 0cm 5.4pt; width: 176.85pt;"
 valign="top" width="236">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>Per&iacute;metro
(m):</b> [PERIMETRO]<o:p></o:p></p>
      </td>
    </tr>
    <tr style="">
      <td colspan="2"
 style="padding: 0cm 5.4pt; width: 424.7pt;" valign="top"
 width="566">
      <p class="western" style="margin-bottom: 0.0001pt;"><b>Sistema
de Refer&ecirc;ncia de Coordenadas:</b> [SRC]<b><o:p></o:p></b></p>
      </td>
    </tr>
  </tbody>
</table>
<p class="western" style="margin-bottom: 0.0001pt;"><o:p>&nbsp;</o:p></p>
<p class="western"
 style="margin-bottom: 0.0001pt; text-align: justify;">Inicia-se
a descri&ccedil;&atilde;o deste per&iacute;metro n'''

    texto_var1 = '''o
v&eacute;rtice <b>[Vn]</b>, de
coordenadas <b>N [Nn] m </b>e <b>E [En] m</b>,
[Descr_k], deste, segue
confrontando com [Confront_k], com os seguintes azimutes planos e
dist&acirc;ncias: [Az_n]
e [Dist_n] m at&eacute; '''

    texto_var2 = '''o v&eacute;rtice<span
 style="color: red;"> </span><b>[Vn]</b>,
de
coordenadas <b>N [Nn] m </b>e <b>E [En] m</b>;
[Az_n] e [Dist_n] m at&eacute; '''

    texto_final = '''o v&eacute;rtice <b>[P-01]</b>, de coordenadas <b>N
[N1] m </b>e <b>E [E1] m</b>, ponto
inicial da descri&ccedil;&atilde;o deste per&iacute;metro.
Todas as coordenadas aqui descritas est&atilde;o
georreferenciadas ao Sistema Geod&eacute;sico Brasileiro, tendo
como SGR o <b>SIRGAS2000</b>,
e encontram-se projetadas no Sistema UTM, fuso [FUSO] e
hemisf&eacute;rio [HEMISFERIO], a
partir das quais todos os azimutes e dist&acirc;ncias,
&aacute;rea e per&iacute;metro foram
calculados.<o:p></o:p></p>
<p class="western"
 style="margin-bottom: 0.0001pt; text-align: justify;"><o:p>&nbsp;</o:p></p>
<p class="western"
 style="margin-bottom: 0.0001pt; text-align: justify;"><o:p>&nbsp;</o:p></p>
<p class="western"
 style="margin-bottom: 0.0001pt; text-align: right;"
 align="right">Olinda - PE, [DATA].<o:p></o:p></p>
<p class="western" style="margin-bottom: 0.0001pt;"><o:p>&nbsp;</o:p></p>
<p class="western" style="margin-bottom: 0.0001pt;"><o:p>&nbsp;</o:p></p>
<p class="western"
 style="margin: 0cm 0cm 0.0001pt; text-align: center;"
 align="center">___________________________________________<o:p></o:p></p>
<p class="western"
 style="margin: 0cm 0cm 0.0001pt; text-align: center;"
 align="center">[RESP_TEC]<o:p></o:p></p>
<p class="western"
 style="margin: 0cm 0cm 0.0001pt; text-align: center;"
 align="center">[CREA]<o:p></o:p></p>
<p class="western"
 style="margin: 0cm 0cm 0.0001pt; text-align: center;"
 align="center">RESPONS&Aacute;VEL T&Eacute;CNICO<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
</body>
</html>
'''

    def translate(self, string):
        return QCoreApplication.translate('Processing', string)

    def tr(self, *string):
        # Traduzir para o portugês: arg[0] - english (translate), arg[1] - português
        if self.LOC == 'pt':
            if len(string) == 2:
                return string[1]
            else:
                return self.translate(string[0])
        else:
            return self.translate(string[0])

    def createInstance(self):
        return MemorialDescritivo()

    def name(self):
        return 'memorialdescritivo'

    def displayName(self):
        return self.tr('Descriptive Memorial', 'Memorial Descritivo')

    def group(self):
        return self.tr('LF Documents', 'LF Documentos')

    def groupId(self):
        return 'lf_documents'

    def shortHelpString(self):
        txt_en = 'Elaboration of Descriptive Memorials based on vector layers that define a property.'
        txt_pt = 'Elaboração de Memorial Descritivo a partir de camadas vetorias que definem uma propriedade.'
        dic_BW = {
            'face':
            'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMwAADDMBUlqVhwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIUSURBVEiJvZa9quJAGIbfMfEYTUSUQAqLYOnehJobsBTlHLbRQtKKlY3lObegxRa7pRdg7y0saiuoKAErMZif2Squ2eTEOCfsC1Pk+3uYb2YyQwCg1+u9UUqHAKoAOCQrB8ASwPt0Ov1JdF3/bprmj4QhoXp5eXnjTdMcUEr/Bw+WZQ15Smn1q4UIIZBlGaIoghBys2+3W1yv19u367rfeAAc6ww5jkOz2USj0YAoigH/aDTCbrfzpfCUUrAC2+02NE2LjPm3NjNQkiTU6/WHsFAgi1RVRSqVCthd171BwmozA/P5fMA2n88xm81g2zYAwHGccCAL9H43elosFrhcLpE5zMCwHMdxImtRSp8DptNpZDIZAIAgCAG/IAi+42Ga5q29nkin06FxgZqmodvtxooFgPF4jPV67bM9NcNnW384HJI7h49kWRZOp9PXgOfzGfv9HgCQy+VQKBR8fsMwYFkWAOB4PMJ13UAN0mq1Yq/hfVytVoOu6z7/YDDAZrP5Wzzk6CR6LOLEJLqGcWrxYX2OW5wJmOQOjQX0ApOERgIrlQoTUJblgK1cLodeWZ4IIeDDngZx5P1T75XNZiFJUmQeTyl1wPAW/awrD7rlpDiOW3qL/cz4DBY1CCG/U4qifDw7O1YpivJBAGAymbwahjG0bbtKKeXjJJdKJaiq6rOtVqvAjU8IsTmOWxaLxfd+v//rD1H2cZ8dKhk8AAAAAElFTkSuQmCC',
            'github':
            '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',
            'instagram':
            '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',
            'lattes':
            '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',
            'linkedin':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6QAADOkBmiiHWwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJRSURBVEiJ7ZU/aCJREMa/t7soa5LOoGihktZOQSxSWykBIYVaWZ6NVa6wSZ/WI5BGziJypA1CQBLsgkKKgBjFQkEtkouJ+AdxdeeKnHvurXdcXK84uK/amfl4v9m3s++xbrdrury8PGm324dEZMYGxRj7arPZvoRCoSPh/Pz8pFqtftgkYEnmTqeT6PV6xDscjs9EZPpLIADAy8uLS5jP52bGGACA4ziEw2H4/X5IkoSrqytcX1/rBhHRrrCcCAaDCAQCShyJRPD6+oq7uzvdMG458Hg8GoPX69UN0YDG47HGsCqnG5TP50FESjyZTFAoFDYCYvF4nBbDAAB7e3vw+XyQJAnFYhGPj4+6IUQE4eekJEm4vb0F8DaFC1mtVphMb3+BLMtotVogItjtdjgcDhiNRvT7fTw8PKzcbhXI5XIhlUqpOkkkEpjP5zg+PoYg/LCfnp5if38fbrdbteB0OsXFxQVubm5+DTIYDKoiYww8z4OIwPO8qhaLxbCzs6Pp3GAwIBqN4vn5Gff390qe0zj/UKsgywqFQqpY843eo1KphHK5DLPZjIODAxiNRqXmdDqxvb2N4XCoD1Sv13F2dqbEs9kM0WhU5bFYLApo7a1bTOZClUpF49na2lKe1wb1ej1VvGqklwdobdB79R/0j4FkWdYYiEh1dawrAcATgF0AaDabyOVyyuE5HA4xmUwAAJlMRjl2ZFlGo9FQLTQajZDNZiGKouKp1WqL8hNLJpPpwWCQ0N3ybySKYpqLxWJHjLFP399s03riOC4dCAQ+fgMeouMzfwx22gAAAABJRU5ErkJggg==',
            'RG':
            'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMfwAADH8BdgxfmQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAM2SURBVEiJtZdLSytJFMd/1dXB+OiQRJEQBEnbPgISJGIQcePKjyBZzoP5AH6OuR9gFjPMznwC1650EbKSy00WPhJ8EMlDtG1M0p2axZCA1871cZM/NDRFnfqdqjrnVJUAODg4+E0IsQ8sA5LRyANKQogve3t7/4hcLvcH8NeIYL5SSv2uAftDHhSl1A/7CCH2dWBxWNBoNIppmgSDQarVKufn53ie59d1RQO0YUBjsRg7OztomkatVmNpaYnNzU2EEH7dNX0YUKUUpmlyd3dHPp9HKcXV1RXT09MIIXyXfihgKSWGYVCpVPoQ27axbXugjS/4HcHh29btdlFKsbq6SiwWA6BQKHB/f/82WEpJKpUiEAi8aA8EAiilaLVaNBoNrq+vabfbr+BCCG5vb3l8fCSTyaDr/ov6KrA8z6NcLtNqtUgkEszPz+M4Ds1mE9u2MQyDjY0Ndnd3mZmZ6ds4jkMoFAKg0Wjw8PCAEIJOp+ML9nWnZ5hIJJBSUiqV+gMIIbAsi3Q6zdbWFoeHh7iuS7lcZn19HcdxsG0by7Ko1+sD93lgcCml6Ha7SClftZ+dnbG4uIhhGMzOznJzc0OlUkHTNBYWFtB1nXq9zunp6aA8/lxUe57H09MThmEwPj7ed+ji4oLLy0uEEHieNyiHPw+WUjI5OYlS6lXE9krmj6DwyaplmiaGYVCtVqnX658Z4u0ZCyGIx+O0223GxsaYm5sjHo9Tq9U4OTn5FPRdYCkly8vLCCEIh8MopSgUCpTLZVzXHR3YdV2Ojo7wPI/t7W1isRjBYPCnoPDOPVZK4XlePz2SySThcHj04J4ajQbFYhEpJZlM5lWOjwwMUCwWaTabRCIR0un08MG9gt/778l1XfL5PJ1OB9M0WVlZGR54amoKy7IIBoPouo5lWYRCof7SNptN8vk83W6XVCrF2toaExMTHwKLXC734vCVUpJOp9F1/cW5rGkapVLpRcGIRqMkk0kikQiO43B8fMzz8/O7wd73Mx90EfArg0opNE178/LwnboaUPID+H2DnPkgFOCbppT68lGrn5VS6k8tm83+DfwKfOX/Z8ao5AFflVK/ZLPZf/8DudZq3wvXLmgAAAAASUVORK5CYII=',
            'tweeter':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMQAADDEBLaRWDgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAK5SURBVEiJpZU7T+NAFEZP4kAePBwZ8TA0EUIRKKIgQhSImnZ/wDYr7Q/barddbRu2QVAQhChAgBQqQClwFEIcx8IYfLdKNk5sJ4GRRh6PPN+Z+90749jZ2VnGsqyfwD6QEREARISocdRcT7dF5AD4lmi327+AL0GCn4QgIhkR+SIiPxIish8k3i/yQVCn7ydEJD0sgjDBMUCZxCg2hQmqqsri4iKKotBoNKhWq741vesSvULpdJrJyUkajcZQy7a2tlheXqa3FQoFzs/PmZubQ9M0Dg8Pu2vivQJLS0sUi0Wy2WykFfl8fgACkEql2N3dJZfLcXFxgaqq/0G9O04mk8RiMba3t8nlcqHRrK6uDkB628TEBDs7O5imORgRgGVZ3Y/z+Tx7e3ssLCz4QMlkkng8HglqNpscHBzw9vbmz1EHVK1WWVtb6wpNTU1RLBZ5f3/HMAzq9TqO40RCAGq1Gq7r+jboK4aZmRmurq7Y3Nz0LVQUBV3X0XV9KATAcZwBy7vlLSJomjbU/1Ha8/PzAMhXdQ8PD5+GABiGEQ1qt9tcXl5+CvL09MTLy8ugdf0T9/f32LbNxsYGs7OzY4Our68DD/oASEQoFApMT0+PDTFNk7u7u0BQPAhULpdxXXds0PHxMZ7nDUBCQa1Wi1KpRKVSwTTNkSAnJyfU6/XQWz4QJCI4jsPj4yOKogyFnJ6ecnt7G3nTd3OUSCSYn59HURRUVUXXdTKZTCTAtm2Ojo585dwP6rx3Qa+vrzSbTdbX11lZWSEWi4UCLMvi5uaGSqWC53mh/67esa/qTNOkXC4DoGka2WyWVCoFgOu6tFotDMPAtu3QXISNA8tbRKjVaoEnPCwPYZZ1nnHP8+ww2Ed7P0RE7LjneX/7ff6ocES0pbiIfBWRP57n2aMAwywKGdsi8ltRlO//AFPkniYXwGRMAAAAAElFTkSuQmCC',
            'udemy':
            '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',
            'youtube':
            'iVBORw0KGgoAAAANSUhEUgAAACEAAAAaCAYAAAA5WTUBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMNwAADDcBracSlQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKkSURBVEiJxZcxSBthFIC/918uGhxMsyptcGjoIgGHW9wSihQKGZrNthHsIC7S1TqWgkRwcZV2lKo0FouixaEuEQ4khIIdrClkEoIZgiae93fQgFgll5jqt93x3v++e3f/3TvhGgYHBx/UarUUEAMeA48A/3WxDagBf4A9EdmsVqsfd3d3j64GyeWDZDJpFAqFKWAC6G6haCPKIjKTzWbfA+4/Ev39/V2dnZ3LwNP/UPwqaycnJy9yuVwFQF2cVIFA4PMdCQAMBQKBL8lk0gAwACzLmtJav7kjgTp95XK5ViwWf0g0Gg36/f7fQLBRltYaEWkU5hkRKTmO06dM03ztRaAuEQ6H0Vq3RUJrHTIM46XR29v7jvNt2BClFEtLS0QiEfb39ymVSrfujIg4Cog0mxiPx1lYWGBycpKenh5c122cdANa64gCHra6QCKRYHl5mfHxcYLBII7jtLJMWAEdrUrUSaVSrK6uMjo6SigU4uzsrJn0DtU4xhs+n4+xsTFWVlYYHh6mq6vL821qm0Qd0zSZmJggk8kQi8XuR6LO1tYW+XzeUzd87S6ey+WYnZ0ln88jIijV+DrbJnF4eMj09DTb29u4rtvU+8MHVLnFDjk+PiadTrO+vs7p6WkrS1R9QAGPb8zLaK2Zm5tjcXGRSqXiqe03sO8D9pqVyGQyzM/PUywWMQzjNgIAv3xa600Ree4l2nEcRkZGODg4QCmFYRi3KQ6AiGzIwMBAt2EYB9znp9y27bLWesZjUtsELkjbtl2ur6osy/qqtX7W7io3ISIb2Wx2CHDrT5TrOE4SWLsjgW+O4yS4mLiv9ldZljWptX6Lx2mrSY6A9M7OzgeuG/kvE41Gg6ZpvhKROPCE85mj5Z8fEfnpuu5313U/2bZdvhr0F9Fo9phaoDu9AAAAAElFTkSuQmCC'
        }
        footer = '''<div align="right">
                      <p align="right"><b>''' + self.tr(
            'Author: Leandro Franca', 'Autor: Leandro França'
        ) + '''</b></p>
                      <div align="right">
                      <a target="_blank" rel="noopener noreferrer" href="https://www.udemy.com/user/leandro-luiz-silva-de-franca/"><img title="Udemy" src="data:image/png;base64,''' + dic_BW[
            'udemy'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/GEOCAPT/"><img title="Facebook" src="data:image/png;base64,''' + dic_BW[
                'face'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/channel/UCLrewDGciytcBG9r0OxTW2w"><img title="Youtube" src="data:image/png;base64,''' + dic_BW[
                    'youtube'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.researchgate.net/profile/Leandro_Franca2"><img title="ResearchGate" src="data:image/png;base64,''' + dic_BW[
                        'RG'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/LEOXINGU"><img title="GitHub" src="data:image/png;base64,''' + dic_BW[
                            'github'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.linkedin.com/in/leandro-fran%C3%A7a-93093714b/"><img title="Linkedin" src="data:image/png;base64,''' + dic_BW[
                                'linkedin'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="http://lattes.cnpq.br/8559852745183879"><img title="Lattes" src="data:image/png;base64,''' + dic_BW[
                                    'lattes'] + '''"></a>
                      </div>
                    </div>'''
        if self.LOC == 'pt':
            return txt_pt + footer
        else:
            return self.tr(txt_en) + footer

    def initAlgorithm(self, config=None):
        # 'INPUTS'
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                'INPUT1',
                self.tr('Boundary Survey Points', 'Pontos de Limite'),
                types=[QgsProcessing.TypeVectorPoint]))
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                'INPUT2',
                self.tr('Neighborhood Dividing Line', 'Elemento Confrontante'),
                types=[QgsProcessing.TypeVectorLine]))
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                'INPUT3',
                self.tr('Property Polygon', 'Área do Imóvel'),
                types=[QgsProcessing.TypeVectorPolygon]))
        # 'OUTPUTS'
        self.addParameter(
            QgsProcessingParameterFileDestination(
                'HTML', self.tr('Descriptive Memorial', 'Memorial Descritivo'),
                self.tr('HTML files (*.html)')))

    # Acentos para HTML
    def str2HTML(self, texto):
        if texto:
            dicHTML = {
                'Á': '&Aacute;',
                'á': '&aacute;',
                'Â': '&Acirc;',
                'â': '&acirc;',
                'À': '&Agrave;',
                'à': '&agrave;',
                'Å': '&Aring;',
                'å': '&aring;',
                'Ã': '&Atilde;',
                'ã': '&atilde;',
                'Ä': '&Auml;',
                'ä': '&auml;',
                'Æ': '&AElig;',
                'æ': '&aelig;',
                'É': '&Eacute;',
                'é': '&eacute;',
                'Ê': '&Ecirc;',
                'ê': '&ecirc;',
                'È': '&Egrave;',
                'è': '&egrave;',
                'Ë': '&Euml;',
                'ë': '&Euml;',
                'Ð': '&ETH;',
                'ð': '&eth;',
                'Í': '&Iacute;',
                'í': '&iacute;',
                'Î': '&Icirc;',
                'î': '&icirc;',
                'Ì': '&Igrave;',
                'ì': '&igrave;',
                'Ï': '&Iuml;',
                'ï': '&iuml;',
                'Ó': '&Oacute;',
                'ó': '&oacute;',
                'Ô': '&Ocirc;',
                'ô': '&ocirc;',
                'Ò': '&Ograve;',
                'ò': '&ograve;',
                'Ø': '&Oslash;',
                'ø': '&oslash;',
                'Ù': '&Ugrave;',
                'ù': '&ugrave;',
                'Ü': '&Uuml;',
                'ü': '&uuml;',
                'Ç': '&Ccedil;',
                'ç': '&ccedil;',
                'Ñ': '&Ntilde;',
                'ñ': '&ntilde;',
                'Ý': '&Yacute;',
                'ý': '&yacute;',
                '"': '&quot;',
                '”': '&quot;',
                '<': '&lt;',
                '>': '&gt;',
                '®': '&reg;',
                '©': '&copy;',
                '\'': '&apos;',
                'ª': '&ordf;',
                'º': '&ordm',
                '°': '&deg;'
            }
            for item in dicHTML:
                if item in texto:
                    texto = texto.replace(item, dicHTML[item])
            return texto
        else:
            return ''

    # Fuso e Hemisfério
    def FusoHemisf(self, pnt):
        lon = pnt.x()
        lat = pnt.y()
        # Calculo do Fuso
        fuso = round((183 + lon) / 6.0)
        # Hemisferio
        hemisf = 'N' if lat >= 0 else 'S'
        return (hemisf, fuso)

    # Cálculo de Azimutes
    def azimute(self, A, B):
        # Cálculo dos Azimutes entre dois pontos (Vetor AB origem A extremidade B)
        if ((B.x() - A.x()) >= 0 and (B.y() - A.y()) > 0):  #1º quadrante
            AzAB = atan((B.x() - A.x()) / (B.y() - A.y()))
            AzBA = AzAB + pi
        elif ((B.x() - A.x()) > 0 and (B.y() - A.y()) < 0):  #2º quadrante
            AzAB = pi + atan((B.x() - A.x()) / (B.y() - A.y()))
            AzBA = AzAB + pi
        elif ((B.x() - A.x()) <= 0 and (B.y() - A.y()) < 0):  #3º quadrante
            AzAB = atan((B.x() - A.x()) / (B.y() - A.y())) + pi
            AzBA = AzAB - pi
        elif ((B.x() - A.x()) < 0 and (B.y() - A.y()) > 0):  #4º quadrante
            AzAB = 2 * pi + atan((B.x() - A.x()) / (B.y() - A.y()))
            AzBA = AzAB + pi
        elif ((B.x() - A.x()) > 0
              and (B.y() - A.y()) == 0):  # no eixo positivo de x (90º)
            AzAB = pi / 2
            AzBA = 1.5 * pi
        else:  # ((B.x()-A.x())<0 and(B.y()-A.y())==0) # no eixo negativo de x (270º)
            AzAB = 1.5 * pi
            AzBA = pi / 2
        # Correção dos ângulos para o intervalo de 0 a 2pi
        if AzAB < 0 or AzAB > 2 * pi:
            if (AzAB < 0):
                AzAB = AzAB + 2 * pi
            else:
                AzAB = AzAB - 2 * pi
        if AzBA < 0 or AzBA > 2 * pi:
            if (AzBA < 0):
                AzBA = AzBA + 2 * pi
            else:
                AzBA = AzBA - 2 * pi
        return (AzAB, AzBA)

    # Graus Decimais para DMS
    def dd2dms(self, dd, n_digits):
        if dd != 0:
            graus = int(floor(abs(dd)))
            resto = round(abs(dd) - graus, 10)
            if dd < 0:
                texto = '-' + str(graus) + '°'
            else:
                texto = str(graus) + '°'
            minutos = int(floor(60 * resto))
            resto = round(resto * 60 - minutos, 10)
            texto = texto + '{:02d}'.format(minutos) + "'"
            segundos = resto * 60
            if n_digits < 1:
                texto = texto + '{:02d}'.format(int(segundos)) + '"'
            else:
                texto = texto + ('{:0' + str(3 + n_digits) + '.' +
                                 str(n_digits) + 'f}').format(segundos) + '"'
            return texto
        else:
            return "0°00'" + ('{:0' + str(3 + n_digits) + '.' + str(n_digits) +
                              'f}').format(0)

    def processAlgorithm(self, parameters, context, feedback):

        vertices = self.parameterAsSource(parameters, 'INPUT1', context)
        limites = self.parameterAsSource(parameters, 'INPUT2', context)
        area = self.parameterAsSource(parameters, 'INPUT3', context)

        meses = {
            1: 'janeiro',
            2: 'fevereiro',
            3: 'março',
            4: 'abril',
            5: 'maio',
            6: 'junho',
            7: 'julho',
            8: 'agosto',
            9: 'setembro',
            10: 'outubro',
            11: 'novembro',
            12: 'dezembro'
        }

        # VALIDAÇÃO DOS DADOS DE ENTRADA!!!
        # atributos codigo deve ser preenchido
        # ordem do numeros

        # Pegando informações dos confrontantes (limites)
        ListaDescr = []
        ListaCont = []
        soma = 0
        for linha in limites.getFeatures():
            Lin_coord = linha.geometry().asMultiPolyline()[0]
            ListaDescr += [[
                self.str2HTML(linha['descr_pnt_inicial']),
                self.str2HTML(linha['confrontante'])
            ]]
            cont = len(Lin_coord)
            ListaCont += [(soma, cont - 1)]
            soma += cont - 1

        # Pegando o SRC do Projeto
        SRC = QgsProject.instance().crs().description()
        # Verificando o SRC
        if QgsProject.instance().crs().isGeographic():
            raise QgsProcessingException(
                self.tr('The Project CRS must be projected!',
                        'O SRC do Projeto deve ser Projetado!'))
        feedback.pushInfo(
            self.tr('Project CRS is {}.', 'SRC do Projeto é {}.').format(SRC))

        # Dados do levantamento
        #Fields = area.fields()
        #fieldnames = [field.name() for field in Fields]
        for feat in area.getFeatures():
            feat1 = feat
            break

        geom = feat1.geometry()
        centroideG = geom.centroid().asPoint()

        # Transformar Coordenadas de Geográficas para o sistema UTM
        coordinateTransformer = QgsCoordinateTransform()
        coordinateTransformer.setDestinationCrs(QgsProject.instance().crs())
        coordinateTransformer.setSourceCrs(
            QgsCoordinateReferenceSystem('EPSG:4674'))

        pnts = {}

        for feat in vertices.getFeatures():
            geom = feat.geometry()
            if geom.isMultipart():
                pnts[feat['ordem']] = [
                    coordinateTransformer.transform(geom.asMultiPoint()[0]),
                    feat['tipo'], feat['codigo']
                ]
            else:
                pnts[feat['ordem']] = [
                    coordinateTransformer.transform(geom.asPoint()),
                    feat['tipo'], feat['codigo']
                ]

        # Cálculo dos Azimutes e Distâncias
        tam = len(pnts)
        Az_lista, Dist = [], []
        for k in range(tam):
            pntA = pnts[k + 1][0]
            pntB = pnts[max((k + 2) % (tam + 1), 1)][0]
            Az_lista += [(180 / pi) * self.azimute(pntA, pntB)[0]]
            Dist += [sqrt((pntA.x() - pntB.x())**2 + (pntA.y() - pntB.y())**2)]

        # Inserindo dados iniciais do levantamento
        itens = {
            '[IMOVEL]':
            self.str2HTML(feat1['imóvel']),
            '[PROPRIETARIO]':
            self.str2HTML(feat1['proprietário']),
            '[UF]':
            feat1['UF'],
            '[MATRICULAS]':
            self.str2HTML(feat1['matrícula']),
            '[AREA]':
            '{:,.2f}'.format(feat1['area']).replace(',', 'X').replace(
                '.', ',').replace('X', '.'),
            '[SRC]':
            SRC,
            '[COMARCA]':
            self.str2HTML(feat1['município'] + ' - ' + feat1['UF']),
            '[MUNICIPIO]':
            self.str2HTML(feat1['município']),
            '[PERIMETRO]':
            '{:,.2f}'.format(feat1['perimetro']).replace(',', 'X').replace(
                '.', ',').replace('X', '.')
        }

        for item in itens:
            self.texto_inicial = self.texto_inicial.replace(item, itens[item])

        LINHAS = self.texto_inicial
        #feedback.pushInfo(str(ListaCont))
        for w, t in enumerate(ListaCont):
            linha0 = self.texto_var1
            itens = {
                '[Vn]':
                pnts[t[0] + 1][2],
                '[En]':
                '{:,.2f}'.format(pnts[t[0] + 1][0].x()).replace(
                    ',', 'X').replace('.', ',').replace('X', '.'),
                '[Nn]':
                '{:,.2f}'.format(pnts[t[0] + 1][0].y()).replace(
                    ',', 'X').replace('.', ',').replace('X', '.'),
                '[Az_n]':
                self.str2HTML(
                    self.dd2dms(Az_lista[t[0]], 2).replace('.', ',')),
                '[Dist_n]':
                '{:,.2f}'.format(Dist[t[0]]).replace(',', 'X').replace(
                    '.', ',').replace('X', '.'),
                '[Descr_k]':
                ListaDescr[w][0],
                '[Confront_k]':
                ListaDescr[w][1]
            }
            for item in itens:
                linha0 = linha0.replace(item, itens[item])
            LINHAS += linha0
            LIN0 = ''
            for k in range(t[0] + 1, t[0] + t[1]):
                linha1 = self.texto_var2
                itens = {
                    '[Vn]':
                    pnts[k + 1][2],
                    '[En]':
                    '{:,.2f}'.format(pnts[k + 1][0].x()).replace(
                        ',', 'X').replace('.', ',').replace('X', '.'),
                    '[Nn]':
                    '{:,.2f}'.format(pnts[k + 1][0].y()).replace(
                        ',', 'X').replace('.', ',').replace('X', '.'),
                    '[Az_n]':
                    self.str2HTML(
                        self.dd2dms(Az_lista[k], 2).replace('.', ',')),
                    '[Dist_n]':
                    '{:,.2f}'.format(Dist[k]).replace(',', 'X').replace(
                        '.', ',').replace('X', '.')
                }
                for item in itens:
                    linha1 = linha1.replace(item, itens[item])
                LIN0 += linha1
            LINHAS += LIN0

        # Inserindo dados finais
        itens = {
            '[P-01]':
            pnts[1][2],
            '[N1]':
            '{:,.2f}'.format(pnts[1][0].y()).replace(',', 'X').replace(
                '.', ',').replace('X', '.'),
            '[E1]':
            '{:,.2f}'.format(pnts[1][0].x()).replace(',', 'X').replace(
                '.', ',').replace('X', '.'),
            '[FUSO]':
            str(self.FusoHemisf(centroideG)[1]),
            '[HEMISFERIO]':
            self.FusoHemisf(centroideG)[0],
            '[RESP_TEC]':
            self.str2HTML(feat1['Resp_Tecnico']),
            '[CREA]':
            self.str2HTML(feat1['CREA']),
            '[LOCAL]':
            self.str2HTML((feat1['município']).title() + ' - ' +
                          (feat1['UF']).upper()),
            '[DATA]':
            ((feat1['data_levantamento'].toPyDate()).strftime("%d de {} de %Y")
             ).format(meses[feat1['data_levantamento'].month()])
        }

        for item in itens:
            self.texto_final = self.texto_final.replace(item, itens[item])

        LINHAS += self.texto_final

        output = self.parameterAsFileOutput(parameters, self.HTML, context)
        arq = open(output, 'w')
        arq.write(LINHAS)
        arq.close()

        # Check for cancelation
        if feedback.isCanceled():
            return {}

        feedback.pushInfo(self.tr('Operation completed successfully!'))
        feedback.pushInfo('Leandro França - Eng Cart')

        return {self.HTML: output}
Ejemplo n.º 14
0
class ImportPhotos(QgsProcessingAlgorithm):

    LOC = QgsApplication.locale()

    def translate(self, string):
        return QCoreApplication.translate('Processing', string)

    def tr(self, *string):
        # Traduzir para o portugês: arg[0] - english (translate), arg[1] - português
        if self.LOC == 'pt':
            if len(string) == 2:
                return string[1]
            else:
                return self.translate(string[0])
        else:
            return self.translate(string[0])

    def createInstance(self):
        return ImportPhotos()

    def name(self):
        return 'importphotos'

    def displayName(self):
        return self.tr('Photos with Geotag', 'Fotos com Geotag')

    def group(self):
        return self.tr('LF Reambulation', 'LF Reambulação')

    def groupId(self):
        return 'lf_reambulation'

    def tags(self):
        return self.tr('import', 'photo', 'reambulation', 'geotag', 'geophoto',
                       'reambulação', 'fotografia', 'photography').split(',')

    def shortHelpString(self):
        txt_en = 'Imports photos with geotag to a Point Layer.'
        txt_pt = 'Importa fotos com geotag para uma camada de pontos.'
        dic_BW = {
            'face':
            'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMwAADDMBUlqVhwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIUSURBVEiJvZa9quJAGIbfMfEYTUSUQAqLYOnehJobsBTlHLbRQtKKlY3lObegxRa7pRdg7y0saiuoKAErMZif2Squ2eTEOCfsC1Pk+3uYb2YyQwCg1+u9UUqHAKoAOCQrB8ASwPt0Ov1JdF3/bprmj4QhoXp5eXnjTdMcUEr/Bw+WZQ15Smn1q4UIIZBlGaIoghBys2+3W1yv19u367rfeAAc6ww5jkOz2USj0YAoigH/aDTCbrfzpfCUUrAC2+02NE2LjPm3NjNQkiTU6/WHsFAgi1RVRSqVCthd171BwmozA/P5fMA2n88xm81g2zYAwHGccCAL9H43elosFrhcLpE5zMCwHMdxImtRSp8DptNpZDIZAIAgCAG/IAi+42Ga5q29nkin06FxgZqmodvtxooFgPF4jPV67bM9NcNnW384HJI7h49kWRZOp9PXgOfzGfv9HgCQy+VQKBR8fsMwYFkWAOB4PMJ13UAN0mq1Yq/hfVytVoOu6z7/YDDAZrP5Wzzk6CR6LOLEJLqGcWrxYX2OW5wJmOQOjQX0ApOERgIrlQoTUJblgK1cLodeWZ4IIeDDngZx5P1T75XNZiFJUmQeTyl1wPAW/awrD7rlpDiOW3qL/cz4DBY1CCG/U4qifDw7O1YpivJBAGAymbwahjG0bbtKKeXjJJdKJaiq6rOtVqvAjU8IsTmOWxaLxfd+v//rD1H2cZ8dKhk8AAAAAElFTkSuQmCC',
            'github':
            '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',
            'instagram':
            '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',
            'lattes':
            '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',
            'linkedin':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6QAADOkBmiiHWwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJRSURBVEiJ7ZU/aCJREMa/t7soa5LOoGihktZOQSxSWykBIYVaWZ6NVa6wSZ/WI5BGziJypA1CQBLsgkKKgBjFQkEtkouJ+AdxdeeKnHvurXdcXK84uK/amfl4v9m3s++xbrdrury8PGm324dEZMYGxRj7arPZvoRCoSPh/Pz8pFqtftgkYEnmTqeT6PV6xDscjs9EZPpLIADAy8uLS5jP52bGGACA4ziEw2H4/X5IkoSrqytcX1/rBhHRrrCcCAaDCAQCShyJRPD6+oq7uzvdMG458Hg8GoPX69UN0YDG47HGsCqnG5TP50FESjyZTFAoFDYCYvF4nBbDAAB7e3vw+XyQJAnFYhGPj4+6IUQE4eekJEm4vb0F8DaFC1mtVphMb3+BLMtotVogItjtdjgcDhiNRvT7fTw8PKzcbhXI5XIhlUqpOkkkEpjP5zg+PoYg/LCfnp5if38fbrdbteB0OsXFxQVubm5+DTIYDKoiYww8z4OIwPO8qhaLxbCzs6Pp3GAwIBqN4vn5Gff390qe0zj/UKsgywqFQqpY843eo1KphHK5DLPZjIODAxiNRqXmdDqxvb2N4XCoD1Sv13F2dqbEs9kM0WhU5bFYLApo7a1bTOZClUpF49na2lKe1wb1ej1VvGqklwdobdB79R/0j4FkWdYYiEh1dawrAcATgF0AaDabyOVyyuE5HA4xmUwAAJlMRjl2ZFlGo9FQLTQajZDNZiGKouKp1WqL8hNLJpPpwWCQ0N3ybySKYpqLxWJHjLFP399s03riOC4dCAQ+fgMeouMzfwx22gAAAABJRU5ErkJggg==',
            'RG':
            'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMfwAADH8BdgxfmQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAM2SURBVEiJtZdLSytJFMd/1dXB+OiQRJEQBEnbPgISJGIQcePKjyBZzoP5AH6OuR9gFjPMznwC1650EbKSy00WPhJ8EMlDtG1M0p2axZCA1871cZM/NDRFnfqdqjrnVJUAODg4+E0IsQ8sA5LRyANKQogve3t7/4hcLvcH8NeIYL5SSv2uAftDHhSl1A/7CCH2dWBxWNBoNIppmgSDQarVKufn53ie59d1RQO0YUBjsRg7OztomkatVmNpaYnNzU2EEH7dNX0YUKUUpmlyd3dHPp9HKcXV1RXT09MIIXyXfihgKSWGYVCpVPoQ27axbXugjS/4HcHh29btdlFKsbq6SiwWA6BQKHB/f/82WEpJKpUiEAi8aA8EAiilaLVaNBoNrq+vabfbr+BCCG5vb3l8fCSTyaDr/ov6KrA8z6NcLtNqtUgkEszPz+M4Ds1mE9u2MQyDjY0Ndnd3mZmZ6ds4jkMoFAKg0Wjw8PCAEIJOp+ML9nWnZ5hIJJBSUiqV+gMIIbAsi3Q6zdbWFoeHh7iuS7lcZn19HcdxsG0by7Ko1+sD93lgcCml6Ha7SClftZ+dnbG4uIhhGMzOznJzc0OlUkHTNBYWFtB1nXq9zunp6aA8/lxUe57H09MThmEwPj7ed+ji4oLLy0uEEHieNyiHPw+WUjI5OYlS6lXE9krmj6DwyaplmiaGYVCtVqnX658Z4u0ZCyGIx+O0223GxsaYm5sjHo9Tq9U4OTn5FPRdYCkly8vLCCEIh8MopSgUCpTLZVzXHR3YdV2Ojo7wPI/t7W1isRjBYPCnoPDOPVZK4XlePz2SySThcHj04J4ajQbFYhEpJZlM5lWOjwwMUCwWaTabRCIR0un08MG9gt/778l1XfL5PJ1OB9M0WVlZGR54amoKy7IIBoPouo5lWYRCof7SNptN8vk83W6XVCrF2toaExMTHwKLXC734vCVUpJOp9F1/cW5rGkapVLpRcGIRqMkk0kikQiO43B8fMzz8/O7wd73Mx90EfArg0opNE178/LwnboaUPID+H2DnPkgFOCbppT68lGrn5VS6k8tm83+DfwKfOX/Z8ao5AFflVK/ZLPZf/8DudZq3wvXLmgAAAAASUVORK5CYII=',
            'tweeter':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMQAADDEBLaRWDgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAK5SURBVEiJpZU7T+NAFEZP4kAePBwZ8TA0EUIRKKIgQhSImnZ/wDYr7Q/barddbRu2QVAQhChAgBQqQClwFEIcx8IYfLdKNk5sJ4GRRh6PPN+Z+90749jZ2VnGsqyfwD6QEREARISocdRcT7dF5AD4lmi327+AL0GCn4QgIhkR+SIiPxIish8k3i/yQVCn7ydEJD0sgjDBMUCZxCg2hQmqqsri4iKKotBoNKhWq741vesSvULpdJrJyUkajcZQy7a2tlheXqa3FQoFzs/PmZubQ9M0Dg8Pu2vivQJLS0sUi0Wy2WykFfl8fgACkEql2N3dJZfLcXFxgaqq/0G9O04mk8RiMba3t8nlcqHRrK6uDkB628TEBDs7O5imORgRgGVZ3Y/z+Tx7e3ssLCz4QMlkkng8HglqNpscHBzw9vbmz1EHVK1WWVtb6wpNTU1RLBZ5f3/HMAzq9TqO40RCAGq1Gq7r+jboK4aZmRmurq7Y3Nz0LVQUBV3X0XV9KATAcZwBy7vlLSJomjbU/1Ha8/PzAMhXdQ8PD5+GABiGEQ1qt9tcXl5+CvL09MTLy8ugdf0T9/f32LbNxsYGs7OzY4Our68DD/oASEQoFApMT0+PDTFNk7u7u0BQPAhULpdxXXds0PHxMZ7nDUBCQa1Wi1KpRKVSwTTNkSAnJyfU6/XQWz4QJCI4jsPj4yOKogyFnJ6ecnt7G3nTd3OUSCSYn59HURRUVUXXdTKZTCTAtm2Ojo585dwP6rx3Qa+vrzSbTdbX11lZWSEWi4UCLMvi5uaGSqWC53mh/67esa/qTNOkXC4DoGka2WyWVCoFgOu6tFotDMPAtu3QXISNA8tbRKjVaoEnPCwPYZZ1nnHP8+ww2Ed7P0RE7LjneX/7ff6ocES0pbiIfBWRP57n2aMAwywKGdsi8ltRlO//AFPkniYXwGRMAAAAAElFTkSuQmCC',
            'udemy':
            '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',
            'youtube':
            'iVBORw0KGgoAAAANSUhEUgAAACEAAAAaCAYAAAA5WTUBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMNwAADDcBracSlQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKkSURBVEiJxZcxSBthFIC/918uGhxMsyptcGjoIgGHW9wSihQKGZrNthHsIC7S1TqWgkRwcZV2lKo0FouixaEuEQ4khIIdrClkEoIZgiae93fQgFgll5jqt93x3v++e3f/3TvhGgYHBx/UarUUEAMeA48A/3WxDagBf4A9EdmsVqsfd3d3j64GyeWDZDJpFAqFKWAC6G6haCPKIjKTzWbfA+4/Ev39/V2dnZ3LwNP/UPwqaycnJy9yuVwFQF2cVIFA4PMdCQAMBQKBL8lk0gAwACzLmtJav7kjgTp95XK5ViwWf0g0Gg36/f7fQLBRltYaEWkU5hkRKTmO06dM03ztRaAuEQ6H0Vq3RUJrHTIM46XR29v7jvNt2BClFEtLS0QiEfb39ymVSrfujIg4Cog0mxiPx1lYWGBycpKenh5c122cdANa64gCHra6QCKRYHl5mfHxcYLBII7jtLJMWAEdrUrUSaVSrK6uMjo6SigU4uzsrJn0DtU4xhs+n4+xsTFWVlYYHh6mq6vL821qm0Qd0zSZmJggk8kQi8XuR6LO1tYW+XzeUzd87S6ey+WYnZ0ln88jIijV+DrbJnF4eMj09DTb29u4rtvU+8MHVLnFDjk+PiadTrO+vs7p6WkrS1R9QAGPb8zLaK2Zm5tjcXGRSqXiqe03sO8D9pqVyGQyzM/PUywWMQzjNgIAv3xa600Ree4l2nEcRkZGODg4QCmFYRi3KQ6AiGzIwMBAt2EYB9znp9y27bLWesZjUtsELkjbtl2ur6osy/qqtX7W7io3ISIb2Wx2CHDrT5TrOE4SWLsjgW+O4yS4mLiv9ldZljWptX6Lx2mrSY6A9M7OzgeuG/kvE41Gg6ZpvhKROPCE85mj5Z8fEfnpuu5313U/2bZdvhr0F9Fo9phaoDu9AAAAAElFTkSuQmCC'
        }
        image = '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'
        footer = '''<div>
                      <div align="center">
                      <img src="data:image/jpg;base64,''' + image + '''
                      </div>
                      <div align="right">
                      <p align="right">
                      <b>''' + self.tr(
            'Author: Leandro Franca', 'Autor: Leandro França'
        ) + '''</b>
                      </p>
                      <a target="_blank" rel="noopener noreferrer" href="https://www.udemy.com/user/leandro-luiz-silva-de-franca/"><img title="Udemy" src="data:image/png;base64,''' + dic_BW[
            'udemy'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/GEOCAPT/"><img title="Facebook" src="data:image/png;base64,''' + dic_BW[
                'face'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/channel/UCLrewDGciytcBG9r0OxTW2w"><img title="Youtube" src="data:image/png;base64,''' + dic_BW[
                    'youtube'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.researchgate.net/profile/Leandro_Franca2"><img title="ResearchGate" src="data:image/png;base64,''' + dic_BW[
                        'RG'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/LEOXINGU"><img title="GitHub" src="data:image/png;base64,''' + dic_BW[
                            'github'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.linkedin.com/in/leandro-fran%C3%A7a-93093714b/"><img title="Linkedin" src="data:image/png;base64,''' + dic_BW[
                                'linkedin'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="http://lattes.cnpq.br/8559852745183879"><img title="Lattes" src="data:image/png;base64,''' + dic_BW[
                                    'lattes'] + '''"></a>
                      </div>
                    </div>'''
        return self.tr(txt_en, txt_pt) + footer

    FOLDER = 'FOLDER'
    NONGEO = 'NONGEO'
    OUTPUT = 'OUTPUT'

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterFile(
                self.FOLDER,
                self.tr('Folder with JPEG photos', 'Pasta com fotos JPEG'),
                behavior=QgsProcessingParameterFile.Folder,
                defaultValue=None))

        self.addParameter(
            QgsProcessingParameterFile(
                self.NONGEO,
                self.tr('Folder to copy the photos without geotag',
                        'Pasta para copiar as fotos sem geotag'),
                behavior=QgsProcessingParameterFile.Folder,
                defaultValue=None,
                optional=True))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Geolocated photos', 'Fotos Geolocalizadas')))

    def processAlgorithm(self, parameters, context, feedback):

        pasta = self.parameterAsFile(parameters, self.FOLDER, context)
        if not pasta:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.FOLDER))

        fotos_nao_geo = self.parameterAsFile(parameters, self.NONGEO, context)

        lista = os.listdir(pasta)
        tam = len(lista)
        copy_ngeo = False
        if os.path.isdir(fotos_nao_geo):
            copy_ngeo = True

        # Funcao para transformar os dados do EXIF em coordenadas em graus decimais
        def coordenadas(exif):
            try:
                ref_lat = exif['GPSInfo'][1][0]
                ref_lon = exif['GPSInfo'][3][0]
                sinal_lat, sinal_lon = 0, 0
                if ref_lat == 'S':
                    sinal_lat = -1
                elif ref_lat == 'N':
                    sinal_lat = 1
                if ref_lon == 'W':
                    sinal_lon = -1
                elif ref_lon == 'E':
                    sinal_lon = 1
                grausLat, grausLon = exif['GPSInfo'][2][0][0], exif['GPSInfo'][
                    4][0][0]
                minLat, minLon = exif['GPSInfo'][2][1][0], exif['GPSInfo'][4][
                    1][0]
                segLat = exif['GPSInfo'][2][2][0] / float(
                    exif['GPSInfo'][2][2][1])
                segLon = exif['GPSInfo'][4][2][0] / float(
                    exif['GPSInfo'][4][2][1])
                if sinal_lat != 0 and sinal_lon != 0:
                    lat = sinal_lat * (float(grausLat) + minLat / 60.0 +
                                       segLat / 3600.0)
                    lon = sinal_lon * (float(grausLon) + minLon / 60.0 +
                                       segLon / 3600.0)
                return lat, lon
            except:
                return 0, 0

        # Funcao para pegar Azimute
        def azimute(exif):
            Az = exif['GPSInfo'][17]
            if isinstance(Az, tuple):
                Az = Az[0] / float(Az[1])
                return Az
            else:
                return Az

        # Funcao para gerar o padrao data-hora
        def data_hora(texto):
            data_hora = texto.replace(' ', ':')
            data_hora = data_hora.split(':')
            ano = int(data_hora[0])
            mes = int(data_hora[1])
            dia = int(data_hora[2])
            hora = int(data_hora[3])
            minuto = int(data_hora[4])
            segundo = int(data_hora[5])
            data_hora = unicode(
                datetime.datetime(ano, mes, dia, hora, minuto, segundo))
            return data_hora

        # Criando Output
        crs = QgsCoordinateReferenceSystem()
        crs.createFromSrid(4326)
        fields = QgsFields()
        fields.append(QgsField(self.tr('name', 'nome'), QVariant.String))
        fields.append(QgsField(self.tr('azimuth', 'azimute'), QVariant.Int))
        fields.append(
            QgsField(self.tr('date_time', 'data_hora'), QVariant.String))
        fields.append(QgsField(self.tr('path', 'caminho'), QVariant.String))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields,
                                               QgsWkbTypes.Point, crs)
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        Percent = 100.0 / tam if tam != 0 else 0
        for index, arquivo in enumerate(lista):
            if (arquivo).lower().endswith(('.jpg', '.jpeg')):
                img = PIL.Image.open(os.path.join(pasta, arquivo))
                if img._getexif():
                    exif = {
                        PIL.ExifTags.TAGS[k]: v
                        for k, v in img._getexif().items()
                        if k in PIL.ExifTags.TAGS
                    }
                else:
                    exif = {}
                lon, lat = 0, 0
                Az = None
                date_time = None
                if 'GPSInfo' in exif:
                    lat, lon = coordenadas(exif)
                    if 17 in exif['GPSInfo']:
                        Az = azimute(exif)
                if 'DateTimeOriginal' in exif:
                    date_time = data_hora(exif['DateTimeOriginal'])
                elif 'DateTime' in exif:
                    date_time = data_hora(exif['DateTime'])
                if lon != 0:
                    feature = QgsFeature(fields)
                    feature.setGeometry(
                        QgsGeometry.fromPointXY(QgsPointXY(lon, lat)))
                    feature.setAttributes(
                        [arquivo, Az, date_time,
                         os.path.join(pasta, arquivo)])
                    sink.addFeature(feature, QgsFeatureSink.FastInsert)
                else:
                    print()
                    feedback.pushInfo(
                        self.tr(
                            'The file "{}" has no geotag!'.format(arquivo),
                            'A imagem "{}" não possui geotag!'.format(
                                arquivo)))
                    if copy_ngeo:
                        shutil.copy2(os.path.join(pasta, arquivo),
                                     os.path.join(fotos_nao_geo, arquivo))
            if feedback.isCanceled():
                break
            feedback.setProgress(int((index + 1) * Percent))

        feedback.pushInfo(
            self.tr('Operation completed successfully!',
                    'Operação finalizada com sucesso!'))
        feedback.pushInfo(
            self.tr('Leandro Franca - Cartographic Engineer',
                    'Leandro França - Eng Cart'))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 15
0
 def initGui(self):
     if QgsApplication.locale() in ['fr','FR']:
         self.switchLang('fr')
     self.initProcessing()
Ejemplo n.º 16
0
class InventoryRaster(QgsProcessingAlgorithm):

    LOC = QgsApplication.locale()

    def translate(self, string):
        return QCoreApplication.translate('Processing', string)

    def tr(self, *string):
        # Traduzir para o portugês: arg[0] - english (translate), arg[1] - português
        if self.LOC == 'pt':
            if len(string) == 2:
                return string[1]
            else:
                return self.translate(string[0])
        else:
            return self.translate(string[0])

    def createInstance(self):
        return InventoryRaster()

    def name(self):
        return 'inventoryraster'

    def displayName(self):
        return self.tr('Raster data inventory', 'Inventário de dados raster')

    def group(self):
        return self.tr('LF Raster')

    def groupId(self):
        return 'lf_raster'

    def shortHelpString(self):
        txt_en = 'Creates a vector layer with the inventory of raster files in a folder. The geometry type of the features of this layer can be Polygon (bounding box) or Point (centroid).'
        txt_pt = 'Cria uma camada vetorial com o inventário de arquivos raster de uma pasta. O tipo de geometria das feições dessa camada pode ser Polígono (retângulo envolvente) ou Ponto (centroide).'
        dic_BW = {
            'face':
            'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMwAADDMBUlqVhwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIUSURBVEiJvZa9quJAGIbfMfEYTUSUQAqLYOnehJobsBTlHLbRQtKKlY3lObegxRa7pRdg7y0saiuoKAErMZif2Squ2eTEOCfsC1Pk+3uYb2YyQwCg1+u9UUqHAKoAOCQrB8ASwPt0Ov1JdF3/bprmj4QhoXp5eXnjTdMcUEr/Bw+WZQ15Smn1q4UIIZBlGaIoghBys2+3W1yv19u367rfeAAc6ww5jkOz2USj0YAoigH/aDTCbrfzpfCUUrAC2+02NE2LjPm3NjNQkiTU6/WHsFAgi1RVRSqVCthd171BwmozA/P5fMA2n88xm81g2zYAwHGccCAL9H43elosFrhcLpE5zMCwHMdxImtRSp8DptNpZDIZAIAgCAG/IAi+42Ga5q29nkin06FxgZqmodvtxooFgPF4jPV67bM9NcNnW384HJI7h49kWRZOp9PXgOfzGfv9HgCQy+VQKBR8fsMwYFkWAOB4PMJ13UAN0mq1Yq/hfVytVoOu6z7/YDDAZrP5Wzzk6CR6LOLEJLqGcWrxYX2OW5wJmOQOjQX0ApOERgIrlQoTUJblgK1cLodeWZ4IIeDDngZx5P1T75XNZiFJUmQeTyl1wPAW/awrD7rlpDiOW3qL/cz4DBY1CCG/U4qifDw7O1YpivJBAGAymbwahjG0bbtKKeXjJJdKJaiq6rOtVqvAjU8IsTmOWxaLxfd+v//rD1H2cZ8dKhk8AAAAAElFTkSuQmCC',
            'github':
            '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',
            'instagram':
            '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',
            'lattes':
            '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',
            'linkedin':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6QAADOkBmiiHWwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJRSURBVEiJ7ZU/aCJREMa/t7soa5LOoGihktZOQSxSWykBIYVaWZ6NVa6wSZ/WI5BGziJypA1CQBLsgkKKgBjFQkEtkouJ+AdxdeeKnHvurXdcXK84uK/amfl4v9m3s++xbrdrury8PGm324dEZMYGxRj7arPZvoRCoSPh/Pz8pFqtftgkYEnmTqeT6PV6xDscjs9EZPpLIADAy8uLS5jP52bGGACA4ziEw2H4/X5IkoSrqytcX1/rBhHRrrCcCAaDCAQCShyJRPD6+oq7uzvdMG458Hg8GoPX69UN0YDG47HGsCqnG5TP50FESjyZTFAoFDYCYvF4nBbDAAB7e3vw+XyQJAnFYhGPj4+6IUQE4eekJEm4vb0F8DaFC1mtVphMb3+BLMtotVogItjtdjgcDhiNRvT7fTw8PKzcbhXI5XIhlUqpOkkkEpjP5zg+PoYg/LCfnp5if38fbrdbteB0OsXFxQVubm5+DTIYDKoiYww8z4OIwPO8qhaLxbCzs6Pp3GAwIBqN4vn5Gff390qe0zj/UKsgywqFQqpY843eo1KphHK5DLPZjIODAxiNRqXmdDqxvb2N4XCoD1Sv13F2dqbEs9kM0WhU5bFYLApo7a1bTOZClUpF49na2lKe1wb1ej1VvGqklwdobdB79R/0j4FkWdYYiEh1dawrAcATgF0AaDabyOVyyuE5HA4xmUwAAJlMRjl2ZFlGo9FQLTQajZDNZiGKouKp1WqL8hNLJpPpwWCQ0N3ybySKYpqLxWJHjLFP399s03riOC4dCAQ+fgMeouMzfwx22gAAAABJRU5ErkJggg==',
            'RG':
            'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMfwAADH8BdgxfmQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAM2SURBVEiJtZdLSytJFMd/1dXB+OiQRJEQBEnbPgISJGIQcePKjyBZzoP5AH6OuR9gFjPMznwC1650EbKSy00WPhJ8EMlDtG1M0p2axZCA1871cZM/NDRFnfqdqjrnVJUAODg4+E0IsQ8sA5LRyANKQogve3t7/4hcLvcH8NeIYL5SSv2uAftDHhSl1A/7CCH2dWBxWNBoNIppmgSDQarVKufn53ie59d1RQO0YUBjsRg7OztomkatVmNpaYnNzU2EEH7dNX0YUKUUpmlyd3dHPp9HKcXV1RXT09MIIXyXfihgKSWGYVCpVPoQ27axbXugjS/4HcHh29btdlFKsbq6SiwWA6BQKHB/f/82WEpJKpUiEAi8aA8EAiilaLVaNBoNrq+vabfbr+BCCG5vb3l8fCSTyaDr/ov6KrA8z6NcLtNqtUgkEszPz+M4Ds1mE9u2MQyDjY0Ndnd3mZmZ6ds4jkMoFAKg0Wjw8PCAEIJOp+ML9nWnZ5hIJJBSUiqV+gMIIbAsi3Q6zdbWFoeHh7iuS7lcZn19HcdxsG0by7Ko1+sD93lgcCml6Ha7SClftZ+dnbG4uIhhGMzOznJzc0OlUkHTNBYWFtB1nXq9zunp6aA8/lxUe57H09MThmEwPj7ed+ji4oLLy0uEEHieNyiHPw+WUjI5OYlS6lXE9krmj6DwyaplmiaGYVCtVqnX658Z4u0ZCyGIx+O0223GxsaYm5sjHo9Tq9U4OTn5FPRdYCkly8vLCCEIh8MopSgUCpTLZVzXHR3YdV2Ojo7wPI/t7W1isRjBYPCnoPDOPVZK4XlePz2SySThcHj04J4ajQbFYhEpJZlM5lWOjwwMUCwWaTabRCIR0un08MG9gt/778l1XfL5PJ1OB9M0WVlZGR54amoKy7IIBoPouo5lWYRCof7SNptN8vk83W6XVCrF2toaExMTHwKLXC734vCVUpJOp9F1/cW5rGkapVLpRcGIRqMkk0kikQiO43B8fMzz8/O7wd73Mx90EfArg0opNE178/LwnboaUPID+H2DnPkgFOCbppT68lGrn5VS6k8tm83+DfwKfOX/Z8ao5AFflVK/ZLPZf/8DudZq3wvXLmgAAAAASUVORK5CYII=',
            'tweeter':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMQAADDEBLaRWDgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAK5SURBVEiJpZU7T+NAFEZP4kAePBwZ8TA0EUIRKKIgQhSImnZ/wDYr7Q/barddbRu2QVAQhChAgBQqQClwFEIcx8IYfLdKNk5sJ4GRRh6PPN+Z+90749jZ2VnGsqyfwD6QEREARISocdRcT7dF5AD4lmi327+AL0GCn4QgIhkR+SIiPxIish8k3i/yQVCn7ydEJD0sgjDBMUCZxCg2hQmqqsri4iKKotBoNKhWq741vesSvULpdJrJyUkajcZQy7a2tlheXqa3FQoFzs/PmZubQ9M0Dg8Pu2vivQJLS0sUi0Wy2WykFfl8fgACkEql2N3dJZfLcXFxgaqq/0G9O04mk8RiMba3t8nlcqHRrK6uDkB628TEBDs7O5imORgRgGVZ3Y/z+Tx7e3ssLCz4QMlkkng8HglqNpscHBzw9vbmz1EHVK1WWVtb6wpNTU1RLBZ5f3/HMAzq9TqO40RCAGq1Gq7r+jboK4aZmRmurq7Y3Nz0LVQUBV3X0XV9KATAcZwBy7vlLSJomjbU/1Ha8/PzAMhXdQ8PD5+GABiGEQ1qt9tcXl5+CvL09MTLy8ugdf0T9/f32LbNxsYGs7OzY4Our68DD/oASEQoFApMT0+PDTFNk7u7u0BQPAhULpdxXXds0PHxMZ7nDUBCQa1Wi1KpRKVSwTTNkSAnJyfU6/XQWz4QJCI4jsPj4yOKogyFnJ6ecnt7G3nTd3OUSCSYn59HURRUVUXXdTKZTCTAtm2Ojo585dwP6rx3Qa+vrzSbTdbX11lZWSEWi4UCLMvi5uaGSqWC53mh/67esa/qTNOkXC4DoGka2WyWVCoFgOu6tFotDMPAtu3QXISNA8tbRKjVaoEnPCwPYZZ1nnHP8+ww2Ed7P0RE7LjneX/7ff6ocES0pbiIfBWRP57n2aMAwywKGdsi8ltRlO//AFPkniYXwGRMAAAAAElFTkSuQmCC',
            'udemy':
            'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAdCAYAAAC9pNwMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMOgAADDoBpJd/BgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAYHSURBVEiJlZdbTFNbGsd/e/cCFtpgxX0sjHgJoepBJcqMl4gGjoYTb5PRYZxEMmq8cI5Oosw8ODG+iJpMHGN0Hsz4QHwwmRgLHpJjqkDqQZ+QWEWisdEIRZoKtAKFFuxt73nQ3aG05pT/2/rW5fd93/rW2msL/Lo0gO727dtlc+fO/cFoNK41mUzzjEajMR6Py6Ojo4FgMPjR4/E8fv369fULFy70A1EgnsHaaSVarVbj3bt3L/b3948pGSgWiylPnz71nj9//kcgBxBnC9Vfv379QG9vb0bAmZJlWXn06NFATU1NFaDPFDrn5s2b/56ampIVRVGCwaDS2NioNDQ0KG1tbbNy4MOHD7H6+vp/AHNmQjQzoXfu3Gnav39/rVarFQCam5vp6ekhHA7jdrtZs2YNBoMhowhyc3PFLVu2fOf3++c6nc5fgFg6sP7atWv/OnjwYK0gCAD4/X5aWlqQZRkARVEoLS3FbDZnmj20Wq2wcePG375582bM5XI5+VJ06uaLdXV1fzh27NhfRfH/9dDe3k40Gk20BUFAp9N9FdLd3c2VK1e4desWk5OTCbvZbBYaGhr+aTQaf6cyVYqhpqbmWnZ2tqAO7u3t5fnz5zO9Z968eWmhIyMj2Gw2vF4vL168wOFwJPWvXLlSe/z48f8ABhWsPXXq1Mmqqqpv1EHhcBibzZZIsSpJksjNzU0LfvjwIeFwONF2uVwoipI05uTJkyskSfo9oBUB/fr16+vUfVUUhaamJnw+X8riJSUlaaHDw8M4nc4kWzAYTNomAIvFIuzYsePvgF7MysqyVFRUFKqdbW1tdHd3pwVYrda09pm1ACDLckrGAKqqqr4FFmh37979l4KCAlFRFFpbW3E4HCkpAtDr9SxatCjF7vF46OnpSbGnWwOgsrJSB3ynlSTp28nJycR5/dqEoqKitBXd2tpKPJ56LSuKkjZii8UiZGVlWUWz2fwbv9+fBJ1+pFQtXbo0xfbu3TtcLhcA8+fPT5ony3LaIERRZMmSJUtEILeoqIjt27djtVrZtm0bmzdvTpmwePHilIju3buHoigIgsCmTZuS+mOxGJFIJGUOQFZWlln0+/1+gMrKSo4ePUp1dXVKRQuCQEFBQZKts7OTgYEBABYuXMiKFStQT4YKmX6JuN1uLl26xPj4OH19fV7t8PDwwMzoPB5PUttoNJKTk5Noj42Ncf/+/YRTW7duJScnB51Ol7Tfb9++JT8/n66uLux2O9FolIGBAWV8fNyjffz4cWcoFKpVFw6FQoRCoSSwwWBg+jlvbm5ORFNcXMzy5csRBAFJknj//n1int1ux+FwMDU1BYDJZKKvr08B3oo+n8/e3t6eOISRSCSlGqcXSUdHR6KgNBoNO3fuTDhVXl6eNE+W5QRUEASqq6ux2+2fAIcIDLW3t7vUwRqNJmmvAD5+/Mjg4CBOp5MHDx4kHKmoqKCwMHH3sG7dOpYtW8ZMZWdns2fPHsrKyrDb7Z3AkABo9Xr93q6urv+uXr1alGWZc+fOpaR7eqrhc0GdOHECrVabkp1Xr17hdruRZZkFCxZQWlqKwWDg6tWr0fr6+v3ATxpAjsfj/YFA4Pu9e/cWCILA0NAQXq83xXNVeXl5HDlyJKngpjsoSRIlJSVYrVYKCwvR6XSMjo5SW1vbMTExcQH4pD4EYi9fvnxZWFj457Vr1+osFgvPnj1LuX/h8xfq8OHD5Ofnf9WxmZJlmQMHDow9efLkCOAGFBWs8Hmv+8vLy3etWrVKLC4uxuv1MjExgSAImEwmNmzYwL59+8jLy8sYCnD27NlPN27cqAMcTHv+TNcci8VypqWlJaK+FgOBgDIyMqJEo9FZvzZjsZhy5syZSeBvpHnwpcA1Gs2fLl68OBaJRGYNU+Xz+eRdu3YNA3/MBKpKD5SVlpb+3NjYGI7FYhkDg8Ggcvny5U+SJDUDZcziXa1K5PPfQGVZWdnPp0+fnujo6IgHAoEU2ODgoGyz2WKHDh0as1gsTcAWfuVPQvhaxzRpAB1gBNZ9iUISRfGbLzfcIDAMvACeABNk8O/0PwJCxMb99V7LAAAAAElFTkSuQmCC',
            'youtube':
            'iVBORw0KGgoAAAANSUhEUgAAACEAAAAaCAYAAAA5WTUBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMNwAADDcBracSlQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKkSURBVEiJxZcxSBthFIC/918uGhxMsyptcGjoIgGHW9wSihQKGZrNthHsIC7S1TqWgkRwcZV2lKo0FouixaEuEQ4khIIdrClkEoIZgiae93fQgFgll5jqt93x3v++e3f/3TvhGgYHBx/UarUUEAMeA48A/3WxDagBf4A9EdmsVqsfd3d3j64GyeWDZDJpFAqFKWAC6G6haCPKIjKTzWbfA+4/Ev39/V2dnZ3LwNP/UPwqaycnJy9yuVwFQF2cVIFA4PMdCQAMBQKBL8lk0gAwACzLmtJav7kjgTp95XK5ViwWf0g0Gg36/f7fQLBRltYaEWkU5hkRKTmO06dM03ztRaAuEQ6H0Vq3RUJrHTIM46XR29v7jvNt2BClFEtLS0QiEfb39ymVSrfujIg4Cog0mxiPx1lYWGBycpKenh5c122cdANa64gCHra6QCKRYHl5mfHxcYLBII7jtLJMWAEdrUrUSaVSrK6uMjo6SigU4uzsrJn0DtU4xhs+n4+xsTFWVlYYHh6mq6vL821qm0Qd0zSZmJggk8kQi8XuR6LO1tYW+XzeUzd87S6ey+WYnZ0ln88jIijV+DrbJnF4eMj09DTb29u4rtvU+8MHVLnFDjk+PiadTrO+vs7p6WkrS1R9QAGPb8zLaK2Zm5tjcXGRSqXiqe03sO8D9pqVyGQyzM/PUywWMQzjNgIAv3xa600Ree4l2nEcRkZGODg4QCmFYRi3KQ6AiGzIwMBAt2EYB9znp9y27bLWesZjUtsELkjbtl2ur6osy/qqtX7W7io3ISIb2Wx2CHDrT5TrOE4SWLsjgW+O4yS4mLiv9ldZljWptX6Lx2mrSY6A9M7OzgeuG/kvE41Gg6ZpvhKROPCE85mj5Z8fEfnpuu5313U/2bZdvhr0F9Fo9phaoDu9AAAAAElFTkSuQmCC'
        }
        footer = '''<div align="right">
                      <p align="right"><b>''' + self.tr(
            'Author: Leandro Franca', 'Autor: Leandro França'
        ) + '''</b></p>
                      <div align="right">
                      <a target="_blank" rel="noopener noreferrer" href="https://www.udemy.com/user/leandro-luiz-silva-de-franca/"><img title="Udemy" src="data:image/png;base64,''' + dic_BW[
            'udemy'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/GEOCAPT/"><img title="Facebook" src="data:image/png;base64,''' + dic_BW[
                'face'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/channel/UCLrewDGciytcBG9r0OxTW2w"><img title="Youtube" src="data:image/png;base64,''' + dic_BW[
                    'youtube'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.researchgate.net/profile/Leandro_Franca2"><img title="ResearchGate" src="data:image/png;base64,''' + dic_BW[
                        'RG'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/LEOXINGU"><img title="GitHub" src="data:image/png;base64,''' + dic_BW[
                            'github'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.linkedin.com/in/leandro-fran%C3%A7a-93093714b/"><img title="Linkedin" src="data:image/png;base64,''' + dic_BW[
                                'linkedin'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="http://lattes.cnpq.br/8559852745183879"><img title="Lattes" src="data:image/png;base64,''' + dic_BW[
                                    'lattes'] + '''"></a>
                      </div>
                    </div>'''
        return self.tr(txt_en, txt_pt) + footer

    FOLDER = 'FOLDER'
    SUBFOLDER = 'SUBFOLDER'
    FORMAT = 'FORMAT'
    GEOMETRY = 'GEOMETRY'
    OUTPUT = 'OUTPUT'
    CRS = 'CRS'

    def initAlgorithm(self, config=None):
        # INPUT
        self.addParameter(
            QgsProcessingParameterFile(
                self.FOLDER,
                self.tr('Folder with raster files',
                        'Pasta com arquivos raster'),
                behavior=QgsProcessingParameterFile.Folder,
                defaultValue=None))

        self.addParameter(
            QgsProcessingParameterBoolean(self.SUBFOLDER,
                                          self.tr('Check subfolders',
                                                  'Verificar sub-pastas'),
                                          defaultValue=False))

        self.addParameter(
            QgsProcessingParameterString(self.FORMAT,
                                         self.tr('Format', 'Formato'),
                                         defaultValue='.tif'))

        self.addParameter(
            QgsProcessingParameterEnum(self.GEOMETRY,
                                       self.tr('Geometry', 'Geometria'),
                                       options=[
                                           self.tr('Polygon', 'Polígono'),
                                           self.tr('Point', 'Ponto')
                                       ],
                                       defaultValue=0))

        self.addParameter(
            QgsProcessingParameterCrs(self.CRS, self.tr('CRS', 'SRC'),
                                      'ProjectCrs'))

        # OUTPUT
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT, self.tr('Inventory Layer',
                                     'Camada de Inventário')))

    def processAlgorithm(self, parameters, context, feedback):

        pasta = self.parameterAsFile(parameters, self.FOLDER, context)
        if not pasta:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.FOLDER))

        subpasta = self.parameterAsBool(parameters, self.SUBFOLDER, context)

        formato = self.parameterAsString(parameters, self.FORMAT, context)

        geometria = self.parameterAsEnum(parameters, self.GEOMETRY, context)

        crs = self.parameterAsCrs(parameters, self.CRS, context)

        # OUTPUT
        GeomType = QgsWkbTypes.Point if geometria == 1 else QgsWkbTypes.Polygon
        Fields = QgsFields()
        itens = {
            self.tr('name', 'nome'): QVariant.String,
            self.tr('extension', 'extensão'): QVariant.String,
            self.tr('path', 'caminho'): QVariant.String,
            self.tr('resX'): QVariant.Double,
            self.tr('resY'): QVariant.Double,
            self.tr('n_cols'): QVariant.Int,
            self.tr('n_rows', 'n_lin'): QVariant.Int,
            self.tr('crs', 'src'): QVariant.String,
            self.tr('n_bands', 'n_bandas'): QVariant.Int,
            self.tr('dataType', 'tipoDado'): QVariant.String,
        }
        for item in itens:
            Fields.append(QgsField(item, itens[item]))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, Fields, GeomType, crs)
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        # Listar Arquivos
        feedback.pushInfo(
            self.tr('Checking files in the folder...',
                    'Checando arquivos na pasta...'))
        lista = []
        if subpasta:
            for root, dirs, files in os.walk(pasta, topdown=True):
                for name in files:
                    if name[-1 * len(formato):] == formato:
                        lista += [os.path.join(root, name)]
        else:
            for item in os.listdir(pasta):
                if item[-1 * len(formato):] == formato:
                    lista += [os.path.join(pasta, item)]

        total = 100.0 / len(lista) if len(lista) > 0 else 0

        # Obter dados dos arquivos listados
        feedback.pushInfo(
            self.tr('Creating raster files...',
                    'Criando inventário de arquivos raster...'))
        for current, file_path in enumerate(lista):
            image = gdal.Open(file_path)  # https://gdal.org/python/
            prj = image.GetProjection()  # wkt
            ulx, xres, xskew, uly, yskew, yres = image.GetGeoTransform()
            GDT = image.GetRasterBand(1).DataType
            n_bands = image.RasterCount
            cols = image.RasterXSize  # Number of columns
            rows = image.RasterYSize  # Number of rows
            CRS = QgsCoordinateReferenceSystem(prj)  # Create CRS
            image = None  # Close image

            # Creating BBox
            coord = [[
                QgsPointXY(ulx, uly),
                QgsPointXY(ulx + cols * xres, uly),
                QgsPointXY(ulx + cols * xres, uly + rows * yres),
                QgsPointXY(ulx, uly + rows * yres),
                QgsPointXY(ulx, uly)
            ]]
            geom = QgsGeometry.fromPolygonXY(coord)

            # CRS transformation
            coordinateTransformer = QgsCoordinateTransform()
            coordinateTransformer.setDestinationCrs(crs)
            coordinateTransformer.setSourceCrs(CRS)
            geom_transf = self.reprojectPoints(geom, coordinateTransformer)

            # Attributes
            path, file = os.path.split(file_path)
            name = os.path.splitext(file)[0]
            extension = os.path.splitext(file)[1]
            att = [
                name, extension, path,
                abs(xres),
                abs(yres), cols, rows,
                CRS.description(), n_bands,
                gdal.GetDataTypeName(GDT)
            ]

            # Saving feature
            feat = QgsFeature()
            feat.setGeometry(geom_transf.centroid()
                             ) if geometria == 1 else feat.setGeometry(
                                 geom_transf)  # centroid or polygon
            feat.setAttributes(att)
            sink.addFeature(feat, QgsFeatureSink.FastInsert)

            if feedback.isCanceled():
                break
            feedback.setProgress(int((current + 1) * total))

        feedback.pushInfo(
            self.tr('Operation completed successfully!',
                    'Operação finalizada com sucesso!'))
        feedback.pushInfo('Leandro França - Eng Cart')
        return {self.OUTPUT: dest_id}

    def reprojectPoints(self, geom, xform):
        if geom.type() == 0:  #Point
            if geom.isMultipart():
                pnts = geom.asMultiPoint()
                newPnts = []
                for pnt in pnts:
                    newPnts += [xform.transform(pnt)]
                newGeom = QgsGeometry.fromMultiPointXY(newPnts)
                return newGeom
            else:
                pnt = geom.asPoint()
                newPnt = xform.transform(pnt)
                newGeom = QgsGeometry.fromPointXY(newPnt)
                return newGeom
        elif geom.type() == 1:  #Line
            if geom.isMultipart():
                linhas = geom.asMultiPolyline()
                newLines = []
                for linha in linhas:
                    newLine = []
                    for pnt in linha:
                        newLine += [xform.transform(pnt)]
                    newLines += [newLine]
                newGeom = QgsGeometry.fromMultiPolylineXY(newLines)
                return newGeom
            else:
                linha = geom.asPolyline()
                newLine = []
                for pnt in linha:
                    newLine += [xform.transform(pnt)]
                newGeom = QgsGeometry.fromPolylineXY(newLine)
                return newGeom
        elif geom.type() == 2:  #Polygon
            if geom.isMultipart():
                poligonos = geom.asMultiPolygon()
                newPolygons = []
                for pol in poligonos:
                    newPol = []
                    for anel in pol:
                        newAnel = []
                        for pnt in anel:
                            newAnel += [xform.transform(pnt)]
                        newPol += [newAnel]
                    newPolygons += [newPol]
                newGeom = QgsGeometry.fromMultiPolygonXY(newPolygons)
                return newGeom
            else:
                pol = geom.asPolygon()
                newPol = []
                for anel in pol:
                    newAnel = []
                    for pnt in anel:
                        newAnel += [xform.transform(pnt)]
                    newPol += [newAnel]
                newGeom = QgsGeometry.fromPolygonXY(newPol)
                return newGeom
        else:
            return None
Ejemplo n.º 17
0
class ExtendLines(QgsProcessingAlgorithm):

    LOC = QgsApplication.locale()

    def translate(self, string):
        return QCoreApplication.translate('Processing', string)

    def tr(self, *string):
        # Traduzir para o portugês: arg[0] - english (translate), arg[1] - português
        if self.LOC == 'pt':
            if len(string) == 2:
                return string[1]
            else:
                return self.translate(string[0])
        else:
            return self.translate(string[0])

    def createInstance(self):
        return ExtendLines()

    def name(self):
        return 'extendlines'

    def displayName(self):
        return self.tr('Extend lines', 'Estender linhas')

    def group(self):
        return self.tr('LF Vector', 'LF Vetor')

    def groupId(self):
        return 'lf_vector'

    def shortHelpString(self):
        txt_en = 'Extends lines at their <b>start</b> and/or <b>end</b> points.'
        txt_pt = 'Estende linhas nos seus pontos inicial e/ou final.'
        dic_BW = {
            'face':
            'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMwAADDMBUlqVhwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIUSURBVEiJvZa9quJAGIbfMfEYTUSUQAqLYOnehJobsBTlHLbRQtKKlY3lObegxRa7pRdg7y0saiuoKAErMZif2Squ2eTEOCfsC1Pk+3uYb2YyQwCg1+u9UUqHAKoAOCQrB8ASwPt0Ov1JdF3/bprmj4QhoXp5eXnjTdMcUEr/Bw+WZQ15Smn1q4UIIZBlGaIoghBys2+3W1yv19u367rfeAAc6ww5jkOz2USj0YAoigH/aDTCbrfzpfCUUrAC2+02NE2LjPm3NjNQkiTU6/WHsFAgi1RVRSqVCthd171BwmozA/P5fMA2n88xm81g2zYAwHGccCAL9H43elosFrhcLpE5zMCwHMdxImtRSp8DptNpZDIZAIAgCAG/IAi+42Ga5q29nkin06FxgZqmodvtxooFgPF4jPV67bM9NcNnW384HJI7h49kWRZOp9PXgOfzGfv9HgCQy+VQKBR8fsMwYFkWAOB4PMJ13UAN0mq1Yq/hfVytVoOu6z7/YDDAZrP5Wzzk6CR6LOLEJLqGcWrxYX2OW5wJmOQOjQX0ApOERgIrlQoTUJblgK1cLodeWZ4IIeDDngZx5P1T75XNZiFJUmQeTyl1wPAW/awrD7rlpDiOW3qL/cz4DBY1CCG/U4qifDw7O1YpivJBAGAymbwahjG0bbtKKeXjJJdKJaiq6rOtVqvAjU8IsTmOWxaLxfd+v//rD1H2cZ8dKhk8AAAAAElFTkSuQmCC',
            'github':
            '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',
            'instagram':
            'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMOQAADDkBCS5eawAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAYPSURBVEiJlZZNaBRbFsd/VXXrI23sDtrRCQkkGkleFHlKiEj8wIgTF0Igs4nzcDFjCOqmdwrPjczmCW4MuDEoM7pQlw6unPEZhRCVKL6gsUmItKhh8tV+JN2d/qiuurNwbk118h7MHLjU17nnf//3/s85pfEfu3z5clsymfxRSvl7TdM2Syk1KSVhU8/q6vt+cJVSIqUM7n3fl8CclPLn3bt3/3T+/PlJAA3g3Llzf5ibm7sFOJqmVQQOP68G/a0RNk3TAApNTU1/vHTp0t+1oaGh7x49evQL4CiAMFgY5NdYhv1+BSj8nO/p6fle6+vr+1upVPrTaucNGzZw5MgR2traiMfj2LaNYRjoul4RyPd9PM+jUCiQTqdJJpMMDw+zvLy8BjgSifxV6+3tnZFS1ofBuru7GRgYwDCMNSv/X6xUKnH16lVGR0crQKWUMwL4XXjVnZ2dnD59GiklDx48YHR0lPn5eQqFAp7nBaJQpus6hmEQiUSoq6vjwIEDHDp0iEQiQTab5dWrV+GdqzO2b9/+F03T0DQNIQQXLlzAcRwGBwe5d+8e6XSaQqFAuVxeA+j7Pr7v47ou+XwegJGRET5//kx7ezutra3cv38/DPiNmjr4HTt2EIvFSCaTPH36NHD8DeWhaRq6rqPrOj09PVy5coWzZ8/y8OFD3r9/z6ZNm2hqaqoQnwgf6rZt2wAYHx+vOGx1H4vFOHjwIFu2bMH3fVKpFCMjI+RyOTZu3AgQXMfHx2lsbKS5uZl3794FcYS60TSNWCwGwKdPn9YIoauri5MnT2KaZvBu//799PX1MTQ0xJ07d0ilUrx58wZd1/ny5QsA0Wi0Io5QYAC2bQPfVBZmt3fvXk6dOgXA48ePefHiRfB+3759JBIJLl68yMjISBCrWCxWxAy2NIyu0sDzvGCiEIL+/n4ABgcHefbsWeD//PlzkskkAwMDDAwMkEgkAj14ngdQkbeapqErhuGkNgwDy7KwbZtdu3axfv16JiYmGBsbW1NBhoeHSaVSxONxmpubAyaqzoYBpZTopmli2zaO4wQMLcuiqqoKx3FobGwEYGpqCsdxsG0b0zSDQFJKJiYmAGhoaGC1KSUHO+Y4DkIIdF1HCBEAOo5TMXHdunXYto2UEs/zgrwsl8uBr+u6awBVSqkmoKtVO44TrNo0zYDN7OwsAO3t7UQiEWzbDvwtyyISibBnzx4Apqen1wCGTdd1dDU5DBgOuri4yOzsLLW1tRw/fpyqqips28ayLKqrqzlz5gw1NTUkk0kWFhYqGIUZKhOqC4QLtWKn+tvdu3fp7++no6OD1tZWpqamAGhra6O6uppsNsuNGzcwTRPXdZFSBvlaLpcrWpiwbTsowErKkUgEy7KCFWYyGW7evMmxY8eor6+no6MjWNzbt2+5desW2Ww2mOO6LlVVVQAUCoVKhqZpBqLJ5XIAxOPxCkAFevv2bWpqaqitrcXzPD5+/MjCwgKu62KaZkXXr6urA/5btRRLYVlWsKXz8/MAtLa28vLlyyCXVBDP88hkMnz9+pVyuUy5XA62LgxmGEYgpMnJyUqGlmUhhMAwDHK5HOl0mng8TldXF2NjYxU/RyoNXNelVCpVCEMNXdc5ceIE0WiU169fk06n14jGM03TMAwDIQRjY2McPXqUlpYWGhoamJmZIZfL4bourusGoGookQghqKmpYefOncRiMTKZDNeuXVv9b+QJx3HmTNOstywL0zTxfZ8nT54EJa2lpYX/11KpFNevX2dpaWl1m/uXiMVi/wT+rEqWEALf9xkfHycajRKNRlFKBsL/nQHLUqlEPp9ncXGRyclJPnz4QLFYDOqz0sLmzZv/oaVSqdbp6elfTNOsMk0TtbXhtqWAFIg6w2KxSKFQYGVlhVwuRzabJZPJkM/ng2+FQgHXddE0bSWRSHwvtm7dOpVMJn9YWlq6I4RwVIqEC+5qQCUy5RNWsTpr13XDcfLd3d0/dHZ2vg0iptPp7+bm5n7UNO2IEKJO+2Zr0iKs1GKxSLFYJJfLsbKyQiaTYXl5WQ25srIya9v2z729vT8dPnx4CuDfBIhl1RKmcgQAAAAASUVORK5CYII=',
            'lattes':
            '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',
            'linkedin':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6QAADOkBmiiHWwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJRSURBVEiJ7ZU/aCJREMa/t7soa5LOoGihktZOQSxSWykBIYVaWZ6NVa6wSZ/WI5BGziJypA1CQBLsgkKKgBjFQkEtkouJ+AdxdeeKnHvurXdcXK84uK/amfl4v9m3s++xbrdrury8PGm324dEZMYGxRj7arPZvoRCoSPh/Pz8pFqtftgkYEnmTqeT6PV6xDscjs9EZPpLIADAy8uLS5jP52bGGACA4ziEw2H4/X5IkoSrqytcX1/rBhHRrrCcCAaDCAQCShyJRPD6+oq7uzvdMG458Hg8GoPX69UN0YDG47HGsCqnG5TP50FESjyZTFAoFDYCYvF4nBbDAAB7e3vw+XyQJAnFYhGPj4+6IUQE4eekJEm4vb0F8DaFC1mtVphMb3+BLMtotVogItjtdjgcDhiNRvT7fTw8PKzcbhXI5XIhlUqpOkkkEpjP5zg+PoYg/LCfnp5if38fbrdbteB0OsXFxQVubm5+DTIYDKoiYww8z4OIwPO8qhaLxbCzs6Pp3GAwIBqN4vn5Gff390qe0zj/UKsgywqFQqpY843eo1KphHK5DLPZjIODAxiNRqXmdDqxvb2N4XCoD1Sv13F2dqbEs9kM0WhU5bFYLApo7a1bTOZClUpF49na2lKe1wb1ej1VvGqklwdobdB79R/0j4FkWdYYiEh1dawrAcATgF0AaDabyOVyyuE5HA4xmUwAAJlMRjl2ZFlGo9FQLTQajZDNZiGKouKp1WqL8hNLJpPpwWCQ0N3ybySKYpqLxWJHjLFP399s03riOC4dCAQ+fgMeouMzfwx22gAAAABJRU5ErkJggg==',
            'RG':
            'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAcCAYAAAB2+A+pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMfwAADH8BdgxfmQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAM2SURBVEiJtZdLSytJFMd/1dXB+OiQRJEQBEnbPgISJGIQcePKjyBZzoP5AH6OuR9gFjPMznwC1650EbKSy00WPhJ8EMlDtG1M0p2axZCA1871cZM/NDRFnfqdqjrnVJUAODg4+E0IsQ8sA5LRyANKQogve3t7/4hcLvcH8NeIYL5SSv2uAftDHhSl1A/7CCH2dWBxWNBoNIppmgSDQarVKufn53ie59d1RQO0YUBjsRg7OztomkatVmNpaYnNzU2EEH7dNX0YUKUUpmlyd3dHPp9HKcXV1RXT09MIIXyXfihgKSWGYVCpVPoQ27axbXugjS/4HcHh29btdlFKsbq6SiwWA6BQKHB/f/82WEpJKpUiEAi8aA8EAiilaLVaNBoNrq+vabfbr+BCCG5vb3l8fCSTyaDr/ov6KrA8z6NcLtNqtUgkEszPz+M4Ds1mE9u2MQyDjY0Ndnd3mZmZ6ds4jkMoFAKg0Wjw8PCAEIJOp+ML9nWnZ5hIJJBSUiqV+gMIIbAsi3Q6zdbWFoeHh7iuS7lcZn19HcdxsG0by7Ko1+sD93lgcCml6Ha7SClftZ+dnbG4uIhhGMzOznJzc0OlUkHTNBYWFtB1nXq9zunp6aA8/lxUe57H09MThmEwPj7ed+ji4oLLy0uEEHieNyiHPw+WUjI5OYlS6lXE9krmj6DwyaplmiaGYVCtVqnX658Z4u0ZCyGIx+O0223GxsaYm5sjHo9Tq9U4OTn5FPRdYCkly8vLCCEIh8MopSgUCpTLZVzXHR3YdV2Ojo7wPI/t7W1isRjBYPCnoPDOPVZK4XlePz2SySThcHj04J4ajQbFYhEpJZlM5lWOjwwMUCwWaTabRCIR0un08MG9gt/778l1XfL5PJ1OB9M0WVlZGR54amoKy7IIBoPouo5lWYRCof7SNptN8vk83W6XVCrF2toaExMTHwKLXC734vCVUpJOp9F1/cW5rGkapVLpRcGIRqMkk0kikQiO43B8fMzz8/O7wd73Mx90EfArg0opNE178/LwnboaUPID+H2DnPkgFOCbppT68lGrn5VS6k8tm83+DfwKfOX/Z8ao5AFflVK/ZLPZf/8DudZq3wvXLmgAAAAASUVORK5CYII=',
            'tweeter':
            'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMMQAADDEBLaRWDgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAK5SURBVEiJpZU7T+NAFEZP4kAePBwZ8TA0EUIRKKIgQhSImnZ/wDYr7Q/barddbRu2QVAQhChAgBQqQClwFEIcx8IYfLdKNk5sJ4GRRh6PPN+Z+90749jZ2VnGsqyfwD6QEREARISocdRcT7dF5AD4lmi327+AL0GCn4QgIhkR+SIiPxIish8k3i/yQVCn7ydEJD0sgjDBMUCZxCg2hQmqqsri4iKKotBoNKhWq741vesSvULpdJrJyUkajcZQy7a2tlheXqa3FQoFzs/PmZubQ9M0Dg8Pu2vivQJLS0sUi0Wy2WykFfl8fgACkEql2N3dJZfLcXFxgaqq/0G9O04mk8RiMba3t8nlcqHRrK6uDkB628TEBDs7O5imORgRgGVZ3Y/z+Tx7e3ssLCz4QMlkkng8HglqNpscHBzw9vbmz1EHVK1WWVtb6wpNTU1RLBZ5f3/HMAzq9TqO40RCAGq1Gq7r+jboK4aZmRmurq7Y3Nz0LVQUBV3X0XV9KATAcZwBy7vlLSJomjbU/1Ha8/PzAMhXdQ8PD5+GABiGEQ1qt9tcXl5+CvL09MTLy8ugdf0T9/f32LbNxsYGs7OzY4Our68DD/oASEQoFApMT0+PDTFNk7u7u0BQPAhULpdxXXds0PHxMZ7nDUBCQa1Wi1KpRKVSwTTNkSAnJyfU6/XQWz4QJCI4jsPj4yOKogyFnJ6ecnt7G3nTd3OUSCSYn59HURRUVUXXdTKZTCTAtm2Ojo585dwP6rx3Qa+vrzSbTdbX11lZWSEWi4UCLMvi5uaGSqWC53mh/67esa/qTNOkXC4DoGka2WyWVCoFgOu6tFotDMPAtu3QXISNA8tbRKjVaoEnPCwPYZZ1nnHP8+ww2Ed7P0RE7LjneX/7ff6ocES0pbiIfBWRP57n2aMAwywKGdsi8ltRlO//AFPkniYXwGRMAAAAAElFTkSuQmCC',
            'udemy':
            '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',
            'youtube':
            'iVBORw0KGgoAAAANSUhEUgAAACEAAAAaCAYAAAA5WTUBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMNwAADDcBracSlQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKkSURBVEiJxZcxSBthFIC/918uGhxMsyptcGjoIgGHW9wSihQKGZrNthHsIC7S1TqWgkRwcZV2lKo0FouixaEuEQ4khIIdrClkEoIZgiae93fQgFgll5jqt93x3v++e3f/3TvhGgYHBx/UarUUEAMeA48A/3WxDagBf4A9EdmsVqsfd3d3j64GyeWDZDJpFAqFKWAC6G6haCPKIjKTzWbfA+4/Ev39/V2dnZ3LwNP/UPwqaycnJy9yuVwFQF2cVIFA4PMdCQAMBQKBL8lk0gAwACzLmtJav7kjgTp95XK5ViwWf0g0Gg36/f7fQLBRltYaEWkU5hkRKTmO06dM03ztRaAuEQ6H0Vq3RUJrHTIM46XR29v7jvNt2BClFEtLS0QiEfb39ymVSrfujIg4Cog0mxiPx1lYWGBycpKenh5c122cdANa64gCHra6QCKRYHl5mfHxcYLBII7jtLJMWAEdrUrUSaVSrK6uMjo6SigU4uzsrJn0DtU4xhs+n4+xsTFWVlYYHh6mq6vL821qm0Qd0zSZmJggk8kQi8XuR6LO1tYW+XzeUzd87S6ey+WYnZ0ln88jIijV+DrbJnF4eMj09DTb29u4rtvU+8MHVLnFDjk+PiadTrO+vs7p6WkrS1R9QAGPb8zLaK2Zm5tjcXGRSqXiqe03sO8D9pqVyGQyzM/PUywWMQzjNgIAv3xa600Ree4l2nEcRkZGODg4QCmFYRi3KQ6AiGzIwMBAt2EYB9znp9y27bLWesZjUtsELkjbtl2ur6osy/qqtX7W7io3ISIb2Wx2CHDrT5TrOE4SWLsjgW+O4yS4mLiv9ldZljWptX6Lx2mrSY6A9M7OzgeuG/kvE41Gg6ZpvhKROPCE85mj5Z8fEfnpuu5313U/2bZdvhr0F9Fo9phaoDu9AAAAAElFTkSuQmCC'
        }
        image = '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'
        footer = '''<div>
                      <div align="center">
                      <img src="data:image/jpg;base64,''' + image + '''
                      </div>
                      <div align="right">
                      <p align="right">
                      <b>''' + self.tr(
            'Author: Leandro Franca', 'Autor: Leandro França'
        ) + '''</b>
                      </p>
                      <a target="_blank" rel="noopener noreferrer" href="https://www.udemy.com/user/leandro-luiz-silva-de-franca/"><img title="Udemy" src="data:image/png;base64,''' + dic_BW[
            'udemy'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/GEOCAPT/"><img title="Facebook" src="data:image/png;base64,''' + dic_BW[
                'face'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/channel/UCLrewDGciytcBG9r0OxTW2w"><img title="Youtube" src="data:image/png;base64,''' + dic_BW[
                    'youtube'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.researchgate.net/profile/Leandro_Franca2"><img title="ResearchGate" src="data:image/png;base64,''' + dic_BW[
                        'RG'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/LEOXINGU"><img title="GitHub" src="data:image/png;base64,''' + dic_BW[
                            'github'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="https://www.linkedin.com/in/leandro-fran%C3%A7a-93093714b/"><img title="Linkedin" src="data:image/png;base64,''' + dic_BW[
                                'linkedin'] + '''"></a> <a target="_blank" rel="noopener noreferrer" href="http://lattes.cnpq.br/8559852745183879"><img title="Lattes" src="data:image/png;base64,''' + dic_BW[
                                    'lattes'] + '''"></a>
                      </div>
                    </div>'''
        return self.tr(txt_en, txt_pt) + footer

    LINES = 'LINES'
    TYPE = 'TYPE'
    DISTANCE = 'DISTANCE'
    OUTPUT = 'OUTPUT'

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.LINES, self.tr('Line Layer', 'Camada de Linhas'),
                [QgsProcessing.TypeVectorLine]))

        tipo = [
            self.tr('Start and End points', 'Pontos inicial e final'),
            self.tr('Only End Point', 'Apenas ponto final'),
            self.tr('Only Start Point', 'Apenas ponto inicial')
        ]

        self.addParameter(
            QgsProcessingParameterEnum(self.TYPE,
                                       self.tr('Attributes', 'Atributos'),
                                       options=tipo,
                                       defaultValue=0))

        self.addParameter(
            QgsProcessingParameterNumber(self.DISTANCE,
                                         self.tr('Distance', 'Distância'),
                                         type=1,
                                         defaultValue=25.0))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT, self.tr('Extended lines', 'Linhas estendidas')))

    def processAlgorithm(self, parameters, context, feedback):

        linhas = self.parameterAsVectorLayer(parameters, self.LINES, context)
        if linhas is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.LINES))

        tipo = self.parameterAsEnum(parameters, self.TYPE, context)

        Distancia = self.parameterAsDouble(parameters, self.DISTANCE, context)
        if Distancia is None or Distancia < 0:
            raise QgsProcessingException(
                self.tr('The input distance must be grater than 0!',
                        'A distância de entrada deve ser maior que 0!'))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, linhas.fields(),
                                               linhas.wkbType(),
                                               linhas.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        # Camada de entrada
        SRC = linhas.sourceCrs()
        fields = linhas.fields()
        extensao = linhas.sourceExtent()
        y_max = extensao.yMaximum()
        y_min = extensao.yMinimum()

        # Transformar distancia para graus, se o SRC for Geográfico
        if SRC.isGeographic():
            EPSG = int(SRC.authid().split(':')[-1])
            proj_crs = CRS.from_epsg(EPSG)
            a = proj_crs.ellipsoid.semi_major_metre
            f = 1 / proj_crs.ellipsoid.inverse_flattening
            e2 = f * (2 - f)
            N = a / np.sqrt(1 - e2 * (np.sin(
                (y_min + y_max) / 2))**2)  # Raio de curvatura 1º vertical
            M = a * (1 - e2) / (1 - e2 * (np.sin((y_min + y_max) / 2))**2)**(
                3 / 2.)  # Raio de curvatura meridiana
            R = np.sqrt(M * N)  # Raio médio de Gauss
            theta = Distancia / R
            Distancia = np.degrees(theta)  # Radianos para graus

        # Varrer linhas
        Percent = 100.0 / linhas.featureCount() if linhas.featureCount(
        ) > 0 else 0
        for index, feat in enumerate(linhas.getFeatures()):
            geom = feat.geometry()
            att = feat.attributes()
            if geom:
                if geom.isMultipart():
                    lines = geom.asMultiPolyline()
                    for line in lines:
                        P1 = line[0]
                        P2 = line[1]
                        Pn = line[-1]
                        Pn_1 = line[-2]
                        P_ini = QgsGeometry.fromPointXY(P1)
                        P_fim = QgsGeometry.fromPointXY(Pn)
                        if tipo == 0 or tipo == 2:
                            vetor = array(P1) - array(P2)
                            P = array(P1) + vetor / norm(vetor) * Distancia
                            P1 = QgsPointXY(P[0], P[1])
                        if tipo == 0 or tipo == 1:
                            vetor = array(Pn) - array(Pn_1)
                            P = array(Pn) + vetor / norm(vetor) * Distancia
                            Pn = QgsPointXY(P[0], P[1])
                        if tipo == 0:
                            line = [P1] + line[1:-1] + [Pn]
                        elif tipo == 1:
                            line = line[0:-1] + [Pn]
                        elif tipo == 2:
                            line = [P1] + line[1:]
                        new_geom = QgsGeometry.fromPolylineXY(line)
                        feature = QgsFeature(fields)
                        feature.setAttributes(att)
                        feature.setGeometry(new_geom)
                        sink.addFeature(feature, QgsFeatureSink.FastInsert)
                else:
                    line = geom.asPolyline()
                    P1 = line[0]
                    P2 = line[1]
                    Pn = line[-1]
                    Pn_1 = line[-2]
                    P_ini = QgsGeometry.fromPointXY(P1)
                    P_fim = QgsGeometry.fromPointXY(Pn)
                    if tipo == 0 or tipo == 2:
                        vetor = array(P1) - array(P2)
                        P = array(P1) + vetor / norm(vetor) * Distancia
                        P1 = QgsPointXY(P[0], P[1])
                    if tipo == 0 or tipo == 1:
                        vetor = array(Pn) - array(Pn_1)
                        P = array(Pn) + vetor / norm(vetor) * Distancia
                        Pn = QgsPointXY(P[0], P[1])
                    if tipo == 0:
                        line = [P1] + line[1:-1] + [Pn]
                    elif tipo == 1:
                        line = line[0:-1] + [Pn]
                    elif tipo == 2:
                        line = [P1] + line[1:]
                    new_geom = QgsGeometry.fromPolylineXY(line)
                    feature = QgsFeature(fields)
                    feature.setAttributes(att)
                    feature.setGeometry(new_geom)
                    sink.addFeature(feature, QgsFeatureSink.FastInsert)
            if feedback.isCanceled():
                break
            feedback.setProgress(int((index + 1) * Percent))

        feedback.pushInfo(
            self.tr('Operation completed successfully!',
                    'Operação finalizada com sucesso!'))
        feedback.pushInfo(
            self.tr('Leandro Franca - Cartographic Engineer',
                    'Leandro França - Eng Cart'))

        return {self.OUTPUT: dest_id}