= !MapGuide RFC 163 - GeoJSON support for WFS/WMS = This page contains a change request (RFC) for the !MapGuide Open Source project. More !MapGuide RFCs can be found on the [wiki:MapGuideRfcs RFCs] page. == Status == ||RFC Template Version||(1.0)|| ||Submission Date||14 Nov 2017|| ||Last Modified||10 Dec 2017|| ||Author||Jackie Ng|| ||RFC Status||adopted|| ||Implementation Status||implemented|| ||Proposed Milestone||4.0|| ||Assigned PSC guide(s)||(when determined)|| ||'''Voting History'''||(vote date)|| ||+1||Jackie,Gordon,Haris|| ||+0|||| ||-0|||| ||-1|||| ||no vote|| || == Overview == This RFC proposes to add support for GeoJSON as an output format for: * WFS `GetFeatures` * WMS `GetFeatureInfo` Along with required infrastructure changes needed to support the above 2 cases. == Motivation == WFS/WMS support in MapGuide is very bare-bones. The usability of WFS/WMS services can be greatly enhanced with support for GeoJSON as an output format, especially for WMS `GetFeatureInfo`, where a GeoJSON response can function as a client-side feature selection overlay with geometry data present. == Proposed Solution == This RFC is completed and ready to merge from the [https://trac.osgeo.org/mapguide/browser/sandbox/jng/ogc ogc sandbox]. Upon adoption of the RFC, the branch will be merged into trunk. The solution in detail is described below. `application/json` will be advertised as a supported output format for WFS `GetFeatures` and WMS `GetFeatureInfo` === GeoJSON Support for WFS GetFeatures === The current `GetWfsFeature` method of `MgFeatureService` is the method that WFS `GetFeatures` ultimately calls to return data for feature data in a WFS `GetFeatures` response. However this method is not re-usable for implementing alternate output formats for WFS `GetFeatures` because the API itself is GML-centric: * Various input parameters have GML-isms * The return value is an `MgByteReader` with GML content To support GeoJSON output for WFS `GetFeatures`, we need a method that gives us the underlying feature reader primitive, which we can then convert to our desired output format of choice in the Web Tier. We'll add a new `GetWfsReader` method for this very purpose: {{{ class MG_PLATFORMBASE_API MgFeatureService : public MgService { PUBLISHED_API: //////////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief /// Retrieves feature information based on the supplied criteria. /// /// \note1 /// /// /// \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 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, CREFSTRING sortCriteria) = 0; }; }}} From the Web Tier, the `MgHttpWfsGetFeature` implementation will call this method instead of `MgFeatureService::GetWfsFeature` if the `INFO_FORMAT` parameter passed in is `application/json`. The returned reader is then housed in our existing GeoJSON output adapter (introduced in MapGuideRfc158) to convert the feature reader data to GeoJSON. === GeoJSON Support for WMS GetFeatureInfo === For WMS `GetFeatureInfo`, there are cases where being able to return geometry data is useful. However there is only one format where the geometry data would be consumable: GeoJSON. To support GeoJSON output, we must first support the ability to output geometry data in a WMS `GetFeatureInfo`. The `QueryFeatureProperties` method of `MgRenderingService` is the method that provides this data. We will add a new internal overload that allows including geometry data as `MgGeometryProperty` instances for each `MgPropertyCollection` of the returned `MgBatchPropertyCollection` {{{ class MG_MAPGUIDE_API MgRenderingService : public MgService { INTERNAL_API: ///////////////////////////////////////////////////////////////// /// \brief /// The QueryFeatureProperties operation identifies those features that /// meet the specified spatial selection criteria. This operation /// is used to implement WMS feature info and returns property values /// for all features which match the spatial query /// /// \param map /// Input /// map object containing current state of map. /// \param layerNames /// Input /// Active layer names for which to query features /// \param filterGeometry /// Input /// geometry object specifying the selection area /// \param selectionVariant /// Input /// selection criterion - integer value corresponding to one of /// the MgFeatureSpatialOperations values /// \param featureFilter /// Input /// an XML selection string containing the required feature IDs /// \param maxFeatures /// Input /// the maximum number of features to return /// \param layerAttributeFilter /// Input /// bitmask values - 1=Visible, 2=Selectable, 4=HasTooltips /// \param bIncludeFeatureBBOX /// Input /// Indicates whether a bounding box should be included. If true, bounding box is recorded as a special property named _MgFeatureBoundingBox /// \param bIncludeGeometry /// Input /// Indicates whether a bounding box should be included. If true, bounding box is recorded as a special property named _MgFeatureBoundingBox /// /// \return /// An MgSelection instance identifying the features that meet the /// selection criteria. Returns null if no features are identified. /// virtual MgBatchPropertyCollection* QueryFeatureProperties( MgMap* map, MgStringCollection* layerNames, MgGeometry* filterGeometry, INT32 selectionVariant, CREFSTRING featureFilter, INT32 maxFeatures, INT32 layerAttributeFilter, bool bIncludeFeatureBBOX, bool bIncludeGeometry) = 0; }; }}} The geometry data will be stored as a `MgGeometryProperty` instance within each child `MgPropertyCollection` of the `MgBatchPropertyCollection` as a special property named `_MgGeometry` The `MgHttpWmsGetFeatureInfo` class in the `HttpHandler` project will be modified to call this new overload. === New OGC XML template engine directives and definitions === Now that we have the infrastructure to return geometry data, we'll add new directives and definitions in the OGC XML templating engine that can detect and output this geometry data. * `EnumFeatureGeometries` - Enumerates all geometry properties of the current feature. Will only enumerate once at most for each feature if geometry data is present. * `FeatureInfo.IsLast` - Defines if the current feature is the last one in the iteration. This is needed for GeoJSON so we know when it is needed to insert the delimiting `,` between GeoJSON features. * `FeatureProperty.IsLast` - Defines if the current property is the last one in the iteration. This is needed for GeoJSON so we know when it is needed to insert the delimiting `,` between GeoJSON properties. * `FeatureGeometry.Value` - Defines the geometry value. The source of the definition (A new `MgWmsFeatureGeometry` class) will output a GeoJSON geometry fragment if the `INFO_FORMAT` is `application/json`. Otherwise it outputs the geometry as WKT text. To illustrate how these directives and definitions are used. These are the additions to the WMS XML template (all versions) to support GeoJSON output for WMS `GetFeatureInfo` {{{ { "type": "FeatureCollection", "features": [] } { "type": "Feature", "properties": {} }, "&FeatureProperty.Name;": "&FeatureProperty.Value;", ,"geometry": &FeatureGeometry.Value; }}} To ensure formats other than GeoJSON can output geometry data, `GetFeatureInfo` response templates for other templates will be modified as such: * `text/plain` - Geometry data is output as WKT on another line item * `text/html` - Geometry data is output as WKT in its own `Geometry` table cell value * `text/xml` - Geometry data is output as WKT in its own `` element named `Geometry` === Configurable Geometry output === Although we now enable geometry output for WMS `GetFeatureInfo` with the RFC, it may not be desirable to have this enabled unconditionally. To control whether to emit geometry data in a WMS `GetFeatureInfo` response, we define a new `_EnableGeometry` simple metadata property in the resource header XML of a Layer Definition. If set to `1` in a layer's resource header XML, the WMS `GetFeatureInfo` requests against this layer will include geometry data in its response, otherwise geometry data is omitted. The `_EnableGeometry` property has no effect if the `_Queryable` property is not set to `1` (ie. The layer is not published for WMS consumption). As this is a new metadata property, this has the effect of geometry output for WMS `GetFeatureInfo` being opt-in. By default, no geometry data is output unless `_EnableGeometry` is defined in the layer resource header with a value of `1` (in addition to `_Queryable` being set to `1`) == Implications == These are new output formats advertised in WFS and WMS capabilities. Existing output formats are not affected. Geometry output is opt-in, so existing WMS `GetFeatureInfo` response will look as they are until the user *chooses* to enable geometry output. == Test Plan == Verify GeoJSON output is present for `GetFeature` for all supported WFS versions. Verify GeoJSON output is present for `GetFeatureInfo` for all supported WMS versions. Verify geometry data is output only for WMS published layer where `_EnableGeometry` is set to `1` == Funding / Resources == Community