iPhoneのプログラム

SATCHを用いたナビアプリ(3)その5

目的地の選択画面を以下のようなフロア別の表示に変更した。

変更したソースのみ掲載しています。

AppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
#import "Spot.h"

// 前
#define NAVI_DIRECT_FORWARD         1

// 右斜め前
#define NAVI_DIRECT_RIGHT_FORWARD   2

// 右
#define NAVI_DIRECT_RIGHT           3

// 右斜め後ろ
#define NAVI_DIRECT_RIGHT_BACKWARD  4

// 後ろ
#define NAVI_DIRECT_BACKWARD        5

// 左斜め後ろ
#define NAVI_DIRECT_LEFT_BACKWARD   6

// 左
#define NAVI_DIRECT_LEFT            7

// 左斜め前
#define NAVI_DIRECT_LEFT_FORWARD    8

@class ViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    int numPath;
    int countPath;
    CMMotionManager *motionManager;
    CMAttitude *referenceAttitude;
    BOOL isCaptured;
    
    int curDirect;
    
    NSArray *obj;
    NSArray *obj1;
    NSArray *obj10;
    NSArray *obj11;

}

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) ViewController *viewController;

@property (strong, nonatomic) UINavigationController *navController;

@property (strong, nonatomic) NSDictionary *spots;

@property (strong, nonatomic) NSArray *keys;

@property (retain, nonatomic) Spot *startSpot;

@property (retain, nonatomic) Spot *destSpot;

@property (retain, nonatomic) NSArray *path1;

@property (retain, nonatomic) NSArray *path2;

@property (retain, nonatomic) NSArray *path;

@property (retain, nonatomic) NSString *capName;

@property (assign, nonatomic) int elevatorSide;

- (void)gotoNaviMode;
- (void)stepNext;

- (BOOL)isCameraMode;
@end

AppDelegate.mm
#import "AppDelegate.h"
#import "ViewController.h"
#import "Spot.h"
#import "URLLoader.h"
#import "NaviXMLParser.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize viewController = _viewController;
@synthesize navController = _navController;
@synthesize spots = _spots;
@synthesize keys = _keys;

@synthesize startSpot;
@synthesize destSpot;

@synthesize path1;
@synthesize path2;
@synthesize path;

@synthesize capName;

@synthesize elevatorSide;

- (void)dealloc
{
    [_window release];
    [_viewController release];
    [_spots release];
    [_keys release];
    [obj release];
    [obj1 release];
    [obj10 release];
    [obj11 release];
    [super dealloc];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    numPath = 1;
    countPath = 0;
    curDirect = 1;
    
    Spot *s1 = [[[Spot alloc] init] autorelease];
    s1.spotID = @"1";
    s1.spotName = @"入り口(K2号館側)";
    
    Spot *s2 = [[[Spot alloc] init] autorelease];
    s2.spotID = @"2";
    s2.spotName = @"入り口(マクドナルド側)";
    
    Spot *s1005 = [[[Spot alloc] init] autorelease];
    s1005.spotID = @"1005";
    s1005.spotName = @"平野教員室(1005)";

    Spot *s1006 = [[[Spot alloc] init] autorelease];
    s1006.spotID = @"1006";
    s1006.spotName = @"平野研究室(1006)";

    Spot *s1007 = [[[Spot alloc] init] autorelease];
    s1007.spotID = @"1007";
    s1007.spotName = @"坂内研究室(1007)";

    Spot *s1008 = [[[Spot alloc] init] autorelease];
    s1008.spotID = @"1008";
    s1008.spotName = @"坂内教員室(1008)";

    Spot *s1009 = [[[Spot alloc] init] autorelease];
    s1009.spotID = @"1009";
    s1009.spotName = @"梶教員室(1009)";

    Spot *s1010 = [[[Spot alloc] init] autorelease];
    s1010.spotID = @"1010";
    s1010.spotName = @"梶研究室・牧研究室(1010)";

    Spot *s1011 = [[[Spot alloc] init] autorelease];
    s1011.spotID = @"1011";
    s1011.spotName = @"福井研究室(1011)";

    Spot *s1012 = [[[Spot alloc] init] autorelease];
    s1012.spotID = @"1012";
    s1012.spotName = @"福井教員室(1012)";

    Spot *s1013 = [[[Spot alloc] init] autorelease];
    s1013.spotID = @"1013";
    s1013.spotName = @"黒川教員室(1013)";

    Spot *s1014 = [[[Spot alloc] init] autorelease];
    s1014.spotID = @"1014";
    s1014.spotName = @"黒川研究室(1014)";

    Spot *s1101 = [[[Spot alloc] init] autorelease];
    s1101.spotID = @"1101";
    s1101.spotName = @"西村(広)教員室(1101)";
    
    Spot *s1102 = [[[Spot alloc] init] autorelease];
    s1102.spotID = @"1102";
    s1102.spotName = @"春日教員室(1102)";
    
    Spot *s1103 = [[[Spot alloc] init] autorelease];
    s1103.spotID = @"1103";
    s1103.spotName = @"共通教員室(1103)";
    
    Spot *s1104 = [[[Spot alloc] init] autorelease];
    s1104.spotID = @"1104";
    s1104.spotName = @"富川研究室・山内研究室(1104)";
    
    Spot *s1105 = [[[Spot alloc] init] autorelease];
    s1105.spotID = @"1105";
    s1105.spotName = @"富川教員室(1105)";
    
    Spot *s1106 = [[[Spot alloc] init] autorelease];
    s1106.spotID = @"1106";
    s1106.spotName = @"谷中教員室(1106)";
    
    Spot *s1107 = [[[Spot alloc] init] autorelease];
    s1107.spotID = @"1107";
    s1107.spotName = @"谷中研究室(1107)";
    
    Spot *s1108 = [[[Spot alloc] init] autorelease];
    s1108.spotID = @"1108";
    s1108.spotName = @"佐藤研究室・鈴木(浩)研究室(1108)";
    
    Spot *s1109 = [[[Spot alloc] init] autorelease];
    s1109.spotID = @"1109";
    s1109.spotName = @"佐藤教員室(1109)";

    Spot *s1110 = [[[Spot alloc] init] autorelease];
    s1110.spotID = @"1110";
    s1110.spotName = @"速水教員室(1110)";

    Spot *s1111 = [[[Spot alloc] init] autorelease];
    s1111.spotID = @"1111";
    s1111.spotName = @"速水研究室(1111)";

    Spot *s1112 = [[[Spot alloc] init] autorelease];
    s1112.spotID = @"1112";
    s1112.spotName = @"徳弘研究室(1112)";
    
    Spot *s1113 = [[[Spot alloc] init] autorelease];
    s1113.spotID = @"1113";
    s1113.spotName = @"徳弘教員室(1113)";

    Spot *s1114 = [[[Spot alloc] init] autorelease];
    s1114.spotID = @"1114";
    s1114.spotName = @"服部(元)教員室(1114)";

    Spot *s1115 = [[[Spot alloc] init] autorelease];
    s1115.spotID = @"1115";
    s1115.spotName = @"服部(元)・小坂研究室(1115)";

    Spot *s1116 = [[[Spot alloc] init] autorelease];
    s1116.spotID = @"1116";
    s1116.spotName = @"小島研究室(1116)";

    Spot *s1117 = [[[Spot alloc] init] autorelease];
    s1117.spotID = @"1117";
    s1117.spotName = @"小島教員室(1117)";
  
    self.keys = [[NSArray alloc] initWithObjects:@"1F", @"10F", @"11F", nil];
    obj1 = [[NSArray alloc] initWithObjects:s1, s2, nil];
    obj10 = [[NSArray alloc] initWithObjects:s1005, s1006, s1007, s1008, s1009,
             s1010, s1011, s1012, s1013, s1014, nil];
    obj11 = [[NSArray alloc] initWithObjects:s1101, s1102, s1103, s1104, s1105,
             s1106, s1107, s1108, s1109, s1110, 
             s1111, s1112, s1113, s1114, s1115,
             s1116, s1117, nil];
    obj = [[NSArray alloc] initWithObjects:obj1, obj10, obj11, nil];
    self.spots = [[NSDictionary alloc] initWithObjects:obj forKeys:self.keys];
    
    // ジャイロ関係
    motionManager = [[CMMotionManager alloc] init];
    motionManager.deviceMotionUpdateInterval = 1.0f/4.0f; // ジャイロのサンプリング間隔[s]    
    isCaptured = NO;
    referenceAttitude = nil;
    
    self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease];
    self.navController = [[UINavigationController alloc] 
                          initWithRootViewController:self.viewController];
    self.window.rootViewController = self.navController;
    
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

- (void)gotoNaviMode
{
    self.viewController.txtMsg.text = [NSString stringWithFormat:@"出発地:%@, 目的地:%@",
                                       startSpot.spotName, destSpot.spotName];
    
    NSLog(@"%@ -> %@", startSpot.spotID, destSpot.spotID);
    
    NSString *url = [NSString 
                     stringWithFormat:@"http://www.tosiyama.jp/personal/cgi/navitest8.cgi?start=%@&target=%@",
                     startSpot.spotID, destSpot.spotID];
    
    URLLoader *loader = [[[URLLoader alloc] init] autorelease];
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(loadNaviDataDidEnd:) 
                                                 name:@"connectionDidFinishNotification"
                                               object:loader];
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(loadNaviDataFailed:) 
                                                 name:@"connectionDidFailWithError" 
                                               object:loader];
    
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [loader loadFromUrl:url method:@"GET"];
}

- (void)loadNaviDataDidEnd:(NSNotification *)notification
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    URLLoader *loader = (URLLoader *)[notification object];
    NSData *xmlData = loader.data;
    
    NaviXMLParser *parser = [[[NaviXMLParser alloc] init] autorelease];
    
    NSArray *naviData = [parser parseNaviData:xmlData];
    numPath = [naviData count];
    self.path1 = [naviData objectAtIndex:0];
    self.path2 = nil;
    if (numPath == 2) {
        self.path2 = [naviData objectAtIndex:1];
    }
    self.path = path1;
    
    self.viewController.btnCorner.enabled = YES;
    [self stepNext];
    [self startGyro:NO];
}

- (int)adjustDirection:(int)dir prev:(int)prevDir
{
    int total = ((prevDir-1) + (dir-1)) % 8;
    return (total + 1);
}

- (void)stepNext
{
    if (countPath < [self.path count]) {
        
        int cmd = [[[self.path objectAtIndex:countPath] objectForKey:@"command"] intValue];
        NSString *msg = [[self.path objectAtIndex:countPath] objectForKey:@"msg"];
        
        if (curDirect == 0) {
            curDirect = 1;
            if (self.elevatorSide != -cmd) {
                self.path = self.path2;
            }
            countPath++;
            
            cmd = [[[self.path objectAtIndex:countPath] objectForKey:@"command"] intValue];
            msg = [NSString stringWithFormat:@"エレベータを降りたら、%@",
                   [[self.path objectAtIndex:countPath] objectForKey:@"msg"]];
            [self.viewController.btnCorner setTitle:@"角" forState:UIControlStateNormal];
            self.viewController.btnCorner.enabled = YES;
            [self startGyro:NO];
        }
        
        if (cmd > 10) {
            curDirect = [self adjustDirection:(cmd % 10) prev:curDirect];
            [self.viewController.btnCorner setTitle:@"確認" forState:UIControlStateNormal];
        }
        else if (cmd == 0) {
            [motionManager stopDeviceMotionUpdates];
            curDirect = 0;
            [self.viewController.btnCorner setTitle:@"撮影" forState:UIControlStateNormal];
            [self.viewController enqueueCommand:@"prepareCamera" args:nil];
        }
        else if (cmd > 0) {
            curDirect = [self adjustDirection:cmd prev:curDirect];
        }
        
        if (msg != nil) {
            self.viewController.txtMsg.text = msg;
        }
        countPath++;
        if (countPath == [self.path1 count]) 
        {
            self.viewController.btnCorner.enabled = NO;
        }
    }
}

- (void)startGyro:(BOOL)restart
{
    // デバイスのハードウェアチェック
    if (!motionManager.deviceMotionAvailable)
    {
        NSLog(@"DeviceMotion is not available.");
        return;
    }
    
    isCaptured = restart;
    
    // モーションデータ更新時のハンドラを作成
    void (^handler)(CMDeviceMotion *, NSError *) = ^(CMDeviceMotion *motion, NSError *error)
    {
        if (isCaptured == NO) {
            referenceAttitude = [motion.attitude retain];
            isCaptured = YES;
        }
        else {
            // ジャイロデータの記録
            [self recordGyroData:motion];
        }
    };
    
    // モーションデータの測定を開始
    NSOperationQueue *queue = [NSOperationQueue currentQueue];
    [motionManager startDeviceMotionUpdatesToQueue:queue withHandler:handler];
}

- (BOOL)isCameraMode
{
    if (curDirect == 0) { 
        return YES;
    }
    
    return NO;
}

- (void)recordGyroData:(CMDeviceMotion *)motion
{    
    CMAttitude *currentAttitude = motion.attitude;
    
    if (referenceAttitude != nil) {
        [currentAttitude multiplyByInverseOfAttitude: referenceAttitude];
    }
    
    // 角度を度数に変換
    float rollDegree = 180 * currentAttitude.roll / M_PI;
    NSNumber *direct = [self calcDirection:rollDegree mode:curDirect];
    
    NSString *_roll = [NSString stringWithFormat:@"%.4f", rollDegree];
    NSString *_roll2 = [NSString stringWithFormat:@"%.4f", [direct floatValue]];    
    NSArray *para = [NSArray arrayWithObjects:_roll, _roll2, nil];
    
    [direct release];
    [self.viewController enqueueCommand:@"sendGyro" args:para];
}

- (NSNumber *)calcDirection:(float)roll mode:(int)num
{
    float _direct = 0.0;
    switch (num) {
        case NAVI_DIRECT_FORWARD:
            _direct = roll;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
        case NAVI_DIRECT_RIGHT:
            _direct = roll + 90.0;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
        case NAVI_DIRECT_LEFT:
            _direct = roll - 90.0;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
        case NAVI_DIRECT_BACKWARD:
            _direct = roll - 180;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
        case NAVI_DIRECT_RIGHT_FORWARD:
            _direct = roll + 45.0;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
        case NAVI_DIRECT_LEFT_FORWARD:
            _direct = roll - 45.0;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
        case NAVI_DIRECT_RIGHT_BACKWARD:
            _direct = roll + 135.0;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
        case NAVI_DIRECT_LEFT_BACKWARD:
            _direct = roll - 135.0;
            if (_direct <= 0) {
                _direct = -_direct;
            }
            else {
                _direct = 360.0 - _direct;
            }
            break;
            
    }
    
    NSNumber *direct = [[NSNumber alloc] initWithFloat:_direct];
    return direct;
}

- (void)loadNaviDataFailed:(NSNotification *)notification
{
    UIAlertView *alert = [[UIAlertView alloc]
                          initWithTitle:@"エラー" 
                          message:@"xmlデータの取得に失敗しました!" 
                          delegate:self 
                          cancelButtonTitle:@"閉じる" 
                          otherButtonTitles:nil];
    [alert show];
    [alert release];
}

@end

ViewController.m
#import "AppDelegate.h"
#import "ViewController.h"
#import "DestController.h"
#import "CaptureController.h"

@interface ViewController ()

@end

@implementation ViewController

@synthesize btnCorner;
@synthesize mRender;
@synthesize txtMsg;

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"メイン画面";
    
    //メッセージのローカライズ
    [SatchARViewer        setDialogMessageLoading:@"ローディング..."];
    [SatchARViewer        setToastMessageNoNetworkConnection:@"ネットワークに接続されていません。"];
    [SatchARViewer        setToastMessageNetworkError:@"通信中にエラーが発生しました。"];
    [SatchARViewer        setToastMessageLicenceError:@"認証に失敗しました"];
    
    mPlayer = [[[SatchARViewer alloc] init] initialize:mRender];
    [self start];
    
    btnCorner.enabled = NO;
    txtMsg.text = @"出発地のマーカをかざしてください!";
	// Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setMRender:nil];
    [self setTxtMsg:nil];
    [self setTxtMsg:nil];
    [self setBtnCorner:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (void)dealloc {
    if (mPlayer) {
        [mPlayer terminate];
        [mPlayer release];
        mPlayer = nil;
    }
    
    [mRender release];
    [txtMsg release];
    [txtMsg release];
    [btnCorner release];
    [super dealloc];
}

- (void)start
{
    if (mPlayer) {
        [mPlayer loadScenario:@"Nov4_iphone/Nov4.dpd"
                     callback:@selector(callback:)
                          obj:self];
    }
}


- (void)callback:(NSNumber*)num
{
    switch ([num intValue]) {
        case LOAD_SCENARIO_STATUS_COMPLETE:
            [mPlayer registerCommunicationCallback:@"setStartPosition" 
                                    callbackObject:self 
                                    callbackMethod:@"TI_SetStartPosNotification"];
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(setStartPosition:) 
                                                         name:@"TI_SetStartPosNotification" 
                                                       object:nil];
            
            [mPlayer registerCommunicationCallback:@"setCaptureFilename" 
                                    callbackObject:self 
                                    callbackMethod:@"TI_SetCapNotification"];
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(setCaptureFilename:) 
                                                         name:@"TI_SetCapNotification" 
                                                       object:nil];
            
            [mPlayer playScenario];
            break;
    }
}

- (void)stop
{
    if (mPlayer && ![mPlayer isScenarioPaused]) {
        [mPlayer pauseScenario];
    }    
}

- (IBAction)pushCornerButton:(id)sender 
{
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    
    if ([delegate isCameraMode] == YES) {
        [self enqueueCommand:@"capture" args:nil];
    }
    else {
        [delegate stepNext];
    }
}

- (void)setStartPosition:(NSNotification *)notification
{
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    
    NSDictionary* l_userInfo = [notification userInfo];
    NSString *s1 = (NSString *)[l_userInfo objectForKey:@"arg0"];
        
    id key = [delegate.keys objectAtIndex:0];
    int idx = [[delegate.spots objectForKey:key]
               indexOfObjectPassingTest:
               ^BOOL(id obj, NSUInteger idx, BOOL *stop) {
                   Spot *cur_spot = (Spot *)obj;
                   return [cur_spot.spotID isEqualToString:s1];
               }];

    if (idx != NSNotFound) {
        delegate.startSpot = (Spot *)[[delegate.spots objectForKey:key] objectAtIndex:idx];
    }
        
    if (idx == NSNotFound) {
        key = [delegate.keys objectAtIndex:1];
        idx = [[delegate.spots objectForKey:key]
               indexOfObjectPassingTest:
               ^BOOL(id obj, NSUInteger idx, BOOL *stop) {
                   Spot *cur_spot = (Spot *)obj;
                   return [cur_spot.spotID isEqualToString:s1];
               }];
        if (idx != NSNotFound) {
            delegate.startSpot = (Spot *)[[delegate.spots objectForKey:key] objectAtIndex:idx];            
        }
    }

    if (idx == NSNotFound) {
        key = [delegate.keys objectAtIndex:2];
        idx = [[delegate.spots objectForKey:key]
               indexOfObjectPassingTest:
               ^BOOL(id obj, NSUInteger idx, BOOL *stop) {
                   Spot *cur_spot = (Spot *)obj;
                   return [cur_spot.spotID isEqualToString:s1];
               }];
        if (idx != NSNotFound) {
            delegate.startSpot = (Spot *)[[delegate.spots objectForKey:key] objectAtIndex:idx];            
        }
    }

    txtMsg.text = [NSString stringWithFormat:@"出発地:%@", delegate.startSpot.spotName];    
    
    DestController *dest = [[DestController alloc] init];
    [delegate.navController pushViewController:dest animated:YES];
}

- (void)setCaptureFilename:(NSNotification *)notification
{
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    btnCorner.enabled = NO;    
    
    NSDictionary* l_userInfo = [notification userInfo];
    NSString *s1 = (NSString *)[l_userInfo objectForKey:@"arg0"];
    
    delegate.capName = s1;
    
    CaptureController *cap = [[CaptureController alloc] init];
    [delegate.navController pushViewController:cap animated:YES];
}

- (void)enqueueCommand:(NSString *)commandName args:(NSArray *)args
{
    if (mPlayer) {
        [mPlayer enqueueCommand:commandName args:args];
    }
}

@end

DestController.h
#import <UIKit/UIKit.h>

@interface DestController : UIViewController   {
    NSArray *keys;
    NSDictionary *spots;
}

@property (retain, nonatomic) IBOutlet UITableView *tableView;

@end

DestController.m
#import "AppDelegate.h"
#import "DestController.h"
#import "Spot.h"

@interface DestController ()

@end

@implementation DestController
@synthesize tableView;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"目的地の選択";
    
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    keys = delegate.keys;
    spots = delegate.spots;
    // Do any additional setup after loading the view from its nib.
}

- (void)viewDidUnload
{
    [self setTableView:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)dealloc {
    [tableView release];
    [super dealloc];
}

#pragma mark UITableViewDataSource Methods

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"cell"];
    if (nil == cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    
    id key = [keys objectAtIndex:indexPath.section];
    
    Spot *thisSpot = [[spots objectForKey:key] objectAtIndex:indexPath.row];
    cell.textLabel.text = thisSpot.spotName;
    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tv
{
    return [keys count];
}

- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section
{
    id key = [keys objectAtIndex:section];
    return [[spots objectForKey:key] count];
}

- (NSString *)tableView:(UITableView *)tv titleForHeaderInSection:(NSInteger)section
{
    return [keys objectAtIndex:section];
}

#pragma mark UITableViewDelegate Methods

- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    id key = [keys objectAtIndex:indexPath.section];
    Spot *thisSpot = [[spots objectForKey:key] objectAtIndex:indexPath.row];
    
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    delegate.destSpot = thisSpot;
    [delegate gotoNaviMode];
    
    [self dismissModalViewControllerAnimated:YES];
}

@end