def runThis(self, iface):
        """This is called when user clicks on "CZML Prism Map"

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # store iface
        self.iface = iface

        # clear lists
        self.layerList.clear()
        self.twTimesAttrs.clear()
        self.twTimesAttrs.setRowCount(0)

        # place to store legend settings
        self.legendSamples = None

        # collect currently loaded polygon layers to "layerList" comboBox
        #layers=iface.legendInterface().layers()
        layers = [
            tree_layer.layer() for tree_layer in
            QgsProject.instance().layerTreeRoot().findLayers()
        ]
        ll = []
        for layer in layers:
            if (layer.type() == layer.VectorLayer
                    and layer.geometryType() == QgsWkbTypes.PolygonGeometry):
                ll.append(layer.name())
        self.layerList.addItems(ll)
        # die if no suitable layers found
        if (len(ll) == 0):
            QMessageBox.warning(self, "Error", "No suitable layers found.")
            return
        # show dialog
        self.show()
        if self.exec_():
            # OK clicked, let's create the CZML file

            # get filename
            filename = self.leFileName.text()
            # set html legend filename
            htmlFn = filename + ".legend.html"
            # find layer by chosen name
            name = self.layerList.currentText()
            for layer in layers:
                if (layer.name() == name):
                    alayer = layer
                    break
            # get chosen attribute name
            aName = self.attrList.currentText()
            # create projection transformer object
            crsDest = QgsCoordinateReferenceSystem(4326)  # WGS 84
            crsSrc = alayer.crs()
            xform = QgsCoordinateTransform(crsSrc, crsDest,
                                           QgsProject.instance())
            # get colors
            c1 = self.cb1.color()
            c2 = self.cb2.color()
            R1 = c1.red()
            G1 = c1.green()
            B1 = c1.blue()
            A1 = c1.alpha()
            dR = c2.red() - R1
            dG = c2.green() - G1
            dB = c2.blue() - B1
            dA = c2.alpha() - A1
            # find minimum/maximum time and attribute value
            self.minT = None
            self.maxT = None
            usedAttrs = []
            for row in range(self.twTimesAttrs.rowCount()):
                t = self.twTimesAttrs.item(row, 0).text()
                usedAttrs.append(self.twTimesAttrs.item(row, 1).text())
                if self.minT == None or self.minT > t:
                    self.minT = t
                if self.maxT == None or self.maxT < t:
                    self.maxT = t
            self.minV = None
            self.maxV = None
            for aName in usedAttrs:
                if self.minV == None or self.minV > self.amin[aName]:
                    self.minV = self.amin[aName]
                if self.maxV == None or self.maxV < self.amax[aName]:
                    self.maxV = self.amax[aName]

            # open output file
            ofile = codecs.open(filename, 'w', 'utf-8')
            # leading [ and document packet
            ofile.write('[\n{"id":"document","version":"1.0"}')
            # iterate over feaures
            polyN = 0
            for f in alayer.getFeatures():
                # calculate animated values (height, color)
                heights = []
                colors = []
                for row in range(self.twTimesAttrs.rowCount()):
                    aTime = self.twTimesAttrs.item(row, 0).text()
                    aName = self.twTimesAttrs.item(row, 1).text()
                    # calculate height
                    value = f[aName]
                    if (not value):
                        value = 0
                    if (self.rbLin.isChecked()):
                        h = value
                    elif (self.rbSqrt.isChecked()):
                        h = math.sqrt(value)
                    else:
                        h = math.log(value)
                    h = h * self.sf
                    heights.append('"' + aTime + '",' + str(h))
                    # calculate color
                    rh = 1.0 * (value - self.minV) / (self.maxV - self.minV)
                    R = R1 + dR * rh
                    G = G1 + dG * rh
                    B = B1 + dB * rh
                    A = A1 + dA * rh
                    rgba = str(R) + ',' + str(G) + ',' + str(B) + ',' + str(A)
                    colors.append('"' + aTime + '",' + rgba)
                # polygon/multipolygon?
                if len(f.geometry().asMultiPolygon()) > 0:
                    mpg = f.geometry().asMultiPolygon()
                else:
                    mpg = [f.geometry().asPolygon()]
                for pg in mpg:
                    coords = ""
                    # iterate over rings of the polygon
                    for ring in pg:
                        for p in ring:
                            p1 = xform.transform(p)
                            if (coords != ""):
                                coords = coords + ","
                            coords = coords + '\n\t\t\t\t' + str(
                                p1.x()) + "," + str(p1.y()) + ",0"
                    # add element name if checked
                    if (self.cbAddName.isChecked()):
                        nameString = ',\n\t"name":"' + f[
                            self.strAttrList.currentText()] + '"'
                    else:
                        nameString = ''
                    packetString = '{\n\t"id":"poly' + str(
                        polyN
                    ) + '"' + nameString + ',"availability":"' + self.minT + '/' + self.maxT + '",\n\t"polygon":{\n\t\t"material":{"solidColor":{"color":{"rgba":[' + (
                        ','.join(colors)
                    ) + ']}}},\n\t\t"positions":{\n\t\t\t"cartographicDegrees":[' + coords + ']},\n\t\t"extrudedHeight":{"number":[' + (
                        ','.join(heights)) + ']}}}'
                    ofile.write(",\n" + packetString)
                    polyN = polyN + 1
            # trailing ] and close flie
            ofile.write('\n]')
            ofile.close()
            # create legend if checked
            if (self.cbCreateLegend.isChecked()):
                # open html legend file
                hfile = codecs.open(htmlFn, 'w', 'utf-8')
                hfile.write(
                    '<style>\n.czmlLegendSample { width: 40px; height: 20px; border-radius:3px; border: solid thin black; display: inline-block; }\n'
                )
                hfile.write(
                    'h3.czmlLegend { margin:0; padding-bottom:10px; }\n')
                if self.legendSamples != None:
                    # we have custom settings for legend
                    for i in range(len(self.legendSamples)):
                        rv = (self.legendSamples[i] - self.minV) / (self.maxV -
                                                                    self.minV)
                        R = int(R1 + dR * rv)
                        G = int(G1 + dG * rv)
                        B = int(B1 + dB * rv)
                        hfile.write('.czmlLegendSample' + str(i) +
                                    ' { background: rgb(' + str(R) + ',' +
                                    str(G) + ',' + str(B) + '); }\n')
                else:
                    for i in range(4):
                        R = int(R1 + dR * i / 3.0)
                        G = int(G1 + dG * i / 3.0)
                        B = int(B1 + dB * i / 3.0)
                        hfile.write('.czmlLegendSample' + str(i) +
                                    ' { background: rgb(' + str(R) + ',' +
                                    str(G) + ',' + str(B) + '); }\n')
                hfile.write('</style>\n<h3 class="czmlLegend">' +
                            self.leLegendAttrName.text() + '</h3>')
                if self.legendSamples != None:
                    # we have custom settings for legend
                    for i in range(len(self.legendSamples)):
                        value = self.legendSamples[i]
                        hfile.write(
                            '<span class="czmlLegendSample czmlLegendSample' +
                            str(i) + '"></span> ' + str(value) + '<br/>')
                else:
                    for i in range(4):
                        value = int(self.amin[aName] +
                                    (self.amax[aName] - self.amin[aName]) * i /
                                    3.0)
                        hfile.write(
                            '<span class="czmlLegendSample czmlLegendSample' +
                            str(i) + '"></span> ' + str(value) + '<br/>')
                hfile.close()
            # farewell message
            msg = "CZML file (" + filename + ")"
            if (self.cbCreateLegend.isChecked()):
                msg = msg + " and legend (" + htmlFn + ")"
            msg = msg + " successfully created."
            QMessageBox.information(self, "Information", msg)
Ejemplo n.º 2
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))

        extent = self.parameterAsExtent(parameters, self.TARGET_AREA, context)
        target_crs = self.parameterAsExtentCrs(parameters, self.TARGET_AREA, context)
        if self.TARGET_AREA_CRS in parameters:
            c = self.parameterAsCrs(parameters, self.TARGET_AREA_CRS, context)
            if c.isValid():
                target_crs = c

        target_geom = QgsGeometry.fromRect(extent)

        fields = QgsFields()
        fields.append(QgsField('auth_id', QVariant.String, '', 20))

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

        # make intersection tests nice and fast
        engine = QgsGeometry.createGeometryEngine(target_geom.constGet())
        engine.prepareGeometry()

        layer_bounds = QgsGeometry.fromRect(source.sourceExtent())

        crses_to_check = QgsCoordinateReferenceSystem.validSrsIds()
        total = 100.0 / len(crses_to_check)

        found_results = 0

        transform_context = QgsCoordinateTransformContext()
        for current, srs_id in enumerate(crses_to_check):
            if feedback.isCanceled():
                break

            candidate_crs = QgsCoordinateReferenceSystem.fromSrsId(srs_id)
            if not candidate_crs.isValid():
                continue

            transform_candidate = QgsCoordinateTransform(candidate_crs, target_crs, transform_context)
            transformed_bounds = QgsGeometry(layer_bounds)
            try:
                if not transformed_bounds.transform(transform_candidate) == 0:
                    continue
            except:
                continue

            try:
                if engine.intersects(transformed_bounds.constGet()):
                    feedback.pushInfo(self.tr('Found candidate CRS: {}').format(candidate_crs.authid()))
                    f = QgsFeature(fields)
                    f.setAttributes([candidate_crs.authid()])
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
                    found_results += 1
            except:
                continue

            feedback.setProgress(int(current * total))

        if found_results == 0:
            feedback.reportError(self.tr('No matching projections found'))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 3
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            areaName = vector.createUniqueFieldName('area', fields)
            fields.append(QgsField(areaName, QVariant.Double))
            perimeterName = vector.createUniqueFieldName('perimeter', fields)
            fields.append(QgsField(perimeterName, QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            lengthName = vector.createUniqueFieldName('length', fields)
            fields.append(QgsField(lengthName, QVariant.Double))
        else:
            xName = vector.createUniqueFieldName('xcoord', fields)
            fields.append(QgsField(xName, QVariant.Double))
            yName = vector.createUniqueFieldName('ycoord', fields)
            fields.append(QgsField(yName, QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                zName = vector.createUniqueFieldName('zcoord', fields)
                fields.append(QgsField(zName, QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                zName = vector.createUniqueFieldName('mvalue', fields)
                fields.append(QgsField(zName, QVariant.Double))

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

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 4
0
    def processAlgorithm(self, parameters, context, feedback):
        layer = QgsProcessingUtils.mapLayerFromString(
            self.getParameterValue(self.INPUT), context)
        method = self.getParameterValue(self.METHOD)

        geometryType = layer.geometryType()
        fields = layer.fields()

        export_z = False
        export_m = False
        if geometryType == QgsWkbTypes.PolygonGeometry:
            areaName = vector.createUniqueFieldName('area', fields)
            fields.append(QgsField(areaName, QVariant.Double))
            perimeterName = vector.createUniqueFieldName('perimeter', fields)
            fields.append(QgsField(perimeterName, QVariant.Double))
        elif geometryType == QgsWkbTypes.LineGeometry:
            lengthName = vector.createUniqueFieldName('length', fields)
            fields.append(QgsField(lengthName, QVariant.Double))
        else:
            xName = vector.createUniqueFieldName('xcoord', fields)
            fields.append(QgsField(xName, QVariant.Double))
            yName = vector.createUniqueFieldName('ycoord', fields)
            fields.append(QgsField(yName, QVariant.Double))
            if QgsWkbTypes.hasZ(layer.wkbType()):
                export_z = True
                zName = vector.createUniqueFieldName('zcoord', fields)
                fields.append(QgsField(zName, QVariant.Double))
            if QgsWkbTypes.hasM(layer.wkbType()):
                export_m = True
                zName = vector.createUniqueFieldName('mvalue', fields)
                fields.append(QgsField(zName, QVariant.Double))

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fields, layer.wkbType(), layer.crs(), context)

        ellips = None
        crs = None
        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        if method == 2:
            ellips = QgsProject.instance().ellipsoid()
            crs = layer.crs().srsid()
        elif method == 1:
            mapCRS = iface.mapCanvas().mapSettings().destinationCrs()
            layCRS = layer.crs()
            coordTransform = QgsCoordinateTransform(layCRS, mapCRS)

        outFeat = QgsFeature()

        outFeat.initAttributes(len(fields))
        outFeat.setFields(fields)

        features = QgsProcessingUtils.getFeatures(layer, context)
        total = 100.0 / QgsProcessingUtils.featureCount(layer, context)
        for current, f in enumerate(features):
            inGeom = f.geometry()

            if method == 1:
                inGeom.transform(coordTransform)

            (attr1, attr2) = vector.simpleMeasure(inGeom, method, ellips, crs)

            outFeat.setGeometry(inGeom)
            attrs = f.attributes()
            attrs.append(attr1)
            if attr2 is not None:
                attrs.append(attr2)

            # add point z/m
            if export_z:
                attrs.append(inGeom.geometry().z())
            if export_m:
                attrs.append(inGeom.geometry().m())

            outFeat.setAttributes(attrs)
            writer.addFeature(outFeat)

            feedback.setProgress(int(current * total))

        del writer
def processPoly(source, sink, feedback):
    layercrs = source.sourceCrs()
    if layercrs != epsg4326:
        transto4326 = QgsCoordinateTransform(layercrs, epsg4326,
                                             QgsProject.instance())
        transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs,
                                               QgsProject.instance())

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

                ptset = []
                for points in poly:
                    numpoints = len(points)
                    if numpoints < 2:
                        continue
                    # If the input is not 4326 we need to convert it to that and then back to the output CRS
                    ptStart = QgsPointXY(points[0][0], points[0][1])
                    if layercrs != epsg4326:  # Convert to 4326
                        ptStart = transto4326.transform(ptStart)
                    pts = [ptStart]
                    for x in range(1, numpoints):
                        ptEnd = QgsPointXY(points[x][0], points[x][1])
                        if layercrs != epsg4326:  # Convert to 4326
                            ptEnd = transto4326.transform(ptEnd)
                        l = geod.InverseLine(ptStart.y(), ptStart.x(),
                                             ptEnd.y(), ptEnd.x())
                        n = int(math.ceil(l.s13 / maxseglen))
                        if n > maxSegments:
                            n = maxSegments

                        seglen = l.s13 / n
                        for i in range(1, n):
                            s = seglen * i
                            g = l.Position(
                                s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                                | Geodesic.LONG_UNROLL)
                            pts.append(QgsPointXY(g['lon2'], g['lat2']))
                        pts.append(ptEnd)
                        ptStart = ptEnd

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

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

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

                            seglen = l.s13 / n
                            for i in range(1, n):
                                s = seglen * i
                                g = l.Position(
                                    s, Geodesic.LATITUDE | Geodesic.LONGITUDE
                                    | Geodesic.LONG_UNROLL)
                                pts.append(QgsPointXY(g['lon2'], g['lat2']))
                            pts.append(ptEnd)
                            ptStart = ptEnd

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

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

                    featureout.setAttributes(feature.attributes())
                    sink.addFeature(featureout)
        except:
            num_bad += 1
            #traceback.print_exc()
            pass
        feedback.setProgress(int(cnt * total))
    return num_bad
Ejemplo n.º 6
0
    def processAlgorithm(self, parameters, context, feedback):
        expression = self.parameterAsString(parameters, self.EXPRESSION,
                                            context)
        layers = self.parameterAsLayerList(parameters, self.LAYERS, context)

        layersDict = {}
        if layers:
            layersDict = {
                os.path.basename(lyr.source().split(".")[0]): lyr
                for lyr in layers
            }

        crs = self.parameterAsCrs(parameters, self.CRS, context)
        if not layers and not crs.isValid():
            raise QgsProcessingException(
                self.tr("No reference layer selected nor CRS provided"))

        if not crs.isValid() and layers:
            crs = list(layersDict.values())[0].crs()

        bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
        if not layers and bbox.isNull():
            raise QgsProcessingException(
                self.tr("No reference layer selected nor extent box provided"))

        if not bbox.isNull():
            bboxCrs = self.parameterAsExtentCrs(parameters, self.EXTENT,
                                                context)
            if bboxCrs != crs:
                transform = QgsCoordinateTransform(bboxCrs, crs,
                                                   context.project())
                bbox = transform.transformBoundingBox(bbox)

        if bbox.isNull() and layers:
            bbox = QgsProcessingUtils.combineLayerExtents(layers, crs)

        cellsize = self.parameterAsDouble(parameters, self.CELLSIZE, context)
        if not layers and cellsize == 0:
            raise QgsProcessingException(
                self.tr(
                    "No reference layer selected nor cellsize value provided"))

        def _cellsize(layer):
            ext = layer.extent()
            if layer.crs() != crs:
                transform = QgsCoordinateTransform(layer.crs(), crs,
                                                   context.project())
                ext = transform.transformBoundingBox(ext)
            return (ext.xMaximum() - ext.xMinimum()) / layer.width()

        if cellsize == 0:
            cellsize = min([_cellsize(lyr) for lyr in layersDict.values()])

        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()):
                ref = '{:s}@{:d}'.format(name, n + 1)
                if ref in expression:
                    entry = QgsRasterCalculatorEntry()
                    entry.ref = ref
                    entry.raster = lyr
                    entry.bandNumber = n + 1
                    entries.append(entry)

        output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)

        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, crs,
                                   width, height, entries)

        res = calc.processCalculation(feedback)
        if res == QgsRasterCalculator.ParserError:
            raise QgsProcessingException(self.tr("Error parsing formula"))

        return {self.OUTPUT: output}
Ejemplo n.º 7
0
    def __init__(self, iface, parent=None, extent=None, crs=None):
        """Constructor for the dialog.

        :param iface: A Quantum GIS QGisAppInterface instance.
        :type iface: QGisAppInterface

        :param parent: Parent widget of this dialog
        :type parent: QWidget

        :param extent: Extent of the user's preferred analysis area.
        :type extent: QgsRectangle

        :param crs: Coordinate reference system for user defined analysis
            extent.
        :type crs: QgsCoordinateReferenceSystem
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)

        self.iface = iface
        self.parent = parent
        self.canvas = iface.mapCanvas()
        self.previous_map_tool = None
        # Prepare the map tool
        self.tool = RectangleMapTool(self.canvas)
        self.previous_map_tool = self.canvas.mapTool()

        if extent is None and crs is None:
            # Use the current map canvas extents as a starting point
            self.tool.set_rectangle(self.canvas.extent())
        else:
            # Ensure supplied extent is in current canvas crs
            transform = QgsCoordinateTransform(
                crs,
                self.canvas.mapRenderer().destinationCrs())
            transformed_extent = transform.transformBoundingBox(extent)
            self.tool.set_rectangle(transformed_extent)

        self._populate_coordinates()

        # Observe inputs for changes
        self.x_minimum.valueChanged.connect(self._coordinates_changed)
        self.y_minimum.valueChanged.connect(self._coordinates_changed)
        self.x_maximum.valueChanged.connect(self._coordinates_changed)
        self.y_maximum.valueChanged.connect(self._coordinates_changed)

        # Draw the rubberband
        self._coordinates_changed()

        # Wire up button events
        self.capture_button.clicked.connect(self.start_capture)
        # Handle cancel
        cancel_button = self.button_box.button(QtGui.QDialogButtonBox.Cancel)
        cancel_button.clicked.connect(self.reject)
        # Make sure to reshow this dialog when rectangle is captured
        self.tool.rectangle_created.connect(self.stop_capture)
        # Setup ok button
        self.ok_button = self.button_box.button(QtGui.QDialogButtonBox.Ok)
        self.ok_button.clicked.connect(self.accept)
        # Set up context help
        self.help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)
        # Reset / Clear button
        clear_button = self.button_box.button(QtGui.QDialogButtonBox.Reset)
        clear_button.setText(self.tr('Clear'))
        clear_button.clicked.connect(self.clear)

        # Populate the bookmarks list and connect the combobox
        self._populate_bookmarks_list()
        self.bookmarks_list.currentIndexChanged.connect(
            self.bookmarks_index_changed)

        # Reinstate the last used radio button
        settings = QSettings()
        mode = settings.value('inasafe/analysis_extents_mode',
                              'HazardExposureView')
        if mode == 'HazardExposureView':
            self.hazard_exposure_view_extent.setChecked(True)
        elif mode == 'HazardExposure':
            self.hazard_exposure_only.setChecked(True)
        elif mode == 'HazardExposureBookmark':
            self.hazard_exposure_bookmark.setChecked(True)
        elif mode == 'HazardExposureBoundingBox':
            self.hazard_exposure_user_extent.setChecked(True)

        show_warnings = settings.value('inasafe/show_extent_warnings',
                                       True,
                                       type=bool)
        if show_warnings:
            self.show_warnings.setChecked(True)
        else:
            self.show_warnings.setChecked(False)

        show_confirmations = settings.value(
            'inasafe/show_extent_confirmations', True, type=bool)
        if show_confirmations:
            self.show_confirmations.setChecked(True)
        else:
            self.show_confirmations.setChecked(False)
Ejemplo n.º 8
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 = [
                _layer for _layer in layers if isinstance(_layer, 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 layer in layer_set:
                    layer_extent = coord_transform.transform(layer.extent())
                    if layer.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 layer in layer_set:
                    # combine extent if different layer is provided.
                    layer_extent = coord_transform.transform(layer.extent())
                    extent.combineExtentWith(layer_extent)
                    if layer.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
Ejemplo n.º 9
0
    def __init__(self, iface, parent=None, extent=None, crs=None):
        """Constructor for the dialog.

        :param iface: A Quantum GIS QgisAppInterface instance.
        :type iface: QgisAppInterface

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param extent: Extent of the user's preferred analysis area.
        :type extent: QgsRectangle

        :param crs: CRS for user defined analysis extent.
        :type crs: QgsCoordinateReferenceSystem
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)

        icon = resources_path('img', 'icons', 'set-extents-tool.svg')
        self.setWindowIcon(QIcon(icon))

        self.iface = iface
        self.parent = parent
        self.canvas = iface.mapCanvas()
        self.previous_map_tool = None
        # Prepare the map tool
        self.tool = RectangleMapTool(self.canvas)
        self.previous_map_tool = self.canvas.mapTool()

        if extent is None:
            # Use the current map canvas extents as a starting point
            self.tool.set_rectangle(self.canvas.extent())
        else:

            if isinstance(extent, QgsGeometry):
                # In InaSAFE V4, the extent is a QgsGeometry.
                # This like a hack to transform a geometry to a rectangle.
                extent = wkt_to_rectangle(extent.asWkt())

            # Ensure supplied extent is in current canvas crs
            transform = QgsCoordinateTransform(
                crs,
                self.canvas.mapSettings().destinationCrs(),
                QgsProject.instance())
            transformed_extent = transform.transformBoundingBox(extent)
            self.tool.set_rectangle(transformed_extent)

        self._populate_coordinates()

        # Observe inputs for changes
        self.x_minimum.valueChanged.connect(self._coordinates_changed)
        self.y_minimum.valueChanged.connect(self._coordinates_changed)
        self.x_maximum.valueChanged.connect(self._coordinates_changed)
        self.y_maximum.valueChanged.connect(self._coordinates_changed)

        # Draw the rubberband
        self._coordinates_changed()

        # Wire up button events
        self.capture_button.clicked.connect(self.start_capture)
        # Handle cancel
        cancel_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Cancel)
        cancel_button.clicked.connect(self.reject)
        # Make sure to reshow this dialog when rectangle is captured
        self.tool.rectangle_created.connect(self.stop_capture)
        # Setup ok button
        self.ok_button = self.button_box.button(QtWidgets.QDialogButtonBox.Ok)
        self.ok_button.clicked.connect(self.accept)
        # Set up context help
        self.help_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)
        # Reset / Clear button
        clear_button = self.button_box.button(QtWidgets.QDialogButtonBox.Reset)
        clear_button.setText(self.tr('Clear'))
        clear_button.clicked.connect(self.clear)

        # Populate the bookmarks list and connect the combobox
        self._populate_bookmarks_list()
        self.bookmarks_list.currentIndexChanged.connect(
            self.bookmarks_index_changed)

        # Reinstate the last used radio button
        mode = setting('analysis_extents_mode', HAZARD_EXPOSURE_VIEW)

        if mode == HAZARD_EXPOSURE_VIEW:
            self.hazard_exposure_view_extent.setChecked(True)
        elif mode == EXPOSURE:
            self.exposure_only.setChecked(True)
        elif mode == HAZARD_EXPOSURE:
            self.hazard_exposure_only.setChecked(True)
        elif mode == HAZARD_EXPOSURE_BOOKMARK:
            self.hazard_exposure_bookmark.setChecked(True)
        elif mode == HAZARD_EXPOSURE_BOUNDINGBOX:
            self.hazard_exposure_user_extent.setChecked(True)

        self.show_warnings.setChecked(
            setting('show_extent_warnings', True, bool))
        self.show_confirmations.setChecked(
            setting('show_extent_confirmations', True, bool))
Ejemplo n.º 10
0
    def matrix_dem_builder(self, dem_dataset, height, width, scale, spacing_mm,
                           roi_x_max, roi_x_min, roi_y_min, h_base, z_scale, projected):

        # Calculate DEM parameters
        dem_col = dem_dataset.RasterXSize
        dem_row = dem_dataset.RasterYSize
        geotransform = dem_dataset.GetGeoTransform()
        dem_x_min = geotransform[0]
        dem_y_max = geotransform[3]
        dem_y_min = dem_y_max + dem_row * geotransform[5]
        dem_x_max = dem_x_min + dem_col * geotransform[1]

        rectParam = self.parameters["roi_rect_Param"]
        rotation = rectParam["rotation"]
        if not projected:
            spacing_deg = spacing_mm * rectParam["width"] / width

        row_stl = int(math.ceil(height / spacing_mm) + 1)
        col_stl = int(math.ceil(width / spacing_mm) + 1)
        matrix_dem = [list(range(col_stl)) for i in range(row_stl)]

        source = self.parameters["crs_map"]
        target = self.parameters["crs_layer"]
        if source != target:
            transform = QgsCoordinateTransform(source, target, QgsProject.instance())

        #  RECORRIDO
        #  0 ---------------------------> 1
        #    --------------------------->
        #  ^ --------------------------->
        #  | --------------------------->
        #  | --------------------------->
        #  X ---------------------------> 2
        var_y = height
        for i in range(row_stl):
            self.updateProgress.emit()
            var_x = 0
            for j in range(col_stl):
                # Model coordinate x(mm), y(mm)
                x_model = round(var_x, 2)
                y_model = round(var_y, 2)

                # Model maps geo_coordinates
                if projected:
                    x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * scale / 1000)
                    x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * scale / 1000)
                else:
                    x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * spacing_deg / spacing_mm)
                    x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * spacing_deg / spacing_mm)
                    
                # Model layer geo_coordinates to query z value
                point = QgsPoint(x, y)
                if source != target:
                    point = transform.transform(point)
                    x = point.x()
                    y = point.y()

                # From x(m) get Column in DEM file
                col_dem = (x - dem_x_min) * dem_col / (dem_x_max - dem_x_min)
                col_dem = int(math.floor(col_dem))
                if col_dem == dem_col:
                    col_dem -= 1
                # From y(m) get Row in DEM file
                row_dem = (dem_y_max - y) * dem_row / (dem_y_max - dem_y_min)
                row_dem = int(math.floor(row_dem))
                if row_dem == dem_row:
                    row_dem -= 1

                # Model coordinate z(mm)
                if col_dem < 0 or row_dem < 0:
                    z_model = self.baseModel
                elif self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] <= h_base:
                    z_model = self.baseModel
                elif math.isnan(self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0]):
                    z_model = self.baseModel
                else:
                    z_model = round((self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] - h_base) / scale * 1000 * z_scale, 2) + self.baseModel

                matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)

                var_x += spacing_mm
                if var_x > width:
                    var_x = width
            var_y = spacing_mm * (row_stl - (i + 2))
            if self.quit:
                return 0
        return matrix_dem
Ejemplo n.º 11
0
def build_layer_table(layer_list=None, only_raster_boundingbox=True):
    """Build a table of layer properties.
    Can be used in conjunction with selecting layers to exclude from mapcomboboxes

    Layer_list: default  None
                if None then it will build the table from all layers in the QGIS project
                otherwise it will use the list.
    
    only_raster_boundingbox: default False 
                    create a bounding box from the raster data 
                   ie removing nodata from polygon. 
                   This will slow it down if large numbers of rasters are present.
    """

    dest_crs = QgsProject.instance().crs()

    gdf_layers = gpd.GeoDataFrame(columns=[
        'layer', 'layer_name', 'layer_id', 'layer_type', 'source', 'format',
        'epsg', 'crs_name', 'is_projected', 'extent', 'provider', 'geometry'
    ],
                                  geometry='geometry',
                                  crs=dest_crs.authid())  # pd.DataFrame()

    if layer_list is None or len(layer_list) == 0:
        layermap = QgsProject.instance().mapLayers().values()

    else:
        layermap = layer_list

    new_rows = []

    for layer in layermap:
        if layer.type() not in [
                QgsMapLayer.VectorLayer, QgsMapLayer.RasterLayer
        ]:
            continue

        if layer.providerType() not in ['ogr', 'gdal', 'delimitedtext']:
            continue

        if layer.type() == QgsMapLayer.VectorLayer:
            format = layer.dataProvider().storageType()
        else:
            format = None

        if layer.crs().isValid() and layer.crs().authid() == '':
            # Try and convert older style coordinates systems
            # were correctly definied in QGIS 2 as GDA94 / MGA zone 54
            # but get interpreted in QGIS 3 as  Unknown CRS: BOUNDCRS[SOURCECRS[PROJCRS["GDA94 / MGA zone 54",.....

            layer_crs = QgsCoordinateReferenceSystem()
            if not layer_crs.createFromProj(layer.crs().toWkt()):
                #print('Could not match a coordinate system for {}'.format(layer.id()))
                layer_crs = layer.crs()

                # could apply to the layer, but what if it's wrong....
                #layer.setCrs(layer_crs)

        else:
            layer_crs = layer.crs()

        # project the bounding box extents to be the same as the qgis project.
        if layer_crs.authid() != dest_crs.authid():
            transform = QgsCoordinateTransform(layer_crs, dest_crs,
                                               QgsProject.instance())
            prj_ext = transform.transformBoundingBox(layer.extent())
        else:
            prj_ext = layer.extent()

        row_dict = {
            'layer': layer,
            'layer_name': layer.name(),
            'layer_id': layer.id(),
            'layer_type': layerTypes[layer.type()],
            'format': format,
            'source': get_layer_source(layer),
            'epsg': layer_crs.authid(),
            'crs_name': layer_crs.description(),
            'is_projected': not layer_crs.isGeographic(),
            'provider': layer.providerType(),
            'geometry': wkt.loads(prj_ext.asWktPolygon())
        }

        # 'extent': prj_ext.asWktPolygon(),

        if layer.type() == QgsMapLayer.RasterLayer:
            pixel_size = get_pixel_size(layer)
            if not only_raster_boundingbox:
                with rasterio.open(get_layer_source(layer)) as src:
                    msk = src.dataset_mask()  # 0 = nodata 255=valid
                    rast_shapes = rasterio.features.shapes(
                        np.ma.masked_equal(np.where(msk > 0, 1, 0), 0),
                        transform=src.transform)
                try:
                    results = ({
                        'properties': {
                            'raster_val': v
                        },
                        'geometry': s
                    } for i, (s, v) in enumerate(rast_shapes))

                    geoms = list(results)
                    gpd_rPoly = gpd.GeoDataFrame.from_features(
                        geoms, crs=layer.crs().authid())
                    dest_crs.authid().replace('epgs:', '')
                    gpd_rPoly.to_crs(dest_crs.authid().replace('epgs:', ''),
                                     inplace=True)
                    row_dict.update({'geometry': gpd_rPoly.unary_union})

                    del gpd_rPoly, results, msk, rast_shapes
                except:
                    pass

            row_dict.update({
                'bandcount':
                layer.bandCount(),
                'datatype':
                dataTypes[layer.dataProvider().dataType(1)],
                'pixel_size':
                pixel_size[0],
                'pixel_text':
                '{} {}'.format(*pixel_size),
            })

        new_rows.append(row_dict)


#    gdf_layers = gpd.GeoDataFrame(new_rows, geometry='extent')

    if len(new_rows) == 0:
        return gdf_layers

    # for pandas 0.23.4 add sort=False to prevent row and column orders to change.
    try:
        gdf_layers = gdf_layers.append(new_rows, ignore_index=True, sort=False)
    except:
        gdf_layers = gdf_layers.append(new_rows, ignore_index=True)

    #df_layers.set_geometry('geometry')
    return gdf_layers
Ejemplo n.º 12
0
    def matrix_dem_builder_interpolation(self, dem_dataset, height, width, scale, scale_h, scale_w, spacing_mm,
                                         roi_x_max, roi_x_min, roi_y_min, z_base, z_scale, projected):

        # Calculate DEM parameters
        columns = dem_dataset.RasterXSize
        rows = dem_dataset.RasterYSize
        geotransform = dem_dataset.GetGeoTransform()
        dem_x_min = geotransform[0]  # Limit pixel (not center)
        dem_y_max = geotransform[3]  # Limit pixel (not center)

        # dem_y_min = dem_y_max + rows * geotransform[5]
        # dem_x_max = dem_x_min + columns * geotransform[1]

        spacing_deg = 0
        rectParam = self.parameters["roi_rect_Param"]
        rotation = rectParam["rotation"]
        if not projected:
            spacing_deg = spacing_mm * rectParam["width"] / width

        row_stl = int(math.ceil(height / spacing_mm) + 1)
        col_stl = int(math.ceil(width / spacing_mm) + 1)
        matrix_dem = [list(range(col_stl)) for i in range(row_stl)]

        #  RECORRIDO
        #  0 ---------------------------> 1
        #    --------------------------->
        #  ^ --------------------------->
        #  | --------------------------->
        #  | --------------------------->
        #  X ---------------------------> 2

        var_y = height
        for i in range(row_stl):
            self.updateProgress.emit()

            var_x = 0
            for j in range(col_stl):
                # Model coordinate x(mm), y(mm)
                x_model = round(var_x, 2)
                y_model = round(var_y, 2)

                # Model maps geo_coordinates
                if projected:
                    x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * scale / 1000)
                    x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * scale / 1000)
                else:
                    x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * spacing_deg / spacing_mm)
                    x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * spacing_deg / spacing_mm)
                # print('punto cuajado (row - col - x - y)', i, j, x_model, y_model, round(x, 3), round(y, 3), sep=" - ")

                # Model layer geo_coordinates to query z value
                # point = QgsPoint(x, y)
                source = self.parameters["crs_map"]
                target = self.parameters["crs_layer"]
                if source != target:
                    transform = QgsCoordinateTransform(source, target, QgsProject.instance())
                    point = transform.transform(x, y)
                    x = point.x()
                    y = point.y()

                # From x(m) get Column in DEM file
                col_dem = (x - dem_x_min) / geotransform[1]
                if col_dem >= columns:
                    col_dem -= 1
                # From y(m) get Row in DEM file
                row_dem = (y - dem_y_max) / geotransform[5]
                if row_dem >= rows:
                    row_dem -= 1

                # region nearest neighbours interpolation
                # row_dem = int(math.floor(row_dem))
                # col_dem = int(math.floor(col_dem))
                #
                # # Model coordinate z(mm)
                # if col_dem < 0 or row_dem < 0:
                #     z_model = self.baseModel
                # elif self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] <= h_base:
                #     z_model = self.baseModel
                # elif math.isnan(self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0]):
                #     z_model = self.baseModel
                # else:
                #     z_model = round((self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] - h_base) /
                #                     scale * 1000 * z_scale, 2) + self.baseModel
                #
                # matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)
                # endregion

                # region Lineal interpolation
                if 0 < col_dem < columns - 1 and 0 < row_dem < rows - 1:
                    min_col = int(math.floor(col_dem))
                    max_col = int(math.ceil(col_dem))
                    min_row = int(math.floor(row_dem))
                    max_row = int(math.ceil(row_dem))

                    # - From geographic coordinates calculate pixel coordinates
                    # - round up and down to see the 4 pixels neighbours integer

                    xP1 = dem_x_min + min_col * geotransform[1]
                    yP1 = dem_y_max + min_row * geotransform[5]
                    zP1 = self.get_z(min_col, min_row, dem_dataset, z_base, scale, z_scale)

                    xP2 = dem_x_min + max_col * geotransform[1]
                    yP2 = dem_y_max + min_row * geotransform[5]
                    zP2 = self.get_z(max_col, min_row, dem_dataset, z_base, scale, z_scale)

                    xP3 = dem_x_min + min_col * geotransform[1]
                    yP3 = dem_y_max + max_row * geotransform[5]
                    zP3 = self.get_z(min_col, max_row, dem_dataset, z_base, scale, z_scale)

                    xP4 = dem_x_min + max_col * geotransform[1]
                    yP4 = dem_y_max + max_row * geotransform[5]
                    zP4 = self.get_z(max_col, max_row, dem_dataset, z_base, scale, z_scale)

                    p = self.pto(x=x, y=y, z=0)
                    p1 = self.pto(x=xP1, y=yP1, z=zP1)
                    p2 = self.pto(x=xP2, y=yP2, z=zP2)
                    p3 = self.pto(x=xP3, y=yP3, z=zP3)
                    p4 = self.pto(x=xP4, y=yP4, z=zP4)

                    z_model = self.interp_line(p, p1, p2, p3, p4)
                    matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)

                else:
                    # Solution for boundaries when col = 0 or col = Nº cols
                    # Manage Boundary limits:
                    if (col_dem == 0 or col_dem >= columns - 1) and (row_dem == 0 or row_dem >= rows - 1):
                        # Corners:
                        col_dem = int(col_dem)
                        row_dem = int(row_dem)
                        z_model = self.get_z(col_dem, row_dem, dem_dataset, z_base, scale, z_scale)
                        matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)

                    elif (col_dem == 0 or col_dem >= columns - 1) and 0 < row_dem < rows - 1:
                        # First and last column
                        min_row = int(math.floor(row_dem))
                        max_row = int(math.ceil(row_dem))
                        col_dem = int(col_dem)

                        if min_row == max_row:
                            z_model = self.get_z(col_dem, max_row, dem_dataset, z_base, scale, z_scale)
                            matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)
                        else:
                            yP1 = dem_y_max + min_row * geotransform[5]
                            zP1 = self.get_z(col_dem, min_row, dem_dataset, z_base, scale, z_scale)

                            yP2 = dem_y_max + max_row * geotransform[5]
                            zP2 = self.get_z(col_dem, max_row, dem_dataset, z_base, scale, z_scale)

                            z_model = zP2 + math.fabs(yP2 - y) * (zP1 - zP2) / math.fabs(yP2 - yP1)
                            matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)

                    elif 0 < col_dem < columns - 1 and (row_dem == 0 or row_dem >= rows - 1):
                        # First and last row
                        min_col = int(math.floor(col_dem))
                        max_col = int(math.ceil(col_dem))
                        row_dem = int(row_dem)

                        if min_col == max_col:
                            z_model = self.get_z(min_col, row_dem, dem_dataset, z_base, scale, z_scale)
                            matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)
                        else:
                            xP1 = dem_x_min + min_col * geotransform[1]
                            zP1 = self.get_z(min_col, row_dem, dem_dataset, z_base, scale, z_scale)

                            xP2 = dem_x_min + max_col * geotransform[1]
                            zP2 = self.get_z(max_col, row_dem, dem_dataset, z_base, scale, z_scale)

                            z_model = zP1 + math.fabs(xP1 - x) * (zP2 - zP1) / math.fabs(xP2 - xP1)
                            matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)
                    else:
                        # print('punto cuajado', x_model, y_model, sep=" ")
                        matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=0)
                # endregion

                var_x += spacing_mm
                if var_x > width:
                    var_x = width
            var_y = spacing_mm * (row_stl - (i + 2))
            if self.quit:
                return 0

        return matrix_dem
Ejemplo n.º 13
0
 def canvasPointXY(self, lat, lon):
     canvasCrs = self.canvas.mapSettings().destinationCrs()
     transform = QgsCoordinateTransform(self.settings.epsg4326, canvasCrs)
     x, y = transform.transform(float(lon), float(lat))
     pt = QgsPointXY(x, y)
     return pt
Ejemplo n.º 14
0
def exportRaster(layer, count, layersFolder, feedback, iface, matchCRS):
    feedback.showFeedback("Exporting %s to PNG..." % layer.name())
    name_ts = (safeName(layer.name()) + unicode(count) +
               unicode(int(time.time())))

    # We need to create a new file to export style
    piped_file = os.path.join(tempfile.gettempdir(), name_ts + '_piped.tif')

    piped_extent = layer.extent()
    piped_width = layer.height()
    piped_height = layer.width()
    piped_crs = layer.crs()
    piped_renderer = layer.renderer()
    piped_provider = layer.dataProvider()

    pipe = QgsRasterPipe()
    pipe.set(piped_provider.clone())
    pipe.set(piped_renderer.clone())

    file_writer = QgsRasterFileWriter(piped_file)

    file_writer.writeRaster(pipe, piped_height, -1, piped_extent, piped_crs)

    # Export layer as PNG
    out_raster = os.path.join(
        layersFolder,
        safeName(layer.name()) + "_" + unicode(count) + ".png")

    projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
    if not (matchCRS and layer.crs() == projectCRS):
        # Extent of the layer in EPSG:3857
        crsSrc = layer.crs()
        crsDest = QgsCoordinateReferenceSystem(3857)
        try:
            xform = QgsCoordinateTransform(crsSrc, crsDest,
                                           QgsProject.instance())
        except:
            xform = QgsCoordinateTransform(crsSrc, crsDest)
        extentRep = xform.transformBoundingBox(layer.extent())

        extentRepNew = ','.join([
            unicode(extentRep.xMinimum()),
            unicode(extentRep.xMaximum()),
            unicode(extentRep.yMinimum()),
            unicode(extentRep.yMaximum())
        ])

        # Reproject in 3857
        piped_3857 = os.path.join(tempfile.gettempdir(),
                                  name_ts + '_piped_3857.tif')
        qgis_version = Qgis.QGIS_VERSION

        old_stdout = sys.stdout
        sys.stdout = mystdout = StringIO()
        try:
            processing.algorithmHelp("gdal:warpreproject")
        except:
            pass
        sys.stdout = old_stdout

        params = {
            "INPUT": piped_file,
            "SOURCE_CRS": layer.crs().authid(),
            "TARGET_CRS": "EPSG:3857",
            "NODATA": 0,
            "TARGET_RESOLUTION": 0,
            "RESAMPLING": 2,
            "TARGET_EXTENT": extentRepNew,
            "EXT_CRS": "EPSG:3857",
            "TARGET_EXTENT_CRS": "EPSG:3857",
            "DATA_TYPE": 0,
            "COMPRESS": 4,
            "JPEGCOMPRESSION": 75,
            "ZLEVEL": 6,
            "PREDICTOR": 1,
            "TILED": False,
            "BIGTIFF": 0,
            "TFW": False,
            "MULTITHREADING": False,
            "COPY_SUBDATASETS": False,
            "EXTRA": "",
            "OUTPUT": piped_3857
        }

        warpArgs = {}

        lines = mystdout.getvalue()
        for count, line in enumerate(lines.split("\n")):
            if count != 0 and ":" in line:
                try:
                    k = line.split(":")[0]
                    warpArgs[k] = params[k]
                except:
                    pass

        try:
            processing.run("gdal:warpreproject", warpArgs)
        except:
            shutil.copyfile(piped_file, piped_3857)

        try:
            processing.run(
                "gdal:translate", {
                    "INPUT": piped_3857,
                    "OUTSIZE": 100,
                    "OUTSIZE_PERC": True,
                    "NODATA": 0,
                    "EXPAND": 0,
                    "TARGET_CRS": "",
                    "PROJWIN": extentRepNew,
                    "SDS": False,
                    "DATA_TYPE": 0,
                    "COMPRESS": 4,
                    "JPEGCOMPRESSION": 75,
                    "ZLEVEL": 6,
                    "PREDICTOR": 1,
                    "TILED": False,
                    "BIGTIFF": 0,
                    "TFW": False,
                    "COPY_SUBDATASETS": False,
                    "OPTIONS": "",
                    "OUTPUT": out_raster
                })
        except:
            shutil.copyfile(piped_3857, out_raster)
    else:
        srcExtent = ','.join([
            unicode(piped_extent.xMinimum()),
            unicode(piped_extent.xMaximum()),
            unicode(piped_extent.yMinimum()),
            unicode(piped_extent.yMaximum())
        ])
        processing.run(
            "gdal:translate", {
                "INPUT": piped_file,
                "OUTSIZE": 100,
                "OUTSIZE_PERC": True,
                "NODATA": 0,
                "EXPAND": 0,
                "TARGET_CRS": "",
                "PROJWIN": srcExtent,
                "SDS": False,
                "DATA_TYPE": 0,
                "COMPRESS": 4,
                "JPEGCOMPRESSION": 75,
                "ZLEVEL": 6,
                "PREDICTOR": 1,
                "TILED": False,
                "BIGTIFF": 0,
                "TFW": False,
                "COPY_SUBDATASETS": False,
                "OPTIONS": "",
                "OUTPUT": out_raster
            })
Ejemplo n.º 15
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))

        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        new_fields = QgsFields()
        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            new_fields.append(QgsField('area', QVariant.Double))
            new_fields.append(QgsField('perimeter', QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            new_fields.append(QgsField('length', QVariant.Double))
            if not QgsWkbTypes.isMultiType(source.wkbType()):
                new_fields.append(QgsField('straightdis', QVariant.Double))
                new_fields.append(QgsField('sinuosity', QVariant.Double))
        else:
            new_fields.append(QgsField('xcoord', QVariant.Double))
            new_fields.append(QgsField('ycoord', QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                new_fields.append(QgsField('zcoord', QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                new_fields.append(QgsField('mvalue', QVariant.Double))

        fields = QgsProcessingUtils.combineFields(fields, new_fields)
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, wkb_type, source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 16
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        ueberhoehung = self.parameterAsInt(parameters, self.INPUTZFACTOR,
                                           context)
        rasterLayer = self.parameterAsRasterLayer(parameters, self.INPUTRASTER,
                                                  context)
        baseLineLayer = self.parameterAsVectorLayer(parameters,
                                                    self.INPUTBASELINE,
                                                    context)
        polygonLayer = self.parameterAsVectorLayer(parameters,
                                                   self.INPUTINTERSECTIONLAYER,
                                                   context)
        outputGeomType = self.parameterAsEnum(parameters, self.OUTPUTGEOMTYPE,
                                              context)

        baseLine = None
        #Basline Layer must have only 1 Feature
        if baseLineLayer.featureCount() == 1:
            #baseLine must be the first feature
            baseLineFeature = next(
                baseLineLayer.getFeatures(QgsFeatureRequest().setLimit(1)))
            baseLine = baseLineFeature.geometry()
        elif len(baseLineLayer.selectedFeatures()) == 1:
            selection = baseLineLayer.selectedFeatures()
            #baseLine must be the first feature
            selFeats = [f for f in selection]
            baseLineFeature = selFeats[0]
            baseLine = baseLineFeature.geometry()
        else:
            msg = self.tr(
                "Error: BaseLine layer needs exactly one line feature! " +
                str(baseLineLayer.featureCount()) +
                " Just select one feature!")
            feedback.reportError(msg)
            raise QgsProcessingException(msg)
        #take CRS from Rasterlayer
        crsProject = QgsProject.instance().crs()
        #check if layers have the same crs
        if not baseLineLayer.crs().authid() == crsProject.authid():
            # if not, transform to raster crs()
            trafo1 = QgsCoordinateTransform(baseLineLayer.crs(), crsProject,
                                            QgsProject.instance())
            #transform BaseLine
            opResult1 = baseLine.transform(
                trafo1, QgsCoordinateTransform.ForwardTransform, False)
        if not polygonLayer.crs().authid() == crsProject.authid():
            # if not, transform to raster crs()
            trafo2 = QgsCoordinateTransform(polygonLayer.crs(), crsProject,
                                            QgsProject.instance())
            #transform BaseLine
            opResult2 = baseLine.transform(
                trafo2, QgsCoordinateTransform.ForwardTransform, False)

        layerZFieldId = -1
        #init Terrain
        tm = TerrainModel(rasterLayer, feedback)
        #init LaengsProfil
        lp = LaengsProfil(baseLine, tm, crsProject, feedback)

        try:
            total = 100.0 / len(lp.linearRef.lineSegments)
        except:
            msg = self.tr("Keine Basislinie")
            feedback.reportError(msg)
            raise QgsProcessingException(msg)

        bufferWidth = 10  #10 m, we make an area to intersect
        #get candidates featuresOnLine=[]
        featuresOnLine = lp.linearRef.getFeaturesOnBaseLine(
            polygonLayer, bufferWidth)
        #Falls Linien, dann ermittle Schnittpunkte mit Laengsprofil
        schnittpunkte = []  #Liste von Features
        if self.isPolygonType(polygonLayer):
            #get intersection point features
            #hier erfolgt ggf. auch die umwandlung der Projektion
            schnittpunkte, schnittLinien = self.getSchnittpunkteAusPolygonen(
                featuresOnLine, polygonLayer.crs(), lp, feedback
            )  #Um Attribute der geschnittenen Objekte zu uebernehmen, muss hier mehr uebergeben werden

            featuresWithZ = []
            if outputGeomType == 1:  #'Points':
                #calculate Z-Values for Points
                featuresWithZ = tm.addZtoPointFeatures(schnittpunkte,
                                                       crsProject,
                                                       layerZFieldId)

            else:  #0 Lines
                if schnittLinien is None or len(schnittLinien) == 0:
                    msg = self.tr(
                        "No polygon feature is intersecting this baseline!")
                    feedback.reportError(msg)
                else:
                    #calculate Z-Values for SchnittLines
                    for schnittLineFeat in schnittLinien:
                        featZ = QgsFeature(schnittLineFeat)
                        linRef = LinearReferencingMaschine(
                            schnittLineFeat.geometry(), crsProject, feedback)
                        line3D = self.calc3DProfile(linRef, tm, crsProject)
                        featZ.setGeometry(line3D)
                        featZ.setAttributes(schnittLineFeat.attributes())
                        featuresWithZ.append(featZ)

            if not featuresWithZ is None:

                if len(featuresWithZ) == 0:
                    feedback.reportError(
                        "No Features was added with z values.")
                    return {self.OUTPUT: 0}
                else:

                    newFields = []
                    try:
                        newFields = featuresWithZ[0].fields()
                        wkbTyp = featuresWithZ[0].geometry().wkbType()
                    except IndexError:
                        msg = self.tr(
                            "No Z values could be assigned to the Geometries.")
                        feedback.reportError(msg)
                        raise QgsProcessingException(msg)

                    #newFields.append( QgsField( "profil_id" ,  QVariant.Int ) )
                    newFields.append(QgsField("z_factor", QVariant.Int))

                    #config Output
                    (sink,
                     dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                                     context, newFields,
                                                     wkbTyp, crsProject)

                    #create geometries as profil coordinates
                    profilFeatures = []
                    iFeat = 0
                    for current, srcFeat in enumerate(featuresWithZ):
                        # Stop the algorithm if cancel button has been clicked
                        if feedback.isCanceled():
                            return {self.OUTPUT: dest_id}
                            #break
                        srcGeom = srcFeat.geometry()
                        if srcGeom.isGeosValid():
                            profilGeometries = lp.extractProfilGeom(
                                srcGeom, ueberhoehung, lp.srcProfilLine)
                            #feedback.pushInfo("b " + str(srcFeat.attributes())+ ""+ str(profilGeometries))
                            for profilGeom in profilGeometries:
                                # build a Feature
                                profilFeat = QgsFeature(newFields)
                                profilFeat.setGeometry(profilGeom)
                                attrs = srcFeat.attributes()
                                attrs.append(ueberhoehung)
                                profilFeat.setAttributes(attrs)
                                # Add a feature in the sink
                                sink.addFeature(profilFeat,
                                                QgsFeatureSink.FastInsert)
                                iFeat = iFeat + 1
                                #feedback.pushInfo(str(profilFeat.attributes()))
                        else:
                            feedback.reportError(
                                str(srcFeat.attributes()) + srcGeom.asWkt())
                        # Update the progress bar
                        feedback.setProgress(int(current * total))

                    msgInfo = self.tr(
                        "{0} intersection features where transformed to profile coordinates:"
                    ).format(iFeat)
                    feedback.pushInfo(msgInfo)
                    # Return the results of the algorithm. In this case our only result is
                    return {self.OUTPUT: dest_id}
Ejemplo n.º 17
0
    def testContextSingle(self):
        """
        Various tests to ensure that datum transforms are correctly set respecting context
        """
        context = QgsCoordinateTransformContext()
        context.addSourceDatumTransform(
            QgsCoordinateReferenceSystem('EPSG:28356'), 1)
        context.addDestinationDatumTransform(
            QgsCoordinateReferenceSystem('EPSG:4283'), 2)
        context.addSourceDestinationDatumTransform(
            QgsCoordinateReferenceSystem('EPSG:28356'),
            QgsCoordinateReferenceSystem('EPSG:4283'), 3, 4)

        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem('EPSG:28354'),
            QgsCoordinateReferenceSystem('EPSG:28353'), context)
        # should be no datum transforms
        self.assertEqual(transform.sourceDatumTransformId(), -1)
        self.assertEqual(transform.destinationDatumTransformId(), -1)
        # matching source
        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem('EPSG:28356'),
            QgsCoordinateReferenceSystem('EPSG:28353'), context)
        self.assertEqual(transform.sourceDatumTransformId(), 1)
        self.assertEqual(transform.destinationDatumTransformId(), -1)
        # matching dest
        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem('EPSG:28354'),
            QgsCoordinateReferenceSystem('EPSG:4283'), context)
        self.assertEqual(transform.sourceDatumTransformId(), -1)
        self.assertEqual(transform.destinationDatumTransformId(), 2)
        # matching src/dest pair
        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem('EPSG:28356'),
            QgsCoordinateReferenceSystem('EPSG:4283'), context)
        self.assertEqual(transform.sourceDatumTransformId(), 3)
        self.assertEqual(transform.destinationDatumTransformId(), 4)

        # test manual overwriting
        transform.setSourceDatumTransform(11)
        transform.setDestinationDatumTransform(13)
        self.assertEqual(transform.sourceDatumTransformId(), 11)
        self.assertEqual(transform.destinationDatumTransformId(), 13)

        # test that auto datum setting occurs when updating src/dest crs
        transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356'))
        self.assertEqual(transform.sourceDatumTransformId(), 3)
        self.assertEqual(transform.destinationDatumTransformId(), 4)
        transform.setSourceDatumTransform(11)
        transform.setDestinationDatumTransform(13)

        transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283'))
        self.assertEqual(transform.sourceDatumTransformId(), 3)
        self.assertEqual(transform.destinationDatumTransformId(), 4)
        transform.setSourceDatumTransform(11)
        transform.setDestinationDatumTransform(13)

        # delayed context set
        transform = QgsCoordinateTransform()
        self.assertEqual(transform.sourceDatumTransformId(), -1)
        self.assertEqual(transform.destinationDatumTransformId(), -1)
        transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356'))
        transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283'))
        self.assertEqual(transform.sourceDatumTransformId(), -1)
        self.assertEqual(transform.destinationDatumTransformId(), -1)
        transform.setContext(context)
        self.assertEqual(transform.sourceDatumTransformId(), 3)
        self.assertEqual(transform.destinationDatumTransformId(), 4)
Ejemplo n.º 18
0
    def getSchnittpunkteAusPolygonen(self, overlapFeats, featureCrs,
                                     laengsProfil, feedback):
        schnittpunktFeatures = []
        schnittLinesFeatures = []
        ioFeat = 0
        countPoints = 0
        try:
            for feat in overlapFeats:
                #Feature bekommt neues Attribut Station
                if ioFeat == 0:
                    fields = feat.fields()
                    fields.append(QgsField("station", QVariant.Double))

                schnittpunktFeaturesOfThisPolygon = []
                schnittpunktListOfPolygon = [
                ]  #Liste [points, stations, feature.id's]
                #check if Multipolygon
                iMulti = 0
                iRing = 0

                tempGeom = QgsGeometry()
                tempGeom.fromWkb(feat.geometry().asWkb())
                #transform geom to Project.crs() if crs a different
                if not featureCrs.authid() == QgsProject.instance().crs(
                ).authid():
                    trafo = QgsCoordinateTransform(featureCrs,
                                                   QgsProject.instance().crs(),
                                                   QgsProject.instance())
                    #transform Geom to Project.crs()
                    tempGeom.transform(trafo,
                                       QgsCoordinateTransform.ForwardTransform,
                                       False)

                if tempGeom.isMultipart():
                    multiGeom = tempGeom.asMultiPolygon()
                    #Schleife zum Auflösen des Multiparts
                    for polygon in multiGeom:
                        for ring in polygon:
                            points = []
                            for pxy in ring:
                                #feedback.pushInfo(str(iMulti)+" "+str(iRing)+" "+str(pxy) + " " + str(type(pxy)))
                                points.append(QgsPoint(pxy.x(), pxy.y()))
                            singlePolygon = QgsGeometry().fromPolyline(points)
                            iRing = iRing + 1

                            #feedback.pushInfo(str(iMulti) + str(type(singlePolygon)) + str(polygon))

                            schnittpunktFeaturesOfThisPolygonItem = self.makeIntersectionFeatures(
                                feat, singlePolygon, laengsProfil, fields,
                                feedback)
                            for spfotp in schnittpunktFeaturesOfThisPolygonItem:
                                schnittpunktFeaturesOfThisPolygon.append(
                                    spfotp)
                            #Liste [points, stations, feature.id's]
                            schnittpunktListOfPolygonItem = self.makeIntersectionPointsStationList(
                                feat, singlePolygon, laengsProfil, fields,
                                feedback)
                            for sp in schnittpunktListOfPolygonItem:
                                schnittpunktListOfPolygon.append(sp)

                            iMulti = iMulti + 1

                else:  # single Geometry
                    schnittpunktFeaturesOfThisPolygon = self.makeIntersectionFeatures(
                        feat, tempGeom, laengsProfil, fields, feedback)
                    schnittpunktListOfPolygon = self.makeIntersectionPointsStationList(
                        feat, tempGeom, laengsProfil, fields, feedback)

                #create LineFeatures
                schnittLinesFeats = self.makeLineFeaturesFromPointStationList(
                    feat, schnittpunktListOfPolygon, laengsProfil, feedback)
                for f in schnittLinesFeats:
                    schnittLinesFeatures.append(f)

                #add to list
                for schnittFeat in schnittpunktFeaturesOfThisPolygon:  #Intersection Feature in project.crs()
                    schnittpunktFeatures.append(schnittFeat)
                    #feedback.pushInfo(str(schnittFeat.attributes()))
                ioFeat = ioFeat + 1
                #count Intersections
                countPoints = countPoints + len(
                    schnittpunktFeaturesOfThisPolygon)

        except:
            msg = self.tr(
                "Error: Creating Intersections Geometry {0} Feature {1}"
            ).format(str(type(feat.geometry())), str(feat.attributes()))
            feedback.reportError(msg)
            raise QgsProcessingException(msg)
        msgInfo = self.tr("Intersected Lines: {0} Intersections: {1}").format(
            ioFeat, countPoints)
        feedback.pushInfo(msgInfo)
        return schnittpunktFeatures, schnittLinesFeatures
Ejemplo n.º 19
0
    def matrix_dem_build(self, dem_dataset, height, width, scale, spacing_mm,
                         roi_x_max, roi_x_min, roi_y_min, h_base, z_scale,
                         projected):

        # Calculate DEM parameters
        dem_col = dem_dataset.RasterXSize
        dem_row = dem_dataset.RasterYSize

        geotransform = dem_dataset.GetGeoTransform()
        dem_x_min = geotransform[0]
        dem_y_max = geotransform[3]
        dem_y_min = dem_y_max + dem_row * geotransform[5]
        dem_x_max = dem_x_min + dem_col * geotransform[1]

        spacing_deg = spacing_mm * (roi_x_max - roi_x_min) / width

        row_stl = int(math.ceil(height / spacing_mm) + 1)
        col_stl = int(math.ceil(width / spacing_mm) + 1)
        matrix_dem = [list(range(col_stl)) for i in range(row_stl)]

        var_y = height
        for i in range(row_stl):
            self.updateProgress.emit()
            QApplication.processEvents()
            var_x = 0
            for j in range(col_stl):
                # Model coordinate x(mm), y(mm)
                x_model = round(var_x, 2)
                y_model = round(var_y, 2)

                # Model maps geo_coordinates
                if projected:
                    x = x_model * scale / 1000 + roi_x_min
                    y = y_model * scale / 1000 + roi_y_min
                else:
                    x = x_model * spacing_deg / spacing_mm + roi_x_min
                    y = y_model * spacing_deg / spacing_mm + roi_y_min

                # Model layer geo_coordinates to query z value
                point = QgsPoint(x, y)
                source = self.parameters["crs_map"]
                target = self.parameters["crs_layer"]
                if source != target:
                    transform = QgsCoordinateTransform(source, target)
                    point = transform.transform(point)
                    x = point.x()
                    y = point.y()

                # From x(m) get Column in DEM file
                col_dem = (x - dem_x_min) * dem_col / (dem_x_max - dem_x_min)
                col_dem = int(math.floor(col_dem))
                if col_dem == dem_col:
                    col_dem -= 1
                # From y(m) get Row in DEM file
                row_dem = (dem_y_max - y) * dem_row / (dem_y_max - dem_y_min)
                row_dem = int(math.floor(row_dem))
                if row_dem == dem_row:
                    row_dem -= 1

                # Model coordinate z(mm)
                if col_dem < 0 or row_dem < 0:
                    z_model = 2
                elif self.get_dem_z(dem_dataset, col_dem, row_dem, 1,
                                    1)[0] <= h_base:
                    z_model = 2
                elif math.isnan(
                        self.get_dem_z(dem_dataset, col_dem, row_dem, 1,
                                       1)[0]):
                    z_model = 2
                else:
                    z_model = round(
                        (self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0]
                         - h_base) / scale * 1000 * z_scale, 2) + 2

                matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model)

                var_x += spacing_mm
                if var_x > width:
                    var_x = width
            var_y = spacing_mm * (row_stl - (i + 2))
            if self.quit:
                return 0
        return matrix_dem
Ejemplo n.º 20
0
    def btn_calculate(self):

        ret = super(DlgCalculateMQI, self).btn_calculate()
        if not ret:
            return

        self.close()

        crosses_180th, geojsons = self.aoi.bounding_box_gee_geojson()
        val = []
        n = 1

        if self.area_tab.area_fromfile.isChecked():
            for f in self.aoi.get_layer_wgs84().getFeatures():
                # Get an OGR geometry from the QGIS geometry
                geom = f.geometry()
                val.append(geom)
                n += 1

            # stringify json object
            val_string = '{}'.format(json.loads(val[0].asJson()))

            # create ogr geometry
            val_geom = ogr.CreateGeometryFromJson(val_string)
            # simplify polygon to tolerance of 0.003
            val_geom_simplified = val_geom.Simplify(0.003)

            # fetch coordinates from json
            coords = json.loads(
                val_geom_simplified.ExportToJson())['coordinates']
            geometries = json.dumps([{
                "coordinates": coords,
                "type": "Polygon"
            }])

        elif self.area_tab.area_fromadmin.isChecked():
            geometries = json.dumps([{
                "coordinates":
                self.get_admin_poly_geojson()['geometry']['coordinates'][0],
                "type":
                "Polygon"
            }])
        elif self.area_tab.area_frompoint.isChecked():
            point = QgsPointXY(
                float(self.area_tab.area_frompoint_point_x.text()),
                float(self.area_tab.area_frompoint_point_y.text()))
            crs_src = QgsCoordinateReferenceSystem(
                self.area_tab.canvas.mapSettings().destinationCrs().authid())
            point = QgsCoordinateTransform(
                crs_src, self.aoi.crs_dst,
                QgsProject.instance()).transform(point)
            geometries = json.dumps(
                json.loads(QgsGeometry.fromPointXY(point).asJson()))

        payload = {
            'year': self.mqi_setup_tab.use_esa_tg_year.date().year(),
            'lu_matrix': self.mqi_setup_tab.dlg_lu_agg.get_agg_as_list()[1],
            'geojsons': geometries,
            'crs': self.aoi.get_crs_dst_wkt(),
            'crosses_180th': crosses_180th,
            'task_name': self.options_tab.task_name.text(),
            'task_notes': self.options_tab.task_notes.toPlainText()
        }

        resp = run_script(get_script_slug('management-quality'), payload)

        if resp:
            mb.pushMessage(
                QtWidgets.QApplication.translate("MISLAND", "Submitted"),
                QtWidgets.QApplication.translate(
                    "MISLAND",
                    "Management Quality task submitted to Google Earth Engine."
                ),
                level=0,
                duration=5)
        else:
            mb.pushMessage(
                QtWidgets.QApplication.translate("MISLAND", "Error"),
                QtWidgets.QApplication.translate(
                    "MISLAND",
                    "Unable to submit management quality task to Google Earth Engine."
                ),
                level=0,
                duration=5)
Ejemplo n.º 21
0
    def testReprojectionErrorsWhileRendering(self):
        # WKT of a polygon which causes reprojection errors while rendering
        # (apologies for the ridiculously complex wkt, but I can't find a way to reproduce with simplifiction)
        wkt = 'MultiPolygon (((16.93392988400009358 42.77094147300012139, 16.88493899800005238 42.72939687700012712, ' \
              '16.80298912900011032 42.76349518400014915, 16.85816491000014139 42.78400299700011544, ' \
              '16.93392988400009358 42.77094147300012139)),((17.38200931100010393 42.79783763200002511, ' \
              '17.65894616000011297 42.74298737200008702, 17.74887129000009622 42.69456614800010641, ' \
              '17.32374108200008322 42.79083893400003547, 17.38200931100010393 42.79783763200002511)),' \
              '((16.768565300000148 42.97223541900014254, 17.03207441500009622 42.98261139500014849, ' \
              '17.13184655000009116 42.96954987200014386, 17.20020592500009116 42.92177969000012183, ' \
              '16.85141035200010151 42.90070221600008438, 16.65544681100004709 42.92625560099999404, ' \
              '16.70679772200014668 42.96954987200014386, 16.63168379000003938 42.98261139500014849, ' \
              '16.768565300000148 42.97223541900014254)),((17.05567467500011958 43.02895742400001211, ' \
              '17.24024498800011429 43.02277252800014651, 17.74146569100011561 42.83926015800001608, ' \
              '17.70736738400009358 42.88703034100014122, 17.65334906206413734 42.8909283361407887, ' \
              '17.70158573400010482 42.91950022500007833, 17.81175988700005064 42.909862570000044, ' \
              '17.85847538200005147 42.81697418200012351, 18.22413781700009849 42.62807098500009317, ' \
              '18.43735477700010961 42.55921213800017711, 18.4371480710000526 42.4934022020000981, ' \
              '18.49642988400009358 42.41632721600008438, 18.23894290500010129 42.55906810100005089, ' \
              '18.21753991000014139 42.6201032570001388, 18.07601972700010151 42.65131256700003348, ' \
              '18.0432235040000819 42.70205312700007028, 17.90162194100014403 42.75189850500014188, ' \
              '17.8928328790000819 42.79083893400003547, 17.72095787900005348 42.8262393250000315, ' \
              '17.7618921230000808 42.77871328300012976, 17.74870853000004445 42.77204010600017625, ' \
              '17.21387780000011958 42.98261139500014849, 17.04615319100011561 42.9950625670000619, ' \
              '17.00163821700004974 43.05149974200010377, 17.05567467500011958 43.02895742400001211)),' \
              '((16.19467207100007045 43.07440827000000638, 16.254893425000148 43.06854889500006323, ' \
              '16.08716881600014403 43.01146067900008063, 16.04883873800011429 43.06517161700004692, ' \
              '16.19467207100007045 43.07440827000000638)),((16.56275475400011032 43.22898997600010773, ' \
              '16.65951582100009887 43.21596914300012315, 16.72771243600001867 43.16461823100003414, ' \
              '17.19336998800014271 43.12726471600016964, 16.67017662900013875 43.12547435099999404, ' \
              '16.37159264400014536 43.19550202000006323, 16.49642988400006516 43.21808502800014651, ' \
              '16.58326256600014403 43.18866608300005794, 16.52051842500006273 43.22898997600010773, ' \
              '16.56275475400011032 43.22898997600010773)),((16.80681399800010922 43.34247467700005529, ' \
              '16.89234459700011826 43.31220123900006058, 16.84620201900008851 43.27338288000005662, ' \
              '16.62826582100012729 43.26373932500008834, 16.50074303500014139 43.28424713700003679, ' \
              '16.42188561300008587 43.31757233300011478, 16.40577233200011165 43.33270905200011214, ' \
              '16.45346113400009358 43.35317617400009738, 16.42628014400008851 43.39411041900011412, ' \
              '16.44703209700008983 43.39484284100014122, 16.80681399800010922 43.34247467700005529)),' \
              '((16.29818769600012729 43.40363190300011809, 16.30274498800008587 43.38727448100009099, ' \
              '16.39144941500012465 43.34638092700005529, 16.348643425000148 43.33869049700003018, ' \
              '16.20045006600014403 43.40704987200003018, 16.29818769600012729 43.40363190300011809)),' \
              '((16.33415774800010922 43.50153229400014254, 16.3752547540000819 43.49017975500008504, ' \
              '16.21143639400008851 43.49005768400009231, 16.26441491000014139 43.51288483300011478, ' \
              '16.33415774800010922 43.50153229400014254)),((15.67888431100004709 43.64801666900014254, ' \
              '15.74040774800010922 43.62750885600009099, 15.67204837300002396 43.63743724200010377, ' \
              '15.60377037900013875 43.67470937700007028, 15.67888431100004709 43.64801666900014254)),' \
              '((15.36736087300005238 43.79010651200015047, 15.39568118600007551 43.7724063170000619, ' \
              '15.22779381600014403 43.87445709800014981, 15.24073326900014536 43.88076406500009341, ' \
              '15.36736087300005238 43.79010651200015047)),((15.44271894600009887 43.89907461100013109, ' \
              '15.35865319100014403 43.91937897300014981, 15.26124108200011165 44.01105377800003282, ' \
              '15.38404381600008719 43.9701602230000077, 15.44271894600009887 43.89907461100013109)),' \
              '((15.22575931100010393 44.06622955900014915, 15.25440514400008851 44.01788971600014122, ' \
              '15.12183678500014139 44.09223053600005926, 15.06251061300008587 44.16193268400012073, ' \
              '15.22575931100010393 44.06622955900014915)),((14.83545983200014007 44.15102773600013109, ' \
              '14.85726972700010151 44.15204498900000374, 14.86915123800014271 44.14052969000006499, ' \
              '14.83521569100008719 44.14166901200009363, 14.81983483200014007 44.15302155199999845, ' \
              '14.82243899800005238 44.16868724200004692, 14.83545983200014007 44.15102773600013109)),' \
              '((14.98511803500011297 44.09096914300012315, 15.21680748800008587 43.91278717700008372, ' \
              '15.13331139400011693 43.92121002800003282, 15.19450931100004709 43.87262604400017096, ' \
              '15.10661868600007551 43.92544179900015422, 14.84961998800014271 44.17560455900014915, ' \
              '14.98511803500011297 44.09096914300012315)),((14.765961134000122 44.26504140800015819, ' \
              '14.74854576900014536 44.26166413000014188, 14.73959394600012729 44.28017812700015554, ' \
              '14.79167728000007287 44.27252838700003679, 14.765961134000122 44.26504140800015819)),' \
              '((14.66138756600011561 44.30866120000014519, 14.6407983730000808 44.31183502800003282, ' \
              '14.59506269600007045 44.34711334800006455, 14.643565300000148 44.32575104400011412, ' \
              '14.66138756600011561 44.30866120000014519)),((14.81120853000004445 44.35004303600000242, ' \
              '14.75619550900009358 44.36399974200004692, 14.76343834700008983 44.41535065300017493, ' \
              '14.80323326900008851 44.40550364800004957, 14.81120853000004445 44.35004303600000242)),' \
              '((14.27116946700002131 44.61253489800004957, 14.23259524800005238 44.62604401200012205, ' \
              '14.2657983730000808 44.67951080900003547, 14.28044681100007551 44.67755768400009231, ' \
              '14.27116946700002131 44.61253489800004957)),((14.84522545700008322 44.60053131700011875, ' \
              '14.93824303500014139 44.59414297100001079, 15.07553144600007045 44.48407623900006058, ' \
              '14.91114342500011958 44.54547760600014783, 15.04802493600004709 44.43943919500001982, ' \
              '15.09669030000009116 44.41518789300000947, 15.04151451900014536 44.47662995000008834, ' \
              '15.25440514400008851 44.34003327000000638, 15.165049675000148 44.36737702000006323, ' \
              '15.22022545700008322 44.3127302100001117, 15.13086998800008587 44.33258698100003414, ' \
              '15.17237389400014536 44.29913971600016964, 15.12875410200007309 44.31199778900018771, ' \
              '15.08920332100009887 44.37421295800000109, 15.11719811300014271 44.38719310099999404, ' \
              '15.04900149800010922 44.39468008000015686, 14.89747155000009116 44.49091217699999845, ' \
              '14.91863040500010129 44.50454336100013109, 14.87696373800011429 44.55975983300005794, ' \
              '14.73365319100008719 44.70319245000014519, 14.84522545700008322 44.60053131700011875)),' \
              '((14.41000410200010151 44.60097890800001608, 14.52662194100011561 44.50372955900012073, ' \
              '14.53435306100010393 44.48407623900006058, 14.42261803500008455 44.57387929900009738, ' \
              '14.36304772200014668 44.57343170800000109, 14.38257897200014668 44.60325755399999537, ' \
              '14.33578535200007309 44.71678294500010509, 14.39747155000009116 44.6856143250000315, ' \
              '14.41000410200010151 44.60097890800001608)),((14.75326582100007045 44.84585195500012844, ' \
              '14.74048912900011032 44.82050202000000638, 14.82243899800005238 44.77142975500005662, ' \
              '14.84961998800014271 44.70319245000014519, 14.65788821700004974 44.79877350500014188, ' \
              '14.7268172540000819 44.79877350500014188, 14.6858016290000819 44.8471540390000456, ' \
              '14.75326582100007045 44.84585195500012844)),((14.47103925900006516 44.95392487200003018, ' \
              '14.45191491000008455 44.79877350500014188, 14.47217858200011165 44.7079531920000619, ' \
              '14.53435306100010393 44.63426341400010244, 14.51335696700007816 44.618841864000089, ' \
              '14.42790774800005238 44.65656159100014122, 14.29420006600008719 44.9086367860001161, ' \
              '14.30152428500011297 44.94342682500014519, 14.38738040500004445 44.90900299700003018, ' \
              '14.39031009200004974 44.96039459800012139, 14.41138756600008719 44.95636627800014651, ' \
              '14.27849368600004709 45.1133487000000315, 14.29957116000014139 45.16233958499999801, ' \
              '14.35621178500014139 45.16925690300008966, 14.387705925000148 45.03904857000013351, ' \
              '14.47103925900006516 44.95392487200003018)),((14.56332441500012465 45.24974192900008063, ' \
              '14.62378991000011297 45.17548248900006058, 14.59742272200011826 45.16644928600005926, ' \
              '14.66529381600011561 45.16181061400011743, 14.66529381600011561 45.08734772300006455, ' \
              '14.74048912900011032 45.07306549700014386, 14.81495201900008851 44.97748444200009033, ' \
              '14.70639082100009887 44.9467227230000077, 14.62891686300014271 44.97817617400004053, ' \
              '14.62086022200008983 45.04559967700011214, 14.61695397200008983 45.02464427300007799, ' \
              '14.51050866000014139 45.03217194200011875, 14.43873131600014403 45.07050202000006323, ' \
              '14.4670516290000819 45.12409088700015047, 14.53012129000009622 45.13483307500014519, ' \
              '14.53435306100010393 45.23753489800002114, 14.56332441500012465 45.24974192900008063)),' \
              '((16.36947066200013978 46.54057118800012915, 16.63767134600004738 46.47447703100009164, ' \
              '16.75508020000012266 46.38187286400001597, 16.83765913900006694 46.38187286400001597, ' \
              '16.88923221800007468 46.29216257800014489, 17.05294315600005461 46.15346303300005104, ' \
              '17.20859257000006437 46.11656606000003933, 17.27587528500004055 46.01202463800002818, ' \
              '17.31680301900004793 45.99765859000002877, 17.29013798000011093 45.98463612900009423, ' \
              '17.40620324700006449 45.94365671800015605, 17.59110152100009827 45.93621531200012953, ' \
              '17.65652388500006964 45.84541982000014571, 17.80917606600013414 45.81441396100005647, ' \
              '17.85806197100004056 45.77172922800004073, 18.21121870900006456 45.78537180600012846, ' \
              '18.40438521300006869 45.74180857400001798, 18.57347049900010916 45.81668772400014689, ' \
              '18.6556360270001278 45.90758656800015558, 18.7755253500000947 45.88283355700004051, ' \
              '18.90130578600007993 45.93120269800006383, 18.87288374800004931 45.89523590100002082, ' \
              '18.90699019400011593 45.86795074500018643, 18.85531376100007606 45.85735707600009903, ' \
              '18.84497847500006174 45.8157058720000947, 18.96848514800012708 45.66873809800016204, ' \
              '18.90357954900008508 45.57308502200005762, 18.94171675700005153 45.53892689999999277, ' \
              '19.01809452300011571 45.56740061400002162, 19.10625451700005328 45.51164174500017623, ' \
              '19.00961958800010621 45.49867095900005154, 19.00300500400010151 45.45536611000007099, ' \
              '19.03742150900006891 45.42229319300010104, 18.97592655400006834 45.39495636000008005, ' \
              '19.09199182100007874 45.34999786400005917, 19.12475467900009107 45.29811472600006539, ' \
              '19.36308638500014467 45.24824696900010679, 19.40783817500010855 45.20313344400013023, ' \
              '19.39068160000005037 45.16933705700016333, 19.22593713300008744 45.16194732700016345, ' \
              '19.12186079900010327 45.195795390000157, 19.13767378700009658 45.14603098600004216, ' \
              '19.04486291500009543 45.13724599300006446, 19.08227665200013234 45.08494944300004192, ' \
              '19.0872375890000967 44.97710072800013847, 19.13167932100006396 44.95317454000003465, ' \
              '19.06667036900009293 44.90568389900012392, 18.99142948400006503 44.9149339800001286, ' \
              '19.01582076000008215 44.86563466400004074, 18.88962691200009658 44.86119049100013001, ' \
              '18.78338016700013213 44.91374542300012251, 18.79175174900009893 45.00154368100008639, ' \
              '18.73831831900008638 45.0159097290000858, 18.68405806500004473 45.08479441400000098, ' \
              '18.64871138500012648 45.06267689999999959, 18.61667199700013953 45.09766184500010411, ' \
              '18.54959598800010667 45.09476796500011631, 18.51703983500007666 45.05585561200003042, ' \
              '18.23788374800011525 45.15745147700012296, 18.15365116400005263 45.0975584930001645, ' \
              '18.00347945100011771 45.1493382780000303, 17.83573775200005684 45.0644338990000648, ' \
              '17.68473921700012852 45.1639627080000281, 17.48185754400009273 45.11440500900012296, ' \
              '17.49622359200009214 45.1416901650001563, 17.44775109900012922 45.13430043600014585, ' \
              '17.44330692500011537 45.16205068000009248, 17.38243208800008688 45.1396231090000839, ' \
              '17.26895064300006766 45.18954254200015441, 17.24548954300007608 45.15538442000017483, ' \
              '17.18709517400012032 45.14856313100001728, 17.0363033440001459 45.23047027600007652, ' \
              '17.00829471800011561 45.21615590500009318, 17.00829471800011561 45.24416453100009505, ' \
              '16.94731652900014751 45.23568959600000028, 16.9243721930001243 45.28452382500016427, ' \
              '16.81171757000004163 45.18122263700009, 16.52894413300009546 45.22225372400005483, ' \
              '16.38921106000003647 45.11683380099999852, 16.31624393700010955 45.00123362300008978, ' \
              '16.12152714000009723 45.09616322900008356, 16.02044803900011516 45.213933818000001, ' \
              '15.79234826700013627 45.18980092400012438, 15.76361617000014803 44.97555043600003444, ' \
              '15.7308533120001357 44.92723297200008403, 15.77343469200010873 44.84501576800015243, ' \
              '15.71607385200013596 44.80320953400008932, 15.72847619600008784 44.76910308800002269, ' \
              '15.80568078600006743 44.69665273000013883, 15.88877648900006534 44.72424794500012979, ' \
              '15.96897831200004703 44.63924021400013942, 16.02830285600006732 44.62471913700009907, ' \
              '16.04473596200011798 44.58937245700018082, 16.00608199000004106 44.54100331600012908, ' \
              '16.11646285000011858 44.52146962500013672, 16.15966434700004584 44.41610138000002905, ' \
              '16.13827030500004867 44.37760243800015303, 16.20286584400008678 44.35977406800010669, ' \
              '16.18756962000011868 44.28241444999999032, 16.21578495300011014 44.20815541600011045, ' \
              '16.32688928200008149 44.08237498000012522, 16.50103885900011846 43.99271637000008184, ' \
              '16.67859908100004418 43.8406843060001421, 16.71260217300007866 43.77151540100005889, ' \
              '17.03051558500007445 43.54847991900005866, 17.27050093600007585 43.46321380700000248, ' \
              '17.28993127500007176 43.3034302780000786, 17.44206669100009321 43.15243174300015028, ' \
              '17.6284119050001209 43.04657257100008394, 17.66272505700004558 42.96569895500012137, ' \
              '17.63450972400008254 42.950402731000068, 17.51563561300008587 42.95888906500012183, ' \
              '17.47087649800005238 43.01341380400010905, 17.50196373800014271 43.03099192900005221, ' \
              '17.43360436300014271 43.01740143400009231, 17.46021569100011561 43.03099192900005221, ' \
              '17.42611738400009358 43.06517161700004692, 17.4045516290000819 43.05149974200010377, ' \
              '17.31625410200012993 43.12726471600016964, 17.11394290500004445 43.21320221600008438, ' \
              '16.88062584700011826 43.40595123900006058, 16.62582441500009622 43.44904205900009231, ' \
              '16.52466881600011561 43.51080963700009363, 16.39144941500012465 43.51080963700009363, ' \
              '16.47339928500008455 43.5381533870001789, 16.43384850400013875 43.54975006700000506, ' \
              '16.11768639400008851 43.52448151200003679, 16.17237389400014536 43.4896914730000077, ' \
              '16.11312910200004467 43.47890859600009605, 15.95948326900011693 43.50397370000008834, ' \
              '15.987315300000148 43.54490794500010509, 15.92530358200011165 43.55857982000004824, ' \
              '15.91895592500009116 43.62872955900012073, 15.96631920700011165 43.64118073100003414, ' \
              '15.90479576900014536 43.64801666900014254, 15.95297285200010151 43.65086497599999404, ' \
              '15.95045006600008719 43.68854401200015047, 15.70630944100008719 43.76341380400005221, ' \
              '15.6174422540000819 43.82550690300017493, 15.66309655000009116 43.81297435099999404, ' \
              '15.67888431100004709 43.81928131700011875, 15.45508873800014271 43.92804596600014122, ' \
              '15.14454186300011429 44.19546133000015686, 15.15219160200012993 44.23529694200014717, ' \
              '15.11036217500011958 44.26434967700011214, 15.14063561300011429 44.28245677300013483, ' \
              '15.17660566500009622 44.24994538000005662, 15.20777428500008455 44.27277252800014651, ' \
              '15.19809004000012465 44.30166250200007028, 15.295258009000122 44.25067780199999845, ' \
              '15.30274498800008587 44.29913971600016964, 15.26124108200011165 44.33258698100003414, ' \
              '15.42448978000001603 44.26797109600006763, 15.52865644600009887 44.27179596600008438, ' \
              '15.30795332100009887 44.35439687700007028, 15.00733483200014007 44.56972890800012976, ' \
              '14.883799675000148 44.7236188820001388, 14.883799675000148 44.86147695500012844, 14.92164147200008983 ' \
              '44.95880768400009231, 14.85279381600011561 45.09365469000000815, 14.65788821700004974 ' \
              '45.19660065300017493, 14.57081139400008851 45.29364655200011214, 14.31153405000009116 ' \
              '45.34398021000005485, 14.23259524800005238 45.14935944200000506, 14.17937259200007816 ' \
              '45.13450755400005221, 14.19312584700008983 45.10561758000012844, 14.14389082100007045 ' \
              '45.05939362200003018, 14.151377800000148 44.97748444200009033, 14.06885826900014536 ' \
              '44.94953034100014122, 14.08383222700007309 44.9863955750000315, 14.04029381600014403 ' \
              '45.03896719000015025, 14.0756942070000548 44.98371002800003282, 14.02051842500011958 ' \
              '44.90110911700004692, 13.97266686300011429 44.90110911700004692, 13.99301191500009622 ' \
              '44.88129303600014453, 13.97266686300011429 44.82664622599999404, 14.00001061300008587 ' \
              '44.81305573100003414, 13.89014733200011165 44.83348216400010244, 13.91797936300014271 ' \
              '44.77826569200009033, 13.90316816500009622 44.77240631700014717, 13.89698326900011693 ' \
              '44.81305573100003414, 13.78711998800014271 44.87506745000008834, 13.84229576900008851 ' \
              '44.88812897300006455, 13.79460696700010658 44.89496491100008768, 13.77409915500007287 ' \
              '44.96381256700014717, 13.6232202480000808 45.07306549700014386, 13.61255944100014403 ' \
              '45.11786530199999845, 13.72624759200004974 45.13450755400005221, 13.5959578790000819 ' \
              '45.14541250200001343, 13.57545006600011561 45.26487864800007799, 13.60271243600001867 ' \
              '45.28534577000012007, 13.57545006600011561 45.30646393400006389, 13.60954837300005238 ' \
              '45.32013580900017757, 13.54127037900013875 45.34613678600005926, 13.50709069100014403 ' \
              '45.51190827000000638, 13.62901778100007277 45.45898346000016943, 13.75929406800014476 ' \
              '45.46316925100011019, 13.88900191200011136 45.42363678000005223, 13.98263960800005634 ' \
              '45.47531321200001742, 13.97189091000012695 45.5142255660000643, 14.09291711400010172 ' \
              '45.47391794800002174, 14.21869755100007637 45.49717234400004884, 14.37279667100006009 ' \
              '45.47784535800009564, 14.4689148350000778 45.52559438100014688, 14.49857710800012001 ' \
              '45.59618438800005435, 14.58094934100009255 45.66780792200010808, 14.66848921700008646 ' \
              '45.53396596300005683, 14.79716353300005949 45.46518463200006011, 14.88160282300009385 ' \
              '45.46978383400001178, 14.9226339110000481 45.51494903600017494, 15.13926151500010064 ' \
              '45.43004465799999991, 15.32519331800011742 45.45283396399999276, 15.36136682100004691 ' \
              '45.48203114900003641, 15.29666792800006192 45.52295888300012905, 15.2685559480001416 ' \
              '45.60166208900012919, 15.37376916500011248 45.64021270800010655, 15.25501672300006817 ' \
              '45.72346344000011698, 15.42906294700014769 45.77529490200011253, 15.45128381300008868 ' \
              '45.81513743100013869, 15.67607629400006886 45.84169911700014666, 15.65943648300003588 ' \
              '45.88882802400014782, 15.69798710100010908 46.0362092080000167, 15.58988000500005455 ' \
              '46.11351715100001059, 15.62284956800010605 46.19170359400006021, 16.01920780400010358 ' \
              '46.29882883700007312, 16.05961877400008575 46.33231516600015709, 16.0579651280001201 ' \
              '46.37753204400003426, 16.2756262620000598 46.37316538500006402, 16.23490523300009158 ' \
              '46.4933389280001137, 16.36947066200013978 46.54057118800012915))) '
        geom = QgsGeometry.fromWkt(wkt)
        f = QgsFeature()
        f.setGeometry(geom)

        image = QImage(200, 200, QImage.Format_RGB32)

        painter = QPainter()
        ms = QgsMapSettings()
        crs = QgsCoordinateReferenceSystem.fromProj4('+proj=ortho +lat_0=36.5 +lon_0=-118.8 +x_0=0 +y_0=0 +a=6371000 +b=6371000 +units=m +no_defs')
        self.assertTrue(crs.isValid())
        ms.setDestinationCrs(crs)
        ms.setExtent(QgsRectangle(1374999.8, 3912610.7, 4724462.5, 6505499.6))
        ms.setOutputSize(image.size())
        context = QgsRenderContext.fromMapSettings(ms)
        context.setPainter(painter)
        context.setScaleFactor(96 / 25.4)  # 96 DPI
        ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('epsg:4326'),
                                    crs, QgsProject.instance())
        self.assertTrue(ct.isValid())
        context.setCoordinateTransform(ct)
        context.setExtent(ct.transformBoundingBox(ms.extent(), QgsCoordinateTransform.ReverseTransform))

        fill_symbol = QgsFillSymbol.createSimple({'color': '#ffffff', 'outline_color': '#ffffff', 'outline_width': '10'})

        painter.begin(image)
        try:
            image.fill(QColor(0, 0, 0))
            fill_symbol.startRender(context)
            fill_symbol.renderFeature(f, context)
            fill_symbol.stopRender(context)
        finally:
            painter.end()

        assert self.imageCheck('Reprojection errors polygon', 'reprojection_errors_polygon', image)

        #also test linestring
        linestring = QgsGeometry(geom.constGet().boundary())
        f.setGeometry(linestring)
        line_symbol = QgsLineSymbol.createSimple({'color': '#ffffff', 'outline_width': '10'})

        image = QImage(200, 200, QImage.Format_RGB32)
        painter.begin(image)
        try:
            image.fill(QColor(0, 0, 0))
            line_symbol.startRender(context)
            line_symbol.renderFeature(f, context)
            line_symbol.stopRender(context)
        finally:
            painter.end()

        assert self.imageCheck('Reprojection errors linestring', 'reprojection_errors_linestring', image)
Ejemplo n.º 22
0
import qgis
import pymgrs
from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform

iface = qgis.utils.iface

iface.mainWindow().statusBar().show()

sourceCrs = QgsCoordinateReferenceSystem(
    iface.mapCanvas().mapSettings().destinationCrs().postgisSrid())
destCrs = QgsCoordinateReferenceSystem(4326)
xform = QgsCoordinateTransform(sourceCrs,
                               destCrs)  #you can also do reverse convertion


def updateSourceCrs():
    global sourceCrs
    global destCrs
    global xform
    sourceCrs = QgsCoordinateReferenceSystem(
        iface.mapCanvas().mapSettings().destinationCrs().postgisSrid())
    destCrs = QgsCoordinateReferenceSystem(4326)
    xform = QgsCoordinateTransform(
        sourceCrs, destCrs)  #you can also do reverse convertion


# bind signal
def showCoords(xy):
    point = xform.transform(xy)
    iface.mainWindow().statusBar().showMessage(
        pymgrs.LLtoMGRS(point.y(), point.x()))
Ejemplo n.º 23
0
    def processAlgorithm(self, parameters, context, feedback):
        feedback.setProgress(1)

        extent = self.parameterAsExtent(parameters, self.EXTENT, context)
        min_zoom = self.parameterAsInt(parameters, self.ZOOM_MIN, context)
        max_zoom = self.parameterAsInt(parameters, self.ZOOM_MAX, context)
        dpi = self.parameterAsInt(parameters, self.DPI, context)
        tile_format = self.formats[self.parameterAsEnum(
            parameters, self.TILE_FORMAT, context)]
        output_format = self.outputs[self.parameterAsEnum(
            parameters, self.OUTPUT_FORMAT, context)]
        if output_format == 'Directory':
            output_dir = self.parameterAsString(parameters,
                                                self.OUTPUT_DIRECTORY, context)
            if not output_dir:
                raise QgsProcessingException(
                    self.tr('You need to specify output directory.'))
        else:  # MBTiles
            output_file = self.parameterAsString(parameters, self.OUTPUT_FILE,
                                                 context)
            if not output_file:
                raise QgsProcessingException(
                    self.tr('You need to specify output filename.'))
        tile_width = 256
        tile_height = 256

        wgs_crs = QgsCoordinateReferenceSystem('EPSG:4326')
        dest_crs = QgsCoordinateReferenceSystem('EPSG:3857')

        project = context.project()
        src_to_wgs = QgsCoordinateTransform(project.crs(), wgs_crs,
                                            context.transformContext())
        wgs_to_dest = QgsCoordinateTransform(wgs_crs, dest_crs,
                                             context.transformContext())

        settings = QgsMapSettings()
        settings.setOutputImageFormat(QImage.Format_ARGB32_Premultiplied)
        settings.setDestinationCrs(dest_crs)
        settings.setLayers(self.layers)
        settings.setOutputDpi(dpi)

        wgs_extent = src_to_wgs.transformBoundingBox(extent)
        wgs_extent = [
            wgs_extent.xMinimum(),
            wgs_extent.yMinimum(),
            wgs_extent.xMaximum(),
            wgs_extent.yMaximum()
        ]

        metatiles_by_zoom = {}
        metatiles_count = 0
        for zoom in range(min_zoom, max_zoom + 1):
            metatiles = get_metatiles(wgs_extent, zoom, 4)
            metatiles_by_zoom[zoom] = metatiles
            metatiles_count += len(metatiles)

        lab_buffer_px = 100
        progress = 0

        tile_params = {
            'format': tile_format,
            'quality': 75,
            'width': tile_width,
            'height': tile_height
        }
        if output_format == 'Directory':
            writer = DirectoryWriter(output_dir, tile_params)
        else:
            writer = MBTilesWriter(output_file, tile_params, wgs_extent,
                                   min_zoom, max_zoom)

        for zoom in range(min_zoom, max_zoom + 1):
            feedback.pushConsoleInfo('Generating tiles for zoom level: %s' %
                                     zoom)

            for i, metatile in enumerate(metatiles_by_zoom[zoom]):
                size = QSize(tile_width * metatile.rows(),
                             tile_height * metatile.columns())
                extent = QgsRectangle(*metatile.extent())
                settings.setExtent(wgs_to_dest.transformBoundingBox(extent))
                settings.setOutputSize(size)

                label_area = QgsRectangle(settings.extent())
                lab_buffer = label_area.width() * (lab_buffer_px /
                                                   size.width())
                label_area.set(label_area.xMinimum() + lab_buffer,
                               label_area.yMinimum() + lab_buffer,
                               label_area.xMaximum() - lab_buffer,
                               label_area.yMaximum() - lab_buffer)
                settings.setLabelBoundaryGeometry(
                    QgsGeometry.fromRect(label_area))

                image = QImage(size, QImage.Format_ARGB32_Premultiplied)
                image.fill(Qt.transparent)
                dpm = settings.outputDpi() / 25.4 * 1000
                image.setDotsPerMeterX(dpm)
                image.setDotsPerMeterY(dpm)
                painter = QPainter(image)
                job = QgsMapRendererCustomPainterJob(settings, painter)
                job.renderSynchronously()
                painter.end()

                # For analysing metatiles (labels, etc.)
                # metatile_dir = os.path.join(output_dir, str(zoom))
                # os.makedirs(metatile_dir, exist_ok=True)
                # image.save(os.path.join(metatile_dir, 'metatile_%s.png' % i))

                for r, c, tile in metatile.tiles:
                    tile_img = image.copy(tile_width * r, tile_height * c,
                                          tile_width, tile_height)
                    writer.writeTile(tile, tile_img)

                progress += 1
                feedback.setProgress(100 * (progress / metatiles_count))

        writer.close()

        results = {}
        if output_format == 'Directory':
            results['OUTPUT_DIRECTORY'] = output_dir
        else:  # MBTiles
            results['OUTPUT_FILE'] = output_file
        return results
Ejemplo n.º 24
0
    def processAlgorithm(self, parameters, context, feedback):

        source = self.parameterAsSource(
            parameters,
            self.INPUT,
            context
        )

        sampled_raster = self.parameterAsRasterLayer(
            parameters,
            self.RASTERCOPY,
            context
        )

        columnPrefix = self.parameterAsString(
            parameters,
            self.COLUMN_PREFIX,
            context
        )

        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        source_fields = source.fields()
        raster_fields = QgsFields()

        # append field to vector as columnPrefix_bandCount
        for b in range(sampled_raster.bandCount()):
            raster_fields.append(QgsField(
                columnPrefix + str('_{}'.format(b + 1)), QVariant.Double
            )
            )

        # combine all the vector fields
        out_fields = QgsProcessingUtils.combineFields(source_fields, raster_fields)

        (sink, dest_id) = self.parameterAsSink(
            parameters,
            self.OUTPUT,
            context,
            out_fields,
            source.wkbType(),
            source.sourceCrs()
        )

        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

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

        # create the coordinates transformation context
        ct = QgsCoordinateTransform(source.sourceCrs(), sampled_raster.crs(), context.transformContext())

        for n, i in enumerate(source.getFeatures()):

            attrs = i.attributes()

            if i.geometry().isMultipart() and i.geometry().constGet().partCount() > 1:
                sink.addFeature(i, QgsFeatureSink.FastInsert)
                feedback.setProgress(int(n * total))
                feedback.reportError(self.tr('Impossible to sample data of multipart feature {}.').format(i.id()))
                continue

            # get the feature geometry as point
            point = QgsPointXY()
            if i.geometry().isMultipart():
                point = i.geometry().asMultiPoint()[0]
            else:
                point = i.geometry().asPoint()

            # reproject to raster crs
            try:
                point = ct.transform(point)
            except QgsCsException:
                for b in range(sampled_raster.bandCount()):
                    attrs.append(None)
                i.setAttributes(attrs)
                sink.addFeature(i, QgsFeatureSink.FastInsert)
                feedback.setProgress(int(n * total))
                feedback.reportError(self.tr('Could not reproject feature {} to raster CRS').format(i.id()))
                continue

            for b in range(sampled_raster.bandCount()):
                value, ok = sampled_raster.dataProvider().sample(point, b + 1)
                if ok:
                    attrs.append(value)
                else:
                    attrs.append(NULL)

            i.setAttributes(attrs)

            sink.addFeature(i, QgsFeatureSink.FastInsert)
            feedback.setProgress(int(n * total))

        return {self.OUTPUT: dest_id}
def processLine(source, sink, feedback, discardVertices):
    layercrs = source.sourceCrs()
    if layercrs != epsg4326:
        transto4326 = QgsCoordinateTransform(layercrs, epsg4326,
                                             QgsProject.instance())
        transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs,
                                               QgsProject.instance())

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

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

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

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

                    fline.setGeometry(QgsGeometry.fromMultiPolylineXY(outseg))

            fline.setAttributes(feature.attributes())
            sink.addFeature(fline)
        except:
            num_bad += 1
            #traceback.print_exc()
            pass
        feedback.setProgress(int(cnt * total))

    return num_bad
Ejemplo n.º 26
0
    def test_insert_srsName(self):
        """Test srsName is respected when insering"""

        post_data = """
        <Transaction xmlns="http://www.opengis.net/wfs" xsi:schemaLocation="http://www.qgis.org/gml http://localhost:8000/?SERVICE=WFS&amp;REQUEST=DescribeFeatureType&amp;VERSION=1.0.0&amp;TYPENAME=as_symbols" service="WFS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="{version}" xmlns:gml="http://www.opengis.net/gml">
            <Insert xmlns="http://www.opengis.net/wfs">
                <as_symbols xmlns="http://www.qgis.org/gml">
                <name xmlns="http://www.qgis.org/gml">{name}</name>
                <geometry xmlns="http://www.qgis.org/gml">
                    <gml:Point srsName="{srsName}">
                    <gml:coordinates cs="," ts=" ">{coordinates}</gml:coordinates>
                    </gml:Point>
                </geometry>
                </as_symbols>
            </Insert>
        </Transaction>
        """

        project = self.testdata_path + \
            "test_project_wms_grouped_layers.qgs"
        assert os.path.exists(project), "Project file not found: " + project

        query_string = '?SERVICE=WFS&MAP={}'.format(
            urllib.parse.quote(project))
        request = post_data.format(
            name='4326-test1',
            version='1.1.0',
            srsName='EPSG:4326',
            coordinates='10.67,52.48'
        )
        header, body = self._execute_request(
            query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8'))

        # Verify
        vl = QgsVectorLayer(self.testdata_path + 'test_project_wms_grouped_layers.gpkg|layername=as_symbols', 'as_symbols')
        self.assertTrue(vl.isValid())
        feature = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'4326-test1\''))))
        geom = feature.geometry()

        tr = QgsCoordinateTransform(QgsCoordinateReferenceSystem.fromEpsgId(4326), vl.crs(), QgsCoordinateTransformContext())

        geom_4326 = QgsGeometry.fromWkt('point( 10.67 52.48)')
        geom_4326.transform(tr)
        self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0))

        # Now: insert a feature in layer's CRS
        request = post_data.format(
            name='25832-test1',
            version='1.1.0',
            srsName='EPSG:25832',
            coordinates='613412,5815738'
        )
        header, body = self._execute_request(
            query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8'))

        feature = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'25832-test1\''))))
        geom = feature.geometry()
        self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0))

        # Tests for inverted axis issue GH #36584
        # Cleanup
        self.assertTrue(vl.startEditing())
        vl.selectByExpression('"name" LIKE \'4326-test%\'')
        vl.deleteSelectedFeatures()
        self.assertTrue(vl.commitChanges())

        self.i = 0

        def _test(version, srsName, lat_lon=False):
            self.i += 1
            name = '4326-test_%s' % self.i
            request = post_data.format(
                name=name,
                version=version,
                srsName=srsName,
                coordinates='52.48,10.67' if lat_lon else '10.67,52.48'
            )
            header, body = self._execute_request(
                query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8'))
            feature = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'%s\'' % name))))
            geom = feature.geometry()
            self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0), "Transaction Failed: %s , %s, lat_lon=%s" % (version, srsName, lat_lon))

        _test('1.1.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
        _test('1.1.0', 'http://www.opengis.net/def/crs/EPSG/0/4326', lat_lon=True)
        _test('1.1.0', 'http://www.opengis.net/gml/srs/epsg.xml#4326', lat_lon=False)
        _test('1.1.0', 'EPSG:4326', lat_lon=False)

        _test('1.0.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
        _test('1.0.0', 'http://www.opengis.net/def/crs/EPSG/0/4326', lat_lon=True)
        _test('1.0.0', 'http://www.opengis.net/gml/srs/epsg.xml#4326', lat_lon=False)
        _test('1.0.0', 'EPSG:4326', lat_lon=False)

        def _test_getFeature(version, srsName, lat_lon=False):
            # Now get the feature through WFS using BBOX filter
            bbox = QgsGeometry.fromWkt('point( 10.7006 52.4317)').boundingBox()
            bbox.grow(0.0001)
            bbox_text = "%s,%s,%s,%s" % ((bbox.yMinimum(), bbox.xMinimum(), bbox.yMaximum(), bbox.xMaximum()) if lat_lon else (bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()))
            req = query_string + '&REQUEST=GetFeature&VERSION={version}&TYPENAME=as_symbols&SRSNAME={srsName}&BBOX={bbox},{srsName}'.format(version=version, srsName=srsName, bbox=bbox_text)
            header, body = self._execute_request(req)
            self.assertTrue(b'gid>7' in body, "GetFeature Failed: %s , %s, lat_lon=%s" % (version, srsName, lat_lon))

        _test_getFeature('1.1.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
        _test_getFeature('1.1.0', 'EPSG:4326', lat_lon=False)

        _test_getFeature('1.0.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True)
        _test_getFeature('1.0.0', 'EPSG:4326', lat_lon=False)

        # Cleanup
        self.assertTrue(vl.startEditing())
        vl.selectByExpression('"name" LIKE \'4326-test%\'')
        vl.deleteSelectedFeatures()
        self.assertTrue(vl.commitChanges())
Ejemplo n.º 27
0
    def processAlgorithm(self, parameters, context, feedback):
        expression = self.parameterAsString(parameters, self.EXPRESSION,
                                            context)
        layers = self.parameterAsLayerList(parameters, self.LAYERS, context)

        layersDict = {}
        if layers:
            layersDict = {lyr.source(): lyr for lyr in layers}

        crs = self.parameterAsCrs(parameters, self.CRS, context)
        if crs is None or not crs.isValid():
            if not layers:
                raise QgsProcessingException(
                    self.tr("No reference layer selected nor CRS provided"))
            else:
                crs = list(layersDict.values())[0].crs()

        bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
        if bbox.isNull() and not layers:
            raise QgsProcessingException(
                self.tr("No reference layer selected nor extent box provided"))

        if not bbox.isNull():
            bboxCrs = self.parameterAsExtentCrs(parameters, self.EXTENT,
                                                context)
            if bboxCrs != crs:
                transform = QgsCoordinateTransform(bboxCrs, crs,
                                                   context.project())
                bbox = transform.transformBoundingBox(bbox)

        if bbox.isNull() and layers:
            bbox = QgsProcessingUtils.combineLayerExtents(layers, crs)

        cellsize = self.parameterAsDouble(parameters, self.CELLSIZE, context)
        if cellsize == 0 and not layers:
            raise QgsProcessingException(
                self.tr(
                    "No reference layer selected nor cellsize value provided"))

        def _cellsize(layer):
            ext = layer.extent()
            if layer.crs() != crs:
                transform = QgsCoordinateTransform(layer.crs(), crs,
                                                   context.project())
                ext = transform.transformBoundingBox(ext)
            return (ext.xMaximum() - ext.xMinimum()) / layer.width()

        if cellsize == 0:
            cellsize = min([_cellsize(lyr) for lyr in layersDict.values()])

        # check for layers available in the model
        layersDictCopy = layersDict.copy(
        )  # need a shallow copy because next calls invalidate iterator
        for lyr in layersDictCopy.values():
            expression = self.mappedNameToLayer(lyr, expression, layersDict,
                                                context)

        # check for layers available in the project
        for lyr in QgsProcessingUtils.compatibleRasterLayers(
                context.project()):
            expression = self.mappedNameToLayer(lyr, expression, layersDict,
                                                context)

        # create the list of layers to be passed as inputs to RasterCalculaltor
        # at this phase expression has been modified to match available layers
        # in the current scope
        entries = []
        for name, lyr in layersDict.items():
            for n in range(lyr.bandCount()):
                ref = '{:s}@{:d}'.format(name, n + 1)

                if ref in expression:
                    entry = QgsRasterCalculatorEntry()
                    entry.ref = ref
                    entry.raster = lyr
                    entry.bandNumber = n + 1
                    entries.append(entry)

        # Append any missing entry from the current project
        for entry in QgsRasterCalculatorEntry.rasterEntries():
            if not [e for e in entries if e.ref == entry.ref]:
                entries.append(entry)

        output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)

        width = round((bbox.xMaximum() - bbox.xMinimum()) / cellsize)
        height = round((bbox.yMaximum() - bbox.yMinimum()) / cellsize)
        driverName = GdalUtils.getFormatShortNameFromFilename(output)

        calc = QgsRasterCalculator(expression, output, driverName, bbox, crs,
                                   width, height, entries)

        res = calc.processCalculation(feedback)
        if res == QgsRasterCalculator.ParserError:
            raise QgsProcessingException(self.tr("Error parsing formula"))

        return {self.OUTPUT: output}
Ejemplo n.º 28
0
    def run(self, layers):
        """Experimental impact function.

        :param layers: List of layers expected to contain at least:
            H: Polygon layer of inundation areas
            E: Vector layer of roads
        :type layers: list

        :returns: A new line layer with inundated roads marked.
        :type: safe_layer
        """
        target_field = self.parameters['target_field']
        road_type_field = self.parameters['road_type_field']
        threshold_min = self.parameters['min threshold [m]']
        threshold_max = self.parameters['max threshold [m]']

        if threshold_min > threshold_max:
            message = tr(
                'The minimal threshold is greater then the maximal specified '
                'threshold. Please check the values.')
            raise GetDataError(message)

        # Extract data
        H = get_hazard_layer(layers)  # Flood
        E = get_exposure_layer(layers)  # Roads

        question = get_question(H.get_name(), E.get_name(), self)

        H = H.get_layer()
        E = E.get_layer()

        # reproject self.extent to the hazard projection
        hazard_crs = H.crs()
        hazard_authid = hazard_crs.authid()

        if hazard_authid == 'EPSG:4326':
            viewport_extent = self.extent
        else:
            geo_crs = QgsCoordinateReferenceSystem()
            geo_crs.createFromSrid(4326)
            viewport_extent = extent_to_geo_array(QgsRectangle(*self.extent),
                                                  geo_crs, hazard_crs)

        # Align raster extent and viewport
        # assuming they are both in the same projection
        raster_extent = H.dataProvider().extent()
        clip_xmin = raster_extent.xMinimum()
        # clip_xmax = raster_extent.xMaximum()
        clip_ymin = raster_extent.yMinimum()
        # clip_ymax = raster_extent.yMaximum()
        if viewport_extent[0] > clip_xmin:
            clip_xmin = viewport_extent[0]
        if viewport_extent[1] > clip_ymin:
            clip_ymin = viewport_extent[1]
        # TODO: Why have these two clauses when they are not used?
        # Commenting out for now.
        # if viewport_extent[2] < clip_xmax:
        #     clip_xmax = viewport_extent[2]
        # if viewport_extent[3] < clip_ymax:
        #     clip_ymax = viewport_extent[3]

        height = ((viewport_extent[3] - viewport_extent[1]) /
                  H.rasterUnitsPerPixelY())
        height = int(height)
        width = ((viewport_extent[2] - viewport_extent[0]) /
                 H.rasterUnitsPerPixelX())
        width = int(width)

        raster_extent = H.dataProvider().extent()
        xmin = raster_extent.xMinimum()
        xmax = raster_extent.xMaximum()
        ymin = raster_extent.yMinimum()
        ymax = raster_extent.yMaximum()

        x_delta = (xmax - xmin) / H.width()
        x = xmin
        for i in range(H.width()):
            if abs(x - clip_xmin) < x_delta:
                # We have found the aligned raster boundary
                break
            x += x_delta
            _ = i

        y_delta = (ymax - ymin) / H.height()
        y = ymin
        for i in range(H.width()):
            if abs(y - clip_ymin) < y_delta:
                # We have found the aligned raster boundary
                break
            y += y_delta
        clip_extent = [x, y, x + width * x_delta, y + height * y_delta]

        # Clip and polygonize
        small_raster = clip_raster(H, width, height,
                                   QgsRectangle(*clip_extent))
        (flooded_polygon_inside,
         flooded_polygon_outside) = polygonize_gdal(small_raster,
                                                    threshold_min,
                                                    threshold_max)

        # Filter geometry and data using the extent
        extent = QgsRectangle(*self.extent)
        request = QgsFeatureRequest()
        request.setFilterRect(extent)

        if flooded_polygon_inside is None:
            message = tr(
                'There are no objects in the hazard layer with "value">%s.'
                'Please check the value or use other extent.' %
                (threshold_min, ))
            raise GetDataError(message)

        # reproject the flood polygons to exposure projection
        exposure_crs = E.crs()
        exposure_authid = exposure_crs.authid()

        if hazard_authid != exposure_authid:
            flooded_polygon_inside = reproject_vector_layer(
                flooded_polygon_inside, E.crs())
            flooded_polygon_outside = reproject_vector_layer(
                flooded_polygon_outside, E.crs())

        # Clip exposure by the extent
        # extent_as_polygon = QgsGeometry().fromRect(extent)
        # no need to clip since It is using a bbox request
        # line_layer = clip_by_polygon(
        #    E,
        #    extent_as_polygon
        # )
        # Find inundated roads, mark them
        line_layer = split_by_polygon_in_out(E, flooded_polygon_inside,
                                             flooded_polygon_outside,
                                             target_field, 1, request)

        target_field_index = line_layer.dataProvider().\
            fieldNameIndex(target_field)

        # Generate simple impact report
        epsg = get_utm_epsg(self.extent[0], self.extent[1])
        output_crs = QgsCoordinateReferenceSystem(epsg)
        transform = QgsCoordinateTransform(E.crs(), output_crs)
        road_len = flooded_len = 0  # Length of roads
        roads_by_type = dict()  # Length of flooded roads by types

        roads_data = line_layer.getFeatures()
        road_type_field_index = line_layer.fieldNameIndex(road_type_field)
        for road in roads_data:
            attributes = road.attributes()
            road_type = attributes[road_type_field_index]
            if road_type.__class__.__name__ == 'QPyNullVariant':
                road_type = tr('Other')
            geom = road.geometry()
            geom.transform(transform)
            length = geom.length()
            road_len += length

            if road_type not in roads_by_type:
                roads_by_type[road_type] = {'flooded': 0, 'total': 0}
            roads_by_type[road_type]['total'] += length

            if attributes[target_field_index] == 1:
                flooded_len += length
                roads_by_type[road_type]['flooded'] += length
        table_body = [
            question,
            TableRow([
                tr('Road Type'),
                tr('Flooded in the threshold (m)'),
                tr('Total (m)')
            ],
                     header=True),
            TableRow([tr('All'), int(flooded_len),
                      int(road_len)])
        ]
        table_body.append(TableRow(tr('Breakdown by road type'), header=True))
        for t, v in roads_by_type.iteritems():
            table_body.append(TableRow([t,
                                        int(v['flooded']),
                                        int(v['total'])]))

        impact_summary = Table(table_body).toNewlineFreeString()
        map_title = tr('Roads inundated')

        style_classes = [
            dict(label=tr('Not Inundated'),
                 value=0,
                 colour='#1EFC7C',
                 transparency=0,
                 size=0.5),
            dict(label=tr('Inundated'),
                 value=1,
                 colour='#F31A1C',
                 transparency=0,
                 size=0.5)
        ]
        style_info = dict(target_field=target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # Convert QgsVectorLayer to inasafe layer and return it
        line_layer = Vector(data=line_layer,
                            name=tr('Flooded roads'),
                            keywords={
                                'impact_summary': impact_summary,
                                'map_title': map_title,
                                'target_field': target_field
                            },
                            style_info=style_info)
        return line_layer
Ejemplo n.º 29
0
    def print_map(self, mode='pdf'):
        """Open impact report dialog that used define report options.

        :param mode: Mode for report - defaults to PDF.
        :type mode:
        """
        # Check if selected layer is valid
        impact_layer = self.iface.activeLayer()
        if impact_layer is None:
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            QtGui.QMessageBox.warning(
                self.parent, self.tr('InaSAFE'),
                self.tr('Please select a valid impact layer before trying to '
                        'print.'))
            return

        # Open Impact Report Dialog
        print_dialog = ImpactReportDialog(self.iface)
        print_dialog.button_ok = QtGui.QPushButton(self.tr('OK'))
        print_dialog.button_box.addButton(print_dialog.button_ok,
                                          QtGui.QDialogButtonBox.ActionRole)

        # noinspection PyUnresolvedReferences
        print_dialog.button_ok.clicked.connect(print_dialog.accept)

        print_dialog.button_save_pdf.hide()
        print_dialog.button_open_composer.hide()

        if not print_dialog.exec_() == QtGui.QDialog.Accepted:
            # noinspection PyTypeChecker
            self.show_dynamic_message(
                self,
                m.Message(m.Heading(self.tr('Map Creator'), **WARNING_STYLE),
                          m.Text(self.tr('Report generation cancelled!'))))
            return

        # Get the extent of the map for report
        use_full_extent = print_dialog.analysis_extent_radio.isChecked()
        if use_full_extent:
            map_crs = self.iface.mapCanvas().mapRenderer().destinationCrs()
            layer_crs = self.iface.activeLayer().crs()
            layer_extent = self.iface.activeLayer().extent()
            if map_crs != layer_crs:
                # noinspection PyCallingNonCallable
                transform = QgsCoordinateTransform(layer_crs, map_crs)
                layer_extent = transform.transformBoundingBox(layer_extent)
            area_extent = layer_extent
        else:
            area_extent = self.iface.mapCanvas().extent()

        # Get selected template path to use
        if print_dialog.default_template_radio.isChecked():
            template_path = print_dialog.template_combo.itemData(
                print_dialog.template_combo.currentIndex())
        else:
            template_path = print_dialog.template_path.text()
            if not os.path.exists(template_path):
                # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
                QtGui.QMessageBox.warning(
                    self.parent, self.tr('InaSAFE'),
                    self.tr('Please select a valid template before printing. '
                            'The template you choose does not exist.'))
                return

        # Instantiate and prepare Report
        # noinspection PyTypeChecker
        self.show_dynamic_message(
            self,
            m.Message(
                m.Heading(self.tr('Map Creator'), **PROGRESS_UPDATE_STYLE),
                m.Text(self.tr('Preparing map and report'))))

        impact_report = ImpactReport(self.iface, template_path, impact_layer)
        impact_report.extent = area_extent

        # Get other setting
        settings = QSettings()
        logo_path = settings.value('inasafe/organisation_logo_path',
                                   '',
                                   type=str)
        impact_report.organisation_logo = logo_path

        disclaimer_text = settings.value('inasafe/reportDisclaimer',
                                         '',
                                         type=str)
        impact_report.disclaimer = disclaimer_text

        north_arrow_path = settings.value('inasafe/north_arrow_path',
                                          '',
                                          type=str)
        impact_report.north_arrow = north_arrow_path

        template_warning_verbose = bool(
            settings.value('inasafe/template_warning_verbose', True,
                           type=bool))

        # Check if there's missing elements needed in the template
        component_ids = [
            'safe-logo', 'north-arrow', 'organisation-logo', 'impact-map',
            'impact-legend'
        ]
        impact_report.component_ids = component_ids
        if template_warning_verbose and \
                len(impact_report.missing_elements) != 0:
            title = self.tr('Template is missing some elements')
            question = self.tr(
                'The composer template you are printing to is missing '
                'these elements: %s. Do you still want to continue') % (
                    ', '.join(impact_report.missing_elements))
            # noinspection PyCallByClass,PyTypeChecker
            answer = QtGui.QMessageBox.question(
                self.parent, title, question,
                QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
            if answer == QtGui.QMessageBox.No:
                return

        create_pdf_flag = bool(mode == 'pdf')
        self.show_busy()
        if create_pdf_flag:
            self.print_map_to_pdf(impact_report)
        else:
            self.open_map_in_composer(impact_report)

        self.hide_busy()
Ejemplo n.º 30
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))

        join_source = self.parameterAsSource(parameters, self.JOIN, context)
        if join_source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.JOIN))

        join_fields = self.parameterAsFields(parameters, self.JOIN_FIELDS,
                                             context)
        discard_nomatch = self.parameterAsBool(parameters,
                                               self.DISCARD_NONMATCHING,
                                               context)
        summaries = [
            self.statistics[i][0] for i in sorted(
                self.parameterAsEnums(parameters, self.SUMMARIES, context))
        ]

        if not summaries:
            # none selected, so use all
            summaries = [s[0] for s in self.statistics]

        source_fields = source.fields()
        fields_to_join = QgsFields()
        join_field_indexes = []
        if not join_fields:
            # no fields selected, use all
            join_fields = [
                join_source.fields().at(i).name()
                for i in range(len(join_source.fields()))
            ]

        def addFieldKeepType(original, stat):
            """
            Adds a field to the output, keeping the same data type as the original
            """
            field = QgsField(original)
            field.setName(field.name() + '_' + stat)
            fields_to_join.append(field)

        def addField(original, stat, type):
            """
            Adds a field to the output, with a specified type
            """
            field = QgsField(original)
            field.setName(field.name() + '_' + stat)
            field.setType(type)
            if type == QVariant.Double:
                field.setLength(20)
                field.setPrecision(6)
            fields_to_join.append(field)

        numeric_fields = (('count', QVariant.Int,
                           'count'), ('unique', QVariant.Int, 'variety'),
                          ('min', QVariant.Double,
                           'min'), ('max', QVariant.Double,
                                    'max'), ('range', QVariant.Double,
                                             'range'), ('sum', QVariant.Double,
                                                        'sum'),
                          ('mean', QVariant.Double,
                           'mean'), ('median', QVariant.Double, 'median'),
                          ('stddev', QVariant.Double,
                           'stDev'), ('minority', QVariant.Double, 'minority'),
                          ('majority', QVariant.Double,
                           'majority'), ('q1', QVariant.Double,
                                         'firstQuartile'),
                          ('q3', QVariant.Double,
                           'thirdQuartile'), ('iqr', QVariant.Double,
                                              'interQuartileRange'))

        datetime_fields = (('count', QVariant.Int, 'count'),
                           ('unique', QVariant.Int, 'countDistinct'),
                           ('empty', QVariant.Int, 'countMissing'),
                           ('filled', QVariant.Int), ('min', None), ('max',
                                                                     None))

        string_fields = (('count', QVariant.Int,
                          'count'), ('unique', QVariant.Int, 'countDistinct'),
                         ('empty', QVariant.Int, 'countMissing'),
                         ('filled', QVariant.Int), ('min', None, 'min'),
                         ('max', None, 'max'), ('min_length', QVariant.Int,
                                                'minLength'),
                         ('max_length', QVariant.Int, 'maxLength'),
                         ('mean_length', QVariant.Double, 'meanLength'))

        field_types = []
        for f in join_fields:
            idx = join_source.fields().lookupField(f)
            if idx >= 0:
                join_field_indexes.append(idx)

                join_field = join_source.fields().at(idx)
                if join_field.isNumeric():
                    field_types.append('numeric')
                    field_list = numeric_fields
                elif join_field.type() in (QVariant.Date, QVariant.Time,
                                           QVariant.DateTime):
                    field_types.append('datetime')
                    field_list = datetime_fields
                else:
                    field_types.append('string')
                    field_list = string_fields

                for f in field_list:
                    if f[0] in summaries:
                        if f[1] is not None:
                            addField(join_field, f[0], f[1])
                        else:
                            addFieldKeepType(join_field, f[0])

        out_fields = QgsProcessingUtils.combineFields(source_fields,
                                                      fields_to_join)

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

        # do the join
        predicates = [
            self.predicates[i][0]
            for i in self.parameterAsEnums(parameters, self.PREDICATE, context)
        ]

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

        # bounding box transform
        bbox_transform = QgsCoordinateTransform(source.sourceCrs(),
                                                join_source.sourceCrs(),
                                                context.project())

        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            if not f.hasGeometry():
                if not discard_nomatch:
                    # ensure consistent count of attributes - otherwise non matching
                    # features will have incorrect attribute length
                    # and provider may reject them
                    attrs = f.attributes()
                    if len(attrs) < len(out_fields):
                        attrs += [NULL] * (len(out_fields) - len(attrs))
                    f.setAttributes(attrs)
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
                continue

            bbox = bbox_transform.transformBoundingBox(
                f.geometry().boundingBox())
            engine = None

            values = []

            request = QgsFeatureRequest().setFilterRect(
                bbox).setSubsetOfAttributes(
                    join_field_indexes).setDestinationCrs(
                        source.sourceCrs(), context.transformContext())
            for test_feat in join_source.getFeatures(request):
                if feedback.isCanceled():
                    break

                join_attributes = []
                for a in join_field_indexes:
                    join_attributes.append(test_feat.attributes()[a])

                if engine is None:
                    engine = QgsGeometry.createGeometryEngine(
                        f.geometry().constGet())
                    engine.prepareGeometry()

                for predicate in predicates:
                    if getattr(engine,
                               predicate)(test_feat.geometry().constGet()):
                        values.append(join_attributes)
                        break

            feedback.setProgress(int(current * total))

            if len(values) == 0:
                if discard_nomatch:
                    continue
                else:
                    # ensure consistent count of attributes - otherwise non matching
                    # features will have incorrect attribute length
                    # and provider may reject them
                    attrs = f.attributes()
                    if len(attrs) < len(out_fields):
                        attrs += [NULL] * (len(out_fields) - len(attrs))
                    f.setAttributes(attrs)
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
            else:
                attrs = f.attributes()
                for i in range(len(join_field_indexes)):
                    attribute_values = [v[i] for v in values]
                    field_type = field_types[i]
                    if field_type == 'numeric':
                        stat = QgsStatisticalSummary()
                        for v in attribute_values:
                            stat.addVariant(v)
                        stat.finalize()
                        for s in numeric_fields:
                            if s[0] in summaries:
                                attrs.append(getattr(stat, s[2])())
                    elif field_type == 'datetime':
                        stat = QgsDateTimeStatisticalSummary()
                        stat.calculate(attribute_values)
                        for s in datetime_fields:
                            if s[0] in summaries:
                                if s[0] == 'filled':
                                    attrs.append(stat.count() -
                                                 stat.countMissing())
                                elif s[0] == 'min':
                                    attrs.append(
                                        stat.statistic(
                                            QgsDateTimeStatisticalSummary.Min))
                                elif s[0] == 'max':
                                    attrs.append(
                                        stat.statistic(
                                            QgsDateTimeStatisticalSummary.Max))
                                else:
                                    attrs.append(getattr(stat, s[2])())
                    else:
                        stat = QgsStringStatisticalSummary()
                        for v in attribute_values:
                            if v == NULL:
                                stat.addString('')
                            else:
                                stat.addString(str(v))
                        stat.finalize()
                        for s in string_fields:
                            if s[0] in summaries:
                                if s[0] == 'filled':
                                    attrs.append(stat.count() -
                                                 stat.countMissing())
                                else:
                                    attrs.append(getattr(stat, s[2])())

                f.setAttributes(attrs)
                sink.addFeature(f, QgsFeatureSink.FastInsert)

        return {self.OUTPUT: dest_id}