プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

マッピングパーティを開催しました

先日(といっても一か月以上前になりますが)9/29(土)に『伊勢河崎でマッピングパーティ』と題して、三重県伊勢市の河崎地区を舞台にマッピングパーティを開催しました。 せっかく開催したイベントなので、当日を振り返りながら、あれこれとメモをまとめておきます。

connpass.com

オリエンテーション

当日は、ファシリテータとして @kudarisenmon さんをお迎えして、小学生を含む8名の方が参加しました。

ほとんどの方が初めて OpenStreetMap に触れるという事情もあり、最初にファシリテータの方から OpenStreetMap とは何かという説明からしていただきました。 続いて、マッピングパーティについてのお話や、各地の事例、イベント当日の流れなどの説明を頂きました。

f:id:junichim:20171029235045j:plain

フィールドワーク

オリエンテーションのあとは、実際に外に出て、フィールドワークです。実は、今回のイベントを企画するまではフィールドワークといっても漠然と単に歩くだけだと思ってましたが、結構準備することも多かったので、簡単にまとめておきます。

準備編

一応、フィールドワーク前に、事前に実際に歩いて、どのあたりをイベントで歩くと面白そうかな?という事前ロケをしています。これを元に、当日歩くルートを決めました。

実際に作成したルートは下記になります。

フルスクリーン表示

uMapというサービスを使ってます。

フィールドワークのやり方もいろいろあるようです。

  • 紙の地図を使って、自分たちで歩いて、気になるところをメモ
  • Mapillary などのアプリを使い記録

Mapillary はスマホを持って歩くと、一定秒数ごとに写真を撮ってくれます。それをあとからアップロードすると撮影時の位置情報を元に、地図と写真を紐づけてくれるようなサービスです。 例えば、今回の河崎付近だと、こんな感じで見ることができます。

今回は前者の紙でメモしながらのマッピングをメインにしました(当日は、 Mapillary に挑戦した方もいらっしゃいましたよ)。 紙の地図も参加者に渡す必要があるので、事前に印刷しておきます。OpenStreetMapだと印刷できるんですよ!印刷が! 今回は、FieldPaper を使って河崎近辺を印刷しました。

当日編

さて、準備の話はこれぐらいにして、実際のフィールドワークの際にメモする気になるところは人によってまちまちですが、例えば、

  • お店などの情報
  • 郵便ポスト
  • バス停
  • 石碑
  • 神社
  • 駐車場
  • 説明版
  • その他いろいろ

などなど、何でもOKです。ただ、個人の住居などは除いておきます。また、メモと一緒に、あとからマッピングする際の参考になるよう、写真も撮っていきます。

自分のメモした紙はこんな感じになってました。 f:id:junichim:20171107003202j:plain

他人が見たらわかんないですねw 自分も今見たらわかんないですwww

あれこれ見ながら歩いていると、予定の2時間があっという間に迫ってきました。 最後のほうは少し駆け足になってしまいましたが、なんとか調査を終えて、集合場所の会場に戻りました。

マッピング

さて、いよいよマッピングですが、その前に差し入れのお菓子とお茶を食べて飲みつつ一休憩です(差し入れありがとうございました!)。

参加者の方には事前に OpenStreetMapのユーザー登録を済ませておいてもらいましました。 PCを立ち上げて、ログインして、早速編集開始です。

マッピングでは、先ほどフィールドワークで調べたメモを元に、 OpenStreetMap に情報を入力していきます。 情報の入力、といってもピンとこないかもしれませんが、OpenStreetMap では地図上のすべてのモノが、個別の図形(四角形や多角形、線やそれらがつながったもの)として表現されています。 OpenStreetMap にログインして、編集モードに入ると、これらの図形を操作することができます。

f:id:junichim:20171107004526p:plain

こんな感じです。後ろの画像(航空写真)のうえに、白い線や、赤い半透明の四角形などがあると思いますが、これらが前述の図形になります。

マッピングでは、新たにこのような図形自体を登録したり、既存の図形にお店の名前や営業時間などの情報を追加したりしていきます。

具体的な入力方法については、@kudarisenmon さんや経験者の方から手ほどきを受けつつ、進めました。 もし、興味を持たれた方がいましたら、iDエディタ などを参考にトライしてみてください!

当日は実際にマッピングを行う時間が足りなくなったこともあり、それぞれが何点かマッピングするところまでで時間切れとなりました。

成果?

イベントだけの成果というわけではありませんが、後日、イベント前後のマップを比べると、

マッピングパーティ前 f:id:junichim:20171026153659p:plain

マッピングパーティ後 f:id:junichim:20171029234237p:plain

と一目でわかるように、ずいぶんと情報が充実しました。

伊勢だとメジャーな場所もまだまだ空白のままというところも多いので、この調子でどんどんと情報が充実していくといいですね。

さいごに

自分自身が今までマッピングパーティに参加したことがなかったので、イベントの主催をやりながら参加もするという立場でした。 経験がないということで、いろいろと不安もありましたが、いざ当日を迎えてやってみると、予想以上に楽しかったです。

イベントの相談・企画・広報・開催など、多くの方々にご協力頂き、ありがとうございました。

次は違う内容(違う場所を対象)にして、また開催したいと思います。

ところで、地方だと同じ活動をしている人たちがどれぐらいいるのか気になりませんか?ひょっとして自分だけだったら寂しいなー、とふと思うときがあります。 OpenStreetMap だと、自分と同じ地域て活動しているユーザーについて教えてくれるTwitterのbotがあるんですね!

twitter.com

これを知ってから、地方にもマッパーがいることがわかり、いろいろと心強いです。

なかなか万人受けしないかもしれませんが、少しでもマッピングパーティが広がるといいかなと思っています。

Cognito ユーザープールを単独で API GateWay と共に使う

前に書いた記事

Cognito ユーザープール使ってみました - プログラマーのメモ書き

では、Cognito ユーザープールを Cognito Identity Pool (Federated Identity) と一緒に使うようなことを書きました。

でも、よくよく考えてみると外部IDプロバイダで認証したIDを使う必要がないなら、Cognito ユーザープールのみを使って、AWSのサービスへは API Gateway 経由でアクセスするのが手っ取り早いのではないかと思い立ちました。

そこで、今回は、Cognito ユーザープールを単独で用いて、ユーザー認証後 API Gateway 経由で Lambda 関数を呼び出す方法を試してみました。 以下は、作業時のメモになります。

なお、Cognito ユーザープールの利用については前回の記事をベースにしているので、詳しくはそちらをご覧ください。

ユーザープールの作成~ログインまで

この部分は、前回の記事と同じなので、割愛します。 クライアント側のログイン処理とマイページの処理が少し変わるので、あとで説明したいと思います。

Lambda 関数の定義

ごく簡単な内容の Lambda関数を定義しておきます(Node.js で定義しています)。 処理内容は以下の通りです。

exports.handler = (event, context, callback) => {
    // TODO implement
    context.done(null, {message: "Hello API:" + event['email']});
};

event['email'] の部分ですが、次節で述べるように API Gateway で認証した情報を eventとして渡すことができ、それを出力しています。

API Gateway の定義

次に API Gateway を定義します。

  1. APIの作成を押します f:id:junichim:20170924152442p:plain
  2. API 名を適当につけます。 f:id:junichim:20170924152600p:plain
  3. 追加したAPIの『リソース』の『アクション』より『メソッドの作成』を選択し、GET を追加します。 f:id:junichim:20170924152811p:plain
  4. 『統合タイプ』でLambda関数を選択し、『Lambdaリージョン』で先ほどLamda関数を作成したリージョンを選択し、Lambda関数名も入力して、保存を押します。 f:id:junichim:20170924153045p:plain
  5. 『Lambda関数に権限を追加する』という確認ダイアログが出てくるので、OKを押します f:id:junichim:20170924153142p:plain
  6. 『アクション』より『CORSの有効化』を選択します
  7. 特に設定を変更する必要がなければデフォルト設定のまま『CORSを有効にして既存のCORSヘッダーを置換』ボタンを押します f:id:junichim:20170924153427p:plain
  8. 『アクション』より『APIのデプロイ』を選択して、この時点で一度デプロイしておきます。デプロイされるステージが未定義の場合は『新しいステージ』を選択し、ステージ名を適当に入れます(ここではprodとしました) f:id:junichim:20170924153704p:plain なお、 ステージエディターが立ち上がりますが、今回は特に設定を変更しません。

これで、 API Gateway が作成できました。

オーソライザー

次に、作成した API Gateway に対して、Cognito ユーザープールを使って、認可(認証に基づく認可)を得るために、オーソライザーを定義します。

  1. 作成したAPIを選択し、『オーソライザー』を選択します f:id:junichim:20170924154227p:plain
  2. 『新しいオーソライザーの作成』ボタンを押します
  3. 『名前』を適当に入力し、『タイプ』として『Cognito』を選択します f:id:junichim:20170924154249p:plain
  4. 『Cognitoユーザープール』として、作成済みのユーザープール名を入力します
  5. 『トークンのソース』として『Authorization』を入力します
  6. 『作成』をクリックします

これでオーソライザーが作成できました

メソッドの認可の設定

作成したオーソライザーを認可に使うため、メソッドの設定を変更します

  1. 『リソース』のGETメソッドを選択します f:id:junichim:20170924154834p:plain
  2. 『メソッドリクエスト』をクリックして、編集画面を開きます
  3. 『認証』(英文だと、Authorizationなので、本当は認可?)の編集アイコン(鉛筆のようなやつ)をクリックして、ドロップダウンリストより、作成したオーソライザーを選択します f:id:junichim:20170924154857p:plain なお、このとき、オーソライザーを作成済みにも関わらず、ドロップダウンリストに現れない場合は、ページをリロードすると表示されるようになりました(もっといい方法があれば教えてください)

本文マッピングテンプレート

最後に、Lambda関数を呼び出したユーザー(API Gatewayを呼び出したユーザー)の情報を伝えるための設定を行います

  1. 『リソース』でGETメソッドを選択します
  2. 『統合リクエスト』をクリックして編集状態にします f:id:junichim:20170924200946p:plain
  3. 『本文マッピングテンプレート』をクリックします f:id:junichim:20170924201108p:plain
  4. 『マッピングテンプレートの追加』をクリックして、Content-Typeとして application/json を入力します f:id:junichim:20170924201226p:plain
  5. パススルー動作の変更を行う旨の確認ダイアログが表示されるので、デフォルトの『はい、この統合を保護します』を選択します f:id:junichim:20170924201345p:plain
  6. テンプレートを定義します(内容は下記参照)
  7. 最後に『保存』ボタンを押せば、完了です

テンプレートの内容は、下記としました。

{
  "email": "$context.authorizer.claims.email",
  "sub" : "$context.authorizer.claims.sub"
}

テンプレートに指定できる項目については、

API Gateway マッピングテンプレートとアクセスのログ記録の変数リファレンス - Amazon API Gateway

などを参照してください。

なお、本文マッピングテンプレートの『リクエスト本文のパススルー』オプションに関するの詳しい説明については、下記記事がわかりやすかったです。

dev.classmethod.jp

クライアント側の処理

前回記事のクライアント側の処理と大きくは変わらないのですが、ログイン画面で承認された後の動作を若干変更し、マイページにAPI Gateway 呼び出し用のボタンと結果の表示を行い処理を追加します。

ログイン

今回は、 Cognito ユーザープール単独で用いる(Cognito Identity Pool を用いない)ので、ログイン後、Identity Pool と統合する処理が不要になります。 具体的には、

upsample.login = function() {
    var username = $('#inputUserName').val();
    var password = $('#inputPassword').val();
    if (!username | !password) { return false; }

    var authenticationData = {
        Username: username,
        Password: password
    };
    var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);

    var userData = {
        Username: username,
        Pool: upsample.UserPool
    };

    var message_text;
    var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
    cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: function(result) {
            console.log('access token + ' + result.getAccessToken().getJwtToken());

            $(location).attr('href', 'mypage.html');
        },

        onFailure: function(err) {
            alert(err);
        }
    });

}

となります。 また、htmlファイルにおいても、AWS SDK を読み込む必要がなくなっています。

マイページでのAPI Gateway の呼び出し

マイページのhtmlファイルで API Gateway を呼び出すためのボタンを追加しておきます。

ボタンが押された際に、API Gateway へアクセスして、その結果を表示します。

upsample.apiCalling = function() {

    var cognitoUser = upsample.UserPool.getCurrentUser();
    if (cognitoUser != null) {
        cognitoUser.getSession(function (err, sessionResult) {
            if (sessionResult) {
                var idToken = sessionResult.getIdToken().getJwtToken();

                $.ajax(
                    "https://API Gateway のURL",
                    {
                        type: 'GET',
                        contentType: 'application/json',
                        headers: {
                            Authorization: idToken
                        },
                        async: false,
                        cache: false
                    }
                )
                .done(function(data) {
                    $('#api_result').text('result: ' + JSON.stringify(data));
                })
                .fail(function() {
                    console.log("failed to call api");
                });
            }
        });
    }
}

呼び出し時は、 Authorization ヘッダーに Idトークンを指定すればOKです。 認証が切れた状態だと、ステータスコード 401 でレスポンスが返ってきます。

実験

では、実際にブラウザからアクセスしてみます。 ログイン画面を表示後、ログインするとマイページが表示されます。

f:id:junichim:20170924204848p:plain

ここで、『API実行』ボタンを押すと、 API Gateway 経由で Lambda が呼ばれ、ログイン時のユーザーのメールアドレスを含んだメッセージが返されます。

f:id:junichim:20170924204752p:plain

問題なく表示されていますね。

コンソールから試し

必要というわけでもないのですが、curlでも試してみます。

ログインを行っていない状態で

curl --include https://xxxx.execute-api.ap-northeast-1.amazonaws.com/ステージ名

とすると 401 が返ってきます。

次にブラウザでログイン後、ディベロッパーツールでIdトークンの値を取得して、curlで呼び出すと

curl --include https://xxxx.execute-api.ap-northeast-1.amazonaws.com/ステージ名 -H 'Authorization: Idトークンの値'

問題がなければ、

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 23
Connection: keep-alive
Date: Sat, 23 Sep 2017 14:04:50 GMT
x-amzn-RequestId: 29d9022d-a068-11e7-a3a1-ad50f52c7d3e
Access-Control-Allow-Origin: *
X-Amzn-Trace-Id: sampled=0;root=1-59c66a01-cdeca4ab25f31bc912c89815
X-Cache: Miss from cloudfront
Via: 1.1 e20489ede5d5a153bd489790cc8e71ab.cloudfront.net (CloudFront)
X-Amz-Cf-Id: awZjLU21X6F0SBrmnSQlezZPMap4Mxv2Arvoxg6TLYy5yLVFfggsbA==

{"message":"Hello API:test@example.com"}

のようなヘッダが返ってきます。 やはり、問題なさそうですね。

はまったところ

実は、最初、 API Gateway のドキュメント

Amazon Cognito ユーザープールをオーソライザーとして使用して REST API へのアクセスを制御する - Amazon API Gateway

を参考にして、API Gateway のオーソライザーの設定でトークンのソースとして

method.request.header.Authorization

としていたのですが、このままでは 401 が返ってきてAPI Gateway にアクセスできません。

単に、

Authorization

のみでよかったのでした。 でも、これがわかるまで、半日ぐらいかかってしまったので、一応書いておきます。

参考

以下の記事を参考にさせていただきました。

Cognito User Poolsの機能と使い所 - たれぱんのびぼーろく

http://www.h4a.jp/detail/25148

API GatewayでCognito UserPools Authorizerを使う - Qiita

AWS Cognito の認証情報を API Gateway + Lambda で受け取りたい - Qiita

また、今回の一連のソースは Github にアップしてあるので、気になる方はご参考までどうぞ。

github.com

ディズニーのくるくる回るおもちゃの電池交換

ずいぶん前に子供をディズニーのイベントに連れてってた時に、欲しい!と言われて買った、くるくる光って回るおもちゃですが、電池が切れました。

f:id:junichim:20170923220721j:plain

これ、裏側のフタを空ければ電池交換できるんですが、これが開けにくいのってなんの。

必要なもの

  • マイナスドライバー(少し大きめのほうがいいかも)
  • 交換用電池(単4 3本)

開け方

  1. 電池フタの下部の隙間にマイナスドライバーを入れます。 f:id:junichim:20170924145346j:plain
  2. ドライバーを隙間の奥に押し込みながら、おもちゃの端(黒いところ)側を支点にして、てこの要領で少し前側(電池が入っている部分側)に押します
  3. 力をよしなに調整すると、フタが、パカッと取れます

力のかけ具合が難しいところですが、少しづつ力を入れていくとうまくできると思います。 (動画撮ればよかったですね、すいません)

ちなみに電池ケース上部にある、ネジは無関係のようで、外さなくてもフタ取れます。

フタがとれた後はこんな感じです。

f:id:junichim:20170923221135j:plain

フタ(表)

f:id:junichim:20170923221157j:plain

フタ(裏)

f:id:junichim:20170923221213j:plain

フタの画像を見ると分かるように、フタの下側(画像で下側)部分のツメで引っ掛けている感じです。

取り付けるときも逆の要領でつけます(ドライバーを使ったほうがスムーズに取り付けられると思います)。

最悪、ツメが折れたりしても責任取れませんので、自己責任でお願いします。

小さいお子さんのいる方、怪我しないように、頑張って電池交換にチャレンジしてみてください。

参考

hirokiman.way-nifty.com