複雑な図形の塗りつぶし

タグの編集
投稿者 h-k  (中学生) 投稿日時 2011/5/26 16:48:43
以下のリンク先にあるような日本地図に、一県ごとに黄色い色を塗りたいです。
http://stat001.ameba.jp/user_images/20110526/16/ryo00na/f2/97/p/o0440044411251816131.png

具体的には、ボタンを押したときにテキストボックスにある都道府県に色を塗りたいです。
都道府県を1個ずつに番号などで分けて県の名前と番号を紐付けると勝手に妄想していますが、こんな方法はありますでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2011/5/26 18:01:33
VB のバージョンも明記しましょう。

> テキストボックスにある都道府県に
TextBox に入力させるよりも、
ComboBox から選択させる方が良い気がします。

以下、開発環境が何であるかわからないので、具体的なコードの提示は避けて方法論だけ:

> 色を塗りたいです。
(案1) 黄色と透明色の 2 色だけの画像(各県の形のもの)を重ねて描画する。
(案2) 塗りつぶし箇所に対するマスクを用意し、それを使って塗りつぶす。
(案3) ExtFloodFill API を使って、閉じた領域を塗りつぶす。
(案4) 塗りつぶし領域に対する Region を作成しておき、それを使って塗りつぶす。
(案5) 県境の輪郭部分の座標を調べておき、それを使って塗りつぶす。


1 はセル画のイメージ。VB6 では透過画像の扱いに制限があるため使いにくいですが、
 VB.NET では png 画像を描画する事で、比較的簡単に実装できます。
 都道府県分の画像数を保持しておくことになるため、データサイズが増加するのが難点です。

2 はスプライト処理。これも、マスク画像を都道府県の数だけ用意する必要があります。
 VB6 の場合は PaintPicture を使います。あるいは ImageList コントロールを使う手も。
 http://homepage1.nifty.com/rucio/main/technique/teq_13.htm
 http://rucio.cloudapp.net/CommentDetail.aspx?ThreadId=9641&CommentId=15402

3 は VB6 のヘルプ「hDC プロパティの使用例」に載っています。
  VB.NET なら過去ログにサンプルがあります。API を使う事になりますが、
 各都道府県内の座標 1 つを用意するだけなので、必要なデータ量はもっとも少なくて済みます。
 http://rucio.cloudapp.net/ThreadDetail.aspx?ThreadId=9715

4 は私が良く使う手法です。リージョン情報を RegionData のバイナリとして保持しておき、
 それを Region クラスに復元してから FillRegion メソッドで塗りつぶします。
 http://dobon.net/vb/dotnet/graphics/fillregion.html
 http://dobon.net/vb/dotnet/graphics/setclip.html

5 は、下記の VB.NET 向けサンプルが参考になるかも。
 http://rucio.cloudapp.net/ThreadDetail.aspx?ThreadId=10147#CommentId20330
投稿者 h-k  (中学生) 投稿日時 2011/5/26 18:16:19
非常に申し訳ありませんでした。
僕の開発環境は
VB2010 Express
Win XP
この通りです。

何といいますか、僕が作成中のプログラムから言うと、やはりTextBoxで選択させたいです。
場合によっては、「関東」でくくって塗る場合もありますし、

テキストボックスに○○県があったら、そこを塗りつぶしなさい。
とか
テキストボックスに○○地方とあったら、そこの地域すべてを塗りつぶしなさい。
こんな感じのものに対応させたいです。
投稿者 とくま  (社会人) 投稿日時 2011/5/26 18:33:07
> こんな感じのものに対応させたいです。 
それを上に説明したような技術を勉強して実現して下さいと
いうことではないのでしょうか?

難しすぎて手が付けられないというのであれば、
・・・小学生でも考えつきそうな案として、

日本地図の1県だけ塗りつぶした画像を48枚用意して
切り替えるだけ。。。とか。

自分の持ってる技術でどうにか方法を考える訓練も
プログラムには必要です。
「私が考えるとこんなプログラムになって、3時間ぐらいで
出来ちゃって全然物足りない!もっとここをこうしたいん
だけどここだけできない!!」
とか説明できる子は有望なんだけど、具体的には何も出来
ません、1行もコードを書いてませんは何だかなぁ。。。
投稿者 平成の  (社会人) 投稿日時 2011/5/26 19:15:12
>「私が考えるとこんなプログラムになって、3時間ぐらいで
>出来ちゃって全然物足りない!もっとここをこうしたいん
>だけどここだけできない!!」
>とか説明できる子は有望なんだけど、具体的には何も出来
>ません、1行もコードを書いてませんは何だかなぁ。。。 
 
有望だとか、無能だとか人間を選別することは私たちを生かしてくれる大いなる存在がすることではないでしょうか?
私たち凡夫は、人の能力を選別してはならないし、そうすることは傲り以外の
なにものではない、不遜にもそう思うのです。
人の心を大切、大切ににして言葉を選んで究極のアドバイスができる私たちでありたい
と共に思います。
  
投稿者 h-k  (中学生) 投稿日時 2011/5/26 21:13:30
VB初心者の低脳な僕は画像切り替え式にします。
http://homepage1.nifty.com/rucio/main/dotnet/Samples/Sample139ImageAlphaBlend.htm
ここを参考に合成するところまでは来たのですが、画像を透明にせず、黄色い画像だけをきっぱりと上に合わせるためにはどうすればいいでしょうか。

それと、
M.Matrix00 = 1.0F
M.Matrix11 = 1.0F
M.Matrix22 = 1.0F
M.Matrix33 = 0.5F
M.Matrix44 = 1.0F
個人的にここでどんなことが行われているのか知りたいです。
ご教授ください。
投稿者 もちだ  (社会人) 投稿日時 2011/5/26 21:44:38
色の塗り方は既に出ているので、塗る直前までの動作の話をしましょう。
自分もTextBoxよりは予め項目を作ってComboBoxに突っ込んだ方が安全かと思います。
自由に記入できるTextBoxだと記入文字列が安定しませんからマッチさせるのは手間かもしれません。
入力が「青森」と「青森県」で同じ処理するようにすれば良いだけですけど…。

TextBoxを使うなら、使用者が何を入力できるか判るように、入力可能なものの
一覧を別窓で表示するなどの工夫が要るかもしれません。
(自分だけで使うなら余計なお世話かもしれませんが)

>県の名前と番号を紐付けると勝手に妄想していますがこんな方法はありますでしょうか? 
>テキストボックスに○○地方とあったら、そこの地域すべてを塗りつぶしなさい。
>こんな感じのものに対応させたいです。 
だとすると、県名に対して、県コードと地方コードを持たせる必要がありますね。
どうするのか判らないのですがイメージとしては
北海道,01,51(=北海道地方)
青森,02,52(=東北地方)
秋田,03,52
宮城,04,52
千葉,08,53(=関東地方)
埼玉,09,53
のような感じで紐付けておき、関東地方を指定したら53を持つ県をすべて塗る、とか。
あるいは、地方名に対して県名を突っ込んでおくのもありでしょう(あくまでもイメージです)。

そういったデータはコードの中に書いておくのもありですし、外部ファイル(CSVとか)に
格納しておくのもありですし、DBを使うのもありでしょう。
投稿者 よねKEN  (社会人) 投稿日時 2011/5/26 21:58:02
> M.Matrix00 = 1.0F
> M.Matrix11 = 1.0F
> M.Matrix22 = 1.0F
> M.Matrix33 = 0.5F
> M.Matrix44 = 1.0F
> 個人的にここでどんなことが行われているのか知りたいです。
> ご教授ください。 

詳しい話は以下のURLをご覧ください。

方法 : カラー行列を使用して単一色を変換する
http://msdn.microsoft.com/ja-jp/library/6tf7sa87.aspx

読んでみてよくわからなかったら、今は一旦あきらめてもよいと思います。
このURLの説明に出てくる行列、ベクターは高校の数学で習う用語ですし、
高校数学でそういったことを習ったとしても、それだけでは理論をきちんと
理解するのは難しいので。
(カラー行列がどうこうという話は、情報工学の中の画像処理系の分野の言葉だと思いますので、
 高校では習わないですし、専門学校や大学でもそういう専門の勉強を選択しなければ習いません。
 そういうわけで、そういう学習課程を経ない場合は、必要な知識を独学で身に着けることになりますね)

で、現実的には、理論的な話ではなく、
このコードサンプルの話としては以下の点だけ理解しておけばよいかと思います。

変数M(ColorMatrixクラスのオブジェクト)には色を変換するための情報を入れます。

ColorMatrixクラスのMartixXX(XXは数字)という名前のプロパティは、
Matrix00~Matrix44にはすべてに1.0を設定し、
00、11、22、33、44といった数字の揃った番号でないMatrix01、Matrix02…にはすべて0.0を設定しておくと「何も変換しない」という変換ルールになります。
(数学の用語で言うと単位行列といいます)

で、このうちMatrix33だけを0.0~1.0をいろいろいじってみるとわかるのですが、
Matrix33の設定内容に応じて画像の透明度が変わります。
コードサンプルでは0.5としているのでちょうど半透明となります。1.0なら不透明、0.0なら透明です。
投稿者 h-k  (社会人) 投稿日時 2011/5/26 22:17:18
皆さんありがとうございました。
とりあえずここで解決とし、ずーっと後でいろいろと理解できるようになってから再び質問したいと思います。
これからも宜しくお願いします。
投稿者 YAS  (社会人) 投稿日時 2011/6/15 19:45:17
遅いレスでごめんなさい。GoogleChartを使う例です。
少し長くなってしまったので,2回に分けて投稿します。

Option Explicit On
Option Strict On

Imports System.Net

Public Class Form1

    Private Shadows Class Region
        Public Name As String
        Public Code() As Integer
        Sub New(ByVal Name As StringByVal Code() As Integer)
            Me.Name = Name
            Me.Code = Code
        End Sub
    End Class

    Private Pref() As Region = {New Region("北海道", {1}), New Region("青森", {2}), New Region("岩手", {3}), New Region("宮城", {4}), New Region("秋田", {5}),
                                New Region("山形", {6}), New Region("福島", {7}), New Region("茨城", {8}), New Region("栃木", {9}), New Region("群馬", {10}),
                                New Region("埼玉", {11}), New Region("千葉", {12}), New Region("東京", {13}), New Region("神奈川", {14}), New Region("新潟", {15}),
                                New Region("富山", {16}), New Region("石川", {17}), New Region("福井", {18}), New Region("山梨", {19}), New Region("長野", {20}),
                                New Region("岐阜", {21}), New Region("静岡", {22}), New Region("愛知", {23}), New Region("三重", {24}), New Region("滋賀", {25}),
                                New Region("京都", {26}), New Region("大阪", {27}), New Region("兵庫", {28}), New Region("奈良", {29}), New Region("和歌山", {30}),
                                New Region("鳥取", {31}), New Region("島根", {32}), New Region("岡山", {33}), New Region("広島", {34}), New Region("山口", {35}),
                                New Region("徳島", {36}), New Region("香川", {37}), New Region("愛媛", {38}), New Region("高知", {39}), New Region("福岡", {40}),
                                New Region("佐賀", {41}), New Region("長崎", {42}), New Region("熊本", {43}), New Region("大分", {44}), New Region("宮崎", {45}),
                                New Region("鹿児島", {46}), New Region("沖縄", {47})}

    Private Regions As New List(Of Region)
    Private PictureBox As New PictureBox With {.Dock = DockStyle.Fill, .SizeMode = PictureBoxSizeMode.Zoom}
    Private ToolStrip As New ToolStrip
    Private WithEvents TextBox As New ToolStripTextBox With {.AutoSize = False, .Width = 200}
    Private WithEvents Button As New ToolStripButton With {.Text = "塗る"}

    Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load
        Regions.Add(New Region("北海道", {1}))
        Regions.Add(New Region("東北", {2, 3, 4, 5, 6, 7}))
        Regions.Add(New Region("関東", {8, 9, 10, 11, 12, 13, 14}))
        Regions.Add(New Region("中部", {15, 16, 17, 18, 19, 20, 21, 22, 23}))
        Regions.Add(New Region("関西", {24, 25, 26, 27, 28, 29, 30}))
        Regions.Add(New Region("中国", {31, 32, 33, 34, 35}))
        Regions.Add(New Region("四国", {36, 37, 38, 39}))
        Regions.Add(New Region("九州", {40, 41, 42, 43, 44, 45, 46}))
        Regions.Add(New Region("沖縄", {47}))
        Me.Size = New Size(800, 600)
        Me.ToolStrip.Items.AddRange({Me.TextBox, Me.Button})
        Me.Controls.AddRange({Me.PictureBox, Me.ToolStrip})
        Me.Button.PerformClick()
        Me.TextBox.TextBox.Select()
    End Sub

投稿者 YAS  (社会人) 投稿日時 2011/6/15 19:46:29
続きです。


    Private Sub Button_Click(ByVal sender As ObjectByVal e As System.EventArgs) Handles Button.Click
        Dim PrefCode As New List(Of Integer)
        Dim Text As String = Me.TextBox.Text
        If Text <> String.Empty Then
            'まず県名で検索 
            For Each c As Integer() In (From p As Region In Me.Pref Where p.Name Like "*" & Text & "*" Or Text Like p.Name & "*" Select p.Code).ToArray
                PrefCode.AddRange(c)
            Next
            '県名で一致しなければ地方名で検索 
            If PrefCode.Count = 0 Then
                For Each c As Integer() In (From p As Region In Me.Regions Where p.Name Like "*" & Text & "*" Or Text Like p.Name & "*" Select p.Code).ToArray
                    PrefCode.AddRange(c)
                Next
            End If
        End If
        Using wc As New WebClient()
            Dim Map As Byte() = wc.DownloadData(CreateUrl(PrefCode.ToArray))
            Dim ImgConv As New ImageConverter
            Me.PictureBox.Image = DirectCast(ImgConv.ConvertFrom(Map), Image)
        End Using
    End Sub

    Private Function CreateUrl(ByVal PrefCode() As IntegerAs String
        Dim Url As String = "https://chart.googleapis.com/chart?" &
            "cht=map:fixed=24,122,46,149" &
            "&chf=bg,s,99CCFF" &
            "&chs=600x500"
        Dim chldLst As New List(Of String)
        Dim chcoLst As New List(Of String)
        For Each p As Region In Pref
            For Each c As Integer In p.Code
                chldLst.Add("JP-" & c.ToString("00"))
                If PrefCode.Contains(c) Then
                    chcoLst.Add("FFFF00"'黄色 
                Else
                    chcoLst.Add("696969"'灰色 
                End If
            Next
        Next
        Dim chld As String = "&chld=" & String.Join("|", chldLst)
        Dim chco As String = "&chco=C0C0C0|" & String.Join("|", chcoLst)
        Return Url & chld & chco
    End Function

    Private Sub TextBox_KeyPress(ByVal sender As ObjectByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox.KeyPress
        If e.KeyChar = ChrW(Keys.Enter) Then
            Me.Button.PerformClick()
            e.Handled = True
        End If
    End Sub

End Class