geodjango教程

介绍

GeoDjango是Django的一个内置的Conrib模块,它将Django变成一个世界级的地理Web框架。GeoDjango致力于让创建地理Web应用程序变得尽可能简单,比如基于位置的服务。其功能包括:

  • Django模型字段 OGC 几何图形和栅格数据。

  • 用于查询和操作空间数据的Django ORM扩展。

  • 松散耦合的高级python接口,用于不同格式的GIS几何图形和栅格操作以及数据操作。

  • 从管理员编辑几何图形字段。

本教程假定您熟悉django;因此,如果您是django的新手,请阅读 regular tutorial 先熟悉 Django 。

备注

吉昂戈有超出吉昂戈要求的其他要求——请咨询 installation documentation 了解更多详细信息。

本教程将指导您创建地理Web应用程序以查看 world borders . [1] 本教程中使用的一些代码取自和/或受 GeoDjango basic apps 项目。 [2]

备注

按照循序渐进的说明,依次进行教程部分。

设置

创建空间数据库

通常不需要特殊的设置,因此可以像创建其他项目一样创建数据库。我们为选定的数据库提供一些提示:

创建新项目

使用标准 django-admin 创建名为 geodjango

$ django-admin startproject geodjango
...\> django-admin startproject geodjango

这将初始化一个新项目。现在,创建一个 world Django应用程序 geodjango 项目:

$ cd geodjango
$ python manage.py startapp world
...\> cd geodjango
...\> py 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.admindjango.contrib.gisworld (新创建的应用程序)::

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 file`_ ②创建一个 data 目录 world 应用程序,下载世界边界数据,然后解压。在GNU/Linux平台上,使用以下命令:

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

world borders zip文件包含一组数据文件,统称为 `ESRI Shapefile`_ _最流行的地理空间数据格式之一。解压后,“世界边界”数据集包含具有以下扩展名的文件:

  • .shp :保存世界边界几何图形的矢量数据。

  • .shx :存储在 .shp .

  • .dbf :用于保存非几何属性数据(例如整数和字符字段)的数据库文件。

  • .prj :包含存储在形状文件中的地理数据的空间参考信息。

使用 ogrinfo 检查空间数据

GDAL ogrinfo 实用工具允许检查形状文件或其他矢量数据源的元数据:

$ 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 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)
...\> 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位小数,最多可保留3位小数。

地理模型

定义地理模型

现在您已经使用 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, null=True)
    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):
        return self.name

请注意 models 模块从导入 django.contrib.gis.db .

几何字段的默认空间参考系统是wgs84(表示 `SRID`_ _也就是说,场坐标是经度,纬度对是以度为单位的。要使用不同的坐标系,请使用 srid 参数。使用表示坐标系EPSG代码的整数。

运行 migrate

定义模型后,需要将其与数据库同步。首先,创建数据库迁移:

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

让我们看看将为 WorldBorder 模型:

$ python manage.py sqlmigrate world 0001
...\> py manage.py sqlmigrate world 0001

此命令应生成以下输出:

BEGIN;
--
-- Create model WorldBorder
--
CREATE TABLE "world_worldborder" (
    "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    "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
...\> py manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, world
Running migrations:
  ...
  Applying world.0001_initial... OK

导入空间数据

本节将演示如何使用 LayerMapping 数据导入实用程序 .

将数据导入空间数据库有许多不同的方法——除了geodjango中包含的工具之外,您还可以使用以下方法:

  • ogr2ogr: gdal中包含的一个命令行实用程序,可以将许多矢量数据格式导入Postgis、MySQL和Oracle数据库。

  • shp2pgsql: Postgis附带的这个实用程序将ESRI形状文件导入Postgis。

GDAL接口

早些时候,你用过 ogrinfo 检查世界边框形状文件的内容。geodjango还包括一个到gdal强大的ogr库的pythonic接口,可以使用ogr支持的所有矢量数据源。

首先,调用django shell:

$ python manage.py shell
...\> py manage.py shell

如果您下载了 世界边界 数据,那么您就可以使用Python的 pathlib.Path

>>> from pathlib import Path
>>> import world
>>> world_shp = Path(world.__file__).resolve().parent / "data" / "TM_WORLD_BORDERS-0.3.shp"

现在,使用GeoDjango打开世界边界shapefile 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)

数据源对象可以具有不同的地理空间要素层;但是,Shapefile仅允许具有一个层:

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

您可以查看该层的几何图形类型及其包含的要素数量:

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

备注

不幸的是,shapefile数据格式不允许对几何类型有更大的特异性。与许多其他形状文件一样,此形状文件实际上包括 MultiPolygon 几何图形,而不是多边形。在模型中使用更通用的字段类型是很重要的:geodjango MultiPolygonField 将接受 Polygon 几何学,但 PolygonField 不接受 MultiPolygon 键入几何图形。这就是为什么 WorldBorder 上面定义的模型使用 MultiPolygonField .

这个 Layer 也可以具有与其相关联的空间参考系。如果是这样,则 srs 属性将返回一个 SpatialReference 对象:

>>> srs = lyr.srs
>>> print(srs)
GEOGCS["WGS 84",
DATUM["WGS_1984",
    SPHEROID["WGS 84",6378137,298.257223563,
        AUTHORITY["EPSG","7030"]],
    AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
    AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
    AUTHORITY["EPSG","9122"]],
AXIS["Latitude",NORTH],
AXIS["Longitude",EAST],
AUTHORITY["EPSG","4326"]]
>>> srs.proj  # PROJ representation
'+proj=longlat +datum=WGS84 +no_defs'

这个shapefile在流行的wgs84空间参考系统中——换句话说,数据使用经度、纬度对(以度数为单位)。

此外,形状文件还支持可能包含其他数据的属性字段。以下是世界边界层上的字段:

>>> 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', 'OFTInteger64', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal']

您可以遍历图层中的每个要素并从要素的几何(通过访问)中提取信息 geom 属性)以及要素的属性字段(其 values 可通过以下方式访问 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>]

并且可以通过其特征ID来检索各个特征:

>>> 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 应用程序,代码如下:

from pathlib import Path
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 = Path(__file__).resolve().parent / "data" / "TM_WORLD_BORDERS-0.3.shp"


def run(verbose=True):
    lm = LayerMapping(WorldBorder, world_shp, world_mapping, transform=False)
    lm.save(strict=True, verbose=verbose)

关于正在发生的事情的一些注释:

  • 中的每个键 world_mapping 字典对应于 WorldBorder 模型。该值是将从中加载数据的shapefile字段的名称。

  • 钥匙 mpoly 因为几何字段是 MULTIPOLYGON ,几何类型geodjango将字段导入为。即使是shapefile中的简单多边形也会在插入数据库之前自动转换为集合。

  • shapefile的路径不是绝对路径——换句话说,如果移动 world 应用程序(带 data 子目录)到另一个位置,脚本仍然可以工作。

  • 这个 transform 关键字设置为 False 因为shapefile中的数据不需要转换——它已经在wgs84中(srid=4326)。

然后,从 geodjango 项目目录:

$ python manage.py shell
...\> py 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]
...\> py 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
...\> py 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
...\> py manage.py shell

现在,定义一个兴趣点 [3]:

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

这个 pnt_wkt 字符串表示经度为-95.3385度、纬度为29.7245度的点。几何图形的格式称为熟知文本(WKT),这是开放地理空间联盟(OGC)发布的标准。 [4] 导入 WorldBorder 模型,并执行 contains 使用 pnt_wkt 作为参数:

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

在这里,你找到了一个 QuerySet 只有一种模式:美国边境(正如你所期望的那样)。

同样,您也可以使用 GEOS geometry object 。在这里,您可以将 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>

这个 containsintersects 查找只是可用查询的一个子集-- geodjango数据库API 文档还有更多。

自动空间转换

在进行空间查询时,如果几何图形位于不同的坐标系中,GeoDjango会自动转换它们。在下面的示例中,坐标将以 `EPSG SRID 32140`_ _,一种特定于德克萨斯州南部的坐标系 only 并以单位为单位 meters ,而不是学位:

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

请注意 pnt 也可以使用EWKT来构建,EWKT是WKT的“扩展”形式,其中包括SRID:

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

GeoDjango的ORM将自动将几何值包装在转换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>]>

原始查询

使用时 raw queries ,则必须对几何图形字段进行换行,以便GEOS可以识别该字段值:

from django.db import connection
# or if you're querying a non-default database:
from django.db import connections
connection = connections['your_gis_db_alias']

City.objects.raw('SELECT id, name, %s as point from myapp_city' % (connection.ops.select % 'point'))

只有当您确切知道自己在做什么时,才应该使用原始查询。

惰性几何

GeoDjango以标准化的文本表示形式加载几何图形。首次访问几何字段时,GeoDjango会创建 GEOSGeometry 对象,提供强大的功能,如流行地理空间格式的序列化属性:

>>> 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
'{ "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还提供了一组地理注释来计算距离和其他一些操作(交叉、差异等)。见 地理数据库功能 文档。

把你的数据放在地图上

地理管理员

Django's admin application 支持编辑几何图形字段。

基础

Django管理员允许用户创建和修改JavaScript滑动地图上的几何(由 OpenLayers )。

让我们直接开始吧。创建名为的文件 admin.py 内部 world 具有以下代码的应用程序:

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

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

接下来,编辑您的 urls.pygeodjango 应用程序文件夹如下:

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
]

创建管理员用户:

$ python manage.py createsuperuser
...\> py manage.py createsuperuser

接下来,启动Django开发服务器:

$ python manage.py runserver
...\> py manage.py runserver

最后,浏览到 http://localhost:8000/admin/ ,然后使用刚刚创建的用户登录。浏览到任何 WorldBorder 条目——可以通过单击多边形并将顶点拖动到所需位置来编辑边界。

GISModelAdmin

GISModelAdmin ,GeoDjango使用 OpenStreetMap 管理中的层。所提供的上下文(包括街道和大道细节)比 ModelAdmin (它使用 Vector Map Level 0 托管的WMS数据集 OSGeo )。

必须安装项目基准面移动文件(参见 PROJ installation instructions 了解更多详细信息)。

如果满足此要求,则使用 GISModelAdmin 选项类在您的 admin.py 文件::

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

脚注