Routing with Schedules
Schedules can be used to model recurring jobs that must be serviced at regular intervals. For example, a customer might need frozen food supplies delivered once a week. The actual day of the delivery does not matter as long as food is delivered every six to eight days. By allowing the optimizer to choose the delivery day (the base_date), RouteCloud can produce more efficient routes by placing nearby deliveries on the same day. This example can be defined as follows:
{
"id": "schedule0",
"type": "weekly",
"earliest_date": "2016-5-15",
"allowed_variance_before": 1,
"allowed_variance_after": 1,
"allowed_days_of_week": [ "mon", "tue", "wed", "thu", "fri" ],
"jobs": [ "schedule0_job0", "schedule0_job1" ]
}
A weekly schedule.
The earliest_date of 2016-5-15
, means that schedule0_job0
will be delivered in the week between 2016-5-15
and 2016-5-21
,
and schedule0_job1
will be delivered in the week between 2016-5-22
and 2016-5-28
.
Because of the allowed_variance_before and allowed_variance_after, schedule0_job1
will be delivered within six to eight days of schedule0_job0
.
For example, schedule0_job0
could be delivered on May 18 (a Wednesday), and schedule0_job1
on May 26 (a Thursday).
The allowed_days_of_week field limits the jobs to Monday-Friday.
The base_date for a job is used to specify on which day of the week or month a delivery occurs.
For example, for schedule0
in the example above, the delivery can be on any day of the week.
But if the base_date is set to a Tuesday (for example, 2016-5-3
) then all deliveries will be made within one day of a Tuesday; that is, on a Monday, Tuesday, or Wednesday.
For example:
{
"id": "schedule1",
"type": "weekly",
"earliest_date": "2016-5-15",
"base_date": "2016-5-3",
"allowed_variance_before": 1,
"allowed_variance_after": 1,
"jobs": [ "schedule1_job0", "schedule1_job1" ]
}
A weekly schedule that must be delivered on a Monday, Tuesday, or Wednesday.
In this case schedule1_job0
might be delivered on May 18 (a Wednesday), and schedule1_job1
on May 23 (a Monday).
If no base_date is specified for a schedule, then RouteCloud chooses the base_date that allows the jobs to be delivered most optimally.
Example Request
{
"schedules": [
{
"id": "schedule0",
"comment": "every day, for 3 deliveries, with the second day skipped",
"jobs": [ "schedule0_job0", null, "schedule0_job2", "schedule0_job3" ],
"type": "daily"
}, {
"id": "schedule1",
"comment": "every week, so job2 will not be routed because there are no routes for week 3",
"jobs": [ "schedule1_job0", "schedule1_job1", "schedule1_job2" ],
"type": "weekly"
}, {
"id": "schedule2",
"comment": "every week on a Tuesday",
"jobs": [ "schedule2_job0", "schedule2_job1" ],
"type": "weekly",
"base_date": "2016-5-10"
}, {
"id": "schedule3",
"comment": "every week on a Tuesday, starting on the 23rd (so job1 will not be routed)",
"jobs": [ "schedule3_job0", "schedule3_job1" ],
"type": "weekly",
"base_date": "2016-5-10",
"earliest_date": "2016-5-23"
}, {
"id": "schedule4",
"comment": "twice a week, but only 3 deliveries",
"jobs": [ "schedule4_job0", "schedule4_job1", "schedule4_job2" ],
"allowed_variance_before": 1,
"allowed_variance_after": 1,
"type": "times_per_week",
"frequency": 2
}
],
"routes": [
{
"id": "route0", "date": "2016-5-16",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route1", "date": "2016-5-17",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route2", "date": "2016-5-18",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route3", "date": "2016-5-19",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route4", "date": "2016-5-20",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
},{
"id": "route5", "date": "2016-5-23",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route6", "date": "2016-5-24",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route7", "date": "2016-5-25",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route8", "date": "2016-5-26",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}, {
"id": "route9", "date": "2016-5-27",
"location": "39.718005, -104.969531", "shift_start_time": "09:00", "max_working_time": "08:00"
}
],
"jobs": [
{ "id": "job0_one_off", "time_on_site": "00:10", "location": "39.653975, -105.093750" },
{ "id": "job1_one_off", "time_on_site": "00:15", "location": "39.590789, -105.084376" },
{ "id": "job2_one_off", "time_on_site": "00:10", "location": "39.638635, -105.128906" },
{ "id": "job3_one_off", "time_on_site": "00:10", "location": "39.597111, -105.041015" },
{ "id": "schedule0_job0", "location": "39.635928, -105.049219" },
{ "id": "schedule0_job2", "location": "39.635928, -105.049219" },
{ "id": "schedule0_job3", "location": "39.635928, -105.049219" },
{ "id": "schedule1_job0", "location": "39.725375, -104.791080" },
{ "id": "schedule1_job1", "location": "39.725375, -104.791080" },
{ "id": "schedule1_job2", "location": "39.725375, -104.791080" },
{ "id": "schedule2_job0", "location": "39.708990, -105.026954" },
{ "id": "schedule2_job1", "location": "39.708990, -105.026954" },
{ "id": "schedule3_job0", "location": "39.708990, -105.026954" },
{ "id": "schedule3_job1", "location": "39.708990, -105.026954" },
{ "id": "schedule4_job0", "location": "39.725375, -104.791080" },
{ "id": "schedule4_job1", "location": "39.725375, -104.791080" },
{ "id": "schedule4_job2", "location": "39.725375, -104.791080" }
]
}
The routing_with_schedules_build.json request. Click here to open it in the UI.
The request defines two weeks of routes, five schedules, and four one-off jobs.
schedule0
defines a daily schedule with three jobs, with the second day skipped.schedule1
defines a weekly schedule with three jobs. Since there are only two weeks of jobs, the third job will not be routed.schedule2
defines a weekly schedule with two jobs. The schedule has a base date of2016-5-10
, which is a Tuesday, so the jobs will be routed on May 17 and May 24 (both Tuesdays).schedule3
defines a weekly schedule with two jobs. The schedule has a base date of2016-5-10
, which is a Tuesday, and an earliest date of2016-5-23
, which is the start of the second week of routes. Because the schedule starts in the second week, the second job will not be routed.schedule4
defines a twice weekly schedule, with three jobs. The first job will be routed in the first half of week one, the second in the second half of week one, and the third in the first half of week two.
Example Response
{
"routes": [
{
"id": "route0", ...
"stops": [
{ "type": "depot", ... },
{ "type": "job", "id": "schedule0_job0", ... },
{ "type": "job", "id": "job0_one_off", ... },
{ "type": "job", "id": "job2_one_off", ... },
{ "type": "job", "id": "job1_one_off", ... },
{ "type": "job", "id": "job3_one_off", ... },
{ "type": "depot", ... }
]
},
{
"id": "route1", ...
"stops": [
{ "type": "depot", ... },
{ "type": "job", "id": "schedule2_job0", ... },
{ "type": "depot", ... }
]
},
{
"id": "route2", ...
"stops": [
{ "type": "depot", ... },
{ "type": "job", "id": "schedule4_job0", ... },
{ "type": "job", "id": "schedule0_job2", ... },
{ "type": "depot", ... }
]
},
{
"id": "route3", ...
"stops": [
{ "type": "depot", ... },
{ "type": "job", "id": "schedule0_job3", ... },
{ "type": "depot", ... }
]
},
{
"id": "route4",
"stops": [
{ "type": "depot", ... },
{ "type": "job", "id": "schedule4_job1", ... },
{ "type": "job", "id": "schedule1_job0", ... },
{ "type": "depot", ... }
]
},
{
"id": "route5",
"stops": []
},
{
"id": "route6",
"stops": [
{ "type": "depot", ... },
{ "type": "job", "id": "schedule4_job2", ... },
{ "type": "job", "id": "schedule2_job1", ... },
{ "type": "job", "id": "schedule3_job0", ... },
{ "type": "depot", ... }
]
},
{
"id": "route7",
"stops": []
},
{
"id": "route8",
"stops": []
},
{
"id": "route9",
"stops": [
{ "type": "depot", ... },
{ "type": "job", "id": "schedule1_job1", ... },
{ "type": "depot", ... }
]
}
],
"unrouted_jobs": [
{ "id": "schedule1_job2" },
{ "id": "schedule3_job1" }
],
"schedules": [
{ "id": "schedule0", "base_date": "2016-05-16" },
{ "id": "schedule1", "base_date": "2016-05-20" },
{ "id": "schedule4", "base_date": "2016-05-18" }
]
}
A snipped version of the routing_with_schedules_build.json response. The full response is available here. Click here to open it in the UI.
As shown in the response, RouteCloud produced routes that obeyed the schedule constraints.
schedule1_job2
and schedule3_job1
were not routed because they were targeted for week three, which was not included in the build.
RouteCloud has assigned base dates to schedule0
, schedule1
, and schedule4
and returned them in the schedules array.
Base dates were not assigned to schedule2
or schedule3
as they already had base dates in the input.
For example, schedule1
, a weekly schedule, was assigned to 2016-05-20
, which is a Friday.
Now that the base date has been assigned, it should be passed into future requests to ensure schedule1
continues to recur on a Friday.
See Also
- Routing with Dates.
- The schedule type.
- The job_delivered_on_disallowed_day violation.