mikeo_410


JPN.mdbの読み取り

  海岸線、県境、市区町村境界データのJPN.mdb(35.2MB) を Visual Studio で読んで見ました。

  ※注意
  Windows の X64 を使っているのですが、Visual Studio でプロジェクトを作ったら、「ビルド」「構成マネージャー」「アクティブ ソリューション プラットフォーム」で、「新規作成」を選び、「新しいプラットフォーム...」で x86 を選んでおく必要があります。

1.前処理 

  以下に示す、「新しいデータソースの追加」を行うと、なぜか全国の海岸線のデータだけがエラーになりました。
  これを解決するために、このデータだけを別のデータベースに移します。例題も、このデータベースを使うことにします。
  県境や市区町村境界のテーブルはそのまま使えました。

1.1.Access で 開く

  JPN.mdb を Access で開いて見ると、36テーブルありました。
  gadm_JPN0、gadm_JPN1、gadm_JPN2 が、それぞれ海岸線、県境、市区町村境界のPolygonデータを含んでいます。他のテーブルの使い方はわかりません。

1.2.新しいデータベースを作ってエクスポート

  Access のメニューから「新規作成」を選ぶと、Database1.accdb と、ファイル名が入っています。このまま、「作成」をしました。
  再度、JPN.mdb を開きます。
  gadm_JPN0 を選んで、右ボタンメニューから「エクスポート」を選択し、先ほどの Database1.accdb に出力します。

1.3.フィールドの削除

  Database1.accdb に、gadm_JPN0 テーブルがコピーできたので、これを開きます。
  このテーブルにはたくさんフィールドがあります。今、必要なのは「Shape」フィールドだけなので、「Shape」より後にあるフィールドを削除しました。
  保存してAccessを終了します。
  これで、このテーブルも Visual Studio で「新しいデータソースの追加」できます。

2.新しいデータソースの追加

   Visual Studio で「新しいデータソースの追加」をします。メニューバーの「データ」にあります。

 1.「データベース」が選ばれているので、そのまま「次へ」  2.「新しい接続」ボタンをクリック
3.「参照」で Database1.accdb をえらんで「OK」 4.「次へ」
5.「はい」でデータベースがプロジェクトにコピーされる。 6.「次へ」
7.テーブルを選択して「次へ」  
 

  「新しいデータソースの追加」よって、プロジェクトにはデータベースファイルと XSD が追加されます。
  下図は、Database1DataSet.xsd をクリックして開いたところです。
  テーブルには、2つのフィールド(OBJECTID、Shape)があり、Fill()、GetData()と言うメソッドが準備されたことがわかります。

3.データベースを読んで見る


  自動的に作成されたクラスを使ってデータベースから座標を読み込んで見ます。
  Visual Studio のエディタの入力機能を頼りにします。
  Data...と言うようにキー入力していくと、Database1DataSetクラスと Database1DataSetTableAdapters名前空間が使えるのがわかります。
  Database1DataSetTableAdapters.gadm_JPN_0TableAdapter を使って、Database1DataSet.gadm_JPN_0DataTable データを読み出します。
  Shapeフィールドは、byte[]型に定義されています。

  下に挙げるコードは、byte[] の Shapeフィールドから、MemoryStream を作って、BinaryReader で読み出すことでマーシャリングをしています。

  データは、

  1. 全座標の左上隅、右下隅を表す値
  2. Parts(Polygon)の数
  3. 座標の総数
  4. Partsインデクス(座標の列へのインデクスで、Polygon の開始位置を示す。+1の位置のインデクスが示す位置の直前の座標までが1つのPolygonを示すと解釈)
  5. 座標の列

  このデータは、1402個のPolygon、566,678ポイントのデータがあります。描画に少し時間がかかります。

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Media;
  4. using System.Windows.Shapes;
  5. using System.IO;
  6. namespace WpfApplication1
  7. {
  8.     public partial class Window1 : Window
  9.     {
  10.         public Window1()
  11.         {
  12.             InitializeComponent();
  13.         }
  14.         //読み出したデータの格納場所
  15.         Point box_top_left = new Point();       //座標の最小値
  16.         Point box_bottom_right = new Point();   //座標の最大値
  17.         int[] Parts;            //Polygonを表す。便宜上Polygon数+1にする。
  18.         Point[] Points;         //座標値
  19.         private void Grid_Loaded(object sender, RoutedEventArgs e)
  20.         {
  21.             //データベースからテーブルを読み出す
  22.             Database1DataSetTableAdapters.gadm_JPN_0TableAdapter
  23.                 adapter = new WpfApplication1.Database1DataSetTableAdapters.gadm_JPN_0TableAdapter();
  24.             Database1DataSet.gadm_JPN_0DataTable table = new Database1DataSet.gadm_JPN_0DataTable();
  25.             adapter.Fill(table);
  26.             //読み出したテーブルの最初の行のShapeの値からMemoryStreamを作る
  27.             Stream stream = new MemoryStream((byte[])table.Rows[0]["Shape"]);
  28.             BinaryReader br = new BinaryReader(stream);
  29.             //MemoryStreamからデータを読み出す
  30.             int type = br.ReadInt32();  //これは、5:Polygon である前提
  31.             box_top_left.X = br.ReadDouble();
  32.             box_top_left.Y = br.ReadDouble();
  33.             box_bottom_right.X = br.ReadDouble();
  34.             box_bottom_right.Y = br.ReadDouble();
  35.             int NumParts = br.ReadInt32();
  36.             int NumPoints = br.ReadInt32();
  37.             Parts = new int[NumParts + 1];
  38.             for (int i = 0; i < Parts.Length - 1; i++)
  39.                 Parts[i] = br.ReadInt32();
  40.             Parts[NumParts] = NumPoints;//最後のPartの終端を追加して置く
  41.             Points = new Point[NumPoints];
  42.             for (int i = 0; i < Points.Length; i++)
  43.             {
  44.                 Points[i].X = br.ReadDouble();
  45.                 Points[i].Y = br.ReadDouble();
  46.             }
  47.             DrawPolygon();//最初の描画
  48.         }
  49.         //Polygonをキャンバスに描画する
  50.         void DrawPolygon()
  51.         {
  52.             double offset_x = box_top_left.X;
  53.             double offset_y = box_top_left.Y;
  54.             double scale_x = LayoutRoot.ActualWidth / (box_bottom_right.X - box_top_left.X);
  55.             double scale_y = LayoutRoot.ActualHeight / (box_bottom_right.Y - box_top_left.Y);
  56.             double scale = Math.Min(scale_x, scale_y);
  57.             canvas1.Children.Clear();
  58.             for (int i = 0; i < Parts.Length - 1; i++)
  59.             {
  60.                 Polygon part = new Polygon();
  61.                 part.Stroke = Brushes.Black;
  62.                 part.StrokeThickness = 1;
  63.                 for (int j = Parts[i]; j < Parts[i + 1]; j++)
  64.                 {
  65.                     part.Points.Add(new Point((Points[j].X - offset_x) * scale,
  66.                          LayoutRoot.ActualHeight-(Points[j].Y - offset_y) * scale));
  67.                 }
  68.                 canvas1.Children.Add(part);
  69.             }
  70.         }
  71.         //リサイズのイベント処理(再描画)
  72.         private void LayoutRoot_SizeChanged(object sender, SizeChangedEventArgs e)
  73.         {
  74.             if (Parts == null) return;//LayoutRootのLoadedイベントが呼び出される前の状態
  75.             DrawPolygon();
  76.         }
  77.     }
  78. }

  1. <Window x:Class="WpfApplication1.Window1"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Window1" Height="300" Width="300">
  5.     <Grid Name="LayoutRoot"  Loaded="Grid_Loaded" SizeChanged="LayoutRoot_SizeChanged">
  6.         <Canvas Name="canvas1"/>
  7.     </Grid>
  8. </Window>

4.行を選択するには

  日本の海岸線を描く例は、もともとテーブルに1行しかないケースです。
  特定の県の県境を描く場合は、テーブル全体を取得する必要はありません。
  この条件の付いたクエリを Visual Studio で行う方法がわかりませんでした。
  なにか間違いがあるようですが、1つだけ方法を見つけたので書いて置きます。

  まず、オリジナルの JPN.mdb を、「2.新しいデータソースの追加」の手順でプロジェクトに追加します。
  JPNDataSet.xsd が作られます。このとき、テーブルは、gadm_JPN1 を選んでください。
  他のテーブルも選んで構いません。ただし、gadm_JPN0 を含んでいると警告が出ます。gadm_JPN0 以外は使えるので、そのままで問題ありません。

  JPNDataSet.xsd を開いて、左図のように Query を追加します。ここ操作で、メソッドを追加します。
  下図のようなダイアログが順次表示されます。
  「複数行を返す」を選択すると、FillBy(テーブル)のようなメソッドが作られ、テーブルが返されるようになります。「単一の値を返す」を選択すると、返すフィールドの型を返す、ScalarQuery() が生成されます。

  最後の図で「クエリビルダー」のボタンを押すと SELECT 文の編集画面になります。


  この編集画面は何とも説明しがたいのですが、最下段に目的のSELECT文を作ってください。
  直接、最下段を編集もできます。
  左図は、県名で Shape を直接引こうと言うものです。

  要点は、県名をScalarQuery()の引数で設定したいので、Where句の値はパラメータにすることです。
  WHERE (NAME_1 = @prefecture)
  のように、@を付けて名前を付けます。

  ここで、問題があります。このエディタは、この@の付いた名前も、シングルクオートで囲もうとします。
  このシングルクオートを削除して「OK」を押します。
  シングルクオートで囲まれたものは、パラメータと見なされないようです。


  すると、左図のような警告が出ます。
  「OK」を押して先へ進んでメソッドを作ってしまいます。

  メソッドが作成され、下図のようにテーブルの図の下段に追加されます。
  この段階では、メソッドは引数なしの状態です。

  このメソッドを選ぶと、プロパティが表示されます。この、CommandText が、先ほど入力した SELECT 文です。必要なら編集できます。

  プロパティの最後が、Parameters になっています。これで、メソッドに引数を与えます。


  Parameters のコレクションをクリックすると、左図のような表示になります。
  追加を押して、右側の ParameterName を書き換えます。
 SELECT 文で、@prefecture と名前を付けたので、ここでも @prefecture と入力します。

  メソッドの表示にも、この名前が引き数として反映されます。

  このメソッドは、下図のように使います。
  県名で直接 byte[]型の Shape データが取得でいます。

  1. JPNDataSetTableAdapters.gadm_JPN_1TableAdapter adapter
  2.     = new WpfApplication1.JPNDataSetTableAdapters.gadm_JPN_1TableAdapter();
  3. byte[] shape = adapter.ScalarQuery("Iwate");
  4. Stream stream = new MemoryStream(shape);


  おそらく、Polygonには向きがあって、湖なのか陸なのかを示しているのでしょうが、良く理解できていません。 


mikeo_410@hotmail.com