Example #1
0
    def initRotation(self, boundBox):
        # calculate rotation parameters..interpreted from affine transformation plugin

        anchorPoint = boundBox.center()
        # We convert the angle from degree to radiant
        rad = self.angle.value() * math.pi / 180.0

        a = math.cos(rad)
        b = -1 * math.sin(rad)
        c = anchorPoint.x() - math.cos(rad) * anchorPoint.x() + math.sin(rad) * anchorPoint.y()
        d = math.sin(rad)
        e = math.cos(rad)
        f = anchorPoint.y() - math.sin(rad) * anchorPoint.x() - math.cos(rad) * anchorPoint.y()

        self.rotationParams = (a, b, c, d, e, f)

        # Rotate the bounding box to set a new extent
        ptMin = QgsPoint(boundBox.xMinimum(), boundBox.yMinimum())
        ptMax = QgsPoint(boundBox.xMaximum(), boundBox.yMaximum())

        self.rotatePoint(ptMin)
        self.rotatePoint(ptMax)

        newBoundBox = QgsRectangle(ptMin, ptMax)
        newBoundBox.combineExtentWith(boundBox)

        return newBoundBox
 def currentHistoryChanged(self, current, previous):
     self.new_geometry.reset()
     self.old_geometry.reset()
     self.tblChanges.model().removeRows()
     if not current.isValid():
         return
     item = current.data(Qt.UserRole)
     if item is None:
         data = {}
     else:
         data = current.data(Qt.UserRole).getDetails()
     with SetLocale_CtxDec():
         extent = None
         if data.get('new_geometry'):
             wkt = CreateGeometryFromJson( json.dumps(data['new_geometry']) ).ExportToWkt()
             geom = QgsGeometry.fromWkt( wkt )
             l = QgsVectorLayer('Point?crs=epsg:4326', 'asd', 'memory')
             self.new_geometry.setToGeometry( geom, l )
             extent = QgsRectangle(geom.boundingBox())
         if data.get('old_geometry'):
             wkt = CreateGeometryFromJson( json.dumps(data['old_geometry']) ).ExportToWkt()
             geom = QgsGeometry.fromWkt( wkt )
             l = QgsVectorLayer('Point?crs=epsg:4326', 'asd', 'memory')
             self.old_geometry.setToGeometry( geom, l )
             if extent is None:
                 extent = QgsRectangle(geom.boundingBox())
             else:
                 extent.combineExtentWith( geom.boundingBox() )
         if extent is not None:
             extent.grow(0.01)
             self.mapCanvas.setExtent( extent )
             self.mapCanvas.refresh()
     if data.get('what_attributes', []):
         self.tblChanges.model().insertRows( 0, data.get('what_attributes', []) )
    def testRectangle(self):
        rect = QgsReferencedRectangle(QgsRectangle(0.0, 1.0, 20.0, 10.0), QgsCoordinateReferenceSystem('epsg:3111'))
        self.assertEqual(rect.xMinimum(), 0.0)
        self.assertEqual(rect.yMinimum(), 1.0)
        self.assertEqual(rect.xMaximum(), 20.0)
        self.assertEqual(rect.yMaximum(), 10.0)
        self.assertEqual(rect.crs().authid(), 'EPSG:3111')

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

        # in variant
        v = QVariant(QgsReferencedRectangle(QgsRectangle(1.0, 2.0, 3.0, 4.0), QgsCoordinateReferenceSystem('epsg:3111')))
        self.assertEqual(v.value().xMinimum(), 1.0)
        self.assertEqual(v.value().yMinimum(), 2.0)
        self.assertEqual(v.value().xMaximum(), 3.0)
        self.assertEqual(v.value().yMaximum(), 4.0)
        self.assertEqual(v.value().crs().authid(), 'EPSG:3111')

        # to rectangle
        r = QgsRectangle(rect)
        self.assertEqual(r.xMinimum(), 0.0)
        self.assertEqual(r.yMinimum(), 1.0)
        self.assertEqual(r.xMaximum(), 20.0)
        self.assertEqual(r.yMaximum(), 10.0)

        # test that QgsReferencedRectangle IS a QgsRectangle
        r2 = QgsRectangle(5, 6, 30, 40)
        r2.combineExtentWith(rect)
        self.assertEqual(r2.xMinimum(), 0.0)
        self.assertEqual(r2.yMinimum(), 1.0)
        self.assertEqual(r2.xMaximum(), 30.0)
        self.assertEqual(r2.yMaximum(), 40.0)
    def testUnion(self):
        rect1 = QgsRectangle( 0.0, 0.0, 5.0, 5.0)
        rect2 = QgsRectangle( 2.0, 2.0, 7.0, 7.0)
        pnt1 = QgsPoint(6.0, 2.0)

        rect1.combineExtentWith(rect2)
        myMessage = ('Expected: %s\nGot: %s\n' %
                      (True, rect1.contains(rect2)))
        assert rect1.contains(rect2), myMessage

        print rect1.toString()
        assert rect1 == QgsRectangle(0.0, 0.0, 7.0, 7.0), "Wrong combine with rectangle result"

        rect1 = QgsRectangle( 0.0, 0.0, 5.0, 5.0)
        rect1.combineExtentWith(6.0, 2.0)
        myMessage = ('Expected: %s\nGot: %s\n' %
                      (True, rect1.contains(pnt1)))
        assert rect1.contains(pnt1), myMessage

        myExpectedResult = QgsRectangle(0.0, 0.0, 6.0, 5.0).toString()
        myResult = rect1.toString()
        myMessage = ('Expected: %s\nGot: %s\n' %
                      (myExpectedResult, myResult))
        self.assertEquals(myResult, myExpectedResult, myMessage)

        rect1 = QgsRectangle( 0.0, 0.0, 5.0, 5.0)
        rect1.unionRect(rect2)
        myMessage = ('Expected: %s\nGot: %s\n' %
                      (True, rect1.contains(rect2)))
        assert rect1.contains(rect2), myMessage

        assert rect1 == QgsRectangle(0.0, 0.0, 7.0, 7.0), "Wrong union result"
Example #5
0
    def processAlgorithm(self, parameters, context, feedback):
        expression = self.getParameterValue(self.EXPRESSION)
        layersValue = self.getParameterValue(self.LAYERS)
        layersDict = {}
        if layersValue:
            layers = [QgsProcessingUtils.mapLayerFromString(f, context) for f in layersValue.split(";")]
            layersDict = {os.path.basename(lyr.source().split(".")[0]): lyr for lyr in layers}

        for lyr in QgsProcessingUtils.compatibleRasterLayers(context.project()):
            name = lyr.name()
            if (name + "@") in expression:
                layersDict[name] = lyr

        entries = []
        for name, lyr in layersDict.items():
            for n in range(lyr.bandCount()):
                entry = QgsRasterCalculatorEntry()
                entry.ref = '{:s}@{:d}'.format(name, n + 1)
                entry.raster = lyr
                entry.bandNumber = n + 1
                entries.append(entry)

        output = self.getOutputValue(self.OUTPUT)
        extentValue = self.getParameterValue(self.EXTENT)
        if not extentValue:
            extentValue = QgsProcessingUtils.combineLayerExtents(layersValue)

        if extentValue:
            extent = extentValue.split(',')
            bbox = QgsRectangle(float(extent[0]), float(extent[2]),
                                float(extent[1]), float(extent[3]))
        else:
            if layersDict:
                bbox = list(layersDict.values())[0].extent()
                for lyr in layersDict.values():
                    bbox.combineExtentWith(lyr.extent())
            else:
                raise GeoAlgorithmExecutionException(self.tr("No layers selected"))

        def _cellsize(layer):
            return (layer.extent().xMaximum() - layer.extent().xMinimum()) / layer.width()
        cellsize = self.getParameterValue(self.CELLSIZE) or min([_cellsize(lyr) for lyr in layersDict.values()])
        width = math.floor((bbox.xMaximum() - bbox.xMinimum()) / cellsize)
        height = math.floor((bbox.yMaximum() - bbox.yMinimum()) / cellsize)
        driverName = GdalUtils.getFormatShortNameFromFilename(output)
        calc = QgsRasterCalculator(expression,
                                   output,
                                   driverName,
                                   bbox,
                                   width,
                                   height,
                                   entries)

        res = calc.processCalculation()
        if res == QgsRasterCalculator.ParserError:
            raise GeoAlgorithmExecutionException(self.tr("Error parsing formula"))
Example #6
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
Example #7
0
def bounds(iface, useCanvas, layers, matchCRS):
    if useCanvas:
        canvas = iface.mapCanvas()
        canvasCrs = canvas.mapSettings().destinationCrs()
        if not matchCRS:
            epsg3857 = QgsCoordinateReferenceSystem("EPSG:3857")
            try:
                transform = QgsCoordinateTransform(canvasCrs, epsg3857,
                                                   QgsProject.instance())
            except:
                transform = QgsCoordinateTransform(canvasCrs, epsg3857)
            try:
                extent = transform.transformBoundingBox(canvas.extent())
            except QgsCsException:
                extent = QgsRectangle(-20026376.39, -20048966.10,
                                      20026376.39, 20048966.10)
        else:
            extent = canvas.extent()
    else:
        extent = None
        for layer in layers:
            if not matchCRS:
                epsg3857 = QgsCoordinateReferenceSystem("EPSG:3857")
                try:
                    transform = QgsCoordinateTransform(layer.crs(), epsg3857,
                                                       QgsProject.instance())
                except:
                    transform = QgsCoordinateTransform(layer.crs(), epsg3857)
                try:
                    layerExtent = transform.transformBoundingBox(
                        layer.extent())
                except QgsCsException:
                    layerExtent = QgsRectangle(-20026376.39, -20048966.10,
                                               20026376.39, 20048966.10)
            else:
                layerExtent = layer.extent()
            if extent is None:
                extent = layerExtent
            else:
                extent.combineExtentWith(layerExtent)

    if extent is None:
        extent = QgsRectangle(-20026376.39, -20048966.10,
                              20026376.39, 20048966.10)

    return "[%f, %f, %f, %f]" % (extent.xMinimum(), extent.yMinimum(),
                                 extent.xMaximum(), extent.yMaximum())
Example #8
0
    def on_zoomButton_clicked(self):
        """
        Slot used to zoom the mapcanvas to the features associated to a complex
        """
        #case no item is selected we should warn the user
        if len(self.treeWidget.selectedItems()) == 0:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Please, select an item to zoom."))
            return

        item = self.treeWidget.selectedItems()[0]
        #checking if the item is a complex (it should have depth = 2)
        if self.depth(item) == 2:
            bbox = QgsRectangle()
            for i in range(item.childCount()):
                aggregated_item = item.child(i)
                aggregated_class = aggregated_item.text(0)
                #getting the layer the needs to be updated
                aggregated_layer = None
                layers = self.iface.mapCanvas().layers()
                for layer in layers:
                    if layer.name() == aggregated_class:
                        aggregated_layer = layer
                        break

                if not aggregated_layer:
                    QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("The associated classes must be loaded in the table of contents."))
                    return

                for j in range(aggregated_item.childCount()):
                    id = aggregated_item.child(j).text(0)
                    freq = QgsFeatureRequest()
                    freq.setFilterFid(int(id))
                    feature = layer.getFeatures( freq ).next()
                    if j==0 and i == 0:
                        bbox=feature.geometry().boundingBox()
                    bbox.combineExtentWith(feature.geometry().boundingBox())

            self.iface.mapCanvas().setExtent(bbox)
            self.iface.mapCanvas().refresh()
        else:
            QMessageBox.warning(self.iface.mainWindow(), self.tr("Warning!"), self.tr("Select a complex."))
            return
Example #9
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
Example #10
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

    Render using qgis composer for a given impact_report data and component
    context.

    :param impact_report: ImpactReport contains data about the report that is
        going to be generated.
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component: Contains the component metadata and context for
        rendering the output.
    :type component:
        safe.report.report_metadata.QgisComposerComponentsMetadata

    :return: Whatever type of output the component should be.

    .. versionadded:: 4.0
    """
    context = component.context
    qgis_composition_context = impact_report.qgis_composition_context

    # load composition object
    layout = QgsPrintLayout(QgsProject.instance())

    # load template
    main_template_folder = impact_report.metadata.template_folder

    # we do this condition in case custom template was found
    if component.template.startswith('../qgis-composer-templates/'):
        template_path = os.path.join(main_template_folder, component.template)
    else:
        template_path = component.template

    with open(template_path) as template_file:
        template_content = template_file.read()

    document = QtXml.QDomDocument()

    # Replace
    for k, v in context.substitution_map.items():
        template_content = template_content.replace('[{}]'.format(k), v)

    document.setContent(template_content)

    rwcontext = QgsReadWriteContext()
    load_status = layout.loadFromTemplate(
        document, rwcontext)

    if not load_status:
        raise TemplateLoadingError(
            tr('Error loading template: %s') % template_path)

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = layout_item(layout, item_id, QgsLayoutItemPicture)
        if image and path:
            image.setPicturePath(path)

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        html_element = layout_item(layout, item_id, QgsLayoutItemHtml)
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsLayoutItemHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsLayoutItemHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    original_crs = impact_report.impact_function.crs
    destination_crs = qgis_composition_context.map_settings.destinationCrs()
    coord_transform = QgsCoordinateTransform(original_crs,
                                             destination_crs,
                                             QgsProject.instance())

    # resize map extent
    for map_el in context.map_elements:
        item_id = map_el.get('id')
        split_count = map_el.get('grid_split_count')
        layers = [
            layer for layer in map_el.get('layers') if isinstance(
                layer, QgsMapLayer)
        ]
        map_extent_option = map_el.get('extent')
        composer_map = layout_item(layout, item_id, QgsLayoutItemMap)

        for index, layer in enumerate(layers):
            # we need to check whether the layer is registered or not
            registered_layer = (
                QgsProject.instance().mapLayer(layer.id()))
            if registered_layer:
                if not registered_layer == layer:
                    layers[index] = registered_layer
            else:
                QgsProject.instance().addMapLayer(layer)

        """:type: qgis.core.QgsLayoutItemMap"""
        if composer_map:

            # Search for specified map extent in the template.
            min_x = composer_map.extent().xMinimum() if (
                impact_report.use_template_extent) else None
            min_y = composer_map.extent().yMinimum() if (
                impact_report.use_template_extent) else None
            max_x = composer_map.extent().xMaximum() if (
                impact_report.use_template_extent) else None
            max_y = composer_map.extent().yMaximum() if (
                impact_report.use_template_extent) else None

            composer_map.setKeepLayerSet(True)
            layer_set = [l for l in layers if isinstance(l, QgsMapLayer)]
            composer_map.setLayers(layer_set)
            map_overview_extent = None
            if map_extent_option and isinstance(
                    map_extent_option, QgsRectangle):
                # use provided map extent
                extent = coord_transform.transform(map_extent_option)
                for l in [layer for layer in layers if
                          isinstance(layer, QgsMapLayer)]:
                    layer_extent = coord_transform.transform(l.extent())
                    if l.name() == map_overview['id']:
                        map_overview_extent = layer_extent
            else:
                # if map extent not provided, try to calculate extent
                # from list of given layers. Combine it so all layers were
                # shown properly
                extent = QgsRectangle()
                extent.setMinimal()
                for l in [layer for layer in layers if
                          isinstance(layer, QgsMapLayer)]:
                    # combine extent if different layer is provided.
                    layer_extent = coord_transform.transform(l.extent())
                    extent.combineExtentWith(layer_extent)
                    if l.name() == map_overview['id']:
                        map_overview_extent = layer_extent

            width = extent.width()
            height = extent.height()
            longest_width = width if width > height else height
            half_length = longest_width / 2
            margin = half_length / 5
            center = extent.center()
            min_x = min_x or (center.x() - half_length - margin)
            max_x = max_x or (center.x() + half_length + margin)
            min_y = min_y or (center.y() - half_length - margin)
            max_y = max_y or (center.y() + half_length + margin)

            # noinspection PyCallingNonCallable
            square_extent = QgsRectangle(min_x, min_y, max_x, max_y)

            if component.key == 'population-infographic' and (
                    map_overview_extent):
                square_extent = map_overview_extent

            composer_map.zoomToExtent(square_extent)
            composer_map.invalidateCache()

            actual_extent = composer_map.extent()

            # calculate intervals for grid
            x_interval = actual_extent.width() / split_count
            composer_map.grid().setIntervalX(x_interval)
            y_interval = actual_extent.height() / split_count
            composer_map.grid().setIntervalY(y_interval)

    # calculate legend element
    for leg_el in context.map_legends:
        item_id = leg_el.get('id')
        title = leg_el.get('title')
        layers = [
            layer for layer in leg_el.get('layers') if isinstance(
                layer, QgsMapLayer)
        ]
        symbol_count = leg_el.get('symbol_count')
        column_count = leg_el.get('column_count')

        legend = layout_item(layout, item_id, QgsLayoutItemLegend)
        """:type: qgis.core.QgsLayoutItemLegend"""
        if legend:
            # set column count
            if column_count:
                legend.setColumnCount(column_count)
            elif symbol_count <= 7:
                legend.setColumnCount(1)
            else:
                legend.setColumnCount(symbol_count / 7 + 1)

            # set legend title
            if title is not None and not impact_report.legend_layers:
                legend.setTitle(title)

            # set legend
            root_group = legend.model().rootGroup()
            for layer in layers:
                # we need to check whether the layer is registered or not
                registered_layer = (
                    QgsProject.instance().mapLayer(layer.id()))
                if registered_layer:
                    if not registered_layer == layer:
                        layer = registered_layer
                else:
                    QgsProject.instance().addMapLayer(layer)
                # used for customizations
                tree_layer = root_group.addLayer(layer)
                if impact_report.legend_layers or (
                        not impact_report.multi_exposure_impact_function):
                    QgsLegendRenderer.setNodeLegendStyle(
                        tree_layer, QgsLegendStyle.Hidden)
            legend.adjustBoxSize()
            legend.updateFilterByMap(False)

    # process to output

    # in case output folder not specified
    if impact_report.output_folder is None:
        impact_report.output_folder = mkdtemp(dir=temp_dir())

    output_format = component.output_format
    component_output_path = impact_report.component_absolute_output_path(
        component.key)
    component_output = None

    doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT
    template_format = QgisComposerComponentsMetadata.OutputFormat.QPT
    if isinstance(output_format, list):
        component_output = []
        for i in range(len(output_format)):
            each_format = output_format[i]
            each_path = component_output_path[i]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    impact_report,
                    each_path,
                    layout,
                    each_format,
                    component)
                component_output.append(result_path)
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, layout)
                component_output.append(result_path)
    elif isinstance(output_format, dict):
        component_output = {}
        for key, each_format in list(output_format.items()):
            each_path = component_output_path[key]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    impact_report,
                    each_path,
                    layout,
                    each_format,
                    component)
                component_output[key] = result_path
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, layout)
                component_output[key] = result_path
    elif (output_format in
            QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT):
        component_output = None

        if output_format in doc_format:
            result_path = create_qgis_pdf_output(
                impact_report,
                component_output_path,
                layout,
                output_format,
                component)
            component_output = result_path
        elif output_format == template_format:
            result_path = create_qgis_template_output(
                component_output_path, layout)
            component_output = result_path

    component.output = component_output

    return component.output
Example #11
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)
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
Example #13
0
 def init_layers(self, scenario):
     '''
     initialize the layers in layer-window for given scenario
     including adding groups and background-layers and setting the
     editability of the institutional layers
     '''
     if not scenario:
         return
     scen_group = get_group(scenario.name, add_at_index=0)
     # just for the right initial order
     get_group('Filter', scen_group)
     cat_group = get_group('Einrichtungen', scen_group)
     get_group('Erreichbarkeiten PKW', scen_group)
     get_group(u'Erreichbarkeiten ÖPNV')
     border_group = get_group('Verwaltungsgrenzen')
     self.add_wms_background_map(group=get_group('Hintergrundkarte',
                                                 add_at_index=-1))
     self.add_xml_background_map(GOOGLE_XML,
                                 group=get_group('Hintergrundkarte'),
                                 visible=False)
     
     for name, tablename in [('Gemeinden', 'gemeinden_20161231'),
                             ('Verwaltungsgemeinschaften', 'vwg_20161231'),
                             ('Kreise', 'kreis_20161231')]:
         border_style = self.borders[name]
         symbology = SimpleFillSymbology(border_style=border_style)
         self.add_db_layer(name, 'verwaltungsgrenzen', tablename,
                           'geom', group=border_group, visible=False,
                           symbology=symbology)
     
     self.canvas.refresh()
     
     ### SET THE EDITABILITY OF INSTITUTIONAL LAYER-FIELDS###
 
     columns = ['spalte', 'editierbar', 'nur_auswahl_zulassen',
                'auswahlmoeglichkeiten', 'alias', 'auto_vervollst',
                'typ', 'min', 'max']
     for category, filter_tree in self.categories.iteritems():
         table = filter_tree.tablename
         symbology = SimpleSymbology(self.colors[category])
         layer = self.add_db_layer(category, SCHEMA, table, 'geom_gk',
                                   symbology, group=cat_group, zoom=False,
                                   where='szenario_id={}'.format(scenario.id))
         rows = get_values('editierbare_spalten', columns,
                           self.db_conn, schema='einrichtungen',
                           where="tabelle='{}'".format(table),
                           order_by='reihenfolge')
         editable_columns = [r.spalte for r in rows]
         #if not rows:
             #continue
         for i, f in enumerate(layer.fields()):
             if f.name() == 'szenario_id':
                 layer.setEditorWidgetV2(i, 'Hidden')
                 layer.setDefaultValueExpression(i, str(scenario.id))
                 continue
             try:
                 idx = editable_columns.index(f.name())
             except:
                 layer.setEditorWidgetV2(i, 'Hidden')
                 continue
             col, is_ed, is_sel, selections, alias, auto_complete, typ, min_value, max_value = rows[idx]
             if alias:
                 layer.addAttributeAlias(i, alias) 
             if not is_ed:
                 layer.setEditorWidgetV2(i, 'Hidden')
                 continue
             # type range (integers)
             if typ == 'range':
                 layer.setEditorWidgetV2(i, 'Range')
                 layer.setEditorWidgetV2Config(
                     i, {'AllowNull': False,
                         'Min': min_value, 'Max': max_value})
             # auto complete: take all existing unique values of field,
             # text will can be auto completed to one of those in UI
             if auto_complete:
                 layer.setEditorWidgetV2(i, 'UniqueValues')
                 layer.setEditorWidgetV2Config(i, {u'Editable': True})
             # selectable values are predefined in database
             elif is_sel and selections:
                 layer.setEditorWidgetV2(i, 'ValueMap')
                 sel = []
                 for s in selections:
                     try:
                         s = s.decode('utf-8')
                     except:
                         pass
                     sel.append(s)
                 d = dict([(s, s) for s in sel])
                 layer.setEditorWidgetV2Config(i, d)
             elif is_sel:
                 layer.setEditorWidgetV2(i, 'UniqueValues')
     # zoom to extent
     extent = QgsRectangle()
     extent.setMinimal()
     for child in cat_group.children():
         if isinstance(child, QgsLayerTreeLayer):
             #print child.layer().extent()
             extent.combineExtentWith(child.layer().extent())
     self.canvas.setExtent(extent)
     self.canvas.refresh()
from qgis.core import QgsRasterLayer, QgsRectangle

os.chdir(Select_directory)
rlist = []
extent = QgsRectangle()
extent.setMinimal()
n = len(glob.glob("*.tif"))

for raster in glob.glob("*.tif"):
    fileInfo = QFileInfo(raster)
    baseName = fileInfo.baseName()
    rlayer = QgsRasterLayer(raster, baseName)
    # Combine raster layers to list
    rlist.append(rlayer)
    # Combine raster extents
    extent.combineExtentWith(rlayer.extent())

# Get extent
xmin = extent.xMinimum()
xmax = extent.xMaximum()
ymin = extent.yMinimum()
ymax = extent.yMaximum()
# Run algorithm and set relevant parameters

mean = processing.runalg(
    "grass7:r.series",
    {
        "input": rlist,
        "-n": False,
        "method": 0,
        "range": "-10000000000,10000000000",
Example #15
0
    def processAlgorithm(self, context, feedback):
        expression = self.getParameterValue(self.EXPRESSION)
        layersValue = self.getParameterValue(self.LAYERS)
        layersDict = {}
        if layersValue:
            layers = [
                dataobjects.getLayerFromString(f)
                for f in layersValue.split(";")
            ]
            layersDict = {
                os.path.basename(lyr.source().split(".")[0]): lyr
                for lyr in layers
            }

        for lyr in QgsProcessingUtils.compatibleRasterLayers(
                QgsProject.instance()):
            name = lyr.name()
            if (name + "@") in expression:
                layersDict[name] = lyr

        entries = []
        for name, lyr in layersDict.items():
            for n in range(lyr.bandCount()):
                entry = QgsRasterCalculatorEntry()
                entry.ref = '{:s}@{:d}'.format(name, n + 1)
                entry.raster = lyr
                entry.bandNumber = n + 1
                entries.append(entry)

        output = self.getOutputValue(self.OUTPUT)
        extentValue = self.getParameterValue(self.EXTENT)

        if extentValue:
            extent = extentValue.split(',')
            bbox = QgsRectangle(float(extent[0]), float(extent[2]),
                                float(extent[1]), float(extent[3]))
        else:
            if layersDict:
                bbox = list(layersDict.values())[0].extent()
                for lyr in layersDict.values():
                    bbox.combineExtentWith(lyr.extent())
            else:
                raise GeoAlgorithmExecutionException(
                    self.tr("No layers selected"))

        def _cellsize(layer):
            return (layer.extent().xMaximum() -
                    layer.extent().xMinimum()) / layer.width()

        cellsize = self.getParameterValue(self.CELLSIZE) or min(
            [_cellsize(lyr) for lyr in layersDict.values()])
        width = math.floor((bbox.xMaximum() - bbox.xMinimum()) / cellsize)
        height = math.floor((bbox.yMaximum() - bbox.yMinimum()) / cellsize)
        driverName = GdalUtils.getFormatShortNameFromFilename(output)
        calc = QgsRasterCalculator(expression, output, driverName, bbox, width,
                                   height, entries)

        res = calc.processCalculation()
        if res == QgsRasterCalculator.ParserError:
            raise GeoAlgorithmExecutionException(
                self.tr("Error parsing formula"))
Example #16
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

    Render using qgis composer for a given impact_report data and component
    context

    :param impact_report: ImpactReport contains data about the report that is
        going to be generated
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component: Contains the component metadata and context for
        rendering the output
    :type component:
        safe.report.report_metadata.QgisComposerComponentsMetadata

    :return: whatever type of output the component should be

    .. versionadded:: 4.0
    """
    context = component.context
    """:type: safe.report.extractors.composer.QGISComposerContext"""
    qgis_composition_context = impact_report.qgis_composition_context
    inasafe_context = impact_report.inasafe_context

    # load composition object
    composition = QgsComposition(qgis_composition_context.map_settings)

    # load template
    main_template_folder = impact_report.metadata.template_folder
    template_path = os.path.join(main_template_folder, component.template)

    with open(template_path) as template_file:
        template_content = template_file.read()

    document = QtXml.QDomDocument()
    document.setContent(template_content)

    load_status = composition.loadFromTemplate(
        document, context.substitution_map)

    if not load_status:
        raise TemplateLoadingError(
            tr('Error loading template: %s') % template_path)

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerPicture"""
        if image is not None and path is not None:
            try:
                image.setPicturePath(path)
            except:
                pass

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        html_element = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerHtml"""
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsComposerHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsComposerHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    # resize map extent
    for map_el in context.map_elements:
        item_id = map_el.get('id')
        split_count = map_el.get('grid_split_count')
        layers = map_el.get('layers')
        map_extent_option = map_el.get('extent')
        composer_map = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerMap"""
        if isinstance(composer_map, QgsComposerMap):
            composer_map.setKeepLayerSet(True)
            layer_set = [l.id() for l in layers if isinstance(l, QgsMapLayer)]
            composer_map.setLayerSet(layer_set)
            if map_extent_option and isinstance(
                    map_extent_option, QgsRectangle):
                # use provided map extent
                extent = map_extent_option
            else:
                # if map extent not provided, try to calculate extent
                # from list of given layers. Combine it so all layers were
                # shown properly
                extent = QgsRectangle()
                extent.setMinimal()
                for l in layers:
                    # combine extent if different layer is provided.
                    extent.combineExtentWith(l.extent())

            width = extent.width()
            height = extent.height()
            longest_width = width if width > height else height
            half_length = longest_width / 2
            margin = half_length / 5
            center = extent.center()
            min_x = center.x() - half_length - margin
            max_x = center.x() + half_length + margin
            min_y = center.y() - half_length - margin
            max_y = center.y() + half_length + margin

            # noinspection PyCallingNonCallable
            square_extent = QgsRectangle(min_x, min_y, max_x, max_y)

            composer_map.zoomToExtent(square_extent)
            composer_map.renderModeUpdateCachedImage()

            actual_extent = composer_map.extent()

            # calculate intervals for grid
            x_interval = actual_extent.width() / split_count
            composer_map.grid().setIntervalX(x_interval)
            y_interval = actual_extent.height() / split_count
            composer_map.grid().setIntervalY(y_interval)

    # calculate legend element
    for leg_el in context.map_legends:
        item_id = leg_el.get('id')
        title = leg_el.get('title')
        layers = leg_el.get('layers')
        symbol_count = leg_el.get('symbol_count')
        column_count = leg_el.get('column_count')

        legend = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerLegend"""
        if isinstance(legend, QgsComposerLegend):
            # set column count
            if column_count:
                legend.setColumnCount(column_count)
            elif symbol_count <= 5:
                legend.setColumnCount(1)
            else:
                legend.setColumnCount(symbol_count / 5 + 1)

            # set legend title
            if title is not None:
                legend.setTitle(title)

            # set legend
            root_group = legend.modelV2().rootGroup()
            for l in layers:
                # used for customizations
                tree_layer = root_group.addLayer(l)
                QgsLegendRenderer.setNodeLegendStyle(
                    tree_layer, QgsComposerLegendStyle.Hidden)
            legend.synchronizeWithModel()

    # process to output

    # in case output folder not specified
    if impact_report.output_folder is None:
        impact_report.output_folder = mkdtemp(dir=temp_dir())

    output_format = component.output_format
    component_output_path = impact_report.component_absolute_output_path(
        component.key)
    component_output = None

    doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT
    template_format = QgisComposerComponentsMetadata.OutputFormat.QPT
    if isinstance(output_format, list):
        component_output = []
        for i in range(len(output_format)):
            each_format = output_format[i]
            each_path = component_output_path[i]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    each_path,
                    composition,
                    impact_report.qgis_composition_context,
                    each_format,
                    component)
                component_output.append(result_path)
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, composition)
                component_output.append(result_path)
    elif isinstance(output_format, dict):
        component_output = {}
        for key, each_format in output_format.iteritems():
            each_path = component_output_path[key]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    each_path,
                    composition,
                    impact_report.qgis_composition_context,
                    each_format,
                    component)
                component_output[key] = result_path
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, composition)
                component_output[key] = result_path
    elif (output_format in
            QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT):
        component_output = None

        if output_format in doc_format:
            result_path = create_qgis_pdf_output(
                component_output_path,
                composition,
                impact_report.qgis_composition_context,
                output_format,
                component)
            component_output = result_path
        elif output_format == template_format:
            result_path = create_qgis_template_output(
                component_output_path, composition)
            component_output = result_path

    component.output = component_output

    return component.output
Example #17
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

    Render using qgis composer for a given impact_report data and component
    context.

    :param impact_report: ImpactReport contains data about the report that is
        going to be generated.
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component: Contains the component metadata and context for
        rendering the output.
    :type component:
        safe.report.report_metadata.QgisComposerComponentsMetadata

    :return: Whatever type of output the component should be.

    .. versionadded:: 4.0
    """
    context = component.context
    """:type: safe.report.extractors.composer.QGISComposerContext"""
    qgis_composition_context = impact_report.qgis_composition_context
    inasafe_context = impact_report.inasafe_context

    # load composition object
    composition = QgsComposition(qgis_composition_context.map_settings)

    # load template
    main_template_folder = impact_report.metadata.template_folder

    # we do this condition in case custom template was found
    if component.template.startswith('../qgis-composer-templates/'):
        template_path = os.path.join(main_template_folder, component.template)
    else:
        template_path = component.template

    with open(template_path) as template_file:
        template_content = template_file.read()

    document = QtXml.QDomDocument()
    document.setContent(template_content)

    load_status = composition.loadFromTemplate(document,
                                               context.substitution_map)

    if not load_status:
        raise TemplateLoadingError(
            tr('Error loading template: %s') % template_path)

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = composition_item(composition, item_id, QgsComposerPicture)
        """:type: qgis.core.QgsComposerPicture"""
        if image and path:
            image.setPicturePath(path)

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        composer_item = composition.getComposerItemById(item_id)
        try:
            html_element = composition.getComposerHtmlByItem(composer_item)
        except:
            pass
        """:type: qgis.core.QgsComposerHtml"""
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsComposerHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsComposerHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    original_crs = impact_report.impact_function.impact.crs()
    destination_crs = qgis_composition_context.map_settings.destinationCrs()
    coord_transform = QgsCoordinateTransform(original_crs, destination_crs)

    # resize map extent
    for map_el in context.map_elements:
        item_id = map_el.get('id')
        split_count = map_el.get('grid_split_count')
        layers = map_el.get('layers')
        map_extent_option = map_el.get('extent')
        composer_map = composition_item(composition, item_id, QgsComposerMap)
        """:type: qgis.core.QgsComposerMap"""
        if composer_map:
            composer_map.setKeepLayerSet(True)
            layer_set = [l.id() for l in layers if isinstance(l, QgsMapLayer)]
            composer_map.setLayerSet(layer_set)
            if map_extent_option and isinstance(map_extent_option,
                                                QgsRectangle):
                # use provided map extent
                extent = coord_transform.transform(map_extent_option)
                for l in [
                        layer for layer in layers
                        if isinstance(layer, QgsMapLayer)
                ]:
                    layer_extent = coord_transform.transform(l.extent())
                    if l.name() == map_overview['id']:
                        map_overview_extent = layer_extent
            else:
                # if map extent not provided, try to calculate extent
                # from list of given layers. Combine it so all layers were
                # shown properly
                map_overview_extent = None
                extent = QgsRectangle()
                extent.setMinimal()
                for l in layers:
                    # combine extent if different layer is provided.
                    layer_extent = coord_transform.transform(l.extent())
                    extent.combineExtentWith(layer_extent)
                    if l.name() == map_overview['id']:
                        map_overview_extent = layer_extent

            width = extent.width()
            height = extent.height()
            longest_width = width if width > height else height
            half_length = longest_width / 2
            margin = half_length / 5
            center = extent.center()
            min_x = center.x() - half_length - margin
            max_x = center.x() + half_length + margin
            min_y = center.y() - half_length - margin
            max_y = center.y() + half_length + margin

            # noinspection PyCallingNonCallable
            square_extent = QgsRectangle(min_x, min_y, max_x, max_y)

            if component.key == 'population-infographic' and (
                    map_overview_extent):
                square_extent = map_overview_extent

            composer_map.zoomToExtent(square_extent)
            composer_map.renderModeUpdateCachedImage()

            actual_extent = composer_map.extent()

            # calculate intervals for grid
            x_interval = actual_extent.width() / split_count
            composer_map.grid().setIntervalX(x_interval)
            y_interval = actual_extent.height() / split_count
            composer_map.grid().setIntervalY(y_interval)

    # calculate legend element
    for leg_el in context.map_legends:
        item_id = leg_el.get('id')
        title = leg_el.get('title')
        layers = leg_el.get('layers')
        symbol_count = leg_el.get('symbol_count')
        column_count = leg_el.get('column_count')

        legend = composition_item(composition, item_id, QgsComposerLegend)
        """:type: qgis.core.QgsComposerLegend"""
        if legend:
            # set column count
            if column_count:
                legend.setColumnCount(column_count)
            elif symbol_count <= 7:
                legend.setColumnCount(1)
            else:
                legend.setColumnCount(symbol_count / 7 + 1)

            # set legend title
            if title is not None:
                legend.setTitle(title)

            # set legend
            root_group = legend.modelV2().rootGroup()
            for l in layers:
                # used for customizations
                tree_layer = root_group.addLayer(l)
                QgsLegendRenderer.setNodeLegendStyle(
                    tree_layer, QgsComposerLegendStyle.Hidden)
            legend.synchronizeWithModel()

    # process to output

    # in case output folder not specified
    if impact_report.output_folder is None:
        impact_report.output_folder = mkdtemp(dir=temp_dir())

    output_format = component.output_format
    component_output_path = impact_report.component_absolute_output_path(
        component.key)
    component_output = None

    doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT
    template_format = QgisComposerComponentsMetadata.OutputFormat.QPT
    if isinstance(output_format, list):
        component_output = []
        for i in range(len(output_format)):
            each_format = output_format[i]
            each_path = component_output_path[i]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(impact_report, each_path,
                                                     composition, each_format,
                                                     component)
                component_output.append(result_path)
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, composition)
                component_output.append(result_path)
    elif isinstance(output_format, dict):
        component_output = {}
        for key, each_format in output_format.iteritems():
            each_path = component_output_path[key]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(impact_report, each_path,
                                                     composition, each_format,
                                                     component)
                component_output[key] = result_path
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, composition)
                component_output[key] = result_path
    elif (output_format
          in QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT):
        component_output = None

        if output_format in doc_format:
            result_path = create_qgis_pdf_output(impact_report,
                                                 component_output_path,
                                                 composition, output_format,
                                                 component)
            component_output = result_path
        elif output_format == template_format:
            result_path = create_qgis_template_output(component_output_path,
                                                      composition)
            component_output = result_path

    component.output = component_output

    return component.output
Example #18
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
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        field_name = self.parameterAsString(parameters, self.FIELD, context)
        type = self.parameterAsEnum(parameters, self.TYPE, context)
        use_field = bool(field_name)

        field_index = -1

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

        if use_field:
            # keep original field type, name and parameters
            field_index = source.fields().lookupField(field_name)
            if field_index >= 0:
                fields.append(source.fields()[field_index])
        if type == 0:
            # envelope
            fields.append(QgsField('width', QVariant.Double, '', 20, 6))
            fields.append(QgsField('height', QVariant.Double, '', 20, 6))
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
            fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))
        elif type == 1:
            # oriented rect
            fields.append(QgsField('width', QVariant.Double, '', 20, 6))
            fields.append(QgsField('height', QVariant.Double, '', 20, 6))
            fields.append(QgsField('angle', QVariant.Double, '', 20, 6))
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
            fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))
        elif type == 2:
            # circle
            fields.append(QgsField('radius', QVariant.Double, '', 20, 6))
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
        elif type == 3:
            # convex hull
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
            fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields,
                                               QgsWkbTypes.Polygon,
                                               source.sourceCrs())

        if field_index >= 0:
            geometry_dict = {}
            bounds_dict = {}
            total = 50.0 / source.featureCount() if source.featureCount(
            ) else 1
            features = source.getFeatures(
                QgsFeatureRequest().setSubsetOfAttributes([field_index]))
            for current, f in enumerate(features):
                if feedback.isCanceled():
                    break

                if not f.hasGeometry():
                    continue

                if type == 0:
                    # bounding boxes - calculate on the fly for efficiency
                    if not f.attributes()[field_index] in bounds_dict:
                        bounds_dict[f.attributes()[field_index]] = f.geometry(
                        ).boundingBox()
                    else:
                        bounds_dict[f.attributes()
                                    [field_index]].combineExtentWith(
                                        f.geometry().boundingBox())
                else:
                    if not f.attributes()[field_index] in geometry_dict:
                        geometry_dict[f.attributes()[field_index]] = [
                            f.geometry()
                        ]
                    else:
                        geometry_dict[f.attributes()[field_index]].append(
                            f.geometry())

                feedback.setProgress(int(current * total))

            if type == 0:
                # bounding boxes
                current = 0
                total = 50.0 / len(bounds_dict) if bounds_dict else 1
                for group, rect in bounds_dict.items():
                    if feedback.isCanceled():
                        break

                    # envelope
                    feature = QgsFeature()
                    feature.setGeometry(QgsGeometry.fromRect(rect))
                    feature.setAttributes([
                        current, group,
                        rect.width(),
                        rect.height(),
                        rect.area(),
                        rect.perimeter()
                    ])
                    sink.addFeature(feature, QgsFeatureSink.FastInsert)
                    geometry_dict[group] = None

                    feedback.setProgress(50 + int(current * total))
                    current += 1
            else:
                current = 0
                total = 50.0 / len(geometry_dict) if geometry_dict else 1

                for group, geometries in geometry_dict.items():
                    if feedback.isCanceled():
                        break

                    feature = self.createFeature(feedback, current, type,
                                                 geometries, group)
                    sink.addFeature(feature, QgsFeatureSink.FastInsert)
                    geometry_dict[group] = None

                    feedback.setProgress(50 + int(current * total))
                    current += 1
        else:
            total = 80.0 / source.featureCount() if source.featureCount(
            ) else 1
            features = source.getFeatures(
                QgsFeatureRequest().setSubsetOfAttributes([]))
            geometry_queue = []
            bounds = QgsRectangle()
            for current, f in enumerate(features):
                if feedback.isCanceled():
                    break

                if not f.hasGeometry():
                    continue

                if type == 0:
                    # bounding boxes, calculate on the fly for efficiency
                    bounds.combineExtentWith(f.geometry().boundingBox())
                else:
                    geometry_queue.append(f.geometry())
                feedback.setProgress(int(current * total))

            if not feedback.isCanceled():
                if type == 0:
                    feature = QgsFeature()
                    feature.setGeometry(QgsGeometry.fromRect(bounds))
                    feature.setAttributes([
                        0,
                        bounds.width(),
                        bounds.height(),
                        bounds.area(),
                        bounds.perimeter()
                    ])
                else:
                    feature = self.createFeature(feedback, 0, type,
                                                 geometry_queue)
                sink.addFeature(feature, QgsFeatureSink.FastInsert)

        return {self.OUTPUT: dest_id}
Example #20
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        field_name = self.parameterAsString(parameters, self.FIELD, context)
        type = self.parameterAsEnum(parameters, self.TYPE, context)
        use_field = bool(field_name)

        field_index = -1

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

        if use_field:
            # keep original field type, name and parameters
            field_index = source.fields().lookupField(field_name)
            if field_index >= 0:
                fields.append(source.fields()[field_index])
        if type == 0:
            # envelope
            fields.append(QgsField('width', QVariant.Double, '', 20, 6))
            fields.append(QgsField('height', QVariant.Double, '', 20, 6))
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
            fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))
        elif type == 1:
            # oriented rect
            fields.append(QgsField('width', QVariant.Double, '', 20, 6))
            fields.append(QgsField('height', QVariant.Double, '', 20, 6))
            fields.append(QgsField('angle', QVariant.Double, '', 20, 6))
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
            fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))
        elif type == 2:
            # circle
            fields.append(QgsField('radius', QVariant.Double, '', 20, 6))
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
        elif type == 3:
            # convex hull
            fields.append(QgsField('area', QVariant.Double, '', 20, 6))
            fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))

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

        if field_index >= 0:
            geometry_dict = {}
            bounds_dict = {}
            total = 50.0 / source.featureCount() if source.featureCount() else 1
            features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index]))
            for current, f in enumerate(features):
                if feedback.isCanceled():
                    break

                if not f.hasGeometry():
                    continue

                if type == 0:
                    # bounding boxes - calculate on the fly for efficiency
                    if not f.attributes()[field_index] in bounds_dict:
                        bounds_dict[f.attributes()[field_index]] = f.geometry().boundingBox()
                    else:
                        bounds_dict[f.attributes()[field_index]].combineExtentWith(f.geometry().boundingBox())
                else:
                    if not f.attributes()[field_index] in geometry_dict:
                        geometry_dict[f.attributes()[field_index]] = [f.geometry()]
                    else:
                        geometry_dict[f.attributes()[field_index]].append(f.geometry())

                feedback.setProgress(int(current * total))

            if type == 0:
                # bounding boxes
                current = 0
                total = 50.0 / len(bounds_dict) if bounds_dict else 1
                for group, rect in bounds_dict.items():
                    if feedback.isCanceled():
                        break

                    # envelope
                    feature = QgsFeature()
                    feature.setGeometry(QgsGeometry.fromRect(rect))
                    feature.setAttributes([current, group, rect.width(), rect.height(), rect.area(), rect.perimeter()])
                    sink.addFeature(feature, QgsFeatureSink.FastInsert)
                    geometry_dict[group] = None

                    feedback.setProgress(50 + int(current * total))
                    current += 1
            else:
                current = 0
                total = 50.0 / len(geometry_dict) if geometry_dict else 1

                for group, geometries in geometry_dict.items():
                    if feedback.isCanceled():
                        break

                    feature = self.createFeature(feedback, current, type, geometries, group)
                    sink.addFeature(feature, QgsFeatureSink.FastInsert)
                    geometry_dict[group] = None

                    feedback.setProgress(50 + int(current * total))
                    current += 1
        else:
            total = 80.0 / source.featureCount() if source.featureCount() else 1
            features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]))
            geometry_queue = []
            bounds = QgsRectangle()
            for current, f in enumerate(features):
                if feedback.isCanceled():
                    break

                if not f.hasGeometry():
                    continue

                if type == 0:
                    # bounding boxes, calculate on the fly for efficiency
                    bounds.combineExtentWith(f.geometry().boundingBox())
                else:
                    geometry_queue.append(f.geometry())
                feedback.setProgress(int(current * total))

            if not feedback.isCanceled():
                if type == 0:
                    feature = QgsFeature()
                    feature.setGeometry(QgsGeometry.fromRect(bounds))
                    feature.setAttributes([0, bounds.width(), bounds.height(), bounds.area(), bounds.perimeter()])
                else:
                    feature = self.createFeature(feedback, 0, type, geometry_queue)
                sink.addFeature(feature, QgsFeatureSink.FastInsert)

        return {self.OUTPUT: dest_id}
    def search_data(self, **kwargs):
        """
        Get plot geometries associated with parcels, both collected and official, zoom to them, activate map swipe tool
            and fill comparison table.

        :param kwargs: key-value (field name-field value) to search in parcel tables, both collected and official
                       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 Official 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
        already_zoomed_in = False

        self.clear_result_table()

        search_field = self.cbo_parcel_fields.currentData()
        search_value = list(kwargs.values())[0]

        # Get OFFICIAL parcel's t_id and get related plot(s)
        expression = QgsExpression("{}='{}'".format(search_field,
                                                    search_value))
        request = QgsFeatureRequest(expression)
        field_idx = self.utils._official_layers[PARCEL_TABLE][LAYER].fields(
        ).indexFromName(ID_FIELD)
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([field_idx
                                       ])  # Note: this adds a new flag
        official_parcels = [
            feature for feature in self.utils._official_layers[PARCEL_TABLE]
            [LAYER].getFeatures(request)
        ]

        if len(official_parcels) > 1:
            # We do not expect duplicates in the official source!
            pass  # We'll choose the first one anyways
        elif len(official_parcels) == 0:
            print("No parcel found!", search_field, search_value)

        official_plot_t_ids = []
        if official_parcels:
            official_plot_t_ids = self.utils.ladm_data.get_plots_related_to_parcels(
                self.utils._official_db, [official_parcels[0][ID_FIELD]],
                field_name=ID_FIELD,
                plot_layer=self.utils._official_layers[PLOT_TABLE][LAYER],
                uebaunit_table=self.utils._official_layers[UEBAUNIT_TABLE]
                [LAYER])

            if official_plot_t_ids:
                self._current_official_substring = "\"{}\" IN ('{}')".format(
                    ID_FIELD,
                    "','".join([str(t_id) for t_id in official_plot_t_ids]))
                self.parent.request_zoom_to_features(
                    self.utils._official_layers[PLOT_TABLE][LAYER], list(),
                    official_plot_t_ids)
                already_zoomed_in = True

        # Now get COLLECTED parcel's t_id and get related plot(s)
        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 = {
                ID_FIELD: 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: search_value}

            request = QgsFeatureRequest(expression)
            request.setFlags(QgsFeatureRequest.NoGeometry)
            request.setSubsetOfAttributes(
                [ID_FIELD], self.utils._layers[PARCEL_TABLE]
                [LAYER].fields())  # Note this adds a new flag
            collected_parcels = self.utils._layers[PARCEL_TABLE][
                LAYER].getFeatures(request)
            collected_parcels_t_ids = [
                feature[ID_FIELD] 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 = {
                            ID_FIELD: collected_parcel_t_id
                        }
                    else:
                        return  # User just cancelled the dialog, there is nothing more to do

        search_criterion_official = {search_field: search_value}

        self.fill_table(search_criterion_official, search_criterion_collected)

        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],
                field_name=ID_FIELD,
                plot_layer=self.utils._layers[PLOT_TABLE][LAYER],
                uebaunit_table=self.utils._layers[UEBAUNIT_TABLE][LAYER])
            if plot_t_ids:
                self._current_substring = "{} IN ('{}')".format(
                    ID_FIELD, "','".join([str(t_id) for t_id in plot_t_ids]))
                if not already_zoomed_in:
                    self.parent.request_zoom_to_features(
                        self.utils._layers[PLOT_TABLE][LAYER], list(),
                        plot_t_ids)

                # Send a custom mouse move on the map to make the map swipe tool's limit appear on the canvas

                # Activate Swipe Tool
                self.utils.qgis_utils.activate_layer_requested.emit(
                    self.utils._official_layers[PLOT_TABLE][LAYER])
                if official_plot_t_ids:  # Otherwise the map swipe tool doesn't add any value :)
                    self.parent.activate_map_swipe_tool()

                    plots = self.utils.ladm_data.get_features_from_t_ids(
                        self.utils._layers[PLOT_TABLE][LAYER], plot_t_ids,
                        True)
                    plots_extent = QgsRectangle()
                    for plot in plots:
                        plots_extent.combineExtentWith(
                            plot.geometry().boundingBox())

                    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)