外部変形の作り方
※Hinoのスキルは大したことないというのを前提に、気になるところはスルーしながら眺めてください。


とりあえず小型モジュール集に幾つかのサンプルスクリプトを記述してありますのでそれを組み合わせて、画像をトレースした敷地形状を、指定の面積に調整する「R敷地」を作ってみます。


元になるデータは↑こんなのです。


面積算定ルーチンでは多角形ソリッド図形の面積取得が気楽なので、敷地図は線要素とソリッド図形要素で描画したものを取り扱うことにします。


それではやってみます。



まずエクセルをJWWの外部変形から呼び出すためにBATファイルをとりあえず作ってみます。


R敷地.BAT
@rem R敷地(敷地面積を整合させます)
@echo off

REM #jww JWW用外部プログラム宣言

REM #h2 選択方法

REM #cd 外変と同じフォルダにjwc_temp.txtを出力する

REM #hc 敷地の元になる輪郭線とソリッドを選択して下さい

REM #g0 書き込みグループレイヤ対象
REM #e


start /w excel R敷地.xls

BATファイルの書き方はJWW_SMPL.BATファイルに全て書いてありますので熟読して下さい。

今回は面積を取り扱うので、異縮尺を取り扱わないように書き込みグループのデータのみ(#g0)取り扱います。



上記BATファイルの最後の行でエクセルファイルの起動を記述してあります。

エクセルをエンジンとして使う場合にはなぜかファイル名の前に「start
/w 」の記述がないとうまくいかないことが多いのでこれはお約束として書いておきます。


JWWでこのBATファイルを呼び出すと、JWWは選択されたデータを「jwc_temp.txt」というファイルに書き出して、外部プログラムの処理が終了するまでじっと待ってくれます。


そのJWWが待ってくれている間にエクセルに何かをさせるのが外部変形です。

まず具体的な処理方法をプランしてみます。

  1.ソリッド図形の面積を集計

  2.集計した結果を表示し、整合させるべき面積を入力

  3.拡大率を座標に掛けて、基準点に基づいて書き出し


こんな感じで良さそうです。


それではまずファイルオープン時にマクロを実行させましょう。

Private Sub Workbook_Open()

  sikiti
End Sub

今回のメインルーチン名は「sikiti」にしました。


これをどこに記述するかといいますと


ここから「Visual Basic Editor」を立ち上げて




この「ThisWorkbook」にペーストします。




ペーストしました。

これでこのファイルは起動時に「sikiti」というマクロが実行されます。



このままでは「sikiti」のスクリプト内容を記述する場所がありませんので「標準モジュール」を挿入します


適当なところで右クリックして出てくるメニューから選んで下さい



無事挿入されました。

この「Module1」に外変本体を色々と仕込んでいきます。


外部テキストの参照方法からスクリプトを貼り付けてみます。

(以降追加された部分を太字で表示します)


スクリプト本体
Sub sikiti()

pth=thisworkbook.path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

'ここに処理を記述

Loop

End Sub



これにまず

  1.ソリッド図形の面積を集計

の処理を記述してみます。


この手の単純なルーチンは転用も効かせ易いのでサブルーチンとして仕込みます。

ここでは多角形ソリッド図形の面積取得を使うことにします。

Sub solid_s_get(tmp, s)
temp = tmp & " 0 0"
s = 0
a = Split(temp)
If UBound(a) = 8 Then a(7) = a(1): a(8) = a(2)
If UBound(a) >= 8 And a(0) = "sl" Then


x0 = Val(a(1)) '各座標を格納
x1 = 0
x2 = (a(3) - x0) / 1000
x3 = (a(5) - x0) / 1000
x4 = (a(7) - x0) / 1000
y0 = Val(a(2))
y1 = 0
y2 = (a(4) - y0) / 1000
y3 = (a(6) - y0) / 1000
y4 = (a(8) - y0) / 1000

la1 = Sqr((x1 - x2) ^ 2 + (y1 - y2) ^ 2)
la2 = Sqr((x2 - x3) ^ 2 + (y2 - y3) ^ 2)
la3 = Sqr((x3 - x4) ^ 2 + (y3 - y4) ^ 2)
la4 = Sqr((x4 - x1) ^ 2 + (y4 - y1) ^ 2)
la5 = Sqr((x3 - x1) ^ 2 + (y3 - y1) ^ 2)

sh1 = (la1 + la2 + la5) / 2
s1 = Sqr(sh1 * (sh1 - la1) * (sh1 - la2) * (sh1 - la5))
sh2 = (la3 + la4 + la5) / 2
s2 = Sqr(sh2 * (sh2 - la3) * (sh2 - la4) * (sh2 - la5))

s = s1 + s2

End If
End Sub

↑これをサブルーチンに置いておきます。

もう一つモジュールを追加してそこに貼り付けたり、スクリプト本体の下に置いたり適当にどうぞ。



このサブルーチンを使って面積を集計するルーチンを記述してみます。


スクリプト本体
Sub sikiti()

pth=thisworkbook.path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki=0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop

End Sub

これで選択されたデータ内のソリッド図形面積集計は完了です。

サブルーチンがあるのでえらい楽です。

ここでは jwc_temp.txt を最初の行から最後の行まで全ての中に存在する多角形ソリッドの面積を拾い出しました。


このまま
  2.集計した結果を表示し、整合させるべき面積を入力

に進んでみます。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み
s = 0
Dim solid_menseki
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop

menseki = InputBox("現在の面積は
"
& Round(solid_menseki, 2) &
"u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )


End Sub

念のため数値以外が入力された場合は終了するようにしました。

面積で取得した倍率を線分要素に当てはめていくために平方根を取っておきました。



最後は

    3.拡大率を座標に掛けて、基準点に基づいて書き出し
です。


その前に作図原点を指示するようにした方が便利そうですね。

これはBATファイルでの指示になりますからBATファイルに書き込んでおきます。


R敷地.BAT
@rem R敷地(敷地面積を整合させます)
@echo off

REM #jww JWW用外部プログラム宣言

REM #h2 選択方法

REM #cd 外変と同じフォルダにjwc_temp.txtを出力する

REM #hc 敷地の元になる輪郭線とソリッドを選択して下さい

REM #g0 書き込みグループレイヤ対象
REM #0 元図の拡大縮小基準点を指定して下さい

REM #1 拡大縮小後の作図位置を指定して下さい
REM #e


start /w excel R敷地.xls



これでBATファイルはOKです。

忘れないウチに作図原点を取得する部分を記述してみます。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

s = 0
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop


menseki = InputBox("現在の面積は
"
& Round(solid_menseki, 2) &
"u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み


If Left(tmp, 3) = "hp1" Then
'基点情報検索

a = Split(tmp) '半角スペースで切り分け

x = a(3) '3番目 = x

y = a(4) '4番目 = y
End If

Loop

End Sub

これで x と y に指示された作図原点の座標を格納できました。

ここでは jwc_temp.txt を最初の行から最後の行まで観察して基点情報を拾い出しました。

本当は最初の十数行だけ観察すれば充分なのですが手抜きです。




それでは出力方法を考えてみます。

元図にあるソリッドは無くて良いですね。

そうすると線分要素だけで済みそうです。

線種・線色・レイヤは書き込み属性を使いましょうか。


とりあえずファイルの書き出しからスクリプトを持ってきましょうか。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

s = 0
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop


menseki = InputBox("現在の面積は "
& Round(solid_menseki, 2) & "u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み


If Left(tmp, 3) = "hp1" Then '基点情報検索

a = Split(tmp) '半角スペースで切り分け

x = a(3) '3番目 = x

y = a(4) '4番目 = y
End If

Loop

Set fs = CreateObject("Scripting.FileSystemObject")
Set jwtmp = fs.CreateTextFile("jwc_temp.txt", True)
jwtmp.writeline ("")
jwtmp.Close

End Sub

このままではjwc_temp.txtを上書きしてしまいますね。

もう少しjwc_temp.txtは使う予定なので違うファイル名で書き出すことにします。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

s = 0
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop


menseki = InputBox("現在の面積は "
& Round(solid_menseki, 2) & "u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み


If Left(tmp, 3) = "hp1" Then '基点情報検索

a = Split(tmp) '半角スペースで切り分け

x = a(3) '3番目 = x

y = a(4) '4番目 = y
End If

Loop

Set fs = CreateObject("Scripting.FileSystemObject")
Set r_tmp = fs.CreateTextFile(pth & "\r_temp.txt", True)
r_tmp.writeline ("")
r_tmp.Close

End Sub

これでjwc_temp.txtと同じフォルダに r_temp.txt
というファイルで出力する準備が出来ました。


ここでもう一度jwc_temp.txtを開いて、線分データだけを拾い出して加工してみます。

線分データはヘッダが半角スペースで座標が4つだけですから簡単ですし。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

s = 0
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop


menseki = InputBox("現在の面積は "
& Round(solid_menseki, 2) & "u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み


If Left(tmp, 3) = "hp1" Then '基点情報検索

a = Split(tmp) '半角スペースで切り分け

x = a(3) '3番目 = x

y = a(4) '4番目 = y
End If

Loop

Set fs = CreateObject("Scripting.FileSystemObject")
Set r_tmp = fs.CreateTextFile(pth & "\r_temp.txt", True)
Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み
header = Left(tmp, 1)


If header = " " Then '線データの処理
a = Split(tmp)

x1 = Val(a(1)) * bairitsu + x '拡大縮小処理
y1 = Val(a(2)) * bairitsu + y
x2 = Val(a(3)) * bairitsu + x
y2 = Val(a(4)) * bairitsu + y
tmp = " " & x1 & " " & y1 & " " & x2 & " " & y2

r_tmp.writeline (tmp) '線分をファイルに出力
End If

Loop
r_tmp.Close

End Sub



だいぶできあがりました。

あとはファイルを保存せずにエクセルを終了するコードを足しておきましょうか。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

s = 0
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop


menseki = InputBox("現在の面積は "
& Round(solid_menseki, 2) & "u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み


If Left(tmp, 3) = "hp1" Then '基点情報検索

a = Split(tmp) '半角スペースで切り分け

x = a(3) '3番目 = x

y = a(4) '4番目 = y
End If

Loop

Set fs = CreateObject("Scripting.FileSystemObject")
Set r_tmp = fs.CreateTextFile(pth & "\r_temp.txt", True)
Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み
header = Left(tmp, 1)


If header = " " Then '線データの処理
a = Split(tmp)

x1 = Val(a(1)) * bairitsu + x '拡大縮小処理
y1 = Val(a(2)) * bairitsu + y
x2 = Val(a(3)) * bairitsu + x
y2 = Val(a(4)) * bairitsu + y
tmp = " " & x1 & " " & y1 & " " & x2 & " " & y2

r_tmp.writeline (tmp) '線分をファイルに出力
End If

Loop
r_tmp.Close

ThisWorkbook.Saved = True
Application.Quit

End Sub

そう言えば先程出力ファイルを r_temp.txtに変えたので、この処理をBATファイルに記述しておきます。



R敷地.BAT
@rem R敷地(敷地面積を整合させます)
@echo off

REM #jww JWW用外部プログラム宣言

REM #h2 選択方法

REM #cd 外変と同じフォルダにjwc_temp.txtを出力する

REM #hc 敷地の元になる輪郭線とソリッドを選択して下さい

REM #g0 書き込みグループレイヤ対象

REM #0 元図の拡大縮小基準点を指定して下さい

REM #1 拡大縮小後の作図位置を指定して下さい
REM #e


start /w excel R敷地.xls
del jwc_temp.txt
ren r_temp.txt jwc_temp.txt
del r_temp.txt



さあ以上で外部変形がひとつ出来上がりました。




あとなんかしたいですね。

線分の両端に点でも打ちますか。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

s = 0
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop


menseki = InputBox("現在の面積は "
& Round(solid_menseki, 2) & "u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み


If Left(tmp, 3) = "hp1" Then '基点情報検索

a = Split(tmp) '半角スペースで切り分け

x = a(3) '3番目 = x

y = a(4) '4番目 = y
End If

Loop

Set fs = CreateObject("Scripting.FileSystemObject")
Set r_tmp = fs.CreateTextFile(pth & "\r_temp.txt", True)
Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み
header = Left(tmp, 1)


If header = " " Then '線データの処理
a = Split(tmp)

x1 = Val(a(1)) * bairitsu + x '拡大縮小処理
y1 = Val(a(2)) * bairitsu + y
x2 = Val(a(3)) * bairitsu + x
y2 = Val(a(4)) * bairitsu + y
tmp = " " & x1 & " " & y1 & " " & x2 & " " & y2

r_tmp.writeline (tmp) '線分をファイルに出力
tmp = "pt " & x1 & " " & y1

r_tmp.writeline (tmp) '点をファイルに出力
tmp = "pt " & x2 & " " & y2

r_tmp.writeline (tmp) '点をファイルに出力
End If

Loop
r_tmp.Close

ThisWorkbook.Saved = True
Application.Quit

End Sub

とりあえず点が重なりますが気にしないことにします。



あとせっかくなので元の面積と処理後の面積を記録しましょう。


スクリプト本体
Sub sikiti()

pth = ThisWorkbook.Path
Dim tf
Set tf = CreateObject("Scripting.FileSystemObject")

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)
solid_menseki = 0

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み

s = 0
Call solid_s_get(tmp, s)
solid_menseki = solid_menseki + s

Loop


menseki = InputBox("現在の面積は "
& Round(solid_menseki, 2) & "u
です.変換する面積を入力して下さい")
If IsNumeric(menseki) = False Then End
bairitsu = Sqr( menseki / solid_menseki )

Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み


If Left(tmp, 3) = "hp1" Then '基点情報検索

a = Split(tmp) '半角スペースで切り分け

x = a(3) '3番目 = x

y = a(4) '4番目 = y
End If

Loop

Set fs = CreateObject("Scripting.FileSystemObject")
Set r_tmp = fs.CreateTextFile(pth & "\r_temp.txt", True)
Set tf_txt = tf.OpenTextFile(pth & "\jwc_temp.txt", 1, True)

Do Until tf_txt.AtEndOfStream


tmp = tf_txt.readline '1行読み込み
header = Left(tmp, 1)


If header = " " Then '線データの処理
a = Split(tmp)

x1 = Val(a(1)) * bairitsu + x '拡大縮小処理
y1 = Val(a(2)) * bairitsu + y
x2 = Val(a(3)) * bairitsu + x
y2 = Val(a(4)) * bairitsu + y
tmp = " " & x1 & " " & y1 & " " & x2 & " " & y2

r_tmp.writeline (tmp) '線分をファイルに出力
tmp = "pt " & x1 & " " & y1

r_tmp.writeline (tmp) '点をファイルに出力
tmp = "pt " & x2 & " " & y2

r_tmp.writeline (tmp) '点をファイルに出力
End If

Loop

tmp = "ch " & x &
"
" & y & " 100 0 "
& Chr(34) & Round(solid_menseki,
2) & "u → " & menseki
& "u"
r_tmp.writeline (tmp)


r_tmp.Close

ThisWorkbook.Saved = True
Application.Quit

End Sub



以上でひとまず完成です。


本来は後々トレースしやすいようにTABやコメントで可読性を向上させておくのですが、htmlではTABがうまく使えなかったのであえてはずしてあります。

全体もそう長くないのでコメントも省きました。


サンプルのJWWデータと、ここまでで作成したR敷地.BATとR敷地.xlsは念のためここに置いておきます。


Hino 18:04 2006.11.14 Tuesday
 署名 
 内容 
      
新しい記事
 題名 
 署名   File
 本文