4

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

非常感谢。

4

2 回答 2

4

我最终使用 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 函数 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。

于 2016-08-16T18:29:25.753 回答
1

GeometryField您可以通过在序列化期间设置精度来节省一些空间。这是我的代码摘录,用于对geodjango GIS 教程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代码查看GeoJSON对象的代码ViewSet

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

于 2020-07-01T17:40:52.223 回答