Exemple #1
1
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)

        extent = self.parameterAsExtent(parameters, self.TARGET_AREA, context)
        target_crs = self.parameterAsCrs(parameters, self.TARGET_AREA_CRS, context)

        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())

        # 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}
Exemple #2
0
    def processAlgorithm(self, parameters, context, feedback):
        spacing = self.parameterAsDouble(parameters, self.SPACING, context)
        inset = self.parameterAsDouble(parameters, self.INSET, context)
        randomize = self.parameterAsBool(parameters, self.RANDOMIZE, context)
        isSpacing = self.parameterAsBool(parameters, self.IS_SPACING, context)
        crs = self.parameterAsCrs(parameters, self.CRS, context)
        extent = self.parameterAsExtent(parameters, self.EXTENT, context, crs)

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

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

        if randomize:
            seed()

        area = extent.width() * extent.height()
        if isSpacing:
            pSpacing = spacing
        else:
            pSpacing = sqrt(area / spacing)

        f = QgsFeature()
        f.initAttributes(1)
        f.setFields(fields)

        count = 0
        total = 100.0 / (area / pSpacing)
        y = extent.yMaximum() - inset

        extent_geom = QgsGeometry.fromRect(extent)
        extent_engine = QgsGeometry.createGeometryEngine(extent_geom.constGet())
        extent_engine.prepareGeometry()

        while y >= extent.yMinimum():
            x = extent.xMinimum() + inset
            while x <= extent.xMaximum():
                if feedback.isCanceled():
                    break

                if randomize:
                    geom = QgsGeometry().fromPointXY(QgsPointXY(
                        uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)),
                        uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0))))
                else:
                    geom = QgsGeometry().fromPointXY(QgsPointXY(x, y))

                if extent_engine.intersects(geom.constGet()):
                    f.setAttribute('id', count)
                    f.setGeometry(geom)
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
                    x += pSpacing
                    count += 1
                    feedback.setProgress(int(count * total))
            y = y - pSpacing

        return {self.OUTPUT: dest_id}
Exemple #3
0
    def processAlgorithm(self, parameters, context, feedback):
        spacing = self.parameterAsDouble(parameters, self.SPACING, context)
        inset = self.parameterAsDouble(parameters, self.INSET, context)
        randomize = self.parameterAsBool(parameters, self.RANDOMIZE, context)
        isSpacing = self.parameterAsBool(parameters, self.IS_SPACING, context)
        crs = self.parameterAsCrs(parameters, self.CRS, context)
        extent = self.parameterAsExtent(parameters, self.EXTENT, context, crs)

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

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, QgsWkbTypes.Point, crs)

        if randomize:
            seed()

        area = extent.width() * extent.height()
        if isSpacing:
            pSpacing = spacing
        else:
            pSpacing = sqrt(area / spacing)

        f = QgsFeature()
        f.initAttributes(1)
        f.setFields(fields)

        count = 0
        total = 100.0 / (area / pSpacing)
        y = extent.yMaximum() - inset

        extent_geom = QgsGeometry.fromRect(extent)
        extent_engine = QgsGeometry.createGeometryEngine(extent_geom.constGet())
        extent_engine.prepareGeometry()

        while y >= extent.yMinimum():
            x = extent.xMinimum() + inset
            while x <= extent.xMaximum():
                if feedback.isCanceled():
                    break

                if randomize:
                    geom = QgsGeometry().fromPointXY(QgsPointXY(
                        uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)),
                        uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0))))
                else:
                    geom = QgsGeometry().fromPointXY(QgsPointXY(x, y))

                if extent_engine.intersects(geom.constGet()):
                    f.setAttribute('id', count)
                    f.setGeometry(geom)
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
                    x += pSpacing
                    count += 1
                    feedback.setProgress(int(count * total))
            y = y - pSpacing

        return {self.OUTPUT: dest_id}
Exemple #4
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)

        extent = self.parameterAsExtent(parameters, self.TARGET_AREA, context)
        target_crs = self.parameterAsCrs(parameters, self.TARGET_AREA_CRS, context)

        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())

        # 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}
Exemple #5
0
def smart_clip(layer_to_clip, mask_layer):
    """Smart clip a vector layer with another.

    Issue https://github.com/inasafe/inasafe/issues/3186

    :param layer_to_clip: The vector layer to clip.
    :type layer_to_clip: QgsVectorLayer

    :param mask_layer: The vector layer to use for clipping.
    :type mask_layer: QgsVectorLayer

    :return: The clip vector layer.
    :rtype: QgsVectorLayer

    .. versionadded:: 4.0
    """
    output_layer_name = smart_clip_steps['output_layer_name']

    writer = create_memory_layer(
        output_layer_name,
        layer_to_clip.geometryType(),
        layer_to_clip.crs(),
        layer_to_clip.fields()
    )
    writer.startEditing()

    # first build up a list of clip geometries
    request = QgsFeatureRequest().setSubsetOfAttributes([])
    iterator = mask_layer.getFeatures(request)
    feature = next(iterator)
    geometries = QgsGeometry(feature.geometry())

    # use prepared geometries for faster intersection tests
    # noinspection PyArgumentList
    engine = QgsGeometry.createGeometryEngine(geometries.constGet())
    engine.prepareGeometry()

    extent = mask_layer.extent()

    for feature in layer_to_clip.getFeatures(QgsFeatureRequest(extent)):

        if engine.intersects(feature.geometry().constGet()):
            out_feat = QgsFeature()
            out_feat.setGeometry(feature.geometry())
            out_feat.setAttributes(feature.attributes())
            writer.addFeature(out_feat)

    writer.commitChanges()

    writer.keywords = layer_to_clip.keywords.copy()
    writer.keywords['title'] = output_layer_name
    check_layer(writer)
    return writer
Exemple #6
0
def smart_clip(layer_to_clip, mask_layer):
    """Smart clip a vector layer with another.

    Issue https://github.com/inasafe/inasafe/issues/3186

    :param layer_to_clip: The vector layer to clip.
    :type layer_to_clip: QgsVectorLayer

    :param mask_layer: The vector layer to use for clipping.
    :type mask_layer: QgsVectorLayer

    :return: The clip vector layer.
    :rtype: QgsVectorLayer

    .. versionadded:: 4.0
    """
    output_layer_name = smart_clip_steps['output_layer_name']

    writer = create_memory_layer(
        output_layer_name,
        layer_to_clip.geometryType(),
        layer_to_clip.crs(),
        layer_to_clip.fields()
    )
    writer.startEditing()

    # first build up a list of clip geometries
    request = QgsFeatureRequest().setSubsetOfAttributes([])
    iterator = mask_layer.getFeatures(request)
    feature = next(iterator)
    geometries = QgsGeometry(feature.geometry())

    # use prepared geometries for faster intersection tests
    # noinspection PyArgumentList
    engine = QgsGeometry.createGeometryEngine(geometries.constGet())
    engine.prepareGeometry()

    extent = mask_layer.extent()

    for feature in layer_to_clip.getFeatures(QgsFeatureRequest(extent)):

        if engine.intersects(feature.geometry().constGet()):
            out_feat = QgsFeature()
            out_feat.setGeometry(feature.geometry())
            out_feat.setAttributes(feature.attributes())
            writer.addFeature(out_feat)

    writer.commitChanges()

    writer.keywords = layer_to_clip.keywords.copy()
    writer.keywords['title'] = output_layer_name
    check_layer(writer)
    return writer
        def _get_uncommon_vertices(_common_vertices, geometry: QgsGeometry):
            res = []

            const_geom = geometry.constGet()
            vid = QgsVertexId()

            vertex_no = 1
            while True:
                ok, vertex = const_geom.nextVertex(vid)
                if ok:
                    if not QgsPointXY(vertex) in _common_vertices:
                        res.append(vertex_no)
                    vertex_no += 1
                else:
                    break

            return res
Exemple #8
0
    def processAlgorithm(self, parameters, context, feedback):
        sourceA = self.parameterAsSource(parameters, self.INPUT, context)
        sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)

        geomType = QgsWkbTypes.multiType(sourceA.wkbType())
        fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields())

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, geomType, sourceA.sourceCrs())

        featA = QgsFeature()
        featB = QgsFeature()
        outFeat = QgsFeature()

        indexA = QgsSpatialIndex(sourceA, feedback)
        indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs(), context.transformContext())), feedback)

        total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1
        count = 0

        for featA in sourceA.getFeatures():
            if feedback.isCanceled():
                break

            lstIntersectingB = []
            geom = featA.geometry()
            atMapA = featA.attributes()
            intersects = indexB.intersects(geom.boundingBox())
            if len(intersects) < 1:
                try:
                    geom.convertToMultiType()
                    outFeat.setGeometry(geom)
                    outFeat.setAttributes(atMapA)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    # This really shouldn't happen, as we haven't
                    # edited the input geom at all
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
            else:
                request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
                request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext())

                engine = QgsGeometry.createGeometryEngine(geom.constGet())
                engine.prepareGeometry()

                for featB in sourceB.getFeatures(request):
                    atMapB = featB.attributes()
                    tmpGeom = featB.geometry()

                    if engine.intersects(tmpGeom.constGet()):
                        int_geom = geom.intersection(tmpGeom)
                        lstIntersectingB.append(tmpGeom)

                        if not int_geom:
                            # There was a problem creating the intersection
                            feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
                            int_geom = QgsGeometry()
                        else:
                            int_geom = QgsGeometry(int_geom)

                        if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.wkbType()) == QgsWkbTypes.GeometryCollection:
                            # Intersection produced different geomety types
                            temp_list = int_geom.asGeometryCollection()
                            for i in temp_list:
                                if i.type() == geom.type():
                                    int_geom = QgsGeometry(i)
                                    try:
                                        int_geom.convertToMultiType()
                                        outFeat.setGeometry(int_geom)
                                        outFeat.setAttributes(atMapA + atMapB)
                                        sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                                    except:
                                        feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
                        else:
                            # Geometry list: prevents writing error
                            # in geometries of different types
                            # produced by the intersection
                            # fix #3549
                            if QgsWkbTypes.geometryType(int_geom.wkbType()) == QgsWkbTypes.geometryType(geomType):
                                try:
                                    int_geom.convertToMultiType()
                                    outFeat.setGeometry(int_geom)
                                    outFeat.setAttributes(atMapA + atMapB)
                                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                                except:
                                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

                # the remaining bit of featA's geometry
                # if there is nothing left, this will just silently fail and we're good
                diff_geom = QgsGeometry(geom)
                if len(lstIntersectingB) != 0:
                    intB = QgsGeometry.unaryUnion(lstIntersectingB)
                    diff_geom = diff_geom.difference(intB)

                if diff_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(diff_geom.wkbType()) == QgsWkbTypes.GeometryCollection:
                    temp_list = diff_geom.asGeometryCollection()
                    for i in temp_list:
                        if i.type() == geom.type():
                            diff_geom = QgsGeometry(i)
                try:
                    diff_geom.convertToMultiType()
                    outFeat.setGeometry(diff_geom)
                    outFeat.setAttributes(atMapA)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

            count += 1
            feedback.setProgress(int(count * total))

        length = len(sourceA.fields())
        atMapA = [None] * length

        for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs(), context.transformContext())):
            if feedback.isCanceled():
                break

            add = False
            geom = featA.geometry()
            diff_geom = QgsGeometry(geom)
            atMap = [None] * length
            atMap.extend(featA.attributes())
            intersects = indexA.intersects(geom.boundingBox())

            if len(intersects) < 1:
                try:
                    geom.convertToMultiType()
                    outFeat.setGeometry(geom)
                    outFeat.setAttributes(atMap)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
            else:
                request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
                request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext())

                # use prepared geometries for faster intersection tests
                engine = QgsGeometry.createGeometryEngine(diff_geom.constGet())
                engine.prepareGeometry()

                for featB in sourceA.getFeatures(request):
                    atMapB = featB.attributes()
                    tmpGeom = featB.geometry()

                    if engine.intersects(tmpGeom.constGet()):
                        add = True
                        diff_geom = QgsGeometry(diff_geom.difference(tmpGeom))
                    else:
                        try:
                            # Ihis only happens if the bounding box
                            # intersects, but the geometry doesn't
                            diff_geom.convertToMultiType()
                            outFeat.setGeometry(diff_geom)
                            outFeat.setAttributes(atMap)
                            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                        except:
                            feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

            if add:
                try:
                    diff_geom.convertToMultiType()
                    outFeat.setGeometry(diff_geom)
                    outFeat.setAttributes(atMap)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

            count += 1
            feedback.setProgress(int(count * total))

        return {self.OUTPUT: dest_id}
Exemple #9
0
    def processFeatures(self, reaches, reach_layer, wastewater_node_layer, distance_threshold):
        ids = list()
        to_ids = list()
        # Gather ids of connected networkelements
        # to_ids are also gathered separately, because they can be either
        # reaches or nodes
        for reach in reaches:
            if reach['rp_from_fk_wastewater_networkelement']:
                ids.append(reach['rp_from_fk_wastewater_networkelement'])

            if reach['rp_to_fk_wastewater_networkelement']:
                ids.append(reach['rp_to_fk_wastewater_networkelement'])
                to_ids.append(reach['rp_to_fk_wastewater_networkelement'])

        # Get all nodes on which to snap
        quoted_ids = [QgsExpression.quotedValue(objid) for objid in ids]
        node_request = QgsFeatureRequest()
        filter_expression = '"obj_id" IN ({ids})'.format(
            ids=','.join(quoted_ids))
        node_request.setFilterExpression(filter_expression)
        node_request.setSubsetOfAttributes([])

        nodes = dict()
        for node in wastewater_node_layer.getFeatures(node_request):
            nodes[node['obj_id']] = node

        # Get all reaches on which to snap
        quoted_to_ids = [QgsExpression.quotedValue(objid) for objid in to_ids]
        reach_request = QgsFeatureRequest()
        filter_expression = '"obj_id" IN ({ids})'.format(
            ids=','.join(quoted_to_ids))
        reach_request.setFilterExpression(filter_expression)
        reach_request.setSubsetOfAttributes([])

        target_reaches = dict()
        for target_reach in reach_layer.getFeatures(reach_request):
            target_reaches[target_reach['obj_id']] = target_reach

        for reach in reaches:
            reach_geometry = QgsGeometry(reach.geometry())
            from_id = reach['rp_from_fk_wastewater_networkelement']
            if from_id in list(nodes.keys()):
                if distance_threshold == 0 or reach_geometry.sqrDistToVertexAt(nodes[from_id].geometry().asPoint(), 0) < distance_threshold:
                    reach_geometry.moveVertex(
                        nodes[from_id].geometry().constGet(), 0)

            to_id = reach['rp_to_fk_wastewater_networkelement']
            if to_id in list(nodes.keys()):
                last_vertex = reach_geometry.constGet().nCoordinates() - 1
                if distance_threshold == 0 or reach_geometry.sqrDistToVertexAt(nodes[to_id].geometry().asPoint(), last_vertex) < distance_threshold:
                    reach_geometry.moveVertex(
                        nodes[to_id].geometry().constGet(), last_vertex)

            if to_id in list(target_reaches.keys()):
                last_vertex = reach_geometry.constGet().nCoordinates() - 1
                target_reach = target_reaches[to_id]
                distance, point, min_distance_point, after_vertex = target_reach.geometry(
                ).closestSegmentWithContext(QgsPointXY(reach_geometry.vertexAt(last_vertex)))
                if distance_threshold == 0 or distance < distance_threshold:
                    reach_geometry.moveVertex(
                        point.x(), point.y(), last_vertex)

            reach.setGeometry(reach_geometry)
            reach_layer.updateFeature(reach)
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        use_weight_field = self.parameterAsBool(parameters,
                                                self.USE_WEIGHT_FIELD, context)
        idx_field = -1
        if use_weight_field:
            weight_field = self.parameterAsString(parameters,
                                                  self.WEIGHT_FIELD, context)
            idx_field = source.fields().lookupField(weight_field)

        center_type = self.parameterAsEnum(parameters, self.CENTER, context)

        cell_fields = QgsFields()
        cell_fields.append(QgsField("count", QVariant.Int))
        cell_field_list = ["min", "max", "sum", "mean", "std_dev", "var"]
        for field_name in cell_field_list:
            cell_fields.append(QgsField(field_name, QVariant.Double))
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, cell_fields,
                                               QgsWkbTypes.Polygon,
                                               source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        anchor_fields = QgsFields()
        anchor_fields.append(QgsField("distance", QVariant.Double))
        anchor_fields.append(QgsField("direction", QVariant.String))
        (anchor, anchor_id) = self.parameterAsSink(parameters,
                                                   self.OUTPUT_ANCHOR, context,
                                                   anchor_fields,
                                                   QgsWkbTypes.LineString,
                                                   source.sourceCrs())
        if anchor is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT_ANCHOR))

        # Center : X, Center Y. if not provided, the center of point layer will be used
        source_crs = iface.mapCanvas().mapSettings().destinationCrs()
        target_crs = source.sourceCrs()
        transform = QgsCoordinateTransform(source_crs, target_crs,
                                           QgsProject.instance())

        extent = source.sourceExtent()
        if center_type == 1:
            extent = iface.mapCanvas().extent()
        elif center_type == 2:
            extent = iface.mapCanvas().fullExtent()

        if center_type != 0 and source_crs != target_crs:
            extent = transform.transformBoundingBox(extent)

        center_point = extent.center()
        # QgsPoint

        minx = extent.xMinimum()
        miny = extent.yMinimum()
        maxx = extent.xMaximum()
        maxy = extent.yMaximum()

        radius = (((maxx - minx)**2 + (maxy - miny)**2)**0.5) / 2.0

        #. create spatial index
        spatial_index = QgsSpatialIndex(source)

        step_angle = 360.0 / self.DEFAULT_SEGS
        half_step = step_angle / 2.0

        minVal = sys.float_info.max
        maxVal = sys.float_info.min
        centroid_features = {}
        for idx_side in range(self.DEFAULT_SEGS):
            from_deg = (idx_side * step_angle) - half_step
            to_deg = ((idx_side + 1) * step_angle) - half_step
            feedback.setProgress(int(100 * idx_side / self.DEFAULT_SEGS))

            cell = self.create_cell(center_point, from_deg, to_deg, radius)

            # sptial query
            hasIntersections = False
            points = spatial_index.intersects(cell.boundingBox())
            if len(points) > 0:
                hasIntersections = True

            visitor = StatisticsVisitor()
            if hasIntersections:
                for fid in points:
                    request = QgsFeatureRequest().setFilterFid(fid)
                    point_feature = next(source.getFeatures(request))
                    if cell.contains(point_feature.geometry()):
                        if idx_field >= 0:
                            weight = str(point_feature.attributes()[idx_field])
                            try:
                                visitor.visit(float(weight))
                            except:
                                pass  # Ignore fields with non-numeric values
                        else:
                            visitor.visit(1)

            # create and write ring feature
            cell_feature = QgsFeature(cell_fields)
            cell_feature.setGeometry(cell)
            ret = visitor.result()
            minVal = min(minVal, ret[3])
            maxVal = max(maxVal, ret[3])
            cell_feature.setAttributes(ret)
            centroid_features[idx_side] = cell_feature

        #. write features
        for idx_side in range(self.DEFAULT_SEGS):
            cell_feature = centroid_features[idx_side]
            value = cell_feature.attributes()[3]
            linear_trans_value = (value - minVal) / (maxVal - minVal)
            adjusted_radius = linear_trans_value * radius
            if adjusted_radius > 0:
                from_deg = (idx_side * step_angle) - half_step
                to_deg = ((idx_side + 1) * step_angle) - half_step
                cell = self.create_cell(center_point, from_deg, to_deg,
                                        adjusted_radius)
                cell_feature.setGeometry(cell)
                sink.addFeature(cell_feature, QgsFeatureSink.FastInsert)

        radius_step = radius / 5
        center = QgsGeometry.fromPointXY(center_point)
        for idx_side in range(5):
            buffer_radius = radius_step * (idx_side + 1)
            ring_anchor = center.buffer(buffer_radius, 32)
            ring_anchor = QgsGeometry(ring_anchor.constGet().boundary())

            anchor_feature = QgsFeature(anchor_fields)
            anchor_feature.setGeometry(ring_anchor)
            anchor_feature.setAttributes([buffer_radius, None])
            anchor.addFeature(anchor_feature, QgsFeatureSink.FastInsert)

        north = [
            'E', 'ENE', 'NE', 'NNE', 'N', 'NNW', 'NW', 'WNW', 'W', 'WSW', 'SW',
            'SSW', 'S', 'SSE', 'SE', 'ESE'
        ]
        for idx_side in range(16):
            degree = 22.5 * idx_side
            anchor_line = self.create_line(center_point, degree, radius)
            anchor_feature = QgsFeature(anchor_fields)
            anchor_feature.setGeometry(anchor_line)
            anchor_feature.setAttributes([None, north[idx_side]])
            anchor.addFeature(anchor_feature, QgsFeatureSink.FastInsert)

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

        use_weight_field = self.parameterAsBool(parameters, self.USE_WEIGHT_FIELD, context)
        idx_field = -1
        if use_weight_field:
            weight_field = self.parameterAsString(parameters, self.WEIGHT_FIELD, context)
            idx_field = source.fields().lookupField(weight_field)

        center_type = self.parameterAsEnum(parameters, self.CENTER, context)

        cell_fields = QgsFields()
        cell_fields.append(QgsField("count", QVariant.Int))
        cell_field_list = ["min", "max", "sum", "mean", "std_dev", "var"]
        for field_name in cell_field_list:
            cell_fields.append(QgsField(field_name, QVariant.Double))
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                          cell_fields, QgsWkbTypes.Polygon, source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        anchor_fields = QgsFields()
        anchor_fields.append(QgsField("distance", QVariant.Double))
        anchor_fields.append(QgsField("direction", QVariant.String))
        (anchor, anchor_id) = self.parameterAsSink(parameters, self.OUTPUT_ANCHOR, context,
                          anchor_fields, QgsWkbTypes.LineString, source.sourceCrs())
        if anchor is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT_ANCHOR))

        # Center : X, Center Y. if not provided, the center of point layer will be used
        source_crs = iface.mapCanvas().mapSettings().destinationCrs()
        target_crs = source.sourceCrs()
        transform = QgsCoordinateTransform(source_crs, target_crs, QgsProject.instance())
        
        extent = source.sourceExtent()
        if center_type == 1:
            extent = iface.mapCanvas().extent()
        elif center_type == 2:
            extent = iface.mapCanvas().fullExtent()
            
        if center_type != 0 and source_crs != target_crs:
           extent = transform.transformBoundingBox(extent);
                
        center_point = extent.center(); # QgsPoint

        minx = extent.xMinimum()
        miny = extent.yMinimum()
        maxx = extent.xMaximum()
        maxy = extent.yMaximum()

        radius = (((maxx - minx)**2 + (maxy - miny)**2) **0.5) / 2.0

        #. create spatial index
        spatial_index = QgsSpatialIndex(source)

        step_angle = 360.0 / self.DEFAULT_SEGS
        half_step = step_angle / 2.0

        minVal = sys.float_info.max
        maxVal = sys.float_info.min
        centroid_features = {}
        for idx_side in range(self.DEFAULT_SEGS):
            from_deg = (idx_side * step_angle) - half_step
            to_deg = ((idx_side + 1) * step_angle) - half_step
            feedback.setProgress(int(100 * idx_side / self.DEFAULT_SEGS))

            cell = self.create_cell(center_point, from_deg, to_deg, radius)

            # sptial query
            hasIntersections = False
            points = spatial_index.intersects(cell.boundingBox())
            if len(points) > 0:
                hasIntersections = True

            visitor = StatisticsVisitor()
            if hasIntersections:
                for fid in points:
                    request = QgsFeatureRequest().setFilterFid(fid)
                    point_feature = next(source.getFeatures(request))
                    if cell.contains(point_feature.geometry()):
                        if idx_field >= 0 :
                            weight = str(point_feature.attributes()[idx_field])
                            try:
                                visitor.visit(float(weight))
                            except:
                                pass  # Ignore fields with non-numeric values
                        else:
                            visitor.visit(1)

            # create and write ring feature
            cell_feature = QgsFeature(cell_fields)
            cell_feature.setGeometry(cell)
            ret = visitor.result()
            minVal = min(minVal, ret[3]);
            maxVal = max(maxVal, ret[3]);
            cell_feature.setAttributes(ret)
            centroid_features[idx_side] = cell_feature

        #. write features
        for idx_side in range(self.DEFAULT_SEGS):
            cell_feature = centroid_features[idx_side]
            value = cell_feature.attributes()[3]
            linear_trans_value = (value - minVal) / (maxVal - minVal);
            adjusted_radius = linear_trans_value * radius
            if adjusted_radius > 0:
                from_deg = (idx_side * step_angle) - half_step
                to_deg = ((idx_side + 1) * step_angle) - half_step
                cell = self.create_cell(center_point, from_deg, to_deg, adjusted_radius)
                cell_feature.setGeometry(cell)
                sink.addFeature(cell_feature, QgsFeatureSink.FastInsert)

        radius_step = radius / 5
        center =  QgsGeometry.fromPointXY(center_point)
        for idx_side in range(5):
            buffer_radius = radius_step * (idx_side + 1)
            ring_anchor = center.buffer(buffer_radius, 32)
            ring_anchor = QgsGeometry(ring_anchor.constGet().boundary())
            
            anchor_feature = QgsFeature(anchor_fields)
            anchor_feature.setGeometry(ring_anchor)
            anchor_feature.setAttributes([buffer_radius, None])
            anchor.addFeature(anchor_feature, QgsFeatureSink.FastInsert)

        north = ['E', 'ENE', 'NE', 'NNE', 'N', 'NNW', 'NW', 'WNW', 'W',  'WSW', 'SW', 'SSW', 'S', 'SSE', 'SE', 'ESE']
        for idx_side in range(16):
            degree = 22.5 * idx_side
            anchor_line = self.create_line(center_point, degree, radius)
            anchor_feature = QgsFeature(anchor_fields)
            anchor_feature.setGeometry(anchor_line)
            anchor_feature.setAttributes([None, north[idx_side]])
            anchor.addFeature(anchor_feature, QgsFeatureSink.FastInsert)

        return {self.OUTPUT: dest_id}