[mapguide-commits] r9207 - in sandbox/jng/geoprocessing: Common/Geometry Server/src/UnitTesting

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Sat Jun 10 09:13:00 PDT 2017


Author: jng
Date: 2017-06-10 09:13:00 -0700 (Sat, 10 Jun 2017)
New Revision: 9207

Added:
   sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.cpp
   sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.h
Modified:
   sandbox/jng/geoprocessing/Common/Geometry/Geometry.cpp
   sandbox/jng/geoprocessing/Common/Geometry/Geometry.h
   sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj
   sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj.filters
   sandbox/jng/geoprocessing/Common/Geometry/GeometryBuild.cpp
   sandbox/jng/geoprocessing/Common/Geometry/GeometryClassId.h
   sandbox/jng/geoprocessing/Common/Geometry/GeometryCommon.h
   sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.cpp
   sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.h
Log:
Enhance the MgGeometry API with support for prepared geometries.
 - A new MgPreparedGeometry represents a prepared version of a given MgGeometry and is obtained by calling Prepare() on the MgGeometry instance.
 - A prepared geometry is optimized for repeated evaluation of boolean spatial predicates against any geometries.
 - A new performance test exercises performance of prepared geometries vs un-prepared geometries. The test exercises a battery of spatial predicates with the full set of 17565 Sheboygan parcels against a "C" shaped geometry in the area of Sheboygan. The test shows a 2.5x to 3x improvement (24-26 seconds with un-prepared vs 8-9 seconds with prepared)
 - The existing Touches unit test has been updated to also test against the prepared versions of the test geometries.

Modified: sandbox/jng/geoprocessing/Common/Geometry/Geometry.cpp
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/Geometry.cpp	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Common/Geometry/Geometry.cpp	2017-06-10 16:13:00 UTC (rev 9207)
@@ -259,3 +259,8 @@
 {
     return MgGeometryEntityType::Geometry;
 }
+
+MgPreparedGeometry* MgGeometry::Prepare()
+{
+    return MgPreparedGeometry::Create(this);
+}
\ No newline at end of file

Modified: sandbox/jng/geoprocessing/Common/Geometry/Geometry.h
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/Geometry.h	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Common/Geometry/Geometry.h	2017-06-10 16:13:00 UTC (rev 9207)
@@ -18,6 +18,7 @@
 #ifndef _MGGEOMETRY_H_
 #define _MGGEOMETRY_H_
 
+class MgPreparedGeometry;
 class MgGeometry;
 template class MG_GEOMETRY_API Ptr<MgGeometry>;
 
@@ -733,6 +734,27 @@
     ///
     virtual MgGeometry* Union(MgGeometry* other);
 
+    ///////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Returns a prepared version of this geometry that is optimized for
+    /// repeated evaluation of spatial predicate operations
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual MgGeometry Prepare();
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual MgGeometry Prepare();
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual MgGeometry Prepare();
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \return
+    /// An MgPreparedGeometry representing the prepared version of this geometry
+    ///
+    MgPreparedGeometry* Prepare();
+
 INTERNAL_API:
 
     virtual INT32 GetEntityType();

Modified: sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj	2017-06-10 16:13:00 UTC (rev 9207)
@@ -492,6 +492,12 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="PreparedGeometry.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="Spatial\MathUtility.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -907,6 +913,7 @@
     <ClInclude Include="Parse\ParseAwktUtil.h" />
     <ClInclude Include="Parse\StringUtility.h" />
     <ClInclude Include="Parse\yyAwkt.h" />
+    <ClInclude Include="PreparedGeometry.h" />
     <ClInclude Include="Spatial\MathUtility.h" />
     <ClInclude Include="Spatial\SpatialUtility.h" />
     <ClInclude Include="Spatial\SpatialUtilityCircularArc.h" />

Modified: sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj.filters
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj.filters	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Common/Geometry/Geometry.vcxproj.filters	2017-06-10 16:13:00 UTC (rev 9207)
@@ -373,6 +373,7 @@
     <ClCompile Include="Ring.cpp" />
     <ClCompile Include="WktReaderWriter.cpp" />
     <ClCompile Include="GeometrySimplifier.cpp" />
+    <ClCompile Include="PreparedGeometry.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Buffer\BorderWalker.h">
@@ -973,6 +974,7 @@
     <ClInclude Include="WktReaderWriter.h" />
     <ClInclude Include="GeometrySimplifier.h" />
     <ClInclude Include="GeometrySimplificationAlgorithmType.h" />
+    <ClInclude Include="PreparedGeometry.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Geometry.rc" />

Modified: sandbox/jng/geoprocessing/Common/Geometry/GeometryBuild.cpp
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/GeometryBuild.cpp	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Common/Geometry/GeometryBuild.cpp	2017-06-10 16:13:00 UTC (rev 9207)
@@ -56,6 +56,7 @@
 #include "PointCollection.cpp"
 #include "Polygon.cpp"
 #include "PolygonCollection.cpp"
+#include "PreparedGeometry.cpp"
 #include "Region.cpp"
 #include "Ring.cpp"
 #include "WktReaderWriter.cpp"

Modified: sandbox/jng/geoprocessing/Common/Geometry/GeometryClassId.h
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/GeometryClassId.h	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Common/Geometry/GeometryClassId.h	2017-06-10 16:13:00 UTC (rev 9207)
@@ -79,6 +79,8 @@
 // Simplifier
 #define Geometry_GeometrySimplifier                       GEOMETRY_ID+54
 
+#define Geometry_PreparedGeometry                         GEOMETRY_ID+55
+
 // CoordinateSystem API
 #define CoordinateSystem_CoordinateSystem                           GEOMETRY_COORDINATE_SYSTEM_ID+0
 #define CoordinateSystem_CoordinateSystemFactory                    GEOMETRY_COORDINATE_SYSTEM_ID+1

Modified: sandbox/jng/geoprocessing/Common/Geometry/GeometryCommon.h
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/GeometryCommon.h	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Common/Geometry/GeometryCommon.h	2017-06-10 16:13:00 UTC (rev 9207)
@@ -100,6 +100,8 @@
 #include "MultiPoint.h"
 #include "MultiPolygon.h"
 
+#include "PreparedGeometry.h"
+
 #include "AgfReaderWriter.h"
 #include "WktReaderWriter.h"
 

Added: sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.cpp
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.cpp	                        (rev 0)
+++ sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.cpp	2017-06-10 16:13:00 UTC (rev 9207)
@@ -0,0 +1,142 @@
+//
+//  Copyright (C) 2004-2017 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+#include "GeometryCommon.h"
+#include <geos/geom/prep/PreparedGeometry.h>
+#include <geos/geom/prep/PreparedGeometryFactory.h>
+
+using namespace geos::geom::prep;
+
+class MgPreparedGeometry::PreparedGeometryImpl 
+{
+public:
+    PreparedGeometryImpl() 
+      : m_pg(NULL), 
+        m_pm(new PrecisionModel()), 
+        m_gf(NULL),
+        m_geom(NULL)
+    {
+        m_gf.reset(new GeometryFactory(m_pm.get(), 10));
+    }
+    ~PreparedGeometryImpl() 
+    {
+        PreparedGeometryFactory::destroy(m_pg);
+    }
+    Geometry* Convert(MgGeometry* geom)
+    {
+        WKTReader r(m_gf.get());
+        Ptr<MgGeometry> tGeom = MgSpatialUtility::TesselateCurve(geom);
+        STRING wktGeom = tGeom->ToAwkt(true);
+
+        return r.read(MgUtil::WideCharToMultiByte(wktGeom));
+    }
+    void Prepare(MgGeometry* geom)
+    {
+        WKTReader r(m_gf.get());
+        Ptr<MgGeometry> tGeom = MgSpatialUtility::TesselateCurve(geom);
+        STRING wktGeom = tGeom->ToAwkt(true);
+
+        m_geom.reset(r.read(MgUtil::WideCharToMultiByte(wktGeom)));
+        m_pg = PreparedGeometryFactory::prepare(m_geom.get());
+    }
+
+    const PreparedGeometry* m_pg;
+
+private:
+    std::auto_ptr<Geometry> m_geom;
+    std::auto_ptr<PrecisionModel> m_pm;
+    std::auto_ptr<GeometryFactory> m_gf;
+};
+
+MgPreparedGeometry* MgPreparedGeometry::Create(MgGeometry* geom)
+{
+    Ptr<MgPreparedGeometry> pGeom;
+
+    MG_GEOMETRY_TRY()
+
+    PreparedGeometryImpl* impl = new PreparedGeometryImpl();
+    impl->Prepare(geom);
+
+    pGeom = new MgPreparedGeometry(impl);
+
+    MG_GEOMETRY_CATCH_AND_THROW(L"MgPreparedGeometry.Create")
+
+    return SAFE_ADDREF((MgPreparedGeometry*)pGeom);
+}
+
+MgPreparedGeometry::MgPreparedGeometry(PreparedGeometryImpl* impl)
+    : d_ptr(impl)
+{
+
+}
+
+MgPreparedGeometry::~MgPreparedGeometry()
+{
+
+}
+
+bool MgPreparedGeometry::Contains(MgGeometry * other)
+{
+    std::auto_ptr<Geometry> gOther(d_ptr->Convert(other));
+    return d_ptr->m_pg->contains(gOther.get());
+}
+
+bool MgPreparedGeometry::Crosses(MgGeometry * other)
+{
+    std::auto_ptr<Geometry> gOther(d_ptr->Convert(other));
+    return d_ptr->m_pg->crosses(gOther.get());
+}
+
+bool MgPreparedGeometry::Disjoint(MgGeometry * other)
+{
+    std::auto_ptr<Geometry> gOther(d_ptr->Convert(other));
+    return d_ptr->m_pg->disjoint(gOther.get());
+}
+
+bool MgPreparedGeometry::Intersects(MgGeometry * other)
+{
+    std::auto_ptr<Geometry> gOther(d_ptr->Convert(other));
+    return d_ptr->m_pg->intersects(gOther.get());
+}
+
+bool MgPreparedGeometry::Overlaps(MgGeometry * other)
+{
+    std::auto_ptr<Geometry> gOther(d_ptr->Convert(other));
+    return d_ptr->m_pg->overlaps(gOther.get());
+}
+
+bool MgPreparedGeometry::Touches(MgGeometry * other)
+{
+    std::auto_ptr<Geometry> gOther(d_ptr->Convert(other));
+    return d_ptr->m_pg->touches(gOther.get());
+}
+
+bool MgPreparedGeometry::Within(MgGeometry * other)
+{
+    std::auto_ptr<Geometry> gOther(d_ptr->Convert(other));
+    return d_ptr->m_pg->within(gOther.get());
+}
+
+INT32 MgPreparedGeometry::GetClassId() 
+{ 
+    return m_cls_id; 
+}
+
+void MgPreparedGeometry::Dispose()
+{
+    delete this;
+}

Added: sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.h
===================================================================
--- sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.h	                        (rev 0)
+++ sandbox/jng/geoprocessing/Common/Geometry/PreparedGeometry.h	2017-06-10 16:13:00 UTC (rev 9207)
@@ -0,0 +1,303 @@
+//
+//  Copyright (C) 2004-2017 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+#ifndef _MGPREPAREDGEOMETRY_H_
+#define _MGPREPAREDGEOMETRY_H_
+
+class MgGeometry;
+
+////////////////////////////////////////////////////////////////
+/// \brief
+/// MgPreparedGeometry is an prepared form of MgGeometry optimized for the case of 
+/// repeated evaluation of spatial predicates against it and any other geometry
+///
+class MG_GEOMETRY_API MgPreparedGeometry : public MgGuardDisposable
+{
+PUBLISHED_API:
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// This is a convenience method. Given 2 geometries a and b,
+    /// a.Contains(b) is true if and only if b.MgGeometry::Within(a)
+    /// is true.
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual bool Contains(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual boolean Contains(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual bool Contains(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param other (MgGeometry)
+    /// The MgGeometry to test against this one.
+    ///
+    /// \return
+    /// True if the other geometry is within this geometry, false
+    /// otherwise.
+    ///
+    virtual bool Contains(MgGeometry* other);
+
+    //////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Given 2 geometries a and b, a.Crosses(b) is true if and only
+    /// if the dimension of the intersection of the interior of a and
+    /// the interior of b is less than the greater of the dimension
+    /// of the interior of a and the dimension of the interior of b
+    /// and the intersection of a and b is neither a nor b.
+    ///
+    /// \remarks
+    /// A Point cannot cross another geometry because the
+    /// intersection of the Point with the other geometry is the
+    /// Point.
+    /// \n
+    /// Two MultiPoint geometries cannot cross one another because
+    /// the dimension of the intersection of their interiors, namely
+    /// the 0-dimension, is not less than the greater of the
+    /// dimensions of their interiors, namely the 0-dimension.
+    /// \n
+    /// [\link OGC99049 OGC99-049 \endlink] implicitly excludes a Crosses
+    /// relationship between 2 polygons. According to the definition,
+    /// the possibility of such a relationship would require that the
+    /// intersection of the interior of one polygon with that of
+    /// another be a Point or Line.
+    /// <h2>Illustration</h2>
+    /// d, e and f are MultiPoint geometries.
+    /// \n
+    /// \image html crosses.png
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual bool Crosses(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual boolean Crosses(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual bool Crosses(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param other (MgGeometry)
+    /// The MgGeometry to test against this
+    /// one.
+    ///
+    /// \return
+    /// True if this geometry spatially crosses the other geometry,
+    /// false otherwise.
+    ///
+    virtual bool Crosses(MgGeometry* other);
+
+    /////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Given 2 geometries a and b, a.Disjoint(b)is true if and only
+    /// if the intersection of a and b is empty.
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual bool Disjoint(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual boolean Disjoint(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual bool Disjoint(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param other (MgGeometry)
+    /// The MgGeometry to test against this one.
+    ///
+    /// \return
+    /// True if this geometry is spatially disjoint from the other
+    /// geometry, false otherwise.
+    ///
+    virtual bool Disjoint(MgGeometry* other);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// This is a convenience method. Given 2 geometries a and b,
+    /// a.Intersects(b) is true if and only if a.\link MgGeometry::Disjoint Disjoint \endlink(b)
+    /// is false.
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual bool Intersects(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual boolean Intersects(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual bool Intersects(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param other (MgGeometry)
+    /// The MgGeometry to test against this one.
+    ///
+    /// \return
+    /// True if this geometry is not disjoint with respect to the
+    /// other geometry, false otherwise.
+    ///
+    virtual bool Intersects(MgGeometry* other);
+
+    /////////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Given 2 geometries a and b, a.Overlaps(b) is true if and only
+    /// if the dimension of the interior of a equals the dimension of
+    /// the interior of b equals the dimension of the intersection of
+    /// the interior of a and the interior of b and the intersection
+    /// of a and b is neither a nor b.
+    ///
+    /// \remarks
+    /// A Point cannot overlap a Point, and a MultiPoint cannot
+    /// overlap a Point but a MultiPoint can overlap a MultiPoint.
+    /// <h2>Illustration</h2>
+    /// c and d are MultiPoint geometries.
+    /// \image html overlaps.png
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual bool Overlaps(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual boolean Overlaps(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual bool Overlaps(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param other (MgGeometry)
+    /// The MgGeometry to test against this one.
+    ///
+    /// \return
+    /// True if this geometry spatially overlaps the other geometry,
+    /// false otherwise.
+    ///
+    virtual bool Overlaps(MgGeometry* other);
+
+    //////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Given 2 geometries a and b, a.Touches(b) is true if and only
+    /// if the intersection of the interior of a and the interior of
+    /// b is empty and the intersection of a and b is not empty.
+    ///
+    /// \remarks
+    /// A Point cannot touch a Point because a Point has no boundary
+    /// and so the intersection of the interiors of the two
+    /// geometries is not empty.
+    /// \n
+    /// A Point can touch a non-closed Line at one of the end points
+    /// of the Line.
+    /// \n
+    /// A Point cannot touch a closed Line because all of the points
+    /// in the Line are interior to it.
+    /// <h2>Illustration</h2>
+    /// e are MultiPoint geometries and f is a LineString.
+    /// \image html touches.png
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual bool Touches(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual boolean Touches(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual bool Touches(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param other (MgGeometry)
+    /// The MgGeometry to test against this
+    /// one.
+    ///
+    /// \return
+    /// True if this geometry spatially touches the other geometry,
+    /// false otherwise.
+    ///
+    virtual bool Touches(MgGeometry* other);
+
+    //////////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Given 2 geometries a and b, a.Within(b) is true if and only
+    /// if the intersection of a and b is a and the intersection of
+    /// the interior of a and the interior of b is not empty.
+    ///
+    /// \remarks
+    /// If the entire point-set of a geometry intersects the boundary
+    /// of another geometry, the former is not within the latter.
+    /// <h2>Illustration</h2>
+    /// The end point of d and the end point of e intersect. a, b, i,
+    /// j, k, and m are MultiPoints. The concentric circles represent
+    /// intersecting points. The polygon n1n2n3n4 is within the
+    /// polygon p1p2p3p4 and vice versa. The LineString q1q2 is
+    /// within the LineString r1r2 and vice versa. The MultiPoint j
+    /// is within the MultiPoint k and vice versa. The Point f is
+    /// within the point g and vice versa.
+    /// \n
+    /// \image html within.png
+    ///
+    /// <!-- Syntax in .Net, Java, and PHP -->
+    /// \htmlinclude DotNetSyntaxTop.html
+    /// virtual bool Within(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude JavaSyntaxTop.html
+    /// virtual boolean Within(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    /// \htmlinclude PHPSyntaxTop.html
+    /// virtual bool Within(MgGeometry other);
+    /// \htmlinclude SyntaxBottom.html
+    ///
+    /// \param other (MgGeometry)
+    /// The MgGeometry to test against this
+    /// one.
+    ///
+    /// \return
+    /// True if this geometry is spatially within the other geometry,
+    /// false otherwise.
+    ///
+    virtual bool Within(MgGeometry* other);
+
+INTERNAL_API:
+    static MgPreparedGeometry* Create(MgGeometry* geom);
+    //////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Get the unique identifier for the class
+    ///
+    /// \return
+    /// Class Identifider.
+    ///
+    virtual INT32 GetClassId();
+
+private:
+    class PreparedGeometryImpl;
+    std::auto_ptr<PreparedGeometryImpl> d_ptr;
+    MgPreparedGeometry(PreparedGeometryImpl* impl);
+
+protected:
+    virtual ~MgPreparedGeometry();
+
+    //////////////////////////////////////////////
+    /// \brief
+    /// Dispose this object.
+    ///
+    virtual void Dispose();
+
+CLASS_ID:
+    static const INT32 m_cls_id = Geometry_PreparedGeometry;
+};
+
+#endif

Modified: sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.cpp
===================================================================
--- sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.cpp	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.cpp	2017-06-10 16:13:00 UTC (rev 9207)
@@ -16,6 +16,7 @@
 //
 
 #include "MapGuideCommon.h"
+#include "ServiceManager.h"
 #include "TestGeometry.h"
 #include "CppUnitExtensions.h"
 #include "FoundationDefs.h"
@@ -41,11 +42,93 @@
 void TestGeometry::TestStart()
 {
     ACE_DEBUG((LM_INFO, ACE_TEXT("\nRunning Geometry tests. (Mentor)\n")));
+    try
+    {
+        MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+        if (serviceManager == 0)
+        {
+            throw new MgNullReferenceException(L"TestGeometry::TestStart", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+
+        Ptr<MgResourceService> pService = dynamic_cast<MgResourceService*>(serviceManager->RequestService(MgServiceType::ResourceService));
+        if (pService == 0)
+        {
+            throw new MgServiceNotAvailableException(L"TestGeometry::TestStart", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+
+        //Set the user information for the current thread to be administrator
+        MgUserInformation::SetCurrentUserInfo(NULL);
+        Ptr<MgUserInformation> userInfo = new MgUserInformation(L"Administrator", L"admin");
+        if (userInfo != NULL)
+        {
+            userInfo->SetLocale(TEST_LOCALE);
+            MgUserInformation::SetCurrentUserInfo(userInfo);
+
+            MgResourceIdentifier resourceIdentifier1(L"Library://UnitTests/Data/Sheboygan_Parcels.FeatureSource");
+    #ifdef _WIN32
+            STRING resourceContentFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.FeatureSource";
+            STRING dataFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.sdf";
+    #else
+            STRING resourceContentFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.FeatureSource";
+            STRING dataFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.sdf";
+    #endif
+
+            //Add a new resource
+            Ptr<MgByteSource> contentSource1 = new MgByteSource(resourceContentFileName1);
+            Ptr<MgByteReader> contentReader1 = contentSource1->GetReader();
+            pService->SetResource(&resourceIdentifier1, contentReader1, NULL);
+
+            //Set the resource data
+            Ptr<MgByteSource> dataSource1 = new MgByteSource(dataFileName1);
+            Ptr<MgByteReader> dataReader1 = dataSource1->GetReader();
+            pService->SetResourceData(&resourceIdentifier1, L"Sheboygan_Parcels.sdf", L"File", dataReader1);
+        }
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TEST_LOCALE);
+        SAFE_RELEASE(e);
+        CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
 }
 
 
 void TestGeometry::TestEnd()
 {
+    try
+    {
+        MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+        if (serviceManager == 0)
+        {
+            throw new MgNullReferenceException(L"TestFeatureService.TestEnd",
+                __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+
+        Ptr<MgResourceService> pService = dynamic_cast<MgResourceService*>(serviceManager->RequestService(MgServiceType::ResourceService));
+        if (pService == 0)
+        {
+            throw new MgServiceNotAvailableException(L"TestFeatureService.TestEnd",
+                __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+
+        // set user info
+        Ptr<MgUserInformation> userInfo = new MgUserInformation(L"Administrator", L"admin");
+        userInfo->SetLocale(TEST_LOCALE);
+        MgUserInformation::SetCurrentUserInfo(userInfo);
+
+        Ptr<MgResourceIdentifier> fsres1 = new MgResourceIdentifier(L"Library://UnitTests/Data/Sheboygan_Parcels.FeatureSource");
+        pService->DeleteResource(fsres1);
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TEST_LOCALE);
+        SAFE_RELEASE(e);
+        CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
+    catch (...)
+    {
+        throw;
+    }
     ACE_DEBUG((LM_INFO, ACE_TEXT("\nGeometry tests completed.\n\n")));
 }
 
@@ -1965,6 +2048,12 @@
         CPPUNIT_ASSERT(touches);
         touches = polygon->Touches(point);
         CPPUNIT_ASSERT(touches);
+        Ptr<MgPreparedGeometry> ppoint = point->Prepare();
+        Ptr<MgPreparedGeometry> ppolygon = polygon->Prepare();
+        touches = ppoint->Touches(polygon);
+        CPPUNIT_ASSERT(touches);
+        touches = ppolygon->Touches(point);
+        CPPUNIT_ASSERT(touches);
 
         //TEST 2
         MgGeometryFactory factory;
@@ -1974,6 +2063,12 @@
         CPPUNIT_ASSERT(!touches);
         touches = polygon->Touches(point);
         CPPUNIT_ASSERT(!touches);
+        ppoint = point->Prepare();
+        ppolygon = polygon->Prepare();
+        touches = ppoint->Touches(polygon);
+        CPPUNIT_ASSERT(!touches);
+        touches = ppolygon->Touches(point);
+        CPPUNIT_ASSERT(!touches);
 
         //TEST 3
         Ptr<MgMultiPolygon> multiPolygon = CreateMultiPolygon();
@@ -1982,6 +2077,12 @@
         CPPUNIT_ASSERT(!touches);
         touches = multiPolygon->Touches(point);
         CPPUNIT_ASSERT(!touches);
+        ppoint = point->Prepare();
+        Ptr<MgPreparedGeometry> pmultiPolygon = multiPolygon->Prepare();
+        touches = ppoint->Touches(multiPolygon);
+        CPPUNIT_ASSERT(!touches);
+        touches = pmultiPolygon->Touches(point);
+        CPPUNIT_ASSERT(!touches);
 
         //TEST 4
         Ptr<MgCoordinateCollection> collection = new MgCoordinateCollection();
@@ -2015,6 +2116,9 @@
         point = factory.CreatePoint(coord);
         touches = polygon->Touches(point);
         CPPUNIT_ASSERT(!touches);
+        ppolygon = polygon->Prepare();
+        touches = ppolygon->Touches(point);
+        CPPUNIT_ASSERT(!touches);
 
         //TEST 5
         coord1 = factory.CreateCoordinateXY( 45.0,  45.0);
@@ -2047,6 +2151,9 @@
         point = factory.CreatePoint(coord);
         touches = point->Touches(polygon);
         CPPUNIT_ASSERT(!touches);
+        ppoint = point->Prepare();
+        touches = ppoint->Touches(polygon);
+        CPPUNIT_ASSERT(!touches);
 
         //TEST 6
         coord1 = factory.CreateCoordinateXY(100.0, 100.0);
@@ -2713,4 +2820,141 @@
     {
         throw;
     }
+}
+
+void TestGeometry::TestCase_PreparedGeometryPerformance()
+{
+    try
+    {
+        MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+        if (serviceManager == 0)
+        {
+            throw new MgNullReferenceException(L"TestGeometry::TestCase_PreparedGeometry", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+
+        Ptr<MgFeatureService> pService = dynamic_cast<MgFeatureService*>(serviceManager->RequestService(MgServiceType::FeatureService));
+        if (pService == 0)
+        {
+            throw new MgServiceNotAvailableException(L"TestGeometry::TestCase_PreparedGeometry", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+
+        Ptr<MgAgfReaderWriter> agfRw = new MgAgfReaderWriter();
+        Ptr<MgWktReaderWriter> wktRw = new MgWktReaderWriter();
+
+        STRING wkt = L"POLYGON ((-87.70660400390625 43.761672246385025, -87.7045440673828 43.76576337413024, -87.7093505859375 43.77196151943297, -87.71999359130858 43.7755561494101, -87.73458480834961 43.770350063442635, -87.73355484008789 43.76315996157264, -87.73853302001953 43.758820688867885, -87.73595809936523 43.74642103270366, -87.72789001464842 43.735135112939105, -87.71844863891602 43.73191017368391, -87.71175384521484 43.73575527365514, -87.70797729492188 43.739228054975506, -87.7090072631836 43.74158447044938, -87.71278381347655 43.739848173302434, -87.71724700927734 43.737615717267765, -87.72720336914062 43.7374916894919, -87.72926330566406 43.74567697163676, -87.7313232421875 43.75125720420175, -87.73149490356445 43.75931662167704, -87.72222518920898 43.76092837491792, -87.72136688232422 43.76787081559809, -87.71467208862305 43.76712702120528, -87.71175384521484 43.76092837491792, -87.70660400390625 43.761672246385025))";
+        Ptr<MgGeometry> testGeom = wktRw->Read(wkt);
+
+        Ptr<MgResourceIdentifier> resource = new MgResourceIdentifier();
+        STRING className = L"";
+        Ptr<MgFeatureQueryOptions> options = new MgFeatureQueryOptions();
+        CPPUNIT_ASSERT_THROW_MG(pService->SelectFeatures(resource, className, options), MgInvalidArgumentException*);
+
+        resource = new MgResourceIdentifier(L"Library://UnitTests/Data/Sheboygan_Parcels.FeatureSource");
+        className = L"Parcels";
+        Ptr<MgFeatureReader> reader = pService->SelectFeatures(resource, className, options);
+        Ptr<MgClassDefinition> clsDef = reader->GetClassDefinition();
+        STRING geomName = clsDef->GetDefaultGeometryPropertyName();
+
+        INT32 containsCount1 = 0;
+        INT32 crossesCount1 = 0;
+        INT32 disjointCount1 = 0;
+        INT32 intersectsCount1 = 0;
+        INT32 overlapsCount1 = 0;
+        INT32 touchesCount1 = 0;
+        INT32 withinCount1 = 0;
+
+        long lStart = GetTickCount();
+        while (reader->ReadNext())
+        {
+            try 
+            {
+                Ptr<MgByteReader> agf = ((MgReader*)reader)->GetGeometry(geomName);
+                Ptr<MgGeometry> geom = agfRw->Read(agf);
+
+                if (testGeom->Contains(geom))
+                    containsCount1++;
+                if (testGeom->Crosses(geom))
+                    crossesCount1++;
+                if (testGeom->Disjoint(geom))
+                    disjointCount1++;
+                if (testGeom->Intersects(geom))
+                    intersectsCount1++;
+                if (testGeom->Overlaps(geom))
+                    overlapsCount1++;
+                if (testGeom->Touches(geom))
+                    touchesCount1++;
+                if (testGeom->Within(geom))
+                    withinCount1++;
+            }
+            catch (MgException* ex)
+            {
+                SAFE_RELEASE(ex);
+            }
+        }
+        reader->Close();
+        ACE_DEBUG((LM_INFO, ACE_TEXT("\n\n  Execution Time: = %6.4f (s)\n"), ((GetTickCount() - lStart) / 1000.0)));
+        ACE_DEBUG((LM_INFO, ACE_TEXT("  Contains: %d, Crosses: %d, Disjoint: %d, Intersects: %d, Overlaps: %d, Touches: %d, Within: %d\n"), containsCount1, crossesCount1, disjointCount1, intersectsCount1, overlapsCount1, touchesCount1, withinCount1));
+
+        //Now, with prepared geometry
+        Ptr<MgPreparedGeometry> prep = testGeom->Prepare();
+
+        reader = pService->SelectFeatures(resource, className, options);
+        INT32 containsCount2 = 0;
+        INT32 crossesCount2 = 0;
+        INT32 disjointCount2 = 0;
+        INT32 intersectsCount2 = 0;
+        INT32 overlapsCount2 = 0;
+        INT32 touchesCount2 = 0;
+        INT32 withinCount2 = 0;
+
+        lStart = GetTickCount();
+        while (reader->ReadNext())
+        {
+            try
+            {
+                Ptr<MgByteReader> agf = ((MgReader*)reader)->GetGeometry(geomName);
+                Ptr<MgGeometry> geom = agfRw->Read(agf);
+
+                if (prep->Contains(geom))
+                    containsCount2++;
+                if (prep->Crosses(geom))
+                    crossesCount2++;
+                if (prep->Disjoint(geom))
+                    disjointCount2++;
+                if (prep->Intersects(geom))
+                    intersectsCount2++;
+                if (prep->Overlaps(geom))
+                    overlapsCount2++;
+                if (prep->Touches(geom))
+                    touchesCount2++;
+                if (prep->Within(geom))
+                    withinCount2++;
+            }
+            catch (MgException* ex)
+            {
+                SAFE_RELEASE(ex);
+            }
+        }
+        reader->Close();
+        ACE_DEBUG((LM_INFO, ACE_TEXT("\n  Execution Time: = %6.4f (s)\n"), ((GetTickCount() - lStart) / 1000.0)));
+        ACE_DEBUG((LM_INFO, ACE_TEXT("  Contains: %d, Crosses: %d, Disjoint: %d, Intersects: %d, Overlaps: %d, Touches: %d, Within: %d\n"), containsCount2, crossesCount2, disjointCount2, intersectsCount2, overlapsCount2, touchesCount2, withinCount2));
+
+        CPPUNIT_ASSERT(containsCount1 == containsCount2);
+        CPPUNIT_ASSERT(crossesCount2 == crossesCount2);
+        CPPUNIT_ASSERT(disjointCount2 == disjointCount2);
+        CPPUNIT_ASSERT(intersectsCount2 == intersectsCount2);
+        CPPUNIT_ASSERT(overlapsCount2 == overlapsCount2);
+        CPPUNIT_ASSERT(touchesCount2 == touchesCount2);
+        CPPUNIT_ASSERT(withinCount2 == withinCount2);
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TEST_LOCALE);
+        SAFE_RELEASE(e);
+        CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
+    catch (...)
+    {
+        throw;
+    }
 }
\ No newline at end of file

Modified: sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.h
===================================================================
--- sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.h	2017-06-10 09:44:55 UTC (rev 9206)
+++ sandbox/jng/geoprocessing/Server/src/UnitTesting/TestGeometry.h	2017-06-10 16:13:00 UTC (rev 9207)
@@ -68,6 +68,8 @@
     CPPUNIT_TEST(TestCase_Simplify_DP);
     CPPUNIT_TEST(TestCase_Simplify_TP);
 
+    CPPUNIT_TEST(TestCase_PreparedGeometryPerformance);
+
     CPPUNIT_TEST(TestEnd); // This must be the very last unit test
     CPPUNIT_TEST_SUITE_END();
 
@@ -116,6 +118,8 @@
     void TestCase_Simplify_DP();
     void TestCase_Simplify_TP();
 
+    void TestCase_PreparedGeometryPerformance();
+
     MgPoint*             CreatePoint();
     MgLineString*        CreateLineString();
     MgLinearRing*        CreateLinearRing();



More information about the mapguide-commits mailing list