The code below is for conducting search with MKLocalSearch and loading the results into an array.
This array gets passed to my [self.mapView addAnnotations:annotations] method. Everything works great until I try to dismiss this viewcontroller by tapping the back button (in my navigation bar for storyboards).
I get EXC_BAD_ACCESS(code=1, address=0x4). If I comment out the Show Pins section below the problem goes away (but of course I am now not loading my annotations).
Please help!
-(void)issueLocalSearchLookup:(NSString *)searchString usingPlacemarksArray:(NSArray *)placemarks {
self.coords = mapView.userLocation.coordinate;
// Set the size of the region we want to get search results for.
MKCoordinateSpan span = MKCoordinateSpanMake(0.001250, 0.001250);
MKCoordinateRegion region = MKCoordinateRegionMake(mapView.userLocation.coordinate, span);
[self.mapView setRegion:region animated:YES];
// Create the search request
self.localSearchRequest = [[MKLocalSearchRequest alloc] init];
self.localSearchRequest.region = region;
self.localSearchRequest.naturalLanguageQuery = searchString;
// Perform the search request...
self.localSearch = [[MKLocalSearch alloc] initWithRequest:self.localSearchRequest];
[self.localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
if(error){
NSLog(#"localSearch startWithCompletionHandlerFailed! Error: %#", error);
return;
} else {
// We are here because we have data!
for(MKMapItem *mapItem in response.mapItems){
// Show pins...
NSMutableArray *annotations = [NSMutableArray array];
Annotation *annotation = [[Annotation alloc] initWithCoordinate: mapItem.placemark.location.coordinate];
annotation.title = mapItem.name;
annotation.subtitle = mapItem.placemark.addressDictionary[(NSString *)kABPersonAddressStreetKey];
[mapView addAnnotation:annotation];
NSLog(#"Name for result: = %#", mapItem.name);
[self.mapView addAnnotations:annotations];
NSLog(#"Name for result: = %#", mapItem.name);
}
MKCoordinateSpan span = MKCoordinateSpanMake(0.01, 0.01);
MKCoordinateRegion region = MKCoordinateRegionMake(self.coords, span);
[self.mapView setRegion:region animated:YES];
}
}];
}
I had set my custom annotation to be a subclass of NKPlacemark....I needed to have it be a subclass of NSObject.
Related
I got 2 ViewControllers:
In the first I got TableView with name of countries.
When I click on one of the countries's cell it moves to the second ViewControl with performSegueWithIdentifier & saves the cell's name (for example: "France") in a variable outside the ViewController. in the seconds ViewController I got a MapKit View. I don't know how to make the map to go to "France".
Any suggestions?
- (void)performSearchWithLocationName:(NSString*)CountryName{
//#define METERS_PER_MILE 1.344
MKLocalSearchRequest *request =
[[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = CountryName;
request.region = _mapView.region;
MKLocalSearch *search =
[[MKLocalSearch alloc]initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse
*response, NSError *error) {
if (response.mapItems.count == 0){
NSLog(#"No Matches");
}
else{
for (MKMapItem *item in response.mapItems)
{
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = item.placemark.coordinate;
annotation.title = item.name;
NSLog(#"%#", annotation.title);
[_mapView addAnnotation:annotation];
MKCoordinateRegion region;
region = MKCoordinateRegionMakeWithDistance(annotation.coordinate,0.3*METERS_PER_MILE, 0.3*METERS_PER_MILE);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
}
}
}];
}
//You can implement map delegate
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
NSLog(#"selected annotation and it's coordinates =>%f & %f",[view.annotation coordinate].latitude,[view.annotation coordinate].longitude);
}
Setp 1 : Getting latitude & longitude using location name
Setp 2 : Create the CLLocationCoordinate2D Using that latitude and longitude
Setp 3 : Create the MKCoordinateRegion
Setp 2 : Change the Mapview Region
I plot the route between source and destination using the following piece of code. After plotting the route sucessfully, I get a list of coordinates where I wanted to plot another route on top of existing one with different color.
The use case I am trying to code is, we set source and destination, plot the dotted route once, then plot the actual line based on the list of locations
From the code, I can see the dotted line but can not see the another line overlapping on it.
My code is:
-(void) getDirection {
CLLocationCoordinate2D sourceCoords = CLLocationCoordinate2DMake(42.130655, -71.041158);
MKPlacemark *sourcePlacemark = [[MKPlacemark alloc] initWithCoordinate:sourceCoords addressDictionary:nil];
MKMapItem *source = [[MKMapItem alloc] initWithPlacemark:sourcePlacemark];
// Make the destination location
CLLocationCoordinate2D destinationCoords = CLLocationCoordinate2DMake(42.128121, -70.936151); MKPlacemark *destinationPlacemark = [[MKPlacemark alloc] initWithCoordinate:destinationCoords addressDictionary:nil];
MKMapItem *destination = [[MKMapItem alloc] initWithPlacemark:destinationPlacemark];
MKDirectionsRequest *directionsRequest = [MKDirectionsRequest new];
[directionsRequest setSource:source];
[directionsRequest setDestination:destination];
[self sourceMarker:sourcePlacemark];
[self destinationMarker:destinationPlacemark];
//
//routeCoords
NSInteger nuberOfStesps = routeCoords.count;
CLLocationCoordinate2D *pointsCoordinate = (CLLocationCoordinate2D *)malloc(sizeof(CLLocationCoordinate2D) * nuberOfStesps);
for (NSInteger index=0; index < nuberOfStesps; index++) {
CLLocation *location = [routeCoords objectAtIndex:index];
coordinate2 = location.coordinate;
//NSLog(#"Location Latitide : %f", coordinate2.latitude);
//NSLog(#"Location Longitude : %f", coordinate2.longitude);
pointsCoordinate[index] = CLLocationCoordinate2DMake(coordinate2.latitude, coordinate2.longitude);
}
routeLine = [MKPolyline polylineWithCoordinates:pointsCoordinate count:nuberOfStesps];
// free(pointsCoordinate);
routeLine.title = #"New points";
[self.mapView addOverlay:routeLine];
//
MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(#"There was an error getting your directions");
return;
}else {
[self showRoute:response];
}
}];
}
- (void) showRoute:(MKDirectionsResponse * ) response {
_currentRoute = [response.routes firstObject];
[self.mapView setVisibleMapRect:_currentRoute.polyline.boundingMapRect animated:NO];
[self.mapView removeOverlays:self.mapView.overlays];
[self.mapView addOverlay:_currentRoute.polyline level:MKOverlayLevelAboveRoads];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolyline class]]) {
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
[renderer setStrokeColor:[UIColor redColor]];
[renderer setLineWidth:4.0];
[renderer setLineDashPattern:#[#2, #5]];
[renderer setStrokeColor:[UIColor redColor]];
return renderer;
}
return nil;
}
I followed this thread iOS Maps draw route (line) between several points (geopoints)
But it did not work for me. What am I doing wrong?
The route is clearly being made but the polyline is not drawn. I am able to find the total distance, and have verified that the coordinates we are using are not (0,0). Is there something wrong with the delegate, since it seems that both the addOverlay and addAnnotation (called in a custom method shown below called createAndAddAnnotationForCoordinate) methods are not working?
-(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//generates map view
mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width,
self.view.bounds.size.height)];
//setting delegate to self is not fixing the error
mapView.delegate = self;
mapView.mapType = MKMapTypeStandard;
//converting CLLocationCoordinate2D to MKPlacemark
MKPlacemark *startPlacemark = [[MKPlacemark alloc]
initWithCoordinate: addressOneCoords addressDictionary:
[NSDictionary dictionaryWithObjectsAndKeys:nil]];
MKPlacemark *endPlacemark = [[MKPlacemark alloc]initWithCoordinate: addressTwoCoords addressDictionary:[NSDictionary dictionaryWithObjectsAndKeys:nil]];
//converting MKPlacemark to MKMapItem
MKMapItem *start = [[MKMapItem alloc ]initWithPlacemark:startPlacemark];
MKMapItem *end = [[MKMapItem alloc]initWithPlacemark:endPlacemark];
MKDirectionsRequest *request = [MKDirectionsRequest new];
[request setSource:start];
[request setDestination:end];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
request.requestsAlternateRoutes = YES;
//Just to check if the coordinates were transferred successfully between view controllers and they did transfer successfully
NSLog(#"address one lat is %f",addressOneCoords.latitude);
NSLog(#"address one lon is %f",addressOneCoords.longitude);
NSLog(#"address two lat is %f",addressTwoCoords.latitude);
NSLog(#"address two lon is %f",addressTwoCoords.longitude);
MKDirections *directions = [[MKDirections alloc]initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse response, NSError error){
//if the route can't be created
if(error){
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Unable to create route" message:#"Go back to check if addresses are valid" delegate:nil cancelButtonTitle:#"Ok"otherButtonTitles:nil];
[alert show];
}
else{
[mapView removeOverlays:self.mapView.overlays];
MKRoute *mainRoute = [response.routes firstObject];
routeLine = mainRoute.polyline;
if(routeLine){
[self.mapView removeOverlay:routeLine];
}
//the addOverlay method is not drawing the polyline
[self.mapView addOverlay: routeLine level:MKOverlayLevelAboveRoads];
//proof that route is being created successfully
NSLog(#"Total distance is %f", mainRoute.distance);
MKMapPoint middlePoint = mainRoute.polyline.points[mainRoute.polyline.pointCount/2];
//also, the addannotation method is not being called either it seems like
[self createAndAddAnnotationForCoordinate:MKCoordinateForMapPoint(middlePoint)];
}
}];
}
Our createAndAddAnnotationForCoordinate method,
-(void) createAndAddAnnotationForCoordinate : (CLLocationCoordinate2D)coordinate{
MKPointAnnotation* annotation = [[MKPointAnnotation alloc]init];
annotation.coordinate = coordinate;
annotation.title = #"Point";
annotation.subtitle = #"subtitle";
[mapView addAnnotation:annotation];
}
Our overridden mapviewdelegate method,
-(MKOverlayRenderer )mapView:(MKMapView )mapView rendererForOverlay:(id<MKOverlay>)overlay{
if([overlay isKindOfClass:[MKPolyline class]]){
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc]initWithOverlay:overlay];
renderer.strokeColor = [UIColor redColor];
renderer.lineWidth = 10.0f;
return renderer;
}
else
return nil;
}
The output if address one was somewhere in NJ and address two was somewhere in CA:
address one lat is 40.902599
address one lon is -74.407097
address two lat is 34.054435
address two lon is -118.253393
Total distance is 4459771.000000
Ron, I'm suspecting your polyline (mainRoute.polyline) - your rendererForOverlay looks almost exactly like one I am using successfully. Barring the very basic mistakes like not setting the MKMapView delegate, or setting it to some other object, I would be almost sure the polyline you add to the map does not contain valid CLLocationCoordinate2D structs.
In Swift, creation goes like
var coordinatePtr = UnsafeMutablePointer<CLLocationCoordinate2D>(track2DCoordinates)
let trackPolygon = MKPolyline(coordinates: coordinatePtr, count: track2DCoordinates.count)
mapView.removeOverlays(mapView.overlays)
mapView.addOverlay(trackPolygon)
Start by verifying that you really have a valid MKPolyline.
I'm also not sure about your middlePoint calculation.
MKMapPoint middlePoint = mainRoute.polyline.points[mainRoute.polyline.pointCount/2];
This kind of thing probably works right now but in Swift you need to be a lot more careful of the data types used as index. What if you have an odd number of points, or zero?
So let's say I'm trying to search for a query within 1 mile radius of the user's current location. Here's my search code so far:
- (void) performLocalSearch{
MKLocalSearchRequest *searchRequest = [[MKLocalSearchRequest alloc] init];
searchRequest.naturalLanguageQuery = _searchText;
MKCoordinateRegion searchRegion;
searchRegion.center.latitude = userLocation.coordinate.latitude;
searchRegion.center.longitude = userLocation.coordinate.longitude;
searchRegion.span.latitudeDelta = 0.0144927536231884;
searchRegion.span.longitudeDelta = 0.0144927536231884;
searchRequest.region = searchRegion;
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:searchRequest];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
for (MKMapItem *item in response.mapItems) {
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.title = item.name;
annotation.coordinate = item.placemark.coordinate;
[_map addAnnotation:annotation];
}
}
];
}
When I run the code on my device, pins show up on the map, but they are not within the specified radius. I also tried using MKCoordinateRegionMakeWithDistance to set the search region but I couldn't figure out how to cast the double radius value as a CLLocationDistance object. Any help would be appreciated. Thanks.
There must be an easy way to do this or somewhere I am going wrong but I can't seem to save my current location as a global variable and then add it to my array to populate my UITableView.
Basically, at the moment I have an empty UITableView that is used to populate results from a local search, via a search bar. This part works great. BUT, what I want is to always have the 1st row as the users current location, like in maps, google maps, reminders etc. I figured this would be a simple task but I cant get it to work. Can someone help me please.
I use the following code to get my current location, reverse geocode it, and plot it on the map when the app starts:
self.locationManager = [[CLLocationManager alloc]init];
if ([CLLocationManager locationServicesEnabled]) {
self.locationManager.delegate = self;
self.locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];
}
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = _locationManager.location.coordinate.latitude;
zoomLocation.longitude= _locationManager.location.coordinate.longitude;
currentCoord.latitude = _locationManager.location.coordinate.latitude;
currentCoord.longitude= _locationManager.location.coordinate.longitude;
//reverse geocoder
CLLocation *currentLocation = [[CLLocation alloc] initWithLatitude:zoomLocation.latitude longitude:zoomLocation.longitude];
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
for (CLPlacemark *placemark in placemarks) {
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = zoomLocation;
annotation.title = #"Current Location";
annotation.subtitle = placemark.name;
// 2
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
// 3
[_mapView setRegion:viewRegion animated:YES];
[_mapView addAnnotation:annotation];
}
}];
When I run my local search I use the following code to populate my array to load the table with:
MKLocalSearchRequest *request =
[[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = _searchBar.text;
request.region = _mapView.region;
MKLocalSearch *search =
[[MKLocalSearch alloc]initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse
*response, NSError *error) {
if (response.mapItems.count == 0)
NSLog(#"No Matches");
else
for (MKMapItem *item in response.mapItems)
{
[tableData addObject:item];
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = item.placemark.coordinate;
annotation.title = item.placemark.name;
annotation.subtitle = item.placemark.title;
[annotations addObject:annotation];
[_tableResults reloadData];
}
}];
I really can't figure this out. How would I add my current location? Would really appreciate some advise. Thanks in advance guys!
Try this,
[search startWithCompletionHandler:^(MKLocalSearchResponse
*response, NSError *error) {
if (response.mapItems.count == 0)
NSLog(#"No Matches");
else
for (MKMapItem *item in response.mapItems)
{
[tableData addObject:item];
[annotations addObject:[self annotationFromMapItem:item]];
}
double latitude = self.locationManager.location.coordinate.latitude;
double longitude = self.locationManager.location.coordinate.longitude;
MKPlacemark *placemark = [[[MKPlacemark alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude, longitude) addressDictionary:nil] autorelease];
MKMapItem *mapItem = [[[MKMapItem alloc] initWithPlacemark:placemark] autorelease];
[mapItem setName:Current Location];
[tableData insertObject:mapItem atIndex:0];
[annotations insertObject:[self annotationFromMapItem:mapItem] atIndex:0];
[_tableResults reloadData];
}];
- (MKPointAnnotation) annotationFromMapItem:(MKMapItem *)item {
MKPointAnnotation *annotation =
[[MKPointAnnotation alloc]init];
annotation.coordinate = item.placemark.coordinate;
annotation.title = item.placemark.name;
annotation.subtitle = item.placemark.title;
return annotation;
}