使用django rest framwork gis降低多边形场的精度

时间:2016-07-20 01:36:53

标签: django-rest-framework geojson django-rest-framework-gis

我正在使用django rest gis加载传单地图,在我的应用程序的顶层我正在查看世界地图。底图来自Mapbox。我打电话给我的rest-api并返回应用程序中包含的所有个别国家/地区的大纲。目前,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的精度?或者,还有另一种方法可以实现我的目标吗?

非常感谢。

2 个答案:

答案 0 :(得分:1)

我最终使用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,查询运行时间为17ms,浏览器加载时间为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。

答案 1 :(得分:0)

您可以通过在序列化期间使用GeometryField设置精度来节省一些空间。这是我的代码的摘录,用于对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将删除由于截断数字而产生的相同点。您需要在geo_field类中保留对几何字段的Meta引用,否则其余框架将无法工作。这是我的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, )

然而,节省空间最有效的改进是简化几何结构,如geoAndrew所述。在这里,我使用序列化器动态计算几何简化:

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 的实现方式)。简化几何形状可能是保留质量和节省空间的更好解决方案。希望对您有帮助!