0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Bラボデジタルサイネージ13】直近2件のバス時刻表を表示する「BusRModule」

Last updated at Posted at 2021-11-01

本記事は、株式会社 函館ラボラトリが運営する「Bラボ」における、大人向け「看板アプリ(デジタルサイネージ)を作るコース」用教材テキストです。

  1. 【Bラボデジタルサイネージ1】Processing+ラズパイでデジタルサイネージを作る!
  2. 【Bラボデジタルサイネージ2】ラズパイ初期設定
  3. 【Bラボデジタルサイネージ3】準備プログラム作成&機能実現の方針決定
  4. 【Bラボデジタルサイネージ4】レイアウトの基準になるグリッドを表示する「GridModule」
  5. 【Bラボデジタルサイネージ5】レイアウトの基準になる枠を表示する「PlaceholderModule」
  6. 【Bラボデジタルサイネージ6】画像を全画面表示する「FullImageModule」
  7. 【Bラボデジタルサイネージ7】ページの自動切り替え
  8. 【Bラボデジタルサイネージ8】設置されている場所の名前を表示する「LocationModule」
  9. 【Bラボデジタルサイネージ9】現在の時間を表示する「DateModule」
  10. 【Bラボデジタルサイネージ10】ページ切り替えの時間が分かる「ProgressBarModule」
  11. 【Bラボデジタルサイネージ11】現在の表示中のページが分かる「PageControlModule」
  12. 【Bラボデジタルサイネージ12】現在の天気を表示する「WeatherRModule」
  13. 【Bラボデジタルサイネージ13】直近2件のバス時刻表を表示する「BusRModule」(本記事)
  14. 【Bラボデジタルサイネージ14】ごみ出しカレンダーを表示する「GomiRModule」
  15. 【Bラボデジタルサイネージ15】(発展編)ツイートを表示する「TwitterRModule」
  16. 【Bラボデジタルサイネージ16】開店/閉店を表示する「OpenCloseRModule」
  17. 【Bラボデジタルサイネージ17】部屋の温度を表示する「TemperatureRModule」
  18. 【Bラボデジタルサイネージ18】部屋の明るさを表示する「BrightnessRModule」
  19. 【Bラボデジタルサイネージ19】起動画面を表示する「LaunchingScreenModule」
  20. 【Bラボデジタルサイネージ20】RModuleの影を実装

前提

BusRModuleでは、バス時刻表のためのデータにGoogleスプレッドシートを使用します。
あらかじめGoogleアカウントを取得しておいてください。

作るもの

Googleスプレッドシート上に打ち込んだバス時刻表を使い、直近2件のバス発車時刻を表示します。

0023.png

画像の準備

BusRModuleの背景画像には、下の写真を使用します。

background.jpg

画像を保存し、「background.jpg」という名前をつけます。
(.png形式など別の形式の場合は、.jpg形式に変換してください。)

バス停までのマップには、下の画像を使用します。

bus_map.jpg

画像を保存し、「bus_map.jpg」という名前をつけます。
(.png形式など別の形式の場合は、.jpg形式に変換してください。)

Processingの画面上のタブで、「スケッチ > スケッチフォルダーを開く」を選ぶと、開いている.pdeファイルが一覧で表示されます。
「data」フォルダの中に、新たに「bus」フォルダを作ってください。
先ほど名前をつけて保存した「background.jpg」を「data/bus」フォルダの直下においてください。(ドラッグ&ドロップ)

ここで新しく作った「data/bus」フォルダの位置を示すパスを、定数として定義します。

DigitalSignage.pde
/* 略 */

final String AD_PATH = "ad/";
final String WEATHER_PATH = "weather/";
final String BUS_PATH = "bus/"; /* 追加 */

/* 略 */

bus/bus_map.jpgをプログラムで読み込みます。
BusRModuleの背景を保持する変数も定義します。

DigitalSignage.pde
/* 略 */

/* ここから追加 */
// BusRModule
PImage busMap;
PGraphics busBackground;
/* ここまで追加 */

/* 略 */

initializeImage()の中でbus_map.jpgを読み込みます。

DigitalSignage.pde
void initializeImage() {
  busMap = pImageCut(loadImage(BUS_PATH + "bus_map.jpg"), CENTER, CENTER, 1280, 720); /* 追加 */
  adImage = new PImage[AD_IMAGE_COUNT];
  for (int i = 0; i < AD_IMAGE_COUNT; i++) {
    adImage[i] = loadImage(AD_PATH + "ad" + i + ".jpg");
  }
}

次にモジュール背景を生成しておきます。
WeatherRModuleのときと同様に、initializeRModuleBackground()内で実装します。

Initialize.pde
/* 略 */

void initializeRModuleBackground() {
  RModule module;
  int w;
  int h;
  PImage back;
  
  module = RModule.Weather;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(WEATHER_PATH + "background.jpg");
  weatherBackground = createGraphics(w, h);
  weatherBackground.beginDraw();
  weatherBackground.colorMode(HSB, 360, 100, 100, 100);
  weatherBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  weatherBackground.fill(0, 0, 0, 40);
  weatherBackground.noStroke();
  weatherBackground.rect(0, 0, w, h);
  weatherBackground.endDraw();
  weatherBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  
  /* ここから追加 */
  module = RModule.Bus;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(BUS_PATH + "background.jpg");
  busBackground = createGraphics(w, h);
  busBackground.beginDraw();
  busBackground.colorMode(HSB, 360, 100, 100, 100);
  busBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  busBackground.fill(0, 0, 0, 40);
  busBackground.noStroke();
  busBackground.rect(0, 0, w, h);
  busBackground.endDraw();
  busBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  /* ここまで追加 */

/* 略 */

BusRModule用関数の準備

新しいファイルRM_Bus.pdeを作ります。
BusRModuleの描画用関数drawBusRModule()、バス時刻表更新用の関数updateBus()を作ります。

RM_Bus.pde
void drawBusRModule(Area area) {
  
}

boolean updateBus() {
  return false;
}

時刻表スプレッドシートの作成

上記のスプレッドシートを、ご自身のアカウントのGoogleドライブ内に複製してください。

スプレッドシートに含まれる値についての説明は以下の通りです。

変数名 意味
line_name バスの系統名(「67系統」など)
departure_hour 出発時刻の「時」
departure_minute 出発時刻の「分」
is_holiday 土日祝ダイヤかどうか

スプレッドシート上にあるデータをWeb API経由で取得するために、Google App Scriptを使用します。
スプレッドシート内のタブから、「拡張機能 > App Script」を選択してください。

スクリーンショット 2021-10-31 18.09.26.png

エディタが表示されるので、下記のプログラムを入力します。

index.gs
function getData(id, sheetName) {
  var sheet = SpreadsheetApp.openById(id).getSheetByName(sheetName);
  var rows = sheet.getDataRange().getValues();
  var keys = rows.splice(0, 1)[0];
  return rows.map(function(row) {
    var obj = {}
    row.map(function(item, index) {
      obj[keys[index]] = item;
    });
    return obj;
  });
}

function doGet(e) {
  var data = getData('URLから取り出した文字列', 'index');
  var output = ContentService.createTextOutput(JSON.stringify(data, null, 2));
  output.setMimeType(ContentService.MimeType.TEXT);
  return output;
}

URLから取り出した文字列の部分は、下記がスプレッドシートのURLのとき、

https://docs.google.com/spreadsheets/d/1B4EFM564k4pHl6GIi6_jf4Lo035YzOb5l5er8rn4sCQ/edit#gid=0

d//editの間にある、1B4EFM564k4pHl6GIi6_jf4Lo035YzOb5l5er8rn4sCQに置換します。
これはあくまで例ですから、実装するときには、先ほど作成したスプレッドシートのURLを参照してください。

次に、画面上部の「デプロイ」ボタンをクリックし、「新しいデプロイ」を選択します。

スクリーンショット 2021-10-31 18.35.59.png

設定用のウィンドウが出ます。
自分として実行する設定にした上で、アクセスできるユーザを全員にします。

スクリーンショット 2021-10-31 18.36.55.png

デプロイをクリックすると、APIのURLがクリックできるようになります。

スクリーンショット 2021-10-31 18.43.04.png

URLを開くと、下のようなデータが返ってきます。

データ例
[
    {
        "line_name": "74系統",
        "departure_hour": 6,
        "departure_minute": 54,
        "is_holiday": false
    },
    {
        "line_name": "74系統",
        "departure_hour": 6,
        "departure_minute": 54,
        "is_holiday": true
    },
    {
        "line_name": "67系統",
        "departure_hour": 7,
        "departure_minute": 25,
        "is_holiday": false
    },
    // 省略

得られたURLは、プログラムの中にも入れておきます。

DigitalSignage.pde
/* 略 */

final String WEATHER_API_KEY = "取得したKey";
final String BUS_API_URL = "取得したURL"; /* 追加 */

/* 略 */

これでWeb APIの実装が完了しました。

バス時刻表APIを叩く

関数updateBus()で、バスの時刻表を取得します。
WeatherRModuleのときと同様に、JSONArrayやJSONObjectを取得します。
取り出した値は、配列に格納します。

BusRModuleで使う変数を定義しておきます。

DigitalSignage.pde
/* 略 */

// BusRModule
PImage busMap;
PGraphics busBackground;
/* ここから追加 */
String[] lineNames;
int[] departureHours;
int[] departureMinutes;
boolean isUpdatedBus = false;
final String BUSSTOP_START = "中央小学校前";
final String BUSSTOP_END = "函館駅前";
/* ここまで追加 */

/* 略 */

Web API上で使われている変数名を配列で定義します。

RM_Bus.pde
/* 略 */

boolean updateBus() {
  final String[] keys = {"line_name", "departure_hour", "departure_minute", "is_holiday"}; /* 追加 */
}

取得したJSONを入れておくための変数と、try-catch文を作ります。
tryの中では、jsonを取得して変数へ代入します。

RM_Bus.pde
/* 略 */

boolean updateBus() {
  final String[] keys = {"line_name", "departure_hour", "departure_minute", "is_holiday"};
  
  /* ここから追加 */
  processing.data.JSONArray json = null;
  try {
    json = loadJSONArray(BUS_API_URL);
    println("updateBus(): バス時刻表を取得しました。");
  } catch (Exception e) {
    println("updateBus(): バス時刻表を取得できませんでした。" + e);
    return false;
  }
  /* ここまで追加 */

  return true;
}

JSONの中から、今日が平日であれば平日ダイヤ、土日祝であれば土日祝ダイヤのみに絞ります。
絞ったあとの件数分だけ、配列の大きさを確保しておきます。

RM_Bus.pde
/* 略 */

boolean updateBus() {
  final String[] keys = {"line_name", "departure_hour", "departure_minute", "is_holiday"};
  
  processing.data.JSONArray json = null;
  try {
    json = loadJSONArray(BUS_API_URL);

    /* ここから追加 */
    // 平日なら平日ダイヤ、土日祝なら土日祝ダイヤだけの数を数える
    int dataCount = 0;
    for (int row = 0; row < json.size(); row++) {
      if (json.getJSONObject(row).getBoolean(keys[3]) == isHoliday) {
        dataCount++;
      }
    }
  
    lineNames = new String[dataCount];
    departureHours = new int[dataCount];
    departureMinutes = new int[dataCount];
    /* ここまで追加 */

    println("updateBus(): バス時刻表を取得しました。");
  } catch (Exception e) {
    println("updateBus(): バス時刻表を取得できませんでした。" + e);
    return false;
  }

  return true;
}

JSONから値を取り出します。

RM_Bus.pde
/* 略 */

boolean updateBus() {
  final String[] keys = {"line_name", "departure_hour", "departure_minute", "is_holiday"};
  
  processing.data.JSONArray json = null;
  try {
    json = loadJSONArray(BUS_API_URL);

    // 平日なら平日ダイヤ、土日祝なら土日祝ダイヤだけの数を数える
    int dataCount = 0;
    for (int row = 0; row < json.size(); row++) {
      if (json.getJSONObject(row).getBoolean(keys[3]) == isHoliday) {
        dataCount++;
      }
    }
  
    lineNames = new String[dataCount];
    departureHours = new int[dataCount];
    departureMinutes = new int[dataCount];
    
    /* ここから追加 */
    // JSONから系統名、時刻を取り出して保存
    int i = 0;
    for (int row = 0; row < json.size(); row++) {
      processing.data.JSONObject obj = json.getJSONObject(row);
      if (obj.getBoolean(keys[3]) == isHoliday) {
        lineNames[i] = obj.getString(keys[0]);
        departureHours[i] = obj.getInt(keys[1]);
        departureMinutes[i] = obj.getInt(keys[2]);
        println("[" + lineNames[i] + "] " + nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2));
        i++;
      }
    }
    /* ここまで追加 */

    println("updateBus(): バス時刻表を取得しました。");
  } catch (Exception e) {
    println("updateBus(): バス時刻表を取得できませんでした。" + e);
    return false;
  }

  return true;
}

関数updateBus()が実装できたので、この関数を呼び出すようにします。
天気データを更新するタイミングは、以下の2つとします。

  • デジタルサイネージを起動したとき
  • 1日経過したとき

関数updateBus()で正しく値を取得できたかを表す変数isUpdatedBusへの代入として呼び出します。

DigitalSignage.pde
/* 略 */

void updateDatas() {
  updateDate();
  
  final boolean isUpdatedDay = (day != beforeDay);
  final boolean isUpdatedSecond = (second != beforeSecond);

  if (isUpdatedDay) {
    println("日付が変わりました。");

    if (!updateIsHoliday()) {
      println("ネットワークに接続できていない可能性があります。接続できているか確認してください。");
    }

    youbi = calcYoubi(year, month, day);
    youbiString = youbiToString(youbi);
    isUpdatedBus = updateBus(); /* 追加 */
    
    beforeDay = day;
  }
  
  if (isUpdatedSecond) {
    if (second % STAY_SECOND == 0) {
      updateNowPageID(true);
    }
    if (minute + second == 0) {
      println(hour + "時になりました。");
    }
    
    beforeSecond = second;
  }
}

/* 略 */
Initialize.pde
/* 略 */

void initialize() {
  initializeDate();
  initializeImage();
  initializeGrid();
  initializePlaceholder();
  initializeRModuleBackground();
  
  isUpdatedWeather = updateWeather();
  isUpdatedBus = updateBus(); /* 追加 */
}

/* 略 */

これでデータの更新処理が実装できました。

BusRModuleの描画

データが変数に代入できている状態になったので、画面上に表示するところまで実装します。

置換可能なReplaceableModuleを実装するとき、最低限定めなければいけないことは以下です。

  • 描画時の基準となるエリア(Area.area1〜Area.area8のいずれか)
  • 描画時のサイズ(Size.S〜Size.Lのいずれか)

まずはBusRModuleの描画時の基準となるエリアについて説明します。
BusRModuleの描画の基準になるのは、8つある表示エリアのうちArea.area3です。

0038e.png

0023.png

BusRModuleの実装に伴い、いくつかの変数に追加するべきものがあります。
RModuleにBusを追加しておきます。

DigitalSignage.pde
/* 略 */

enum RModule {
  Weather,
  Bus /* 追加 */
}

/* 略 */

関数moduleSize()もBusRModuleに対応させます。
BusRModuleのサイズはSize.Lです。

DigitalSignage.pde
Size moduleSize(RModule module) {
  if (module == RModule.Weather) return Size.M;
  if (module == RModule.Bus) return Size.L; /* 追加 */
  return Size.S;
}

次に関数drawBusRModule()の実装に入ります。
Area.area3の左上の座標、BusRModuleの幅と高さを取得します。

RM_Bus.pde
void drawBusRModule(Area area) {
  /* ここから追加 */
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  /* ここまで追加 */
}

BusRModuleのタイトルの描画を実装します。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  /* ここから追加 */
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  /* ここまで追加 */
}

データ更新が正しくできなかったときの処理を実装します。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  
  /* ここから追加 */
  if (isUpdatedBus) {

  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BusModule\nデータを取得できません", x+w/2, y+h/2);
  }
  /* ここまで追加 */
}

データが正しく取得できた時の描画処理を実装します。
まずはバス停名を表示する部分を実装します。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  
  if (isUpdatedBus) {
    /* ここから追加 */
    noStroke();
    fill(GREEN_COLOR);
    rect(x+50, y+120, w/2-100, 60);
    rect(x+w/2+50, y+120, w/2-100, 60);
    
    // 始点と終点のバス停名を表示し、間に矢印
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_START, x+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_END, x+w/2+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, "→", x+w/2, y+130);
    /* ここまで追加 */
  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BusModule\nデータを取得できません", x+w/2, y+h/2);
  }
}

現在の時刻以降の2件を表示する処理を実装します。
取得した時刻表の件数分実行するfor文と、時刻の「時(departure_hour)」が現在の「時(hour)」と等しい時、現在より大きいときのif文を実装します。
countで、現在の時刻から近い2件を数えます。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  
  if (isUpdatedBus) {
    noStroke();
    fill(GREEN_COLOR);
    rect(x+50, y+120, w/2-100, 60);
    rect(x+w/2+50, y+120, w/2-100, 60);
    
    // 始点と終点のバス停名を表示し、間に矢印
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_START, x+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_END, x+w/2+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, "→", x+w/2, y+130);
    
    /* ここから追加 */
    int count = 0;
    for (int i = 0; i < lineNames.length; i++) {
      if (hour == departureHours[i]) {
        
      }
      if (hour < departureHours[i]) {
        
      }
      // 表示する2件が揃ったらこれ以降のダイヤは見ない
      if (count == 2) break;
    }
    /* ここまで追加 */
  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BusModule\nデータを取得できません", x+w/2, y+h/2);
  }
}

現在時刻から近い2件を取得するときには、次の2つの条件が使えます。

  • 時刻表のdeparture_hourと現在時刻のhourが同値で、時刻表のdeparture_minuteが現在時刻のminute以上
  • 時刻表のdeparture_hourより現在時刻のhourが大きい

一つ目の条件とそのときの処理を、for文内のif文1つ目で実装します。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  
  if (isUpdatedBus) {
    noStroke();
    fill(GREEN_COLOR);
    rect(x+50, y+120, w/2-100, 60);
    rect(x+w/2+50, y+120, w/2-100, 60);
    
    // 始点と終点のバス停名を表示し、間に矢印
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_START, x+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_END, x+w/2+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, "→", x+w/2, y+130);
    
    int count = 0;
    for (int i = 0; i < lineNames.length; i++) {
      if (hour == departureHours[i]) {
        /* ここから追加 */
        // すでに出発した便は飛ばす
        if (departureMinutes[i] < minute) continue;
        // 「13:00」のように発車時刻表示
        String time = nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2);
        drawText(CENTER, BASELINE, WHITE_COLOR, 48, time, x+50+(w/2-100)/2, y+220+70*count-16);
        // 「67系統」のように系統名表示
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, lineNames[i], x+w/2+50+(w/2-100)/2, y+220+70*count);
        // 「30分後」のように発車までの時間(分)表示、「67系統」のように系統名表示
        int remainMinute = departureMinutes[i] - minute;
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, remainMinute+"分後", x+w/2, y+220+70*count);
        // 文字の下に緑の線を表示
        stroke(WHITE_COLOR, 50);
        line(x+50, y+255+70*count, x+w-50, y+260+70*count);
        
        count++;
        /* ここまで追加 */
      }
      if (hour < departureHours[i]) {
        
      }
      // 表示する2件が揃ったらこれ以降のダイヤは見ない
      if (count == 2) break;
    }
  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BusModule\nデータを取得できません", x+w/2, y+h/2);
  }
}

2つ目の条件とそのときの処理を、for文内のif文2つ目で実装します。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  
  if (isUpdatedBus) {
    noStroke();
    fill(GREEN_COLOR);
    rect(x+50, y+120, w/2-100, 60);
    rect(x+w/2+50, y+120, w/2-100, 60);
    
    // 始点と終点のバス停名を表示し、間に矢印
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_START, x+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_END, x+w/2+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, "→", x+w/2, y+130);
    
    int count = 0;
    for (int i = 0; i < lineNames.length; i++) {
      if (hour == departureHours[i]) {
        // すでに出発した便は飛ばす
        if (departureMinutes[i] < minute) continue;
        // 「13:00」のように発車時刻表示
        String time = nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2);
        drawText(CENTER, BASELINE, WHITE_COLOR, 48, time, x+50+(w/2-100)/2, y+220+70*count-16);
        // 「67系統」のように系統名表示
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, lineNames[i], x+w/2+50+(w/2-100)/2, y+220+70*count);
        // 「30分後」のように発車までの時間(分)表示、「67系統」のように系統名表示
        int remainMinute = departureMinutes[i] - minute;
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, remainMinute+"分後", x+w/2, y+220+70*count);
        // 文字の下に緑の線を表示
        stroke(WHITE_COLOR, 50);
        line(x+50, y+255+70*count, x+w-50, y+260+70*count);
        
        count++;
      }
      if (hour < departureHours[i]) {
        /* ここから追加 */
        String time = nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2);
        drawText(CENTER, BASELINE, WHITE_COLOR, 48, time, x+50+(w/2-100)/2, y+220+70*count-16);
        
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, lineNames[i], x+w/2+50+(w/2-100)/2, y+220+70*count);
        
        int remainMinute = (departureHours[i] - hour)*60 + (departureMinutes[i] - minute);
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, remainMinute+"分後", x+w/2, y+220+70*count);
        // 文字の下に緑の線を表示
        stroke(WHITE_COLOR, 50);
        line(x+50, y+255+70*count, x+w-50, y+260+70*count);
        
        count++;
        /* ここまで追加 */
      }
      // 表示する2件が揃ったらこれ以降のダイヤは見ない
      if (count == 2) break;
    }
  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BusModule\nデータを取得できません", x+w/2, y+h/2);
  }
}

バス停までのマップを表示します。
マップの画像の左右と下に50pxのスペースをあけ、画像を縮小して配置します。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  
  if (isUpdatedBus) {
    noStroke();
    fill(GREEN_COLOR);
    rect(x+50, y+120, w/2-100, 60);
    rect(x+w/2+50, y+120, w/2-100, 60);
    
    // 始点と終点のバス停名を表示し、間に矢印
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_START, x+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_END, x+w/2+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, "→", x+w/2, y+130);
    
    int count = 0;
    for (int i = 0; i < lineNames.length; i++) {
      if (hour == departureHours[i]) {
        // すでに出発した便は飛ばす
        if (departureMinutes[i] < minute) continue;
        // 「13:00」のように発車時刻表示
        String time = nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2);
        drawText(CENTER, BASELINE, WHITE_COLOR, 48, time, x+50+(w/2-100)/2, y+220+70*count-16);
        // 「67系統」のように系統名表示
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, lineNames[i], x+w/2+50+(w/2-100)/2, y+220+70*count);
        // 「30分後」のように発車までの時間(分)表示、「67系統」のように系統名表示
        int remainMinute = departureMinutes[i] - minute;
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, remainMinute+"分後", x+w/2, y+220+70*count);
        // 文字の下に緑の線を表示
        stroke(WHITE_COLOR, 50);
        line(x+50, y+255+70*count, x+w-50, y+260+70*count);
        
        count++;
      }
      if (hour < departureHours[i]) {
        String time = nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2);
        drawText(CENTER, BASELINE, WHITE_COLOR, 48, time, x+50+(w/2-100)/2, y+220+70*count-16);
        
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, lineNames[i], x+w/2+50+(w/2-100)/2, y+220+70*count);
        
        int remainMinute = (departureHours[i] - hour)*60 + (departureMinutes[i] - minute);
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, remainMinute+"分後", x+w/2, y+220+70*count);
        // 文字の下に緑の線を表示
        stroke(WHITE_COLOR, 50);
        line(x+50, y+255+70*count, x+w-50, y+260+70*count);
        
        count++;
      }
      // 表示する2件が揃ったらこれ以降のダイヤは見ない
      if (count == 2) break;
    }

    image(busMap, x+50, y+h-(w-100)*9.0/16.0-50, w-100, (w-100)*9.0/16.0); /* 追加 */
  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BusModule\nデータを取得できません", x+w/2, y+h/2);
  }
}

最後に、BusRModuleの背景を表示します。

RM_Bus.pde
void drawBusRModule(Area area) {
  RModule module = RModule.Bus;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);

  image(busBackground, x, y, w, h); /* 追加 */
  
  String title = "函館バス(平日ダイヤ)";
  if (isHoliday) title = "函館バス(土日祝ダイヤ)";
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, title, x+50, y+50);
  
  if (isUpdatedBus) {
    noStroke();
    fill(GREEN_COLOR);
    rect(x+50, y+120, w/2-100, 60);
    rect(x+w/2+50, y+120, w/2-100, 60);
    
    // 始点と終点のバス停名を表示し、間に矢印
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_START, x+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, BUSSTOP_END, x+w/2+50+(w/2-100)/2, y+130);
    drawText(CENTER, BASELINE, WHITE_COLOR, 32, "→", x+w/2, y+130);
    
    int count = 0;
    for (int i = 0; i < lineNames.length; i++) {
      if (hour == departureHours[i]) {
        // すでに出発した便は飛ばす
        if (departureMinutes[i] < minute) continue;
        // 「13:00」のように発車時刻表示
        String time = nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2);
        drawText(CENTER, BASELINE, WHITE_COLOR, 48, time, x+50+(w/2-100)/2, y+220+70*count-16);
        // 「67系統」のように系統名表示
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, lineNames[i], x+w/2+50+(w/2-100)/2, y+220+70*count);
        // 「30分後」のように発車までの時間(分)表示、「67系統」のように系統名表示
        int remainMinute = departureMinutes[i] - minute;
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, remainMinute+"分後", x+w/2, y+220+70*count);
        // 文字の下に緑の線を表示
        stroke(WHITE_COLOR, 50);
        line(x+50, y+255+70*count, x+w-50, y+260+70*count);
        
        count++;
      }
      if (hour < departureHours[i]) {
        String time = nf(departureHours[i], 2) + ":" + nf(departureMinutes[i], 2);
        drawText(CENTER, BASELINE, WHITE_COLOR, 48, time, x+50+(w/2-100)/2, y+220+70*count-16);
        
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, lineNames[i], x+w/2+50+(w/2-100)/2, y+220+70*count);
        
        int remainMinute = (departureHours[i] - hour)*60 + (departureMinutes[i] - minute);
        drawText(CENTER, BASELINE, WHITE_COLOR, 32, remainMinute+"分後", x+w/2, y+220+70*count);
        // 文字の下に緑の線を表示
        stroke(WHITE_COLOR, 50);
        line(x+50, y+255+70*count, x+w-50, y+260+70*count);
        
        count++;
      }
      // 表示する2件が揃ったらこれ以降のダイヤは見ない
      if (count == 2) break;
    }

    image(busMap, x+50, y+h-(w-100)*9.0/16.0-50, w-100, (w-100)*9.0/16.0);
  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BusModule\nデータを取得できません", x+w/2, y+h/2);
  }
}

これで描画用関数が実装できたので、drawModules()内で呼び出すようにします。

DigitalSignage.pde
/* 略 */

void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3); /* 追加 */
  } else if (nowPageID == 1) {

/* 略 */

ここまで実装できたら実行してみてください。
最新2件の発車時刻と、発車時刻までの時間、バス停までのマップが正しく表示されているはずです。

0023.png

最後に

これで直近2件のバス時刻表を表示する「BusRModule」が実装できました。
次は、**ごみ出しカレンダーを表示する「GomiRModule」**を作ってみましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?