iphone and gis development notes
By Rupert
By Rupert
Jul 15th
As many developers keep on asking me how I reached the rankings I have now for MyTravelPhilippines, I would like to share the marketing/launch strategy that we did. The strategy is not new. I’ve read from numerous iphone developer blogs who have a few apps in the store about cross app promotion. But how do you really do it?
On Dec 2009, I have released the “Philippines 2010 Election Survey”. The red dot is on May 11 which is the actual elections for the Philippines. However after this event, you can see the downloads went down.. well because the elections are over…

When the election results came out from the government, I immediately made the app, “Philippines 2010 Election Results”. I have launched this on June 26. I was hoping that most users would be eager to see the results and most did update to the new app. Within the updated app, I made a link to download “MyTravelPhilippines” in the appstore.

The election app has a total of 4k users where 25% updated. Consequently, the “MyTravelPhilippines” eyeball did do the trick and on June 26 we had 849 downloads.

The 849 downloads on June 26 was significant as it was able to push “MyTravelPhilippines” to rank #1(Free Philippines Travel Category) and rank #9 (Free Philippines Overall).

In just two weeks, I was able to hit 5k mark for MyTravelPhilippines which took me 6 months to do in the Philippines Elections app.
What I’ve learned?
- Use an event, in this case the election results, to get most out of the updates.
- Cross app promotion works. It’s like migrating your users from one app to another.
- Application Visibility is very important. Try hard to make it to the Top 10 in that category. (Yes, I know its easier said that done.. but really aim for this..)
What I’ve missed?
Application updates to keep the app alive. This would help in “the download and never open again or open occasionally syndrome”. At the time of this writing, I am currently trying to do this as I could see the graph is slowly trending down. What I should have anticipated is to keep at least one version higher (1.1) and uploading the older version(1.0) on the store. The timing of this updates could be periodical–after 3 weeks, a month or an event. But with the limited resources that we had, only a two man team, and no marketing $$$ expense at all in ads, it was a great experience.
Jul 8th
I have neglected this blog for the past few months because of the past activities I was involved in. It was a fruitful year for the iPhone as I have launched two successful and rewarding projects.
1. The iphone app for a client in the beforeYouDig industry has extremely improved over the past 6 months. It has won second place in the Navteq LBS Challenge for APAC. If you have been following my posts, this was related to route-me. Hopefully, we should launch this in the AppStore soon. Stay tuned…
2. MyTravelPhilippines was launched last April on the web. After that, I started working on the mobile version on the iPhone. I pushed it in the store on June 15 and became Top 10 Free after a week in the PH store, and Top 1 in Travel Free category for the PH store. This was really great as we haven’t spent any $$$ on the marketing and relied on friends, facebook, etc for viral marketing. I am pushing again for another release before this month ends. Again, stay tuned.
So to break the ice.. I will be blogging again…
Feb 7th
If you haven’t installed ruby, follow this post
Part 1: Installation and Configuration (Rails and Passenger)
1. Upgrade existing ruby gems
sudo gem list sudo gem update --system
UPDATE: Took me 4 hours figuring this out. There was a problem when i run script/console that it will say the “gem” detected was 1.0.1 although the current gem version is 1.3.5 after gem update –system. Google didn’t helped out. But I was able to nail down the problem from this post:
https://wincent.com/wiki/Upgrading_to_RubyGems_1.0.1_on_Mac_OS_X_10.5.1
In the post above, notice that rubygems 1.0.1 was installed in /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin. I guess this gem was being referenced first before the actual /usr/local/bin/gem. So I deleted this directory /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr.
Possible sources of gem installations:
/Users/rupert/.gem/ruby/1.8/gems /Library/Ruby/Gems/1.8/gems
rupert:1.8 rupert$ gem env RubyGems Environment: - RUBYGEMS VERSION: 1.3.5 - RUBY VERSION: 1.8.6 (2008-08-08 patchlevel 286) [i686-darwin9.5.0] - INSTALLATION DIRECTORY: /usr/local/lib/ruby/gems/1.8 - RUBY EXECUTABLE: /usr/local/bin/ruby - EXECUTABLE DIRECTORY: /usr/local/bin - RUBYGEMS PLATFORMS: - ruby - x86-darwin-9 - GEM PATHS: - /usr/local/lib/ruby/gems/1.8 - /Users/rupert/.gem/ruby/1.8 - GEM CONFIGURATION: - :update_sources => true - :verbose => true - :benchmark => false - :backtrace => false - :bulk_threshold => 1000 - REMOTE SOURCES: - http://gems.rubyforge.org/
2. Install rails. Download from ruby-forge. Link?
gem install -V rails-2.3.3.gem gem install -V mysql
3. Install and configure passenger for Apache2
gem install -V passenger cd /Users/rupert/.gem/ruby/1.8/gems/passenger-2.2.5/bin passenger-install-apache2-module
469 LoadModule passenger_module /Users/rupert/.gem/ruby/1.8/gems/passenger-2.2.5/ext/apache2/mod_passenger.so 470 PassengerRoot /Users/rupert/.gem/ruby/1.8/gems/passenger-2.2.5 471 PassengerRuby /usr/local/bin/ruby 472 473 <VirtualHost *:80> 474 RailsBaseURI /rails/travelsiteph 475 </VirtualHost>
4. Create a sample rails project (“travelsiteph”) in your project directory (“/Users/rupert/projects/rails”).
$ cd /Users/rupert/projects/rails $ rails travelisteph create create app/controllers create app/helpers create app/models create app/views/layouts create config/environments ..... $ ls -la travelsiteph drwxr-xr-x 15 rupert admin 510 2 Sep 21:14 . drwxr-xr-x 5 rupert admin 170 30 Sep 16:31 .. -rw-r--r-- 1 rupert admin 10011 2 Sep 21:14 README -rw-r--r-- 1 rupert admin 307 2 Sep 21:14 Rakefile drwxr-xr-x 6 rupert admin 204 2 Sep 21:14 app drwxr-xr-x 9 rupert admin 306 2 Sep 21:14 config drwxr-xr-x 4 rupert admin 136 2 Sep 21:18 db drwxr-xr-x 3 rupert admin 102 2 Sep 21:14 doc drwxr-xr-x 3 rupert admin 102 2 Sep 21:14 lib drwxr-xr-x 6 rupert admin 204 2 Sep 21:14 log drwxr-xr-x 11 rupert admin 374 2 Sep 21:14 public drwxr-xr-x 11 rupert admin 374 2 Sep 21:14 script drwxr-xr-x 8 rupert admin 272 2 Sep 21:55 test drwxr-xr-x 6 rupert admin 204 2 Sep 22:07 tmp drwxr-xr-x 3 rupert admin 102 2 Sep 21:14 vendor
I have /wwwroot as my document WebRoot. Its running cf, php and mapserv (cgi-bin). Since I want to mix it with rails development, I’ll just make a rails subdirectory. Inside the rails subdirectory, I can create symbolic links to my rails applications located in my projects directory. This way, rails configuration is not exposed from Apache.
cd /wwwroot mkdir rails ln -s /Users/rupert/projects/rails/travelsiteph/public travelsiteph $ ls -la total 8 drwxr-xr-x 3 rupert admin 102 2 Sep 14:09 . drwxrwxr-x 60 root admin 2040 2 Sep 14:08 .. lrwxr-xr-x 1 rupert admin 42 2 Sep 14:09 travelsiteph -> /Users/rupert/projects/rails/travelsiteph/public
5. Restart Apache to take the new configuration
sudo /Library/StartupItems/Apache2/Apache2 restart
6. Open http://127.0.0.1/rails/travelsiteph/
But for development purposes, it is better to use http://127.0.0.1:3000/ to see immediately any changes in code.

Part 2: Rails Development
MySQL Prerequisites:
GRANT ALL PRIVILEGES ON *.* TO rupert@'%' IDENTIFIED BY '*************' WITH GRANT OPTION; $ mysql -u rupert -p
1. Create three databases:
mysql> CREATE DATABASE travelsiteph_development; Query OK, 1 row affected (0.00 sec) mysql> CREATE DATABASE travelsiteph_test; Query OK, 1 row affected (0.00 sec) mysql> CREATE DATABASE travelsiteph_deployment; Query OK, 1 row affected (0.00 sec)
2. Launch Textmate
cd /Users/rupert/projects/rails/travelsiteph mate .
3. Edit database.yml
development: adapter: mysql database: travelsiteph_development username: root password: xxxxxxx host: localhost test: adapter: mysql database: travelsiteph_test username: root password: xxxxxxx host: localhost production: adapter: mysql database: travelsiteph_deployment username: root password: xxxxxxx host: localhost
4. Generate a Poi model. The model should be capitalized and singular.
$ ruby script/generate model Poi exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/poi.rb create test/unit/poi_test.rb create test/fixtures/pois.yml create db/migrate create db/migrate/20090902111538_create_pois.rb
5. Now let’s create the database table for Poi using migrations.
class Poi < ActiveRecord::Migration def self.up create_table :pois, :id => :poi_id do |t| t.column :name, :string t.column :full_address, :string t.column :location, :string t.column :get_there, :string t.column :short_description, :string t.column :full_description, :text t.column :other_info, :text t.column :tel_no, :string t.column :fax_no, :string t.column :mobile_no, :string t.column :email, :string t.column :website, :string t.column :other_contact_details, :text t.column :longitude, :decimal, :precision => 10, :scale => 7 t.column :latitude, :decimal, :precision => 10, :scale => 7 t.column :created_at, :timestamp t.column :updated_at, :timestamp t.timestamps end end def self.down drop_table :pois end end
$ rake db:migrate (in /Users/rupert/projects/rails/travelsiteph) == CreatePois: migrating ========================================= -- create_table(:point_of_interests, {:id=>:poi_id}) -> 0.0353s == CreatePois: migrated (0.0362s) ================================
6. Generate a Poi controller.
$ script/generate controller Poi exists app/controllers/ exists app/helpers/ create app/views/poi create test/functional/ create test/unit/helpers/ create app/controllers/poi_controller.rb create test/functional/poi_controller_test.rb create app/helpers/poi_helper.rb create test/unit/helpers/poi_helper_test.rb
7. Add a list function to Poi Controller
class PoiController < ApplicationController def list @pois = Poi.find(:all) end end
8. Lets test. Open a browser and point to http://127.0.0.1:3000/travelsiteph/poi/list
$ruby script/server
9. Now create the view list.rhtml in views/poi/
<% if @pois.blank? %> <p>There are currently no pois in the system. </p> <% else %> <p>These are the pois in the system: </p> <ul> <% @pois.each do |poi| %> <li><%= link_to poi.name, {:action => 'show', :id => poi.id} -%></li> <% end %> </ul> <% end %>
Part 3: Deploying
1. set RAILS_ENV to production
export RAILS_ENV=production
2. Make sure to populate the database in production mode, run rake db:migrate
3. Capistrano
set :port, 2210 set :application, "halalan2010" #set :repository, "svn+ssh://2rmobile.com/data/repos/web/rails/halalan2010/" set :repository, "http://2rmobile.com/repos/web/rails/halalan2010/" set :scm, :subversion set :scm_username, 'rupert' set :scm_password, proc{Capistrano::CLI.password_prompt('SVN pass:')} # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none` role :web, "2rmobile.com" # IP Your HTTP server, Apache/etc role :app, "2rmobile.com" # This may be the same as your `Web` server role :db, "2rmobile.com", :primary => true # This is where Rails migrations will run #role :db, "your slave db-server here" set :user, "rupert" set :runner, "rupert" set :deploy_to, "/opt/rails/#{application}" # If you are using Passenger mod_rails uncomment this: # if you're still using the script/reapear helper you will need # these http://github.com/rails/irs_process_scripts namespace :deploy do task :start do run "/etc/init.d/apache2 start" end task :stop do run "/etc/init.d/apache2 stop" end task :restart, :roles => :app, :except => { :no_release => true } do run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}" end task :production do run "export RAILS_ENV=production" end end namespace :db do task :seed do run "cd #{deploy_to}/current && RAILS_ENV=production rake db:seed" end task :populate do run "cd #{deploy_to}/current && RAILS_ENV=production rake db:populate" end end
Capistrano commands I normally use:
#on local #cap deploy:setup #on remote and change owner and permissions of project sudo chown -Rf rupert:root halalan2010 #on local #cap deploy #cap db:seed #cap db:populate
Part 4: Miscellaneous
1. Get a description or rake commands
rake -D db
2. How to populate the database in production mode?
rupert:halalan2010 rupert$ export RAILS_ENV=production rupert:halalan2010 rupert$ rake db:migrate (in /Users/rupert/projects/rails/halalan2010) == CreateDatabase: migrating ================================================= -- create_table(:candidates) -> 0.0036s -- create_table(:voters) -> 0.0031s -- create_table(:votes) -> 0.0027s == CreateDatabase: migrated (0.0100s) ========================================
http://blog.airbladesoftware.com/2009/4/10/avoid-typing-rails_env-all-the-time
3. Uninstall specific gem version
gem uninstall activesupport -v 2.2.2
4 Add a source to gem
rupert:rails rupert$ sudo gem sources -a http://gems.github.com http://gems.github.com added to sources
5. Adding a rails project in svn
#create a remote dir svn mkdir http://www.2rmobile.com/repos/web/rails/virginmobilechecker #checkout and copy all files cd ~/projects/rails mv virginmobilechecker virginmobilechecker_old svn co "svn+ssh://2rmobile.com/data/repos/web/rails/virginmobilechecker" virginmobilechecker mv virginmobilechecker_old/* virginmobilechecker/ cd virginmobilechecker svn add * #ignoring log files svn revert log/* svn propset svn:ignore "*.log" log svn propset svn:ignore "*" tmp svn propset svn:ignore "*" doc #commit the files svn commit -m "first commit" *
Jan 14th
Below is a summary of how I was able to implement Mapserver, TileCache and Route-Me iPhone Mapping Framework.
1. Assuming you have a working Mapserver/TileCache setup. Take note of the ff parameters: resolution and bbox. Below is my tilecache.cfg:
61 [mapserver_australia_3857] 62 type=MapServerLayer 63 mapfile=/Users/rupert/projects/pelicancorp/DMOB/trunk/map/australia_3857.map 64 layers=all 65 extension=jpg 66 bbox=-20037508.34, -20037508.34, 20037508.34, 20037508.34 68 maxResolution=156543.033928041 70 levels=20 71 srs=EPSG:3857 72 tms_type=google 73 extent_type=loose 74 spherical_mercator=true
2. Grab the RMGenericMercatorWMSSource from http://groups.google.com/group/route-me-map/browse_thread/thread/b58fa1d20cf15823/e30c42d9c90a8170?lnk=gst&q=Generic#e30c42d9c90a8170
3. By default without any changes, the RMGenericMercatorWMSSource could display Mapserver WMS Tiles. This is possible by passing an NSDictionary *parameters, which contains arrayValues and arrayKeys for creating an http 256×256 image request to the http://127.0.0.1/cgi-bin/mapserv binary.
//This would request to http://127.0.0.1/cgi-bin/mapserv NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"/path-to/australia_3857.map", @"all", @"png", @"EPSG:3857",nil]; NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"MAP", @"LAYERS", @"FORMAT", @"SRS", nil]; NSDictionary *wmsParameters = [[NSDictionary alloc] initWithObjects: arrayValues forKeys:arrayKeys ];
Resulting http requests to the mapserv binary:
http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&SRS=EPSG:3857&REQUEST=GetMap&SERVICE=WMS&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&FORMAT=png&VERSION=1.1.1&WIDTH=256&HEIGHT=256&BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000
Note that the bbox values does not contain decimal places. Nevertheless, it still works on Mapserver.
4. Now, assuming we have a valid tilecache running. And it is tested from browser, i.e http://127.0.0.1/map/tilecache_3857.html, open up firebug to see the requests. Below is a sample…
http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&FORMAT=jpg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A3857&BBOX=16123932.497377,-4539747.9811239,16143500.376618,-4520180.1018829&WIDTH=256&HEIGHT=256
But it seems, route-me is not supplying the bbox values accurately, as the decimal values is truncated (BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000) from our previous request(3).
After changing the wmsParameter values to create a tilecache request, I noticed that route-me is not displaying the tiles correctly.
//Testing for Windows:TileCache - ? NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"australia_3857", @"png", @"EPSG:3857",nil]; //for WIndows //TileCache URL Parameters: NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"LAYERS", @"FORMAT", @"SRS", nil];
5. The workaround is to use “double” instead of “floats” in the RMGenericMercatorWMSSource. You can download the zip from RMGenericMercatorWMSSource.zip. I have modified “initialResolution” and “originShift” to both use “double”.
typedef struct { double x; double y; } CGDoublePoint; typedef struct { CGDoublePoint ul; CGDoublePoint lr; } CGXYRect;
Afterwards, I copied the maxResolution and bbox (tilecache) and specified it for the initialResoultion and originShift respectively.
#import "RMGenericMercatorWMSSource.h" CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180/ M_PI;}; @implementation RMGenericMercatorWMSSource -(id) initWithBaseUrl:(NSString *)baseUrl parameters:(NSDictionary *)params { if (![super init]) return nil; // 156543.03392804062 for sideLength 256 pixels // initialResolution = 2 * M_PI * 6378137 / [self tileSideLength]; // specify here whatever the resolution is from tilecache. initialResolution = 156543.033928041; // 20037508.342789244 //originShift = 2 * M_PI * 6378137 / 2; //originShift = 20037508.342789244f; // specify here whatever the bbox is from tilecache. originShift = 20037508.32; NSLog(@"test initialResolution:%f originShift:%f", initialResolution, originShift); // setup default parameters // use official EPSG:3857 by default, user can override to 900913 if needed. wmsParameters = [[NSMutableDictionary alloc] initWithObjects:[[[NSArray alloc] initWithObjects:@"EPSG:3857",@"image/png",@"GetMap",@"1.1.1",@"WMS",nil] autorelease] forKeys:[[[NSArray alloc] initWithObjects:@"SRS",@"FORMAT",@"REQUEST",@"VERSION",@"SERVICE",nil] autorelease]]; [wmsParameters addEntriesFromDictionary:params]; // build WMS request URL template urlTemplate = [NSString stringWithString:baseUrl]; NSEnumerator *e = [wmsParameters keyEnumerator]; NSString *key; NSString *delimiter = @""; while (key = [e nextObject]) { urlTemplate = [urlTemplate stringByAppendingFormat:@"%@%@=%@", delimiter, [[key uppercaseString] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding], [[wmsParameters objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]]; delimiter = @"&"; } int sideLength = [self tileSideLength]; urlTemplate = [[urlTemplate stringByAppendingFormat:@"&WIDTH=%d&HEIGHT=%d",sideLength,sideLength] retain]; return self; } // implement in subclass? -(NSString*) uniqueTilecacheKey { return @"AbstractMercatorWMSSource"; } -(NSString *)shortName { return @"Generic WMS Source"; } -(NSString *)longDescription { return @"Generic WMS Source"; } -(NSString *)shortAttribution { return @"Generic WMS Source"; } -(NSString *)longAttribution { return @"Generic WMS Source"; } -(float) minZoom { return 1.0f; } -(float) maxZoom { return 19.0f; } // Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:3857 -(CGPoint) LatLonToMeters: (CLLocationCoordinate2D) latlon { CGPoint meters; meters.x = latlon.longitude * originShift / 180; meters.y = (log( tan((90.0 + latlon.latitude) * M_PI / 360.0 )) / (M_PI / 180.0)) * originShift / 180; return meters; } //Converts XY point from Spherical Mercator EPSG:3857 to lat/lon in WGS84 Datum -(CLLocationCoordinate2D) MetersToLatLon: (CGPoint) meters { CLLocationCoordinate2D latlon; latlon.longitude = (meters.x / originShift) * 180.0; latlon.latitude = (meters.y / originShift) * 180.0; //latlon.latitude = - 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); latlon.latitude = 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); return latlon; } // Converts pixel coordinates in given zoom level of pyramid to EPSG:3857 -(CGDoublePoint) PixelsToMeters: (int) px PixelY:(int)py atZoom:(int)zoom { double resolution = [self ResolutionAtZoom: zoom]; CGDoublePoint meters; double x = (px * resolution - originShift); double y = (py * resolution - originShift); meters.x = x; meters.y = y; NSLog(@"px: %d py: %d resolution: %f originShift: %f x: %f y: %f", px, py, resolution, originShift, x, y); return meters; } -(NSString*) tileURL: (RMTile) tile { //RMLatLongBounds tileBounds = [self TileLatLonBounds:tile]; // Get BBOX coordinates in meters CGXYRect tileBounds = [self TileBounds:tile]; NSString *url = [urlTemplate stringByAppendingFormat:@"&BBOX=%f,%f,%f,%f", tileBounds.ul.x, tileBounds.lr.y, tileBounds.lr.x, tileBounds.ul.y]; NSLog(@"Tile %d,%d,%d yields %@",tile.zoom, tile.x, tile.y, url); return url; } //Returns bounds of the given tile in EPSG:3857 coordinates -(CGXYRect) TileBounds: (RMTile) tile { int sideLength = [self tileSideLength]; int zoom = tile.zoom; long twoToZoom = pow(2,zoom); CGXYRect tileBounds; tileBounds.ul = [self PixelsToMeters: (tile.x * sideLength) PixelY: ((twoToZoom-tile.y) * sideLength) atZoom: zoom ]; tileBounds.lr = [self PixelsToMeters: ((tile.x+1) * sideLength) PixelY: ((twoToZoom-tile.y-1) * sideLength) atZoom: zoom]; return tileBounds; } //Resolution (meters/pixel) for given zoom level (measured at Equator) -(double) ResolutionAtZoom : (int) zoom { return initialResolution / pow(2,zoom); } @end
6. Download TileCache_RouteMe.zip Note: You need a valid mapserver and tilecache running.
Dec 8th
Route-Me is an opensource mapping api similar to the MapKit.framework. The main project resides in googlecode (http://code.google.com/p/route-me/).
1. Download the code from svn.
svn co http://route-me.googlecode.com/svn/trunk/ route-me
2. In the route-me directory, you would have 3 directories:
- MapView – this contains the mapping framework
- Proj4 – projection library.
- samples
3. Build MapView. I encountered some problems here, particularly this specific issue: Cannot build on 3.1 using iphone simulator.
How to make the build? Now, at the time of this writing, I have XCode3.1.4 and iPhone 3.0, 3.1, 3.1.2. Edit the project settings so that the “build” and “release” will point to use 3.0. If you encounter a problem, it is best to “Clean All”, remove the build directory, then “Build” again. To verfiy if you have a successful build, notice that libMapView.a was created.

Note: Notice here that I have libMapView.a both in Debug-iphoneos/ and Debug-iphonesimulator/
No need to build Proj4 as long as it is sitting on the same directory where MapView is.
4. To test route-me, we can study from the samples directory. Let’s build samples/SampleMap. Notice that SampleMap has a reference to MapView.xcodeproj.

Similarly, we can build this using 3.0 both in the simulator and device.

Reference: http://code.google.com/p/route-me/wiki/EmbeddingGuide
1. Create a view-based project: MyRMSampleMap.
2. Project -> “Add To Project”. Make the reference type as “Relative to Project”.

3. Configure your project to have a direct dependency with MapView.xcodeproj. Follow the instructions exactly as stated from the EmbeddingGuide.

Click on the “Build” tab in your target’s info window.
Change the Configuration popup to read “All Configurations”, so that your changes can be made just once.
Find “Header Search Paths” under “Search Paths” (shortcut: type “header” in the search box at upper right of the target info window). Double-click on the ‘Header Search Paths’ text and add the path to the MapView directory contained in the route-me project located on your file system. (Note that if there are any spaces in the path, enclose the entire entry with “”. If the MapView project folder is placed next to your project’s project folder on the file system, you would have the following in the path: “../MapView”.
Check the ‘Recursive’ box and click Ok.

4. Make a test “Build”.
5. Add a reference to:
- QuartzCore.framework
- libsqlite3.dylib
6. Let’s add our mapview in the interface: MyRMSampleMapViewController.h
#import <UIKit/UIKit.h> #import "RMMapView.h" @interface MyRMSampleMapViewController : UIViewController { RMMapView *mapview; } @property(nonatomic, retain) RMMapView *mapview; @end
7. In the implementation, add this snippet.
@synthesize mapview; ... - (void)viewWillAppear:(BOOL)animated{ mapview = [[RMMapView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)]; [mapview setBackgroundColor:[UIColor blackColor]]; [self.view addSubview:mapview]; }
Download: MyRMSampleMap.zip
Comments