map settings api endpoint (fixes #201)

This commit is contained in:
Laura Klünder 2024-12-03 15:05:53 +01:00
parent 35a8738424
commit d254f199e3
4 changed files with 66 additions and 9 deletions

View file

@ -1,6 +1,7 @@
import json
from typing import Annotated, Union
from celery import chain
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Prefetch
from django.shortcuts import redirect
@ -16,6 +17,7 @@ from c3nav.api.exceptions import API404, APIPermissionDenied, APIRequestValidati
from c3nav.api.schema import BaseSchema
from c3nav.api.utils import NonEmptyStr
from c3nav.mapdata.api.base import api_etag, api_stats, can_access_geometry
from c3nav.mapdata.grid import grid
from c3nav.mapdata.models import Source, Theme, Area, Space
from c3nav.mapdata.models.geometry.space import ObstacleGroup, Obstacle
from c3nav.mapdata.models.locations import DynamicLocation, LocationRedirect, Position, LocationGroup
@ -26,7 +28,7 @@ from c3nav.mapdata.schemas.models import (AnyPositionStatusSchema, FullListableL
LocationDisplay, ProjectionPipelineSchema, ProjectionSchema,
SlimListableLocationSchema, SlimLocationSchema, all_location_definitions,
listable_location_definitions, LegendSchema, LegendItemSchema)
from c3nav.mapdata.schemas.responses import LocationGeometry, WithBoundsSchema
from c3nav.mapdata.schemas.responses import LocationGeometry, WithBoundsSchema, MapSettingsSchema
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request,
searchable_locations_for_request, visible_locations_for_request)
from c3nav.mapdata.utils.user import can_access_editor
@ -34,6 +36,25 @@ from c3nav.mapdata.utils.user import can_access_editor
map_api_router = APIRouter(tags=["map"])
@map_api_router.get('/settings/', summary="get map settings",
description="get useful/required settings for displaying the map",
response={200: MapSettingsSchema, **auth_responses})
@api_etag(permissions=False)
def map_settings(request):
initial_bounds = settings.INITIAL_BOUNDS
if not initial_bounds:
initial_bounds = tuple(chain(*Source.max_bounds()))
else:
initial_bounds = (tuple(settings.INITIAL_BOUNDS)[:2], tuple(settings.INITIAL_BOUNDS)[2:])
return MapSettingsSchema(
initial_bounds=initial_bounds,
initial_level=settings.INITIAL_LEVEL or None,
grid=grid.serialize().model_dump() if grid else None,
tile_server=settings.TILE_CACHE_SERVER,
)
@map_api_router.get('/bounds/', summary="get boundaries",
description="get maximum boundaries of everything on the map",
response={200: WithBoundsSchema, **auth_responses})

View file

@ -1,9 +1,11 @@
import bisect
import string
from abc import ABC, abstractmethod
from typing import Optional
from dataclasses import dataclass, field
from typing import Optional, ClassVar, Sequence
from django.conf import settings
from ninja import Schema
class AbstractGrid(ABC):
@ -18,6 +20,13 @@ class AbstractGrid(ABC):
pass
class GridSchema(Schema):
rows: Sequence[float]
cols: Sequence[float]
invert_x: bool
invert_y: bool
class Grid(AbstractGrid):
enabled = True
@ -41,6 +50,14 @@ class Grid(AbstractGrid):
else:
raise ValueError('column coordinates are not ordered')
def serialize(self) -> GridSchema:
return GridSchema(
rows=self.rows,
cols=self.cols,
invert_x=self.invert_y,
invert_y=self.invert_y,
)
def get_square_for_point(self, x, y):
x = bisect.bisect(self.cols, x)
if x <= 0 or x >= len(self.cols):

View file

@ -1,12 +1,36 @@
from typing import Annotated, Union
from typing import Annotated, Union, Optional
from pydantic import Field as APIField
from pydantic import PositiveInt
from c3nav.api.schema import BaseSchema, GeometrySchema
from c3nav.mapdata.grid import GridSchema
from c3nav.mapdata.schemas.model_base import AnyLocationID, BoundsSchema
class MapSettingsSchema(BaseSchema):
"""
various c3nav instance settings
"""
initial_bounds: Optional[BoundsSchema] = APIField(
title="initial boundaries",
description="(left, bottom) to (top, right)",
)
initial_level: Optional[PositiveInt] = APIField(
title="initial level id",
description="the level id that is intially shown when opening the map",
)
grid: Optional[GridSchema] = APIField(
title="grid config",
description="grid configuration, if available",
)
tile_server: Optional[str] = APIField(
title="tile server base URL",
description="tile server base URL to use, if configured",
)
class WithBoundsSchema(BaseSchema):
"""
Describing a bounding box

View file

@ -217,12 +217,7 @@ def map_index(request, mode=None, slug=None, slug2=None, details=None, options=N
}
if grid.enabled:
ctx['grid'] = json.dumps({
'rows': grid.rows,
'cols': grid.cols,
'invert_x': grid.invert_x,
'invert_y': grid.invert_y,
}, separators=(',', ':'), cls=DjangoJSONEncoder)
ctx['grid'] = json.dumps(grid.serialize().model_dump(), separators=(',', ':'), cls=DjangoJSONEncoder)
csrf.get_token(request)