Asset Location Tracking

GoIndoor SDK

Welcome to GoIndoor SDK which allows real-time beacon based indoor locationing and more. This SDK will help you build smart applications without the need of understanding complex algorithms of indoor locationing. This guide will help you integrate GoIndoor technology with your application easily.

Introduction
GoIndoor SDK is a set of tools for building precise location services for indoor places. You can achieve an accuracy of upto 2 meters with our SDK. Our SDK makes use of the signals from the beacons to calculate your position. It is implemented as a wrapper to the native GPS location services available in the devices. The SDK will provide indoor location if beacons are available, otherwise it will change to GPS. The developer can thus rely on single set of APIs in our SDK for indoor/outdoor geolocation.

Here is a list of features which can be implemented easily with our SDK:

  • Accurate Indoor positioning
  • Routing
  • Floor Transitions
  • Asset Management (beacon management)
  • Event generation / Notifications

We offer native SDKs for both Android and iOS devices. GoIndoor Cordova plugin is also available but it is in beta state and offers limited features.

Getting Started

Prerequisites

  • A GoIndoor account
  • BLE enabled android or iOS device
  • For Android devices, Android version 4.3 (API level 18) or later is required
  • For iOS devices, iOS 7.0 or later is required
Sample Application

If you want to see a running application before writing your own, download our sample app ‘Indoor Navigator’ and get started. It demonstrates features like user location, routing, notifications, indoor maps and more.

    

You can also download the code, build and run it locally. Download the iOS sample from here and the android sample is available here.

Getting Started with Android SDK

Following sections show how to implement basic locationing related functionalities in android applications. For an extensive list of classes and methods exposed, refer this.

SDK Installation

Include the SDK in android studio using maven.

The GoIndoor library can be downloaded from the jcenter and maven central repositories. In Android Studio, if you wish to add GoIndoor library to your application, add the following line of dependency in module's build.gradle file. Android Studio then automatically downloads this library from Maven Repository Server defined in build.gradle.

dependencies {
    compile 'com.oym.indoor:goindoor:x.y.z'
}

It might be required to add the following lines inside the android closure.

packagingOptions {
    exclude 'META-INF/ASL2.0'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/NOTICE'
}

Include the SDK in android studio manually

  1. First of all, download the latest version of Goindoor-x.y.z.aar file from here.
  2. Next step is to import .aar file. Add the .aar library to the libs directory of the module (it is located in the same level as the java and res folders, create the folder manually if it does not already exist).
  3. Add the following line at the end of the build.gradle file for your module
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
  4. Add the following dependency in gradle
    dependencies {
        compile(name:'goindoor-x.y.z', ext:'aar')
    }

GoIndoor SDK is available to the project.

Create a New Application

Steps to create a new application

  1. Start by creating a new Android Studio project. Name it appropriately.
  2. Set “Minimum SDK" to “API 18:Android 4.3 (Jelly Bean)".
  3. Choose “Blank Activity" to begin with.
  4. Add SDK dependency as shown in the previous section.
  5. Make the following changes in AndroidManifest.xml.
    Set the “Minimum SDK” for the application.
    GoIndoor SDK Code
    To be able to use Bluetooth and Google Location Services, add the following permissions.
    GoIndoor & Indoor SDK Code
    To allow the application to use Bluetooth Low Energy (Bluetooth 4.0) feature, include the following line.
    GoIndoor SDK Module Code
    Finally we need to define the Indoor Location Service to be used, which can be defined as follows.
    GoIndoor Location Service Code
  6. Ensure that Bluetooth and WiFi/Network connection features are available in the device.
Initialization and Synchronization

Initialization

To use the GoIndoor services in your application, include the following in your applications's AndroidManifest.xml

GoIndoor Android Location Service Code

The first step to access GoIndoor's features is to create a GoIndoor object. Use the build() method of GoIndoor.Builder class to create the object. In the backend, it will try to connect to the server and synchronize with it. The connection status with the server will be notified through the ConnectCallback object, provided as an argument to the build() method.

/* Create a GoIndoor object . */
go = new GoIndoor.Builder()
.setContext(context)
.setAccount(account)
.setPassword(password)
.setConnectCallback(callbackConnect )
.build();

Where,

  • Context– GoIndoor server URL
  • Account – GoIndoor account name
  • Password – GoIndoor account password
  • CallbackConnect – ConnectCallback object to handle the connection process.

It is mandatory to set the account, password and callback parameters in the builder class. If context is not provided, the default one will be used.

There are few other optional parameters which can be set in the builder class, if needed. The GoIndoor.Builder class exposes various setter methods for all the parameters.

setAccount(String account) – Sets the account name.
setConnectCallback(ConnectCallback connectCallback) – Sets the callback to handle the connection process.
setContext(Context context) – Sets the indoor backend URL.
setDatabaseUpdate(long refresh) – Sets the database update rate in msec.
setDebug(boolean debug) – Sets the debug mode.
setLocationType(int type) – Sets the positioning type.
setLocationUpdate(long refresh) – Sets the location update rate in msec.
setPassword(String password) – Sets the password.
setProfile(String type) – Sets the profile to be used.
setUpdatePolicy(int policy) – Sets the update policy.
setUrl(String url) – Sets the app context.

A sample ConnectCallback object is shown below.

private ConnectCallback callbackConnect = new ConnectCallback() {
@Override
//when the connection to the server has been correctly established and the library is initialized
public void onConnected() {
    Log.i(TAG, "Connected");
}
@Override
//when an exception is thrown when trying to connect to the server
public void onConnectFailure() {
    Log.i(TAG, "Connection Failed");
}

Synchronization
When an application first connects to the server, it automatically downloads information about beacons, buildings, floors, POIs, notifications and assets to the device. This not only makes the application more efficient but also provides it offline capabilities. The SDK continues to synchronize with the server in the background at regular intervals to fetch updates, if any available.

Location

In order to start the location services , it is required to provide a LocationBroadcast to the library. This will be called each time a position is computed or a new notification is triggered by the SDK.

private LocationBroadcast broadcast = new LocationBroadcast() {
    @Override
public void onLocation(LocationResult location) {
//Process location updates
}
    @Override
public void onNotification(NotificationResult notification) {
// Process notifications
}
};

The LocationResult object has the following information about the new location.

public final double longitude; /* WGS84 Longitude */
public final double latitude; /* WGS84 Latitude */
public final int used; /* Number of beacons used */
public final double accuracy; /* Position accuracy (meters) */
public final ArrayList found; /* List including the longitude, latitude and accuracy of each beacon in sight */
public final String floor; /* Floor ID */
public final int floorNumber; /* Floor number */
public final int type; /* Positioning type: TYPE_BEACON or TYPE_FUSED} */
public final String buildingName; /* Building name */
public final String building; /* Building ID */
public final int geofences; /* Number of geofences */

When ConnectCallback.onConnected() is invoked, it indicates a successful connection with the server. GoIndoor location service should now be started to start receiving location updates. After starting the service, every time a new position is computed, LocationBroadcast.onLocation() is invoked and every time a notification is triggered by the SDK, LocationBroadcast.onNotification() method is invoked.

go.startLocate(br);

When you no longer wish to receive location updates, stop the location updates using this method.

go.stopLocate();

Before exiting the app, close the session with the server properly using the following call.

go.disconnect();

The location updates can be used in different ways in the code – to mark the location on the map, find distance from a particular place or to save the coordinates for analysis. Following code snippet shows how to use the location update to mark the location on a map. Go through the 'Show Indoor Maps' section to learn how to overlay your indoor map over Google Maps.

Mark user's current position on a map:

@Override
public void onLocation(LocationResult location) {
    // Show indoor tiles
    if(location.type == LocationResult.TYPE_BEACON && (floorNumber != location.floorNumber || tileOverlay == null)) {
        Building building = go.getBuildings(location.building);
        if (tileOverlay != null) {
            tileOverlay.remove();
        }
        currentFloor = building.getFloors().get(floornumber).getFloorNumber();
        tileOverlay = map.addTileOverlay(new TileOverlayOptions().tileProvider(building.getFloors().get(floorNumber) .getGMTileProvider()).zIndex(1));
        }
    // Show marker
    LatLng point = new LatLng(location.latitude, location.longitude);
    if (markerPosition == null) {
        markerPosition = map.addMarker(new MarkerOptions().position(point));
    } else {
        markerPosition.setPosition(point);
    }
}

There are three types that you can set for the location updates that you wish to receive. These differ in the algorithms which are used to derive the location. Use the following method to set the type.

go.setLocationType(type);

In the above method, type can take the following values:

LOCATION_TYPE_AVERAGE – In this case, signals from multiple beacons are used to triangulate the location.
LOCATION_TYPE_CLOSEST – In this case, the location is calculated by using only the signals of the beacon which is closest to the user.
LOCATION_TYPE_PROJECT – In this case, weighted average of signals from multiple beacons (as in the case of LOCATION_TYPE_AVERAGE) is used to determine the location which is then projected to the closest edge.

Notifications

A notification is a kind of event that happens when a specific action happens with respect of a Point of Interest (POI).

Different notifications can be configured for a POI using the Admin Panel. SDK generates notifications when those specific events occur and it is the responsibility of the application to handle them appropriately.

Please note that the notifications generated by SDK are not push notifications on the phone. The LocationBroadcast object exposes onNotification() method which is invoked every time a notification is generated. This method can be used in different ways to handle notifications. The notifications can also be filtered further based on user profiles.

There are four types of notifications which can be associated to any POI :

  1. Enter – This notification is generated when the device enters the POI.
  2. Stay – This notification is generated when the device has been inside the POI for a specified time period.
  3. Leave – This notification is generated after the device has left the POI.
  4. Nearby – This notification is generated when the device goes near a POI.

Simple onNotification method usage:

private LocationBroadcast indoorLocation = new LocationBroadcast() {
    @Override
    public void onLocation(LocationResult location) {
    }
    @Override
    public void onNotification(NotificationResult nr) {
        switch (nr.notification.action) {
        case ENTER:
            showMessage("Welcome to "+ nr.place.getName());
            break;
        case STAY:
            showMessage("Would you like to know more about "+ nr.place.getName());
            break;
        case LEAVE:
            showMessage("Thank you for visiting" + nr.place.getName());
            break;
        case NEARBY:
            showMessage("We are waiting for you at" + nr.place.getName());
            break;
        default:
            return;
        }
    }
};

Trigger an action on a specific notification:
The following example shows displays a message to the user if his current location is close to exit (POI in this case) and the user is about to leave the place.

@Override
public void onNotification(NotificationResult nr) {
    // Check if exit tag in POI && notification action is of type NEARBY
    if (nr.place.getTags().contains("exit") && nr.notification.action == Notification.NotificationAction.NEARBY) {
        showPopupWithText("Thanks for your visit");
    }
}

You can also associate any number of properties with every notification. These properties can be used by the developers in any way in the code.

Filter notifications based on user profiles:
Notifications can also be sent to specific users based on their profiles. For this, while configuring a notification from the Admin Panel, add a target to it. Target can be set based on user profile like age, gender or any other property. Update the user profile on the server. See the related sample here. The SDK will then automatically trigger notifications based on the profile. For example, if a notification is to be sent to adults above 21 years of age only, then update the user's age on the server through the application and while creating a notification on Admin Panel, add age property as a filter to it. The SDK will now send that notification only to users which fulfill the age criterion.

Navigation

Navigation is the process of finding a route from one point to another. Our SDK supports only indoor navigation. It provides the optimal shortest path for the user to navigate from the starting point to the desired destination. The navigation API returns the routing points or the roadmap to be traversed along with the estimated time it will take to reach that place and distance to be covered.

Please note that the route/navigation path is returned as a list of instructions which can then be used by the developers to display in their application as needed.

How to use Navigation API ?

Use the computeRoute API from GoIndoor class to compute the navigation route. It takes two RoutePoint objects to specify the two points between which the route is to be computed. This method returns a route object. The route object has the following information.

  • Distance – route distance in meters
  • Time – route time in seconds
  • Instructions – List of instructions to be followed to reach the destination
  • Route – List of RoutePoint containing the route.
  • arePropsFulfilled – flag to indicate whether route could be computed or not.

Following code shows how to compute the Route between two points, assuming that the source is the current user location identified with variable 'location' and destination is a POI identified by variable 'poi'.

RoutePoint start = new RoutePoint(location.latitude, location.longitude, location.building, location.floorNumber);
RoutePoint end = new RoutePoint(poi.getLatitude(), poi.getLongitude(), poi.getBuilding(), poi.getFloorNumber());
Route route;
try {
    route = go.computeRoute(start, end);
} catch (Exception exc) {
    // Handle exceptions properly - wrong source/destination points, no edges in the building
}

Route returns the optimum shortest path between the two points. This information can be now used to get instructions for navigation on the route. Instructions are messages to be shown to the user for navigation like Turn left, Go straight and so on.

To get the user's position projected on the route, create an object of RouteProjectedPoint class. Use projectedPoint() method of the RouteResult object to get this point. RouteProjectedPoint has the following information:

bearing - Orientation of the route segment in which the user is, counted clockwise from north
distanceFromStart - Distance in meters from the starting point
distanceToShape - Distance between the user position and the shape

The following code shows how to get the projected point and the next instruction from the route.

@Override
public void onLocation(LocationResult location) {
if (isNavigationMode) {
    // Get projected point
    RoutingResult rr = route.getProjection(location);
    RouteProjectedPoint rpp = rr.projectedPoint;
    // Get next instruction to show
    Instruction instruction = route.getRouteInstruction(location);
    int nextInstructionDistance = (int) (instruction.distance - rpp.distanceFromStart);
    // Get localized instruction and image
    int stringResource = InstructionAdapter.getString(context, instruction);
    int imageResource = InstructionAdapter.getImageResource(instruction);
    }
}

The localization support currently includes the instruction strings in English, French and Catalan. It is done through the InstructionAdapter class which converts an Instruction object into a human-readable string and icon.

Best practices
It is a good practice to check the arePropsFulfilled flag before using any route object for navigation. This indicates if all the properties were fulfilled to compute a route. If the flag is false, it is advised to recompute the route. Similarly, when the user location returned by getProjection method is far from the edge, it populates a field 'isRecomputeRequired' to indicate that the user should recompute its route. This field should be checked before using the projected point for navigation.

Places

A point of interest or POI is a specific zone that someone may find useful or interesting. In our guide, terms place and POI are synonymously used. Some examples of POIs in a hotel would be restaurants, parking spaces, reception area, gym. Our SDK aids the user in finding specific places or find information about the points of interest he is near to.

With reference to GoIndoor, a POI can be of the following types based on their shapes:

  • Point – When the Point of interest can be defined by a point on the map. For example, an entry or exit gate.
  • Circle – When the POI covers a circular area. It is identified by a particular point and a radius around it.
  • Polygon – When the POI is of polygon geometry and is defined by a list of boundary points.

Place object contains all the information about a poi like building id, floor number, latitude, longitude, geometry, properties and tags.

Use getPlaces method to fetch the places in the location. You can specify tags and filters to add search criterion. The is an overloaded method and can be used in a number of ways to get the places. For more details, refer this.

Consider the following example where this method is used to fetch the closest place in the location. Assuming the current location of the user is 'location', specify the radius from the location as zero and tags and filters as null as there is no specific search criteria in this case.

ArrayList places = go.getPlaces(location, 0, null, null);

Tags
Tags are used to save attributes of a place. These tags can be used in the applications in different ways, like, for grouping similar elements, searching through the elements or for different graphical representations. For instance, if you want to show all the emergency exit gates in your building in red color, add a tag to all of them as ‘emergency_exit’. In the application, fetch all the places with that tag and show them in red.

Following example illustrates a simple usage of tags. This code snippet shows how to get list of all the exit places. Assuming the exit gates (POIs in this case) have been tagged “exit_gate", to get the list of all the exit gates in a building with buildingID as '1e76b0ac-2e19-4c18-84a9-b5b2e66b6e7f', add the following code.

Find places in a building with a given tag

ArrayList tags = new ArrayList<>();
tags.add("exit_gate");
ArrayList places = go.getPlaces("1e76b0ac-2e19-4c18-84a9-b5b2e66b6e7f";, tags, null);

Properties
Properties are used to save additional information about the points of interest. For example, for a place like reception, add a property 'operating_timings' and save its operating timings as its value. This information can then be used in any way in the application.

Tracking

Tracking can be done by logging the location of the user on the server frequently. This can be done using the logPosition method of the Logger class.

Every time there is a change in location, onLocation method of the LocationBroadcast is invoked with the new location attributes. Once the new location is received, use logPosition() to log the position to the server at a certain frequency.

Log user's position:

public void onLocation(LocationResult location) {
    go.getLogger().logPosition(loc);
}

This method sends user's location details to the server in the form of LocationResult object.

User Profile

Our SDK can help you customize the user experience based on their profiles. For this, the user's profile needs to be updated on the server. An example would be to send different notifications to different users. This customization is based on the user properties defined in the Admin Panel.

Assume you want to send a notification that is suitable to only visitors above 18 years of age. In this case, you can configure the notification in the Admin Panel and set the target based on age. From the device application, update the user information (age, in this case). The SDK then takes care of triggering the notification only when the user profile satisfies the age criteria.

Following code sample shows how to update a user property on the server. Assuming that in the Admin Panel settings, there is a user property called age of type number and user's age is defined by a variable 'age', this code snippet updates user's age to the server. The same method putStatsProp can be used to update any user property to the server.

Values.Settings.UserValue value = new Values.Settings.UserValue<>(age, Values.Format.NUMBER);
go.getLogger().putStatsProp("age", value);

Another use case of maintaining user profiles is to compute multimodal routes. In this case, user specific values of the properties of the edges are to be updated on the server so as to compute appropriate navigational paths. This value can be updated by using putNavProp method. This method updates the value of an existing navigation property for the user by providing its key (a.k.a. the exact name as defined in the Admin Panel) and the new value encoded inside a UserValue object. Currently, it can only supports a BOOLEAN as value.

Below is an example which shows how to indicate the SDK that a wheelchair friendly path should be computed for the current user. Assuming the related property of the edges is called 'wheelchair_accessible', we are setting its value to true.

Values.Settings.UserValue value = new Values.Settings.UserValue<>(true, Values.Format.BOOLEAN);
go.getLogger().putNavProp("wheelchair_accessible", value);

There is another overloaded method to store a bunch of properties together. Refer this for more details.

Show Indoor Maps

Google Maps Integration

In order to show an indoor map overlapped in Google Map, first step is to initialize a GoogleMap object called map as explained here.

Next step is to get the UrlTileProvider for the floor in question. The GoIndoor library includes a list of the buildings available for the account. Every Building object includes all the floors available for that building. Floor object has the information of TileProvider for indoor tiles. In order to access the UrlTileProvider for a floor, use getGMTileProvider() method . The following example shows how to do it.

ArrayList floors = building.getFloorsList();
UrlTileProvider tiles = floors.get(iFloor).getGMTileProvider();

Once the UrlTileProvider is available, it can be directly added to the map using the method shown below.

map.addTileOverlay(new TileOverlayOptions().tileProvider(tiles));

Getting Started with iOS SDK

Following sections show how to implement basic locationing related functionalities in iOS applications. For an extensive list of classes and methods exposed, refer this.

SDK Installation

Manual set up of the SDK

  1. Download latest version of the GoIndoor iOS SDK from here.
  2. Drop the Goindoor.framework directory to the Xcode project. This will make the GoIndoor library available to the project.
  3. Copy routing.bundle directory also to the Xcode project. This provides icons and strings to the routing framework.
Create a New Application

For the SDK to work, it needs Bluetooth and WiFi/Network connection support from the devices. Ensure these features are available.

  1. Create a basic iOS app.
  2. Set the Base SDK version to iOS7.
  3. Add –ObjC flag under the Linking section of the Build Settings tab of your Xcode project.
Initialization and Synchronization

Initialization
The first step is to create a new instance of the GoIndoor library. This can be done through the goIndoorWithBlock method of OYMGoIndoor class. Here, it tries to connect to the server and synchronize with it. The success or failure of this method is notified by the callback method provided.

go = [OYMGoIndoor goIndoorWithBlock:^(id builder) {
    [builder setAccount:account];
    [builder setPassword:password];
    [builder setConnectCallBack:callback];
}];

Where

  • Account – GoIndoor account name
  • Password – GoIndoor account password
  • Callback – Callback method to handle the connection process.

It is mandatory to set the above three parameters in the GoIndoor library to get its object. There are other optional parameters that can be set through the following methods exposed in GoIndoorBuilder Protocol.

setURL: Sets the Goindoor backend URL.
setUser: Sets the profile to be used. Default is user.
setAccount: Sets the account.
setPassword: Sets the password.
setConnectCallBack: Sets the callback to handle the connection process.
setLocationType: Sets the positioning type. Default is kOYMGoIndoorLocationTypeAverage.
setLocationUpdate: Sets the update rate in msec. Default is kOYMGoIndoorDefaultLocationRefresh.
setDebug: Sets the debug mode. Default is false.
setUpdatePolicy: Sets the update policy. Default is (kOYMGoIndoorUpdateWifi | kOYMGoIndoorUpdateMobile).
setDatabaseUpdate: Sets the database update rate in msec. Default is kOYMGoIndoorDefaultUpdateTime.

Synchronization
When an application first connects to the server, it automatically downloads information about beacons, buildings, floors, POIs, notifications and assets to the device. This not only makes the application more efficient but also provides it offline capabilities. The SDK continues to synchronize with the server in the background at regular intervals to fetch updates, if any available.

Location

In order to use GoIndoor location library, define a delegate class that conforms to the OYMLocationDelegate protocol. If the Location provider is successfully initiated, didStartSuccessfully method is invoked and in case of failure/exception, didFailStarting is invoked. On successful initialization, onLocation method of this delegate is called each time a new position is computed and onNotification is called each time a new notification is triggered.

/**
* This delegate will provide feedback to the user regarding the indoor location library.
*/
@protocol OYMLocationDelegate
@required
/**
* This method is called when the indoor location service has been
* correctly started.
*/
- (void) didStartSuccessfully;
/**
* This method is called when an exception is thrown when trying to
* start the indoor location service.
*/
- (void) didFailStarting;
/**
* This method is called when a new position is available.
*
* @param location User position
*/
- (void) onLocation:(OYMLocationResult*)location;
/**
* This method is callen when a notification is triggered.
*
* @param notification Notification triggered
*/
- (void) onNotification:(OYMNotificationResult*)notification;
@optional
/**
* This method is called when the app has not the right authorisation for the Location Services.
*
* @param current Current Authorisation Permission
*/
- (void) locationAlwaysAuthorizationRequired:(CLAuthorizationStatus)current;
/**
* This method is called when the Location Services are disabled.
*/
- (void) locationServicesAreDisabled;
/**
* This method is called when the Core Blueooth Central Manager state has changed.
*
* @param state The new Core Blueooth Central Manager state
*/
- (void) centralManagerDidChangeState:(enum CBCentralManagerState)state;
@end

The location is sent by the library as OYMLocationResult object which has the following fields.

@property (readonly) double latitude; /** WGS84 Latitude */
@property (readonly) double longitude; /** WGS84 Longitude */
@property (readonly) int used; /** Number of iBeacons used */
@property (readonly) double accuracy; /** Position accuracy (meters) */
@property (readonly) NSArray* found; /** List including the longitude, latitude and accuracy of each iBeacon in sight */
@property (readonly) int floorNumber; /** Floor number */
@property (readonly) int type; /** Positioning type: kOYMIndoorLocationTypeNo, kOYMIndoorLocationTypeIbeacon, kOYMIndoorLocationTypeGps */
@property (readonly) NSString* buildingName; /** Building name */
@property (readonly) NSString* building; /** Building ID */
@property (readonly) int geofences; /** Number of geofences */

To start receiving location updates, provide an object that conforms the OYMLocationDelegate protocol to startLocate method of OYMGoIndoor object.

[go startLocate:delegate];

After starting the library, the onLocation method of the delegate will be invoked whenever a new position is computed. In order to stop receiving location updates, invoke the stopLocate method with the same delegate object.

[go stopLocate:delegate];

In order to properly stop the library, it is necessary to call disconnect method.

[go disconnect];

There are three positioning types that you can set for the location updates that you wish to receive. These types differ in the way SDK computes the location. Use the following method to set the type.

-(void)setLocationType:(OYMGoIndoorLocationType)type

In the above method, type can take the following values:

  1. kOYMGoIndoorLocationTypeAverage – Weighted average of the signals from multiple beacons is used to triangulate the location.
  2. kOYMGoIndoorLocationTypeClosest – Location is calculated by using only the signals of the beacon which is closest to the user.
  3. kOYMGoIndoorLocationTypeProject – Weighted average of signals from multiple beacons (as in the case of kOYMGoIndoorLocationTypeAverage) is used to determine the location which is then projected to the closest edge.

You can use the coordinates obtained from Indoor Location provider to do various things. One of the most common use cases is to show user's current location on the map. Refer the 'Show Indoor Maps' section to learn how to display indoor maps. Our SDK allows easy integration with both Google Maps and Apple's MapKit framework. In the following example, we are displaying user's location in Apple Maps.

Mark user's current position:

OYMAnnotation *markerPosition;
int floorNumber;
- (void) onLocation:(OYMLocationResult *)location {
    // Show indoor tiles
    if (location.type == kOYMLocationTypeIbeacon && (floorNumber != location.floorNumber || tileOverlay == nil)) {
        OYMBuilding *building = [go getBuildings:location.building];
        if (tileOverlay) {
            [mapView removeOverlay:tileOverlay];
        }
        floorNumber = location.floorNumber;
        OYMFloor *f = building.floors[@(location.floorNumber) stringValue];
        tileOverlay = f.tileProvider;
        [mapView addOverlay:tileOverlay level:MKOverlayLevelAboveLabels];
    }
    // Show marker
    CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(location.latitude, location.longitude);
    if (!markerPosition) {
        markerPosition = [[OYMAnnotation alloc] initWithLocation:coord andImage:markerImage];
    } else {
        [markerPosition setCoordinate:coord];
    }
}

Navigation

Navigation is the process of finding a route or way between two points. Our SDK supports only indoor navigation. It provides the optimal shortest path for the user to navigate from the starting point to the desired destination. The navigation APIs return the routing points or the roadmap to be traversed along with the estimated time it will take to reach the destination and the distance to be covered.

Please note that the route/navigation path is returned as a list of instructions which can then be used by the developers in their applications as suitable – display on the UI, draw indicators etc.

Creating a route
In order to create a route between two points, these points need to be encoded inside a OYMRoutePoint object.

(OYMRoutePoint*) initWithX:(NSNumber*)newX andY:(NSNumber*)newY andFloornumber:(NSNumber*)fn andBuildingId:(NSString*)bId;

To get the OYMRoute object, use the computeRoute() method:

OYMRoute* route = [go computeRouteFrom:start to:destination];

The route object obtained contains the following information

  1. Route – List of OYMRoutePoint containing the route instructions – List of instructions to follow
  2. Distance – Route distance in meters
  3. Time – Route time in seconds
  4. arePropsFulfilled – Flag telling whether the properties are fulfilled to compute the route or not.

Following code snippet shows how to use computeRoute method to find the route between source, which is the current user location identified by variable 'location' and destination, which is a POI identified by variable 'poi'.

OYMRoutePoint *start = [[OYMRoutePoint alloc] initWithLatitude:@(location.latitude) andLongitude:@(location.longitude) andBuilding:location.building andFloorNumber:@(location.floorNumber)];
OYMRoutePoint *end = [[OYMRoutePoint alloc] initWithLatitude:poi.latitude andLongitude:poi.longitude andBuilding:poi.building andFloorNumber:poi.floorNumber];
OYMRoute *route = [go computeRouteFrom:start to:end];

Now the OYMRoute object should be used to retrieve the projected position and the next instruction for the user.

// Assuming computed route is a OYMRoute object with name route
- (void) onLocation:(OYMLocationResult *)location {
    if (isNavigationMode) {
        // Get projected point
        OYMRoutingResult *rr = [route getProjectionForLocation:location];
        OYMRouteProjectedPoint* projPoint = rr.projectedPoint;
        // Get next instruction to show
        OYMInstruction *instruction = [route getRouteInstructionForLocation:location];
        int nextInstructionDistance = (int) (instruction.distance - [rpp.distanceFromStart doubleValue]);
        // Get localized instruction and image
        NSString *string = [OYMInstructionAdapter getStringForInstruction:instruction]
        UIImage *image = [OYMInstructionAdapter getImageForInstruction:instruction];
    }
}

The localization support currently includes the instruction strings in English, French and Catalan. It is done through the OYMInstructionAdapter class which converts an Instruction object into a human-readable string and icon.

Best practices
It is a good practice to check the arePropsFulfilled flag before using any route object for navigation. This indicates if all the properties were fulfilled to compute a route. If the flag is false, it is advised to recompute the route. Similarly, when the user location returned by getProjectionForLocation method is far from the edge, it populates a field isRecomputeRequired to indicate that the user should recompute its route. This field should be checked before using the projected point for navigation.

Notifications

A notification is a kind of event that happens when a specific action happens with respect of a Point of Interest (POI).

Different notifications can be configured for a POI using the Admin Panel. SDK generates notifications when those specific events occur and it is the responsibility of the application to handle them appropriately.

Please note that the notifications generated by SDK are not push notifications on the phone. Whenever a notification is generated, onNotification of OYMLocationDelegate protocol is invoked. This method can be then used in different ways to handle notifications. The notifications can also be filtered further based on its properties or according to the user profile.

There are four types of notifications triggered by the SDK:

  • Enter – This notification is generated when the device enters the POI.
  • Stay – This notification is generated when the device has been inside the POI for a specified time period.
  • Leave – This notification is generated after the device has left the POI.
  • Nearby – This notification is generated when the device goes near a POI.

Trigger an action on receiving a particular notification:
The following code shows how to display a message to a user on a 'NEARBY' notification. In this particular case, the application will show a Thank you message when the user is near an exit gate and is about to leave the place.

- (void)onNotification:(OYMNotificationResult *)nr {
    // Check if exit tag in POI && notification action is of type NEARBY
    if ([nr.place.tags containsObject:@"exit"] && nr.notification.action == NEARBY) {
        [self showPopupWithText:@"Thanks for your visit"];
    }
}

Filter notifications based on user profiles:
Notifications can also be sent to users based on their profiles. For this, add target to the notifications based on user properties. Update the user profile on the server. See the related sample here. The SDK will then automatically trigger notifications based on the profile. For example, if a notification is to be sent to adults above 21 years of age only, then update the user's age on the server through the application and while creating a notification on Admin Panel, add age property as a filter to it. The SDK will now send that notification only to users which fulfill the age criterion.

Places

A point of interest or POI is a specific zone that someone may find useful or interesting. For example, various POIs in a location like hotel would be restaurants, parking spaces, reception area, gym etc.

With respect to GoIndoor, a POI can be one of the following types based on their shapes:

  • Point – When the Point of interest can be defined by a point on the map. For example, an entry or exit gate.
  • Circle – When the POI covers a circular area. It is identified by a particular point and a radius around it.
  • Polygon – When the POI is of polygon geometry and is defined by a list of boundary points.

GoIndoor library defines a OYMPlace object which contains all the information about a POI like building id, floor number, latitude, longitude, geometry, properties and tags. OYMGoIndoor class exposes various methods to fetch places.For more details, refer this.

Consider the following example where 'getPlacesWithLocationResult:andRadius:andTags:andFilter:' method is used to fetch the closest place in the location. Assuming the current location of the user is 'location', specify the radius from the location as zero and tags and filters as null as there is no specific search criteria in this case.

NSArray* places = [go getPlacesWithLocationResult:location andRadius:0 andTags:nil andFilter:nil];

Tags
Tags are used to save attributes of a place. These tags can be used in the applications in different ways, like, for grouping similar elements, searching through the elements or for different graphical representations. For instance, if you want to show all the emergency exit gates in your building in red color, add a tag to all of them as ‘emergency_exit’. In the application, fetch all the places with that tag and show them in red.

The following example illustrates the use of tags. This shows how to find all the exit gates in a location. Assuming the exit gates (POIs in this case) have been tagged “exit_gate", to get the list of all the exit gates in a building with buildingID as '1e76b0ac-2e19-4c18-84a9-b5b2e66b6e7f', add the following code:

Find places in a building with a given tag

NSArray *places = [go getPlacesWithId:@"1e76b0ac-2e19-4c18-84a9-b5b2e66b6e7f" andTags:@[@"exit_gate"] andFilter:nil];

Properties
Properties are used to save additional information about the points of interest. For example, for a place like reception, add a property 'timings' and save the receptionist's availability times as its value. This information can then be used in any way in the application.

Tracking

Tracking can be done by logging the location of the user on the server frequently. This can be done using various log methods of the OYMLogger class.

Every time there is a change in location, onLocation method of the OYMLocationDelegate is invoked with the new location attributes. Once the new location is received, use logPosition method to simply log current location to the server at a certain frequency.

Log user's position

[go.getLogger logPosition:loc];

This method sends user's location details to the server in the form of OYMLocationResult object.

User Profile

Our SDK can help you customize the user experience based on their profiles. For this, the user's profile needs to be updated on the server. An example would be to send different notifications to different users. This customization is based on the user properties defined in the Admin Panel.

Assume you want to send a notification that is suitable to only visitors above 18 years of age. In this case, you can configure the notification in the Admin Panel and set the target based on age. From the device application, update the user information (age, in this case). The SDK then takes care of triggering the notification only when the user profile satisfies the age criteria.

Following code sample shows how to update a user property on the server. Assuming that in the Admin Panel settings, there is a user property called age of type number and user age is defined by variable age, this code snippet updates user profile with the age field. This method putStatsPropKey can be used to update any user property to the server.

OYMUserValue *value = [[OYMUserValue alloc] initWithValue:[NSNumber numberWithInt:age] settingFormat:NUMBER];
[go.getLogger putStatsPropKey:@"age" andUserValue:value];

Another use case of maintaining user profiles is to compute multimodal routes. In this case, user specific values of the properties of the edges are to be updated on the server so as to compute appropriate navigational paths. This value can be updated by using '– putNavPropKey:andUserValue:' method. This method updates the value of an existing navigation property for the user by providing its key (a.k.a. the exact name as defined in the Admin Panel) and the new value encoded inside a UserValue object. Currently, it can only supports a BOOLEAN as value.

Below is an example which shows how to indicate the SDK that a wheelchair friendly path should be computed for the current user. Assuming the related property of the edges is called 'wheelchair_accessible', we are setting its value to true.

OYMUserValue* value = [[OYMUserValue alloc] initWithValue:[NSNumber numberWithBool:YES] settingFormat:BOOLEAN];
[go.getLogger putStatsPropKey:@"wheelchair_accessible" andUserValue:value];

There is another overloaded method to store a bunch of properties together. Refer this for more details.

Show Indoor Maps

MapKit Integration
The GoIndoor library maintains the list of the buildings available for your account. Each Building object in turn includes all the floors available for that building and every floor has information about the tile provider. In order to show an indoor map overlap in MapKit framework, our SDK has a helper class OYMTileOverlay that inherits from MKTileOverlay. This object can be retrieved from the OYMFloor class and can be used to overlay it on a MKMapView instance.

tileOverlay = f.tileProvider;
[mapView addOverlay:tileOverlay level:MKOverlayLevelAboveLabels];

Google Maps Integration
GoIndoor can also be used with Google Maps for iOS. In this case, first of all add the Google Maps for iOS frameworks. Find more information on this here. In order to ensure compatibility, it is required to include the OYMFloor+GoogleMaps category available in our repository. This category provides a tileProviderGoogle variable of type GMSURLTileLayer to be integrated in the GMSMapView. Assuming the map variable has been correctly initialized, to overlay indoor tiles in Google Maps, use the following code snippet for reference.

GMSURLTileLayer *layer = floor.tileProviderGoogle;
layer.map = map;