Javascript Timer の停止再開インターバル変更など

データベースのデータを読み込んで、飛行機の航跡地図を表示する処理で、録画した航跡の再現速度を速くしたり、遅くしたり、録画位置の変更、一時停止などする必要があるため作ってみました。
調べましたところ、タイマーを解除するのはclearIntervalで出来るのがわかりましたが、一時停止するメソッドがないようなので、タイマーは動かしたままにして、タイマーの処理内でフラグを見て判別してます。
再生速度変更も同じようにして、clearIntervalで解除してインターバルを変えて再開しています。

< コード例 >

HTML
Bootstrap4のモーダルダイアログのタイトル

        <div class="pull-left mt-0 histrangeclass">

          <!-- ポーズ再開ボタン -->
          <button id="histpause" type="button" class="btn btn-sm btn-primary tippyspan histshow pauseresume"
                    title="Pause live"><i class="fa fa-pause"></i></button>
          <button id="histresume" type="button" class="btn btn-sm btn-primary tippyspan histshow pauseresume dsp_none"
                    title="Resume live"><i class="fa fa-play"></i></button>          

          <!-- ヒストリースライダー -->
          <input id="histrange" type="range" class="histshow dsp_none curpo" min="1" max="200" step="1">  
          <span id="range_pos" class="histshow dsp_none">126/200</span>

          <!-- インターバルボタンズ --> 
          <span id="hist_int_buttons">

            <button id="histint_2" type="button" class="btn btn-sm btn-primary tippyspan histshow intbtns"
                    title="Change to 2 sec interval">2</button>
            <button id="histint_3" type="button" class="btn btn-sm btn-primary tippyspan histshow intbtns"
                    title="Change to 3 sec interval">3</button>

            <button id="histint_7" type="button" class="btn btn-sm btn-primary tippyspan histshow intbtns"
                    title="Change to 7 sec interval">7</button>

            <button id="histint_15" type="button" class="btn btn-sm btn-primary tippyspan histshow intbtns"
                    title="Change to 15 sec interval">15</button>

            <button id="histint_22" type="button" class="btn btn-sm btn-primary tippyspan histshow intbtns"
                    title="Change to 22 sec interval">22</button>

            <button id="histint_33" type="button" class="btn btn-sm btn-primary tippyspan histshow intbtns"
                    title="Change to 33 sec interval">33</button>

            <button id="histint_45" type="button" class="btn btn-sm btn-primary tippyspan histshow intbtns"
                    title="Change to 45 sec interval">45</button>

          </span>  

        </div>

タイマー関数


    /**
     * 飛行機マーカー描画タイマー関数
     * @param {type} interval タイマーインターバル (ms)
     * @returns {undefined}
     */
    const timerFunc = (interval) => {

      $("#timerint").removeClass("dsp_none")
      $("#timerint").show()
      $("#timerint").text("T:" + parseInt(interval / 1000))

      HistoryMap.histTimer = setInterval(() => {

        if (!isPaused) {

          const slidepos = eval(recpos) + eval(1)

          // スライダーを動かす //
          $("#histrange").val(recpos)
          $("#range_pos").text(`${slidepos}/${recs.length}`)

          console.log("TimerPos:" + recpos)
          const tmpdata = JSON.parse(recs[recpos]["TXT_JSON"])

          // 時計更新 //
          const currtmtxt = recs[recpos]["TM_ADD"].slice(5, 16).replace("-", "/") + "(JST)"
          const currtmtxtUTC = recs[recpos]["TM_ADD_UTC"].slice(5, 16).replace("-", "/") + "(UTC)"
          const postxt = `${eval(recpos) + eval(1)}/${recs.length}`
          console.log(currtmtxt)

          $("#historymap_time").text(currtmtxt + "  " + currtmtxtUTC + "  " + postxt)

          // 中心、ズーム //
          const clat = recs[recpos]["CLAT"]
          const clon = recs[recpos]["CLON"]
          const zoom = recs[recpos]["ZOOM"]
          //lmap.setView([clat, clon], zoom)
          lmap.flyTo([clat, clon], zoom)
          //lmap.panTo([clat, clon], zoom)


          // 飛行機マーカー描画 //
          const mmp = new MoveMarkerParent("", this.userId, false,
                  false, "", "")
          mmp.setTypeAndVoice("HISTORY", recs[recpos]["MP3_MSG"])
          const mkres = mmp.drawAircraftMarkers(lmap, tmpdata, false, false)
          console.log(mkres)

          recpos++;

          if (recpos === recs.length) {
           
            recpos = 0
          }

        }

      }, interval)

    }

初回読込


    let isPaused = false    // 停止用フラグ
    let recpos = 1          // 再生位置
 
    let intvl = Math.floor(this.firstInterval * 3 / 4 * 1000)

    // 初期インターバルボタン強調 //
    $("#histint_" + parseInt(intvl / 1000)).removeClass("btn-primary")
    $("#histint_" + parseInt(intvl / 1000)).addClass("btn-dark")

    // ライブ受信ランプ 受信コストを常に消す //
    $("#liveramp, #t2scost, #livecost").addClass("dsp_none")
    $("#liveramp, #t2scost, #livecost").hide()

    //alert(intvl)
    // 飛行機マーカー描画タイマー関数 //
    timerFunc(intvl)

停止、再開ボタンのリスナー


    // @@ 停止再開ボタンを押した時 @@ //
    $(".pauseresume").on("click", function (e) {

      const id = $(this).attr("id")
      isPaused = id === "histpause"
      $(this).addClass("dsp_none")
      $(this).hide()

      const opobtnid = isPaused ? "#histresume" : "#histpause"
      $(opobtnid).removeClass("dsp_none")
      $(opobtnid).show()

    })

スライダーでの位置変更


    $("#histrange").off("input")

    // @@ レンジスライダーの値が変わる時 @@ //
    // ユーザーが変えた場合発生、タイマーからは発生しない //
    $("#histrange").on("input", function (e) {

      const val = $(this).val()
      console.log("# input # slider pos is " + val)

      $("#range_pos").text(`${val}/${recs.length}`)

      isPaused = true

    })

再生インターバル変更ボタンのリスナー


    $(".intbtns").off("click")

    // @@ インターバル変更ボタンを押した時 @@ //
    $(".intbtns").on("click", function (e) {

      const val = $(this).text()
      //alert(val)

      $(".intbtns").removeClass("btn-dark")
      $(".intbtns").addClass("btn-primary")
      $(this).addClass("btn-dark")

      // タイマー再セットインターバル変更 //
      try {
        clearInterval(HistoryMap.histTimer)
      }
      catch (e) {
      }

      // 飛行機マーカー描画タイマー関数 //
      timerFunc(val * 1000)


    })

< 画面例 >
ボタンのテキストはインターバルの秒数です。
スライダーはHTMLの標準のを使ってスタイルはここを真似ました。

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

航空情報 2023年 4月号 [雑誌]
価格:1425円(税込、送料無料) (2023/3/3時点)

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

航空ファン 2023年 4月号 [雑誌]
価格:1361円(税込、送料無料) (2023/3/3時点)

GCP Translation API RESTでPHPから使う

< コード例 >


<?php

/*
  Google Translationリクエスト用スクリプト
  ソース言語は英語に固定
  
 */

include '../DefaultHeaderIni.php';
include '../MySQLConnectConfig_aviation.php';

// ============= Const =============== //

$URL = "https://www.googleapis.com/language/translate/v2?key=";
$KEY = "??????????????????????????????????";


// =================================== //

// == リクエストパラメータ取得 == //
$q = $_POST["q"] ? $_POST["q"] : $_GET["q"];
$target = $_POST["target"] ? $_POST["target"] : $_GET["target"];

if (!$q) {
  $q = "Good Morning";
}
if (!$target) {
  $target = "in";
}


// 送信データ //
// リテラルは必ずはシングルクオテーションにする //
$postdata = array(
    'q' => $q,
    'source' => 'en',
    'target' => $target
);

$postdata_json = json_encode($postdata);
$postdata_json = str_replace("\r", "", $postdata_json);
//exit;

$url = $URL.$KEY;

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_POSTFIELDS => $postdata_json,
    CURLOPT_HTTPHEADER => [
          'Content-Type: application/json; charset=UTF-8',
          "accept: application/json"
      ],
    
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
}
else {
  echo $response;
}

< リクエストForm data >

q: U A881 United Airlines B789 N17963 Landed 
from ORD to HND  Last Direction 269 Runway 22 Squawk 3126
21Seconds ago
target: ja

< レスポンス例 >

{
  "data": {
    "translations": [
      {
        "translatedText": "U A881 ユナイテッド航空 B789 N17963 ORD から HND に着陸 最後の方向 269 滑走路 22 スコーク 3126 21 秒前"
      }
    ]
  }
}

Tabulator formatterでフォーマットしたセルのhtmlをタイマーで更新する

 

 

 

Tabulatorでタイマーで動的に更新された値をもとにしたHTMLでセルの表示を部分的に変更させてみました。
formatterを使ってない場合は、JSONの配列を使って簡潔に出来そうですが、HTMLの部分入替がややこしくなるので、jQueryでDOMエレメントを書き換えています。

< コード例 >

Unix時刻フォーマット関数


/**
   * Unix時刻(ms)指定 時:分 フォーマット時刻
   * @param {type} utm ミリ秒Unix時刻
   * @param {type} incdate 日付有無
   * @param {type} withS 秒有無
   * @returns {String}
   */
  getFormatLocalTime2(utm, incdate, withS = false, withYMD = false) {

    //console.log(utm);

    const lTM = new Date(utm);
    const currMS = lTM.getTime();
    const Y = lTM.getFullYear();
    const M = lTM.getMonth() + 1;
    const D = lTM.getDate();
    let h = lTM.getHours();
    let n = lTM.getMinutes();
    let s = lTM.getSeconds();

    h = h.toString().length === 1 ? "0" + h : h;
    n = n.toString().length === 1 ? "0" + n : n;
    s = s.toString().length === 1 ? "0" + s : s;

    const tmtxt = h + ":" + n;
    const tmtxtWithDate = Y + "-" + (M < 10 ? "0" : "") + M + "-" +
            (D < 10 ? "0" : "") + D + " " + h + ":" + n;

    let res = ""
    if (incdate) {
      res = tmtxtWithDate;
    }
    else {
      res = tmtxt;
    }

    if (withS) {
      res += ":" + s
    }
    
    if (withYMD) {
      res = Y + "-" + M + "-" + D + " " + res
    }

    return res

  }

現在時刻タイマー


  // 時計スタート時刻 //
  const sttmutc = Number(sttm - UTC_OFFSET_SECONDS * 1000)

  const lt = new LocalTimes()

  let tmtxt = lt.getFormatLocalTime2(sttmutc, 0, true)
  $("#utctime").text(tmtxt + " (UTC)")
  // tableのcellのformatterで使う //
  let tmtxtUTCLong = lt.getFormatLocalTime2(sttmutc, 0, true, true)
  //console.log(tmtxtUTCLong)


  tmtxt = lt.getFormatLocalTime2(sttm, 0, true)
  $("#yourtime").text(tmtxt + " (You)")

  // == 時計時刻タイマー == //

  let tmpos = 0
  const clockTimer = setInterval(() => {

    const curtmutc = sttmutc + parseInt(1000 * tmpos)
    let tmfmt = lt.getFormatLocalTime2(curtmutc, 0, true)
    //console.log(tmfmt)
    tmtxtUTCLong = lt.getFormatLocalTime2(curtmutc, 0, true, true)
    //console.log(tmtxtUTCLong)

    $("#utctime").text(tmfmt + " (UTC)")

    const curtmyou = sttm + parseInt(1000 * tmpos)
    tmfmt = lt.getFormatLocalTime2(curtmyou, 0, true)
    $("#yourtime").text(tmfmt + " (You)")

    if (tmpos % 30 === 0 && tmpos !== 0) {
      // セルのAgo minutesを書き換え //
      updateRowDiffTimes(lt, tmtxtUTCLong)
    }


    tmpos++;


  }, 1000)   // <=== 1秒間隔

カラムの初期設定


        // ログ追加 クライアント時刻 UTC時刻 更新遅延秒 //
        {
          //headerTooltip: true,
          title: "Your Time", field: "TM_ADD_UTC_SHORT",
          width: 50, hozAlign: "center",
          formatter: function (cell, formatterParams, onRendered) {

            // 反転させて強調 //
            const cellelm = cell.getElement()
            $(cellelm).on("mouseover", function (e) {
              $(this).addClass("text_reverse")

            })
            $(cellelm).on("mouseout", function (e) {
              $(this).removeClass("text_reverse")
            })

            /**
             * Now - UTC追加時刻 分取得
             */
            const diffMinutesToNow = (datatm, nowtm) => {
              const lts = new LocalTimes()
              return Math.round(lts.getMinuteTimeDifference(datatm, nowtm))
            }

            const utcfmt = cell.getData().TM_ADD_UTC
            const dt = utcfmt.split(" ")[0]
            const tm = utcfmt.split(" ")[1]
            const y = dt.split("-")[0]
            let m = dt.split("-")[1]
            let d = dt.split("-")[2]
            let h = tm.split(":")[0]
            let n = tm.split(":")[1]
            let s = tm.split(":")[2]
            m = m.slice(0, 1) === "0" ? m.slice(1) : m
            d = d.slice(0, 1) === "0" ? d.slice(1) : d
            h = h.slice(0, 1) === "0" ? h.slice(1) : h
            n = n.slice(0, 1) === "0" ? n.slice(1) : n
            s = s.slice(0, 1) === "0" ? s.slice(1) : s
            const dttm = new Date(y, m, d, h, n, s)
            const dttmvalUTC = dttm.getTime()
            const dttmvalLocal = Number(dttmvalUTC) + Number(UTC_OFFSET_SECONDS * 1000)


            const lt = new LocalTimes()
            const ltfmt = lt.getFormatLocalTime2(dttmvalLocal, 0, false, false)

            const secagotxt = "SA " + cell.getData().UPDATED_SEC_AGO

            const diffToNow = diffMinutesToNow(cell.getData().TM_ADD_UTC, tmtxtUTCLong)

            return  ltfmt + "<br>" + cell.getValue() + "<br>" + secagotxt + "<br>" +
                    diffToNow + "MA"

            // tmtxtUTCLong

          },

タイマーで更新した値にしてHTMLを部分的書き換え


  /**
   * テーブルの差異分更新
   * @param {type} lt 時刻処理ユーティリティクラスのインスタンス
   * @param {type} currtmtxt 現在時刻テキスト
   * @returns {undefined}
   */
  const updateRowDiffTimes = (lt, currtmtxt) => {

    const rows = table.getRows()
    //console.log("rows.length:" + rows.length)

    // ++ テーブル行走査 ++ //
    rows.forEach((row) => {

      const utclong = row.getData().TM_ADD_UTC

      // ++ jQueryによる列走査 ++ //
      $.each($(row.getElement()).children(""), function (j, itm) {

        const fld = $(this).attr("tabulator-field")

        // UTC時刻列の差異分を書き換え //
        if (fld === "TM_ADD_UTC_SHORT") {

          //console.log($(this).html())
          let html = $(this).html()
          let ary = html.split("<br>")

          // セルデータのUTC時刻と今との差異分 //
          const diffToNow = Math.round(lt.getMinuteTimeDifference(utclong, currtmtxt)) + "MA"
          //console.log(diffToNow)

          // セルを書き換え //
          ary[3] = diffToNow
          $(this).html(`${ary[0]}<br>${ary[1]}<br>${ary[2]}<br>${ary[3]}`)

        }

      })
    })


  }

< 画面例 >

タイマーで30秒に1回、離着陸が何分前であるかを更新していく

航空WEBアプリ 3AFB

< URL >

https://mapflight.net/aviation/

3AFBとは

Airport, Airline, Aircraft と Favorite, Book の頭文字をとった航空WEBアプリです。
空港、エアライン、機材のデータベースで調べ物に使えます。
FlightRadar24のようなライブマップが楽しめます。
機材の写真集ページ(PHOTO)はお好みにカスタマイズ出来ます。
ブックしたフライトの通知が写真付メールで届きます。
多くの外部リンクにより幅広い情報を参照出来ます。

3AFBで出来ること

[ AIRPORT ]

1) IATA, ICAOで登録されている世界中の空港を検索出来ます。
2) 空港の基本的情報がわかります。
3) 明細から滑走路、ルート別フライト数、空港の地図と現在居る機体がわかります。

[ AIRLINE ]

1) IATA, ICAOで登録されている世界中の航空会社を検索出来ます。
2) 現在の機材数、航空会社のタイプ、重大インシデント数、クラッシュ数がわかります。
3) クリックすると、保有機材とその明細、明細からその機材の登録履歴、写真が見れます。飛行中の場合は地図で表示します。

機材明細

[ AIRCRAFT ]

1) ADS-Bが装着された世界中の機材を検索出来ます。
2) 明細からICAO機材コードでの航空会社別保有数、機材の写真がスライドショー表示で見れます。
3) 1か月に1回、データベースを更新していますのでほぼ最新の情報が参照出来ます。

[ FAVORITE ]

1) 各機材写真はお気に入り登録出来ます。
2) お気に入り登録した機材写真のサムネイル一覧です。
3) 現在飛行中のお気に入り機材については、ライブ地図で確認出来ます。

[ BOOKED ]

1) フライト番号を登録しておけますので、お仕事で活用出来ます。
2) 登録ユーザーの方には出発到着のメール通知が出来ます。
3) 登録内容はタイムラインチャートで確認出来ます。
4) 飛行中のフライトはライブ地図で確認出来ます。
5) 出発到着はすぐに更新されます。
6) アナウンスもあります。

タイムラインチャート

通知メール例

出発、到着、キャンセル、ダイバートがあった後、届きます。
機材が写真でわかります。(実際とは異なる場合もあります)
時刻はタイムゾーンで3種類(Local, UTC, You)、ステータスで3種類(Scheduled, Estimated, Actual)
地図で経路がわかります。(実際の飛行ルートとは異なります)


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[ LiveMap ]

トップメニューにはありません。
AIRPORTの表の2列目にあるアイコンをクリックして空港周辺を指定して開きます。

 

 

 

 

 

1) 各ページから、空港、機材コード、航空会社、ブッキング内容、お気に入り機材を指定したライブ地図が使えます。
2) ライブ地図は様々な設定により、自分の好みに出来ます。
3) 離着陸のアナウンスがあります。
4) ターミナル出発到着のアナウンスがあります。
5) 空港の境界線がわかります。
6) 空港の天気がわかります。
7) 機材の写真が見れます。
8) フライト情報がわかります。
9) 登録ユーザーの方は録画再生機能により予約したり、後からライブ地図を見ることが出来ます。
10) フライトリストで地図内飛行機のステータスがわかります。
11) ビューマップで飛行機からの景色を楽しめます。
12) どのスポットに到着したか、どのスポットから出発したかがわかります。
13) Waypoint, FIX, ローカライザー他、無線誘導施設装置の位置がわかります。(主要空港)
14) スポットの位置、名前がわかります。(主要空港)
15) 空港のMETARがわかります。
16) FIX, WayPointがポップアップしますのでどのチャート経路を飛行してるのかがわかりやすいです。

フライトリスト

ビュー地図

Waypoint
マウスが乗ると名前がわかります。

空港スポット、無線施設
空港を大ズームで見ると、スポット、ローカライザーがわかります。

[ TFTO ]

1) ライブ地図の離着陸状況を機材写真付一覧表で表示します。
2) ライブ地図から開くと、リアルタイムで更新されていきます。
3) ライブ地図のターミナル出発到着情報、スポット出発到着情報がわかります。
4) 空港近辺の天気がわかります。

[ STATIC MAP ]

1) 国内の空港の離陸、着陸、トランジションのチャートがわかります。

RJFF HAWKS NORTH ARRIVAL

RJTT LAXAS THREE DEPARTURE 34L

[ PHOTO ]

1) 機材を検索してサムネイル画像を表にして表示します。

Fuji Dream

ANA

JAL

 

 

 

 

[ カラーカスタマイズ ]

1) トップメニューと検索メニュー、背景の色を設定出来ます。
2) 右上の設定アイコンから、あらかじめ用意されたカラー、自分で選んだカラーを設定出来ます。
3) PHOTOページ写真右クリックメニューからDominat colorを選んだ検出結果から選んで、エアラインのカラーに出来ます。

トップメニュー・設定アイコンからの設定

 

 

 

 

 

PHOTO・機材写真 右クリック Dominat color からの設定

 

 

 

 

設定検索内容はヒストリーボタンから選べます。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[ データソース ]

AeroDataBox API
AirLabs API
Opensky network Aircradt database
PLANESPOTTERS PHOTO API
国土交通省国土数値情報
自作空港空軍基地境界線
AVWX
OpenWeatherNetwork API
OpenFlights Airport Airline database
AIS Japan と海外AIPの情報
FAA
EUROCONTROL

[ 外部リンク ]

実用的Skyscanner
他のマイ航空アプリ
FlightAware
AirNav RadarBox
航空会社ウェブページ
航空会社Facebook
航空会社Twitter
航空会社Instagram
航空会社Linkdln
SkyVector
AirNav
FlightPlan
EuroControl Aircraft Performance Database
PLANESPOTTERS
FlyTeam

 

松本零士・先祖代々

あなたの体の中には、ものすごい数の先祖代々の思いと夢が詰まっている

Tabulator 行コンテキストメニュー ビルトインファンクション編

ドキュメントはこちら

 

 

< コード例 >


    rowContextMenu: function (component, row) {
      //component - column/cell/row component that triggered the menu
      //e - click event object  <=== eventではなくrow

      console.log("--- component")
      console.log(component)

      console.log("--- e")
      console.log(row.getData().iata_code)
      //console.log(e.Target._row.data)


      const icao = row.getData().icao_code
      const iata = row.getData().iata_code
      const name = row.getData().name

      const icon = `Img/airline/airline_${iata.toLowerCase()}_square.png`


      let menu = [];

      let itm = {
        label: "<span class='mr-1'>" + "<img src='" + icon + "' width='20'/></span>" + icao + " Show LiveMap",
        action: function (e, column) {


          // -C モーダルダイアログで表示 //
          const lm = new LiveMap("#modal_livemap", "livemap-container")
          lm.setAirportLatLon(-500, -500)
          lm.setUserGroup(userGroup)
          lm.setUserId(userId)
          lm.showDialog(icao, iata, name)

        }
      }
      menu.push(itm)

      return menu;
    }

< 画面例 >