make linter happy
This commit is contained in:
parent
d7f175f7ef
commit
8c8ef12cf0
19 changed files with 107 additions and 75 deletions
|
@ -9,14 +9,14 @@ from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import FieldDoesNotExist
|
from django.core.exceptions import FieldDoesNotExist
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.db.models import Q, Prefetch
|
from django.db.models import Prefetch, Q
|
||||||
from django.forms import (BooleanField, CharField, ChoiceField, DecimalField, Form, JSONField, ModelChoiceField,
|
from django.forms import (BooleanField, CharField, ChoiceField, DecimalField, Form, JSONField, ModelChoiceField,
|
||||||
ModelForm, MultipleChoiceField, Select, ValidationError)
|
ModelForm, MultipleChoiceField, Select, ValidationError)
|
||||||
from django.forms.widgets import HiddenInput, TextInput
|
from django.forms.widgets import HiddenInput, TextInput
|
||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from shapely.geometry.geo import mapping
|
|
||||||
from pydantic import ValidationError as PydanticValidationError
|
from pydantic import ValidationError as PydanticValidationError
|
||||||
|
from shapely.geometry.geo import mapping
|
||||||
|
|
||||||
from c3nav.editor.models import ChangeSet, ChangeSetUpdate
|
from c3nav.editor.models import ChangeSet, ChangeSetUpdate
|
||||||
from c3nav.mapdata.fields import GeometryField
|
from c3nav.mapdata.fields import GeometryField
|
||||||
|
@ -40,12 +40,12 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
|
||||||
obstaclegroup_theme_colors = {}
|
obstaclegroup_theme_colors = {}
|
||||||
else:
|
else:
|
||||||
locationgroup_theme_colors = {
|
locationgroup_theme_colors = {
|
||||||
l.location_group_id: l
|
theme_location_group.location_group_id: theme_location_group
|
||||||
for l in self.instance.location_groups.filter(theme_id=self.instance.pk)
|
for theme_location_group in self.instance.location_groups.filter(theme_id=self.instance.pk)
|
||||||
}
|
}
|
||||||
obstaclegroup_theme_colors = {
|
obstaclegroup_theme_colors = {
|
||||||
o.obstacle_group_id: o
|
theme_obstacle.obstacle_group_id: theme_obstacle
|
||||||
for o in self.instance.obstacle_groups.filter(theme_id=self.instance.pk)
|
for theme_obstacle in self.instance.obstacle_groups.filter(theme_id=self.instance.pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: can we get the model class via relationships?
|
# TODO: can we get the model class via relationships?
|
||||||
|
@ -54,15 +54,16 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
|
||||||
related = locationgroup_theme_colors.get(locationgroup.pk, None)
|
related = locationgroup_theme_colors.get(locationgroup.pk, None)
|
||||||
value = related.fill_color if related is not None else None
|
value = related.fill_color if related is not None else None
|
||||||
other_themes_colors = {
|
other_themes_colors = {
|
||||||
l.title: l.fill_color
|
theme_location_group.title: theme_location_group.fill_color
|
||||||
for l in locationgroup.theme_colors.all()
|
for theme_location_group in locationgroup.theme_colors.all()
|
||||||
if related is None or l.pk != related.pk
|
if related is None or theme_location_group.pk != related.pk
|
||||||
}
|
}
|
||||||
if len(other_themes_colors) > 0:
|
if len(other_themes_colors) > 0:
|
||||||
other_themes_colors = json.dumps(other_themes_colors)
|
other_themes_colors = json.dumps(other_themes_colors)
|
||||||
else:
|
else:
|
||||||
other_themes_colors = False
|
other_themes_colors = False
|
||||||
field = CharField(max_length=32,
|
field = CharField(
|
||||||
|
max_length=32,
|
||||||
label=locationgroup.title,
|
label=locationgroup.title,
|
||||||
required=False,
|
required=False,
|
||||||
initial=value,
|
initial=value,
|
||||||
|
@ -375,7 +376,8 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
|
||||||
self.instance.groups.set(groups)
|
self.instance.groups.set(groups)
|
||||||
|
|
||||||
if self._meta.model.__name__ == 'Theme':
|
if self._meta.model.__name__ == 'Theme':
|
||||||
locationgroup_colors = {l.location_group_id: l for l in self.instance.location_groups.all()}
|
locationgroup_colors = {theme_location_group.location_group_id: theme_location_group
|
||||||
|
for theme_location_group in self.instance.location_groups.all()}
|
||||||
for locationgroup in LocationGroup.objects.all():
|
for locationgroup in LocationGroup.objects.all():
|
||||||
value = self.cleaned_data[f'locationgroup_{locationgroup.pk}']
|
value = self.cleaned_data[f'locationgroup_{locationgroup.pk}']
|
||||||
if value:
|
if value:
|
||||||
|
|
|
@ -87,8 +87,10 @@ class GeometryField(models.JSONField):
|
||||||
def _validate_geomtype(self, value, exception: typing.Type[Exception] = ValidationError):
|
def _validate_geomtype(self, value, exception: typing.Type[Exception] = ValidationError):
|
||||||
if not isinstance(value, self.classes):
|
if not isinstance(value, self.classes):
|
||||||
# if you get this error with wrappedgeometry, looked into wrapped_geom
|
# if you get this error with wrappedgeometry, looked into wrapped_geom
|
||||||
raise exception('Expected %s instance, got %s instead.' % (' or '.join(c.__name__ for c in self.classes),
|
raise TypeError('Expected %s instance, got %s, %s instead.' % (
|
||||||
repr(value)))
|
' or '.join(c.__name__ for c in self.classes),
|
||||||
|
repr(value), value.wrapped_geom
|
||||||
|
))
|
||||||
|
|
||||||
def get_final_value(self, value, as_json=False):
|
def get_final_value(self, value, as_json=False):
|
||||||
json_value = format_geojson(mapping(value))
|
json_value = format_geojson(mapping(value))
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from pydantic import Field, BaseModel
|
from pydantic import BaseModel, Field, PositiveInt
|
||||||
from pydantic import PositiveInt
|
|
||||||
from shapely import Point
|
from shapely import Point
|
||||||
from shapely.geometry import shape
|
from shapely.geometry import shape
|
||||||
|
|
||||||
from c3nav.api.utils import NonEmptyStr
|
from c3nav.api.utils import NonEmptyStr
|
||||||
from c3nav.mapdata.models import Area, Space, LocationGroup, LocationSlug, MapUpdate
|
from c3nav.mapdata.models import Area, LocationGroup, LocationSlug, MapUpdate, Space
|
||||||
from c3nav.mapdata.models.geometry.space import POI
|
from c3nav.mapdata.models.geometry.space import POI
|
||||||
from c3nav.mapdata.models.report import Report
|
from c3nav.mapdata.models.report import Report
|
||||||
from c3nav.mapdata.utils.cache.changes import changed_geometries
|
from c3nav.mapdata.utils.cache.changes import changed_geometries
|
||||||
|
@ -189,7 +187,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
if result.geometry != new_geometry or True:
|
if result.geometry != new_geometry or True:
|
||||||
if result.import_block_geom:
|
if result.import_block_geom:
|
||||||
geometry_needs_change.append(f"change geometry")
|
geometry_needs_change.append("change geometry")
|
||||||
print(f"NOTE: {item.slug} / {item.id} geometry has changed but is blocked")
|
print(f"NOTE: {item.slug} / {item.id} geometry has changed but is blocked")
|
||||||
else:
|
else:
|
||||||
result.geometry = new_geometry
|
result.geometry = new_geometry
|
||||||
|
@ -198,7 +196,7 @@ class Command(BaseCommand):
|
||||||
new_main_point = Point(item.location) if item.location else None
|
new_main_point = Point(item.location) if item.location else None
|
||||||
if result.main_point != new_main_point:
|
if result.main_point != new_main_point:
|
||||||
if result.import_block_geom:
|
if result.import_block_geom:
|
||||||
geometry_needs_change.append(f"change main point")
|
geometry_needs_change.append("change main point")
|
||||||
print(f"NOTE: {item.slug} / {item.id} main point has changed but is blocked")
|
print(f"NOTE: {item.slug} / {item.id} main point has changed but is blocked")
|
||||||
else:
|
else:
|
||||||
result.main_point = new_main_point
|
result.main_point = new_main_point
|
||||||
|
@ -210,8 +208,8 @@ class Command(BaseCommand):
|
||||||
obj=item,
|
obj=item,
|
||||||
report=Report(
|
report=Report(
|
||||||
category="location-issue",
|
category="location-issue",
|
||||||
title=f"importhub: geometry is blocked but needs changing",
|
title="importhub: geometry is blocked but needs changing",
|
||||||
description=f"changes needed: "+','.join(geometry_needs_change),
|
description="changes needed: "+','.join(geometry_needs_change),
|
||||||
location=result,
|
location=result,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -278,8 +276,8 @@ class Command(BaseCommand):
|
||||||
obj=item,
|
obj=item,
|
||||||
report=Report(
|
report=Report(
|
||||||
category="location-issue",
|
category="location-issue",
|
||||||
title=f"importhub: data is blocked but needs changing",
|
title="importhub: data is blocked but needs changing",
|
||||||
description=f"changes needed: "+','.join(data_needs_change),
|
description="changes needed: "+','.join(data_needs_change),
|
||||||
location=result,
|
location=result,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -299,8 +297,8 @@ class Command(BaseCommand):
|
||||||
obj=item,
|
obj=item,
|
||||||
report=Report(
|
report=Report(
|
||||||
category="location-issue",
|
category="location-issue",
|
||||||
title=f"importhub: location no longer has any valid group ids",
|
title="importhub: location no longer has any valid group ids",
|
||||||
description=f"from the hub we would remove all groups, this seems wrong",
|
description="from the hub we would remove all groups, this seems wrong",
|
||||||
location=result,
|
location=result,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -326,8 +324,8 @@ class Command(BaseCommand):
|
||||||
obj=item,
|
obj=item,
|
||||||
report=Report(
|
report=Report(
|
||||||
category="location-issue",
|
category="location-issue",
|
||||||
title=f"importhub: new groups",
|
title="importhub: new groups",
|
||||||
description=(f"hub wants new groups for this, groups are now: " +
|
description=("hub wants new groups for this, groups are now: " +
|
||||||
str([group.title for group in new_groups])),
|
str([group.title for group in new_groups])),
|
||||||
location=result,
|
location=result,
|
||||||
)
|
)
|
||||||
|
@ -343,10 +341,9 @@ class Command(BaseCommand):
|
||||||
obj=import_tag,
|
obj=import_tag,
|
||||||
report=Report(
|
report=Report(
|
||||||
category="location-issue",
|
category="location-issue",
|
||||||
title=f"importhub: delete this",
|
title="importhub: delete this",
|
||||||
description=f"hub wants to delete this",
|
description="hub wants to delete this",
|
||||||
location=location,
|
location=location,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(f"NOTE: {location.slug} / {import_tag} should be deleted")
|
print(f"NOTE: {location.slug} / {import_tag} should be deleted")
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import typing
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -21,6 +22,9 @@ from c3nav.mapdata.utils.cache.changes import changed_geometries
|
||||||
from c3nav.mapdata.utils.geometry import unwrap_geom
|
from c3nav.mapdata.utils.geometry import unwrap_geom
|
||||||
from c3nav.mapdata.utils.json import format_geojson
|
from c3nav.mapdata.utils.json import format_geojson
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from c3nav.mapdata.render.theme import ThemeColorManager
|
||||||
|
|
||||||
|
|
||||||
class SpaceGeometryMixin(GeometryMixin):
|
class SpaceGeometryMixin(GeometryMixin):
|
||||||
space = models.ForeignKey('mapdata.Space', on_delete=models.CASCADE, verbose_name=_('space'))
|
space = models.ForeignKey('mapdata.Space', on_delete=models.CASCADE, verbose_name=_('space'))
|
||||||
|
@ -240,7 +244,11 @@ class Obstacle(SpaceGeometryMixin, models.Model):
|
||||||
def get_color(self, color_manager: 'ThemeColorManager', instance=None):
|
def get_color(self, color_manager: 'ThemeColorManager', instance=None):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
instance = self
|
instance = self
|
||||||
return color_manager.obstaclegroup_fill_color(instance.group) if instance.group is not None else color_manager.obstacles_default_fill
|
return (
|
||||||
|
color_manager.obstaclegroup_fill_color(instance.group)
|
||||||
|
if instance.group is not None
|
||||||
|
else color_manager.obstacles_default_fill
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LineObstacle(SpaceGeometryMixin, models.Model):
|
class LineObstacle(SpaceGeometryMixin, models.Model):
|
||||||
|
@ -284,7 +292,11 @@ class LineObstacle(SpaceGeometryMixin, models.Model):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
instance = self
|
instance = self
|
||||||
# TODO: should line obstacles use border color?
|
# TODO: should line obstacles use border color?
|
||||||
return color_manager.obstaclegroup_fill_color(instance.group) if instance.group is not None else color_manager.obstacles_default_fill
|
return (
|
||||||
|
color_manager.obstaclegroup_fill_color(instance.group)
|
||||||
|
if instance.group is not None
|
||||||
|
else color_manager.obstacles_default_fill
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def buffered_geometry(self):
|
def buffered_geometry(self):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import string
|
import string
|
||||||
|
import typing
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
@ -25,6 +26,9 @@ from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
|
||||||
from c3nav.mapdata.utils.fields import LocationById
|
from c3nav.mapdata.utils.fields import LocationById
|
||||||
from c3nav.mapdata.utils.models import get_submodels
|
from c3nav.mapdata.utils.models import get_submodels
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from c3nav.mapdata.render.theme import ThemeColorManager
|
||||||
|
|
||||||
|
|
||||||
class LocationSlugManager(models.Manager):
|
class LocationSlugManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
|
|
@ -54,6 +54,7 @@ class ThemeObstacleGroupBackgroundColor(models.Model):
|
||||||
A background color for an ObstacleGroup in a theme
|
A background color for an ObstacleGroup in a theme
|
||||||
"""
|
"""
|
||||||
theme = models.ForeignKey(Theme, on_delete=models.CASCADE, related_name="obstacle_groups")
|
theme = models.ForeignKey(Theme, on_delete=models.CASCADE, related_name="obstacle_groups")
|
||||||
obstacle_group = models.ForeignKey(ObstacleGroup, on_delete=models.SET_NULL, null=True, blank=True, related_name="theme_colors")
|
obstacle_group = models.ForeignKey(ObstacleGroup, on_delete=models.SET_NULL, null=True, blank=True,
|
||||||
|
related_name="theme_colors")
|
||||||
fill_color = models.CharField(max_length=32, null=True, blank=True)
|
fill_color = models.CharField(max_length=32, null=True, blank=True)
|
||||||
border_color = models.CharField(max_length=32, null=True, blank=True)
|
border_color = models.CharField(max_length=32, null=True, blank=True)
|
||||||
|
|
|
@ -6,8 +6,7 @@ from itertools import chain
|
||||||
from typing import Literal, TypeVar
|
from typing import Literal, TypeVar
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from matplotlib.patches import Polygon
|
from shapely.geometry import GeometryCollection, LineString, MultiLineString, MultiPolygon, Point, Polygon
|
||||||
from shapely.geometry import GeometryCollection, LineString, MultiLineString, MultiPolygon, Point
|
|
||||||
from shapely.geometry.base import BaseGeometry
|
from shapely.geometry.base import BaseGeometry
|
||||||
from shapely.ops import unary_union
|
from shapely.ops import unary_union
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import operator
|
import operator
|
||||||
|
import typing
|
||||||
from collections import Counter, deque
|
from collections import Counter, deque
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
@ -15,6 +16,9 @@ from c3nav.mapdata.utils.cache import AccessRestrictionAffected
|
||||||
from c3nav.mapdata.utils.geometry import get_rings, unwrap_geom
|
from c3nav.mapdata.utils.geometry import get_rings, unwrap_geom
|
||||||
from c3nav.mapdata.utils.mesh import triangulate_rings
|
from c3nav.mapdata.utils.mesh import triangulate_rings
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from c3nav.mapdata.render.theme import ThemeColorManager
|
||||||
|
|
||||||
empty_geometry_collection = GeometryCollection()
|
empty_geometry_collection = GeometryCollection()
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,7 +140,9 @@ class LevelGeometries:
|
||||||
area.geometry = area.geometry.intersection(unwrap_geom(space.walkable_geom))
|
area.geometry = area.geometry.intersection(unwrap_geom(space.walkable_geom))
|
||||||
if access_restriction is not None:
|
if access_restriction is not None:
|
||||||
access_restriction_affected.setdefault(access_restriction, []).append(area.geometry)
|
access_restriction_affected.setdefault(access_restriction, []).append(area.geometry)
|
||||||
colors.setdefault(area.get_color_sorted(color_manager), {}).setdefault(access_restriction, []).append(area.geometry)
|
colors.setdefault(
|
||||||
|
area.get_color_sorted(color_manager), {}
|
||||||
|
).setdefault(access_restriction, []).append(area.geometry)
|
||||||
|
|
||||||
for column in space.columns.all():
|
for column in space.columns.all():
|
||||||
access_restriction = column.access_restriction_id
|
access_restriction = column.access_restriction_id
|
||||||
|
@ -162,7 +168,9 @@ class LevelGeometries:
|
||||||
for lineobstacle in space.lineobstacles.all():
|
for lineobstacle in space.lineobstacles.all():
|
||||||
if not lineobstacle.height:
|
if not lineobstacle.height:
|
||||||
continue
|
continue
|
||||||
obstacles.setdefault(int(lineobstacle.height*1000), {}).setdefault(lineobstacle.get_color(color_manager), []).append(
|
obstacles.setdefault(int(lineobstacle.height*1000), {}).setdefault(
|
||||||
|
lineobstacle.get_color(color_manager), []
|
||||||
|
).append(
|
||||||
lineobstacle.buffered_geometry.intersection(unwrap_geom(space.walkable_geom))
|
lineobstacle.buffered_geometry.intersection(unwrap_geom(space.walkable_geom))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,9 @@ class LevelRenderData:
|
||||||
altitudeareas_above = [] # todo: typing
|
altitudeareas_above = [] # todo: typing
|
||||||
for render_level in reversed(levels):
|
for render_level in reversed(levels):
|
||||||
# build level geometry for every single level
|
# build level geometry for every single level
|
||||||
single_level_geoms[render_level.pk] = LevelGeometries.build_for_level(render_level, color_manager, altitudeareas_above)
|
single_level_geoms[render_level.pk] = LevelGeometries.build_for_level(
|
||||||
|
render_level, color_manager, altitudeareas_above
|
||||||
|
)
|
||||||
|
|
||||||
# ignore intermediate levels in this pass
|
# ignore intermediate levels in this pass
|
||||||
if render_level.on_top_of_id is not None:
|
if render_level.on_top_of_id is not None:
|
||||||
|
|
|
@ -11,7 +11,6 @@ from c3nav.mapdata.render.utils import get_full_levels, get_min_altitude
|
||||||
from c3nav.mapdata.utils.color import color_to_rgb, rgb_to_color
|
from c3nav.mapdata.utils.color import color_to_rgb, rgb_to_color
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MapRenderer:
|
class MapRenderer:
|
||||||
def __init__(self, level, minx, miny, maxx, maxy, scale=1, access_permissions=None, full_levels=False,
|
def __init__(self, level, minx, miny, maxx, maxy, scale=1, access_permissions=None, full_levels=False,
|
||||||
min_width=None):
|
min_width=None):
|
||||||
|
|
|
@ -24,13 +24,13 @@ class ThemeColorManager:
|
||||||
self.obstacles_default_border = RENDER_COLOR_OBSTACLES_DEFAULT_BORDER
|
self.obstacles_default_border = RENDER_COLOR_OBSTACLES_DEFAULT_BORDER
|
||||||
self.location_group_border_colors = {}
|
self.location_group_border_colors = {}
|
||||||
self.location_group_fill_colors = {
|
self.location_group_fill_colors = {
|
||||||
l.pk: l.color
|
location_group.pk: location_group.color
|
||||||
for l in LocationGroup.objects.filter(color__isnull=False).all()
|
for location_group in LocationGroup.objects.filter(color__isnull=False).all()
|
||||||
}
|
}
|
||||||
self.obstacle_group_border_colors = {}
|
self.obstacle_group_border_colors = {}
|
||||||
self.obstacle_group_fill_colors = {
|
self.obstacle_group_fill_colors = {
|
||||||
o.pk: o.color
|
obstacle_group.pk: obstacle_group.color
|
||||||
for o in ObstacleGroup.objects.filter(color__isnull=False).all()
|
for obstacle_group in ObstacleGroup.objects.filter(color__isnull=False).all()
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
self.background = theme.color_background
|
self.background = theme.color_background
|
||||||
|
@ -41,20 +41,20 @@ class ThemeColorManager:
|
||||||
self.obstacles_default_fill = theme.color_obstacles_default_fill
|
self.obstacles_default_fill = theme.color_obstacles_default_fill
|
||||||
self.obstacles_default_border = theme.color_obstacles_default_border
|
self.obstacles_default_border = theme.color_obstacles_default_border
|
||||||
self.location_group_border_colors = {
|
self.location_group_border_colors = {
|
||||||
l.location_group_id: l.border_color
|
theme_location_group.location_group_id: theme_location_group.border_color
|
||||||
for l in theme.location_groups.all()
|
for theme_location_group in theme.location_groups.all()
|
||||||
}
|
}
|
||||||
self.location_group_fill_colors = {
|
self.location_group_fill_colors = {
|
||||||
l.location_group_id: l.fill_color
|
theme_location_group.location_group_id: theme_location_group.fill_color
|
||||||
for l in theme.location_groups.all()
|
for theme_location_group in theme.location_groups.all()
|
||||||
}
|
}
|
||||||
self.obstacle_group_border_colors = {
|
self.obstacle_group_border_colors = {
|
||||||
o.obstacle_group_id: o.border_color
|
theme_obstacle_group.obstacle_group_id: theme_obstacle_group.border_color
|
||||||
for o in theme.obstacle_groups.all()
|
for theme_obstacle_group in theme.obstacle_groups.all()
|
||||||
}
|
}
|
||||||
self.obstacle_group_fill_colors = {
|
self.obstacle_group_fill_colors = {
|
||||||
o.obstacle_group_id: o.fill_color
|
theme_obstacle.obstacle_group_id: theme_obstacle.fill_color
|
||||||
for o in theme.obstacle_groups.all()
|
for theme_obstacle in theme.obstacle_groups.all()
|
||||||
}
|
}
|
||||||
|
|
||||||
def locationgroup_border_color(self, location_group: LocationGroup):
|
def locationgroup_border_color(self, location_group: LocationGroup):
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django.urls import path, register_converter
|
||||||
|
|
||||||
from c3nav.mapdata.converters import (AccessPermissionsConverter, ArchiveFileExtConverter, HistoryFileExtConverter,
|
from c3nav.mapdata.converters import (AccessPermissionsConverter, ArchiveFileExtConverter, HistoryFileExtConverter,
|
||||||
HistoryModeConverter, SignedIntConverter)
|
HistoryModeConverter, SignedIntConverter)
|
||||||
from c3nav.mapdata.views import get_cache_package, map_history, tile, preview_location, preview_route
|
from c3nav.mapdata.views import get_cache_package, map_history, preview_location, preview_route, tile
|
||||||
from c3nav.site.converters import LocationConverter
|
from c3nav.site.converters import LocationConverter
|
||||||
|
|
||||||
register_converter(LocationConverter, 'loc')
|
register_converter(LocationConverter, 'loc')
|
||||||
|
@ -16,7 +16,8 @@ urlpatterns = [
|
||||||
path('<int:level>/<sint:zoom>/<sint:x>/<sint:y>/<int:theme>.png', tile, name='mapdata.tile'),
|
path('<int:level>/<sint:zoom>/<sint:x>/<sint:y>/<int:theme>.png', tile, name='mapdata.tile'),
|
||||||
path('preview/l/<loc:slug>.png', preview_location, name='mapdata.preview.location'),
|
path('preview/l/<loc:slug>.png', preview_location, name='mapdata.preview.location'),
|
||||||
path('preview/r/<loc:slug>/<loc:slug2>.png', preview_route, name='mapdata.preview.route'),
|
path('preview/r/<loc:slug>/<loc:slug2>.png', preview_route, name='mapdata.preview.route'),
|
||||||
path('<int:level>/<sint:zoom>/<sint:x>/<sint:y>/<int:theme>/<a_perms:access_permissions>.png', tile, name='mapdata.tile'),
|
path('<int:level>/<sint:zoom>/<sint:x>/<sint:y>/<int:theme>/<a_perms:access_permissions>.png', tile,
|
||||||
|
name='mapdata.tile'),
|
||||||
path('history/<int:level>/<h_mode:mode>.<h_fileext:filetype>', map_history, name='mapdata.map_history'),
|
path('history/<int:level>/<h_mode:mode>.<h_fileext:filetype>', map_history, name='mapdata.map_history'),
|
||||||
path('cache/package.<archive_fileext:filetype>', get_cache_package, name='mapdata.cache_package'),
|
path('cache/package.<archive_fileext:filetype>', get_cache_package, name='mapdata.cache_package'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -51,5 +51,6 @@ def build_access_cache_key(access_permissions: set):
|
||||||
def build_tile_etag(level_id, zoom, x, y, theme_id, base_cache_key, access_cache_key, tile_secret):
|
def build_tile_etag(level_id, zoom, x, y, theme_id, base_cache_key, access_cache_key, tile_secret):
|
||||||
# we want a short etag so HTTP 304 responses are tiny
|
# we want a short etag so HTTP 304 responses are tiny
|
||||||
return '"' + binascii.b2a_base64(hashlib.sha256(
|
return '"' + binascii.b2a_base64(hashlib.sha256(
|
||||||
('%d-%d-%d-%d:%s:%s:%s:%s' % (level_id, zoom, x, y, str(theme_id), base_cache_key, access_cache_key, tile_secret[:26])).encode()
|
('%d-%d-%d-%d:%s:%s:%s:%s' %
|
||||||
|
(level_id, zoom, x, y, str(theme_id), base_cache_key, access_cache_key, tile_secret[:26])).encode()
|
||||||
).digest()[:15], newline=False).decode() + '"'
|
).digest()[:15], newline=False).decode() + '"'
|
||||||
|
|
|
@ -12,7 +12,7 @@ from django.http import Http404, HttpResponse, HttpResponseNotModified, Streamin
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.http import content_disposition_header
|
from django.utils.http import content_disposition_header
|
||||||
from django.views.decorators.http import etag
|
from django.views.decorators.http import etag
|
||||||
from shapely import Point, box, LineString, unary_union
|
from shapely import LineString, Point, box, unary_union
|
||||||
|
|
||||||
from c3nav.mapdata.middleware import no_language
|
from c3nav.mapdata.middleware import no_language
|
||||||
from c3nav.mapdata.models import Level, MapUpdate
|
from c3nav.mapdata.models import Level, MapUpdate
|
||||||
|
@ -345,7 +345,7 @@ def tile(request, level, zoom, x, y, theme, access_permissions: Optional[set] =
|
||||||
if not cache_package.bounds_valid(minx, miny, maxx, maxy):
|
if not cache_package.bounds_valid(minx, miny, maxx, maxy):
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
theme = None if theme is 0 else int(theme)
|
theme = None if theme == 0 else int(theme)
|
||||||
theme_key = str(theme)
|
theme_key = str(theme)
|
||||||
|
|
||||||
# get level
|
# get level
|
||||||
|
@ -372,7 +372,8 @@ def tile(request, level, zoom, x, y, theme, access_permissions: Optional[set] =
|
||||||
access_cache_key = build_access_cache_key(access_permissions)
|
access_cache_key = build_access_cache_key(access_permissions)
|
||||||
|
|
||||||
# check browser cache
|
# check browser cache
|
||||||
tile_etag = build_tile_etag(level, zoom, x, y, theme_key, base_cache_key, access_cache_key, settings.SECRET_TILE_KEY)
|
tile_etag = build_tile_etag(level, zoom, x, y, theme_key, base_cache_key, access_cache_key,
|
||||||
|
settings.SECRET_TILE_KEY)
|
||||||
if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
|
if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
|
||||||
if if_none_match == tile_etag:
|
if if_none_match == tile_etag:
|
||||||
return HttpResponseNotModified()
|
return HttpResponseNotModified()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from typing import Annotated, Optional, Union, Any
|
from typing import Annotated, Any, Optional, Union
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
|
@ -361,7 +361,7 @@ MIDDLEWARE = [
|
||||||
'c3nav.mapdata.middleware.UserDataMiddleware',
|
'c3nav.mapdata.middleware.UserDataMiddleware',
|
||||||
'c3nav.site.middleware.MobileclientMiddleware',
|
'c3nav.site.middleware.MobileclientMiddleware',
|
||||||
'c3nav.control.middleware.UserPermissionsMiddleware',
|
'c3nav.control.middleware.UserPermissionsMiddleware',
|
||||||
#'c3nav.api.middleware.JsonRequestBodyMiddleware', # might still be needed in editor
|
# 'c3nav.api.middleware.JsonRequestBodyMiddleware', # might still be needed in editor
|
||||||
]
|
]
|
||||||
|
|
||||||
with suppress(ImportError):
|
with suppress(ImportError):
|
||||||
|
|
|
@ -2,7 +2,7 @@ from itertools import chain
|
||||||
|
|
||||||
from django.urls import path, register_converter
|
from django.urls import path, register_converter
|
||||||
|
|
||||||
from c3nav.site.converters import AtPositionConverter, CoordinatesConverter, IsEmbedConverter, LocationConverter
|
from c3nav.site.converters import AtPositionConverter, CoordinatesConverter, IsEmbedConverter
|
||||||
from c3nav.site.views import (about_view, access_redeem_view, account_manage, account_view, api_secret_create,
|
from c3nav.site.views import (about_view, access_redeem_view, account_manage, account_view, api_secret_create,
|
||||||
api_secret_list, change_password_view, choose_language, delete_account_view, login_view,
|
api_secret_list, change_password_view, choose_language, delete_account_view, login_view,
|
||||||
logout_view, map_index, position_create, position_detail, position_list, position_set,
|
logout_view, map_index, position_create, position_detail, position_list, position_set,
|
||||||
|
@ -18,13 +18,15 @@ pos = '<at_pos:pos>'
|
||||||
|
|
||||||
def index_paths(pre, suf):
|
def index_paths(pre, suf):
|
||||||
return [
|
return [
|
||||||
path(f'{pre}l/<loc:slug>/{suf}', map_index, {'mode': 'l', 'details': False, 'nearby': False}, name='site.index', ),
|
path(f'{pre}l/<loc:slug>/{suf}', map_index, {'mode': 'l', 'details': False, 'nearby': False},
|
||||||
|
name='site.index'),
|
||||||
path(f'{pre}l/<loc:slug>/details/{suf}', map_index, {'mode': 'l', 'details': True}, name='site.index'),
|
path(f'{pre}l/<loc:slug>/details/{suf}', map_index, {'mode': 'l', 'details': True}, name='site.index'),
|
||||||
path(f'{pre}l/<loc:slug>/nearby/{suf}', map_index, {'mode': 'l', 'nearby': True}, name='site.index'),
|
path(f'{pre}l/<loc:slug>/nearby/{suf}', map_index, {'mode': 'l', 'nearby': True}, name='site.index'),
|
||||||
path(f'{pre}o/<loc:slug>/{suf}', map_index, {'mode': 'o'}, name='site.index'),
|
path(f'{pre}o/<loc:slug>/{suf}', map_index, {'mode': 'o'}, name='site.index'),
|
||||||
path(f'{pre}d/<loc:slug>/{suf}', map_index, {'mode': 'd'}, name='site.index'),
|
path(f'{pre}d/<loc:slug>/{suf}', map_index, {'mode': 'd'}, name='site.index'),
|
||||||
path(f'{pre}r/{suf}', map_index, {'mode': 'r'}, name='site.index'),
|
path(f'{pre}r/{suf}', map_index, {'mode': 'r'}, name='site.index'),
|
||||||
path(f'{pre}r/<loc:slug>/<loc:slug2>/{suf}', map_index, {'mode': 'r', 'details': False, 'options': False}, name='site.index'),
|
path(f'{pre}r/<loc:slug>/<loc:slug2>/{suf}', map_index, {'mode': 'r', 'details': False, 'options': False},
|
||||||
|
name='site.index'),
|
||||||
path(f'{pre}r/<loc:slug>/<loc:slug2>/details/{suf}', map_index, {'mode': 'r', 'details': True},
|
path(f'{pre}r/<loc:slug>/<loc:slug2>/details/{suf}', map_index, {'mode': 'r', 'details': True},
|
||||||
name='site.index'),
|
name='site.index'),
|
||||||
path(f'{pre}r/<loc:slug>/<loc:slug2>/options/{suf}', map_index, {'mode': 'r', 'options': True},
|
path(f'{pre}r/<loc:slug>/<loc:slug2>/options/{suf}', map_index, {'mode': 'r', 'options': True},
|
||||||
|
|
|
@ -29,7 +29,7 @@ from c3nav import __version__ as c3nav_version
|
||||||
from c3nav.api.models import Secret
|
from c3nav.api.models import Secret
|
||||||
from c3nav.mapdata.grid import grid
|
from c3nav.mapdata.grid import grid
|
||||||
from c3nav.mapdata.models import Location, Source
|
from c3nav.mapdata.models import Location, Source
|
||||||
from c3nav.mapdata.models.access import AccessPermissionToken, AccessPermission
|
from c3nav.mapdata.models.access import AccessPermission, AccessPermissionToken
|
||||||
from c3nav.mapdata.models.locations import LocationRedirect, Position, SpecificLocation, get_position_secret
|
from c3nav.mapdata.models.locations import LocationRedirect, Position, SpecificLocation, get_position_secret
|
||||||
from c3nav.mapdata.models.report import Report, ReportUpdate
|
from c3nav.mapdata.models.report import Report, ReportUpdate
|
||||||
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,
|
||||||
|
|
|
@ -188,7 +188,7 @@ class TileServer:
|
||||||
error = False
|
error = False
|
||||||
try:
|
try:
|
||||||
last_check = self.cache.get('cache_package_last_successful_check')
|
last_check = self.cache.get('cache_package_last_successful_check')
|
||||||
except pylibmc.Error as e:
|
except pylibmc.Error:
|
||||||
error = True
|
error = True
|
||||||
text = b'memcached error'
|
text = b'memcached error'
|
||||||
else:
|
else:
|
||||||
|
@ -300,7 +300,8 @@ class TileServer:
|
||||||
if cached_result is not None:
|
if cached_result is not None:
|
||||||
return self.deliver_tile(start_response, tile_etag, cached_result)
|
return self.deliver_tile(start_response, tile_etag, cached_result)
|
||||||
|
|
||||||
r = requests.get('%s/map/%d/%d/%d/%d/%d/%s.png' % (self.upstream_base, level, zoom, x, y, theme_id, access_cache_key),
|
r = requests.get('%s/map/%d/%d/%d/%d/%d/%s.png' %
|
||||||
|
(self.upstream_base, level, zoom, x, y, theme_id, access_cache_key),
|
||||||
headers=self.auth_headers, auth=self.http_auth)
|
headers=self.auth_headers, auth=self.http_auth)
|
||||||
|
|
||||||
if r.status_code == 200 and r.headers['Content-Type'] == 'image/png':
|
if r.status_code == 200 and r.headers['Content-Type'] == 'image/png':
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue