fredag 28 december 2012

Using maps offline in Android and iOS part 1

Introduction

In some apps offline maps are needed in order to use the app without internet connectivity. The Google Maps API for Android and the MapKit API for iOS currently doesn't support offline maps but there are other options.

Instead of using maps from Google or Apple it's possible to use map data from the Open Street Map (OSM) project. The OSM data is collected by people around the world and is free for both non-commercial and commercial use.

Rendering the tiles

Before the OSM data can be used as a map it needs to be rendered into tiles. One tile is usually a 250x250 pixels PNG image and a map consists of several hundreds or thousands tiles. Rendering tiles requires a lot of resources, which is why the tiles available at the Open Street Map website are not free to download.

There are some companies such as MapBox that provides an API to download rendered OSM tiles for a few dollars every month. But if the tiles are going to be bundled with the app and used offline you can render the tiles your self using Open Source Software.

If you are running Windows or OS X you can install Ubuntu in a VirtualBox and follow the instructions at the switch2osm website to setup your own tile server. The tile server will render tiles using the default OSM Mapnik stylesheet using raw OSM data that you provide. When running the tile server in a virtual machine it's a good idea to use a OSM extract from Geofabrik to reduce the rendering time.

Using the tiles

When the tile server is up and running you should be able to access it from a web browser. If the tile server is running in a virtual machine make sure you use a bridged network configuration.

To use the map offline in an app all the tiles could be downloaded from the tile server and stored in the app as images. However, accessing thousands of images in the file system when browsing the map is not great for performance. A better solution is to use a SQLite database to store the tiles in a single file according to the MBTiles specification.

MBTiles can be created using the Mobile Atlas Creator which supports different map sources. By adding your own tile server as a custom map source it can be used to browse your map. Select a region in the map, the number of zoom levels and then export the atlas using the MBTiles format. If the exported file is to large modify the region or number of zoom levels and try again.

Summary

Using the Google Maps API or MapKit API is simple and recommended for most apps. If your app requires offline support more work is required.

In this post we have focused on the steps required to render tiles and save them as a map in the MBTiles format. The map can then be used by third-party libraries for both Android and iOS which will be covered in upcoming posts.

onsdag 7 november 2012

Introducing DroidNetworking - A network library for Android

Most Android apps need to use HTTP to send and receive data. There are many options for network communication in Android. Google recommends using the Apache Http Client for Eclair (2.1) and Froyo (2.2). For Gingerbread (2.3) and newer the HttpURLConnection is recommended. However response caching was not introduced in the HttpURLConnection until Ice Cream Sandwich (4.0) was released.

DroidNetworking is a network library built on top of the Apache Http Client. It has support for response caching, authentication, HTTP and HTTPS and many other features. Best of all, DroidNetworking can be used on Eclair (2.1) and newer. It has a simple API which reduces the amount of code needed for network communication.

Basic example

DroidNetworking supports both synchronous and asynchronous requests. The example below shows a simple asynchronous GET request that will be executed in a background thread.
NetworkEngine.getInstance().init(this);

NetworkOperation operation = 
    NetworkEngine.getInstance().createOperationWithURLString("http://www.google.com");

operation.setListener(new OperationListener() {
  @Override
  public void onCompletion(NetworkOperation operation) {
    String response = operation.getResponseString();
    int status = operation.getHttpStatusCode();
  }

  @Override
  public void onError(NetworkOperation operation) {          
  }
});

NetworkEngine.getInstance().enqueueOperation(operation);

Response data

There are three different ways to get the response data from a completed operation. The first way is to get the response as a string, recommended for small responses:
String response = operation.getResponseString();
The second way is to get the response data as a byte array, which is especially useful for binary data such as images:
byte[] responseData = operation.getResponseData();
The third and final way to get the response data is to use the input stream directly:
operation.setParser(new ResponseParser() {
  @Override
  public void parse(InputStream is) throws IOException {
  }
});
Using the input stream directly in combination with a JSON stream parser will improve performance and reduce memory consumption, which is important when working with large responses. The parser must be set before executing the request.

Response cache

DroidNetworking includes a HTTP response cache with support for both memory and disk cache. By default the cache is disabled but it can be enabled by calling:
NetworkEngine.getInstance().setUseCache(true);
The default memory cache size is 2 MB and the default disk cache size is 10 MB. These sizes can be customized by calling:
NetworkEngine.getInstance().setMemoryCacheSize(1024*1024);
NetworkEngine.getInstance().setDiskCacheSize(0);
In the example above the disk cache size is set to 0 which disables the disk cache completely. Please note that the above methods must be called before init().

Authentication

Currently only Basic Authentication is supported. The username and password is set on each operation where authentication is required:
operation.setBasicAuthenticationHeader("username", "password");

HTTP methods

Both GET, POST, PUT, DELETE and HEAD requests are supported by DroidNetworking. The example below shows how to create a POST request:
HashMap<String, String> params = new HashMap<String, String>();
params.put("message[title]", "Hello");
params.put("message[body]", "DroidNetworking");
        
NetworkOperation operation = NetworkEngine.getInstance()
    .createOperationWithURLString("http://www.google.com", params, HttpMethod.POST);

Download the library

DroidNetworking can be used as a library project or you can add it as a jar-file to your project. Get the source code at: https://github.com/sogeti/DroidNetworking or the jar at https://s3.amazonaws.com/droid-networking/droid-networking-20140312.jar.