diff --git a/.gitignore b/.gitignore
index b40d932..eef5d36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,11 @@
 Makefile*
+gitmake-all
+gitmake-origin
+gitmake-build
 bin/*
 bundle/*
+bundles/
+save
 !bin/data/chars.grf
 !bin/data/openttdd.grf
 !bin/data/openttdw.grf
@@ -10,3 +15,11 @@ bundle/*
 config.*
 objs/*
 src/rev.cpp
+.cproject
+.project
+data/
+media/openttd.desktop
+patches/
+old/
+grf/
+docs/own
diff --git a/gitmake b/gitmake
new file mode 100644
index 0000000..46a2efb
--- /dev/null
+++ b/gitmake
@@ -0,0 +1,80 @@
+
+VPATH=.:./.git/refs/heads/
+
+gitmake-all: cargodist diaglvl alteconomy
+	touch gitmake-all
+
+check: clean gitmake-origin
+	echo "check OK"
+
+clean:
+	rm -f gitmake-origin
+	rm -f patches/current/*
+
+%:
+	git checkout ${@}-tmp
+	for branch in $(^F); do git merge $${branch}; done 
+	git checkout $@
+	git merge ${@}-tmp
+	test -e gitmake-build && make -j`cat gitmake-build` || true
+	./make_diffs
+
+gitmake-origin:
+	git fetch git://git.openttd.org/openttd/trunk.git master:trunk
+	git log trunk | grep -m 1 '(svn r[0-9]*)' | awk '{print $$2}' | sed -e 's/)//' > gitmake-origin
+	cp gitmake-origin patches/current/TRUNK_VERSION.txt
+	git diff --numstat trunk master | grep -v .gitignore | grep -v gitmake | grep -v make_diffs && touch gitmake-origin || touch -t 197001010100 gitmake-origin
+
+master: gitmake-origin
+	git checkout $@
+	git merge trunk
+	test -e gitmake-build && make -j`cat gitmake-build` || true
+
+smallmap-zoom-in: master 
+
+supplyscale: components
+
+cargodist: station-gui smallmap-stats warning-sign supplyscale
+
+station-gui: cargomap
+
+smallmap-stats: smallmap-zoom-in cargomap
+
+texteff: master
+
+flowmapping-core: mcf
+
+mcf: demands
+
+demands: components
+
+components: capacities
+
+capacities: moving-average
+
+diaglvl: tileiter
+
+tileiter: master
+
+cargomap: flowmapping-core texteff multimap reservation
+
+reservation: master
+
+multimap: master
+
+warning-sign: demands
+
+moving-average: master
+
+delivery-rating: master
+
+rating-weight: delivery-rating
+
+rating-payment: delivery-rating
+
+alteconomy: rating-payment rating-weight
+
+push: master cargodist station-gui smallmap-stats flowmapping-core mcf demands components capacities smallmap-zoom-in diaglvl tileiter texteff cargomap multimap reservation warning-sign moving-average supplyscale delivery-rating rating-weight rating-payment alteconomy
+	git push ulf@laramies.com:~/fonsinchen/openttd.git $(^F)
+	rsync -av --delete patches ulf@laramies.com:~/fonsinchen/
+
diff --git a/make_diffs b/make_diffs
new file mode 100755
index 0000000..00e7aed
--- /dev/null
+++ b/make_diffs
@@ -0,0 +1,6 @@
+#!/bin/sh
+BRANCH=`git status | grep "On branch" | awk '{print $4}'`
+git diff ${BRANCH}-tmp > patches/${BRANCH}_`cat gitmake-origin`.diff
+git diff master > patches/against_trunk/${BRANCH}_`cat gitmake-origin`.diff
+ln -sf ../${BRANCH}_`cat gitmake-origin`.diff patches/current/${BRANCH}.diff
+ln -sf ../against_trunk/${BRANCH}_`cat gitmake-origin`.diff patches/current/trunk-${BRANCH}.diff
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index d7a5ffc..c22e41a 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -588,6 +588,22 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\map.cpp"
 				>
 			</File>
@@ -1084,6 +1100,30 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\demand_settings.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph_type.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\livery.h"
 				>
 			</File>
@@ -1100,6 +1140,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\moving_average.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\moving_average.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\network\network.h"
 				>
 			</File>
@@ -1708,6 +1756,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\core\multimap.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\overflowsafe_type.hpp"
 				>
 			</File>
@@ -2124,6 +2176,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\saveload\linkgraph_sl.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\saveload\map_sl.cpp"
 				>
 			</File>
diff --git a/projects/openttd_vs90.sln b/projects/openttd_vs90.sln
index 431c379..aa13b19 100644
--- a/projects/openttd_vs90.sln
+++ b/projects/openttd_vs90.sln
@@ -28,8 +28,8 @@ Global
 		{668328A0-B40E-4CDB-BD72-D0064424414A}.Debug|Win32.Build.0 = Debug|Win32
 		{668328A0-B40E-4CDB-BD72-D0064424414A}.Debug|x64.ActiveCfg = Debug|x64
 		{668328A0-B40E-4CDB-BD72-D0064424414A}.Debug|x64.Build.0 = Debug|x64
-		{668328A0-B40E-4CDB-BD72-D0064424414A}.Release|Win32.ActiveCfg = Release|Win32
-		{668328A0-B40E-4CDB-BD72-D0064424414A}.Release|Win32.Build.0 = Release|Win32
+		{668328A0-B40E-4CDB-BD72-D0064424414A}.Release|Win32.ActiveCfg = Release|x64
+		{668328A0-B40E-4CDB-BD72-D0064424414A}.Release|Win32.Build.0 = Release|x64
 		{668328A0-B40E-4CDB-BD72-D0064424414A}.Release|x64.ActiveCfg = Release|x64
 		{668328A0-B40E-4CDB-BD72-D0064424414A}.Release|x64.Build.0 = Release|x64
 		{A133A442-BD0A-4ADE-B117-AD7545E4BDD1}.Debug|Win32.ActiveCfg = Debug|Win32
diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj
index 9021d62..8cb98e2 100644
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -585,6 +585,22 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\map.cpp"
 				>
 			</File>
@@ -1081,6 +1097,30 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\demand_settings.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph_type.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\livery.h"
 				>
 			</File>
@@ -1097,6 +1137,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\moving_average.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\moving_average.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\network\network.h"
 				>
 			</File>
@@ -1705,6 +1753,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\core\multimap.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\overflowsafe_type.hpp"
 				>
 			</File>
@@ -2121,6 +2173,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\saveload\linkgraph_sl.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\saveload\map_sl.cpp"
 				>
 			</File>
diff --git a/source.list b/source.list
index adae228..c0c07bd 100644
--- a/source.list
+++ b/source.list
@@ -32,6 +32,10 @@ heightmap.cpp
 highscore.cpp
 ini.cpp
 landscape.cpp
+linkgraph/demands.cpp
+linkgraph/flowmapper.cpp
+linkgraph/linkgraph.cpp
+linkgraph/mcf.cpp
 map.cpp
 misc.cpp
 mixer.cpp
@@ -180,10 +184,18 @@ industrytype.h
 ini_type.h
 landscape.h
 landscape_type.h
+linkgraph/demands.h
+linkgraph/demand_settings.h
+linkgraph/flowmapper.h
+linkgraph/linkgraph.h
+linkgraph/linkgraph_type.h
+linkgraph/mcf.h
 livery.h
 map_func.h
 map_type.h
 mixer.h
+moving_average.h
+moving_average.cpp
 network/network.h
 network/network_base.h
 network/network_client.h
@@ -352,6 +364,7 @@ core/geometry_type.hpp
 core/math_func.cpp
 core/math_func.hpp
 core/mem_func.hpp
+core/multimap.hpp
 core/overflowsafe_type.hpp
 core/pool_func.hpp
 core/pool_type.hpp
@@ -460,6 +473,7 @@ saveload/gamelog_sl.cpp
 saveload/group_sl.cpp
 saveload/industry_sl.cpp
 saveload/labelmaps_sl.cpp
+saveload/linkgraph_sl.cpp
 saveload/map_sl.cpp
 saveload/misc_sl.cpp
 saveload/newgrf_sl.cpp
diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp
index 395c6ed..4cdf361 100644
--- a/src/aircraft_cmd.cpp
+++ b/src/aircraft_cmd.cpp
@@ -1306,6 +1306,7 @@ static void AircraftEntersTerminal(Aircraft *v)
 	if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
 
 	Station *st = Station::Get(v->targetairport);
+	StationID previous_station = v->last_station_visited;
 	v->last_station_visited = v->targetairport;
 
 	/* Check if station was ever visited before */
@@ -1322,7 +1323,7 @@ static void AircraftEntersTerminal(Aircraft *v)
 		AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
 	}
 
-	v->BeginLoading();
+	v->BeginLoading(previous_station);
 }
 
 static void AircraftLandAirplane(Aircraft *v)
diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp
index 26a92e5..334c528 100644
--- a/src/autoreplace_cmd.cpp
+++ b/src/autoreplace_cmd.cpp
@@ -118,7 +118,7 @@ static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chai
 			uint amount = min(src->cargo.Count(), dest->cargo_cap - dest->cargo.Count());
 			if (amount <= 0) continue;
 
-			src->cargo.MoveTo(&dest->cargo, amount, VehicleCargoList::MTA_UNLOAD, NULL);
+			src->cargo.MoveTo(&dest->cargo, amount);
 		}
 	}
 
diff --git a/src/cargo_type.h b/src/cargo_type.h
index 8d9a1a8..277a01c 100644
--- a/src/cargo_type.h
+++ b/src/cargo_type.h
@@ -13,6 +13,8 @@
 #define CARGO_TYPE_H
 
 #include "core/enum_type.hpp"
+#include "core/math_func.hpp"
+#include "saveload/saveload.h"
 
 /**
  * Cargo slots to indicate a cargo type within a game.
@@ -23,6 +25,7 @@ typedef byte CargoID;
 
 /** Available types of cargo */
 enum CargoTypes {
+	CT_BEGIN        =  0,
 	/* Temperate */
 	CT_PASSENGERS   =  0,
 	CT_COAL         =  1,
@@ -64,6 +67,7 @@ enum CargoTypes {
 	CT_FIZZY_DRINKS = 11,
 
 	NUM_CARGO       = 32,   ///< Maximal number of cargo types in a game.
+	CT_END          = 32,
 
 	CT_NO_REFIT     = 0xFE, ///< Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-new).
 	CT_INVALID      = 0xFF, ///< Invalid cargo type.
@@ -104,6 +108,24 @@ public:
 	}
 };
 
+class GlobalCargoAcceptance {
+private:
+	CargoArray acceptance;
+	CargoArray current_tile_loop;
+
+public:
+	friend const SaveLoad *GetGlobalCargoAcceptanceDesc();
+
+	FORCEINLINE uint Get(CargoID c) {return (acceptance)[c];}
+	FORCEINLINE CargoArray &CurrentLoop() {return current_tile_loop;}
+	FORCEINLINE void NewLoop()
+	{
+		acceptance = current_tile_loop;
+		current_tile_loop.Clear();
+	}
+
+	static GlobalCargoAcceptance inst;
+};
 
 /** Types of cargo source and destination */
 enum SourceType {
diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp
index 418eb8e..0242d7a 100644
--- a/src/cargopacket.cpp
+++ b/src/cargopacket.cpp
@@ -10,7 +10,9 @@
 /** @file cargopacket.cpp Implementation of the cargo packets */
 
 #include "stdafx.h"
+#include "station_base.h"
 #include "core/pool_func.hpp"
+#include "core/random_func.hpp"
 #include "economy_base.h"
 
 /* Initialize the cargopacket-pool */
@@ -47,13 +49,13 @@ CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, So
 }
 
 CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share, SourceType source_type, SourceID source_id) :
-		feeder_share(feeder_share),
-		count(count),
-		days_in_transit(days_in_transit),
-		source_id(source_id),
-		source(source),
-		source_xy(source_xy),
-		loaded_at_xy(loaded_at_xy)
+	feeder_share(feeder_share),
+	count(count),
+	days_in_transit(days_in_transit),
+	source_id(source_id),
+	source(source),
+	source_xy(source_xy),
+	loaded_at_xy(loaded_at_xy)
 {
 	assert(count != 0);
 	this->source_type = source_type;
@@ -72,6 +74,22 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
 	}
 }
 
+CargoPacket * CargoPacket::Split(uint new_size)
+{
+	Money fs = this->feeder_share * new_size / static_cast<uint>(this->count);
+	CargoPacket *cp_new = new CargoPacket(new_size, this->days_in_transit, this->source, this->source_xy, this->loaded_at_xy, fs, this->source_type, this->source_id);
+	this->feeder_share -= fs;
+	this->count -= new_size;
+	return cp_new;
+}
+
+void CargoPacket::Merge(CargoPacket *cp)
+{
+	this->count += cp->count;
+	this->feeder_share += cp->feeder_share;
+	delete cp;
+}
+
 /**
  * Invalidates (sets source to INVALID_STATION) all cargo packets from given station
  * @param sid the station that gets removed
@@ -90,37 +108,32 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
  *
  */
 
-template <class Tinst>
-CargoList<Tinst>::~CargoList()
+template <class Tinst, class Tcont>
+CargoList<Tinst, Tcont>::~CargoList()
 {
 	for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
 		delete *it;
 	}
 }
 
-template <class Tinst>
-void CargoList<Tinst>::RemoveFromCache(const CargoPacket *cp)
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp)
 {
 	this->count                 -= cp->count;
 	this->cargo_days_in_transit -= cp->days_in_transit * cp->count;
 }
 
-template <class Tinst>
-void CargoList<Tinst>::AddToCache(const CargoPacket *cp)
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
 {
 	this->count                 += cp->count;
 	this->cargo_days_in_transit += cp->days_in_transit * cp->count;
 }
-
-template <class Tinst>
-void CargoList<Tinst>::Append(CargoPacket *cp)
+void VehicleCargoList::MergeOrPush(CargoPacket *cp)
 {
-	assert(cp != NULL);
-	static_cast<Tinst *>(this)->AddToCache(cp);
-
-	for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
+	for (CargoPacketList::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
 		CargoPacket *icp = *it;
-		if (Tinst::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
+		if (VehicleCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
 			icp->count        += cp->count;
 			icp->feeder_share += cp->feeder_share;
 
@@ -134,8 +147,16 @@ void CargoList<Tinst>::Append(CargoPacket *cp)
 }
 
 
-template <class Tinst>
-void CargoList<Tinst>::Truncate(uint max_remaining)
+void VehicleCargoList::Append(CargoPacket *cp)
+{
+	assert(cp != NULL);
+	this->AddToCache(cp);
+	this->MergeOrPush(cp);
+}
+
+
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::Truncate(uint max_remaining)
 {
 	for (Iterator it(packets.begin()); it != packets.end(); /* done during loop*/) {
 		CargoPacket *cp = *it;
@@ -161,87 +182,94 @@ void CargoList<Tinst>::Truncate(uint max_remaining)
 	}
 }
 
-template <class Tinst>
-template <class Tother_inst>
-bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, uint data)
+void VehicleCargoList::Reserve(CargoPacket *cp)
 {
-	assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
-	assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL);
+	assert(cp != NULL);
+	this->AddToCache(cp);
+	this->reserved_count += cp->count;
+	this->reserved.push_back(cp);
+}
+
 
-	Iterator it(this->packets.begin());
-	while (it != this->packets.end() && max_move > 0) {
+void VehicleCargoList::Unreserve(StationID next, StationCargoList *dest)
+{
+	Iterator it(this->reserved.begin());
+	while (it != this->reserved.end()) {
 		CargoPacket *cp = *it;
-		if (cp->source == data && mta == MTA_FINAL_DELIVERY) {
-			/* Skip cargo that originated from this station. */
-			++it;
-			continue;
-		}
+		this->RemoveFromCache(cp);
+		this->reserved_count -= cp->count;
+		dest->Append(next, cp);
+		this->reserved.erase(it++);
+	}
+}
 
+uint VehicleCargoList::LoadReserved(uint max_move)
+{
+	uint orig_max = max_move;
+	Iterator it(this->reserved.begin());
+	while (it != this->reserved.end() && max_move > 0) {
+		CargoPacket *cp = *it;
 		if (cp->count <= max_move) {
 			/* Can move the complete packet */
 			max_move -= cp->count;
-			this->packets.erase(it++);
-			static_cast<Tinst *>(this)->RemoveFromCache(cp);
-			switch(mta) {
-				case MTA_FINAL_DELIVERY:
-					payment->PayFinalDelivery(cp, cp->count);
-					delete cp;
-					continue; // of the loop
-
-				case MTA_CARGO_LOAD:
-					cp->loaded_at_xy = data;
-					break;
-
-				case MTA_TRANSFER:
-					cp->feeder_share += payment->PayTransfer(cp, cp->count);
-					break;
-
-				case MTA_UNLOAD:
-					break;
-			}
-			dest->Append(cp);
-			continue;
-		}
-
-		/* Can move only part of the packet */
-		if (mta == MTA_FINAL_DELIVERY) {
-			/* Final delivery doesn't need package splitting. */
-			payment->PayFinalDelivery(cp, max_move);
-
-			/* Remove the delivered data from the cache */
-			uint left = cp->count - max_move;
-			cp->count = max_move;
-			static_cast<Tinst *>(this)->RemoveFromCache(cp);
-
-			/* Final delivery payment pays the feeder share, so we have to
-			 * reset that so it is not 'shown' twice for partial unloads. */
-			cp->feeder_share = 0;
-			cp->count = left;
+			this->reserved.erase(it++);
+			this->reserved_count -= cp->count;
+			this->MergeOrPush(cp);
 		} else {
-			/* But... the rest needs package splitting. */
-			Money fs = cp->feeder_share * max_move / static_cast<uint>(cp->count);
-			cp->feeder_share -= fs;
 			cp->count -= max_move;
-
-			CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, (mta == MTA_CARGO_LOAD) ? data : cp->loaded_at_xy, fs, cp->source_type, cp->source_id);
-			static_cast<Tinst *>(this)->RemoveFromCache(cp_new); // this reflects the changes in cp.
-
-			if (mta == MTA_TRANSFER) {
-				/* Add the feeder share before inserting in dest. */
-				cp_new->feeder_share += payment->PayTransfer(cp_new, max_move);
-			}
-
-			dest->Append(cp_new);
+			CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, cp->loaded_at_xy, 0, cp->source_type, cp->source_id);
+			this->MergeOrPush(cp_new);
+			this->reserved_count -= max_move;
+			max_move = 0;
 		}
+	}
+	return orig_max - max_move;
+}
 
-		max_move = 0;
+template<class Tinst, class Tcont>
+uint CargoList<Tinst, Tcont>::MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place, bool reserve)
+{
+	CargoPacket *packet = MovePacket(it, cap, load_place);
+	uint ret = packet->count;
+	if (reserve) {
+		dest->Reserve(packet);
+	} else {
+		dest->Append(packet);
 	}
+	return ret;
+}
+
+template<class Tinst, class Tcont>
+uint CargoList<Tinst, Tcont>::MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap)
+{
+	CargoPacket *packet = MovePacket(it, cap);
+	uint ret = packet->count;
+	dest->Append(next, packet);
+	return ret;
+}
 
-	return it != packets.end();
+template<class Tinst, class Tcont>
+CargoPacket *CargoList<Tinst, Tcont>::MovePacket(Iterator &it, uint cap, TileIndex load_place)
+{
+	CargoPacket *packet = *it;
+	/* load the packet if possible */
+	if (packet->count > cap) {
+		/* packet needs to be split */
+		packet = packet->Split(cap);
+		assert(packet->count == cap);
+		++it;
+	} else {
+		this->packets.erase(it++);
+	}
+	static_cast<Tinst *>(this)->RemoveFromCache(packet);
+	if (load_place != INVALID_TILE) {
+		packet->loaded_at_xy = load_place;
+	}
+	return packet;
 }
 
-template <class Tinst>
-void CargoList<Tinst>::InvalidateCache()
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::InvalidateCache()
 {
 	this->count = 0;
 	this->cargo_days_in_transit = 0;
@@ -251,6 +279,186 @@ void CargoList<Tinst>::InvalidateCache()
 	}
 }
 
+VehicleCargoList::~VehicleCargoList()
+{
+	for (Iterator it(this->reserved.begin()); it != this->reserved.end(); ++it) {
+		delete *it;
+	}
+}
+
+uint VehicleCargoList::DeliverPacket(Iterator &c, uint remaining_unload, CargoPayment *payment) {
+	CargoPacket * p = *c;
+	uint loaded = 0;
+	if (p->count <= remaining_unload) {
+		payment->PayFinalDelivery(p, p->count);
+		packets.erase(c++);
+		this->RemoveFromCache(p);
+		loaded = p->count;
+		delete p;
+	} else {
+		payment->PayFinalDelivery(p, remaining_unload);
+		this->count -= remaining_unload;
+		this->cargo_days_in_transit -= remaining_unload * p->days_in_transit;
+		this->feeder_share -= p->feeder_share;
+		p->feeder_share = 0;
+		p->count -= remaining_unload;
+		loaded = remaining_unload;
+		++c;
+	}
+	return loaded;
+}
+
+uint VehicleCargoList::KeepPacket(Iterator &c)
+{
+	CargoPacket *cp = *c;
+	this->reserved.push_back(cp);
+	this->reserved_count += cp->count;
+	this->packets.erase(c++);
+	return cp->count;
+}
+
+
+uint VehicleCargoList::TransferPacket(Iterator &c, uint remaining_unload, GoodsEntry *dest, CargoPayment *payment, StationID next)
+{
+	CargoPacket *p = this->MovePacket(c, remaining_unload);
+	p->feeder_share += payment->PayTransfer(p, p->count);
+	uint ret = p->count;
+	dest->cargo.Append(next, p);
+	SetBit(dest->acceptance_pickup, GoodsEntry::PICKUP);
+	return ret;
+}
+
+/* static */ UnloadType VehicleCargoList::WillUnloadOld(byte flags, StationID curr_station, StationID source)
+{
+	/* try to unload cargo */
+	bool move = (flags & (UL_DELIVER | UL_ACCEPTED | UL_TRANSFER)) != 0;
+	/* try to deliver cargo if unloading */
+	bool deliver = (flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && (source != curr_station);
+	/* transfer cargo if delivery was unsuccessful */
+	bool transfer = (flags & (UL_TRANSFER | UL_DELIVER)) != 0;
+	if (move) {
+		if(deliver) {
+			return UL_DELIVER;
+		} else if (transfer) {
+			return UL_TRANSFER;
+		} else {
+			/* this case is for (non-)delivery to the source station without special flags.
+			 * like the code in MoveTo did, we keep the packet in this case
+			 */
+			return UL_KEEP;
+		}
+	} else {
+		return UL_KEEP;
+	}
+}
+
+/* static */ UnloadType VehicleCargoList::WillUnloadCargoDist(byte flags, StationID curr_station, StationID next_station, StationID via, StationID source)
+{
+	if (via == curr_station) {
+		/* this is the final destination, deliver ... */
+		if (flags & UL_TRANSFER) {
+			/* .. except if explicitly told not to do so ... */
+			return UL_TRANSFER;
+		} else if (flags & UL_ACCEPTED) {
+			return UL_DELIVER;
+		} else if (flags & UL_DELIVER) {
+			/* .. or if the station suddenly doesn't accept our cargo, but we have an explicit deliver order... */
+			return UL_TRANSFER;
+		} else {
+			/* .. or else if it doesn't accept. */
+			return UL_KEEP;
+		}
+	} else {
+		/* packet has to travel on, find out if it can stay on board */
+		if (flags & UL_DELIVER) {
+			/* order overrides cargodist:
+			 * play by the old loading rules here as player is interfering with cargodist
+			 * try to deliver, as move has been forced upon us */
+			if ((flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && source != curr_station) {
+				return UL_DELIVER;
+			} else {
+				/* transfer cargo, as delivering didn't work */
+				/* plan might still be fulfilled as the packet can be picked up by another vehicle travelling to "via" */
+				return UL_TRANSFER;
+			}
+		} else if (flags & UL_TRANSFER) {
+			/* transfer forced, plan still fulfilled as above */
+			return UL_TRANSFER;
+		} else if (next_station == via) {
+			/* vehicle goes to the packet's next hop or has nondeterministic order: keep the packet*/
+			return UL_KEEP;
+		} else {
+			/* vehicle goes somewhere else, transfer the packet*/
+			return UL_TRANSFER;
+		}
+	}
+}
+
+void VehicleCargoList::SwapReserved()
+{
+	assert(this->packets.empty());
+	this->packets.swap(this->reserved);
+	this->reserved_count = 0;
+}
+
+uint VehicleCargoList::MoveToStation(GoodsEntry * dest, uint max_unload, OrderUnloadFlags order_flags, StationID curr_station, StationID next_station, CargoPayment *payment) {
+	uint remaining_unload = max_unload;
+	uint unloaded;
+	UnloadType action;
+	byte flags = GetUnloadFlags(dest, order_flags);
+
+	for(Iterator c = packets.begin(); c != packets.end() && remaining_unload > 0;) {
+		StationID source = (*c)->source;
+		FlowStatSet &flows = dest->flows[source];
+		FlowStatSet::iterator begin = flows.begin();
+		StationID via = (begin != flows.end() ? begin->Via() : INVALID_STATION);
+		if (via != INVALID_STATION && next_station != INVALID_STATION) {
+			/* use cargodist unloading*/
+			action = WillUnloadCargoDist(flags, curr_station, next_station, via, source);
+		} else {
+			/* there is no plan: use normal unloading */
+			action = WillUnloadOld(flags, curr_station, source);
+		}
+
+		switch(action) {
+			case UL_DELIVER:
+				unloaded = this->DeliverPacket(c, remaining_unload, payment);
+				if (via != INVALID_STATION) {
+					if (via == curr_station) {
+						dest->UpdateFlowStats(flows, begin, unloaded);
+					} else {
+						dest->UpdateFlowStats(flows, unloaded, curr_station);
+					}
+				}
+				remaining_unload -= unloaded;
+				break;
+			case UL_TRANSFER:
+				/* TransferPacket may split the packet and return the transferred part */
+				if (via == curr_station) {
+					via = (++begin != flows.end()) ? begin->Via() : INVALID_STATION;
+				}
+				unloaded = this->TransferPacket(c, remaining_unload, dest, payment, via);
+				if (via != INVALID_STATION) {
+					dest->UpdateFlowStats(flows, begin, unloaded);
+				}
+				remaining_unload -= unloaded;
+				break;
+			case UL_KEEP:
+				unloaded = this->KeepPacket(c);
+				if (via != INVALID_STATION && next_station != INVALID_STATION) {
+					if (via == next_station) {
+						dest->UpdateFlowStats(flows, begin, unloaded);
+					} else {
+						dest->UpdateFlowStats(flows, unloaded, next_station);
+					}
+				}
+				break;
+			default:
+				NOT_REACHED();
+		}
+	}
+	return max_unload - remaining_unload;
+}
 
 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
 {
@@ -264,6 +472,17 @@ void VehicleCargoList::AddToCache(const CargoPacket *cp)
 	this->Parent::AddToCache(cp);
 }
 
+uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
+{
+	uint orig_cap = cap;
+	Iterator it = packets.begin();
+	while(it != packets.end() && cap > 0) {
+		cap -= MovePacket(dest, it, cap);
+	}
+	return orig_cap - cap;
+}
+
+
 void VehicleCargoList::AgeCargo()
 {
 	for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
@@ -276,21 +495,119 @@ void VehicleCargoList::AgeCargo()
 	}
 }
 
+/* static */ byte VehicleCargoList::GetUnloadFlags(GoodsEntry *dest, OrderUnloadFlags order_flags)
+{
+	byte flags = 0;
+	if (HasBit(dest->acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
+		flags |= UL_ACCEPTED;
+	}
+	if (order_flags & OUFB_UNLOAD) {
+		flags |= UL_DELIVER;
+	}
+	if (order_flags & OUFB_TRANSFER) {
+		flags |= UL_TRANSFER;
+	}
+	return flags;
+}
+
+/*
+ *
+ * Station cargo list implementation
+ *
+ */
+
+void StationCargoList::Append(StationID next, CargoPacket *cp)
+{
+	assert(cp != NULL);
+	this->AddToCache(cp);
+	StationCargoPacketMap::List &list = this->packets[next];
+
+	for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
+		CargoPacket *icp = *it;
+		if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
+			icp->Merge(cp);
+			return;
+		}
+	}
+
+	/* The packet could not be merged with another one */
+	list.push_back(cp);
+}
+
+uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, TileIndex load_place, bool reserve) {
+	uint orig_cap = cap;
+	while(begin != end && cap > 0) {
+		cap -= this->MovePacket(dest, begin, cap, load_place, reserve);
+	}
+	return orig_cap - cap;
+}
+
+uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID selected_station, TileIndex load_place, bool reserve) {
+	uint orig_cap = cap;
+	if (selected_station != INVALID_STATION) {
+		std::pair<Iterator, Iterator> bounds(packets.equal_range(selected_station));
+		cap -= MovePackets(dest, cap, bounds.first, bounds.second, load_place, reserve);
+		if (cap > 0) {
+			bounds = packets.equal_range(INVALID_STATION);
+			cap -= MovePackets(dest, cap, bounds.first, bounds.second, load_place, reserve);
+		}
+	} else {
+		cap -= MovePackets(dest, cap, packets.begin(), packets.end(), load_place, reserve);
+	}
+	return orig_cap - cap;
+}
+
+void StationCargoList::RerouteStalePackets(StationID curr, StationID to, GoodsEntry *ge) {
+	std::pair<Iterator, Iterator> range(packets.equal_range(to));
+	for(Iterator it(range.first); it != range.second && it.GetKey() == to;) {
+		CargoPacket *packet = *it;
+		packets.erase(it++);
+		StationID next = ge->UpdateFlowStatsTransfer(packet->source, packet->count, curr);
+		assert(next != to);
+
+		/* legal, as insert doesn't invalidate iterators in the MultiMap, however
+		 * this might insert the packet between range.first and range.second (which might be end())
+		 * This is why we check for GetKey above to avoid infinite loops
+		 */
+		packets.Insert(next, packet);
+	}
+}
+
+void StationCargoList::RandomTruncate(uint max_remaining) {
+	uint prev_count = this->count;
+	while (this->count > max_remaining) {
+		for(Iterator it(packets.begin()); it != packets.end();) {
+			if (RandomRange(prev_count) < max_remaining) continue;
+			CargoPacket *packet = *it;
+			StationID next = it.GetKey();
+			uint diff = this->count - max_remaining;
+			if (packet->count > diff) {
+				packet->count -= diff;
+				this->count = max_remaining;
+				this->cargo_days_in_transit -= packet->days_in_transit * diff;
+				return;
+			} else {
+				packets.erase(it++);
+				this->RemoveFromCache(packet);
+				delete packet;
+			}
+		}
+	}
+}
+
 void VehicleCargoList::InvalidateCache()
 {
 	this->feeder_share = 0;
+	this->reserved_count = 0;
 	this->Parent::InvalidateCache();
+	for (ConstIterator it(this->reserved.begin()); it != this->reserved.end(); it++) {
+		this->AddToCache(*it);
+		this->reserved_count += (*it)->count;
+	}
 }
 
 /*
  * We have to instantiate everything we want to be usable.
  */
-template class CargoList<VehicleCargoList>;
-template class CargoList<StationCargoList>;
-
-/** Autoreplace Vehicle -> Vehicle 'transfer' */
-template bool CargoList<VehicleCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
-/** Cargo unloading at a station */
-template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
-/** Cargo loading at a station */
-template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
+template class CargoList<VehicleCargoList, CargoPacketList>;
+template class CargoList<StationCargoList, StationCargoPacketMap>;
diff --git a/src/cargopacket.h b/src/cargopacket.h
index dfd5e72..3827912 100644
--- a/src/cargopacket.h
+++ b/src/cargopacket.h
@@ -15,20 +15,25 @@
 #include "core/pool_type.hpp"
 #include "economy_type.h"
 #include "station_type.h"
+#include "order_type.h"
 #include "cargo_type.h"
 #include "vehicle_type.h"
+#include "core/multimap.hpp"
 #include <list>
 
 /** Unique identifier for a single cargo packet. */
 typedef uint32 CargoPacketID;
 struct CargoPacket;
+struct GoodsEntry;
 
 /** Type of the pool for cargo packets. */
 typedef Pool<CargoPacket, CargoPacketID, 1024, 1048576, true, false> CargoPacketPool;
 /** The actual pool with cargo packets */
 extern CargoPacketPool _cargopacket_pool;
 
-template <class Tinst> class CargoList;
+template <class Tinst, class Tcont> class CargoList;
+class StationCargoList;
+class VehicleCargoList;
 extern const struct SaveLoad *GetCargoPacketDesc();
 
 /**
@@ -46,8 +51,9 @@ private:
 	TileIndex loaded_at_xy;     ///< Location where this cargo has been loaded into the vehicle
 
 	/** The CargoList caches, thus needs to know about it. */
-	template <class Tinst> friend class CargoList;
+	template <class Tinst, class Tcont> friend class CargoList;
 	friend class VehicleCargoList;
+	friend class ReservationList;
 	friend class StationCargoList;
 	/** We want this to be saved, right? */
 	friend const struct SaveLoad *GetCargoPacketDesc();
@@ -164,6 +170,8 @@ public:
 		return this->loaded_at_xy;
 	}
 
+	CargoPacket *Split(uint new_size);
+	void Merge(CargoPacket *other);
 
 	static void InvalidateAllFrom(SourceType src_type, SourceID src);
 	static void InvalidateAllFrom(StationID sid);
@@ -183,33 +191,37 @@ public:
  */
 #define FOR_ALL_CARGOPACKETS(var) FOR_ALL_CARGOPACKETS_FROM(var, 0)
 
+enum UnloadType {
+	UL_KEEP     = 0,      ///< keep cargo on vehicle
+	UL_DELIVER  = 1 << 0, ///< deliver cargo
+	UL_TRANSFER = 1 << 1, ///< transfer cargo
+	UL_ACCEPTED = 1 << 2, ///< cargo is accepted
+};
+
+class StationCargoList;
+class VehicleCargoList;
+
 /**
  * Simple collection class for a list of cargo packets
  * @tparam Tinst The actual instantation of this cargo list
  */
-template <class Tinst>
+template <class Tinst, class Tcont>
 class CargoList {
 public:
-	/** Container with cargo packets */
-	typedef std::list<CargoPacket *> List;
 	/** The iterator for our container */
-	typedef List::iterator Iterator;
+	typedef typename Tcont::iterator Iterator;
 	/** The const iterator for our container */
-	typedef List::const_iterator ConstIterator;
-
-	/** Kind of actions that could be done with packets on move */
-	enum MoveToAction {
-		MTA_FINAL_DELIVERY, ///< "Deliver" the packet to the final destination, i.e. destroy the packet
-		MTA_CARGO_LOAD,     ///< Load the packet onto a vehicle, i.e. set the last loaded station ID
-		MTA_TRANSFER,       ///< The cargo is moved as part of a transfer
-		MTA_UNLOAD,         ///< The cargo is moved as part of a forced unload
-	};
+	typedef typename Tcont::const_iterator ConstIterator;
+	/** The reverse iterator for our container */
+	typedef typename Tcont::reverse_iterator ReverseIterator;
+	/** The const reverse iterator for our container */
+	typedef typename Tcont::const_reverse_iterator ConstReverseIterator;
 
 protected:
 	uint count;                 ///< Cache for the number of cargo entities
 	uint cargo_days_in_transit; ///< Cache for the sum of number of days in transit of each entity; comparable to man-hours
 
-	List packets;               ///< The cargo packets in this list
+	Tcont packets;               ///< The cargo packets in this list
 
 	/**
 	 * Update the cache to reflect adding of this packet.
@@ -225,6 +237,12 @@ protected:
 	 */
 	void RemoveFromCache(const CargoPacket *cp);
 
+	CargoPacket *MovePacket(Iterator &it, uint cap, TileIndex load_place = INVALID_TILE);
+
+	uint MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap);
+
+	uint MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place = INVALID_TILE, bool reserved = false);
+
 public:
 	/** Create the cargo list */
 	CargoList() {}
@@ -235,7 +253,7 @@ public:
 	 * Returns a pointer to the cargo packet list (so you can iterate over it etc).
 	 * @return pointer to the packet list
 	 */
-	FORCEINLINE const List *Packets() const
+	FORCEINLINE const Tcont *Packets() const
 	{
 		return &this->packets;
 	}
@@ -259,15 +277,6 @@ public:
 	}
 
 	/**
-	 * Returns source of the first cargo packet in this list
-	 * @return the before mentioned source
-	 */
-	FORCEINLINE StationID Source() const
-	{
-		return this->Empty() ? INVALID_STATION : this->packets.front()->source;
-	}
-
-	/**
 	 * Returns average number of days in transit for a cargo entity
 	 * @return the before mentioned number
 	 */
@@ -276,16 +285,6 @@ public:
 		return this->count == 0 ? 0 : this->cargo_days_in_transit / this->count;
 	}
 
-
-	/**
-	 * Appends the given cargo packet
-	 * @warning After appending this packet may not exist anymore!
-	 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
-	 * @param cp the cargo packet to add
-	 * @pre cp != NULL
-	 */
-	void Append(CargoPacket *cp);
-
 	/**
 	 * Truncates the cargo in this list to the given amount. It leaves the
 	 * first count cargo entities and removes the rest.
@@ -293,43 +292,30 @@ public:
 	 */
 	void Truncate(uint max_remaining);
 
-	/**
-	 * Moves the given amount of cargo to another list.
-	 * Depending on the value of mta the side effects of this function differ:
-	 *  - MTA_FINAL_DELIVERY: destroys the packets that do not originate from a specific station
-	 *  - MTA_CARGO_LOAD:     sets the loaded_at_xy value of the moved packets
-	 *  - MTA_TRANSFER:       just move without side effects
-	 *  - MTA_UNLOAD:         just move without side effects
-	 * @param dest  the destination to move the cargo to
-	 * @param count the amount of cargo entities to move
-	 * @param mta   how to handle the moving (side effects)
-	 * @param data  Depending on mta the data of this variable differs:
-	 *              - MTA_FINAL_DELIVERY - station ID of packet's origin not to remove
-	 *              - MTA_CARGO_LOAD     - station's tile index of load
-	 *              - MTA_TRANSFER       - unused
-	 *              - MTA_UNLOAD         - unused
-	 * @param payment The payment helper
-	 *
-	 * @pre mta == MTA_FINAL_DELIVERY || dest != NULL
-	 * @pre mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL
-	 * @return true if there are still packets that might be moved from this cargo list
-	 */
-	template <class Tother_inst>
-	bool MoveTo(Tother_inst *dest, uint count, MoveToAction mta, CargoPayment *payment, uint data = 0);
-
 	/** Invalidates the cached data and rebuild it */
 	void InvalidateCache();
 };
 
+typedef std::list<CargoPacket *> CargoPacketList;
+
 /**
  * CargoList that is used for vehicles.
  */
-class VehicleCargoList : public CargoList<VehicleCargoList> {
+class VehicleCargoList : public CargoList<VehicleCargoList, CargoPacketList> {
 protected:
+	static UnloadType WillUnloadOld(byte flags, StationID curr_station, StationID source);
+	static UnloadType WillUnloadCargoDist(byte flags, StationID curr_station, StationID next_station, StationID via, StationID source);
+
+	uint TransferPacket(Iterator &c, uint remaining_unload, GoodsEntry *dest, CargoPayment *payment, StationID next);
+	uint DeliverPacket(Iterator &c, uint remaining_unload, CargoPayment *payment);
+	uint KeepPacket(Iterator &c);
+
 	/** The (direct) parent of this class */
-	typedef CargoList<VehicleCargoList> Parent;
+	typedef CargoList<VehicleCargoList, CargoPacketList> Parent;
 
-	Money feeder_share; ///< Cache for the feeder share
+	CargoPacketList reserved; ///< The packets reserved for unloading in this list
+	Money feeder_share;       ///< Cache for the feeder share
+	uint reserved_count;      ///< count(reserved)
 
 	/**
 	 * Update the cache to reflect adding of this packet.
@@ -345,11 +331,37 @@ protected:
 	 */
 	void RemoveFromCache(const CargoPacket *cp);
 
+	static byte GetUnloadFlags(GoodsEntry *dest, OrderUnloadFlags order_flags);
+
 public:
 	/** The super class ought to know what it's doing */
-	friend class CargoList<VehicleCargoList>;
+	friend class CargoList<VehicleCargoList, CargoPacketList>;
 	/** The vehicles have a cargo list (and we want that saved). */
 	friend const struct SaveLoad *GetVehicleDescription(VehicleType vt);
+	/**
+	 * Moves the given amount of cargo from a vehicle to a station.
+	 * Depending on the value of flags and dest the side effects of this function differ:
+	 *  - dest->acceptance_pickup & GoodsEntry::ACCEPTANCE:
+	 *                        => MoveToStation sets OUF_UNLOAD_IF_POSSIBLE in the flags
+	 *                        packets are accepted here and may be unloaded and/or delivered (=destroyed);
+	 *                        if not using cargodist: all packets are unloaded and delivered
+	 *                        if using cargodist: only packets which have this station as final destination are unloaded and delivered
+	 *                        if using cargodist: other packets may or may not be unloaded, depending on next_station
+	 *                        if not set and using cargodist: packets may still be unloaded, but not delivered.
+	 *  - OUFB_UNLOAD: unload all packets unconditionally;
+	 *                        if OUF_UNLOAD_IF_POSSIBLE set and OUFB_TRANSFER not set: also deliver packets (no matter if using cargodist)
+	 *  - OUFB_TRANSFER: don't deliver any packets;
+	 *                        overrides delivering aspect of OUF_UNLOAD_IF_POSSIBLE
+	 * @param dest         the destination to move the cargo to
+	 * @param max_unload   the maximum amount of cargo entities to move
+	 * @param flags        how to handle the moving (side effects)
+	 * @param curr_station the station where the cargo currently resides
+	 * @param next_station the next unloading station in the vehicle's order list
+	 * @return the number of cargo entities actually moved
+	 */
+	uint MoveToStation(GoodsEntry * dest, uint max_unload, OrderUnloadFlags flags, StationID curr_station, StationID next_station, CargoPayment *payment);
+
+	~VehicleCargoList();
 
 	/**
 	 * Returns total sum of the feeder share for all packets
@@ -361,6 +373,92 @@ public:
 	}
 
 	/**
+	 * tries to merge the packet with another one in the packets list.
+	 * if no fitting packet is found, appends it.
+	 * @param cp the packet to be inserted
+	 */
+	void MergeOrPush(CargoPacket *cp);
+
+	/**
+	 * Appends the given cargo packet
+	 * @warning After appending this packet may not exist anymore!
+	 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
+	 * @param cp the cargo packet to add
+	 * @param check_merge if true, check existing packets in the list for mergability
+	 * @pre cp != NULL
+	 */
+	void Append(CargoPacket *cp);
+
+	/**
+	 * Returns sum of cargo on board the vehicle (ie not only
+	 * reserved)
+	 * @return cargo on board the vehicle
+	 */
+	FORCEINLINE uint OnboardCount() const
+	{
+		return this->count - this->reserved_count;
+	}
+
+	/**
+	 * Returns sum of cargo reserved for the vehicle
+	 * @return cargo reserved for the vehicle
+	 */
+	FORCEINLINE uint ReservedCount() const
+	{
+		return this->reserved_count;
+	}
+
+	/**
+	 * Returns a pointer to the reserved cargo list (so you can iterate over it etc).
+	 * @return pointer to the reserved list
+	 */
+	FORCEINLINE const CargoPacketList *Reserved() const
+	{
+		return &this->reserved;
+	}
+
+	/**
+	 * Returns source of the first cargo packet in this list
+	 * If the regular packets list is empty but there are packets
+	 * in the reservation list it returns the source of the first
+	 * reserved packet.
+	 * @return the before mentioned source
+	 */
+	FORCEINLINE StationID Source() const
+	{
+		if (this->Empty()) {
+			return INVALID_STATION;
+		} else if (this->packets.empty()) {
+			return this->reserved.front()->source;
+		} else {
+			return this->packets.front()->source;
+		}
+	}
+
+	/**
+	 * Reserves a packet for later loading
+	 */
+	void Reserve(CargoPacket *cp);
+
+	/**
+	 * Returns all reserved cargo to the station
+	 */
+	void Unreserve(StationID next, StationCargoList *dest);
+
+	/**
+	 * load packets from the reserved list
+	 * @params count the number of cargo to load
+	 * @return true if there are still packets that might be loaded from the reservation list
+	 */
+	uint LoadReserved(uint count);
+
+	/**
+	 * swap the reserved and packets lists when starting to load cargo.
+	 * @pre this->packets.empty()
+	 */
+	void SwapReserved();
+
+	/**
 	 * Ages the all cargo in this list
 	 */
 	void AgeCargo();
@@ -369,6 +467,13 @@ public:
 	void InvalidateCache();
 
 	/**
+	 * Moves the given amount of cargo to another vehicle (during autoreplace).
+	 * @param dest         the destination to move the cargo to
+	 * @param max_load     the maximum amount of cargo entities to move
+	 */
+	uint MoveTo(VehicleCargoList *dest, uint cap);
+
+	/**
 	 * Are two the two CargoPackets mergeable in the context of
 	 * a list of CargoPackets for a Vehicle?
 	 * @param cp1 the first CargoPacket
@@ -385,17 +490,28 @@ public:
 	}
 };
 
+typedef MultiMap<StationID, CargoPacket *> StationCargoPacketMap;
+
 /**
  * CargoList that is used for stations.
  */
-class StationCargoList : public CargoList<StationCargoList> {
+class StationCargoList : public CargoList<StationCargoList, StationCargoPacketMap> {
 public:
 	/** The super class ought to know what it's doing */
-	friend class CargoList<StationCargoList>;
+	friend class CargoList<StationCargoList, StationCargoPacketMap>;
 	/** The stations, via GoodsEntry, have a CargoList. */
 	friend const struct SaveLoad *GetGoodsDesc();
 
 	/**
+	 * Returns source of the first cargo packet in this list
+	 * @return the before mentioned source
+	 */
+	FORCEINLINE StationID Source() const
+	{
+		return this->Empty() ? INVALID_STATION : this->packets.begin()->second.front()->source;
+	}
+
+	/**
 	 * Are two the two CargoPackets mergeable in the context of
 	 * a list of CargoPackets for a Vehicle?
 	 * @param cp1 the first CargoPacket
@@ -409,6 +525,34 @@ public:
 				cp1->source_type     == cp2->source_type &&
 				cp1->source_id       == cp2->source_id;
 	}
+
+	uint MoveTo(VehicleCargoList *dest, uint cap, StationID next_station, TileIndex load_place = INVALID_TILE, bool reserve = false);
+
+	/**
+	 * Appends the given cargo packet to the range of packets with the same next station
+	 * @warning After appending this packet may not exist anymore!
+	 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
+	 * @param next the next hop
+	 * @param cp the cargo packet to add
+	 * @pre cp != NULL
+	 */
+	void Append(StationID next, CargoPacket *cp);
+
+	/**
+	 * route all packets with station "to" as next hop to a different place, except "curr"
+	 */
+	void RerouteStalePackets(StationID curr, StationID to, GoodsEntry *ge);
+
+	/**
+	 * Truncate where each destination loses roughly the same percentage of its cargo.
+	 * This is done by randomizing the selection of packets to be removed.
+	 */
+	void RandomTruncate(uint max_remaining);
+
+	static void InvalidateAllFrom(SourceType src_type, SourceID src);
+
+protected:
+	uint MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, TileIndex load_place, bool reserve);
 };
 
 #endif /* CARGOPACKET_H */
diff --git a/src/cargotype.cpp b/src/cargotype.cpp
index 3e2e742..c3745e8 100644
--- a/src/cargotype.cpp
+++ b/src/cargotype.cpp
@@ -19,6 +19,7 @@
 #include "table/cargo_const.h"
 
 CargoSpec CargoSpec::array[NUM_CARGO];
+GlobalCargoAcceptance GlobalCargoAcceptance::inst;
 
 /** Bitmask of cargo types available.
  * Initialized during a call to #SetupCargoForClimate.
diff --git a/src/cargotype.h b/src/cargotype.h
index 29c7b54..a1a531d 100644
--- a/src/cargotype.h
+++ b/src/cargotype.h
@@ -131,6 +131,9 @@ void SetupCargoForClimate(LandscapeID l);
 CargoID GetCargoIDByLabel(CargoLabel cl);
 CargoID GetCargoIDByBitnum(uint8 bitnum);
 
+/* set up the cargos to be displayed in the smallmap's route legend */
+void BuildLinkStatsLegend();
+
 /** Does cargo \a c have cargo class \a cc?
  * @param c  Cargo type.
  * @param cc Cargo class.
diff --git a/src/core/math_func.cpp b/src/core/math_func.cpp
index 7f0630a..1a9f398 100644
--- a/src/core/math_func.cpp
+++ b/src/core/math_func.cpp
@@ -46,3 +46,21 @@ int GreatestCommonDivisor(int a, int b)
 	return a;
 
 }
+
+
+/**
+ * Deterministic approximate division.
+ * Cancels out division errors stemming from the integer nature of the division over multiple runs.
+ */
+int DivideApprox(int a, int b) {
+	int random_like = ((a + b) * (a - b)) % b;
+
+	int remainder = a % b;
+
+	int ret = a / b;
+	if (abs(random_like) < abs(remainder)) {
+		ret += ((a < 0) ^ (b < 0)) ? -1 : 1;
+	}
+
+	return ret;
+}
diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp
index e0b9dbc..9d7319c 100644
--- a/src/core/math_func.hpp
+++ b/src/core/math_func.hpp
@@ -317,5 +317,6 @@ static FORCEINLINE uint ToPercent16(uint i)
 
 int LeastCommonMultiple(int a, int b);
 int GreatestCommonDivisor(int a, int b);
+int DivideApprox(int a, int b);
 
 #endif /* MATH_FUNC_HPP */
diff --git a/src/core/multimap.hpp b/src/core/multimap.hpp
new file mode 100644
index 0000000..58e8e97
--- /dev/null
+++ b/src/core/multimap.hpp
@@ -0,0 +1,245 @@
+/*
+ * multimap.hpp
+ *
+ *  Created on: Sep 15, 2009
+ *      Author: alve
+ */
+
+#ifndef MULTIMAP_HPP_
+#define MULTIMAP_HPP_
+
+#include <map>
+#include <list>
+
+template<typename KEY, typename VALUE, typename COMPARE>
+class MultiMap;
+
+template<class MAP_ITER, class LIST_ITER, class KEY, class VALUE, class COMPARE>
+class MultiMapIterator {
+protected:
+	friend class MultiMap<KEY, VALUE, COMPARE>;
+	typedef MultiMapIterator<MAP_ITER, LIST_ITER, KEY, VALUE, COMPARE> Self;
+	LIST_ITER list_iter;
+	MAP_ITER map_iter;
+	bool list_valid;
+
+public:
+	MultiMapIterator() : list_valid(false) {}
+	template<class NONCONST>
+	MultiMapIterator(NONCONST mi) : map_iter(mi), list_valid(false) {}
+	MultiMapIterator(MAP_ITER mi, LIST_ITER li) : list_iter(li), map_iter(mi)
+	{
+		list_valid = (list_iter != map_iter->second.begin());
+	}
+
+	template<class NONCONST>
+	Self &operator=(NONCONST mi)
+	{
+		map_iter = mi;
+		list_valid = false;
+	}
+
+	VALUE &operator*() const
+	{
+		assert(!map_iter->second.empty());
+		if (list_valid) {
+			return list_iter.operator*();
+		} else {
+			return map_iter->second.begin().operator*();
+		}
+	}
+
+	VALUE *operator->() const
+	{
+		assert(!map_iter->second.empty());
+		if (list_valid) {
+			return list_iter.operator->();
+		} else {
+			return map_iter->second.begin().operator->();
+		}
+	}
+
+	FORCEINLINE const MAP_ITER &GetMapIter() const {return map_iter;}
+	FORCEINLINE const LIST_ITER &GetListIter() const {return list_iter;}
+	FORCEINLINE bool ListValid() const {return list_valid;}
+
+	const KEY &GetKey() const {return map_iter->first;}
+
+	Self &operator++()
+	{
+		assert(!map_iter->second.empty());
+		if (list_valid) {
+			if(++list_iter == map_iter->second.end()) {
+				++map_iter;
+				list_valid = false;
+			}
+		} else {
+			list_iter = ++(map_iter->second.begin());
+			if (list_iter == map_iter->second.end()) {
+				++map_iter;
+			} else {
+				list_valid = true;
+			}
+		}
+		return *this;
+	}
+
+	Self operator++(int)
+	{
+		Self tmp = *this;
+		this->operator++();
+		return tmp;
+	}
+
+	Self &operator--()
+	{
+		assert(!map_iter->second.empty());
+		if (!list_valid) {
+			--map_iter;
+			list_iter = map_iter->second.end();
+			assert(!map_iter->second.empty());
+		}
+
+		if(--list_iter == map_iter->second.begin()) {
+			list_valid = false;
+		} else {
+			list_valid = true;
+		}
+
+		return *this;
+	}
+
+	Self operator--(int)
+	{
+		Self tmp = *this;
+		this->operator--();
+		return tmp;
+	}
+};
+
+/* generic comparison functions for const/non-const multimap iterators and map iterators */
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class LIST_ITER2, class KEY, class VALUE1, class VALUE2, class COMPARE>
+bool operator==(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE1, COMPARE> &iter1, const MultiMapIterator<MAP_ITER2, LIST_ITER2, KEY, VALUE2, COMPARE> &iter2)
+{
+	if (iter1.ListValid()) {
+		if (!iter2.ListValid()) {
+			return false;
+		} else if (iter1.GetListIter() != iter2.GetListIter()) {
+			return false;
+		}
+	} else if (iter2.ListValid()) {
+		return false;
+	}
+	return (iter1.GetMapIter() == iter2.GetMapIter());
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class LIST_ITER2, class KEY, class VALUE1, class VALUE2, class COMPARE>
+bool operator!=(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE1, COMPARE> &iter1, const MultiMapIterator<MAP_ITER2, LIST_ITER2, KEY, VALUE2, COMPARE> &iter2)
+{
+	return !(iter1 == iter2);
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator==(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1, const MAP_ITER2 &iter2)
+{
+	return !iter1.ListValid() && iter1.GetMapIter() == iter2;
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator!=(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1, const MAP_ITER2 &iter2)
+{
+	return iter1.ListValid() || iter1.GetMapIter() != iter2;
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator==(const MAP_ITER2 &iter2, const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1)
+{
+	return !iter1.ListValid() && iter1.GetMapIter() == iter2;
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator!=(const MAP_ITER2 &iter2, const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1)
+{
+	return iter1.ListValid() || iter1.GetMapIter() != iter2;
+}
+
+
+/**
+ * hand-rolled multimap as map of lists. behaves mostly like a list, but is sorted
+ * by KEY.
+ */
+template<typename KEY, typename VALUE, typename COMPARE = std::less<KEY> >
+class MultiMap : public std::map<KEY, std::list<VALUE>, COMPARE > {
+public:
+	typedef typename std::list<VALUE> List;
+	typedef typename List::iterator ListIterator;
+	typedef typename List::const_iterator ConstListIterator;
+
+	typedef typename std::map<KEY, List, COMPARE > Map;
+	typedef typename Map::iterator MapIterator;
+	typedef typename Map::const_iterator ConstMapIterator;
+
+	typedef MultiMapIterator<MapIterator, ListIterator, KEY, VALUE, COMPARE> iterator;
+	typedef MultiMapIterator<ConstMapIterator, ConstListIterator, KEY, const VALUE, COMPARE> const_iterator;
+
+	void erase(iterator it)
+	{
+		List &list = it.map_iter->second;
+		assert(!list.empty());
+		if (it.ListValid()) {
+			list.erase(it.list_iter);
+		} else {
+			list.erase(list.begin());
+		}
+
+		if (list.empty()) {
+			Map::erase(it.map_iter);
+		}
+	}
+
+	void Insert(const KEY &key, const VALUE &val)
+	{
+		List &list = (*this)[key];
+		list.push_back(val);
+		assert(!list.empty());
+	}
+
+	size_t size() const
+	{
+		size_t ret = 0;
+		for(ConstMapIterator it = Map::begin(); it != Map::end(); ++it) {
+			ret += it->second.size();
+		}
+		return ret;
+	}
+
+	size_t MapSize() const
+	{
+		return Map::size();
+	}
+
+	std::pair<iterator, iterator> equal_range(const KEY &key)
+	{
+		MapIterator begin(lower_bound(key));
+		if (begin != Map::end() && begin->first == key) {
+			MapIterator end = begin;
+			return std::make_pair(begin, ++end);
+		} else {
+			return std::make_pair(begin, begin);
+		}
+	}
+
+	std::pair<const_iterator, const_iterator> equal_range(const KEY &key) const
+	{
+		ConstMapIterator begin(lower_bound(key));
+		if (begin != Map::end() && begin->first == key) {
+			ConstMapIterator end = begin;
+			return std::make_pair(begin, ++end);
+		} else {
+			return std::make_pair(begin, begin);
+		}
+	}
+};
+
+#endif /* MULTIMAP_HPP_ */
diff --git a/src/economy.cpp b/src/economy.cpp
index de56391..dfae128 100644
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -1033,22 +1033,26 @@ CargoPayment::~CargoPayment()
 
 	this->front->cargo_payment = NULL;
 
-	if (this->visual_profit == 0) return;
+	if (this->visual_profit == 0 && this->visual_transfer == 0) return;
 
 	CompanyID old_company = _current_company;
 	_current_company = this->front->owner;
 
 	SubtractMoneyFromCompany(CommandCost(this->front->GetExpenseType(true), -this->route_profit));
-	this->front->profit_this_year += this->visual_profit << 8;
+	this->front->profit_this_year += (this->visual_profit + this->visual_transfer) << 8;
 
+	int transfer_offset = 0;
 	if (this->route_profit != 0) {
 		if (IsLocalCompany() && !PlayVehicleSound(this->front, VSE_LOAD_UNLOAD)) {
 			SndPlayVehicleFx(SND_14_CASHTILL, this->front);
 		}
 
 		ShowCostOrIncomeAnimation(this->front->x_pos, this->front->y_pos, this->front->z_pos, -this->visual_profit);
-	} else {
-		ShowFeederIncomeAnimation(this->front->x_pos, this->front->y_pos, this->front->z_pos, this->visual_profit);
+		transfer_offset = 6;
+	}
+
+	if (this->visual_transfer != 0){
+		ShowFeederIncomeAnimation(this->front->x_pos + transfer_offset, this->front->y_pos + transfer_offset, this->front->z_pos, this->visual_transfer);
 	}
 
 	_current_company = old_company;
@@ -1090,7 +1094,7 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count)
 
 	profit = profit * _settings_game.economy.feeder_payment_share / 100;
 
-	this->visual_profit += profit; // accumulate transfer profits for whole vehicle
+	this->visual_transfer += profit; // accumulate transfer profits for whole vehicle
 	return profit; // account for the (virtual) profit already made for the cargo packet
 }
 
@@ -1098,7 +1102,7 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count)
  * Prepare the vehicle to be unloaded.
  * @param front_v the vehicle to be unloaded
  */
-void PrepareUnload(Vehicle *front_v)
+void PrepareUnload(Station * curr_station, Vehicle *front_v, StationID next_station_id)
 {
 	/* At this moment loading cannot be finished */
 	ClrBit(front_v->vehicle_flags, VF_LOADING_FINISHED);
@@ -1106,7 +1110,10 @@ void PrepareUnload(Vehicle *front_v)
 	/* Start unloading in at the first possible moment */
 	front_v->load_unload_ticks = 1;
 
-	if ((front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
+	if ((front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) != 0) {
+		/* vehicle will keep all its cargo and LoadUnloadVehicle will never call MoveToStation */
+		UpdateFlows(curr_station, front_v, next_station_id);
+	} else {
 		for (Vehicle *v = front_v; v != NULL; v = v->Next()) {
 			if (v->cargo_cap > 0 && !v->cargo.Empty()) {
 				SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
@@ -1119,40 +1126,67 @@ void PrepareUnload(Vehicle *front_v)
 }
 
 /**
+ * reserves cargo if the full load order and improved_load is set.
+ * @param st The station where the consist is loading at the moment
+ * @param u The front of the loading vehicle consist
+ * @param next_station The next station the vehicle will stop at
+ * @return bit field for the cargo classes with bit for the reserved cargos set (if anything was reserved).
+ */
+uint32 ReserveConsist(Station * st, Vehicle * u, StationID next_station)
+{
+	uint32 ret = 0;
+	if (_settings_game.order.improved_load && (u->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
+		/* Update reserved cargo */
+		for (Vehicle * v = u; v != NULL; v = v->Next()) {
+			// only reserve if the vehicle is not unloading anymore. Otherwise we'll swap in reserved cargo
+			if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) continue;
+			int cap = v->cargo_cap - v->cargo.Count();
+			if (cap > 0) {
+				StationCargoList & list = st->goods[v->cargo_type].cargo;
+				if (list.MoveTo(&v->cargo, cap, next_station, st->xy, true) > 0) {
+					SetBit(ret, v->cargo_type);
+				}
+			}
+		}
+	}
+	return ret;
+}
+
+
+/**
  * Loads/unload the vehicle if possible.
  * @param v the vehicle to be (un)loaded
- * @param cargo_left the amount of each cargo type that is
- *                   virtually left on the platform to be
- *                   picked up by another vehicle when all
- *                   previous vehicles have loaded.
+ * @param cargos_reserved bit field: the cargo classes for which cargo has been reserved in this loading cycle
+ * @return the updated cargo_reserved
  */
-static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
+static uint32 LoadUnloadVehicle(Vehicle *v, uint32 cargos_reserved)
 {
 	assert(v->current_order.IsType(OT_LOADING));
 
 	assert(v->load_unload_ticks != 0);
 
+	StationID last_visited = v->last_station_visited;
+	Station *st = Station::Get(last_visited);
+
+	StationID next_station = INVALID_STATION;
+	OrderList * orders = v->orders.list;
+	if (orders != NULL) {
+		next_station = orders->GetNextStoppingStation(v->cur_order_index, v->type == VEH_TRAIN || v->type == VEH_ROAD);
+	}
+
 	/* We have not waited enough time till the next round of loading/unloading */
 	if (--v->load_unload_ticks != 0) {
-		if (_settings_game.order.improved_load && (v->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
-			/* 'Reserve' this cargo for this vehicle, because we were first. */
-			for (; v != NULL; v = v->Next()) {
-				int cap_left = v->cargo_cap - v->cargo.Count();
-				if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
-			}
-		}
-		return;
+		return cargos_reserved | ReserveConsist(st, v, next_station);
 	}
 
-	StationID last_visited = v->last_station_visited;
-	Station *st = Station::Get(last_visited);
+	OrderUnloadFlags unload_flags = v->current_order.GetUnloadType();
 
 	if (v->type == VEH_TRAIN && (!IsTileType(v->tile, MP_STATION) || GetStationIndex(v->tile) != st->index)) {
 		/* The train reversed in the station. Take the "easy" way
 		 * out and let the train just leave as it always did. */
 		SetBit(v->vehicle_flags, VF_LOADING_FINISHED);
 		v->load_unload_ticks = 1;
-		return;
+		return cargos_reserved;
 	}
 
 	int unloading_time = 0;
@@ -1186,56 +1220,30 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 
 		GoodsEntry *ge = &st->goods[v->cargo_type];
 
-		if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (u->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
-			uint cargo_count = v->cargo.Count();
+		if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) {
+			/* vehicle wants to unload something */
+
+			uint cargo_count = v->cargo.OnboardCount();
 			uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
-			bool remaining = false; // Are there cargo entities in this vehicle that can still be unloaded here?
-			bool accepted  = false; // Is the cargo accepted by the station?
 
 			payment->SetCargo(v->cargo_type);
+			uint delivered = v->cargo.MoveToStation(ge, amount_unloaded, unload_flags, last_visited, next_station, payment);
 
-			if (HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) && !(u->current_order.GetUnloadType() & OUFB_TRANSFER)) {
-				/* The cargo has reached it's final destination, the packets may now be destroyed */
-				remaining = v->cargo.MoveTo<StationCargoList>(NULL, amount_unloaded, VehicleCargoList::MTA_FINAL_DELIVERY, payment, last_visited);
-
-				dirty_vehicle = true;
-				accepted = true;
-			}
-
-			/* The !accepted || v->cargo.Count == cargo_count clause is there
-			 * to make it possible to force unload vehicles at the station where
-			 * they were loaded, but to not force unload the vehicle when the
-			 * station is still accepting the cargo in the vehicle. It doesn't
-			 * accept cargo that was loaded at the same station. */
-			if ((u->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) && (!accepted || v->cargo.Count() == cargo_count)) {
-				remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, u->current_order.GetUnloadType() & OUFB_TRANSFER ? VehicleCargoList::MTA_TRANSFER : VehicleCargoList::MTA_UNLOAD, payment);
-				SetBit(ge->acceptance_pickup, GoodsEntry::PICKUP);
-
-				dirty_vehicle = dirty_station = true;
-			} else if (!accepted) {
-				/* The order changed while unloading (unset unload/transfer) or the
-				 * station does not accept our goods. */
-				ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
-
-				/* Say we loaded something, otherwise we'll think we didn't unload
-				 * something and we didn't load something, so we must be finished
-				 * at this station. Setting the unloaded means that we will get a
-				 * retry for loading in the next cycle. */
-				anything_unloaded = true;
-				continue;
-			}
-
-			/* Deliver goods to the station */
 			st->time_since_unload = 0;
-
-			unloading_time += amount_unloaded;
-
+			unloading_time += delivered;
 			anything_unloaded = true;
-			if (_settings_game.order.gradual_loading && remaining) {
-				completely_emptied = false;
-			} else {
-				/* We have finished unloading (cargo count == 0) */
+			dirty_vehicle = true;
+
+			/* load_amount might (theoretically) be 0, which would make delivered == 0 even though there is still cargo
+			 * in the vehicle. Thus OnboardCount > 0. In that case we can't stop unloading as SwapReserved wouldn't work.
+			 * v->cargo also contains the cargo reserved for the vehicle which is not on board at the moment, but will be
+			 * swapped back when done unloading.
+			 */
+			if (v->cargo.OnboardCount() == 0) {
+				/* done delivering */
+				if (!v->cargo.Empty()) completely_emptied = false;
 				ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
+				v->cargo.SwapReserved();
 			}
 
 			continue;
@@ -1261,28 +1269,19 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 
 		/* If there's goods waiting at the station, and the vehicle
 		 * has capacity for it, load it on the vehicle. */
-		int cap_left = v->cargo_cap - v->cargo.Count();
-		if (!ge->cargo.Empty() && cap_left > 0) {
+		int cap_left = v->cargo_cap - v->cargo.OnboardCount();
+		if (cap_left > 0) {
 			uint cap = cap_left;
-			uint count = ge->cargo.Count();
-
-			/* Skip loading this vehicle if another train/vehicle is already handling
-			 * the same cargo type at this station */
-			if (_settings_game.order.improved_load && cargo_left[v->cargo_type] <= 0) {
-				SetBit(cargo_not_full, v->cargo_type);
-				continue;
-			}
-
-			if (cap > count) cap = count;
 			if (_settings_game.order.gradual_loading) cap = min(cap, load_amount);
+
+			uint loaded = 0;
 			if (_settings_game.order.improved_load) {
-				/* Don't load stuff that is already 'reserved' for other vehicles */
-				cap = min((uint)cargo_left[v->cargo_type], cap);
-				cargo_left[v->cargo_type] -= cap;
+				loaded += v->cargo.LoadReserved(cap);
+			}
+			if (loaded < cap) {
+				assert(v->cargo.ReservedCount() == 0);
+				loaded += ge->cargo.MoveTo(&v->cargo, cap - loaded, next_station, st->xy);
 			}
-
-			if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
-
 			/* TODO: Regarding this, when we do gradual loading, we
 			 * should first unload all vehicles and then start
 			 * loading them. Since this will cause
@@ -1290,23 +1289,28 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 			 * the whole vehicle chain is really totally empty, the
 			 * completely_emptied assignment can then be safely
 			 * removed; that's how TTDPatch behaves too. --pasky */
-			completely_emptied = false;
-			anything_loaded = true;
-
-			ge->cargo.MoveTo(&v->cargo, cap, StationCargoList::MTA_CARGO_LOAD, NULL, st->xy);
+			if (loaded > 0) {
+				completely_emptied = false;
+				anything_loaded = true;
 
-			st->time_since_load = 0;
-			st->last_vehicle_type = v->type;
+				st->time_since_load = 0;
+				st->last_vehicle_type = v->type;
 
-			StationAnimationTrigger(st, st->xy, STAT_ANIM_CARGO_TAKEN, v->cargo_type);
-			AirportAnimationTrigger(st, AAT_STATION_CARGO_TAKEN, v->cargo_type);
+				StationAnimationTrigger(st, st->xy, STAT_ANIM_CARGO_TAKEN, v->cargo_type);
+				AirportAnimationTrigger(st, AAT_STATION_CARGO_TAKEN, v->cargo_type);
 
-			unloading_time += cap;
+				unloading_time += loaded;
 
-			dirty_vehicle = dirty_station = true;
+				dirty_vehicle = dirty_station = true;
+			} else if  (_settings_game.order.improved_load && HasBit(cargos_reserved, v->cargo_type)) {
+				/* Skip loading this vehicle if another train/vehicle is already handling
+				 * the same cargo type at this station */
+				SetBit(cargo_not_full, v->cargo_type);
+				continue;
+			}
 		}
 
-		if (v->cargo.Count() >= v->cargo_cap) {
+		if (v->cargo.OnboardCount() >= v->cargo_cap) {
 			SetBit(cargo_full, v->cargo_type);
 		} else {
 			SetBit(cargo_not_full, v->cargo_type);
@@ -1316,18 +1320,6 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 	/* Only set completly_emptied, if we just unloaded all remaining cargo */
 	completely_emptied &= anything_unloaded;
 
-	/* We update these variables here, so gradual loading still fills
-	 * all wagons at the same time instead of using the same 'improved'
-	 * loading algorithm for the wagons (only fill wagon when there is
-	 * enough to fill the previous wagons) */
-	if (_settings_game.order.improved_load && (u->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
-		/* Update left cargo */
-		for (v = u; v != NULL; v = v->Next()) {
-			int cap_left = v->cargo_cap - v->cargo.Count();
-			if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
-		}
-	}
-
 	v = u;
 
 	if (!anything_unloaded) delete payment;
@@ -1346,7 +1338,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 			if (v->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
 				/* if the aircraft carries passengers and is NOT full, then
 				 * continue loading, no matter how much mail is in */
-				if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap > v->cargo.Count()) ||
+				if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap > v->cargo.OnboardCount()) ||
 						(cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are stull non-full cargos
 					finished_loading = false;
 				}
@@ -1400,6 +1392,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 		st->MarkTilesDirty(true);
 		SetWindowDirty(WC_STATION_VIEW, last_visited);
 	}
+	return cargos_reserved;
 }
 
 /**
@@ -1412,14 +1405,12 @@ void LoadUnloadStation(Station *st)
 	/* No vehicle is here... */
 	if (st->loading_vehicles.empty()) return;
 
-	int cargo_left[NUM_CARGO];
-
-	for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
+	uint32 cargos_reserved = 0;
 
 	std::list<Vehicle *>::iterator iter;
 	for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
 		Vehicle *v = *iter;
-		if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
+		if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) cargos_reserved = LoadUnloadVehicle(v, cargos_reserved);
 	}
 
 	/* Call the production machinery of industries */
diff --git a/src/economy_base.h b/src/economy_base.h
index 5d7124c..c6b9241 100644
--- a/src/economy_base.h
+++ b/src/economy_base.h
@@ -24,9 +24,10 @@ extern CargoPaymentPool _cargo_payment_pool;
  * Helper class to perform the cargo payment.
  */
 struct CargoPayment : CargoPaymentPool::PoolItem<&_cargo_payment_pool> {
-	Vehicle *front;      ///< The front vehicle to do the payment of
-	Money route_profit;  ///< The amount of money to add/remove from the bank account
-	Money visual_profit; ///< The visual profit to show
+	Vehicle *front;        ///< The front vehicle to do the payment of
+	Money route_profit;    ///< The amount of money to add/remove from the bank account
+	Money visual_profit;   ///< The visual profit to show
+	Money visual_transfer; ///< The transfer credits to be shown
 
 	/* Unsaved variables */
 	Company *owner;            ///< The owner of the vehicle
diff --git a/src/economy_func.h b/src/economy_func.h
index dfeefbb..838f994 100644
--- a/src/economy_func.h
+++ b/src/economy_func.h
@@ -33,7 +33,7 @@ void StartupIndustryDailyChanges(bool init_counter);
 Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
 uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations);
 
-void PrepareUnload(Vehicle *front_v);
+void PrepareUnload(Station * curr_station, Vehicle *front_v, StationID next_station_id);
 void LoadUnloadStation(Station *st);
 
 Money GetPrice(Price index, uint cost_factor, const struct GRFFile *grf_file, int shift = 0);
diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp
index 36c8ecc..99f90e7 100644
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -794,6 +794,9 @@ static void TileLoopIndustry_BubbleGenerator(TileIndex tile)
 
 static void TileLoop_Industry(TileIndex tile)
 {
+	uint dummy;
+	AddAcceptedCargo_Industry(tile, GlobalCargoAcceptance::inst.CurrentLoop(), &dummy);
+
 	IndustryGfx newgfx;
 	IndustryGfx gfx;
 
diff --git a/src/landscape.cpp b/src/landscape.cpp
index f122c0c..4c95018 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -669,6 +669,7 @@ void RunTileLoop()
 	assert((tile & ~TILELOOP_ASSERTMASK) == 0);
 	uint count = (MapSizeX() / TILELOOP_SIZE) * (MapSizeY() / TILELOOP_SIZE);
 	do {
+		if (tile == 0) GlobalCargoAcceptance::inst.NewLoop();
 		_tile_type_procs[GetTileType(tile)]->tile_loop_proc(tile);
 
 		if (TileX(tile) < MapSizeX() - TILELOOP_SIZE) {
@@ -940,6 +941,7 @@ void OnTick_Station();
 void OnTick_Industry();
 
 void OnTick_Companies();
+void OnTick_LinkGraph();
 
 void CallLandscapeTick()
 {
@@ -949,4 +951,5 @@ void CallLandscapeTick()
 	OnTick_Industry();
 
 	OnTick_Companies();
+	OnTick_LinkGraph();
 }
diff --git a/src/lang/english.txt b/src/lang/english.txt
index e9f64e7..34811ab 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -23,6 +23,7 @@ STR_NULL                                                        :
 STR_EMPTY                                                       :
 STR_UNDEFINED                                                   :(undefined string)
 STR_JUST_NOTHING                                                :Nothing
+STR_TINY_NOTHING                                                :{TINYFONT}Nothing
 
 # Cargo related strings
 # Plural cargo name
@@ -220,6 +221,7 @@ STR_LIST_FILTER_OSKTITLE                                        :{BLACK}Enter fi
 STR_LIST_FILTER_TOOLTIP                                         :{BLACK}Enter a keyword to filter the list for
 STR_LIST_FILTER_TITLE                                           :{BLACK}Filter string:
 
+STR_TOOLTIP_GROUP_ORDER                                         :{BLACK}Select grouping order
 STR_TOOLTIP_SORT_ORDER                                          :{BLACK}Select sorting order (descending/ascending)
 STR_TOOLTIP_SORT_CRITERIA                                       :{BLACK}Select sorting criteria
 STR_TOOLTIP_FILTER_CRITERIA                                     :{BLACK}Select filtering criteria
@@ -279,6 +281,8 @@ STR_SORT_BY_FACILITY                                            :Station type
 STR_SORT_BY_WAITING                                             :Waiting cargo value
 STR_SORT_BY_RATING_MAX                                          :Highest cargo rating
 STR_SORT_BY_RATING_MIN                                          :Lowest cargo rating
+STR_SORT_BY_AMOUNT                                              :{BLACK}Amount
+STR_SORT_BY_STATION                                             :{BLACK}Station
 STR_SORT_BY_ENGINE_ID                                           :EngineID (classic sort)
 STR_SORT_BY_COST                                                :Cost
 STR_SORT_BY_POWER                                               :Power
@@ -643,12 +647,14 @@ STR_SMALLMAP_CAPTION                                            :{WHITE}Map - {S
 STR_SMALLMAP_TYPE_CONTOURS                                      :Contours
 STR_SMALLMAP_TYPE_VEHICLES                                      :Vehicles
 STR_SMALLMAP_TYPE_INDUSTRIES                                    :Industries
+STR_SMALLMAP_TYPE_ROUTEMAP                                      :Link Statistics
 STR_SMALLMAP_TYPE_ROUTES                                        :Routes
 STR_SMALLMAP_TYPE_VEGETATION                                    :Vegetation
 STR_SMALLMAP_TYPE_OWNERS                                        :Owners
 STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP                  :{BLACK}Show land contours on map
 STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP                       :{BLACK}Show vehicles on map
 STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP                     :{BLACK}Show industries on map
+STR_SMALLMAP_TOOLTIP_SHOW_LINK_STATS_ON_MAP                     :{BLACK}Show link statistics on map
 STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON                   :{BLACK}Show transport routes on map
 STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP                     :{BLACK}Show vegetation on map
 STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP                    :{BLACK}Show land owners on map
@@ -686,10 +692,26 @@ STR_SMALLMAP_LEGENDA_TOWNS                                      :{TINYFONT}{BLAC
 STR_SMALLMAP_LEGENDA_INDUSTRIES                                 :{TINYFONT}{BLACK}Industries
 STR_SMALLMAP_LEGENDA_DESERT                                     :{TINYFONT}{BLACK}Desert
 STR_SMALLMAP_LEGENDA_SNOW                                       :{TINYFONT}{BLACK}Snow
+STR_SMALLMAP_LEGENDA_CAPACITY                                   :{TINYFONT}{BLACK}Capacity
+STR_SMALLMAP_LEGENDA_USAGE                                      :{TINYFONT}{BLACK}Usage
+STR_SMALLMAP_LEGENDA_PLANNED                                    :{TINYFONT}{BLACK}Planned Flow
+STR_SMALLMAP_LEGENDA_SENT                                       :{TINYFONT}{BLACK}Sent Cargo
+STR_SMALLMAP_LEGENDA_SHOW_TEXT                                  :{TINYFONT}{BLACK}As Text
+STR_SMALLMAP_LEGENDA_SHOW_GRAPH                                 :{TINYFONT}{BLACK}As Graphs
+
+STR_SMALLMAP_SUPPLY_CAPTION                                     :{BLACK}Monthly supply at {STATION}
+STR_SMALLMAP_SUPPLY                                             :{TINYFONT}{CARGO}
+STR_SMALLMAP_LINK_CAPTION                                       :{BLACK}Monthly traffic from {STATION} to {STATION}
+STR_SMALLMAP_LINK                                               :{TINYFONT}{STRING} -
+STR_SMALLMAP_LINK_CAPACITY                                      :{TINYFONT}  cap: {NUM}
+STR_SMALLMAP_LINK_USAGE                                         :{TINYFONT}  usg: {NUM}
+STR_SMALLMAP_LINK_PLANNED                                       :{TINYFONT}  pln: {NUM}
+STR_SMALLMAP_LINK_SENT                                          :{TINYFONT}  snt: {NUM}
 
 STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF                   :{BLACK}Toggle town names on/off on map
 STR_SMALLMAP_CENTER                                             :{BLACK}Centre the smallmap on the current position
 STR_SMALLMAP_INDUSTRY                                           :{TINYFONT}{STRING} ({NUM})
+STR_SMALLMAP_LINKSTATS_LEGEND                                   :{TINYFONT}{STRING}
 STR_SMALLMAP_TOWN                                               :{TINYFONT}{WHITE}{TOWN}
 STR_SMALLMAP_DISABLE_ALL                                        :{BLACK}Disable all
 STR_SMALLMAP_ENABLE_ALL                                         :{BLACK}Enable all
@@ -1296,12 +1318,42 @@ STR_CONFIG_SETTING_LARGER_TOWNS                                 :{LTBLUE}Proport
 STR_CONFIG_SETTING_LARGER_TOWNS_DISABLED                        :{LTBLUE}Proportion of towns that will become cities: {ORANGE}None
 STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER                         :{LTBLUE}Initial city size multiplier: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_MODIFIED_ROAD_REBUILD                        :{LTBLUE}Remove absurd road-elements during the road construction: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_AVERAGE_UNIT                                 :{LTBLUE}Moving Average Unit: {ORANGE}{STRING1} days
+STR_CONFIG_SETTING_AVERAGE_LENGTH                               :{LTBLUE}Minimum Moving Average Length: {ORANGE}{STRING1} units
+STR_CONFIG_SETTING_CAP_RATING                                   :{LTBLUE}Cap cargo ratings by size of linkgraph component: {ORANGE}{STRING1} 
+
+STR_CONFIG_SETTING_LINKGRAPH_INTERVAL                           :{LTBLUE}Link graph recalculation interval: {ORANGE}{STRING1} days
+STR_CONFIG_SETTING_DEMAND_PAX                                   :{LTBLUE}Demand function for passengers: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_PAX_SYMMETRIC                         :symmetric
+STR_CONFIG_SETTING_DEMAND_PAX_ANTISYMMETRIC                     :antisymmetric
+STR_CONFIG_SETTING_DEMAND_PAX_UNHANDLED                         :unhandled
+STR_CONFIG_SETTING_DEMAND_MAIL                                  :{LTBLUE}Demand function for mail: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_MAIL_SYMMETRIC                        :symmetric
+STR_CONFIG_SETTING_DEMAND_MAIL_ANTISYMMETRIC                    :antisymmetric
+STR_CONFIG_SETTING_DEMAND_MAIL_UNHANDLED                        :unhandled
+STR_CONFIG_SETTING_DEMAND_EXPRESS                               :{LTBLUE}Demand function for the EXPRESS cargo class: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_EXPRESS_SYMMETRIC                     :symmetric
+STR_CONFIG_SETTING_DEMAND_EXPRESS_ANTISYMMETRIC                 :antisymmetric
+STR_CONFIG_SETTING_DEMAND_EXPRESS_UNHANDLED                     :unhandled
+STR_CONFIG_SETTING_DEMAND_ARMOURED                              :{LTBLUE}Demand function for the ARMOURED cargo class: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_ARMOURED_SYMMETRIC                    :symmetric
+STR_CONFIG_SETTING_DEMAND_ARMOURED_ANTISYMMETRIC                :antisymmetric
+STR_CONFIG_SETTING_DEMAND_ARMOURED_UNHANDLED                    :unhandled
+STR_CONFIG_SETTING_DEMAND_DEFAULT                               :{LTBLUE}Demand function for other cargo classes: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_DEFAULT_SYMMETRIC                     :symmetric
+STR_CONFIG_SETTING_DEMAND_DEFAULT_ANTISYMMETRIC                 :antisymmetric
+STR_CONFIG_SETTING_DEMAND_DEFAULT_UNHANDLED                     :unhandled
+STR_CONFIG_SETTING_LINKGRAPH_ACCURACY                           :{LTBLUE}Accuracy when calculating things on the link graph: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_DISTANCE                              :{LTBLUE}Effect of distance on demands: {ORANGE}{STRING1}%
+STR_CONFIG_SETTING_DEMAND_SIZE                                  :{LTBLUE}Effect of remote station's popularity on symmetric demands: {ORANGE}{STRING1}%
+STR_CONFIG_SETTING_SHORT_PATH_SATURATION                        :{LTBLUE}Saturation of short paths before using capacious paths: {ORANGE}{STRING1}%
 
 STR_CONFIG_SETTING_GUI                                          :{ORANGE}Interface
 STR_CONFIG_SETTING_CONSTRUCTION                                 :{ORANGE}Construction
 STR_CONFIG_SETTING_VEHICLES                                     :{ORANGE}Vehicles
 STR_CONFIG_SETTING_STATIONS                                     :{ORANGE}Stations
 STR_CONFIG_SETTING_ECONOMY                                      :{ORANGE}Economy
+STR_CONFIG_SETTING_LINKGRAPH                                    :{ORANGE}Link graph
 STR_CONFIG_SETTING_AI                                           :{ORANGE}Competitors
 STR_CONFIG_SETTING_DISPLAY_OPTIONS                              :{ORANGE}Display options
 STR_CONFIG_SETTING_INTERACTION                                  :{ORANGE}Interaction
@@ -2497,8 +2549,31 @@ STR_STATION_VIEW_ACCEPTS_CARGO                                  :{BLACK}Accepts:
 
 STR_STATION_VIEW_RATINGS_BUTTON                                 :{BLACK}Ratings
 STR_STATION_VIEW_RATINGS_TOOLTIP                                :{BLACK}Show station ratings
-STR_STATION_VIEW_CARGO_RATINGS_TITLE                            :{BLACK}Local rating of transport service:
+STR_STATION_VIEW_CARGO_RATINGS_TITLE                            :{BLACK}Monthly supply and local rating:
 STR_STATION_VIEW_CARGO_RATING                                   :{WHITE}{STRING}: {YELLOW}{STRING} ({COMMA}%)
+STR_STATION_VIEW_CARGO_SUPPLY_RATING                            :{WHITE}{STRING}: {YELLOW}{COMMA} / {STRING} ({COMMA}%)
+
+STR_STATION_VIEW_WAITING                                        :Waiting
+STR_STATION_VIEW_PLANNED                                        :Planned
+STR_STATION_VIEW_SENT                                           :Sent
+STR_STATION_VIEW_TOGGLE_CARGO_VIEW                              :{BLACK}Toggle between waiting, planned and sent cargo
+STR_STATION_VIEW_FROM                                           :{YELLOW}{SHORTCARGO} from {STATION}
+STR_STATION_VIEW_VIA                                            :{YELLOW}{SHORTCARGO} via {STATION}
+STR_STATION_VIEW_TO                                             :{YELLOW}{SHORTCARGO} to {STATION}
+STR_STATION_VIEW_FROM_ANY                                       :{RED}{SHORTCARGO} from unknown station
+STR_STATION_VIEW_TO_ANY                                         :{RED}{SHORTCARGO} to any station
+STR_STATION_VIEW_VIA_ANY                                        :{RED}{SHORTCARGO} via any station
+STR_STATION_VIEW_FROM_HERE                                      :{GREEN}{SHORTCARGO} from this station
+STR_STATION_VIEW_VIA_HERE                                       :{GREEN}{SHORTCARGO} stopping at this station
+STR_STATION_VIEW_TO_HERE                                        :{GREEN}{SHORTCARGO} to this station
+STR_STATION_VIEW_NONSTOP                                        :{YELLOW}{SHORTCARGO} non-stop
+
+STR_STATION_VIEW_GROUP_S_V_D                                    :Source-Via-Destination
+STR_STATION_VIEW_GROUP_S_D_V                                    :Source-Destination-Via
+STR_STATION_VIEW_GROUP_V_S_D                                    :Via-Source-Destination
+STR_STATION_VIEW_GROUP_V_D_S                                    :Via-Destination-Source
+STR_STATION_VIEW_GROUP_D_S_V                                    :Destination-Source-Via
+STR_STATION_VIEW_GROUP_D_V_S                                    :Destination-Via-Source
 
 ############ range for rating starts
 STR_CARGO_RATING_APPALLING                                      :Appalling
@@ -3292,6 +3367,7 @@ STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE                       :File not readab
 STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE                      :File not writeable
 STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED             :Data integrity check failed
 STR_WARNING_LOADGAME_REMOVED_TRAMS                              :{WHITE}Game was saved in version without tram support. All trams have been removed.
+STR_WARNING_NONSTOP_CARGODIST                                   :{WHITE}You have set at least one cargo class to be automatically distributed in{}"Avanced Settings -> Link Graph"{}and disabled{}"New orders are 'non-stop' by default"{}in{}"Advanced Settings -> Vehicles"{}This is a BAD idea.
 
 # Map generation messages
 STR_ERROR_COULD_NOT_CREATE_TOWN                                 :{WHITE}Map generation aborted...{}... no suitable town locations
@@ -4112,3 +4188,8 @@ STR_BUS                                                         :{BLACK}{BUS}
 STR_LORRY                                                       :{BLACK}{LORRY}
 STR_PLANE                                                       :{BLACK}{PLANE}
 STR_SHIP                                                        :{BLACK}{SHIP}
+
+STR_NUM                                                         :{BLACK}{NUM}
+STR_NUM_RELATION_2                                              :{BLACK}{NUM}/{NUM}
+STR_NUM_RELATION_3                                              :{BLACK}{NUM}/{NUM}/{NUM}
+STR_NUM_RELATION_4                                              :{BLACK}{NUM}/{NUM}/{NUM}/{NUM}
diff --git a/src/linkgraph/demand_settings.h b/src/linkgraph/demand_settings.h
new file mode 100644
index 0000000..8131a6b
--- /dev/null
+++ b/src/linkgraph/demand_settings.h
@@ -0,0 +1,20 @@
+/** @file demand_settings.h Declaration of distribution types for demand calculation. */
+
+#ifndef DEMAND_SETTINGS_H_
+#define DEMAND_SETTINGS_H_
+
+enum DistributionType {
+	DT_BEGIN = 0,
+	DT_SYMMETRIC = 0,
+	DT_ANTISYMMETRIC,
+	DT_UNHANDLED,
+	DT_NUM = 3,
+	DT_END = 3
+};
+
+/** It needs to be 8bits, because we save and load it as such
+ * Define basic enum properties */
+template <> struct EnumPropsT<DistributionType> : MakeEnumPropsT<DistributionType, byte, DT_BEGIN, DT_END, DT_NUM> {};
+typedef TinyEnumT<DistributionType> DistributionTypeByte; // typedefing-enumification of DistributionType
+
+#endif /* DEMAND_SETTINGS_H_ */
diff --git a/src/linkgraph/demands.cpp b/src/linkgraph/demands.cpp
new file mode 100644
index 0000000..85f6fd4
--- /dev/null
+++ b/src/linkgraph/demands.cpp
@@ -0,0 +1,166 @@
+/** @file demands.h Definition of demand calculating link graph handler. */
+
+#include "demands.h"
+#include "../station_base.h"
+#include "../settings_type.h"
+#include "../newgrf_cargo.h"
+#include "../cargotype.h"
+#include "../core/math_func.hpp"
+#include <list>
+#include <iostream>
+
+typedef std::list<NodeID> NodeList;
+
+void DemandCalculator::PrintDemandMatrix(LinkGraphComponent * graph) {
+	for (NodeID from = 0; from < graph->GetSize(); ++from) {
+		std::cout << graph->GetNode(from).station << "\t";
+		for(NodeID to = 0; to < graph->GetSize(); ++to) {
+			if (from == to) {
+				std::cout << graph->GetNode(from).supply << "\t";
+			} else {
+				std::cout << graph->GetEdge(from, to).distance << ":" << graph->GetEdge(from, to).demand << "\t";
+			}
+		}
+		std::cout << "\n";
+	}
+}
+
+void DemandCalculator::CalcDemand(LinkGraphComponent * graph) {
+	NodeList supplies;
+	NodeList demands;
+	uint supply_sum = 0;
+	uint num_demands = 0;
+	uint num_supplies = 0;
+	for(NodeID node = 0; node < graph->GetSize(); node++) {
+		Node & n = graph->GetNode(node);
+		if (n.supply > 0) {
+			supplies.push_back(node);
+			supply_sum += n.supply;
+			num_supplies++;
+		}
+		if (n.demand > 0) {
+			demands.push_back(node);
+			num_demands++;
+		}
+	}
+
+	if (supply_sum == 0 || num_demands == 0) {
+		return;
+	}
+
+	uint demand_per_node = max(supply_sum / num_demands, (uint)1);
+	uint chance = 0;
+
+	while(!supplies.empty() && !demands.empty()) {
+		NodeID node1 = supplies.front();
+		supplies.pop_front();
+
+		Node & from = graph->GetNode(node1);
+
+		for(uint i = 0; i < num_demands; ++i) {
+			assert(!demands.empty());
+			NodeID node2 = demands.front();
+			demands.pop_front();
+			if (node1 == node2) {
+				if (demands.empty() && supplies.empty()) {
+					/* only one node with supply and demand left */
+					return;
+				} else {
+					demands.push_back(node2);
+					continue;
+				}
+			}
+			Node & to = graph->GetNode(node2);
+			Edge & forward = graph->GetEdge(node1, node2);
+			Edge & backward = graph->GetEdge(node2, node1);
+
+			int32 supply = from.supply;
+			if (this->mod_size > 0) {
+				supply = max(1, (int32)(supply * to.supply * this->mod_size / 100 / demand_per_node));
+			}
+			assert(supply > 0);
+
+			/* scale the distance by mod_dist around max_distance */
+			int32 distance = this->max_distance - (this->max_distance - (int32)forward.distance) * this->mod_dist / 100;
+
+			/* scale the accuracy by distance around accuracy / 2 */
+			int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 + this->accuracy * distance / this->max_distance + 1;
+			assert(divisor > 0);
+
+			uint demand_forw = 0;
+			if (divisor < supply) {
+				demand_forw = supply / divisor;
+			} else if (++chance > this->accuracy * num_demands * num_supplies) {
+				/* after some trying distribute demand also to other nodes */
+				demand_forw = 1;
+			}
+
+			demand_forw = min(demand_forw, from.undelivered_supply);
+
+			if (this->mod_size > 0 && from.demand > 0) {
+				uint demand_back = demand_forw * this->mod_size / 100;
+				if (demand_back > to.undelivered_supply) {
+					demand_back = to.undelivered_supply;
+					demand_forw = demand_back * 100 / this->mod_size;
+				}
+				backward.demand += demand_back;
+				backward.unsatisfied_demand += demand_back;
+				to.undelivered_supply -= demand_back;
+			}
+
+			forward.demand += demand_forw;
+			forward.unsatisfied_demand += demand_forw;
+			from.undelivered_supply -= demand_forw;
+
+			if (this->mod_size == 0 || to.undelivered_supply > 0) {
+				demands.push_back(node2);
+			} else {
+				num_demands--;
+			}
+
+			if (from.undelivered_supply == 0) {
+				break;
+			}
+		}
+		if (from.undelivered_supply != 0) {
+			supplies.push_back(node1);
+		}
+	}
+}
+
+void DemandCalculator::Run(LinkGraphComponent * graph) {
+	CargoID cargo = graph->GetCargo();
+	const LinkGraphSettings & settings = graph->GetSettings();
+	DistributionType type = settings.demand_default;
+	if (IsCargoInClass(cargo, CC_PASSENGERS)) {
+		type = settings.demand_pax;
+	} else if (IsCargoInClass(cargo, CC_MAIL)) {
+		type = settings.demand_mail;
+	} else if (IsCargoInClass(cargo, CC_EXPRESS)) {
+		type = settings.demand_express;
+	} else if (IsCargoInClass(cargo, CC_ARMOURED)) {
+		type = settings.demand_armoured;
+	}
+
+	this->accuracy = settings.accuracy;
+	this->mod_size = settings.demand_size;
+	this->mod_dist = settings.demand_distance;
+	if (this->mod_dist > 100) {
+		/* increase effect of mod_dist > 100 */
+		int over100 = this->mod_dist - 100;
+		this->mod_dist = 100 + over100 * over100;
+	}
+
+	switch (type) {
+	case DT_SYMMETRIC:
+		CalcDemand(graph);
+		break;
+	case DT_ANTISYMMETRIC:
+		this->mod_size = 0;
+		CalcDemand(graph);
+		break;
+	default:
+		/* ignore */
+		break;
+	}
+}
diff --git a/src/linkgraph/demands.h b/src/linkgraph/demands.h
new file mode 100644
index 0000000..61b9436
--- /dev/null
+++ b/src/linkgraph/demands.h
@@ -0,0 +1,26 @@
+/** @file demands.h Declaration of demand calculating link graph handler. */
+
+#ifndef DEMANDS_H_
+#define DEMANDS_H_
+
+#include "linkgraph.h"
+#include "demand_settings.h"
+#include "../stdafx.h"
+#include "../cargo_type.h"
+#include "../map_func.h"
+
+class DemandCalculator : public ComponentHandler {
+public:
+	DemandCalculator() : max_distance(MapSizeX() + MapSizeY() + 1) {}
+	virtual void Run(LinkGraphComponent * graph);
+	static void PrintDemandMatrix(LinkGraphComponent * graph);
+	virtual ~DemandCalculator() {}
+private:
+	int32 max_distance;
+	int32 mod_size;
+	int32 mod_dist;
+	int32 accuracy;
+	void CalcDemand(LinkGraphComponent * graph);
+};
+
+#endif /* DEMANDS_H_ */
diff --git a/src/linkgraph/flowmapper.cpp b/src/linkgraph/flowmapper.cpp
new file mode 100644
index 0000000..b2ec3e6
--- /dev/null
+++ b/src/linkgraph/flowmapper.cpp
@@ -0,0 +1,38 @@
+/** @file flowmapper.cpp Definition of flowmapper */
+
+#include "flowmapper.h"
+
+void FlowMapper::Run(LinkGraphComponent * c) {
+	component = c;
+	for (NodeID node_id = 0; node_id < component->GetSize(); ++node_id) {
+		Node & prev_node = component->GetNode(node_id);
+		StationID prev = prev_node.station;
+		PathSet & paths = prev_node.paths;
+		for(PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+			Path * path = *i;
+			uint flow = path->GetFlow();
+			if (flow == 0) continue;
+			Node & node = component->GetNode(path->GetNode());
+			StationID via = node.station;
+			assert(prev != via);
+			StationID origin = component->GetNode(path->GetOrigin()).station;
+			assert(via != origin);
+			/* mark all of the flow for local consumation at first */
+			node.flows[origin][via] += flow;
+			/* pass some of the flow marked for local consumation at prev on to this node */
+			prev_node.flows[origin][via] += flow;
+			/* find simple circular flows ... */
+			assert(node.flows[origin][prev] == 0);
+			if (prev != origin) {
+				prev_node.flows[origin][prev] -= flow;
+			}
+		}
+	}
+	for (NodeID node_id = 0; node_id < component->GetSize(); ++node_id) {
+		PathSet & paths = component->GetNode(node_id).paths;
+		for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+			delete (*i);
+		}
+		paths.clear();
+	}
+}
diff --git a/src/linkgraph/flowmapper.h b/src/linkgraph/flowmapper.h
new file mode 100644
index 0000000..56c3fb1
--- /dev/null
+++ b/src/linkgraph/flowmapper.h
@@ -0,0 +1,16 @@
+/** @file flowmapper.h Declaration of flow mapper; merges paths into flows at nodes */
+
+#ifndef FLOWMAPPER_H_
+#define FLOWMAPPER_H_
+
+#include "linkgraph.h"
+
+class FlowMapper : public ComponentHandler {
+public:
+	FlowMapper() : component(NULL) {}
+	void Run(LinkGraphComponent * c);
+private:
+	LinkGraphComponent * component;
+};
+
+#endif /* FLOWMAPPER_H_ */
diff --git a/src/linkgraph/linkgraph.cpp b/src/linkgraph/linkgraph.cpp
new file mode 100644
index 0000000..5db4507
--- /dev/null
+++ b/src/linkgraph/linkgraph.cpp
@@ -0,0 +1,439 @@
+/** @file linkgraph.cpp Definition of link graph classes used for cargo distribution. */
+
+#include "linkgraph.h"
+#include "demands.h"
+#include "mcf.h"
+#include "flowmapper.h"
+#include "../date_func.h"
+#include "../variables.h"
+#include "../map_func.h"
+#include "../core/bitmath_func.hpp"
+#include "../debug.h"
+#include "../window_func.h"
+#include "../window_gui.h"
+#include "../moving_average.h"
+#include <queue>
+
+LinkGraph _link_graphs[NUM_CARGO];
+
+typedef std::map<StationID, NodeID> ReverseNodeIndex;
+
+void LinkGraph::CreateComponent(Station * first) {
+	ReverseNodeIndex index;
+	NodeID node = 0;
+	std::queue<Station *> search_queue;
+	LinkGraphComponent * component = NULL;
+
+	search_queue.push(first);
+
+	first->goods[this->cargo].last_component = this->current_component_id;
+	component = new LinkGraphComponent(this->cargo, this->current_component_id);
+	GoodsEntry & good = first->goods[this->cargo];
+	node = component->AddNode(this->current_station_id, good.supply.Value(), good.acceptance);
+	this->component_acceptance[this->current_component_id] = good.acceptance;
+	index[this->current_station_id++] = node;
+	// find all stations belonging to the current component
+	while(!search_queue.empty()) {
+		Station * source = search_queue.front();
+		StationID source_id = source->index;
+		search_queue.pop();
+		GoodsEntry & good = source->goods[cargo];
+		LinkStatMap & links = good.link_stats;
+		for(LinkStatMap::iterator i = links.begin(); i != links.end(); ++i) {
+			StationID target_id = i->first;
+			Station *target = Station::GetIfValid(target_id);
+			if (target == NULL) {
+				continue;
+			}
+			assert(target_id != source_id);
+			LinkStat & link_stat = i->second;
+			ReverseNodeIndex::iterator index_it = index.find(target_id);
+			if (index_it == index.end()) {
+				GoodsEntry & good = target->goods[cargo];
+				good.last_component = this->current_component_id;
+				search_queue.push(target);
+				node = component->AddNode(target_id, good.supply.Value(), good.acceptance);
+				this->component_acceptance[this->current_component_id] += good.acceptance;
+				index[target_id] = node;
+			} else {
+				node = index_it->second;
+			}
+			
+			assert(!link_stat.IsNull());
+			component->AddEdge(index[source_id], node, link_stat.Capacity());
+		}
+	}
+	// here the list of nodes and edges for this component is complete.
+	component->CalculateDistances();
+	LinkGraphJob * job = new LinkGraphJob(component);
+	assert(job != NULL);
+	job->SpawnThread(this->cargo);
+	this->jobs.push_back(job);
+}
+
+void LinkGraph::NextComponent()
+{
+	while (!Station::IsValidID(this->current_station_id) && this->current_station_id > 0) {
+		--this->current_station_id;
+	}
+	StationID last_station_id = this->current_station_id;
+
+	do {
+		// find first station of next component
+		if (Station::IsValidID(this->current_station_id)) {
+			Station * station = Station::Get(this->current_station_id);
+			GoodsEntry & ge = station->goods[this->cargo];
+			if ((ge.last_component + this->current_component_id) % 2 != 0) {
+				// has not been seen in this run through the graph
+
+				LinkStatMap & links = station->goods[cargo].link_stats;
+				if (!links.empty()) {
+					this->current_component_id += 2;
+					if (this->current_component_id >= LinkGraph::MAX_COMPONENTS) {
+						if (this->current_component_id % 2 == 0) {
+							this->current_component_id = 0;
+						} else {
+							this->current_component_id = 1;
+						}
+					}
+					CreateComponent(station);
+					return;
+				}
+			}
+		}
+
+		if (++this->current_station_id == Station::GetPoolSize()) {
+			this->current_station_id = 0;
+			if (this->current_component_id % 2 == 0) {
+				this->current_component_id = 1;
+			} else {
+				this->current_component_id = 0;
+			}
+		}
+	} while (this->current_station_id != last_station_id);
+}
+
+void OnTick_LinkGraph()
+{
+	bool spawn = (_tick_counter + LinkGraph::COMPONENTS_SPAWN_TICK) % DAY_TICKS == 0;
+	bool join =  (_tick_counter + LinkGraph::COMPONENTS_JOIN_TICK)  % DAY_TICKS == 0;
+	if (spawn || join) {
+		for(CargoID cargo = CT_BEGIN; cargo != CT_END; ++cargo) {
+			if ((_date + cargo) % _settings_game.linkgraph.recalc_interval == 0) {
+				LinkGraph & graph = _link_graphs[cargo];
+				if (spawn) {
+					graph.NextComponent();
+				} else {
+					graph.Join();
+				}
+			}
+		}
+	}
+}
+
+LinkGraph::LinkGraph()  : current_component_id(1), current_station_id(0), cargo(CT_INVALID)
+{
+	for (CargoID i = CT_BEGIN; i != CT_END; ++i) {
+		if (this == &(_link_graphs[i])) {
+			cargo = i;
+		}
+	}
+}
+
+NodeID LinkGraphComponent::AddNode(StationID st, uint supply, uint demand) {
+	nodes.push_back(Node(st, supply, demand));
+	for(NodeID i = 0; i < num_nodes; ++i) {
+		edges[i].push_back(Edge());
+	}
+	edges.push_back(std::vector<Edge>(++num_nodes));
+	return num_nodes - 1;
+}
+
+void LinkGraphComponent::AddEdge(NodeID from, NodeID to, uint capacity) {
+	assert(from != to);
+	Edge & edge = edges[from][to];
+	Edge & first = edges[from][from];
+	edge.capacity = capacity;
+	edge.next_edge = first.next_edge;
+	first.next_edge = to;
+	edges[from][to].capacity = capacity;
+}
+
+void LinkGraphComponent::CalculateDistances() {
+	for(NodeID i = 0; i < num_nodes; ++i) {
+		for(NodeID j = 0; j < i; ++j) {
+			Station * st1 = Station::Get(nodes[i].station);
+			Station * st2 = Station::Get(nodes[j].station);
+			uint distance = DistanceManhattan(st1->xy, st2->xy);
+			edges[i][j].distance = distance;
+			edges[j][i].distance = distance;
+		}
+	}
+}
+
+void LinkGraphComponent::SetSize(uint size) {
+	num_nodes = size;
+	nodes.resize(num_nodes);
+	edges.resize(num_nodes, std::vector<Edge>(num_nodes));
+}
+
+LinkGraphComponent::LinkGraphComponent(CargoID car, LinkGraphComponentID col) :
+	settings(_settings_game.linkgraph),
+	cargo(car),
+	num_nodes(0),
+	index(col)
+{
+}
+
+void LinkGraph::Join() {
+	while (!this->jobs.empty()) {
+		LinkGraphJob * job = this->jobs.front();
+		assert(job != NULL);
+
+		/* also join if join date is far in the future. This prevents excessive memory use when resetting time */
+		if (job->GetJoinDate() > _date && job->GetJoinDate() <= _date + _settings_game.linkgraph.recalc_interval) {
+			return;
+		}
+		job->Join();
+
+		LinkGraphComponent * comp = job->GetComponent();
+
+		for(NodeID node_id = 0; node_id < comp->GetSize(); ++node_id) {
+			Node & node = comp->GetNode(node_id);
+			if (Station::IsValidID(node.station)) {
+				FlowStatMap & station_flows = Station::Get(node.station)->goods[cargo].flows;
+				node.ExportFlows(station_flows, cargo);
+				Window *station_gui = FindWindowById(WC_STATION_VIEW, node.station);
+				if (station_gui != NULL) {
+					station_gui->OnInvalidateData(comp->GetCargo());
+				}
+			}
+		}
+		delete job;
+		this->jobs.pop_front();
+	}
+}
+
+/**
+ * exports all entries in the FlowViaMap pointed to by source_flows it and erases it afterwards
+ */
+void Node::ExportNewFlows(FlowMap::iterator & source_flows_it, FlowStatSet & via_set, CargoID cargo) {
+	StationID source = source_flows_it->first;
+	FlowViaMap & source_flows = source_flows_it->second;
+	if (!Station::IsValidID(source)) {
+		source_flows.clear();
+	} else {
+		Station *curr_station = Station::Get(this->station);
+		for (FlowViaMap::iterator update = source_flows.begin(); update != source_flows.end();) {
+			StationID next = update->first;
+			int planned = update->second;
+			assert(planned >= 0);
+
+			Station *via = Station::GetIfValid(next);
+			if (planned > 0 && via != NULL) {
+				uint distance = GetMovingAverageLength(curr_station, via);
+				if (next != this->station) {
+					LinkStatMap & ls = curr_station->goods[cargo].link_stats;
+					if (ls.find(next) != ls.end()) {
+						via_set.insert(FlowStat(distance, next, planned, 0));
+					}
+				} else {
+					via_set.insert(FlowStat(distance, next, planned, 0));
+				}
+			}
+			source_flows.erase(update++);
+		}
+	}
+	assert(source_flows.empty());
+
+	flows.erase(source_flows_it++);
+}
+
+void Node::ExportFlows(FlowStatMap & station_flows, CargoID cargo) {
+	FlowStatSet new_flows;
+	/* loop over all existing flows in the station and update them */
+	for(FlowStatMap::iterator flowmap_it = station_flows.begin(); flowmap_it != station_flows.end();) {
+		FlowMap::iterator source_flows_it = flows.find(flowmap_it->first);
+		if (source_flows_it == flows.end()) {
+			/* there are no flows for this source node anymore */
+			station_flows.erase(flowmap_it++);
+		} else {
+			FlowViaMap & source_flows = source_flows_it->second;
+			FlowStatSet & via_set = flowmap_it->second;
+			/* loop over the station's flow stats for this source node and update them */
+			for (FlowStatSet::iterator flowset_it = via_set.begin(); flowset_it != via_set.end();) {
+				FlowViaMap::iterator update = source_flows.find(flowset_it->Via());
+				if (update != source_flows.end()) {
+					assert(update->second >= 0);
+					if (update->second > 0) {
+						new_flows.insert(FlowStat(*flowset_it, update->second));
+					}
+					source_flows.erase(update);
+				}
+				via_set.erase(flowset_it++);
+			}
+			/* swap takes constant time, so we swap instead of adding all entries */
+			via_set.swap(new_flows);
+			assert(new_flows.empty());
+			/* insert remaining flows for this source node */
+			ExportNewFlows(source_flows_it, via_set, cargo);
+			/* source_flows is dangling here */
+			++flowmap_it;
+		}
+	}
+	/* loop over remaining flows (for other sources) in the node's map and insert them into the station */
+	for (FlowMap::iterator source_flows_it = flows.begin(); source_flows_it != flows.end();) {
+		FlowStatSet & via_set = station_flows[source_flows_it->first];
+		ExportNewFlows(source_flows_it, via_set, cargo);
+	}
+	assert(flows.empty());
+}
+
+void LinkGraph::AddComponent(LinkGraphComponent * component, uint join) {
+	LinkGraphComponentID index = component->GetIndex();
+	for(NodeID i = 0; i < component->GetSize(); ++i) {
+		Station *station = Station::GetIfValid(component->GetNode(i).station);
+		if (station != NULL) {
+			station->goods[cargo].last_component = index;
+		}
+	}
+	LinkGraphJob * job = new LinkGraphJob(component, join);
+	assert(job != NULL);
+	job->SpawnThread(this->cargo);
+	this->jobs.push_back(job);
+}
+
+void LinkGraphJob::Run() {
+	for (HandlerList::iterator i = this->handlers.begin(); i != this->handlers.end(); ++i) {
+		ComponentHandler * handler = *i;
+		handler->Run(this->component);
+	}
+}
+
+LinkGraphJob::~LinkGraphJob() {
+	for (HandlerList::iterator i = this->handlers.begin(); i != this->handlers.end(); ++i) {
+		ComponentHandler * handler = *i;
+		delete handler;
+	}
+	this->handlers.clear();
+	DEBUG(misc, 2, "removing job for cargo %d with index %d and join date %d at %d", this->component->GetCargo(),
+			this->component->GetIndex(), this->join_date, _date);
+	delete this->component;
+	delete this->thread;
+}
+
+void RunLinkGraphJob(void * j) {
+	LinkGraphJob * job = (LinkGraphJob *)j;
+	job->Run();
+}
+
+void Path::Fork(Path * base, int cap, uint dist) {
+	capacity = min(base->capacity, cap);
+	distance = base->distance + dist;
+	assert(distance > 0);
+	if (parent != base) {
+		if (parent != NULL) {
+			parent->num_children--;
+		}
+		parent = base;
+		parent->num_children++;
+	}
+	origin = base->origin;
+}
+
+uint Path::AddFlow(uint f, LinkGraphComponent * graph, bool only_positive) {
+	if (parent != NULL) {
+		Edge & edge = graph->GetEdge(parent->node, node);
+		if (only_positive) {
+			uint usable_cap = edge.capacity * graph->GetSettings().short_path_saturation / 100;
+			if(usable_cap > edge.flow) {
+				f = min(f, usable_cap - edge.flow);
+			} else {
+				return 0;
+			}
+		}
+		f = parent->AddFlow(f, graph, only_positive);
+		if (f > 0) {
+			graph->GetNode(parent->node).paths.insert(this);
+		}
+		edge.flow += f;
+	}
+	flow += f;
+	return f;
+}
+
+void Path::UnFork() {
+	if (parent != NULL) {
+		parent->num_children--;
+		parent = NULL;
+	}
+}
+
+Path::Path(NodeID n, bool source)  :
+	distance(source ? 0 : UINT_MAX),
+	capacity(source ? INT_MAX : INT_MIN),
+	flow(0), node(n), origin(source ? n : Node::INVALID),
+	num_children(0), parent(NULL)
+{}
+
+void LinkGraphJob::SpawnThread(CargoID cargo) {
+	AddHandler(new DemandCalculator);
+	AddHandler(new MCF1stPass);
+	AddHandler(new FlowMapper);
+	AddHandler(new MCF2ndPass);
+	AddHandler(new FlowMapper);
+	if (!ThreadObject::New(&(RunLinkGraphJob), this, &thread)) {
+		thread = NULL;
+		// Of course this will hang a bit.
+		// On the other hand, if you want to play games which make this hang noticably
+		// on a platform without threads then you'll probably get other problems first.
+		// OK:
+		// If someone comes and tells me that this hangs for him/her, I'll implement a
+		// smaller grained "Step" method for all handlers and add some more ticks where
+		// "Step" is called. No problem in principle.
+		RunLinkGraphJob(this);
+	}
+}
+
+uint LinkGraph::GetComponentAcceptance(LinkGraphComponentID id)
+{
+	return this->component_acceptance[id];
+}
+
+LinkGraphJob::LinkGraphJob(LinkGraphComponent * c) :
+	thread(NULL),
+	join_date(_date + c->GetSettings().recalc_interval),
+	component(c)
+{
+	DEBUG(misc, 2, "new job for cargo %d with index %d and join date %d at %d", c->GetCargo(), c->GetIndex(), join_date, _date);
+}
+
+LinkGraphJob::LinkGraphJob(LinkGraphComponent * c, Date join) :
+	thread(NULL),
+	join_date(join),
+	component(c)
+{
+	DEBUG(misc, 2, "new job for cargo %d with index %d and join date %d at %d", c->GetCargo(), c->GetIndex(), join_date, _date);
+}
+
+Node::~Node() {
+	for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+		delete (*i);
+	}
+}
+
+void LinkGraph::Clear() {
+	for (JobList::iterator i = jobs.begin(); i != jobs.end(); ++i) {
+		LinkGraphJob * job = *i;
+		assert(job != NULL);
+		job->Join();
+		delete job;
+	}
+	jobs.clear();
+	current_component_id = 1;
+	current_station_id = 0;
+}
+
+void InitializeLinkGraphs() {
+	for (CargoID c = CT_BEGIN; c != CT_END; ++c) _link_graphs[c].Clear();
+}
diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h
new file mode 100644
index 0000000..c370fc8
--- /dev/null
+++ b/src/linkgraph/linkgraph.h
@@ -0,0 +1,186 @@
+/** @file linkgraph.h Declaration of link graph classes used for cargo distribution. */
+
+#ifndef LINKGRAPH_H_
+#define LINKGRAPH_H_
+
+#include "../stdafx.h"
+#include "../station_base.h"
+#include "../cargo_type.h"
+#include "../thread/thread.h"
+#include "../settings_type.h"
+#include "linkgraph_type.h"
+#include <list>
+#include <vector>
+#include <set>
+
+struct SaveLoad;
+class Path;
+
+typedef std::set<Path *> PathSet;
+typedef std::map<NodeID, Path *> PathViaMap;
+typedef std::map<StationID, int> FlowViaMap;
+typedef std::map<StationID, FlowViaMap> FlowMap;
+
+class Node {
+public:
+	static const NodeID INVALID = UINT_MAX;
+	Node() : supply(0), undelivered_supply(0), demand(0), station(INVALID_STATION) {}
+	Node(StationID st, uint sup, uint dem) : supply(sup), undelivered_supply(sup), demand(dem), station(st) {}
+	~Node();
+	void ExportFlows(FlowStatMap & station_flows, CargoID cargo);
+	uint supply;
+	uint undelivered_supply;
+	uint demand;
+	StationID station;
+	PathSet paths;
+	FlowMap flows;
+private:
+	void ExportNewFlows(FlowMap::iterator & source_flows_it, FlowStatSet & via_set, CargoID cargo);
+};
+
+typedef std::set<NodeID> ViaSet;
+
+class Edge {
+public:
+	Edge() : distance(0), capacity(0), demand(0), unsatisfied_demand(0), flow(0), next_edge(Node::INVALID) {}
+	uint distance;
+	uint capacity;
+	uint demand;
+	uint unsatisfied_demand;
+	uint flow;
+	NodeID next_edge;
+};
+
+class LinkGraphComponent {
+	typedef std::vector<Node> NodeVector;
+	typedef std::vector<std::vector<Edge> > EdgeMatrix;
+
+public:
+	LinkGraphComponent(CargoID cargo, LinkGraphComponentID c = 0);
+	Edge & GetEdge(NodeID from, NodeID to) {return edges[from][to];}
+	Node & GetNode(NodeID num) {return nodes[num];}
+	uint GetSize() const {return num_nodes;}
+	void SetSize(uint size);
+	NodeID AddNode(StationID st, uint supply, uint demand);
+	void AddEdge(NodeID from, NodeID to, uint capacity);
+	void CalculateDistances();
+	LinkGraphComponentID GetIndex() const {return index;}
+	CargoID GetCargo() const {return cargo;}
+	const LinkGraphSettings & GetSettings() const {return settings;}
+	NodeID GetFirstEdge(NodeID from) {return edges[from][from].next_edge;}
+private:
+	friend const SaveLoad * GetLinkGraphComponentDesc();
+	LinkGraphSettings settings;
+	CargoID cargo;
+	uint num_nodes;
+	LinkGraphComponentID index;
+	NodeVector nodes;
+	EdgeMatrix edges;
+};
+
+class ComponentHandler {
+public:
+	virtual void Run(LinkGraphComponent * component) = 0;
+	virtual ~ComponentHandler() {}
+};
+
+class LinkGraphJob {
+	typedef std::list<ComponentHandler *> HandlerList;
+public:
+	LinkGraphJob(LinkGraphComponent * c);
+	LinkGraphJob(LinkGraphComponent * c, Date join);
+
+	void AddHandler(ComponentHandler * handler) {this->handlers.push_back(handler);}
+	void Run();
+	void SpawnThread(CargoID cargo);
+	void Join() {if (this->thread != NULL) this->thread->Join();}
+	Date GetJoinDate() {return this->join_date;}
+	LinkGraphComponent * GetComponent() {return this->component;}
+	~LinkGraphJob();
+private:
+	/**
+	 * there cannot be two identical LinkGraphJobs,
+	 */
+	LinkGraphJob(const LinkGraphJob & other) {NOT_REACHED();}
+	ThreadObject * thread;
+	Date join_date;
+	LinkGraphComponent * component;
+	HandlerList handlers;
+};
+
+typedef std::list<LinkGraphJob *> JobList;
+
+class LinkGraph {
+public:
+	static const uint MAX_COMPONENTS = 128;
+
+	LinkGraph();
+	void Clear();
+	CargoID GetCargo() const {return cargo;}
+	/**
+	 * Starts calcluation of the next component of the link graph.
+	 * Uses a breadth first search on the graph spanned by the
+	 * stations' link stats.
+	 *
+	 * TODO: This method could be changed to only search a defined number
+	 * of stations in each run, thus decreasing the delay. The state of
+	 * the search queue would have to be saved and loaded then.
+	 */
+	void NextComponent();
+
+	/**
+	 * Merges the results of the link graph calculation into the main
+	 * game state.
+	 *
+	 * TODO: This method could be changed to only merge a fixed number of
+	 * nodes in each run. In order to do so, the ID of last node merged
+	 * would have to be saved and loaded. Merging only a fixed  number
+	 * of nodes is faster than merging all nodes of the component.
+	 */
+	void Join();
+	size_t GetNumJobs() const {return jobs.size();}
+	JobList & GetJobs() {return jobs;}
+	void AddComponent(LinkGraphComponent * component, uint join);
+	uint GetComponentAcceptance(LinkGraphComponentID id);
+
+	const static uint COMPONENTS_JOIN_TICK  = 21;
+	const static uint COMPONENTS_SPAWN_TICK = 58;
+
+private:
+	friend const SaveLoad * GetLinkGraphDesc(uint);
+	void CreateComponent(Station * first);
+	LinkGraphComponentID current_component_id;
+	uint component_acceptance[MAX_COMPONENTS];
+	StationID current_station_id;
+	CargoID cargo;
+	JobList jobs;
+};
+
+class Path {
+public:
+	Path(NodeID n, bool source = false);
+	NodeID GetNode() const {return node;}
+	NodeID GetOrigin() const {return origin;}
+	Path * GetParent() {return parent;}
+	int GetCapacity() const {return capacity;}
+	uint GetDistance() const {return distance;}
+	void Fork(Path * base, int cap, uint dist);
+	void ReduceFlow(uint f) {flow -= f;}
+	void AddFlow(uint f) {flow += f;}
+	uint AddFlow(uint f, LinkGraphComponent * graph, bool only_positive);
+	uint GetFlow() const {return flow;}
+	uint GetNumChildren() const {return num_children;}
+	void UnFork();
+protected:
+	uint distance;
+	int capacity;      ///< this capacity is edge.capacity - edge.flow for the current run of dijkstra
+	uint flow;         ///< this is the flow the current run of the mcf solver assigns
+	NodeID node;
+	NodeID origin;
+	uint num_children;
+	Path * parent;
+};
+
+extern LinkGraph _link_graphs[NUM_CARGO];
+
+#endif /* LINKGRAPH_H_ */
diff --git a/src/linkgraph/linkgraph_type.h b/src/linkgraph/linkgraph_type.h
new file mode 100644
index 0000000..b4e1fa2
--- /dev/null
+++ b/src/linkgraph/linkgraph_type.h
@@ -0,0 +1,10 @@
+/** @file linkgraph_type.h Declaration of link graph types used for cargo distribution. */
+
+#ifndef LINKGRAPH_TYPE_H_
+#define LINKGRAPH_TYPE_H_
+
+typedef uint16 LinkGraphComponentID;
+typedef uint NodeID;
+
+
+#endif /* LINKGRAPH_TYPE_H_ */
diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp
new file mode 100644
index 0000000..c5e9c62
--- /dev/null
+++ b/src/linkgraph/mcf.cpp
@@ -0,0 +1,308 @@
+/** @file mcf.cpp Definition of Multi-Commodity-Flow solver */
+
+#include "mcf.h"
+#include "../core/math_func.hpp"
+
+MultiCommodityFlow::MultiCommodityFlow() :
+	graph(NULL)
+{}
+
+void MultiCommodityFlow::Run(LinkGraphComponent * g) {
+	assert(g->GetSettings().accuracy >= 1);
+	graph = g;
+}
+
+bool DistanceAnnotation::IsBetter(const DistanceAnnotation * base, int cap, uint dist) const {
+	if (cap > 0 && base->capacity > 0) {
+		if (this->capacity <= 0) {
+			return true; // if the other path has capacity left and this one hasn't, the other one's better
+		} else {
+			return base->distance + dist < this->distance;
+		}
+	} else {
+		if (this->capacity > 0 || base->distance == UINT_MAX) {
+			return false; // if the other path doesn't have capacity left or is disconnected, but this one has, this one is always better
+		} else {
+			/* if both paths are out of capacity, do the regular distance comparison again */
+			return base->distance + dist < this->distance;
+		}
+	}
+}
+
+bool CapacityAnnotation::IsBetter(const CapacityAnnotation * base, int cap, uint dist) const {
+	int min_cap = min(base->capacity, cap);
+	if (min_cap == this->capacity) {
+		if (base->distance != UINT_MAX) { // if the capacities are the same, choose the shorter path
+			return (base->distance + dist < this->distance);
+		} else {
+			return false;
+		}
+	} else {
+		return min_cap > this->capacity;
+	}
+}
+
+template<class ANNOTATION>
+void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector & paths, bool create_new_paths) {
+	typedef std::set<ANNOTATION *, typename ANNOTATION::comp> AnnoSet;
+	uint size = this->graph->GetSize();
+	StationID source_station = this->graph->GetNode(source_node).station;
+	AnnoSet annos;
+	paths.resize(size, NULL);
+	for (NodeID node = 0; node < size; ++node) {
+		ANNOTATION * anno = new ANNOTATION(node, node == source_node);
+		annos.insert(anno);
+		paths[node] = anno;
+	}
+	while(!annos.empty()) {
+		typename AnnoSet::iterator i = annos.begin();
+		ANNOTATION * source = *i;
+		annos.erase(i);
+		NodeID from = source->GetNode();
+		NodeID to = this->graph->GetFirstEdge(from);
+		while (to != Node::INVALID) {
+			Edge & edge = graph->GetEdge(from, to);
+			assert(edge.distance < UINT_MAX);
+			if (create_new_paths || this->graph->GetNode(from).flows[source_station][graph->GetNode(to).station] > 0) {
+				int capacity = edge.capacity;
+				if (create_new_paths) {
+					capacity *= this->graph->GetSettings().short_path_saturation;
+					capacity /= 100;
+					if (capacity == 0) {
+						capacity = 1;
+					}
+					assert(capacity > 0);
+				}
+				capacity -= edge.flow;
+				uint distance = edge.distance + 1; // punish in-between stops a little
+				ANNOTATION * dest = static_cast<ANNOTATION *>(paths[to]);
+				if (dest->IsBetter(source, capacity, distance)) {
+					annos.erase(dest);
+					dest->Fork(source, capacity, distance);
+					annos.insert(dest);
+				}
+			}
+			to = edge.next_edge;
+		}
+	}
+}
+
+
+
+void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector & paths) {
+	Path * source = paths[source_id];
+	paths[source_id] = NULL;
+	for(PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
+		Path * path = *i;
+		if (path != NULL) {
+			if (path->GetParent() == source) {
+				path->UnFork();
+			}
+			while (path != source && path != NULL && path->GetFlow() == 0) {
+				Path * parent = path->GetParent();
+				path->UnFork();
+				if (path->GetNumChildren() == 0) {
+					paths[path->GetNode()] = NULL;
+					delete path;
+				}
+				path = parent;
+			}
+		}
+	}
+	delete source;
+	paths.clear();
+}
+
+uint MultiCommodityFlow::PushFlow(Edge &edge, Path * path, uint accuracy, bool positive_cap) {
+	uint flow = edge.unsatisfied_demand / accuracy;
+	if (flow == 0) flow = 1;
+	flow = path->AddFlow(flow, graph, positive_cap);
+	edge.unsatisfied_demand -= flow;
+	return flow;
+}
+
+uint MCF1stPass::FindCycleFlow(const PathVector & path, const Path * cycle_begin)
+{
+	uint flow = UINT_MAX;
+	const Path * cycle_end = cycle_begin;
+	do {
+		flow = min(flow, cycle_begin->GetFlow());
+		cycle_begin = path[cycle_begin->GetNode()];
+	} while(cycle_begin != cycle_end);
+	return flow;
+}
+
+void MCF1stPass::EliminateCycle(PathVector & path, Path * cycle_begin, uint flow)
+{
+	Path * cycle_end = cycle_begin;
+	do {
+		NodeID prev = cycle_begin->GetNode();
+		cycle_begin->ReduceFlow(flow);
+		cycle_begin = path[cycle_begin->GetNode()];
+		Edge & edge = this->graph->GetEdge(prev, cycle_begin->GetNode());
+		edge.flow -= flow;
+	} while(cycle_begin != cycle_end);
+}
+
+bool MCF1stPass::EliminateCycles(PathVector & path, NodeID origin_id, NodeID next_id)
+{
+	static Path * invalid_path = new Path(Node::INVALID, true);
+	Path * at_next_pos = path[next_id];
+	if (at_next_pos == invalid_path) {
+		/* this node has already been searched */
+		return false;
+	} else if (at_next_pos == NULL) {
+		/* summarize paths; add up the paths with the same source and next hop in one path each */
+		PathSet & paths = this->graph->GetNode(next_id).paths;
+		PathViaMap next_hops;
+		for(PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+			Path * new_child = *i;
+			if (new_child->GetOrigin() == origin_id) {
+				PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
+				if (via_it == next_hops.end()) {
+					next_hops[new_child->GetNode()] = new_child;
+				} else {
+					Path * child = via_it->second;
+					uint new_flow = new_child->GetFlow();
+					child->AddFlow(new_flow);
+					new_child->ReduceFlow(new_flow);
+				}
+			}
+		}
+		bool found = false;
+		/* search the next hops for nodes we have already visited */
+		for (PathViaMap::iterator via_it = next_hops.begin(); via_it != next_hops.end(); ++via_it) {
+			Path * child = via_it->second;
+			if (child->GetFlow() > 0) {
+				/* push one child into the path vector and search this child's children */
+				path[next_id] = child;
+				found = EliminateCycles(path, origin_id, child->GetNode()) || found;
+			}
+		}
+		/* All paths departing from this node have been searched. Mark as resolved if no cycles found.
+		 * If cycles were found further cycles could be found in this branch, thus it has to be
+		 * searched again next time we spot it.
+		 */
+		if (found) {
+			path[next_id] = NULL;
+		} else {
+			path[next_id] = invalid_path;
+		}
+		return found;
+	} else {
+		/* this node has already been visited => we have a cycle
+		 * backtrack to find the exact flow
+		 */
+		uint flow = FindCycleFlow(path, at_next_pos);
+		if (flow > 0) {
+			EliminateCycle(path, at_next_pos, flow);
+			return true;
+		} else {
+			return false;
+		}
+	}
+}
+
+bool MCF1stPass::EliminateCycles()
+{
+	bool cycles_found = false;
+	uint size = this->graph->GetSize();
+	PathVector path(size, NULL);
+	for (NodeID node = 0; node < size; ++node) {
+		/* starting at each node in the graph find all cycles involving this node */
+		std::fill(path.begin(), path.end(), (Path *)NULL);
+		cycles_found = EliminateCycles(path, node, node) || cycles_found;
+	}
+	return cycles_found;
+}
+
+void MCF1stPass::Run(LinkGraphComponent * graph) {
+	MultiCommodityFlow::Run(graph);
+	PathVector paths;
+	uint size = graph->GetSize();
+	uint accuracy = graph->GetSettings().accuracy;
+	bool more_loops = true;
+
+	while (more_loops) {
+		more_loops = false;
+
+		for (NodeID source = 0; source < size; ++source) {
+			/* first saturate the shortest paths */
+			Dijkstra<DistanceAnnotation>(source, paths, true);
+
+			for (NodeID dest = 0; dest < size; ++dest) {
+				Edge & edge = graph->GetEdge(source, dest);
+				if (edge.unsatisfied_demand > 0) {
+					Path * path = paths[dest];
+					/* generally only allow paths that don't exceed the available capacity.
+					 * but if no demand has been assigned yet, make an exception and allow
+					 * any valid path *once*.
+					 */
+					if (path->GetCapacity() > 0 && PushFlow(edge, path, accuracy, true) > 0) {
+						more_loops = (edge.unsatisfied_demand > 0); {
+							/* if a path has been found there is a chance we can find more */
+							more_loops = true;
+						}
+					} else if (edge.unsatisfied_demand == edge.demand && path->GetCapacity() > INT_MIN) {
+						PushFlow(edge, path, accuracy, false);
+					}
+				}
+			}
+			CleanupPaths(source, paths);
+		}
+		if (!more_loops) {
+			more_loops = EliminateCycles();
+		}
+		if (accuracy > 1) --accuracy;
+	}
+}
+
+void MCF2ndPass::Run(LinkGraphComponent * graph) {
+	MultiCommodityFlow::Run(graph);
+	PathVector paths;
+	uint size = graph->GetSize();
+	uint accuracy = graph->GetSettings().accuracy;
+	bool demand_left = true;
+	while (demand_left) {
+		demand_left = false;
+		for (NodeID source = 0; source < size; ++source) {
+			/* Then assign all remaining demands */
+			Dijkstra<CapacityAnnotation>(source, paths, false);
+			for (NodeID dest = 0; dest < size; ++dest) {
+				Edge & edge = graph->GetEdge(source, dest);
+				Path * path = paths[dest];
+				if (edge.unsatisfied_demand > 0 && path->GetCapacity() > INT_MIN) {
+					PushFlow(edge, path, accuracy, false);
+					if (edge.unsatisfied_demand > 0) {
+						demand_left = true;
+					}
+				}
+			}
+			CleanupPaths(source, paths);
+		}
+		if (accuracy > 1) --accuracy;
+	}
+}
+
+/**
+ * avoid accidentally deleting different paths of the same capacity/distance in a set.
+ * When the annotation is the same node IDs are compared, so there are no equal ranges.
+ */
+template <typename T>
+bool greater(T x_anno, T y_anno, const Path * x, const Path * y) {
+	if (x_anno > y_anno) {
+		return true;
+	} else if (x_anno < y_anno) {
+		return false;
+	} else {
+		return x->GetNode() > y->GetNode();
+	}
+}
+
+bool CapacityAnnotation::comp::operator()(const CapacityAnnotation * x, const CapacityAnnotation * y) const {
+	return greater<int>(x->GetAnnotation(), y->GetAnnotation(), x, y);
+}
+
+bool DistanceAnnotation::comp::operator()(const DistanceAnnotation * x, const DistanceAnnotation * y) const {
+	return x != y && !greater<uint>(x->GetAnnotation(), y->GetAnnotation(), x, y);
+}
diff --git a/src/linkgraph/mcf.h b/src/linkgraph/mcf.h
new file mode 100644
index 0000000..e79611b
--- /dev/null
+++ b/src/linkgraph/mcf.h
@@ -0,0 +1,62 @@
+/** @file mcf.h Declaration of Multi-Commodity-Flow solver */
+
+#ifndef MCF_H_
+#define MCF_H_
+
+#include "linkgraph.h"
+#include <vector>
+
+class DistanceAnnotation : public Path {
+public:
+	DistanceAnnotation(NodeID n, bool source = false) : Path(n, source) {}
+	bool IsBetter(const DistanceAnnotation * base, int cap, uint dist) const;
+	uint GetAnnotation() const {return this->distance;}
+	struct comp {
+		bool operator()(const DistanceAnnotation * x, const DistanceAnnotation * y) const;
+	};
+};
+
+class CapacityAnnotation : public Path {
+public:
+	CapacityAnnotation(NodeID n, bool source = false) : Path(n, source) {}
+	bool IsBetter(const CapacityAnnotation * base, int cap, uint dist) const;
+	int GetAnnotation() const {return this->capacity;}
+	struct comp {
+		bool operator()(const CapacityAnnotation * x, const CapacityAnnotation * y) const;
+	};
+};
+
+
+typedef std::vector<Path *> PathVector;
+
+class MultiCommodityFlow : public ComponentHandler {
+public:
+	virtual void Run(LinkGraphComponent * graph);
+	MultiCommodityFlow();
+	virtual ~MultiCommodityFlow() {}
+protected:
+	template<class ANNOTATION>
+		void Dijkstra(NodeID from, PathVector & paths, bool create_new_paths);
+	uint PushFlow(Edge & edge, Path * path, uint accuracy, bool positive_cap);
+	void SetVia(NodeID source, Path * path);
+	void CleanupPaths(NodeID source, PathVector & paths);
+	LinkGraphComponent * graph;
+};
+
+class MCF1stPass : public MultiCommodityFlow {
+private:
+	bool EliminateCycles();
+	bool EliminateCycles(PathVector & path, NodeID origin, Path * next);
+	bool EliminateCycles(PathVector & path, NodeID origin_id, NodeID next_id);
+	void EliminateCycle(PathVector & path, Path * cycle_begin, uint flow);
+	uint FindCycleFlow(const PathVector & path, const Path * cycle_begin);
+public:
+	virtual void Run(LinkGraphComponent * graph);
+};
+
+class MCF2ndPass : public MultiCommodityFlow {
+public:
+	virtual void Run(LinkGraphComponent * graph);
+};
+
+#endif /* MCF_H_ */
diff --git a/src/misc.cpp b/src/misc.cpp
index 995f0dd..c93acd6 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -53,6 +53,7 @@ void InitializeCompanies();
 void InitializeCheats();
 void InitializeNPF();
 void InitializeOldNames();
+void InitializeLinkGraphs();
 
 void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings)
 {
@@ -114,6 +115,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
 #endif /* ENABLE_NETWORK */
 	InitializeAnimatedTiles();
 
+	InitializeLinkGraphs();
 	InitializeEconomy();
 
 	ResetObjectToPlace();
diff --git a/src/moving_average.cpp b/src/moving_average.cpp
new file mode 100644
index 0000000..08d42b9
--- /dev/null
+++ b/src/moving_average.cpp
@@ -0,0 +1,28 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file moving_average.cpp Implementation of moving average functions */
+
+
+#include "moving_average.h"
+#include "variables.h"
+#include "station_base.h"
+
+template <class Titem> void RunAverages()
+{
+	uint interval = _settings_game.economy.moving_average_unit * DAY_TICKS;
+	for(uint id = _tick_counter % interval; id < Titem::GetPoolSize(); id += interval) {
+		Titem *item = Titem::GetIfValid(id);
+		if (item != NULL) {
+			item->RunAverages();
+		}
+	}
+}
+
+template void RunAverages<Station>();
diff --git a/src/moving_average.h b/src/moving_average.h
new file mode 100644
index 0000000..ca9dce2
--- /dev/null
+++ b/src/moving_average.h
@@ -0,0 +1,45 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file moving_average.h Utility class for moving averages. */
+
+#ifndef MOVING_AVERAGE_H_
+#define MOVING_AVERAGE_H_
+
+#include "stdafx.h"
+#include "settings_type.h"
+#include "core/math_func.hpp"
+
+template<class Tvalue>
+class MovingAverage {
+protected:
+	uint length;
+
+public:
+	FORCEINLINE MovingAverage(uint length = _settings_game.economy.moving_average_length) : length(length) 
+		{assert(this->length > 0);}
+
+	FORCEINLINE uint Length() const
+		{return this->length;}
+
+	FORCEINLINE Tvalue Monthly(const Tvalue &value) const
+		{return value * 30 / (this->length * _settings_game.economy.moving_average_unit);}
+
+	FORCEINLINE Tvalue &Decrease(Tvalue &value) const
+	{
+		value *= this->length;
+		value /= (this->length + 1);
+		return value;
+	}
+};
+
+template<class Titem> void RunAverages();
+
+#endif /* MOVING_AVERAGE_H_ */
+
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index c3e0de9..bb535da 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -6624,6 +6624,9 @@ static void AfterLoadGRFs()
 	/* Create dynamic list of industry legends for smallmap_gui.cpp */
 	BuildIndustriesLegend();
 
+	/* build the routemap legend, based on the available cargos */
+	BuildLinkStatsLegend();
+
 	/* Add all new airports to the airports array. */
 	FinaliseAirportsArray();
 
diff --git a/src/openttd.cpp b/src/openttd.cpp
index 9886b9e..3f76cb3 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -1080,6 +1080,13 @@ void SwitchToMode(SwitchMode new_mode)
 		default: NOT_REACHED();
 	}
 
+	LinkGraphSettings &lg = _settings_game.linkgraph;
+	if (!_settings_client.gui.new_nonstop) {
+		if ((lg.demand_armoured | lg.demand_default | lg.demand_express | lg.demand_mail | lg.demand_pax) != DT_UNHANDLED) {
+			ShowErrorMessage(STR_WARNING_NONSTOP_CARGODIST, INVALID_STRING_ID, WL_WARNING, 0, true);
+		}
+	}
+
 	if (_switch_mode_errorstr != INVALID_STRING_ID) {
 		ShowErrorMessage(_switch_mode_errorstr, INVALID_STRING_ID, WL_CRITICAL);
 	}
diff --git a/src/order_base.h b/src/order_base.h
index 3e74f4c..71783e1 100644
--- a/src/order_base.h
+++ b/src/order_base.h
@@ -261,6 +261,8 @@ struct OrderList : OrderListPool::PoolItem<&_orderlist_pool> {
 private:
 	friend void AfterLoadVehicles(bool part_of_load); ///< For instantiating the shared vehicle chain
 	friend const struct SaveLoad *GetOrderListDescription(); ///< Saving and loading of order lists.
+	const Order * GetNext(const Order * curr) const;
+	const Order * GetNextStoppingOrder(const Order * next, uint hops, bool check_nonstop) const;
 
 	Order *first;                   ///< First order of the order list
 	VehicleOrderID num_orders;      ///< NOSAVE: How many orders there are in the list
@@ -310,6 +312,8 @@ public:
 	 */
 	inline Order *GetLastOrder() const { return this->GetOrderAt(this->num_orders - 1); }
 
+	StationID GetNextStoppingStation(VehicleOrderID curr, bool check_nonstop) const;
+
 	/**
 	 * Get number of orders in the order list.
 	 * @return number of orders in the chain. */
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index e198589..614e81d 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -257,6 +257,67 @@ Order *OrderList::GetOrderAt(int index) const
 	return order;
 }
 
+const Order * OrderList::GetNext(const Order * curr) const
+{
+	const Order * next = curr->next;
+	if (next == NULL) {
+		next = GetFirstOrder();
+	}
+	return next;
+}
+
+const Order * OrderList::GetNextStoppingOrder(const Order * next, uint hops, bool check_nonstop) const
+{
+	if (next == NULL || hops > GetNumOrders()) {
+		return NULL;
+	}
+
+	if (next->GetType() == OT_CONDITIONAL) {
+		const Order * skip_to = GetNextStoppingOrder(GetOrderAt(next->GetConditionSkipToOrder()), hops + 1, check_nonstop);
+		const Order * advance = GetNextStoppingOrder(next, hops + 1, check_nonstop);
+		if (skip_to == advance) {
+			return skip_to; // skipping over non-stopping orders
+		} else {
+			return NULL; // nondeterministic
+		}
+	}
+
+	bool is_station = (next->GetType() == OT_GOTO_STATION);
+
+	if (check_nonstop) {
+		switch(next->GetNonStopType()) {
+		case ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS:
+			if (is_station) return next; // else fall through
+		case ONSF_NO_STOP_AT_ANY_STATION:
+			return GetNextStoppingOrder(GetNext(next), hops + 1, check_nonstop);
+		default: // nondeterministic
+			return NULL;
+		}
+	} else {
+		if (is_station) {
+			return next;
+		} else {
+			return GetNextStoppingOrder(GetNext(next), hops + 1, check_nonstop);
+		}
+	}
+}
+
+StationID OrderList::GetNextStoppingStation(VehicleOrderID curr_id, bool check_nonstop) const {
+	const Order * curr = GetOrderAt(curr_id);
+	if (curr == NULL) {
+		curr = GetFirstOrder();
+		if (curr == NULL) {
+			return INVALID_STATION;
+		}
+	}
+	const Order * next = GetNextStoppingOrder(GetNext(curr), 1, check_nonstop);
+	if (next == NULL) {
+		return INVALID_STATION;
+	} else {
+		return next->GetDestination();
+	}
+}
+
 void OrderList::InsertOrderAt(Order *new_order, int index)
 {
 	if (this->first == NULL) {
@@ -674,6 +735,8 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 			}
 			/* Update any possible open window of the vehicle */
 			InvalidateVehicleOrder(u, INVALID_VEH_ORDER_ID | (sel_ord << 8));
+
+			RecalcFrozenIfLoading(u);
 		}
 
 		/* As we insert an order, the order to skip to will be 'wrong'. */
@@ -708,6 +771,9 @@ static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags)
 	if (flags & DC_EXEC) {
 		DeleteVehicleOrders(dst);
 		InvalidateVehicleOrder(dst, -1);
+
+		RecalcFrozenIfLoading(dst);
+
 		InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
 	}
 	return CommandCost();
@@ -757,6 +823,8 @@ CommandCost CmdDeleteOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 
 			/* Update any possible open window of the vehicle */
 			InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
+
+			RecalcFrozenIfLoading(u);
 		}
 
 		/* As we delete an order, the order to skip to will be 'wrong'. */
@@ -801,10 +869,10 @@ CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 	}
 
 	if (flags & DC_EXEC) {
-		v->cur_order_index = sel_ord;
-
 		if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
 
+		v->cur_order_index = sel_ord;
+
 		InvalidateVehicleOrder(v, -2);
 	}
 
@@ -867,6 +935,8 @@ CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
 			assert(v->orders.list == u->orders.list);
 			/* Update any possible open window of the vehicle */
 			InvalidateVehicleOrder(u, moving_order | (target_order << 8));
+
+			RecalcFrozenIfLoading(u);
 		}
 
 		/* As we move an order, the order to skip to will be 'wrong'. */
@@ -1117,6 +1187,8 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 				u->current_order.SetLoadType(order->GetLoadType());
 			}
 			InvalidateVehicleOrder(u, -2);
+
+			RecalcFrozenIfLoading(u);
 		}
 	}
 
@@ -1242,6 +1314,8 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
 		default: return CMD_ERROR;
 	}
 
+	RecalcFrozenIfLoading(dst);
+
 	return CommandCost();
 }
 
@@ -1539,6 +1613,8 @@ void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
 					/* In GUI, simulate by removing the order and adding it back */
 					InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
 					InvalidateVehicleOrder(w, (INVALID_VEH_ORDER_ID << 8) | id);
+
+					RecalcFrozenIfLoading(w);
 				}
 			}
 		}
@@ -1582,6 +1658,8 @@ void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist)
 		v->orders.list->FreeChain(keep_orderlist);
 		if (!keep_orderlist) v->orders.list = NULL;
 	}
+
+	RecalcFrozenIfLoading(v);
 }
 
 uint16 GetServiceIntervalClamped(uint interval, CompanyID company_id)
@@ -1782,7 +1860,7 @@ bool ProcessOrders(Vehicle *v)
 	if (((v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) || v->current_order.IsType(OT_GOTO_WAYPOINT)) &&
 			IsTileType(v->tile, MP_STATION) &&
 			v->current_order.GetDestination() == GetStationIndex(v->tile)) {
-		if (v->current_order.IsType(OT_GOTO_STATION)) v->last_station_visited = v->current_order.GetDestination();
+		/* treat it like a waypoint and don't set last_station_visited */
 		UpdateVehicleTimetable(v, true);
 		v->IncrementOrderIndex();
 	}
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 4a9ed1a..281e007 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -1432,9 +1432,10 @@ again:
 					v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
 					GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) {
 				Station *st = Station::GetByTile(v->tile);
+				StationID previous_station = v->last_station_visited;
 				v->last_station_visited = st->index;
 				RoadVehArrivesAt(v, st);
-				v->BeginLoading();
+				v->BeginLoading(previous_station);
 			}
 			return false;
 		}
@@ -1488,12 +1489,12 @@ again:
 			}
 
 			rs->SetEntranceBusy(false);
-
+			StationID previous_station = v->last_station_visited;
 			v->last_station_visited = st->index;
 
 			if (IsDriveThroughStopTile(v->tile) || (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == st->index)) {
 				RoadVehArrivesAt(v, st);
-				v->BeginLoading();
+				v->BeginLoading(previous_station);
 				return false;
 			}
 		} else {
diff --git a/src/saveload/cargopacket_sl.cpp b/src/saveload/cargopacket_sl.cpp
index 8090ed6..fa41231 100644
--- a/src/saveload/cargopacket_sl.cpp
+++ b/src/saveload/cargopacket_sl.cpp
@@ -26,7 +26,7 @@
 		 * to the current tile of the vehicle to prevent excessive profits
 		 */
 		FOR_ALL_VEHICLES(v) {
-			const VehicleCargoList::List *packets = v->cargo.Packets();
+			const CargoPacketList *packets = v->cargo.Packets();
 			for (VehicleCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
 				CargoPacket *cp = *it;
 				cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : v->tile;
@@ -44,7 +44,7 @@
 			for (CargoID c = 0; c < NUM_CARGO; c++) {
 				GoodsEntry *ge = &st->goods[c];
 
-				const StationCargoList::List *packets = ge->cargo.Packets();
+				const StationCargoPacketMap *packets = ge->cargo.Packets();
 				for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
 					CargoPacket *cp = *it;
 					cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : st->xy;
diff --git a/src/saveload/economy_sl.cpp b/src/saveload/economy_sl.cpp
index cce2d7f..1c1bed8 100644
--- a/src/saveload/economy_sl.cpp
+++ b/src/saveload/economy_sl.cpp
@@ -63,11 +63,11 @@ static void Load_ECMY()
 }
 
 static const SaveLoad _cargopayment_desc[] = {
-	SLE_REF(CargoPayment, front,         REF_VEHICLE),
-	SLE_VAR(CargoPayment, route_profit,  SLE_INT64),
-	SLE_VAR(CargoPayment, visual_profit, SLE_INT64),
-
-	SLE_END()
+	    SLE_REF(CargoPayment, front,           REF_VEHICLE),
+	    SLE_VAR(CargoPayment, route_profit,    SLE_INT64),
+	    SLE_VAR(CargoPayment, visual_profit,   SLE_INT64),
+	SLE_CONDVAR(CargoPayment, visual_transfer, SLE_INT64, CARGOMAP_SV, SL_MAX_VERSION),
+	    SLE_END()
 };
 
 static void Save_CAPY()
diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp
new file mode 100644
index 0000000..7b9955c
--- /dev/null
+++ b/src/saveload/linkgraph_sl.cpp
@@ -0,0 +1,163 @@
+/** @file linkgraph_sl.cpp Code handling saving and loading of link graphs */
+
+#include "../linkgraph/linkgraph.h"
+#include "../linkgraph/demands.h"
+#include "../settings_internal.h"
+#include "saveload.h"
+#include <vector>
+
+const SettingDesc *GetSettingDescription(uint index);
+
+static uint32 _num_components;
+static Date _join_date;
+
+enum {
+	LGRP_GRAPH = 0,
+	LGRP_COMPONENT = 1,
+	LGRP_NODE = 2,
+	LGRP_EDGE = 3,
+};
+
+const SaveLoad * GetLinkGraphComponentDesc() {
+
+	static const SaveLoad _component_desc[] = {
+		 SLE_CONDVAR(LinkGraphComponent, num_nodes,        SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraphComponent, index,            SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION),
+		SLEG_CONDVAR(                    _join_date,       SLE_INT32,  LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	size_t offset_gamesettings = cpp_offsetof(GameSettings, linkgraph);
+	size_t offset_component = cpp_offsetof(LinkGraphComponent, settings);
+
+	typedef std::vector<SaveLoad> SaveLoadVector;
+	static SaveLoadVector saveloads;
+	static const char * prefix = "linkgraph.";
+	size_t prefixlen = strlen(prefix);
+
+	int setting = 0;
+	const SettingDesc * desc = GetSettingDescription(setting);
+	while (desc->save.cmd != SL_END) {
+		if (desc->desc.name != NULL && strncmp(desc->desc.name, prefix, prefixlen) == 0) {
+			SaveLoad sl = desc->save;
+			char *& address = reinterpret_cast<char *&>(sl.address);
+			address -= offset_gamesettings;
+			address += offset_component;
+			saveloads.push_back(sl);
+		}
+		desc = GetSettingDescription(++setting);
+	}
+
+	int i = 0;
+	do {
+		saveloads.push_back(_component_desc[i++]);
+	} while (saveloads.back().cmd != SL_END);
+
+	return &saveloads[0];
+}
+
+const SaveLoad *GetGlobalCargoAcceptanceDesc()
+{
+	static const SaveLoad acceptance_desc[] = {
+		SLE_CONDARR(GlobalCargoAcceptance, acceptance,        SLE_UINT, NUM_CARGO, SUPPLY_SV, SL_MAX_VERSION),
+		SLE_CONDARR(GlobalCargoAcceptance, current_tile_loop, SLE_UINT, NUM_CARGO, SUPPLY_SV, SL_MAX_VERSION),
+		SLE_END()
+	};
+
+	return acceptance_desc;
+}
+
+const SaveLoad * GetLinkGraphDesc(uint type) {
+
+	static const SaveLoad _linkgraph_desc[] = {
+		SLEG_CONDVAR(           _num_components,      SLE_UINT32,                            LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraph, current_component_id, SLE_UINT16,                            LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraph, current_station_id,   SLE_UINT16,                            LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraph, cargo,                SLE_UINT8,                             LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDARR(LinkGraph, component_acceptance, SLE_UINT,   LinkGraph::MAX_COMPONENTS, SUPPLY_SV,    SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	static const SaveLoad * _component_desc = GetLinkGraphComponentDesc();
+
+	// edges and nodes are saved in the correct order, so we don't need to save their ids.
+
+	static const SaveLoad _node_desc[] = {
+		 SLE_CONDVAR(Node, supply,    SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Node, demand,    SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Node, station,   SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	static const SaveLoad _edge_desc[] = {
+		 SLE_CONDVAR(Edge, distance,  SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Edge, capacity,  SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Edge, next_edge, SLE_UINT32,       MCF_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	static const SaveLoad *_lgrp_descs[] = {
+		_linkgraph_desc,
+		_component_desc,
+		_node_desc,
+		_edge_desc,
+	};
+
+	return _lgrp_descs[type];
+
+}
+
+static void SaveLoad_LinkGraphComponent(LinkGraphComponent * comp) {
+	for (NodeID from = 0; from < comp->GetSize(); ++from) {
+		Node * node = &comp->GetNode(from);
+		SlObject(node, GetLinkGraphDesc(LGRP_NODE));
+		node->undelivered_supply = node->supply;
+		for (NodeID to = 0; to < comp->GetSize(); ++to) {
+			SlObject(&comp->GetEdge(from, to), GetLinkGraphDesc(LGRP_EDGE));
+		}
+	}
+}
+
+static void DoSave_LGRP(void *)
+{
+	for(CargoID cargo = CT_BEGIN; cargo != CT_END; ++cargo) {
+		LinkGraph & graph = _link_graphs[cargo];
+		_num_components = (uint32)graph.GetNumJobs();
+		SlObject(&graph, GetLinkGraphDesc(LGRP_GRAPH));
+		JobList & jobs = graph.GetJobs();
+		for (JobList::iterator i = jobs.begin(); i != jobs.end(); ++i) {
+			LinkGraphJob * job = *i;
+			LinkGraphComponent * comp = job->GetComponent();
+			_join_date = job->GetJoinDate();
+			SlObject(comp, GetLinkGraphDesc(LGRP_COMPONENT));
+			SaveLoad_LinkGraphComponent(comp);
+		}
+	}
+
+	SlObject(&GlobalCargoAcceptance::inst, GetGlobalCargoAcceptanceDesc());
+}
+
+static void Load_LGRP()
+{
+	for(CargoID cargo = CT_BEGIN; cargo != CT_END; ++cargo) {
+		LinkGraph & graph = _link_graphs[cargo];
+		SlObject(&graph, GetLinkGraphDesc(LGRP_GRAPH));
+		for (uint32 i = 0; i < _num_components; ++i) {
+			LinkGraphComponent * comp = new LinkGraphComponent(cargo);
+			SlObject(comp, GetLinkGraphDesc(LGRP_COMPONENT));
+			comp->SetSize(comp->GetSize());
+			SaveLoad_LinkGraphComponent(comp);
+			graph.AddComponent(comp, _join_date);
+		}
+	}
+
+	SlObject(&GlobalCargoAcceptance::inst, GetGlobalCargoAcceptanceDesc());
+}
+
+static void Save_LGRP() {
+	SlAutolength((AutolengthProc*)DoSave_LGRP, NULL);
+}
+
+extern const ChunkHandler _linkgraph_chunk_handlers[] = {
+	{ 'LGRP', Save_LGRP,      Load_LGRP,	NULL,      CH_LAST},
+};
diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp
index b2eec5a..1c2919d 100644
--- a/src/saveload/oldloader_sl.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -700,7 +700,7 @@ static bool LoadOldGood(LoadgameState *ls, int num)
 	SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
 	SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, _cargo_source != 0xFF);
 	if (GB(_waiting_acceptance, 0, 12) != 0) {
-		ge->cargo.Append(new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, 0, 0));
+		ge->cargo.Append(INVALID_STATION, new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, 0, 0));
 	}
 
 	return true;
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index 7a9c81e..76d5065 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -46,7 +46,7 @@
 
 #include "saveload_internal.h"
 
-extern const uint16 SAVEGAME_VERSION = 138;
+extern const uint16 SAVEGAME_VERSION = CARGOMAP_SV;
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
@@ -128,6 +128,7 @@ extern const ChunkHandler _group_chunk_handlers[];
 extern const ChunkHandler _cargopacket_chunk_handlers[];
 extern const ChunkHandler _autoreplace_chunk_handlers[];
 extern const ChunkHandler _labelmaps_chunk_handlers[];
+extern const ChunkHandler _linkgraph_chunk_handlers[];
 
 static const ChunkHandler * const _chunk_handlers[] = {
 	_gamelog_chunk_handlers,
@@ -155,6 +156,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
 	_cargopacket_chunk_handlers,
 	_autoreplace_chunk_handlers,
 	_labelmaps_chunk_handlers,
+	_linkgraph_chunk_handlers,
 	NULL,
 };
 
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 0914a50..07d05e0 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -338,4 +338,14 @@ bool SaveloadCrashWithMissingNewGRFs();
 
 extern char _savegame_format[8];
 
+#define MOVING_AVERAGE_SV 149
+#define CAPACITIES_SV 150
+#define LINKGRAPH_SV 155
+#define SUPPLY_SV 157
+#define DEMANDS_SV 160
+#define MCF_SV 165
+#define FLOWMAP_SV 170
+#define RESERVATION_SV 240
+#define CARGOMAP_SV 250
+
 #endif /* SAVELOAD_H */
diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp
index 72f107e..e3f9949 100644
--- a/src/saveload/station_sl.cpp
+++ b/src/saveload/station_sl.cpp
@@ -207,9 +207,11 @@ static const SaveLoad _old_station_desc[] = {
 };
 
 static uint16 _waiting_acceptance;
+static uint16 _num_links;
+static uint32 _num_flows;
 static uint16 _cargo_source;
 static uint32 _cargo_source_xy;
-static uint16 _cargo_days;
+static uint8  _cargo_days;
 static Money  _cargo_feeder_share;
 
 static const SaveLoad _station_speclist_desc[] = {
@@ -219,6 +221,44 @@ static const SaveLoad _station_speclist_desc[] = {
 	SLE_END()
 };
 
+static StationID _station_id;
+
+const SaveLoad *GetLinkStatDesc() {
+	static const SaveLoad linkstat_desc[] = {
+		SLEG_CONDVAR(             _station_id,         SLE_UINT16,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkStat,    length,              SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkStat,    capacity,            SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkStat,    frozen,              SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkStat,    usage,               SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+	
+	return linkstat_desc;
+}
+
+const SaveLoad *GetFlowStatDesc() {
+	static const SaveLoad _flowstat_desc[] = {
+		SLEG_CONDVAR(             _station_id,         SLE_UINT16,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(FlowStat,    via,                 SLE_UINT16,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(FlowStat,    length,              SLE_UINT32,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(FlowStat,    planned,             SLE_UINT32,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(FlowStat,    sent,                SLE_UINT32,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	return _flowstat_desc;
+}
+
+void CountFlows(FlowStatMap & flows) {
+	_num_flows = 0;
+	for(FlowStatMap::iterator i = flows.begin(); i != flows.end(); ++i) {
+		_num_flows += (uint32)i->second.size();
+	}
+}
+
+std::list<CargoPacket *> _packets;
+uint32 _num_dests;
+
 /**
  * Wrapper function to get the GoodsEntry's internal structure while
  * some of the variables itself are private.
@@ -229,6 +269,7 @@ const SaveLoad *GetGoodsDesc()
 	static const SaveLoad goods_desc[] = {
 		SLEG_CONDVAR(            _waiting_acceptance, SLE_UINT16,                  0, 67),
 		 SLE_CONDVAR(GoodsEntry, acceptance_pickup,   SLE_UINT8,                  68, SL_MAX_VERSION),
+		 SLE_CONDVAR(GoodsEntry, acceptance,          SLE_UINT,            SUPPLY_SV, SL_MAX_VERSION),
 		SLE_CONDNULL(2,                                                           51, 67),
 		     SLE_VAR(GoodsEntry, days_since_pickup,   SLE_UINT8),
 		     SLE_VAR(GoodsEntry, rating,              SLE_UINT8),
@@ -240,14 +281,43 @@ const SaveLoad *GetGoodsDesc()
 		     SLE_VAR(GoodsEntry, last_age,            SLE_UINT8),
 		SLEG_CONDVAR(            _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, 14, 64),
 		SLEG_CONDVAR(            _cargo_feeder_share, SLE_INT64,                  65, 67),
-		 SLE_CONDLST(GoodsEntry, cargo.packets,       REF_CARGO_PACKET,           68, SL_MAX_VERSION),
-
+		SLEG_CONDLST(            _packets,            REF_CARGO_PACKET,           68, CARGOMAP_SV - 1),
+		SLEG_CONDVAR(            _num_dests,          SLE_UINT32,        CARGOMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(GoodsEntry, supply.supply,       SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		SLEG_CONDVAR(            _num_links,          SLE_UINT16,      CAPACITIES_SV, SL_MAX_VERSION),
+		SLEG_CONDVAR(            _num_flows,          SLE_UINT32,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(GoodsEntry, last_component,      SLE_UINT16,       LINKGRAPH_SV, SL_MAX_VERSION),
 		SLE_END()
 	};
 
 	return goods_desc;
 }
 
+typedef std::pair<const StationID, std::list<CargoPacket *> > StationCargoPair;
+
+static const SaveLoad _cargo_list_desc[] = {
+	SLE_VAR(StationCargoPair, first,  SLE_UINT16),
+	SLE_LST(StationCargoPair, second, REF_CARGO_PACKET),
+	SLE_END()
+};
+
+static void SwapPackets(GoodsEntry *ge)
+{
+	StationCargoPacketMap &ge_packets = const_cast<StationCargoPacketMap &>(*ge->cargo.Packets());
+
+	if (_packets.empty()) {
+		std::map<StationID, std::list<CargoPacket *> >::iterator it  = ge_packets.find(INVALID_STATION);
+		if (it == ge_packets.end()) {
+			return;
+		} else {
+			it->second.swap(_packets);
+		}
+	} else {
+		std::list<CargoPacket *> &list = ge_packets[INVALID_STATION];
+		assert(list.empty());
+		list.swap(_packets);
+	}
+}
 
 static void Load_STNS()
 {
@@ -263,6 +333,7 @@ static void Load_STNS()
 		for (CargoID i = 0; i < num_cargo; i++) {
 			GoodsEntry *ge = &st->goods[i];
 			SlObject(ge, GetGoodsDesc());
+			SwapPackets(ge);
 			if (CheckSavegameVersion(68)) {
 				SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
 				if (GB(_waiting_acceptance, 0, 12) != 0) {
@@ -270,7 +341,8 @@ static void Load_STNS()
 					StationID source = (CheckSavegameVersion(7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
 
 					/* Don't construct the packet with station here, because that'll fail with old savegames */
-					ge->cargo.Append(new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share));
+					CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share);
+					ge->cargo.Append(INVALID_STATION, cp);
 					SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, 1);
 				}
 			}
@@ -296,7 +368,9 @@ static void Ptrs_STNS()
 		if (!CheckSavegameVersion(68)) {
 			for (CargoID i = 0; i < NUM_CARGO; i++) {
 				GoodsEntry *ge = &st->goods[i];
+				SwapPackets(ge);
 				SlObject(ge, GetGoodsDesc());
+				SwapPackets(ge);
 			}
 		}
 		SlObject(st, _old_station_desc);
@@ -379,7 +453,28 @@ static void RealSave_STNN(BaseStation *bst)
 	if (!waypoint) {
 		Station *st = Station::From(bst);
 		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			SlObject(&st->goods[i], GetGoodsDesc());
+			GoodsEntry *ge = &st->goods[i];
+			_num_dests = (uint32)ge->cargo.Packets()->MapSize();
+			LinkStatMap &stats = ge->link_stats;
+			_num_links = (uint16)stats.size();
+			FlowStatMap & flows = ge->flows;
+			CountFlows(flows);
+			SlObject(ge, GetGoodsDesc());
+			for (LinkStatMap::iterator i = stats.begin(); i != stats.end(); ++i) {
+				_station_id = i->first;
+				SlObject(&(i->second), GetLinkStatDesc());
+			}
+			for (FlowStatMap::iterator i = flows.begin(); i != flows.end(); ++i) {
+				_station_id = i->first;
+				FlowStatSet & flow_set = i->second;
+				for (FlowStatSet::iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
+					FlowStat fs = *j;
+					SlObject(&fs, GetFlowStatDesc());
+				}
+			}
+			for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) {
+				SlObject(const_cast<StationCargoPacketMap::value_type *>(&(*it)), _cargo_list_desc);
+			}
 		}
 	}
 
@@ -411,7 +506,32 @@ static void Load_STNN()
 		if (!waypoint) {
 			Station *st = Station::From(bst);
 			for (CargoID i = 0; i < NUM_CARGO; i++) {
-				SlObject(&st->goods[i], GetGoodsDesc());
+				GoodsEntry *ge = &st->goods[i];
+				LinkStatMap &stats = ge->link_stats;
+				FlowStatMap & flows = ge->flows;
+				SlObject(ge, GetGoodsDesc());
+
+				LinkStat ls;
+				for (uint16 i = 0; i < _num_links; ++i) {
+					SlObject(&ls, GetLinkStatDesc());
+					assert(!ls.IsNull());
+					stats[_station_id] = ls;
+				}
+				FlowStat fs;
+				for (uint32 i = 0; i < _num_flows; ++i) {
+					SlObject(&fs, GetFlowStatDesc());
+					flows[_station_id].insert(fs);
+				}
+				if (CheckSavegameVersion(CARGOMAP_SV -1)) {
+					SwapPackets(ge);
+				} else {
+					StationCargoPair pair;
+					for(uint i = 0; i < _num_dests; ++i) {
+						SlObject(&pair, _cargo_list_desc);
+						const_cast<StationCargoPacketMap &>(*ge->cargo.Packets())[pair.first].swap(pair.second);
+						assert(pair.second.empty());
+					}
+				}
 			}
 		}
 
@@ -434,7 +554,17 @@ static void Ptrs_STNN()
 	FOR_ALL_STATIONS(st) {
 		for (CargoID i = 0; i < NUM_CARGO; i++) {
 			GoodsEntry *ge = &st->goods[i];
-			SlObject(ge, GetGoodsDesc());
+			if (CheckSavegameVersion(CARGOMAP_SV)) {
+				SwapPackets(ge);
+				SlObject(ge, GetGoodsDesc());
+				SwapPackets(ge);
+			} else {
+				SlObject(ge, GetGoodsDesc());
+				for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) {
+					SlObject(const_cast<StationCargoPair *>(&(*it)), _cargo_list_desc);
+				}
+			}
+			// as there are no pointers in the link stats we don't have to consider them
 		}
 		SlObject(st, _station_desc);
 	}
diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp
index 70ce2e1..1e98441 100644
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -447,6 +447,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
 		     SLE_VAR(Vehicle, cargo_cap,             SLE_UINT16),
 		SLEG_CONDVAR(         _cargo_count,          SLE_UINT16,                   0,  67),
 		 SLE_CONDLST(Vehicle, cargo.packets,         REF_CARGO_PACKET,            68, SL_MAX_VERSION),
+		 SLE_CONDLST(Vehicle, cargo.reserved,        REF_CARGO_PACKET,RESERVATION_SV, SL_MAX_VERSION),
 
 		     SLE_VAR(Vehicle, day_counter,           SLE_UINT8),
 		     SLE_VAR(Vehicle, tick_counter,          SLE_UINT8),
diff --git a/src/settings.cpp b/src/settings.cpp
index 051a11e..60711f3 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1437,7 +1437,7 @@ void DeleteGRFPresetFromConfig(const char *config_name)
 	delete ini;
 }
 
-static const SettingDesc *GetSettingDescription(uint index)
+const SettingDesc *GetSettingDescription(uint index)
 {
 	if (index >= lengthof(_settings)) return NULL;
 	return &_settings[index];
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 70dc8a4..423c488 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1380,11 +1380,29 @@ static SettingEntry _settings_economy[] = {
 	SettingEntry(&_settings_economy_industries_page, STR_CONFIG_SETTING_ECONOMY_INDUSTRIES),
 	SettingEntry("economy.inflation"),
 	SettingEntry("economy.smooth_economy"),
+	SettingEntry("economy.moving_average_unit"),
+	SettingEntry("economy.moving_average_length"),
+	SettingEntry("economy.cap_ratings_by_component"),
 	SettingEntry("economy.feeder_payment_share"),
 };
 /** Economy sub-page */
 static SettingsPage _settings_economy_page = {_settings_economy, lengthof(_settings_economy)};
 
+static SettingEntry _settings_linkgraph[] = {
+	SettingEntry("linkgraph.recalc_interval"),
+	SettingEntry("linkgraph.demand_pax"),
+	SettingEntry("linkgraph.demand_mail"),
+	SettingEntry("linkgraph.demand_express"),
+	SettingEntry("linkgraph.demand_armoured"),
+	SettingEntry("linkgraph.demand_default"),
+	SettingEntry("linkgraph.accuracy"),
+	SettingEntry("linkgraph.demand_distance"),
+	SettingEntry("linkgraph.demand_size"),
+	SettingEntry("linkgraph.short_path_saturation"),
+};
+/** Linkgraph sub-page */
+static SettingsPage _settings_linkgraph_page = {_settings_linkgraph, lengthof(_settings_linkgraph)};
+
 static SettingEntry _settings_ai_npc[] = {
 	SettingEntry("ai.ai_in_multiplayer"),
 	SettingEntry("ai.ai_disable_veh_train"),
@@ -1475,6 +1493,7 @@ static SettingEntry _settings_main[] = {
 	SettingEntry(&_settings_vehicles_page,     STR_CONFIG_SETTING_VEHICLES),
 	SettingEntry(&_settings_stations_page,     STR_CONFIG_SETTING_STATIONS),
 	SettingEntry(&_settings_economy_page,      STR_CONFIG_SETTING_ECONOMY),
+	SettingEntry(&_settings_linkgraph_page,    STR_CONFIG_SETTING_LINKGRAPH),
 	SettingEntry(&_settings_ai_page,           STR_CONFIG_SETTING_AI),
 };
 
diff --git a/src/settings_type.h b/src/settings_type.h
index 8a7c15e..0561595 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -17,6 +17,7 @@
 #include "transport_type.h"
 #include "network/core/config.h"
 #include "company_type.h"
+#include "linkgraph/demand_settings.h"
 
 /** Settings related to the difficulty of the game */
 struct DifficultySettings {
@@ -101,6 +102,10 @@ struct GUISettings {
 
 	uint16 console_backlog_timeout;          ///< the minimum amount of time items should be in the console backlog before they will be removed in ~3 seconds granularity.
 	uint16 console_backlog_length;           ///< the minimum amount of items in the console backlog before items will be removed.
+
+	uint8  station_gui_group_order;          ///< the order of grouping cargo entries in the station gui
+	uint8  station_gui_sort_by;              ///< sort cargo entries in the station gui by station name or amount
+	uint8  station_gui_sort_order;           ///< the sort order of entries in the station gui - ascending or descending
 #ifdef ENABLE_NETWORK
 	uint16 network_chat_box_width;           ///< width of the chat box in pixels
 	uint8  network_chat_box_height;          ///< height of the chat box in lines
@@ -347,6 +352,22 @@ struct EconomySettings {
 	TownFoundingByte found_town;             ///< town founding, @see TownFounding
 	bool   station_noise_level;              ///< build new airports when the town noise level is still within accepted limits
 	uint16 town_noise_population[3];         ///< population to base decision on noise evaluation (@see town_council_tolerance)
+	uint16 moving_average_unit;              ///< unit of time to use for calculating the moving averages
+	uint16 moving_average_length;            ///< minimum length of moving averages
+	bool   cap_ratings_by_component;         ///< cap the supply of cargo by size of the linkgraph component
+};
+
+struct LinkGraphSettings {
+	uint16 recalc_interval;                  ///< minimum interval (in days) between subsequent recalculations of the same component of the link graph
+	DistributionTypeByte demand_pax;         ///< demand calculation for passengers
+	DistributionTypeByte demand_mail;        ///< demand calculation for mail
+	DistributionTypeByte demand_express;     ///< demand calculation for express cargo class
+	DistributionTypeByte demand_armoured;    ///< demand calculation for armoured cargo class
+	DistributionTypeByte demand_default;     ///< demand calculation for all other goods
+	uint8 accuracy;                          ///< accuracy when calculating things on the link graph. low accuracy => low running time
+	uint8 demand_size;                       ///< influence of supply ("station size") on the demand function
+	uint8 demand_distance;                   ///< influence of distance between stations on the demand function
+	uint8 short_path_saturation;             ///< percentage up to which short paths are saturated before saturating most capacious paths
 };
 
 /** Settings related to stations. */
@@ -389,6 +410,7 @@ struct GameSettings {
 	OrderSettings        order;              ///< settings related to orders
 	VehicleSettings      vehicle;            ///< options for vehicles
 	EconomySettings      economy;            ///< settings to change the economy
+	LinkGraphSettings    linkgraph;          ///< settings for link graph calculations
 	StationSettings      station;            ///< settings related to station management
 	LocaleSettings       locale;             ///< settings related to used currency/unit system in the current game
 };
diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp
index ec907d2..a1550af 100644
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -517,13 +517,14 @@ static void ShipController(Ship *v)
 									return;
 								}
 							} else if (v->current_order.IsType(OT_GOTO_STATION)) {
+								StationID previous_station = v->last_station_visited;
 								v->last_station_visited = v->current_order.GetDestination();
 
 								/* Process station in the orderlist. */
 								Station *st = Station::Get(v->current_order.GetDestination());
 								if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
 									ShipArrivesAt(v, st);
-									v->BeginLoading();
+									v->BeginLoading(previous_station);
 								} else { // leave stations without docks right aways
 									v->current_order.MakeLeaveStation();
 									v->IncrementOrderIndex();
diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp
index 5155436..4aa17ba 100644
--- a/src/smallmap_gui.cpp
+++ b/src/smallmap_gui.cpp
@@ -25,35 +25,60 @@
 #include "vehicle_base.h"
 #include "sound_func.h"
 #include "window_func.h"
+#include "cargotype.h"
+#include "openttd.h"
+#include "company_func.h"
+#include "station_base.h"
 #include "company_base.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
 
+#include <cmath>
+#include <vector>
+
 /** Widget numbers of the small map window. */
 enum SmallMapWindowWidgets {
 	SM_WIDGET_CAPTION,           ///< Caption widget.
 	SM_WIDGET_MAP_BORDER,        ///< Border around the smallmap.
 	SM_WIDGET_MAP,               ///< Panel containing the smallmap.
 	SM_WIDGET_LEGEND,            ///< Bottom panel to display smallmap legends.
+	SM_WIDGET_BLANK,
 	SM_WIDGET_ZOOM_IN,           ///< Button to zoom in one step.
 	SM_WIDGET_ZOOM_OUT,          ///< Button to zoom out one step.
 	SM_WIDGET_CONTOUR,           ///< Button to select the contour view (height map).
 	SM_WIDGET_VEHICLES,          ///< Button to select the vehicles view.
 	SM_WIDGET_INDUSTRIES,        ///< Button to select the industries view.
+	SM_WIDGET_LINKSTATS,
 	SM_WIDGET_ROUTES,            ///< Button to select the routes view.
 	SM_WIDGET_VEGETATION,        ///< Button to select the vegetation view.
 	SM_WIDGET_OWNERS,            ///< Button to select the owners view.
 	SM_WIDGET_CENTERMAP,         ///< Button to move smallmap center to main window center.
 	SM_WIDGET_TOGGLETOWNNAME,    ///< Toggle button to display town names.
 	SM_WIDGET_SELECTINDUSTRIES,  ///< Selection widget for the buttons at the industry mode.
-	SM_WIDGET_ENABLEINDUSTRIES,  ///< Button to enable display of all industries.
-	SM_WIDGET_DISABLEINDUSTRIES, ///< Button to disable display of all industries.
+	SM_WIDGET_ENABLE_ALL,        ///< Button to enable display of all industries or link stats.
+	SM_WIDGET_DISABLE_ALL,       ///< Button to disable display of all industries or link stats.
 	SM_WIDGET_SHOW_HEIGHT,       ///< Show heightmap toggle button.
 };
 
+
 static int _smallmap_industry_count; ///< Number of used industries
 
+/* number of cargos in the link stats legend */
+static int _smallmap_cargo_count;
+
+enum SmallMapStats {
+	STAT_CAPACITY,
+	STAT_BEGIN = STAT_CAPACITY,
+	STAT_USAGE,
+	STAT_PLANNED,
+	STAT_SENT,
+	STAT_TEXT,
+	STAT_GRAPH,
+	STAT_END,
+	NUM_STATS = STAT_END,
+};
+
 /** Macro for ordinary entry of LegendAndColour */
 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, false}
 /** Macro for an entry with configurable colour. */
@@ -148,8 +173,8 @@ static const LegendAndColour _legend_land_owners[] = {
 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
 /* For connecting industry type to position in industries list(small map legend) */
 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
-/** Show heightmap in industry mode of smallmap window. */
-static bool _smallmap_industry_show_heightmap;
+/** Show heightmap in smallmap window. */
+static bool _smallmap_show_heightmap;
 
 /**
  * Fills an array for the industries legends.
@@ -181,10 +206,81 @@ void BuildIndustriesLegend()
 	_smallmap_industry_count = j;
 }
 
+static LegendAndColour _legend_linkstats[NUM_CARGO + NUM_STATS + 1];
+
+/**
+ * Populate legend table for the route map view.
+ */
+void BuildLinkStatsLegend()
+{
+	/* Clear the legend */
+	memset(_legend_linkstats, 0, sizeof(_legend_linkstats));
+
+	uint i = 0;
+
+	for (CargoID c = CT_BEGIN; c != CT_END; ++c) {
+		const CargoSpec *cs = CargoSpec::Get(c);
+		if (!cs->IsValid()) continue;
+
+		_legend_linkstats[i].legend = cs->name;
+		_legend_linkstats[i].colour = cs->legend_colour;
+		_legend_linkstats[i].type = c;
+		_legend_linkstats[i].show_on_map = true;
+
+		i++;
+	}
+
+	_legend_linkstats[i].col_break = true;
+
+	_smallmap_cargo_count = i;
+
+	/* the colours cannot be resolved before the gfx system is initialized.
+	 * So we have to build the legend when creating the window.
+	 */
+	for (uint st = 0; st < NUM_STATS; ++st) {
+		LegendAndColour & legend_entry = _legend_linkstats[i + st];
+		switch(st) {
+		case STAT_CAPACITY:
+			legend_entry.colour = _colour_gradient[COLOUR_WHITE][7];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_CAPACITY;
+			legend_entry.show_on_map = true;
+			break;
+		case STAT_USAGE:
+			legend_entry.colour = _colour_gradient[COLOUR_GREY][1];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_USAGE;
+			legend_entry.show_on_map = false;
+			break;
+		case STAT_PLANNED:
+			legend_entry.colour = _colour_gradient[COLOUR_RED][5];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_PLANNED;
+			legend_entry.show_on_map = true;
+			break;
+		case STAT_SENT:
+			legend_entry.colour = _colour_gradient[COLOUR_YELLOW][5];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_SENT;
+			legend_entry.show_on_map = false;
+			break;
+		case STAT_TEXT:
+			legend_entry.colour = _colour_gradient[COLOUR_GREY][7];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_SHOW_TEXT;
+			legend_entry.show_on_map = false;
+			break;
+		case STAT_GRAPH:
+			legend_entry.colour = _colour_gradient[COLOUR_GREY][7];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_SHOW_GRAPH;
+			legend_entry.show_on_map = true;
+			break;
+		}
+	}
+
+	_legend_linkstats[i + NUM_STATS].end = true;
+}
+
 static const LegendAndColour * const _legend_table[] = {
 	_legend_land_contours,
 	_legend_vehicles,
 	_legend_from_industries,
+	_legend_linkstats,
 	_legend_routes,
 	_legend_vegetation,
 	_legend_land_owners,
@@ -398,7 +494,7 @@ static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
 	}
 
 	const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
-	return ApplyMask(_smallmap_industry_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
+	return ApplyMask(_smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]);
 }
 
 /**
@@ -426,6 +522,18 @@ static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t)
 	return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
 }
 
+/**
+ * Return the colour a tile would be displayed with in the small map in mode "link stats".
+ *
+ * @param tile The tile of which we would like to get the colour.
+ * @param t    Effective tile type of the tile (see #GetEffectiveTileType).
+ * @return The colour of tile in the small map in mode "link stats"
+ */
+static inline uint32 GetSmallMapLinkStatsPixels(TileIndex tile, TileType t)
+{
+	return _smallmap_show_heightmap ? GetSmallMapContoursPixels(tile, t) : GetSmallMapRoutesPixels(tile, t);
+}
+
 
 static const uint32 _vegetation_clear_bits[] = {
 	MKCOLOUR(0x54545454), ///< full grass
@@ -492,12 +600,29 @@ static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t)
 	return _owner_colours[o];
 }
 
+
 /** Vehicle colours in #SMT_VEHICLES mode. Indexed by #VehicleTypeByte. */
 static const byte _vehicle_type_colours[6] = {
 	184, 191, 152, 15, 215, 184
 };
 
 
+void DrawVertex(int x, int y, int size, int colour, int boder_colour)
+{
+	size--;
+	int w1 = size / 2;
+	int w2 = size / 2 + size % 2;
+
+	GfxFillRect(x - w1, y - w1, x + w2, y + w2, colour);
+
+	w1++;
+	w2++;
+	GfxDrawLine(x - w1, y - w1, x + w2, y - w1, boder_colour);
+	GfxDrawLine(x - w1, y + w2, x + w2, y + w2, boder_colour);
+	GfxDrawLine(x - w1, y - w1, x - w1, y + w2, boder_colour);
+	GfxDrawLine(x + w2, y - w1, x + w2, y + w2, boder_colour);
+}
+
 /** Class managing the smallmap window. */
 class SmallMapWindow : public Window {
 	/** Types of legends in the #SM_WIDGET_LEGEND widget. */
@@ -505,11 +630,24 @@ class SmallMapWindow : public Window {
 		SMT_CONTOUR,
 		SMT_VEHICLES,
 		SMT_INDUSTRY,
+		SMT_LINKSTATS,
 		SMT_ROUTES,
 		SMT_VEGETATION,
 		SMT_OWNER,
 	};
 
+	/**
+	 * save the Vehicle's old position here, so that we don't get glitches when redrawing
+	 */
+	struct VehicleAndPosition {
+		VehicleAndPosition(const Vehicle *v) : tile(v->tile), vehicle(v->index) {}
+		TileIndex tile;
+		VehicleID vehicle;
+	};
+
+	typedef std::list<VehicleAndPosition> VehicleList;
+	VehicleList vehicles_on_map;
+	
 	/** Available kinds of zoomlevel changes. */
 	enum ZoomLevelChange {
 		ZLC_INITIALIZE, ///< Initialize zoom level.
@@ -526,12 +664,85 @@ class SmallMapWindow : public Window {
 	uint min_number_of_fixed_rows; ///< Minimal number of rows in the legends for the fixed layouts only (all except #SMT_INDUSTRY).
 	uint column_width;             ///< Width of a column in the #SM_WIDGET_LEGEND widget.
 
+	bool HasButtons()
+	{
+		return this->map_type == SMT_INDUSTRY || this->map_type == SMT_LINKSTATS;
+	}
+
+	Point cursor;
+
+	struct BaseCargoDetail {
+		BaseCargoDetail()
+		{
+			this->Clear();
+		}
+
+		void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow)
+		{
+			this->capacity += orig_link.Capacity();
+			this->usage += orig_link.Usage();
+			this->planned += orig_flow.Planned();
+			this->sent += orig_flow.Sent();
+		}
+
+		void Clear()
+		{
+			capacity = usage = planned = sent = 0;
+		}
+
+		uint capacity;
+		uint usage;
+		uint planned;
+		uint sent;
+	};
+
+	struct CargoDetail : public BaseCargoDetail {
+		CargoDetail(const LegendAndColour * c, const LinkStat &ls, const FlowStat &fs) : legend(c)
+		{
+			this->AddLink(ls, fs);
+		}
+
+		const LegendAndColour *legend;
+	};
+
+	typedef std::vector<CargoDetail> StatVector;
+
+	struct LinkDetails {
+		LinkDetails() {Clear();}
+
+		StationID sta;
+		StationID stb;
+		StatVector a_to_b;
+		StatVector b_to_a;
+
+		void Clear()
+		{
+			this->sta = INVALID_STATION;
+			this->stb = INVALID_STATION;
+			this->a_to_b.clear();
+			this->b_to_a.clear();
+		}
+
+		bool Empty() const
+		{
+			return this->sta == INVALID_STATION;
+		}
+	};
+
+	/**
+	 * those are detected while drawing the links and used when drawing
+	 * the legend. They don't represent game state.
+	 */
+	mutable LinkDetails link_details;
+	mutable StationID supply_details;
+
 	int32 scroll_x;  ///< Horizontal world coordinate of the base tile left of the top-left corner of the smallmap display.
 	int32 scroll_y;  ///< Vertical world coordinate of the base tile left of the top-left corner of the smallmap display.
 	int32 subscroll; ///< Number of pixels (0..3) between the right end of the base tile and the pixel at the top-left corner of the smallmap display.
 	int zoom;        ///< Zoom level. Bigger number means more zoom-out (further away).
 
 	static const uint8 FORCE_REFRESH_PERIOD = 0x1F; ///< map is redrawn after that many ticks
+	static const uint8 REFRESH_NEXT_TICK = 1;
 	uint8 refresh; ///< refresh counter, zeroed every FORCE_REFRESH_PERIOD ticks
 
 	/**
@@ -542,16 +753,21 @@ class SmallMapWindow : public Window {
 	 */
 	FORCEINLINE Point RemapTile(int tile_x, int tile_y) const
 	{
-		int x_offset = tile_x - this->scroll_x / TILE_SIZE;
-		int y_offset = tile_y - this->scroll_y / TILE_SIZE;
+		if (this->zoom > 0) {
+			int x_offset = tile_x - this->scroll_x / TILE_SIZE;
+			int y_offset = tile_y - this->scroll_y / TILE_SIZE;
 
-		if (this->zoom == 1) return RemapCoords(x_offset, y_offset, 0);
+			/* For negative offsets, round towards -inf. */
+			if (x_offset < 0) x_offset -= this->zoom - 1;
+			if (y_offset < 0) y_offset -= this->zoom - 1;
 
-		/* For negative offsets, round towards -inf. */
-		if (x_offset < 0) x_offset -= this->zoom - 1;
-		if (y_offset < 0) y_offset -= this->zoom - 1;
+			return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
+		} else {
+			int x_offset = tile_x * (-this->zoom) - this->scroll_x * (-this->zoom) / TILE_SIZE;
+			int y_offset = tile_y * (-this->zoom) - this->scroll_y * (-this->zoom) / TILE_SIZE;
 
-		return RemapCoords(x_offset / this->zoom, y_offset / this->zoom, 0);
+			return RemapCoords(x_offset, y_offset, 0);
+		}
 	}
 
 	/**
@@ -564,21 +780,34 @@ class SmallMapWindow : public Window {
 	 * @return Tile being displayed at the given position relative to #scroll_x and #scroll_y.
 	 * @note The #subscroll offset is already accounted for.
 	 */
-	FORCEINLINE Point PixelToTile(int px, int py, int *sub, bool add_sub = true) const
+	FORCEINLINE Point PixelToWorld(int px, int py, int *sub, bool add_sub = true) const
 	{
 		if (add_sub) px += this->subscroll;  // Total horizontal offset.
 
 		/* For each two rows down, add a x and a y tile, and
 		 * For each four pixels to the right, move a tile to the right. */
-		Point pt = {((py >> 1) - (px >> 2)) * this->zoom, ((py >> 1) + (px >> 2)) * this->zoom};
+		Point pt = {
+			((py >> 1) - (px >> 2)) * TILE_SIZE,
+			((py >> 1) + (px >> 2)) * TILE_SIZE
+		};
+
+		if (this->zoom > 0) {
+			pt.x *= this->zoom;
+			pt.y *= this->zoom;
+		} else {
+			pt.x /= (-this->zoom);
+			pt.y /= (-this->zoom);
+		}
+
 		px &= 3;
 
 		if (py & 1) { // Odd number of rows, handle the 2 pixel shift.
+			int offset = this->zoom > 0 ? this->zoom * TILE_SIZE : TILE_SIZE / (-this->zoom);
 			if (px < 2) {
-				pt.x += this->zoom;
+				pt.x += offset;
 				px += 2;
 			} else {
-				pt.y += this->zoom;
+				pt.y += offset;
 				px -= 2;
 			}
 		}
@@ -601,19 +830,21 @@ class SmallMapWindow : public Window {
 		assert(x >= 0 && y >= 0);
 
 		int new_sub;
-		Point tile_xy = PixelToTile(x, y, &new_sub, false);
+		Point tile_xy = PixelToWorld(x, y, &new_sub, false);
 		tx -= tile_xy.x;
 		ty -= tile_xy.y;
 
+		int offset = this->zoom < 0 ? TILE_SIZE / (-this->zoom) : this->zoom * TILE_SIZE;
+
 		Point scroll;
 		if (new_sub == 0) {
 			*sub = 0;
-			scroll.x = (tx + this->zoom) * TILE_SIZE;
-			scroll.y = (ty - this->zoom) * TILE_SIZE;
+			scroll.x = tx + offset;
+			scroll.y = ty - offset;
 		} else {
 			*sub = 4 - new_sub;
-			scroll.x = (tx + 2 * this->zoom) * TILE_SIZE;
-			scroll.y = (ty - 2 * this->zoom) * TILE_SIZE;
+			scroll.x = tx + 2 * offset;
+			scroll.y = ty - 2 * offset;
 		}
 		return scroll;
 	}
@@ -625,16 +856,17 @@ class SmallMapWindow : public Window {
 	 */
 	void SetZoomLevel(ZoomLevelChange change, const Point *zoom_pt)
 	{
-		static const int zoomlevels[] = {1, 2, 4, 6, 8}; // Available zoom levels. Bigger number means more zoom-out (further away).
+		static const int zoomlevels[] = {-8, -4, -2, 1, 2, 4, 6, 8}; // Available zoom levels. Bigger number means more zoom-out (further away).
 		static const int MIN_ZOOM_INDEX = 0;
+		static const int DEFAULT_ZOOM_INDEX = 3;
 		static const int MAX_ZOOM_INDEX = lengthof(zoomlevels) - 1;
 
 		int new_index, cur_index, sub;
-		Point tile;
+		Point position;
 		switch (change) {
 			case ZLC_INITIALIZE:
 				cur_index = - 1; // Definitely different from new_index.
-				new_index = MIN_ZOOM_INDEX;
+				new_index = DEFAULT_ZOOM_INDEX;
 				break;
 
 			case ZLC_ZOOM_IN:
@@ -644,7 +876,7 @@ class SmallMapWindow : public Window {
 				}
 				assert(cur_index <= MAX_ZOOM_INDEX);
 
-				tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
+				position = this->PixelToWorld(zoom_pt->x, zoom_pt->y, &sub);
 				new_index = Clamp(cur_index + ((change == ZLC_ZOOM_IN) ? -1 : 1), MIN_ZOOM_INDEX, MAX_ZOOM_INDEX);
 				break;
 
@@ -654,9 +886,9 @@ class SmallMapWindow : public Window {
 		if (new_index != cur_index) {
 			this->zoom = zoomlevels[new_index];
 			if (cur_index >= 0) {
-				Point new_tile = this->PixelToTile(zoom_pt->x, zoom_pt->y, &sub);
-				this->SetNewScroll(this->scroll_x + (tile.x - new_tile.x) * TILE_SIZE,
-						this->scroll_y + (tile.y - new_tile.y) * TILE_SIZE, sub);
+				Point new_pos = this->PixelToWorld(zoom_pt->x, zoom_pt->y, &sub);
+				this->SetNewScroll(this->scroll_x + position.x - new_pos.x,
+						this->scroll_y + position.y - new_pos.y, sub);
 			}
 			this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN,  this->zoom == zoomlevels[MIN_ZOOM_INDEX]);
 			this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == zoomlevels[MAX_ZOOM_INDEX]);
@@ -703,6 +935,9 @@ class SmallMapWindow : public Window {
 			case SMT_OWNER:
 				return GetSmallMapOwnerPixels(tile, et);
 
+			case SMT_LINKSTATS:
+				return GetSmallMapLinkStatsPixels(tile, et);
+
 			default: NOT_REACHED();
 		}
 	}
@@ -726,9 +961,12 @@ class SmallMapWindow : public Window {
 		void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
 		uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
 
+		int increment = this->zoom > 0 ? this->zoom * TILE_SIZE : TILE_SIZE / (-this->zoom);
+		int extent = this->zoom > 0 ? this->zoom : 1;
+
 		do {
 			/* Check if the tile (xc,yc) is within the map range */
-			if (xc >= MapMaxX() || yc >= MapMaxY()) continue;
+			if (xc / TILE_SIZE >= MapMaxX() || yc / TILE_SIZE >= MapMaxY()) continue;
 
 			/* Check if the dst pointer points to a pixel inside the screen buffer */
 			if (dst < _screen.dst_ptr) continue;
@@ -736,12 +974,12 @@ class SmallMapWindow : public Window {
 
 			/* Construct tilearea covered by (xc, yc, xc + this->zoom, yc + this->zoom) such that it is within min_xy limits. */
 			TileArea ta;
-			if (min_xy == 1 && (xc == 0 || yc == 0)) {
-				if (this->zoom == 1) continue; // The tile area is empty, don't draw anything.
+			if (min_xy == 1 && (xc < TILE_SIZE || yc < TILE_SIZE)) {
+				if (this->zoom <= 1) continue; // The tile area is empty, don't draw anything.
 
-				ta = TileArea(TileXY(max(min_xy, xc), max(min_xy, yc)), this->zoom - (xc == 0), this->zoom - (yc == 0));
+				ta = TileArea(TileXY(max(min_xy, xc / TILE_SIZE), max(min_xy, yc / TILE_SIZE)), this->zoom - (xc < TILE_SIZE), this->zoom - (yc < TILE_SIZE));
 			} else {
-				ta = TileArea(TileXY(xc, yc), this->zoom, this->zoom);
+				ta = TileArea(TileXY(xc / TILE_SIZE, yc / TILE_SIZE), extent, extent);
 			}
 			ta.ClampToMap(); // Clamp to map boundaries (may contain MP_VOID tiles!).
 
@@ -753,7 +991,7 @@ class SmallMapWindow : public Window {
 				idx++;
 			}
 		/* Switch to next tile in the column */
-		} while (xc += this->zoom, yc += this->zoom, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
+		} while (xc += increment, yc += increment, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
 	}
 
 	/**
@@ -763,36 +1001,457 @@ class SmallMapWindow : public Window {
 	 */
 	void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
 	{
-		const Vehicle *v;
-		FOR_ALL_VEHICLES(v) {
-			if (v->type == VEH_EFFECT) continue;
-			if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
+		for(VehicleList::const_iterator i = this->vehicles_on_map.begin(); i != this->vehicles_on_map.end(); ++i) {
+			const Vehicle *v = Vehicle::GetIfValid((*i).vehicle);
+			if (v == NULL) continue;
+
+			TileIndex tile = (*i).tile;
 
 			/* Remap into flat coordinates. */
-			Point pt = this->RemapTile(v->x_pos / TILE_SIZE, v->y_pos / TILE_SIZE);
+			Point pt = this->RemapTile(TileX(tile), TileY(tile));
 
 			int y = pt.y - dpi->top;
-			if (!IsInsideMM(y, 0, dpi->height)) continue; // y is out of bounds.
-
-			bool skip = false; // Default is to draw both pixels.
 			int x = pt.x - this->subscroll - 3 - dpi->left; // Offset X coordinate.
-			if (x < 0) {
-				/* if x+1 is 0, that means we're on the very left edge,
-				 * and should thus only draw a single pixel */
-				if (++x != 0) continue;
-				skip = true;
-			} else if (x >= dpi->width - 1) {
-				/* Check if we're at the very right edge, and if so draw only a single pixel */
-				if (x != dpi->width - 1) continue;
-				skip = true;
-			}
+
+			int scale = this->zoom < 0 ? -this->zoom : 1;
 
 			/* Calculate pointer to pixel and the colour */
 			byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
 
-			/* And draw either one or two pixels depending on clipping */
-			blitter->SetPixel(dpi->dst_ptr, x, y, colour);
-			if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
+			/* Draw rhombus */
+			for (int dy = 0; dy < scale; dy++) {
+				for (int dx = 0; dx < scale; dx++) {
+					Point pt = RemapCoords(dx, dy, 0);
+					if (IsInsideMM(y + pt.y, 0, dpi->height)) {
+						if (IsInsideMM(x + pt.x, 0, dpi->width)) {
+							blitter->SetPixel(dpi->dst_ptr, x + pt.x, y + pt.y, colour);
+						}
+						if (IsInsideMM(x + pt.x + 1, 0, dpi->width)) {
+							blitter->SetPixel(dpi->dst_ptr, x + pt.x + 1, y + pt.y, colour);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	inline Point GetStationMiddle(const Station * st) const {
+		int x = (st->rect.right + st->rect.left + 1) / 2;
+		int y = (st->rect.bottom + st->rect.top + 1) / 2;
+		Point ret = this->RemapTile(x, y);
+		ret.x -= 3 + this->subscroll;
+		if (this->zoom < 0) {
+			/* add half a tile if width or height is odd */
+			if (((st->rect.bottom - st->rect.top) & 1) == 0) {
+				Point offset = RemapCoords(0, -this->zoom / 2, 0);
+				ret.x += offset.x;
+				ret.y += offset.y;
+			}
+			if (((st->rect.right - st->rect.left) & 1) == 0) {
+				Point offset = RemapCoords(-this->zoom / 2, 0, 0);
+				ret.x += offset.x;
+				ret.y += offset.y;
+			}
+		}
+		return ret;
+	}
+
+	StationID DrawStationDots() const {
+		const Station *supply_details = NULL;
+
+		const Station *st;
+		FOR_ALL_STATIONS(st) {
+			if (st->owner != _local_company && Company::IsValidID(st->owner)) continue;
+
+			Point pt = GetStationMiddle(st);
+
+			if (supply_details == NULL && CheckStationSelected(&pt)) {
+				supply_details = st;
+			}
+
+			/* Add up cargo supplied for each selected cargo type */
+			uint q = 0;
+			int colour = 0;
+			int numCargos = 0;
+			for (int i = 0; i < _smallmap_cargo_count; ++i) {
+				const LegendAndColour &tbl = _legend_table[this->map_type][i];
+				if (!tbl.show_on_map && supply_details != st) continue;
+				CargoID c = tbl.type;
+				const SupplyMovingAverage &supply = st->goods[c].supply;
+				if (!supply.IsNull()) {
+					q += supply.Value();
+					colour += tbl.colour;
+					numCargos++;
+				}
+			}
+			if (numCargos > 1)
+				colour /= numCargos;
+
+			uint r = 2;
+			if (q >= 10) r++;
+			if (q >= 20) r++;
+			if (q >= 40) r++;
+			if (q >= 80) r++;
+			if (q >= 160) r++;
+
+			DrawVertex(pt.x, pt.y, r, colour, _colour_gradient[COLOUR_GREY][supply_details == st ? 3 : 1]);
+		}
+		return (supply_details == NULL) ? INVALID_STATION : supply_details->index;
+	}
+
+	class LinkDrawer {
+
+	protected:
+		virtual void DrawContent() = 0;
+		virtual void Highlight() {}
+		virtual void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow, const LegendAndColour &cargo_entry) = 0;
+
+		Point pta, ptb;
+		bool search_link_details;
+		LinkDetails link_details;
+		const SmallMapWindow * window;
+
+		void DrawLink(StationID sta, StationID stb) {
+
+			this->pta = window->GetStationMiddle(Station::Get(sta));
+			this->ptb = window->GetStationMiddle(Station::Get(stb));
+
+			bool highlight_empty = this->search_link_details && this->link_details.Empty();
+			bool highlight =
+					(sta == this->link_details.sta && stb == this->link_details.stb) ||
+					(highlight_empty && window->CheckLinkSelected(&pta, &ptb));
+			bool reverse_empty = this->link_details.b_to_a.empty();
+			bool reverse_highlight = (sta == this->link_details.stb && stb == this->link_details.sta);
+			if (highlight_empty && highlight) {
+				this->link_details.sta = sta;
+				this->link_details.stb = stb;
+			}
+
+			if (highlight || reverse_highlight) {
+				this->Highlight();
+			}
+
+			for (int i = 0; i < _smallmap_cargo_count; ++i) {
+				const LegendAndColour &cargo_entry = _legend_table[window->map_type][i];
+				CargoID cargo = cargo_entry.type;
+				if (cargo_entry.show_on_map || highlight || reverse_highlight) {
+					GoodsEntry &ge = Station::Get(sta)->goods[cargo];
+					FlowStat sum_flows = ge.GetSumFlowVia(stb);
+					const LinkStatMap &ls_map = ge.link_stats;
+					LinkStatMap::const_iterator i = ls_map.find(stb);
+					if (i != ls_map.end()) {
+						const LinkStat &link_stat = i->second;
+						AddLink(link_stat, sum_flows, cargo_entry);
+						if (highlight_empty && highlight) {
+							this->link_details.a_to_b.push_back(CargoDetail(&cargo_entry, link_stat, sum_flows));
+						} else if (reverse_empty && reverse_highlight) {
+							this->link_details.b_to_a.push_back(CargoDetail(&cargo_entry, link_stat, sum_flows));
+						}
+					}
+				}
+			}
+		}
+
+		virtual void DrawForwBackLinks(StationID sta, StationID stb) {
+			DrawLink(sta, stb);
+			DrawContent();
+			DrawLink(stb, sta);
+			DrawContent();
+		}
+
+	public:
+		virtual ~LinkDrawer() {}
+
+		LinkDetails DrawLinks(const SmallMapWindow * w, bool search)
+		{
+			this->link_details.Clear();
+			this->window = w;
+			this->search_link_details = search;
+			std::set<StationID> seen_stations;
+			std::set<std::pair<StationID, StationID> > seen_links;
+
+			const Station * sta;
+			FOR_ALL_STATIONS(sta) {
+				if (sta->owner != _local_company && Company::IsValidID(sta->owner)) continue;
+				for (int i = 0; i < _smallmap_cargo_count; ++i) {
+					const LegendAndColour &tbl = _legend_table[window->map_type][i];
+					if (!tbl.show_on_map) continue;
+
+					CargoID c = tbl.type;
+					const LinkStatMap & links = sta->goods[c].link_stats;
+					for (LinkStatMap::const_iterator i = links.begin(); i != links.end(); ++i) {
+						StationID from = sta->index;
+						StationID to = i->first;
+						if (Station::IsValidID(to) && seen_stations.find(to) == seen_stations.end()) {
+							const Station *stb = Station::Get(to);
+
+							if (stb->owner != _local_company && Company::IsValidID(stb->owner)) continue;
+							if (seen_links.find(std::make_pair(to, from)) != seen_links.end()) continue;
+
+							DrawForwBackLinks(sta->index, stb->index);
+							seen_stations.insert(to);
+						}
+						seen_links.insert(std::make_pair(from, to));
+					}
+				}
+				seen_stations.clear();
+			}
+			return this->link_details;
+		}
+
+	};
+
+	class LinkLineDrawer : public LinkDrawer {
+	public:
+		LinkLineDrawer() : highlight(false) {}
+
+	protected:
+		typedef std::set<uint16> ColourSet;
+		ColourSet colours;
+		bool highlight;
+
+		virtual void DrawForwBackLinks(StationID sta, StationID stb) {
+			DrawLink(sta, stb);
+			DrawLink(stb, sta);
+			DrawContent();
+		}
+
+		virtual void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow, const LegendAndColour &cargo_entry) {
+			this->colours.insert(cargo_entry.colour);
+		}
+
+		virtual void Highlight() {
+			this->highlight = true;
+		}
+
+		virtual void DrawContent() {
+			uint colour = 0;
+			uint num_colours = 0;
+			for (ColourSet::iterator i = colours.begin(); i != colours.end(); ++i) {
+				colour += *i;
+				num_colours++;
+			}
+			colour /= num_colours;
+			byte border_colour = _colour_gradient[COLOUR_GREY][highlight ? 3 : 1];
+			GfxDrawLine(this->pta.x - 1, this->pta.y, this->ptb.x - 1, this->ptb.y, border_colour);
+			GfxDrawLine(this->pta.x + 1, this->pta.y, this->ptb.x + 1, this->ptb.y, border_colour);
+			GfxDrawLine(this->pta.x, this->pta.y - 1, this->ptb.x, this->ptb.y - 1, border_colour);
+			GfxDrawLine(this->pta.x, this->pta.y + 1, this->ptb.x, this->ptb.y + 1, border_colour);
+			GfxDrawLine(this->pta.x, this->pta.y, this->ptb.x, this->ptb.y, colour);
+			this->colours.clear();
+			this->highlight = false;
+		}
+	};
+
+	class LinkValueDrawer : public LinkDrawer, public BaseCargoDetail {
+	protected:
+
+		virtual void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow, const LegendAndColour &cargo_entry)
+		{
+			this->BaseCargoDetail::AddLink(orig_link, orig_flow);
+		}
+	};
+
+	class LinkTextDrawer : public LinkValueDrawer {
+	protected:
+		virtual void DrawContent() {
+			Point ptm;
+			ptm.x = (this->pta.x + 2*this->ptb.x) / 3;
+			ptm.y = (this->pta.y + 2*this->ptb.y) / 3;
+			int nums = 0;
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_CAPACITY].show_on_map) {
+				SetDParam(nums++, this->capacity);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_USAGE].show_on_map) {
+				SetDParam(nums++, this->usage);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_PLANNED].show_on_map) {
+				SetDParam(nums++, this->planned);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_SENT].show_on_map) {
+				SetDParam(nums++, this->sent);
+			}
+			StringID str;
+			switch (nums) {
+			case 0:
+				str = STR_EMPTY; break;
+			case 1:
+				str = STR_NUM; break;
+			case 2:
+				str = STR_NUM_RELATION_2; break;
+			case 3:
+				str = STR_NUM_RELATION_3; break;
+			case 4:
+				str = STR_NUM_RELATION_4; break;
+			default:
+				NOT_REACHED();
+			}
+			DrawString(ptm.x, ptm.x + this->window->ColumnWidth(), ptm.y, str , TC_BLACK);
+			this->Clear();
+		}
+	};
+
+	class LinkGraphDrawer : public LinkValueDrawer {
+		typedef std::multimap<uint, byte, std::greater<uint> > SizeMap;
+	protected:
+		virtual void DrawContent() {
+			Point ptm;
+			SizeMap sizes;
+			/* these floats only serve to calculate the size of the coloured boxes for capacity, usage, planned, sent
+			 * they are not reused anywhere, so it's network safe.
+			 */
+			const LegendAndColour *legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_USAGE;
+			if (legend_entry->show_on_map && this->usage > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->usage), legend_entry->colour));
+			}
+			legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_CAPACITY;
+			if (legend_entry->show_on_map && this->capacity > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->capacity), legend_entry->colour));
+			}
+			legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_PLANNED;
+			if (legend_entry->show_on_map && this->planned > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->planned),  legend_entry->colour));
+			}
+			legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_SENT;
+			if (legend_entry->show_on_map && this->sent > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->sent), legend_entry->colour));
+			}
+
+			ptm.x = (this->pta.x + this->ptb.x) / 2;
+			ptm.y = (this->pta.y + this->ptb.y) / 2;
+
+			for (SizeMap::iterator i = sizes.begin(); i != sizes.end(); ++i) {
+				if (this->pta.x > this->ptb.x) {
+					ptm.x -= 1;
+					GfxFillRect(ptm.x - i->first / 2, ptm.y - i->first * 2, ptm.x, ptm.y, i->second);
+				} else {
+					ptm.x += 1;
+					GfxFillRect(ptm.x, ptm.y - i->first * 2, ptm.x + i->first / 2, ptm.y, i->second);
+				}
+			}
+			this->Clear();
+		}
+	};
+
+	static const uint MORE_SPACE_NEEDED = 0x1000;
+
+	uint DrawLinkDetails(StatVector &details, uint x, uint y, uint right, uint bottom) const {
+		uint x_orig = x;
+		SetDParam(0, 9999);
+		static uint entry_width = LEGEND_BLOB_WIDTH +
+				GetStringBoundingBox(STR_ABBREV_PASSENGERS).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_CAPACITY).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_USAGE).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_PLANNED).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_SENT).width;
+		uint entries_per_row = (right - x_orig) / entry_width;
+		if (details.empty()) {
+			DrawString(x, x + entry_width, y, STR_TINY_NOTHING, TC_BLACK);
+			return y + FONT_HEIGHT_SMALL;
+		}
+		for (uint i = 0; i < details.size(); ++i) {
+			CargoDetail &detail = details[i];
+			if (x + entry_width >= right) {
+				x = x_orig;
+				y += FONT_HEIGHT_SMALL;
+				if (y + 2 * FONT_HEIGHT_SMALL > bottom && details.size() - i > entries_per_row) {
+					return y | MORE_SPACE_NEEDED;
+				}
+			}
+			uint x_next = x + entry_width;
+			if (detail.legend->show_on_map) {
+				GfxFillRect(x, y + 1, x + LEGEND_BLOB_WIDTH, y + FONT_HEIGHT_SMALL - 1, 0); // outer border of the legend colour
+			}
+			GfxFillRect(x + 1, y + 2, x + LEGEND_BLOB_WIDTH - 1, y + FONT_HEIGHT_SMALL - 2, detail.legend->colour); // legend colour
+			x += LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
+			TextColour textcol[4];
+			for (int stat = STAT_CAPACITY; stat <= STAT_SENT; ++stat) {
+				textcol[stat] = (detail.legend->show_on_map && _legend_linkstats[_smallmap_cargo_count + stat].show_on_map) ?
+						TC_BLACK : TC_GREY;
+			}
+
+			SetDParam(0, STR_ABBREV_PASSENGERS + detail.legend->type);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK, detail.legend->show_on_map ? TC_BLACK : TC_GREY);
+			SetDParam(0, detail.capacity);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_CAPACITY, textcol[STAT_CAPACITY]);
+			SetDParam(0, detail.usage);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_USAGE, textcol[STAT_USAGE]);
+			SetDParam(0, detail.planned);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_PLANNED, textcol[STAT_PLANNED]);
+			SetDParam(0, detail.sent);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_SENT, textcol[STAT_SENT]);
+			x = x_next;
+		}
+		return y + FONT_HEIGHT_SMALL;
+	}
+
+	uint DrawLinkDetailCaption(uint x, uint y, uint right, StationID sta, StationID stb) const {
+		SetDParam(0, sta);
+		SetDParam(1, stb);
+		static uint height = GetStringBoundingBox(STR_SMALLMAP_LINK_CAPTION).height;
+		DrawString(x, right - 1, y, STR_SMALLMAP_LINK_CAPTION, TC_BLACK);
+		y += height;
+		return y;
+	}
+
+	void DrawLinkDetails(uint x, uint y, uint right, uint bottom) const {
+		y = DrawLinkDetailCaption(x, y, right, this->link_details.sta, this->link_details.stb);
+		if (y + 2 * FONT_HEIGHT_SMALL > bottom) {
+			DrawString(x, right, y, "...", TC_BLACK);
+			return;
+		}
+		y = DrawLinkDetails(this->link_details.a_to_b, x, y, right, bottom);
+		if (y + 3 * FONT_HEIGHT_SMALL > bottom) {
+			/* caption takes more space -> 3 * row height */
+			DrawString(x, right, y, "...", TC_BLACK);
+			return;
+		}
+		y = DrawLinkDetailCaption(x, y + 2, right, this->link_details.stb, this->link_details.sta);
+		if (y + 2 * FONT_HEIGHT_SMALL > bottom) {
+			DrawString(x, right, y, "...", TC_BLACK);
+			return;
+		}
+		y = DrawLinkDetails(this->link_details.b_to_a, x, y, right, bottom);
+		if (y & MORE_SPACE_NEEDED) {
+			/* only draw "..." if more entries would have been drawn */
+			DrawString(x, right, y ^ MORE_SPACE_NEEDED, "...", TC_BLACK);
+			return;
+		}
+	}
+
+	void DrawSupplyDetails(uint x, uint y_org, uint bottom) const {
+		const Station *st = Station::GetIfValid(this->supply_details);
+		if (st == NULL) return;
+		SetDParam(0, this->supply_details);
+		static uint height = GetStringBoundingBox(STR_SMALLMAP_SUPPLY_CAPTION).height;
+		DrawString(x, x + 2 * this->column_width - 1, y_org, STR_SMALLMAP_SUPPLY_CAPTION, TC_BLACK);
+		y_org += height;
+		uint y = y_org;
+		for (int i = 0; i < _smallmap_cargo_count; ++i) {
+			if (y + FONT_HEIGHT_SMALL - 1 >= bottom) {
+				/* Column break needed, continue at top, SD_LEGEND_COLUMN_WIDTH pixels
+				 * (one "row") to the right. */
+				x += this->column_width;
+				y = y_org;
+			}
+
+			const LegendAndColour &tbl = _legend_table[this->map_type][i];
+
+			CargoID c = tbl.type;
+			int supply = st->goods[c].supply.Value();
+			if (supply > 0) {
+				TextColour textcol = TC_BLACK;
+				if (tbl.show_on_map) {
+					GfxFillRect(x, y + 1, x + LEGEND_BLOB_WIDTH, y + FONT_HEIGHT_SMALL - 1, 0); // outer border of the legend colour
+				} else {
+					textcol = TC_GREY;
+				}
+				SetDParam(0, c);
+				SetDParam(1, supply);
+				DrawString(x + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT, x + this->column_width - 1, y, STR_SMALLMAP_SUPPLY, textcol);
+				GfxFillRect(x + 1, y + 2, x + LEGEND_BLOB_WIDTH - 1, y + FONT_HEIGHT_SMALL - 2, tbl.colour); // legend colour
+				y += FONT_HEIGHT_SMALL;
+			}
 		}
 	}
 
@@ -908,13 +1567,14 @@ class SmallMapWindow : public Window {
 
 		/* Which tile is displayed at (dpi->left, dpi->top)? */
 		int dx;
-		Point tile = this->PixelToTile(dpi->left, dpi->top, &dx);
-		int tile_x = this->scroll_x / TILE_SIZE + tile.x;
-		int tile_y = this->scroll_y / TILE_SIZE + tile.y;
+		Point position = this->PixelToWorld(dpi->left, dpi->top, &dx);
+		int pos_x = this->scroll_x + position.x;
+		int pos_y = this->scroll_y + position.y;
 
 		void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
 		int x = - dx - 4;
 		int y = 0;
+		int increment = this->zoom > 0 ? this->zoom * TILE_SIZE : TILE_SIZE / (-this->zoom); 
 
 		for (;;) {
 			/* Distance from left edge */
@@ -924,16 +1584,16 @@ class SmallMapWindow : public Window {
 				int end_pos = min(dpi->width, x + 4);
 				int reps = (dpi->height - y + 1) / 2; // Number of lines.
 				if (reps > 0) {
-					this->DrawSmallMapColumn(ptr, tile_x, tile_y, dpi->pitch * 2, reps, x, end_pos, blitter);
+					this->DrawSmallMapColumn(ptr, pos_x, pos_y, dpi->pitch * 2, reps, x, end_pos, blitter);
 				}
 			}
 
 			if (y == 0) {
-				tile_y += this->zoom;
+				pos_y += increment;
 				y++;
 				ptr = blitter->MoveTo(ptr, 0, 1);
 			} else {
-				tile_x -= this->zoom;
+				pos_x -= increment;
 				y--;
 				ptr = blitter->MoveTo(ptr, 0, -1);
 			}
@@ -944,6 +1604,22 @@ class SmallMapWindow : public Window {
 		/* Draw vehicles */
 		if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
 
+		if (this->map_type == SMT_LINKSTATS && _game_mode == GM_NORMAL) {
+			LinkLineDrawer lines;
+			this->link_details = lines.DrawLinks(this, true);
+
+			this->supply_details = DrawStationDots();
+
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_TEXT].show_on_map) {
+				LinkTextDrawer text;
+				text.DrawLinks(this, false);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_GRAPH].show_on_map) {
+				LinkGraphDrawer graph;
+				graph.DrawLinks(this, false);
+			}
+		}
+
 		/* Draw town names */
 		if (this->show_towns) this->DrawTowns(dpi);
 
@@ -953,18 +1629,78 @@ class SmallMapWindow : public Window {
 		_cur_dpi = old_dpi;
 	}
 
+	bool CheckStationSelected(Point *pt) const {
+		return abs(this->cursor.x - pt->x) < 7 && abs(this->cursor.y - pt->y) < 7;
+	}
+
+	bool CheckLinkSelected(Point * pta, Point * ptb) const {
+		if (this->cursor.x == -1 && this->cursor.y == -1) return false;
+		if (CheckStationSelected(pta) || CheckStationSelected(ptb)) return false;
+		if (pta->x > ptb->x) Swap(pta, ptb);
+		int minx = min(pta->x, ptb->x);
+		int maxx = max(pta->x, ptb->x);
+		int miny = min(pta->y, ptb->y);
+		int maxy = max(pta->y, ptb->y);
+		if (!IsInsideMM(cursor.x, minx - 3, maxx + 3) || !IsInsideMM(cursor.y, miny - 3, maxy + 3)) {
+			return false;
+		}
+
+		if (pta->x == ptb->x || ptb->y == pta->y) {
+			return true;
+		} else {
+			int incliney = (ptb->y - pta->y);
+			int inclinex = (ptb->x - pta->x);
+			int diff = (cursor.x - minx) * incliney / inclinex - (cursor.y - miny);
+			if (incliney < 0) {
+				diff += maxy - miny;
+			}
+			return abs(diff) < 4;
+		}
+	}
+
+	void RecalcVehiclePositions() {
+		this->vehicles_on_map.clear();
+		const Vehicle *v;
+		const NWidgetCore *wi = this->GetWidget<NWidgetCore>(SM_WIDGET_MAP);
+		int scale = this->zoom < 0 ? -this->zoom : 1;
+
+		FOR_ALL_VEHICLES(v) {
+			if (v->type == VEH_EFFECT) continue;
+			if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
+
+			/* Remap into flat coordinates. */
+			Point pos = RemapTile(TileX(v->tile), TileY(v->tile));
+
+			/* Check if rhombus is inside bounds */
+			if (IsInsideMM(pos.x, -2 * scale, wi->current_x + 2 * scale) &&
+				IsInsideMM(pos.y, -2 * scale, wi->current_y + 2 * scale)) {
+
+				this->vehicles_on_map.push_back(VehicleAndPosition(v));
+			}
+		}
+	}
+
 public:
-	SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
+	SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), supply_details(INVALID_STATION), refresh(FORCE_REFRESH_PERIOD)
 	{
+		this->cursor.x = -1;
+		this->cursor.y = -1;
 		this->InitNested(desc, window_number);
+		if (_smallmap_cargo_count == 0) {
+			this->DisableWidget(SM_WIDGET_LINKSTATS);
+			if (this->map_type == SMT_LINKSTATS) {
+				this->map_type = SMT_CONTOUR;
+			}
+		}
+
 		this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
 
-		_smallmap_industry_show_heightmap = false;
+		_smallmap_show_heightmap = (this->map_type != SMT_INDUSTRY);
 		BuildLandLegend();
-		this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
+		this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
 
 		this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
-		this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
+		this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY && this->map_type != SMT_LINKSTATS);
 
 		this->SetZoomLevel(ZLC_INITIALIZE, NULL);
 		this->SmallMapCenterOnCurrentPos();
@@ -975,8 +1711,7 @@ public:
 	 */
 	inline uint GetMaxLegendHeight() const
 	{
-		uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + this->min_number_of_columns - 1) / this->min_number_of_columns);
-		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
+		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + this->GetMaxNumberRowsLegend(this->min_number_of_columns) * FONT_HEIGHT_SMALL;
 	}
 
 	/** Compute minimal required width of the legends.
@@ -1001,8 +1736,7 @@ public:
 	uint GetLegendHeight(uint width) const
 	{
 		uint num_columns = this->GetNumberColumnsLegend(width);
-		uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + num_columns - 1) / num_columns);
-		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
+		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + this->GetMaxNumberRowsLegend(num_columns) * FONT_HEIGHT_SMALL;
 	}
 
 	virtual void SetStringParameters(int widget) const
@@ -1024,10 +1758,10 @@ public:
 			uint num_columns = 1;
 			for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
 				StringID str;
-				if (i == SMT_INDUSTRY) {
+				if (i == SMT_INDUSTRY || i == SMT_LINKSTATS) {
 					SetDParam(0, tbl->legend);
 					SetDParam(1, IndustryPool::MAX_SIZE);
-					str = STR_SMALLMAP_INDUSTRY;
+					str = (i == SMT_INDUSTRY) ? STR_SMALLMAP_INDUSTRY : STR_SMALLMAP_LINKSTATS_LEGEND;
 				} else {
 					if (tbl->col_break) {
 						this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
@@ -1057,56 +1791,94 @@ public:
 			} break;
 
 			case SM_WIDGET_LEGEND: {
-				uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
-				uint number_of_rows = max(this->map_type == SMT_INDUSTRY ? (_smallmap_industry_count + columns - 1) / columns : 0, this->min_number_of_fixed_rows);
-				bool rtl = _dynlang.text_dir == TD_RTL;
-				uint y_org = r.top + WD_FRAMERECT_TOP;
-				uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
-				uint y = y_org;
-				uint i = 0; // Row counter for industry legend.
-				uint row_height = FONT_HEIGHT_SMALL;
-
-				uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
-				uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
-				uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
-				uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
-
-				for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
-					if (tbl->col_break || (this->map_type == SMT_INDUSTRY && i++ >= number_of_rows)) {
-						/* Column break needed, continue at top, COLUMN_WIDTH pixels
-						 * (one "row") to the right. */
-						x += rtl ? -(int)this->column_width : this->column_width;
-						y = y_org;
-						i = 1;
-					}
+				DrawLegend(r);
+			} break;
+		}
+	}
 
-					if (this->map_type == SMT_INDUSTRY) {
+	uint GetNumberRowsLegend(uint columns) const {
+		uint number_of_rows = this->min_number_of_fixed_rows;
+		if (this->map_type == SMT_INDUSTRY) {
+			number_of_rows = max(number_of_rows, (_smallmap_industry_count + columns - 1) / columns);
+		} else if (this->map_type == SMT_LINKSTATS) {
+			number_of_rows = max(number_of_rows, (_smallmap_cargo_count + columns - 2) / (columns - 1));
+		}
+		return number_of_rows;
+	}
+
+	uint GetMaxNumberRowsLegend(uint columns) const {
+		uint number_of_rows = this->min_number_of_fixed_rows;
+		number_of_rows = max(number_of_rows, (_smallmap_industry_count + columns - 1) / columns);
+		number_of_rows = max(number_of_rows, (_smallmap_cargo_count + columns - 2) / (columns - 1));
+		return number_of_rows;
+	}
+
+	void DrawLegend(const Rect &r) const {
+		uint y_org = r.top + WD_FRAMERECT_TOP;
+		uint x = r.left + WD_FRAMERECT_LEFT;
+		if (this->supply_details != INVALID_STATION) {
+			this->DrawSupplyDetails(x, y_org, r.bottom - WD_FRAMERECT_BOTTOM);
+		} else if (!link_details.Empty()) {
+			this->DrawLinkDetails(x, y_org, r.right - WD_FRAMERECT_RIGHT, r.bottom - WD_FRAMERECT_BOTTOM);
+		} else {
+			uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
+
+			uint number_of_rows = this->GetNumberRowsLegend(columns);
+
+			bool rtl = _dynlang.text_dir == TD_RTL;
+			uint y_org = r.top + WD_FRAMERECT_TOP;
+			uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
+			uint y = y_org;
+			uint i = 0; // Row counter for industry legend.
+			uint row_height = FONT_HEIGHT_SMALL;
+
+			uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
+			uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
+			uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
+			uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
+
+			StringID string = (this->map_type == SMT_INDUSTRY) ? STR_SMALLMAP_INDUSTRY : STR_SMALLMAP_LINKSTATS_LEGEND;
+
+			for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
+				if (tbl->col_break || ((this->map_type == SMT_INDUSTRY || this->map_type == SMT_LINKSTATS) && i++ >= number_of_rows)) {
+					/* Column break needed, continue at top, COLUMN_WIDTH pixels
+					 * (one "row") to the right. */
+					x += rtl ? -(int)this->column_width : this->column_width;
+					y = y_org;
+					i = 1;
+				}
+
+				switch(this->map_type) {
+					case SMT_INDUSTRY:
 						/* Industry name must be formatted, since it's not in tiny font in the specs.
 						 * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
-						SetDParam(0, tbl->legend);
 						assert(tbl->type < NUM_INDUSTRYTYPES);
 						SetDParam(1, _industry_counts[tbl->type]);
+					case SMT_LINKSTATS:
+						SetDParam(0, tbl->legend);
 						if (!tbl->show_on_map) {
 							/* Simply draw the string, not the black border of the legend colour.
 							 * This will enforce the idea of the disabled item */
-							DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
+							DrawString(x + text_left, x + text_right, y, string, TC_GREY);
 						} else {
-							DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
-							GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
+							DrawString(x + text_left, x + text_right, y, string, TC_BLACK);
+							GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // outer border of the legend colour
 						}
-					} else {
+						break;
+					default:
 						/* Anything that is not an industry is using normal process */
 						GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
 						DrawString(x + text_left, x + text_right, y, tbl->legend);
-					}
-					GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
-
-					y += row_height;
 				}
+				GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
+
+				y += row_height;
 			}
 		}
 	}
 
+
+
 	virtual void OnPaint()
 	{
 		this->DrawWidgets();
@@ -1129,9 +1901,10 @@ public:
 				const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
 				Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
 				int sub;
-				pt = this->PixelToTile(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
-				pt = RemapCoords(this->scroll_x + pt.x * TILE_SIZE + this->zoom * (TILE_SIZE - sub * TILE_SIZE / 4),
-						this->scroll_y + pt.y * TILE_SIZE + sub * this->zoom * TILE_SIZE / 4, 0);
+				pt = this->PixelToWorld(pt.x - wid->pos_x, pt.y - wid->pos_y, &sub);
+				int offset = this->zoom > 0 ? this->zoom * TILE_SIZE : TILE_SIZE / (-this->zoom);
+				pt = RemapCoords(this->scroll_x + pt.x + offset - offset * sub / 4,
+						this->scroll_y + pt.y + sub * offset / 4, 0);
 
 				w->viewport->follow_vehicle = INVALID_VEHICLE;
 				w->viewport->dest_scrollpos_x = pt.x - (w->viewport->virtual_width  >> 1);
@@ -1152,6 +1925,7 @@ public:
 			case SM_WIDGET_CONTOUR:    // Show land contours
 			case SM_WIDGET_VEHICLES:   // Show vehicles
 			case SM_WIDGET_INDUSTRIES: // Show industries
+			case SM_WIDGET_LINKSTATS:   // Show route map
 			case SM_WIDGET_ROUTES:     // Show transport routes
 			case SM_WIDGET_VEGETATION: // Show vegetation
 			case SM_WIDGET_OWNERS:     // Show land owners
@@ -1160,7 +1934,7 @@ public:
 				this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
 
 				/* Hide Enable all/Disable all buttons if is not industry type small map */
-				this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
+				this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY && this->map_type != SMT_LINKSTATS);
 
 				this->SetDirty();
 				SndPlayFx(SND_15_BEEP);
@@ -1182,12 +1956,13 @@ public:
 
 			case SM_WIDGET_LEGEND: // Legend
 				/* If industry type small map*/
-				if (this->map_type == SMT_INDUSTRY) {
+				if (this->map_type == SMT_INDUSTRY || this->map_type == SMT_LINKSTATS) {
 					/* If click on industries label, find right industry type and enable/disable it */
 					const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
 					uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
 					uint columns = this->GetNumberColumnsLegend(wi->current_x);
-					uint number_of_rows = max((_smallmap_industry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
+					uint entry_count = (this->map_type == SMT_INDUSTRY) ? _smallmap_industry_count : _smallmap_cargo_count;
+					uint number_of_rows = max((entry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
 					if (line >= number_of_rows) break;
 
 					bool rtl = _dynlang.text_dir == TD_RTL;
@@ -1196,46 +1971,79 @@ public:
 					uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
 
 					/* Check if click is on industry label*/
-					int industry_pos = (column * number_of_rows) + line;
-					if (industry_pos < _smallmap_industry_count) {
-						_legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
+					int click_pos = (column * number_of_rows) + line;
+					if (this->map_type == SMT_INDUSTRY) {
+						if (click_pos < _smallmap_industry_count) {
+							_legend_from_industries[click_pos].show_on_map = !_legend_from_industries[click_pos].show_on_map;
+						}
+					} else if (this->map_type == SMT_LINKSTATS) {
+						if (click_pos < _smallmap_cargo_count) {
+							_legend_linkstats[click_pos].show_on_map = !_legend_linkstats[click_pos].show_on_map;
+						} else {
+							uint stats_column = _smallmap_cargo_count / number_of_rows;
+							if (_smallmap_cargo_count % number_of_rows != 0) stats_column++;
+
+							if (column == stats_column && line < NUM_STATS) {
+								click_pos = _smallmap_cargo_count + line;
+								_legend_linkstats[click_pos].show_on_map = !_legend_linkstats[click_pos].show_on_map;
+							}
+						}
 					}
 
 					/* Raise the two buttons "all", as we have done a specific choice */
-					this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
-					this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
+					this->RaiseWidget(SM_WIDGET_ENABLE_ALL);
+					this->RaiseWidget(SM_WIDGET_DISABLE_ALL);
 					this->SetDirty();
 				}
 				break;
 
-			case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
-				for (int i = 0; i != _smallmap_industry_count; i++) {
-					_legend_from_industries[i].show_on_map = true;
+			case SM_WIDGET_ENABLE_ALL: { // Enable all items
+				LegendAndColour *tbl = (this->map_type == SMT_INDUSTRY) ? _legend_from_industries : _legend_linkstats;
+				for (; !tbl->end; ++tbl) {
+					tbl->show_on_map = true;
 				}
 				/* Toggle appeareance indicating the choice */
-				this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
-				this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
+				this->LowerWidget(SM_WIDGET_ENABLE_ALL);
+				this->RaiseWidget(SM_WIDGET_DISABLE_ALL);
 				this->SetDirty();
 				break;
+			}
 
-			case SM_WIDGET_DISABLEINDUSTRIES: // Disable all industries
-				for (int i = 0; i != _smallmap_industry_count; i++) {
-					_legend_from_industries[i].show_on_map = false;
+			case SM_WIDGET_DISABLE_ALL: { // Disable all items
+				LegendAndColour *tbl = (this->map_type == SMT_INDUSTRY) ? _legend_from_industries : _legend_linkstats;
+				for (; !tbl->end; ++tbl) {
+					tbl->show_on_map = false;
 				}
 				/* Toggle appeareance indicating the choice */
-				this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
-				this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
+				this->RaiseWidget(SM_WIDGET_ENABLE_ALL);
+				this->LowerWidget(SM_WIDGET_DISABLE_ALL);
 				this->SetDirty();
 				break;
+			}
 
 			case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
-				_smallmap_industry_show_heightmap = !_smallmap_industry_show_heightmap;
-				this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
+				_smallmap_show_heightmap = !_smallmap_show_heightmap;
+				this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
 				this->SetDirty();
 				break;
 		}
 	}
 
+	virtual void OnMouseOver(Point pt, int widget) {
+		static Point invalid = {-1, -1};
+		if (pt.x != cursor.x || pt.y != cursor.y) {
+			this->refresh = 1;
+			if (widget == SM_WIDGET_MAP) {
+				cursor = pt;
+				cursor.x -= WD_FRAMERECT_LEFT;
+				cursor.y -= WD_FRAMERECT_TOP + WD_CAPTION_HEIGHT;
+			} else {
+				cursor = invalid;
+			}
+		}
+	}
+
+
 	virtual void OnRightClick(Point pt, int widget)
 	{
 		if (widget == SM_WIDGET_MAP) {
@@ -1260,6 +2068,8 @@ public:
 		/* Update the window every now and then */
 		if (--this->refresh != 0) return;
 
+		this->RecalcVehiclePositions();
+
 		this->refresh = FORCE_REFRESH_PERIOD;
 		this->SetDirty();
 	}
@@ -1275,8 +2085,13 @@ public:
 	{
 		const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
 		Point hv = InverseRemapCoords(wi->current_x * TILE_SIZE / 2, wi->current_y * TILE_SIZE / 2);
-		hv.x *= this->zoom;
-		hv.y *= this->zoom;
+		if (this->zoom > 0) {
+			hv.x *= this->zoom;
+			hv.y *= this->zoom;
+		} else {
+			hv.x /= (-this->zoom);
+			hv.y /= (-this->zoom);
+		}
 
 		if (sx < -hv.x) {
 			sx = -hv.x;
@@ -1306,8 +2121,8 @@ public:
 
 		/* While tile is at (delta.x, delta.y)? */
 		int sub;
-		Point pt = this->PixelToTile(delta.x, delta.y, &sub);
-		this->SetNewScroll(this->scroll_x + pt.x * TILE_SIZE, this->scroll_y + pt.y * TILE_SIZE, sub);
+		Point pt = this->PixelToWorld(delta.x, delta.y, &sub);
+		this->SetNewScroll(this->scroll_x + pt.x, this->scroll_y + pt.y, sub);
 
 		this->SetDirty();
 	}
@@ -1319,10 +2134,12 @@ public:
 
 		int sub;
 		const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
-		Point sxy = this->ComputeScroll(pt.x / TILE_SIZE, pt.y / TILE_SIZE, max(0, (int)wid->current_x / 2 - 2), wid->current_y / 2, &sub);
+		Point sxy = this->ComputeScroll(pt.x, pt.y, max(0, (int)wid->current_x / 2 - 2), wid->current_y / 2, &sub);
 		this->SetNewScroll(sxy.x, sxy.y, sub);
 		this->SetDirty();
 	}
+
+	uint ColumnWidth() const {return column_width;}
 };
 
 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
@@ -1418,6 +2235,7 @@ static const NWidgetPart _nested_smallmap_bar[] = {
 				NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 					NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN),
 					NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
+					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_BLANK), SetMinimalSize(22, 22), SetDataTip(SPR_DOT_SMALL, STR_NULL),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
@@ -1426,6 +2244,7 @@ static const NWidgetPart _nested_smallmap_bar[] = {
 				NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 					NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
+					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_LINKSTATS), SetDataTip(SPR_IMG_GRAPHS, STR_SMALLMAP_TOOLTIP_SHOW_LINK_STATS_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
@@ -1460,8 +2279,8 @@ static const NWidgetPart _nested_smallmap_widgets[] = {
 			NWidget(NWID_HORIZONTAL),
 				NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECTINDUSTRIES),
 					NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
-						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
-						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
+						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
+						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
 						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
 					EndContainer(),
 					NWidget(NWID_SPACER), SetFill(1, 1),
@@ -1474,7 +2293,7 @@ static const NWidgetPart _nested_smallmap_widgets[] = {
 };
 
 static const WindowDesc _smallmap_desc(
-	WDP_AUTO, 446, 314,
+	WDP_AUTO, 488, 314,
 	WC_SMALLMAP, WC_NONE,
 	WDF_UNCLICK_BUTTONS,
 	_nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
diff --git a/src/station.cpp b/src/station.cpp
index 039be31..d47e4b1 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -70,6 +70,16 @@ Station::~Station()
 		if (a->targetairport == this->index) a->targetairport = INVALID_STATION;
 	}
 
+	Station * st;
+	FOR_ALL_STATIONS(st) {
+		for (CargoID c = CT_BEGIN; c != CT_END; ++c) {
+			GoodsEntry & ge = st->goods[c];
+			ge.link_stats.erase(this->index);
+			DeleteStaleFlows(st->index, c, this->index);
+			ge.cargo.RerouteStalePackets(this->index, this->index, &ge);
+		}
+	}
+
 	Vehicle *v;
 	FOR_ALL_VEHICLES(v) {
 		/* Forget about this station if this station is removed */
diff --git a/src/station_base.h b/src/station_base.h
index e3aa8b7..e962a5e 100644
--- a/src/station_base.h
+++ b/src/station_base.h
@@ -17,12 +17,216 @@
 #include "newgrf_airport.h"
 #include "cargopacket.h"
 #include "industry_type.h"
+#include "linkgraph/linkgraph_type.h"
+#include "moving_average.h"
+#include <list>
+#include <map>
+#include <set>
 
 typedef Pool<BaseStation, StationID, 32, 64000> StationPool;
 extern StationPool _station_pool;
 
 static const byte INITIAL_STATION_RATING = 175;
 
+class LinkStat : private MovingAverage<uint> {
+private:
+	/**
+	 * capacity of the link.
+	 * This is a moving average use MovingAverage::Monthly() to get a meaningful value 
+	 */
+	uint capacity;
+	
+	/**
+	 * capacity of currently loading vehicles
+	 */
+	uint frozen;
+	
+	/**
+	 * usage of the link.
+	 * This is a moving average use MovingAverage::Monthly() to get a meaningful value 
+	 */
+	uint usage;
+
+public:
+	friend const SaveLoad *GetLinkStatDesc();
+
+	FORCEINLINE LinkStat(uint distance = 1, uint capacity = 0, uint frozen = 0, uint usage = 0) :
+		MovingAverage<uint>(distance), capacity(capacity), frozen(frozen), usage(usage) {}
+
+	FORCEINLINE void Clear()
+	{
+		this->capacity = 0;
+		this->usage = 0;
+		this->frozen = 0;
+	}
+
+	FORCEINLINE void Decrease()
+	{
+		this->MovingAverage<uint>::Decrease(this->usage);
+		this->capacity = max(this->MovingAverage<uint>::Decrease(this->capacity), this->frozen);
+	}
+
+	FORCEINLINE uint Capacity() const
+	{
+		return this->MovingAverage<uint>::Monthly(this->capacity);
+	}
+
+	FORCEINLINE uint Usage() const
+	{
+		return this->MovingAverage<uint>::Monthly(this->usage);
+	}
+
+	FORCEINLINE uint Frozen() const
+	{
+		return this->frozen;
+	}
+
+	FORCEINLINE void Increase(uint capacity, uint usage)
+	{
+		this->capacity += capacity;
+		this->usage += usage;
+	}
+
+	FORCEINLINE void Freeze(uint capacity)
+	{
+		this->frozen += capacity;
+		this->capacity = max(this->frozen, this->capacity);
+	}
+
+	FORCEINLINE void Unfreeze(uint capacity)
+	{
+		this->frozen -= capacity;
+	}
+
+	FORCEINLINE void Unfreeze()
+	{
+		this->frozen = 0;
+	}
+
+	FORCEINLINE bool IsNull() const
+	{
+		return this->capacity == 0;
+	}
+};
+
+class FlowStat : private MovingAverage<uint> {
+private:
+	uint planned;
+	uint sent;
+	StationID via;
+
+public:
+	friend const SaveLoad *GetFlowStatDesc();
+
+	FORCEINLINE FlowStat(uint distance = 1, StationID st = INVALID_STATION, uint p = 0, uint s = 0) :
+		MovingAverage<uint>(distance), planned(p), sent(s), via(st) {}
+
+	FORCEINLINE FlowStat(const FlowStat &prev, uint new_plan) :
+		MovingAverage<uint>(prev.length), planned(new_plan), sent(prev.sent), via(prev.via) {}
+
+	/**
+	 * prevents one copy operation when moving a flowstat from one set to another and decreasing it at the same time
+	 */
+	FORCEINLINE FlowStat GetDecreasedCopy() const
+	{
+		FlowStat ret(this->length, this->via, this->planned, this->sent);
+		this->MovingAverage<uint>::Decrease(ret.sent);
+		return ret;
+	}
+
+	FORCEINLINE void Increase(int sent)
+	{
+		this->sent += sent;
+	}
+
+	FORCEINLINE uint Sent() const
+	{
+		return this->MovingAverage<uint>::Monthly(sent);
+	}
+
+	FORCEINLINE uint Planned() const
+	{
+		return this->planned;
+	}
+
+	FORCEINLINE StationID Via() const
+	{
+		return this->via;
+	}
+
+	struct comp {
+		bool operator()(const FlowStat & x, const FlowStat & y) const {
+			int diff_x = (int)x.Planned() - (int)x.Sent();
+			int diff_y = (int)y.Planned() - (int)y.Sent();
+			if (diff_x != diff_y) {
+				return diff_x > diff_y;
+			} else {
+				return x.Via() > y.Via();
+			}
+		}
+	};
+
+	FORCEINLINE FlowStat &operator+=(const FlowStat &other)
+	{
+		assert(this->via == INVALID_STATION || other.via == INVALID_STATION || this->via == other.via);
+		if (other.via != INVALID_STATION) this->via = other.via;
+		this->planned += other.planned;
+		uint sent = this->sent + other.sent;
+		if (sent > 0) {
+			this->length = (this->length * this->sent + other.length * other.sent) / sent;
+			assert(this->length > 0);
+		}
+		this->sent = sent;
+		return *this;
+	}
+
+	FORCEINLINE void Clear()
+	{
+		this->planned = 0;
+		this->sent = 0;
+		this->via = INVALID_STATION;
+	}
+};
+
+typedef std::set<FlowStat, FlowStat::comp> FlowStatSet; ///< percentage of flow to be sent via specified station (or consumed locally)
+
+typedef std::map<StationID, LinkStat> LinkStatMap;
+typedef std::map<StationID, FlowStatSet> FlowStatMap; ///< flow descriptions by origin stations
+
+uint GetMovingAverageLength(const Station *from, const Station *to);
+
+class SupplyMovingAverage {
+private:
+	uint supply;
+
+public:
+	friend const SaveLoad *GetGoodsDesc();
+
+	FORCEINLINE SupplyMovingAverage(uint supply = 0) : supply(supply) {}
+
+	FORCEINLINE void Increase(uint value) {this->supply += value;}
+
+	FORCEINLINE void Decrease() {MovingAverage<SupplyMovingAverage>().Decrease(*this);}
+
+	FORCEINLINE uint Value() const {return MovingAverage<uint>().Monthly(this->supply);}
+
+	FORCEINLINE SupplyMovingAverage &operator/=(uint divident)
+		{this->supply = DivideApprox(this->supply, divident); return *this;}
+
+	FORCEINLINE SupplyMovingAverage &operator*=(uint factor)
+		{this->supply *= factor; return *this;}
+
+	FORCEINLINE SupplyMovingAverage operator/(uint divident) const
+		{return SupplyMovingAverage(DivideApprox(this->supply, divident));}
+
+	FORCEINLINE SupplyMovingAverage operator*(uint factor) const
+		{return SupplyMovingAverage(this->supply * factor);}
+
+	FORCEINLINE bool IsNull() const
+		{return this->supply == 0;}
+};
+
+
 struct GoodsEntry {
 	enum AcceptancePickup {
 		ACCEPTANCE,
@@ -34,7 +238,8 @@ struct GoodsEntry {
 		days_since_pickup(255),
 		rating(INITIAL_STATION_RATING),
 		last_speed(0),
-		last_age(255)
+		last_age(255),
+		last_component(0)
 	{}
 
 	byte acceptance_pickup;
@@ -43,6 +248,26 @@ struct GoodsEntry {
 	byte last_speed;
 	byte last_age;
 	StationCargoList cargo; ///< The cargo packets of cargo waiting in this station
+	SupplyMovingAverage supply;
+	uint acceptance;        ///< Accepted cargo (by houses, HQs, industry tiles)
+	FlowStatMap flows;      ///< The planned flows through this station
+	LinkStatMap link_stats; ///< capacities and usage statistics for outgoing links
+	LinkGraphComponentID last_component; ///< the component this station was last part of in this cargo's link graph
+	
+	FlowStat GetSumFlowVia(StationID via) const;
+
+	/**
+	 * update the flow stats for count cargo from source sent to next
+	 */
+	void UpdateFlowStats(StationID source, uint count, StationID next);
+	void UpdateFlowStats(FlowStatSet &flow_stats, uint count, StationID next);
+	void UpdateFlowStats(FlowStatSet &flow_stats, FlowStatSet::iterator flow_it, uint count);
+
+	/**
+	 * update the flow stats for count cargo that cannot be delivered here
+	 * return the direction where it is sent
+	 */
+	StationID UpdateFlowStatsTransfer(StationID source, uint count, StationID curr);
 };
 
 
@@ -137,6 +362,8 @@ public:
 	/* virtual */ uint32 GetNewGRFVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) const;
 
 	/* virtual */ void GetTileArea(TileArea *ta, StationType type) const;
+
+	void RunAverages();
 };
 
 #define FOR_ALL_STATIONS(var) FOR_ALL_BASE_STATIONS_OF_TYPE(Station, var)
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 337c7ba..8e78f00 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -47,6 +47,8 @@
 #include "core/random_func.hpp"
 #include "company_base.h"
 #include "newgrf.h"
+#include "moving_average.h"
+#include "linkgraph/linkgraph.h"
 #include "table/airporttile_ids.h"
 #include "newgrf_airporttiles.h"
 
@@ -579,6 +581,9 @@ void UpdateStationAcceptance(Station *st, bool show_msg)
 		if ((!is_passengers && !(st->facilities & ~FACIL_BUS_STOP)) ||
 				(is_passengers && !(st->facilities & ~FACIL_TRUCK_STOP))) {
 			amt = 0;
+			st->goods[i].acceptance = 0;
+		} else {
+			st->goods[i].acceptance = acceptance[i];
 		}
 
 		SB(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, amt >= 8);
@@ -3020,6 +3025,12 @@ static void UpdateStationRating(Station *st)
 			(rating += 10, age >= 1) ||
 			(rating += 13, true);
 
+			if (_settings_game.economy.cap_ratings_by_component) {
+				CargoID c_id = cs->Index();
+				uint component_cap = 128 + 128 * _link_graphs[c_id].GetComponentAcceptance(ge->last_component) / (GlobalCargoAcceptance::inst.Get(c_id) + 1);
+				rating = min(rating, component_cap);
+			}
+
 			{
 				int or_ = ge->rating; // old rating
 
@@ -3060,7 +3071,7 @@ static void UpdateStationRating(Station *st)
 					waiting_changed = true;
 				}
 
-				if (waiting_changed) ge->cargo.Truncate(waiting);
+				if (waiting_changed) ge->cargo.RandomTruncate(waiting);
 			}
 		}
 	}
@@ -3073,6 +3084,176 @@ static void UpdateStationRating(Station *st)
 	}
 }
 
+void DeleteStaleFlows(StationID at, CargoID c_id, StationID to) {
+	FlowStatMap & flows = Station::Get(at)->goods[c_id].flows;
+	for (FlowStatMap::iterator f_it = flows.begin(); f_it != flows.end();) {
+		FlowStatSet & s_flows = f_it->second;
+		for (FlowStatSet::iterator s_it = s_flows.begin(); s_it != s_flows.end();) {
+			if (s_it->Via() == to) {
+				s_flows.erase(s_it++);
+			} else {
+				++s_it;
+			}
+		}
+		if (s_flows.empty()) {
+			flows.erase(f_it++);
+		} else {
+			++f_it;
+		}
+	}
+}
+
+uint GetMovingAverageLength(const Station *from, const Station *to)
+{
+	return _settings_game.economy.moving_average_length + (DistanceManhattan(from->xy, to->xy) >> 2);
+}
+
+void Station::RunAverages() {
+	FlowStatSet new_flows;
+	for(int goods_index = CT_BEGIN; goods_index != CT_END; ++goods_index) {
+		GoodsEntry & good = this->goods[goods_index];
+		good.supply.Decrease();
+		LinkStatMap & links = good.link_stats;
+		for (LinkStatMap::iterator i = links.begin(); i != links.end();) {
+			StationID id = i->first;
+			Station *other = Station::GetIfValid(id);
+			if (other == NULL) {
+				links.erase(i++);
+			} else {
+				LinkStat & ls = i->second;
+				ls.Decrease();
+				if (ls.IsNull()) {
+					DeleteStaleFlows(this->index, goods_index, id);
+					good.cargo.RerouteStalePackets(this->index, id, &good);
+					links.erase(i++);
+				} else {
+					++i;
+				}
+			}
+		}
+
+		FlowStatMap & flows = good.flows;
+		for (FlowStatMap::iterator i = flows.begin(); i != flows.end();) {
+			StationID source = i->first;
+			if (!Station::IsValidID(source)) {
+				flows.erase(i++);
+			} else {
+				FlowStatSet & flow_set = i->second;
+				for (FlowStatSet::iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
+					if (Station::IsValidID(j->Via())) {
+						new_flows.insert(j->GetDecreasedCopy());
+					}
+				}
+				flow_set.swap(new_flows);
+				new_flows.clear();
+				++i;
+			}
+		}
+	}
+}
+
+void UpdateFlows(Station *st, Vehicle *front, StationID next_station_id) {
+	if (next_station_id == INVALID_STATION) {
+		return;
+	} else {
+		for (Vehicle *v = front; v != NULL; v = v->Next()) {
+			GoodsEntry *ge = &st->goods[v->cargo_type];
+			const CargoPacketList *packets = v->cargo.Packets();
+			for(VehicleCargoList::ConstIterator i = packets->begin(); i != packets->end(); ++i) {
+				CargoPacket *p = *i;
+				ge->UpdateFlowStats(p->SourceStation(), p->Count(), next_station_id);
+			}
+		}
+	}
+}
+
+void RecalcFrozenIfLoading(const Vehicle * v) {
+	if (v->current_order.IsType(OT_LOADING)) {
+		RecalcFrozen(Station::Get(v->last_station_visited));
+	}
+}
+
+void RecalcFrozen(Station * st) {
+	if (st->loading_vehicles.empty()) {
+		/* if no vehicles are there the frozen values are always correct */
+		return;
+	}
+
+	for(int goods_index = CT_BEGIN; goods_index != CT_END; ++goods_index) {
+		GoodsEntry & good = st->goods[goods_index];
+		LinkStatMap & links = good.link_stats;
+		for (LinkStatMap::iterator i = links.begin(); i != links.end(); ++i) {
+			i->second.Unfreeze();
+		}
+	}
+
+	std::list<Vehicle *>::iterator v_it = st->loading_vehicles.begin();
+	while(v_it != st->loading_vehicles.end()) {
+		const Vehicle * front = *v_it;
+		OrderList * orders = front->orders.list;
+		if (orders != NULL) {
+			StationID next_station_id = orders->GetNextStoppingStation(front->cur_order_index, front->type == VEH_ROAD || front->type == VEH_TRAIN);
+			if (next_station_id != INVALID_STATION && next_station_id != st->index) {
+				IncreaseStats(st, front, next_station_id, true);
+			}
+		}
+		++v_it;
+	}
+}
+
+void DecreaseFrozen(Station *st, const Vehicle *front, StationID next_station_id) {
+	assert(st->index != next_station_id && next_station_id != INVALID_STATION);
+	for (const Vehicle *v = front; v != NULL; v = v->Next()) {
+		if (v->cargo_cap > 0) {
+			LinkStatMap & link_stats = st->goods[v->cargo_type].link_stats;
+			LinkStatMap::iterator lstat_it = link_stats.find(next_station_id);
+			if (lstat_it == link_stats.end()) {
+				DEBUG(misc, 0, "frozen not in linkstat list.");
+				RecalcFrozen(st);
+				return;
+			} else {
+				LinkStat & link_stat = lstat_it->second;
+				if (link_stat.Frozen() < v->cargo_cap) {
+					DEBUG(misc, 0, "frozen is smaller than cargo cap.");
+					RecalcFrozen(st);
+					return;
+				} else {
+					link_stat.Unfreeze(v->cargo_cap);
+				}
+				assert(!link_stat.IsNull());
+			}
+		}
+	}
+}
+
+void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id, bool freeze) {
+	Station *next = Station::GetIfValid(next_station_id);
+	assert(st->index != next_station_id && next != NULL);
+	uint average_length = GetMovingAverageLength(st, next);
+
+	for (const Vehicle *v = front; v != NULL; v = v->Next()) {
+		if (v->cargo_cap > 0) {
+			LinkStatMap &stats = st->goods[v->cargo_type].link_stats;
+			LinkStatMap::iterator i = stats.find(next_station_id);
+			if (i == stats.end()) {
+				stats.insert(std::make_pair(next_station_id, LinkStat(
+					average_length, v->cargo_cap,
+					freeze ? v->cargo_cap : 0,
+					freeze ? 0 : v->cargo.Count()
+				)));
+			} else {
+				LinkStat & link_stat = i->second;
+				if (freeze) {
+					link_stat.Freeze(v->cargo_cap);
+				} else {
+					link_stat.Increase(v->cargo_cap, v->cargo.Count());
+				}
+				assert(!link_stat.IsNull());
+			}
+		}
+	}
+}
+
 /* called for every station each tick */
 static void StationHandleSmallTick(BaseStation *st)
 {
@@ -3089,10 +3270,12 @@ void OnTick_Station()
 {
 	if (_game_mode == GM_EDITOR) return;
 
+	RunAverages<Station>();
+
 	BaseStation *st;
 	FOR_ALL_BASE_STATIONS(st) {
 		StationHandleSmallTick(st);
-
+		
 		/* Run 250 tick interval trigger for station animation.
 		 * Station index is included so that triggers are not all done
 		 * at the same time. */
@@ -3131,8 +3314,19 @@ void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint rad
 
 static void UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
 {
-	st->goods[type].cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id));
-	SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
+	GoodsEntry & good = st->goods[type];
+	StationID id = st->index;
+	StationID next = INVALID_STATION;
+	FlowStatSet & flow_stats = good.flows[id];
+	FlowStatSet::iterator i = flow_stats.begin();
+	if (i != flow_stats.end()) {
+		next = i->Via();
+		good.UpdateFlowStats(flow_stats, i, amount);
+	}
+	CargoPacket * packet = new CargoPacket(st->index, st->xy, amount, source_type, source_id);
+	good.cargo.Append(next, packet);
+	SetBit(good.acceptance_pickup, GoodsEntry::PICKUP);
+	good.supply.Increase(amount);
 
 	StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
 	AirportAnimationTrigger(st, AAT_STATION_NEW_CARGO, type);
@@ -3489,6 +3683,65 @@ static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, ui
 	return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 }
 
+void GoodsEntry::UpdateFlowStats(FlowStatSet &flow_stats, FlowStatSet::iterator flow_it, uint count)
+{
+	FlowStat fs = *flow_it;
+	fs.Increase(count);
+	flow_stats.erase(flow_it);
+	flow_stats.insert(fs);
+}
+
+void GoodsEntry::UpdateFlowStats(FlowStatSet &flow_stats, uint count, StationID next)
+{
+	FlowStatSet::iterator flow_it = flow_stats.begin();
+	while (flow_it != flow_stats.end()) {
+		StationID via = flow_it->Via();
+		if (via == next) { //usually the first one is the correct one
+			this->UpdateFlowStats(flow_stats, flow_it, count);
+			return;
+		} else {
+			++flow_it;
+		}
+	}
+}
+
+void GoodsEntry::UpdateFlowStats(StationID source, uint count, StationID next)
+{
+	if (source == INVALID_STATION || next == INVALID_STATION || this->flows.empty()) return;
+	FlowStatSet &flow_stats = this->flows[source];
+	this->UpdateFlowStats(flow_stats, count, next);
+}
+
+StationID GoodsEntry::UpdateFlowStatsTransfer(StationID source, uint count, StationID curr) {
+	if (source == INVALID_STATION || this->flows.empty()) return INVALID_STATION;
+	FlowStatSet &flow_stats = this->flows[source];
+	FlowStatSet::iterator flow_it = flow_stats.begin();
+	while (flow_it != flow_stats.end()) {
+		StationID via = flow_it->Via();
+		if (via != curr) {
+			UpdateFlowStats(flow_stats, flow_it, count);
+			return via;
+		}
+		else {
+			++flow_it;
+		}
+	}
+	return INVALID_STATION;
+}
+
+FlowStat GoodsEntry::GetSumFlowVia(StationID via) const {
+	FlowStat ret(1, via);
+	for(FlowStatMap::const_iterator i = this->flows.begin(); i != this->flows.end(); ++i) {
+		const FlowStatSet &flow_set = i->second;
+		for (FlowStatSet::const_iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
+			const FlowStat &flow = *j;
+			if (flow.Via() == via) {
+				ret += flow;
+			}
+		}
+	}
+	return ret;
+}
 
 extern const TileTypeProcs _tile_type_station_procs = {
 	DrawTile_Station,           // draw_tile_proc
diff --git a/src/station_func.h b/src/station_func.h
index 2a2d831..e5b8194 100644
--- a/src/station_func.h
+++ b/src/station_func.h
@@ -18,6 +18,7 @@
 #include "road_type.h"
 #include "cargo_type.h"
 #include "company_type.h"
+#include "vehicle_base.h"
 
 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius);
 
@@ -49,4 +50,16 @@ bool IsStationTileElectrifiable(TileIndex tile);
 
 void UpdateAirportsNoise();
 
+void DecreaseFrozen(Station *st, const Vehicle *v, StationID next_station_id);
+
+void RecalcFrozen(Station * st);
+
+void RecalcFrozenIfLoading(const Vehicle * v);
+
+void IncreaseStats(Station *st, const Vehicle *v, StationID next_station_id, bool freeze);
+
+void UpdateFlows(Station * st, Vehicle *v, StationID next_station_id);
+
+void DeleteStaleFlows(StationID at, CargoID c_id, StationID to);
+
 #endif /* STATION_FUNC_H */
diff --git a/src/station_gui.cpp b/src/station_gui.cpp
index 6efb569..6a868db 100644
--- a/src/station_gui.cpp
+++ b/src/station_gui.cpp
@@ -32,6 +32,8 @@
 #include "table/strings.h"
 #include "table/sprites.h"
 
+#include <vector>
+
 /**
  * Draw a (multi)line of cargos seperated by commas, and prefixed with a string.
  * @param cargo_mask Mask of cargos to include in the list.
@@ -822,10 +824,18 @@ static const NWidgetPart _nested_station_view_widgets[] = {
 		NWidget(WWT_STICKYBOX, COLOUR_GREY),
 	EndContainer(),
 	NWidget(NWID_HORIZONTAL),
-		NWidget(WWT_PANEL, COLOUR_GREY, SVW_WAITING), SetMinimalSize(237, 52), SetResize(1, 10), EndContainer(),
+		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_SORT_ORDER), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
+		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_SORT_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
+	EndContainer(),
+	NWidget(NWID_HORIZONTAL),
+		NWidget(WWT_DROPDOWN, COLOUR_GREY, SVW_MODE), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_STATION_VIEW_WAITING, STR_STATION_VIEW_TOGGLE_CARGO_VIEW),
+		NWidget(WWT_DROPDOWN, COLOUR_GREY, SVW_GROUP_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
+	EndContainer(),
+	NWidget(NWID_HORIZONTAL),
+		NWidget(WWT_PANEL, COLOUR_GREY, SVW_WAITING), SetMinimalSize(237, 44), SetResize(1, 10), EndContainer(),
 		NWidget(WWT_SCROLLBAR, COLOUR_GREY, SVW_SCROLLBAR),
 	EndContainer(),
-	NWidget(WWT_PANEL, COLOUR_GREY, SVW_ACCEPTLIST), SetMinimalSize(249, 32), SetResize(1, 0), EndContainer(),
+	NWidget(WWT_PANEL, COLOUR_GREY, SVW_ACCEPTLIST), SetMinimalSize(249, 23), SetResize(1, 0), EndContainer(),
 	NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_LOCATION), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
 				SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
@@ -858,33 +868,228 @@ static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
 
 	SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
 
-	int x = _dynlang.text_dir == TD_RTL ? right - num * 10 : left;
+	int x = _dynlang.text_dir == TD_RTL ? left : right - num * 10;
 	do {
 		DrawSprite(sprite, PAL_NONE, x, y);
 		x += 10;
 	} while (--num);
 }
 
-struct CargoData {
-	CargoID cargo;
-	StationID source;
-	uint count;
+CargoDataEntry::CargoDataEntry() :
+	parent(NULL),
+	station(INVALID_STATION),
+	size(0),
+	count(0),
+	subentries(new CargoDataSet(CargoSorter(ST_CARGO_ID)))
+{}
+
+CargoDataEntry::CargoDataEntry(CargoID car, uint c, CargoDataEntry * p) :
+	parent(p),
+	cargo(car),
+	size(0),
+	count(c),
+	subentries(new CargoDataSet)
+{}
+
+CargoDataEntry::CargoDataEntry(StationID st, uint c, CargoDataEntry * p) :
+	parent(p),
+	station(st),
+	size(0),
+	count(c),
+	subentries(new CargoDataSet)
+{}
+
+CargoDataEntry::CargoDataEntry(StationID st) :
+	parent(NULL),
+	station(st),
+	size(0),
+	count(0),
+	subentries(NULL)
+{}
+
+CargoDataEntry::CargoDataEntry(CargoID ca) :
+	parent(NULL),
+	cargo(ca),
+	size(0),
+	count(0),
+	subentries(NULL)
+{}
+
+CargoDataEntry::~CargoDataEntry() {
+	this->Clear();
+	delete subentries;
+}
 
-	CargoData(CargoID cargo, StationID source, uint count) :
-		cargo(cargo),
-		source(source),
-		count(count)
-	{ }
-};
+void CargoDataEntry::Clear() {
+	if (subentries != NULL) {
+		for (CargoDataSet::iterator i = subentries->begin(); i != subentries->end(); ++i) {
+			assert(*i != this);
+			delete *i;
+		}
+		subentries->clear();
+	}
+	if (parent != NULL) {
+		parent->count -= this->count;
+	}
+	this->count = 0;
+	this->size = 0;
+}
 
-typedef std::list<CargoData> CargoDataList;
+void CargoDataEntry::Remove(CargoDataEntry * comp) {
+	CargoDataSet::iterator i = subentries->find(comp);
+	if (i != subentries->end()) {
+		delete(*i);
+		subentries->erase(i);
+	}
+}
+
+template<class ID>
+CargoDataEntry * CargoDataEntry::InsertOrRetrieve(ID s) {
+	CargoDataEntry tmp(s);
+	CargoDataSet::iterator i = subentries->find(&tmp);
+	if (i == subentries->end()) {
+		IncrementSize();
+		return *(subentries->insert(new CargoDataEntry(s, 0, this)).first);
+	} else {
+		CargoDataEntry * ret = *i;
+		assert(subentries->value_comp().GetSortType() != ST_COUNT);
+		return ret;
+	}
+}
+
+void CargoDataEntry::Update(uint count) {
+	this->count += count;
+	if (parent != NULL) {
+		parent->Update(count);
+	}
+}
+
+void CargoDataEntry::IncrementSize() {
+	 ++size;
+	 if (parent != NULL) parent->IncrementSize();
+}
+
+void CargoDataEntry::Resort(CargoSortType type, SortOrder order) {
+	CargoDataSet * new_subs = new CargoDataSet(subentries->begin(), subentries->end(), CargoSorter(type, order));
+	delete subentries;
+	subentries = new_subs;
+}
+
+CargoDataEntry * CargoDataEntry::Retrieve(CargoDataSet::iterator i) const {
+	if (i == subentries->end()) {
+		return NULL;
+	} else {
+		assert(subentries->value_comp().GetSortType() != ST_COUNT);
+		return *i;
+	}
+}
+
+bool CargoSorter::operator()(const CargoDataEntry * cd1, const CargoDataEntry * cd2) const {
+	switch (type) {
+	case ST_STATION_ID:
+		return SortId<StationID>(cd1->GetStation(), cd2->GetStation());
+		break;
+	case ST_CARGO_ID:
+		return SortId<CargoID>(cd1->GetCargo(), cd2->GetCargo());
+		break;
+	case ST_COUNT:
+		return SortCount(cd1, cd2);
+		break;
+	case ST_STATION_STRING:
+		return SortStation(cd1->GetStation(), cd2->GetStation());
+		break;
+	default:
+		NOT_REACHED();
+	}
+	return false;
+}
+
+template<class ID>
+bool CargoSorter::SortId(ID st1, ID st2) const {
+	if (order == SO_ASCENDING) {
+		return st1 < st2;
+	} else {
+		return st2 < st1;
+	}
+}
+
+bool CargoSorter::SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const {
+	uint c1 = cd1->GetCount();
+	uint c2 = cd2->GetCount();
+	if (c1 == c2) {
+		return SortStation(cd1->GetStation(), cd2->GetStation());
+	} else if (order == SO_ASCENDING) {
+		return c1 < c2;
+	} else {
+		return c2 < c1;
+	}
+}
+
+bool CargoSorter::SortStation(StationID st1, StationID st2) const {
+	static char buf1[64];
+	static char buf2[64];
+
+	if (!Station::IsValidID(st1)) {
+		if (!Station::IsValidID(st2)) {
+			return SortId(st1, st2);
+		} else {
+			return order == SO_ASCENDING;
+		}
+	} else if (!Station::IsValidID(st2)) {
+		return order == SO_DESCENDING;
+	}
+
+	SetDParam(0, st1);
+	GetString(buf1, STR_STATION_NAME, lastof(buf1));
+	SetDParam(0, st2);
+	GetString(buf2, STR_STATION_NAME, lastof(buf2));
+
+	int res = strcmp(buf1, buf2);
+	if (res == 0) {
+		return SortId(st1, st2);
+	} else if (res < 0) {
+		return order == SO_ASCENDING;
+	} else {
+		return order == SO_DESCENDING;
+	}
+}
 
 /**
  * The StationView window
  */
 struct StationViewWindow : public Window {
-	uint32 cargo;                 ///< Bitmask of cargo types to expand
-	uint16 cargo_rows[NUM_CARGO]; ///< Header row for each cargo type
+	struct RowDisplay {
+		RowDisplay(CargoDataEntry * f, StationID n) : filter(f), next_station(n) {}
+		RowDisplay(CargoDataEntry * f, CargoID n) : filter(f), next_cargo(n) {}
+		CargoDataEntry * filter;
+		union {
+			StationID next_station;
+			CargoID next_cargo;
+		};
+	};
+
+	typedef std::vector<RowDisplay> CargoDataVector;
+
+	static const int _num_columns = 4;
+
+	enum Invalidation {
+		INV_FLOWS = 0x100,
+		INV_CARGO = 0x200
+	};
+
+	enum Grouping {
+		GR_SOURCE,
+		GR_NEXT,
+		GR_DESTINATION,
+		GR_CARGO,
+	};
+
+	enum Mode {
+		WAITING,
+		PLANNED,
+		SENT
+	};
+	
 	uint expand_shrink_width;     ///< The width allocated to the expand/shrink 'button'
 	int rating_lines;             ///< Number of lines in the cargo ratings view.
 	int accepts_lines;            ///< Number of lines in the accepted cargo view.
@@ -895,7 +1100,24 @@ struct StationViewWindow : public Window {
 		ALH_ACCEPTS = 3,  ///< Height of the accepted cargo view.
 	};
 
-	StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
+	static const StringID _sort_names[];
+	static const StringID _group_names[];
+	static const StringID _mode_names[];
+
+	CargoSortType sortings[_num_columns];
+	SortOrder sort_orders[_num_columns];
+
+	int scroll_to_row;
+	int grouping_index;
+	Mode current_mode;
+	Grouping groupings[_num_columns];
+
+	CargoDataEntry expanded_rows;
+	CargoDataEntry cached_destinations;
+	CargoDataVector displayed_rows;
+
+	StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(),
+		scroll_to_row(INT_MAX), grouping_index(0)
 	{
 		this->rating_lines  = ALH_RATING;
 		this->accepts_lines = ALH_ACCEPTS;
@@ -904,6 +1126,13 @@ struct StationViewWindow : public Window {
 		/* Nested widget tree creation is done in two steps to ensure that this->GetWidget<NWidgetCore>(SVW_ACCEPTS) exists in UpdateWidgetSize(). */
 		this->FinishInitNested(desc, window_number);
 
+		this->groupings[0] = GR_CARGO;
+		this->sortings[0] = ST_AS_GROUPING;
+		this->SelectGroupBy(_settings_client.gui.station_gui_group_order);
+		this->SelectSortBy((CargoSortType)_settings_client.gui.station_gui_sort_by);
+		this->sort_orders[0] = SO_ASCENDING;
+		this->SelectSortOrder((SortOrder)_settings_client.gui.station_gui_sort_order);
+		this->SelectMode(WAITING);
 		Owner owner = Station::Get(window_number)->owner;
 		if (owner != OWNER_NONE) this->owner = owner;
 	}
@@ -918,12 +1147,39 @@ struct StationViewWindow : public Window {
 		DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11), false);
 	}
 
+	void ShowCargo(CargoDataEntry * data, CargoID cargo, StationID source, StationID next, StationID dest, uint count) {
+		if (count == 0) return;
+		const CargoDataEntry * expand = &expanded_rows;
+		for (int i = 0; i < _num_columns && expand != NULL; ++i) {
+			switch (groupings[i]) {
+			case GR_CARGO:
+				assert(i == 0);
+				data = data->InsertOrRetrieve(cargo);
+				expand = expand->Retrieve(cargo);
+				break;
+			case GR_SOURCE:
+				data = data->InsertOrRetrieve(source);
+				expand = expand->Retrieve(source);
+				break;
+			case GR_NEXT:
+				data = data->InsertOrRetrieve(next);
+				expand = expand->Retrieve(next);
+				break;
+			case GR_DESTINATION:
+				data = data->InsertOrRetrieve(dest);
+				expand = expand->Retrieve(dest);
+				break;
+			}
+		}
+		data->Update(count);
+	}
+
 	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 	{
 		switch (widget) {
 			case SVW_WAITING:
 				resize->height = FONT_HEIGHT_NORMAL;
-				size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
+				size->height = WD_FRAMERECT_TOP + 4 * resize->height + WD_FRAMERECT_BOTTOM;
 				this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
 				break;
 
@@ -935,20 +1191,21 @@ struct StationViewWindow : public Window {
 
 	virtual void OnPaint()
 	{
-		CargoDataList cargolist;
-		uint32 transfers = 0;
-		this->OrderWaitingCargo(&cargolist, &transfers);
+		const Station *st = Station::Get(this->window_number);
+		CargoDataEntry cargo;
+		BuildCargoList(&cargo, st);
 
-		this->vscroll.SetCount((int)cargolist.size() + 1); // update scrollbar
+		this->vscroll.SetCount(cargo.Size()); // update scrollbar
 
 		/* disable some buttons */
-		const Station *st = Station::Get(this->window_number);
 		this->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_company);
 		this->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
 		this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
 		this->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
 		this->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
 
+		SetDParam(0, st->index);
+		SetDParam(1, st->facilities);
 		this->DrawWidgets();
 
 		if (!this->IsShaded()) {
@@ -971,10 +1228,20 @@ struct StationViewWindow : public Window {
 				}
 			}
 
+			/* draw arrow pointing up/down for ascending/descending sorting */
+			this->DrawSortButtonState(SVW_SORT_ORDER, sort_orders[1] == SO_ASCENDING ? SBS_UP : SBS_DOWN);
+
+			int pos = this->vscroll.GetPosition(); ///< = this->vscroll.pos
+
+			int maxrows = this->vscroll.GetCapacity();
+
+			displayed_rows.clear();
+
 			/* Draw waiting cargo. */
 			NWidgetBase *nwi = this->GetWidget<NWidgetBase>(SVW_WAITING);
 			Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
-			this->DrawWaitingCargo(waiting_rect, cargolist, transfers);
+			this->DrawEntries(&cargo, waiting_rect, pos, maxrows, 0);
+			scroll_to_row = INT_MAX;
 		}
 	}
 
@@ -987,115 +1254,267 @@ struct StationViewWindow : public Window {
 		}
 	}
 
-	/** Order waiting cargo by type and destination.
-	 * @param cargolist [out] Ordered cargo.
-	 * @param transfers [out] Bitmask for cargoes being transfered.
-	 * @pre \c *cargolist must be empty.
-	 */
-	void OrderWaitingCargo(CargoDataList *cargolist, uint32 *transfers)
-	{
-		assert(cargolist->size() == 0);
-		*transfers = 0;
+	void RecalcDestinations(CargoID i) {
+		const Station *st = Station::Get(this->window_number);
+		CargoDataEntry *cargo_entry = cached_destinations.InsertOrRetrieve(i);
+		cargo_entry->Clear();
+
+		const FlowStatMap & flows = st->goods[i].flows;
+		for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
+			StationID from = it->first;
+			CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from);
+			const FlowStatSet & flow_set = it->second;
+			for (FlowStatSet::const_iterator flow_it = flow_set.begin(); flow_it != flow_set.end(); ++flow_it) {
+				const FlowStat & stat = *flow_it;
+				StationID via = stat.Via();
+				CargoDataEntry * via_entry = source_entry->InsertOrRetrieve(via);
+				if (via == this->window_number) {
+					via_entry->InsertOrRetrieve(via)->Update(stat.Planned());
+				} else {
+					EstimateDestinations(i, from, via, stat.Planned(), via_entry);
+				}
+			}
+		}
+	}
 
-		StationID station_id = this->window_number;
-		const Station *st = Station::Get(station_id);
+	void EstimateDestinations(CargoID cargo, StationID source, StationID next, uint count, CargoDataEntry *dest) {
+		if (Station::IsValidID(next) && Station::IsValidID(source)) {
+			CargoDataEntry tmp;
+			FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows;
+			FlowStatMap::iterator map_it = flowmap.find(source);
+			if (map_it != flowmap.end()) {
+				FlowStatSet &flows = map_it->second;
+				for (FlowStatSet::iterator i = flows.begin(); i != flows.end(); ++i) {
+					tmp.InsertOrRetrieve(i->Via())->Update(i->Planned());
+				}
+			}
 
-		/* count types of cargos waiting in station */
-		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			if (st->goods[i].cargo.Empty()) {
-				this->cargo_rows[i] = 0;
+			if (tmp.GetCount() == 0) {
+				dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
 			} else {
-				/* Add an entry for total amount of cargo of this type waiting. */
-				cargolist->push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
-
-				/* Set the row for this cargo entry for the expand/hide button */
-				this->cargo_rows[i] = (uint16)cargolist->size();
-
-				/* Add an entry for each distinct cargo source. */
-				const StationCargoList::List *packets = st->goods[i].cargo.Packets();
-				for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
-					const CargoPacket *cp = *it;
-					if (cp->SourceStation() != station_id) {
-						bool added = false;
-
-						/* Enable the expand/hide button for this cargo type */
-						SetBit(*transfers, i);
-
-						/* Don't add cargo lines if not expanded */
-						if (!HasBit(this->cargo, i)) break;
-
-						/* Check if we already have this source in the list */
-						for (CargoDataList::iterator jt(cargolist->begin()); jt != cargolist->end(); jt++) {
-							CargoData *cd = &(*jt);
-							if (cd->cargo == i && cd->source == cp->SourceStation()) {
-								cd->count += cp->Count();
-								added = true;
-								break;
+				uint sum_estimated = 0;
+				while(sum_estimated < count) {
+					for(CargoDataSet::iterator i = tmp.Begin(); i != tmp.End() && sum_estimated < count; ++i) {
+						CargoDataEntry *child = *i;
+						uint estimate = DivideApprox(child->GetCount() * count, tmp.GetCount());
+						if (estimate == 0) estimate = 1;
+
+						sum_estimated += estimate;
+						if (sum_estimated > count) {
+							estimate -= sum_estimated - count;
+							sum_estimated = count;
+						}
+
+						if (estimate > 0) {
+							if (child->GetStation() == next) {
+								dest->InsertOrRetrieve(next)->Update(estimate);
+							} else {
+								EstimateDestinations(cargo, source, child->GetStation(), estimate, dest);
 							}
 						}
+					}
 
-						if (!added) cargolist->push_back(CargoData(i, cp->SourceStation(), cp->Count()));
+				}
+			}
+		} else {
+			dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
+		}
+	}
+
+	void BuildFlowList(CargoID i, const FlowStatMap & flows, CargoDataEntry * cargo) {
+		const CargoDataEntry *source_dest = cached_destinations.Retrieve(i);
+		for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
+			StationID from = it->first;
+			const CargoDataEntry *source_entry = source_dest->Retrieve(from);
+			const FlowStatSet & flow_set = it->second;
+			for (FlowStatSet::const_iterator flow_it = flow_set.begin(); flow_it != flow_set.end(); ++flow_it) {
+				const FlowStat & stat = *flow_it;
+				const CargoDataEntry *via_entry = source_entry->Retrieve(stat.Via());
+				for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
+					CargoDataEntry *dest_entry = *dest_it;
+					uint val = dest_entry->GetCount();
+					if (this->current_mode == SENT) {
+						val *= stat.Sent();
+						val = DivideApprox(val, via_entry->GetCount());
 					}
+					ShowCargo(cargo, i, from, stat.Via(), dest_entry->GetStation(), val);
 				}
 			}
 		}
 	}
 
-	/** Draw waiting cargo.
-	 * @param r Rectangle of the widget.
-	 * @param cargolist Cargo, ordered by type and destination.
-	 * @param transfers Bitmask for cargoes that are transfered.
-	 */
-	void DrawWaitingCargo(const Rect &r, const CargoDataList &cargolist, uint32 transfers) const
-	{
-		int y = r.top + WD_FRAMERECT_TOP;
-		int pos = this->vscroll.GetPosition();
+	void BuildCargoList(CargoID i, const StationCargoList &packets, CargoDataEntry *cargo) {
+		const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(i);
+		for (StationCargoList::ConstIterator it = packets.Packets()->begin(); it != packets.Packets()->end(); it++) {
+			const CargoPacket *cp = *it;
+			StationID next = it.GetKey();
 
-		const Station *st = Station::Get(this->window_number);
-		if (--pos < 0) {
-			StringID str = STR_JUST_NOTHING;
-			for (CargoID i = 0; i < NUM_CARGO; i++) {
-				if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
+			const CargoDataEntry *source_entry = source_dest->Retrieve(cp->SourceStation());
+			if (source_entry == NULL) {
+				ShowCargo(cargo, i, cp->SourceStation(), next, INVALID_STATION, cp->Count());
+				continue;
+			}
+
+			const CargoDataEntry *via_entry = source_entry->Retrieve(next);
+			if (via_entry == NULL) {
+				ShowCargo(cargo, i, cp->SourceStation(), next, INVALID_STATION, cp->Count());
+				continue;
+			}
+
+			for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
+				CargoDataEntry * dest_entry = *dest_it;
+				uint val = DivideApprox(cp->Count() * dest_entry->GetCount(), via_entry->GetCount());
+				ShowCargo(cargo, i, cp->SourceStation(), next, dest_entry->GetStation(), val);
 			}
-			SetDParam(0, str);
-			DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_TITLE);
-			y += FONT_HEIGHT_NORMAL;
 		}
+	}
 
-		bool rtl = _dynlang.text_dir == TD_RTL;
-		int text_left    = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT;
-		int text_right   = rtl ? r.right - WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width;
-		int shrink_left  = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
-		int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
-
-
-		int maxrows = this->vscroll.GetCapacity();
-		for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
-			if (--pos < 0) {
-				const CargoData *cd = &(*it);
-				if (cd->source == INVALID_STATION) {
-					/* Heading */
-					DrawCargoIcons(cd->cargo, cd->count, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y);
-					SetDParam(0, cd->cargo);
-					SetDParam(1, cd->count);
-					if (HasBit(transfers, cd->cargo)) {
-						/* This cargo has transfers waiting so show the expand or shrink 'button' */
-						const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
-						DrawString(text_left, text_right, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
-						DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW, SA_RIGHT);
-					} else {
-						DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
-					}
+	void BuildCargoList(CargoDataEntry * cargo, const Station * st) {
+		for (CargoID i = 0; i < NUM_CARGO; i++) {
+
+			if (this->cached_destinations.Retrieve(i) == NULL) {
+				this->RecalcDestinations(i);
+			}
+
+			if (this->current_mode == WAITING) {
+				BuildCargoList(i, st->goods[i].cargo, cargo);
+			} else {
+				BuildFlowList(i, st->goods[i].flows, cargo);
+			}
+		}
+	}
+
+	void SetDisplayedRow(const CargoDataEntry * data) {
+		std::list<StationID> stations;
+		const CargoDataEntry * parent = data->GetParent();
+		if (parent->GetParent() == NULL) {
+			displayed_rows.push_back(RowDisplay(&expanded_rows, data->GetCargo()));
+			return;
+		}
+
+		StationID next = data->GetStation();
+		while(parent->GetParent()->GetParent() != NULL) {
+			stations.push_back(parent->GetStation());
+			parent = parent->GetParent();
+		}
+
+		CargoID cargo = parent->GetCargo();
+		CargoDataEntry * filter = expanded_rows.Retrieve(cargo);
+		while(!stations.empty()) {
+			filter = filter->Retrieve(stations.back());
+			stations.pop_back();
+		}
+
+		displayed_rows.push_back(RowDisplay(filter, next));
+	}
+
+	StringID GetEntryString(StationID station, StringID here, StringID other_station, StringID any) {
+		if (station == this->window_number) {
+			return here;
+		} else if (station != INVALID_STATION) {
+			SetDParam(2, station);
+			return other_station;
+		} else {
+			return any;
+		}
+	}
+
+	StringID SearchNonStop(CargoDataEntry * cd, StationID station, int column) {
+		CargoDataEntry * parent = cd->GetParent();
+		for (int i = column - 1; i > 0; --i) {
+			if (groupings[i] == GR_DESTINATION) {
+				if (parent->GetStation() == station) {
+					return STR_STATION_VIEW_NONSTOP;
+				} else {
+					return STR_STATION_VIEW_VIA;
+				}
+			}
+			parent = parent->GetParent();
+		}
+
+		if (groupings[column + 1] == GR_DESTINATION) {
+			CargoDataSet::iterator begin = cd->Begin();
+			CargoDataSet::iterator end = cd->End();
+			if (begin != end && ++(cd->Begin()) == end && (*(begin))->GetStation() == station) {
+				return STR_STATION_VIEW_NONSTOP;
+			} else {
+				return STR_STATION_VIEW_VIA;
+			}
+		}
+
+		return STR_STATION_VIEW_VIA;
+	}
+
+	int DrawEntries(CargoDataEntry * entry, Rect &r, int pos, int maxrows, int column, CargoID cargo = CT_INVALID) {
+		if (sortings[column] == ST_AS_GROUPING) {
+			if (groupings[column] != GR_CARGO) {
+				entry->Resort(ST_STATION_STRING, sort_orders[column]);
+			}
+		} else {
+			entry->Resort(ST_COUNT, sort_orders[column]);
+		}
+		for (CargoDataSet::iterator i = entry->Begin(); i != entry->End(); ++i) {
+			CargoDataEntry *cd = *i;
+
+			if (groupings[column] == GR_CARGO) {
+				cargo = cd->GetCargo();
+			}
+
+			if (pos > -maxrows && pos <= 0) {
+				StringID str = STR_EMPTY;
+				int y = r.top + WD_FRAMERECT_TOP - pos * FONT_HEIGHT_NORMAL;
+				SetDParam(0, cargo);
+				SetDParam(1, cd->GetCount());
+
+				if (groupings[column] == GR_CARGO) {
+					str = STR_STATION_VIEW_WAITING_CARGO;
+					DrawCargoIcons(cd->GetCargo(), cd->GetCount(), r.left + WD_FRAMERECT_LEFT + this->expand_shrink_width, r.right - WD_FRAMERECT_RIGHT - this->expand_shrink_width, y);
 				} else {
-					SetDParam(0, cd->cargo);
-					SetDParam(1, cd->count);
-					SetDParam(2, cd->source);
-					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_EN_ROUTE_FROM, TC_FROMSTRING, SA_RIGHT);
+					StationID station = cd->GetStation();
+
+					switch(groupings[column]) {
+					case GR_SOURCE:
+						str = GetEntryString(station, STR_STATION_VIEW_FROM_HERE, STR_STATION_VIEW_FROM, STR_STATION_VIEW_FROM_ANY);
+						break;
+					case GR_NEXT:
+						str = GetEntryString(station, STR_STATION_VIEW_VIA_HERE, STR_STATION_VIEW_VIA, STR_STATION_VIEW_VIA_ANY);
+						if (str == STR_STATION_VIEW_VIA) {
+							str = SearchNonStop(cd, station, column);
+						}
+						break;
+					case GR_DESTINATION:
+						str = GetEntryString(station, STR_STATION_VIEW_TO_HERE, STR_STATION_VIEW_TO, STR_STATION_VIEW_TO_ANY);
+						break;
+					default:
+						NOT_REACHED();
+					}
+					if (pos == -scroll_to_row && Station::IsValidID(station)) {
+						ScrollMainWindowToTile(Station::Get(station)->xy);
+					}
 				}
+				
+				bool rtl = _dynlang.text_dir == TD_RTL;
+				int text_left    = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT + column * this->expand_shrink_width;
+				int text_right   = rtl ? r.right - WD_FRAMERECT_LEFT - column * this->expand_shrink_width : r.right - this->expand_shrink_width;
+				int shrink_left  = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
+				int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
 
-				y += FONT_HEIGHT_NORMAL;
+				DrawString(text_left, text_right, y, str, TC_FROMSTRING);
+
+				if (column < _num_columns - 1) {
+					const char *sym = cd->Size() > 0 ? "-" : "+";
+					DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW);
+				}
+				SetDisplayedRow(cd);
 			}
+			pos = DrawEntries(cd, r, --pos, maxrows, column + 1, cargo);
 		}
+		return pos;
+	}
+
+	virtual void OnInvalidateData(int cargo) {
+		this->cached_destinations.Remove((CargoID)cargo);
+		this->SetDirty();
 	}
 
 	/** Draw accepted cargo in the #SVW_ACCEPTLIST widget.
@@ -1133,32 +1552,45 @@ struct StationViewWindow : public Window {
 			if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
 
 			SetDParam(0, cs->name);
-			SetDParam(2, ToPercent8(ge->rating));
-			SetDParam(1, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
-			DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_RATING);
+			SetDParam(1, ge->supply.Value());
+			SetDParam(3, ToPercent8(ge->rating));
+			SetDParam(2, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
+			DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_SUPPLY_RATING);
 			y += FONT_HEIGHT_NORMAL;
 		}
 		return (y - r.top - WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL - 1) / FONT_HEIGHT_NORMAL;
 	}
 
+	template<class ID>
+	void HandleCargoWaitingClick(CargoDataEntry * filter, ID next) {
+		if (filter->Retrieve(next) != NULL) {
+			filter->Remove(next);
+		} else {
+			filter->InsertOrRetrieve(next);
+		}
+	}
+
 	void HandleCargoWaitingClick(int row)
 	{
-		if (row == 0) return;
-
-		for (CargoID c = 0; c < NUM_CARGO; c++) {
-			if (this->cargo_rows[c] == row) {
-				ToggleBit(this->cargo, c);
-				this->SetWidgetDirty(SVW_WAITING);
-				break;
+		if (row < 0 || (uint)row >= displayed_rows.size()) return;
+		if (_ctrl_pressed) {
+			scroll_to_row = row;
+		} else {
+			RowDisplay & display = displayed_rows[row];
+			if (display.filter == &expanded_rows) {
+				HandleCargoWaitingClick<CargoID>(display.filter, display.next_cargo);
+			} else {
+				HandleCargoWaitingClick<StationID>(display.filter, display.next_station);
 			}
 		}
+		this->SetWidgetDirty(SVW_WAITING);
 	}
 
 	virtual void OnClick(Point pt, int widget, int click_count)
 	{
 		switch (widget) {
 			case SVW_WAITING:
-				this->HandleCargoWaitingClick((pt.y - this->GetWidget<NWidgetBase>(SVW_WAITING)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL + this->vscroll.GetPosition());
+				this->HandleCargoWaitingClick((pt.y - this->GetWidget<NWidgetBase>(SVW_WAITING)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL);
 				break;
 
 			case SVW_LOCATION:
@@ -1217,6 +1649,100 @@ struct StationViewWindow : public Window {
 				ShowVehicleListWindow(owner, VEH_SHIP, (StationID)this->window_number);
 				break;
 			}
+
+			case SVW_MODE: {
+				ShowDropDownMenu(this, _mode_names, this->current_mode, SVW_MODE, 0, 0);
+				break;
+			}
+
+			case SVW_SORT_BY: {
+				CargoSortType sorting = (sortings[1] == ST_AS_GROUPING ? ST_COUNT : ST_AS_GROUPING);
+				SelectSortBy(sorting);
+				this->flags4 |= WF_TIMEOUT_BEGIN;
+				this->LowerWidget(SVW_SORT_BY);
+				break;
+			}
+
+			case SVW_GROUP_BY: {
+				ShowDropDownMenu(this, _group_names, this->grouping_index, SVW_GROUP_BY, 0, 0);
+				break;
+			}
+
+			case SVW_SORT_ORDER: { // flip sorting method asc/desc
+				SortOrder order = (sort_orders[1] == SO_ASCENDING ? SO_DESCENDING : SO_ASCENDING);
+				SelectSortOrder(order);
+				this->flags4 |= WF_TIMEOUT_BEGIN;
+				this->LowerWidget(SVW_SORT_ORDER);
+				break;
+			}
+		}
+	}
+
+	void SelectSortBy(CargoSortType sorting) {
+		_settings_client.gui.station_gui_sort_by = sorting;
+		sortings[1] = sortings[2] = sortings[3] = sorting;
+		/* Display the current sort variant */
+		this->GetWidget<NWidgetCore>(SVW_SORT_BY)->widget_data = this->_sort_names[sorting];
+		this->SetDirty();
+	}
+
+	void SelectSortOrder(SortOrder order) {
+		sort_orders[1] = sort_orders[2] = sort_orders[3] = order;
+		_settings_client.gui.station_gui_sort_order = sort_orders[1];
+		this->SetDirty();
+	}
+
+	void SelectMode(int index) {
+		this->current_mode = (Mode)index;
+		this->GetWidget<NWidgetCore>(SVW_MODE)->widget_data = _mode_names[index];
+		this->SetDirty();
+	}
+
+	void SelectGroupBy(int index) {
+		this->grouping_index = index;
+		_settings_client.gui.station_gui_group_order = index;
+		this->GetWidget<NWidgetCore>(SVW_GROUP_BY)->widget_data = _group_names[index];
+		switch(_group_names[index]) {
+		case STR_STATION_VIEW_GROUP_S_V_D:
+			groupings[1] = GR_SOURCE;
+			groupings[2] = GR_NEXT;
+			groupings[3] = GR_DESTINATION;
+			break;
+		case STR_STATION_VIEW_GROUP_S_D_V:
+			groupings[1] = GR_SOURCE;
+			groupings[2] = GR_DESTINATION;
+			groupings[3] = GR_NEXT;
+			break;
+		case STR_STATION_VIEW_GROUP_V_S_D:
+			groupings[1] = GR_NEXT;
+			groupings[2] = GR_SOURCE;
+			groupings[3] = GR_DESTINATION;
+			break;
+		case STR_STATION_VIEW_GROUP_V_D_S:
+			groupings[1] = GR_NEXT;
+			groupings[2] = GR_DESTINATION;
+			groupings[3] = GR_SOURCE;
+			break;
+		case STR_STATION_VIEW_GROUP_D_S_V:
+			groupings[1] = GR_DESTINATION;
+			groupings[2] = GR_SOURCE;
+			groupings[3] = GR_NEXT;
+			break;
+		case STR_STATION_VIEW_GROUP_D_V_S:
+			groupings[1] = GR_DESTINATION;
+			groupings[2] = GR_NEXT;
+			groupings[3] = GR_SOURCE;
+			break;
+		}
+		this->SetDirty();
+	}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		if (widget == SVW_MODE) {
+			SelectMode(index);
+		} else {
+			SelectGroupBy(index);
 		}
 	}
 
@@ -1234,8 +1760,31 @@ struct StationViewWindow : public Window {
 };
 
 
+const StringID StationViewWindow::_sort_names[] = {
+	STR_SORT_BY_STATION,
+	STR_SORT_BY_AMOUNT,
+	INVALID_STRING_ID
+};
+
+const StringID StationViewWindow::_mode_names[] = {
+	STR_STATION_VIEW_WAITING,
+	STR_STATION_VIEW_PLANNED,
+	STR_STATION_VIEW_SENT,
+	INVALID_STRING_ID
+};
+
+const StringID StationViewWindow::_group_names[] = {
+	STR_STATION_VIEW_GROUP_S_V_D,
+	STR_STATION_VIEW_GROUP_S_D_V,
+	STR_STATION_VIEW_GROUP_V_S_D,
+	STR_STATION_VIEW_GROUP_V_D_S,
+	STR_STATION_VIEW_GROUP_D_S_V,
+	STR_STATION_VIEW_GROUP_D_V_S,
+	INVALID_STRING_ID
+};
+
 static const WindowDesc _station_view_desc(
-	WDP_AUTO, 249, 110,
+	WDP_AUTO, 249, 117,
 	WC_STATION_VIEW, WC_NONE,
 	WDF_UNCLICK_BUTTONS,
 	_nested_station_view_widgets, lengthof(_nested_station_view_widgets)
diff --git a/src/station_gui.h b/src/station_gui.h
index cb8b278..2343c5b 100644
--- a/src/station_gui.h
+++ b/src/station_gui.h
@@ -13,21 +13,28 @@
 #define STATION_GUI_H
 
 #include "command_type.h"
+#include "station_type.h"
 #include "tilearea_type.h"
 #include "window_type.h"
+#include "cargo_type.h"
+#include <set>
 
 /** Enum for StationView, referring to _station_view_widgets and _station_view_expanded_widgets */
 enum StationViewWidgets {
 	SVW_CAPTION    =  0, ///< Caption of the window
-	SVW_WAITING    =  1, ///< List of waiting cargo
-	SVW_SCROLLBAR  =  2, ///< Scrollbar
-	SVW_ACCEPTLIST =  3, ///< List of accepted cargos
-	SVW_RATINGLIST =  3, ///< Ratings of cargos
-	SVW_LOCATION   =  4, ///< 'Location' button
-	SVW_RATINGS    =  5, ///< 'Ratings' button
-	SVW_ACCEPTS    =  5, ///< 'Accepts' button
-	SVW_RENAME     =  6, ///< 'Rename' button
-	SVW_TRAINS     =  7, ///< List of scheduled trains button
+	SVW_SORT_ORDER =  1, ///< 'Sort order' button
+	SVW_SORT_BY    =  2, ///< 'Sort by' button
+	SVW_MODE       =  3, ///< button for toggling planned and real flows
+	SVW_GROUP_BY   =  4, ///< 'Group by' button
+	SVW_WAITING    =  5, ///< List of waiting cargo
+	SVW_SCROLLBAR  =  6, ///< Scrollbar
+	SVW_ACCEPTLIST =  7, ///< List of accepted cargos
+	SVW_RATINGLIST =  7, ///< Ratings of cargos
+	SVW_LOCATION   =  8, ///< 'Location' button
+	SVW_RATINGS    =  9, ///< 'Ratings' button
+	SVW_ACCEPTS    =  9, ///< 'Accepts' button
+	SVW_RENAME     = 10, ///< 'Rename' button
+	SVW_TRAINS     = 11, ///< List of scheduled trains button
 	SVW_ROADVEHS,        ///< List of scheduled road vehs button
 	SVW_PLANES,          ///< List of scheduled planes button
 	SVW_SHIPS,           ///< List of scheduled ships button
@@ -46,4 +53,85 @@ void CheckRedrawStationCoverage(const Window *w);
 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta);
 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta);
 
+enum SortOrder {
+	SO_DESCENDING,
+	SO_ASCENDING
+};
+
+class CargoDataEntry;
+
+enum CargoSortType {
+	ST_AS_GROUPING,    ///< by the same principle the entries are being grouped
+	ST_COUNT,          ///< by amount of cargo
+	ST_STATION_STRING, ///< by station name
+	ST_STATION_ID,     ///< by station id
+	ST_CARGO_ID,       ///< by cargo id
+};
+
+class CargoSorter {
+public:
+	CargoSorter(CargoSortType t = ST_STATION_ID, SortOrder o = SO_ASCENDING) : type(t), order(o) {}
+	CargoSortType GetSortType() {return type;}
+	bool operator()(const CargoDataEntry * cd1, const CargoDataEntry * cd2) const;
+
+private:
+	CargoSortType type;
+	SortOrder order;
+
+	template<class ID>
+	bool SortId(ID st1, ID st2) const;
+	bool SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
+	bool SortStation (StationID st1, StationID st2) const;
+};
+
+typedef std::set<CargoDataEntry *, CargoSorter> CargoDataSet;
+
+class CargoDataEntry {
+public:
+	CargoDataEntry();
+	~CargoDataEntry();
+
+	CargoDataEntry * InsertOrRetrieve(StationID s) {return InsertOrRetrieve<StationID>(s);}
+	CargoDataEntry * InsertOrRetrieve(CargoID car) {return InsertOrRetrieve<CargoID>(car);}
+	void Update(uint count);
+
+	void Remove(StationID s) {CargoDataEntry t(s); Remove(&t);}
+	void Remove(CargoID c) {CargoDataEntry t(c); Remove(&t);}
+
+	CargoDataEntry * Retrieve(StationID s) const {CargoDataEntry t(s); return Retrieve(subentries->find(&t));}
+	CargoDataEntry * Retrieve(CargoID c) const {CargoDataEntry t(c);return Retrieve(subentries->find(&t));}
+
+	void Resort(CargoSortType type, SortOrder order);
+
+	StationID GetStation() const {return station;}
+	CargoID GetCargo() const {return cargo;}
+	uint GetCount() const {return count;}
+	CargoDataEntry * GetParent() const {return parent;}
+	uint Size() const {return size;}
+
+	CargoDataSet::iterator Begin() const {return subentries->begin();}
+	CargoDataSet::iterator End() const {return subentries->end();}
+
+	void Clear();
+private:
+
+	CargoDataEntry(StationID st, uint c, CargoDataEntry * p);
+	CargoDataEntry(CargoID car, uint c, CargoDataEntry * p);
+	CargoDataEntry(StationID s);
+	CargoDataEntry(CargoID c);
+	CargoDataEntry * Retrieve(CargoDataSet::iterator i) const;
+	template<class ID>
+	CargoDataEntry * InsertOrRetrieve(ID s);
+	void Remove(CargoDataEntry * comp);
+	void IncrementSize();
+	CargoDataEntry * parent;
+	const union {
+		StationID station;
+		CargoID cargo;
+	};
+	uint size;
+	uint count;
+	CargoDataSet * subentries;
+};
+
 #endif /* STATION_GUI_H */
diff --git a/src/table/settings.h b/src/table/settings.h
index 745c100..3e7da71 100644
--- a/src/table/settings.h
+++ b/src/table/settings.h
@@ -452,10 +452,24 @@ const SettingDesc _settings[] = {
 	     SDT_VAR(GameSettings, economy.dist_local_authority,         SLE_UINT8,                     0, 0,    20,     5,      60, 0, STR_NULL,                                  NULL),
 	     SDT_VAR(GameSettings, pf.wait_oneway_signal,                SLE_UINT8,                     0, 0,    15,     2,     255, 0, STR_NULL,                                  NULL),
 	     SDT_VAR(GameSettings, pf.wait_twoway_signal,                SLE_UINT8,                     0, 0,    41,     2,     255, 0, STR_NULL,                                  NULL),
+	 SDT_CONDVAR(GameSettings, economy.moving_average_unit,         SLE_UINT16, MOVING_AVERAGE_SV, SL_MAX_VERSION, 0, 0, 1, 1, 4096, 1,	STR_CONFIG_SETTING_AVERAGE_UNIT,   NULL),
+	 SDT_CONDVAR(GameSettings, economy.moving_average_length,       SLE_UINT16, MOVING_AVERAGE_SV, SL_MAX_VERSION, 0, 0, 64, 1, 4096, 4, STR_CONFIG_SETTING_AVERAGE_LENGTH,    NULL),
+    SDT_CONDBOOL(GameSettings, economy.cap_ratings_by_component,         SUPPLY_SV, SL_MAX_VERSION, 0, 0,  true,                    STR_CONFIG_SETTING_CAP_RATING,             NULL),
 	 SDT_CONDVAR(GameSettings, economy.town_noise_population[0],    SLE_UINT16, 96, SL_MAX_VERSION, 0, 0,   800,   200,   65535, 0, STR_NULL,                                  NULL),
 	 SDT_CONDVAR(GameSettings, economy.town_noise_population[1],    SLE_UINT16, 96, SL_MAX_VERSION, 0, 0,  2000,   400,   65535, 0, STR_NULL,                                  NULL),
 	 SDT_CONDVAR(GameSettings, economy.town_noise_population[2],    SLE_UINT16, 96, SL_MAX_VERSION, 0, 0,  4000,   800,   65535, 0, STR_NULL,                                  NULL),
 
+	 SDT_CONDVAR(GameSettings, linkgraph.recalc_interval,           SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION,  0, 0,16, 1, 4096, 1, STR_CONFIG_SETTING_LINKGRAPH_INTERVAL,     NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_pax,                 SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION,0,MS,DT_SYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_PAX, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_mail,                SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_SYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_MAIL, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_express,      SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_ANTISYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_EXPRESS, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_armoured,        SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_SYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_ARMOURED, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_default,      SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_ANTISYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_DEFAULT, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.accuracy,                   SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION, 0, 0,16,  2,      64, 1, STR_CONFIG_SETTING_LINKGRAPH_ACCURACY,     NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_distance,            SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION, 0, 0,100, 0,     255, 5, STR_CONFIG_SETTING_DEMAND_DISTANCE,        NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_size,                SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION, 0, 0,100, 0,     100, 5, STR_CONFIG_SETTING_DEMAND_SIZE,            NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.short_path_saturation,      SLE_UINT8,  MCF_SV, SL_MAX_VERSION, 0, 0,80,    0,     250, 5, STR_CONFIG_SETTING_SHORT_PATH_SATURATION,  NULL),
+
 	 SDT_CONDVAR(GameSettings, pf.wait_for_pbs_path,                 SLE_UINT8,100, SL_MAX_VERSION, 0, 0,    30,     2,     255, 0, STR_NULL,                                  NULL),
 	SDT_CONDBOOL(GameSettings, pf.reserve_paths,                               100, SL_MAX_VERSION, 0, 0, false,                    STR_NULL,                                  NULL),
 	 SDT_CONDVAR(GameSettings, pf.path_backoff_interval,             SLE_UINT8,100, SL_MAX_VERSION, 0, 0,    20,     1,     255, 0, STR_NULL,                                  NULL),
@@ -602,6 +616,9 @@ const SettingDesc _settings[] = {
 	  SDTC_VAR(gui.developer,                 SLE_UINT8, S,  0,     1,        0,        2, 0, STR_NULL,                                       NULL),
 	  SDTC_VAR(gui.console_backlog_timeout,  SLE_UINT16, S,  0,   100,       10,    65500, 0, STR_NULL,                                       NULL),
 	  SDTC_VAR(gui.console_backlog_length,   SLE_UINT16, S,  0,   100,       10,    65500, 0, STR_NULL,                                       NULL),
+	  SDTC_VAR(gui.station_gui_group_order,   SLE_UINT8, S,  0,     0,        0,        5, 1, STR_NULL,                                       NULL),
+	  SDTC_VAR(gui.station_gui_sort_by,       SLE_UINT8, S,  0,     0,        0,        1, 1, STR_NULL,                                       NULL),
+	  SDTC_VAR(gui.station_gui_sort_order,    SLE_UINT8, S,  0,     0,        0,        1, 1, STR_NULL,                                       NULL),
 #ifdef ENABLE_NETWORK
 	  SDTC_VAR(gui.network_chat_box_width,   SLE_UINT16, S,  0,   700,      200,    65535, 0, STR_NULL,                                       NULL),
 	  SDTC_VAR(gui.network_chat_box_height,   SLE_UINT8, S,  0,    25,        5,      255, 0, STR_NULL,                                       NULL),
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index 28c2fe0..4c416bb 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -430,6 +430,56 @@ static void MakeTownHouseBigger(TileIndex tile)
 	if (flags & BUILDING_HAS_4_TILES) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 1));
 }
 
+static inline void AddAcceptedCargoSetMask(CargoID cargo, uint amount, CargoArray &acceptance, uint32 *always_accepted)
+{
+	if (cargo == CT_INVALID || amount == 0) return;
+	acceptance[cargo] += amount;
+	SetBit(*always_accepted, cargo);
+}
+
+static void AddAcceptedCargo_Town(TileIndex tile, CargoArray &acceptance, uint32 *always_accepted)
+{
+	const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
+	CargoID accepts[3];
+
+	/* Set the initial accepted cargo types */
+	for (uint8 i = 0; i < lengthof(accepts); i++) {
+		accepts[i] = hs->accepts_cargo[i];
+	}
+
+	/* Check for custom accepted cargo types */
+	if (HasBit(hs->callback_mask, CBM_HOUSE_ACCEPT_CARGO)) {
+		uint16 callback = GetHouseCallback(CBID_HOUSE_ACCEPT_CARGO, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
+		if (callback != CALLBACK_FAILED) {
+			/* Replace accepted cargo types with translated values from callback */
+			accepts[0] = GetCargoTranslation(GB(callback,  0, 5), hs->grffile);
+			accepts[1] = GetCargoTranslation(GB(callback,  5, 5), hs->grffile);
+			accepts[2] = GetCargoTranslation(GB(callback, 10, 5), hs->grffile);
+		}
+	}
+
+	/* Check for custom cargo acceptance */
+	if (HasBit(hs->callback_mask, CBM_HOUSE_CARGO_ACCEPTANCE)) {
+		uint16 callback = GetHouseCallback(CBID_HOUSE_CARGO_ACCEPTANCE, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
+		if (callback != CALLBACK_FAILED) {
+			AddAcceptedCargoSetMask(accepts[0], GB(callback, 0, 4), acceptance, always_accepted);
+			AddAcceptedCargoSetMask(accepts[1], GB(callback, 4, 4), acceptance, always_accepted);
+			if (_settings_game.game_creation.landscape != LT_TEMPERATE && HasBit(callback, 12)) {
+				/* The 'S' bit indicates food instead of goods */
+				AddAcceptedCargoSetMask(CT_FOOD, GB(callback, 8, 4), acceptance, always_accepted);
+			} else {
+				AddAcceptedCargoSetMask(accepts[2], GB(callback, 8, 4), acceptance, always_accepted);
+			}
+			return;
+		}
+	}
+
+	/* No custom acceptance, so fill in with the default values */
+	for (uint8 i = 0; i < lengthof(accepts); i++) {
+		AddAcceptedCargoSetMask(accepts[i], hs->cargo_acceptance[i], acceptance, always_accepted);
+	}
+}
+
 /**
  * Tile callback function.
  *
@@ -438,6 +488,9 @@ static void MakeTownHouseBigger(TileIndex tile)
  */
 static void TileLoop_Town(TileIndex tile)
 {
+	uint dummy;
+	AddAcceptedCargo_Town(tile, GlobalCargoAcceptance::inst.CurrentLoop(), &dummy);
+
 	HouseID house_id = GetHouseType(tile);
 
 	/* NewHouseTileLoop returns false if Callback 21 succeeded, i.e. the house
@@ -587,56 +640,6 @@ static void AddProducedCargo_Town(TileIndex tile, CargoArray &produced)
 	}
 }
 
-static inline void AddAcceptedCargoSetMask(CargoID cargo, uint amount, CargoArray &acceptance, uint32 *always_accepted)
-{
-	if (cargo == CT_INVALID || amount == 0) return;
-	acceptance[cargo] += amount;
-	SetBit(*always_accepted, cargo);
-}
-
-static void AddAcceptedCargo_Town(TileIndex tile, CargoArray &acceptance, uint32 *always_accepted)
-{
-	const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
-	CargoID accepts[3];
-
-	/* Set the initial accepted cargo types */
-	for (uint8 i = 0; i < lengthof(accepts); i++) {
-		accepts[i] = hs->accepts_cargo[i];
-	}
-
-	/* Check for custom accepted cargo types */
-	if (HasBit(hs->callback_mask, CBM_HOUSE_ACCEPT_CARGO)) {
-		uint16 callback = GetHouseCallback(CBID_HOUSE_ACCEPT_CARGO, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
-		if (callback != CALLBACK_FAILED) {
-			/* Replace accepted cargo types with translated values from callback */
-			accepts[0] = GetCargoTranslation(GB(callback,  0, 5), hs->grffile);
-			accepts[1] = GetCargoTranslation(GB(callback,  5, 5), hs->grffile);
-			accepts[2] = GetCargoTranslation(GB(callback, 10, 5), hs->grffile);
-		}
-	}
-
-	/* Check for custom cargo acceptance */
-	if (HasBit(hs->callback_mask, CBM_HOUSE_CARGO_ACCEPTANCE)) {
-		uint16 callback = GetHouseCallback(CBID_HOUSE_CARGO_ACCEPTANCE, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
-		if (callback != CALLBACK_FAILED) {
-			AddAcceptedCargoSetMask(accepts[0], GB(callback, 0, 4), acceptance, always_accepted);
-			AddAcceptedCargoSetMask(accepts[1], GB(callback, 4, 4), acceptance, always_accepted);
-			if (_settings_game.game_creation.landscape != LT_TEMPERATE && HasBit(callback, 12)) {
-				/* The 'S' bit indicates food instead of goods */
-				AddAcceptedCargoSetMask(CT_FOOD, GB(callback, 8, 4), acceptance, always_accepted);
-			} else {
-				AddAcceptedCargoSetMask(accepts[2], GB(callback, 8, 4), acceptance, always_accepted);
-			}
-			return;
-		}
-	}
-
-	/* No custom acceptance, so fill in with the default values */
-	for (uint8 i = 0; i < lengthof(accepts); i++) {
-		AddAcceptedCargoSetMask(accepts[i], hs->cargo_acceptance[i], acceptance, always_accepted);
-	}
-}
-
 static void GetTileDesc_Town(TileIndex tile, TileDesc *td)
 {
 	const HouseID house = GetHouseType(tile);
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index 5723f3d..4398740 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -2976,6 +2976,7 @@ int Train::UpdateSpeed()
 
 static void TrainEnterStation(Train *v, StationID station)
 {
+	StationID previous_station = v->last_station_visited;
 	v->last_station_visited = station;
 
 	/* check if a train ever visited this station before */
@@ -2992,7 +2993,7 @@ static void TrainEnterStation(Train *v, StationID station)
 		AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
 	}
 
-	v->BeginLoading();
+	v->BeginLoading(previous_station);
 
 	StationAnimationTrigger(st, v->tile, STAT_ANIM_TRAIN_ARRIVES);
 }
diff --git a/src/unmovable_cmd.cpp b/src/unmovable_cmd.cpp
index 55fe0a4..12e46cd 100644
--- a/src/unmovable_cmd.cpp
+++ b/src/unmovable_cmd.cpp
@@ -336,6 +336,9 @@ static void TileLoop_Unmovable(TileIndex tile)
 {
 	if (!IsCompanyHQ(tile)) return;
 
+	uint dummy;
+	AddAcceptedCargo_Unmovable(tile, GlobalCargoAcceptance::inst.CurrentLoop(), &dummy);
+
 	/* HQ accepts passenger and mail; but we have to divide the values
 	 * between 4 tiles it occupies! */
 
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 1a72220..7284136 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -576,10 +576,11 @@ void Vehicle::PreDestructor()
 	if (CleaningPool()) return;
 
 	if (Station::IsValidID(this->last_station_visited)) {
-		Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
+		Station *st = Station::Get(this->last_station_visited);
+		st->loading_vehicles.remove(this);
 
 		HideFillingPercent(&this->fill_percent_te_id);
-
+		this->CancelReservation(INVALID_STATION, st);
 		delete this->cargo_payment;
 	}
 
@@ -1542,7 +1543,7 @@ uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
 }
 
 
-void Vehicle::BeginLoading()
+void Vehicle::BeginLoading(StationID last_station_id)
 {
 	assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
 
@@ -1562,9 +1563,25 @@ void Vehicle::BeginLoading()
 		current_order.MakeLoading(false);
 	}
 
-	Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
+	StationID curr_station_id = this->last_station_visited;
+	Station * curr_station = Station::Get(curr_station_id);
+	curr_station->loading_vehicles.push_back(this);
+
+	StationID next_station_id = INVALID_STATION;
+	OrderList * orders = this->orders.list;
+	if (orders != NULL) {
+		next_station_id = orders->GetNextStoppingStation(this->cur_order_index, this->type == VEH_ROAD || this->type == VEH_TRAIN);
+	}
+
+	if (last_station_id != INVALID_STATION && last_station_id != curr_station_id) {
+		IncreaseStats(Station::Get(last_station_id), this, curr_station_id, false);
+	}
 
-	PrepareUnload(this);
+	if (next_station_id != INVALID_STATION && next_station_id != curr_station_id) {
+		IncreaseStats(curr_station, this, next_station_id, true);
+	}
+
+	PrepareUnload(curr_station, this, next_station_id);
 
 	SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
 	SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
@@ -1576,19 +1593,49 @@ void Vehicle::BeginLoading()
 	this->MarkDirty();
 }
 
+/**
+ * return all reserved cargo packets to the station
+ * @param st the station where the reserved packets should go.
+ */
+void Vehicle::CancelReservation(StationID next, Station *st) {
+	for(Vehicle *v = this; v != NULL; v = v->next) {
+		VehicleCargoList &cargo = v->cargo;
+		if (cargo.ReservedCount() > 0) {
+			DEBUG(misc, 1, "cancelling cargo reservation");
+			GoodsEntry &ge = st->goods[v->cargo_type];
+			cargo.Unreserve(next, &ge.cargo);
+			SetBit(ge.acceptance_pickup, GoodsEntry::PICKUP);
+		}
+	}
+}
+
+
 void Vehicle::LeaveStation()
 {
 	assert(current_order.IsType(OT_LOADING));
-
+	Station *st = Station::Get(this->last_station_visited);
 	delete this->cargo_payment;
 
 	/* Only update the timetable if the vehicle was supposed to stop here. */
 	if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
 
 	current_order.MakeLeaveStation();
-	Station *st = Station::Get(this->last_station_visited);
 	st->loading_vehicles.remove(this);
 
+
+	OrderList * orders = this->orders.list;
+	if (orders != NULL) {
+		StationID next_station_id = orders->GetNextStoppingStation(this->cur_order_index, this->type == VEH_ROAD || this->type == VEH_TRAIN);
+		this->CancelReservation(next_station_id, st);
+		if (next_station_id != INVALID_STATION && next_station_id != this->last_station_visited) {
+			DecreaseFrozen(st, this, next_station_id);
+		}
+	} else {
+		this->CancelReservation(INVALID_STATION, st);
+		DEBUG(misc, 0, "orders are NULL");
+		RecalcFrozen(st);
+	}
+
 	HideFillingPercent(&this->fill_percent_te_id);
 
 	if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index bea2485..6d9659f 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -193,7 +193,8 @@ public:
 	/** We want to 'destruct' the right class. */
 	virtual ~Vehicle();
 
-	void BeginLoading();
+	void BeginLoading(StationID last_station_id);
+	void CancelReservation(StationID next, Station *st);
 	void LeaveStation();
 
 	/**
