Color Thief で画像のドミナントカラーを検出

Google chart API のバーの色をロゴ画像のドミナントカラーにしたいので、画像からドミナントカラーを検出するライブラリを探してましたところ、Color Thiefで出来るので使ってみました。
ここで教えて頂きました。

< コード例 >

ライブラリはCDNから読み込んでます。


<!-- Color Thief -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/color-thief/2.3.0/color-thief.umd.js"></script>

画像エレメントをColor Thiefで指定させるため、画像にidをつけます。


// ++ 行走査 ++ //
      $.each(rows, function (i, itm) {

        // エアラインロゴ画像にidをつける //
        td = $("#" + itm["id"]).children("td").eq(2)
        $(td).children("img").eq(0).attr("id", "alimg_" + i)

        == 省略 ==
})

Color Thiefで検出


// サイズが小さいとエラーなのでデフォルトをセットして例外で囲む //
      let color = [0, 0, 255]
      try {

        // ロゴのドミナントカラーを検出 //
        const img = document.querySelector('#alimg_' + i);
        color = colorThief.getColor(img);
        console.log(color)

      }
      catch (e) {
        //console.log(e)
      }

RGB値をhexのカラーコードに変換 (公式サイトのFAQにありました)


// RGBカラー配列Hex文字列変換 //
    const rgbToHex = (r, g, b) => '#' + [r, g, b].map(x => {
        const hex = x.toString(16)
        return hex.length === 1 ? '0' + hex : hex
      }).join('')

変換結果 (開発ツールのconsole)


DOWTABLE_BC1_0620_0820
(3) [27, 58, 138]
DOWTABLE_NH239_0625_0830
(3) [12, 51, 139]
DOWTABLE_7G41_0640_0845
(3) [44, 44, 44]
DOWTABLE_JL305_0700_0905
(3) [203, 4, 4]
DOWTABLE_NH241_0725_0930
(3) [12, 51, 139]
DOWTABLE_BC3_0730_0940
(3) [27, 58, 138]
DOWTABLE_JL307_0800_1005
(3) [203, 4, 4]
DOWTABLE_NH243_0830_1030
(3) [12, 51, 139]

< 画面例 >

羽田=>福岡の週間フライトスケジュール

 

Googleのタイムラインチャートで表示
JAL, ANA, SkyMark, StarFlyer で色分けされたバーになります。

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

天草エアラインの奇跡。 (集英社文庫(日本)) [ 鳥海 高太朗 ]
価格:572円(税込、送料無料) (2023/2/14時点)

楽天で購入

 

 

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

航空情報増刊 世界航空機年鑑 2022-2023 2023年 3月号 [雑誌]
価格:7700円(税込、送料無料) (2023/2/14時点)

楽天で購入

 

 

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

【全部揃ってます!!】日本のエアライン4 [全8種セット(フルコンプ)]【 ネコポス不可 】
価格:5984円(税込、送料別) (2023/2/16時点)

楽天で購入

 

 

Leaflet Javascript 地図内に画像を配置

HTMLで地図を配置したdiv内に画像用divを配置して、CSSで画像用divの位置をセットします。

< コード例 >
HTML
#livemap_photoのsrcはJavascriptでダイナミカリーにセットしています。

<!-- Leaflet伸縮地図 -->
<div id="livemap-container" class="z-depth-1-half map-container p-0">
<!-- 省略 -->
<!-- Planespotter 機材写真 -->
    <figure>
        <div id="livemap_photo_div" class="dsp_none">
            <a id="livemap_photo_a" href="" target="_blank">
                <img id="livemap_photo" src="" width="240" class="border border-primary rounded fade-in"></img>
            </a>
            <figcaption id="livemap_photo_figcap" class="flagcap_dark">..</figcaption>

        </div>
    </figure>
</div>

CSS

positionを absoluteにする
bootom, top, left. right で地図内でのパッディングをセットする
z-indexを地図より大きくする

/* ライブ地図機材写真 */
#livemap_photo_div {
    position: absolute;
    bottom: 8px;
    left: 8px;
    z-index: 99999;
    color: white;
    font-size: 14px;
    opacity: 0.8
}

< 画面例 >

Tabulator topCalc ビルトイン関数編

 

 

 

TabulatorのtopCalcプロパティを使えば集計値の表示が出来ます。
ビルトインで用意されたプロパティを使ってみました。
公式ドキュメントはこちら
sum, avg, count, maximum, minmum, concatenate が使えます。、

< コード例 >

sumとcount


        // 機材数 //
        {
          topCalc: "sum",
          title: "Fleet",
          field: "fleet_size",
          hozAlign: "right",
          width: 46,
          formatterParams: {thousand: ","},
          formatter: function (cell, formatterParams, onRendered) {
            if (cell.getValue() <= 0) {
              return ""
            }
            else {
              return numeral(cell.getValue()).format("#,###")
            }
          },
          //width: 50,
          sorter: "number",
          cssClass: "cell_num_onlytext_navy"
        },
        
        // 旅客貨物定時国際タイプ記号列挙 //
        { topCalc: "count",
          title: "Type", field: "type", hozAlign: "center", width: 50
        },

< 画面例 >

Tabulator topCalc カスタム自作関数編

 

 

 

Tabulatorで集計行の表示が、topCalc で簡単に出来て使ってましたが、ビルトインのプロパティにない集計が必要になり作ってみました。
公式ドキュメントはこちら

Group byした結果のアイテム数
メジアン
0や空白を除外した集計
などです。

< コード例 >
実は別関数にしたところ、うまく動かないので、カラムのプロパティに記述してます。

0以下除外平均


topCalc: function (values, dtoata, calcParams) {

  // 0 (データは-1)を除外した平均 //
  let cnt = 0
  let sum = 0
  values.forEach(function (value) {
  
    /*console.log("--- value")
     console.log(value)
     console.log("--- data")
     console.log(data)*/
  
    let val = value === -1 ? 0 : value
  
    if (val > 0) {
      cnt++;
      sum = Number(sum) + Number(val)
    }
  })
  //alert(sum + ":" + cnt)
  
  if (sum > 0 && cnt > 0) {
    const res = Math.round(sum / cnt * 10) / 10
    return res;
  }
  else {
    return 0
  }

}

0以下除外メジアン


topCalc: function (values, data, calcParams) {

  // 0 -1を除外したメジアン //
  let targets = []
  values.forEach((value) => {

    console.log(value)
    //let val = value.replaceAll(",", "")

    let val = value < 0 ? 0 : value
    val = parseInt(val)

    if (val > 0) {
      targets.push(val)
    }
  })
  console.log(targets)

  const half = Math.floor(targets.length / 2);

  if (targets.length % 2) {
    return targets[half];
  }
  else {
    return (targets[half - 1] + targets[half]) / 2;
  }

}

Group by アイテム数


topCalc: function (values, data, calcParams) {

  let manuCnts = {}
  values.forEach((tmp) => {

    manuCnts[tmp] = (manuCnts[tmp] || 0) + 1;

  })
  console.log("--- manufac")
  console.log(manuCnts)

  return Object.keys(manuCnts).length + " Manufactures"
}

< 画面例 >

GoogleMap Javascript のツールチップ

Leaflet Javascriptのツールチップがいい感じに出来たので、グレートサークルラインがかんたんなので、GoogleMapを使ってるアプリで同じようなこと出来ないか調べてみたところ、
Google語では、Labelのようで、調べて実装してみました。

ここで教えて頂きました。

公式ドキュメント(英語)

公式ドキュメント(日本語)

< コード例 >
マーカーのプロパティに label をセットします。


// 空港IATAラベル //
    let label = new google.maps.Marker({
      position: latlng,
      map: this.map,
      icon: {
        url: '',
        size: new google.maps.Size(1, 1),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(0, -12), // //左に0px、下に12px テキストはセンター
      },
      label: {
        text: this.poptxt,
        color: '#ffff00',
        fontFamily: 'sans-serif',
        fontWeight: 'bold',
        fontSize: '14px'
      }
    });

// 中央上ラベル //
          DrawMapG.nclabel = new google.maps.Marker({
            position: nclatlng,
            map: mapThis,
            icon: {
              url: '',
              size: new google.maps.Size(1, 1),
              origin: new google.maps.Point(0, 0),
              anchor: new google.maps.Point(0, -12), // //左に0px、下に12px テキストはセンター
            },
            label: {
              text: this.nctxt,
              color: '#ffffff',
              fontFamily: 'sans-serif',
              fontWeight: 'bold',
              fontSize: '14px'
            }
          });

         // 中央下ラベル //
          DrawMapG.sclabel = new google.maps.Marker({
            position: sclatlng,
            map: mapThis,
            icon: {
              url: '',
              size: new google.maps.Size(1, 1),
              origin: new google.maps.Point(0, 0),
              anchor: new google.maps.Point(0, 30), // //右に0px、上に30px テキストはセンター
            },
            label: {
              text: this.sctxt,
              color: '#ffffff',
              fontFamily: 'sans-serif',
              fontWeight: 'bold',
              fontSize: '14px'
            }
          });
 

< 画面例 >

Tabulator addRow()で行を追加した時の位置が上と下があるので

Tabulator addRow()で行を追加した時、原因はわかりませんが、既存の表の一番上に挿入されたり、一番下に追加されたりするので、戸惑いそうなので、上下線を入れて強調させてみました。

< コード例 >
CSS


.border-bottom-red1 {
    border-bottom: solid 1px red
}

.border-bottom-red2 {
    border-bottom: solid 2px red
}
.border-top-red1 {
    border-top: solid 1px red
}

.border-top-red2 {
    border-top: solid 2px red
}

Javascript


  // @@ テーブルタイトル右側 +アイコンを押した時 @@ //
  // ++ 追加 ++ //
  $(document).on("click", "#addarow", function (e) {
    //alert ("plus button clicked")

    // table行追加 //
    table.addRow({NM_GROUP: userGroup, DT_DEP: TODAY_FORMAT, ID_USER: userId, NO_FLIGHT: "NH____",
      IS_NOTIFY: 0, DEP_IATA: "", ARV_IATA: ""}, true)
    //table.selectRow([-1]);

    $("#savearow").removeClass("dsp_none")
    $("#savearow").show()
    $("#cancelarow").removeClass("dsp_none")
    $("#cancelarow").show()
    
    // 行を強調 //
    // NH____の行を特定 //
    const rows = table.getRows()
    rows.forEach((row) => {
      const noflight = row.getData().NO_FLIGHT
      if (noflight === "NH____") {
        
        const elm = row.getElement()
        $(elm).addClass("border-bottom-red1")
        $(elm).addClass("border-top-red1")
        return
      }
    })

    // == 以下省略 == //


  })

< 画面例 >

Tabulator コンテキストメニュー

ここで教えていただきました。

< コード例 >

HTML

        <!-- 空港テーブル本体 -->
        <div id="aptable" class="main_table"></div>
        <!-- コンテキストメニュー -->
        <div id="aptable_contextmenu" class="contextmenu-wrapper" style="display: none;">
            <div class="contextmenu-contents apcntxtdiv">
              
                <!-- 出発ライブ -->
                <ul id="cntxt_livemap_ul_arv" class="curpo apcntxt">
                    <img id="cntxt_livemap_icon_arv" src="Img/arrival_48_green.png" width="18" />
                    <span id="cntxt_livemap_arv">Show arrival LiveMap</span>
                </ul>
                <!-- 到着ライブ -->
                <ul id="cntxt_livemap_ul_dep" class="curpo apcntxt">
                    <img id="cntxt_livemap_icon_dep" src="Img/departure_48_blue.png" width="18" />
                    <span id="cntxt_livemap_dep">Show departure LiveMap</span>
                </ul>
                
                <!-- 出発ステータスリンク -->
                <ul id="cntxt_status_ul_arv" class="curpo apcntxt">
                    <img id="cntxt_status_icon_arv" src="Img/arrival_48_green.png" width="18" />
                    <span id="cntxt_status_arv">Open arrival status</span>
                </ul>
                <!-- 到着ステータスリンク -->
                <ul id="cntxt_status_ul_dep" class="curpo apcntxt">
                    <img id="cntxt_status_icon_dep" src="Img/departure_48_blue.png" width="18" />
                    <span id="cntxt_status_dep">Open departure status</span>
                </ul>
                
                
                <!-- SkyVector -->
                <ul id="cntxt_skyvec_ul" class="curpo apcntxt">
                    <img id="cntxt_skyvec_icon" src="Img/skyvector.png" width="18" />
                    <span id="cntxt_skyvec">Open SkyVector</span>
                </ul>
                <!-- Airnav -->
                <ul id="cntxt_airnav_ul" class="curpo apcntxt">
                    <img id="cntxt_airnav_icon" src="Img/airnav.png" width="18" />
                    <span id="cntxt_airnav">Open AirNav</span>
                </ul>
                 <!-- FlightPlan -->
                 <ul id="cntxt_fplan_ul" class="curpo apcntxt">
                    <img id="cntxt_fplan_icon" src="Img/flightplan_icon.png" width="18" />
                    <span id="cntxt_fplan">Open FlightPlan</span>
                </ul>
                 <!-- LiveATC -->
                 <!--<ul id="cntxt_liveatc_ul" class="curpo apcntxt">
                    <img id="cntxt_liveatc_icon" src="Img/liveatc.png" width="18" />
                    <span id="cntxt_liveatc">Open LiveATC</span>
                </ul>-->

            </div>
        </div>

カラムのセルコンテキストイベントから自作メニューを呼ぶ


    // 行番号 //
    {
      title: 'No.',
      field: 'rowno',
      formatter: 'rownum',
      frozen: true,
      width: 16,
      hozAlign: 'right',
      cssClass: 'cell_num',
      cellContext: function (event, cell) {
        //event - the click event object
        //cell - cell component

        const row = cell.getRow()

        $(".apcntxtdiv").show()

        // 通常の右クリックメニューを停止 //
        event.preventDefault()
        // 代わりに自作のメニューを表示 //
        showNoContextMenu(row, event.pageX, event.pageY)
      }
    },

自作メニュー


  /**
   *  No列コンテキストメニュー
   * @param {type} row 行オブジェクト
   * @param {type} posx ページX位置
   * @param {type} posy ページY位置
   * @returns {undefined}
   */
  const showNoContextMenu = (row, posx, posy) => {

    const cm = document.getElementById("aptable_contextmenu")
    
    // 行のデータ //
    const icao = row.getData().cdIcao
    const iata = row.getData().cdIata
    const city = row.getData().nmCity
    const apname = row.getData().nmAirport

    // 非表示をやめて、クリックした位置に表示する
    cm.style.left = (eval(posx) + eval(20)) + 'px'
    cm.style.top = (eval(posy) + eval(10)) + 'px'
    cm.style.display = ''

    // メニューのテキストを列の値で変更 //
    $("#cntxt_livemap_arv").text(iata + " Show arrival LiveMap")
    $("#cntxt_livemap_dep").text(iata + " Show departure LiveMap")

    $("#cntxt_status_arv").text(iata + " Open arrival status")
    $("#cntxt_status_dep").text(iata + " Open departure status")

    $("#cntxt_skyvec").text(icao + " Open SkyVector")
    $("#cntxt_airnav").text(icao + " Open AirNav")
    $("#cntxt_fplan").text(icao + " Open FlightPlan")
    $("#cntxt_liveatc").text(icao + " Open LiveATC")


    $(".apcntxt").show()

    $(".apcntxt").off("click")

    // @@ アイテムをクリックした時 @@ //
    $(".apcntxt").on("click", function (e) {

      cm.style.display = 'none'

      const id = $(this).attr("id")

      // == id別の処理 省略 == //
      


    })

  }

< 画面例 >

Tabulator テーブルの行IDを使えるようにするため

Tabulator で選んだ行などに対して処理を行いたい場合、一意に識別したい場合のIdの使い方です。

詳しくはこちら

< コード例 >

カラムに非表示のID列をセット

// 行ID //
{field: "id", visible: false},

行選択

// テーブル行選択 //
table.selectRow(0) //select row with id of 0

行Id取得

// 行ID取得 //
const rowidx = row.getIndex()

サーバー側

  // パラメータセット //
  $stmt = $dbh->prepare($SQL);
  $stmt->bindvalue(':MINDT', $mindt, PDO::PARAM_STR);
  $stmt->bindvalue(':MAXDT', $maxdt, PDO::PARAM_STR);
  $stmt->bindvalue(':GROUP', $group, PDO::PARAM_STR);
  $stmt->execute();

  $response = new stdClass();

  // 結果格納 //
  $i = 0;
  while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

    $response->data[$i]["ID_BOOK"] = $row["ID_BOOK"];
    $response->data[$i]["NM_GROUP"] = $row["NM_GROUP"];
    
    // Tabulator行ID //
    $response->data[$i]["id"] = $i;

    // == 省略 == //

    $i++;
  }
} catch (PDOException $e) {
  var_dump($e->getMessage());
}

echo json_encode($response, JSON_NUMERIC_CHECK);