[tesseract] 免許証の文字を読み込みたい : tesseract_layoutの検証

免許証画像は以下を使用する

tesseract_layoutの値を1から10まで変更して検証する

### tesseract_layout=1
$ python3 main.py
(mm| 日 本 花 子 軌和61年 5月

]1 日生

人誠| 東京都生代田区霞が関2ー 1一2
け| 令和01年05807H 12345

### tesseract_layout=2
$ python3 main.py
Traceback (most recent call last):
File “main.py”, line 9, in
txt = tools[0].image_to_string(
File “/home/vagrant/.local/lib/python3.8/site-packages/pyocr/tesseract.py”, line 386, in image_to_string
raise TesseractError(
pyocr.error.TesseractError: (-1, “Unable to find output file (tested [‘/tmp/tmpuiijvfgx/output.txt’])”)

### tesseract_layout=3
$ python3 main.py
(mm| 日 本 花 子 軌和61年 5月

]1 日生

人誠| 東京都生代田区霞が関2ー 1一2
信和01年05』07H 12345

### tesseract_layout=4
$ python3 main.py
(名| 日 本 花 子 昭和61年 5月 1日生

人誠| 東京都生代田区霞が関2ー 1一2
交付| 令和O1年05』07H 12345

2024箇(06$06有O1Hまで勧

%の 眼鏡等
条件等

見 本
き引第 012345678900 号

0
を人 ト民捧人構誠還| 〇〇OOO
=層衣2908』018導笛にに回較固 ま中

### tesseract_layout=5
$ python3 main.py
| |
に 5
中 M
加 9
9
避 Ok
ら 呈較っ
守 CN OS
| 較 抽| |睦旧旧計員
ー|。居 則遇|
| mp
– 還 RE 逢遇
| | 国~園 必g式還
玲|上 O、 は半較|
陸中 8
| 錠 [壮 加|
| 略叶RS sss
是 陸中
| 還|二 地
| 層間 ら5 8さき
EE ーーベ
[es = 絆|還> 証庄計
首日 EHP

### tesseract_layout=6
$ python3 main.py
(信所| 東京都寺代田区霞が関2-1一2

$和01キ05』07B 12345

天の 眼鏡等 に

条件等

見本 *

*引012345678900 呈 叶

に天上1 5404』01H話司にに放還 ま
に17108801間 汗天市生天 )
にafW29*08801 只間四時間 Sgms 583

### tesseract_layout=7
$ python3 main.py
で 敵 ーー an

### tesseract_layout=8
$ python3 main.py
で 敵

### tesseract_layout=9
$ python3 main.py
|

### tesseract_layout=10
$ python3 main.py
er

### 結論
tesseract_layout=4, lang=’jpn’ が一番まともな気がするけど、
なんでだろう?
きちんと精度高く読み取るにはGoogleのVison APIで実装するっぽいな…

[Ubuntu] tesseractをinstallして使いたい

$ sudo apt -y install tesseract-ocr tesseract-ocr-jpn libtesseract-dev libleptonica-dev tesseract-ocr-script-jpan tesseract-ocr-script-jpan-vert
$ tesseract -v
tesseract 4.1.3
leptonica-1.79.0
libgif 5.1.4 : libjpeg 8d (libjpeg-turbo 2.0.3) : libpng 1.6.37 : libtiff 4.1.0 : zlib 1.2.11 : libwebp 0.6.1 : libopenjp2 2.3.1
Found AVX2
Found AVX
Found SSE
Found libarchive 3.4.0 zlib/1.2.11 liblzma/5.2.4 bz2lib/1.0.8 liblz4/1.9.2 libzstd/1.4.4
$ tesseract –list-langs
List of available languages (5):
Japanese
Japanese_vert
eng
jpn
osd
※osdとは、言語の判定、文字角度の識別を行う

$ tesseract test1.png output
->
$ tesseract test1.png stdout
Hello world
$ tesseract test2.png stdout -l jpn
こんにちは

### Pythonで使えるようにする
$ pip3 install pyocr

from PIL import Image
import sys
import pyocr

tools = pyocr.get_available_tools()
langs = tools[0].get_available_languages()

img = Image.open('test1.png')
txt = tools[0].image_to_string(
	img,
	lang=langs[0],
	builder=pyocr.builders.TextBuilder(tesseract_layout=6)
)
print(txt)

※tesseract_layout=6 が精度に重要

$ python3 main.py
Hello world

lang=’jpn’に変更すると、日本語も出力できる
$ python3 main.py
とこんにちは
|
tesseract_layout=3で実行すると…
$ python3 main.py
こんにちは

#### tesseract_layoutの意味
0 = Orientation and script detection (OSD) only.
1 = Automatic page segmentation with OSD.
2 = Automatic page segmentation, but no OSD, or OCR
3 = Fully automatic page segmentation, but no OSD. (Default)
4 = Assume a single column of text of variable sizes.
5 = Assume a single uniform block of vertically aligned text.
6 = Assume a single uniform block of text.
7 = Treat the image as a single text line.
8 = Treat the image as a single word.
9 = Treat the image as a single word in a circle.
10 = Treat the image as a single character.

0 =方向とスクリプトの検出(OSD)のみ。
1 = OSDによる自動ページセグメンテーション。
2 =自動ページセグメンテーション、ただしOSDまたはOCRなし
3 =完全自動のページセグメンテーション。ただし、OSDはありません。 (ディフォルト)
4 =可変サイズのテキストの単一列を想定します。
5 =垂直に配置されたテキストの単一の均一なブロックを想定します。
6 =単一の均一なテキストブロックを想定します。
7 =画像を単一のテキスト行として扱います。
8 =画像を1つの単語として扱います。
9 =画像を円の中の1つの単語として扱います。
10 =画像を1文字として扱います。

6で設定されているのが多いですね
文字角度を考慮しなければ3で、文字角度を考慮する場合は6でしょうか?
読み取りたい文字に併せて設定を変えながら試していくイメージか…

[mysql] 地震テーブルを作ろう

とりあえず、日付、震源地、マグニチュード、北緯、東経、深さにしておく。
magnitudeは”M不明”となることがあるので、floatではなくvarcharにする.

create table quakes (
	id int AUTO_INCREMENT primary key,
	date datetime,
	epicenter varchar(255),
	magnitude varchar(255),
	intensity varchar(255),
	longitude float,
	latitude float,
	depth int
);

mysql> describe quakes;
+———–+————–+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———–+————–+——+—–+———+—————-+
| id | int | NO | PRI | NULL | auto_increment |
| date | datetime | YES | | NULL | |
| epicenter | varchar(255) | YES | | NULL | |
| magnitude | float | YES | | NULL | |
| intensity | int | YES | | NULL | |
| longitude | float | YES | | NULL | |
| latitude | float | YES | | NULL | |
| depth | int | YES | | NULL | |
+———–+————–+——+—–+———+—————-+
8 rows in set (0.00 sec)

main.py

cur = conn.cursor()
sql1 = "truncate table quakes"
cur.execute(sql1)

for item in json:
	print(item["at"][:16] +" " + item["anm"] + " " + item["mag"] + " " + item["maxi"] + " " + item["cod"][:5] + " " + item["cod"][5:11]+ " " + item["cod"][12:-1])
	longitude = None
	latitude = None
	depth = None
	if(item["cod"]):
		if(item["cod"][5] == "+"):
			longitude = item["cod"][:5]
			latitude = item["cod"][5:11]
			depth = item["cod"][12:-1]
	sql2 = "INSERT INTO quakes (date, epicenter, magnitude, intensity, longitude, latitude, depth) VALUES(%s, %s, %s, %s, %s, %s, %s)"
	cur.execute(sql2, (item["at"][:16], item["anm"], item["mag"], item["maxi"], longitude, latitude, depth))
	conn.commit()

cur.close
conn.close

mysql> select * from quakes;
+—–+———————+——————————–+———–+———–+———–+———-+——–+
| id | date | epicenter | magnitude | intensity | longitude | latitude | depth |
+—–+———————+——————————–+———–+———–+———–+———-+——–+
| 1 | 2022-01-16 17:15:00 | 台湾付近 | 5.4 | 1 | 24 | 122.3 | 40000 |
| 2 | 2022-01-16 10:40:00 | 和歌山県北部 | 2.9 | 2 | 34.2 | 135.2 | 10000 |
| 3 | 2022-01-15 13:10:00 | 南太平洋 | M不明 | | NULL | NULL | NULL |
| 4 | 2022-01-15 23:42:00 | 根室半島南東沖 | 4.3 | 2 | 43.2 | 146.1 | 70000 |
| 5 | 2022-01-15 22:52:00 | 根室半島南東沖 | 3.8 | 1 | 43.3 | 146.2 | 50000 |
| 6 | 2022-01-15 19:56:00 | 宮城県沖 | 3.9 | 1 | 38.3 | 142 | 50000 |
| 7 | 2022-01-15 13:10:00 | 南太平洋 | M不明 | | NULL | NULL | NULL |
| 8 | 2022-01-15 13:10:00 | 南太平洋 | M不明 | | NULL | NULL | NULL |
| 9 | 2022-01-15 14:43:00 | 茨城県南部 | 2.9 | 1 | 36.2 | 140.1 | 70000 |
| 10 | 2022-01-15 10:28:00 | 宮城県沖 | 4.3 | 2 | 38.2 | 141.8 | 50000 |
| 11 | 2022-01-15 09:47:00 | 父島近海 | 4.5 | 1 | 26.9 | 142.8 | 10000 |

とりあえずデータが入ったので、flaskで表示するとこまで作っていきます
ああああああああああああ 苦しい………

flask

@app.route("/")
def index():

	connection = getConnection()

	sql = "SELECT * FROM quakes"
	cursor = connection.cursor()
	cursor.execute(sql)
	results = cursor.fetchall()

	cursor.close()
	connection.close()
	return render_template('index.html', results=results)

template.html

    <table class="table">
    <tr>
        <th>ID</th>
        <th>日付</th>
        <th>震源地</th>
        <th>マグニチュード</th>
        <th>震度</th>
        <th>北緯</th>
        <th>東経</th>
        <th>深さ</th>
    </tr>
	{% for result in results %}
        <tr>
            <td>{{result.id}}</td>
            <td>{{ result.date }}</td>
            <td>{{result.epicenter}}</td>
            <td>{{result.magnitude}}</td>
            <td>{{result.intensity}}</td>
            <td>{{result.longitude}}</td>
            <td>{{result.latitude}}</td>
            <td>{{result.depth}}</td>
        </tr>
    {% endfor %}
    </table>

jinja2側で値を操作できないみたいだから、データを入れるときに考えないといけない

[Python]地震情報を取得して表示したい

print("地震検知日時 震央地 マグニチュード 最大震度")
for item in json:
	print(item["at"][:16] +" " + item["anm"] + " " + item["mag"] + " " + item["maxi"] + " " + item["cod"])

$ python3 main.py
地震検知日時 震央地 マグニチュード 最大震度
2022-01-16T17:15 台湾付近 5.4 1 +24.0+122.3-40000/
2022-01-16T10:40 和歌山県北部 2.9 2 +34.2+135.2-10000/
2022-01-15T13:10 南太平洋 M不明 -20.3-175.2/
2022-01-15T23:42 根室半島南東沖 4.3 2 +43.2+146.1-70000/
2022-01-15T22:52 根室半島南東沖 3.8 1 +43.3+146.2-50000/
2022-01-15T19:56 宮城県沖 3.9 1 +38.3+142.0-50000/
2022-01-15T13:10 南太平洋 M不明 -20.3-175.2/
2022-01-15T13:10 南太平洋 M不明 -20.3-175.2/
2022-01-15T14:43 茨城県南部 2.9 1 +36.2+140.1-70000/
2022-01-15T10:28 宮城県沖 4.3 2 +38.2+141.8-50000/
2022-01-15T09:47 父島近海 4.5 1 +26.9+142.8-10000/
2022-01-15T09:21 若狭湾 3.8 1 +35.8+135.8-10000/
2022-01-14T13:11 石川県能登地方 3.8 2 +37.5+137.2-10000/
2022-01-14T06:55 父島近海 4.8 2 +27.0+141.5-10000/
2022-01-14T00:56 千葉県東方沖 3.8 2 +35.4+140.4-60000/
2022-01-13T15:26 大隅半島東方沖 4.7 3 +31.1+131.5-30000/
2022-01-13T15:26 大隅半島東方沖 4.7 +31.1+131.5-30000/

OK
あとはMySQLにテーブルを作ってFlaskで呼び出して表示

GeoJSONとは

GeoJSONはjsonを基としたGISデータを記述するためのフォーマット
Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollectionをサポート

点データ
L 1地点の場合

{
	"type": "Point",
	"crs" : { "type": "name",
		"properties" : {
			"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
		}
	},
	"coordinates": [ 138.7309, 35.3628]
}

typeは”Point”, “MultiPoint”, “LineString”, “MultiLineString”, “Polygon”, “MultiPolygon”, “GeometryCollection”, “Feature”, および “FeatureCollection”のいずれか
“crs”は、Named CRSとし、”properties”からWGS84を指定
“coordinates”は、経度、緯度、(高度)の順

2地点の場合

{
	"type": "MultiPoint",
	"crs" : { "type": "name",
		"properties" : {
			"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
		}
	},
	"coordinates": [[138.7309, 35.3628],[138.8079, 35.1983]]
}

2地点をFeatureCollectionで表示

{
	"type": "MultiPoint",
	"crs" : { "type": "name",
		"properties" : {
			"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
		}
	},
	"features": [
		{ "type": "Feature",
		  "properties" : { },
		  "geometry" : { "type": "Point",
		"coordinates" : [138.7309, 35.3628]
	}},
		{ "type": "Feature",
			"properties": { },
			"geometry" : {
				"type": "Point",
				"coordinates": [138.8079, 35.1983]
			}}
	]
}

属性情報はpropertiesに記述する

なるほどmap上にアイコンを表示する規格みたいなものね
理解した

[Python] 地震のAPIを叩きたい

気象庁の地震情報をBeautifulSoupでスクレイピングしようとすると、地震情報がレンダリングされる前の最初のテンプレートのHTML情報しか取得できないので、肝心の地震情報が取得できない

import requests
from bs4 import BeautifulSoup

response = requests.get('https://www.data.jma.go.jp/multi/quake/index.html')
soup = BeautifulSoup(response.text, 'html.parser')
title = soup.find('title').get_text()

print(title)

requests-htmlを使うと、ブラウザエンジンでレンダリングさせてから情報を取得できる
$ pip3 install requests-html

from requests_html import HTMLSession

url = "https://www.data.jma.go.jp/multi/quake/index.html"

session = HTMLSession()
r = session.get(url)

r.html.render()
title = r.html.find("title")
print(title)

pyppeteer.errors.BrowserError: Browser closed unexpectedly:

だめだあああああああああああああ
地震ハザードステーションから取らないとあかんか…

[Python] DataFrameを使った操作

pandas.DataFrame 二次元の表形式のデータを表すpandasの基本形

pandas DataFrame
pandas.DataFrame

### 構造
– values, columns, index
– 列columns, 行index
– 行列要素の選択、抽出および変更ができる
– 列ごとに様々な型を持つ

### 作成方法
– 二次元配列・リストからDataFrameを作成
– 複数の一次元配列・リストからDataFrameを作成
– 辞書からDataFrameを作成
– CSVファイルから読み込み

import pandas as pd 
import numpy as np 

df_simple = pd.DataFrame(np.arange(12).reshape(3, 4))

print(df_simple)

$ python3 test.py
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11

print(df_simple.values)
print(type(df_simple.values))

$ python3 test.py
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]

print(df_simple.columns)
print(type(df_simple.columns))
print(df_simple.index)
print(type(df_simple.index))

$ python3 test.py
RangeIndex(start=0, stop=4, step=1)

RangeIndex(start=0, stop=3, step=1)

print(list(df_simple.columns))
print(type(list(df_simple.columns)))
print(df_simple.columns.tolist())
print(type(df_simple.columns.tolist()))

$ python3 test.py
[0, 1, 2, 3]

[0, 1, 2, 3]

columns, indexを設定することで、各列・各行に任意の名前を付けることができる

df = pd.DataFrame(np.arange(12).reshape(3, 4),
	columns=['col_0', 'col_1', 'col_2', 'col_3'],
	index=['row_0','row_1','row_2'])

print(df)

$ python3 test.py
col_0 col_1 col_2 col_3
row_0 0 1 2 3
row_1 4 5 6 7
row_2 8 9 10 11

後から変更もできる

df = pd.DataFrame(np.arange(12).reshape(3, 4),
	columns=['col_0', 'col_1', 'col_2', 'col_3'],
	index=['row_0','row_1','row_2'])

df.columns = ['Col_0', 'Col_1', 'Col_2', 'Col_3']
df.index = ['Row_01', 'Row_1', 'Row_2']

print(df)

特定の列のみ取得

print(df['Col_1'])

$ python3 test.py
Row_0 1
Row_1 5
Row_2 9
Name: Col_1, dtype: int64

全行、全列取得

print(df.loc[:,'Col_1'])
print(df.loc['Row_1',:])
print(df.loc[['Row_0', 'Row_2'],['Col_1', 'Col_3']])

$ python3 test.py
Col_1 Col_3
Row_0 1 3
Row_2 9 11

loc[], at[], queryなども可能

行/列/要素の変更

df.loc[:, 'Col_1'] = [10, 50, 90]

print(df)

$ python3 test.py
Col_0 Col_1 Col_2 Col_3
Row_0 0 10 2 3
Row_1 4 50 6 7
Row_2 8 90 10 11

df.loc[:] = np.arange(12).reshape(3,4) * 100

print(df)

$ python3 test.py
Col_0 Col_1 Col_2 Col_3
Row_0 0 100 200 300
Row_1 400 500 600 700
Row_2 800 900 1000 1100

### CSV読み込み
適当にファイルを作成

import pandas as pd 
import numpy as np 

df = pd.read_csv('data.csv', index_col=0)

print(df)

$ python3 test.py
age state point
name
alice 24 NY 12
bob 42 NY 23
charlie 18 CA 45
dave 22 TX 6
ellen 34 CA 7
frank 56 GA 23
おおおおおおおおおおおおおおおおおおおおおおおお

### 二次元配列/リストからDataFrame作成

print(np.arange(9).reshape(3,3))

print(pd.DataFrame(np.arange(9).reshape(3,3)))

$ python3 test.py
[[0 1 2]
[3 4 5]
[6 7 8]]
0 1 2
0 0 1 2
1 3 4 5
2 6 7 8

print(pd.DataFrame([[0, 1, 2],[3, 4, 5],[6,7,8,9,10]]))

$ python3 test.py
0 1 2 3 4
0 0 1 2 NaN NaN
1 3 4 5 NaN NaN
2 6 7 8 9.0 10.0
要素数がバラバラの場合は足りない部分が欠損値NaN

### 複数の一次元配列から作成

print(pd.DataFrame({'col_0': [0, 1, 2],
					'col_1': np.arange(3,6),
					'col_2':(9,8,7)},
					index=['row_0', 'row_1', 'row_2']))

$ python3 test.py
col_0 col_1 col_2
row_0 0 3 9
row_1 1 4 8
row_2 2 5 7

その他辞書からでもできる
Python取得した値をコマンドラインで確認したい時などはかなり使えそうじゃんか

[Python] 気象庁の気象情報データを取得する

エリアコード
http://www.jma.go.jp/bosai/common/const/area.json
コンテンツ種別
http://www.jma.go.jp/bosai/common/const/contents.json
テロップ
https://plaza.rakuten.co.jp/rabbit77/3000/

# -*- coding:utf-8 -*-
import requests
import json

area_code = "130000"
jma_url = "https://www.jma.go.jp/bosai/forecast/data/forecast/"+str(area_code)+".json"
jma_json = requests.get(jma_url).json()

jma_date = jma_json[0]["timeSeries"][0]["timeDefines"][0]
jma_weather = jma_json[0]["timeSeries"][0]["areas"][0]["weathers"][0]
# jma_rainfall = jma_json["Feature"][0]["Property"]["WeatherList"]["Weather"][0]["Rainfall"]

jma_weather = jma_weather.replace('	', '')
print(jma_date)
print(jma_weather)
# print(jma_rainfall)

$ python3 main.py
2022-01-16T11:00:00+09:00
晴れ

なるほど

以下のJsonデータを整形して、欲しい情報を取れば良い
ざっと見たところ、
timeDefinesが3つあり、weatherCode、weathers(日本語)、wind, waves, pops, temp, tempsMin、tempsMinUpper, tempsMinLower, tempsMax, tempsMaxUpper, tempsMaxLower, tempAverage, precipAverage が このforecastのURLから取得できるようだ。

[{‘publishingOffice’: ‘気象庁’, ‘reportDatetime’: ‘2022-01-16T11:00:00+09:00’, ‘timeSeries’: [{‘timeDefines’: [‘2022-01-16T11:00:00+09:00’, ‘2022-01-17T00:00:00+09:00’, ‘2022-01-18T00:00:00+09:00’], ‘areas’: [{‘area’: {‘name’: ‘東京地方’, ‘code’: ‘130010’}, ‘weatherCodes’: [‘100’, ‘100’, ‘101’], ‘weathers’: [‘晴れ’, ‘晴れ’, ‘晴れ\u3000時々\u3000くもり’], ‘winds’: [‘南の風\u3000後\u3000北の風’, ‘北の風’, ‘北西の風\u3000後\u3000やや強く’], ‘waves’: [‘0.5メートル’, ‘0.5メートル’, ‘0.5メートル\u3000後\u30001メートル’]}, {‘area’: {‘name’: ‘伊豆諸島北部’, ‘code’: ‘130020’}, ‘weatherCodes’: [‘111’, ‘101’, ‘101’], ‘weathers’: [‘晴れ\u3000夜\u3000くもり’, ‘晴れ\u3000時々\u3000くもり’, ‘晴れ\u3000時々\u3000くもり’], ‘winds’: [‘西の風\u3000後\u3000強く’, ‘西の風\u3000強く’, ‘西の風\u3000強く’], ‘waves’: [‘2メートル\u3000後\u30002.5メートル’, ‘2.5メートル\u3000ただし\u3000新島\u3000では\u30002.5メートル\u3000後\u30003メートル’, ‘3メートル\u3000後\u30002.5メートル\u3000ただし\u3000新島\u3000では\u30003メートル’]}, {‘area’: {‘name’: ‘伊豆諸島南部’, ‘code’: ‘130030’}, ‘weatherCodes’: [‘201’, ‘200’, ‘200’], ‘weathers’: [‘くもり\u3000時々\u3000晴れ’, ‘くもり’, ‘くもり’], ‘winds’: [‘西の風\u3000後\u3000やや強く\u3000三宅島\u3000では\u3000西の風\u3000強く’, ‘西の風\u3000強く’, ‘西の風\u3000強く’], ‘waves’: [‘2メートル\u3000後\u30002.5メートル’, ‘2.5メートル\u3000後\u30003メートル’, ‘4メートル’]}, {‘area’: {‘name’: ‘小笠原諸島’, ‘code’: ‘130040’}, ‘weatherCodes’: [‘101’, ‘111’, ‘200’], ‘weathers’: [‘晴れ\u3000時々\u3000くもり’, ‘晴れ\u3000昼前\u3000から\u3000くもり’, ‘くもり’], ‘winds’: [‘北の風’, ‘南西の風\u3000後\u3000北西の風’, ‘北西の風\u3000やや強く’], ‘waves’: [‘2.5メートル\u3000後\u30002メートル\u3000うねり\u3000を伴う’, ‘1.5メートル\u3000後\u30002メートル\u3000うねり\u3000を伴う’, ‘2メートル\u3000後\u30003メートル\u3000うねり\u3000を伴う’]}]}, {‘timeDefines’: [‘2022-01-16T12:00:00+09:00’, ‘2022-01-16T18:00:00+09:00’, ‘2022-01-17T00:00:00+09:00’, ‘2022-01-17T06:00:00+09:00’, ‘2022-01-17T12:00:00+09:00’, ‘2022-01-17T18:00:00+09:00’], ‘areas’: [{‘area’: {‘name’: ‘東京地方’, ‘code’: ‘130010’}, ‘pops’: [‘0’, ‘0’, ‘0’, ‘0’, ‘0’, ‘0’]}, {‘area’: {‘name’: ‘伊豆諸島北部’, ‘code’: ‘130020’}, ‘pops’: [‘0′, ’10’, ’10’, ’10’, ’10’, ’10’]}, {‘area’: {‘name’: ‘伊豆諸島南部’, ‘code’: ‘130030’}, ‘pops’: [’20’, ’10’, ’10’, ’10’, ’10’, ’10’]}, {‘area’: {‘name’: ‘小笠原諸島’, ‘code’: ‘130040’}, ‘pops’: [’10’, ‘0’, ‘0’, ’10’, ’20’, ’10’]}]}, {‘timeDefines’: [‘2022-01-16T09:00:00+09:00’, ‘2022-01-16T00:00:00+09:00’, ‘2022-01-17T00:00:00+09:00’, ‘2022-01-17T09:00:00+09:00’], ‘areas’: [{‘area’: {‘name’: ‘東京’, ‘code’: ‘44132’}, ‘temps’: [’12’, ’12’, ‘1’, ’12’]}, {‘area’: {‘name’: ‘大島’, ‘code’: ‘44172’}, ‘temps’: [’14’, ’14’, ‘7’, ’12’]}, {‘area’: {‘name’: ‘八丈島’, ‘code’: ‘44263’}, ‘temps’: [’15’, ’15’, ’10’, ’12’]}, {‘area’: {‘name’: ‘父島’, ‘code’: ‘44301’}, ‘temps’: [’20’, ’20’, ’15’, ’21’]}]}]}, {‘publishingOffice’: ‘気象庁’, ‘reportDatetime’: ‘2022-01-16T11:00:00+09:00’, ‘timeSeries’: [{‘timeDefines’: [‘2022-01-17T00:00:00+09:00’, ‘2022-01-18T00:00:00+09:00’, ‘2022-01-19T00:00:00+09:00’, ‘2022-01-20T00:00:00+09:00’, ‘2022-01-21T00:00:00+09:00’, ‘2022-01-22T00:00:00+09:00’, ‘2022-01-23T00:00:00+09:00’], ‘areas’: [{‘area’: {‘name’: ‘東京地方’, ‘code’: ‘130010’}, ‘weatherCodes’: [‘100’, ‘101’, ‘101’, ‘101’, ‘101’, ‘101’, ‘101’], ‘pops’: [”, ’10’, ’10’, ’10’, ’10’, ’10’, ’20’], ‘reliabilities’: [”, ”, ‘A’, ‘A’, ‘A’, ‘A’, ‘A’]}, {‘area’: {‘name’: ‘伊豆諸島北部’, ‘code’: ‘130020’}, ‘weatherCodes’: [‘101’, ‘101’, ‘101’, ‘201’, ‘101’, ‘101’, ‘201’], ‘pops’: [”, ’20’, ’20’, ’30’, ’20’, ’20’, ’30’], ‘reliabilities’: [”, ”, ‘A’, ‘A’, ‘A’, ‘A’, ‘A’]}, {‘area’: {‘name’: ‘伊豆諸島南部’, ‘code’: ‘130030’}, ‘weatherCodes’: [‘200’, ‘200’, ‘201’, ‘202’, ‘200’, ‘201’, ‘200’], ‘pops’: [”, ’40’, ’30’, ’50’, ’40’, ’20’, ’40’], ‘reliabilities’: [”, ”, ‘A’, ‘C’, ‘B’, ‘B’, ‘B’]}, {‘area’: {‘name’: ‘小笠原諸島’, ‘code’: ‘130040’}, ‘weatherCodes’: [‘111’, ‘200’, ‘101’, ‘201’, ‘201’, ‘101’, ‘201’], ‘pops’: [”, ’30’, ’20’, ’30’, ’30’, ’20’, ’30’], ‘reliabilities’: [”, ”, ‘A’, ‘A’, ‘A’, ‘A’, ‘A’]}]}, {‘timeDefines’: [‘2022-01-17T00:00:00+09:00’, ‘2022-01-18T00:00:00+09:00’, ‘2022-01-19T00:00:00+09:00’, ‘2022-01-20T00:00:00+09:00’, ‘2022-01-21T00:00:00+09:00’, ‘2022-01-22T00:00:00+09:00’, ‘2022-01-23T00:00:00+09:00’], ‘areas’: [{‘area’: {‘name’: ‘東京’, ‘code’: ‘44132’}, ‘tempsMin’: [”, ‘0’, ‘0’, ‘1’, ‘0’, ‘0’, ‘1’], ‘tempsMinUpper’: [”, ‘3’, ‘2’, ‘2’, ‘2’, ‘3’, ‘3’], ‘tempsMinLower’: [”, ‘-1’, ‘-1’, ‘-1’, ‘-2’, ‘-1’, ‘-1’], ‘tempsMax’: [”, ‘8’, ‘8’, ‘8’, ‘8’, ‘8’, ‘9’], ‘tempsMaxUpper’: [”, ’10’, ’10’, ’10’, ’10’, ’11’, ’11’], ‘tempsMaxLower’: [”, ‘6’, ‘6’, ‘5’, ‘5’, ‘6’, ‘6’]}, {‘area’: {‘name’: ‘大島’, ‘code’: ‘44172’}, ‘tempsMin’: [”, ‘3’, ‘3’, ‘2’, ‘2’, ‘4’, ‘4’], ‘tempsMinUpper’: [”, ‘5’, ‘5’, ‘4’, ‘5’, ‘6’, ‘6’], ‘tempsMinLower’: [”, ‘1’, ‘1’, ‘0’, ‘0’, ‘2’, ‘2’], ‘tempsMax’: [”, ‘9’, ‘9’, ‘8’, ‘8’, ‘9’, ’10’], ‘tempsMaxUpper’: [”, ’10’, ’10’, ‘9’, ’10’, ’12’, ’13’], ‘tempsMaxLower’: [”, ‘7’, ‘7’, ‘6’, ‘6’, ‘8’, ‘8’]}, {‘area’: {‘name’: ‘八丈島’, ‘code’: ‘44263’}, ‘tempsMin’: [”, ‘6’, ‘5’, ‘6’, ‘5’, ‘6’, ‘8’], ‘tempsMinUpper’: [”, ‘8’, ‘7’, ‘7’, ‘7’, ‘8’, ’10’], ‘tempsMinLower’: [”, ‘5’, ‘4’, ‘4’, ‘3’, ‘4’, ‘6’], ‘tempsMax’: [”, ’10’, ‘9’, ‘9’, ‘9’, ’12’, ’13’], ‘tempsMaxUpper’: [”, ’11’, ’10’, ’10’, ’12’, ’14’, ’15’], ‘tempsMaxLower’: [”, ‘8’, ‘8’, ‘8’, ‘8’, ’10’, ’10’]}, {‘area’: {‘name’: ‘父島’, ‘code’: ‘44301’}, ‘tempsMin’: [”, ’16’, ’15’, ’15’, ’14’, ’14’, ’14’], ‘tempsMinUpper’: [”, ’17’, ’16’, ’16’, ’15’, ’15’, ’16’], ‘tempsMinLower’: [”, ’15’, ’13’, ’13’, ’12’, ’12’, ’13’], ‘tempsMax’: [”, ’21’, ’18’, ’19’, ’18’, ’18’, ’20’], ‘tempsMaxUpper’: [”, ’22’, ’19’, ’20’, ’19’, ’20’, ’21’], ‘tempsMaxLower’: [”, ’19’, ’17’, ’18’, ’16’, ’17’, ’18’]}]}], ‘tempAverage’: {‘areas’: [{‘area’: {‘name’: ‘東京’, ‘code’: ‘44132’}, ‘min’: ‘1.1’, ‘max’: ‘9.5’}, {‘area’: {‘name’: ‘大島’, ‘code’: ‘44172’}, ‘min’: ‘3.7’, ‘max’: ‘10.6’}, {‘area’: {‘name’: ‘八丈島’, ‘code’: ‘44263’}, ‘min’: ‘7.4’, ‘max’: ‘12.7’}, {‘area’: {‘name’: ‘父島’, ‘code’: ‘44301’}, ‘min’: ‘15.7’, ‘max’: ‘20.6’}]}, ‘precipAverage’: {‘areas’: [{‘area’: {‘name’: ‘東京’, ‘code’: ‘44132’}, ‘min’: ‘2.0’, ‘max’: ‘15.5’}, {‘area’: {‘name’: ‘大島’, ‘code’: ‘44172’}, ‘min’: ‘9.7’, ‘max’: ‘40.2’}, {‘area’: {‘name’: ‘八丈島’, ‘code’: ‘44263’}, ‘min’: ‘23.4’, ‘max’: ‘57.4’}, {‘area’: {‘name’: ‘父島’, ‘code’: ‘44301’}, ‘min’: ‘5.7’, ‘max’: ‘16.1’}]}}]

政府標準利用規約
https://www.kantei.go.jp/jp/singi/it2/densi/kettei/gl2_betten_1.pdf

その他
ひまわり、レーダー降水ナウキャスト、アメダスなどが取得できる模様
https://qiita.com/e_toyoda/items/7a293313a725c4d306c0

プログラミングそのものよりデータ定義が大切そう

Docker-compose ネットワークの活用とボリューム

ネットワークとボリュームの活用により、1つのサーバに複数のアプリケーション環境を動かす

Dockerではデフォルトでbridgeという仮想的な内部ネットワークを通じて通信を行う。また、外部ネットワークと通信可能になる。
docker-composeではyamlファイルがあるディレクトリ名がプロジェクトになる。
このプロジェクト単位でブリッジねーとワークが分けられており、お互いに影響を与えずに実行することができる。
※プロジェクト専用のブリッジネットワーク

docker-compose.yml

version: "3"
services:
  web:
    image: alpine
    command: ping 127.0.0.1
    networks:
      - frontend
  middle:
    image: alpine
    command: ping 127.0.0.1
    networks:
      - frontend
      - backend
  db:
    image: alpine
    command: ping 127.0.0.1
    networks:
      - backend
networks:
  frontend:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.10.0/24
  backend:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.20.0/24

$ sudo docker-compose up -d
$ sudo docker network ls
$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
f88bc78c1381 bridge bridge local
de0f0e92c466 docker_default bridge local
4f21f478247a host host local
cb121c115cfe mybridge bridge local
6e4c733d1dd5 mysql_default bridge local
562eb5e84daa none null local
e16234d07d34 sakura_backend bridge local
6cbb5ad3b3c6 sakura_frontend bridge local
41ddbd98141d testphp_default bridge local

sakura_backend, frontendが作られている
$ sudo docker exec sakura_web_1 ping -c 3 middle
$ sudo docker exec sakura_web_1 ping -c 3 db
$ sudo docker exec sakura_middle_1 ping -c 3 web
$ sudo docker exec sakura_middle_1 ping -c 3 db
$ sudo docker-compose down -v

### Composeのサービス間で共有するデータはボリュームに保存
ボリュームという機能を使い、コンテナ内に記録しないデータを保管したり、ホスト上のファイルやディレクトリをマウントするために利用できる
ボリュームとは、イメージ・レイヤの制約を受けることなくコンテナ内のファイルシステム上に存在する領域
そのため、動的なデータやログファイルの保存、一時的なファイルを読み書きする場所として用いられる

Docker Composeプロジェクト内でのみデータ共有が可能なボリューム作成
docker-composer.yml

version: "3"
services:
  web:
    image: alpine
    command: ping 127.0.0.1
    volumes:
      - share:/data
  db:
    image: alpine
    command: ping 127.0.0.1
    volumes:
      - share:/data
volumes:
  share:

「share」という名前付きボリュームを「/data」にマウントしている

$ sudo docker volume ls
DRIVER VOLUME NAME
local 4c677ce4af7945140ca9cd8d61791d014cb3ef074c9773ee75526124d34c324a
local 957ed5d98aadef07bcf76d00a6dd9e8b07eed04ac2ce00ff406156e34b18e205
local d6f9d76d3a30b873adf2369dee6de743289ff15f13e5a7451cca820f762e1c92
local docker_db-data
local mydata_share

mydata_shareは、/var/lib/docker/volumes/mydata_share/_data/になる
$ sudo echo “hello” > sudo /var/lib/docker/volumes/mydata_share/_data/hello.txt

ふむ、心理的障壁がかなり下がったな
OKKKKKKKKKKKKK

Docker-compose基礎 ネットワーク

通常、Linuxでは複数プロセスが重複してポートを開くことはできない
コンテナの場合、PIDなどの名前空間が隔離(isolate)され、ホスト側で使用するポートが重複しなければ、お互いに影響を与えずに起動できる
Docker composeの場合は、プロジェクト単位で内部ネットワークを持つ
ユーザ定義・ブリッジ・ネットワークという

### Dockerのネットワークモデル
Dockerコンテナはデフォルトではコンテナ外部とは通信できない
docker runを実行すると、通常bridge0に接続する
同じブリッジ・ネットワーク上に接続しているコンテナはお互いに通信できる
ネットワークを指定しない場合はIPを使っては通信できる
DockerではコンテナのIPを指定するのではなく、通信先をコンテナ名で指定できる
bridge0は名前解決の機能がないので、自分でブリッジネットワークを作成する必要がある。これをUser Defined Bridge Networkと呼ぶ

デフォルトでは3つのネットワークを持つ
$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
f88bc78c1381 bridge bridge local
de0f0e92c466 docker_default bridge local
4f21f478247a host host local
6e4c733d1dd5 mysql_default bridge local
562eb5e84daa none null local
41ddbd98141d testphp_default bridge local

「bridge」はLinuxのブリッジ機能を使うブリッジネットワークで、コンテナ内からの通信はインターネット側にルーティング
「host」はホスト側のネットワークインターフェイスと共有するもの コンテナ内でプログラムがポートをリッスンすると、インターネット側と通信可能になる
「none」はコンテナにネットワークインターフェイスを持たせない。コンテナ内外の通信ができない

コンテナ起動時にどのネットワークに接続するかは –net オプションで指定できる
$ docker run -itd –net host –name hostnginx nginx:alpine
hostネットワークを使っているので、80を指定せずに接続できる

「none」を使ってコンテナを起動する場合
$ sudo docker run -it –net none –rm nginx:alpine ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
noneは外部と通信するインターフェイスが表示できない

$ sudo docker run -it –net none nginx:alpine ping -c 3 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
ping: sendto: Network unreachable
疎通できない

### ブリッジネットワークの挙動
$ sudo docker run -itd –name alpine1 alpine /bin/sh
$ sudo docker run -itd –name alpine2 alpine /bin/sh
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
51b660488406 alpine “/bin/sh” 4 seconds ago Up 3 seconds alpine2
c03d307d63ce alpine “/bin/sh” 9 seconds ago Up 8 seconds alpine1

コンテナのIPを確認するには docker network inspectを使う
$ sudo docker network inspect bridge
“Containers”: {
“51b660488406b1f726f720c9fde6a4d394f12a639d46c1b03acee716342a7cd9”: {
“Name”: “alpine2”,
“EndpointID”: “fb7b9ba7de6b74f865ad9a725e410f9ce0df60b2533af3bba734248a0941854c”,
“MacAddress”: “02:42:ac:11:00:03”,
“IPv4Address”: “172.17.0.3/16”,
“IPv6Address”: “”
},
“c03d307d63cecf61bec943bbde104edf30fabafd0bff11ee46e125df263354c4”: {
“Name”: “alpine1”,
“EndpointID”: “695755eefdf9075e0fd9f32ea380fb3e52419251b2255d6f26a1205eab87964e”,
“MacAddress”: “02:42:ac:11:00:02”,
“IPv4Address”: “172.17.0.2/16”,
“IPv6Address”: “”
}
},
alpine1は172.17.0.2/16、alpine2は172.17.0.3/16

alpine1の中に入ります
$ sudo docker exec -it alpine1 /bin/sh
# ip addr
1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
60: eth0@if61: mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 * 255.255.0.0 U 0 0 0 eth0

$ ping -c 3 172.17.0.3
$ ping -c 3 alpine2

### 自分のネットワークを作成
$ sudo docker network create mybridge
$ sudo docker network ls

$ sudo docker run -itd –net mybridge –name alpine3 alpine /bin/sh
$ sudo docker run -itd –net mybridge –name alpine4 alpine /bin/sh
$ sudo docker exec -it alpine3 /bin/sh
# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default ubuntu-focal.lo 0.0.0.0 UG 0 0 0 eth0
172.25.0.0 * 255.255.0.0 U 0 0 0 eth0

先ほどのalpine1に通信できない。なぜなら接続しているネットワークが異なるため
$ ping 172.17.0.2
ただし、同じネットワークのalpine4には接続できる
# ping -c 3 172.25.0.3
PING 172.25.0.3 (172.25.0.3): 56 data bytes
64 bytes from 172.25.0.3: seq=0 ttl=64 time=0.243 ms
64 bytes from 172.25.0.3: seq=1 ttl=64 time=0.109 ms
64 bytes from 172.25.0.3: seq=2 ttl=64 time=0.088 ms

— 172.25.0.3 ping statistics —
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.088/0.146/0.243 ms

そして、コンテナ名でも疎通できる
# ping -c 3 alpine4
PING alpine4 (172.25.0.3): 56 data bytes
64 bytes from 172.25.0.3: seq=0 ttl=64 time=0.096 ms
64 bytes from 172.25.0.3: seq=1 ttl=64 time=0.087 ms
64 bytes from 172.25.0.3: seq=2 ttl=64 time=0.084 ms

— alpine4 ping statistics —
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.084/0.089/0.096 ms

複数のネットワークで通信するにはこのネットワークの仕組みの理解が必要

なるほどー