💥 All location points provided by backend are in EPSG:3857
This commit is contained in:
@@ -12,10 +12,9 @@ from typing import (
|
||||
|
||||
from aiofiles import open as async_open
|
||||
from aiohttp import ClientSession
|
||||
|
||||
from msgspec import ValidationError
|
||||
from msgspec.json import Decoder
|
||||
from rich import print
|
||||
from pyproj import Transformer
|
||||
from shapefile import Reader as ShapeFileReader, ShapeRecord
|
||||
|
||||
from ..db import Database
|
||||
@@ -64,6 +63,8 @@ class IdfmInterface:
|
||||
|
||||
self._http_headers = {"Accept": "application/json", "apikey": self._api_key}
|
||||
|
||||
self._epsg2154_epsg3857_transformer = Transformer.from_crs(2154, 3857)
|
||||
|
||||
self._json_stops_decoder = Decoder(type=List[IdfmStop])
|
||||
self._json_stop_areas_decoder = Decoder(type=List[IdfmStopArea])
|
||||
self._json_connection_areas_decoder = Decoder(type=List[IdfmConnectionArea])
|
||||
@@ -89,19 +90,19 @@ class IdfmInterface:
|
||||
(
|
||||
StopShape,
|
||||
self._request_stop_shapes,
|
||||
IdfmInterface._format_idfm_stop_shapes,
|
||||
self._format_idfm_stop_shapes,
|
||||
),
|
||||
(
|
||||
ConnectionArea,
|
||||
self._request_idfm_connection_areas,
|
||||
IdfmInterface._format_idfm_connection_areas,
|
||||
self._format_idfm_connection_areas,
|
||||
),
|
||||
(
|
||||
StopArea,
|
||||
self._request_idfm_stop_areas,
|
||||
IdfmInterface._format_idfm_stop_areas,
|
||||
self._format_idfm_stop_areas,
|
||||
),
|
||||
(Stop, self._request_idfm_stops, IdfmInterface._format_idfm_stops),
|
||||
(Stop, self._request_idfm_stops, self._format_idfm_stops),
|
||||
)
|
||||
|
||||
for model, get_method, format_method in STEPS:
|
||||
@@ -391,23 +392,26 @@ class IdfmInterface:
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def _format_idfm_stops(*stops: IdfmStop) -> Iterable[Stop]:
|
||||
def _format_idfm_stops(self, *stops: IdfmStop) -> Iterable[Stop]:
|
||||
for stop in stops:
|
||||
fields = stop.fields
|
||||
|
||||
try:
|
||||
created_ts = int(fields.arrcreated.timestamp()) # type: ignore
|
||||
except AttributeError:
|
||||
created_ts = None
|
||||
|
||||
epsg3857_point = self._epsg2154_epsg3857_transformer.transform(
|
||||
fields.arrxepsg2154, fields.arryepsg2154
|
||||
)
|
||||
|
||||
yield Stop(
|
||||
id=int(fields.arrid),
|
||||
name=fields.arrname,
|
||||
latitude=fields.arrgeopoint.lat,
|
||||
longitude=fields.arrgeopoint.lon,
|
||||
epsg3857_x=epsg3857_point[0],
|
||||
epsg3857_y=epsg3857_point[1],
|
||||
town_name=fields.arrtown,
|
||||
postal_region=fields.arrpostalregion,
|
||||
xepsg2154=fields.arrxepsg2154,
|
||||
yepsg2154=fields.arryepsg2154,
|
||||
transport_mode=TransportMode(fields.arrtype.value),
|
||||
version=fields.arrversion,
|
||||
created_ts=created_ts,
|
||||
@@ -419,53 +423,76 @@ class IdfmInterface:
|
||||
record_ts=int(stop.record_timestamp.timestamp()),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _format_idfm_stop_areas(*stop_areas: IdfmStopArea) -> Iterable[StopArea]:
|
||||
def _format_idfm_stop_areas(self, *stop_areas: IdfmStopArea) -> Iterable[StopArea]:
|
||||
for stop_area in stop_areas:
|
||||
fields = stop_area.fields
|
||||
|
||||
try:
|
||||
created_ts = int(fields.zdacreated.timestamp()) # type: ignore
|
||||
except AttributeError:
|
||||
created_ts = None
|
||||
|
||||
epsg3857_point = self._epsg2154_epsg3857_transformer.transform(
|
||||
fields.zdaxepsg2154, fields.zdayepsg2154
|
||||
)
|
||||
|
||||
yield StopArea(
|
||||
id=int(fields.zdaid),
|
||||
name=fields.zdaname,
|
||||
town_name=fields.zdatown,
|
||||
postal_region=fields.zdapostalregion,
|
||||
xepsg2154=fields.zdaxepsg2154,
|
||||
yepsg2154=fields.zdayepsg2154,
|
||||
epsg3857_x=epsg3857_point[0],
|
||||
epsg3857_y=epsg3857_point[1],
|
||||
type=StopAreaType(fields.zdatype.value),
|
||||
version=fields.zdaversion,
|
||||
created_ts=created_ts,
|
||||
changed_ts=int(fields.zdachanged.timestamp()),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _format_idfm_connection_areas(
|
||||
self,
|
||||
*connection_areas: IdfmConnectionArea,
|
||||
) -> Iterable[ConnectionArea]:
|
||||
for connection_area in connection_areas:
|
||||
|
||||
epsg3857_point = self._epsg2154_epsg3857_transformer.transform(
|
||||
connection_area.zdcxepsg2154, connection_area.zdcyepsg2154
|
||||
)
|
||||
|
||||
yield ConnectionArea(
|
||||
id=int(connection_area.zdcid),
|
||||
name=connection_area.zdcname,
|
||||
town_name=connection_area.zdctown,
|
||||
postal_region=connection_area.zdcpostalregion,
|
||||
xepsg2154=connection_area.zdcxepsg2154,
|
||||
yepsg2154=connection_area.zdcyepsg2154,
|
||||
epsg3857_x=epsg3857_point[0],
|
||||
epsg3857_y=epsg3857_point[1],
|
||||
transport_mode=StopAreaType(connection_area.zdctype.value),
|
||||
version=connection_area.zdcversion,
|
||||
created_ts=int(connection_area.zdccreated.timestamp()),
|
||||
changed_ts=int(connection_area.zdcchanged.timestamp()),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _format_idfm_stop_shapes(*shape_records: ShapeRecord) -> Iterable[StopShape]:
|
||||
def _format_idfm_stop_shapes(
|
||||
self, *shape_records: ShapeRecord
|
||||
) -> Iterable[StopShape]:
|
||||
for shape_record in shape_records:
|
||||
|
||||
epsg3857_points = [
|
||||
self._epsg2154_epsg3857_transformer.transform(*point)
|
||||
for point in shape_record.shape.points
|
||||
]
|
||||
|
||||
bbox_it = iter(shape_record.shape.bbox)
|
||||
epsg3857_bbox = [
|
||||
self._epsg2154_epsg3857_transformer.transform(*point)
|
||||
for point in zip(bbox_it, bbox_it)
|
||||
]
|
||||
|
||||
yield StopShape(
|
||||
id=shape_record.record[1],
|
||||
type=shape_record.shape.shapeType,
|
||||
bounding_box=list(shape_record.shape.bbox),
|
||||
points=shape_record.shape.points,
|
||||
epsg3857_bbox=epsg3857_bbox,
|
||||
epsg3857_points=epsg3857_points,
|
||||
)
|
||||
|
||||
async def render_line_picto(self, line: Line) -> tuple[None | str, None | str]:
|
||||
@@ -508,7 +535,7 @@ class IdfmInterface:
|
||||
print("---------------------------------------------------------------------")
|
||||
return data
|
||||
|
||||
async def get_next_passages(self, stop_point_id: str) -> IdfmResponse | None:
|
||||
async def get_next_passages(self, stop_point_id: int) -> IdfmResponse | None:
|
||||
ret = None
|
||||
params = {"MonitoringRef": f"STIF:StopPoint:Q:{stop_point_id}:"}
|
||||
async with ClientSession(headers=self._http_headers) as session:
|
||||
|
@@ -48,8 +48,8 @@ class _Stop(Base):
|
||||
name = mapped_column(String, nullable=False, index=True)
|
||||
town_name = mapped_column(String, nullable=False)
|
||||
postal_region = mapped_column(String, nullable=False)
|
||||
xepsg2154 = mapped_column(BigInteger, nullable=False)
|
||||
yepsg2154 = mapped_column(BigInteger, nullable=False)
|
||||
epsg3857_x = mapped_column(Float, nullable=False)
|
||||
epsg3857_y = mapped_column(Float, nullable=False)
|
||||
|
||||
version = mapped_column(String, nullable=False)
|
||||
created_ts = mapped_column(BigInteger)
|
||||
@@ -111,8 +111,6 @@ class Stop(_Stop):
|
||||
|
||||
id = mapped_column(BigInteger, ForeignKey("_stops.id"), primary_key=True)
|
||||
|
||||
latitude = mapped_column(Float, nullable=False)
|
||||
longitude = mapped_column(Float, nullable=False)
|
||||
transport_mode = mapped_column(Enum(TransportMode), nullable=False)
|
||||
accessibility = mapped_column(Enum(IdfmState), nullable=False)
|
||||
visual_signs_available = mapped_column(Enum(IdfmState), nullable=False)
|
||||
@@ -191,8 +189,8 @@ class StopShape(Base):
|
||||
|
||||
id = mapped_column(BigInteger, primary_key=True) # Same id than ConnectionArea
|
||||
type = mapped_column(Integer, nullable=False)
|
||||
bounding_box = mapped_column(JSON)
|
||||
points = mapped_column(JSON)
|
||||
epsg3857_bbox = mapped_column(JSON)
|
||||
epsg3857_points = mapped_column(JSON)
|
||||
|
||||
__tablename__ = "stop_shapes"
|
||||
|
||||
@@ -206,8 +204,8 @@ class ConnectionArea(Base):
|
||||
name = mapped_column(String, nullable=False)
|
||||
town_name = mapped_column(String, nullable=False)
|
||||
postal_region = mapped_column(String, nullable=False)
|
||||
xepsg2154 = mapped_column(BigInteger, nullable=False)
|
||||
yepsg2154 = mapped_column(BigInteger, nullable=False)
|
||||
epsg3857_x = mapped_column(Float, nullable=False)
|
||||
epsg3857_y = mapped_column(Float, nullable=False)
|
||||
transport_mode = mapped_column(Enum(StopAreaType), nullable=False)
|
||||
|
||||
version = mapped_column(String, nullable=False)
|
||||
|
@@ -7,10 +7,8 @@ class Stop(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
town: str
|
||||
lat: float
|
||||
lon: float
|
||||
# xepsg2154: int
|
||||
# yepsg2154: int
|
||||
epsg3857_x: float
|
||||
epsg3857_y: float
|
||||
lines: list[str]
|
||||
|
||||
|
||||
@@ -18,15 +16,16 @@ class StopArea(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
town: str
|
||||
# xepsg2154: int
|
||||
# yepsg2154: int
|
||||
type: StopAreaType
|
||||
lines: list[str] # SNCF lines are linked to stop areas and not stops.
|
||||
stops: list[Stop]
|
||||
|
||||
|
||||
Point = tuple[float, float]
|
||||
|
||||
|
||||
class StopShape(BaseModel):
|
||||
id: int
|
||||
type: int
|
||||
bbox: list[float]
|
||||
points: list[tuple[float, float]]
|
||||
epsg3857_bbox: list[Point]
|
||||
epsg3857_points: list[Point]
|
||||
|
@@ -58,8 +58,6 @@ async def shutdown():
|
||||
await db.disconnect()
|
||||
|
||||
|
||||
# /addwidget https://localhost:4443/static/#?widgetId=$matrix_widget_id&userId=$matrix_user_id
|
||||
# /addwidget https://localhost:3000/widget?widgetId=$matrix_widget_id&userId=$matrix_user_id
|
||||
STATIC_ROOT = "../frontend/"
|
||||
app.mount("/widget", StaticFiles(directory=STATIC_ROOT, html=True), name="widget")
|
||||
|
||||
@@ -94,22 +92,16 @@ async def get_line(line_id: str) -> LineSchema:
|
||||
|
||||
|
||||
def _format_stop(stop: Stop) -> StopSchema:
|
||||
# print(stop.__dict__)
|
||||
return StopSchema(
|
||||
id=stop.id,
|
||||
name=stop.name,
|
||||
town=stop.town_name,
|
||||
# xepsg2154=stop.xepsg2154,
|
||||
# yepsg2154=stop.yepsg2154,
|
||||
lat=stop.latitude,
|
||||
lon=stop.longitude,
|
||||
epsg3857_x=stop.epsg3857_x,
|
||||
epsg3857_y=stop.epsg3857_y,
|
||||
lines=[line.id for line in stop.lines],
|
||||
)
|
||||
|
||||
|
||||
# châtelet
|
||||
|
||||
|
||||
@app.get("/stop/")
|
||||
async def get_stop(
|
||||
name: str = "", limit: int = 10
|
||||
@@ -142,8 +134,6 @@ async def get_stop(
|
||||
id=stop_area.id,
|
||||
name=stop_area.name,
|
||||
town=stop_area.town_name,
|
||||
# xepsg2154=stop_area.xepsg2154,
|
||||
# yepsg2154=stop_area.yepsg2154,
|
||||
type=stop_area.type,
|
||||
lines=[line.id for line in stop_area.lines],
|
||||
stops=formatted_stops,
|
||||
@@ -187,8 +177,9 @@ async def get_next_passages(stop_id: str) -> NextPassagesSchema | None:
|
||||
|
||||
dst_names = call.DestinationDisplay
|
||||
dsts = [dst.value for dst in dst_names] if dst_names else []
|
||||
|
||||
print(f"{call.ArrivalPlatformName = }")
|
||||
arrivalPlatformName = (
|
||||
call.ArrivalPlatformName.value if call.ArrivalPlatformName else None
|
||||
)
|
||||
|
||||
next_passage = NextPassageSchema(
|
||||
line=line_id,
|
||||
@@ -197,9 +188,7 @@ async def get_next_passages(stop_id: str) -> NextPassagesSchema | None:
|
||||
atStop=call.VehicleAtStop,
|
||||
aimedArrivalTs=optional_datetime_to_ts(call.AimedArrivalTime),
|
||||
expectedArrivalTs=optional_datetime_to_ts(call.ExpectedArrivalTime),
|
||||
arrivalPlatformName=call.ArrivalPlatformName.value
|
||||
if call.ArrivalPlatformName
|
||||
else None,
|
||||
arrivalPlatformName=arrivalPlatformName,
|
||||
aimedDepartTs=optional_datetime_to_ts(call.AimedDepartureTime),
|
||||
expectedDepartTs=optional_datetime_to_ts(call.ExpectedDepartureTime),
|
||||
arrivalStatus=call.ArrivalStatus.value,
|
||||
@@ -250,7 +239,10 @@ async def get_stop_shape(stop_id: int) -> StopShapeSchema | None:
|
||||
and (shape := await StopShape.get_by_id(connection_area.id)) is not None
|
||||
):
|
||||
return StopShapeSchema(
|
||||
id=shape.id, type=shape.type, bbox=shape.bounding_box, points=shape.points
|
||||
id=shape.id,
|
||||
type=shape.type,
|
||||
epsg3857_bbox=shape.epsg3857_bbox,
|
||||
epsg3857_points=shape.epsg3857_points,
|
||||
)
|
||||
|
||||
msg = f"No shape found for stop {stop_id}"
|
||||
|
@@ -17,6 +17,7 @@ uvicorn = "^0.20.0"
|
||||
asyncpg = "^0.27.0"
|
||||
msgspec = "^0.12.0"
|
||||
pyshp = "^2.3.1"
|
||||
pyproj = "^3.5.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
|
Reference in New Issue
Block a user