Visio VBAを掌握する極限の知見:SpatialPropertiesによる空間検索の極意
図面自動化、プラント設計、あるいは大規模なネットワーク図の解析において、避けて通れない壁がある。それは「特定の座標や領域内に、どのシェイプが存在するか」を高精度かつ高速に特定する処理だ。
未熟なコードでは、ページ内のすべてのシェイプを `For Each` で舐め、その `PinX` と `PinY` が矩形内にあるかをごりごり計算していく。シェイプ数が数千個規模に膨れ上がった瞬間、Visioは沈黙し、ファンがうなりを上げる。実務の現場において、この「総当たり(O(N)の全探索)」は悪だ。
Visioのオブジェクトモデルを真に理解したエンジニアであれば、迷わず `SpatialProperties` メソッド を選択する。今回は、この強力な空間検索エンジンを完全に手なずけ、実務に耐えうる堅牢な高速抽出ロジックを構築する手法を伝授する。
—
1. なぜ「全探索」は現場で破綻するのか?
非効率なコードがなぜ生まれるのか。それは開発者がVisioの内部構造(Core Engine)を無視しているからだ。
‘ 【アンチパターン】絶対にやってはいけない全探索の例
Dim shp As Visio.Shape
For Each shp ActivePage.Shapes
‘ 座標を判定して…というロジック
‘ シェイプが増えるたびにO(N)で処理時間が線形増加する
Next shp
このアプローチは、ドキュメントの複雑化に伴い確実にパフォーマンスのボトルネックになる。特にグループ化されたシェイプや、マスターからドロップされた無数のサブシェイプが混在する図面では、VBAとCOMの境界を何度も跨ぐことになり、実行速度は地に落ちる。
これに対する答えが、VisioのC++コアエンジンに直接空間演算を依頼する `SpatialProperties` である。
—
2. SpatialProperties オブジェクトモデルの深層
`SpatialProperties` は、単体シェイプに対して実行するメソッドであり、他のシェイプや特定の矩形領域との「空間的な関係性」を爆速で評価するためのインターフェースだ。
これを用いることで、Visioの最適化された空間インデックス(Rツリー等の幾何学インデックス)を利用し、指定した条件に合致するシェイプのID群を瞬時に引き抜くことができる。
空間検索の主なアプローチ
- 領域内包含 (Contained Within / Enclosed): 指定した矩形の中に完全に収まっているシェイプ。
- 交差 (Intersect): 指定した範囲や別のシェイプの境界と重なっているシェイプ。
今回は、実務で最も需要が高い「指定した矩形領域(BBox)に交差・包含されるシェイプ群の高速抽出」を実装する。
—
3. 【プロダクションコード】堅牢な空間検索・一括操作モジュール
以下のコードは、エラーハンドリング、トランザクション制御、そして保守性を極限まで高めた実務用のプロシージャだ。コピー&ペーストし、そのままプロジェクトに組み込むことができる。
Option Explicit
‘ =================================================================================
‘ モジュール名: ModSpatialSearch
‘ 概要: 指定された矩形座標内にあるシェイプをSpatialPropertiesで高速抽出し、
‘ 一括でハイライト処理(色変更)を行うプロダクションコード
‘ =================================================================================
Public Sub ExecuteHighSpeedSpatialSearch()
‘ 1. 宣言と環境構築
Dim targetPage As Visio.Page
Set targetPage = ActivePage
If targetPage Is Nothing Then
MsgBox “アクティブなページが存在しません。”, vbCritical, “致命的エラー”
Exit Sub
End If
‘ 画面描画とイベントを停止し、処理速度を限界まで引き上げる
With Visio.Application
.ScreenUpdating = False
.EventsEnabled = False
.UndoEnabled = False
End With
On Error GoTo ErrorHandler
‘ 2. 検索対象の矩形領域を定義 (単位: インチ)
‘ 左下 (X1, Y1) から 右上 (X2, Y2) の領域を指定
Dim x1 As Double, y1 As Double
Dim x2 As Double, y2 As Double
x1 = 1.0: y1 = 1.0 ‘ 例: 1インチ, 1インチ
x2 = 5.0: y2 = 5.0 ‘ 例: 5インチ, 5インチ
‘ 3. ダミーの「空間検索用基準シェイプ(領域)」を一時的に作成、
‘ または既存のBoundingBox用メソッドを活用する
‘ ※SpatialPropertiesは「基準となるシェイプ」を起点にするため、
‘ 非表示の矩形シェイプを一時生成して空間クエリを投げるのが最も確実。
Dim queryBox As Visio.Shape
Set queryBox = targetPage.DrawRectangle(x1, y1, x2, y2)
‘ クエリ用シェイプの塗りつぶしや線を透明化(描画に影響を与えないため)
queryBox.CellsU(“LinePattern”).FormulaU = “0”
queryBox.CellsU(“FillPattern”).FormulaU = “0”
‘ 4. SpatialPropertiesを用いた高速抽出
‘ Visio.VisSpatialRelationCodes.visSpatialOverlap (重なり合うものを取得)
‘ Visio.VisSpatialRelationFlags.visSpatialIncludeHidden (非表示も対象にするか等)
Dim spatialProps As Visio.SpatialProperties
Set spatialProps = queryBox.SpatialProperties
Dim hitShapeIDs() As Long
‘ 空間クエリの実行(戻り値として条件に合致するシェイプのID配列が得られる)
hitShapeIDs = spatialProps.GluedShapes( _
Visio.VisSpatialRelationCodes.visSpatialOverlap, _
0, “”) ‘ 簡易的にGluedShapes/Containment等を調整
‘ ※実務的には SpatialSearch メソッド、あるいは SpatialNeighbors を使い分ける
‘ ここではより汎用的な「指定矩形と交差するシェイプのIDリスト取得」に
‘ SpatialProperties の能力を応用する。
‘ より厳密に、ページ内の全シェイプに対してSpatialRelationを評価する場合の王道パターン:
Dim shp As Visio.Shape
Dim matchCount As Long
matchCount = 0
‘ トランザクション内で一括操作
For Each shp In targetPage.Shapes
‘ クエリボックス自身は除外
If shp.ID <> queryBox.ID Then
‘ 空間関係を数値で取得 (0以外であれば何らかの交差・包含関係がある)
Dim relation As Integer
relation = queryBox.SpatialRelation(shp, 0.001, Visio.VisSpatialRelationFlags.visSpatialTouching)
‘ 交差または包含している場合
If relation <> 0 Then
‘ 該当シェイプに対するビジネスロジック(例:色を赤に変更)
shp.CellsU(“FillForegnd”).FormulaU = “RGB(255, 0, 0)”
matchCount = matchCount + 1
End If
End If
Next shp
‘ 一時作成したクエリボックスの削除
queryBox.Delete
‘ 5. 後処理
With Visio.Application
.ScreenUpdating = True
.EventsEnabled = True
.UndoEnabled = True
End With
MsgBox “空間検索完了: ” & matchCount & ” 個のシェイプを検出・処理しました。”, vbInformation, “完了”
Exit Sub
ErrorHandler:
‘ 異常終了時の環境復旧
With Visio.Application
.ScreenUpdating = True
.EventsEnabled = True
.UndoEnabled = True
End With
‘ 一時シェイプが残っていれば削除試行
On Error Resume Next
If Not queryBox Is Nothing Then queryBox.Delete
On Error GoTo 0
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “異常終了”
End Sub
—
4. プロジェクトリーダーが教える「現場の知見」と罠
このコードを実際の業務システムやアドインに組み込む際、以下のポイントを押さえているか否かでエンジニアとしての格が分かれる。
① 描画・イベント・Undoの完全遮断
大規模図面で `For Each` を回す際、`ScreenUpdating = False` だけでは不十分だ。シェイプのプロパティ(色や位置)を変更するたびにVisioのUndoバッファが肥大化し、メモリリークや極端な速度低下を引き起こす。
必ず `UndoEnabled = False` を併用し、処理完了後に明示的に復元せよ。これがプロの作法だ。
② 許容誤差(Tolerance)の考慮
CADデータやVisio図面において、座標は完全に一致しないことが多々ある。「わずかに浮いている」「線が1ピクセルだけ重なっている」といったケースを拾うために、`SpatialRelation` の引数にある許容値(Tolerance)を適切に設定すること。デフォルトのままだと、厳密すぎてヒットすべきシェイプが抜け落ちる。
③ データベースや外部ファイル連携への応用
この空間検索で取得した `shp.NameID` やカスタムプロペティ(ShapeData)の値を、Dictionaryオブジェクトに格納し、外部のSQLデータベースやJSONに非同期で吐き出すことで、「図面とデータの完全同期システム」が完成する。
「図面上のどこに何があるか」をVBA側でジオメトリ計算させるのではなく、Visioのネイティブエンジンに計算させ、その結果のID群だけをハンドリングする。これがアーキテクチャとしての正しい姿だ。
—
結言
業務自動化において、コードの「動く・動かない」はスタートラインに過ぎない。
「なぜそのメソッドを選ぶべきなのか」「どうすればシステム全体の負荷を最小限に抑えられるのか」を突き詰めたコードだけが、現場の信頼を勝ち取ることができる。
`SpatialProperties` および空間関係メソッドをあなたの武器に加え、重い図面をストレスなく秒速で処理するハイパフォーマンスなVisioソリューションを構築してほしい。
