Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
GNEMoveElementLaneDouble.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2026 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/****************************************************************************/
18// Class used for elements that can be moved over a lane with two positions
19/****************************************************************************/
20#include <config.h>
21
26#include <netedit/GNENet.h>
28#include <netedit/GNEUndoList.h>
31
34
35// ===========================================================================
36// static members
37// ===========================================================================
38
40
41// ===========================================================================
42// member method definitions
43// ===========================================================================
44
46 SumoXMLAttr startPosAttr, double& startPosValue, SumoXMLAttr endPosAttr,
47 double& endPosValue, bool& friendlyPosition) :
48 GNEMoveElement(element),
49 myStartPos(new GNEMoveElementLaneSingle(element, startPosAttr, startPosValue, friendlyPosition,
50 GNEMoveElementLaneSingle::PositionType::STARPOS)),
51 myEndPos(new GNEMoveElementLaneSingle(element, endPosAttr, endPosValue, friendlyPosition,
52 GNEMoveElementLaneSingle::PositionType::ENDPOS)) {
53}
54
55
60
61
64 const auto& parentLanes = myMovedElement->getHierarchicalElement()->getParentLanes();
65 // get allow change lane
66 const bool allowChangeLane = myMovedElement->getNet()->getViewParent()->getMoveFrame()->getCommonMoveOptions()->getAllowChangeLane();
67 // fist check if we're moving only extremes
68 if (myMovedElement->drawMovingGeometryPoints()) {
69 // get geometry points under cursor
70 const auto& geometryPoints = gViewObjectsHandler.getSelectedGeometryPoints(myMovedElement->getGUIGlObject());
71 // continue depending of clicked geometry point
72 if (geometryPoints.empty()) {
73 return nullptr;
74 } else {
75 if (geometryPoints.front() == 0) {
76 // move start position
77 return myStartPos->getMoveOperation();
78 } else {
79 // move end position
80 return myEndPos->getMoveOperation();
81 }
82 }
83 } else if ((myStartPos->myPosOverLane != INVALID_DOUBLE) && (myEndPos->myPosOverLane != INVALID_DOUBLE)) {
84 // move both start and end positions depending of number of lanes
85 if (parentLanes.size() > 1) {
86 if (gViewObjectsHandler.isObjectSelected(parentLanes.front())) {
87 return new GNEMoveOperation(myMovedElement->getMoveElement(), parentLanes.front(), myStartPos->myPosOverLane, parentLanes.back(), myEndPos->myPosOverLane, true, false);
88 } else if (gViewObjectsHandler.isObjectSelected(parentLanes.back())) {
89 return new GNEMoveOperation(myMovedElement->getMoveElement(), parentLanes.front(), myStartPos->myPosOverLane, parentLanes.back(), myEndPos->myPosOverLane, false, false);
90 } else {
91 // temporal, in the future will be allow, clicking in the intermediate lanes
92 return nullptr;
93 }
94 } else {
95 return new GNEMoveOperation(myMovedElement->getMoveElement(), parentLanes.front(), myStartPos->myPosOverLane, myEndPos->myPosOverLane, allowChangeLane);
96 }
97 } else if (myStartPos->myPosOverLane != INVALID_DOUBLE) {
98 // move only start position
99 return myStartPos->getMoveOperation();
100 } else if (myEndPos->myPosOverLane != INVALID_DOUBLE) {
101 // move only end position
102 return myEndPos->getMoveOperation();
103 } else {
104 // start and end positions undefined, then nothing to move
105 return nullptr;
106 }
107}
108
109
110std::string
112 // position attributes
113 if (key == myStartPos->myPosAttr) {
114 return myStartPos->getMovingAttribute(key);
115 } else if (key == myEndPos->myPosAttr) {
116 return myEndPos->getMovingAttribute(key);
117 } else {
118 // other attributes
119 switch (key) {
120 case SUMO_ATTR_LANE:
121 case SUMO_ATTR_LANES:
122 return GNEAttributeCarrier::parseIDs(myMovedElement->getHierarchicalElement()->getParentLanes());
124 return myStartPos->getMovingAttribute(key);
126 return "";
127 case SUMO_ATTR_LENGTH:
128 case GNE_ATTR_SIZE:
129 if (myMovedElement->isTemplate()) {
130 return toString(myTemplateSize);
131 } else {
133 }
138 default:
139 return myMovedElement->getCommonAttribute(key);
140 }
141 }
142}
143
144
145double
147 // position attributes
148 if (key == myStartPos->myPosAttr) {
149 return myStartPos->myPosOverLane;
150 } else if (key == myEndPos->myPosAttr) {
151 return myEndPos->myPosOverLane;
152 } else {
153 // other attributes
154 switch (key) {
155 case SUMO_ATTR_CENTER:
156 return (getStartFixedPositionOverLane(false) + getEndFixedPositionOverLane(false)) * 0.5;
157 case SUMO_ATTR_LENGTH:
158 case GNE_ATTR_SIZE:
159 if (myMovedElement->isTemplate()) {
160 return myTemplateSize;
161 } else {
163 }
164 default:
165 throw InvalidArgument(myMovedElement->getTagStr() + " doesn't have a moving attribute of type '" + toString(key) + "'");
166 }
167 }
168}
169
170
173 return myMovedElement->getCommonAttributePosition(key);
174}
175
176
179 return myMovedElement->getCommonAttributePositionVector(key);
180}
181
182
183void
185 // position attributes
186 if (key == myStartPos->myPosAttr) {
188 } else if (key == myEndPos->myPosAttr) {
190 } else {
191 // other attributes
192 switch (key) {
193 case SUMO_ATTR_LANE:
194 case SUMO_ATTR_LANES:
200 break;
201 case SUMO_ATTR_LENGTH:
202 if (myMovedElement->isTemplate()) {
203 // use size value
205 } else {
206 // change end position
208 }
209 break;
210 case GNE_ATTR_SIZE:
211 if (myMovedElement->isTemplate()) {
213 } else {
214 setSize(value, undoList);
215 }
216 break;
217 default:
218 myMovedElement->setCommonAttribute(key, value, undoList);
219 break;
220 }
221 }
222}
223
224
225bool
227 // position attributes
228 if (key == myStartPos->myPosAttr) {
229 return myStartPos->isMovingAttributeValid(key, value);
230 } else if (key == myEndPos->myPosAttr) {
231 return myEndPos->isMovingAttributeValid(key, value);
232 } else {
233 // other attributes
234 switch (key) {
235 case SUMO_ATTR_LANE:
236 case SUMO_ATTR_LANES:
237 if (value.empty()) {
238 return false;
239 } else {
241 }
243 return myStartPos->isMovingAttributeValid(key, value);
245 return true;
246 case SUMO_ATTR_LENGTH:
247 case GNE_ATTR_SIZE:
248 if (value.empty()) {
249 return false;
250 } else {
252 }
257 default:
258 return myMovedElement->isCommonAttributeValid(key, value);
259 }
260 }
261}
262
263
264void
266 // position attributes
267 if (key == myStartPos->myPosAttr) {
268 myStartPos->setMovingAttribute(key, value);
269 } else if (key == myEndPos->myPosAttr) {
270 myEndPos->setMovingAttribute(key, value);
271 } else {
272 // other attributes
273 switch (key) {
275 myStartPos->setMovingAttribute(key, value);
276 break;
277 case SUMO_ATTR_LENGTH:
278 case GNE_ATTR_SIZE:
279 if (value.empty()) {
281 } else {
283 }
284 break;
287 break;
290 break;
291 default:
292 return myMovedElement->setCommonAttribute(key, value);
293 }
294 }
295}
296
297
298void
299GNEMoveElementLaneDouble::removeGeometryPoint(const Position /*clickedPosition*/, GNEUndoList* /*undoList*/) {
300 // nothing to do here
301}
302
303
304bool
306 // first check if lanes are connected
307 if (!GNEAdditional::areLaneConsecutives(myMovedElement->getHierarchicalElement()->getParentLanes())) {
308 return false;
309 } else if (!GNEAdditional::areLaneConnected(myMovedElement->getHierarchicalElement()->getParentLanes())) {
310 return false;
311 } else if (!myStartPos->isMoveElementValid() || !myEndPos->isMoveElementValid()) {
312 return false;
313 } else if ((myMovedElement->getHierarchicalElement()->getParentLanes().size() == 1) &&
314 (myStartPos->getFixedPositionOverLane(false) > (myEndPos->getFixedPositionOverLane(false) - POSITION_EPS))) {
315 return false;
316 } else {
317 return true;
318 }
319}
320
321
322std::string
324 // first check if lanes are connected
325 if (!GNEAdditional::areLaneConsecutives(myMovedElement->getHierarchicalElement()->getParentLanes())) {
326 return TL("Lanes aren't consecutives");
327 } else if (!GNEAdditional::areLaneConnected(myMovedElement->getHierarchicalElement()->getParentLanes())) {
328 return TL("Lanes aren't connected");
329 } else if (!myStartPos->isMoveElementValid()) {
330 return myStartPos->getMovingProblem();
331 } else if (!myEndPos->isMoveElementValid()) {
332 return myEndPos->getMovingProblem();
333 } else if ((myMovedElement->getHierarchicalElement()->getParentLanes().size() == 1) &&
334 (myStartPos->getFixedPositionOverLane(false) > (myEndPos->getFixedPositionOverLane(false) - POSITION_EPS))) {
335 return TL("starPos > (endPos - EPS)");
336 } else {
337 return "";
338 }
339}
340
341
342void
344 const auto undolist = myMovedElement->getNet()->getUndoList();
345 // iterate over all lanes and build connections
346 for (int i = 1; i < (int)myMovedElement->getHierarchicalElement()->getParentLanes().size(); i++) {
347 // get lanes
348 const auto firstLane = myMovedElement->getHierarchicalElement()->getParentLanes().at(i - 1);
349 const auto secondLane = myMovedElement->getHierarchicalElement()->getParentLanes().at(i);
350 // search connection
351 bool foundConnection = false;
352 for (const auto& connection : firstLane->getParentEdge()->getGNEConnections()) {
353 if ((connection->getLaneFrom() == firstLane) && (connection->getLaneTo() == secondLane)) {
354 foundConnection = true;
355 break;
356 }
357 }
358 // check if connection exist
359 if (!foundConnection) {
360 // create new connection manually
361 NBEdge::Connection newCon(firstLane->getIndex(), secondLane->getParentEdge()->getNBEdge(), secondLane->getIndex());
362 // allow to undo creation of new lane
363 undolist->add(new GNEChange_Connection(firstLane->getParentEdge(), newCon, false, true), true);
364 }
365 }
366 // Fix both position
367 myStartPos->fixMovingProblem();
368 myEndPos->fixMovingProblem();
369 if (myMovedElement->getHierarchicalElement()->getParentLanes().size() == 1) {
370 // extra if starPos > endPos (endPos is dominant)
371 const double finalLenght = myMovedElement->getHierarchicalElement()->getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength();
372 const double maxStartPos = (myEndPos->myPosOverLane == INVALID_DOUBLE) ? finalLenght : (myEndPos->getFixedPositionOverLane(false) - POSITION_EPS);
373 if (maxStartPos < POSITION_EPS) {
374 myMovedElement->setAttribute(myStartPos->myPosAttr, "0", undolist);
375 myMovedElement->setAttribute(myEndPos->myPosAttr, toString(POSITION_EPS), undolist);
376 } else if (myStartPos->getFixedPositionOverLane(false) > maxStartPos) {
377 // use truncate to avoid problem precission under certain conditions
378 const double newStartPos = (std::trunc(maxStartPos * 1000) / 1000);
379 myMovedElement->setAttribute(myStartPos->myPosAttr, toString(newStartPos), undolist);
380 }
381 }
382}
383
384
385void
386GNEMoveElementLaneDouble::writeMoveAttributes(OutputDevice& device, const bool writeLength) const {
387 // lane/s
388 if (myMovedElement->getTagProperty()->hasAttribute(SUMO_ATTR_LANE)) {
390 } else {
392 }
393 // write start position
394 if (myStartPos->myPosOverLane != myMovedElement->getTagProperty()->getDefaultDoubleValue(myStartPos->myPosAttr)) {
395 device.writeAttr(myStartPos->myPosAttr, myStartPos->myPosOverLane);
396 }
397 // write end position depending of lenght
398 if (writeLength) {
399 device.writeAttr(SUMO_ATTR_LENGTH, (myEndPos->myPosOverLane - myStartPos->myPosOverLane));
400 } else if (myEndPos->myPosOverLane != myMovedElement->getTagProperty()->getDefaultDoubleValue(myEndPos->myPosAttr)) {
401 device.writeAttr(myEndPos->myPosAttr, myEndPos->myPosOverLane);
402 }
403 // friendly position (only if true)
404 if (myStartPos->myFriendlyPos) {
405 device.writeAttr(SUMO_ATTR_FRIENDLY_POS, myStartPos->myFriendlyPos);
406 }
407}
408
409
410double
411GNEMoveElementLaneDouble::getStartFixedPositionOverLane(const bool adjustGeometryFactor) const {
412 if (myStartPos->getFixedPositionOverLane(adjustGeometryFactor) < 0) {
413 return 0;
414 } else if (myStartPos->getFixedPositionOverLane(adjustGeometryFactor) > (myEndPos->getFixedPositionOverLane(adjustGeometryFactor) - POSITION_EPS)) {
415 return (myEndPos->getFixedPositionOverLane(adjustGeometryFactor) - POSITION_EPS);
416 } else {
417 return myStartPos->getFixedPositionOverLane(adjustGeometryFactor);
418 }
419}
420
421
422double
423GNEMoveElementLaneDouble::getEndFixedPositionOverLane(const bool adjustGeometryFactor) const {
424 if (myEndPos->getFixedPositionOverLane(adjustGeometryFactor) < POSITION_EPS) {
425 return POSITION_EPS;
426 } else if (myStartPos->getFixedPositionOverLane(adjustGeometryFactor) > (myEndPos->getFixedPositionOverLane(adjustGeometryFactor) - POSITION_EPS)) {
427 return myEndPos->getFixedPositionOverLane(adjustGeometryFactor);
428 } else {
429 return myEndPos->getFixedPositionOverLane(adjustGeometryFactor);
430 }
431}
432
433
434void
436 // check if we're moving both points
437 if ((moveResult.newFirstPos != INVALID_DOUBLE) && (moveResult.newLastPos != INVALID_DOUBLE)) {
438 // change both position
439 myStartPos->setMoveShape(moveResult);
440 myEndPos->setMoveShape(moveResult);