[mapguide-commits] r9576 - in trunk/MgDev: . Common/MapGuideCommon/Services Common/PlatformBase/Services Oem/LinuxApt Server/src/Services/Feature Web/src/ApacheAgent Web/src/CgiAgent Web/src/HttpHandler Web/src/IsapiAgent Web/src/MapAgentCommon

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Wed Jul 17 02:32:14 PDT 2019


Author: jng
Date: 2019-07-17 02:32:14 -0700 (Wed, 17 Jul 2019)
New Revision: 9576

Modified:
   trunk/MgDev/
   trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.cpp
   trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.h
   trunk/MgDev/Common/PlatformBase/Services/FeatureDefs.h
   trunk/MgDev/Common/PlatformBase/Services/FeatureService.h
   trunk/MgDev/Oem/LinuxApt/
   trunk/MgDev/Server/src/Services/Feature/FeatureOperationFactory.cpp
   trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.cpp
   trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.h
   trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj
   trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters
   trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp
   trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp
   trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.h
   trunk/MgDev/Web/src/ApacheAgent/ApachePostParser.cpp
   trunk/MgDev/Web/src/CgiAgent/CgiPostParser.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp
   trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h
   trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp
   trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp
   trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.cpp
   trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.h
   trunk/MgDev/Web/src/HttpHandler/WmsMapUtil.cpp
   trunk/MgDev/Web/src/IsapiAgent/IsapiPostParser.cpp
   trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.cpp
   trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.h
Log:
MapGuide RFC 175: Merge wfs_hits sandbox.

Index: trunk/MgDev
===================================================================
--- trunk/MgDev	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev	2019-07-17 09:32:14 UTC (rev 9576)

Property changes on: trunk/MgDev
___________________________________________________________________
Modified: svn:mergeinfo
## -30,5 +30,6 ##
 /sandbox/jng/utfgrid:9179-9212
 /sandbox/jng/v30:8212-8227
 /sandbox/jng/v4:9511-9519
+/sandbox/jng/wfs_hits:9569-9575
 /sandbox/rfc94:5099-5163
 /trunk/MgDev:9397-9399
\ No newline at end of property
Modified: trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.cpp
===================================================================
--- trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -1505,6 +1505,28 @@
     return SAFE_ADDREF((MgProxyFeatureReader*)featReader);
 }
 
+INT32 MgProxyFeatureService::GetWfsFeatureTotal(MgResourceIdentifier* featureSourceId,
+                                                CREFSTRING featureClass,
+                                                CREFSTRING filter,
+                                                INT32 maxFeatures)
+{
+    MgCommand cmd;
+    cmd.ExecuteCommand(m_connProp,                   // Connection
+        MgCommand::knInt32,                          // Return type expected
+        MgFeatureServiceOpId::GetWfsFeatureTotal_Id, // Command Code
+        4,                                           // No of arguments
+        Feature_Service,                             // Service Id
+        BUILD_VERSION(4, 0, 0),                      // Operation version
+        MgCommand::knObject, featureSourceId,        // Argument#1
+        MgCommand::knString, &featureClass,          // Argument#2
+        MgCommand::knString, &filter,                // Argument#3
+        MgCommand::knInt32, maxFeatures,             // Argument#4
+        MgCommand::knNone);                          // End of argument
+
+    SetWarning(cmd.GetWarningObject());
+    return cmd.GetReturnValue().val.m_i32;
+}
+
 //////////////////////////////////////////////////////////////////
 MgBatchPropertyCollection* MgProxyFeatureService::GetFeatures(CREFSTRING featureReader)
 {

Modified: trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.h
===================================================================
--- trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Common/MapGuideCommon/Services/ProxyFeatureService.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -1563,6 +1563,49 @@
                                   CREFSTRING filter,
                                   CREFSTRING sortCriteria);
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Retrieves the total feature count for the given WFS query
+    ///
+    /// \note1
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual int GetWfsFeatureTotal(MgResourceIdentifier featureSourceId, string featureClass, string filter, int maxFeatures);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual int GetWfsFeatureTotal(MgResourceIdentifier featureSourceId, string featureClass, string filter, int maxFeatures);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual int GetWfsFeatureTotal(MgResourceIdentifier featureSourceId, string featureClass, string filter, int maxFeatures);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param featureSourceId (MgResourceIdentifier)
+    /// The resource identifier defining the
+    /// location of the feature source in
+    /// the repository.
+    /// \param featureClass (String/string)
+    /// The feature class containing the features to retrieve.
+    /// \param filter (String/string)
+    /// An XML string containing the definition for an OGC filter
+    /// \param maxFeatures (int)
+    /// If greater than 0 and less than the real computed total, this will be the value returned
+    ///
+    /// \remarks
+    /// This method is primarily used to service the WFS GetFeatures operation in resultType=hits mode
+    /// where a total is desired over requesting the whole set of feature data
+    ///
+    /// \return
+    /// Returns an MgByteReader containing the requested feature information.
+    ///
+    /// \exception MgInvalidArgumentException
+    ///
+    /// \since 4.0
+    INT32 GetWfsFeatureTotal(MgResourceIdentifier* featureSourceId,
+                             CREFSTRING featureClass,
+                             CREFSTRING filter,
+                             INT32 maxFeatures);
+
     ////////////////////////////////////////////////////////////////////////////////
     /// \brief
     /// This method enumerates all the providers and if they are FDO enabled for

Modified: trunk/MgDev/Common/PlatformBase/Services/FeatureDefs.h
===================================================================
--- trunk/MgDev/Common/PlatformBase/Services/FeatureDefs.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Common/PlatformBase/Services/FeatureDefs.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -85,6 +85,7 @@
     static const int SelectFeaturesWithTransform = 0x1111ED35;
 
     static const int GetWfsReader_Id = 0x1111ED36;
+    static const int GetWfsFeatureTotal_Id = 0x1111ED37;
 };
 /// \endcond
 

Modified: trunk/MgDev/Common/PlatformBase/Services/FeatureService.h
===================================================================
--- trunk/MgDev/Common/PlatformBase/Services/FeatureService.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Common/PlatformBase/Services/FeatureService.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -2046,13 +2046,13 @@
     ///
     /// <!-- Syntax in .Net, Java, and PHP -->
     /// \htmlinclude DotNetSyntaxTop.html
-    /// virtual MgFeatureReader GetWfsReader(MgResourceIdentifier featureSourceId, string featureClass, MgStringCollection requiredProperties, string srs, string filter, int maxFeatures, string outputFormat, string sortCriteria);
+    /// virtual MgFeatureReader GetWfsReader(MgResourceIdentifier featureSourceId, string featureClass, MgStringCollection requiredProperties, string srs, string filter, string sortCriteria);
     /// \htmlinclude SyntaxBottom.html
     /// \htmlinclude JavaSyntaxTop.html
-    /// virtual MgFeatureReader GetWfsReader(MgResourceIdentifier featureSourceId, string featureClass, MgStringCollection requiredProperties, string srs, string filter, int maxFeatures, string outputFormat, string sortCriteria);
+    /// virtual MgFeatureReader GetWfsReader(MgResourceIdentifier featureSourceId, string featureClass, MgStringCollection requiredProperties, string srs, string filter, string sortCriteria);
     /// \htmlinclude SyntaxBottom.html
     /// \htmlinclude PHPSyntaxTop.html
-    /// virtual MgFeatureReader GetWfsReader(MgResourceIdentifier featureSourceId, string featureClass, MgStringCollection requiredProperties, string srs, string filter, int maxFeatures, string outputFormat, string sortCriteria);
+    /// virtual MgFeatureReader GetWfsReader(MgResourceIdentifier featureSourceId, string featureClass, MgStringCollection requiredProperties, string srs, string filter, string sortCriteria);
     /// \htmlinclude SyntaxBottom.html
     ///
     /// \param featureSourceId (MgResourceIdentifier)
@@ -2122,6 +2122,49 @@
 
 INTERNAL_API:
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Retrieves the total feature count for the given WFS query
+    ///
+    /// \note1
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual int GetWfsFeatureTotal(MgResourceIdentifier featureSourceId, string featureClass, string filter, int maxFeatures);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual int GetWfsFeatureTotal(MgResourceIdentifier featureSourceId, string featureClass, string filter, int maxFeatures);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual int GetWfsFeatureTotal(MgResourceIdentifier featureSourceId, string featureClass, string filter, int maxFeatures);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param featureSourceId (MgResourceIdentifier)
+    /// The resource identifier defining the
+    /// location of the feature source in
+    /// the repository.
+    /// \param featureClass (String/string)
+    /// The feature class containing the features to retrieve.
+    /// \param filter (String/string)
+    /// An XML string containing the definition for an OGC filter
+    /// \param maxFeatures (int)
+    /// If greater than 0 and less than the real computed total, this will be the value returned
+    ///
+    /// \remarks
+    /// This method is primarily used to service the WFS GetFeatures operation in resultType=hits mode
+    /// where a total is desired over requesting the whole set of feature data
+    ///
+    /// \return
+    /// Returns an MgByteReader containing the requested feature information.
+    ///
+    /// \exception MgInvalidArgumentException
+    ///
+    /// \since 4.0
+    virtual INT32 GetWfsFeatureTotal(MgResourceIdentifier* featureSourceId,
+        CREFSTRING featureClass,
+        CREFSTRING filter,
+        INT32 maxFeatures) = 0;
+
     //////////////////////////////////////////////////////////////////
     /// \brief
     /// Construct an MgFeatureService object.

Index: trunk/MgDev/Oem/LinuxApt
===================================================================
--- trunk/MgDev/Oem/LinuxApt	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Oem/LinuxApt	2019-07-17 09:32:14 UTC (rev 9576)

Property changes on: trunk/MgDev/Oem/LinuxApt
___________________________________________________________________
Modified: svn:ignore
## -1,3 +1,3 ##
 *.tar
-httpd-2.4.29
-php-5.6.33
+httpd-2.4.37
+php-5.6.40
Modified: trunk/MgDev/Server/src/Services/Feature/FeatureOperationFactory.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/FeatureOperationFactory.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/FeatureOperationFactory.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -59,6 +59,7 @@
 #include "OpDescribeWfsFeatureType.h"
 #include "OpGetWfsFeature.h"
 #include "OpGetWfsReader.h"
+#include "OpGetWfsFeatureTotal.h"
 #include "OpEnumerateDataStores.h"
 #include "OpGetSchemaMapping.h"
 #include "OpGetFdoCacheInfo.h"
@@ -592,6 +593,18 @@
         }
         break;
 
+    case MgFeatureServiceOpId::GetWfsFeatureTotal_Id:
+        switch (VERSION_NO_PHASE(operationVersion))
+        {
+        case VERSION_SUPPORTED(4, 0):
+            handler.reset(new MgOpGetWfsFeatureTotal());
+            break;
+        default:
+            throw new MgInvalidOperationVersionException(
+                L"MgFeatureOperationFactory.GetOperation", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+        break;
+
     case MgFeatureServiceOpId::EnumerateDataStores_Id:
         switch (VERSION_NO_PHASE(operationVersion))
         {

Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -2067,6 +2067,28 @@
 }
 
 
+INT32 MgServerFeatureService::GetWfsFeatureTotal(MgResourceIdentifier* fs,
+                                                 CREFSTRING featureClass,
+                                                 CREFSTRING wfsFilter,
+                                                 INT32 maxFeatures)
+{
+    INT32 total = -1;
+
+    MG_LOG_TRACE_ENTRY(L"MgServerFeatureService::GetWfsFeatureTotal()");
+
+    Ptr<MgFeatureReader> mgfReader;
+
+    MG_FEATURE_SERVICE_TRY()
+
+    MgWfsQueryAdapter wfsQuery(this);
+    wfsQuery.SetOptions(fs, featureClass, NULL, L"", wfsFilter, L"");
+    total = wfsQuery.GetTotal(maxFeatures);
+
+    MG_FEATURE_SERVICE_CHECK_CONNECTION_CATCH_AND_THROW(fs, L"MgServerFeatureService.GetWfsFeatureTotal")
+
+    return total;
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 /// \brief
 /// Retrieves feature information based on the supplied criteria with specified format.

Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.h
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -952,6 +952,11 @@
                                   CREFSTRING filter,
                                   CREFSTRING sortCriteria);
 
+    INT32 GetWfsFeatureTotal(MgResourceIdentifier* featureSourceId,
+                             CREFSTRING featureClass,
+                             CREFSTRING filter,
+                             INT32 maxFeatures);
+
     ////////////////////////////////////////////////////////////////////////////////
     /// <summary>
     /// This method enumerates all the providers and if they are FDO enabled for

Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj	2019-07-17 09:32:14 UTC (rev 9576)
@@ -513,6 +513,12 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="OpGetWfsFeatureTotal.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="OpXmlToSchema.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -864,6 +870,7 @@
     <ClInclude Include="OpGetSpatialContexts.h" />
     <ClInclude Include="OpGetSqlRows.h" />
     <ClInclude Include="OpGetWfsFeature.h" />
+    <ClInclude Include="OpGetWfsFeatureTotal.h" />
     <ClInclude Include="OpGetWfsReader.h" />
     <ClInclude Include="OpInsertFeatures.h" />
     <ClInclude Include="OpInsertFeaturesBatched.h" />

Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters	2019-07-17 09:32:14 UTC (rev 9576)
@@ -205,6 +205,9 @@
       <Filter>Ops</Filter>
     </ClCompile>
     <ClCompile Include="WfsQueryAdapter.cpp" />
+    <ClCompile Include="OpGetWfsFeatureTotal.cpp">
+      <Filter>Ops</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="FeatureOperation.h">
@@ -419,6 +422,9 @@
       <Filter>Ops</Filter>
     </ClInclude>
     <ClInclude Include="WfsQueryAdapter.h" />
+    <ClInclude Include="OpGetWfsFeatureTotal.h">
+      <Filter>Ops</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="ServerFeatureService.rc" />

Modified: trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -86,6 +86,7 @@
 #include "OpGetIdentityProperties.cpp"
 #include "OpDescribeWfsFeatureType.cpp"
 #include "OpGetWfsFeature.cpp"
+#include "OpGetWfsFeatureTotal.cpp"
 #include "OpGetWfsReader.cpp"
 #include "FilterUtil.cpp"
 #include "WfsQueryAdapter.cpp"

Modified: trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -213,4 +213,29 @@
     fr = m_featSvc->SelectFeatures(m_fs, m_className, m_options);
     MG_FEATURE_SERVICE_CHECK_CONNECTION_CATCH_AND_THROW(m_fs, L"MgWfsQueryAdapter.GetWfsReader")
     return fr.Detach();
+}
+
+INT32 MgWfsQueryAdapter::GetTotal(INT32 maxFeatures)
+{
+    INT32 total = 0;
+
+    MG_FEATURE_SERVICE_TRY()
+    // TODO: can FeatureName be an extension name rather than a FeatureClass?
+    Ptr<MgFeatureReader> fr = m_featSvc->SelectFeatures(m_fs, m_className, m_options);
+
+    // You may think: Why are we raw spinning this reader? We have select aggregates (with COUNT()) 
+    // and other performant shortcuts available, but the real stickler is the maxFeatures parameter
+    // passed in. WFS queries allow for this to be passed in and if we get one, we must respect
+    // the value. None of our performant shortcuts allow us to apply an upper limit on it. So given
+    // these constraints, raw spinning the feature reader is the only viable option.
+    while (fr->ReadNext())
+    {
+        total++;
+        //Break loop if we hit max features
+        if (maxFeatures > 0 && total >= maxFeatures)
+            break;
+    }
+
+    MG_FEATURE_SERVICE_CHECK_CONNECTION_CATCH_AND_THROW(m_fs, L"MgWfsQueryAdapter.GetWfsReader")
+    return total;
 }
\ No newline at end of file

Modified: trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.h
===================================================================
--- trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Server/src/Services/Feature/WfsQueryAdapter.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -37,6 +37,7 @@
     MgCoordinateSystem* GetMapCs();
     MgCoordinateSystemTransform* GetTransform();
     MgClassDefinition* GetClassDefinition();
+    INT32 GetTotal(INT32 maxFeatures);
 
 private:
     Ptr<MgClassDefinition> m_classDef;

Modified: trunk/MgDev/Web/src/ApacheAgent/ApachePostParser.cpp
===================================================================
--- trunk/MgDev/Web/src/ApacheAgent/ApachePostParser.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/ApacheAgent/ApachePostParser.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -158,7 +158,7 @@
     // The check for text/xml is not always sufficient.  CarbonTools, for example,
     // fails to set Content-Type: text/xml and just sends Content-Type: utf-8.
     // A better check might be looking into the buffer to find "<?xml" at the beginning.
-    else if (content.find(MapAgentStrings::TextXml) != content.npos || MapAgentCommon::IsXmlPi((char *)m_pBuffer))
+    else if (MapAgentStrings::IsXmlMimeType(content) || MapAgentCommon::IsXmlPi((char *)m_pBuffer))
     {
         m_pBuffer[totalBytes] = '\0';
         params->SetXmlPostData((char *)m_pBuffer);

Modified: trunk/MgDev/Web/src/CgiAgent/CgiPostParser.cpp
===================================================================
--- trunk/MgDev/Web/src/CgiAgent/CgiPostParser.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/CgiAgent/CgiPostParser.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -145,9 +145,9 @@
                 // url-encoded, since the question mark in <?xml...?>
                 // should itself be url-encoded: <%3Fxml... )
                 if(IsXmlPi(m_buf))
-	                params->SetXmlPostData(m_buf);
+                    params->SetXmlPostData(m_buf);
                 else
-	                MapAgentGetParser::Parse(m_buf, params);
+                    MapAgentGetParser::Parse(m_buf, params);
             }
         }
         else if (content.find(MapAgentStrings::MultiPartForm) != content.npos)
@@ -281,7 +281,7 @@
         // The check for text/xml is not always sufficient.  CarbonTools, for example,
         // fails to set Content-Type: text/xml and just sends Content-Type: utf-8.
         // A better check might be looking into the buffer to find "<?xml" at the beginning.
-        else if (content.find(MapAgentStrings::TextXml) != content.npos || IsXmlPi(m_buf))
+        else if (MapAgentStrings::IsXmlMimeType(content) || IsXmlPi(m_buf))
         {
             m_buf[nBytes] = '\0';
             params->SetXmlPostData(m_buf);

Modified: trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -326,6 +326,7 @@
 const STRING MgHttpResourceStrings::reqWfsBbox = L"BBOX";
 const STRING MgHttpResourceStrings::reqWfsOutputFormat = L"OUTPUTFORMAT";
 const STRING MgHttpResourceStrings::reqWfsSortBy = L"SORTBY";
+const STRING MgHttpResourceStrings::reqWfsResultType = L"RESULTTYPE";
 
 // Web Application Parameters
 const STRING MgHttpResourceStrings::reqFormat = L"FORMAT";

Modified: trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/HttpHandler/HttpResourceStrings.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -237,6 +237,7 @@
     static const STRING reqWfsBbox;
     static const STRING reqWfsOutputFormat;
     static const STRING reqWfsSortBy;
+    static const STRING reqWfsResultType;
 
     // PREDEFINED WEB APPLICATION REQUEST PARAMETERS
     static const STRING reqFormat;

Modified: trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/HttpHandler/HttpWfsGetFeature.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -300,41 +300,79 @@
                                 numFeaturesToRetrieve = limit;
                             }
 
-                            // Call the C++ API
-                            // NOTE: I updated the maxFeatures value from numFeaturesToRetrieve to numFeaturesToRetrieve-1
-                            // Because the MgServerFdoFeatureReader in MapGuide server uses -1 to mark empty, while MgWfsFeatures
-                            // in MapGuide web tier uses 0
+                            // Is this hit mode? If so, just request the raw total
+                            if (m_getFeatureParams->IsHitMode())
+                            {
+                                INT32 total = featureService->GetWfsFeatureTotal(featureSourceId, ((sSchemaHash.size() == 0) ? sClass : sSchemaHash + _(":") + sClass), sSortCriteria, numFeaturesToRetrieve);
+                                std::string sTotal;
+                                MgUtil::Int32ToString(total, sTotal);
+                                if (sOutputFormat == MgMimeType::Json)
+                                {
+                                    std::string json = "{";
+                                    json += "\"numberOfFeatures\": ";
+                                    json += sTotal;
+                                    json += "}"; 
+                                    Ptr<MgByteSource> bs = new MgByteSource((BYTE_ARRAY_IN)json.data(), (INT32)json.length());
+                                    bs->SetMimeType(MgMimeType::Json);
+                                    resultReader = bs->GetReader();
+                                }
+                                else
+                                {
+                                    std::string mbPrefix;
+                                    MgUtil::WideCharToMultiByte(sPrefix, mbPrefix);
 
-                            //For GeoJSON, use the new GetWfsReader API
-                            if (sOutputFormat == MgMimeType::Json)
-                            {
-                                // NOTE: This API doesn't accept WFS version, format and XML namepaces because these are GML-isms baked into the GetWfsFeature API itself, making
-                                // it unsuitable for non-GML output, hence the need for this new GetWfsReader API
-                                Ptr<MgFeatureReader> fr = featureService->GetWfsReader(featureSourceId, ((sSchemaHash.size() == 0) ? sClass : sSchemaHash + _(":") + sClass),
-                                    requiredProperties, m_getFeatureParams->GetSrs(), filter, sSortCriteria);
-                                
-                                //MgByteSource owns this and will clean it up when done
-                                MgReaderByteSourceImpl* bsImpl = new MgReaderByteSourceImpl(fr, MgMimeType::Json, true, false, -1, NULL);
-                                bsImpl->SetMaxFeatures(numFeaturesToRetrieve - 1);
-                                Ptr<MgByteSource> bs = new MgByteSource(bsImpl);
-                                resultReader = bs->GetReader();
+                                    std::string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
+                                    xml += "<wfs:FeatureCollection xmlns:gml=\"http://www.opengis.net/gml\" xmlns:wfs=\"http://www.opengis.net/wfs\" xmlns:";
+                                    xml += mbPrefix;
+                                    xml += "=\"http://fdo.osgeo.org/schemas/feature/";
+                                    xml += mbPrefix;
+                                    xml += "\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/base/feature.xsd http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"";
+                                    xml += " numberOfFeatures=\"";
+                                    xml += sTotal;
+                                    xml += "\" />";
+                                    Ptr<MgByteSource> bs = new MgByteSource((BYTE_ARRAY_IN)xml.data(), (INT32)xml.length());
+                                    bs->SetMimeType(MgMimeType::Xml);
+                                    resultReader = bs->GetReader();
+                                }
                             }
-                            else 
+                            else
                             {
                                 // Call the C++ API
                                 // NOTE: I updated the maxFeatures value from numFeaturesToRetrieve to numFeaturesToRetrieve-1
                                 // Because the MgServerFdoFeatureReader in MapGuide server uses -1 to mark empty, while MgWfsFeatures
                                 // in MapGuide web tier uses 0
-                                resultReader = featureService->GetWfsFeature(featureSourceId, ((sSchemaHash.size() == 0) ? sClass : sSchemaHash + _(":") + sClass),
-                                    requiredProperties, m_getFeatureParams->GetSrs(), filter, numFeaturesToRetrieve - 1, sVersion, sOutputFormat, sSortCriteria, sPrefix, oFeatureTypes.GetNamespaceUrl());
 
-                                // Store the MgByteReader directly for retrieval
-                                //
-                                // DO NOT PASS THROUGH OGC XML TEMPLATE PROCESSING CODE!
-                                // DO NOT PASS GO!
-                                // DO NOT COLLECT MEMORY SPIKES NEEDLESSLY BUFFERING XML TEMPLATE CONTENT AS A RESULT!
-                                //
-                                // This *is* already the WFS GetFeature response. There is nothing to post-process through the XML templates!
+                                //For GeoJSON, use the new GetWfsReader API
+                                if (sOutputFormat == MgMimeType::Json)
+                                {
+                                    // NOTE: This API doesn't accept WFS version, format and XML namepaces because these are GML-isms baked into the GetWfsFeature API itself, making
+                                    // it unsuitable for non-GML output, hence the need for this new GetWfsReader API
+                                    Ptr<MgFeatureReader> fr = featureService->GetWfsReader(featureSourceId, ((sSchemaHash.size() == 0) ? sClass : sSchemaHash + _(":") + sClass),
+                                        requiredProperties, m_getFeatureParams->GetSrs(), filter, sSortCriteria);
+
+                                    //MgByteSource owns this and will clean it up when done
+                                    MgReaderByteSourceImpl* bsImpl = new MgReaderByteSourceImpl(fr, MgMimeType::Json, true, false, -1, NULL);
+                                    bsImpl->SetMaxFeatures(numFeaturesToRetrieve - 1);
+                                    Ptr<MgByteSource> bs = new MgByteSource(bsImpl);
+                                    resultReader = bs->GetReader();
+                                }
+                                else
+                                {
+                                    // Call the C++ API
+                                    // NOTE: I updated the maxFeatures value from numFeaturesToRetrieve to numFeaturesToRetrieve-1
+                                    // Because the MgServerFdoFeatureReader in MapGuide server uses -1 to mark empty, while MgWfsFeatures
+                                    // in MapGuide web tier uses 0
+                                    resultReader = featureService->GetWfsFeature(featureSourceId, ((sSchemaHash.size() == 0) ? sClass : sSchemaHash + _(":") + sClass),
+                                        requiredProperties, m_getFeatureParams->GetSrs(), filter, numFeaturesToRetrieve - 1, sVersion, sOutputFormat, sSortCriteria, sPrefix, oFeatureTypes.GetNamespaceUrl());
+
+                                    // Store the MgByteReader directly for retrieval
+                                    //
+                                    // DO NOT PASS THROUGH OGC XML TEMPLATE PROCESSING CODE!
+                                    // DO NOT PASS GO!
+                                    // DO NOT COLLECT MEMORY SPIKES NEEDLESSLY BUFFERING XML TEMPLATE CONTENT AS A RESULT!
+                                    //
+                                    // This *is* already the WFS GetFeature response. There is nothing to post-process through the XML templates!
+                                }
                             }
                         }
                         else //Cannot resolve feature source from feature type name

Modified: trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/HttpHandler/ReaderByteSourceImpl.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -166,7 +166,7 @@
         }
     }
 
-    INT32 maxIndex = m_buf.length() - 1;
+    auto maxIndex = m_buf.length() - 1;
     //We have an internal buffer. Clear this out first
     if (m_bufOffset < maxIndex)
     {
@@ -311,7 +311,7 @@
     while (ret < length)
     {
         m_bufOffset++;
-        INT32 maxIndex = m_buf.length() - 1;
+        auto maxIndex = m_buf.length() - 1;
         if (m_bufOffset <= maxIndex)
         {
             buffer[fromIndex + ret] = m_buf[m_bufOffset];

Modified: trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -37,6 +37,7 @@
 ,   m_filterStrings(new MgStringCollection())
 ,   m_featureTypeList(new MgStringCollection())
 ,   m_pNamespaces(new MgXmlNamespaceManager())
+,   m_hitMode(false)
 {
 
     // Get the required properties
@@ -105,6 +106,12 @@
     // Get the sortby property name
     m_sortCriteria = GetRequestParameter(oServer,MgHttpResourceStrings::reqWfsSortBy);
 
+    // Try to set hit mode flag (only if WFS version != 1.0.0 as it was only introduced from 1.1.0 onwards)
+    if (m_version != L"1.0.0")
+    {
+        STRING resultType = GetRequestParameter(oServer, MgHttpResourceStrings::reqWfsResultType);
+        m_hitMode = (resultType == L"hits");
+    }
 }
 
 WfsGetFeatureParams::~WfsGetFeatureParams()
@@ -134,6 +141,7 @@
 ,   m_filterStrings(new MgStringCollection())
 ,   m_featureTypeList(new MgStringCollection())
 ,   m_pNamespaces(new MgXmlNamespaceManager())
+,   m_hitMode(false)
 {
     MgXmlParser parser(xmlRequestString.c_str());
     MgXmlNamespaceManager oNamespaces;
@@ -179,6 +187,15 @@
             if(pBegin->GetAttribute(_("outputFormat"), sOutputFormat) && sOutputFormat.length() > 0)
                 m_outputFormat = oServer.ProcessArgumentAs(_("OutputFormat"),sOutputFormat.c_str());
 
+            // Try to set hit mode flag (only if WFS version != 1.0.0 as it was only introduced from 1.1.0 onwards)
+            if (m_version != L"1.0.0")
+            {
+                STRING sResultType;
+                if (pBegin->GetAttribute(_("resultType"), sResultType) && sResultType.length() > 0)
+                {
+                    m_hitMode = (sResultType == L"hits");
+                }
+            }
 
             // We want to hang onto the namespaces that are
             // defined in the GetFeature request, since that will
@@ -609,3 +626,8 @@
     }
     // --------------------------------------------------------
 }
+
+bool WfsGetFeatureParams::IsHitMode()
+{
+    return m_hitMode;
+}
\ No newline at end of file

Modified: trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.h
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/HttpHandler/WfsGetFeatureParams.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -68,6 +68,14 @@
     ~WfsGetFeatureParams();
 
     /// <summary>
+    /// Gets whether the query is only asking for hits (ie. A total feature count)
+    /// </summary>
+    /// <returns>
+    /// True if the query is asking for hits. False otherwise
+    /// </returns>
+    bool IsHitMode();
+
+    /// <summary>
     /// Retrieves the filter strings for the request
     /// </summary>
     /// <returns>
@@ -183,6 +191,7 @@
     STRING m_outputFormat;
     STRING m_version;
     STRING m_sortCriteria;
+    bool m_hitMode;
 };
 
 #endif  // _FS_WFS_GET_FEATURE_PARAMS_H

Modified: trunk/MgDev/Web/src/HttpHandler/WmsMapUtil.cpp
===================================================================
--- trunk/MgDev/Web/src/HttpHandler/WmsMapUtil.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/HttpHandler/WmsMapUtil.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -219,7 +219,7 @@
 
                         STRING qualifiedName = mgLayer->GetFeatureClassName();
 
-                        int pos = qualifiedName.find(L":");
+                        auto pos = qualifiedName.find(L":");
                         STRING schemaName = qualifiedName.substr(0,pos);
                         STRING className = qualifiedName.substr(pos+1,qualifiedName.length()-pos-1);
 
@@ -527,11 +527,11 @@
     // Looking for all <Bounds> elements from layerBounds
     // Example: <Bounds SRS="EPSG:4326" west="-87.74" south="43.68" east="-87.69" north="43.815"/>
     Ptr<MgStringCollection> bounds = new MgStringCollection();
-    int pos = 0;
+    size_t pos = 0;
     while((pos = layerBounds.find(L"<Bounds",pos)) != STRING::npos)
     {
         pos += 7; // pos+7 to the first character after <Bounds
-        int endPos = layerBounds.find(L"/>",pos); 
+        auto endPos = layerBounds.find(L"/>",pos); 
         bounds->Add(layerBounds.substr(pos,endPos-pos));
     }
 
@@ -541,8 +541,8 @@
         STRING bound = bounds->GetItem(i);
 
         //Find SRS value from Bounds element 
-        int startPos = bound.find(L"SRS=\"");
-        int endPos = bound.find(L"\"",startPos+5); // pos+5 to the first character after SRS="
+        auto startPos = bound.find(L"SRS=\"");
+        auto endPos = bound.find(L"\"",startPos+5); // pos+5 to the first character after SRS="
         STRING srs = bound.substr(startPos+5,endPos-startPos-5);
 
         if(MgUtil::ToUpper(srs) == MgUtil::ToUpper(sSrs))

Modified: trunk/MgDev/Web/src/IsapiAgent/IsapiPostParser.cpp
===================================================================
--- trunk/MgDev/Web/src/IsapiAgent/IsapiPostParser.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/IsapiAgent/IsapiPostParser.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -140,9 +140,9 @@
             // url-encoded, since the question mark in <?xml...?>
             // should itself be url-encoded: <%3Fxml... )
             if (MapAgentCommon::IsXmlPi((char *)m_pBuffer))
-	            params->SetXmlPostData((char *)m_pBuffer);
+                params->SetXmlPostData((char *)m_pBuffer);
             else
-	            MapAgentGetParser::Parse((char *)m_pBuffer, params);
+                MapAgentGetParser::Parse((char *)m_pBuffer, params);
         }
     }
     else if (content.find(MapAgentStrings::MultiPartForm) != content.npos)
@@ -185,7 +185,7 @@
     // The check for text/xml is not always sufficient.  CarbonTools, for example,
     // fails to set Content-Type: text/xml and just sends Content-Type: utf-8.
     // A better check might be looking into the buffer to find "<?xml" at the beginning.
-    else if (content.find(MapAgentStrings::TextXml) != content.npos || MapAgentCommon::IsXmlPi((char *)m_pBuffer))
+    else if (MapAgentStrings::IsXmlMimeType(content) || MapAgentCommon::IsXmlPi((char *)m_pBuffer))
     {
         m_pBuffer[dwTotalBytes] = '\0';
         params->SetXmlPostData((char *)m_pBuffer);

Modified: trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.cpp
===================================================================
--- trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.cpp	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.cpp	2019-07-17 09:32:14 UTC (rev 9576)
@@ -51,6 +51,7 @@
 const char* MapAgentStrings::TextPlain = "text/plain";
 const char* MapAgentStrings::TextHtml = "text/html";
 const char* MapAgentStrings::TextXml = "text/xml";
+const char* MapAgentStrings::ApplicationXml = "application/xml";
 const char* MapAgentStrings::PostBoundary = "boundary=";
 const char* MapAgentStrings::PostName = "name=\"";
 const char* MapAgentStrings::PostContent = "Content-Type: ";
@@ -71,3 +72,9 @@
 
 // used to indicate the creation of temporary files as part of a request
 const wchar_t* MapAgentStrings::TempfileKey = L"tempfile";
+
+bool MapAgentStrings::IsXmlMimeType(const std::string& content)
+{
+    return content.find(MapAgentStrings::TextXml) != content.npos
+        || content.find(MapAgentStrings::ApplicationXml) != content.npos;
+}
\ No newline at end of file

Modified: trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.h
===================================================================
--- trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.h	2019-07-14 13:50:50 UTC (rev 9575)
+++ trunk/MgDev/Web/src/MapAgentCommon/MapAgentStrings.h	2019-07-17 09:32:14 UTC (rev 9576)
@@ -18,6 +18,8 @@
 #ifndef MAPAGENT_STRINGS_H
 #define MAPAGENT_STRINGS_H
 
+#include <string>
+
 class MapAgentStrings
 {
 public:
@@ -54,6 +56,7 @@
     const static char* TextPlain;
     const static char* TextHtml;
     const static char* TextXml;
+    const static char* ApplicationXml;
     const static char* PostBoundary;
     const static char* PostName;
     const static char* PostContent;
@@ -71,5 +74,7 @@
     const static wchar_t* FailedAuth2;
     const static wchar_t* ProductName;
     const static wchar_t* TempfileKey;
+
+    static bool IsXmlMimeType(const std::string& mimeType);
 };
 #endif



More information about the mapguide-commits mailing list