マップチャートの動的更新

4. サーブレットの修正

実際の業務アプリケーションを想定し、DBから取得したデータをもとにモデルを構築するため、前回作成したSampleServletのほかに、以下の2つのクラスを追加します。

クラス名 機能
SampleModel モデルを生成するクラス
DataAccessBean データアクセスクラス

今回作成するプログラムのシーケンスは下図のようになります。

画像「プログラムのシーケンス」拡大

DataAccessBeanの作成

各都道府県のデータを保持するデータアクセスクラスです。実際のシステムではDBなどからデータ抽出を行うクラスに該当します。このクラスでは、getData()メソッドが、指定された年と県名に対するデータを返します。

[DataAccessBean.java]

public class DataAccessBean {

    /**
     * 各都道府県のデータ
     */
    private final int[][] KANTODATA = {
            // 東京都,神奈川県,栃木県,茨城県,群馬県,千葉県,埼玉県
            { 1691, 1068, 1209, 2170, 1186, 1569, 1078 }, // 2009
            { 2304, 2576, 1982, 1805, 1255, 2095, 1587 }, // 2010
            { 1536, 1132, 2598, 2331, 2128, 1782, 2269 } // 2011
    };

    public DataAccessBean() {
    }

    /*
     * データ取得用メソッド
     * @param year : 年
     * @param pref : 県名
     * @return 年と県名に対するデータ。
     *         該当するデータが無い場合は-1を返す。
     */
    public int getData(String year, String pref) {
        // 年のデータ取得用インデックスを取得します
        int yearIndex = getYearIndex(year);
        // 都道府県のデータ取得用インデックスを取得します
        int prefIndex = getPrefIndex(pref);
        // 該当するデータがない場合は-1を返す
        if ((yearIndex < 0) || (prefIndex < 0)) {
            return -1;
        }
        return KANTODATA[yearIndex][prefIndex];
    }

    /*
     * 年のデータ取得用インデックスを返します。
     * @param arg 年
     * @return 指定された年のデータ取得用インデックス
     */
    private int getYearIndex(String arg) {
        int ret = -1;
        if (arg.equals("2009")) {
            ret = 0;
        } else if (arg.equals("2010")) {
            ret = 1;
        } else if (arg.equals("2011")) {
            ret = 2;
        }
        return ret;
    }

    /*
     * 都道府県のデータ取得用インデックスを返します。
     * @param arg 都道府県名
     * @return 指定された県のデータ取得用インデックス
     */
    private int getPrefIndex(String arg) {
        int ret = -1;
        if (arg.equals("東京都")) {
            ret = 0;
        } else if (arg.equals("神奈川県")) {
            ret = 1;
        } else if (arg.equals("栃木県")) {
            ret = 2;
        } else if (arg.equals("茨城県")) {
            ret = 3;
        } else if (arg.equals("群馬県")) {
            ret = 4;
        } else if (arg.equals("千葉県")) {
            ret = 5;
        } else if (arg.equals("埼玉県")) {
            ret = 6;
        }
        return ret;
    }
}

SampleModelの作成

モデルを構築するクラスです。
getMapModel()メソッドがモデルを構築するメソッドになります。SampleServletからコールされ、引数にはリクエストパラメータから取得した年が渡されます。年に対応したデータをDataAccessBeanから取得し、マップモデルを作成します。
前回の補足事項で触れたように、モデルはマップデータとname属性をキーとしたツリー構造として関連付けられています。マップデータのツリー構造と一致していることを確認しやすいように、モデルのXMLイメージと合わせながら解説します。

前回の補足情報

なお、マップデータのXMLイメージは、sample.mapファイルをマップエディタで開き、[XML]タブを選択することで確認できます。

  1. マップモデルの作成
    はじめにマップモデルを作成します。マップモデルのインスタンスはMxMapModelクラスから作成します。また、ルートノードであるmap要素の各属性値を設定します。

          // モデルオブジェクトを作成します
    1.    MxMapModel mapModel = new MxMapModel();
          // ルートノードのname属性に"Map"を設定します
    2.    mapModel.root.setName("Map");
          // title属性に年を設定します
    3.    mapModel.root.setTitle(year);

    ここまでのXMLイメージは次のようになります。

    <?xml version="1.0" encoding="UTF-8"?>
    <map name="Map" title="2009">
    </map>
  2. "関東地方"のitem要素の追加
    次に、"関東地方"のitem要素を作成します。item要素のインスタンスはFxMapItemクラスから作成します。

    5行目 :name属性に"関東地方"を設定
    6行目 :popup属性にarea要素へのマウスホバーによるポップアップの書式を設定

    これら2つの属性値を設定したのち、7行目でmap要素に追加しています。

          // 関東地方のitem要素を作成します
    4.    FxMapItem kanto = new FxMapItem();
          // name属性に"関東地方"を設定します
    5.    kanto.setName("関東地方");
          // エリアへのマウスホバーによるポップアップの書式設定をします
    6.    kanto.setPopup("$(title)年\n$(name)\n$(value)");
          // ルートノードへ関東地方を追加します
    7.    mapModel.root.add(kanto);

    ここまでのXMLイメージは次のようになります。

    <?xml version="1.0" encoding="UTF-8"?>
    <map name="Map" title="2009">
         <item name="関東地方" popup="$(title)年\n$(name)\n$(value)"/>
    </map>
  3. 各都道府県のitem要素の追加
    最後に、各都道府県のitem要素を作成します。コード内で使用しているKANTOPREFは各都道府県名の配列です。

    8行目 :データアクセスクラスのインスタンスを作成
    9行目 :都道府県数分のループ
    10行目 :データアクセスクラスからデータを取得
    11行目 :getColor()メソッドで値の範囲に応じて割り当てた色を取得
    13~15行目 :属性値の設定
    13行目 :name属性に県名を設定
    14行目 :ポップアップへ表示するためにvalue属性へデータを設定
    15行目 : backColor属性に塗りつぶし色を設定

    すべての属性値を設定したのち、16行目で"関東地方"のitem要素へ追加しています。

          // データアクセスビーンを作成します
    8.    DataAccessBean dab = new DataAccessBean();
          // 各都道府県のitem要素の作成ループです
    9.    for (int i = 0; i < KANTOPREF.length; i++) {
              // 選択された年の各都道府県データを取得します
    10.       int data = dab.getData(year, KANTOPREF[i]);
              // データから塗りつぶし色を求めます
    11.       Color color = getColor(data);
              // 都道府県のitem要素を作成します
    12.       FxMapItem item = new FxMapItem();
              // name属性に都道府県名を設定します
    13.       item.setName(KANTOPREF[i]);
              // value属性にデータを設定します
    14.       item.setValue(String.valueOf(data));
              // backColor属性に塗りつぶし色を設定します
    15.       item.setBackColor(color);
              // 関東地方のitem要素のへ都道府県のitem要素を追加します
    16.       kanto.add(item);
    17.   }

    ここまでのXMLイメージは次のようになります。

    <map name="Map" title="2009">
         <item name="関東地方" popup="$(title)年\n$(name)\n$(value)">
              <item name="東京都" value="2304" backColor="#6168E1"/>
              <item name="神奈川県" value="2576" backColor="#3944D2"/>
              <item name="栃木県" value="1982" backColor="#898DF1"/>
              <item name="茨城県" value="1805" backColor="#898DF1"/>
              <item name="群馬県" value="1255" backColor="#A7ABEA"/>
              <item name="千葉県" value="2095" backColor="#6168E1"/>
              <item name="埼玉県" value="1587" backColor="#A7ABEA"/>
         </item>
    </map>

[SampleModel.java]

import java.awt.Color;
import com.gp.api.model.mapmodel.FxMapItem;
import com.gp.api.model.mapmodel.MxMapModel;

public class SampleModel {

    /**
     * 都道府県名の列挙(マップデータのname属性と一致させます)
     */
    public static final String[] KANTOPREF = {
            "東京都", "神奈川県", "栃木県", "茨城県", "群馬県", "千葉県", "埼玉県"
    };

    /**
     * 年をもとに作成されたモデルを返す
     * 
     * @param year
     *            :Webページで選択された年
     * @return 年をもとに作成したモデル
     */
    public MxMapModel getMapModel(String year) {
        // モデルオブジェクトを作成します
        MxMapModel mapModel = new MxMapModel();
        // ルートノードのname属性に"Map"を設定します
        mapModel.root.setName("Map");
        // title属性に年を設定します
        mapModel.root.setTitle(year);
        // 関東地方のitem要素を作成します
        FxMapItem kanto = new FxMapItem();
        // name属性に"関東地方"を設定します
        kanto.setName("関東地方");
        // エリアへのマウスホバーによるポップアップの書式設定をします
        kanto.setPopup("$(title)年\n$(name)\n$(value)");
        // ルートノードへ関東地方を追加します
        mapModel.root.add(kanto);

        // データアクセスビーンを作成します
        DataAccessBean dab = new DataAccessBean();

        // 各都道府県のitem要素の作成ループです
        for (int i = 0; i < KANTOPREF.length; i++) {
            // 選択された年の各都道府県データを取得します
            int data = dab.getData(year, KANTOPREF[i]);
            // データから塗りつぶし色を求めます
            Color color = getColor(data);

            // 都道府県のitem要素を作成します
            FxMapItem item = new FxMapItem();
            // name属性に都道府県名を設定します
            item.setName(KANTOPREF[i]);
            // value属性にデータを設定します
            item.setValue(String.valueOf(data));
            // backColor属性に塗りつぶし色を設定します
            item.setBackColor(color);
            // 関東地方のitem要素のへ都道府県のitem要素を追加します
            kanto.add(item);
        }
        return mapModel;
    }

    /**
     * 与えられたデータに対する、塗りつぶし色を返します
     * 
     * @param arg
     *            各都道府県のデータ
     * @return 塗りつぶし色
     */
    private Color getColor(int data) {
        if (data < 1200) {
            // ~1199
            return new Color(212, 221, 251);
        } else if ((1200 <= data) && (data < 1600)) {
            // 1200~1599
            return new Color(167, 171, 234);
        } else if ((1600 <= data) && (data < 2000)) {
            // 1600~1999
            return new Color(137, 141, 241);
        } else if ((2000 <= data) && (data < 2400)) {
            // 2000~2399
            return new Color(97, 104, 225);
        } else if (2400 <= data) {
            // 2400~
            return new Color(57, 68, 210);
        } else {
            // それ以外:白
            return Color.WHITE;
        }
    }
}
【補足情報】

モデルでは、親要素の属性値を子要素へ引き継ぎます。

親要素で定義された属性は

<map name="root_node" value="testvalue">
 <item name="parent_node">
  <item name="node1"/>
  <item name="node2"/>
  <item name="node3"/>
 </item>
</map>

そのまま子要素に引き継がれます。

<map name="root_node" value="testvalue">
 <item name="parent_node" value="testvalue">
  <item name="node1" value="testvalue"/>
  <item name="node2" value="testvalue"/>
  <item name="node3" value="testvalue"/>
 </item>
</map>

また、子要素で属性値が再定義された場合はその値で上書きされます。

途中で属性値が再定義された場合は

<map name="root_node" value="testvalue">
 <item name="parent_node" value="updatevalue">
  <item name="node1"/>
  <item name="node2"/>
  <item name="node3"/>
 </item>
</map>

上書きされ子要素に引き継がれます。

<map name="root_node" value="testvalue">
 <item name="parent_node" value="updatevalue">
  <item name="node1" value="updatevalue"/>
  <item name="node2" value="updatevalue"/>
  <item name="node3" value="updatevalue"/>
 </item>
</map>

今回作成したモデルは下に示すものと同等です。"関東地方"のitem要素のpopup属性に設定した書式で、各都道府県のポップアップが表示されるのは、親要素の属性値が引き継がれているためです。

<map name="Map" title="2010">
 <item name="関東地方" title="2010" popup="$(title)年\n$(name)\n$(value)">
  <item name="東京都"   title="2010" value="2304"
        backColor="#6168E1" popup="$(title)年\n$(name)\n$(value)"/>
  <item name="神奈川県" title="2010" value="2576"
        backColor="#3944D2" popup="$(title)年\n$(name)\n$(value)"/>
  <item name="栃木県"   title="2010" value="1982"
        backColor="#898DF1" popup="$(title)年\n$(name)\n$(value)"/>
  <item name="茨城県"   title="2010" value="1805"
        backColor="#898DF1" popup="$(title)年\n$(name)\n$(value)"/>
  <item name="群馬県"   title="2010" value="1255"
        backColor="#A7ABEA" popup="$(title)年\n$(name)\n$(value)"/>
  <item name="千葉県"   title="2010" value="2095"
        backColor="#6168E1" popup="$(title)年\n$(name)\n$(value)"/>
  <item name="埼玉県"   title="2010" value="1587"
        backColor="#A7ABEA" popup="$(title)年\n$(name)\n$(value)"/>
 </item>
</map>

SampleServletの修正

SampleServletの修正箇所はリクエストパラメータの取得とモデル設定の部分です。モデルはXMLファイルから読み込むのではなく、SampleModelクラスより取得し、設定します。

現在、プログラムは以下のようになっています。

  // チャートモデルを外部のXMLファイルから読み込みます
  fis = new FileInputStream(modelPath);
  model = MxMapModel.read(fis, null);
  chart.setModel(model);
  fis.close();

ここを次のようなコードに置き換えます。リクエストパラメータ("year")から取得した年をもとにモデル作成し、チャートに設定します。

  // リクエストパラメータより年を取得します
  String year = request.getParameter("year");
  // 取得できなかった場合は 2009 年をデフォルト値とします
  if ((year == null) || year.equals("")) {
      year = "2009";
  }
  // チャートモデルを設定します
  chart.setModel(sampleModel.getMapModel(year));

サンプルコードは下記のようになります。今回の修正箇所は赤字で示しました。

[SampleServlet.java]

import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gp.api.jsp.MxChartDescription;
import com.gp.api.jsp.MxServerComponent;
import com.gp.api.styles.chart.MxMapStyle;

public class SampleServlet extends HttpServlet implements Servlet {

    // マップデータファイルのパス
    private final String mapData = "/xml/sample.map";
    // チャートスタイルを定義したXMLファイルのパス
    private final String xmlStyle = "/xml/sample_style.xml";
    // ファイル読み込み用ストリーム
    private FileInputStream fis = null;
    // チャートスタイルオブジェクト
    private MxMapStyle style = null;
    // チャートモデルオブジェクト
    private final SampleModel sampleModel = new SampleModel();

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // サーブレットコンテキストを取得
        ServletContext sc = getServletContext();
        // サーバーコンポーネントのインスタンスを取得します
        MxServerComponent svr = MxServerComponent.getDefaultInstance(sc);
        // チャート生成に必要な情報を含むMxChartDescriptionの新しいインスタンスを取得します
        MxChartDescription chart = svr.newImageSpec();

        // チャートのサイズを設定します
        chart.width = 450;
        chart.height = 350;
        // 画像タイプを設定します
        chart.type = "PNG";

        // パスの組み立て
        String stylePath = sc.getRealPath(xmlStyle);
        String mapPath = sc.getRealPath(mapData);

        // チャートスタイルを外部のXMLファイルから読み込みます
        fis = new FileInputStream(stylePath);
        style = (MxMapStyle) MxMapStyle.read(fis, null);
        // マップデータのパスを設定します
        style.source = new MxMapStyle.MxMapRef(mapPath);
        // チャートスタイルを設定します
        chart.setStyle(style);
        fis.close();

        // リクエストパラメータより年を取得します
        String year = request.getParameter("year");
        // 取得できなかった場合は 2009 年をデフォルト値とします
        if ((year == null) || year.equals("")) {
            year = "2009";
        }
        // チャートモデルを設定します
        chart.setModel(sampleModel.getMapModel(year));

        // リクエスト変数へMxChartDescriptionオブジェクトを格納します
        request.setAttribute("chart", chart);

        // sample.jspへforward
        RequestDispatcher rd = sc.getRequestDispatcher("/sample.jsp");
        rd.forward(request, response);
    }
}