Documentation for iOS

Getting started - indoor navigation for iOS

Setup Demo Project

If you haven’t already, install CocoaPods: https://guides.cocoapods.org/using/getting-started.html Then in your terminal, run pod try MapsIndoors This will install and fire up the full-featured SDK Demo Project. Remember to add your own key Google Maps iOS API key in AppDelegate.m: [GMSServices provideAPIKey:@"add-your-own-api-key"]; For directions to work in the demo app, you need to provide a Google Directions API Key also. Also add this in AppDelegate.m. If you are not familiar with using Google APIs opening the link below will guide you through the process of enabling APIs and generating keys. https://console.developers.google.com

Setup Steps

To setup your project with MapsIndoors using xcode, follow this setup guide:
  1. Follow the instructions at https://developers.google.com/maps/documentation/ios-sdk/start.
  2. Add pod ‘MapsIndoors’ to your pod file.
  3. Run pod install with your terminal at project root.
  4. Add #import <MapsIndoors/MapsIndoors.h> in your view controller displaying a Google map. (Please Note: Starting from version 1.6.31, due to issues with headers resolving in Cocoapods / Xcode, import statement has been changed from #import <MapsIndoorsSDK/MapsIndoorsSDK.h> to #import <MapsIndoors/MapsIndoors.h> or @import MapsIndoors;)
 

Using MPMapControl

Use the MPMapControl convenience class for setting up a Google map with MapsPeople venues, buildings, locations and routing. Place the following code in the viewDidLoad method in your view controller displaying a Google map. // Place the map above the demo-building GMSCameraPosition* camera = [GMSCameraPosition cameraWithLatitude:57.08585 longitude:9.95751 zoom:17]; // Initialise the Google map GMSMapView* myGMSMapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; // Use your solution and network ids, // "550c26a864617400a40f0000" will fire up a solution containing a demo venue, // in this case use this starting coordinate: 57.08585,9.95751 NSString* clientId = @@"add-your-own-mapsindoors-solution-id"; NSString* siteId = @@"add-your-route-network-identifier"; //Typically a human-readable venue name or abbreviation - "rtx" may be used for a demo MPMapControl* myMapControl = [[MPMapControl alloc] initWithMap: myGMSMapView]; [myMapControl setupMapWith: clientId site: siteId];

Using Display Rules

When working with MapsIndoors locations, you need to set display rules that defines when, where and how to show a location.

To add display rules, you need to know the categories in your location dataset. The display rule name corresponds to the location category we want the rule to apply for.

// Adding a rule with name nil, will apply generally to all categories
[myMapControl addDisplayRule:[[MPLocationDisplayRule alloc] initWithName:nil AndIcon:[UIImage imageNamed:@"poi"] AndZoomLevelOn:19 AndShowLabel:YES]]; // Adding a rule with a name, will override a more general rule [myMapControl addDisplayRule:[[MPLocationDisplayRule alloc] initWithName:@"info" AndIcon:[UIImage imageNamed:@"info"] AndZoomLevelOn:17.0]]; [myMapControl addDisplayRule:[[MPLocationDisplayRule alloc] initWithName:@"parking" AndIcon:[UIImage imageNamed:@"parking"] AndZoomLevelOn:17.0]]; [myMapControl addDisplayRule:[[MPLocationDisplayRule alloc] initWithName:@"staircase" AndIcon:[UIImage imageNamed:@"staircase"] AndZoomLevelOn:18.0]];

Using Custom Providers

It is possible to setup MapsIndoors with locations, venues or routing from your own backend. E.g. to use your own locations backend, let your custom provider conform to the MPLocationsProvider protocol (or make a subclass of the interface of same name): @interface MyCustomLocationsProvider : NSObject<MPLocationsProvider> @property (retain) id <MPLocationsProviderDelegate> delegate; - (void)getLocationsAsync: (NSString*) arg language: (NSString*) language; - (void)getLocationsUsingQueryAsync: (MPLocationQuery*) locationQuery language: (NSString*) language; - (void)getLocationDetailsAsync: (NSString*) arg withId:(NSString*)locationId language: (NSString*) language; @end  

In cunjunction with your setup code, make MPMapControl use your provider.

MyCustomLocationsProvider* myProvider = [[MyCustomLocationsProvider alloc] init]; NSString* clientId = @@"f72911b4-7c90-478a-b533-fb2077ff02e4"; NSString* siteId = @@"rtx"; MPMapControl* myMapControl = [[MPMapControl alloc] initWithMap: myGMSMapView]; [myMapControl setupMapWith: clientId site: siteId locations: myProvider venues: nil routing: nil appData: nil];

Getting a marker from a location and vice versa

If a location is currently displayed on a map, the marker can be retrieved using location.marker. On the opposite, get a location from a marker using getLocation:marker:

[myMapControl getLocation:marker];

Handling marker selections and other user events

Detecting the user tapping a marker or an infowindow is part of the Google Maps SDK for iOS. Assign myGMSMapView.delegate and implementing the methods: mapView:didTapMarker:marker and mapView:didTapInfoWindowOfMarker:marker

By default, if the user taps on a marker, an infosnippet will appear in the bottom of the map. Subscribe for the info-snippet events using MPMapDelegate:

- (void)viewDidLoad { [super viewDidLoad]; ... myMapControl.delegate = self; ... } - (void)infoSnippetTapped:(MPLocation *)location tapPosition:(NSString *)position { self.currentLocation = location; if ([position isEqualToString: kTapPositionRIGHT]) // Tapped on right side of info snippet ... else if ([position isEqualToString:kTapPositionCENTER]) { // If tapped on the location name ... } }

Using Routing

Routing is requested with at least an origin, a destination and a transit mode – transit mode corresponds directly to Google Maps transit modes: Walking, bicycling, driving and transit (public transportation).

The MPDirectionsService class will handle routing and its delegate will receive the resulting MPRoute object. First set up the service:

MPLocation *origin = [[MPLocation alloc] initWithPoint:[[MPPoint alloc] initWithLat:57.0853523 lon:9.957473] andName:@"My location"]; MPLocation *destination = [[MPLocation alloc] initWithPoint:[[MPPoint alloc] initWithLat:57.0853743 lon:9.957698] andName:@"Destination"]; MPDirectionsService* directionsService = [[MPDirectionsService alloc] initWithMapsIndoorsSolutionId: @"mapsindoors-solution-id" googleApiKey: @"google-api-key"]; directionsService.delegate = self; [directionsService routingFrom: origin to: destination by: @"WALKING" avoid: nil depart: nil arrive: nil];


Then make sure the delegate object, typically one of UIViewController, conforms to MPRoutingProviderDelegate:

@interface ViewController : UIViewController<MPRoutingProviderDelegate>


Hence, by implementing the following method:

- (void)onRouteResultReady:(MPRoute *)route { }


The transit mode is always WALKING when requesting only indoor routes – regardless of what you assign it to.

Route restrictions

Add a restriction to the route by setting the avoid argument. Avoid stairs on the route using the following restriction parameter:

NSArray* restrictions = [NSArray arrayWithObject:@"stairs"]; [directionsService routingFrom:origin to:destination by:@"WALKING" avoid:restrictions depart:nil arrive:nil];


Public transit mode

Using TRANSIT mode requires either a departure date or an arrival date.

NSArray* restrictions = [NSArray arrayWithObject:@"stairs"]; [directionsService routingFrom:origin to:destination by:@"WALKING" avoid:restrictions depart:nil arrive:nil];


Route rendering

Once a route result is received, you might want to render the route on a map. The MPDirectionsRenderer will handle this for you. This example will setup a renderer and do some initial but optional settings.

MPDirectionsRenderer* directionsRenderer = [[MPDirectionsRenderer alloc] init]; directionsRenderer.delegate = self; directionsRenderer.map = myGMSMapView; directionsRenderer.fitBounds = NO; //Defaults to YES directionsRenderer.solidColor = [UIColor blueColor]; directionsRenderer.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.5];


So in your onRouteResultReady delegate method, set the route property to trigger the renderer:

- (void)onRouteResultReady:(MPRoute *)route { directionsRenderer.route = route; }


Clear the rendering by setting either map or route to nil:

directionsRenderer.route = nil; // or directionsRenderer.map = nil;


The renderer will split the route in logical parts, called route legs, based on information on context such as indoor/outdoor, access levels etc., but also floor level and transportation type. By default, the first route leg will be shown. Here are examples of rendering other parts of the route.

directionsRenderer.routeLegIndex = 1 // Render second route leg, if exists directionsRenderer.routeLegIndex += 1 // Render next route leg, if exists directionsRenderer.routeLegIndex -= 1 // Render previous route leg, if exists


Given an instance of MPRoute, you can get the number of legs by reading the length of its legs property:

int numberOfLegs = route.legs.count;


The rendering can be animated using the animate method just after invoking a rendering.

directionsRenderer.routeLegIndex = 1; [directionsRenderer animate:4]; // Animate drawing of route line with 4 seconds duration


If the rendering of a route leg happens on a new floor level, you might want to get notified, so you can update the current visible floor plan (mapControl.currentFloor). For this purpose you can implement the MPDirectionsRendererDelegate method:

- (void)floorDidChange:(NSNumber *)floor { mapControl.currentFloor = floor; }


The route renderer will render a route line and two buttons. In fact they become markers on the GMSMapWiew, but for your convenience we treat them as buttons. In this way you can add target selectors for them:

[directionsRenderer.nextRouteLegButton addTarget:self action:@selector(showNextRouteLeg) forControlEvents:UIControlEventTouchUpInside]; [directionsRenderer.previousRouteLegButton addTarget:self action:@selector(showPreviousRouteLeg) forControlEvents:UIControlEventTouchUpInside];

Working with mapped POI’s / Locations

This example will show you the possible ways of querying the POI’s on your venue(s). You need to provide your solution id as a mandatory property to the query.

MPLocationsProvider* locationsProvider = [[MPLocationsProvider alloc] init]; locationsProvider.delegate = self; // MPLocationsProviderDelegate will need to implement onLocationsReady and onLocationDetailsReady MPLocationQuery* q = [[MPLocationQuery alloc] init]; q.solutionId = @"my-mapsindoors-solutionId"; // MapsIndoors solution ID / client ID q.query = @"lo"; // Searches in room ID, name and aliases q.max = 10; q.orderBy = @"name"; // Possible values are // name, relevance, roomId, floor, building, venue // Sorting will override "near/radius" q.sortOrder = @"desc"; q.near = [[MPPoint alloc] initWithLat: 55.6377969 lng:12.5787581]; q.radius = 60 //get nearest locations within 60 metres q.max = 1 //get the nearest (a list of one location). Note: To be sure to get at least 1 result, don't set radius [locationsProvider getLocationsUsingQueryAsync:q language:@"en"]; // or single query [locationsProvider getLocationDetailsAsync: @"my-mapsindoors-solutionId" withId: @"mapsindoors-location-id" language:"en"]; // mapsindoors-location-id can be retrieved from ((MPLocation*)location).locationId


To show a single POI on a map you can use this method:

mapControl.selectedLocation = (MPLocation*)location;


Implement MPLocationsProviderDelegate and its onLocationsReady method in order to receive the locations result:

- (void)onLocationsReady:(MPLocationDataset *)locationData { }