Django Rest Framework的地理扩展
项目描述
django-rest-framework-gis
Django Rest Framework 的地理信息扩展 - 邮件列表。
从 PyPI 安装最新稳定版
pip install djangorestframework-gis
安装开发版
pip install https://github.com/openwisp/django-rest-framework-gis/tarball/master
配置
在 settings.INSTALLED_APPS 中添加 rest_framework_gis,在 rest_framework 之后
INSTALLED_APPS = [
# ...
'rest_framework',
'rest_framework_gis',
# ...
]
与 DRF、Django 和 Python 的兼容性
DRF-gis 版本 |
DRF 版本 |
Django 版本 |
Python 版本 |
1.1.x |
3.12 至 3.15 |
3.2, 4.2 至 5.1 |
3.8 至 3.12 |
1.0.x |
3.10 至 3.13 |
2.2 至 4.0 |
3.6 至 3.9 |
0.18.x |
3.10 至 3.13 |
2.2 至 4.0 |
3.6 至 3.9 |
0.17.x |
3.10 至 3.12 |
2.2 至 3.1 |
3.6 至 3.8 |
0.16.x |
3.10 |
2.2 至 3.1 |
3.6 至 3.8 |
0.15.x |
3.10 |
1.11, 2.2 至 3.0 |
3.5 至 3.8 |
0.14.x |
3.3 至 3.9 |
1.11 至 2.1 |
3.4 至 3.7 |
0.13.x |
3.3 至 3.8 |
1.11 至 2.0 |
2.7 至 3.6 |
0.12.x |
3.1 至 3.7 |
1.11 至 2.0 |
2.7 至 3.6 |
0.11.x |
3.1 至 3.6 |
1.7 至 1.11 |
2.7 至 3.6 |
0.10.x |
3.1 至 3.3 |
1.7 至 1.9 |
2.7 至 3.5 |
0.9.6 |
3.1 至 3.2 |
1.5 至 1.8 |
2.6 至 3.5 |
0.9.5 |
3.1 至 3.2 |
1.5 至 1.8 |
2.6 至 3.4 |
0.9.4 |
3.1 至 3.2 |
1.5 至 1.8 |
2.6 至 3.4 |
0.9.3 |
3.1 |
1.5 至 1.8 |
2.6 至 3.4 |
0.9.2 |
3.1 |
1.5 至 1.8 |
2.6 至 3.4 |
0.9.1 |
3.1 |
1.5 至 1.8 |
2.6 至 3.4 |
0.9 |
3.1 |
1.5 至 1.8 |
2.6, 2.7, 3.3, 3.4 |
0.9 |
3.1 |
1.5 至 1.8 |
2.6, 2.7, 3.3, 3.4 |
0.9 |
3.1 |
1.5 至 1.8 |
2.6, 2.7, 3.3, 3.4 |
0.8.2 |
3.0.4 至 3.1.1 |
1.5 至 1.8 |
2.6, 2.7, 3.3, 3.4 |
0.8.1 |
3.0.4 至 3.1.1 |
1.5 至 1.8 |
2.6, 2.7, 3.3, 3.4 |
0.8 |
3.0.4 |
1.5 至 1.7 |
2.6, 2.7, 3.3, 3.4 |
0.7 |
2.4.3 |
1.5 至 1.7 |
2.6, 2.7, 3.3, 3.4 |
0.6 |
2.4.3 |
1.5 至 1.7 |
2.6, 2.7, 3.3, 3.4 |
0.5 |
从 2.3.14 至 2.4.2 |
1.5 至 1.7 |
2.6, 2.7, 3.3, 3.4 |
0.4 |
从 2.3.14 至 2.4.2 |
1.5 至 1.7 |
2.6, 2.7, 3.3, 3.4 |
0.3 |
从 2.3.14 至 2.4.2 |
1.5, 1.6 |
2.6, 2.7 |
0.2 |
从 2.2.2 至 2.3.13 |
1.5, 1.6 |
2.6, 2.7 |
字段
GeometryField
提供 GeometryField,它是 Django Rest Framework (以下简称 DRF) 的子类 WritableField。该字段处理 GeoDjango 几何字段,为 GeoJSON 输入/输出提供自定义的 to_native 和 from_native 方法。
此字段接受三个可选参数
precision:通过 Python 内置的 round() 函数(文档)传递坐标,将值四舍五入到提供的精度级别。例如,一个经纬度为 [51.0486, -114.0708] 的点通过 GeometryField(precision=2) 传递,将返回一个经纬度为 [51.05, -114.07] 的点。
remove_duplicates:从线和多边形几何中删除重复的坐标。当与 precision 参数一起使用时,这特别有用,因为坐标的重复概率会随着坐标精度的降低而增加。
auto_bbox:如果为 True,GeoJSON 对象将包含一个 边界框,即包围几何形状的最小可能矩形。
注意:虽然 precision 和 remove_duplicates 设计用于减小 API 响应的字节大小,但它们也会增加渲染响应所需的处理时间。对于小的 GeoJSON 响应,这可能是微不足道的,但对于大型响应可能会成为问题。
自 0.9.3 以来新增:无需在序列化器中显式定义此字段,它将在 rest_framework_gis.apps.AppConfig.ready() 初始化期间自动映射。
GeometrySerializerMethodField
提供了一种名为 GeometrySerializerMethodField 的字段,它是 DRF SerializerMethodField 的子类,用于处理通过序列化方法计算并用作 geo_field 的值。请参见下面的示例。
序列化器
GeoModelSerializer (已废弃)
已废弃,将在 1.0 版本中删除:从 0.9.3 版本开始,使用此序列化器不再需要,如果将 rest_framework_gis 添加到 settings.INSTALLED_APPS,则序列化将直接与 DRF 一起工作。请参考 问题 #156。
提供了一种名为 GeoModelSerializer 的序列化器,它是 DRF ModelSerializer 的子类。此序列化器更新 field_mapping 字典,包括 GeoDjango 几何字段的映射到上面的 GeometryField。
例如,以下模型
class Location(models.Model):
"""
A model which holds information about a particular location
"""
address = models.CharField(max_length=255)
city = models.CharField(max_length=100)
state = models.CharField(max_length=100)
point = models.PointField()
默认情况下,DRF ModelSerializer ver < 0.9.3 将输出
{
"id": 1,
"address": "742 Evergreen Terrace",
"city": "Springfield",
"state": "Oregon",
"point": "POINT(-123.0208 44.0464)"
}
相比之下,GeoModelSerializer 将输出
{
"id": 1,
"address": "742 Evergreen Terrace",
"city": "Springfield",
"state": "Oregon",
"point": {
"type": "Point",
"coordinates": [-123.0208, 44.0464],
}
}
注意:对于 ver>=0.9.3:如果;
rest_framework_gis 设置在 settings.INSTALLED_APPS 或
序列化器中的字段明确设置为 GeometryField,则 DRF 模型序列化器将给出与上面相同的输出。
GeoFeatureModelSerializer
GeoFeatureModelSerializer 是 rest_framework.ModelSerializer 的子类,它将以与 GeoJSON 兼容的格式输出数据。使用上面的示例,GeoFeatureModelSerializer 将输出
{
"id": 1,
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-123.0208, 44.0464],
},
"properties": {
"address": "742 Evergreen Terrace",
"city": "Springfield",
"state": "Oregon"
}
}
如果您正在序列化对象列表,GeoFeatureModelSerializer 将创建一个 FeatureCollection
{
"type": "FeatureCollection",
"features": [
{
"id": 1
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-123.0208, 44.0464],
},
"properties": {
"address": "742 Evergreen Terrace",
"city": "Springfield",
"state": "Oregon",
}
}
{
"id": 2,
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-123.0208, 44.0489],
},
"properties": {
"address": "744 Evergreen Terrace",
"city": "Springfield",
"state": "Oregon"
}
}
}
指定几何字段:“geo_field”
GeoFeatureModelSerializer 需要您定义一个 geo_field 以将其序列化为“几何”。例如
from rest_framework_gis.serializers import GeoFeatureModelSerializer
class LocationSerializer(GeoFeatureModelSerializer):
""" A class to serialize locations as GeoJSON compatible data """
class Meta:
model = Location
geo_field = "point"
# you can also explicitly declare which fields you want to include
# as with a ModelSerializer.
fields = ('id', 'address', 'city', 'state')
如果您的模型没有几何字段,可以将 geo_field 设置为 None,并将生成一个空几何。
将 GeometrySerializerMethodField 用作“geo_field”
geo_field 也可以是 GeometrySerializerMethodField 的实例。在这种情况下,您可以在序列化过程中计算其值。例如
from django.contrib.gis.geos import Point
from rest_framework_gis.serializers import GeoFeatureModelSerializer, GeometrySerializerMethodField
class LocationSerializer(GeoFeatureModelSerializer):
""" A class to serialize locations as GeoJSON compatible data """
# a field which contains a geometry value and can be used as geo_field
other_point = GeometrySerializerMethodField()
def get_other_point(self, obj):
return Point(obj.point.lat / 2, obj.point.lon / 2)
class Meta:
model = Location
geo_field = 'other_point'
geo_field 的序列化器也可以返回 None 值,这将转换为 geojson 几何字段的 null 值。
指定 ID:“id_field”
模型的 主键(通常是“id”属性)自动用作每个 GeoJSON Feature 对象 的 id 字段。
默认行为遵循 GeoJSON RFC,但可以通过将 id_field 设置为 False 来禁用。
from rest_framework_gis.serializers import GeoFeatureModelSerializer
class LocationSerializer(GeoFeatureModelSerializer):
class Meta:
model = Location
geo_field = "point"
id_field = False
fields = ('id', 'address', 'city', 'state')
id_field 也可以设置为使用模型中的某个其他唯一字段,例如:slug
from rest_framework_gis.serializers import GeoFeatureModelSerializer
class LocationSerializer(GeoFeatureModelSerializer):
class Meta:
model = Location
geo_field = 'point'
id_field = 'slug'
fields = ('slug', 'address', 'city', 'state')
边界框:“auto_bbox”和“bbox_geo_field”
GeoJSON 规范允许一个特征包含一个 特征的边界框。 GeoFeatureModelSerializer 允许两种不同的方式来填充此属性。第一种是使用 geo_field 来计算特征的边界框。这仅允许 REST 客户端进行读取访问,可以使用 auto_bbox 实现。示例
from rest_framework_gis.serializers import GeoFeatureModelSerializer
class LocationSerializer(GeoFeatureModelSerializer):
class Meta:
model = Location
geo_field = 'geometry'
auto_bbox = True
第二种方法使用bbox_geo_field来指定模型的一个额外的GeometryField,该字段将用于计算边界框。这允许边界框与要素几何形状的精确范围不同。此外,这还使得REST客户端可以读写访问。客户端发送的边界框将被保存为多边形。示例
from rest_framework_gis.serializers import GeoFeatureModelSerializer
class LocationSerializer(GeoFeatureModelSerializer):
class Meta:
model = BoxedLocation
geo_field = 'geometry'
bbox_geo_field = 'bbox_geometry'
自定义GeoJSON属性源
在GeoJSON中,每个要素都可以有一个包含要素属性的properties成员。默认情况下,此字段填充了来自您的Django模型的属性,不包括id、几何和边界框字段。您可以覆盖此行为并实现一个自定义的properties成员源。
以下示例展示了如何使用PostgreSQL HStore字段作为properties成员的源
# models.py
class Link(models.Model):
"""
Metadata is stored in a PostgreSQL HStore field, which allows us to
store arbitrary key-value pairs with a link record.
"""
metadata = HStoreField(blank=True, null=True, default=dict)
geo = models.LineStringField()
objects = models.GeoManager()
# serializers.py
class NetworkGeoSerializer(GeoFeatureModelSerializer):
class Meta:
model = models.Link
geo_field = 'geo'
auto_bbox = True
def get_properties(self, instance, fields):
# This is a PostgreSQL HStore field, which django maps to a dict
return instance.metadata
def unformat_geojson(self, feature):
attrs = {
self.Meta.geo_field: feature["geometry"],
"metadata": feature["properties"]
}
if self.Meta.bbox_geo_field and "bbox" in feature:
attrs[self.Meta.bbox_geo_field] = Polygon.from_bbox(feature["bbox"])
return attrs
当序列化器渲染GeoJSON时,它会为数据库中的每个对象调用get_properties方法。此函数应返回一个包含要素属性的字典。在HStore字段的情况下,此函数很容易实现。
反之亦然也是必要的:将GeoJSON格式的结构映射到模型的属性。此任务由unformat_geojson完成。它应返回一个字典,其中包含您的模型属性作为键,以及从GeoJSON要素数据中检索到的对应值。
分页
我们提供了一个GeoJsonPagination类。
GeoJsonPagination
基于rest_framework.pagination.PageNumberPagination。
代码示例
from rest_framework_gis.pagination import GeoJsonPagination
# --- other omitted imports --- #
class GeojsonLocationList(generics.ListCreateAPIView):
# -- other omitted view attributes --- #
pagination_class = GeoJsonPagination
示例结果响应(仅截取一个元素,而不是10个)
{
"type": "FeatureCollection",
"count": 25,
"next": "http://localhost:8000/geojson/?page=2",
"previous": null,
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
42.0,
50.0
]
},
"properties": {
"name": "test"
}
}
]
}
过滤器
注意:此功能已测试到django-filter 1.0。
我们提供了一个用于与django_filter一起使用的GeometryFilter字段以及GeoFilterSet。您只需在查询字符串中提供GEOSGeometry支持的文本类型之一。默认情况下,这包括WKT、HEXEWKB、WKB(在缓冲区中)和GeoJSON。
GeometryFilter
from rest_framework_gis.filterset import GeoFilterSet
from rest_framework_gis.filters import GeometryFilter
from django_filters import filters
class RegionFilter(GeoFilterSet):
slug = filters.CharFilter(name='slug', lookup_expr='istartswith')
contains_geom = GeometryFilter(name='geom', lookup_expr='contains')
class Meta:
model = Region
然后我们可以在URL中使用GeoJSON进行过滤,并将执行一个__contains几何查找,例如:/region/?contains_geom={ "type": "Point", "coordinates": [ -123.26436996459961, 44.564178042345375 ] }。
GeoFilterSet
GeoFilterSet提供了一个与django_filter兼容的FilterSet,该FilterSet将自动为GeometryFields创建GeometryFilters。
InBBoxFilter
提供了一个InBBoxFilter,它是DRF BaseFilterBackend的子类。将查询集过滤为仅包含特定边界框内的实例。
views.py
from rest_framework_gis.filters import InBBoxFilter
class LocationList(ListAPIView):
queryset = models.Location.objects.all()
serializer_class = serializers.LocationSerializer
bbox_filter_field = 'point'
filter_backends = (InBBoxFilter,)
bbox_filter_include_overlapping = True # Optional
然后我们可以在URL中使用边界框格式(最小经度,最小纬度,最大经度,最大纬度)进行过滤,并可以搜索边界框内的实例,例如:/location/?in_bbox=-90,29,-89,35。
默认情况下,InBBoxFilter仅返回完全位于指定边界框内的实例。要包含与边界框重叠的实例,请在您的视图中包含bbox_filter_include_overlapping = True。
注意:如果您使用其他过滤器,您还需要在视图中包含您的其他过滤器后端。例如
filter_backends = (InBBoxFilter, DjangoFilterBackend,)
TMSTileFilter
提供了一个TMSTileFilter,它是InBBoxFilter的子类。它将查询集过滤为仅包含由TMS瓦片地址定义的边界框内的实例。
views.py
from rest_framework_gis.filters import TMSTileFilter
class LocationList(ListAPIView):
queryset = models.Location.objects.all()
serializer_class = serializers.LocationSerializer
bbox_filter_field = 'point'
filter_backends = (TMSTileFilter,)
bbox_filter_include_overlapping = True # Optional
然后我们可以在URL中进行过滤,使用TMS瓦片地址的缩放/x/y格式,例如:/location/?tile=8/100/200,这相当于对边界框(-39.37500,-71.07406,-37.96875,-70.61261)进行过滤。
有关配置选项的更多信息,请参阅InBBoxFilter。
请注意,瓦片地址从上左角开始,而不是某些实现中使用的下左角原点。
DistanceToPointFilter
提供了一个DistanceToPointFilter,它是DRF BaseFilterBackend的子类。它将查询集过滤为仅包含在给定点一定距离内的实例。
views.py
from rest_framework_gis.filters import DistanceToPointFilter
class LocationList(ListAPIView):
queryset = models.Location.objects.all()
serializer_class = serializers.LocationSerializer
distance_filter_field = 'geometry'
filter_backends = (DistanceToPointFilter,)
然后我们可以在URL中进行过滤,使用以(lon, lat)格式表示的距离和点。距离可以用米或度表示。
例如:/location/?dist=4000&point=-122.4862,37.7694&format=json,这相当于在点(-122.4862, 37.7694)周围4000米内进行过滤。
默认情况下,DistanceToPointFilter会将URL中的‘distance’直接传递给数据库进行搜索。效果取决于使用的数据库srid。如果地理数据以米为索引(srid 3875,即900913),则可以直接传递米距离,无需转换。对于如srid 4326的经纬度数据库,它以度为索引,‘distance’将被解释为度。将标志‘distance_filter_convert_meters’设置为‘True’以将输入距离从米转换为度。这种转换是近似的,且在纬度>60度时的误差大于25%。
DistanceToPointOrderingFilter
提供了一个DistanceToPointOrderingFilter,适用于Django >= 3.0,它是DistanceToPointFilter的子类。根据到给定点的距离对查询集进行排序,从最近的点到最远的点。
views.py
from rest_framework_gis.filters import DistanceToPointOrderingFilter
class LocationList(ListAPIView):
queryset = models.Location.objects.all()
serializer_class = serializers.LocationSerializer
distance_ordering_filter_field = 'geometry'
filter_backends = (DistanceToPointOrderingFilter,)
然后我们可以通过在URL中传递以(lon, lat)格式表示的点来对结果进行排序。
例如:/location/?point=-122.4862,37.7694&format=json将根据点(-122.4862, 37.7694)的距离对结果进行排序。
我们还可以通过传递order=desc来反转结果的顺序:/location/?point=-122.4862,37.7694&order=desc&format=json
Schema生成
注意:仅当DRF >= 3.12时才支持Schema生成。
最简单的方法是,将DEFAULT_SCHEMA_CLASS更改为rest_framework_gis.schema.GeoFeatureAutoSchema
REST_FRAMEWORK = {
...
'DEFAULT_SCHEMA_CLASS': 'rest_framework_gis.schema.GeoFeatureAutoSchema',
...
}
如果您不想更改默认的schema生成类
运行测试
必需的设置
您需要GeoDjango支持的其中一个空间数据库服务器,并为测试创建一个数据库。
以下可用于PostgreSQL
createdb django_restframework_gis
psql -U postgres -d django_restframework_gis -c "CREATE EXTENSION postgis"
您可能需要根据您的数据库配置调整DB设置。您可以将文件local_settings.example.py复制到local_settings.py,并更改其中的DATABASES和/或INSTALLED_APPS指令。
这应该允许您运行测试。
作为参考,以下步骤将为贡献项目设置开发环境
创建一个名为“django_restframework_gis”的空间数据库
创建local_settings.py,例如:cp local_settings.example.py local_settings.py
根据您的数据库设置,调整DATABASES配置指令
取消注释INSTALLED_APPS
运行python manage.py syncdb
运行python manage.py collectstatic
运行python manage.py runserver
使用tox
运行测试的推荐方法是使用tox,可以使用pip install tox进行安装。
您可以使用tox -l列出可用的环境,然后例如使用以下命令以Python 3.6和Django 1.11运行所有测试
tox -e py36-django111
默认情况下,使用Django的测试运行器,但tox的envlist有一个变体可以使用pytest(使用-pytest后缀)。
您可以将可选参数传递给测试运行器,如下所示
tox -e py36-django111-pytest -- -k test_foo
手动运行测试
如果您想手动/不使用tox运行测试,请参考tox.ini文件以获取参考/帮助。
要在docker中运行测试,请使用
docker-compose build
docker-compose run --rm test
运行QA检查
安装测试需求
pip install -r requirements-test.txt
使用以下链接根据我们的编码风格约定重新格式化代码https://openwisp.io/docs/developer/contributing.html#coding-style-conventions
openwisp-qa-format
使用以下命令运行QA检查
./run-qa-checks
在docker测试中,QA检查会自动执行。
贡献
加入Django REST Framework GIS邮件列表并宣布您的意图
叉取此仓库
编写代码
为您的代码编写测试
确保所有测试通过
确保测试覆盖率不低于90%
记录您的更改
发送拉取请求
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。
源分布
构建分布
哈希值 for djangorestframework_gis-1.1-py2.py3-none-any.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | fd8631c2c10208df5f05ee297ce333aaa9a794acd654420207f72e8f9acd59ea |
|
MD5 | 1a27fc9c1b2112b885ff32b84228f3bd |
|
BLAKE2b-256 | f4c409f9d899e827fd7fbed450962c9b6c67d501c06f7e29e0abee30c13e53d0 |