7. 現在位置を取得できる拡張機能を作ろう

← 目次に戻る

< 6. Scratch 3のオリジナルブロックを作ってみよう 8. 音声を認識し、文章に変換する拡張機能を作ろう >

前章で作った拡張機能はアラートダイアログにメッセージを表示するという簡単なものでしたが、もう少し役に立ちそうなものを作ってみましょう。

この章では、PCであればWi-FiやIPアドレスから、スマートフォンであればGPSから現在位置の位置情報(緯度・経度)を取得する拡張機能を作ります。

GPSとScratchをつなげるということで英語の「GPS to Scratch」のtoと2(two)の音が一緒ということで、今回作る拡張機能の名前はGPS2Scratchにします。

拡張機能をつくる手順は前章のHello拡張機能のときとほとんど同じです。

まず、node_modules以下にあるscratch-vm以下、src/extensionsの下にscratch3_gps2scratchというフォルダを作成し、その下にindex.jsというファイルを新しく作ります。

index.jsの内容は以下の通りです。

const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Cast = require('../../util/cast');

class Scratch3GPS2Scratch {
    constructor (runtime) {
        this.runtime = runtime;
        this.lat = '';
        this.lng = '';
    }

    getInfo () {
        return {
            id: 'gps2scratch',
            name: 'GPS2Scratch',
            blocks: [
                {
                    opcode: 'getCurrentPosition',
                    blockType: BlockType.COMMAND,
                    text: '位置情報取得',
                },
                {
                    opcode: 'getLat',
                    blockType: BlockType.REPORTER,
                    text: '緯度'
                },
                {
                    opcode: 'getLng',
                    blockType: BlockType.REPORTER,
                    text: '経度'
                }
            ],
            menus: {
            }
        };
    }

    getCurrentPosition () {
        if( navigator.geolocation ) {
            let self = this;
            navigator.geolocation.getCurrentPosition(
                function(position) {
                    self.lat = position.coords.latitude;
                    self.lng = position.coords.longitude;
                },
                function(error) {
                    alert(error);
                }
              );
        } else {
            alert('あなたの端末では、現在位置を取得できません。');
        }
    }

    getLat () {
        return this.lat;
    }

    getLng () {
        return this.lng;
    }
}

module.exports = Scratch3GPS2Scratch;

Hello拡張機能のときと比べて少し長いコードです。主要な箇所を説明していきます。

constructor (runtime) {
    this.runtime = runtime;
    this.lat = '';
    this.lng = '';
}

constructor関数は、最初に一度だけ実行される関数です。この中で、あとで利用することになる緯度を表す変数this.lat、経度を表す変数this.lngの初期化をおこなっています。両方ともに空の文字列''をセットします。このthisは何?という説明は、それだけでかなりの分量になってしまうので、ここでは割愛します。詳しく知りたい場合は「this javascript」でヒットする中から自分にとってわかりやすい情報を参照してください。

getInfoでは追加するブロックの定義をおこなっています。

「位置情報取得」のブロックはHello拡張機能でもでてきたCOMMANDタイプのブロックで、クリックするとopcodeで定義されたgetCurrentPosition関数が実行されます。

{
    opcode: 'getCurrentPosition',
    blockType: BlockType.COMMAND,
    text: '位置情報取得',
},

getCurrentPosition関数をみると、navigator.geolocationの値によって処理を分岐しています。

getCurrentPosition () {
    if( navigator.geolocation ) {
        let self = this;
        navigator.geolocation.getCurrentPosition(
            function(position) {
                self.lat = position.coords.latitude;
                self.lng = position.coords.longitude;
            },
            function(error) {
                alert(error);
            }
          );
    } else {
        alert('あなたの端末では、現在位置を取得できません。');
    }
}

navigator.geolocationがfalseの場合は、現在位置を取得できないので、エラーメッセージを表示しています。

trueの場合はnavigator.geolocation.getCurrentPositionを実行して、正常に実行された場合はself.latとself.lngにそれぞれ緯度(position.coords.latitude)と経度(position.coords.longitude)の値をセットしています。

function(position)の中ではthisが変化してしまうので、その前に、

let self = this;

であらかじめselfにthisをセットして、this.latやthis.lngに値をセットする代わりにself.latやself.lngに値をセットしています。

「位置情報取得」「緯度」「経度」の3つのブロックを定義していますが、このうち「緯度」と「経度」はREPORTERという新しいタイプのブロックです。

{
    opcode: 'getLat',
    blockType: BlockType.REPORTER,
    text: '緯度'
},
{
    opcode: 'getLng',
    blockType: BlockType.REPORTER,
    text: '経度'
}

レポーターブロック(Reporter Block)は、数値や文字列を値として持っているブロックで、動きのカテゴリであれば、「x座標」「y座標」「向き」のようにチェックボックスがついっている形の丸いブロックがこのタイプのブロックです。

「緯度」、「経度」のブロックをクリックした場合の処理は、

getLat () {
    return this.lat;
}

getLng () {
    return this.lng;
}

で、それぞれthis.latやthis.lngが保持している値を返しています。

次にsrc/extension-support/extension-manager.jsを編集して、scratch3_gps2scratchを追加します。

次にscratch-guiのコードを修正し、GPS2Scratch拡張機能を「拡張機能」のメニューに追加して選べるようにします。scratch-gui以下のsrc/lib/libraries/extensions/index.jsxの最後に以下を追加します。

{
  name: 'GPS2Scratch',
  extensionId: 'gps2scratch',
  iconURL: gps2scratchImage,
  insetIconURL: gps2scratchInsetImage,
  featured: true,
},

index.jsxの最初の方には、以下のように画像のパスもimportしておきます。

import gps2scratchImage from './gps2scratch.png';
import gps2scratchInsetImage from './gps2scratch-small.png';

gps2scratch.png[サンプル]とgps2scratch-small.png[サンプル]はsrc/lib/libraries/extensionsフォルダに置いておきます。

ターミナルより

npm start

を実行して、Scratch3を起動します。

ブラウザで http://localhost:8601/ にアクセスし、GPS2Scratch拡張機能を追加します。

「位置情報取得」ブロックをクリックすると現在の位置情報を取得し、緯度と経度のそれぞれの値は「緯度」「経度」ブロックをクリックすれば得られるようになります。

新幹線の速度を測ってみる(2019/05/07 追記)

京都に行く機会があったので、GPS2Scratchを使ってやってみたかった、「新幹線の速度を測ってみる」をやってみました。

一分間に進んだ距離がわかれば、それを60倍すれば一時間あたり進む距離、つまり時速を求めることができます。

測定開始のときの緯度と経度、そしてそこから一分後の緯度と経度がわかれば、一分間にどのくらい緯度と経度が変わったかがわかります。緯度、経度それぞれ1度あたりの長さがわかれば、緯度/経度の差から距離がわかるのですが、注意しなくてはいけないのが、緯度1度あたりの長さは地球上どこでも一緒で、110.9463Km なのですが、経度1度あたりの長さは緯度が高くなるごとに短くなっていくということです。(参考に挙げた緯度経度より 2点間の距離を計算|有限会社 エス技研に掲載されている図をみるとわかると思います)

京都までに行くときに乗ったのは東海道新幹線、だいたい東京駅と同じ緯度上を進んでいるんじゃないかと推測して、東京駅と同じ緯度上の経度1度あたりの長さ 90.4219Km で計算することにしました。

進んだ緯度と経度の距離がわかれば、三平方の定理を使って、斜めに進んでいる場合の距離を求めることができます。

これらを元に緯度経度を測定して距離を求めるScratchのプログラムを書くとこんな感じになります。ネコに測定した結果を言わせています。

GPSで位置を測定するために、改造版Scratch 3をiPhoneで動かしました。

品川 - 京都間でプログラムを作成し、何度か測定してやっとそれらしい結果が出たのは、京都に到着しようとして減速し始めた頃でした。

GPS2Scratch拡張機能を追加した完成版は https://scratch3.netlify.app/ にて公開しています。

参考

< 6. Scratch 3のオリジナルブロックを作ってみよう 8. 音声を認識し、文章に変換する拡張機能を作ろう >

← 目次に戻る