Backup connector

Illustration on a dark street grid: a lime route leads from a grey pin through a box with curly braces and a panel with a ruler and a clock to a lime pin

When the input has no connection between two elements, JOpt calculates the distance and the driving time itself. This guide explains that default calculation and how a custom backup connector changes it. It is written for developers who use JOpt as a Java library and know nodes, resources and element connections from Basic elements.

What it does

Automatically calculated distances and driving times between nodes and resources are usually sufficient to solve an optimization problem. By default, a direct line between two elements gives the distance. Along this direct line, the driving time follows from an average speed. A backup connector can modify these calculations, account for external conditions, or replace the algorithm that calculates a connection.

Description - How JOptTourOptimizer calculates distances and times

By default, JOpt uses a spherical earth model projected onto a plane for the distance calculation (flat-surface formula).

The flat-surface formula gets less precise the further apart two points are. However, directly connected nodes in an optimized solution are usually not far from one another on the earth's scale, so the flat-surface formula is usually sufficient. For performance, the flat-surface formula is the more efficient of the two.

The Haversine formula is more precise, because it uses the great-circle distance between two points on a sphere. When you compare distances calculated with both formulas, the difference is almost negligible.

Comparing the Haversine and the flat-surface formula

As an example, take a distance of about 400 km between the cities "Cologne" and "Nuernberg". The figures are computed as the backup connector examples compute distances: each formula times the factor 1.2, for roads that are longer than the straight line:

  • Flat-surface formula x 1.2: 398.77 km
  • Haversine formula x 1.2: 403.73 km

The difference in the calculations of this magnitude is in the order of about 1%.

For a short distance of about 30 km between the cities "Essen" and "Wuppertal", the calculations look as follows:

  • Flat-surface formula x 1.2: 28.09 km
  • Haversine formula x 1.2: 28.13 km

Here, the two differ by about 0.12%.

In an optimized solution, the distances between consecutively visited nodes are usually small. When optimizing within cities, the distance between two successively visited nodes is usually a few kilometres at most.

Calculating the driving time

The driving time follows from the distance and an average speed. With the default average speed of avgSpeed = 22 meter/second, the driving time is:

drivingTime = distance / avgSpeed.

When to use it

If you provide distances and driving times for all required element pairs, for example from a routing engine, the backup connector is rarely used. If some connections are missing, the optimizer asks the backup connector to compute them on demand. A custom backup connector is the right tool when you need to:

  • run without external routing data, or with only partial routing data;
  • apply a systematic correction factor for terrain, detours or traffic;
  • switch the distance model, for example from flat-surface to Haversine;
  • implement custom logic per edge;
  • make the travel time depend on the resource, for different average speeds.

How to use it

Set the average speed of a resource

The average speed of each resource can be modified as follows:

IResource capacityResource = new CapacityResource("John", latitude, longitude,
    maxWorkingTime, maxDistance, johnWorkingHours);
    
// Setting average speed to 30 kilometres per hour
Quantity<Speed> avgSpeed = Quantities.getQuantity(30.0, Units.KILOMETRE_PER_HOUR);

capacityResource.setAvgSpeed(avgSpeed);

Basic elements shows how to create a resource.

Set a custom backup connector

First, tell the optimizer's NodeEdgeConnector to use a custom BackupElementConnector. In this example, it is the class MyBackupElementConnector. The code is from the example CustomNodeBackUpConnectorExample.java.

// Set custom backup connector
INodeEdgeConnector connector = new NodeEdgeConnector();
connector.setBackupElementConnector(new MyBackupElementConnector(false));
optimization.setNodeConnector(connector);

Extend the default backup connector

A custom backup connector can modify the distance and driving time calculation of a connection. For this, create a class that extends DefaultFlatEarthAverageSpeedBackupElementConnector. The constructor of MyBackupElementConnector expects a boolean called doRecalculateElement2ElementDuration.

public class MyBackupElementConnector
      extends DefaultFlatEarthAverageSpeedBackupElementConnector {

    /**
     * Instantiates a new backup element connector.
     *
     * @param doRecalculateElement2ElementDuration the do recalculate 
     *        element 2 element duration
     */
    public MyBackupElementConnector(boolean doRecalculateElement2ElementDuration){
      super(doRecalculateElement2ElementDuration);
    }
    
    ...

When all resources have the same average speed, every resource needs the same time for the same connection, and no recalculation is necessary: set doRecalculateElement2ElementDuration = false. When resources have different average speeds, the driving time has to be evaluated for each resource separately: set doRecalculateElement2ElementDuration = true.

Override the distance and the duration

Overriding the two methods getElement2ElementDistance(...) and getElement2ElementDuration(...) changes the default calculation of distances and driving times in JOpt. The following code implements both methods:

@Override
public Quantity<Length> getElement2ElementDistance(
    String fromElementId,
    double fromElementLon,
    double fromElementLat,
    String toElementId,
    double toElementLon,
    double toElementLat,
    IResource visitor) {

  double distance =
      NodeEdgeConnector.distancePlacePlaceFlatEarth(
              fromElementLon, fromElementLat, toElementLon, toElementLat)
          * 1.2;

  return Quantities.getQuantity(distance, METRE);
}


@Override
public Duration getElement2ElementDuration(
    String fromElementId, 
    String toElementId, 
    double distanceMeter, 
    IResource visitor) {

  long traveltimeMillis = (long) (distanceMeter / visitor.getAvgSpeed() * 1000L);

  return Duration.ofMillis(traveltimeMillis);
}

The implementation uses the default method NodeEdgeConnector.distancePlacePlaceFlatEarth(...) to calculate the distance between two points on the map, and adds a correction factor of 1.2 to each distance for difficult terrain and twisty roads. Every connection is then 1.2 times longer than JOpt would usually calculate. The duration override keeps the default formula: the distance divided by the visiting resource's average speed.

Use the Haversine formula

To apply the Haversine formula, create a new method and use it within getElement2ElementDistance(...). The example CustomNodeBackUpConnectorHaversineExample.java does this.


public static double haversineDistanceMeter(
   double lon1,
   double lat1,
   double lon2,
       double lat2) {

    double r = 6371 * 1000.0; // Radius of the earth in meter

    double deltaLatRad = toRad(lat2 - lat1);
    double deltaLonRad = toRad(lon2 - lon1);

    double a =
        Math.sin(deltaLatRad / 2) * Math.sin(deltaLatRad / 2)
            + Math.cos(toRad(lat1))
                * Math.cos(toRad(lat2))
                * Math.sin(deltaLonRad / 2)
                * Math.sin(deltaLonRad / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return r * c; // Distance in meter

}

private static Double toRad(Double value) {
    return value * Math.PI / 180;
}

Average speed in the JSON input of the REST service

In the JSON input of the REST service, the resource field averageSpeed sets the average speed in meters per second, and connections that you provide go into elementConnections. The API reference shows the schema.

Limits

  • The flat-surface formula loses precision as the distance grows. Between consecutively visited nodes the distances are usually small, and there the difference to the Haversine formula is small.
  • With doRecalculateElement2ElementDuration = false, different average speeds of the resources do not change the driving times, unless your own connector implements that.

Authors

DNA Evolutions GmbH


Added: 2026-01-03 (JOpt core 7.5.2-j17) | Last updated: 2026-10-23