Np-Urのデータ分析教室

オーブンソースデータなどWeb上から入手できるデータを用いて、RとPython両方使って分析した結果を書いていきます

R Shinyでドラッグ&ドロップ番外編 画像ファイルを読み込んで表示する

本記事は、Shiny Advent Calendar 2017の12日目の記事です。



今回は、Shiny100本ノックの第13弾です。

前回・前々回と、Drag&DropでShiny上にデータを読み込み、そのデータをプロットしてみる、ということを実践しました。
www.randpy.tokyo
www.randpy.tokyo


こちらを公開したところ、

という嬉しい反応をいただきました!
@R_beginnerさんありがとうございます!

と、いうことで!
やってみます!

Githubに一応コード置いてあります。といっても前回と大体一緒ですが、よろしければこちらも是非.
github.com

完成形のイメージ

まずは完成形イメージを共有します。

ドラッグ前
f:id:Np-Ur:20171211214613p:plain
ドラッグ後
f:id:Np-Ur:20171211214634p:plain

前回・前回同様、赤枠部分に画像をドラッグ&ドロップします。
すると、右側に画像が表示されるという、非常にシンプルなShinyアプリケーションです。

これだけだと機能として寂しいですが、皆さん色々組み合わせて面白いアプリケーションに変身させてください!

ソースコード解説

ソースコードを紹介します。
CSSファイルは前回・前々回と同様なので割愛します。

drag_and_drop.js

まずは、jsファイルから紹介します。

var datasets = {};
var f1 = function(e) { 
  e.preventDefault(); //ドラッグを許可するための定型文
};
var f2 = function(e) {
    e.preventDefault();//ドラッグを許可するための定型文
    
    var file = e.dataTransfer.files[0];
    var reader = new FileReader();//ファイルを読み込むためのオブジェクト

    reader.name = file.name;//onload処理で名前を取り出すために追加...bad know-howらしい 
    reader.onload = function() {
        Shiny.onInputChange("imgfile", reader.result);
    };
    reader.readAsDataURL(file);//この読み込みが終わってから上のonload処理が始まる
};

前回は、csvファイル内のテキスト情報を読み込む必要があったので、

reader.onload = function(e) {
  datasets[e.target.name] = e.target.result;
  Shiny.onInputChange("mydata", datasets);
};
reader.readAsText(file);

という風に、reader.readAsText()と書いていました。

しかし今回ドラッグ&ドロップで読み取るのは、画像ファイルなのでここを「reader.readAsDataURL()」と変える必要があります。

そして前回同様「Shiny.onInputChange()」メソッドを使って、shinyのinput変数として画像ファイル情報を渡してあげます。

server.Rのソースコード

次に、server.Rです。

library(shiny)

server = function(input, output, session) {
  observeEvent(input$imgfile, {
    
    output$image = renderUI({ 
      img(src = input$imgfile, alt=input$imgfile)
    })
  })
}

renderUIメソッドを使って、ui.R側で描写する内容を記述していきます。

「src = input$imgfile」と書くことで、imgタグのsrc属性に、jsファイルで受け取った画像情報を渡すことができます。
alt属性にも同じ内容を入れていますが、ここは何でもOkです。

ui.Rのソースコード

最後に、ui.Rです。

library(shiny)

shinyUI(
  fluidPage(
    tags$head(tags$link(rel = "stylesheet", href = "styles.css", type = "text/css"),
              tags$script(src = "drag_and_drop.js")),
    sidebarLayout(
      sidebarPanel(
        h2("赤枠に画像ファイルをドロップ"),
        div(id="drop-area", ondragover = "f1(event)", ondrop = "f2(event)")
      ),
      mainPanel(
        htmlOutput("image")
      )
    )
  )
)

今回変更したところは、mainPanel内のみです。

ここではhtmlOutputメソッドを使うことで、server.Rで記述した以下の内容をそのまま描写してくれます。

output$image = renderUI({ 
  img(src = input$imgfile, alt=input$imgfile)
})

ちょっとつまったところ

当初はShinyでよく使うrenderImageメソッドを使って画像を出力しようと思っていたのですが、なぜかうまくsrc属性が渡せず…

結構粘ったのですが、諦めてもうrenderUIで渡せばいいやん!と気づいてからは一瞬でした笑

まとめ

前回・前々回で行ったドラッグ&ドロップによるデータ読み込み機能の番外編として、画像ファイル読み込み機能を実装してみました。

リクエストというのは初めての経験だったのですが、非常に嬉しいですね!
@R_beginnerさんが求めていたものがこれだったのか、少し不安ではありますす……。

もし「こういう機能欲しいんだけどどうすればいい?」というのがあれば、どんどんリクエストくださいませ!