2010-08-09

ExtJS: 縦積み上げ棒グラフ(StackedColumnChart)とスタイルのサンプル

最近ExtJSでグラフを生成できることを知ったので、いろいろテストしながらExtJSで縦積み上げ棒グラフのサンプルをつくってみました。日本語の解説があまりないのでつまりやすいですが、機能が豊富そうだし表示がきれい。

ExtJS: 縦積み上げ棒グラフ(StackedColumnChart)とスタイルのサンプル - jsdo.it - share JavaScript, HTML5 and CSS

(今回はjsdo.itにコードがあります。jsdo.itはExtJS等のたくさんのライブラリをすぐ読み込めるのが良いですね。実行結果はリンク先で見ることが出来ます。…何でこの場で見れないんだろう? 追記: 一日置いたら(?)実行結果がこの場で見れるようになっていました。「Play」を押してみてください)

このサンプルをつくるまでにつまって調べたところをメモしておきます。

1. 棒グラフを縦向きにする方法がわからない
縦向きの棒グラフはExt.chart.ColumnChartです。積み上げの場合はExt.chart.StackedColumnChartです。

2. 軸ラベルを設定したい
xAxis, yAxisに指定する軸オブジェクトにtitleを指定します。次の例ではX軸に「X軸」という軸ラベルを設定します。

new Ext.chart.StackedColumnChart({
  // (...中略...)
  xAxis: new Ext.chart.CategoryAxis({title: "X軸"})
  // (...後略...)
});

3. 凡例を表示したい
extraStyle.legend.displayで表示位置を指定します。次の例では凡例を下部に表示するようにします。

new Ext.chart.StackedColumnChart({
  // (...中略...)
  // ("bottom"は凡例を下部に表示します)
  extraStyle: { legend: { display: "buttom" } }
  // (...後略...)
});

4. 積み上げ棒グラフを表示したい
StackedBarChart (横) か StackedColumnChart (縦) にしてseriesを設定、積み上げる方の軸オブジェクトのstackingEnabledをtrueにします。次の例ではY軸を「積み上げ」にします。

new Ext.chart.StackedColumnChart({
  // (...中略...)
  yAxis: new NumericAxis({
    title: "Y軸",
    stackingEnabled: true
  }),
  series: [
    { yField: "a", displayName: "A" },
    { yField: "b", displayName: "B" }
  ]
  // (...後略...)
});

5. フォントの大きさを変えたい
グラフはextraStyle.font.size、凡例はextraStyle.legend.font、ツールチップはextraStyle.dataTip.font.sizeに値を設定します。次の例ではグラフのフォントサイズを14に、凡例のフォントサイズを13に、ツールチップのフォントサイズを12に設定します。

new Ext.chart.StackedColumnChart({
  // (...中略...)
  extraStyle: {
    font: { size: 14 },
    legend: {
      font: { size: 13 }
    },
    dataTip: {
      font: { size: 12 }
    }
  }
  // (...後略...)
});

軸ラベルのフォントの大きさの設定方法はまだわかりません…

6. 凡例を表示するとグラフのアニメーションができなくなる?
今調査中です…

2010-06-04

入れ子集合モデルのノードの入れ替え

CakePHPのTreeBehaviorを使っているModelで部分木を入れ替える必要があったので SQLで木と階層構造のデータを扱う(1)―― 入れ子集合モデル 3-5.ノードを入れ替える のクエリを使わせてもらったんですが、動作のイメージがつかなかったので自分なりにまとめてみました。

クエリ

--部分木を入れ替える
UPDATE OrgChart
   SET lft = CASE WHEN lft BETWEEN :lft_1 AND :rgt_1 -- (1)
                    THEN :rgt_2 + lft - :rgt_1 
                  WHEN lft BETWEEN :lft_2 AND :rgt_2 -- (2)
                    THEN :lft_1 + lft - :lft_2
                  WHEN lft BETWEEN :rgt_1 + 1 AND :lft_2 - 1 -- (3)
                    THEN :lft_1 + :rgt_2 + lft - :rgt_1 - :lft_2 
                  ELSE lft END, -- (4)
       rgt = CASE WHEN rgt BETWEEN :lft_1 AND :rgt_1
                    THEN :rgt_2 + rgt - :rgt_1
                  WHEN rgt BETWEEN :lft_2 AND :rgt_2
                    THEN :lft_1 + rgt - :lft_2
                  WHEN rgt BETWEEN :rgt_1 + 1 AND :lft_2 - 1
                    THEN :lft_1 + :rgt_2 + rgt - :rgt_1 - :lft_2 
                  ELSE rgt END
 WHERE lft BETWEEN :lft_1 AND :rgt_2
   AND :lft_1 < :rgt_1
   AND :rgt_1 < :lft_2
   AND :lft_2 < :rgt_2;

ケースごとの動作

(※lftもrgtもやっていることは同じなのでxとします。(1)などの番号は上記クエリにコメントしたものに対応します)

  • (1). 対象ノードが左ノード内にある、または左ノード自身の場合: ノードを右に移動するために、左ノードと右ノードの位置の差の分、対象ノードの位置を右にずらす
    • x = x + (rgt_2-rgt_1) と同じ
    • (添字に歯抜けのある場合、左端を基準にしたlft_2-lft_1だと他のノードとの位置関係がおかしくなる可能性がある)
  • (2). 対象ノードが右ノード内にある、または右ノード自身の場合: ノードを左に移動するために、左ノードと右ノードの位置の差の分、対象ノードの位置を左にずらす
    • x = x - (lft_2-lft_1) と同じ
    • (添字に歯抜けのある場合、右端を基準にしたrgt_2-rgt_1だと他のノードとの位置関係がおかしくなる可能性がある)
  • (3). 対象ノードが左ノードと右ノードの間にある場合: 左右ノードのサイズの違う分のノードの位置をずらす
    • x = x + (右ノードサイズ - 左ノードサイズ)
         = x + ((rgt_2-lft_2) - (rgt_1-lft_1)) と同じ
  • (4). (1)(2)(3)どれにも当てはまらない場合: 更新する必要がないので更新しない

上記のようにA(2, 7)とB(10, 13)の部分木を入れ替える場合、

  • A: ケース(1)のためrgt_2 - rgt_1 = 13 - 7 = 6を加算してA(8, 13)
    • A内のノードもそれぞれ6加算
  • B: ケース(2)のためlft_2 - lft_2 = 10 - 2 = 8を減算してB(2, 5)
    • B内のノードもそれぞれ8減算
  • C: ケース(3)のため(rgt_2 - lft_2) - (rgt_1 - lft_1) = (13-7) - (10-2) = -2を加算してC(6, 7)

2010-06-01

Services_TwitterとServices_Bitlyでつぶやく

いい案を思いついたらTwitterを使って何か作りたいなぁと考えているんですが、思いつかないので月日だけ流れていっています。

今日はServices_Twitterを使ってOAuthという記事を見かけたのでこの記事を参考にServices_Twitterと、あとOpenpearのServices_Bitlyを使わせてもらってつぶやきのテストをしてみました。

結果

途中下に書いたところでつまったりはしましたが、親切なライブラリと記事のおかげで非常に簡単につぶやくことが出来ました…!

  • Services_Twitterに「Warning: simplexml_load_file(): I/O warning : failed to load external entity」と言われる
  • Services_Bitlyに「Fatal error: Uncaught Services_Bitly_Exception: MISSING_ARG_APIKEY」と言われる
    • 自分の環境(XAMPP)のphp.iniのarg_separator.outputが&になっていたので&に変更して解決

2010-05-20

phpMyAdmin: エクスポート時にデフォルトでストアドプロシージャをエクスポート

phpMyAdminはデータのエクスポート時にストアドプロシージャがデフォルトでエクスポートされないようになっていて、 先日それに気づかずにエクスポート→再インポートしてストアドプロシージャを消してしまったので、デフォルトでストアドプロシージャをエクスポートする方法を調べていました。

ネット上に情報がなかったのでソースコードを調べたところphpMyAdminの設定ファイルに設定を少し書くだけでよかったようで、config.inc.phpに

$cfg['Export']['sql_procedure_function'] = true;

を追加することでエクスポート画面の「CREATE PROCEDURE / FUNCTION / EVENT を追加」のチェックボックスに初めからチェックが入るようになり、ストアドプロシージャもデフォルトでエクスポートできるようになりました。

2010-05-18

動的言語は引数チェックが面倒

例えばPHPで

  1. PHPのImage関数のラッパークラスをつくる必要がある
  2. imageline()関数をラップして線を引くメソッドをつくる必要がある
  3. 線を引くメソッドはdrawLine(int $x1, int $y1, int $x2, int $y2, int $color)としたい
  4. drawLine(Point $p1, Point $p2, int $color)ともできるようにしたい (Pointはxとyをフィールドとして持つクラス)

と考えたとき、もし仮にPHPにオーバーロード(PHP: オーバーロード - Manualじゃなく一般的な意味でのオーバーロード)があればdrawLine()は

class Image
{
  protected $id; // 画像リソースID

// コンストラクタ等省略...

  public function drawLine($x1, $y1, $x2, $y2, $color)
  {
    return imageline($this->id, $x1, $y1, $x2, $y2, $color);
  }
  public function drawLine($p1, $p2, $color)
  {
    return imageline($this->id, $p1->x, $p1->y, $p2->x, $p2->y, $color);
  }
}

というような形で上手く収まるんですが、PHPのような動的言語はオーバーロードができないので引数の数や型を自力で調べて適切に例外を出したり…というとても面倒な事になります。

引数の数をチェックするだけにしても

class Image
{
  protected $id; // 画像リソースID

// コンストラクタ等省略...

  public function drawLine()
  {
    // 引数の数をチェックし、
    // 数が5ならdrawLine(int $x1, int $y1, int $x2, int $y2, int $color)、
    // 数が3ならdrawLine(Point $p1, Point $p2, int $color)とみなします。
    $args = func_get_args();
    switch (count($args)) {
    case 5:
      list($x1, $y1, $x2, $y2, $color) = $args;
      break;
    case 3:
      list($p1, $p2, $color) = $args;
      list($x1, $y1, $x2, $y2) = array($p1->x, $p1->y, $p2->x, $p2->y);
      break;
    default:
      // argument exception...
    }

    return imageline($this->id, $x1, $y1, $x2, $y2, $color);
  }
}

のようにどうにもすっきりしません。これが例えば

  1. 線を引くメソッドはdrawLine(int $x1, int $y1, int $x2, int $y2)としたい
  2. drawLine(int $x1, int $y1, int $x2, int $y2, int $color)ともできるようにしたい

だけであれば、PHPにはデフォルト引数があるのでそれを使って

class Image
{
  protected $id; // 画像リソースID

// コンストラクタ等省略...

  public function drawLine($x1, $y1, $x2, $y2, $color=0x000000)
  {
    return imageline($this->id, $x1, $y1, $x2, $y2, $color);
  }
}

とするだけでいいんですが…。こういう場合は静的言語の方が親切ですよね。

追記(6月1日)

もとの話とはそれるんですが、GIFはインデックスカラーなので上のコードのように$colorにRGB値をそのまま入れてもだめで、ちゃんとimagecolorallocate()等で割り当てしたインデックス値を入れないといけませんでした。サボったらだめですね。

2010-05-08

DateTime::add(), DateTime::sub()での月の計算

PHPのstrtotime()やDateTime::modify()で月の計算をすると余った日数が無視されずそのまま加算されてしまうので使い勝手が悪い(例えば 2010-03-31 + 1month == 2010-04-31 == 2010-04-30 + 1 day == 2010-05-01 であり、2010-03-31 - 1month == 2010-02-31 == 2010-02-28 + 3 day == 2010-03-03)んですが、DateTime::add(), DateTime::sub()だともしかしたら余った日数を無視するようになっているんじゃないかと思って試してみたところ…同じ結果でした(がっかり...)。

$date = new DateTime();
$interval = new DateInterval('P1M');
$format = 'Y-m-d';

echo 'PHP: ' . PHP_VERSION . PHP_EOL;
echo 'date: ' . $date->setDate(2010, 3, 31)->format($format) . PHP_EOL;
echo ' +1m: ' . $date->setDate(2010, 3, 31)->add($interval)->format($format) . PHP_EOL;
echo ' -1m: ' . $date->setDate(2010, 3, 31)->sub($interval)->format($format);
(結果)
PHP: 5.3.0
date: 2010-03-31
+1m: 2010-05-01
-1m: 2010-03-03

DateIntervalのもとになっているのがISO 8601のdurationなんですが、ISO 8601:2000の和訳のJIS X 0301:2002をざっと見ても時間長(duration)の計算に関する記述がないようなので、そもそも定義されていないのかもしれません。

同じようにISO 8601のdurationがもとになっているXML Schema DatatypesのdurationだとE.2 Commutativity and AssociativityTime durations are added by simply adding each of their fields, respectively, without overflow. (時間長は各フィールドの単純な加算によってそれぞれあふれずに加えられる?)となっていて余った日数を切り捨てるようになっているみたいなんですが…。

とりあえず自力で余った日数を切り捨てるしかなさそうです。

2010-03-19

jQueryプラグインの習作(ロールオーバー)

jQueryのプラグインの習作で簡単な画像のロールオーバーをつくってみました。

サンプル (JSBin)

Bloggerへのgistの貼付けとGoogle SitesでHTML+JavaScriptが使えるかのテストも兼ねていたんですが、Google SitesのページにはJavaScriptは使えないみたいですし、添付でアップロードしたHTMLはダウンロードしかできないみたいでした。

あと今回はマウスカーソルが画像に乗っていないときの画像と乗っているときの画像のアドレスに一定の規則がないといけなかったのでそういうサンプル画像を置ける場所を探していたんですが、Google Sitesの添付ファイルとSkyDriveの公開フォルダ内はパラメータが付いたりファイル毎にディレクトリ名がランダムだったりして今回は使えませんでした。Dropboxの公開フォルダ内は大丈夫だったので、今後はそこに画像をおくことになるかもしれません。考えたらHTMLもJSBinじゃなくてDropboxでも良かったですね。

サンプルの画像はAs Button Generatorさんで作成しました。ありがとうございます。

2010-03-14

GoogleドキュメントからEvernoteへの移行…

GoogleドキュメントからEvernoteへのデータの一括移行の仕方を調べています。

ボタン一発でやるような手軽なものは相変わらず見つからないですが、Googleドキュメントのファイルの一括エクスポートがあることがより便利に! GoogleDocsが正式にエクスポート機能を搭載 : ライフハッカー[日本版]に書かれていました。てっきりこういうものは「設定」のところからやるものだろうと思っていて、「設定」になかったのでそういう機能は公式にはないんだと思い込んでいました。思い込みって怖いです…。

EvernoteのWindows用クライアントでは「ツール」「フォルダーをインポート...」で複数ファイルのインポートができるみたいなので、この方法で移行出来るかな?と思って試してみたんですが、Googleドキュメントの方はHTML形式でZIPエクスポートするとGoogleドキュメントのファイル名に含まれている日本語がハイフンに置き換わったものがフォルダ名、index.htmlがファイル名になってどのファイルに何が書かれているかよくわからなくなるし、Evernoteの方はWindowsクライアント(3.5.2.1764)では通常のノートと同じ形で取り込むことができませんでした (HTML、テキストとも)。

Evernoteの方は2chにこういう書き込みがありました。

http://pc11.2ch.net/test/read.cgi/esite/1261284947/202

202 :名無しさん@お腹いっぱい。:2010/01/18(月) 08:10:25
今日初めてEVERNOTEのWindows版3.5を入れて、早速テキストファイルをインポートしてみたんだが
内容が展開されずテキストファイルがそのまま添付ファイル?のような形で取り込まれてしまう。
つまりファイル名そのままのテキストファイルのアイコンが表示されていて、内容の検索ができない。
そのアイコンクリックするとNotepadでテキストが開くだけ。
同期はちゃんと出来ているようでWeb版からもテキストが開けるが、ファイルアイコンの表示のままで
内容は展開されていない。
エンコードを変えてもHTMLファイルで試しても同じように添付ファイルみたいな形になってしまうので
こりゃおかしいと。
で、3.1を入れてインポートウィザードを試してみたらちゃんと展開されて取り込まれた。
この状態は俺だけ?3.5のバグ? 

これに対する返答は「ドロップする場所を変えてみたら?(203)」というものだったんですが、「フォルダのインポートでやってもファイルのドロップでやっても3.5だと駄目(204)」と同じように自分のところでも上手くいきませんでした。

何とか簡単に移行できればいいんですが…。

2010-03-12

Evernoteを使ってみることにしました

今までメモをGoogleドキュメントで取っていたんですが、知り合いのIT系に強い薬剤師の先生がEvernoteの話をしていたので大分遅いですがEvernoteを使ってみることにしました。

EvernoteはWindows用のクライアントも用意されているのがいいですね。Googleドキュメントだと完全にブラウザ上なので読み込み時間やタブを開く待ち時間が若干気になるんですが、Evernoteだとローカルですぐメモを取ったりキャプチャを取ったりもできる。

GoogleドキュメントはエディタのCtrl+1~Ctrl+6で<h1>~<h6>、Ctrl+7で<ol>、Ctrl+8で<ul>っていうショートカットが使い易くて好きなので、まとめる文書はGoogleドキュメント、まとまらないメモとかはEvernoteに入れるようにするといいかもしれないですね。Googleドキュメントで書いたものをEvernoteにも入れておけるといいんですけど…ちょっとまだ簡単に入れられるやり方がわかりません。Googleノートブックからのインポート方法はあるみたいなんですけど、また調べてみます。

2010-03-09

CakePHP(1.2.6)のpaginationで検索条件を引き継ぐ

今仕事でCakePHPを使っているんですが、こういうフレームワークってルールを覚えるのが大変ですね。命名ルールを覚えたりメソッドの動作順序を覚えたり…

今日はpaginationで検索条件(GETパラメータ)を引き継ぐ方法がよくわからなくていろいろ調べていたんですが、view内で$paginator->options($options)に渡す$options['url']['?']にデータ(連想配列かクエリ文字列)を入れておくと検索条件を引き継げるみたいです。

$search_params; // 連想配列かクエリ文字列

// passedArgsがあるとソートのキーも引き継いでくれます
$paginator->options(
array('url' => array('?' => $search_params) + $this->passedArgs)
);

$search_paramsは$this->params['url']から必要なデータを抜き出したものをcontroller内で作って$this->set()しておくか、view内で作ります($this->params['url']をそのまま$search_paramsにすると$this->params['url']['url']のせいで上手くいかないです)。

これで一応できるんですが、こういう処理はできればcontroller内でやりたい…。なのでcontrollerでどうやってやるのかもうちょっと調べてみます。

(クリボウの Blogger Tips: コードをハイライトする「Blogger Syntax Highlighter」ウィジェット を参考にシンタックスハイライトを入れてみました)

追記(3月12日)

CakePHPのpaginateにURLパラメータを渡す方法 - iakioの日記経由の極める routes.php (CakePHP 1.2) : akiyan.comでcontrollerでオプションを設定する方法を知りました。ただiakioの日記さんにもあるようにControllerクラスでオプションを設定するとNoticeエラーが出るのでやめました。消されてしまうlimitを使っている$paginator->counter()のstartとendが変な値になりますね…。

2010-03-07

ドメインを取りました

考えたらサーバがなくてもドメインを取ればいろいろなサービスを同じドメイン上で運用できるな、と思ったので1つドメインを取得してサービスをサブドメインにマッピングしてみました。

  • メール: Gmail (Google Apps)
  • blog: Blogger、Tumblr
  • wiki: Google Sites (Google Apps)

ちょっとしたプログラムを公開したいときはGAE使えば多分いいですね。

ドメインは.comドメインとか.infoドメインなら1,000円/年程度なので安いです。DNSレコードの設定は、今回はムームードメインでドメインを取ったのでコントロールパネル上で苦労せず設定できました。