speedup geometry serialization by smart wrapping magic
This commit is contained in:
parent
4819d67f99
commit
f1e379aea8
3 changed files with 54 additions and 9 deletions
|
@ -14,7 +14,7 @@ from shapely import validation
|
||||||
from shapely.geometry import LineString, MultiPolygon, Point, Polygon, mapping, shape
|
from shapely.geometry import LineString, MultiPolygon, Point, Polygon, mapping, shape
|
||||||
from shapely.geometry.base import BaseGeometry
|
from shapely.geometry.base import BaseGeometry
|
||||||
|
|
||||||
from c3nav.mapdata.utils.geometry import clean_geometry
|
from c3nav.mapdata.utils.geometry import WrappedGeometry, clean_geometry
|
||||||
from c3nav.mapdata.utils.json import format_geojson
|
from c3nav.mapdata.utils.json import format_geojson
|
||||||
|
|
||||||
logger = logging.getLogger('c3nav')
|
logger = logging.getLogger('c3nav')
|
||||||
|
@ -52,7 +52,7 @@ class GeometryField(models.TextField):
|
||||||
def from_db_value(self, value, expression, connection):
|
def from_db_value(self, value, expression, connection):
|
||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
return shape(json.loads(value))
|
return WrappedGeometry(json.loads(value))
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if value is None or value == '':
|
if value is None or value == '':
|
||||||
|
|
|
@ -4,11 +4,11 @@ from collections import OrderedDict
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from shapely.geometry import Point, box, mapping
|
from shapely.geometry import Point, box
|
||||||
from shapely.ops import unary_union
|
from shapely.ops import unary_union
|
||||||
|
|
||||||
from c3nav.mapdata.models.base import SerializableMixin
|
from c3nav.mapdata.models.base import SerializableMixin
|
||||||
from c3nav.mapdata.utils.geometry import assert_multipolygon, good_representative_point
|
from c3nav.mapdata.utils.geometry import assert_multipolygon, good_representative_point, smart_mapping
|
||||||
from c3nav.mapdata.utils.json import format_geojson
|
from c3nav.mapdata.utils.json import format_geojson
|
||||||
|
|
||||||
geometry_affecting_fields = ('height', 'width', 'access_restriction')
|
geometry_affecting_fields = ('height', 'width', 'access_restriction')
|
||||||
|
@ -45,11 +45,11 @@ class GeometryMixin(SerializableMixin):
|
||||||
result = OrderedDict((
|
result = OrderedDict((
|
||||||
('type', 'Feature'),
|
('type', 'Feature'),
|
||||||
('properties', self.get_geojson_properties(instance=instance)),
|
('properties', self.get_geojson_properties(instance=instance)),
|
||||||
('geometry', format_geojson(mapping(self.geometry), round=False)),
|
('geometry', format_geojson(smart_mapping(self.geometry), round=False)),
|
||||||
))
|
))
|
||||||
original_geometry = getattr(self, 'original_geometry', None)
|
original_geometry = getattr(self, 'original_geometry', None)
|
||||||
if original_geometry:
|
if original_geometry:
|
||||||
result['original_geometry'] = format_geojson(mapping(original_geometry), round=False)
|
result['original_geometry'] = format_geojson(smart_mapping(original_geometry), round=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -72,7 +72,7 @@ class GeometryMixin(SerializableMixin):
|
||||||
def _serialize(self, geometry=True, simple_geometry=False, **kwargs):
|
def _serialize(self, geometry=True, simple_geometry=False, **kwargs):
|
||||||
result = super()._serialize(simple_geometry=simple_geometry, **kwargs)
|
result = super()._serialize(simple_geometry=simple_geometry, **kwargs)
|
||||||
if geometry:
|
if geometry:
|
||||||
result['geometry'] = format_geojson(mapping(self.geometry), round=False)
|
result['geometry'] = format_geojson(smart_mapping(self.geometry), round=False)
|
||||||
if simple_geometry:
|
if simple_geometry:
|
||||||
result['point'] = (self.level_id, ) + tuple(round(i, 2) for i in self.point.coords[0])
|
result['point'] = (self.level_id, ) + tuple(round(i, 2) for i in self.point.coords[0])
|
||||||
if not isinstance(self.geometry, Point):
|
if not isinstance(self.geometry, Point):
|
||||||
|
@ -84,9 +84,9 @@ class GeometryMixin(SerializableMixin):
|
||||||
def details_display(self, detailed_geometry=True, **kwargs):
|
def details_display(self, detailed_geometry=True, **kwargs):
|
||||||
result = super().details_display(**kwargs)
|
result = super().details_display(**kwargs)
|
||||||
if detailed_geometry:
|
if detailed_geometry:
|
||||||
result['geometry'] = format_geojson(mapping(self.geometry), round=False)
|
result['geometry'] = format_geojson(smart_mapping(self.geometry), round=False)
|
||||||
else:
|
else:
|
||||||
result['geometry'] = format_geojson(mapping(box(*self.geometry.bounds)), round=False)
|
result['geometry'] = format_geojson(smart_mapping(box(*self.geometry.bounds)), round=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_shadow_geojson(self):
|
def get_shadow_geojson(self):
|
||||||
|
|
|
@ -6,10 +6,13 @@ from typing import List, Sequence, Union
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from django.core import checks
|
from django.core import checks
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from matplotlib.patches import PathPatch
|
from matplotlib.patches import PathPatch
|
||||||
from matplotlib.path import Path
|
from matplotlib.path import Path
|
||||||
from shapely import prepared, speedups
|
from shapely import prepared, speedups
|
||||||
from shapely.geometry import GeometryCollection, LinearRing, LineString, MultiLineString, MultiPolygon, Point, Polygon
|
from shapely.geometry import GeometryCollection, LinearRing, LineString, MultiLineString, MultiPolygon, Point, Polygon
|
||||||
|
from shapely.geometry import mapping as shapely_mapping
|
||||||
|
from shapely.geometry import shape as shapely_shape
|
||||||
|
|
||||||
if speedups.available:
|
if speedups.available:
|
||||||
speedups.enable()
|
speedups.enable()
|
||||||
|
@ -29,6 +32,48 @@ def check_speedups(app_configs, **kwargs):
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
class WrappedGeometry():
|
||||||
|
picklable = False
|
||||||
|
wrapped_geojson = None
|
||||||
|
|
||||||
|
def __init__(self, geojson):
|
||||||
|
self.wrapped_geojson = geojson
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def wrapped_geom(self):
|
||||||
|
return shapely_shape(self.wrapped_geojson)
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name in ('__getstate__'):
|
||||||
|
self.picklable = True
|
||||||
|
# make sure geometry is cached
|
||||||
|
if self.wrapped_geojson:
|
||||||
|
# noinspection PyStatementEffect
|
||||||
|
self.wrapped_geom
|
||||||
|
raise AttributeError
|
||||||
|
if name in ('__reduce__', '__getstate__', '__setstate__', '__reduce_ex__',
|
||||||
|
'__getnewargs__', '__getnewargs_ex__'):
|
||||||
|
raise AttributeError
|
||||||
|
return getattr(self.wrapped_geom, name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __class__(self):
|
||||||
|
result = WrappedGeometry if self.picklable else self.wrapped_geom.__class__
|
||||||
|
self.__dict__.pop('picklable', None)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def unwrap_geometry(geometry):
|
||||||
|
return getattr(geometry, 'geom', geometry)
|
||||||
|
|
||||||
|
|
||||||
|
def smart_mapping(geometry):
|
||||||
|
if hasattr(geometry, 'wrapped_geojson'):
|
||||||
|
print('being smart!')
|
||||||
|
return geometry.wrapped_geojson
|
||||||
|
return shapely_mapping(geometry)
|
||||||
|
|
||||||
|
|
||||||
def clean_geometry(geometry):
|
def clean_geometry(geometry):
|
||||||
"""
|
"""
|
||||||
if the given geometry is a Polygon and invalid, try to make it valid if it results in a Polygon (not MultiPolygon)
|
if the given geometry is a Polygon and invalid, try to make it valid if it results in a Polygon (not MultiPolygon)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue