使用 django rest framework gis 降低多边形字段的精度

Reduce precision of multi polygon field with django rest framwork gis

我正在使用 django rest gis 加载传单地图,在我的应用程序的顶层,我正在查看世界地图。底图来自 Mapbox。我打电话给我的休息 - api 和 return 概述了应用程序中包含的所有单个国家/地区。目前,return 的 GeoJSON 文件大小为 1.1MB,我要添加更多国家/地区,因此我想减小大小以提高性能。

以下是内容示例:

{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-64.54916992187498,-54.71621093749998],[-64.43881835937495,-54.739355468749984],[-64.22050781249999,-54.721972656249996],[-64.10532226562495,-54.72167968750003],[-64.054931640625,-54.72988281250001],[-64.03242187499995,-54.74238281249998],[-63.881933593750006,-54.72294921875002],[-63.81542968749997,-54.725097656250014],[-63.83256835937499,-54.76796874999995],[-63.97124023437499,-54.810644531250034],[-64.0283203125,-54.79257812499999],[-64.32290039062497,-54.79648437499999],[-64.45327148437497,-54.84033203124995],[-64.50869140625,-54.83994140624996],[-64.637353515625,-54.90253906250001],

文件的大小是点数和这些点的精度的函数。我在想,在保留原始数据的同时减小尺寸的最便捷方法是降低几何点的精度。但是,我对如何做到这一点有点不知所措。我查看了 github 上的文档,但没有找到任何线索。

是否有字段选项可以降低 GeoJSON returned 的精度?或者,还有另一种方法可以实现我想要做的事情吗?

非常感谢。

我最终使用 PostGIS 简化了几何图形,然后将该查询集传递给序列化程序。我开始在模型管理器中创建一个原始查询。

class RegionQueryset(models.query.QuerySet):
    def simplified(self):
        return self.raw(
            "SELECT region_code, country_code, name, slug, ST_SimplifyVW(geom, 0.01) as geom FROM regions_region "
            "WHERE active=TRUE AND region_type = 'Country'"
        )

class RegionsManager (models.GeoManager):
    def get_queryset(self):
        return RegionQueryset(self.model, using=self._db)

    def simplified(self):
        return self.get_queryset().simplified()

观点很简单:

class CountryApiGeoListView(ListAPIView):
    queryset = Region.objects.simplified()
    serializer_class = CountryGeoSerializer

和序列化程序:

class CountryGeoSerializer(GeoFeatureModelSerializer):
    class Meta:
        model = Region
        geo_field = 'geom'
        queryset = Region.objects.filter(active=True)
        fields = ('name', 'slug', 'region_code', 'geom')

经过 运行 一些测试后,我最终选择了 PostGIS function ST_SimplifyVW()

我的数据集包含 20 个国家/地区,其几何图形由 Natural Earth 提供。在没有优化的情况下,geojson 文件的大小为 1.2MB,查询耗时 17 毫秒到 运行,在我的浏览器中加载耗时 1.15 秒。当然,渲染轮廓的质量非常好。然后我尝试使用不同参数的 ST_Simplify() 和 ST_SimplifyVW() 函数。通过这些非常粗略的测试,我决定 ST_SimplifyVW(geom, 0.01)

**Function                 Size   Query time   Load time   Appearance**
None                       1.2MB  17ms         1.15s       Great
ST_Simplify(geom, 0.1)     240K   15.94ms      371ms    Barely Acceptable
ST_Simplify(geom, 0.01)    935k   22.45ms      840ms       Good
ST_SimplifyVW(geom, 0.01)  409K   25.92ms      628ms       Good

我的设置是 Postgres 9.4 和 PostGIS 2.2。 ST_SimplifyVW 未包含在 PostGIS 2.1 中,因此您必须使用 2.2。

您可以通过在序列化期间使用 GeometryField 设置精度来节省一些 space。这是我的代码的摘录,用于对 geodjango GIS tutorial 中定义的相同 WorldBorder 模型进行建模。对于 serializers.py:

from rest_framework_gis.serializers import (
    GeoFeatureModelSerializer, GeometryField)

from .models import WorldBorder


class WorldBorderSerializer(GeoFeatureModelSerializer):
    # set a custom precision for the geometry field
    mpoly = GeometryField(precision=2, remove_duplicates=True)

    class Meta:
        model = WorldBorder
        geo_field = "mpoly"
        fields = (
            "id", "name", "area", "pop2005", "fips", "iso2", "iso3",
            "un", "region", "subregion", "lon", "lat",
        )

使用 mpoly = GeometryField(precision=2) 明确定义精度即可。 remove_duplicates=True 将删除由截断数字生成的相同点。您需要在 Meta class 中保留对几何字段的 geo_field 引用,否则其余框架将无法工作。这是我的 views.py 使用 ViewSet:

查看 GeoJSON 对象的代码
from rest_framework import viewsets, permissions

from .models import WorldBorder
from .serializers import WorldBorderSerializer


class WorldBorderViewSet(viewsets.ModelViewSet):
    queryset = WorldBorder.objects.all()
    serializer_class = WorldBorderSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, )

然而,在节省 space 方面最有效的改进是按照 所述简化几何图形。在这里,我使用序列化器动态计算几何简化:

from rest_framework_gis.serializers import (
    GeoFeatureModelSerializer, GeometrySerializerMethodField)

from .models import WorldBorder


class WorldBorderSerializer(GeoFeatureModelSerializer):
    # in order to simplify poligons on the fly
    simplified_mpoly = GeometrySerializerMethodField()

    def get_simplified_mpoly(self, obj):
        # Returns a new GEOSGeometry, simplified to the specified tolerance
        # using the Douglas-Peucker algorithm. A higher tolerance value implies
        # fewer points in the output. If no tolerance is provided, it
        # defaults to 0.
        return obj.mpoly.simplify(tolerance=0.01, preserve_topology=True)

    class Meta:
        model = WorldBorder
        geo_field = "simplified_mpoly"
        fields = (
            "id", "name", "area", "pop2005", "fips", "iso2", "iso3",
            "un", "region", "subregion", "lon", "lat",
        )

两种解决方案不同,不能合并(参见rest_framework.gis.fields 如何实现)。也许简化几何形状是保持质量和节省 space 的更好解决方案。希望对您有所帮助!