iPhoneのプログラム

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

disp3.lua
local MLTPlugin = getMLTPluginManager()
local scene = getCurrentScene()
local camera = Camera(scene:getCurrentCamera())
local vidCap = VideoCapture(scene:getObjectByName("vidcap"))
local msg = Text2D(scene:getObjectByName("msg"))

-- 方向指示用
local boussole = Entity(scene:getObjectByName("Boussole"))
local fleche = Entity(scene:getObjectByName("Fleche"))
boussole:setVisible(false)
fleche:setVisible(false)

-- 色識別用
local mainView = RenderTarget(scene:getMainView())
local targetDirectory = getUserAppDataDirectory()
local targetJpg = targetDirectory .. "c1.jpg"
local camera_mode = 1

local componentInterface = getComponentInterface()

local err_ret = eOk
local trackingIndex = -1
local targetStatus = {-1, -1, -1}
local action = {"1104    found!", "2    found!", "1011    found!"}
local marker_no = {
1104, 2, 1011
}

local found_marker = -1
local dest = -1
local found = -1
local i=0
local mode = 1

local isCommand = nil
local command = nil

msg:setText(" ")

err_ret, trackingIndex = MLTPlugin:startTracking("tracker/tracker.xml", vidCap:getVidCapID(), camera)

repeat
	if (mode == 1) then

		if (found_marker < 0) then
			for i = 1,3 do
				err_ret, targetStatus[i] = MLTPlugin:getTargetStatus(trackingIndex, i-1)
			end

			found = -1
			for i = 1,3 do
				if (targetStatus[i] == 1) then
					found = i
					break
				end
			end

			if (found ~= -1) then
				msg:setText(action[found])
				found_marker = marker_no[found]
			else
				msg:setText(" ")
			end
		else
   			if componentInterface then
				componentInterface:executeAppFunc("setStartPosition", found_marker);
			end
			mode = 2
		end

	elseif (mode == 2) then
		msg:setText(" ")

		if componentInterface then
			isCommand, command = componentInterface:pullCommand()
			if isCommand then
				if command["CommandName"]=="sendGyro" then
					boussole:setVisible(true)
					fleche:setVisible(true)
					fleche:setOrientationEuler(0,command["arg1"],0,boussole)
					mode = 3;
				end
			end
		end

	elseif (mode == 3) then

		if componentInterface then
			isCommand, command = componentInterface:pullCommand()
			if isCommand then
				if command["CommandName"]=="sendGyro" then
					fleche:setOrientationEuler(0,command["arg1"],0,boussole)
				elseif command["CommandName"]=="prepareCamera" then
					boussole:setVisible(false)
					fleche:setVisible(false)
					camera_mode = 1
					mode = 4
				end
			end
		end

	elseif (mode == 4) then

		if camera_mode == 1 then
			if componentInterface then
				isCommand, command = componentInterface:pullCommand()
				if isCommand then
					if command["CommandName"]=="capture" then
						mainView:dump(targetJpg)
						camera_mode = 2
					end
				end
			end
		elseif camera_mode == 2 then
			if componentInterface then
				componentInterface:executeAppFunc("setCaptureFilename", targetJpg);
			end
			mode = 2
		end

	end

until coroutine.yield()
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;
}

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) ViewController *viewController;

@property (strong, nonatomic) UINavigationController *navController;

@property (strong, nonatomic) NSMutableArray *spots;

@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 startSpot;
@synthesize destSpot;

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

@synthesize capName;

@synthesize elevatorSide;

- (void)dealloc
{
    [_window release];
    [_viewController release];
    [_spots 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];
    s1.spotID = @"1";
    s1.spotName = @"入り口(K2号館側)";

    Spot *s2 = [[Spot alloc] init];
    s2.spotID = @"2";
    s2.spotName = @"入り口(マクドナルド側)";
    
    Spot *s1101 = [[Spot alloc] init];
    s1101.spotID = @"1101";
    s1101.spotName = @"西村(広)教員室(1101)";

    Spot *s1102 = [[Spot alloc] init];
    s1102.spotID = @"1102";
    s1102.spotName = @"春日教員室(1102)";

    Spot *s1103 = [[Spot alloc] init];
    s1103.spotID = @"1103";
    s1103.spotName = @"共通教員室(1103)";

    Spot *s1104 = [[Spot alloc] init];
    s1104.spotID = @"1104";
    s1104.spotName = @"富川研究室・山内研究室(1104)";

    Spot *s1105 = [[Spot alloc] init];
    s1105.spotID = @"1105";
    s1105.spotName = @"富川教員室(1105)";

    Spot *s1106 = [[Spot alloc] init];
    s1106.spotID = @"1106";
    s1106.spotName = @"谷中教員室(1106)";

    Spot *s1107 = [[Spot alloc] init];
    s1107.spotID = @"1107";
    s1107.spotName = @"谷中研究室(1107)";

    Spot *s1108 = [[Spot alloc] init];
    s1108.spotID = @"1108";
    s1108.spotName = @"佐藤研究室・鈴木(浩)研究室(1108)";

    Spot *s1109 = [[Spot alloc] init];
    s1109.spotID = @"1109";
    s1109.spotName = @"佐藤教員室(1109)";

    self.spots = [[NSMutableArray alloc] 
                  initWithObjects:s1, s2, 
                  s1101, s1102, s1103, s1104, s1105,
                  s1106, s1107, s1108, s1109, nil];
    
    // ジャイロ関係
    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.h
#import <UIKit/UIKit.h>
#import "satchARViewer.h"
#import "Spot.h"

@interface ViewController : UIViewController {
    SatchARViewer *mPlayer;
}

@property (retain, nonatomic) IBOutlet UIButton *btnCorner;
@property (retain, nonatomic) IBOutlet UIView *mRender;
@property (retain, nonatomic) IBOutlet UITextView *txtMsg;

- (IBAction)pushCornerButton:(id)sender;

- (void)start;
- (void)stop;

- (void)enqueueCommand:(NSString *)commandName args:(NSArray *)args;

@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:@"Nov1_iphone/Nov1.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"];
    
    NSUInteger idx = [delegate.spots indexOfObjectPassingTest:
                      ^BOOL(id obj, NSUInteger idx, BOOL *stop) {
                          Spot *cur_spot = (Spot *)obj;
                          return [cur_spot.spotID isEqualToString:s1];
                      }];
    delegate.startSpot = (Spot *)[delegate.spots 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

URLLoader.h
#import <Foundation/Foundation.h>

@interface URLLoader : NSObject {
    NSURLConnection *connection;
    NSMutableData *data;
}

@property (retain, nonatomic) NSURLConnection *connection;
@property (retain, nonatomic) NSMutableData *data;

- (void)loadFromUrl:(NSString *)_url method:(NSString *)_method;

@end

URLLoader.m
#import "URLLoader.h"

@implementation URLLoader

@synthesize connection;
@synthesize data;

- (void)connection:(NSURLConnection *)conn
        didReceiveResponse:(NSURLResponse *)response {
    self.data = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)conn
        didReceiveData:(NSData *)receiveData {
    [self.data appendData:receiveData];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
    [[NSNotificationCenter defaultCenter]
        postNotificationName:@"connectionDidFinishNotification"
        object:self];
}

- (void)connection:(NSURLConnection *)conn
        didFailWithError:(NSError *)error {
    [[NSNotificationCenter defaultCenter]
     postNotificationName:@"connectionDidFailWithError"
     object:self];
}

- (void)loadFromUrl:(NSString *)_url method:(NSString *)_method {
    NSMutableURLRequest *req = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:_url]];
    [req setHTTPMethod:_method];
    self.connection = [NSURLConnection connectionWithRequest:req delegate:self];
}

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

@end

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

@interface NaviXMLParser : NSObject <NSXMLParserDelegate> {
    NSMutableString *currentXpath;
    NSMutableArray *NaviData;
    NSMutableArray *PathData;
    NSMutableDictionary *currentGuide;
    NSMutableString *textNodeCharacters;
}

@property (retain , nonatomic) NSMutableString *currentXpath;
@property (retain , nonatomic) NSMutableArray *NaviData;
@property (retain , nonatomic) NSMutableArray *PathData;

@property (retain , nonatomic) NSMutableDictionary *currentGuide;
@property (retain , nonatomic) NSMutableString *textNodeCharacters;

- (NSArray *) parseNaviData: (NSData *) xmlData;

@end

NaviXMLParser.m
#import "NaviXMLParser.h"

@implementation NaviXMLParser

@synthesize currentXpath;
@synthesize NaviData;
@synthesize PathData;
@synthesize currentGuide;
@synthesize textNodeCharacters;

- (void)parserDidStartDocument:(NSXMLParser *)parser
{
    self.currentXpath = [[[NSMutableString alloc] init] autorelease];
    self.NaviData = [[[NSMutableArray alloc] init] autorelease];
}

- (void)parser:(NSXMLParser *)parser 
    didStartElement:(NSString *)elementName
    namespaceURI:(NSString *)namespaceURI 
    qualifiedName:(NSString *)qName 
    attributes:(NSDictionary *)attributeDict 
{
    [self.currentXpath appendString: elementName];
    [self.currentXpath appendString: @"/"];
    
    self.textNodeCharacters = [[[NSMutableString alloc] init] autorelease];
    
    if ([self.currentXpath isEqualToString: @"navi/path/"]) {
        self.PathData = [[[NSMutableArray alloc] init] autorelease];
    }
    else if ([self.currentXpath isEqualToString: @"navi/path/guide/"]) {
        self.currentGuide = [[[NSMutableDictionary alloc] init] autorelease];
    }
}

- (void)parser:(NSXMLParser *)parser 
    didEndElement:(NSString *)elementName 
    namespaceURI:(NSString *)namespaceURI 
    qualifiedName:(NSString *)qName 
{    
    NSString *textData = [self.textNodeCharacters stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    
    if ([self.currentXpath isEqualToString: @"navi/path/"]) 
    {
        [self.NaviData addObject:self.PathData];
        self.PathData = nil;
    }
    else if ([self.currentXpath isEqualToString: @"navi/path/guide/"]) 
    {
        [self.PathData addObject:self.currentGuide];
        self.currentGuide = nil;        
    }
    else if ([self.currentXpath isEqualToString: @"navi/path/guide/command/"]) 
    {
        [self.currentGuide setValue:textData forKey:@"command"];
    }
    else if ([self.currentXpath isEqualToString: @"navi/path/guide/msg/"]) 
    {
        [self.currentGuide setValue:textData forKey:@"msg"];
    }
    
    int delLength = [elementName length] + 1;
    int delIndex = [self.currentXpath length] - delLength;
    
    [self.currentXpath deleteCharactersInRange:NSMakeRange(delIndex,delLength)];
}

- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string 
{
    [self.textNodeCharacters appendString:string];
}

- (NSArray *)parseNaviData:(NSData *)xmlData 
{
    NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:xmlData] autorelease];
    [parser setDelegate:self];
    [parser parse];
    
    return self.NaviData;
}

- (void) dealloc 
{
    [currentXpath release];
    [NaviData release];
    [PathData release];
    [currentGuide release];
    [textNodeCharacters release];
    [super dealloc];
}

@end

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

@interface Spot : NSObject

@property (nonatomic, strong) NSString *spotID;
@property (nonatomic, strong) NSString *spotName;

@end

Spot.m
#import "Spot.h"

@implementation Spot

@synthesize spotID;
@synthesize spotName;

@end

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

@interface DestController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
    NSMutableArray *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];
    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"];
    }
    
    Spot *thisSpot = [spots objectAtIndex:indexPath.row];
    cell.textLabel.text = thisSpot.spotName;
    return cell;
}

- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section
{
    return [spots count];
}

#pragma mark UITableViewDelegate Methods

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

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

@interface HSI : NSObject {
    double H, S, I;
}

@property (nonatomic, assign) double H;
@property (nonatomic, assign) double S;
@property (nonatomic, assign) double I;

- (id)initWithRed:(int)_r green:(int)_g blue:(int)_b;

@end

HSI.m
#import "HSI.h"

@implementation HSI

@synthesize H;
@synthesize S;
@synthesize I;

- (id)initWithRed:(int)_r green:(int)_g blue:(int)_b
{
    if (self = [super init]) {
        double R = _r / 255.0;
        double G = _g / 255.0;
        double B = _b / 255.0;
        
        double _pi_3 = atan(1.0) * 4 / 3;
        double _2_pi = atan(1.0) * 8;
        
        double maxI = [self max3:R p2:G p3:B];
        double minI = [self min3:R p2:G p3:B];
        
        I = (maxI + minI)/2;
        
        if (I <= 0.5) {
            S = (maxI - minI)*(maxI + minI);
        }
        else {
            S = (maxI - minI)*(2 - (maxI + minI));
        }
        
        if (maxI != minI) {
            double rr = (maxI - R)/(maxI - minI);
            double gg = (maxI - G)/(maxI - minI);
            double bb = (maxI - B)/(maxI - minI);
            
            if (R == maxI) {
                H = _pi_3 * (bb -gg);
            }
            else if (G == maxI) {
                H = _pi_3 * (2 + rr - bb);
            }
            else {
                H = _pi_3 * (4 + gg - rr);
            }
            
            if (H < 0) {
                H += _2_pi;
            }
        }
        else {
            H = 0;
        }
    }
    
    return self;
}

- (double)max2:(double)v p2:(double)v2
{
    if (v >= v2) return v;
    return v2;
}

- (double)max3:(double)v p2:(double)v2 p3:(double)v3
{
    return [self max2:[self max2:v p2:v2] p2:v3];
}

- (double)min2:(double)v p2:(double)v2
{
    if (v <= v2) return v;
    return v2;
}

- (double)min3:(double)v p2:(double)v2 p3:(double)v3
{
    return [self min2:[self min2:v p2:v2] p2:v3];
}

@end

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

@interface ColorInfo : NSObject {
    double H_angle;
    NSString *title;
    int side;
}

@property (nonatomic, assign) double H_angle;
@property (nonatomic, assign) NSString *title;
@property (nonatomic, assign) int side;

- (id)initWithAngle:(double)_a title:(NSString *)_t side:(int)_s;

- (double)calcDistance:(double)angle;

@end

ColorInfo.m
#import "ColorInfo.h"

@implementation ColorInfo

@synthesize H_angle;
@synthesize title;
@synthesize side;

- (id)initWithAngle:(double)_a title:(NSString *)_t side:(int)_s
{
    if (self = [super init]) {
        H_angle = _a;
        title = _t;
        side = _s;
    }
    
    return self;
}

- (double)calcDistance:(double)angle
{
    double H_rad = H_angle * atan(1.0) / 45.0;
    double x0 = cos(H_rad);
    double y0 = sin(H_rad);
    
    double _rad = angle * atan(1.0) / 45.0;
    double x1 = cos(_rad);
    double y1 = sin(_rad);
    
    double v = sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
    
    NSLog(@"(%f, %f) - (%f, %f) : %f",
          x0, y0, x1, y1, v);
    
    return v;
}

@end

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

@interface CaptureController : UIViewController

@property (retain, nonatomic) IBOutlet UIImageView *imgView;
@property (retain, nonatomic) IBOutlet UILabel *labMsg;

@end

CaptureController.m
#import "AppDelegate.h"
#import "CaptureController.h"
#import "HSI.h"
#import "ColorInfo.h"

@interface CaptureController ()

@end

@implementation CaptureController
@synthesize imgView;
@synthesize labMsg;

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

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    
    UIImage *img0 = [UIImage imageWithContentsOfFile:delegate.capName];
    if (img0 == nil) {
        NSLog(@"img0 is nil!");
    }
    else {
        NSLog(@"%f %f", img0.size.width, img0.size.height);
    }
    self.imgView.image = img0;
    
    CGImageRef cgImage = img0.CGImage;
    size_t width = CGImageGetWidth(cgImage);
    size_t height = CGImageGetHeight(cgImage);
    size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
    CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImage);
    CFDataRef data = CGDataProviderCopyData(dataProvider);
    UInt8 *buffer = (UInt8 *)CFDataGetBytePtr(data);
    int i, j;
    
    int hist[72];
    for (i = 0; i < 72; i++) {
        hist[i] = 0;
    }
    int div = 360 / 72;
    for (j = 0; j < height; j++) {
        for (i = 0; i < width; i++) {
            UInt8 *tmp = buffer + j * bytesPerRow + i * 4;
            
            UInt8 rr = *(tmp + 0);
            UInt8 gg = *(tmp + 1);
            UInt8 bb = *(tmp + 2);
            
            int rrr = (int)rr;
            int ggg = (int)gg;
            int bbb = (int)bb;
            
            HSI *c2 = [[HSI alloc] initWithRed:rrr green:ggg blue:bbb];
            
            if (c2.S >= 0.1) {
                int Hangle = (int)(c2.H * 45 / atan(1.0));
                if (Hangle < 0) {
                    Hangle += 360;
                }
                
                hist[Hangle / div]++; 
            }
            
            [c2 release];
        }
    }
    
    int maxBin = 0;
    int maxF = hist[0];
    for (i = 1; i < 72; i++) {
        if (maxF < hist[i]) {
            maxF = hist[i];
            maxBin = i;
        }
    }
    
    NSLog(@"maxBin = %d, angle = %f", maxBin, (maxBin + 0.5)*5.0);
    
    NSArray *cand = [[NSArray alloc] 
                     initWithObjects:[[ColorInfo alloc] initWithAngle:237.5 
                                    title:@"エレベータA(青)" side:1],
                     [[ColorInfo alloc] initWithAngle:337.5 
                                    title:@"エレベータB(赤)" side:1],
                     [[ColorInfo alloc] initWithAngle:22.5 
                                    title:@"エレベータC(オレンジ)" side:2],
                     [[ColorInfo alloc] initWithAngle:187.5 
                                    title:@"エレベータD(緑)" side:2],nil];
    
    id cur;
    i = 0;
    int i_min = 0;
    double dist_min = 0.0;
    NSString *found_title;
    int found_side = 1;
    
    for (cur in cand)
    {
        ColorInfo *info = (ColorInfo *)cur;
        if (i == 0) {
            i_min = 0;
            dist_min = [info calcDistance:(maxBin + 0.5)*5.0];
            found_title = info.title;
            found_side = info.side;
        }
        else {
            double dist2 = [info calcDistance:(maxBin + 0.5)*5.0];
            if (dist_min > dist2) {
                i_min = i;
                dist_min = dist2;
                found_title = info.title;
                found_side = info.side;
            }
        }
        i++;
    }
    labMsg.text = found_title;
    delegate.elevatorSide = found_side;
    
    [cand release];
    CFRelease(data);
    
    [delegate stepNext];
}

- (void)viewDidUnload
{
    [self setImgView:nil];
    [self setLabMsg: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 {
    [imgView release];
    [labMsg release];
    [super dealloc];
}
@end

1Fでマクドナルド側の入り口に相当するマーカをかざし、目的地を1103に設定

エレベータ内でカラーバーを撮影し、当該エレベータを認識

エレベータ認識後の状態

エレベータを降りて左に向かい、最初の角で「角」ボタンを押した状態

次の角で「角」ボタンを押した状態

この角で左に曲がった状態