Would you like to browse Groupon, Citysearch, Restaurants.com, and individual store web sites, or look at them all on a Google Map? The Dealmap, a mashup of web-based coupons, is betting you’d like the latter. More »
Google – Searching – Search Engines – Google Map – Groupon
Blog Archives
The Dealmap Finds Web-Based Deals Offered Near You [Coupons]
Boxoh Maps and Tracks Your Packages [Maps]
The web doesn’t lack for package tracking apps, and some of them are very clever with their data. Boxoh, on the other hand, is simple, and shows you, on a map, what you usually care about most: where your package is. More »
Google City Tours Adds Walking Directions, Custom Maps [Travel]
Six months after launching City Tours in Labs, Google’s Maps team has tweaked the interface and made it more friendly to how people actually vacation: head to a city, pick places to go, and get precise directions to them.
At launch, City Tours did a decent job of knowing neat places to go inside a city, and even knew (sometimes) when they were open and what they cost. All it did for the traveler, though, was tell you how far apart those destinations were. Now City Tours includes detailed walking directions in your itinerary. And if you’re a My Maps nerd who’s picked out spots to visit on your own, you can import it and lay those map points over the cities that Google has picked out, so you get a mix of suggestions and pre-picked favorites.
There’s more to the latest upgrade, detailed at the blog post below. Have you used City Tours for a real, honest-to-goodness vacation? Tell us what works, and what you needed to DIY, in the comments.
Introduction to MapKit in iPhone OS 3.0 Part 2
Introduction
Back in September I posted a large post going over all the components required to implement the MapKit in your application. The MapKit is a framework introduced in iPhone OS 3.0 and allows developers to easily take advantage of Google’s mapping technology. In my first post I go over how to present a map as well as annotate the map with custom badges to highlight points of interest. The MapKit also gives developers access to reverse geocoding services from Google which I will cover in this post.
Reverse Geocoding
A users location is defined by coordinates. When using the Core Location services, or specifying where a map should center its view, coordinates will be the units developers will be working with. This is all well and good for presenting mapping information visually but there is a whole other set of information that can be derived from a set of coordinates.
- Country
- State
- Zip Code
- Address
Google provides the service to translate any coordinate set to an MKPlacemark object. An MKPlacemark object contains properties to access all this information. Let’s look into the process of getting this object.
Step 1
The first thing you need to do is make a “View Based Application”. Now we need to is pick a set of coordinates we want to use. I have decided to use the address of Arizona State University.
Longitude: -111.936619;
Latitude: 33.416199;
Make your AppDelegates .m file look like this:
- (void)applicationDidFinishLaunching:(UIApplication *)application { // Override point for customization after app launch [window addSubview:viewController.view]; [window makeKeyAndVisible]; CLLocationCoordinate2D coord; coord.longitude = -111.936619; coord.latitude = 33.416199; MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:coord]; [geocoder setDelegate:self]; [geocoder start] }
Step 2
Now we need to implement the MKReverseGeocoderDelegate methods. There are only 2 delegate methods:
Called when an error occurs when fetching the reverse geocoder object
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error
Called when the ReverseGeocode object is successfully returned.
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
Step 3
Now we have a MKPlacemark object to use. An MKPlacemark object conforms to the MKAnnotation protocol. So these objects could be added as annotations to an MKMapView. For more information on MKAnnotations reference part 1. The MKPlacemark object contains the following properties that you can access to use in your application.
To see all of these values make the success delegate method look like this:
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark { NSLog(@"The geocoder has returned: %@", [placemark addressDictionary]); }
You should see this output:
2009-12-22 11:23:05.492 ReverseGeocoder[2591:207] The geocoder has returned: { City = Tempe; Country = "United States"; CountryCode = US; FormattedAddressLines = ( "200-206 E Lemon St", "Tempe, AZ 85281", USA ); State = Arizona; Street = "200-206 E Lemon St"; SubAdministrativeArea = Maricopa; SubThoroughfare = "200-206"; Thoroughfare = "E Lemon St"; ZIP = 85281; }
Introduction to MapKit in iPhone OS 3.0
Introduction
Hello everyone. Welcome to another screencast. Today we will be looking into the MapKit, a new API’s made available by Apple in the iPhone OS 3.0 release. The MapKit allows simple access to the map seen in the maps application. Using GoogleMaps as its engine the map kit allows for a developer to make their own custom map interface to fit their own application. Today we will be reviewing the MapView as well as the Map Annotations that can be used to highlight points of interest in a map. We will create our own custom map app, along with custom annotations. Let’s dive in.
Skill Level Intermediate
This tutorial is one for people familiar with general Objective C development and introductory experience to the iPhone SDK. Knowledge of general Interface Builder usage and DataSource and Delegate methods are required.
Screencast
I film myself coding out the entire sample project for each post. I personally think going through the Screencast is the best way to learn. But feel free to look through the slides and text if that suites you better.
Introduction to Map Kit on iPhone OS 3.0 from Collin Ruffenach on Vimeo.
Source
iCodeBlogMap Source
Tutorial


iCodeBlogMapViewController.h
#import "iCodeBlogAnnotation.h" #import "iCodeBlogAnnotationView.h" @interface iCodeMapViewController : UIViewController { IBOutlet UITableView *tableview; IBOutlet MKMapView *mapView; IBOutlet UIImageView *shadowImage; } @property (nonatomic, retain) IBOutlet UITableView *tableview; @property (nonatomic, retain) IBOutlet MKMapView *mapView; @property (nonatomic, retain) IBOutlet UIImageView *shadowImage; -(void)loadOurAnnotations; @end
iCodeBlogAnnoation.h
typedef enum {
iCodeBlogAnnotationTypeApple = 0, iCodeBlogAnnotationTypeEDU = 1, iCodeBlogAnnotationTypeTaco = 2 } iCodeMapAnnotationType; @interface iCodeBlogAnnotation : NSObject { CLLocationCoordinate2D coordinate; NSString *title; NSString *subtitle; iCodeMapAnnotationType annotationType; } @property (nonatomic) CLLocationCoordinate2D coordinate; @property (nonatomic, retain) NSString *title; @property (nonatomic, retain) NSString *subtitle; @property (nonatomic) iCodeMapAnnotationType annotationType; @end
iCodeBlogAnnoation.m
@implementation iCodeBlogAnnotation @synthesize coordinate; @synthesize title; @synthesize subtitle; @synthesize annotationType; -init { return self; } -initWithCoordinate:(CLLocationCoordinate2D)inCoord { coordinate = inCoord; return self; } @end
iCodeBlogAnnoationView.h
@interface iCodeBlogAnnotationView : MKAnnotationView { UIImageView *imageView; } @property (nonatomic, retain) UIImageView *imageView; @end
Images to Use



iCodeBlogAnnoationView.m
#import "iCodeBlogAnnotationView.h" @implementation iCodeBlogAnnotationView @synthesize imageView; #define kHeight 40 #define kWidth 37 #define kBorder 2 - (id)initWithAnnotation:(id )annotation reuseIdentifier:(NSString *)reuseIdentifier { iCodeBlogAnnotation* myAnnotation = (iCodeBlogAnnotation*)annotation; if([myAnnotation annotationType] == iCodeBlogAnnotationTypeApple) { self = [super initWithAnnotation:myAnnotation reuseIdentifier:reuseIdentifier]; self.frame = CGRectMake(0, 0, kWidth, kHeight); self.backgroundColor = [UIColor clearColor]; imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"AppleMarker.png"]]; imageView.frame = CGRectMake(kBorder, kBorder, kWidth - 2 * kBorder, kWidth - 2 * kBorder); [self addSubview:imageView]; } else if([myAnnotation annotationType] == iCodeBlogAnnotationTypeEDU) { self = [super initWithAnnotation:myAnnotation reuseIdentifier:reuseIdentifier]; self.frame = CGRectMake(0, 0, kWidth, kHeight); self.backgroundColor = [UIColor clearColor]; imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SchoolMarker.png"]]; imageView.frame = CGRectMake(kBorder, kBorder, kWidth - 2 * kBorder, kWidth - 2 * kBorder); [self addSubview:imageView]; } else if([myAnnotation annotationType] == iCodeBlogAnnotationTypeTaco) { self = [super initWithAnnotation:myAnnotation reuseIdentifier:reuseIdentifier]; self.frame = CGRectMake(0, 0, kWidth, kHeight); self.backgroundColor = [UIColor clearColor]; imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"TacosMarker.png"]]; imageView.frame = CGRectMake(kBorder, kBorder, kWidth - 2 * kBorder, kWidth - 2 * kBorder); [self addSubview:imageView]; } [imageView setContentMode:UIViewContentModeScaleAspectFill]; return self; } @end
iCodeBlogMapViewController.m
-(void)loadOurAnnotations { CLLocationCoordinate2D workingCoordinate; workingCoordinate.latitude = 40.763856; workingCoordinate.longitude = -73.973034; iCodeBlogAnnotation *appleStore1 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [appleStore1 setTitle:@"Apple Store 5th Ave."]; [appleStore1 setSubtitle:@"Apple's Flagship Store"]; [appleStore1 setAnnotationType:iCodeBlogAnnotationTypeApple]; [mapView addAnnotation:appleStore1]; workingCoordinate.latitude = 51.514298; workingCoordinate.longitude = -0.141949; iCodeBlogAnnotation *appleStore2 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [appleStore2 setTitle:@"Apple Store St. Regent"]; [appleStore2 setSubtitle:@"London England"]; [appleStore2 setAnnotationType:iCodeBlogAnnotationTypeApple]; [mapView addAnnotation:appleStore2]; workingCoordinate.latitude = 35.672284; workingCoordinate.longitude = 139.765702; iCodeBlogAnnotation *appleStore3 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [appleStore3 setTitle:@"Apple Store Giza"]; [appleStore3 setSubtitle:@"Tokyo, Japan"]; [appleStore3 setAnnotationType:iCodeBlogAnnotationTypeApple]; [mapView addAnnotation:appleStore3]; workingCoordinate.latitude = 37.331741; workingCoordinate.longitude = -122.030564; iCodeBlogAnnotation *appleStore4 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [appleStore4 setTitle:@"Apple Headquarters"]; [appleStore4 setSubtitle:@"The Mothership"]; [appleStore4 setAnnotationType:iCodeBlogAnnotationTypeApple]; [mapView addAnnotation:appleStore4]; workingCoordinate.latitude = 41.894518; workingCoordinate.longitude = -87.624005; iCodeBlogAnnotation *appleStore5 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [appleStore5 setTitle:@"Apple Store Michigan Ave."]; [appleStore5 setSubtitle:@"Chicago, IL"]; [appleStore5 setAnnotationType:iCodeBlogAnnotationTypeApple]; [mapView addAnnotation:appleStore5]; workingCoordinate.latitude = 32.264977; workingCoordinate.longitude = -110.944011; iCodeBlogAnnotation *tacoShop1 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [tacoShop1 setTitle:@"Nico's Taco Shop"]; [tacoShop1 setSubtitle:@"Tucson, AZ"]; [tacoShop1 setAnnotationType:iCodeBlogAnnotationTypeTaco]; [mapView addAnnotation:tacoShop1]; workingCoordinate.latitude = 32.743242; workingCoordinate.longitude = -117.181451; iCodeBlogAnnotation *tacoShop2 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [tacoShop2 setTitle:@"Lucha Libre Gourmet"]; [tacoShop2 setSubtitle:@"San Diego, CA"]; [tacoShop2 setAnnotationType:iCodeBlogAnnotationTypeTaco]; [mapView addAnnotation:tacoShop2]; workingCoordinate.latitude = 32.594987; workingCoordinate.longitude = -117.060936; iCodeBlogAnnotation *tacoShop3 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [tacoShop3 setTitle:@"El Ranchero Taco Shop"]; [tacoShop3 setSubtitle:@"Rocky Pointe, Mexico"]; [tacoShop3 setAnnotationType:iCodeBlogAnnotationTypeTaco]; [mapView addAnnotation:tacoShop3]; workingCoordinate.latitude = -34.594859; workingCoordinate.longitude = -58.384336; iCodeBlogAnnotation *tacoShop4 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [tacoShop4 setTitle:@"Taco Tequila Sangria S.A."]; [tacoShop4 setSubtitle:@"Buneos Aires, Argentina"]; [tacoShop4 setAnnotationType:iCodeBlogAnnotationTypeTaco]; [mapView addAnnotation:tacoShop4]; workingCoordinate.latitude = 38.240550; workingCoordinate.longitude = -0.526509; iCodeBlogAnnotation *tacoShop5 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [tacoShop5 setTitle:@"Albertsma Taco"]; [tacoShop5 setSubtitle:@"Gran Alacant, Spain"]; [tacoShop5 setAnnotationType:iCodeBlogAnnotationTypeTaco]; [mapView addAnnotation:tacoShop5]; workingCoordinate.latitude = 33.419490; workingCoordinate.longitude = -111.930563; iCodeBlogAnnotation *school1 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [school1 setTitle:@"Arizona State University"]; [school1 setSubtitle:@"Go Sun Devils"]; [school1 setAnnotationType:iCodeBlogAnnotationTypeEDU]; [mapView addAnnotation:school1]; workingCoordinate.latitude = 35.087537; workingCoordinate.longitude = -106.618184; iCodeBlogAnnotation *school2 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [school2 setTitle:@"University of New Mexico"]; [school2 setSubtitle:@"Go Lobos"]; [school2 setAnnotationType:iCodeBlogAnnotationTypeEDU]; [mapView addAnnotation:school2]; workingCoordinate.latitude = 40.730838; workingCoordinate.longitude = -73.997498; iCodeBlogAnnotation *school3 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [school3 setTitle:@"New York University"]; [school3 setSubtitle:@"New York, NY"]; [school3 setAnnotationType:iCodeBlogAnnotationTypeEDU]; [mapView addAnnotation:school3]; workingCoordinate.latitude = 51.753523; workingCoordinate.longitude = -1.253171; iCodeBlogAnnotation *school4 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [school4 setTitle:@"Oxford University"]; [school4 setSubtitle:@"Oxford, England"]; [school4 setAnnotationType:iCodeBlogAnnotationTypeEDU]; [mapView addAnnotation:school4]; workingCoordinate.latitude = 22.131982; workingCoordinate.longitude = 82.142302; iCodeBlogAnnotation *school5 = [[iCodeBlogAnnotation alloc] initWithCoordinate:workingCoordinate]; [school5 setTitle:@"India Institute of Technology"]; [school5 setSubtitle:@"Delhi, India"]; [school5 setAnnotationType:iCodeBlogAnnotationTypeEDU]; [mapView addAnnotation:school5]; }
iCodeblogMapViewController.m
- (iCodeBlogAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation { iCodeBlogAnnotationView *annotationView = nil; // determine the type of annotation, and produce the correct type of annotation view for it. iCodeBlogAnnotation* myAnnotation = (iCodeBlogAnnotation *)annotation; if(myAnnotation.annotationType == iCodeBlogAnnotationTypeApple) { NSString* identifier = @"Apple"; iCodeBlogAnnotationView *newAnnotationView = (iCodeBlogAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; if(nil == newAnnotationView) { newAnnotationView = [[[iCodeBlogAnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease]; } annotationView = newAnnotationView; } else if(myAnnotation.annotationType == iCodeBlogAnnotationTypeEDU) { NSString* identifier = @"School"; iCodeBlogAnnotationView *newAnnotationView = (iCodeBlogAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; if(nil == newAnnotationView) { newAnnotationView = [[[iCodeBlogAnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease]; } annotationView = newAnnotationView; } else if(myAnnotation.annotationType == iCodeBlogAnnotationTypeTaco) { NSString* identifier = @"Taco"; iCodeBlogAnnotationView *newAnnotationView = (iCodeBlogAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; if(nil == newAnnotationView) { newAnnotationView = [[[iCodeBlogAnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease]; } annotationView = newAnnotationView; } [annotationView setEnabled:YES]; [annotationView setCanShowCallout:YES]; return annotationView; }
iCodeBlogMapViewController.m
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 3; }
iCodeBlogMapViewController.m
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{ if(section == iCodeBlogAnnotationTypeApple) { return @"Apple Markers"; } else if(section == iCodeBlogAnnotationTypeEDU) { return @"Schools"; } else if(section == iCodeBlogAnnotationTypeTaco) { return @"Taco Shops"; } return nil; }
iCodeBlogMapViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 5; }
iCodeBlogMapViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } NSMutableArray *annotations = [[NSMutableArray alloc] init]; if(indexPath.section == 0) { for(iCodeBlogAnnotation *annotation in [mapView annotations]) { if([annotation annotationType] == iCodeBlogAnnotationTypeApple) { [annotations addObject:annotation]; } } cell.textLabel.text = [[annotations objectAtIndex:indexPath.row] title]; } else if(indexPath.section == 1) { for(iCodeBlogAnnotation *annotation in [mapView annotations]) { if([annotation annotationType] == iCodeBlogAnnotationTypeEDU) { [annotations addObject:annotation]; } } cell.textLabel.text = [[annotations objectAtIndex:indexPath.row] title]; } else if(indexPath.section == 2) { for(iCodeBlogAnnotation *annotation in [mapView annotations]) { if([annotation annotationType] == iCodeBlogAnnotationTypeTaco) { [annotations addObject:annotation]; } } cell.textLabel.text = [[annotations objectAtIndex:indexPath.row] title]; } return cell; }
iCodeBlogMapViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { for(iCodeBlogAnnotation *annotation in [mapView annotations]) { if([[[[tableView cellForRowAtIndexPath:indexPath] textLabel] text] isEqualToString:[annotation title]]) { [mapView setRegion:MKCoordinateRegionMake([annotation coordinate], MKCoordinateSpanMake(.01, .01)) animated:YES]; } } }
Gsalr Finds Garage Sales and Plans an Effective Route [Sales]
If you’re looking to hit some garage sales, forget combing over the local paper and trying to put together a route. Gsalr makes finding and mapping garage sales a breeze.
Similar to previous mentioned Yard Sale Treasure Map, although a bit more polished, Gsalr helps you find and map garage sales in your area. Plug a zip code or state and city into Gsalr and you’ll be given a Google Maps mashup with local garage sales flagged. Each red flag represents a garage sale listing, clicking on it gives you a summary of the Craiglist listing, a link to the full post if it’s lengthy, the days the sale is going on, and address of the location.
The “Add to Trip Planner” button lets you easily toss a sale you like into the route maker. When you’re all done browsing the listings click on the Trip Planner tab in the upper right corner and get a handy turn by turn driving route to help you hit all the garage sales in the most effective way. If you know of another tool for helping you discover goods to repurpose and deals to score, let’s hear about it in the comments.
Nextstop Offers Travel, Food, and Local Activity Guides [Travel]
Plenty of web sites offer business reviews, restaurant recommendations, and other local finds for those less in the know. Today marks the launch of still one more, called Nextstop, which lets users create their own local or global guides—think best beer bars in the world, for example.
Founded by several former Google employees, Nextstop lets users read and build various guides covering anything under the sun, from the the best beer bars in the world (mentioned above) and best beer bars in Massachusetts to hidden spots in San Francisco and Go! Play! Portland! (a fun tour of Portland).
You can search for guides on Nextstop by places, guides, and map, or just focus your search on a specific city. The site was in closed beta until today, which helps explain why a good number of my NYC recommendations included more obvious tourist spots (think Statute of Liberty) and less off-the-beaten-path selections. Of course, if and when more users create their own guides, the site could potentially be a decent resource for interesting things to do in a given city, especially centered around a specific theme.
Create and Share Panoramic Images at viewAt [Images]
If you’re interested in panoramic photography, viewAt combines a panoramic maker with a Google Maps mashup so you can not only create interactive panoramas but geotag them and share them with the world.
Even if you’re not interested in making panoramic photos, just browsing the viewAt map is a visual treat. The vast majority of user uploaded content is absolutely stunning. The photos you upload can be converted into a cylindrical or spherical panoramic. Cylindrical panoramics are the ones you’re most likely to have already come across, where viewing the photo is like rotating around in a circle viewing a band of the scene before you.
Spherical panoramics require more photos to be taken, but they create almost a 360 view of scene allowing you to pan up and down as well as left and right to take in everything at the site of the photograph. For more information and a chance to check out some of the spectacular panoramas already hosted by viewAt, check out the link below.
Save and Share Google Maps Directions with My Maps [UltraNewb]
The My Maps feature of Google Maps has been around for quite sometime, but if you are a regular Google Map user and you’re not using it to share common directions, it’s worth it.
The Google LatLong blog (Google's official blog for all things Google Earth and Maps) has a simple beginner's tutorial for using the My Maps feature to customize and save directions—complete with notes—so you've got a repository of common directions with helpful annotation.
As I said, this isn’t a new feature by any means, but it’s something that I’ve used several times here at Lifehacker (see the embedded map) and to share directions with friends and family. If you’ve used My Maps, let’s hear what you’re using it for in the comments.










































