1 package org.opentrafficsim.road.network.lane;
2
3 import java.util.List;
4 import java.util.Map;
5 import java.util.NavigableMap;
6 import java.util.TreeMap;
7
8 import org.djunits.value.vdouble.scalar.Length;
9 import org.djunits.value.vdouble.scalar.Speed;
10 import org.djutils.draw.line.PolyLine2d;
11 import org.djutils.draw.line.Polygon2d;
12 import org.djutils.draw.point.Point2d;
13 import org.djutils.exceptions.Try;
14 import org.opentrafficsim.core.geometry.ContinuousLine;
15 import org.opentrafficsim.core.geometry.ContinuousStraight;
16 import org.opentrafficsim.core.geometry.FractionalLengthData;
17 import org.opentrafficsim.core.geometry.OtsLine2d;
18 import org.opentrafficsim.core.gtu.GtuType;
19 import org.opentrafficsim.road.network.lane.Stripe.Type;
20
21
22
23
24
25
26
27
28
29
30
31
32 public class LaneGeometryUtil
33 {
34
35
36
37
38 private LaneGeometryUtil()
39 {
40
41 }
42
43
44
45
46
47
48
49
50 public static List<CrossSectionSlice> getSlices(final ContinuousLine designLine, final Length offset, final Length width)
51 {
52 return List.of(new CrossSectionSlice(Length.ZERO, offset, width),
53 new CrossSectionSlice(Length.instantiateSI(designLine.getLength()), offset, width));
54 }
55
56
57
58
59
60
61
62
63
64
65 public static List<CrossSectionSlice> getSlices(final ContinuousLine designLine, final Length startOffset,
66 final Length endOffset, final Length startWidth, final Length endWidth)
67 {
68 return List.of(new CrossSectionSlice(Length.ZERO, startOffset, startWidth),
69 new CrossSectionSlice(Length.instantiateSI(designLine.getLength()), endOffset, endWidth));
70 }
71
72
73
74
75
76
77
78 public static FractionalLengthData getCenterOffsets(final ContinuousLine designLine,
79 final List<CrossSectionSlice> crossSectionSlices)
80 {
81 return getOffsets(crossSectionSlices, designLine.getLength(), 0.0);
82 }
83
84
85
86
87
88
89
90 public static FractionalLengthData getLeftEdgeOffsets(final ContinuousLine designLine,
91 final List<CrossSectionSlice> crossSectionSlices)
92 {
93 return getOffsets(crossSectionSlices, designLine.getLength(), 0.5);
94 }
95
96
97
98
99
100
101
102 public static FractionalLengthData getRightEdgeOffsets(final ContinuousLine designLine,
103 final List<CrossSectionSlice> crossSectionSlices)
104 {
105 return getOffsets(crossSectionSlices, designLine.getLength(), -0.5);
106 }
107
108
109
110
111
112
113
114
115 private static FractionalLengthData getOffsets(final List<CrossSectionSlice> crossSectionSlices, final double length,
116 final double widthFactor)
117 {
118 NavigableMap<Double, Double> map = new TreeMap<>();
119 crossSectionSlices.forEach((slice) -> map.put(slice.getRelativeLength().si / length,
120 slice.getOffset().si + widthFactor * slice.getWidth().si));
121 return new FractionalLengthData(map);
122 }
123
124
125
126
127
128
129
130 public static Polygon2d getContour(final PolyLine2d leftEdge, final PolyLine2d rightEdge)
131 {
132 Point2d[] points = new Point2d[leftEdge.size() + rightEdge.size() + 1];
133 System.arraycopy(leftEdge.getPointList().toArray(), 0, points, 0, leftEdge.size());
134 System.arraycopy(rightEdge.reverse().getPointList().toArray(), 0, points, leftEdge.size(), rightEdge.size());
135 points[points.length - 1] = points[0];
136 return new Polygon2d(true, points);
137 }
138
139
140
141
142
143
144
145
146
147
148
149 public static Lane createStraightLane(final CrossSectionLink link, final String id, final Length offset, final Length width,
150 final LaneType laneType, final Map<GtuType, Speed> speedLimits)
151 {
152 return createStraightLane(link, id, offset, offset, width, width, laneType, speedLimits);
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public static Lane createStraightLane(final CrossSectionLink link, final String id, final Length startOffset,
168 final Length endOffset, final Length startWidth, final Length endWidth, final LaneType laneType,
169 final Map<GtuType, Speed> speedLimits)
170 {
171 ContinuousLine designLine = new ContinuousStraight(
172 Try.assign(() -> link.getDesignLine().getLocationFraction(0.0), "Link should have a valid design line."),
173 link.getLength().si);
174 List<CrossSectionSlice> slices = getSlices(designLine, startOffset, endOffset, startWidth, endWidth);
175 return createStraightLane(link, id, slices, laneType, speedLimits);
176 }
177
178
179
180
181
182
183
184
185
186
187 public static Lane createStraightLane(final CrossSectionLink link, final String id, final List<CrossSectionSlice> slices,
188 final LaneType laneType, final Map<GtuType, Speed> speedLimits)
189 {
190 ContinuousLine designLine = new ContinuousStraight(
191 Try.assign(() -> link.getDesignLine().getLocationFraction(0.0), "Link should have a valid design line."),
192 link.getLength().si);
193 PolyLine2d centerLine = designLine.flattenOffset(getCenterOffsets(designLine, slices), null);
194 PolyLine2d leftEdge = designLine.flattenOffset(getLeftEdgeOffsets(designLine, slices), null);
195 PolyLine2d rightEdge = designLine.flattenOffset(getRightEdgeOffsets(designLine, slices), null);
196 Polygon2d contour = getContour(leftEdge, rightEdge);
197 return Try.assign(() -> new Lane(link, id, new OtsLine2d(centerLine), contour, slices, laneType, speedLimits),
198 "Network exception.");
199 }
200
201
202
203
204
205
206
207
208
209 public static Stripe createStraightStripe(final Type type, final CrossSectionLink link, final Length offset,
210 final Length width)
211 {
212 ContinuousLine designLine = new ContinuousStraight(
213 Try.assign(() -> link.getDesignLine().getLocationFraction(0.0), "Link should have a valid design line."),
214 link.getLength().si);
215 List<CrossSectionSlice> slices = getSlices(designLine, offset, width);
216 PolyLine2d centerLine = designLine.flattenOffset(getCenterOffsets(designLine, slices), null);
217 PolyLine2d leftEdge = designLine.flattenOffset(getLeftEdgeOffsets(designLine, slices), null);
218 PolyLine2d rightEdge = designLine.flattenOffset(getRightEdgeOffsets(designLine, slices), null);
219 Polygon2d contour = getContour(leftEdge, rightEdge);
220 return Try.assign(() -> new Stripe(type, link, new OtsLine2d(centerLine), contour, slices), "Network exception.");
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234 public static Object createStraightShoulder(final CrossSectionLink link, final String id, final Length startOffset,
235 final Length endOffset, final Length startWidth, final Length endWidth, final LaneType laneType)
236 {
237 ContinuousLine designLine = new ContinuousStraight(
238 Try.assign(() -> link.getDesignLine().getLocationFraction(0.0), "Link should have a valid design line."),
239 link.getLength().si);
240 List<CrossSectionSlice> slices = getSlices(designLine, startOffset, endOffset, startWidth, endWidth);
241 PolyLine2d centerLine = designLine.flattenOffset(getCenterOffsets(designLine, slices), null);
242 PolyLine2d leftEdge = designLine.flattenOffset(getLeftEdgeOffsets(designLine, slices), null);
243 PolyLine2d rightEdge = designLine.flattenOffset(getRightEdgeOffsets(designLine, slices), null);
244 Polygon2d contour = getContour(leftEdge, rightEdge);
245 return Try.assign(() -> new Shoulder(link, id, new OtsLine2d(centerLine), contour, slices, laneType),
246 "Network exception.");
247 }
248 }