diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp
index 4dab240247..08999edb3c 100644
--- a/src/clear_cmd.cpp
+++ b/src/clear_cmd.cpp
@@ -16,6 +16,7 @@
 #include "tunnel_map.h"
 #include "bridge_map.h"
 #include "bridge.h"
+#include "landscape.h"
 #include "variables.h"
 #include "table/sprites.h"
 #include "unmovable_map.h"
@@ -620,7 +621,7 @@ void TileLoopClearHelper(TileIndex tile)
 /* convert into snowy tiles */
 static void TileLoopClearAlps(TileIndex tile)
 {
-	int k = GetTileZ(tile) - _opt.snow_line + TILE_HEIGHT;
+	int k = GetTileZ(tile) - GetSnowLine() + TILE_HEIGHT;
 
 	if (k < 0) { // well below the snow line
 		if (!IsClearGround(tile, CLEAR_SNOW)) return;
diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp
index 2352c52da0..f237dcf7b9 100644
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -12,6 +12,7 @@
 #include "table/sprites.h"
 #include "map.h"
 #include "tile.h"
+#include "landscape.h"
 #include "viewport.h"
 #include "command.h"
 #include "industry.h"
@@ -814,7 +815,7 @@ static void PlantFarmField(TileIndex tile, IndustryID industry)
 	int type;
 
 	if (_opt.landscape == LT_HILLY) {
-		if (GetTileZ(tile) + TILE_HEIGHT * 2 >= _opt.snow_line)
+		if (GetTileZ(tile) + TILE_HEIGHT * 2 >= GetSnowLine())
 			return;
 	}
 
@@ -1016,7 +1017,7 @@ static bool CheckNewIndustry_NULL(TileIndex tile)
 static bool CheckNewIndustry_Forest(TileIndex tile)
 {
 	if (_opt.landscape == LT_HILLY) {
-		if (GetTileZ(tile) < _opt.snow_line + TILE_HEIGHT * 2U) {
+		if (GetTileZ(tile) < HighestSnowLine() + TILE_HEIGHT * 2U) {
 			_error_message = STR_4831_FOREST_CAN_ONLY_BE_PLANTED;
 			return false;
 		}
@@ -1048,7 +1049,7 @@ static bool CheckNewIndustry_OilRig(TileIndex tile)
 static bool CheckNewIndustry_Farm(TileIndex tile)
 {
 	if (_opt.landscape == LT_HILLY) {
-		if (GetTileZ(tile) + TILE_HEIGHT * 2 >= _opt.snow_line) {
+		if (GetTileZ(tile) + TILE_HEIGHT * 2 >= HighestSnowLine()) {
 			_error_message = STR_0239_SITE_UNSUITABLE;
 			return false;
 		}
diff --git a/src/landscape.cpp b/src/landscape.cpp
index 7c3ada82f2..9ed7d9df1d 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -5,6 +5,7 @@
 #include "bridge_map.h"
 #include "heightmap.h"
 #include "clear_map.h"
+#include "date.h"
 #include "functions.h"
 #include "map.h"
 #include "player.h"
@@ -14,6 +15,7 @@
 #include <stdarg.h>
 #include "viewport.h"
 #include "command.h"
+#include "landscape.h"
 #include "vehicle.h"
 #include "variables.h"
 #include "void_map.h"
@@ -61,6 +63,7 @@ const Slope _inclined_tileh[] = {
 	SLOPE_NWS, SLOPE_WSE, SLOPE_SEN, SLOPE_ENW
 };
 
+SnowLine *_snow_line = NULL;
 
 uint GetPartialZ(int x, int y, Slope corners)
 {
@@ -302,6 +305,62 @@ void GetTileDesc(TileIndex tile, TileDesc *td)
 	_tile_type_procs[GetTileType(tile)]->get_tile_desc_proc(tile, td);
 }
 
+/**
+ * Has a snow line table already been loaded.
+ * @return true if the table has been loaded already.
+ */
+bool IsSnowLineSet(void)
+{
+	return _snow_line != NULL;
+}
+
+/**
+ * Set a variable snow line, as loaded from a newgrf file.
+ * @param table the 12 * 32 byte table containing the snowline for each day
+ */
+void SetSnowLine(byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS])
+{
+	_snow_line = CallocT<SnowLine>(1);
+	memcpy(_snow_line->table, table, sizeof(_snow_line->table));
+
+	for (uint i = 0; i < SNOW_LINE_MONTHS; i++) {
+		for (uint j = 0; j < SNOW_LINE_DAYS; j++) {
+			_snow_line->highest_value = max(_snow_line->highest_value, table[i][j]);
+		}
+	}
+}
+
+/**
+ * Get the current snow line, either variable or static.
+ * @return the snow line height.
+ */
+byte GetSnowLine(void)
+{
+	if (_snow_line == NULL) return _opt.snow_line;
+
+	YearMonthDay ymd;
+	ConvertDateToYMD(_date, &ymd);
+	return _snow_line->table[ymd.month][ymd.day];
+}
+
+/**
+ * Get the highest possible snow line height, either variable or static.
+ * @return the highest snow line height.
+ */
+byte HighestSnowLine(void)
+{
+	return _snow_line == NULL ? _opt.snow_line : _snow_line->highest_value;
+}
+
+/**
+ * Clear the variable snow line table and free the memory.
+ */
+void ClearSnowLine(void)
+{
+	free(_snow_line);
+	_snow_line = NULL;
+}
+
 /** Clear a piece of landscape
  * @param tile tile to clear
  * @param flags of operation to conduct
diff --git a/src/landscape.h b/src/landscape.h
new file mode 100644
index 0000000000..5cfe80edf0
--- /dev/null
+++ b/src/landscape.h
@@ -0,0 +1,19 @@
+/* $Id$ */
+
+/** @file landscape.h */
+
+enum {
+	SNOW_LINE_MONTHS = 12,
+	SNOW_LINE_DAYS   = 32,
+};
+
+struct SnowLine {
+	byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS];
+	byte highest_value;
+};
+
+bool IsSnowLineSet(void);
+void SetSnowLine(byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS]);
+byte GetSnowLine(void);
+byte HighestSnowLine(void);
+void ClearSnowLine(void);
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 40185e3d7e..6fbcf764ff 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -27,6 +27,7 @@
 #include "fontcache.h"
 #include "date.h"
 #include "currency.h"
+#include "landscape.h"
 #include "sound.h"
 #include "newgrf_config.h"
 #include "newgrf_house.h"
@@ -1510,6 +1511,22 @@ static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, i
 			break;
 
 		case 0x10: // 12 * 32 * B Snow line height table
+			if (numinfo > 1 || IsSnowLineSet()) {
+				grfmsg(1, "GlobalVarChangeInfo: The snowline can only be set once (%d)", numinfo);
+			} else if (len < SNOW_LINE_MONTHS * SNOW_LINE_DAYS) {
+				grfmsg(1, "GlobalVarChangeInfo: Not enough entries set in the snowline table (%d)", len);
+			} else {
+				byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS];
+
+				for (uint i = 0; i < SNOW_LINE_MONTHS; i++) {
+					for (uint j = 0; j < SNOW_LINE_DAYS; j++) {
+						table[i][j] = grf_load_byte(&buf);
+					}
+				}
+				SetSnowLine(table);
+			}
+			break;
+
 		default:
 			ret = true;
 	}
@@ -3973,6 +3990,9 @@ static void ResetNewGRFData()
 	ResetStationClasses();
 	ResetCustomStations();
 
+	/* Reset the snowline table. */
+	ClearSnowLine();
+
 	/* Reset NewGRF files */
 	ResetNewGRF();
 
diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp
index 707825341e..888b5764fc 100644
--- a/src/newgrf_house.cpp
+++ b/src/newgrf_house.cpp
@@ -8,6 +8,7 @@
 #include "variables.h"
 #include "debug.h"
 #include "viewport.h"
+#include "landscape.h"
 #include "date.h"
 #include "town.h"
 #include "town_map.h"
@@ -252,7 +253,7 @@ static uint32 GetTerrainType(TileIndex tile)
 {
 	switch (_opt.landscape) {
 		case LT_DESERT: return GetTropicZone(tile) == TROPICZONE_DESERT ? 1 : 2;
-		case LT_HILLY:  return GetTileZ(tile) >= _opt.snow_line ? 4 : 0;
+		case LT_HILLY:  return GetTileZ(tile) >= GetSnowLine() ? 4 : 0;
 		default:        return 0;
 	}
 }
diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp
index fcde3cc284..7fba72c0f7 100644
--- a/src/newgrf_spritegroup.cpp
+++ b/src/newgrf_spritegroup.cpp
@@ -4,6 +4,7 @@
 #include "openttd.h"
 #include "variables.h"
 #include "macros.h"
+#include "landscape.h"
 #include "oldpool.h"
 #include "newgrf_callbacks.h"
 #include "newgrf_spritegroup.h"
@@ -91,7 +92,7 @@ static inline uint32 GetVariable(const ResolverObject *object, byte variable, by
 		case 0x1A: return UINT_MAX;
 		case 0x1B: return GB(_display_opt, 0, 6);
 		case 0x1C: return object->last_value;
-		case 0x20: return _opt.landscape == LT_HILLY ? _opt.snow_line : 0xFF;
+		case 0x20: return _opt.landscape == LT_HILLY ? GetSnowLine() : 0xFF;
 
 		/* Not a common variable, so evalute the feature specific variables */
 		default: return object->GetVariable(object, variable, parameter, available);
diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp
index cb975f731f..28beec4717 100644
--- a/src/newgrf_station.cpp
+++ b/src/newgrf_station.cpp
@@ -6,6 +6,7 @@
 #include "openttd.h"
 #include "variables.h"
 #include "functions.h"
+#include "landscape.h"
 #include "debug.h"
 #include "sprite.h"
 #include "table/sprites.h"
@@ -370,7 +371,7 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by
 		case 0x40: return GetPlatformInfoHelper(tile, false, false, false);
 		case 0x41: return GetPlatformInfoHelper(tile, true,  false, false);
 		case 0x42: /* Terrain and rail type */
-			return ((_opt.landscape == LT_HILLY && GetTileZ(tile) > _opt.snow_line) ? 4 : 0) |
+			return ((_opt.landscape == LT_HILLY && GetTileZ(tile) > GetSnowLine()) ? 4 : 0) |
 			       (_opt.landscape == LT_DESERT ? GetTropicZone(tile) : 0) |
 			       (GetRailType(tile) << 8);
 		case 0x43: return st->owner; /* Station owner */
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 0bb915ec14..6a3f0a043b 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -12,6 +12,7 @@
 #include "table/sprites.h"
 #include "table/strings.h"
 #include "map.h"
+#include "landscape.h"
 #include "tile.h"
 #include "town_map.h"
 #include "tunnel_map.h"
@@ -1738,7 +1739,7 @@ static void TileLoop_Track(TileIndex tile)
 
 	switch (_opt.landscape) {
 		case LT_HILLY:
-			if (GetTileZ(tile) > _opt.snow_line) {
+			if (GetTileZ(tile) > GetSnowLine()) {
 				new_ground = RAIL_GROUND_ICE_DESERT;
 				goto set_ground;
 			}
diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp
index f5213a7874..fdc03a009f 100644
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -12,6 +12,7 @@
 #include "table/strings.h"
 #include "functions.h"
 #include "map.h"
+#include "landscape.h"
 #include "tile.h"
 #include "town_map.h"
 #include "vehicle.h"
@@ -864,7 +865,7 @@ static void TileLoop_Road(TileIndex tile)
 {
 	switch (_opt.landscape) {
 		case LT_HILLY:
-			if (IsOnSnow(tile) != (GetTileZ(tile) > _opt.snow_line)) {
+			if (IsOnSnow(tile) != (GetTileZ(tile) > GetSnowLine())) {
 				ToggleSnow(tile);
 				MarkTileDirtyByTile(tile);
 			}
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index ec689b8f1d..84166b689c 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -11,6 +11,7 @@
 #include "table/strings.h"
 #include "table/sprites.h"
 #include "map.h"
+#include "landscape.h"
 #include "tile.h"
 #include "town_map.h"
 #include "tunnel_map.h"
@@ -1700,7 +1701,7 @@ static void UpdateTownGrowRate(Town *t)
 	}
 
 	if (_opt.landscape == LT_HILLY) {
-		if (TilePixelHeight(t->xy) >= _opt.snow_line && t->act_food == 0 && t->population > 90)
+		if (TilePixelHeight(t->xy) >= GetSnowLine() && t->act_food == 0 && t->population > 90)
 			return;
 	} else if (_opt.landscape == LT_DESERT) {
 		if (GetTropicZone(t->xy) == TROPICZONE_DESERT && (t->act_food==0 || t->act_water==0) && t->population > 60)
diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp
index ca1af6788e..d52d0b63a6 100644
--- a/src/tree_cmd.cpp
+++ b/src/tree_cmd.cpp
@@ -9,6 +9,7 @@
 #include "table/tree_land.h"
 #include "functions.h"
 #include "map.h"
+#include "landscape.h"
 #include "tile.h"
 #include "tree_map.h"
 #include "viewport.h"
@@ -53,7 +54,7 @@ static void PlaceTree(TileIndex tile, uint32 r)
 		MakeTree(tile, tree, GB(r, 22, 2), min(GB(r, 16, 3), 6), TREE_GROUND_GRASS, 0);
 
 		// above snowline?
-		if (_opt.landscape == LT_HILLY && GetTileZ(tile) > _opt.snow_line) {
+		if (_opt.landscape == LT_HILLY && GetTileZ(tile) > GetSnowLine()) {
 			SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, 3);
 			SetTreeCounter(tile, (TreeGround)GB(r, 24, 3));
 		} else {
@@ -150,7 +151,7 @@ void PlaceTreesRandomly()
 			j = GetTileZ(tile) / TILE_HEIGHT * 2;
 			while (j--) {
 				/* Above snowline more trees! */
-				if (_opt.landscape == LT_HILLY && ht > _opt.snow_line) {
+				if (_opt.landscape == LT_HILLY && ht > GetSnowLine()) {
 					PlaceTreeAtSameHeight(tile, ht);
 					PlaceTreeAtSameHeight(tile, ht);
 				};
@@ -496,7 +497,7 @@ static void TileLoopTreesDesert(TileIndex tile)
 
 static void TileLoopTreesAlps(TileIndex tile)
 {
-	int k = GetTileZ(tile) - _opt.snow_line + TILE_HEIGHT;
+	int k = GetTileZ(tile) - GetSnowLine() + TILE_HEIGHT;
 
 	if (k < 0) {
 		if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) return;
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index 8d04c32116..ae7711f962 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -14,6 +14,7 @@
 #include "table/strings.h"
 #include "functions.h"
 #include "map.h"
+#include "landscape.h"
 #include "tile.h"
 #include "tunnel_map.h"
 #include "unmovable_map.h"
@@ -1178,7 +1179,7 @@ static void TileLoop_TunnelBridge(TileIndex tile)
 	bool snow_or_desert = IsTunnelTile(tile) ? HasTunnelSnowOrDesert(tile) : HasBridgeSnowOrDesert(tile);
 	switch (_opt.landscape) {
 		case LT_HILLY:
-			if (snow_or_desert != (GetTileZ(tile) > _opt.snow_line)) {
+			if (snow_or_desert != (GetTileZ(tile) > GetSnowLine())) {
 				if (IsTunnelTile(tile)) {
 					SetTunnelSnowOrDesert(tile, !snow_or_desert);
 				} else {