SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

スマホアプリ開発の最新トレンド! ぜったい外せない位置情報活用術

YOLPで挑戦~目的地を地図で表示するiOSアプリを、 地図ライブラリを使わずにお手軽に作る!

スマホアプリ開発の最新トレンド! ぜったい外せない位置情報活用術(5)

Yahoo!スタティックマップAPIについて

 住所一覧を取得・表示ができたら、次は一覧から住所を選んだ際に地図を表示する画面を作ります。

 今回は1~4回目で使われてきたスクロール地図ではなく、静的な一枚の画像を取得できるYahoo!スタティックマップAPIを使うことにします。

 Yahoo!スタティックマップAPIを使う上で必要な情報は以下のとおりです。

  • 表示したい位置の緯度経度
  • 表示したい地図のズームレベル

 なお日本以外では、指定できるズームレベルなどに制約があります。詳しい情報については「掲載情報について」を参照してください。

 以下、緯度経度とズームレベルを指定した一例です。

Yahoo!スタティックマップAPIへのアクセス例
(latおよびlonは「東京都新宿区舟町5」の緯度経度)
http://map.olp.yahooapis.jp/OpenLocalPlatform/V1/static?appid=<準備したアプリケーションID>&lat=35.68966308&lon=139.72146148&z=20

 上記例でアクセスすると、縦横共に500(pixel)の画像が返ってきます。もちろん画像サイズも指定することができます。

Yahoo!スタティックマップAPIへのアクセス例 画像サイズ指定あり
(latおよびlonは「東京都新宿区舟町5」の緯度経度)
http://map.olp.yahooapis.jp/OpenLocalPlatform/V1/static?appid=<準備したアプリケーションID>&lat=35.68966308&lon=139.72146148&z=20&width=320&height=320

地図表示画面の作成

 キーワード入力画面、リストアップ画面に続いて、地図表示画面の作成に入ります。

 ズームレベルを決めてしまえば、緯度経度だけでもその場所の地図は表示できますが、住所名も表示させた方がどこの地図なのかが分かりやすいので、住所名と緯度経度は一式として扱いましょう。

 これら一式は、YOLP標準フォーマットであるYDFでは、Feature要素のことです。

 大まかな流れは以下のとおりです。

  1. 初期化の際に緯度経度情報を取得する(どこの地図なのかが分かるように、住所名も含めてまとめて取得する)
  2. 基本的な画面生成を行う
  3. 画面生成後、緯度経度情報を元にYahoo!スタティックマップAPIにアクセスする
  4. Yahoo!スタティックマップAPIからレスポンスを取得したら、画像として画面に表示させる

 レスポンスを取得した後の処理は、JSONか画像かの違いはありますが、先のYahoo!ジオコーダAPIにアクセスしたときと同じと言えます。

  • UIViewControllerをサブクラスとし、緯度経度を受け取れるイニシャライズのメソッドを用意する
  • UIViewControllerのviewDidLoadで、画像を表示するUIImageViewを準備する
  • UIViewControllerのviewDidLoadの最後に、Yahoo!スタティックマップAPIへアクセスするためにNSURLConnectionを使う
  • Yahoo!スタティックマップAPIから得られたデータを、viewDidLoadで用意したUIImageViewに反映させる

 [File]-[New]-[New File](Xcode4.3では[File])を選んで、新しいファイルのテンプレート選択画面を出します。

 テンプレートは「UIViewController subclass」(Xcode4.3では「Objective-C class」)を選んで、クラス名はMapImageViewController、サブクラスはUIViewControllerでファイルを作ります。

MapImageViewController.h
#import <UIKit/UIKit.h>
 
@interface MapImageViewController : UIViewController {
  NSDictionary *_feature;
  NSString *_lat;
  NSString *_lon;
  NSURLConnection *_connection;
  NSMutableData *_data;
  UIImageView *_imageView;
}
 
- (id)initWithFeature:(NSDictionary *)feature;
 
@end
MapImageViewController.m
#import "MapImageViewController.h"
 
#define APP_ID @"<準備したアプリケーションID>"
#define IMAGE_WIDTH 320.0f
#define IMAGE_HIGHT 320.0f
 
@implementation MapImageViewController
 
- (id)initWithFeature:(NSDictionary *)feature {
  self = [super init];
  if (self) {
    _feature = [feature retain];
    _connection = nil;
    _data = nil;
    _imageView = nil;
  }
  return self;
}
 
- (void)dealloc {
  [_feature release];
  if(_connection){
    [_connection cancel];
    [_connection release];
  }
  [_data release];
  [_imageView release];
  [super dealloc];
}
 
- (void)viewDidLoad {
  [super viewDidLoad];
  self.title = [_feature objectForKey:@"Name"];
 
  if(_imageView == nil){
    _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, IMAGE_WIDTH, IMAGE_HIGHT)];
    
    //feature要素から緯度経度を抜き出す
    NSDictionary *targetGeometory = [_feature objectForKey:@"Geometry"];
    NSString *coordinate = [targetGeometory objectForKey:@"Coordinates"];
    NSArray *aryLonLat = [coordinate componentsSeparatedByString:@","];
    NSString *lat = [aryLonLat objectAtIndex:1];
    NSString *lon = [aryLonLat objectAtIndex:0];
 
    //取得する画像サイズをあらかじめ定義しておく(Retina対応)
    NSUInteger imageWidth = (NSUInteger)(IMAGE_WIDTH*[UIScreen mainScreen].scale);
    NSUInteger imageHeight = (NSUInteger)(IMAGE_HIGHT*[UIScreen mainScreen].scale);
     
    //ズームレベルは20で固定
    NSMutableArray *paramList = [[NSMutableArray alloc] init];
    [paramList addObject:[NSString stringWithFormat:@"appid=%@",APP_ID]];
    [paramList addObject:[NSString stringWithFormat:@"lat=%@",lat]];
    [paramList addObject:[NSString stringWithFormat:@"lon=%@",lon]];
    [paramList addObject:@"z=20"];
    [paramList addObject:[NSString stringWithFormat:@"width=%d",imageWidth]];
    [paramList addObject:[NSString stringWithFormat:@"height=%d",imageHeight]];
     
    NSString *urlString = [NSString stringWithFormat:@"http://map.olp.yahooapis.jp/OpenLocalPlatform/V1/static?%@",[paramList componentsJoinedByString:@"&"]];
    [paramList release];
    NSLog(@"%@",urlString);
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
  }
   
  //画面中央に置かれるように指定
  _imageView.center = CGPointMake(self.view.bounds.size.width/2.0f, self.view.bounds.size.height/2.0f);
  _imageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
 
  [self.view addSubview:_imageView];  
}
 
- (void)viewDidUnload {
  [super viewDidUnload];
}
 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
 
#pragma mark - NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
  [_connection cancel];
  [_connection autorelease],_connection = nil;
}
 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  if(_data){
    [_data release];
  }
  _data = [[NSMutableData alloc] init];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
  [_data appendData:data];
}
 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
  UIImage *tmpImage = [UIImage imageWithData:_data];
  _imageView.image = [UIImage imageWithCGImage:tmpImage.CGImage scale:[UIScreen mainScreen].scale orientation:tmpImage.imageOrientation];
  [_connection autorelease],_connection = nil;
  [_data release],_data = nil;
}
 
@end

 これで地図表示画面ができました。

 この画面を住所候補リストアップ画面から呼び出すために、住所候補リストアップ画面のプログラムを少しだけ書き換えます。

GeoCodingViewController.m
#import "GeoCodingViewController.h"
//作成した地図表示画面を使えるようにimport文を追加
#import "MapImageViewController.h"
 
---- 中略 ----
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  static NSString *CellIdentifier = @"AddressCell";
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  if(cell == nil){
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
  }
 
  NSDictionary *target = [_resultList objectAtIndex:indexPath.row];
  cell.textLabel.text = [target objectForKey:@"Name"];
  cell.detailTextLabel.text = [[target objectForKey:@"Property"] objectForKey:@"Yomi"];
    
  return cell;
}
  
#pragma mark - Table view delegate
  
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  //ここから追加
  NSDictionary *target = [_resultList objectAtIndex:indexPath.row];
  MapImageViewController *next = [[[MapImageViewController alloc] initWithFeature:target] autorelease];
  [self.navigationController pushViewController:next animated:YES];
  //追加ここまで
}
 
---- 以下略 ----

 これを実行してキーワード入力、住所一覧から選択することで以下のような画面で地図が表示されます。

次のページ
応用編

この記事は参考になりましたか?

スマホアプリ開発の最新トレンド! ぜったい外せない位置情報活用術連載記事一覧

もっと読む

この記事の著者

宮川 博幸(ヤフー株式会社)(ミヤカワ ヒロユキ(ヤフーカブシキガイシャ))

メディアサービスカンパニー 開発本部所属。2005年度のACM主催のプログラミングコンテストでヤフーのエンジニアと話したことがきっかけとなり、2007年にヤフーに入社。道路交通情報、地図、路線にかかわり、現在はiOS版「地図 Yahoo!ロコ」アプリの開発を主とする。好きな言葉は「全ての道はローマに...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/6711 2012/08/23 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー