Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/elements/moving/GNEMoveElementLaneSingle.cpp
185790 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file GNEMoveElementLaneSingle.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Sep 2025
17
///
18
// Class used for elements that can be moved over a lane with only one position
19
/****************************************************************************/
20
#include <config.h>
21
22
#include <netedit/changes/GNEChange_Attribute.h>
23
#include <netedit/frames/common/GNEMoveFrame.h>
24
#include <netedit/GNENet.h>
25
#include <netedit/GNEUndoList.h>
26
#include <netedit/GNEViewParent.h>
27
28
#include "GNEMoveElementLaneSingle.h"
29
30
// ===========================================================================
31
// static members
32
// ===========================================================================
33
34
const std::string GNEMoveElementLaneSingle::PositionType::SINGLE = "single";
35
const std::string GNEMoveElementLaneSingle::PositionType::STARPOS = TL("lane start");
36
const std::string GNEMoveElementLaneSingle::PositionType::ENDPOS = TL("lane end");
37
38
// ===========================================================================
39
// Method definitions
40
// ===========================================================================
41
42
GNEMoveElementLaneSingle::GNEMoveElementLaneSingle(GNEAttributeCarrier* element,
43
SumoXMLAttr posAttr, double& position, bool& friendlyPos, const std::string& defaultBehavior) :
44
GNEMoveElement(element),
45
myPosAttr(posAttr),
46
myPosOverLane(position),
47
myFriendlyPos(friendlyPos),
48
myPositionType(defaultBehavior) {
49
}
50
51
52
GNEMoveElementLaneSingle::~GNEMoveElementLaneSingle() {}
53
54
55
GNEMoveOperation*
56
GNEMoveElementLaneSingle::getMoveOperation() {
57
// check if allow change lane is enabled
58
const bool allowChangeLane = myMovedElement->getNet()->getViewParent()->getMoveFrame()->getCommonMoveOptions()->getAllowChangeLane();
59
// continue depending if we're moving the start or the end position
60
if (myPositionType == PositionType::ENDPOS) {
61
return new GNEMoveOperation(this, myMovedElement->getHierarchicalElement()->getParentLanes().front(), INVALID_DOUBLE,
62
myMovedElement->getHierarchicalElement()->getParentLanes().back(), myPosOverLane, false, allowChangeLane);
63
} else {
64
return new GNEMoveOperation(this, myMovedElement->getHierarchicalElement()->getParentLanes().front(), myPosOverLane,
65
myMovedElement->getHierarchicalElement()->getParentLanes().back(), INVALID_DOUBLE, true, allowChangeLane);
66
}
67
}
68
69
70
std::string
71
GNEMoveElementLaneSingle::getMovingAttribute(SumoXMLAttr key) const {
72
if (key == myPosAttr) {
73
if ((myPosOverLane == INVALID_DOUBLE) && (myPositionType != PositionType::SINGLE)) {
74
return myPositionType;
75
} else {
76
return toString(myPosOverLane);
77
}
78
} else {
79
switch (key) {
80
case SUMO_ATTR_LANE:
81
return myMovedElement->getHierarchicalElement()->getParentLanes().front()->getID();
82
case SUMO_ATTR_FRIENDLY_POS:
83
return toString(myFriendlyPos);
84
default:
85
return myMovedElement->getCommonAttribute(key);
86
}
87
}
88
}
89
90
91
double
92
GNEMoveElementLaneSingle::getMovingAttributeDouble(SumoXMLAttr key) const {
93
if (key == myPosAttr) {
94
return myPosOverLane;
95
} else {
96
return myMovedElement->getCommonAttributeDouble(key);
97
}
98
}
99
100
101
Position
102
GNEMoveElementLaneSingle::getMovingAttributePosition(SumoXMLAttr key) const {
103
return myMovedElement->getCommonAttributePosition(key);
104
}
105
106
107
PositionVector
108
GNEMoveElementLaneSingle::getMovingAttributePositionVector(SumoXMLAttr key) const {
109
return myMovedElement->getCommonAttributePositionVector(key);
110
}
111
112
113
void
114
GNEMoveElementLaneSingle::setMovingAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
115
if (key == myPosAttr) {
116
GNEChange_Attribute::changeAttribute(myMovedElement, key, value, undoList);
117
} else {
118
switch (key) {
119
case SUMO_ATTR_LANE:
120
case SUMO_ATTR_FRIENDLY_POS:
121
GNEChange_Attribute::changeAttribute(myMovedElement, key, value, undoList);
122
break;
123
default:
124
myMovedElement->setCommonAttribute(key, value, undoList);
125
break;
126
}
127
}
128
}
129
130
131
bool
132
GNEMoveElementLaneSingle::isMovingAttributeValid(SumoXMLAttr key, const std::string& value) const {
133
if (key == myPosAttr) {
134
if ((myPositionType != PositionType::SINGLE) && (value.empty() || (value == myPositionType))) {
135
return true;
136
} else {
137
return GNEAttributeCarrier::canParse<double>(value);
138
}
139
} else {
140
switch (key) {
141
case SUMO_ATTR_LANE:
142
if (myMovedElement->getNet()->getAttributeCarriers()->retrieveLane(value, false) != nullptr) {
143
return true;
144
} else {
145
return false;
146
}
147
case SUMO_ATTR_FRIENDLY_POS:
148
return GNEAttributeCarrier::canParse<bool>(value);
149
default:
150
return myMovedElement->isCommonAttributeValid(key, value);
151
}
152
}
153
}
154
155
156
void
157
GNEMoveElementLaneSingle::setMovingAttribute(SumoXMLAttr key, const std::string& value) {
158
if (key == myPosAttr) {
159
if (value.empty()) {
160
myPosOverLane = INVALID_DOUBLE;
161
} else if ((value == PositionType::STARPOS) && (myPositionType == PositionType::STARPOS)) {
162
myPosOverLane = INVALID_DOUBLE;
163
} else if ((value == PositionType::ENDPOS) && (myPositionType == PositionType::ENDPOS)) {
164
myPosOverLane = INVALID_DOUBLE;
165
} else {
166
myPosOverLane = GNEAttributeCarrier::parse<double>(value);
167
}
168
} else {
169
switch (key) {
170
case SUMO_ATTR_FRIENDLY_POS:
171
myFriendlyPos = GNEAttributeCarrier::parse<bool>(value);
172
break;
173
default:
174
myMovedElement->setCommonAttribute(key, value);
175
break;
176
}
177
}
178
}
179
180
181
void
182
GNEMoveElementLaneSingle::removeGeometryPoint(const Position /*clickedPosition*/, GNEUndoList* /*undoList*/) {
183
// nothing to do here
184
}
185
186
187
bool
188
GNEMoveElementLaneSingle::isMoveElementValid() const {
189
// obtain lane final length
190
const double laneLenght = (myPositionType == PositionType::ENDPOS) ? myMovedElement->getHierarchicalElement()->getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength() :
191
myMovedElement->getHierarchicalElement()->getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();
192
// adjust position (negative means start counting from backward)
193
const double adjustedPosition = (myPosOverLane == INVALID_DOUBLE) ? 0 : (myPosOverLane < 0) ? (myPosOverLane + laneLenght) : myPosOverLane;
194
// check conditions
195
if (myFriendlyPos) {
196
return true;
197
} else if (adjustedPosition < 0) {
198
return false;
199
} else if (adjustedPosition > laneLenght) {
200
return false;
201
} else if ((myPositionType == PositionType::STARPOS) && (adjustedPosition > (laneLenght - POSITION_EPS))) {
202
return false;
203
} else {
204
return true;
205
}
206
}
207
208
209
std::string
210
GNEMoveElementLaneSingle::getMovingProblem() const {
211
// obtain lane final length
212
const double laneLenght = (myPositionType == PositionType::ENDPOS) ? myMovedElement->getHierarchicalElement()->getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength() :
213
myMovedElement->getHierarchicalElement()->getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();
214
// adjust position (negative means start counting from backward)
215
const double adjustedPosition = (myPosOverLane == INVALID_DOUBLE) ? 0 : (myPosOverLane < 0) ? (myPosOverLane + laneLenght) : myPosOverLane;
216
// check conditions
217
if (myFriendlyPos) {
218
return "";
219
} else if (adjustedPosition < 0) {
220
return TLF("% < 0", toString(myPosAttr));
221
} else if (adjustedPosition > laneLenght) {
222
return TLF("% > length of lane", toString(myPosAttr));
223
} else if ((myPositionType == PositionType::STARPOS) && (adjustedPosition > (laneLenght - POSITION_EPS))) {
224
return TLF("% > (length of lane - EPS)", toString(myPosAttr));
225
} else {
226
return "";
227
}
228
}
229
230
231
void
232
GNEMoveElementLaneSingle::fixMovingProblem() {
233
// obtain lane final length
234
const double laneLenght = myMovedElement->getHierarchicalElement()->getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();
235
// adjust position (negative means start counting from backward)
236
const double adjustedPosition = (myPosOverLane == INVALID_DOUBLE) ? 0 : (myPosOverLane < 0) ? (myPosOverLane + laneLenght) : myPosOverLane;
237
// check conditions
238
if (adjustedPosition < 0) {
239
myMovedElement->setAttribute(myPosAttr, "0", myMovedElement->getNet()->getUndoList());
240
} else if (adjustedPosition > laneLenght) {
241
myMovedElement->setAttribute(myPosAttr, toString(laneLenght), myMovedElement->getNet()->getUndoList());
242
} else if ((myPositionType == PositionType::STARPOS) && (adjustedPosition > (laneLenght - POSITION_EPS))) {
243
myMovedElement->setAttribute(myPosAttr, toString(laneLenght - POSITION_EPS), myMovedElement->getNet()->getUndoList());
244
}
245
}
246
247
248
void
249
GNEMoveElementLaneSingle::writeMoveAttributes(OutputDevice& device) const {
250
// lane
251
device.writeAttr(SUMO_ATTR_LANE, myMovedElement->getAttribute(SUMO_ATTR_LANE));
252
// position (don't write if is an invalid double, except in no default)
253
if ((myPositionType == PositionType::SINGLE) || (myPosOverLane != INVALID_DOUBLE)) {
254
device.writeAttr(myPosAttr, myPosOverLane);
255
}
256
// friendly position (only if true)
257
if (myFriendlyPos) {
258
device.writeAttr(SUMO_ATTR_FRIENDLY_POS, myFriendlyPos);
259
}
260
}
261
262
263
double
264
GNEMoveElementLaneSingle::getFixedPositionOverLane(const bool adjustGeometryFactor) const {
265
// get lane depending of type
266
const auto& lane = (myPositionType == PositionType::ENDPOS) ? myMovedElement->getHierarchicalElement()->getParentLanes().back() : myMovedElement->getHierarchicalElement()->getParentLanes().front();
267
const double laneLength = lane->getParentEdge()->getNBEdge()->getFinalLength();
268
// continue depending if we defined a end position
269
if (myPosOverLane == INVALID_DOUBLE) {
270
if (myPositionType == PositionType::ENDPOS) {
271
return adjustGeometryFactor ? (laneLength * lane->getLengthGeometryFactor()) : laneLength;
272
} else {
273
return 0;
274
}
275
} else {
276
// fix position
277
double fixedPos = myPosOverLane;
278
// adjust fixedPos
279
if (fixedPos < 0) {
280
fixedPos += laneLength;
281
}
282
// set length geometry factor
283
if (adjustGeometryFactor) {
284
// adjust geometry factor
285
fixedPos *= lane->getLengthGeometryFactor();
286
// return depending of fixedPos
287
if (fixedPos < 0) {
288
return 0;
289
} else if (fixedPos > lane->getLaneShapeLength()) {
290
return lane->getLaneShapeLength();
291
} else {
292
return fixedPos;
293
}
294
} else {
295
// return depending of fixedPos
296
if (fixedPos < 0) {
297
return 0;
298
} else if (fixedPos > laneLength) {
299
return laneLength;
300
} else {
301
return fixedPos;
302
}
303
}
304
}
305
}
306
307
308
void
309
GNEMoveElementLaneSingle::setMoveShape(const GNEMoveResult& moveResult) {
310
if (myPositionType == PositionType::ENDPOS) {
311
// change position
312
myPosOverLane = moveResult.newLastPos;
313
// set lateral offset
314
myMovingLateralOffset = moveResult.lastLaneOffset;
315
} else {
316
// change position
317
myPosOverLane = moveResult.newFirstPos;
318
// set lateral offset
319
myMovingLateralOffset = moveResult.firstLaneOffset;
320
}
321
// update geometry
322
myMovedElement->updateGeometry();
323
}
324
325
326
void
327
GNEMoveElementLaneSingle::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {
328
// reset lateral offset
329
myMovingLateralOffset = 0;
330
// begin change attribute
331
undoList->begin(myMovedElement, TLF("position of %", myMovedElement->getTagStr()));
332
// set position
333
if (myPositionType == PositionType::ENDPOS) {
334
myMovedElement->setAttribute(myPosAttr, toString(moveResult.newLastPos), undoList);
335
// check if lane has to be changed
336
if (moveResult.newLastLane && (moveResult.newLastLane != myMovedElement->getHierarchicalElement()->getParentLanes().back())) {
337
// set new lane
338
myMovedElement->setAttribute(SUMO_ATTR_LANE, moveResult.newLastLane->getID(), undoList);
339
}
340
} else {
341
myMovedElement->setAttribute(myPosAttr, toString(moveResult.newFirstPos), undoList);
342
// check if lane has to be changed
343
if (moveResult.newFirstLane && (moveResult.newFirstLane != myMovedElement->getHierarchicalElement()->getParentLanes().front())) {
344
// set new lane
345
myMovedElement->setAttribute(SUMO_ATTR_LANE, moveResult.newFirstLane->getID(), undoList);
346
}
347
}
348
// end change attribute
349
undoList->end();
350
}
351
352
/****************************************************************************/
353
354