Electric Vehicle Features in the HERE SDKs for Android, iOS, and Flutter

By Richard Süselbeck | 18 May 2021

Try HERE Maps

Create a free API key to build location-aware apps and services.

Get Started

It’s been over a year now since we released the latest version of the HERE SDKs for iOS, Android, and Flutter. Since then, we’ve added even more cool new features and capabilities. Today I want to highlight some of those features, specifically those focused on use cases for electric vehicles.

In this blog post we will look at how to:

  • Using the routing feature for electric vehicles
  • Determine the reachable area of an electric vehicle given a remaining range or battery charge
  • Search for charging stations along a route

Before we get started, I want to point out that you can see all of these features in action in our “EVRouting” example in on GitHub. Go ahead and grab this for a running demo plus full code of everything we will be talking about below.

Electric Vehicle Routing

Let’s get started with the Routing API. A call to the Routing API in EV mode is fundamentally the same as any other call to the Routing API, except that we are using an EVCarOptions object to define the vehicle parameters. This does turn out to be a little more involved than usually, as there are quite a few parameters we can set.

Here’s an example for setting the parameters in EVCarOptions. (Note that this example is for Android, but all the features discussed here are also available on the HERE SDKs for Flutter and iOS.)


EVCarOptions evCarOptions = new EVCarOptions();

evCarOptions.consumptionModel.ascentConsumptionInWattHoursPerMeter = 9;
evCarOptions.consumptionModel.descentRecoveryInWattHoursPerMeter = 4.3;
evCarOptions.consumptionModel.freeFlowSpeedTable = new HashMap<Integer, Double>() {{
    put(0, 0.239);
    put(27, 0.239);
    put(60, 0.196);
    put(90, 0.238);
}};

evCarOptions.ensureReachability = true;

evCarOptions.routeOptions.optimizationMode = OptimizationMode.FASTEST;
evCarOptions.routeOptions.alternatives = 0;
evCarOptions.batterySpecifications.connectorTypes =
        new ArrayList<ChargingConnectorType>(Arrays.asList(ChargingConnectorType.TESLA,
                ChargingConnectorType.IEC_62196_TYPE_1_COMBO, ChargingConnectorType.IEC_62196_TYPE_2_COMBO));
evCarOptions.batterySpecifications.totalCapacityInKilowattHours = 80.0;
evCarOptions.batterySpecifications.initialChargeInKilowattHours = 10.0;
evCarOptions.batterySpecifications.targetChargeInKilowattHours = 72.0;
evCarOptions.batterySpecifications.chargingCurve = new HashMap<Double, Double>() {{
    put(0.0, 239.0);
    put(64.0, 111.0);
    put(72.0, 1.0);
}};

The first three parameters are required to make any EV routing call. The free flow table defines the energy consumption of the vehicle as a curve using pairs of speed (km/h) and consumption (Wh/m) values. In addition, the ascent consumption and descent recovery values indicate what effect inclines have on the battery (in Wh/m).

By default, the Routing API will assume that you have enough charge to reach any given destination. Given that range anxiety and charging infrastructure are still issues, we may also want to take the vehicles battery charge and the location of compatible charging points into account. That why thingsget interesting when you turn on ensureReachability. With this parameter, the Routing API will automatically route you to charging points where necessary and even take the charging time into account when calculating arrival time. Neat!

However, when using this feature, you must provide some additional parameters. These include the total capacity of the battery (in kWh), the charging connector types available on the vehicle, and a battery charging curve. This curve is provided as a list of charging rate (in kW), charge level (in kWh) pairs. In addition, you must provide an initial charge level and a target charge level. These are the respective battery charge levels at the first and last waypoints. (Providing a target charge level ensures you don’t strand your users at their destination with an empty battery.)

Check out the SDK documentation for full details on these parameters. The API Reference of the underlying Routing REST API can also be helpful.

EV Reachability

The range of EVs remains a major concern, both when purchasing and using electric vehicles. The Isoline Routing feature of the SDKs allows you to calculate the area an EV can reach given a current battery level.

Let’s say you are in the city center Berlin and you want to calculate which area you can potentially reach with an energy budget of 400 Wh. Once again, using Isoline Routing based on battery charge is very similar to making a standard Isoline Routing call. In fact, you can check out my recent blog post on Isoline Routing with Android for details on how to do this. The only thing we need to change is the range type in the calculation options and provide an appropriate value for battery charge. Here’s a complete example of how to call the Isoline Routing feature with a consumption-based range type.


List<Integer> rangeValues = Collections.singletonList(400);

Integer maxPoints = null;
IsolineOptions.Calculation calculationOptions =
        new IsolineOptions.Calculation(IsolineRangeType.CONSUMPTION_IN_WATT_HOURS, rangeValues, IsolineCalculationMode.BALANCED, maxPoints);
IsolineOptions isolineOptions = new IsolineOptions(calculationOptions, getEVCarOptions());

routingEngine.calculateIsoline(new Waypoint(startGeoCoordinates), isolineOptions, new CalculateIsolineCallback() {
    @Override
    public void onIsolineCalculated(RoutingError routingError, List<Isoline> list) {
        if (routingError != null) {
            showDialog("Error while calculating reachable area:", routingError.toString());
            return;
        }

        Isoline isoline = list.get(0);

        for (GeoPolygon geoPolygon : isoline.getPolygons()) {
            Color fillColor = Color.valueOf(0, 0.56f, 0.54f, 0.5f); // RGBA
            MapPolygon mapPolygon = new MapPolygon(geoPolygon, fillColor);
            mapView.getMapScene().addMapPolygon(mapPolygon);
            mapPolygons.add(mapPolygon);
        }
    }
});

Note that this example shows how to visualize the reachable area on the map, however you can also use the result to match the area against a list of possible customers in a delivery use case, to find charging stations inside the area, and much more.

Charging Stations Along a Route

As we’ve seen above, the Routing API will automatically search for charging points along a route and even integrate them into the route for you if necessary. However, what if we have a given route and want to search for charging points manually? That is where the Search feature of the SDK comes in handy. In addition to searching near a given location, or within a bounding box, it also allows us to search along a GeoCorridor. This corridor can be defined by a GeoPolyline and a radius (in meters). Of course, this is useful for everything from gas stations to restaurants, but in the example below we will search for charging points.


int radiusInMeters = 200;
GeoCorridor routeCorridor = new GeoCorridor(route.getPolyline(), radiusInMeters);
TextQuery textQuery = new TextQuery("charging station", routeCorridor,
        mapView.getCamera().getState().targetCoordinates);

int maxItems = 30;
SearchOptions searchOptions = new SearchOptions(LanguageCode.EN_US, maxItems);
searchEngine.search(textQuery, searchOptions, new SearchCallback() {
    @Override
    public void onSearchCompleted(SearchError searchError, List<Place> items) {
        if (searchError != null) {
            if (searchError == SearchError.POLYLINE_TOO_LONG) {                        
                Log.d("Search", "Route too long or route corridor radius too small.");
            } else {
                Log.d("Search", "No charging stations found along the route. Error: " + searchError);
            }
            return;
        }
        
        Log.d("Search","Search along route found " + items.size() + " charging stations:");
        for (Place place : items) {
            if (chargingStationsIDs.contains(place.getId())) {
                Log.d("Search", "Skipping: This charging station was already required to reach the destination.");
            } else {
        
                addCircleMapMarker(place.getGeoCoordinates(), R.drawable.charging);
                Log.d("Search", place.getAddress().addressText);
            }
        }
    }
});

First, we define a GeoCorridor by providing our route as a polyline (which we could get from the Routing API or any other data source) and the search radius of 200m. This means that we will only search for results which are no more than 200 meters away from our route. Then we simply provide this corridor as a search area for a TextQuery and make a normal search request using this query.

I’ll close with another reminder to check out the “EVRouting” example on GitHub. Also check out our Routing API Demo Client. While this uses the underlying REST API and not the SDK directly, it is a great way to experiment with the various charge and battery parameters, visualize the curves, and see how they affect the routing results.