Ejemplo n.º 1
0
    def add_geom_columns(context, layer: QgsVectorLayer) -> None:
        """ Add latitude and longitude columns in the layer. """
        fields = [
            QgsField('longitude', type=QVariant.Double),
            QgsField('latitude', type=QVariant.Double),
        ]
        transform = QgsCoordinateTransform(
            layer.crs(), QgsCoordinateReferenceSystem('EPSG:4326'),
            context.project())

        with edit(layer):
            for field in fields:
                layer.addAttribute(field)

            request = QgsFeatureRequest()
            request.setSubsetOfAttributes(['latitude', 'longitude'],
                                          layer.fields())
            for feature in layer.getFeatures(request):
                geom = QgsGeometry(feature.geometry())
                if not geom:
                    continue

                geom.transform(transform)
                geom = geom.centroid().asPoint()
                feature.setAttribute('longitude', geom.x())
                feature.setAttribute('latitude', geom.y())
                layer.updateFeature(feature)
Ejemplo n.º 2
0
    def translateCenterGeom(self, g: QgsGeometry, target: QgsGeometry):
        """Translate a geometry to the center another geometry
        
        :param g QgsGeometry: Geometry that be translated
        :param target QgsGeometry: The target geometry

        """
        # duplicating
        g_new = QgsGeometry(g)
        # print("g duplicated")
        target_new = QgsGeometry(target)
        # print("target duplicated")

        c = target_new.centroid().asQPointF()
        # print("centroid created")
        c2 = g_new.centroid().asQPointF()
        # print("centroid created")
        transX = c.x() - c2.x()
        # print("translate x calculated")
        transY = c.y() - c2.y()
        # print("translate y calculated")
        g.translate(transX, transY)
        # print("g translated")
        return g
Ejemplo n.º 3
0
    def processAlgorithm(
            self,  # pylint: disable=missing-function-docstring,too-many-statements,too-many-branches,too-many-locals
            parameters,
            context,
            feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)

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

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

        roundabout_expression_string = self.parameterAsExpression(
            parameters, self.EXPRESSION, context)

        # step 1 - find all roundabouts
        exp = QgsExpression(roundabout_expression_string)
        expression_context = self.createExpressionContext(
            parameters, context, source)
        exp.prepare(expression_context)

        roundabouts = []
        not_roundabouts = {}
        not_roundabout_index = QgsSpatialIndex()

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

        _id = 1
        for current, feature in enumerate(features):
            if feedback.isCanceled():
                break

            def add_feature(f, _id, geom, is_roundabout):
                output_feature = QgsFeature(f)
                output_feature.setGeometry(geom)
                output_feature.setId(_id)
                if is_roundabout:
                    roundabouts.append(output_feature)
                else:
                    not_roundabouts[output_feature.id()] = output_feature
                    not_roundabout_index.addFeature(output_feature)

            expression_context.setFeature(feature)

            is_roundabout = exp.evaluate(expression_context)
            if not feature.geometry().wkbType() == QgsWkbTypes.LineString:
                geom = feature.geometry()
                for p in geom.parts():
                    add_feature(feature, _id, QgsGeometry(p.clone()),
                                is_roundabout)
                    _id += 1
            else:
                add_feature(feature, _id, feature.geometry(), is_roundabout)
                _id += 1

            # Update the progress bar
            feedback.setProgress(int(current * total))

        feedback.pushInfo(
            self.tr('Found {} roundabout parts'.format(len(roundabouts))))
        feedback.pushInfo(
            self.tr('Found {} not roundabouts'.format(len(not_roundabouts))))

        if feedback.isCanceled():
            return {self.OUTPUT: dest_id}

        all_roundabouts = QgsGeometry.unaryUnion(
            [r.geometry() for r in roundabouts])
        feedback.setProgress(20)
        all_roundabouts = all_roundabouts.mergeLines()
        feedback.setProgress(25)

        total = 70.0 / all_roundabouts.constGet().numGeometries(
        ) if all_roundabouts.isMultipart() else 1

        for current, roundabout in enumerate(all_roundabouts.parts()):
            touching = not_roundabout_index.intersects(
                roundabout.boundingBox())
            if not touching:
                continue

            if feedback.isCanceled():
                break

            roundabout_engine = QgsGeometry.createGeometryEngine(roundabout)
            roundabout_engine.prepareGeometry()
            roundabout_geom = QgsGeometry(roundabout.clone())
            roundabout_centroid = roundabout_geom.centroid()

            other_points = []

            # find all touching roads, and move the touching part to the centroid
            for t in touching:
                touching_geom = not_roundabouts[t].geometry()
                touching_road = touching_geom.constGet().clone()
                if not roundabout_engine.touches(touching_road):
                    # print('not touching!!')
                    continue

                # work out if start or end of line touched the roundabout
                nearest = roundabout_geom.nearestPoint(touching_geom)
                _, v = touching_geom.closestVertexWithContext(
                    nearest.asPoint())

                if v == 0:
                    # started at roundabout
                    other_points.append((touching_road.endPoint(), True, t))
                else:
                    # ended at roundabout
                    other_points.append((touching_road.startPoint(), False, t))

            if not other_points:
                continue

            # see if any incoming segments originate at the same place ("V" patterns)
            averaged = set()
            for point1, started_at_roundabout1, id1 in other_points:
                if id1 in averaged:
                    continue

                if feedback.isCanceled():
                    break

                parts_to_average = [id1]
                for point2, _, id2 in other_points:
                    if id2 == id1:
                        continue

                    if point2 != point1:
                        # todo tolerance?
                        continue

                    parts_to_average.append(id2)

                if len(parts_to_average) == 1:
                    # not a <O pattern, just a round coming straight to the roundabout
                    line = not_roundabouts[id1].geometry().constGet().clone()
                    if started_at_roundabout1:
                        # extend start of line to roundabout centroid
                        line.moveVertex(QgsVertexId(0, 0, 0),
                                        roundabout_centroid.constGet())
                    else:
                        # extend end of line to roundabout centroid
                        line.moveVertex(
                            QgsVertexId(0, 0,
                                        line.numPoints() - 1),
                            roundabout_centroid.constGet())

                    not_roundabout_index.deleteFeature(
                        not_roundabouts[parts_to_average[0]])
                    not_roundabouts[parts_to_average[0]].setGeometry(
                        QgsGeometry(line))
                    not_roundabout_index.addFeature(
                        not_roundabouts[parts_to_average[0]])

                elif len(parts_to_average) == 2:
                    # <O pattern
                    src_part, other_part = parts_to_average  # pylint: disable=unbalanced-tuple-unpacking
                    averaged.add(src_part)
                    averaged.add(other_part)

                    averaged_line = GeometryUtils.average_linestrings(
                        not_roundabouts[src_part].geometry().constGet(),
                        not_roundabouts[other_part].geometry().constGet())

                    if started_at_roundabout1:
                        # extend start of line to roundabout centroid
                        averaged_line.moveVertex(
                            QgsVertexId(0, 0, 0),
                            roundabout_centroid.constGet())
                    else:
                        # extend end of line to roundabout centroid
                        averaged_line.moveVertex(
                            QgsVertexId(0, 0,
                                        averaged_line.numPoints() - 1),
                            roundabout_centroid.constGet())

                    not_roundabout_index.deleteFeature(
                        not_roundabouts[src_part])
                    not_roundabouts[src_part].setGeometry(
                        QgsGeometry(averaged_line))
                    not_roundabout_index.addFeature(not_roundabouts[src_part])

                    not_roundabout_index.deleteFeature(
                        not_roundabouts[other_part])
                    del not_roundabouts[other_part]

            feedback.setProgress(25 + int(current * total))

        total = 5.0 / len(not_roundabouts)
        current = 0
        for _, f in not_roundabouts.items():
            if feedback.isCanceled():
                break

            sink.addFeature(f, QgsFeatureSink.FastInsert)
            current += 1
            feedback.setProgress(95 + int(current * total))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 4
0
    def processAlgorithm(self, parameters, context, feedback):
        if DEBUG_MODE:
            logMessage(
                "processAlgorithm(): {}".format(self.__class__.__name__),
                False)

        clayer = self.parameterAsLayer(parameters, self.INPUT, context)
        title_field = self.parameterAsString(parameters, self.TITLE_FIELD,
                                             context)
        cf_filter = self.parameterAsBool(parameters, self.CF_FILTER, context)
        fixed_scale = self.parameterAsEnum(parameters, self.SCALE,
                                           context)  # == 1
        buf = self.parameterAsDouble(parameters, self.BUFFER, context)
        tex_width = self.parameterAsInt(parameters, self.TEX_WIDTH, context)
        orig_tex_height = self.parameterAsInt(parameters, self.TEX_HEIGHT,
                                              context)

        header_exp = QgsExpression(
            self.parameterAsExpression(parameters, self.HEADER, context))
        footer_exp = QgsExpression(
            self.parameterAsExpression(parameters, self.FOOTER, context))

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(clayer))

        out_dir = self.parameterAsString(parameters, self.OUTPUT, context)
        if not QDir(out_dir).exists():
            QDir().mkpath(out_dir)

        if DEBUG_MODE:
            openDirectory(out_dir)

        mapSettings = self.controller.settings.mapSettings
        baseExtent = self.controller.settings.baseExtent
        rotation = mapSettings.rotation()
        orig_size = mapSettings.outputSize()

        if cf_filter:
            cf_layer = QgsMemoryProviderUtils.createMemoryLayer(
                "current feature", clayer.fields(), clayer.wkbType(),
                clayer.crs())
            layers = [
                cf_layer if lyr == clayer else lyr
                for lyr in mapSettings.layers()
            ]
            mapSettings.setLayers(layers)

            doc = QDomDocument("qgis")
            clayer.exportNamedStyle(doc)
            cf_layer.importNamedStyle(doc)

        total = clayer.featureCount()
        for current, feature in enumerate(clayer.getFeatures()):
            if feedback.isCanceled():
                break

            if cf_filter:
                cf_layer.startEditing()
                cf_layer.deleteFeatures(
                    [f.id() for f in cf_layer.getFeatures()])
                cf_layer.addFeature(feature)
                cf_layer.commitChanges()

            title = feature.attribute(title_field)
            feedback.setProgressText("({}/{}) Exporting {}...".format(
                current + 1, total, title))
            logMessage("Exporting {}...".format(title), False)

            # extent
            geometry = QgsGeometry(feature.geometry())
            geometry.transform(self.transform)
            center = geometry.centroid().asPoint()

            if fixed_scale or geometry.type() == QgsWkbTypes.PointGeometry:
                tex_height = orig_tex_height or int(
                    tex_width * orig_size.height() / orig_size.width())
                rect = RotatedRect(center, baseExtent.width(),
                                   baseExtent.width() * tex_height / tex_width,
                                   rotation).scale(1 + buf / 100)
            else:
                geometry.rotate(rotation, center)
                rect = geometry.boundingBox().scaled(1 + buf / 100)
                center = RotatedRect.rotatePoint(rect.center(), rotation,
                                                 center)
                if orig_tex_height:
                    tex_height = orig_tex_height
                    tex_ratio = tex_width / tex_height
                    rect_ratio = rect.width() / rect.height()
                    if tex_ratio > rect_ratio:
                        rect = RotatedRect(center,
                                           rect.height() * tex_ratio,
                                           rect.height(), rotation)
                    else:
                        rect = RotatedRect(center, rect.width(),
                                           rect.width() / tex_ratio, rotation)
                else:
                    # fit to buffered geometry bounding box
                    rect = RotatedRect(center, rect.width(), rect.height(),
                                       rotation)
                    tex_height = tex_width * rect.height() / rect.width()

            rect.toMapSettings(mapSettings)
            mapSettings.setOutputSize(QSize(tex_width, tex_height))

            self.controller.settings.setMapSettings(mapSettings)

            # labels
            exp_context.setFeature(feature)
            self.controller.settings.setHeaderLabel(
                header_exp.evaluate(exp_context))
            self.controller.settings.setFooterLabel(
                footer_exp.evaluate(exp_context))

            self.export(title, out_dir, feedback)

            feedback.setProgress(int(current / total * 100))

        if P_OPEN_DIRECTORY and not DEBUG_MODE:
            openDirectory(out_dir)

        return {}
Ejemplo n.º 5
0
    def get_reduction_factor(self, layer, field):
        """Calculate the reduction factor."""
        data_provider = layer.dataProvider()
        meta_features = []

        total_area = 0.0
        total_value = 0.0

        if self.min_value is None:
            self.min_value = self.get_min_value(data_provider, field)

        for feature in data_provider.getFeatures():
            meta_feature = CartogramFeature()

            geometry = QgsGeometry(feature.geometry())

            area = QgsDistanceArea().measureArea(geometry)
            total_area += area

            feature_value = feature.attribute(field)
            if type(feature_value) is None or feature_value == 0:
                feature_value = self.min_value / 100

            total_value += feature_value

            meta_feature.area = area
            meta_feature.value = feature_value

            centroid = geometry.centroid()
            (cx, cy) = centroid.asPoint().x(), centroid.asPoint().y()
            meta_feature.center_x = cx
            meta_feature.center_y = cy

            meta_features.append(meta_feature)

        fraction = total_area / total_value

        total_size_error = 0

        for meta_feature in meta_features:
            polygon_value = meta_feature.value
            polygon_area = meta_feature.area

            if polygon_area < 0:
                polygon_area = 0

            # this is our 'desired' area...
            desired_area = polygon_value * fraction

            # calculate radius, a zero area is zero radius
            radius = math.sqrt(polygon_area / math.pi)
            meta_feature.radius = radius

            if desired_area / math.pi > 0:
                mass = math.sqrt(desired_area / math.pi) - radius
                meta_feature.mass = mass
            else:
                meta_feature.mass = 0

            size_error = max(polygon_area, desired_area) / \
             min(polygon_area, desired_area)

            total_size_error += size_error

        average_error = total_size_error / len(meta_features)
        force_reduction_factor = 1 / (average_error + 1)

        return (meta_features, force_reduction_factor)
Ejemplo n.º 6
0
    def get_reduction_factor(self, layer, field):
        """Calculate the reduction factor."""
        data_provider = layer.dataProvider()
        meta_features = []

        total_area = 0.0
        total_value = 0.0

        for feature in data_provider.getFeatures():
            meta_feature = CartogramFeature()

            geometry = QgsGeometry(feature.geometry())

            area = QgsDistanceArea().measure(geometry)
            total_area += area

            feature_value = feature.attribute(field)
            total_value += feature_value

            meta_feature.area = area
            meta_feature.value = feature_value

            centroid = geometry.centroid()
            (cx, cy) = centroid.asPoint().x(), centroid.asPoint().y()
            meta_feature.center_x = cx
            meta_feature.center_y = cy

            meta_features.append(meta_feature)

        fraction = total_area / total_value

        total_size_error = 0

        for meta_feature in meta_features:
            polygon_value = meta_feature.value
            polygon_area = meta_feature.area

            if polygon_area < 0:
                polygon_area = 0

            # this is our 'desired' area...
            desired_area = polygon_value * fraction

            # calculate radius, a zero area is zero radius
            radius = math.sqrt(polygon_area / math.pi)
            meta_feature.radius = radius

            if desired_area / math.pi > 0:
                mass = math.sqrt(desired_area / math.pi) - radius
                meta_feature.mass = mass
            else:
                meta_feature.mass = 0

            size_error = max(polygon_area, desired_area) / \
                min(polygon_area, desired_area)

            total_size_error += size_error

        average_error = total_size_error / len(meta_features)
        force_reduction_factor = 1 / (average_error + 1)

        return (meta_features, force_reduction_factor)
Ejemplo n.º 7
0
    def in_mask(self, feature, srid=None):
        if feature is None:  # expression overview
            return False

        if self.layer is None:
            return False

        try:
            # layer is not None but destroyed ?
            self.layer.id()
        except:
            self.reset_mask_layer()
            return False

        # mask layer empty due to unloaded memlayersaver plugin > no filtering
        if self.layer.featureCount() == 0:
            return True

        mask_geom, bbox = self.mask_geometry()
        geom = QgsGeometry(feature.geometry())
        if not geom.isGeosValid():
            geom = geom.buffer(0.0, 1)

        if geom is None:
            return False

        if srid is not None and self.layer.crs().postgisSrid() != srid:
            src_crs = QgsCoordinateReferenceSystem(srid)
            dest_crs = self.layer.crs()
            xform = QgsCoordinateTransform(src_crs, dest_crs, QgsProject.instance())
            try:
                geom.transform(xform)
            except:
                # transformation error. Check layer projection.
                pass

        if geom.type() == QgsWkbTypes.PolygonGeometry:
            if self.parameters.polygon_mask_method == 2 and not self.has_point_on_surface:
                self.parameters.polygon_mask_method = 1

            if self.parameters.polygon_mask_method == 0:
                # this method can only work when no geometry simplification is involved
                return (mask_geom.overlaps(geom) or mask_geom.contains(geom))
            elif self.parameters.polygon_mask_method == 1:
                # the fastest method, but with possible inaccuracies
                pt = geom.vertexAt(0)
                return bbox.contains(QgsPointXY(pt)) and mask_geom.contains(geom.centroid())
            elif self.parameters.polygon_mask_method == 2:
                # will always work
                pt = geom.vertexAt(0)
                return bbox.contains(QgsPointXY(pt)) and mask_geom.contains(geom.pointOnSurface())
            else:
                return False
        elif geom.type() == QgsWkbTypes.LineGeometry:
            if self.parameters.line_mask_method == 0:
                return mask_geom.intersects(geom)
            elif self.parameters.line_mask_method == 1:
                return mask_geom.contains(geom)
            else:
                return False
        elif geom.type() == QgsWkbTypes.PointGeometry:
            return mask_geom.intersects(geom)
        else:
            return False
Ejemplo n.º 8
0
    def processAlgorithm(self, parameters, context, feedback):
        if DEBUG_MODE:
            logMessage("processAlgorithm(): {}".format(
                self.__class__.__name__))

        source = self.parameterAsSource(parameters, self.INPUT, context)
        source_layer = self.parameterAsLayer(parameters, self.INPUT, context)
        title_field = self.parameterAsString(parameters, self.TITLE_FIELD,
                                             context)
        cf_filter = self.parameterAsBool(parameters, self.CF_FILTER, context)
        fixed_scale = self.parameterAsEnum(parameters, self.SCALE,
                                           context)  # == 1
        buf = self.parameterAsDouble(parameters, self.BUFFER, context)
        tex_width = self.parameterAsInt(parameters, self.TEX_WIDTH, context)
        orig_tex_height = self.parameterAsInt(parameters, self.TEX_HEIGHT,
                                              context)
        out_dir = self.parameterAsString(parameters, self.OUTPUT, context)

        mapSettings = self.controller.settings.mapSettings
        baseExtent = self.controller.settings.baseExtent
        rotation = mapSettings.rotation()
        orig_size = mapSettings.outputSize()

        if cf_filter:
            #TODO: FIX ME
            #cf_layer = QgsMemoryProviderUtils.createMemoryLayer("current feature",
            #                                                    source_layer.fields(),
            #                                                    source_layer.wkbType(),
            #                                                    source_layer.crs())

            doc = QDomDocument("qgis")
            source_layer.exportNamedStyle(doc)

            orig_layers = mapSettings.layers()

        total = source.featureCount()
        for current, feature in enumerate(source.getFeatures()):
            if feedback.isCanceled():
                break

            if cf_filter:
                cf_layer = QgsMemoryProviderUtils.createMemoryLayer(
                    "current feature", source_layer.fields(),
                    source_layer.wkbType(), source_layer.crs())
                cf_layer.startEditing()
                cf_layer.addFeature(feature)
                cf_layer.commitChanges()

                cf_layer.importNamedStyle(doc)

                layers = [
                    cf_layer if lyr == source_layer else lyr
                    for lyr in orig_layers
                ]
                mapSettings.setLayers(layers)

            title = feature.attribute(title_field)
            feedback.setProgressText("({}/{}) Exporting {}...".format(
                current + 1, total, title))

            # extent
            geometry = QgsGeometry(feature.geometry())
            geometry.transform(self.transform)
            center = geometry.centroid().asPoint()

            if fixed_scale or geometry.type() == QgsWkbTypes.PointGeometry:
                tex_height = orig_tex_height or int(
                    tex_width * orig_size.height() / orig_size.width())
                rect = RotatedRect(center, baseExtent.width(),
                                   baseExtent.width() * tex_height / tex_width,
                                   rotation).scale(1 + buf / 100)
            else:
                geometry.rotate(rotation, center)
                rect = geometry.boundingBox().scaled(1 + buf / 100)
                center = RotatedRect.rotatePoint(rect.center(), rotation,
                                                 center)
                if orig_tex_height:
                    tex_height = orig_tex_height
                    tex_ratio = tex_width / tex_height
                    rect_ratio = rect.width() / rect.height()
                    if tex_ratio > rect_ratio:
                        rect = RotatedRect(center,
                                           rect.height() * tex_ratio,
                                           rect.height(), rotation)
                    else:
                        rect = RotatedRect(center, rect.width(),
                                           rect.width() / tex_ratio, rotation)
                else:
                    # fit to buffered geometry bounding box
                    rect = RotatedRect(center, rect.width(), rect.height(),
                                       rotation)
                    tex_height = tex_width * rect.height() / rect.width()

            rect.toMapSettings(mapSettings)
            mapSettings.setOutputSize(QSize(tex_width, tex_height))

            self.controller.settings.setMapSettings(mapSettings)

            self.export(title, out_dir, feedback)

            feedback.setProgress(int(current / total * 100))

        return {}
Ejemplo n.º 9
0
    def in_mask(self, feature, srid=None):
        if feature is None:  # expression overview
            return False

        if self.layer is None:
            return False

        try:
            # layer is not None but destroyed ?
            self.layer.id()
        except:
            self.reset_mask_layer()
            return False

        # mask layer empty due to unloaded memlayersaver plugin > no filtering
        if self.layer.featureCount() == 0:
            return True

        mask_geom, bbox = self.mask_geometry()
        geom = QgsGeometry(feature.geometry())
        if not geom.isGeosValid():
            geom = geom.buffer(0.0, 1)

        if geom is None:
            return False

        if srid is not None and self.layer.crs().postgisSrid() != srid:
            src_crs = QgsCoordinateReferenceSystem(srid)
            dest_crs = self.layer.crs()
            xform = QgsCoordinateTransform(src_crs, dest_crs,
                                           QgsProject.instance())
            try:
                geom.transform(xform)

            except Exception as e:
                for m in e.args:
                    QgsMessageLog.logMessage("in_mask - {}".format(m),
                                             "Extensions")
                    # transformation error. Check layer projection.
                    pass

        if geom.type() == QgsWkbTypes.PolygonGeometry:
            if (self.parameters.polygon_mask_method == 2
                    and not self.has_point_on_surface):
                self.parameters.polygon_mask_method = 1

            if self.parameters.polygon_mask_method == 0:
                # this method can only work when no geometry simplification is involved
                return mask_geom.overlaps(geom) or mask_geom.contains(geom)
            elif self.parameters.polygon_mask_method == 1:
                # the fastest method, but with possible inaccuracies
                pt = geom.vertexAt(0)
                return bbox.contains(QgsPointXY(pt)) and mask_geom.contains(
                    geom.centroid())
            elif self.parameters.polygon_mask_method == 2:
                # will always work
                pt = geom.vertexAt(0)
                return bbox.contains(QgsPointXY(pt)) and mask_geom.contains(
                    geom.pointOnSurface())
            else:
                return False
        elif geom.type() == QgsWkbTypes.LineGeometry:
            if self.parameters.line_mask_method == 0:
                return mask_geom.intersects(geom)
            elif self.parameters.line_mask_method == 1:
                return mask_geom.contains(geom)
            else:
                return False
        elif geom.type() == QgsWkbTypes.PointGeometry:
            return mask_geom.intersects(geom)
        else:
            return False