[mapguide-commits] r7478 - sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Wed May 8 04:37:42 PDT 2013


Author: jng
Date: 2013-05-08 04:37:42 -0700 (Wed, 08 May 2013)
New Revision: 7478

Modified:
   sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.cpp
   sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.h
Log:
Update the v2.6.0 QUERYMAPFEATURES response format. Rather than perform a verbatim xml dump of the MgBatchPropertyCollection, output selected feature attributes using the following structure:
 - [0...n] <SelectedLayer> A layer containing selected features has a name and id if needed to match by name or against selection set xml.
   - [1] <LayerMetadata> Describing properties, its display name and its property type. Due to how QueryFeatureProperties works, display names are used for property names for each feature. This element provides a reverse lookup table to ascertain the FDO system property name for each attribute for client applications that require such information
   - [0...n] <Feature> Each selected feature in the layer
     - [1] <Bounds> The feature's bounding box [minx miny maxx maxy]
     - [0...n] <Property> Property value for current feature

The top-level <Attributes> element has been changed to <SelectedFeatures>

This structure should be more clearer and intuitive for client applications to understand. No need to process _MgLayerName and _MgFeatureBoundingBox, MgHtmlController will do all of that.

Modified: sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.cpp
===================================================================
--- sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.cpp	2013-05-06 12:46:20 UTC (rev 7477)
+++ sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.cpp	2013-05-08 11:37:42 UTC (rev 7478)
@@ -17,6 +17,8 @@
 
 #include "MapGuideCommon.h"
 #include "Base64.h"
+#include "VectorLayerDefinition.h"
+#include "NameStringPair.h"
 
 #define REQUEST_ATTRIBUTES       1
 #define REQUEST_INLINE_SELECTION 2
@@ -247,15 +249,15 @@
     Ptr<MgResourceService> resourceService = (MgResourceService*)GetService(MgServiceType::ResourceService);
 
     // Create MgMap
-    Ptr<MgMap> map = new MgMap();
-    map->Open(resourceService, mapName);
+    Ptr<MgMap> map = new MgMap(m_siteConn);
+    map->Open(mapName);
 
     // Make sure we clear any track changes - these are not applicable for AJAX.
     Ptr<MgNamedCollection> changeLists = map->GetChangeLists();
     if (changeLists->GetCount() > 0)
     {
         map->ClearChanges();
-        map->Save(resourceService);
+        map->Save();
     }
 
     // Create Proxy Rendering Service instance
@@ -274,6 +276,9 @@
             selection = new MgSelection(map);
         selection->Save(resourceService, mapName);
         newSelection = SAFE_ADDREF(selection.p);
+
+        //Needed for GetLayers() to work below
+        newSelection->SetMap(map);
     }
 
     // Render an image of this selection if requested
@@ -284,18 +289,21 @@
         inlineSelectionImg = service->RenderDynamicOverlay(map, newSelection, renderOpts);
     }
 
+    // Collect any attributes of selected features
     if ((requestData & REQUEST_ATTRIBUTES) == REQUEST_ATTRIBUTES)
     {
-        attributes = service->QueryFeatureProperties(map, layerNames, selectionGeometry, selectionVariant, L"", -1, layerAttributeFilter, true);
+        // This could be chunky for big selections, but client applications can control this via MAXFEATURES, so the onus is on them
+        attributes = service->QueryFeatureProperties(map, layerNames, selectionGeometry, selectionVariant, L"", maxFeatures, layerAttributeFilter, true);
     }
 
-    result = CollectQueryMapFeaturesResult(requestData, featureInfo, newSelection, attributes, inlineSelectionImg);
+    result = CollectQueryMapFeaturesResult(resourceService, requestData, featureInfo, newSelection, attributes, inlineSelectionImg);
 
     // Return XML
     return result.Detach();
 }
 
-MgByteReader* MgHtmlController::CollectQueryMapFeaturesResult(INT32 requestData,
+MgByteReader* MgHtmlController::CollectQueryMapFeaturesResult(MgResourceService* resourceService,
+                                                              INT32 requestData,
                                                               MgFeatureInformation* featInfo,
                                                               MgSelection* selectionSet,
                                                               MgBatchPropertyCollection* attributes, 
@@ -312,7 +320,7 @@
         hyperlink = featInfo->GetHyperlink();
     }
 
-    // TODO: define a schema for this XML
+    // TODO: Stil haven't defined a schema for v2.6. Should we?
     xml.append(L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<FeatureInformation>\n");
 
     size_t len = xmlSelection.length();
@@ -348,10 +356,10 @@
     if (((requestData & REQUEST_INLINE_SELECTION) == REQUEST_INLINE_SELECTION) && NULL != inlineSelection)
     {
         xml.append(L"<InlineSelectionImage>\n");
-        xml.append(L"<MimeType>\n");
+        xml.append(L"<MimeType>");
         xml.append(inlineSelection->GetMimeType());
         xml.append(L"</MimeType>\n");
-        xml.append(L"<Content>\n");
+        xml.append(L"<Content>");
         MgByteSink sink(inlineSelection);
         Ptr<MgByte> bytes = sink.ToBuffer();
         Ptr<MgMemoryStreamHelper> streamHelper = new MgMemoryStreamHelper((INT8*) bytes->Bytes(), bytes->GetLength(), false);
@@ -366,10 +374,9 @@
 
     if (((requestData & REQUEST_ATTRIBUTES) == REQUEST_ATTRIBUTES) && NULL != attributes)
     {
-        xml.append(L"<Attributes>\n");
-        Ptr<MgByteReader> attrXml = attributes->ToXml();
-        xml.append(attrXml->ToString());
-        xml.append(L"</Attributes>\n");
+        xml.append(L"<SelectedFeatures>\n");
+        WriteSelectedFeatureAttributes(resourceService, selectionSet, attributes, xml);
+        xml.append(L"</SelectedFeatures>\n");
     }
     else
         xml.append(L"<Attributes />\n");
@@ -381,6 +388,255 @@
     return MgUtil::GetByteReader(xmlDoc, &mimeType);
 }
 
+void MgHtmlController::WriteSelectedFeatureAttributes(MgResourceService* resourceService,
+                                                      MgSelection* selectionSet,
+                                                      MgBatchPropertyCollection* attributes,
+                                                      REFSTRING xmlOut)
+{
+    //Rather than doing a verbatim xml dump of MgBatchPropertyCollection, let's output something
+    //that is more intuitive for client applications using this service operation to understand.
+
+    //We could assume sorted layers in the MgBatchPropertyCollection, but play safe
+    //and store our per-layer XML fragments in a bucket (keyed on layer name) and return 
+    //the merged bucket result at the end
+    std::map<STRING, STRING> bucket;
+
+    MgAgfReaderWriter agfRw;
+    MgWktReaderWriter wktRw;
+
+    Ptr<MgReadOnlyLayerCollection> selLayers = selectionSet->GetLayers();
+    if (NULL != selLayers.p)
+    {
+        // Our structure is as follows
+        //
+        // [0...n] <SelectedLayer> - A layer containing selected features
+        //   [1] <LayerMetadata> - Describing properties, its display name and its property type. Due to how QueryFeatureProperties works, display names
+        //                         are used for property names for each feature. This element provides a reverse lookup table to ascertain the FDO system 
+        //                         property name for each attribute for client applications that require such information
+        //     [0...n] <Feature> - Each selected feature in the layer
+        //       [1] <Bounds> - The feature's bounding box [minx miny maxx maxy]
+        //       [0...n] <Property> - Property value for current feature
+
+        for (INT32 i = 0; i < selLayers->GetCount(); i++)
+        {
+            Ptr<MgLayerBase> selLayer = selLayers->GetItem(i);
+            STRING layerName = selLayer->GetName();
+            if (bucket.find(layerName) == bucket.end())
+            {
+                STRING xml = L"<SelectedLayer id=\"";
+                xml.append(selLayer->GetObjectId());
+                xml.append(L"\" name=\"");
+                xml.append(selLayer->GetName());
+                xml.append(L"\">\n");
+                xml.append(L"<LayerMetadata>\n");
+                Ptr<MgClassDefinition> clsDef = selLayer->GetClassDefinition();
+                Ptr<MgPropertyDefinitionCollection> clsProps = clsDef->GetProperties();
+                Ptr<MgResourceIdentifier> layerId = selLayer->GetLayerDefinition();
+                //We need the property mappings of the source layer definition to compile our layer metadata
+                std::auto_ptr<MdfModel::LayerDefinition> ldf(MgLayerBase::GetLayerDefinition(resourceService, layerId));
+                if (ldf.get() != NULL)
+                {
+                    MdfModel::VectorLayerDefinition* vl = dynamic_cast<MdfModel::VectorLayerDefinition*>(ldf.get());
+                    if(vl != NULL)
+                    {
+                        MdfModel::NameStringPairCollection* pmappings = vl->GetPropertyMappings();
+                        for (int j=0; j<pmappings->GetCount(); j++)
+                        {
+                            STRING pTypeStr;
+                            MdfModel::NameStringPair* m = pmappings->GetAt(j);
+                            INT32 pidx = clsProps->IndexOf(m->GetName());
+                            if (pidx >= 0)
+                            {
+                                Ptr<MgPropertyDefinition> propDef = clsProps->GetItem(pidx);
+                                INT32 pdType = propDef->GetPropertyType();
+                                INT32 pType = MgPropertyType::Null;
+                                if (pdType == MgFeaturePropertyType::DataProperty)
+                                {
+                                    pType = ((MgDataPropertyDefinition*)propDef.p)->GetDataType();
+                                }
+                                else if (pdType == MgFeaturePropertyType::GeometricProperty)
+                                {
+                                    pType = MgPropertyType::Geometry;
+                                }
+                                MgUtil::Int32ToString(pType, pTypeStr);
+                                xml.append(L"<Property>\n");
+                                xml.append(L"<Name>");
+                                xml.append(m->GetName());
+                                xml.append(L"</Name>\n");
+                                xml.append(L"<Type>");
+                                xml.append(pTypeStr);
+                                xml.append(L"</Type>\n");
+                                xml.append(L"<DisplayName>");
+                                xml.append(m->GetValue());
+                                xml.append(L"</DisplayName>\n");
+                                xml.append(L"</Property>\n");
+                            }
+                        }
+                    }
+                }
+                xml += L"</LayerMetadata>\n";
+                bucket[layerName] = xml;
+            }
+        }
+
+        for (INT32 i = 0; i < attributes->GetCount(); i++)
+        {
+            Ptr<MgPropertyCollection> featProps = attributes->GetItem(i);
+            INT32 lidx = featProps->IndexOf(L"_MgLayerName");
+            INT32 fidx = featProps->IndexOf(L"_MgFeatureBoundingBox");
+
+            //Must have both the _MgLayerName and _MgFeatureBoundingBox
+            if (lidx < 0 || fidx < 0)
+                continue;
+
+            Ptr<MgStringProperty> layerNameProp = (MgStringProperty*)featProps->GetItem(lidx);
+            Ptr<MgStringProperty> boundsProp = (MgStringProperty*)featProps->GetItem(fidx);
+
+            //Locate the matching bucketed fragment to append to
+            STRING layerName = layerNameProp->GetValue();
+            if (bucket.find(layerName) != bucket.end())
+            {
+                //Good to go, write our feature to this bucketed fragment
+                REFSTRING xml = bucket[layerName];
+                xml.append(L"<Feature>\n");
+                xml.append(L"<Bounds>");
+                xml.append(boundsProp->GetValue());
+                xml.append(L"</Bounds>\n");
+                for (INT32 p = 0; p < featProps->GetCount(); p++)
+                {
+                    //Skip special properties
+                    if (p == lidx || p == fidx)
+                        continue;
+                    Ptr<MgNullableProperty> prop = dynamic_cast<MgNullableProperty*>(featProps->GetItem(p));
+                    if (NULL != prop.p)
+                    {
+                        xml.append(L"<Property>\n");
+                        xml.append(L"<Name>");
+                        xml.append(prop->GetName());
+                        xml.append(L"</Name>\n");
+                        //We'll follow MgProperty spec. Null is represented by omission of <Value>
+                        if (!prop->IsNull())
+                        {
+                            INT32 ptype = prop->GetPropertyType();
+                            switch(ptype)
+                            {
+                            //case MgPropertyType::Blob:
+                            case MgPropertyType::Boolean:
+                                {
+                                    xml.append(L"<Value>");
+                                    xml.append(((MgBooleanProperty*)prop.p)->GetValue() ? L"true" : L"false");
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            case MgPropertyType::Byte:
+                                {
+                                    STRING sVal;
+                                    MgUtil::Int32ToString((INT32)((MgByteProperty*)prop.p)->GetValue(), sVal);
+                                    xml.append(L"<Value>");
+                                    xml.append(sVal);
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            //case MgPropertyType::Clob:
+                            case MgPropertyType::DateTime:
+                                {
+                                    Ptr<MgDateTime> dt = ((MgDateTimeProperty*)prop.p)->GetValue();
+                                    xml.append(L"<Value>");
+                                    xml.append(dt->ToXmlString());
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            case MgPropertyType::Decimal:
+                            case MgPropertyType::Double:
+                                {
+                                    STRING sVal;
+                                    MgUtil::DoubleToString(((MgDoubleProperty*)prop.p)->GetValue(), sVal);
+                                    xml.append(L"<Value>");
+                                    xml.append(sVal);
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            case MgPropertyType::Geometry:
+                                {
+                                    try 
+                                    {
+                                        Ptr<MgByteReader> agf = ((MgGeometryProperty*)prop.p)->GetValue();
+                                        Ptr<MgGeometry> geom = agfRw.Read(agf);
+                                        STRING wkt = wktRw.Write(geom);
+                                        xml.append(L"<Value>");
+                                        xml.append(wkt);
+                                        xml.append(L"</Value>\n");
+                                    }
+                                    catch (MgException* ex) //Bad geom maybe
+                                    {
+                                        SAFE_RELEASE(ex);
+                                    }
+                                }
+                                break;
+                            case MgPropertyType::Int16:
+                                {
+                                    STRING sVal;
+                                    MgUtil::Int32ToString((INT32)((MgInt16Property*)prop.p)->GetValue(), sVal);
+                                    xml.append(L"<Value>");
+                                    xml.append(sVal);
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            case MgPropertyType::Int32:
+                                {
+                                    STRING sVal;
+                                    MgUtil::Int32ToString(((MgInt32Property*)prop.p)->GetValue(), sVal);
+                                    xml.append(L"<Value>");
+                                    xml.append(sVal);
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            case MgPropertyType::Int64:
+                                {
+                                    STRING sVal;
+                                    MgUtil::Int64ToString(((MgInt64Property*)prop.p)->GetValue(), sVal);
+                                    xml.append(L"<Value>");
+                                    xml.append(sVal);
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            case MgPropertyType::Single:
+                                {
+                                    STRING sVal;
+                                    MgUtil::SingleToString(((MgSingleProperty*)prop.p)->GetValue(), sVal);
+                                    xml.append(L"<Value>");
+                                    xml.append(sVal);
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            case MgPropertyType::String:
+                                {
+                                    xml.append(L"<Value>");
+                                    xml.append(MgUtil::ReplaceEscapeCharInXml(((MgStringProperty*)prop.p)->GetValue()));
+                                    xml.append(L"</Value>\n");
+                                }
+                                break;
+                            }
+                        }
+                        xml.append(L"</Property>\n");
+                    }
+                }
+                xml.append(L"</Feature>\n");
+            }
+        }
+    
+        //Now merge the bucketed fragments
+        for (std::map<STRING, STRING>::iterator it = bucket.begin(); it != bucket.end(); it++)
+        {
+            //Close off the <SelectedLayer> elements
+            STRING xml = it->second;
+            xml.append(L"</SelectedLayer>");
+            //Good to append to the final result
+            xmlOut.append(xml);
+        }
+    }
+}
+
 //////////////////////////////////////////////////////////////////
 // Generates JavaScript code that can be embedded in an HTML response
 // to a non-viewer initiated web application request. The returned code

Modified: sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.h
===================================================================
--- sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.h	2013-05-06 12:46:20 UTC (rev 7477)
+++ sandbox/jng/queryfeatures_v2/Common/MapGuideCommon/Controller/HtmlController.h	2013-05-08 11:37:42 UTC (rev 7478)
@@ -271,7 +271,8 @@
     /// \brief
     /// Assembles the composite QueryMapFeatures result
     ///
-    virtual MgByteReader* CollectQueryMapFeaturesResult(INT32 requestData, 
+    virtual MgByteReader* CollectQueryMapFeaturesResult(MgResourceService* resourceService,
+                                                        INT32 requestData, 
                                                         MgFeatureInformation* featInfo,
                                                         MgSelection* selectionSet,
                                                         MgBatchPropertyCollection* attributes, 
@@ -289,6 +290,12 @@
         delete this;
     }
 
+private:
+    static void WriteSelectedFeatureAttributes(MgResourceService* resourceService,
+                                               MgSelection* selectionSet,
+                                               MgBatchPropertyCollection* attriubtes,
+                                               REFSTRING xmlOut);
+
 CLASS_ID:
     static const INT32 m_cls_id = MapGuide_MapLayer_ZcvController;
 



More information about the mapguide-commits mailing list