Index: frmts/wms/minidriver_tms.h
===================================================================
--- frmts/wms/minidriver_tms.h	(revision 0)
+++ frmts/wms/minidriver_tms.h	(revision 0)
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ * Project:  WMS Client Driver
+ * Purpose:  Implementation of Dataset and RasterBand classes for WMS
+ *           and other similar services.
+ * Author:   Adam Nowacki, nowak@xpam.de
+ *
+ ******************************************************************************
+ * Copyright (c) 2007, Adam Nowacki
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+H_GDALWMSMiniDriverFactory(TMS)
+
+class GDALWMSMiniDriver_TMS : public GDALWMSMiniDriver {
+public:
+    GDALWMSMiniDriver_TMS();
+    virtual ~GDALWMSMiniDriver_TMS();
+
+public:
+    virtual CPLErr Initialize(CPLXMLNode *config);
+    virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps);
+    virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri);
+    virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri);
+
+protected:
+    CPLString m_base_url;
+    CPLString m_dataset;
+    CPLString m_version;
+    CPLString m_format;
+};
Index: frmts/wms/makefile.vc
===================================================================
--- frmts/wms/makefile.vc	(revision 16514)
+++ frmts/wms/makefile.vc	(working copy)
@@ -1,6 +1,6 @@
 
 OBJ	=	cache.obj dataset.obj gdalhttp.obj md5.obj minidriver.obj rasterband.obj stuff.obj wmsdriver.obj \
-		minidriver_wms.obj minidriver_tileservice.obj minidriver_worldwind.obj
+		minidriver_wms.obj minidriver_tileservice.obj minidriver_worldwind.obj minidriver_tms.obj
 
 EXTRAFLAGS = -DHAVE_CURL $(CURL_CFLAGS) $(CURL_INC)
 
Index: frmts/wms/frmt_wms_openstreetmap_tms.xml
===================================================================
--- frmts/wms/frmt_wms_openstreetmap_tms.xml	(revision 0)
+++ frmts/wms/frmt_wms_openstreetmap_tms.xml	(revision 0)
@@ -0,0 +1,20 @@
+<GDAL_WMS>
+    <Service name="TMS">
+        <ServerUrl>http://tile.openstreetmap.org/${z}/${x}/${y}.png</ServerUrl>
+    </Service>
+    <DataWindow>
+        <UpperLeftX>-20037508.34</UpperLeftX>
+        <UpperLeftY>20037508.34</UpperLeftY>
+        <LowerRightX>20037508.34</LowerRightX>
+        <LowerRightY>-20037508.34</LowerRightY>
+        <TileLevel>19</TileLevel>
+	<TileCountX>1</TileCountX>
+	<TileCountY>1</TileCountY>
+        <YOrigin>top</YOrigin>
+    </DataWindow>
+    <Projection>EPSG:900913</Projection>
+    <BlockSizeX>256</BlockSizeX>
+    <BlockSizeY>256</BlockSizeY>
+    <BandsCount>3</BandsCount>
+    <Cache />
+</GDAL_WMS>
Index: frmts/wms/GNUmakefile
===================================================================
--- frmts/wms/GNUmakefile	(revision 16514)
+++ frmts/wms/GNUmakefile	(working copy)
@@ -2,7 +2,7 @@
 include ../../GDALmake.opt
 
 OBJ	=	cache.o dataset.o gdalhttp.o md5.o minidriver.o rasterband.o stuff.o wmsdriver.o \
-		minidriver_wms.o minidriver_tileservice.o minidriver_worldwind.o
+		minidriver_wms.o minidriver_tileservice.o minidriver_worldwind.o minidriver_tms.o
 
 
 CPPFLAGS	:=	$(GDAL_INCLUDE) $(CPPFLAGS) $(CURL_INC)
@@ -14,4 +14,4 @@
 
 install-obj:	$(O_OBJ:.o=.$(OBJ_EXT))
 
-$(OBJ) $(O_OBJ):	gdalhttp.h md5.h minidriver_tileservice.h minidriver_wms.h minidriver_worldwind.h wmsdriver.h
+$(OBJ) $(O_OBJ):	gdalhttp.h md5.h minidriver_tileservice.h minidriver_wms.h minidriver_worldwind.h minidriver_tms.h wmsdriver.h 
Index: frmts/wms/frmt_wms.html
===================================================================
--- frmts/wms/frmt_wms.html	(revision 16514)
+++ frmts/wms/frmt_wms.html	(working copy)
@@ -119,6 +119,10 @@
 			<td class="desc">Can be used to define image size, SizeY = TileCountY * BlockSizeY * 2<sup>TileLevel</sup>. (tiled image sources only, optional, defaults to 0)</td>
 		</tr>
 		<tr>
+			<td class="xml">        &lt;YOrigin&gt;<span class="value">top</span>&lt;/YOrigin&gt;</td>
+			<td class="desc">Can be used to define the position of the Y origin with respect to the tile grid. Possible values are 'top', 'bottom', and 'default', where the default behavior is mini-driver-specific. (TMS mini-driver only, optional, defaults to 'bottom' for TMS)</td>
+		</tr>
+		<tr>
 			<td class="xml">    &lt;/DataWindow&gt;</td>
 			<td class="desc"></td>
 		</tr>
@@ -196,6 +200,69 @@
 		</tr>
 	</table>
 
+<h2>Minidrivers</h2>
+<p>
+  The GDAL WMS driver has support for several internal 'minidrivers', which 
+  allow access to different web mapping services. Each of these services may
+  support a different set of options in the Service block.
+</p>
+<h3>WMS</h3>
+<p>
+  Communications with an OGC WMS server. Has support for both tiled and 
+  untiled requests.
+</p>
+
+<h3>TileService</h3>
+<p>
+  Service to support talking to a WorldWind <a href="http://www.worldwindcentral.com/wiki/TileService">TileService</a>. Access is always tile based.
+</p>
+
+<h3>WorldWind</h3>
+<p>
+  Access to web-based WorldWind tile services. Access is always tile based.
+</p>
+
+<h3>TMS</h3>
+<p>
+  The TMS Minidriver is designed primarily to support the users of the 
+  <a href="http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification">TMS
+  Specification</a>. This service supports only access by tiles.
+</p>
+<p>
+  Because TMS is similar to many other 'x/y/z' flavored services on the web, 
+  this service can also be used to access these services. To use it in this
+  fashion, you can use  replacement variables, of the format ${x}, ${y}, etc.
+</p>
+<p>
+ Supported variables are:
+  <ul>
+    <li>${x} -- x position of the tile</li>
+    <li>${y} -- y position of the tile. This can be either from the top or the bottom of the tileset, based on whether the YOrigin parameter is set to true or false.</li>
+    <li>${z} -- z position of the tile -- zoom level</li>
+    <li>${version} -- version parameter, set in the config file. Defaults to 1.0.0.</li>
+    <li>${format} -- format parameter, set in the config file. Defaults to 'jpg'.</li>
+    <li>${layer} -- layer parameter, set in the config file. Defaults to nothing.</li>
+  </ul>
+</p>
+<p>
+  A typical ServerURL might look like:<br />
+  <tt>http://labs.metacarta.com/wms-c/Basic.py/${version}/${layer}/${z}/${x}/${y}.${format}</tt><br />
+  In order to better suit TMS users, any URL that does not contain "${" will automatically have the string above (after "Basic.py/") appended to their URL.
+</p> 
+<p>
+  The TMS Service has 3 XML configuration elements that are different from
+  other services: <tt>Format</tt> which defaults to <tt>jpg</tt>, <tt>Layer</tt> which has no default, and <tt>Version</tt> which defaults to <tt>1.0.0</tt>.
+</p>
+<p>
+  Additionally, the TMS service respects one additional parameter, at the
+  DataWindow level, which is the YOrigin element. This element should be one of
+  <tt>bottom</tt> (the default in TMS) or <tt>top</tt>, which matches
+  OpenStreetMap and many other popular tile services.
+</p>  
+<p>
+  Two examples of usage of the TMS service are included in the examples below.
+</p>  
+
 <h2>Examples</h2>
 
 <ul>
@@ -225,6 +292,15 @@
 	<pre>gdal_translate -of JPEG -projwin -73.687030 41.262680 -73.686359 41.262345 -outsize 500 250 tileservice_nysdop2004.xml tileservice_nysdop2004.jpg</pre>
 	<img src="http://sydney.freeearthfoundation.com/gdalwms/tileservice_nysdop2004.jpg" alt="example output">
     </li>
+    <li>
+     <a href="frmt_wms_openstreetmap_tms.xml">OpenStreetMap TMS Service Example</a>: Connect to OpenStreetMap tile service. Note that this file takes advantage of the tile cache; more information about configuring the tile cache settings is available above.<br />
+     <tt>gdal_translate -of PNG -outsize 512 512 frmt_wms_openstreetmap_tms.xml openstreetmap.png</tt>
+    </li>
+
+    <li>
+     <a href="frmt_wms_metacarta_tms.xml">MetaCarta TMS Layer Example</a>, accessing the default MetaCarta TMS layer.<br />
+          <tt>gdal_translate -of PNG -outsize 512 256 frmt_wms_metacarta_tms.xml metacarta.png</tt>
+    </li>
 </ul>
 
 <p>See Also:</p>
Index: frmts/wms/frmt_wms_metacarta_tms.xml
===================================================================
--- frmts/wms/frmt_wms_metacarta_tms.xml	(revision 0)
+++ frmts/wms/frmt_wms_metacarta_tms.xml	(revision 0)
@@ -0,0 +1,20 @@
+<GDAL_WMS>
+    <Service name="TMS">
+        <ServerUrl>http://labs.metacarta.com/wms-c/Basic.py</ServerUrl>
+        <Layer>basic</Layer>
+        <Format>png</Format>
+    </Service>
+    <DataWindow>
+        <UpperLeftX>-180.0</UpperLeftX>
+        <UpperLeftY>90.0</UpperLeftY>
+        <LowerRightX>180.0</LowerRightX>
+        <LowerRightY>-90.0</LowerRightY>
+        <TileLevel>19</TileLevel>
+	<TileCountX>2</TileCountX>
+	<TileCountY>1</TileCountY>
+    </DataWindow>
+    <Projection>EPSG:4326</Projection>
+    <BlockSizeX>256</BlockSizeX>
+    <BlockSizeY>256</BlockSizeY>
+    <BandsCount>3</BandsCount>
+</GDAL_WMS>
Index: frmts/wms/wmsdriver.cpp
===================================================================
--- frmts/wms/wmsdriver.cpp	(revision 16514)
+++ frmts/wms/wmsdriver.cpp	(working copy)
@@ -80,5 +80,6 @@
         mdm->Register(new GDALWMSMiniDriverFactory_WMS());
         mdm->Register(new GDALWMSMiniDriverFactory_TileService());
         mdm->Register(new GDALWMSMiniDriverFactory_WorldWind());
+        mdm->Register(new GDALWMSMiniDriverFactory_TMS());
     }
 }
Index: frmts/wms/dataset.cpp
===================================================================
--- frmts/wms/dataset.cpp	(revision 16514)
+++ frmts/wms/dataset.cpp	(working copy)
@@ -154,6 +154,7 @@
             const char *tlevel = CPLGetXMLValue(data_window_node, "TileLevel", "");
             const char *str_tile_count_x = CPLGetXMLValue(data_window_node, "TileCountX", "1");
             const char *str_tile_count_y = CPLGetXMLValue(data_window_node, "TileCountY", "1");
+            const char *y_origin = CPLGetXMLValue(data_window_node, "YOrigin", "default");
 
             if (ret == CE_None) {
                 if ((ulx[0] != '\0') && (uly[0] != '\0') && (lrx[0] != '\0') && (lry[0] != '\0')) {
@@ -208,6 +209,20 @@
                     m_overview_count = MAX(0, MIN(static_cast<int>(ceil(a)), 32));
                 }
             }
+            if (ret == CE_None) {
+                CPLString y_origin_str = y_origin;
+                if (y_origin_str == "top") {
+                    m_data_window.m_y_origin = GDALWMSDataWindow::TOP;
+                } else if (y_origin_str == "bottom") {
+                    m_data_window.m_y_origin = GDALWMSDataWindow::BOTTOM;
+                } else if (y_origin_str == "default") {
+                    m_data_window.m_y_origin = GDALWMSDataWindow::DEFAULT;
+                } else {
+                    CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: DataWindow YOrigin must be set to " 
+                                "one of 'default', 'top', or 'bottom', not '%s'.", y_origin_str.c_str());
+                    ret = CE_Failure;
+                }
+            }
         }
     }
     if (ret == CE_None) {
Index: frmts/wms/stuff.cpp
===================================================================
--- frmts/wms/stuff.cpp	(revision 16514)
+++ frmts/wms/stuff.cpp	(working copy)
@@ -119,3 +119,20 @@
     if (EQUAL(p, "0") || EQUAL(p, "false") || EQUAL(p, "no") || EQUAL(p, "disable") || EQUAL(p, "disabled") || EQUAL(p, "off")) return 0;
     return -1;
 }
+
+int URLSearchAndReplace (CPLString *base, const char *search, const char *fmt, ...) {
+    CPLString tmp;
+    va_list args;
+
+    size_t start = base->find(search);
+    if (start == std::string::npos) {
+        return -1;
+    }
+
+    va_start(args, fmt);
+    tmp.vPrintf(fmt, args);
+    va_end(args);
+
+    base->replace(start, strlen(search), tmp);
+    return start;
+}
Index: frmts/wms/wmsdriver.h
===================================================================
--- frmts/wms/wmsdriver.h	(revision 16514)
+++ frmts/wms/wmsdriver.h	(working copy)
@@ -38,10 +38,12 @@
 CPLString BufferToVSIFile(GByte *buffer, size_t size);
 CPLErr MakeDirs(const char *path);
 int StrToBool(const char *p);
+int URLSearchAndReplace (CPLString *base, const char *search, const char *fmt, ...);
 
 /* Convert a.b.c.d to a * 0x1000000 + b * 0x10000 + c * 0x100 + d */
 int VersionStringToInt(const char *version);
 
+
 class GDALWMSImageRequestInfo {
 public:
     double m_x0, m_y0;
@@ -55,6 +57,7 @@
     double m_x1, m_y1;
     int m_sx, m_sy;
     int m_tx, m_ty, m_tlevel;
+    enum { BOTTOM = -1, DEFAULT = 0, TOP = 1 } m_y_origin;
 };
 
 class GDALWMSTiledImageRequestInfo {
Index: frmts/wms/stdinc.h
===================================================================
--- frmts/wms/stdinc.h	(revision 16514)
+++ frmts/wms/stdinc.h	(working copy)
@@ -18,3 +18,4 @@
 #include "minidriver_wms.h"
 #include "minidriver_tileservice.h"
 #include "minidriver_worldwind.h"
+#include "minidriver_tms.h"
Index: frmts/wms/minidriver_tms.cpp
===================================================================
--- frmts/wms/minidriver_tms.cpp	(revision 0)
+++ frmts/wms/minidriver_tms.cpp	(revision 0)
@@ -0,0 +1,96 @@
+/******************************************************************************
+ *
+ * Project:  WMS Client Driver
+ * Purpose:  Implementation of Dataset and RasterBand classes for WMS
+ *           and other similar services.
+ * Author:   Adam Nowacki, nowak@xpam.de
+ *
+ ******************************************************************************
+ * Copyright (c) 2007, Adam Nowacki
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "stdinc.h"
+
+CPP_GDALWMSMiniDriverFactory(TMS)
+
+GDALWMSMiniDriver_TMS::GDALWMSMiniDriver_TMS() {
+}
+
+GDALWMSMiniDriver_TMS::~GDALWMSMiniDriver_TMS() {
+}
+
+CPLErr GDALWMSMiniDriver_TMS::Initialize(CPLXMLNode *config) {
+    CPLErr ret = CE_None;
+
+    if (ret == CE_None) {
+        const char *base_url = CPLGetXMLValue(config, "ServerURL", "");
+        if (base_url[0] != '\0') {
+            m_base_url = base_url;
+            if (m_base_url.find("${") == std::string::npos) {
+                if (m_base_url[m_base_url.size()-1] != '/') {
+                    m_base_url += "/";
+                }
+                m_base_url += "${version}/${layer}/${z}/${x}/${y}.${format}";
+            }
+        } else {
+            CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, TMS mini-driver: ServerURL missing.");
+            ret = CE_Failure;
+        }
+    }
+
+    m_dataset  = CPLGetXMLValue(config, "Layer", "");
+    m_version  = CPLGetXMLValue(config, "Version", "1.0.0");
+    m_format   = CPLGetXMLValue(config, "Format", "jpg");
+
+    return ret;
+}
+
+void GDALWMSMiniDriver_TMS::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) {
+    caps->m_capabilities_version = 1;
+    caps->m_has_arb_overviews = 0;
+    caps->m_has_image_request = 0;
+    caps->m_has_tiled_image_requeset = 1;
+    caps->m_max_overview_count = 32;
+}
+
+void GDALWMSMiniDriver_TMS::ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri) {
+}
+
+void GDALWMSMiniDriver_TMS::TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri) {
+    const GDALWMSDataWindow *data_window = m_parent_dataset->WMSGetDataWindow();
+    int tms_y;
+
+    if (data_window->m_y_origin != GDALWMSDataWindow::TOP) {
+        tms_y = static_cast<int>(floor(((data_window->m_y1 - data_window->m_y0)
+                                      / (iri.m_y1 - iri.m_y0)) + 0.5)) - tiri.m_y - 1;
+    } else {
+        tms_y = tiri.m_y;
+    }
+    // http://tms25.arc.nasa.gov/tile/tile.aspx?T=geocover2000&L=0&X=86&Y=39
+    *url = m_base_url;
+
+    URLSearchAndReplace(url, "${version}", "%s", m_version.c_str());
+    URLSearchAndReplace(url, "${layer}", "%s", m_dataset.c_str());
+    URLSearchAndReplace(url, "${format}", "%s", m_format.c_str());
+    URLSearchAndReplace(url, "${x}", "%d", tiri.m_x);
+    URLSearchAndReplace(url, "${y}", "%d", tms_y);
+    URLSearchAndReplace(url, "${z}", "%d", tiri.m_level);
+}

