GeoDjango 教程 — Django 文档
GeoDjango 教程
介绍
GeoDjango 是一个包含在 Django 中的 contrib 模块,它将它变成了一个世界级的地理 Web 框架。 GeoDjango 努力使创建地理 Web 应用程序(如基于位置的服务)尽可能简单。 其特点包括:
- OGC 几何和栅格数据的 Django 模型字段。
- Django ORM 的扩展,用于查询和操作空间数据。
- 松散耦合的高级 Python 接口,用于 GIS 几何和栅格操作以及不同格式的数据处理。
- 从管理界面编辑几何字段。
本教程假设您熟悉 Django; 因此,如果您是 Django 的新手,请先通读 常规教程 以熟悉 Django。
本教程将指导您创建用于查看 世界边界 的地理 Web 应用程序。 1 本教程中使用的一些代码取自和/或受 GeoDjango 基本应用程序 项目的启发。 2
笔记
依次进行教程部分的步骤说明。
设置
创建一个新项目
使用标准的 django-admin
脚本创建一个名为 geodjango
的项目:
这将初始化一个新项目。 现在,在 geodjango
项目中创建一个 world
Django 应用程序:
配置settings.py
geodjango
项目设置存储在 geodjango/settings.py
文件中。 编辑数据库连接设置以匹配您的设置:
DATABASES = {
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': 'geodjango',
'USER': 'geo',
},
}
此外,修改 :setting:`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 文件 中找到。 在world
应用程序中创建data
目录,下载世界边界数据,解压。 在 GNU/Linux 平台上,使用以下命令:
世界边界 ZIP 文件包含一组数据文件,统称为 ESRI Shapefile,这是最流行的地理空间数据格式之一。 解压缩后,世界边界数据集包含具有以下扩展名的文件:
.shp
:保存世界边界几何图形的矢量数据。.shx
:存储在.shp
中的几何的空间索引文件。.dbf
:用于保存非几何属性数据(例如,整数和字符字段)的数据库文件。.prj
:包含存储在 shapefile 中的地理数据的空间参考信息。
使用 ogrinfo 检查空间数据
GDAL ogrinfo
实用程序允许检查 shapefile 或其他矢量数据源的元数据:
ogrinfo
告诉我们 shapefile 有一层,并且该层包含多边形数据。 要了解更多信息,我们将指定图层名称并使用 -so
选项仅获取重要的摘要信息:
这些详细的摘要信息告诉我们图层中的要素数量 (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):
return self.name
注意models
模块是从django.contrib.gis.db
导入的。
几何字段的默认空间参考系统是 WGS84(意思是 SRID 是 4326)——换句话说,字段坐标以经度、纬度对为单位,以度为单位。 要使用不同的坐标系,请使用 srid
参数设置几何字段的 SRID。 使用一个整数表示坐标系的 EPSG 代码。
运行 migrate
定义模型后,您需要将其与数据库同步。 首先,创建一个数据库迁移:
让我们看看将为 WorldBorder
模型生成表的 SQL:
该命令应产生以下输出:
如果这看起来正确,请运行 :djadmin:`migrate` 在数据库中创建此表:
导入空间数据
本节将向您展示如何使用 LayerMapping 数据导入实用程序 通过 GeoDjango 模型将世界边界 shapefile 导入数据库。
有许多不同的方法可以将数据导入空间数据库——除了 GeoDjango 中包含的工具,您还可以使用以下工具:
- ogr2ogr:GDAL 附带的命令行实用程序,可以将多种矢量数据格式导入 PostGIS、MySQL 和 Oracle 数据库。
- shp2pgsql:包含在 PostGIS 中的此实用程序将 ESRI shapefile 导入 PostGIS。
GDAL 接口
之前,您使用 ogrinfo
检查世界边界 shapefile 的内容。 GeoDjango 还包括一个 Pythonic 接口到 GDAL 强大的 OGR 库,可以处理 OGR 支持的所有矢量数据源。
首先,调用 Django 命令行:
如果您在本教程前面下载了 World Borders 数据,那么您可以使用 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'))
现在,使用 GeoDjango 的 DataSource 接口打开世界边界 shapefile:
>>> 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 数据格式不允许在几何类型方面具有更大的特异性。 这个 shapefile 和许多其他文件一样,实际上包括 MultiPolygon
几何图形,而不是多边形。 在模型中使用更通用的字段类型很重要:GeoDjango MultiPolygonField
将接受 Polygon
几何,但 PolygonField
将不接受 MultiPolygon
类型几何学。 这就是上面定义的 WorldBorder
模型使用 MultiPolygonField
的原因。
层 也可能有一个与之关联的空间参考系统。 如果是,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 空间参考系统中——换句话说,数据使用以度为单位的经度、纬度对。
此外,shapefile 还支持可能包含附加数据的属性字段。 以下是 World Borders 图层上的字段:
>>> 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>]
并且可以通过其特征 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
要导入数据,请在 Python 脚本中使用 LayerMapping。 在 world
应用程序中创建一个名为 load.py
的文件,代码如下:
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)
lm.save(strict=True, verbose=verbose)
关于正在发生的事情的一些注意事项:
world_mapping
字典中的每个键对应于WorldBorder
模型中的一个字段。 该值是将从中加载数据的 shapefile 字段的名称。- 几何字段的键
mpoly
是MULTIPOLYGON
,GeoDjango 会将字段导入为几何类型。 在插入数据库之前,即使是 shapefile 中的简单多边形也会自动转换为集合。 - shapefile 的路径不是绝对的——换句话说,如果您将
world
应用程序(带有data
子目录)移动到不同的位置,该脚本仍然可以工作。 transform
关键字设置为False
,因为 shapefile 中的数据不需要转换——它已经在 WGS84 (SRID=4326) 中。
然后,从 geodjango
项目目录调用 Django shell:
接下来,导入load
模块,调用run
例程,观察LayerMapping
做的工作:
>>> from world import load
>>> load.run()
试试 ogrinspect
现在您已经了解了如何使用 LayerMapping 数据导入实用程序 定义地理模型和导入数据,可以使用 :djadmin:`ogrinspect` 进一步自动化此过程管理命令。 :djadmin:`ogrinspect` 命令内省 GDAL 支持的矢量数据源(例如,shapefile)并自动生成模型定义和 LayerMapping
字典。
该命令的一般用法如下:
data_source
是 GDAL 支持的数据源的路径,model_name
是用于模型的名称。 命令行选项可用于进一步定义模型的生成方式。
例如,以下命令几乎自动复制了上面创建的 WorldBorder
模型和映射字典:
关于上面给出的命令行选项的一些说明:
--srid=4326
选项设置地理字段的 SRID。--mapping
选项告诉ogrinspect
也生成一个映射字典,用于 LayerMapping。- 指定
--multi
选项,以便地理字段是 MultiPolygonField 而不仅仅是 PolygonField。
该命令产生以下输出,可以直接复制到 GeoDjango 应用程序的 models.py
中:
# 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
表中找到包含特定点的国家/地区。 首先,启动管理外壳:
现在,定义一个兴趣点 3:
>>> pnt_wkt = 'POINT(-95.3385 29.7245)'
pnt_wkt
字符串表示位于 -95.3385 度经度和 29.7245 度纬度的点。 几何图形采用众所周知的文本 (WKT) 格式,这是开放地理空间联盟 (OGC) 发布的标准。 4 导入 WorldBorder
模型,并使用 pnt_wkt
作为参数执行 contains
查找:
>>> 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 数据库 API 文档有更多。
自动空间变换
在进行空间查询时,如果几何体在不同的坐标系中,GeoDjango 会自动转换它们。 在以下示例中,坐标将以 EPSG SRID 32140 表示,这是一个特定于南德克萨斯 的坐标系 ,单位为 米 ,而不是度数:
>>> from django.contrib.gis.geos import GEOSGeometry, Point
>>> pnt = Point(954158.1, 4215137.1, srid=32140)
请注意,pnt
也可以用 EWKT 构建,EWKT 是包含 SRID 的 WKT 的“扩展”形式:
>>> 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>]>
原始查询
使用 原始查询 时,您必须包装几何字段,以便 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 创建一个 GEEOSGeometry 对象,公开强大的功能,例如流行地理空间格式的序列化属性:
>>> 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 的管理应用程序 ,支持编辑几何字段。
基础
GeoDjango 还通过允许用户在 JavaScript 滑动地图(由 OpenLayers 提供支持)上创建和修改几何图形来补充 Django 管理员。
让我们直接潜入。 使用以下代码在 world
应用程序中创建一个名为 admin.py
的文件:
from django.contrib.gis import admin
from .models import WorldBorder
admin.site.register(WorldBorder, admin.GeoModelAdmin)
接下来,在 geodjango
应用程序文件夹中编辑您的 urls.py
,如下所示:
from django.contrib.gis import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
]
创建一个管理用户:
接下来,启动 Django 开发服务器:
最后,浏览到 http://localhost:8000/admin/
,并使用您刚刚创建的用户登录。 浏览到任何 WorldBorder
条目 - 可以通过单击多边形并将顶点拖动到所需位置来编辑边界。
OSMGeoAdmin
使用 OSMGeoAdmin,GeoDjango 在管理中使用 Open Street Map 层。 这提供了比 GeoModelAdmin(使用托管在 OSGeo 上的 矢量地图级别 0 WMS 数据集)更多的上下文(包括街道和大道详细信息)。
必须安装 PROJ.4 数据移动文件(更多详细信息,请参阅 PROJ.4 安装说明 )。
如果您满足此要求,则只需替换 admin.py
文件中的 OSMGeoAdmin
选项类:
admin.site.register(WorldBorder, admin.OSMGeoAdmin)
脚注
- 1
- 特别感谢 thematicmapping.org 的 Bjørn Sandvik 提供和维护此数据集。
- 2
- GeoDjango 基本应用由 Dane Springmeyer、Josh Livni 和 Christopher Schmidt 编写。
- 3
- 这一点是休斯顿大学法律中心。
- 4
- Open Geospatial Consortium, Inc., OpenGIS SQL 的简单特征规范。