output geojson for mesh connections

This commit is contained in:
Laura Klünder 2024-03-30 17:01:19 +01:00
parent 7798206fe2
commit b1ae6ea391
2 changed files with 52 additions and 9 deletions

View file

@ -9,6 +9,7 @@ from ninja import Schema, UploadedFile
from ninja.pagination import paginate from ninja.pagination import paginate
from pydantic import PositiveInt, field_validator from pydantic import PositiveInt, field_validator
from shapely.geometry.geo import mapping from shapely.geometry.geo import mapping
from shapely.geometry import LineString
from c3nav.api.auth import APIKeyAuth, auth_permission_responses, auth_responses, validate_responses from c3nav.api.auth import APIKeyAuth, auth_permission_responses, auth_responses, validate_responses
from c3nav.api.exceptions import API404, APIConflict, APIRequestValidationFailed from c3nav.api.exceptions import API404, APIConflict, APIRequestValidationFailed
@ -16,7 +17,8 @@ from c3nav.api.schema import BaseSchema
from c3nav.mapdata.models.geometry.space import RangingBeacon from c3nav.mapdata.models.geometry.space import RangingBeacon
from c3nav.mesh.messages import MeshMessageType, MeshMessage from c3nav.mesh.messages import MeshMessageType, MeshMessage
from c3nav.mesh.models import FirmwareBuild, FirmwareVersion, NodeMessage, MeshNode from c3nav.mesh.models import FirmwareBuild, FirmwareVersion, NodeMessage, MeshNode
from c3nav.mesh.schemas import BoardType, ChipType, FirmwareImage, RangingBeaconGeoFeature from c3nav.mesh.schemas import BoardType, ChipType, FirmwareImage, RangingBeaconGeoFeature, MeshConnectionGeoFeature, \
RangingMapData
mesh_api_router = APIRouter(tags=["mesh"], auth=APIKeyAuth(permissions={"mesh_control"})) mesh_api_router = APIRouter(tags=["mesh"], auth=APIKeyAuth(permissions={"mesh_control"}))
@ -250,24 +252,32 @@ def messages_list(request, filters: Query[MessagesFilter]):
@mesh_api_router.get( @mesh_api_router.get(
'/map/{level_id}/', summary="ranging beacons map", '/map/{level_id}/', summary="ranging beacons map",
description="query and filter all received mesh messages", description="query and filter all received mesh messages",
response={200: list[RangingBeaconGeoFeature], **auth_permission_responses}, response={200: RangingMapData, **auth_permission_responses},
openapi_extra={"security": [{"APIKeyAuth": ["mesh_control"]}]} openapi_extra={"security": [{"APIKeyAuth": ["mesh_control"]}]}
) )
def mesh_map(request, level_id: int): def mesh_map(request, level_id: int):
beacons = RangingBeacon.objects.filter(space__level__id=level_id) beacons = RangingBeacon.objects.all().select_related("space")
beacon_ids = set(beacon.id for beacon in beacons) beacon_ids = set(beacon.id for beacon in beacons)
nodes = {
node.address: node
for node in MeshNode.objects.all().prefetch_last_messages().prefetch_ranging_beacon()
}
nodes_for_beacons = { nodes_for_beacons = {
node.ranging_beacon.id: node node.ranging_beacon.id: node
for node in MeshNode.objects.all().prefetch_last_messages().prefetch_ranging_beacon() for node in nodes.values()
if node.ranging_beacon and node.ranging_beacon.id in beacon_ids if node.ranging_beacon and node.ranging_beacon.id in beacon_ids
} }
result = [] mesh_connection_result = []
for beacon in RangingBeacon.objects.filter(space__level__id=level_id): ranging_beacon_result = []
for beacon in beacons:
if beacon.space.level_id != level_id:
continue
node = nodes_for_beacons.get(beacon.id, None) node = nodes_for_beacons.get(beacon.id, None)
node_uplink = None if node is None else node.get_uplink() node_uplink = None if node is None else node.get_uplink()
result.append({
ranging_beacon_result.append({
"type": "Feature", "type": "Feature",
"geometry": mapping(beacon.geometry), "geometry": mapping(beacon.geometry),
"properties": { "properties": {
@ -281,4 +291,21 @@ def mesh_map(request, level_id: int):
} }
}) })
return result if node_uplink:
uplink_node = nodes[node_uplink.node_id]
if uplink_node.ranging_beacon:
mesh_connection_result.append({
"type": "Feature",
"geometry": mapping(LineString(
list(beacon.geometry.coords) + list(uplink_node.ranging_beacon.geometry.coords)
)),
"properties": {
"ap": uplink_node.address,
"sta": node.address,
}
})
return RangingMapData(
connections=mesh_connection_result,
ranging_beacons=ranging_beacon_result,
)

View file

@ -9,7 +9,7 @@ from pydantic.main import BaseModel
from pydantic.types import Discriminator, NonNegativeInt, NonPositiveInt from pydantic.types import Discriminator, NonNegativeInt, NonPositiveInt
from pydantic_extra_types.mac_address import MacAddress from pydantic_extra_types.mac_address import MacAddress
from c3nav.api.schema import BaseSchema, PointSchema from c3nav.api.schema import BaseSchema, PointSchema, LineSchema
from c3nav.mesh.cformats import AsDefinition, AsHex, CName, ExistingCStruct, discriminator_value, \ from c3nav.mesh.cformats import AsDefinition, AsHex, CName, ExistingCStruct, discriminator_value, \
CEnum, TwoNibblesEncodable CEnum, TwoNibblesEncodable
@ -301,3 +301,19 @@ class RangingBeaconGeoFeature(BaseSchema):
type: Literal["Feature"] type: Literal["Feature"]
geometry: PointSchema geometry: PointSchema
properties: RangingBeaconGeoFeatureProperties properties: RangingBeaconGeoFeatureProperties
class MeshConnectionGeoFeatureProperties(BaseSchema):
sta: MacAddress
ap: MacAddress
class MeshConnectionGeoFeature(BaseSchema):
type: Literal["Feature"]
geometry: LineSchema
properties: MeshConnectionGeoFeatureProperties
class RangingMapData(BaseSchema):
connections: list[MeshConnectionGeoFeature]
ranging_beacons: list[RangingBeaconGeoFeature]