Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/elements/additional/GNEPOI.cpp
193717 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 GNEPOI.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Jun 2017
17
///
18
// A class for visualizing and editing POIS in netedit
19
/****************************************************************************/
20
21
#include <netedit/changes/GNEChange_Attribute.h>
22
#include <netedit/elements/moving/GNEMoveElementLaneSingle.h>
23
#include <netedit/elements/moving/GNEMoveElementViewResizable.h>
24
#include <netedit/GNENet.h>
25
#include <netedit/GNETagProperties.h>
26
#include <utils/common/StringTokenizer.h>
27
#include <utils/gui/div/GLHelper.h>
28
#include <utils/gui/div/GUIDesigns.h>
29
#include <utils/gui/div/GUIParameterTableWindow.h>
30
#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
31
#include <utils/gui/globjects/GUIPointOfInterest.h>
32
#include <utils/gui/images/GUITextureSubSys.h>
33
#include <utils/xml/NamespaceIDs.h>
34
35
#include "GNEPOI.h"
36
37
// ===========================================================================
38
// method definitions
39
// ===========================================================================
40
41
GNEPOI::GNEPOI(SumoXMLTag tag, GNENet* net) :
42
Shape(""),
43
GNEAdditional(net, tag),
44
myMoveElementLaneSingle(new GNEMoveElementLaneSingle(this, SUMO_ATTR_POSITION, myPosOverLane, myFriendlyPos, GNEMoveElementLaneSingle::PositionType::SINGLE)),
45
myMoveElementViewResizable(new GNEMoveElementViewResizable(this, (tag == GNE_TAG_POIGEO) ? GNEMoveElementView::AttributesFormat::GEO : GNEMoveElementView::AttributesFormat::CARTESIAN,
46
GNEMoveElementViewResizable::ResizingFormat::WIDTH_HEIGHT, SUMO_ATTR_POSITION, myPosOverView)) {
47
}
48
49
50
GNEPOI::GNEPOI(const std::string& id, GNENet* net, FileBucket* fileBucket, const std::string& type, const RGBColor& color, const Position& pos,
51
const bool geo, POIIcon icon, const double layer, const double angle, const std::string& imgFile, const double width,
52
const double height, const std::string& name, const Parameterised::Map& parameters) :
53
Shape(id, type, color, layer, angle, imgFile, ""),
54
GNEAdditional(id, net, geo ? GNE_TAG_POIGEO : SUMO_TAG_POI, fileBucket, name),
55
Parameterised(parameters),
56
myPosOverView(pos),
57
myWidth(width),
58
myHeight(height),
59
myPOIIcon(icon),
60
myMoveElementLaneSingle(new GNEMoveElementLaneSingle(this, SUMO_ATTR_POSITION, myPosOverLane, myFriendlyPos, GNEMoveElementLaneSingle::PositionType::SINGLE)),
61
myMoveElementViewResizable(new GNEMoveElementViewResizable(this, geo ? GNEMoveElementView::AttributesFormat::GEO : GNEMoveElementView::AttributesFormat::CARTESIAN,
62
GNEMoveElementViewResizable::ResizingFormat::WIDTH_HEIGHT, SUMO_ATTR_POSITION, myPosOverView)) {
63
// update position depending of GEO
64
if (geo) {
65
Position cartesian = myPosOverView;
66
GeoConvHelper::getFinal().x2cartesian_const(cartesian);
67
myPosOverView = cartesian;
68
}
69
// update centering boundary without updating grid
70
updateCenteringBoundary(false);
71
}
72
73
74
GNEPOI::GNEPOI(const std::string& id, GNENet* net, FileBucket* fileBucket, const std::string& type, const RGBColor& color, GNELane* lane, const double posOverLane,
75
const bool friendlyPos, const double posLat, POIIcon icon, const double layer, const double angle, const std::string& imgFile, const double width,
76
const double height, const std::string& name, const Parameterised::Map& parameters) :
77
Shape(id, type, color, layer, angle, imgFile, ""),
78
GNEAdditional(id, net, GNE_TAG_POILANE, fileBucket, name),
79
Parameterised(parameters),
80
myPosOverLane(posOverLane),
81
myFriendlyPos(friendlyPos),
82
myWidth(width),
83
myHeight(height),
84
myPosLat(posLat),
85
myPOIIcon(icon),
86
myMoveElementLaneSingle(new GNEMoveElementLaneSingle(this, SUMO_ATTR_POSITION, myPosOverLane, myFriendlyPos, GNEMoveElementLaneSingle::PositionType::SINGLE)),
87
myMoveElementViewResizable(new GNEMoveElementViewResizable(this, GNEMoveElementView::AttributesFormat::POSITION, GNEMoveElementViewResizable::ResizingFormat::WIDTH_HEIGHT,
88
SUMO_ATTR_POSITION, myPosOverView)) {
89
// set parents
90
setParent<GNELane*>(lane);
91
// update centering boundary without updating grid
92
updateCenteringBoundary(false);
93
}
94
95
96
GNEPOI::~GNEPOI() {
97
delete myMoveElementLaneSingle;
98
delete myMoveElementViewResizable;
99
}
100
101
102
GNEMoveElement*
103
GNEPOI::getMoveElement() const {
104
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
105
return myMoveElementLaneSingle;
106
} else {
107
return myMoveElementViewResizable;
108
}
109
}
110
111
112
Parameterised*
113
GNEPOI::getParameters() {
114
return this;
115
}
116
117
118
const Parameterised*
119
GNEPOI::getParameters() const {
120
return this;
121
}
122
123
124
std::string
125
GNEPOI::generateChildID(SumoXMLTag /*childTag*/) {
126
return "";
127
}
128
129
130
CommonXMLStructure::SumoBaseObject*
131
GNEPOI::getSumoBaseObject() const {
132
CommonXMLStructure::SumoBaseObject* POIBaseObject = new CommonXMLStructure::SumoBaseObject(nullptr);
133
POIBaseObject->setTag(SUMO_TAG_POI);
134
// fill attributes
135
POIBaseObject->addStringAttribute(SUMO_ATTR_ID, myID);
136
POIBaseObject->addColorAttribute(SUMO_ATTR_COLOR, getShapeColor());
137
POIBaseObject->addStringAttribute(SUMO_ATTR_TYPE, getShapeType());
138
POIBaseObject->addStringAttribute(SUMO_ATTR_ICON, SUMOXMLDefinitions::POIIcons.getString(myPOIIcon));
139
POIBaseObject->addDoubleAttribute(SUMO_ATTR_LAYER, getShapeLayer());
140
POIBaseObject->addStringAttribute(SUMO_ATTR_IMGFILE, getShapeImgFile());
141
POIBaseObject->addDoubleAttribute(SUMO_ATTR_WIDTH, myWidth);
142
POIBaseObject->addDoubleAttribute(SUMO_ATTR_HEIGHT, myHeight);
143
POIBaseObject->addDoubleAttribute(SUMO_ATTR_ANGLE, getShapeNaviDegree());
144
POIBaseObject->addStringAttribute(SUMO_ATTR_NAME, myAdditionalName);
145
return POIBaseObject;
146
}
147
148
149
void
150
GNEPOI::writeAdditional(OutputDevice& device) const {
151
device.openTag(SUMO_TAG_POI);
152
// write common additional attributes
153
writeAdditionalAttributes(device);
154
// specific of poi lanes
155
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
156
// write move attributes
157
myMoveElementLaneSingle->writeMoveAttributes(device);
158
// write specific attributes
159
if (myPosLat != 0) {
160
device.writeAttr(SUMO_ATTR_POSITION_LAT, myPosLat);
161
}
162
} else {
163
// write move attributes
164
myMoveElementViewResizable->writeMoveAttributes(device);
165
}
166
// write shape attributes
167
writeShapeAttributes(device, RGBColor::RED, Shape::DEFAULT_LAYER_POI);
168
// width
169
if (myWidth != Shape::DEFAULT_IMG_WIDTH) {
170
device.writeAttr(SUMO_ATTR_WIDTH, myWidth);
171
}
172
// height
173
if (myHeight != Shape::DEFAULT_IMG_HEIGHT) {
174
device.writeAttr(SUMO_ATTR_HEIGHT, myHeight);
175
}
176
// Icon
177
if (myPOIIcon != POIIcon::NONE) {
178
device.writeAttr(SUMO_ATTR_ICON, SUMOXMLDefinitions::POIIcons.getString(myPOIIcon));
179
}
180
// params
181
writeParams(device);
182
device.closeTag();
183
}
184
185
186
bool
187
GNEPOI::isAdditionalValid() const {
188
// only for POIS over lanes
189
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
190
// only movement problems
191
return myMoveElementLaneSingle->isMoveElementValid();
192
} else {
193
return true;
194
}
195
}
196
197
198
std::string
199
GNEPOI::getAdditionalProblem() const {
200
// only for POIS over lanes
201
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
202
// only movement problems
203
return myMoveElementLaneSingle->getMovingProblem();
204
} else {
205
return "";
206
}
207
}
208
209
210
void
211
GNEPOI::fixAdditionalProblem() {
212
// only for POIS over lanes
213
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
214
// only movement problems
215
myMoveElementLaneSingle->fixMovingProblem();
216
}
217
}
218
219
220
void
221
GNEPOI::updateGeometry() {
222
// check if update width and height shapes
223
if ((myWidth > 0) && (myHeight > 0)) {
224
// calculate shape length
225
myMoveElementViewResizable->myShapeHeight.clear();
226
myMoveElementViewResizable->myShapeHeight.push_back(Position(0, myHeight * -0.5));
227
myMoveElementViewResizable->myShapeHeight.push_back(Position(0, myHeight * 0.5));
228
// move
229
myMoveElementViewResizable->myShapeHeight.add(myPosOverView);
230
// calculate shape width
231
PositionVector leftShape = myMoveElementViewResizable->myShapeHeight;
232
leftShape.move2side(myWidth * -0.5);
233
PositionVector rightShape = myMoveElementViewResizable->myShapeHeight;
234
rightShape.move2side(myWidth * 0.5);
235
myMoveElementViewResizable->myShapeWidth = {leftShape.getCentroid(), rightShape.getCentroid()};
236
}
237
// set additional geometry
238
if (getParentLanes().size() > 0) {
239
myAdditionalGeometry.updateGeometry(getParentLanes().front()->getLaneShape(),
240
myMoveElementLaneSingle->getFixedPositionOverLane(true), myPosLat);
241
} else {
242
myAdditionalGeometry.updateSinglePosGeometry(myPosOverView, 0);
243
}
244
}
245
246
247
Position
248
GNEPOI::getPositionInView() const {
249
return myAdditionalGeometry.getShape().getPolygonCenter();
250
}
251
252
253
double
254
GNEPOI::getExaggeration(const GUIVisualizationSettings& s) const {
255
return s.poiSize.getExaggeration(s, this);
256
}
257
258
259
void
260
GNEPOI::updateCenteringBoundary(const bool updateGrid) {
261
// Remove object from net
262
if (updateGrid) {
263
myNet->removeGLObjectFromGrid(this);
264
}
265
// update geometry
266
updateGeometry();
267
// reset boundary
268
myAdditionalBoundary.reset();
269
// add center
270
myAdditionalBoundary.add(myPosOverView);
271
// add width
272
for (const auto& pos : myMoveElementViewResizable->myShapeWidth) {
273
myAdditionalBoundary.add(pos);
274
}
275
// add height
276
for (const auto& pos : myMoveElementViewResizable->myShapeHeight) {
277
myAdditionalBoundary.add(pos);
278
}
279
// grow boundary
280
myAdditionalBoundary.grow(5);
281
// add object into net
282
if (updateGrid) {
283
myNet->addGLObjectIntoGrid(this);
284
}
285
}
286
287
288
void
289
GNEPOI::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* /*newElement*/, GNEUndoList* /*undoList*/) {
290
// nothing to split
291
}
292
293
294
GUIGlID
295
GNEPOI::getGlID() const {
296
return GUIGlObject::getGlID();
297
}
298
299
300
bool
301
GNEPOI::checkDrawMoveContour() const {
302
// get edit modes
303
const auto& editModes = myNet->getViewNet()->getEditModes();
304
// check if we're in move mode
305
if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&
306
!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&
307
(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {
308
// only move the first element
309
return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;
310
} else {
311
return false;
312
}
313
}
314
315
316
std::string
317
GNEPOI::getParentName() const {
318
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
319
return getParentLanes().front()->getID();
320
} else {
321
return myNet->getMicrosimID();
322
}
323
}
324
325
326
GUIGLObjectPopupMenu*
327
GNEPOI::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
328
// create popup
329
GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
330
// build common options
331
buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);
332
// specific of non juPedSim polygons
333
if (!myTagProperty->isJuPedSimElement()) {
334
// continue depending of lane number
335
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
336
// add option for convert to GNEPOI
337
GUIDesigns::buildFXMenuCommand(ret, TL("Release from lane"), GUIIconSubSys::getIcon(GUIIcon::LANE), &parent, MID_GNE_POI_RELEASE);
338
} else {
339
// add option for convert to GNEPOI
340
GUIDesigns::buildFXMenuCommand(ret, TL("Attach to nearest lane"), GUIIconSubSys::getIcon(GUIIcon::LANE), &parent, MID_GNE_POI_ATTACH);
341
// check if transform
342
if (GeoConvHelper::getFinal().getProjString() != "!") {
343
if (getTagProperty()->getTag() == GNE_TAG_POIGEO) {
344
GUIDesigns::buildFXMenuCommand(ret, TL("Transform to POI"), GUIIconSubSys::getIcon(GUIIcon::POI), &parent, MID_GNE_POI_TRANSFORM_POI);
345
} else {
346
GUIDesigns::buildFXMenuCommand(ret, TL("Transform to POI Geo"), GUIIconSubSys::getIcon(GUIIcon::POIGEO), &parent, MID_GNE_POI_TRANSFORM_POIGEO);
347
}
348
}
349
}
350
}
351
return ret;
352
}
353
354
355
void
356
GNEPOI::drawGL(const GUIVisualizationSettings& s) const {
357
// first check if POI can be drawn
358
if (myNet->getViewNet()->getDemandViewOptions().showShapes() &&
359
myNet->getViewNet()->getDataViewOptions().showShapes()) {
360
// draw boundaries
361
GLHelper::drawBoundary(s, getCenteringBoundary());
362
// obtain POIExaggeration
363
const double POIExaggeration = getExaggeration(s);
364
// get detail level
365
const auto d = s.getDetailLevel(POIExaggeration);
366
// check if draw moving geometry points (only if we have a defined image
367
const bool movingGeometryPoints = getShapeImgFile().empty() ? false : drawMovingGeometryPoints();
368
// draw geometry only if we'rent in drawForObjectUnderCursor mode
369
if (s.checkDrawPOI(myWidth, myHeight, d, isAttributeCarrierSelected())) {
370
// draw POI
371
drawPOI(s, d, movingGeometryPoints);
372
// draw lock icon
373
GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), POIExaggeration);
374
// draw dotted contours
375
if (movingGeometryPoints) {
376
// get snap radius
377
const double snapRadius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.additionalGeometryPointRadius;
378
const double snapRadiusSquared = snapRadius * snapRadius;
379
// get mouse position
380
const Position mousePosition = myNet->getViewNet()->getPositionInformation();
381
// check if we're editing width or height
382
if ((myMoveElementViewResizable->myShapeHeight.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) ||
383
(myMoveElementViewResizable->myShapeHeight.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared)) {
384
myMoveElementViewResizable->myMovingContourUp.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);
385
myMoveElementViewResizable->myMovingContourDown.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);
386
} else if ((myMoveElementViewResizable->myShapeWidth.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) ||
387
(myMoveElementViewResizable->myShapeWidth.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared)) {
388
myMoveElementViewResizable->myMovingContourLeft.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);
389
myMoveElementViewResizable->myMovingContourRight.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);
390
}
391
} else {
392
myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);
393
}
394
}
395
// calculate contour
396
calculatePOIContour(s, d, POIExaggeration, movingGeometryPoints);
397
}
398
}
399
400
401
std::string
402
GNEPOI::getAttribute(SumoXMLAttr key) const {
403
switch (key) {
404
case SUMO_ATTR_ID:
405
return myID;
406
case SUMO_ATTR_COLOR:
407
return toString(getShapeColor());
408
case SUMO_ATTR_POSITION_LAT:
409
return toString(myPosLat);
410
case SUMO_ATTR_LON:
411
if (GeoConvHelper::getFinal().getProjString() != "!") {
412
return toString(getAttributeDouble(key), 8);
413
} else {
414
return TL("No geo-conversion defined");
415
}
416
case SUMO_ATTR_LAT:
417
if (GeoConvHelper::getFinal().getProjString() != "!") {
418
return toString(getAttributeDouble(key), 8);
419
} else {
420
return TL("No geo-conversion defined");
421
}
422
case SUMO_ATTR_TYPE:
423
return getShapeType();
424
case SUMO_ATTR_ICON:
425
return SUMOXMLDefinitions::POIIcons.getString(myPOIIcon);
426
case SUMO_ATTR_LAYER:
427
return toString(getShapeLayer());
428
case SUMO_ATTR_IMGFILE:
429
return getShapeImgFile();
430
case SUMO_ATTR_WIDTH:
431
return toString(myWidth);
432
case SUMO_ATTR_HEIGHT:
433
return toString(myHeight);
434
case SUMO_ATTR_ANGLE:
435
return toString(getShapeNaviDegree());
436
case SUMO_ATTR_NAME:
437
return myAdditionalName;
438
case GNE_ATTR_SHIFTLANEINDEX:
439
return "";
440
default:
441
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
442
return myMoveElementLaneSingle->getMovingAttribute(key);
443
} else {
444
return myMoveElementViewResizable->getMovingAttribute(key);
445
}
446
}
447
}
448
449
450
double
451
GNEPOI::getAttributeDouble(SumoXMLAttr key) const {
452
switch (key) {
453
case SUMO_ATTR_POSITION_LAT:
454
return myPosLat;
455
case SUMO_ATTR_LON:
456
if (GeoConvHelper::getFinal().getProjString() != "!") {
457
// calculate geo position
458
Position GEOPosition = myPosOverView;
459
GeoConvHelper::getFinal().cartesian2geo(GEOPosition);
460
// return lon
461
return GEOPosition.x();
462
} else {
463
throw InvalidArgument(getTagStr() + " attribute '" + toString(key) + "' not allowed");
464
}
465
case SUMO_ATTR_LAT:
466
if (GeoConvHelper::getFinal().getProjString() != "!") {
467
// calculate geo position
468
Position GEOPosition = myPosOverView;
469
GeoConvHelper::getFinal().cartesian2geo(GEOPosition);
470
// return lat
471
return GEOPosition.y();
472
} else {
473
throw InvalidArgument(getTagStr() + " attribute '" + toString(key) + "' not allowed");
474
}
475
case SUMO_ATTR_LAYER:
476
return getShapeLayer();
477
case SUMO_ATTR_WIDTH:
478
return myWidth;
479
case SUMO_ATTR_HEIGHT:
480
return myHeight;
481
case SUMO_ATTR_ANGLE:
482
return getShapeNaviDegree();
483
default:
484
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
485
return myMoveElementLaneSingle->getMovingAttributeDouble(key);
486
} else {
487
return myMoveElementViewResizable->getMovingAttributeDouble(key);
488
}
489
}
490
}
491
492
493
Position
494
GNEPOI::getAttributePosition(SumoXMLAttr key) const {
495
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
496
return myMoveElementLaneSingle->getMovingAttributePosition(key);
497
} else {
498
return myMoveElementViewResizable->getMovingAttributePosition(key);
499
}
500
}
501
502
503
PositionVector
504
GNEPOI::getAttributePositionVector(SumoXMLAttr key) const {
505
return getCommonAttributePositionVector(key);
506
}
507
508
509
void
510
GNEPOI::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
511
switch (key) {
512
case SUMO_ATTR_ID:
513
case SUMO_ATTR_COLOR:
514
case SUMO_ATTR_POSITION_LAT:
515
case SUMO_ATTR_LON:
516
case SUMO_ATTR_LAT:
517
case SUMO_ATTR_TYPE:
518
case SUMO_ATTR_ICON:
519
case SUMO_ATTR_LAYER:
520
case SUMO_ATTR_IMGFILE:
521
case SUMO_ATTR_WIDTH:
522
case SUMO_ATTR_HEIGHT:
523
case SUMO_ATTR_ANGLE:
524
case SUMO_ATTR_NAME:
525
case GNE_ATTR_SHIFTLANEINDEX:
526
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
527
break;
528
default:
529
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
530
return myMoveElementLaneSingle->setMovingAttribute(key, value, undoList);
531
} else {
532
return myMoveElementViewResizable->setMovingAttribute(key, value, undoList);
533
}
534
}
535
}
536
537
538
bool
539
GNEPOI::isValid(SumoXMLAttr key, const std::string& value) {
540
switch (key) {
541
case SUMO_ATTR_ID:
542
return isValidAdditionalID(NamespaceIDs::POIs, value);
543
case SUMO_ATTR_COLOR:
544
return canParse<RGBColor>(value);
545
case SUMO_ATTR_POSITION_LAT:
546
return canParse<double>(value);
547
case SUMO_ATTR_LON:
548
return canParse<double>(value);
549
case SUMO_ATTR_LAT:
550
return canParse<double>(value);
551
case SUMO_ATTR_TYPE:
552
return true;
553
case SUMO_ATTR_ICON:
554
return SUMOXMLDefinitions::POIIcons.hasString(value);
555
case SUMO_ATTR_LAYER:
556
if (value.empty()) {
557
return true;
558
} else {
559
return canParse<double>(value);
560
}
561
case SUMO_ATTR_IMGFILE:
562
if (value == "") {
563
return true;
564
} else {
565
// check that image can be loaded
566
return GUITexturesHelper::getTextureID(value) != -1;
567
}
568
case SUMO_ATTR_WIDTH:
569
return canParse<double>(value) && (parse<double>(value) > 0);
570
case SUMO_ATTR_HEIGHT:
571
return canParse<double>(value) && (parse<double>(value) > 0);
572
case SUMO_ATTR_ANGLE:
573
return canParse<double>(value);
574
case SUMO_ATTR_NAME:
575
return SUMOXMLDefinitions::isValidAttribute(value);
576
default:
577
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
578
return myMoveElementLaneSingle->isMovingAttributeValid(key, value);
579
} else {
580
return myMoveElementViewResizable->isMovingAttributeValid(key, value);
581
}
582
}
583
}
584
585
586
bool
587
GNEPOI::isAttributeEnabled(SumoXMLAttr key) const {
588
switch (key) {
589
case SUMO_ATTR_POSITION:
590
if (myTagProperty->getTag() == GNE_TAG_POIGEO) {
591
return (GeoConvHelper::getFinal().getProjString() != "!");
592
} else {
593
return true;
594
}
595
case SUMO_ATTR_LON:
596
case SUMO_ATTR_LAT:
597
return (GeoConvHelper::getFinal().getProjString() != "!");
598
default:
599
return true;
600
}
601
}
602
603
604
std::string
605
GNEPOI::getPopUpID() const {
606
return getTagStr() + ": " + getID();
607
}
608
609
610
std::string
611
GNEPOI::getHierarchyName() const {
612
return getTagStr();
613
}
614
615
// ===========================================================================
616
// private
617
// ===========================================================================
618
619
void
620
GNEPOI::drawPOI(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,
621
const bool movingGeometryPoints) const {
622
if (GUIPointOfInterest::checkDraw(s, this)) {
623
const double exaggeration = getExaggeration(s);
624
const auto position = getPositionInView();
625
// push matrix
626
GLHelper::pushMatrix();
627
// set POI color
628
GUIPointOfInterest::setPOIColor(s, getShapeColor(), this, drawUsingSelectColor());
629
// add extra offset z provided by icon to avoid overlapping
630
if (myDrawInFront) {
631
glTranslated(position.x(), position.y(), GLO_FRONTELEMENT + (double)myPOIIcon);
632
} else {
633
glTranslated(position.x(), position.y(), s.poiUseCustomLayer ? s.poiCustomLayer : getShapeLayer() + (double)myPOIIcon);
634
}
635
glRotated(-getShapeNaviDegree(), 0, 0, 1);
636
// check if has to be drawn as a circle or with an image
637
if (getShapeImgFile() != DEFAULT_IMG_FILE) {
638
int textureID = GUITexturesHelper::getTextureID(getShapeImgFile());
639
if (textureID > 0) {
640
GUITexturesHelper::drawTexturedBox(textureID,
641
myWidth * 0.5 * exaggeration, myHeight * 0.5 * exaggeration,
642
myWidth * 0.5 * exaggeration, myHeight * 0.5 * exaggeration);
643
} else {
644
// draw box
645
GLHelper::drawRectangle(Position(0, 0), myWidth * exaggeration, myHeight * exaggeration);
646
}
647
} else {
648
// fallback if no image is defined
649
GLHelper::drawFilledCircle(std::max(myWidth, myHeight) * 0.5 * exaggeration, s.poiDetail);
650
// check if draw polygon
651
if (myPOIIcon != POIIcon::NONE) {
652
// translate
653
glTranslated(0, 0, 0.1);
654
// rotate
655
glRotated(180, 0, 0, 1);
656
// draw texture
657
GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getPOITexture(myPOIIcon), exaggeration * 0.8);
658
}
659
}
660
// pop matrix
661
GLHelper::popMatrix();
662
if (!s.drawForRectangleSelection) {
663
const Position namePos = position;
664
drawName(namePos, s.scale, s.poiName, s.angle);
665
if (s.poiType.show(this)) {
666
const Position p = namePos + Position(0, -0.6 * s.poiType.size / s.scale);
667
GLHelper::drawTextSettings(s.poiType, getShapeType(), p, s.scale, s.angle);
668
}
669
if (s.poiText.show(this)) {
670
GLHelper::pushMatrix();
671
glTranslated(namePos.x(), namePos.y(), 0);
672
std::string value = getParameter(s.poiTextParam, "");
673
if (value != "") {
674
auto lines = StringTokenizer(value, StringTokenizer::NEWLINE).getVector();
675
glRotated(-s.angle, 0, 0, 1);
676
glTranslated(0, 0.7 * s.poiText.scaledSize(s.scale) * (double)lines.size(), 0);
677
glRotated(s.angle, 0, 0, 1);
678
// FONS_ALIGN_LEFT = 1
679
// FONS_ALIGN_CENTER = 2
680
// FONS_ALIGN_MIDDLE = 16
681
const int align = (lines.size() > 1 ? 1 : 2) | 16;
682
for (std::string& line : lines) {
683
GLHelper::drawTextSettings(s.poiText, line, Position(0, 0), s.scale, s.angle, GLO_MAX, align);
684
glRotated(-s.angle, 0, 0, 1);
685
glTranslated(0, -0.7 * s.poiText.scaledSize(s.scale), 0);
686
glRotated(s.angle, 0, 0, 1);
687
}
688
}
689
GLHelper::popMatrix();
690
}
691
}
692
// draw geometry points
693
if (movingGeometryPoints) {
694
if (myMoveElementViewResizable->myShapeHeight.size() > 0) {
695
drawUpGeometryPoint(s, d, myMoveElementViewResizable->myShapeHeight.front(), 180, RGBColor::ORANGE);
696
drawDownGeometryPoint(s, d, myMoveElementViewResizable->myShapeHeight.back(), 180, RGBColor::ORANGE);
697
}
698
if (myMoveElementViewResizable->myShapeWidth.size() > 0) {
699
drawLeftGeometryPoint(s, d, myMoveElementViewResizable->myShapeWidth.back(), -90, RGBColor::ORANGE);
700
drawRightGeometryPoint(s, d, myMoveElementViewResizable->myShapeWidth.front(), -90, RGBColor::ORANGE);
701
}
702
}
703
}
704
}
705
706
707
void
708
GNEPOI::calculatePOIContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,
709
const double exaggeration, const bool movingGeometryPoints) const {
710
// check if we're calculating the contour or the moving geometry points
711
if (movingGeometryPoints) {
712
myMoveElementViewResizable->myMovingContourUp.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeHeight.front(), s.neteditSizeSettings.additionalGeometryPointRadius,
713
getShapeLayer(), exaggeration, nullptr);
714
myMoveElementViewResizable->myMovingContourDown.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeHeight.back(), s.neteditSizeSettings.additionalGeometryPointRadius,
715
getShapeLayer(), exaggeration, nullptr);
716
myMoveElementViewResizable->myMovingContourLeft.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeWidth.front(), s.neteditSizeSettings.additionalGeometryPointRadius,
717
getShapeLayer(), exaggeration, nullptr);
718
myMoveElementViewResizable->myMovingContourRight.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeWidth.back(), s.neteditSizeSettings.additionalGeometryPointRadius,
719
getShapeLayer(), exaggeration, nullptr);
720
} else {
721
const auto parentEdgeBoundary = (getTagProperty()->getTag() == GNE_TAG_POILANE) ? getParentLanes().front()->getParentEdge() : nullptr;
722
if (getShapeImgFile().empty()) {
723
myAdditionalContour.calculateContourCircleShape(s, d, this, getPositionInView(), std::max(myWidth, myHeight) * 0.5, getShapeLayer(), exaggeration, parentEdgeBoundary);
724
} else {
725
myAdditionalContour.calculateContourRectangleShape(s, d, this, getPositionInView(), myHeight * 0.5, myWidth * 0.5, getShapeLayer(), 0, 0, getShapeNaviDegree(), exaggeration, parentEdgeBoundary);
726
}
727
}
728
}
729
730
731
void
732
GNEPOI::setAttribute(SumoXMLAttr key, const std::string& value) {
733
switch (key) {
734
case SUMO_ATTR_ID: {
735
// update microsimID
736
setAdditionalID(value);
737
// set named ID
738
myID = value;
739
break;
740
}
741
case SUMO_ATTR_COLOR:
742
setShapeColor(parse<RGBColor>(value));
743
break;
744
case SUMO_ATTR_LANE:
745
replaceAdditionalParentLanes(value);
746
break;
747
case SUMO_ATTR_POSITION: {
748
if (myTagProperty->getTag() == GNE_TAG_POILANE) {
749
myPosOverLane = parse<double>(value);
750
} else {
751
myPosOverView = parse<Position>(value);
752
}
753
break;
754
}
755
case SUMO_ATTR_POSITION_LAT:
756
myPosLat = parse<double>(value);
757
break;
758
case SUMO_ATTR_LON: {
759
// parse geo attributes
760
Position pos(parse<double>(value), getAttributeDouble(SUMO_ATTR_LAT));
761
// transform to cartesian
762
GeoConvHelper::getFinal().x2cartesian_const(pos);
763
// update view position
764
myPosOverView = pos;
765
break;
766
}
767
case SUMO_ATTR_LAT: {
768
// parse geo attributes
769
Position pos(getAttributeDouble(SUMO_ATTR_LON), parse<double>(value));
770
// transform to cartesian
771
GeoConvHelper::getFinal().x2cartesian_const(pos);
772
// update view position
773
myPosOverView = pos;
774
break;
775
}
776
case SUMO_ATTR_TYPE:
777
setShapeType(value);
778
break;
779
case SUMO_ATTR_ICON:
780
SUMOXMLDefinitions::POIIcons.get(value);
781
break;
782
case SUMO_ATTR_LAYER:
783
if (value.empty()) {
784
setShapeLayer(myTagProperty->getDefaultDoubleValue(key));
785
} else {
786
setShapeLayer(parse<double>(value));
787
}
788
break;
789
case SUMO_ATTR_IMGFILE:
790
// first remove object from grid due img file affect to boundary
791
if (getID().size() > 0) {
792
myNet->removeGLObjectFromGrid(this);
793
}
794
setShapeImgFile(value);
795
// all textures must be refresh
796
GUITexturesHelper::clearTextures();
797
// add object into grid again
798
if (getID().size() > 0) {
799
myNet->addGLObjectIntoGrid(this);
800
}
801
break;
802
case SUMO_ATTR_WIDTH:
803
// set new width
804
myWidth = parse<double>(value);
805
break;
806
case SUMO_ATTR_HEIGHT:
807
// set new height
808
myHeight = parse<double>(value);
809
break;
810
case SUMO_ATTR_ANGLE:
811
setShapeNaviDegree(parse<double>(value));
812
break;
813
case SUMO_ATTR_NAME:
814
myAdditionalName = value;
815
break;
816
case GNE_ATTR_SHIFTLANEINDEX:
817
shiftLaneIndex();
818
break;
819
default:
820
if (getTagProperty()->getTag() == GNE_TAG_POILANE) {
821
return myMoveElementLaneSingle->setMovingAttribute(key, value);
822
} else {
823
return myMoveElementViewResizable->setMovingAttribute(key, value);
824
}
825
}
826
// update boundary (except for template)
827
if (getID().size() > 0) {
828
updateCenteringBoundary(myTagProperty->getTag() != GNE_TAG_POILANE);
829
}
830
}
831
832
/****************************************************************************/
833
834