banniere

Le portail francophone de la géomatique


Toujours pas inscrit ? Mot de passe oublié ?
Nom d'utilisateur    Mot de passe              Toujours pas inscrit ?   Mot de passe oublié ?

Annonce

Rencontres QGIS 2025

L'appel à participation est ouvert jusqu'au 19 janvier 2025!

#1 Thu 16 February 2023 16:56

Abdoulayi111
Participant occasionnel
Date d'inscription: 17 Oct 2018
Messages: 15

Clustering spatial avec poids maximum par cluster

Je souhaite faire du clustering spatial autrement dit regrouper des données ayant une proximité spatiale en se basant sur le réseau.
En effet, je dispose de deux couches de shapefiles en entrée.
Une couche "linéaire" constituant le réseau, celle sur laquelle la distance de proximité doit se baser.
Une autre couche du type "Point" sera utilisée pour le clustering.

Il est important de noter que ma couche "Point" dispose d'un champ poids.
Le cluster doit tenir compte de ce poids, pour que la somme totale des valeurs de poids dans un cluster ne dépasse pas la valeur indiquée : Ex : 200.

J'ai déjà réalisé un code utilisant DBSCAN. Cependant, cet algorithme ne tient pas compte du poids, mais de la distance séparant deux entités proches pour être considérée appartenant au même cluster.
Dans mon cas, la distance a peu d'importance tant que les entités sont les plus proches et que la somme totale des poids dans un cluster est respectée.


Je prends tout ce qui pourrait m'aider à avancer dans cette recherche.
En vous remerciant par avance de votre aide.

Hors ligne

 

#2 Thu 16 February 2023 17:03

Abdoulayi111
Participant occasionnel
Date d'inscription: 17 Oct 2018
Messages: 15

Re: Clustering spatial avec poids maximum par cluster

Voici le code en question avec DBSCAN ci-dessous. Il ne répond pas à mes besoins à cause de la non prise en compte du poids et de la distance maximum entre deux entités d'un même cluster.


'''python

from sklearn.cluster import DBSCAN
import networkx as nx
import pandas as pd
import numpy as np
from qgis.core import QgsProject, QgsField, QgsFeatureIterator
from qgis.PyQt.QtCore import QVariant


class Clustering:
    """
    Clustering logical grouping of points (populations) based on the linear networks (road networks) """

    def __init__(self, copy_network_layer, node_nearest_point_layer) -> None:
        """
        :param copy_network_layer: copy_network_layer: name of the new network layer
        :param node_nearest_point_layer: Name of the new node table.
        """
        self.copy_network_layer = copy_network_layer
        self.node_nearest_point_layer = node_nearest_point_layer

    @staticmethod
    def formation_graph_in_network(feat_network_layer: QgsFeatureIterator) -> nx:
        """
        :param feat_network_layer: Network layer name
        :return: Graph constitutes the origin and the end of the network
        """
        network_graph = nx.Graph()

        for feat_line in feat_network_layer:
            start_point = feat_line.geometry().asPolyline()[0]
            end_point = feat_line.geometry().asPolyline()[-1]
            network_graph.add_edge(start_point, end_point)

        return network_graph

    @staticmethod
    def association_node_closest_network(feat_points_layer: QgsFeatureIterator,
                                         network_graph: nx) -> tuple[np, pd]:
        """
        Association of each point entity to the network closest to it
        :param feat_points_layer: point layer name
        :param network_graph: origin and the end of the network
        :return:
        """
        # Récupération des points en utilisant qgis.core
        points = []
        gid = []

        for feat_pt in feat_points_layer:
            point = feat_pt.geometry().asPoint()
            nearest_node = min(network_graph.nodes(),
                               key=lambda x: np.linalg.norm(np.array(x) -
                                                            np.array([point.x(),
                                                                      point.y()])))
            gid.append(feat_pt.id())
            points.append(nearest_node)

        data = pd.DataFrame({"gid": gid})
        return np.array(points), data

    def cluster_dbscan(self, field_weight: str) -> pd.DataFrame:
        """
        :param field_weight: This field will be created for contains information cluster
        The same cluster will have same number.
        :return: Each entity is associated with the number of its cluster
        """
        network_layer = QgsProject.instance().mapLayersByName(self.copy_network_layer)[0]
        points_layer = QgsProject.instance().mapLayersByName(self.node_nearest_point_layer)[0]

        network_graph = self.formation_graph_in_network(network_layer.getFeatures())
        points, clustering = self.association_node_closest_network(points_layer.getFeatures(),
                                                                   network_graph)

        # Clustering en utilisant DBSCAN
        # esp : distance maximale entre deux échantillons pour qu'ils soient considérés comme
        # faisant partie du même voisinage. Il ne s'agit pas de distance entre toutes les éléments d'un même cluster
        # min_samples : nombre minimum d'échantillons requis pour former une densité d'échantillons
        dbscan = DBSCAN(eps=500, min_samples=3, algorithm="auto")

        clustering[field_weight] = dbscan.fit_predict(points)

        points_layer.dataProvider().addAttributes([QgsField("label",
                                                            QVariant.Int,
                                                            comment="Valeur des cluster")])

        points_layer.updateExtents()
        points_layer.updateFields()

        # If not existe, it will be create
        idx_label = points_layer.fields().indexFromName("label")

        for [gid, label] in clustering.values:
            attrs = {idx_label: int(label)}
            points_layer.dataProvider().changeAttributeValues({gid: attrs})

        return clustering


if __name__ == "__main__":

    cluster_instance = Clustering("network_layer", "pt_node_nearest")
    cluster_instance.cluster_dbscan("poids")
'''

Hors ligne

 

Pied de page des forums

Powered by FluxBB