[mapguide-commits] r10218 - in branches/4.0/MgDev: Common/Stylization Server/src/UnitTesting

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Tue Sep 22 04:16:06 PDT 2026


Author: jng
Date: 2026-09-22 04:16:06 -0700 (Tue, 22 Sep 2026)
New Revision: 10218

Modified:
   branches/4.0/MgDev/Common/Stylization/DefaultStylizer.cpp
   branches/4.0/MgDev/Common/Stylization/LineBuffer.cpp
   branches/4.0/MgDev/Common/Stylization/LineBuffer.h
   branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp
Log:
Implement missing support for rendering GeometryCollection instances

Fixes #2461

Modified: branches/4.0/MgDev/Common/Stylization/DefaultStylizer.cpp
===================================================================
--- branches/4.0/MgDev/Common/Stylization/DefaultStylizer.cpp	2026-09-21 12:56:43 UTC (rev 10217)
+++ branches/4.0/MgDev/Common/Stylization/DefaultStylizer.cpp	2026-09-22 11:16:06 UTC (rev 10218)
@@ -361,19 +361,61 @@
             continue;
         }
 
-        // if we know how to stylize this type of geometry, then go ahead
-        GeometryAdapter* adapter = FindGeomAdapter(lb->geom_type());
-        if (adapter)
+        // A MultiGeometry (a.k.a. GeometryCollection) is a heterogeneous
+        // aggregate, so stylize each sub-geometry with the adapter matching
+        // its own geometry type.
+        if (lb->geom_type() == FdoGeometryType_MultiGeometry)
         {
-            // we need to stylize once for each FeatureTypeStyle that matches
-            // the geometry type (Note: this may have to change to match
-            // feature classes)
-            for (int i=0; i<ftsc->GetCount(); ++i)
+            for (int g=0; g<lb->geom_count(); ++g)
             {
-                MdfModel::FeatureTypeStyle* fts = ftsc->GetAt(i);
-                adapter->Stylize(renderer, features, initialPass, exec, lb, fts, lrTip, lrUrl, elevSettings);
+                int subType = lb->geom_type_of(g);
+
+                // TODO: Nested MultiGeometry sub-geometries are skipped here
+                // (FindGeomAdapter returns NULL for them).  Nested geometry
+                // collections are extremely rare; if support is ever needed,
+                // iterate the nested geometry's own sub-geometries recursively.
+                GeometryAdapter* subAdapter = FindGeomAdapter(subType);
+                if (!subAdapter)
+                    continue;
+
+                LineBuffer* subLb = LineBufferPool::NewLineBuffer(&m_lbPool, 8, lb->dimensionality(), lb->ignoreZ());
+                if (!subLb)
+                    continue;
+
+                std::unique_ptr<LineBuffer> spSubLB(subLb);
+                lb->ExtractGeometry(g, subLb);
+
+                if (subLb->point_count() == 0)
+                {
+                    LineBufferPool::FreeLineBuffer(&m_lbPool, spSubLB.release());
+                    continue;
+                }
+
+                for (int i=0; i<ftsc->GetCount(); ++i)
+                {
+                    MdfModel::FeatureTypeStyle* fts = ftsc->GetAt(i);
+                    subAdapter->Stylize(renderer, features, initialPass, exec, subLb, fts, lrTip, lrUrl, elevSettings);
+                }
+
+                LineBufferPool::FreeLineBuffer(&m_lbPool, spSubLB.release());
             }
         }
+        else
+        {
+            // if we know how to stylize this type of geometry, then go ahead
+            GeometryAdapter* adapter = FindGeomAdapter(lb->geom_type());
+            if (adapter)
+            {
+                // we need to stylize once for each FeatureTypeStyle that matches
+                // the geometry type (Note: this may have to change to match
+                // feature classes)
+                for (int i=0; i<ftsc->GetCount(); ++i)
+                {
+                    MdfModel::FeatureTypeStyle* fts = ftsc->GetAt(i);
+                    adapter->Stylize(renderer, features, initialPass, exec, lb, fts, lrTip, lrUrl, elevSettings);
+                }
+            }
+        }
 
         // free geometry when done stylizing
         LineBufferPool::FreeLineBuffer(&m_lbPool, spLB.release());

Modified: branches/4.0/MgDev/Common/Stylization/LineBuffer.cpp
===================================================================
--- branches/4.0/MgDev/Common/Stylization/LineBuffer.cpp	2026-09-21 12:56:43 UTC (rev 10217)
+++ branches/4.0/MgDev/Common/Stylization/LineBuffer.cpp	2026-09-22 11:16:06 UTC (rev 10218)
@@ -70,6 +70,8 @@
     m_num_geomcntrs_len = m_cntrs_len;
     m_num_geomcntrs = new int[m_num_geomcntrs_len];
     m_num_geomcntrs[0] = 0;
+    m_geom_types = new int[m_num_geomcntrs_len];
+    m_geom_types[0] = 0;
     if (!m_bProcessZ)
         m_bounds.minz = m_bounds.maxz = 0.0;
 }
@@ -99,7 +101,8 @@
     m_bProcessZ(false),
     m_bTransform2DPoints(false),
     m_num_geomcntrs_len(0),
-    m_num_geomcntrs(NULL)
+    m_num_geomcntrs(NULL),
+    m_geom_types(NULL)
 {
 }
 
@@ -111,6 +114,7 @@
     delete[] m_cntrs;
     delete[] m_csp;
     delete[] m_num_geomcntrs;
+    delete[] m_geom_types;
     delete[] m_arcs_sp;
     delete[] m_closeseg;
 }
@@ -127,6 +131,7 @@
     m_bTransform2DPoints = false;
     m_cur_geom = -1;
     m_num_geomcntrs[0] = 0;
+    m_geom_types[0] = 0;
     m_drawingScale = 0.0;
 
     m_cur_arcs_sp = -1;
@@ -190,12 +195,15 @@
     if (m_num_geomcntrs_len <= src.m_cur_geom)
     {
         delete [] m_num_geomcntrs;
+        delete [] m_geom_types;
         m_num_geomcntrs_len = src.m_num_geomcntrs_len;
         m_num_geomcntrs = new int[m_num_geomcntrs_len];
+        m_geom_types = new int[m_num_geomcntrs_len];
     }
 
     m_cur_geom = src.m_cur_geom;
     memcpy(m_num_geomcntrs, src.m_num_geomcntrs, sizeof(int)*(1+m_cur_geom));
+    memcpy(m_geom_types, src.m_geom_types, sizeof(int)*(1+m_cur_geom));
 
     // arc start point indices
     if (m_arcs_sp_len < src.m_arcs_sp_len)
@@ -236,6 +244,7 @@
     if (m_cur_geom >= m_num_geomcntrs_len)
         ResizeNumGeomContours(m_cur_geom * 2);
     m_num_geomcntrs[m_cur_geom] = 0;
+    m_geom_types[m_cur_geom] = m_geom_type;
 }
 
 
@@ -526,6 +535,7 @@
     if (m_cur_geom + other.m_cur_geom + 2 > m_num_geomcntrs_len)
         ResizeNumGeomContours(m_cur_geom + other.m_cur_geom + 2);
     memcpy(m_num_geomcntrs + m_cur_geom + 1, other.m_num_geomcntrs, sizeof(int)*(1+other.m_cur_geom));
+    memcpy(m_geom_types + m_cur_geom + 1, other.m_geom_types, sizeof(int)*(1+other.m_cur_geom));
     m_cur_geom += other.m_cur_geom + 1; // follows same pattern as contour
 
     m_bounds.add_point(RS_F_Point(other.m_bounds.minx, other.m_bounds.miny));
@@ -538,10 +548,19 @@
 void LineBuffer::ResizeNumGeomContours(int size)
 {
     _ASSERT(size > m_num_geomcntrs_len);
+
+    int old_len = m_num_geomcntrs_len;
+
     int* tempCntrs = new int[size];
-    memcpy(tempCntrs, m_num_geomcntrs, sizeof(int)*m_num_geomcntrs_len);
+    memcpy(tempCntrs, m_num_geomcntrs, sizeof(int)*old_len);
     delete[] m_num_geomcntrs;
     m_num_geomcntrs = tempCntrs;
+
+    int* tempTypes = new int[size];
+    memcpy(tempTypes, m_geom_types, sizeof(int)*old_len);
+    delete[] m_geom_types;
+    m_geom_types = tempTypes;
+
     m_num_geomcntrs_len = size;
 }
 
@@ -866,14 +885,23 @@
 {
     int* ireader = (int*)data;
 
-    // the geometry type
-    m_geom_type = (GeometryType)*ireader++;
-
     double last_z = 0.0;
     bool use_last_z = false;
     bool have_bad_z = false;
 
-    switch (m_geom_type)
+    // the geometry type
+    GeometryType geom_type = (GeometryType)*ireader++;
+
+    LoadAgfGeometry(geom_type, ireader, xformer, last_z, use_last_z, have_bad_z);
+}
+
+
+void LineBuffer::LoadAgfGeometry(GeometryType geomType, int*& ireader, CSysTransformer* xformer,
+                                 double& last_z, bool& use_last_z, bool& have_bad_z)
+{
+    m_geom_type = geomType;
+
+    switch (geomType)
     {
         // all the linear types...
         case GeometryType_MultiLineString:
@@ -1162,7 +1190,34 @@
 
         case GeometryType_MultiGeometry:
         {
-            // can't do that yet
+            // A MultiGeometry (a.k.a. GeometryCollection) is a heterogeneous
+            // aggregate of geometries.  It is serialized as:
+            //
+            //   [type = MultiGeometry][num geometries]
+            //   [sub-geometry type][sub-geometry data]...
+            //
+            // Each sub-geometry is itself a complete AGF geometry, so parse
+            // each one recursively and separate them in the LineBuffer with
+            // NewGeometry().
+            int num_geoms = *ireader++;
+            for (int q=0; q<num_geoms; ++q)
+            {
+                // read the type of this sub-geometry
+                GeometryType sub_type = (GeometryType)*ireader++;
+
+                // mark the beginning of a new geometry and record its type
+                if (q > 0)
+                {
+                    m_geom_type = sub_type;
+                    NewGeometry();
+                }
+
+                // parse the sub-geometry
+                LoadAgfGeometry(sub_type, ireader, xformer, last_z, use_last_z, have_bad_z);
+            }
+
+            // restore the aggregate geometry type
+            m_geom_type = GeometryType_MultiGeometry;
             break;
         }
     }
@@ -1169,6 +1224,37 @@
 }
 
 
+void LineBuffer::ExtractGeometry(int geomIndex, LineBuffer* dst)
+{
+    _ASSERT(dst != NULL);
+
+    // locate the contour range belonging to this geometry
+    int contourStart = 0;
+    for (int g = 0; g < geomIndex; ++g)
+        contourStart += m_num_geomcntrs[g];
+
+    int contourCount = m_num_geomcntrs[geomIndex];
+    if (contourCount == 0)
+        return;
+
+    int ptStart = m_csp[contourStart];
+    int ptEnd = m_csp[contourStart + contourCount - 1] + m_cntrs[contourStart + contourCount - 1];
+
+    // make room for the extracted points up front
+    dst->EnsurePoints(ptEnd - ptStart);
+
+    for (int i = ptStart; i < ptEnd; ++i)
+    {
+        if (m_types[i] == (unsigned char)stMoveTo)
+            dst->MoveTo(m_pts[i][0], m_pts[i][1], m_pts[i][2]);
+        else
+            dst->LineTo(m_pts[i][0], m_pts[i][1], m_pts[i][2]);
+    }
+
+    dst->SetGeometryType(m_geom_types[geomIndex]);
+}
+
+
 #define WRITE_INT(os, val) { \
     int val2 = val;          \
     os->write(&val2, 4);   } \

Modified: branches/4.0/MgDev/Common/Stylization/LineBuffer.h
===================================================================
--- branches/4.0/MgDev/Common/Stylization/LineBuffer.h	2026-09-21 12:56:43 UTC (rev 10217)
+++ branches/4.0/MgDev/Common/Stylization/LineBuffer.h	2026-09-22 11:16:06 UTC (rev 10218)
@@ -167,6 +167,9 @@
     // start a new geometry
     STYLIZATION_API void NewGeometry();
 
+    // extract a single geometry (by index) into a separate line buffer
+    STYLIZATION_API void ExtractGeometry(int geomIndex, LineBuffer* dst);
+
     // checks for a point in any contour
     STYLIZATION_API bool PointInPolygon(double& x, double& y) const;
     STYLIZATION_API bool PointInPolygon(int cntr, double& x, double& y) const; // point in specific contour
@@ -190,6 +193,7 @@
     inline int* geoms() const;
     inline int geom_count() const;
     inline int geom_size(int geom) const;
+    inline int geom_type_of(int geom) const;
     inline const RS_Bounds& bounds() const;
     inline void EnsurePoints(int n);
     inline void EnsureContours(int n);
@@ -241,6 +245,7 @@
     bool m_bTransform2DPoints;
     Matrix3D m_T;
     int* m_num_geomcntrs;
+    int* m_geom_types;
     int m_num_geomcntrs_len;
     int m_cur_geom;
     bool m_bIgnoreZ;
@@ -287,6 +292,9 @@
     void ResizeContours(int n);
     void ResizeArcsSpArray(int n);
     void ResizeCloseSegArray(int n);
+
+    void LoadAgfGeometry(GeometryType geomType, int*& ireader, CSysTransformer* xformer,
+                         double& last_z, bool& use_last_z, bool& have_bad_z);
 };
 
 
@@ -373,6 +381,12 @@
 }
 
 
+int LineBuffer::geom_type_of(int geom) const
+{
+    return m_geom_types[geom];
+}
+
+
 const RS_Bounds& LineBuffer::bounds() const
 {
     return m_bounds;

Modified: branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp
===================================================================
--- branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp	2026-09-21 12:56:43 UTC (rev 10217)
+++ branches/4.0/MgDev/Server/src/UnitTesting/TestRenderingService.cpp	2026-09-22 11:16:06 UTC (rev 10218)
@@ -18,6 +18,7 @@
 #include "MapGuideCommon.h"
 #include "ServiceManager.h"
 #include "ServerSiteService.h"
+#include "Services/FeatureService.h"
 #include "Fdo.h"
 #include "StylizationDefs.h"
 //#include "AGGRenderer.h"
@@ -1803,4 +1804,257 @@
     {
         throw;
     }
+}
+
+
+///----------------------------------------------------------------------------
+/// Test Case Description:
+///
+/// Renders a map whose single layer is backed by an SDF feature source that
+/// contains one GeometryCollection feature (a point, a linestring and a
+/// polygon).  The resulting image is written to the test results directory for
+/// manual visual inspection.  This test only asserts that rendering completes
+/// without throwing.
+///----------------------------------------------------------------------------
+TEST_CASE("RenderGeometryCollection", "[RenderingService]")
+{
+    try
+    {
+        // Set the user information for the current thread to be administrator.
+        Ptr<MgUserInformation> adminUserInfo = new MgUserInformation(MgUser::Administrator, L"");
+        MgUserInformation::SetCurrentUserInfo(adminUserInfo);
+
+        MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+        if (serviceManager == NULL)
+            throw new MgException(MgExceptionCodes::MgNullReferenceException, L"TestRenderingService.RenderGeometryCollection", __LINE__, __WFILE__, NULL, L"", NULL);
+
+        Ptr<MgFeatureService> svcFeature = dynamic_cast<MgFeatureService*>(serviceManager->RequestService(MgServiceType::FeatureService));
+        if (svcFeature == NULL)
+            throw new MgException(MgExceptionCodes::MgServiceNotAvailableException, L"TestRenderingService.RenderGeometryCollection", __LINE__, __WFILE__, NULL, L"", NULL);
+
+        Ptr<MgResourceService> svcResource = TestServiceFactory::CreateResourceService();
+
+        // Use a well-known geographic coordinate system.
+        Ptr<MgCoordinateSystemFactory> csFactory = new MgCoordinateSystemFactory();
+        STRING csWkt = csFactory->ConvertCoordinateSystemCodeToWkt(L"LL84");
+        std::string csWktMb = MgUtil::WideCharToMultiByte(csWkt);
+
+        STRING scName = L"Default";
+
+        // Build a schema whose geometry property accepts geometry collections.
+        // MultiGeometry is only added to the specific geometry types when the
+        // full set of geometric categories (point|curve|surface|solid) is set.
+        Ptr<MgFeatureSchema> schema = new MgFeatureSchema(L"Default", L"GeometryCollection test schema");
+        Ptr<MgClassDefinition> klass = new MgClassDefinition();
+        klass->SetName(L"TestClass");
+
+        Ptr<MgPropertyDefinitionCollection> clsProps = klass->GetProperties();
+        Ptr<MgPropertyDefinitionCollection> clsIdProps = klass->GetIdentityProperties();
+
+        Ptr<MgDataPropertyDefinition> id = new MgDataPropertyDefinition(L"ID");
+        id->SetDataType(MgPropertyType::Int32);
+        id->SetAutoGeneration(true);
+        clsProps->Add(id);
+        clsIdProps->Add(id);
+
+        Ptr<MgGeometricPropertyDefinition> geom = new MgGeometricPropertyDefinition(L"Geom");
+        geom->SetGeometryTypes(MgFeatureGeometricType::Point | MgFeatureGeometricType::Curve | MgFeatureGeometricType::Surface | MgFeatureGeometricType::Solid);
+        geom->SetSpatialContextAssociation(scName);
+        clsProps->Add(geom);
+
+        klass->SetDefaultGeometryPropertyName(L"Geom");
+
+        Ptr<MgClassDefinitionCollection> classes = schema->GetClasses();
+        classes->Add(klass);
+
+        Ptr<MgResourceIdentifier> fsId = new MgResourceIdentifier(L"Library://UnitTests/Data/GeometryCollectionTest.FeatureSource");
+        Ptr<MgFileFeatureSourceParams> fsParams = new MgFileFeatureSourceParams(L"OSGeo.SDF", scName, csWkt, schema);
+        svcFeature->CreateFeatureSource(fsId, fsParams);
+
+        // Build a geometry collection containing a point, a linestring and a polygon.
+        MgGeometryFactory geomFactory;
+        Ptr<MgGeometryCollection> geoms = new MgGeometryCollection();
+
+        Ptr<MgCoordinate> ptCoord = geomFactory.CreateCoordinateXY(0.0, 0.0);
+        Ptr<MgGeometry> point = geomFactory.CreatePoint(ptCoord);
+        geoms->Add(point);
+
+        Ptr<MgCoordinateCollection> lineCoords = new MgCoordinateCollection();
+        Ptr<MgCoordinate> lineCoord1 = geomFactory.CreateCoordinateXY(0.1, 0.1);
+        Ptr<MgCoordinate> lineCoord2 = geomFactory.CreateCoordinateXY(0.3, 0.3);
+        lineCoords->Add(lineCoord1);
+        lineCoords->Add(lineCoord2);
+        Ptr<MgGeometry> line = geomFactory.CreateLineString(lineCoords);
+        geoms->Add(line);
+
+        Ptr<MgCoordinateCollection> ringCoords = new MgCoordinateCollection();
+        Ptr<MgCoordinate> ringCoord1 = geomFactory.CreateCoordinateXY(0.1, 0.2);
+        Ptr<MgCoordinate> ringCoord2 = geomFactory.CreateCoordinateXY(0.2, 0.2);
+        Ptr<MgCoordinate> ringCoord3 = geomFactory.CreateCoordinateXY(0.2, 0.3);
+        Ptr<MgCoordinate> ringCoord4 = geomFactory.CreateCoordinateXY(0.1, 0.3);
+        Ptr<MgCoordinate> ringCoord5 = geomFactory.CreateCoordinateXY(0.1, 0.2);
+        ringCoords->Add(ringCoord1);
+        ringCoords->Add(ringCoord2);
+        ringCoords->Add(ringCoord3);
+        ringCoords->Add(ringCoord4);
+        ringCoords->Add(ringCoord5);
+        Ptr<MgLinearRing> ring = geomFactory.CreateLinearRing(ringCoords);
+        Ptr<MgGeometry> polygon = geomFactory.CreatePolygon(ring, nullptr);
+        geoms->Add(polygon);
+
+        Ptr<MgGeometry> multiGeom = geomFactory.CreateMultiGeometry(geoms);
+
+        // Insert the geometry collection feature.
+        Ptr<MgAgfReaderWriter> agfRw = new MgAgfReaderWriter();
+        Ptr<MgByteReader> agf = agfRw->Write(multiGeom);
+
+        Ptr<MgPropertyCollection> propVals = new MgPropertyCollection();
+        Ptr<MgGeometryProperty> geomVal = new MgGeometryProperty(L"Geom", agf);
+        propVals->Add(geomVal);
+
+        Ptr<MgFeatureReader> fr = svcFeature->InsertFeatures(fsId, L"Default:TestClass", propVals);
+        fr->Close();
+
+        // Layer definition with a point, a line and an area style so that every
+        // part of the geometry collection has a matching style.
+        std::string ldfXml;
+        ldfXml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+        ldfXml += "<LayerDefinition xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" version=\"1.2.0\" xsi:noNamespaceSchemaLocation=\"LayerDefinition-1.2.0.xsd\">";
+        ldfXml += "<VectorLayerDefinition>";
+        ldfXml += "<ResourceId>Library://UnitTests/Data/GeometryCollectionTest.FeatureSource</ResourceId>";
+        ldfXml += "<FeatureName>Default:TestClass</FeatureName>";
+        ldfXml += "<FeatureNameType>FeatureClass</FeatureNameType>";
+        ldfXml += "<Geometry>Geom</Geometry>";
+        ldfXml += "<VectorScaleRange>";
+        ldfXml += "<PointTypeStyle>";
+        ldfXml += "<DisplayAsText>false</DisplayAsText>";
+        ldfXml += "<AllowOverpost>false</AllowOverpost>";
+        ldfXml += "<PointRule>";
+        ldfXml += "<LegendLabel />";
+        ldfXml += "<PointSymbolization2D>";
+        ldfXml += "<Mark>";
+        ldfXml += "<Unit>Points</Unit>";
+        ldfXml += "<SizeContext>DeviceUnits</SizeContext>";
+        ldfXml += "<SizeX>10</SizeX>";
+        ldfXml += "<SizeY>10</SizeY>";
+        ldfXml += "<Rotation>0</Rotation>";
+        ldfXml += "<Shape>Square</Shape>";
+        ldfXml += "<Fill>";
+        ldfXml += "<FillPattern>Solid</FillPattern>";
+        ldfXml += "<ForegroundColor>ffff0000</ForegroundColor>";
+        ldfXml += "<BackgroundColor>ffff0000</BackgroundColor>";
+        ldfXml += "</Fill>";
+        ldfXml += "<Edge>";
+        ldfXml += "<LineStyle>Solid</LineStyle>";
+        ldfXml += "<Thickness>1</Thickness>";
+        ldfXml += "<Color>ff000000</Color>";
+        ldfXml += "<Unit>Points</Unit>";
+        ldfXml += "<SizeContext>DeviceUnits</SizeContext>";
+        ldfXml += "</Edge>";
+        ldfXml += "</Mark>";
+        ldfXml += "</PointSymbolization2D>";
+        ldfXml += "</PointRule>";
+        ldfXml += "</PointTypeStyle>";
+        ldfXml += "<LineTypeStyle>";
+        ldfXml += "<LineRule>";
+        ldfXml += "<LegendLabel />";
+        ldfXml += "<LineSymbolization2D>";
+        ldfXml += "<LineStyle>Solid</LineStyle>";
+        ldfXml += "<Thickness>2</Thickness>";
+        ldfXml += "<Color>ff00ff00</Color>";
+        ldfXml += "<Unit>Points</Unit>";
+        ldfXml += "<SizeContext>DeviceUnits</SizeContext>";
+        ldfXml += "</LineSymbolization2D>";
+        ldfXml += "</LineRule>";
+        ldfXml += "</LineTypeStyle>";
+        ldfXml += "<AreaTypeStyle>";
+        ldfXml += "<AreaRule>";
+        ldfXml += "<LegendLabel />";
+        ldfXml += "<AreaSymbolization2D>";
+        ldfXml += "<Fill>";
+        ldfXml += "<FillPattern>Solid</FillPattern>";
+        ldfXml += "<ForegroundColor>ff0000ff</ForegroundColor>";
+        ldfXml += "<BackgroundColor>ff0000ff</BackgroundColor>";
+        ldfXml += "</Fill>";
+        ldfXml += "<Stroke>";
+        ldfXml += "<LineStyle>Solid</LineStyle>";
+        ldfXml += "<Thickness>1</Thickness>";
+        ldfXml += "<Color>ff000000</Color>";
+        ldfXml += "<Unit>Points</Unit>";
+        ldfXml += "<SizeContext>DeviceUnits</SizeContext>";
+        ldfXml += "</Stroke>";
+        ldfXml += "</AreaSymbolization2D>";
+        ldfXml += "</AreaRule>";
+        ldfXml += "</AreaTypeStyle>";
+        ldfXml += "</VectorScaleRange>";
+        ldfXml += "</VectorLayerDefinition>";
+        ldfXml += "</LayerDefinition>";
+
+        Ptr<MgByteSource> ldfBs = new MgByteSource((BYTE_ARRAY_IN)ldfXml.c_str(), (INT32)ldfXml.length());
+        Ptr<MgByteReader> ldfContent = ldfBs->GetReader();
+        Ptr<MgResourceIdentifier> ldfId = new MgResourceIdentifier(L"Library://UnitTests/Layers/GeometryCollectionTest.LayerDefinition");
+        svcResource->SetResource(ldfId, ldfContent, nullptr);
+
+        // Map definition pointing at the layer definition.
+        std::string mdfXml;
+        mdfXml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+        mdfXml += "<MapDefinition xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" version=\"3.0.0\" xsi:noNamespaceSchemaLocation=\"MapDefinition-3.0.0.xsd\">";
+        mdfXml += "<Name>GeometryCollectionTest</Name>";
+        mdfXml += "<CoordinateSystem>";
+        mdfXml += csWktMb;
+        mdfXml += "</CoordinateSystem>";
+        mdfXml += "<Extents><MinX>-0.1</MinX><MaxX>0.5</MaxX><MinY>-0.1</MinY><MaxY>0.5</MaxY></Extents>";
+        mdfXml += "<BackgroundColor>ffffffff</BackgroundColor>";
+        mdfXml += "<MapLayer>";
+        mdfXml += "<Name>GeometryCollection</Name>";
+        mdfXml += "<ResourceId>Library://UnitTests/Layers/GeometryCollectionTest.LayerDefinition</ResourceId>";
+        mdfXml += "<Selectable>true</Selectable>";
+        mdfXml += "<ShowInLegend>true</ShowInLegend>";
+        mdfXml += "<LegendLabel>GeometryCollection</LegendLabel>";
+        mdfXml += "<ExpandInLegend>true</ExpandInLegend>";
+        mdfXml += "<Visible>true</Visible>";
+        mdfXml += "<Group />";
+        mdfXml += "</MapLayer>";
+        mdfXml += "<Watermarks />";
+        mdfXml += "</MapDefinition>";
+
+        Ptr<MgByteSource> mdfBs = new MgByteSource((BYTE_ARRAY_IN)mdfXml.c_str(), (INT32)mdfXml.length());
+        Ptr<MgByteReader> mdfContent = mdfBs->GetReader();
+        Ptr<MgResourceIdentifier> mdfId = new MgResourceIdentifier(L"Library://UnitTests/Maps/GeometryCollectionTest.MapDefinition");
+        svcResource->SetResource(mdfId, mdfContent, nullptr);
+
+        // Render the map.
+        Ptr<MgServerSiteService> svcSite = TestServiceFactory::CreateSiteService();
+        Ptr<MgSiteConnection> siteConnection = TestServiceFactory::CreateSiteConnection(svcSite);
+
+        Ptr<MgMap> map = new MgMap(siteConnection);
+        map->Create(mdfId, mdfId->GetName());
+
+        Ptr<MgCoordinate> coordNewCenter = new MgCoordinateXY(0.2, 0.2);
+        Ptr<MgPoint> ptNewCenter = new MgPoint(coordNewCenter);
+        map->SetViewCenter(ptNewCenter);
+        map->SetViewScale(250000.0);
+        map->SetDisplayDpi(96);
+        map->SetDisplayWidth(1024);
+        map->SetDisplayHeight(1024);
+
+        Ptr<MgRenderingService> svcRendering = TestServiceFactory::CreateRenderingService();
+        Ptr<MgByteReader> image = svcRendering->RenderMap(map, nullptr, MgImageFormats::Png);
+        image->ToFile(L"../UnitTestFiles/Results/RenderGeometryCollection.png");
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TestServiceFactory::TEST_LOCALE);
+        SAFE_RELEASE(e);
+        FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
+    catch (FdoException* e)
+    {
+        FDO_SAFE_RELEASE(e);
+        FAIL("FdoException occurred");
+    }
+    catch (...)
+    {
+        throw;
+    }
 }
\ No newline at end of file



More information about the mapguide-commits mailing list