1515from sqlalchemy import func
1616from sqlalchemy .orm import Session
1717from sqlalchemy .orm import joinedload
18+ from shared .helpers .locations import ReverseGeocodingStrategy
1819
1920from location_group_utils import (
2021 ERROR_STATUS_CODE ,
3738 Gtfsfeed ,
3839)
3940from shared .helpers .logger import get_logger
41+ from shared .helpers .runtime_metrics import track_metrics
4042
4143
4244@with_db_session
@@ -231,6 +233,7 @@ def create_geojson_aggregate(
231233 data_type : str ,
232234 logger ,
233235 extraction_urls : List [str ] = None ,
236+ public : bool = True ,
234237) -> None :
235238 """Create a GeoJSON file with the aggregated locations. This file will be uploaded to GCS and used for
236239 visualization."""
@@ -286,7 +289,8 @@ def create_geojson_aggregate(
286289 bucket = storage_client .bucket (bucket_name )
287290 blob = bucket .blob (f"{ stable_id } /geolocation.geojson" )
288291 blob .upload_from_string (json .dumps (json_data ))
289- blob .make_public ()
292+ if public :
293+ blob .make_public ()
290294 logger .info ("GeoJSON data saved to %s" , blob .name )
291295
292296
@@ -316,7 +320,7 @@ def get_or_create_location(
316320
317321
318322@with_db_session
319- def extract_location_aggregates (
323+ def extract_location_aggregates_per_point (
320324 feed_id : str ,
321325 stops_df : pd .DataFrame ,
322326 location_aggregates : Dict [str , GeopolygonAggregate ],
@@ -381,6 +385,9 @@ def extract_location_aggregates(
381385
382386
383387@with_db_session
388+ @track_metrics (
389+ metrics = ("time" , "memory" , "cpu" ), logger = get_logger (__name__ , "stable_id" )
390+ )
384391def update_dataset_bounding_box (
385392 dataset_id : str , stops_df : pd .DataFrame , db_session : Session
386393) -> shapely .Polygon :
@@ -418,9 +425,27 @@ def reverse_geolocation_process(
418425) -> Tuple [str , int ] | Tuple [Dict , int ]:
419426 """
420427 Main function to handle reverse geolocation processing.
421- """
422- overall_start = datetime .now ()
428+ @:request: Flask request object containing the parameters for the reverse geolocation process.
429+ Example request JSON(GTFS):
430+ {
431+ "stable_id": "example_stable_id",
432+ "dataset_id": "example_dataset_id",
433+ "stops_url": "https://example.com/path/to/stops.csv",
434+ "data_type": "gtfs",
435+ "strategy": "per-point"
436+ }
437+ Example request JSON(GBFS):
438+ {
439+ "stable_id": "example_stable_id",
440+ "dataset_id": "example_dataset_id",
441+ "station_information_url": "https://example.com/path/to/station_information.json",
442+ "vehicle_status_url": "https://example.com/path/to/vehicle_status.json",
443+ "free_bike_status_url": "https://example.com/path/to/free_bike_status.json",
444+ "strategy": "per-point"
445+ }
446+ @:returns: A tuple containing a message and the HTTP status code.
423447
448+ """
424449 try :
425450 # Parse request parameters
426451 (
@@ -429,6 +454,8 @@ def reverse_geolocation_process(
429454 dataset_id ,
430455 data_type ,
431456 extraction_urls ,
457+ public ,
458+ strategy ,
432459 ) = parse_request_parameters (request )
433460
434461 # Remove duplicate lat/lon points
@@ -447,19 +474,16 @@ def reverse_geolocation_process(
447474 return str (e ), ERROR_STATUS_CODE
448475
449476 logger = get_logger (__name__ , stable_id )
450-
451477 try :
452478 # Update the bounding box of the dataset
453479 bounding_box = update_dataset_bounding_box (dataset_id , stops_df )
454480
455- # Get Cached Geopolygons
456- feed_id , location_groups , stops_df = get_cached_geopolygons (
457- stable_id , stops_df , logger
481+ location_groups = reverse_geolocation (
482+ strategy ,
483+ stable_id ,
484+ stops_df ,
485+ logger ,
458486 )
459- logger .info ("Number of location groups extracted: %s" , len (location_groups ))
460-
461- # Extract Location Groups
462- extract_location_aggregates (feed_id , stops_df , location_groups , logger )
463487
464488 # Create GeoJSON Aggregate
465489 create_geojson_aggregate (
@@ -470,15 +494,12 @@ def reverse_geolocation_process(
470494 data_type = data_type ,
471495 extraction_urls = extraction_urls ,
472496 logger = logger ,
497+ public = public ,
473498 )
474-
475- # Overall Time
476- overall_duration = (datetime .now () - overall_start ).total_seconds ()
477- logger .info (f"Total time taken for the process: { overall_duration :.2f} seconds" )
478499 logger .info (
479- "COMPLETED. Processed %s stops for stable ID %s. "
500+ "COMPLETED. Processed %s stops for stable ID %s with strategy . "
480501 "Retrieved %s locations." ,
481- total_stops ,
502+ len ( stops_df ) ,
482503 stable_id ,
483504 len (location_groups ),
484505 )
@@ -493,3 +514,34 @@ def reverse_geolocation_process(
493514 logger .error ("Error processing geopolygons: %s" , e )
494515 logger .error (traceback .format_exc ()) # Log full traceback
495516 return str (e ), ERROR_STATUS_CODE
517+
518+
519+ @track_metrics (
520+ metrics = ("time" , "memory" , "cpu" ), logger = get_logger (__name__ , "stable_id" )
521+ )
522+ def reverse_geolocation (
523+ strategy ,
524+ stable_id ,
525+ stops_df ,
526+ logger ,
527+ ):
528+ """
529+ Reverse geolocation processing based on the specified strategy.
530+ """
531+ logger .info ("Processing geopolygons with per-point strategy." )
532+ # Get Cached Geopolygons
533+ feed_id , location_groups , stops_df = get_cached_geopolygons (
534+ stable_id , stops_df , logger
535+ )
536+ logger .info ("Number of location groups extracted: %s" , len (location_groups ))
537+ # Extract Location Groups
538+ match strategy :
539+ case ReverseGeocodingStrategy .PER_POINT :
540+ extract_location_aggregates_per_point (
541+ feed_id , stops_df , location_groups , logger
542+ )
543+ case _:
544+ logger .error ("Invalid strategy: %s" , strategy )
545+ return f"Invalid strategy: { strategy } " , ERROR_STATUS_CODE
546+
547+ return location_groups
0 commit comments