Skip to content
This repository has been archived by the owner on Sep 2, 2020. It is now read-only.

feature request - geospatial query support #36

Open
knackjax opened this issue May 16, 2018 · 4 comments
Open

feature request - geospatial query support #36

knackjax opened this issue May 16, 2018 · 4 comments

Comments

@knackjax
Copy link
Contributor

knackjax commented May 16, 2018

can we get geo-spatial queries support? bounding box would be useful for us displaying data on a map.

I imagine having something similar to this library

https://github.com/barseghyanartur/django-elasticsearch-dsl-drf/blob/master/advanced_usage_examples.rst#geo-spatial-features

will take a look if i have time as well for a PR...

@knackjax
Copy link
Contributor Author

I added this in as a subclass implementation, please let me know if you want a PR.

rest_framework_elasticsearch_ext.es_filters_ext


# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

from elasticsearch_dsl import Q

from rest_framework_elasticsearch.es_filters import BaseEsFilterBackend

GEO_BOUNDING_BOX_PARAM = 'location__geo_bounding_box'


class ElasticGeoBoundingBoxFilter(BaseEsFilterBackend):
    geo_bounding_box_param = GEO_BOUNDING_BOX_PARAM

    def get_geo_bounding_box_params(self, request, view):
        """
        Geo bounding box are set by a ?location__geo_bounding_box=... query parameter,
        and may be comma and/or whitespace delimited.
        """
        s_fields = view.get_es_geo_bounding_box_fields()
        if not s_fields:
            return {}

        values = request.query_params.get(self.geo_bounding_box_param, '').split('|')

        if len(values) < 2:
            return {}

        top_left_points = {}
        bottom_right_points = {}
        options = {}

        # Top left
        lat_lon = values[0].split(
            ','
        )
        if len(lat_lon) >= 2:
            top_left_points.update({
                'lat': float(lat_lon[0]),
                'lon': float(lat_lon[1]),
            })

        # Bottom right
        lat_lon = values[1].split(
            ','
        )
        if len(lat_lon) >= 2:
            bottom_right_points.update({
                'lat': float(lat_lon[0]),
                'lon': float(lat_lon[1]),
            })

        # Options
        for value in values[2:]:
            if ':' in value:
                opt_name_val = value.split(
                    ':'
                )
                if len(opt_name_val) >= 2:
                    if opt_name_val[0] in ('_name', 'validation_method', 'type'):
                        options.update(
                            {
                                opt_name_val[0]: opt_name_val[1]
                            }
                        )

        if not top_left_points or not bottom_right_points:
            return {}

        params = {}
        for s_field in s_fields:
            param = {
                s_field.name: {
                    'top_left': top_left_points,
                    'bottom_right': bottom_right_points,
                }
            }
            params.update(param)
        params.update(options)
        return params

    def filter_search(self, request, search, view):
        s_params = self.get_geo_bounding_box_params(request, view)

        if not s_params:
            return search

        print(s_params)

        q = Q('geo_bounding_box', **s_params)
        search = search.query(q)
        return search

rest_framework_elasticsearch_ext.es_filters_ext


# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

from rest_framework_elasticsearch import es_views

from rest_framework_elasticsearch.es_mixins import ListElasticMixin
from rest_framework_elasticsearch.es_pagination import ElasticLimitOffsetPagination


class ElasticAPIViewExt(es_views.ElasticAPIView):
    """Elasticsearch base API view class."""
    def get_es_geo_bounding_box_fields(self):
        """
        Return field or fields used for search.
        The return value must be an iterable.
        """
        return getattr(self, 'es_geo_bounding_box_fields', None)


class ListElasticAPIViewExt(ListElasticMixin, ElasticAPIViewExt):
    """Concrete view for listing a queryset."""
    es_pagination_class = ElasticLimitOffsetPagination

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

@dmvass
Copy link
Collaborator

dmvass commented May 18, 2018

Hello @knackjax, thanks for your proposal. Can you please create PR with all related tests and documentary updates?

@knackjax
Copy link
Contributor Author

I haven't had time to create tests/docs, but I have a branch if you like to look https://github.com/knackjax/django-rest-elasticsearch/tree/gis_queries

myarik added a commit that referenced this issue Oct 29, 2019
@josemlp91
Copy link

@myarik I think, this issue can be closed yet.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants