Return to depot

Hi. I think Im confused about return to Depot option. I have set for each vehicle the endLocation and I have set returnToDepot to be true. But it does not seem to do anything and Im not sure what the outcome should be.

Should I be expecting another task to be added to the end of each route sends the vehicle to the endLocation or does it just make sure there is travel time available to be back at the depot by the lastArrival time for the vehicle ?

Im not sure how this should manifest itself and it does not seem to do anything.

Other than that - I love this system. It is pure digital magic.

Thanks
Grant

If returnToDepot is true, then the vehicle needs to return to where it actually started (start location). If it is false, it can end its route anywhere. To be a bit more precise, “anywhere” means that the algorithm determines the end location of the vehicle. It chooses the end location from the set of all task locations. And yes, if returnToDepot is false, the vehicle do not need to make the return leg, i.e. it might have time to conduct another task.

Hi Stefan

Ok so my problem is that although I set return to depot as true it does not seem to do anything.

The route still seems to end at the last delivery.

What should I be seeing on the route ? An extra delivery location at the starting point ? Or some other thing ?

Regards
Grant

Hi Grant,

How are you determining this? I think the travel distance and time to the depot at the end of the route should be clear in an unmodified SolutionPrinter output. Are you manually extracting the stops from your route?

Hi Roganjosh

We are using SolutionPrinter but a modified version. I’ll put some code in to generate the original as well and see if it looks different. Thanks for the suggestion

Im still struggling with this.
I have included the end of a detailed solution printout for a job that was set to "return to depot"
I see nothing to indicate that it should return to depot and the last activity is deliverShipment then End.

| 4 | 8500154829445 | deliverShipment | 2580_94 | 08:41 | 08:42 | 4086 |

| 4 | 8500154829445 | deliverShipment | 2581_106 | 08:45 | 08:46 | 4327 |

| 4 | 8500154829445 | deliverShipment | 2571_26 | 08:46 | 08:47 | 4327 |

| 4 | 8500154829445 | deliverShipment | 2581_110 | 08:51 | 08:52 | 4569 |

| 4 | 8500154829445 | deliverShipment | 2571_30 | 08:52 | 08:53 | 4569 |

| 4 | 8500154829445 | end | - | 08:53 | undef | 4569 |

±-------------------------------------------------------------------------------------------------------------------------------+

I could do with some help here. I just can’t see what Im doing wrong. I have confirmed that the option is set to true for return to depot. Are there any circumstances that would result it in being ignored ?

regards

Grant

Stefan (or someone) could you please confirm that the return to depot is actually a routeActivity in the route collection ?

When I look at the print function it seems to add the “end” as a separate item that is not mentioned in the actual route activities.

regards
Grant

if the end location of vehicle 8500154829445 (if not set, then the start location, since return to depot is true) and the delivery location of shipment 2571_30 are not the same, then something seems not correct.

if you can confirm they are different, could you send a code snippet of constructing the vrp? because return to depot works fine for me and I am not able to reproduce your issue. Thanks.

Hi Jie31best

I can confirm that location 2571_30 is not the start location depot.
The code is below.
regards
Grant

		Object obj = parser.parse(request);
		req = (JSONObject) obj;
		JSONArray jJobs =  (JSONArray) req.get("jobs");
		JSONArray jVehicles =  (JSONArray) req.get("vehicles");
		locationList = new Locations[jJobs.size()+jVehicles.size()];
		Double pickupLat = 0.0; 
		Double pickupLon = 0.0; 
		Double pickupWindowStart = 0.0;
		Double pickupWindowEnd = 0.0;
		Double pickupTimeOnSite = 1.0;
		Location pickupLocation = null;
		Iterator<JSONObject> vehicles = jVehicles.iterator(); 
		while (vehicles.hasNext()) { 
			JSONObject vehicleJson = vehicles.next();
			String deviceID = (String)vehicleJson.get("deviceID") ;
			Double startShift = Double.parseDouble((String) vehicleJson.get("startShift"));
			Double costPerDistance = Double.parseDouble(((String) vehicleJson.get("costPerDistance") == null ? "1" : (String)vehicleJson.get("costPerDistance")));
			Boolean returnToDepot = (String)vehicleJson.get("returnToDepot") == "true" ? true : false;
                            returnToDepot = true; // DEBUG REMOVE FOR PRODUCTION
			int capacity = Integer.parseInt(((String)vehicleJson.get("capacity") == null ? "0" : (String)vehicleJson.get("capacity")));
			if(earliestStart >=0)
				earliestStart = earliestStart <= startShift ? earliestStart : startShift;
			else
				earliestStart = startShift;
			Double endShift = Double.parseDouble((String) vehicleJson.get("endShift"));
			Double vehicleStartLat = Double.parseDouble((String) vehicleJson.get("StartLocationLatitude"));
			Double vehicleStartLon = Double.parseDouble((String) vehicleJson.get("StartLocationLongitude"));

			locationList[locIdx] = new Locations();
			locationList[locIdx].name = getLocationName(vehicleStartLat, vehicleStartLon);
			locationList[locIdx].lat = vehicleStartLat;
			locationList[locIdx++].lon = vehicleStartLon;
			
			String vehicleTypeName = Integer.toString(capacity) + "_" + Double.toString(costPerDistance);
			Builder vehicleBuilder = VehicleImpl.Builder.newInstance(deviceID)
					.setStartLocation(loc(Coordinate.newInstance(vehicleStartLat, vehicleStartLon)))
					.setType(vt.get(vehicleTypeName, VehicleTypes.CONSTRAINT_CAPACITY, capacity, costPerDistance))
					
					.setEarliestStart(startShift)
					.setLatestArrival(endShift)
					.setEndLocation(loc(Coordinate.newInstance(vehicleStartLat, vehicleStartLon)))
					.setReturnToDepot(returnToDepot);

		/*
		 * now add any breaks that have been defined for this vehicle
		 */
		/*
		 * Now read and add the jobs
		 */

A route is a sequence of activities, i.e. start, act1, act2, …, act_n, end.
Start and end need to be retrieved by route.getStart() and route.getEnd(). If you want to get any other activity between start and end, you need to iterate over route.getActivities(). Thus, start and end are not included in this list (route.getActivities()).
If returnToDepot == true then route.getEnd() is either equal to route.getStart() (if you omit to set a specific end) or your specified end location. If returnToDepot == false, route.getEnd() should be equal to the last activity (act_n) location from route.getActivities().

Thanks Stefan that helped to understand what I should be looking for. It also shows that I have a problem. In the details report above it shows that the end time of the last job is the start time as the arrive time of the “end”. But the vehicle would have to have travelled some distance to get there So I would expect the arrive time of “end” to be somewhat later than the “depart” time of the last activity.

Anything in the code fragment that suggests a bungle on my part ?

regards
Grant

the part of the code that builds the vehicles looks fine to me. could you please also share the part that reads and adds the jobs? Thanks.

Meanwhile, could you double check the value of the following?

vrp.getTransportCosts().getTransportTime(delivery location of shipment 2571_30, end location of vehicle 8500154829445, ...);
1 Like

Hi He

I think you hit the nail on the head

I extended the print out to include the location of the END event and it is showing as the location of the last job - whereas it should be showing as the location of the depot (start point).

The code explicitly sets it as:

.setStartLocation(loc(Coordinate.newInstance(vehicleStartLat, vehicleStartLon)))
.setEndLocation(loc(Coordinate.newInstance(vehicleStartLat, vehicleStartLon)))

Below is an extract from the output. Note that the value after the location is the distance from the previous activity to the named location - in this case 0.0 :frowning:

| 3 | 8500154829445 | deliverShipment | 2778_78 | [x=-31.6490768][y=115.6976231] | 1185.2 | -49.0 | 13:11 | 13:19 | 11974 |

| 3 | 8500154829445 | end | - | [x=-31.6490768][y=115.6976231] | 0.0 | - | 13:19 | undef | 11974 |

here is the code you asked for - loading the jobs.

		Iterator<JSONObject> j = jJobs.iterator(); 
		while (j.hasNext()) { 
			JSONObject v = j.next();
			String jobID = (String)v.get("jobID");
			String jobType = (String)v.get("type");
			if(jobType.compareToIgnoreCase("pickup") == 0){
				JSONObject pickup = (JSONObject)v.get("details");
				pickupLat = Double.parseDouble((String) pickup.get("latitude"));
				pickupLon = Double.parseDouble((String) pickup.get("longitude"));
				pickupTimeOnSite = 360.0; //Double.parseDouble((String) pickup.get("timeOnSite"));
				pickupWindowStart = Double.parseDouble((String) pickup.get("ArriveWindowStart"));
				//					pickupWindowStart = earliestStart > pickupWindowStart ? earliestStart : pickupWindowStart;
				pickupWindowEnd = Double.parseDouble((String) pickup.get("ArriveWindowEnd"));
				//					Utils.LOGGER.log(Level.INFO, "adding pickup job with  window time start: " + pickupWindowStart + " window time end " + pickupWindowEnd);

				locationList[locIdx] = new Locations();
				locationList[locIdx].name = getLocationName(pickupLat, pickupLon);
				locationList[locIdx].lat = pickupLat;
				locationList[locIdx++].lon = pickupLon;
				pickupLocation = Location.newInstance(getLocationName(pickupLat, pickupLon));
			} else {
				JSONObject deliver = (JSONObject)v.get("details");
				String assignedVehicleID = deliver.containsKey("assignedVehicle") ? (String) deliver.get("assignedVehicle") : "Any";
				Double dLat = Double.parseDouble((String) deliver.get("latitude"));
				Double dLon = Double.parseDouble((String) deliver.get("longitude"));
				Double windowStart = Double.parseDouble((String) deliver.get("ArriveWindowStart"));
				Double windowEnd = Double.parseDouble((String) deliver.get("ArriveWindowEnd"));
				Double timeOnSite = Double.parseDouble((String) deliver.get("timeOnSite"));
				Double demandD = Double.parseDouble((String)deliver.get("demand"));
				int demand = demandD.intValue();
				Shipment shipment = Shipment.Builder.newInstance(jobID)
						// pickup side
						.setPickupLocation(pickupLocation)
						.setPickupTimeWindow(TimeWindow.newInstance(pickupWindowStart, pickupWindowEnd))
						.setPickupServiceTime(demand * Model_wss.PICKUP_TIME_ON_SITE_PER_KG)
						// delivery side
						.setName((String)deliver.get("jobID"))
						.addSizeDimension(0,demand > 0 ? demand : 1)
						.setDeliveryLocation(Location.newInstance(getLocationName(dLat, dLon)))
						.setDeliveryServiceTime(timeOnSite)
						.setDeliveryTimeWindow(TimeWindow.newInstance(windowStart, windowEnd))							
						.build();
				locationList[locIdx] = new Locations();
				locationList[locIdx].name = getLocationName(dLat, dLon);
				locationList[locIdx].lat = dLat;
				locationList[locIdx++].lon = dLon;
				vrpBuilder.addJob(shipment);					
				// if the job has been assigned to a specific vehicle
				if(assignedVehicleID.compareToIgnoreCase("any") != 0 ){
					jobVehicleMap.put(shipment.getId(), assignedVehicleID);
				}

			}
		}

Hi Folks

Im still struggling with this. It is clear that the “End” activity is the same location as the last “Deliver” activity and as far as I can tell it should not be - it should be the depot location.

I could really use some help please

can you check the vehicle start/end location right after the call of vehicleBuilder.build()? because, once it is built, it will not be able to change.

2 Likes

I am delighted to report that the issue is resolved. Your suggestion (jie31best) pointed us at the problem. It was actually that the code to set the return to depot flag was failing the conditional test

Thanks to everyone that helped.

regards
Grant

1 Like

stefan is it possible to close this issue ? It is not really helpful to the community in general as the problem was an “undocumented feature” in our code. :slight_smile:

regards
Grant