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 поддерживают только операции с ограничивающими рамками (то, что MySQL называет минимальными ограничивающими прямоугольниками, или MBR). В частности, MySQL does not conform to the OGC standard:

В настоящее время MySQL не реализует эти функции [Contains, Crosses, Disjoint, Intersects, Overlaps, Touches, Within] в соответствии со спецификацией. Те из них, которые реализованы, возвращают тот же результат, что и соответствующие функции на основе MBR.

Другими словами, хотя пространственный поиск, такой как contains, доступен в GeoDjango при использовании MySQL, возвращаемые результаты действительно эквивалентны тем, которые были бы возвращены при использовании bbcontains на другом пространственном бэкенде.

Предупреждение

Настоящие пространственные индексы (R-деревья) поддерживаются только в таблицах MyISAM на MySQL. [5] Другими словами, при использовании пространственных расширений 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 [3]. По существу, если входные данные не являются объектом 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, MySQL, Oracle, SpatiaLite, PGRaster (Native)

Доступны следующие виды поиска расстояний:

Примечание

Для измерения, а не для запроса расстояний, используйте функцию Distance.

Для поиска расстояния используется кортеж параметров, состоящий из:

  1. Геометрия или растр для расчета; и
  2. Число или объект Distance, содержащий расстояние.

Если используется объект Distance, то он может быть выражен в любых единицах (генерируемый SQL будет использовать единицы, преобразованные в единицы поля); в противном случае предполагается, что числовые параметры выражены в единицах поля.

Примечание

В PostGIS ST_Distance_Sphere не ограничивает типы геометрии, с которыми выполняются запросы на географическое расстояние. [4] Однако эти запросы могут занять много времени, так как расстояния по большой окружности должны вычисляться на лету для каждой строки в запросе. Это происходит потому, что пространственный индекс для традиционных геометрических полей не может быть использован.

Для гораздо лучшей производительности при запросах расстояния 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 MySQL [6] SpatiaLite PGRaster
bbcontains X   X X N
bboverlaps X   X X N
contained X   X X N
contains X X X X B
contains_properly X       B
coveredby X X   X B
covers X X   X B
crosses X     X C
disjoint X X X X B
distance_gt X X X X N
distance_gte X X X X N
distance_lt X X X X N
distance_lte X X X X N
dwithin X X   X B
equals X X X X C
exact X X X X B
intersects X X X X B
isvalid X X X (≥ 5.7.5) X (LWGEOM)  
overlaps X X X X B
relate X X   X C
same_as X X X X B
touches X X X X B
within 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 MySQL SpatiaLite
Area X X X X
AsGeoJSON X   X (≥ 5.7.5) X
AsGML X X   X
AsKML X     X
AsSVG X     X
Azimuth X     X (LWGEOM)
BoundingCircle X X    
Centroid X X X X
Difference X X X X
Distance X X X X
Envelope X X X X
ForcePolygonCW X     X
ForceRHR X      
GeoHash X   X (≥ 5.7.5) X (LWGEOM)
Intersection X X X X
IsValid X X X (≥ 5.7.5) X (LWGEOM)
Length X X X X
LineLocatePoint X     X
MakeValid X     X (LWGEOM)
MemSize X      
NumGeometries X X X X
NumPoints X X X X
Perimeter X X   X
PointOnSurface X X   X
Reverse X X   X
Scale X     X
SnapToGrid X     X
SymDifference X X X X
Transform X X   X
Translate X     X
Union X X X X

Агрегатные функции

В следующей таблице приведена сводная информация о том, какие специфические для ГИС агрегатные функции доступны в каждом пространственном бэкенде. Обратите внимание, что MySQL не поддерживает ни один из этих агрегатов и поэтому исключен из таблицы.

Агрегирование ПостГИС Oracle SpatiaLite
Collect X   X
Extent X X X
Extent3D X    
MakeLine X   X
Union X X X

Сноски

[1]**См.* 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]См. Howard Butler, Martin Daly, Allan Doyle, Tim Schaub, & Christopher Schmidt, The GeoJSON Format Specification, Revision 1.0 (June 16, 2008).
[4]Смотрите Документацию PostGIS <https://postgis.net/docs/ST_DistanceSphere.html>`_ по ``ST_DistanceSphere.
[5]

**Смотрите* Создание пространственных индексов в Справочном руководстве MySQL:

Для таблиц MyISAM SPATIAL INDEX создает индекс R-дерева. Для движков хранения, поддерживающих непространственную индексацию пространственных столбцов, движок создает индекс B-дерева. Индекс B-дерева для пространственных значений будет полезен для поиска точных значений, но не для сканирования диапазона.
[6]Более подробную информацию смотрите в разделе Пространственные ограничения MySQL.
Вернуться на верх