文化祭のHPとLINE@のマップ機能を実装した話 (2/4)

はじめに

学校の文化祭で技術系の開発をする団体ができたので、活動の一環としてHPとLINE@の製作に携わりました。
このページでは主にLINE@の自分が担当した箇所について書いています。
文化祭の話については、4つのページに分けています。

1.実装した機能(HP/LINE@)全体の概要

sho0126hiro.hatenablog.com

2.LINE@のmap機能について (今回)

3.HPのmapページについて(概要とCreateJSやHTML,JSの基本事項について)

sho0126hiro.hatenablog.com

4.HPのmapページについて(実装方法、実装時の注意点などについて)

sho0126hiro.hatenablog.com

目次

LINE@ - map機能の概要

自分はフロントエンドを担当したので、サーバ側については書いていません。
主に、ユーザーからの入力の判定や、送信するメッセ―ジの指定等について書いています。
LINE@のマップ機能では、直感的な操作で詳細マップ、模擬店の詳細を表示することで、エリアごとの遷移などを容易に行うシステムを実現しています。
(詳細はこちら

f:id:sho0126hiro:20181106232409p:plain:w300 f:id:sho0126hiro:20181106232404p:plain:w300 f:id:sho0126hiro:20181106232412p:plain:w300 f:id:sho0126hiro:20181106232414p:plain:w300

システムの概要

  • 1.ユーザーからのメッセージに対応する応答
    ユーザーがメッセージを送信すると、そのメッセージによって指定の画像やメッセージを返信することができます。
  • 2.imagemapメッセージの返信
    「○○のマップを表示」等のメッセージに対して、imagemapオブジェクトという、アクション等が決められた画像を返信します。
  • 3.アクションの指定
    指定できるアクションには、特定のメッセージをユーザーに送信させることができます。
  • 4.アクションに対してimagemapメッセージを返信
    送信させたメッセージに対して、imagemapオブジェクトを返信し、次の詳細マップを表示します。

以上のような流れで、画像の一部をタップした時に別の画像を表示させることができます。

また、Flex Messageを用いることで模擬店データ、研究室データをリッチに表示しています。

imagemapオブジェクトについて

詳細はこちら - LINE Messaging API リファレンス

イメージマップメッセージは、複数のタップ領域を設定した画像を送信できるメッセージです。 画像全体、画像を区切って複数のタップ領域を設定することもできます。

実際に作成したイメージマップメッセージ(一部)

    {
        "type": "imagemap",
        "baseUrl": "https://URL/linebot_mapImg10/Top/map-OutsideTop",
        "altText": "[Top] 校外全体マップ",
        "baseSize": {
            "height": 469,
            "width": 1040
        },
        "actions": [
            {
                "type": "message",
                "text": "[Top] 校内全体マップへ",
                "area": {
                    "x": 731,
                    "y": 55,
                    "width": 256,
                    "height": 63
                }
            }
        ]
    }

'actions'プロパティ―には、イメージマップメッセージアクションオブジェクトを指定しています。
この例の場合https://URL/linebot_mapImg10/Top/map-OutsideTopの画像で
画像の横幅を1040pxとした時に左上:(731,55)から、右下(731+256 , 55+63)を範囲とする領域をタップすると、”[Top] 校内全体マップへ”というメッセージをユーザーに送信させます。

プロパティ―・内容

プロパティーと内容は以下の通りです。

プロパティ― 内容
type メッセージのタイプ
baseUrl 画像のurlです。細かな仕様は後程説明します。
altText 代替テキスト
baseSize.height 画像のサイズです。横幅を1040pxとした時の高さ(px)
baseSize.width 1040(px)を指定します。(固定)
actions イメージマップアクションオブジェクト(詳細はこちら )
action.type アクションのタイプ : message
action.text 送信するメッセ―ジ
action.area イメージマップ領域オブジェクト(詳細はこちら)
action.text.x イメージマップの左上角からの横方向相対位置
action.text.y イメージマップの左上角からの縦方向相対位置
action.text.width タップ領域の幅
action.text.height タップ領域の高さ

actionで指定する座標位置は、不規則な値なので、座標指定をするのがかなり大変でした。

f:id:sho0126hiro:20181208130824p:plain

imagemapオブジェクトの生成

imagemapオブジェクトの生成には、次のようなプログラムを用いています。

function msg_imagemap(usage,data){
    /*
     * イメージマップメッセージの生成
     * @param {string} usage 用途(map)
     * @param {obj}    data  用途に対応するデータ
     * @return {obj}   msg  イメージマップメッセージ
     */
    var msg = {"type":"imagemap"};
    if(usage == "map"){
        var location = map_data[data.location];
        msg.baseUrl  = location.baseUrl;
        msg.altText  = location.altText;
        msg.baseSize = location.baseSize;
        msg.actions  = location.actions;
    }
    return msg;
}

data.locationには場所情報を示すIDが格納されています。
map_data[data.location]で、場所に対応するデータを取得できます。

map_data[data.location]には、場所に対する以下の項目が含まれています。

  • baseUrl(画像のURL)
  • altText(代替テキスト)
  • 画像の幅を1040としたときの画像の高さ、幅(1040)
  • action
    • メッセージのタイプ(テキスト)
    • ユーザーに送信させるメッセージ
    • タップ領域、上の画像のデータを使用しています(x,y,高さ,幅)
      タップ領域ごとにユーザーに送信させるメッセージを変えるため、actionは複数存在します。

イメージマップオブジェクトについての注意点

1.画像形式・仕様について

imagemapオブジェクトの画像形式、仕様はかなり細かく定められています。

1.httpsであること
2.画像形式は png,jpeg
3.ファイルサイズは1MB以下
4.同じ画像で横幅を 240, 300, 460, 700, 1040px にした5つを用意する。
5.ディレクトリの構成は下の画像のようにすること

f:id:sho0126hiro:20181203211624p:plain:w150
6.ファイル名に拡張子は含めないこと
7.baseUrl二はフォルダまでのpathを書くこと

baseUrlが正しくない(または条件に合わない)とこのように表示されます。

f:id:sho0126hiro:20181203215646j:plain:w400

2.キャッシュについて

画像を変更しても、LINE@で送信されるimagemapオブジェクトの画像は変わりません。
推測ですが、LINEの内部で画像のキャッシュをとっているからだと思います。
BaseUrlを変更(アップロードするファイル名)すると更新されました。
少し面倒ではありますが、開発中に画像を変更する際、一番上の親ディレクトリの名前を変更する必要があります。

補足

httpsで画像ファイルを置く場所の確保

無料でデータを置くサービスは

等がありますが、FC2サーバーを利用しました。
(それ以外は、画像単体のURLが確保できない等の理由で実装できませんでした。)
参考にしたサイトはこちら

指定した画像形式への加工方法について

1.ImageMagickについて

横幅指定の画像を多く使用する場合、一度に多くの画像を処理しなければなりません。
自分は、shellScriptでImageMagickというソフトウェアを用いて、指定した画像形式のフォルダを生成させました。
ImageMagickについての詳細はこちらが分かりやすいです。

ImageMagickのconvertコマンドを使うと画像サイズを変更できます。
convertコマンドはImageMagickの代表的なコマンドで、画像のサイズ変更、フォーマット変更、画像の編集、色加工など機能は多伎にわたります。

ここでは幅を指定した画像サイズ変更の方法について書いておきます。
convert [変換前の画像名] -resize (width)x [変換後の画像名]
convert hoge.png -resize 240x 240
のように使用すると、"hoge.png"を幅240xに指定してサイズ変更し、"240"というファイルを出力します。

ただし、実際にイメージマップ画像の生成用に使用するには、ディレクトリ構造などを考えないといけません。
実際に使用したコードはこちら

github.com

2.画像サイズの取得

画像サイズが1040だった時の高さもsize.heightプロパティ―に指定する必要があります。

ImageMagickのidentifyコマンドを使用すると、画像サイズを取得することが可能です。
identifyコマンドは画像のフォーマットや容量、幅・高さはもちろん、他にも様々な情報を取得する事ができます。

identify -format "%w %h\n" [画像ファイル名] のように使用すると、%wの部分に幅、%hの部分に高さが出力されます。

実際に使用したコードはこちら  

github.com

Flex Messageについて

マップから表示する研究室の表示及び模擬店情報は、リッチにするためにFlex Messageを使用しています。
Flex Messageについての詳細はこちら - LINE Messaging API > Flex Messageを使う

自分は研究室情報のFlex Messageの作成を担当しました。
公開制作したLINE@では、他に「近くの模擬店を表示する機能」「MAPから模擬店を表示する機能」で、Flex Messageを使用しています。

Flex Messageは、複数の要素を組み合わせてレイアウトを自由にカスタマイズできるメッセージです。 Flex Messageはコンテナ、ブロック、コンポーネントの3層のデータ構造から構成されます。

FlexSimulatorを使うとレイアウトを容易に作成できます。

研究室情報及びLINE@ 詳細map機能では、3種類のFlex Messageを実装しました。

  • 簡易ボタン
  • 研究室詳細表示
  • 研究室詳細表示(カルーセル

    簡易ボタン

    簡易ボタンの構造は下の画像のようになっています。

f:id:sho0126hiro:20181208153414j:plain

簡易ボタン生成部は以下のプログラムで実装しています。
textsは文字列が入っている配列です。
配列に表示したい文字(選択肢)を複数代入すると、複数の選択肢からなるボタンが生成されます。

function Build_flexButton(texts){
    /**
     * 指定する選択肢のボタン(flex)を作成する
     * @param {str[]} text  // それぞれの選択肢で指定する文字列
     * @return {obj} flex message (Button)
     */
    var tmp = {
        "type": "bubble",
        "body": {
            "type": "box",
            "layout": "vertical",
            "spacing": "md",
            "contents": [
            ]
        }
    }
    for(i=0;i<texts.length;i++){
        var addtmp = {
            "type": "button",
            "style": "primary",
            "action": {
                "type":"message",
                "label":texts[i],
                "text":texts[i]
             }
        }
        tmp.body.contents.push(addtmp);
    }
    return tmp;
}

研究室詳細表示

研究室詳細表示の構造は下の画像のようになっています。

f:id:sho0126hiro:20181208153404j:plain

Flex Messageは、JSONデータが大きいため、メッセージの形式テンプレートを作成しました。
テンプレートに静的な文字列や、値を代入しています。
実際に作成したFlex Message のテンプレート(一部)はこちら

github.com

値の代入部分は以下のようなコードで実装しています。

  • 両日終日のところ
  • 日によって紹介の時間が変わるところ
  • 複数回研究室紹介が開かれている所
  • 文化祭2日のうち1日しか開いていない所等、

色々なパターンを 一つのプログラムで表示させるため、少し苦労しました。

JSONは参照渡しされるので、そのままだと、テンプレートデータを書き換えてしまいます。
一度Stringに変換すると、参照渡しが値渡しになるため、テンプレートを使用する場合Stringに変換する必要があります。

function Build_LaboFlex_Bubble(laboid){
    /**
     * 研究室IDに対する研究室のFlexデータを作成する
     * @param {string} laboid 研究室ID
     * @return {obj} tmp 研究室のFlexデータ(ひとつだけ。bubbleを返す)
     *
     * laboFlex_tmpdataにテンプレートのJSON
     * labo_dataに研究室の詳細データが格納されている。
     * labo_data[laboid]でlaboidの詳細データにアクセスする。
     */
    // JSONの参照渡しを値渡しにする
    var tmp = JSON.parse(JSON.stringify(laboFlex_tmpdata.tmp));
    tmp.body.contents[3].contents = []; // 初期化
    // 室内番号・詳細・タイトル
    tmp.body.contents[0].contents[0].text = labo_data[laboid].floor;
    if(labo_data[laboid] == "3208・3223") tmp.body.contents[0].contents[0].align = "center";
    else tmp.body.contents[0].contents[0].align = "start";
    tmp.body.contents[0].contents[1].text = labo_data[laboid].floorText;
    tmp.body.contents[1].text = labo_data[laboid].title;
    tmp.body.contents[1].size = labo_data[laboid].titleSize;
    // 日付・実施時間
    for(var i=0;i<labo_data[laboid].datetime.length;i++){
        var date = JSON.parse(JSON.stringify(laboFlex_tmpdata.dateTmp));
        date.contents[1].text = labo_data[laboid].datetime[i].date;
        tmp.body.contents[3].contents.push(date);
        var times = JSON.parse(JSON.stringify(laboFlex_tmpdata.timesTmp));
        for(var j=0;j<labo_data[laboid].datetime[i].times.length;j++){
            var time = JSON.parse(JSON.stringify(laboFlex_tmpdata.timeTmp));
            time.text = labo_data[laboid].datetime[i].times[j];
            times.contents[1].contents.push(time);
        }
        tmp.body.contents[3].contents.push(times);
        var separator = laboFlex_tmpdata.separator;
        tmp.body.contents[3].contents.push(separator);
    }
    // 補足情報
    tmp.body.contents[4].text = labo_data[laboid].supplementation;
    return tmp;
}

labo_dataには、以下の項目が含まれています。

  • 研究室ID
  • 部屋番号
  • 部屋の名前(○○室、等)
  • 研究紹介のタイトル
    • タイトルの文字の大きさ
  • 日付1
    • 時間1
    • 時間2
    • 時間3
    • 時間4
  • 日付2
    • 時間1
    • 時間2
    • 時間3
    • 時間4
  • 補足情報、メッセージ

実際には急な場所の変更に対応できるように、場所と研究室IDを結びつけていますが、詳細については割愛します。

研究室詳細表示(カルーセル

一部、カルーセルも実装しました。

f:id:sho0126hiro:20181208153407j:plain

ルーセルにすることで左右にスライドして他のbubbleが表示することができます。(最大10個まで)

ルーセルはbubbleオブジェクトをcontentsに入れるだけで実装できます。

Flex Message - カルーセルの例(一部)

{
  "type": "carousel",
  "contents": [
    {
      "type": "bubble",
      "body": {
        "type": "box"
      }
    }
  ]
}

代入部は以下のように実装しています。
msg(json)にflex-carouselオブジェクトを定義します。
その後、msg.contents.contentsにbubbleを代入しています。
Build_LaboFlex_Bubble(id);で、idに対応するbubbleを取得してます。

msg = {
            "type": "flex",
            "altText": "7棟の研究室情報",
            "contents": {
                "type": "carousel",
                "contents": []
            }
        };
msg.contents.contents.push(Build_LaboFlex_Bubble("stamp"));
for (var i=17; i<= 23; i++) {
    msg.contents.contents.push(Build_LaboFlex_Bubble("labo"+i));
}

idがstamplabo17からlabo23までのbubbleをカルーセル表示しています。

急遽LINE@フロント担当になり、フロント側の知識が結構身につきました。
しかし、バックエンド、サーバー系の知識が薄いのでこれから勉強していきたいです。
次回はHPの詳細マップぺージについて書きます。

sho0126hiro.hatenablog.com