Example #1
0
File: cow.py Project: brunky37/cow
    def drawLimits (self):
        self.dlg.listWidget.clear()
        #comprobate if the layer already exists and delete it
        for lyr in QgsMapLayerRegistry.instance().mapLayers().values():
            if lyr.name() == "Limites":
                QgsMapLayerRegistry.instance().removeMapLayer( lyr.id() )
                break

        #crete a vector layer with a expecific name and color
        v_layer = QgsVectorLayer("LineString", "Limites", "memory")
        symbols =v_layer.rendererV2().symbols()
        symbol=symbols[0]
        symbol.setColor(QColor('magenta'))
        #create the provider and add the layer
        pr = v_layer.dataProvider()
        QgsMapLayerRegistry.instance().addMapLayers([v_layer])
        seg = QgsFeature()
        iterations=len(self.tool.polygon)
        #draw the lines between the buttons in order
        for i in range (iterations):
            if i== iterations-1:
                seg.setGeometry(QgsGeometry.fromPolyline([self.tool.polygon[i], self.tool.polygon[0]]))
            else:
                seg.setGeometry(QgsGeometry.fromPolyline([self.tool.polygon[i], self.tool.polygon[i+1]]))
            #add the lines to the provider and update the layer
            pr.addFeatures( [ seg ] )
            v_layer.updateExtents()
            v_layer.triggerRepaint()

        #add the points to the QlistWidget
        for i in range (len(self.tool.polygon)):
            self.addItem(i)
    def create_layer(self, data):
        display_name = 'some-layer'
        uri = 'Point?crs=epsg:4326&index=yes&uuid=%s' % uuid.uuid4()
        vlayer = QgsVectorLayer(uri, display_name, 'memory')
        QgsMapLayerRegistry.instance().addMapLayer(vlayer)

        provider = vlayer.dataProvider()
        vlayer.startEditing()
        provider.addAttributes([
            QgsField('population_density', QtCore.QVariant.Double),
        ])
        features = []
        for x, y, density in data:
            feat = QgsFeature()
            geom = QgsGeometry.fromPoint(QgsPoint(x, y))
            feat.setGeometry(geom)
            feat.setAttributes([density])
            features.append(feat)
        provider.addFeatures(features)
        vlayer.commitChanges()

        vlayer.updateExtents()
        self.canvas.setExtent(vlayer.extent())
        cl = QgsMapCanvasLayer(vlayer)
        self.canvas.setLayerSet([cl])
        vlayer.triggerRepaint()
Example #3
0
    def testRequestRepaintSimple(self):
        """ test requesting repaint with a single dependent layer """
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer", "memory")
        QgsProject.instance().addMapLayers([layer])
        self.assertTrue(layer.isValid())

        # add image to cache
        cache = QgsMapRendererCache()
        im = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('xxx', im, [layer])
        self.assertFalse(cache.cacheImage('xxx').isNull())
        self.assertTrue(cache.hasCacheImage('xxx'))

        # trigger repaint on layer
        layer.triggerRepaint()
        # cache image should be cleared
        self.assertTrue(cache.cacheImage('xxx').isNull())
        self.assertFalse(cache.hasCacheImage('xxx'))
        QgsProject.instance().removeMapLayer(layer.id())

        # test that cache is also cleared on deferred update
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer", "memory")
        cache.setCacheImage('xxx', im, [layer])
        layer.triggerRepaint(True)
        self.assertFalse(cache.hasCacheImage('xxx'))
Example #4
0
    def checkRepaintNonLabeledLayerDoesNotInvalidateLabelCache(self, job_type):
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")
        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(cache.hasCacheImage(layer.id()))
        self.assertEqual(cache.dependentLayers('_labels_'), [])

        # trigger repaint on layer - should not invalidate label cache because layer is not labeled
        layer.triggerRepaint()
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertFalse(cache.hasCacheImage(layer.id()))
        self.assertTrue(job.takeLabelingResults())

        # second job should still use label cache
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertTrue(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())
Example #5
0
 def load_countries(self):
     display_name = 'Population density'
     uri = DATA_DIR + 'Countries.shp'
     vlayer = QgsVectorLayer(uri, display_name, 'ogr')
     QgsMapLayerRegistry.instance().addMapLayers([vlayer])
     vlayer.updateExtents()
     self.canvas.setExtent(vlayer.extent())
     # set the map canvas layer set
     cl = QgsMapCanvasLayer(vlayer)
     self.layers.insert(0, cl)
     self.canvas.setLayerSet(self.layers)
     vlayer.triggerRepaint()
Example #6
0
    def testDeferredUpdate(self):
        """ test that map canvas doesn't auto refresh on deferred layer update """
        canvas = QgsMapCanvas()
        canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326))
        canvas.setFrameStyle(0)
        canvas.resize(600, 400)
        self.assertEqual(canvas.width(), 600)
        self.assertEqual(canvas.height(), 400)

        layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string",
                               "layer", "memory")

        canvas.setLayers([layer])
        canvas.setExtent(QgsRectangle(10, 30, 20, 35))
        canvas.show()

        # need to wait until first redraw can occur (note that we first need to wait till drawing starts!)
        while not canvas.isDrawing():
            app.processEvents()
        while canvas.isDrawing():
            app.processEvents()

        self.assertTrue(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas))

        # add polygon to layer
        f = QgsFeature()
        f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45)))
        self.assertTrue(layer.dataProvider().addFeatures([f]))

        # deferred update - so expect that canvas will not been refreshed
        layer.triggerRepaint(True)
        timeout = time.time() + 0.1
        while time.time() < timeout:
            # messy, but only way to check that canvas redraw doesn't occur
            self.assertFalse(canvas.isDrawing())
        # canvas should still be empty
        self.assertTrue(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas))

        # refresh canvas
        canvas.refresh()
        while not canvas.isDrawing():
            app.processEvents()
        while canvas.isDrawing():
            app.processEvents()

        # now we expect the canvas check to fail (since they'll be a new polygon rendered over it)
        self.assertFalse(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas))
Example #7
0
    def checkRepaintLabeledLayerInvalidatesLabelCache(self, job_type):
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")

        labelSettings = QgsPalLayerSettings()
        labelSettings.fieldName = "fldtxt"
        layer.setLabeling(QgsVectorLayerSimpleLabeling(labelSettings))
        layer.setLabelsEnabled(True)

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        self.assertEqual(cache.dependentLayers('_labels_'), [layer])

        # trigger repaint on layer - should invalidate cache and block use of cached labels
        layer.triggerRepaint()
        self.assertFalse(cache.hasCacheImage('_labels_'))

        # second job should not use label cache, since layer was repainted
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # shouldn't use cache
        self.assertFalse(job.usedCachedLabels())
        # but results should have been cached
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())
Example #8
0
    def testRequestRepaintMultiple(self):
        """ test requesting repaint with multiple dependent layers """
        layer1 = QgsVectorLayer("Point?field=fldtxt:string", "layer1",
                                "memory")
        layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2",
                                "memory")
        QgsProject.instance().addMapLayers([layer1, layer2])
        self.assertTrue(layer1.isValid())
        self.assertTrue(layer2.isValid())

        # add image to cache - no dependent layers
        cache = QgsMapRendererCache()
        im1 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('nolayer', im1)
        self.assertFalse(cache.cacheImage('nolayer').isNull())
        self.assertTrue(cache.hasCacheImage('nolayer'))

        # trigger repaint on layer
        layer1.triggerRepaint()
        layer1.triggerRepaint(
        )  # do this a couple of times - we don't want errors due to multiple disconnects, etc
        layer2.triggerRepaint()
        layer2.triggerRepaint()
        # cache image should still exist - it's not dependent on layers
        self.assertFalse(cache.cacheImage('nolayer').isNull())
        self.assertTrue(cache.hasCacheImage('nolayer'))

        # image depends on 1 layer
        im_l1 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('im1', im_l1, [layer1])

        # image depends on 2 layers
        im_l1_l2 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('im1_im2', im_l1_l2, [layer1, layer2])

        # image depends on 2nd layer alone
        im_l2 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('im2', im_l2, [layer2])

        self.assertFalse(cache.cacheImage('im1').isNull())
        self.assertTrue(cache.hasCacheImage('im1'))
        self.assertFalse(cache.cacheImage('im1_im2').isNull())
        self.assertTrue(cache.hasCacheImage('im1_im2'))
        self.assertFalse(cache.cacheImage('im2').isNull())
        self.assertTrue(cache.hasCacheImage('im2'))

        # trigger repaint layer 1 (check twice - don't want disconnect errors)
        for i in range(2):
            layer1.triggerRepaint()
            # should be cleared
            self.assertTrue(cache.cacheImage('im1').isNull())
            self.assertFalse(cache.hasCacheImage('im1'))
            self.assertTrue(cache.cacheImage('im1_im2').isNull())
            self.assertFalse(cache.hasCacheImage('im1_im2'))
            # should be retained
            self.assertTrue(cache.hasCacheImage('im2'))
            self.assertFalse(cache.cacheImage('im2').isNull())
            self.assertEqual(cache.cacheImage('im2'), im_l2)
            self.assertTrue(cache.hasCacheImage('nolayer'))
            self.assertFalse(cache.cacheImage('nolayer').isNull())
            self.assertEqual(cache.cacheImage('nolayer'), im1)

        # trigger repaint layer 2
        for i in range(2):
            layer2.triggerRepaint()
            # should be cleared
            self.assertFalse(cache.hasCacheImage('im1'))
            self.assertTrue(cache.cacheImage('im1').isNull())
            self.assertFalse(cache.hasCacheImage('im1_im2'))
            self.assertTrue(cache.cacheImage('im1_im2').isNull())
            self.assertFalse(cache.hasCacheImage('im2'))
            self.assertTrue(cache.cacheImage('im2').isNull())
            # should be retained
            self.assertTrue(cache.hasCacheImage('nolayer'))
            self.assertFalse(cache.cacheImage('nolayer').isNull())
            self.assertEqual(cache.cacheImage('nolayer'), im1)
Example #9
0
    def createAnalysis(self):

        newTableName = self.dlg.cmbAnalysis.currentText()

        analysisLayerList = self.lReg.mapLayersByName(newTableName)
        if len(analysisLayerList) == 0:
            analysisLayer = QgsVectorLayer("Polygon?crs=EPSG:2056",
                                           newTableName, "memory")
            analysisLp = analysisLayer.dataProvider()
            analysisCreateNewLayer = True
        elif len(analysisLayerList) == 1:
            analysisLayer = analysisLayerList[0]
            analysisLp = analysisLayer.dataProvider()
            analysisCreateNewLayer = False

        # Delete all existing features in layer if applicable
        iter = analysisLayer.getFeatures()
        featureToDelete = []
        for feature in iter:
            featureToDelete.append(feature.id())
        analysisLp.deleteFeatures(featureToDelete)

        # Create PyQt connection to MSSQL if not already there
        if not self.qtmsdb:
            self.qtmsdb, isMSOpened = self.msdb.createQtMSDB()
            if isMSOpened:
                self.messageBar.pushMessage("Connexion SQL Server",
                                            unicode("Connexion réussie!",
                                                    "utf-8"),
                                            level=QgsMessageBar.INFO)
            else:
                return

        selectedIndex = self.dlg.cmbAnalysis.currentIndex()

        if selectedIndex == 0:
            self.messageBar.pushMessage("Erreur ",
                                        unicode("Sélectionnez une analyse!",
                                                "utf-8"),
                                        level=QgsMessageBar.WARNING)
            return

        # Get the analysis parameters
        params = self.getAnalysisFromDb()
        self.qstr = params["querystring"]

        if self.qstr is None or self.qstr == "":
            self.messageBar.pushMessage(
                "Erreur",
                unicode("Requête mal définie", "utf-8") + self.qstr,
                level=QgsMessageBar.WARNING)
            return

        # Adapt connection string if applicable - method could be cleaner...
        if (self.dlg.lnYearStart.text() == ""
                and params["date_filtering"] == 't'
                and not self.dlg.chkLastSurvey.isChecked()):

            self.messageBar.pushMessage("Erreur",
                                        "Vous devez saisir une date " +
                                        newTableName,
                                        level=QgsMessageBar.CRITICAL)
            return

        if ((self.dlg.lnYearStart.text() == ""
             or self.dlg.lnYearEnd.text() == "")
                and params["date_filtering"] == 't'
                and params["timerange_filtering"] == 't'):

            self.messageBar.pushMessage("Erreur",
                                        "Vous devez  saisir 2 dates" +
                                        newTableName,
                                        level=QgsMessageBar.CRITICAL)
            return

        if (params["date_filtering"] == 't'
                and not self.dlg.chkLastSurvey.isChecked()
                and params["timerange_filtering"] == 'f'):

            self.qstr = self.qstr.replace(
                '--EDITTIMESELECT',
                ' ,' + self.dlg.lnYearStart.text() + ' annee ')
            self.qstr = self.qstr.replace(
                '--EDITTIMESTRING', ' AND ' + params["datefield"] + ' = ' +
                self.dlg.lnYearStart.text())

        if (params["date_filtering"] == 't'
                and self.dlg.chkLastSurvey.isChecked()
                and params["timerange_filtering"] == 'f'):

            self.qstr = self.qstr.replace(
                '--EDITTIMESELECT',
                ' , max(' + params["datefield"] + ') annee ')

        if (params["date_filtering"] == 't'
                and params["timerange_filtering"] == 't'):
            self.qstr = self.qstr.replace('--STARTYEAR',
                                          self.dlg.lnYearStart.text())
            self.qstr = self.qstr.replace('--ENDYEAR',
                                          self.dlg.lnYearEnd.text())

        coupeFilter = self.dlg.lnCoupeType.text()
        self.editCoupeFilter(coupeFilter, params["coupetype_filtering"])

        admFilter = self.dlg.txtAdmShortName.text()
        self.editAdmFilter(admFilter)

        # Advanced users and developers can see and edit the Query string
        if self.dlg.btAdvancedUserMode.isChecked():
            self.qstr = self.dlg.txtMssqlQuery.toPlainText()
        # Get the geometric layer from PostGIS
        pgLayer = self.pgdb.getLayer(params["join_target_schema"],
                                     params["join_target_table"], "geom", "",
                                     params["join_target_table"], "fake_id")

        if not pgLayer:
            self.messageBar.pushMessage("Erreur au chargement de la couche",
                                        params["join_target_schema"] + '.' +
                                        params["join_target_table"],
                                        level=QgsMessageBar.CRITICAL)
            return

            # Execute the query and parse the results
        query = self.qtmsdb.exec_(self.qstr)
        query.setForwardOnly(True)

        # Create memory layer and add it to LegendInterface
        joinTableName = "SELVANS"
        selvansTable = QgsVectorLayer("Point?crs=EPSG:2056", joinTableName,
                                      "memory")

        selvansTableProvider = selvansTable.dataProvider()

        # Add fields with type given as defined in table main.analysis
        fieldList = params["field_of_interest"].split(",")

        joinedFieldsNames = []
        if params["field_of_interest_type"] == "number":
            for fieldname in fieldList:
                selvansTableProvider.addAttributes(
                    [QgsField(params["join_source_fkfield"], QVariant.Int)])
                selvansTableProvider.addAttributes(
                    [QgsField(fieldname, QVariant.Double)])
                joinedFieldsNames.append(joinTableName + '_' + fieldname)
        else:
            for fieldname in fieldList:
                selvansTableProvider.addAttributes(
                    [QgsField(params["join_source_fkfield"], QVariant.Int)])
                selvansTableProvider.addAttributes(
                    [QgsField(fieldname, QVariant.String)])
                joinedFieldsNames.append(joinTableName + '_' + fieldname)

        # Some more field are required for tables related to surveys
        if params["date_filtering"] == 't':
            selvansTableProvider.addAttributes(
                [QgsField('ANNEE_VALEUR', QVariant.Int)])
            joinedFieldsNames.append(joinTableName + '_' + 'ANNEE_VALEUR')
            fieldList.append('ANNEE_VALEUR')
            selvansTableProvider.addAttributes(
                [QgsField('DIV_SERIE', QVariant.Int)])
            joinedFieldsNames.append(joinTableName + '_DIV_SERIE')
            fieldList.append('DIV_SERIE')

        # Create progress bar
        progress = self.createProgressbar(query.numRowsAffected())

        # Parse the query result and add the features to memory layer
        self.fillSelvansTable(query, selvansTableProvider, progress, fieldList,
                              params["join_source_fkfield"])
        selvansTable.updateFields()
        self.lReg.addMapLayer(selvansTable)
        self.messageBar.clearWidgets()

        # Join the memory layer to a geographic PG layer
        targetlayer = self.joinLayer(pgLayer, params["join_target_pkfield"],
                                     selvansTable,
                                     params["join_source_fkfield"])

        # Get the fields names of the PG layer
        fields = targetlayer.pendingFields()
        fieldslist = []
        fieldNamesTest = []
        for field in fields:
            fieldslist.append(field)
            fieldNamesTest.append(field.name())

        # Add the features to the result layer
        analysisLp.deleteAttributes(analysisLp.attributeIndexes())
        analysisLp.addAttributes(fieldslist)
        outFeat = QgsFeature()
        iter = targetlayer.getFeatures()

        for inFeat in iter:
            if inFeat.geometry():
                outFeat.setGeometry(inFeat.geometry())
                outFeat.setAttributes(inFeat.attributes())
                analysisLp.addFeatures([outFeat])

        analysisLayer.updateFields()
        QgsMapLayerRegistry.instance().addMapLayer(analysisLayer)

        # # Apply the style store in public.layer_styles to the result layer
        # # 24.11.2016: deactivated for now
        # if params["default_style"] is not None:
        #     qmlstyle = pgLayer.getStyleFromDatabase(params["default_style"],
        #                                             "")
        #     analysisLayer.applyNamedStyle(qmlstyle)
        # else:
        #     self.messageBar.pushMessage("Erreur",
        #                                 unicode("Style non défini dans la "
        #                                         + "table main.analysis",
        #                                         "utf-8"),
        #                                 level=QgsMessageBar.WARNING)

        if self.lReg.mapLayersByName('SELVANS'):
            self.lReg.removeMapLayer(
                self.lReg.mapLayersByName('SELVANS')[0].id())

        if self.dlg.chkSaveAnalysisResult.isChecked():
            self.saveAnalysisToDisk(analysisLayer)

        if analysisCreateNewLayer:
            self.messageBar.pushMessage("Avertissement",
                                        unicode("La couche manquante",
                                                "utf-8"),
                                        level=QgsMessageBar.WARNING)
            self.moveLayerToGroup(analysisLayer, "Analyses SELVANS")

        self.activateLastAnalysis(analysisLayer)
        self.expandGroup(analysisLayer, "Analyses SELVANS", True)

        analysisLayer.triggerRepaint()
        self.legendInterface.refreshLayerSymbology(analysisLayer)

        # Zoom to selected administration(s)
        if admFilter != '':
            admFilter = ' adm = \'' + admFilter
            adminSql = admFilter.replace(',', '\' OR adm = \'')
            adminSql += '\''
            admLayer = self.pgdb.getLayer("parcellaire", "administrations",
                                          "geom", adminSql, "adminSubset",
                                          "idobj")

            if admLayer.featureCount() > 0:
                admExtent = admLayer.extent()
                self.iface.mapCanvas().setExtent(admExtent)
                self.iface.mapCanvas().refresh()
            else:
                self.messageBar.pushMessage("Avertissement",
                                            "Administration(s)" + admFilter +
                                            " manquante dans la base PostGIS!",
                                            level=QgsMessageBar.WARNING)
Example #10
0
class WGS84Layer(object):

    "..."

    def __init__(self,
                 iface,
                 layer_name,
                 layer_type,
                 visible,
                 group=None,
                 show_count=True):
        "..."
        self.iface = iface
        self.layer_name = layer_name
        self.visible = visible
        self.layer = QgsVectorLayer(layer_type + "?crs=EPSG:4326", layer_name,
                                    "memory")
        self.provider = self.layer.dataProvider()
        self.layer = QgsProject.instance().addMapLayer(self.layer)
        self.canvas = iface.mapCanvas()
        if group is not None:
            layer_t = QgsProject.instance().mapLayersByName(layer_name)[0]
            root = QgsProject.instance().layerTreeRoot()

            myLayer = root.findLayer(layer_t.id())
            myClone = myLayer.clone()
            parent = myLayer.parent()

            group.insertChildNode(0, myClone)
            parent.removeChildNode(myLayer)

            # collapse groups
            for child in QgsProject.instance().layerTreeRoot().children():
                if isinstance(child, QgsLayerTreeGroup):
                    child.setExpanded(False)

        if show_count:
            root = QgsProject.instance().layerTreeRoot()
            leaf = root.findLayer(self.layer.id())
            leaf.setCustomProperty("showFeatureCount", True)

    def show(self, show_it):
        "..."
        node = QgsProject.instance().layerTreeRoot().findLayer(self.layer)
        if node:
            node.setItemVisibilityChecked(show_it)

    def remove(self):
        "..."
        # It seems QGis does already delete the layers
        # Ensure that the layer is still in the registry before calling removeMapLayer
        if self.layer is not None and len(
                QgsProject.instance().mapLayersByName(self.layer_name)) > 0:
            QgsProject.instance().removeMapLayer(self.layer)
            self.layer = None

    def refresh(self):
        "..."
        if not self.canvas.isDrawing():
            self.layer.triggerRepaint(True)
            self.canvas.refresh()

    def refresh_legend(self):
        "..."
        root = QgsProject.instance().layerTreeRoot().findLayer(self.layer.id())
        self.iface.layerTreeView().layerTreeModel().refreshLayerLegend(root)
        self.iface.mapCanvas().refresh()

    def poly_marker(self, placement, qcolor, width):
        "..."
        marker = QgsMarkerLineSymbolLayer()
        marker.setColor(qcolor)
        marker.setPlacement(placement)
        marker.setWidth(width)
        self.layer.renderer().symbol().appendSymbolLayer(marker)

    def poly_markers(self, qcolor, width):
        "..."
        self.poly_marker(QgsMarkerLineSymbolLayer.Vertex, qcolor, width * 1.33)
        self.poly_marker(QgsMarkerLineSymbolLayer.FirstVertex, qcolor,
                         width * 2.25)
        self.poly_marker(QgsMarkerLineSymbolLayer.LastVertex, qcolor,
                         width * 2.25)

    def set_attributes(self, attributes):
        "..."
        self.provider.addAttributes(attributes)
        self.layer.updateFields()

    def add_feature(self, geometry, attributes):
        "..."
        added_feature = QgsFeature()
        added_feature.setGeometry(geometry)
        added_feature.setAttributes(attributes)
        success, feat = self.provider.addFeatures([added_feature])
        if success:
            self.layer.updateExtents()
            return feat[0]
        return None

    def remove_feature(self, feature):
        "..."
        self.provider.deleteFeatures([feature.id()])

    def remove_all_features(self):
        "..."
        for feature in self.layer.getFeatures():
            self.remove_feature(feature)

    def enable_labeling(self,
                        field,
                        font="Arial",
                        size=10,
                        weight=50,
                        rgb=(0, 0, 0),
                        placement=1):
        "..."
        self.layer.setCustomProperty("labeling", "pal")
        self.layer.setCustomProperty("labeling/enabled", "true")
        self.layer.setCustomProperty("labeling/fontFamily", font)
        self.layer.setCustomProperty("labeling/fontSize", str(size))
        self.layer.setCustomProperty("labeling/fontWeight", str(weight))
        self.layer.setCustomProperty("labeling/textColorR", str(rgb[0]))
        self.layer.setCustomProperty("labeling/textColorG", str(rgb[1]))
        self.layer.setCustomProperty("labeling/textColorB", str(rgb[2]))
        self.layer.setCustomProperty("labeling/placement", str(placement))
        self.layer.setCustomProperty("labeling/fieldName", field)
Example #11
0
class ImportPhotos:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'ImportPhotos_{}.qm'.format(locale))

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

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&ImportPhotos')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'ImportPhotos')
        self.toolbar.setObjectName(u'ImportPhotos')

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('ImportPhotos', message)

    def add_action(
            self,
            icon_path,
            text,
            callback,
            enabled_flag=True,
            add_to_menu=True,
            add_to_toolbar=True,
            status_tip=None,
            whats_this=None,
            parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        # Create the dialog (after translation) and keep reference

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""
        icon_path = ':/plugins/ImportPhotos/svg/ImportImage.svg'
        self.add_action(
            icon_path,
            text=self.tr(u'Import Photos'),
            callback=self.run,
            parent=self.iface.mainWindow())
        icon_path = ':/plugins/ImportPhotos/svg/SelectImage.svg'
        self.clickPhotos = self.add_action(
            icon_path,
            text=self.tr(u'Click Photos'),
            callback=self.mouseClick,
            parent=self.iface.mainWindow())
        self.dlg = ImportPhotosDialog()
        self.dlg.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint)

        self.dlg.ok.clicked.connect(self.ok)
        self.dlg.closebutton.clicked.connect(self.close)
        self.dlg.toolButtonImport.clicked.connect(self.toolButtonImport)
        self.dlg.toolButtonOut.clicked.connect(self.toolButtonOut)

        self.clickPhotos.setCheckable(True)
        self.clickPhotos.setEnabled(True)

        self.listPhotos = []
        self.layernamePhotos = []
        self.canvas = self.iface.mapCanvas()
        self.toolMouseClick = MouseClick(self.canvas, self)

        self.fields = ['ID', 'Name', 'Date', 'Time', 'Lon', 'Lat', 'Altitude', 'North', 'Azimuth', 'Camera Maker',
                       'Camera Model', 'Path']

        self.extension_switch = {
            ".shp": "ESRI Shapefile",
            ".geojson": "GeoJSON",
            ".gpkg":"GPKG",
            ".csv": "CSV",
            ".kml": "KML",
            ".tab": "MapInfo File",
            ".ods": "ODS"
        }

    def mouseClick(self):
        try:
            self.iface.setActiveLayer(self.canvas.layers()[0])
        except:
            pass
        self.canvas.setMapTool(self.toolMouseClick)
        self.clickPhotos.setChecked(True)

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

    def run(self):
        self.dlg.ok.setEnabled(True)
        self.dlg.closebutton.setEnabled(True)
        self.dlg.toolButtonImport.setEnabled(True)
        self.dlg.toolButtonOut.setEnabled(True)
        self.clickPhotos.setEnabled(True)
        self.dlg.out.setText('')
        self.dlg.imp.setText('')
        self.dlg.show()

    def close(self):
        self.dlg.close()

    def toolButtonOut(self):
        typefiles = 'ESRI Shapefile (*.shp *.SHP);; GeoJSON (*.geojson *.GEOJSON);; GeoPackage (*.gpkg *.GPKG);; Comma Separated Value (*.csv *.CSV);; Keyhole Markup Language (*.kml *.KML);; Mapinfo TAB (*.tab *.TAB);; Open Document Spreadsheet (*.ods *.ODS)'
        if platform.system() == 'Linux':
            try:
                self.outputPath, self.extension = QFileDialog.getSaveFileNameAndFilter(None, 'Save File', os.path.join(
                    os.path.join(os.path.expanduser('~')),
                    'Desktop'), typefiles)
            except:
                self.outputPath = QFileDialog.getSaveFileName(None, 'Save File', os.path.join(
                    os.path.join(os.path.expanduser('~')),
                    'Desktop'), typefiles) #hack line
        else:
            self.outputPath = QFileDialog.getSaveFileName(None, 'Save File', os.path.join(
                os.path.join(os.path.expanduser('~')),
                'Desktop'), typefiles)

        self.outputPath = self.outputPath[0]
        self.dlg.out.setText(self.outputPath)

    def toolButtonImport(self):
        self.directoryPhotos = QFileDialog.getExistingDirectory(None, 'Select a folder:',
                                                                os.path.join(os.path.join(os.path.expanduser('~')),
                                                                             'Desktop'), QFileDialog.ShowDirsOnly)
        self.dlg.imp.setText(self.directoryPhotos)

    def selectDir(self):
        title = 'Warning'
        msg = 'Please select a directory photos.'
        self.showMessage(title, msg, 'Warning')
        return True

    def selectOutp(self):
        title = 'Warning'
        msg = 'Please define output file location.'
        self.showMessage(title, msg, 'Warning')
        return True

    def noImageFound(self):
        title = 'Warning'
        msg = 'No image path found.'
        self.showMessage(title, msg, 'Warning')
        return True

    def ok(self):
        if self.dlg.imp.text() == '':
            if self.selectDir():
                return
        if not os.path.isdir(self.dlg.imp.text()):
            if self.selectDir():
                return
        if self.dlg.out.text() == '':
            if self.selectOutp():
                return
        if not os.path.isabs(self.dlg.out.text()):
            if self.selectOutp():
                return

        if platform.system() == 'Linux':
            self.lphoto = os.path.basename(self.outputPath)
            try:
                self.extension = '.'+self.extension.split()[-1][2:-1].lower()
            except:
                self.extension = '.shp' #hack line, temporary
        else:
            _ , self.extension = os.path.splitext(self.outputPath)
            basename = os.path.basename(self.outputPath)
            self.lphoto = basename[:-len(self.extension)]

        self.outputPath = self.dlg.out.text()
        self.directoryPhotos = self.dlg.imp.text()
        self.outDirectoryPhotosGeoJSON = self.plugin_dir + '/tmp.geojson'

        self.dlg.ok.setEnabled(False)
        self.dlg.closebutton.setEnabled(False)
        self.dlg.toolButtonImport.setEnabled(False)
        self.dlg.toolButtonOut.setEnabled(False)

        # get paths of photos
        extens = ['jpg', 'jpeg', 'JPG', 'JPEG']
        self.photos = []
        for root, dirs, files in os.walk(self.directoryPhotos):
            self.photos.extend(os.path.join(root, name) for name in files
                          if name.lower().endswith(tuple(extens)))

        self.initphotos = len(self.photos)

        if self.initphotos == 0:
            title = 'Warning'
            msg = 'No photos.'
            self.showMessage(title, msg, 'Warning')
            self.dlg.ok.setEnabled(True)
            self.dlg.closebutton.setEnabled(True)
            self.dlg.toolButtonImport.setEnabled(True)
            self.dlg.toolButtonOut.setEnabled(True)
            self.clickPhotos.setChecked(True)
            return

        self.canvas.setMapTool(self.toolMouseClick)

        self.truePhotosCount = 0

        self.Qpr_inst = QgsProject.instance()
        if platform.system()=='Darwin':
            self.layernamePhotos.append(self.lphoto+' OGRGeoJSON Point')
        else:
            self.layernamePhotos.append(self.lphoto)

        if platform.system() == 'Linux':
            self.outputPath = self.outputPath + self.extension
            self.extension = self.extension_switch[self.extension]
        else:
            self.extension = self.extension_switch[self.extension.lower()]

        self.exifread_module = False
        self.pil_module = False

        if CHECK_MODULE == '':
            self.showMessage('Python Modules', 'Please install python module "exifread" or "PIL".' , 'Warning')

        #self.import_photos_task('', '')
        self.call_import_photos()
        self.dlg.close()

    def refresh(self):  # Deselect features
        mc = self.canvas
        for layer in mc.layers():
            if layer.type() == layer.VectorLayer:
                layer.removeSelection()
        mc.refresh()

    def showMessage(self, title, msg, icon):
        if icon == 'Warning':
            icon = QMessageBox.Warning
        elif icon == 'Information':
            icon = QMessageBox.Information

        msgBox = QMessageBox()
        msgBox.setIcon(icon)
        msgBox.setWindowTitle(title)
        msgBox.setText(msg)
        msgBox.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint)
        msgBox.exec_()

    def completed(self, exception, result=None):
        geojson = {"type": "FeatureCollection",
                   "name": self.lphoto,
                   "crs": {"type": "name", "properties": {"name": "crs:OGC:1.3:CRS84"}},
                   "features": self.geoPhotos}

        geofile = open(self.plugin_dir + '/tmp.geojson', 'w')
        json.dump(geojson, geofile)
        geofile.close()
        del self.geoPhotos, geojson

        try:
            for layer in self.canvas.layers():
                if layer.publicSource() == self.outputPath:
                    self.Qpr_inst.instance().removeMapLayer(layer.id())
                    os.remove(self.outputPath)
        except:
            pass

        self.layerPhotos = QgsVectorLayer(self.outDirectoryPhotosGeoJSON, self.lphoto, "ogr")
        QgsVectorFileWriter.writeAsVectorFormat(self.layerPhotos, self.outputPath, "utf-8",
                                                    QgsCoordinateReferenceSystem(self.layerPhotos.crs().authid()),
                                                    self.extension)
        self.layerPhotos_final = QgsVectorLayer(self.outputPath, self.lphoto, "ogr")

        # clear temp.geojson file
        try:
            f = open(self.outDirectoryPhotosGeoJSON, 'r+')
            f.truncate(0)  # need '0' when using r+
        except:
            pass

        try:
            self.layerPhotos_final.loadNamedStyle(self.plugin_dir + "/svg/photos.qml")
        except:
            title = 'Warning'
            msg = 'No geo-tagged images were detected.'
            self.showMessage(title, msg, 'Warning')
            self.taskPhotos.destroyed()
            return

        self.layerPhotos_final.setReadOnly(False)
        self.layerPhotos_final.reload()
        self.layerPhotos_final.triggerRepaint()

        try:
            xmin = min(self.lon)
            ymin = min(self.lat)
            xmax = max(self.lon)
            ymax = max(self.lat)
            self.canvas.zoomToSelected(self.layerPhotos_final)
            self.canvas.setExtent(QgsRectangle(xmin, ymin, xmax, ymax))
        except:
            pass

        ###########################################
        self.dlg.ok.setEnabled(True)
        self.dlg.closebutton.setEnabled(True)
        self.dlg.toolButtonImport.setEnabled(True)
        self.dlg.toolButtonOut.setEnabled(True)
        self.clickPhotos.setChecked(True)

        noLocationPhotosCounter = self.initphotos - self.truePhotosCount
        if self.truePhotosCount == noLocationPhotosCounter == 0 or self.truePhotosCount == 0:
            title = 'Import Photos'
            msg = 'Import Completed.\n\nDetails:\n  No new photos were added.'
            self.showMessage(title, msg, 'Information')
            self.taskPhotos.destroyed()
            return
        elif (self.truePhotosCount == self.initphotos) or ((noLocationPhotosCounter + self.truePhotosCount) == self.initphotos):
            title = 'Import Photos'
            msg = 'Import Completed.\n\nDetails:\n  ' + str(
                int(self.truePhotosCount)) + ' photo(s) added without error.\n  ' + str(
                int(noLocationPhotosCounter)) + ' photo(s) skipped (because of missing location).'
            self.showMessage(title, msg, 'Information')

        self.Qpr_inst.addMapLayers([self.layerPhotos_final])
        
        self.taskPhotos.destroyed()

    def stopped(self, task):
        QgsMessageLog.logMessage(
            'Task "{name}" was canceled'.format(
                name=task.description()),
            'ImportPhotos', Qgis.Info)

    def import_photos_task(self, task, wait_time):
        self.geoPhotos = []
        self.lon = []
        self.lat = []
        for count, imgpath in enumerate(self.photos):
            try:
                name = os.path.basename(imgpath)
                if CHECK_MODULE == 'exifread' and not self.pil_module:
                    self.exifread_module = True
                    self.taskPhotos.setProgress(count/self.initphotos)
                    with open(imgpath, 'rb') as imgpathF:
                        tags = exifread.process_file(imgpathF, details=False)
                    if not tags.keys() & {"GPS GPSLongitude", "GPS GPSLatitude"}:
                        continue

                    lat, lon = self.get_exif_location(tags, "lonlat")
                    try:
                        altitude = float(tags["GPS GPSAltitude"].values[0].num) / float(
                            tags["GPS GPSAltitude"].values[0].den)
                    except:
                        altitude = ''
                    uuid_ = str(uuid.uuid4())
                    try:
                        dt1, dt2 = tags["EXIF DateTimeOriginal"].values.split()
                        date = dt1.replace(':', '/')
                        time_ = dt2
                        timestamp = dt1.replace(':', '-') + 'T' + time_
                    except:
                        try:
                            date = tags["GPS GPSDate"].values.replace(':', '/')
                            tt = [str(i) for i in tags["GPS GPSTimeStamp"].values]
                            time_ = "{:0>2}:{:0>2}:{:0>2}".format(tt[0], tt[1], tt[2])
                            timestamp = tags["GPS GPSDate"].values.replace(':', '-') + 'T' + time_
                        except:
                            date = ''
                            time_ = ''
                            timestamp = ''
                    try:
                        azimuth = float(tags["GPS GPSImgDirection"].values[0].num) / float(
                            tags["GPS GPSImgDirection"].values[0].den)
                    except:
                        azimuth = ''
                    try:
                        north = str(tags["GPS GPSImgDirectionRef"].values)
                    except:
                        north = ''
                    try:
                        maker = tags['Image Make']
                    except:
                        maker = ''
                    try:
                        model = tags['Image Model']
                    except:
                        model = ''

                if CHECK_MODULE == 'PIL' and not self.exifread_module:
                    self.pil_module = True
                    a = {}
                    info = Image.open(imgpath)
                    info = info._getexif()

                    if info == None:
                        continue

                    for tag, value in info.items():
                        if TAGS.get(tag, tag) == 'GPSInfo' or TAGS.get(tag, tag) == 'DateTime' or TAGS.get(tag,
                                                                                                           tag) == 'DateTimeOriginal':
                            a[TAGS.get(tag, tag)] = value

                    if a == {}:
                        continue

                    if a['GPSInfo'] != {}:
                        if 1 and 2 and 3 and 4 in a['GPSInfo']:
                            lat = [float(x) / float(y) for x, y in a['GPSInfo'][2]]
                            latref = a['GPSInfo'][1]
                            lon = [float(x) / float(y) for x, y in a['GPSInfo'][4]]
                            lonref = a['GPSInfo'][3]

                            lat = lat[0] + lat[1] / 60 + lat[2] / 3600
                            lon = lon[0] + lon[1] / 60 + lon[2] / 3600

                            if latref == 'S':
                                lat = -lat
                            if lonref == 'W':
                                lon = -lon
                        else:
                            continue

                        uuid_ = str(uuid.uuid4())
                        if 'DateTime' or 'DateTimeOriginal' in a:
                            if 'DateTime' in a:
                                dt1, dt2 = a['DateTime'].split()
                            if 'DateTimeOriginal' in a:
                                dt1, dt2 = a['DateTimeOriginal'].split()
                            date = dt1.replace(':', '/')
                            time_ = dt2
                            timestamp = dt1.replace(':', '-') + 'T' + time_

                        if 6 in a['GPSInfo']:
                            if len(a['GPSInfo'][6]) > 1:
                                mAltitude = float(a['GPSInfo'][6][0])
                                mAltitudeDec = float(a['GPSInfo'][6][1])
                                altitude = mAltitude / mAltitudeDec
                        else:
                            altitude = ''

                        if 16 and 17 in a['GPSInfo']:
                            north = str(a['GPSInfo'][16])
                            azimuth = float(a['GPSInfo'][17][0]) / float(a['GPSInfo'][17][1])
                        else:
                            north = ''
                            azimuth = ''

                        maker = ''
                        model = ''
                self.lon.append(lon)
                self.lat.append(lat)
                self.truePhotosCount = self.truePhotosCount + 1

                geo_info = {"type": "Feature",
                            "properties": {'ID': uuid_, 'Name': name, 'Date': date, 'Time': time_,
                                           'Lon': lon,
                                           'Lat': lat, 'Altitude': altitude, 'North': north,
                                           'Azimuth': azimuth,
                                           'Camera Maker': str(maker), 'Camera Model': str(model), 'Path': imgpath,
                                           'Timestamp': timestamp},
                            "geometry": {"coordinates": [lon, lat], "type": "Point"}}
                self.geoPhotos.append(geo_info)

                if self.taskPhotos.isCanceled():
                    self.stopped(self.taskPhotos)
                    self.taskPhotos.destroyed()
                    return None
            except:
                pass
        return True

    def call_import_photos(self):
        self.taskPhotos = QgsTask.fromFunction(u'ImportPhotos', self.import_photos_task,
                                 on_finished=self.completed, wait_time=4)
        QgsApplication.taskManager().addTask(self.taskPhotos)

######################################################
# based on http://www.codegists.com/snippet/python/exif_gpspy_snakeye_python

    def _get_if_exist(self, data, key):
        if key in data:
            return data[key]

        return None


    def _convert_to_degress(self, value):
        """
        Helper function to convert the GPS coordinates stored in the EXIF to degress in float format

        :param value:
        :type value: exifread.utils.Ratio
        :rtype: float
        """
        d = float(value.values[0].num) / float(value.values[0].den)
        m = float(value.values[1].num) / float(value.values[1].den)
        s = float(value.values[2].num) / float(value.values[2].den)

        return d + (m / 60.0) + (s / 3600.0)


    def get_exif_location(self, exif_data, lonlat):
        """
        Returns the latitude and longitude, if available, from the provided exif_data (obtained through get_exif_data above)
        """

        if lonlat=='lonlat':
            lat = ''
            lon = ''
            gps_latitude = self._get_if_exist(exif_data, 'GPS GPSLatitude')
            gps_latitude_ref = self._get_if_exist(exif_data, 'GPS GPSLatitudeRef')
            gps_longitude = self._get_if_exist(exif_data, 'GPS GPSLongitude')
            gps_longitude_ref = self._get_if_exist(exif_data, 'GPS GPSLongitudeRef')

            if gps_latitude and gps_latitude_ref and gps_longitude and gps_longitude_ref:
                lat = self._convert_to_degress(gps_latitude)
                if gps_latitude_ref.values[0] != 'N':
                    lat = 0 - lat

                lon = self._convert_to_degress(gps_longitude)
                if gps_longitude_ref.values[0] != 'E':
                    lon = 0 - lon

            return lat, lon
Example #12
0
class BestPathFinder(object):

    RAW_FN_TEMPLATE = 'frd_raw_{}_num_{}.shp'
    SIMPLIFIED_FN_TEMPLATE = 'frd_simplified_{}_num_{}.shp'

    def __init__(self, dtm_layer, exclusion_areas_layer=None):

        self.dtm = {"layer": dtm_layer, "array": af.raster_2_array(dtm_layer)}
        self.converter = af.Converter(dtm_layer)

        self.exclusion_areas = {
            "layer":
            exclusion_areas_layer,
            "array":
            exclusion_areas_fn.create_exclusion_array(dtm_layer,
                                                      exclusion_areas_layer)
        }

        self.optimizer = a_star.DefaultConstraintsOptimizer(
            self.dtm["array"], self.converter.pixel_width)
        self.set_parameters({
            "semi_size": 5,
            "min_slope_pct": 0,
            "max_slope_pct": 10,
            "penalty_factor_xy": 50,
            "penalty_factor_z": 50
        })

    def _slope_label(self):
        return _slope_label(self.parameters["min_slope_pct"],
                            self.parameters["max_slope_pct"])

    def set_output_folder(self, output_folder):

        self.output_folder = output_folder

    def set_parameters(self, parameters):

        self.parameters = parameters

        self.optimizer.reset_config(parameters["min_slope_pct"] / 100.0,
                                    parameters["max_slope_pct"] / 100.0,
                                    parameters["semi_size"],
                                    parameters["penalty_factor_xy"],
                                    parameters["penalty_factor_z"],
                                    self.exclusion_areas["array"])

    def add_segment_to(self,
                       goal_coords,
                       max_dist_m=None,
                       iterative=False,
                       force=False):

        self.check_point(goal_coords)
        goal_index = self.converter.coord_to_index([goal_coords])
        self.optimizer.add_segment_to(goal_index[0], max_dist_m, iterative,
                                      force)
        self._update_output_layer()
        self._update_output_layer()

    def remove_last_segment(self):

        self.optimizer.remove_last_segment()

        self._update_output_layer()
        self._update_output_layer()

    def check_point(self, goal_coords):
        """Check for given points not inside exclusion areas and not out of
        dtm extent limits
        """
        if not self.dtm['layer'].extent().contains(
                QgsPoint(goal_coords[0], goal_coords[-1])):
            raise (ValueError(
                u"Error: ¡No se admiten puntos fuera de la extensión\n" +
                u" del Modelo Digital del Terreno!."))

        if self.exclusion_areas["layer"]:
            if self.optimizer._waypoints_index == []:
                for elem in self.exclusion_areas["layer"].dataProvider(
                ).getFeatures():
                    if elem.geometry().contains(
                            QgsPoint(goal_coords[0], goal_coords[-1])):
                        raise (ValueError(
                            u"Error: ¡No se admiten puntos dentro de" +
                            u" las áreas de exclusión!"))

    def _output_raw_filename(self):
        """Output the raw filename with last updated output index on set
            folder.
        """
        import glob
        file_mask = 'forest_road_lines_{}_num_{}.shp'  # due raw_points_layer is a memory layer
        key_for_glob = os.path.join(self.output_folder,
                                    file_mask.format(self._slope_label(), '*'))
        files_list = glob.glob(key_for_glob)
        raw_file_name = self.RAW_FN_TEMPLATE.format(self._slope_label(),
                                                    len(files_list) + 1)

        return os.path.join(self.output_folder, raw_file_name)

    def create_raw_output_layer(self):
        """creates the raw layer as point memory layer
        """
        fn = self._output_raw_filename()
        layer_name = os.path.splitext(os.path.split(fn)[-1])[0]

        self.raw_layer = QgsVectorLayer(
            'Point?crs={}'.format(self.dtm["layer"].crs().toWkt()), layer_name,
            'memory')

        self._update_output_layer()
        if profiler:
            profiler.toolrenderer.setSelectionMethod(2)

        return self.raw_layer

    def _update_output_layer(self):
        """Updates the output layer (raw)
        """
        if self.raw_layer is None or not self.raw_layer.isValid():
            return False
        raw_path_index = self.optimizer.waypoints_index()
        raw_path_height_profile, _, _ = \
                dtm_values_at_points(self.dtm["array"],
                                     raw_path_index,
                                     self.parameters["max_slope_pct"] / 100.0,
                                     self.converter.pixel_width)
        dp = self.raw_layer.dataProvider()
        dp.deleteFeatures(self.raw_layer.allFeatureIds())

        raw_path_coords = self.converter.index_to_coord(raw_path_index)

        features = []
        for point_coords in raw_path_coords:
            new_feature = QgsFeature()
            new_feature.setGeometry(
                QgsGeometry.fromPoint(QgsPoint(*point_coords)))
            features.append(new_feature)

        result, _ = dp.addFeatures(features)
        dp.updateExtents()
        self.raw_layer.triggerRepaint()

        return result

    def create_simplified_output_layer(self, polyline_threshold):
        """Create the simplified layer (LineString Layer)
        """
        raw_path_index = self.optimizer.waypoints_index()

        if polyline_threshold > 0 and len(raw_path_index) > 0:
            path_simplifier = polysimplify.VWSimplifier(raw_path_index)
            simp_path_index = path_simplifier.from_threshold(
                polyline_threshold)
        else:
            simp_path_index = raw_path_index
        """Recover the height profile and create the lines layer."""
        lines_height_data, lines_section_slope, lines_projected_cum_dist = \
                dtm_values_at_points(self.dtm["array"],
                                     simp_path_index,
                                     self.parameters["max_slope_pct"] / 100.0,
                                     self.converter.pixel_width)

        road_layer = af.line_layer_from_coords_array(
            self.converter.index_to_coord(simp_path_index), self.output_folder,
            self.dtm["layer"].crs(), self._slope_label(), lines_height_data,
            lines_section_slope, lines_projected_cum_dist)

        AttributeLayerSymbology(road_layer, "slope_p",
                                self.parameters["max_slope_pct"])
        return road_layer
    def createAnalysis(self):

        newTableName = self.dlg.cmbAnalysis.currentText()

        analysisLayerList = self.projectInstance.mapLayersByName(newTableName)
        if len(analysisLayerList) == 0:
            analysisLayer = QgsVectorLayer("Polygon?crs=EPSG:2056",
                                           newTableName,
                                           "memory")
            analysisDp = analysisLayer.dataProvider()
            analysisCreateNewLayer = True
        elif len(analysisLayerList) == 1:
            analysisLayer = analysisLayerList[0]
            analysisDp = analysisLayer.dataProvider()
            analysisCreateNewLayer = False

        # Delete all existing features in layer if applicable
        iter = analysisLayer.getFeatures()
        featuresToDelete = []
        for feature in iter:
            featuresToDelete.append(feature.id())
        analysisDp.deleteFeatures(featuresToDelete)

        # Check if one analysis is selected
        selectedIndex = self.dlg.cmbAnalysis.currentIndex()
        if selectedIndex == 0:
            self.messageBar.pushWarning("Erreur ",
                                        str(u"Sélectionnez une analyse!"))
            return

        # Get the analysis parameters
        params = self.getAnalysisFromDb()
        self.qstr = params["querystring"]
        if self.qstr is None or self.qstr == "":
            self.messageBar.pushWarning("Erreur",
                                        str(u"Requête mal définie") + self.qstr)
            return

        # Validate user inputs
        if (self.dlg.lnYearStart.text() == ""
            and params["date_filtering"]
                and not self.dlg.chkLastSurvey.isChecked()):

            self.messageBar.pushCritical("Erreur",
                                         "Vous devez saisir une date "
                                         + newTableName)
            return

        if ((self.dlg.lnYearStart.text() == ""
             or self.dlg.lnYearEnd.text() == "")
                and params["date_filtering"]
                and params["timerange_filtering"]):

            self.messageBar.pushCritical("Erreur",
                                         "Vous devez  saisir 2 dates"
                                         + newTableName)
            return

        # Adapt connection string if applicable - method could be cleaner...
        if (params["date_filtering"]
            and not self.dlg.chkLastSurvey.isChecked()
                and not params["timerange_filtering"]):

            self.qstr = self.qstr.replace('--EDITTIMESELECT',
                                          ' ,' + self.dlg.lnYearStart.text() +
                                          ' annee ')
            self.qstr = self.qstr.replace('--EDITTIMESTRING',
                                          ' AND ' + params["datefield"] + ' = '
                                          + self.dlg.lnYearStart.text())

        if (params["date_filtering"]
            and self.dlg.chkLastSurvey.isChecked()
                and not params["timerange_filtering"]):

            self.qstr = self.qstr.replace('--EDITTIMESELECT',
                                          ' , max(' + params["datefield"]
                                          + ') annee ')

        if (params["date_filtering"]
                and params["timerange_filtering"]):
            self.qstr = self.qstr.replace('--STARTYEAR',
                                          self.dlg.lnYearStart.text())
            self.qstr = self.qstr.replace('--ENDYEAR',
                                          self.dlg.lnYearEnd.text())

        coupeFilter = self.dlg.lnCoupeType.text()
        self.editCoupeFilter(coupeFilter, params["coupetype_filtering"])

        admFilter = self.dlg.txtAdmShortName.text()
        self.editAdmFilter(admFilter)

        # Advanced users and developers can see and edit the Query string
        if self.dlg.btAdvancedUserMode.isChecked():
            self.qstr = self.dlg.txtMssqlQuery.toPlainText()
        # Get the geometric layer from PostGIS
        pgLayer = self.pgdb.getLayer(params["join_target_schema"],
                                     params["join_target_table"],
                                     "geom",
                                     "",
                                     params["join_target_table"],
                                     "fake_id")

        if not pgLayer:
            self.messageBar.pushCritical("Erreur au chargement de la couche",
                                         params["join_target_schema"] + '.' +
                                         params["join_target_table"])
            return

        # Execute the query and parse the results
        query = self.qtmsdb.exec_(self.qstr)
        # TODO: Check query validity
        query.setForwardOnly(True)

        # Create memory layer and add it to LegendInterface
        joinTableName = "SELVANS"
        selvansTable = QgsVectorLayer("Point?crs=EPSG:2056",
                                      joinTableName,
                                      "memory")

        selvansTableProvider = selvansTable.dataProvider()

        # Add fields with type given as defined in table main.analysis
        fieldList = params["field_of_interest"].split(",")
        joinedFieldsNames = []
        if params["field_of_interest_type"] == "number":
            for fieldname in fieldList:
                selvansTableProvider.addAttributes([QgsField(
                    params["join_source_fkfield"], QVariant.Int)])
                selvansTableProvider.addAttributes([QgsField(
                    fieldname, QVariant.Double)])
                joinedFieldsNames.append(joinTableName + '_' + fieldname)
        else:
            for fieldname in fieldList:
                selvansTableProvider.addAttributes([QgsField(
                    params["join_source_fkfield"], QVariant.Int)])
                selvansTableProvider.addAttributes([QgsField(
                    fieldname, QVariant.String)])
                joinedFieldsNames.append(joinTableName + '_' + fieldname)

        # Some more field are required for tables related to surveys
        if params["date_filtering"]:
            selvansTableProvider.addAttributes([QgsField(
                'ANNEE_VALEUR', QVariant.Int)])
            joinedFieldsNames.append(joinTableName + '_' + 'ANNEE_VALEUR')
            fieldList.append('ANNEE_VALEUR')
            selvansTableProvider.addAttributes([
                QgsField('DIV_SERIE', QVariant.Int)])
            joinedFieldsNames.append(joinTableName + '_DIV_SERIE')
            fieldList.append('DIV_SERIE')

        # Create progress bar
        progress = self.createProgressbar(query.numRowsAffected())

        # Parse the query result and add the features to memory layer
        self.fillSelvansTable(query,
                              selvansTableProvider,
                              progress, fieldList,
                              params["join_source_fkfield"])
        selvansTable.updateFields()
        self.projectInstance.addMapLayer(selvansTable, False)
        self.projectInstance.addMapLayer(pgLayer, False)
        self.messageBar.clearWidgets()

        # Join the memory layer to a geographic PG layer
        # NOTE: layers MUST be added to the project for join to work in pyQGIS
        targetlayer = self.joinLayer(pgLayer,
                                     params["join_target_pkfield"],
                                     selvansTable,
                                     params["join_source_fkfield"])

        # Get the fields names of the PG layer
        fields = targetlayer.fields()
        fieldslist = []
        for field in fields:
            fieldslist.append(field)

        # Copy the features to the result layer
        analysisDp.deleteAttributes(analysisDp.attributeIndexes())
        analysisLayer.updateFields()
        analysisDp.addAttributes(fieldslist)
        analysisLayer.updateFields()

        outFeat = QgsFeature()
        iter = targetlayer.getFeatures()
        for inFeat in iter:
            if inFeat.geometry():
                outFeat.setGeometry(inFeat.geometry())
                outFeat.setAttributes(inFeat.attributes())
                analysisDp.addFeatures([outFeat])

        analysisLayer.updateFields()
        # QgsProject.instance().addMapLayer(analysisLayer)

        if self.dlg.chkSaveAnalysisResult.isChecked():
            self.saveAnalysisToDisk(analysisLayer)

        if analysisCreateNewLayer:
            self.messageBar.pushCritical("Avertissement",
                                         str(u"La couche est manquante - " +
                                             u"mettre à jour l'impression..."))

            root = QgsProject.instance().layerTreeRoot()
            sgeoGroup = root.findGroup('Analyses SELVANS')
            if sgeoGroup:
                sgeoGroup.addLayer(analysisLayer)
            else:
                QgsProject.instance().addMapLayer(analysisLayer)

        self.activateLastAnalysis(analysisLayer)
        if qversion == 3:
            self.expandGroup("Analyses SELVANS", True)
        else:
            print("handle that too")
        # 24.11.2016: deactivated for now
        self.setStyleFromDb(params, pgLayer, analysisLayer)
        analysisLayer.triggerRepaint()

        # Zoom to selected administration(s)
        self.zoomToSelectedAdministration(admFilter)

        # remove temporary map layers from project
        # Backward compatibility QGIS3=>2
        if qversion == 3:
            self.projectInstance.removeMapLayer(selvansTable)
            self.projectInstance.removeMapLayer(pgLayer)
        else:
            print("handle that")
Example #14
0
def stylize_map(layer: QgsVectorLayer) -> [List[str], List[str]]:
    """Stylize the layer with unique colors per timezone

    Args:
        layer (QgsVectorLayer): The layer to stylize

    Returns:
        [List[str], List[str]]: A list with all timezone ids and one with the respective color
    """

    print("Reading timezones from file")
    timezones = layer.uniqueValues(layer.fields().indexOf("tzid"))
    timezones = list(timezones)
    timezones.sort()

    categorized_renderer = QgsCategorizedSymbolRenderer()

    print("Stylizing map")

    timezone_ids = []
    timezone_colors = []
    features = layer.getFeatures()
    categories = []
    currentColor = 0

    for tz in timezones:
        # Modify the Etc timezones to match the Qt format

        qt_tz = tz

        # There are a few exceptions where the Qt timezone ids differ from the dataset ids:
        match = re.match(r"Etc/GMT([+-])(\d+)", tz)
        if match:
            qt_tz = f"UTC{match.group(1)}{match.group(2):0>2}:00"
        elif tz == "Etc/UTC":
            qt_tz = "UTC"
        elif tz == "Etc/GMT":
            qt_tz = "UTC+00:00"

        # Generate a consecutive color for each timezone
        currentColor += 25000
        r = (currentColor >> 16) & 255
        g = (currentColor >> 8 ) & 255
        b = (currentColor      ) & 255

        # Add it to the mapping
        rh = hex(r)[2:]
        gh = hex(g)[2:]
        bh = hex(b)[2:]
        timezone_ids.append(qt_tz)
        timezone_colors.append(f"#{rh:0>2}{gh:0>2}{bh:0>2}")

        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        symbol_layer = QgsSimpleFillSymbolLayer.create({"color": f"{r}, {g}, {b}"})
        symbol_layer.setStrokeWidth(0.0)
        symbol_layer.setStrokeStyle(Qt.PenStyle.NoPen)
        symbol.changeSymbolLayer(0, symbol_layer)

        category = QgsRendererCategory(tz, symbol, tz)
        categories.append(category)

    renderer = QgsCategorizedSymbolRenderer("tzid", categories)
    layer.setRenderer(renderer)
    layer.triggerRepaint()

    return timezone_ids, timezone_colors
Example #15
0
    def __build_lines(self) -> None:
        """
        Save the current self.__tmp_units in an in-memory layer called 'Parallel Unit Lines'
        Create a layer with the given name if it is not existing
        :return: Nothing
        """
        unit_list = list()
        for unit in self.__tmp_units:
            unit_list.append([unit[0], unit[1].asGeometry()])

        layers = [
            lyr for lyr in self.__iface.mapCanvas().layers()
            if lyr.name() == "Parallel Unit Lines"
        ]
        if len(layers) == 0:
            current_layer = self.__iface.mapCanvas().currentLayer()
            # noinspection PyArgumentList
            crs = QgsProject.instance().crs().toWkt()
            uri = "linestring?crs=wkt:{}&field=name:string(255)".format(crs)
            vector_layer = QgsVectorLayer(uri, "Parallel Unit Lines", "memory")
            # noinspection PyArgumentList
            QgsProject.instance().addMapLayer(vector_layer)
            self.__iface.mapCanvas().setCurrentLayer(current_layer)
        else:
            vector_layer = layers[0]

        if (not vector_layer.isValid()) or (vector_layer.type() !=
                                            QgsMapLayer.VectorLayer):
            self.__iface.messageBar(). \
                pushCritical("Wrong Layer Type",
                             "The layer \"Parallel Unit Lines\" cannot be created or has the wrong format")
            return

        vpr = vector_layer.dataProvider()
        fields = [f.name() for f in vpr.fields().toList()]
        if "name" not in fields:
            # noinspection PyArgumentList
            vpr.addAttributes([QgsField("name", QVariant.String, len=255)])

        name_field_index = vpr.fields().indexOf("name")
        if vpr.fields()[name_field_index].typeName().lower() != "string":
            self.__iface.messageBar(). \
                pushCritical("Wrong Attribute Type",
                             "The name attribute of the layer \"Parallel Unit Lines\" is not of type \"String\"!")
            return

        try:
            # adding the features to the layer
            for unit in unit_list:
                f = QgsFeature()
                f.setGeometry(unit[1])
                attr = list()
                for _ in vpr.fields().toList():
                    attr.append(None)
                attr[name_field_index] = unit[0]
                f.setAttributes(attr)
                vpr.addFeatures([f])

            vector_layer.updateExtents()

            # at least: try to set symbology
            symbology = list()
            for index in range(self.model.rowCount()):
                row = self.model.row(index)
                # noinspection PyArgumentList
                sym = QgsSymbol.defaultSymbol(vector_layer.geometryType())
                sym.setColor(row.color)
                sym.setWidth(0.4)
                category = QgsRendererCategory(row.name, sym, row.name)
                symbology.append(category)

            renderer = QgsCategorizedSymbolRenderer("name", symbology)
            vector_layer.setRenderer(renderer)
            vector_layer.triggerRepaint()

        except Exception as e:
            _, _, exc_traceback = sys.exc_info()
            text = "Error Message:\n{}\nTraceback:\n{}".format(
                str(e), '\n'.join(traceback.format_tb(exc_traceback)))
            # noinspection PyTypeChecker,PyCallByClass
            QgsMessageLog.logMessage(text, level=2)
        finally:
            self.reset()
class CatalogDialogTool(QObject):
    """
    Tool for managing the search and export functionality
    """

    def __init__(self, iface, dialog_ui, bbox_tool):
        """
        Constructor for the dialog tool
        :param iface: The QGIS Interface
        :param dialog_ui: The dialog GUI
        :param bbox_tool The bounding box tool
        :return: dialog tool
        """
        QObject.__init__(self, None)
        self.iface = iface
        self.dialog_ui = dialog_ui
        self.bbox_tool = bbox_tool

        self.progress_bar = None
        self.progress_message_bar = None
        self.progress_message_bar_widget = None
        self.search_thread_pool = QThreadPool()
        self.search_lock = Lock()
        self.export_thread_pool = QThreadPool()
        self.export_lock = Lock()
        self.query = None
        self.previous_credentials = None
        self.export_file = None
        self.footprint_layer = None

        self.filters = CatalogFilters(self.dialog_ui)

        self.dialog_ui.aoi_button.clicked.connect(self.aoi_button_clicked)
        self.dialog_ui.reset_button.clicked.connect(self.reset_button_clicked)
        self.dialog_ui.export_button.clicked.connect(self.export_button_clicked)
        self.bbox_tool.released.connect(self.search)
        self.model = None

    def init_progress_bar(self, progress_max):
        """
        Sets up the progress bar for search functionality
        :return: None
        """
        if not self.progress_message_bar:
            self.progress_message_bar = self.iface.messageBar().createMessage("Querying for data")
            self.progress_bar = QProgressBar()
            self.progress_bar.setMinimum(0)
            self.progress_bar.setMaximum(progress_max)
            self.progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignCenter)
            self.progress_message_bar.layout().addWidget(self.progress_bar)
            self.progress_message_bar_widget = self.iface.messageBar().pushWidget(self.progress_message_bar, self.iface.messageBar().INFO)

    def init_layers(self):
        """
        Sets up the layers for rendering the items
        :return: None
        """
        if self.footprint_layer:
            QgsMapLayerRegistry.instance().removeMapLayer(self.footprint_layer.id())

        self.footprint_layer = QgsVectorLayer("Polygon?crs=EPSG:4326", "Catalog Footprints", "memory")
        self.footprint_layer.setCrs(QgsCoordinateReferenceSystem(4326), True)
        self.footprint_layer.dataProvider().addAttributes(CatalogAcquisitionFeature.get_fields())
        QgsMapLayerRegistry.instance().addMapLayer(self.footprint_layer)

    def clear_widgets(self):
        """
        Clears the progress bar
        :return: None
        """
        self.progress_bar = None
        self.progress_message_bar = None
        if self.progress_message_bar_widget:
            self.iface.messageBar().popWidget(self.progress_message_bar_widget)
        self.progress_message_bar_widget = None

    def is_searching(self):
        """
        Check to see if the system is still searching (checks if there's work in the search thread pool)
        :return: True if searching; False otherwise
        """
        return self.get_search_active_thread_count() > 0

    def is_exporting(self):
        """
        Check to see if the system is still exporting (checks if there's work in the export thread pool)
        :return: True if searching; False otherwise
        """
        return self.get_export_active_thread_count() > 0

    def get_search_active_thread_count(self):
        """
        Gets the number of active threads in the search thread pool
        :return:
        """
        with self.search_lock:
            return self.search_thread_pool.activeThreadCount()

    def get_export_active_thread_count(self):
        """
        Gets the number of active threads in the export thread pool
        :return:
        """
        with self.export_lock:
            return self.export_thread_pool.activeThreadCount()

    def aoi_button_clicked(self):
        """
        Validates and runs the search if validation successful
        :return: None
        """
        # can't run search during export
        if self.is_exporting():
            self.iface.messageBar().pushMessage("Error", "Cannot run search while export is running.", level=QgsMessageBar.CRITICAL)
        # can't run multiple search
        elif self.is_searching():
            self.iface.messageBar().pushMessage("Error", "Cannot run a new search while a search is running.", level=QgsMessageBar.CRITICAL)
        else:
            self.bbox_tool.reset()
            self.iface.mapCanvas().setMapTool(self.bbox_tool)

    def reset_button_clicked(self):
        """
        Resets filters.
        :return: None
        """
        self.reset()

    def export_button_clicked(self):
        """
        Validates and runs the export if validation successful
        :return: None
        """
        # can't run export during search
        if self.is_searching():
            self.iface.messageBar().pushMessage("Error", "Cannot run export while search is running.", level=QgsMessageBar.CRITICAL)
        # can't run multiple exports
        elif self.is_exporting():
            self.iface.messageBar().pushMessage("Error", "Cannot run a new export while a export is running.", level=QgsMessageBar.CRITICAL)
        else:
            self.export()

    def search(self, top, bottom, left, right):
        self.search_thread_pool.waitForDone(0)

        # validate credentials if they changed
        errors = []
        username, password, api_key, max_items_to_return = SettingsOps.get_settings()
        credentials = [username, password, api_key]
        if not self.previous_credentials or self.previous_credentials != credentials:
            SettingsOps.validate_stored_info(username, password, api_key, max_items_to_return, errors)
        self.previous_credentials = credentials

        # validate filters
        if not errors:
            self.filters.validate(errors)

        if errors:
            self.iface.messageBar().pushMessage("Error", "The following errors occurred: " + "<br />".join(errors), level=QgsMessageBar.CRITICAL)
        else:
            self.init_layers()

            self.dialog_ui.tab_widget.setCurrentIndex(RESULTS_TAB_INDEX)
            
            next_x_list = self.drange_list(float(left) + INCREMENTAL_INTERVAL, float(right), INCREMENTAL_INTERVAL)
            next_y_list = self.drange_list(float(bottom) + INCREMENTAL_INTERVAL, float(top), INCREMENTAL_INTERVAL)
            self.init_progress_bar(len(next_x_list) * len(next_y_list))

            self.model = CatalogTableModel(self.dialog_ui.table_view)
            self.dialog_ui.table_view.setModel(self.model)
            self.dialog_ui.table_view.selectionModel().selectionChanged.connect(self.selection_changed)

            if not self.query:
                self.query = GBDQuery(username=username, password=password, api_key=api_key)

            filters = self.filters.get_query_filters()
            time_begin = self.filters.get_datetime_begin()
            time_end = self.filters.get_datetime_end()

            current_x = float(left)
            current_y = float(bottom)
            for next_x in next_x_list:
                for next_y in next_y_list:
                    search_runnable = CatalogSearchRunnable(self.query, self.model, self, top=next_y, left=current_x, right=next_x, bottom=current_y, 
                                                            time_begin=time_begin, time_end=time_end, filters=filters)
                    search_runnable.task_object.task_complete.connect(self.on_search_complete)
                    self.search_thread_pool.start(search_runnable)
                    current_y = next_y
                current_y = bottom
                current_x = next_x

    def reset(self):
        self.filters.remove_all()

    def export(self):
        self.export_thread_pool.waitForDone(0)
        acquisitions = None
        if self.model is not None:
            acquisitions = self.model.data

        if not acquisitions:
            self.iface.messageBar().pushMessage("Error", "No data to export.", level=QgsMessageBar.CRITICAL)
        else:
            # open file ui
            select_file_ui = QFileDialog()
            starting_file = self.export_file or os.path.expanduser("~")
            export_file = select_file_ui.getSaveFileName(None, "Choose output file", starting_file, SELECT_FILTER)

            if export_file:
                self.export_file = export_file
                self.init_progress_bar(0)
                export_runnable = CatalogExportRunnable(acquisitions, self.export_file)
                export_runnable.task_object.task_complete.connect(self.on_export_complete)
                self.export_thread_pool.start(export_runnable)

    @pyqtSlot()
    def on_search_complete(self):
        thread_count = self.get_search_active_thread_count()
        if self.progress_message_bar:
            self.progress_bar.setValue(self.progress_bar.value() + 1)
        if thread_count == 0:
            self.clear_widgets()
            self.dialog_ui.table_view.resizeColumnsToContents()

    @pyqtSlot()
    def on_export_complete(self):
        thread_count = self.get_export_active_thread_count()
        if self.progress_message_bar:
            self.progress_bar.setValue(self.progress_bar.value() + 1)
        if thread_count == 0:
            self.clear_widgets()
            self.iface.messageBar().pushMessage("Info", 'File export has completed to "%s".' % self.export_file)

    def selection_changed(self, selected, deselected):
        self.footprint_layer.startEditing()

        # draw footprints for selected rows
        selected_rows = set()
        for index in selected.indexes():
            selected_rows.add(index.row())
        for row in selected_rows:
            acquisition = self.model.get(row)
            feature_id = self.model.generate_feature_id()
            self.model.set_feature_id(acquisition, feature_id)
            feature = CatalogAcquisitionFeature(feature_id, acquisition)
            self.footprint_layer.dataProvider().addFeatures([feature])

        # remove footprints for deselected rows
        deselected_rows = set()
        for index in deselected.indexes():
            deselected_rows.add(index.row())
        feature_ids_to_remove = []
        for row in deselected_rows:
            acquisition = self.model.get(row)
            feature_id = self.model.get_feature_id(acquisition)
            feature_ids_to_remove.append(feature_id)
            self.model.remove_feature_id(acquisition)
        if feature_ids_to_remove:
            self.footprint_layer.dataProvider().deleteFeatures(feature_ids_to_remove)

        self.footprint_layer.commitChanges()
        self.footprint_layer.updateExtents()
        self.footprint_layer.triggerRepaint()

    def drange_list(self, start, stop, step):
        drange_list = []
        r = start
        while r < stop:
            drange_list.append(r)
            r += step
        if not drange_list:
            drange_list.append(stop)
        return drange_list
Example #17
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      divide=0):
    """Adding Points along the line
    """
    # Create a new memory layer and add a distance attribute self.layerNameLine
    #layer_crs = virt_layer.setCrs(layer.crs())
    virt_layer = QgsVectorLayer("Point?crs=%s" % layer.crs().authid(),
                                layerout,
                                "memory")
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()   # actually writes attributes
    units = layer.crs().mapUnits()
    unit_dic = {
        QGis.Degrees: 'Degrees',
        QGis.Meters: 'Meters',
        QGis.Feet: 'Feet',
        QGis.UnknownUnit: 'Unknown'}
    unit = unit_dic.get(units, 'Unknown')
    provider.addAttributes([QgsField("fid", QVariant.Int)])
    provider.addAttributes([QgsField("cng_("+unit+")", QVariant.Int)])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint, endpoint, distance, geom,
                                    fid, force, divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    QgsMapLayerRegistry.instance().addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    #from here Add labeling
    #generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName", "cng_("+unit+")")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")

        #virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbolV2.createSimple({"name": "capital"})
    # virt_layer.setRendererV2(QgsSingleSymbolRendererV2(symbol))
    virt_layer.triggerRepaint()
    return
Example #18
0
    def run(self):
        """Run method that performs all the real work"""

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False
            self.dlg = HealthSIGDialog()

        # Clear the contents of the comboBox from previous runs
        self.dlg.comboBox.clear()
        # Populate the comboBox
        self.dlg.comboBox.addItems(["Hospitais", "ACES", "Farmácias"])
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()

        # See if OK was pressed and check index
        index = self.dlg.comboBox.currentIndex()

        # file directory for relative paths
        dirn = os.path.dirname(__file__)

        # escolheu unidades de saude
        if result and index == 1:
            # create layer
            vl = QgsVectorLayer("Point?crs=epsg:3763&index=yes", "aces",
                                "memory")
            pr = vl.dataProvider()

            # Enter editing mode
            vl.startEditing()

            # add fields
            pr.addAttributes([
                QgsField("ACES", QVariant.String),
                QgsField("ARS", QVariant.String),
                QgsField("Coord_X", QVariant.Double),
                QgsField("Coord_Y", QVariant.Double),
                QgsField("Num_USF", QVariant.Int)
            ])
            vl.updateFields(
            )  # tell the vector layer to fetch changes from the provider

            # add features ---> IR BUSCAR AO JSON....
            filename = os.path.join(dirn, 'unidades_saude.json')
            #with open("/home/skywalker/.local/share/QGIS/QGIS3/profiles/default/python/plugins/health_sig/unidades_saude.json",
            #          "r") as read_file:
            with open(filename, "r") as read_file:
                json_file = json.load(read_file)

            # transformar coordenadas gps para as pretendidas
            crsSrc = QgsCoordinateReferenceSystem(4326)  # gps
            crsDest = QgsCoordinateReferenceSystem(3763)  # pt
            xform = QgsCoordinateTransform(crsSrc, crsDest,
                                           QgsProject.instance())

            # addfeatures
            for entry in json_file:
                if entry["fields"]["tempo"] == "2019-01":  # valores recentes
                    x_coord = float(entry["geometry"]["coordinates"][0])
                    y_coord = float(entry["geometry"]["coordinates"][1])
                    num_usf = int(
                        entry["fields"]
                        ["total_usf"])  #numero de unidades de saude familiares
                    entidade = entry["fields"]["entidade"]
                    ars = entry["fields"]["ars"]
                    fet = QgsFeature()
                    fet.setGeometry(QgsGeometry().buffer(
                        distance=num_usf * 10, segments=100).fromPointXY(
                            xform.transform(QgsPointXY(x_coord, y_coord))))
                    fet.setAttributes([
                        QVariant(entidade),
                        QVariant(ars),
                        QVariant(x_coord),
                        QVariant(y_coord),
                        QVariant(num_usf)
                    ])
                    pr.addFeatures([fet])

            vl.updateExtents()
            vl.commitChanges()

            # fazer render categorizado
            features = vl.getFeatures()

            categories = []

            for feat in features:

                f = feat.attributes()[4]  # num_usf
                entidade = feat.attributes()[0]  # entidade
                symbol = QgsSymbol.defaultSymbol(vl.geometryType())
                svgStyle = {}
                path_uni = os.path.join(dirn, 'medicine.svg')
                svgStyle['name'] = path_uni
                svgStyle['size'] = str((f / 10) + 1)

                symbol_layer = QgsSvgMarkerSymbolLayer.create(svgStyle)

                if symbol_layer is not None:
                    symbol.changeSymbolLayer(0, symbol_layer)

                # create renderer object
                category = QgsRendererCategory(f, symbol, str(entidade))
                # entry for the list of category items
                categories.append(category)

                # create renderer object
            renderer = QgsCategorizedSymbolRenderer('Num_USF', categories)

            # assign the created renderer to the layer
            if renderer is not None:
                vl.setRenderer(renderer)

            vl.triggerRepaint()

            QgsProject.instance().addMapLayer(vl)

        # hospitais
        elif result and index == 0:
            vl = QgsVectorLayer("Point?crs=epsg:3763&index=yes", "hospitais",
                                "memory")
            pr = vl.dataProvider()

            # Enter editing mode
            vl.startEditing()

            # add fields
            pr.addAttributes([
                QgsField("Hospital", QVariant.String),
                QgsField("Morada", QVariant.String),
                QgsField("Coord_X", QVariant.Double),
                QgsField("Coord_Y", QVariant.Double)
            ])
            vl.updateFields(
            )  # tell the vector layer to fetch changes from the provider

            # add features ---> IR BUSCAR AO JSON....
            filename = os.path.join(dirn, 'hospitais.json')

            with open(filename, "r") as read_file:
                json_file = json.load(read_file)

            # transformar coordenadas gps para as pretendidas
            crsSrc = QgsCoordinateReferenceSystem(4326)  # gps
            crsDest = QgsCoordinateReferenceSystem(3763)  # pt
            xform = QgsCoordinateTransform(crsSrc, crsDest,
                                           QgsProject.instance())

            # addfeatures
            for entry in json_file.keys():

                x_coord = float(json_file[entry][1][1])
                y_coord = float(json_file[entry][1][0])
                morada = json_file[entry][0]
                fet = QgsFeature()
                fet.setGeometry(QgsGeometry().fromPointXY(
                    xform.transform(QgsPointXY(x_coord, y_coord))))
                fet.setAttributes([
                    QVariant(entry),
                    QVariant(morada),
                    QVariant(x_coord),
                    QVariant(y_coord)
                ])
                pr.addFeatures([fet])

            vl.updateExtents()
            vl.commitChanges()

            #symbol = QgsMarkerSymbol.createSimple({'name': 'square', 'color': 'red'})
            svgStyle = {}
            path_hosp = os.path.join(dirn, 'hospital.svg')
            svgStyle['name'] = path_hosp
            svgStyle['size'] = '6'

            symbol_layer = QgsSvgMarkerSymbolLayer.create(svgStyle)
            symbol = QgsSymbol.defaultSymbol(vl.geometryType())
            #vl.renderer().setSymbol(symbol)
            symbol.changeSymbolLayer(0, symbol_layer)
            vl.renderer().setSymbol(symbol)

            # show the change
            vl.triggerRepaint()

            QgsProject.instance().addMapLayer(vl)

        # farmacias
        elif result and index == 2:
            vl = QgsVectorLayer("Point?crs=epsg:3763&index=yes", "farmacias",
                                "memory")
            pr = vl.dataProvider()

            # Enter editing mode
            vl.startEditing()

            # add fields
            pr.addAttributes([
                QgsField("Farmácia", QVariant.String),
                QgsField("Morada", QVariant.String),
                QgsField("Coord_X", QVariant.Double),
                QgsField("Coord_Y", QVariant.Double)
            ])
            vl.updateFields(
            )  # tell the vector layer to fetch changes from the provider

            # add features ---> IR BUSCAR AO JSON....
            filename = os.path.join(dirn, 'farmacias.json')

            with open(filename, "r") as read_file:
                json_file = json.load(read_file)

            # transformar coordenadas gps para as pretendidas
            crsSrc = QgsCoordinateReferenceSystem(4326)  # gps
            crsDest = QgsCoordinateReferenceSystem(3763)  # pt
            xform = QgsCoordinateTransform(crsSrc, crsDest,
                                           QgsProject.instance())

            # addfeatures
            for entry in json_file.keys():
                x_coord = float(json_file[entry][1][1])
                y_coord = float(json_file[entry][1][0])
                morada = json_file[entry][0]
                fet = QgsFeature()
                fet.setGeometry(QgsGeometry().fromPointXY(
                    xform.transform(QgsPointXY(x_coord, y_coord))))
                fet.setAttributes([
                    QVariant(entry),
                    QVariant(morada),
                    QVariant(x_coord),
                    QVariant(y_coord)
                ])
                pr.addFeatures([fet])

            vl.updateExtents()
            vl.commitChanges()

            #symbol = QgsMarkerSymbol.createSimple({'name': 'square', 'color': 'purplet'})
            #vl.renderer().setSymbol(symbol)
            svgStyle = {}
            path_farm = os.path.join(dirn, 'pharmacy.svg')
            svgStyle['name'] = path_farm
            svgStyle['size'] = '4'
            symbol_layer = QgsSvgMarkerSymbolLayer.create(svgStyle)
            symbol = QgsSymbol.defaultSymbol(vl.geometryType())
            # vl.renderer().setSymbol(symbol)
            symbol.changeSymbolLayer(0, symbol_layer)
            vl.renderer().setSymbol(symbol)

            # show the change
            vl.triggerRepaint()

            QgsProject.instance().addMapLayer(vl)
    def search(self):
        try:
            # Current service
            service = self.tab.tabText(self.tab.currentIndex())

            # Params
            params = {'geometry': self.polygonInput.text()}

            # Set auth and add params according to service
            if service == 'BaseMap':
                # ! Auth
                self.bmSetAuth()

                # Set request attributes
                url = 'https://view.geoapi-airbusds.com/api/v1/images'
                auth = self.bmAuth
                headers = None

                # Update params
                params.update({
                    'size': self.maxResultsInput.value(),
                    'insertdtstart': '1970-01-01T00:00:00',
                    'insertdtend': self.now()
                })

                # Dates
                if self.bmFromCheck.isChecked():
                    params['insertdtstart'] = self.bmFromInput.dateTime(
                    ).toString(Qt.ISODate)
                if self.bmToCheck.isChecked():
                    params['insertdtend'] = self.bmToInput.dateTime().toString(
                        Qt.ISODate)

            else:
                # ! Auth
                self.dtSetAuth()

                url = 'https://search.oneatlas.geoapi-airbusds.com/api/v1/opensearch'
                auth = None
                headers = self.dtHeaders

                # Constellations (at least one)
                constellations = []
                if self.dtSpotCheck.isChecked():
                    constellations.append('SPOT')
                if self.dtPleiadesCheck.isChecked():
                    constellations.append('PHR')

                # Dates
                # MAYBE remove hours from dates
                dateFrom, dateTo = '1970-01-01T00:00:00', self.now()
                if self.dtFromCheck.isChecked():
                    dateFrom = self.dtFromInput.dateTime().toString(Qt.ISODate)
                if self.dtToCheck.isChecked():
                    dateTo = self.dtToInput.dateTime().toString(Qt.ISODate)

                # Angles
                angleMin, angleMax = 0, 30
                if self.dtAngleMinCheck.isChecked():
                    angleMin = self.dtAngleMinInput.value()
                if self.dtAngleMaxCheck.isChecked():
                    angleMax = self.dtAngleMaxInput.value()

                # Covers (directlly update params)
                if self.dtCloudCheck.isChecked():
                    params['cloudCover'] = f'[0,{self.dtCloudInput.value()}]'
                if self.dtSnowCheck.isChecked():
                    params['snowCover'] = f'[0,{self.dtSnowInput.value()}]'

                # Workspaces (at leat one)
                workspaces = []
                if self.dtPublicCheck.isChecked():
                    workspaces.append('0e33eb50-3404-48ad-b835-b0b4b72a5625')
                if self.dtPrivateCheck.isChecked():
                    workspaces.append(self.dtWorkspaceId)

                # Update all params with right format
                params.update({
                    'itemsPerPage': self.maxResultsInput.value(),
                    'constellation': ','.join(constellations),
                    'acquisitionDate': f'[{dateFrom},{dateTo}]',
                    'incidenceAngle': f'[{angleMin},{angleMax}]',
                    'workspace': ','.join(workspaces)
                })

            # Finally do the api call
            t = datetime.datetime.now()
            print(f'START {service} search')
            r = self.session.get(url,
                                 auth=auth,
                                 headers=headers,
                                 params=params)
            rSearch = r.json()

            # Exception request error
            if r.status_code != 200:
                self.error(
                    f'{service} search error {r.status_code}\n{rSearch["message"]}'
                )
                return

            # Create the search result layer with fields according to current service
            layer = QgsVectorLayer(
                f'Polygon?crs=epsg:4326&index=yes&{FIELDS[service]}',
                providerLib='memory',
                baseName=f'{service} search results')

            # Extract features
            features = []
            self.errorFeatures = []
            for rFeature in rSearch['features']:
                # Add a feature of the bbox on the new layer
                feature = QgsFeature(layer.fields())
                # Try to get each attributes
                feature['service'] = service
                feature['constellation'] = self.getPropertie(
                    rFeature, 'constellation')
                feature['incidenceAngle'] = self.getPropertie(
                    rFeature, 'incidenceAngle')
                feature['cloudCover'] = self.getPropertie(
                    rFeature, 'cloudCover')
                if service == 'BaseMap':
                    feature['id'] = rFeature['id']
                    feature['insertionDate'] = self.getPropertie(
                        rFeature, 'insertionDate')
                    feature['wmts'] = self.getPropertie(rFeature, 'wmts')
                    # Bbox
                    fBbox = rFeature['properties']['bbox']
                    rectangle = QgsRectangle(fBbox[0], fBbox[1], fBbox[2],
                                             fBbox[3])
                else:
                    feature['id'] = self.getPropertie(rFeature, 'id')
                    feature['acquisitionDate'] = self.getPropertie(
                        rFeature, 'acquisitionDate')
                    feature['snowCover'] = self.getPropertie(
                        rFeature, 'snowCover')
                    try:
                        for json in rFeature['_links']['imagesWmts']:
                            feature[f'wmts_{json["name"]}'] = json['href']
                        for json in rFeature['_links']['imagesWcs']:
                            if 'buffer' in rFeature['rights']:
                                feature[f'wcs_{json["name"]}'] = json['href']
                            else:
                                feature[f'wcs_{json["name"]}'] = None
                    except Exception as e:
                        print(
                            f'ERROR * eF = qgis.utils.plugins["OneAtlas"].mySearch.errorFeatures[{len(self.errorFeatures)}]'
                        )
                        print(str(e))
                        self.errorFeatures.append(rFeature)
                        continue
                    # Bbox
                    coordinates = rFeature['geometry']['coordinates'][0]
                    rectangle = QgsRectangle(coordinates[0][0],
                                             coordinates[0][1],
                                             coordinates[2][0],
                                             coordinates[2][1])
                # Add geometry from rectangle
                feature.setGeometry(
                    QgsGeometry.fromWkt(rectangle.asWktPolygon()))
                # Add feature to list
                features.append(feature)

            # Total
            if service == 'BaseMap':
                # Note : rSearch['totalResults'] is maybe the number of total element in bbox ?
                #   and numberOfElements is the true total result
                total = rSearch['numberOfElements']
                color = QColor.fromRgb(0, 250, 0)
            else:
                total = rSearch['totalResults']
                color = QColor.fromRgb(0, 250, 250)

            if len(self.errorFeatures) > 0:
                total -= len(self.errorFeatures)
                print(f'* {len(self.errorFeatures)} error feature')

            # Notification for number of total results
            msgBox = QMessageBox()
            msgBox.setWindowTitle(WINDOW_TITLE)
            msgBox.setText(f'There are {total} results')
            if total > len(features):
                msgBox.setIcon(QMessageBox.Warning)
                msgBox.setInformativeText(
                    f'The maximum is configured to {self.maxResultsInput.value()}\nPlease refine your criteria or your AOI'
                )
                msgBox.setStandardButtons(QMessageBox.Retry
                                          | QMessageBox.Ignore)
                msgBox.setDefaultButton(QMessageBox.Retry)
            else:
                msgBox.setIcon(QMessageBox.Information)
                msgBox.setStandardButtons(QMessageBox.Retry | QMessageBox.Ok)
                msgBox.setDefaultButton(QMessageBox.Ok)
            msgBox.button(QMessageBox.Retry).setText('Refine')
            msgBox.button(QMessageBox.Retry).setIcon(
                QIcon(os.path.dirname(__file__) + f'/search.png'))

            reply = msgBox.exec_()
            if reply == QMessageBox.Retry or len(features) == 0:
                return

            # Add result feature to the new layer
            (res, outFeats) = layer.dataProvider().addFeatures(features)

            # Change layer syle programmatically
            # Note : if we styling before save and add layer, we avoid to update legend style
            #   => self.iface.layerTreeView().refreshLayerSymbology(vlayer.id())
            symbol = layer.renderer().symbol()
            symbol.setOpacity(0.2)
            symbol.setColor(color)

            QgsProject.instance().addMapLayer(layer)
            # And refresh view
            layer.triggerRepaint()
            self.close()
        except:
            return
Example #20
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      fo_fila=False,
                      divide=0,
                      decimal=2):
    """Adding Points along the line
    """

    crs = layer.crs().authid()

    # TODO check for virtual or shapelayer and set virt_layer according to it
    shape = False
    if shape:
        # define fields for feature attributes. A list of QgsField objects is needed
        fields = [
            QgsField("first", QVariant.Int),
            QgsField("second", QVariant.String)
        ]
        # create an instance of vector file writer, which will create the vector file.
        # Arguments:
        # 1. path to new file (will fail if exists already)
        # 2. encoding of the attributes
        # 3. field map
        # 4. geometry type - from WKBTYPE enum
        # 5. layer's spatial reference (instance of
        #    QgsCoordinateReferenceSystem) - optional
        # 6. driver name for the output file
        writer = QgsVectorFileWriter("my_shapes.shp", "CP1250", fields,
                                     Qgis.WKBPoint, crs, "ESRI Shapefile")
        if writer.hasError() != QgsVectorFileWriter.NoError:
            # fix_print_with_import
            print("Error when creating shapefile: ", writer.hasError())
        # add a feature
        fet = QgsFeature()
        fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10)))
        fet.setAttributes([1, "text"])
        writer.addFeature(fet)
        # delete the writer to flush features to disk (optional)
        del writer

        layer_type = "Shapefile"  # TODO Add Shapefile functionality here
    else:
        layer_type = "memory"

    virt_layer = QgsVectorLayer("Point?crs=%s" % crs, layerout, layer_type)
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()  # actually writes attributes

    units = layer.crs().mapUnits()

    unitname = QgsUnitTypes.toString(units)
    provider.addAttributes([
        QgsField("fid", QVariant.Int),
        QgsField("cng" + unitname, QVariant.Double)
    ])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint, endpoint, distance, geom, fid,
                                    force, fo_fila, divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    proj = QgsProject.instance()
    proj.addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    # generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName", "cng")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")
        virt_layer.setCustomProperty("labeling/formatNumbers", "true")
        virt_layer.setCustomProperty("labeling/decimals", decimal)
        virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbol.createSimple({"name": "capital"})
    # virt_layer.setRenderer(QgsSingleSymbolRenderer(symbol))
    virt_layer.triggerRepaint()
    return
Example #21
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      divide=0):
    """Adding Points along the line
    """
    # Create a new memory layer and add a distance attribute self.layerNameLine
    #layer_crs = virt_layer.setCrs(layer.crs())
    virt_layer = QgsVectorLayer("Point?crs=%s" % layer.crs().authid(),
                                layerout, "memory")
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()  # actually writes attributes
    units = layer.crs().mapUnits()
    unit_dic = {
        QGis.Degrees: 'Degrees',
        QGis.Meters: 'Meters',
        QGis.Feet: 'Feet',
        QGis.UnknownUnit: 'Unknown'
    }
    unit = unit_dic.get(units, 'Unknown')
    provider.addAttributes([QgsField("fid", QVariant.Int)])
    provider.addAttributes([QgsField("cng_(" + unit + ")", QVariant.Int)])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint, endpoint, distance, geom, fid,
                                    force, divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    QgsMapLayerRegistry.instance().addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    #from here Add labeling
    #generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName",
                                     "cng_(" + unit + ")")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")

        #virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbolV2.createSimple({"name": "capital"})
    # virt_layer.setRendererV2(QgsSingleSymbolRendererV2(symbol))
    virt_layer.triggerRepaint()
    return
Example #22
0
def checkoutLayer(repo, layername, bbox, ref=None):
    ref = ref or repo.HEAD
    newCommitId = repo.revparse(ref)
    trackedlayer = getTrackingInfoForGeogigLayer(repo.url, layername)
    if trackedlayer is not None:
        try:
            source = trackedlayer.source
            layer = QgsVectorLayer(source, layername, "ogr")
            assert layer.isValid()
        except:
            removeTrackedLayer(trackedlayer.source)
            trackedlayer = None
            filename = layerGeopackageFilename(layername, repo.title,
                                               repo.group)
            source = "%s|layername=%s" % (filename, layername)
    else:
        filename = layerGeopackageFilename(layername, repo.title, repo.group)
        source = "%s|layername=%s" % (filename, layername)

    if trackedlayer is None:
        repo.checkoutlayer(filename, layername, bbox, ref or repo.HEAD)
        addTrackedLayer(source, repo.url)
        try:
            layer = layerFromSource(source)
            iface.messageBar().pushMessage(
                "GeoGig",
                "Layer was already included in the current QGIS project",
                level=QgsMessageBar.INFO,
                duration=5)
        except WrongLayerSourceException:
            layer = loadLayerNoCrsDialog(source, layername, "ogr")
            QgsMapLayerRegistry.instance().addMapLayers([layer])
            iface.messageBar().pushMessage("GeoGig",
                                           "Layer correctly added to project",
                                           level=QgsMessageBar.INFO,
                                           duration=5)
    elif ref is not None:
        currentCommitId = getCommitId(source)
        try:
            layer = layerFromSource(source)
            wasLoaded = True
        except WrongLayerSourceException:
            layer = loadLayerNoCrsDialog(source, layername, "ogr")
            wasLoaded = False

        if newCommitId != currentCommitId:
            if hasLocalChanges(layer):
                raise HasLocalChangesError()
            filename, layername = namesFromLayer(layer)
            repo.checkoutlayer(filename, layername, bbox, ref)
            layer.reload()
            if not wasLoaded:
                QgsMapLayerRegistry.instance().addMapLayers([layer])
                iface.messageBar().pushMessage(
                    "GeoGig",
                    "Layer correctly added to project",
                    level=QgsMessageBar.INFO,
                    duration=5)
            else:
                iface.messageBar().pushMessage(
                    "GeoGig",
                    "Layer correctly updated to specified version",
                    level=QgsMessageBar.INFO,
                    duration=5)
                layer.triggerRepaint()
        else:
            if wasLoaded:
                iface.messageBar().pushMessage(
                    "GeoGig",
                    "Layer was already included in the current QGIS project",
                    level=QgsMessageBar.INFO,
                    duration=5)
            else:
                QgsMapLayerRegistry.instance().addMapLayers([layer])
                iface.messageBar().pushMessage(
                    "GeoGig",
                    "Layer correctly added to the current QGIS project",
                    level=QgsMessageBar.INFO,
                    duration=5)

    #repoWatcher.repoChanged.emit(repo)
    return layer
Example #23
0
    def run(self):

        global almacen
        #coloco el puntero arriba del todo
        QgsProject.instance().layerTreeRegistryBridge().setLayerInsertionPoint(
            QgsProject.instance().layerTreeRoot(), 0)

        #genero una lista con los sistemas de referencia
        misdatos = [["Etrs89 Zona30 (25830)", "25830"],
                    ["Etrs89 Zona29 (25829)", "25829"],
                    ["ED50 Zona30 (23030)", "23030"],
                    ["ED50_Zona29 (23029)", "23029"],
                    ["WGS84 geograficas sexagesimales(4326)", "4326"],
                    ["WGS84 geograficas centesimales(4326)", "4258"]]
        self.dlg.comboBox_src.clear()
        for element in misdatos:
            self.dlg.comboBox_src.addItem(element[0])
        """Run method that performs all the real work"""

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False

        #leo la cache
        rutacache = os.path.join(QgsApplication.qgisSettingsDirPath(),
                                 r"python\plugins\zoomSigmena\cache.txt")
        if os.path.isfile(rutacache) == True:
            filecache = open(rutacache, "r")
            filecacheleido = filecache.readlines()
            try:
                import ast
                almacen = ast.literal_eval((filecacheleido[0].replace(
                    '\n', '')).replace(" [[",
                                       "[[").replace("]] ",
                                                     "]]"))  #.split(','))
                cache_utm = int(almacen[0])
                cache_geo = int(almacen[1])
                cache_escala = almacen[2]

                print(cache_escala)
                print(almacen)
                #miscomarcas=(filecacheleido[3].replace('\n','')).strip('][').split(',') #convierto una str en una list
                #mismunicipios=ast.literal_eval((filecacheleido[4].replace('\n','')).replace(" [[","[[").replace("]] ","]]"))#.split(',')) #convierto una str en una list
                filecache.close()

            except:
                print("esta no encuentra el file cache")
            self.dlg.lineEdit_escala.setText(str(cache_escala))
            self.dlg.checkBox_utm.setChecked(cache_utm)
            self.dlg.checkBox_geo.setChecked(cache_geo)
        # show the dialog
        self.dlg.show()

        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:

            # Do something useful here - delete the line containing pass and
            # substitute with your code.

            def deg_to_dms(deg, type='lat'):
                decimals, number = math.modf(deg)
                d = int(number)
                m = int(decimals * 60)
                s = (deg - d - m / 60) * 3600.00
                compass = {'lat': ('N', 'S'), 'lon': ('E', 'W')}
                compass_str = compass[type][0 if d >= 0 else 1]
                return '{}º{}\'{:.2f}"{}'.format(abs(d), abs(m), abs(s),
                                                 compass_str)

            #saco de  aqui variables que estan en las cajitas
            src_seleccionado = self.dlg.comboBox_src.currentIndex()

            # Get the coordinates and scale factor from the dialog
            x = self.dlg.XX.text()  ##displayText()
            y = self.dlg.YY.text()  ##displayText()
            escala = self.dlg.lineEdit_escala.text()

            x = x.replace(',', '.')
            y = y.replace(',', '.')
            escala = int(escala.replace('.', ''))
            src = misdatos[int(src_seleccionado)][1]

            if src == "4326":
                latext = y
                longtext = x

                lag = float(latext.split()[0])
                lam = float(latext.split()[1])
                las = float(latext.split()[2])
                log = float(longtext.split()[0])
                lom = float(longtext.split()[1])
                los = float(longtext.split()[2])

                lon = -1 * (log + (lom / 60) + (los / 3600))
                lat = lag + (lam / 60) + (las / 3600)

                x = float(lon)
                y = float(lat)
                print(x)
                print(y)
                huso = 30
                destinoProj = pyproj.Proj(proj="utm",
                                          zone=huso,
                                          ellps="WGS84",
                                          units="m")
                origenProj = pyproj.Proj(proj='longlat',
                                         ellps='WGS84',
                                         datum='WGS84')
                UTM_X, UTM_Y = pyproj.transform(origenProj, destinoProj, lon,
                                                lat)

            if src == "4258":
                print("por el camino adecuado")
                lat = float(y)
                lonn = float(x)
                lon = -1.0 * lonn

                print(lat)
                print(lon)

                huso = 30
                destinoProj = pyproj.Proj(proj="utm",
                                          zone=huso,
                                          ellps="WGS84",
                                          units="m")
                origenProj = pyproj.Proj(proj='longlat',
                                         ellps='WGS84',
                                         datum='WGS84')
                UTM_X, UTM_Y = pyproj.transform(origenProj, destinoProj, lon,
                                                lat)
                print(UTM_X)
                print(UTM_Y)
                x = lon
                y = lat

            #creo una capa temporal con las coordenadas

            # create layer
            vl2 = QgsVectorLayer("Point?crs=EPSG:" + src, "Zoom", "memory")
            pr2 = vl2.dataProvider()

            vl2.startEditing()
            # add fields

            pr2.addAttributes([
                QgsField("x", QVariant.Double),
                QgsField("y", QVariant.Double),
                QgsField("xx", QVariant.String),
                QgsField("yy", QVariant.String),
                QgsField("xxx", QVariant.Double),
                QgsField("yyy", QVariant.Double)
            ])
            vl2.updateFields()
            # tell the vector layer to fetch changes from the provider

            #$add a feature
            fet = QgsFeature()
            print("punto")
            print(x)
            print(y)
            fet.setGeometry(
                QgsGeometry.fromPointXY(QgsPointXY(float(x), float(y))))
            if src == "25830":
                huso = 30
                origenProj = pyproj.Proj(proj="utm",
                                         zone=huso,
                                         ellps="WGS84",
                                         units="m")
                destinoProj = pyproj.Proj(proj='longlat',
                                          ellps='WGS84',
                                          datum='WGS84')
                xxx, yyy = pyproj.transform(origenProj, destinoProj, x, y)

                xx = (deg_to_dms(xxx, 'lon'))
                yy = (deg_to_dms(yyy))

            if src == "25829":
                huso = 29
                origenProj = pyproj.Proj(proj="utm",
                                         zone=huso,
                                         ellps="WGS84",
                                         units="m")
                destinoProj = pyproj.Proj(proj='longlat',
                                          ellps='WGS84',
                                          datum='WGS84')
                xxx, yyy = pyproj.transform(origenProj, destinoProj, x, y)

                xx = (deg_to_dms(xxx, 'lon'))
                yy = (deg_to_dms(yyy))

            if src == "23030":
                huso = 30
                origenProj = pyproj.Proj(proj="utm",
                                         zone=huso,
                                         ellps="intl",
                                         units="m")
                destinoProj = pyproj.Proj(proj='longlat',
                                          ellps='WGS84',
                                          datum='WGS84')
                xxx, yyy = pyproj.transform(origenProj, destinoProj, x, y)

                xx = (deg_to_dms(xxx, 'lon'))
                yy = (deg_to_dms(yyy))

            if src == "23029":
                huso = 29
                origenProj = pyproj.Proj(proj="utm",
                                         zone=huso,
                                         ellps="intl",
                                         units="m")
                destinoProj = pyproj.Proj(proj='longlat',
                                          ellps='WGS84',
                                          datum='WGS84')
                xxx, yyy = pyproj.transform(origenProj, destinoProj, x, y)

                xx = (deg_to_dms(xxx, 'lon'))
                yy = (deg_to_dms(yyy))

            #para que lo pase a utms en pantalla
            if src == "4326":
                x = int(UTM_X)
                y = int(UTM_Y)
                #xx=longtext
                #yy=latext
                huso = 30
                origenProj = pyproj.Proj(proj="utm",
                                         zone=huso,
                                         ellps="intl",
                                         units="m")
                destinoProj = pyproj.Proj(proj='longlat',
                                          ellps='WGS84',
                                          datum='WGS84')
                xxx, yyy = pyproj.transform(origenProj, destinoProj, x, y)
                xx = (deg_to_dms(xxx, 'lon'))
                yy = (deg_to_dms(yyy))
            #para que lo pase a utms en pantalla
            if src == "4258":
                x = int(UTM_X)
                y = int(UTM_Y)

                xxx = lon
                yyy = lat
                xx = (deg_to_dms(xxx, 'lon'))
                yy = (deg_to_dms(yyy))
            fet.setAttributes(
                [float(x),
                 float(y),
                 str(xx),
                 str(yy),
                 float(xxx),
                 float(yyy)])
            pr2.addFeatures([fet])

            #cambio la simbologia
            symbol = QgsMarkerSymbol.createSimple({
                'name': 'circle',
                'color': 'red',
                'size': '3',
            })
            vl2.renderer().setSymbol(symbol)

            # update layer's extent when new features have been added
            # because change of extent in provider is not propagated to the layer

            layer_settings = QgsPalLayerSettings()
            text_format = QgsTextFormat()

            text_format.setFont(QFont("Arial", 12))
            text_format.setSize(12)
            text_format.setColor(QColor("Orange"))

            layer_settings.setFormat(text_format)
            layer_settings.placement = 1
            layer_settings.xOffset = 0.0
            layer_settings.yOffset = 10.0
            mostrar = True
            if self.dlg.checkBox_utm.isChecked(
            ) and self.dlg.checkBox_geo.isChecked():
                layer_settings.fieldName = '''concat('X: ',"X",' Y: ',"Y",'\n','Lon: ',"xx",' Lat: ',"yy" )'''
                almacen = [1, 1]

            else:
                if self.dlg.checkBox_utm.isChecked():
                    layer_settings.fieldName = '''concat('X: ',"X",' Y: ',"Y" )'''
                    almacen = [1, 0]
                    print("caso1")
                if self.dlg.checkBox_geo.isChecked():
                    layer_settings.fieldName = '''concat('Lon: ',"xx",' Lat: ',"yy" )'''
                    almacen = [0, 1]
                    print("caso2")
                if not self.dlg.checkBox_utm.isChecked(
                ) and not self.dlg.checkBox_geo.isChecked():
                    mostrar = False
                    almacen = [0, 0]
                    print("caso3")
            print("almacen despues de etiquetar", almacen)
            layer_settings.isExpression = True

            print(mostrar)
            layer_settings.enabled = mostrar

            layer_settings = QgsVectorLayerSimpleLabeling(layer_settings)
            vl2.setLabelsEnabled(True)
            vl2.setLabeling(layer_settings)
            vl2.triggerRepaint()

            # update layer's extent when new features have been added
            # because change of extent in provider is not propagated to the layer
            vl2.updateExtents()
            vl2.commitChanges()
            vl2.updateExtents()
            canvas = self.iface.mapCanvas()
            canvas.setExtent(vl2.extent())

            crsSrc = QgsCoordinateReferenceSystem('EPSG:' + str(src))
            crsDest = QgsProject.instance().crs()

            if crsSrc != crsDest:

                xform = QgsCoordinateTransform(crsSrc, crsDest,
                                               QgsProject.instance())
                canvas.setExtent(xform.transform(vl2.extent()))

            self.iface.mapCanvas().zoomScale(escala)
            #self.limpiar_pressed()
            almacen.append(escala)
            #lo escribo en el txt, mavhacando lo que ya tenia
            f = open(rutacache, "w")
            escribir = str(almacen)
            f.write(escribir)
            f.close()
            print(almacen)
            QgsProject.instance().addMapLayer(vl2)
Example #24
0
def add_design_to_map(qris_project, item, node):
    """adds designs to the map"""
    # Establish paths to layers
    design_id = item.data(item_code['feature_id'])
    subset_string = ("design_id = " + str(design_id))
    design_name = item.text()
    geopackage_path = qris_project.project_designs.geopackage_path(
        qris_project.project_path)
    designs_layer = QgsVectorLayer(geopackage_path + "|layername=designs",
                                   "Designs", "ogr")
    structure_types_layer = QgsVectorLayer(
        geopackage_path + "|layername=structure_types", "Structure Types",
        "ogr")
    phases_layer = QgsVectorLayer(geopackage_path + "|layername=phases",
                                  "Implementation Phases", "ogr")
    zoi_layer = QgsVectorLayer(geopackage_path + "|layername=zoi", "ZOI",
                               "ogr")
    zoi_types_layer = QgsVectorLayer(geopackage_path + "|layername=zoi_types",
                                     "ZOI", "ogr")
    complexes_layer = QgsVectorLayer(geopackage_path + "|layername=complexes",
                                     "Complexes", "ogr")
    structure_points_layer = QgsVectorLayer(
        geopackage_path + "|layername=structure_points", "Structures", "ogr")
    structure_lines_layer = QgsVectorLayer(
        geopackage_path + "|layername=structure_lines", "Structures", "ogr")

    # Get the structure geometry type
    # Could also do this with SQL
    design_iterator = designs_layer.getFeatures(
        QgsFeatureRequest().setFilterFid(design_id))
    design_feature = next(design_iterator)
    structure_geometry = design_feature['structure_geometry']

    def add_design_table(display_name, table_name, qml_name, read_only,
                         group_node):
        """A handy way to add design layers and tables to the map"""
        if not any([c.name() == display_name for c in group_node.children()]):
            layer = QgsProject.instance().addMapLayer(
                QgsVectorLayer(geopackage_path + "|layername=" + table_name,
                               display_name, "ogr"), False)
            if qml_name:
                layer_qml = os.path.join(symbology_path, 'symbology', qml_name)
                layer.loadNamedStyle(layer_qml)
            if read_only:
                layer.setReadOnly()
            group_node.addLayer(layer)

    # Summary tables (views)
    if any([c.name() == "Low-Tech Tables" for c in node.children()]):
        # if is there set it to the design node
        table_node = next(n for n in node.children()
                          if n.name() == "Low-Tech Tables")
    else:
        # if not add the node as a group
        table_node = node.addGroup("Low-Tech Tables")
        table_node.setExpanded(False)

    # Summary tables (views)
    if any([c.name() == "Summary Tables" for c in table_node.children()]):
        # if is there set it to the design node
        summary_node = next(n for n in table_node.children()
                            if n.name() == "Summary Tables")
    else:
        # if not add the node as a group
        summary_node = table_node.addGroup("Summary Tables")
        summary_node.setExpanded(False)

    add_design_table('Structure Totals - Points',
                     'qry_total_structures_points', None, True, summary_node)
    add_design_table('Structure Totals - Lines', 'qry_total_structures_lines',
                     None, True, summary_node)
    add_design_table('Structure Summary - Points',
                     'qry_structure_summary_points', None, True, summary_node)
    add_design_table('Structure Summary - Lines',
                     'qry_structure_summary_lines', None, True, summary_node)
    add_design_table('Complexes Summary - Points',
                     'qry_complexes_by_type_points', None, True, summary_node)
    add_design_table('Complexes Summary - Lines',
                     'qry_complexes_by_type_lines', None, True, summary_node)
    add_design_table('ZOI Summary', 'qry_zoi_summary', None, True,
                     summary_node)

    # Lookup Tables
    if any([c.name() == "Lookup Tables" for c in table_node.children()]):
        # if is there set it to the design node
        lookup_node = next(n for n in table_node.children()
                           if n.name() == "Lookup Tables")
    else:
        # if not add the node as a group
        lookup_node = table_node.addGroup("Lookup Tables")
        lookup_node.setExpanded(False)

    add_design_table('Design Status', 'lkp_design_status',
                     'lkp_design_status.qml', True, lookup_node)
    add_design_table('Phase Action', 'lkp_phase_action',
                     'lkp_phase_action.qml', True, lookup_node)
    add_design_table('ZOI Influence', 'lkp_zoi_influence',
                     'lkp_zoi_influence.qml', True, lookup_node)
    add_design_table('ZOI Stage', 'lkp_zoi_stage', 'lkp_zoi_stage.qml', True,
                     lookup_node)
    add_design_table('Structure Mimics', 'lkp_structure_mimics',
                     'lkp_structure_mimics.qml', True, lookup_node)

    # Add Design Tables
    if any([c.name() == "Design Tables" for c in table_node.children()]):
        # if is there set it to the design node
        design_tables_node = next(n for n in table_node.children()
                                  if n.name() == "Design Tables")
    else:
        # if not add the node as a group
        design_tables_node = table_node.addGroup("Design Tables")
        design_tables_node.setExpanded(False)

    # Check if the designs table has been added and if not add it.
    add_design_table('Designs', 'designs', 'designs.qml', False,
                     design_tables_node)
    add_design_table('Structure Types', 'structure_types',
                     'structure_types.qml', False, design_tables_node)
    add_design_table('ZOI Types', 'zoi_types', 'zoi_types.qml', False,
                     design_tables_node)
    add_design_table('Phases', 'phases', 'phases.qml', False,
                     design_tables_node)

    # Check if the design node is already added
    design_group_name = str(design_id) + "-" + item.text()
    if any([c.name() == design_group_name for c in node.children()]):
        # if is there set it to the design node
        design_node = next(n for n in node.children()
                           if n.name() == design_group_name)
    else:
        # if not add the node as a group
        design_node = node.addGroup(design_group_name)

    # Add structures
    structure_layer_name = str(design_id) + "-Structures"
    if structure_geometry == 'Point':
        # Start setting custom symbology
        # TODO Refactor into a functio
        unique_values = []
        for feature in structure_types_layer.getFeatures():
            values = (feature["fid"], feature["name"])
            unique_values.append(values)
        categories = []
        for value in unique_values:
            layer_style = {}
            layer_style["color"] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['size'] = '3'
            layer_style['outline_color'] = 'black'
            symbol_layer = QgsMarkerSymbol.createSimple(layer_style)
            category = QgsRendererCategory(str(value[0]), symbol_layer,
                                           value[1])
            categories.append(category)
        renderer = QgsCategorizedSymbolRenderer('structure_type_id',
                                                categories)

        if not any(
            [c.name() == structure_layer_name
             for c in design_node.children()]):
            # Adding the type suffix as I could see adding qml that symbolizes on other attributes
            structure_points_qml = os.path.join(symbology_path, 'symbology',
                                                'structure_points.qml')
            structure_points_layer.loadNamedStyle(structure_points_qml)
            QgsExpressionContextUtils.setLayerVariable(structure_points_layer,
                                                       'parent_id', design_id)
            structure_points_layer.setSubsetString(subset_string)
            QgsProject.instance().addMapLayer(structure_points_layer, False)
            structure_points_layer.setName(structure_layer_name)
            design_node.addLayer(structure_points_layer)
            layer_node = design_node.findLayer(structure_points_layer.id())
            layer_node.setExpanded(False)
        else:
            structure_points_layer = QgsProject.instance().mapLayersByName(
                structure_layer_name)[0]
        if renderer is not None:
            structure_points_layer.setRenderer(renderer)
            structure_points_layer.triggerRepaint()

    else:
        # Add line structures
        # Start setting custom symbology
        # TODO Refactor into a function
        unique_values = []
        for feature in structure_types_layer.getFeatures():
            values = (feature["fid"], feature["name"])
            unique_values.append(values)

        categories = []
        for value in unique_values:
            layer_style = {}
            layer_style["color"] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['width'] = '1'
            layer_style['capstyle'] = 'round'
            symbol_layer = QgsLineSymbol.createSimple(layer_style)
            category = QgsRendererCategory(str(value[0]), symbol_layer,
                                           value[1])
            categories.append(category)
        renderer = QgsCategorizedSymbolRenderer('structure_type_id',
                                                categories)
        # end custom symbology

        if not any(
            [c.name() == structure_layer_name
             for c in design_node.children()]):
            structures_lines_qml = os.path.join(symbology_path, 'symbology',
                                                'structure_lines.qml')
            structure_lines_layer.loadNamedStyle(structures_lines_qml)
            QgsExpressionContextUtils.setLayerVariable(structure_lines_layer,
                                                       'parent_id', design_id)
            structure_lines_layer.setSubsetString(subset_string)
            QgsProject.instance().addMapLayer(structure_lines_layer, False)
            structure_lines_layer.setName(structure_layer_name)
            design_node.addLayer(structure_lines_layer)
            layer_node = design_node.findLayer(structure_lines_layer.id())
            layer_node.setExpanded(False)
        else:
            structure_lines_layer = QgsProject.instance().mapLayersByName(
                structure_layer_name)[0]
        if renderer is not None:
            structure_lines_layer.setRenderer(renderer)
            structure_lines_layer.triggerRepaint()

    # Add zoi
    zoi_layer_name = str(design_id) + "-ZOI"
    # TODO Refactor into a function
    # TODO Refactor into sql query
    unique_values = []
    for feature in zoi_types_layer.getFeatures():
        values = (feature["fid"], feature["name"])
        unique_values.append(values)
    categories = []
    for value in unique_values:
        layer_style = {}
        alpha = 60
        layer_style["color"] = "{}, {}, {}, {}".format(randrange(0, 256),
                                                       randrange(0, 256),
                                                       randrange(0, 256),
                                                       alpha)
        layer_style["outline_width"] = '0.50'
        layer_style["outline_style"] = 'dash'
        symbol_layer = QgsFillSymbol.createSimple(layer_style)
        category = QgsRendererCategory(str(value[0]), symbol_layer, value[1])
        categories.append(category)
    renderer = QgsCategorizedSymbolRenderer('influence_type_id', categories)
    # End custom symbology

    # check for the zoi layer, and if it is not there symbolize and add it
    if not any([c.name() == zoi_layer_name for c in design_node.children()]):
        zoi_qml = os.path.join(symbology_path, 'symbology', 'zoi.qml')
        zoi_layer.loadNamedStyle(zoi_qml)
        QgsExpressionContextUtils.setLayerVariable(zoi_layer, 'parent_id',
                                                   design_id)
        zoi_layer.setSubsetString(subset_string)
        QgsProject.instance().addMapLayer(zoi_layer, False)
        zoi_layer.setName(zoi_layer_name)
        design_node.addLayer(zoi_layer)
        layer_node = design_node.findLayer(zoi_layer.id())
        layer_node.setExpanded(False)
    else:
        zoi_layer = QgsProject.instance().mapLayersByName(zoi_layer_name)[0]
    if renderer is not None:
        zoi_layer.setRenderer(renderer)
    zoi_layer.triggerRepaint()

    # Add complexes
    complex_layer_name = str(design_id) + "-Complexes"
    if not any(
        [c.name() == complex_layer_name for c in design_node.children()]):
        complex_qml = os.path.join(symbology_path, 'symbology',
                                   'complexes.qml')
        complexes_layer.loadNamedStyle(complex_qml)
        QgsExpressionContextUtils.setLayerVariable(complexes_layer,
                                                   'parent_id', design_id)
        complexes_layer.setSubsetString(subset_string)
        QgsProject.instance().addMapLayer(complexes_layer, False)
        complexes_layer.setName(complex_layer_name)
        design_node.addLayer(complexes_layer)
Example #25
0
    def testRequestRepaintMultiple(self):
        """ test requesting repaint with multiple dependent layers """
        layer1 = QgsVectorLayer("Point?field=fldtxt:string",
                                "layer1", "memory")
        layer2 = QgsVectorLayer("Point?field=fldtxt:string",
                                "layer2", "memory")
        QgsProject.instance().addMapLayers([layer1, layer2])
        self.assertTrue(layer1.isValid())
        self.assertTrue(layer2.isValid())

        # add image to cache - no dependent layers
        cache = QgsMapRendererCache()
        im1 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('nolayer', im1)
        self.assertFalse(cache.cacheImage('nolayer').isNull())
        self.assertTrue(cache.hasCacheImage('nolayer'))

        # trigger repaint on layer
        layer1.triggerRepaint()
        layer1.triggerRepaint()  # do this a couple of times - we don't want errors due to multiple disconnects, etc
        layer2.triggerRepaint()
        layer2.triggerRepaint()
        # cache image should still exist - it's not dependent on layers
        self.assertFalse(cache.cacheImage('nolayer').isNull())
        self.assertTrue(cache.hasCacheImage('nolayer'))

        # image depends on 1 layer
        im_l1 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('im1', im_l1, [layer1])

        # image depends on 2 layers
        im_l1_l2 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('im1_im2', im_l1_l2, [layer1, layer2])

        # image depends on 2nd layer alone
        im_l2 = QImage(200, 200, QImage.Format_RGB32)
        cache.setCacheImage('im2', im_l2, [layer2])

        self.assertFalse(cache.cacheImage('im1').isNull())
        self.assertTrue(cache.hasCacheImage('im1'))
        self.assertFalse(cache.cacheImage('im1_im2').isNull())
        self.assertTrue(cache.hasCacheImage('im1_im2'))
        self.assertFalse(cache.cacheImage('im2').isNull())
        self.assertTrue(cache.hasCacheImage('im2'))

        # trigger repaint layer 1 (check twice - don't want disconnect errors)
        for i in range(2):
            layer1.triggerRepaint()
            # should be cleared
            self.assertTrue(cache.cacheImage('im1').isNull())
            self.assertFalse(cache.hasCacheImage('im1'))
            self.assertTrue(cache.cacheImage('im1_im2').isNull())
            self.assertFalse(cache.hasCacheImage('im1_im2'))
            # should be retained
            self.assertTrue(cache.hasCacheImage('im2'))
            self.assertFalse(cache.cacheImage('im2').isNull())
            self.assertEqual(cache.cacheImage('im2'), im_l2)
            self.assertTrue(cache.hasCacheImage('nolayer'))
            self.assertFalse(cache.cacheImage('nolayer').isNull())
            self.assertEqual(cache.cacheImage('nolayer'), im1)

        # trigger repaint layer 2
        for i in range(2):
            layer2.triggerRepaint()
            # should be cleared
            self.assertFalse(cache.hasCacheImage('im1'))
            self.assertTrue(cache.cacheImage('im1').isNull())
            self.assertFalse(cache.hasCacheImage('im1_im2'))
            self.assertTrue(cache.cacheImage('im1_im2').isNull())
            self.assertFalse(cache.hasCacheImage('im2'))
            self.assertTrue(cache.cacheImage('im2').isNull())
            # should be retained
            self.assertTrue(cache.hasCacheImage('nolayer'))
            self.assertFalse(cache.cacheImage('nolayer').isNull())
            self.assertEqual(cache.cacheImage('nolayer'), im1)
Example #26
0
class FlyingTrackerDialog(QtGui.QDockWidget, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """Constructor."""
        super(FlyingTrackerDialog, self).__init__(parent)

        self.setupUi(self)
        self.iface = iface

        self.course_comboBox.clear()
        self.lcdNumberWpt.display(1)

        self.GpsFixlcdNumber.display(0)
        self.SatelliteslcdNumber.display(0)

        self.pushButtonDisconnect.hide()
        self.pushButtonConnect.clicked.connect(self.Connect)
        self.pushButtonDisconnect.clicked.connect(self.Disconnect)
        self.pushCloseButton.clicked.connect(self.Close)
        self.LoadtoolButton.clicked.connect(self.FillComboBox)
        self.wptplus_toolButton.clicked.connect(self.NextWpt)
        self.wptmin_toolButton.clicked.connect(self.BackWpt)
        QtCore.QObject.connect(self.wptplus_toolButton,
                               QtCore.SIGNAL("valueChanged(int)"),
                               self.AdjNxtWpt)
        QtCore.QObject.connect(self.wptmin_toolButton,
                               QtCore.SIGNAL("valueChanged(int)"),
                               self.AdjNxtWpt)
        self.ZoomIntoolButton.clicked.connect(self.ZoomIn)
        self.ZoomOuttoolButton.clicked.connect(self.ZoomOut)

        shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_S),
                                   self.iface.mainWindow())
        shortcut.setContext(QtCore.Qt.ApplicationShortcut)
        shortcut.activated.connect(self.ZoomIn)
        shortcut2 = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_A),
                                    self.iface.mainWindow())
        shortcut2.setContext(QtCore.Qt.ApplicationShortcut)
        shortcut2.activated.connect(self.ZoomOut)
        shortcut3 = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_X),
                                    self.iface.mainWindow())
        shortcut3.setContext(QtCore.Qt.ApplicationShortcut)
        shortcut3.activated.connect(self.NextWpt)
        shortcut4 = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Z),
                                    self.iface.mainWindow())
        shortcut4.setContext(QtCore.Qt.ApplicationShortcut)
        shortcut4.activated.connect(self.BackWpt)

        self.timer = QtCore.QTimer()
        QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"),
                               self.SendSerial)

        self.NxtWptRubber = False
        self.PositionMarker = False
        self.RubberBand = False
        self.WptVertexSignal = False

        self.trackCounter = 4

    def AdjNxtWpt(self):

        if self.WptVertexSignal == True:
            self.iface.mapCanvas().scene().removeItem(self.WptVertex)
            del self.WptVertex
            self.WptVertexSignal = False

        if self.NxtWptRubber == True:
            self.iface.mapCanvas().scene().removeItem(self.rdue)
            del self.rdue
            self.iface.mapCanvas().scene().removeItem(self.rtre)
            del self.rtre
            self.NxtWptRubber = False

        canvas = self.iface.mapCanvas()
        mapRenderer = canvas.mapRenderer()
        crsSrc = QgsCoordinateReferenceSystem(4326)  # NMEA is in WGS 84
        crsDest = mapRenderer.destinationCrs()
        xform = QgsCoordinateTransform(crsSrc, crsDest)

        WptValue = int(self.lcdNumberWpt.value())

        try:
            Wpt = self.pts[WptValue - 1]

        except IndexError:
            return

        self.WptVertex = QgsVertexMarker(self.iface.mapCanvas())
        self.WptVertex.setIconSize(20)
        self.WptVertex.setIconType(QgsVertexMarker.ICON_X)
        self.WptVertex.setPenWidth(20)
        self.WptVertex.setCenter(xform.transform(Wpt))
        self.WptVertexSignal = True

        if WptValue != 1:

            Wpt2 = self.pts[WptValue - 2]
            self.rdue = QgsRubberBand(self.iface.mapCanvas(),
                                      False)  # False = not a polygon
            pointsdue = [xform.transform(Wpt), xform.transform(Wpt2)]

            self.rdue.setColor(QtGui.QColor(255, 0, 0))
            self.rdue.setWidth(8)
            self.rdue.setLineStyle(QtCore.Qt.PenStyle(QtCore.Qt.DotLine))
            self.rdue.setToGeometry(QgsGeometry.fromPolyline(pointsdue),
                                    None)  #creation of NextWayPoint rubberband

            self.rtre = QgsRubberBand(self.iface.mapCanvas(), False)
            pointstre = self.pts[0:WptValue - 1]

            for i in xrange(len(pointstre)):
                pointstre[i] = xform.transform(pointstre[i])

            self.rtre.setColor(QtGui.QColor(127, 0, 255))
            self.rtre.setWidth(8)
            self.rtre.setLineStyle(QtCore.Qt.PenStyle(QtCore.Qt.DotLine))
            self.rtre.setToGeometry(QgsGeometry.fromPolyline(pointstre), None)

            self.NxtWptRubber = True

    def Connect(self):

        try:

            self.positionMarker = PositionMarker(self.iface.mapCanvas())
            self.PositionMarker = True
            portName = self.comboBox.currentText()
            self.ser = serial.Serial(portName, 38400)
            layername = self.course_comboBox.itemData(
                self.course_comboBox.currentIndex())
            RouteLayer = QgsMapLayerRegistry.instance().mapLayer(layername)
            RouteLayer.selectAll()
            feats = RouteLayer.selectedFeatures()
            RouteLayer.removeSelection()
            feat = feats[0]
            geom = feat.geometry()
            self.pts = geom.asPolyline()
            SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1])

            if SourceIntCRS != 4326:
                SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1])
                SourceCRS = QgsCoordinateReferenceSystem(SourceIntCRS)
                DestCRS = QgsCoordinateReferenceSystem(4326)
                xformRouteLayer = QgsCoordinateTransform(SourceCRS, DestCRS)
                for i in xrange(len(self.pts)):
                    x = self.pts[i][0]
                    y = self.pts[i][
                        1]  #if track layer is not in WGS84 Geographic every coordinate is transformed
                    TmpPoint = QgsPoint(x, y)
                    Tmp2Point = xformRouteLayer.transform(TmpPoint)
                    self.pts[i] = Tmp2Point

            self.TrackLayer = QgsVectorLayer("Point?crs=epsg:4326&index=yes",
                                             "Flight_track", "memory")
            self.TrackLayerProvider = self.TrackLayer.dataProvider()
            self.TrackLayerProvider.addAttributes([
                QgsField("id", QtCore.QVariant.Int),
                QgsField('Time', QtCore.QVariant.String),
                QgsField('Ele', QtCore.QVariant.String),
            ])

            QgsMapLayerRegistry.instance().addMapLayer(self.TrackLayer)
            symbols = self.TrackLayer.rendererV2().symbols()
            symbol = symbols[0]
            symbol.setColor(QtGui.QColor(0, 255, 0))
            self.iface.legendInterface().refreshLayerSymbology(self.TrackLayer)

            if self.lcdNumberWpt.value() == 0:
                self.lcdNumberWpt.display(1)

            elif self.lcdNumberWpt.value() > len(self.pts):
                self.lcdNumberWpt.display(1)

            self.InRouteTolerance = float(
                self.DTrack_spinBox.value()
            )  #if we are distant from route less than this value path become green, otherwise red
            self.CompassTolerance = self.DCompass_spinBox.value(
            )  #if compass to next wpt confront to actual compass diverge less than this value projection of direction become green, otherwise red
            self.WptArrivedTolerance = float(
                self.DWpt_spinBox.value()
            )  #if we pass near a wpt less than this value (in meters) the program will set the next wpt
            self.EleTolerance = float(
                self.DHeightspinBox.value()
            )  #if our elevation diverge from planned elevation more than this value our cursor will be red, otherwise green

            canvas = self.iface.mapCanvas()
            mapRenderer = canvas.mapRenderer()
            crsSrc = QgsCoordinateReferenceSystem(4326)  # NMEA is in WGS 84
            crsDest = mapRenderer.destinationCrs()

            self.backxform = QgsCoordinateTransform(crsDest, crsSrc)

            self.xform = QgsCoordinateTransform(
                crsSrc, crsDest)  #usage: xform.transform(QgsPoint)
            self.AdjNxtWpt()
            self.timer.start(1000)

        except:
            pass

    def SendSerial(self):
        thread = Thread(target=self.ReadSerial)
        thread.start()
        thread.join()

    def Disconnect(self, serialPort):

        self.timer.stop()
        self.lcdNumberSpeed.display(0)
        self.lcdNumberCompass.display(0)
        try:
            self.iface.mapCanvas().scene().removeItem(self.WptVertex)
            self.ser.close()
            self.iface.mapCanvas().scene().removeItem(self.r)
            self.iface.mapCanvas().scene().removeItem(self.runo)
            self.iface.mapCanvas().scene().removeItem(self.positionMarker)
            del self.WptVertex
            del self.r
            del self.runo
        except:
            pass

        self.iface.mapCanvas().setRotation(0)

        self.RubberBand = False

        if self.NxtWptRubber == True:
            self.iface.mapCanvas().scene().removeItem(self.rdue)
            self.iface.mapCanvas().scene().removeItem(self.rtre)
            del self.rdue
            del self.rtre
            self.NxtWptRubber = False

        else:
            pass

        self.WptVertexSignal = False
        self.lcdNumberHeights.display(0)
        self.lcdNumberSpeed.display(0)
        self.lcdNumberCompass.display(0)
        self.lcdCompassWpt.display(0)
        self.GpsFixlcdNumber.display(0)
        self.SatelliteslcdNumber.display(0)

    def FillComboBox(self):
        try:
            self.comboBox.clear()
            portlist = []
            system_name = platform.system()
            if system_name == "Windows":
                # Scan for available ports.
                available = []
                for i in range(256):
                    try:
                        s = serial.Serial(i)
                        available.append(i)
                        s.close()
                    except serial.SerialException:  #Search for active serial port
                        pass
                #print available
                list1 = available
            elif system_name == "Darwin":
                # Mac
                #print glob.glob('/dev/tty*') + glob.glob('/dev/cu*')
                list1 = glob.glob('/dev/tty*') + glob.glob('/dev/cu*')
            else:
                # Assume Linux or something else
                #print glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*')
                list1 = glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*')
            for i in list1:
                try:
                    serial.Serial(i).close()
                    portlist.append(i)
                except IOError:
                    pass

            for x in portlist:
                self.comboBox.addItem(x)

            self.course_comboBox.clear()

            LayerRegistryItem = QgsMapLayerRegistry.instance().mapLayers()
            for id, layer in LayerRegistryItem.iteritems():
                if layer.type() == QgsMapLayer.VectorLayer:
                    self.course_comboBox.addItem(layer.name(), id)

            layername = self.course_comboBox.itemData(
                self.course_comboBox.currentIndex())
            RouteLayer = QgsMapLayerRegistry.instance().mapLayer(layername)
            RouteLayer.selectAll()
            RouteLayer.featureCount()
            feats = RouteLayer.selectedFeatures()
            RouteLayer.removeSelection()
            feat = feats[0]
            geom = feat.geometry()
            self.pts = geom.asPolyline()
            SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1])

            if SourceIntCRS != 4326:
                SourceIntCRS = int(RouteLayer.crs().authid().split(':')[1])
                SourceCRS = QgsCoordinateReferenceSystem(SourceIntCRS)
                DestCRS = QgsCoordinateReferenceSystem(4326)
                xformRouteLayer = QgsCoordinateTransform(SourceCRS, DestCRS)
                for i in xrange(len(self.pts)):
                    x = self.pts[i][0]
                    y = self.pts[i][
                        1]  #if track layer is not in WGS84 Geographic every coordinate is transformed
                    TmpPoint = QgsPoint(x, y)
                    Tmp2Point = xformRouteLayer.transform(TmpPoint)
                    self.pts[i] = Tmp2Point

            self.AdjNxtWpt()
        except:
            pass

    def Close(self):

        self.timer.stop()
        self.lcdNumberHeights.display(0)
        self.lcdNumberSpeed.display(0)
        self.lcdNumberCompass.display(0)
        self.lcdCompassWpt.display(0)

        self.course_comboBox.clear()
        if self.PositionMarker == True:
            self.iface.mapCanvas().scene().removeItem(self.positionMarker)

        if self.NxtWptRubber == True:
            self.iface.mapCanvas().scene().removeItem(self.rdue)
            self.iface.mapCanvas().scene().removeItem(self.rtre)
            del self.rdue
            del self.rtre
            self.NxtWptRubber = False

        else:
            pass

        if self.WptVertexSignal == True:
            self.iface.mapCanvas().scene().removeItem(self.WptVertex)
            del self.WptVertex
            self.WptVertexSignal = False

        self.close()

    def ZoomIn(self):
        self.iface.mapCanvas().zoomIn()

    def ZoomOut(self):
        self.iface.mapCanvas().zoomOut()

    def NextWpt(self):
        try:
            currentValue = self.lcdNumberWpt.value()
            if currentValue == len(self.pts):
                pass
            else:
                self.lcdNumberWpt.display(currentValue + 1)
                self.AdjNxtWpt()
        except:
            pass

    def BackWpt(self):
        try:
            currentValue = self.lcdNumberWpt.value()
            if currentValue >= 2:
                self.lcdNumberWpt.display(currentValue - 1)
                self.AdjNxtWpt()
        except:
            pass

    def ReadSerial(self):

        if self.trackCounter > 5:
            self.trackCounter = 4
        self.trackCounter = self.trackCounter + 1  # when it arrive to 5 a gps point is painted (default is 4)

        GPGGA = 0
        GPVTG = 0

        data = self.ser.read(1)
        n = self.ser.inWaiting()
        if n:
            data = data + self.ser.read(n)

        if re.search("\r\n", data):
            # Since we found a CRLF, split it out
            data2 = data.split("\r\n")

            for i in range(len(data2)):

                if data2[i][0:6] == '$GPGGA':
                    GPGGA = data2[i].split(',')
                    #print GPGGA

                elif data2[i][0:6] == '$GPVTG':
                    GPVTG = data2[i].split(',')
                    #print GPRMC

            if GPGGA == 0:
                #print 'mancato'
                return

            elif GPVTG == 0:
                #print 'mancato'
                return

            else:

                decimalsLat = (float(GPGGA[2][2:])) / 60
                degreeLat = float(GPGGA[2][0:2])
                decimalsLon = (float(GPGGA[4][3:])) / 60
                degreeLon = float(GPGGA[4][0:3])

                Lat = degreeLat + decimalsLat
                Lon = degreeLon + decimalsLon

                if GPGGA[5] == 'W':
                    Lon = -Lon
                if GPGGA[3] == 'S':
                    Lat = -Lat

                Ele = float(GPGGA[9])
                Compass = float(GPVTG[1])
                Speed = (float(GPVTG[7]))  # in Km/h
                GpsFix = int(GPGGA[6])
                GpsSatellites = int(GPGGA[7])

                self.ser.flushInput()
                self.ser.flushOutput()

                self.GpsFixlcdNumber.display(GpsFix)
                self.SatelliteslcdNumber.display(GpsSatellites)

                if self.RubberBand == True:
                    self.iface.mapCanvas().scene().removeItem(self.r)
                    del self.r

                    self.RubberBand = False

                Point = QgsPoint()
                Point.set(Lon, Lat)

                TransfPoint = self.xform.transform(Point)

                canvas = self.iface.mapCanvas()
                if Compass <= 180:
                    #canvas.setRotation(-(Compass-self.rotation))
                    canvas.setRotation(
                        -Compass
                    )  # set canvas rotation according to:  UP of the map = Compass Direction
                else:
                    Compass = 360 - Compass
                    canvas.setRotation(Compass)

                canvas.setCenter(TransfPoint)

                self.positionMarker.newCoords(
                    TransfPoint)  # Put the arrow on screen
                #self.positionMarker.angle = 0.0

                WptValue = int(self.lcdNumberWpt.value())
                WptE = self.pts[WptValue - 1][0]
                WptN = self.pts[WptValue - 1][1]

                GeodesicAircraftToWpt = Geodesic.WGS84.Inverse(
                    Lat, Lon, WptN, WptE)
                distance = GeodesicAircraftToWpt['s12']
                azim = GeodesicAircraftToWpt[
                    'azi1']  #determine azimuth from next wpt
                if azim < 0:
                    azim += 360

                if distance <= self.WptArrivedTolerance:  # tolerance in meter for next wpt
                    self.NextWpt()

                #feetEle = Ele * 3.2808399            #meters to feet

                if self.comboBox_2.currentText() == 'ft.':
                    feetEle = Ele * 3.2808399  #Convert if needed
                    self.lcdNumberHeights.display(feetEle)
                else:
                    self.lcdNumberHeights.display(Ele)

                if self.comboBox_3.currentText() != 'km/h':
                    Speed = Speed * 0.53995694  #Convert if needed
                    self.lcdNumberSpeed.display(float(Speed))
                else:
                    self.lcdNumberSpeed.display(float(Speed))

                self.lcdNumberCompass.display(float(Compass))
                self.lcdCompassWpt.display(azim)

                canvasInPixel = canvas.getCoordinateTransform()
                ExtentHeightInPixel = canvasInPixel.mapHeight()
                ExtentWidthInPixel = canvasInPixel.mapWidth()

                LocateCompassProjectionEndInMapUnit = canvasInPixel.toMapPoint(
                    ExtentWidthInPixel / 2.0,
                    ExtentHeightInPixel - (ExtentHeightInPixel * 0.95))

                self.r = QgsRubberBand(self.iface.mapCanvas(),
                                       False)  # False = not a polygon

                #points = [TransfPoint, QgsPoint(x,y)]							#creazione della proiezione della prua su mappa
                points = [TransfPoint, LocateCompassProjectionEndInMapUnit]
                self.r.setWidth(8)
                self.r.setToGeometry(QgsGeometry.fromPolyline(points), None)

                if abs(Compass -
                       azim) <= self.CompassTolerance:  #Compass tolerance
                    self.r.setColor(QtGui.QColor(0, 255, 0))
                else:
                    self.r.setColor(QtGui.QColor(255, 0, 0))

                self.RubberBand = True

                try:
                    self.iface.mapCanvas().scene().removeItem(
                        self.runo)  # remove track for first waypoint
                except:
                    pass

                if WptValue != 1:

                    #DistanceFromLineTolerance = 100   #meter      set distance from route

                    BackwardLat = self.rdue.asGeometry().asPolyline()[1][
                        1]  #start to design a QgsRectangle buffer around current route to confront to Point
                    BackwardLon = self.rdue.asGeometry().asPolyline()[1][0]
                    BackwardPoint = QgsPoint(BackwardLon, BackwardLat)

                    BackwardPointTransformed = self.backxform.transform(
                        BackwardPoint)

                    GeodesicWptWpt = Geodesic.WGS84.Inverse(
                        BackwardPointTransformed.y(),
                        BackwardPointTransformed.x(), WptN, WptE)
                    #GeodesicWptWpt = Geodesic.WGS84.Inverse(BackwardLat, BackwardLon, WptN, WptE)
                    WptWptCompass = GeodesicWptWpt['azi1']

                    if WptWptCompass < 0:
                        WptWptCompass += 360
                    #print WptWptCompass

                    WptWptCompassRight = WptWptCompass + 90
                    if WptWptCompassRight > 360:
                        WptWptCompassRight = WptWptCompassRight - 360
                    #print WptWptCompassRight

                    WptWptCompassLeft = WptWptCompass - 90
                    if WptWptCompassLeft < 0:
                        WptWptCompassLeft += 360
                    #print WptWptCompassLeft

                    origin = geopy.Point(WptN, WptE)
                    URBufferVertex = vincenty(
                        meters=self.InRouteTolerance).destination(
                            origin, WptWptCompassRight)
                    URBufferVertexPoint = QgsPoint(URBufferVertex.longitude,
                                                   URBufferVertex.latitude)

                    ULBufferVertex = vincenty(
                        meters=self.InRouteTolerance).destination(
                            origin, WptWptCompassLeft)
                    ULBufferVertexPoint = QgsPoint(ULBufferVertex.longitude,
                                                   ULBufferVertex.latitude)
                    del origin

                    origin = geopy.Point(BackwardPointTransformed.y(),
                                         BackwardPointTransformed.x())
                    DRBufferVertex = vincenty(
                        meters=self.InRouteTolerance).destination(
                            origin, WptWptCompassRight)
                    DRBufferVertexPoint = QgsPoint(DRBufferVertex.longitude,
                                                   DRBufferVertex.latitude)

                    DLBufferVertex = vincenty(
                        meters=self.InRouteTolerance).destination(
                            origin, WptWptCompassLeft)
                    DLBufferVertexPoint = QgsPoint(DLBufferVertex.longitude,
                                                   DLBufferVertex.latitude)
                    del origin

                    gPolygon = QgsGeometry.fromPolygon([[
                        URBufferVertexPoint, ULBufferVertexPoint,
                        DLBufferVertexPoint, DRBufferVertexPoint
                    ]])

                    if not gPolygon.contains(Point):
                        self.rdue.setColor(QtGui.QColor(255, 0, 0))
                        #print 'noncontiene'
                    else:
                        self.rdue.setColor(QtGui.QColor(0, 255, 0))
                        #print 'contiene'
                else:
                    self.runo = QgsRubberBand(self.iface.mapCanvas(), False)
                    points = [TransfPoint,
                              self.xform.transform(self.pts[0])
                              ]  # draw track for first waypoint
                    self.runo.setColor(QtGui.QColor(255, 0, 0))
                    self.runo.setWidth(6)
                    self.runo.setLineStyle(
                        QtCore.Qt.PenStyle(QtCore.Qt.DotLine))
                    self.runo.setToGeometry(QgsGeometry.fromPolyline(points),
                                            None)

                #if abs(float((self.fixedHeightspinBox.value()/ 3.2808399)) - (feetEle/ 3.2808399)) <= self.EleTolerance:         #ele tolerance expressed in meters
                if abs(self.fixedHeightspinBox.value() -
                       Ele) <= self.EleTolerance:
                    self.positionMarker.setHasPosition(
                        True)  #True or False to change color
                else:
                    self.positionMarker.setHasPosition(False)

                if self.trackCounter == 5:
                    #pass
                    fc = int(self.TrackLayerProvider.featureCount())
                    time = str(GPGGA[1])[0:2] + ':' + str(
                        GPGGA[1])[2:4] + ':' + str(
                            GPGGA[1])[4:6]  # timestamp for GPX layer

                    feature = QgsFeature()
                    feature.setGeometry(QgsGeometry.fromPoint(Point))
                    feature.setAttributes([fc, time, Ele])
                    self.TrackLayer.startEditing()
                    self.TrackLayer.addFeature(feature, True)
                    self.TrackLayer.commitChanges()
                    self.TrackLayer.setCacheImage(None)
                    self.TrackLayer.triggerRepaint()
                    self.trackCounter = 0

                return
Example #27
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      fo_fila=False,
                      divide=0,
                      decimal=2):
    """Adding Points along the line
    """

    crs = layer.crs().authid()
    # TODO check for virtual or shapelayer and set virt_layer according to it
    shape = False
    if shape:
        # define fields for feature attributes. A list of QgsField objects is needed
        fields = [QgsField("first", QVariant.Int),
                  QgsField("second", QVariant.String)]
        # create an instance of vector file writer, which will create the vector file.
        # Arguments:
        # 1. path to new file (will fail if exists already)
        # 2. encoding of the attributes
        # 3. field map
        # 4. geometry type - from WKBTYPE enum
        # 5. layer's spatial reference (instance of
        #    QgsCoordinateReferenceSystem) - optional
        # 6. driver name for the output file
        writer = QgsVectorFileWriter("my_shapes.shp",
                                     "CP1250",
                                     fields,
                                     QGis.WKBPoint,
                                     crs,
                                     "ESRI Shapefile")
        if writer.hasError() != QgsVectorFileWriter.NoError:
            print "Error when creating shapefile: ", writer.hasError()
        # add a feature
        fet = QgsFeature()
        fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10)))
        fet.setAttributes([1, "text"])
        writer.addFeature(fet)
        # delete the writer to flush features to disk (optional)
        del writer

        layer_type = "Shapefile"  # TODO Add Shapefile functionality here
    else:
        layer_type = "memory"

    virt_layer = QgsVectorLayer("Point?crs=%s" % crs,
                                layerout,
                                layer_type)
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()   # actually writes attributes
    units = layer.crs().mapUnits()
    unit_dic = {
        QGis.Degrees: 'Degrees',
        QGis.Meters: 'Meters',
        QGis.Feet: 'Feet',
        QGis.UnknownUnit: 'Unknown'}
    unit = unit_dic.get(units, 'Unknown')
    provider.addAttributes([QgsField("fid", QVariant.Int),
                            QgsField("cng_("+unit+")", QVariant.Double)])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint,
                                    endpoint,
                                    distance,
                                    geom,
                                    fid,
                                    force,
                                    fo_fila,
                                    divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    QgsMapLayerRegistry.instance().addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    # from here Add labeling
    # generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName", "cng_("+unit+")")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")
        virt_layer.setCustomProperty("labeling/formatNumbers", "true")
        virt_layer.setCustomProperty("labeling/decimals", decimal)

        # virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbolV2.createSimple({"name": "capital"})
    # virt_layer.setRendererV2(QgsSingleSymbolRendererV2(symbol))
    virt_layer.triggerRepaint()
    return
Example #28
0
    def layer_to_QgsVectorLayer(
            source_layer,  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
            input_file,
            context: Context,
            fallback_crs=QgsCoordinateReferenceSystem(),
            defer_layer_uri_set: bool = False):
        """
        Converts a vector layer
        """
        if source_layer.__class__.__name__ == 'CadFeatureLayer':
            layer = source_layer.layer
        else:
            layer = source_layer

        crs = CrsConverter.convert_crs(
            layer.layer_extent.crs,
            context) if layer.layer_extent else QgsCoordinateReferenceSystem()
        if not crs.isValid():
            crs = fallback_crs

        subset_string = ''
        if layer.selection_set:
            subset_string = 'fid in ({})'.format(','.join(
                [str(s) for s in layer.selection_set]))
        elif layer.definition_query:
            subset_string = ExpressionConverter.convert_esri_sql(
                layer.definition_query)

        base, _ = os.path.split(input_file)

        uri, wkb_type, provider, encoding, file_name = VectorLayerConverter.get_uri(
            source_layer=source_layer,
            obj=layer,
            base=base,
            crs=crs,
            subset=subset_string,
            context=context)

        if wkb_type is None or wkb_type == QgsWkbTypes.Unknown:
            wkb_type = VectorLayerConverter.layer_to_wkb_type(layer)
        context.layer_type_hint = wkb_type

        if Qgis.QGIS_VERSION_INT >= 31600:
            # try to get the layer name so that we can remove it from field references.
            # e.g. if layer name is polys then qgis won't support to arcgis style "polys.field" format
            parts = QgsProviderRegistry.instance().decodeUri(provider, uri)
            context.main_layer_name = parts.get('layerName')
            if not context.main_layer_name and provider == 'ogr':
                context.main_layer_name = Path(parts['path']).stem

            if context.main_layer_name:
                subset_string = subset_string.replace(
                    context.main_layer_name + '.', '')
        else:
            context.main_layer_name = None

        if provider == 'ogr' and (not file_name
                                  or not os.path.exists(file_name)
                                  ) and context.invalid_layer_resolver:
            res = context.invalid_layer_resolver(layer.name, uri, wkb_type)
            uri = res.uri
            provider = res.providerKey

        if Qgis.QGIS_VERSION_INT >= 31000:
            opts = QgsVectorLayer.LayerOptions()
            if wkb_type is not None:
                opts.fallbackWkbType = wkb_type

            if provider == 'ogr' and subset_string:
                uri += '|subset={}'.format(subset_string)

            original_uri = uri
            if defer_layer_uri_set:
                uri = 'xxxxxxxxx' + uri

            vl = QgsVectorLayer(uri, layer.name, provider, opts)
            if defer_layer_uri_set:
                vl.setCustomProperty('original_uri', original_uri)
        else:
            vl = QgsMemoryProviderUtils.createMemoryLayer(
                layer.name, QgsFields(), wkb_type, crs)

        # context.style_folder, _ = os.path.split(output_file)
        if layer.renderer:
            renderer = VectorRendererConverter.convert_renderer(
                layer.renderer, context)
            try:
                if not renderer.usingSymbolLevels():
                    renderer.setUsingSymbolLevels(
                        layer.use_advanced_symbol_levels)
            except AttributeError:
                pass

            if layer.use_page_definition_query:
                filter_expression = '"{}" {} @atlas_pagename'.format(
                    layer.page_name_field, layer.page_name_match_operator)
                root_rule = QgsRuleBasedRenderer.Rule(None)

                # special case -- convert a simple renderer
                if isinstance(renderer, QgsSingleSymbolRenderer):
                    filter_rule = QgsRuleBasedRenderer.Rule(
                        renderer.symbol().clone())
                    filter_rule.setFilterExpression(filter_expression)
                    filter_rule.setLabel(layer.name)
                    filter_rule.setDescription(layer.name)
                    root_rule.appendChild(filter_rule)
                else:
                    source_rule_renderer = QgsRuleBasedRenderer.convertFromRenderer(
                        renderer)
                    filter_rule = QgsRuleBasedRenderer.Rule(None)
                    filter_rule.setFilterExpression(filter_expression)
                    filter_rule.setLabel('Current Atlas Page')
                    filter_rule.setDescription('Current Atlas Page')
                    root_rule.appendChild(filter_rule)
                    for child in source_rule_renderer.rootRule().children():
                        filter_rule.appendChild(child.clone())

                renderer = QgsRuleBasedRenderer(root_rule)

            if renderer:
                vl.setRenderer(renderer)
                vl.triggerRepaint()
        else:
            vl.setRenderer(QgsNullSymbolRenderer())
            vl.triggerRepaint()

        metadata = vl.metadata()
        metadata.setAbstract(layer.description)
        vl.setMetadata(metadata)  #

        # layer.zoom_max = "don't show when zoomed out beyond"
        zoom_max = layer.zoom_max
        # layer.zoom_min = "don't show when zoomed in beyond"
        zoom_min = layer.zoom_min

        enabled_scale_range = bool(zoom_max or zoom_min)
        if zoom_max and zoom_min and zoom_min > zoom_max:
            # inconsistent scale range -- zoom_max should be bigger number than zoom_min
            zoom_min, zoom_max = zoom_max, zoom_min

        # qgis minimum scale = don't show when zoomed out beyond, i.e. ArcGIS zoom_max
        vl.setMinimumScale(
            zoom_max if enabled_scale_range else layer.stored_zoom_max)
        # qgis maximum scale = don't show when zoomed in beyond, i.e. ArcGIS zoom_min
        vl.setMaximumScale(
            zoom_min if enabled_scale_range else layer.stored_zoom_min)
        vl.setScaleBasedVisibility(enabled_scale_range)

        vl.setOpacity(1.0 - (layer.transparency or 0) / 100)

        if layer.display_expression_properties and layer.display_expression_properties.expression and layer.display_expression_properties.expression_parser is not None:
            vl.setDisplayExpression(
                ExpressionConverter.convert(
                    layer.display_expression_properties.expression,
                    layer.display_expression_properties.expression_parser,
                    layer.display_expression_properties.advanced, context))

        if Qgis.QGIS_VERSION_INT < 31000:
            vl.setDataSource(uri, layer.name, provider)

        if encoding:
            vl.dataProvider().setEncoding(encoding)

        if subset_string:
            vl.setSubsetString(subset_string)

        vl.setCrs(crs)

        for e in layer.extensions:
            if e.__class__.__name__ == 'ServerLayerExtension':
                if 'CopyrightText' in e.properties.properties:
                    layer_credits = e.properties.properties['CopyrightText']
                    metadata = vl.metadata()
                    rights = metadata.rights()
                    rights.append(layer_credits)
                    metadata.setRights(rights)
                    vl.setMetadata(metadata)

        LabelConverter.convert_annotation_collection(
            layer.annotation_collection, dest_layer=vl, context=context)
        vl.setLabelsEnabled(layer.labels_enabled)

        DiagramConverter.convert_diagrams(layer.renderer,
                                          dest_layer=vl,
                                          context=context)

        # setup joins
        join_layer = VectorLayerConverter.add_joined_layer(
            source_layer=layer,
            input_file=input_file,
            base_layer=vl,
            context=context)

        context.dataset_name = ''

        vl.setLegend(QgsMapLayerLegend.defaultVectorLegend(vl))

        if layer.hyperlinks:
            VectorLayerConverter.convert_hyperlinks(layer.hyperlinks, vl)

        vl.setDisplayExpression(
            QgsExpression.quotedColumnRef(layer.display_field))

        res = [vl]
        if join_layer:
            res.append(join_layer)

        context.main_layer_name = None
        return res
def epa2gis(inpname):
    plugin_path = os.path.dirname(__file__)
    file_extension = os.path.dirname(inpname)
    inpname = os.path.basename(inpname)
    inp = file_extension + '/' + inpname
    if len(file_extension) == 0:
        inp = inpname
    newpath = file_extension + '/_shapefiles_'
    if not os.path.exists(newpath):
        os.makedirs(newpath)

    iface = qgis.utils.iface
    d.LoadFile(inp)
    d.BinUpdateClass()
    nlinkCount = d.getBinLinkCount()

    res = newpath + '\\'
    saveFile = res + inpname[:len(inpname) - 4]

    # Get all Sections
    mixing = d.getMixingSection()
    reactions = d.getReactionsSection()
    sources = d.getSourcesSection()
    rules = d.getRulesSection()
    quality = d.getQualitySection()
    curves = d.getCurvesSection()
    patterns = d.getPatternsSection()
    controls = d.getControlsSection()
    emitters = d.getEmittersSection()
    status = d.getStatusSection()
    demands = d.getDemandsSection()
    energy = d.getEnergySection()
    optReactions = d.getReactionsOptionsSection()
    times = d.getTimesSection()
    report = d.getReportSection()
    options = d.getOptionsSection()

    # Get all Section lengths
    allSections = [len(energy), len(optReactions), len(demands), len(status), len(emitters), len(controls),
                   len(patterns),
                   len(curves[0]), len(quality), len(rules), len(sources), len(reactions), len(mixing), len(times),
                   len(report),
                   len(options), d.getBinNodeCount(), d.getBinLinkCount()]
    ss = max(allSections)
    root = QgsProject.instance().layerTreeRoot()
    idx = root.insertGroup(0, inpname[:len(inpname) - 4])

    xy = d.getBinNodeCoordinates()
    x = xy[0]
    y = xy[1]
    vertx = xy[2]
    verty = xy[3]
    vertxyFinal = []
    for i in range(len(vertx)):
        vertxy = []
        for u in range(len(vertx[i])):
            vertxy.append([float(vertx[i][u]), float(verty[i][u])])
        if vertxy:
            vertxyFinal.append(vertxy)

    otherDemads = d.getBinNodeBaseDemandsDemSection()
    ndID = d.getBinNodeNameID()
    ndBaseD = d.getBinNodeBaseDemands()
    ndPatID = d.getBinNodeDemandPatternID()
    otherDemadsIndex = []
    otherDemadsPatterns = []
    for i, p in enumerate(otherDemads[1]):
        otherDemadsIndex.append(ndID.index(p))
        otherDemadsPatterns.append(otherDemads[2][i])

    counter = collections.Counter(otherDemadsIndex)
    maxCategories = 1
    if counter:
        maxCategories = max(counter.values())

    if not ndBaseD:
        ndBaseD = otherDemads[0]

    # Get data of Junctions
    if d.getBinNodeJunctionCount() > 0:
        ndBaseTmp = np.empty((len(ndBaseD), maxCategories,))
        ndPatTmp = []
        for t in range(0, maxCategories):
            for u in range(0, len(ndBaseD)):
                ndBaseTmp[u][t] = 0
                ndPatTmp.append(['None'] * 2)

        for uu in range(0, len(ndBaseD)):
            if d.getBinNodeBaseDemands():
                ndBaseTmp[uu][0] = ndBaseD[uu]
                ndPatTmp[uu][0] = ndPatID[uu]
        t = 0
        for i, p in enumerate(otherDemadsIndex):
            if d.getBinNodeBaseDemands():
                ndBaseTmp[p][t] = ndBaseD[otherDemadsIndex[i]]
                ndPatTmp[p][t] = ndPatID[otherDemadsIndex[i]]
            else:
                ndBaseTmp[p][t] = otherDemads[0][i]
                ndPatTmp[p][t] = otherDemads[2][i]
            t = t + 1
            if t > max(counter.values()) - 1:
                t = max(counter.values()) - 1
            if i > 0:
                if otherDemadsIndex[i - 1] == p:
                    ndBaseTmp[p][t] = otherDemads[0][i]
                    ndPatTmp[p][t] = otherDemads[2][i]
                    t = t - 1
        # Write Junction Shapefile
        fields = ["ID", "Elevation"]  # , "pattern", "demand"]
        fieldsCode = [0, 1]
        for u in range(0, maxCategories):
            fields.append('Demand' + str(u + 1))
            fields.append('Pattern' + str(u + 1))
            fieldsCode.append(1)
            fieldsCode.append(0)
        posJunction = QgsVectorLayer("point?crs=EPSG:4326", "Junctions", "memory")
        prJunction = posJunction.dataProvider()
        ndBaseTmp = ndBaseTmp.tolist()

        createColumnsAttrb(prJunction, fields, fieldsCode)
        posJunction.startEditing()
        ndEle = d.getBinNodeJunctionElevations()

    # Get data of Pipes
    # Write shapefile pipe
    if nlinkCount > 0:
        posPipe = QgsVectorLayer("LineString?crs=EPSG:4326", "Pipes", "memory")
        prPipe = posPipe.dataProvider()
        fields = ["ID", "NodeFrom", "NodeTo", "Status", "Length", "Diameter", "Roughness", "MinorLoss"]
        fieldsCode = [0, 0, 0, 0, 1, 1, 1, 1]
        createColumnsAttrb(prPipe, fields, fieldsCode)
        posPipe.startEditing()

        pIndex = d.getBinLinkPumpIndex()
        vIndex = d.getBinLinkValveIndex()
        ndlConn = d.getBinNodesConnectingLinksID()
        x1 = []
        x2 = []
        y1 = []
        y2 = []
        stat = d.getBinLinkInitialStatus()

        kk = 0
        ch = 0
        linkID = d.getBinLinkNameID()
        linkLengths = d.getBinLinkLength()
        linkDiameters = d.getBinLinkDiameter()
        linkRough = d.getBinLinkRoughnessCoeff()
        linkMinorloss = d.getBinLinkMinorLossCoeff()

    # Write Tank Shapefile and get tank data
    posTank = QgsVectorLayer("point?crs=EPSG:4326", "Tanks", "memory")
    prTank = posTank.dataProvider()

    fields = ["ID", "Elevation", "InitLevel", "MinLevel", "MaxLevel", "Diameter", "MinVolume", "VolumeCurve"]
    fieldsCode = [0, 1, 1, 1, 1, 1, 1, 0]
    createColumnsAttrb(prTank, fields, fieldsCode)
    posTank.startEditing()

    if d.getBinNodeTankCount() > 0:
        ndTankelevation = d.getBinNodeTankElevations()
        initiallev = d.getBinNodeTankInitialLevel()
        minimumlev = d.getBinNodeTankMinimumWaterLevel()
        maximumlev = d.getBinNodeTankMaximumWaterLevel()
        diameter = d.getBinNodeTankDiameter()
        minimumvol = d.getBinNodeTankMinimumWaterVolume()
        volumecurv = d.getBinNodeTankVolumeCurveID()
        ndTankID = d.getBinNodeTankNameID()

    # Write Reservoir Shapefile
    posReservoirs = QgsVectorLayer("point?crs=EPSG:4326", "Reservoirs", "memory")
    prReservoirs = posReservoirs.dataProvider()
    fields = ["ID", "Head"]
    fieldsCode = [0, 1]
    createColumnsAttrb(prReservoirs, fields, fieldsCode)
    head = d.getBinNodeReservoirElevations()
    posReservoirs.startEditing()

    if times:
        posTimes = QgsVectorLayer("point?crs=EPSG:4326", "Times", "memory")
        prTimes = posTimes.dataProvider()
    if energy:
        posE = QgsVectorLayer("point?crs=EPSG:4326", "Energy", "memory")
        prE = posE.dataProvider()
    if report:
        posRep = QgsVectorLayer("point?crs=EPSG:4326", "Report", "memory")
        prRep = posRep.dataProvider()
    if options:
        posOpt = QgsVectorLayer("point?crs=EPSG:4326", "Options", "memory")
        prOpt = posOpt.dataProvider()
    if optReactions:
        posO = QgsVectorLayer("point?crs=EPSG:4326", "Reactions", "memory")
        prO = posO.dataProvider()

    ppE = []
    ppO = []
    ppTimes = []
    ppRep = []
    ppOpt = []
    ppMix = []
    ppReactions = []
    ppSourc = []
    ppRul = []
    ppPat = []
    ppQual = []
    ppDem = []
    ppStat = []
    ppEmit = []
    ppCont = []
    ppCurv = []

    for i in range(ss):
        if i < d.getBinNodeJunctionCount():
            featJ = QgsFeature()
            point = QgsPointXY(float(x[i]), float(y[i]))
            featJ.initAttributes(2 + len(ndBaseTmp[0]) * 2)
            featJ.setGeometry(QgsGeometry.fromPointXY(point))
            featJ.setAttribute(0, ndID[i])
            featJ.setAttribute(1, ndEle[i])
            w = 2
            for j in range(0, len(ndBaseTmp[0])):
                featJ.setAttribute(w, ndBaseTmp[i][j])
                featJ.setAttribute(w + 1, ndPatTmp[i][j])
                w = w + 2
            prJunction.addFeatures([featJ])

        if i < nlinkCount:
            if len(stat) == i:
                ch = 1
            if ch == 1:
                stat.append('OPEN')

            x1.append(x[ndID.index(d.getBinLinkFromNode()[i])])
            y1.append(y[ndID.index(d.getBinLinkFromNode()[i])])
            x2.append(x[ndID.index(d.getBinLinkToNode()[i])])
            y2.append(y[ndID.index(d.getBinLinkToNode()[i])])

            if i in pIndex:
                pass
            elif i in vIndex:
                pass
            else:
                point1 = QgsPointXY(float(x1[i]), float(y1[i]))
                point2 = QgsPointXY(float(x2[i]), float(y2[i]))
                featPipe = QgsFeature()
                if vertx[i]:
                    parts = []
                    parts.append(point1)
                    for mm in range(len(vertxyFinal[kk])):
                        a = vertxyFinal[kk][mm]
                        parts.append(QgsPointXY(a[0], a[1]))
                    parts.append(point2)
                    featPipe.setGeometry((QgsGeometry.fromPolylineXY(parts)))
                    kk = kk + 1
                else:
                    featPipe.setGeometry(QgsGeometry.fromPolylineXY([point1, point2]))

                featPipe.setAttributes(
                    [linkID[i], ndlConn[0][i], ndlConn[1][i], stat[i], linkLengths[i], linkDiameters[i], linkRough[i],
                     linkMinorloss[i]])
                prPipe.addFeatures([featPipe])

        if i < d.getBinNodeTankCount():
            p = d.getBinNodeTankIndex()[i] - 1
            featTank = QgsFeature()
            point = QgsPointXY(float(x[p]), float(y[p]))
            featTank.setGeometry(QgsGeometry.fromPointXY(point))
            featTank.setAttributes(
                [ndTankID[i], ndTankelevation[i], initiallev[i], minimumlev[i], maximumlev[i], diameter[i],
                 minimumvol[i], volumecurv[i]])
            prTank.addFeatures([featTank])

        if i < d.getBinNodeReservoirCount():
            p = d.getBinNodeReservoirIndex()[i] - 1
            feature = QgsFeature()
            point = QgsPointXY(float(x[p]), float(y[p]))
            feature.setGeometry(QgsGeometry.fromPointXY(point))
            feature.setAttributes([ndID[p], head[i]])
            prReservoirs.addFeatures([feature])

        if i < allSections[12]:
            if len(mixing[i]) == 3:
                ppMix.append([mixing[i][0], mixing[i][1], mixing[i][2]])
            else:
                ppMix.append([mixing[i][0], mixing[i][1]])
        if i < allSections[11]:
            ppReactions.append([reactions[i][0], reactions[i][1], reactions[i][2]])
        if i < allSections[10]:
            if len(sources[i]) == 4:
                ppSourc.append([sources[i][0], sources[i][1], sources[i][2], sources[i][3]])
            elif len(sources[i]) == 3:
                ppSourc.append([sources[i][0], sources[i][1], sources[i][2]])
            else:
                ppSourc.append([sources[i][0], sources[i][1]])

        if i < allSections[9]:
            if len(rules[i]) > 2:
                ppRul.append([rules[i][0][1][1], rules[i][1][0] + rules[i][2][0] + rules[i][3][0]])
        if i < allSections[8]:
            ppQual.append([quality[i][0], quality[i][1]])
        if i < allSections[7]:
            ppCurv.append([str(curves[0][i][0]), str(curves[0][i][1]), str(curves[0][i][2]), str(curves[1][i])])
        if i < allSections[6]:
            ppPat.append([patterns[i][0], str(patterns[i][1])])
        if i < allSections[5]:
            ppCont.append([controls[i]])
        if i < allSections[4]:
            ppEmit.append([emitters[i][0], emitters[i][1]])
        if i < allSections[3]:
            ppStat.append([status[i][0], status[i][1]])
        if i < allSections[2]:
            if len(demands[i]) > 2:
                ppDem.append([demands[i][0], demands[i][1], demands[i][2]])
        if i < allSections[0]:
            mm = energy[i][0]
            if mm.upper() == "GLOBAL":
                prE.addAttributes([QgsField("Global" + energy[i][1], QVariant.String)])
                if len(energy[i]) > 2:
                    ppE.append(energy[i][2])
                else:
                    ppE.append('')
            if mm.upper() == "PUMP":
                prE.addAttributes([QgsField("Pump", QVariant.String)])
                if len(energy[i]) > 2:
                    ppE.append(energy[i][1] + ' ' + energy[i][2])
                else:
                    ppE.append(energy[i][1])
            elif mm.upper() == "DEMAND":
                if energy[i][1].upper() == "CHARGE":
                    prE.addAttributes([QgsField("DemCharge", QVariant.String)])
                    if len(energy[i]) > 2:
                        ppE.append(energy[i][2])
        if i < allSections[1]:
            mm = optReactions[i][0]
            if mm.upper() == "ORDER":
                prO.addAttributes([QgsField("Order" + optReactions[i][1], QVariant.String)])
                if len(optReactions[i]) > 2:
                    ppO.append(optReactions[i][2])
                else:
                    ppO.append('')
            elif mm.upper() == "GLOBAL":
                prO.addAttributes([QgsField("Global" + optReactions[i][1], QVariant.String)])
                if len(optReactions[i]) > 2:
                    ppO.append(optReactions[i][2])
                else:
                    ppO.append('')
            elif mm.upper() == "BULK":
                prO.addAttributes([QgsField("Bulk", QVariant.String)])
                if len(optReactions[i]) > 2:
                    ppO.append(optReactions[i][1] + ' ' + optReactions[i][2])
                else:
                    ppO.append(optReactions[i][1])
            elif mm.upper() == "WALL":
                prO.addAttributes([QgsField("Wall", QVariant.String)])
                if len(optReactions[i]) > 2:
                    ppO.append(optReactions[i][1] + ' ' + optReactions[i][2])
                else:
                    ppO.append(optReactions[i][1])
            elif mm.upper() == "TANK":
                prO.addAttributes([QgsField("Tank", QVariant.String)])
                if len(optReactions[i]) > 2:
                    ppO.append(optReactions[i][1] + ' ' + optReactions[i][2])
                else:
                    ppO.append(optReactions[i][1])
            elif mm.upper() == "LIMITING":
                if optReactions[i][1].upper() == "POTENTIAL":
                    prO.addAttributes([QgsField("LimPotent", QVariant.String)])
                    if len(optReactions[i]) > 2:
                        ppO.append(optReactions[i][2])
            elif mm.upper() == "ROUGHNESS":
                if optReactions[i][1].upper() == "CORRELATION":
                    prO.addAttributes([QgsField("RoughCorr", QVariant.String)])
                    if len(optReactions[i]) > 2:
                        ppO.append(optReactions[i][2])
        if i < allSections[13]:
            mm = times[i][0]
            if mm.upper() == "DURATION":
                prTimes.addAttributes([QgsField("Duration", QVariant.String)])
                ppTimes.append(times[i][1])
            if mm.upper() == "HYDRAULIC":
                prTimes.addAttributes([QgsField("HydStep", QVariant.String)])
                ppTimes.append(times[i][2])
            elif mm.upper() == "QUALITY":
                prTimes.addAttributes([QgsField("QualStep", QVariant.String)])
                ppTimes.append(times[i][2])
            elif mm.upper() == "RULE":
                prTimes.addAttributes([QgsField("RuleStep", QVariant.String)])
                ppTimes.append(times[i][2])
            elif mm.upper() == "PATTERN":
                if times[i][1].upper() == "TIMESTEP":
                    prTimes.addAttributes([QgsField("PatStep", QVariant.String)])
                    ppTimes.append(times[i][2])
                if times[i][1].upper() == "START":
                    prTimes.addAttributes([QgsField("PatStart", QVariant.String)])
                    ppTimes.append(times[i][2])
            elif mm.upper() == "REPORT":
                if times[i][1].upper() == "TIMESTEP":
                    prTimes.addAttributes([QgsField("RepStep", QVariant.String)])
                    ppTimes.append(times[i][2])
                if times[i][1].upper() == "START":
                    prTimes.addAttributes([QgsField("RepStart", QVariant.String)])
                    ppTimes.append(times[i][2])
            elif mm.upper() == "START":
                if times[i][1].upper() == "CLOCKTIME":
                    prTimes.addAttributes([QgsField("StartClock", QVariant.String)])
                    if len(times[i]) > 3:
                        ppTimes.append(times[i][2] + ' ' + times[i][3])
                    else:
                        ppTimes.append(times[i][2])
            elif mm.upper() == "STATISTIC":
                prTimes.addAttributes([QgsField("Statistic", QVariant.String)])
                if times[i][1].upper() == 'NONE' or times[i][1].upper() == 'AVERAGE' or times[i][1].upper() \
                        == 'MINIMUM' or times[i][1].upper() == 'MAXIMUM' or times[i][1].upper() == 'RANGE':
                    ppTimes.append(times[i][1])
        if i < allSections[14]:
            mm = report[i][0]
            if mm.upper() == "PAGESIZE":
                prRep.addAttributes([QgsField("PageSize", QVariant.String)])
                ppRep.append(report[i][1])
            if mm.upper() == "FILE":
                prRep.addAttributes([QgsField("FileName", QVariant.String)])
                ppRep.append(report[i][1])
            elif mm.upper() == "STATUS":
                prRep.addAttributes([QgsField("Status", QVariant.String)])
                ppRep.append(report[i][1])
            elif mm.upper() == "SUMMARY":
                prRep.addAttributes([QgsField("Summary", QVariant.String)])
                ppRep.append(report[i][1])
            elif mm.upper() == "ENERGY":
                prRep.addAttributes([QgsField("Energy", QVariant.String)])
                ppRep.append(report[i][1])
            elif mm.upper() == "NODES":
                prRep.addAttributes([QgsField("Nodes", QVariant.String)])
                if len(report[i]) > 2:
                    ppRep.append(report[i][1] + ' ' + report[i][2])
                else:
                    ppRep.append(report[i][1])
            elif mm.upper() == "LINKS":
                prRep.addAttributes([QgsField("Links", QVariant.String)])
                if len(report[i]) > 2:
                    ppRep.append(report[i][1] + ' ' + report[i][2])
                else:
                    ppRep.append(report[i][1])
            else:
                prRep.addAttributes([QgsField(mm, QVariant.String)])
                if len(report[i]) > 2:
                    ppRep.append(report[i][1] + ' ' + report[i][2])
                else:
                    ppRep.append(report[i][1])
        if i < allSections[15]:
            mm = options[i][0]
            if mm.upper() == "UNITS":
                prOpt.addAttributes([QgsField("Units", QVariant.String)])
                ppOpt.append(options[i][1])
            if mm.upper() == "HYDRAULICS":
                prOpt.addAttributes([QgsField("Hydraulics", QVariant.String)])
                if len(options[i]) > 2:
                    ppOpt.append(options[i][1] + ' ' + options[i][2])
                else:
                    ppOpt.append(options[i][1])
            elif mm.upper() == "QUALITY":
                prOpt.addAttributes([QgsField("Quality", QVariant.String)])
                if len(options[i]) > 2:
                    ppOpt.append(options[i][1] + ' ' + options[i][2])
                elif len(options[i]) > 3:
                    ppOpt.append(options[i][1] + ' ' + options[i][2] + ' ' + options[i][3])
                else:
                    ppOpt.append(options[i][1])
            elif mm.upper() == "VISCOSITY":
                prOpt.addAttributes([QgsField("Viscosity", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "DIFFUSIVITY":
                prOpt.addAttributes([QgsField("Diffusivity", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "SPECIFIC":
                if options[i][1].upper() == "GRAVITY":
                    prOpt.addAttributes([QgsField("SpecGrav", QVariant.String)])
                    ppOpt.append(options[i][2])
            elif mm.upper() == "TRIALS":
                prOpt.addAttributes([QgsField("Trials", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "HEADLOSS":
                prOpt.addAttributes([QgsField("Headloss", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "ACCURACY":
                prOpt.addAttributes([QgsField("Accuracy", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "UNBALANCED":
                prOpt.addAttributes([QgsField("Unbalanced", QVariant.String)])
                if len(options[i]) > 2:
                    ppOpt.append(options[i][1] + ' ' + options[i][2])
                else:
                    ppOpt.append(options[i][1])
            elif mm.upper() == "PATTERN":
                prOpt.addAttributes([QgsField("PatID", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "TOLERANCE":
                prOpt.addAttributes([QgsField("Tolerance", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "MAP":
                prOpt.addAttributes([QgsField("Map", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "DEMAND":
                if options[i][1].upper() == "MULTIPLIER":
                    prOpt.addAttributes([QgsField("DemMult", QVariant.String)])
                    ppOpt.append(options[i][2])
            elif mm.upper() == "EMITTER":
                if options[i][1].upper() == "EXPONENT":
                    prOpt.addAttributes([QgsField("EmitExp", QVariant.String)])
                    ppOpt.append(options[i][2])
            elif mm.upper() == "CHECKFREQ":
                prOpt.addAttributes([QgsField("CheckFreq", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "MAXCHECK":
                prOpt.addAttributes([QgsField("MaxCheck", QVariant.String)])
                ppOpt.append(options[i][1])
            elif mm.upper() == "DAMPLIMIT":
                prOpt.addAttributes([QgsField("DampLimit", QVariant.String)])
                ppOpt.append(options[i][1])

    writeDBF(posOpt, [ppOpt], prOpt, saveFile, inpname, "_OPTIONS", idx)

    writeDBF(posRep, [ppRep], prRep, saveFile, inpname, "_REPORT", idx)

    #if times:
    writeDBF(posTimes, [ppTimes], prTimes, saveFile, inpname, "_TIMES", idx)

    #if energy:
    writeDBF(posE, [ppE], prE, saveFile, inpname, "_ENERGY", idx)

    #if optReactions:
    writeDBF(posO, [ppO], prO, saveFile, inpname, "_REACTIONS", idx)

    posMix = QgsVectorLayer("point?crs=EPSG:4326", "Mixing", "memory")
    prMix = posMix.dataProvider()
    fields = ["Tank_ID", "Model", "Fraction"]
    fieldsCode = [0, 0, 1]  # 0 String, 1 Double
    createColumnsAttrb(prMix, fields, fieldsCode)
    writeDBF(posMix, ppMix, prMix, saveFile, inpname, "_MIXING", idx)

    posReact = QgsVectorLayer("point?crs=EPSG:4326", "ReactionsI", "memory")
    prReact = posReact.dataProvider()
    fields = ["Type", "Pipe/Tank", "Coeff."]
    fieldsCode = [0, 0, 1]
    createColumnsAttrb(prReact, fields, fieldsCode)
    writeDBF(posReact, ppReactions, prReact, saveFile, inpname, "_REACTIONS_I", idx)

    posSourc = QgsVectorLayer("point?crs=EPSG:4326", "Sources", "memory")
    prSourc = posSourc.dataProvider()
    fields = ["Node_ID", "Type", "Strength", "Pattern"]
    fieldsCode = [0, 0, 1, 0]
    createColumnsAttrb(prSourc, fields, fieldsCode)
    writeDBF(posSourc, ppSourc, prSourc, saveFile, inpname, "_SOURCES", idx)

    posRul = QgsVectorLayer("point?crs=EPSG:4326", "Rules", "memory")
    prRul = posRul.dataProvider()
    fields = ["Rule_ID", "Rule"]
    fieldsCode = [0, 0]
    createColumnsAttrb(prRul, fields, fieldsCode)
    writeDBF(posRul, ppRul, prRul, saveFile, inpname, "_RULES", idx)

    posQual = QgsVectorLayer("point?crs=EPSG:4326", "Sources", "memory")
    prQual = posQual.dataProvider()
    fields = ["Node_ID", "Init_Qual"]
    fieldsCode = [0, 1]
    createColumnsAttrb(prQual, fields, fieldsCode)
    writeDBF(posQual, ppQual, prQual, saveFile, inpname, "_QUALITY", idx)

    posStat = QgsVectorLayer("point?crs=EPSG:4326", "Status", "memory")
    prStat = posStat.dataProvider()
    fields = ["Link_ID", "Status/Setting"]
    fieldsCode = [0, 0]
    createColumnsAttrb(prStat, fields, fieldsCode)
    writeDBF(posStat, ppStat, prStat, saveFile, inpname, "_STATUS", idx)

    posEmit = QgsVectorLayer("point?crs=EPSG:4326", "Emitters", "memory")
    prEmit = posEmit.dataProvider()
    fields = ["Junc_ID", "Coeff."]
    fieldsCode = [0, 1]
    createColumnsAttrb(prEmit, fields, fieldsCode)
    writeDBF(posEmit, ppEmit, prEmit, saveFile, inpname, "_EMITTERS", idx)

    posCont = QgsVectorLayer("point?crs=EPSG:4326", "Controls", "memory")
    prCont = posCont.dataProvider()
    fields = ["Controls"]
    fieldsCode = [0]
    createColumnsAttrb(prCont, fields, fieldsCode)
    writeDBF(posCont, ppCont, prCont, saveFile, inpname, "_CONTROLS", idx)

    posPat = QgsVectorLayer("point?crs=EPSG:4326", "Patterns", "memory")
    prPat = posPat.dataProvider()
    fields = ["Pattern_ID", "Multipliers"]
    fieldsCode = [0, 0]
    createColumnsAttrb(prPat, fields, fieldsCode)
    writeDBF(posPat, ppPat, prPat, saveFile, inpname, "_PATTERNS", idx)

    posCurv = QgsVectorLayer("point?crs=EPSG:4326", "Curves", "memory")
    prCurv = posCurv.dataProvider()
    fields = ["Curve_ID", "X-Value", "Y-Value", "Type"]
    fieldsCode = [0, 0, 0, 0]
    createColumnsAttrb(prCurv, fields, fieldsCode)
    writeDBF(posCurv, ppCurv, prCurv, saveFile, inpname, "_CURVES", idx)

    # Write Valve Shapefile
    posValve = QgsVectorLayer("LineString?crs=EPSG:4326", "Valve", "memory")
    prValve = posValve.dataProvider()

    fields = ["ID", "NodeFrom", "NodeTo", "Diameter", "Type", "Setting", "MinorLoss"]
    fieldsCode = [0, 0, 0, 1, 0, 1, 1]
    createColumnsAttrb(prValve, fields, fieldsCode)
    posValve.startEditing()

    if d.getBinLinkValveCount() > 0:

        linkID = d.getBinLinkValveNameID()
        linkType = d.getBinLinkValveType()  # valve type
        linkDiameter = d.getBinLinkValveDiameters()
        linkInitSett = d.getBinLinkValveSetting()  # BinLinkValveSetting
        linkMinorloss = d.getBinLinkValveMinorLoss()

        for i, p in enumerate(d.getBinLinkValveIndex()):
            point1 = QgsPointXY(float(x[ndID.index(d.getBinLinkFromNode()[p])]), float(y[ndID.index(d.getBinLinkFromNode()[p])]))
            point2 = QgsPointXY(float(x[ndID.index(d.getBinLinkToNode()[p])]), float(y[ndID.index(d.getBinLinkToNode()[p])]))
            feature = QgsFeature()
            feature.setGeometry(QgsGeometry.fromPolylineXY([point1, point2]))

            feature.setAttributes(
                [linkID[i], ndlConn[0][p], ndlConn[1][p], linkDiameter[i], linkType[i], linkInitSett[i],
                 linkMinorloss[i]])
            prValve.addFeatures([feature])

    QgsVectorFileWriter.writeAsVectorFormat(posValve, saveFile + "_valves" + '.shp', "utf-8",
                                            QgsCoordinateReferenceSystem(posValve.crs().authid()), "ESRI Shapefile")
    ll = QgsVectorLayer(saveFile + "_valves" + '.shp', inpname[:len(inpname) - 4] + "_valves", "ogr")
    QgsProject.instance().addMapLayer(ll, False)
    nvalves = QgsLayerTreeLayer(ll)
    idx.insertChildNode(0, nvalves)
    nvalves.setCustomProperty("showFeatureCount", True)
    ll.loadNamedStyle(plugin_path + "/qmls/" + 'valvesline' + ".qml")
    ll.triggerRepaint()

    # Write Pump Shapefile
    posPump = QgsVectorLayer("LineString?crs=EPSG:4326", "Pump", "memory")
    prPump = posPump.dataProvider()
    fields = ["ID", "NodeFrom", "NodeTo", "Power", "Pattern", "Curve"]
    fieldsCode = [0, 0, 0, 0, 0, 0]
    createColumnsAttrb(prPump, fields, fieldsCode)
    posPump.startEditing()

    if d.getBinLinkPumpCount() > 0:
        curveXY = d.getBinCurvesXY()
        curvesID = d.getBinCurvesNameID()

        a = curvesID
        b = []
        for l in a:
            if l not in b:
                b.append(l)
        curvesIDunique = b
        CurvesTmpIndices = []
        for p in range(0, len(curvesIDunique)):
            CurvesTmpIndices.append(curvesID.count(curvesIDunique[p]))

        curveIndices = []
        Curve = d.getBinLinkPumpCurveNameID()
        for i in range(len(Curve)):
            curveIndices.append(curvesIDunique.index(Curve[i]))
        if d.getBinCurveCount():
            CurvesTmpIndicesFinal = []
            CurvesTmpIndicesFinal.append([CurvesTmpIndices[index] for index in curveIndices])

            CurvesTmp = []
            i = 0
            for u in range(max(CurvesTmpIndicesFinal[0])):
                fields.append('Head' + str(u + 1))
                fields.append('Flow' + str(u + 1))
                fieldsCode.append(1)
                fieldsCode.append(1)
                if u < d.getBinCurveCount():
                    tmp1 = []
                    for p in range(CurvesTmpIndices[u]):
                        tmp1.append([curveXY[i][0], curveXY[i][1]])
                        i = i + 1
                    CurvesTmp.append(tmp1)

        createColumnsAttrb(prPump, fields, fieldsCode)

        chPowerPump = d.getBinLinkPumpPower()
        pumpID = d.getBinLinkPumpNameID()
        patternsIDs = d.getBinLinkPumpPatterns()
        ppatt = d.getBinLinkPumpPatternsPumpID()
        linkID = d.getBinLinkNameID()

        for i, p in enumerate(d.getBinLinkPumpIndex()):

            Curve = []
            power = []
            pattern = []
            pumpNameIDPower = d.getBinLinkPumpNameIDPower()
            if len(pumpNameIDPower) > 0:
                for uu in range(0, len(pumpNameIDPower)):
                    if pumpNameIDPower[uu] == pumpID[i]:
                        power = chPowerPump[uu]
            if len(patternsIDs) > 0:
                for uu in range(0, len(ppatt)):
                    if ppatt[uu] == pumpID[i]:
                        pattern = patternsIDs[uu]

            point1 = QgsPointXY(float(x[ndID.index(d.getBinLinkFromNode()[p])]), float(y[ndID.index(d.getBinLinkFromNode()[p])]))
            point2 = QgsPointXY(float(x[ndID.index(d.getBinLinkToNode()[p])]), float(y[ndID.index(d.getBinLinkToNode()[p])]))
            feature = QgsFeature()
            feature.setGeometry(QgsGeometry.fromPolylineXY([point1, point2]))

            if not Curve:
                Curve = 'NULL'
            if not power:
                power = 'NULL'
            if not pattern:
                pattern = 'NULL'

            if d.getBinCurveCount() > 0 and len(pumpNameIDPower) == 0:
                Curve = d.getBinLinkPumpCurveNameID()[i]
                curveIndex = curvesIDunique.index(Curve)

            feature.initAttributes(6 + sum(CurvesTmpIndices) * 2 + 1)
            feature.setAttribute(0, linkID[p])
            feature.setAttribute(1, ndlConn[0][p])
            feature.setAttribute(2, ndlConn[1][p])
            feature.setAttribute(3, power)
            feature.setAttribute(4, pattern)
            feature.setAttribute(5, Curve)

            if d.getBinCurveCount() == 1:
                w = 6
                for p in range(CurvesTmpIndices[curveIndex]):
                    feature.setAttribute(w, CurvesTmp[curveIndex][p][0])
                    feature.setAttribute(w + 1, CurvesTmp[curveIndex][p][1])
                    w = w + 2

            for j in range(d.getBinCurveCount() - 1):
                w = 6
                for p in range(CurvesTmpIndices[curveIndex]):
                    feature.setAttribute(w, CurvesTmp[curveIndex][p][0])
                    feature.setAttribute(w + 1, CurvesTmp[curveIndex][p][1])
                    w = w + 2

            prPump.addFeatures([feature])

    QgsVectorFileWriter.writeAsVectorFormat(posPump,saveFile+"_pumps"+'.shp', "utf-8",
                                            QgsCoordinateReferenceSystem(posPump.crs().authid()), "ESRI Shapefile")
    ll = QgsVectorLayer(saveFile + "_pumps" + '.shp', inpname[:len(inpname) - 4] + "_pumps", "ogr")
    QgsProject.instance().addMapLayer(ll, False)
    npump = QgsLayerTreeLayer(ll)
    idx.insertChildNode(0, npump)
    npump.setCustomProperty("showFeatureCount", True)
    ll.loadNamedStyle(plugin_path + "/qmls/" + 'pumpsline' + ".qml")
    ll.triggerRepaint()

    QgsVectorFileWriter.writeAsVectorFormat(posPipe,saveFile+"_pipes"+'.shp', "utf-8",
                                            QgsCoordinateReferenceSystem(posPipe.crs().authid()), "ESRI Shapefile")
    ll = QgsVectorLayer(saveFile + "_pipes" + '.shp', inpname[:len(inpname) - 4] + "_pipes", "ogr")
    QgsProject.instance().addMapLayer(ll, False)
    npipe = QgsLayerTreeLayer(ll)
    idx.insertChildNode(0, npipe)
    npipe.setCustomProperty("showFeatureCount", True)
    ll.loadNamedStyle(plugin_path + "/qmls/" + 'pipes' + ".qml")
    ll.triggerRepaint()
    iface.mapCanvas().setExtent(ll.extent())

    QgsVectorFileWriter.writeAsVectorFormat(posJunction,saveFile+"_junctions"+'.shp', "utf-8",
                                            QgsCoordinateReferenceSystem(posJunction.crs().authid()), "ESRI Shapefile")
    ll = QgsVectorLayer(saveFile + "_junctions" + '.shp', inpname[:len(inpname) - 4] + "_junctions", "ogr")
    QgsProject.instance().addMapLayer(ll, False)
    njunc = QgsLayerTreeLayer(ll)
    idx.insertChildNode(0, njunc)
    njunc.setCustomProperty("showFeatureCount", True)
    ll.loadNamedStyle(plugin_path + "/qmls/" + 'junctions' + ".qml")
    ll.triggerRepaint()

    QgsVectorFileWriter.writeAsVectorFormat(posTank, saveFile + "_tanks" + '.shp', "utf-8",
                                            QgsCoordinateReferenceSystem(posTank.crs().authid()), "ESRI Shapefile")
    ll = QgsVectorLayer(saveFile + "_tanks" + '.shp', inpname[:len(inpname) - 4] + "_tanks", "ogr")
    QgsProject.instance().addMapLayer(ll, False)
    ntanks = QgsLayerTreeLayer(ll)
    idx.insertChildNode(0, ntanks)
    ntanks.setCustomProperty("showFeatureCount", True)
    ll.loadNamedStyle(plugin_path + "/qmls/" + 'tanks' + ".qml")
    ll.triggerRepaint()

    QgsVectorFileWriter.writeAsVectorFormat(posReservoirs, saveFile + "_reservoirs" + '.shp', "utf-8",
                                            QgsCoordinateReferenceSystem(posReservoirs.crs().authid()), "ESRI Shapefile")
    ll = QgsVectorLayer(saveFile + "_reservoirs" + '.shp', inpname[:len(inpname) - 4] + "_reservoirs", "ogr")
    QgsProject.instance().addMapLayer(ll, False)
    nres = QgsLayerTreeLayer(ll)
    idx.insertChildNode(0, nres)
    nres.setCustomProperty("showFeatureCount", True)
    ll.loadNamedStyle(plugin_path + "/qmls/" + 'reservoirs' + ".qml")
    ll.triggerRepaint()
Example #30
0
class GoogleDriveLayer(QObject):
    """ Pretend we are a data provider """

    invalidEdit = pyqtSignal()
    deferredEdit = pyqtSignal()
    dirty = False
    doing_attr_update = False
    geom_types = ("Point", "LineString", "Polygon","Unknown","NoGeometry")


    def __init__(self, parent, authorization, layer_name, spreadsheet_id = None, loading_layer = None, importing_layer = None, crs_def = None, geom_type = None, test = None):
        '''
        Initialize the layer by reading the Google drive sheet, creating a memory
        layer, and adding records to it, optionally used fo layer export to google drive
        :param parent:
        :param authorization: google authorization object
        :param layer_name: the layer name
        :param spreadsheet_id: the spreadsheetId of the table to download and load as qgis layer; default to None
        :param loading_layer: the layer loading from project file; default to None
        :param importing_layer: the layer that is being imported; default to None
        :param test: used for testing
        '''

        super(GoogleDriveLayer, self).__init__()
        # Save the path to the file soe we can update it in response to edits
        self.test = test
        self.parent = parent
        self.iface = parent.iface
        bar = progressBar(self, 'loading google drive layer')
        self.service_drive = service_drive(authorization)
        self.client_id = authorization.client_id
        self.authorization = authorization
        if spreadsheet_id:
            self.spreadsheet_id = spreadsheet_id
            self.service_sheet = service_spreadsheet(authorization, self.spreadsheet_id)
        elif importing_layer:
            layer_as_list = self.qgis_layer_to_list(importing_layer)
            self.service_sheet = service_spreadsheet(authorization, new_sheet_name=importing_layer.name(), new_sheet_data = layer_as_list)
            self.spreadsheet_id = self.service_sheet.spreadsheetId
            self.service_sheet.set_crs(importing_layer.crs().authid())
            self.service_sheet.set_geom_type(self.geom_types[importing_layer.geometryType()])
            self.service_sheet.set_style_qgis(self.layer_style_to_xml(importing_layer))
            self.service_sheet.set_style_sld(self.SLD_to_xml(importing_layer))
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(importing_layer))
            self.dirty = True
            self.saveFieldTypes(importing_layer.fields())

        self.reader = self.service_sheet.get_sheet_values()
        self.header = self.reader[0]

        self.crs_def = self.service_sheet.crs()
        self.geom_type = self.service_sheet.geom_type()
        logger("LOADED GOOGLE SHEET LAYER: %s CRS_ID:%s GEOM_type:%s" % (self.service_sheet.name,self.crs_def, self.geom_type))
        #logger( "GEOM_type "+ self.geom_type)
        # Build up the URI needed to create memory layer
        if loading_layer:
            self.lyr = loading_layer
            print "LOADIN", self.lyr, loading_layer
            attrIds = [i for i in range (0, self.lyr.fields().count())]
            self.lyr.dataProvider().deleteAttributes(attrIds)
            self.lyr.updateFields()
        else:
            self.uri = self.uri = "Multi%s?crs=%s&index=yes" % (self.geom_type, self.crs_def)
            #logger(self.uri)
            # Create the layer
            self.lyr = QgsVectorLayer(self.uri, layer_name, 'memory')
            self.lyr.setCustomProperty("googleDriveId", self.spreadsheet_id)
        fields_types = self.service_sheet.get_line("ROWS", 1, sheet="settings")
        attributes = []
        for i in range(2,len(self.header)):
            if self.header[i][:8] != 'DELETED_':
                type_pack = fields_types[i].split("|")
                attributes.append(QgsField(name=self.header[i],type=int(type_pack[0]), len=int(type_pack[1]), prec=int(type_pack[2])))
                #self.uri += u'&field={}:{}'.format(fld.decode('utf8'), field_name_types[fld])
        self.lyr.dataProvider().addAttributes(attributes)
        self.lyr.updateFields()

        self.xml_to_layer_style(self.lyr,self.service_sheet.style())
        self.lyr.rendererChanged.connect(self.style_changed)

        self.add_records()

        # Make connections
        self.makeConnections(self.lyr)

        # Add the layer the map
        if not loading_layer:
            QgsMapLayerRegistry.instance().addMapLayer(self.lyr)
        else:
            pass
            #self.lyr.editingStarted.emit()
        #create summary if importing
        if importing_layer:
            self.update_summary_sheet()
        self.lyr.gdrive_control = self
        bar.stop("Layer %s succesfully loaded" % layer_name)

    def makeConnections(self,lyr):
        '''
        The method handle default signal connections to the connected qgis memory layer
        :param lyr: qgis layer
        :return:
        '''
        self.deferredEdit.connect(self.apply_locks)
        lyr.editingStarted.connect(self.editing_started)
        lyr.editingStopped.connect(self.editing_stopped)
        lyr.committedAttributesDeleted.connect(self.attributes_deleted)
        lyr.committedAttributesAdded .connect(self.attributes_added)
        lyr.committedFeaturesAdded.connect(self.features_added)
        lyr.committedGeometriesChanges.connect(self.geometry_changed)
        lyr.committedAttributeValuesChanges.connect(self.attributes_changed)
        lyr.layerDeleted.connect(self.unsubscribe)
        lyr.beforeCommitChanges.connect(self.inspect_changes)
        #add contextual menu
        self.sync_with_google_drive_action = QAction(QIcon(os.path.join(self.parent.plugin_dir,'sync.png')), "Sync with Google drive", self.iface.legendInterface() )
        self.iface.legendInterface().addLegendLayerAction(self.sync_with_google_drive_action, "","01", QgsMapLayer.VectorLayer,False)
        self.iface.legendInterface().addLegendLayerActionForLayer(self.sync_with_google_drive_action, lyr)
        self.sync_with_google_drive_action.triggered.connect(self.sync_with_google_drive)
        lyr.gDriveInterface = self

    def add_records(self):
        '''
        Add records to the memory layer by reading the Google Sheet
        '''
        self.lyr.startEditing()

        for i, row in enumerate(self.reader[1:]):
            flds = collections.OrderedDict(zip(self.header, row))
            status = flds.pop('STATUS')

            if status != 'D': #non caricare i deleted
                wkt_geom = zlib.decompress(base64.b64decode(flds.pop('WKTGEOMETRY')))
                #fid = int(flds.pop('FEATUREID'))
                feature = QgsFeature()
                geometry = QgsGeometry.fromWkt(wkt_geom)
                feature.setGeometry(geometry)
                cleared_row = [] #[fid]
                for field, attribute in flds.iteritems():
                    if field[:8] != 'DELETED_': #skip deleted fields
                        if attribute == '()':
                            cleared_row.append(qgis.core.NULL)
                        else:
                            cleared_row.append(attribute)
                    else:
                        logger( "DELETED " + field)
                #print "cleared_row", cleared_row
                feature.setAttributes(cleared_row)
                self.lyr.addFeature(feature)
        self.lyr.commitChanges()

    def style_changed(self):
        '''
        landing method for rendererChanged signal. It stores xml qgis style definition to the setting sheet
        '''
        logger( "style changed")
        self.service_sheet.set_style_qgis(self.layer_style_to_xml(self.lyr))
        self.service_sheet.set_style_sld(self.SLD_to_xml(self.lyr))
        self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))

    def renew_connection(self):
        '''
        when connection stay alive too long we have to rebuild service
        '''
        self.service_drive.renew_connection()

    def sync_with_google_drive(self):
        self.renew_connection()
        self.update_from_subscription()
        self.update_summary_sheet()

    def update_from_subscription(self):
        '''
        The method updates qgis memory layer with changes made by other users and sincronize the local qgis layer with google sheet spreadsheet
        '''
        self.renew_connection()
        bar = progressBar(self, 'updating local layer from remote')
        print "canEdit", self.service_sheet.canEdit
        if self.service_sheet.canEdit:
            updates = self.service_sheet.get_line('COLUMNS','A', sheet=self.client_id)
            if updates:
                self.service_sheet.erase_cells(self.client_id)
        else:
            new_changes_log_rows = self.service_sheet.get_line("COLUMNS",'A',sheet="changes_log")
            if len(new_changes_log_rows) > len(self.service_sheet.changes_log_rows):
                updates = new_changes_log_rows[-len(new_changes_log_rows)+len(self.service_sheet.changes_log_rows):]
                self.service_sheet.changes_log_rows = new_changes_log_rows
            else:
                updates = []
        print "UPDATES", updates
        for update in updates:
            decode_update = update.split("|")
            if decode_update[0] in ('new_feature', 'delete_feature', 'update_geometry', 'update_attributes'):
                sheet_feature = self.service_sheet.get_line('ROWS',decode_update[1])
                if decode_update[0] == 'new_feature':
                    feat = QgsFeature()
                    geom = QgsGeometry().fromWkt(zlib.decompress(base64.b64decode(sheet_feature[0])))
                    feat.setGeometry(geom)
                    feat.setAttributes(sheet_feature[2:])
                    logger(( "updating from subscription, new_feature: " + str(self.lyr.dataProvider().addFeatures([feat]))))
                else:
                    sheet_feature_id = decode_update[1]
                    feat = self.lyr.getFeatures(QgsFeatureRequest(QgsExpression(' "FEATUREID" = %s' % sheet_feature_id))).next()
                    if   decode_update[0] == 'delete_feature':
                        print "updating from subscription, delete_feature: " + str(self.lyr.dataProvider().deleteFeatures([feat.id()]))
                    elif decode_update[0] == 'update_geometry':
                        update_set = {feat.id(): QgsGeometry().fromWkt(zlib.decompress(base64.b64decode(sheet_feature[0])))}
                        print "update_set", update_set
                        print "updating from subscription, update_geometry: " + str(self.lyr.dataProvider().changeGeometryValues(update_set))
                    elif decode_update[0] == 'update_attributes':
                        new_attributes = sheet_feature_id[2:]
                        attributes_map = {}
                        for i in range(0, len(new_attributes)):
                            attributes_map[i] = new_attributes[i]
                        update_map = {feat.id(): attributes_map,}
                        print "update_map", update_map
                        print "updating from subscription, update_attributes: " +(self.lyr.dataProvider().changeAttributeValues(update_map))
            elif decode_update[0] == 'add_field':
                field_a1_notation = self.service_sheet.header_map[decode_update[1]]
                type_def = self.service_sheet.sheet_cell('settings!%s1' % field_a1_notation)
                type_def_decoded = type_def.split("|")
                new_field = QgsField(name=decode_update[1],type=int(type_def_decoded[0]), len=int(type_def_decoded[1]), prec=int(type_def_decoded[2]))
                print "updating from subscription, add_field: ", + (self.lyr.dataProvider().addAttributes([new_field]))
                self.lyr.updateFields()
            elif decode_update[0] == 'delete_field':
                print "updating from subscription, delete_field: " + str(self.lyr.dataProvider().deleteAttributes([self.lyr.dataProvider().fields().fieldNameIndex(decode_update[1])]))
                self.lyr.updateFields()
        self.lyr.triggerRepaint()
        bar.stop("local layer updated")

    def editing_started(self):
        '''
        Connect to the edit buffer so we can capture geometry and attribute
        changes
        '''
        print "editing"
        self.update_from_subscription()
        self.bar = None
        if self.service_sheet.canEdit:
            self.activeThreads = 0
            self.editing = True
            self.lyr.geometryChanged.connect(self.buffer_geometry_changed)
            self.lyr.attributeValueChanged.connect(self.buffer_attributes_changed)
            self.lyr.beforeCommitChanges.connect(self.catch_deleted)
            self.lyr.beforeRollBack.connect(self.rollBack)
            self.invalidEdit.connect(self.rollBack)
            self.changes_log=[]
            self.locking_queue = []
            self.timer = 0
        else: #refuse editing if file is read only
            self.lyr.rollBack()

    def buffer_geometry_changed(self,fid,geom):
        '''
        Landing method for geometryChanged signal.
        When a geometry is modified, the row related to the modified feature is marked as modified by local user.
        Further edits to the modified feature are denied to other concurrent users
        :param fid:
        :param geom:
        '''
        if self.editing:
            self.lock_feature(fid)

    def buffer_attributes_changed(self,fid,attr_id,value):
        '''
        Landing method for attributeValueChanged signal.
        When an attribute is modified, the row related to the modified feature is marked as modified by local user.
        Further edits to the modified feature are denied to other concurrent users
        :param fid:
        :param attr_id:
        :param value:
        '''
        if self.editing:
            self.lock_feature(fid)


    def lock_feature(self, fid):
        """
        The row in google sheet linked to feature that has been modified is locked
        Filling the the STATUS column with the client_id.
        Further edits to the modified feature are denied to other concurrent users
        """
        if fid >= 0: # fid <0 means that the change relates to newly created features not yet present in the sheet
            self.locks_applied = None
            feature_locking = self.lyr.getFeatures(QgsFeatureRequest(fid)).next()
            locking_row_id = feature_locking[0]
            self.locking_queue.append(locking_row_id)
            thread.start_new_thread(self.deferred_apply_locks, ())


    def deferred_apply_locks(self):
        if self.timer > 0:
            self.timer = 0
            return
        else:
            while self.timer < 100:
                self.timer += 1
                sleep(0.01)
            #APPLY_LOCKS
            self.deferredEdit.emit()
            #self.apply_locks()

    def apply_locks(self):
        if self.locks_applied:
            return
        self.locks_applied = True
        status_range = []
        for row_id in self.locking_queue:
            #print "locking_row_id",locking_row_id
            status_range.append(['STATUS', row_id])
        status_control = self.service_sheet.multicell(status_range)
        if "valueRanges" in status_control:
            mods = []
            for valueRange in status_control["valueRanges"]:
                if valueRange["values"][0][0] in ('()', None):
                    mods.append([valueRange["range"],0,self.client_id])
                    row_id = valueRange["range"].split('B')[-1]
            if mods:
                self.service_sheet.set_multicell(mods, A1notation=True)
        self.locking_queue = []
        self.timer = 0

    def ex_apply_locks(self):
        mods = []
        for row_id in self.locking_queue:
            #print "locking_row_id",locking_row_id
            status = self.service_sheet.cell('STATUS', row_id)
            if status in (None,''):
                self.service_sheet.set_cell('STATUS', row_id, self.client_id)
                #mods.append(['STATUS', row_id, self.client_id])
        if mods:
            self.service_sheet.set_multicell(mods)
        self.locking_queue = []
        self.timer = 0

    def ex_buffer_geometry_changed(self,fid,geom):
        '''
        Landing method for geometryChanged signal.
        When a geometry is modified, the row related to the modified feature is marked as modified by local user.
        Further edits to the modified feature are denied to other concurrent users
        :param fid:
        :param geom:
        '''
        if self.editing:
            if self.test:
                self.lock_feature(fid)
            else:
                print "geom changed fid:",fid, self.locking_queue
                thread.start_new_thread(self.lock_feature, (fid,))
            #logger("active threads: "+ str( self.activeThreads))
            #self.lock_feature(fid)

    def ex_buffer_attributes_changed(self,fid,attr_id,value):
        '''
        Landing method for attributeValueChanged signal.
        When an attribute is modified, the row related to the modified feature is marked as modified by local user.
        Further edits to the modified feature are denied to other concurrent users
        :param fid:
        :param attr_id:
        :param value:
        '''
        if self.editing:
            if self.test:
                self.lock_feature(fid)
            else:
                print "attr changed fid:",fid, self.locking_queue
                thread.start_new_thread(self.lock_feature, (fid,))
            #logger("active threads: "+ str( self.activeThreads))
            #print "active threads: ", self.activeThreads
            #self.lock_feature(fid)

    def ex_lock_feature(self,fid):
        """
        The row in google sheet linked to feature that has been modified is locked
        Filling the the STATUS column with the client_id.
        Further edits to the modified feature are denied to other concurrent users
        """
        self.activeThreads += 1
        if fid >= 0:
            self.locking_queue.append(fid)
            if self.activeThreads < 2: # fid <0 means that the change relates to newly created features not yet present in the sheet
                while self.locking_queue:
                    lock_fid = self.locking_queue[0]
                    self.locking_queue = self.locking_queue[1:]
                    feature_locking = self.lyr.getFeatures(QgsFeatureRequest(lock_fid)).next()
                    locking_row_id = feature_locking[0]
                    status = self.service_sheet.cell('STATUS', locking_row_id)
                    if status in (None,''):
                        self.service_sheet.set_cell('STATUS', locking_row_id, self.client_id)
                        #logger("feature #%s locked by %s" % (locking_row_id, self.client_id))

        self.activeThreads -= 1

    def rollBack(self):
        """
        before rollback changes status field is cleared and the edits from concurrent user are allowed
        """
        print "ROLLBACK"
        try:
            self.lyr.geometryChanged.disconnect(self.buffer_geometry_changed)
        except:
            pass
        try:
            self.lyr.attributeValueChanged.disconnect(self.buffer_attributes_changed)
        except:
            pass
        self.renew_connection()
        self.clean_status_row()
        try:
            self.lyr.beforeRollBack.disconnect(self.rollBack)
        except:
            pass

        #self.lyr.geometryChanged.disconnect(self.buffer_geometry_changed)
        #self.lyr.attributeValueChanged.disconnect(self.buffer_attributes_changed)
        self.editing = False

    def editing_stopped(self):
        """
        Update the remote sheet if changes were committed
        """
        print "EDITING_STOPPED"
        self.renew_connection()
        self.clean_status_row()
        if self.service_sheet.canEdit:
            self.service_sheet.advertise(self.changes_log)
        self.editing = False
        #if self.dirty:
        #    self.update_summary_sheet()
        #    self.dirty = None
        if self.bar:
            self.bar.stop("update to remote finished")

    def inspect_changes(self):
        '''
        here we can inspect changes before commit them
        self.deleted_list = []
        for deleted in self.lyr.editBuffer().deletedAttributeIds():
            self.deleted_list.append(self.lyr.fields().at(deleted).name())
        print self.deleted_list

        logger("attributes_added")
        for field in self.lyr.editBuffer().addedAttributes():
            print "ADDED FIELD", field.name()
            self.service_sheet.add_column([field.name()], fill_with_null = True)
        '''
        print "INSPECT_CHANGES"
        pass

    def attributes_added(self, layer, added):
        """
        Landing method for attributeAdded.
        Fields (attribute) changed
        New colums are appended to the google drive spreadsheets creating remote colums syncronized with the local layer fields.
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        logger("attributes_added")
        for field in added:
            logger( "ADDED FIELD %s" % field.name())
            self.service_sheet.add_column([field.name()], fill_with_null = True)
            self.service_sheet.add_column(["%d|%d|%d" % (field.type(), field.length(), field.precision())],child_sheet="settings", fill_with_null = None)
            self.changes_log.append('%s|%s' % ('add_field', field.name()))
        self.dirty = True

    def attributes_deleted(self, layer, deleted_ids):
        """
        Landing method for attributeDeleted.
        Fields (attribute) are deleted
        New colums are marked as deleted in the google drive spreadsheets.
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        logger("attributes_deleted")
        for deleted in deleted_ids:
            deleted_name = self.service_sheet.mark_field_as_deleted(deleted)
            self.changes_log.append('%s|%s' % ('delete_field', deleted_name))
        self.dirty = True


    def features_added(self, layer, features):
        """
        Landing method for featureAdded.
        The new features are written adding rows to the google drive spreadsheets .
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        logger("features added")

        for count,feature in enumerate(features):
            new_fid = self.service_sheet.new_fid()
            print "NEWFID", new_fid, count
            self.lyr.dataProvider().changeAttributeValues({feature.id() : {0: new_fid}})
            feature.setAttribute(0, new_fid+count)
            '''
            print "WKB", base64.b64encode(feature.geometry().asWkb())
            print "WKB", base64.b64encode(zlib.compress(feature.geometry().asWkb()))
            print "WKT", base64.b64encode(zlib.compress(feature.geometry().exportToWkt()))
            '''
            new_row_dict = {}.fromkeys(self.service_sheet.header,'()')
            new_row_dict['WKTGEOMETRY'] = base64.b64encode(zlib.compress(feature.geometry().exportToWkt()))
            new_row_dict['STATUS'] = '()'
            for i,item in enumerate(feature.attributes()):
                fieldName = self.lyr.fields().at(i).name()
                print "FFFF", fieldName
                try:
                    new_row_dict[fieldName] = item.toString(format = Qt.ISODate)
                except:
                    if not item or item == qgis.core.NULL:
                        new_row_dict[fieldName] = '()'
                    else:
                        new_row_dict[fieldName] = item
            new_row_dict['FEATUREID'] = '=ROW()' #assure correspondance between feature and sheet row
            result = self.service_sheet.add_row(new_row_dict)
            sheet_new_row = int(result['updates']['updatedRange'].split('!A')[1].split(':')[0])
            self.changes_log.append('%s|%s' % ('new_feature', str(new_fid)))
        self.dirty = True

    def catch_deleted(self):
        """
        Landing method for beforeCommitChanges signal.
        The method intercepts edits before they were written to the layer so from deleted features
        can be extracted the feature id of the google drive spreadsheet related rows.
        The affected rows are marked as deleted and hidden away from the layer syncronization
        """
        self.bar = progressBar(self, 'updating local edits to remote')
        """ Features removed; but before commit """
        deleted_ids = self.lyr.editBuffer().deletedFeatureIds()
        if deleted_ids:
            deleted_mods = []
            for fid in deleted_ids:
                removed_feat = self.lyr.dataProvider().getFeatures(QgsFeatureRequest(fid)).next()
                removed_row = removed_feat[0]
                logger ("Deleting FEATUREID %s" % removed_row)
                deleted_mods.append(("STATUS",removed_row,'D'))
                self.changes_log.append('%s|%s' % ('delete_feature', str(removed_row)))
            if deleted_mods:
                self.service_sheet.set_protected_multicell(deleted_mods)
            self.dirty = True

    def geometry_changed(self, layer, geom_map):
        """
        Landing method for geometryChange signal.
        Features geometries changed
        The edited geometry, not locked by other users, are written to the google drive spreadsheets modifying the related rows.
        the WKT geometry definition is zipped and then base64 encoded for a compact storage
        (sigle cells string contents can't be larger the 50000 bytes)
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        geometry_mod = []
        for fid,geom in geom_map.iteritems():
            feature_changing = self.lyr.getFeatures(QgsFeatureRequest(fid)).next()
            row_id = feature_changing[0]
            wkt = geom.exportToWkt(precision=10)
            geometry_mod.append(('WKTGEOMETRY',row_id, base64.b64encode(zlib.compress(wkt))))
            logger ("Updated FEATUREID %s geometry" % row_id)
            self.changes_log.append('%s|%s' % ('update_geometry', str(row_id)))

        value_mods_result = self.service_sheet.set_protected_multicell(geometry_mod, lockBy=self.client_id)
        self.dirty = True

    def attributes_changed(self, layer, changes):
        """
        Landing method for attributeChange.
        Attribute values changed
        Edited feature, not locked by other users, are written to the google drive spreadsheets modifying the related rows.
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        if not self.doing_attr_update:
            #print "changes",changes
            attribute_mods = []
            for fid,attrib_change in changes.iteritems():
                feature_changing = self.lyr.getFeatures(QgsFeatureRequest(fid)).next()
                row_id = feature_changing[0]
                logger ( "Attribute changing FEATUREID: %s" % row_id)
                for attrib_idx, new_value in attrib_change.iteritems():
                    fieldName = QgsMapLayerRegistry.instance().mapLayer(layer).fields().field(attrib_idx).name()
                    if fieldName == 'FEATUREID':
                        logger("can't modify FEATUREID")
                        continue
                    try:
                        cleaned_value = new_value.toString(format = Qt.ISODate)
                    except:
                        if not new_value or new_value == qgis.core.NULL:
                            cleaned_value = '()'
                        else:
                            cleaned_value = new_value
                    attribute_mods.append((fieldName,row_id, cleaned_value))
                self.changes_log.append('%s|%s' % ('update_attributes', str(row_id)))

            if attribute_mods:
                attribute_mods_result = self.service_sheet.set_protected_multicell(attribute_mods, lockBy=self.client_id)
            self.dirty = True

    def clean_status_row(self):
        status_line = self.service_sheet.get_line("COLUMNS","B")
        clean_status_mods = []
        for row_line, row_value in enumerate(status_line):
            if row_value == self.client_id:
                clean_status_mods.append(("STATUS",row_line+1,'()'))
        value_mods_result = self.service_sheet.set_multicell(clean_status_mods)
        return value_mods_result

    def unsubscribe(self):
        '''
        When a read/write layer is removed from the legend the remote subscription sheet is removed and update summary sheet if dirty
        '''
        self.renew_connection()
        self.service_sheet.unsubscribe()

    def qgis_layer_to_csv(self,qgis_layer):
        '''
        method to transform the specified qgis layer in a csv object for uploading
        :param qgis_layer:
        :return: csv object
        '''
        stream = io.BytesIO()
        writer = csv.writer(stream, delimiter=',', quotechar='"', lineterminator='\n')
        row = ["WKTGEOMETRY","FEATUREID","STATUS"]
        for feat in qgis_layer.getFeatures():
            for field in feat.fields().toList():
                row.append(field.name().encode("utf-8"))
            break
        writer.writerow(row)
        for feat in qgis_layer.getFeatures():
            row = [base64.b64encode(zlib.compress(feat.geometry().exportToWkt(precision=10))),feat.id(),"()"]
            for field in feat.fields().toList():
                if feat[field.name()] == qgis.core.NULL:
                    content = "()"
                else:
                    if type(feat[field.name()]) == unicode:
                        content = feat[field.name()].encode("utf-8")
                    else:
                        content = feat[field.name()]
                row.append(content)
            writer.writerow(row)
        #print stream.getvalue()
        stream.seek(0)
        #csv.reader(stream, delimiter=',', quotechar='"', lineterminator='\n')
        return stream

    def qgis_layer_to_list(self,qgis_layer):
        '''
        method to transform the specified qgis layer in list of rows (field/value) dicts for uploading
        :param qgis_layer:
        :return: row list object
        '''
        row = ["WKTGEOMETRY","STATUS","FEATUREID"]
        for feat in qgis_layer.getFeatures():
            for field in feat.fields().toList():
                row.append(unicode(field.name()).encode("utf-8"))# slugify(field.name())
            break
        rows = [row]
        for feat in qgis_layer.getFeatures():
            row = [base64.b64encode(zlib.compress(feat.geometry().exportToWkt(precision=10))),"()","=ROW()"] # =ROW() perfect row/featureid correspondance
            if len(row[0]) > 10000:
                print feat.id, len(row)
            if len(row[0]) > 50000: # ignore features with geometry > 50000 bytes zipped
                continue
            for field in feat.fields().toList():
                if feat[field.name()] == qgis.core.NULL:
                    content = "()"
                else:
                    if type(feat[field.name()]) == unicode:
                        content = feat[field.name()].encode("utf-8")
                    elif field.typeName() in ('Date', 'Time'):
                        content = feat[field.name()].toString(format = Qt.ISODate)
                    else:
                        content = feat[field.name()]
                row.append(content)
            rows.append(row)
        #csv.reader(stream, delimiter=',', quotechar='"', lineterminator='\n')
        return rows

    def saveFieldTypes(self,fields):
        '''
        writes the layer field types to the setting sheet
        :param fields:
        :return:
        '''
        types_array = ["s1","s2","4|4|0"] #default featureId type to longint
        for field in fields.toList():
            types_array.append("%d|%d|%d" % (field.type(), field.length(), field.precision()))
        print "FIELDTYPES",self.service_sheet.update_cells('settings!A1',types_array)

    def layer_style_to_xml(self,qgis_layer):
        '''
        saves qgis style to the setting sheet
        :param qgis_layer:
        :return:
        '''
        XMLDocument = QDomDocument("qgis_style")
        XMLStyleNode = XMLDocument.createElement("style")
        XMLDocument.appendChild(XMLStyleNode)
        error = None
        qgis_layer.writeSymbology(XMLStyleNode, XMLDocument, error)
        xmldoc = XMLDocument.toString(1)
        return xmldoc

    def SLD_to_xml(self,qgis_layer):
        '''
        saves SLD style to the setting sheet. Not used, keeped here for further extensions.
        :param qgis_layer:
        :return:
        '''
        XMLDocument = QDomDocument("sld_style")
        error = None
        qgis_layer.exportSldStyle(XMLDocument, error)
        xmldoc = XMLDocument.toString(1)
        return xmldoc

    def xml_to_layer_style(self,qgis_layer,xml):
        '''
        retrieve qgis style from the setting sheet
        :param qgis_layer:
        :return:
        '''
        XMLDocument = QDomDocument()
        error = None
        XMLDocument.setContent(xml)
        XMLStyleNode = XMLDocument.namedItem("style")
        qgis_layer.readSymbology(XMLStyleNode, error)

    def layer_style_to_json(self, qgis_layer):
        mapbox_style = toMapboxgl([qgis_layer])
        print mapbox_style
        return json.dumps(mapbox_style)

    def get_gdrive_id(self):
        '''
        returns spreadsheet_id associated with layer
        :return: spreadsheet_id associated with layer
        '''
        return self.spreadsheet_id

    def get_service_drive(self):
        '''
        returns the google drive wrapper object associated with layer
        :return: google drive wrapper object
        '''
        return self.service_drive

    def get_service_sheet(self):
        '''
        returns the google spreadsheet wrapper object associated with layer
        :return: google spreadsheet wrapper object
        '''
        return self.service_sheet

    def get_layer_metadata(self):
        '''
        builds a metadata dict of the current layer to be stored in summary sheet
        '''
        #fields = collections.OrderedDict()
        fields = ""
        for field in self.lyr.fields().toList():
            fields += field.name()+'_'+QVariant.typeToName(field.type())+'|'+str(field.length())+'|'+str(field.precision())+' '
        #metadata = collections.OrderedDict()
        metadata = [
            ['layer_name', self.lyr.name(),],
            ['gdrive_id', self.service_sheet.spreadsheetId,],
            ['geometry_type', self.geom_types[self.lyr.geometryType()],],
            ['features', "'%s" % str(self.lyr.featureCount()),],
            ['extent', self.lyr.extent().asWktCoordinates(),],
            ['fields', fields,],
            ['srid', self.lyr.crs().authid(),],
            ['proj4_def', "'%s" % self.lyr.crs().toProj4(),]
        ]
        return metadata

    def update_summary_sheet(self):
        '''
        Creates a summary sheet with thumbnail, layer metadata and online view link
        '''
        #create a layer snapshot and upload it to google drive
        mapbox_style = self.service_sheet.sheet_cell('settings!A5')
        if not mapbox_style:
            print "migrating mapbox style"
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))
        if not self.dirty:
            return
        canvas = QgsMapCanvas()
        canvas.resize(QSize(300,300))
        canvas.setCanvasColor(Qt.white)
        canvas.setExtent(self.lyr.extent())
        canvas.setLayerSet([QgsMapCanvasLayer(self.lyr)])
        canvas.refresh()
        canvas.update()
        settings = canvas.mapSettings()
        settings.setLayers([self.lyr.id()])
        job = QgsMapRendererParallelJob(settings)
        job.start()
        job.waitForFinished()
        image = job.renderedImage()
        tmp_path = os.path.join(self.parent.plugin_dir,self.service_sheet.name+".png")
        image.save(tmp_path,"PNG")
        image_istances = self.service_drive.list_files(mimeTypeFilter='image/png',filename=self.service_sheet.name+".png")
        for imagename, image_props in image_istances.iteritems():
            print imagename, image_props['id']
            self.service_drive.delete_file(image_props['id'])
        result = self.service_drive.upload_image(tmp_path)
        print "UPLOADED", result
        self.service_drive.add_permission(result['id'],'anyone','reader')
        webLink = 'https://drive.google.com/uc?export=view&id='+result['id']
        os.remove(tmp_path)
        print 'result',result,webLink

        #update layer metadata
        summary_id = self.service_sheet.add_sheet('summary', no_grid=True)
        self.service_sheet.erase_cells('summary')
        metadata = self.get_layer_metadata()
        range = 'summary!A1:B8'
        update_body = {
            "range": range,
            "values": metadata,
        }
        print "update", self.service_sheet.service.spreadsheets().values().update(spreadsheetId=self.spreadsheet_id,range=range, body=update_body, valueInputOption='USER_ENTERED').execute()

        #merge cells to visualize snapshot and aaply image snapshot
        request_body = {
            'requests': [{
                'mergeCells': {
                    "range": {
                        "sheetId": summary_id,
                        "startRowIndex": 9,
                        "endRowIndex": 32,
                        "startColumnIndex": 0,
                        "endColumnIndex": 9,
                    },
                "mergeType": 'MERGE_ALL'
                }
            }]
        }
        print "merge", self.service_sheet.service.spreadsheets().batchUpdate(spreadsheetId=self.spreadsheet_id, body=request_body).execute()
        print "image", self.service_sheet.set_sheet_cell('summary!A10','=IMAGE("%s",3)' % webLink)

        permissions = self.service_drive.file_property(self.spreadsheet_id,'permissions')
        for permission in permissions:
            if permission['type'] == 'anyone':
                public = True
                break
            else:
                public = False
        if public:
            range = 'summary!A9:B9'
            update_body = {
                "range": range,
                "values": [['public link', "https://enricofer.github.io/GooGIS2CSV/converter.html?spreadsheet_id="+self.spreadsheet_id]]
            }
            print "update_public_link", self.service_sheet.service.spreadsheets().values().update(spreadsheetId=self.spreadsheet_id,range=range, body=update_body, valueInputOption='USER_ENTERED').execute()

        #hide worksheets except summary
        sheets = self.service_sheet.get_sheets()
        #self.service_sheet.toggle_sheet('summary', sheets['summary'], hidden=None)
        for sheet_name,sheet_id in sheets.iteritems():
            if not sheet_name == 'summary':
                print sheet_name, sheet_id
                self.service_sheet.toggle_sheet(sheet_name, sheet_id, hidden=True)