[mapguide-commits] r9191 - sandbox/jng/utfgrid/Common/Renderers

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Tue May 30 08:19:47 PDT 2017


Author: jng
Date: 2017-05-30 08:19:47 -0700 (Tue, 30 May 2017)
New Revision: 9191

Modified:
   sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.cpp
   sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.h
   sandbox/jng/utfgrid/Common/Renderers/UTFGridRenderer.cpp
Log:
Convert the utfgrid content rendering into a 2-pass process:
 - 1st pass: Track the rendered "pixels" from the AGG backend and re-map them to a new replacement pixel starting from (decoded: 0, encoded: 32) that increments with each tracked pixel. This pass is to ensure that:
   a. Our utf character budget is utilized to the max. We don't want to skip across any valid characters.
   b. We can render keys in the order of the re-mapped "pixels". This is important as the rendered character "pixel" must resolve to the index of the "keys" array (per the UTFGrid spec). Re-mapping character pixels allows us to establish this relationship.
 - 2nd pass: With the pixels re-mapped, render the utfgrid with the re-mapped pixels.

This commit also ensures any FDO string properties are escaped for JSON content rendering.

Modified: sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.cpp
===================================================================
--- sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.cpp	2017-05-26 16:05:42 UTC (rev 9190)
+++ sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.cpp	2017-05-30 15:19:47 UTC (rev 9191)
@@ -7,7 +7,10 @@
 //feature
 #define DEBUG_UTFGRID
 
+#define EMPTY_ENCODED_CHAR 32
+
 UTFGridContent::UTFGridContent()
+    : m_currentRemappedColor(0)
 {
 }
 
@@ -22,6 +25,27 @@
     return mbContent;
 }
 
+void UTFGridContent::EscapeJsonString(const std::wstring& input, std::wstring& output)
+{
+    std::wostringstream ss;
+    for (std::wstring::const_iterator iter = input.begin(); iter != input.end(); iter++)
+    {
+        switch (*iter)
+        {
+        case '\\': ss << L"\\\\"; break;
+        case '"': ss << L"\\\""; break;
+        case '/': ss << L"\\/"; break;
+        case '\b': ss << L"\\b"; break;
+        case '\f': ss << L"\\f"; break;
+        case '\n': ss << L"\\n"; break;
+        case '\r': ss << L"\\r"; break;
+        case '\t': ss << L"\\t"; break;
+        default: ss << *iter; break;
+        }
+    }
+    output.assign(ss.str());
+}
+
 bool UTFGridContent::AddFeature(RS_LayerUIInfo* layer, unsigned int decodedColor, RS_FeatureReader* feature, const RS_String * tooltip, const RS_String * url)
 {
     static const size_t MAX_STRING = 64;
@@ -61,9 +85,22 @@
             int propType = feature->GetPropertyType(propName);
             if (propType == FdoDataType_String || propType == FdoDataType_DateTime)
             {
-                wVal.append(L"\"");
-                wVal.append(propVal);
-                wVal.append(L"\"");
+                if (propType == FdoDataType_String)
+                {
+                    std::wstring oPropVal;
+                    std::wstring iPropVal = propVal;
+                    EscapeJsonString(iPropVal, oPropVal);
+
+                    wVal.append(L"\"");
+                    wVal.append(oPropVal);
+                    wVal.append(L"\"");
+                }
+                else 
+                {
+                    wVal.append(L"\"");
+                    wVal.append(propVal);
+                    wVal.append(L"\"");
+                }
             }
             else 
             {
@@ -132,10 +169,37 @@
         m_content << "," << std::endl << "    \"";
 }
 
+void UTFGridContent::BeginTracking()
+{
+    _ASSERT(m_currentRemappedColor == 0);
+    // Pre-register the "empty space" pixel
+    m_trackedEncodedColors.insert(EMPTY_ENCODED_CHAR);
+    m_remappedEncodedColors.insert(std::make_pair(EMPTY_ENCODED_CHAR, EncodeChar(m_currentRemappedColor)));
+    m_remappedEncodedColorsInv.insert(std::make_pair(m_currentRemappedColor, EMPTY_ENCODED_CHAR));
+}
+
+void UTFGridContent::TrackPixel(unsigned int pixel)
+{
+    // To maximize our utf character "budget", start from 0 again and for each rendered
+    // "pixel" we encounter, map it to our re-mapped and pre-encoded offset pixel. When it comes time to render
+    // the actual content, use the re-mapped offset pixel instead of what was rendered to the AGG backend, but
+    // retain the use of the original pixel for accessing the keys and data to be rendered out
+    if (m_trackedEncodedColors.find(pixel) == m_trackedEncodedColors.end())
+    {
+        m_currentRemappedColor++;
+        m_remappedEncodedColors.insert(std::make_pair(pixel, EncodeChar(m_currentRemappedColor)));
+        m_remappedEncodedColorsInv.insert(std::make_pair(m_currentRemappedColor, pixel));
+        m_trackedEncodedColors.insert(pixel);
+    }
+}
+
 void UTFGridContent::AppendRowPixel(unsigned int pixel)
 {
-    m_trackedEncodedColors.insert(pixel);
-    m_content << (wchar_t)pixel;
+    // This pixel should've been already re-mapped
+    _ASSERT(m_remappedEncodedColors.find(pixel) != m_remappedEncodedColors.end());
+
+    // Don't write the original pixel, write its re-mapped one
+    m_content << (wchar_t)m_remappedEncodedColors[pixel];
 }
 
 void UTFGridContent::EndGridRow()
@@ -152,20 +216,15 @@
 {
     m_content << "," << std::endl;
     m_content << "  \"keys\": [" << std::endl;
-    for (ColorSet::iterator it = m_trackedEncodedColors.begin(); it != m_trackedEncodedColors.end(); it++)
+    m_content << "    \"\""; //This is the "empty space" pixel
+    for (unsigned int i = 1; i <= m_currentRemappedColor; i++)
     {
-        if (it != m_trackedEncodedColors.begin())
-            m_content << "," << std::endl;
+        //Every remapped pixel in this "ramp" should be accounted for
+        _ASSERT(m_remappedEncodedColorsInv.find(i) != m_remappedEncodedColorsInv.end());
 
-        if (*it == 32) // This is the "empty space" pixel
-        {
-            m_content << "    \"\"";
-        }
-        else
-        {
-            unsigned int decodedKey = DecodeChar(*it);
-            m_content << "    \"" << decodedKey << "\"";
-        }
+        m_content << "," << std::endl;
+        unsigned int decodedKey = DecodeChar(m_remappedEncodedColorsInv[i]);
+        m_content << "    \"" << decodedKey << "\"";
     }
     m_content << std::endl << "  ]";
 }
@@ -177,7 +236,7 @@
     bool bFirst = true;
     for (ColorSet::iterator it = m_trackedEncodedColors.begin(); it != m_trackedEncodedColors.end(); it++)
     {
-        if (*it == 32) // This is the "empty space" pixel
+        if (*it == EMPTY_ENCODED_CHAR) // This is the "empty space" pixel
         {
             continue;
         }

Modified: sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.h
===================================================================
--- sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.h	2017-05-26 16:05:42 UTC (rev 9190)
+++ sandbox/jng/utfgrid/Common/Renderers/UTFGridContent.h	2017-05-30 15:19:47 UTC (rev 9191)
@@ -25,6 +25,7 @@
 #include <set>
 #include <sstream>
 
+typedef std::map<unsigned int, unsigned int> ColorReMap;
 typedef std::map<unsigned int, std::string> ColorKeyMap;
 typedef std::map<std::string, std::wstring> KeyFeatureMap;
 typedef std::set<unsigned int> ColorSet;
@@ -41,6 +42,8 @@
 
     void StartGrid();
     void StartGridRow(bool bFirst);
+    void BeginTracking();
+    void TrackPixel(unsigned int pixel);
     void AppendRowPixel(unsigned int pixel);
     void EndGridRow();
     void EndGrid();
@@ -52,9 +55,12 @@
     static unsigned int DecodeChar(unsigned int toDecode);
 
 private:
-
+    static void EscapeJsonString(const std::wstring& input, std::wstring& output);
+    unsigned int m_currentRemappedColor;
     std::wstringstream m_content;
     ColorSet m_trackedEncodedColors;
+    ColorReMap m_remappedEncodedColors;
+    ColorReMap m_remappedEncodedColorsInv;
 
     KeyFeatureMap m_features;
     ColorKeyMap m_pixels;

Modified: sandbox/jng/utfgrid/Common/Renderers/UTFGridRenderer.cpp
===================================================================
--- sandbox/jng/utfgrid/Common/Renderers/UTFGridRenderer.cpp	2017-05-26 16:05:42 UTC (rev 9190)
+++ sandbox/jng/utfgrid/Common/Renderers/UTFGridRenderer.cpp	2017-05-30 15:19:47 UTC (rev 9191)
@@ -116,14 +116,31 @@
 {
     m_mapInfo = NULL;
 
+    //First pass: Track the rendered pixels
+    m_content->BeginTracking();
+
+    unsigned int bufHeight = m_context->rendering_buffer.height();
+    unsigned int bufWidth = m_context->rendering_buffer.width();
+
+    for (int y = bufHeight - 1; y >= 0; y--)
+    {
+        utfgrid_band_type* row_ptr = m_context->rendering_buffer.row_ptr(y);
+        for (int x = 0; x < bufWidth; x++)
+        {
+            m_content->TrackPixel(row_ptr[x]);
+        }
+    }
+
+    //Second pass: Render the actual grid content. The tracked pixels in the previous pass
+    //will assist in any re-mapping (as character space is at a premium)
     m_content->StartGrid();
     bool bFirst = true;
     //Y-axis is flipped, so reverse iterate the y-axis
-    for (int y = m_context->rendering_buffer.height() - 1; y >= 0; y--)
+    for (int y = bufHeight - 1; y >= 0; y--)
     {
         m_content->StartGridRow(bFirst);
         utfgrid_band_type* row_ptr = m_context->rendering_buffer.row_ptr(y);
-        for (int x = 0; x < m_context->rendering_buffer.width(); x++)
+        for (int x = 0; x < bufWidth; x++)
         {
             m_content->AppendRowPixel(row_ptr[x]);
         }



More information about the mapguide-commits mailing list