Get routes for electric vehicles
Electric vehicle (EV) usage and sales continue growing around the world. How can HERE help to provide the best routes for electric vehicles?
-
HERE EV Routing delivers optimized routes for EVs to get from A to B, minimizing the number of charging stops and optimizing battery charge times (based on the vehicle’s consumption model). It also takes into account a number of factors when planning trips, including topography, road geometry, real-time traffic information and traffic patterns.
-
HERE analyses the different charging times across the available charging stations and tries to optimize the combined travel time, including driving and charging along the way.
Routing EV is based on HERE Routing API v8 - EV Routing. An example of a transaction based on EV Routing is: Calculate a route with the RoutingEngine with RoutingOptions and ElectricVehicleOptions with ensureReachability set to true.
NoteFor information about the pricing of EV Routing, see the HERE Base Plan Pricing. If you are using the Navigate license or have other questions about pricing, contact us.
Define a waypoint as a charging stop
The Waypoint class allows you to specify a user-planned ChargingStop in addition to charging stops that are added by the engine. User-planned charging stops are locations that users can optionally specify in advance as per their own preferences; these are places where they plan to charge a vehicle during their journey. To define a ChargingStop, you need to set the following fields in the BatterySpecification:
totalCapacityInKilowattHoursinitialChargeInKilowattHourschargingCurve
Please ensure that all these fields are set. If any of them are missing, the route calculation will not succeed and will return an INVALID_PARAM error.
The following parameters define a ChargingStop:
- powerInKilowatts: The rated power of the connector, in kilowatts (kW).
- currentInAmperes: The rated current of the connector, in amperes (A).
- voltageInVolts: The rated voltage of the connector, in volts (V).
- chargingSupplyType: The type of power supply (for example, AC or DC).
- minimumDuration: The minimum duration (in seconds) the user plans to charge at the station.
- maximumDuration: The maximum duration (in seconds) the user plans to charge at the station.
NoteThe
RoutingEnginedoes not verify the correctness of the provided charging stop. Once the user defines the charging stop, it treats the specified location as the charging stop and uses the provided values.
You can create a ChargingStop object using the following code:
let plannedChargingStopWaypoint = Waypoint(coordinates: chargingStopGeoCoordinates)
// The rated power of the connector, in kilowatts (kW).
let powerInKilowatts: Double = 350.0
// The rated current of the connector, in amperes (A).
let currentInAmperes: Double = 350.0
// The rated voltage of the connector, in volts (V).
let voltageInVolts: Double = 1000.0
// The minimum duration (in seconds) the user plans to charge at the station.
let minimumDuration = TimeInterval(3000)
// The maximum duration (in seconds) the user plans to charge at the station.
let maximumDuration = TimeInterval(4000)
// Add a user-defined charging stop.
//
// Note: To specify a ChargingStop, you must also set totalCapacityInKilowattHours,
// initialChargeInKilowattHours, and chargingCurve using BatterySpecifications in ElectricVehicleOptions.
// If any of these values are missing, the route calculation will fail with an invalid parameter error.
let plannedChargingStop = ChargingStop(
powerInKilowatts: powerInKilowatts,
currentInAmperes: currentInAmperes,
voltageInVolts: voltageInVolts,
supplyType: .dc,
minDuration: minimumDuration,
maxDuration: maximumDuration
)To set the planned charging stop as a waypoint, use the following code:
plannedChargingStopWaypoint.chargingStop = plannedChargingStopIf the provided specifications indicate that a charging stop is required, the resulting route may include this waypoint as a RoutePlace with a non-null ChargingStation member. However, if ElectricVehicleOptions.ensureReachability is not set to true, the route calculation may still consider the planned charging stops.
If you prefer to plan charging along the entire route without relying on the RoutingEngine, you may set all the necessary charging stops as user-planned stops. Setting ElectricVehicleOptions.ensureReachability to false ensures no stations are automatically added.
Using one or more user-planned charging stops doesn't mean that all charging stops on the route must be added by the user. You can combine user-planned stops with the ones added automatically. To do so, set ElectricVehicleOptions.ensureReachability to true. This automatically adds more charging stops to ensure the vehicle can reach the selected destination.
If any destination or charging stop is unreachable, the RoutingEngine will indicate it using SectionNotice.
NoteThis feature is currently not supported by the
OfflineRoutingEngine.
Calculate EV routes
Getting EV routes is simple. Similar to getting car or truck routes, for EV routing you just need to add electric vehicle specific route options. This way you can get routes for electric vehicles in the same way as for other transport modes. Just specify RoutingOptions with ElectricVehicleOptions and add them to the calculateRoute() method:
routingEngine.calculateRoute(with: [Waypoint(coordinates: startGeoCoordinates!),
Waypoint(coordinates: destinationGeoCoordinates!)],
options: getEVRoutingOptions()) { (routingError, routes) in
if let error = routingError {
self.showDialog(title: "Error while calculating a route:", message: "\(error)")
return
}
// Use routes from list.
}Define EV RoutingOptions
By default, RoutingOptions will not include the required EV parameters to ensure that a destination can be reached without running out of energy.
To ensure this, you need to set the required parameters that may add charging stations to the route - while still optimizing the result for overall travel time.
Below you can see an example how to create such options:
private func applyEMSPPreferences(evOptions: inout ElectricVehicleOptions) {
// You can get a list of all E-Mobility Service Providers and their partner IDs by using the request described here:
// https://www.here.com/docs/bundle/ev-charge-points-api-developer-guide/page/topics/example-charging-station.html.
// The RoutingEngine will follow the priority order you specify when calculating routes, so try to specify at least most preferred providers.
// Note that this may impact the route geometry.
// Most preferred provider for route calculation: As an example, we use "Jaguar Charging" referenced by the partner ID taken from above link.
let preferredProviders: [String] = ["3379b852-cca5-11ed-8856-42010aa40002"]
// Example code for a least preferred provider.
let leastPreferredProviders: [String] = ["12345678-abcd-0000-0000-000000000000"]
// Alternative provider for route calculation to be used only when no better options are available.
// Example code for an alternative provider.
let alternativeProviders: [String] = ["12345678-0000-abcd-0000-000123456789"]
evOptions.evMobilityServiceProviderPreferences = EVMobilityServiceProviderPreferences()
evOptions.evMobilityServiceProviderPreferences.high = preferredProviders;
evOptions.evMobilityServiceProviderPreferences.low = leastPreferredProviders;
evOptions.evMobilityServiceProviderPreferences.medium = alternativeProviders;
}
private func getEVRoutingOptions() -> RoutingOptions {
var routingOptions = RoutingOptions()
// Configure a data-driven EV energy consumption model that combines empirically
// derived vehicle parameters with speed and elevation characteristics.
var empiricalConsumptionModel = EmpiricalConsumptionModel()
// The below three options are the minimum you must specify or routing will result in an error.
empiricalConsumptionModel.ascentConsumptionInWattHoursPerMeter = 9
empiricalConsumptionModel.descentRecoveryInWattHoursPerMeter = 4.3
empiricalConsumptionModel.freeFlowSpeedTable = [0: 0.239,
27: 0.239,
60: 0.196,
90: 0.238]
var evOptions = ElectricVehicleOptions()
// Set the empirical consumption model so the EV routing
// can estimate energy usage based on speed and elevation.
evOptions.empiricalConsumptionModel = empiricalConsumptionModel
// Must be 0 for isoline calculation.
routingOptions.routeOptions.alternatives = 0
// Ensure that the vehicle does not run out of energy along the way
// and charging stations are added as additional waypoints.
evOptions.ensureReachability = true
// The below options are required when setting the ensureReachability option to true
// (AvoidanceOptions need to be empty).
routingOptions.avoidanceOptions = AvoidanceOptions()
routingOptions.routeOptions.speedCapInMetersPerSecond = nil
routingOptions.routeOptions.optimizationMode = .fastest
var batterySpecifications = BatterySpecifications()
batterySpecifications.connectorTypes = [.tesla, .iec62196Type1Combo, .iec62196Type2Combo]
batterySpecifications.totalCapacityInKilowattHours = 80.0
batterySpecifications.initialChargeInKilowattHours = 10.0
batterySpecifications.targetChargeInKilowattHours = 72.0
batterySpecifications.chargingCurve = [0: 239.0,
64: 111.0,
72: 1.0]
evOptions.batterySpecifications = batterySpecifications
// Apply EV mobility service provider preferences (eMSP).
applyEMSPPreferences(evOptions: &evOptions)
routingOptions.evOptions = evOptions
// Note: More EV options are available, the above shows only the minimum viable options.
return routingOptions
}As part of the RoutingOptions we have defined above a consumption model via EmpiricalConsumptionModel, battery specs via BatterySpecifications and optionally, EV mobility service provider preferences.
Define the consumption model
The following parameters define a consumption model to get more accurate results for electric vehicles:
- ascentConsumptionInWattHoursPerMeter: Rate of energy consumed per meter rise in elevation.
- descentRecoveryInWattHoursPerMeter: Rate of energy recovered per meter fall in elevation.
- freeFlowSpeedTable: Function curve specifying consumption rate at a given free flow speed on a flat stretch of road.
- trafficSpeedTable: Function curve specifying consumption rate at a given speed under traffic conditions on a flat stretch of road.
- auxiliaryConsumptionInWattHoursPerSecond: Rate of energy consumed by the vehicle's auxiliary systems (e.g., air conditioning, lights) per second of travel.
A consumption speed table defines the energy consumption rate when the vehicle travels on a straight road without elevation change at a given speed in km/h. It represents a piecewise linear function.
Here is an example of a free flow speed list. On the left you see the speed and on the right, consumption:
- 0: 0.239
- 27: 0.239
- 45: 0.259
- 60: 0.196
- 75: 0.207
- 90: 0.238
- 100: 0.26
- 110: 0.296
- 120: 0.337
- 130: 0.351
In a graph it will look like this:

Screenshot: An example for a consumption speed graph.
You can specify two different consumption speed tables - free flow speed table and traffic speed tables:
- Free flow speed: Describes the energy consumption when traveling at constant speed.
- Traffic speed: Describes the energy consumption when traveling under heavy traffic conditions, for example, when the vehicle is expected to often change the travel speed at a given average speed.
If a trafficSpeedTable is not provided then only the freeFlowSpeedTable is used for calculating speed-related energy consumption.
The EmpiricalConsumptionModel specifies the energy consumption model of the electric vehicle. We also add BatterySpecifications. With these options, additional charging stations may be inserted into the route as additional stopovers - which means that the route will be split into more sections depending on the number of inserted charging stations.
NoteIf you want to include such required charging stations, it is also mandatory to use
OptimizationMode.fastest. You also need to setensureReachabilityto true. WhenensureReachabilityis activated, the route may be adjusted so that required charging stations are along the resulting path and the required charging stations are added asWayPointitems to theroute.Transactions for this feature are counted as "Routing EV" only when this field is set to true. Otherwise, such API calls are billed as normal car route.
All options can influence the time it takes to calculate a route for electric vehicles. Most importantly, a large battery capacity - as set with totalCapacityInKilowattHours - can decrease the need for charging stops - as well as a fully loaded battery when starting the trip - see initialChargeInKilowattHours: a relatively low value means that the route must include charging stations near the beginning. Otherwise, the route calculation may even fail.
Currently, the engine will only include charging stations that are in service. Charging stations that our out-of-service or in maintenance are not considered. However, due to the dynamic situation at charging stations, the engine does not consider if a station is currently occupied or reserved. So, in worst case, it can happen that you arrive at a station and it is currently being used to charge another car.
Note that we also specify the available battery connector types, so that charging stations that do not provide a compatible connector to charge the car will be excluded.
NoteUsually, you can take the consumption and battery information from the car itself or consult the manual or your car manufacturer directly. Note that not all information may be available to you and some information may be only exclusively known by the manufacturer. Either way, the route calculation process will take the provided specifications into account and missing values will be filled with suitable default values.
Define the ev mobility service provider preferences
When calculating routes, the RoutingEngine allows specifying priority preferences for evMobilityServiceProviderPreferences to optimize the route based on user requirements. Note that this feature requires an active online connection.
An e-Mobility Service Provider (eMSP) is a company or organization that provides services to electric vehicle drivers, focusing on enabling access to and use of EV charging infrastructure. eMSPs serve as intermediaries between EV drivers and charging station networks.
eMSPs preferences allow users to select the preferred partner charging stations.
A most preferred eMSPs can be selected to ensure it is prioritized during route calculation, while a least preferred provider can be included with lower priority. Alternative providers can also be specified, to be considered only when no better options are available. Additionally, certain providers can be excluded entirely from the route calculation process.
Specifying evMobilityServiceProviderPreferences may impact the resulting route geometry. It is not mandatory, but it can help to improve the user experience. For example, users that want to exclude certain providers can avoid that a route will include such providers.
Find charging stations along the route
Especially for longer journeys with electric vehicles, it is important to plan for charging stops along the way. After all, charging stations are much less common than petrol stations. With the above options, the RoutingEngine tries to find the fastest route, i.e., one with the lowest overall time consumed to reach the destination, while ensuring that the vehicle does not run out of energy along the way.
The result of the calculation is a route optimized for electric vehicles - instead of just adding any charging stations found along the way - as we have shown in the Search Along a Route section.
Once the route is calculated, you can gather more useful information. The below code snippet will log the estimated energy consumption per Section and list the actions you need to take in order to charge the battery - if needed:
// Find inserted charging stations that are required for this route.
// Note that this example assumes only one start waypoint and one destination waypoint.
// By default, each route has one section.
let additionalSectionCount = route.sections.count - 1
if (additionalSectionCount > 0) {
// Each additional waypoint splits the route into two sections.
print("Number of required stops at charging stations: \(additionalSectionCount)")
} else {
print("Based on the provided options, the destination can be reached without a stop at a charging station.")
}
var sectionIndex = 0
let sections = route.sections
for section in sections {
print("Estimated net energy consumption in kWh for this section: \(String(describing: section.consumptionInKilowattHours))")
for postAction in section.postActions {
switch postAction.action {
case .chargingSetup:
print("At the end of this section you need to setup charging for \(postAction.duration) s.")
break
case .charging:
print("At the end of this section you need to charge for \(postAction.duration) s.")
break
case .wait:
print("At the end of this section you need to wait for \(postAction.duration) s.")
break
default: fatalError("Unknown post action type.")
}
}
print("Section \(sectionIndex): Estimated battery charge in kWh when leaving the departure place: \(String(describing: section.departurePlace.chargeInKilowattHours))")
print("Section \(sectionIndex): Estimated battery charge in kWh when leaving the arrival place: \(String(describing: section.arrivalPlace.chargeInKilowattHours))")
// Only charging stations that are needed to reach the destination are listed below.
let depStation = section.departurePlace.chargingStation
if depStation != nil && !chargingStationsIDs.contains(depStation?.id ?? "-1") {
print("Section \(sectionIndex), name of charging station: \(String(describing: depStation?.name))")
chargingStationsIDs.append(depStation?.id ?? "-1")
addCircleMapMarker(geoCoordinates: section.departurePlace.mapMatchedCoordinates, imageName: "required_charging.png")
}
let arrStation = section.departurePlace.chargingStation
if arrStation != nil && !chargingStationsIDs.contains(arrStation?.id ?? "-1") {
print("Section \(sectionIndex), name of charging station: \(String(describing: arrStation?.name))")
chargingStationsIDs.append(arrStation?.id ?? "-1")
addCircleMapMarker(geoCoordinates: section.arrivalPlace.mapMatchedCoordinates, imageName: "required_charging.png")
}
sectionIndex += 1
}Note that postAction.duration provides the estimated time in seconds that takes to charge the battery. This time is included in the overall route calculation and the estimated time of arrival (ETA).
You can also log per-span consumption and verify battery reachability using the following helpers:
private func logSpanConsumption(route: Route) {
var sectionIndex = 0
for section in route.sections {
var spanIndex = 0
for span in section.spans {
if let kWh = span.consumptionInKilowattHours {
print("EVSpan: Section \(sectionIndex) span \(spanIndex) consumption: \(String(format: "%.3f", kWh)) kWh")
} else {
print("EVSpan: Section \(sectionIndex) span \(spanIndex) consumption: n/a")
}
spanIndex += 1
}
sectionIndex += 1
}
}private func logSectionArrivalCharge(route: Route) {
var sectionIndex = 0
var lastSectionArrivalChargeKWh: Double?
for routeSection in route.sections {
let remainingChargeAtArrivalKWh = routeSection.arrivalPlace.chargeInKilowattHours
if let charge = remainingChargeAtArrivalKWh {
print("EVArrival: Section \(sectionIndex): remaining charge upon arrival = \(String(format: "%.2f", charge)) kWh")
lastSectionArrivalChargeKWh = charge
} else {
print("EVArrival: Section \(sectionIndex): remaining charge upon arrival not available")
}
sectionIndex += 1
}
if let finalCharge = lastSectionArrivalChargeKWh {
print("EVArrival: Final destination arrival charge = \(String(format: "%.2f", finalCharge)) kWh")
if finalCharge < 0.0 {
print("EVArrival: Destination not reachable with the current battery configuration.")
}
} else {
print("EVArrival: No arrival charge data available for any section in this route.")
}
}To verify whether a user-planned charging stop was included in the calculated route, store the waypoint and compare it to the route's charging stations:
private var lastPlannedChargingStopWaypoint: Waypoint?
private func verifyAndLogPlannedStopOutcome(route: Route) {
if lastPlannedChargingStopWaypoint == nil {
print("EVChargingStop: No user-planned charging stop to verify.")
return
}
let coordinateMatchRadiusMeters = 200.0
var isStopIncludedInRoute = false
var matchedChargingStationName: String?
let plannedStopCoordinates = lastPlannedChargingStopWaypoint!.coordinates
for routeSection in route.sections {
if let departureStation = routeSection.departurePlace.chargingStation {
let departureStationCoordinates = routeSection.departurePlace.mapMatchedCoordinates
if departureStationCoordinates.distance(to: plannedStopCoordinates) <= coordinateMatchRadiusMeters {
isStopIncludedInRoute = true
matchedChargingStationName = departureStation.name
break
}
}
if let arrivalStation = routeSection.arrivalPlace.chargingStation {
let arrivalStationCoordinates = routeSection.arrivalPlace.mapMatchedCoordinates
if arrivalStationCoordinates.distance(to: plannedStopCoordinates) <= coordinateMatchRadiusMeters {
isStopIncludedInRoute = true
matchedChargingStationName = arrivalStation.name
break
}
}
}
if isStopIncludedInRoute {
let suffix = matchedChargingStationName != nil ? " (≈ \(matchedChargingStationName!))." : "."
print("EVChargingStop: User-defined charging stop was included in the calculated route\(suffix)")
} else {
print("EVChargingStop: User-defined charging stop was adjusted or replaced during route optimization.")
}
}Below is a screenshot of how a resulting route may look.

Screenshot: Showing a route for electric vehicles
Here you can see that the route required two stops at a charging station - indicated by red markers. The route therefore contains three sections as each charging station splits the route - like when inserting additional waypoints.
The first section includes a post action, which describes the charging stop. It contains the information on the expected arrival charge among other information.
Unless specified otherwise, the energy consumption is assumed to be in Wh.
Search for EV charging pools
If you don't plan to follow a Route, we got you covered, too: with the online SearchEngine you can also search for EVChargingStation connectors that are part of a EVChargingPool. A charging pool for electric vehicles is an area equipped with one or more charging stations.
Use PlaceCategory.businessAndServicesEVChargingStation to find pools. Alternatively, you can use a free text query like "charging station". In the Details of a Place result you can find the list of found pools containing stations, if any.
You can also search for stations with the OfflineSearchEngine, if this engine is supported by your license of the HERE SDK.
The HERE SDK also allows populating the results with additional charging station parameters, such as EVChargingPoolDetails and more fields in EVChargingStation. For online use, obtaining these detailed results requires enabling the feature by calling searchEngine.setCustomOption("discover.show", "ev"). Additionally, an extra license is required. For use with the OfflineSearchEngine (if available for your license), no license is required. Please contact us to receive online access. If your credentials are not enabled, a SearchError.forbidden error will indicate the missing license.
Show reachable area
With isoline routing you can visualize the area of reach based on parameters like battery consumption. This is explained in greater detail here.
Try the EV example app
You can find the EVRouting example app on GitHub.
Updated 4 hours ago