How to access the category or type of an MKMapItem

4k views Asked by At

I'm writing an Application that makes use of MapKit. I have implemented MKLocalSearch and I get back an array of MKMapItem's. However I was wondering if it was possible to get the category of each of these items.For example in the Maps application there is different icons displayed for shops, hotels, train stations and so on. Also if you view a place mark.You get a category label such as Grocery. As a developer can I access that information for a Map Item? If so I would like to know how.

Thank You

4

There are 4 answers

6
Mehul Patel On

Yes you can get this information. See below method for information details from search location.

I fear that you can only get address details from MKPlacemark.

What now you have to do is, get address details from MKPlacemark and you need to take a help of any open source API that can help you to categories addresses into some labels/annotation.

One of the good API is Mapbox, but unfortunately it is paid.

So from third party API you can do magical search. I haven't search for kind of API / WebService but it should be there.

Objective C Code:

- (void) searchForPlace:(NSString *) keyWord {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

    MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
    request.naturalLanguageQuery = keyWord; // @"restaurant"
    MKCoordinateSpan span = MKCoordinateSpanMake(.1, .1);

    CLLocationCoordinate2D location = self.mapView.centerCoordinate;
    request.region = MKCoordinateRegionMake(location, span);
    MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];

    [search startWithCompletionHandler:
     ^(MKLocalSearchResponse *response, NSError *error) {
         [self.txtSearch setEnabled:YES];
         [self removeMapOverlay];

         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
         if (!error) {
             // Result found
             @try {

                 if (response.mapItems && [response.mapItems count] > 0) {

                     for (MKMapItem *item in response.mapItems) {
                         MKPlacemark *placeMark = item.placemark;

                         // Address details
                         NSDictionary *address = placeMark.addressDictionary;
                         NSString *titleString = @"";
                         NSString *subtitleString = @"";
                         NSString *name = @"";
                         NSString *Thoroughfare = @"";
                         NSString *State = @"";
                         NSString *City = @"";
                         NSString *Country = @"";

                         name = [address objectForKey:@"Name"] ? [address objectForKey:@"Name"] : @"";
                         Thoroughfare = [address objectForKey:@"Thoroughfare"] ? [address objectForKey:@"Thoroughfare"] : @"";
                         State = [address objectForKey:@"State"] ? [address objectForKey:@"State"] : @"";
                         City = [address objectForKey:@"City"] ? [address objectForKey:@"City"] : @"";
                         Country = [address objectForKey:@"Country"] ? [address objectForKey:@"Country"] : @"";

                         titleString = [NSString stringWithFormat:@"%@ %@", name, Thoroughfare];
                         subtitleString = [NSString stringWithFormat:@"%@ %@ %@", State, City, Country];

                         CustomAnnotation *annotation = [[CustomAnnotation alloc] initWithTitle:titleString subTitle:subtitleString detailURL:item.url location:placeMark.location.coordinate];
                         [self.mapView addAnnotation:annotation];
                     }
                     [self mapView:self.mapView regionDidChangeAnimated:YES];
                 }

             }
             @catch (NSException *exception) {
                 NSLog(@"Exception :%@",exception.description);
             }

         } else {
             NSLog(@"No result found.");
         }
     }];
}

Swift Code:

func searchForPlace(keyword: String) {

    UIApplication.sharedApplication().networkActivityIndicatorVisible = true

    var requset = MKLocalSearchRequest()
    requset.naturalLanguageQuery = keyword

    let span = MKCoordinateSpanMake(0.1, 0.1)
    let region = MKCoordinateRegion(center: self.mapView.centerCoordinate, span: span)

    var search = MKLocalSearch(request: requset)

    search.startWithCompletionHandler { (var response: MKLocalSearchResponse!, var error: NSError!) -> Void in
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false

        if (error != nil) {
            // Result found

            if (response.mapItems != nil && response.mapItems.count > 0) {

                for item: MKMapItem! in response.mapItems as [MKMapItem] {
                    var placeMark = item.placemark as MKPlacemark!

                    // Address details...
                    var address = placeMark.addressDictionary as NSDictionary!
                    var titleString: String!
                    var subtitleString: String!
                    var name: String!
                    var Thoroughfare: String!
                    var State: String!
                    var City: String!
                    var Country: String!

                    var emptyString: String! = " "

                    name = (address.objectForKey("name") != nil ? address.objectForKey("name") : emptyString) as String
                    Thoroughfare = (address.objectForKey("Thoroughfare") != nil ? address.objectForKey("Thoroughfare") : emptyString) as String
                    State = (address.objectForKey("State") != nil ? address.objectForKey("State") : emptyString) as String
                    City = (address.objectForKey("City") != nil ? address.objectForKey("City") : emptyString) as String
                    Country = (address.objectForKey("Country") != nil ? address.objectForKey("Country") : emptyString) as String

                    titleString = String(format: "%@ %@", name, Thoroughfare)
                    subtitleString = String(format: "%@ %@ %@", State, City, Country)

                    var customAnnotation = CustomAnnotation(coordinate: placeMark.location.coordinate, title: titleString, subtitle: subtitleString, detailURL: item.url)
                    self.mapView.addAnnotation(customAnnotation)
                }

                self.mapView(self.mapView, regionDidChangeAnimated: true)
            }
        }

    }
}
0
milesau On

For anyone still looking a few years later, MKMapItem has a pointOfInterestCategory attribute

0
GuanacoBE On
func retriveCategory(item:MKMapItem) -> [String] {
    let geo_place = item.value(forKey: "place") as! NSObject
    let geo_business = geo_place.value(forKey: "business") as! NSObject
    let categories = geo_business.value(forKey: "localizedCategories") as! [AnyObject]

    var categoriesList = [String]()

    if let listCategories = (categories.first as? [AnyObject]) {
        for geo_cat in listCategories {
            let geo_loc_name = geo_cat.value(forKeyPath: "localizedNames") as! NSObject
            let name = (geo_loc_name.value(forKeyPath: "name") as! [String]).first!

            categoriesList.append(name)
        }
    }

    return categoriesList
}

Another version of the answer @Will Von Ullrich

0
Will Von Ullrich On

I know this is an old question, but I stumbled upon the same need - getting the category info from a MKMapItem object.

Q: Can you get category information from a MKMapItem?

A: Yes this is possible. You can pull this from a map item.


Q: Is this allowed by Apple?

A: Not really... you must use key-values to get these properties, so it technically would be accessing private frameworks. BUT, you didnt ask if it had to be "allowed."


Here's a nice method that I have (it was in Objc but converted it to swift for you, so forgive me if there are any bugs - haven't tested this)

// Let "item" be some MKMapItem

let geo_place = item.value(forKey: "place") as! NSObject
let geo_business = geo_place.value(forKey: "business") as! NSObject
let categories = geo_business.value(forKey: "localizedCategories") as! [AnyObject]

var categoriesList = [Any]()

for geo_cat in categories.first as! [AnyObject] {

    let level = geo_cat.value(forKeyPath: "level") as! Int
    let geo_loc_name = geo_cat.value(forKeyPath: "localizedNames") as! NSObject
    let name = (geo_loc_name.value(forKeyPath: "name") as! [String]).first

    categoriesList.append(["level" : NSNumber(value: level), "name" : name as Any])

}

This gives you categories like:

{

    level : 1;
    name : "Coffee & Tea"

}