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 で読み出すことでマーシャリングをしています。
データは、
- 全座標の左上隅、右下隅を表す値
- Parts(Polygon)の数
- 座標の総数
- Partsインデクス(座標の列へのインデクスで、Polygon の開始位置を示す。+1の位置のインデクスが示す位置の直前の座標までが1つのPolygonを示すと解釈)
- 座標の列
このデータは、1402個のPolygon、566,678ポイントのデータがあります。描画に少し時間がかかります。
- using System;
- using System.Windows;
- using System.Windows.Media;
- using System.Windows.Shapes;
- using System.IO;
- namespace WpfApplication1
- {
- public partial class Window1 : Window
- {
- public Window1()
- {
- InitializeComponent();
- }
- //読み出したデータの格納場所
- Point box_top_left = new Point(); //座標の最小値
- Point box_bottom_right = new Point(); //座標の最大値
- int[] Parts; //Polygonを表す。便宜上Polygon数+1にする。
- Point[] Points; //座標値
- private void Grid_Loaded(object sender, RoutedEventArgs e)
- {
- //データベースからテーブルを読み出す
- Database1DataSetTableAdapters.gadm_JPN_0TableAdapter
- adapter = new WpfApplication1.Database1DataSetTableAdapters.gadm_JPN_0TableAdapter();
- Database1DataSet.gadm_JPN_0DataTable table = new Database1DataSet.gadm_JPN_0DataTable();
- adapter.Fill(table);
- //読み出したテーブルの最初の行のShapeの値からMemoryStreamを作る
- Stream stream = new MemoryStream((byte[])table.Rows[0]["Shape"]);
- BinaryReader br = new BinaryReader(stream);
- //MemoryStreamからデータを読み出す
- int type = br.ReadInt32(); //これは、5:Polygon である前提
- box_top_left.X = br.ReadDouble();
- box_top_left.Y = br.ReadDouble();
- box_bottom_right.X = br.ReadDouble();
- box_bottom_right.Y = br.ReadDouble();
- int NumParts = br.ReadInt32();
- int NumPoints = br.ReadInt32();
- Parts = new int[NumParts + 1];
- for (int i = 0; i < Parts.Length - 1; i++)
- Parts[i] = br.ReadInt32();
- Parts[NumParts] = NumPoints;//最後のPartの終端を追加して置く
- Points = new Point[NumPoints];
- for (int i = 0; i < Points.Length; i++)
- {
- Points[i].X = br.ReadDouble();
- Points[i].Y = br.ReadDouble();
- }
- DrawPolygon();//最初の描画
- }
- //Polygonをキャンバスに描画する
- void DrawPolygon()
- {
- double offset_x = box_top_left.X;
- double offset_y = box_top_left.Y;
- double scale_x = LayoutRoot.ActualWidth / (box_bottom_right.X - box_top_left.X);
- double scale_y = LayoutRoot.ActualHeight / (box_bottom_right.Y - box_top_left.Y);
- double scale = Math.Min(scale_x, scale_y);
- canvas1.Children.Clear();
- for (int i = 0; i < Parts.Length - 1; i++)
- {
- Polygon part = new Polygon();
- part.Stroke = Brushes.Black;
- part.StrokeThickness = 1;
- for (int j = Parts[i]; j < Parts[i + 1]; j++)
- {
- part.Points.Add(new Point((Points[j].X - offset_x) * scale,
- LayoutRoot.ActualHeight-(Points[j].Y - offset_y) * scale));
- }
- canvas1.Children.Add(part);
- }
- }
- //リサイズのイベント処理(再描画)
- private void LayoutRoot_SizeChanged(object sender, SizeChangedEventArgs e)
- {
- if (Parts == null) return;//LayoutRootのLoadedイベントが呼び出される前の状態
- DrawPolygon();
- }
- }
- }
- <Window x:Class="WpfApplication1.Window1"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window1" Height="300" Width="300">
- <Grid Name="LayoutRoot" Loaded="Grid_Loaded" SizeChanged="LayoutRoot_SizeChanged">
- <Canvas Name="canvas1"/>
- </Grid>
- </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 データが取得でいます。
- JPNDataSetTableAdapters.gadm_JPN_1TableAdapter adapter
- = new WpfApplication1.JPNDataSetTableAdapters.gadm_JPN_1TableAdapter();
- byte[] shape = adapter.ScalarQuery("Iwate");
- Stream stream = new MemoryStream(shape);
おそらく、Polygonには向きがあって、湖なのか陸なのかを示しているのでしょうが、良く理解できていません。
|