![見出し画像](https://assets.st-note.com/production/uploads/images/32236045/rectangle_large_type_2_7d812ad9a8e0ccd1eb7dbcbd6584c6e7.jpg?width=1200)
LibreOffice Calc Basic で写真一覧作成 rev.2
改訂履歴
2020年9月30日改訂 rev.2
プログラムを修正。main を実行後、セルに文字を入力しても表示されない問題がありました。
画面の更新関係で問題がありそうだったので、関連のコードを消しました。
' 画面の更新を止める(2020/09/22)
' ThisComponent.LockControllers → 削除
なお、ロック解除のつもりでロックするコードを書いていました。しかし、これをアンロックに書き換えても問題は解消しませんでした。
2020年9月22日改訂 rev.1
プログラムを修正。バグフィックスおよび機能にはあまり関係のない改良。
この記事も、それに伴って修正、さらに誤記も訂正。
プログラムの主な変更点は次のとおり
画像リスト作成(main)
シートの初期化において
・B列からF 列まで消去、消し残しは G列だけにした。
画像取り込みにおいて
・画面の更新の停止、処理中にリサイズ前の写真を表示しないようにした
・If 文で jpg ファイルだけ取り出すのではなく、Dir 関数の設定で最初から *.jpg ファイルだけを拾うように改変
・配列を使用してファイル一覧を作り、それをワークシートに転記するように改良
写真の情報処理関連
・最後の写真データおよび最後の地番データまでを処理するように修正
画像タイトル作成(MakeTitle3)
・機能変更にともない、名称を MakeTitle2 から MakeTitle3 に変更
・最初にG列を消去
・配列を使用、処理対象のデータを配列に取り込んで処理
・地番データは、あるときだけ付加
・全データをテキストファイル "Caption.txt" に書き出す。メッセージボックスには表示しない
・記事作成の利便性向上のため、画像のキャプションの間に、改行と"◇"を挟んだ形にした
概要
LibreOffice Calc Basic で写真一覧を作成することができました。
こんな感じです。
画面イメージ
説明
Sub プロシージャおよび Function 一覧
Sub Main
Sub Clear_Contents
Sub Import_Image
Sub Get_Address3
Sub Address_Code
Sub MakeTitle3
Function asin
Function min
Sub Main
次の4つを順番に実行します。
・Sub Clear_Contents
・Sub Import_Image
・Sub Get_Address2
・Sub Address_Code
Sub Clear_Contents
シートを初期化します。
1行目タイトル行を除くB列からF列のデータ、およびすべての画像を消去します。
Sub Import_Image
A列に写真をリストアップします。
B列にファイル名を書き込みます。
Sub Get_Address2
別途用意したファイルから位置情報を読み込み、写真に紐付けつつC列に書き込みます。
Sub Address_Code
Address_List というシートに記入した緯度経度の中から、最も近いものを選びます。
このブックに Address_List シートがない場合には、何もしません。
Sub MakeTitle2
キャプション用の文字列を作ります。
F列とD列から、「F列(D列)」の形の文字列を作り、G列に書きます。
最後に、すべての文字列をテキストファイル "Caption.txt" に書き出します。
Function asin
Function min
プログラムの中で、アークサイン関数および min 関数が使えなかったので、ワークシート関数を呼び出すことにしました。毎回、ワークシート関数を呼び出す手続きをとるのも面倒だと思い、Function として作りました。
データの準備
関連ファイルを同じフォルダーに置いてください。
あるフォルダー
┣ マクロファイル
┣ Picture_Address.csv
┣ jpeg ファイル1
┣ jpeg ファイル2
┣ .......
┣ .......
┣ .......
┗ jpeg ファイルX
Picture_Address.csv 写真の緯度経度情報を記録した csv ファイル
書式:"jpg ファイル名","35.44157833333333, 139.29411166666665"
コンマ区切り
このファイルがない場合、写真とファイル名だけの一覧ができます。
Picture_Address.csv 作成ツール
写真に埋め込まれた位置情報を取り出すためのツールを、Python Script を用いて作りました。
このスクリプトを使えば、写真の位置情報を csv ファイルにすることができます。
マクロファイルの作成
新規スプレッドシートファイルを作成し、モジュールに上のコードを貼り付けてください。
Tools > Macros > Organize Macros > Basic
Basic Macros ダイアログで、作成したシートを選択。
Untitled 1をクリックすると、Standard とか出るので、選択。
右の New をクリック。
New Module ダイアログに、Name: Module1 と出ているので、OKをクリック。
マクロエディターの左側に、Module1 > Main ができている。
全部を上書きする形で、コードを貼り付ける。
「Pictures」という名前のシートを作ってください。
Sheet1 の名称を変更するなり、新規シートを作るなりして、Pictures というシートを作ります。
A列のサイズを適当に変えます。写真は、A列のセルに収まるようにリサイズされます。
写真はリンク貼り付けです。このファイルと写真の位置関係が変わると表示されなくなります。
ちなみに、自分の場合、次の様に1行目に見出しを入れています。
A1:写真(リンク)
B1:ファイル名
C1:緯度経度
D1:地番
E1:距離
F1:タイトル
G1:キャプション
[参考]
セルの高さを一度に全部変えるときは、A列の見出し"A"をクリックしてから、1行目と2行目の境目を掴んでドラッグ。ここまで、言わなくてもいいか。
必要におうじて「Address_List」という名前のシートを用意します。
プログラム中、Address_List というシートを処理しますが、なかったらだまって無視します。
ちなみに、Address_List シートの仕様は次のとおりです。
A列:地名など。自分の場合は、地番杭に表示された記号番号です。
B列:緯度経度。(例 35.44135, 139.29426)
Address_Code という処理が、このデータの中から写真の撮影地点に最も近い地名や地番を引き出します。
マクロの実行
メニューバー > Tools > Run Macro
Macro Selector ダイアログで、このシートのModuleに含まれる、Main を選択し、Run。
「終わりました。」が表示されたら完了です。
コード
REM ***** BASIC *****
Option Explicit
REM ***********************************************************
REM シートの初期化
REM 写真取り込み
REM 位置情報紐付け
REM 番地取得
REM ***********************************************************
REM
REM 実行前に Pictures というシートを作っておく。
REM すでにある場合は、A列からG列までのコンテンツ
REM およびすべての画像を消去した上で、作り直す。
Sub Main
Call Clear_Contents
Call Import_Image
Call Get_Address2
Call Address_Code
MsgBox "終わりました。"
End Sub
REM ***********************************************************
REM シートの初期化
REM ***********************************************************
Sub Clear_Contents
Dim MySheet As Object
Dim MyCursor As Object
Dim MyRange As Object
Dim LastRow As Long
Dim drawpage As Object
Dim shape As Object
Dim i As Long
REM 事前に Pictures というシートを作ること
REM Pictures というシートがなかったら、終了。
If ThisComponent.Sheets.hasByName("Pictures") = False Then
MsgBox "このプログラムは、Pictures という名称のシートを処理します。"
End
End If
MySheet = ThisComponent.Sheets.getByName("Pictures")
'データの最終行を調べる(B列はファイル名)
MyRange = MySheet.getCellRangeByName("B1")
MyCursor = MySheet.createCursorByRange(MyRange)
MyCursor.gotoEndOfUsedArea(True)
LastRow = MyCursor.Rows.Count
' セル B2 以下にデータが存在する場合は、コンテンツを消去(B列からF列まで)
' G列は残す
If LastRow > 1 Then
MyRange = MySheet.getCellRangeByName("B2:F" & LastRow)
MyRange.ClearContents(1+2+4+8+16)
End If
'画像(shape)の消去
drawpage = MySheet.getDrawPage()
'番号の大きい方から削除, i はインデックス番号
For i = drawpage.getcount-1 To 0 Step -1
shape = drawpage.getByIndex(i)
drawpage.remove(shape)
Next i
End Sub
REM ***********************************************************
REM 写真のリストアップ
REM ***********************************************************
Sub Import_Image
REM *.jpg ファイルのリストアップ
' 縦横比固定で、セルに収める。
Dim MyUrl As String
Dim PictPath As String
Dim MyFile As String
Dim MyFolder As String
Dim MyFileCollection As Variant
Dim FileCount As Long
Dim MySheet As Object
Dim MyRow As Long
Dim LastRow As Long
Dim MyRange As Object
Dim MyRange_A As Object
Dim MyRange_B As Object
Dim MyCursor As Object
Dim MyPath As String
Dim PictName As String
Dim drawpage As Object
Dim shape As Object
Dim i As Long
Dim aSize as new com.sun.star.awt.Size
Dim aPos as new com.sun.star.awt.Point
Dim sH As Long
Dim sW As Long
Dim pX As Long
Dim pY As Long
Dim Margin_for_Image As Double
Dim Ratio_H As Double
Dim Ratio_W As Double
Dim Ratio As Double
Dim frame as Object
Dim args(1) as new com.sun.star.beans.PropertyValue
Dim args1() As new com.sun.star.beans.PropertyValue
Dim pictUrl As String
Dim args2(1) As Variant
Dim controller AS Object
Dim dispatcher As Object
' セルに対して 2.5% ずつ余白を作って画像を貼り付ける
Margin_for_Image = 0.025
REM Pictures というシートがなかったら、終了。
If ThisComponent.Sheets.hasByName("Pictures") = False Then
MsgBox "このプログラムは、Pictures という名称のシートを処理します。"
End
End If
MySheet = ThisComponent.Sheets.getByName("Pictures")
MyRow = 2
'---- ファイルを検索 -----
'このファイルのURL
MyUrl = ThisComponent.getUrl
' URL表現から普通のファイルパスに変換
MyPath = ConvertFromURL(MyUrl)
'このファイルのファイル名
MyFile = Dir(MyUrl, 16)
'このファイルが所属するフォルダー
'ファイルのフルパスの左何文字かを取り出す。
'文字数は、フルパスの文字数からファイル名の文字数を引いたもの
'結果、"/"まで含む。
MyFolder = Left(MyPath, Len(MyPath) - Len(MyFile))
' ーーーーーーーー 差し替え(2020/09/22) ーーーーーーーー
' 配列を使ってファイルリストを作る
FileCount = 0
'このシートと同じフォルダーにあるファイルを調べ、jpg だったらシートに記入する。
'Dir 関数を使って最初のファイルをゲット
MyFile = Dir(MyFolder + "*.jpg")
' <> "._" という条件は、NAS の隠しファイルまで読み込むのを阻止するため
Do While MyFile <> ""
If Left(MyFile, 2) <> "._" then
Redim Preserve MyFileCollection(FileCount)
MyFileCollection(Ubound(MyFileCollection)) = Array(MyFile)
FileCount = FileCount + 1
End If
MyFile = Dir()
Loop
'B2 から貼り付け
'最後の行を計算
LastRow = 2 + Ubound(MyFileCollection)
'貼り付け先の範囲
MyRange = MySheet.getCellRangeByName("B2:B" & LastRow)
'配列をシートにコピー
MyRange.setDataArray(MyFileCollection)
' ーーーーーーーー 差し替えここまで ーーーーーーーー
' ---- 画像貼り付け ----
'
' http://hermione.s41.xrea.com/pukiwiki/index.php?OOoBasic%2FCalc%2Fimage
'
drawpage = MySheet.getDrawPage()
' B列のファイル名から画像を読み込んで、セルに貼り付ける。
controller = ThisComponent.getCurrentController()
frame = controller.getFrame()
dispatcher = CreateUnoService("com.sun.star.frame.DispatchHelper")
For MyRow = 2 To LastRow
REM 列Aのセルの大きさを調べる
MyRange_A = MySheet.getCellRangeByName("A" & MyRow)
'小数点以下、四捨五入
With MyRange_A
sW = CLng(.Size.Width * (1 - Margin_for_Image * 2))
sH = CLng(.Size.Height * (1 - Margin_for_Image * 2))
pX = CLng(.Position.X + sW * Margin_for_Image)
pY = CLng(.Position.Y + sH * Margin_for_Image)
End With
REM B列からファイル名をゲット
MyRange_B = MySheet.getCellRangeByName("B" & MyRow)
PictName = MyRange_B.String
PictPath = MyFolder + PictName
' システムファイル名をファイルURLへ変換
pictUrl = ConvertToURL(PictPath)
' プロパティーの設定
' 画像のファイル名およびリンク貼り付け
args(0).Name = "FileName"
args(0).Value = pictUrl
args(1).Name = "AsLink"
args(1).Value = True
' 画像の挿入
REM セルを選択し、フレームとして設定
Controller.Select(MyRange_A)
frame = Controller.getFrame()
REM 画像の挿入
dispatcher.executeDispatch(frame, ".uno:InsertGraphic", "", 0, args)
' 最後に貼り付けた画像(と思われるもの)を、shape に設定、選択したのちフレームとして設定
shape = drawpage.getByIndex(drawpage.getcount-1)
controller.Select(shape)
frame = controller.getFrame()
REM ----------------------------------------------------------------------
REM マクロ自動記録から、Document となっていたところを、frame に変更。
REM 画像のアンカーを設定する。
REM args1() は、空の配列。
dispatcher.executeDispatch(frame, ".uno:SetAnchorToCell", "", 0, args1())
' 画像のサイズを設定
' 幅に合わせたときと高さに合わせたときの縮尺を計算する。
Ratio_W = sW / shape.Graphic.SizePixel.Width
Ratio_H = sH / shape.Graphic.SizePixel.Height
' 関数 min に渡す引数を用意する
Args2(0) = Ratio_W
Args2(1) = Ratio_H
' 小さい方を求めるために、Function min を作った。
' とはいえワークシート関数を使うだけのルーチンだ。
Ratio = min(args2())
' 幅と高さを決定
aSize.Width = shape.Graphic.SizePixel.Width * Ratio
aSize.Height = shape.Graphic.SizePixel.Height * Ratio
' 貼り付けた画像に適用
shape.Size = aSize
' 画像の位置を設定
aPos.X = pX
aPos.Y = pY
shape.Position = aPos
REM 画像の Description > Title: にファイル名を書き込む。
shape.title = PictName
Next MyRow
End sub
REM ***********************************************************
REM csv ファイルから写真の位置情報を取り込む
REM ***********************************************************
Sub Get_Address2
' 写真リストと csvファイルのデータを結びつける。
' ファイル名:Picture_Address.csv
' 文字コード:utf-8
' データ形式:"写真のファイル名","緯度経度"
Dim MyController As Object
Dim MyCursor As Object
Dim MyFolder As String
' マクロシート関連
Dim MyURL As String
Dim MyPath As String
Dim MyFile As String
Dim MySheets As Object
Dim MySheet As Object
Dim MyRange AS Object
Dim MyRow As Long
Dim MyLastRow As Long
' csvファイル関連
Dim CSVPath As String
Dim CSVUrl As String
Dim CSVBook As Object
Dim CSVSheets As Object
Dim CSVSheet As Object
Dim CSVRange As Object
Dim CSVRow As Long
Dim CSVLastRow As Long
Dim CSVData As Object
Dim FileProperties(1) As New com.sun.star.beans.PropertyValue
REM Pictures というシートがなかったら、終了。
If ThisComponent.Sheets.hasByName("Pictures") = False Then
MsgBox "このプログラムは、Pictures という名称のシートを処理します。"
End
End If
' csv ファイル読み込み用プロパティー設定
FileProperties(0).Name = "FilterName"
FileProperties(0).Value = "Text - txt - csv (StarCalc)"
FileProperties(1).Name = "FilterOptions"
FileProperties(1).Value = "44,34,76,1,2/1/1/1/1"
'
'FilterOptions プロパティについて
'
'セルの区切り文字を ASCII コードで指定。コンマ: 44
'テキストを囲む文字を ASCII コードで指定。ダブルクォート: 34
'テキストの文字コード Unicode: 65535, UTF-8: 76, UTF-7: 75, EUC-JP: 69, Shift_JIS: 64, ISO-2022-JP: 72
'エクスポートし始める行
'セルのタイプ指定 1: 数値, 2: テキスト, 3: MM/DD/YY, 4: DD/MM/YY, 5: YY/MM/DD, 6, 7, 8: テキスト, 9: セルを無視, 10: US タイプの数値フォーマット です。各フィールドに複数の値を指定する際には,数値を "/" で区切ります。
'利用可能な文字コード数値はソースコードの textenc.h で確認できます。とのこと。
' ---- このフォルダー -----
'このファイルのURL
MyUrl = ThisComponent.getUrl
' URL表現から普通のファイルパスに変換
MyPath = ConvertFromURL(MyUrl)
'このファイルのファイル名
MyFile = Dir(MyUrl, 16)
'このファイルが所属するフォルダー
'ファイルのフルパスの左何文字かを取り出す。
'文字数は、フルパスの文字数からファイル名の文字数を引いたもの
'結果、"/"まで含む。
MyFolder = Left(MyPath, Len(MyPath) - Len(MyFile))
' ---- csv ファイルがあるかを調べる ----
CSVPath = MyFolder + "Picture_Address.csv"
If Dir(CSVPath) = "" Then
Print "GPS データ(Picture_Address.csv)がありません。"
Exit Sub
End If
' ---- csv ファイルを開く ----
CSVURL = ConvertToURL(CSVPath)
CSVBook = StarDesktop.loadComponentFromURL(CSVURL, "_default", 0, FileProperties)
MyController = CSVBook.getCurrentController()
CSVSheets = CSVBook.getSheets()
CSVSheet = CSVSheets.getByName("Picture_Address")
' ---- csvファイルの最終行 ----
CSVRange = CSVSheet.getCellRangeByName("A1")
MyCursor = CSVSheet.createCursorByRange(CSVRange)
MyCursor.gotoEndOfUsedArea(True)
CSVLastRow = MyCursor.Rows.Count
' ---- このシートの最終行 -----
' MySheet = ThisComponent.CurrentController.Activesheet
MySheet = ThisComponent.Sheets.getByName("Pictures")
MyRange = MySheet.getCellRangeByName("B1")
MyCursor = MySheet.createCursorByRange(MyRange)
MyCursor.gotoEndOfUsedArea(True)
MyLastRow = MyCursor.Rows.Count
' 写真一覧ファイルの2行目から最後の行まで処理する
For MyRow = 2 To MyLastRow
' B列のデータ=ファイル名
MyRange = MySheet.getCellRangeByName("B" & MyRow)
' GPS データファイルの1行目から最後の行まで処理する
For CSVRow = 1 To CSVLastRow
'A列のデータ=ファイル名
CSVRange = CSVSheet.getCellRangeByName("A" & CSVRow)
' ファイル名の一致を確認して、位置情報取り込み
If CSVRange.String = MyRange.String Then
' このシートに、GPS ファイルから次のデータを書き込む
' C列に B列のデータ(緯度,経度)
CSVRange = CSVSheet.getCellRangeByName("B" & CSVRow)
MyRange = MySheet.getCellRangeByName("C" & MyRow)
MyRange.String = CSVRange.String
Exit For
End If
Next CSVRow
Next MyRow
CSVBook.close(True)
End Sub
REM ***********************************************************
REM Address_List シートから、もっとも近い Location Name をみつける
REM ***********************************************************
REM Address_List シートがない場合には、だまって無視。
REM 結果、簡単な写真リストが出来上がる。
REM Address_List シートの仕様
' A列:場所、自分の場合は、地番杭に表示された記号番号
' B列:緯度経度(例 35.44135, 139.29426)
Sub Address_Code
' Address_List シート
' A列 地名などの名称
' B列 緯度経度 書式の例 35.44135, 139.29426
Dim MySheet As Object
Dim MyRow As Long
Dim MyCursor As Object
Dim LastRow As Long
Dim MyRange As Object
Dim MyData As Variant
Dim AddressSheet As Object
Dim MyRange_A As Object
Dim AddressList As Variant
Dim i As Long
Dim j As Long
Dim MyString As String
Dim Distance As Double
Dim lat0 As Double
Dim lat1 As Double
Dim lon0 As Double
Dim lon1 As Double
Dim Phi0 As Double
Dim Phi1 As Double
Dim Lambda0 As Double
Dim Lambda1 As Double
Dim radius As Long
Dim term1 As Double
Dim term2 As Double
REM Pi = 3.1415926 は事前定義されている定数で、円周率の近似値
radius = 6378137 '地球の Radius 単位は、メートル
REM Pictures というシートがなかったら、終了。
If ThisComponent.Sheets.hasByName("Pictures") = False Then
MsgBox "このプログラムは、Pictures という名称のシートを処理します。"
Exit Sub
End If
MySheet = ThisComponent.Sheets.getByName("Pictures")
'データの最終行を調べる
MyRange = MySheet.getCellRangeByName("B1")
MyCursor = MySheet.createCursorByRange(MyRange)
MyCursor.gotoEndOfUsedArea(True)
LastRow = MyCursor.Rows.Count
MyRange = MySheet.getCellRangeByName("C2:E" & LastRow)
MyData = MyRange.getDataArray
' Address_List シートの A列と B列を配列に格納する。
If ThisComponent.Sheets.hasByName("Address_List") = False Then
REM Address_List というシートがなかったら、処理を終了。
Exit Sub
End If
AddressSheet = ThisComponent.Sheets.getByName("Address_List")
MyRange_A = AddressSheet.getCellRangeByName("A2:B132")
AddressList = MyRange_A.getDataArray
' Ubound(MyData) - 1 としていたために最後のデータを処理していなかった。
' 1 を引くのをやめる。2020/09/22
For i = 0 To UBound(MyData)
'緯度経度 MyData(i)(0)
'地番 Mydata(i)(1)
'距離 MyData(i)(2)
Mydata(i)(1) = ""
'未処理であることを示すために、距離に -1 を入れておく。
Mydata(i)(2) = -1
If Instr(MyData(i)(0), ",") > 0 Then
'緯度経度のデータはコンマで区切られているので、split で分割する。
lat0 = Split(MyData(i)(0), ",")(0)
lon0 = Split(MyData(i)(0), ",")(1)
'ラジアンに変換
Phi0 = lat0 * Pi/180
Lambda0 = lon0 * Pi/180
' Ubound(AddressList) - 1 としていたため、最後の地番を無視していた。
' 1 を引くのをやめる。2020/09/22
For j = 0 To Ubound(AddressList)
'地番 AddressList(j)(0)
'緯度経度 AddressList(j)(1)
'距離を計算する
If Instr(AddressList(j)(1), ",") > 0 Then
'緯度経度のデータはコンマで区切られているので、split で分割する。
lat1 = Split(AddressList(j)(1), ",")(0)
lon1 = Split(AddressList(j)(1), ",")(1)
'ラジアンに変換
Phi1 = lat1 * Pi/180
Lambda1 = lon1 * Pi/180
'一気に公式を書かずに、項ごとに計算する。
term1 = sin((phi1 - Phi0) / 2.0) ^ 2
term2 = sin((Lambda1 - Lambda0) / 2.0) ^ 2
term2 = cos(Phi0) * cos(Phi1) * term2
Distance = 2.0 * radius * asin(sqr(term1 + term2))
'asin は アークサイン、実行時関数として存在しないのか。
'Function として作った。
If Mydata(i)(2) = -1 Then
'最初の距離データを格納
Mydata(i)(2) = Distance
MyData(i)(1) = AddressList(j)(0)
Else
'距離の数値が小さい方を残す
If Mydata(i)(2) > Distance Then
Mydata(i)(2) = Distance
MyData(i)(1) = AddressList(j)(0)
End If
End If
End If
Next j
End If
Next i
'地番と距離を書き込んだ配列を、元のレンジに書き戻す。
MyRange.setDataArray(MyData)
End Sub
REM ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
REM 2.手入力したタイトルと地番から、画像のキャプションを生成する
REM ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
Sub MakeTitle3
' F列 + ( + D列 + ) を G列に書く
' 全データをテキストファイル "Caption.txt" に書き出す。(2020/09/22)
Dim MySheet As Object
Dim MyRow As Long
Dim MyRange As Object
Dim MyCursor As Object
Dim LastRow As Long
Dim I As Long
Dim MyUrl As String
Dim MyPath As String
Dim MyFile As String
Dim MyFolder As String
Dim TextFilePath As String
Dim FileNumber As Long
Dim Data_D As Variant
Dim Data_F As Variant
Dim Data_G As Variant
Dim MyData As String
Dim MyString As String
REM Pictures というシートがなかったら、終了。
If ThisComponent.Sheets.hasByName("Pictures") = False Then
MsgBox "このプログラムは、Pictures という名称のシートを処理します。"
End
End If
MySheet = ThisComponent.Sheets.getByName("Pictures")
' G列の最終行を調べる
MyRange = MySheet.getCellRangeByName("G1")
MyCursor = MySheet.createCursorByRange(MyRange)
MyCursor.gotoEndOfUsedArea(True)
LastRow = MyCursor.Rows.Count
' セル G2 以下にデータが存在する場合は、コンテンツを消去(G列だけ)
If LastRow > 1 Then
MyRange = MySheet.getCellRangeByName("G2:G" & LastRow)
MyRange.ClearContents(1+2+4+8+16)
End If
' B列の最終行を調べる
MyRange = MySheet.getCellRangeByName("B1")
MyCursor = MySheet.createCursorByRange(MyRange)
MyCursor.gotoEndOfUsedArea(True)
LastRow = MyCursor.Rows.Count
' 1個ずつセルを読み書きするのはやめて、配列を使って処理する (2020/09/22)
' D列:地番のデータを配列に取り込む
MyRange = MySheet.getCellRangeByName("D2:D" & LastRow)
Data_D = MyRange.getDataArray
' F列:タイトルを配列に取り込む
MyRange = MySheet.getCellRangeByName("F2:F" & LastRow)
Data_F = MyRange.getDataArray
' G列のデータを入れる配列を用意する
' 最大インデックスは、最終行から最初の行を引いた数
ReDim Data_G(LastRow - 2)
' 地番データがあるときだけ、括弧付きで地番データを追加
For I = 0 To Ubound(Data_G)
If Data_D(I)(0) <> "" Then
Data_G(I) = Array(Data_F(I)(0) & "(" & Data_D(I)(0) & ")")
Else
Data_G(I) = Array(Data_F(I)(0))
End If
MyString = MyString & Chr(13) & Data_G(I)(0) & Chr(13) & "◇"
Next I
' ワークシートに書き出す
MyRange = MySheet.getCellRangeByName("G2:G" & LastRow)
MyRange.setDataArray(Data_G)
' G列のデータ用の配列の要素をすべて結合する。
' 間に、改行と◇を挟む
MyString = Join(Data_G, Chr(13) & Chr(10) & "◇" & Chr(13) & Chr(10))
' 結果は、メッセージボックスに表示しない。
' テキストファイル "Caption.txt" に書き出す。2020/09/22
'このファイルのURL
MyUrl = ThisComponent.getUrl
' URL表現から普通のファイルパスに変換
MyPath = ConvertFromURL(MyUrl)
'このファイルのファイル名
MyFile = Dir(MyUrl, 16)
'このファイルが所属するフォルダー
'ファイルのフルパスの左何文字かを取り出す。
'文字数は、フルパスの文字数からファイル名の文字数を引いたもの
'結果、"/" まで含む。
MyFolder = Left(MyPath, Len(MyPath) - Len(MyFile))
TextFilePath = MyFolder & "Caption.txt"
' ファイルのオープン
FileNumber = FreeFile()
Open TextFilePath For Output As #FileNumber
' 出力
Print #FileNumber , MyString
' ファイルのクローズ
Close #FileNumber
MsgBox "終わりました。"
End Sub
REM ***********************************************************
REM アークサイン関数
REM ***********************************************************
REM 引数は、一つの数値
Function asin(Number As Double)
Dim ShtFunc As Object
Dim Args(0) As Variant
ShtFunc = CreateUnoService( "com.sun.star.sheet.FunctionAccess" )
Args(0) = Number
asin = ShtFunc.callFunction("ASIN", Args())
End Function
REM ***********************************************************
REM 最小値を求める関数
REM ***********************************************************
REM 引数は配列で渡す
Function min(Args As Variant)
Dim ShtFunc As Object
ShtFunc = CreateUnoService( "com.sun.star.sheet.FunctionAccess" )
min = ShtFunc.callFunction("MIN", Args())
End Function
Caption.txt のサンプル
キイトトンボ(E30)
◇
キイトトンボ(E30)
◇
オオシオカラトンボ(K16)
◇
クロイトトンボ(K16)
◇
キイトトンボ(K16)
◇
コゲラ
◇
写真につけるキャプションを生成する場合は、F列に文字を記入した後で、MakeTitle3 を実行します。
参考にしたサイト
主に次のサイトを参考にしました。
画像の貼り付け
指定したシートがあるかどうか
データが入力されている最終行・最終列を求める
セルの内容をクリアする
画像の消去
CSV ファイルの取り扱い
Haversine formula を使って2点間の距離を求める
ワークシート関数を LO Basic で使う方法
余談
これを使ったワークフロー的な話は、また別の機会にしたいと思います。
Python でやれば楽なのに、あえて LO Calc に取り込んだ話
緯度経度から距離を求めるときに Haversine formula という公式があると知りました。 最初、流れで Python のモジュールを使ったら楽にできるとか考えましたので、距離の計算も Python スクリプトでやっていました。しかし、基準となる番地と緯度経度は、元は LO Calc で作りますし、モジュールを使わず式で計算するようになって、LO Basic でやったら良いだろうとなりました。
しかし、LO Basic のネックは、Python ほど楽に計算できないところでしょうか。Python の場合、配列を一気に処理するみたいなことが簡単にできてしまいます。LO Basic の場合は、地味に処理していかなければならないことが多い気がします。
ま、しかしやればなんとかなると、緯度経度を取り出すところだけ Python に任せることにして、距離の計算を LO Basic に移植しました。あと、あれこれ改善してやっと完成しました。
LO Calc のマクロ取り込みが使えない話
マクロを記録しても、完全な形で記録されない場合がありますね。本当に困ります。そんなときは諦めた方が良いのでしょうね。
もっとも、そのような場合、マクロやプログラムでどうしようもないことだったりするようです。すくなくとも、自分の知識や情報収集能力ではどうにもならないことがよくあります。
今回、貼り付けた写真を圧縮したいと思っていろいろ試行錯誤しました。が、結局、圧縮するのはあきらめて、リンク貼り付けという代替手段で落ち着きました。リンクなら取り込んだものを圧縮する以上にファイルサイズを節約できますので、これでよかったかなと思います。
アプリケーション上は、画像を右クリックして、Compress... から圧縮できます。
その操作をマクロを記録すると、↓こうです。
sub compress
rem ----------------------------------------------------------------------
rem define variables
dim document as object
dim dispatcher as object
rem ----------------------------------------------------------------------
rem get access to the document
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
rem ----------------------------------------------------------------------
rem dispatcher.executeDispatch(document, ".uno:CompressGraphic", "", 0, Array())
end sub
肝心なところが rem として書かれています。これを実行しても何も起きません。パラメーターを設定すれば動作するのではないかと思ったのですが、そんな簡単な話ではないみたいで、いくら調べても解決策が見つかりませんでした。
しかし、なんでこんな制約があるのか理解できません。できるようにしてくれたらいいじゃないかと思います。
しかし、自分の理解が足りないのかもしれません。DispatchHelper を理解すればできるようになるかと思って、勉強しました。
理解できませんでした。使える情報がほしいです。
executeDispatch()
any executeDispatch ( [in] XDispatchProvider DispatchProvider,
[in] string URL,
[in] string TargetFrameName,
[in] long SearchFlags,
[in] sequence< com::sun::star::beans::PropertyValue > Arguments
)
Parameters
DispatchProvider points to the provider, which should be asked for valid dispatch objects
URL describes the feature which should be supported by internally used dispatch object
TargetFrameName specifies the frame which should be the target for this request
SearchFlags optional search parameter for finding the frame if no special TargetFrameName was used
Arguments optional arguments for this request They depend on the real implementation of the dispatch object.
結局、".uno:CompressGraphic" は、有効なパラメーターを持っていないのでしょうか。
これの Arguments が、公式ドキュメントに明示されていたら楽だと思うのですが、無理なことなのでしょうか。
t.koba
この記事が気に入ったらサポートをしてみませんか?