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