Skip to content

Commit

Permalink
add stops logic
Browse files Browse the repository at this point in the history
  • Loading branch information
CbI3PAHb committed Jun 2, 2023
1 parent f618a98 commit 8ac7cb4
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 4 deletions.
48 changes: 47 additions & 1 deletion api/fastapi_service/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from fastapi import FastAPI, HTTPException, Query
from uvicorn import run
from os import getenv
from schemas import CityBase, RegionBase, GraphBase
from schemas import CityBase, RegionBase, GraphBase, StopsBase
from database import database, engine, metadata

import pandas as pd
Expand Down Expand Up @@ -137,6 +137,52 @@ async def city_graph_poly(
logger.info(f"{request} {status_code} {detail}")
return services.graph_to_scheme(points, edges, pprop, wprop)


@app.post('/api/city/stops/region/', response_model=StopsBase)
@logger.catch(exclude=HTTPException)
async def stops_graph(
city_id: int,
regions_ids: List[int],
):
request = f"GET /api/cities/stops/?city_id={city_id}&regions={regions_ids}"
status_code = 200
detail = "OK"

stops, edges, stops_prop = await services.stops_graph_from_ids(city_id=city_id, regions_ids=regions_ids, regions=regions_df)

if stops is None:
status_code = 404
detail = "NOT FOUND"
logger.error(f"{request} {status_code} {detail}")
raise HTTPException(status_code=status_code, detail=detail)

logger.info(f"{request} {status_code} {detail}")
return services.get_stops_answer(stops, edges, stops_prop)


@app.post('/api/city/stops/bbox/{city_id}/', response_model=StopsBase)
@logger.catch(exclude=HTTPException)
async def stops_graph_poly(
city_id: int,
polygons_as_list: List[List[List[float]]]
):
request = f"POST /api/city/stops/bbox/{city_id}/"
status_code = 200
detail = "OK"

polygon = services.list_to_polygon(polygons=polygons_as_list)

stops, edges, stops_prop = await services.stops_graph_from_poly(city_id=city_id, polygon=polygon)

if stops is None:
status_code = 404
detail = "NOT FOUND"
logger.error(f"{request} {status_code} {detail}")
raise HTTPException(status_code=status_code, detail=detail)

logger.info(f"{request} {status_code} {detail}")
return services.get_stops_answer(stops, edges, stops_prop)



# Useless:
Expand Down
9 changes: 8 additions & 1 deletion api/fastapi_service/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ class GraphBase(BaseModel):
# reversed_matrix_csv : str


class StopsBase(BaseModel):
stops_properties_csv : str
stops_edges_csv : str
stops_nodes_csv : str



class RouteBase(BaseModel):
routes_properties_csv : str
routes_edges_csv : str
routes_nodes_csv : str
113 changes: 111 additions & 2 deletions api/fastapi_service/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from models import City, CityProperty, Point
from shapely.geometry.point import Point as ShapelyPoint
from database import *
from schemas import CityBase, PropertyBase, PointBase, RegionBase, GraphBase
from schemas import CityBase, PropertyBase, PointBase, RegionBase, GraphBase, StopsBase
from shapely.geometry.multilinestring import MultiLineString
from shapely.geometry.linestring import LineString
from shapely.geometry.polygon import Polygon
Expand Down Expand Up @@ -76,6 +76,17 @@ def graph_to_scheme(points, edges, pprop, wprop) -> GraphBase:
reversed_edges_csv=r_edges_str, reversed_nodes_csv=r_nodes_str)
# reversed_matrix_csv=r_matrix_str)

def get_stops_answer(stops, edges, stops_prop) -> StopsBase:
stops_csv_str = list_to_csv_str(stops, ['id', 'longitude', 'latitude'])[0]
stops_prop_csv_str = list_to_csv_str(stops_prop, ['id', 'property', 'value'])[0]
stops_edges_csv_str = list_to_csv_str(edges, ['id', 'id_route', 'id_src', 'id_dest'])[0]

return StopsBase(
stops_nodes_csv=stops_csv_str,
stops_properties_csv=stops_prop_csv_str,
stops_edges_csv=stops_edges_csv_str,
)


async def property_to_scheme(property : CityProperty) -> PropertyBase:
if property is None:
Expand Down Expand Up @@ -480,6 +491,14 @@ async def graph_from_ids(city_id : int, regions_ids : List[int], regions : GeoDa
if polygon == None:
return None, None, None, None
return await graph_from_poly(city_id=city_id, polygon=polygon)

async def stops_graph_from_ids(city_id : int, regions_ids : List[int], regions : GeoDataFrame):
polygon = polygons_from_region(regions_ids=regions_ids, regions=regions)
if polygon == None:
return None, None, None
return await stops_graph_from_poly(city_id=city_id, polygon=polygon)



def point_obj_to_list(db_record) -> List:
return [db_record.id, db_record.longitude, db_record.latitude]
Expand All @@ -494,6 +513,14 @@ def record_obj_to_pprop(record):
return [record.id_point ,record.property ,record.value]


def point_obj_to_list(db_record):
return [db_record.id, db_record.longitude, db_record.latitude]


def stops_edge_obj_to_list(db_record):
return [db_record.id, db_record.id_route, db_record.id_src, db_record.id_dest]


async def graph_from_poly(city_id, polygon):
bbox = polygon.bounds # min_lon, min_lat, max_lon, max_lat

Expand Down Expand Up @@ -643,6 +670,32 @@ def filter_by_polygon(polygon, edges, points):
return points_filtred, edges_filtred, ways_prop_ids, points_ids



def filter_stops_by_polygon(polygon, points, edges):
"""
points -> [...[id, longitude, latitude], ...]
edges -> [.. [id_edge, id_route, id_src, id_dest], ... ]
"""
points_ids = set()
points_filtred = []
edges_filtred = []

for point in points:
lon = point[1]
lat = point[2]
if polygon.contains(ShapelyPoint(lon, lat)):
points_ids.add(point[0])
points_filtred.append(point)

for edge in edges:
id_from = edge[2]
id_to = edge[3]
if (id_from in points_ids) and (id_to in points_ids):
edges_filtred.append(edge)

return points_filtred, edges_filtred, points_ids


# nodata = '-'
# merging_col = 'id_way'

Expand Down Expand Up @@ -973,4 +1026,60 @@ def getRoutesGraph(city_id):
adjacency_list[route1].add(route2)
adjacency_list[route1].remove(route1)

return adjacency_list
return adjacency_list


async def stops_graph_from_poly(city_id, polygon):
bbox = polygon.bounds # min_lon, min_lat, max_lon, max_lat

conn = engine.connect()

q = CityAsync.select().where(CityAsync.c.id == city_id)
city = await database.fetch_one(q)
if city is None or not city.downloaded:
return None, None, None

query = text(
f"""SELECT n.id, n.longitude, n.latitude
FROM "Nodes" n
JOIN "Stops" s ON s.id_node = n.id
JOIN "RoutesTable" r ON s.id_route = r.id
WHERE r.id_city = {city_id}
AND (n.longitude BETWEEN {bbox[0]} AND {bbox[2]})
AND (n.latitude BETWEEN {bbox[1]} AND {bbox[3]});
"""
)
result = await database.fetch_all(query)
stops = list(map(point_obj_to_list, result)) # [...[id, longitude, latitude]...]

query = text(
f"""
SELECT e.id, e.id_src, e.id_dest, e.id_route
FROM EdgesTable as e
JOIN Nodes as n ON n.id = e.id_src
WHERE (n.longitude BETWEEN {bbox[0]} AND {bbox[2]})
AND (n.latitude BETWEEN {bbox[1]} AND {bbox[3]});
"""
)
results = conn.execute(query).fetchall()

stops_edges = list(map(stops_edge_obj_to_list, results))

points_filtred, edges_filtred, points_prop_ids = filter_stops_by_polygon(polygon, stops, stops_edges)

ids_points = build_in_query('id_point', points_prop_ids)

query = text(
f"""
SELECT id_point, property, value FROM
(SELECT id_point, id_property, value FROM "NodesProperty" WHERE {ids_points}) AS p
JOIN "NodesPropertyTable" ON p.id_property = "NodesPropertyTable".id;
"""
)

result = conn.execute(query).fetchall()

stops_prop = list(map(record_obj_to_pprop, result)) # [...[id.point, property, value]...]

return points_filtred, edges_filtred, stops_prop

0 comments on commit 8ac7cb4

Please sign in to comment.