Nodes
The work to be scheduled
A Node represents a task, a location, or an appointment that needs to be visited or performed by a Resource. Nodes are the primary demand objects in JOpt.TourOptimizer. The optimizer assigns nodes to resources and sequences them into routes, respecting time windows, capacity, and all other constraints.
JOpt provides several node types, each designed for a different modeling scenario. All node types implement the INode interface and share common properties (ID, opening hours, visit duration, importance), but differ in what additional information they carry and how the optimizer treats them.
Overview
- Common properties
- TimeWindowGeoNode
- EventNode
- PillarTimeWindowGeoNode
- PillarEventNode
- Optional nodes
- Stay nodes
- Builder pattern
- Adding nodes to the optimization
- Summary
Common properties
All node types share the following properties:
Unique ID
Every node needs a unique string identifier. This ID is used throughout the optimization for assignment, reporting, and result analysis. No two nodes may share the same ID.
String nodeId = "Cologne";
Opening hours
A node holds a list of IOpeningHours entries. Each entry defines a time window during which the node can be visited. Multiple opening hours represent multiple days or multiple time slots.
ZonedDateTime start = ZonedDateTime.of(2020, MAY.getValue(), 6, 8, 0, 0, 0,
ZoneId.of("Europe/Berlin"));
ZonedDateTime end = ZonedDateTime.of(2020, MAY.getValue(), 6, 17, 0, 0, 0,
ZoneId.of("Europe/Berlin"));
IOpeningHours firstDay = new OpeningHours(start, end);
Alternatively, you can construct an OpeningHours from a TimeWindow object:
TimeWindow tw = new TimeWindow(start, end);
IOpeningHours firstDay = new OpeningHours(tw);
Multiple opening hours are collected into a list:
List<IOpeningHours> openingHours = new ArrayList<>();
openingHours.add(dayOneHours);
openingHours.add(dayTwoHours);
Each opening hour corresponds to a potential visit slot. If a resource has matching WorkingHours on the same day, the optimizer can schedule the visit during that window.
Visit duration
The time required to perform the task at the node. This is the productive service time, not including travel.
Duration visitDuration = Duration.ofMinutes(20);
The optimizer accounts for this duration when scheduling: the resource must arrive, spend the visit duration at the node, and then travel to the next stop. A node with a 20-minute visit duration and a 30-minute driving time from the previous stop consumes at least 50 minutes of the resource's working time.
Visit duration can be made route-dependent using the visit duration efficiency feature, where different resources can perform the same task faster or slower. See setHasRouteDependentVisitDuration(true) and setMinimalVisitDuration(...).
Importance
A relative integer value that influences how the optimizer prioritizes nodes when violations are unavoidable. A node with higher importance is less likely to be violated (late arrival, skipping) than a node with lower importance.
int importance = 1; // default
Importance is relative. If all nodes have the same importance value, the optimizer treats them equally. The value only matters when nodes compete for limited resources or time windows. Common practice: use a small integer scale (e.g. 1 to 10) and assign higher values to contractually important tasks.
TimeWindowGeoNode
The most common node type. Represents a physical location that must be visited within a time window.
Constructor
INode node = new TimeWindowGeoNode(
"Cologne", // unique ID
50.9333, // latitude
6.95, // longitude
openingHours, // List<IOpeningHours>
Duration.ofMinutes(20), // visit duration
1 // importance
);
| Parameter | Type | Description |
|---|---|---|
id | String | Unique identifier for the node |
latitude | double | Geographic latitude of the node's location |
longitude | double | Geographic longitude of the node's location |
openingHours | List<IOpeningHours> | Time windows when the node can be visited |
visitDuration | Duration | Service time required at the node |
importance | int | Relative priority (higher = more protected from violations) |
The geo coordinates define where the node is located on the map. The optimizer uses these coordinates to calculate distances and travel times between nodes, either through external connection data or the built-in backup connector.
When to use
Use TimeWindowGeoNode for any task that requires physical presence at a location: customer visits, deliveries, inspections, maintenance jobs, installations.
EventNode
A node without a geographical location. Represents a task that must happen within a time window but does not require travel.
Constructor
INode event = new EventNode(
"Customer Call", // unique ID
openingHours, // List<IOpeningHours>
Duration.ofMinutes(30), // visit duration
1 // importance
);
| Parameter | Type | Description |
|---|---|---|
id | String | Unique identifier |
openingHours | List<IOpeningHours> | Time windows when the task can be performed |
visitDuration | Duration | Time required for the task |
importance | int | Relative priority |
An EventNode has no latitude/longitude. It is placed into the route timeline like any other node (consuming visit duration within the opening hours), but it does not introduce travel distance. The optimizer schedules it in a gap between physical visits where it fits the time window.
When to use
Use EventNode for phone calls, remote support sessions, paperwork, compliance steps, administrative tasks, or any work that can be done from anywhere within working hours. EventNodes can be mixed freely with geo nodes in the same optimization.
PillarTimeWindowGeoNode
A geo-located node with a hard-constrained time window. The optimizer's architecture guarantees that the opening hours of a Pillar cannot be violated. This is not a high-penalty soft constraint. It is a structural property of the solution space.
Constructor
IOpeningHours pillarWindow = new OpeningHours(
ZonedDateTime.of(2020, MAY.getValue(), 6, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")),
ZonedDateTime.of(2020, MAY.getValue(), 6, 10, 50, 0, 0, ZoneId.of("Europe/Berlin")));
IPillarNode pillar = new PillarTimeWindowGeoNode(
"Plumbing Appointment", // unique ID
50.9333, // latitude
6.95, // longitude
pillarWindow // single OpeningHours (the SLA window)
);
When the visit duration equals the opening hour window, it is not necessary to provide the duration separately. The node must be visited during exactly this window.
A Pillar can optionally be attached to a specific resource:
pillar.attachResource(john);
This guarantees not only when the task is done but also who does it.
When to use
Use PillarTimeWindowGeoNode for contractual SLAs, legal obligations, appointments with fixed time commitments, or any task where "almost on time" is not acceptable. See the Pillar Nodes feature guide for detailed conflict resolution behavior.
PillarEventNode
A non-geographical Pillar. The same hard time-window guarantee as PillarTimeWindowGeoNode, but without a physical location.
Constructor
IPillarNode pillarCall = new PillarEventNode(
"Important Call", // unique ID
pillarOpeningHours, // single OpeningHours
Duration.ofMinutes(30) // visit duration
);
pillarCall.attachResource(jack);
When to use
Use PillarEventNode for time-critical phone calls, compliance deadlines, or remote tasks that must happen in an exact window and cannot be missed.
Optional nodes
Any node type can be marked as optional. An optional node may or may not be visited, depending on what is globally optimal. The optimizer decides whether including the node improves or worsens the total cost.
node.setIsOptional(true);
Optional nodes are useful for:
- Intermediate stops in pickup and delivery (e.g. waste dumps, reload points)
- Nice-to-have visits that can be deferred if the schedule is too tight
- Buffer nodes that absorb excess capacity
When an optional node is not visited, it does not appear in the result routes. The optimizer reports which optional nodes were included and which were skipped.
Stay nodes
Any geo node can be marked as a stay node, making it eligible as an overnight position for multi-day routing:
node.setIsStayNode(true);
A stay node can simultaneously be optional. The optimizer will visit the hotel only if staying out is beneficial. See the Overnight Stay feature guide for policy controls.
Builder pattern
For enterprise integrations and REST-compatible workflows, JOpt provides an immutable builder pattern. Instead of using constructors, you compose nodes fluently:
Node node = Node.builder()
.id("Cologne")
.type(GeoNode.of(Position.of(50.9333, 6.95)))
.visitDuration(Duration.ofMinutes(20))
.addOpeningHours(
OpeningHours.builder()
.begin(begin.toInstant())
.end(end.toInstant())
.zoneId(begin.getZone())
.build())
.build();
The builder produces an immutable object that can be serialized to JSON and used across REST, SDK, and test environments. The OptimizationConfig builder composes nodes, resources, and properties into a single immutable configuration:
OptimizationConfig<JSONConfig> config = OptimizationConfig.<JSONConfig>builder()
.addAllNodes(createNodes())
.addAllResources(createResources())
.extension(createExtension())
.options(createOptions())
.build();
This is the recommended approach when building optimization inputs programmatically, especially in service contexts where reproducibility and serialization matter.
Adding nodes to the optimization
Once constructed, nodes are added to the optimization instance:
opti.addElement(node);
All node types use the same addElement(...) method. Geo nodes, event nodes, pillars, and optional nodes are all added the same way. The optimizer identifies the type internally and applies the appropriate handling.
Node IDs must be unique. If you add a node with an ID that already exists, it will be rejected.
Summary
| Node type | Has location | Hard time window | Typical use case |
|---|---|---|---|
TimeWindowGeoNode | Yes | No (soft) | Customer visits, deliveries, inspections |
EventNode | No | No (soft) | Phone calls, remote tasks, paperwork |
PillarTimeWindowGeoNode | Yes | Yes (architectural) | Contractual SLAs, fixed appointments |
PillarEventNode | No | Yes (architectural) | Time-critical calls, compliance deadlines |
Additional flags applicable to any node:
setIsOptional(true): optimizer decides whether to visitsetIsStayNode(true): eligible as overnight position
For the next step, see the Basic Elements tutorial to learn how nodes and resources work together in an optimization.
Authors
A product by dna-evolutions ©