LoginSignup
7

More than 5 years have passed since last update.

Pythonで世界地図-28(国土交通省 国土数値情報を利用する)

Last updated at Posted at 2018-08-14

国土数値情報 ダウンロードサービス
http://nlftp.mlit.go.jp/ksj/
のデータは、物凄く詳細につくられています。
BaseMapはデータが古いので、海岸線の形状が違いますし(港は埋め立て前の形状で桟橋などはありません。)、精度をFullにしても精度は悪いです。処理時間が遅くなってもいけませんし。

国土数値情報は精度が良い分、時間が非常にかかります。

でも、今まではシェイプファイルをただ表示するだけでした。
フィールドリスト等を見る方法がわかりませんでした。
Pythonで世界地図-21(シェープファイルを作成)
https://qiita.com/ty21ky/items/8449a66662fdd02354e9
等で作ったものは、編集もできるし属性なども見ることが出来ますが、ダウンロードしてきたシェイプファイルはやり方がわかりませんでした。

しかし、数日前にわからない時のprint頼みで、おぉ〜見ることができました。

[{'N03_001': b'\x96k\x8aC\x93\xb9', 'N03_002': b'\x92_\x90U\x91\x8d\x8d\x87\x90U\x8b\xbb\x8b\xc7', 'N03_003': '', 'N03_004': b'\x93\xcf\x8f\xac\x96q\x8es', 'N03_007': '01213', 'RINGNUM': 1, 'SHAPENUM': 1001}, 
・・・・
{'N03_001': b'\x96k\x8aC\x93\xb9', 'N03_002': b'\x8d\xaa\x8e\xba\x90U\x8b\xbb\x8b\xc7', 'N03_003': '', 'N03_004': b'\x8d\xaa\x8e\xba\x8es', 'N03_007': '01223', 'RINGNUM': 1, 'SHAPENUM': 1500}]
・・・・

???調べたら国土数値情報 ダウンロードサービスはシフトJIS(CP932らしいです)を使っているようです。
えっ、Windowsはまだ使っているの?
それに、「b'\x96k\x8aC\x93\xb9'」が何のことかわかりません。
この文字列だけネットで見つかって「北海道」だそうです。
シフトJIS文字コード表見ても、3文字のコードが載っていないので、2文字のコードだけ変換しました。
「b'\x8d\xaa\x8e\xba\x8es'」は「根室」で最後の「\x8es」はわかりません。

これで、ちょっとはわかってきました。
N03_001 都道府県名
N03_004 は一般的なほとんどの市
N03_003 は東京の23区と地方の郡
大阪府の大阪市と堺市は、N03_003が市で、N03_004が区になります。
仙台も大阪市と同じように区長がいない、住居表示だけの区でした。

ダウンロードのページに解説してありました。

image.png
市町村コード一覧表
http://www.tt.rim.or.jp/~ishato/tiri/code/code.htm

これで、郵便番号から位置を検索するコードを書いてみました。

#!/usr/bin/python3
# coding: UTF-8

import json
import sys
import requests
from japanmap import *
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon 
from matplotlib.collections import PatchCollection 
import numpy as np

def zip_search(zipcode): #郵便番号から住所を検索
    url = "http://zipcloud.ibsnet.co.jp/api/search"
    param = {"zipcode": zipcode}

    res = requests.get(url, params=param)
    response = json.loads(res.text)
    address = response["results"][0]
           # 都道府県           市                町
    return [address["address1"],address["address2"],address["address3"]]

def get_lon_lat(prefectures_name): #引数:都道府県名
    pref_code1 = pref_code(prefectures_name)
    if pref_code1 == 1: #北海道
        return [139., 41., 149., 46.]
    elif pref_code1 == 13: #東京
        return [136., 20.3, 140., 36.]
    elif pref_code1 == 46: #鹿児島
        return [128.1, 26.9, 132., 32.5]
    elif pref_code1 < 8: #東北
        return [139., 36.7, 142.3, 41.7]
    elif pref_code1 < 15: #関東
        return [138.2, 34.7, 141., 37.2]
    elif pref_code1 < 24: #中部
        return [135.4, 34.4, 140., 38.7]
    elif pref_code1 < 31: #近畿
        return [134., 33.3, 137., 36.]
    elif pref_code1 < 36: #中国
        return [130.7, 33.5, 134.6, 37.3]
    elif pref_code1 < 40: #四国
        return [131.8, 32.5, 135., 34.8]
    elif pref_code1 < 47: #九州
        return [128.1, 30.9, 132.2, 34.9]
    elif pref_code1 == 47: #沖縄
        return [123.3, 23.7, 131.6, 27.5]
    else:
        return 'error:'

#--

args = sys.argv
argc = len(args) # 引数の個数

if (argc != 2):   # 引数がない場合
    print ('起動方法\n$ ./map.py 郵便番号(例. 1234567)')
    quit()

add = zip_search(sys.argv[1]) #引数:郵便番号
lon_lat = get_lon_lat(add[0]) #引数:都道府県名

font = {'family': 'IPAGothic'}  # 日本語Fontを指定

fig     = plt.figure() #shp_color
ax      = fig.add_subplot(111)

map = Basemap(llcrnrlon=lon_lat[0],llcrnrlat=lon_lat[1],urcrnrlon=lon_lat[2],urcrnrlat=lon_lat[3],resolution='c',projection='cyl')


map.readshapefile('/home/ty/python/map/japan/N03-170101_GML/N03-17_170101', 'test', color ='gray')

patches1   = [] #shp_color

for info, shape in zip(map.test_info, map.test): #都道府県を着色
    if info['N03_001'] == add[0].encode('shift-jis'): #都道府県名
        patches1.append( Polygon(np.array(shape), True) )

ax.add_collection(PatchCollection(patches1, facecolor= 'r', edgecolor='k', linewidths=1., zorder=1, alpha=0.3)) #shp_color
#--
number = len(add[1]) #add[1]の文字数
nn = add[1].rfind('市')
if nn != -1:
    f_add_1 = add[1][:nn+1] #例「大阪市」
    r_add_1 = add[1][-(number-nn)+1:] #例「北区」
else:
    nn1 = add[1].rfind('郡')
    f_add_1 = add[1][:nn1+1] #例「○○郡」
    r_add_1 = add[1][-(number-nn1)+1:] #例「○○町」    

patches2   = [] #shp_color

if f_add_1[-1:] == '郡':
    for info, shape in zip(map.test_info, map.test): #郡を着色
        if  info['N03_001'] == add[0].encode('shift-jis') and info['N03_003'] == f_add_1.encode('shift-jis'): 
            patches2.append( Polygon(np.array(shape), True) )
    plt.title('郵便番号:{}-{}\n{}{}'.format(str(sys.argv[1])[:3], str(sys.argv[1])[-4:], add[0], f_add_1), fontsize=15, **font)

elif f_add_1 == '大阪市' or f_add_1 == '堺市' or f_add_1 == '仙台市':
    for info, shape in zip(map.test_info, map.test): #区を着色(大阪府の場合)
        if  info['N03_003'] == f_add_1.encode('shift-jis') and info['N03_004'] == r_add_1.encode('shift-jis'): 
            patches2.append( Polygon(np.array(shape), True) )
    plt.title('郵便番号:{}-{}\n{}{}'.format(str(sys.argv[1])[:3], str(sys.argv[1])[-4:], add[0], add[1]), fontsize=15, **font)

elif add[0] == '東京都' and add[1][-1:] == '区':
    for info, shape in zip(map.test_info, map.test): #区を着色(東京都の場合)
        if info['N03_003'] == add[1].encode('shift-jis'):
            patches2.append( Polygon(np.array(shape), True) )
    plt.title('郵便番号:{}-{}\n{}{}'.format(str(sys.argv[1])[:3], str(sys.argv[1])[-4:], add[0], add[1]), fontsize=15, **font)

else:
#一般の市(上記以外の市町村)
    for info, shape in zip(map.test_info, map.test): #市町村を着色
        if info['N03_004'] == add[1].encode('shift-jis'): 
            patches2.append( Polygon(np.array(shape), True) )
    plt.title('郵便番号:{}-{}\n{}{}'.format(str(sys.argv[1])[:3], str(sys.argv[1])[-4:], add[0], add[1]), fontsize=15, **font)

ax.add_collection(PatchCollection(patches2, facecolor= 'b', edgecolor='k', linewidths=1., zorder=2, alpha=0.5)) #shp_color
#--
#都府県境界線
qp, qo = get_data()
for k in range(len(qo)):  # 各県ごと
    for othr, ls in qo[k]:  # 各線分ごと
        if othr > k:  # 県コードkが小さい県のとき
            plt.plot([qp[i][0][0] for i in ls], [qp[i][0][1] for i in ls],color = 'r',linewidth = 1,linestyle = '--')

#plt.title('郵便番号:{}-{}\n{}{}'.format(str(sys.argv[1])[:3], str(sys.argv[1])[-4:], add[0], add[1]), fontsize=15, **font)

plt.show()
$ ./郵便番号から位置検索.py 1000001

image.png

東京都は広いのでズームで拡大します。

image.png

赤い部分が東京都、青の部分が千代田区です。

県境を表示する方法がわからないので、ライブラリ japanmapを使用しています。
ちょっとずれるのはどうしようもありません。

郵便番号から住所に変換するのは、
Python3とRequestsで郵便番号から住所を取得するAPIを叩く
http://taillook.hateblo.jp/entry/python3-requests-zipcloud
を使用させていただきました。

matplotlib basemap toolkit
https://matplotlib.org/basemap/api/basemap_api.html

readshapefile(shapefile, name, drawbounds=True, zorder=None, linewidth=0.5, color='k', antialiased=1, ax=None, default_encoding='utf-8')

シェイプファイルを読み込み、必要に応じてマップ上に境界線を描画します。

Note

・形状は2Dであると仮定する
・Point、MultiPoint、Polyline、Polygonの各図形に対してのみ機能します。
・頂点/ポイントは、地理的(緯度/経度)座標でなければなりません。

必須の引数:

Argument Description
shapefile シェイプファイルコンポーネントへのパス。例: shapefile=’/home/jeff/esri/world_borders’ assumes that world_borders.shp, world_borders.shx and world_borders.dbf live in /home/jeff/esri.
name シェイプファイルの頂点またはポイントをマップ投影座標に保持するBasemap属性の名前。 クラス属性名+ '_情報'は、各形状の1つで、dbfファイルの各図形の属性を含む辞書のリストです。たとえば、name = 'counties'の場合​​、self.countiesはそれぞれのx、y頂点のリストになりますシェイプアトリビュートを持つディクショナリのリストになります。個々の多角形のリングは別々のポリゴンに分割され、追加キー 'RINGNUM'と 'SHAPENUM'が形状属性辞書に追加されます。

次のオプションのキーワード引数は、ポリラインとポリゴンのシェイプタイプにのみ関連し、ポイントとマルチポイントのシェイプは無視されます。

Keyword Description
drawbounds 形状の境界を描く (default True).
zorder shape boundary zorder(指定されていない場合は、mathplotlib.lines.LineCollectionのデフォルトが使用されます)。
linewidth shape boundary line width (default 0.5)
color shape boundary line color (default black)
antialiased シェイプ境界のアンチエイリアシングスイッチ (default True).
ax axes instance (デフォルトの軸インスタンスをオーバーライドする)

形状ファイル情報を含むタプル(num_shapes、type、min、max)が返されます。 num_shapesはシェイプの数、typeはタイプコード(shapeelibモジュールで定義されたSHPT *定数の1つ、 see http://shapelib.maptools.org/shp_api.html) and minとmaxは頂点の最小値と最大値を持つ4要素リストです。 drawbounds = Trueの場合、matplotlib.patches.LineCollectionオブジェクトがタプルに追加されます。

シェイプファイルを使うのに、便利そうなツールがありました。

地図とかの空間情報をSQLiteに格納するSpatiaLiteを使用してみる
https://qiita.com/mima_ita/items/64f6c2b8bb47c4b5b391

Ubuntuは、
Both Debian Squeeze and Ubuntu 11.10 やや陳腐化しています(それでもなお役に立つ)。
使えそうな雰囲気。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7