= FDO RFC 57 - Implement the !GetFeatureInfo command for WMS provider = This page contains a request for comments document (RFC) for the FDO Open Source project. More FDO RFCs can be found on the [wiki:FDORfcs RFCs] page. == Status == ||RFC Template Version||1.1|| ||Submission Date||March 23, 2011|| ||Last Modified||Greg Boone, April 20, 2011|| ||Author||Cheney Sun|| ||RFC Status||Adopted|| ||Implementation Status||In Progress|| ||Proposed Milestone||3.7.0.0 || ||Assigned PSC guide(s)||Orest Halustchak, Greg Boone|| ||'''Voting History'''||April 20, 2011|| ||+1||Greg Boone, Orest Halustchak, Jackie Ng, Haris Kurtagic|| ||+0|||| ||-0|||| ||-1|||| == Overview == The RFC is to implement the !GetFeatureInfo command for WMS provider. == Motivation == !GetFeatureInfo is an optional operation of WMS server. It is only supported for those Layers for which the attribute queryable="1" (true) has been defined or inherited. The !GetFeatureInfo operation is designed to provide clients of a WMS with more information about features in the pictures of maps that were returned by Get Map requests. The canonical use case for !GetFeatureInfo is that a user sees the response of a Map request and chooses a point on that map for which to obtain more information. Here is the table of the parameters of a !GetFeatureInfo Request based on the 1.3.0 version: ||'''Request parameter''' ||'''Mandatory/optional''' ||'''Description'''|| ||VERSION=1.3.0 ||M ||Request version|| ||REQUEST=!GetFeatureInfo ||M ||Request name|| ||map request part|| M ||Partial copy of the Map request parameters that generated the map for which information is desired.|| ||QUERY_LAYERS=layer_list || M ||Comma-separated list of one or more layers to be queried|| ||INFO_FORMAT=output_format ||M ||Return format of feature information (MIME type).|| ||FEATURE_COUNT=number ||O ||Number of features about which to return information (default=1).|| ||I=pixel_column ||M ||i coordinate in pixels of feature in Map CS|| ||J=pixel_row ||M ||j coordinate in pixels of feature in Map CS|| ||EXCEPTIONS=exception_format ||O ||The format in which exceptions are to be reported by the WMS (default= XML)|| Basically, the goal is that users must be able to not only connect to WMS services and add features to maps from those services; they should also be able to click on map features to get more information.[[BR]] As to the 1.1.0 version, the standard employs the x = pixel_column and y = pixel_row to replace I = pixel_column and J = pixel_row.[[BR]] More details regarding the WMS !GetFeatureInfo request can be found in the WMS standards http://www.opengeospatial.org/standards/wms. == Proposed Solution == The shortest WMS !GetFeatureInfo implementation path would involve implementing !GetFeatureInfo request within the current FDO WMS provider. The design solution would provide a custom command, !FdoWmsCommandType_GetFeatureInfo, for users to retrieve the feature information.[[BR]] '''• Input of the !GetFeatureInfo request'''[[BR]] From the required parameters table above, we can see !GetFeatureInfo requires a copy of parameters required by !GetMap request and plus its own parameter. So the custom command would also require the same input for Select command and extra parameters ( QUERY_LAYERS, INFO_FORMAT, FEATURE_COUNT, I and J ). To simplify the implementation and usage of the command, the relevant !GetMap request will be cached in the connection class and this command would pick up it as the part of the !GetMap parameters. This presumes that the command shoud be invoked after the relevant select command. If no cached !GetMap request available, execution of the command would throw an exception.[[BR]] Although, it's possible that the command would employ a different bounding box and resolution with those used in the cached !GetMap request, The command also exposes another entries to let user specify the expected bounding box, height and width.[[BR]] '''• Output of the !GetFeatureInfo request'''[[BR]] The format of responded message from the !GetFeatureInfo request is defined by the WMS server. According to the WMS standard, the retrieved feature information is a MIME type. For example, it could be the following format.[[BR]] text/plain [[BR]] text/html [[BR]] text/xml [[BR]] image/png [[BR]] video/mpeg [[BR]] .......[[BR]] Currently, the WMS specification doesn’t define a standard format for each of the subtype. So, the solution is to return a !FdoIoStream* type pointer by this command. Users take the responsibilities to parse the returned data stream. If user set the FEATURE_COUNT greater than 1, the information for multiple candidate features will be returned. [[BR]] For example, if the "text/plain" is specified as the output format and set the FEATURE_COUNT as 2, the string stream below would be obtained.[[BR]] Layer 'IBA'[[BR]] Feature 6:[[BR]] SITEID = 'NU009'[[BR]] NAME = 'Queen Maud Lowlands' [[BR]] LINK = 'http://www.bsc-eoc.org/iba/site.jsp?siteID=NU009' [[BR]] Feature 66:[[BR]] SITEID = 'NU089'[[BR]] NAME = 'Middle Back River'[[BR]] LINK = 'http://www.bsc-eoc.org/iba/site.jsp?siteID=NU089' [[BR]] However, the following XML formatted message stream will return if issue the command to a !MapGuide server and specifying the output format as "text/xml". [[BR]] {{{ }}} Similarly, if specifying the output format as "image/png", an image stream would be returned; specifying it as "video/mpeg" would get a video stream. Users have the freedom to choose a way of comsuming those data.[[BR]] '''• !GetFeatureInfo custom command interface'''[[BR]] {{{ /// \brief /// The FdoWmsIGetFeatureInfo interface defines the /// FdoWmsIGetFeatureInfo command, which retrieves more information /// about features in the maps. The Execute operation returns a /// FdoIoStream* type pointer pointing to the feature information /// data stream. class FdoWmsIGetFeatureInfo : public FdoICommand { friend class FdoIConnection; public: /// \brief /// Execute the FdoWmsIGetFeatureInfo command and returns a /// FdoIoStream* pointer. It could be a text, image, video and any /// binary data stream, ect. The result depends on the specified output /// format. /// /// \return /// Return a FdoIoStream representing the feature information. /// FDO_API virtual FdoIoStream* Execute() = 0; /// \brief /// Set the feature class name for which feature information /// will be retrieved. This function is mandatory; if not specified or the feature class /// name can’t be found in the schema, execution of the command will throw an /// exception. /// /// \param value /// Input the feature class name. /// /// \return /// Returns nothing /// FDO_API virtual void SetFeatureClassName(FdoIdentifier* values) = 0; /// \brief /// Get the name of the feature class for which feature information /// will be retrieved. /// /// \return /// Input the query feature class name. /// FDO_API virtural FdoIdentifier * GetFeatureClassName() = 0; /// \brief /// Set the format of output feature information. The supported values are MIME type /// strings and should be listed in one or more elements inside the /// element of the Capabilities XML. /// An exception would be thrown if the value isn't in the supported list when /// executing the command. The default value is the first one specified in the /// Capabilities document. /// /// \para value /// Input the output feature information format. /// FDO_API virtual void SetOutputFormat(FdoString* value) = 0; /// \brief /// Get the specified format for the returning feature information. /// /// \return /// Return the output feature information format. /// FDO_API virtual FdoString* GetOutputFormat() = 0; /// \brief /// Set the bounding box that would be used to replace the one in /// cached GetMap request. If it's not set or the bounding box is /// empty, directly use the one in cached GetMap request. /// /// \param value /// Input the interest bounding box. /// FDO_API virtual void SetBoundingBox(FdoIEnvelope* value) = 0; /// \brief /// Get the bounding box that's specified to replace the one in /// cached GetMap request. /// /// \return /// Return the specified bounding box for GetMap request. /// FDO_API virtual FdoIEnvelope* GetBoundingBox() = 0; /// \brief /// Set the height to replace the one used in the cached GetMap /// request. Its default value is zero, if the specified value is /// greater than zero, the replacement would happen. Otherwise, /// directly use the one in the cached GetMap request. /// /// \param value /// Input the specified image height. /// FDO_API virtual void SetHeight(FdoSize value) = 0; /// \brief /// Get the height that's specified to replace the one in cached /// GetMap request. Its default value is zero. /// /// \return /// Return specified image height. /// FDO_API virtual FdoSize GetHeight() = 0; /// \brief /// Set the width to replace the one used in the cached GetMap /// request. Its default value is zero, if the specified value is /// greater than zero, the replacement would happen. Otherwise, /// directly use the one in the cached GetMap request. /// /// \param value /// Input the specified image width. /// FDO_API virtual void SetWidth(FdoSize value) = 0; /// \brief /// Get the width that's specified to replace the one in cached /// GetMap request. Its default value is zero. /// /// \return /// Return specified image width. /// FDO_API virtual FdoSize GetWidth() = 0; /// \brief /// Set the position (X, Y) which indicates a point of interest on the map that was /// produced by the embedded !GetMap request.The point (X, Y) is a /// point in the (i, j) space defined by the Map CS. /// This function is mandatory; if not specified, /// execution of the command will throw an exception. /// /// \param pos /// Input the point's coordinate value (X, Y) /// /// \return /// Return nothing /// FDO_API virtual void SetPosition(FdoIDirectPosition* pos) = 0; /// \brief /// Get the position (X, Y) which indicates a point of interest on the map that was /// produced by the embedded !GetMap request. The point (X, Y) is a /// point in the (i, j) space defined by the Map CS. /// /// \return /// Return the interesting position. /// FDO_API virtual FdoIDirectPosition* GetPosition() = 0; /// \brief /// Set the number of features about which to return information (default=1). /// /// \param featureCount /// The number of the features to query. /// /// \return /// Returns nothing /// FDO_API virtual void SetFeatureCount(FdoInt32 featureCount = 1) = 0; /// \brief /// Get the number of features about which to return information. /// /// \return /// Returns candidate feature number. /// FDO_API virtual FdoInt32 GetFeatureCount () = 0; }; }}} In this class, both methods !GetFeatureClassName and !SetFeatureClassName are used to get/set a feature class which would be mapped to WMS layer in order to query the relevant feature information. [[BR]] '''• Example''' [[BR]] Generally, the !FdoWmsSelectCommand will be called before the !GetFeatureInfo custom command. The following select command can be called to bring map to the application.[[BR]] {{{ FdoPtr conn = CreateConnection (); conn->SetConnectionString (L"FeatureServer=http://www.WMSSERVER.net/wms_service"); FdoConnectionState_Open == conn->Open (); // Open the connection. FdoPtr cmd = static_cast (conn->CreateCommand (FdoCommandType_Select)); cmd->SetFeatureClassName (L"Borders_Poly"); FdoPtr selProps = cmd->GetPropertyNames (); FdoPtr prop = FdoIdentifier::Create (L"FeatId"); selProps->Add (prop); FdoPtr featReader = cmd->Execute (); ...... }}} After viewing the generated WMS map, users could wonder the information at some point of the map. The custom !GetFeatureInfo command could be called as below.[[BR]] {{{ FdoPtr cmdGFI = static_cast ( conn->CreateCommand(FdoWmsCommandType_GetFeatureInfo) ); FdoPtr id = FdoIdentifier::Create(L"Borders_Poly"); cmdGFI->SetFeatureClassName(id); FdoPtr gf = FdoFgfGeometryFactory::GetInstance(); FdoPtr pt = gf->CreatePositionXY(-1000, 1000); cmdGFI->SetPosition(pt); cmdGFI->SetOutputFormat(L"text/xml"); FdoPtr stream = cmdGFI->Execute (); ...... }}} In the upper case, the cached bounding box and resolution are not changed. However, if the application employs the tile approach to improve the map loading performance, the following extra code snippets would be involved to set the select command for execution. {{{ // This is the specified bounding box and resolution values for a tile. double minx, miny, maxx, maxy; long xResolution, yResolution; // Set up the CLIP function. FdoString* pRasterName = L"RasterPropertyName"; FdoPtr clipFuncParams = FdoExpressionCollection::Create(); FdoPtr rasterProp = FdoIdentifier::Create(pRasterName); funcParams->Add(rasterProp); FdoPtr minX = FdoDataValue::Create(minx, FdoDataType_Double); clipFuncParams->Add(minX); FdoPtr minY = FdoDataValue::Create(miny, FdoDataType_Double); clipFuncParams->Add(minY); FdoPtr maxX = FdoDataValue::Create(maxx, FdoDataType_Double); clipFuncParams->Add(maxX); FdoPtr maxY = FdoDataValue::Create(maxy, FdoDataType_Double); clipFuncParams->Add(maxY); FdoPtr clipFunc = FdoFunction::Create(L"CLIP", clipFuncParams); FdoPtr clipIdentifier = FdoComputedIdentifier::Create(L"ClippedRaster", clipFunc ); selProps->Add(clipIdentifier); // Set up the RESAMPLE function. FdoPtr resampleFuncParams = FdoExpressionCollection::Create(); FdoPtr rasterProp = FdoIdentifier::Create(pRasterName); resampleFuncParams->Add(rasterProp); minX = FdoDataValue::Create(minx, FdoDataType_Double); resampleFuncParams->Add(minX); minY = FdoDataValue::Create(miny, FdoDataType_Double); resampleFuncParams->Add(minY); maxX = FdoDataValue::Create(maxx, FdoDataType_Double); resampleFuncParams->Add(maxX); maxY = FdoDataValue::Create(maxy, FdoDataType_Double); resampleFuncParams->Add(maxY); FdoPtr height = FdoDataValue::Create((unsigned int)(yResolution), FdoDataType_Double); resampleFuncParams->Add(height); FdoPtr width = FdoDataValue::Create((unsigned int)(xResolution), FdoDataType_Double); resampleFuncParams->Add(width); FdoPtr resampleFunc = FdoFunction::Create(L"RESAMPLE", resampleFuncParams); FdoPtr resampleIdentifier = FdoComputedIdentifier::Create(pRasterName, resampleFunc); selProps->Add(resampleIdentifier); // Execute the command with "CLIP" and "RESAMPLE" functions. featReader = cmd->Execute (); }}} In this situation, the bounding box and resolution in the cached !GetMap parameters couldn't meet the request. If the requested tile for the feature information isn't the last requested one in the select phase, the cached !GetMap request should be updated with the relevant bounding box and height/width; the methods !SetBoundingBox, !SetHeight and !SetWidth need be called to set the expected values, and the command would automatically replace the cached parameters before execution. However, the application has the responsibilities to calculate which tile the specified interest point is in, and the new envelope points and resolution would be obtained. Accordingly, the !GetFeatureInfo command would be called with the extra settings as below.[[BR]] {{{ ... FdoPtr env = gf->CreateEnvelopeXY( minx, miny, maxx, maxy ); // Update the cached bounding box. cmdGFI->SetBoundingBox(env); // Update the resolution. cmdGFI->SetWidth(xResolution); cmdGFI->SetHeight(yResolution); // Execute the command in consideration of new bounding box and resolution. stream = cmdGFI->Execute (); }}} Note: If the select command isn't called before executing the !FdoWmsCommandType_!GetFeatureInfo command, there is no cached !GetMap request and an exception would be thrown.[[BR]] == Implications == • A new command !FdoWmsCommandType_GetFeatureInfo is added for WMS provider. == Test Plan == Add unit tests into WMS provider to test the new command. == !Funding/Resources == Autodesk to provide resources / funding