この記事は地震界隈 Advent Calendar 2020 24日目の記事です。
誰だこいつ(自己紹介)
初めまして。EqFiと申します。まずこの記事を書くにあたって簡単に自己紹介をしておきたいと思います。
・名前 EqFi(Earth quake Fukushima Iwakiの頭文字より)
・年齢 1985年生まれ
・居住地 名前の通り 福島県いわき市
・主に扱っている言語 C#
こんな感じでしょうか。では早速本題に入っていきましょう。
はじめに
この記事では一応強震モニタについてある程度知っていることを前提に書いています
※もし知らないでやってみたいという人はこの下を読んでください
地震検知機能を作る前に
基本的な考え方
STEP1 強震モニタの画像を取得する
STEP2 取得した画像を処理する
STEP3 ユーザーへ出力
かなりざっくりしてますがこのような感じです。
生活振動
おそらく強震モニタを知っている方、上記のAbout 強震モニタを読んでリンクに飛んだ方はわかると思いますが、地震計は高精度なために生活振動もひろいます。そのため後で詳しく解説しますが全国均一に処理すると誤検知が発生します。場所によっては生活振動が要因で震度1以上の値を示すこともあり、さらに地震計が故障するとには震度5や6、場合によっては震度7を示すこともあります。これは地震検知機能を作るうえで大きな課題ですね。
機能を実装するにあたって
ここまで大体のことを解説してきましたが、このような強震モニタの使用方法は法律上は問題なく、NIEDも暗黙していますが一応不正であることを理解しておいてください。決して悪いことではありませんが、提供している側からすると少し嫌な感じがすると思います。(特大ブーメラン)
実際に書く
STEP0 - プロジェクトの作成
初心者コース
コンソールアプリ(.Net Framework)を選択します。
名前は適宜決めてください。とりあえずEq_Detectにしておきましょう。
作成が完了したら、プロジェクト>Windowsフォームの追加>作成 でフォームを作成しましょう。
フォームにラベルを3つとタイマーを1つ作成します。
次にフォーム自体を選択してダブルクリックしてLoadイベントを作成、次にtimer1をダブルクリックしてTickイベントを作成します。
これが出ていればOKです。
次にProgram.csのusing群のあたりにusingSystem.Windows.Forms;
staticvoidMain(string[]args){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);//ラベルをきれいに表示するApplication.Run(newForm1());//フォームの表示}
開始ボタンを押してフォームが出ればOK。
以上にてSTEP0は終了です。
STEP1 -強震モニタの画像を取得する
さて、いよいよ本格的に書いていきますよ。
強震モニタでは実際に利用することができる画像が提供されています。またこのほか、json形式で緊急地震速報の情報や強震モニタサーバー上の最新時刻が提供されています。
URL形式
もちろんURLにつなげば最新の画像にアクセスできるわけありません。日付と時刻の指定が必要です。
強震モニタのサーバー上では、3時間前までの画像が残されており、毎秒ごとに最新の画像が追加され、同時に3時間前の画像が削除されています。このため常にサーバー上の画像ファイル数は同じになっています。
今回使う震度の画像のフォーマットは、
http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/{現在時刻のyyyyMMdd}/{現在時刻のyyyyMMddHHmmss}.jma_s.gif
となっています。(このURLをクリックしても404が返されます)ちょっと難しいけど
強震モニタは見るとわかる通りかなり多くの色があります。これを簡単に利用するために取得したのちに整理してからSTEP2の処理につなげます。初期設定
まずフォームのcsのusing群をこちらのコードに変更します。
usingSystem;usingSystem.Collections.Generic;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Net.Http;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;usingSystem.Net;usingSystem.IO;usingSystem.Drawing.Imaging;
そうしたらForm1_Loadにタイマーをセットするコードを書きます。
privatevoidForm1_Load(objectsender,EventArgse){timer1.Enabled=true;//タイマーを有効化timer1.Interval=1000;//処理間隔を設定}
- いよいよ内部へ
まずtimer1_Tickイベントにひと工夫凝らします。
private void .. を private async void ... に変更してください。 また、その上に以下のコードを書いてください。
Bitmapbitmap;
これは非同期処理といって重い処理をフォームを固めずに処理する方法です。
次にかなり長いですが次のコードを記述します。
このcolorMap[n]では強震モニタの画像の100種類ある色を15種類ほどにしています。表示する
privateasyncvoidtimer1_Tick(objectsender,EventArgse){DateTimenow=DateTime.Now.AddSeconds(-2);//PC時刻だと早すぎる可能性があるので2秒前にするstringdate=now.ToString("yyyyMMdd");//yyyyMMdd形式にするstringtime=now.ToString("yyyyMMddHHmmss");//yyyyMMddHHmmss形式にするtry{//描画先のImageオブジェクトを作成するBitmapcanvas=newBitmap(352,400);//ImageオブジェクトのGraphicsオブジェクトを作成するGraphicsg=Graphics.FromImage(canvas);//強震モニタ画像を取得WebClientwc=newWebClient();Streamstream=wc.OpenRead($"http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/{date}/{time}.jma_s.gif");wc.Dispose();wc=null;Bitmapimg=newBitmap(stream);//URLから画像を読み込む//開いたURLを閉じるstream.Dispose();stream.Close();stream=null;//強震モニタの色を簡潔にするテーブルを作成ColorMap[]colormap=newColorMap[100];for(inti=0;i<colormap.Length;i++){colormap[i]=newColorMap();}{colormap[0].OldColor=Color.FromArgb(0,0,205);//-3.0colormap[0].NewColor=Color.Gray;colormap[1].OldColor=Color.FromArgb(0,7,209);colormap[1].NewColor=Color.Gray;colormap[2].OldColor=Color.FromArgb(0,14,214);colormap[2].NewColor=Color.Gray;colormap[3].OldColor=Color.FromArgb(0,21,218);colormap[3].NewColor=Color.Gray;colormap[4].OldColor=Color.FromArgb(0,28,223);colormap[4].NewColor=Color.Gray;colormap[5].OldColor=Color.FromArgb(0,36,227);colormap[5].NewColor=Color.DarkGray;colormap[6].OldColor=Color.FromArgb(0,43,231);colormap[6].NewColor=Color.DarkGray;colormap[7].OldColor=Color.FromArgb(0,50,236);colormap[7].NewColor=Color.DarkGray;colormap[8].OldColor=Color.FromArgb(0,57,240);colormap[8].NewColor=Color.DarkGray;colormap[9].OldColor=Color.FromArgb(0,64,245);colormap[9].NewColor=Color.DarkGray;colormap[10].OldColor=Color.FromArgb(0,72,250);//-2.0colormap[10].NewColor=Color.Silver;colormap[11].OldColor=Color.FromArgb(0,85,238);colormap[11].NewColor=Color.Silver;colormap[12].OldColor=Color.FromArgb(0,99,227);colormap[12].NewColor=Color.Silver;colormap[13].OldColor=Color.FromArgb(0,112,216);colormap[13].NewColor=Color.Silver;colormap[14].OldColor=Color.FromArgb(0,126,205);colormap[14].NewColor=Color.Silver;colormap[15].OldColor=Color.FromArgb(0,140,194);colormap[15].NewColor=Color.LightGray;colormap[16].OldColor=Color.FromArgb(0,153,183);colormap[16].NewColor=Color.LightGray;colormap[17].OldColor=Color.FromArgb(0,167,172);colormap[17].NewColor=Color.LightGray;colormap[18].OldColor=Color.FromArgb(0,180,161);colormap[18].NewColor=Color.LightGray;colormap[19].OldColor=Color.FromArgb(0,194,150);colormap[19].NewColor=Color.LightGray;colormap[20].OldColor=Color.FromArgb(0,208,139);//-1.0colormap[20].NewColor=Color.Gainsboro;colormap[21].OldColor=Color.FromArgb(6,212,130);colormap[21].NewColor=Color.Gainsboro;colormap[22].OldColor=Color.FromArgb(12,216,121);colormap[22].NewColor=Color.Gainsboro;colormap[23].OldColor=Color.FromArgb(18,220,113);colormap[23].NewColor=Color.Gainsboro;colormap[24].OldColor=Color.FromArgb(25,224,104);colormap[24].NewColor=Color.Gainsboro;colormap[25].OldColor=Color.FromArgb(31,228,96);colormap[25].NewColor=Color.WhiteSmoke;colormap[26].OldColor=Color.FromArgb(37,233,88);colormap[26].NewColor=Color.WhiteSmoke;colormap[27].OldColor=Color.FromArgb(44,237,79);colormap[27].NewColor=Color.WhiteSmoke;colormap[28].OldColor=Color.FromArgb(50,241,71);colormap[28].NewColor=Color.WhiteSmoke;colormap[29].OldColor=Color.FromArgb(56,245,62);colormap[29].NewColor=Color.WhiteSmoke;colormap[30].OldColor=Color.FromArgb(63,250,54);//0.0colormap[30].NewColor=Color.White;colormap[31].OldColor=Color.FromArgb(75,250,49);colormap[31].NewColor=Color.White;colormap[32].OldColor=Color.FromArgb(88,250,45);colormap[32].NewColor=Color.White;colormap[33].OldColor=Color.FromArgb(100,251,41);colormap[33].NewColor=Color.White;colormap[34].OldColor=Color.FromArgb(113,251,37);colormap[34].NewColor=Color.White;colormap[35].OldColor=Color.FromArgb(125,252,33);//0.5colormap[35].NewColor=Color.FromArgb(135,206,235);colormap[36].OldColor=Color.FromArgb(138,252,28);colormap[36].NewColor=Color.FromArgb(135,206,235);colormap[37].OldColor=Color.FromArgb(151,253,24);colormap[37].NewColor=Color.FromArgb(135,206,235);colormap[38].OldColor=Color.FromArgb(163,253,20);colormap[38].NewColor=Color.FromArgb(135,206,235);colormap[39].OldColor=Color.FromArgb(176,254,16);colormap[39].NewColor=Color.FromArgb(135,206,235);colormap[40].OldColor=Color.FromArgb(189,255,12);//1.0colormap[40].NewColor=Color.FromArgb(135,206,235);colormap[41].OldColor=Color.FromArgb(195,254,10);colormap[41].NewColor=Color.FromArgb(135,206,235);colormap[42].OldColor=Color.FromArgb(202,254,9);colormap[42].NewColor=Color.FromArgb(135,206,235);colormap[43].OldColor=Color.FromArgb(208,254,8);colormap[43].NewColor=Color.FromArgb(135,206,235);colormap[44].OldColor=Color.FromArgb(215,254,7);colormap[44].NewColor=Color.FromArgb(135,206,235);colormap[45].OldColor=Color.FromArgb(222,255,5);//1.5colormap[45].NewColor=Color.FromArgb(144,238,144);colormap[46].OldColor=Color.FromArgb(228,254,4);colormap[46].NewColor=Color.FromArgb(144,238,144);colormap[47].OldColor=Color.FromArgb(235,255,3);colormap[47].NewColor=Color.FromArgb(144,238,144);colormap[48].OldColor=Color.FromArgb(241,254,2);colormap[48].NewColor=Color.FromArgb(144,238,144);colormap[49].OldColor=Color.FromArgb(248,255,1);colormap[49].NewColor=Color.FromArgb(144,238,144);colormap[50].OldColor=Color.FromArgb(255,255,0);//2.0colormap[50].NewColor=Color.FromArgb(144,238,144);colormap[51].OldColor=Color.FromArgb(254,251,0);colormap[51].NewColor=Color.FromArgb(144,238,144);colormap[52].OldColor=Color.FromArgb(254,248,0);colormap[52].NewColor=Color.FromArgb(144,238,144);colormap[53].OldColor=Color.FromArgb(254,244,0);colormap[53].NewColor=Color.FromArgb(144,238,144);colormap[54].OldColor=Color.FromArgb(254,241,0);colormap[54].NewColor=Color.FromArgb(144,238,144);colormap[55].OldColor=Color.FromArgb(255,238,0);//2.5colormap[55].NewColor=Color.FromArgb(255,230,0);colormap[56].OldColor=Color.FromArgb(254,234,0);colormap[56].NewColor=Color.FromArgb(255,230,0);colormap[57].OldColor=Color.FromArgb(255,231,0);colormap[57].NewColor=Color.FromArgb(255,230,0);colormap[58].OldColor=Color.FromArgb(254,227,0);colormap[58].NewColor=Color.FromArgb(255,230,0);colormap[59].OldColor=Color.FromArgb(255,224,0);colormap[59].NewColor=Color.FromArgb(255,230,0);colormap[60].OldColor=Color.FromArgb(255,221,0);//3.0colormap[60].NewColor=Color.FromArgb(255,230,0);colormap[61].OldColor=Color.FromArgb(254,213,0);colormap[61].NewColor=Color.FromArgb(255,230,0);colormap[62].OldColor=Color.FromArgb(254,205,0);colormap[62].NewColor=Color.FromArgb(255,230,0);colormap[63].OldColor=Color.FromArgb(254,197,0);colormap[63].NewColor=Color.FromArgb(255,230,0);colormap[64].OldColor=Color.FromArgb(254,190,0);colormap[64].NewColor=Color.FromArgb(255,230,0);colormap[65].OldColor=Color.FromArgb(255,182,0);//3.5colormap[65].NewColor=Color.FromArgb(255,165,0);colormap[66].OldColor=Color.FromArgb(254,174,0);colormap[66].NewColor=Color.FromArgb(255,165,0);colormap[67].OldColor=Color.FromArgb(255,167,0);colormap[67].NewColor=Color.FromArgb(255,165,0);colormap[68].OldColor=Color.FromArgb(254,159,0);colormap[68].NewColor=Color.FromArgb(255,165,0);colormap[69].OldColor=Color.FromArgb(255,151,0);colormap[69].NewColor=Color.FromArgb(255,165,0);colormap[70].OldColor=Color.FromArgb(255,144,0);//4.0colormap[70].NewColor=Color.FromArgb(255,165,0);colormap[71].OldColor=Color.FromArgb(254,136,0);colormap[71].NewColor=Color.FromArgb(255,165,0);colormap[72].OldColor=Color.FromArgb(254,128,0);colormap[72].NewColor=Color.FromArgb(255,165,0);colormap[73].OldColor=Color.FromArgb(254,121,0);colormap[73].NewColor=Color.FromArgb(255,165,0);colormap[74].OldColor=Color.FromArgb(254,113,0);colormap[74].NewColor=Color.FromArgb(255,165,0);colormap[75].OldColor=Color.FromArgb(255,106,0);//4.5colormap[75].NewColor=Color.FromArgb(255,0,0);colormap[76].OldColor=Color.FromArgb(254,98,0);colormap[76].NewColor=Color.FromArgb(255,0,0);colormap[77].OldColor=Color.FromArgb(255,90,0);colormap[77].NewColor=Color.FromArgb(255,0,0);colormap[78].OldColor=Color.FromArgb(254,83,0);colormap[78].NewColor=Color.FromArgb(255,0,0);colormap[79].OldColor=Color.FromArgb(255,75,0);colormap[79].NewColor=Color.FromArgb(255,0,0);colormap[80].OldColor=Color.FromArgb(255,68,0);//5.0colormap[80].NewColor=Color.FromArgb(255,0,1);colormap[81].OldColor=Color.FromArgb(254,61,0);colormap[81].NewColor=Color.FromArgb(255,0,1);colormap[82].OldColor=Color.FromArgb(253,54,0);colormap[82].NewColor=Color.FromArgb(255,0,1);colormap[83].OldColor=Color.FromArgb(252,47,0);colormap[83].NewColor=Color.FromArgb(255,0,1);colormap[84].OldColor=Color.FromArgb(251,40,0);colormap[84].NewColor=Color.FromArgb(255,0,1);colormap[85].OldColor=Color.FromArgb(250,33,0);//5.5colormap[85].NewColor=Color.FromArgb(255,192,203);colormap[86].OldColor=Color.FromArgb(249,27,0);colormap[86].NewColor=Color.FromArgb(255,192,203);colormap[87].OldColor=Color.FromArgb(248,20,0);colormap[87].NewColor=Color.FromArgb(255,192,203);colormap[88].OldColor=Color.FromArgb(247,13,0);colormap[88].NewColor=Color.FromArgb(255,192,203);colormap[89].OldColor=Color.FromArgb(246,6,0);colormap[89].NewColor=Color.FromArgb(255,192,203);colormap[90].OldColor=Color.FromArgb(245,0,0);//6.0colormap[90].NewColor=Color.FromArgb(255,192,204);colormap[91].OldColor=Color.FromArgb(238,0,0);colormap[91].NewColor=Color.FromArgb(255,192,204);colormap[92].OldColor=Color.FromArgb(230,0,0);colormap[92].NewColor=Color.FromArgb(255,192,204);colormap[93].OldColor=Color.FromArgb(223,0,0);colormap[93].NewColor=Color.FromArgb(255,192,204);colormap[94].OldColor=Color.FromArgb(215,0,0);colormap[94].NewColor=Color.FromArgb(255,192,204);colormap[95].OldColor=Color.FromArgb(208,0,0);//6.5colormap[95].NewColor=Color.FromArgb(238,0,238);colormap[96].OldColor=Color.FromArgb(200,0,0);colormap[96].NewColor=Color.FromArgb(238,0,238);colormap[97].OldColor=Color.FromArgb(192,0,0);colormap[97].NewColor=Color.FromArgb(238,0,238);colormap[98].OldColor=Color.FromArgb(185,0,0);colormap[98].NewColor=Color.FromArgb(238,0,238);colormap[99].OldColor=Color.FromArgb(177,0,0);colormap[99].NewColor=Color.FromArgb(238,0,238);//7.0};//ImageAttributesオブジェクトの作成System.Drawing.Imaging.ImageAttributesia=newSystem.Drawing.Imaging.ImageAttributes();//簡潔に整理したテーブルを設定するia.SetRemapTable(colormap);colormap=null;g.DrawImage(img,0,0);//色を変換するg.DrawImage(img,newRectangle(0,0,img.Width,img.Height),0,0,img.Width,img.Height,GraphicsUnit.Pixel,ia);img.Dispose();img=null;ia.Dispose();ia=null;//Graphicsオブジェクトのリソースを解放するg.Dispose();g=null;bitmap=canvas;canvas=null;Console.WriteLine("処理に成功");//成功したことをコンソールに表示。}catch(Exceptionex){Console.WriteLine(ex);//エラーをコンソールに表示。}}
開始してコンソールにこう表示されたらこれで成功です。
以上でSTEP1は終了です。
STEP2 -取得した画像を処理する
- まずどうするのか
まずそもそも画像を取得したところでどうすればいいのでしょうか。強震モニタのGif画像サイズは352x400です。この中から観測点の値を抽出して検知しなければいけません。
↑強震モニタのGif画像。352x400のサイズはあるが実際観測点の部分は全体に比べて少ない。
勘のいい方はもうわかったかもしれませんが、この画像をfor文で回して色をGetpixelというものを使って色を抽出していきます。ここでようやくなぜ100色を15色にしたのかがわかりましたね。
for文の中でいちいちDictionaryやifなどで100色の色を判断するのは効率が悪いです。(ただ、細かい数値まで出したいという方は生の画像を使い、きちんと100色判断しましょう。)
※今回の目的はあくまで地震を検知するだけなので、細かい数値までは解説しません。
CPU使用率の問題
今回、1秒ごとに画像を取得するので、毎秒毎秒for文でこの画像のピクセルすべてをとるというのはものすごく重くなるわけではありませんが、少し負荷がかかってしまいます。そのため、画像を2ピクセルごとに解析していきます。これで全体を解析する方法よりも処理、かかる時間が単純計算で半分になります。- 2ピクセルごとじゃダメじゃん?
強震モニタの画像上での一つ一つの観測点の大きさは3x3となっているので、2ピクセルごとだと必ず取得することができます。
- 2ピクセルごとじゃダメじゃん?
地域はどうするのか(その1)
これはかなり重要な課題です。地震を検知した!といっても、どこで地震が起きているのかがわかりません。いろいろな方法はあると思いますが、本記事ではブロック法で考えていきます。(詳しいことは後程。)Let's Write
ではまずは細かいことは後にして取得した画像を2ピクセルずつ取得しましょう。
ですがその前に、色を定義しておく必要があります。
Colorcu=Color.FromArgb(0,0,0);
こちらを private async void timer1_Tick(object sender, EventArgs e)
の上に書いておいてください。
privateasyncvoidtimer1_Tick(objectsender,EventArgse){........Console.WriteLine("処理に成功");for(inty=1;y<400;y=y+2){for(intx=1;x<352;x=x+2){cu=bitmap.GetPixel(x,y);Console.WriteLine(cu);}}awaitTask.Delay(10);catch.......
catchとConsoleの間にこれを書きましょう。これで開始してみてください。
こんな感じで大量にコンソールに色の情報が流れ込んできます。またCPU使用率も......
これではただの重いソフトです......
さて、次にまいりましょう。
- いらない部分は除外する
実はこんなに重いのにはある理由があります。色をデバッグするために書いたConsole.WriteLineくんです。こちらをこうすると.....?
//Console.WriteLine(cu);
まるで見違えたかのように軽くなります。なぜわざわざこうしたかというと、ただ色が正しくとれているか確認するだけです。(は?)
とやかく、色が正常にとれていることがわかったので、次に進めます。
- 色をさらに扱いやすくする
先程15色に変換しましたが、これをさらに3つに分別していきます。
実は先ほどのテーブルは、震度ごとに(0以下(6色),1,2,3,4,5弱,5強,6弱,6強,7)色がわかれています。こうしておくとのちのち便利になります。
まずは色を格納するint型変数を
Color cu = ....
の下に定義しておきます。
intgn=0,yw=0,rd=0;
次にこのコードをxのほうのfor文のcu = ....
の下に書きましょう。
if(cu==Color.FromArgb(238,0,238)||cu==Color.FromArgb(255,192,204)||cu==Color.FromArgb(255,192,203)||cu==Color.FromArgb(255,0,1)||cu==Color.FromArgb(255,0,0)||cu==Color.FromArgb(255,165,0)){rd++;//震度4以上を赤判定}if(cu==Color.FromArgb(255,230,0)||cu==Color.FromArgb(144,238,144)||cu==Color.FromArgb(135,206,235)){yw++;//震度1以上震度3以下を黄判定}if(cu==Color.FromArgb(255,255,255)||cu==Color.FromArgb(245,245,245)||cu==Color.FromArgb(220,220,220)){gn++;//微弱な揺れを緑判定}
次はfor文の外にコンソールに出力するコードを書きます
Console.WriteLine("R,Y,G"+rd+","+yw+","+gn);//赤黄緑を出力//無限にふえ続けてしまうのでリセットrd=0;yw=0;gn=0;
どうでしょう。きちんと出力されているでしょうか...?
- この値は何だ この値は、強震モニタ上の 緑 なら微弱な揺れを示している点の数黄なら震度1~3の揺れを示している点の数、赤なら震度4以上の揺れを観測している点の数を表しています。
地域をどうするか(その2)
ここ重要。
ついに色が取れました。では、「どうこれを扱うか」です。先ほどは全体の色が取れただけで地域ごとのデータはまったくありません。そのため地域を判断する必要があるのですが、少し面倒.....
↑強震モニタをブロック化したマップ
本記事では私が開発しているQEQ3と全く同じブロック単位をもとに解説しようと思います。
ここではDictionaryを追加するだけです。表示する
Dictionary<int,string>prefkeys=newDictionary<int,string>//地域{{1,"北海道"},{2,"北海道"},{3,"北海道"},{4,"北海道"},{5,"北海道"},{6,"北海道"},{7,"北海道"},{8,"北海道"},{9,"北海道"},{10,"北海道"},{11,"北海道"},{12,"北海道"},{13,"北海道"},{14,"北海道"},{15,"北海道"},{16,"青森県"},{17,"青森秋田"},{18,"青森岩手"},{19,"秋田県"},{20,"岩手県"},{21,"新潟県"},{22,"山形県"},{23,"宮城県"},{24,"石川県"},{25,"石川富山"},{26,"新潟県"},{27,"福島県"},{28,"福島県"},{29,"島根県"},{30,"福井県"},{31,"富山岐阜"},{32,"長野県"},{33,"群馬栃木"},{34,"栃木茨城"},{35,"島根県"},{36,"鳥取県"},{37,"兵庫岡山"},{38,"福井滋賀"},{39,"岐阜愛知"},{40,"長野静岡"},{41,"神奈川県"},{42,"千葉県"},{43,"長崎県"},{44,"山口県"},{45,"広島愛媛"},{46,"香川岡山"},{47,"香川徳島"},{48,"大阪奈良"},{49,"愛知三重"},{50,"静岡県"},{51,"伊豆諸島"},{52,"佐賀県"},{53,"福岡熊本"},{54,"愛媛大分"},{55,"高知県"},{56,"徳島県"},{57,"和歌山県"},{58,"八丈島"},{59,"長崎熊本"},{60,"熊本県"},{61,"宮崎県"},{62,"高知県"},{63,"鹿児島県"},{64,"宮崎県"},{65,"種子島"},{66,"奄美大島"},{67,"奄美諸島"},{68,"奄美諸島"},{69,"久米島"},{70,"沖縄本島"},{71,"八重山"},{72,"八重山"},};Dictionary<int,int>numx=newDictionary<int,int>//x方向{{1,237},{2,261},{3,253},{4,277},{5,301},{6,325},{7,238},{8,262},{9,286},{10,310},{11,213},{12,238},{13,262},{14,286},{15,213},{16,237},{17,226},{18,250},{19,226},{20,250},{21,196},{22,220},{23,244},{24,148},{25,172},{26,196},{27,220},{28,244},{29,83},{30,131},{31,155},{32,179},{33,203},{34,227},{35,59},{36,83},{37,107},{38,131},{39,155},{40,179},{41,203},{42,227},{43,11},{44,35},{45,59},{46,83},{47,107},{48,131},{49,155},{50,179},{51,203},{52,11},{53,35},{54,59},{55,83},{56,107},{57,131},{58,217},{59,11},{60,35},{61,59},{62,83},{63,18},{64,42},{65,29},{66,131},{67,95},{68,119},{69,77},{70,101},{71,18},{72,42},};Dictionary<int,int>numy=newDictionary<int,int>//y方向{{1,14},{2,14},{3,38},{4,38},{5,38},{6,38},{7,62},{8,62},{9,62},{10,62},{11,78},{12,86},{13,86},{14,86},{15,102},{16,111},{17,135},{18,135},{19,159},{20,159},{21,183},{22,183},{23,183},{24,207},{25,207},{26,207},{27,207},{28,207},{29,231},{30,231},{31,231},{32,231},{33,231},{34,231},{35,255},{36,255},{37,255},{38,255},{39,255},{40,255},{41,255},{42,255},{43,279},{44,279},{45,279},{46,279},{47,279},{48,279},{49,279},{50,279},{51,279},{52,303},{53,303},{54,303},{55,303},{56,303},{57,303},{58,303},{59,327},{60,327},{61,327},{62,327},{63,351},{64,351},{65,375},{66,77},{67,101},{68,101},{69,125},{70,125},{71,177},{72,170},};
こちらをtimer1_Tickの外に書きます。
次に元のfor文をすべてこちらに変更し、地域が判別できるようにします。
for(inti=1;i<73;i++){for(inty=numy[i];y<numy[i]+24;y=y+2){for(intx=numx[i];x<numx[i]+24;x=x+2){cu=bitmap.GetPixel(x,y);if(cu==Color.FromArgb(238,0,238)||cu==Color.FromArgb(255,192,204)||cu==Color.FromArgb(255,192,203)||cu==Color.FromArgb(255,0,1)||cu==Color.FromArgb(255,0,0)||cu==Color.FromArgb(255,165,0)){rd++;//震度4以上を赤判定}if(cu==Color.FromArgb(255,230,0)||cu==Color.FromArgb(144,238,144)||cu==Color.FromArgb(135,206,235)){yw++;//震度1以上震度3以下を黄判定}if(cu==Color.FromArgb(255,255,255)||cu==Color.FromArgb(245,245,245)||cu==Color.FromArgb(220,220,220)){gn++;//微弱な揺れを緑判定}}}Console.WriteLine("R,Y,G,Pref"+rd+","+yw+","+gn+","+prefkeys[i]);//赤黄緑都道府県を出力//無限にふえ続けてしまうのでリセットrd=0;yw=0;gn=0;}
コンソールにはこんなものが.....!
ついに地域を取得することができました。
- 検知を判断する
さて、必要な情報はすべてそろいました。いよいよ終わりに近づいてきています。
あと一息ですね!
次に検知を判断するif文をiのfor文に書いた
Console.WriteLine("R,...)
の下に書いていきます。
boolEQ=false;//これはint gn...の付近に書いておく//ここからConsole.WriteLine("R,...)の下にif(i==33||i==34||i==41||i==42)//生活振動の多いブロックでは閾値を上げる{if(rd>=9){EQ=true;label3.Text="地域:"+prefkeys[i];}elseif(rd>=6&&yw>=6){EQ=true;label3.Text="地域:"+prefkeys[i];}elseif(yw>=9){EQ=true;label3.Text="地域:"+prefkeys[i];}elseif(yw>=6&&gn>=6){EQ=true;label3.Text="地域:"+prefkeys[i];}}else//通常ブロック{if(rd>=5){EQ=true;label3.Text="地域:"+prefkeys[i];}elseif(rd>=3&&yw>=3){EQ=true;label3.Text="地域:"+prefkeys[i];}elseif(yw>=5){EQ=true;label3.Text="地域:"+prefkeys[i];}elseif(yw>=3&&gn>=5){EQ=true;label3.Text="地域:"+prefkeys[i];}}
生活振動の多い首都圏では誤検知を抑えるために閾値を上げています。
そしてiのfor文の下にこれを書いておきます
EQ=false;
お分かりの通り、あとは検知フラグEQ
の状態によって出力しましょう。
長くなりましたが以上でSTEP2は終了です。
STEP3 -ユーザーへ出力する
長くなっていますが、もう終わりに近づいています。もう一息!iのfor文の外に書きましょう。
if(EQ==true){label1.Text="次の地域で揺れを検出しました";}else{label1.Text="地震を検出していません";label3.Text="地域 ーー";}