[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