出直し!! ヘルプ

連載中

連載 終了

2024/06/30

DXL のヘルプ はじめました!

今年は、DXL の理解を進めることをメインの目標にしてノーツに取り組んでいます。このブログでも、昨年掲載した『DXL ことはじめ』にはじまり、『DXL Step-by-Step』の連載は現在も継続中です。

DXL はドキュメントなどの情報が少ないため、連載の材料収集はトライ&エラーで収集しています。わかったことは都度記事にまとめていますが、一覧性がなく、必要なものを探し出しにくい状態になっています。

そこで、DXL のノードや属性をまとめたサイトを作ることにしました。


題して『出直し!! ヘルプ DXL ノード編』です!


出直し!! ヘルプ DXL ノード編

今年1月に公開した @関数編 の DXL 版というところですね。右の『出直し!! ヘルプ』メニューからアクセスしてください。

左の一覧はノード名順のインデックス、右側は機能別にノードをまとめて表示しています。


このヘルプでは、ノードを主軸に、

  • 上位のノード(Contained by)
  • 下位のノード(Contains)
  • 属性(Attributes)
  • 当ブログ内の関連記事

をまとめています。


乞うご期待!

もう少しまとまってからの公開を考えていたのですが、まとめる情報量がお余りに多くいつまでたってもゴールが見えないこと、すでに多くの情報が集まったことから公開することとしました。

書きかけの項目や不足しているノードが多数あり、まだまだ未完成な状態です。今後の調査で分かったことも日々追記して、少しずつ充実させていきます。発展途上であることをあらかじめご了承ください。


DXL を使えば、LotusScript の限界を超えることができます。興味のある方は、冒頭に紹介した連載とこの『出直し!! ヘルプ DXL ノード編』でチャレンジください。


すでに DXL で開発している超レアリティが高い方にも参考になるようまとめていく予定です。参考になれば幸いです!


2024/06/29

DXL Step-by-Step:#36)罫線の設定 - 境界線のスタイル

罫線の設定は色や線の種類のように表全体に適用される設定と罫線の幅のようにセル単位で指定できる設定があります。今回は前者の境界線スタイルの設定についてまとめます。


境界線のスタイル

下図のようにスタイルを「エンボス1」に設定し、色を緑に設定した表を作成します。

この表を DXL に変換すると 2 つの属性 cellborderstyle と cellbordercolor が設定されます。設定の詳細は次の通りです。

属性 設定値 補足
cellborderstyle (なし) 塗りつぶし
'ridge' エンボス1
'groove' エンボス2
cellbordercolor 設定については #22)文字の装飾 の文字色の設定と同様


サンプルプログラム

前回作成したプログラムに境界線のスタイルをセットする部分を追加します。

今回は属性を 2 つ追加するだけなので単純です。関数化するほどでもないので、メインルーチン内でセットしています。

Function xSetDXL(vdprs As NotesDOMParser)
         ・・・
   '列幅の設定
   Call xSetDXL_colwidth(denTbl, 1, DXL_CMToInch(3))
   Call xSetDXL_colwidth(denTbl, 2, "70%")
   Call xSetDXL_colwidth(denTbl, 3, "30%")
   Call xSetDXL_colwidth(denTbl, 4, "30%")

   '境界線のスタイル
   Call denTbl.SetAttribute("cellborderstyle", "ridge")
   Call denTbl.SetAttribute("cellbordercolor", "green")


   '表後の段落追加
   Call xSetDXL_par(ddn, denRT)
End Function

実行すると、境界線のスタイルと色の設定ができていることが確認できます。

このプログラムで作成される DXL は次の通りです。


まとめ

第 32 回から 5 回にわたり、表の全体にかかわる設定をまとめました。通常のリッチテキストクラスでは操作できない表のプロパティ設定がいくつもありましたね。

次回からは表のセルについてまとめます。セルの設定でも、ノーツクライアントから設定できる操作はほとんど実現できます。DXL 面白いですね。


前回 DXL Step-by-Step 次回


2024/06/28

DXL Step-by-Step:#35)表の列幅の設定

前回ご紹介した通り、表の列幅は tablecolumn ノードの width 属性で設定します。今回はこの列幅を設定するプログラムを作成します。

tablecolumn ノードは table ノードの子ノードとして存在し、表の列数だけ存在しました。

これまで作成したプログラム(#32 を参照)に組み込むとすれば、表を作成する部分で設定すると簡単に実現できます。

例えば以下のサンプルでは、1 列目を固定幅(3cm)に設定しています。

Function xSetDXL_table(_
      ・・・

   '表の作成
   Set den = vddn.CreateElementNode("table")
   Set denTbl = vdenRT.AppendChild(den)

   '列定義の作成
   For iCol = 1 To viCols
      Set den = vddn.CreateElementNode("tablecolumn")
      If iCol = 1 Then
         Call den.SetAttribute("width", DXL_CMToInch(3))
      End If

      Call denTbl.AppendChild(den)
   Next

   '行の作成
      ・・・
End Function

このプログラムで 1 列目の tablecolumn ノードは次のようになります。

<tablecolumn width='1.1811in' />


tablecolumn ノードを取得するには

ただ、この方法だと、表を新規で作成(=列を新規で作成)するタイミングでしか設定できません。そこで、今回は、別途作成した表や既存の表に対して列幅を設定する方法を考えます。

必要となる機能は、

  1. table ノードの子ノードから tablecolumn ノードを探す
  2. 特定の列(n 番目)の tablecolumn ノードを取得する

となります。


NotesDOMElementNode クラスには GetElementsByTagName メソッドが定義されています。このメソッドを使えばノード名称で配下のノードを検索できます。ただ、この命令は子ノードだけでなく、孫ノードなど配下のすべてのサブノードが対象となります。そこで子ノードだけを対象にした関数 GetChildByName_Nth を自作します(lsDXL ライブラリに作成)。

引数は 3 つで、検索するノード(親ノード)、ノードの名称、何個目か となっています。発見できたらそのノード、見つからない場合は Nothing を返します。

Public Function GetChildByName_Nth(_
               vdenParent As NotesDOMElementNode, _
               ByVal vsNodeName As String, _
               ByVal viIndex As Integer) As NotesDOMElementNode
   Dim i As Integer
   Dim dn As NotesDOMNode

   Set GetChildByName_Nth = Nothing

   i = 0
   Set dn = vdenParent.FirstChild
   Do Until dn.Isnull
      If dn.NodeType = DOMNODETYPE_ELEMENT_NODE Then
         If dn.NodeName = vsNodeName Then
            i = i + 1
            If i = viIndex Then
               Set GetChildByName_Nth = dn
               Exit Function
            End If
         End If
      End If
      Set dn = dn.NextSibling
   Loop
End Function

仕組みは単純で FirstChild と NextSibling プロパティを使用して子ノードを順に取得してチェックしているだけです。万一、子ノードが textnode など NotesDOMElementNode でない場合に備え、汎用的な NotesDOMNode のオブジェクト変数を使用しています。


既存の列幅の設定

GetChildByName_Nth 関数の準備ができたら、これを使用して列幅を設定する関数を作成します。引数は 3 つで table ノード、列番号、列幅の設定となっています(エージェント内に作成)。

Function xSetDXL_colwidth(vdenTbl As NotesDOMElementNode, _
                                        ByVal viCol As Integer, ByVal vsWidth As String)
   Dim denCol As NotesDOMElementNode

   Set denCol = GetChildByName_Nth(vdenTbl, "tablecolumn", viCol)
   If Not(denCol Is Nothing) Then
      Call denCol.SetAttribute("width", vsWidth)
   End If
End Function

関数内では指定した列番号の tablecolumn ノードを取得。取得できたら列幅 width 属性をセットします。


メインルーチンを修正して、列幅を設定します。今回は、1 列目を 3cm で固定、2 列目を 70%、3 列目を 30% としています。

Function xSetDXL(vdprs As NotesDOMParser)
            ・・・
   '表の追加
   Set denTbl = xSetDXL_table(ddn, denRT, 3, 3)

   '間隔の設定
   Call xSetDXL_table_spcing(denTbl, 0.2, 0.5)

   '列幅の設定
   Call xSetDXL_colwidth(denTbl, 1, DXL_CMToInch(3))
   Call xSetDXL_colwidth(denTbl, 2, "70%")
   Call xSetDXL_colwidth(denTbl, 3, "30%")


   '表後の段落追加
   Call xSetDXL_par(ddn, denRT)
End Function

なお、今回のプログラムでは列の設定効果がわかりやすいよう作成する表を 3 列に変更しています。ご注意ください。


実行すると列の幅が設定された表が作成されます。実行結果と生成される DXL は以下の通りです。



前回 DXL Step-by-Step 次回


2024/06/25

アプリケーションカタログ にフォルダ別ビューを追加

先日まで数回にわたりアプリケーションカタログを紹介しました。さまざまな情報が集約されており、アプリケーションの管理に活用できそうですね。

サーバ内のアプリケーション(nsf)は通常、利用部門やアプリの種類などに応じてフォルダ分けして整理している場合が多いと思います。このような場合フォルダからアプリを探すことも多いですよね。カタログアプリでは、[アプリケーション]-[サーバー]のビューがそれに近いといえます。

ただ、このビュー、タイトル列でソートされて、アプリの配置からは探せません。そこで、今回は、アプリケーションカタログにフォルダ別のビューを追加してみましょう。


ビューの準備

上記ビュー『アプリケーション¥サーバー | ByServer』を複製してベースとします。デザイナーからビューをコピペすると次のような確認メッセージが表示されます。これは、コピーしたビューがテンプレートから引き継がれるべきか確認しています。独自のビューでテンプレートにはないので、[いいえ]を選択します。

複製されたビューは次の通り、「更新時に再設計/設計の置換を禁止する」にチェックが入ります。これで、設計置換やデザインタスクなどで、ビューが更新されることを抑制できます。

具体的な作業に入る前に、ビューの名前を更新しておきましょう。


フォルダでカテゴライズ

今回はフォルダでカテゴライズすることから、ファイルパスをファイル名とパスに分割します。

まずは、ファイル名です。ファイル列に次の式をセットします(バックスラッシュに変換されないよう¥は全角文字を使用しています)。

xLst := @Explode(Pathname; "¥¥");
@Subset(xLst; -1)

この式は @Explode を使って ¥ マークで分離したリスト値を作成します。そのリストから @Subset で一番後ろの要素を取得しています。これでフォルダ階層に関係なくファイル名だけを取得しています。


次にフォルダ名です。ファイル列の左に新しい列を作成し、次の式をセットします。

xLst := @Explode(Pathname; "¥¥");
xIdx := @Elements(xLst);
@If(xIdx = 1;
   "";
   @Implode(@Subset(xLst; @xIdx-1); "¥¥")
)

ファイル列と同様のテクニックを使用していますが、ファイル名を除いた部分を取得することから、@Elements を使用して要素数を取得、1 引くことでファイル名を除いています。この結果を @Implode で ¥ 区切りの文字列に戻し、フォルダ名としています。

なお、ルート(フォルダがない)場合はエラーとなるので、@If でエラー処理をしています。


フォルダ別のカテゴライズ

ここまでできたら、フォルダ列とファイル列を昇順にソートします。

そして、フォルダ列をカテゴライズします。カテゴリ列は ¥ を階層化して表示する機能があります。これを利用すると、以下のようにフォルダ階層に応じて、階層の深さが違うカテゴリを 1 列で実現できます。

ただ、サーバ直下のアプリは『(カテゴリ無し)』となっていて、格好が悪いですね。これを回避するためのカテゴリの第一階層をサーバ名に変更します。

xSrv := @Name([Abbreviate]; Server);
xLst := xSrv:@Explode(Pathname; "¥¥");
xIdx := @Elements(xLst);
xFol := @Subset(xLst;@Elements(xLst)-1);
@Implode(xFol; "¥¥")

今回の式では ¥ で分離したリスト値 xLst を作成する時点で先頭の要素にサーバ名を足しています。こうすることで、要素数が 1 となることはないので、エラー処理を省略しています。

ここまでの対応でビューは次のようになります。

ルート直下のアプリケーションが多くそれでも見づらい部分があります。これについてはテンプレート(.ntf)を表示しない、カタログの表示対象から外すなどの対応が必要かと思います。


まとめ

今回は、カタログにビューを追加する作業を行い、テンプレートから更新しない方法と ¥ でカテゴリを階層化する方法を解説しました。

なお、今回は事例として標準テンプレートの設計を変更しました。ビューの追加だけですので、影響はないと判断しています。とは言え、標準テンプレートをカスタマイズした場合、メーカサポートの対象外となることもあります。お使いの環境に適用される際には、リスクとベネフィットを十分にご検討の上、ご自身の責任で実施ください。


2024/06/23

DXL Step-by-Step:#34)表の列幅のしくみ

今回は、表の列幅の設定についてまとめます。


列幅の設定

ノーツクライアントでは表のプロパティで列幅を設定できます。設定は固定幅と可変幅に大別できます。固定幅の場合は、『固定幅』にチェックを入れて、幅を指定します。

可変幅の場合は、『固定幅』のチェックを外し、幅を指定します。画面上の表全体の幅からの相対的な幅となるので、画面のサイズが変われば伸縮します。


列幅の設定と DXL

2 行 3 列の表(マージンに合わせる設定)を作成し、1 列目の幅を 2 cmで固定します。残り 2 列は可変幅のまま、サイズを適当に調整します。

この表を DXL に変換し、列の定義 tablecolumn ノードを確認すると次のようになります。

固定幅の場合は、長さを明示的に指定し、可変幅の場合は % で指定されています。マージンの設定などと同じ仕組みですね。ただ、列幅の場合には、可変幅の合計が 100% となる仕様のようです。当たり前ではありますが...

列幅の設定 tablecolumn ノードの width 属性は次の通りとなります。

属性 設定値 補足
width '2.7875in' など 固定幅として幅を長さ(インチ)で指定
'33%' など 可変幅として割合(%)で指定
表内の可変幅の合計が 100% となる

また、前回 紹介した表の幅の設定(table ノードの widthtype 属性)で固定幅となっている場合には、列幅も固定幅だけとなります。DXL で表を操作する際には、設定のつじつまが合うように注意が必要ですね。


前回 DXL Step-by-Step 次回


2024/06/21

アプリカタログのカテゴリを活用しよう!

前回作成したアプリケーションカタログには、カテゴリ別というビューが存在します。

数が少ないので、サーバ内のすべてのアプリケーションが表示されているわけではなさそうです。このビューの設計をデザイナーで開き確認します。細かな点はさておき、Categories フィールドが 空 でない文書を表示していることがわかります。

カタログ内の文書では、以下の項目の部分が Categories フィールドとなります。

では、この値の出所を確認しましょう。そのアプリケーションのプロパティを確認すると、カテゴリ欄にセットされていました。Catalog タスクで情報を収集する際、このプロパティの値をカテゴリとして記録しているということですね。


改めて、カテゴリ別というビューの1列目をデザイナーで確認します。列式は次の通りです。

カンマとセミコロンで分離してカテゴリ化するしているようです。そこで、ヘルプDBのカテゴリを修正します(確認を兼ねていますので、分離記号をあえてそろえていません)。

ついでにその他のヘルプ系のDBのプロパティも同様に修正し、Catalog タスクを実行します。すると、次のような結果となり、複数のカテゴリにヘルプDBが表示されます。


この機能を利用すれは、利用者の部門や事業所、アプリケーションの種類など、さまざまな切り口でアプリケーションを分類・整理できます。複数のカテゴリに対応しているあたりが、かゆいところに手が届いていていいですね。


2024/06/19

アプリケーションカタログ とは?

Domino サーバ内のアプリケーションデータベースをどのように管理されてますでしょうか? 

Domino サーバには標準でサーバ内のアプリケーションを管理する機能が存在します。今回は、『アプリケーションカタログ』というその機能を紹介します。

なお、nsf をデータベースと呼んできた時代には、『データベースカタログ』と呼んでいました。


アプリケーションカタログの概要

Domino サーバ内には、catalog.nsf というアプリケーションが存在します。これを開くとサーバ内のアプリケーションの一覧が確認できます。例えば、[データベース名]を開くと名称でカテゴライズされたビューが開きます。


文書を開くとデータベースの様々な情報が確認できます。


まず、アプリケーション タブには、名称だけでなく、配置されているサーバ名、ファイル名、レプリカIDまで記録されています。ほかには、文書数やDBのファイルサイズ、作成日などもあります。

タブの最下部には、使用状況が表示されています。

また、アクセス制御リストタブには ACL の設定が列挙されています。

アプリケーションに関する情報が集約されて記録されていますので、さまざまな用途で利用できそうですね。


カタログ作成の仕組み

アプリケーションカタログを作成するには、サーバ上で Catalog タスクを実行します。実行するとそのサーバ内にあるすべてのデータベースがカタログに登録されます。

もし、サーバに catalog.nsf が存在しない場合には、 catalog.ntf から自動的に作成されます。

Domino サーバのデフォルトでは、午前 1 時に Catalog タスクが実行されます。これは、サーバの notes.ini に以下の行が記述されているからです。

ServerTasksAt1=Catalog,Design


カタログの表示

Catalog タスクは、メールやテンプレートをを含む、サーバ内のすべてのアプリケーションの情報を収集して記録します。

ただし、 プロパティの[設計]タブで『データベースカタログに表示』にチェックされているものだけがビューに表示される仕組みになっています。

この設定をうまく利用すると、一般利用者通常使用するアプリだけをリストアップすることができます。しかも、データ収集はサーバが自動で行ってくれますので便利ですね。


まとめ

カタログ文書を開くと上部に[開く]ボタンがあります。クリックするとそのアプリケーションが開きます。うまく使えば、アプリの一覧をお手軽に作成できそうですね。


2024/06/17

すべての LotusScript をリコンパイルは ”すべて” ではない !?

このブログでは、汎用的な関数など共通の関数やクラスなどはスクリプトライブラリで管理し、各アプリケーションではそれを利用する方法をお薦めしています。プログラムの独立性の向上や分業化など開発効率が上がると考えているからです。

今回はスクリプトライブラリを使用した環境で、気がついた障害(?)と注意点についてまとめます。


すべての LotusScript をリコンパイル

Domino デザイナーのメニューには[ツール]-[すべての LotusScript をリコンパイル]があります。一般的に、スクリプトライブラリに変更があった場合など、LotusScript のプログラム全体をチェックしたい場合に利用します。実行すると開発中のアプリケーション内のすべての LotusScript に対して文法的な整合性チェックを行います。

ライブラリの新バージョンがリリースされてそれを開発中のアプリに組み込んだ時、ライブラリ内の関数のインタフェース(引数など)を修正した場合に利用すると便利な機能です。


発見した問題と再現方法

先日、この『すべての LotusScript をリコンパイル』という機能が、すべてのコードを対象にしていないという問題を発見しました。

まずは、現象の再現です。

次のような単純なスクリプトライブラリとそれを利用するビューのアクションボタンを作成します。


プリビューして、ボタンをクリックすると ”テスト” とメッセージが表示されます。当たり前ですが...

この状態で、スクリプトライブラリを下記の通り、引数を削除して、メッセージを固定の文字列に変更して保存します。

Option Declare

Public Function ShowMsg()
   MsgBox "テスト", 64
End Function

この状態で「すべての LotusScript をリコンパイル」を実行します。引数が合わないので文法エラーとなるべきところなのですが、正常に終了してしまいます。

ただ、コンパイルが成功したからと言って、ノーツからそのボタンを押すとエラーが発生します。

エラーメッセージには、引数が正しくないことと関数名が表示されていますので、この現象はコンパイルの機能に問題があるということになりますね。


問題の詳細と対策

調査した結果この現象は、Notes 6 のころから発生している既知の問題(問題報告番号 GFLY5VWM3C)で、現在(14.0)でも発生するそうです。詳細が以下のサポート情報に掲載されていますのでご確認ください。

すべての LotusScript をリコンパイルを実施してもアクションボタンの LotusScript が精査されない

Workaround として、エラーが出るべき設計要素をデザイナーで編集し、再保存するタイミングでエラーが検知されるので取り除く、と紹介されています。設計数が多い時には少々つらい対策ですね。

現時点で開発者が注意すべきは「すべての LotusScript をリコンパイル」機能を過信しないことになります。今回事例にあげた引数の変更以外に、サポート情報にある関数名の不一致なども検知できないようです。アプリ利用時に文法エラーのような基本的なエラーに出くわした場合、この問題を疑ってみるべきかもしれませんね。

ちなみに、修正は『問題の重要度やリクエスト数など複数の要素で勘案される』そうです。開発ツールとして恥ずかしいレベルの問題と思うので、勘案している場合じゃないぐらい重要度は高いと感じますけどね...


2024/06/08

クラス化に挑戦: #12)クラス化作業のまとめ

前回まで 6 回にわたり、Google マップ の Place API の検索機能を題材にクラス化する作業を体験しました。結果を振り返りながら、クラス化の作業について整理しましょう。

改めて、今回作成したライブラリをデザイナーで確認してみます。全体を眺めると見えてくることがあります。

カプセル化

まず、最初に感じるのはスッキリ感ですね。

クラスを使用しない開発の場合、ライブラリに関数が増えてくると、煩雑になり、必要なものを探すのに苦労するようになります。名称順に並んではくれますが、機能的関連性はわかりません。名称を工夫する必要が出てきますが、数が多くなると悩ましくなってきます。

クラスは階層化して表示されます。そして、その階層の中にはクラスに関係があるものが集約されています。整理された状態になっており、気持ちがいいですね。

このように集約してまとめることをオブジェクト指向の世界では『カプセル化』というようです。


インターフェースの明確化と隠蔽

デザイナーでは Private な関数や変数には黄色の■がアイコン表示されています。これが付いていないものが Public となります。このマークを見れば、インターフェースとなるプロパティやメソッドがわかるということですね。

インターフェースを明確にするのは、不用意にデータにアクセスさせないなど、クラスが持つ機能やデータを安全に利用することが目的です。

プロパティで入力チェックを行い合格したデータだけを採用(メンバ変数にセット)したり、内部で利用するだけの関数を Private 化するなど、クラスの利用者が想定外の操作をしないようにして、安全性を高めます。このような考え方を『隠蔽』といいます。


命名規則

インターフェースとなるプロパティやメソッドは Count や Search など簡潔で端的な名称としています。クラス内に作成するメンバ変数や Private な関数には接頭文字として z をつけており、一覧で下の方に集まるようにしています。

結果的に、インターフェースが上に集まり目立つということですね。

複数のクラスで使用しそうな汎用的な関数(xGetElementByName_Obj や xGetLocation)はクラス外で定義されています。クラス外であることがわかるよう接頭文字は x としています。

関数の命名を少し工夫するだけで、さらにわかりやすいプログラムになりますね。


デバッガとクラス

クラスを利用した開発でデバッガを利用するには注意が必要です。

まずは不便な点、下図のイベント欄です。

クラスの名称や実行中の関数などが表示されず Declarations と表示されています。実際には NearBySearch クラスの Search メソッドを実行中なのですが表示されていません。

ステップ実行して確認するだけであれば、あまり気になりませんが、クラス内の Private 関数にブレークポイントをセットする場合など、クラス内を横断的に見たいときには、その場所を探すが大変です。

イベント欄にクラスを表示して、クラスを選択したら右にクラス内の関数を表示するなど、改善してほしいですね。

Notes 8 以降、スタンダード版のデザイナーが提供されるようになって、クラスが階層化して表示されるなど、クラス開発の効率がずいぶん上がりました。ただ、デバッガの機能は進化がなく旧来のままとなっています。機能強化が待たれますね。


デバッガの変数のタブは見方に少し注意がが必要です。下図は NearBySearch クラスの Search メソッドを実行中の、変数タブの状態です。

関数内の変数に混ざって、ME という変数があります。これが、Search メソッドを実行してるクラスのインスタンス(自分自身)を表します。中を確認すると、クラスのメンバ変数とその値が確認できます。NotesDocument などノーツオブジェクトクラスの場合はプロパティの値が見えますが、ユーザ定義クラスではプロパティとしては確認できません。

なお、3 列目の型には NEARBYSEARCH とクラス名が表示されています。また、zoLocation のようにメンバ変数内にユーザ定義クラスのインスタンスがある場合、そのメンバ変数がさらに階層化されて表示されます。


次回
前回 クラス化に挑戦


2024/06/07

クラス化に挑戦: #11)Google マップ - Place API の NearBySearch をクラス化 ③

Google マップ の Place API の検索機能のクラス化に挑戦する第3回です。前回でクラスが出来上がったので、今回はそのクラスを利用してみます。


テストエージェントの作成

第 8 回 に作成したテストエージェントと同様の機能を今回作成した NearBySearch クラスを使用して実現します。

せっかくですから、今回追加した新機能を使ってみましょう。検索結果のすべての地点を順に表示するため、ヒットした件数でループさせ、順に地点情報を取得してみます。

Option Declare
Use "lsGoogleMAP"

Sub Initialize
   Dim dLat As Double '緯度
   Dim dLng As Double '経度
   Dim oLocation As Location
   Dim oSearch As NearBySearch
   Dim oPlace As Place
   Dim i As Integer

   dLat = 34.683742526906634
   dLng = 135.49698067096077
   Set oLocation = New Location(dLat, dLng)
   
   ' 検索準備
   Set oSearch = New NearBySearch(oLocation)
   oSearch.Keyword = "鳥貴族"

   ' 検索実行
   If oSearch.Search() Then
       ' すべての検索結果を近い順に表示
       For i = 1 To oSearch.Count
         Set oPlace = oSearch.GetPlace_Nth(i)
         MsgBox CStr(i) & " 番目に近い店舗 = " & oPlace.PlaceName, 64
      Next
   End If
End Sub

実行すると、検索地点(本町駅)を中心に近い店舗が順に表示されます。大阪には鳥貴族がいっぱいありますね(笑)。

それはさて置き、正しく表示されれば、クラス化は成功です。


後片付け

クラス化作業は終了しましたが、元となったエージェントで不要となった関数が残っていますので掃除します。第 8 回 に作成した関数を含め、次の 4 つの関数が不要ですので削除します。

最終的にライブラリをデザイナーで確認すると、以下のようになります。ずいぶんすっきりしましたね。


なお、この作業を行うと 第 8 回 で作成したテストエージェントは動作しなくなりますのでご了承ください。



前回 クラス化に挑戦 次回


2024/06/06

クラス化に挑戦: #10)Google マップ - Place API の NearBySearch をクラス化 ②

Google マップ の Place API の検索機能のクラス化に挑戦する第2回です。今回は、検索メソッドを NearBySearch クラスに追加します。


検索機能作成の方針

第 8 回 に作成した GetNearestPlace とその関連関数を NearBySearch クラス内に移植します。

この関数は、検索を実行して、一番近い場所を返す関数でした。今回クラス化するにあたり、検索の実行と n 番目に近い場所を返す機能に分離したいと思います。

まずは、検索結果を保持するためのメンバ変数を作成します。

   Private zjnavResults As NotesJSONNavigator   '検索結果
   Private zjaResults As NotesJSONArray   '検索結果(地点情報の配列)


検索メソッドの作成

それでは、検索を実行するメソッドを作成します。GetNearestPlace をベースに検索して結果をメンバ変数に保持するまでがこのメソッドの役割です。

以下がサンプルプログラムです。赤字の部分が修正箇所となります。

   Public Function Search() As Boolean
      Dim sURL As String
      Dim sPram As String
      Dim http As NotesHTTPRequest
      Dim jnav As NotesJSONNavigator
      Dim jeResults As NotesJSONElement

      If zoLocation Is Nothing Then Exit Function
      On Error GoTo Proc_Err


      'URL の準備
      sURL = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
      sPram = zxGetPram()
      sURL = sURL & "?" & sPram

      'API に接続しレスポンスを JSON で取得
      Set http = xns.CreateHttpRequest()
      http.PreferJSONNavigator = True
      Set jnav = http.Get(sURL)

      '検索結果を取得
      Set jeResults = jnav.GetElementByName("results")
      If jeResults.Type = Jsonelem_type_array Then
         '検索結果をメンバ変数に保存
         Set zjnavResults = jnav
         Set zjaResults = jeResults.Value
         Search = True
      End If

Proc_Exit:
      Exit Function

Proc_Err:
      Resume Proc_Exit

   End Function

主な作業内容は2つです。

まず、検索に必要な情報は、前回の作業でクラス内(メンバ変数)に保持されています。保持した値を利用するので、関数に引数で渡す必要がなくなりますので、引数が各所で省略されています。

もう一点がエラー処理です。クラス内のエラーはクラス内で処理する前提で、エラー発生時はメソッドの戻り値を False とし、検索結果数を 0 となるようにしています。


また、このメソッドでは zxGetPram というサブ関数をコールしています。API コール時の URL パラメータを作成する関数で、ベースとなるのは xGetPram 関数です。

クラスへの移植にあたり修正した箇所を赤字としています。

   Private Function zxGetPram() As String
      Dim s As String
      Dim v As Variant

      s = "key=" & GoogleAPIKey

      '座標の指定
      s = s & "&" & "location=" & CStr(zoLocation.Latitude)
      s = s & "," & CStr(zoLocation.Longitude)

      '検索キーワードの指定
      If zsKeyword <> "" Then
         v = Evaluate(|@URLEncode("UTF-8"; "| & zsKeyword & |")|)
         s = s & "&" & "keyword=" & v(0)
      End If

      'プレイスタイプの指定

      If zsType <> "" Then
         v = Evaluate(|@URLEncode("UTF-8"; "| & zsType & |")|)
         s = s & "&" & "types=" & v(0)
      End If


      '固定パラメータの指定

      s = s & "&" & "language=ja"
      s = s & "&" & "rankby=distance"

      zxGetPram = s
   End Function

メンバ変数を使用する修正以外では、プレイスタイプ(zsType)を URL パラメータに付加する機能を追加しています。


検索結果を返すメソッド

最後に、検索結果から地点情報を返すメソッドを作成します。初めに記載したように n 番目に近い情報を取得できるメソッド GetPlace_Nth として作成します。

   Public Function GetPlace_Nth(ByVal viIndex As Integer) As Place
      Dim jePlace As NotesJSONElement

      'エラーチェック
      If zjaResults Is Nothing Then Exit Function
      If viIndex < 1 Then Exit Function
      If zjaResults.Size < viIndex Then Exit Function

      'n 番目の地点情報を取得
      Set jePlace = zjaResults.GetNthElement(viIndex)

      '地点情報からPlaceインスタンスを取得して返す
      Set GetPlace_Nth = New Place(jePlace)
   End Function


また、ヒットした件数を返すプロパティも併せて作成します。

   Public Property Get Count As Integer
      On Error Resume Next
      Count = zjaResults.Size
   End Property

なお、このプロパティでは、メンバ変数 zjaResults が Nothing である可能性があるので、エラーを無視するようにしています。


次回の予定

これで NearBySearch クラスは完成です。次回はこのクラスを組み込んでテストします。


前回 クラス化に挑戦 次回


2024/06/05

クラス化に挑戦: #9)Google マップ - Place API の NearBySearch をクラス化 ①

前回までは Google マップ の Place API の検索結果をクラス化してみました。要は検索結果の1件分を表すクラスでしたね。今回からは検索機能本体のクラス化に挑戦します。

作成するのは、NearBySearch クラスです。API の名称そのままとしています。今回はこのクラスで、検索前に必要な情報を管理する機能を作成します。


クラスと初期化の作成

NearBySearch で一番重要な情報は、検索の位置情報です。これがないと検索が成立しません。ですので、NearBySearch クラスでは検索位置を引数に初期化することとします。

クラス全体の定義を含め、コンストラクタである New メソッドを記述すると次の通りです。

Public Class NearBySearch
   Private zoLocation As Location

   Sub New(voLocation As Location)
      If Not(voLocation Is Nothing) Then
         Set zoLocation = voLocation
      End If
   End Sub
End Class

引数に検索位置を示す Location が指定されています。念のためではありますが Nothing でなければメンバ変数に保持するようにしています。


プロパティの作成

続いて必要なプロパティを作成します。作成するのは以下のプロパティです。

Location 取得のみ 検索位置の座標
SearchType 取得と設定 検索する場所の種類
Keyword 取得と設定 検索キーワード

NearBySearch クラスに追加するプログラムは次の通りです。

まず、メンバ変数を2つ追加します。

   Private zsKeyword As String
   Private zsType As String

続いて、次のプロパティを作成します。

   Public Property Get Location As Location
      Set Location = zoLocation
   End Property

   Public Property Set Keyword As String
      zsKeyword = Keyword
   End Property
   Public Property Get Keyword As String
      Keyword = zsKeyword
   End Property

   Public Property Get SearchType As String
      SearchType = zsType
   End Property
   Public Property Set SearchType As String
      zsType = SearchType
   End Property


次回の予定

これで検索までに必要な情報を管理する機能ができました。次回はこのクラスに検索メソッドを追加します。


前回 クラス化に挑戦 次回