連載
» 2011年06月06日 00時00分 公開

iOSでジオ(GEO)プログミラング入門(2):Flickr APIと位置情報を使い画像をiPhoneの地図に表示するには (2/3)

[郷田まり子,株式会社鳥人間]

Flickr APIで近所の画像を検索するには

 Flickr APIのエンドポイントは「http://api.flickr.com/services/rest/」です。これに、いろいろなパラメータを付けてアクセスすることで、APIをコールできます。ユーザー認証を必要とする機能もありますが、今回のように、Web全体に公開された写真を検索するだけなら、ユーザー認証は不要です。

HTTPリクエスト作り方

 「指定した緯度経度の周辺の写真を取得する」場合は、次のようなURLになります(青文字の部分は、適宜書き換えてください)。

http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=【アプリのAPIキー】&lat=【緯度】&lon=【経度】&extras=geo,url_t,owner_name

 コーディングを始める前に、取りあえずWebブラウザのURL欄に入れて試してみるといいでしょう。このURLに含まれる各パラメータの意味を簡単に説明します。

機能を指定する「method」パラメータ

 機能を指定します。検索や投稿などさまざまな機能があります。今回は「画像の検索」を行う「flickr.photos.search」メソッドを利用します(参考:Flickr Servicesメソッド一覧)。

APIキーを指定する「api_key」パラメータ

 アプリケーションのAPIキーです。先ほど取得した値を必ず明記しなければいけません。

世界測地系を使う「lat」「lon」パラメータ

 検索の基準地点の緯度です。緯度経度は世界測地系です(ちなみに、日本発のサービスでは、世界測地系と日本測地系を選べるもの、デフォルトで日本測地系が選ばれるものもあります。iOS SDKでは、位置情報は全て世界測地系を使います。マッシュアップの際には測地系をチェックしてください)。

詳細情報を取得する「extras」パラメータ

 デフォルトでは、画像のタイトルやIDなど、写真の最低限の情報しか含まれていません。さらに詳しい情報を取得したい場合は、欲しい情報の種類をカンマ区切りで指定します。今回は、サムネイルのURLの「url_t」と、位置情報の「geo」、そして投稿者の名前の「owner_name」を指定しました。

XML形式のレスポンス結果

 このURLにHTTPリクエストを送信すると、次のようなXML形式のレスポンスが得られます。

<?xml version="1.0" encoding="utf-8"?>
<rsp stat="ok">
<photos page="1" pages="2" perpage="250" total="347">
<photo id="◯◯◯◯◯" owner="◯◯◯◯◯" secret="◯◯◯◯◯" server="2654" farm="3" title="つけめん並盛 味玉つき" ispublic="1" isfriend="0" isfamily="0" latitude="35.XXXXXX" longitude="139.XXXXX" accuracy="16" place_id="◯◯◯◯◯" woeid="◯◯◯◯◯" geo_is_family="0" geo_is_friend="0" geo_is_contact="0" geo_is_public="1" url_t="http://farm3.static.flickr.com/2655/◯◯◯◯◯_t.jpg" height_t="100" width_t="100" ownername="◯◯◯◯◯"/>
<photo id="◯◯◯◯◯" owner="◯◯◯◯◯" secret="◯◯◯◯◯" server="2655" farm="3" title="ちょっと面白い看板" ispublic="1" isfriend="0" isfamily="0" latitude="35.XXXXX" longitude="139.XXXXX" accuracy="16" place_id="◯◯◯◯◯" woeid="◯◯◯◯◯" geo_is_family="0" geo_is_friend="0" geo_is_contact="0" geo_is_public="1" url_t="http://farm3.static.flickr.com/2655/◯◯◯◯◯_t.jpg" height_t="100" width_t="100" ownername="◯◯◯◯◯"/>
</photos>
</rsp>

 では、このXMLデータをパースして、地図上に表示してみます。

写真情報を格納するクラスを作る

 まずは、写真情報を格納する「Photo」というクラスを作ります。

図6 Xcode 4にてPhotoクラスを生成 図6 Xcode 4にてPhotoクラスを生成

 Photoクラスは、6つのプロパティを持ったシンプルなクラスです。MKMapViewにそのまま追加できるように、「MKAnnotation」というプロトコルを実装しました。アノテーションのタイトルとして写真のタイトルを、サブタイトルとして投稿者の名前を返すようにしています。

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface Photo : NSObject <MKAnnotation> {
NSString *title; //写真のタイトル
CLLocationCoordinate2D coordinate; //座標
NSString *owner; //投稿者ーナーID
NSString *ownerName; //投稿者名
NSString *photoId; //写真のID
NSString *thumbUrl; //サムネイル画像のURL
}

@property (nonatomic, retain) NSString *title;
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, retain) NSString *owner;
@property (nonatomic, retain) NSString *ownerName;
@property (nonatomic, retain) NSString *photoId;
@property (nonatomic, retain) NSString *thumbUrl;

- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate;
- (NSString *)title;
- (NSString *)subtitle;
- (NSString *) url ;

@end
Photo.h
#import "Photo.h"
 
@implementation Photo
 
@synthesize title;
@synthesize coordinate;
@synthesize owner;
@synthesize ownerName;
@synthesize photoId;
@synthesize thumbUrl;
 
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate {
    coordinate = newCoordinate;
}
- (NSString *)title {
    return title;
}
- (NSString *)subtitle {
    return ownerName;
}
 
- (NSString *) url { //……【1】
    return [NSString stringWithFormat:@"http://www.flickr.com/photos/%@/%@",
           owner, photoId];
}
 
@end
Photo.m

 【1】の「url」メソッドは、Flickrのサイト上で写真を見るためのURLを返すものです。写真を閲覧するには、以下の形式のURLにアクセスします。

http://www.flickr.com/photos/【投稿者のID】/【写真のID】

図7 写真の閲覧ページの例 図7 写真の閲覧ページの例

iOSのNSXMLParserクラスでXMLをパースするには

 次に、XMLパーサを作りましょう。「FlickrPhotoParser」という名前のクラスを新規作成します。

 XMLのパースには、iOS SDK標準のXMLパーサである「NSXMLParser」というクラスを利用します。このNSXMLParserは、「SAX」と呼ばれるタイプのXMLパーサです。XMLパーサは、大きく分けると、DOMとSAXの2種類があります。

SAXとは

 XMLを先頭から順に読んでいき、「タグの開始」「タグの中身の検出」「タグの終了」などを検出したら、その都度イベントとして通知するもの。開発者は、それらのイベントをハンドルするコードを書かなければなりません。

DOMとは

 文字列をパースし、それを木構造のオブジェクトに一気に変換してくれます。詳細は、以下を参照してください。

イベントハンドラとプロトコルを実装

 NSXMLParserはSAXタイプなので、「タグの開始」などの通知を受けて適宜処理を行うイベントハンドラを実装します。イベント通知を受け取る側のクラスは、「NSXMLParserDelegate」プロトコルを実装します。

#import <Foundation/Foundation.h>
#import "Photo.h"
 
@interface FlickrPhotoParser : NSObject <NSXMLParserDelegate>  {
    NSMutableArray *list;   
}
 
@property (nonatomic, retain) NSMutableArray *list;
 
- (NSArray*) fetchNearbyPhotos: (CLLocationCoordinate2D) centerCoordinate;
 
@end
FlickrPhotoParser.h
#import "FlickrPhotoParser.h"
 
@implementation FlickrPhotoParser
 
@synthesize list;
 
static NSString *API_KEY = @"【あなたのアプリケーションのAPIキー】"; //……【1】
 
- (NSArray*) fetchNearbyPhotos: (CLLocationCoordinate2D) centerCoordinate { //……【2】
    NSString* urlString = [NSString stringWithFormat:@"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%@&lat=%f&lon=%f&extras=geo,url_t,owner_name",
                          API_KEY, centerCoordinate.latitude, centerCoordinate.longitude];   
           NSLog(urlString);
    list = [[NSMutableArray alloc]init];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:urlString]];   //……【3】
    [parser setDelegate:self];   //このクラスでイベントをハンドルできるようにする
    [parser setShouldProcessNamespaces:NO];  
    [parser setShouldReportNamespacePrefixes:NO];  
    [parser setShouldResolveExternalEntities:NO];  
    [parser parse];  //……【4】
    [parser release];
    return list;
}
 
- (void)parserDidStartDocument:(NSXMLParser *)parser {   //……【5】
    list = [[NSMutableArray alloc]init];
}  
 
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { if ([@"photo" isEqualToString:elementName]) { //……【6】 Photo *photo = [[[Photo alloc]init]autorelease]; [photo setOwner:(NSString*)[attributeDict objectForKey:@"owner"]]; [photo setOwnerName:(NSString*)[attributeDict objectForKey:@"ownername"]]; [photo setPhotoId:(NSString*)[attributeDict objectForKey:@"id"]]; [photo setTitle:(NSString*)[attributeDict objectForKey:@"title"]]; [photo setCoordinate: CLLocationCoordinate2DMake([(NSString*)[attributeDict objectForKey:@"latitude"] floatValue], [(NSString*)[attributeDict objectForKey:@"longitude"] floatValue])]; [photo setThumbUrl:(NSString*)[attributeDict objectForKey:@"url_t"]]; [list addObject:photo]; } } - (void) dealloc { [list release]; [super dealloc]; } @end
FlickrPhotoParser.m

 【1】の個所には、アプリのAPIキーを入れてください。

 【2】の「fetchNearbyPhotos」メソッドは、緯度経度を受け取り、画像を検索し、Photoインスタンスを格納したNSMutableArrayを返すメソッドです。【3】の個所でURLを指定してNSXMLParserインスタンスを生成し、【4】の個所で実際にリクエストを送信し、パースを開始します。

 【5】の「parserDidStartDocument」は、「ドキュメントの開始」のイベントをハンドルするメソッドです。ここでは、NSMutableArrayインスタンスを生成しています。生成したPhotoインスタンスはこのNSMutableArrayインスタンスに順次追加していくことにします。

 【6】は、「タグの開始」の通知をハンドルします。引数「elementName」は、タグの名前です。写真の情報を含んだ「photo」タグを見つけたら、その属性「attribute」を読み、生成したPhotoインスタンスのプロパティとしてセットしていきます。

Flickr APIで取得したタグの中身

 <photo>タグは、以下のように、属性値として写真の情報が含まれています。

<photo
    id="◯◯◯◯◯" owner="◯◯◯◯◯" secret="◯◯◯◯◯"
    server="2654" farm="3" title="つけめん並盛 味玉つき"
    ispublic="1" isfriend="0" isfamily="0"
    latitude="35.XXXXXX" longitude="139.XXXXX" accuracy="16"
    place_id="◯◯◯◯◯" woeid="◯◯◯◯◯" ownername="◯◯◯◯◯"
    geo_is_family="0" geo_is_friend="0" geo_is_contact="0" geo_is_public="1"
    url_t="http://farm3.static.flickr.com/2655/◯◯◯◯◯_t.jpg"
    height_t="100" width_t="100"/>

 NSDictionary型のパラメータ「attributeDict」は、これらの属性を含んだ辞書(キーと値のペアを含んだハッシュテーブルのようなもの)となっています。上記のようなタグを読み込んだ場合、以下の戻り値は「@"つけめん並盛 味玉つき"」というNSStringになります。

[attributeDict objectForKey:@"title"]

 次ページでは、パースした情報を基に、地図に表示する方法と、アノテーションをボタンに変える方法をコードを交えて解説します。

Copyright © ITmedia, Inc. All Rights Reserved.

編集部からのお知らせ

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。