Python
Ubuntu

Pythonで世界地図-4

2018.8.10は使用出来ました。サーバーか何かのメンテだったのかな?

2018.07.29迄は、pygeocoder が使用できていましたが2018.08.09に使ったら

$ python3
Python 3.6.5 (default, Apr  1 2018, 05:46:30) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pygeocoder import Geocoder
>>> result = Geocoder.geocode('hokaidou')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ty/.local/lib/python3.6/site-packages/pygeocoder.py", line 129, in geocode
    return GeocoderResult(Geocoder.get_data(params=params))
  File "/home/ty/.local/lib/python3.6/site-packages/pygeocoder.py", line 212, in get_data
    raise GeocoderError(response_json['status'], response.url)
pygeolib.GeocoderError: Error OVER_QUERY_LIMIT
Query: https://maps.google.com/maps/api/geocode/json?address=hokaidou&sensor=false&bounds=&region=&language=&components=

とエラーになります。GoogleのAPIが変更になったと想像しています?

他に無いかと検索していたら、Yahoo!ジオコーダAPI
https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/geocoder.html
がありました。
これは、GoogleのAPIのように色々なことは、出来ないようですが

$ ./test1.py
139.70199214,35.66043257    東京都渋谷区渋谷1丁目

のように、住所から座標を取得することが出来ます。

YahooのアプリケーションIDが必要です。

・yahooユーザー登録
https://developer.yahoo.co.jp/start/

Yahoo! JAPAN IDを持っていない場合は取得します。

アプリケーションIDを取得するには、アプリケーションを登録します。

Client IDおよびシークレットが発行されます。Client IDがアプリケーションIDですので、Pythonのコードの中に入力します。

参考
python urllib による http getと yahoo ジオコーダapiで、住所→座標(緯度経度)変換
https://end0tknr.hateblo.jp/entry/20170903/1504412651

Python3では、エラーが出たのでPython2で実行しました。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import socket
import ssl
import traceback
import urllib
import urllib2

def main():
    ssl._create_default_https_context = ssl._create_unverified_context

    socket.setdefaulttimeout(5);  # sec

    u_agent = urllib2.build_opener() #module 'urllib3' has no attribute 'build_opener'
    u_agent.addheaders = [('User-Agent','python-urllib2-test')];

    api_end_point = 'https://map.yahooapis.jp/geocode/V1/geoCoder' 
    #サンプルリクエスト(東京都港区六本木の住所検索)の場合
    #https://map.yahooapis.jp/geocode/V1/geoCoder?appid=<あなたのアプリケーションID>&query=%e6%9d%b1%e4%ba%ac%e9%83%bd%e6%b8%af%e5%8c%ba%e5%85%ad%e6%9c%ac%e6%9c%a8
    params = {
        'appid' : '<あなたのアプリケーションID>',
        'output' :'json',
        'ei':'UTF-8',
        "al":4,  #level -> 1=都道府県, 2=市区町村, 3=町/大字, 4=丁目/字
        "recursive": 'true', #見つからない場合、階層上位を探す
        "query": '東京都渋谷区渋谷1-11'
    }

    encoded_params = urllib.urlencode(params)
    req_url = api_end_point + "?" + encoded_params
#    print (req_url)

    try:
        # openの引数1コはGET, 2コはPOST
        response = u_agent.open(req_url)
    except urllib2.HTTPError as err:
        print("HTTP ERROR "+ err.reason)
        return
    except socket.timeout as err:
        print("SOCKET.TIMEOUT")
        return
    except Exception as err:
        print("UNKNOWN ERROR")
        print (traceback.format_exc())
        return
    else:
        pass

    res_all_json = response.read()
    res_all = json.loads( res_all_json )
    res_geocodes = res_all["Feature"]

    for ret_geocode in res_geocodes:
        disp_line = "\t".join([ret_geocode["Geometry"]["Coordinates"],
                               ret_geocode["Property"]["Address"] ]);
        print (disp_line)

if __name__ == '__main__':
    main()
$ ./test1.py
139.70199214,35.66043257    東京都渋谷区渋谷1丁目

上記の取り出した座標は文字列なので数値に変換する必要があります。
参考
正規表現で数値を抽出
https://qiita.com/wrblue_mica34/items/9253d6ba7e0baf5cc714

import re #文字列にある複数の数字文字列を数値に変換
#・・・・・
    print(disp_line[0]) #139.70199214,35.66043257
    print(disp_line[1]) #東京都渋谷区渋谷1丁目

    #文字列にある複数の数字文字列を数値に変換
    pattern=r'([+-]?[0-9]+\.?[0-9]*)'
    lists=re.findall(pattern,disp_line[0])
    print(float(lists[0]))
    print(float(lists[1]))
    print(float(lists[0]) + float(lists[1]))

if __name__ == '__main__':
    main()

if・・・の上側にコードを追加します。

$ ./test1.py
139.70199214,35.66043257    東京都渋谷区渋谷1丁目
139.70199214,35.66043257
東京都渋谷区渋谷1丁目
139.70199214
35.66043257
175.36242471

2018.08.09現在、GoogleのAPIが変更になったみたいで、使用することが出来ません。

国名・都市名から色々なデータを取得する。
英語のHELPから使えそうなものを調べたのですが、ネットで検索したら沢山ヒットしました。

その内の一つ
pythonでgooglemap情報を扱う(地名から座標を取得、座標から地図画像を取得)方法まとめ
http://www.robotech-note.com/entry/2016/12/21/213024

ここでは、ヘルプから拾った内容を紹介

$ python3
Python 3.6.5 (default, Apr  1 2018, 05:46:30) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pygeocoder
>>> help(pygeocoder)

国名・都市名でデータを取得できる。(Google Mapで検索できるものが使えそうです。)
"Japan","japan","JP","tokyo","osaka","paris","国会議事堂","us"等

$ python3
Python 3.6.5 (default, Apr  1 2018, 05:46:30) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pygeocoder import Geocoder
>>> result = Geocoder.geocode('hokaidou') #都市名が間違っていたがエラーになっていない
>>> print(result.country)
Japan
>>> print(result.postal_code)
None
>>> print(result.coordinates)
(43.2203266, 142.8634737)
>>> print(result.location)
None
>>> print(result.formatted_address)
Hokkaido, Japan
>>> print(result.country__short_name)
JP
>>> print(result.country__long_name)
Japan
>>> print(result.count)
1
>>> print(result.latitude)
43.2203266
>>> print(result.location_type)
APPROXIMATE
>>> print(result.longitude)
142.8634737
>>> print(result.raw)
[{'address_components': [{'long_name': 'Hokkaido', 'short_name': 'Hokkaido', 'types': ['establishment', 'natural_feature']}, {'long_name': 'Japan', 'short_name': 'JP', 'types': ['country', 'political']}], 'formatted_address': 'Hokkaido, Japan', 'geometry': {'bounds': {'northeast': {'lat': 45.5281272, 'lng': 145.8228658}, 'southwest': {'lat': 41.3858779, 'lng': 139.7602944}}, 'location': {'lat': 43.2203266, 'lng': 142.8634737}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 45.5281272, 'lng': 145.8228658}, 'southwest': {'lat': 41.3858779, 'lng': 139.7602944}}}, 'place_id': 'ChIJey4h6L1rDV8RqrrXBH9USz4', 'types': ['establishment', 'natural_feature']}]
>>> print(result.valid_address)
False
ランドマークから東経、北緯を取得し地図を作成する。
map1.py
#!/usr/bin/python3
# coding: UTF-8

try:
    #Python2の場合
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
except:
    pass  #Python3

import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pygeocoder import Geocoder
import sys

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

if (argc != 2):   # 引数がない場合
    print ('./map1.py 目的地の名称')
    quit()

area_name = args[1]

font = {'family':'IPAGothic'}

try:
    result = Geocoder.geocode(area_name)
    lon1 = float(result.longitude)
    lat1 = float(result.latitude)
except:
    quit()

map = Basemap(llcrnrlon=lon1-2,llcrnrlat=lat1-2,urcrnrlon=lon1+2,urcrnrlat=lat1+2,resolution='h',projection='cyl')

map.drawcoastlines()
map.drawmeridians(np.arange(0, 360, 1))
map.drawparallels(np.arange(-90, 90, 1))

map.drawmapboundary(fill_color='aqua')
map.fillcontinents(color='gray')

plt.text(lon1,lat1+0.15,area_name,fontsize=9,color='w', **font)
x,y = map(lon1, lat1)
map.plot(x, y, 'ro', markersize=6)

plt.show()

$ ./map1.py ユニバーサルスタジオジャパン

image.png

上の地図では、余りにも殺風景ですが、中学校の地図帳の英語版のようなデータがあるArcGIS Server??を見つけました。

参考
GitHub
https://github.com/matplotlib/basemap/issues/75

$ ./test3.py ユニバーサルスタジオジャパン
実行した時に、情報を取るのに失敗するのかすぐに終了してしまうことがあります。10回くらい繰り返してやっと地図が表示されました。
japanmapは以下を参考にしてください。
japanmap(県別データの可視化)
https://qiita.com/SaitoTsutomu/items/6d17889ba47357e44131

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

#https://github.com/matplotlib/basemap/issues/75

import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pygeocoder import Geocoder
import sys
from japanmap import get_data

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

if (argc != 2):   # 引数がない場合
    print ('./map1.py 目的地の名称')
    quit()

area_name = args[1]

font = {'family':'IPAGothic'}

try:
    result = Geocoder.geocode(area_name)
    lon1 = float(result.longitude)
    lat1 = float(result.latitude)
except:
    quit()

plt.figure(figsize=(6,8))
epsg = 4326; 
map=Basemap(epsg=epsg,resolution='i',llcrnrlon=lon1-2,llcrnrlat=lat1-2, urcrnrlon=lon1+2,urcrnrlat=lat1+2)
map.arcgisimage(service='NatGeo_World_Map',verbose=True,xpixels=600)

#japanmapで都府県境界線を表示
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 = 'k',linewidth = 0.5,linestyle = '--')

plt.text(lon1,lat1," "+area_name,fontsize=15,color='r', **font)
x,y = map(lon1, lat1)
map.plot(x, y, 'ro', markersize=6)

plt.show()

image.png