[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