Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/elements/additional/GNECalibrator.cpp
193723 views
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
/****************************************************************************/
14
/// @file GNECalibrator.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Nov 2015
17
///
18
// Calibrator over edge or lane
19
/****************************************************************************/
20
#include <config.h>
21
22
#include <netedit/changes/GNEChange_Attribute.h>
23
#include <netedit/dialogs/elements/GNECalibratorDialog.h>
24
#include <netedit/GNENet.h>
25
#include <netedit/GNEUndoList.h>
26
#include <utils/gui/div/GLHelper.h>
27
#include <utils/xml/NamespaceIDs.h>
28
29
#include "GNECalibrator.h"
30
31
// ===========================================================================
32
// member method definitions
33
// ===========================================================================
34
35
GNECalibrator::GNECalibrator(SumoXMLTag tag, GNENet* net) :
36
GNEAdditional(net, tag),
37
myEdgeCalibratorContours(new std::vector<GNEContour*>()) {
38
}
39
40
41
GNECalibrator::GNECalibrator(const std::string& id, GNENet* net, FileBucket* fileBucket, GNEEdge* edge, double pos, SUMOTime frequency, const std::string& name,
42
const std::string& output, const double jamThreshold, const std::vector<std::string>& vTypes, const Parameterised::Map& parameters) :
43
GNEAdditional(id, net, SUMO_TAG_CALIBRATOR, fileBucket, name),
44
Parameterised(parameters),
45
myPositionOverLane(pos),
46
myFrequency(frequency),
47
myOutput(output),
48
myJamThreshold(jamThreshold),
49
myVTypes(vTypes),
50
myEdgeCalibratorContours(new std::vector<GNEContour*>()) {
51
// set parents
52
setParent<GNEEdge*>(edge);
53
// update centering boundary without updating grid
54
updateCenteringBoundary(false);
55
}
56
57
58
GNECalibrator::GNECalibrator(const std::string& id, GNENet* net, FileBucket* fileBucket, GNEEdge* edge, double pos, SUMOTime frequency, const std::string& name,
59
const std::string& output, GNEAdditional* routeProbe, const double jamThreshold, const std::vector<std::string>& vTypes,
60
const Parameterised::Map& parameters) :
61
GNEAdditional(id, net, SUMO_TAG_CALIBRATOR, fileBucket, name),
62
Parameterised(parameters),
63
myPositionOverLane(pos),
64
myFrequency(frequency),
65
myOutput(output),
66
myJamThreshold(jamThreshold),
67
myVTypes(vTypes),
68
myEdgeCalibratorContours(new std::vector<GNEContour*>()) {
69
// set parents
70
setParent<GNEEdge*>(edge);
71
setParent<GNEAdditional*>(routeProbe);
72
// update centering boundary without updating grid
73
updateCenteringBoundary(false);
74
}
75
76
77
GNECalibrator::GNECalibrator(const std::string& id, GNENet* net, FileBucket* fileBucket, GNELane* lane, double pos, SUMOTime frequency, const std::string& name,
78
const std::string& output, const double jamThreshold, const std::vector<std::string>& vTypes, const Parameterised::Map& parameters) :
79
GNEAdditional(id, net, GNE_TAG_CALIBRATOR_LANE, fileBucket, name),
80
Parameterised(parameters),
81
myPositionOverLane(pos),
82
myFrequency(frequency),
83
myOutput(output),
84
myJamThreshold(jamThreshold),
85
myVTypes(vTypes),
86
myEdgeCalibratorContours(new std::vector<GNEContour*>()) {
87
// set parents
88
setParent<GNELane*>(lane);
89
// update centering boundary without updating grid
90
updateCenteringBoundary(false);
91
}
92
93
94
GNECalibrator::GNECalibrator(const std::string& id, GNENet* net, FileBucket* fileBucket, GNELane* lane, double pos, SUMOTime frequency, const std::string& name,
95
const std::string& output, GNEAdditional* routeProbe, const double jamThreshold, const std::vector<std::string>& vTypes,
96
const Parameterised::Map& parameters) :
97
GNEAdditional(id, net, GNE_TAG_CALIBRATOR_LANE, fileBucket, name),
98
Parameterised(parameters),
99
myPositionOverLane(pos),
100
myFrequency(frequency),
101
myOutput(output),
102
myJamThreshold(jamThreshold),
103
myVTypes(vTypes),
104
myEdgeCalibratorContours(new std::vector<GNEContour*>()) {
105
// set parents
106
setParent<GNELane*>(lane);
107
setParent<GNEAdditional*>(routeProbe);
108
// update centering boundary without updating grid
109
updateCenteringBoundary(false);
110
}
111
112
113
GNECalibrator::~GNECalibrator() {
114
for (auto it = myEdgeCalibratorContours->begin(); it != myEdgeCalibratorContours->end(); it++) {
115
delete *it;
116
}
117
delete myEdgeCalibratorContours;
118
}
119
120
121
GNEMoveElement*
122
GNECalibrator::getMoveElement() const {
123
return nullptr;
124
}
125
126
127
Parameterised*
128
GNECalibrator::getParameters() {
129
return this;
130
}
131
132
133
const Parameterised*
134
GNECalibrator::getParameters() const {
135
return this;
136
}
137
138
139
void
140
GNECalibrator::writeAdditional(OutputDevice& device) const {
141
// open tag
142
device.openTag(SUMO_TAG_CALIBRATOR);
143
// write common additional attributes
144
writeAdditionalAttributes(device);
145
// write specific attributes
146
if (getParentEdges().size() > 0) {
147
device.writeAttr(SUMO_ATTR_EDGE, getParentEdges().front()->getID());
148
}
149
if (getParentLanes().size() > 0) {
150
device.writeAttr(SUMO_ATTR_LANE, getParentLanes().front()->getID());
151
}
152
device.writeAttr(SUMO_ATTR_POSITION, myPositionOverLane);
153
if (time2string(myFrequency) != "1.00") {
154
device.writeAttr(SUMO_ATTR_PERIOD, time2string(myFrequency));
155
}
156
if (!myOutput.empty()) {
157
device.writeAttr(SUMO_ATTR_OUTPUT, myOutput);
158
}
159
if (getParentAdditionals().size() > 0) {
160
device.writeAttr(SUMO_ATTR_ROUTEPROBE, getParentAdditionals().front()->getID());
161
}
162
if (myJamThreshold != 0.5) {
163
device.writeAttr(SUMO_ATTR_JAM_DIST_THRESHOLD, myJamThreshold);
164
}
165
if (myVTypes.size() > 0) {
166
device.writeAttr(SUMO_ATTR_VTYPES, myVTypes);
167
}
168
// write calibrator flows
169
for (const auto& calibratorFlow : getChildAdditionals()) {
170
if (calibratorFlow->getTagProperty()->getTag() == GNE_TAG_CALIBRATOR_FLOW) {
171
calibratorFlow->writeAdditional(device);
172
}
173
}
174
// write parameters (Always after children to avoid problems with additionals.xsd)
175
writeParams(device);
176
device.closeTag();
177
}
178
179
180
bool
181
GNECalibrator::isAdditionalValid() const {
182
return true;
183
}
184
185
186
std::string
187
GNECalibrator::getAdditionalProblem() const {
188
return "";
189
}
190
191
192
void
193
GNECalibrator::fixAdditionalProblem() {
194
// nothing to fix
195
}
196
197
198
void
199
GNECalibrator::updateGeometry() {
200
// get shape depending of we have a edge or a lane
201
if (getParentLanes().size() > 0) {
202
// simply update geometry
203
myAdditionalGeometry.updateGeometry(getParentLanes().front()->getLaneShape(), myPositionOverLane, 0);
204
} else if (getParentEdges().size() > 0) {
205
// clear all contours
206
for (auto it = myEdgeCalibratorContours->begin(); it != myEdgeCalibratorContours->end(); it++) {
207
delete *it;
208
}
209
// clear all edge geometries
210
myEdgeCalibratorGeometries.clear();
211
myEdgeCalibratorContours->clear();
212
// iterate over every lane and upadte geometries
213
for (const auto& lane : getParentEdges().front()->getChildLanes()) {
214
// this is needed for centering calibratorFlows as additional listed
215
if (lane == getParentEdges().front()->getChildLanes().front()) {
216
myAdditionalGeometry.updateGeometry(lane->getLaneShape(), myPositionOverLane, 0);
217
}
218
// add new calibrator geometry
219
GUIGeometry calibratorGeometry;
220
calibratorGeometry.updateGeometry(lane->getLaneShape(), myPositionOverLane, 0);
221
myEdgeCalibratorGeometries.push_back(calibratorGeometry);
222
// also add a new contour
223
myEdgeCalibratorContours->push_back(new GNEContour());
224
}
225
}
226
// update geometries of all children
227
for (const auto& rerouterElement : getChildAdditionals()) {
228
rerouterElement->updateGeometry();
229
}
230
}
231
232
233
Position
234
GNECalibrator::getPositionInView() const {
235
return myAdditionalGeometry.getShape().getPolygonCenter();
236
}
237
238
239
void
240
GNECalibrator::updateCenteringBoundary(const bool /*updateGrid*/) {
241
// nothing to update
242
}
243
244
245
void
246
GNECalibrator::splitEdgeGeometry(const double splitPosition, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* newElement, GNEUndoList* undoList) {
247
if (splitPosition < myPositionOverLane) {
248
// change lane or edge
249
if (newElement->getTagProperty()->getTag() == SUMO_TAG_LANE) {
250
setAttribute(SUMO_ATTR_LANE, newElement->getID(), undoList);
251
} else {
252
setAttribute(SUMO_ATTR_EDGE, newElement->getID(), undoList);
253
}
254
// now adjust start position
255
setAttribute(SUMO_ATTR_POSITION, toString(myPositionOverLane - splitPosition), undoList);
256
}
257
}
258
259
260
std::string
261
GNECalibrator::getParentName() const {
262
// get parent name depending of we have a edge or a lane
263
if (getParentLanes().size() > 0) {
264
return getParentLanes().front()->getID();
265
} else if (getParentEdges().size() > 0) {
266
return getParentEdges().front()->getChildLanes().at(0)->getID();
267
} else {
268
throw ProcessError(TL("Both myEdge and myLane aren't defined"));
269
}
270
}
271
272
273
void
274
GNECalibrator::drawGL(const GUIVisualizationSettings& s) const {
275
const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();
276
// first check if additional has to be drawn
277
if (myNet->getViewNet()->getDataViewOptions().showAdditionals()) {
278
// get values
279
const double exaggeration = getExaggeration(s);
280
// get detail level
281
const auto d = s.getDetailLevel(exaggeration);
282
if (myEdgeCalibratorGeometries.size() > 0) {
283
// draw all calibrator symbols
284
for (int i = 0; i < (int)myEdgeCalibratorGeometries.size(); i++) {
285
drawCalibratorSymbol(s, d, exaggeration, myEdgeCalibratorGeometries.at(i).getShape().front(),
286
myEdgeCalibratorGeometries.at(i).getShapeRotations().front(), i);
287
}
288
} else {
289
// draw single calibrator symbol
290
drawCalibratorSymbol(s, d, exaggeration, myAdditionalGeometry.getShape().front(),
291
myAdditionalGeometry.getShapeRotations().front(), -1);
292
}
293
// draw additional ID
294
drawAdditionalID(s);
295
// iterate over additionals and check if drawn
296
for (const auto& calibratorFlow : getChildAdditionals()) {
297
// if calibrator is being inspected or selected, then draw
298
if (myNet->getViewNet()->getNetworkViewOptions().showSubAdditionals() ||
299
isAttributeCarrierSelected() || inspectedElements.isACInspected(this) ||
300
calibratorFlow->isAttributeCarrierSelected() || inspectedElements.isACInspected(calibratorFlow) ||
301
calibratorFlow->isMarkedForDrawingFront()) {
302
calibratorFlow->drawGL(s);
303
}
304
}
305
}
306
}
307
308
309
bool
310
GNECalibrator::checkDrawMoveContour() const {
311
// get edit modes
312
const auto& editModes = myNet->getViewNet()->getEditModes();
313
// check if we're in move mode
314
if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&
315
!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&
316
(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {
317
// only move the first element
318
return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;
319
} else {
320
return false;
321
}
322
}
323
324
325
void
326
GNECalibrator::openAdditionalDialog(FXWindow* /* restoringFocusWindow */) {
327
// Open calibrator dialog
328
GNECalibratorDialog calibratorDialog(this);
329
}
330
331
332
std::string
333
GNECalibrator::getAttribute(SumoXMLAttr key) const {
334
switch (key) {
335
case SUMO_ATTR_ID:
336
return getMicrosimID();
337
case SUMO_ATTR_EDGE:
338
return getParentEdges().front()->getID();
339
case SUMO_ATTR_LANE:
340
return getParentLanes().front()->getID();
341
case SUMO_ATTR_POSITION:
342
return toString(myPositionOverLane);
343
case SUMO_ATTR_PERIOD:
344
case SUMO_ATTR_FREQUENCY:
345
return time2string(myFrequency);
346
case SUMO_ATTR_NAME:
347
return myAdditionalName;
348
case SUMO_ATTR_OUTPUT:
349
return myOutput;
350
case SUMO_ATTR_ROUTEPROBE:
351
if (getParentAdditionals().size() > 0) {
352
return getParentAdditionals().front()->getID();
353
} else {
354
return "";
355
}
356
case SUMO_ATTR_JAM_DIST_THRESHOLD:
357
return toString(myJamThreshold);
358
case SUMO_ATTR_VTYPES:
359
return toString(myVTypes);
360
case GNE_ATTR_SHIFTLANEINDEX:
361
return "";
362
default:
363
return getCommonAttribute(key);
364
}
365
}
366
367
368
double
369
GNECalibrator::getAttributeDouble(SumoXMLAttr key) const {
370
return getCommonAttributeDouble(key);
371
}
372
373
374
Position
375
GNECalibrator::getAttributePosition(SumoXMLAttr key) const {
376
return getCommonAttributePosition(key);
377
}
378
379
380
PositionVector
381
GNECalibrator::getAttributePositionVector(SumoXMLAttr key) const {
382
return getCommonAttributePositionVector(key);
383
}
384
385
386
void
387
GNECalibrator::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
388
switch (key) {
389
case SUMO_ATTR_ID:
390
case SUMO_ATTR_EDGE:
391
case SUMO_ATTR_LANE:
392
case SUMO_ATTR_POSITION:
393
case SUMO_ATTR_PERIOD:
394
case SUMO_ATTR_FREQUENCY:
395
case SUMO_ATTR_NAME:
396
case SUMO_ATTR_OUTPUT:
397
case SUMO_ATTR_ROUTEPROBE:
398
case SUMO_ATTR_JAM_DIST_THRESHOLD:
399
case SUMO_ATTR_VTYPES:
400
case GNE_ATTR_SHIFTLANEINDEX:
401
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
402
break;
403
default:
404
setCommonAttribute(key, value, undoList);
405
break;
406
}
407
408
}
409
410
411
bool
412
GNECalibrator::isValid(SumoXMLAttr key, const std::string& value) {
413
switch (key) {
414
case SUMO_ATTR_ID:
415
return isValidAdditionalID(NamespaceIDs::calibrators, value);
416
case SUMO_ATTR_EDGE:
417
if (myNet->getAttributeCarriers()->retrieveEdge(value, false) != nullptr) {
418
return true;
419
} else {
420
return false;
421
}
422
case SUMO_ATTR_LANE:
423
if (myNet->getAttributeCarriers()->retrieveLane(value, false) != nullptr) {
424
return true;
425
} else {
426
return false;
427
}
428
case SUMO_ATTR_POSITION:
429
if (canParse<double>(value)) {
430
// obtain position and check if is valid
431
const double newPosition = parse<double>(value);
432
if (isTemplate()) {
433
return (newPosition >= 0);
434
}
435
// get shape
436
PositionVector shape = (getParentLanes().size() > 0) ? getParentLanes().front()->getLaneShape() : getParentEdges().front()->getChildLanes().at(0)->getLaneShape();
437
if ((newPosition < 0) || (newPosition > shape.length())) {
438
return false;
439
} else {
440
return true;
441
}
442
} else {
443
return false;
444
}
445
case SUMO_ATTR_PERIOD:
446
case SUMO_ATTR_FREQUENCY:
447
return canParse<SUMOTime>(value) ? (parse<SUMOTime>(value) >= 0) : false;
448
case SUMO_ATTR_NAME:
449
return SUMOXMLDefinitions::isValidAttribute(value);
450
case SUMO_ATTR_OUTPUT:
451
return SUMOXMLDefinitions::isValidFilename(value);
452
case SUMO_ATTR_ROUTEPROBE:
453
if (value.empty()) {
454
return true;
455
} else {
456
return (myNet->getAttributeCarriers()->retrieveAdditional(SUMO_TAG_ROUTEPROBE, value, false) != nullptr);
457
}
458
case SUMO_ATTR_JAM_DIST_THRESHOLD:
459
return canParse<double>(value) ? (parse<double>(value) >= 0) : false;
460
case SUMO_ATTR_VTYPES:
461
if (value.empty()) {
462
return true;
463
} else {
464
return SUMOXMLDefinitions::isValidListOfTypeID(value);
465
}
466
default:
467
return isCommonAttributeValid(key, value);
468
}
469
}
470
471
472
std::string
473
GNECalibrator::getPopUpID() const {
474
return getTagStr() + ": " + getID();
475
}
476
477
478
std::string
479
GNECalibrator::getHierarchyName() const {
480
return getTagStr();
481
}
482
483
// ===========================================================================
484
// private
485
// ===========================================================================
486
487
void
488
GNECalibrator::drawCalibratorSymbol(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const double exaggeration,
489
const Position& pos, const double rot, const int symbolIndex) const {
490
// draw geometry only if we'rent in drawForObjectUnderCursor mode
491
if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {
492
// push layer matrix
493
GLHelper::pushMatrix();
494
// translate to front
495
drawInLayer(GLO_CALIBRATOR);
496
// translate to position
497
glTranslated(pos.x(), pos.y(), 0);
498
// rotate over lane
499
GUIGeometry::rotateOverLane(rot + 90);
500
// scale
501
glScaled(exaggeration, exaggeration, 1);
502
// set drawing mode
503
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
504
// set color
505
GLHelper::setColor(drawUsingSelectColor() ? s.colorSettings.selectedAdditionalColor : s.additionalSettings.calibratorColor);
506
// base
507
glBegin(GL_TRIANGLES);
508
glVertex2d(0 - s.additionalSettings.calibratorWidth, 0);
509
glVertex2d(0 - s.additionalSettings.calibratorWidth, s.additionalSettings.calibratorHeight);
510
glVertex2d(0 + s.additionalSettings.calibratorWidth, s.additionalSettings.calibratorHeight);
511
glVertex2d(0 + s.additionalSettings.calibratorWidth, 0);
512
glVertex2d(0 - s.additionalSettings.calibratorWidth, 0);
513
glVertex2d(0 + s.additionalSettings.calibratorWidth, s.additionalSettings.calibratorHeight);
514
glEnd();
515
// draw text if isn't being drawn for selecting
516
if (d <= GUIVisualizationSettings::Detail::Text) {
517
// set color depending of selection status
518
RGBColor textColor = drawUsingSelectColor() ? s.colorSettings.selectionColor : RGBColor::BLACK;
519
// draw "C"
520
GLHelper::drawText("C", Position(0, 1.5), 0.1, 3, textColor, 180);
521
// draw "edge" or "lane "
522
if (getParentLanes().size() > 0) {
523
GLHelper::drawText("lane", Position(0, 3), .1, 1, textColor, 180);
524
} else if (getParentEdges().size() > 0) {
525
GLHelper::drawText("edge", Position(0, 3), .1, 1, textColor, 180);
526
} else {
527
throw ProcessError(TL("Both myEdge and myLane aren't defined"));
528
}
529
}
530
// pop layer matrix
531
GLHelper::popMatrix();
532
// draw dotted contours
533
if (symbolIndex == -1) {
534
myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);
535
} else {
536
myEdgeCalibratorContours->at(symbolIndex)->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);
537
}
538
}
539
GUIGlObject* parentBoundary = nullptr;
540
if (getParentEdges().size() > 0) {
541
parentBoundary = getParentEdges().front();
542
} else if (getParentLanes().size() > 0) {
543
parentBoundary = getParentLanes().front();
544
}
545
// calculate dotted contour
546
if (symbolIndex == -1) {
547
myAdditionalContour.calculateContourRectangleShape(s, d, this, pos, s.additionalSettings.calibratorWidth,
548
s.additionalSettings.calibratorHeight * 0.5, getType(), 0, s.additionalSettings.calibratorHeight * 0.5, rot + 90,
549
exaggeration, parentBoundary);
550
} else {
551
if (symbolIndex == 0) {
552
myAdditionalContour.calculateContourRectangleShape(s, d, this, pos, s.additionalSettings.calibratorWidth,
553
s.additionalSettings.calibratorHeight * 0.5, getType(), 0, s.additionalSettings.calibratorHeight * 0.5, rot + 90,
554
exaggeration, parentBoundary);
555
}
556
myEdgeCalibratorContours->at(symbolIndex)->calculateContourRectangleShape(s, d, this, pos, s.additionalSettings.calibratorWidth,
557
s.additionalSettings.calibratorHeight * 0.5, getType(), 0, s.additionalSettings.calibratorHeight * 0.5, rot + 90,
558
exaggeration, parentBoundary);
559
}
560
}
561
562
void
563
GNECalibrator::setAttribute(SumoXMLAttr key, const std::string& value) {
564
switch (key) {
565
case SUMO_ATTR_ID:
566
// update microsimID
567
setAdditionalID(value);
568
break;
569
case SUMO_ATTR_EDGE:
570
replaceAdditionalParentEdges(value);
571
break;
572
case SUMO_ATTR_LANE:
573
replaceAdditionalParentLanes(value);
574
break;
575
case SUMO_ATTR_POSITION:
576
myPositionOverLane = parse<double>(value);
577
break;
578
case SUMO_ATTR_PERIOD:
579
case SUMO_ATTR_FREQUENCY:
580
myFrequency = parse<SUMOTime>(value);
581
break;
582
case SUMO_ATTR_NAME:
583
myAdditionalName = value;
584
break;
585
case SUMO_ATTR_OUTPUT:
586
myOutput = value;
587
break;
588
case SUMO_ATTR_ROUTEPROBE:
589
replaceAdditionalParent(SUMO_TAG_ROUTEPROBE, value, 0);
590
break;
591
case SUMO_ATTR_JAM_DIST_THRESHOLD:
592
myJamThreshold = parse<double>(value);
593
break;
594
case SUMO_ATTR_VTYPES:
595
myVTypes = parse<std::vector<std::string> >(value);
596
break;
597
case GNE_ATTR_SHIFTLANEINDEX:
598
shiftLaneIndex();
599
break;
600
default:
601
setCommonAttribute(key, value);
602
break;
603
}
604
}
605
606
/****************************************************************************/
607
608