Skip to content

Repository files navigation

LinkedIT

Vehicle routing and delivery planning with real road-network costs.

LinkedIT takes a depot, a fleet, and a set of deliveries, then decides which vehicle should handle each stop and in what order. We use jsprit for the optimization and OSRM for road travel times, distances, and map-ready route geometry.

LinkedIT planning dashboard

What is LinkedIT?

Planning a few deliveries by hand is manageable. Add more stops, several vehicles, capacity limits, service times, priorities, and delivery windows, and the number of possible plans grows quickly.

LinkedIT turns those inputs into a vehicle-routing problem. The current dashboard lets our team choose a depot, define vehicles by ID and capacity, and add deliveries manually or import them from CSV. A delivery contains coordinates, demand, unload time, and priority; CSV and direct API requests can also include a time window. The API additionally accepts vehicle-specific start and end locations—when they are omitted, vehicles start and finish at the depot.

For road-aware planning, the backend asks OSRM for one distance-and-duration matrix before the solver starts. jsprit then works entirely against that in-memory matrix while assigning deliveries and testing stop sequences. Once it has selected the final routes, OSRM is called again for the road geometry needed by the map.

The result includes each used vehicle, its ordered stops, arrival and departure times, remaining load, route distance and duration, delivered load, and a GeoJSON LineString. Jobs that cannot be placed in a feasible route are returned separately instead of disappearing from the plan.

Depot + fleet + deliveries
            ↓
        OSRM Table
            ↓
 distance / duration matrix
            ↓
           jsprit
            ↓
 vehicle assignment + stop order
            ↓
        OSRM Route
            ↓
 route metrics + GeoJSON geometry

Why LinkedIT?

Manual planning gets unreliable when deliveries compete for limited vehicle capacity and time. A route that looks short on a map may be slow on the road, while a locally convenient assignment can make the rest of the fleet inefficient or infeasible.

We built LinkedIT to make those trade-offs explicit: model the real constraints, solve the fleet plan as one problem, and return an answer that dispatchers can inspect rather than a list of abstract solver values.

Features

Plan the fleet

  • Set the depot through location search, presets, GPS, the map picker, or exact coordinates.
  • Add and edit vehicles with application-defined capacity units.
  • Add deliveries with demand, service duration, and priority (1 is highest).
  • Import delivery jobs from CSV with a validation preview, duplicate checks, and optional time-window columns.
  • Load the included nine-stop Bhubaneswar dataset for a quick walkthrough.

Optimize against constraints

  • Assign delivery jobs across a finite fleet and optimize the stop order for each used vehicle.
  • Enforce vehicle capacity and delivery time windows supplied through CSV or the API.
  • Account for service time at every delivery and report jobs the solver could not assign.
  • Preserve optional per-vehicle start and end locations in API requests.

Use real roads

  • Build an asymmetric OSRM distance/time matrix in a single request before optimization.
  • Keep solver cost lookups in memory instead of making network calls during the search.
  • Request full GeoJSON road geometry only for the final routes.
  • Switch to offline great-circle costs and straight-line geometry with the crowfly provider.

Inspect the plan

  • Render the depot, deliveries, numbered stops, and route lines on a Leaflet map with OpenStreetMap tiles.
  • Give each vehicle a consistent route color; selecting a vehicle focuses its route across the map and result views.
  • Review orders by pending, assigned, or unassigned status and filter by vehicle or priority.
  • Compare route distance, duration, capacity use, and complete DEPOT → stops → DEPOT sequences.
  • Follow estimated arrivals in the per-vehicle timeline. The API also returns aggregate distance, cumulative duration, assignment counts, vehicles used, and objective cost.

How it works

flowchart LR
    A[React planning dashboard] -->|Depot, vehicles, deliveries| B[Spring Boot API]
    B -->|Unique coordinates| C[OSRM Table]
    C -->|Road distance and time matrix| D[jsprit optimizer]
    D -->|Vehicle assignment and stop order| E[OSRM Route]
    E -->|GeoJSON LineStrings| B
    B -->|Routes, stops, loads, metrics| A
Loading
  1. The dashboard sends an optimization request to POST /api/optimize.
  2. The backend validates IDs, coordinates, capacities, priorities, service times, and time windows.
  3. OSRM returns road distances in metres and travel times in seconds for every unique location pair.
  4. The request is mapped into a finite-fleet jsprit delivery problem and solved over 200 iterations.
  5. The final jsprit solution is mapped into routes, stops, loads, totals, and unassigned job IDs.
  6. Geometry requests for the used vehicles run through a bounded executor and return GeoJSON road lines.
  7. React draws the routes and exposes the plan through Orders, Routes, and Timeline views.

Repeated optimization results, OSRM matrices, and route geometries are held in bounded in-memory caches. The checked-in configuration keeps entries for four hours, up to 500 entries per cache.

Tech stack

Layer Technology
Frontend React 19, TypeScript 6, Vite 8, Tailwind CSS 4
Map Leaflet 1.9, OpenStreetMap tiles
Backend Java 21, Spring Boot 3.3.5
Optimization jsprit 2.1.0-SNAPSHOT (jsprit-core)
Road routing OSRM Table and Route services
Build Maven 3.6+, npm
Test and lint JUnit 5, Spring Boot Test, Mockito, Oxlint

Repository structure

LinkedIT/
├── frontend/           # React planning dashboard and API client
├── routing-backend/    # Spring Boot API, validation, routing, and jsprit adapters
├── jsprit-core/        # Upstream optimization engine we build on
├── docs/               # README media
├── PROJECT_GUIDE/      # Extended project notes
├── pom.xml             # Maven reactor for jsprit-core and routing-backend
└── Dockerfile          # Production backend image

Application code stays in routing-backend; we depend on jsprit-core rather than mixing LinkedIT-specific behavior into the engine.

Getting started

Prerequisites

  • Java 21
  • Maven 3.6 or newer
  • Node.js ^20.19.0 or >=22.12.0
  • npm
  • Internet access for the checked-in public OSRM configuration, or no routing service if using crowfly

Backend

From the repository root, build the reactor once so the backend can resolve the local jsprit module, then start Spring Boot:

mvn -pl routing-backend -am install
mvn -pl routing-backend spring-boot:run

The API starts on http://localhost:8080. Check it with:

curl http://localhost:8080/api/health

The repository currently runs in OSRM mode against https://router.project-osrm.org. For a local OSRM instance or offline planning, set Spring Boot environment variables before starting the backend:

Environment variable Purpose Checked-in value
ROUTING_PROVIDER osrm for road routing, crowfly for offline great-circle routing osrm
ROUTING_OSRM_BASE_URL Base URL of the OSRM server https://router.project-osrm.org
CORS_ALLOWED_ORIGINS Comma-separated frontend origins http://localhost:5173,http://localhost:4173

The public OSRM server is convenient for development and the demo, but a self-hosted regional instance is the better fit for controlled or heavier workloads. See DEPLOYMENT_AND_OSRM_GUIDE.md for local OSRM preparation and deployment notes.

Frontend

In a second terminal:

cd frontend
npm install
npm run dev

Open http://localhost:5173. The API client reads VITE_API_BASE_URL and falls back to http://localhost:8080. To override it, copy the example environment file:

cp .env.example .env.local

Then edit VITE_API_BASE_URL in .env.local. On PowerShell, use Copy-Item .env.example .env.local instead.

API

POST /api/optimize is the main application endpoint. Coordinates are WGS84 latitude/longitude, service and time-window values are seconds, and capacity/demand use the same application-defined integer unit.

{
  "depot": { "id": "DEPOT-1", "latitude": 20.2961, "longitude": 85.8245 },
  "vehicles": [
    { "id": "V1", "capacity": 50 }
  ],
  "jobs": [
    {
      "id": "D1",
      "latitude": 20.305,
      "longitude": 85.817,
      "demand": 18,
      "serviceDuration": 240,
      "priority": 2,
      "timeWindow": { "start": 28800, "end": 43200 }
    }
  ]
}

A shortened response has this shape:

{
  "routes": [
    {
      "vehicleId": "V1",
      "stops": [
        {
          "jobId": "D1",
          "sequence": 1,
          "latitude": 20.305,
          "longitude": 85.817,
          "arrivalTime": 28800,
          "departureTime": 29040,
          "remainingLoad": 0
        }
      ],
      "distance": 14320.5,
      "duration": 2340.2,
      "initialLoad": 18,
      "deliveredLoad": 18,
      "geometry": {
        "type": "LineString",
        "coordinates": [[85.8245, 20.2961], [85.817, 20.305]]
      }
    }
  ],
  "unassignedJobs": [],
  "summary": {
    "totalDistance": 14320.5,
    "totalDuration": 2340.2,
    "vehiclesUsed": 1,
    "totalJobs": 1,
    "assignedJobs": 1,
    "unassignedJobs": 0,
    "objectiveCost": 14.3205
  }
}

GET /api/health returns the backend status for local checks and deployment probes.

Under the hood

jsprit. We map vehicles to a finite fleet and deliveries to capacity-bearing jobs with service times, priorities, and optional time windows. jsprit searches for a low-cost feasible assignment and ordering; our application layer then converts its solution into the stable API model used by the dashboard.

OSRM Table. One request loads the full asymmetric road matrix before optimization. Distance values are metres and durations are seconds. During solver search, a cost lookup is an in-memory matrix access—there is no HTTP request inside the optimization loop.

OSRM Route. Geometry is deliberately postponed until the stop order is final. The backend makes one Route-service request per used vehicle and asks for overview=full&geometries=geojson; these calls can run concurrently. Route geometry is for display, while the Table matrix remains the source of route distance and time.

Current scope

Each LinkedIT optimization is a static snapshot: a depot, delivery jobs, and a finite fleet of capacity-limited vehicles. It plans delivery-only routes, supports static time windows and priorities, and returns routes that end at their configured vehicle end location (the depot in the dashboard workflow).

It does not include authentication, persistent storage, live vehicle tracking, dynamic dispatch, or traffic prediction. Routing uses the costs supplied by the configured provider and may reuse them from the in-memory cache.

Acknowledgements

LinkedIT’s application layer is built around jsprit for vehicle-routing optimization and OSRM for road-network costs and geometry. The dashboard uses OpenStreetMap data and tiles through Leaflet. We do not claim authorship of these upstream projects.

License

This repository is distributed under the Apache License 2.0. The vendored jsprit-core module retains its upstream GraphHopper copyright, license, and notices; see NOTICE.md and jsprit-core/LICENSE.md.

About

LinkedIT is an intelligent vehicle route optimization platform built with **React + Vite, Spring Boot, jsprit, and OSRM**. It optimizes delivery assignments and stop sequences using real road distance and travel-time matrices, supports vehicle capacities, priorities and time windows.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages