i3Factory World

Your Iphone, iPad & Android Application Factory

Browsing Posts tagged Apple

IOS_OSX_maps_OFFLINE

New MapKit features

In WWDC 2013 Apple introduced many new MapKit features for iOS 7 and added this framework in OSX 10.9 (Mavericks). One of the major changes, which in my opinion didn’t get enough relevance in the developer community, has been the introduction of some base classes that allow full map customization and support for offline maps. In this article I’m going to describe the new MKTileOverlay class and present an example, for both iOS and OSX, that demonstrates the new capabilities.

Since the earlier iPhone OS versions there were many apps in the App Store that were supporting maps different from the ones provided by the operating system: consider for example Navigation apps that required support for offline navigation, that is the possibility to see the map even without internet connection. Another requirements for some special kind of applications that needed to show proprietary information (such as “Yellow Pages” apps) or technical information (e.g. when there was the requirement to show level curves for mountains or to represent the sea level).

There were several issues due to this limitation: first of all the overall mapping experience was completely different of these different approaches each other and in most cases they were subpar if compared with the OS maps performance (either with Google or Apple data). Besides from the point of view of the developer there were the problem of providing the right mapping code to support the map provider data: there were no a unique solution, but many. Some were commercial and expensive, other were open source but with lack of support and finally there were a lot of web-browser based solutions whose performances were far from the native maps other than difficult to integrate with Objective-C.

What we’re going to show in this article is how these things changed drastically and how it is easy to integrate your own map content inside the common MapKit framework.

Map overlays

At the base of our discussion there is the concept of “map overlay”. This is not new in MapKit, but with iOS7 things changed. Overlays are essentially parts of a map that can be overlayed over the base map, that is the part of the map representing the ground, the borders, the roads, and so on. Typically the usage of overlayed is to emphasize some regions of the map having a common property: e.g. to highlight a specific country or to represent the several intensities of an earthquake that occurred in a certain area or finally to highlight a road path in a navigation app.

From the point of view of the developer, an overlay is any object that conforms to the MKOverlay protocol. This protocol defines the minimum properties and methods required to define an overlay: their are the approximate center coordinate and the bounding box the fully encloses the overlay (boundingMapRect). These two properties allow MapKit to determine if a specific overlay is currently visible or not in the map so that the framework can take the actions needed to display this overlay. When an overlay object is added to the map using one of the MKMapView’s addOverlay: methods the control passes to the framework which, when determines that a specific overlay needs to be displayed, calls the map view delegate asking him to provide the graphical representation of the overlay. Before iOS7 Apple was providing a set of concrete MKOverlay compatible classes and they were associated to their corresponding MKOverlayView. E.g. to represent a circular overlay we could use the built-in MKCircle class and then provide, for rendering, the associated MKCircleView class, without the need to define our own object.

With iOS7 things changed: now the MKOverlayView has been replaced by the MKOverlayRenderer. Even if this changesdoesn’t require difficult refactoring to translate the code from pre-iOS7 to iOS7, thanks to the fact that Apple did a 1:1 mapping of methods from the old class to the new class, conceptually the change is significant: now the graphical representation of the overlay is no more provided by a UIView subclass, which is typically considered a heavy class, but it is provided by a class, MKOverlayRenderer, which is much more lightweight and descends directly from NSObject. However the mapping between the old and new class is complete, so in the circle example we can see MKCircleView replaced by MKCircleRenderer.

Finally overlays are stacked on the map, so they are given a Z-index that provides the relative positions of overlays each other and with the fixed parts of the map. Before iOS7 you could stack this overlays and define their positions as in an array, with iOS7 two stacks are defined in fact, and they are called “levels”: one level is the “above the roads level”, the other level is the “above the labels level”. This is an important and useful distinction because now we can change how the overlay rendering interacts with the map by specifying if it lies above or below the labels.

Tile overlays

Whatever is the complexity and size of the overlay, we have seen them up to now overlays as specific shapes. With the new MapKit provided with iOS 7 and OSX Mavericks, there is a new type of overlay called tiled overlay. You may consider this type of overlay as a particular layer the covers the whole map: due to its large dimensions this overlay is tiled, that is it is partitioned in bitmap areas to reduce the memory required to show the data and make the overlay rendering efficient. The purpose of this concrete implementation of the MKOverlay protocol, called MKTileOverlay (together with its rendering counterpart given by the MKTileOverlayRenderer class), is to efficiently represent the whole set of tiles across the map plane and for different zoom levels. This last point is important: when you’re displaying a map using bitmap drawing (to be compared with vector drawing) you can get an efficient implementation only if the specific bitmap representing a part an area of the map has the right details suitable for the current zoom level. This means that if we show the full Europe map we don’t need to present road and cities should be represented as points and only for the major ones; as soon as we zoom in in a specific area then we cannot continue to represent the area by scaling the same tile, because it doesn’t contain the required information and also because we would see evident scaling effects. The solution to this is to divide the continuous allowed zoom range in discrete levels and for each level provide the required set of tiles that will show the details appropriate for that levels. It is evident that if we keep the single bitmap tile size constant (e.g. 256 x 256 pixels) then for each zoom level we must increse the number of tiles by a factor of 4: you can see this in the picture below: the single european tile at zoom level 3, when zoom to zoom level 4 has been split, and furtherly details, with four new different tiles having all the same size of the original tile.

 

map_tiles_offline-mapping

URL templates

The tiled overlay class works efficiently as it does a lazy loading of the tiles: this means that a bitmap tile is looked for and loaded only when it needs to be displayed. In order to know the location of the tile, the developer must define in the tile overlay definition the so-called URL template. This is a string representing a template for the final URL that will be used to retrieve the tile: this template will contain some placeholders that will be replaced by effective values to get the final URL. Each tile can be characterized by 4 parameters: x and y for the tile indexes in the map plane, z for the zoom level and finally scale for the bitmap image resolution (scale factor). The corresponding placeholders for these parameters are: {x} {y} {z} {scale}. So as an example, the OpenStreetMap template URL will be http://c.tile.openstreetmap.org/{z}/{x}/{y}.png and then the tile with index X=547 Y=380 and zoom level Z=10, that fully encloses the city of Rome, will be represented by the URL: http://c.tile.openstreetmap.org/10/547/380.png (see below the image taken from our OSX demo app).

iOS_offline_map_rome_tile

Note that a URL template can be an http:// template to retrieve tiles from the internet, but it could also be a file:// template if we want to retrieve files from the disk: in this way we can save our tiles in the application bundle, or download and install a full tiles package for a certain city, and then display maps even if the device is not connected to the internet.

The mechanism that is used by the framework to translate a required tile coordinate (x;y;z;scale) to an effective bitmap is composed of several steps: this gives the developer the possibility to hook its own code to effectively customize the way the tiles are generated. This can be done by subclassing MKOverlayTile. Note that this is not required if setting the URL template is enough for you.

When the map framework needs a specific map tile, it calls the loadTileAtPath:result: of the MKOverlayTile class (or subclass):

1
- (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *tileData, NSError *error))result;

The first method argument is called path and is a MKTileOverlayPath structure which contains the tile coordinates:

1
2
3
4
5
6
typedef struct {
	NSInteger x;
	NSInteger y;
	NSInteger z;
	CGFloat contentScaleFactor; // The screen scale that the tile will be shown on. Either 1.0 or 2.0.
} MKTileOverlayPath;

The second method argument is a completion block that needs to be called when the tile data has been retrieved: this completion block will be called by passing the data and an error object. The MKTileOverlay default implementation will call the -URLForTilePath method to retrieve the URL and then it will use NSURLConnection to load the tile data asynchronously.

If we want to customize the tile loading behaviour we can easily subclass MKTileOverlay and redefine the loadTileAtPath:result: with our implementation of the loading mechanism. E.g. we can implement our own tiles caching mechanism (other than the one provided by the system via NSURLConnection) to return the cached data before triggering the network call; or we could watermark the default tile if we are shipping a freemium version of our offline map.

A more light way to hook into the tile loading mechanism is to redefine in our subclass the -URLForTilePath: method:

1
- (NSURL *)URLForTilePath:(MKTileOverlayPath)path;

The purpose of this method is to return the URL given the tile path. The default implementation is just to fill-out the URL template, as specified above. You need to redefine this method if the URL template mechanism is not sufficient for your needs. A typical case is when you want to pass in the URL a sort of “device identifier” to validate the eligibility of that specific app to access the URL (e.g. if you provide a limit to the quantity of data that can be accessed by a user on a given time or if you want to charge for this data), another case if you have multiple tile servers and you want to do a sort of “in-app” load balancing or regional-based API access (e.g. you have servers in multiple locations and based on the effective device location you want to access the closer server).

The tile renderer

As all overlays are associated to a renderer, also the tile overlay has its concrete renderer class: MKTileOverlayRenderer. Normally you don’t need to subclass this renderer so your map delegate’s -mapView:rendererForOverlay: method can simply instantiate the default tile overlay renderer initialized with your default or subclassed tile overlay instance. Possible applcations of a custom overlay renderer are when you need to further manipulate the bitmap image, e.g. adding a watermark or applying a filter, and this manipulation is independent from the tile source. In the demo code I defined a custom renderer to be used specifically for the Google map, whose effect is to add a sort of colored translucent mosaic on top of the map tiles.

The demo code

You can get the demo code from Github. This code works on both iOS 7 and OSX 10.9 and its purpose is to present a map and give the user the possibility to switch between different tile set: Apple (system), Google, OpenStreetMap and offline from a subset of OpenStreetMap tiles bundled within the app. In all cases I applied an extra overlay layer to show the tile grid with the x,y,z path associated to each grid. (Note: in OSX if you don’t code sign the app using your OSX Developer Program certificate, you will not be able to see the Apple tiles: the other three tile sets will be visible instead). You will see how you can fully take advantage of all features common to the MapKit (zoom, rotation, pan, custom overlays and also annotations which I didn’t include in the demo) and the only difference is in the tiles source and how they are rendered.

 

map-offline-tiles-demo_code

As you can see in the demo apps, there is a main view controller (iOS) and window controller (OSX). In both cases the main view contains an instance of MKMapView and a segmented control to switch between different visualizations. On the map I have instantiated two overlays. The first one is the grid overlay:

1
2
3
4
 // load grid tile overlay
 self.gridOverlay = [[GridTileOverlay alloc] init];
 self.gridOverlay.canReplaceMapContent=NO;
 [self.mapView addOverlay:self.gridOverlay level:MKOverlayLevelAboveLabels];

This is a tile overlay of subclass GridTileOverlay. It will not replace the map content (this means that is effectively overlayed on the map content) and its purpose is to draw, just above labels, the tiles grid.

The reloadOverlay method is called each time the overlay type selector is changed or when the view is loaded. It removes any existing tileOverlay and replaces it with a new one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-(void)reloadTileOverlay {

	// remove existing map tile overlay
	if(self.tileOverlay) {
		[self.mapView removeOverlay:self.tileOverlay];
	}

	// define overlay
	if(self.overlayType==CustomMapTileOverlayTypeApple) {
		// do nothing
		self.tileOverlay = nil;
	} else if(self.overlayType==CustomMapTileOverlayTypeOpenStreet || self.overlayType==CustomMapTileOverlayTypeGoogle) {
		// use online overlay
		NSString *urlTemplate = nil;
		if(self.overlayType==CustomMapTileOverlayTypeOpenStreet) {
			urlTemplate = @"http://c.tile.openstreetmap.org/{z}/{x}/{y}.png";
		} else {
			urlTemplate = @"http://mt0.google.com/vt/x={x}&y={y}&z={z}";
		}
		self.tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:urlTemplate];
		self.tileOverlay.canReplaceMapContent=YES;
		[self.mapView insertOverlay:self.tileOverlay belowOverlay:self.gridOverlay];
	}
	else if(self.overlayType==CustomMapTileOverlayTypeOffline) {
		NSString *baseURL = [[[NSBundle mainBundle] bundleURL] absoluteString];
		NSString *urlTemplate = [baseURL stringByAppendingString:@"/tiles/{z}/{x}/{y}.png"];
		self.tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:urlTemplate];
		self.tileOverlay.canReplaceMapContent=YES;
		[self.mapView insertOverlay:self.tileOverlay belowOverlay:self.gridOverlay];
	}
}

In the Apple maps case no extra overlay is added of course: we just use the base map. When we select to view the Google and OpenStreetMap we will use a standard MKTileOverlay class with the appropriate URL template. In both cases the overlay will be added with the canReplaceMapContent property set to YES: this replaces the Apple base maps completely and will avoid that those data will be loaded. Note that we add the tileOverlay just below the gridOverlay. Finally the offline case still uses a base overlay class but with a file URL template: note that we create the path from a hierarchical directory structure build inside the bundle. In this case too the new tiles replace the base ones and are inserted below the grid.

Our controller, being a delegate of MKMapView, responds to the -mapView:rendererForOverlay:. This is required by every application that uses overlays as this is the point where the app effectively tells the system how to draw an overlay that is currently visible in the map. In our case we just check that the overlay is a tile overlay (this is a general case to consider that fact that we might have other types of overlays) and based on the selection we use the standard MKTileOverlayRenderer or a custom renderer WatermarkTileOverlayRenderer. The latter is used to apply a randomly colored semi-transparent effect on top of the tiles, getting as a result a vitreous mosaic effect.

Conclusions

The possibility to easily switch between different map types but keeping the same “map navigation experience” is one of the most revolutionary features introduced with iOS 7, other than the longly awaited introduction of native maps inside OSX. This provides the same map infrastructure whatever is the content. Obviously the generation of custom map content is another huge and highly specialized task that we cannot cover here: but for developers this is a great step forward.

References

  • Location and Maps Programming Guide from Apple Developer Library
  • WWDC 2013 session 304 video: What’s new in Map Kit from Apple WWDC 2013 videos
  • MBXMapKit GitHub project by Mapbox – A simple library to intergrate Mapbox maps on top of MapKit, one of the first applications of tiled overlays

“viggiosoft github”

  • The GDAL project one of the main references for custom maps creation. Here is a link to a compiled version of the GDAL OSX Framework
  • Maperitive another great tool (for Windows only) to create custom maps and prepare them for offline usage
Posted by Carlo Vgiani

Sony Playstation App per Iphone

Nel suo Blog la Sony ha appena annunciato che sta preparando a lanciare una applicazione ufficiale PlayStation per iPhone e iPod touch (link: http://blog.eu.playstation.com/2010/12/16/the-official-playstation-app-coming-soon-to-iphone-and-android-handsets/).

L’applicazione sarà gratuita e sarà disponibile solo nel Regno Unito, Francia, Germania, Spagna, Italia e Olanda nella sua prima release.

L’applicazione offrirà le seguenti caratteristiche:

– Controllare il proprio PlayStation Network trophies e tenersi aggiornati sui giochi e lo stato dei propri contatti online.
– Scoprire gli ultimi giochi, news e hardware per PlayStation 3, PSP e PlayStation 2.
– Leggere tutti gli annunci su PlayStation.Blog europeo.
– Condividere i propri prodotti preferiti o notizie con i tuoi amici su Facebook, Twitter o via e-mail.

L’applicazione richiede iOS 4 o superiore. Una versione per il sistema operativo Android sarà rilasciata. L’ingresso di Sony nel mercato App iOS è interessante data la crescente concorrenza tra Sony e Apple nel mobile gaming.

Dopo aver venduto 2 milioni di iPad in 59 giorni, Apple mette a segno un nuovo record: 3 milioni di iPad venduti in 80 giorni.
Apple ha indovinato il prodotto e molti hanno preferito il Tablet di Steve Jobs al notebook o a i mini portatili , da questa’anno il mercato Pc vedrà dominare Apple in capacità di percezione di prodotto di qualità.

Apple lancerà ad agosto iPad in altri nove paesi.

App Store: ad oggi presenti piu’ di 11 mila le Web apps native per iPad oltre a tutte quelle per iPhone perfettamente compatibili.

A caldo le novita’ del nuovo iPhone OS 4.0, in distribuzione da subito agli sviluppatori e disponibile al pubblico dalla prossima estate.

0. Novita’ tecnica per far capire lo sforzo compiuto: oltre 1500 nuove API per gli sviluppatori e 100 nuove funzioni per gli utenti.

E adesso passiamo alle 7 grandi novita’.

1. multitasking “controllato”, per preservare il consumo della batteria; sara’ possibile:
a) ascoltare web radio in background
b) mantenere Skype online e quindi continuare a ricevere messaggi, chiamate ecc.
c) permettere al TomTom di monitorare la nostra posizione,
d) servizi di localizzazione potenziati: adesso le app come TomTom potranno continuare a tener traccia dei nostri spostamenti anche se non sono attive
e) push notifications: saranno possibili notifiche locali
f) task completion: certi app caratterizzate da tempi lunghi di processamento (esempio Flickr quando fa l’upload di foto) riceveranno la notifica di fine task
g) app fast-switching: passare da un’app all’altra velocemente tenendole “dormienti” nel sottofondo, senza consumare risorse di CPU

2. Folders: finalmente potrete organizzare le icone delle vostre numerosissime app installate in cartelle!

3. Mail potenziata: 1 inbox per tanti account, inoltre le mail potranno essere raggruppate per soggetto ed infine sara’ possibile aprire un attachment con qualunque app.

4. iBooks per iPhone: la versione “piccolo schermo” del lettore di libri gia’ visto per iPad (e il libro su Winnie the Pooh sara’ gratis, saranno contenti i miei bambini)

5. Aziende: un po’ di novita’ che faranno piacere alle aziende: cifratura dei dati (e-mail), gestione unificata dei telefoni aziendali (purche’ siano iPhone… ovviamente), distribuzione wireless delle app aziendali, exchange 2010, VPN

6. Giochi: si introduce il “Game Center”, definito come una rete di “social gaming”, uno spazio di aggregazione degli iPhone-giocatori.

7. iAd: pubblicita’ mobile direttamente sull’iPhone. Apple aiuta gli sviluppatori a far soldi (… grazie …) e gli utenti ad avere tante buone app gratuite; Jobs ha calcolato che attualmente si ha la possibilita’ di oltre 1 miliardo di inserzioni al giorno che appariranno sugli schermi degli utilizzatori di iPhone! il 60% delle vendite di pubblicita’ andranno agli sviluppatori; e’ inutile dire che cliccando sull’ad non si esce dall’applicazione! Inoltre queste pubblicita’ sono davvero belle e anche interattive. Parole testuali di Jobs: “Have you ever seen an ad like this?” (silenzio in aula) “Anything even close?”

Gli iPhone 3GS e iPod Touch next-gen supporteranno del tutto questa nuova versione del sistema operativo. iPhone 3G e iPod Touch 2nd-gen solo alcune, e purtroppo il multitasking non sara’ tra queste…

5 minuti di pausa…