add center and radius params

This commit is contained in:
Matteo Rosati
2026-01-18 11:41:55 +01:00
parent 6cd8997516
commit de53b760c4
2 changed files with 177 additions and 11 deletions

113
app.py
View File

@@ -32,9 +32,12 @@ def get_plexts_from_db():
timestamp_from: Minimum timestamp in milliseconds (optional)
timestamp_to: Maximum timestamp in milliseconds (optional)
limit: Maximum number of results to return (optional, default: 100)
center: Geographic center point in format "latitude,longitude" (optional, requires radius)
radius: Search radius in meters (optional, requires center)
Returns:
JSON response with list of plexts (without _id field), limited by the limit parameter
JSON response with list of plexts (without _id field), limited by the limit parameter.
When using geospatial query (center and radius), includes a 'distance' field in meters.
"""
try:
# Parse query parameters
@@ -42,6 +45,8 @@ def get_plexts_from_db():
timestamp_from = request.args.get("timestamp_from")
timestamp_to = request.args.get("timestamp_to")
limit_param = request.args.get("limit")
center = request.args.get("center")
radius = request.args.get("radius")
# Validate and convert timestamp parameters to integers if provided
if timestamp_from is not None:
@@ -67,6 +72,44 @@ def get_plexts_from_db():
else:
limit = 100
# Validate geospatial parameters
# Both center and radius must be provided together, or neither
if (center is not None) != (radius is not None):
return jsonify({
"error": "Both 'center' and 'radius' parameters must be provided together"
}), 400
# Parse and validate center parameter if provided
center_coords = None
if center is not None:
try:
# Parse center in format "latitude,longitude"
lat_str, lng_str = center.split(',')
lat = float(lat_str)
lng = float(lng_str)
# Validate latitude and longitude ranges
if not (-90 <= lat <= 90):
return jsonify({"error": "Latitude must be between -90 and 90"}), 400
if not (-180 <= lng <= 180):
return jsonify({"error": "Longitude must be between -180 and 180"}), 400
center_coords = (lat, lng)
except ValueError:
return jsonify({
"error": "Invalid center format. Use format: 'latitude,longitude' (e.g., '45.573661,12.365208')"
}), 400
# Parse and validate radius parameter if provided
radius_meters = None
if radius is not None:
try:
radius_meters = int(radius)
if radius_meters <= 0:
return jsonify({"error": "Radius must be a positive integer"}), 400
except ValueError:
return jsonify({"error": "Radius must be an integer"}), 400
# Build MongoDB filter query
filter_query = {}
@@ -91,18 +134,59 @@ def get_plexts_from_db():
collection = db[collection_name]
try:
# Projection to exclude _id field
projection = {"_id": 0}
# Ensure geospatial index exists
try:
collection.create_index([("coordinates", "2dsphere")], background=True)
except PyMongoError as e:
logger.warning(f"Could not create geospatial index: {e}")
# Execute query with sorting (timestamp DESC - most recent first) and dynamic limit
cursor = (
collection.find(filter=filter_query, projection=projection)
.sort("timestamp", -1)
.limit(limit)
)
# Execute query based on whether geospatial parameters are provided
if center_coords and radius_meters:
# Use $geoNear aggregation for geospatial query
lat, lng = center_coords
pipeline = [
{
"$geoNear": {
"near": {
"type": "Point",
"coordinates": [lng, lat] # GeoJSON uses [longitude, latitude]
},
"distanceField": "distance", # Add distance to results
"maxDistance": radius_meters, # in meters
"spherical": True # Use spherical geometry
}
},
{
"$match": filter_query # Apply other filters
},
{
"$sort": {"timestamp": -1} # Sort by timestamp (most recent first)
},
{
"$limit": limit
},
{
"$project": {
"_id": 0 # Exclude _id field
}
}
]
# Convert cursor to list
plexts = list(cursor)
# Execute aggregation pipeline
plexts = list(collection.aggregate(pipeline))
else:
# Use standard find() query for non-geospatial queries
projection = {"_id": 0}
# Execute query with sorting (timestamp DESC - most recent first) and dynamic limit
cursor = (
collection.find(filter=filter_query, projection=projection)
.sort("timestamp", -1)
.limit(limit)
)
# Convert cursor to list
plexts = list(cursor)
return jsonify({"count": len(plexts), "plexts": plexts}), 200
@@ -237,8 +321,15 @@ def index():
"timestamp_from": "Minimum timestamp in milliseconds (optional)",
"timestamp_to": "Maximum timestamp in milliseconds (optional)",
"limit": "Maximum number of results to return (optional, default: 100)",
"center": "Geographic center point in format 'latitude,longitude' (optional, requires radius)",
"radius": "Search radius in meters (optional, requires center)",
},
"authentication": "Basic Auth required",
"geospatial_query": {
"description": "When both center and radius are provided, returns plexts within the specified radius from the center point. If neither is provided, returns all plexts matching other filters.",
"example": "center=45.573661,12.365208&radius=1000",
"response_field": "distance (meters) - included in results when using geospatial query",
},
},
},
}