MapKit上にポリラインの経路を描画するには

### MapKit上にポリラインの経路を描画するには

経路を表すCSVファイルの内容は以下のようにする（カンマの後ろに半角スペースは入れない）。

```
35.7319802777778,139.7251925
35.7319802777778,139.7251925
35.7319238888889,139.725016666667
35.7319033333333,139.724955833333
35.7315475,139.725145833333
35.7314455555556,139.724840833333
35.7313033333333,139.724543611111
35.73079,139.724795277778
35.7307391666667,139.724551111111
35.7305155555556,139.724314444444
35.7303875,139.724231111111
35.7302358333333,139.72404
35.7298288888889,139.723658333333
35.7293147222222,139.723254722222
35.7291594444444,139.723546666667
```

CSVファイルをパースするには以下のようなメソッドを用意する。

```objc:MapLineViewController.m

...

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        ...
        points = [self getLocationPointsFromCsvFile:@"route"];
        _routeView = [[CSMapRouteLayerView alloc] initWithRoute:points mapView:_mapView];
        ...
    }
}
...

- (NSMutableArray *)getLocationPointsFromCsvFile:(NSString *)filename {
	//
	// 内部のリソースの地点の集合をロードする
	//
    NSString* filePath = [[NSBundle mainBundle] pathForResource:filename ofType:@"csv"];
	NSString* fileContents = [NSString stringWithContentsOfFile:filePath];
	NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
	
	NSMutableArray* points = [[NSMutableArray alloc] initWithCapacity:pointStrings.count];
	
	for(int idx = 0; idx < pointStrings.count; idx++)
	{
		// 文字列を分解して緯度と軽度を取得する
		NSString* currentPointString = [pointStrings objectAtIndex:idx];
		NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
		
		CLLocationDegrees latitude  = [[latLonArr objectAtIndex:0] doubleValue];
		CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
		
		CLLocation* currentLocation = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
		[points addObject:currentLocation];
	}
    return points;
}
```

実際にMapKit上に経路を描画するには以下のようにする。

```objc:CSMapRouteLayerView.h
#import <MapKit/MapKit.h>

@interface CSMapRouteLayerView : UIView <MKMapViewDelegate>
{
	MKMapView* _mapView;
	NSArray* _points;
	UIColor* _lineColor;
}

-(id) initWithRoute:(NSArray*)routePoints mapView:(MKMapView*)mapView;

@property (nonatomic) NSArray* points;
@property (nonatomic) MKMapView* mapView;
@property (nonatomic) UIColor* lineColor; 

@end
```

```objc:CSMapRouteLayerView.m
#import "CSMapRouteLayerView.h"


@implementation CSMapRouteLayerView
@synthesize mapView   = _mapView;
@synthesize points    = _points;
@synthesize lineColor = _lineColor; 

-(id) initWithRoute:(NSArray*)routePoints mapView:(MKMapView*)mapView
{
	self = [super initWithFrame:CGRectMake(0, 0, mapView.frame.size.width, mapView.frame.size.height)];
	[self setBackgroundColor:[UIColor clearColor]];
	
	[self setMapView:mapView];
	[self setPoints:routePoints];
	
	// 表示領域の上限、下限などを決める
	CLLocationDegrees maxLat = -90;
	CLLocationDegrees maxLon = -180;
	CLLocationDegrees minLat = 90;
	CLLocationDegrees minLon = 180;
	
	for(int idx = 0; idx < self.points.count; idx++)
	{
		CLLocation* currentLocation = [self.points objectAtIndex:idx];
		if(currentLocation.coordinate.latitude > maxLat)
			maxLat = currentLocation.coordinate.latitude;
		if(currentLocation.coordinate.latitude < minLat)
			minLat = currentLocation.coordinate.latitude;
		if(currentLocation.coordinate.longitude > maxLon)
			maxLon = currentLocation.coordinate.longitude;
		if(currentLocation.coordinate.longitude < minLon)
			minLon = currentLocation.coordinate.longitude;
	}
	
	MKCoordinateRegion region;
	region.center.latitude     = (maxLat + minLat) / 2;
	region.center.longitude    = (maxLon + minLon) / 2;
	region.span.latitudeDelta  = maxLat - minLat;
	region.span.longitudeDelta = maxLon - minLon;
	
	[self.mapView setRegion:region];
	[self.mapView setDelegate:self];
	[self.mapView addSubview:self];
	
	return self;
}


- (void)drawRect:(CGRect)rect
{
	// 画面遷移中や描画中の点がない場合のみ描画する
	if(!self.hidden && nil != self.points && self.points.count > 0)
	{
		CGContextRef context = UIGraphicsGetCurrentContext(); 
		
		if(nil == self.lineColor)
			self.lineColor = [UIColor blueColor];
		
		CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
		CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);

		// 太さ2.0でラインを引く
		CGContextSetLineWidth(context, 2.0);
		
		for(int idx = 0; idx < self.points.count; idx++)
		{
			CLLocation* location = [self.points objectAtIndex:idx];
			CGPoint point = [_mapView convertCoordinate:location.coordinate toPointToView:self];
			
			if(idx == 0)
			{
				// 最初の点に移動
				CGContextMoveToPoint(context, point.x, point.y);
			}
			else
			{
                // ラインを引く
				CGContextAddLineToPoint(context, point.x, point.y);
			}
		}
		
		CGContextStrokePath(context);
	}
}


#pragma mark mapView delegate functions
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
    // 表示領域を変更しているあいだはビューの描画を止める。
    // 画面移動中に間違った位置にラインが引かれるのを防ぐ。
	self.hidden = YES;
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
	// 経路表示を再度可能にし、場所をセットし直す
	self.hidden = NO;
	[self setNeedsDisplay];
}

@end
```