[geos-commits] r2240 - in trunk: . source/geom source/geom/util source/headers/geos/geom source/headers/geos/geom/util source/headers/geos/index/strtree source/headers/geos/noding source/headers/geos/operation source/headers/geos/operation/union source/index/strtree source/operation source/operation/union tests/unit tests/unit/operation tests/unit/operation/union

svn_geos at osgeo.org svn_geos at osgeo.org
Sun Jan 18 15:34:09 EST 2009


Author: pramsey
Date: 2009-01-18 15:34:09 -0500 (Sun, 18 Jan 2009)
New Revision: 2240

Added:
   trunk/source/geom/util/GeometryCombiner.cpp
   trunk/source/headers/geos/geom/util/GeometryCombiner.h
   trunk/source/headers/geos/operation/union/
   trunk/source/headers/geos/operation/union/CascadedPolygonUnion.h
   trunk/source/headers/geos/operation/union/Makefile.am
   trunk/source/operation/union/
   trunk/source/operation/union/CascadedPolygonUnion.cpp
   trunk/source/operation/union/Makefile.am
   trunk/tests/unit/operation/union/
   trunk/tests/unit/operation/union/CascadedPolygonUnionTest.cpp
Modified:
   trunk/configure.in
   trunk/source/geom/Envelope.cpp
   trunk/source/geom/util/Makefile.am
   trunk/source/headers/geos/geom/Envelope.h
   trunk/source/headers/geos/index/strtree/AbstractSTRtree.h
   trunk/source/headers/geos/noding/MCIndexNoder.h
   trunk/source/headers/geos/operation/Makefile.am
   trunk/source/index/strtree/AbstractSTRtree.cpp
   trunk/source/operation/Makefile.am
   trunk/tests/unit/
   trunk/tests/unit/Makefile.am
Log:
Apply cascaded union patch, for issue #225


Modified: trunk/configure.in
===================================================================
--- trunk/configure.in	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/configure.in	2009-01-18 20:34:09 UTC (rev 2240)
@@ -282,6 +282,7 @@
 	source/headers/geos/operation/polygonize/Makefile
 	source/headers/geos/operation/predicate/Makefile
 	source/headers/geos/operation/relate/Makefile
+	source/headers/geos/operation/union/Makefile
 	source/headers/geos/operation/valid/Makefile
 	source/headers/geos/planargraph/Makefile
 	source/headers/geos/planargraph/algorithm/Makefile
@@ -307,6 +308,7 @@
 	source/operation/polygonize/Makefile
 	source/operation/predicate/Makefile
 	source/operation/relate/Makefile
+	source/operation/union/Makefile
 	source/operation/valid/Makefile
 	source/planargraph/Makefile
 	source/precision/Makefile

Modified: trunk/source/geom/Envelope.cpp
===================================================================
--- trunk/source/geom/Envelope.cpp	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/geom/Envelope.cpp	2009-01-18 20:34:09 UTC (rev 2240)
@@ -425,7 +425,7 @@
 
 /*public*/
 bool
-Envelope::intersection(const Envelope& env, Envelope& result)
+Envelope::intersection(const Envelope& env, Envelope& result) const
 {
 	if (isNull() || env.isNull() || ! intersects(env)) return false;
 

Added: trunk/source/geom/util/GeometryCombiner.cpp
===================================================================
--- trunk/source/geom/util/GeometryCombiner.cpp	                        (rev 0)
+++ trunk/source/geom/util/GeometryCombiner.cpp	2009-01-18 20:34:09 UTC (rev 2240)
@@ -0,0 +1,102 @@
+/**********************************************************************
+ * $Id$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2006 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/geom/util/GeometryCombiner.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/GeometryCollection.h>
+
+namespace geos {
+namespace geom { // geos.geom
+namespace util { // geos.geom.util
+
+Geometry* GeometryCombiner::combine(std::vector<Geometry*> const& geoms)
+{
+    GeometryCombiner combiner(geoms);
+    return combiner.combine();
+}
+
+Geometry* GeometryCombiner::combine(Geometry* g0, Geometry* g1)
+{
+    std::vector<Geometry*> geoms;
+    geoms.push_back(g0);
+    geoms.push_back(g1);
+
+    GeometryCombiner combiner(geoms);
+    return combiner.combine();
+}
+
+Geometry* GeometryCombiner::combine(Geometry* g0, Geometry* g1, Geometry* g2)
+{
+    std::vector<Geometry*> geoms;
+    geoms.push_back(g0);
+    geoms.push_back(g1);
+    geoms.push_back(g2);
+
+    GeometryCombiner combiner(geoms);
+    return combiner.combine();
+}
+
+GeometryCombiner::GeometryCombiner(std::vector<Geometry*> const& geoms)
+  : geomFactory(extractFactory(geoms)), skipEmpty(false), inputGeoms(geoms)
+{
+}
+
+GeometryFactory const* 
+GeometryCombiner::extractFactory(std::vector<Geometry*> const& geoms) 
+{
+    return geoms.empty() ? NULL : geoms.front()->getFactory();
+}
+
+Geometry* GeometryCombiner::combine()
+{
+    std::vector<Geometry*> elems;
+
+    std::vector<Geometry*>::const_iterator end = inputGeoms.end();
+    for (std::vector<Geometry*>::const_iterator i = inputGeoms.begin(); 
+         i != end; ++i) 
+    {
+        extractElements(*i, elems);
+    }
+
+    if (elems.empty()) {
+        if (geomFactory != NULL) {
+            return geomFactory->createGeometryCollection(NULL);
+        }
+        return NULL;
+    }
+
+    // return the "simplest possible" geometry
+    return geomFactory->buildGeometry(elems);
+}
+
+void 
+GeometryCombiner::extractElements(Geometry* geom, std::vector<Geometry*>& elems)
+{
+    if (geom == NULL)
+        return;
+
+    for (std::size_t i = 0; i < geom->getNumGeometries(); ++i) {
+        Geometry* elemGeom = const_cast<Geometry*>(geom->getGeometryN(i));
+        if (skipEmpty && elemGeom->isEmpty())
+            continue;
+        elems.push_back(elemGeom);
+    }
+}
+
+} // namespace geos.geom.util
+} // namespace geos.geom
+} // namespace geos
+

Modified: trunk/source/geom/util/Makefile.am
===================================================================
--- trunk/source/geom/util/Makefile.am	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/geom/util/Makefile.am	2009-01-18 20:34:09 UTC (rev 2240)
@@ -13,7 +13,8 @@
 	CoordinateOperation.cpp \
 	GeometryEditor.cpp \
 	GeometryTransformer.cpp \
-	ShortCircuitedGeometryVisitor.cpp
+	ShortCircuitedGeometryVisitor.cpp \
+        GeometryCombiner.cpp
 
 #	LinearComponentExtracter.cpp 
 #	PointExtracter.cpp 

Modified: trunk/source/headers/geos/geom/Envelope.h
===================================================================
--- trunk/source/headers/geos/geom/Envelope.h	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/headers/geos/geom/Envelope.h	2009-01-18 20:34:09 UTC (rev 2240)
@@ -235,7 +235,7 @@
 	 *               if either argument is null, or they do not intersect)
 	 * @return false if not intersection is found
 	 */
-	bool intersection(const Envelope& env, Envelope& result);
+	bool intersection(const Envelope& env, Envelope& result) const;
 
 	/** \brief
 	 * Translates this envelope by given amounts in the X and Y direction.

Added: trunk/source/headers/geos/geom/util/GeometryCombiner.h
===================================================================
--- trunk/source/headers/geos/geom/util/GeometryCombiner.h	                        (rev 0)
+++ trunk/source/headers/geos/geom/util/GeometryCombiner.h	2009-01-18 20:34:09 UTC (rev 2240)
@@ -0,0 +1,115 @@
+/**********************************************************************
+ * $Id$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2006 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+#ifndef GEOS_GEOM_UTIL_GEOMETRYCOMBINER_H
+#define GEOS_GEOM_UTIL_GEOMETRYCOMBINER_H
+
+#include <vector>
+
+// Forward declarations
+namespace geos {
+    namespace geom {
+        class Geometry;
+        class GeometryFactory;
+    }
+}
+
+namespace geos {
+namespace geom { // geos.geom
+namespace util { // geos.geom.util
+
+/**
+ * Combines {@link Geometry}s
+ * to produce a {@link GeometryCollection} of the most appropriate type.
+ * Input geometries which are already collections
+ * will have their elements extracted first.
+ * No validation of the result geometry is performed.
+ * (The only case where invalidity is possible is where {@link Polygonal} geometries
+ * are combined and result in a self-intersection).
+ * 
+ * @see GeometryFactory#buildGeometry
+ */
+class GeometryCombiner 
+{
+public:
+    /**
+     * Combines a collection of geometries.
+     * 
+     * @param geoms the geometries to combine
+     * @return the combined geometry
+     */
+    static Geometry* combine(std::vector<Geometry*> const& geoms);
+
+    /**
+     * Combines two geometries.
+     * 
+     * @param g0 a geometry to combine
+     * @param g1 a geometry to combine
+     * @return the combined geometry
+     */
+    static Geometry* combine(Geometry* g0, Geometry* g1);
+
+    /**
+     * Combines three geometries.
+     * 
+     * @param g0 a geometry to combine
+     * @param g1 a geometry to combine
+     * @param g2 a geometry to combine
+     * @return the combined geometry
+     */
+    static Geometry* combine(Geometry* g0, Geometry* g1, Geometry* g2);
+
+private:
+    GeometryFactory const* geomFactory;
+    bool skipEmpty;
+    std::vector<Geometry*> const& inputGeoms;
+
+public:
+    /**
+     * Creates a new combiner for a collection of geometries
+     * 
+     * @param geoms the geometries to combine
+     */
+    GeometryCombiner(std::vector<Geometry*> const& geoms);
+
+    /**
+     * Extracts the GeometryFactory used by the geometries in a collection
+     * 
+     * @param geoms
+     * @return a GeometryFactory
+     */
+    static GeometryFactory const* extractFactory(std::vector<Geometry*> const& geoms);
+
+    /**
+     * Computes the combination of the input geometries
+     * to produce the most appropriate {@link Geometry} or {@link GeometryCollection}
+     * 
+     * @return a Geometry which is the combination of the inputs
+     */
+    Geometry* combine();
+
+private:
+    void extractElements(Geometry* geom, std::vector<Geometry*>& elems);
+};
+
+} // namespace geos.geom.util
+} // namespace geos.geom
+} // namespace geos
+
+#endif
+
+/**********************************************************************
+ * $Log$
+ *
+ **********************************************************************/

Modified: trunk/source/headers/geos/index/strtree/AbstractSTRtree.h
===================================================================
--- trunk/source/headers/geos/index/strtree/AbstractSTRtree.h	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/headers/geos/index/strtree/AbstractSTRtree.h	2009-01-18 20:34:09 UTC (rev 2240)
@@ -22,6 +22,7 @@
 #include <list>
 #include <memory> // for auto_ptr
 #include <cassert> // for inlines
+#include <algorithm>
 
 // Forward declarations
 namespace geos {
@@ -42,6 +43,79 @@
 typedef std::vector<Boundable*> BoundableList;
 //typedef std::list<Boundable*> BoundableList;
 
+/// list contains boundables or lists of boundables. The lists are owned by 
+/// this class, the plain boundables are held by reference only.
+class ItemsList;
+
+class ItemsListItem
+{
+public:
+    enum type {
+        item_is_geometry,
+        item_is_list
+    };
+
+    ItemsListItem(void *item_)
+      : t(item_is_geometry)
+    {
+        item.g = item_;
+    }
+    ItemsListItem(ItemsList* item_)
+      : t(item_is_list)
+    {
+        item.l = item_;
+    }
+
+    type get_type() const { return t; }
+
+    void* get_geometry() const
+    {
+        assert(t == item_is_geometry);
+        return item.g;
+    }
+    ItemsList* get_itemslist() const
+    {
+        assert(t == item_is_list);
+        return item.l;
+    }
+
+    type t;
+    union {
+        void* g;
+        ItemsList* l;
+    } item;
+};
+
+class ItemsList : public std::vector<ItemsListItem>
+{
+private:
+    typedef std::vector<ItemsListItem> base_type;
+
+    static void delete_item(ItemsListItem& item)
+    {
+        if (ItemsListItem::item_is_list == item.t)
+            delete reinterpret_cast<ItemsList*>(item.item.l);
+    }
+
+public:
+    ~ItemsList()
+    {
+        std::for_each(begin(), end(), &ItemsList::delete_item);
+    }
+
+    // lists of items need to be deleted in the end
+    void push_back(void* item)
+    {
+        this->base_type::push_back(ItemsListItem(item));
+    }
+
+    // lists of items need to be deleted in the end
+    void push_back_owned(ItemsList* itemList)
+    {
+        this->base_type::push_back(ItemsListItem(itemList));
+    }
+};
+
 /** \brief
  * Base class for STRtree and SIRtree.
  *
@@ -79,6 +153,8 @@
 	bool remove(const void* searchBounds, AbstractNode& node, void* item);
 	bool removeItem(AbstractNode& node, void* item);
 
+    ItemsList* itemsTree(AbstractNode* node);
+
 protected:
 
 	/** \brief
@@ -207,6 +283,22 @@
 	 */
 	virtual void boundablesAtLevel(int level, AbstractNode* top,
 			BoundableList* boundables);
+
+    /**
+     * Gets a tree structure (as a nested list) 
+     * corresponding to the structure of the items and nodes in this tree.
+     * <p>
+     * The returned {@link List}s contain either {@link Object} items, 
+     * or Lists which correspond to subtrees of the tree
+     * Subtrees which do not contain any items are not included.
+     * <p>
+     * Builds the tree if necessary.
+     * 
+     * @note The caller is responsible for releasing the list
+     *
+     * @return a List of items and/or Lists
+     */
+    ItemsList* itemsTree();
 };
 
 

Modified: trunk/source/headers/geos/noding/MCIndexNoder.h
===================================================================
--- trunk/source/headers/geos/noding/MCIndexNoder.h	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/headers/geos/noding/MCIndexNoder.h	2009-01-18 20:34:09 UTC (rev 2240)
@@ -100,6 +100,7 @@
 		void overlap(geom::LineSegment* s1, geom::LineSegment* s2)
         {
             UNREFERENCED_PARAMETER(s1);
+            UNREFERENCED_PARAMETER(s2);
             assert(0);
         }
 	};

Modified: trunk/source/headers/geos/operation/Makefile.am
===================================================================
--- trunk/source/headers/geos/operation/Makefile.am	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/headers/geos/operation/Makefile.am	2009-01-18 20:34:09 UTC (rev 2240)
@@ -1,12 +1,13 @@
 SUBDIRS = \
 	buffer \
+	distance \
+	linemerge \
 	overlay \
-	valid \
+	polygonize \
+	predicate \
 	relate \
-	predicate \
-	distance \
-	linemerge \
-	polygonize
+	union \
+	valid 
 
 #EXTRA_DIST = 
 


Property changes on: trunk/source/headers/geos/operation/union
___________________________________________________________________
Name: svn:ignore
   + Makefile.in
Makefile


Added: trunk/source/headers/geos/operation/union/CascadedPolygonUnion.h
===================================================================
--- trunk/source/headers/geos/operation/union/CascadedPolygonUnion.h	                        (rev 0)
+++ trunk/source/headers/geos/operation/union/CascadedPolygonUnion.h	2009-01-18 20:34:09 UTC (rev 2240)
@@ -0,0 +1,226 @@
+/**********************************************************************
+ * $Id$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2006 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+#ifndef GEOS_OP_UNION_CASCADEDPOLYGONUNION_H
+#define GEOS_OP_UNION_CASCADEDPOLYGONUNION_H
+
+#include <vector>
+#include <algorithm>
+
+// Forward declarations
+namespace geos {
+    namespace geom {
+        class GeometryFactory;
+        class Geometry;
+        class Polygon;
+        class Envelope;
+    }
+    namespace index {
+        namespace strtree {
+            class ItemsList;
+        }
+    }
+}
+
+namespace geos {
+namespace operation { // geos::operation
+namespace geounion {  // geos::operation::geounion
+
+/**
+ * \brief Helper class holding Geometries, part of which are held by reference
+ *        others are held exclusively.
+ */
+class GeometryListHolder : public std::vector<geom::Geometry*>
+{
+private:
+    typedef std::vector<geom::Geometry*> base_type;
+
+public:
+    GeometryListHolder() {}
+    ~GeometryListHolder()
+    {
+        std::for_each(ownedItems.begin(), ownedItems.end(), 
+            &GeometryListHolder::deleteItem);
+    }
+
+    // items need to be deleted in the end
+    void push_back_owned(geom::Geometry* item)
+    {
+        this->base_type::push_back(item);
+        ownedItems.push_back(item);
+    }
+
+    geom::Geometry* getGeometry(std::size_t index)
+    {
+      if (index >= this->base_type::size()) 
+          return NULL;
+      return (*this)[index];
+    }
+
+private:
+    static void deleteItem(geom::Geometry* item);
+
+private:
+    std::vector<geom::Geometry*> ownedItems;
+};
+
+/**
+ * \brief 
+ * Provides an efficient method of unioning a collection of 
+ * {@link Polygonal} geometries.
+ * This algorithm is faster and likely more robust than
+ * the simple iterated approach of 
+ * repeatedly unioning each polygon to a result geometry.
+ * <p>
+ * The <tt>buffer(0)</tt> trick is sometimes faster, but can be less robust and 
+ * can sometimes take an exceptionally long time to complete.
+ * This is particularly the case where there is a high degree of overlap
+ * between the polygons.  In this case, <tt>buffer(0)</tt> is forced to compute
+ * with <i>all</i> line segments from the outset, 
+ * whereas cascading can eliminate many segments
+ * at each stage of processing.
+ * The best case for buffer(0) is the trivial case
+ * where there is <i>no</i> overlap between the input geometries. 
+ * However, this case is likely rare in practice.
+ */
+class CascadedPolygonUnion 
+{
+private:
+    std::vector<geom::Polygon*>* inputPolys;
+    geom::GeometryFactory const* geomFactory;
+
+    /**
+     * The effectiveness of the index is somewhat sensitive
+     * to the node capacity.  
+     * Testing indicates that a smaller capacity is better.
+     * For an STRtree, 4 is probably a good number (since
+     * this produces 2x2 "squares").
+     */
+    static int const STRTREE_NODE_CAPACITY = 4;
+
+public:
+    CascadedPolygonUnion();
+
+    /**
+     * Computes the union of
+     * a collection of {@link Polygonal} {@link Geometry}s.
+     * 
+     * @param polys a collection of {@link Polygonal} {@link Geometry}s
+     */
+    static geom::Geometry* Union(std::vector<geom::Polygon*>* polys);
+
+    /**
+     * Creates a new instance to union
+     * the given collection of {@link Geometry}s.
+     * 
+     * @param geoms a collection of {@link Polygonal} {@link Geometry}s
+     */
+    CascadedPolygonUnion(std::vector<geom::Polygon*>* polys)
+      : inputPolys(polys),
+        geomFactory(NULL)
+    {}
+
+    /**
+     * Computes the union of the input geometries.
+     * 
+     * @return the union of the input geometries
+     * @return null if no input geometries were provided
+     */
+    geom::Geometry* Union();
+
+private:
+    geom::Geometry* unionTree(index::strtree::ItemsList* geomTree);
+
+    /**
+     * Unions a list of geometries 
+     * by treating the list as a flattened binary tree,
+     * and performing a cascaded union on the tree.
+     */
+    geom::Geometry* binaryUnion(GeometryListHolder* geoms);
+
+    /**
+     * Unions a section of a list using a recursive binary union on each half
+     * of the section.
+     * 
+     * @param geoms
+     * @param start
+     * @param end
+     * @return the union of the list section
+     */
+    geom::Geometry* binaryUnion(GeometryListHolder* geoms, std::size_t start, 
+        std::size_t end);
+
+    /**
+     * Reduces a tree of geometries to a list of geometries
+     * by recursively unioning the subtrees in the list.
+     * 
+     * @param geomTree a tree-structured list of geometries
+     * @return a list of Geometrys
+     */
+    GeometryListHolder* reduceToGeometries(index::strtree::ItemsList* geomTree);
+
+    /**
+     * Computes the union of two geometries, 
+     * either of both of which may be null.
+     * 
+     * @param g0 a Geometry
+     * @param g1 a Geometry
+     * @return the union of the input(s)
+     * @return null if both inputs are null
+     */
+    geom::Geometry* unionSafe(geom::Geometry* g0, geom::Geometry* g1);
+
+    geom::Geometry* unionOptimized(geom::Geometry* g0, geom::Geometry* g1);
+
+    /**
+     * Unions two polygonal geometries.
+     * The case of MultiPolygons is optimized to union only 
+     * the polygons which lie in the intersection of the two geometry's envelopes.
+     * Polygons outside this region can simply be combined with the union result,
+     * which is potentially much faster.
+     * This case is likely to occur often during cascaded union, and may also
+     * occur in real world data (such as unioning data for parcels on different street blocks).
+     * 
+     * @param g0 a polygonal geometry
+     * @param g1 a polygonal geometry
+     * @param common the intersection of the envelopes of the inputs
+     * @return the union of the inputs
+     */
+    geom::Geometry* unionUsingEnvelopeIntersection(geom::Geometry* g0, 
+        geom::Geometry* g1, geom::Envelope const& common);
+
+    geom::Geometry* extractByEnvelope(geom::Envelope const& env, 
+        geom::Geometry* geom, std::vector<geom::Geometry*>& disjointGeoms);
+
+    /**
+     * Encapsulates the actual unioning of two polygonal geometries.
+     * 
+     * @param g0
+     * @param g1
+     * @return
+     */
+    static geom::Geometry* unionActual(geom::Geometry* g0, geom::Geometry* g1);
+};
+
+} // namespace geos::operation::union
+} // namespace geos::operation
+} // namespace geos
+
+#endif
+
+/**********************************************************************
+ * $Log$
+ *
+ **********************************************************************/
+

Added: trunk/source/headers/geos/operation/union/Makefile.am
===================================================================
--- trunk/source/headers/geos/operation/union/Makefile.am	                        (rev 0)
+++ trunk/source/headers/geos/operation/union/Makefile.am	2009-01-18 20:34:09 UTC (rev 2240)
@@ -0,0 +1,8 @@
+#SUBDIRS = 
+
+#EXTRA_DIST = 
+
+geosdir = $(includedir)/geos/operation/union
+
+geos_HEADERS = \
+	CascadedPolygonUnion.h

Modified: trunk/source/index/strtree/AbstractSTRtree.cpp
===================================================================
--- trunk/source/index/strtree/AbstractSTRtree.cpp	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/index/strtree/AbstractSTRtree.cpp	2009-01-18 20:34:09 UTC (rev 2240)
@@ -334,6 +334,49 @@
 	return;
 }
 
+ItemsList* AbstractSTRtree::itemsTree(AbstractNode* node) 
+{
+    std::auto_ptr<ItemsList> valuesTreeForNode (new ItemsList());
+
+    BoundableList::iterator end = node->getChildBoundables()->end();
+    for (BoundableList::iterator i = node->getChildBoundables()->begin(); 
+         i != end; ++i) 
+    {
+        Boundable* childBoundable = *i;
+        if (dynamic_cast<AbstractNode*>(childBoundable)) {
+            ItemsList* valuesTreeForChild = 
+                itemsTree(static_cast<AbstractNode*>(childBoundable));
+            // only add if not null (which indicates an item somewhere in this tree
+            if (valuesTreeForChild != NULL)
+                valuesTreeForNode->push_back_owned(valuesTreeForChild);
+        }
+        else if (dynamic_cast<ItemBoundable*>(childBoundable)) {
+            valuesTreeForNode->push_back(
+                static_cast<ItemBoundable*>(childBoundable)->getItem());
+        }
+        else {
+            assert(!"should never be reached");
+        }
+    }
+    if (valuesTreeForNode->empty()) 
+        return NULL;
+
+    return valuesTreeForNode.release();
+}
+
+ItemsList* AbstractSTRtree::itemsTree()
+{
+    if (!built) { 
+        build(); 
+    }
+
+    ItemsList* valuesTree (itemsTree(root));
+    if (valuesTree == NULL)
+        return new ItemsList();
+
+    return valuesTree;
+}
+
 } // namespace geos.index.strtree
 } // namespace geos.index
 } // namespace geos

Modified: trunk/source/operation/Makefile.am
===================================================================
--- trunk/source/operation/Makefile.am	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/source/operation/Makefile.am	2009-01-18 20:34:09 UTC (rev 2240)
@@ -6,6 +6,7 @@
 	polygonize	\
 	predicate	\
 	relate		\
+        union           \
 	valid		
 
 noinst_LTLIBRARIES = liboperation.la
@@ -22,8 +23,9 @@
 	linemerge/liboplinemerge.la	\
 	overlay/libopoverlay.la		\
 	polygonize/liboppolygonize.la	\
+	predicate/liboppredicate.la	\
 	relate/liboprelate.la		\
-	predicate/liboppredicate.la	\
+        union/libopunion.la             \
 	valid/libopvalid.la
 
 


Property changes on: trunk/source/operation/union
___________________________________________________________________
Name: svn:ignore
   + .libs
.deps
Makefile
Makefile.in


Added: trunk/source/operation/union/CascadedPolygonUnion.cpp
===================================================================
--- trunk/source/operation/union/CascadedPolygonUnion.cpp	                        (rev 0)
+++ trunk/source/operation/union/CascadedPolygonUnion.cpp	2009-01-18 20:34:09 UTC (rev 2240)
@@ -0,0 +1,196 @@
+/**********************************************************************
+ * $Id$
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.refractions.net
+ *
+ * Copyright (C) 2006 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation. 
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/operation/union/CascadedPolygonUnion.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/util/GeometryCombiner.h>
+#include <geos/index/strtree/STRtree.h>
+
+namespace geos {
+namespace operation { // geos.operation
+namespace geounion {  // geos.operation.geounion
+
+///////////////////////////////////////////////////////////////////////////////
+void GeometryListHolder::deleteItem(geom::Geometry* item)
+{
+    delete item;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+geom::Geometry* CascadedPolygonUnion::Union(std::vector<geom::Polygon*>* polys)
+{
+    CascadedPolygonUnion op (polys);
+    return op.Union();
+}
+
+geom::Geometry* CascadedPolygonUnion::Union()
+{
+    if (inputPolys->empty())
+        return NULL;
+
+    geomFactory = inputPolys->front()->getFactory();
+
+    /**
+     * A spatial index to organize the collection
+     * into groups of close geometries.
+     * This makes unioning more efficient, since vertices are more likely 
+     * to be eliminated on each round.
+     */
+    index::strtree::STRtree index(STRTREE_NODE_CAPACITY);
+
+    typedef std::vector<geom::Polygon*>::iterator iterator_type;
+    iterator_type end = inputPolys->end();
+    for (iterator_type i = inputPolys->begin(); i != end; ++i) {
+        index.insert((*i)->getEnvelopeInternal(), *i);
+    }
+
+    std::auto_ptr<index::strtree::ItemsList> itemTree (index.itemsTree());
+
+    return unionTree(itemTree.get());
+}
+
+geom::Geometry* CascadedPolygonUnion::unionTree(
+    index::strtree::ItemsList* geomTree)
+{
+    /**
+     * Recursively unions all subtrees in the list into single geometries.
+     * The result is a list of Geometry's only
+     */
+    std::auto_ptr<GeometryListHolder> geoms(reduceToGeometries(geomTree));
+    return binaryUnion(geoms.get());
+}
+
+geom::Geometry* CascadedPolygonUnion::binaryUnion(GeometryListHolder* geoms)
+{
+    return binaryUnion(geoms, 0, geoms->size());
+}
+
+geom::Geometry* CascadedPolygonUnion::binaryUnion(GeometryListHolder* geoms, 
+    std::size_t start, std::size_t end)
+{
+    if (end - start <= 1) {
+        return unionSafe(geoms->getGeometry(start), NULL);
+    }
+    else if (end - start == 2) {
+        return unionSafe(geoms->getGeometry(start), geoms->getGeometry(start + 1));
+    }
+    else {
+        // recurse on both halves of the list
+        std::size_t mid = (end + start) / 2;
+        std::auto_ptr<geom::Geometry> g0 (binaryUnion(geoms, start, mid));
+        std::auto_ptr<geom::Geometry> g1 (binaryUnion(geoms, mid, end));
+        return unionSafe(g0.get(), g1.get());
+    }
+}
+
+GeometryListHolder* 
+CascadedPolygonUnion::reduceToGeometries(index::strtree::ItemsList* geomTree)
+{
+    std::auto_ptr<GeometryListHolder> geoms (new GeometryListHolder());
+
+    typedef index::strtree::ItemsList::iterator iterator_type;
+    iterator_type end = geomTree->end();
+    for (iterator_type i = geomTree->begin(); i != end; ++i) {
+        if ((*i).get_type() == index::strtree::ItemsListItem::item_is_list) {
+            std::auto_ptr<geom::Geometry> geom (unionTree((*i).get_itemslist()));
+            geoms->push_back_owned(geom.get());
+            geom.release();
+        }
+        else if ((*i).get_type() == index::strtree::ItemsListItem::item_is_geometry) {
+            geoms->push_back(reinterpret_cast<geom::Geometry*>((*i).get_geometry()));
+        }
+        else {
+            assert(!"should never be reached");
+        }
+    }
+
+    return geoms.release();
+}
+
+geom::Geometry* 
+CascadedPolygonUnion::unionSafe(geom::Geometry* g0, geom::Geometry* g1)
+{
+    if (g0 == NULL && g1 == NULL)
+        return NULL;
+
+    if (g0 == NULL)
+        return g1->clone();
+    if (g1 == NULL)
+        return g0->clone();
+
+    return unionOptimized(g0, g1);
+}
+
+geom::Geometry* 
+CascadedPolygonUnion::unionOptimized(geom::Geometry* g0, geom::Geometry* g1)
+{
+    geom::Envelope const* g0Env = g0->getEnvelopeInternal();
+    geom::Envelope const* g1Env = g1->getEnvelopeInternal();
+
+    if (!g0Env->intersects(g1Env))
+        return geom::util::GeometryCombiner::combine(g0, g1);
+
+    if (g0->getNumGeometries() <= 1 && g1->getNumGeometries() <= 1)
+        return unionActual(g0, g1);
+
+    geom::Envelope commonEnv; 
+    g0Env->intersection(*g1Env, commonEnv);
+    return unionUsingEnvelopeIntersection(g0, g1, commonEnv);
+}
+
+geom::Geometry* 
+CascadedPolygonUnion::unionUsingEnvelopeIntersection(geom::Geometry* g0, 
+    geom::Geometry* g1, geom::Envelope const& common)
+{
+    std::vector<geom::Geometry*> disjointPolys;
+
+    std::auto_ptr<geom::Geometry> g0Int(extractByEnvelope(common, g0, disjointPolys));
+    std::auto_ptr<geom::Geometry> g1Int(extractByEnvelope(common, g1, disjointPolys));
+
+    std::auto_ptr<geom::Geometry> u(unionActual(g0Int.get(), g1Int.get()));
+    disjointPolys.push_back(u.get());
+
+    return geom::util::GeometryCombiner::combine(disjointPolys);
+}
+
+geom::Geometry* 
+CascadedPolygonUnion::extractByEnvelope(geom::Envelope const& env, 
+    geom::Geometry* geom, std::vector<geom::Geometry*>& disjointGeoms)
+{
+    std::vector<geom::Geometry*> intersectingGeoms;
+
+    for (std::size_t i = 0; i < geom->getNumGeometries(); i++) { 
+        geom::Geometry* elem = const_cast<geom::Geometry*>(geom->getGeometryN(i));
+        if (elem->getEnvelopeInternal()->intersects(env))
+            intersectingGeoms.push_back(elem);
+        else
+            disjointGeoms.push_back(elem);
+    }
+
+    return geomFactory->buildGeometry(intersectingGeoms);
+}
+
+geom::Geometry* 
+CascadedPolygonUnion::unionActual(geom::Geometry* g0, geom::Geometry* g1)
+{
+    return g0->Union(g1);
+}
+
+} // namespace geos.operation.union
+} // namespace geos.operation
+} // namespace geos
+

Added: trunk/source/operation/union/Makefile.am
===================================================================
--- trunk/source/operation/union/Makefile.am	                        (rev 0)
+++ trunk/source/operation/union/Makefile.am	2009-01-18 20:34:09 UTC (rev 2240)
@@ -0,0 +1,12 @@
+SUBDIRS = 
+
+noinst_LTLIBRARIES = libopunion.la
+
+INCLUDES = -I$(top_srcdir)/source/headers 
+
+libopunion_la_SOURCES = \
+	CascadedPolygonUnion.cpp 
+
+libopunion_la_LIBADD = 
+
+


Property changes on: trunk/tests/unit
___________________________________________________________________
Name: svn:ignore
   - .deps
.libs
.gdb*
Makefile
Makefile.in
geos_unit

   + geostest
threadtest
badthreadtest
.deps
.libs
.gdb*
Makefile
Makefile.in
geos_unit


Modified: trunk/tests/unit/Makefile.am
===================================================================
--- trunk/tests/unit/Makefile.am	2009-01-15 01:00:54 UTC (rev 2239)
+++ trunk/tests/unit/Makefile.am	2009-01-18 20:34:09 UTC (rev 2240)
@@ -21,43 +21,44 @@
 
 
 geos_unit_SOURCES = geos_unit.cpp \
-	algorithm/ConvexHullTest.cpp \
-	algorithm/PointLocatorTest.cpp \
 	algorithm/CGAlgorithms/isCCWTest.cpp \
 	algorithm/CGAlgorithms/isPointInRingTest.cpp \
 	algorithm/CGAlgorithms/computeOrientationTest.cpp \
+	algorithm/ConvexHullTest.cpp \
+	algorithm/PointLocatorTest.cpp \
 	geom/CoordinateArraySequenceFactoryTest.cpp \
 	geom/CoordinateArraySequenceTest.cpp \
 	geom/CoordinateListTest.cpp \
 	geom/CoordinateTest.cpp \
 	geom/DimensionTest.cpp \
 	geom/EnvelopeTest.cpp \
+	geom/Geometry/isRectangleTest.cpp \
+	geom/Geometry/coversTest.cpp \
 	geom/GeometryFactoryTest.cpp \
 	geom/IntersectionMatrixTest.cpp \
+	geom/LinearRingTest.cpp \
 	geom/LineSegmentTest.cpp \
 	geom/LineStringTest.cpp \
-	geom/LinearRingTest.cpp \
 	geom/LocationTest.cpp \
 	geom/MultiLineStringTest.cpp \
 	geom/MultiPointTest.cpp \
 	geom/MultiPolygonTest.cpp \
 	geom/PointTest.cpp \
 	geom/PolygonTest.cpp \
+	geom/prep/PreparedGeometryFactoryTest.cpp \
 	geom/TriangleTest.cpp \
-	geom/Geometry/isRectangleTest.cpp \
-	geom/Geometry/coversTest.cpp \
-	geom/prep/PreparedGeometryFactoryTest.cpp \
 	index/quadtree/DoubleBitsTest.cpp \
+	io/ByteOrderValuesTest.cpp \
 	io/WKBReaderTest.cpp \
-	io/ByteOrderValuesTest.cpp \
+	noding/SegmentNodeTest.cpp \
+	noding/SegmentPointComparatorTest.cpp \
+	noding/SegmentStringTest.cpp \
+	operation/distance/DistanceOpTest.cpp \
 	operation/IsSimpleOpTest.cpp \
-	operation/distance/DistanceOpTest.cpp \
 	operation/overlay/FuzzyPointLocatorTest.cpp \
 	operation/overlay/OffsetPointGeneratorTest.cpp \
 	operation/overlay/OverlayResultValidatorTest.cpp \
-	noding/SegmentNodeTest.cpp \
-	noding/SegmentPointComparatorTest.cpp \
-	noding/SegmentStringTest.cpp \
+	operation/union/CascadedPolygonUnionTest.cpp \
 	precision/GeometrySnapperTest.cpp \
 	precision/LineStringSnapperTest.cpp \
 	precision/SimpleGeometryPrecisionReducerTest.cpp \

Added: trunk/tests/unit/operation/union/CascadedPolygonUnionTest.cpp
===================================================================
--- trunk/tests/unit/operation/union/CascadedPolygonUnionTest.cpp	                        (rev 0)
+++ trunk/tests/unit/operation/union/CascadedPolygonUnionTest.cpp	2009-01-18 20:34:09 UTC (rev 2240)
@@ -0,0 +1,161 @@
+// $Id$
+// 
+// Test Suite for geos::operation::geounion::CascadedPolygonUnion class.
+
+// TUT
+#include <tut.h>
+// GEOS
+#include <geos/operation/union/CascadedPolygonUnion.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/Polygon.h>
+#include <geos/geom/Point.h>
+#include <geos/io/WKTReader.h>
+#include <geos/io/WKTWriter.h>
+
+namespace tut
+{
+    //
+    // Test Group
+    //
+
+    // Common data used by tests
+    struct test_unaryuniontest_data
+    {
+        geos::geom::GeometryFactory gf;
+        geos::io::WKTReader wktreader;
+        geos::io::WKTWriter wktwriter;
+
+        typedef geos::geom::Geometry::AutoPtr GeomPtr;
+
+        test_unaryuniontest_data()
+          : gf(),
+            wktreader(&gf)
+        {}
+    };
+
+    typedef test_group<test_unaryuniontest_data> group;
+    typedef group::object object;
+
+    group test_unaryuniontest_group("geos::operation::geounion::CascadedPolygonUnion");
+
+    // test runner
+    geos::geom::Geometry* unionIterated(
+        std::vector<geos::geom::Polygon*>* geoms)
+    {
+        typedef std::vector<geos::geom::Polygon*>::iterator iterator;
+
+        std::auto_ptr<geos::geom::Geometry> unionAll;
+        iterator end = geoms->end();
+        for (iterator i = geoms->begin(); i != end; ++i) {
+            if (!unionAll.get()) {
+                unionAll.reset((*i)->clone());
+            }
+            else {
+                unionAll.reset(unionAll->Union(*i));
+            }
+        }
+        return unionAll.release();
+    }
+
+    geos::geom::Geometry* unionCascaded(
+        std::vector<geos::geom::Polygon*>* geoms)
+    {
+        using geos::operation::geounion::CascadedPolygonUnion;
+        return CascadedPolygonUnion::Union(geoms);
+    }
+
+    void test_runner(test_unaryuniontest_data& t,
+        std::vector<geos::geom::Polygon*>* geoms) 
+    {
+        std::auto_ptr<geos::geom::Geometry> union1(unionIterated(geoms));
+        std::auto_ptr<geos::geom::Geometry> union2(unionCascaded(geoms));
+
+        // For now we compare the WKT representations of the two generated 
+        // geometries which works well for simple geometries only.
+        // More complex geometries require to use special similarity measure
+        // criteria instead.
+        std::string iterated(t.wktwriter.writeFormatted(union1.get()));
+        std::string cascaded(t.wktwriter.writeFormatted(union2.get()));
+
+        ensure_equals(iterated, cascaded);
+    }
+
+    void delete_geometry(geos::geom::Geometry* g)
+    {
+        delete g;
+    }
+
+    //
+    // Test Cases
+    //
+
+    template<>
+    template<>
+    void object::test<1>()
+    {
+        static char const* const polygons[] = 
+        {
+            "POLYGON ((80 260, 200 260, 200 30, 80 30, 80 260))",
+            "POLYGON ((30 180, 300 180, 300 110, 30 110, 30 180))",
+            "POLYGON ((30 280, 30 150, 140 150, 140 280, 30 280))",
+            NULL
+        };
+
+        std::vector<geos::geom::Polygon*> g;
+        for (char const* const* p = polygons; *p != NULL; ++p)
+        {
+            std::string wkt(*p);
+            geos::geom::Polygon* geom = 
+                static_cast<geos::geom::Polygon*>(wktreader.read(wkt));
+            g.push_back(geom);
+        }
+
+        test_runner(*this, &g); 
+
+        for_each(g.begin(), g.end(), delete_geometry);
+    }
+
+    void create_discs(geos::geom::GeometryFactory& gf, int num, double radius, 
+        std::vector<geos::geom::Polygon*>* g)
+    {
+        for (int i = 0; i < num; ++i) {
+            for (int j = 0; j < num; ++j) {
+                std::auto_ptr<geos::geom::Point> pt(
+                    gf.createPoint(geos::geom::Coordinate(i, j)));
+                g->push_back(static_cast<geos::geom::Polygon*>(pt->buffer(radius)));
+            }
+        }
+    }
+
+// these tests currently fail because the geometries generated by the different 
+// union algoritms are slightly different. In order to make those tests pass 
+// we need to port the similarity measure classes from JTS, allowing to 
+// approximately compare the two results 
+
+//     template<>
+//     template<>
+//     void object::test<2>()
+//     {
+//         std::vector<geos::geom::Polygon*> g;
+//         create_discs(gf, 5, 0.7, &g);
+// 
+//         test_runner(*this, &g); 
+// 
+//         std::for_each(g.begin(), g.end(), delete_geometry);
+//     }
+
+//     template<>
+//     template<>
+//     void object::test<3>()
+//     {
+//         std::vector<geos::geom::Polygon*> g;
+//         create_discs(gf, 5, 0.55, &g);
+// 
+//         test_runner(*this, &g); 
+// 
+//         std::for_each(g.begin(), g.end(), delete_geometry);
+//     }
+
+} // namespace tut
+



More information about the geos-commits mailing list