1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 2011 Sandro Santilli <strk@keybit.net>
7 * Copyright (C) 2006 Refractions Research Inc.
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
14 **********************************************************************
16 * Last port: operation/linemerge/LineSequencer.java r378 (JTS-1.12)
18 **********************************************************************/
20 #include <geos/operation/linemerge/LineSequencer.h>
21 #include <geos/operation/linemerge/LineMergeEdge.h>
22 #include <geos/geom/Geometry.h>
23 #include <geos/geom/GeometryFactory.h>
24 #include <geos/geom/MultiLineString.h>
25 #include <geos/geom/Coordinate.h>
26 #include <geos/geom/CoordinateSequence.h>
27 #include <geos/geom/LineString.h>
28 #include <geos/planargraph/Node.h>
29 #include <geos/planargraph/DirectedEdge.h>
30 #include <geos/planargraph/Subgraph.h>
31 #include <geos/planargraph/algorithm/ConnectedSubgraphFinder.h>
32 #include <geos/util/Assert.h>
39 //using namespace geos::planargraph;
40 using namespace geos::geom
;
41 //using namespace geos::planargraph::algorithm;
44 #pragma warning(disable : 4127)
48 namespace operation
{ // geos.operation
49 namespace linemerge
{ // geos.operation.linemerge
53 LineSequencer::isSequenced(const Geometry
* geom
)
55 const MultiLineString
*mls
;
57 if ( 0 == (mls
=dynamic_cast<const MultiLineString
*>(geom
)) )
62 // the nodes in all subgraphs which have been completely scanned
63 Coordinate::ConstSet prevSubgraphNodes
;
64 Coordinate::ConstVect currNodes
;
66 const Coordinate
* lastNode
= NULL
;
68 for (unsigned int i
=0, n
=mls
->getNumGeometries(); i
<n
; ++i
)
70 const LineString
* lineptr
= \
71 dynamic_cast<const LineString
*>(mls
->getGeometryN(i
));
73 const LineString
& line
= *lineptr
;
76 const Coordinate
* startNode
= &(line
.getCoordinateN(0));
77 const Coordinate
* endNode
= &(line
.getCoordinateN(line
.getNumPoints() - 1));
80 * If this linestring is connected to a previous subgraph,
81 * geom is not sequenced
83 if (prevSubgraphNodes
.find(startNode
) != prevSubgraphNodes
.end())
87 if (prevSubgraphNodes
.find(endNode
) != prevSubgraphNodes
.end())
94 if (! startNode
->equals2D(*lastNode
))
96 // start new connected sequence
97 prevSubgraphNodes
.insert(currNodes
.begin(),
102 currNodes
.push_back(startNode
);
103 currNodes
.push_back(endNode
);
111 LineSequencer::hasSequence(planargraph::Subgraph
& graph
)
113 int oddDegreeCount
= 0;
114 for (planargraph::NodeMap::container::const_iterator
115 it
=graph
.nodeBegin(), endIt
=graph
.nodeEnd();
119 planargraph::Node
* node
= it
->second
;
120 if (node
->getDegree() % 2 == 1)
123 return oddDegreeCount
<= 2;
127 LineSequencer::delAll( LineSequencer::Sequences
& s
)
129 for ( Sequences::iterator i
=s
.begin(), e
=s
.end(); i
!=e
; ++i
)
136 LineSequencer::Sequences
*
137 LineSequencer::findSequences()
139 Sequences
*sequences
= new Sequences();
140 planargraph::algorithm::ConnectedSubgraphFinder
csFinder(graph
);
141 vector
<planargraph::Subgraph
*> subgraphs
;
142 csFinder
.getConnectedSubgraphs(subgraphs
);
143 for (vector
<planargraph::Subgraph
*>::const_iterator
144 it
=subgraphs
.begin(), endIt
=subgraphs
.end();
148 planargraph::Subgraph
* subgraph
= *it
;
149 if (hasSequence(*subgraph
)) {
150 planargraph::DirectedEdge::NonConstList
* seq
=findSequence(*subgraph
);
151 sequences
->push_back(seq
);
154 // if any subgraph cannot be sequenced, abort
167 LineSequencer::addLine(const LineString
*lineString
)
169 if (factory
== NULL
) {
170 factory
= lineString
->getFactory();
172 graph
.addEdge(lineString
);
178 LineSequencer::computeSequence()
183 Sequences
* sequences
= findSequences();
184 if (sequences
== NULL
) return;
186 sequencedGeometry
= auto_ptr
<Geometry
>(buildSequencedGeometry(*sequences
));
187 isSequenceableVar
= true;
192 // Lines were missing from result
193 assert(lineCount
== sequencedGeometry
->getNumGeometries());
195 // Result is not linear
196 assert(dynamic_cast<LineString
*>(sequencedGeometry
.get())
197 || dynamic_cast<MultiLineString
*>(sequencedGeometry
.get()));
202 LineSequencer::buildSequencedGeometry(const Sequences
& sequences
)
204 auto_ptr
<Geometry::NonConstVect
> lines(new Geometry::NonConstVect
);
206 for (Sequences::const_iterator
207 i1
=sequences
.begin(), i1End
=sequences
.end();
211 planargraph::DirectedEdge::NonConstList
& seq
= *(*i1
);
212 for(planargraph::DirectedEdge::NonConstList::iterator i2
=seq
.begin(),
213 i2End
=seq
.end(); i2
!= i2End
; ++i2
)
215 const planargraph::DirectedEdge
* de
= *i2
;
216 assert(dynamic_cast<LineMergeEdge
* >(de
->getEdge()));
217 LineMergeEdge
* e
= static_cast<LineMergeEdge
* >(de
->getEdge());
218 const LineString
* line
= e
->getLine();
220 // lineToAdd will be a *copy* of input things
221 LineString
* lineToAdd
;
223 if ( ! de
->getEdgeDirection() && ! line
->isClosed() ) {
224 lineToAdd
= reverse(line
);
226 Geometry
* lineClone
= line
->clone();
227 lineToAdd
= dynamic_cast<LineString
*>(lineClone
);
231 lines
->push_back(lineToAdd
);
235 if ( lines
->size() == 0 ) {
238 Geometry::NonConstVect
*l
=lines
.get();
240 return factory
->buildGeometry(l
);
246 LineSequencer::reverse(const LineString
*line
)
248 CoordinateSequence
* cs
=line
->getCoordinates();
249 CoordinateSequence::reverse(cs
);
250 return line
->getFactory()->createLineString(cs
);
254 const planargraph::Node
*
255 LineSequencer::findLowestDegreeNode(const planargraph::Subgraph
& graph
)
257 size_t minDegree
= numeric_limits
<size_t>::max();
258 const planargraph::Node
* minDegreeNode
= NULL
;
259 for (planargraph::NodeMap::container::const_iterator
260 it
= graph
.nodeBegin(), itEnd
= graph
.nodeEnd();
264 const planargraph::Node
* node
= (*it
).second
;
265 if (minDegreeNode
== NULL
|| node
->getDegree() < minDegree
)
267 minDegree
= node
->getDegree();
268 minDegreeNode
= node
;
271 return minDegreeNode
;
275 const planargraph::DirectedEdge
*
276 LineSequencer::findUnvisitedBestOrientedDE(const planargraph::Node
* node
)
278 using planargraph::DirectedEdge
;
279 using planargraph::DirectedEdgeStar
;
281 const DirectedEdge
* wellOrientedDE
= NULL
;
282 const DirectedEdge
* unvisitedDE
= NULL
;
283 const DirectedEdgeStar
* des
=node
->getOutEdges();
284 for (DirectedEdge::NonConstVect::const_iterator i
=des
->begin(),
289 planargraph::DirectedEdge
* de
= *i
;
290 if (! de
->getEdge()->isVisited()) {
292 if (de
->getEdgeDirection()) wellOrientedDE
= de
;
295 if (wellOrientedDE
!= NULL
)
296 return wellOrientedDE
;
303 LineSequencer::addReverseSubpath(const planargraph::DirectedEdge
*de
,
304 planargraph::DirectedEdge::NonConstList
& deList
,
305 planargraph::DirectedEdge::NonConstList::iterator lit
,
308 using planargraph::Node
;
309 using planargraph::DirectedEdge
;
311 // trace an unvisited path *backwards* from this de
312 Node
* endNode
= de
->getToNode();
314 Node
* fromNode
= NULL
;
316 deList
.insert(lit
, de
->getSym());
317 de
->getEdge()->setVisited(true);
318 fromNode
= de
->getFromNode();
319 const DirectedEdge
* unvisitedOutDE
= findUnvisitedBestOrientedDE(fromNode
);
321 // this must terminate, since we are continually marking edges as visited
322 if (unvisitedOutDE
== NULL
) break;
323 de
= unvisitedOutDE
->getSym();
325 if ( expectedClosed
) {
326 // the path should end at the toNode of this de,
327 // otherwise we have an error
328 util::Assert::isTrue(fromNode
== endNode
, "path not contiguos");
329 //assert(fromNode == endNode);
335 planargraph::DirectedEdge::NonConstList
*
336 LineSequencer::findSequence(planargraph::Subgraph
& graph
)
338 using planargraph::DirectedEdge
;
339 using planargraph::Node
;
340 using planargraph::GraphComponent
;
342 GraphComponent::setVisited(graph
.edgeBegin(),
343 graph
.edgeEnd(), false);
345 const Node
* startNode
= findLowestDegreeNode(graph
);
347 const DirectedEdge
*startDE
= *(startNode
->getOutEdges()->begin());
348 const DirectedEdge
*startDESym
= startDE
->getSym();
350 DirectedEdge::NonConstList
*seq
= new DirectedEdge::NonConstList();
352 DirectedEdge::NonConstList::iterator lit
=seq
->begin();
353 addReverseSubpath(startDESym
, *seq
, lit
, false);
356 while (lit
!= seq
->begin()) {
357 const DirectedEdge
* prev
= *(--lit
);
358 const DirectedEdge
* unvisitedOutDE
= findUnvisitedBestOrientedDE(prev
->getFromNode());
359 if (unvisitedOutDE
!= NULL
)
360 addReverseSubpath(unvisitedOutDE
->getSym(), *seq
, lit
, true);
363 // At this point, we have a valid sequence of graph DirectedEdges,
364 // but it is not necessarily appropriately oriented relative to
365 // the underlying geometry.
366 DirectedEdge::NonConstList
* orientedSeq
= orient(seq
);
368 if (orientedSeq
!= seq
) delete seq
;
374 planargraph::DirectedEdge::NonConstList
*
375 LineSequencer::orient(planargraph::DirectedEdge::NonConstList
* seq
)
377 using namespace geos::planargraph
;
379 const DirectedEdge
* startEdge
= seq
->front();
380 const DirectedEdge
* endEdge
= seq
->back();
381 Node
* startNode
= startEdge
->getFromNode();
382 Node
* endNode
= endEdge
->getToNode();
384 bool flipSeq
= false;
385 bool hasDegree1Node
= \
386 startNode
->getDegree() == 1 || endNode
->getDegree() == 1;
390 bool hasObviousStartNode
= false;
392 // test end edge before start edge, to make result stable
393 // (ie. if both are good starts, pick the actual start
394 if (endEdge
->getToNode()->getDegree() == 1 &&
395 endEdge
->getEdgeDirection() == false)
397 hasObviousStartNode
= true;
400 if (startEdge
->getFromNode()->getDegree() == 1 &&
401 startEdge
->getEdgeDirection() == true)
403 hasObviousStartNode
= true;
407 // since there is no obvious start node,
408 // use any node of degree 1
409 if (! hasObviousStartNode
)
411 // check if the start node should actually
413 if (startEdge
->getFromNode()->getDegree() == 1)
415 // if the end node is of degree 1, it is
416 // properly the end node
422 // if there is no degree 1 node, just use the sequence as is
423 // (Could insert heuristic of taking direction of majority of
424 // lines as overall direction)
428 return reverse(*seq
);
434 planargraph::DirectedEdge::NonConstList
*
435 LineSequencer::reverse(planargraph::DirectedEdge::NonConstList
& seq
)
437 using namespace geos::planargraph
;
439 DirectedEdge::NonConstList
* newSeq
= new DirectedEdge::NonConstList();
440 DirectedEdge::NonConstList::iterator it
=seq
.begin(), itEnd
=seq
.end();
441 for (; it
!=itEnd
; ++it
) {
442 const DirectedEdge
*de
= *it
;
443 newSeq
->push_front(de
->getSym());
450 } // namespace geos.operation.linemerge
451 } // namespace geos.operation
454 /**********************************************************************
456 * Revision 1.10 2006/06/12 10:49:43 strk
457 * unsigned int => size_t
459 * Revision 1.9 2006/03/24 11:04:44 strk
460 * Changed assert() with Assert::isTrue in addReverseSubpath
462 * Revision 1.8 2006/03/24 10:44:07 strk
463 * Fixed to build with -DNDEBUG
465 * Revision 1.7 2006/03/22 10:13:54 strk
466 * opLinemerge.h split
468 * Revision 1.6 2006/03/21 21:42:54 strk
469 * planargraph.h header split, planargraph:: classes renamed to match JTS symbols
471 **********************************************************************/