まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:transform

OpenStreetMap とその API を使って簡易ドライブゲームを作って公開しました。

OpenStreetMap は誰でも無料&自由に使える地図や地理情報のオープンデータプロジェクトです。API も公開されており、位置情報アプリケーション開発時に利用することができます:
2019081707


この OpenStreetMap API を使ってドライブゲームを作って公開してみました。GitHub リポジトリはこちらです:
https://github.com/dotnsf/openmap_drive/

2019081708


実質的に index.html ファイル1つだけのプロジェクトなので GitHub Pages で公開もしています。実際に遊ぶには PC のブラウザでこちらを参照してください:
https://github.com/dotnsf/openmap_drive/

2019081702


パラメータを付けずにアクセスすると、デフォルトで三重県鈴鹿市の鈴鹿サーキットのメインストレートの一部が OpenStreetMap でブラウザ画面いっぱいに表示され、その中心に赤い車(この車でドライブします)が表示されます。

キー操作は以下の3つのみです:
 矢印キーの「下」: スタート/ストップ
 矢印キーの「左」: 左旋回
 矢印キーの「右」: 右旋回


とりあえず「下」を押すと動き出します(もう一度押すと止まります)。初期状態では5時の方角を向いているので右下方向に向かって移動を始めます(実際には車の位置は変わらず、地図部分がスクロールしていきます)。矢印の「左」や「右」を押して、なるべくコース上を走るようにしてみましょう:
2019081703
(↑ 1990年にセナとプロストが接触した1コーナー)


ぶっちゃけコースアウトしようが、何かと接触しようが車は動き続けます。なので「細かいことは忘れて」コースを走り続けましょう:
2019081704
(↑ 1989年にセナとプロストが接触したトライアングルシケイン、ここはこの赤い車でも難しい)


起動時に URL パラメータを与えることで初期位置と初期角度を変えることもできます:
 lat: 緯度
 lng: 経度
 anglelevel: 角度(何時の方向、という時計の短針の方角を示す。0~11 の整数)

下図は
 https://dotnsf.github.io/openmap_drive/?lat=43.733009&lng=7.4228981&anglelevel=4
にアクセスした時の様子です。画面下に有名なレストラン "La Rascasse(ラスカス)" が見えていますが、ここは F-1 モナコグランプリ市街地コース上です。これから最終コーナーに向かうラスカス・コーナーのあたり:
2019081705


地図で表示するのは必ずしもサーキットである必要はありません。こちら
 https://dotnsf.github.io/openmap_drive/?lat=35.6828558&lng=139.7596134&anglelevel=1
は日本の皇居前、内堀通りを走る状態からスタートします:
2019081706


実は以前に同様のゲームを Google MAPs API で作って教育目的に使っていたことがありました。他の地図 API と比較して多くの機能を持ち、今でも便利に使えると思ってはいるのですが、Google MAPs API は当時と異なり現在では API キーの取得が必要になってしまい、オープンな教材としては使いにくくなってしまったのでした。


という背景もあって、今回は Google MAPs API の API キーを使うのではなく、地図 API としてはOpenStreetMap API を、leaflet.js ライブラリを併用して使う形で作り直しました。加えて車の画像も「いらすとや」で公開されているものを直接参照し、CSS の transform で角度を変えて表示するように改良しました(この辺り、詳しくは index.html の内容を直接参照してください)。結果として(画像ファイルは不要になり)index.html ファイル1つだけで完結できるようになって、GitHub Pages で公開できる形にもなった、という副産物的なメリットも生まれました。


今回公開したファイルはあくまでテンプレートというか、「これを元に改良するための素」のつもりです。このゲームは上述のように3つのキーだけで(3種類の信号だけで)操作できるので、以前に紹介したキータッチと組み合わせて体感ゲームにしたり、IoT 的な要素を使ってデバイスからの信号を受け取れるようにしたり、身近なものをリモートコントローラー代わりにして使えたら面白いかな、と思っています。あと少し技術方向性は異なるけど、複数台の車を同時に走らせたりとかも面白そう。


最近はスマホのカメラの性能が上がり、その結果として画像ファイルサイズが大きくなりました。解像度が上がったことは嬉しいのですが、画像ファイルをウェブアプリなどにアップロードしようとする際に時間がかかったり、パケット通信量が増えてしまったりします。また送信先の API やシステム設定によってはアップロードサイズに上限が設定されていて、そのままでは送れなかったりすることもあります。

そんな場合に「画像を小さくしてから処理する」方法が考えられますが、Java によるその一例を紹介します。画像ファイルデータが img 変数にバイト配列で格納できているという前提で、横幅を 800 ピクセル、縦横比を変えずに高さをそれにあわせる形で変更するような処理内容を記述しています(こうすると、大抵 1MB 以下になります):

byte[] img = null; //. この変数に画像データが byte 配列で格納されているものとする
   :
   :

//. 画像のサイズを変更
if( img != null ){
  BufferedImage src = null;
  BufferedImage dst = null;
  AffineTransformOp xform = null;

  InputStream is = new ByteArrayInputStream( img );
  src = ImageIO.read( is );

  int width = src.getWidth();    //. オリジナル画像の幅
  int height = src.getHeight();  //. オリジナル画像の高さ

  int w = 800; //. 幅をこの数値に合わせて調整する

int new_height = w * height / width; int new_width = w;
//. 画像変換 xform = new AffineTransformOp( AffineTransform.getScaleInstance( ( double )new_width / width, ( double )new_height / height ), AffineTransformOp.TYPE_BILINEAR ); dst = new BufferedImage( new_width, new_height, src.getType() ); xform.filter( src, dst );
//. 変換後のバイナリイメージを byte 配列に再格納 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write( dst, "jpeg", baos ); img = baos.toByteArray(); }

上記のように ImageIO と java.awt のアフィン変換を使って画像サイズに変換をかけて実装しています。

 

このページのトップヘ