From a4c435c398e62ac623b8fdcff61a053206b2206a Mon Sep 17 00:00:00 2001
From: Helldragon67 <kilian.scheidecker@orange.fr>
Date: Wed, 4 Dec 2024 19:23:26 +0100
Subject: [PATCH] better pep8

---
 backend/src/main.py                      |   2 +-
 backend/src/structs/shopping_location.py |  23 -----
 backend/src/utils/cluster_processing.py  | 103 +++++++++++++++++++----
 backend/src/utils/landmarks_manager.py   |   2 +-
 4 files changed, 89 insertions(+), 41 deletions(-)
 delete mode 100644 backend/src/structs/shopping_location.py

diff --git a/backend/src/main.py b/backend/src/main.py
index 11e3039..2cc558f 100644
--- a/backend/src/main.py
+++ b/backend/src/main.py
@@ -61,7 +61,7 @@ def new_trip(preferences: Preferences,
                               attractiveness=0,
                               must_do=True,
                               n_tags = 0)
-    
+
     end_landmark = Landmark(name='finish',
                             type='finish',
                             location=(end[0], end[1]),
diff --git a/backend/src/structs/shopping_location.py b/backend/src/structs/shopping_location.py
deleted file mode 100644
index d784ca4..0000000
--- a/backend/src/structs/shopping_location.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Literal, Optional
-from pydantic import BaseModel
-
-
-class ShoppingLocation(BaseModel):
-    """"
-    A classe representing an interesting area for shopping.
-    
-    It can represent either a general area or a specifc route with start and end point.
-    The importance represents the number of shops found in this cluster.
-    
-    Attributes:
-        type :       either a 'street' or 'area' (representing a denser field of shops).
-        importance : size of the cluster (number of points).
-        centroid :   center of the cluster.
-        start :      if the type is a street it goes from here...
-        end :        ...to here
-    """
-    type: Literal['street', 'area']
-    importance: int
-    centroid: tuple
-    start: Optional[list] = None
-    end: Optional[list] = None
\ No newline at end of file
diff --git a/backend/src/utils/cluster_processing.py b/backend/src/utils/cluster_processing.py
index a6ef15b..2114bcb 100644
--- a/backend/src/utils/cluster_processing.py
+++ b/backend/src/utils/cluster_processing.py
@@ -11,13 +11,28 @@ from ..structs.landmark import Landmark
 from ..utils.get_time_separation import get_distance
 from ..constants import AMENITY_SELECTORS_PATH, LANDMARK_PARAMETERS_PATH, OPTIMIZER_PARAMETERS_PATH, OSM_CACHE_DIR
 
+
 class ShoppingLocation(BaseModel):
+    """"
+    A classe representing an interesting area for shopping.
+    
+    It can represent either a general area or a specifc route with start and end point.
+    The importance represents the number of shops found in this cluster.
+    
+    Attributes:
+        type :       either a 'street' or 'area' (representing a denser field of shops).
+        importance : size of the cluster (number of points).
+        centroid :   center of the cluster.
+        start :      if the type is a street it goes from here...
+        end :        ...to here
+    """
     type: Literal['street', 'area']
     importance: int
     centroid: tuple
     # start: Optional[list] = None      # for later use if we want to have streets as well
     # end: Optional[list] = None
 
+
 class ShoppingManager:
 
     logger = logging.getLogger(__name__)
@@ -31,7 +46,11 @@ class ShoppingManager:
 
     def __init__(self, bbox: tuple) -> None:
         """
-        Upon intialization, generate the list of shops used for cluster points.
+        Upon intialization, generate the point cloud used for cluster detection.
+        The points represent bag/clothes shops and general boutiques.
+
+        Args: 
+            bbox: The bounding box coordinates (around:radius, center_lat, center_lon).
         """
 
         # Initialize overpass and cache
@@ -52,27 +71,34 @@ class ShoppingManager:
         except Exception as e:
             self.logger.error(f"Error fetching landmarks: {e}")
 
-        if len(result.elements()) > 0 :
-
+        if len(result.elements()) == 0 :
+            self.valid = False
+        
+        else :
             points = []
             for elem in result.elements() :
                 points.append(tuple((elem.lat(), elem.lon())))
 
             self.all_points = np.array(points)
-            self.valid = True
-
-        else : 
-            self.valid = False
+            self.valid = True            
 
 
     def generate_shopping_landmarks(self) -> list[Landmark]:
+        """
+        Generate shopping landmarks based on clustered locations.
+
+        This method first generates clusters of locations and then  extracts shopping-related 
+        locations from these clusters. It transforms each shopping location into a `Landmark` object.
+
+        Returns:
+            list[Landmark]: A list of `Landmark` objects representing shopping locations.
+                            Returns an empty list if no clusters are found.
+        """
 
-        # First generate the clusters
         self.generate_clusters()
 
-        # Return empty list if no clusters were found
         if len(set(self.cluster_labels)) == 0 :
-            return []
+            return []       # Return empty list if no clusters were found
 
         # Then generate the shopping locations
         self.generate_shopping_locations()
@@ -87,6 +113,19 @@ class ShoppingManager:
 
 
     def generate_clusters(self) :
+        """
+        Generate clusters of points using DBSCAN.
+
+        This method applies the DBSCAN clustering algorithm with different
+        parameters depending on the size of the city (number of points). 
+        It filters out noise points and keeps only the largest clusters.
+
+        The method updates:
+            - `self.cluster_points`: The points belonging to clusters.
+            - `self.cluster_labels`: The labels for the points in clusters.
+        
+        The method also calls `filter_clusters()` to retain only the largest clusters.
+        """
 
         # Apply DBSCAN to find clusters. Choose different settings for different cities.
         if len(self.all_points) > 200 :
@@ -105,6 +144,19 @@ class ShoppingManager:
 
 
     def generate_shopping_locations(self) :
+        """
+        Generate shopping locations based on clustered points.
+
+        This method iterates over the different clusters, calculates the centroid 
+        (as the mean of the points within each cluster), and assigns an importance 
+        based on the size of the cluster.
+
+        The generated shopping locations are stored in `self.shopping_locations` 
+        as a list of `ShoppingLocation` objects, each with:
+            - `type`: Set to 'area'.
+            - `centroid`: The calculated centroid of the cluster.
+            - `importance`: The number of points in the cluster.
+        """
 
         locations = []
 
@@ -127,6 +179,21 @@ class ShoppingManager:
 
 
     def create_landmark(self, shopping_location: ShoppingLocation) -> Landmark:
+        """
+        Create a Landmark object based on the given shopping location.
+
+        This method queries the Overpass API for nearby neighborhoods and shopping malls 
+        within a 1000m radius around the shopping location centroid. It selects the closest 
+        result and creates a landmark with the associated details such as name, type, and OSM ID.
+
+        Parameters:
+            shopping_location (ShoppingLocation): A ShoppingLocation object containing 
+            the centroid and importance of the area.
+
+        Returns:
+            Landmark: A Landmark object containing details such as the name, type, 
+            location, attractiveness, and OSM details.
+        """
 
         # Define the bounding box for a given radius around the coordinates
         lat, lon = shopping_location.centroid
@@ -153,10 +220,10 @@ class ShoppingManager:
             try:
                 result = self.overpass.query(query)
             except Exception as e:
-                raise Exception("query unsuccessful")
+                self.logger.error(f"Error fetching landmarks: {e}")
+                continue
 
             for elem in result.elements():
-
                 location = (elem.centerLat(), elem.centerLon())
 
                 if location[0] is None : 
@@ -168,10 +235,10 @@ class ShoppingManager:
                 if  d < min_dist :
                     min_dist = d
                     new_name = elem.tag('name')
-                    osm_type = elem.type()              # Add type: 'way' or 'relation'
-                    osm_id = elem.id()                  # Add OSM id 
+                    osm_type = elem.type()      # Add type: 'way' or 'relation'
+                    osm_id = elem.id()          # Add OSM id 
 
-                    # add english name if it exists
+                    # Add english name if it exists
                     try :
                         new_name_en = elem.tag('name:en')
                     except:
@@ -191,7 +258,11 @@ class ShoppingManager:
 
     def filter_clusters(self):
         """
-        Remove clusters of lesser importance.
+        Filter clusters to retain only the 5 largest clusters by point count.
+
+        This method calculates the size of each cluster and filters out all but the 
+        5 largest clusters. It then updates the cluster points and labels to reflect 
+        only those from the top 5 clusters.
         """
         label_counts = np.bincount(self.cluster_labels)
 
diff --git a/backend/src/utils/landmarks_manager.py b/backend/src/utils/landmarks_manager.py
index 310692f..883fca9 100644
--- a/backend/src/utils/landmarks_manager.py
+++ b/backend/src/utils/landmarks_manager.py
@@ -184,7 +184,7 @@ class LandmarkManager:
         Fetches landmarks of a specified type from OpenStreetMap (OSM) within a bounding box centered on given coordinates.
 
         Args:
-            bbox (tuple[float, float, float, float]): The bounding box coordinates (min_lat, min_lon, max_lat, max_lon).
+            bbox (tuple[float, float, float, float]): The bounding box coordinates (around:radius, center_lat, center_lon).
             amenity_selector (dict): The Overpass API query selector for the desired landmark type. 
             landmarktype (str): The type of the landmark (e.g., 'sightseeing', 'nature', 'shopping').
             score_function (callable): The function to compute the score of the landmark based on its attributes.