Spec-Zone.ru › Django 1.10

Учебник GeoDjango

Введение

GeoDjango — это модуль contrib для Django, который превращает его в географический веб-фреймворк мирового класса. GeoDjango стремится максимально упростить создание географических веб-приложений, таких как сервисы на основе местоположения. Его функции включают:

  • Поля модели Django для геометрий OGC и растровых данных.
  • Расширения ORM Django для запросов и обработки пространственных данных.
  • Слабосвязанные, высокоуровневые Python-интерфейсы для операций с геометрией и растром ГИС, а также обработки данных в различных форматах.
  • Редактирование полей геометрии в админ-панели.

Этот учебник предполагает знакомство с Django; поэтому, если вы новичок в Django, пожалуйста, прочитайте обычный учебник, чтобы сначала ознакомиться с Django.

Примечание

GeoDjango имеет дополнительные требования помимо требований Django — см. документацию по установке для получения более подробной информации.

Этот учебник проведет вас через создание географического веб-приложения для просмотра границ мира. [1] Часть кода, используемого в этом учебнике, взята из проекта GeoDjango basic apps и/или вдохновлена им. [2]

Примечание

Для пошаговых инструкций следуйте разделам учебника последовательно.

Настройка

Создание пространственной базы данных

Обычно особая настройка не требуется, поэтому вы можете создать базу данных так же, как и для любого другого проекта. Мы предоставляем некоторые советы для выбранных баз данных:

  • Установка PostGIS
  • Установка SpatiaLite

Создание нового проекта

Используйте стандартную django-admin команду для создания проекта под названием geodjango.

$ django-admin startproject geodjango

Это инициализирует новый проект. Теперь создайте world приложение Django в рамках проекта geodjango.

$ cd geodjango
$ python manage.py startapp world

Настройка settings.py

Настройки проекта geodjango хранятся в файле geodjango/settings.py. Измените настройки подключения к базе данных, чтобы они соответствовали вашей настройке:

DATABASES = {
    'default': {
         'ENGINE': 'django.contrib.gis.db.backends.postgis',
         'NAME': 'geodjango',
         'USER': 'geo',
    },
}

Кроме того, измените настройку INSTALLED_APPS, чтобы включить django.contrib.admin, django.contrib.gis и world (ваше недавно созданное приложение):

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.gis',
    'world',
]

Географические данные

Границы мира

Данные границ мира доступны в этом архиве ZIP. Создайте data директорию в приложении world, скачайте данные границ мира и распакуйте их. На платформах GNU/Linux используйте следующие команды:

$ mkdir world/data
$ cd world/data
$ wget http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
$ unzip TM_WORLD_BORDERS-0.3.zip
$ cd ../..

Архив ZIP с границами мира содержит набор файлов данных, коллективно известных как ESRI Shapefile, один из самых популярных форматов пространственных данных. При распаковке набор данных границ мира включает файлы с следующими расширениями:

  • .shp: Содержит векторные данные для геометрий границ мира.
  • .shx: Файл пространственного индекса для геометрий, хранящихся в .shp.
  • .dbf: Файл базы данных для хранения негеометрических атрибутивных данных (например, целочисленных и символьных полей).
  • .prj: Содержит информацию о пространственной системе координат для географических данных, хранящихся в shapefile.

Использование ogrinfo для проверки пространственных данных

Утилита GDAL ogrinfo позволяет просматривать метаданные shapefile или других источников векторных данных:

$ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp
INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
      using driver `ESRI Shapefile' successful.
1: TM_WORLD_BORDERS-0.3 (Polygon)

ogrinfo сообщает нам, что shapefile содержит один слой, и этот слой содержит данные полигонов. Для получения дополнительной информации укажем имя слоя и воспользуемся опцией -so для получения только важной сводной информации:

$ ogrinfo -so world/data/TM_WORLD_BORDERS-0.3.shp TM_WORLD_BORDERS-0.3
INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
      using driver `ESRI Shapefile' successful.

Layer name: TM_WORLD_BORDERS-0.3
Geometry: Polygon
Feature Count: 246
Extent: (-180.000000, -90.000000) - (180.000000, 83.623596)
Layer SRS WKT:
GEOGCS["GCS_WGS_1984",
    DATUM["WGS_1984",
        SPHEROID["WGS_1984",6378137.0,298.257223563]],
    PRIMEM["Greenwich",0.0],
    UNIT["Degree",0.0174532925199433]]
FIPS: String (2.0)
ISO2: String (2.0)
ISO3: String (3.0)
UN: Integer (3.0)
NAME: String (50.0)
AREA: Integer (7.0)
POP2005: Integer (10.0)
REGION: Integer (3.0)
SUBREGION: Integer (3.0)
LON: Real (8.3)
LAT: Real (7.3)

Эта подробная сводная информация указывает количество объектов в слое (246), географические границы данных, систему пространственных координат («SRS WKT»), а также информацию о типе для каждого атрибутивного поля. Например, FIPS: String (2.0) указывает, что символьное поле FIPS имеет максимальную длину 2. Аналогично, LON: Real (8.3) — это поле с плавающей точкой, которое может содержать максимум 8 знаков до трех десятичных знаков.

Географические модели

Определение географической модели

Теперь, когда вы изучили свой набор данных с помощью ogrinfo, создайте модель GeoDjango для представления этих данных:

from django.contrib.gis.db import models

class WorldBorder(models.Model):
    # Regular Django fields corresponding to the attributes in the
    # world borders shapefile.
    name = models.CharField(max_length=50)
    area = models.IntegerField()
    pop2005 = models.IntegerField('Population 2005')
    fips = models.CharField('FIPS Code', max_length=2)
    iso2 = models.CharField('2 Digit ISO', max_length=2)
    iso3 = models.CharField('3 Digit ISO', max_length=3)
    un = models.IntegerField('United Nations Code')
    region = models.IntegerField('Region Code')
    subregion = models.IntegerField('Sub-Region Code')
    lon = models.FloatField()
    lat = models.FloatField()

    # GeoDjango-specific: a geometry field (MultiPolygonField)
    mpoly = models.MultiPolygonField()

    # Returns the string representation of the model.
    def __str__(self):              # __unicode__ on Python 2
        return self.name

Обратите внимание, что модуль models импортируется из django.contrib.gis.db.

По умолчанию система пространственных координат для полей геометрии — WGS84 (т.е., SRID равен 4326) — другими словами, координаты поля представляют пары долгота, широта в градусах. Для использования другой системы координат установите SRID поля геометрии с помощью аргумента srid. Используйте целое число, представляющее код EPSG системы координат.

Выполнение migrate

После определения модели необходимо синхронизировать ее с базой данных. Сначала создайте миграцию базы данных:

$ python manage.py makemigrations
Migrations for 'world':
  world/migrations/0001_initial.py:
    - Create model WorldBorder

Давайте рассмотрим SQL-запрос, который сгенерирует таблицу для модели WorldBorder:

$ python manage.py sqlmigrate world 0001

Эта команда должна вывести следующее:

BEGIN;
--
-- Create model WorldBorder
--
CREATE TABLE "world_worldborder" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(50) NOT NULL,
    "area" integer NOT NULL,
    "pop2005" integer NOT NULL,
    "fips" varchar(2) NOT NULL,
    "iso2" varchar(2) NOT NULL,
    "iso3" varchar(3) NOT NULL,
    "un" integer NOT NULL,
    "region" integer NOT NULL,
    "subregion" integer NOT NULL,
    "lon" double precision NOT NULL,
    "lat" double precision NOT NULL
    "mpoly" geometry(MULTIPOLYGON,4326) NOT NULL
)
;
CREATE INDEX "world_worldborder_mpoly_id" ON "world_worldborder" USING GIST ( "mpoly" );
COMMIT;

Если это выглядит правильно, выполните migrate, чтобы создать эту таблицу в базе данных:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, world
Running migrations:
  ...
  Applying world.0001_initial... OK

Импорт пространственных данных

В этом разделе показано, как импортировать shapefile границ мира в базу данных с помощью моделей GeoDjango, используя утилиту импорта данных LayerMapping.

Существует множество способов импорта данных в пространственную базу данных — помимо инструментов, включенных в GeoDjango, вы также можете использовать следующие:

  • ogr2ogr: Утилита командной строки, включенная в GDAL, которая может импортировать многие форматы векторных данных в PostGIS, MySQL и Oracle базы данных.
  • shp2pgsql: Эта утилита, включенная в PostGIS, импортирует ESRI shapefiles в PostGIS.

Интерфейс GDAL

Ранее вы использовали ogrinfo для просмотра содержимого shapefile границ мира. GeoDjango также включает Pythonic интерфейс к мощной библиотеке OGR GDAL, который может работать со всеми источниками векторных данных, поддерживаемыми OGR.

Сначала вызовите оболочку Django:

$ python manage.py shell

Если вы скачали данные Границы мира ранее в учебнике, вы можете определить его путь, используя встроенный модуль Python os:

>>> import os
>>> import world
>>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__),
...                             'data', 'TM_WORLD_BORDERS-0.3.shp'))

Теперь откройте shapefile границ мира, используя интерфейс GeoDjango DataSource:

>>> from django.contrib.gis.gdal import DataSource
>>> ds = DataSource(world_shp)
>>> print(ds)
/ ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)

Объекты источника данных могут иметь разные слои пространственных объектов; однако shapefiles могут содержать только один слой:

>>> print(len(ds))
1
>>> lyr = ds[0]
>>> print(lyr)
TM_WORLD_BORDERS-0.3

Вы можете увидеть тип геометрии слоя и количество содержащихся объектов:

>>> print(lyr.geom_type)
Polygon
>>> print(len(lyr))
246

Примечание

К сожалению, формат данных shapefile не позволяет более детально задавать типы геометрий. Этот shapefile, как и многие другие, на самом деле содержит MultiPolygon геометрии, а не Polygons. Важно использовать более общий тип поля в моделях: GeoDjango MultiPolygonField примет Polygon геометрию, но PolygonField не примет MultiPolygon тип геометрии. Именно поэтому модель WorldBorder выше использует MultiPolygonField.

У объекта Layer также может быть связанная с ним система пространственных координат. Если это так, атрибут srs вернет объект SpatialReference:

>>> srs = lyr.srs
>>> print(srs)
GEOGCS["GCS_WGS_1984",
    DATUM["WGS_1984",
        SPHEROID["WGS_1984",6378137.0,298.257223563]],
    PRIMEM["Greenwich",0.0],
    UNIT["Degree",0.0174532925199433]]
>>> srs.proj4 # PROJ.4 representation
'+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '

Этот shapefile использует популярную систему пространственных координат WGS84 — другими словами, данные используют пары долгота, широта в градусах.

Кроме того, shapefiles также поддерживают атрибутивные поля, которые могут содержать дополнительные данные. Вот поля в слое «Границы мира»:

>>> print(lyr.fields)
['FIPS', 'ISO2', 'ISO3', 'UN', 'NAME', 'AREA', 'POP2005', 'REGION', 'SUBREGION', 'LON', 'LAT']

Следующий код позволит вам изучить типы OGR (например, целые числа или строки), связанные с каждым из полей:

>>> [fld.__name__ for fld in lyr.field_types]
['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal']

Вы можете итерироваться по каждому объекту в слое и извлекать информацию как из геометрии объекта (доступ к ней через атрибут geom), так и из атрибутивных полей объекта (их значения доступны через метод get()):

>>> for feat in lyr:
...    print(feat.get('NAME'), feat.geom.num_points)
...
Guernsey 18
Jersey 26
South Georgia South Sandwich Islands 338
Taiwan 363

Объекты Layer могут быть срезаны:

>>> lyr[0:2]
[<django.contrib.gis.gdal.feature.Feature object at 0x2f47690>, <django.contrib.gis.gdal.feature.Feature object at 0x2f47650>]

А отдельные объекты могут быть получены по их идентификатору объекта:

>>> feat = lyr[234]
>>> print(feat.get('NAME'))
San Marino

Геометрии границ могут быть экспортированы в формате WKT и GeoJSON:

>>> geom = feat.geom
>>> print(geom.wkt)
POLYGON ((12.415798 43.957954,12.450554 ...
>>> print(geom.json)
{ "type": "Polygon", "coordinates": [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...

LayerMapping

Для импорта данных используйте LayerMapping в скрипте Python. Создайте файл под названием load.py в приложении world, с кодом:

import os
from django.contrib.gis.utils import LayerMapping
from .models import WorldBorder

world_mapping = {
    'fips' : 'FIPS',
    'iso2' : 'ISO2',
    'iso3' : 'ISO3',
    'un' : 'UN',
    'name' : 'NAME',
    'area' : 'AREA',
    'pop2005' : 'POP2005',
    'region' : 'REGION',
    'subregion' : 'SUBREGION',
    'lon' : 'LON',
    'lat' : 'LAT',
    'mpoly' : 'MULTIPOLYGON',
}

world_shp = os.path.abspath(
    os.path.join(os.path.dirname(__file__), 'data', 'TM_WORLD_BORDERS-0.3.shp'),
)

def run(verbose=True):
    lm = LayerMapping(
        WorldBorder, world_shp, world_mapping,
        transform=False, encoding='iso-8859-1',
    )
    lm.save(strict=True, verbose=verbose)

Некоторые замечания по тому, что происходит:

  • Каждый ключ в словаре world_mapping соответствует полю в модели WorldBorder. Значение — имя поля shapefile, из которого будут загружаться данные.
  • Ключ mpoly для поля геометрии — MULTIPOLYGON, тип геометрии, который GeoDjango импортирует как поле. Даже простые полигоны в shapefile будут автоматически преобразованы в коллекции перед вставкой в базу данных.
  • Путь к shapefile не является абсолютным — другими словами, если вы переместите приложение world (вместе с подкаталогом data) в другое место, скрипт по-прежнему будет работать.
  • Ключевое слово transform установлено в значение False, потому что данные в shapefile не нуждаются в преобразовании — они уже находятся в системе координат WGS84 (SRID=4326).
  • Ключевое слово encoding установлено в кодировку символов строковых значений в shapefile. Это гарантирует, что строковые значения будут корректно считываться и сохраняться из их исходной кодировки.

После этого вызовите Django shell из каталога проекта geodjango:

$ python manage.py shell

Далее, импортируйте модуль load, вызовите процедуру run, и наблюдайте за тем, как LayerMapping выполняет свою работу:

>>> from world import load
>>> load.run()

Попробуйте ogrinspect

Теперь, когда вы увидели, как определить географические модели и импортировать данные с помощью утилиты импорта данных LayerMapping, вы можете автоматизировать этот процесс с помощью команды управления ogrinspect. Команда ogrinspect инспектирует источник векторных данных, поддерживаемый GDAL (например, shapefile), и автоматически генерирует определение модели и словарь LayerMapping.

Общий синтаксис команды:

$ python manage.py ogrinspect [options] <data_source> <model_name> [options]

data_source — путь к источнику данных, поддерживаемому GDAL, а model_name — имя, которое будет использоваться для модели. Командные опции могут быть использованы для более точной настройки генерации модели.

Например, следующая команда почти воспроизводит модель WorldBorder и словарь сопоставления, созданные выше, автоматически:

$ python manage.py ogrinspect world/data/TM_WORLD_BORDERS-0.3.shp WorldBorder \
    --srid=4326 --mapping --multi

Некоторые замечания по командным опциям, приведенным выше:

  • Опция --srid=4326 задает SRID для географического поля.
  • Опция --mapping сообщает ogrinspect также сгенерировать словарь сопоставления для использования с LayerMapping.
  • Опция --multi указана, чтобы географическое поле было MultiPolygonField, а не просто PolygonField.

Команда выводит следующий вывод, который можно скопировать непосредственно в models.py приложения GeoDjango:

# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models

class WorldBorder(models.Model):
    fips = models.CharField(max_length=2)
    iso2 = models.CharField(max_length=2)
    iso3 = models.CharField(max_length=3)
    un = models.IntegerField()
    name = models.CharField(max_length=50)
    area = models.IntegerField()
    pop2005 = models.IntegerField()
    region = models.IntegerField()
    subregion = models.IntegerField()
    lon = models.FloatField()
    lat = models.FloatField()
    geom = models.MultiPolygonField(srid=4326)

# Auto-generated `LayerMapping` dictionary for WorldBorder model
worldborders_mapping = {
    'fips' : 'FIPS',
    'iso2' : 'ISO2',
    'iso3' : 'ISO3',
    'un' : 'UN',
    'name' : 'NAME',
    'area' : 'AREA',
    'pop2005' : 'POP2005',
    'region' : 'REGION',
    'subregion' : 'SUBREGION',
    'lon' : 'LON',
    'lat' : 'LAT',
    'geom' : 'MULTIPOLYGON',
}

Пространственные запросы

Пространственные поиски

GeoDjango добавляет пространственные поиски в Django ORM. Например, вы можете найти страну в таблице WorldBorder, которая содержит определенную точку. Сначала запустите оболочку управления:

$ python manage.py shell

Теперь определите точку интереса [3]:

>>> pnt_wkt = 'POINT(-95.3385 29.7245)'

Строка pnt_wkt представляет точку с долготой -95,3385 градусов и широтой 29,7245 градусов. Геометрия представлена в формате Well Known Text (WKT), стандарт, выпущенный Open Geospatial Consortium (OGC). [4] Импортируйте модель WorldBorder, и выполните поиск contains с использованием pnt_wkt в качестве параметра:

>>> from world.models import WorldBorder
>>> WorldBorder.objects.filter(mpoly__contains=pnt_wkt)
<QuerySet [<WorldBorder: United States>]>

Здесь вы получили QuerySet с одной моделью: границей Соединенных Штатов (точно то, что вы ожидаете).

Аналогично, вы также можете использовать объект геометрии GEOS. Здесь вы можете объединить пространственный поиск intersects с методом get для получения только экземпляра WorldBorder для Сан-Марино вместо набора результатов:

>>> from django.contrib.gis.geos import Point
>>> pnt = Point(12.4604, 43.9420)
>>> WorldBorder.objects.get(mpoly__intersects=pnt)
<WorldBorder: San Marino>

Поиски contains и intersects — лишь часть доступных запросов — в документации по GeoDjango Database API содержится больше информации.

Автоматические пространственные преобразования

При выполнении пространственных запросов GeoDjango автоматически преобразует геометрии, если они находятся в другой системе координат. В следующем примере координаты будут выражены в системе координат EPSG SRID 32140, специфичной для южной Техаса, только и в единицах метров, а не градусов:

>>> from django.contrib.gis.geos import Point, GEOSGeometry
>>> pnt = Point(954158.1, 4215137.1, srid=32140)

Обратите внимание, что pnt также может быть создано с использованием EWKT, «расширенной» формы WKT, которая включает SRID:

>>> pnt = GEOSGeometry('SRID=32140;POINT(954158.1 4215137.1)')

ORM GeoDjango автоматически обернет значения геометрии в SQL-преобразования, позволяя разработчику работать на более высоком уровне абстракции:

>>> qs = WorldBorder.objects.filter(mpoly__intersects=pnt)
>>> print(qs.query) # Generating the SQL
SELECT "world_worldborder"."id", "world_worldborder"."name", "world_worldborder"."area",
"world_worldborder"."pop2005", "world_worldborder"."fips", "world_worldborder"."iso2",
"world_worldborder"."iso3", "world_worldborder"."un", "world_worldborder"."region",
"world_worldborder"."subregion", "world_worldborder"."lon", "world_worldborder"."lat",
"world_worldborder"."mpoly" FROM "world_worldborder"
WHERE ST_Intersects("world_worldborder"."mpoly", ST_Transform(%s, 4326))
>>> qs # printing evaluates the queryset
<QuerySet [<WorldBorder: United States>]>

Прямые запросы

При использовании прямых запросов SQL-запросов, вы обычно должны обернуть свои поля геометрии функцией SQL asText() (или ST_AsText для PostGIS), чтобы значение поля было распознано GEOS:

City.objects.raw('SELECT id, name, asText(point) from myapp_city')

Это не абсолютно необходимо для PostGIS, но, как правило, вы должны использовать прямые запросы только в тех случаях, когда вы точно знаете, что делаете.

Ленивые геометрии

GeoDjango загружает геометрии в стандартном текстовом представлении. Когда поле геометрии впервые обращается к нему, GeoDjango создаёт GEOS geometry object <ref-geos>, открывая мощную функциональность, такую как свойства сериализации для популярных геопространственных форматов:

>>> sm = WorldBorder.objects.get(name='San Marino')
>>> sm.mpoly
<MultiPolygon object at 0x24c6798>
>>> sm.mpoly.wkt # WKT
MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
>>> sm.mpoly.wkb # WKB (as Python binary buffer)
<read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
>>> sm.mpoly.geojson # GeoJSON (requires GDAL)
'{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...

Это включает доступ ко всем сложным геометрическим операциям, предоставляемым библиотекой GEOS:

>>> pnt = Point(12.4604, 43.9420)
>>> sm.mpoly.contains(pnt)
True
>>> pnt.contains(sm.mpoly)
False

Географические аннотации

GeoDjango также предоставляет набор географических аннотаций для вычисления расстояний и других операций (пересечения, разность и т. д.). См. документацию по Географическим функциям базы данных.

Вывод данных на карту

Географический администратор

GeoDjango расширяет приложение администратора Django с поддержкой редактирования полей геометрии.

Основы

GeoDjango также дополняет Django admin, позволяя пользователям создавать и изменять геометрии на JavaScript-карте с функцией прокрутки (на основе OpenLayers).

Давайте сразу начнем. Создайте файл admin.py внутри приложения world со следующим кодом:

from django.contrib.gis import admin
from .models import WorldBorder

admin.site.register(WorldBorder, admin.GeoModelAdmin)

Затем отредактируйте ваш файл urls.py в папке приложения geodjango следующим образом:

from django.conf.urls import url, include
from django.contrib.gis import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

Создайте пользователя администратора:

$ python manage.py createsuperuser

Далее запустите сервер разработки Django:

$ python manage.py runserver

Наконец, перейдите на http://localhost:8000/admin/, войдите в систему с пользователем, которого вы только что создали. Перейдите к любому из записей WorldBorder — границы можно редактировать, нажав на полигон и перетащив вершины в нужное положение.

OSMGeoAdmin

С помощью OSMGeoAdmin, GeoDjango использует слой Open Street Map в администраторе. Это обеспечивает более контекстуальную информацию (включая улицы и транспортные детали), чем доступно с GeoModelAdmin (который использует набор данных WMS Vector Map Level 0, размещённый на OSGeo).

Сначала есть некоторые важные требования:

  • OSMGeoAdmin требует, чтобы GDAL был установлен.
  • Файлы смещения датумов PROJ.4 должны быть установлены (подробнее см. инструкции по установке PROJ.4).

Если вы удовлетворяете этому требованию, просто замените класс опции OSMGeoAdmin в файле admin.py:

admin.site.register(WorldBorder, admin.OSMGeoAdmin)

Примечания

[1] Особая благодарность Bjørn Sandvik из thematicmapping.org за предоставление и поддержку этого набора данных.
[2] Базовые приложения GeoDjango были написаны Дэйном Спрингмейером, Джошем Ливни и Кристофером Шмидтом.
[3] Эта точка — юридический центр Университета Хьюстона .
[4] Open Geospatial Consortium, Inc., Спецификация OpenGIS для простых объектов SQL.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.10/ref/contrib/gis/tutorial/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API