9.2 等高線

番号 以下をクリックすると,該当箇所にジャンプします
(1) 考え方
(2) フォーム定義と実行例
(3) プログラムの説明

(1)考え方

等高線は,地形図等を表示する有力な方法です。
ここでは,等高線を描く簡便な方法について示します。

通常の数値地図等はメッシュデータで表現されます。

ここでは,
横Δx,縦Δyの長方形の各辺にhとなる点を見つけて,
線を引く方法を示します。

     
ただし,以下のように交点が3つあるような等高線の場合,
点線のように描くことになります。

         

番号 以下をクリックすると,該当箇所にジャンプします
(1) 考え方
(2) フォーム定義と実行例
(3) プログラムの説明

(2)フォーム定義と実行例

次のようなフォームを定義しておきます。
      

提示するプログラムを実行すると,以下のような画面になります。
       

右上の「数値地図読込み」ボタンをクリックして,
国土地理院発行の50mメッシュ数値地図(いわゆるMEMファイル)を
指定すると,例えば,次のように等高線が描かれます。
背景には,色地図を表示します。
       

番号 以下をクリックすると,該当箇所にジャンプします
(1) 考え方
(2) フォーム定義と実行例
(3) プログラムの説明

(3)プログラムの説明

2次元の絵を描くことになります。
また,数値地図を読み込みます。

したがって,以下のusingを追加しておきます。

   using System.Drawing.Drawing2D;
   using System.IO;

データ領域は,次のように宣言します。

   private int numX=201;
   private int numY=201;
   private double dx=1.0;
   private double dy=1.0;
   private double X0, Y0;
   private string pbA;
   private double Hstep;
   private double[,] Z=new double[201,201];
   private Matrix matrix=new Matrix();
   private Image image;

ここで,matrixは,座標変換用のデータです。

補間のための関数は,以下のように記述することができます。

   public double 補間(double H, double Z1, double Z2)
   {
      return ((H-Z1)/(Z2-Z1));
   }

以下は,等高線を描くプログラムです。

  public void Contour(Graphics g, int j, int k, double H)
  {
   double Z0=Z[j , k ]; double Z1=Z[j+1, k ];
   double Z2=Z[j+1, k+1]; double Z3=Z[j , k+1];

   bool P01 = (Z0> H) ^ (Z1 > H);
   bool P12 = (Z1> H) ^ (Z2 > H);
   bool P23 = (Z2> H) ^ (Z3 > H);
   bool P30 = (Z3> H) ^ (Z0 > H);

   if( P01 || P12 || P23 || P30)
   {
     float x0=0; float y1=0; float x2=0; float y3=0;
     Pen pen = new Pen(Color.Black,0.01F);

     if(P01) x0 =(float)( dx*(((double)j)+補間(H, Z0,Z1)));
     if(P12) y1 =(float)( dy*(((double)k)+補間(H, Z1,Z2)));
     if(P23) x2 =(float)( dx*(((double)j)+補間(H, Z3,Z2)));
     if(P30) y3 =(float)( dy*(((double)k)+補間(H, Z0,Z3)));

     float yk =(float)(dy*(double)(k ));
     float yk1=(float)(dy*(double)(k+1));
     float xj =(float)(dx*(double)(j ));
     float xj1=(float)(dx*(double)(j+1));

     if(P01 && P12) g.DrawLine(pen, x0 , yk , xj1, y1 );
     if(P12 && P23) g.DrawLine(pen, xj1, y1 , x2 , yk1);
     if(P23 && P30) g.DrawLine(pen, x2 , yk1, xj , y3 );
     if(P30 && P01) g.DrawLine(pen, xj , y3 , x0 , yk );
     if(P01 && P23) g.DrawLine(pen, x0 , yk , x2 , yk1);
     if(P12 && P30) g.DrawLine(pen, xj1, y1 , xj , y3 );
   }
  }
 public void Contours(Graphics g, double v1, double v2, double dv)
  {
    for(int j=0; j<numX-1; j++)
      for(int k=0; k<numY-1; k++)
        for(double v=v1; v<=v2; v +=dv) Contour(g,j,k,v);
  }

  以下は,色地図を表示します。

  public void ColorMap(Graphics g, double v1, double v2, double dv)
  {
     Color color;
     float w =(float) dx;
     float h =(float) dy;
     double DD=(v2-v1)/20;
     for(int j=0; j<numX-1; j++)
     for(int k=0; k<numY-1; k++)
     {
       float x1 =(float)( dx*((double)(j)));
       float y1 =(float)( dy*((double)(k)));
       int ID=(int)((Z[j,k]-v1)/DD);
       switch(ID)
       {
         case 0 : color=Color.FromArgb(  0, 63, 0); break;
         case 1 : color=Color.FromArgb(  0, 95, 0); break;
         case 2 : color=Color.FromArgb(  0,127, 0); break;
         case 3 : color=Color.FromArgb(  0,159, 0); break;
         case 4 : color=Color.FromArgb(  0,191, 0); break;
         case 5 : color=Color.FromArgb(  0,223, 0); break;
         case 6 : color=Color.FromArgb(  0,255, 0); break;
         case 7 : color=Color.FromArgb( 31,255, 0); break;
         case 8 : color=Color.FromArgb( 63,255, 0); break;
         case 9 : color=Color.FromArgb(127,255, 0); break;
         case 10 : color=Color.FromArgb(159,255, 0); break;
         case 11 : color=Color.FromArgb(191,255, 0); break;
         case 12 : color=Color.FromArgb(223,255, 0); break;
         case 13 : color=Color.FromArgb(255,255, 0); break;
         case 14 : color=Color.FromArgb(255,223, 0); break;
         case 15 : color=Color.FromArgb(255,191, 0); break;
         case 16 : color=Color.FromArgb(255,159, 0); break;
         case 17 : color=Color.FromArgb(255,127, 0); break;
         case 18 : color=Color.FromArgb(255, 63, 0); break;
         case 19 : color=Color.FromArgb(255, 31, 0); break;
         default:  color=Color.FromArgb(255, 0, 0); break;
       }
       Brush brush = new SolidBrush(color);
       g.FillRectangle(brush,x1,y1,w,h);
     }
   }

上記の色地図,等高線を呼び出して,imageに描画します。

  public double minZ()
  {
    double R=Z[0,0];
    for(int j=0; j<numX-1; j++)
      for(int k=0; k<numY-1; k++)
         if(Z[j,k]<R) R=Z[j,k];
    return R;
  }
  public double maxZ()
  {
    double R=Z[0,0];
    for(int j=0; j<numX-1; j++)
      for(int k=0; k<numY-1; k++)
         if(Z[j,k]>R) R=Z[j,k];
    return R;
  }
  private void 描画()
  {
    image =new Bitmap(1000,1000);
    Graphics g=Graphics.FromImage(image);
    g.Clear(this.BackColor);
    g.Transform=matrix;
    double V1=minZ();
    double V2=maxZ();
    if(Math.Abs(V2-V1)<0.0000001) V2=V1+1;
    ColorMap(g, V1, V2, 0.1);
    Contours(g, V1, V2, Hstep);
  }

ウインドウの描画が必要になると,
ControlクラスのOnPaintメソッドが呼び出されますので,
これをオーバライドします。

  protected override void OnPaint(PaintEventArgs e)
  {
    base.OnPaint(e);
    e.Graphics.DrawImage(image,0,0);
  }

座標移動,スケールファクタなどの
座標変換マトリックスを設定します。

  private void window(double X1, double Y1, double X2, double Y2)
  {
    double W=this.ClientSize.Width;
    double H=this.ClientSize.Height;
    float SX=(float)(W/(X2-X1));
    float SY=(float)(H/(Y2-Y1));
    matrix.Scale(SX,SY);
    matrix.Translate(-(float)X1,-(float)Y1);
  }

初期処理を行います。
便宜上,適当な値を設定して描画します。

  private void Form1_Load(object sender, System.EventArgs e)
  {
    double XX=((double) numX)*0.5;
    double YY=((double) numY)*0.5;
    X0 = dx * XX; Y0= dy * YY;
    double C = 0.02; //倍率
    Hstep=double.Parse(textBox1.Text);
    for(int i=0; i<numX; i++)
      for(int j=0; j<numY; j++)
      {
        double x=C*(((double)i)-XX);
        double y=C*(((double)j)-YY);
        Z[i,j]=(x-y)*Math.Exp(-(x*x+y*y))*5000;
      }
    window(-20,240,220,-50);
    描画();
  }

ボタンが押されたら,「ファイルを開く」ダイアログを表示します。

  private void button1_Click(object sender, System.EventArgs e)
  {
    openFileDialog1.ShowDialog();
  }

「開く」ボタンが押されたら,数値地図を読み込んで,描画します。

  private string midStr(string DT, int ist,int N)
  {
    string S="";
    int k=ist-1;
    for(int i=1 ;i<=N;i++){S = S + DT[k]; k=k+1;}
    return S;
  }
  private void openFileDialog1_FileOk
    (object sender, System.ComponentModel.CancelEventArgs e)
  {
    StreamReader DTS;
    int ii, pbII;
    string FName=openFileDialog1.FileName;
    if(FName == "") return;
    DTS=new StreamReader(FName,System.Text.Encoding.Default);
    pbA=DTS.ReadLine();
    pbII=0;string DT=DTS.ReadLine();
    while(DT !=null)
    {
      ii = 5;
      for(int j=0;j<200;j++)
      {
        ii = ii + 5;
        Z[pbII, j] = int.Parse(midStr(DT, ii, 5));
      }
      DT=DTS.ReadLine();
      pbII++;
    }
    DTS.Close();
    描画();
    this.Invalidate();
  }

テキストが変更されたら,等高線間隔を変更します。

  private void textBox1_TextChanged(object sender, System.EventArgs e)
  {
    Hstep=double.Parse(textBox1.Text);
  }

ボタン2が押されたら,描画しなおします。

  private void button2_Click(object sender, System.EventArgs e)
  {
    描画();
    this.Invalidate();
  }

番号 以下をクリックすると,該当箇所にジャンプします
(1) 考え方
(2) フォーム定義と実行例
(3) プログラムの説明


1. 基本的なアルゴリズム

2. 基本的なデータ構造

3. 操作を伴うデータ構造

4. 探索

5. 再帰的アルゴリズム

6. ソート

7. 集合

8. 文字列処理

9. 色々なアルゴリズム


上のタイトルをクリックします