diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml
index d464162e281cb28dff627c31e3046296a6568c23..ee2b98f32d9feb9b7237888266278c79a959fdb0 100644
--- a/indra/newview/app_settings/settings_alchemy.xml
+++ b/indra/newview/app_settings/settings_alchemy.xml
@@ -299,6 +299,17 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
+    <key>AlchemyMinimapTile</key>
+    <map>
+      <key>Comment</key>
+      <string>Use world map tile as minimap image</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
     <key>AlchemyMotionResetsCamera</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 16dc05910ca134b5a27850d2609fbcee8acda033..f210336a93a3ff09fd7fbc2e94cc6b8551d29b9c 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -48,6 +48,7 @@
 #include "llappviewer.h" // for gDisconnected
 #include "llcallingcard.h" // LLAvatarTracker
 #include "llfloaterworldmap.h"
+#include "llparcel.h"
 #include "lltracker.h"
 #include "llsurface.h"
 #include "llviewercamera.h"
@@ -56,6 +57,8 @@
 #include "llviewertexturelist.h"
 #include "llviewermenu.h"
 #include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
 #include "llviewerregion.h"
 #include "llviewerwindow.h"
 #include "llworld.h"
@@ -77,7 +80,7 @@ const F32 DOT_SCALE = 0.75f;
 const F32 MIN_PICK_SCALE = 2.f;
 const S32 MOUSE_DRAG_SLOP = 2;		// How far the mouse needs to move before we think it's a drag
 
-const F64 COARSEUPDATE_MAX_Z = 1020.0f;
+const F64 COARSEUPDATE_MAX_Z = 1020.0;
 
 LLNetMap::LLNetMap (const Params & p)
 :	LLUICtrl (p),
@@ -167,6 +170,7 @@ void LLNetMap::draw()
 	static LLUIColor map_frustum_color = LLUIColorTable::instance().getColor("MapFrustumColor", LLColor4::white);
 	static LLUIColor map_frustum_rotating_color = LLUIColorTable::instance().getColor("MapFrustumRotatingColor", LLColor4::white);
 	
+	static const LLCachedControl<bool> use_world_map_image(gSavedSettings, "AlchemyMinimapTile", true);
 	if (mObjectImagep.isNull())
 	{
 		createObjectImage();
@@ -234,6 +238,7 @@ void LLNetMap::draw()
 			LLVector3 rel_region_pos = origin_agent - gAgentCamera.getCameraPositionAgent();
 			F32 relative_x = (rel_region_pos.mV[0] / region_width) * mScale;
 			F32 relative_y = (rel_region_pos.mV[1] / region_width) * mScale;
+			const F32 real_width(regionp->getWidth());
 
 			// background region rectangle
 			F32 bottom =	relative_y;
@@ -254,40 +259,76 @@ void LLNetMap::draw()
 			{
 				gGL.color4f(1.f, 0.5f, 0.5f, 1.f);
 			}
-
-
-			// Draw using texture.
-			gGL.getTexUnit(0)->bind(regionp->getLand().getSTexture());
-			gGL.begin(LLRender::QUADS);
-				gGL.texCoord2f(0.f, 1.f);
-				gGL.vertex2f(left, top);
-				gGL.texCoord2f(0.f, 0.f);
-				gGL.vertex2f(left, bottom);
-				gGL.texCoord2f(1.f, 0.f);
-				gGL.vertex2f(right, bottom);
-				gGL.texCoord2f(1.f, 1.f);
-				gGL.vertex2f(right, top);
-			gGL.end();
-
-			// Draw water
-			gGL.setAlphaRejectSettings(LLRender::CF_GREATER, ABOVE_WATERLINE_ALPHA / 255.f);
+			
+			// <alchemy>
+			bool render_land_textures = true;
+			if (use_world_map_image)
+			{
+				const LLViewerRegion::tex_matrix_t& tiles(regionp->getWorldMapTiles());
+				for (S32 i(0), scaled_width(real_width / region_width), square_width(scaled_width * scaled_width);
+					 i < square_width; ++i)
+				{
+					const F32 y(i / scaled_width);
+					const F32 x(i - y * scaled_width);
+					const F32 local_left(left + x * mScale);
+					const F32 local_right(local_left + mScale);
+					const F32 local_bottom(bottom + y * mScale);
+					const F32 local_top(local_bottom + mScale);
+					LLViewerTexture* img = tiles[x * scaled_width + y];
+					if (img && img->hasGLTexture())
+					{
+						gGL.getTexUnit(0)->bind(img);
+						gGL.begin(LLRender::QUADS);
+							gGL.texCoord2f(0.f, 1.f);
+							gGL.vertex2f(local_left, local_top);
+							gGL.texCoord2f(0.f, 0.f);
+							gGL.vertex2f(local_left, local_bottom);
+							gGL.texCoord2f(1.f, 0.f);
+							gGL.vertex2f(local_right, local_bottom);
+							gGL.texCoord2f(1.f, 1.f);
+							gGL.vertex2f(local_right, local_top);
+						gGL.end();
+						img->setBoostLevel(LLViewerTexture::BOOST_MAP_VISIBLE);
+						render_land_textures = false;
+					}
+				}
+			}
+			
+			if (render_land_textures)
 			{
-				if (regionp->getLand().getWaterTexture())
+				// Draw using texture.
+				gGL.getTexUnit(0)->bind(regionp->getLand().getSTexture());
+				gGL.begin(LLRender::QUADS);
+					gGL.texCoord2f(0.f, 1.f);
+					gGL.vertex2f(left, top);
+					gGL.texCoord2f(0.f, 0.f);
+					gGL.vertex2f(left, bottom);
+					gGL.texCoord2f(1.f, 0.f);
+					gGL.vertex2f(right, bottom);
+					gGL.texCoord2f(1.f, 1.f);
+					gGL.vertex2f(right, top);
+				gGL.end();
+
+				// Draw water
+				gGL.setAlphaRejectSettings(LLRender::CF_GREATER, ABOVE_WATERLINE_ALPHA / 255.f);
 				{
-					gGL.getTexUnit(0)->bind(regionp->getLand().getWaterTexture());
-					gGL.begin(LLRender::QUADS);
-						gGL.texCoord2f(0.f, 1.f);
-						gGL.vertex2f(left, top);
-						gGL.texCoord2f(0.f, 0.f);
-						gGL.vertex2f(left, bottom);
-						gGL.texCoord2f(1.f, 0.f);
-						gGL.vertex2f(right, bottom);
-						gGL.texCoord2f(1.f, 1.f);
-						gGL.vertex2f(right, top);
-					gGL.end();
+					if (regionp->getLand().getWaterTexture())
+					{
+						gGL.getTexUnit(0)->bind(regionp->getLand().getWaterTexture());
+						gGL.begin(LLRender::QUADS);
+							gGL.texCoord2f(0.f, 1.f);
+							gGL.vertex2f(left, top);
+							gGL.texCoord2f(0.f, 0.f);
+							gGL.vertex2f(left, bottom);
+							gGL.texCoord2f(1.f, 0.f);
+							gGL.vertex2f(right, bottom);
+							gGL.texCoord2f(1.f, 1.f);
+							gGL.vertex2f(right, top);
+						gGL.end();
+					}
 				}
+				gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 			}
-			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 		}
 
 		// Redraw object layer periodically
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 04725e2f2307d902d807ba2292b0e7c7f5c4dd4e..723affb695ae887c3a9819a01d8519fd0091da7d 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -661,7 +661,10 @@ LLViewerRegion::~LLViewerRegion()
 	saveObjectCache();
 
 	delete mImpl;
-	mImpl = NULL;
+	mImpl = nullptr;
+
+	for (LLPointer<LLViewerTexture> tile : mWorldMapTiles)
+		tile->setBoostLevel(LLViewerTexture::BOOST_NONE);
 }
 
 /*virtual*/ 
@@ -3322,3 +3325,44 @@ U32 LLViewerRegion::getMaxMaterialsPerTransaction() const
 
 
 
+std::string LLViewerRegion::getMapServerURL() const
+{
+	std::string url;
+	if (mSimulatorFeatures.has("OpenSimExtras")
+		&& mSimulatorFeatures["OpenSimExtras"].has("map-server-url"))
+	{
+		url = mSimulatorFeatures["OpenSimExtras"]["map-server-url"].asString();
+	}
+	else
+	{
+		url = gSavedSettings.getString("CurrentMapServerURL");
+	}
+	return url;
+}
+
+
+const LLViewerRegion::tex_matrix_t& LLViewerRegion::getWorldMapTiles() const
+{
+	if (mWorldMapTiles.empty())
+	{
+		U32 gridX, gridY;
+		grid_from_region_handle(mHandle, &gridX, &gridY);
+		U32 totalX(getWidth() / REGION_WIDTH_U32);
+		if (!totalX) ++totalX; // If this region is too small, still get an image.
+		// *TODO: Non-square regions?
+		//U32 totalY(getLength()/REGION_WIDTH_U32);
+		//if (!totalY) ++totalY; // If this region is too small, still get an image.
+		const U32 totalY(totalX);
+		mWorldMapTiles.reserve(totalX * totalY);
+		for (U32 x = 0; x != totalX; ++x)
+			for (U32 y = 0; y != totalY; ++y)
+			{
+				const std::string map_url = getMapServerURL().append(llformat("map-1-%d-%d-objects.jpg", gridX + x, gridY + y));
+				LLPointer<LLViewerTexture> tex(LLViewerTextureManager::getFetchedTextureFromUrl(map_url, FTT_MAP_TILE, TRUE,
+											   LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE));
+				mWorldMapTiles.push_back(tex);
+				tex->setBoostLevel(LLViewerTexture::BOOST_MAP);
+			}
+	}
+	return mWorldMapTiles;
+}
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index c79008a7d9c7a4e351d80d6a01b8dbaca2221947..4596f5b01712b17ab0c3acbcbeaf8066ed9da77c 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -53,6 +53,7 @@ const U32 REGION_HANDSHAKE_SUPPORTS_SELF_APPEARANCE = 1U << 2;
 class LLEventPoll;
 class LLVLComposition;
 class LLViewerObject;
+class LLViewerTexture;
 class LLMessageSystem;
 class LLNetMap;
 class LLViewerParcelOverlay;
@@ -71,7 +72,7 @@ class LLViewerRegionImpl;
 class LLViewerOctreeGroup;
 class LLVOCachePartition;
 
-class LLViewerRegion: public LLCapabilityProvider // implements this interface
+class LLViewerRegion final : public LLCapabilityProvider // implements this interface
 {
 public:
 	//MUST MATCH THE ORDER OF DECLARATION IN CONSTRUCTOR
@@ -390,6 +391,12 @@ class LLViewerRegion: public LLCapabilityProvider // implements this interface
 
 	static BOOL isNewObjectCreationThrottleDisabled() {return sNewObjectCreationThrottle < 0;}
 
+	/// Hypergrid map server url
+	std::string getMapServerURL() const;
+
+	typedef std::vector<LLPointer<LLViewerTexture> > tex_matrix_t;
+	const tex_matrix_t& getWorldMapTiles() const;
+
 private:
 	void addToVOCacheTree(LLVOCacheEntry* entry);
 	LLViewerObject* addNewObject(LLVOCacheEntry* entry);
@@ -552,6 +559,8 @@ class LLViewerRegion: public LLCapabilityProvider // implements this interface
 	LLFrameTimer mMaterialsCapThrottleTimer;
 	LLFrameTimer mRenderInfoRequestTimer;
 	LLFrameTimer mRenderInfoReportTimer;
+
+	mutable tex_matrix_t mWorldMapTiles;
 };
 
 inline BOOL LLViewerRegion::getRegionProtocol(U64 protocol) const