1 /**********************************************************************
2  *
3  * GEOS - Geometry Engine Open Source
4  * http://geos.osgeo.org
5  *
6  * Copyright (C) 2005-2006 Refractions Research Inc.
7  * Copyright (C) 2001-2002 Vivid Solutions Inc.
8  *
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.
13  *
14  **********************************************************************
15  *
16  * Last port: linearref/ExtractLineByLocation.java rev. 1.35
17  *
18  **********************************************************************/
19 
20 #include <geos/geom/Coordinate.h>
21 #include <geos/geom/CoordinateArraySequence.h>
22 #include <geos/geom/MultiLineString.h>
23 #include <geos/geom/LineString.h>
24 #include <geos/linearref/ExtractLineByLocation.h>
25 #include <geos/linearref/LinearIterator.h>
26 #include <geos/linearref/LinearLocation.h>
27 #include <geos/linearref/LengthLocationMap.h>
28 #include <geos/linearref/LengthIndexOfPoint.h>
29 #include <geos/linearref/LinearGeometryBuilder.h>
30 
31 #include <cassert>
32 #include <typeinfo>
33 
34 using namespace std;
35 
36 using namespace geos::geom;
37 
38 namespace geos {
39 namespace linearref { // geos.linearref
40 
41 
42 std::unique_ptr<Geometry>
extract(const Geometry * line,const LinearLocation & start,const LinearLocation & end)43 ExtractLineByLocation::extract(const Geometry* line, const LinearLocation& start, const LinearLocation& end)
44 {
45     ExtractLineByLocation ls(line);
46     return ls.extract(start, end);
47 }
48 
ExtractLineByLocation(const Geometry * p_line)49 ExtractLineByLocation::ExtractLineByLocation(const Geometry* p_line) :
50     line(p_line) {}
51 
52 
53 std::unique_ptr<Geometry>
extract(const LinearLocation & start,const LinearLocation & end)54 ExtractLineByLocation::extract(const LinearLocation& start, const LinearLocation& end)
55 {
56     if(end.compareTo(start) < 0) {
57         auto backwards = computeLinear(end, start);
58         auto forwards = reverse(backwards.get());
59         return forwards;
60     }
61     return computeLinear(start, end);
62 }
63 
64 std::unique_ptr<Geometry>
reverse(const Geometry * linear)65 ExtractLineByLocation::reverse(const Geometry* linear)
66 {
67     const LineString* ls = dynamic_cast<const LineString*>(linear);
68     if(ls) {
69         return ls->reverse();
70     }
71     else {
72         const MultiLineString* mls = dynamic_cast<const MultiLineString*>(linear);
73         if(mls) {
74             return mls->reverse();
75         }
76         else {
77             assert(!static_cast<bool>("non-linear geometry encountered"));
78             return nullptr;
79         }
80     }
81 }
82 
83 std::unique_ptr<LineString>
computeLine(const LinearLocation & start,const LinearLocation & end)84 ExtractLineByLocation::computeLine(const LinearLocation& start, const LinearLocation& end)
85 {
86     auto coordinates = line->getCoordinates();
87     CoordinateArraySequence newCoordinateArray;
88 
89     const size_t indexStep = 1;
90     auto startSegmentIndex = start.getSegmentIndex();
91 
92     if(start.getSegmentFraction() > 0.0) {
93         startSegmentIndex += indexStep;
94     }
95 
96     auto lastSegmentIndex = end.getSegmentIndex();
97     if(end.getSegmentFraction() == 1.0) {
98         lastSegmentIndex += indexStep;
99     }
100 
101     if(lastSegmentIndex >= coordinates->size()) {
102         assert(!coordinates->isEmpty());
103         lastSegmentIndex = coordinates->size() - indexStep;
104     }
105 
106     if(! start.isVertex()) {
107         newCoordinateArray.add(start.getCoordinate(line));
108     }
109 
110     for(auto i = startSegmentIndex; i <= lastSegmentIndex; i++) {
111         newCoordinateArray.add((*coordinates)[i]);
112     }
113 
114     if(! end.isVertex()) {
115         newCoordinateArray.add(end.getCoordinate(line));
116     }
117 
118     // ensure there is at least one coordinate in the result
119     if(newCoordinateArray.isEmpty()) {
120         newCoordinateArray.add(start.getCoordinate(line));
121     }
122 
123     /*
124      * Ensure there is enough coordinates to build a valid line.
125      * Make a 2-point line with duplicate coordinates, if necessary.
126      * There will always be at least one coordinate in the coordList.
127      */
128     if(newCoordinateArray.size() <= 1) {
129         newCoordinateArray.add(newCoordinateArray[0]);
130     }
131 
132     return std::unique_ptr<LineString>(line->getFactory()->createLineString(newCoordinateArray));
133 }
134 
135 std::unique_ptr<Geometry>
computeLinear(const LinearLocation & start,const LinearLocation & end)136 ExtractLineByLocation::computeLinear(const LinearLocation& start, const LinearLocation& end)
137 {
138     LinearGeometryBuilder builder(line->getFactory());
139     builder.setFixInvalidLines(true);
140 
141     if(! start.isVertex()) {
142         builder.add(start.getCoordinate(line));
143     }
144 
145     for(LinearIterator it(line, start); it.hasNext(); it.next()) {
146         if(end.compareLocationValues(it.getComponentIndex(), it.getVertexIndex(), 0.0) < 0) {
147             break;
148         }
149         Coordinate pt = it.getSegmentStart();
150         builder.add(pt);
151         if(it.isEndOfLine()) {
152             builder.endLine();
153         }
154     }
155     if(! end.isVertex()) {
156         builder.add(end.getCoordinate(line));
157     }
158     return std::unique_ptr<Geometry>(builder.getGeometry());
159 }
160 }
161 }
162