2011年12月27日火曜日

HTML5 - Canvas 円同士の衝突アニメーション

<canvas id="hoge" width="400" height="400"></canvas>

<script type="text/javascript">
var canvas = document.getElementById("hoge");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;

var g = 0.1;
var circles = [];
var time = 0;
var colorList = ['0','1','2','3','4','5','6','7',
                 '8','9','a','b','c','d','e','f'];

var tm;
tm = setInterval(main,10);

function main(){
 ctx.clearRect(0,0,cw,ch);
 if(time>100 || time==0){
  time = 0;
  circles.push(new Circle());
  circles[circles.length-1].init(circles.length%2);
 }
 for(i=0;i<circles.length;i++){
  for(j=i+1;j<circles.length;j++){
   collisionCircleCircle(circles[i],circles[j]);
  }
 }
 for(i=0;i<circles.length;i++){
  circles[i].move();
  circles[i].collisionWall();
  circles[i].view();
 }
 time++;
}

function Circle(){
 this.h = 0.9; this.x = 0; this.y = 100;
 this.vx = 0; this.vy = 0; this.r = 0;
 this.color = '#';
 
 this.init = function(vec){
  this.vx = Math.random()*9+1;
  this.r = Math.random()*25+5;
  if(vec){
   this.x = cw+100;
   this.vx *= -1;
  }else{
   this.x = -100;
  }
  this.color += colorList[Math.floor(Math.random()*3)+3];
  this.color += colorList[Math.floor(Math.random()*5)+5];
  this.color += colorList[Math.floor(Math.random()*7)+9];
 }
 
 this.move = function(){
  this.x += this.vx;
  this.y += this.vy;
  this.vy += g;
 }
 
 this.view = function(){
  ctx.beginPath();
  ctx.fillStyle = this.color;
  ctx.arc(this.x, this.y, this.r, 0, 360,false);
  ctx.fill();
 }
 
 this.collisionWall = function(){
  if(this.x+this.r > cw && this.vx > 0 || 
    this.x-this.r < 0 && this.vx < 0){
   this.vx *= -this.h;
  }else if(this.y+this.r > ch && this.vy > 0 || 
    this.y-this.r < 0 && this.vy < 0){
   this.vy *= -this.h;
  }
 }
}

function collisionCircleCircle(a,b){
 if((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y) < (a.r+b.r)*(a.r+b.r)){
  var vx = a.x-b.x;
  var vy = a.y-b.y;
  var len = Math.sqrt(vx*vx+vy*vy);
  var d = a.r+b.r-len;
  if(len>0) len =1/len;
  
  vx *= len;
  vy *= len;
  
  d /= 2.0;
  a.x += vx*d;
  a.y += vy*d;
  b.x -= vx*d;
  b.y -= vy*d;
  
  var t;
  t = -(vx*a.vx+vy*a.vy)/(vx*vx+vy*vy);
  var arx = a.vx+vx*t;
  var ary = a.vy+vy*t;

  t = -(-vy*a.vx+vx*a.vy)/(vy*vy+vx*vx);
  var amx = a.vx-vy*t;
  var amy = a.vy+vx*t;

  t = -(vx*b.vx+vy*b.vy)/(vx*vx+vy*vy);
  var brx = b.vx+vx*t;
  var bry = b.vy+vy*t;

  t = -(-vy*b.vx+vx*b.vy)/(vy*vy+vx*vx);
  var bmx = b.vx-vy*t;
  var bmy = b.vy+vx*t;
  
  var e = 0.8;
  var adx = (a.r*amx+b.r*bmx+bmx*e*b.r-amx*e*b.r)/(a.r+b.r);
  var bdx = -e*(bmx-amx)+adx;
  var ady = (a.r*amy+b.r*bmy+bmy*e*b.r-amy*e*b.r)/(a.r+b.r);
  var bdy = -e*(bmy-amy)+ady;
  
  a.vx = adx+arx;
  a.vy = ady+ary;
  b.vx = bdx+brx;
  b.vy = bdy+bry;
 }
}
</script>

2011年12月26日月曜日

一人レーベル

津田さんの音楽業界の本を読んだのでだらだらまとめておきます。読み返してないので間違ってるところもあると思います。

音楽業界は、レコード会社が牛耳っていたが、最近はそうもいかなくなってきています。それも結構前からそうもいかなくなっております。昔はカセットテープでしたしもっと前はレコードでした。エジソンが発明したレコード再生機は非常に高価で宝石商などが扱う高級品でした。カセットテープになり、再生機のコモディティ化に伴い音楽産業が拡大しました。

その後CDが出来まして、カラオケも流行りまして、CMタイアップも流行りまして、小室的な画一的なサビさえよけりゃどうでもいい的音楽が繁栄しました。音楽業界に絡む事業主体が増え、音楽を追求するというより儲かる音楽ありきのビジネスに音楽業界全体が突き進んだ時期です。このときレコード会社を主軸とする音楽業界の最盛期を迎えました。

しかし今は、変わりました。CDからMP3に代わり、より簡単にコピーができるようになりました。インターネットの発展に伴い、グローバルに瞬時に音楽共有ができるようになりました。ナップスターの登場以降その交換は当たり前のものになり、CDの価値は低下し続けました。またエンターテインメントの種類や媒体も多様化され、DVDがあらゆるエンターテインメントを安価で提供することとなり、これらもCD消費を減退させる一因になりました。CD消費は急激に減退する中で、ライブの売り上げはフジロックなどのフェアの成功もあり増加傾向になっています。

さて、レコード会社の機能は、音楽家の発掘、音楽の作成(CD化)、プロモーションの3機能がメインになりますが、MP3にせよCDーRにせよ非常に安価に作成出来る時代に今は来ています。またメジャーデビューして多大なプロモーションをうったとしても意外と儲からない時代になっています。また音楽家の発掘についても、そもそも儲からず、会社の成長が下降傾向にあるレコード会社は発掘するモチベーションも低下しています。そこで提案されているのが、一人レーベルというものです。

発掘は主体的にインターネットのソーシャルメディアを活用することでファンと直接的コミュニケーションをとりながら、レコード会社という巨人一名に発掘してもらうのではなく、多数の一般ピープルから発掘してもらう方向にシフトすればいいのではということです。音楽作成はローコストで出来るし、プロモーションも発掘同様にソーシャルメディアの一員となりましょうということでした。ライブも重要な収入源としなければならないし、ライブ参加者限定のCD発売などメジャーからマイナーに指向をシフトしてファンクラブ的概念を音楽活動の主軸にしていこうぜということでした。

ちなみに、音楽のレンタルは立教大学の3人の学生が始めたのですが、はやりました。瞬間的に全国でレンタルショップが出来上がり、レコード会社など既存の音楽産業既得権益者が裁判を起こしましたが、既存レンタルショップがすでに非常に多くの数になっていたこともあり、レンタルショップを潰さない妥協案が判決となりました。iTunseは全世界で流行ってますが、日本はレンタルショップの存在も大きく、メジャーレコードがiTuneseに存在しないといった状況もあることから、他国に比べるとiTunseの成功は控えめでした。

この一人レーベルは当然ながら音楽だけにしか適用できない話ではなく、全てのUGCに適用できます。個人が表現し、そのアウトプットを商材にするような業界は全て当てはまります。電子書籍は上記の音楽の発展の経緯と類似しているといわれており、今比較しながら議論されるケースが多いです。自炊の裁判は、音楽レンタルと照らし合わせて、むやみに禁止しては行けないのでは?発展を阻害するのでは?といった議論があるのかなと思います。そしてその一人レーベル化を促すプラットフォームづくりがあらゆる分野で取り組まれている状況です。

2011年12月25日日曜日

PHP - 集合に関する関数

PHPで集合に関する操作をしたいので、関連する関数を調べます。今cakePHPでプログラムしているのでcakePHPを使います。

参考:PHP Arrays as Stacks, Queues and Set(スタック・待ち行列・和集合)

Controller

public function test(){
 $a = '1,2,3,4,5,6,7';
 $a = split(',',$a);
 $b = array(1,3,5,7,9,11,13);
  
 $merge = array_merge($a,$b);
 $set = array_unique($merge);
 $diff = array_diff($a,$b);
 $diff_assoc = array_diff_assoc($a,$b);
 $intersect = array_intersect($a,$b);
  
 $data = array(
  'merge' => $merge,
  'set' => $set,
  'diff' => $diff,
  'diff_assoc' => $diff_assoc,
  'intersect' => $intersect
 );
  
 $this->set('data',$data);
}

View

<?php 
 echo 'merge: ';
 foreach($data['merge'] as $a){
  echo $a. ' , ';
 }
 echo '<br /><br />';
 
 echo 'set: ';
 foreach($data['set'] as $a){
  echo $a. ' , ';
 }
 echo '<br /><br />';
 
 echo 'diff: ';
 foreach($data['diff'] as $a){
  echo $a. ' , ';
 }
 echo '<br /><br />';
 
 echo 'diff_assoc: ';
 foreach($data['diff_assoc'] as $a){
  echo $a. ' , ';
 }
 echo '<br /><br />';
 
 echo 'intersect: ';
 foreach($data['intersect'] as $a){
  echo $a. ' , ';
 }
 echo '<br /><br />';
?>

結果

merge: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 1 , 3 , 5 , 7 , 9 , 11 , 13 , 

set: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 9 , 11 , 13 , 

diff: 2 , 4 , 6 , 

diff_assoc: 2 , 3 , 4 , 5 , 6 , 7 , 

intersect: 1 , 3 , 5 , 7 , 

まとめ

array_merge($a,$b)単純に配列$aの後ろに配列$bをつなげて出力する
array_unique($merge)配列の要素から重複を取り除いて出力する
array_diff($a,$b)配列$aにあって、配列$bにない要素のみ出力する
array_diff_assoc($a,$b)array_diffと基本的に同じだが、配列$aと配列$bを比較する際に、キーの一致も確認する。今回の場合、キーと値のペアが一致するのは、1のみである為、配列$aの1以外の数字が出力された。
array_intersect($a,$b)配列$aにも配列$bにもある要素のみ出力する。

cakePHP - RequestHandlerとJSONレスポンス

RequestHandler

★RequestHandlerコンポーネント★

RequestHandlerコンポーネントを使うと、クライアントの状態をチェックできる。GETによるアクセスなのか、POSTによるアクセスなのか、Ajaxによるアクセスなのか、などが分かる。

RequestHandlerを使うには、コントローラの$componentsにRequestHandlerを追加する必要がある。

public $components = array('RequestHandler');
$this->RequestHandler->isGet → GETによるアクセスかどうか
$this->RequestHandler->isPost → POSTによるアクセスかどうか
$this->RequestHandler->isSSL → SSL(HTTPS)アクセスかどうか
$this->RequestHandler->isAjax → Ajaxによるアクセスかどうか
$this->RequestHandler->isXml → XMLレスポンスを受け付けるかどうか
$this->RequestHandler->isRss → RSSレスポンスを受け付けるかどうか
$this->RequestHandler->isAtom → Atomレスポンスを受け付けるかどうか
$this->RequestHandler->isMobile → モバイルからのアクセスかどうか

クライアントの状態チェック以外にも使える。例えば、cakePHPのアクションがHTMLではなくJson形式でレスポンスしたいといった場合に、コンテンツの形式を変更できる。


Ajax処理のJSON出力

[CakePHP]Ajax処理のJSON出力を共通化する

というページで、cakePHPでJSON出力する際の便利なコードが書いてある。これを使えば、Ajaxの仕組みを作る度にctpファイルを作るというめんどうな作業がなくなる。ちなみにこのページのコードでは下記でRequestHandlerを使っている。

$this->RequestHandler->setContent('json');
$this->RequestHandler->respondAs('application/json; charset=UTF-8');

このページのコードを使えば、$this->set($data);を使う代わりに、$this->_renderJson($data);を使うだけで、JSON形式で出力できる。あとは、viewでjavascriptのコードを書いて、Ajaxでコントローラーのアクションにアクセスし、ゲットしたJSONを使えばいい。

下記はjavascriptのコード例。jQueryを使っている。#bookSearchをクリックしたらbooksearch()が起動して、#bookSearchTextの内容を./book_searchアクションに渡している。アクションのJSONレスポンスをもとに、#book_infosに本の情報やページ情報を記載している様。

$(function(){
    $('#bookSearch').click(function(){booksearch();});
});

function booksearch_ajax(keyword,page){
 $.ajax({
  type: "POST",
  dataType: "json",
  data: {"keyword":keyword,'page':page},
  url: "./book_search",
  success: function(data){
   if(data != '') {
         $('#book_infos').empty();
          writeBookInfo(data);
          writePage(data);
   }
  }
 });
}

function booksearch(){
 var keyword = $('#bookSearchText').val();
 var page = 1;
 booksearch_ajax(keyword,page);
}

2011年12月13日火曜日

cakePHP - twitterAPIを使う

cakePHPでtwitterAPIを使う。超分かり易くて簡単なライブラリがあった。

Consuming OAuth-enabled APIs with CakePHP

このページからライブラリをダウンロードして、vendorsに、OAuthフォルダをそのまま格納する。
そして、controllerとviewにそれぞれ下記のようなコードを書く。
※コールバック用URLとコンシューマーキー、コンシューマーシークレットは状況に合わせて入力する必要がある


hoge_controller.php
<?php 
App::import('Vendor', 'oauth', array('file' => 'OAuth'.DS.'oauth_consumer.php'));

class HogeController extends AppController{
    public $name = 'Hoge';
    public $uses = Null;
 
    function index(){
        //セッションからアクセストークンの取得
        $accessToken = $this->Session->read('twitter_access_token');
        if(!empty($accessToken)){
            $consumer = $this->createConsumer();
            //認証情報の確認
            $user_data = $consumer->get($accessToken->key, $accessToken->secret,
                'http://api.twitter.com/1/account/verify_credentials.json');
            $user_data = json_decode($user_data);
            if(!$user_data->id){
                $this->redirect('./login');
            }
            $this->set('user_data',$user_data);
        }else{
            $this->redirect('./login');
        }
    }

    public function login(){
  
    }
 
    public function logout(){
        $this->Session->delete('twitter_access_token');
        $this->redirect('./login');
    }
 
    public function twitter() {
        $consumer = $this->createConsumer();
        $requestToken = $consumer->getRequestToken('https://api.twitter.com/oauth/request_token', 'http://コールバックURL');
        $this->Session->write('twitter_request_token', $requestToken);
        $this->redirect('https://api.twitter.com/oauth/authorize?oauth_token=' . $requestToken->key);
    }

    public function twitter_callback() {
        $requestToken = $this->Session->read('twitter_request_token');
        $consumer = $this->createConsumer();
        $accessToken = $consumer->getAccessToken('https://api.twitter.com/oauth/access_token', $requestToken);
        $this->Session->write('twitter_access_token', $accessToken);
        $this->redirect('./');
    }

    private function createConsumer() {
        return new OAuth_Consumer('コンシューマーキー', 'コンシューマーシークレット');
    }
}
?>


index.ctp(index用のview)
<h1>Hoge!</h1>
<img src="<?php echo $user_data->profile_image_url;?>" width="24" height="24" />
<?php echo $user_data->screen_name; ?>
 ( <a href="./logout">Logout?</a> )


login.ctp(login用のview)
<h1>Hoge!</h1>
<h2><a href="./twitter">Please Login!</a></h2>


これで、下記のようにtwitterAPIを使ってログインできるようになり、ログインすると、twitterアカウントのアイコンとscreen_nameが表示される。


ログインしていないと、下記のログイン画面が表示される。


ログインした後のindex画面

2011年12月12日月曜日

cakePHP セッション

cakePHPでのセッションの取り扱いも超簡単だ。

『CakePHP』を使ってみる ~11~ セッションの使い方確認 に書いてある。以下引用。
// Write
$this->Session->write('myname', 'Yossy');
echo 'Write: '.$this->Session->read('myname');

// Delete
$this->Session->delete('username');
echo 'Delete: '.$this->Session->read('myname');

cakePHP JSONデコード

cakePHPでJSONデータをPHPで使えるようにデコードするには、下記で簡単にできる。
json_decode('JSON形式のデータ');

2011年12月11日日曜日

cakePHP - データ更新

データ更新は、saveメソッドで実施できる。プライマリーキーが引数で与えられたデータに含まれない場合は、データを新規追加し、含まれる場合は与えられたデータ内容で更新する。

特定のフィールドのみ更新する場合は下記のようになる。
$this->Board->id = $this->data['Board']['id'];
$this->Board->saveField('content',$this->data['Board']['content']);

cakePHP - データの削除

データの削除はidをもとに削除する場合は単純である。

$this->Board->delete($this->data['Board']['id']);
delete('idの数字')だけで削除できる。 上記は、Boardモデルのdeleteメソッドを使って、idフィールドが$this->data['Board']['id']であるデータを、boardsテーブルから削除している。

delete(id)以外の方法は下記である。

$this->Board->deleteAll(array('Board.name'=>
 $this->data['Board']['name']));
これは、Board.nameが、$this->data['Board']['name']と等しいデータが全て削除される。

$this->Board->deleteAll(array('Board.name like ?'=>
 "%{$this->data['Board']['name']}%"));
これは、データ検索(2)で記載されているfindメソッドの「?」と同じことが行われている。cakePHPでは、内部でSQL構文を構築するにあたり、「?」に、その後与えられている配列の値が順に当てはめられる。すなわち上記は、Board.nameに、$this->data['Board']['name']の値が含まれるデータが全て削除される。

cakePHP - データ検索(2)

cakePHP - データ検索(1)
上記でfindの概要がわかったが、もっと色々findの使い方がある。主にオプションであるconditionsの設定の仕方である。これを色々試す。

$data = $this->Board->find('all',array('conditions'=>
 array('Board.id'=>$this->data['Board']['id'])));
これは(1)で試したコードで、Board.idが、$this->data['Board']['id']の値と等しいものを取得する。

$data = $this->Board->find('all',array('conditions'=>
 array('Board.name like'=>"%{$this->data['Board']['name']}%")));
これは、Board.nameに、%演算子で囲まれたテキストが含まれるものを取得する。

$data = $this->Board->find('all',array('conditions'=>
 array('Board.name like ?'=>array(
 "%{$this->data['Board']['name']}%"))));
これは、一つ上のコードと全く同じことが行われる。findメソッドは、キー名に含まれる「?」部分に、値として指定されている配列から順に要素を取り出しはめ込んでいく。SQL構文とこの?の仕組みを理解していれば、SQLのWHERE句で実施される全てが行えるようだ。

$data = $this->Board->find('all',array('conditions'=>
 array('Board.id between ? and ?'=>array(2,5))));
これは、最初の?に2を入れて、次の?に5を入れている。Board.idが2〜5の間のものを取得する。

$data = $this->Board->findAllById(1);
これは、○○=××であるといったシンプルな条件指定の際に利用できる、非常に簡便なメソッドである。findAllByの後にフィールド名をキャメル記法でつなげればよい。findAllById(1)というのは、idフィールドが1であるものを全て取得することになる。

$data = $this->Board->findById(1);
これは、findAllByと仕組みは同じだが、条件に合致する最初の一つを取得する。(1)記載のallとfirstの関係と同じであり出力されるデータ構造もそれとおなじである。

cakePHP - データ検索

データの検索はこれでできる。
$data = $this->Board->find('all',array('conditions'=>
 array('Board.id'=>$this->data['Board']['id'])));
Boardモデルのfindメソッドを使って、テーブルboardsを検索している。 find('検索仕様','オプション');のように引数を二つ要する。 第一引数は、検索仕様の設定であり、all、first、list、countのいずれかを入力する。 第二引数は、オプションの設定であり、conditions,fields,recursive,order,limit,pageを連想配列として入力する。

第一引数(検索仕様)

all

allは検索条件に合致する全てを返す。データ構造は下記のとおり。
Array
(
    [0] => Array
        (
            [Board] => Array
                (
                    [id] => 1
                    [name] => taro
                    [title] => test
                    [content] => hello every one. how are you today?
                )

        )

)

first

firstは検索条件に合致する最初の一つを返す。データ構造は下記のとおり。
Array
(
    [Board] => Array
        (
            [id] => 1
            [name] => taro
            [title] => test
            [content] => hello every one. how are you today?
        )

)

list

listは条件に合致する全てのデータの、キーとその次のフィールドを返す。データ構造は下記のとおり。
Array
(
    [1] => test
)

count

countは条件に合致するデータの個数を返す。

第二引数(オプション)

conditions

条件の設定。条件をつけるフィールド名をキーに、検索する値を値とする連想配列として用意する

fields

取得するフィールド名を配列として用意する

recursive

再帰的に取得する深度

order

取得順。順序を示す数字または名前を配列として用意する

limit

取得するレコード数

page

取得するページ数

MySQL 照合順序 - 日本語を使えるようにする

照合順序を「utf8_unicode_ci」にすると、日本語使えるようになった。

cakePHP - テーブルへのデータ追加

この一行でデータ追加ができる。
$this->Board->save($this->data);
これは、Boardモデルのsaveメソッドによって$this->dataを登録している様だ。Boardモデルは、テーブルboardsの単数形をファイル名、クラス名に適用している。$this->dataも、テーブルboardsの構造に合致した配列構造になっている。

例えば、テーブルboardsが、id,name,title,contentという4つのデータフィールドを持つとする。idはauto_incrementであるとすると、$this->dataに格納する必要があるのは、name,title,contentである。フォームからこれらのデータをユーザがインプットする場合、フォームに適用させるnameはそれぞれ、Board.name,Board.title,Board.contentである。つまり「テーブル名.データフィールド名」という形態にする必要がある。

cakePHP - リダイレクト(redirect)

cakePHPのリダイレクトは超お手軽だ。
$this->redirect('.');

cakePHP - scaffoldの使い方

テーブルを用意して、コントローラーに下記のように書くだけ。
<?php 
class BoardsController extends AppController{
 public $name = 'Boards';
 public $scaffold;
}
?>
  • テーブル名:boards
  • コントローラファイル名:boards_controller.php
  • コントローラークラス名:BoardsController

CtpファイルをEclipseでPHPファイルとして表示する(CakePHP)

CtpファイルをEclipseでPHPファイルとして表示する(CakePHP)にやり方がのっているが、Eclipseの設定変更でctpファイルもPHPファイルとして表示されるようにした。
Eclipseのメニューから Windows -> Preferences をクリックし、 General -> ContentTypesを選択します。
※macだと、メニューのEclipse->環境設定(Preferences)・・・になる。

cakePHP モデルの規約

データベースのテーブル名を、usersにして、モデルファイルを、user.phpにして、モデルクラスをUserにすれば、コントローラーからUserモデルクラスにアクセスするだけで、テーブルusersにアクセスできる。 つまりテーブル名は複数形、モデルファイル及びモデルクラス名は単数形で徹底する必要がある。複数形、単数形を管理している辞書がある。

2011年12月10日土曜日

cakePHP - Appフォルダの構造

CakePHPのフォルダ構造

config CakePHP が使用する(数個の)設定ファイルが入る場所です。データベース接続の詳細、ブートストラップ、コアの設定ファイルなどがここに入ります。
controllers アプリケーションのコントローラとコンポーネントが入ります。
libs サードパーティ、外部ベンダからのライブラリではなく、ファーストパーティのライブラリが入ります。これはベンダライブラリと内部ライブラリの構成を分割することを可能にします。
locale 国際化のための文字ファイルが入ります。
models アプリケーションのモデル、ビヘイビア、データソースが入ります。
plugins プラグインパッケージが入ります。
tmp

CakePHP が一時的なデータを保管する場所です。保管される実際のデータは、CakePHP の設定しだいですが、このフォルダは通常、モデルの内容デ ータや、ログの保管に使用されます。時にはセッション情報も入ります。

確実にこのフォルダが存在し、書き込み可能であるようにしてください。そうしないと、アプリケーションのパフォーマンスは激しく影響をうけることになります。デバッグモードでは、CakePHPがそうなっているかどうかを警告してくれます。

vendors 外部(サードパーティ)で作成されたクラスやライブラリは、ここに置いてください。そうすることで、App::import('vendor', 'name') で簡単にアクセス できるようになります。注意して観察している人は、これは重複しているのではないか、と言うかもしれません。ディレクトリ構造のいちばん上にも vendors フォルダがあるからです。この二つのフォルダの違いは、複数のアプリケーションを動作させて、より複雑なシステムセットアップをする場合のことを考える際 に扱いましょう。
views 表示用のファイルはここに置きます。エレメント、エラーページ、ヘルパー、レイアウト、ビューのファイルなどです。
webroot 運用時(production)用のセットアップでは、このフォルダがアプリケーションのドキュメントルートになります。CSS スタイルシートや画像、JavaScript を入れるためのフォルダもあります。

2011年12月8日木曜日

cakePHPの「・・・is not writable」エラー

xamppでcakePHPを使ってみているのですが、Warning (512): /var/www/html/cakephp/app/tmp/cache/ is not writable のようなエラーがでておりました。これまた権限の問題で、Finder上で権限変更しても直らず、念のためxamppのapatchを再起動してもダメでした。 CakePHPフレームワークで行こうにも同じエラーに関する記載があり、このブログのようにターミナル上で権限変更したら何故かオッケーでした。
chmod -R 707 /Applications/XAMPP/xamppfiles/htdocs/cake/caketest/app/tmp

xamppでcakePHPを使うときの設定

xamppでcakePHP1.3を使う場合、プロジェクトを格納しているフォルダに、サーバ設定の上書きを許可する為に、 /Applications/XAMPP/xamppfiles/etc/httpd.conf に下記を追記する必要がある。
<Directory "/Applications/XAMPP/xamppfiles/htdocs/cake/">
 Allow from all
</Directory>
上記パスはプロジェクトを格納しているフォルダのパスとなる。上記はxamppのhtdocsフォルダの配下にcakeというフォルダをつくり、そこにcakePHPプロジェクトを入れることを想定したパス。

macでxampp使う

macでxampp使うときは、最初xamppのhtdocsフォルダが読みのみOKという権限になっていて書き込めなかった。色々試したが、結局、/Applications/XAMPP/xamppfiles/htdocsフォルダのアクセス権限を読み/書き可に設定して、htdocsフォルダにサイトを置くのが一番分かり易かった。eclipseであれば、htdocsフォルダをワークスペースにして、localhost/プロジェクトフォルダという風にアクセスする。

権限の変更は変更したいフォルダに、Finderで移動して、右クリックで情報をみるをクリックする。情報の一番したにアクセス権に関する内容が掲載されている。鍵マークをクリックして鍵を解除したら権限を変更できる。

2011年12月4日日曜日

MySQL 数字データをINSERTしたら別の数字になっている件

SQL初心者です。PHPでMySQLを本日使い始めました。初めてのINSERTで衝撃を受けています。というのも、数字データをINSERTしたら、全く別の数字データがINSERTされているからです。原因不明なので調べていきます。

現象はこうです。INSERTした数値データは、409312097であるにも関わらず、INSERTされた結果を確認すると、8388607になっている。

すぐに原因が分かりました。。数値型(データ型)のまとめに記載されていますが、数値データのデータ型を何も考えずにMEDIUMINTにしていました。しかし、これの最大値が8388607なのでした。最大値より大きい数値を登録しようとすると最大値になるんですね。勉強になりました。INTにしたら解決されました。

PHP 文字列内での変数展開について

ダブルクオーテンションで囲った文字列内に変数が入っている場合、それは展開されます。しかし注意点があります。変数の後に半角スペースがない場合、その後の文字列まで変数名として扱われてしまいます。これを回避するには、シングルクオーテンションで囲った場合のように"."で文字列連結をするか、あるいは変数を{}で囲み変数名を明示するかのいずれかの方法があります。コード作成上は{}で囲む方が楽な気がしますが、文字列連結の方が若干高速であるらしいです。
$a = 'aiueo';
echo "これは、{$a}です。";
参考:文字列内での変数展開

xamppのMySQLのrootのパスワード設定が記載されているファイル

phpMyAdmin上でrootのパスワード設定をしたところ、phpMyAdminにアクセスできなくなり少しビビった。当然解決策は設定されているファイルを探して書き換えればよいとかそういうことなはずなので探した。たまたまphpMyAdmin上でパスワード生成したときのパスワードをコピーしていたのでそれをしかるべき箇所にペーストすれば復活するのではないかと思っている。

どのようにMySQL"root"ユーザのパスワードを設定するのですか?(方法2)

上記のページに、下記のような記載がある。
次の手順で、phpMyAdmin設定を調整します。
"\xampp\phpMyAdmin\config.inc.php"ファイル内にある以下の行を変更します:
変更前:
$cfg['Servers'][$i]['user'] = 'root';
$cfg['Servers'][$i]['password'] = '';
変更後:
$cfg['Servers'][$i]['user'] = 'root';
$cfg['Servers'][$i]['password'] = 'secret';
早速上記のconfig.inc.phpファイルを探したところ確かに存在し、$cfg['Servers'][$i]['password']にコピーしておいたパスワードをペーストし保存したところ、phpMyAdminにアクセスできるようになった。しかしphpMyAdminのホームページでは依然としてエラーになる。。支障はないが。

XAMPPでpearコマンドを使う on mac

参照:XAMPPでpearコマンドを使う
XAMPPディレクトリに移動して、pear install パッケージ名

僕のPCはmacです。XAMPPは最新版をインストールしたばかり。今2011年12月4日です。phpinfo()で、pearで検索すると、pear=/Applications/XAMPP/xamppfiles/lib/php/pearと書いてありました。ターミナルを起動して、cdで/Applications/XAMPP/xamppfiles/lib/php/に移動しました。

今回はDB.phpを使えるようにしたいのです。
PEAR::DBの利用というページがDB.phpに詳しいようです。これを見ながらセットアップしていきます。

pear install DBを実行したところ、下記のエラーになりました。
Warning: touch(): Unable to create file /usr/lib/php/.lock because Permission denied in PEAR/Registry.php on line 835

Warning: touch(): Unable to create file /usr/lib/php/.lock because Permission denied in /usr/lib/php/PEAR/Registry.php on line 835
could not create lock file: fopen(/usr/lib/php/.lock): failed to open stream: No such file or directory
権限がないエラーのようです。同じ状況が『snow leopardでPEARを使う』に書いてあります。sudo pear install DBを実行すると、インストールが完了しました。このDBをどうやってPHPファイル上でrequire出来るのでしょうか?単純にrequire 'DB.php';とやると存在しないとのエラーになります。どうもpearでインストールしたファイルは、/usr/lib/php/にいるようです。xamppのphp.iniのinclude_pathにこのパスを追加したところ、require 'DB.php'で起動するようになりました!ちなみに、xamppのphp.iniは/Applications/XAMPP/xamppfiles/etcにあります。

2011年11月29日火曜日

GAEなのか?

GAEは制限が多い。1000件までしかデータベースから取得できない。抽出条件を複雑化するのはindexをつくれば出来るが、いずれにしても1000件までしか取得できない。またエンティティの検索条件設定については部分一致はできない。1万件と1万件のデータをそれぞれ取得して、両方のデータに存在するデータを抽出したいとしても、それは1000件ずつしか取得できないので、残りのそれぞれ9000件にはアクセスできない。これは大いなる問題である。GAEを使わないのであればAmazonEC2なのか、はたまた昔ながらのレンタルサーバーが以外といいのか?という話がでてくる。Amazonは遅いが自由度は高いらしい。またもし大きなWEBサービスとなるのであればスケールアウトというやつで圧倒的なメリットをAmazonは提供してくれる。でももし大きくなる見込みが低いWEBサービスなのであれば、それは速い、安い、うまい昔ながらのレンタルサーバに軍配が上がる可能性が高い。

さて、それでも尚GAEを使おうというのであれば、上記のデータベースの設計はよろしくない。クエリの複雑化はインデックスで対応するとして、データベースから取得したデータをいじるのではなく、可能な限りクエリによって最終的に取得したいデータを取得できるように設計する必要がある。が、どう考えてもいまつくろうとしているアプリケーションがGAEで作れるとは思えない。GAEはやめよう。

python リストの追加 append()

list = [1,2,3,4,5]
list.append(6)
これは list += [6] よりも効率的らしい

2011年11月27日日曜日

めんどくさいjQueryを研究する

もうjQueryって色んな括弧がごちゃごちゃしてて見ていて腹立つよ。 しかし、色んなブラウザの違いなども吸収してるらしいし、とりあえず使えるところはjQuery使った方がいい気がするし、そもそもDOMとかって大体分かるけどその使い方は全然勉強してないから、何しろjQuery勉強しないといけないと思ってるんです。最近WEBサービスつくるとき一番つまずくのがjQueryなもんで、一度研究して頭を整理しておかないといつまでたっても、遅々として先に進まないんだよね。いつも止まって適当に調べてなんとか出来たら次に行くっていうやり方が結局のところ最も非効率的なやり方だと思うんです。

さて、はっきりいってjQueryでAjaxとかやってるわりには、全然基本すらわかってないので、基本から始めたいと思う。まずはjQeryで#homuに"homuhomu"とインプットしてみよう。ちなみに、このブログは最初からjQueryを読み込んでいたはず。あと、jQueryを学ぶならやはりきっとここは外せないだろう。『jQuery日本語リファレンス』だ。

↓これがdiv#homuです

homuhomuhomuhomuhomuhomuhomuhomu



empty

<div id="homu"><p>homuhomuhomuhomuhomuhomuhomuhomu</p></div>
<script type="text/javascript">
$('#homu').empty();
</script>

ちょっと最初に宣言したことと違うことをしているが、#homuのhomuhomu...という文字列をjQueryを使って削除した様だ。ところでブログに書くに当たりそもそもDOMとは何を意味するのかについて念のため確認したい。
文書オブジェクトモデル(DOM)とは、HTML文書およびXML文書のためのアプリケーション=プログラミング=インターフェイス(API)である。これは、文書の論理的構造や、文書へのアクセスや操作の方法を定義するものである。
何言ってるのか分からん。文書を構成する個々のタグというかタグで構成される要素のことをDOMっていうのかなと思ったんだけどDOMはタグで文章を作ること自体をいうようだな。まあDOMの詳細は置いておいて、何しろDOMの構成要素を$('#homu')といった形で表すのだ。そして要素の選択方法についてはSelectors/API/jQueryに書いてあるようだ。

Androidゲーム『Dodge Mouse』つくりました!

Androidゲーム『Dodge Mouse』をつくりました!第一弾目です。シューティングゲームの敵の弾を避ける要領で、敵をよけつづけるシンプルなゲームです。Pankiaをつかってスコア共有できるようになっていて、スコアはtwitterでつぶやけるようになってます。Pankiaの使い方はsuumo jumpを参考にしました。よかったらダウンロードしてください!
Discover more Android apps

2011年11月13日日曜日

Android 開発 マルチ画面対応の研究

Androidは多種多様な端末があり、それらはそれぞれ画面サイズやDPIといったものが全然違います。マルチ画面に対応する為のルールや手法について研究します。

研究の為の簡単なプロジェクトを作成しましました。DpiTestActivityとDpiTestViewの2つのクラスから構成されます。res/drawable-mdpiに、tw.pngを格納しています。tw.pngはtwitterのbirdのフリーのイメージで、サイズは幅70(px)x高さ85(px)です。
DpiTestActivityクラス
package com.homuhomu.dpitest;

import android.app.Activity;
import android.os.Bundle;

public class DpiTestActivity extends Activity {
 private DpiTestView dView;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        dView = new DpiTestView(this);
        setContentView(dView);
    }
}
DpiTestViewクラス
package com.homuhomu.dpitest;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;

public class DpiTestView extends View {
 private Bitmap img;
 private Paint paint;
 
 public DpiTestView(Context context) {
  super(context);
  //paintの設定
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setTextSize(70);
  paint.setARGB(255, 255, 255, 255);
  //imgの登録
  Resources res = context.getResources();
  img = BitmapFactory.decodeResource(res, R.drawable.tw);
 }
 
 @Override
 protected void onDraw(Canvas c){
  c.drawBitmap(img,0,0,null); //imgの描画
  c.drawBitmap(img,70,0,null); //imgの描画
  c.drawText("A", 70, 85, paint);
 }
}

2011年9月11日日曜日

2011年9月6日火曜日

線分と線分の交差判定

線分と線分の交差判定をしたい。線分abと線分cdの交差判定をすることにしよう。交差判定には直線の方程式を使う。直線は、y=ax+bといった形になる。この直線よりも上に線分の一方の点があり、もう一方の点がこの直線よりも下にあればそれは、この直線と線分が交差するということだ。線分と線分の交差は、「ある線分ともう一方の線分を直線にしたものが交差し、かつ、もう一方の線分とある線分を直線にしたものが交差すれば」、それはすなわち線分と線分が交差することになるのだ。試してみたら分かるのだ。

よって、直線の方程式を使って上記条件に合致するか計算すればよろしい。
直線の方程式を変形しよう。y=ax+bをy-ax-b=0にする。この式に線分の各点のx,yを入れてみればよい。もし0になったらその点は既に直線にのっているということだ。交差というか接している状態だといえるだろう。大体の場合0よりも大きいか、0よりも小さいという結果になるだろう。0よりも大きければ、その点は直線よりも上にあるということだ。小さければその点は直線よりも下にあるということだ。要するに線分の端点である2点を上記式に当てはめたときの結果が、一方がマイナスで一方がプラスであれば交差するといえるわけだ。2つの結果を掛け合わせて、その結果がマイナスであれば一方のみがマイナスであったとことになる。これと同様にもう一方の線分と直線に対してもチェックして、両方とも結果がマイナスであれば、この線分と線分は交差していることになる。

javascriptでやるとこうなる。

var ax = 3, ay = 10, bx = 10, by = 3, cx = 3, cy = 3, dx = 10, dy = 10;

function lineCheck(ax,ay,bx,by,cx,cy){
  return   (by-ay)/(bx-ax)*cx+(ay-(by-ay)/(bx-ax)*ax)-cy;
} 

function lineCrossCheck(ax,ay,bx,by,cx,cy,dx,dy){
    var a = lineCheck(ax,ay,bx,by,cx,cy);
    var b = lineCheck(ax,ay,bx,by,dx,dy);
    var c = lineCheck(cx,cy,dx,dy,ax,ay);
    var d = lineCheck(cx,cy,dx,dy,bx,by);
    if(a*b<0 && c*d<0) return true;
    else return false;
}

document.write(lineCrossCheck(ax,ay,bx,by,cx,cy,dx,dy));

trueなので交差している。 そういえば、これだとx=5といったxが一定の直線との交差判定をしようとするとエラーになる。式のbx-axの部分が両方とも同じ値になるため0になるからだ。0で割るとjavascriptは結果がinfinityになるようなので、infinity様に何を足そうが引こうがきっと答えはinfinityになるのだろう。よって修正版をつくる。
ffunction lineCheck(ax,ay,bx,by,cx,cy){
 if(ax==bx){
  return cx - ax;
 }else{
  return (by-ay)/(bx-ax)*cx+(ay-(by-ay)/(bx-ax)*ax)-cy;
 }
}

これでどうだろう。

点が長方形の内側にあるかチェックする

点が長方形の内側にあるか否かをチェックするには、外積を使えばよい。

ある長方形(10,10),(30,10),(30,30),(10,30)と、ある点(20,20)があるとしよう。このある点がある長方形の内側にあるのか外側にあるのかを判定したい。このような長方形であれば、外積を使うまでもなくチェックが可能だが、長方形が回転している場合や、長方形以外の多角形の場合などになると、簡単ではなくなる。しかし、外積を使えば一発で分かる。外積はこのようにベクトルAとベクトルBの外積を求めた場合、その値がマイナスであればベクトルBはベクトルAの右側にあることになるので、長方形の各辺とある点が全て右側にあることを外積によって確かめればよい。(HTML5のcanvasの場合、座標は左上が原点となるので注意が必要じゃ。一般的な一番左下を原点とする形態であれば、長方形の各辺を時計回りにベクトルにしていけば、点がベクトルの右側にある場合、外積の数値はマイナスになるが、canvasの場合は反時計回りにベクトルにしていくとよい。マイナスになればベクトルの左側にあることにあり、(全ての辺に対して左側にあるのであれば)それはすなわち内側にあるということだ。)

まず、長方形の(30,10)-(10,10)の辺と点の位置関係を確認しよう。長方形の辺をベクトルにすると(-20,0)になる。長方形の辺の始点と点をベクトルにすると、(-10,10)となる。この2つのベクトルの外積を求めると、下記になる。
-20*10-10*0 = -200

マイナスなので点は辺の左側にあるということだ。これを全ての辺でチェックして、全ての結果がマイナスであれば内側にあるということになる。

javascriptによって内側判定をするとこうなる。

var x = 10; //長方形の左上x
var y = 10; //長方形の左上y
var w = 20; //長方形のwidth
var h = 20; //長方形のheight
var px = 20; //点のx
var py = 20; //点のy

function gaiseki(ax,ay,bx,by){
     return ax*by-bx*ay;
}

function pointInCheck(X,Y,W,H,PX,PY){
    var a = gaiseki(-W,0,PX-W-X,PY-Y);
    var b = gaiseki(0,H,PX-X,PY-Y);
    var c = gaiseki(W,0,PX-X,PY-Y-H);
    var d = gaiseki(0,-H,PX-W-X,PY-H-Y);
    if(a<0&&b<0&&c<0&&d<0) return true;
    else return false;
}

document.write(pointInCheck(x,y,w,h,px,py)); 


tureだから、内側にある。

2011年9月5日月曜日

ベクトルの外積

ベクトルの外積は便利だ。ベクトルAとベクトルBの外積を求めたときに値がマイナスであれば、ベクトルBはベクトルAの右側にいるということが分かる。

試してみよう。

ベクトルA(6,6)、ベクトルB(6,0)としよう。
外積は下記の式になる。

Ax * By - Bx * Ay

よって、6*0-6*6 = -36 となる。
確かにマイナスである。

それでは、ベクトルA(6,0)、ベクトルB(6,6)としてみよう。
6*6-6*0 = 36 となる。
確かにプラスである。

ベクトルにはもう一つの成立する式がある。
|A| * |B| * sinθ

ベクトルA(6,0)、ベクトルB(6,6)だとすると、
6 * 6ルート2 *sin(45度)になるはずなので、javascriptだと下記になる。

document.write(6*6*Math.sqrt(2)*Math.sin(45*Math.PI/180));



確かに36で一致する。

cosθの値をグラフにしてみる

0度〜360度のcosθの値をグラフに出してみよう。HTML canvasを使おう。




一番上が1を表している。真ん中が0で、一番下が-1である。一番左が0を表していて縦の目盛線は、45ずつに引いている つまり、0度はcosは1。それから角度が拡大するにつれてcosは徐々に小さくなっていき、90度だとcosは0になる。90度を超えるとマイナス数値になり、180度でcosは−1になる。180度を超えると逆に大きくなり、270度で0に戻る。270度を超えるとプラス数値になり、360度で1に戻る。

<canvas id="cosCanvas" width="365" height="110"></canvas>
<script type="text/javascript">
var cosCanvas = document.getElementById("cosCanvas");
var COSC = cosCanvas.getContext("2d");
var CW = cosCanvas.width, CH = cosCanvas.height;

var cos = 0;

for(var deg=0;deg<361;deg++){
    cos = Math.cos(deg*Math.PI/180);
    COSC.beginPath();
    COSC.fillStyle = '#ff5500';
    COSC.arc(deg,CH/2-cos*50, 1, 0, 360,false);
    COSC.fill();
}

COSC.beginPath();
COSC.strokeStyle = '#666666';
COSC.moveTo(0,CH/2); 
COSC.lineTo(CW,CH/2); 
COSC.closePath();
COSC.stroke();

for(var i=0;i<361;i+=45){
    COSC.beginPath();
    COSC.strokeStyle = '#666666';
    COSC.moveTo(i,0); 
    COSC.lineTo(i,CH); 
    COSC.closePath();
    COSC.stroke();
}
</script>

ベクトルの内積の研究(1)

ベクトルの内積を研究する。

まずV,Uをそれぞれ、(3,5)、(6,3)としよう。
ベクトルの内積は下記のようになるものをいうらしい。

V●U = Vx * Ux + Vy * Uy

よって、内積は、3*6+5*3=となる。

内積ではもう一つ成立する式があるらしい。
V●U = |V|*|U|*cosθ

ベクトルの大きさは、三平方の定理で求められるので、下記のようになる。
|V| = 3^2+5^2の平方根 =
|U| = 6^2+3^2の平方根 =

よって、cosθの値が求められる。javascriptでやると下記のようになる。

var vx = 3, vy = 5;
var ux = 6, uy = 3;
var VU = vx*ux+vy*uy;
var valV = Math.sqrt(3*3+5*5);
var valU = Math.sqrt(6*6+3*3);
var cos = VU / valV / valU;
document.write(cos); 

 となる。

javascriptのMath.acos(cos)という関数は、cosの値を入れると角度を返してくれるようなのでやってみよう。

var rad = Math.acos(cos);
document.write(rad);



これはラジアンだが、360 度表記にすると、度ということだ。

これが合っているかをatan2関数を使って確認してみよう。

var radV = Math.atan2(vy, vx);
var radU = Math.atan2(uy,ux);
document.write(radV - radU);



おお合っている。

しかし、内積が便利に使えるシーンがまだよく分からない。atan2関数があれば不要だったりするのかのう。

2011年9月4日日曜日

Javascript Math.sin(rad) サインを求める

Math.sin(rad)でサインを求められる。引数のradはラジアン。ラジアンがよく分からない。

コンピュータゲームの物理という私のバイブルによると、ラジアンは角度を表す方法の1つのようだ。我々は360度で一周する方法をいつも使っているが、この360という中途半端な数字は、そもそも1年が360日だと思われていた頃の名残であること以外に理由はないそうだ。プログラム等で計算する場合もっと使い易い角度表記がないものかと、考えられたのがラジアンであるということだ。

ラジアンは、半径1の円を基準にした角度表記であるそうだ。ラジアンは、半径1の場合に角度がθだった場合の円周の長さのことである。つまり、角度θを半径1の場合の円周の長さで表すのがラジアンである。ちなみに、π(約3.14)は円周の長さが直径の何倍になるかを表した数字である。例えば半径1であれば、直径は2なので円周は、2π(約6.28)である。

つまり、360度をラジアンで表すと、2πということである。180度ならばπである。1度であれば、π/180ということだ。

ラジアンのすごいところは、360度表記と比べて円周の長さという実際の値に対応しているところと、長さと角度が完全に完全に一対一になっているところだそうだ。あんまりすごさが分からないが、確かに360度の360という数字自体には何の意味もないことはよく分かった。コンピュータがラジアンを使いたくなる気持ちも分かる。

さてこれでラジアンが分かったので、最初のMath.sin関数をみると、引数がラジアンなので、Math.sin(30度)ではいけない。これをラジアンに変えて引数に投入するので、Math.sin(30*Math.PI/180)となる。

Javascript Math.PI 円周率を求める関数

document.write(Math.PI);

Javascript Math.atan2() X軸からポイントまでの角度を取得

Math.atan2(y, x)

http://www.ajaxtower.jp/js/math_class/index15.html
ここの説明が分かり易そうだ。
atan2関数は座標の逆正接(アークタンジェント)を計算して返します。引数に指定した原点と座標(x, y)、そして座標からX座標へ降ろした点の3点からなる三角形を対象として逆正接(アークタンジェント)を計算します。引数がY座標からとなっていることに注意して下さい。

例えば座標(1, 1)とした場合、角度は45度になりタンジェントはtan(45度) = 1 / 1 = 1となります。atan2関数はタンジェントの結果が1となるような角度を取得するために、タンジェントの結果ではなく座標を指定します。つまりこの関数は座標を指定することで、原点からその座標に引いた直線のX軸からの角度を取得することができます。

ちょっと試してみよう。

var x = 10, y = 10;
document.write(Math.atan2(y,x));



結果はラジアンなので、360度表記に直してみよう。
ラジアンπが180度なので、ラジアンをπで割って180で書ければいいだろう。

var x = 10, y = 10, rad, deg;
rad = Math.atan2(y,x);
deg = rad/Math.PI*180;
document.write(deg);



原点を0,0とすると、x,yが10,10なので、直角二等辺三角形になるということだな。

では、x=1,y=ルート3にしてみよう。

var x = 1, y = Math.sqrt(3), rad, deg;
rad = Math.atan2(y,x);
deg = rad/Math.PI*180;
document.write(deg);



やはり60度だな。HTML5 Canvasの場合は、左上を原点とするから、そこだけ気をつけないといけないな。

2011年8月28日日曜日

Java LinkedListのテスト(Android Game 敵と弾の当たり判定〜削除)

Androidでゲームを作っているのですが、敵も沢山いて、プレイヤーが撃った弾も沢山ある場合、敵毎に全ての弾と当たり判定を実施する必要があります。敵が弾に当たっている場合は、敵と当たっている弾を削除します。簡単に出来ると思っていたのですが、今これにハマっています。。よって頭を整理しながらまとめていきたいと思います。

敵と弾は数が自在に調整できた方がいいので、今回のテスト対象であります、LinkedListに格納していきます。ArrayListかLinkedListかどちらを選択するかについてですが、このページを見て、削除が多いのでLinkedListを使うことにします。敵はEnemyクラスのインスタンスになり、弾はBulletクラスのインスタンスになります。

まず、Enemyクラスは下記とします。
public class Enemy {
	float x, y, w = 30, h = 30;
	
	public Enemy(float X, float Y){
		x = X; y = Y;
	}
}

次に、Bulletクラスは下記とします。(今回はテストなので分かり易くする為に弾の大きさを敵と同じにしました。)
public class Bullet {
	float x, y, w = 30, h = 30;
	
	public Bullet(float X, float Y){
		x = X; y = Y;
	}
}

次に、EnemyクラスとBulletクラスを格納するためのLinkedListを2つ作ります。
private LinkedList<Enemy> enemys = new LinkedList<Enemy>();
private LinkedList<Bullet> bullets = new LinkedList<Bullet>();

次に、敵と弾のインスタンスを3つずつ作成し、LinkedListに格納します。インスタンス作成時の引数はx座標とy座標になりますので、最初の敵と最初の弾はまさに同じ場所にいて、その他の敵と弾は全く別の場所にいることになります。つまり、後程のコードで、うまく最初の敵と最初の弾だけ消えればゴールになります。
//敵の生成
enemys.add(new Enemy(10,10));
enemys.add(new Enemy(30,50));
enemys.add(new Enemy(100,100));
//弾の生成
bullets.add(new Bullet(10,10));
bullets.add(new Bullet(200,200));
bullets.add(new Bullet(300,300));

それでは、実際に当たり判定処理と、当たっている場合の削除をしてみます。想定するコードは下記です。
//敵と弾の衝突判定
Enemy eNow; Bullet bNow;
for(Iterator<Enemy> i = enemys.iterator(); i.hasNext();){
	eNow = i.next();
	for(Iterator<Bullet> j = bullets.iterator(); j.hasNext();){
		bNow = j.next();
		if(eNow.x<bNow.x+bNow.w && bNow.x<eNow.x+eNow.w &&
				eNow.y<bNow.y+bNow.h && bNow.y<=eNow.y+eNow.h){
			j.remove(); //弾を消す
			i.remove(); //敵を消す
		}
	}
}

上記コードを通した結果を下記のコードで表示してみます。
//結果の表示
System.out.println("実行結果");
for(Iterator<Enemy> i = enemys.iterator(); i.hasNext();){
	eNow = i.next();
	System.out.println("敵は("+(int)eNow.x+","+(int)eNow.y+")にいます。");
}
for(Iterator<Bullet> i = bullets.iterator(); i.hasNext();){
	bNow = i.next();
	System.out.println("弾は("+(int)bNow.x+","+(int)bNow.y+")にあります。");
}

さて結果は下記のようになりました。やはり問題ないようだな。しっかり想定どおり最初の敵と最初の弾のみ消えている。別の部分に問題があるってことだな。。
実行結果
敵は(30,50)にいます。
敵は(100,100)にいます。
弾は(200,200)にあります。
弾は(300,300)にあります。

試しに弾の位置を少し変えて実行してみよう。最初の弾の位置をずらしました。
//敵の生成
enemys.add(new Enemy(10,10));
enemys.add(new Enemy(30,50));
enemys.add(new Enemy(100,100));
//弾の生成
bullets.add(new Bullet(30,40));
bullets.add(new Bullet(200,200));
bullets.add(new Bullet(300,300));

実行結果は下記のとおり。これは敵・弾ともに消えているけど、敵はもう一匹消えるべきだな。最初の敵と2番目の敵に両方当たっているはずだからね。まあでも今は敵同士は離れているので、これが問題ではないな。
実行結果
敵は(30,50)にいます。
敵は(100,100)にいます。
弾は(200,200)にあります。
弾は(300,300)にあります。

2011年8月22日月曜日

Android 横スクロールゲームをつくってみる


地面とボールをつくりました。ボールは既にタッチするとジャンプするようになっています。このページで作成したタッチするとジャンプし、ジャンプ中にタッチすると2段ジャンプする仕様です。ちなみに、タッチをし続けるとジャンプ力が増すようになっています。これからこれをベースに横スクロールゲームを作ってみます。

雲を表示する



とりあえず適当に雲を作りました。現時点のコードを記載します。

2011年8月21日日曜日

Android SDK 横画面に固定する方法

AndroidManifest.xmlのタグのscreenOrientation属性にlandscapeを指定すればOK。
尚、エミュレーターの向きを横に回転させるには、macの場合、fn+control+F11でOK。

AndroidSDK タッチイベント onTouchEventの研究

Androidアプリはタッチイベントによってことが進むので、タッチイベントの正確な把握は必須であり、基本中の基本だ。時間をかけてじっくり研究することは必要なことだ。まだ全体像がAndroidSDKはよく分かっていないので、間違った記載があると思いますので、ご指摘いただければ幸いです。

http://goo.gl/1AcZh
この記事によると、
タッチイベントを取得するには、ActivityクラスのonTouchEvent()をオーバーライドします。引数には、MotionEventのインスタンスが渡されます。
とある。
また、
MotionEventは、getAction()を呼び出すことで、タッチアクション(DOWN/UP/MOVE/CANCEL)、getEventTime()を呼び出すことで、イベント発生時刻(ms)、getX()、getY()を呼び出すことで、タッチされたx、y座標、を取得することができます。
とある。

指一本しか使わない場合


指一本しか使わない場合はかなり単純に実装できそうだ。例えば、どこでもいいからユーザーがタッチすると、ジャンプするボールをつくることにしよう。長くタッチした場合にジャンプ力を増したりするケースが多いと思うが、今回はそのようなことは考慮しないことにしよう。タッチしたらジャンプするだけだ。

サンプルコードは下記になります。黒い画面に白いボールが一つ表示され、タッチするとボールが跳ねます。

2011年8月4日木曜日

HTML5 - Canvas アニメーション Walk


<canvas id="game2" width="500" height="270"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("game2");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;

var img_jero = [];
img_jero[0] = new Image();
img_jero[0].src = '/pic/jeronimo3.png';
img_jero[1] = new Image();
img_jero[1].src = '/pic/jeronimo2.png';
img_jero[2] = new Image();
img_jero[2].src = '/pic/jeronimo1.png';

var tm;
img_jero[2].onload = function() { 
 tm = setInterval(main,200);
}

var size_x = 50;
var size_y = 80;
var x = cw;
var y = 0;
var idx = 2;

function main(){
 ctx.clearRect(0,0,cw,ch);
 ctx.drawImage(img_jero[idx],x+250,y+175,size_x,size_y);
 ctx.drawImage(img_jero[idx],x,y);
 idx--;
 if(idx==-1){idx=2;}
 x=x-30;
 if(x<-300){x=cw;} 
}
</script>

2011年7月23日土曜日

HTML5 - Canvas アニメーション - 陣取りゲーム

マウスドラッグの向きにプレイヤー(青いボール)が動きます。動いている途中のパスが敵か自分に当たったらプレイヤーが死にます。ボールを動かして陣地を80%以上確保したらステージクリアです。敵を閉じ込めたら敵を倒せてボーナス点数がもらえます。(キーボード入力がきかないです。)

http://endo-yuta2.appspot.com/practice7



2011年7月3日日曜日

JavaScript 配列の比較(要素ごとにやらないといけないらしい)

Pythonだと余裕で出来た気がするんだけど、javascritpだと配列の比較は、要素ごとにやらないといけないんすかね。
何かいい方法あったら教えてください。

<script type="text/javascript">
var a = [0,1];
var b = [0,1];

//これだと違うよになる。
if(a == b)  alert('同じだよ');
else alert('違うよ');

//これなら同じだよになる。
if(a[0] == b[0] && a[1] == b[1])  alert('同じだよ');
else alert('違うよ');
</script>

配列の比較関数をつくるとしたらこんな感じでしょうか?

2011年6月28日火曜日

HTML5 - Canvas マウスイベント時の座標取得について(座標は小数点以下があるらしい)

こんな感じでマウスクリック時の座標を検知する。

var mouseX;
var mouseY;

//イベントリスナーの登録
canvas.addEventListener("mousedown", mouseDownHandler, false);

function mouseDownHandler(e) {
 var rect = e.target.getBoundingClientRect();
 mouseX =  Math.floor(e.clientX - rect.left);
 mouseY =  Math.floor(e.clientY - rect.top);
}

2011年6月26日日曜日

HTML5 - Canvas アニメーション ゲーム(2)

ゲーム(1)の改良版。右左で移動できて、上でジャンプできます。敵の熊達をよけながら、GUNで敵を全部倒せばクリアです。GUNはAボタンで撃てます。Blog上ではうまく動かないので、リンクはります。動作確認はパソコンのChrome,Firefoxで確認済みです。

http://endo-yuta2.appspot.com/practice3

code

HTML5 Canvas - 上からボールを落として跳ね返る

上からボールを落として跳ね返る様。重力があり跳ね返り係数があるのだ。オランダの科学者が重力はないといっており、他の著名科学者、物理学者が一定の賛同をしているようだ。これが本当なら重力がある前提で全ての物理の教科書がつくられている現代において、コロンブス並みの大発見になるかもとのことだ。僕は今覚えたばかりの重力を前提に跳ね返らせているわけだが、それでも自然にみえるものだ。何が正しくて何が本質かなどということはわからないものじゃ。

2011年6月21日火曜日

HTML5 - Canvas 自然なジャンプをする四角形

地球には重力がありますので、飛んでもしまいには落ちます。飛ぶときは初速度がありますが、この速度は毎秒9.8m/s2重力によって減速します。当然落下中は毎秒9.8m/s2速度が増します。この9.8m/s2を重力加速度といいます。9.8m/s2というのは大体の数値で、場所や色々な条件によってこの速度は微妙に変わります。この自然界の掟をプログラムのジャンプに反映させると、いかにも自然なジャンプに見えるのであります。

2011年6月20日月曜日

HTML5 - Canvas アニメーション ゲーム(1)

若干ゲームっぽくなってきたHTML5のCanvasアニメーションです。プレイヤーは青いボールに顔が書いてあるやつで、彼はカーソルキーで動かせます。上を押すとジャンプします。しかしジャンプしだしたら天井にぶつかって帰ってくるまで止まりません。右左を押すと右左に移動します。ジャンプ中も移動できます。敵はくま達です。くま達にぶつかった時点でゲームオーバーです。得点などはございません。ゲームオーバーになったらリロードするまでもはや動きません。

2011年6月18日土曜日

HTML5 - Canvas アニメーション くま(2)

くまのアニメーション2。ランダムでキャラ選ぶ。ランダムで向きと速度決める。ブログのトップページだと表示されない。。。見るときは単一ページからお願いします。

HTML5 - Canvas アニメーション くま(1)

くまのアニメーション。端までいったら跳ね返る。なぜかブログのトップページだと、くまが消えない。。見るときは単一ページに飛んでください。

2011年6月13日月曜日

HTML5 - Canvas パックマンのモンスターを描くJavaScript関数(2)

x,y座標ともに指定できるモンスター関数です。

サンプル



HTML5 - Canvas パックマンのモンスターを描くJavaScript関数(1)

パックマンを描くスクリプトを調べてモンスター関数を作成します。

xだけをインプットして、yは勝ってに決まってしまういけていないモンスター関数です。

サンプル



モンスター関数(1)の使い方

monster(ctx,x,collor)

モンスター関数(1)

function monster(ctx,x,collor){
    ctx.fillStyle = collor;
    ctx.beginPath();
    ctx.moveTo(x,x+33);
    ctx.lineTo(x,x+19);
    ctx.bezierCurveTo(x,x+11,x+6,x+5,x+14,x+5);
    ctx.bezierCurveTo(x+22,x+5,x+28,x+11,x+28,x+19);
    ctx.lineTo(x+28,x+33);
    ctx.lineTo(x+23.333,x+28.333);
    ctx.lineTo(x+18.666,x+33);
    ctx.lineTo(x+14,x+28.333);
    ctx.lineTo(x+9.333,x+33);
    ctx.lineTo(x+4.666,x+28.333);
    ctx.lineTo(x,x+33);
    ctx.fill();
    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.moveTo(x+8,x+13);
    ctx.bezierCurveTo(x+5,x+13,x+4,x+16,x+4,x+18);
    ctx.bezierCurveTo(x+4,x+20,x+5,x+23,x+8,x+23);
    ctx.bezierCurveTo(x+11,x+23,x+12,x+20,x+12,x+18);
    ctx.bezierCurveTo(x+12,x+16,x+11,x+13,x+8,x+13);
    ctx.moveTo(x+20,x+13);
    ctx.bezierCurveTo(x+17,x+13,x+16,x+16,x+16,x+18);
    ctx.bezierCurveTo(x+16,x+20,x+17,x+23,x+20,x+23);
    ctx.bezierCurveTo(x+23,x+23,x+24,x+20,x+24,x+18);
    ctx.bezierCurveTo(x+24,x+16,x+23,x+13,x+20,x+13);
    ctx.fill();
    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.arc(x+18,x+19,2,0,Math.PI*2,true);
    ctx.fill();
    ctx.beginPath();
    ctx.arc(x+6,x+19,2,0,Math.PI*2,true);
    ctx.fill();
}

ちなみにgoogleのPac-Manです。

2011年6月12日日曜日

HTML5 - Canvasで色々な絵をかく(ハートマークからパックマンまで)

Canvasリファレンスや、MDC Docsというやつのチュートリアルを参考に色々な絵をCanvasを使って書いてみよう。下の絵は全部他のサイトからパクってきたものだよ。ちゃんとパクリ先は明示しておきますよ。パクってきたのは、上2つのサイトですよ。HTML5 Canvasを使えば、ハートマークもマンガの吹き出しも、パックマンまでJavascript1つで描けるのだ。これでJavaScriptのTimer使えば、JavaScriptだけでアニメーションがつくれるのだ。もうFlashなんて不要だ。しかし、Canvasにはアニメーション機能がないので、Timerを使いながらすべてを消して全てを一から描写するというプロセスをアニメーションのコマ数分続けなくてはいけない為、あまり膨大なアニメーションをつくるのはお勧めできないらしい。膨大ってどのくらいなのかは分かりませんが、ゲームを作ろうと思ってもちょっと難しいのではないでしょうか?といっても、こんなことや、こんなこともできるらしい!すごい。

線を描く



<canvas id="canvas2" width="50" height="50"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas2");
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(0, 40);
    ctx.lineTo(40, 40);
    ctx.lineTo(40, 0);
    ctx.closePath();
    ctx.stroke();
</script>

四角を描く



<canvas id="canvas3" width="50" height="50"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas3");
    var ctx = canvas.getContext("2d");
    ctx.fillRect(0,0,40,40);
</script>

ハートマークを描く



<canvas id="canvas4" width="140" height="140"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas4");
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(75,40);
    ctx.bezierCurveTo(75,37,70,25,50,25);
    ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
    ctx.bezierCurveTo(20,80,40,102,75,120);
    ctx.bezierCurveTo(110,102,130,80,130,62.5);
    ctx.bezierCurveTo(130,62.5,130,25,100,25);
    ctx.bezierCurveTo(85,25,75,37,75,40);
    ctx.fill();
</script>

マンガの吹き出しを描く



<canvas id="canvas5" width="140" height="140"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas5");
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(75,25);
    ctx.quadraticCurveTo(25,25,25,62.5);
    ctx.quadraticCurveTo(25,100,50,100);
    ctx.quadraticCurveTo(50,120,30,125);
    ctx.quadraticCurveTo(60,120,65,100);
    ctx.quadraticCurveTo(125,100,125,62.5);
    ctx.quadraticCurveTo(125,25,75,25);
    ctx.stroke();
</script>

パックマンを描く


<canvas id="canvas6" width="150" height="150"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas6");
    var ctx = canvas.getContext("2d");
    roundedRect(ctx,12,12,150,150,15);
    roundedRect(ctx,19,19,150,150,9);
    roundedRect(ctx,53,53,49,33,10);
    roundedRect(ctx,53,119,49,16,6);
    roundedRect(ctx,135,53,49,33,10);
    roundedRect(ctx,135,119,25,49,10);

    // Character 1
    ctx.beginPath();
    ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false);
    ctx.lineTo(34,37);
    ctx.fill();

    // blocks
    for(i=0;i<8;i++){
      ctx.fillRect(51+i*16,35,4,4);
    }
    for(i=0;i<6;i++){
      ctx.fillRect(115,51+i*16,4,4);
    }
    for(i=0;i<8;i++){
      ctx.fillRect(51+i*16,99,4,4);
    }

    // character 2
    ctx.beginPath();
    ctx.moveTo(83,116);
    ctx.lineTo(83,102);
    ctx.bezierCurveTo(83,94,89,88,97,88);
    ctx.bezierCurveTo(105,88,111,94,111,102);
    ctx.lineTo(111,116);
    ctx.lineTo(106.333,111.333);
    ctx.lineTo(101.666,116);
    ctx.lineTo(97,111.333);
    ctx.lineTo(92.333,116);
    ctx.lineTo(87.666,111.333);
    ctx.lineTo(83,116);
    ctx.fill();
    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.moveTo(91,96);
    ctx.bezierCurveTo(88,96,87,99,87,101);
    ctx.bezierCurveTo(87,103,88,106,91,106);
    ctx.bezierCurveTo(94,106,95,103,95,101);
    ctx.bezierCurveTo(95,99,94,96,91,96);
    ctx.moveTo(103,96);
    ctx.bezierCurveTo(100,96,99,99,99,101);
    ctx.bezierCurveTo(99,103,100,106,103,106);
    ctx.bezierCurveTo(106,106,107,103,107,101);
    ctx.bezierCurveTo(107,99,106,96,103,96);
    ctx.fill();
    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.arc(101,102,2,0,Math.PI*2,true);
    ctx.fill();
    ctx.beginPath();
    ctx.arc(89,102,2,0,Math.PI*2,true);
    ctx.fill();

function roundedRect(ctx,x,y,width,height,radius){
  ctx.beginPath();
  ctx.moveTo(x,y+radius);
  ctx.lineTo(x,y+height-radius);
  ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
  ctx.lineTo(x+width-radius,y+height);
  ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
  ctx.lineTo(x+width,y+radius);
  ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
  ctx.lineTo(x+radius,y);
  ctx.quadraticCurveTo(x,y,x,y+radius);
  ctx.stroke();
}
</script>

BloggerでHTML5のCanvasのテスト

bloggerはHTML5使えるようなので、Google Chart Apiだけでなく、HTML5のCanvasで絵を書いていこうと思う。CanvasならJavaScriptのTimerを使えば簡単にアニメーションも作れるようだ。

とりあえずHTML5 Canvasのテスト




<canvas id="canvas1" width="50" height="50"></canvas>
<script type="text/javascript">
    var canvas = document.getElementById("canvas1");
    var context = canvas.getContext("2d");
    context.fillRect(0,0,40,40);
</script>

Canvasを勉強するためのサイト

2011年6月6日月曜日

google chart api Visualizationでフェルマー(2次関数の包絡線)

フェルマーでやりたかったのはこれだ。フェルマーは色々なことを発見したらしいが、これもフェルマーが発見したそうだ。下記グラフに描かれているのは、61本の直線ですが、全てy=x^2のグラフ上のある点の接線であります。結果的に、y=x^2のグラフが浮かび上がってくるのだ。こういうのを包絡線ともいうらしい。

y=x^2のグラフ上のある点を(a,a^2)とすると、その点の接線のy切片は、(0,-a^2)となるのだ。これはどの点をとってもそうなっているのだ!とフェルマーが発見したそうだ。すごいですね。この接線の傾きは、(a^2+a^2)/a = 2a^2/a = 2a ということで、2aになるのだ。
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
      google.load("visualization", "1", {packages:["corechart"]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        var data = new google.visualization.DataTable();
        
        var i ;
        var x;
        var y;
        var z = 0;
        var max = 61;
        var a;
        var c = 0;

        for(i=0; i<max;i++){
            data.addColumn('number', i);
        }

        data.addRows(max);
  
        for (a = -30; a < 31; a = a +1){
          for(x = -30; x < 31; x = x +1){
            y = 2*a*x-a*a;
            data.setValue(z, c, y);
            z = z+1;
          }
          z=0;
          c= c+1;
        }  
 
      new google.visualization.LineChart(document.getElementById('chart_div4')).
      draw(data, {width: 500, height: 400,vAxis: {maxValue: 10},legend:'none'});
}
</script>
<div id="chart_div4">
</div>

google chart api Visualizationの使い方

Visualizationを覚えよう。やはりjavascriptを使うようだが、若干複雑になっている。


上記は最初のサンプルになっている。普通のgoogle chart apiの最初のサンプルより、ちょっと奇麗で豪華になっている。ソースは下記だ。

<script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load("visualization", "1", {packages:["corechart"]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Year');
        data.addColumn('number', 'Sales');
        data.addColumn('number', 'Expenses');
        data.addRows(4);
        data.setValue(0, 0, '2004');
        data.setValue(0, 1, 1000);
        data.setValue(0, 2, 400);
        data.setValue(1, 0, '2005');
        data.setValue(1, 1, 1170);
        data.setValue(1, 2, 460);
        data.setValue(2, 0, '2006');
        data.setValue(2, 1, 860);
        data.setValue(2, 2, 580);
        data.setValue(3, 0, '2007');
        data.setValue(3, 1, 1030);
        data.setValue(3, 2, 540);

        var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
        chart.draw(data, {width: 400, height: 240, title: 'Company Performance'});
      }
</script>
<div id="chart_div"></div>

シンプルにしてみよう。



二次関数のグラフを作ってみよう
できた!すばらしい。ソースはこれ。
<script type="text/javascript">
      google.load("visualization", "1", {packages:["corechart"]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        var data = new google.visualization.DataTable();
        data.addColumn('number', 'y');
        
        var x;
        var y;
        var z = 0;
        var max = 1001;

        data.addRows(max);
  
        for (x = -500; x < 501; x = x +1){
          y = x*x;
          data.setValue(z, 0, y);
          z = z+1;
        }  
 
      new google.visualization.LineChart(document.getElementById('chart_div3')).
      draw(data, {width: 500, height: 400,vAxis: {maxValue: 10}});
}
</script>
<div id="chart_div3">
</div>
これでフェルマーも作れるはずだ。

2011年6月5日日曜日

フェルマー

せっかくだからなんかグラフにしようと思う。フェルマーが発見したのは、y=xの2乗のグラフ上のある点(a,a2乗)の接線のy切片は、-a2乗になるということだ。これはどの点をとってもそうなるらしい。google chart apiの問題は数値を0〜100しか扱えないことだ。なんとマイナスが扱えないらしい。本当か。。。それじゃあそもそもフェルマーの確認ができないじゃないか。不便な世の中だ。

念のためgoogle chart apiを試してみよう。y=-3x+10という数式のグラフを作ってみよう。xは1〜10まで1ずつアップさせることにしよう。そうするとyは、7,4,1,-2,-5,......という風になる。xが4以降は残念な全部0という結果を想定している。



なんとマイナスの数値は表示されないのだ。残念だ。念のため、マイナスにならない1次関数グラフを作ってみよう。y=3x+10だ。



でも、y=-3x+10をx=10までグラフ化するだけなら、擬似的に見せることは可能だ。x=10なら、y=-20だから、y=-3x+30のグラフをプロットして、目盛をいじればいいのだ。



上記と同じように、全ての値に50を足して 、目盛で-50すりゃいいのだ。



・・・これじゃあx=7までしか表示できない。なんて使えないapiなんだ。。こうなったら、x=-3〜3までにしよう。そりゃそうだ。そうじゃないと放物線にならないし。



google chart apiはおらをばかにしているのか??あくまでy軸は内部的には0-100の範囲で表示するのか?人類が使用する数字は0-100でマイナスはないと思っているのか?それとも俺がその活用法をしらないだけなのか?あるいは、何か少しでもまともに使おうとすると、もう一ランク上のVisualizationを使う必要があるのか??

google chart apiは0-100ではなく、0-4095まで扱えるそうだ。しかしマイナスは扱えないし、4095を扱おうとすれば独自表記を覚えないといけないし、変換するプログラムを書かないといけない。これはもう速やかにVisualizationというのを覚えよう。

フェルマーは、Visualizationを覚えてからだな。

Google chart apiのURLを作るJavaScript

Google chart apiのURLを作るpythonスクリプトを今日中につくるぞ。やりたいのは、超低レベルなんだけど、本当に2次関数というのは放物線を描くのかを自分でデータをつくってグラフ化して試したいのだ。一次関数が一直線なのは当たり前に分かるからな。

Google chart apiはURLを決められたルールで生成し、それにアクセスすることでグラフのイメージデータが返ってくる仕組みだ。グラフの種類、色、目盛、数値などをルールに沿っていれていけばいい。getだとURLの長さが結構制限が強いみたいだけど、Postで呼び出せばその制限は緩和されるようだ。postでどうやって呼び出すのかは、今のところわかっていない。getでどの程度の数の数値データを扱えるのかも分からない。

y=2x+4というのは一次関数だべよ。これは単純だ。x=0のときy=4で後は正比例のまっすぐなグラフになるのは一目瞭然だ。y=2x2(二乗)+4というのは2次関数だべよ。これはx=0のときy=4だけど、その後の動きが分からないのだ。100〜1000個くらいのデータをぶち込んで滑らかな放物線を描いてみたい。

どうやらGoogle chat apiには、数値データを一個一個ぶち込む方法と、数式をぶち込む方法の2種類あるように思える。

https://chart.googleapis.com/chart?cht=lc&chd=t:-1&chs=250x150&chco=FF0000&chfd=0,x,0,11,0.1,sin(x)*50%2b50&chxt=x,y

これは非常に短いURLだが、sin(x)*・・・・という数式めいたものが入っており、イメージ出力するとこうなる。



なんて便利なツールなんだ。そして奇麗な曲線が一瞬で描かれるのだ。すばらしい。
数式でグラフを作れるのは便利だけど、俺はその数式からくるグラフが本当なのかを実感したいので、数式をインプットするのは今の俺にとっては無意味だ。だから、数式のxをずらしながら、100ないし1000のyを出して、インプットしたxとアウトプットしたyの組み合わせを100ないし、1000作るのだ。その組み合わせを元にURLを自動生成するプログラムをpythonで作るのだ。

Google Chart APIのURLの仕組み

仕組みを試しながらマスターしよう。https://chart.googleapis.com/chart?が基本URLのようだ。cht=lcはチャートの種類を指定しているようだ。2次関数にせよグラフはラインチャートでOKなんで、cht=lcでOKだ。円グラフや地図の国別の色の濃さで国別シェアなんかも可視化できるようだ。今度試そう。

http://code.google.com/intl/ja/apis/chart/image/docs/gallery/line_charts.html

このページにいろいろと詳しい説明がのっているようだ。

https://chart.googleapis.com/chart?cht=lc&chs=200x125&chd=t:40,60,60,45,47,75,70,72

これは恐らく最もシンプルなchartApiの使いかただろう。chs=200x125というのは、画像の大きさのようだ。イメージ出力するとこうなる。



ためしに、chs=500x250にするとこうなる。



chd=t:40,60,60,45,47,75,70,72というのは、数値データそのものだ。8個あるとおり、グラフの点も8個あるのだ。t:というのはよくわからないので後で調べよう。これはすなわちy軸の数値データであり、x軸の数値データは記載がない。xは一定間隔という前提で、yの値のみインプットしているのだ。二次関数のグラフの形を確認するにあたっては、xの値を明示する必要がないから、はっきりいって、このシンプルな形態にそって、データを1000個いれさえすれば終わりだ。なんて簡単なんだ。xが-500〜500まで(1刻み)のyをプロットすればいいのだ。こういうのをきっとプロットというのだろう。

まあpythonで作るのは結構なことだけど、javascriptでつくっちゃえば、このBlog上で全てを完結できるので、せっかくだから、javascriptでやるか。まずはy=x2乗を出すぞ。

<script type="text/javascript">
<!--
var x;
var y;
var txt = '';

for (x = -500; x < 501; x = x +1){
    y = x*x;
    if(txt == ''){txt = y;}
    else{txt = txt+','+y;}
}
document.write('<img src="https://chart.googleapis.com/chart?cht=lc&chs=500x250&chd=t:',txt,' "/>');
// -->
</script>

これで1000個のデータをプロットしてみたけど、エラーになった。
too largeだそうだ。ということは数を減らしてみよう。そして、postでアクセスする方法も調べよう。



300個のデータならいけるらしい。上記はxが-150〜150までの、y=x2乗のグラフだ。しかし、何か天井にひっかかっているようだ。嘘のグラフを表示するとは何事。。恐らく、目盛を明示しないとこういったバグが生じるんだろう。最大数値を150*150=22500を与えれば天井のつっかかりはなくなるはずだ。



なくならない。理由が分かった。google chart apiは数字は0-100までしか扱えないらしい。100以上は100として扱うらしい。よって、天井ができるのだ。。。めんどうだ。扱う数値は、係数100/22500をかけることにしよう。

100/22500の係数をかけると、数字1つ1つの文字列数が多くなるので、エラーになる。数字の数で制限してるわけではなく、URLの長さで制限しているからだ。POSTでどの程度緩和されるのか分からないけど、結構めんどうな仕組みだ。よって、-50〜50のxを元に、グラフを作った。yは、0.04 の係数をかけて出している。よって、x=50のときのy2500が100になるのだ。

<script type="text/javascript">
<!--
var x;
var y;
var z = 0.04;
var txt = '';

for (x = -50; x < 51; x = x +1){
    y = x*x*z;
    if(txt == ''){txt = y;}
    else{txt = txt+','+y;}
}
document.write('<img src="https://chart.googleapis.com/chart?chxt=x,y&chxr=0,-50,50|1,0,100&cht=lc&chs=500x250&chd=t:',txt,' "/>');
// -->
</script>




Google Chart ApiをPOSTで使う

POSTで使う方法はここに詳細が載っている。

GETで使う場合は超お手軽だが、2kしか許されない。POSTは若干めんどくさいが、16Kまで許されるのだ。URLの長さが8倍までいいってことだろう。また数字を短縮する独自の表記法もあるようで、それを使えばもっとたくさんのデータを扱ってグラフを作ることができるだろう。ただ独自の表記を覚えるのがめんどくさそうだし、小数点以下にも対応していなそうだ。(多分)

pythonでPNGを吐き出すchart.pyをつくって、htmlから<img src="chart.py" />というように使うのだ。
これなら入力値に応じたグラフをリアルタイムに表示することも可能だ。データも8倍使えるしな。でもいずれにせよ制限があるから、使い方によっては、matplotlibのがやっぱりいいのかもしれない。ただ、GAEでmatplotlibが使えるのかは不明だ。

2011年5月25日水曜日

GAE 多対多モデルのデータ削除について

Google app engin の datastoreで多対多を下記ページなどを参考につくりました。
http://code.google.com/intl/ja/appengine/articles/modeling.html

グループとユーザの関係を多対多で構築しています。models.pyの内容は下記になります。
※私は、GAEをPythonとDjango(パッチ)でつくっています。

class Users(db.Model):
    name= db.StringProperty()
    group=db.ListProperty(db.Key)

class Groups(db.Model):
    name=db.StringProperty()
    owner=db.IntergerProperty()

グループのオーナーがグループを削除する為のスクリプトを書こうと思っているのですが、グループを削除する際に、参加しているユーザーのgroupから、削除するグループのkeyを削除する必要があります。これについて最も効率的な方法を教えていただけますでしょうか。

GAEはデータの変更・削除に結構時間がかかります。仮に数万人所属しているグループがあったとし、このグループをオーナーが削除する場合、グループに所属しているユーザーのgroupに全てアクセスし、該当グループのkeyを削除する操作は非効率(30秒を超える可能性が十分あるの)ではないかと思っています。

宜しくお願いします。

投稿日時 - 2011-05-22 17:11:48

と、OkWebで質問した。
下記を回答いただきました。

GAEは、ちょっと特殊ですからね……。あんまりいい方法とは思えないのですが、削除するときは、単純にGroupsのownerをクリアするだけにしてはどうでしょうか(ownerが、グループの作者を示すデータですよね?)検索時に、グループのownerがないときは「そのグループは抹消済み」として表示しないようにするなど対処しておくわけですね。

そして、それとは別に、CRONなどを使って定期的にownerが空のGroupsを検索し、そのGroupsが保管されているUsersを少しずつ(一度に最大1000ずつとか)取り出してはgroupを更新していく、というのはどうでしょうか。いわば、ガベージコレクションをCRONで作るわけです。あるいは、最悪、手動で定期的に削除してもよいでしょうし……。

残念だけどそういうことなのか。

2011年5月22日日曜日

GAE Keyを文字列で取得した場合にオブジェクトを取り出す方法

将来の俺ならこのタイトルでわかるだろう。

GAEのmodelにはkeyがあります。これは各エンティティに固有のキーです。これはKeyオブジェクトのインスタンスですが、文字列に変更できます。よって、サイト上でこの文字列をやり取りし、後程このkey文字列をもとにエンティティを取得することができます。db.Key(文字列)によって、文字列はKeyオブジェクトのインスタンスに変わります。後は、db.get(Keyインスタンス)とすることで、エンティティが取り出せます。

obj = MyModel(name="Foo")
self.response.write('%s' % (str(obj.key()), obj.name()))

# ...

key_name = self.request.get('key')
obj = db.get(db.Key(key_name))

データの作成、取得、削除 - キーを使用したエンティティの取得

jQuery 将来的にも適用されるイベントハンドラの登録

基本的にjavascriptが読み込まれた時点で存在しない要素には、イベントハンドラは適用されない。
将来発生する要素にもイベントハンドラを適用したい場合は、liveを使う。

live(type, fn)

jQuery日本語リファレンス - live(type, fn)

登録されたイベントは、現在および将来的にも、セレクタにマッチする全ての要素に適用されます。
カスタムイベントに対してbindすることも可能です。

この関数で指定できるイベントは、次の通りです:
click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, keydown, keypress, keyup

現時点ではサポートしていないイベントは、次の通りです:
blur, focus, mouseenter, mouseleave, change, submit

bindとほぼ同様の関数ですが、ハンドラ登録時にマッチする要素だけでなく、永続的にイベント発生時点でマッチする要素に反応する点が異なります。

サンプル
<script type="text/javascript">
$(function(){
 $('.gp_del').live("click", function(){del_group(this.name);});
});
</script>

2011年4月9日土曜日

GAEのDatastoreのkey_nameで引っぱってきて上書きするとき

最悪なタイトルだが、きっとこれで将来の俺なら分かるだろう。

Horie(key_name='hf1')

これは、key_nameがhf1である新しいHorieを作っているのであり、これをputすると、もし既にkey_nameがhf1であるデータが保存されている場合は、上書きされる。新しいデータはkey_name以外は空のデータであるので、上書きされた瞬間に今まで保存されたデータは消えてなくなるのだ。

Horie.get_by_key_name('hf1')

これは、key_nameがhf1であるデータを引っぱってきているのであり、これをputすると、引っぱってきたデータに変更を加えずにputしているだけなので、何にもかわらないのだ。

2011年4月3日日曜日

jQuery 要素の属性を取得する

<div class="pic1"><img id="hf36" src="http://hoge.hoge/hoge.jpg" /></div>

上記のようなHTMLがあったときに、pic1クラスの子供のimg要素のid属性を取得するには、こうやってやる。

var key = $('.pic1 > img').attr('id');

上記でid属性の値が取得できる。変数keyを宣言しつつ、keyにid属性の値を格納している様。

2011年3月27日日曜日

jQueryのAjaxの基本型

jQueryでAjaxを使う場合の基本型。
windowがloadされたら、get_pic()が呼び出される。
Ajaxでjson形式のデータを受け取りたい場合は、dataTypeをjsonにする。
指定されたURLにアクセスした結果をdataに格納する。
dataが空でなければ、dataの0番目をアラートで表示する。
※上記は、dataがリストデータであることが前提。

<script type="text/javascript">
$(function(){
 $(window).load(function(){get_pic();});
});
function get_pic(){
    $.ajax({
        dataType: "json",
        url: "/get_pic",
        success: function(data){
         if(data != '') {
                alert(data[0]);
            }
        }
    });
}
</script>

GAEのcron

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

cron.yamlに下記のように記載する。

cron:
- description: daily summary job
  url: /tasks/summary
  schedule: every 24 hours
- description: monday morning mailout
  url: /mail/weekly
  schedule: every monday of month 09:00
  timezone: Australia/NSW

scheduleは下記の仕様が使える。

every 5 minutes
every 12 hours
2nd,third mon,wed,thu of march 17:00
every monday of month 09:00
1st monday of sep,oct,nov 17:00

cronで使うURLにユーザがアクセス出来ないようにするには、app.yamlに下記のように、login: admin をハンドラ設定に追加する。

application: hello-cron
version: 1
runtime: python
api_version: 1

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

Python 文字列の最後の空白文字(改行文字)を削除する

http://memo.jj-net.jp/182

rstrip()は行末の空白文字を取除く.空白文字とはstring.whitespaceで定義されているように '\t', '\n', '\r', '\v', '\f' である.本当に改行コードだけを削除する場合は,rstrip()に削除する文字を引数として渡せばよい.

Horie(key_name='hf'+str(no),userid=id,name=name.rstrip(),pic=pic).put();

GAE/PythonでtwitterAPIを使ってホリエモンのフォロワーをデータベースに登録している様
HorieというKindのnameはStringPropertyなので、複数行の登録は不可。
しかし、twitterのnameにはたまに改行文字が含まれている為、rstrip()を使ってそれを削除している。

2011年3月17日木曜日

Mac eclipseでGAE開発する為のPydev(Python2.5)の設定

eclipseをインストールして、Pydevプラグインをインストールすれば、eclipse上でpython開発ができますね。最近のPydevはGAEプロジェクトもついているのでGAEの開発をeclipse上でPythonでできますね。

GAEはPython2.5しかサポートしてませんが、Mac OS X 10.6.1はPython2.6が標準になってるので、eclipse上でpythonのインタープリターをpython2.5に設定しないといけませんね。やり方は下記であります。

eclipseインストールの後に、Pydevをインストールし終わったら、
eclipseのメニューバーから下記のとおり選択していきます。
[Eclipse] > [環境設定] > [Pydev] > [インタープリター - Python]

するとPython インタープリターという画面につきます。


右上の新規ボタンを押します。
次に参照ボタンを押します。
そして、下記ファイルを選択します。

/System/Library/Frameworks/Python.framework/Versions/2.5/bin/python2.5

あとはOKOKしていけばよい。
以上。

2011年3月16日水曜日

Mac ターミナル上のPython コマンド一覧

usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-B : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x
-c cmd : program passed in as string (terminates option list)
-d : debug output from parser; also PYTHONDEBUG=x
-E : ignore PYTHON* environment variables (such as PYTHONPATH)
-h : print this help message and exit (also --help)
-i : inspect interactively after running script; forces a prompt even
if stdin does not appear to be a terminal; also PYTHONINSPECT=x
-m mod : run library module as a script (terminates option list)
-O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x
-OO : remove doc-strings in addition to the -O optimizations
-Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew
-s : don't add user site directory to sys.path; also PYTHONNOUSERSITE
-S : don't imply 'import site' on initialization
-t : issue warnings about inconsistent tab usage (-tt: issue errors)
-u : unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x
see man page for details on internal buffering relating to '-u'
-v : verbose (trace import statements); also PYTHONVERBOSE=x
can be supplied multiple times to increase verbosity
-V : print the Python version number and exit (also --version)
-W arg : warning control; arg is action:message:category:module:lineno
-x : skip first line of source, allowing use of non-Unix forms of #!cmd
-3 : warn about Python 3.x incompatibilities
file : program read from script file
- : program read from stdin (default; interactive mode if a tty)
arg ...: arguments passed to program in sys.argv[1:]

Other environment variables:
PYTHONSTARTUP: file executed on interactive startup (no default)
PYTHONPATH : ':'-separated list of directories prefixed to the
default module search path. The result is sys.path.
PYTHONHOME : alternate directory (or :).
The default module search path uses /pythonX.X.
PYTHONCASEOK : ignore case in 'import' statements (Windows).
PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.

2011年3月11日金曜日

GAEのDatastoreのkey_name

すべてのエンティティは識別子を持っています。アプリケーションは、インスタンス コンストラクタに key_name 引数(str 値)を指定して、キーに使用するためにアプリケーション自身の識別子を割り当てることができます。

s = Story(key_name="xzy123")

key_name は Unicode 文字列(str 値が ASCII テキストに変換されたもの)として格納されます。key_name は先頭に数字を持つことができません。また、__*__(2 つのアンダースコアで始まり、終了する)の形式を取ることもできません。ユーザーのアプリケーションは、ユーザーが送信したデータをデータストア エンティティ キー名(メール アドレスなど)として使用するには、既知の文字列を前に付けるなどの方法で、その値を最初にサニタイズして、上記の要件を満たす必要があります。

key_name が指定されていない場合、エンティティが初めてデータストアに格納されるときに数値 ID が割り当てられます。

パス、種類、名前や数値 ID を含んだエンティティの完全なキーは、一意であり、エンティティに固有のものです。完全なキーはエンティティがデータストアに作成されたときに割り当てられ、どの部分も変更することができません。

個別の 2 つのエンティティのキーは、少なくとも一部が異なりさえすれば、類似した部分があっても構いません。たとえば、親が違っていれば、2 つのエンティティが同じ種類と名前を持つことができます。同様に、2 つのエンティティが同じ名前と親を持っていても(または親なしでも)、種類が異なっていれば構いません。

アプリケーションは、エンティティを作成した順番に数字を増やして割り当てる数値 ID に依存しないようにしてください。この方法は一般的ですが、保証はされません。

2011年2月27日日曜日

GAEの全文検索

http://www.ianlewis.org/jp/gae-hackathon-disc-3-jp
このページにGAEで全文検索する為のコードが紹介されている。全文検索は今僕は不要だが、とりあえずメモっておこう。すごく詳しそうな人のページだ。

DatastoreのTips

http://d.hatena.ne.jp/kazunori_279/20090617/1245212016
このページにDatastoreのTipsが色々書いてある。読んで覚えよう。
ところで、DataStoreで部分一致検索をしたいんだけど、どうすればいいんだろう?今情報探し中。

GAEの検索パターン

http://d.hatena.ne.jp/knj77/20100313/1268447499
このサイトに、GAEの検索方法について、書いてあったのでメモ。GAEは複雑な検索をするにはインデックスを作らないといけないけど、場合によっては全てをインデックス作成するのは非現実なことがあります。その場合の問題回避方法として最も優れた方法だと思われていることが書いてあります。

2011年2月26日土曜日

Djangoでチェックボックスの複数の値を読み取る

セレクトボックスや、チェックボックスで複数チェックされてリクエストされた場合に、チェックされた全ての値を読み取るには、

request.POST.getlist(キー)

とやる。キーに対応する全ての値がリストで貰える。

その他、Djangoのリクエストオブジェクトを参照。

GAEの開発サーバのデータを消去する

dev_appserver.py --clear_datastore {{app名}}
でいいらしい。

2011年2月22日火曜日

GAEの開発サーバのDashboardをみる

今まで知らなかった。超便利だ。
http://localhost:8000/_ah/admin/datastore

GAEのbulk loaderツールを使って駅データのCSVをデータストアにアップロードする

駅データのcsvファイルをGAEのデータストアにアップロードしたい。

csv等をデータストアに一括アップロードする場合、bulk loaderツールを使う。bulk loaderはappcfg.pyコマンドから使用できる。bulk loaderツールは、remote_apiを使用してGAEアプリのデータストアに一括アップロードする。

remote_apiが設定されていない場合は、まず最初に設定する必要がある。 http://bit.ly/9YoNpV に2種類の設定方法が書いてある。

remote_apiの設定が終わったら、次にbulk loaderの設定をする必要がある。bulk loaderの設定をするには、まずbulkloader.yamlを自動生成する。

appcfg.py create_bulkloader_config --filename=bulkloader.yaml {directory}
※{directory}はアップロードしたいアプリのディレクトリ名を入れる。

これによって、bulkloader.yamlが自動生成されるので、生成されたファイルを開き、編集する。
但し編集の前に、データの仕様を決めておく必要がある。僕はDjangoパッチを使っているので、models.pyに下記クラスを記入した。駅データのCSVを開いて、各カラム名とその要素のデータ仕様に合わせたもの。

class Station(db.Model):
    rr_cd = db.IntegerProperty()
    line_cd = db.IntegerProperty()
    station_cd = db.IntegerProperty()
    line_sort = db.IntegerProperty()
    station_sort = db.IntegerProperty()
    station_g_cd = db.IntegerProperty()
    r_type = db.IntegerProperty()
    rr_name = db.StringProperty()
    line_name = db.StringProperty()
    station_name = db.StringProperty()
    pref_cd = db.IntegerProperty()
    lon = db.FloatProperty()
    lat = db.FloatProperty()
    f_flag = db.IntegerProperty()

上記データ仕様に合わせて、bulkloader.yamlを編集する。編集方法は、 http://bit.ly/9YoNpV に詳細が書いてあるが、編集結果は下記のとおり(編集したのは、transformers:以下の行)。

# Autogenerated bulkloader.yaml file.
# You must edit this file before using it. TODO: Remove this line when done.
# At a minimum address the items marked with TODO:
#  * Fill in connector and connector_options
#  * Review the property_map.
#    - Ensure the 'external_name' matches the name of your CSV column,
#      XML tag, etc.
#    - Check that __key__ property is what you want. Its value will become
#      the key name on import, and on export the value will be the Key
#      object.  If you would like automatic key generation on import and
#      omitting the key on export, you can remove the entire __key__
#      property from the property map.

# If you have module(s) with your model classes, add them here. Also
# change the kind properties to model_class.
python_preamble:
- import: base64
- import: re
- import: google.appengine.ext.bulkload.transform
- import: google.appengine.ext.bulkload.bulkloader_wizard
- import: google.appengine.ext.db
- import: google.appengine.api.datastore
- import: google.appengine.api.users

transformers:

- kind: Station
  connector: csv
  connector_options:
    encoding: euc-jp
  property_map:
    - property: rr_cd
      external_name: rr_cd
      import_transform: int

    - property: line_cd
      external_name: line_cd
      import_transform: int

    - property: station_cd
      external_name: station_cd
      import_transform: int

    - property: line_sort
      external_name: line_sort
      import_transform: int

    - property: station_sort
      external_name: station_sort
      import_transform: int

    - property: station_g_cd
      external_name: station_g_cd
      import_transform: int

    - property: r_type
      external_name: r_type
      import_transform: int

    - property: rr_name
      external_name: rr_name

    - property: line_name
      external_name: line_name

    - property: station_name
      external_name: station_name

    - property: pref_cd
      external_name: pref_cd
      import_transform: int

    - property: lon
      external_name: lon
      import_transform: float

    - property: lat
      external_name: lat
      import_transform: float
    
    - property: f_flag
      external_name: f_flag
      import_transform: int

編集したら、下記コマンドを打って、アップロードする。

appcfg.py upload_data --config_file=bulkloader.yaml --filename=m_station.csv --kind=base_station {directory}

追記2011/02/24:--kind=Stationをbase_staitionに修正した。Djangoの場合特有かもしれないが、baseという名のapp内のmodels.pyでStationクラスを作った場合、kindは、base_stationになる。これ知らなかった。


※{directory}はアップロードしたいアプリのディレクトリ名を入れる。
※m_station.csvは、csvファイルの名前にする。これは駅データからダウンロードしたcsvのファイル名。
これで、アップロード完了。下記のように、10745データを9分位かけてアップロードした。

[INFO    ] 10745 entities (10684126 bytes) transferred in 535.9 seconds
[INFO    ] All entities successfully transferred

ちなみに、ローカルの開発サーバにアップロードするコマンドは下記。

appcfg.py upload_data --config_file=bulkloader.yaml --filename=m_station.csv --kind=base_station --url=http://localhost:8000/remote_api {directory}

追記2011/02/24:--kind=Stationをbase_staitionに修正した。Djangoの場合特有かもしれないが、baseという名のapp内のmodels.pyでStationクラスを作った場合、kindは、base_stationになる。これ知らなかった。



開発サーバへのアップロードの場合は、10745データを3.5分位でアップロードした。

[INFO    ] 10745 entities (4096563 bytes) transferred in 211.9 seconds