Finding Business Listings and Displaying with MapKit – Part 1

In this three part series, I want to show you how you can easily show a user business listings based on their current location. To do so we’ll be using the CoreLocation and MapKit Frameworks. First, we need a way to actually find nearby businesses. There are a number of third-party mapping API’s out there, some with paid subscription, but for a simpler application there is a simpler solution. For the first part in this series we are going to connect to the Google Maps service directly and retrieve xml output with which we will later use to populate MapKit. First, let’s take a look at the actual Google Maps call. It will look something like this:

http://maps.google.com/maps?q=Apple&mrt=yp&sll=26.1209,-80.1444&output=kml

With the Google Maps API there are a number of parameters you can use to get different information. In this case we are set the “q” param as the search keyword. Next we have the “mrt” param. This specifies what type of search you want to do. In the case we are setting this to “yp” which is short for “Yellow Pages”. This means we’ll be returned a list of business information much like a phone book look-up. Next we set the “sll” or “search latitude and longitude”. Finally, we specify the “output” type as “kml”. KML is based on XML standard and is used specifically to describe geographic data. For more on the available parameters, check out Google’s MapKi documentation.

NOTE: Typically JSON is the data format of choice for iphone because of it’s size and ease to parse. In this case, I found that XML was typically smaller in size, so it was my choice.

The first thing you should do is add the MapKit and CoreLocation frameworks to your project. Then, let’s just right into the MapDataParser class.

#import <Foundation/Foundation.h>
#import "MapLocationVO.h"

 

@interface MapDataParser : NSObject <NSXMLParserDelegate>
{
    BOOL isPlacemark;
   
    NSXMLParser *mapsDataParser;
    NSMutableString *currentElementValue;
    NSMutableArray *locationData;
    MapLocationVO *currentMapLocation;
    NSMutableString *currentElementName;
}

@property (nonatomic, retain) NSMutableArray *locationData;

-(void) getBusinessListingsByKeyword:(NSString *)keyword atLat:(float)lat atLng:(float)lng;
-(void) parseXMLFileAtURL:(NSString *)URL;

@end

Here we define the NSXMLParser and the set the NSXMLParserDelegate for the class. The locationData array is where other classes will later have access to the parsed data. Finally, we have the getBusinessListingsByKeyword method, which accepts the search term and the center latitude and longitude. Let’s take a look at how this is implemented.

#import "MapDataParser.h"
#import "MapLocationVO.h"

 


@implementation MapDataParser

@synthesize locationData;

-(void) getBusinessListingsByKeyword:(NSString *)keyword atLat:(float)lat atLng:(float)lng
{
    // Construct our maps call.
   
    [self parseXMLFileAtURL:[NSString stringWithFormat:@"http://maps.google.com/maps?q=%@&mrt=yp&sll=%g,%g&output=kml", [keyword stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding], lat, lng]];
}

- (void)parseXMLFileAtURL:(NSString *)URL
{  
    // parse file at url
   
    NSURL *xmlURL = [NSURL URLWithString:URL];
   
    mapsDataParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
    mapsDataParser.delegate = self;
    [mapsDataParser setShouldProcessNamespaces:NO];
    [mapsDataParser setShouldReportNamespacePrefixes:NO];
    [mapsDataParser setShouldResolveExternalEntities:NO];
   
    [mapsDataParser parse];
}

 - (void)parserDidStartDocument:(NSXMLParser *)parser
{
    // Parser has started so create locationData array to store MapLocation value objects
   
    if(self.locationData)
    {
        [self.locationData removeAllObjects];
    }
    else
    {
        self.locationData = [[NSMutableArray alloc] init];
    }
}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
    // Unable to reach the service, show error alert
   
    NSString * errorString = [NSString stringWithFormat:@"Unable to get map data. (Error code %i )", [parseError code]];
    UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:@"Error loading content" message:errorString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [errorAlert show];
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if ([elementName isEqualToString:@"Placemark"])
    {
        // Placemark item found, prepare to save data to new MapLocationVO
       
        currentMapLocation = [[MapLocationVO alloc] init];
        isPlacemark = YES;
    }
    else
    {
        // Element other than Placemark found, store it for "foundCharacters" check
       
        [currentElementName release];
        currentElementName = nil;
        currentElementName = [[NSString alloc] initWithString:elementName];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([elementName isEqualToString:@"Placemark"])
    {
        // Placemark element ended, store data
       
        isPlacemark = NO;
       
        NSLog(@"Map Item %i Added", [self.locationData count]);
        NSLog(@"Title: %@", currentMapLocation.locationTitle);
        NSLog(@"Snippet: %@", currentMapLocation.locationSnippet);
        NSLog(@"Coordinates: %@", currentMapLocation.mapLocation);
       
        [self.locationData addObject: currentMapLocation];
       
        [currentMapLocation release];
        currentMapLocation = nil;
    }  
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if(isPlacemark)
    {      
        // If we are currently in a Placemark element,
        // check if current is something we want to store
       
        if ([currentElementName isEqualToString:@"name"])
        {
            // Store Location Title
           
            if(!currentMapLocation.locationTitle)
            {
                currentMapLocation.locationTitle = [[NSMutableString alloc] initWithString:string];
            }
            else
            {
                [currentMapLocation.locationTitle appendString: string];
            }
        }
        else if ([currentElementName isEqualToString:@"Snippet"])
        {
            // Replace line breaks with new line
           
            currentMapLocation.locationSnippet = currentMapLocation.locationSnippet = [[NSMutableString alloc] initWithString:[string stringByReplacingOccurrencesOfString:@"<br/>" withString:@"\n"]];
        }
        else if ([currentElementName isEqualToString:@"coordinates"])
        {      
            // Create a coordinate array to use when allocating CLLocation.
            // We will use the CLLocation object later when populating the map
           
            NSArray *coordinatesList = [string componentsSeparatedByString:@","];
            currentMapLocation.mapLocation = [[CLLocation alloc] initWithLatitude:[[coordinatesList objectAtIndex:1] floatValue] longitude:[[coordinatesList objectAtIndex:0] floatValue]];
        }
    }
}

- (void)parserDidEndDocument:(NSXMLParser *)parser
{      
    // XML document is completely parsed, dispatch an event for app to handle data as needed
   
    [[NSNotificationCenter defaultCenter] postNotificationName:@"mapDataParseComplete" object:nil];
    [mapsDataParser release]; mapsDataParser = nil;
}

- (void)dealloc
{
    [super dealloc];
   
    mapsDataParser.delegate = nil;
    [mapsDataParser release]; mapsDataParser = nil;
    [currentMapLocation release]; currentMapLocation = nil;
    [locationData release]; locationData = nil;
}

@end

Here’s the basic rundown of how the implementation works:

  1. Construct the Google Maps URL from passed data
  2. Create the NSXML
  3. Set it’s delegate to the MapDataParser class
  4. Load the XML from constructed string
  5. Step through the loaded data saving what we need in MapLocationVO objects.
  6. Dispatch event notifying app that the data is complete and ready for use

You can see that we always create a new MapLocationVO in the didStartElement delegate method if the element name is “Placemark”. You can see in the XML that “Placemark” is the wrapping element for a business listing. There is also some additional data in the XML we don’t care about, so we set the isPlacemark boolean to true.

If it is not a “Placemark” element, we store the value to check against in the foundCharacters delegate method. In this method, we are checking for the “title”, “Snippet”, and “coordinates” elements. When we encounter these elements we set them accordingly on the current MapLocationVO.

In the didEndElement delegate method, we set isPlacemark to false becuase have exited the wrapping “Placemark” element. We also add the MapLocationVO to our data array and do some clean up.

After we have gathered all our map data the parser reaches the end of the document and fires the parserDidEndDocument method. Here dispatch the “mapDataParseComplete” event.

You can now use the collected data however you need. In part 2 of this series we’ll take a look at how to use CoreLocation to find the user’s location and get the data based on the coordinates. Then, in part 3 we’ll tie it all together by displaying the business listings in a map view with custom markers.

SOURCE FILES

http://www.zen-sign.com/finding-business-listings-and-displaying-with-mapkit-part-1/

posted @ 2010-10-22 14:57  周宏伟  阅读(340)  评论(0编辑  收藏  举报