PostGISへデータインポート

テスト用のGeoJSONファイルをPostGISにインポートします。

使用データ

国土数値情報 ダウンロードサービス - http://nlftp.mlit.go.jp/ksj/index.htmlの北海道のデータを使用します

利用約款 国土数値情報利用約款 - http://nlftp.mlit.go.jp/ksj/other/yakkan.html

GeoJSONに変換をしたファイルの大きさ

  • 国土数値情報 行政区域データ (hokkaido.geojson (63.4MB)

  • 国土数値情報 小学校区データ (elementary_school.geojson (196KB))

  • 国土数値情報 公共施設データ (public_facility.geojson (3.3MB))

  • 国土数値情報 バス停留所データ (busstop.geojson (13.9MB))

一括データダウンロード

GeoJSONデータのモデル構造を調べる

Modelクラスを定義するために、GeoJSONデータの内容を調べてる必要があります。 手動でも構造は調べられますが、管理コマンドの「ogrinspect」を利用して自動で構造を調べます。 ogrinspectコマンドはデータファイルを解析して、モデルで定義出来る形式に自動生成してコンソールに出力します。

ogrinspectコマンド $ python manage.py ogrinspect <データファイル> <モデル名>

注意

  • ogrinspectでGeoJSONデータを調べた場合には”models.CharField(max_length=0)”のようにmax_lengthが0になります。Modelに記述する場合にはmax_lenghに最大値の長さを設定してください。((例)max_length=100)

行政区域データ

(env) $ python manage.py ogrinspect --srid=4326 hokkaido.geojson Border
# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models

class Border(models.Model):
    n03_001 = models.CharField(max_length=0)
    n03_002 = models.CharField(max_length=0)
    n03_003 = models.CharField(max_length=0)
    n03_004 = models.CharField(max_length=0)
    n03_007 = models.CharField(max_length=0)
    geom = models.PolygonField(srid=4326)

小学校区データ

(env) $ python manage.py ogrinspect --srid=4326 elementary_school.geojson School
# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models

class School(models.Model):
    a27_001 = models.CharField(max_length=0)
    a27_002 = models.CharField(max_length=0)
    a27_003 = models.CharField(max_length=0)
    a27_004 = models.CharField(max_length=0)
    geom = models.PointField(srid=4326)

公共施設データ

(env) $ python manage.py ogrinspect --srid=4326 public_facility.geojson Facility
# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models

class Facility(models.Model):
    p02_001 = models.CharField(max_length=0)
    p02_002 = models.CharField(max_length=0)
    p02_003 = models.CharField(max_length=0)
    p02_004 = models.CharField(max_length=0)
    p02_005 = models.CharField(max_length=0)
    p02_006 = models.CharField(max_length=0)
    p02_007 = models.CharField(max_length=0)
    geom = models.PointField(srid=4326)

バス停留所データ

(env) $ python manage.py ogrinspect --srid=4326 busstop.geojson Busstop
# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models

    class Busstop(models.Model):
    p11_001 = models.CharField(max_length=0)
    p11_002 = models.CharField(max_length=0)
    p11_003_1 = models.CharField(max_length=0)
    p11_003_2 = models.CharField(max_length=0)
    p11_003_3 = models.CharField(max_length=0)
    p11_003_4 = models.CharField(max_length=0)
    p11_003_5 = models.CharField(max_length=0)
    p11_003_6 = models.CharField(max_length=0)
    p11_003_7 = models.CharField(max_length=0)
    p11_003_8 = models.CharField(max_length=0)
    p11_003_9 = models.CharField(max_length=0)
    p11_003_10 = models.CharField(max_length=0)
    p11_003_11 = models.CharField(max_length=0)
    p11_003_12 = models.CharField(max_length=0)
    p11_003_13 = models.CharField(max_length=0)
    p11_003_14 = models.CharField(max_length=0)
    p11_003_15 = models.CharField(max_length=0)
    p11_003_16 = models.CharField(max_length=0)
    p11_003_17 = models.CharField(max_length=0)
    p11_003_18 = models.CharField(max_length=0)
    p11_003_19 = models.CharField(max_length=0)
    p11_004_1 = models.CharField(max_length=0)
    p11_004_2 = models.CharField(max_length=0)
    p11_004_3 = models.CharField(max_length=0)
    p11_004_4 = models.CharField(max_length=0)
    p11_004_5 = models.CharField(max_length=0)
    p11_004_6 = models.CharField(max_length=0)
    p11_004_7 = models.CharField(max_length=0)
    p11_004_8 = models.CharField(max_length=0)
    p11_004_9 = models.CharField(max_length=0)
    p11_004_10 = models.CharField(max_length=0)
    p11_004_11 = models.CharField(max_length=0)
    p11_004_12 = models.CharField(max_length=0)
    p11_004_13 = models.CharField(max_length=0)
    p11_004_14 = models.CharField(max_length=0)
    p11_004_15 = models.CharField(max_length=0)
    p11_004_16 = models.CharField(max_length=0)
    p11_004_17 = models.CharField(max_length=0)
    p11_004_18 = models.CharField(max_length=0)
    p11_004_19 = models.CharField(max_length=0)
    geom = models.PointField(srid=4326)

Note

  • コマンドパラメータは「$ python manage.py ogrinspect --help」を入力するとヘルプが表示されます。

GeoJSONデータのモデル定義

worldアプリケーションのmodels.pyに、GeoJSONの定義でを記述します。

  • str() にクラスの名称文字列を定義

  • CharFieldの長さが「max_length=0」になっているので「max_length=50〜256」に変更。シェープファイルではmax_lengthが出力されるがGeoJSONの場合出力されない

(env)$ vi world/models.py
# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models

class Border(models.Model):
    n03_001 = models.CharField(max_length=50)
    n03_002 = models.CharField(max_length=50)
    n03_003 = models.CharField(max_length=50)
    n03_004 = models.CharField(max_length=50)
    n03_007 = models.CharField(max_length=50)
    geom = models.PolygonField(srid=4326)

    def __str__(self):
        return self.n03_004

class School(models.Model):
    a27_001 = models.CharField(max_length=50)
    a27_002 = models.CharField(max_length=50)
    a27_003 = models.CharField(max_length=50)
    a27_004 = models.CharField(max_length=50)
    geom = models.PointField(srid=4326)

    def __str__(self):
        return self.a27_003

class Facility(models.Model):
    p02_001 = models.CharField(max_length=50)
    p02_002 = models.CharField(max_length=50)
    p02_003 = models.CharField(max_length=50)
    p02_004 = models.CharField(max_length=50)
    p02_005 = models.CharField(max_length=50)
    p02_006 = models.CharField(max_length=50)
    p02_007 = models.CharField(max_length=50)
    geom = models.PointField(srid=4326)

    def __str__(self):
        return self.p02_004

class Busstop(models.Model):
    p11_001 = models.CharField(max_length=256)
    p11_002 = models.CharField(max_length=256)
    p11_003_1 = models.CharField(max_length=256)
    p11_003_2 = models.CharField(max_length=256)
    p11_003_3 = models.CharField(max_length=256)
    p11_003_4 = models.CharField(max_length=256)
    p11_003_5 = models.CharField(max_length=256)
    p11_003_6 = models.CharField(max_length=256)
    p11_003_7 = models.CharField(max_length=256)
    p11_003_8 = models.CharField(max_length=256)
    p11_003_9 = models.CharField(max_length=256)
    p11_003_10 = models.CharField(max_length=256)
    p11_003_11 = models.CharField(max_length=256)
    p11_003_12 = models.CharField(max_length=256)
    p11_003_13 = models.CharField(max_length=256)
    p11_003_14 = models.CharField(max_length=256)
    p11_003_15 = models.CharField(max_length=256)
    p11_003_16 = models.CharField(max_length=256)
    p11_003_17 = models.CharField(max_length=256)
    p11_003_18 = models.CharField(max_length=256)
    p11_003_19 = models.CharField(max_length=256)
    p11_004_1 = models.CharField(max_length=256)
    p11_004_2 = models.CharField(max_length=256)
    p11_004_3 = models.CharField(max_length=256)
    p11_004_4 = models.CharField(max_length=256)
    p11_004_5 = models.CharField(max_length=256)
    p11_004_6 = models.CharField(max_length=256)
    p11_004_7 = models.CharField(max_length=256)
    p11_004_8 = models.CharField(max_length=256)
    p11_004_9 = models.CharField(max_length=256)
    p11_004_10 = models.CharField(max_length=256)
    p11_004_11 = models.CharField(max_length=256)
    p11_004_12 = models.CharField(max_length=256)
    p11_004_13 = models.CharField(max_length=256)
    p11_004_14 = models.CharField(max_length=256)
    p11_004_15 = models.CharField(max_length=256)
    p11_004_16 = models.CharField(max_length=256)
    p11_004_17 = models.CharField(max_length=256)
    p11_004_18 = models.CharField(max_length=256)
    p11_004_19 = models.CharField(max_length=256)
    geom = models.PointField(srid=4326)

    def __str__(self):
        return self.p11_001

モデルのマイグレーションファイルを作成

データベースの更新をするために、マイグレーションファイルを作成します。 makemigrationsは、モデルの変更内容をデータベースに反映するための命令ファイルを作成します。

データベースのマイグレーションファイル作成 $ python manage.py makemigrations

(env) $ python manage.py makemigrations
Migrations for 'world':
world\migrations\0001_initial.py
    - Create model Border
    - Create model Busstop
    - Create model Facility
    - Create model School

データベースのテーブル更新

データベースのテーブルを更新します。 migrateは、モデルの変更内容をデータベースに反映します。

データベースのマイグレーション $ python manage.py migrate

(env) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, world
Running migrations:
Applying world.0001_initial... OK

データインポート

GeoJSONファイルのデータをインポートをするスクリプトファイルを作成し、これを実行してデータをインポートします。 インポートにはユーティリティのdjango.contrib.gis.utils.LayerMappingを利用します。

Note GDALのコマンドラインユーティリティを利用してインポートすることも出来ます。

  • ogr2ogr: GDALのコマンドラインユーティリティ

  • shp2pgsql: ESRI shapefile用のユーティリティ

行政区域データ

行政区域データのインポートスクリプトを作成します

(env) $ vi world/load_hokkaido.py
# -*- coding: utf-8 -*-
import os
from django.contrib.gis.utils import LayerMapping
from world.models import Border

# Modelとファイルのカラムのマッピング
mapping = {
    'n03_001' : 'N03_001',
    'n03_002' : 'N03_002',
    'n03_003' : 'N03_003',
    'n03_004' : 'N03_004',
    'n03_007' : 'N03_007',
    'geom' : 'POLYGON',
}

# ファイルパス
geojson_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data', 'hokkaido.geojson'))

# 実行
def run(verbose=True):
    lm = LayerMapping(Border, geojson_file, mapping, transform=False, encoding='UTF-8')
    lm.save(strict=True, verbose=verbose)

Djangoのシェルを起動して、ここからインポートスクリプトを実行します。

(env) $ python manage.py shell
>>> from world import load_hokkaido
>>> load_hokkaido.run()

Saved: 小樽市
Saved: 小樽市

    >>> exit()

小学校区データ

小学校区データのインポートスクリプトを作成します

(env) $ vi world/load_elementary_school.py
# -*- coding: utf-8 -*-
import os
from django.contrib.gis.utils import LayerMapping
from world.models import School

# Modelとファイルのカラムのマッピング
mapping = {
    'a27_001' : 'A27_001',
    'a27_002' : 'A27_002',
    'a27_003' : 'A27_003',
    'a27_004' : 'A27_004',
    'geom'    : 'POINT',
}

# ファイルパス
geojson_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data', 'elementary_school.geojson'))

# 実行
def run(verbose=True):
    lm = LayerMapping(School, geojson_file, mapping, transform=False, encoding='UTF-8')
    lm.save(strict=True, verbose=verbose)

Djangoのシェルを起動して、ここからインポートスクリプトを実行します。

(env) $ python manage.py shell
>>> from world import load_elementary_school
>>> load_elementary_school.run()
Saved: 大船小学校
Saved: 高盛小学校

>>> exit()

公共施設データ

公共施設データのインポートスクリプトを作成します

(env) $ vi world/load_public_facility.py
# -*- coding: utf-8 -*-
import os
from django.contrib.gis.utils import LayerMapping
from world.models import Facility

# Modelとファイルのカラムのマッピング
mapping = {
    'p02_001' : 'P02_001',
    'p02_002' : 'P02_002',
    'p02_003' : 'P02_003',
    'p02_004' : 'P02_004',
    'p02_005' : 'P02_005',
    'p02_006' : 'P02_006',
    'p02_007' : 'P02_007',
    'geom'    : 'POINT',
}

# ファイルパス
geojson_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data', 'public_facility.geojson'))

# 実行
def run(verbose=True):
    lm = LayerMapping(Facility, geojson_file, mapping, transform=False, encoding='UTF-8')
    lm.save(strict=True, verbose=verbose)

Djangoのシェルを起動して、ここからインポートスクリプトを実行します。

(env) $ python manage.py shell
>>> from world import load_public_facility
>>> load_public_facility.run()

Saved: 森町消防本部
Saved: 八雲町消防本部

>>> exit()

バス停留所データ

バス停留所データのインポートスクリプトを作成します

(env) $ vi world/load_busstop.py
# -*- coding: utf-8 -*-
import os
from django.contrib.gis.utils import LayerMapping
from world.models import Busstop

# Modelとファイルのカラムのマッピング
mapping = {
    'p11_001' : 'P11_001' ,
    'p11_002' : 'P11_002',
    'p11_003_1' : 'P11_003_1',
    'p11_003_2' : 'P11_003_2',
    'p11_003_3' : 'P11_003_3',
    'p11_003_4' : 'P11_003_4',
    'p11_003_5' : 'P11_003_5',
    'p11_003_6' : 'P11_003_6',
    'p11_003_7' : 'P11_003_7',
    'p11_003_8' : 'P11_003_8',
    'p11_003_9' : 'P11_003_9',
    'p11_003_10' : 'P11_003_10',
    'p11_003_11' : 'P11_003_11',
    'p11_003_12' : 'P11_003_12',
    'p11_003_13' : 'P11_003_13',
    'p11_003_14' : 'P11_003_14',
    'p11_003_15' : 'P11_003_15',
    'p11_003_16' : 'P11_003_16',
    'p11_003_17' : 'P11_003_17',
    'p11_003_18' : 'P11_003_18',
    'p11_003_19' : 'P11_003_19',
    'p11_004_1' : 'P11_004_1',
    'p11_004_2' : 'P11_004_2',
    'p11_004_3' : 'P11_004_3',
    'p11_004_4' : 'P11_004_4',
    'p11_004_5' : 'P11_004_5',
    'p11_004_6' : 'P11_004_6',
    'p11_004_7' : 'P11_004_7',
    'p11_004_8' : 'P11_004_8',
    'p11_004_9' : 'P11_004_9',
    'p11_004_10' : 'P11_004_10',
    'p11_004_11' : 'P11_004_11',
    'p11_004_12' : 'P11_004_12',
    'p11_004_13' : 'P11_004_13',
    'p11_004_14' : 'P11_004_14',
    'p11_004_15' : 'P11_004_15',
    'p11_004_16' : 'P11_004_16',
    'p11_004_17' : 'P11_004_17',
    'p11_004_18' : 'P11_004_18',
    'p11_004_19' : 'P11_004_19',
    'geom'    : 'POINT',
}

# ファイルパス
geojson_file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data', 'busstop.geojson'))

# 実行
def run(verbose=True):
    lm = LayerMapping(Busstop, geojson_file, mapping, transform=False, encoding='UTF-8')
    lm.save(strict=True, verbose=verbose)

Djangoのシェルを起動して、ここからインポートスクリプトを実行します。

(env) $ python manage.py shell
>>> from world import load_busstop
>>> load_busstop.run()

Saved: 農協入口
Saved: 幕別南町

>>> exit()

管理画面の設定変更

管理画面でマップ表示するように変更をします.

geomフィールドをマップ表示

geomフィールドをマップ表示するように変更します。 (powered by OpenLayers) worldアプリケーションの管理画面の設定を変更します。

(env) $ vi world/admin.py
from django.contrib.gis import admin
from world.models import Border, School, Facility, Busstop

admin.site.register(Border, admin.GeoModelAdmin)
admin.site.register(School, admin.GeoModelAdmin)
admin.site.register(Facility, admin.GeoModelAdmin)
admin.site.register(Busstop, admin.GeoModelAdmin)

URL.confの参照モジュールをGISモジュールに変更します。

(env) $ vi geodjango/urls.py
from django.contrib import admin

from django.contrib.gis import admin

Webサーバを起動します。

(env) $ python manage.py runserver

ブラウザからURL: http://127.0.0.1:8000/admin にアクセスし、ログインした後に表示を確認します。

テーブル一覧が表示されているのを確認します

項目を選択して、geomフィールドがマップ表示されているのを確認します

geomフィールドをOpenStreetMapで表示

背景地図をOpenStreetMapに変更する為に、worldアプリの管理画面の設定を変更します。

(env) $ vi world/admin.py
変更前
admin.site.register(Border, admin.GeoModelAdmin)
admin.site.register(School, admin.GeoModelAdmin)
admin.site.register(Facility, admin.GeoModelAdmin)
admin.site.register(Busstop, admin.GeoModelAdmin)

変更後
admin.site.register(Border, admin.OSMGeoAdmin)
admin.site.register(School, admin.OSMGeoAdmin)
admin.site.register(Facility, admin.OSMGeoAdmin)
admin.site.register(Busstop, admin.OSMGeoAdmin)

OpenStreetMapでマップが表示されているのを確認します

django-leafletで管理画面のマップを表示

管理画面のマップをdjango-leafletで表示をします。

django-leafletをインストール

(env) $ pip install django-leaflet

設定ファイルに追加

(env) $ vi geodjango/settings.py
INSTALLED_APPS = [
    :
    'leaflet',
]

管理画面設定ファイルを変更

(env) $ vi world/admin.py
変更前
admin.site.register(Border, admin.OSMGeoAdmin)
admin.site.register(School, admin.OSMGeoAdmin)
admin.site.register(Facility, admin.OSMGeoAdmin)
admin.site.register(Busstop, admin.OSMGeoAdmin)

変更後
from leaflet.admin import LeafletGeoAdmin

class BorderAdmin(LeafletGeoAdmin):
  search_fields = ['n03_001','n03_003','n03_004']
  list_filter = ('n03_003')

admin.site.register(Border, LeafletGeoAdmin)
admin.site.register(School, LeafletGeoAdmin)
admin.site.register(Facility, LeafletGeoAdmin)
admin.site.register(Busstop, LeafletGeoAdmin)

LeafletGeoAdminを登録します。

LeafletGeoAdminを継承したBorderAdminクラスを定義しこれを登録することで、検索・フィルタ機能の追加することが出来ます。

  • search_fields: 検索対象

  • list_filter: フィールド名がフィルタ対象

Note

管理画面をタイトルを変更

設定ファイルのsettings.pyの設定で、管理画面のヘッダとタイトルを変更することが出来ます。

タイトル文字列の設定

(env) $ vi geodjango/settings.py
from django.contrib.gis import admin

admin.site.site_title  = 'GeoDjangoログイン'
admin.site.site_header = 'GeoDjangoハンズオン'
admin.site.index_title = 'GeoDjangoメニュー'

または

(env) $ vi geodjango/settings.py
from django.contrib import admin

admin.AdminSite.site_title  = 'GeoDjangoログイン'
admin.AdminSite.site_header = 'GeoDjangoハンズオン'
admin.AdminSite.index_title = 'GeoDjangoメニュー'

worldアプリケーションのモデル定義のクラスにメタクラスを追加すると、管理画面のテーブル名称を見やすい形に変更することが出来ます。 テーブル名称は、単数と複数の2種類を指定出来ます。

モデルのタイトル文字列の設定

(env) $ vi world/modles.py
class Border(models.Model):
    class Meta:
        verbose_name = '行政区域データ'  # オブジェクトの人間が読める名前(単数)小文字でよい
        verbose_name_plural = '行政区域データ'  # オブジェクトの複数の名前 小文字でよい

    n03_001 = models.CharField(max_length=50)
    n03_002 = models.CharField(max_length=50)
    :
    :

ログイン画面の例

管理画面のテーブル一覧画面の例

Last updated