2009年4月9日木曜日

Google App Engineのcronサポートまとめ



http://code.google.com/intl/en/appengine/docs/python/config/cron.html

■注意
以下の情報は2009/04/09現在(Google App Engine 1.2.0.0)の情報です。皆様がごらんになっているときには古くなっている可能性があります。



■時間指定方法
cron.yamlを書く
本家cronとは記法が異なるが、本家cronとほぼ同様のスケジューリングが可能
起動タイムゾーンの指定も可能
cron:
- description: daily summary job
  url: /tasks/summary
  schedule: every 24 hours
- description: monday morning mailout
  url: /mail/weekly
  schedule: every monday 9:00
  timezone: Australia/NSW

■認証
cronによって起動されたURLはadminユーザーによって起動されるため、以下の
2種類の方法で認証が可能
app.yamlのhandlers要素にlogin: adminを指定する
application: hello-cron
version: 1
runtime: python
api_version: 1

handlers:
  - url: /report/weekly
    script: reports.py
    login: admin

2009/04/13追記、以下の方法は利用できません。詳しくはこちらの記事をご覧ください。ご迷惑をおかけいたしまして申し訳ありませんでした。
アプリケーションのRequestHandler内で、users.is_current_user_admin()を利用して判定する(こちらの方法は未確認)
from google.appengine.api import users

# ログインしていない場合やadmin以外の場合にはFalse。adminがログインしている場合のみTrue。
if users.is_current_user_admin():
    # cronジョブをここで実行
else:
    # エラー画面を表示するなど

さらにcronはURLを起動する際にHTTPリクエストヘッダーに以下の要素を付けるため、RequestHandler内でヘッダをチェックすることが出来る
X-AppEngine-Cron: true

■cronタスクの管理方法
cronタスクをアップロードするには、通常のアプリケーションをアップロードするときと同様に以下のコマンドを実行すれば、アプリケーションと一緒にアップロードされる
appcfg.py update

cronタスクだけをアップロードしたい場合場合は以下のコマンドを実行
appcfg.py update_cron

cronタスクを削除したい場合は、cron.yamlを以下のように書き換えてからアップロードすればよい
cron: 

以下のコマンドを実行すれば、現在のcronの設定状況を見ることが出来る(ただし、タイムゾーンを扱う場合はpytzライブラリが必要)
appcfg.py cron_info

■制限
  • 今のところPython版のみ(Java版はない)Java版もある。Java版はcron.xmlを記述する。http://code.google.com/intl/ja/appengine/docs/java/config/cron.html
  • タスクは20個まで
  • 最短実行間隔は1分
  • cron.yamlから起動できるのは自アプリのURLのみ。人のアプリを起動したり、全然関係ないURLを指定したり、シェルスクリプトを起動したりは出来ない
  • cronから対象URLを起動した場合のCPU時間・通信量もすべて従量対象になる

2009年4月5日日曜日

canvasとJavaScriptでゲームなど(2) タイトルつけてみました



http://akisuesandbox.appspot.com/games/zzz
(前回とパスが変わってます、注意)

前回のゲームに悪のりしてタイトルをつけてみました。

ついでにソースコードも公開してみました。まぁ、JavaScriptなので意図しようとも意図すまいとも自動的にオープンソースになってしまいますけれども、一応w
http://github.com/akisute/gae_akisutesandbox/tree/master



以下、余談。

ゲームの画面を制御するにはループと入力受付を制御しなくてはならないのですが、
JavaScriptを用いると、
ループの制御はsetIntevalとclearInterval
入力受付の制御はhogehoge.onmousemoveとかhogehoge.onmousedownに適切なファンクションをセットする
ことで制御が出来るので比較的簡単な気がします。

すごーく昔にDirectXでプログラムを作ったときに
このあたりの制御をするのが余りにも面倒で(といいますか手続き型のプログラミングが面倒で)嫌になって辞めてしまった記憶があります。

最近のゲーム開発環境はどうなんでしょう。特にiPhoneのゲーム開発フレームワークとか大いに気になります。

2009年4月3日金曜日

canvasとJavaScriptでゲームなど




http://akisuesandbox.appspot.com/welcome/zzz

こんなの作ってました。中身は無いに等しいです。



以下駄文。

canvasとJavaScriptは思っていたよりは高速に動作しますが、やはりFlashに比べると遅いです。
ですが2Dグラフィックスの勉強にはcanvas + JavaScriptのほうがFlashより向いていると思います。Flashはかじった程度ですが、2Dグラフィックス(線引いたり四角描いたり)というよりはオブジェクトとアニメーションの制御が中心のような印象を受けました。

2009年3月19日木曜日

Your mobile device has encountered an unexpected error (0xE800003A)

いつになってもこのエラーの対処法が覚えられなくてGoogle先生にお尋ねすることになっているので、ここに備忘録を記しておきます。

1.info.plistのBundle identifierとProvisioning ProfileのApp Identifierを一致させる


これはネットのどこを見ても書いてある情報なので大丈夫だと思いますが一応。
面倒ならば、裏技的手法として、Provisioning ProfileのApp Identifierを*だけにするという手段があります。こんな風に。
ABCDEFGHIJK123.*
この方法だとオープンソースのプロジェクトを拾ってきたときでもすぐに実機インストールすることができるので、いちばんお勧めかもしれません。


2.クリーニングする(Shift+Command+K)

で、1.は毎回試すのですが、それでもうまくいかないので次はこれ。クリーニングを行います。
もしくはプロジェクトフォルダ以下のbuildディレクトリの中身をすべて消してしまっても同様の効果が得られます。


今のところこの二つを試したら毎回うまくビルド出来ているので、とりあえずはこれで大丈夫かと思います。

2009年3月17日火曜日

Pythonで、特定のディレクトリ以下にあるすべてのモジュールをインポートする方法

Pythonで特定のディレクトリの下にあるすべてのモジュールをインポートしたいときの方法。
これもGAEOのソースから見つけました。
def initPlugins():
"""
Initialize the installed plugins
"""
plugins_root = os.path.join(os.path.dirname(__file__), 'plugins')
if os.path.exists(plugins_root):
plugins = os.listdir(plugins_root)
for plugin in plugins:
if not re.match('^__|^\.', plugin):
try:
exec('from plugins import %s' % plugin)
except ImportError:
logging.error('Unable to import %s' % (plugin))
except SyntaxError:
logging.error('Unable to import name %s' % (plugin))


キモは二つ。
  1. os.listdirで特定のディレクトリ以下のすべてのファイル名・ディレクトリ名を取得する
  2. 取得したディレクトリ名を使ってimport文を生成して、exec()で実行する
exec()で無理矢理実行とか正直嫌な感じです。しかしこれ以外に方法はなさそうですね・・・
ファイル名を使ってコマンドインジェクションとかできそうじゃないかと試してみましたが、そもそもファイル名には改行とかバックスラッシュとかが使えないので意外と安全かも。

GAEOでPythonのunittestモジュールを使って単体テストにチャレンジ

■まとめ
site-packagesにインポートするモジュールとかまとめておくのが使うのがいちばん楽


■何がしたいか
以下のようなGAEOプロジェクトがあります。



ここで、test.controller.forecastareaTest.pyにforecastareaモジュールのテストケースを記述するためには、applicationディレクトリにPYTHONPATHを通す必要があります。
それをいちばんスマートにやるにはどうすればよいかいろいろ悩んでました。


■どうしてそこで悩むの?
forecastareaTest.pyは単体で動作させなくてはいけないからです。
もしここで、jweeklyforecastディレクトリの直下にテストケースをおくことが出来るのであれば、
application_root = os.path.join(os.path.dirname(__file__), 'application')
test_root = os.path.join(os.path.dirname(__file__), 'test')
sys.path.append(application_root)
sys.path.append(test_root)

from application.controller import ForecastareaController
from test.controller import TestForecastarea

こんな風に自分のディレクトリからのパスをsys.pathに追加してからインポートすれば一発です。applicationもtestも自分の子供ディレクトリですから楽です。
しかしforecastareaTest.pyの位置から見ると・・・
application_root = os.path.join(os.path.dirname(__file__), '../../../application')
sys.path.append(application_root)

from application.controller import ForecastareaController

こんな風にインポートしなくちゃいけなくなります。相対指定とかする必要があって、ちょっとやな感じです。
PROJECT_HOME = '/User/akisute/DropBox/Projects/GoogleAppEngine/jweeklyforecast/'
application_root = os.path.join(PROJECT, 'application')
sys.path.append(application_root)

from application.controller import ForecastareaController

これでもいいのですが、これだとPROJECT_HOMEが各テストケースごとにべた書きになってしまいます。これまたディレクトリの移動に弱いです。
ディレクトリが動いたらテストケース100件の定数を全部書き直しとか嫌すぎです。


■最終的に出した結論がこれ
site-packagesディレクトリの中にjweeklyforecast_path.pyなる以下のようなモジュールを配置。
#!/usr/bin/env python
#encoding: utf-8
import os, sys

PROJECT_HOME = '/Users/akisute/Dropbox/Projects/GoogleAppEngine/jweeklyforecast'
sys.path.append(PROJECT_HOME)
sys.path.append(os.path.join(PROJECT_HOME, 'application'))

そしてこれを各テストケースからインポート。
するとapplicationディレクトリにパスが通るからインポートして使えるという仕組みです。
#!/usr/bin/env python
#coding: utf-8
import logging
try:
    import jweeklyforecast_path
except Exception, e:
    logging.warn("jweeklyforecast_path not found.")

import unittest
from controller.forecastarea import Forecastarea

class TestForecastarea(unittest.TestCase):
    """Forecastarea test case.
    """
    #以下テストケースがずらずらと続く・・・

ディレクトリが動いたり環境が変わったときには、このjweeklyforecast_pathをsite-packagesにコピーして、中身のPROJECT_HOMEを書き直すだけですみます。


■最後になりましたが
意味不明な記事になってしまってごめんなさい。自分の理解度不足ゆえグダグダになってしまいました><

GTDで己の無知の知を知る

OmniFocusを導入していちばん大事だと思ったことなのですが、
GTDを実践していると、「実はびっくりするぐらい自分は何も分かっていない」ということが明らかになります。

どういうことか?
と申しますと、

たとえば今開発しているアプリに新機能を追加したので、テストしたいと考えています。
ですからまずはOmniFocusに「〜をテストする」という考えを追加します。

これだけでは行動に落とし込めないので
「テストクラスを書く」
「テストクラスをTextMateから実行する」
といった行動レベルに分解する必要があるのですが、

ここでふと「テストクラスを書くって、どうやるんだっけ」とか
「TextMateからテストを実行するショートカットってどうだったっけ」とか
そういう疑問が次々にわいてくるわけです。

だからそれらの本筋アクションを実行する前に、
「Pythonのunittestの使い方をネットで調べる」
「サンプルコードを動かして学ぶ」
「TextMateのショートカットを調べてEvernoteにメモする」
というような雑用作業が次々に出てきてしまいます。

今までの自分は、こういう「調べる」とか「学ぶ」というアクションを
全く思考に入れずにプログラムを作ったり仕事をしたりしていました。
これが今までずっと「なぜか知らないが、たったこれだけしか仕事の量がないのにどうしてこんなに時間がかかるのか」と思っていた原因なんだなぁと独り合点しています。

おかげさまで、今では「自分は何も知らないのだから時間がかかる」という風に考えられるようになり、仕事が進まないストレスも減ったかなーと。

pythonで、Objective-Cのカテゴリのように、あるクラスのソースを変更せずに任意のメソッドを追加する方法

■結論
すべてのクラスには__bases__という隠しフィールドがあるので、そこに任意のスーパークラスを作って追加する


■何がしたいか
たとえば以下のようなクラスがあります。
class Foo:
def do_something(self, x):
  print x
  return

このクラスのソースコードに指一本触れずに以下のメソッドを追加したい。
  def another_method(self, x, y):
  print x+y
  return

って将軍様が言っていたので、一⑨さんは考えました。


■チーン!ひらめいた!
まずは以下のようにスーパークラスになるクラスを作ります。
class SampleCategory:
def another_method(self, x, y):
  print x+y
  return

そしてここからがポイント。__bases__というTupleに、今作ったスーパークラスのtype型を追加します。
スーパークラスを宣言した後で・・・
from com.akisute.example import Foo
Foo.__bases__ += (SampleCategory,)

これだけです。
なぜかこれでうまくいくのがPythonのふしぎ。

実際のソースコードの例は以下のようになります(以下、拙作のプログラムから抜粋)
#!/usr/bin/env python
# encoding: utf-8
"""Login Authentication plugin.
Extends BasicController to give it an additional function for authentication
using Google account.
"""
import sys
import os

class LoginAuthPlugin:
    def is_login(self):
        """Returns True if requesting user is logged in."""
        #Do some work here
        return false

from gaeo.controller import BaseController
BaseController.__bases__ += (LoginAuthPlugin,)


JavaScriptのprototype書き換えにも似ていますね。
ただしprototype書き換えに比べて優れているのは、__bases__はTupleなので、簡単に好きなだけスーパークラスを追加することが出来ます。
(prototypeだと複数のスーパークラスを追加するのがきわめて困難)
from com.akisute.example import Foo
Foo.__bases__ += (SampleCategory,)
Foo.__bases__ += (AnotherCategory,)


■元ネタ
GAEOでpluginを読み込んでBaseControllerを拡張するときにこのテクが使われていました。
GAEO作った人は凄い。

2009年3月14日土曜日

Firebugのconsole APIを使ってLoggerもどきを作ってみた

突然ですが、Firebugのconsole APIは凄く便利です。
console.log("abesi");
console.debug("object:%o, decimal:%d, float:%f", anObject, count, Math.PI);

問題はこれ、Firebugがインストールされていて、かつコンソールが有効になっているとき以外は
エラーになってしまいます。
(consoleというオブジェクトがないから)
これをなんとかしたい。
それからついでに、せっかくlog, debug, info, warn, errorと5つも種類があるので、ログレベル制御もしてみたい。

ということで思い立ったが吉日、車輪の再発明をしようということで、ちょっとLoggerもどきを書いてみました。
http://gist.github.com/79018

使い方はこんな感じです。
    //Get logger
    var logger = new Logger(Logger.LEVEL_DEBUG);

    logger.debug($("abesi"));
    logger.info(hidebu);
    logger.warn("tawaba=%d", 100);

ログレベル制御をしているので、ログレベルをInfoにするとdebugログが出なくなる・・・はずです。
JavaScriptのprototypeなんて触ったのは実は人生初なので問題点が多々あるかと思いますが、まぁ、せっかくですから、どうぞお使いください。

2009年3月4日水曜日

MacとiPhone向けの高機能GTDツール、OmniFocusを使って、ThinkingRockと比べてみました


  • 非常にMac的なショートカットと、GTDツールというよりアウトラインプロセッサーのような操作感覚に慣れるまでが大変だが、慣れてしまえば大変高速にタスクを収集・処理できる優良ソフト=OmniFocus
  • 名前の通り、フォーカス能力が超強力。自分が今何をしているか、何をするべきかに一瞬でフォーカスできる
  • 「次に今すぐ行動可能なアクション」だけが目の前に表示される快感
  • Macのどこからでもショートカットキー一発でタスクを収集できる機能や、現在の検索条件および画面設定をパースペクティブとして保存できる機能など、地味に便利な機能がそろっている
  • 入力補完がかなり高レベルのできばえで気持ちよく使える。Remember the Milkと同等以上
  • 日本語版のOmniFocusは翻訳が狂っていたり日付指定フォーマットに一部問題があるので、英語版を推奨
  • 欠点は高すぎることと、Mac+iPhoneの環境が前提になっていること(それ以外の環境で利用できない)
  • 安くてどこでも使えるシステムを構築したい方にはThinkingRock + Remember the Milkをお勧め

つい先日にThinkingRockを使いはじめた旨の記事を書いたのですが
アレからしばらくして、やっぱり我慢ならなくなり、ついにMac&iPhone向けの高機能GTDツール、OmniFocusに手を出してしまいました。
ということでThinkingRockと比較してみたレポをアップしたいと思います。


■そもそもGTDとは何ぞや

グーグル先生にお尋ねになるか、または以下の本を読んでみていただけるとよいかと思います。
はじめてのGTD ストレスフリーの整理術


■これまで試したGTDツール変遷

フランクリンプランナー

Remember the Milk + フランクリンプランナー

Remember the Milk Pro + Appigo Todo + フランクリンプランナー

Remember the Milk Pro + Remember the Milk for iPhone + フランクリンプランナー

ThinkingRock + Remember the Milk for iPhone + フランクリンプランナー

OmniFocus + OmniFocus for iPhone + フランクリンプランナー


新しい物好きが災いして、いろいろと試してます。
一時期フランクリンプランナーを辞めようかと思った時期もあったのですが、紙の手帳はやはり絶対必須みたいです。
私はアナログな人間だからか知りませんが、ペンで書かないと頭に入ってこないですし、モニタを見るのも嫌だというぐらい疲れているときでも紙でしたら平気です。あと、会議中にiPhoneを取り出してメモを取るのがはばかられるという問題もあったりします。

その後長いことRemember the Milkを使っていたのですが、使い込んでいるうちにRemember the Milkは備忘録としては超一級でもGTDツールとしては3流という結論に至ってしまったため、別のツールを探す必要性が出てきました。
(コンテキストの概念がないのはまだ運用で何とかなったのですが、プロジェクトの階層化ができないのと、プロジェクトとアクションをシームレスに追加変更できないのが致命的でした)


えーマジ?階層化できないの?キモーイ!orz

そこで最初に目を付けたのが前回ご紹介したThinkingRockです。


■ThinkingRock + Remember the Milk
この組み合わせの長所と短所を簡単にご紹介すると以下のようになります。

○長所
  • ThinkingRockは厳密にGTDのプロセスを体現しているので、GTD初心者には最適。使っているだけでGTDの考え方を学べる
  • プラットフォームを全く選ばない。ThinkingRockはJavaさえあればどこでも動きますし、Remember the MilkはWebに接続できればどこからでも利用できます
  • プラットフォーム間の連携が容易。DropBoxを利用してThinkingRockのデータベースを共有することで、手持ちのマシン全てで簡単に同期を行うことが出来ます。WinとMacの間の連携もスムーズ
  • ほとんど無料。必要な出費は、せいぜいRemember the MilkをProにして(年間たったの25ドル)iPhoneからでも利用できるようにする程度
×短所
  • ThinkingRockとRemember the Milkの間の連携が全くない。Remember the Milkで収集したタスクをThinkingRockに移動したり、逆にThinkingRockから明日やりたいタスクを持ち出すためにRemember the Milkに書き出すなどといった無駄な作業を行う必要があります
  • ThinkingRockは厳密にGTDのプロセスを体現しているがゆえに厳密すぎ、逆にRemember the Milkは備忘録程度の機能しかなくプロジェクトを扱うには不向き
  • どちらのツールもフォーカス力不足。プロジェクトが30程度、アクションが100を超えてくるともはや制御不能


ThinkingRockとRemember the Milkで、無料&どこでも使えるGTDを!

お金がかからずに、どこでも使える組み合わせと言う意味で、これからGTDをやってみようとお考えの方には最適ではないかと思われる組み合わせです。ThinkingRockは本当にお勧め。
問題はプロジェクトとアクションの数が増えてきたときです。どちらのツールもフォーカスをするための機能が貧弱です。
ThinkingRockには検索とソート程度しかフォーカスをするための選択肢がなく、
RtMには強力な検索機能がありますが、ThinkingRockがメインのシステムになっているため生かしきれませんでした。あと、検索結果を残すためのSmart Listが10とか20とか大変な数になってしまい、今度はどのSmart Listを利用すればよいのかわけがわからなくなるという別の問題が生じる恐れが・・・


■それで結局、OmniFocusってどうなの?
対するOmniFocus。こちらも長所と短所をまとめてみました。

○長所
  • 「今すぐ出来る行動」と「今すぐは出来ない行動」を自動的に判断してくれるのが素晴らしい
  • Mac版、iPhone版ともに、タスクに対するフォーカス能力が超強力
  • MacとiPhoneが完全にシームレスに同期可能。iPhoneからいつでもメインのシステムに入っているアクション一覧を参照可能だし、iPhoneからいつでもInboxの整理をしたり出来る
  • ThinkingRockに比べ入力補完やショートカットキーが大変充実しておりサクサク使える
×短所
  • 高い。高すぎる。Mac版とiPhone版あわせて1万円、さらにMobile Me代が年間1万円
  • Mac+iPhone以外では利用できない。うかつに携帯を買い換えたりPCを買い換えたりしようものならタスクが参照できなくなる危険がある
  • 同期が重い。Mobile Meを利用しているのですが、同期ボタンを押したらたまに2分とか待たされるときがあります。RtMなら20秒程度で済むのに


OmniFocus for iPhone

OmniFocusの機能で特筆に価するのはなんといってもフォーカス力です。
Thingsのようなほかの有名GTDツールを使ったことがないので比較は出来ないのですが・・・

「期日が明日までで、かつ仕事先のオフィスの近くで出来るタスクだけを表示してほしい」
「今週どうしてもやっておきたかったとマークしたタスクだけを、今すぐ行動できるものだけ表示してほしい」
「そういえば最近Blog書いていなかったので、Blogネタの中で一番時間がかからないものを書きたいからアクションを表示してほしい」
「時間が空いたので開発をしたい。開発タスクの中で次に行動できるアクションを表示してほしい」

このようなニーズを(設定さえしておけば)ショートカットキー一発で満たしてくれるというのが神業的に素晴らしいです。
しかもMacの中だけではなく、iPhoneでもほぼ同じことが出来ます。
帰りの電車の中で、「家に帰ったら寝るまで1時間しかないけれども、何が出来るか」などと考えたりできるわけです。もう最高。


今日、帰ってから実行するアクションは・・・

「今すぐここで出来る行動」を管理できると言うのも素晴らしい点です。
たとえば3月3日現在の地点で、「今すぐやる」リストに3月8日開催の「例大祭の列に並ぶ」なんてアクションを出されてもイラッとするだけです。
このアクションは3月8日の朝に「今すぐやる」リストに表示されてほしいのです。そしてこれが実現できているGTDツールが意外なほど少ない。
RtMには「開始日時」の概念がないですし、ThinkingRockには「開始日時」の設定項目がありますが、それを使って今すぐ実行可能なタスクだけにフォーカスをすることが出来ないに等しかったので役に立ちませんでした。


これで安心!あとは例大祭のことは忘れてソースでも書きましょうか!


あとは完全に主観になってしまいますが、「使っていて楽しい」というのも非常に重要なポイントです。
はじめてのGTD ストレスフリーの整理術にも、「使っていて楽しいツールを利用するとタスクを管理するのが楽しくなる」といった旨の記述がありますし、使っていて楽しくて仕方がないと思うのであれば少々高くてもお金を出す価値はあると思います。


ただしOmniFocusは欠点も無茶苦茶多いです。まず第一にMac + iPhoneの組み合わせ以外では利用不可能。これだけで使う人を選んでしまいます。
残念ながら今後Windowsなど他のプラットフォームで利用可能になることも永遠にないと思います・・・(開発元のOmni社がMac製品しか作らないので)。
そしてべらぼうに高い値段。「無料でいくらでもネット上に落ちているタスク管理ツールに1万円+αも出せるかどうか」というところがポイントになりそうです。
この「Mac信者のMac信者によるMac信者のためのGTDツール」と言わんばかりのハードルの高さを乗り越えられるかどうかが肝だと思います。実際にはMac信者とか全く関係なしに非常に良いツールなのですが・・・


■まとめ
初めての人、複数台マシンを持っている方、Linuxユーザーの方、無料だけれどもしっかり使える優良ツールをお探しの方にはThinkingRock+αを、 手持ちのコンピュータが全てMacで携帯はもちろんiPhoneという方、人とは違うものを使ってみたい方、「次にいますぐ行動可能なアクション」だけが目の前に出てくる快感を味わってみたい方にはOmniFocusをお勧めします。


■おまけ:日本語版のOmniFocusはイマイチ
何がイマイチって、まず翻訳が何かおかしい。所々見ていてへんな単語が・・・フォマーットとか・・・
そして何より、日付の指定が日本語じゃないとできないのです。


英語版だとこう指定できます。「next sun@8」で来週の日曜日の8時にセットできます。
「tod」だけで今日の0時、「tom」で明日0時、「now」で今この瞬間にセット。
便利!


ところが日本語版だとこうなります。ちょっと!なんだよ「翌週日曜日」って!
「明日」とか「今日」とか「今」って入力面倒じゃないの!
こんなところまで余計なローカライズしなくていいよ!!

と言うことで私は英語版を強くお勧めいたします。
英語版のダウンロードは、http://www.omnigroup.com/applications/omnifocus/ の画面右端、



こちらをクリックすると英語版がダウンロードできますよ。

2009年3月1日日曜日

CONIT社主催の、iPhone開発者懇親会に参加してきました

  • 主催はコニットさん、サムライチェスの中の人
  • 会場は超満員(総勢で120名近くの方が集まったそうです)
  • 参加者のメンツが凄すぎ、国内のiPhone関連の大御所が全員集まったような感じ!
  • 名刺入れ二つ分の名刺が全部無くなった、こんなに名刺交換したのはたぶん生まれて初めて

昨日は港区海岸付近で開催された、株式会社コニット様主催の「iPhone開発者懇親会」に参加して参りました!
参加された皆様、本当にお疲れ様でした!


右を見ても左を見ても「どこかで聞いたことがあるアプリの作者さん」だったり
「RSSを購読しているブログの中の人」だったりと、なんだかとんでもない空間に迷い込んだ気分でした。



以下、ちょっとだけ写真紹介。



コニットさん(サムライチェスを開発された会社さん)が今年のMac Worldの際に行ったプレゼンをされているの図。



同じくプレゼン、UEIの清水社長。
「果たして来年同じように浮かれていられるか」というトークが衝撃的でした。
確かに、国内iPhone開発者全体の課題ですよね。国内市場が小さいから。



最後に、LCD Clock開発者の方からお話ししていただいた、興味深いエピソードをいくつか。
  • 米国と日本では、米国のほうが圧倒的にクレームの量が多い。彼らはたかが115円のアプリにそこまで言うかというぐらいケチョンケチョンに文句をつけてくる
  • しかし、日本のクレームの方が理不尽なものが多い。たとえばiPhone SDKの仕様上どうしても再現できない問題にクレームをつけてきて金返せだのレビューで星1個つけるだの
  • 機能に文句をつけられるのはわかるが、お金を取るところに文句をつけられるとやっぱり萎える
  • 開発者とデザイナーが二人にわかれていると、デザイナーは開発者にちょっと無茶な要求を出来るから、開発者が120%の力を出せる。開発者一人が作ると、自分が出来ないことを知っているから、仕様が丸く収まってしまう。

特に開発者とデザイナーの話が凄く興味深かったですね。
自分の限界を知っているから丸く収まってしまう、といわれると確かに納得です。



ところで、なんだか私のブログがMacお宝鑑定団さんで紹介されているような気がするよー・・・
ものすごい恐縮です・・・

moo.comで最高にカッコイイ名刺を作ってもらいました!

自分の名前を売るならどうするのがいちばんいいだろう?
→それなら名刺を作るのがいい!


ということで、このたびパーソナル用の名刺を作ってみました。
私の勤めている会社からも自分用の名刺を作っていただいているのですが、
自分のウェブサイトが載せられなかったり、Twitter IDが記載できなかったり、住所が会社だから郵便物が会社に届いたりでいかにも不便。
とうとう、自分で名刺を作ることを決意いたしました。


名刺を作ってもらうのなんて初めてですから、まずはGoogle先生にお尋ねしてみることに。
http://www.google.co.jp/search?q=%E5%90%8D%E5%88%BA+%E4%BD%9C%E6%88%90&ie=UTF-8

うわー出るわ出るわ!こんなにたくさん!
一つ一つ見ていきましたが、どこのサービスもほとんど同じような内容で困ります。
まずほとんどが「既存のデザインを選ぶタイプ」です。「自分の好きなデザイン」で名刺を作ろうと思ったら、Illustratorを使ってデザインしたファイルを送るしかありません。
私はデザイナーさんではないですから、当然そんな高価なソフトは持っていませんし・・・
しかもせっかくのネット注文なのに、システムを使ってその場で完成品を見ることも出来ません。注文時にお願いしたらまず最初に見本品を印刷してお送りしてくれるサービスもありましたが、これも結局何日かは待つことになります。気に入らなければまたオンラインから注文して・・・ってこれじゃ何のためのネット注文なんでしょう。

自分の好きなように名前・住所・URLなどなどを配置したいのですが、それすらかなわず。

困り果てていたところに@technohippyさんが面白いサービスを紹介してくださいました。
それがこちら。moo.comです。
http://www.moo.com/products/

イギリスの会社さんらしいのですが、大丈夫でしょうか?
早速Business Cardを注文してみました。

注文できる名刺の種類は以下の通り。
  • Flickerから画像ファイルを持ってきて、それを元に名刺を作成
  • 画像ファイルをアップロードして、それを元に名刺を作成
  • テキストメッセージを自由に入力して、それを元に名刺を作成
  • できあいのデザインから名刺を作成
自由に画像ファイルを選択できる!というのがものすごく面白そうだったのですが、
残念ながら名刺に適した画像など手持ちになかったため、今回はテキストメッセージを元に名刺を作ってみました。


文字を入力すると、こんな具合に「完成予想図」をその場で見せてくれます!
その場で確認が出来て、その場で修正が出来るってすごいですよね!?


海外のサービスですが、きちんと日本語も使えます!
(ただし、modern以外のフォントは選択できません)


入力した文字の回転や移動もできます!!すごい!
どうやらFlashを使って自由な回転・移動をサポートしているみたいです。すごい。


続けて裏面の設計も出来ます。
こちらも自由に文字が入力でき、イメージを貼り付けることも出来ます!
入力した内容がその場で確認できるって、本当に便利です。

最後に作成した名刺の表裏プレビューを確認して、問題がなければ注文です。
決済方法はPaypalかクレジットカードを選択することが出来ます。今回はPaypalを使ってみました。
イギリスのサービスなので英語で住所を指定しないとまずいかと気をもんだのですが、
中の人曰く、Billing Addressが日本語の場合でも大丈夫らしいです。安心ですね。


気になるお値段は、50枚で$21.99。送料を含めると$27ぐらいになります。
一般的な日本の名刺作成サービスが100枚で1000円程度ですから、相当割高な感じです。
しかし私はあまり名刺を渡す機会がありませんから100枚も作る必要はないですし、
どうせ作るなら自分が本当に納得できる物がいいだろうと思い、注文!


以下、注文後の経過です。
日本時間2月6日(金):注文、自動返答システムからお返事
日本時間2月7日(土):Moo.comの中の人からお返事
日本時間2月10日(火):発送しましたよメールが到着
日本時間2月19日(木):郵便ポストにエアメールが到着しましたよ通知が届く
日本時間2月21日(土):無事受け取り完了!

実物を手に取ってみた感想ですが、
  • まずなんといっても、手に取った瞬間にわかるほど紙が上質!きちんとラミネートされており、普通の名刺に比べてかなり厚みとコシがあります。
  • 一般的な日本の名刺と比べて、長辺の長さが 3mm程度短いです。ですがあまり気にする必要はないかと思います。
  • そして印刷がとても綺麗!日本語の文字にもかかわらず、とても綺麗にアンチエイリアスされています。色のにじみもほとんどありません。
  • 添付した写真も思った以上に綺麗に表示できていました。
  • 何より、テキストや写真の配置がプレビューを正確に再現してくれていました!


本日皆さんに届いたばかりの名刺をお配りしてきましたが、
何人もの方から「面白い名刺だね」とか「インパクトがあるね」とお褒めいただいてちょっと有頂天です。
値段は張りましたが、その分素晴らしい名刺が出来て大満足です!
普通の名刺には飽きた、みんなとは違う名刺が作りたい!という方にはmoo.comは本当にお勧めです!


ただ、名刺ばっかり褒められて肝心要の私自身が中身のないスッカラカンな奴では何の意味もありません。
もっともっと中身を磨いてモノを作れる男にならねば・・・

2009年2月26日木曜日

input type="radio"でchecked指定しているにもかかわらずデフォルトでチェックが入らない?と思ったときは・・・

原因を調べるのにずいぶんと苦戦したので備忘録を作ります。
input name="calendar_1" value="1" type="radio" checked

こんな感じのチェックボックスが画面にあるのですが、何度試してもデフォルトcheckedにならないのです。
IEでもFFでも再現しました。

Firebugからcheckedを再度指定し直したり、きちんとchecked="checked"に書き方を変えてみてもダメ。HTMLのバグかと思いましたよ。

原因は・・・そう、ラジオボタン、カレンダーで使ってたんですよ。
3月の1日と4月の1日が同じカレンダーの上に乗っかることもありますよね。

input name="calendar_1" value="1" type="radio" checked
input name="calendar_2" value="2" type="radio" checked
input name="calendar_3" value="3" type="radio" checked
・・・中略・・・
input name="calendar_31" value="31" type="radio" checked
input name="calendar_1" value="1" type="radio" disabled checked
あ、同じ名前のラジオボタンがあるじゃーん。しかもcheckedだー。
そりゃだめだわーアハハハハというお話でした。
checkedが機能しないときはまず真っ先に同じ名前のラジオボタンがないか疑え、ということで。

2009年2月22日日曜日

Mac OS X LeopardでPYTHONPATHとPYTHONSTARTUPを設定してみました

  • 環境変数PYTHONPATHとは、Pythonがimportするときにモジュールを探しに行くパスのこと。JavaでいうところのCLASSPATHにあたる
  • 環境変数PYTHONSTARTUPとは、Pythonを対話モード(プロンプトモード)で実行した時に自動的に実行されるPythonスクリプトのこと。起動時に毎回読み込みたい設定とかを書いておくことができる
  • autoimpとPYTHONSTARTUPを組み合わせるとimportの手間から解放されて非常に便利

Pythonのモジュール名・関数名がわからないときや、ちょっとだけスクリプトを実験してみたいときなどPythonをコマンドラインから呼び出すことがよくあると思います。
ところが未設定のままのPython対話モードだと以下のような問題が生じます。
  • 毎回毎回使うimport osとかimport sysとかをいちいち手で実行するのが面倒くさい。自動でやりたい
  • 後からインストールしたライブラリ、特にGoogle App Engineのモジュールが利用できない
そこでこれらの問題を解決するべくPythonの環境設定を行ってみることにしました。
以下、すべてMac OS X 10.5.6と付属のPython 2.5.1 (Apple Build)で設定を行っています。python.orgからダウンロードしてきたPython 2.6や3.0の場合はファイルのあるパスが違ったりなどするかもしれません。


■PythonPathを通そう
PythonPathとは要するにJavaのクラスパスです。このパスが通っているところからPythonはモジュールをインポートしようとします。
現在のPythonPathを確認するためには、以下のPythonスクリプトを実行します。
>>> import sys
>>> sys.path

似たようなのにos.pathがありますが、アレは全く別物みたいです。まどろっこしいです。

さて、PythonPathを上手に利用するためには2通りの方法があります。
一つは自分で環境変数PYTHONPATHを設定してPythonPathに好きなパスを追加する方法です。
export PYTHONPATH=${PYTHONPATH}:/usr/local/google_appengine:/usr/local/google_appengine/lib/antlr3:/usr/local/google_appengine/lib/django:/usr/local/google_appengine/lib/webob:/usr/local/google_appengine/lib/yaml/lib

もう一つは最初からPythonPathが通っていて、かつユーザーが自由に使ってもいい領域「site-package」を利用する方法です。
詳しい理屈はよくわかりませんが、Pythonには最初からsite-packageとかいう自由にライブラリを追加するための領域があるみたいです。
現在のsite-packageを調べるためには、id:a2cさん曰く
python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"

これだけでいいそうです。試しにPython 2.5.1 Apple Buildで実行してみたところ、
$ python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"
/Library/Python/2.5/site-packages

こうなりました。たぶんpython.orgのPythonや、macportでインストールしたPythonだとまた全然違った値になるのではないかと。
ということでこの/Library/Python/2.5/site-packages以下に、ダウンロードしてきた外部のライブラリなどを配置していけば自動的にパスが通っていい感じになりそうです。


■autoimpを使ってみよう
autoimpというライブラリを利用すると、いちいち最初にimport osとかimport mathとかしなくても直接os.path.join()とかmath.piとかが呼び出せるみたいです。
http://www.connellybarnes.com/code/autoimp/
使い方は簡単、まずはライブラリのソースを↑のページからダウンロードしてきて、
先ほどご説明したsite-packages以下に置くだけ。

あとは、
>>> from autoimp import *
>>> os.stat('.')
>>> sys.path
>>> math.pi
>>> google.appengine.ext.db.Model

一度from autoimp import *してしまえばimportなしで何でも自由に使えるという寸法らしいです。すごい。
でもこれでは結局最初にfrom autoimp import *と書く手間がかかってしまいますので、
Python起動時に自動的にfrom autoimp import *してくれるように設定してみましょう。


■PYTHONSTARTUPを設定しよう
環境変数PYTHONSTARTUPを利用すると、Pythonの起動時に毎回決められたスクリプトを実行することが出来るらしいです。
たとえば自分のホームディレクトリに、以下の.pythonstartup.pyというファイルを作ってみます。
#!/usr/bin/env python
#coding: utf-8
print 'hogehoge'

そして環境変数PYTHONSTARTUPを以下のように設定します。
export PYTHONSTARTUP=~/.pythonstartup.py

一端ターミナルを再起動してからpythonを実行してみると・・・
akisute ~$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
hogehoge
>>>

ほら、print 'hogehoge'が実行されてますよね。
同様にして、以下のように.pythonstartup.pyを設定すれば・・・
from autoimp import *

Pythonを実行すると・・・
akisute ~$ python
Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> os.path
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/posixpath.py'>
>>> math.pi
3.1415926535897931
>>> google.appengine.ext.db.Model
<class 'google.appengine.ext.db.Model'>

ごらんの通り!importなしに自由にモジュールが利用できています!

2009年2月15日日曜日

14 Essential Xcode Tips, Tricks and Resources for iPhone Devsを試してみた


Mobile Orchidで紹介されていた14 Essential Xcode Tips, Tricks and Resources for iPhone DevsというTipsを試してみました。
本当は全部試してみたかったのですが、そこまでXcodeを使い込んでいるわけでもないのでまずは手軽なところだけを試しました。


■プロジェクトビューのスタイルを変更してみる
現在開いているプロジェクトを全部閉じてから環境設定>全般>レイアウト。

閉じないとこんな風になっていて変更できません。

オールインワンビューがお勧めです。ウィンドウがたくさん開いてうざったらしかったのが、タブにまとめられてすっきりします。

すっきり!


■ショートカットいろいろ
  • Command+Shift+Dで「すばやく開く」。EclipseでいうCtrl+Shift+T、TextMateでいうCommand+T
  • Command+Shift+Eで「ソースコード以外を隠す」。Eclipseで言うソースコードビューの最大化
  • Control+1で「最近開いたファイルのポップアップ」。ポップアップの中でさらにタイピングしてメニューを選択できる。
Control+1がおすすめです。


こんな感じのポップアップが開きます。この状況でjweekly...とタイプしていくと、
マッチする選択肢を選択してくれますので、SpaceキーやEnterキーを押して確定。
確定したファイルが開きます。
キーボードだけで次々とファイルが切り替えていけるのが便利です。


■テンプレートを作る
Xcodeのプロジェクトテンプレートを自分で作ることが出来るらしいです。
試してみたいのですが断念。喫緊の必要が迫ったときにまたやってみます。
http://www.turkeysheartrhinos.com/?p=8#custom_project_templates


■gitと連携する
特にgitを使ってファイル管理をされている方に便利かと思います。
Xcode Project用の.gitignoreを作る:http://shanesbrain.net/2008/7/9/using-xcode-with-git
gitにコミットしたときの番号をXcodeプロジェクトののCFBundleVersionに自動的に登録してくれるスクリプト:http://www.cimgf.com/2008/04/13/git-and-xcode-a-git-build-number-script/
どちらも非常に便利そうですが、そもそもほとんどプログラムを書いていない自分には今のところ必要が無く・・・
こちらもまた喫緊のニーズが発生したときのお楽しみに取っておきます。

jweekyforecast近況報告(2009/02/15)


ようやく予報区のマスターメンテができるようになりました。
天気予報区のデータをアプリケーションに登録して、それをAPIを通じてiPhoneに引き渡すというような処理を行おうと思っています。


天気予報区には大きく分けて一次細分区域と二次細分区域がありますが、今のところ一次細分区域しか対応していません。二次細分区域を対応させようと思ったら親子関係を持たせないとなぁ。

何より問題はこのマスターメンテが死ぬほど面倒くさいことかなぁ・・・と思います ;(

さてさて、次はAPIを作らないとシステムとして成り立たないですね。

2009年2月12日木曜日

jweeklyforecast (server) のリポジトリを公開しました

http://github.com/akisute/jweeklyforecast/tree/master

先日話題にいたしました、天気予報用API提供用サーバーjWeeklyForecastのリポジトリを作成いたしましたので、取り急ぎご報告だけさせていただきます。


ちなみに中身はスッカスカです。まだGoogle App Engineの本番サーバーにもアップできていない状況なので、何とか今週末までにはアップしたいです!

2009年2月8日日曜日

生粋のWindowsユーザー向け、初めてのVim設定 on Mac OS X 10.5

  • vimはきちんと設定しないとただのメモ帳
  • きちんと設定すると地上最強クラスのエディタになる
  • でもvimの設定って意味がわからない、難しすぎる。ということで適当に調べて適当にやってみました
  • 設定は.vimrcファイルと、.vimディレクトリで行う
  • .vimrcは全体設定、.vimディレクトリは主にファイルタイプごとの設定
  • :help runtimepathとか:help ftpluginするといろいろ教えてくれる

「Windowsには秀丸があるけど、Linuxには秀丸がないし、Linuxにはろくなテキストエディタがないよね」
・・・あるWindowsユーザのぼやき


「Linuxにはvimがあるけど、WindowsだとCygwin入れないといけないから面倒だよね。GUI付きのエディタなんてろくなのがないし」
・・・あるLinuxユーザのぼやき


「TextMate + vim + emacs = pWnAgE!!」
・・・あるアメリカ人Mac OS Xギークのぼやき


「Mac OS Xで日本語の使えるテキストエディタを探しています。TextMateやvimというソフトをお勧めされたのですが、どちらも日本語が使えなくて困っています」
・・・ある日本人Mac OS X初心者のぼやき


会社で必要に迫られ、つい最近Linux環境でvimを使い始めました。
同じLinux環境のエディタ、emacsとどちらにしようか非常に悩んだのですが、なんと私が作業するRHESのサーバにはemacsがインストールされていないという驚愕の事実が明らかになり、やむなくvimに流れることになりました。
(噂には聞いていましたが、本当にvimしかないシステムってあるんですね・・・)

しかしながら、vimはWindowsのGUI付きエディタで慣れ親しんだ操作方法と全く異なる非常に独特な操作感を持ち、操作するだけでもなかなか苦労します。そして何より、適切に設定していないvimはWindows付属のメモ帳程度の機能しか持ち合わせていません。設定しなくてはならないのですが、その設定の仕方すらわからない。「設定ファイルはどこ?設定をするための画面はどうやって出すの?」という具合です。

自分も最初はquitの仕方すらわからずターミナルごとたたき落とすしかなかったのですが、使い込んでいくうちにコマンドに慣れ親しんできて、むしろ秀丸やSakuraよりも優れていると思えるほどになってきました。
ですが、設定方法だけが未だにいまいちよくわかっていません。ということでちょっと時間を取ってまとめてみることにしました。

○設定ファイル一覧
主な設定ファイルは2つ。~/.vimrcファイルと~/.vimディレクトリです。
.vimrcはvim全体の設定を行うためのファイルです。
対する.vimディレクトリは、ファイルタイプごとに異なった設定を行うための設定ファイルを配置するディレクトリになります。

○.vimrc
まずは.vimrcファイルから書いていきます。
検索時の挙動、画面の色やシンタックスハイライトの設定、ステータスバーの設定、それからもっとも大事なファイルタイププラグインの設定などを行います。
実際のファイルは以下のような感じになりました。
" .vimrc

" General
set nocompatible
set history=50

" Search
set ignorecase
set smartcase
set wrapscan
set hlsearch

" View
colorscheme desert
syntax on
set number
set title
set ruler
set list

" File system
set nobackup

" Filetype specific settings
" Set any other file type specific settings
" in ~/.vim and ~/.vim/after
" Type :set runtimepath to see you runtime path.
filetype plugin indent on

" Other programming staffs
set showmatch

個々の命令の詳しい解説は探せばたくさんありますのでそちらにお譲りしますが、
とりあえずWindowsユーザーだった人に覚えて欲しいのは、syntax onを書くこと、filetype plugin indent onを書くこと、
そしてこのファイルに書いてある内容をvim上で:(コロン)に続けて書くとこのファイルに書いたのと全く同じ効果が得られるということです。
たとえばvim上で:filetypeとタイプするとファイルタイプの設定が表示されますし、:syntax onや:syntax offとvimからタイプすると、シンタックスハイライトの設定がon off切り替わると思います。:colorscheme とタイプしてTabキーを押すと次々に利用可能なcolorschemeを表示してくれます。

この「.vimrcの内容=コロンに続けて書くことが出来る」というルールを覚えてしまうと、vimの設定がずいぶんと楽になると思います。

で、上記の.vimrcを書くことでファイルタイプごとのデフォルト設定とデフォルトインデントが有効になります。
これだけですばらしく強力なエディタになるのですが、デフォルト設定のままではいくつか気になる箇所があったり、不満が出てくる場合があります。
たとえばPython。Pythonのファイルを開いて編集してみればわかるのですが、
ごらんの通り、インデントがタブになってしまいます。PythonはPEP8でインデントを半角スペース4個でやれと規定されているので、それにあわせたいですね。


○.vimディレクトリ
そんなときこそ.vimディレクトリの出番です。
~/.vim/after/というディレクトリを作成し、その中にファイルタイプごとの設定ファイルを作成して配置することで、自由自在にファイルタイプごとの設定を行うことが出来るようになります。

ここでは自分の行った設定だけをさらしますので、
より深く知りたい方は、「runtimepath」とか、「ftplugin」とかでvimのhelpを検索してみたりググったりしてみるとよろしいかと思います。

じゃ早速やってみます。
$ mkdir -p ~/.vim/after/indent
$ cd ~/.vim/after/indent
$ vim python.vim

何やってるかよくわからんという方は、とりあえず「インデントの設定したいときは~/.vim/after/indentってディレクトリの中にファイルタイプ.vimって名前のファイル作ればよい」と覚えておけばよいかと思います。
(もちろん詳細は全然違うのできちんと調べられることを推奨いたします)

で、python.vimを書きます。
setlocal shiftwidth=4
setlocal expandtab

これだけです。

じゃ、もう一回Pythonのファイルを開いてみましょう。

ばっちりですね!!これでPythonのファイルをvimでストレス無く編集できるかと思います。

最大の問題は、vimで日本語を使うためにはさらに設定が必要だというところでしょうか。
その上vimは日本語入力が非常に大変です。
モード切替→日本語入力に切り替え→タイプ→英字に切り替え→モードを元に戻す
という非常にかったるい操作が必要になります。
(私の場合、vimで書くソースはコメント含めてすべて英語で記述するようにしています)

ほんと、コンピューターは英語以外の言語に優しくありません。


○おまけ、LeopardのTerminal.appでANSIカラーを変更するツール
vimのcolorschemeとかTerminal.appの色設定を触っているときに偶然発見したツールです。
端末のANSIカラー設定を変更することが出来ます。
http://niw.at/articles/2007/11/02/TerminalColoreopard/ja
ANSIカラーというのは、UNIX系のOSでは端末で使える色がANSIカラーとして8色決められていて、
その色を指定して自由にカラーを出力することが出来るという仕様です。

UNIX系のOSで色指定をしたいときはこのカラーコードを設定ファイルに指定してやればよいらしいのですが、この作業はとても大変です。
ということで、出来る限り端末自体の色設定を変えてやって対応するようにしたいですよね。そんな願いを叶えてくれるのがこのTerminalColorLeopardというわけです。
TerminalColorLeopardを使えばll黒背景のときと白背景の時でいちいちシェルの設定ファイルの色設定を書き換えたりしなくてすみそうです。おすすめです。
まぁ、これぐらいの機能は標準で搭載して欲しいものですが・・・

2009年1月31日土曜日

天気予報アプリが作りたい、その名はjWeeklyForecast(仮)

  • マップをタップしたら、タップしたあたりの天気予報がぱぱっと出る。ただそれだけのアプリ
  • 出来れば一週間先まで見たい。
  • 天気情報はYahoo.co.jpからスクレイピングする(Yahooさんごめんなさい)
  • そのためにバックグラウンドサーバーが欲しいのでGoogle App Engineで作っちゃう

最近、fladdictさんが日本のiPhone開発コミュニティ全体に「情報をもっとオープンにしていこう」と働きかけています。
その結果が少しずつ実をつけてきたのを肌で感じています。
たった一人の働きかけでここまでコミュニティ全体の流れを変えることができるものなのか、と感動することしきりです。

ということで私もこの流れに乗っかり、現在作成中のアプリを洗いざらい公開することにしました。

現在、jWeeklyForecastという天気予報アプリを作成しています。
  • 地図から予報を見る場所を選択したい
  • 一週間先の予報が見たい
  • 日本の天気だけ見られればよい
というニーズを満たすために開発を始めたのですが、私がトロトロしているうちにウェザーニュースさんがもうこれ以上完璧な天気予報アプリは無いだろうというぐらい完璧なアプリをリリースされてしまいましたので、すでに私のニーズは満たされてしまいました。
ここから先は自分の勉強と日本iPhone開発コミュニティへのネタ還元のためにアプリの作成を続けていこうと思います。

「日本iPhone開発コミュニティに対するネタ還元」という観点から見たjWeeklyForecastの価値ですが、
  • 地図(route-me)を使っている
  • バックエンドサーバーを自作している(Google App Engine)
この2点が大きくアピールできるのではないかと思います。
特に最近fladdictさんをはじめとして台頭しているFlasherさんたちは、バックグラウンドサーバーを自分で作った経験がほとんど無いはずです。ですので、無料で簡単に、自分の(ひいては他の開発者の皆さんのための)iPhoneアプリ用APIサーバーなどを作る方法のノウハウなどが提供できれば少しでも価値になるのではないかなーなどと考えてます。

問題は、私が大変飽きっぽい上にすぐ挫折するので、最後までやり遂げられるかとっても不安ですが・・・
まぁ若輩者の戯れ言と思っておつきあいいただければ幸いです。

ドメインakisute.comを取得いたしました

本日akisute.comドメインを取得いたしましたので、
http://akisute.com/
こちらのアドレスでも本Blogにアクセスできるようになりました!
どうぞよろしくお願いします。


余談ですが、
http://www.akisute.net/
こちらの方と私は別人ですので、あしからずご了承ください。

さらに余談ですが、
http://aniki.kululu.net/sc/talk/t_index.htm
こちらの方とも私は別人ですので、あしからずご了承ください。

2009年1月26日月曜日

ThinkingRockを使ってGTDを初めてみました


  • 最近はタスクが多すぎる!!
  • Remember The Milkだけではもう限界!サブプロジェクトすら作れないし・・・リストにはいつもタスクがあふれている!
  • そこでGTDを本格的にやってみる
  • でもOmniFocusは高くて躊躇
  • と思ってたら救世主登場、その名はThinkingRock
  • Javaなのでマルチプラットフォームなのもいい感じ
ThinkingRockというJava製のGTDアプリケーションを最近使い始めました。
同時並行で5つぐらいのプロジェクトを抱えており、さすがにRemember The Milkだけでは仕事を背負いきれなくなってきたのが始めたきっかけです。

このアプリの特徴を一言で表現するなら、

超本格的、でも簡単

これです。

実は私、GTDについてはウェブで聞きかじった程度の知識しかありません。
しかしこのThinkingRock、
「思考を集めるための画面」
「集めた思考を仕分けするための画面」
「仕分けした思考をプロジェクトやアクションとして管理するための画面」
が明示的に分離されており、
何を入力すればよいかもはっきりと決まっているので、後はヘルプを見ながらそれに従って項目を入力したりタスクを仕分けしていけば厳格にGTDに乗っ取ったタスク管理が自動的にできてしまうといった作りになっています。


このように、GTD全体の構成を表したOverviewが用意されております。

ヘルプにもGTD関連の情報が満載で、
わからないことがあれば頼りになります。
(たいていこういうツールのヘルプって頼りにならないので、驚きでした)

ヘルプ画面はこんな感じです。英語ばっかりで難しいですが・・・読めないことはないです!

サブプロジェクトとかも作成でき、コンテキストも作れて高性能でいい感じです。
気に入ったので職場のWindowsマシンにも導入しました。

2009年1月24日土曜日

route-meでタッチイベントを扱いたい

  • タッチイベントを扱うときはRMMapViewDelegateプロトコルを採用する
  • シングルタッチ、ダブルタッチを感知したり、マーカー上のタッチやドラッグを感知したり、地図の移動およびズームを感知したりできる
  • 現状、マップのドラッグやズームを使用不可能にするための手段は用意されていない。Delegateの返り値による操作もできない。
  • UIMapViewにenableDraggingおよびenableZoomというインスタンス変数が用意されているが、mapView->enableDraggingのようにしてアクセスしようとするとコンパイルエラーになってしまう。

route-me上でタッチイベントを取得してみました。

こんな感じで、画面上のタップした点を取得することができます。

タッチイベントの取得方法は、
まずRMMapViewDelegateプロトコルを任意のクラスに適合させて、
@interface MapViewController : UIViewController  {
/* 中略 */
- (void) singleTapOnMap: (RMMapView*) map At: (CGPoint) point
{
NSString *message = [NSString stringWithFormat:@"(%f, %f)", point.x, point.y];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"singleTapOnMap"
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
}
/* 後略 */

適合させたクラスのオブジェクトをRMMapViewのdelegateプロパティにセットします。
RMMapView *mapView = [[[RMMapView alloc]
initWithFrame:[UIScreen mainScreen].applicationFrame WithLocation:initialLocation.coordinate]
autorelease];
mapView.delegate = self;

普通のUIViewとほとんど同じですね。

しかしこの方法では画面上の座標がとれるだけで、緯度経度を取ることができませんので、
RMMapViewのメソッドを用いて、緯度経度に変換します。
@interface MapViewController : UIViewController  {
/* 中略 */
- (void) singleTapOnMap: (RMMapView*) map At: (CGPoint) point
{
//ポイントをLatLongに変換して表示する
CLLocationCoordinate2D coordinate = [map pixelToLatLong:point];
NSString *message = [NSString stringWithFormat:@"(%f, %f)", coordinate.latitude, coordinate.longitude];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"singleTapOnMap"
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
}
/* 後略 */

これで緯度経度になりました。

ところで、地図のスクロールやズームを制限して利用したいというときがあると思います。
たとえば日本向けのアプリなのにアルゼンチンの地図をスクロールして表示されたら困るという場合です。

早速地図のスクロールを制限する方法を調べてみたところ、
RMMapViewにいかにもそれらしいメンバー変数を発見。
@interface RMMapView : UIView
{
RMMapContents *contents;
id delegate;
BOOL enableDragging;
BOOL enableZoom;
RMGestureDetails lastGesture;
float decelerationFactor;
}

しかしながらこのメンバー変数、プロパティも操作するためのメソッドもなく、外部から操作することができません。
ためしに無理矢理以下のようなコードを書いてアクセスしてみたところ、コンパイルではねられました。
RMMapView *mapView = [[[RMMapView alloc]
initWithFrame:[UIScreen mainScreen].applicationFrame WithLocation:initialLocation.coordinate]
autorelease];
//以下の行でエラー。アクセスできません
mapView->enableDragging = NO;

残念ながらまだこの機能は現在のバージョンのroute-meでは利用できないみたいです。

その他、delegateメソッドの返り値をNOにしたらスクロールしなくなるとかそういう機能がないか調べてみましたがやはりなさそうで、今のところ地図のスクロールおよびズームを制限するのは難しそうです。今後のバージョンアップに期待ですね。

2009年1月19日月曜日

英語の発音を練習するアプリ「iはつおん」をAppBank様から頂戴いたしました

  • こちらのキャンペーンに応募→http://www.appbank.net/2009/01/13/iphone-application/6376.php
  • 見事当選!(AppBank様ありがとうございます)
  • 数ある英語の発音23種類を、お手本を聞きながら実際に自分で発音して練習できるアプリ
  • 「録音」機能がついていて、自分の発音を録音し、お手本と聞き比べられるのがすごくよい
  • 一音単位だけではなくて、単語単位での発音練習も可能
  • バージョン1.1現在、'U'の発音方法の説明がバグっていて、'Z'のものになっている
  • 単語単位だけではなくて、実際の文章単位での発音練習もできるようにして欲しい
毎日欠かさずチェックしている、iPhoneアプリレビューサイトのAppBank様。そのAppBank様の「英語発音練習iPhoneアプリ「iはつおん」無料プレゼントキャンペーンのお知らせ。」を見て、まさにこれぞチャンス!と思い速攻で申し込んだところ、なんと当選してしまいました。

当選してアプリとiTunes Store (US)のアカウントという飛び切りのお年玉をいただくことになりました。本当にありがとうございます。
受かったからには気合いを入れてレビューを書かねば!

まず最初に1点だけバグが見つかったので、その報告だけ。

なるほど、Aの発音はお腹から「ズッ」っと発声すればいいのかー・・・って違うわ!


【2009/01/22】
iはつおんの機能のレビューにつきましてはこちらを参考にしていただくとして、
ここでは、私の英語学習体験とiはつおんによる英語学習を照らし合わせたレビューをしてみます。

まずは私自身の英語学習歴から簡単にご説明。
  • 12歳で塾(公文式だったかな)に通い始めて、そこで英語を覚える。たいしたことはしていないけれども、「英語は日本語じゃないから日本語とは違うようにしゃべろう」と決心したことだけは覚えている。おかげで変な日本人発音だけはしないようになりました。
  • 13歳で中学に入る。中1の英語のセンセイがとてもよい女性の先生だったためめきめき覚える。ちなみに2年3年のころのセンセイは大変生徒から不評であった。
  • 14歳でウルティマオンラインを始める。当時のUOはAsukaとかYamatoとか日本人向けサーバーができはじめてはいたが、まだNPCは平気で英語をしゃべったりしていた。そのため必然的に英語を取得。"vender buy bank guards i ban thee"などといった日常ではあまり使わないであろう単語を中心に読み書きできるようになりました。
  • 15歳ぐらいでInfantryという神ゲーに出会う。こちら英語版しかなく、音声も画面メッセージもすべて英語。味方も敵も外人。必然的に英語で"n00b lamers you can't even stop my single assault =P"などとののしりあうことができるようになりました。
  • 16歳ぐらいでDiablo2という宇宙最強のRPGに出会う。こちら英語版しか(ry)・・・なのですが、Diablo2はInfantryと違ってRPGであったため、キャラが英語でストーリーをしゃべってくれます。おかげである程度英語が聴けるようになりました。
  • 17歳ぐらいで世界遺産級のRTSゲームことWarcraft3に出会う。こちら今でもプレイしていますが、これまたキャラが英語でしゃべるので(ry)。またこのゲーム、キャラの台詞がいちいちカッコイイ(例:これとか)ので、ついつい口ずさんでしまう。端から見るとただのキモいオタクでしたが、この実際に声に出したところが後々の英語力として残ったのではないかと思ってます。
  • その他World of Warcraftなどプレイして今に至る。
ごらんの通り、英語は完全にPCゲームから覚えています。
まともに発音記号の読み方がわかるようになったのは大学に入ってからだったり、英単語帳とか大学受験の年になって慌てて買ったというような具合ですので、
いわゆる「英語学習」というような型にはめる勉強法をほとんどしていないのです。

そんな私がiはつおん、早速試してみました。



私が見つけたiはつおんの最大のメリットは、「正しい発声方法」の「イラスト」と「解説」と「お手本発声」と「自分へのフィードバック(録音)」の4点セットが1画面で提供されていることでした。
普通の英語教材って、
  • CDだったら音はありますが発声方法がない
  • 本だと発声方法はあるけど音がない
  • そもそも自分の発音が正しいのか聴くことができない、テープレコーダーとか大げさで面倒すぎる
などなど、この4点を同時にまとめてカバーしてくれる教材なんて皆無だったのです。
ところがiはつおんなら、発声方法を見ながら、正しい音を同時に聞きつつ、さらに自分で同時に発声して練習もでき、再生ボタンを押せばすぐにフィードバックが返ってきます。
この4つの機能が1画面にまとまっている!というのは他にないアドバンテージですね。



逆に、問題点として以下の2点が浮かびました。
  • 文章単位での練習ができない。せいぜい単語単位までの音の流れしかつかめず、単語同士の接続の流れ(極端に言えばan appleがあんあっぷるじゃなくてあなぽーになる)が身につかないのではないか
  • やればやるほど目に見えて増える項目がないので達成感がない、楽しくない、したがって続かないのではないか
私の経験と照らし合わせると、
  • 聴く内容は文章、またはストーリー
  • 楽しいので毎日毎日朝から晩までやっていた
という要素があり、これが学習効果としてすごくよかったのではないのかと今になって思うからです。

また、例となる単語の数がそれほど多くない(1発音記号につき12個しかない)ので、やはり継続性に欠けると思います。
現在の私は、もっぱらiはつおんを発音方法のリファレンスにしながら、YouTubeの英語の動画の台詞をまねして発音してみたりという勉強方法にシフトしており、iはつおん単品での学習は行っていません。

現在のままでも発音方法のリファレンスとして使う分には100点満点クラスのアプリだと思いますが、せっかくならやはりよい学習コンテンツと組み合わせていただきたい!と感じてます。

そこでもしiはつおん2を出す予定がおありなら、是非ストーリー形式で文章を読み上げていくような形にして欲しいです。
英単語帳にDUOというシリーズがありますが、まさにこのシリーズをiはつおんのシステムでやれば、これまでの英語学習教材にはない学習体験が得られるかも!と考えているのですが、いかがでしょう?

RakuRaku Technologies さん、どうですか!?次はDUOでやっていただけませんか!?

2009年1月18日日曜日

iMac上でWindows XP Professional アップグレード版をBoot Campする

  • AppleはVistaまたはXPの通常版のみをサポートしている
  • が、実際にはアップグレード版もインストールできる
  • そのままではインストールできないので、外付けDVDドライブを買ってくる必要がある
  • ディスプレイドライバを更新する際に、ツールを利用してちょっとしたが必要になる
    http://www28.atwiki.jp/2chmac/pages/59.html を参照のこと
iMac (Early 2008モデル)にWindows XP Professional アップグレード版をBoot Campを用いてインストールしたときの手順メモを公開いたします。メモなのであんまり期待しないでください。



○Boot Campする前にまず覚えておきたい基本動作
・iMacのディスクは再起動時にマウスボタン(右でも左でも)を押しっぱなしにしていれば取り出せる
・再起動時にOptionキーを押しっぱなしにしていると、MacでブートするかWindowsでブートするか選択できる


○アップグレード版Windowsをインストールする方法
・外付けのDVDドライブを用意する。接続はFireWire推奨、でも最近のiMac(Leopard以降)ならばUSBでも起動ディスクとして認識してくれると思う。
・アップグレード対象の古いWindowsを用意する。自分の場合はWindows 2000のSP4適用済みディスクを利用した。
・本体にWindows XPを入れて、外付けにWindows 2000を入れた状態でBoot Campアシスタントからインストールを開始。
 途中で例によって昔のディスクを入れてくれと言われて一度止まるが、そのまま続行すると昔のWindowsを認識してくれてインストールできた
・インストール後のユーザー登録はネットで一発だった。
・パーティションサイズは32GB以下であればFAT32になってMacから読み書きできる。
 それ以上になるとNTFSになるので単純にMacからは見えない。
・インストールするパーティションを聞かれたら、Cドライブ(BOOTCAMP)を選択すること。また、再度FAT32でフォーマットをかけること。


○ディスプレイドライバ
・Leopard付属のドライバでほとんどは事足りるが、そのままではATI Catalyst Control Centerがインストールされないため、目つぶし液晶を暗くすることができない。
(もちろんBoot Campアシスタントを利用すればある程度は暗くできるが、それでもまだ明るすぎるので)
http://www28.atwiki.jp/2chmac/pages/59.html こちらのページに記載されている、「Q ドライバをインストールしてもグラフィックの細かな設定ができません」の項目を参考にして、最新のディスプレイドライバ with Catalyst Control CenterをATIからダウンロードしインストールすると、Control Centerから明度の調整ができるようになって一件落着

iMac買っちゃいました



もうすぐiLife'09がでたり、3月ぐらいには新モデルが出たりするのはわかりきっているのですが、
それでも我慢できなかったので・・・買っちゃいましたよ。

Windowsからの移行だったので過去の資産が使えなくなって困ったりしないかと不安で仕方がなかったのですが、想像より遙かに簡単に移行ができました。
躓いたのは動画(WMVとかDivXは専用のプラグインを入れないと見られない)と、
Leopardのマウスドライバは最悪最低なので、USB Overdriveというサードパーティ製のマウスドライバを使用する必要があるところぐらいでした。

Boot Campも実行してWindows XPと2本立てです。
処理速度も現行最速とはほど遠いですが、それでも自分の用途にとっては十分すぎるぐらいです。

今回iMacにインストールしたソフトウェア一覧はこちらです。
http://osx.iusethis.com/user/akisute

2009年1月11日日曜日

iPhoneやiPhoneシミュレータ上でNSURLCacheクラスを使う

  • NSURLConnectionやNSURLDownloadを利用すると自動的にNSURLCacheにキャッシュデータを蓄える
  • iPhoneシミュレータは/private/var/folders/XX/XXXXXXXXXXXXXXXXXXXXXXX/-Caches-/iPhoneのアプリ名/Cache.dbの中にキャッシュデータを蓄えている
  • iPhone実機では、メモリ上へのキャッシュは働くがファイル上へのキャッシュは行われない。したがってアプリを終了するとキャッシュはすべて消える。
  • NSURLCacheクラスについて参考 http://episteme.arstechnica.com/eve/forums/a/tpc/f/8300945231/m/863005881931/p/5
  • [NSURLRequest setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData];を使って、意図的にNSURLCacheクラスへのキャッシュを止めることもできる
  • キャッシュを停止する方法の例 http://github.com/takuma104/ntlniph/tree/master/Classes/models/NTLNHttpClient.m 76行目

HTTP通信を行う際にキャッシュを使いたい場合があると思います。
特に通信状況の良くないiPhoneプログラムでは、キャッシュを利用したいと思う機会が多いはずです。
Cocoaフレームワーク上でHTTP通信を行う場合には、NSURLConnectionクラスやNSURLDownloadクラスを利用するのが一般的だと思いますが、
これらの通信クラスを利用すると、自動的にNSURLCacheクラスのShared Instanceに通信結果がキャッシュされていくようなしくみになっています。

キャッシュされた結果は以下のようなコードで取り出せます。
  NSURL *url = [NSURL URLWithString: urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//キャッシュされたURLレスポンスを、NSURLCacheのshared instanceから取得します
NSCachedURLResponse *cachedData = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];

こうして取得されたNSCachedURLResponseは通常のNSURLResponseと同じように扱うことができます。

で、問題になってくるのはここからです。
このNSURLCacheクラスは2種類のキャッシュを内部的に持っています。メモリキャッシュとファイルキャッシュです。
メモリキャッシュの方はもはや説明不要だと思うのですが、やっかいなのはファイルキャッシュのほうです。再起動しても結果が消えないため、以前の結果が表示されると言うことが起こりえます。
iPhoneシミュレータ上で実行された場合、どこにこのURLレスポンスのキャッシュが保存されているかを調べてみたところ、
/private/var/folders/XX/XXXXXXXXXXXXXXXXXXXXXXX/-Caches-/iPhoneのアプリ名/Cache.db

の中にキャッシュが生成されていることがわかりました。(XXの部分は実行するマシンによって異なります。)
従って、キャッシュが不要になった場合はこのファイルを消してください。

2009年1月5日月曜日

route-meの、地図画像 (Tile) のキャッシュを操作したい

  • 公式のドキュメントはこちら http://code.google.com/p/route-me/wiki/CacheConfiguration
  • キャッシュにはメモリに対するキャッシュ、ファイルを利用したキャッシュ、sqliteを利用したキャッシュの3通りがある。デフォルトではメモリに32枚、sqliteに無制限に地図画像をキャッシュするようになっている。
  • キャッシュの設定を変更するにはrouteme.plistファイルを用いる
  • route-meは内部的にsqliteを処理するためにFMDBライブラリ (http://gusmueller.com/blog/archives/2008/03/fmdb_for_iphone.html) を利用しているため、route-meを利用したプロジェクトでは何もしなくてもFMDBが使える
  • たとえば、一度OpenStreetMapで開いた地図画像のキャッシュは、地図提供元をVirtualEarthにしてもOpenStreetMapにしても残ってしまうので、一度キャッシュクリアする必要がある
  • キャッシュをまとめて削除したいときはrouteme.plist中でdb-cacheのcapacityとminimalPurgeを1にする
  • またはSQLを直接発行してZCACHEテーブルをDELETEすればよい
  • またはiPhone Simulatorのdbファイルを直接Finderから削除する (~/Library/Application Support/iPhone Simulator/User/Applications/英字の長ったらしい識別子/Documents/*.sqlite) 参考はhttp://ameblo.jp/xcc/entry-10171488747.html

route-meを使っているうちに、困った事態に遭遇しました。
最初の頃に使っていたOpenStreetMapの地図画像が、いつまでたってもVirtualEarthの地図画像に入れ替わらないんです。

ごらんの通りOpenStreetMapの地図が出ます。tileSourceはVirtualEarthになっているのに!

iPhoneシミュレータをリセットしたりアプリケーションを削除したりしましたが効果なしです。
そこで原因を調査したところ、route-meは内部的にsqliteを用いた画像データのキャッシュを持っており、このキャッシュが自動的に最新のデータと入れ替わらないのが原因であることがわかりました。
(通常キャッシュはある程度保持期間が決まっていて、保持期間を過ぎたデータは破棄するようになっていると思うのですが、route-meについては少なくとも1日2日程度の期間ではキャッシュを破棄してくれないようです)
(というかsqliteのデータベースってアプリケーションごとに/var/root/Library/アプリケーション名/以下に配置されているからアプリケーションを削除したら消えるんじゃないんですか・・・?それとも全部ゴミになって残るの?単にiPhoneシミュレータが悪い?)

そこでキャッシュを破棄する方法を調べるがてら、route-meの地図画像キャッシュを設定する方法について調べてみました。
http://code.google.com/p/route-me/wiki/CacheConfiguration
公式のドキュメントをみると、"routeme.plist"というplistファイルを作成してビルドターゲットに加えることで、地図画像キャッシュの動作を変更することができるらしいです。

こんな感じで。


こうすることで、sqliteを用いたキャッシュの中に保存されるデータ量を制限することができます。
ただし、データベースの構造をみても、キャッシュ古くなったデータを自動的に削除するような機能はないようです。そのため、毎日決まった箇所の地図データを見るようなアプリの場合は、何年たっても地図の画像データが更新されません。
このように地図の画像データが更新されない恐れがある場合には、sqliteを用いたキャッシュを利用しないように設定するのがよいと思います。


さて、これで今後のキャッシュの利用方法は設定することができましたので、いますでにキャッシュされてしまっているデータを削除しましょう。
route-meではZCACHEというテーブルの中に地図画像キャッシュデータを保存していますので、このテーブルのデータを削除すればよいです。
sqliteにはTRUNCATEが無いので、DELETE文を使ってすべてのデータを削除します。
DELETE FROM ZCACHE

参考:http://www.mail-archive.com/sqlite-users@sqlite.org/msg09144.html

また、iPhoneシミュレータ上でキャッシュを消したい場合には、以下のパスにsqliteのデータベースファイルが存在しますので、そちらを丸ごと削除すればより簡単です。
(~/Library/Application Support/iPhone Simulator/User/Applications/英字の長ったらしい識別子/Documents/*.sqlite)

さぁ削除していざチャレンジ!


・・・あの、変わってないんですけど。何で?どうして?
ログをコンソールから調べてみましたが、routeme.plistが正しく読み込まれていて、間違いなくDBへのアクセスは発生していません。
となると怪しいのは地図提供元URLへのアクセスです。ここで何らかの仕組みが働いてキャッシュされているのではないかと思って調査してみました。
すると、RMWebTileImageクラスの中にこんなソースを発見。
 NSURL *url = [NSURL URLWithString: urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSCachedURLResponse *cachedData = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];

怪しいです。怪しさ大爆発です。次回はちょっと脇道にそれて、このNSURLCacheについて調査してみようと思います。

2009年1月3日土曜日

iPhoneでMapView route-meを使ってみよう! RMMarkerManager編

  • マーカーを配置するには、まずRMMarkerクラスのインスタンスを適当に生成する
  • 生成には[RMMarker markerWithNamedStyle:]を使うと便利。styleNameはRMMarker.hで2種類ほど定数として定義されている
  • マーカーを生成したら、[RMMarkerManager addMarker: AtLatLong:]で地図上に配置する
  • RMMarkerのlocation初期値はおそらくnilなので、生成したマーカーを何も考えずに[RMMarkerManager addMarker]とするとエラーになるはず(未確認)
  • マーカーにラベルをつけるときには[RMMarker setLabel:]または[RMMarker setTextLabel:]でよい。setTextLabelは正直いまいち

地図のズーム設定に初期位置設定もできるようになり、さらには地図の提供元の設定もできるようになりました。
しかしこれだけではただの地図が存在するだけです。実用的なアプリケーションにするためには、地図上に情報を配置して、アプリケーションからユーザーに何かを提示してあげなければなりません。
ということでお次は地図上にマーカーを配置してみようと思います。

route-meには標準でマーカーを管理するための機能があります。
マーカーそのものを表すRMMarkerクラスと、マーカーを管理するRMMarkerManagerクラスです。
まずはRMMarkerManagerクラスからみていきましょう。RMMarkerManager.hから、重要そうなプロパティおよびメソッドだけを切り出してみました。
//マーカーの追加・削除・表示非表示
- (void) addMarker: (RMMarker*)marker;
- (void) addMarker: (RMMarker*)marker AtLatLong:(CLLocationCoordinate2D)point;
- (void) addDefaultMarkerAt: (CLLocationCoordinate2D)point;
- (void) removeMarkers;
- (void) hideAllMarkers;
- (void) unhideAllMarkers;

//マーカーの取得
- (NSArray *)getMarkers;
- (void) removeMarker:(RMMarker *)marker;
- (CGPoint) getMarkerScreenCoordinate: (RMMarker *)marker;
- (CLLocationCoordinate2D) getMarkerCoordinate2D: (RMMarker *) marker;
- (NSArray *) getMarkersForScreenBounds;

//状態を取得
- (BOOL) isMarkerWithinScreenBounds:(RMMarker*)marker;
- (BOOL) isMarker:(RMMarker*)marker withinBounds:(CGRect)rect;
- (BOOL) managingMarker:(RMMarker*)marker;

//マーカーの移動
- (void) moveMarker:(RMMarker *)marker AtLatLon:(RMLatLong)point;
- (void) moveMarker:(RMMarker *)marker AtXY:(CGPoint)point;

一通り必要そうなメソッドはそろっており、「使い方等はもう宣言をみればわかるだろ!」といわんばかりの丁寧な作りになっています。
マーカーを配置したければ、- (void) addMarker: (RMMarker*)markerあたりを呼び出せばよさそうですね。

次にRMMarker.hをみてみます。重要な物だけ抜粋してみました。
//マーカーインスタンスの生成
+ (RMMarker*) markerWithNamedStyle: (NSString*) styleName;
- (id) initWithCGImage: (CGImageRef) image anchorPoint: (CGPoint) anchorPoint;
- (id) initWithCGImage: (CGImageRef) image;
- (id) initWithKey: (NSString*) key;
- (id) initWithUIImage: (UIImage*) image;
- (id) initWithStyle: (RMMarkerStyle*) style;
- (id) initWithNamedStyle: (NSString*) styleName;

//ラベルの設定および表示&非表示
- (void) setLabel: (UIView*)aView;
- (void) setTextLabel: (NSString*)text;
- (void) setTextLabel: (NSString*)text toPosition:(CGPoint)position;
- (void) toggleLabel;
- (void) showLabel;
- (void) hideLabel;
- (void) removeLabel;

//マーカーの画像変更および表示&非表示
- (void) replaceImage:(CGImageRef)image anchorPoint:(CGPoint)_anchorPoint;
- (void) hide;
- (void) unhide;

//各種プロパティ(おそらくここから直接操作することはない)
@property (assign, nonatomic) RMXYPoint location;
@property (retain) NSObject* data;
@property (nonatomic, retain) UIView* labelView;

デフォルトで用意されているマーカーを利用する場合は、+ (RMMarker*) markerWithNamedStyle: (NSString*) styleNameを使えばよさそうです。
自分でマーカーの画像を指定する場合は- (id) initWithUIImage: (UIImage*) image;などを用いればうまくいきそうです。

では実際に地図上にマーカーを配置してみます。
例によってRMMapViewの初期表示時に、以下のようなソースを追加します。
    CLLocation *markerLocation = [[[CLLocation alloc]
initWithLatitude:35.689613731585375 longitude:139.7616720199585]
autorelease];
RMMarker *marker = [RMMarker markerWithNamedStyle:RMMarkerRedKey];
[mapView.markerManager addMarker:marker AtLatLong:markerLocation.coordinate];

ソースの先頭で"RMMarker.h"と"RMMarkerManager.h"を#importするのをお忘れなく!
では実行してみましょう。


ばっちりです!マーカーが表示されました。

今度はマーカーにラベルをつけてみます。
ラベルとはマーカーの上に表示される説明書き領域です。RMMarkerには標準でラベルを扱う機能があります。
ラベルとしてシンプルに文字列を指定する方法と、凝ったUIViewを指定する方法が用意されていますが、まずはシンプルに文字列を出してみようと思います。
    [marker setTextLabel:@"Japan Meteorological Agency"];

これを実行すると・・・


出ましたね。
しかし正直言ってこれではわかりづらすぎです。とても実用に耐えません。
ラベルを使いたい場合には、setTextLabel:を用いず、きちんとしたUIViewを設計してから、setLabel:を利用して指定する方がよいと思います。

2009年1月1日木曜日

iPhoneでMapView route-meを使ってみよう! RMMapContents詳細編

  • 利用する地図の提供元を変更したい場合には、tileSourceプロパティを変更する。利用できるtileSourceはMapViewプロジェクト内の/Map/Tile Sourceグループの中にある
  • 地図画像(Tile)が更新されたときに呼び出すためのdelegate(RMTilesUpdateDelegate)を持っている
  • 投影方法(Projection)をメルカトル図法以外に変更できるようなプロパティがあるが、実際に変更できるかどうかは未調査

前回の記事ではroute-meライブラリのRMMapViewクラスを調査して、地図の初期表示座標やズーム倍率を設定する方法を調べてみました。
今回はroute-meライブラリのうち、地図の実体そのものを表現するRMMapContentsクラスを調査してみようと思います。

さっそくRMMapContents.hを読んでみます。利用できるプロパティとメソッドの一覧はこんな感じです。
@property (readwrite) CLLocationCoordinate2D mapCenter;
@property (readwrite) RMXYRect XYBounds;
@property (readonly) RMTileRect tileBounds;
@property (readonly) CGRect screenBounds;
@property (readwrite) float scale;
@property (readwrite) float zoom;

@property (readwrite) float minZoom, maxZoom;

@property (readonly) RMTileImageSet *imagesOnScreen;

@property (readonly) RMProjection *projection;
@property (readonly) id mercatorToTileProjection;
@property (readonly) RMMercatorToScreenProjection *mercatorToScreenProjection;

@property (retain, readwrite) id tileSource;
@property (retain, readwrite) RMMapRenderer *renderer;

@property (readonly) CALayer *layer;

@property (retain, readwrite) RMMapLayer *background;
@property (retain, readwrite) RMLayerSet *overlay;
@property (retain, readonly) RMMarkerManager *markerManager;
@property (nonatomic, retain) id tilesUpdateDelegate;
@property (readwrite) NSUInteger boundingMask;

- (id) initForView: (UIView*) view;
- (id) initForView: (UIView*) view WithLocation:(CLLocationCoordinate2D)latlong;

// Designated initialiser
- (id)initForView:(UIView*)view WithTileSource:(id)tileSource WithRenderer:(RMMapRenderer*)renderer LookingAt:(CLLocationCoordinate2D)latlong;

- (void) didReceiveMemoryWarning;

- (void)moveToLatLong: (CLLocationCoordinate2D)latlong;
- (void)moveToXYPoint: (RMXYPoint)aPoint;

- (void)moveBy: (CGSize) delta;
- (void)zoomByFactor: (float) zoomFactor near:(CGPoint) center;
- (void)zoomInToNextNativeZoomAt:(CGPoint) pivot animated:(BOOL) animated;
- (void)zoomByFactor: (float) zoomFactor near:(CGPoint) center animated:(BOOL) animated;

- (void)zoomInToNextNativeZoomAt:(CGPoint) pivot;
- (float)adjustZoomForBoundingMask:(float)zoomFactor;
- (void)adjustMapPlacementWithScale:(float)aScale;
- (void)setZoomBounds:(float)aMinZoom maxZoom:(float)aMaxZoom;

- (void) drawRect: (CGRect) rect;

// During touch and move operations on the iphone its good practice to
// hold off on any particularly expensive operations so the user's
+ (BOOL) performExpensiveOperations;
+ (void) setPerformExpensiveOperations: (BOOL)p;

- (CGPoint)latLongToPixel:(CLLocationCoordinate2D)latlong;
- (CGPoint)latLongToPixel:(CLLocationCoordinate2D)latlong withScale:(float)aScale;
- (CLLocationCoordinate2D)pixelToLatLong:(CGPoint)aPixel;
- (CLLocationCoordinate2D)pixelToLatLong:(CGPoint)aPixel withScale:(float)aScale;

- (void)zoomWithLatLngBoundsNorthEast:(CLLocationCoordinate2D)ne SouthWest:(CLLocationCoordinate2D)se;
- (void)zoomWithRMMercatorRectBounds:(RMXYRect)bounds;

- (RMLatLongBounds) getScreenCoordinateBounds;
- (RMLatLongBounds) getCoordinateBounds:(CGRect) rect;

- (void) tilesUpdatedRegion:(CGRect)region;

長い!長いです!
RMMapContentsクラス独自のメソッドだけではなくて、RMMapViewクラスが持っている機能と同じ、地図の表示位置を動かしたり、拡大倍率を操作するメソッドも持っています。
どういうことかと調べてみると・・・
-(void) moveToXYPoint: (RMXYPoint) aPoint
{
if (delegateHasBeforeMapMove) [delegate beforeMapMove: self];
[contents moveToXYPoint:aPoint];
if (delegateHasAfterMapMove) [delegate afterMapMove: self];
}
-(void) moveToLatLong: (CLLocationCoordinate2D) point
{
if (delegateHasBeforeMapMove) [delegate beforeMapMove: self];
[contents moveToLatLong:point];
if (delegateHasAfterMapMove) [delegate afterMapMove: self];
}

どうやらRMMapViewクラスのメソッドの実装は、デリゲートのメソッドの呼び出しに加えて、RMMapContentsクラスのメソッドを呼び出しているだけみたいですね。

閑話休題。
たくさん利用できるプロパティとメソッドがありますが、今回はその中でも「利用する地図の提供元を制御する」方法を調べてみようと思います。

RMMapViewの標準の地図提供元はOpenStreetViewです。
http://www.openstreetmap.org/
OpenStreetViewとは一般のユーザーがGPSを利用して測定した道などの地理情報を自由にアップロードして作るオープンソースな地図プロジェクトです。
このOpenStreetView、ヨーロッパなどのプロジェクト参加者の多い地域では非常に精度がよいのですが、
日本ではまだそれほど定着していないようで、ちょっと田舎に入ると地図の精度が絶望的なことになってきます。

ということで、地図の提供元をOpenStreetViewから別の提供元に変更する必要があります。
route-meでは、OpenStreetView以外にも標準で、
Microsoft VirtualEarth(http://www.microsoft.com/japan/virtualearth/ http://maps.live.com/
またはCloudMade(http://www.cloudmade.com/ http://maps.cloudmade.com/
の地図を利用することができます。

やり方は簡単で、RMMapViewのcontentsプロパティのtileSourceプロパティに、
MapViewプロジェクト内の/Map/Tile Sourceグループ内のクラスを指定してやるだけです。
たとえばMicrosoft VirtualEarthを利用するときは、以下のようにします。
    RMMapView *mapView = [[[RMMapView alloc]
initWithFrame:[UIScreen mainScreen].applicationFrame WithLocation:initialLocation.coordinate]
autorelease];
mapView.contents.tileSource = [[[RMVirtualEarthSource alloc] init] autorelease];

これを実行すると以下のようになります。


見事VirtualEarthになりました。これで日本地図を拡大しても安心です。

※2009/01/11追記
本記事で紹介している、RMMapViewクラスのcontentsプロパティのtileSourceプロパティを書き換える方法を用いると、
新しくサーバーからロードされてくるタイルに関しては変更後のtileSourceからロードされてくるのですが、
既存のロード済みのタイルに関しては変更前のtileSourceからロードされてキャッシュされている画像がそのまま表示されてしまいます。

従って、以下のような実装ができません。
  • 最初からOpenStreetMap以外の地図提供元を利用した実装ができない。RMMapViewのinitと同時に最初の地図情報がOpenStreetMapからロードされてしまうため。
  • あとからユーザーの操作に応じて地図の提供元を変更するような実装ができない。
この問題はroute-meプロジェクトのバグトラッカーにも掲載されています。
http://code.google.com/p/route-me/issues/detail?id=12#c5
現在のところは残念ながら改善されていないようです。とりあえず今のところは、以下のような対応を心がけましょう。
  • 途中で地図の提供元を変更する場合には、動的に画像キャッシュをクリアする。ただし、簡単にキャッシュをクリアできるような構造になってはいない。
  • 最初からOpenStreetMap以外の地図提供元を利用するときには、ダウンロードしてきたMapViewプロジェクトのソース自体を直接修正する。
具体的にはRMMapContents.mの56行目の、以下の箇所を書き換えることで実現できます。
- (id) initForView: (UIView*) view WithLocation:(CLLocationCoordinate2D)latlong
{
//この行のRMOpenStreetMapsSourceを変更する
id _tileSource = [[RMOpenStreetMapsSource alloc] init];
RMMapRenderer *_renderer = [[RMCoreAnimationRenderer alloc] initWithContent:self];

id mapContents = [self initForView:view WithTileSource:_tileSource WithRenderer:_renderer LookingAt:latlong];
[_tileSource release];
[_renderer release];

return mapContents;
}

バグトラッカーにも高い優先順位で掲載されているので、近い将来に対応されることが期待できますので、それまではちょっと様子見という感じでしょうか?

iPhoneでMapView route-meを使ってみよう! RMMapView詳細編

  • RMMapViewには主に「現在の表示位置」「現在のズーム」を操作するためのメソッドが用意されている
  • RMMapViewのプロパティとして、マップ自身を表すRMMapContents、マップ上のマーカーを操作するRMMarkerManager、そしてマップからの操作を受け取るRMMapViewDelegateが用意されている

前回の記事では実際にroute-meの地図を自分のアプリに組み込んでiPhoneシミュレーター上で動作させるところまでをやってみました。
今回はさらに一歩進んで、route-meの実体であるRMMapViewの使い方について調べてみようと思います。

○ソースを読んでみる
公式ページを見ても一切ドキュメントが用意されていない・・・ので、ココはソースを読んで解析するしかありません。
まずは前回、実際にViewとして自分のアプリに読み込ませた、RMMapView.hを調査してみます。
// Any other functions you need to manipulate the mapyou can access through this
// property. The contents structure holds the actual map bits.
@property (readonly) RMMapContents *contents;

@property (retain, readonly) RMMarkerManager *markerManager;

// do not retain the delegate so you can let the corresponding controller implement the
// delegate without circular references
@property (assign) id delegate;
@property (readwrite) float decelerationFactor;

- (id)initWithFrame:(CGRect)frame WithLocation:(CLLocationCoordinate2D)latlong;

- (void)moveToLatLong: (CLLocationCoordinate2D)latlong;
- (void)moveToXYPoint: (RMXYPoint)aPoint;

- (void)moveBy: (CGSize) delta;
- (void)zoomByFactor: (float) zoomFactor near:(CGPoint) aPoint;
- (CGPoint)latLongToPixel:(CLLocationCoordinate2D)latlong;
- (CLLocationCoordinate2D)pixelToLatLong:(CGPoint)aPixel;
- (void)zoomInToNextNativeZoomAt:(CGPoint) pivot;
- (void)setZoom:(int)zoomInt;
- (void)zoomWithLatLngBoundsNorthEast:(CLLocationCoordinate2D)ne SouthWest:(CLLocationCoordinate2D)se;
- (void)setZoomBounds:(float)aMinZoom maxZoom:(float)aMaxZoom;

- (RMLatLongBounds) getScreenCoordinateBounds;

うーん読みやすい。すばらしい。
ヘッダのソースコードを読めば、リファレンスがなくても普通に使えそうな感じですね。

では実際に使ってみます。
手始めに、前回までは初期表示としてオーストラリアの片田舎が表示されるようになっていたのですが、
これを日本全体が見えるような初期位置とズーム倍率に設定してみようと思います。

MapViewを初期生成するところで、以下のように初期位置を設定します。
- (void)loadView
{
CLLocation *initialLocation = [[[CLLocation alloc]
initWithLatitude:35.0 longitude:135.0]
autorelease];
self.view = [[[RMMapView alloc]
initWithFrame:[UIScreen mainScreen].applicationFrame WithLocation:initialLocation.coordinate]
autorelease]
}

initWithFrame:(CGRect)frame WithLocation:(CLLocationCoordinate2D)latlongの第2引数の値を生成するために、CLLocationを利用します。
CLLocationはCocoa Touchに標準で付属されている、Core Locationライブラリに含まれるクラスです。
GPSを使ったアプリを作られたことのある方でしたら馴染み深いクラスだと思います。GPSを使う場合には、上記の例のように直接initすることはなく、CLLocationManagerからCLLocationの値を生成します。
CoreLocationを利用するためだと思いますが、ビルド時にCLLocationの実体がないのでリンクができないと怒られてしまいます。以下の図のようにCoreLocation.Frameworkをプロジェクトに追加してください。もちろん追加したらチェックボックスを入れてターゲットに追加するのも忘れずに。


これでビルドが通るはずです。やってみましょう。


初期位置が日本になりました!でもズームがちょっと近すぎる感じがします。もう少し日本全体が入るようにしてみましょう。
[self.view setZoom:5];


これでも何となくうまくいきましたが、ズーム率をint型で指定するのはいまいちわかりづらいです。
表示したい地理的位置がわかっている場合には、緯度経度の値を用いてズームを設定するための便利なメソッドが用意されています。
CLLocation *northEast = [[[CLLocation alloc]
initWithLatitude:40.0 longitude:145.0]
autorelease];
CLLocation *southWest = [[[CLLocation alloc]
initWithLatitude:30.0 longitude:125.0]
autorelease];
[self.view zoomWithLatLngBoundsNorthEast:northEast.coordinate SouthWest:southWest.coordinate];


いい感じですね。日本全体が見えるようになりました。

その他、地図の中心位置を移動するためのメソッド(画面上のXY座標指定、緯度経度指定、偏差指定の3タイプあります)や、
Mapアプリ上で特定地点をダブルタップしたときのように、地図上の特定の位置に向かってズームするためのメソッドが用意されています。