An important feature in the mobile area are maps capabilities. There isn't any app in the market that won't improve their value without a map. Consider a running-app without a map - useless. Navigation with your mobile-phone without a map - nonsense.

Apple and Google doing a great job with there Map-SDKs, but sometimes you hit a spot where they won't suit your needs. Maybe for legal reasons or because you hit a bug inside of them. The later was my case, which is happily resoleved with iOS 10. But I needed an alternative that works offline too.

What is the OpenStreetMap-project?

The OpenStreetMap-project aims to provide geo-information like streets, rivers or houses. You can add these informations as well, if you like. OpenStreetMap set appart from Google or Apple that you have access to the raw data too. Great if you like to create your own maps or routing information. Visit www.openstreetmap.org to learn more about this awesome project.

Presenting an OpenStreetMap

To extend your Xamarin.iOS app with maps from the OpenStreetMap-project you need to download and reference the latest release from OSMSharp. After that create a new Xamarin.iOS single view app and add a reference to all files included in the zip-file. The current NuGet-packages won't work that is why you reference the assemblies.

Some background-information upfront. Most if not all Map-SDKs are layer-based. Each layer will be used for different types of information. For example if you want to add a marker you want it at the highest layer to be visible at all time. City and street names should be above the surface layer too.

The second thing is, that such SDKs work with a grid-system. Each grid will be filled with a tile, a small peace of the whole map, which is an image.

Before a map can be presented we need to get a tile-provider. OpenStreetMap provides a small list of tile-servers at their website. Also, worth a view http://wiki.openstreetmap.org/wiki/Tileserver.

With the tile-server a map will be initialized within a few lines of code in ViewDidLoad.

public override void ViewDidLoad()
{
    base.ViewDidLoad();

    try
    {
        Native.Initialize();

        var _mapView = new MapView(View.Frame);
        _mapView.MapCenter = new GeoCoordinate(52.207767, 8.803513);
        _mapView.MapZoom = 12;
        _mapView.Map.AddLayerTile("http://a.tile.openstreetmap.de/tiles/osmde/{0}/{1}/{2}.png");

        View.AddSubview(_mapView);
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex);
    }
}

The image on the left is the map with the default OpenStreetMap-style. By using another tile-server as the source of our map, we got a more appealing style for the map on the right side.

Display Markers with OSMSharp

We centered the map at the Cayas Software headquarter. Now, with a working map, it is time to add a marker the make it more recognizable. To do so we need two information:

  • The position where we want to place the marker
  • An image to represent the marker.

With that the rest is a small peace of code:

var marker = new MapMarker(new GeoCoordinate(52.207767, 8.803513), MapControlAlignmentType.CenterBottom, UIImage.FromFile("pin.png"));
_mapView.AddMarker(marker);

In this example the marker is not clickable, to present further information, to keep it simple. Take a look at the AddPopup method if you wish to add one.

Add offline-functionality

One of the main reasons I played around with OSMSharp and OpenStreetMap are the capability to use maps even in offline-scenarios. With OSMSharp you have multiple solutions available. Each of them has their own pro and cons. The focus here is on MBTiles as data source but osm.pbf files can be used similar.

MBTiles are basically SQLite databases while osm.pbf-files uses Google Protobuf to store the map information. Depending on the level of information - streets, cities, rivers - and size of the map - whole world, single country or a region - the file size can become really big.

In this article I am going to add my mbtiles-file as a BundleResource. That way it is already included in the app and I don't need to write the code to download it somewhere. In a real-world scenario you might going another route but the way of loading the file will be the same: Get a stream and tell the MBTile-layer the name of the database.

using (var mapStream = File.OpenRead("./demo_layers.mbtiles"))
{
    _mapView.Map.AddLayer(new LayerMBTile(SQLiteConnection.CreateFrom(mapStream, "map")));
}

To see a difference between my online-map and the offline-map I choose a different style. I also set a higher zoom level to save some space in the MBTiles file. Our result looks now like the following:

You can find the demo project and all files on bitbucket. If you like the article or have any questions write a comment.

comments powered by Disqus