How to use the PostGIS aggregate function ST_AsMVT with Django ORM

65 views
Skip to first unread message

Stefan Brand

unread,
Feb 25, 2021, 2:11:01 PM2/25/21
to django...@googlegroups.com
Hello everyone,

this is a repost of
https://stackoverflow.com/questions/65508291/how-to-use-the-postgis-aggregate-function-st-asmvt-with-django-orm (click for nice
formatting).

I'm providing the problem description below as well.

Thank you, for your kind consideration!

Best, Stefan


____

## Problem

I would like to create a Mapbox vector tile (MVT) in Django, using the
ORM.
In SQL (PostgreSQL, PostGIS) the SQL query looks like this for the tile
with zoom=8, x=137, y=83:

```sql
SELECT ST_AsMVT(tile)
FROM (SELECT id, ST_AsMVTGeom(geometry, ST_TileEnvelope(8, 137, 83)) AS
"mvt_geom"
FROM geomodel
WHERE ST_Intersects(geometry, ST_TileEnvelope(8, 137, 83))
) AS tile;
```

`ST_AsMVT` aggregates all rows and the output is a binary Field
(`bytea`) which can be sent as response.

As GeoDjango does not include the specific PostGIS functions I created
custom functions for them:

```python
class TileEnvelope(Func):
function = "ST_TileEnvelope"
arity = 3
output_field = models.GeometryField()


class AsMVTGeom(GeoFunc):
function = "ST_AsMVTGeom"
arity = 2
output_field = models.GeometryField()
```

I managed to create the inner subquery and it works:

```python
tile_envelope = TileEnvelope(8, 137, 83)
tile_geometries =
GeoModel.objects.filter(geometry__intersects=tile_envelope)
tile_geometries_mvt =
tile_geometries.annotate(mvt_geom=AsMVTGeom("geometry", tile_envelope))
tile_geometries_mvt = tile_geometries_mvt.values("id", "mvt_geom")

print(tile_geometries_mvt)
> > <QuerySet [{'id': 165, 'mvt_geom': <Point object at
0x7f552f9d3490>}, {'id': 166, 'mvt_geom': <Point object at
0x7f552f9d3590>},...>
```

Now the last part is missing. I would like run `ST_AsMVT` on
`tile_geometries_mvt`:

```sql
SELECT ST_AsMVT(tile)
FROM 'tile_geometries_mvt' AS tile;
```

## Question

I tried to create a custom Aggregate function for [`ST_AsMVT`](
https://postgis.net/docs/manual-dev/ST_AsMVT.html), but was not
successful.
Normally aggregate functions like `MAX`, for example, expect one column
as input, whereas `ST_AsMVT` expects an `anyelement set row`.

**How can I turn `ST_AsMVT` into a Django `Aggregate` (similar to [this
SO question](https://stackoverflow.com/a/31337612/9778755))?**

I know, that I can use `raw_sql` queries in Django, but this question
is explicitly about solving it with the Django ORM.



Reply all
Reply to author
Forward
0 new messages