Google Calendar API の Push Notification 機能を使うと、Google カレンダー側の更新をリアルタイムに自分のアプリケーションに反映することができるらしいので試してみました。
Push Notification 機能を使うためには、通知を受け取る URL を Google 側に事前に登録しておく必要があるのですが、手順はドキュメントに書いてあるため省略します。
今回、クライアント用のライブラリには google-api-client を使用します。
# Gemfile gem 'google-api-client'
Push 通知を受け取るために、まず Channel と呼ばれるものを作成する必要があります。
変更の通知を受け取りたいカレンダーを指定して Channel を作成することで、そのカレンダー内のイベントが変更されるたびに、Google 側から HTTPS でリクエストが飛んでくるようになります。
ちなみに Channel は変更の通知を受け取りたいカレンダーごとに作成する必要があるようです。
require 'google/api_client' client = Google::APIClient.new client.authorization.client_id = '' client.authorization.client_secret = ' ' client.authorization.refresh_token = ' ' client.authorization.fetch_access_token! service = client.discovered_api('calendar', 'v3') client.execute!( api_method: service.events.watch, parameters: { calendarId: ' ' }, body_object: { id: ' ', type: 'web_hook', address: ' ' } )
<CLIENT_ID>
と <CLIENT_SECRET>
は Google Cloud Console で確認できます。<YOUR_REFRESH_TOKEN>
は OAuth 2.0 認証後に取得できる refresh_token
を指定します。<YOUR_CALENDAR_ID>
に変更の通知を受け取りたいカレンダー ID を指定します。
ちなみに、ここで自分のカレンダー ID を調べることができます。<CHANNEL_ID>
には任意の文字列を指定します。
複数の Channel を作成する場合は Channel 間でユニークになっているほうがいいでしょう。
ちなみに、自分はSecureRandom.uuid()
を使っています。<YOUR_RECEIVING_URL>
には通知を受け取りたい URL を指定しておきます。
リクエストに問題がなければ、以下のようなレスポンスが返ってきます。
{ "kind": "api#channel", "id": "4a0d04ac-b5df-4a4d-ba03-65d121fbc100", "resourceId": "ROKWIDXHB6wV6KdPqFWmo_lTTLF", "resourceUri": "https://www.googleapis.com/calendar/v3/calendars/sei@me.com/events?alt=json", "expiration": "1384317818000" }
ここで大事なのはid
とexpiration
です。id
は先ほど指定した<CHANNEL_ID>
の値が入ってきます。expiration
は Channel の有効期限で、これが切れると通知が飛んでこなくなります。
もし、引き続き通知を受け取りたい場合は、期限が切れる前に Channel を作成し直す必要があります。
Channel 作成後、カレンダーに変更があると以下のようなヘッダー付きのリクエストが飛んできます。
HTTP_X_GOOG_CHANNEL_EXPIRATION: Sun, 10 Nov 2013 11:08:46 GMT HTTP_X_GOOG_CHANNEL_ID: 4a0d04ac-b5df-4a4d-ba03-65d121fbc100 HTTP_X_GOOG_MESSAGE_NUMBER: 235226900 HTTP_X_GOOG_RESOURCE_ID: ROKWIDXHB6wV6KdPqFWmo_lTTLF HTTP_X_GOOG_RESOURCE_STATE: exists HTTP_X_GOOG_RESOURCE_URI: https://www.googleapis.com/calendar/v3/calendars/sei@me.com/events?alt=json
HTTP_X_GOOG_CHANNEL_ID
は先ほど登録した<CHANNEL_ID>
です。
通知では変更の内容までは教えてくれないので、通知が来る度に API を使って更新された内容を取得する必要があります。
<YOUR_CALENDAR_ID>
にカレンダー ID を指定して更新があったイベントを取得します。
複数の Channel をもつアプリケーションを構築する場合は、通知内のHTTP_X_GOOG_CHANNEL_ID
の値をもとにカレンダー ID を参照できるようにしておく必要があります。
require 'google/api_client' client = Google::APIClient.new client.authorization.client_id = '' client.authorization.client_secret = ' ' client.authorization.refresh_token = ' ' client.authorization.fetch_access_token! service = client.discovered_api('calendar', 'v3') client.execute!( api_method: service.events.list, parameters: { calendarId: ' ', updatedMin: 1.minute.ago.to_datetime.rfc3339 } )
変更があったイベントをピンポイントで取得できるわけではないので、直近で変更があったイベントを検索する形になります。
ヘタすると取りこぼしの危険があるので、多めに 1 分前以降の変更を取得するようにしていますが、もっといい方法があればなぁ、という感じです。
(Google さん、変更があった時刻も教えてくれるとうれしいですっ!)
グローバルオブジェクトをガンガン汚染してる得体の知れない HTML をメンテせざるを得ないこともあったりします。(ちょっと韻踏んでる)
これでグローバルリークしているプロパティを探せます。ちなみに Tumblr のダッシュボードはこんな感じ。
["ENVIRONMENT","Tumblr","tinyMCE","_sf_startpt","language_for_tinymce","l10n_str","localized_str","i","localized_str_ajax_error","localized_str_loading","localized_str_remove_tag","localized_str_promote_this_post_in","localized_str_promote","localized_str_promote_warning_none","localized_str_promote_warning_singular","localized_str_promote_warning_plural","localized_str_markdown","localized_str_html_enabled","localized_str_bold","localized_str_italic","localized_str_strikethrough","localized_str_enter_the_url","localized_str_insert_link","localized_str_adding_tags","localized_str_removing_tags","localized_str_only_100_posts","localized_str_select_posts_to_edit","localized_str_select_posts_to_delete","localized_str_enter_tags_to_add","localized_str_select_posts_to_tag","localized_str_wait_for_last_operation","localized_str_confirm_delete_selected_posts","localized_str_my_posts","localized_str_search_tumblr","localized_str_my_dashboard","localized_str_search_posts","localized_str_search_help","localized_str_search_by_tag","localized_str_search","localized_str_this_tumblelog","localized_str_you_answered","localized_str_thank_you","localized_str_confirm_block_this_person","localized_str_cancel","localized_str_reply","localized_str_250_max","localized_str_confirm_block","localized_str_new_posts","localized_str_over_max_file_size_mb","localized_str_empty_query","localized_str_image_upload","localized_str_old_password","localized_str_password_mismatch","localized_str_confirm_password","localized_str_valid_email","localized_str_unsaved_changes","$","jQuery","_","Backbone","video_thumbnail_hover","load_typekit","select_field","get_cookie","set_cookie","unset_cookie","trackable_follow","toggle_video_embed","cycle_video_thumbnails","pano_iframe_preloader","flashVersion","renderVideo","replaceIfFlash","Ajax","Spinner","AutoPaginator","loading_next_page","BeforeAutoPaginationQueue","AfterAutoPaginationQueue","add_to_image_queue","process_image_queue","start_processing_image_queue","increment_note_count","decrement_note_count","GIF","jQuery110205483331584837288","_gaq","tumblr_custom_tracking_url","_gat","gaGlobal","_qevents","_comscore","__qc","quantserve","uh","YAHOO","__","udm_","ns_p","COMSCORE","dialog_translations","audiojs","audiojsInstance","tinymce","data-mce-expando","Markdown","mejs","JpegMeta","MediaElement","createSetter","createGetter","vdata1384174873883","_V_","VideoJS","default_search_text","next_page","toast_translations","img_obj"]多すぎ!
いつの間にか Homebrew から OpenCV が消えている模様。コミットログを見ると別のリポジトリに動いているっぽい。
brew tap
コマンドでリポジトリを追加すればインストールできるようになる。$ brew tap homebrew/science$ brew install opencv ==> Downloading ...
Socket.IO でちょっとしたアクション系のゲームを作ることになり特にレイテンシがシビアになりそうなので調査。
テストした条件などはこんな感じ。
- アプリは Node.js 0.10.15 + Socket.IO 0.9.16 + Express 3.3.4 で実装
- ディストリビューションは Amazon Linux AMI 2013.3
- m1.{xlarge,large,medium,small} の 4 種類のインスタンスタイプを使用
- クライアントは専用の m1.xlarge のインスタンスから接続
- 同時接続数は 1, 10, 50, 100, 500, 1000,…
ブロック(ス)はすばらC〜!!!
そんなすばらC〜!!! ブロック(ス)はと何かというと、
Apple が C/C++/Objective-C 向けに独自実装したクロージャみたいなものだよ。
boost がなくても、C++11 でなくても、クロージャがなにも考えずに使える!!!
すばらC〜!!!
typedef void (^blk_t)(); class Test { private : blk_t _callback; public : Test() { _callback = ^() { printf("%p\n",this); }; } blk_t callback() { return _callback; } }; int main(int argc, char *argv[]) { @autoreleasepool { Test *t1 = new Test(); printf("%p\n",t1); t1->callback()(); } return 0; }
とかいて実行すると、、、
同じ値コンソールに、表示された!!!
うれC〜!!!
以上テスト投稿でした〜
X-CTUがWindowsしか対応してない、という状況はMacなMakerにとって非常にストレスフルなことである(多分)。
でも、もう大丈夫です。以下、MacでX-CTUを起動してXBeeのファームを書き込む手順。
~/Applicatons/CrossOver/Digi/X-CTU.app
という感じで入っているはず。
$ cd ~/Library/Application Support/CrossOver/Bottles//dosdevices
$ ln -s /dev/tty.usbserial-AXXXXXX COM1
文字をSVGに書き出そうとして、最初は FontForge -script 使おうとしていたのだけれど、スクリプトを覚える気がないからどうも小回りが効かなく FreeType でなんとかならないかなと思い、適当に日本語で検索したら、「flashなどで利用するためにフォントのグリフを取得する方法」を見つけたので、この資料を参考に、どうせ UCS-2 の範囲で事足りるし iconv を使わずに NSString で代用して Objective-C++ でコードを書いてみた。
#import <Cocoa/Cocoa.h>
#import "string"
#import "sstream"
#import <ft2build.h>
#import <freetype/freetype.h>
#define WEIGHT 0.015625
template <class T> static std::string str(const T &value) { std::ostringstream out; out<<value; return out.str(); }
void dumptype(NSString *text,std::string otf,int size=200,int dpi=72) {
FT_Library lib;
FT_Face face;
std::string dir=[[([[NSBundle mainBundle] bundlePath]) stringByDeletingLastPathComponent] UTF8String];
FT_Init_FreeType(&lib);
if(FT_New_Face(lib,(dir+"/data/"+otf).c_str(),0,&face)) ;
else {
FT_Set_Char_Size(face,size<<6,size<<6,dpi,dpi);
FT_Select_Charmap(face,FT_ENCODING_MS_SJIS);
for(int k=0; k<[text length]; k++) {
char *chars=(char *)[[text substringWithRange:NSMakeRange(k,1)] cStringUsingEncoding:NSUTF16BigEndianStringEncoding];
char tmp[6];
sprintf(tmp,"U+%02X%02X",(unsigned char)chars[0],(unsigned char)chars[1]);
std::string svg=("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
svg+=("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
svg+=("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\""+str(size)+"px\" height=\""+str(size)+"px\" viewBox=\"0 0 "+str(size)+" "+str(size)+"\" xml:space=\"preserve\">");
if(!FT_Load_Glyph(face,FT_Get_Char_Index(face,((unsigned char)chars[0])<<8|((unsigned char)chars[1])),FT_LOAD_DEFAULT)){
FT_Vector *p=face->glyph->outline.points;
char *t=face->glyph->outline.tags;
int n=face->glyph->outline.n_contours;
int b=0;
svg+=("<path d=\"");
for(int i=0; i<n; i++){
if(i) b=face->glyph->outline.contours[i-1]+1;
int e=face->glyph->outline.contours[i]+1;
svg+=((i)?"Z M ":"M ")+str(p[b].x*WEIGHT)+" "+str(size-p[b].y*WEIGHT);
double px1=p[b].x,py1=-p[b].y;
for(int j=b; j<e; j++){
if(FT_CURVE_TAG(t[j])==FT_CURVE_TAG_ON){
if(px1!=(p[j].x)||py1!=-(p[j].y)) svg+=" L "+str(p[j].x*WEIGHT)+" "+str(size-p[j].y*WEIGHT);
px1=p[b].x;
py1=-p[b].y;
}
else{
int i1=j-1,i2=j+1;
if(j==0) i1=e-1;
if(i2>=e) i2=b;
if(FT_CURVE_TAG(t[i1])==FT_CURVE_TAG_CUBIC) {
double px2=p[i2].x,py2=-p[i2].y;
double cx1=p[i1].x,cy1=-p[i1].y;
double cx2=p[j].x,cy2=-p[j].y;
svg+=" C "+str(cx1*WEIGHT)+" "+str(size+cy1*WEIGHT)+" "+str(cx2*WEIGHT)+" "+str(size+cy2*WEIGHT)+" "+str(px2*WEIGHT)+" "+str(size+py2*WEIGHT);
px1=px2;
py1=py2;
}
}
}
}
svg+=" Z\"/>";
}
svg+="</svg>";
NSError *error;
NSString *dst=[NSString stringWithUTF8String:svg.c_str()];
[dst writeToFile:[NSString stringWithUTF8String:(dir+"/data/"+tmp+".svg").c_str()] atomically:YES encoding:NSUTF8StringEncoding error:&error];
}
}
FT_Done_FreeType(lib);
}
dumptype(@"おっぱいさわり隊","uniba.otf");
/data フォルダにオープンタイプフォントを入れて実行すると U+304A.svg, U+308A.svg, U+308F.svg, U+968A.svg, U+3044.svg, U+3055.svg, U+3063.svg, U+3071.svg を吐き出してくれるので、うれしい。
※ この文章はテスト投稿となります。
Tokyo SuperCollider #10 @東京工芸大学中野キャンパス IMスタジオ 2013 /10 / 27 (日)
割とまじめにgeo系をやる必要が出てきたのでメモ。
d3.js の geometry 変換を THREE.js 上のレンダラで利用できないかいろいろ調べていて、
d3で地図を描く時、標準になっている GeoJSONという地図データを表現するためのJSONフォーマットが良さそうという事に。*1
この記事を参考に、GeoJSONを読んでTHREE.jsのマテリアルとして表示させてみた。
記事にあるように、このライブラリで一度 GeoJSONを読み込んで、 d3 の geometry 変換で経緯座標系をメルカトル地図に変換、 SVG Pathにする。地図座標 -> デカルト座標系への変換。
それをTHREE Meshマテリアルのパスにして3Dオブジェクトとして生成する。
demo
国内の地図データが必要なときは、国土交通省国土政策局GISHPのサイトで国土数値情報のデータが公開されているので、
ここから shape 形式のデータが利用できる。
shap形式のデータを GeoJSON に直すには、GDAL というコマンドラインのツール や QGIS というpython製の素敵なツールがある。
こちらの記事がとても詳しく紹介されている。
需要があるか分からないが、SuperColliderでDMX制御についてメモ。
インターフェースとしてはENTTEC USB Proがメジャーな様。
SuperColliderではExtensionでquarksにdmxというのが存在する。結局はSerialPortでパケット整形してあげてるようだ。
これでサクっとできると思ったのだが、DMX512を送るのに baud rate 57600で、シリアルパケットが詰まるので結局oscを受けてシリアルパケットを整形する部分をPythonでする事にした。
https://github.com/c0z3n/pySimpleDMX この辺りのコードが参考になった。
Grunt と組み合わせて Node.js で Web アプリ作るときに、node とは別に grunt を起動するのが面倒なので、child_process を使って Node のアプリを立ち上げる時に
grunt watch
するようにしてみた。var express = require('express'); var path = require('path'); var cp = require('child_process'); var app = express(); app.set('port', process.env.PORT || 3000); // grunt...
ofxiPhone のアプリで環境設定でユーザー設定を設けたくなるときがあると思います。
これはCcocoaTouchの時と同じように、プロジェクトにSettings.bundleを追加して、testApp::setup()時にNSUserDefaultsを呼び出せばできます。
1) Settings.bundleの追加
XCodeのインスペクタでxcodeprojファイルを左クリック->New Group でSettings のようなグループを作成
作成したグループを左クリック->New File で iOS / Resource の Settings Bundle を選択してプロジェクトのディレクトリに作成
前々から気になっていた ofxiPhone で Cocoa UIKit にアクセスする方法。
rootViewController になっている ofxiPhoneViewController を NavigationController に
むりやりねじ込む方法をやってみた。
ofxiPhoneViewController は setup では早すぎてまだ生成されてないので、
draw の中でdispatch_once している。もっといい方法はないのかな…。
…
XBee Wi-Fi を手に入れてほったらかしにしてあったので、いい加減動かしてみる。
以前やったAPIモードとADサンプリングで三軸加速度センサーを作った物を今度はWi-Fi経由で
PCに送るまでをやってみた…。
回路は以前のものと全く同じで、ボタン電池で駆動、D0~D2ピンに加速度のXYZ軸のピンをそれぞれアサインさせるもの。
XBeeはホストとして、サンプリングしたデータをアドレス指定したクライアントへUDP経由でたれ流す。
…