Automatic floor plan detection is handled with IARegion events as described in the floor detection chapter. For your convenience, IndoorAtlas SDK also provides an API for fetching the floor plan images that you have stored in our cloud in the mapping phase.

If you use an indoor map provider such as Micello, you probably want to skip this and use their API for displaying the floor plan instead.

To display a location on the floor plan, we are going to download a floor plan image from the service and add it to the map as an overlay view.

The IAResourceManager class provides an interface for downloading floor plan images from IndoorAtlas services, The IAFloorPlan class represents floor plan data received from the service.

We will also need a UIImage object to hold the floor plan image.

In the interface section, add two delegates MKMapViewDelegate and IALocationManagerDelegate and define the required instance variables and properties.

Add two delegates MKMapViewDelegate and IALocationManagerDelegate.

Obj C:

@interface AppDelegate () <MKMapViewDelegate, IALocationManagerDelegate> {
    IALocationManager *locationManager;
    IAResourceManager *resourceManager;
    IAFloorPlan *floorPlan;

    UIImage *floorPlanImage;
    MKMapView *mapView;
@property (strong) MapOverlay *mapOverlay;


class AppDelegate: MKMapViewDelegate, IALocationManagerDelegate {

    var locationManager: IALocationManager
    var resourceManager: IAResourceManager
    var floorPlan : IAFloorPlan? = nil

    var floorPlanImage = UIImage()
    var mapView: MKMapView

    var mapOverlay: MapOverlay

Refer to the previous sections to see how to create a location manager and Apple MapKit Documentation for info on the MapOverlay.

As soon as you get to a location for which you have a floor plan, you will receive a enter region event. Use the fetchFloorPlanWithId:andCompletion method of IAFloorPlanManager to fetch the floor plan data.

If there are no errors, fetch the image data with the fetchFloorPlanImageWithId:andCompletion method.

Obj C:

- (void)indoorLocationManager:(IALocationManager *)manager didEnterRegion:(IARegion *)region
    (void) manager;

    // Fetch the floorplan for the given region identifier
    [resourceManager fetchFloorPlanWithId:region.identifier
    andCompletion:^(IAFloorPlan *floorPlan, NSError *error) {

        if (!error) {
            self.floorPlan =floorPlan;
            [resourceManager fetchFloorPlanImageWithId:region.identifier
            andCompletion:^(NSData *imageData, NSError *error){
                __weak typeof(self) weakSelf = self;
                fpImage = [[UIImage alloc] initWithData:imageData];
                [weakSelf changeMapOverlay];
        } else {
            NSLog(@"There was an error during floor plan fetch");


func indoorLocationManager(_ manager: IALocationManager, didEnter region: IARegion) {

    guard region.type == ia_region_type.iaRegionTypeFloorPlan else { return }

    // Fetch the floorplan for the given region identifier
    self.resourceManager.fetchFloorPlan(withId: region.identifier,
    andCompletion: { (floorplan, error) in

        if (error == nil) {
            self.floorPlan = floorplan!
            self.resourceManager.fetchFloorPlanImage(with: floorPlan.imageUrl!,
            andCompletion: { (data, error) in

                if (error != nil) {
                    print(error as Any)
                } else {
                    self.fpImage = UIImage.init(data: data!)!
        } else {
            print("There was an error during floor plan fetch")

Now that you have a floor plan image, you can create the mapOverlayobject to be placed on the map.

Obj C:

- (void)changeMapOverlay
    if (mapOverlay != nil)
        [map removeOverlay:mapOverlay];

    double mapPointsPerMeter = MKMapPointsPerMeterAtLatitude(;
    double widthMapPoints = self.floorPlan.widthMeters * mapPointsPerMeter;
    double heightMapPoints = self.floorPlan.heightMeters * mapPointsPerMeter;
    CGRect cgRect = CGRectMake(0, 0, widthMapPoints, heightMapPoints);
    double bearing = degreesToRadians(self.floorPlan.bearing);
    self.rotated = CGRectApplyAffineTransform(cgRect, CGAffineTransformMakeRotation(bearing));

    mapOverlay = [[MapOverlay alloc] initWithFloorPlan:self.floorPlan
    [map addOverlay:mapOverlay];


func changeMapOverlay() {

    if mapOverlay != nil {

    let mapPointsPerMeter = (Float) MKMapPointsPerMeterAtLatitude(
    let widthMapPoints = floorPlan.widthMeters * mapPointsPerMeter
    let heightMapPoints = floorPlan.heightMeters * mapPointsPerMeter

    let cgRect = CGRect(x: 0, y: 0, width: CGFloat(widthMapPoints), height: CGFloat(heightMapPoints))
    let bearing = degreesToRadians(self.floorPlan.bearing)
    rotated = cgRect.applying(CGAffineTransform(rotationAngle: CGFloat(bearing)));
    mapOverlay = MapOverlay(floorPlan: floorPlan, andRotatedRect: rotated)