wiki:MapGuideRfc126

MapGuide RFC 126 - Viewer selection optimization

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 Date8 Aug 2012
Last Modified25 May 2013
AuthorJackie Ng
RFC StatusImplemented
Implementation Statuscompleted
Proposed Milestone2.6
Assigned PSC guide(s)(when determined)
Voting History(vote date)
+1Jackie,Zac
+0
-0
-1
no voteBob,Bruce,Tom,Haris,Paul,Trevor

Overview

This RFC proposes to add a new version of QUERYMAPFEATURES in the mapagent that is designed to eliminate the need for additional round-tripping during selection operations.

Motivation

The current suite of client viewer applications all suffer from inefficent selections due to excessive roundtripping.

For example, the AJAX viewer will fire the following requests for a selection.

AJAX selection requests

For Fusion, the round-tripping is even worse:

Fusion selection requests

So what is the root problem for such excessive round-tripping? The answer is simply that QUERYMAPFEATURES does not fulfill the requirements that our current lineup of client-side viewers are demanding, so we have to make additional requests to other APIs or web extensions glue to work-around the shortcomings of this API. Some examples:

1) Select and give me back attributes of all features? Not possible. QUERYMAPFEATURES can only return attributes for one. That's why the AJAX viewer has to then make an additional call to getselectedfeatures.[php/jsp/aspx] and why Fusion has to make at least 4 extra requests (!!!) through various PHP glue (!!!!!) to get the required attribute information. We have a QueryFeatureProperties? API that supposedly does this, but there is no way to tap into this API via the mapagent. We need a mapagent API that can do the query and selection persistence, but be able to give us the attributes of all selected features instead of one.

[Benefit: no additional requests for extra attribute data]

2) QUERYMAPFEATURES also modifies the server-side selection, so it requires a follow-up GETDYNAMICMAPOVERLAYIMAGE request to update the selection image. If we can utilise data URIs to return the actual selection image back inline (as base64 encoded content) as part of the response, this eliminates the extra request for the selection image. Not all browsers support data URIs (a certain browser from Microsoft for example), so we can workaround that by letting the client specify if they want the inline selection image (browsers that support data URIs will say "yes" and those that can't will say "no"). The current state of data URI support can be found here

[Benefit: no additional requests for browsers that support data URIs. Those that don't will fall back to existing behaviour of doing the additional GETDYNAMICMAPOVERLAYIMAGE request.]

3) QUERYMAPFEATURES for tooltip requests doesn't need attribute information, so don't include this information.

[Benefit: Less data through the wire]

Proposed Solution

This RFC introduces a new, enhanced version of QUERYMAPFEATURES that can satisify all of the above requirements, being a one-stop shop of all our viewer selection and tooltip needs. The key features of this API over the previous QUERYMAPFEATURES API is that:

a) the client can specify what bits of information they want. So for the above cases:

1) and 2) wants attribute information, with an inline selection image if they say it is possible 3) Only wants tooltip and hyperlink information

b) If requested, it returns attributes of all selected features instead of one. It turns out, that we already have an existing API in MgRenderingService? that gets us half-way there (QueryFeatureProperties). The problem with the current implementation of QueryFeatureProperties is that it only records the layer name for each feature (as a special hard-coded property: _MgLayerName) and does not include other useful information such as the feature's bounding box (needed by the "Zoom to current feature" function of the AJAX viewer property pane). So we will offer a new internal API to MgRenderingService?:

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
    ///
    /// \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) = 0;
};

The existing public implementation of QueryFeatureProperties? will be re-routed into this internal API with bIncludeFeatureBBOX = false to preserve existing response behaviour (for WFS, which uses this API). When bIncludeFeatureBBOX = true, a new _MgFeatureBoundingBox special property is included with each MgPropertyCollection of the MgBatchPropertyCollection that is returned.

To support computation of each feature's bounding box, the existing FeaturePropRenderer needs to be modified as such:

  • The constructor includes a boolean parameter bIncludeFeatureBBOX
  • Overriding the following methods of Renderer, to get and the bounding boxes of each stylized geometry. The FeaturePropRenderer does not actually stylize features per-se, but these methods still get passed the raw geometry that we're interested in for each iteration of the stylization loop.
    • ProcessMarker
    • ProcessPolyline
    • ProcessPolygon
  • The bounding box computation only happens if bIncludeFeatureBBOX = true

A new overload of QueryMapFeatures? method will be added to MgHtmlController?

class MG_MAPGUIDE_API MgHtmlController : public MgController
{
INTERNAL_API:
    //////////////////////////////////////////////////////////////////
    /// \brief
    /// Processes a QueryMapFeatures request from the Viewer
    ///
    /// \param mapName
    /// Name of the map
    /// \param layer
    /// Layer for which to provide feature descriptions
    /// \param selectionGeometry
    /// Geometry defining which features to select
    /// \param selectionVariant
    /// Specifies how features are selected
    /// \param maxFeatures
    /// Max number of feature descriptions to return
    /// \param persist
    /// Indicates if the returned selection set should be persisted in the session repository
    ///
    /// \return
    /// A byte reader containing the feature info
    ///
    MgByteReader* QueryMapFeatures(
        CREFSTRING mapName,
        MgStringCollection* layerNames,
        MgGeometry* selectionGeometry,
        INT32 selectionVariant,
        CREFSTRING featureFilter,
        INT32 maxFeatures,
        bool persist,
        INT32 layerAttributeFilter,
        INT32 requestData,
        CREFSTRING selectionColor,
        CREFSTRING selectionFormat);
};

This method will be invoked by v2.6.0 of the QUERYMAPFEATURES operation. v2.6.0 of QUERYMAPFEATURES includes the following extra parameters:

NameDescriptionRequired
REQUESTDATAA bitmask indicating the type of information to be returned to the clientYes
SELECTIONCOLORThe color of the inline selection imageYes
SELECTIONFORMATThe format of the inline selection imageNo

This new overload behaves mostly like the original QueryMapFeatures? (clearing MgMap? changes before API invocation and saving selection if persist = true) with the following differences.

The requestData parameter is a bitmask indicating the information the client viewer requires.

Attributes = 1 InlineSelection? = 2 Tooltip = 4 Hyperlink = 8

Thus based on the value of requestData, we do the following:

  • If Tooltip flag set or Hyperlink flag set: Call existing MgRenderingService::QueryMapFeatures and store the result
  • If Attributes flag set: Call new internal MgRenderingService::QueryFeatureProperties and store the result
  • If InlineSelection flag set: Call MgRenderingService::RenderDynamicOverlay for selection only and convert the response to a base64 string

Depending on the requestData masked value, we will have a MgFeatureInformation (containing the tooltip, hyperlink and selection set), a MgBatchPropertyCollection containing the selected feature attributes and a STRING (containing the base64 selection image). Then we construct the appropriate XML response based on what pieces of data we have.

Structurally speaking the combined response looks like the original QUERYMAPFEATURES response, with the addition of 2 top-level elements:

For the <SelectedFeatures?> element, in order to produce a response that non-MapGuide client applications can easily understand, we don't do a verbatim dump of the MgBatchPropertyCollection? in our combined response. We massage the collection and output a custom XML structure like so:

  • [1] <SelectedFeatures?> Describes the selected features and its attributes. Empty if no attributes requested in the bitmask
    • [0...n] <SelectedLayer?> Describes a layer containing selected features. Includes name and id attributes to cross-match with the selection XML that's also included in the response
      • [1] <LayerMetadata?> Describes the layer properties, its display name and property type. Due to how QueryFeatureProperties? works, display names are written out instead of the FDO property names. This element provides a reverse lookup in-case the FDO property name for an attribute needs to be known
      • [0...n] <Feature> Describes each selected feature of that layer
        • [1] <Bounds> Describes the bounding box of the feature as a space-delimited quartet [minx miny maxx maxy]
        • [0...n] <Property> Describes a property value for the current feature
          • [1] <Name> The mapped property name (display name). To get the FDO property name, one can cross reference this name against the parent <LayerMetadata?>
          • [0...1] <Value> The property value. We follow MgProperty? XML behaviour. The omission of this tag indicates a null value.

An easy way to understand this new structure is to see some sample responses provided below

A rectangular selection query (assuming no support for data URIs) would look like this (note the value of REQUESTDATA):

OPERATION:QUERYMAPFEATURES
VERSION:2.6.0
PERSIST:1
MAPNAME:Sheboygan
SESSION:2da80b84-b5a1-11e2-8000-080027004461_en_MTI3LjAuMC4x0B060B050B04
SEQ:0.5907083495985717
LAYERNAMES:Parcels
GEOMETRY:POLYGON((-87.71863496334385 43.74829197543962, -87.71830632635577 43.74829197543962, -87.71830632635577 43.747986812522115, -87.71863496334385 43.747986812522115, -87.71863496334385 43.74829197543962))
SELECTIONVARIANT:INTERSECTS
CLIENTAGENT:Ajax Viewer
REQUESTDATA:1
SELECTIONCOLOR:0x0000FFFF
SELECTIONFORMAT:PNG
MAXFEATURES:-1

Or if the browser supports data URIs (note the value of REQUESTDATA):

OPERATION:QUERYMAPFEATURES
VERSION:2.6.0
PERSIST:1
MAPNAME:Sheboygan
SESSION:2da80b84-b5a1-11e2-8000-080027004461_en_MTI3LjAuMC4x0B060B050B04
SEQ:0.5907083495985717
LAYERNAMES:Parcels
GEOMETRY:POLYGON((-87.71863496334385 43.74829197543962, -87.71830632635577 43.74829197543962, -87.71830632635577 43.747986812522115, -87.71863496334385 43.747986812522115, -87.71863496334385 43.74829197543962))
SELECTIONVARIANT:INTERSECTS
CLIENTAGENT:Ajax Viewer
REQUESTDATA:3
SELECTIONCOLOR:0x0000FFFF
SELECTIONFORMAT:PNG
MAXFEATURES:-1

And produces a response like this (without inline selection):

<FeatureInformation>
<FeatureSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FeatureSet-1.0.0.xsd">
<Layer id="a754a6f0-b7d5-11e2-8004-080027004461">
<Class id="SHP_Schema:Parcels">
<ID>1R8AAA==</ID>
</Class>
</Layer>
</FeatureSet>
<Tooltip></Tooltip>
<Hyperlink></Hyperlink>
<InlineSelectionImage></InlineSelectionImage>
<SelectedFeatures>
<SelectedLayer id="a754a6f0-b7d5-11e2-8004-080027004461" name="Parcels">
<LayerMetadata>
<Property>
<Name>RLDESCR1</Name>
<Type>9</Type>
<DisplayName>Description1</DisplayName>
</Property>
<Property>
<Name>RTYPE</Name>
<Type>9</Type>
<DisplayName>Zone</DisplayName>
</Property>
<Property>
<Name>RACRE</Name>
<Type>9</Type>
<DisplayName>Acreage</DisplayName>
</Property>
<Property>
<Name>RLOT</Name>
<Type>9</Type>
<DisplayName>Lot Dimensions</DisplayName>
</Property>
<Property>
<Name>RNAME</Name>
<Type>9</Type>
<DisplayName>Owner</DisplayName>
</Property>
<Property>
<Name>RLDESCR2</Name>
<Type>9</Type>
<DisplayName>Description2</DisplayName>
</Property>
<Property>
<Name>RLDESCR3</Name>
<Type>9</Type>
<DisplayName>Description3</DisplayName>
</Property>
<Property>
<Name>RBILAD</Name>
<Type>9</Type>
<DisplayName>Billing Address</DisplayName>
</Property>
<Property>
<Name>RSQFT</Name>
<Type>7</Type>
<DisplayName>Lot Size (SqFt)</DisplayName>
</Property>
<Property>
<Name>RLDESCR4</Name>
<Type>9</Type>
<DisplayName>Description4</DisplayName>
</Property>
</LayerMetadata>
<Feature>
<Bounds>-87.722788142888234 43.755335868606466 -87.721634440510442 43.756252668096828</Bounds>
<Property>
<Name>Description1</Name>
<Value>ORIGINAL PLAT</Value>
</Property>
<Property>
<Name>Zone</Name>
<Value>MER</Value>
</Property>
<Property>
<Name>Acreage</Name>
<Value>1.61</Value>
</Property>
<Property>
<Name>Lot Dimensions</Name>
<Value>IRREG</Value>
</Property>
<Property>
<Name>Owner</Name>
<Value>MRED (14TH/ERIE) ASS</Value>
</Property>
<Property>
<Name>Description2</Name>
<Value>CSM REC IN VOL 17 P 242-4 AS</Value>
</Property>
<Property>
<Name>Description3</Name>
<Value>DOC #1593757 ROD BEING PRT</Value>
</Property>
<Property>
<Name>Billing Address</Name>
<Value>W228 N745 WESTMOUND</Value>
</Property>
<Property>
<Name>Lot Size (SqFt)</Name>
<Value>70140</Value>
</Property>
<Property>
<Name>Description4</Name>
<Value>OF LOTS 2 & 6 AND ALL OF</Value>
</Property>
</Feature>
</SelectedLayer>
</SelectedFeatures>
</FeatureInformation>

And a response like this (with inline selection):

<FeatureInformation>
<FeatureSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FeatureSet-1.0.0.xsd">
<Layer id="e3ec8e66-b7d5-11e2-8004-080027004461">
<Class id="SHP_Schema:Parcels">
<ID>2x4AAA==</ID>
</Class>
</Layer>
</FeatureSet>
<Tooltip></Tooltip>
<Hyperlink></Hyperlink>
<InlineSelectionImage>
<MimeType>image/png</MimeType>
<Content>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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANwx/weNt3ZF5c4jFwAAAABJRU5ErkJggg==</Content>
</InlineSelectionImage>
<SelectedFeatures>
<SelectedLayer id="e3ec8e66-b7d5-11e2-8004-080027004461" name="Parcels">
<LayerMetadata>
<Property>
<Name>RLDESCR1</Name>
<Type>9</Type>
<DisplayName>Description1</DisplayName>
</Property>
<Property>
<Name>RTYPE</Name>
<Type>9</Type>
<DisplayName>Zone</DisplayName>
</Property>
<Property>
<Name>RACRE</Name>
<Type>9</Type>
<DisplayName>Acreage</DisplayName>
</Property>
<Property>
<Name>RLOT</Name>
<Type>9</Type>
<DisplayName>Lot Dimensions</DisplayName>
</Property>
<Property>
<Name>RNAME</Name>
<Type>9</Type>
<DisplayName>Owner</DisplayName>
</Property>
<Property>
<Name>RLDESCR2</Name>
<Type>9</Type>
<DisplayName>Description2</DisplayName>
</Property>
<Property>
<Name>RLDESCR3</Name>
<Type>9</Type>
<DisplayName>Description3</DisplayName>
</Property>
<Property>
<Name>RBILAD</Name>
<Type>9</Type>
<DisplayName>Billing Address</DisplayName>
</Property>
<Property>
<Name>RSQFT</Name>
<Type>7</Type>
<DisplayName>Lot Size (SqFt)</DisplayName>
</Property>
<Property>
<Name>RLDESCR4</Name>
<Type>9</Type>
<DisplayName>Description4</DisplayName>
</Property>
</LayerMetadata>
<Feature>
<Bounds>-87.722795366494324 43.756662887792743 -87.721545117334017 43.757359091595923</Bounds>
<Property>
<Name>Description1</Name>
<Value>ORIGINAL PLAT</Value>
</Property>
<Property>
<Name>Zone</Name>
<Value>EXM</Value>
</Property>
<Property>
<Name>Acreage</Name>
<Value>1.12</Value>
</Property>
<Property>
<Name>Lot Dimensions</Name>
<Value>IRREG</Value>
</Property>
<Property>
<Name>Owner</Name>
<Value>REHABILITATION CENTE</Value>
</Property>
<Property>
<Name>Description2</Name>
<Value>THE W 1/2 OF LOT 1, ALL OF</Value>
</Property>
<Property>
<Name>Description3</Name>
<Value>LOTS 2, 3, 4 & 5; THE N</Value>
</Property>
<Property>
<Name>Billing Address</Name>
<Value>P O BOX 685</Value>
</Property>
<Property>
<Name>Lot Size (SqFt)</Name>
<Value>48870</Value>
</Property>
<Property>
<Name>Description4</Name>
<Value>50.00' OF LOT 6, ALSO THE N</Value>
</Property>
</Feature>
</SelectedLayer>
</SelectedFeatures>
</FeatureInformation>

A tooltip/hyperlink query, would look like this:

OPERATION:QUERYMAPFEATURES
VERSION:2.6.0
PERSIST:0
MAPNAME:Sheboygan
SESSION:c46f34fc-b5a1-11e2-8000-080027004461_en_MTI3LjAuMC4x0B060B050B04
SEQ:0.03694401495158672
LAYERNAMES:Islands,Hydrography,CityLimits,Parcels,Roads
GEOMETRY:POLYGON((-87.72742956817393 43.74290470419973, -87.72740966091371 43.74290470419973, -87.72740966091371 43.7428847969395, -87.72742956817393 43.7428847969395, -87.72742956817393 43.74290470419973))
SELECTIONVARIANT:INTERSECTS
MAXFEATURES:1
LAYERATTRIBUTEFILTER:5
REQUESTDATA:12
CLIENTAGENT:Ajax Viewer

and produces a response like this (note the lack of attributes. Previously, this was always included whether we wanted it or not):

<FeatureInformation>
<FeatureSet />
<Tooltip>Parcel\nName: TOBOTIPE, INC\nAddress: 4902 N. 18TH ST.</Tooltip>
<Hyperlink />
<InlineSelectionImage />
<SelectedFeatures />
</FeatureInformation>

Inline selection images are rendered out as inline base64 string, inside the <Content> element of the <InlineSelectionImage?> element, avoiding an additional GETDYNAMICMAPOVERLAYIMAGE request. This exploits the new Data URI feature supported by most modern browsers, the AJAX and Fusion viewers can do the capability checks client-side to determine whether to request an inline selection. Attributes of all features are grouped by their respective layer name.

The inline image does not have the mime-type prepended to the string. It is included as a separate <MimeType?> element of the <InlineSelectionImage?> element. Browsers that can support data URIs can assemble the final data URI from these components. Non-browser clients should be able to have the necessary means to process the base64 content based on the given <MimeType?> value.

As a result of this implementation, we are effectively batching up a series of operations server-side that previously would've been done with individual HTTP requests. Doing all this work in one shot eliminates the need for these additional requests.

On the viewer front, although we can't make a true "one size fits all" model that can accommodate both viewers, we can at least provide enough base information in the response for client viewers to reshape and re-project into their desired form. Some client-side processing will be required by both the AJAX and Fusion viewers.

The AJAX viewer will need some slight modifications to deal with the new QUERYMAPFEATURES response as the structure differs from what is returned by getselectedfeatures.[php/jsp/aspx], but the content is the same. Selected features can be grouped by layer name and each individual selected feature has a bounding box for the "Zoom to current feature" function to work. If data URIs are supported, the viewer needs to simply check the content of the InlineSelectionImage property in the response. If it is not empty, it can be assigned straight into the <img> element that holds the selection image, otherwise we proceeed as normal and send off a GETDYNAMICMAPOVERLAYIMAGE request for that image. With the implementation of this RFC, getselectedfeatures.[php/jsp/ajax] will no longer be required and can be removed.

Because we have the bounding box of individual features client-side, we gain an additional benefit by eliminating a GETFEATURESETENVELOPE request for the "Zoom to Selection" command, as we can instead compute the aggregate bounding box based on the bounding boxes of our selected features and use that instead for calculating the desired zoom level/location. In fact, the AJAX viewer will get new APIs to access this new information:

function GetSelectedBounds()
    Returns the bounding box of the selected features. Returns null if no features are selected. The box is a Bounds object:

    Bounds {
      minx; /* The min x coordinate of this bounding box */
      miny; /* The min y coordinate of this bounding box */
      maxx; /* The max x coordinate of this bounding box */
      maxy; /* The max y coordinate of this bounding box */
    }

function GetSelectedFeatures()
    Returns the selection set including attributes of selected features. The selection set is structured like so:

    {
        layerName1 : [
        { //feature 1 in layerName1
            values: [
                { name: "attribute1", value: "value1" },
                { name: "attribute2", value: "value2" },
                { name: "attribute3", value: "value3" },
                ...
                { name: "attributeN", value: "valueN" }
            ]
            zoom: { minx: <feature1_bbox_minx>, miny: <feature1_bbox_miny>, maxx: <feature1_bbox_maxx>, maxy: <feature1_bbox_maxy> }
        },
        { //feature 2 in layerName1
            values: [
                { name: "attribute1", value: "value1" },
                { name: "attribute2", value: "value2" },
                { name: "attribute3", value: "value3" },
                ...
                { name: "attributeN", value: "valueN" }
            ]
            zoom: { minx: <feature2_bbox_minx>, miny: <feature2_bbox_miny>, maxx: <feature2_bbox_maxx>, maxy: <feature2_bbox_maxy> }
        },
        ...,
        { //feature N in layerName1
            values: [
                { name: "attribute1", value: "value1" },
                { name: "attribute2", value: "value2" },
                { name: "attribute3", value: "value3" },
                ...
                { name: "attributeN", value: "valueN" }
            ]
            zoom: { minx: <featureN_bbox_minx>, miny: <featureN_bbox_miny>, maxx: <featureN_bbox_maxx>, maxy: <featureN_bbox_maxy> }
        }
        ], layerName2 : [
            ...
        ], 
        ..., 
        layerNameN : [
            ...
        ]
    }



For Fusion, it gets a bit more complicated as it makes various calls to assorted PHP scripts (SaveSelection?.php, GetSelectionProperties?.php, etc). This asynchronous call chain of PHP scripts needs to be refactored so that it only needs to send the new QUERYMAPFEATURES request and process its response as it does all the things that these PHP scripts were previously needed for. In addition, it's client-side selection structure is radically different from that of the AJAX viewer.

Discussion required: Currently, the following parts of the Fusion selection structure are not set by this new QUERYMAPFEATURES operation:

  • metadata (what widgets/functionality use this information? Current implementation blanks all values. Due some possible defect, the current pre-RFC implementation also blanks these values.)

Should v2.6.0 QUERYMAPFEATURE also accomodate for such information?

With these modifications in place, the AJAX Viewer request chain for a single selection looks like this (assuming browser support for data URIs):

For browsers that don't support data URIs, there will be an additional GETDYNAMICMAPOVERLAYIMAGE request.t

And the Fusion Viewer request chain for a single selection looks like this:

The last request is a GETDYNAMICOVERLAYIMAGE request. The reason we cannot currently eliminate the sending of this 3rd request is because the OpenLayers.Layer.MapGuide class is yet not aware of the changes in this RFC.

Modifying OpenLayers.Layer.MapGuide to accept an inline selection image data URI will eliminate the need for this 3rd request.

Similarly, the request to SaveSelection?.php may not even be needed either as QUERYMAPFEATURES supports selection set persistence.

Such changes however, are not within the scope of this RFC and is left as a post-RFC implementation exercise.

This RFC has already been implemented in their respective MapGuide and Fusion sandboxes. Upon adoption of this RFC, both sandboxes will be merged into their respective trunk streams.

Implications

This RFC is a mapagent API addition. No existing APIs are affected. No additions are made to the MapGuide Web Extension API.

The AJAX and Fusion viewers will be updated to take advantage of this new version of QUERYMAPFEATURES

Test Plan

Test AJAX and Fusion viewers with sample selections and verify the selection set data matches the previous implementation.

Verify that the invocations of QueryFeatureProperties? from WFS does not include our newly introduced special property.

Funding / Resources

Community

Last modified 5 years ago Last modified on May 24, 2013 7:49:35 AM

Attachments (4)

Download all attachments as: .zip