概要
ここまでデジタイザを作ってきたが、そもそもデジタイザというものはプロットによって明らかにした座標の数値データを何かに利用するためにあるのだから、画面上でプロットしたデータをファイル出力して扱える形にしないとあまり意味がない。
x, y座標を表示しているテーブル部分をドラッグ&コピーして適当にエクセルなどに貼り付ければ、一応数値として取り出すことは可能だ。しかし、そんな手作業をいちいちやるのは面倒くさすぎるので、プロットした座標データを一発でファイル出力できるようにしてみる。
完成イメージ
- プロットした座標値をファイル出力する
実装
CSV出力処理
はじめに、コンポーネントのメンバ変数として出力ファイル名を定義しておく。outFileName
という変数を定義して適当な名前を設定する。
outFileName = 'coordinate.csv';
コンポーネントのクラスファイルに追加するメソッドは1つだけ。CSV出力処理を行うexportCSV()
を定義する。
まず、座標点が一点もプロットされていない状態だと当然ながら出力するものがないので、その場合はメッセージを表示して以降の処理はスキップする。
出力するCSVファイルは、データの情報(列名)を示すheader部分とデータ本体のbody部分をくっつけて出力する。
CSV形式はデータをカンマ,
で区切る。データ区切りの記号を指定する変数としてdelimiter
を用意し,
を格納する。
また、座標点のx, yの前に通し番号をつけて表示することにする。
header
データの内容が分かるように通し番号No
、座標値X
、Y
という列名を表示する。
'No, X, Y\n'
のようにそのまま文字列を書いてもいいが、せっかくdelimiter
があるので列名を入れた配列をdelimiter
でjoin
している。行末には改行コード\n
を入れる。
body
座標点を格納している配列であるvertexList
の各要素についてx座標、y座標の値を取り出して1行分のデータを作っていく。なお、vertexList
はx
とy
をkeyに持つオブジェクトを1つの要素とした配列になっている。
1つ目のmap()
の内部ではvertexList
の各要素vertex
についてObject.keys(vertex)
でkeyの配列[x, y]
を取得し、さらに2つ目のmap()
でkeyに対応するvalueを取得している。今回は座標値を小数点以下3桁まで表示するものとして、toFixed(3)
で小数点以下の桁数を指定している。「通し番号, x座標, y座標」の形ができたら最後に改行コード\n
を入れて1行分のデータが出来上がる。
このheader
とbody
を結合したcsvString
が、出力するCSVデータの中身そのものになる。
後は一般的なファイル出力の手順を踏むだけだ。
まず、データ文字列とデータタイプ(text/csv;charset=utf-8
)を指定してBlob
オブジェクトを生成する。
生成したBlob
オブジェクトをwindow.URL.createObjectURL(blob)
でurlに変換する。
次に、ファイルをダウンロードするためのリンクとなるHTMLAnchorElement
(a
タグ)をdocument.createElement('a')
で生成する。
生成したHTMLAnchorElement
要素のhref
属性にダウンロード用urlを、download
属性にファイル名を設定する。
HTMLAnchorElement
要素自体は画面上に見せないものなのでvisibility
はhidden
にする。
後はdocument.body.appendChild(link)
でHTMLAnchorElement
要素を追加してクリックすることでファイルがダウンロードされる。
ダウンロード後はHTMLAnchorElement
要素を除いておく。
exportCSV(): void { if (this.vertexList.length === 0) { alert('座標点がプロットされていません。'); return; } const delimiter = ','; const header = ['No', 'X', 'Y'].join(delimiter) + '\n'; const body = this.vertexList.map((vertex, index) => { return index + delimiter + Object.keys(vertex).map(key => { return vertex[key].toFixed(3); }).join(delimiter); }).join('\n'); const csvString = header + body; const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;'}); const url = window.URL.createObjectURL(blob); const link: HTMLAnchorElement = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', this.outFileName); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }
次にhtmlの方を編集する。
「CSV出力」ボタンを追加してclick
イベントとexportCSV()
を紐付けるだけでいい。
画像ファイルが読み込まれていない時だけボタンを非活性にしている。
<div class="d-flex image-configure mt-2"> <div class="col-10 p-0"> <button mat-raised-button (click)="onClickFileInputButton()">画像を読み込む</button> <button mat-raised-button (click)="setAxisRange()" class="ml-2" [class.editingAxis]="isEditAxis" [disabled]="!file || vertexList.length !== 0"> {{ isEditAxis ? '座標軸の設定終了' : '座標軸を設定する' }} </button> {{vertexList.length}}{{vertexList.length !== 0}} <button mat-raised-button class="ml-2" [disabled]="!file" (click)="resetViewConfig()">Viewをリセット</button> <button mat-raised-button class="ml-2 export-csv" [disabled]="!file" (click)="exportCSV()">CSV出力</button> </div> </div>
おわりに
以上で、長きに渡ったデジタイザを作る話は終わりとなる🎉🎉🎉
これまで実装してきたコードはこちら
参考