본문 바로가기
프로그래밍/iOS

맵뷰(MapView) 4장 - 원하는 위치를 표시(Pin Annotation)하자!!

by 백룡화검 2012. 2. 9.


MapKit에서 핀을 꼽는데 필요한 정보는 latitude/longitude가 필요하고, 핀을 터치했을때 팝업될 메시지로 Title/SubTilte을 필요로 합니다.

Pin을 꼽기 원하는 위치의 좌표를 NSArray로 잡아줍니다. 여기서는 랜덤으로 좌표를 구해서 10개정도 찍어보려합니다.

우선 좌표를 구해야 겠죠? 제가 핀을 꼽으려는 위치는 경기도 일산에 위치한 호수공원 주위입니다.

네이버 지도나 구글맵을 이용하시면 됩니다. 방법은 비슷합니다. 저는 네이버 지도를 이용했습니다.



 

으헝... 정사각형을 그리려고 했는데 망했네요 ;ㅅ; 저 빨간 네모 안에 좌표를 구해줄꺼에요. 모서리를 중심으로 잡습니다. 구글맵에는 '지도 중앙으로 설정' 옵션이 있는데 네이버꺼는 없네요. 야메로 해줍시다. 모서리를 우클릭해서 '여기를 확대'를 누르시면 배율과 관계없이 원하는 위치가 중심으로 잡힙니다.

이제 오른쪽 위에 메뉴중 'URL복사'를 누르고 복사해줍니다.



 

"http://map.naver.com/?dlevel=9&lat=37.6673231&lng=126.753411&menu=location&mapMode=0&enc=b64"

복사한 내용에 'lat=37.6673231', ' lng=126.753411' 위도와 경도가 보이시나요?

우리가 구해줄 좌표는 왼쪽 윗부분(A점)과 오른쪽 아랫부분(B점) 두 점의 위도와 경도 입니다.

대략 A의 위도는 37.66xxxx, 경도는 126.75xxxx, B의 위도는 37.64xxxx, 경도는 126.77xxxx 정도 됩니다.

랜덤으로 찍을 핀의 위도는 37.66크고 37.64작은 소숫점 6자리 숫자가 되어야겠네요. (경도 : 126.75< 경도 <126.77)


first이상 last이하 인경우 rand() % (last - first)+ first 입니다. 거기에 위도와 경도는 소숫점 6자리로 구성하려고 하기때문에  float형식이 필요합니다.(소숫점은 정확도와 관계가 있습니다.)


float a = ((rand() % (376655427 - 376529902) + 376529902) * 0.0000001);

좀 난잡해 보이네요. =( 그래도 할수 없지요. 야매로 찍어주려다 보니까 이렇게 되네요 ;ㅅ; 정상적인 좌표를 가지고 계시다면 NSArray나 NSDictionary등을 사용하셔서 좀더 쉽게 좌표를 잡을수 있습니다. 이제 Xcode로 갑시다.


우선 클래스 하나를 추가해줘야합니다.

Classes -> 우클릭 -> Add -> New File...


 
Objective-C class를 추가해 줍니다.

중앙에 보이는 Subclass of 'NSObject'를 선택해주세요.


 

클래스는 Pin으로 만들었습니다. 취향대로 해주세요 =)


 

Pin.h



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

@interface Pin : NSObject <MKAnnotation> {
   
    CLLocationCoordinate2D coordinate;
    NSString *title;
    NSString *subtitle;
}
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

@end

===============================================================================

NSObject를 상속받았고 MKAnnotation의 델리게이트를 따르는 Pin클래스입니다.

Pin은 3개의 인자를 갖습니다. CLLocationCoordinate2D형식의 coordinate, NSString형식의 title, subtitle.

coordinate는 핀을꼽게되는 좌표를, title와 subtitle는 핀을 터치했을때 뜨는 팝업의 제목과 설명을 나타낼것입니다.


Pin.m


 

#import "Pin.h"

@implementation Pin
@synthesize coordinate, title, subtitle;

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

@end

===============================================================================

@synthesize 로 받아준뒤 바로 해제합니다.


다시 testLocationViewController.h로 가서 헤더를 수정합니다.

방금 추가한 "Pin.h"를 #import해되고, 랜덤으로 잡아주는 10개의 좌표를 위해서 NSMutableArray를 만들어 봅시다. (위도/경도 2개)


 
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "Pin.h"

@interface testLocationViewController : UIViewController <CLLocationManagerDelegate> {
   
    CLLocationManager *locationManager;
    CLLocation *startPoint;
   
    MKMapView *myMapView;
   
    NSMutableArray *arrayLatitude;
    NSMutableArray *arrayLongitude;
}

@property (nonatomic, retain) CLLocationManager *locationManager;
@property (nonatomic, retain) CLLocation *startPoint;
@property (nonatomic, retain) NSMutableArray *arrayLatitude;
@property (nonatomic, retain) NSMutableArray *arrayLongitude;

@end

============================================================================

3장에 이어서, NSMutableArray 두개를 전역변수로 잡아줬습니다.


testLocationViewController.m으로 갑니다.


 
 

 
 
 
#import "testLocationViewController.h"

@implementation testLocationViewController
@synthesize locationManager, startPoint;
@synthesize arrayLatitude, arrayLongitude;

- (id) init {
    self = [super init];
    if (self != nil) {
        myMapView = [[MKMapView alloc] initWithFrame:CGRectMake(0.0, 20.0, 320.0, 460.0)];
        [myMapView setShowsUserLocation:YES];
        [myMapView setMapType:MKMapTypeStandard];
        [myMapView .userLocation setTitle:@"Here I am"];
        [self.view insertSubview:myMapView atIndex:0];
        
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = self;
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        [locationManager startUpdatingLocation];
        
        float rLatitude;
        float rLongitude;
        arrayLatitude = [[NSMutableArray alloc] init];
        arrayLongitude = [[NSMutableArray alloc] init];
        for (int i = 0 ; i < 10; i ++) {
            int a = rand();
            NSLog(@"A:%d",a);
            
            rLatitude = ((rand()%(376655427-376529902) + 376529902) * 0.0000001);
            rLongitude = ((rand()%(1267663371-1267552399) + 1267552399) * 0.0000001);
            
            NSNumber *temLatitude = [NSNumber numberWithFloat:rLatitude];
            NSNumber *temrLongitude = [NSNumber numberWithFloat:rLongitude];
            [arrayLatitude addObject:temLatitude];
            [arrayLongitude addObject:temrLongitude];
        }
        for (int i = 0; i < 10; i++) {
            NSLog(@"rLatitude : %f", [[arrayLatitude objectAtIndex:i] floatValue]);
            NSLog(@"arrayLongitude : %f", [[arrayLongitude objectAtIndex:i] floatValue]);
        }
        
        for (int i = 0; i < [arrayLatitude count] ; i++) {
            Pin *ann = [[Pin alloc] init];
            ann.title = [NSString stringWithFormat:@"좌표 %d", i];
            CLLocationCoordinate2D center;
            center.latitude = [[arrayLatitude objectAtIndex:i] doubleValue];
            center.longitude = [[arrayLongitude objectAtIndex:i] doubleValue];
            ann.coordinate = center;
            [myMapView addAnnotation:ann];
        }
    }
    return self;
}

- (void)locationManager:(CLLocationManager *)manager
     didUpdateToLocation:(CLLocation *)newLocation
                 fromLocation:(CLLocation *)oldLocation {

    if (startPoint == nil) {
        self.startPoint = newLocation;
    }
    
    NSLog(@"Location: %@", [newLocation description]);
    MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 100, 100);
    [myMapView setRegion:viewRegion animated:YES];
    [locationManager stopUpdatingLocation];
}


- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
    self.locationManager = nil;
    self.startPoint = nil;
}


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

@end

===========================================================================

이제 코드가 길어졌네요. 추가된 부분부터 이어가죠.


float rLatitude;
float rLongitude;

랜덤좌표를 계산하기 위해 float 형 변수 두개를 잡아줬습니다.


arrayLatitude = [[NSMutableArray alloc] init];
arrayLongitude = [[NSMutableArray alloc] init];

.h에서 전역변수로 만든 두 배열을 초기화 시켰습니다.


 for (int i = 0 ; i < 10; i ++) {
            int a = rand();
            NSLog(@"A:%d",a);
            
            rLatitude = ((rand()%(376655427-376529902) + 376529902) * 0.0000001);
            rLongitude = ((rand()%(1267663371-1267552399) + 1267552399) * 0.0000001);
            
            NSNumber *temLatitude = [NSNumber numberWithFloat:rLatitude];
            NSNumber *temrLongitude = [NSNumber numberWithFloat:rLongitude];
            [arrayLatitude addObject:temLatitude];
            [arrayLongitude addObject:temrLongitude];
}

int a = rand();
NSLog(@"A:%d",a);

rand함수로 어떤수가 잡히는지 로그를 띄워본 부분입니다. 무시하세요. ;ㅅ;


위에서 열심히 만들어준 계산법을 쓸시간입니다!

rLatitude = ((rand()%(376655427-376529902) + 376529902) * 0.0000001);

rLongitude = ((rand()%(1267663371-1267552399) + 1267552399) * 0.0000001);

이제 rLatitude와 rLongitude에 우리가 원하는 범위의 좌표가 들어가 있고요, 이것을 배열에 넣어줍니다.


NSNumber *temLatitude = [NSNumber numberWithFloat:rLatitude];

NSNumber *temrLongitude = [NSNumber numberWithFloat:rLongitude];

각각의 좌표를 NSNumber형으로 캐스트한뒤에


[arrayLatitude addObject:temLatitude];
[arrayLongitude addObject:temrLongitude];

어레이에 addObject로 배열에 넣습니다.


이걸 for문으로 10회 반복했습니다. 여기서 어레이에 안넣고 바로 쏴도 됩니다만, 이것저것 실습을 해야하니...


for (int i = 0; i < 10; i++) {
            NSLog(@"rLatitude : %f", [[arrayLatitude objectAtIndex:i] floatValue]);
            NSLog(@"arrayLongitude : %f", [[arrayLongitude objectAtIndex:i] floatValue]);
}

그다음의 포문은 별거없지요. Debugger Console에 좌표가 잘 잡혔나 띄워봤습니다.


for (int i = 0; i < [arrayLatitude count] ; i++) {
            Pin *ann = [[Pin alloc] init];
            ann.title = [NSString stringWithFormat:@"좌표 %d", i];
            CLLocationCoordinate2D center;
            center.latitude = [[arrayLatitude objectAtIndex:i] doubleValue];
            center.longitude = [[arrayLongitude objectAtIndex:i] doubleValue];
            ann.coordinate = center;
            [myMapView addAnnotation:ann];
}

자 이제 드디어 핀을 꼽아봅시다 ;ㅅ;

For문으로 [arrayLatitude count] 만큼 반복을 할껍니다. "10"이라고 수를 그냥 넣으셔도 되지만, 변수로 넣으시는 버릇을 들이시는게 나중을 위해 좋습니다. 이런 짧은 코드가 아니라 긴 코드에서 그냥 숫자로 넣을경우 나중에 디버그나 수정하실때 왜 그숫자가 들어갔는지 파악하는데 시간이 좀 걸립니다. =)


Pin *ann = [[Pin alloc] init];

ann.title = [NSString stringWithFormat:@"좌표 %d", i];

핀을 터치했을때 뜨는 팝업의 타이틀을 설정했습니다. 좌표가 구성된 순서대로 좌표 1, 좌표 2, 좌표 3..... 이 될껍니다.


CLLocationCoordinate2D center;

CLLocationCoordinate2D는 CLLocationDegress형식의 latitude와 longitude를 가지고 있는 구조체 입니다.


center.latitude = [[arrayLatitude objectAtIndex:i] doubleValue];
center.longitude = [[arrayLongitude objectAtIndex:i] doubleValue];

각각의 부분에 배열로 만들어줬던 값들을 할당합니다.


ann.coordinate = center;
[myMapView addAnnotation:ann];

이렇게 만들어준 핀을 myMapView에 추가해줍니다.


랜덤생성된 좌표들의 로그입니다.




호수공원과 그 근처에 찍힌 10개의 핀입니다.

 

 
 subtilte를 활용하셔서 각핀의 좌표를 출력하셔도 되고 현재 위치와의 거리를 뽑아내셔도 됩니다.


4장 끗!!


==============================================================================

시니여님 질문을 적용한 사진입니다. 첨부파일도 추가합니다.