wiki:MapGuideRfc143

Version 3 (modified by jng, 10 years ago) ( diff )

--

MapGuide RFC 143 - Convenience APIs

This page contains a change request (RFC) for the MapGuide Open Source project. More MapGuide RFCs can be found on the RFCs page.

Status

RFC Template Version(1.0)
Submission Date1 July 2014
Last Modified
AuthorJackie Ng
RFC Status
Implementation Status
Proposed Milestone
Assigned PSC guide(s)(when determined)
Voting History(vote date)
+1
+0
-0
-1
no vote

Overview

This RFC proposes include various convenience APIs for improved developer experience

Motivation

MapGuide RFC 9 introduced some useful convenience APIs for common developer patterns in MapGuide applications. However, there are several cases there are still lots of boilerplate code involved in achieving the desired result:

In addition, there are several internal APIs that have value being exposed for external/public consumption

Proposed Solution

Inserting/Updating/Deleting features

Inserting/Updating/Deleting features using the existing UpdateFeatures() API is a verbose and complex affair, even if the developer only wants to do one of the actions (inserting, updating or deleting and not any combination thereof).

In addition to the complex setup process to use this API, another common problem that can easily be overlooked is the need to actually process the return value of this API:

  • Result has to be checked for any feature properties (that contain MgFeatureReader results for insert operations)
  • Result has to be checked for any string properties (that contain FDO exception messages for any operation when UpdateFeatures() is called without transaction support. Even more confusing, when transactions are used the application has to be ready to catch any exceptions as they won't be serialized out to string properties in the result)

Failure to do this may cause busy resource errors (due to un-closed MgFeatureReader objects) and unspecified application behaviour (due to failing to handle FDO errors).

Here's a piece of sample code to insert a new feature, demonstrating the verbosity of this current API:

MgResourceIdentifier fsId = new MgResourceIdentifier("Library://Test.FeatureSource");
MgWktReaderWriter wktRw = new MgWktReaderWriter();
MgAgfReaderWriter agfRw = new MgAgfReaderWriter();
MgGeometry geom = wktRw.Read("POINT(1 1)");
MgByteReader agf = agfRw.Write(geom);

//Prepare insert values
MgPropertyCollection insertVals = new MgPropertyCollection();
MgStringProperty nameVal = new MgStringProperty("Name", "Hello World");
MgGeometryProperty geomVal = new MgGeometryProeprty("Geometry", agf);
insertVals.Add(nameVal);
insertVals.Add(geomVal);

//Prepare command collection for UpdateFeatures
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgInsertFeatures insertCmd = new MgInsertFeatures("Default:Test", insertVals);
commands.Add(insertCmd);

//Call API and process result (important!)
MgPropertyCollection result = featureService.UpdateFeatures(fsId, commands, false);
for (int i = 0; i < result.GetCount(); i++)
{
    MgProperty prop = result.GetItem(i);
    if (prop.GetPropertyType() == MgPropertyType::Feature) //Insert result
    {
        MgFeatureProperty fp = (MgFeatureProperty)prop;
        //Must close the reader inside to prevent busy resource errors
        MgFeatureReader reader = fp.GetValue();
        reader.Close();
    }
    else if (prop.GetPropertyType() ==- MgPropertyType::String) //FDO error in non-transactional mode
    {
        MgStringProperty sp = (MgStringProperty)prop;
        String fdoError = sp.GetValue();
        //Handle this error
    }
}

As you can see, this type of code is a lot of boilerplate for just inserting features.

For this RFC, we will add convenience APIs to MgFeatureService to allow individual insert/update/delete operations instead of a one-size-fits-all API that we currently have

class MG_PLATFORMBASE_API MgFeatureService : public MgService
{
PUBLISHED_API:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Inserts a new feature into the specified feature class of the specified Feature Source
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, string className, MgPropertyCollection propertyValues);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection propertyValues);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection propertyValues);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the insert operation is performed.
    /// \param propertyValues (MgPropertyCollection)
    /// The collection of property values to insert
    ///
    /// \return
    /// Returns a feature reader object that contains the set of properties 
    /// inserted into the datastore by the insert command.
    ///
    /// \remarks
    /// Remember to close any feature readers returned by this method, even if you don't intend
    /// to do anything with them
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgFeatureReader* InsertFeatures(MgResourceIdentifier* resource,
                                            CREFSTRING className,
                                            MgPropertyCollection* propertyValues) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Inserts a new feature into the specified feature class of the specified Feature Source
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, string className, MgPropertyCollection propertyValues, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection propertyValues, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection propertyValues, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the insert operation is performed.
    /// \param propertyValues (MgPropertyCollection)
    /// The collection of property values to insert
    /// \param trans (MgTransaction)
    /// The transaction to execute this operation under
    ///
    /// \return
    /// Returns a feature reader object that contains the set of properties 
    /// inserted into the datastore by the insert command.
    ///
    /// \remarks
    /// Remember to close any feature readers returned by this method, even if you don't intend
    /// to do anything with them
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgFeatureReader* InsertFeatures(MgResourceIdentifier* resource,
                                            CREFSTRING className,
                                            MgPropertyCollection* propertyValues,
                                            MgTransaction* trans) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Inserts a set of new features into the specified feature class of the specified Feature Source
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, string className, MgBatchPropertyCollection batchPropertyValues);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgBatchPropertyCollection batchPropertyValues);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgBatchPropertyCollection batchPropertyValues);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the insert operation is performed.
    /// \param batchPropertyValues (MgBatchPropertyCollection)
    /// The collection of property values to insert. Each MgPropertyCollection within 
    /// this collection represents property values for a single feature to insert
    ///
    /// \return
    /// Returns a feature reader object that contains the set of properties 
    /// inserted into the datastore by the insert command.
    ///
    /// \remarks
    /// Remember to close any feature readers returned by this method, even if you don't intend
    /// to do anything with them
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgFeatureReader* InsertFeatures(MgResourceIdentifier* resource,
                                            CREFSTRING className,
                                            MgBatchPropertyCollection* batchPropertyValues) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Inserts a set of new features into the specified feature class of the specified Feature Source
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, string className, MgBatchPropertyCollection batchPropertyValues, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgBatchPropertyCollection batchPropertyValues, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgResourceIdentifier resource, String className, MgBatchPropertyCollection batchPropertyValues, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the insert operation is performed.
    /// \param batchPropertyValues (MgBatchPropertyCollection)
    /// The collection of property values to insert. Each MgPropertyCollection within 
    /// this collection represents property values for a single feature to insert
    /// \param trans (MgTransaction)
    /// The transaction to execute this operation under
    ///
    /// \return
    /// Returns a feature reader object that contains the set of properties 
    /// inserted into the datastore by the insert command.
    ///
    /// \remarks
    /// Remember to close any feature readers returned by this method, even if you don't intend
    /// to do anything with them
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgFeatureReader* InsertFeatures(MgResourceIdentifier* resource,
                                            CREFSTRING className,
                                            MgBatchPropertyCollection* batchPropertyValues,
                                            MgTransaction* trans) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Updates all features that match the given filter with the specified property values
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgResourceIdentifier resource, string className, MgPropertyCollection properties, string filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection properties, String filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection properties, String filter);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the update operation is performed.
    /// \param properties (MgBatchPropertyCollection)
    /// The property values to update matching features with
    /// \param filter (String/string)
    /// The FDO filter string that detemines what features will be updated
    ///
    /// \return
    /// Returns the number of features updated by this operation
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual INT32 UpdateMatchingFeatures(MgResourceIdentifier* resource,
                                         CREFSTRING className,
                                         MgPropertyCollection* properties,
                                         CREFSTRING filter) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Updates all features that match the given filter with the specified property values
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgResourceIdentifier resource, string className, MgPropertyCollection properties, string filter, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection properties, String filter, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgResourceIdentifier resource, String className, MgPropertyCollection properties, String filter, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the update operation is performed.
    /// \param properties (MgBatchPropertyCollection)
    /// The property values to update matching features with
    /// \param filter (String/string)
    /// The FDO filter string that detemines what features will be updated
    /// \param trans (MgTransaction)
    /// The transaction to execute this operation under
    ///
    /// \return
    /// Returns the number of features updated by this operation
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual INT32 UpdateMatchingFeatures(MgResourceIdentifier* resource,
                                         CREFSTRING className,
                                         MgPropertyCollection* properties,
                                         CREFSTRING filter,
                                         MgTransaction* trans) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Deletes all features that match the given filter
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int DeleteFeatures(MgResourceIdentifier resource, string className, string filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int DeleteFeatures(MgResourceIdentifier resource, String className, String filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int DeleteFeatures(MgResourceIdentifier resource, String className, String filter);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the delete operation is performed.
    /// \param filter (String/string)
    /// The FDO filter string that detemines what features will be deleted
    ///
    /// \return
    /// Returns the number of features deleted by this operation
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual INT32 DeleteFeatures(MgResourceIdentifier* resource,
                                 CREFSTRING className,
                                 CREFSTRING filter) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Deletes all features that match the given filter
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int DeleteFeatures(MgResourceIdentifier resource, string className, string filter, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int DeleteFeatures(MgResourceIdentifier resource, String className, String filter, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int DeleteFeatures(MgResourceIdentifier resource, String className, String filter, MgTransaction trans);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class on which
    /// the delete operation is performed.
    /// \param filter (String/string)
    /// The FDO filter string that detemines what features will be deleted
    /// \param trans (MgTransaction)
    /// The transaction to execute this operation under
    ///
    /// \return
    /// Returns the number of features deleted by this operation
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual INT32 DeleteFeatures(MgResourceIdentifier* resource,
                                 CREFSTRING className,
                                 CREFSTRING filter,
                                 MgTransaction* trans) = 0;
};

With these convenience APIs, the above example for inserting features now becomes:

MgResourceIdentifier fsId = new MgResourceIdentifier("Library://Test.FeatureSource");
MgWktReaderWriter wktRw = new MgWktReaderWriter();
MgAgfReaderWriter agfRw = new MgAgfReaderWriter();
MgGeometry geom = wktRw.Read("POINT(1 1)");
MgByteReader agf = agfRw.Write(geom);

//Prepare insert values
MgPropertyCollection insertVals = new MgPropertyCollection();
MgStringProperty nameVal = new MgStringProperty("Name", "Hello World");
MgGeometryProperty geomVal = new MgGeometryProeprty("Geometry", agf);
insertVals.Add(nameVal);
insertVals.Add(geomVal);

//Call the API
try
{
    MgFeatureReader result = featureService.InsertFeatures(fsId, "Default:Test", insertVals);
    result.Close();
}
catch (MgFdoException ex) //Transactions or not, an exception will be thrown on any FDO error
{
    //Handle insert error
}

For updating/deleting features you will see similar reductions in code using these convenience APIs

For MgLayerBase, we'll add convenience APIs to shortcut boilerplate that would normally require MgFeatureService and extracting additional information from the layer:

  • Individual insert/update/delete operations instead of a one-size-fits-all API that we currently have
  • The ability to start transactions on the layer's feature source
  • The ability to interrogate spatial contexts for the layer's feature source
  • The ability to call UpdateFeatures() transactionally or non-transactionally instead of having the method internally decide (ie. UpdateFeatures() that can take a MgTransaction parameter)
class MG_PLATFORMBASE_API MgLayerBase : public MgNamedSerializable
{
PUBLISHED_API:
    ////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Starts a transaction on the this layer. The FDO provider indicated by the layer's 
    /// Feature Source must support transactions.
    ///
    /// \remarks
    /// The XML returned by MgFeatureService::GetCapabilities says
    /// whether a provider supports transactions. See \link ProviderCapabilities Provider Capabilities \endlink.
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgTransaction BeginTransaction();
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgTransaction BeginTransaction();
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgTransaction BeginTransaction();
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \return
    /// Returns an MgTransaction instance (or NULL).
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgTransaction* BeginTransaction();

    //////////////////////////////////////////////////////////////////
    /// \brief
    /// Executes the MgDeleteFeatures, MgInsertFeatures,
    /// MgUpdateFeatures, MgLockFeatures or MgUnlockFeatures commands
    /// contained in the given MgFeatureCommandCollection object.
    ///
    /// \remarks
    /// The XML returned by MgFeatureService::GetCapabilities says
    /// whether a provider supports SQL commands. See \link ProviderCapabilities Provider Capabilities \endlink.
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgPropertyCollection UpdateFeatures(MgFeatureCommandCollection commands);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgPropertyCollection UpdateFeatures(MgFeatureCommandCollection commands);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgPropertyCollection UpdateFeatures(MgFeatureCommandCollection commands);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param commands (MgFeatureCommandCollection)
    /// A collection of feature commands to be
    /// executed.
    /// \param transaction (MgTransaction)
    /// An optional transaction to execute this operation under
    ///
    /// \return
    /// Returns an MgPropertyCollection object. Each property in the
    /// collection corresponds to a command in the
    /// MgFeatureCommandCollection argument. The property name is the
    /// index of the command in the feature command collection.
    /// <ul>
    ///   <li>If the command is of type MgDeleteFeatures, the property
    ///     type is an MgPropertyType::Int32, and its value is the number
    ///     of features deleted.</li>
    ///   <li>If the command is of type MgInsertFeatures, the property
    ///     type is an MgPropertyType::Feature, and its value is a
    ///     MgFeatureReader object. The feature reader object contains
    ///     the set of properties inserted into the datastore by the
    ///     insert command.</li>
    ///   <li>If the command is of type MgUpdateFeatures, the property
    ///     type is MgPropertyType::Int32, and its value is the number of
    ///     features updated.</li>
    ///   <li>If the command is of type MgLockFeatures, the property
    ///     type is MgPropertyType::Feature, and its value is the number
    ///     of features locked.</li>
    ///   <li>If the command is of type MgUnLockFeatures, the property
    ///     type is MgPropertyType::Int32, and its value is the number of
    ///     features unlocked.</li>
    /// </ul>
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgPropertyCollection* UpdateFeatures(MgFeatureCommandCollection* commands, MgTransaction* transaction);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Inserts a new feature into the specified feature class of the specified Feature Source
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgPropertyCollection properties);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgPropertyCollection properties);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgPropertyCollection properties);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param properties (MgPropertyCollection)
    /// The collection of property values to insert
    /// \param transaction (MgTransaction)
    /// An optional transaction to execute this operation under
    ///
    /// \return
    /// Returns a feature reader object that contains the set of properties 
    /// inserted into the datastore by the insert command.
    ///
    /// \remarks
    /// Transactions will be used internally if the provider supports them.
    /// Remember to close any feature readers returned by this method, even if you don't intend
    /// to do anything with them
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgFeatureReader* InsertFeatures(MgPropertyCollection* properties, MgTransaction* transaction);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Inserts a set of new features into the specified feature class of the specified Feature Source
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgBatchPropertyCollection properties);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgBatchPropertyCollection properties);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader InsertFeatures(MgBatchPropertyCollection properties);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param properties (MgBatchPropertyCollection)
    /// The collection of property values to insert. Each MgPropertyCollection within 
    /// this collection represents property values for a single feature to insert
    /// \param transaction (MgTransaction)
    /// An optional transaction to execute this operation under
    ///
    /// \return
    /// Returns a feature reader object that contains the set of properties 
    /// inserted into the datastore by the insert command.
    ///
    /// \remarks
    /// Transactions will be used internally if the provider supports them.
    /// Remember to close any feature readers returned by this method, even if you don't intend
    /// to do anything with them
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgFeatureReader* InsertFeatures(MgBatchPropertyCollection* properties, MgTransaction* transaction);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Updates all features that match the given filter with the specified property values
    ///
    /// \remarks
    /// Transactions will be used internally if the provider supports them
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgPropertyCollection properties, string filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgPropertyCollection properties, String filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int UpdateMatchingFeatures(MgPropertyCollection properties, String filter);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param properties (MgBatchPropertyCollection)
    /// The property values to update matching features with
    /// \param filter (String/string)
    /// The FDO filter string that detemines what features will be updated
    /// \param transaction (MgTransaction)
    /// An optional transaction to execute this operation under
    ///
    /// \return
    /// Returns the number of features updated by this operation
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual INT32 UpdateMatchingFeatures(MgPropertyCollection* properties, CREFSTRING filter, MgTransaction* transaction);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Deletes all features that match the given filter
    ///
    /// \remarks
    /// Transactions will be used internally if the provider supports them
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int DeleteFeatures(string filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int DeleteFeatures(String filter);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int DeleteFeatures(String filter);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param filter (String/string)
    /// The FDO filter string that detemines what features will be deleted
    /// \param transaction (MgTransaction)
    /// An optional transaction to execute this operation under
    ///
    /// \return
    /// Returns the number of features deleted by this operation
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual INT32 DeleteFeatures(CREFSTRING filter, MgTransaction* transaction);

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Gets all of the spatial contexts available in the layer or just the active one
    ///
    /// \remarks
    /// The \link FdoSpatialContextList_schema FdoSpatialContextList \endlink XML schema contains
    /// a specification of the content of the spatial context
    /// information returned in the MgSpatialContextReader object.
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgSpatialContextReader GetSpatialContexts(bool bActiveOnly);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgSpatialContextReader GetSpatialContexts(boolean bActiveOnly);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgSpatialContextReader GetSpatialContexts(bool bActiveOnly);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param bActiveOnly (boolean/bool)
    /// This flag is obsolete and no longer used.
    ///
    /// \return
    /// Returns an MgSpatialContextReader object.
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgInvalidOperationException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgSpatialContextReader* GetSpatialContexts(bool bActiveOnly);
};

Rendering layer style icons

The GenerateLegendImage() API of MgRenderingService currently requires you to manually process the XML of the Layer Definition of interest in order to determine the correct parameters for geomType and themeCategory. This results in lots of boilerplate code for unnecessary XML processing. Additional boilerplate is required when dealing with composite styles as you need to calculate the correct offset values for themeCategory in order to generate the correct icon.

For this RFC, we'll introduce several APIs to MgLayer to simplify this:

class MG_MAPGUIDE_API MgLayer : public MgLayerBase
{
PUBLISHED_API:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Gets the list of geometry type styles for this layer at the map's current scale. Returns NULL if there are no applicable geometry types
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgIntCollection GetGeometryTypeStyles();
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgIntCollection GetGeometryTypeStyles();
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgIntCollection GetGeometryTypeStyles();
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \remarks
    /// The map's current scale is used to determine what scale range in the layer definition to search for.
    /// For a scale range with multiple composite styles, multiple instances of (4 = composite) will be in the resulting collection
    ///
    /// \return
    /// The list of geometry type styles for this layer at the map's current scale. Returns NULL if there are no applicable geometry types
    ///
    /// \since 3.0
    MgIntCollection* GetGeometryTypeStyles();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Gets the number of theme categories for this layer at the map's current scale for the given geometry type style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int GetThemeCategoryCount(int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int GetThemeCategoryCount(int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int GetThemeCategoryCount(int geomType);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param geomType (int)
    /// The geometry type
    ///
    /// \remarks
    /// The map's current scale is used to determine what scale range in the layer definition to search for.
    /// When geomType = 4, it will only count the number of the theme categories for the first composite style it finds. For a scale range
    /// with multiple composite type styles, you should use GetCompositeThemeCategoryCount() instead
    ///
    /// \return
    /// The number of theme categories for this layer at the map's current scale for the given geometry type style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// \since 3.0
    INT32 GetThemeCategoryCount(INT32 geomType);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Gets the number of composite theme categories for this layer at the map's current scale for the given composite style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param compositeOffset (int)
    /// The zero-based index denoting the particular composite style to count from. 0 = 1st composite style, 1 = 2nd composite style
    ///
    /// \return
    /// The number of theme categories for this layer at the map's current scale for the given composite style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// \since 3.0
    INT32 GetCompositeThemeCategoryCount(INT32 compositeOffset);

    ////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Returns the legend image for the specified geometry type and theme category
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgByteReader GenerateLegendImage(int width, int height, string format, int geomType, int themeCategory);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgByteReader GenerateLegendImage(int width, int height, String format, int geomType, int themeCategory);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgByteReader GenerateLegendImage(int width, int height, string format, int geomType, int themeCategory);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// Input
    /// MgResourceIdentifier object identifying the layer definition resource.
    /// \param scale (double)
    /// Input
    /// The scale at which the symbolization is requested.
    /// \param width (int)
    /// Input
    /// The requested image width in pixels.
    /// \param height (int)
    /// Input
    /// The requested image height in pixels.
    /// \param format (String/string)
    /// Input
    /// Image format, from MgImageFormats. Example: PNG, JPG, PNG8, etc 
    /// \param geomType (int)
    /// Input
    /// The type of symbolization required: 1=Point, 2=Line, 3=Area, 4=Composite
    /// \param themeCategory (int)
    /// Input
    /// The value indicating which theme category swatch to return.
    /// Used when there is a theme defined at this scale. An exception will be
    /// thrown if a requested them category doesn't exist.
    ///
    /// \remarks
    /// The map's current scale is used to determine what scale range in the layer definition to search for
    ///
    /// \return
    /// Returns a stream representing the legend image.
    /// The default returned image format will be PNG8 unless a different supported
    /// format is requested. An exception will be thrown if an unsupported image
    /// format is requested.
    ///
    /// \exception MgArgumentOutOfRangeException
    /// \exception MgInvalidResourceTypeException
    /// \exception MgNullArgumentException
    /// \exception MgInvalidImageFormatException
    ///
    /// \since 3.0
    MgByteReader* GenerateLegendImage(INT32 width, INT32 height, CREFSTRING format, INT32 geomType, INT32 themeCategory);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Gets the list of geometry type styles for this layer at the map's current scale. Returns NULL if there are no applicable geometry types
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgIntCollection GetGeometryTypeStyles(double scale);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgIntCollection GetGeometryTypeStyles(double scale);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgIntCollection GetGeometryTypeStyles(double scale);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param scale (double)
    /// The scale at which to retrive the list of applicable geometry types
    ///
    /// \remarks
    /// For a scale range with multiple composite styles, multiple instances of (4 = composite) will be in the resulting collection
    ///
    /// \return
    /// The list of geometry type styles for this layer at the map's current scale. Returns NULL if there are no applicable geometry types
    ///
    /// \since 3.0
    MgIntCollection* GetGeometryTypeStyles(double scale);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Gets the number of theme categories for this layer at the map's current scale for the given geometry type style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param scale (double)
    /// The scale at which to count the number of applicable theme categories
    /// \param geomType (int)
    /// The geometry type
    ///
    /// \remarks
    /// When geomType = 4, it will only count the number of the theme categories for the first composite style it finds. For a scale range
    /// with multiple composite type styles, you should use GetCompositeThemeCategoryCount() instead
    ///
    /// \return
    /// The number of theme categories for this layer at the map's current scale for the given geometry type style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// \since 3.0
    INT32 GetThemeCategoryCount(double scale, INT32 geomType);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Gets the number of composite theme categories for this layer at the map's current scale for the given composite style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual int GetThemeCategoryCount(double scale, int geomType);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param scale (double)
    /// The scale at which to count the number of applicable theme categories
    /// \param compositeOffset (int)
    /// The zero-based index denoting the particular composite style to count from. 0 = 1st composite style, 1 = 2nd composite style
    ///
    /// \return
    /// The number of theme categories for this layer at the map's current scale for the given composite style. A count greater than 1 indicates a themed layer. Returns -1 if there are no applicable styles at the current scale
    ///
    /// \since 3.0
    INT32 GetCompositeThemeCategoryCount(double scale, INT32 compositeOffset);

    ////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Returns the legend image for the specified geometry type and theme category
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgByteReader GenerateLegendImage(double scale, int width, int height, string format, int geomType, int themeCategory);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgByteReader GenerateLegendImage(double scale, int width, int height, String format, int geomType, int themeCategory);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgByteReader GenerateLegendImage(double scale, int width, int height, string format, int geomType, int themeCategory);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// Input
    /// MgResourceIdentifier object identifying the layer definition resource.
    /// \param scale (double)
    /// Input
    /// The scale at which the symbolization is requested.
    /// \param width (int)
    /// Input
    /// The requested image width in pixels.
    /// \param height (int)
    /// Input
    /// The requested image height in pixels.
    /// \param format (String/string)
    /// Input
    /// Image format, from MgImageFormats. Example: PNG, JPG, PNG8, etc 
    /// \param geomType (int)
    /// Input
    /// The type of symbolization required: 1=Point, 2=Line, 3=Area, 4=Composite
    /// \param themeCategory (int)
    /// Input
    /// The value indicating which theme category swatch to return.
    /// Used when there is a theme defined at this scale. An exception will be
    /// thrown if a requested them category doesn't exist.
    ///
    /// \return
    /// Returns a stream representing the legend image.
    /// The default returned image format will be PNG8 unless a different supported
    /// format is requested. An exception will be thrown if an unsupported image
    /// format is requested.
    ///
    /// \exception MgArgumentOutOfRangeException
    /// \exception MgInvalidResourceTypeException
    /// \exception MgNullArgumentException
    /// \exception MgInvalidImageFormatException
    ///
    /// \since 3.0
    MgByteReader* GenerateLegendImage(double scale, INT32 width, INT32 height, CREFSTRING format, INT32 geomType, INT32 themeCategory);
};

SelectFeatures with transformation

This RFC will also implement this previously un-implemented API:

class MG_PLATFORMBASE_API MgFeatureService : public MgService
{
PUBLISHED_API:
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// \brief
    /// Selects features from a feature source according to the
    /// criteria set in the MgFeatureQueryOptions argument The
    /// criteria are applied to all of the features in the feature
    /// source. Use the coordinateSystem argument to set the target
    /// coordinate system if you want to transform.
    /// If you want to apply the criteria to a subset of the
    /// features, use MgFeatureService::SelectAggregate.
    /// See \link FiltersAndExpressions Filters and expressions \endlink.
    ///
    /// \remarks
    /// Be sure to Close() the MgFeatureReader object returned by this method.
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader SelectFeatures(MgResourceIdentifier resource, string className, MgFeatureQueryOptions options, string coordinateSystem);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader SelectFeatures(MgResourceIdentifier resource, String className, MgFeatureQueryOptions options, String coordinateSystem);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader SelectFeatures(MgResourceIdentifier resource, string className, MgFeatureQueryOptions options, string coordinateSystem);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param resource (MgResourceIdentifier)
    /// A resource identifier for the feature
    /// source.
    /// \param className (String/string)
    /// The name of the feature class from which
    /// the properties of interest are selected.
    /// \param options (MgFeatureQueryOptions)
    /// MgFeatureQueryOptions instance
    /// containing all required filters for this
    /// select operation.
    /// \param coordinateSystem (String/string)
    /// The name of the coordinate system to transform to.
    ///
    /// \return
    /// Returns an MgFeatureReader containing the set of selected
    /// features.
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgFdoException
    ///
    virtual MgFeatureReader* SelectFeatures(MgResourceIdentifier* resource,
                                            CREFSTRING className,
                                            MgFeatureQueryOptions* options,
                                            CREFSTRING coordinateSystem) = 0;
};

And we'll include a convenience form of this API in MgLayerBase

class MG_PLATFORMBASE_API MgLayerBase : public MgNamedSerializable
{
PUBLISHED_API:
    //////////////////////////////////////////////////////////////////
    /// \brief
    /// Selects features from a feature source according to the
    /// criteria set in the MgFeatureQueryOptions argument The
    /// criteria are applied to all of the features in the feature
    /// source. If you want to apply the criteria to a subset of the
    /// features, use the \link MgFeatureService::SelectAggregate MgFeatureService::SelectAggregate Method \endlink.
    /// See \link FiltersAndExpressions Filters and expressions \endlink.
    ///
    /// <!-- Syntax in .Net, Java, and PHP -->
    /// \htmlinclude DotNetSyntaxTop.html
    /// virtual MgFeatureReader SelectFeatures(MgFeatureQueryOptions options);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude JavaSyntaxTop.html
    /// virtual MgFeatureReader SelectFeatures(MgFeatureQueryOptions options);
    /// \htmlinclude SyntaxBottom.html
    /// \htmlinclude PHPSyntaxTop.html
    /// virtual MgFeatureReader SelectFeatures(MgFeatureQueryOptions options);
    /// \htmlinclude SyntaxBottom.html
    ///
    /// \param options (MgFeatureQueryOptions)
    /// MgFeatureQueryOptions instance
    /// containing all required filters for this
    /// select operation.
    /// \param coordinateSystem (String/string)
    /// The coordinate system to transform features to
    ///
    /// \return
    /// Returns an MgFeatureReader containing the set of selected
    /// features.
    ///
    /// \exception MgFeatureServiceException
    /// \exception MgInvalidArgumentException
    /// \exception MgFdoException
    ///
    /// \since 3.0
    virtual MgFeatureReader* SelectFeatures(MgFeatureQueryOptions* options, CREFSTRING coordinateSystem);
};

API promotions

This RFC will also promote the following APIs previously marked as INTERNAL_API:

MgResourceService::GetResourceModifiedDate

This API has been promoted to EXTERNAL_API. This method has utility in implementing HTTP caching for representations of a resource. In order to do this, you need to know the date/time a resource was modified to set and/or compare against so that proper cache expiry logic can be applied. This method already provides this information.

MgResourceService::EnumerateResourceDocuments

This API has been promoted to EXTERNAL_API. This method is currently used by the WFS and WMS services to query resource headers in a repository-wide manner for WFS/WMS configuration parameters. This method has utility for applications that want to embed their own custom application-specific metadata into resource headers and be able to query for such information in a repository-wide manner. The associated MgResourceHeaderProperties constant class has also been promoted to EXTERNAL_API.

Implications

As with MapGuide RFC 9, the new convenience APIs in MgLayer/MgLayerBase will only work if the parent MgMap was initialized with an MgSiteConnection instance. If MapGuide RFC 139 is adopted, the deprecated MgMap() constructor will be removed, eliminating the possibility of these APIs not working under certain conditions.

It's up to Autodesk whether they choose to implement these convenience APIs for their AutoCAD Map implementation of MgLayerBase and MgFeatureService. These methods will throw MgNotImplementedException if not overridden.

Test Plan

Add unit tests on server and web tier levels to exercise these new convenience APIs

Funding / Resources

Community

Note: See TracWiki for help on using the wiki.