diff --git a/src/simutrans/display/simview.cc b/src/simutrans/display/simview.cc
index fcbb2b19f..ba72bbea5 100644
--- a/src/simutrans/display/simview.cc
+++ b/src/simutrans/display/simview.cc
@@ -6,6 +6,9 @@
 #include <stdio.h>
 
 #include "../world/simworld.h"
+
+#include "../dataobj/ribi.h"          // line-route overlay: travel direction per segment
+#include "../vehicle/vehicle_base.h"  // line-route overlay: reuse the lane offset table
 #include "simview.h"
 #include "simgraph.h"
 #include "viewport.h"
@@ -294,6 +297,56 @@ void main_view_t::display(bool force_dirty)
 		}
 	}
 
+	// line-route overlay: draw the selected line's real path as directional lane strokes in the
+	// owning player's colour (display only). Each segment is shifted toward the lane used for its
+	// own travel direction (driving-side aware); because the lane table is antisymmetric, the
+	// outward and return legs land on opposite lanes automatically. The scheduled stops keep their
+	// own obj_t::highlight channel, so the route never collides with the schedule-stop highlight.
+	{
+		const vector_tpl<koord3d>& route = welt->get_line_route_overlay();
+		if(  route.get_count() > 1  ) {
+			// Two coherent shades derived once per frame from the owning player's colour ramp: a bright
+			// core (ramp base + the GUI "bright" offset) that stays legible on dark terrain, and a
+			// neutral-dark outline that separates the stroke from light terrain. The stored colour is
+			// player_color1 + 1 (see tool_line_route_overlay_t), so ramp base = stored - 1. No new
+			// setting is introduced; this only reuses the existing player-colour ramp.
+			const palette_index_t col_idx = welt->get_line_route_overlay_color();
+			const PIXVAL col_main = gfx->palette_lookup( (palette_index_t)(col_idx - 1 + env_t::gui_player_color_bright) );
+			const PIXVAL col_halo = gfx->palette_lookup( COL_BLACK );
+			const sint8 lane_sign = welt->get_settings().is_drive_left() ? 1 : -1;
+			// tile-centre anchor (screen pixels): half a tile across and ~19/32 down, so the stroke
+			// rests on the way surface (near the engine's own on-way signal anchor of 9/16) instead of
+			// floating above it.
+			const scr_coord_val cx = IMG_SIZE/2;
+			const scr_coord_val cy = (IMG_SIZE*19)/32;
+			for(  uint32 i = 1;  i < route.get_count();  i++  ) {
+				const koord3d a = route[i-1];
+				const koord3d b = route[i];
+				const koord d = b.get_2d() - a.get_2d();
+				if(  d.x < -1  ||  d.x > 1  ||  d.y < -1  ||  d.y > 1  ) {
+					continue; // not adjacent tiles: leg gap / broken leg -> no stroke across it
+				}
+				// lane offset for this segment's travel direction (base-64 units, scaled to zoom)
+				const uint8 dir = ribi_t::get_dir( ribi_type( a, b ) );
+				const scr_coord_val lx = tile_raster_scale_x( lane_sign * vehicle_base_t::get_driveleft_base_offset(dir,0), IMG_SIZE );
+				const scr_coord_val ly = tile_raster_scale_y( lane_sign * vehicle_base_t::get_driveleft_base_offset(dir,1), IMG_SIZE );
+				const scr_coord pa = viewport->get_screen_coord( a ) + scr_coord( cx + lx, cy + ly );
+				const scr_coord pb = viewport->get_screen_coord( b ) + scr_coord( cx + lx, cy + ly );
+				// A two-pixel bright core wrapped by a one-pixel dark outline above and below: a cheap
+				// vertical halo that reads cleanly on Simutrans' iso, mostly-diagonal way strokes.
+				gfx->draw_line( pa.x, pa.y - 1, pb.x, pb.y - 1, col_halo );
+				gfx->draw_line( pa.x, pa.y + 2, pb.x, pb.y + 2, col_halo );
+				gfx->draw_line( pa.x, pa.y,     pb.x, pb.y,     col_main );
+				gfx->draw_line( pa.x, pa.y + 1, pb.x, pb.y + 1, col_main );
+				// Round the joins and ends: a small core dot at each segment end bridges the small
+				// lane-offset step between adjacent segments at a turn (they sit on slightly different
+				// lanes) and gives the route rounded caps instead of hard square ends.
+				gfx->draw_filled_circle( pa.x, pa.y, 1, col_main );
+				gfx->draw_filled_circle( pb.x, pb.y, 1, col_main );
+			}
+		}
+	}
+
 	obj_t *zeiger = welt->get_zeiger();
 	DBG_DEBUG4("main_view_t::display", "display pointer");
 	if( zeiger  &&  zeiger->get_pos() != koord3d::invalid ) {
diff --git a/src/simutrans/gui/line_management_gui.cc b/src/simutrans/gui/line_management_gui.cc
index ca99cb21a..119b90b38 100644
--- a/src/simutrans/gui/line_management_gui.cc
+++ b/src/simutrans/gui/line_management_gui.cc
@@ -11,12 +11,14 @@
 #include "../dataobj/schedule.h"
 #include "../dataobj/loadsave.h"
 #include "../dataobj/translator.h"
+#include "../dataobj/route.h"
 
 #include "../vehicle/vehicle.h"
 
 #include "../convoihandle.h"
 #include "../simconvoi.h"
 #include "../obj/depot.h"
+#include "../ground/grund.h"
 #include "../simhalt.h"
 #include "../simline.h"
 #include "../tool/simmenu.h"
@@ -95,6 +97,7 @@ line_management_gui_t::line_management_gui_t( linehandle_t line_, player_t* play
 	loading_info( &loading_text )
 {
 	is_saving_gui = false;
+	route_shown = false;
 	set_table_layout( 3, 0 );
 	set_alignment(ALIGN_TOP);
 	line = line_;
@@ -118,6 +121,13 @@ line_management_gui_t::line_management_gui_t( linehandle_t line_, player_t* play
 	add_component( &loading_info, 2 );
 	loading_text.printf( translator::translate("Capacity: %s\nLoad: %d (%d%%)"), 0, 0, 0);
 
+	// MVP: toggle to draw the line's real route (engine pathfinder) on the main map.
+	// Kept in the always-visible header so it works from every tab.
+	bt_show_route.init( button_t::roundbox_state | button_t::flexible, "Show route on map" );
+	bt_show_route.set_tooltip( "Show the calculated route of this line on the main map." );
+	bt_show_route.add_listener( this );
+	add_component( &bt_show_route, 3 );
+
 	// tab panel: connections, chart panels, details
 	add_component( &switch_mode, 3 );
 	switch_mode.add_listener( this );
@@ -237,6 +247,10 @@ void line_management_gui_t::draw(scr_coord pos, scr_size size)
 		bt_withdraw_line.enable(is_change_allowed);
 		bt_find_convois.enable(is_change_allowed);
 
+		// route overlay needs a convoy to act as the test driver
+		bt_show_route.enable( line->count_convoys() > 0 );
+		bt_show_route.pressed = route_shown;
+
 		if(  line->count_convoys() != old_convoi_count  ) {
 
 			old_convoi_count = line->count_convoys();
@@ -404,6 +418,31 @@ void line_management_gui_t::apply_schedule()
 }
 
 
+// The whole action behind the "Show route on map" button. Extracted so the automated
+// demo/test harness can exercise the exact same code path as the button (see
+// api_line_route_test.cc). The GUI does NOT calculate the route here: it only issues a request
+// to tool_line_route_overlay_t, which runs the pathfinder during a proper step (not in sync_step)
+// and applies the highlight. "s,<id>" shows the line's route, "c" clears it.
+void line_management_gui_t::toggle_route_overlay()
+{
+	route_shown = !route_shown;
+	bt_show_route.pressed = route_shown;
+	tool_t *tool = create_tool( TOOL_LINE_ROUTE_OVERLAY | SIMPLE_TOOL );
+	cbuffer_t buf;
+	if(  route_shown  &&  line.is_bound()  ) {
+		buf.printf( "s,%u", line.get_id() );
+	}
+	else {
+		buf.printf( "c" );
+	}
+	tool->set_default_param( buf );
+	welt->set_tool( tool, player );
+	// init always returns false and the queued command copies the parameters, so it is safe to
+	// delete immediately
+	delete tool;
+}
+
+
 bool line_management_gui_t::action_triggered( gui_action_creator_t *comp, value_t v )
 {
 	if(line->count_convoys()>0) {
@@ -458,6 +497,9 @@ bool line_management_gui_t::action_triggered( gui_action_creator_t *comp, value_
 			delete tmp_tool;
 		}
 	}
+	else if(  comp == &bt_show_route  ) {
+		toggle_route_overlay();
+	}
 	else if(  comp == &bt_find_convois  ) {
 		for(convoihandle_t cnv : welt->convoys()) {
 			if(  cnv->get_owner()==player  ) {
@@ -517,6 +559,15 @@ bool line_management_gui_t::infowin_event( const event_t *ev )
 		}
 		scd.highlight_schedule( false );
 		minimap_t::get_instance()->set_selected_cnv(convoihandle_t());
+		// clear our route overlay so no highlight flags are left on the world (via the tool, so the
+		// clear happens in a proper step); skip when the game itself is closing down
+		if(  route_shown  &&  !is_saving_gui  ) {
+			tool_t *tool = create_tool( TOOL_LINE_ROUTE_OVERLAY | SIMPLE_TOOL );
+			tool->set_default_param( "c" );
+			welt->set_tool( tool, player );
+			delete tool;
+		}
+		route_shown = false;
 	}
 
 	if(  ev->ev_class == INFOWIN  &&  ev->ev_code == WIN_TOP  ) {
diff --git a/src/simutrans/gui/line_management_gui.h b/src/simutrans/gui/line_management_gui.h
index c56beaec7..abb974777 100644
--- a/src/simutrans/gui/line_management_gui.h
+++ b/src/simutrans/gui/line_management_gui.h
@@ -23,6 +23,8 @@
 #include "components/gui_textinput.h"
 
 #include "../linehandle.h"
+#include "../dataobj/koord3d.h"
+#include "../tpl/vector_tpl.h"
 
 class player_t;
 class loadsave_t;
@@ -47,8 +49,15 @@ class line_management_gui_t : public gui_frame_t, public action_listener_t
 	gui_button_to_chart_array_t button_to_chart;
 
 	button_t bt_withdraw_line, bt_find_convois;
+	button_t bt_show_route;
 	gui_scrolled_list_t scrolly_convois, scrolly_halts;
 
+	// Overlay: real route of the line drawn on the main map (proto/line-route-overlay).
+	// The window only tracks whether the overlay is shown; the actual route calculation and the
+	// highlighting are done by tool_line_route_overlay_t during a proper step (not in GUI code /
+	// sync_step). The button just issues a request to that tool.
+	bool route_shown;
+
 	gui_aligned_container_t container_schedule, container_stats, container_convois, container_halts;
 
 	cbuffer_t loading_text;
@@ -89,6 +98,12 @@ public:
 	bool infowin_event( const event_t *ev ) OVERRIDE;
 
 	uint32 get_rdwr_id() OVERRIDE { return magic_line_schedule_rdwr_dummy; }
+
+	// The action behind the "Show route on map" button, shared by the button handler and the
+	// automated demo/test harness so both run the identical code path (api_line_route_test.cc).
+	// It only issues a request to tool_line_route_overlay_t; the diagnostic counters now live on
+	// that tool.
+	void toggle_route_overlay();
 };
 
 #endif
diff --git a/src/simutrans/tool/simmenu.cc b/src/simutrans/tool/simmenu.cc
index 8f219db4f..74fb48c89 100644
--- a/src/simutrans/tool/simmenu.cc
+++ b/src/simutrans/tool/simmenu.cc
@@ -171,6 +171,7 @@ const char* tool_t::id_to_string(uint16 id)
 			CASE_TO_STRING(TOOL_DAY_NIGHT_TOGGLE);
 			CASE_TO_STRING(TOOL_WORK_WORLD);
 			CASE_TO_STRING(TOOL_HALT_PERMISSION);
+			CASE_TO_STRING(TOOL_LINE_ROUTE_OVERLAY);
 		}
 	}
 	else if (id & DIALOGE_TOOL) {
@@ -344,6 +345,7 @@ tool_t* create_simple_tool(int toolnr)
 	case TOOL_SINGLE_WAY_TOOGLE:    tool = new tool_show_single_ways_t();     break;
 	case TOOL_WORK_WORLD:           tool = new tool_work_world_t();           break;
 	case TOOL_HALT_PERMISSION:      tool = new tool_change_permission_t();    break;
+	case TOOL_LINE_ROUTE_OVERLAY:   tool = new tool_line_route_overlay_t();   break;
 	case UNUSED_TOOL_ADD_MESSAGE: // fall-through - intended!!!111elf
 	case UNUSED_WKZ_PWDHASH_TOOL:
 		dbg->warning("create_simple_tool()", "Deprecated tool [%i] requested", toolnr);
diff --git a/src/simutrans/tool/simmenu.h b/src/simutrans/tool/simmenu.h
index 756b41cf1..0f94af0a1 100644
--- a/src/simutrans/tool/simmenu.h
+++ b/src/simutrans/tool/simmenu.h
@@ -134,6 +134,7 @@ enum {
 	TOOL_SINGLE_WAY_TOOGLE,
 	TOOL_WORK_WORLD,
 	TOOL_HALT_PERMISSION,
+	TOOL_LINE_ROUTE_OVERLAY,
 	SIMPLE_TOOL_COUNT,
 	SIMPLE_TOOL = 0x2000
 };
diff --git a/src/simutrans/tool/simtool.cc b/src/simutrans/tool/simtool.cc
index 9d568781f..64d5c5400 100644
--- a/src/simutrans/tool/simtool.cc
+++ b/src/simutrans/tool/simtool.cc
@@ -76,6 +76,8 @@
 #include "../dataobj/schedule.h"
 #include "../dataobj/route.h"
 #include "../dataobj/scenario.h"
+#include "../simline.h"
+#include "../vehicle/vehicle.h"
 #include "../network/network_cmd_ingame.h" // for dragging raise / lower tools
 
 #include "../builder/tunnelbauer.h"
@@ -8977,6 +8979,149 @@ bool tool_work_world_t::init(player_t*)
 }
 
 
+// ---- tool_line_route_overlay_t: a line's real route drawn on the main map -------------------
+// Two independent channels (prissi, forum 24000): the route PATH is drawn procedurally by the map
+// view from welt->get_line_route_overlay() in the owning player's colour, and the scheduled STOPS
+// keep the existing obj_t::highlight bit (their own colour) so they stay distinguishable and
+// selectable. Because the route no longer touches the highlight bit, it can no longer collide with
+// the schedule-stop highlight. Display-only, transient: nothing here is saved.
+// The stop tiles currently highlighted (kept so we can clear exactly them, no map scan).
+static vector_tpl<koord3d> line_route_overlay_stops;
+
+uint32 tool_line_route_overlay_t::calc_route_call_count = 0;
+uint32 tool_line_route_overlay_t::last_segments_attempted = 0;
+uint32 tool_line_route_overlay_t::last_segments_valid = 0;
+uint32 tool_line_route_overlay_t::last_segments_failed = 0;
+uint32 tool_line_route_overlay_t::last_route_tiles = 0;
+
+// Set/clear the obj_t::highlight on the STOP tiles only (the route path is drawn, not flagged).
+static void line_route_apply_stop_highlight(bool marking)
+{
+	karte_t *welt = world();
+	for(  koord3d const& pos : line_route_overlay_stops  ) {
+		grund_t *gr = welt->lookup(pos);
+		if(  gr == NULL  ) {
+			continue;
+		}
+		for(  uint idx = 0;  idx < gr->obj_count();  idx++  ) {
+			obj_t *obj = gr->obj_bei(idx);
+			if(  marking  ) {
+				if(  !obj->is_moving()  ) {
+					obj->set_flag( obj_t::highlight );
+				}
+			}
+			else {
+				obj->clear_flag( obj_t::highlight );
+			}
+		}
+		if(  gr->is_water()  ||  gr->ist_natur()  ) {
+			if(  marking  ) {
+				gr->set_flag( grund_t::marked );
+			}
+			else {
+				gr->clear_flag( grund_t::marked );
+			}
+		}
+		gr->set_flag( grund_t::dirty );
+	}
+}
+
+// Compute the line's real path leg by leg and store it, in order, in welt->access_line_route_overlay()
+// for the view to draw; also collect the scheduled stop tiles. The test driver is the line's first
+// convoy's lead vehicle (it *is* a test_driver_t), so the trace honours that convoy's waytype,
+// electrification and one-way rules. This runs during a step (from the command queue), never in the
+// display loop: the A* node array is static and not reentrant.
+static void line_route_compute(linehandle_t line)
+{
+	karte_t *welt = world();
+	vector_tpl<koord3d>& path = welt->access_line_route_overlay();
+	tool_line_route_overlay_t::calc_route_call_count++;
+	tool_line_route_overlay_t::last_segments_attempted = 0;
+	tool_line_route_overlay_t::last_segments_valid     = 0;
+	tool_line_route_overlay_t::last_segments_failed    = 0;
+	tool_line_route_overlay_t::last_route_tiles         = 0;
+	path.clear();
+	line_route_overlay_stops.clear();
+	if(  !line.is_bound()  ||  line->count_convoys() == 0  ) {
+		return;
+	}
+	convoihandle_t cnv = line->get_convoy(0);
+	if(  !cnv.is_bound()  ||  cnv->get_vehicle_count() == 0  ) {
+		return;
+	}
+	schedule_t *schedule = line->get_schedule();
+	if(  schedule == NULL  ||  schedule->get_count() < 2  ) {
+		return;
+	}
+	// stops keep their own highlight channel: remember the schedule entry tiles
+	for(  uint8 i = 0;  i < schedule->get_count();  i++  ) {
+		line_route_overlay_stops.append_unique( schedule->entries[i].pos );
+	}
+	// route colour = owning player's colour (per-company distinct, minimap convention)
+	if(  line->get_owner()  ) {
+		welt->set_line_route_overlay_color( line->get_owner()->get_player_color1() + 1 );
+	}
+	vehicle_t *test_driver = cnv->front();
+	const sint32 max_speed = cnv->get_min_top_speed();
+	const uint8 count = schedule->get_count();
+	route_t seg;
+	// the schedule is cyclic, so also connect the last entry back to the first
+	for(  uint8 i = 0;  i < count;  i++  ) {
+		const koord3d start = schedule->entries[i].pos;
+		const koord3d ziel  = schedule->entries[(i+1)%count].pos;
+		if(  start == ziel  ) {
+			continue; // repeated stop: zero-length leg
+		}
+		tool_line_route_overlay_t::last_segments_attempted++;
+		if(  seg.calc_route(welt, start, ziel, test_driver, max_speed, 0) != route_t::no_route  ) {
+			tool_line_route_overlay_t::last_segments_valid++;
+			// keep the path ORDERED (not deduped) so the view can draw tile-to-tile segments;
+			// only drop a tile equal to the previous one (leg boundaries share the stop tile)
+			for(  uint32 n = 0;  n < seg.get_count();  n++  ) {
+				const koord3d t = seg.at(n);
+				if(  path.get_count() == 0  ||  path[path.get_count()-1] != t  ) {
+					path.append( t );
+				}
+			}
+		}
+		else {
+			tool_line_route_overlay_t::last_segments_failed++;
+		}
+	}
+	tool_line_route_overlay_t::last_route_tiles = path.get_count();
+}
+
+bool tool_line_route_overlay_t::init(player_t*)
+{
+	karte_t *welt = world();
+	// default_param: "s,<line_id>" show that line's route, "c" (or anything else) clear
+	const char *p = default_param;
+	while(  p  &&  *p  &&  *p <= ' '  ) {
+		p++;
+	}
+	const char cmd = (p  &&  *p) ? *p : 'c';
+
+	// always clear the previous overlay first (stop highlights + drawn path)
+	line_route_apply_stop_highlight( false );
+	line_route_overlay_stops.clear();
+	welt->access_line_route_overlay().clear();
+
+	if(  cmd == 's'  ) {
+		// skip up to and past the comma to reach the id
+		while(  *p  &&  *p++ != ','  ) {
+		}
+		linehandle_t line;
+		line.set_id( (uint16)atoi(p) );
+		if(  line.is_bound()  ) {
+			line_route_compute( line );
+			line_route_apply_stop_highlight( true );
+		}
+	}
+	welt->set_dirty(); // repaint so the drawn route appears / disappears
+	return false; // unsafe tools must return false
+}
+
+
 
 /*
  * Add a message to the message queue
diff --git a/src/simutrans/tool/simtool.h b/src/simutrans/tool/simtool.h
index 08e3dc40c..69dcce0e4 100644
--- a/src/simutrans/tool/simtool.h
+++ b/src/simutrans/tool/simtool.h
@@ -1037,6 +1037,28 @@ public:
 	bool is_work_keeps_game_state() const OVERRIDE { return false; }
 };
 
+/**
+ * Display-only overlay: compute and highlight a line's real route on the main map.
+ * The pathfinder runs here, during a proper step -- never in GUI code / sync_step, which is
+ * enforced by is_init_keeps_game_state()==false (the request is queued from the GUI and executed
+ * from the command queue between steps). It is local/per-client (WFL_LOCAL): nothing is sent over
+ * the network, nothing is saved. The GUI only issues the request; this tool does the work.
+ * default_param: "s,<line_id>" shows that line's route, "c" clears the overlay.
+ */
+class tool_line_route_overlay_t : public tool_t {
+public:
+	tool_line_route_overlay_t() : tool_t(TOOL_LINE_ROUTE_OVERLAY | SIMPLE_TOOL) { flags = WFL_LOCAL | WFL_NO_CHK; }
+	bool init(player_t*) OVERRIDE;
+	bool is_init_keeps_game_state() const OVERRIDE { return false; }
+	bool is_work_keeps_game_state() const OVERRIDE { return false; }
+	// diagnostic counters for the automated demo/test harness (last computation + total passes)
+	static uint32 calc_route_call_count;
+	static uint32 last_segments_attempted;
+	static uint32 last_segments_valid;
+	static uint32 last_segments_failed;
+	static uint32 last_route_tiles;
+};
+
 class tool_rotate90_t : public tool_t {
 public:
 	tool_rotate90_t() : tool_t(TOOL_ROTATE90 | SIMPLE_TOOL) {}
diff --git a/src/simutrans/vehicle/vehicle_base.h b/src/simutrans/vehicle/vehicle_base.h
index e253f5dbc..ee10bb41c 100644
--- a/src/simutrans/vehicle/vehicle_base.h
+++ b/src/simutrans/vehicle/vehicle_base.h
@@ -104,6 +104,11 @@ public:
 
 	static void set_overtaking_offsets( bool driving_on_the_left );
 
+	// Per screen-direction (ribi_t::get_dir 0..7) lane offset in base-64 pixel units, used by
+	// vehicles to shift to the left-hand lane. The table is antisymmetric (offset[opposite] ==
+	// -offset), so the line-route overlay reuses it to draw outward and return on opposite lanes.
+	static sint8 get_driveleft_base_offset( uint8 dir, uint8 axis ) { return driveleft_base_offsets[dir & 7][axis & 1]; }
+
 	// if true, this convoi needs to restart for correct alignment
 	bool need_realignment() const;
 
diff --git a/src/simutrans/world/simworld.cc b/src/simutrans/world/simworld.cc
index e1c354b44..1cf4916f6 100644
--- a/src/simutrans/world/simworld.cc
+++ b/src/simutrans/world/simworld.cc
@@ -1232,6 +1232,7 @@ void karte_t::init(settings_t* const sets, sint8 const* const h_field)
 	ticks = 0;
 	last_step_ticks = ticks;
 	schedule_counter = 0;
+	line_route_overlay.clear(); // display-only overlay: drop any route from a previous world
 	// ticks = 0x7FFFF800;  // Testing the 31->32 bit step
 
 	last_month = 0;
@@ -2039,6 +2040,7 @@ karte_t::karte_t() :
 
 	zeiger = NULL;
 	schedule_counter = 0;
+	line_route_overlay_color = 0; // display-only line-route overlay (empty until a line is shown)
 	nosave_warning = nosave = false;
 	loaded_rotation = 0;
 	last_year = 1930;
@@ -2373,6 +2375,10 @@ DBG_MESSAGE( "karte_t::rotate90()", "called" );
 	// clear marked region
 	zeiger->change_pos( koord3d::invalid );
 
+	// drop the line-route overlay: its stored tiles are pre-rotation coordinates (display only, the
+	// user re-shows the route to recompute it on the rotated map)
+	line_route_overlay.clear();
+
 	// preprocessing, detach stops from factories to prevent crash
 	for(halthandle_t const s : haltestelle_t::get_alle_haltestellen()) {
 		s->release_factory_links();
@@ -3813,6 +3819,9 @@ bool karte_t::load(const char *filename)
 	gfx->set_show_load_cursor(true);
 	loadsave_t file;
 
+	// drop any line-route overlay from the previous game (display only, not saved)
+	line_route_overlay.clear();
+
 	// clear hash table with missing paks (may cause some small memory loss though)
 	pakset_manager_t::clear_missing_paks();
 
diff --git a/src/simutrans/world/simworld.h b/src/simutrans/world/simworld.h
index 80e55ae29..9fec52835 100644
--- a/src/simutrans/world/simworld.h
+++ b/src/simutrans/world/simworld.h
@@ -172,6 +172,15 @@ private:
 	zeiger_t *zeiger;
 	/** @} */
 
+	/**
+	 * Ordered tiles of the line-route overlay currently shown on the main map (display only, never
+	 * saved). Filled by tool_line_route_overlay_t during a step; drawn by the map view. Empty when
+	 * no overlay is shown. See tool_line_route_overlay_t.
+	 */
+	vector_tpl<koord3d> line_route_overlay;
+	/// Colour index (player colour) the overlay route is drawn in.
+	uint8 line_route_overlay_color;
+
 	/**
 	 * Time when last mouse moved to check for ambient sound events.
 	 */
@@ -719,6 +728,12 @@ public:
 	 */
 	zeiger_t * get_zeiger() const { return zeiger; }
 
+	/// Ordered tiles of the line-route overlay (display only). Written by the overlay tool, read by the view.
+	const vector_tpl<koord3d>& get_line_route_overlay() const { return line_route_overlay; }
+	vector_tpl<koord3d>& access_line_route_overlay() { return line_route_overlay; }
+	uint8 get_line_route_overlay_color() const { return line_route_overlay_color; }
+	void set_line_route_overlay_color(uint8 c) { line_route_overlay_color = c; }
+
 	/**
 	 * Marks an area using the grund_t mark flag.
 	 */
