Using the new floor and indoor lock APIs


The old way (SDK versions < 2.9)


In our previous SDK versions prior to 2.9, you would use the setLocation method of IALocationManager to either set your location explicitly, lock position to specific floor, give hints about the floor number or lock position to specific venue.


There are three main uses for the setLocation:


1. Setting your location explicitly to specific latitude & longitude in specific floor


IALocationManager.setLocation(IALocation.Builder()
        .withLatitude(60.01)
        .withLongitude(24.123)
        .withAccuracy(5.0)
        .withFloorLevel(3)
        .build());


    Here we initialize the position to latitude 60.01, longitude 24.123 to floor number 3 with accuracy of 5 meters.


2. Forcing positioning to specific floorplan id (a.k.a. explict floor plan or explicit region with floor plan id). This is flagged as deprecated in SDK 2.9 and will be removed in SDK 3.0 (see below)


IALocationManager.setLocation(IALocation.Builder()
        .withRegion(IARegion.floorPlan("floorPlanId"))
        .build());


    Here we force the positioning to specific floorplan of id "floorPlanId". Positioning will be locked to this floorplan until either next floorplan id is provided through the setLocation or positioning is stopped.


3. Forcing positioning to specific venue id (a.k.a. explicit venue or explicit region with venue id). This is flagged as deprecated in SDK 2.9 and will be removed in SDK 3.0 (see below)


IALocationManager.setLocation(IALocation.Builder()
        .withRegion(IARegion.venue("locationId"))
        .build());


    Here we force the positioning to specific venue of id "locationId". Positioning will be locked to this venue until either next venue or floorplan id is provided through the setLocation or positioning is stopped.


The new way (SDK versions >= 2.9)


In the new SDK 2.9, we have added new APIs for locking the positioning to new specific floor and / or building.


1. Setting your location explicitly to specific latitude & longitude can be done via setLocation like in older SDK versions.


2. Forcing positioning to specific floorplan id is replaced by locking positioning to specific floor number. Locking positioning can be done by using the new lockFloor method in IALocationManager. In addition to locking, new method unlockFloor is provided so positioning can be unlocked from the specific floor.


// Lock positioning to floor number 3
IALocationManager.lockFloor(3);

// Unlock positioning so floor level is detected automatically
IALocationManager.unlockFloor();


3. Forcing positioning to specific venue id is replaced by locking positioning to indoors. Correct venue is automatically detected by using platform locations. Locking indoors can be achieved by new method lockIndoors(boolean lock) which locks the positioning inside the venue. This also means that GPS is no longer scanned. lockIndoors needs to be called with a boolean argument indicating whether you want to lock or unlock to indoor positioning.


// Lock positioning indoors (i.e., enable indoor-only mode)
IALocationManager.lockIndoors(true);

// Unlock positioning indoors (disable indoor-only mode)
IALocationManager.lockIndoors(false);


The new APIs lockFloor and lockIndoors can be used together but it is redundant in most cases: Locking positioning to specific floor also implicitly locks positioning indoors.


Why to migrate


The new lockFloor and lockIndoors methods provide cleaner solution to manipulating where the positioning happens. You no longer need to use any UUIDs to set the positioning to specific floorplan or venue id. This in turn results to much cleaner application level logic if specific floor positioning is required. 


The new APIs also provide unlock methods which were previously missing. SDK 2.9 also supports the old  setLocation method, but its functionality will be removed in SDK 3.0.


No more manual meta data fetching for regions


The old way (SDK versions < 2.9)


In our older SDK versions you would use IAResourceManager to fetch the meta data of floor plans, e.g. the bitmap image or pixel to WGS transformations.

For exampl,e if you wanted to fetch the bitmap image of the specific floor plan by id, you would have had to implement something similar to the following:


1. Implement IARegionListener and fetch the metadata from IndoorAtlas servers using the IAResourceManager. The information is stored in IAFloorPlan

private IAResourceManager mResourceManager;

private IARegion.Listener mRegionListener = new IARegion.Listener() {
    @Override
    public void onEnterRegion(IARegion region) {
        if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
            fetchFloorPlan(region.getId());
        }
    }

    @Override
    public void onExitRegion(IARegion region) {
        // leaving a previously entered region
    }
};

private void fetchFloorPlan(String id) {
    // Cancel pending operation, if any
    if (mPendingAsyncResult != null && !mPendingAsyncResult.isCancelled()) {
        mPendingAsyncResult.cancel();
    }

    mPendingAsyncResult = mResourceManager.fetchFloorPlanWithId(id);
    if (mPendingAsyncResult != null) {
        mPendingAsyncResult.setCallback(new IAResultCallback<IAFloorPlan>() {
            @Override
            public void onResult(IAResult<IAFloorPlan> result) {
                Logger.d(TAG, "onResult: %s", result);

                if (result.isSuccess()) {
                    handleFloorPlanChange(result.getResult());
                } else {
                    // do something with error
                    Toast.makeText(FloorPlanManagerActivity.this,
                        "loading floor plan failed: " + result.getError(), Toast.LENGTH_LONG)
                        .show();
                }
            }
        }, Looper.getMainLooper()); // deliver callbacks in main thread
    }
}


2. Read the URL of the bitmap that is stored in IndoorAtlas servers using the getUrl method of the IAFloorPlan. Here we use Picasso library for convenience to download the image from the URL.


import com.squareup.picasso.Picasso;
// ...
private void handleFloorPlanChange(IAFloorPlan newFloorPlan) {
    Picasso.with(this)
        .load(newFloorPlan.getUrl())
        .into(mFloorPlanImage);
}



The new way (SDK versions >= 2.9)


WARNING: The new getFloorPlan and getVenue methods described below are not available (will return null) if the old setLocation API is used withe explicit region ids. (explicit floor plans, 2., or venues, 3.). If the old setLocation API is used, meta data needs to be fetched the old way. This does not concern using setLocation with locations (1.). Moral of the story: do not use explicit region IDs anymore.


In our new SDK versions, starting from version 2.9.0, the metadata information is provided in the IARegion ready for use. This means that the application level logic does not need to handle the explicit metadata downloading using the IAResourceManager.


Starting from 2.9.0, IARegion class has two new methods relating to the new functionality: getFloorPlan and getVenue. The new methods will return the metadata relating to either the floor plan or venue, depending which one the IARegion corresponds to (as previously, type can be checked with getType. We have also added new class IAVenue which represents the metadata for venue


With the new APIs, the previous example simplifies to the following:


1. Implement the IARegionListener


private IARegion.Listener mRegionListener = new IARegion.Listener() {
    @Override
    public void onEnterRegion(IARegion region) {
        if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
            handleFloorPlanChange(region.getFloorPlan());
        }
    }

    @Override
    public void onExitRegion(IARegion region) {
        // leaving a previously entered region
    }
};


2. Fetch the image bitmap (same as before)


import com.squareup.picasso.Picasso;
// ...
private void handleFloorPlanChange(IAFloorPlan newFloorPlan) {
    Picasso.with(this)
        .load(newFloorPlan.getUrl())
        .into(mFloorPlanImage);
}



Wayfinding


The old way (Wayfinding plugin beta versions 2.7 and 2.8)


In the wayfinding plugin, one would initialize an IndoorAtlas wayfinder (IAWayfinder) using a wayfinding graph, which has been manually downloaded from the IndoorAtlas web app, together with the application context:


// Load the JSON file to String graphJSON
IAWayfinder wayfinder = IAWayfinder.create(context, graphJson);


Then to start routing an user to a given destination, the destination was first set in the wayfinder


// Set the latitude, longitude and floor number
wayfinder.setDestination(60.1684003, 24.9306895, 1);


and finally the route from the current location to destination is was obtained by first providing the user’s location and then calling getRoute:


// Set the latitude, longitude and floor number
wayfinder.setLocation(60.1696597, 24.932497, 1);
IARoutingLeg[] route = wayfinder.getRoute();
// empty if route could not be found, otherwise list of legs



The new way (SDK 2.9+)


See the new Android wayfinding sample code to see how this works in a complete application. Note: The new integrated wayfinding is not yet available in mainland China.


In the new SDK, the wayfinding graph does not need to be manually downloaded. Instead the wayfinding graphs are automatically handled by the SDK. The wayfinding API is also integrated into the SDK instead of being in a separate plugin. The new integrated API works as follows. First, one defines an IAWayfindingListener

private IAWayfindingListener mWayfindingListener = new IAWayfindingListener() {
    @Override
    public void onWayfindingUpdate(IARoute route) {
        List<IARoute.Leg> legs = route.getLegs();
        // visualize route
    }
};


then wayfinding is started by providing the destination as an IAWayfindingRequest and registering the listener with IALocationManager.requestWayfindingUpdates

IAWayfindingRequest wayfindingRequest = new IAWayfindingRequest.Builder()
    .withFloor(1) // destination floor number
    .withLatitude(60.1696597) // destination latitude
    .withLongitude(24.932497) // destination longitude
    .build();

mIALocationManager.requestWayfindingUpdates(wayfindingRequest, mWayfindingListener);


The registered mWayfindingListener starts receiving updates through onWayfindingUpdate as soon as the wayfinding graph has been downloaded (or loaded from the automatic disk cache). The route is updated on each new IndoorAtlas location and it always connects the latest IALocation and the destination set in IAWayfindingRequest. If routing between these two locations is impossible, for instance, the floor the user is on has no wayfinding graph or the wayfinding graph is disconnected, the returned list of routing legs (IARoute.getLegs()) is empty.


The returned IARoute object consists of IARoute.Legs (which used to be called IARoutingLeg in the old wayfinding plugin). Each leg has begin and end IARoute.Points (which used to be called IARoutingPoints in the old plugin). Unlike the wayfinding plugin, the SDK has automatic rerouting logic, which avoids unnecessary re-routing and tries to primarily route the user to the destination along the original (first returned) route. 


When the user has reached the destination or, for example, wants to cancel the wayfinding session, IndoorAtlas wayfinding can be stopped by calling IALocationManager.removeWayfindingUpdates

mIALocationManager.removeWayfindingUpdates();

See the Android sample code for one method of checking if the user has reached the destination using the returned route.