From d94027da9ad9bd28d756b12ed09e08dda3483343 Mon Sep 17 00:00:00 2001 From: Adrien Date: Sun, 23 Apr 2023 11:14:11 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=A5=20All=20location=20points=20provid?= =?UTF-8?q?ed=20by=20backend=20are=20in=20EPSG:3857?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/idfm_interface/idfm_interface.py | 75 +++++++++++++------ backend/backend/models/stop.py | 14 ++-- backend/backend/schemas/stop.py | 15 ++-- backend/main.py | 28 +++---- backend/pyproject.toml | 1 + 5 files changed, 75 insertions(+), 58 deletions(-) diff --git a/backend/backend/idfm_interface/idfm_interface.py b/backend/backend/idfm_interface/idfm_interface.py index c6cf33e..2c04e99 100644 --- a/backend/backend/idfm_interface/idfm_interface.py +++ b/backend/backend/idfm_interface/idfm_interface.py @@ -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: diff --git a/backend/backend/models/stop.py b/backend/backend/models/stop.py index d286878..b686741 100644 --- a/backend/backend/models/stop.py +++ b/backend/backend/models/stop.py @@ -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) diff --git a/backend/backend/schemas/stop.py b/backend/backend/schemas/stop.py index 39b8fc0..2bd6f6e 100644 --- a/backend/backend/schemas/stop.py +++ b/backend/backend/schemas/stop.py @@ -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] diff --git a/backend/main.py b/backend/main.py index 7387c1f..9eeef97 100644 --- a/backend/main.py +++ b/backend/main.py @@ -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}" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 6d16503..a593d1b 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -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"]