GeoDjangoチュートリアル—Djangoドキュメント

提供:Dev Guides
< DjangoDjango/docs/3.2.x/ref/contrib/gis/tutorial
移動先:案内検索

GeoDjangoチュートリアル

序章

GeoDjangoは、Djangoに含まれているcontribモジュールであり、Djangoをワールドクラスの地理的Webフレームワークに変えます。 GeoDjangoは、ロケーションベースのサービスなどの地理的なWebアプリケーションをできるだけ簡単に作成できるように努めています。 その機能は次のとおりです。

  • OGC ジオメトリとラスターデータのDjangoモデルフィールド。
  • 空間データをクエリおよび操作するためのDjangoのORMの拡張。
  • GISジオメトリとラスター操作、およびさまざまな形式でのデータ操作のための、緩く結合された高レベルのPythonインターフェース。
  • 管理者からのジオメトリフィールドの編集。

このチュートリアルは、Djangoに精通していることを前提としています。 したがって、Djangoを初めて使用する場合は、通常のチュートリアルを読んで、最初にDjangoに慣れてください。

ノート

GeoDjangoには、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.admindjango.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ファイルには、最も一般的な地理空間データ形式の1つである ESRI Shapefile と総称されるデータファイルのセットが含まれています。 解凍すると、世界の境界データセットには次の拡張子のファイルが含まれます。

  • .shp:世界の境界ジオメトリのベクトルデータを保持します。
  • .shx.shpに保存されているジオメトリの空間インデックスファイル。
  • .dbf:非幾何学的属性データ(整数および文字フィールドなど)を保持するためのデータベースファイル。
  • .prj:シェープファイルに保存されている地理データの空間参照情報が含まれています。


ogrinfoを使用して空間データを調べます

GDAL ogrinfoユーティリティを使用すると、シェープファイルまたはその他のベクターデータソースのメタデータを調べることができます。

ogrinfoは、シェープファイルに1つのレイヤーがあり、このレイヤーにポリゴンデータが含まれていることを示しています。 詳細については、レイヤー名を指定し、-soオプションを使用して、重要な要約情報のみを取得します。

この詳細な要約情報は、レイヤー内のフィーチャの数(246)、データの地理的境界、空間参照系( "SRS WKT")、および各属性フィールドのタイプ情報を示します。 たとえば、FIPS: String (2.0)は、FIPS文字フィールドの最大長が2であることを示します。 同様に、LON: Real (8.3)は、小数点以下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, 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 が4326であることを意味します)。つまり、フィールド座標は経度、緯度のペアは度単位です。 別の座標系を使用するには、srid引数を使用してジオメトリフィールドのSRIDを設定します。 座標系のEPSGコードを表す整数を使用します。


migrateを実行します

モデルを定義したら、データベースと同期する必要があります。 まず、データベース移行を作成します。

WorldBorderモデルのテーブルを生成するSQLを見てみましょう。

このコマンドは、次の出力を生成するはずです。

これが正しいと思われる場合は、:djadmin: `migrate` を実行して、データベースに次のテーブルを作成します。


空間データのインポート

このセクションでは、 LayerMappingデータインポートユーティリティを使用してGeoDjangoモデルを介してワールドボーダーシェープファイルをデータベースにインポートする方法を示します。

空間データベースにデータをインポートするには、さまざまな方法があります。GeoDjangoに含まれているツールの他に、次のものを使用することもできます。

  • ogr2ogr :GDALに含まれているコマンドラインユーティリティで、PostGIS、MySQL、およびOracleデータベースに多くのベクターデータ形式をインポートできます。
  • shp2pgsql :PostGISに含まれているこのユーティリティは、ESRIシェープファイルをPostGISにインポートします。

GDALインターフェース

以前は、ogrinfoを使用して、ワールドボーダーシェープファイルの内容を調べました。 GeoDjangoには、OGRがサポートするすべてのベクトルデータソースで動作できるGDALの強力なOGRライブラリへのPythonicインターフェイスも含まれています。

まず、Djangoシェルを呼び出します。

チュートリアルの前半で World Borders データをダウンロードした場合は、Pythonのpathlib.Pathを使用してそのパスを決定できます。

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

次に、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)

データソースオブジェクトは、地理空間フィーチャのさまざまなレイヤーを持つことができます。 ただし、シェープファイルには次の1つのレイヤーしか含めることができません。

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

レイヤーのジオメトリタイプと、レイヤーに含まれるフィーチャの数を確認できます。

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

ノート

残念ながら、シェープファイルのデータ形式では、ジオメトリタイプに関してより具体的にすることはできません。 このシェープファイルには、他の多くのシェープファイルと同様に、実際にはポリゴンではなくMultiPolygonジオメトリが含まれています。 モデルでは、より一般的なフィールドタイプを使用することが重要です。GeoDjangoMultiPolygonFieldPolygonジオメトリを受け入れますが、PolygonFieldMultiPolygonタイプを受け入れません。ジオメトリ。 これが、上記で定義された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'

このシェープファイルは、一般的なWGS84空間参照系にあります。つまり、データは度単位の経度と緯度のペアを使用します。

さらに、シェープファイルは、追加データを含む可能性のある属性フィールドもサポートします。 WorldBordersレイヤーのフィールドは次のとおりです。

>>> 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属性を介してアクセス)とフィーチャの属性フィールド(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というファイルを作成します。

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モデルのフィールドに対応しています。 値は、データのロード元のシェープファイルフィールドの名前です。
  • ジオメトリフィールドのキーmpolyMULTIPOLYGONで、ジオメトリタイプGeoDjangoはフィールドをとしてインポートします。 シェープファイル内の単純なポリゴンでさえ、データベースに挿入する前に自動的にコレクションに変換されます。
  • シェープファイルへのパスは絶対的なものではありません。つまり、worldアプリケーション(dataサブディレクトリを含む)を別の場所に移動しても、スクリプトは引き続き機能します。
  • transformキーワードはFalseに設定されています。これは、シェープファイル内のデータを変換する必要がないためです。すでにWGS84(SRID = 4326)にあります。

その後、geodjangoプロジェクトディレクトリからDjangoシェルを呼び出します。

次に、loadモジュールをインポートし、runルーチンを呼び出して、LayerMappingが動作するのを確認します。

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

ogrinspectをお試しください

LayerMappingデータインポートユーティリティを使用して地理モデルを定義し、データをインポートする方法を確認したので、:djadmin: `ogrinspect` を使用してこのプロセスをさらに自動化することができます。管理コマンド。 :djadmin: `ogrinspect` コマンドは、GDALでサポートされているベクターデータソース(シェープファイルなど)をイントロスペクトし、モデル定義とLayerMappingディクショナリを自動的に生成します。

コマンドの一般的な使用法は次のとおりです。

data_sourceはGDALでサポートされているデータソースへのパスであり、model_nameはモデルに使用する名前です。 コマンドラインオプションを使用して、モデルの生成方法をさらに定義できます。

たとえば、次のコマンドは、上記で作成したWorldBorderモデルとマッピング辞書をほぼ自動的に再現します。

上記のコマンドラインオプションに関する注意事項:

  • --srid=4326オプションは、地理フィールドのSRIDを設定します。
  • --mappingオプションは、ogrinspectに、 LayerMapping で使用するマッピング辞書も生成するように指示します。
  • --multiオプションは、地理フィールドが PolygonField ではなく MultiPolygonField になるように指定されています。

このコマンドは次の出力を生成します。これらの出力は、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は、DjangoORMに空間ルックアップを追加します。 たとえば、特定のポイントを含むWorldBorderテーブルで国を見つけることができます。 まず、管理シェルを起動します。

ここで、関心のあるポイント 3 を定義します。

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

pnt_wkt文字列は、経度-95.3385度、緯度29.7245度のポイントを表します。 ジオメトリは、Open Geospatial Consortium(OGC)によって発行された標準であるWell Know Text(WKT)として知られている形式です。 4 WorldBorderモデルをインポートし、pnt_wktをパラメーターとして使用してcontainsルックアップを実行します。

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

ここでは、QuerySetを、米国の国境(まさにあなたが期待するもの)という1つのモデルだけで取得しました。

同様に、 GEOSジオメトリオブジェクトを使用することもできます。 ここでは、intersects空間ルックアップをgetメソッドと組み合わせて、クエリセットの代わりにSanMarinoの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は、SRIDを含むWKTの「拡張」形式であるEWKTを使用して構築することもできることに注意してください。

>>> 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は 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は、距離やその他のいくつかの操作(交差、差など)を計算するための地理的注釈のセットも提供します。 地理データベース関数のドキュメントを参照してください。


データを地図に載せる

地理管理者

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は管理者で OpenStreetMap レイヤーを使用します。 これにより、 GeoModelAdminOSGeo でホストされている Vector Map Level 0 WMSデータセットを使用)で利用できるよりも多くのコンテキスト(通りや道の詳細を含む)が提供されます。

PROJデータムシフトファイルをインストールする必要があります(詳細については、 PROJインストール手順を参照してください)。

この要件を満たしている場合は、admin.pyファイルのOSMGeoAdminオプションクラスを置き換えます。

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

脚注

1
このデータセットを提供および保守してくれた thematicmapping.org のBjørnSandvikに特に感謝します。
2
GeoDjangoの基本的なアプリは、Dane Springmeyer、Josh Livni、およびChristopherSchmidtによって作成されました。
3
ここがヒューストン大学ローセンターです。
4
Open Geospatial Consortium、Inc。、 SQL用のOpenGISシンプル機能仕様