Tip: How to Attach an ArcSDE Personal Database File After Moving It

by James Richards April 13, 2011

I recently needed to move an ArcSDE Personal database file from my local hard drive to another location. The directory where the MDF and LDF files are located is different on the target computer. This caused problems when attempting to attach the database in ArcCatalog. I received the following error message:

Problem selecting this Geodatabase file
File activation failure. The physical file name
"C:\GIS Data\MyDatabase.LDF" may be incorrect.

It seems that ArcCatalog does not figure out that log file has moved along side the MDF file. To solve the problem, I attached the database using SQL Server Management Studio, which automatically fixes the path to the LDF file.

But this created a second problem. Now that the database is attached to the local SQLEXPRESS instance with Management Studio, attempting to attach it in ArcCatalog causes this error:

Problem selecting this Geodatabase file
CREATE FILE encountered operating system error 32(The process cannot
access the file because it is already being used by another process.) while
attempting to open or create the physical file 'D:\GIS
Data\MyDatabase.mdf'.

This was easily solved by detaching the database in SQL Server Management Studio and then attaching it in ArcCatalog.

Summary

To move an ArcSDE Personal database to a new location with a different directory path, first attach the MDF file to SQLEXPRESS using SQL Server Management Studio. This will fix the broken path to the log file. Then detatch the database and attach it in ArcCatalog.

Hope this helps!

Tags: , ,

ArcGIS Server | ArcSDE | How To

QuakeFeed in App Store, Webinar, White Paper

by James Richards December 01, 2010

QuakeFeed Now Available in the App Store

QuakeFeed is now available in the App Store! This is a free app that I've created using the Esri ArcGIS API for iOS.

Download Now: QuakeFeed in the iTunes Store

The app displays all earthquakes from the past 7 days with a magnitude > 2.5 on a map or in list. Six beautiful base maps are provided by Esri. A variety of filter and sort options are available. The app is location aware so you can find quakes that are closest to you. It has an intuitive user interface and features Twitter, Facebook, and email integration.

QuakeFeed Screenshot

 

Esri ArcGIS for iOS Webinar Archive

Earlier this month I participated in a Directions Media Webinar in which I shared some tips & tricks and lessons learned while developing QuakeFeed and working on another ArcGIS API for iOS integration project for our client CitySourced. The archive of this webinar is now available on the Directions site.

View now: ArcGIS for the iPhone and iPad (Registration Required). 

Note: My portion of the Webinar starts at 22:45.

 

Esri ArcGIS For iOS White Paper

For this event, myself and Kurt Daradics of CitySourced also collaborated on a technical white paper.

The paper begins with an overview of the CitySourced app and the reasons why they migrated to the ArcGIS API for iOS.

A technical discussion follows, with code samples that show:

  • Base map options available through the API.
  • Waiting for the map to load before drawing graphics.
  • How to perform WGS84 to Web Mercator transformation on the device.
  • Tips for drawing pins on the map for improved usability.

Dowload now: CitySourced ArcGIS API for iOS Implementation

For those if you interested in working with the ArcGIS API for iOS, I'll be expanding on these topics and more in the coming weeks.

Tags: , , , , ,

ESRI | iOS | iPhone

Esri ArcGIS for iOS Webinar

by James Richards November 12, 2010

I've been "crazy busy" the past few months working on some iPhone projects. I'll share some of the things I've learned about working with the Esri ArcGIS API for iOS in the upcoming Access ArcGIS on the iPhone and iPad webinar hosted by Directions Media.

I'll be joining Dave Cardella of Esri (@dcardella) and Kurt Daradics of CitySourced (@kurtyd). I will share some real world tips & tricks about working with the API that were learned while integrating Esri maps into CitySourced's iPhone app.

This free webinar kicks off at 2:00 pm EST next Tuesday, November 16th. You can find more info and sign up for the webinar here: https://www2.gotomeeting.com/register/882551307

Hope you'll join us!

Tags: , , ,

ESRI | iPhone | Mobile | iOS

iPhone In App Social Networking Wireframes

by James Richards June 14, 2010

Over the weekend I was working on an iPhone app design for a personal project. The design includes a series of screens for cross promoting the app through social networking by sending an email, posting on Twitter, or sharing on Facebook.

This is a pretty common scenario in iPhone apps, so I decided to pull out the social networking screens and share them in a reusable package. I used Balsamiq Mockups to create the wireframes, so it was easy to share them on Balsamiq’s Mockups to Go site. If your doing an iPhone design in Mockups that requires social networking integration, you can download the mockups from here.

iPhone In App Social Networking

Tags: , , , ,

iPhone | Mobile | Balsamiq Mockups

ESRI ArcGIS iPhone API – Class Breaks Renderer Sample

by James Richards June 04, 2010

Introduction

Here is a quick sample demonstrating the use of a Class Breaks Renderer in the ESRI ArcGIS iPhone API. The sample queries an ArcGIS Server Map Service for cities in California and renders them as graphics based on the population. Here’s a screenshot of the app running in the simulator.

ClassBreaks

Click here to download the source code from this article.

[more]

Discussion

If you’ve been programming with any of the ESRI Client APIs, then you’ll recognize the Class Breaks Renderer concept. For those who aren’t familiar, here’s a snippet from the Working with Symbols and Renderers section of the ArcGIS iPhone SDK Concepts documentation:

A class breaks renderer symbolizes each Graphic based on the value of some numeric attribute. Graphics with similar values for the attribute get the same Symbol. The "breaks" define the values at which the symbology changes.

Code Listings

ClassBreaksViewController.h

#import <UIKit/UIKit.h>
#import "AGSiPhone.h"

#define kTiledMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer"
#define kDynamicMapServiceURL @"http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0"

@interface ClassBreaksViewController : UIViewController<AGSMapViewDelegate, AGSQueryTaskDelegate> {
    AGSMapView *mapView;
    AGSGraphicsLayer *cityGraphicsLayer;
    AGSQueryTask *cityQueryTask;
    
}

@property (nonatomic, retain) IBOutlet AGSMapView *mapView;
@property (nonatomic, retain) AGSGraphicsLayer *cityGraphicsLayer;
@property (nonatomic, retain) AGSQueryTask *cityQueryTask;

@end

ClassBreaksViewController.m

#import "ClassBreaksViewController.h"

@implementation ClassBreaksViewController

@synthesize mapView;
@synthesize cityGraphicsLayer;
@synthesize cityQueryTask;

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Set map view delegate
    self.mapView.mapViewDelegate = self;
    
    // Create tile base map layer
    AGSTiledMapServiceLayer *tiledLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kTiledMapServiceURL]];
    [self.mapView addMapLayer:tiledLayer withName:@"BaseLayer"];
    [tiledLayer release];
    
    // Create grpahics layer
    self.cityGraphicsLayer = [AGSGraphicsLayer graphicsLayer];
    
    // Create symbols for the three class breaks
    AGSSimpleMarkerSymbol *lowSymbol = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
    lowSymbol.color = [UIColor colorWithRed:151.0/255.0 green:216.0/255.0 blue:255.0/255.0 alpha:0.8];
    lowSymbol.outline.width = 0;
    lowSymbol.size = 15;
    
    AGSSimpleMarkerSymbol *mediumSymbol = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
    mediumSymbol.color = [UIColor colorWithRed:255.0/255.0 green:165.0/255.0 blue:83.0/255.0 alpha:0.8];
    mediumSymbol.outline.width = 0;
    mediumSymbol.size = 20;
    
    AGSSimpleMarkerSymbol *highSymbol = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
    highSymbol.color = [UIColor colorWithRed:222.0/255.0 green:0.0 blue:0.0 alpha:0.8];
    highSymbol.outline.width = 0;
    highSymbol.size = 25;
    
    // Create a class breaks renderer with a default simple marker symbol and an attribute field
    AGSClassBreaksRenderer *cityRenderer = [AGSClassBreaksRenderer
                                            classBreaksRendererWithDefaultSymbol:lowSymbol 
                                                               forAttributeField:@"POP1990"];
    
    // Create three AGSClassBreak objects, one each for low, medium and high populations
    AGSClassBreak* lowClassBreak = [AGSClassBreak
                                    classBreakInfoWithSymbol:lowSymbol forMinValue:DBL_MIN
                                    maxValue:50000.0];
    
    AGSClassBreak* mediumClassBreak = [AGSClassBreak
                                       classBreakInfoWithSymbol:mediumSymbol forMinValue:50000.0
                                       maxValue:250000];
    
    AGSClassBreak* highClassBreak = [AGSClassBreak
                                     classBreakInfoWithSymbol:highSymbol forMinValue:250000.0
                                     maxValue:DBL_MAX];
    
    // Create an NSMutableArray, fill it with the class break objects,
    // and set it to the renderer’s classBreaks property
    cityRenderer.classBreaks = [NSMutableArray arrayWithObjects:
                                lowClassBreak, mediumClassBreak, highClassBreak, nil];
    
    // Add the renderer to the graphics layer
    self.cityGraphicsLayer.renderer = cityRenderer;
    
    // Add the graphics layer to the map view
    [self.mapView addMapLayer:self.cityGraphicsLayer withName:@"CityGraphicsLayer"];
}

// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return YES;
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    self.cityGraphicsLayer = nil;
    self.mapView = nil;
    [super viewDidUnload];
}

- (void)dealloc {
    [cityGraphicsLayer release];
    [mapView release];
    [super dealloc];
}

#pragma mark AGSMapViewDelegate

// Called when the map view is loaded (after the view is loaded) 
- (void)mapViewDidLoad:(AGSMapView *)mapView {
        
    // Set up query task for cities and perform query returning all attributes
    self.cityQueryTask = [AGSQueryTask queryTaskWithURL:[NSURL URLWithString:kDynamicMapServiceURL]];
    self.cityQueryTask.delegate = self;
    
    AGSQuery *cityQuery = [AGSQuery query];
    cityQuery.where = @"STATE_NAME = 'California'";
    cityQuery.outFields = [NSArray arrayWithObject:@"*"];
        
    [self.cityQueryTask executeWithQuery:cityQuery];
    
    // Create extent to be used as default
    AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:-118.6
                                                     ymin:33.6
                                                     xmax:-118.1
                                                     ymax:34.2
                                         spatialReference:self.mapView.spatialReference];
    
    // Call method to set extent, pass in envelope
    [self.mapView performSelector:@selector(zoomToEnvelope:animated:) 
                       withObject:envelope
                       afterDelay:0.5];    
}    

#pragma mark AGSQueryTaskDelegate

// When query is executed ....
- (void)queryTask:(AGSQueryTask *)queryTask didExecuteWithFeatureSetResult:(AGSFeatureSet *)featureSet {
    // Iterate the returned features (graphics) and add them to the graphics layer
    for (AGSGraphic *graphic in featureSet.features) {
        [self.cityGraphicsLayer addGraphic:graphic];
    }
    [self.cityGraphicsLayer dataChanged];
    
    // Clean up query task memory
    self.cityQueryTask = nil;
}

// If there's an error with the query task give info to user
- (void)queryTask:(AGSQueryTask *)queryTask didFailWithError:(NSError *)error {
    // Clean up query task memory
    self.cityQueryTask = nil;

    // Display error message
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
                                                    message:[error localizedDescription]
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];
}

@end

Summary

In this post you learned how to use a Class Breaks Renderer in the ESRI ArcGIS iPhone API to display cities of varying population with different symbols.

Click here to download the source code from this article.

I hope you are finding these posts on the ESRI iPhone API to be helpful. If you have any suggestions, ideas or feedback, please leave them in the comments below.

Additional Resources

Tags: , , ,

ArcGIS Server | ESRI | iPhone | Mobile

ESRI ArcGIS iPhone API – Integration with Core Location

by James Richards May 14, 2010

Introduction

Last week I started to familiarize myself with the ESRI ArcGIS iPhone API public beta and I blogged about my First Impressions. This week I had a chance to play around with it some more and I decided to investigate working with location. I enhanced my sample application from last week to include a Location button, which when pressed zooms the map to the current location and displays a custom push pin graphic.

Screen1     Screen2

Click here to download the source code from this article.

There are two ways you can work with location while writing an app with ESRI’s iPhone API:

ESRI’s AGSGPS class provides a convenient wrapper around Core Location if you don’t want (or need) to delve into the details of the framework.

[more]

ESRI AGSGPS Class vs. Apple Core Location Framework

Here’s how the ESRI iPhone API documentation describes the AGSGPS class:

“This object controls how the map responds to GPS input. To make the map start responding to GPS input, call the start method. The map will automatically zoom to the first location specified by the GPS input. You can control the zoom level by specifying the zoomLevel property. If the autoPan property is enabled, the map will recenter everytime a new GPS location is received. To make the map stop responding to GPS input, call the stop method. By default, the map uses a round, blue symbol to display the current location. You can replace this symbol with an image icon of your choice. This image must be included in the application bundle, it must be named GpsDisplay.png and it must be 35x35 pixels in size.”

The class definitely simplifies working with Core Location, but the trade off is that you are somewhat limited in what you can do. For example, Core Location includes the CLLocationManagerDelegate protocol which is “used to receive location and heading updates” so you can respond to them in your code. I’d like to see something similar in the ESRI iPhone SDK to give us more flexibility to respond to location updates. With the ESRI iPhone SDK, you just turn on the GPS and let the map do it’s thing.

For my sample app, I decided to work with the Core Location framework for finer grained control over the user interaction.

Implementation Details

The first step I took was to rearrange the UI in Interface Builder to accommodate the new button. I decided to add a proper toolbar along the bottom and center the new button and existing segment control. This required adding Flexible Bar Button Items on each side of the toolbar, as shown:

Finding the proper icon for the Location button was a bit tricky. Interface Builder doesn’t give you a way to specify this particular system icon for your button. I had to follow Diallo’s advice from this Stack Overflow question to get the icon from the UI Kit and save it to disk. I temporarily added this code to the viewDidLoad event, and ran the application once to write the icon out to my document directory.

UIImage* img = [UIImage kitImageNamed:@"UIButtonBarLocate.png"];
// Get the location of the Documents directory NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) ; NSString *imagePath = [paths objectAtIndex:0] ; NSString *filename = @"Locate.png" ; NSString *filepath = [NSString stringWithFormat:@"%@/%@", imagePath, filename] ; // Save the image NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(img)]; [imageData writeToFile:filepath atomically:YES];

If your not sure where the file gets saved, you can add an NSLog call to output imagePath to the Console. After the icon was saved, I removed the temporary code and copied the icon into the project directory, under the Resources group.

Once I had the view setup properly in interface builder, I modified the view controller’s header file to:

  • Import Core Location
  • Conform to the CLLocationManagerDelegate protocol
  • Declare members and properties for the location manager and locate button
  • Declare an IBAction method to run when the user clicks the locate button

I also cleaned up the previous week’s code a bit, to remove unnecessary AGSTiledMapServiceLayer members and properties.

Here is the new version of MyFirstMapAppViewController.h:

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "AGSiPhone.h"

#define kTiledStreetMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"
#define kTiledImageryMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer"
#define kTiledReliefMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_ShadedRelief_World_2D/MapServer"

@interface MyFirstMapAppViewController : UIViewController<AGSMapViewDelegate, CLLocationManagerDelegate> {
    AGSMapView *mapView;
    CLLocationManager *locationManager;
    
    UIView *streetView;    
    UIView *imageryView;    
    UIView *reliefView;
    
    UIBarButtonItem *locateButton;
}

@property (nonatomic, retain) IBOutlet AGSMapView *mapView;
@property (nonatomic, retain) CLLocationManager *locationManager;

@property (nonatomic, retain) UIView *streetView;
@property (nonatomic, retain) UIView *imageryView;
@property (nonatomic, retain) UIView *reliefView;

@property (nonatomic, retain) IBOutlet UIBarButtonItem *locateButton;

- (IBAction)toggleLayer:(id)sender;
- (IBAction)showLocation:(id)sender;

@end

For the implementation file, Core Location is used in the following manner:

  • The location manager is created and started in viewWillAppear
  • The locate button is disabled in viewWillAppear to ensure that the button can’t be used until Core Location services have become available
  • A new graphics layer is created and added to the map in viewDidLoad
  • The location button is enabled in the location manager’s didUpdateToLocation event once core location is started and ready to use
  • When the user touches the Locate button, the showLocation method gets the location, creates and zooms to an envelope around that location, and displays a push pin graphic in the graphics layer
  • The location manager is shut down in viewWillDisappear

This code follows the same basic pattern shown in one of the examples in Head First iPhone Development.

Here is the MyFirstMapAppViewController.m file:

#import "MyFirstMapAppViewController.h"

@implementation MyFirstMapAppViewController

@synthesize mapView;
@synthesize streetView;
@synthesize imageryView;
@synthesize reliefView;
@synthesize locationManager;
@synthesize locateButton;

- (void)viewWillAppear:(BOOL)animated {
    // Setup location manager
    NSLog(@"Starting core location");
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
    self.locationManager.delegate = self;
    [self.locationManager startUpdatingLocation];
    self.locateButton.enabled = NO;
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.mapView.mapViewDelegate = self;
    
    AGSTiledMapServiceLayer *streetLayer = [[AGSTiledMapServiceLayer alloc] 
                                            initWithURL:[NSURL URLWithString:kTiledStreetMapServiceURL]];
    self.streetView = [self.mapView addMapLayer:streetLayer withName:@"Street"];
    [streetLayer release];
    
    AGSTiledMapServiceLayer *imageryLayer = [[AGSTiledMapServiceLayer alloc] 
                                             initWithURL:[NSURL URLWithString:kTiledImageryMapServiceURL]];
    self.imageryView = [self.mapView addMapLayer:imageryLayer withName:@"Imagery"];
    [imageryLayer release];
    
    AGSTiledMapServiceLayer *reliefLayer = [[AGSTiledMapServiceLayer alloc] 
                                            initWithURL:[NSURL URLWithString:kTiledReliefMapServiceURL]];
    self.reliefView = [self.mapView addMapLayer:reliefLayer withName:@"Relief"];
    [reliefLayer release];

    self.streetView.hidden = NO;
    self.imageryView.hidden = YES;
    self.reliefView.hidden = YES;

    // Create a graphics layer to display the push pin
    AGSGraphicsLayer *graphicsLayer = [AGSGraphicsLayer graphicsLayer];
    [self.mapView addMapLayer:graphicsLayer withName:@"GraphicsLayer"];
}

// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return YES;
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    NSLog(@"Shutting down core location");
    [self.locationManager stopUpdatingLocation];
    self.locationManager = nil;
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)dealloc {
    self.mapView = nil;
    self.streetView = nil;
    self.imageryView = nil;
    self.reliefView = nil;
    
    [locateButton release];
    [super dealloc];
}

- (IBAction)toggleLayer:(id)sender {
    
    self.streetView.hidden = (((UISegmentedControl *)sender).selectedSegmentIndex != 0);
    self.imageryView.hidden = (((UISegmentedControl *)sender).selectedSegmentIndex != 1);
    self.reliefView.hidden = (((UISegmentedControl *)sender).selectedSegmentIndex != 2);    
}

- (IBAction)showLocation:(id)sender {
    NSLog(@"Show Location");
    
    CLLocation *location = self.locationManager.location;
    double lat = location.coordinate.latitude;
    double lon = location.coordinate.longitude;
    NSLog(@"%.3f, %.3f", lat, lon);
    
    double size = 0.05;
    AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:lon - size 
                                                     ymin:lat - size
                                                     xmax:lon + size 
                                                     ymax:lat + size
                                         spatialReference:self.mapView.spatialReference];
    [self.mapView zoomToEnvelope:envelope animated:YES];
    
    // Get reference to the graphics layer
    id<AGSLayerView> graphicsLayerView = [self.mapView.mapLayerViews objectForKey:@"GraphicsLayer"];
    AGSGraphicsLayer *graphicsLayer = (AGSGraphicsLayer*)graphicsLayerView.agsLayer;
    
    // Clear graphics
    [graphicsLayer removeAllGraphics];
    
    // Create a marker symbol using the Location.png graphic
    AGSPictureMarkerSymbol *markerSymbol = [AGSPictureMarkerSymbol pictureMarkerSymbolWithImageNamed:@"Location.png"];
    
    // Create a new graphic using the location and marker symbol
    AGSGraphic* graphic = [AGSGraphic graphicWithGeometry:[envelope center]
                                                   symbol:markerSymbol
                                               attributes:nil
                                             infoTemplate:nil];
    
    // Add the graphic to the graphics layer
    [graphicsLayer addGraphic:graphic];
}

- (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    NSLog(@"Core location has a new position");
    self.locateButton.enabled = YES;
}

- (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    NSLog(@"Core location failed to get position");
    self.locateButton.enabled = NO;
}

#pragma mark AGSMapViewDelegate

//called when the map view is loaded (after the view is loaded) 
- (void)mapViewDidLoad:(AGSMapView *)mapView {
    
    //create extent to be used as default
    AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:-124.83145667
                                                     ymin:30.49849464
                                                     xmax:-113.91375495
                                                     ymax:44.69150688
                                         spatialReference:mapView.spatialReference];
    
    //call method to set extent, pass in envelope
    [self.mapView performSelector:@selector(zoomToEnvelope:animated:) 
                       withObject:envelope
                       afterDelay:0.5];
}    

@end

Summary

In this post we looked at some options for utilizing Core Location with the ESRI ArcGIS iPhone API. ESRI’s AGSGPS class provides an easy to use convenience wrapper around Core Location, but it is not as flexible as working directly with Apple’s Core Location framework. An example was presented that demonstrates how to work with the Core Location framework and ESRI’s iPhone API. The example showed how to get the current location and zoom to it while showing a custom graphic on the map.

Click here to download the source code from this article.

I’m having a lot of fun learning about iPhone programming and ESRI’s iPhone API. I addition to testing in the simulator, I signed up for the Apple Developer Program so I can now run these apps on my iPod Touch. (The Mac Mini in the background is my dev box!)

iPodTouch

I hope you are enjoying these posts and finding the information helpful. If you have any suggestions, ideas or feedback, please leave them in the comments below.

Additional Resources

Tags: , , , ,

ArcGIS Server | ESRI | iPhone | Mobile

ESRI ArcGIS iPhone API – First Impressions

by James Richards May 04, 2010

The ESRI ArcGIS iPhone API was released to public beta today. Jeff Shaner blogged and tweeted about it this morning.

I downloaded the SDK this afternoon and took it for a spin. My first impressions are very favorable. Although the documentation is still a bit sparse in a few places, that’s to be expected for a first beta.

The SDK is another client API for consuming ArcGIS Server REST endpoints, and it works with versions 9.3.1 and 10.0. If you have worked with any of the other client APIs, you will already be familiar with the basic paradigm of interaction with the various REST services offered by ArcGIS Server.

The SDK Concepts Documentation provides a basic overview of the technology and includes a number of brief walkthroughs as well as short code samples illustrating how to perform common programming tasks with the API.

The installation also includes six sample applications which are installed into your ~/Library/SDKs/Samples folder. Studying these sample apps is a good way to jumpstart your familiarity with the API.

But enough talk already, let’s see some code! [more]

After following the My First iPhone Application tutorial,  I decided to jump in and modify the application to improve it in a some specific ways:

  • Add a button bar to toggle three base map types: Streets, Aerial Imagery, and Shaded Relief Map
  • Zoom to a predefined initial extent on application startup
  • Support any device rotation

I won’t spend a lot of time describing everything here. I’ll just show a few screen snapshots with the source code and make everything available for download.

Here are some screen snapshots of the app running in the simulator:

StreetView     ImageryView     ReliefView

Rotated

Here is the MyFirstMapAppViewController.h header file:

#import <UIKit/UIKit.h>
#import "AGSiPhone.h"

#define kTiledStreetMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"
#define kTiledImageryMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer"
#define kTiledReliefMapServiceURL @"http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_ShadedRelief_World_2D/MapServer"

@interface MyFirstMapAppViewController : UIViewController<AGSMapViewDelegate> {
    AGSMapView *_mapView;
    
    UIView *_streetView;
    AGSTiledMapServiceLayer *_streetLayer;
    
    UIView *_imageryView;
    AGSTiledMapServiceLayer *_imageryLayer;

    UIView *_reliefView;
    AGSTiledMapServiceLayer *_reliefLayer;
    
}

@property (nonatomic, retain) IBOutlet AGSMapView *mapView;

@property (nonatomic, retain) UIView *streetView;
@property (nonatomic, retain) AGSTiledMapServiceLayer *streetLayer;

@property (nonatomic, retain) UIView *imageryView;
@property (nonatomic, retain) AGSTiledMapServiceLayer *imageryLayer;

@property (nonatomic, retain) UIView *reliefView;
@property (nonatomic, retain) AGSTiledMapServiceLayer *reliefLayer;

- (IBAction)toggleLayer:(id)sender;

@end

(Sorry, this is my first post about iPhone programming and I haven’t figured out how to highlight the syntax for Objective-C yet!)

And here is the MyFirstMapAppViewController.m implementation file:

#import "MyFirstMapAppViewController.h"

@implementation MyFirstMapAppViewController

@synthesize mapView = _mapView;
@synthesize streetView = _streetView;
@synthesize streetLayer = _streetLayer;
@synthesize imageryView = _imageryView;
@synthesize imageryLayer = _imageryLayer;
@synthesize reliefView = _reliefView;
@synthesize reliefLayer = _reliefLayer;

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.mapView.mapViewDelegate = self;
    
    self.streetLayer = [AGSTiledMapServiceLayer tiledMapServiceLayerWithURL:[NSURL URLWithString:kTiledStreetMapServiceURL]];
    self.streetView = [self.mapView addMapLayer:self.streetLayer withName:@"Street"];
    self.streetView.hidden = NO;

    self.imageryLayer = [AGSTiledMapServiceLayer tiledMapServiceLayerWithURL:[NSURL URLWithString:kTiledImageryMapServiceURL]];
    self.imageryView = [self.mapView addMapLayer:self.imageryLayer withName:@"Imagery"];
    self.imageryView.hidden = YES;

    self.reliefLayer = [AGSTiledMapServiceLayer tiledMapServiceLayerWithURL:[NSURL URLWithString:kTiledReliefMapServiceURL]];
    self.reliefView = [self.mapView addMapLayer:self.reliefLayer withName:@"Relief"];
    self.reliefView.hidden = YES;
        
}

// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return YES;
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)dealloc {
    self.mapView = nil;
    self.streetView = nil;
    self.streetLayer = nil;
    self.imageryView = nil;
    self.imageryLayer = nil;
    self.reliefView = nil;
    self.reliefLayer = nil;
    
    [super dealloc];
}

- (IBAction)toggleLayer:(id)sender {
    
    self.streetView.hidden = (((UISegmentedControl *)sender).selectedSegmentIndex != 0);
    self.imageryView.hidden = (((UISegmentedControl *)sender).selectedSegmentIndex != 1);
    self.reliefView.hidden = (((UISegmentedControl *)sender).selectedSegmentIndex != 2);    
}

#pragma mark AGSMapViewDelegate

//called when the map view is loaded (after the view is loaded) 
- (void)mapViewDidLoad:(AGSMapView *)mapView {
    
    //create extent to be used as default
    AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:-124.83145667
                                                     ymin:30.49849464
                                                     xmax:-113.91375495
                                                     ymax:44.69150688
                                         spatialReference:mapView.spatialReference];
    
    //call method to set extent, pass in envelope
    [self.mapView performSelector:@selector(zoomToEnvelope:animated:) 
                       withObject:envelope
                       afterDelay:0.5];
}    

@end

There are a couple of points to note about how things are wired up in Interface Builder.

The UISegmentedControl’s Value Changed event is hooked up to the File’s Owner’s toggleLayer function.

IB1

And the UISegmentedControl’s Autosizing is set to bottom center. By configuring the control this way in Interface Builder, we do not need to write any code to handle placement of the UISegmentedControl when the view rotates.

IB2

Summary

Well, that about covers everything for my first experience with the ESRI iPhone SDK. All in all, I’d say it looks like another great client API for use with ArcGIS Server. For those of us who have been wanting to create native iPhone apps on the ESRI stack, the wait is over!

I hope you enjoyed this brief tour of ESRI’s latest offering.

Click here to download the source code from this article.

Additional Resources

Tags: , , ,

ArcGIS Server | ESRI | Mobile | iPhone

Continuously Display Lat Lon Coordinates in a Bing Maps Silverlight App

by James Richards April 12, 2010

Overview

This article presents code and a brief tutorial showing how to continuously display the real world latitude / longitude coordinates of the mouse location in a Bing Maps Silverlight Application.

You can view a live sample or download the source code.

Tutorial

Create a new Bing Maps Silverlight application called LatLonApp using the steps shown in my previous Getting Started with the Bing Maps Silverlight control post.

Open the solution in Blend 3, and open the MainPage.xaml user control.

Select the Map control in the Objects and Timeline Window

image

In the upper right hand corner, enter the name “MyMap” for the Name property in the Properties window and hit return.

image

Select the TextBlock tool in the toolbar, and click and drag on the artboard to add new text block to the project.

image

Position the text block in the lower right, just above the scale bar. Change the name the text block to “Coords”.

In the Properties window, set the Text property to “Lat, Lon” and the justification to Right.

image

image

Ensure that the Horizontal Alignment is set to Right, the Vertical Alignment is set to Bottom, the Left and Top Margins are set to 0 and the Right and Bottom margins are set to 5 and 57 respectively.

image

Notice how the text on the scale bar has a 1 pixel white drop shadow. Next, we’ll duplicate that effect for the Coords text block.

Click on the Assets tab in the upper left, and then select the Effects category. This will display any effects you have registered with Blend on the right hand side of the split window.

image

Drag the DropShadowEffect onto the Coords text block.

image

This will add the effect to the text block, select the effect in the Objects and Timeline window, and display the effect’s properties in the Properties window on the right.

Change the Blur Radius to 1, the Color to White, and the Shadow Depth to 1.

image

Now the text block is styled in the same way as the scale bar text.

image

Select the Map in the Objects and Timeline window or on the artboard and click the Events icon in the upper right hand corner of the Properties window.

image

Find the MouseMove event, enter MyMap_MouseMove and hit enter.

image

This will create a new event handler and open up the code behind file MainPage.xaml.cs.

image

At this point you can either code up the event in Blend, or switch back to Visual Studio. I prefer to switch back to Visual Studio for the Intellisense. Note that you could also have switch back earlier and created the event in Visual Studio as well.

Make sure that all of the files are saved before switching back by choosing Save All from the File menu or pressing Ctrl+Shift+S.

When you switch back to Visual Studio it will notice that the files have been modified and present a dialog asking if you want to reload the file(s). Click Yes to All.

image

Open the MainPage.xaml.cs and add a using statement for the Microsoft.Maps.MapControl namespace.

Add the following code to the MyMap_MouseMove event.

private void MyMap_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
    Point viewportPoint = e.GetPosition(MyMap);
    Location location;
    if (MyMap.TryViewportPointToLocation(viewportPoint, out location))
    {
        Coords.Text = String.Format("Lat: {0:f4}, Lon: {1:f4}",
            location.Latitude, location.Longitude);
    }
}

This code gets the current mouse position in Viewport coordinates and transforms the Point to a latitude longitude Location. If the transformation is successful, the latitude and longitude are rounded to 4 decimal places and the Coords text block is update to display the coordinate.

Press F5 to compile and run the project. As you move the mouse around the map, the coordinates are displayed above the scale bar.

image

Wrapup

In this article you learned how to continuously display the real world latitude / longitude coordinates of the mouse location in a Bing Maps Silverlight Application.

You can view a live sample of the application or download the source code.

Additional Resources

Tags: , , ,

Bing Maps | Silverlight

How will Apple’s New Developer Agreement Affect iPhone Mapping Apps?

by James Richards April 09, 2010

The Twitterverse and Blogosphere are all abuzz with yesterday’s news that Apple has apparently changed terms of its Developer Agreement to explicitly ban apps created with cross-compilers. This got me thinking about how this change might affect those of us developing mapping apps for the iPhone (and iPad).

The change that most people are talking about is in section 3.3.1 which now reads:

3.3.1 — Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs. Applications must be originally written in Objective-C, C, C++, or JavaScript as executed by the iPhone OS WebKit engine, and only code written in C, C++, and Objective-C may compile and directly link against the Documented APIs (e.g., Applications that link to Documented APIs through an intermediary translation or compatibility layer or tool are prohibited).

I have to wonder if ESRI’s iPhone SDK or CloudMade’s iPhone SDK or any of the location toolkits such as SimpleGeo’s iPhone SDK were “originally written” in one of the approved languages without the aid of a cross-compiler. If not, apps built with these tools could be banned.

As an example, the linked SimpleGeo page states that “The SGClient is a basic Objective-C wrapper around SimpleGeo's OAuth API.” What is SimpleGeo’s OAuth API and does it meet these new requirements?

I’d like to know from some of these vendors whether or not their SDKs meet Apple’s new requirements.

But even more disturbing is this language in section 3.3.2:

“An Application may not itself install or launch other executable code by any means, including without limitation through the use of a plug-in architecture, calling other frameworks, other APIs or otherwise.”

That sounds to me like Apple just banned the use of any third party APIs. Scary.

For now, we have to take a wait and see approach until Apple clarifies these points. But even if we can move forward using these SDKs to develop mapping apps for the iPhone / iPad, are people going to be willing to risk investing in this technology under these conditions? Even if you do everything right by Apple’s standards, there is a very real possibility of getting banned in the future when Apple suddenly changes it’s terms again.

It’s well known that Apple acquired Placebase last year, but no one really knows what the Apple Geo Team is up to. Who’s to say that Apple won’t release their own mapping platform and ban the use of all other mapping platforms on their devices? Sounds crazy, but then again, it appears that Apple has banned the use of a Lua which helped produce Angry Birds (the best selling paid app of all time) as well as every single game created by Electronic Arts.

Does this news change your perspective on developing mapping apps for the iPhone / iPad? Leave a comment to share your thoughts…

Tags: , , , , ,

Mobile

Getting Started with the Bing Maps Silverlight Control, Visual Studio 2008 and Blend 3

by James Richards April 08, 2010

Overview

This walkthrough shows how to create a new Bing Maps Silverlight Control project with either Visual Studio 2008 SP1 or Blend 3. While some of this material has been covered on other sites and blogs, I’m writing about it here for two reasons:

  1. Some of the other posts are now outdated, using older, beta or CTP versions of the APIs and development tools.
  2. In future posts about developing with the Bing Maps Silverlight Control, I want to be able to refer to this “Getting Started” guide so I don’t have to keep repeating the basics.

Download and Install the Development Tools

If you haven’t already done so, download and install the following applications, SDKs and Toolkits:

Required

VS2008

Microsoft Visual Studio 2008 SP1 (Includes .NET Framework 3.5 SP1)

Silverlight Microsoft Silverlight 3 Tools for Visual Studio 2008 SP1

Blend

Expression Blend 3 or Expression Studio 3
Bing Bing Maps Silverlight Control SDK v 1.0.1

Optional

SilverlightToolkit Microsoft Silverlight 3 Toolkit November 2009

 

 

[more]

Setup a Big Maps Developer Account Using Your Windows Live ID

image

If you don’t already have a Windows Live ID, you’ll need to signup for one on the Windows Live site: https://signup.live.com/signup.aspx

One you have your Windows Live ID, go the the Bing Maps Developer Portal: https://www.bingmapsportal.com/

Click on the “Create or view Bing Maps keys” link in the left menu:

image

When prompted, login with your Windows Live ID. After logging in, you will be redirected to the proper page.

Enter the Application Name and the URL, and click the Create Key button.

image

Here you can see I’ve created two keys. One for development and testing through the http://localhost url and one for running applications on a live URL (in this case, my blog.) I’ve blacked out the actual keys.

Create a New Bing Maps Silverlight Application with Visual Studio

When creating a new Silverlight application, you can start with either Visual Studio or Blend. First we’ll look at how to create the application with Visual Studio. Then in the next section we’ll see how it’s done with Blend.

Start Visual Studio 2008 and choose File > New > Project…

Select Silverlight from the Project types tree, and Silverlight Application from the Templates list.

Give your application a name and click OK.

image

A dialog will appear asking if you want to host the application in a new web site. Make sure this option is checked, select ASP.NET Web Application Project, and click OK.

image

After the project is created, add references to the Bing Maps assemblies.

Right click on the References folder in the BingMapsApp project and choose Add Reference…

image

Select the Browse tab and browse to the Libraries directory in the Bing Maps Silverlight Control installation folder. The default installation location is C:\Program Files\Bing Maps Silverlight Control\V1\Libraries.

Select both the Microsoft.Maps.MapControl.dll and Microsoft.Maps.MapControl.Common.dll assemblies, and click OK.

image

image

Modify the xaml for the MainPage.xaml user control by adding an xml namespace declaration for the Bing Maps assembly.

image

xmlns:bing="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"

Note: The samples on Microsoft’s Bing Maps Silverlight Control Interactive SDK all use the “m” prefix for the namespace, but I prefer to use “bing” as it makes the code a little more expressive. Hat tip to Bobby Diaz’s Bing Maps Silverlight MVVM Sample for that idea.

Add a <bing:Map …/> element inside the layout root and set the CredentialsProvider property to the key that you generated earlier.

image

At this point, you should be able to press F5 and see the default map in your browser.

image

If you see the “Invalid Credentials” message then double check to make sure you have properly set the CredentialsProvider property.

image

At this point I like to clean up the Web project a little bit.

Open up the BingMapsAppTestPage.aspx page, select everything from <!DOCTYPE … down to the bottom of the file, and Ctrl+C to copy it to the clipboard.

Open up the Default.aspx page, delete everything from <!DOCTYPE … down to the bottom of the file, and paste in the contents from the clipboard.

Save the Default.aspx file.

Delete the BingMapsAppTestPage.aspx page and the BingMapsAppTestPage.html file.

Right click on Default.aspx and choose Set As Start Page…

image

Press F5 to run the project and confirm that the control is now hosted in the default page.

 

 

Create a New Bing Maps Silverlight Application with Blend

Now that we’ve covered how to create a new Bing Maps Silverlight application with Visual Studio 2008, lets take a look at how to accomplish the same task with Blend 3.

Start Blend 3 and choose File > New Project…

Select Silverlight from the Project types tree, and Silverlight 3 Application + Website from the template list.

Give your application a name, and click OK.

image

When you create a new Silverlight project in Blend, the structure of the web site is different than if you had created it in Visual Studio. The Silverlight app is hosted in an html page rather than an ASP.NET aspx page.

image

After the project is created, add references to the Bing Maps assemblies.

Right click on the References folder in the BingMapsApp project and choose Add Reference…

image

Browse to the Libraries directory in the Bing Maps Silverlight Control installation folder. The default installation location is C:\Program Files\Bing Maps Silverlight Control\V1\Libraries.

Select both the Microsoft.Maps.MapControl.dll and Microsoft.Maps.MapControl.Common.dll assemblies, and click OK.

image

image

Click the Asset Library button and type “map” in the search box to filter the assets to those controls that contain the phrase “map” in their name. Then select the Bing Maps Silverlight Control from the available choices.

image

Note: The preceding screen shot shows two controls named “Map”. The other one is ESRI’s ArcGIS API for Microsoft Silverlight/WPF Map Control. If you also happen to have this library installed, make sure you choose the correct Map.

After selecting the Map control, it will appear below the Asset Library button.

image

Double click the Map control button to add a map to the artboard at the default size.

image

Click on the Xaml button on the upper right hand side of the artboard to switch to Xaml view.

image

Notice that when you added the Map control Blend automatically added a namespace declaration for you.

image

Since the automatically generated namespace name is rather verbose, change it to “bing”.

image

Click the Design button on the upper right hand side of the code window to switch back to the Design view.

In the Properties window Layout bucket, the right and bottom margins will have defaulted to 240 and 180 respectively.

image

Set each of these properties to 0 so that the map fills the whole layout.

image

image

At the bottom of the Properties window, click arrow next to the Miscellaneous tab to expand it.

Paste your API Key into the text box for the CredentialsProvider property and hit return.

image

At this point you can click F5 to compile and run the project. Notice that the map does not fill the entire browser window like it did with the Visual Studio solution we built in in part 1.

image

This is because Blend sets an explicit Width and Height by default when creating a User Control as part of a new project, while Visual Studio sets DesignWidth and DesignHeight properties with the “Ignorable” namespace prefix.

Switch back to Xaml view, and replace the names of the Width and Height properties with d:DesignWidth and d:DesignHeight.

image

Now run the project again and the Map will fill the whole browser window.

As was the case in the Visual Studio walkthrough, if you see the “Invalid Credentials” message then double check to make sure you have properly set the CredentialsProvider property.

Wrapup

This post presented two methods for creating a new Bing Maps Silverlight Control project. The first method gave a walkthrough using Visual Studio 2008 SP1, and the second showed how to accomplish the same task with Blend 3. You can use either method, depending on which tool you are most comfortable with.

Additional Resources

Microsoft

Bing Maps MVVM Samples

Tags: , , ,

Bing Maps | Silverlight

Powered by BlogEngine.NET 1.6.0.0
Theme by Mads Kristensen | Modified by Mooglegiant
Creative Commons License This work is licensed under a Creative Commons Attribution 3.0 United States License.

Welcome

James Richards

Hi, I'm James Richards and I'm an advisor to Zaarly and the CTO and co-founder of Artisan Global LLC. We make location-aware mobile apps with maps. I also enjoy surfing, snowboarding, golfing, yoga, and music. I love my family and our cats Remy and Twiggy. They make sure I don't work too hard. Thanks for stopping by, I hope you find something helpful here.

Subscribe by RSS   Follow me on Twitter   Connect on Facebook   View my profile on LinkedIn


Amazon Associates

Some of my posts may contain Amazon Associates links. I only include these links when it makes sense within the context of the article. If you are in the market for one of these items, please consider clicking on an affiliate link to make your purchase. You still get the same great deal from Amazon and it will help me pay for hosting and bandwidth. Thanks!