Mar*_*ode 5 python django postgresql postgis vector-tiles
我想使用 ORM 在 Django 中创建一个 Mapbox 矢量瓦片 (MVT)。在 SQL(PostgreSQL、PostGIS)中,对于缩放=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;
Run Code Online (Sandbox Code Playgroud)
ST_AsMVT聚合所有行,输出是bytea可以作为响应发送的二进制字段 ( )。
由于 GeoDjango 不包含特定的 PostGIS 函数,我为它们创建了自定义函数:
class TileEnvelope(Func):
function = "ST_TileEnvelope"
arity = 3
output_field = models.GeometryField()
class AsMVTGeom(GeoFunc):
function = "ST_AsMVTGeom"
arity = 2
output_field = models.GeometryField()
Run Code Online (Sandbox Code Playgroud)
我设法创建了内部子查询并且它有效:
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>},...>
Run Code Online (Sandbox Code Playgroud)
现在缺少最后一部分。我想ST_AsMVT继续tile_geometries_mvt:
SELECT ST_AsMVT(tile)
FROM 'tile_geometries_mvt' AS tile;
Run Code Online (Sandbox Code Playgroud)
我尝试为 创build一个自定义的聚合函数ST_AsMVT,但没有成功。通常聚合函数喜欢MAX,例如,期望一列作为输入,而ST_AsMVT期望的anyelement set row。
我怎样才能ST_AsMVT变成一个 Django Aggregate(类似于这个 SO 问题)?
我知道,我可以raw_sql在 Django 中使用查询,但是这个问题明确地是关于用 Django ORM 来解决它的。
Django 中已经有一个用于提供 Mapbox 矢量切片的解决方案,即 djangorestframework-mvt。根据我的经验,它非常有用。您可以通过 url 查询按字段值进行过滤。
https://github.com/corteva/djangorestframework-mvt
在 Deck.gl 中使用服务图块的小示例:
getCityTileData = () => (
new TileLayer({
stroked: true,
getLineColor: [0, 0, 192],
getFillColor: [140, 170, 180],
filled: false,
getLineWidth: 1,
lineWidthMinPixels: 1,
getTileData: ({ x, y, z }) => {
const mapSource = `${API_URL}/mvt/city?tile=${z}/${x}/${y}&name=ANKARA`;
return fetch(mapSource)
.then(response => response.arrayBuffer())
.then(buffer => {
const tile = new VectorTile(new Protobuf(buffer));
const features = [];
for (const layerName in tile.layers) {
const vectorTileLayer = tile.layers[layerName];
for (let i = 0; i < vectorTileLayer.length; i++) {
const vectorTileFeature = vectorTileLayer.feature(i);
const feature = vectorTileFeature.toGeoJSON(x, y, z);
features.push(feature);
}
}
return features;
});
}
})
Run Code Online (Sandbox Code Playgroud)