API базы данных GeoDjango¶
Пространственные бэкенды¶
В настоящее время GeoDjango предоставляет следующие бэкенды пространственных баз данных:
django.contrib.gis.db.backends.postgis
django.contrib.gis.db.backends.mysql
django.contrib.gis.db.backends.oracle
django.contrib.gis.db.backends.spatialite
Пространственные ограничения MySQL¶
До версии MySQL 5.6.1 пространственные расширения поддерживали только операции с ограничивающими рамками (то, что MySQL называет минимальными ограничивающими прямоугольниками, или MBR). В частности, MySQL не соответствовал стандарту OGC. Django поддерживает пространственные функции, работающие с реальной геометрией, доступные в современных версиях MySQL. Однако, пространственные функции не так богаты, как в других бэкендах, таких как PostGIS.
Предупреждение
Настоящие пространственные индексы (R-деревья) поддерживаются только в таблицах MyISAM на MySQL. [4] Другими словами, при использовании пространственных расширений MySQL вам придется выбирать между быстрым пространственным поиском и целостностью ваших данных - таблицы MyISAM не поддерживают транзакции или ограничения внешнего ключа.
Поддержка растровых изображений¶
RasterField
в настоящее время реализована только для бэкенда PostGIS. Пространственный поиск доступен для растровых полей, но функции пространственной базы данных и агрегаты не реализованы для растровых полей.
Создание и сохранение моделей с полями геометрии¶
Вот пример того, как создать геометрический объект (в предположении модели Zipcode
):
>>> from zipcode.models import Zipcode
>>> z = Zipcode(code=77096, poly='POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))')
>>> z.save()
Объекты GEOSGeometry
могут также использоваться для сохранения геометрических моделей:
>>> from django.contrib.gis.geos import GEOSGeometry
>>> poly = GEOSGeometry('POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))')
>>> z = Zipcode(code=77096, poly=poly)
>>> z.save()
Более того, если GEOSGeometry
находится в другой системе координат (имеет другое значение SRID), чем поле, то он будет неявно преобразован в SRID поля модели с помощью процедуры преобразования пространственной базы данных:
>>> poly_3084 = GEOSGeometry('POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))', srid=3084) # SRID 3084 is 'NAD83(HARN) / Texas Centric Lambert Conformal'
>>> z = Zipcode(code=78212, poly=poly_3084)
>>> z.save()
>>> from django.db import connection
>>> print(connection.queries[-1]['sql']) # printing the last SQL statement executed (requires DEBUG=True)
INSERT INTO "geoapp_zipcode" ("code", "poly") VALUES (78212, ST_Transform(ST_GeomFromWKB('\\001 ... ', 3084), 4326))
Таким образом, параметры геометрии могут быть переданы с помощью объекта GEOSGeometry
, WKT (Well Known Text [1]), HEXEWKB (специфика PostGIS - геометрия WKB в шестнадцатеричном формате [2]) и GeoJSON (см. RFC 7946). По существу, если входные данные не являются объектом GEOSGeometry
, поле геометрии попытается создать экземпляр GEOSGeometry
из входных данных.
Подробнее о создании объектов GEOSGeometry
см. в разделе GEOS tutorial.
Создание и сохранение моделей с растровыми полями¶
При создании растровых моделей поле raster будет неявно преобразовывать входные данные в GDALRaster
с помощью ленивой оценки. Поэтому растровое поле будет принимать любой ввод, который принимается конструктором GDALRaster
.
Вот пример создания растрового объекта из растрового файла volcano.tif
(в предположении модели Elevation
):
>>> from elevation.models import Elevation
>>> dem = Elevation(name='Volcano', rast='/path/to/raster/volcano.tif')
>>> dem.save()
Объекты GDALRaster
также могут быть использованы для сохранения растровых моделей:
>>> from django.contrib.gis.gdal import GDALRaster
>>> rast = GDALRaster({'width': 10, 'height': 10, 'name': 'Canyon', 'srid': 4326,
... 'scale': [0.1, -0.1], 'bands': [{"data": range(100)}]})
>>> dem = Elevation(name='Canyon', rast=rast)
>>> dem.save()
Обратите внимание, что это эквивалентно:
>>> dem = Elevation.objects.create(
... name='Canyon',
... rast={'width': 10, 'height': 10, 'name': 'Canyon', 'srid': 4326,
... 'scale': [0.1, -0.1], 'bands': [{"data": range(100)}]},
... )
Пространственные поиски¶
Типы поиска GeoDjango могут использоваться с любым методом менеджера, например filter()
, exclude()
и т.д. Однако типы поиска, уникальные для GeoDjango, доступны только для пространственных полей.
Фильтры на «обычных» полях (например, CharField
) могут быть объединены с фильтрами на географических полях. Географический поиск принимает геометрические и растровые данные с обеих сторон, и типы данных можно смешивать.
Ниже описана общая структура географических поисков. Полный справочник можно найти в разделе spatial lookup reference.
Геометрический поиск¶
Географические запросы с геометрией имеют следующий общий вид (предполагая модель Zipcode
, используемую в API модели GeoDjango):
>>> qs = Zipcode.objects.filter(<field>__<lookup_type>=<parameter>)
>>> qs = Zipcode.objects.exclude(...)
Например:
>>> qs = Zipcode.objects.filter(poly__contains=pnt)
>>> qs = Elevation.objects.filter(poly__contains=rst)
В этом случае poly
- географическое поле, contains
- тип пространственного поиска, pnt
- параметр (который может быть GEOSGeometry
объектом или строкой GeoJSON , WKT или HEXEWKB), а rst
- GDALRaster
объектом.
Растровый поиск¶
Синтаксис поиска растра аналогичен синтаксису для геометрии. Единственное отличие заключается в том, что индекс полосы может быть указан в качестве дополнительного ввода. Если индекс полосы не указан, то по умолчанию используется первая полоса (индекс 0
). В этом случае синтаксис идентичен синтаксису для поиска геометрии.
Чтобы указать индекс диапазона, дополнительный параметр может быть указан с обеих сторон поиска. С левой стороны для передачи индекса полосы используется синтаксис двойного подчеркивания. С правой стороны может быть указан кортеж из растра и индекса полосы.
Это приводит к следующей общей форме для поиска с использованием растров (предполагая модель Elevation
, используемую в API модели GeoDjango):
>>> qs = Elevation.objects.filter(<field>__<lookup_type>=<parameter>)
>>> qs = Elevation.objects.filter(<field>__<band_index>__<lookup_type>=<parameter>)
>>> qs = Elevation.objects.filter(<field>__<lookup_type>=(<raster_input, <band_index>)
Например:
>>> qs = Elevation.objects.filter(rast__contains=geom)
>>> qs = Elevation.objects.filter(rast__contains=rst)
>>> qs = Elevation.objects.filter(rast__1__contains=geom)
>>> qs = Elevation.objects.filter(rast__contains=(rst, 1))
>>> qs = Elevation.objects.filter(rast__1__contains=(rst, 1))
В левой части примера rast
- поле географического растра, а contains
- тип пространственного поиска. В правой части geom
- это геометрический вход, а rst
- объект GDALRaster
. Индекс полосы по умолчанию равен 0
в первых двух запросах и установлен на 1
в остальных.
Хотя все пространственные поиски могут быть использованы с растровыми объектами с обеих сторон, не все базовые операторы изначально принимают растровые данные. В тех случаях, когда оператор ожидает ввода геометрии, растр автоматически преобразуется в геометрию. Важно помнить об этом при интерпретации результатов поиска.
Тип поддержки растров указан для всех поисков в compatibility table. В настоящее время поиск с использованием растров доступен только для бэкенда PostGIS.
Запросы о расстоянии¶
Вступление¶
Вычисление расстояний с помощью пространственных данных является сложной задачей, поскольку, к сожалению, Земля не плоская. Некоторые запросы о расстоянии с полями в географической системе координат, возможно, придется выражать по-другому из-за ограничений PostGIS. Более подробную информацию см. в разделе Выбор SRID в документации API модели GeoDjango.
Поиск расстояний¶
Доступность: PostGIS, MariaDB, MySQL, Oracle, SpatiaLite, PGRaster (Native)
Доступны следующие виды поиска расстояний:
distance_lt
distance_lte
distance_gt
distance_gte
dwithin
(кроме MariaDB и MySQL)
Примечание
Для измерения, а не для запроса расстояний, используйте функцию Distance
.
Для поиска расстояния используется кортеж параметров, состоящий из:
- Геометрия или растр для расчета; и
- Число или объект
Distance
, содержащий расстояние.
Если используется объект Distance
, то он может быть выражен в любых единицах (генерируемый SQL будет использовать единицы, преобразованные в единицы поля); в противном случае предполагается, что числовые параметры выражены в единицах поля.
Примечание
В PostGIS ST_Distance_Sphere
не ограничивает типы геометрии, с которыми выполняются запросы на географическое расстояние. [3] Однако эти запросы могут занять много времени, так как расстояния по большой окружности должны вычисляться на лету для каждой строки в запросе. Это происходит потому, что пространственный индекс для традиционных геометрических полей не может быть использован.
Для гораздо лучшей производительности при запросах расстояния WGS84, рассмотрите возможность использования geography columns в вашей базе данных вместо этого, потому что они могут использовать свой пространственный индекс в запросах расстояния. Вы можете указать GeoDjango использовать географический столбец, задав geography=True
в определении вашего поля.
Например, допустим, у нас есть модель SouthTexasCity
(из GeoDjango distance tests ) на проецируемой системе координат, действительной для городов в южном Техасе:
from django.contrib.gis.db import models
class SouthTexasCity(models.Model):
name = models.CharField(max_length=30)
# A projected coordinate system (only valid for South Texas!)
# is used, units are in meters.
point = models.PointField(srid=32140)
Тогда запросы о расстоянии могут быть выполнены следующим образом:
>>> from django.contrib.gis.geos import GEOSGeometry
>>> from django.contrib.gis.measure import D # ``D`` is a shortcut for ``Distance``
>>> from geoapp.models import SouthTexasCity
# Distances will be calculated from this point, which does not have to be projected.
>>> pnt = GEOSGeometry('POINT(-96.876369 29.905320)', srid=4326)
# If numeric parameter, units of field (meters in this case) are assumed.
>>> qs = SouthTexasCity.objects.filter(point__distance_lte=(pnt, 7000))
# Find all Cities within 7 km, > 20 miles away, and > 100 chains away (an obscure unit)
>>> qs = SouthTexasCity.objects.filter(point__distance_lte=(pnt, D(km=7)))
>>> qs = SouthTexasCity.objects.filter(point__distance_gte=(pnt, D(mi=20)))
>>> qs = SouthTexasCity.objects.filter(point__distance_gte=(pnt, D(chain=100)))
Растровые запросы работают аналогичным образом, заменяя геометрическое поле point
растровым полем, или объект pnt
растровым объектом, или и тем, и другим. Чтобы указать индекс диапазона растрового входа в правой части, в поиск можно передать 3 кортежа следующим образом:
>>> qs = SouthTexasCity.objects.filter(point__distance_gte=(rst, 2, D(km=7)))
Где полоса с индексом 2 (третья полоса) растра rst
будет использоваться для поиска.
Таблицы совместимости¶
Пространственные поиски¶
В следующей таблице представлена сводная информация о том, какие пространственные поиски доступны для каждого бэкенда базы данных пространственных данных. Поиски PostGIS Raster (PGRaster) делятся на три категории, описанные в raster lookup details: «родная» поддержка N
, двусторонняя «родная» поддержка B
и поддержка преобразования геометрии C
.
Тип поиска | ПостГИС | Oracle | MariaDB | MySQL [5] | SpatiaLite | PGRaster |
---|---|---|---|---|---|---|
bbcontains |
X | X | X | X | N | |
bboverlaps |
X | X | X | X | N | |
contained |
X | X | X | X | N | |
contains |
X | X | X | X | X | B |
contains_properly |
X | B | ||||
coveredby |
X | X | X | B | ||
covers |
X | X | X | B | ||
crosses |
X | X | X | X | C | |
disjoint |
X | X | X | X | X | B |
distance_gt |
X | X | X | X | X | N |
distance_gte |
X | X | X | X | X | N |
distance_lt |
X | X | X | X | X | N |
distance_lte |
X | X | X | X | X | N |
dwithin |
X | X | X | B | ||
equals |
X | X | X | X | X | C |
exact |
X | X | X | X | X | B |
intersects |
X | X | X | X | X | B |
isvalid |
X | X | X (≥ 5.7.5) | X (LWGEOM) | ||
overlaps |
X | X | X | X | X | B |
relate |
X | X | X | X | C | |
same_as |
X | X | X | X | X | B |
touches |
X | X | X | X | X | B |
within |
X | X | X | X | X | B |
left |
X | C | ||||
right |
X | C | ||||
overlaps_left |
X | B | ||||
overlaps_right |
X | B | ||||
overlaps_above |
X | C | ||||
overlaps_below |
X | C | ||||
strictly_above |
X | C | ||||
strictly_below |
X | C |
Функции базы данных¶
В следующей таблице приведено краткое описание функций базы данных, специфичных для географии, доступных в каждом пространственном бэкенде.
Функция | ПостГИС | Oracle | MariaDB | MySQL | SpatiaLite |
---|---|---|---|---|---|
Area |
X | X | X | X | X |
AsGeoJSON |
X | X | X (≥ 10.2.4) | X (≥ 5.7.5) | X |
AsGML |
X | X | X | ||
AsKML |
X | X | |||
AsSVG |
X | X | |||
AsWKB |
X | X | X | X | X |
AsWKT |
X | X | X | X | X |
Azimuth |
X | X (LWGEOM) | |||
BoundingCircle |
X | X | |||
Centroid |
X | X | X | X | X |
Difference |
X | X | X | X | X |
Distance |
X | X | X | X | X |
Envelope |
X | X | X | X | X |
ForcePolygonCW |
X | X | |||
GeoHash |
X | X (≥ 5.7.5) | X (LWGEOM) | ||
Intersection |
X | X | X | X | X |
IsValid |
X | X | X (≥ 5.7.5) | X (LWGEOM) | |
Length |
X | X | X | X | X |
LineLocatePoint |
X | X | |||
MakeValid |
X | X (LWGEOM) | |||
MemSize |
X | ||||
NumGeometries |
X | X | X | X | X |
NumPoints |
X | X | X | X | X |
Perimeter |
X | X | X | ||
PointOnSurface |
X | X | X | X | |
Reverse |
X | X | X | ||
Scale |
X | X | |||
SnapToGrid |
X | X | |||
SymDifference |
X | X | X | X | X |
Transform |
X | X | X | ||
Translate |
X | X | |||
Union |
X | X | X | X | X |
Агрегатные функции¶
В следующей таблице приведена сводная информация о том, какие специфические для ГИС агрегатные функции доступны в каждом пространственном бэкенде. Обратите внимание, что MySQL не поддерживает ни один из этих агрегатов и поэтому исключен из таблицы.
A | ПостГИС | Oracle | SpatiaLite |
---|---|---|---|
Collect |
X | X | |
Extent |
X | X | X |
Extent3D |
X | ||
MakeLine |
X | X | |
Union |
X | X | X |
Сноски
[1] | See Open Geospatial Consortium, Inc., OpenGIS Simple Feature Specification For SQL, Document 99-049 (May 5, 1999), at Ch. 3.2.5, p. 3-11 (SQL Textual Representation of Geometry). |
[2] | **Смотрите* PostGIS EWKB, EWKT и канонические формы, документация PostGIS в гл. 4.1.2. |
[3] | Смотрите Документацию PostGIS <https://postgis.net/docs/ST_DistanceSphere.html>`_ по ``ST_DistanceSphere . |
[4] | **Смотрите* Создание пространственных индексов в Справочном руководстве MySQL:
|
[5] | Более подробную информацию смотрите в разделе Пространственные ограничения MySQL. |