2014/09/11

OpenDataを活用した滋賀県(大津市)の観光スポットなどをナビゲーションするiOSアプリを作りました

オープンデータを活用した滋賀県(大津市)の観光スポットなどをナビゲーションするiOSアプリを作りました






アプリの概要

※オンライン状態でGPS機能をONにしてください。
機能一覧:
  • 市営駐車場
  • 観光名所
  • 社寺
  • 市営駐車場
  • 公衆トイレ
  • ホテル(小休憩用)
  • 花火大会当日用ゴミ箱 ※現在は花火大会終了しているため、使えません。
    上記スポットを選択するとマップにピンが立ちます。
  • 徒歩と車でどのくらい時間がかかるかの表示(デフォルトで車となっています)
  • テーブルのダブルタップで目的地への3Dルート案内
  • スポットのWeb案内
  • 目的地までの方角コンパスとリアルタイム距離表示、方位磁石付き

※マップにピンが立たない、表示されない時はしばらく経ってからアイテムを選択してください。

技術的な話


ホテル以外はすべて大津市のびわ湖大花火大会オープンデータを活用させてもらっています。
オープンデータとはなに?という詳細は割愛させてもらいますが、ざっくり言うと国や公共機関などが所有、管理するデータをアクセスしやすい、誰でも活用しやすい形に加工したデータです。そして、そのWeb上のデータをつなぐ(linkする)ことで、新しい価値を生み出そうとする取り組み、総称をLOD(Linked Open Data)というそうです。LODはWebの創始者Tim Berners-Leeが提唱しています。
最近テレビでも鯖江市などのオープンデータの活用事例が放映されていたり、今後かなりアツい分野であることは確かです。


公開されているオープンデータの取得方法

RDFで書かれたメタデータを検索するためのクエリ言語「SPARQL」を使用しています。

滋賀県のホテルリストの取得方法

ホテルリスト表示APIからObjective-cでスクレイピングしています。

スポットの画像取得

オープンデータで定義されていないものに関しては、
Google Image Search APIにて非同期で取得


最後に

京都でオープンデータはないのかと思い、調べたところ、オープンデータでの形式はないようです。

京都市 観光・モビリティ・防災 オープンデータ
https://kyoto.smartercity.jp/

全国の状況はというとこんなのを見つけました。

日本のオープンデータ都市数
http://fukuno.jig.jp/2013/opendatamap
2014/09/05日現在で45です。

まだまだ少ないといった印象ですね。

2014/09/06

SwiftからSPARQL利用

SwiftからSPARQLを利用するには

SPARQLは、エンドポイントと呼ばれるURIにGETリクエストでSPARQLクエリを投げると、json、xmlなど(指定可能)がレスポンスで返ってきます。

例えば、CocoaPodsでAFNetworkingを利用する場合
execQueryにSPARQLクエリを入れてください。

以下、びわ湖大花火大会に関するオープンデータ
        let manager :AFHTTPRequestOperationManager = AFHTTPRequestOperationManager()
        let url :String = "http://dl.opendata.shiga.jp/sparql"
        let sparql :String = execQuery
        
        let parameters :Dictionary = [
            "default-graph-uri" : "http://lod.opendata.shiga.jp/hanabi2014",
            "query"             : sparql,
            "format"            : "json",
        ]
        
        manager.requestSerializer = AFJSONRequestSerializer()
        manager.requestSerializer.setValue("application/sparql-results+json", forHTTPHeaderField: "Accept")
        manager.responseSerializer = AFHTTPResponseSerializer()
        let requestSuccess = {
            (operation :AFHTTPRequestOperation!, responseObject :AnyObject!) -> Void in
            NSLog("requestSuccess \(operation.responseString)")            
        }
        let requestFailure = {
            (operation :AFHTTPRequestOperation!, error :NSError!) -> Void in
            NSLog("requestFailure: \(error)")
        }
        
        manager.GET(url, parameters: parameters, success: requestSuccess, failure: requestFailure)


CC BY Yuichi Matsuoka / @you_matz

2014/09/05

Objective-CでのSPARQL利用

Objective-CからSPARQLを利用するには

SPARQLは、エンドポイントと呼ばれるURIにGETリクエストでSPARQLクエリを投げると、json、xmlなど(指定可能)がレスポンスで返ってきます。

例えば、大津市内の観光名所のデータ一覧を取得する場合は

  
prefix geo: <http://www.w3.org/2003/01/geo/wgs84_pos# ≷
prefix schema: <http://schema.org/ ≷
prefix place: <http://fp.yafjp.org/terms/place# ≷

select * where {
?uri geo:lat ?lat;
geo:long ?long;
rdfs:label ?label;
schema:address ?address;
schema:description ?description;
place:access ?access;
schema:url ?homepage.
filter(regex(str(?uri),"http://lod.opendata.shiga.jp/otsucity/tourist_attraction")).
}

Objective-CでのGETリクエスト送信部のサンプル
execQueryの部分にさきほどのSPARQLを突っ込む
  
    NSString *endpointurl = @"http://dl.opendata.shiga.jp/sparql?default-graph-uri=http%3A%2F%2Flod.opendata.shiga.jp%2Fhanabi2014&query=";
    // ここがポイント
    NSString *encodedText = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
                                                                                                  NULL,
                                                                                                  (__bridge CFStringRef)execQuery, //元の文字列
                                                                                                  NULL,
                                                                                                  CFSTR("!*'();:@&=+$,/?%#[]"),
                                                                                                  CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
    
    NSString *finalreq = [[endpointurl stringByAppendingString:encodedText] stringByAppendingString:@"&format=json"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:finalreq]];
    // Accept ヘッダ値は以下の指定に
    [request setValue:@"application/sparql-results+json" forHTTPHeaderField:@"Accept"];
    NSURLResponse *resp;
    NSError *error;
    NSData *resdata = [NSURLConnection sendSynchronousRequest:request returningResponse:&resp error:&error];    
 // jsonに変換
    NSMutableArray *jsonResponse = [NSJSONSerialization JSONObjectWithData:resdata
                                                                   options:kNilOptions
                                                                     error:nil];
    
    return [[jsonResponse valueForKey:@"results"] valueForKey:@"bindings"];


エンドポイントURLにエンコードしたクエリをひっつけて、
レスポンスの形式をapplication/sparql-results+jsonとし、GETリクエストを投げる。
 するとレスポンスはjsonで返ってくる。
jsonを配列に格納して、必要な部分を返り値として渡す。
エラー処理などはしていませんので、あくまで参考に。 

NSStringクラスの標準URLエンコードでは、対応しきれていない特殊な文字("!*'();:@&=+$,/?%#[]")がある。

  
//encoding
NSString *encodedText
     = [execQuery stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] 


CoreFoundationのCFURLCreateStringByAddingPercentEscapes()を使用
ARC環境では__bridge_transferを使用することでメモリリークを回避
  
 NSString *encodedText = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
                                                                                                  NULL,
                                                                                                  (__bridge CFStringRef)execQuery, //元の文字列
                                                                                                  NULL,
                                                                                                  CFSTR("!*'();:@&=+$,/?%#[]"),
                                                                                                  CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));



次はSwiftでやってみる

CC BY Yuichi Matsuoka / @you_matz