【テクニカル・上級編】【実務中級】AcadDocument.Utility.AngleToStringで取得した角度を、測量単位(度分秒)形式でExcelへエクスポートする変換ロジック – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAを掌握する極限の知見:測量単位(度分秒)の極致 – ラジアンからExcelへの非妥協的変換ロジック

AutoCAD VBAにおいて、ジオメトリの計算と外部システム(特にExcel)との連携は、実務開発者にとって避けて通れない領域である。特に測量・土木分野の成果品作成において、AutoCAD内部が保持する「ラジアン(Radian)」を、日本の現場で絶対的な基準とされる「度分秒(DMS: 度・分・秒)」形式へ正確に変換し、Excelへ流し込む処理は、幾度となく実装を要求されるコア要件だ。

一般的な入門書やリファレンスサイトには、「`AngleToString`を使えば文字列になる」といった表面的な記述しか見当たらない。しかし、シニアエンジニアが直面する現実はもっとシビアである。
AutoCADの内部設定(`AUNITS`や`AUPREC`)に依存した文字列出力では、機械的なExcelでの数値集計や、厳密なフォーマット統一が求められる成果品データとしては使い物にならない。また、COMオブジェクトの寿命管理や、大規模図面からのデータ抽出におけるパフォーマンス劣化も考慮しなければ、実用に耐えるシステムとは言えない。

本稿では、AutoCAD VBAのオブジェクトモデルの深層を突き詰め、ラジアン値からダイレクトに度分秒を算出してExcelへ美しくエクスポートする、妥協なき変換ロジックの全貌を解説する。

1. AutoCAD内部における角度データの構造と落とし穴

AutoCADの図面空間では、すべての角度は「ラジアン」で管理されている。そして、オブジェクトモデルの `AcadDocument.Utility` オブジェクトには、角度を文字列化する `AngleToString` メソッドが存在する。

‘ 一般的なアプローチ(非推奨:環境依存性が高すぎる)
Dim angleRad As Double
Dim angleStr As String
angleStr = ThisDrawing.Utility.AngleToString(angleRad, acDegreeMinutesSeconds, 2)

このメソッドは手軽だが、以下の致命的な欠点を抱えている。
1. 図面環境への依存: `AUNITS`(角度単位)や `AUPREC`(精度)のシステム変数に結果が引きずられ、図面ごとに出力が変わるリスクがある。
2. Excel連携の困難さ: 返り値が文字列(例: `”30°15’20\””`)となるため、Excel側で数値としての四捨五入や、統計計算、後続の座標計算(逆求点計算など)に組み込むことが極めて困難。

真にロバストなシステム構築を目指すのであれば、AutoCADの環境設定に依存せず、ラジアン値を数学的に直接分解し、任意の精度で度・分・秒の数値およびフォーマット済み文字列を生成する自前 hält(保持)のロジックをVBA内に実装すべきである。

2. 実装アーキテクチャの設計思想

今回構築するモジュールは、以下の要件を完全に満たす設計とする。

  • 完全な自己完結性: 図面の表示単位系(`AUNITS`)を一切参照せず、純粋な数学的ラジアン演算を行う。
  • メモリの極限最適化: AutoCADのCOMオブジェクト(`Utility`や各種ラッパー)への無駄なアクセスを排除し、ループ処理時のオーバーヘッドを極小化。
  • Excelとの堅牢なバインド: 型安全なデータ構造でExcelのセル群へバルクインポート(一括転送)を実現。

3. 実用コード:ラジアンから度分秒変換&Excelエクスポート

以下のコードは、選択された線分(`AcadLine`)群の傾き(ラジアン)を抽出し、度分秒へ変換した上で、Excelのワークシートへ高速に出力する実戦的なプロシージャである。

Option Explicit

‘ =================================================================================
‘ 処理名: ExportAngleDMS_To_Excel
‘ 概要 : AutoCADの図面から全線分エンティティの角度(ラジアン)を抽出し、
‘ 独自の数学的変換によって「度分秒」形式に調律してExcelへ出力する。
‘ =================================================================================
Public Sub ExportAngleDMS_To_Excel()
‘ 1. 宣言とオブジェクトのライフサイクル管理
Dim acadApp As Object
Dim acadDoc As AcadDocument
Dim sset As AcadSelectionSet
Dim ent As AcadEntity
Dim lineObj As AcadLine

Dim xlApp As Object
Dim xlWb As Object
Dim xlWs As Object

Dim rawData() As Variant
Dim rowCount As Long
Dim i As Long

On Error GoTo ErrorHandler

‘ AutoCADセッションの取得(早期/遅延バインディングのハイブリッド設計)
Set acadDoc = ThisDrawing

‘ 2. セレクションセットの安全な生成と破棄(メモリリーク防止の極意)
On Error Resume Next
acadDoc.SelectionSets.Item(“DMS_Export_SS”).Delete
On Error GoTo ErrorHandler

Set sset = acadDoc.SelectionSets.Add(“DMS_Export_SS”)

‘ モデル空間から「線分(Line)」のみをフィルタリング抽出
Dim filterType(0) As Integer
Dim filterData(0) As Variant
filterType(0) = 0
filterData(0) = “LINE”

sset.Select acSelectionSetAll, , , filterType, filterData

If sset.Count = 0 Then
MsgBox “対象となる線分エンティティが存在しません。”, vbExclamation, “処理中断”
GoTo Cleanup
End If

‘ 3. 配列のメモリ確保(Excelへの高速一括転送用)
ReDim rawData(1 To sset.Count, 1. To 4)
rowCount = 0

For i = 0 To sset.Count – 1
Set ent = sset.Item(i)
If TypeOf ent is AcadLine Then
Set lineObj = ent
rowCount = rowCount + 1

‘ 角度の取得(ラジアン)
Dim rad As Double
rad = CalculateLineAngle(lineObj)

‘ 度分秒への数学的分解
Dim dmsResult As TypeDMS
dmsResult = ConvertRadianToDMS(rad)

‘ 配列への格納 (Handle, Radian, DMSString, DecimalDegrees)
rawData(rowCount, 1) = lineObj.Handle
rawData(rowCount, 2) = rad
rawData(rowCount, 3) = dmsResult.FormattedString
rawData(rowCount, 4) = dmsResult.DecimalDegrees
End If
‘ オブジェクト変数の明示的解放(VBAのCOM参照カウンタ対策)
Set ent = Nothing
Set lineObj = Nothing
Next i

If rowCount = 0 Then
MsgBox “有効な線分データがありませんでした。”, vbExclamation, “処理中断”
GoTo Cleanup
End If

‘ 4. Excelアプリケーションの制御(遅延バインディングによるバージョン非依存化)
Set xlApp = CreateObject(“Excel.Application”)
xlApp.Visible = True
Set xlWb = xlApp.Workbooks.Add
Set xlWs = xlWb.Sheets(1)
xlWs.Name = “測量角度成果”

‘ ヘッダーの書き込み
xlWs.Cells(1, 1).Value = “Entity Handle”
xlWs.Cells(1, 2).Value = “ラジアン (Radian)”
xlWs.Cells(1, 3).Value = “度分秒 (DMS)”
xlWs.Cells(1, 4).Value = “10進度 (Degree)”

‘ 一括データ転送(Variant配列による爆速書き込み)
xlWs.Range(xlWs.Cells(2, 1), xlWs.Cells(rowCount + 1, 4)).Value = rawData

‘ 書式設定の適用
xlWs.Columns(“A:D”).AutoFit

MsgBox “処理が正常に完了しました。出力件数: ” & rowCount & “件”, vbInformation, “完了”

Cleanup:
‘ 5. リソースの完全解放(メモリ最適化の極み)
If Not sset Is Nothing Then
sset.Delete
Set sset = Nothing
End If
Set acadDoc = Nothing
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume Cleanup
End Sub

‘ — 構造体定義:度分秒保持用 —
Public Type TypeDMS
Degrees As Long
Minutes As Long
Seconds As Double
FormattedString As String
DecimalDegrees As Double
End Type

‘ =================================================================================
‘ 関数名: ConvertRadianToDMS
‘ 概要 : ラジアン値を厳密な数学演算で度・分・秒に分解する
‘ =================================================================================
Private Function ConvertRadianToDMS(ByVal radian As Double) As TypeDMS
Const PI As Double = 3.14159265358979
Dim degTotal As Double
Dim d As Long, m As Long, s As Double
Dim res As TypeDMS

‘ ラジアンを10進度へ変換
degTotal = radian (180 / PI)
res.DecimalDegrees = degTotal

‘ 度の抽出
d = Int(degTotal)
degTotal = (degTotal – d) 60

‘ 分の抽出
m = Int(degTotal)
degTotal = (degTotal – m) 60

‘ 秒の抽出(小数点第2位で丸め処理)
s = Round(degTotal, 2)

‘ 秒が60に繰り上がった場合の桁上がり補正
If s >= 60# Then
s = 0#
m = m + 1
End If
If m >= 60 Then
m = 0
d = d + 1
End If

res.Degrees = d
res.Minutes = m
res.Seconds = s

‘ 「30°15’20″」形式の文字列フォーマット構築
res.FormattedString = d & “°” & Format(m, “00”) & “‘” & Format(s, “00.00”) & ‘”‘

ConvertRadianToDMS = res
End Function

‘ =================================================================================
‘ 関数名: CalculateLineAngle
‘ 概要 : 線分の始点から終点への方向角をラジアンで精密計算する
‘ =================================================================================
Private Function CalculateLineAngle(ByVal ln As AcadLine) As Double
Dim pStart As Variant
Dim pEnd As Variant
Dim dx As Double, dy As Double

pStart = ln.StartPoint
pEnd = ln.EndPoint

dx = pEnd(0) – pStart(0)
dy = pEnd(1) – pStart(1)

‘ Atn2関数による全象限を考慮した正確なラジアン計算
CalculateLineAngle = Atn2(dy, dx)

‘ 負のラジアンを正の方向角(0〜2π)へ正規化する場合の処理(必要に応じて有効化)
If CalculateLineAngle < 0 Then CalculateLineAngle = CalculateLineAngle + (2 3.14159265358979) End If End Function ' VBA標準にはないAtn2関数の実装(全象限対応の数学的必須パーツ) Private Function Atn2(ByVal y As Double, ByVal x As Double) As Double If x > 0 Then
Atn2 = Atn(y / x)
ElseIf x < 0 Then If y >= 0 Then
Atn2 = Atn(y / x) + 3.14159265358979
Else
Atn2 = Atn(y / x) – 3.14159265358979
End If
Else
If y > 0 Then
Atn2 = 3.14159265358979 / 2
ElseIf y < 0 Then Atn2 = -3.14159265358979 / 2 Else Atn2 = 0 End If End If End Function ---

4. シニアエンジニアが押さえるべき「極限の知見」とパフォーマンス最適化

上記のコードベースを現場に導入するにあたり、アーキテクトとして担保すべき技術的ポイントを解説する。

A. 揮発性オブジェクトとメモリリークの完全排除

AutoCAD VBAにおいて、`SelectionSets` は明示的に削除(`Delete`)しない限り、図面ファイル(.dwg)のセッション内に残り続ける。これはメモリリークを引き起こし、大規模図面を連続処理する際にAutoCADをクラッシュさせる主原因となる。上記のコードでは、エラーハンドリングの有無にかかわらず、終了処理(`Cleanup`)ラベルで確実に `SelectionSet` をパージする堅牢なライフサイクル管理を施している。

B. COM境界を跨ぐ通信コストの最小化

AutoCADとExcelの間でのデータやり取りにおいて、セル単位で `Cells(i, j).Value = …` のようにループ内でアクセスするのは極刑レベルのアンチパターンである。COMのプロセス境界を何千回も跨ぐことになり、実行速度が劇的に低下する。
本実装では、VBAの二次元配列(`Variant`配列)にデータを一度すべて蓄積し、`Range.Value = rawData` によって一撃でExcelへメモリ転送(バルクインポート)している。この手法により、数千件のエンティティ処理であっても一瞬で完了するパフォーマンスを実現している。

C. 独自のAtn2関数による数学的完全性

VBAの標準数学関数には `Atn2` が備わっていない。安易に `Atn(dy / dx)` を使用すると、X座標が0の場合のゼロ除算エラー(Overflow)や、象限(第1〜第4)の判定ミスを引き起こす。測量計算において方向角の算出ミスは致命傷であるため、全象限を完全網羅した `Atn2` をプライベート関数として実装し、計算の信頼性を担保している。

5. 総括

AutoCAD VBAとExcelの連携は、単なる「お絵描きソフトの自動化」ではない。それは精密な幾何学データを産業データへ昇華させるための高度なエンジニアリング領域である。

今回解説したラジアンから度分秒への数学的分解、そしてメモリ最適化を極めたバルク転送の知見は、あなたのVBAシステムを「動くが不安定なおもちゃ」から「ミッションクリティカルなエンタープライズツール」へと引き上げる確かな武器となるはずだ。
レガシーと侮るなかれ、VBAの深淵を極めた者だけが、圧倒的な生産性の果実を手にすることができる。

タイトルとURLをコピーしました