Mini Tokyo 3D バージョン 2.7.0 リリース

Mini Tokyo 3D バージョン 2.7.0 がリリースされました (GitHub)。2.6.0 からの追加・修正機能を見ていきましょう。

ライブカメラ映像の配信

Mini Tokyo 3D にライブカメラ映像の配信機能が登場しました。地図上のライブカメラボタンをクリックすると、カメラと同じ視点でリアルタイムに行き来する列車を眺めることができます。現時点で、赤羽、新宿、汐留、さいたま新都心など、東京近郊の16箇所に設置しています。

映像は YouTube で配信されているライブカメラ映像を埋め込んで使用しています。場所によっては不定期に YouTube の URL が変わるため、随時 URL をチェックした上でライブ配信中の場所のみを地図上に表示するようにしています。

ちなみにライブカメラの映像は大体20〜30秒くらい遅れるので、3Dマップ上の列車の方が早く現れがちになっています。

f:id:nagixx:20210130000806j:plain

パネルアニメーションの追加

列車・旅客機・駅の詳細情報やライブカメラ映像、経路検索のパネル表示の際のアニメーションを追加しました。画面下部に表示されるパネルが滑らかに開閉します。

経路検索結果のスワイプジェスチャー対応

経路検索の結果として複数の経路が返ってきたときに、水平方向のスワイプジャスチャーで経路を切り替えられるようにしました。特にタッチスクリーンデバイスで直感的な操作が可能になっています。

経路検索時に列車・航空機・追加レイヤーを非表示に

経路検索を行う際に列車・航空機・追加レイヤーを表示しないように変更しました。これによって、地図上のクリックによる駅の選択がしやすくなるとともに、画面描画処理が軽くなって遷移アニメーションがスムーズになっています。

緊急事態宣言に伴う終電時刻繰り上げダイヤに対応

1/8に発令された新型コロナウイルス感染症緊急事態宣言に伴う、首都圏の各路線の終電時刻繰り上げダイヤに対応しました(JR 東日本各線、りんかい線を除く)。

また、東武鉄道は昨年6月のリバティりょうもう増発、11月のアーバンパークライナー増発が反映されていなかったので合わせて運行ダイヤを修正しました。

駅出口情報の拡充

横浜市営地下鉄の各駅、および東急東横線みなとみらい線、JR 相鉄直通線、京急空港線東京モノレールの地下駅の出口情報を追加しました。

東京都交通局列車番号表示対応

都営地下鉄日暮里・舎人ライナー列車番号を、「ODPTxxxx」形式から本来の番号に変更しました。

f:id:nagixx:20210130000036j:plain

時間経過とともに色合いが変わる地図の作成

この記事は Mapbox Advent Calendar 2020 の23日目の記事です。

Mapbox GL JS では地図がベクトルタイルとして配信されるがゆえに、クライアント側で自由に描画をカスタマイズできるという利点があります。例えば、マップをモノクロ白地図風にしたり、ダークな背景に特定の道路だけ明るくして目立たせる、みたいなことが簡単にできます。

東京の公共交通のリアルタイム3Dマップ Mini Tokyo 3D では、これを活用して昼間なら明るい街、夜なら暗い街、そして夕方には夕焼け風、といった具合に時間の経過とともに色合いが変わる様子を表現しています。本記事では、これをどのように実現しているかを紹介します。

f:id:nagixx:20201223182309j:plain

まずはマップのスタイルから、色調整の対象となるレイヤーとその Paint プロパティを選別し、初期設定値をオブジェクトの配列に保存しておきます。これは後で、色を調整するための基準カラーとして使用します。

// 色調整の対象となる Paint プロパティキー
const paintPropertyKeys = {
    'background': ['background-color'],
    'line': ['line-color'],
    'fill': ['fill-color', 'fill-outline-color'],
    'fill-extrusion': ['fill-extrusion-color']
};

// 色調整の対象となるレイヤータイプ
const layerTypes = Object.keys(paintPropertyKeys);

// 初期カラー情報を格納する配列
const styleColors = [];

map.getStyle().layers.filter(
    layer => layerTypes.includes(layer.type)
).forEach(({id, type}) => {
    for (const key of paintPropertyKeys[type]) {
        let prop = map.getPaintProperty(id, key);

        if (typeof prop === 'string') {
            const [r, g, b, a] = parseCSSColor(prop);
            styleColors.push({id, key, r, g, b, a});
        }
    }
});

paintPropertyKeys には、レイヤータイプをキーとして、値には各レイヤーの対象となる Paint プロパティキーの配列を格納しており、すぐ下のループの中で色調整の対象となるレイヤーとプロパティの選択に利用しています。なお、Paint プロパティのカラー値には CSS カラーの文字列が入っていますので、それをパースするために css-color-parser-js を使っています。

取得したカラー情報は、レイヤー ID、プロパティキーとともに styleColors に入ります。この部分は初回に一回だけ実行していれば大丈夫です。

次に、「いつが昼でいつが夜か」という基準が必要ですが、これにはその場所の日の出・日の入時刻を知る必要があります。日の出・日の入時間は季節や緯度・経度によって変わるので、実際に求めるのは結構複雑です。そこで、ここでは SunCalc という JavaScript ライブラリを使っています。

// 日照に関する時刻を格納するオブジェクト
const times = SunCalc.getTimes(new Date(), 35.6814, 139.7670);

// 日の出時刻を表す UNIX epoch
const sunriseTime = times.sunrise.getTime();

// 日の入時刻を表す UNIX epoch
const sunsetTime = times.sunset.getTime();

上記のコードの通り、SunCalc.getTimes() の引数に現在時刻を表す Date オブジェクト、緯度、経度(例では東京)を渡すと、日照に関する時刻を格納するオブジェクトが得られます。その中の sunrise および sunset プロパティが日の出・日の入時刻を表す Date オブジェクトで、sunriseTime および sunsetTime には UNIX epoch (1970年1月1日からの経過ミリ秒) が入ります。

そして、「夜」「日の出」「昼」「日の入」における基準の色合いを決めておきます。下の表は、それぞれの状態における RGB 要素に乗じる係数と、この係数を白に乗じた場合の色見本を示しています。

状態 R G B 色見本
0.4 0.4 0.5  
日の出 0.8 0.9 1.0  
1.0 1.0 1.0  
日の入 1.0 0.9 0.8  

さらに、日の出前後の1時間ずつ、日の入前後の1時間ずつを色合いの推移の時間として、夜→日の出→昼、昼→日の入→夜の推移を滑らかにすることを考えます。

// 現在時刻を表す UNIX epoch
const now = Date.now();

// 時間推移の係数(0〜1)
let t;

// カラー係数
let cr, cg, cb;

if (now >= sunriseTime - 3600000 && now < sunriseTime) {
    // 夜〜日の出
    t = (now - sunriseTime) / 3600000 + 1;
    cr = .4 * (1 - t) + .8 * t;
    cg = .4 * (1 - t) + .9 * t;
    cb = .5 * (1 - t) + 1 * t;
} else if (now >= sunriseTime && now < sunriseTime + 3600000) {
    // 日の出〜昼
    t = (now - sunrise) / 3600000;
    cr = .8 * (1 - t) + 1 * t;
    cg = .9 * (1 - t) + 1 * t;
    cb = 1;
} else if (now >= sunriseTime + 3600000 && now < sunsetTime - 3600000) {
    // 昼
    cr = cg = cb = 1;
} else if (now >= sunsetTime - 3600000 && now < sunsetTime) {
    // 昼〜日の入
    t = (now - sunsetTime) / 3600000 + 1;
    cr = 1;
    cg = 1 * (1 - t) + .9 * t;
    cb = 1 * (1 - t) + .8 * t;
} else if (now >= sunsetTime && now < sunsetTime + 3600000) {
    // 日の入〜夜
    t = (now - sunsetTime) / 3600000;
    cr = 1 * (1 - t) + .4 * t;
    cg = .9 * (1 - t) + .4 * t;
    cb = .8 * (1 - t) + .5 * t;
} else {
    // 夜
    cr = cg = .4;
    cb = .5;
}

上のコードでは、先ほど求めた日の出・日の入時刻と現在時刻を比較して、状態により条件分岐しています。夜または昼であればカラー係数は固定ですが、日の出前後の1時間、日の入前後の1時間の期間は t を 0〜1 の間で変化する係数として、カラー係数に乗じることで滑らかにカラー係数が変化するようにしています。

for (const {id, key, r, g, b, a} of styleColors) {
    const prop = `rgba(${[r * cr, g * cg, b * cb, a].join(',')})`;
    map.setPaintProperty(id, key, prop);
});

最後に、冒頭で保存した各レイヤーの各プロパティの基準カラー情報からレイヤー ID、プロパティキー、カラー要素を取得し、RGB 要素にカラー係数を乗じて Paint プロパティに設定しています。

以上の処理を例えば1分毎に呼び出すことで、時間に応じて色合いが変わる地図が実現できます。

追記

・・・と、ここまで書いて気づいたのですが、Paint プロパティのカラー値には CSS カラーを表す文字列だけではなく、動的にスタイルを変えるための Expressions 用の配列やオブジェクトが入っている場合もあるため、実際にはもっと複雑なことをしていたのでした。

ご興味ある方は、初期カラー取得コードカラー更新コードあたりを見てください。

Mapbox GL JS のポップアップをアニメーション化

この記事は Mapbox Advent Calendar 2020 の13日目の記事です。

Mapbox のポップアップ(吹き出しで情報が表示されるやつ)って、実にシンプル、言い方を変えるとあまりに素っ気ないんです。こんな感じ。

f:id:nagixx:20201213020802j:plain

一応独自のクラスを指定したり、2日目の記事のように CSS をいじったりすると見た目を変えることはできるのですが、なんかこう、面白味がないんですよね。ポップアップなんだから、もう少し出てくる時にポップアップ感が欲しい。元気のあるやつ。

そこで Mapbox の Popup クラスを拡張して、アニメーションをつけた AnimatedPopup クラスを作ってみました。実際に動いているのを見てもらうのが分かりやすいので、サンプルをお見せします。どん!真ん中のマーカーをクリックすると、ポップアップが出たり消えたりします。

 

どうですか?生きのいいポップアップになりましたよね?

設置方法は非常に簡単です。まず、HTML ヘッダに次のタグを追加します。これは Mapbox GL 本体の後に読み込むようにしてくださいね。

<script src="https://cdn.jsdelivr.net/npm/mapbox-gl-animated-popup@latest"></script>

そして従来 Popup を new していたところを、

var popup = new mapboxgl.Popup();

AnimatedPopup で置き換える。これだけ。コンストラクタオプションは Popup と同じものを指定できます。

var popup = new AnimatedPopup();

ところで、「デフォルトのアニメーション、勢い良過ぎ!ポップアップアニメーションの動きや速さを変えられないの?」という人もいるかと思います。そのような方のために、アニメーションのパラメータは次のように指定できるようになっています。

var popup = new AnimatedPopup({
    openingAnimation: {
        duration: 1000,
        easing: 'easeOutElastic'
    },
    closingAnimation: {
        duration: 300,
        easing: 'easeInBack'
    }
});

上記はデフォルトの設定ですが、duration でアニメーションの持続時間を、easing でアニメーションのイージング関数を、オープン時とクローズ時のそれぞれで指定することができます。パラメータの選択は、次のページで動きを確認しながら確認することができますので、ぜひお使いください。

Mapbox GL JS Animated Popup

f:id:nagixx:20201213033020j:plain f:id:nagixx:20201213033055j:plain

それから GitHub のリンクも載せておきますので、実装に興味がある方はこちらもどうぞ。

最後に、このポップアップのアニメーション化は、元々 東京の公共交通のリアルタイム3Dマップ Mini Tokyo 3D のために作ったというのもあり、ぜひこちらのほうもお楽しみください。

Mini Tokyo 3D バージョン 2.6.0 リリース

Mini Tokyo 3D バージョン 2.6.0 がリリースされました (GitHub)。2.5.0 からの追加・修正機能を見ていきましょう。

経路検索にリアルタイムの遅延情報を反映

バージョン 2.5.0 で未実装になっていた、リアルタイムの遅延情報の経路検索への反映を行うようにしました。列車の遅れに応じた乗り換え案内をするようになっています。これはつい最近ジョルダンナビタイムが始めている、列車遅延を考慮したルート検索と同等の機能です。

経路検索の1クリック出発駅・到着駅選択

経路検索を行うには、出発駅および到着駅の駅名を(自動補完機能はあるものの)わざわざキーボードから入力する必要があったのですが、マップ上の駅をクリックすることで指定できるように改良しました。場所がわかっていればすばやく入力できますね。駅名入力欄に水色の枠が表示されており、そこにクリックした駅の名前が入ります。

レイヤー表示設定パネルの追加

降水アニメーションや花火など、地図の上に追加で表示する部分をレイヤーとして分離し、表示するレイヤーを選択できるようにしました。さらに、レイヤーをプラグインとして追加できるような構造にしたので、今後はAPIを整えて開発者が自由にレイヤーを組み込めるようにするつもりです。

アプリ情報表示の変更

アプリ情報ポップアップをモーダルダイアログで置き換えました。画面が小さいデバイスで表示が見やすくなったと思います。今後、ちょっとずつモバイルファーストのユーザーインターフェースに変えていく予定です。

ポップアップアニメーションの追加

列車、旅客機、駅のポップアップが開くとき・閉じるときのアニメーションを追加しました。また、駅出口のポップアップにも同様に弾力感のあるアニメーションをつけてみました。何度も開いたり閉じたりしたくなる動きになるよう調整しています。

このアニメーションを実装するために、Mapbox GL JS 用のプラグインとして Mapbox GL JS Animated Popup を公開し、それを組み込む形にしました。独立した外部モジュールにしたおかげでだいぶスッキリした実装になっています。

経路検索結果のルート上にポップアップ表示

経路検索の結果のルート上にも、出発駅、乗換駅、到着駅のポップアップを表示するようにしました。降車しなければいけない場所を地図上でわかりやすく示します。

京王・都営新宿線の10/30ダイヤ改正に対応

京王電鉄各線、都営新宿線の10/30ダイヤ改正に対応しました。新型コロナウイルスの流行で大きく変化した旅客需要に対応すべく、京王は京王ライナーの増便とともに深夜および平日夕方の時間帯で減便が行われています。

駅出口情報の拡充

りんかい線埼玉高速鉄道東葉高速鉄道の各駅、および京王新線西武有楽町線東急田園都市線東急目黒線つくばエクスプレスの地下駅、上野中央通り地下歩道の出口情報を追加しました。

Mini Tokyo 3D で学ぶ 〜 今日から君も東京交通エキスパート

小学生のつばさ君と、交通に詳しい謎の齧歯類風生物チュー太先生の会話を通じて、Mini Tokyo 3D の使い方をインタラクティブに紹介する子供向けコンテンツ。Google の検索結果に出てこないので、クロールされるようにここにリンクを貼っておきます。

Mini Tokyo 3D バージョン 2.5.0 リリース

Mini Tokyo 3D バージョン 2.5.0 がリリースされました (GitHub)。2.4.0 からの追加・修正機能を見ていきましょう。

経路検索機能が新登場

しばらく構想を温めていた、経路検索(乗り換え案内)機能を追加しました。ルートを3Dマップ上で見ながら複数の経路を見比べることができます。バージョン 2.5.0 現在、時刻の指定は出発時刻のみ、当日の列車のみ検索可能、リアルタイムの遅延は反映されない、という制限があります。初回リリースなので検索精度はまだまだですが、これから精度の向上とリアルタイム遅延の反映、ルートのオートパイロットなど面白い機能を拡充予定です。

経路検索機能のアルゴリズムはサーバ側で動作しており、1から手作りしています。既存の乗り換え案内サービスと遜色ないレベルまで達することが当面の目標です。

駅出口情報の拡充

都営浅草線三田線新宿線大江戸線各駅の出口情報を追加しました。また、Mapbox上で虎ノ門ヒルズ駅の地下通路マップが更新されていたので、これらの出口情報も追加。虎ノ門駅に新設された2b、B5、B6出口にも対応しました。

JR特急列車の追加

特急あずさ・かいじ・富士回遊・はちおうじ・おうめとして中央線を走るJR東日本E353系に対応。また、新宿発着の東武日光線鬼怒川線直通特急「日光」「きぬがわ」で運用されているJR東日本253系に対応。さらに、JR新宿駅東武鬼怒川温泉駅間を結ぶJR・東武直通特急スペーシアきぬがわ」に東武100系電車スペーシアの割り当ても行いました。

f:id:nagixx:20201017114719p:plain f:id:nagixx:20201016135804p:plain


列車時刻表の修正

東武東上線坂戸駅をすべての列車が通過していたのを修正。また、東京モノレールの土曜休日ダイヤについて、3月14日の駅名変更への対応が不十分で、羽田空港第3ターミナル駅付近で列車が表示されていなかった問題を修正。

Mini Tokyo 3D バージョン 2.4.0 リリース

Mini Tokyo 3D バージョン 2.4.0 がリリースされました (GitHub)。2.3.0 からの追加・修正機能を見ていきましょう。

JR成田線我孫子支線が全線開通

成田線我孫子支線我孫子〜成田間全線開通しました。常磐線快速列車の一部が我孫子から成田まで乗り入れています。単線なので途中の駅で列車交換(対向列車の待ち合わせ)を行いますが、成田線我孫子支線では全駅で列車交換が可能です。

駅名検索機能の改善

駅名検索フィールドのオートコンプリートの候補リストにおけるローマ字の長音記号(例: Ō)や副駅名(例: 押上〈スカイツリー前〉)などの表記の揺れを吸収し、さらに同名表記で位置の異なる駅(例: 小川町 (東京)と小川町 (埼玉))を区別するようにしました。

再生モードでの旅客機の表示

これまで再生モードに対応していたのは列車の運行だけでしたが、羽田・成田発着旅客機の運行にも対応しました。ただし、当日発着便に限ります。任意の時刻・再生スピードで旅客機の動きを見ることができます。

下の動画は羽田空港周辺の100倍速再生の様子ですが、7時台は羽田から離陸する分が多いものの、8時台になると各地を早朝に離陸した便が集まってくるのがわかります。

駅出口情報の拡充

銀座線に加えて、丸の内線、日比谷線東西線、千代田線、有楽町線半蔵門線南北線副都心線各駅の出口情報を追加しました。

高尾発着成田エクスプレス、八王子発着むさしの号、千代田線乗り入れ小田急ロマンスカーに対応

高尾発着成田エクスプレスに対応。なぜか東京公共交通オープンデータチャレンジ提供データには中央線を走る特急列車のデータが含まれていないので、手動で入力しました。

また、武蔵野線経由で大宮と八王子を結ぶむさしの号も、国立〜八王子間が表示されていませんでしたが、全線運転を開始しました。

さらに、小田急ロマンスカーのうち、千代田線に乗り入れるメトロはこね・メトロモーニングウェイ・メトロホームウェイの表参道〜成城学園前新百合ヶ丘・町田間が表示されていなかった問題を解消しました。

可視領域の厳密な計算による描画性能向上

画面に表示されない列車や旅客機などの3Dオブジェクトは毎フレーム位置計算を行う必要はなく、計算を省略することでパフォーマンスを最適化することができます。これまではマップの可視領域を含む緯線・経線に沿った矩形を使い、3Dオブジェクト位置計算の必要性を簡易的に判定していましたが、厳密な可視領域の台形を使うように切り替えました。実際にはスムーズなアニメーションのために若干のバッファ領域も入れています。これにより、10%程度描画性能が向上しました。

f:id:nagixx:20200911174046p:plain

JR特急列車の追加

房総方面の特急しおさいわかしお・さざなみで使用される255系、E257系500番台に対応。

これに合わせて、総武快速線の列車のうち千葉以東で成田線総武本線に乗り入れる快速列車の一部が表示されなかったり、列車番号が切り替わっていなかった問題を修正しました。また、佐倉駅での連結・切り離しを行う列車にも対応しました。

モード取得・変更 API、列車・旅客機追跡 API の追加

バージョン 2.4.0 では、モードの取得・変更を行うメソッド、追跡する列車 ID・旅客機 ID の取得・設定を行うメソッドが追加されました。

メソッド 説明
getClockMode 現在のクロックモードを返す
getSelection 追跡中の列車またはフライトの ID を返す
getTrackingMode 現在の追跡モードを返す
getViewMode 現在のビューモードを返す
setClockMode クロックモードを設定する
setSelection 追跡する列車またはフライトの ID を設定する
setTrackingMode 追跡モードを設定する
setViewMode ビューモードを設定する

詳細は Mini Tokyo 3D 開発者ガイドをご覧ください。