Latitude/longitude widget for pointfield?

前端 未结 5 1480
灰色年华
灰色年华 2020-12-16 00:39

Is there a widget for PointField as separate latitude/longitude inputs? Like SplitDateTimeWidget for DateTimeField.

相关标签:
5条回答
  • 2020-12-16 01:04

    Create a custom widget for this, you can get inspired by SelectDateWidget here.

    0 讨论(0)
  • 2020-12-16 01:11

    Here's my working custom field and widget:

    class LatLongWidget(forms.MultiWidget):
        """
        A Widget that splits Point input into two latitude/longitude boxes.
        """
    
        def __init__(self, attrs=None, date_format=None, time_format=None):
            widgets = (forms.TextInput(attrs=attrs),
                       forms.TextInput(attrs=attrs))
            super(LatLongWidget, self).__init__(widgets, attrs)
    
        def decompress(self, value):
            if value:
                return tuple(reversed(value.coords))
            return (None, None)
    
    class LatLongField(forms.MultiValueField):
    
        widget = LatLongWidget
        srid = 4326
    
        default_error_messages = {
            'invalid_latitude' : _('Enter a valid latitude.'),
            'invalid_longitude' : _('Enter a valid longitude.'),
        }
    
        def __init__(self, *args, **kwargs):
            fields = (forms.FloatField(min_value=-90, max_value=90), 
                      forms.FloatField(min_value=-180, max_value=180))
            super(LatLongField, self).__init__(fields, *args, **kwargs)
    
        def compress(self, data_list):
            if data_list:
                # Raise a validation error if latitude or longitude is empty
                # (possible if LatLongField has required=False).
                if data_list[0] in validators.EMPTY_VALUES:
                    raise forms.ValidationError(self.error_messages['invalid_latitude'])
                if data_list[1] in validators.EMPTY_VALUES:
                    raise forms.ValidationError(self.error_messages['invalid_longitude'])
                # SRID=4326;POINT(1.12345789 1.123456789)
                srid_str = 'SRID=%d'%self.srid
                point_str = 'POINT(%f %f)'%tuple(reversed(data_list))
                return ';'.join([srid_str, point_str])
            return None
    
    0 讨论(0)
  • 2020-12-16 01:16

    The answers are great for building your own solution, but if you want a simple solution that looks good and is easy to use, try: http://django-map-widgets.readthedocs.io/en/latest/index.html

    You can:

    • enter coordinates
    • select directly on the map
    • look up items from Google places.

    0 讨论(0)
  • 2020-12-16 01:21

    @Ramast, approaches the same issue in a more pythonic way. I have incorporated the changes to my code, it feels great to always improve.


    This is how I managed -at last- to keep separate fields for lattitude and longitude without having to save them in the database since the values are already saved in the PointField.

    The idea is :

    • If we are inserting a new entry, the latitude and longitude fields will be used to set the PointField
    • If we open an existing PointField entry, it will be used to provide the latitude and longitude values in the relevant FormFields.

    models.py

    from django.contrib.gis.db import models as geomodels
    
    
    class Entry(geomodels.Model):
        point = geomodels.PointField(
            srid=4326,
            blank=True,
            )
    

    admin.py

    from myapp.forms import EntryForm
    from django.contrib import admin
    
    
    class EntryAdmin(admin.ModelAdmin):
        form = EntryForm
    
    
    admin.site.register(Entry, EntryAdmin)
    

    forms.py

    from django import forms
    from myapp.models import Entry
    from django.contrib.gis.geos import Point
    
    
    class MarketEntryForm(forms.ModelForm):
    
        latitude = forms.FloatField(
            min_value=-90,
            max_value=90,
            required=True,
        )
        longitude = forms.FloatField(
            min_value=-180,
            max_value=180,
            required=True,
        )
    
        class Meta(object):
            model = MarketEntry
            exclude = []
            widgets = {'point': forms.HiddenInput()}
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            coordinates = self.initial.get('point', None)
            if isinstance(coordinates, Point):
                self.initial['longitude'], self.initial['latitude'] = coordinates.tuple
    
        def clean(self):
            data = super().clean()
            latitude = data.get('latitude')
            longitude = data.get('longitude')
            point = data.get('point')
            if latitude and longitude and not point:
                data['point'] = Point(longitude, latitude)
            return data
    
    0 讨论(0)
  • 2020-12-16 01:24

    This is based on @rara_tiru's solution but with some improvements

    class GisForm(forms.ModelForm):
        """
        This form can be used by any model that has "coordinates" field.
        It will show a better looking map than the default one
        """
        lat = forms.FloatField(required=False)
        lng = forms.FloatField(required=False)
        coordinates = PointField(widget=CustomPointWidget(), required=False, srid=4326)
    
        def __init__(self, *args, **kwargs):
            super(GisForm, self).__init__(*args, **kwargs)
            coordinates = self.initial.get("coordinates", None)
            if isinstance(coordinates, Point):
                self.initial["lng"], self.initial["lat"] = coordinates.tuple
    
        def clean(self):
            data = super(GisForm, self).clean()
            if "lat" in self.changed_data or "lng" in self.changed_data:
                lat, lng = data.pop("lat", None), data.pop("lng", None)
                data["coordinates"] = Point(lng, lat, srid=4326)
    
            if not (data.get("coordinates") or data.get("lat")):
                raise forms.ValidationError({"coordinates": "Coordinates is required"})
            return data
    

    This will basically allow users to either set location on map or manually put lat/lng values. once saved the map will reflect the specificed lat/lng value

    0 讨论(0)
提交回复
热议问题