【问题标题】:Reduce precision of multi polygon field with django rest framwork gis使用 django rest framework gis 降低多面域的精度
【发布时间】:2016-11-23 01:58:49
【问题描述】:

我正在使用 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 精度的字段选项?或者,有没有另一种方法来实现我想要做的事情?

非常感谢。

【问题讨论】:

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


    【解决方案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,查询运行时间为 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。

    【讨论】:

      【解决方案2】:

      您可以通过在序列化期间使用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 将删除由截断数字生成的相同点。您需要在 Meta 类中保留对几何字段的 geo_field 引用,否则其余框架将不起作用。这是我的views.py 代码,用于查看使用ViewSetGeoJSON 对象:

      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 是如何实现的)。也许简化几何是保持质量和节省空间的更好解决方案。希望对您有所帮助!

      【讨论】:

        猜你喜欢
        • 2015-03-24
        • 1970-01-01
        • 1970-01-01
        • 2017-12-05
        • 1970-01-01
        • 2011-07-16
        • 2017-08-11
        • 2018-12-28
        • 2022-11-02
        相关资源
        最近更新 更多