/******************************************************************************* * Copyright (c) 2011 Stefan Schroeder. * eMail: stefan.schroeder@kit.edu * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Stefan Schroeder - initial API and implementation ******************************************************************************/ package basics; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import util.Coordinate; import util.CrowFlyCosts; import util.Locations; import util.Neighborhood; import util.NeighborhoodImpl; import basics.costs.DefaultVehicleRoutingActivityCosts; import basics.costs.VehicleRoutingActivityCosts; import basics.costs.VehicleRoutingTransportCosts; import basics.route.Vehicle; import basics.route.VehicleImpl; import basics.route.VehicleImpl.VehicleType; /** * Contains and describes the vehicle routing problem. * *
A routing problem is defined as jobs, vehicles and costs. * *
To construct the problem, use VehicleRoutingProblem.Builder (VehicleRoutingProblem.Builder.newInstance()). * *
By default, fleetSize is INFINITE and fleetComposition is HOMOGENEOUS, transport-costs are calculated as euclidean-distance (CrowFlyCosts),
* and activity-costs are set to DefaultVehicleRoutingActivityCosts which represent hard time-windows (missed time-windows are penalyzed with Double.MAX_VALUE).
*
*
*
* @author stefan schroeder
*
*/
public class VehicleRoutingProblem {
/**
* Builder to build the routing-problem.
*
* @author stefan schroeder
*
*/
public static class Builder {
/**
* Returns a new instance of this builder.
*
* @return builder
*/
public static Builder newInstance(){ return new Builder(); }
private VehicleRoutingTransportCosts transportCosts;
private VehicleRoutingActivityCosts activityCosts = new DefaultVehicleRoutingActivityCosts();
private Map Locations are cached when adding a shipment, service, depot, vehicle.
*
* @return locations
*
*/
public Locations getLocations(){
return new Locations() {
@Override
public Coordinate getCoord(String id) {
return coordinates.get(id);
}
};
}
/**
* Sets routing costs.
*
* @param costs
* @return builder
* @see VehicleRoutingTransportCosts
*/
public Builder setRoutingCost(VehicleRoutingTransportCosts costs){
this.transportCosts = costs;
return this;
}
/**
* Sets the type of fleetSize.
*
* FleetSize is either FleetSize.INFINITE or FleetSize.FINITE
*
* @param fleetSize
* @return
*/
public Builder setFleetSize(FleetSize fleetSize){
this.fleetSize = fleetSize;
return this;
}
/**
* Sets the fleetComposition.
*
* FleetComposition is either FleetComposition.HETEROGENEOUS or FleetComposition.HOMOGENEOUS
*
* @param fleetComposition
* @return
*/
public Builder setFleetComposition(FleetComposition fleetComposition){
this.fleetComposition = fleetComposition;
return this;
}
/**
* Adds a service to jobList.
*
* If jobList already contains service, a warning message is printed, and the existing job will be overwritten.
*
* @param service
* @return
*/
public Builder addService(Service service){
coordinates.put(service.getLocationId(), service.getCoord());
if(jobs.containsKey(service.getId())){ logger.warn("service " + service + " already in job list. overrides existing job."); }
jobs.put(service.getId(),service);
return this;
}
/**
* Adds a job which is either a service or a shipment.
*
* @param job
* @return
* @throws IllegalStateException if job is neither a shipment or a service.
*/
public Builder addJob(Job job) {
if(job instanceof Service) {
addService((Service) job);
}
else throw new IllegalStateException("job can only be a shipment or a service, but is instance of " + job.getClass());
return this;
}
/**
* Adds a vehicle.
*
*
* @param vehicle
* @return
*/
public Builder addVehicle(Vehicle vehicle) {
vehicles.add(vehicle);
if(!vehicleTypes.contains(vehicle.getType())){
vehicleTypes.add(vehicle.getType());
}
coordinates.put(vehicle.getLocationId(), vehicle.getCoord());
return this;
}
/**
* Adds a vehicleType.
*
* @param type
* @return builder
*/
public Builder addVehicleType(VehicleType type){
vehicleTypes.add(type);
return this;
}
public Builder setNeighborhood(Neighborhood neighborhood){
this.neighborhood = neighborhood;
return this;
}
/**
* Sets the activityCostFunction that considers also activities on a vehicle-route.
*
* Here you can consider missed time-windows for example. By default, this is set to a DefaultVehicleActivityCostFunction.
*
* @param activityCosts
* @return
* @see VehicleRoutingTransportCosts, DefaultVehicleRouteCostFunction
*/
public Builder setActivityCosts(VehicleRoutingActivityCosts activityCosts){
this.activityCosts = activityCosts;
return this;
}
public VehicleRoutingProblem build() {
log.info("build problem ...");
if(transportCosts == null){
logger.warn("set routing costs crowFlyDistance.");
transportCosts = new CrowFlyCosts(getLocations());
}
return new VehicleRoutingProblem(this);
}
public Builder addLocation(String id, Coordinate coord) {
coordinates.put(id, coord);
return this;
}
public Builder addAllJobs(Collection By default, it is INFINITE.
*
* @return either FleetSize.INFINITE or FleetSize.FINITE
*/
public FleetSize getFleetSize() {
return fleetSize;
}
/**
* Returns the unmodifiable job map.
*
* @return unmodifiable jobMap
*/
public Map