宇宙仮面の
C# Programming

 

Image簡単なモバイルアプリケーション開発7〜Amedas 情報の表示

開発環境: Visual Studio 2005

1.目次

1.目次
2.目的
3.参考書
4.作り方
5.ダウンロード
6.ソースコード
7.まとめ

2.目的

WZero3 をいつも使っていますが、結構いらつくことがあります。それは特に外出先で雨が降りそうな時に、気象情報をチェックするためにブラウザを起動して、雨の状況などをチェックする ときです。気象庁のAmedas は、Java Scriptを正しく処理できず、W-Zero3標準の IE では気象情報の画像が表示できません。また、いくつか他のサイトの気象情報を渡り歩こうとしても、気象情報のページにたどり着くまで、メニューをいくつかたどる必要があ り、使い慣れていないとなかなかたどりつけなかったり、普段は画像を表示しないモードにしているので画像がないと何が何だかわからないページだったり、 画像を表示するにするにしておくと余分な広告まで表示されたりしてアクセスが遅くなったりして、とてもいらつきます。気象情報は、特に移動時に地下鉄や電車の中でチェックすることが多いので、とぎれとぎれの電波状況の中では、とても我慢が出来ないほどいらつきます。

そこで、直接気象庁の防災気象情報をアクセスするための 専用アプリケーションを作成しました。.NET Compact Frameworkでは、WebBrowserコントロールがサポートされているので、とっても簡単に作れることはわかっていたのですが、なかなか手つかずでした。

今回作成したのは、次のような表示をするアプリケーションで、最初は簡単に雨の動きさえ見れればと思いましたが、実装する段階で気象庁の防災気象情報を見ているうちに、あれもこれもと、いろいろと意味もなく追加してしまいました。もう少し手を加えれば、動画での表示もできないことはないのですが、通信量の問題で静止画像をワンショットで取得するだけに留めました。

ImageImageImageImage

今回のアプリケーションでは、気象庁の防災気象情報の気象情報URLにアクセスするだけの簡単なものです。.NET Compact Framework では、WebBrowser コンポーネントが標準で用意されていますので、URLを渡してあげるだけで、簡単にこのようなアプリケーションが組めますので、試してみてはいかがでしょうか。

注意:

  • このアプリケーションによる気象情報は、すべての気象庁の防災気象情報をカバーしていませんので、クリティカルな用途には使わないでください。
  • 種類、地域の設定を保存する機能は実装していません。気が向けば・・・
  • 気象庁の防災気象情報の気象情報URLが変更になってしまうと、動かなくなってしまいます。

3.参考書

(1) 気象庁の防災気象情報

4.作り方

4.1 気象庁の防災気象情報

気象庁の防災気象情報では、次のようにメニューから、種類、地域、時間を指定すると、次のような画像が表示されます。

Image

画像のURLは、例えば

http://www.jma.go.jp/jp/amedas/imgs/temp/206/200707161600-00.png

というようになっています。ここで、206は関東地方、200707161600は、2007年7月16日、16:00:00を表しています。また、"-00"は、今後の予想を表しています。

ただし、これらのパターンは、それぞれの表示種類によって微妙に異なっており、気象庁の防災気象情報のページでは、JavaScriptでサーバ側から情報を適宜更新して、画像を表示するようになっています。

たとえば、警報に関しては、時間の指定はなく、関東地方の気象警報・注意報は、

http://www.jma.go.jp/jp/warn/206.html

であるのに対して、レーダー・降水ナウキャストの1時間後の予想に関しては、

http://www.jma.go.jp/jp/radnowc/imgs/nowcast/000/200707161940-06.png

のようになります。

したがって、これらのパターンを分析して、それにあったURLを渡してあげる必要があります。

このため、全体の動作自体は、メニューでパラメータを設定して、適切なURLを作成し、WebBrowserに渡してあげるという簡単な動作になりますが、パラメータの作成が一律に実装できず、気象情報ごとに個別に作成する必要があり、実装上つらつらと書く必要があり、ちょっと面倒です。

4.2 プロジェクトの作成

  • プロジェクトの新規作成
    Visual Studioのメニューからプロジェクトの新規作成で、プロジェクトの種類で [スマートデバイス] → [デバイスアプリケーション] を選択します。 
  • ターゲットデバイスの設定
    エミュレータでも可能ですが、エミュレータではレスポンスが悪く、かったるいです。そこで、Windows Mobile 5.0 Pocket PC Deviceを指定して、実機上で開発します。 
  • Formの [FormFactor]プロパティ
    [Windows Mobile 5.0 Pocket PC VGA] を指定します。

4.3 表示部分の作成

  1. フォームファクターの変更
    次のようにユーザーコントロールが作成されますので、フォームファクターを [Windows Mobile 5.0 Pocket PC Square VGA] に変更します。
     
  2. WebBrowserコントロールの貼り付け
    ツールボックスより WebBrowser をForm上にDrag&Dropし、Dockプロパティを "Fill" に設定します。

4.4 メニューの作成

メニューには、気象情報の種類により、次のように追加します。

種類
  →天気予報
  →レーダー・降水ナウキャスト
  →気象衛星→赤外線、可視光、水蒸気
  →・・・

メニュー
  →地方
  →時間
  →About
  →終了

それぞれ、必要なイベントハンドラを追加します。

4.5 地方メニューの追加

地方メニューを追加するメソッドを追加します。直接GUIから設定してもOKですが、メインテナンスを考えて、まとめて実装しました。デフォルトでは、"全国"としてありますが、設定ファイルから読み込むようにしたほうが良いかもしれません。

/// <summary>
///
地方メニューを作成
/// </summary>
private void CreateMenuLocation()
{
  ameLocation.Add(
new AmedasLocation("000", "全国"));
  ameLocation.Add(
new AmedasLocation("201", "北海道地方(北西部)"));
  ameLocation.Add(
new AmedasLocation("202", "北海道地方(東部"));
  ・・・中略

 
foreach (AmedasLocation al in ameLocation)
  {
   
MenuItem mi = new MenuItem();
    mi.Text = al.Text;
    mi.Checked =
false;
    menuItemLocation.MenuItems.Add(mi);
    mi.Click +=
new System.EventHandler(this.menuItemLocationEvent);
  }
  LocationCode =
"000";
  menuItemLocation.MenuItems[0].Checked =
true;
}

4.6 種類を選択した場合の動作

気象情報の種類を選択した場合の動作を実装します。

/// <summary>
///
気温を選択
/// </summary>
private void menuItemTemperature_Click(object sender, EventArgs e)
{
  ClearAllMenuItemTypeChecked();
  menuItemTemperature.Checked =
true;

  menuItemLocation.Enabled =
true;
  menuItemTimeMain.Enabled =
true;

  SetNormalTimeMenu();
  SetDefaultTime();

  ShowAmedas();
}

最初の2行は、メニューにチェックマークを設定するためのものです。まず、種類選択のメニューのチェックをすべて外し、選択されたメニューにチェックを入れます。

次の2行は、地域情報の選択メニューを有効にするかどうか、時間選択メニューを有効にするかどうかです。気温の場合は、どちらも指定することができますので、両方とも "true" を指定します。

次の2行は、時間メニューの設定を行います。気象情報によって、設定可能な時間が違ってきます。気温では、過去1時間おきに指定することができます。そのために、SetNormalTimeMenu()メソッドです。また、表示する気象情報が変更されたので、SetDefaultTime() メソッドによりデフォルトの表示時間を元に(現在時刻)に戻します。

最後のShowAmedas()メソッドが、ブラウザに気象情報を表示するためのコードです。

4.7 気象情報を表示するためのメソッドの実装

次に気象情報を表示するためのメソッドを実装します。

/// <summary>
///
アメダス情報を表示する
/// </summary>
private void ShowAmedas()
{
 
Uri amedasUrl = null;
 
if (menuItemRadar.Checked)
  {
    amedasUrl =
new Uri("http://www.jma.go.jp/jp/radnowc/imgs/radar/" + LocationCode + "/" + timeString);
   
foreach (MenuItem mi in menuItemTimeMain.MenuItems)
    {
     
if (mi.Checked && mi.Text.EndsWith("(予想)"))
      {
        amedasUrl =
new Uri("http://www.jma.go.jp/jp/radnowc/imgs/nowcast/" + LocationCode + "/" + timeString);
       
break;
      }
    }
  }
 
else if (menuItemRain.Checked)
    amedasUrl =
new Uri("http://www.jma.go.jp/jp/amedas/imgs/rain/" + LocationCode + "/" + timeString);
 
else if (menuItemTemperature.Checked)
    amedasUrl =
new Uri("http://www.jma.go.jp/jp/amedas/imgs/temp/" + LocationCode + "/" + timeString);
  ・・・中略

  webBrowser1.Navigate(amedasUrl);
}

  1. 基本的には、if (menuItemTemperature.Checked)で、気象情報の種類にチェックされているものを探す。
  2. 該当するURLを組み立てる。
     amedasUrl = new Uri("http://www.jma.go.jp/jp/amedas/imgs/temp/" + LocationCode + "/" + timeString);
  3. WebBrowserコントロールのNavigateメソッドに、表示したいURLを渡して、表示する。

という簡単な流れになります。ただし、気象情報の種類により、生成するURLが異なりますので、それぞれ適切なURLを生成するようにします。URL自体は、気象情報の種類+[場所コード]+[時間情報]という形です。

ただし、レーダーの降水ナウキャストだけは、時間指定が1時間先までの予想が可能なので、その部分の実装が少し変則になっています。

4.8 時間指定,、およびその更新

ところで、気象情報は時間ごとに新しい情報が追加されていきます。このため、時間メニューも適宜更新して、最新情報を追加してあげる必要があります。そこで、タイマーを追加して、10分おきに、時間指定を更新するようにします。

/// <summary>
///
時間指定を更新
/// </summary>
private void timer1_Tick(object sender, EventArgs e)
{
  CreateTimeMenu();
  CreateTimeMenuForNowCast();

 
if (menuItemRadar.Checked)
    SetNowCastTimeMenu();
 
else
   
SetNormalTimeMenu();
}

時間メニューには、厳密に処理しようと思うと、いくつかのバリエーションが発生します。しかし、あまり細かく指定してもあまり意味がないし、実装も複雑になるので、2種類だけにしました。

  • NowCastTimeMenu 現在時刻から、過去6時間、および今後の10分おきに1時間後まで。
  • NormalTimeMenu 現在時刻から、過去12時間まで。

そこで、NormalTime自体は、AmedasTimeというクラスのジェネリックとして、そこに時間情報を格納するようにしました。AmedasTimeというクラスには、時間メニューに表示するための文字列、時間(DateTime型)、URLに渡す引数、予想があるかどうかを識別するためのNowCastブール値を持たせました。NowCastブール値を持たせているのがちょっと汚くて嫌です。継承を使ってという手もあると思いますが、より複雑になりそうなので、べたに書きました。

public class AmedasTime
{
 
public string Text;
 
public DateTime Time;
 
public string TimeCommand;
 
public bool NowCast;

 
public AmedasTime(string menuText, DateTime time, string timeCmd, bool nowCast)
  {
    Text = menuText;
    Time = time;
    TimeCommand = timeCmd;
    NowCast = nowCast;
  }
}

List<AmedasTime> normalTime = new List<AmedasTime>();

/// <summary>
///
レーダー・降水ナウキャスト以外の場合の時間メニューを作成
/// 過去12時間まで1時間刻み
/// </summary>
private void CreateTimeMenu()
{
  // リアルタイムでAmedasのデータは取得できない。
  // 大体20分ぐらい遅れるので、現在時刻ー20分する。
  DateTime nowMinus20Min = DateTime.Now.Subtract(new TimeSpan(0, 20, 0));

  // その時の時間(H)をベースの時間とする。
  DateTime baseHour = new DateTime(
  nowMinus20Min.Year, nowMinus20Min.Month, nowMinus20Min.Day,
    nowMinus20Min.Hour, 0, 0);

  normalTime.Clear();

  for (int i = 0; i < 12; i++)
  {
    DateTime t = baseHour.Subtract(new TimeSpan(i, 0, 0));
    string timeCmd = t.ToString("yyyyMMddHHmm") + "-00.png";
    if (i == 0)
      defaultTimeString = timeCmd;
    normalTime.Add(new AmedasTime(t.ToString("HH:mm"), t, timeCmd, false));
  }
}

これにより、適切な時間メニューをメニューにセットし、なおかつ時間メニューを定期的に更新するようにします。

同様にナウキャストのための時間メニューも実装します。

4.9 地域を選択した場合の動作

次に、地域を選択した場合の動作を実装します。地域情報は、AmedasLocationというクラスのジェネリックとして、そこに地域情報を格納するようにしました。AmedasLocationというクラスには、地域メニューに表示するための文字列、地域コードを持たせました。

public class AmedasLocation
{
 
public string Text;
 
public string LocationCode;
 
public bool Checked;

 
public AmedasLocation(string code, string locationName)
  {
    Text = locationName;
    LocationCode = code;
  }
}

次に、地域を選択した場合のイベントハンドラを追加します。ここでは、地域メニューのチェックマークを更新します。次に、選択した地域メニューと一致する地域コードを検索し、LocationCodeに格納します。そして、ShowAmedas()メソッドで、表示を行います。

///
<summary>
///
場所を選択した
/// </summary>
private void menuItemLocationEvent(object sender, EventArgs e)
{
  // Location メニューのチェックマークを更新
 
foreach (MenuItem mi in menuItemLocation.MenuItems)
  mi.Checked = false;
  ((MenuItem)sender).Checked = true;

  // メニューと一致するAmedasLocation情報を検索
 
AmedasLocation al = ameLocation.Find(
    delegate(AmedasLocation aloc)
    {
      return aloc.Text == ((MenuItem)sender).Text;
    }
  );

  // 場所コードをAmedasLocationから取得
 
if (al != null)
    LocationCode = al.LocationCode;
  ShowAmedas();
}
 

4.10 残りの作業

次に、お決まりの次の作業を行えば、完成です。
アイコンの追加
バージョン情報の表示
インストーラの追加

5. ダウンロード

Version 1.0.0.0  2007/7/16 WZero3DeAmedas.cab

WZero3DeAmedas Version 2.0.0.0 Beta1 2007/8/3 を公開しました。

動作環境

.NET Compact Framework 2.0 が必要です。

検証

WS004SH で動作確認をしました。

注意 

  • 動作は保証しません。クリティカルな用途には使わないでください。
  • 気象庁の防災気象情報の一部しか対応ていません。
  • 気象庁の防災気象情報の気象情報URLが変更になってしまうと、動かなくなってしまいます。
  • 1つの画像に対して、数十KB〜数百KBの通信が必要です。通信コストにご注意ください。
  • データが無い場合、"Not Found" エラーになる場合があります。原因としては、パラメータを渡す際のバグ、データ作成が間に合っていないこととが考えられます。

6. ソースコード

主なソースコードです。

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;

namespace Uchukamen.WZero3.WZero3DeAmedas
{
  public partial class Form1 : Form
  {
    public class AmedasLocation
    {
      public string Text;
      public string LocationCode;
      public AmedasLocation(string code, string locationName)
      {
        Text = locationName;
        LocationCode = code;        
      }
    }

    public class AmedasTime
    {
      public string Text;
      public DateTime Time;
      public string TimeCommand;
      public bool NowCast;
      public AmedasTime(string menuText, DateTime time, 
                        string timeCmd, bool nowCast)
      {
        Text = menuText;
        Time = time;
        TimeCommand = timeCmd;
        NowCast = nowCast;
      }
    }

    public Form1()
    {
      InitializeComponent();
    }


    private void Form1_Load(object sender, EventArgs e)
    {
      // Create Location Menu
      CreateMenuLocation();

      // Create Time Menu
      CreateTimeMenu();
      CreateTimeMenuForNowCast();

      timer1.Interval = 10 * 60 * 1000; // 10 min
      timer1.Enabled = true;

      SetNowCastTimeMenu();
    }

    /// 
    /// 終了
    /// 
    private void menuItemTerminate_Click(object sender, EventArgs e)
    {
      Application.Exit();
    }

    /// 
    /// About、バージョン情報
    /// 
    private void menuItemAbout_Click(object sender, EventArgs e)
    {
      FormAbout fb = new FormAbout();
      fb.ShowDialog();
    }

    /// 
    /// 時間指定を更新
    /// 
    private void timer1_Tick(object sender, EventArgs e)
    {
      CreateTimeMenu();
      CreateTimeMenuForNowCast();

      if (menuItemRadar.Checked)
        SetNowCastTimeMenu();
      else
        SetNormalTimeMenu();
    }

    /// 
    /// アメダス情報を表示する
    /// 
    private void ShowAmedas()
    {
      Uri amedasUrl = null;

      if (menuItemRadar.Checked)
      {
        amedasUrl = new Uri("http://www.jma.go.jp/jp/radnowc/imgs/radar/" 
           + LocationCode + "/" + timeString);
        foreach (MenuItem mi in menuItemTimeMain.MenuItems)
        {
          if (mi.Checked && mi.Text.EndsWith("(予想)"))
          {
            amedasUrl = new Uri("http://www.jma.go.jp/jp/radnowc/imgs/nowcast/" 
               + LocationCode + "/" + timeString);
            break;
          }
        }
      }
      else if (menuItemRain.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/amedas/imgs/rain/" 
          + LocationCode + "/" + timeString);
      else if (menuItemTemperature.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/amedas/imgs/temp/" 
          + LocationCode + "/" + timeString);
      else if (menuItemWind.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/amedas/imgs/wind/" 
          + LocationCode + "/" + timeString);
      else if (menuItemSun.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/amedas/imgs/sun/" 
          + LocationCode + "/" + timeString);
      else if (menuItemSnow.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/amedas/imgs/snow/" 
          + LocationCode + "/" + timeString);
      else if (menuItemWeather.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/yoho/images/g1/" 
          + LocationCode + "_telop_today.png");
      else if (menuItemInfrared.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/gms/imgs_c/0/infrared/1/" 
          + timeString);
      else if (menuItemVisible.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/gms/imgs_c/0/visible/1/" 
          + timeString);
      else if (menuItemWatervapor.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/gms/imgs_c/0/watervapor/1/" 
          + timeString);
      else if (menuItemTyphoon.Checked)
        amedasUrl = 
           new Uri("http://www.jma.go.jp/jp/typh/images/wide/all-00.png");
      else if (menuItemWindProf.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/windpro/imgs/000/" 
          + timeString);
      else if (menuItemWarnAll.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/99.png");
      else if (menuItemWarnRain.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/02.png");
      else if (menuItemWarnFlood.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/03.png");
      else if (menuItemWarnWind.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/04.png");
      else if (menuItemWarnWave.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/06.png");
      else if (menuItemWarnThunder.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/08.png");
      else if (menuItemWarnFog.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/10.png");
      else if (menuItemWarnLowTemp.Checked)
        amedasUrl = new Uri("http://www.jma.go.jp/jp/warn/imgs/" 
          + LocationCode + "/13.png");

      webBrowser1.Navigate(amedasUrl);
    }

    #region 位置メニュー

    List ameLocation = new List();
    private string LocationCode = "000";

    /// 
    /// 地方メニューを作成
    /// 
    private void CreateMenuLocation()
    {
      ameLocation.Add(new AmedasLocation("000", "全国"));
      ameLocation.Add(new AmedasLocation("201", "北海道地方(北西部)"));
      ameLocation.Add(new AmedasLocation("202", "北海道地方(東部"));
      ameLocation.Add(new AmedasLocation("203", "北海道地方(南西部)"));
      ameLocation.Add(new AmedasLocation("204", "東北地方(北部)"));
      ameLocation.Add(new AmedasLocation("205", "東北地方(南部)"));
      ameLocation.Add(new AmedasLocation("206", "関東地方"));
      ameLocation.Add(new AmedasLocation("207", "甲信地方"));
      ameLocation.Add(new AmedasLocation("208", "北陸地方(東部)"));
      ameLocation.Add(new AmedasLocation("209", "北陸地方(西部)"));
      ameLocation.Add(new AmedasLocation("210", "東海地方"));
      ameLocation.Add(new AmedasLocation("211", "近畿地方"));
      ameLocation.Add(new AmedasLocation("212", "中国地方"));
      ameLocation.Add(new AmedasLocation("213", "四国地方"));
      ameLocation.Add(new AmedasLocation("214", "九州地方(北部)"));
      ameLocation.Add(new AmedasLocation("215", "九州地方(南部)"));
      ameLocation.Add(new AmedasLocation("216", "奄美地方"));
      ameLocation.Add(new AmedasLocation("217", "沖縄・大東島地方"));
      ameLocation.Add(new AmedasLocation("219", "宮古・八重山地方"));

      foreach (AmedasLocation al in ameLocation)
      {
        MenuItem mi = new MenuItem();
        mi.Text = al.Text;
        mi.Checked = false;
        menuItemLocation.MenuItems.Add(mi);
        mi.Click += new System.EventHandler(this.menuItemLocationEvent);
      }

      LocationCode = "000";
      menuItemLocation.MenuItems[0].Checked = true;
    }

    /// 
    /// 地域を選択した
    /// 
    private void menuItemLocationEvent(object sender, EventArgs e)
    {
      // Location メニューのチェックマークを更新
      foreach (MenuItem mi in menuItemLocation.MenuItems)
        mi.Checked = false;
      ((MenuItem)sender).Checked = true;

      // メニューと一致するAmedasLocation情報を検索
      AmedasLocation al = ameLocation.Find(
        delegate(AmedasLocation aloc)
        {
          return aloc.Text == ((MenuItem)sender).Text;
        }
        );

      // 場所コードをAmedasLocationから取得
      if (al != null)
        LocationCode = al.LocationCode;

      ShowAmedas();
    }
    #endregion

    #region 時間メニュー

    List nowCastTime = new List();
    List normalTime = new List();

    /// 
    /// メニューから時間を選択した
    /// 
    private void menuItemTimeEvent(object sender, EventArgs e)
    {
      // 選択した時間のメニューをチェックする。
      foreach (MenuItem mi in menuItemTimeMain.MenuItems)
        mi.Checked = false;
      ((MenuItem)sender).Checked = true;

      // NowCast の場合は、時間の処理が違う
      if (menuItemRadar.Checked)
      {
        AmedasTime mt = nowCastTime.Find(
          delegate(AmedasTime t)
          {
            return t.Text == ((MenuItem)sender).Text;
          }
        );
        if (mt != null)
        {
          timeString = mt.TimeCommand;
        }
      }
      else
      {
        // 通常の場合は、normalTime を使用する。
        AmedasTime mt = normalTime.Find(
          delegate(AmedasTime t)
          {
            return t.Text == ((MenuItem)sender).Text;
          }
          );
        if (mt != null)
        {
          timeString = mt.TimeCommand;
        }
      }

      ShowAmedas();
    }

    /// 
    /// レーダー・降水ナウキャストの場合の時間メニューを作成
    /// 1時間後まで10分刻みの予想と
    /// 過去6時間まで1時間刻み
    /// 
    private void CreateTimeMenuForNowCast()
    {
      // リアルタイムでAmedasのデータは取得できない。
      // 大体20分ぐらい遅れるので、現在時刻ー20分する。
      DateTime nowMinus20Min = DateTime.Now.Subtract(new TimeSpan(0, 20, 0));
      // その時の時間(10分単位)をベースの時間(baseHourMin)とする。
      DateTime baseHourMin = nowMinus20Min.Subtract
         (new TimeSpan(0, nowMinus20Min.Minute % 10, 0));
      // その時の時間(1時間単位)をベースの時間(baseHour)とする。
      DateTime baseHour = new DateTime(
        nowMinus20Min.Year, nowMinus20Min.Month, nowMinus20Min.Day,
        nowMinus20Min.Hour, 0, 0);

      nowCastTime.Clear();

      // 1時間後まで10分刻みの予想
      for (int i = 6; i > 0; i--)
      {
        DateTime t = baseHourMin.Add(new TimeSpan(0, 10 * i, 0));
        string timeCmd = baseHourMin.ToString("yyyyMMddHHmm") + "-0" 
          + i.ToString() + ".png";
        nowCastTime.Add(new AmedasTime
          (t.ToString("HH:mm(予想)"), t, timeCmd, true));
      }

      // 過去6時間まで、1時間刻み
      for (int i = 0; i < 6; i++)
      {
        DateTime t = baseHour.Subtract(new TimeSpan(i, 0, 0));
        string timeCmd = t.ToString("yyyyMMddHHmm") + "-00.png";
        if (i == 0)
          timeString = timeCmd;
        nowCastTime.Add(new AmedasTime(t.ToString("HH:mm"), t, timeCmd, false));
      }

    }

    private void SetNowCastTimeMenu()
    {
      menuItemTimeMain.MenuItems.Clear();

      foreach (AmedasTime at in nowCastTime)
      {
        MenuItem mi = new MenuItem();
        mi.Text = at.Text;
        menuItemTimeMain.MenuItems.Add(mi);
        mi.Click += new System.EventHandler(this.menuItemTimeEvent);
      }
      menuItemTimeMain.MenuItems[6].Checked = true;

    }

    /// 
    /// レーダー・降水ナウキャスト以外の場合の時間メニューを作成
    /// 過去12時間まで1時間刻み
    /// 
    private void CreateTimeMenu()
    {
      // リアルタイムでAmedasのデータは取得できない。
      // 大体20分ぐらい遅れるので、現在時刻ー20分する。
      DateTime nowMinus20Min = DateTime.Now.Subtract(new TimeSpan(0, 20, 0));
      // その時の時間(H)をベースの時間とする。
      DateTime baseHour = new DateTime(
        nowMinus20Min.Year, nowMinus20Min.Month, nowMinus20Min.Day,
        nowMinus20Min.Hour, 0, 0);

      normalTime.Clear();

      for (int i = 0; i < 12; i++)
      {
        DateTime t = baseHour.Subtract(new TimeSpan(i, 0, 0));
        string timeCmd = t.ToString("yyyyMMddHHmm") + "-00.png";
        if (i == 0)
          defaultTimeString = timeCmd;
        normalTime.Add(new AmedasTime(t.ToString("HH:mm"), t, timeCmd, false));
      }
    }

    private void SetNormalTimeMenu()
    {
      menuItemTimeMain.MenuItems.Clear();
      foreach (AmedasTime at in normalTime)
      {
        MenuItem mi = new MenuItem();
        mi.Text = at.Text;
        menuItemTimeMain.MenuItems.Add(mi);
        mi.Click += new System.EventHandler(this.menuItemTimeEvent);
      }
      menuItemTimeMain.MenuItems[0].Checked = true;
    }
    #endregion

    private string timeString = "";
    private string defaultTimeString = "";



    #region アメダスの種類を選択するメニュー

    /// 
    /// アメダスの種類を選択した場合に、デフォルトの時間に戻す
    /// 
    private void SetDefaultTime()
    {
      timeString = defaultTimeString;
    }

    /// 
    /// タイプのチェックマークをすべて削除する
    /// 
    private void ClearAllMenuItemTypeChecked()
    {
      menuItemRadar.Checked = false;
      menuItemRain.Checked = false;
      menuItemTemperature.Checked = false;
      menuItemWind.Checked = false;
      menuItemSun.Checked = false;
      menuItemSnow.Checked = false;
      menuItemWeather.Checked = false;
      menuItemInfrared.Checked = false;
      menuItemVisible.Checked = false;
      menuItemWatervapor.Checked = false;
      menuItemTyphoon.Checked = false;
      menuItemWindProf.Checked = false;
      menuItemWarnAll.Checked = false;
      menuItemWarnRain.Checked = false;
      menuItemWarnFlood.Checked = false;
      menuItemWarnWind.Checked = false;
      menuItemWarnWave.Checked = false;
      menuItemWarnThunder.Checked = false;
      menuItemWarnFog.Checked = false;
      menuItemWarnLowTemp.Checked = false;
    }

    /// 
    /// レーダー・予測
    /// 
    private void menuItemRadar_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemRadar.Checked = true;

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = true;

      SetNowCastTimeMenu();

      ShowAmedas();
    }

    /// 
    /// 降水量を選択
    /// 
    private void menuItemRain_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemRain.Checked = true;

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      ShowAmedas();
    }

    /// 
    /// 気温を選択
    /// 
    private void menuItemTemperature_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemTemperature.Checked = true;

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      ShowAmedas();
    }

    /// 
    /// 風向を選択
    /// 
    private void menuItemWind_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWind.Checked = true;

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      ShowAmedas();
    }

    /// 
    /// 日照時間
    /// 
    private void menuItemSun_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemSun.Checked = true;

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      ShowAmedas();
    }

    /// 
    /// 積雪量
    /// 
    private void menuItemSnow_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemSnow.Checked = true;

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      ShowAmedas();
    }

    /// 
    /// 天気予報
    /// 
    private void menuItemWeather_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWeather.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    /// 
    /// 衛星・赤外線
    /// 
    private void menuItemInfrared_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemInfrared.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = false;
      menuItemTimeMain.Enabled = true;

      ShowAmedas();
    }
    
    /// 
    /// 衛星・可視
    /// 
    private void menuItemVisible_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemVisible.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = false;
      menuItemTimeMain.Enabled = true;

      ShowAmedas();
    }

    /// 
    /// 衛星・水蒸気
    /// 
    private void menuItemWatervapor_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWatervapor.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = false;
      menuItemTimeMain.Enabled = true;

      ShowAmedas();
    }

    /// 
    /// 台風情報
    /// 
    private void menuItemTyphoon_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemTyphoon.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = false;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    /// 
    /// ウィンドプロファイラ(上空の風) 
    /// 
    private void menuItemWindProf_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWindProf.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = false;
      menuItemTimeMain.Enabled = true;

      ShowAmedas();
    }

    /// 
    /// 警報・すべて
    /// 
    private void menuItemWarnAll_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnAll.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    private void menuItemWarnRain_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnRain.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    private void menuItemWarnFlood_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnFlood.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    private void menuItemWarnWind_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnWind.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    private void menuItemWarnWave_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnWave.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    private void menuItemWarnThunder_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnThunder.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    private void menuItemWarnFog_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnFog.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    private void menuItemWarnLowTemp_Click(object sender, EventArgs e)
    {
      ClearAllMenuItemTypeChecked();
      menuItemWarnLowTemp.Checked = true;

      SetNormalTimeMenu();
      SetDefaultTime();

      menuItemLocation.Enabled = true;
      menuItemTimeMain.Enabled = false;

      ShowAmedas();
    }

    #endregion

  }
}

7. まとめ

.NET Compact Framework でも WebBrowser コントロールが使えるのはとても便利ですね。 いろいろな用途に使えると思います。試してみてはいかがでしょうか。

なお、今回は気象庁の防災気象情報をハックして、画像のURLに直接アクセスするしかありませんでした。このため、実装が面倒になってしまいましたが、Web Service で画像を提供するサービスを提供してもらえると、実装が楽になるし、応用が広がるので、ぜひ検討してもらいたいですね。>>気象庁。

あと、今回気がついたのですが、レーダー・ナウキャストの降水量と、アメダスの降水量には、かなり違いがあることに気がつきました。結構気象予報も奥が深そうですね。