<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7174491101899985374</id><updated>2018-08-28T15:46:00.391+09:00</updated><category term="cocoa"/><category term="iphone/ipad"/><category term="objc"/><category term="tx"/><category term="ime"/><category term="map"/><category term="ruby"/><category term="sinatra"/><category term="visualization"/><title type='text'>blog.yatsu.info</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.yatsu.info/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://blog.yatsu.info/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>yatsu</name><uri>http://www.blogger.com/profile/15610097960453845292</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>5</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7174491101899985374.post-8581472539846186041</id><published>2010-05-14T15:58:00.001+09:00</published><updated>2010-05-14T16:02:18.461+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cocoa"/><category scheme="http://www.blogger.com/atom/ns#" term="iphone/ipad"/><category scheme="http://www.blogger.com/atom/ns#" term="objc"/><category scheme="http://www.blogger.com/atom/ns#" term="tx"/><title type='text'>iPhoneのUIWebViewではてなキーワードを高速置換</title><content type='html'>&lt;a href=&quot;http://blog.yatsu.info/2010/05/cocoatxobjc-tx-objc-wrapper.html&quot;&gt;blog.yatsu.info: CocoaでTxを使うためのObjcラッパー tx-objc-wrapper&lt;/a&gt;&amp;nbsp;で書いた&lt;a href=&quot;http://github.com/yatsu/tx-objc-wrapper&quot;&gt;tx-objc-wrapper&lt;/a&gt;にはもうひとつ&lt;a href=&quot;http://github.com/yatsu/tx-objc-wrapper/tree/master/HatenaKeywords/&quot;&gt;HatenaKeywords&lt;/a&gt;というサンプルプログラムがあります。&lt;br /&gt;&lt;br /&gt;これはiPhone用のシンプルなブラウザアプリで、開いたWebページのすべての&lt;a href=&quot;http://d.hatena.ne.jp/keyword/&quot;&gt;はてなキーワード&lt;/a&gt;をリンクに置換するものです。&lt;br /&gt;&lt;br /&gt;このアプリをビルドするには、tx-objc-wrapperディレクトリで以下のようにはてなキーワードCSVをダウンロードします。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:shell&quot;&gt;% wget http://d.hatena.ne.jp/images/keyword/keywordlist_furigana.csv&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;そして HatenaKeywords/HatenaKeywords.xcodeproj をXcodeでビルドします。&lt;br /&gt;&lt;br /&gt;起動するとUIWebViewで&lt;a href=&quot;http://ja.wikipedia.org/&quot;&gt;Wikipedia日本語版&lt;/a&gt;を読み込み、以下のようにキーワードを置換します。緑色がはてなキーワードへのリンクです。&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/_oJ9ufs4Ajm8/S-zuWJqfxnI/AAAAAAAAACE/PxOPhRB27S8/s1600/tx-hatena.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/_oJ9ufs4Ajm8/S-zuWJqfxnI/AAAAAAAAACE/PxOPhRB27S8/s320/tx-hatena.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;iPhone 3G実機で26万語からTxを構築すると90秒くらいかかります。ラッパーのコードがNSStringへの変換などを含んでいて、その分遅いかもしれません。後でチューニングしようと思います。&lt;br /&gt;&lt;br /&gt;２回目に起動すると、前回構築したTxを読み込みます。&lt;br /&gt;&lt;br /&gt;以下が実装の主な部分です。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;- (void)webViewDidFinishLoad:(UIWebView *)aWebView {&lt;br /&gt;  NSError *error = nil;&lt;br /&gt;&lt;br /&gt;  // get the original HTML&lt;br /&gt;  NSString *html = [aWebView stringByEvaluatingJavaScriptFromString:@&quot;document.body.innerHTML;&quot;];&lt;br /&gt;  DDXMLDocument *doc =&lt;br /&gt;    [[DDXMLDocument alloc] initWithHTMLString:html&lt;br /&gt;                      options:HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR&lt;br /&gt;                      error:&amp;amp;error];&lt;br /&gt;&lt;br /&gt;  // find text nodes that are not included in a link&lt;br /&gt;  NSArray *nodes = [doc nodesForXPath:@&quot;//*[name(.)!=&#39;a&#39;]/text()&quot;&lt;br /&gt;                  error:&amp;amp;error];&lt;br /&gt;  for (DDXMLNode *node in nodes) {&lt;br /&gt;    [node setStringValue:[txo replace:[node stringValue]]];&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // generate HTML from DDXMLDocument&lt;br /&gt;  html = [[[NSString alloc] initWithData:[doc XMLData]&lt;br /&gt;                  encoding:NSUTF8StringEncoding]&lt;br /&gt;      autorelease];&lt;br /&gt;&lt;br /&gt;  // replace &#39;[[[word]]]&#39; with &#39;&lt;a href=&quot;http://www.blogger.com/...&quot;&gt;word&lt;/a&gt;&#39;&lt;br /&gt;  NSString *linkStr = [NSString stringWithFormat:@&quot;&lt;a #059028;=&quot;&quot; class=&quot;\&amp;quot;hatenaKeyword\&amp;quot;&quot; href=&quot;http://www.blogger.com/%22http://d.hatena.ne.jp/keyword/$1/%22&quot; text-decoration:=&quot;&quot; underline;\&quot;=&quot;&quot;&gt;$1&lt;/a&gt;&quot;];&lt;br /&gt;  html = [html stringByReplacingOccurrencesOfRegex:@&quot;\\[\\[\\[([^\\]]*)\\]\\]\\]&quot;&lt;br /&gt;                      withString:linkStr];&lt;br /&gt;&lt;br /&gt;  // escape quotation marks&lt;br /&gt;  html = [html stringByReplacingOccurrencesOfString:@&quot;&#39;&quot; withString:@&quot;\\&#39;&quot;];&lt;br /&gt;&lt;br /&gt;  // make it one line&lt;br /&gt;  html = [html stringByReplacingOccurrencesOfString:@&quot;\n&quot; withString:@&quot;\\n&quot;];&lt;br /&gt;&lt;br /&gt;  // execute the JavaScript code&lt;br /&gt;  NSString *js = [NSString stringWithFormat:@&quot;document.body.innerHTML = &#39;%@&#39;;&quot;, html];&lt;br /&gt;  [aWebView stringByEvaluatingJavaScriptFromString:js];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このメソッドはUIWebViewの読み込みが終わったときに呼び出され、以下を行います。&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;JavaScriptを実行しHTML(&lt;code&gt;document.body.innerHTML&lt;/code&gt;)を取得する&lt;/li&gt;&lt;li&gt;XPathでリンクの外のテキストノードを取得する&lt;br /&gt;(&lt;a href=&quot;http://d.hatena.ne.jp/cooldaemon/20090911/1252637257&quot;&gt;KissXML+HTML&lt;/a&gt;を使用)&lt;/li&gt;&lt;li&gt;[Txo replace:] ではてなキーワードを [[[キーワード]]] に置換し、ノードにセット&lt;/li&gt;&lt;li&gt;DDXMLDocumentからHTMLを生成&lt;/li&gt;&lt;li&gt;正規表現で [[[キーワード]]] をリンクに置換&lt;br /&gt;(&lt;a href=&quot;http://regexkit.sourceforge.net/RegexKitLite/index.html&quot;&gt;RegexKitLite&lt;/a&gt;を使用)&lt;/li&gt;&lt;li&gt;HTML文字列のクォーテーションマークをエスケープ&lt;/li&gt;&lt;li&gt;HTML文字列を１行にまとめる&lt;/li&gt;&lt;li&gt;JavaScriptでUIWebViewのHTMLを置き換える&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;いろいろ非効率的なところがあったり、エラー処理がなかったりするので、実際のアプリにするには書き換える必要があります。Webページによってはメモリが足りなくて落ちたり、その他のエラーが出たりすると思うので、ご注意ください。&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.yatsu.info/feeds/8581472539846186041/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://blog.yatsu.info/2010/05/iphoneuiwebview.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/8581472539846186041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/8581472539846186041'/><link rel='alternate' type='text/html' href='http://blog.yatsu.info/2010/05/iphoneuiwebview.html' title='iPhoneのUIWebViewではてなキーワードを高速置換'/><author><name>yatsu</name><uri>http://www.blogger.com/profile/15610097960453845292</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_oJ9ufs4Ajm8/S-zuWJqfxnI/AAAAAAAAACE/PxOPhRB27S8/s72-c/tx-hatena.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7174491101899985374.post-2822991377863259522</id><published>2010-05-14T14:08:00.005+09:00</published><updated>2010-05-14T16:05:34.584+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cocoa"/><category scheme="http://www.blogger.com/atom/ns#" term="iphone/ipad"/><category scheme="http://www.blogger.com/atom/ns#" term="objc"/><category scheme="http://www.blogger.com/atom/ns#" term="tx"/><title type='text'>CocoaでTxを使うためのObjcラッパー tx-objc-wrapper</title><content type='html'>Cocoaで&lt;a href=&quot;http://www-tsujii.is.s.u-tokyo.ac.jp/~hillbig/tx.htm&quot;&gt;Tx&lt;/a&gt;を使うためのObjective-Cラッパーを作りました。&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://github.com/yatsu/tx-objc-wrapper&quot;&gt;tx-objc-wrapper&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;iPhone/iPod touch/iPadで大量の文字列を検出したり置換したりするのに使えます。&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;&lt;b&gt;使い方&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;ソースをダウンロード&lt;br /&gt;git clone http://github.com/yatsu/tx-objc-wrapper.git&lt;/li&gt;&lt;li&gt;Txディレクトリ以下のファイルをXcodeプロジェクトに追加&lt;/li&gt;&lt;li&gt;&quot;Txo.h&quot;をimport&lt;/li&gt;&lt;li&gt;Txoのメソッドを呼び出す&lt;/li&gt;&lt;/ol&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;API&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;構築／読み込み&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;// NSStringが入ったNSSetからTxを構築し、ファイルに保存する&lt;br /&gt;+ (Txo *)build:(NSSet *)wordSet withFileName:(NSString *)fileName;&lt;br /&gt;&lt;br /&gt;// Txをファイルから読み込む&lt;br /&gt;+ (Txo *)read:(NSString *)fileName;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Txの単純なラッパー&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;- (NSUInteger)prefixSearch:(const char *)aString&lt;br /&gt;                    length:(NSInteger)length&lt;br /&gt;              resultLength:(NSInteger *)resultLength;&lt;br /&gt;&lt;br /&gt;- (NSUInteger)commonPrefixSearch:(const char *)aString&lt;br /&gt;                          length:(NSInteger)length&lt;br /&gt;                         strings:(NSArray **)strings&lt;br /&gt;                           txIds:(NSArray **)txIds&lt;br /&gt;                           limit:(NSUInteger)limit;&lt;br /&gt;&lt;br /&gt;- (NSUInteger)predictiveSearch:(const char *)aString&lt;br /&gt;                        length:(NSInteger)length&lt;br /&gt;                       strings:(NSArray **)strings&lt;br /&gt;                         txIds:(NSArray **)txIds&lt;br /&gt;                         limit:(NSUInteger)limit;&lt;br /&gt;&lt;br /&gt;- (NSString *)resultLog;&lt;br /&gt;&lt;br /&gt;- (NSString *)errorLog;&lt;br /&gt;&lt;br /&gt;- (NSUInteger)keyNum;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Txo独自メソッド&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;以下のものは&lt;a href=&quot;http://gimite.net/en/index.php?tx-ruby&quot;&gt;tx-ruby&lt;/a&gt;をマネして作りました。こちらのメソッドを呼びだすと便利でしょう。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;- (NSInteger)longestPrefix:(NSString *)aString fromPosition:(NSInteger)position;&lt;br /&gt;&lt;br /&gt;- (NSInteger)longestPrefix:(NSString *)aString;&lt;br /&gt;&lt;br /&gt;- (NSArray *)searchPrefixes:(NSString *)aString forPosition:(NSInteger)position;&lt;br /&gt;&lt;br /&gt;- (NSArray *)searchPrefixes:(NSString *)aString;&lt;br /&gt;&lt;br /&gt;- (NSArray *)searchExpansions:(NSString *)aString forPosition:(NSInteger)position;&lt;br /&gt;&lt;br /&gt;- (NSArray *)searchExpansions:(NSString *)aString;&lt;br /&gt;&lt;br /&gt;- (void)scan:(NSString *)aString;&lt;br /&gt;&lt;br /&gt;- (NSString *)replace:(NSString *)aString;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;&lt;b&gt;サンプルコード&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://gimite.net/en/index.php?tx-ruby&quot;&gt;tx-rubyのサンプルコード&lt;/a&gt;と同じことをしてみます。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tx構築&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);&lt;br /&gt;NSString *documentsDirectory = [paths objectAtIndex:0];&lt;br /&gt;NSString *txPath = [NSString stringWithFormat:@&quot;%@/%@&quot;, documentsDirectory, @&quot;keywords.tx&quot;];&lt;br /&gt;&lt;br /&gt;NSMutableSet *wordSet = [[NSMutableSet alloc] init];&lt;br /&gt;[wordSet addObject:@&quot;foo&quot;];&lt;br /&gt;[wordSet addObject:@&quot;ho&quot;];&lt;br /&gt;[wordSet addObject:@&quot;hog&quot;];&lt;br /&gt;[wordSet addObject:@&quot;hoga&quot;];&lt;br /&gt;[wordSet addObject:@&quot;hoge&quot;];&lt;br /&gt;[wordSet addObject:@&quot;hogeshi&quot;];&lt;br /&gt;&lt;br /&gt;Txo *txo = [Txo build:wordSet withFileName:txPath];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;文字列検索&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;[txo longestPrefix:@&quot;hogeeee&quot;] // =&amp;gt; 4 (which means &quot;hoge&quot; is in the index)&lt;br /&gt;&lt;br /&gt;[txo searchPrefixes:@&quot;hoge&quot;] // =&amp;gt; (ho, hog, hoge)&lt;br /&gt;&lt;br /&gt;[txo searchExpansions:@&quot;hog&quot;] // =&amp;gt; (hog, hoga, hoge, hogeshi)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;部分文字列検索(scan)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;TxoDelegateプロトコルの&lt;code&gt;txo:scannedString:atPosition:&lt;/code&gt;を実装します。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;- (void)txo:(Txo *)aTxo scannedString:(NSString *)aString atPosition:(NSInteger)position {&lt;br /&gt;  // 文字列と位置を含む配列をarrayに追加&lt;br /&gt;  // (arrayはNSMutableArrayとしてクラスに定義しておく)&lt;br /&gt;  [array addObject:[NSArray arrayWithObjects:aString,&lt;br /&gt;                    [NSNumber numberWithInt:position], nil]];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;scan&lt;/code&gt;を実行。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;array = [[NSMutableArray alloc] init];&lt;br /&gt;[txo scan:@&quot;hogefugafoo&quot;]; // =&amp;gt; ((hoge, 0), (foo, 8))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;部分文字列置換(replace)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;TxoDelegateプロトコルの&lt;code&gt;txo:replace:&lt;/code&gt;を実装します。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;- (NSString *)txo:(Txo *)aTxo replace:(NSString *)aString {&lt;br /&gt;  return [aString uppercaseString]; // 大文字にして返す&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;replace&lt;/code&gt;を実行。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:objc&quot;&gt;[txo replace:@&quot;hogefugafoo&quot;] // =&amp;gt; HOGEfugaFOO&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;以上のサンプルコードは&lt;a href=&quot;http://github.com/yatsu/tx-objc-wrapper/tree/master/IPhoneSimpleDemo/&quot;&gt;IPhoneSimpleDemo&lt;/a&gt;というサンプルアプリに実装しているので、Xcodeでビルドしてお試しください。&lt;br /&gt;実行すると以下のようになります。&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/_oJ9ufs4Ajm8/S-zaDVJDsWI/AAAAAAAAAB8/M09O-5hzpAI/s1600/tx-simpledemo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/_oJ9ufs4Ajm8/S-zaDVJDsWI/AAAAAAAAAB8/M09O-5hzpAI/s320/tx-simpledemo.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;C++ライブラリをObjective-Cでラップする方法は、&lt;a href=&quot;http://note.sonots.com/Comp/CompLang/objective2D-c.html&quot;&gt;Objective-C Wrappers for C++ Classes&lt;/a&gt;&amp;nbsp;を参考にしました。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;今後の予定&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;NSDictionary互換インターフェース&lt;/li&gt;&lt;li&gt;テストコードを書く&lt;/li&gt;&lt;li&gt;UTF8以外の文字コードに対応&lt;/li&gt;&lt;li&gt;ラッパーコードの効率的でないところを書き直し&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;もうひとつのサンプルプログラム&lt;a href=&quot;http://github.com/yatsu/tx-objc-wrapper/tree/master/HatenaKeywords/&quot;&gt;HatenaKeywords&lt;/a&gt;についてこちらに書きました:&lt;br /&gt;&lt;a href=&quot;http://blog.yatsu.info/2010/05/iphoneuiwebview.html&quot;&gt;blog.yatsu.info: iPhoneのUIWebViewではてなキーワードを高速置換&lt;/a&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.yatsu.info/feeds/2822991377863259522/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://blog.yatsu.info/2010/05/cocoatxobjc-tx-objc-wrapper.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/2822991377863259522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/2822991377863259522'/><link rel='alternate' type='text/html' href='http://blog.yatsu.info/2010/05/cocoatxobjc-tx-objc-wrapper.html' title='CocoaでTxを使うためのObjcラッパー tx-objc-wrapper'/><author><name>yatsu</name><uri>http://www.blogger.com/profile/15610097960453845292</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_oJ9ufs4Ajm8/S-zaDVJDsWI/AAAAAAAAAB8/M09O-5hzpAI/s72-c/tx-simpledemo.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7174491101899985374.post-8634481951952455707</id><published>2010-05-06T22:08:00.015+09:00</published><updated>2010-05-24T17:09:28.597+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="map"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><category scheme="http://www.blogger.com/atom/ns#" term="sinatra"/><title type='text'>Google Chart ToolsのGeomapで世界地図のドリルダウン</title><content type='html'>&lt;a href=&quot;http://code.google.com/intl/ja/apis/charttools/&quot; rel=&quot;noreferrer&quot;&gt;Google Chart Tools&lt;/a&gt;の&lt;a href=&quot;http://code.google.com/intl/ja/apis/visualization/documentation/gallery/geomap.html&quot; rel=&quot;noreferrer&quot;&gt;Geomap&lt;/a&gt;で世界地図のドリルダウンを実装してみました。&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://geomapselect.heroku.com/&quot; rel=&quot;noreferrer&quot;&gt;サンプルサイト&lt;/a&gt;&amp;nbsp;(&lt;b&gt;2010-05-24 URL更新&lt;/b&gt;)&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://3.bp.blogspot.com/_oJ9ufs4Ajm8/S-K72LtabgI/AAAAAAAAABk/2j52jWFM7sc/s320/mapselect_world.png&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/_oJ9ufs4Ajm8/S-K8B4bZYkI/AAAAAAAAABs/B2bfwpOnQTI/s1600/mapselect_japan.png&quot; imageanchor=&quot;1&quot; rel=&quot;noreferrer&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/_oJ9ufs4Ajm8/S-K8B4bZYkI/AAAAAAAAABs/B2bfwpOnQTI/s320/mapselect_japan.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;地図中の領域をクリックしてドリルダウンしていくことができます。上に戻るには左上の「Zoom Out」ボタンをクリックします。&lt;br /&gt;&lt;br /&gt;本来は統計データを表示するものですが、地図から住所を入力したり、人やお店を探したりするインターフェースとしても使えそうです。&lt;br /&gt;&lt;br /&gt;あまり深くまで行こうとするとエラーになりますので、実際に利用する場合は修正が必要です。&lt;br /&gt;&lt;br /&gt;コードは以下のようになっています。&lt;a href=&quot;http://www.sinatrarb.com/&quot; rel=&quot;noreferrer&quot;&gt;Sinatra&lt;/a&gt;で実装しました。&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush:ruby&quot;&gt;require &#39;rubygems&#39;&lt;br /&gt;require &#39;sinatra&#39;&lt;br /&gt;&lt;br /&gt;helpers do&lt;br /&gt;  include Rack::Utils&lt;br /&gt;  alias_method :h, :escape_html&lt;br /&gt;&lt;br /&gt;  def render_map(region)&lt;br /&gt;    &amp;lt;&amp;lt;-END&lt;br /&gt;    var data = new google.visualization.DataTable();&lt;br /&gt;    data.addRows(7);&lt;br /&gt;    data.addColumn(&#39;string&#39;, &#39;Country&#39;);&lt;br /&gt;    data.addColumn(&#39;number&#39;, &#39;Number of people&#39;);&lt;br /&gt;    data.setValue(0, 0, &#39;Germany&#39;);&lt;br /&gt;    data.setValue(0, 1, 10);&lt;br /&gt;    data.setValue(1, 0, &#39;United States&#39;);&lt;br /&gt;    data.setValue(1, 1, 31);&lt;br /&gt;    data.setValue(2, 0, &#39;Brazil&#39;);&lt;br /&gt;    data.setValue(2, 1, 17);&lt;br /&gt;    data.setValue(3, 0, &#39;Canada&#39;);&lt;br /&gt;    data.setValue(3, 1, 8);&lt;br /&gt;    data.setValue(4, 0, &#39;France&#39;);&lt;br /&gt;    data.setValue(4, 1, 22);&lt;br /&gt;    data.setValue(5, 0, &#39;RU&#39;);&lt;br /&gt;    data.setValue(5, 1, 34);&lt;br /&gt;    data.setValue(6, 0, &#39;Japan&#39;);&lt;br /&gt;    data.setValue(6, 1, 22);&lt;br /&gt;    var options = {};&lt;br /&gt;    options[&#39;region&#39;] = &#39;#{h(region)}&#39;;&lt;br /&gt;    options[&#39;dataMode&#39;] = &#39;regions&#39;;&lt;br /&gt;    options[&#39;showZoomOut&#39;] = #{region == &#39;world&#39; ? &#39;false&#39; : &#39;true&#39;};&lt;br /&gt;&lt;br /&gt;    var container = document.getElementById(&#39;mapCanvas&#39;);&lt;br /&gt;    var geomap = new google.visualization.GeoMap(container);&lt;br /&gt;    geomap.draw(data, options);&lt;br /&gt;&lt;br /&gt;    google.visualization.events.addListener(geomap, &#39;regionClick&#39;, function(props) {&lt;br /&gt;      var region = props.region;&lt;br /&gt;      new Ajax.Request(region, {&lt;br /&gt;        method: &#39;get&#39;,&lt;br /&gt;        asynchronous: true,&lt;br /&gt;        evalScripts: true&lt;br /&gt;      });&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    google.visualization.events.addListener(geomap, &#39;zoomOut&#39;, function() {&lt;br /&gt;      new Ajax.Request(&#39;world&#39;, {&lt;br /&gt;        method: &#39;get&#39;,&lt;br /&gt;        asynchronous: true,&lt;br /&gt;        evalScripts: true&lt;br /&gt;      });&lt;br /&gt;    });&lt;br /&gt;    END&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;get &#39;/&#39; do&lt;br /&gt;  haml :index&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;get &#39;/:region&#39; do&lt;br /&gt;  content_type(&#39;text/javascript&#39;)&lt;br /&gt;  render_map(h(params[:region]))&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;__END__&lt;br /&gt;&lt;br /&gt;@@ layout&lt;br /&gt;%html&lt;br /&gt;  %head&lt;br /&gt;    %title Map Select&lt;br /&gt;    %script{ :type =&amp;gt; &#39;text/javascript&#39;, :src =&amp;gt; &#39;http://www.google.com/jsapi&#39; }&lt;br /&gt;    %script{ :type =&amp;gt; &#39;text/javascript&#39;, :src =&amp;gt; &#39;js/prototype.js&#39; }&lt;br /&gt;    %script{ :type =&amp;gt; &#39;text/javascript&#39; } google.load(&#39;visualization&#39;, &#39;1&#39;, {&#39;packages&#39;: [&#39;geomap&#39;]});&lt;br /&gt;  %body&lt;br /&gt;    #mapCanvas&lt;br /&gt;    = yield&lt;br /&gt;&lt;br /&gt;@@ index&lt;br /&gt;%script{ :type =&amp;gt; &#39;text/javascript&#39; }= render_map(&#39;world&#39;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;やっていることは、Geomapを表示し、領域がクリックされたときにそのregionをAjaxでサーバーに送り、新しいregionでGeomapを表示しなおしているだけです。&lt;br /&gt;&lt;br /&gt;動作させるには、これをmapselect.rbとして保存します。そして保存したディレクトリの下にpublic/jsというディレクトリを作成し、そこに&lt;a href=&quot;http://www.prototypejs.org/&quot; rel=&quot;noreferrer&quot;&gt;prototype.js&lt;/a&gt;を置きます。起動は&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ruby mapselect.rb&lt;/pre&gt;とします。&lt;br /&gt;&lt;br /&gt;時間があればRailsプラグインか何かにしてみようと思います。</content><link rel='replies' type='application/atom+xml' href='http://blog.yatsu.info/feeds/8634481951952455707/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://blog.yatsu.info/2010/05/google-chart-toolsgeomap.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/8634481951952455707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/8634481951952455707'/><link rel='alternate' type='text/html' href='http://blog.yatsu.info/2010/05/google-chart-toolsgeomap.html' title='Google Chart ToolsのGeomapで世界地図のドリルダウン'/><author><name>yatsu</name><uri>http://www.blogger.com/profile/15610097960453845292</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_oJ9ufs4Ajm8/S-K72LtabgI/AAAAAAAAABk/2j52jWFM7sc/s72-c/mapselect_world.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7174491101899985374.post-3836655150162490379</id><published>2010-04-28T23:59:00.001+09:00</published><updated>2010-05-14T14:12:47.927+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ime"/><title type='text'>IME開発者会議@Google食堂</title><content type='html'>今日はGoogle食堂にIME開発者が集まり、情報交換しました。GoogleIME, Social IME, SKKIME, ChaIME, AquaSKK, uim, MacUIMの開発者など総勢14名(たぶん)が参加し、ライトニングトーク大会になりました。&lt;br /&gt;&lt;br /&gt;私の発表資料(余分なもの省略版)&lt;br /&gt;&lt;div id=&quot;__ss_3884824&quot; style=&quot;width: 425px;&quot;&gt;&lt;b style=&quot;display: block; margin: 12px 0pt 4px;&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/yatsu/inputhiliter-shortkey&quot; title=&quot;変換文字列をカラフルにする(InputHiliter)&quot;&gt;変換文字列をカラフルにする(InputHiliter)&lt;/a&gt;&lt;/b&gt;&lt;object height=&quot;355&quot; id=&quot;__sse3884824&quot; width=&quot;425&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=inputhilitershort-key-100428102847-phpapp02&amp;stripped_title=inputhiliter-shortkey&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed name=&quot;__sse3884824&quot; src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=inputhilitershort-key-100428102847-phpapp02&amp;stripped_title=inputhiliter-shortkey&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;div style=&quot;padding: 5px 0pt 12px;&quot;&gt;View more &lt;a href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a href=&quot;http://www.slideshare.net/yatsu&quot;&gt;yatsu&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;InputHiliterについてはこちら。&lt;br /&gt;&lt;a href=&quot;http://inputhiliter.yatsu.info/&quot;&gt;http://inputhiliter.yatsu.info/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;バージョン3.0から色を設定できるようになり、楽しいアプリになりました。&lt;br /&gt;実用度はわかりませんが。&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://farm4.static.flickr.com/3254/4556849341_1a7d30e0ec.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;249&quot; src=&quot;http://farm4.static.flickr.com/3254/4556849341_1a7d30e0ec.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://farm4.static.flickr.com/3006/4557479054_7dd4cf6e54.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;245&quot; src=&quot;http://farm4.static.flickr.com/3006/4557479054_7dd4cf6e54.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div id=&quot;FLASH_MESSAGE&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;&lt;div id=&quot;FLASH_MESSAGE&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;&lt;div id=&quot;FLASH_MESSAGE&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;&lt;div id=&quot;FLASH_MESSAGE&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.yatsu.info/feeds/3836655150162490379/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://blog.yatsu.info/2010/04/imegoogle.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/3836655150162490379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/3836655150162490379'/><link rel='alternate' type='text/html' href='http://blog.yatsu.info/2010/04/imegoogle.html' title='IME開発者会議@Google食堂'/><author><name>yatsu</name><uri>http://www.blogger.com/profile/15610097960453845292</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm4.static.flickr.com/3254/4556849341_1a7d30e0ec_t.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7174491101899985374.post-7943590691323323372</id><published>2010-04-15T10:15:00.026+09:00</published><updated>2010-05-14T14:14:31.544+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cocoa"/><category scheme="http://www.blogger.com/atom/ns#" term="iphone/ipad"/><category scheme="http://www.blogger.com/atom/ns#" term="objc"/><category scheme="http://www.blogger.com/atom/ns#" term="visualization"/><title type='text'>TreemapKit公開</title><content type='html'>iPhone/iPod touch/iPadでTreemapを表示するライブラリTreemapKitを公開しました。&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://github.com/yatsu/treemapkit&quot;&gt;http://github.com/yatsu/treemapkit&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;object classid=&quot;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&quot; data=&quot;http://www.flickr.com/apps/video/stewart.swf?v=71377&quot; height=&quot;400&quot; type=&quot;application/x-shockwave-flash&quot; width=&quot;300&quot;&gt; &lt;param name=&quot;flashvars&quot; value=&quot;intl_lang=en-us&amp;amp;photo_secret=4c1a37681b&amp;amp;photo_id=4507143423&quot;&gt;&lt;/param&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.flickr.com/apps/video/stewart.swf?v=71377&quot;&gt;&lt;/param&gt;&lt;param name=&quot;bgcolor&quot; value=&quot;#000000&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;&lt;embed type=&quot;application/x-shockwave-flash&quot; src=&quot;http://www.flickr.com/apps/video/stewart.swf?v=71377&quot; bgcolor=&quot;#000000&quot; allowfullscreen=&quot;true&quot; flashvars=&quot;intl_lang=en-us&amp;amp;photo_secret=4c1a37681b&amp;amp;photo_id=4507143423&quot; height=&quot;400&quot; width=&quot;300&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;これは&lt;a href=&quot;http://www.ovalplan.co.jp/&quot;&gt;Oval Plan&lt;/a&gt;社との共同プロジェクトでの成果の一部をオープンソースとして公開するものです。ライセンスはMITライセンスです。&lt;br /&gt;ソースを拡張された場合、なるべくGitHub上で公開していただけるとありがたいです。一般的に有用な拡張は取り込んでいきたいと思います。&lt;br /&gt;&lt;br /&gt;現時点では、値とラベルの配列を受け取り、内部で値に応じで２分割をネストしていく形になっています。独自のツリー構造を渡すことや、割り当てのアルゴリズムを変更することはできません。それらの機能は今後実装したいと思っています。&lt;br /&gt;&lt;br /&gt;表示される四角は画面上では実際にはネストしていません。フラットな四角を並べた構造になっています。これは上のスクリーンキャストのように、データが変更されたときにアニメーションやエフェクトを適用するのに都合がよいからです。&lt;br /&gt;&lt;div id=&quot;FLASH_MESSAGE&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.yatsu.info/feeds/7943590691323323372/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://blog.yatsu.info/2010/04/treemapkit.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/7943590691323323372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7174491101899985374/posts/default/7943590691323323372'/><link rel='alternate' type='text/html' href='http://blog.yatsu.info/2010/04/treemapkit.html' title='TreemapKit公開'/><author><name>yatsu</name><uri>http://www.blogger.com/profile/15610097960453845292</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>