GeoDjango是Django的一个内置的Conrib模块,它将Django变成一个世界级的地理Web框架。GeoDjango致力于让创建地理Web应用程序变得尽可能简单,比如基于位置的服务。其功能包括:
Django模型字段 OGC 几何图形和栅格数据。
用于查询和操作空间数据的Django ORM扩展。
松散耦合的高级python接口,用于不同格式的GIS几何图形和栅格操作以及数据操作。
从管理员编辑几何图形字段。
本教程假定您熟悉django;因此,如果您是django的新手,请阅读 regular tutorial 先熟悉姜戈。
备注
吉昂戈有超出吉昂戈要求的其他要求——请咨询 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.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 file`_ ②创建一个 data
目录 world
应用程序,下载世界边界数据,然后解压。在GNU/Linux平台上,使用以下命令:
$ mkdir world/data
$ cd world/data
$ wget https://web.archive.org/web/20231220150759/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://web.archive.org/web/20231220150759/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。
早些时候,你用过 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>
这个 contains
和 intersects
查找只是可用查询的一个子集-- 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.py
在 geodjango
应用程序文件夹如下:
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基准移动文件(请参阅 PROJ installation instructions 以了解更多详细信息)。
如果满足此要求,则使用 GISModelAdmin
选项类在您的 admin.py
文件::
admin.site.register(WorldBorder, admin.GISModelAdmin)
脚注
7月 22, 2024