示例#1
0
    def calculate_layer_extent(self, layer):
        extent = QgsRectangle()

        features = layer.getFeatures()
        for feature in features:
            geometry = feature.geometry()
            if extent.isEmpty():
                extent = geometry.boundingBox()
            else:
                extent.combineExtentWith(geometry.boundingBox())

        if extent.isEmpty():
            extent = layer.extent()

        return extent
示例#2
0
 def calculate_layer_extent(self, layer):
     extent = QgsRectangle()
     
     features = layer.getFeatures()
     for feature in features:
         geometry = feature.geometry()
         if extent.isEmpty() :
             extent = geometry.boundingBox()
         else:
             extent.combineExtentWith(geometry.boundingBox())
             
     if extent.isEmpty() :
         extent = layer.extent()
         
     return extent
    def setDiffContent(self, commit, commit2):
        if self.layer is None:
            layers = set(self.server.layers(self.user, self.repo, commit.commitid))
            layers2 = set(self.server.layers(self.user, self.repo, commit2))
            layers = layers.union(layers2)
        else:
            layers = [self.layer]

        diffs = {layer: execute(lambda: self.server.diff(self.user, self.repo, layer, commit.commitid, commit2)) for layer in layers}
        diffs = {key:value for (key,value) in diffs.items() if len(value) !=0}
        layers = [l for l in diffs.keys()]
        self.diffViewer.setChanges(diffs)

        self.canvas.setLayers([])
        self.removeMapLayers()
        extent = QgsRectangle()
        for layer in layers:
            if not diffs[layer]:
                continue
            beforeLayer, afterLayer = execute(lambda: self._getLayers(diffs[layer]))
            if afterLayer is not None:
                resourcesPath =  os.path.join(os.path.dirname(__file__), os.pardir, "resources")
                oldStylePath = os.path.join(resourcesPath, "{}_before.qml".format(
                                            QgsWkbTypes.geometryDisplayString(beforeLayer.geometryType())))
                newStylePath = os.path.join(resourcesPath, "{}_after.qml".format(
                                            QgsWkbTypes.geometryDisplayString(afterLayer.geometryType())))

                beforeLayer.loadNamedStyle(oldStylePath)
                afterLayer.loadNamedStyle(newStylePath)

                QgsProject.instance().addMapLayer(beforeLayer, False)
                QgsProject.instance().addMapLayer(afterLayer, False)

                extent.combineExtentWith(beforeLayer.extent())
                extent.combineExtentWith(afterLayer.extent())
                self.extraLayers.append(beforeLayer)
                self.extraLayers.append(afterLayer)
        # make extent a bit bit (10%) bigger
        # this gives some margin around the dataset (not cut-off at edges)
        if not extent.isEmpty():
            widthDelta = extent.width() * 0.05
            heightDelta = extent.height() * 0.05
            extent = QgsRectangle(extent.xMinimum() - widthDelta,
                                  extent.yMinimum() - heightDelta,
                                  extent.xMaximum() + widthDelta,
                                  extent.yMaximum() + heightDelta)

        layers = self.extraLayers
        hasChanges = False
        for layer in layers:
            if layer is not None and layer.featureCount() > 0:
                hasChanges = True
                break
        self.canvas.setLayers(layers)
        self.canvas.setExtent(extent)
        self.canvas.refresh()
                
        self.canvas.setVisible(hasChanges)
        self.labelNoChanges.setVisible(not hasChanges)
示例#4
0
    def _populate_bookmarks_list(self):
        """Read the sqlite database and populate the bookmarks list.

        If no bookmarks are found, the bookmarks radio button will be disabled
        and the label will be shown indicating that the user should add
        bookmarks in QGIS first.

        Every bookmark are reprojected to mapcanvas crs.
        """
        # Connect to the QGIS sqlite database and check if the table exists.
        # noinspection PyArgumentList
        db_file_path = QgsApplication.qgisUserDatabaseFilePath()
        db = sqlite3.connect(db_file_path)
        cursor = db.cursor()
        cursor.execute(
            'SELECT COUNT(*) '
            'FROM sqlite_master '
            'WHERE type=\'table\' '
            'AND name=\'tbl_bookmarks\';')

        number_of_rows = cursor.fetchone()[0]
        if number_of_rows > 0:
            cursor.execute(
                'SELECT * '
                'FROM tbl_bookmarks;')
            bookmarks = cursor.fetchall()

            canvas_crs = self.canvas.mapSettings().destinationCrs()

            for bookmark in bookmarks:
                name = bookmark[1]
                srid = bookmark[7]
                rectangle = QgsRectangle(
                    bookmark[3], bookmark[4], bookmark[5], bookmark[6])

                if srid != canvas_crs.srsid():
                    transform = QgsCoordinateTransform(
                        QgsCoordinateReferenceSystem(srid),
                        canvas_crs,
                        QgsProject.instance()
                    )
                    try:
                        rectangle = transform.transform(rectangle)
                    except QgsCsException:
                        rectangle = QgsRectangle()

                if rectangle.isEmpty():
                    pass

                self.bookmarks_list.addItem(name, rectangle)
        if self.bookmarks_list.currentIndex() >= 0:
            self.create_bookmarks_label.hide()
        else:
            self.create_bookmarks_label.show()
            self.hazard_exposure_bookmark.setDisabled(True)
            self.bookmarks_list.hide()
示例#5
0
    def _populate_bookmarks_list(self):
        """Read the sqlite database and populate the bookmarks list.

        If no bookmarks are found, the bookmarks radio button will be disabled
        and the label will be shown indicating that the user should add
        bookmarks in QGIS first.

        Every bookmark are reprojected to mapcanvas crs.
        """
        # Connect to the QGIS sqlite database and check if the table exists.
        # noinspection PyArgumentList
        db_file_path = QgsApplication.qgisUserDatabaseFilePath()
        db = sqlite3.connect(db_file_path)
        cursor = db.cursor()
        cursor.execute(
            'SELECT COUNT(*) '
            'FROM sqlite_master '
            'WHERE type=\'table\' '
            'AND name=\'tbl_bookmarks\';')

        number_of_rows = cursor.fetchone()[0]
        if number_of_rows > 0:
            cursor.execute(
                'SELECT * '
                'FROM tbl_bookmarks;')
            bookmarks = cursor.fetchall()

            canvas_crs = self.canvas.mapSettings().destinationCrs()

            for bookmark in bookmarks:
                name = bookmark[1]
                srid = bookmark[7]
                rectangle = QgsRectangle(
                    bookmark[3], bookmark[4], bookmark[5], bookmark[6])

                if srid != canvas_crs.srsid():
                    transform = QgsCoordinateTransform(
                        QgsCoordinateReferenceSystem(srid),
                        canvas_crs,
                        QgsProject.instance()
                    )
                    try:
                        rectangle = transform.transform(rectangle)
                    except QgsCsException:
                        rectangle = QgsRectangle()

                if rectangle.isEmpty():
                    pass

                self.bookmarks_list.addItem(name, rectangle)
        if self.bookmarks_list.currentIndex() >= 0:
            self.create_bookmarks_label.hide()
        else:
            self.create_bookmarks_label.show()
            self.hazard_exposure_bookmark.setDisabled(True)
            self.bookmarks_list.hide()
示例#6
0
    def testCtor(self):
        rect = QgsRectangle(5.0, 5.0, 10.0, 10.0)

        myExpectedResult = True
        myResult = rect.isEmpty()
        myMessage = ('Expected: %s Got: %s' % (myExpectedResult, myResult))
        assert rect.isEmpty(), myMessage

        myMessage = ('Expected: %s\nGot: %s\n' % (5.0, rect.xMinimum()))
        assert rect.xMinimum() == 5.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' % (5.0, rect.yMinimum()))
        assert rect.yMinimum() == 5.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' % (10.0, rect.xMaximum()))
        assert rect.xMaximum() == 10.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' % (10.0, rect.yMaximum()))
        assert rect.yMaximum() == 10.0, myMessage
示例#7
0
    def testCtor(self):
        rect = QgsRectangle(5.0, 5.0, 10.0, 10.0)

        myExpectedResult = True
        myResult = rect.isEmpty()
        myMessage = "Expected: %s Got: %s" % (myExpectedResult, myResult)
        assert rect.isEmpty(), myMessage

        myMessage = "Expected: %s\nGot: %s\n" % (5.0, rect.xMinimum())
        assert rect.xMinimum() == 5.0, myMessage

        myMessage = "Expected: %s\nGot: %s\n" % (5.0, rect.yMinimum())
        assert rect.yMinimum() == 5.0, myMessage

        myMessage = "Expected: %s\nGot: %s\n" % (10.0, rect.xMaximum())
        assert rect.xMaximum() == 10.0, myMessage

        myMessage = "Expected: %s\nGot: %s\n" % (10.0, rect.yMaximum())
        assert rect.yMaximum() == 10.0, myMessage
示例#8
0
    def _populate_bookmarks_list(self):
        """Read the sqlite database and populate the bookmarks list.

        Every bookmark are reprojected to mapcanvas crs.
        """

        # Connect to the QGIS sqlite database and check if the table exists.
        db_file_path = QgsApplication.qgisUserDbFilePath()
        if not isfile(db_file_path):
            # If the database does not exist.
            return

        try:
            db = sqlite3.connect(db_file_path)
            cursor = db.cursor()
            cursor.execute(
                'SELECT COUNT(*) '
                'FROM sqlite_master '
                'WHERE type=\'table\' '
                'AND name=\'tbl_bookmarks\';')

            number_of_rows = cursor.fetchone()[0]
            if number_of_rows > 0:
                cursor.execute(
                    'SELECT * '
                    'FROM tbl_bookmarks;')
                bookmarks = cursor.fetchall()

                map_renderer = self.canvas.mapRenderer()
                canvas_srid = map_renderer.destinationCrs().srsid()

                for bookmark in bookmarks:
                    name = bookmark[1]
                    srid = bookmark[7]
                    rectangle = QgsRectangle(
                        bookmark[3], bookmark[4], bookmark[5], bookmark[6])

                    if srid != canvas_srid:
                        transform = QgsCoordinateTransform(
                            srid, canvas_srid)
                        rectangle = transform.transform(rectangle)

                    if rectangle.isEmpty():
                        pass

                    self.comboBox_bookmarks_list.addItem(name, rectangle)
        except sqlite3.Error:
            # If we have any SQL error with SQLite.
            return
    def zoom_to_selected_plots(self):
        plot_layer = self.utils._layers[self.utils._db.names.LC_PLOT_T]
        supplies_plot_layer = self.utils._supplies_layers[
            self.utils._supplies_db.names.GC_PLOT_T]
        bbox_selected_features = QgsRectangle()

        if plot_layer.selectedFeatureCount():
            bbox_selected_features.combineExtentWith(
                plot_layer.boundingBoxOfSelected())

        if supplies_plot_layer.selectedFeatureCount():
            bbox_selected_features.combineExtentWith(
                supplies_plot_layer.boundingBoxOfSelected())

        if not bbox_selected_features.isEmpty():
            self.utils.iface.mapCanvas().zoomToFeatureExtent(
                bbox_selected_features)
示例#10
0
    def testCtor(self):
        rect = QgsRectangle( 5.0, 5.0, 10.0, 10.0)

        assert rect.isEmpty(), "Empty rectangle constructed"

        myMessage = ('Expected: %s\nGot: %s\n' %
                      (5.0, rect.xMinimum()))
        assert rect.xMinimum() == 5.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' %
                      (5.0, rect.yMinimum()))
        assert rect.yMinimum() == 5.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' %
                      (10.0, rect.xMaximum()))
        assert rect.xMaximum() == 10.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' %
                      (10.0, rect.yMaximum()))
        assert rect.yMaximum() == 10.0, myMessage
示例#11
0
class PyProvider(QgsVectorDataProvider):

    next_feature_id = 1

    @classmethod
    def providerKey(cls):
        """Returns the memory provider key"""
        return 'pythonprovider'

    @classmethod
    def description(cls):
        """Returns the memory provider description"""
        return 'Python Test Provider'

    @classmethod
    def createProvider(cls, uri, providerOptions):
        return PyProvider(uri, providerOptions)

    # Implementation of functions from QgsVectorDataProvider

    def __init__(self, uri='', providerOptions=QgsDataProvider.ProviderOptions()):
        super().__init__(uri)
        # Use the memory layer to parse the uri
        mlayer = QgsVectorLayer(uri, 'ml', 'memory')
        self.setNativeTypes(mlayer.dataProvider().nativeTypes())
        self._uri = uri
        self._fields = mlayer.fields()
        self._wkbType = mlayer.wkbType()
        self._features = {}
        self._extent = QgsRectangle()
        self._extent.setMinimal()
        self._subset_string = ''
        self._crs = mlayer.crs()
        self._spatialindex = None
        self._provider_options = providerOptions
        if 'index=yes'in self._uri:
            self.createSpatialIndex()

    def featureSource(self):
        return PyFeatureSource(self)

    def dataSourceUri(self, expandAuthConfig=True):
        return self._uri

    def storageType(self):
        return "Python test memory storage"

    def getFeatures(self, request=QgsFeatureRequest()):
        return QgsFeatureIterator(PyFeatureIterator(PyFeatureSource(self), request))

    def uniqueValues(self, fieldIndex, limit=1):
        results = set()
        if fieldIndex >= 0 and fieldIndex < self.fields().count():
            req = QgsFeatureRequest()
            req.setFlags(QgsFeatureRequest.NoGeometry)
            req.setSubsetOfAttributes([fieldIndex])
            for f in self.getFeatures(req):
                results.add(f.attributes()[fieldIndex])
        return results

    def wkbType(self):
        return self._wkbType

    def featureCount(self):
        if not self.subsetString():
            return len(self._features)
        else:
            req = QgsFeatureRequest()
            req.setFlags(QgsFeatureRequest.NoGeometry)
            req.setSubsetOfAttributes([])
            return len([f for f in self.getFeatures(req)])

    def fields(self):
        return self._fields

    def addFeatures(self, flist, flags=None):
        added = False
        f_added = []
        for f in flist:
            if f.hasGeometry() and (f.geometry().wkbType() != self.wkbType()):
                return added, f_added

        for f in flist:
            _f = QgsFeature(self.fields())
            _f.setGeometry(f.geometry())
            attrs = [None for i in range(_f.fields().count())]
            for i in range(min(len(attrs), len(f.attributes()))):
                attrs[i] = f.attributes()[i]
            _f.setAttributes(attrs)
            _f.setId(self.next_feature_id)
            self._features[self.next_feature_id] = _f
            self.next_feature_id += 1
            added = True
            f_added.append(_f)

            if self._spatialindex is not None:
                self._spatialindex.insertFeature(_f)

        if len(f_added):
            self.clearMinMaxCache()
            self.updateExtents()

        return added, f_added

    def deleteFeatures(self, ids):
        if not ids:
            return True
        removed = False
        for id in ids:
            if id in self._features:
                if self._spatialindex is not None:
                    self._spatialindex.deleteFeature(self._features[id])
                del self._features[id]
                removed = True
        if removed:
            self.clearMinMaxCache()
            self.updateExtents()
        return removed

    def addAttributes(self, attrs):
        try:
            for new_f in attrs:
                if new_f.type() not in (QVariant.Int, QVariant.Double, QVariant.String, QVariant.Date, QVariant.Time, QVariant.DateTime, QVariant.LongLong, QVariant.StringList, QVariant.List):
                    continue
                self._fields.append(new_f)
                for f in self._features.values():
                    old_attrs = f.attributes()
                    old_attrs.append(None)
                    f.setAttributes(old_attrs)
            self.clearMinMaxCache()
            return True
        except Exception:
            return False

    def renameAttributes(self, renamedAttributes):
        result = True
        # We need to replace all fields because python bindings return a copy from [] and at()
        new_fields = [self._fields.at(i) for i in range(self._fields.count())]
        for fieldIndex, new_name in renamedAttributes.items():
            if fieldIndex < 0 or fieldIndex >= self._fields.count():
                result = False
                continue
            if self._fields.indexFromName(new_name) >= 0:
                #field name already in use
                result = False
                continue
            new_fields[fieldIndex].setName(new_name)
        if result:
            self._fields = QgsFields()
            for i in range(len(new_fields)):
                self._fields.append(new_fields[i])
        return result

    def deleteAttributes(self, attributes):
        attrIdx = sorted(attributes, reverse=True)

        # delete attributes one-by-one with decreasing index
        for idx in attrIdx:
            self._fields.remove(idx)
            for f in self._features.values():
                attr = f.attributes()
                del(attr[idx])
                f.setAttributes(attr)
        self.clearMinMaxCache()
        return True

    def changeAttributeValues(self, attr_map):
        for feature_id, attrs in attr_map.items():
            try:
                f = self._features[feature_id]
            except KeyError:
                continue
            for k, v in attrs.items():
                f.setAttribute(k, v)
        self.clearMinMaxCache()
        return True

    def changeGeometryValues(self, geometry_map):
        for feature_id, geometry in geometry_map.items():
            try:
                f = self._features[feature_id]
                f.setGeometry(geometry)
            except KeyError:
                continue
        self.updateExtents()
        return True

    def allFeatureIds(self):
        return list(self._features.keys())

    def subsetString(self):
        return self._subset_string

    def setSubsetString(self, subsetString):
        if subsetString == self._subset_string:
            return True
        self._subset_string = subsetString
        self.updateExtents()
        self.clearMinMaxCache()
        self.dataChanged.emit()
        return True

    def supportsSubsetString(self):
        return True

    def createSpatialIndex(self):
        if self._spatialindex is None:
            self._spatialindex = QgsSpatialIndex()
            for f in self._features.values():
                self._spatialindex.insertFeature(f)
        return True

    def capabilities(self):
        return QgsVectorDataProvider.AddFeatures | QgsVectorDataProvider.DeleteFeatures | QgsVectorDataProvider.CreateSpatialIndex | QgsVectorDataProvider.ChangeGeometries | QgsVectorDataProvider.ChangeAttributeValues | QgsVectorDataProvider.AddAttributes | QgsVectorDataProvider.DeleteAttributes | QgsVectorDataProvider.RenameAttributes | QgsVectorDataProvider.SelectAtId | QgsVectorDataProvider. CircularGeometries

    #/* Implementation of functions from QgsDataProvider */

    def name(self):
        return self.providerKey()

    def extent(self):
        if self._extent.isEmpty() and self._features:
            self._extent.setMinimal()
            if not self._subset_string:
                # fast way - iterate through all features
                for feat in self._features.values():
                    if feat.hasGeometry():
                        self._extent.combineExtentWith(feat.geometry().boundingBox())
            else:
                for f in self.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])):
                    if f.hasGeometry():
                        self._extent.combineExtentWith(f.geometry().boundingBox())

        elif not self._features:
            self._extent.setMinimal()
        return QgsRectangle(self._extent)

    def updateExtents(self):
        self._extent.setMinimal()

    def isValid(self):
        return True

    def crs(self):
        return self._crs
示例#12
0
class PyProvider(QgsVectorDataProvider):

    next_feature_id = 1

    @classmethod
    def providerKey(cls):
        """Returns the memory provider key"""
        return 'pythonprovider'

    @classmethod
    def description(cls):
        """Returns the memory provider description"""
        return 'Python Test Provider'

    @classmethod
    def createProvider(cls, uri, providerOptions):
        return PyProvider(uri, providerOptions)

    # Implementation of functions from QgsVectorDataProvider

    def __init__(self, uri='', providerOptions=QgsDataProvider.ProviderOptions()):
        super().__init__(uri)
        # Use the memory layer to parse the uri
        mlayer = QgsVectorLayer(uri, 'ml', 'memory')
        self.setNativeTypes(mlayer.dataProvider().nativeTypes())
        self._uri = uri
        self._fields = mlayer.fields()
        self._wkbType = mlayer.wkbType()
        self._features = {}
        self._extent = QgsRectangle()
        self._extent.setMinimal()
        self._subset_string = ''
        self._crs = mlayer.crs()
        self._spatialindex = None
        self._provider_options = providerOptions
        if 'index=yes'in self._uri:
            self.createSpatialIndex()

    def featureSource(self):
        return PyFeatureSource(self)

    def dataSourceUri(self, expandAuthConfig=True):
        return self._uri

    def storageType(self):
        return "Python test memory storage"

    def getFeatures(self, request=QgsFeatureRequest()):
        return QgsFeatureIterator(PyFeatureIterator(PyFeatureSource(self), request))

    def uniqueValues(self, fieldIndex, limit=1):
        results = set()
        if fieldIndex >= 0 and fieldIndex < self.fields().count():
            req = QgsFeatureRequest()
            req.setFlags(QgsFeatureRequest.NoGeometry)
            req.setSubsetOfAttributes([fieldIndex])
            for f in self.getFeatures(req):
                results.add(f.attributes()[fieldIndex])
        return results

    def wkbType(self):
        return self._wkbType

    def featureCount(self):
        if not self.subsetString():
            return len(self._features)
        else:
            req = QgsFeatureRequest()
            req.setFlags(QgsFeatureRequest.NoGeometry)
            req.setSubsetOfAttributes([])
            return len([f for f in self.getFeatures(req)])

    def fields(self):
        return self._fields

    def addFeatures(self, flist, flags=None):
        added = False
        f_added = []
        for f in flist:
            if f.hasGeometry() and (f.geometry().wkbType() != self.wkbType()):
                return added, f_added

        for f in flist:
            _f = QgsFeature(self.fields())
            _f.setGeometry(f.geometry())
            attrs = [None for i in range(_f.fields().count())]
            for i in range(min(len(attrs), len(f.attributes()))):
                attrs[i] = f.attributes()[i]
            _f.setAttributes(attrs)
            _f.setId(self.next_feature_id)
            self._features[self.next_feature_id] = _f
            self.next_feature_id += 1
            added = True
            f_added.append(_f)

            if self._spatialindex is not None:
                self._spatialindex.insertFeature(_f)

        if len(f_added):
            self.clearMinMaxCache()
            self.updateExtents()

        return added, f_added

    def deleteFeatures(self, ids):
        if not ids:
            return True
        removed = False
        for id in ids:
            if id in self._features:
                if self._spatialindex is not None:
                    self._spatialindex.deleteFeature(self._features[id])
                del self._features[id]
                removed = True
        if removed:
            self.clearMinMaxCache()
            self.updateExtents()
        return removed

    def addAttributes(self, attrs):
        try:
            for new_f in attrs:
                if new_f.type() not in (QVariant.Int, QVariant.Double, QVariant.String, QVariant.Date, QVariant.Time, QVariant.DateTime, QVariant.LongLong, QVariant.StringList, QVariant.List):
                    continue
                self._fields.append(new_f)
                for f in self._features.values():
                    old_attrs = f.attributes()
                    old_attrs.append(None)
                    f.setAttributes(old_attrs)
            self.clearMinMaxCache()
            return True
        except Exception:
            return False

    def renameAttributes(self, renamedAttributes):
        result = True
        # We need to replace all fields because python bindings return a copy from [] and at()
        new_fields = [self._fields.at(i) for i in range(self._fields.count())]
        for fieldIndex, new_name in renamedAttributes.items():
            if fieldIndex < 0 or fieldIndex >= self._fields.count():
                result = False
                continue
            if self._fields.indexFromName(new_name) >= 0:
                #field name already in use
                result = False
                continue
            new_fields[fieldIndex].setName(new_name)
        if result:
            self._fields = QgsFields()
            for i in range(len(new_fields)):
                self._fields.append(new_fields[i])
        return result

    def deleteAttributes(self, attributes):
        attrIdx = sorted(attributes, reverse=True)

        # delete attributes one-by-one with decreasing index
        for idx in attrIdx:
            self._fields.remove(idx)
            for f in self._features.values():
                attr = f.attributes()
                del(attr[idx])
                f.setAttributes(attr)
        self.clearMinMaxCache()
        return True

    def changeAttributeValues(self, attr_map):
        for feature_id, attrs in attr_map.items():
            try:
                f = self._features[feature_id]
            except KeyError:
                continue
            for k, v in attrs.items():
                f.setAttribute(k, v)
        self.clearMinMaxCache()
        return True

    def changeGeometryValues(self, geometry_map):
        for feature_id, geometry in geometry_map.items():
            try:
                f = self._features[feature_id]
                f.setGeometry(geometry)
            except KeyError:
                continue
        self.updateExtents()
        return True

    def allFeatureIds(self):
        return list(self._features.keys())

    def subsetString(self):
        return self._subset_string

    def setSubsetString(self, subsetString):
        if subsetString == self._subset_string:
            return True
        self._subset_string = subsetString
        self.updateExtents()
        self.clearMinMaxCache()
        self.dataChanged.emit()
        return True

    def supportsSubsetString(self):
        return True

    def createSpatialIndex(self):
        if self._spatialindex is None:
            self._spatialindex = QgsSpatialIndex()
            for f in self._features.values():
                self._spatialindex.insertFeature(f)
        return True

    def capabilities(self):
        return QgsVectorDataProvider.AddFeatures | QgsVectorDataProvider.DeleteFeatures | QgsVectorDataProvider.CreateSpatialIndex | QgsVectorDataProvider.ChangeGeometries | QgsVectorDataProvider.ChangeAttributeValues | QgsVectorDataProvider.AddAttributes | QgsVectorDataProvider.DeleteAttributes | QgsVectorDataProvider.RenameAttributes | QgsVectorDataProvider.SelectAtId | QgsVectorDataProvider. CircularGeometries

    #/* Implementation of functions from QgsDataProvider */

    def name(self):
        return self.providerKey()

    def extent(self):
        if self._extent.isEmpty() and self._features:
            self._extent.setMinimal()
            if not self._subset_string:
                # fast way - iterate through all features
                for feat in self._features.values():
                    if feat.hasGeometry():
                        self._extent.combineExtentWith(feat.geometry().boundingBox())
            else:
                for f in self.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])):
                    if f.hasGeometry():
                        self._extent.combineExtentWith(f.geometry().boundingBox())

        elif not self._features:
            self._extent.setMinimal()
        return QgsRectangle(self._extent)

    def updateExtents(self):
        self._extent.setMinimal()

    def isValid(self):
        return True

    def crs(self):
        return self._crs
示例#13
0
    def search_data(self, **kwargs):
        """
        Get plot geometries associated with parcels, both collected and supplies, zoom to them, fill comparison table
        and activate map swipe tool.

        To fill the comparison table we build two search dicts, one for supplies (already given because the alphanumeric
        search is on supplies db source), and another one for collected. For the latter, we have 3 cases. We specify
        them below (inline).

        :param kwargs: key-value (field name-field value) to search in parcel tables, both collected and supplies
                       Normally, keys are parcel_number, old_parcel_number or FMI, but if duplicates are found, an
                       additional t_id disambiguates only for the collected source. In the supplies source we assume
                       we will not find duplicates, if there are, we will choose the first record found an will not deal
                       with letting the user choose one of the duplicates by hand (as we do for the collected source).
        """
        self.chk_show_all_plots.setEnabled(False)
        self.chk_show_all_plots.setChecked(True)
        self.initialize_tools_and_layers()  # Reset any filter on layers

        plots_supplies = list()
        plots_collected = list()
        self.clear_result_table()

        search_option = self.cbo_parcel_fields.currentData()
        search_field_supplies = get_supplies_search_options(
            self.utils._supplies_db.names)[search_option]
        search_field_collected = get_collected_search_options(
            self.utils._db.names)[search_option]
        search_value = list(kwargs.values())[0]

        # Build search criterion for both supplies and collected
        search_criterion_supplies = {search_field_supplies: search_value}

        # Get supplies parcel's t_id and get related plot(s)
        expression_supplies = QgsExpression("{}='{}'".format(
            search_field_supplies, search_value))
        request = QgsFeatureRequest(expression_supplies)
        field_idx = self.utils._supplies_layers[
            self.utils._supplies_db.names.GC_PARCEL_T].fields().indexFromName(
                self.utils._supplies_db.names.T_ID_F)
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([field_idx
                                       ])  # Note: this adds a new flag
        supplies_parcels = [
            feature for feature in self.utils._supplies_layers[
                self.utils._supplies_db.names.GC_PARCEL_T].getFeatures(request)
        ]

        if len(supplies_parcels) > 1:
            # We do not expect duplicates in the supplies source!
            pass  # We'll choose the first one anyways
        elif len(supplies_parcels) == 0:
            self.logger.info(
                __name__, "No supplies parcel found! Search: {}={}".format(
                    search_field_supplies, search_value))

        supplies_plot_t_ids = []
        if supplies_parcels:
            supplies_plot_t_ids = self.utils.ladm_data.get_plots_related_to_parcels_supplies(
                self.utils._supplies_db,
                [supplies_parcels[0][self.utils._supplies_db.names.T_ID_F]],
                self.utils._supplies_db.names.T_ID_F,
                self.utils._supplies_layers[
                    self.utils._supplies_db.names.GC_PLOT_T])

            if supplies_plot_t_ids:
                self._current_supplies_substring = "\"{}\" IN ('{}')".format(
                    self.utils._supplies_db.names.T_ID_F,
                    "','".join([str(t_id) for t_id in supplies_plot_t_ids]))
                plots_supplies = self.utils.ladm_data.get_features_from_t_ids(
                    self.utils._supplies_layers[
                        self.utils._supplies_db.names.GC_PLOT_T],
                    self.utils._supplies_db.names.T_ID_F, supplies_plot_t_ids,
                    True)

        # Now get COLLECTED parcel's t_id to build the search dict for collected
        collected_parcel_t_id = None
        if 'collected_parcel_t_id' in kwargs:
            # This is the case when this panel is called and we already know the parcel number is duplicated
            collected_parcel_t_id = kwargs['collected_parcel_t_id']
            search_criterion_collected = {
                self.utils._db.names.T_ID_F: collected_parcel_t_id
            }  # As there are duplicates, we need to use t_ids
        else:
            # This is the case when:
            #   + Either this panel was called and we know the parcel number is not duplicated, or
            #   + This panel was shown without knowing about duplicates (e.g., individual parcel search) and we still
            #     need to discover whether we have duplicates for this search criterion
            search_criterion_collected = {search_field_collected: search_value}

            expression_collected = QgsExpression("{}='{}'".format(
                search_field_collected, search_value))
            request = QgsFeatureRequest(expression_collected)
            request.setFlags(QgsFeatureRequest.NoGeometry)
            request.setSubsetOfAttributes(
                [self.utils._db.names.T_ID_F],
                self.utils._layers[self.utils._db.names.LC_PARCEL_T].fields(
                ))  # Note this adds a new flag
            collected_parcels = self.utils._layers[
                self.utils._db.names.LC_PARCEL_T].getFeatures(request)
            collected_parcels_t_ids = [
                feature[self.utils._db.names.T_ID_F]
                for feature in collected_parcels
            ]

            if collected_parcels_t_ids:
                collected_parcel_t_id = collected_parcels_t_ids[0]
                if len(collected_parcels_t_ids
                       ) > 1:  # Duplicates in collected source after a search
                    QApplication.restoreOverrideCursor(
                    )  # Make sure cursor is not waiting (it is if on an identify)
                    QCoreApplication.processEvents()
                    dlg_select_parcel = SelectDuplicateParcelDialog(
                        self.utils, collected_parcels_t_ids, self.parent)
                    dlg_select_parcel.exec_()

                    if dlg_select_parcel.parcel_t_id:  # User selected one of the duplicated parcels
                        collected_parcel_t_id = dlg_select_parcel.parcel_t_id
                        search_criterion_collected = {
                            self.utils._db.names.T_ID_F: collected_parcel_t_id
                        }
                    else:
                        return  # User just cancelled the dialog, there is nothing more to do

        self.fill_table(search_criterion_supplies, search_criterion_collected)

        # Now get related plot(s) for both collected and supplies,
        if collected_parcel_t_id is not None:
            plot_t_ids = self.utils.ladm_data.get_plots_related_to_parcels(
                self.utils._db, [collected_parcel_t_id],
                self.utils._db.names.T_ID_F,
                plot_layer=self.utils._layers[self.utils._db.names.LC_PLOT_T],
                uebaunit_table=self.utils._layers[
                    self.utils._db.names.COL_UE_BAUNIT_T])

            if plot_t_ids:
                self._current_substring = "{} IN ('{}')".format(
                    self.utils._db.names.T_ID_F,
                    "','".join([str(t_id) for t_id in plot_t_ids]))
                plots_collected = self.utils.ladm_data.get_features_from_t_ids(
                    self.utils._layers[self.utils._db.names.LC_PLOT_T],
                    self.utils._db.names.T_ID_F, plot_t_ids, True)

        # Zoom to combined extent
        plot_features = plots_supplies + plots_collected  # Feature list
        plots_extent = QgsRectangle()
        for plot in plot_features:
            plots_extent.combineExtentWith(plot.geometry().boundingBox())

        if not plots_extent.isEmpty():
            self.utils.iface.mapCanvas().zoomToFeatureExtent(plots_extent)

            if plots_supplies and plots_collected:  # Otherwise the map swipe tool doesn't add any value :)
                # Activate Swipe Tool
                self.utils.app.gui.activate_layer(self.utils._supplies_layers[
                    self.utils._supplies_db.names.GC_PLOT_T])
                self.parent.activate_map_swipe_tool()

                # Send a custom mouse move on the map to make the map swipe tool's limit appear on the canvas
                coord_x = plots_extent.xMaximum() - (plots_extent.xMaximum(
                ) - plots_extent.xMinimum()) / 9  # 90%
                coord_y = plots_extent.yMaximum() - (plots_extent.yMaximum(
                ) - plots_extent.yMinimum()) / 2  # 50%

                coord_transform = self.utils.iface.mapCanvas(
                ).getCoordinateTransform()
                map_point = coord_transform.transform(coord_x, coord_y)
                widget_point = map_point.toQPointF().toPoint()
                global_point = self.utils.canvas.mapToGlobal(widget_point)

                self.utils.canvas.mousePressEvent(
                    QMouseEvent(QEvent.MouseButtonPress, global_point,
                                Qt.LeftButton, Qt.LeftButton, Qt.NoModifier))
                self.utils.canvas.mouseMoveEvent(
                    QMouseEvent(QEvent.MouseMove, widget_point + QPoint(1, 0),
                                Qt.NoButton, Qt.LeftButton, Qt.NoModifier))
                self.utils.canvas.mouseReleaseEvent(
                    QMouseEvent(QEvent.MouseButtonRelease,
                                widget_point + QPoint(1, 0), Qt.LeftButton,
                                Qt.LeftButton, Qt.NoModifier))

        # Once the query is done, activate the checkbox to alternate all plots/only selected plot
        self.chk_show_all_plots.setEnabled(True)
示例#14
0
class DjangoProvider(QgsVectorDataProvider):

    @classmethod
    def providerKey(cls):
        return 'django'

    @classmethod
    def description(cls):
        return 'Django vector provider'

    @classmethod
    def createProvider(cls, uri, providerOptions):
        return DjangoProvider(uri, providerOptions)

    def __init__(self, uri='', providerOptions=QgsDataProvider.ProviderOptions()):
        """
        :param uri: <app>.<model>[?geofield=<name>]
        :param providerOptions:
        """
        super().__init__(uri)
        self._is_valid = False
        self.setNativeTypes((
            # TODO
            QgsVectorDataProvider.NativeType('Integer', 'integer', QVariant.Int, -1, -1, 0, 0),
            QgsVectorDataProvider.NativeType('Text', 'text', QVariant.String, -1, -1, -1, -1),
        ))
        self._uri = uri
        url = QUrl(uri)
        url_query = QUrlQuery(url)
        self._full_model_name = url.path()
        self._app_label, self._model_name = self._full_model_name.split('.')
        self._model = apps.get_model(self._app_label, self._model_name)  # Django model
        self._meta = self._model._meta

        self._qgis_fields = QgsFields()
        self._django_fields = []  # Django fields represented by provider in the same order as QgsFields
        for django_field in self._meta.get_fields():
            # TODO: more field types
            qgis_field = self._get_qgis_field_from_django_field(django_field)

            if qgis_field:
                self._qgis_fields.append(qgis_field)
                self._django_fields.append(django_field)

        self._geo_field_name = url_query.queryItemValue('geofield')
        self._geo_field = None  # Django geometry field
        if self._geo_field_name:
            self._meta.get_field(self._geo_field_name)
        else:
            # If geometry field was not specified in uri, use the first one if any.
            for field in self._meta.get_fields():
                if isinstance(field, models.GeometryField):
                    self._geo_field = field
                    self._geo_field_name = field.name
                    break

        self._wkbType = QgsWkbTypes.NoGeometry
        if self._geo_field:
            for geo_field_class in wkb_types.keys():
                if isinstance(self._geo_field, geo_field_class):
                    self._wkbType = wkb_types[geo_field_class]
                    break

        self._extent = QgsRectangle()
        self._crs = None
        if self._geo_field:
            self._crs = QgsCoordinateReferenceSystem.fromEpsgId(self._geo_field.srid)
        self._provider_options = providerOptions
        self._is_valid = True

    def featureSource(self):
        return DjangoFeatureSource(self._model, self._qgis_fields, self._django_fields, self._geo_field, self._crs)

    def dataSourceUri(self, expandAuthConfig=True):
        return self._uri

    def storageType(self):
        return "Django"

    def getFeatures(self, request=QgsFeatureRequest()):
        return QgsFeatureIterator(self.featureSource().getFeatures(request))

    def uniqueValues(self, fieldIndex, limit=-1):
        if fieldIndex < 0 or fieldIndex >= self.fields().count():
            return set()

        dj_field = self._django_fields[fieldIndex]
        values = self._model.objects.get_queryset().order_by(dj_field.name).values_list(dj_field.name, flat=True).distinct()
        if limit >= 0:
            values = values[:limit]
        return set(values)

    def wkbType(self):
        return self._wkbType

    def featureCount(self):
        return self._model.objects.get_queryset().count()

    def fields(self):
        return self._qgis_fields

    def addFeatures(self, features, flags=None):
        # TODO
        return False

    def deleteFeatures(self, ids):
        # TODO
        return False

    def addAttributes(self, attrs):
        return False

    def renameAttributes(self, renamedAttributes):
        return False

    def deleteAttributes(self, attributes):
        return False

    def changeAttributeValues(self, attr_map):
        # TODO
        for feature_id, attrs in attr_map.items():
            pass
        self.clearMinMaxCache()
        return True

    def changeGeometryValues(self, geometry_map):
        # TODO
        for feature_id, geometry in geometry_map.items():
            pass
        self.updateExtents()
        return True

    def allFeatureIds(self):
        return list(self._model.objects.get_queryset().values_list(self._meta.pk.name, flat=True))

    def subsetString(self):
        return None

    def setSubsetString(self, subsetString):
        return False

    def supportsSubsetString(self):
        return False

    def createSpatialIndex(self):
        return False

    def capabilities(self):
        # TODO: QgsVectorDataProvider.AddFeatures | QgsVectorDataProvider.DeleteFeatures | QgsVectorDataProvider.ChangeGeometries | QgsVectorDataProvider.ChangeAttributeValues
        # TODO: TransactionSupport
        return QgsVectorDataProvider.SelectAtId

    # ---------------------------- functions from QgsDataProvider ----------------------------

    def name(self):
        return self.providerKey()

    def extent(self):
        # TODO
        return QgsRectangle(-20037508.34, -20037508.34, 20037508.34, 20037508.34)
        if self._extent.isEmpty() and self._geo_field:
            box = list(self._model.objects.get_queryset().aggregate(models.Extent(self._geo_field_name)).values())[0]
            self._extent = QgsRectangle(box[0], box[0], box[0], box[0])

        return QgsRectangle(self._extent)

    def updateExtents(self):
        self._extent.setMinimal()

    def isValid(self):
        return self._is_valid

    def crs(self):
        return self._crs

    # -------------------------------- Private methods --------------------------------

    def _get_django_field(self, field_index):
        return self._django_fields[field_index]

    @staticmethod
    def _get_qgis_field_from_django_field(django_field):
        # IS it OK to take class name?
        name = django_field.name
        type_name = type(django_field).__name__.replace('Field', '').lower()
        comment = django_field.verbose_name
        # boolean
        if isinstance(django_field, models.BooleanField):
            return QgsField(name, QVariant.Bool, type_name, -1, -1, comment)
        elif isinstance(django_field, models.NullBooleanField):
            return QgsField(name, QVariant.Bool, type_name, -1, -1, comment)
        # integer
        elif isinstance(django_field, models.SmallIntegerField):
            return QgsField(name, QVariant.Int, type_name, -1, 0, comment)
        elif isinstance(django_field, models.IntegerField):
            return QgsField(name, QVariant.Int, type_name, -1, 0, comment)
        elif isinstance(django_field, models.BigIntegerField):
            return QgsField(name, QVariant.LongLong, type_name, -1, 0, comment)
        # float
        elif isinstance(django_field, models.FloatField):
            return QgsField(name, QVariant.Double, type_name, -1, -1, comment)
        elif isinstance(django_field, models.DecimalField):
            return QgsField(name, QVariant.Double, type_name, django_field.max_digits, django_field.decimal_places, comment)
        # char
        elif isinstance(django_field, models.CharField):
            return QgsField(name, QVariant.String, type_name, django_field.max_length, -1, comment)
        elif isinstance(django_field, models.TextField):
            return QgsField(name, QVariant.String, type_name, -1, -1, comment)
        # datetime
        elif isinstance(django_field, models.DateField):
            return QgsField(name, QVariant.Date, type_name, -1, -1, comment)
        elif isinstance(django_field, models.TimeField):
            return QgsField(name, QVariant.Time, type_name, -1, -1, comment)
        elif isinstance(django_field, models.DateTimeField):
            return QgsField(name, QVariant.DateTime, type_name, -1, -1, comment)

        return None
class ROSVectorProvider(QgsVectorDataProvider):

    fieldsUpdated = pyqtSignal()

    @classmethod
    def providerKey(cls):
        return 'rosvectorprovider'

    @classmethod
    def description(cls):
        return 'ROS Vector Provider'

    @classmethod
    def createProvider(cls, uri, providerOptions):
        return ROSVectorProvider(uri, providerOptions)

    def __init__(self,
                 uri='',
                 providerOptions=QgsDataProvider.ProviderOptions()):
        '''Set up the Data Provider.

        uri contains the topic name, topic type.
        Example uri: 'foo/my_pose?type=geometry_msgs/PoseStamped'
        '''
        try:
            self._topic, argString = uri.split('?')
        except IndexError:
            raise ValueError(
                'uri Cannot be parsed. Is it valid? uri: {}'.format(uri))

        # Parse string of arguments into dict of python types.
        args = parseUrlArgs(argString)
        super().__init__(uri)

        self._translator = TranslatorRegistry.instance().get(args['type'])

        # There's no source for a reasonable collection of native types so we just steal from another provider.
        mlayer = QgsVectorLayer('Polygon?crs=epsg:4326', 'ml',
                                'memory')  # Wrong url but doesn't matter.
        nativeTypes = mlayer.dataProvider().nativeTypes()
        self.setNativeTypes(nativeTypes)

        self._uri = uri
        self._fields = QgsFields()
        self._wkbType = self._translator.geomType  # TODO: if unknown, infer it.
        self._features = {}
        self._extent = QgsRectangle()
        self._extent.setMinimal()
        self._subset_string = ''
        self._spatialindex = None
        self._provider_options = providerOptions
        self.next_feature_id = 0  # TODO: Consider a generator for a better numbering approach.
        self._lock = RLock()
        self._subscriber = None

        self.keepOlderMessages = args.get('keepOlderMessages', False)
        self._handledMessageCount = 0
        self.sampleInterval = int(args.get('sampleInterval', 1))

        if args.get('index'):
            self.createSpatialIndex()

        if args.get('subscribe'):
            self._subscriber = rospy.Subscriber(self._topic,
                                                self._translator.messageType,
                                                self._handleMessage)
        else:
            msg = rospy.wait_for_message(self._topic,
                                         self._translator.messageType,
                                         timeout=5)
            self._handleMessage(msg)

    def _handleMessage(self, msg):
        self._handledMessageCount += 1
        # Skip message if not on interval.
        if (self._handledMessageCount - 1
            ) % self.sampleInterval:  # -1 so that first message is handled.
            return

        features = self._translator.translate(msg)
        qgsFeatures, fields = featuresToQgs(features)

        # TODO: check if fields changed and emit a signal only then.
        self._fields = fields
        self.fieldsUpdated.emit()

        # If we're not accumulating history, clear features first.
        if not self.keepOlderMessages:
            self.next_feature_id = 0
            self._features = {}  # Clear features.

        try:
            self._setFeatures(qgsFeatures)
        except RuntimeError:
            self._cleanup()

        # Throttle data update to avoid over-stressing QGIS runtime. Consider alleviating this.
        global last_global_refresh
        now = rospy.get_time()
        if now - last_global_refresh > DATA_UPDATE_THROTTLE:
            last_global_refresh = now
            self.dataChanged.emit(
            )  # TODO: remove occasional flicker when this happens.

    def _cleanup(self):
        ''' Clean up ROS subscriber connection.
        The provider is owned by the QgsVectorLayer, which is owned by QGIS internals (layer registry)
        When a layer is removed, it gets deleted and cleaned up. However, there's no clean way to perform
        cleanup activities on the Python side before the underlying C++ object is deleted, thus making this
        object unstable.  A RuntimeError is raised when this is detected. We'll perform cleanup at that point.
        '''
        if self._subscriber:
            self._subscriber.unregister()
            self._subscriber = None

    def featureSource(self):
        with self._lock:
            return ROSVectorFeatureSource(self)

    def dataSourceUri(self, expandAuthConfig=True):
        return self._uri

    def storageType(self):
        return "ROS Topic"

    def getFeatures(self, request=QgsFeatureRequest()):
        with self._lock:
            return QgsFeatureIterator(
                ROSVectorFeatureIterator(ROSVectorFeatureSource(self),
                                         request))

    def uniqueValues(self, fieldIndex, limit=1):
        with self._lock:
            results = set()
            if fieldIndex >= 0 and fieldIndex < self._fields.count():
                req = QgsFeatureRequest()
                req.setFlags(QgsFeatureRequest.NoGeometry)
                req.setSubsetOfAttributes([fieldIndex])
                for f in self.getFeatures(req):
                    results.add(f.attributes()[fieldIndex])
            return results

    def wkbType(self):
        return self._wkbType

    def featureCount(self):
        with self._lock:
            if not self.subsetString():
                return len(self._features)
            else:
                req = QgsFeatureRequest()
                req.setFlags(QgsFeatureRequest.NoGeometry)
                req.setSubsetOfAttributes([])
                return len([f for f in self.getFeatures(req)])

    def fields(self):
        with self._lock:
            return self._fields

    def _setFeatures(self, flist, flags=None):
        with self._lock:
            added = False
            f_added = []

            if self._spatialindex is not None:
                for f in self._features.values():
                    self._spatialindex.deleteFeature(f)

            for _f in flist:
                self._features[self.next_feature_id] = _f
                _f.setId(self.next_feature_id)
                self.next_feature_id += 1
                added = True
                f_added.append(_f)

            if self._spatialindex is not None:
                self._spatialindex.insertFeature(_f)

            if f_added:
                self.clearMinMaxCache()
                self.updateExtents()

            return added, f_added

    def allFeatureIds(self):
        with self._lock:
            return list(self._features.keys())

    def subsetString(self):
        return self._subset_string

    def setSubsetString(self, subsetString):
        if subsetString == self._subset_string:
            return True
        self._subset_string = subsetString
        self.updateExtents()
        self.clearMinMaxCache()
        self.dataChanged.emit()
        return True

    def supportsSubsetString(self):
        return True

    def createSpatialIndex(self):
        if self._spatialindex is None:
            self._spatialindex = QgsSpatialIndex()
            for f in self._features.values():
                self._spatialindex.insertFeature(f)
        return True

    def capabilities(self):
        return QgsVectorDataProvider.SelectAtId | QgsVectorDataProvider.CreateSpatialIndex

    def name(self):
        return self.providerKey()

    def extent(self):
        if self._extent.isEmpty() and self._features:
            self._extent.setMinimal()
            if not self._subset_string:
                # fast way - iterate through all features
                for feat in self._features.values():
                    if feat.hasGeometry():
                        self._extent.combineExtentWith(
                            feat.geometry().boundingBox())
            else:
                for f in self.getFeatures(
                        QgsFeatureRequest().setSubsetOfAttributes([])):
                    if f.hasGeometry():
                        self._extent.combineExtentWith(
                            f.geometry().boundingBox())

        elif not self._features:
            self._extent.setMinimal()
        return QgsRectangle(self._extent)

    def updateExtents(self):
        self._extent.setMinimal()

    def isValid(self):
        return True

    def crs(self):
        return simpleCrs