たとえば、下のような図。これはある都市の町丁目の境界線を引いた中に、それぞれの町丁目の建築確認申請件数をドットの数で表示したものです。
こんな感じの図がほしいことがよくあります。市町村別の人口とか、コンビニの軒数とか、いろいろなものがありそうですね。表をにらんでいてもわからないけど、こういう風に図にしてみると全体の傾向や構造が見えてくることがよくあります。
しかし、これを手で書くのは大変です。
そこで、これをイラストレーターで簡単に描けるように、工夫してみました。
もっと賢い方法があるかもしれませんが、けっこう素直な手順で作ったので、コード自体は少し長くなりました。しかし、上の程度のスケールであれば、あっという間に描画が終了します。
DrawMark() がこの仕事を行う本体です。
イラストレーターで legend と boundary のレイヤーに必要なパスを作成したファイルを用意しておきます。
次に、境界線のパスの名前と凡例のパスの名前、マークの数を引数として DrawMark() を呼ぶと、境界線で囲まれた範囲の中央部に、ほぼ正方形の領域に整列した所定の個数のマークが描かれます。マークは、指定した凡例マークのパスをコピーして設置します。描かれたマークは、"marks"レイヤー内に境界線ごとにグループ化されてはいります。
このマクロを呼ぶ前に、対象となるイラストレーター・ドキュメント AiDoc を取得しておく必要がありますが、ここでは「イラストレーターを開く」で作成した SelectAiFile() を使いました。これを Public AiDoc という宣言とともに、どこかの標準モジュールにいれておきましょう。
DrawMark() の使い方は、DrawMarksDemo() をご覧ください。ここでは、それぞれの引数を直接列挙していますが、実際はどこかのワークシート上に作成された表を For~Next などのループで読み込んで DrawMark() に渡すことになるはずです。
DrawMark() は、コピーした凡例マークを設置するアドレスを計算するために PlaceBoxRectangle() と GetMarkPosition() を呼んでいますが、いっしょのモジュールに入れると煩雑なので、別のモジュールにいれました。
標準モジュール DrawMark()
'意味を持つレイヤーの名称 Private Const areaLayerName = "boundary" Private Const legendLayerName = "legend" Private Const marksLayerName = "marks" 'マークを描く際の隣同士の間隔 Private Const markGap = 4
Private areaPath Private legendPath
'----- DrawMark() の使い方 Public Sub DrawMarksDemo() 'イラストレータファイルを選択して、ドキュメントを取得 If Not SelectAiFile Then Exit Sub 'それぞれのエリア内に指定のマークを指定個数描いていく DrawMark "昭和町", "circle", 25 DrawMark "平成町", "rectangle", 10 DrawMark "令和町", "star", 5 End Sub '----- 指定個数のマークを所定の場所に描く Public Function DrawMark(areaName, legendName, count) As Boolean Dim newGroup, newMark, index If count <= 0 Then Exit Function If Not getPath(areaName, legendName) Then Exit Function legendPath.Locked = False Set newGroup = getMarksLayer.GroupItems.Add PlaceBoxRectangle areaPath, legendPath, count, markGap '指定の凡例マークをコピーして、所定の位置に移動しグループに追加する For index = 1 To count Set newMark = legendPath.Duplicate newMark.left = GetMarkPosition(index).X newMark.top = GetMarkPosition(index).Y newMark.moveToBeginning newGroup Next DrawMark = True End Function '----- パスを検索 Private Function getPath(areaName, legendName) As Boolean On Error Resume Next Set areaPath = AiDoc.Layers(areaLayerName).PageItems(areaName) If Err.Number <> 0 Then notExistName areaLayerName, areaName: Exit Function Set legendPath = AiDoc.Layers(legendLayerName).PageItems(legendName) If Err.Number <> 0 Then notExistName legendLayerName, legendName: Exit Function getPath = True End Function '----- パスが見つからなかった名前を列挙・保存 Private Sub notExistName(layer, name) MsgBox _ "レイヤー [ " & layer & " ] の中に" & vbCrLf & _ "[ " & name & " ] というパスが/ありません。", _ vbOKOnly, "" End Sub '----- "marks"レイヤーを取得 (なければ新たに作成) Private Function getMarksLayer() As Object On Error Resume Next Set getMarksLayer = AiDoc.Layers(marksLayerName) If Err.Number <> 0 Then Set getMarksLayer = AiDoc.Layers.Add getMarksLayer.name = marksLayerName End If End Function
標準モジュール PlaceBoxRectangle(), GetMarkPosition()
'Positionオブジェクトの宣言 Public Type Position X As Double Y As Double End Type '親ポリゴンのパス、凡例マークのパス Private areaPath, markPath '描画するマークの個数 Private marksCount As Long 'マーク描画エリアの位置 Private boxPosition As Position '描画するマークの行・列数 Private columnsCount As Long Private rowsCount As Long '隣接するマークの間隔 Private markGap As Long
'----- 描画エリアの位置、マークの行・列数を計算しておく Public Sub PlaceBoxRectangle(areaPathItem, markPathItem, count, mGap) Set areaPath = areaPathItem Set markPath = markPathItem marksCount = count markGap = mGap calculateBoxSize End Sub '----- index 番目のマークを描画する位置 Public Function GetMarkPosition(index) As Position Dim row, column row = (index - 1) \ columnsCount column = (index - 1) Mod columnsCount GetMarkPosition.X = boxPosition.X + (markPath.width + markGap) * column GetMarkPosition.Y = boxPosition.Y - (markPath.height + markGap) * row End Function '----- マーク描画エリアの位置と大きさ、描画するマークの行・列数の計算 Private Sub calculateBoxSize() Dim boxWidth As Double Dim boxHeight As Double columnsCount = WorksheetFunction.RoundUp(marksCount ^ (1 / 2), 0) rowsCount = WorksheetFunction.RoundUp(marksCount / columnsCount, 0) boxWidth = (markPath.width + markGap) * columnsCount - markGap boxHeight = (markPath.height + markGap) * rowsCount - markGap With areaPath boxPosition.X = .left + .width / 2 - boxWidth / 2 boxPosition.Y = .top - .height / 2 + boxHeight / 2 End With End Sub