start of proj4 support (part of GPS support)
This commit is contained in:
parent
edba90481e
commit
aa49840806
4 changed files with 129 additions and 2 deletions
|
@ -9,6 +9,7 @@ from ninja import Router as APIRouter
|
||||||
from pydantic import Field as APIField
|
from pydantic import Field as APIField
|
||||||
from pydantic import PositiveInt
|
from pydantic import PositiveInt
|
||||||
|
|
||||||
|
from c3nav import settings
|
||||||
from c3nav.api.auth import auth_permission_responses, auth_responses, validate_responses
|
from c3nav.api.auth import auth_permission_responses, auth_responses, validate_responses
|
||||||
from c3nav.api.exceptions import API404, APIPermissionDenied, APIRequestValidationFailed
|
from c3nav.api.exceptions import API404, APIPermissionDenied, APIRequestValidationFailed
|
||||||
from c3nav.api.schema import BaseSchema
|
from c3nav.api.schema import BaseSchema
|
||||||
|
@ -19,8 +20,9 @@ from c3nav.mapdata.models.locations import DynamicLocation, LocationRedirect, Po
|
||||||
from c3nav.mapdata.schemas.filters import BySearchableFilter, RemoveGeometryFilter
|
from c3nav.mapdata.schemas.filters import BySearchableFilter, RemoveGeometryFilter
|
||||||
from c3nav.mapdata.schemas.model_base import AnyLocationID, AnyPositionID, CustomLocationID
|
from c3nav.mapdata.schemas.model_base import AnyLocationID, AnyPositionID, CustomLocationID
|
||||||
from c3nav.mapdata.schemas.models import (AnyPositionStatusSchema, FullListableLocationSchema, FullLocationSchema,
|
from c3nav.mapdata.schemas.models import (AnyPositionStatusSchema, FullListableLocationSchema, FullLocationSchema,
|
||||||
LocationDisplay, SlimListableLocationSchema, SlimLocationSchema,
|
LocationDisplay, ProjectionPipelineSchema, ProjectionSchema,
|
||||||
all_location_definitions, listable_location_definitions)
|
SlimListableLocationSchema, SlimLocationSchema, all_location_definitions,
|
||||||
|
listable_location_definitions)
|
||||||
from c3nav.mapdata.schemas.responses import LocationGeometry, WithBoundsSchema
|
from c3nav.mapdata.schemas.responses import LocationGeometry, WithBoundsSchema
|
||||||
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request,
|
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)
|
searchable_locations_for_request, visible_locations_for_request)
|
||||||
|
@ -310,3 +312,20 @@ def set_position(request, position_id: AnyPositionID, update: UpdatePositionSche
|
||||||
location.save()
|
location.save()
|
||||||
|
|
||||||
return location.serialize_position()
|
return location.serialize_position()
|
||||||
|
|
||||||
|
|
||||||
|
@map_api_router.get('/projection/', summary='get proj4 string',
|
||||||
|
description="get proj4 string for converting WGS84 coordinates to c3nva coordinates",
|
||||||
|
response={200: Union[ProjectionSchema, ProjectionPipelineSchema], **auth_responses})
|
||||||
|
def get_projection(request):
|
||||||
|
obj = {
|
||||||
|
"pipeline": settings.PROJECTION_TRANSFORMER_STRING
|
||||||
|
}
|
||||||
|
if True:
|
||||||
|
obj.update({
|
||||||
|
'proj4': settings.PROJECTION_PROJ4,
|
||||||
|
'zero_point': settings.PROJECTION_ZERO_POINT,
|
||||||
|
'rotation': settings.PROJECTION_ROTATION,
|
||||||
|
'rotation_matrix': settings.PROJECTION_ROTATION_MATRIX,
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
|
|
@ -793,3 +793,46 @@ AnyPositionStatusSchema = Annotated[
|
||||||
],
|
],
|
||||||
Discriminator("available"),
|
Discriminator("available"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# c3nav settings, mostly taken from the pretix project
|
# c3nav settings, mostly taken from the pretix project
|
||||||
|
import math
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
|
@ -12,6 +13,7 @@ from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.utils.dateparse import parse_duration
|
from django.utils.dateparse import parse_duration
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from pyproj import Proj, Transformer
|
||||||
|
|
||||||
from c3nav import __version__ as c3nav_version
|
from c3nav import __version__ as c3nav_version
|
||||||
from c3nav.utils.config import C3navConfigParser
|
from c3nav.utils.config import C3navConfigParser
|
||||||
|
@ -606,6 +608,68 @@ BASE_THEME = {
|
||||||
|
|
||||||
WIFI_SSIDS = [n for n in config.get('c3nav', 'wifi_ssids', fallback='').split(',') if n]
|
WIFI_SSIDS = [n for n in config.get('c3nav', 'wifi_ssids', fallback='').split(',') if n]
|
||||||
|
|
||||||
|
|
||||||
|
# Projection
|
||||||
|
PROJECTION_PROJ4 = config.get('projection', 'proj4', fallback=None)
|
||||||
|
PROJECTION_ZERO_POINT = config.get('projection', 'zero_point', fallback=None)
|
||||||
|
PROJECTION_ZERO_POINT_IS_WGS84 = '°' in PROJECTION_ZERO_POINT if PROJECTION_ZERO_POINT else False
|
||||||
|
PROJECTION_ROTATION = config.getfloat('projection', 'rotation', fallback=0.0)
|
||||||
|
PROJECTION_ROTATION_MATRIX = config.get('projection', 'rotation_matrix', fallback=None)
|
||||||
|
PROJECTION_TRANSFORMER: Optional[Transformer] = None
|
||||||
|
PROJECTION_TRANSFORMER_STRING: Optional[str] = None
|
||||||
|
|
||||||
|
if PROJECTION_PROJ4:
|
||||||
|
if '+units=m' not in PROJECTION_PROJ4:
|
||||||
|
PROJECTION_PROJ4 += ' +units=m'
|
||||||
|
PROJECTION_TRANSFORMER_STRING = re.sub(r'\s?\+no_defs', '', PROJECTION_PROJ4)
|
||||||
|
|
||||||
|
if (PROJECTION_ZERO_POINT or PROJECTION_ROTATION) and 'pipeline' not in PROJECTION_TRANSFORMER_STRING:
|
||||||
|
PROJECTION_TRANSFORMER_STRING = f'+proj=pipeline +step {PROJECTION_TRANSFORMER_STRING}'
|
||||||
|
|
||||||
|
if PROJECTION_ZERO_POINT:
|
||||||
|
PROJECTION_ZERO_POINT = tuple((float(i) for i in PROJECTION_ZERO_POINT.split(',')))
|
||||||
|
if len(PROJECTION_ZERO_POINT) != 2:
|
||||||
|
raise ImproperlyConfigured(f'invalid projection zero point "{PROJECTION_ZERO_POINT!r}"')
|
||||||
|
if PROJECTION_ZERO_POINT_IS_WGS84:
|
||||||
|
PROJECTION_ZERO_POINT = Proj.from_pipeline(PROJECTION_PROJ4).transform(PROJECTION_ZERO_POINT[0],
|
||||||
|
PROJECTION_ZERO_POINT[1])
|
||||||
|
PROJECTION_TRANSFORMER_STRING += (f' +step +proj=affine +xoff=-{PROJECTION_ZERO_POINT[0]} '
|
||||||
|
f'+yoff=-{PROJECTION_ZERO_POINT[1]}')
|
||||||
|
|
||||||
|
if PROJECTION_ROTATION != 0:
|
||||||
|
PROJECTION_ROTATION_MATRIX = (
|
||||||
|
math.cos(math.radians(PROJECTION_ROTATION)), math.sin(math.radians(PROJECTION_ROTATION)), 0, 0,
|
||||||
|
-math.sin(math.radians(PROJECTION_ROTATION)), math.cos(math.radians(PROJECTION_ROTATION)), 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
)
|
||||||
|
elif PROJECTION_ROTATION_MATRIX:
|
||||||
|
PROJECTION_ROTATION_MATRIX = tuple((float(i) for i in PROJECTION_ROTATION_MATRIX.split(',')))
|
||||||
|
if len(PROJECTION_ROTATION_MATRIX) != 16:
|
||||||
|
raise ImproperlyConfigured(f'invalid rotation matrix "{PROJECTION_ROTATION_MATRIX!r}"')
|
||||||
|
|
||||||
|
if PROJECTION_ROTATION_MATRIX:
|
||||||
|
PROJECTION_TRANSFORMER_STRING += (
|
||||||
|
f' +step +proj=affine '
|
||||||
|
f'+s11={PROJECTION_ROTATION_MATRIX[0]} +s12={PROJECTION_ROTATION_MATRIX[1]}'
|
||||||
|
)
|
||||||
|
if PROJECTION_ROTATION_MATRIX[2] != 0:
|
||||||
|
PROJECTION_TRANSFORMER_STRING += f' +s13={PROJECTION_ROTATION_MATRIX[2]}'
|
||||||
|
PROJECTION_TRANSFORMER_STRING += f' +s21={PROJECTION_ROTATION_MATRIX[4]} +s22={PROJECTION_ROTATION_MATRIX[5]}'
|
||||||
|
if PROJECTION_ROTATION_MATRIX[6] != 0:
|
||||||
|
PROJECTION_TRANSFORMER_STRING += ' +s23={PROJECTION_ROTATION_MATRIX[6]}'
|
||||||
|
if PROJECTION_ROTATION_MATRIX[8] != 0:
|
||||||
|
PROJECTION_TRANSFORMER_STRING += f' +s31={PROJECTION_ROTATION_MATRIX[8]}'
|
||||||
|
if PROJECTION_ROTATION_MATRIX[9] != 0:
|
||||||
|
PROJECTION_TRANSFORMER_STRING += f' +s32={PROJECTION_ROTATION_MATRIX[9]}'
|
||||||
|
if PROJECTION_ROTATION_MATRIX[10] != 1:
|
||||||
|
PROJECTION_TRANSFORMER_STRING += f' +s33={PROJECTION_ROTATION_MATRIX[10]}'
|
||||||
|
if PROJECTION_ROTATION_MATRIX[15] != 1:
|
||||||
|
PROJECTION_TRANSFORMER_STRING += f' +tscale={PROJECTION_ROTATION_MATRIX[15]}'
|
||||||
|
|
||||||
|
PROJECTION_TRANSFORMER_STRING += ' +no_defs'
|
||||||
|
PROJECTION_TRANSFORMER = Proj.from_pipeline(PROJECTION_TRANSFORMER_STRING)
|
||||||
|
|
||||||
USER_REGISTRATION = config.getboolean('c3nav', 'user_registration', fallback=True)
|
USER_REGISTRATION = config.getboolean('c3nav', 'user_registration', fallback=True)
|
||||||
|
|
||||||
INTERNAL_IPS = ('127.0.0.1', '::1')
|
INTERNAL_IPS = ('127.0.0.1', '::1')
|
||||||
|
|
|
@ -20,3 +20,4 @@ django_libsass==0.9
|
||||||
channels==4.0.0
|
channels==4.0.0
|
||||||
daphne==4.1.0
|
daphne==4.1.0
|
||||||
pyzstd==0.15.9
|
pyzstd==0.15.9
|
||||||
|
pyproj==3.6.1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue