2024-12-04 12:42:03 +01:00
|
|
|
from contextlib import suppress
|
2024-12-03 18:42:33 +01:00
|
|
|
from typing import Annotated, ClassVar, Literal, Optional, Union, Any
|
2023-11-18 21:29:35 +01:00
|
|
|
|
2024-12-03 18:42:33 +01:00
|
|
|
from django.db.models import Model
|
|
|
|
from pydantic import Discriminator, Tag
|
2023-11-18 21:29:35 +01:00
|
|
|
from pydantic import Field as APIField
|
2023-11-24 01:05:38 +01:00
|
|
|
from pydantic import NonNegativeFloat, PositiveFloat, PositiveInt
|
2023-11-18 21:29:35 +01:00
|
|
|
|
2024-12-04 11:35:03 +01:00
|
|
|
from c3nav.api.schema import BaseSchema, GeometrySchema, PointSchema, AnyGeometrySchema, PolygonSchema
|
2023-11-18 21:29:35 +01:00
|
|
|
from c3nav.api.utils import NonEmptyStr
|
2024-03-24 15:24:36 +01:00
|
|
|
from c3nav.mapdata.models import LocationGroup
|
2024-12-04 11:35:03 +01:00
|
|
|
from c3nav.mapdata.models.geometry.base import GeometryMixin
|
2023-11-24 01:05:38 +01:00
|
|
|
from c3nav.mapdata.schemas.model_base import (AnyLocationID, AnyPositionID, CustomLocationID, DjangoModelSchema,
|
2023-12-11 20:49:50 +01:00
|
|
|
LabelSettingsSchema, LocationSchema, PositionID,
|
2023-11-26 17:55:23 +01:00
|
|
|
SimpleGeometryLocationsSchema, SimpleGeometryPointAndBoundsSchema,
|
|
|
|
SimpleGeometryPointSchema, SpecificLocationSchema, TitledSchema,
|
2023-11-23 21:11:31 +01:00
|
|
|
WithAccessRestrictionSchema, WithLevelSchema,
|
|
|
|
WithLineStringGeometrySchema, WithPointGeometrySchema,
|
2023-12-04 18:58:49 +01:00
|
|
|
WithPolygonGeometrySchema, WithSpaceSchema, schema_definitions,
|
2024-12-16 11:36:28 +01:00
|
|
|
schema_description, LocationSlugSchema, WithGeometrySchema)
|
2024-12-04 11:35:03 +01:00
|
|
|
from c3nav.mapdata.utils.geometry import smart_mapping
|
|
|
|
from c3nav.mapdata.utils.json import format_geojson
|
2023-11-18 21:29:35 +01:00
|
|
|
|
|
|
|
|
2023-11-19 15:34:08 +01:00
|
|
|
class LevelSchema(SpecificLocationSchema, DjangoModelSchema):
|
2023-11-19 00:12:10 +01:00
|
|
|
"""
|
|
|
|
A physical level of the map, containing building, spaces, doors…
|
|
|
|
|
2023-11-19 16:36:46 +01:00
|
|
|
A level is a specific location, and can therefore be routed to and from, as well as belong to location groups.
|
2023-11-19 00:12:10 +01:00
|
|
|
"""
|
2023-11-18 21:29:35 +01:00
|
|
|
short_label: NonEmptyStr = APIField(
|
|
|
|
title="short label (for level selector)",
|
|
|
|
description="unique among levels",
|
|
|
|
)
|
2024-12-19 10:56:38 +01:00
|
|
|
level_index: NonEmptyStr = APIField(
|
|
|
|
title="level index (for coordinates)",
|
|
|
|
description="unique among levels",
|
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
on_top_of: Union[
|
|
|
|
Annotated[PositiveInt, APIField(title="level ID", description="level this level is on top of", example=1)],
|
|
|
|
Annotated[None, APIField(title="null", description="this is a main level, not on top of any other")]
|
|
|
|
] = APIField(
|
2023-11-18 21:29:35 +01:00
|
|
|
title="on top of level ID",
|
|
|
|
description="if set, this is not a main level, but it's on top of this other level"
|
|
|
|
)
|
|
|
|
base_altitude: float = APIField(
|
|
|
|
title="base/default altitude",
|
2023-12-04 18:58:49 +01:00
|
|
|
description="default ground altitude for this level, if it can't be determined using altitude markers.",
|
2023-11-18 21:29:35 +01:00
|
|
|
)
|
|
|
|
default_height: PositiveFloat = APIField(
|
|
|
|
title="default ceiling height",
|
2023-12-04 18:58:49 +01:00
|
|
|
description="default ceiling height for all spaces that don't set their own",
|
|
|
|
example=2.5
|
2023-11-18 21:29:35 +01:00
|
|
|
)
|
|
|
|
door_height: PositiveFloat = APIField(
|
|
|
|
title="door height",
|
2023-12-04 18:58:49 +01:00
|
|
|
description="height for all doors on this level",
|
|
|
|
example="2.0",
|
2023-11-18 21:29:35 +01:00
|
|
|
)
|
|
|
|
|
2023-11-19 15:34:08 +01:00
|
|
|
|
|
|
|
class BuildingSchema(WithPolygonGeometrySchema, WithLevelSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A non-outdoor part of the map.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class SpaceSchema(WithPolygonGeometrySchema, SpecificLocationSchema, WithLevelSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
An accessible area on a level. It can be outside-only or inside-only.
|
|
|
|
|
2023-11-19 16:36:46 +01:00
|
|
|
A space is a specific location, and can therefore be routed to and from, as well as belong to location groups.
|
2023-11-19 15:34:08 +01:00
|
|
|
"""
|
|
|
|
outside: bool = APIField(
|
|
|
|
title="outside only",
|
|
|
|
description="determines whether to truncate to buildings or to the outside of buildings"
|
|
|
|
)
|
|
|
|
height: Optional[PositiveFloat] = APIField(
|
|
|
|
title="ceiling height",
|
|
|
|
description="if not set, default height for this level will be used"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-11-19 16:48:40 +01:00
|
|
|
class DoorSchema(WithPolygonGeometrySchema, WithAccessRestrictionSchema, WithLevelSchema, DjangoModelSchema):
|
2023-11-19 15:34:08 +01:00
|
|
|
"""
|
|
|
|
A link between two spaces
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class HoleSchema(WithPolygonGeometrySchema, WithSpaceSchema):
|
|
|
|
"""
|
|
|
|
A hole in a space, showing the levels below
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class AreaSchema(WithPolygonGeometrySchema, SpecificLocationSchema, WithSpaceSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
An area inside a space.
|
|
|
|
|
2023-11-19 16:36:46 +01:00
|
|
|
An area is a specific location, and can therefore be routed to and from, as well as belong to location groups.
|
2023-11-19 15:34:08 +01:00
|
|
|
"""
|
|
|
|
slow_down_factor: PositiveFloat = APIField(
|
|
|
|
title="slow-down factor",
|
2023-12-04 18:58:49 +01:00
|
|
|
description="how much walking in this area is slowed down, overlapping areas are multiplied",
|
|
|
|
example=1.0,
|
2023-11-19 15:34:08 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class StairSchema(WithLineStringGeometrySchema, WithSpaceSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A line sharply dividing the accessible surface of a space into two different altitudes.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class RampSchema(WithPolygonGeometrySchema, WithSpaceSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
An area in which the surface has an altitude gradient.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class BaseObstacleSchema(WithSpaceSchema, DjangoModelSchema):
|
|
|
|
height: PositiveFloat = APIField(
|
|
|
|
title="height",
|
|
|
|
description="size of the obstacle in the z dimension"
|
|
|
|
)
|
|
|
|
altitude: NonNegativeFloat = APIField(
|
|
|
|
title="altitude above ground",
|
|
|
|
description="altitude above ground"
|
|
|
|
)
|
2023-11-19 16:36:46 +01:00
|
|
|
color: Optional[NonEmptyStr] = APIField(
|
2023-11-19 15:34:08 +01:00
|
|
|
title="color",
|
|
|
|
description="an optional color for this obstacle"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class ObstacleSchema(WithPolygonGeometrySchema, BaseObstacleSchema):
|
|
|
|
"""
|
|
|
|
An obstacle to be subtracted from the accessible surface of a space.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class LineObstacleSchema(WithLineStringGeometrySchema, BaseObstacleSchema):
|
|
|
|
"""
|
|
|
|
An obstacle to be subtracted from the accessible surface of a space, defined as a line with width.
|
|
|
|
"""
|
2024-12-04 11:35:03 +01:00
|
|
|
buffered_geometry: Union[
|
|
|
|
PolygonSchema,
|
|
|
|
Annotated[None, APIField(title="null", description="geometry not available of excluded from endpoint")]
|
|
|
|
] = APIField(
|
|
|
|
None,
|
|
|
|
title="buffered geometry",
|
|
|
|
description="line turned into a polygon with the given width, "
|
|
|
|
"can be null if not available or excluded from endpoint",
|
|
|
|
)
|
2023-11-19 15:34:08 +01:00
|
|
|
width: PositiveFloat = APIField(
|
|
|
|
title="width",
|
|
|
|
description="width of the line"
|
|
|
|
)
|
|
|
|
|
2024-12-04 11:35:03 +01:00
|
|
|
@classmethod
|
|
|
|
def get_overrides(cls, value) -> dict:
|
|
|
|
# todo: move into model
|
|
|
|
value: GeometryMixin
|
|
|
|
if "geometry" in value.get_deferred_fields() or value.geometry is None:
|
|
|
|
return {
|
|
|
|
**super().get_overrides(value),
|
|
|
|
"buffered_geometry": None,
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
**super().get_overrides(value),
|
|
|
|
"buffered_geometry": (
|
|
|
|
format_geojson(smart_mapping(value.buffered_geometry), rounded=False)
|
|
|
|
if not getattr(value, '_hide_geometry', False) else None
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
2023-11-19 15:34:08 +01:00
|
|
|
|
|
|
|
class ColumnSchema(WithPolygonGeometrySchema, WithSpaceSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A ceiling-high obstacle subtracted from the space, effectively creating a "building" again.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class POISchema(WithPointGeometrySchema, SpecificLocationSchema, WithSpaceSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A point of interest inside a space.
|
|
|
|
|
2023-11-19 16:36:46 +01:00
|
|
|
A POI is a specific location, and can therefore be routed to and from, as well as belong to location groups.
|
2023-11-19 15:34:08 +01:00
|
|
|
"""
|
|
|
|
pass
|
2023-11-19 16:36:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
class LeaveDescriptionSchema(WithSpaceSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A description for leaving a space to enter another space.
|
|
|
|
"""
|
|
|
|
target_space: PositiveInt = APIField(
|
|
|
|
title="target space",
|
|
|
|
description="the space that is being entered",
|
|
|
|
)
|
|
|
|
descriptions: dict[NonEmptyStr, NonEmptyStr] = APIField(
|
|
|
|
title="description (all languages)",
|
|
|
|
description="property names are the ISO-language code. languages may be missing.",
|
|
|
|
example={
|
|
|
|
"en": "Stanley walked through the red door.",
|
|
|
|
"de": "Stanley ging durch die rote Tür.",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
description: NonEmptyStr = APIField(
|
|
|
|
title="description (preferred language)",
|
|
|
|
description="preferred language based on the Accept-Language header."
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class CrossDescriptionSchema(WithSpaceSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A description for crossing through a space from one space to another.
|
|
|
|
"""
|
|
|
|
origin_space: PositiveInt = APIField(
|
|
|
|
title="origin space",
|
|
|
|
description="the space from which the main space is being entered",
|
|
|
|
)
|
|
|
|
target_space: PositiveInt = APIField(
|
|
|
|
title="target space",
|
|
|
|
description="the space that is being entered from the main space",
|
|
|
|
)
|
|
|
|
descriptions: dict[NonEmptyStr, NonEmptyStr] = APIField(
|
|
|
|
title="description (all languages)",
|
|
|
|
description="property names are the ISO-language code. languages may be missing.",
|
|
|
|
example={
|
|
|
|
"en": "Go straight ahead through the big glass doors.",
|
|
|
|
"de": "gehe geradeaus durch die Glastüren.",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
description: NonEmptyStr = APIField(
|
|
|
|
title="description (preferred language)",
|
|
|
|
description="preferred language based on the Accept-Language header."
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class LocationGroupSchema(LocationSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A location group, always belonging to a location group category.
|
|
|
|
|
|
|
|
A location group is a (non-specific) location, which means it can be routed to and from.
|
|
|
|
"""
|
|
|
|
category: PositiveInt = APIField(
|
|
|
|
title="category",
|
|
|
|
description="location group category that this location group belongs to",
|
|
|
|
)
|
|
|
|
priority: int = APIField() # todo: ???
|
|
|
|
hierarchy: int = APIField() # todo: ???
|
2024-12-03 16:07:07 +01:00
|
|
|
label_settings: Optional[PositiveInt] = APIField(
|
2023-11-19 16:36:46 +01:00
|
|
|
default=None,
|
|
|
|
title="label settings",
|
2023-12-04 18:58:49 +01:00
|
|
|
description=(
|
|
|
|
schema_description(LabelSettingsSchema) +
|
|
|
|
"\n\nlocations can override this setting"
|
|
|
|
)
|
2023-11-19 16:36:46 +01:00
|
|
|
)
|
2024-03-24 15:24:36 +01:00
|
|
|
can_report_missing: LocationGroup.CanReportMissing = APIField(
|
2023-11-19 16:36:46 +01:00
|
|
|
title="report missing locations",
|
2024-03-24 15:24:36 +01:00
|
|
|
description="whether this location group can be used to report missing locations",
|
2023-11-19 16:36:46 +01:00
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
color: Union[
|
|
|
|
Annotated[NonEmptyStr, APIField(title="color", description="a valid CSS color expression")],
|
|
|
|
Annotated[None, APIField(title="null", description="default/no color will be used")],
|
|
|
|
] = APIField(
|
2023-11-19 16:36:46 +01:00
|
|
|
title="color",
|
|
|
|
description="an optional color for spaces and areas with this group"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2024-12-04 12:08:19 +01:00
|
|
|
class LocationRedirectSchema(LocationSlugSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A location group redirect describes a slug that, when used redirects to another location
|
|
|
|
"""
|
|
|
|
slug: NonEmptyStr = APIField( # todo: copy from somewhere?
|
|
|
|
title="location slug",
|
|
|
|
description="a slug is a unique way to refer to a location. while locations have a shared ID space, slugs"
|
|
|
|
"are meants to be human-readable and easy to remember.",
|
|
|
|
example="entrance",
|
|
|
|
)
|
|
|
|
target: PositiveInt = APIField(
|
|
|
|
title="target",
|
|
|
|
description="location to redirect to",
|
|
|
|
)
|
|
|
|
target_slug: NonEmptyStr = APIField(
|
|
|
|
title="effective target location slug",
|
|
|
|
description="effective slug of the target location",
|
|
|
|
example="lobby",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-11-19 16:48:40 +01:00
|
|
|
class LocationGroupCategorySchema(TitledSchema, DjangoModelSchema):
|
2023-11-19 16:36:46 +01:00
|
|
|
"""
|
|
|
|
A location group category can hold either one or multiple location groups.
|
|
|
|
|
|
|
|
It is used to allow for having different kind of groups for different means.
|
|
|
|
"""
|
|
|
|
name: NonEmptyStr = APIField(
|
|
|
|
title="name/slug",
|
|
|
|
description="name/slug of this location group category",
|
|
|
|
)
|
|
|
|
single: bool = APIField(
|
|
|
|
title="single choice",
|
|
|
|
description="if true, every location can only have one group from this category, not a list"
|
|
|
|
)
|
|
|
|
titles_plural: dict[NonEmptyStr, NonEmptyStr] = APIField(
|
|
|
|
title="plural title (all languages)",
|
|
|
|
description="property names are the ISO-language code. languages may be missing.",
|
|
|
|
example={
|
|
|
|
"en": "Title",
|
|
|
|
"de": "Titel",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
title_plural: NonEmptyStr = APIField(
|
|
|
|
title="plural title (preferred language)",
|
|
|
|
description="preferred language based on the Accept-Language header."
|
|
|
|
)
|
|
|
|
help_texts: dict[NonEmptyStr, NonEmptyStr] = APIField(
|
|
|
|
title="help text (all languages)",
|
|
|
|
description="property names are the ISO-language code. languages may be missing.",
|
|
|
|
example={
|
|
|
|
"en": "Title",
|
|
|
|
"de": "Titel",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
help_text: str = APIField(
|
|
|
|
title="help text (preferred language)",
|
|
|
|
description="preferred language based on the Accept-Language header."
|
|
|
|
)
|
|
|
|
allow_levels: bool = APIField(
|
|
|
|
description="whether groups with this category can be assigned to levels"
|
|
|
|
)
|
|
|
|
allow_spaces: bool = APIField(
|
|
|
|
description="whether groups with this category can be assigned to spaces"
|
|
|
|
)
|
|
|
|
allow_areas: bool = APIField(
|
|
|
|
description="whether groups with this category can be assigned to areas"
|
|
|
|
)
|
|
|
|
allow_pois: bool = APIField(
|
|
|
|
description="whether groups with this category can be assigned to POIs"
|
|
|
|
)
|
|
|
|
allow_dynamic_locations: bool = APIField(
|
|
|
|
description="whether groups with this category can be assigned to dynamic locations"
|
|
|
|
)
|
|
|
|
priority: int = APIField() # todo: ???
|
|
|
|
|
|
|
|
|
2023-11-23 23:22:30 +01:00
|
|
|
class DynamicLocationSchema(SpecificLocationSchema, DjangoModelSchema):
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
Represents a moving object. Its position has to be separately queried through the position API.
|
2023-11-23 23:22:30 +01:00
|
|
|
|
|
|
|
A dynamic location is a specific location, and can therefore be routed to and from,
|
|
|
|
as well as belong to location groups.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2024-11-21 11:56:31 +01:00
|
|
|
class DataOverlaySchema(TitledSchema, DjangoModelSchema):
|
2024-12-03 09:59:51 +01:00
|
|
|
"""
|
|
|
|
Represents a collection of geometries to be displayed as an optional overlay to the map.
|
|
|
|
"""
|
2024-12-16 11:36:43 +01:00
|
|
|
description: Optional[str]
|
|
|
|
stroke_color: Optional[str]
|
|
|
|
stroke_width: Optional[float]
|
2024-12-16 15:54:45 +01:00
|
|
|
stroke_opacity: Optional[float]
|
2024-12-16 11:36:43 +01:00
|
|
|
fill_color: Optional[str]
|
2024-12-16 15:54:45 +01:00
|
|
|
fill_opacity: Optional[float]
|
2024-12-26 04:08:03 +01:00
|
|
|
cluster_points: bool
|
2024-11-21 11:56:31 +01:00
|
|
|
|
|
|
|
|
2024-12-16 11:36:28 +01:00
|
|
|
|
|
|
|
class DataOverlayFeatureSchema(TitledSchema, WithGeometrySchema, DjangoModelSchema):
|
2024-12-03 09:59:51 +01:00
|
|
|
"""
|
|
|
|
A feature (any kind of geometry) to be displayed as part of a data overlay.
|
|
|
|
"""
|
2024-11-21 11:56:31 +01:00
|
|
|
geometry: AnyGeometrySchema
|
|
|
|
level_id: PositiveInt
|
|
|
|
stroke_color: Optional[str]
|
|
|
|
stroke_width: Optional[float]
|
2024-12-16 15:54:45 +01:00
|
|
|
stroke_opacity: Optional[float]
|
2024-11-21 11:56:31 +01:00
|
|
|
fill_color: Optional[str]
|
2024-12-16 15:54:45 +01:00
|
|
|
fill_opacity: Optional[float]
|
2024-11-21 11:56:31 +01:00
|
|
|
show_label: bool
|
|
|
|
show_geometry: bool
|
|
|
|
interactive: bool
|
|
|
|
point_icon: Optional[str]
|
|
|
|
external_url: Optional[str]
|
2024-12-20 21:34:20 +01:00
|
|
|
extra_data: Optional[dict[str, str | int | float]]
|
2024-11-21 11:56:31 +01:00
|
|
|
|
|
|
|
|
2024-12-16 20:42:28 +01:00
|
|
|
class WayTypeSchema(TitledSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
Waytypes for navigation like stairs, escalators etc
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2023-11-19 16:48:40 +01:00
|
|
|
class SourceSchema(WithAccessRestrictionSchema, DjangoModelSchema):
|
2023-11-19 16:36:46 +01:00
|
|
|
"""
|
|
|
|
A source image that can be traced in the editor.
|
|
|
|
"""
|
|
|
|
name: NonEmptyStr = APIField(
|
|
|
|
title="name",
|
|
|
|
description="name/filename of the source",
|
|
|
|
)
|
2023-12-03 17:39:58 +01:00
|
|
|
bounds: tuple[
|
2023-12-03 17:42:12 +01:00
|
|
|
tuple[
|
|
|
|
Annotated[float, APIField(name="left")],
|
|
|
|
Annotated[float, APIField(name="bottom")],
|
|
|
|
],
|
|
|
|
tuple[
|
|
|
|
Annotated[float, APIField(name="right")],
|
|
|
|
Annotated[float, APIField(name="top")],
|
|
|
|
]
|
2023-12-03 17:39:58 +01:00
|
|
|
]
|
2023-11-19 16:48:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
class AccessRestrictionSchema(TitledSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
A category that some objects can belong to.
|
|
|
|
|
|
|
|
If they do, you can only see them if you have a permission to see objects with this access retriction.
|
|
|
|
"""
|
2024-12-04 11:45:03 +01:00
|
|
|
public: bool
|
2023-11-19 16:48:40 +01:00
|
|
|
groups: list[PositiveInt] = APIField(
|
|
|
|
title="access restriction groups"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class AccessRestrictionGroupSchema(WithAccessRestrictionSchema, DjangoModelSchema):
|
|
|
|
"""
|
|
|
|
For simplicity's sake, access restrictions can belong to groups, and you can grant permissions for the entire group.
|
|
|
|
"""
|
|
|
|
pass
|
2023-11-23 16:37:25 +01:00
|
|
|
|
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class CustomLocationSchema(BaseSchema):
|
2023-11-24 01:05:38 +01:00
|
|
|
"""
|
|
|
|
A custom location represents coordinates that have been put in or calculated.
|
|
|
|
|
|
|
|
A custom location is a location, so it can be routed to and from.
|
|
|
|
"""
|
|
|
|
id: CustomLocationID = APIField(
|
|
|
|
description="ID representing the coordinates"
|
|
|
|
)
|
|
|
|
slug: CustomLocationID = APIField(
|
|
|
|
description="slug, identical to ID"
|
|
|
|
)
|
2024-12-04 13:14:44 +01:00
|
|
|
effective_slug: CustomLocationID = APIField(
|
|
|
|
description="slug, identical to ID"
|
|
|
|
)
|
2023-12-04 23:07:30 +01:00
|
|
|
icon: Optional[NonEmptyStr] = APIField( # todo: not optional?
|
2024-12-03 19:00:35 +01:00
|
|
|
title="set icon name",
|
|
|
|
description="as set in the object specifically (any material design icon name)",
|
|
|
|
example="pin_drop",
|
|
|
|
)
|
|
|
|
effective_icon: Optional[NonEmptyStr] = APIField( # todo: not optional?
|
|
|
|
title="icon name to use",
|
|
|
|
description="effective icon to use (any material design icon name)",
|
2023-12-04 18:58:49 +01:00
|
|
|
example="pin_drop",
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
|
|
|
title: NonEmptyStr = APIField(
|
|
|
|
title="title (preferred language)",
|
|
|
|
description="preferred language based on the Accept-Language header."
|
|
|
|
)
|
|
|
|
subtitle: NonEmptyStr = APIField(
|
|
|
|
title="subtitle (preferred language)",
|
|
|
|
description="an automatically generated short description for this location. "
|
|
|
|
"preferred language based on the Accept-Language header."
|
|
|
|
)
|
|
|
|
level: PositiveInt = APIField(
|
2023-12-04 18:58:49 +01:00
|
|
|
description="level ID this custom location is located on",
|
|
|
|
example=1,
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
space: Union[
|
|
|
|
Annotated[PositiveInt, APIField(title="space ID", example=1)],
|
|
|
|
Annotated[None, APIField(title="null", description="the location is not inside a space")],
|
|
|
|
] = APIField(
|
|
|
|
default=None,
|
|
|
|
description="space ID this custom location is located in"
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
|
|
|
areas: list[PositiveInt] = APIField(
|
|
|
|
description="IDs of areas this custom location is located in"
|
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
grid_square: Union[
|
|
|
|
Annotated[NonEmptyStr, APIField(title="grid square", description="grid square(s) that this location is in")],
|
2024-12-03 18:42:33 +01:00
|
|
|
Annotated[Literal[""], APIField(title="grid square", description="outside of grid")],
|
2023-12-04 18:58:49 +01:00
|
|
|
Annotated[None, APIField(title="null", description="no grid defined or outside of grid")],
|
|
|
|
] = APIField(
|
2023-11-24 01:05:38 +01:00
|
|
|
default=None,
|
|
|
|
title="grid square",
|
2023-12-04 18:58:49 +01:00
|
|
|
description="grid cell(s) that this location is in, if a grid is defined and the location is within it",
|
|
|
|
example="C3",
|
|
|
|
)
|
|
|
|
near_area: Union[
|
|
|
|
Annotated[PositiveInt, APIField(title="area ID", example=1)],
|
|
|
|
Annotated[None, APIField(title="null", description="the location is not near any areas")],
|
|
|
|
] = APIField(
|
|
|
|
near="nearby area",
|
|
|
|
description="the ID of an area near this custom location"
|
|
|
|
)
|
|
|
|
near_poi: Union[
|
|
|
|
Annotated[PositiveInt, APIField(title="POI ID", example=1)],
|
|
|
|
Annotated[None, APIField(title="null", description="the location is not near any POIs")],
|
|
|
|
] = APIField(
|
|
|
|
title="nearby POI",
|
|
|
|
description="the ID of a POI near this custom location"
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
2023-11-24 17:21:39 +01:00
|
|
|
nearby: list[PositiveInt] = APIField(
|
2023-11-24 01:05:38 +01:00
|
|
|
description="list of IDs of nearby locations"
|
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
altitude: Union[
|
|
|
|
Annotated[float, APIField(title="ground altitude", example=1)],
|
|
|
|
Annotated[None, APIField(title="null", description="could not be determined (outside of space?)")],
|
|
|
|
] = APIField(
|
|
|
|
title="ground altitude",
|
|
|
|
description="ground altitude (in the map-wide coordinate system)"
|
|
|
|
)
|
2024-12-04 13:01:02 +01:00
|
|
|
geometry: PointSchema = APIField(
|
2023-11-24 01:05:38 +01:00
|
|
|
description="point geometry for this custom location",
|
|
|
|
)
|
|
|
|
|
2024-12-04 13:01:02 +01:00
|
|
|
@classmethod
|
|
|
|
def get_overrides(cls, value):
|
2024-12-04 13:02:55 +01:00
|
|
|
from c3nav.mapdata.grid import grid
|
2024-12-04 13:01:02 +01:00
|
|
|
return {
|
|
|
|
"id": value.pk,
|
|
|
|
"space": value.space.pk if value.space else None,
|
|
|
|
"level": value.level.pk,
|
|
|
|
"geometry": value.serialized_geometry,
|
2024-12-04 13:02:55 +01:00
|
|
|
"grid_square": value.grid_square if grid.enabled else None
|
2024-12-04 13:01:02 +01:00
|
|
|
}
|
|
|
|
|
2023-11-24 01:05:38 +01:00
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class TrackablePositionSchema(BaseSchema):
|
2023-11-24 01:05:38 +01:00
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
A trackable position. Its position can be set or reset.
|
2023-11-24 01:05:38 +01:00
|
|
|
"""
|
|
|
|
id: PositionID = APIField(
|
2023-12-04 18:58:49 +01:00
|
|
|
description="ID representing the position",
|
|
|
|
example="p:adskjfalskdj",
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
|
|
|
slug: PositionID = APIField(
|
2023-12-04 18:58:49 +01:00
|
|
|
description="slug representing the position",
|
|
|
|
example="p:adskjfalskdj",
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
2023-12-04 23:07:30 +01:00
|
|
|
icon: Optional[NonEmptyStr] = APIField( # todo: not optional?
|
2024-12-03 19:00:35 +01:00
|
|
|
title="set icon name",
|
|
|
|
description="icon as set in the location specifically (any material design icon name)",
|
|
|
|
example="pin_drop",
|
|
|
|
)
|
|
|
|
effective_icon: Optional[NonEmptyStr] = APIField( # todo: not optional?
|
|
|
|
title="icon name to use",
|
|
|
|
description="effective icon to use (any material design icon name)",
|
2023-12-04 18:58:49 +01:00
|
|
|
example="pin_drop",
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
|
|
|
title: NonEmptyStr = APIField(
|
|
|
|
title="title of the position",
|
2023-12-04 18:58:49 +01:00
|
|
|
example="My position"
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
|
|
|
subtitle: NonEmptyStr = APIField(
|
|
|
|
title="subtitle (preferred language)",
|
|
|
|
description="an automatically generated short description, which might change when the position changes. "
|
2023-12-04 18:58:49 +01:00
|
|
|
"preferred language based on the Accept-Language header.",
|
|
|
|
example="Near Bällebad"
|
2023-11-24 01:05:38 +01:00
|
|
|
)
|
|
|
|
|
2024-12-04 12:42:03 +01:00
|
|
|
@classmethod
|
|
|
|
def get_overrides(cls, value) -> dict:
|
|
|
|
from c3nav.mapdata.models.locations import Position
|
|
|
|
value: Position
|
|
|
|
return {
|
|
|
|
"id": value.slug,
|
|
|
|
}
|
|
|
|
|
2023-11-24 01:05:38 +01:00
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class LocationTypeSchema(BaseSchema):
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: str = APIField(title="location type",
|
|
|
|
description="indicates what kind of location is included. "
|
|
|
|
"different location types have different fields.")
|
2023-12-03 22:52:06 +01:00
|
|
|
|
|
|
|
|
2023-12-04 18:58:49 +01:00
|
|
|
def LocationTypeAPIField():
|
|
|
|
return APIField(title="location type",
|
|
|
|
description="indicates what kind of location is included. "
|
|
|
|
"different location types have different fields.")
|
2023-12-03 22:52:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
class FullLevelLocationSchema(LevelSchema, LocationTypeSchema):
|
2023-11-23 16:37:25 +01:00
|
|
|
"""
|
|
|
|
A level for the location API.
|
|
|
|
See Level schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["level"] = LocationTypeAPIField()
|
2023-11-23 16:37:25 +01:00
|
|
|
|
|
|
|
|
2023-12-03 22:52:06 +01:00
|
|
|
class FullSpaceLocationSchema(SimpleGeometryPointAndBoundsSchema, SpaceSchema, LocationTypeSchema):
|
2023-11-23 16:37:25 +01:00
|
|
|
"""
|
|
|
|
A space with some additional information for the location API.
|
|
|
|
See Space schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["space"] = LocationTypeAPIField()
|
2023-11-23 16:37:25 +01:00
|
|
|
|
|
|
|
|
2023-12-03 22:52:06 +01:00
|
|
|
class FullAreaLocationSchema(SimpleGeometryPointAndBoundsSchema, AreaSchema, LocationTypeSchema):
|
2023-11-23 16:37:25 +01:00
|
|
|
"""
|
|
|
|
An area with some additional information for the location API.
|
|
|
|
See Area schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["area"] = LocationTypeAPIField()
|
2023-11-23 16:37:25 +01:00
|
|
|
|
|
|
|
|
2023-12-03 22:52:06 +01:00
|
|
|
class FullPOILocationSchema(SimpleGeometryPointSchema, POISchema, LocationTypeSchema):
|
2023-11-23 16:37:25 +01:00
|
|
|
"""
|
|
|
|
A point of interest with some additional information for the location API.
|
|
|
|
See POI schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["poi"] = LocationTypeAPIField()
|
2023-11-23 16:37:25 +01:00
|
|
|
|
|
|
|
|
2023-12-03 22:52:06 +01:00
|
|
|
class FullLocationGroupLocationSchema(SimpleGeometryLocationsSchema, LocationGroupSchema, LocationTypeSchema):
|
2023-11-23 16:37:25 +01:00
|
|
|
"""
|
2023-11-23 18:10:31 +01:00
|
|
|
A location group with some additional information for the location API.
|
2023-11-23 16:37:25 +01:00
|
|
|
See LocationGroup schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["locationgroup"] = LocationTypeAPIField()
|
2023-11-23 16:37:25 +01:00
|
|
|
|
|
|
|
|
2023-12-03 22:52:06 +01:00
|
|
|
class FullDynamicLocationLocationSchema(DynamicLocationSchema, LocationTypeSchema):
|
2023-11-23 23:22:30 +01:00
|
|
|
"""
|
|
|
|
A dynamic location for the location API.
|
|
|
|
See DynamicLocation schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["dynamiclocation"] = LocationTypeAPIField()
|
2023-11-23 23:22:30 +01:00
|
|
|
|
|
|
|
|
2023-12-03 22:52:06 +01:00
|
|
|
class CustomLocationLocationSchema(SimpleGeometryPointAndBoundsSchema, CustomLocationSchema, LocationTypeSchema):
|
2023-11-24 01:05:38 +01:00
|
|
|
"""
|
|
|
|
A custom location for the location API.
|
|
|
|
See CustomLocation schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["customlocation"] = LocationTypeAPIField()
|
2023-11-24 01:05:38 +01:00
|
|
|
|
|
|
|
|
2023-12-03 22:52:06 +01:00
|
|
|
class TrackablePositionLocationSchema(TrackablePositionSchema, LocationTypeSchema):
|
2023-11-24 01:05:38 +01:00
|
|
|
"""
|
|
|
|
A trackable position for the location API.
|
|
|
|
See TrackablePosition schema for details.
|
|
|
|
"""
|
2023-12-04 18:58:49 +01:00
|
|
|
locationtype: Literal["position"] = LocationTypeAPIField()
|
2023-11-24 01:05:38 +01:00
|
|
|
|
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class SlimLocationMixin(BaseSchema):
|
2023-11-23 18:10:31 +01:00
|
|
|
level: ClassVar[None]
|
|
|
|
space: ClassVar[None]
|
|
|
|
titles: ClassVar[None]
|
|
|
|
access_restriction: ClassVar[None]
|
|
|
|
can_search: ClassVar[None]
|
|
|
|
can_describe: ClassVar[None]
|
|
|
|
groups: ClassVar[None]
|
2024-12-03 15:22:16 +01:00
|
|
|
groups_by_category: ClassVar[None]
|
2024-12-03 18:42:33 +01:00
|
|
|
geometry: ClassVar[None]
|
2023-11-23 18:10:31 +01:00
|
|
|
|
|
|
|
|
|
|
|
class SlimLevelLocationSchema(SlimLocationMixin, FullLevelLocationSchema):
|
|
|
|
"""
|
|
|
|
A level for the location API with some rarely needed fields removed.
|
|
|
|
See Level schema for details.
|
|
|
|
"""
|
|
|
|
short_label: ClassVar[None]
|
|
|
|
on_top_of: ClassVar[None]
|
|
|
|
base_altitude: ClassVar[None]
|
|
|
|
default_height: ClassVar[None]
|
|
|
|
door_height: ClassVar[None]
|
|
|
|
|
|
|
|
|
|
|
|
class SlimSpaceLocationSchema(SlimLocationMixin, FullSpaceLocationSchema):
|
|
|
|
"""
|
|
|
|
A space with some rarely needed fields removed and some additional information for the location API.
|
|
|
|
See Space schema for details.
|
|
|
|
"""
|
|
|
|
outside: ClassVar[None]
|
|
|
|
height: ClassVar[None]
|
|
|
|
|
|
|
|
|
|
|
|
class SlimAreaLocationSchema(SlimLocationMixin, FullAreaLocationSchema):
|
|
|
|
"""
|
|
|
|
An area with some rarely needed fields removed and some additional information for the location API.
|
|
|
|
See Area schema for details.
|
|
|
|
"""
|
|
|
|
slow_down_factor: ClassVar[None]
|
|
|
|
|
|
|
|
|
|
|
|
class SlimPOILocationSchema(SlimLocationMixin, FullPOILocationSchema):
|
|
|
|
"""
|
|
|
|
A point of interest with some rarely needed fields removed and some additional information for the location API.
|
|
|
|
See POI schema for details.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class SlimLocationGroupLocationSchema(SlimLocationMixin, FullLocationGroupLocationSchema):
|
|
|
|
"""
|
2023-11-23 23:22:30 +01:00
|
|
|
A location group with some rarely needed fields removed and some additional information for the location API.
|
2023-11-23 18:10:31 +01:00
|
|
|
See LocationGroup schema for details.
|
|
|
|
"""
|
|
|
|
category: ClassVar[None]
|
|
|
|
priority: ClassVar[None]
|
|
|
|
hierarchy: ClassVar[None]
|
|
|
|
color: ClassVar[None]
|
|
|
|
can_report_missing: ClassVar[None]
|
|
|
|
|
|
|
|
|
2023-11-23 23:22:30 +01:00
|
|
|
class SlimDynamicLocationLocationSchema(SlimLocationMixin, FullDynamicLocationLocationSchema):
|
|
|
|
"""
|
|
|
|
A dynamic location with some rarely needed fields removed for the location API.
|
|
|
|
See DynamicLocation schema for details.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2023-11-23 18:10:31 +01:00
|
|
|
|
2024-12-03 18:42:33 +01:00
|
|
|
def get_locationtype(v: Any):
|
|
|
|
if isinstance(v, Model):
|
|
|
|
return v._meta.model_name
|
2024-12-04 12:42:03 +01:00
|
|
|
with suppress(AttributeError):
|
|
|
|
return v.locationtype
|
2024-12-03 18:42:33 +01:00
|
|
|
return v["locationtype"]
|
|
|
|
|
|
|
|
|
2023-11-24 01:05:38 +01:00
|
|
|
FullListableLocationSchema = Annotated[
|
2023-11-23 18:10:31 +01:00
|
|
|
Union[
|
2024-12-03 18:42:33 +01:00
|
|
|
Annotated[FullLevelLocationSchema, Tag("level")],
|
|
|
|
Annotated[FullSpaceLocationSchema, Tag("space")],
|
|
|
|
Annotated[FullAreaLocationSchema, Tag("area")],
|
|
|
|
Annotated[FullPOILocationSchema, Tag("poi")],
|
|
|
|
Annotated[FullLocationGroupLocationSchema, Tag("locationgroup")],
|
|
|
|
Annotated[FullDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
2023-11-23 18:10:31 +01:00
|
|
|
],
|
2024-12-03 18:42:33 +01:00
|
|
|
Discriminator(get_locationtype),
|
2023-11-23 18:10:31 +01:00
|
|
|
]
|
|
|
|
|
2023-11-24 01:05:38 +01:00
|
|
|
FullLocationSchema = Annotated[
|
|
|
|
Union[
|
2024-12-03 18:42:33 +01:00
|
|
|
Annotated[FullLevelLocationSchema, Tag("level")],
|
|
|
|
Annotated[FullSpaceLocationSchema, Tag("space")],
|
|
|
|
Annotated[FullAreaLocationSchema, Tag("area")],
|
|
|
|
Annotated[FullPOILocationSchema, Tag("poi")],
|
|
|
|
Annotated[FullLocationGroupLocationSchema, Tag("locationgroup")],
|
|
|
|
Annotated[FullDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
|
|
|
Annotated[CustomLocationLocationSchema, Tag("customlocation")],
|
|
|
|
Annotated[TrackablePositionLocationSchema, Tag("position")],
|
2023-11-24 01:05:38 +01:00
|
|
|
],
|
2024-12-03 18:42:33 +01:00
|
|
|
Discriminator(get_locationtype),
|
2023-11-24 01:05:38 +01:00
|
|
|
]
|
|
|
|
|
|
|
|
SlimListableLocationSchema = Annotated[
|
2023-11-23 16:37:25 +01:00
|
|
|
Union[
|
2024-12-03 18:42:33 +01:00
|
|
|
Annotated[SlimLevelLocationSchema, Tag("level")],
|
|
|
|
Annotated[SlimSpaceLocationSchema, Tag("space")],
|
|
|
|
Annotated[SlimAreaLocationSchema, Tag("area")],
|
|
|
|
Annotated[SlimPOILocationSchema, Tag("poi")],
|
|
|
|
Annotated[SlimLocationGroupLocationSchema, Tag("locationgroup")],
|
|
|
|
Annotated[SlimDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
2023-11-23 16:37:25 +01:00
|
|
|
],
|
2024-12-03 18:42:33 +01:00
|
|
|
Discriminator(get_locationtype),
|
2023-11-23 16:37:25 +01:00
|
|
|
]
|
|
|
|
|
2023-11-24 01:05:38 +01:00
|
|
|
SlimLocationSchema = Annotated[
|
|
|
|
Union[
|
2024-12-03 18:42:33 +01:00
|
|
|
Annotated[SlimLevelLocationSchema, Tag("level")],
|
|
|
|
Annotated[SlimSpaceLocationSchema, Tag("space")],
|
|
|
|
Annotated[SlimAreaLocationSchema, Tag("area")],
|
|
|
|
Annotated[SlimPOILocationSchema, Tag("poi")],
|
|
|
|
Annotated[SlimLocationGroupLocationSchema, Tag("locationgroup")],
|
|
|
|
Annotated[SlimDynamicLocationLocationSchema, Tag("dynamiclocation")],
|
|
|
|
Annotated[CustomLocationLocationSchema, Tag("customlocation")],
|
|
|
|
Annotated[TrackablePositionLocationSchema, Tag("position")],
|
2023-11-24 01:05:38 +01:00
|
|
|
],
|
2024-12-03 18:42:33 +01:00
|
|
|
Discriminator(get_locationtype),
|
2023-11-24 01:05:38 +01:00
|
|
|
]
|
|
|
|
|
2023-12-04 18:58:49 +01:00
|
|
|
listable_location_definitions = schema_definitions(
|
|
|
|
(LevelSchema, SpaceSchema, AreaSchema, POISchema, DynamicLocationSchema, LocationGroupSchema)
|
|
|
|
)
|
|
|
|
all_location_definitions = listable_location_definitions + "\n" + schema_definitions(
|
|
|
|
(CustomLocationSchema, TrackablePositionSchema)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class DisplayLink(BaseSchema):
|
2023-11-23 21:11:31 +01:00
|
|
|
"""
|
|
|
|
A link for the location display
|
|
|
|
"""
|
|
|
|
id: PositiveInt
|
|
|
|
slug: NonEmptyStr
|
|
|
|
title: NonEmptyStr
|
|
|
|
can_search: bool
|
|
|
|
|
|
|
|
|
2023-12-22 01:19:53 +01:00
|
|
|
class DisplayURL(BaseSchema):
|
|
|
|
"""
|
|
|
|
A URL link for the location display
|
|
|
|
"""
|
|
|
|
title: NonEmptyStr
|
|
|
|
url: NonEmptyStr
|
|
|
|
|
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class LocationDisplay(BaseSchema):
|
2023-11-24 01:05:38 +01:00
|
|
|
id: AnyLocationID = APIField(
|
2023-11-23 21:11:31 +01:00
|
|
|
description="a numeric ID for a map location or a string ID for generated locations",
|
2023-12-04 18:58:49 +01:00
|
|
|
example=1,
|
2023-11-23 21:11:31 +01:00
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
|
|
|
|
level: Union[
|
|
|
|
Annotated[PositiveInt, APIField(title="level ID", description="ID of relevant level")],
|
|
|
|
Annotated[None, APIField(title="null", description="no relevant level")],
|
|
|
|
] = APIField(
|
2023-11-23 21:11:31 +01:00
|
|
|
None,
|
2023-12-04 18:58:49 +01:00
|
|
|
title="level",
|
|
|
|
example=2,
|
2023-11-23 21:11:31 +01:00
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
space: Union[
|
|
|
|
Annotated[PositiveInt, APIField(title="level ID", description="ID of relevant level")],
|
|
|
|
Annotated[None, APIField(title="null", description="no relevant level")],
|
|
|
|
] = APIField(
|
2023-11-23 21:11:31 +01:00
|
|
|
None,
|
2023-12-04 18:58:49 +01:00
|
|
|
description="space",
|
|
|
|
example=3,
|
2023-11-23 21:11:31 +01:00
|
|
|
)
|
2024-12-19 11:59:11 +01:00
|
|
|
external_url: Optional[DisplayURL] = None
|
2023-11-23 21:11:31 +01:00
|
|
|
display: list[
|
|
|
|
tuple[
|
2023-11-24 01:08:40 +01:00
|
|
|
Annotated[NonEmptyStr, APIField(title="field title")],
|
2023-11-23 21:11:31 +01:00
|
|
|
Annotated[Union[
|
2023-11-24 01:08:40 +01:00
|
|
|
Annotated[str, APIField(title="a simple string value")],
|
|
|
|
Annotated[DisplayLink, APIField(title="a link value")],
|
|
|
|
Annotated[list[DisplayLink], APIField(title="a list of link values")],
|
2023-12-22 01:19:53 +01:00
|
|
|
Annotated[DisplayURL, APIField(title="an URL value")],
|
2023-11-24 14:19:23 +01:00
|
|
|
Annotated[None, APIField(title="no value")]
|
2023-11-24 01:08:40 +01:00
|
|
|
], APIField(title="field value", union_mode='left_to_right')]
|
2023-11-23 21:11:31 +01:00
|
|
|
]
|
2023-12-04 18:58:49 +01:00
|
|
|
] = APIField(
|
|
|
|
title="display fields",
|
|
|
|
description="a list of human-readable display values",
|
|
|
|
example=[
|
|
|
|
("Title", "Awesome location"),
|
|
|
|
("Access Restriction", None),
|
|
|
|
("Level", {
|
|
|
|
"id": 2,
|
|
|
|
"slug": "level0",
|
|
|
|
"title": "Ground Floor",
|
|
|
|
"can_search": True,
|
|
|
|
}),
|
|
|
|
("Groups", [
|
|
|
|
{
|
|
|
|
"id": 10,
|
|
|
|
"slug": "entrances",
|
|
|
|
"title": "Entrances",
|
|
|
|
"can_search": True,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"id": 11,
|
|
|
|
"slug": "startswithe",
|
|
|
|
"title": "Locations that Start with E",
|
|
|
|
"can_search": False,
|
|
|
|
}
|
2023-12-22 01:19:53 +01:00
|
|
|
]),
|
|
|
|
("External URL", {
|
|
|
|
"title": "Open",
|
|
|
|
"url": "https://example.com/",
|
|
|
|
})
|
2023-12-04 18:58:49 +01:00
|
|
|
]
|
|
|
|
)
|
|
|
|
geometry: Union[
|
|
|
|
GeometrySchema,
|
|
|
|
Annotated[None, APIField(title="null", description="no geometry available")]
|
|
|
|
] = APIField(
|
2023-11-23 21:11:31 +01:00
|
|
|
None, description="representative geometry, if available"
|
|
|
|
)
|
2023-12-04 18:58:49 +01:00
|
|
|
editor_url: Union[
|
|
|
|
Annotated[NonEmptyStr, APIField(title="path to editor")],
|
|
|
|
Annotated[None, APIField(title="null", description="no editor access or object is not editable")],
|
|
|
|
] = APIField(
|
|
|
|
None,
|
|
|
|
title="editor URL",
|
|
|
|
description="path to edit this object in the editor",
|
|
|
|
example="/editor/spaces/2/pois/1/"
|
2023-11-23 21:11:31 +01:00
|
|
|
)
|
2023-11-24 01:05:38 +01:00
|
|
|
|
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class PositionStatusSchema(BaseSchema):
|
2023-11-24 01:05:38 +01:00
|
|
|
id: AnyPositionID = APIField(
|
|
|
|
description="the ID of the dynamic position that has been queries",
|
|
|
|
)
|
|
|
|
slug: NonEmptyStr = APIField(
|
|
|
|
description="a description for the dynamic position that has been queried"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-12-11 20:49:50 +01:00
|
|
|
class PositionAvailabilitySchema(BaseSchema):
|
2023-12-03 22:52:06 +01:00
|
|
|
available: str
|
|
|
|
|
|
|
|
|
2023-12-25 12:04:39 +01:00
|
|
|
class PositionUnavailableStatusSchema(PositionStatusSchema, TrackablePositionSchema, PositionAvailabilitySchema):
|
2023-12-03 22:52:06 +01:00
|
|
|
""" position unavailable """
|
2023-11-24 01:05:38 +01:00
|
|
|
available: Literal[False]
|
|
|
|
|
|
|
|
|
2023-11-24 18:05:59 +01:00
|
|
|
class PositionAvailableStatusSchema(PositionStatusSchema, SimpleGeometryPointAndBoundsSchema, TrackablePositionSchema,
|
2023-12-03 22:52:06 +01:00
|
|
|
CustomLocationSchema, PositionAvailabilitySchema):
|
|
|
|
""" position available """
|
2023-11-24 01:05:38 +01:00
|
|
|
available: Literal[True]
|
|
|
|
|
|
|
|
|
|
|
|
AnyPositionStatusSchema = Annotated[
|
|
|
|
Union[
|
|
|
|
Annotated[PositionUnavailableStatusSchema, APIField(title="position is unavailable")],
|
|
|
|
Annotated[PositionAvailableStatusSchema, APIField(title="position is available")],
|
|
|
|
],
|
|
|
|
Discriminator("available"),
|
|
|
|
]
|
2024-08-13 21:17:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
class ProjectionPipelineSchema(BaseSchema):
|
|
|
|
pipeline: Union[
|
|
|
|
Annotated[NonEmptyStr, APIField(title='proj4 string')],
|
|
|
|
Annotated[None, APIField(title='null', description='projection not available')]
|
|
|
|
] = APIField(
|
|
|
|
title='proj4 string',
|
|
|
|
description='proj4 string for converting WGS84 coordinates to c3nav coordinates if available',
|
|
|
|
example='+proj=utm +zone=33 +ellps=GRS80 +units=m +no_defs'
|
|
|
|
)
|
|
|
|
|
2024-09-06 15:07:54 +02:00
|
|
|
|
2024-08-13 21:17:36 +02:00
|
|
|
class ProjectionSchema(ProjectionPipelineSchema):
|
|
|
|
proj4: NonEmptyStr = APIField(
|
|
|
|
title='proj4 string',
|
|
|
|
description='proj4 string for converting WGS84 coordinates to c3nav coordinates without offset and rotation',
|
|
|
|
example='+proj=utm +zone=33 +ellps=GRS80 +units=m +no_defs'
|
|
|
|
)
|
|
|
|
zero_point: tuple[float, float] = APIField(
|
|
|
|
title='zero point',
|
|
|
|
description='coordinates of the zero point of the c3nav coordinate system',
|
|
|
|
example=(0.0, 0.0),
|
|
|
|
)
|
|
|
|
rotation: float = APIField(
|
|
|
|
title='rotation',
|
|
|
|
description='rotational offset of the c3nav coordinate system',
|
|
|
|
example=0.0,
|
|
|
|
)
|
|
|
|
rotation_matrix: Optional[tuple[
|
|
|
|
float, float, float, float,
|
|
|
|
float, float, float, float,
|
|
|
|
float, float, float, float,
|
|
|
|
float, float, float, float,
|
|
|
|
]] = APIField(
|
|
|
|
title='rotation matrix',
|
|
|
|
description='rotation matrix for rotational offset of the c3nav coordinate system',
|
|
|
|
example=[
|
|
|
|
1, 0, 0, 0,
|
|
|
|
0, 1, 0, 0,
|
|
|
|
0, 0, 1, 0,
|
|
|
|
0, 0, 0, 1
|
|
|
|
]
|
|
|
|
)
|
2024-09-06 15:07:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
class LegendItemSchema(BaseSchema):
|
|
|
|
title: NonEmptyStr = APIField()
|
|
|
|
fill: str | None
|
|
|
|
border: str | None
|
|
|
|
|
|
|
|
|
|
|
|
class LegendSchema(BaseSchema):
|
|
|
|
base: list[LegendItemSchema]
|
|
|
|
groups: list[LegendItemSchema]
|
2024-12-16 20:42:28 +01:00
|
|
|
obstacles: list[LegendItemSchema]
|