Search by

    GeoDjango公式チュートリアルの解説

    GeoDjango公式チュートリアルのをやってみたメモ

    geodjangoって何?


    公式

    GeoDjango intends to be a world-class geographic Web framework.

    emoji-cycloneMyTranslate
    • ワールドクラスのジオグラフィックwebフレームワークを目指している。

    公式チュートリアル

    GeoDjango is an included contrib module for Django that turns it into a world-class geographic Web framework. GeoDjango strives to make it as simple as possible to create geographic Web applications, like location-based services. Its features include:

    emoji-cycloneMyTranslate
    • Djangoに含まれているcotrib module。具体的には、下記の機能が。
    • Django model fields for OGC geometries and raster data.
    • Extensions to Django’s ORM for querying and manipulating spatial data.
    • Loosely-coupled, high-level Python interfaces for GIS geometry and raster operations and data manipulation in different formats.
    • Editing geometry fields from the admin.
    emoji-cycloneMyTranslate
    • OGCジオメトリクスと、ラスタデータに対する、djangoのモデルフィールドを持っている。
    • 位置データをクエリし、操作するためのDjangoのORMの拡張
    • GISジオメトリーとラスターオペレーションに対する疎結合、高次元なpythonのインターフェイス、異なるフォーマットにおけるデータ操作
    • ジオメトリーフィールドをadminから編集すること
    emoji-fireMyMemo
    • 地理データの仕様について理解が必要。言葉の定義が色々わからん。
    • 地図の情報を扱うには、OGCジオメトリクスと、ラスタデータというものが必要。
    • 3番目がよくわからない。恐らく、ORMで引っ張ってきた各種の地理データをあれこれするための、pythonのインターフェイスってことだろう。
    • adminサイトでも地理データを管理できる。

    チュートリアル


    公式チュートリアルを通して、やってみて内容のまとめや、わかったことなど(ダラダラと)まとめています。

    今回は下記の環境で試します。

    os : windows 10 pro 64bit
    db : postgres SQL

    PostGISのインストール

    まずはデータベースが、地理データを扱えるようにします。
    PostGISと、その他諸々をインストールする必要があります。

    公式にプラットフォーム毎のインストール方法について記載が。色々面倒ですが、大きく躓くところはなかったです。postgresをすでにインストール済みであれば、Application Stack Builderはwindowsのスタートメニューから選択できるようになっていました。この段階で、OSGeo4Wを使って、 PROJ.4, GDAL, and GEOS librariesをインストールします。

    emoji-exclamationNote

    )を参考にさせていただきました。

    psycopg2は、pipで入れるから、今必要?と思って放置しました。 後で怒られるかも。

    PostGISを入れ終わったら

    データベースに拡張を下記コマンドで拡張する。スーパーユーザーで行うこと。djangoの中で書くことも出来る模様だが、ここでは割愛。

    createdb  <db name>
    $ \c <db name>
    > CREATE EXTENSION postgis;

    postgis_topologyは今は力をいれていないという記載があるが、何の機能か現時点ではあまりよくわからない。

    djangoPJをつくる

    一般的な方法なので、割愛。 読んだままをやっていく。

    地理データの中身

    サンプルのデータを引っ張ってきて、覗いてみましょう、というお話。 まずは、サンプルデータをダウンロードして、展開する。

    空間データを扱うファイルはどんな仕様か

    ESRI Shapefileという、地理データを扱う際の最も一般的なデータ形式について、下記の説明がある。

    • .shp: Holds the vector data for the world borders geometries.
    • .shx: Spatial index file for geometries stored in the .shp.
    • .dbf: Database file for holding non-geometric attribute data (e.g., integer and character fields).
    • .prj: Contains the spatial reference information for the geographic data stored in the shapefile.
    emoji-cycloneMyTranslate
    • .shp : 国境ジオメトリに対するベクターデータを持っている。
    • .shx : .shpの中に保管されているジオメトリ用の、空間インデックスファイル
    • .dbf : (例えば、intやchar型などの)ノンジオメトリック属性データを保管するための、データベースファイル
    • .prj : shapefileに保管されているジオグラフィックデータに対する空間参情報を含んでいる
    emoji-fireMyMemo
    • なるほどわからん。wikiを参照
    • 地理データというのは、複数のファイルに分けて、色んな情報保管してんんだな、うん。今の所大まかな理解でいいや。

    GDALのogrinfoコマンドで空間データの中身を確認

    下記のコマンドを入力

    bash
    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)

    shapefileが一つのレイヤーを持っていて、そのレイヤーはポリゴンデータを含んでいることがわかる。 更にデータから情報を読み取るには、レイヤー名を指定して、要約された重要情報だけを取得するために-soオプションを使う。

    bash
    ...\> 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)

    この詳細なサマリで、レイヤーの中のfeatureの数(246個)、データの地理的な境界、空間参照システム(SRS WKT)、同様に各属性情報に関する型情報がわかります。という説明があるが、よくわからない。大まかな理解としては、-soを使うと、データの性質について、表示してくれるということだね。(やばいほんとに何もわかってない。)

    各属性情報に関する型情報というのは、例えば、FIPS: String (2.0)は、 「FIPSというデータがcharacter型で最大長は2」という意味になるということです。

    featureについてはこちらがわかりやすかったです。

    emoji-questionそもそも何シリーズ
    • GDAL : wikiを確認、なるほどわからん。
      • GDAL is for ラスタ (.shpはベクタではないのか??)
      • OGR is for ベクタ

    Geographic Models

    モデルの定義

    ここまで、地理データの概要について、学んできました。 ここでやっと!GeoDjango modelを、djangoの中に作っていきます。

    models.py
    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

    Note that the models module is imported from django.contrib.gis.db.

    The default spatial reference system for geometry fields is WGS84 (meaning the SRID is 4326) – in other words, the field coordinates are in longitude, latitude pairs in units of degrees. To use a different coordinate system, set the SRID of the geometry field with the srid argument. Use an integer representing the coordinate system’s EPSG code.

    emoji-cycloneMyTranslate

    ここでmodelsdjango.contrib.gis.dbからインポートされていることに注目

    デフォルトのgeometry fieldsに対する、空間参照システムはWGS84です(ということは、SRIDが4326だということを意味してる) 別の言い方をすると、緯度経度は、degreeの単位での緯度経度のペアの中にあるということです。 別の座標系を用いるには、srid 引数が付帯したgeometry fieldのSRIDをセットして下さい。 整数表記の座標系のEPSGコードを使って下さい。

    Migrateする

    migrationファイル作って、migrateして。基本公式の通り。ただし、前段でOSGeo4Wの64bit版で諸々のライブラリを入れてしまった人(私)は、32bit版で入れ直しましょう。そうしないとmakemigraionsが通りません。この現象が起こります。

    GDALはwindowsのコンパネからアンインストールは出来ませんでした。対象フォルダやファイルを削除してから、再インストールしました。また環境変数、レジストリ周りも64bitの痕跡が残っているものは、きれいにしないと怒られます。多分。

    あと、下記の記述も必要だと思います。インストールした時のGDALのバージョンでdllの名前は調整が必要でしょうか(?) インストールされているpathの中のgdal***.dllの中身に応じて変更して下さい。

    setting.py
    GDAL_LIBRARY_PATH = r"C:\OSGeo4W\bin\gdal300"

    それにしても、マイグレーションやコンパイル等が上手くいった時って気持ちいいですよね。「動いた!」という軽い感動がプログラミングが好きな一つの要因かもしれない。

    データインポート

    モデルを作り、DBにmigrateも完了したので、DBにデータを投入していきます。インポートツールには、LayerMapping data import utility、ogr2ogr、shp2pgsqlといったものがあるらしい。

    GDAL Interface

    前段で、GDALのoriginfoを使いましたが、GeoDjangoにもGDALの強力なOGRライブラリを扱える、Pythonicインターフェイスが含まれている、とのこと。

    まずはshellを起動して、あれこれデータの中身を確認していきます。 基本的には、djangoを通してるだけで、最初にやったデータの確認と類似した内容でした。

    Noteとして下記の記載。

    shapfileのデータフォーマットでは、geometory typeを特定できない。 具体的に言うと、shapfileの中に、Polygontypeのデータが入っているのかMultiPolygonが入っているのかがわからない。なので、django側はMultipolygonFieldで用意しておく。そうすることで、どっちのデータが入ってきても良いようにしておく(大は小を兼ねる)というのがベスプラのようです。

    上手く動かなかったのは下記の部分、なぜかそのid 無効だよって怒られる。

    bash
    >>> lyr[0:2]

    for でidを取り出してみると、ちゃんと0~2はインデックスとして存在してるけど…、なんでかな。とりあえず放置。

    データは、wkt形式や、geojson形式で取り出せる模様。

    LayerMapping

    LayerMappingはデータをインポートするためのユーティリティーモジュール。 これを使って、DBにデータ投入しましょうと言う話。 pythonスクリプトを作って、shellから投入する、djangoではおなじみのやり方。

    1. 投入するデータとdjango側で用意しているフィールドの名前の対応関係を辞書で定義
    2. 1とデータ本体を、import utiltyに渡すということをしているみたい。

    あとは細々としたこと書いているけど、ここでは省略。

    ogrinspectを使ってみる。

    ogrinspectはこれまでみてきた、データの定義や、読み込み等のプロセスを自動化するコマンド。

    つまり「毎回shapefileの中身をorginfoとかで確認して、その結果から、modelでフィールド定義して…、とか面倒だね、shapefile中覗いて、自動でmodel定義までしてくれたら便利すね、そのコマンド作ったよ」ってことですね。

    コマンドの一般的な利用方法は下記

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

    実例は下記

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

    フラグで色々指定できる。実行すると、標準出力にモデル定義が出力されるので、コピペするなり、テキストにパイプするなり、煮るなり焼くなりして、適宜利用できる。便利ね。

    Spatial Queries 空間情報のクエリ

    Spatial Lookups 空間情報のルックアップ

    (ふう、やっとここまできた、登りはじめは息あがっちゃうね、あと一息。) Geodjangoは、空間のルックアップをDjangoのORMに加えます。 例えば、緯度経度から、それを含んでいる国を見つけることが出来ます。

    こんな感じ。クエリセットが返る。

    bash
    python manage.py shell
    >>> pnt_wkt = 'POINT(-95.3385 29.7245)'
    >>> from world.models import WorldBorder
    >>> WorldBorder.objects.filter(mpoly__contains=pnt_wkt)
    <QuerySet [<WorldBorder: United States>]>

    通常のORMと同じような感覚で使うことが出来る。

    同様に、GEOS gepmetory objectを使うことも出来る。 intersects とgetを組み合わせると、WorldBorderのインスタンスが取得できる。

    bash
    >>> from django.contrib.gis.geos import Point
    >>> pnt = Point(12.4604, 43.9420)
    >>> WorldBorder.objects.get(mpoly__intersects=pnt)
    <WorldBorder: San Marino>
    emoji-fireMyMemo
    • inrsectは交差するという意味か。

    他にも、色んなlookupが可能。ドキュメント参照のこと。

    Automatic Spatial Transformations

    座標系が異なるものが引数に渡されても、geodjangoの中で変換して、いい具合にクエリしてくれるって話。読み流そう。省略。

    Lazy Geometries

    遅延読み込みの考え方がわかっていなかったので、最初何を言っているのか判然としなかった。

    この説明がわかりやすい。

    つまり、lazy(怠惰)というのは「クエリ時点でGEOSGeometryオブジェクトを生成するとマシンのリソースとか時間とか無駄に消費してしまうので、本当に必要になった時にコンストラクトするよ」ってことですね。なるほど。

    Geographic annotations

    前提知識として
    djangoのORMにはannotate()という機能がある。annotate()は引数にQuery Expressionsを取る。

    f()と組み合わせで使うと、djangoにはデータをpullせずに、DBの中でデータ操作される。速いし、排他制御にも有用みたい。

    そういうことが、地理データでも出来ますよって話。具体的なことはあまりイメージできないが、おそらく、「こんな条件の場所が何個あるか」みたいなことが出来るよってことだと思います。

    Putting your data on the map

    このパートでは、いつものadmin.pyに必要なモデル登録して、スーパーユーザー作ってということしかしてない。迷うところはないでしょう。

    ruserverすると、djangoが起動、adminサイトでこんな感じのデータが入っていることがわかる。

    japan

    というわけで、無事にやりきった。わかたような、わからないような。 実際に何か作ってみないと、手に馴染まないかな。

    実際の開発

    adminサイトでは、geodjangoがフロントを書いてくれるから地図が表示されるけど 実際の開発ではどの様にフロントの描画をすればいいのかしら。

    djangoからは位置情報をjsonで返して、(googleとかの)javascriptのAPIに渡すみたいなやり方になるんですかね。そうっぽいですね。

    djangoからのデータを受け取って地図を描くライブラリとか

    背景地図

    次の目標

    web上のどこかから、面白いshapefileを見つけて、かっこよく描画させてみたい。

    以上、geodjangoのチュートリアルでした。お疲れ様です。

    ソースコード


    github