diff --git a/generic_map_api/constants.py b/generic_map_api/constants.py new file mode 100644 index 0000000..16b1491 --- /dev/null +++ b/generic_map_api/constants.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class ViewportHandling(Enum): + TILES = "tiles" + SPLIT = "split" + SINGLE = "single" diff --git a/generic_map_api/values.py b/generic_map_api/values.py index 74d04d7..a8e1978 100644 --- a/generic_map_api/values.py +++ b/generic_map_api/values.py @@ -1,5 +1,7 @@ from __future__ import annotations +import re + import geohash2 from shapely.geometry import Point, Polygon from skytek_utils.spatial import tiles @@ -8,6 +10,11 @@ class BaseViewPort: + def __init__(self) -> None: + self.size = None + self.meters_per_pixel = None + self.zoom = None + def to_polygon(self) -> Polygon: raise NotImplementedError() @@ -17,6 +24,7 @@ def get_dimensions(self): class ViewPort(BaseViewPort): def __init__(self, upper_left, lower_right) -> None: + super().__init__() self.upper_left = upper_left self.lower_right = lower_right @@ -37,7 +45,8 @@ def get_dimensions(self): def from_geohashes_query_param(cls, geohashes): if geohashes is None: return None - geohashes_arr = geohashes.split("/") + split_by = r"[\/,\- ]" + geohashes_arr = re.split(split_by, geohashes) if len(geohashes_arr) >= 2: lat1, lon1, lat1_err, lon1_err = geohash2.decode_exactly(geohashes_arr[0]) lat2, lon2, lat2_err, lon2_err = geohash2.decode_exactly(geohashes_arr[1]) @@ -58,6 +67,7 @@ def from_geohashes_query_param(cls, geohashes): class Tile(BaseViewPort): def __init__(self, x: int, y: int, z: int) -> None: + super().__init__() self.x = x self.y = y self.z = z @@ -99,7 +109,9 @@ def from_query_param(cls, param): if param is None: return None - param_arr = param.split("/") + split_by = r"[\/,\- ]" + param_arr = re.split(split_by, param) + if len(param_arr) != 3: raise ValueError("Tile has to be defined by exactly 3 integers") diff --git a/generic_map_api/views.py b/generic_map_api/views.py index ae754d6..075f36f 100644 --- a/generic_map_api/views.py +++ b/generic_map_api/views.py @@ -11,6 +11,7 @@ from rest_framework.viewsets import ViewSet from .clustering import Clustering +from .constants import ViewportHandling from .serializers import BaseFeatureSerializer, ClusterSerializer from .values import BaseViewPort, Tile, ViewPort @@ -104,8 +105,10 @@ class MapFeaturesBaseView(MapApiBaseView): require_viewport_zoom: bool = False require_viewport_size: bool = False + require_viewport_meters_per_pixel: bool = False - preferred_viewport_handling: str = "viewport" + preferred_viewport_handling: str = ViewportHandling.SPLIT + preferred_viewport_chunks: int = 10 def get_meta(self, request): return { @@ -116,6 +119,7 @@ def get_meta(self, request): "icon": self.get_icon(), "clustering": self.clustering, "preferred_viewport_handling": self.preferred_viewport_handling, + "preferred_viewport_chunks": self.preferred_viewport_chunks, "query_params": self.render_query_params_meta(request), "requirements": self.render_requirements(request), "urls": { @@ -135,6 +139,15 @@ def list(self, request): request.GET.get("viewport", None) ) + if "viewport.zoom" in request.GET: + viewport.zoom = request.GET["viewport.zoom"] + + if "viewport.mpp" in request.GET: + viewport.meters_per_pixel = request.GET["viewport.mpp"] + + if "viewport.size" in request.GET: + viewport.size = tuple(request.GET["viewport.size"].split("x")) + params = self._parse_params(request) clustering_config = self._parse_clustering_config(request) @@ -170,6 +183,8 @@ def render_requirements(self, request): # pylint: disable=unused-argument requirements.append("viewport.size") if self.require_viewport_zoom: requirements.append("viewport.zoom") + if self.require_viewport_meters_per_pixel: + requirements.append("viewport.mpp") return requirements def retrieve(self, request, pk): # pylint: disable=unused-argument