Skip to main content
SDKs & dev tools 23 min read

Migrating from HERE SDK 3.x to 4.x: A Comprehensive Guide

HERE SDK Migration

Introduction

In the rapidly evolving world of mobile development, keeping your applications up to date with the latest technologies is crucial for maintaining competitive edge and providing users with the best experience. HERE Technologies has been at the forefront of mapping and location services, and with the release of HERE SDK 4.x, developers have access to more powerful and efficient tools than ever before. This guide is designed to help you navigate the transition from HERE SDK 3.x Premium to 4.x Navigate, covering essential topics such as initialization, positioning, navigation, and more. Whether you're looking to leverage improved performance, access new features, or ensure your app remains compatible with modern devices, this migration guide has you covered.

Initialization

SDK 3.x

Initialization in SDK 3.x typically involves setting up the map engine and requesting necessary permissions at runtime.

Copied
        // SDK 3.x: Initialize the map engine
MapEngine.getInstance().init(context, new OnEngineInitListener() {
    @Override
    public void onEngineInitializationCompleted(Error error) {
        if (error == Error.NONE) {
            // Map engine initialized successfully
            mapView = new MapView(context);
            // Setup your mapView here: center, zoom level, etc.
        } else {
            // Handle initialization failure
        }
    }
});
  
SDK 4.x

SDK 4.x requires initializing the SDKNativeEngine with SDKOptions, which includes your credentials.

Copied
        // SDK 4.x: Initialize the HERE SDK
String accessKeyID = "YOUR_ACCESS_KEY_ID";
String accessKeySecret = "YOUR_ACCESS_KEY_SECRET";
SDKOptions options = new SDKOptions(accessKeyID, accessKeySecret);

try {
    SDKNativeEngine sdkNativeEngine = new SDKNativeEngine(options);
    SDKNativeEngine.setSharedInstance(sdkNativeEngine);
    // Proceed with map view setup and other initializations
} catch (InstantiationErrorException e) {
    // Handle initialization error
}
  

Positioning

SDK 3.x

Positioning in SDK 3.x is managed through the PositioningManager.

Copied
        PositioningManager positioningManager = PositioningManager.getInstance();
positioningManager.start(PositioningManager.LocationMethod.GPS_NETWORK);

// Listen for position updates
positioningManager.addListener(new WeakReference<>(new PositioningManager.OnPositionChangedListener() {
    @Override
    public void onPositionUpdated(PositioningManager.LocationMethod method, GeoPosition position, boolean isMapMatched) {
        // Handle position update
    }

    @Override
    public void onPositionFixChanged(PositioningManager.LocationMethod method, PositioningManager.LocationStatus status) {
        // Handle position fix change
    }
}));
  
SDK 4.x

In SDK 4.x, positioning is integrated into the LocationEngine and the VisualNavigator for navigation-specific updates.

Copied
        LocationEngine locationEngine = LocationEngine.getInstance();
locationEngine.start(LocationAccuracy.NAVIGATION);

locationEngine.addLocationListener(new LocationListener() {
    @Override
    public void onLocationUpdated(Location location) {
        // Update location in VisualNavigator for navigation updates
       visualNavigator.updateLocation(location);
    }
});
  

Navigation and Route Calculation

SDK 3.x

Navigation and route calculation in SDK 3.x involve creating a RoutePlan and calculating the route with CoreRouter.

Copied
        CoreRouter coreRouter = new CoreRouter();
RoutePlan routePlan = new RoutePlan();

// Add waypoints to the route plan
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(startLatitude, startLongitude)));
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(endLatitude, endLongitude)));

coreRouter.calculateRoute(routePlan, new Router.Listener<List<RouteResult>, RoutingError>() {
    @Override
    public void onCalculateRouteFinished(List<RouteResult> routeResults, RoutingError error) {
        if (error == RoutingError.NONE) {
            Route route = routeResults.get(0).getRoute();
            // Start navigation with the calculated route
           NavigationManager.getInstance().startNavigation(route);
        } else {
            // Handle route calculation error
        }
    }
});
  
SDK 4.x

In SDK 4.x, route calculation is performed using the RoutingEngine, and navigation is managed by the VisualNavigator.

Copied
        RoutingEngine routingEngine = new RoutingEngine();
Waypoint startWaypoint = new Waypoint(new GeoCoordinates(startLatitude, startLongitude));
Waypoint destinationWaypoint = new Waypoint(new GeoCoordinates(endLatitude, endLongitude));

List<Waypoint> waypoints = new ArrayList<>(Arrays.asList(startWaypoint, destinationWaypoint));

routingEngine.calculateRoute(waypoints, new CarOptions(), new CalculateRouteCallback() {
    @Override
    public void onRouteCalculated(@Nullable RoutingError routingError, @Nullable List<Route> routes) {
        if (routingError == null && routes != null) {
            Route route = routes.get(0);
            // Use the route for navigation
            visualNavigator.setRoute(route);
        } else {
            // Handle route calculation error
        }
    }
});
  

Listeners and Navigation Updates

SDK 3.x

Listeners in SDK 3.x are added to the NavigationManager for various navigation events.

Copied
        NavigationManager navigationManager = NavigationManager.getInstance();

navigationManager.addNavigationManagerEventListener(new WeakReference<>(new NavigationManager.NavigationManagerEventListener() {
    @Override
    public void onRunningStateChanged() {
        // Handle running state changed
    }

    @Override
    public void onNavigationModeChanged() {
        // Handle navigation mode changed
    }

    // Implement other event methods...
}));
  
SDK 4.x

SDK 4.x introduces a more granular approach with listeners for specific navigation events, such as RouteProgressListener and MilestoneStatusListener.

Copied
        visualNavigator.addRouteProgressListener(new RouteProgressListener() {
    @Override
    public void onRouteProgressUpdated(@NonNull RouteProgress routeProgress) {
        // Handle route progress updates
    }
});

//Receiving Waypoints Events

// Notifies when the destination of the route is reached.
visualNavigator.setDestinationReachedListener(new DestinationReachedListener() {
    @Override
    public void onDestinationReached() {
        String message = "Destination reached.";
        messageView.setText(message);
        // Guidance has stopped. Now consider to, for example,
        // switch to tracking mode or stop rendering or locating or do anything else that may
        // be useful to support your app flow.
        // If the DynamicRoutingEngine was started before, consider to stop it now.
    }
});

// Notifies when a waypoint on the route is reached or missed
visualNavigator.setMilestoneStatusListener(new MilestoneStatusListener() {
    @Override
    public void onMilestoneStatusUpdated(@NonNull Milestone milestone, @NonNull MilestoneStatus milestoneStatus) {
      if (milestone.waypointIndex != null && milestoneStatus == MilestoneStatus.REACHED) {
          Log.d(TAG, "A user-defined waypoint was reached, index of waypoint: " + milestone.waypointIndex);
          Log.d(TAG,"Original coordinates: " + milestone.originalCoordinates);
      }
      else if (milestone.waypointIndex != null && milestoneStatus == MilestoneStatus.MISSED) {
          Log.d(TAG, "A user-defined waypoint was missed, index of waypoint: " + milestone.waypointIndex);
          Log.d(TAG,"Original coordinates: " + milestone.originalCoordinates);
      }
      else if (milestone.waypointIndex == null && milestoneStatus == MilestoneStatus.REACHED) {
          // For example, when transport mode changes due to a ferry a system-defined waypoint may have been added.
          Log.d(TAG, "A system-defined waypoint was reached, index of waypoint: " + milestone.mapMatchedCoordinates);
      }
     else if (milestone.waypointIndex == null && milestoneStatus == MilestoneStatus.MISSED) {
          // For example, when transport mode changes due to a ferry a system-defined waypoint may have been added.
          Log.d(TAG, "A system-defined waypoint was missed, index of waypoint: " + milestone.mapMatchedCoordinates);
      }
}
});
  

The onMilestoneStatusUpdated() method provides a Milestone instance that contains the information about the passed or missed waypoints along the route. Note that only stopover waypoints are included. Also, the destination waypoint is included and any other stopover waypoint that was added by a user. In addition, waypoints added by the HERE SDK are included, for example, when there is a need to take a ferry. However, the first waypoint - which is the starting point of your trip - is excluded. Waypoints of type passThrough are also excluded.

A Milestone includes an index that refers to the waypoint list set by the user when calculating the route. If it is not available, then the Milestone refers to a waypoint that was set during the route calculation - for example, when an additional stopover was included by the routing algorithm to indicate that a ferry must be taken.

The MilestoneStatus enum indicates if the corresponding Milestone has been reached or missed.

Map Prefetching

SDK 3.x

Map data prefetching in SDK 3.x is done through the MapDataPrefetcher.

Copied
        // Define the area to prefetch
GeoCoordinate center = new GeoCoordinate(52.530932, 13.384915);
double radius = 5000; // in meters
MapDataPrefetcher.Request request = MapDataPrefetcher.getInstance().fetchMapData(center, radius);

MapDataPrefetcher.getInstance().addListener(new MapDataPrefetcher.Adapter() {
    @Override
    public void onStatus(int requestId, PrefetchStatus status) {
        if (requestId == request.getId() && status == PrefetchStatus.PREFETCH_SUCCESS) {
            // Prefetching succeeded
        }
    }
});
  
SDK 4.x

SDK 4.x introduces the MapDownloader for offline map data management, including prefetching.

Copied
        MapDownloader mapDownloader = new MapDownloader();
mapDownloader.fetchMapData(new GeoBox(new GeoCoordinates(startLatitude, startLongitude), new GeoCoordinates(endLatitude, endLongitude)), new FetchMapDataCallback() {
    @Override
    public void onFetchMapDataCompleted(@Nullable MapError mapError) {
        if (mapError == null) {
            // Map data prefetching completed successfully
        } else {
            // Handle map data prefetching error
        }
    }
});
mapDownloader.downloadRegions(geoBox, new DownloadRegionsCallback() {
    @Override
    public void onDownloadCompleted(@NonNull MapError mapError) {
        if (mapError == MapError.NONE) {
            // Map data download succeeded
        }
    }
});
  

Consent Screen for Positioning

SDK 3.x

Consent for positioning in SDK 3.x is typically managed at the application level, without a dedicated HERE SDK API.

Copied
        if (!hasLocationPermission()) {
    requestLocationPermission();
} else {
    startPositioning();
}

private boolean hasLocationPermission() {
    return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}

private void requestLocationPermission() {
    ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_LOCATION_PERMISSION);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_CODE_LOCATION_PERMISSION && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        startPositioning();
    } else {
        // Show consent screen or information dialog
    }
}

private void startPositioning() {
    // Initialize PositioningManager and start positioning
    PositioningManager positioningManager = PositioningManager.getInstance();
   positioningManager.start(PositioningManager.LocationMethod.GPS_NETWORK);
}
  
SDK 4.x

SDK 4.x requires explicit user consent for positioning, which can be managed through the LocationEngine.

Copied
        ConsentEngine consentEngine;

try {
    consentEngine = new ConsentEngine();
} catch (InstantiationErrorException e) {
    throw new RuntimeException("Initialization of ConsentEngine failed: " + e.getMessage());
}

// Request user consent
button.setOnClickListener(view -> consentEngine.requestUserConsent());

// Refresh the displayed consent state
switch(consentEngine.getUserConsentState()) {
    case GRANTED:
       consentStateTextView.setText(R.string.consent_state_granted);
        break;
    case DENIED:
    case NOT_HANDLED:
    case REQUESTING:
       consentStateTextView.setText(R.string.consent_state_denied);
        break;
}
  

Before starting location updates, it checks the user's consent state using the ConsentEngine. If consent has not been handled, it requests consent. Once consent is confirmed, or if it was already granted, the app proceeds with location updates.

Copied
        if (consentEngine.getUserConsentState() == Consent.UserReply.NOT_HANDLED) {
    consentEngine.requestUserConsent();
} else {
    startLocating();
}
private void startLocating() {
   locationEngine.start(LocationAccuracy.BEST_AVAILABLE);
}
  

Calculating Time-To-Arrival (TTA)

SDK 3.x

TTA can be calculated using the NavigationManager by accessing route information during navigation.

Copied
        NavigationManager navigationManager = NavigationManager.getInstance();
Route route = // Assume this is your current route
long ttaExcludingTraffic = navigationManager.getTta(route, false).getDuration();
long ttaIncludingTraffic = navigationManager.getTta(route, true).getDuration();
  
SDK 4.x

Initially, you can calculate the ETA using the route's duration and traffic delay information. This gives you a snapshot based on the route's current state.

Copied
        LocationTime eta = route.getArrivalLocationTime();
long estimatedTravelTimeInSeconds = route.getDuration().getSeconds();
long estimatedTrafficDelayInSeconds = route.getTrafficDelay().getSeconds();
int lengthInMeters = route.getLengthInMeters();

Log.d(TAG, "Initial ETA (seconds): " + estimatedTravelTimeInSeconds);
Log.d(TAG, "Initial Traffic Delay (seconds): " + estimatedTrafficDelayInSeconds);
Log.d(TAG, "Route Length (meters): " + lengthInMeters);

  

RouteProgressListener for Updated ETA

Implement RouteProgressListener to get updates on the estimated travel time, including any traffic delays known at the route calculation time. Remember, this won't automatically account for new traffic conditions encountered after starting navigation.

Copied
        visualNavigator.addRouteProgressListener(routeProgress -> {
    List<SectionProgress> sectionProgressList = routeProgress.sectionProgress;
    SectionProgress lastSectionProgress = sectionProgressList.get(sectionProgressList.size() - 1);
    Log.d(TAG, "Estimated travel time including traffic delay: " + lastSectionProgress.remainingDuration.getSeconds());
    Log.d(TAG, "Traffic delay ahead in seconds: " + lastSectionProgress.trafficDelay.getSeconds());
});

  

Time-To-Arrival (TTA) with Traffic Updates

First, ensure your route calculation considers traffic by setting the trafficOptimizationMode in your RouteOptions. This is crucial for the initial route calculation to include traffic delays.

Copied
        RouteOptions routeOptions = new RouteOptions();
routeOptions.trafficOptimizationMode = TrafficOptimizationMode.ENABLED;
// Set other route options as needed
  

Updating ETA Based on Real-time Traffic

To update the ETA based on real-time traffic, you need to periodically refresh the route. This can be achieved by recalculating the route with the current location as the start point and the original destination, using the same RouteOptions that enabled traffic optimization.

Copied
        // Method to refresh the route for traffic updates
public void refreshRouteForTrafficUpdates(GeoCoordinates currentLocation, GeoCoordinates destination) {
    RoutingEngine routingEngine = new RoutingEngine();
    List<Waypoint> waypoints = new ArrayList<>();
    waypoints.add(new Waypoint(currentLocation));
    waypoints.add(new Waypoint(destination));

    routingEngine.calculateRoute(
        waypoints,
        routeOptions,
        (routingError, routes) -> {
            if (routingError == null) {
                Route newRoute = routes.get(0); // Assuming the first route is the most optimal
               visualNavigator.setRoute(newRoute);

                // Log the updated ETA
                long updatedETAInSeconds = newRoute.getDuration().getSeconds();
                Log.d(TAG, "Updated ETA (seconds): " + updatedETAInSeconds);
            } else {
                Log.e(TAG, "Failed to refresh route: " + routingError.toString());
            }
        }
    );
}
  

Dynamic Routing with Automatic Traffic Avoidance

Copied
        //Enabling Route Handle

CarOptions routingOptions = new CarOptions();
routingOptions.routeOptions.enableRouteHandle = true; // Essential for dynamic routing.

Configuring and Starting Dynamic Routing
DynamicRoutingEngineOptions dynamicRoutingOptions = new DynamicRoutingEngineOptions();
dynamicRoutingOptions.minTimeDifference = Duration.ofSeconds(1);
dynamicRoutingOptions.minTimeDifferencePercentage = 0.1;
dynamicRoutingOptions.pollInterval = Duration.ofMinutes(5);

DynamicRoutingEngine dynamicRoutingEngine;
try {
    dynamicRoutingEngine = new DynamicRoutingEngine(dynamicRoutingOptions);
} catch (InstantiationErrorException e) {
    throw new RuntimeException("Initialization of DynamicRoutingEngine failed: " + e.error.name());
}

dynamicRoutingEngine.start(route, new DynamicRoutingListener() {
    @Override
    public void onBetterRouteFound(@NonNull Route newRoute, int etaDifferenceInSeconds, int distanceDifferenceInMeters) {
        Log.d(TAG, "Better route found. ETA difference: " + etaDifferenceInSeconds + " seconds.");
    }

    @Override
    public void onRoutingError(@NonNull RoutingError routingError) {
        Log.d(TAG, "Routing error: " + routingError.name());
    }
});
  


Loading Map Schemes

SDK 3.x

Map schemes in SDK 3.x are set directly on the Map object.

Copied
        Map map = mapView.getMap();
map.setMapScheme(Map.Scheme.NORMAL_DAY);
  
SDK 4.x

In SDK 4.x, map schemes are applied through the MapScene API.

Copied
        MapView mapView = // Your MapView instance
mapView.getMapScene().loadScene(MapScheme.NORMAL_DAY, new LoadSceneCallback() {
    @Override
    public void onLoadScene(@Nullable MapError mapError) {
        if (mapError == null) {
            // Map scheme loaded successfully
        }
    }
});
  

Disposing SDK

SDK 3.x

Disposing or cleaning up the SDK in SDK 3.x typically involves stopping any ongoing navigation or positioning services and releasing resources.

Copied
        NavigationManager.getInstance().stop();
PositioningManager.getInstance().stop();
// No explicit SDK dispose method in SDK 3.x
  
SDK 4.x

SDK 4.x requires disposing of the SDKNativeEngine instance when the application is terminated or when it's no longer needed.

Copied
        SDKNativeEngine sdkNativeEngine = SDKNativeEngine.getSharedInstance();
if (sdkNativeEngine != null) {
    sdkNativeEngine.dispose();
}
  

Turn-By-Turn Navigation and Speed Limits

SDK 3.x

Turn-by-turn navigation in SDK 3.x is managed by starting navigation on the NavigationManager with a calculated route. Speed limits are accessed through navigation listeners.

Copied
        NavigationManager navigationManager = NavigationManager.getInstance();

// Adding a NavigationManagerEventListener to listen for speed limit changes
navigationManager.addNavigationManagerEventListener(new WeakReference<NavigationManager.NavigationManagerEventListener>(
    new NavigationManager.NavigationManagerEventListener() {
        @Override
        public void onSpeedLimitExceeded(SpeedLimit speedLimit) {
            // This method is called when the current speed exceeds the current speed limit.
            double currentSpeedLimit = speedLimit.getCurrentSpeedLimit();
            // Handle the speed limit exceeded event, e.g., notify the user
        }

        @Override
        public void onSpeedLimitReached(SpeedLimit speedLimit) {
            // This method is called when the current speed reaches the speed limit.
            // It can be used to notify the user that they are now driving at the speed limit.
        }
    }));

// Start navigation with a previously calculated route
navigationManager.startNavigation(route);
  

In this Premium SDK 3.x version, the SpeedLimit object provides the current speed limit on the road segment the user is currently on. The onSpeedLimitExceeded and onSpeedLimitReached methods are triggered based on the user's speed relative to these limits.

SDK 4.x

In SDK 4.x, turn-by-turn navigation is handled by setting a route on the VisualNavigator and listening for navigation events. Speed limits are part of the NavigationStatus.

Copied
        VisualNavigator visualNavigator = new VisualNavigator();
visualNavigator.setRoute(route); // 'route' is a Route object from route calculation.

if (usingMapView) {
    visualNavigator.startRendering(mapView); // For visual navigation.
}

visualNavigator.addNavigationUpdateListener(navigationUpdate -> {
    Log.d(TAG, "Next maneuver: " + navigationUpdate.getNextManeuver().getAction().name());
});

  

In SDK 4.x, by implementing the SpeedLimitListener you can receive events on the speed limits that are available along a road. This allows for more nuanced speed limit handling, especially for applications catering to different vehicle types.

Copied
        // Notifies on the current speed limit valid on the current road.
visualNavigator.setSpeedLimitListener(new SpeedLimitListener() {
    @Override
    public void onSpeedLimitUpdated(@NonNull SpeedLimit speedLimit) {
        Double currentSpeedLimit = getCurrentSpeedLimit(speedLimit);

        if (currentSpeedLimit == null) {
            Log.d(TAG, "Warning: Speed limits unknown, data could not be retrieved.");
        } else if (currentSpeedLimit == 0) {
            Log.d(TAG, "No speed limits on this road! Drive as fast as you feel safe ...");
        } else {
            Log.d(TAG, "Current speed limit (m/s):" + currentSpeedLimit);
        }
    }
});

private Double getCurrentSpeedLimit(SpeedLimit speedLimit) {

    // Note that all values can be null if no data is available.

    // The regular speed limit if available. In case of unbounded speed limit, the value is zero.
    Log.d(TAG,"speedLimitInMetersPerSecond: " + speedLimit.speedLimitInMetersPerSecond);

    // A conditional school zone speed limit as indicated on the local road signs.
    Log.d(TAG,"schoolZoneSpeedLimitInMetersPerSecond: " + speedLimit.schoolZoneSpeedLimitInMetersPerSecond);

    // A conditional time-dependent speed limit as indicated on the local road signs.
    // It is in effect considering the current local time provided by the device's clock.
    Log.d(TAG,"timeDependentSpeedLimitInMetersPerSecond: " + speedLimit.timeDependentSpeedLimitInMetersPerSecond);

    // A conditional non-legal speed limit that recommends a lower speed,
    // for example, due to bad road conditions.
    Log.d(TAG,"advisorySpeedLimitInMetersPerSecond: " + speedLimit.advisorySpeedLimitInMetersPerSecond);

    // A weather-dependent speed limit as indicated on the local road signs.
    // The HERE SDK cannot detect the current weather condition, so a driver must decide
    // based on the situation if this speed limit applies.
    Log.d(TAG,"fogSpeedLimitInMetersPerSecond: " + speedLimit.fogSpeedLimitInMetersPerSecond);
    Log.d(TAG,"rainSpeedLimitInMetersPerSecond: " + speedLimit.rainSpeedLimitInMetersPerSecond);
    Log.d(TAG,"snowSpeedLimitInMetersPerSecond: " + speedLimit.snowSpeedLimitInMetersPerSecond);

    // For convenience, this returns the effective (lowest) speed limit between
    // - speedLimitInMetersPerSecond
    // - schoolZoneSpeedLimitInMetersPerSecond
    // - timeDependentSpeedLimitInMetersPerSecond
    return speedLimit.effectiveSpeedLimitInMetersPerSecond();
}

  

Speed Warning Events

The onSpeedWarningStatusChanged() method will notify as soon as the driver exceeds the current speed limit allowed. It will also notify as soon as the driver is driving slower again after exceeding the speed limit:

Copied
        // Notifies when the current speed limit is exceeded.
visualNavigator.setSpeedWarningListener(new SpeedWarningListener() {
    @Override
    public void onSpeedWarningStatusChanged(SpeedWarningStatus speedWarningStatus) {
        if (speedWarningStatus == SpeedWarningStatus.SPEED_LIMIT_EXCEEDED) {
            // Driver is faster than current speed limit (plus an optional offset).
            // Play a notification sound to alert the driver.
            // Note that this may not include temporary special speed limits, see SpeedLimitListener.
            Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
            Ringtone ringtone = RingtoneManager.getRingtone(context, ringtoneUri);
            ringtone.play();
        }

        if (speedWarningStatus == SpeedWarningStatus.SPEED_LIMIT_RESTORED) {
            Log.d(TAG, "Driver is again slower than current speed limit (plus an optional offset).");
        }
    }
});
  

Optionally, you can define an offset that is added to the speed limit value. You will be notified only when you exceed the speed limit, including the offset. Below, we define two offsets, one for lower and one for higher speed limits. The boundary is defined by highSpeedBoundaryInMetersPerSecond:

Copied
        private void setupSpeedWarnings() {
    SpeedLimitOffset speedLimitOffset = new SpeedLimitOffset();
   speedLimitOffset.lowSpeedOffsetInMetersPerSecond = 2;
   speedLimitOffset.highSpeedOffsetInMetersPerSecond = 4;
   speedLimitOffset.highSpeedBoundaryInMetersPerSecond = 25;

    visualNavigator.setSpeedWarningOptions(new SpeedWarningOptions(speedLimitOffset));
}
  

Here we set the highSpeedBoundaryInMetersPerSecond to 25 m/s: If a speed limit sign is showing a value above 25 m/s, the offset used is highSpeedOffsetInMetersPerSecond. If it is below 25 m/s, the offset used is lowSpeedOffsetInMetersPerSecond.

For the example values used above,

  • if the speed limit on the road is 27 m/s, the (high) speed offset used is 4 m/s. This means we will only receive a warning notification when we are driving above 31 m/s = 27 m/s + 4 m/s. The highSpeedOffsetInMetersPerSecond is used, as the current speed limit is greater than highSpeedBoundaryInMetersPerSecond.
  • if the speed limit on the road is 20 m/s, the (low) speed offset used is 2 m/s. This means we will only receive a warning notification when we are driving above 22 m/s = 20 m/s + 2 m/s. The lowSpeedOffsetInMetersPerSecond is used, as the current speed limit is smaller than highSpeedBoundaryInMetersPerSecond.

You can also set negative offset values. This may be useful if you want to make sure you never exceed the speed limit by having a buffer before you reach the limit. Note that you will never get notifications when you drive too slow, for example, slower than a defined offset - unless a previous speed warning has been restored.

Place Search

SDK 3.x

In SDK 3.x, performing a place search involves creating a SearchRequest, configuring it with search parameters, and handling the results asynchronously.

Copied
        // Initialize the SearchRequest with a query string and center point.
SearchRequest searchRequest = new SearchRequest("pizza");
searchRequest.setSearchCenter(new GeoCoordinate(52.5200, 13.4050));
searchRequest.setRadius(5000); // Search within a 5km radius

// Execute the search request.
searchRequest.execute(new ResultListener<DiscoveryResultPage>() {
    @Override
    public void onCompleted(DiscoveryResultPage resultPage, ErrorCode errorCode) {
        if (errorCode == ErrorCode.NONE) {
            // Process the search results.
            for (DiscoveryResult result : resultPage.getItems()) {
                if (result.getResultType() == DiscoveryResult.ResultType.PLACE) {
                    PlaceLink placeLink = (PlaceLink) result;
                   System.out.println("Place found: " + placeLink.getTitle() + ", Location: " + placeLink.getPosition());
                }
            }
        } else {
            System.err.println("Error during place search: " + errorCode.toString());
        }
    }
});
  
SDK 4.x

SDK 4.x simplifies the search process with a unified SearchEngine and provides detailed search options.

Copied
        SearchEngine searchEngine;
try {
    searchEngine = new SearchEngine();
} catch (InstantiationErrorException e) {
    throw new RuntimeException("Initialization of SearchEngine failed: " + e.error.name());
}

// Define the search query and area.
String searchTerm = "pizza";
GeoBox searchArea = new GeoBox(new GeoCoordinates(52.5200, 13.4050), new GeoCoordinates(52.5300, 13.4150));
TextQuery textQuery = new TextQuery(searchTerm, searchArea);

// Define search options, such as language and max items.
SearchOptions searchOptions = new SearchOptions(LanguageCode.EN_US, 10);

// Perform the search.
searchEngine.search(textQuery, searchOptions, new SearchCallback() {
    @Override
    public void onSearchCompleted(SearchError searchError, List<Place> list) {
        if (searchError != null) {
            System.err.println("Search error: " + searchError.toString());
            return;
        }

        // Process search results.
        for (Place place : list) {
            System.out.println("Place found: " + place.getTitle() + ", Address: " + place.getAddress().addressText);
        }
    }
});
  

Geocoding

SDK 3.x

Geocoding in SDK 3.x converts a textual address into geographical coordinates.

Copied
        GeocodeRequest geocodeRequest = new GeocodeRequest("Invalidenstr 116, Berlin");
geocodeRequest.setSearchArea(new GeoCoordinate(52.5200, 13.4050), 5000);
geocodeRequest.execute(new ResultListener<List<Location>>() {
    @Override
    public void onCompleted(List<Location> locations, ErrorCode errorCode) {
        if (errorCode == ErrorCode.NONE && locations != null) {
            for (Location location : locations) {
               System.out.println("Geocoded location: " + location.getCoordinate());
            }
        } else {
            System.err.println("Geocoding error: " + errorCode.toString());
        }
    }
});
  
SDK 4.x

SDK 4.x streamlines geocoding with the SearchEngine.

Copied
        // Assuming searchEngine is already initialized
String address = "Invalidenstr 116, Berlin";
GeoCoordinates queryCoordinates = new GeoCoordinates(52.5200, 13.4050); // Optional: hint for the geocoding query

AddressQuery addressQuery = new AddressQuery(address, queryCoordinates);
SearchOptions options = new SearchOptions(LanguageCode.EN_US, 1); // Limit to one result for simplicity

searchEngine.search(addressQuery, options, (searchError, places) -> {
    if (searchError != null) {
        System.err.println("Geocoding error: " + searchError.toString());
        return;
    }

    for (Place place : places) {
        System.out.println("Geocoded address: " + place.getAddress().addressText + ", Location: " + place.getGeoCoordinates());
    }
});
  

Auto-Suggest

SDK 3.x

Auto-suggest in SDK 3.x provides suggestions based on partial user input.

Copied
        SuggestionRequest suggestionRequest = new SuggestionRequest("pizz");
suggestionRequest.setSearchCenter(new GeoCoordinate(52.5200, 13.4050));
suggestionRequest.execute(new ResultListener<List<Suggestion>>() {
    @Override
    public void onCompleted(List<Suggestion> suggestions, ErrorCode errorCode) {
        if (errorCode == ErrorCode.NONE) {
            for (Suggestion suggestion : suggestions) {
               System.out.println("Suggestion: " + suggestion.getTitle());
            }
        } else {
           System.err.println("Auto-suggest error: " + errorCode.toString());
        }
    }
});
  
SDK 4.x
Copied
        SDK 4.x enhances auto-suggest functionality with the SearchEngine.
// Assuming searchEngine is already initialized
GeoCoordinates center = new GeoCoordinates(52.5200, 13.4050);
SearchOptions searchOptions = new SearchOptions(LanguageCode.EN_US, 5); // Limit to 5 suggestions

searchEngine.suggest(new TextQuery("pizz", center), searchOptions, (searchError, suggestions) -> {
    if (searchError != null) {
        System.err.println("Auto-suggest error: " + searchError.toString());
        return;
    }

    for (Suggestion suggestion : suggestions) {
        System.out.println("Suggestion: " + suggestion.getTitle() + ", Address: " + (suggestion.getPlace() != null ? suggestion.getPlace().getAddress().addressText : "N/A"));
    }
});
  

Conclusion

Migrating from HERE SDK 3.x Premium SDK to 4.x Navigate SDK represents a significant step forward in the development of location-based services. The latest version of the SDK introduces a host of new features, optimizations, and a more streamlined approach to handling location data, navigation, and map interactions. By following this guide, developers can ensure a smooth transition to SDK 4.x, enabling them to take full advantage of improved performance, enhanced accuracy, and the latest in mapping technology. As the digital landscape continues to evolve, staying updated with the latest SDK versions is key to offering users a seamless, efficient, and engaging experience.

To further support this transition, we've created reference applications for iOS and Android, as well as for developers using Flutter. These examples are designed to showcase the capabilities of the 4.x Navigate SDK and provide practical insights into its implementation:

 

Embrace the future of location-based services with HERE SDK 4.x and unlock the full potential of your mobile applications.

Sachin Jonda

Sachin Jonda

Lead Onboarding Engineer

Have your say

Sign up for our newsletter

Why sign up:

  • Latest offers and discounts
  • Tailored content delivered weekly
  • Exclusive events
  • One click to unsubscribe