[mapguide-commits] r9502 - in sandbox/jng/tiling_v2/Server/src: Services/Rendering Services/Tile UnitTesting

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Wed May 8 05:07:18 PDT 2019


Author: jng
Date: 2019-05-08 05:07:18 -0700 (Wed, 08 May 2019)
New Revision: 9502

Added:
   sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.h
Modified:
   sandbox/jng/tiling_v2/Server/src/Services/Rendering/ImageProcessor.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Rendering/ServerRenderingService.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj
   sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj.filters
   sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileServiceBuild.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefault.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.h
   sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.cpp
   sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.h
   sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.cpp
   sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.h
Log:
Initial implementation of metatiling support for the XYZ tileset provider. Move the metatile locking code out to a separate MgMetatileLockUtil helper class so we can reuse it in the XYZ provider.

While the right sub-tile is fetched and matches the baseline. The rendering and storage of the sub-tiles by this provider when meta-tiling, is not.

We'll deal with that in a subsequent submission

Modified: sandbox/jng/tiling_v2/Server/src/Services/Rendering/ImageProcessor.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Rendering/ImageProcessor.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Rendering/ImageProcessor.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -153,14 +153,16 @@
     MgByteSource* bs = image->GetByteSource();
     // upcast to bytesource Impl to access bytearray member
     ByteSourceMemoryImpl* source = dynamic_cast<ByteSourceMemoryImpl*>(bs->GetSourceImpl());
-    assert(source);
-    assert(bs->GetMimeType() == MgMimeType::Meta);
+    _ASSERT(source);
+    _ASSERT(bs->GetMimeType() == MgMimeType::Meta);
     INT32 size = (INT32)source->GetLength();
 
     // some checking: the complete meta tile of 32b pixels should be in the framebuffer handed over
-    INT32 expectedSize = 4 * (metaTileFactor * origTileWidth)
-        * (metaTileFactor * origTileHeight);
-    assert(size == expectedSize);
+    //INT32 expectedSize = 4 * (metaTileFactor * origTileWidth)
+    //    * (metaTileFactor * origTileHeight);
+    //assert(size == expectedSize);
+    INT32 expectedSize = 4 * metaTile->GetWidth() * metaTile->GetHeight();
+    _ASSERT(size == expectedSize);
 
     INT32 scaledTileWidth = origTileWidth;
     INT32 scaledTileHeight = origTileHeight;

Modified: sandbox/jng/tiling_v2/Server/src/Services/Rendering/ServerRenderingService.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Rendering/ServerRenderingService.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Rendering/ServerRenderingService.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -2475,7 +2475,11 @@
 
     // call the internal helper API to do all the stylization overhead work
     STRING format = (metaTilingFactor > 1) ? MgImageFormats::Meta : tileImageFormat;
-    Ptr<MgByteReader> tile = RenderMapInternal(map, NULL, roLayers, dr.get(), drawWidth, drawHeight, width, height, format, scale, extent, true, true, false, NULL);
+    Ptr<MgByteReader> tile;
+    if (metaTilingFactor > 1)
+        tile = RenderMapInternal(map, NULL, roLayers, dr.get(), drawWidth, drawHeight, drawWidth, drawHeight, format, scale, extent, true, true, false, NULL);
+    else
+        tile = RenderMapInternal(map, NULL, roLayers, dr.get(), drawWidth, drawHeight, width, height, format, scale, extent, true, true, false, NULL);
     ret = new MgMetatile(tile, drawWidth, drawHeight, XYZ_TILE_WIDTH, XYZ_TILE_HEIGHT, tileImageFormat, metaTilingFactor);
 
     // restore the base group's visibility

Added: sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.cpp	                        (rev 0)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -0,0 +1,116 @@
+//
+//  Copyright (C) 2004-2019 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library 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
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+#include "MapGuideCommon.h"
+#include "MetatileLockUtil.h"
+
+std::map<STRING, ACE_Condition<ACE_Recursive_Thread_Mutex>*> MgMetatileLockUtil::sm_lockMap;
+ACE_Recursive_Thread_Mutex MgMetatileLockUtil::sm_tileMutex;
+
+MgMetatileLockUtil::MgMetatileLockUtil() { }
+
+MgMetatileLockUtil::~MgMetatileLockUtil() { }
+
+void MgMetatileLockUtil::WaitForLock(CREFSTRING tilePathname)
+{
+    // protect test of lockmap entry with a guard
+    ACE_Condition<ACE_Recursive_Thread_Mutex> *tileLock = 0;
+    // look for lock and wait if found
+    ACE_MT(ACE_GUARD(ACE_Recursive_Thread_Mutex, ace_mon, sm_tileMutex));
+    if (tileLock = sm_lockMap[tilePathname])
+    {   // lock has been found
+#ifdef _DEBUG
+        std::wstringstream text;
+        text << L"(" << ACE_OS::thr_self() << L") METATILE: WaitForLock1("
+            << tilePathname << L")\n";
+        ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+        //                ACE_Time_Value waitTime(METATILEWAIT);
+        int ret = tileLock->wait();    // returns -1 if timed out which is not supposed to happen
+        if (-1 == ret) // so the wait might never come back!!! and the thread gets cleaned out by the service manager
+        {
+#ifdef _DEBUG
+            std::wstringstream text;
+            text << L"(" << ACE_OS::thr_self() << L") METATILE: WaitForLockTimedOut1(" << tilePathname << L")\n";
+            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+            MgStringCollection arguments;
+            arguments.Add(tilePathname);
+            throw new MgFileIoException(L"MgServerTileService.GetTile",
+                __LINE__, __WFILE__, &arguments, L"MgWaitForLockTimedOut", NULL);
+        }
+#ifdef _DEBUG
+        {
+            std::wstringstream text;
+            text << L"(" << ACE_OS::thr_self() << L") METATILE: LockReleased(" << tilePathname << L")\n";
+            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+        }
+#endif
+    }
+}
+
+void MgMetatileLockUtil::ClearLock(CREFSTRING tilePathname)
+{
+    try
+    {   // synchronize access to map
+        ACE_MT(ACE_GUARD(ACE_Recursive_Thread_Mutex, ace_mon, sm_tileMutex));
+        ACE_Condition<ACE_Recursive_Thread_Mutex> *lock = sm_lockMap[tilePathname];
+        if (lock)
+        {
+            int result = lock->broadcast();    // notify waiters of finished tile
+#ifdef _DEBUG
+            std::wstringstream text;
+            text << L"(" << ACE_OS::thr_self() << L") METATILE: Broadcast(" << tilePathname
+                << L") returns: " << result << L"\n";
+            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+            sm_lockMap.erase(tilePathname); // erase only existing lock: this destroys the ace condition
+        }
+        else
+        {
+#ifdef _DEBUG
+            std::wstringstream text;
+            text << L"(" << ACE_OS::thr_self() << L") METATILE: NoLockFor(" << tilePathname << L")\n";
+            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+        }
+    }
+    catch (MgException* e)
+    {
+#ifdef _DEBUG
+        std::wstringstream text;
+        text << L"(" << ACE_OS::thr_self() << L") METATILE: Broadcastcrash (" << tilePathname
+            << L")\n" << e->GetExceptionMessage() << L")\n";
+        ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+        throw e;
+    }
+}
+
+void MgMetatileLockUtil::LockTile(CREFSTRING tilePathname)
+{
+    // create ace condition for this tile as we are going to make it
+    ACE_Condition<ACE_Recursive_Thread_Mutex> *tileLock = new ACE_Condition<ACE_Recursive_Thread_Mutex>(sm_tileMutex);
+    // keep reference to lock (ace condition) in our map
+    sm_lockMap[tilePathname] = tileLock;
+
+#ifdef _DEBUG
+    std::wstringstream text;
+    text << L"(" << ACE_OS::thr_self() << L") METATILE: CreateLock(" << tilePathname << L"\n";
+    ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+}
\ No newline at end of file

Added: sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.h
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.h	                        (rev 0)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/MetatileLockUtil.h	2019-05-08 12:07:18 UTC (rev 9502)
@@ -0,0 +1,39 @@
+//
+//  Copyright (C) 2004-2019 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library 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
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+
+#ifndef MG_METATILE_LOCK_UTIL_H
+#define MG_METATILE_LOCK_UTIL_H
+
+/// \brief
+/// Metatile helper class for file-based locking
+class MgMetatileLockUtil
+{
+public:
+    MgMetatileLockUtil();
+    ~MgMetatileLockUtil();
+
+    void WaitForLock(CREFSTRING tilePathname);
+    void ClearLock(CREFSTRING tilePathname);
+    void LockTile(CREFSTRING tilePathname);
+
+private:
+    static std::map<STRING, ACE_Condition<ACE_Recursive_Thread_Mutex>*> sm_lockMap;
+    static ACE_Recursive_Thread_Mutex sm_tileMutex;
+};
+
+#endif
\ No newline at end of file

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -643,7 +643,7 @@
             path = MgTileParameters::tileCachePath;
         }
 
-        cache = new MgTileCacheXYZProvider(tileSetId, path, format, bRenderOnly, tileExtentOffset, metaTileFactor, metaTileLockMethod);
+        cache = new MgTileCacheXYZProvider(tileSetId, path, format, bRenderOnly, tileExtentOffset, m_rendererName, metaTileFactor, metaTileLockMethod);
     }
     else 
     {

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj	2019-05-08 12:07:18 UTC (rev 9502)
@@ -198,6 +198,12 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="MetatileLockUtil.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="OpClearCache.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -291,6 +297,7 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="MetatileLockUtil.h" />
     <ClInclude Include="OpClearCache.h" />
     <ClInclude Include="OpGetDefaultTileSizeX.h" />
     <ClInclude Include="OpGetDefaultTileSizeY.h" />

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj.filters
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj.filters	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileService.vcxproj.filters	2019-05-08 12:07:18 UTC (rev 9502)
@@ -38,6 +38,7 @@
       <Filter>Ops</Filter>
     </ClCompile>
     <ClCompile Include="TileCacheXYZProvider.cpp" />
+    <ClCompile Include="MetatileLockUtil.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="OpClearCache.h">
@@ -72,6 +73,7 @@
       <Filter>Ops</Filter>
     </ClInclude>
     <ClInclude Include="TileCacheXYZProvider.h" />
+    <ClInclude Include="MetatileLockUtil.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="ServerTileService.rc" />

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileServiceBuild.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileServiceBuild.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/ServerTileServiceBuild.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -18,6 +18,7 @@
 #ifdef _WIN32
 #include "Dll.cpp"
 #endif
+#include "MetatileLockUtil.cpp"
 #include "OpClearCache.cpp"
 #include "OpGetTile.cpp"
 #include "OpSetTile.cpp"

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefault.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefault.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefault.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -265,15 +265,6 @@
             GetMapFromDefinition(resource, scaleIndex, map, false /* Allow MgMap to be created from any tile provider if resource is tile set */);
         }   // end of mutex scope
 
-        //Some tile providers (eg. XYZ) may not work with pre-defined scale lists, so the scale index doesn't
-        //resolve to some position in a finite scale list (that won't exist for XYZ), but rather it is part of 
-        //some formula used to determine the appropriate scale
-        if (map->GetFiniteDisplayScaleCount() > 0)
-        {
-            double scale = map->GetFiniteDisplayScaleAt(scaleIndex);
-            map->SetViewScale(scale);
-        }
-
         // Render the tile and cache it.
         ret = RenderAndCacheTile(tilePathname, map, scaleIndex, baseMapLayerGroupName, tileColumn, tileRow);
         break;
@@ -941,6 +932,13 @@
         sm_mapCache[mapString] = SAFE_ADDREF((MgMemoryStreamHelper*)cachedMap);
     }
 
-    double scale = map->GetFiniteDisplayScaleAt(scaleIndex);
-    map->SetViewScale(scale);
+    //Some tile providers (eg. XYZ) may not work with pre-defined scale lists, so the scale index doesn't
+    //resolve to some position in a finite scale list (that won't exist for XYZ), but rather it is part of 
+    //some formula used to determine the appropriate scale
+    INT32 scaleCount = map->GetFiniteDisplayScaleCount();
+    if (scaleCount > 0 && scaleIndex > 0 && scaleIndex < scaleCount)
+    {
+        double scale = map->GetFiniteDisplayScaleAt(scaleIndex);
+        map->SetViewScale(scale);
+    }
 }
\ No newline at end of file

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -18,10 +18,9 @@
 #include "MapGuideCommon.h"
 #include "TileCacheDefaultProvider.h"
 #include "ServerRenderingService.h"
+#include "MetatileLockUtil.h"
 
-std::map<STRING, ACE_Condition<ACE_Recursive_Thread_Mutex>*> MgTileCacheDefaultProvider::sm_lockMap;
 ACE_Recursive_Thread_Mutex MgTileCacheDefaultProvider::sm_MetaTileMutex;
-ACE_Recursive_Thread_Mutex MgTileCacheDefaultProvider::sm_tileMutex;
 
 MgTileCacheDefaultProvider::MgTileCacheDefaultProvider(MgResourceIdentifier* tileSetId,
     CREFSTRING path,
@@ -141,6 +140,7 @@
     STRING metaTileLockPathname, metaTilePathname;
     int maxX = -1, maxY = -1, subTileX = -1, subTileY = -1, metaTileColumn = -1, metaTileRow = -1;
     bool cacheHit = false;
+    MgMetatileLockUtil metaLock;
 
     MG_TRY()
 
@@ -197,7 +197,7 @@
             ACE_DEBUG((LM_DEBUG, text.str().c_str()));
         }
 #endif
-        WaitForLock(metaTilePathname);
+        metaLock.WaitForLock(metaTilePathname);
     }
     else // this is the same locking strategie as with single tiles but using one mapped metatile locking all subtile requests
     {
@@ -281,7 +281,7 @@
             // ==================================================  LOCKING
             if (m_metaTileLockMethod)
             {
-                LockTile(metaTilePathname);    // ace lock once per metatile
+                metaLock.LockTile(metaTilePathname);    // ace lock once per metatile
             }
             else
             {
@@ -383,7 +383,7 @@
         // remove all lockfiles or conditions
         if (m_metaTileLockMethod)
         {
-            ClearLock(metaTilePathname);
+            metaLock.ClearLock(metaTilePathname);
         }
         else
         {
@@ -414,94 +414,4 @@
         }
 
     return returnedSubtile.Detach();
-}
-
-void MgTileCacheDefaultProvider::WaitForLock(CREFSTRING tilePathname)
-{
-    // protect test of lockmap entry with a guard
-    ACE_Condition<ACE_Recursive_Thread_Mutex> *tileLock = 0;
-    // look for lock and wait if found
-    ACE_MT(ACE_GUARD(ACE_Recursive_Thread_Mutex, ace_mon, sm_tileMutex));
-    if (tileLock = sm_lockMap[tilePathname])
-    {   // lock has been found
-#ifdef _DEBUG
-        std::wstringstream text;
-        text << L"(" << ACE_OS::thr_self() << L") METATILE: WaitForLock1("
-            << tilePathname << L")\n";
-        ACE_DEBUG((LM_DEBUG, text.str().c_str()));
-#endif
-        //                ACE_Time_Value waitTime(METATILEWAIT);
-        int ret = tileLock->wait();    // returns -1 if timed out which is not supposed to happen
-        if (-1 == ret) // so the wait might never come back!!! and the thread gets cleaned out by the service manager
-        {
-#ifdef _DEBUG
-            std::wstringstream text;
-            text << L"(" << ACE_OS::thr_self() << L") METATILE: WaitForLockTimedOut1(" << tilePathname << L")\n";
-            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
-#endif
-            MgStringCollection arguments;
-            arguments.Add(tilePathname);
-            throw new MgFileIoException(L"MgServerTileService.GetTile",
-                __LINE__, __WFILE__, &arguments, L"MgWaitForLockTimedOut", NULL);
-        }
-#ifdef _DEBUG
-        {
-            std::wstringstream text;
-            text << L"(" << ACE_OS::thr_self() << L") METATILE: LockReleased(" << tilePathname << L")\n";
-            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
-        }
-#endif
-    }
-}
-
-void MgTileCacheDefaultProvider::ClearLock(CREFSTRING tilePathname)
-{
-    try
-    {   // synchronize access to map
-        ACE_MT(ACE_GUARD(ACE_Recursive_Thread_Mutex, ace_mon, sm_tileMutex));
-        ACE_Condition<ACE_Recursive_Thread_Mutex> *lock = sm_lockMap[tilePathname];
-        if (lock)
-        {
-            int result = lock->broadcast();    // notify waiters of finished tile
-#ifdef _DEBUG
-            std::wstringstream text;
-            text << L"(" << ACE_OS::thr_self() << L") METATILE: Broadcast(" << tilePathname
-                << L") returns: " << result << L"\n";
-            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
-#endif
-            sm_lockMap.erase(tilePathname); // erase only existing lock: this destroys the ace condition
-        }
-        else
-        {
-#ifdef _DEBUG
-            std::wstringstream text;
-            text << L"(" << ACE_OS::thr_self() << L") METATILE: NoLockFor(" << tilePathname << L")\n";
-            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
-#endif
-        }
-    }
-    catch (MgException* e)
-    {
-#ifdef _DEBUG
-        std::wstringstream text;
-        text << L"(" << ACE_OS::thr_self() << L") METATILE: Broadcastcrash (" << tilePathname
-            << L")\n" << e->GetExceptionMessage() << L")\n";
-        ACE_DEBUG((LM_DEBUG, text.str().c_str()));
-#endif
-        throw e;
-    }
-}
-
-void MgTileCacheDefaultProvider::LockTile(CREFSTRING tilePathname)
-{
-    // create ace condition for this tile as we are going to make it
-    ACE_Condition<ACE_Recursive_Thread_Mutex> *tileLock = new ACE_Condition<ACE_Recursive_Thread_Mutex>(sm_tileMutex);
-    // keep reference to lock (ace condition) in our map
-    sm_lockMap[tilePathname] = tileLock;
-
-#ifdef _DEBUG
-    std::wstringstream text;
-    text << L"(" << ACE_OS::thr_self() << L") METATILE: CreateLock(" << tilePathname << L"\n";
-    ACE_DEBUG((LM_DEBUG, text.str().c_str()));
-#endif
 }
\ No newline at end of file

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.h
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.h	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheDefaultProvider.h	2019-05-08 12:07:18 UTC (rev 9502)
@@ -64,12 +64,7 @@
                                          INT32 scaleIndex);
 
     // use a memory based locking scheme
-    static std::map<STRING, ACE_Condition<ACE_Recursive_Thread_Mutex>*> sm_lockMap;
     static ACE_Recursive_Thread_Mutex sm_MetaTileMutex;
-    static ACE_Recursive_Thread_Mutex sm_tileMutex;
-    void WaitForLock(CREFSTRING tilePathname);
-    void ClearLock(CREFSTRING tilePathname);
-    void LockTile(CREFSTRING tilePathname);
 
     // ------------------- End Metatile stuff -------------------- //
 

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -18,11 +18,14 @@
 #include "MapGuideCommon.h"
 #include "TileCacheXYZProvider.h"
 
+ACE_Recursive_Thread_Mutex MgTileCacheXYZProvider::sm_MetaTileMutex;
+
 MgTileCacheXYZProvider::MgTileCacheXYZProvider(MgResourceIdentifier* tileSetId,
                                                CREFSTRING path,
                                                CREFSTRING format,
                                                bool bRenderOnly,
                                                double tileExtentOffset,
+                                               CREFSTRING rendererName,
                                                INT32 metaTileFactor,
                                                INT32 metaTileLockMethod)
 {
@@ -31,6 +34,7 @@
     m_format = format;
     m_renderOnly = bRenderOnly;
     m_tileExtentOffset = tileExtentOffset;
+    m_rendererName = rendererName;
     m_metaTileFactor = metaTileFactor;
     m_metaTileLockMethod = metaTileLockMethod;
 }
@@ -48,7 +52,10 @@
     Ptr<MgByteReader> ret;
     MG_TRY()
 
-    ret = GetTileForResource(m_tilesetId, baseMapLayerGroupName, tileColumn, tileRow, scaleIndex);
+        if (m_metaTileFactor > 1)
+            ret = GetMetatileForResource(m_tilesetId, baseMapLayerGroupName, tileColumn, tileRow, scaleIndex);
+        else
+            ret = GetTileForResource(m_tilesetId, baseMapLayerGroupName, tileColumn, tileRow, scaleIndex);
 
     MG_CATCH_AND_THROW(L"MgTileCacheXYZProvider.GetTile")
     return ret.Detach();
@@ -199,4 +206,298 @@
     STRING ret;
     MgUtil::Int32ToString(tileColumn, ret);
     return ret;
+}
+
+MgByteReader* MgTileCacheXYZProvider::GetMetatileForResource(MgResourceIdentifier* resource,
+    CREFSTRING baseMapLayerGroupName,
+    INT32 tileColumn,
+    INT32 tileRow,
+    INT32 scaleIndex)
+{
+    // Must have a renderer name set
+    _ASSERT(!m_rendererName.empty());
+
+    Ptr<MgByteReader> returnedSubtile;
+    FILE* lockFile = NULL;
+    STRING tilePathname, lockPathname;
+    // metatiling vars
+    FILE* subTileLockFile[MG_METATILE_MAX_FACTOR][MG_METATILE_MAX_FACTOR];
+    STRING subTileLockPathname[MG_METATILE_MAX_FACTOR][MG_METATILE_MAX_FACTOR], subTilePathname[MG_METATILE_MAX_FACTOR][MG_METATILE_MAX_FACTOR];
+    STRING metaTileLockPathname, metaTilePathname;
+    int maxX = -1, maxY = -1, subTileX = -1, subTileY = -1, metaTileColumn = -1, metaTileRow = -1;
+    bool cacheHit = false;
+    MgMetatileLockUtil metaLock;
+
+    MG_TRY()
+
+        // Generate tile and lock pathnames for later use
+        //m_tileCache->GeneratePathnames(mapDefinition, scaleIndex, baseMapLayerGroupName,
+        //    tileColumn, tileRow, tilePathname, lockPathname, false);
+        GeneratePathNames(scaleIndex, baseMapLayerGroupName, tileColumn, tileRow, tilePathname, lockPathname, false);
+
+    subTileX = subTileY = 0;
+    metaTileColumn = tileColumn;
+    metaTileRow = tileRow;
+
+    /*
+    ////// compute additional names and control variables to deal with meta tiles:
+    // a meta tile is m_metaTileFactor^2 larger than the requested subtile so there are less tiles computed per map
+    // the metatile coords are computed from original coords modulo metatilesize
+    // e.g. with metatile=2 4 tiles (0,0)(0,1)(1,0)(1,1) get mapped to metatile(0,0)
+    subTileX = abs(tileColumn % m_metaTileFactor);
+    subTileY = abs(tileRow % m_metaTileFactor);
+    // determine left top corner of metaTile having m_metaTileFactor**2 subtiles
+    metaTileColumn = tileColumn - subTileX;
+    metaTileRow = tileRow - subTileY;
+    */
+    maxX = maxY = m_metaTileFactor;  // init control vars
+    //// iterate subtiles and create all pathnames of the created tiles
+    for (int y = 0; y < maxY; y++)    // subtile rows
+    {
+        for (int x = 0; x < maxX; x++)    // subtile columns
+        {   // Generate tile and lock pathnames (locknames are not used)
+            subTileLockFile[x][y] = NULL;
+            //m_tileCache->GeneratePathnames(mapDefinition, scaleIndex, baseMapLayerGroupName,
+            //    metaTileColumn + x, metaTileRow + y, subTilePathname[x][y], subTileLockPathname[x][y], false);
+
+            GeneratePathNames(scaleIndex, baseMapLayerGroupName,
+                metaTileColumn + x, metaTileRow + y, subTilePathname[x][y], subTileLockPathname[x][y], false);
+        }
+    }
+
+    // Generate tile and lock pathnames for meta tile in the larger grid mod xx 
+    //m_tileCache->GeneratePathnames(mapDefinition, scaleIndex, baseMapLayerGroupName,
+    //    metaTileColumn, metaTileRow, metaTilePathname, metaTileLockPathname, false);
+    GeneratePathNames(scaleIndex, baseMapLayerGroupName,
+        metaTileColumn, metaTileRow, metaTilePathname, metaTileLockPathname, false);
+
+    // m_metaTileLockMethod == 0 is using 1 remapped metalockfile for all subtiles
+    // m_metaTileLockMethod > 0 is using 1 an ace condition mapped by metatilename
+    if (m_metaTileLockMethod)
+    {
+#ifdef _DEBUG
+        {
+            std::wstringstream text;
+            text << L"(" << ACE_OS::thr_self() << L") METATILE: " << "WaitForLock("
+                << tileRow << L", " << tileColumn << L") (" << metaTilePathname << L")\n";
+            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+        }
+#endif
+        metaLock.WaitForLock(metaTilePathname);
+    }
+    else // this is the same locking strategie as with single tiles but using one mapped metatile locking all subtile requests
+    {
+        bool fileExist = DetectTileLockFile(metaTileLockPathname);
+        if (fileExist)
+        {   // TODO: Handle the exception by displaying a tile with an error message?
+            MgFileUtil::DeleteFile(metaTileLockPathname, true);
+#ifdef _DEBUG
+            std::wstringstream text;
+            text << L"(" << ACE_OS::thr_self() << L") METATILE: " << (fileExist ? L"DeletedDanglingLockfile(" : L"NoLockfile(")
+                << metaTileRow << L", " << metaTileColumn << L") " << metaTileLockPathname << L")\n";
+            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+        }
+    }
+    // try getting the tile from the cache first time
+    cacheHit = returnedSubtile = Get(tilePathname);
+#ifdef _DEBUG
+    std::wstringstream text;
+    text << L"(" << ACE_OS::thr_self() << (cacheHit ? L") CACHEHIT" : L") CACHEMISS") << L": GetTile("
+        << tileRow << L", " << tileColumn << L") " << tilePathname << L"\n";
+    ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+    // if the reader is NULL then the tile wasn't in the cache and we need to generate it
+    while (NULL == returnedSubtile) // while for if to be able to use break to skip to end;
+    {
+        Ptr<MgMap> map;
+
+        // Protect the serialized MgMap cache with a mutex.  Stream reading is not
+        // thread safe so we need to deserialize the map within the mutex to ensure
+        // that a Rewind() is not called in the middle of a Deserialize().
+        // Lockfile test and creation is in same protected scope.
+        { // ------------------------ Locking Context
+            // Attempt to get the mapcache mutex .
+            ACE_MT(ACE_GUARD_RETURN(ACE_Recursive_Thread_Mutex, ace_mon, sm_MetaTileMutex, NULL));
+            if (m_metaTileLockMethod)
+            {
+                // we DONT need the second lock test here in a signal/wait algorithm
+    //          m_tileCache->WaitForLock(metaTilePathname);
+            }
+            else
+            {
+                if (DetectTileLockFile(metaTileLockPathname)) //
+                {
+                    MgStringCollection arguments;
+                    arguments.Add(metaTileLockPathname);
+                    throw new MgFileIoException(L"MgServerTileService.GetTile",
+                        __LINE__, __WFILE__, &arguments, L"MgUnableToLockMetaTileFile", NULL);
+                }
+            }
+
+            // try getting the tile from the cache second time ???????????????????????????????????????????
+            cacheHit = returnedSubtile = Get(tilePathname);
+
+            if (NULL != returnedSubtile)
+            {
+#ifdef _DEBUG
+                std::wstringstream text;
+                text << L"(" << ACE_OS::thr_self() << L") CACHEHIT2: GetTile(" << tileRow << L", " << tileColumn << L") " << tilePathname << L"\n";
+                ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif        
+                break;  /////////////////////////////////// tile was in tileCache .. done.
+            }
+            else {
+#ifdef _DEBUG
+                std::wstringstream text;
+                text << L"(" << ACE_OS::thr_self() << L") CACHEMISS2: GetTile(" << tileRow << L", " << tileColumn << L") " << tilePathname << L"\n";
+                ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+            }
+            // Create the directory structure all subtiles!
+            for (int y = 0; y < maxY; y++)    // rows
+            {
+                for (int x = 0; x < maxX; x++)    // columns
+                {
+                    //m_tileCache->CreateFullPath(mapDefinition, scaleIndex, baseMapLayerGroupName, 
+                    //    metaTileColumn + x, metaTileRow + y);
+                    CreateFullPath(GetBasePath(), scaleIndex, baseMapLayerGroupName, metaTileColumn + x, metaTileRow + y);
+                }
+            }
+            // ==================================================  LOCKING
+            if (m_metaTileLockMethod)
+            {
+                metaLock.LockTile(metaTilePathname);    // ace lock once per metatile
+            }
+            else
+            {
+                // Create the lock file 
+                lockFile = ACE_OS::fopen(MG_WCHAR_TO_TCHAR(metaTileLockPathname), ACE_TEXT("wb"));
+
+                if (NULL == lockFile)
+                {
+                    MgStringCollection arguments;
+                    arguments.Add(metaTileLockPathname);
+                    throw new MgFileIoException(L"MgServerTileService.GetTile",
+                        __LINE__, __WFILE__, &arguments, L"MgUnableToCreateLockFile", NULL);
+                }
+                else
+                {
+                    ACE_OS::fclose(lockFile); // and close it right away.
+#ifdef _DEBUG
+                    std::wstringstream text;
+                    text << L"(" << ACE_OS::thr_self() << L") METATILE: CreatedLockfile(" << metaTileRow << L", " << metaTileColumn << L") "
+                        << metaTileLockPathname << L")\n";
+                    ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+                }
+            }
+            //=========================================================================
+            GetMapFromDefinition(resource, -1 /* Don't set view scale from index for XYZ*/, map, false);
+        }   // end of mutex scope
+
+        // Render the larger meta tile but do not cache it yet! (m_metaTileFactor [prohibits caching in GetTile)
+        STRING metaTileName = L"META" + metaTilePathname;
+#ifdef _DEBUG
+        std::wstringstream text;
+        text << L"(" << ACE_OS::thr_self() << L") METATILE: RenderMetaTile(" << metaTileRow << L", " << metaTileColumn << L") " << metaTileName << " using map 0x" << map << L"\n";
+        ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+        // Get a server-side rendering Service only...
+        MgServiceManager* serviceMan = MgServiceManager::GetInstance();
+        _ASSERT(NULL != serviceMan);
+        Ptr<MgServerRenderingService> svcRendering = dynamic_cast<MgServerRenderingService*>(
+            serviceMan->RequestService(MgServiceType::RenderingService));
+
+        // Bail if no server-side impl exists (we can't use proxy impl)
+        if (NULL == svcRendering)
+        {
+            throw new MgServiceNotAvailableException(L"MgTileCacheXYZProvider.GetMetatileForResource", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+
+        //Ptr<MgByteReader> metaTileBitMap = GetTile(metaTileName, map, scaleIndex, baseMapLayerGroupName, metaTileColumn, metaTileRow);
+        INT32 tileDpi = map->GetDisplayDpi();
+        Ptr<MgMetatile> metaTile = svcRendering->RenderMetatileXYZ(map, baseMapLayerGroupName, metaTileRow, metaTileColumn, scaleIndex, tileDpi, m_format, m_tileExtentOffset, m_metaTileFactor);
+#ifdef _DEBUG
+        Ptr<MgByteReader> mtContent = metaTile->GetImage();
+        INT32 metaTileLen = mtContent->GetLength();
+#endif
+        // splitup the meta tiles
+        for (int y = 0; y < maxY; y++)    // rows
+        {
+            for (int x = 0; x < maxX; x++)    // columns
+            {
+                // Collect the subtile from the metatile
+                Ptr<MgByteReader> img = MgImageProcessor::RenderTileFromMetaTile(map, metaTile, m_rendererName, svcRendering, x, y);
+#ifdef _DEBUG
+                INT32 tileLen = img->GetLength();
+#endif          
+                if ((subTileX == x) && (subTileY == y))
+                    returnedSubtile = img;       // keep pointer for subtile which has triggered the metatile in the current thread
+                _ASSERT(x < maxX && y < maxY);
+#ifdef _DEBUG
+                std::wstringstream text;    // streams for more compact debug statements
+                text << L"(" << ACE_OS::thr_self();
+                text << L") RenderTileFromMetaTile dim:" << 256 << L"x" << 256;
+                text << L" format:" << m_format << L" subTile(" << x << L"," << y;
+                text << L") metaTile(" << metaTileRow << L"," << metaTileColumn << L") " << metaTilePathname.c_str() << L"\n";
+                ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+                Set(img, subTilePathname[x][y]);   // store all subtiles in tile cache
+
+                // rewind the reader since setting the tile advances it to the end
+                if (img)
+                {
+                    img->Rewind();
+                }
+#ifdef _DEBUG
+                {
+                    std::wstringstream text;
+                    text << L"(" << ACE_OS::thr_self() << L") METATILE: " << (img ? L"SetTile" : L"NoTileReturned");
+                    text << "FromMetaTile(" << tileRow << L", " << tileColumn << L") ";
+                    text << subTilePathname[x][y] << L" S(" << x << L", " << y << L")\n";
+                    ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+                }
+#endif
+            }
+        }
+        break;
+    }   // end of gettile loop end MgMap scope
+
+    MG_CATCH(L"MgServerTileService.GetTile")
+
+        // remove all lockfiles or conditions
+        if (m_metaTileLockMethod)
+        {
+            metaLock.ClearLock(metaTilePathname);
+        }
+        else
+        {
+            if (NULL != lockFile)
+            {
+                MgFileUtil::DeleteFile(metaTileLockPathname, false);
+                {
+#ifdef _DEBUG
+                    std::wstringstream text;
+                    text << L"(" << ACE_OS::thr_self() << L") METATILE: DeletedLockfile(" << metaTileRow << L", " << metaTileColumn << L") "
+                        << metaTileLockPathname << L"\n";
+                    ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+                }
+            }
+        }
+
+    MG_THROW()
+
+        if (!cacheHit)
+        {
+#ifdef _DEBUG
+            std::wstringstream text;
+            text << L"(" << ACE_OS::thr_self() << L") Rendered: GetTILE(" << tileRow << L", " << tileColumn << L") "
+                << L" S(" << subTileX << L", " << subTileY << L") (" << tilePathname << L")\n";
+            ACE_DEBUG((LM_DEBUG, text.str().c_str()));
+#endif
+        }
+
+    return returnedSubtile.Detach();
 }
\ No newline at end of file

Modified: sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.h
===================================================================
--- sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.h	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/Services/Tile/TileCacheXYZProvider.h	2019-05-08 12:07:18 UTC (rev 9502)
@@ -27,6 +27,7 @@
                            CREFSTRING format,
                            bool bRenderOnly,
                            double tileExtentOffset,
+                           CREFSTRING rendererName,
                            INT32 metaTileFactor,
                            INT32 metaTileLockMethod);
     virtual ~MgTileCacheXYZProvider();
@@ -59,6 +60,18 @@
     virtual STRING GetTileFileExtension();
 
 private:
+    // ---------------- Begin Metatile stuff --------------------- //
+    MgByteReader* GetMetatileForResource(MgResourceIdentifier* resource,
+        CREFSTRING baseMapLayerGroupName,
+        INT32 tileColumn,
+        INT32 tileRow,
+        INT32 scaleIndex);
+
+    // use a memory based locking scheme
+    static ACE_Recursive_Thread_Mutex sm_MetaTileMutex;
+
+    // ------------------- End Metatile stuff -------------------- //
+
     Ptr<MgResourceIdentifier> m_tilesetId;
     STRING m_path;
     STRING m_format;
@@ -66,6 +79,8 @@
     double m_tileExtentOffset;
     INT32 m_metaTileFactor;
     INT32 m_metaTileLockMethod;
+
+    STRING m_rendererName;
 };
 
 #endif
\ No newline at end of file

Modified: sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.cpp
===================================================================
--- sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.cpp	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.cpp	2019-05-08 12:07:18 UTC (rev 9502)
@@ -1784,13 +1784,13 @@
 
         Ptr<MgMetatile> metaTile = renderSvc->RenderMetatile(map, L"BaseLayers", col, row, MgTileParameters::tileWidth, MgTileParameters::tileHeight, MgTileParameters::tileDPI, MgImageFormats::Png, MgConfigProperties::DefaultRenderingServicePropertyTileExtentOffset, metaTileFactor);
         Ptr<MgByteReader> mtContent = metaTile->GetImage();
-        INT32 metaTileLen = mtContent->GetLength();
+        INT64 metaTileLen = mtContent->GetLength();
         for (INT32 x = 0; x < metaTile->GetMetaTilingFactor(); x++)
         {
             for (INT32 y = 0; y < metaTile->GetMetaTilingFactor(); y++)
             {
                 Ptr<MgByteReader> img = MgImageProcessor::RenderTileFromMetaTile(map, metaTile, L"AGG", renderSvc, x, y);
-                INT32 tileLen = img->GetLength();
+                INT64 tileLen = img->GetLength();
                 STRING fileName = L"../UnitTestFiles/GetMetatileSingle_Baseline/";
                 STRING s;
                 MgUtil::Int32ToString(y, s);
@@ -1824,9 +1824,69 @@
 {
     try
     {
-        Ptr<MgResourceIdentifier> tsId = new MgResourceIdentifier(L"Library://UnitTests/TileSets/XYZ_Metatiled.TileSetDefinition");
+        INT32 x = 23892;
+        INT32 y = 16798;
+        INT32 z = 16;
+
+        Ptr<MgByteReader> tile;
+        Ptr<MgResourceIdentifier> tsId;
+
+        tsId = new MgResourceIdentifier(L"Library://UnitTests/TileSets/XYZ.TileSetDefinition");
         m_svcTile->ClearCache(tsId);
-        Ptr<MgByteReader> tile = m_svcTile->GetTile(tsId, L"BaseLayers", 16795, 23890, 16);
+        tile = m_svcTile->GetTile(tsId, L"BaseLayers", x, y, z);
+        tile->ToFile(L"../UnitTestFiles/GetMetatileXYZSingle_Baseline.png");
+
+        INT32 metaTileFactor = 4;
+        //Render full 4x4 tiles to check we're generating the same file paths from metatile
+        for (INT32 x1 = x; x1 < x + metaTileFactor; x1++)
+        {
+            for (INT32 y1 = y; y1 < y + metaTileFactor; y1++)
+            {
+                tile = m_svcTile->GetTile(tsId, L"BaseLayers", x1, y1, z);
+            }
+        }
+
+        Ptr<MgCoordinateSystemFactory> csFactory = new MgCoordinateSystemFactory();
+        STRING ovCsvWkt = csFactory->ConvertCoordinateSystemCodeToWkt(L"LL84");
+
+        Ptr<MgMap> map = CreateMapLinked2(L"MetaTileBaseline", ovCsvWkt);
+        map->SetViewScale(3125);
+        Ptr<MgServerRenderingService> renderSvc = dynamic_cast<MgServerRenderingService*>(m_siteConnection->CreateService(MgServiceType::RenderingService));
+        CPPUNIT_ASSERT(NULL != renderSvc);
+
+        MgFileUtil::CreateDirectory(L"../UnitTestFiles/GetMetatileSingleXYZ_Baseline");
+
+        //NOTE: When rendering the XYZ tile we need to flip x/y
+        INT32 mtX = y;
+        INT32 mtY = x;
+
+        Ptr<MgMetatile> metaTile = renderSvc->RenderMetatileXYZ(map, L"BaseLayers", mtX, mtY, z, MgTileParameters::tileDPI, MgImageFormats::Png, MgConfigProperties::DefaultRenderingServicePropertyTileExtentOffset, metaTileFactor);
+        Ptr<MgByteReader> mtContent = metaTile->GetImage();
+        INT64 metaTileLen = mtContent->GetLength();
+        INT32 mtFactor = metaTile->GetMetaTilingFactor();
+        for (INT32 x1 = 0; x1 < mtFactor; x1++)
+        {
+            for (INT32 y1 = 0; y1 < mtFactor; y1++)
+            {
+                Ptr<MgByteReader> img = MgImageProcessor::RenderTileFromMetaTile(map, metaTile, L"AGG", renderSvc, x1, y1);
+                INT64 tileLen = img->GetLength();
+                STRING fileName = L"../UnitTestFiles/GetMetatileSingleXYZ_Baseline/";
+                STRING s;
+                MgUtil::Int32ToString(y1 + y, s);
+                fileName += s;
+                fileName += L"y_";
+                MgUtil::Int32ToString(x1 + x, s);
+                fileName += s;
+                fileName += L"x.png";
+                img->ToFile(fileName);
+            }
+        }
+
+        tsId = new MgResourceIdentifier(L"Library://UnitTests/TileSets/XYZ_Metatiled.TileSetDefinition");
+        m_svcTile->ClearCache(tsId);
+        tile = m_svcTile->GetTile(tsId, L"BaseLayers", x, y, z);
+        tile->ToFile(L"../UnitTestFiles/GetMetatileXYZSingle.png");
+
     }
     catch (MgException* e)
     {
@@ -1894,7 +1954,7 @@
     return map;
 }
 
-MgMap* TestTileService::CreateMapLinked2(CREFSTRING mapName)
+MgMap* TestTileService::CreateMapLinked2(CREFSTRING mapName, CREFSTRING ovCsWkt)
 {
     // set a default name if not supplied
     STRING name = (mapName.empty()) ? L"UnitTestBaseMapLinked" : mapName;
@@ -1929,6 +1989,12 @@
             break;
         }
     }
+
+    if (!ovCsWkt.empty())
+    {
+        mdf->SetCoordinateSystem(ovCsWkt);
+    }
+
     // Fix up extents too
     mdf->SetExtents(tsd->GetExtents());
 

Modified: sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.h
===================================================================
--- sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.h	2019-05-03 13:04:33 UTC (rev 9501)
+++ sandbox/jng/tiling_v2/Server/src/UnitTesting/TestTileService.h	2019-05-08 12:07:18 UTC (rev 9502)
@@ -25,8 +25,8 @@
     CPPUNIT_TEST_SUITE(TestTileService);
     CPPUNIT_TEST(TestStart); // This must be the very first unit test
     
-    CPPUNIT_TEST(TestCase_GetMetatileSingle);
-    //CPPUNIT_TEST(TestCase_GetMetatileXYZSingle);
+    //CPPUNIT_TEST(TestCase_GetMetatileSingle);
+    CPPUNIT_TEST(TestCase_GetMetatileXYZSingle);
     /*
     CPPUNIT_TEST(TestCase_GetTileProviders);
     CPPUNIT_TEST(TestCase_MgMap_Inline);
@@ -78,7 +78,7 @@
 private:
     MgMap* CreateMap(CREFSTRING mapName = L"");
     MgMap* CreateMapLinked(CREFSTRING mapName = L"");
-    MgMap* CreateMapLinked2(CREFSTRING mapName = L"");
+    MgMap* CreateMapLinked2(CREFSTRING mapName = L"", CREFSTRING ovCsWkt = L"");
     INT32 Rand(INT32 n);
 
 private:



More information about the mapguide-commits mailing list