[mapguide-commits] r9242 - in sandbox/jng/ogc: Common/MapGuideCommon/Services Common/PlatformBase/Services Server/src/Services/Feature Web/src/HttpHandler

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Mon Oct 2 15:12:58 PDT 2017


Author: jng
Date: 2017-10-02 15:12:57 -0700 (Mon, 02 Oct 2017)
New Revision: 9242

Added:
   sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.cpp
   sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.h
Modified:
   sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.cpp
   sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.h
   sandbox/jng/ogc/Common/PlatformBase/Services/FeatureDefs.h
   sandbox/jng/ogc/Common/PlatformBase/Services/FeatureService.h
   sandbox/jng/ogc/Server/src/Services/Feature/FeatureOperationFactory.cpp
   sandbox/jng/ogc/Server/src/Services/Feature/Makefile.am
   sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.cpp
   sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.h
   sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj
   sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters
   sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp
   sandbox/jng/ogc/Web/src/HttpHandler/HttpWfsGetFeature.cpp
Log:
First cut of GeoJSON format support for WFS GetFeature. This adds a new API to MgFeatureService, GetWfsReader which takes the same set of parameters as GetWfsFeature except:

 - It omits the parameters (wfsVersion, outputFormat, namespacePrefix, namespaceUrl) which are all GML-related
 - It returns a MgFeatureReader instead of a MgByteReader

The purpose of this API is to allow for a base feature reader to be returned for WFS GetFeatures output (using the same input query parameters), but allowing the caller of the API to control how that reader to be output for a WFS GetFeatures response, which in our case is housing the reader inside a MgReaderByteSourceImpl for automatic streamed conversion to GeoJSON.

This API is used by WFS GetFeature when the format is determined to be: application/json. This API doesn't yet respect the maxFeatures parameter, which we'll address in a follow-up submission.

Modified: sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.cpp
===================================================================
--- sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.cpp	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.cpp	2017-10-02 22:12:57 UTC (rev 9242)
@@ -1473,7 +1473,40 @@
     return (MgByteReader*)cmd.GetReturnValue().val.m_obj;
 }
 
+MgFeatureReader* MgProxyFeatureService::GetWfsReader(MgResourceIdentifier* featureSourceId,
+                                                     CREFSTRING featureClass,
+                                                     MgStringCollection* requiredProperties,
+                                                     CREFSTRING srs,
+                                                     CREFSTRING filter,
+                                                     INT32 maxFeatures,
+                                                     CREFSTRING sortCriteria)
+{
+    MgCommand cmd;
+    cmd.ExecuteCommand(m_connProp,                                  // Connection
+                       MgCommand::knObject,                         // Return type expected
+                       MgFeatureServiceOpId::GetWfsReader_Id,       // Command Code
+                       7,                                           // No of arguments
+                       Feature_Service,                             // Service Id
+                       BUILD_VERSION(3,3,0),                        // Operation version
+                       MgCommand::knObject, featureSourceId,        // Argument#1
+                       MgCommand::knString, &featureClass,          // Argument#2
+                       MgCommand::knObject, requiredProperties,     // Argument#3
+                       MgCommand::knString, &srs,                   // Argument#4
+                       MgCommand::knString, &filter,                // Argument#5
+                       MgCommand::knInt32, maxFeatures,             // Argument#6
+                       MgCommand::knString, &sortCriteria,          // Argument#7
+                       MgCommand::knNone);                          // End of argument
 
+    SetWarning(cmd.GetWarningObject());
+
+    Ptr<MgProxyFeatureReader> featReader = (MgProxyFeatureReader*)cmd.GetReturnValue().val.m_obj;
+
+    if (featReader != NULL)
+        featReader->SetService(this); // Feature reader on proxy side would store proxy service to call GetFeatures()
+
+    return SAFE_ADDREF((MgProxyFeatureReader*)featReader);
+}
+
 //////////////////////////////////////////////////////////////////
 MgBatchPropertyCollection* MgProxyFeatureService::GetFeatures(CREFSTRING featureReader)
 {

Modified: sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.h
===================================================================
--- sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.h	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Common/MapGuideCommon/Services/ProxyFeatureService.h	2017-10-02 22:12:57 UTC (rev 9242)
@@ -1507,6 +1507,63 @@
                                 CREFSTRING namespacePrefix,
                                 CREFSTRING namespaceUrl);
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Retrieves feature information based on the supplied criteria.
+    ///
+    /// \note1
+    ///
+    /// <!-- 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);
+    /// \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);
+    /// \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);
+    /// \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 requiredProperties (MgStringCollection)
+    /// The collection of properties to retrieve for each feature. If the
+    /// collection is null or empty, all properties will be retrieved.
+    /// \param srs (String/string)
+    /// The spatial reference system in which to return feature geometries
+    /// \param filter (String/string)
+    /// An XML string containing the definition for an OGC filter
+    /// \param maxFeatures (int)
+    /// The maximum number of features to retrieve. If the value is less
+    /// than or equal to zero, all features will be retrieved.
+    /// \param sortCriteria (String/string)
+    /// A string identifying the sort criteria
+    ///
+    /// \remarks
+    /// The purpose of this method, as opposed to GetWfsFeature is to return the base reader using the same input parameters
+    /// allowing for the caller to determine the desired output format and the desired content transformations required from 
+    /// the reader.
+    ///
+    /// The main use case for this method is for providing the base feature reader for outputting WFS GetFeature responses in 
+    /// formats other than GML
+    ///
+    /// \return
+    /// Returns an MgByteReader containing the requested feature information.
+    ///
+    /// \exception MgInvalidArgumentException
+    ///
+    MgFeatureReader* GetWfsReader(MgResourceIdentifier* featureSourceId,
+                                  CREFSTRING featureClass,
+                                  MgStringCollection* requiredProperties,
+                                  CREFSTRING srs,
+                                  CREFSTRING filter,
+                                  INT32 maxFeatures,
+                                  CREFSTRING sortCriteria);
+
     ////////////////////////////////////////////////////////////////////////////////
     /// \brief
     /// This method enumerates all the providers and if they are FDO enabled for

Modified: sandbox/jng/ogc/Common/PlatformBase/Services/FeatureDefs.h
===================================================================
--- sandbox/jng/ogc/Common/PlatformBase/Services/FeatureDefs.h	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Common/PlatformBase/Services/FeatureDefs.h	2017-10-02 22:12:57 UTC (rev 9242)
@@ -83,6 +83,8 @@
     static const int UpdateMatchingFeatures = 0x1111ED33;
     static const int DeleteFeatures         = 0x1111ED34;
     static const int SelectFeaturesWithTransform = 0x1111ED35;
+
+    static const int GetWfsReader_Id = 0x1111ED36;
 };
 /// \endcond
 

Modified: sandbox/jng/ogc/Common/PlatformBase/Services/FeatureService.h
===================================================================
--- sandbox/jng/ogc/Common/PlatformBase/Services/FeatureService.h	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Common/PlatformBase/Services/FeatureService.h	2017-10-02 22:12:57 UTC (rev 9242)
@@ -2038,6 +2038,64 @@
                                         CREFSTRING namespacePrefix,
                                         CREFSTRING namespaceUrl) = 0;
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Retrieves feature information based on the supplied criteria.
+    ///
+    /// \note1
+    ///
+    /// <!-- 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);
+    /// \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);
+    /// \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);
+    /// \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 requiredProperties (MgStringCollection)
+    /// The collection of properties to retrieve for each feature. If the
+    /// collection is null or empty, all properties will be retrieved.
+    /// \param srs (String/string)
+    /// The spatial reference system in which to return feature geometries
+    /// \param filter (String/string)
+    /// An XML string containing the definition for an OGC filter
+    /// \param maxFeatures (int)
+    /// The maximum number of features to retrieve. If the value is less
+    /// than or equal to zero, all features will be retrieved.
+    /// \param sortCriteria (String/string)
+    /// A string identifying the sort criteria
+    ///
+    /// \remarks
+    /// The purpose of this method, as opposed to GetWfsFeature is to return the base reader using the same input parameters
+    /// allowing for the caller to determine the desired output format and the desired content transformations required from 
+    /// the reader.
+    ///
+    /// The main use case for this method is for providing the base feature reader for outputting WFS GetFeature responses in 
+    /// formats other than GML
+    ///
+    /// \return
+    /// Returns an MgByteReader containing the requested feature information.
+    ///
+    /// \exception MgInvalidArgumentException
+    ///
+    /// \since 3.3
+    virtual MgFeatureReader* GetWfsReader(MgResourceIdentifier* featureSourceId,
+                                          CREFSTRING featureClass,
+                                          MgStringCollection* requiredProperties,
+                                          CREFSTRING srs,
+                                          CREFSTRING filter,
+                                          INT32 maxFeatures,
+                                          CREFSTRING sortCriteria) = 0;
+
     ////////////////////////////////////////////////////////////////////////////////
     /// \brief
     /// This method enumerates all the providers and if they are FDO enabled for

Modified: sandbox/jng/ogc/Server/src/Services/Feature/FeatureOperationFactory.cpp
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/FeatureOperationFactory.cpp	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Server/src/Services/Feature/FeatureOperationFactory.cpp	2017-10-02 22:12:57 UTC (rev 9242)
@@ -58,6 +58,7 @@
 #include "OpGetIdentityProperties.h"
 #include "OpDescribeWfsFeatureType.h"
 #include "OpGetWfsFeature.h"
+#include "OpGetWfsReader.h"
 #include "OpEnumerateDataStores.h"
 #include "OpGetSchemaMapping.h"
 #include "OpGetFdoCacheInfo.h"
@@ -579,6 +580,18 @@
         }
         break;
 
+    case MgFeatureServiceOpId::GetWfsReader_Id:
+        switch (VERSION_NO_PHASE(operationVersion))
+        {
+        case VERSION_SUPPORTED(3, 3):
+            handler.reset(new MgOpGetWfsReader());
+            break;
+        default:
+            throw new MgInvalidOperationVersionException(
+                L"MgFeatureOperationFactory.GetOperation", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+        break;
+
     case MgFeatureServiceOpId::EnumerateDataStores_Id:
         switch (VERSION_NO_PHASE(operationVersion))
         {

Modified: sandbox/jng/ogc/Server/src/Services/Feature/Makefile.am
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/Makefile.am	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Server/src/Services/Feature/Makefile.am	2017-10-02 22:12:57 UTC (rev 9242)
@@ -105,6 +105,7 @@
   ByteSourceRasterStreamImpl.cpp \
   OpDescribeWfsFeatureType.cpp \
   OpGetWfsFeature.cpp \
+  OpGetWfsReader.cpp \
   FilterUtil.cpp \
   OpEnumerateDataStores.cpp \
   OpGetSchemaMapping.cpp \
@@ -216,6 +217,7 @@
   DateTimeDataReaderCreator.h \
   OpDescribeWfsFeatureType.h \
   OpGetWfsFeature.h \
+  OpGetWfsReader.h \
   FilterUtil.h \
   OpEnumerateDataStores.h \
   OpGetSchemaMapping.h \

Added: sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.cpp
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.cpp	                        (rev 0)
+++ sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.cpp	2017-10-02 22:12:57 UTC (rev 9242)
@@ -0,0 +1,146 @@
+//
+//  Copyright (C) 2004-2017 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 "ServerFeatureServiceDefs.h"
+#include "OpGetWfsFeature.h"
+#include "ServerFeatureService.h"
+#include "LogManager.h"
+
+
+///----------------------------------------------------------------------------
+/// <summary>
+/// Constructs the object.
+/// </summary>
+///----------------------------------------------------------------------------
+MgOpGetWfsReader::MgOpGetWfsReader()
+{
+}
+
+
+///----------------------------------------------------------------------------
+/// <summary>
+/// Destructs the object.
+/// </summary>
+///----------------------------------------------------------------------------
+MgOpGetWfsReader::~MgOpGetWfsReader()
+{
+}
+
+
+///----------------------------------------------------------------------------
+/// <summary>
+/// Executes the operation.
+/// </summary>
+///
+/// <exceptions>
+/// MgException
+/// </exceptions>
+///----------------------------------------------------------------------------
+void MgOpGetWfsReader::Execute()
+{
+    ACE_DEBUG((LM_DEBUG, ACE_TEXT("  (%t) MgOpGetWfsReader::Execute()\n")));
+
+    MG_LOG_OPERATION_MESSAGE(L"GetWfsFeature");
+
+    MG_FEATURE_SERVICE_TRY()
+
+        MG_LOG_OPERATION_MESSAGE_INIT(m_packet.m_OperationVersion, m_packet.m_NumArguments);
+
+    ACE_ASSERT(m_stream != NULL);
+
+    if (7 == m_packet.m_NumArguments)
+    {
+        // Get the feature source
+        Ptr<MgResourceIdentifier> featureSourceId = (MgResourceIdentifier*)m_stream->GetObject();
+
+        // Get the schema name
+        STRING featureClass;
+        m_stream->GetString(featureClass);
+
+        // Get the required properties
+        Ptr<MgStringCollection> requiredProperties = (MgStringCollection*)m_stream->GetObject();
+
+        // Get the srs
+        STRING srs;
+        m_stream->GetString(srs);
+
+        // Get the filter
+        STRING filter;
+        m_stream->GetString(filter);
+
+        // Get the max features to return
+        INT32 maxFeatures;
+        m_stream->GetInt32(maxFeatures);
+
+        // Get the sort criteria
+        STRING sortCriteria;
+        m_stream->GetString(sortCriteria);
+
+        BeginExecution();
+
+        MG_LOG_OPERATION_MESSAGE_PARAMETERS_START();
+        MG_LOG_OPERATION_MESSAGE_ADD_STRING(featureSourceId->ToString().c_str());
+        MG_LOG_OPERATION_MESSAGE_ADD_SEPARATOR();
+        MG_LOG_OPERATION_MESSAGE_ADD_STRING(featureClass.c_str());
+        MG_LOG_OPERATION_MESSAGE_ADD_SEPARATOR();
+        MG_LOG_OPERATION_MESSAGE_ADD_STRING(L"MgStringCollection");
+        MG_LOG_OPERATION_MESSAGE_ADD_SEPARATOR();
+        MG_LOG_OPERATION_MESSAGE_ADD_STRING(srs.c_str());
+        MG_LOG_OPERATION_MESSAGE_ADD_SEPARATOR();
+        MG_LOG_OPERATION_MESSAGE_ADD_STRING(filter.c_str());
+        MG_LOG_OPERATION_MESSAGE_ADD_SEPARATOR();
+        MG_LOG_OPERATION_MESSAGE_ADD_INT32(maxFeatures);
+        MG_LOG_OPERATION_MESSAGE_ADD_SEPARATOR();
+        MG_LOG_OPERATION_MESSAGE_ADD_STRING(sortCriteria.c_str());
+        MG_LOG_OPERATION_MESSAGE_PARAMETERS_END();
+
+        Validate();
+
+        // Execute the operation
+        Ptr<MgFeatureReader> fr = m_service->GetWfsReader(featureSourceId, featureClass, requiredProperties, srs, filter, maxFeatures, sortCriteria);
+
+        // Write the response
+        EndExecution(fr);
+    }
+    else
+    {
+        MG_LOG_OPERATION_MESSAGE_PARAMETERS_START();
+        MG_LOG_OPERATION_MESSAGE_PARAMETERS_END();
+    }
+
+    if (!m_argsRead)
+    {
+        throw new MgOperationProcessingException(L"MgOpGetWfsReader.Execute",
+            __LINE__, __WFILE__, NULL, L"", NULL);
+    }
+
+    // Successful operation
+    MG_LOG_OPERATION_MESSAGE_ADD_STRING(MgResources::Success.c_str());
+
+    MG_FEATURE_SERVICE_CATCH(L"MgOpGetWfsReader.Execute")
+
+        if (mgException != NULL)
+        {
+            // Failed operation
+            MG_LOG_OPERATION_MESSAGE_ADD_STRING(MgResources::Failure.c_str());
+        }
+
+    // Add access log entry for operation
+    MG_LOG_OPERATION_MESSAGE_ACCESS_ENTRY();
+
+    MG_FEATURE_SERVICE_THROW()
+}

Added: sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.h
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.h	                        (rev 0)
+++ sandbox/jng/ogc/Server/src/Services/Feature/OpGetWfsReader.h	2017-10-02 22:12:57 UTC (rev 9242)
@@ -0,0 +1,33 @@
+//
+//  Copyright (C) 2004-2017 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_OP_GET_WFS_READER_H
+#define MG_OP_GET_WFS_READER_H
+
+#include "FeatureOperation.h"
+
+class MgOpGetWfsReader : public MgFeatureOperation
+{
+public:
+    MgOpGetWfsReader();
+    virtual ~MgOpGetWfsReader();
+
+public:
+    virtual void Execute();
+};
+
+#endif

Modified: sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.cpp
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.cpp	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.cpp	2017-10-02 22:12:57 UTC (rev 9242)
@@ -2043,7 +2043,181 @@
     return msds.GetIdentityProperties(resource, schemaName, classNames);
 }
 
+MgFeatureReader* MgServerFeatureService::GetWfsReader(MgResourceIdentifier* fs,
+                                                      CREFSTRING featureClass,
+                                                      MgStringCollection* propNames,
+                                                      CREFSTRING srs,
+                                                      CREFSTRING wfsFilter,
+                                                      INT32 maxFeatures,
+                                                      CREFSTRING sortCriteria)
+{
+    Ptr<MgFeatureReader> mgfReader;
+    TransformCacheMap transformCache;
 
+    MG_FEATURE_SERVICE_TRY()
+
+        STRING lfeatureName = featureClass;
+    STRING schemaName, className;
+    MgUtil::ParseQualifiedClassName(lfeatureName, schemaName, className);
+
+    bool hashed = (0 == schemaName.find_first_of(
+        MG_SCHEMA_NAME_HASH_PREFIX.c_str(), 0, MG_SCHEMA_NAME_HASH_PREFIX.length()));
+    Ptr<MgStringCollection> classNames = new MgStringCollection();
+
+    if (!className.empty())
+    {
+        classNames->Add(className);
+    }
+
+    // Find the needed class definition.
+    Ptr<MgFeatureSchemaCollection> fsc = DescribeSchema(
+        fs, hashed ? L"" : schemaName, classNames);
+    Ptr<MgFeatureSchema> schema;
+    Ptr<MgClassDefinition> fc;
+    STRING schemaHash;
+
+    FindClassDefinition(fsc, schemaName, className, schemaHash, schema, fc);
+
+    if (hashed && NULL != schema.p)
+    {
+        MgUtil::FormatQualifiedClassName(schema->GetName(), className, lfeatureName);
+    }
+
+    MgCoordinateSystemFactory fact;
+    std::wstring wkt = srs;
+    if (wkt.empty())
+    {
+        //If there is no coordinate system pass in, get the default one in resource header.
+        MgServiceManager* serviceMan = MgServiceManager::GetInstance();
+        assert(NULL != serviceMan);
+        Ptr<MgResourceService> resourceService = dynamic_cast<MgResourceService*>(serviceMan->RequestService(MgServiceType::ResourceService));
+        assert(resourceService != NULL);
+        Ptr<MgByteReader> byteReaderHeader = resourceService->GetResourceHeader(fs);
+        Ptr<MgByteSink> byteSinkHeader = new MgByteSink(byteReaderHeader);
+        std::string resourceHeader;
+        byteSinkHeader->ToStringUtf8(resourceHeader);
+        //parse for default SRS of this WFS, the format is:
+        //<Property xsi:noNamespaceSchemaLocation="Property-1.0.0.xsd">
+        //  <Name>_PrimarySRS</Name>
+        //  <Value>EPSG:4326</Value>
+        //</Property>
+        std::string primary("<Name>_PrimarySRS</Name>");
+        std::size_t primaryPos = resourceHeader.find(primary);
+        if (primaryPos != std::string::npos)
+        {
+            std::string begin("<Value>EPSG:");
+            std::size_t beginPos = resourceHeader.find(begin, primaryPos);
+            if (beginPos != std::string::npos)
+            {
+                std::size_t endPos = resourceHeader.find("</Value>", beginPos);
+                if (endPos != std::string::npos)
+                {
+                    std::string primarySRS = resourceHeader.substr(beginPos + begin.length(), endPos - beginPos - begin.length());
+                    int epsgCode = atoi(primarySRS.c_str());
+                    wkt = fact.ConvertEpsgCodeToWkt(epsgCode);
+                }
+            }
+        }
+    }
+
+    Ptr<MgCoordinateSystem> mapCs = NULL;
+    if (!wkt.empty())
+    {
+        MG_TRY();
+        mapCs = fact.Create(wkt);
+        MG_CATCH_AND_RELEASE();
+    }
+
+    //get a transform from feature space to mapping space
+    TransformCache* item = TransformCache::GetLayerToMapTransform(transformCache, lfeatureName, fs, mapCs, &fact, this, false);
+    Ptr<MgCoordinateSystemTransform> trans = item ? item->GetMgTransform() : NULL;
+
+    assert(fc != NULL);
+    if (fc == NULL)
+        return NULL;
+
+    //execute the spatial query
+    Ptr<MgFeatureQueryOptions> options = new MgFeatureQueryOptions();
+
+    //add the properties we need from FDO
+    if (propNames)
+    {
+        for (int i = 0; i<propNames->GetCount(); i++)
+            options->AddFeatureProperty(propNames->GetItem(i));
+    }
+
+    //convert the WFS filter to an FDO filter
+    //and set it to the FDO feature query
+    if (!wfsFilter.empty())
+    {
+        STRING GEOM_PROP_TAG = L"%MG_GEOM_PROP%"; //NOXLATE
+        STRING fdoFilterString = L""; //NOXLATE
+
+        Ptr<MgPropertyDefinitionCollection> properties = fc->GetProperties();
+        MgOgcFilterUtil u;
+
+        for (int i = 0; i<properties->GetCount(); i++)
+        {
+            Ptr<MgPropertyDefinition> prop = properties->GetItem(i);
+            if (prop->GetPropertyType() == MgFeaturePropertyType::GeometricProperty)
+            {
+                STRING ogcFilter = wfsFilter;
+
+                if (wfsFilter.find(GEOM_PROP_TAG, 0) != STRING::npos)
+                {
+                    ogcFilter = MgUtil::ReplaceString(wfsFilter, GEOM_PROP_TAG.c_str(), prop->GetName().c_str());
+                }
+                if (!fdoFilterString.empty())
+                {
+                    fdoFilterString += L" OR "; //NOXLATE
+                }
+                TransformCache* itemFilter = TransformCache::GetLayerToMapTransform(transformCache, lfeatureName, fs, mapCs, &fact, this, true);
+                Ptr<MgCoordinateSystemTransform> transFilter = itemFilter ? itemFilter->GetMgTransform() : NULL;
+                fdoFilterString += u.Ogc2FdoFilter(ogcFilter, transFilter, prop->GetName(), properties);
+            }
+        }
+
+        options->SetFilter(fdoFilterString);
+    }
+
+    if (!sortCriteria.empty())
+    {
+        Ptr<MgStringCollection> orderByProperties = new MgStringCollection();
+        int orderOption = MgOrderingOption::Ascending;
+
+        STRING sSortCriteria = sortCriteria;
+        STRING::size_type pos = sSortCriteria.find_last_of(L" ");
+        if (pos != STRING::npos)
+        {
+            STRING sSortByProperty = sSortCriteria.substr(0, pos);
+            orderByProperties->Add(sSortByProperty);
+
+            STRING sSortOption = MgUtil::ToUpper(sSortCriteria.substr(pos + 1));
+            if (sSortOption == L"D")
+            {
+                orderOption = MgOrderingOption::Descending;
+            }
+        }
+        else
+        {
+            orderByProperties->Add(sortCriteria);
+        }
+
+        options->SetOrderingFilter(orderByProperties, orderOption);
+    }
+    // TODO: can FeatureName be an extension name rather than a FeatureClass?
+    mgfReader = SelectFeatures(fs, lfeatureName, options);
+
+    MG_FEATURE_SERVICE_CHECK_CONNECTION_CATCH(fs, L"MgServerFeatureService.GetWfsReader")
+
+    TransformCache::Clear(transformCache);
+
+    MG_FEATURE_SERVICE_THROW()
+
+    return mgfReader.Detach();
+}
+
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 /// \brief
 /// Retrieves feature information based on the supplied criteria with specified format.
@@ -2189,7 +2363,6 @@
     //get a transform from feature space to mapping space
     TransformCache* item = TransformCache::GetLayerToMapTransform(transformCache, lfeatureName, fs, mapCs, &fact, this, false);
     Ptr<MgCoordinateSystemTransform> trans = item? item->GetMgTransform() : NULL;
-    FdoPtr<MgGMLCsTransform> transform = new MgGMLCsTransform(trans);
 
     assert(fc != NULL);
     if (fc == NULL)
@@ -2267,6 +2440,8 @@
     // TODO: can FeatureName be an extension name rather than a FeatureClass?
     Ptr<MgFeatureReader> mgfReader = SelectFeatures(fs, lfeatureName, options);
 
+    FdoPtr<MgGMLCsTransform> transform = new MgGMLCsTransform(trans);
+
     //get underlying FDO feature reader
     FdoPtr<FdoIFeatureReader> fdoReader = (NULL == mgfReader.p) ? NULL : ((MgServerFeatureReader*)(mgfReader.p))->GetInternalReader();
 

Modified: sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.h
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.h	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.h	2017-10-02 22:12:57 UTC (rev 9242)
@@ -941,6 +941,14 @@
                                 CREFSTRING filter,
                                 INT32 maxFeatures);
 
+    MgFeatureReader* GetWfsReader(MgResourceIdentifier* featureSourceId,
+                                  CREFSTRING featureClass,
+                                  MgStringCollection* requiredProperties,
+                                  CREFSTRING srs,
+                                  CREFSTRING filter,
+                                  INT32 maxFeatures,
+                                  CREFSTRING sortCriteria);
+
     ////////////////////////////////////////////////////////////////////////////////
     /// <summary>
     /// This method enumerates all the providers and if they are FDO enabled for

Modified: sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj	2017-10-02 22:12:57 UTC (rev 9242)
@@ -415,6 +415,12 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="OpGetWfsReader.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="OpInsertFeatures.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -852,6 +858,7 @@
     <ClInclude Include="OpGetSpatialContexts.h" />
     <ClInclude Include="OpGetSqlRows.h" />
     <ClInclude Include="OpGetWfsFeature.h" />
+    <ClInclude Include="OpGetWfsReader.h" />
     <ClInclude Include="OpInsertFeatures.h" />
     <ClInclude Include="OpInsertFeaturesBatched.h" />
     <ClInclude Include="OpReleaseSavePoint.h" />

Modified: sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureService.vcxproj.filters	2017-10-02 22:12:57 UTC (rev 9242)
@@ -201,6 +201,9 @@
       <Filter>Ops</Filter>
     </ClCompile>
     <ClCompile Include="TransformedGeometryFeatureReader.cpp" />
+    <ClCompile Include="OpGetWfsReader.cpp">
+      <Filter>Ops</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="FeatureOperation.h">
@@ -411,6 +414,9 @@
       <Filter>Ops</Filter>
     </ClInclude>
     <ClInclude Include="TransformedGeometryFeatureReader.h" />
+    <ClInclude Include="OpGetWfsReader.h">
+      <Filter>Ops</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="ServerFeatureService.rc" />

Modified: sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp
===================================================================
--- sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Server/src/Services/Feature/ServerFeatureServiceBuild.cpp	2017-10-02 22:12:57 UTC (rev 9242)
@@ -86,6 +86,7 @@
 #include "OpGetIdentityProperties.cpp"
 #include "OpDescribeWfsFeatureType.cpp"
 #include "OpGetWfsFeature.cpp"
+#include "OpGetWfsReader.cpp"
 #include "FilterUtil.cpp"
 #include "ServerGwsFeatureReader.cpp"
 #include "GwsConnectionPool.cpp"

Modified: sandbox/jng/ogc/Web/src/HttpHandler/HttpWfsGetFeature.cpp
===================================================================
--- sandbox/jng/ogc/Web/src/HttpHandler/HttpWfsGetFeature.cpp	2017-10-02 15:36:26 UTC (rev 9241)
+++ sandbox/jng/ogc/Web/src/HttpHandler/HttpWfsGetFeature.cpp	2017-10-02 22:12:57 UTC (rev 9242)
@@ -291,20 +291,36 @@
                                 sSortCriteria  = sSortCriteria.substr(pos+1);
                             }
 
-                            // 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());
+                            //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, numFeaturesToRetrieve - 1, sSortCriteria);
+                                
+                                //MgByteSource owns this and will clean it up when done
+                                ByteSourceImpl* bsImpl = new MgReaderByteSourceImpl(fr, MgMimeType::Json, true, false, -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!
+                                // 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
                         {



More information about the mapguide-commits mailing list