iphone and gis development notes

By Rupert

Follow me on TwitterRSS Feeds

  • Home
  • About

iPhone Cross Application Launch/Marketing Strategy

Jul 15th

Posted by rupert in business

No comments

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…
photo1.jpg

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.
photo3.png

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.
photo2.jpg

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).
photo4.jpg

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.

iphone

Blogging, MyTravelPhilippines, Navteq

Jul 8th

Posted by rupert in business

No comments

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…

business, iphone, mobile

Rails Note #14: QuickStart Tutorial

Feb 7th

Posted by rupert in Uncategorized

No comments

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.

rails.png

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" *
ruby on rails

iPhone Note #20:Integrating Mapserver/TileCache to RouteMe

Jan 14th

Posted by rupert in Uncategorized

No comments

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.

iphone, mapserver, tilecache

iPhone Note #19: Route-Me: Opensource mapping for the iphone

Dec 8th

Posted by rupert in iphone

No comments

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/).

Part 1: Making the Sample Apps work.

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.

routeme-libMapView.png

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.

routeme-samplemap.png

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

routeme-samplemap-ok.jpg

Part 2: Integrating with your own project.

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”.

routeme-referencing-mapview.jpg

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

routeme-ref-dd.jpg

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.

routeme-searchpaths.jpg

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

iphone
12345»102030...Last »
  • Tags

    Algorithms apache centos china coldfusion coldspring debian eclipse ExtJS firefox gdal GeoJSON google iphone javascript lbs leopard linux mac mapinfo mapserver modelglue mod_python mysql ogr2ogr openlayers oracle pgRouting postgis postgres python qmail raster reactor routing ruby ruby on rails sqlite3 subversion svn tilecache trac uml windows mobile WordPress
  • Recent Posts

    • iPhone Cross Application Launch/Marketing Strategy
    • Blogging, MyTravelPhilippines, Navteq
    • Rails Note #14: QuickStart Tutorial
    • iPhone Note #20:Integrating Mapserver/TileCache to RouteMe
    • iPhone Note #19: Route-Me: Opensource mapping for the iphone
    • iPhone Note #18: Integrating Ads (AdMob) on your iPhone App
    • iPhone Note #17: Displaying a custom view controller from a UITextField
    • Subversion
    • iPhone Note #16: Creating a MKMapView using IB
    • iPhone Note #15: Omnigraffle iPhone Stencils, Screen Mockups via PPT, creating an iPhone interface before actual development
  • Comments

    • wholesale sunglasses on SQLite3 Cheatsheet
    • bad credit loan on Loading Mapinfo table to PostGis
    • Bad credit loan on SQLite3 Cheatsheet
    • dradaynah on iPhone Note #6: Presenting a DatePicker as a ViewController Modally
    • rupert on Oracle SQL CheatSheet
  • Archives

    • July 2010 (2)
    • February 2010 (1)
    • January 2010 (1)
    • December 2009 (2)
    • November 2009 (2)
    • October 2009 (4)
    • September 2009 (4)
    • August 2009 (13)
    • July 2009 (1)
    • June 2009 (2)
    • May 2009 (2)
    • April 2009 (1)
    • December 2008 (4)
    • November 2008 (18)
    • October 2008 (2)
    • September 2008 (6)
    • August 2008 (7)
    • July 2008 (6)
    • June 2008 (3)
    • May 2008 (9)
    • April 2008 (2)
    • March 2008 (9)
    • February 2008 (7)
    • January 2008 (7)
    • December 2007 (2)
    • November 2007 (8)
    • October 2007 (3)
    • August 2007 (16)
    • July 2007 (13)
    • June 2007 (11)
    • May 2007 (1)
    • April 2007 (5)
    • March 2007 (5)
    • February 2007 (9)
    • January 2007 (12)
    • September 2006 (5)
  • Useful Links

Mystique theme by digitalnature | Powered by WordPress
RSS Feeds XHTML 1.1 Top