5
3

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.

Electron / Leaflet / GTFS-RealTimeでシミュレーションデスクトップアプリを作ろう

Last updated at Posted at 2021-04-30

概要

学校の研究でバスのシミュレーションソフトを作ることとなったのでその作り方をまとめようと思います。

ソースコードはこちら

2023/08/26追記
記事にはしていませんが、静的GTFSをアップロードできるようにしています。
詳しくはソースコードを参照してください。

Electronについて

こちらの記事が分かりやすかったです
ようこそ!Electron入門

開発環境

ライブラリ等 バージョン
node.js 14.16.1
npm 6.14.12
Electron 12.0.5
leaflet 1.7.1
gtfs-realtime-bindings 0.0.6
sqlite3 5.0.2
request 2.88.2
Bootstrap 4.3.1

Electronの導入

プロジェクトの作成

まずはアプリ用のフォルダを作成して、その中で以下を実行してください。

npm init -y

インストール

必要なライブラリをインストールします

npm install electron --save

アプリの作成

まずsrcフォルダを作成します。
srcフォルダの中に以下のファイルを作成してください。

  • index.html
  • main.js
  • style.css
  • package.json

中身はこのようにしてください。

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MY ELECTRON</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <p>Hello World</p>
</body>
</html>
main.js
const { app, BrowserWindow } = require('electron')

const createWindow = () => {
    const mainWindow = new BrowserWindow(
        {
            width: 2000,
            height: 1000
        }
    )

    // 開発ツールを有効化
    mainWindow.webContents.openDevTools({ mode: "detach" });

    mainWindow.loadFile('index.html')
}

app.whenReady().then(() => {
    createWindow()
    app.on(
        'activate',
        () => !BrowserWindow.getAllWindows().length && createWindow()
    )
})

app.on(
    'window-all-closed',
    () => process.platform !== 'darwin' && app.quit()
)
package.json
{
    "main": "main.js"
}

実行してみよう

ここまで入力したら以下を実行してみましょう

npx electron src

実行するとこのような画面が現れると思います。
img1.PNG

Leafletで地図を表示しよう

インストール

必要なライブラリをインストールします。

npm install leaflet --save

アプリの作成

srcフォルダ内にpreload.jsを作成します。
中身を以下のようにします。

preload.js
window.onload = () => {
    global.L = require('leaflet');

    global.map = L.map('mapid').setView([34.6650016, 133.9316551], 14);

    L.tileLayer(
        'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
    ).addTo(map)
}

今作成したpreload.jsを読み込むためmain.jsのように変更します。

main.js
const createWindow = () => {
    const mainWindow = new BrowserWindow(
        {
            width: 2000,
            height: 1000,
            // ↓追加 ///////////////////////
            webPreferences: {
                preload: __dirname + '/preload.js'
            }
            ///////////////////////////////
        }
    )

    // 開発ツールを有効化
    mainWindow.webContents.openDevTools({ mode: "detach" });

    mainWindow.loadFile('index.html')
}

マップを表示する箇所に以下を追加します。

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MY ELECTRON</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <!----- ↓追加 -------->
    <div id="mapid"></div>
    <!------------------->
</body>


</html>

style.cssに以下を追加してください。
htmlとbodyと#mapidをheight: 100%にしていますが、このようにしないと地図が表示されません。
(htmlとbodyにheight: 100%が必要かは分かりません)

style.css
html, body, #mapid {
    height: 100%;
}

body {
    margin: 0;
}

実行してみよう

ここまで入力したら以下を実行してみましょう

npx electron src

実行すると私の環境ではこのようになってしまいました。
img2.PNG

なのでindex.htmlheadに以下を追加しました。

index.html
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
    integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
    crossorigin="" />
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
    integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
    crossorigin=""></script>

追加してから、再度実行してください。
するとこのように表示されました。
img3.PNG

GTFS-RealTimeの位置情報を表示する

GTFS-RealTimeとは

こちらに書いてあります。

GTFS リアルタイムは、公共交通機関が運行車両に関するリアルタイムの最新情報をデベロッパーに提供できるようにするためのフィードの仕様です。

インストール

必要なライブラリをインストールします。

npm install gtfs-realtime-bindings --save
npm install request --save

アプリの作成

まずGTFS-RealTimeのデータを取得します。
GTFS-RealTimeのデータの取り方はこちらに書いてあります。
今回は宇野バス様のリアルタイムデータを使わせていただきます。
preload.jsを以下のように変更します。

preload.js
// ↓追加
var GtfsRealtimeBindings = require('gtfs-realtime-bindings');
var request = require('request');
var realtime_markers = []

window.onload = () => {
    global.L = require('leaflet');

    global.map = L.map('mapid').setView([34.6650016, 133.9316551], 14);

    L.tileLayer(
        'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
    ).addTo(map)

    show_gtfs_realtime()
}

// ↓追加
function show_gtfs_realtime() {
    const blueMarker = { icon: L.divIcon({ className: 'blue marker', iconSize: [10, 10] }) }
    var requestSettings = {
        method: 'GET',
        url: 'http://www3.unobus.co.jp/GTFS/GTFS_RT-VP.bin',
        encoding: null
    };
    request(requestSettings, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var feed = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(body);
            feed.entity.forEach(function (entity) {
                if (entity.vehicle) {
                    realtime_marker = L.marker([entity.vehicle.position.latitude, entity.vehicle.position.longitude], blueMarker).addTo(map).bindPopup("BUS")
                    realtime_markers.push(realtime_marker)
                }
            });
        }
    });
}

style.cssにも以下を追加します

style.css
.marker {
    text-align      : center;
    color           : white;
    font-size       : 16;
    border-radius   : 8px;
    box-shadow      : 8px 8px 8px rgba( 0, 0, 0, 0.4 );
}

.blue {
    background      : blue;
}

実行してみよう

ここまで入力したら以下を実行してみましょう

npx electron src

青い丸が表示されたと思います。
これはバスの現在地を表しています
img4.PNG

GTFS-RealTime データ更新

宇野バス様のデータは約15秒ごとに更新されるようなのでデータ更新が行えるようにします
ipc通信というものを使用します

ipc通信について

こちらに詳しく書かれてあります
Electron(v.12.0.0 現在)の IPC 通信入門 - よりセキュアな方法への変遷

アプリの作成

srcフォルダ内にbus_simulation.jsを追加して、index.htmlで読み込めるようにしてください。
bodyの一番下に書いてください。

bus_simulation.jsの中身は以下のようにしてください。

bus_simulation.js
window.onload = function () {
    setInterval(update_gtfs_realtime, 15000);
}

function update_gtfs_realtime() {
    window.api.send("update_marker");
    console.log("update")
}

preload.jsに以下のコードを追加してください。

preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld(
    "api", {
    //rendererからの送信用//
    send: (channel, data) => {
        if (channel == "update_marker") {
            update_gtfs_realtime()
        }
        else {
            ipcRenderer.send(channel, data);
        }
    },
    //rendererでの受信用, funcはコールバック関数//
    on: (channel, func) => {
        ipcRenderer.on(channel, (event, ...args) => func(...args));
    }
});

function update_gtfs_realtime() {
    for (let i = 0; i < realtime_markers.length; i++) {
        map.removeLayer(realtime_markers[i])
    }
    realtime_markers = []
    show_gtfs_realtime()
}

実行してみよう

ここまで入力したら以下を実行してみましょう

npx electron src

15秒おきに青ピンが動いているのが確認できましたか?
Consoleにもupdateと見えていれば成功だと思います。
確認作業は昼間行うと確認しやすいです。
img5.PNG

さいごに

今回記載したやり方は私なりのやり方です。もしほかに良い方法があれば教えていただければ幸いです。
こちらはまだ開発中なのでこれからも追記していきたいと思います。
以上読んでいただきありがとうございます。

参考文献

ようこそ!Electron入門
日頃お世話になっているElectronのアプリ開発に入門してみる
Electron で地図を表示する無料で最短の道
JavaScript / Node.js サンプル | リアルタイム乗換案内データ | Google Developers
Electron(v.12.0.0 現在)の IPC 通信入門 - よりセキュアな方法への変遷

5
3
8

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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?