HERE iOS SDK Developer's Guide

Street-level Imagery

This section provides instructions on how to add street-level imagery capability to your HERE SDK-enabled application. The main entry point to do this is NMAStreetLevelView, a child class of UIView. NMAStreetLevelView provides methods to control the display of street-level imagery at specified geocoordinate positions and orientations. You can also use NMAStreetLevelView with the NMAStreetLevelGestureDelegate protocol so that users can navigate and zoom through street-level imagery using gestures.

NMAStreetLevelView

The first step to integrate street-level imagery into an application is to insert an NMAStreetLevelView into a UIView hierarchy. NMAStreetLevelView is derived from UIView and behaves accordingly—thus it may be resized and have layout constraints associated with it.

To add a full-screen NMAStreetLevelView instance to a UIViewController, you can do the following in your UIViewController implementation:


@interface MyViewController : UIViewController

@property (nonatomic, strong) NMAStreetLevelView* streetLevelView;

@end

@implementation MyViewController

-(void)loadView {
  self.view = [UIView new];
  self.streetLevelView = [NMAStreetLevelView new];
  [self.view addSubview:self.streetLevelView];
}

@end

This results in a fully functional NMAStreetLevelView instance that handles user interactions such as rotating, tapping, pinching, and link navigation (tapping arrows to move between sections). However, this view is blank until you instruct the view to move to a street level section. To display a street level section in New York City, add the following method to the MyViewController implementation above.


- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  [self moveToTimesSquare];
}

-(void)moveToTimesSquare {
  // Create some coordinates in NYC
  NMAGeoCoordinates *nyc = [[NMAGeoCoordinates alloc]
                initWithLatitude:40.7566520478576
                   longitude:-73.9865112863481];

  // Create a fixed orientation for the camera relevant to the location in NYC
  NMAStreetLevelOrientation orientation;
  orientation.heading = 30.4342; // look up the street
  orientation.pitch = 0;     // eyes at street level

  // Move to the street level section
  [self.streetLevelView moveToSectionAtGeoCoordinates:nyc
                     withSearchRadius:10.0 // Meters
                        animation:YES
                      orientation:orientation
                           zoom:0.5];

  // NOTE: Implement NMAStreetLevelViewDelegate protocol methods to receive
  // the result of the move programmatically
}
An NMAStreetLevelView has a set of link navigation arrows by default, you can replace these arrows with custom images. For example,

NMAImage *link = [NMAImage imageWithUIImage:[UIImage imageNamed:@"link.png"]];
[self.streetLevelView setNavigationLinkImage:link forPressedState:NO];

NMAImage *linkPress = [NMAImage imageWithUIImage:[UIImage imageNamed:@"linkPress.png"]];
[self.streetLevelView setNavigationLinkImage:linkPress forPressedState:YES];

Transitioning Between NMAMapView and NMAStreetLevelView

A common use case for street-level imagery is to move to a street-level section when a user taps on a location from a regular map view. The basic steps required to perform this action are:
  • Show street-level coverage on the NMAMapView (optional)
  • Get a geocoordinate from the NMAMapView via a tap gesture recognizer
  • Move the NMAStreetLevelView to a section that contains the geocoordinates
  • Handle move failures
This use case can be implemented as follows:

@interface MyViewController : UIViewController <NMAMapGestureDelegate,
                        NMAStreetLevelViewDelegate>
@property (nonatomic, strong) NMAMapView* mapView;
@property (nonatomic, strong) NMAStreetLevelView* streetLevelView;

@end

@implementation MyViewController

// Assume mapView and streetView have been initialized

-(void)viewDidLoad {
  self.mapView.gestureDelegate = self;
  self.mapView.streetLevelCoverageVisible = YES;
  self.streetLevelView.gestureDelegate = self;
}

-(void) mapView:(NMAMapView*)mapView
    didReceiveTapFromRecognizer:(UITapGestureRecognizer*)recognizer
{
  // Get geo coordinates from the tap in the regular map view
  CGPoint tapPoint = [recognizer locationInView:mapView];
  NMAGeoCoordinates* coords = [mapView geoCoordinatesFromPoint:tapPoint];

  // Create a camera orientation that syncs the street view with the regular map view
  NMAStreetLevelOrientation cameraOrientation;
  cameraOrientation.pitch = 0; // eyes at street level
  cameraOrientation.heading = mapView.orientation;

  // Move to the street level section
  [self.streetLevelView moveToSectionAtGeoCoordinates:coords
                     withSearchRadius:20.0 // 20M
                        animation:YES
                      orientation:orientation
                           zoom:0.5];

  // Note default map tap gesture handling is overridden by this callback
  // To re-enable add the following line
  //[mapView performDefaultActionForGestureRecognizer:recognizer];
}

-(void)streetLevelView:(NMAStreetLevelView *)view
       didMoveTo:(NMAGeoCoordinates *)geoCoordinates
       withSuccess:(BOOL)success
{
  if (!success) {
    // A street level section that contains the geo coordinate was not found
    // Alert the user
  }
}

@end

Note that moveToSectionAtGeoCoordinates: takes an NMAGeoCoordinate and a searchRadius parameter. This method searches for a street-level section that is closest to the specified coordinate before moving the view to that section. The search area around the coordinate is defined by the radius which is expressed in meters. Consider using a larger search radius at lower zoom levels to increase the likelihood of finding a street level section.

You can programmatically check if a street-level section exists for a geocoordinate before attempting the actual move by calling the following method:

// Check street level coverage, within a given radius, for the geo coordinates
[NMAStreetLevelView checkCoverageAtGeoCoordinates:coords
       withRadius:100.0 // Meters
    completion:^(NMAStreetLevelCoverageResult *result) {
      if (result.coverage == NMAStreetLevelCoverageAvailable) {
        // We have coverage - proceed.
      } else {
        // No coverage, or coverage could not be determined due to network error
        // If required, differentiate between no coverage and error cases
        if (result.coverage == NMAStreetLevelCoverageUnavailable) {
          // No coverage - alert user
        } else {
          // Could not determine coverage due to error - alert user
        }
      }
}];

Zooming in a Street-level Imagery View

You can set a street-level imagery view's zoom level using the moveToSection* or setZoom methods. The valid values for these methods can be obtained through the minimumZoom and maximumZoom properties. Currently, this zoom range is between 0 and 1. Setting the zoom value to 0 gives the most zoomed-out camera view and the widest viewing angle. Setting it to 1 gives the most zoomed-in camera view and the smallest viewing angle. If you specify a zoom level (for example, at 0.5) and later change the street level imagery view's width and height (therefore changing the view's aspect ratio), the zoom level remains unchanged (0.5). However, the actual image may appear larger or smaller depending on the new aspect ratio.

Note: If you attempt to set a zoom level before specifying a valid width and height for the street-level imagery view, then the HERE SDK cannot determine and use the appropriate aspect ratio and zoom level. Instead, the view uses a default value and adjusts it later when a valid width and height are provided. To avoid seeing this adjustment transition, it is recommended to setup the street-level imagery view's width and height before setting the zoom level. If auto layout is being used, it is recommended to set the zoom value in the view controller's viewDidLayoutSubviews method instead of viewDidLoad. Alternatively, you can explicitly set the width and height for the street level imagery view, and then safely set the zoom value.

NMAStreetLevelView Gestures

The street-level map supports the following default gestures:

To select an on-screen object, tap on it with one finger.
To move to a new location, tap on the screen twice with one finger. The center of the new location will be in the vicinity of where the screen was tapped.
To rotate the camera or change the pitch, use one finger to pan the street-level view .
To zoom out or zoom in, pinch or spread two fingers that are held to the screen.

Default gesture behaviors can be overridden by implementing the NMAStreetLevelGestureDelegate protocol and setting the NMAStreetLevelView object's gestureDelegate property. Only implement the protocol methods for the gestures you want to override. Overriding a gesture disables the default gesture behavior. However, you can reinstate the default behavior by calling [NMAStreetLevelView performDefaultActionForGestureRecognizer:] within the protocol method.

For example, to customize a tap gesture without disabling the default behavior,


@interface MyViewController : UIViewController <NMAStreetLevelGestureDelegate>

@property (nonatomic, strong) NMAStreetLevelView* streetLevelView;

@end

@implementation MyViewController

// Assume streetLevelView has been initialized

-(void)viewDidLoad {
  self.streetLevelView.gestureDelegate = self;
}

-(void)streetLevelView:(NMAStreetLevelView *)view didReceiveTapFromRecognizer:(UITapGestureRecognizer *)recognizer {
  // Perform some custom action
  // ...

  // Invoke the default tap gesture behavior
  [view performDefaultActionForGestureRecognizer:recognizer];
}

@end

You cannot use this account to purchase a commercial plan on Developer Portal, as it is already associated to plans with different payment methods.

To purchase a commercial plan on Developer Portal, please register for or sign in with a different HERE Account.

Something took longer than expected.

The project should be available soon under your projects page.

Sorry, our services are not available in this region.

Something seems to have gone wrong. Please try again later.

We've detected that your account is set to Australian Dollars (AUD).
Unfortunately, we do not offer checkouts in AUD anymore.
You can continue using your current plan as normal, but to subscribe to one of our new plans,
please register for a new HERE account or contact us for billing questions on selfservesupport@here.com.