Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/frames/network/GNEShapeFrame.cpp
193674 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 GNEShapeFrame.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Aug 2017
17
///
18
// The Widget for add polygons
19
/****************************************************************************/
20
21
#include <netedit/GNEApplicationWindow.h>
22
#include <netedit/GNENet.h>
23
#include <netedit/GNEViewParent.h>
24
#include <netedit/elements/additional/GNEAdditionalHandler.h>
25
#include <netedit/frames/GNEAttributesEditor.h>
26
#include <netedit/frames/GNEDrawingShape.h>
27
#include <netedit/frames/GNETagSelector.h>
28
#include <utils/foxtools/MFXDynamicLabel.h>
29
#include <utils/gui/div/GUIDesigns.h>
30
#include <utils/gui/div/GUIUserIO.h>
31
32
#include "GNEShapeFrame.h"
33
34
// ===========================================================================
35
// FOX callback mapping
36
// ===========================================================================
37
38
FXDEFMAP(GNEShapeFrame::GEOPOICreator) GEOPOICreatorMap[] = {
39
FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_ATTRIBUTE, GNEShapeFrame::GEOPOICreator::onCmdSetCoordinates),
40
FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_OPERATION, GNEShapeFrame::GEOPOICreator::onCmdSetFormat),
41
FXMAPFUNC(SEL_COMMAND, MID_GNE_CREATE, GNEShapeFrame::GEOPOICreator::onCmdCreateGEOPOI),
42
};
43
44
// Object implementation
45
FXIMPLEMENT(GNEShapeFrame::GEOPOICreator, GNEGroupBoxModule, GEOPOICreatorMap, ARRAYNUMBER(GEOPOICreatorMap))
46
47
48
// ===========================================================================
49
// method definitions
50
// ===========================================================================
51
52
// ---------------------------------------------------------------------------
53
// GNEShapeFrame::GEOPOICreator - methods
54
// ---------------------------------------------------------------------------
55
56
GNEShapeFrame::GEOPOICreator::GEOPOICreator(GNEShapeFrame* polygonFrameParent) :
57
GNEGroupBoxModule(polygonFrameParent, TL("GEO POI Creator")),
58
myShapeFrameParent(polygonFrameParent) {
59
// create RadioButtons for formats
60
myLonLatRadioButton = new FXRadioButton(getCollapsableFrame(), TL("Format: Lon-Lat"), this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);
61
myLatLonRadioButton = new FXRadioButton(getCollapsableFrame(), TL("Format: Lat-Lon"), this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);
62
// set lat-lon as default
63
myLatLonRadioButton->setCheck(TRUE);
64
// create text field for coordinates
65
myCoordinatesTextField = new FXTextField(getCollapsableFrame(), GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);
66
// create checkBox
67
myCenterViewAfterCreationCheckButton = new FXCheckButton(getCollapsableFrame(), TL("Center View after creation"), this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButton);
68
// create button for create GEO POIs
69
myCreateGEOPOIButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Create GEO POI (clipboard)"), "", "", nullptr, this, MID_GNE_CREATE, GUIDesignButton);
70
// create information label
71
myLabelCartesianPosition = new MFXDynamicLabel(getCollapsableFrame(),
72
(TL("Cartesian equivalence:") + std::string("\n") +
73
TL("- X = give valid longitude") + std::string("\n") +
74
TL("- Y = give valid latitude")).c_str(),
75
0, GUIDesignLabelFrameInformation);
76
}
77
78
79
GNEShapeFrame::GEOPOICreator::~GEOPOICreator() {}
80
81
82
void
83
GNEShapeFrame::GEOPOICreator::showGEOPOICreatorModule() {
84
// check if there is an GEO Proj string is defined
85
if (GeoConvHelper::getFinal().getProjString() != "!") {
86
myCoordinatesTextField->enable();
87
myCoordinatesTextField->setText("");
88
myCoordinatesTextField->enable();
89
myCreateGEOPOIButton->enable();
90
} else {
91
myCoordinatesTextField->setText(TL("No geo-conversion defined"));
92
myCoordinatesTextField->disable();
93
myCreateGEOPOIButton->disable();
94
}
95
show();
96
}
97
98
99
void
100
GNEShapeFrame::GEOPOICreator::hideGEOPOICreatorModule() {
101
hide();
102
}
103
104
105
long
106
GNEShapeFrame::GEOPOICreator::onCmdSetCoordinates(FXObject*, FXSelector, void*) {
107
// check if input contains spaces
108
std::string input = myCoordinatesTextField->getText().text();
109
std::string inputWithoutSpaces;
110
for (const auto& i : input) {
111
if (i != ' ') {
112
inputWithoutSpaces.push_back(i);
113
}
114
}
115
// if input contains spaces, call this function again, and in other case set red text color
116
if (input.size() != inputWithoutSpaces.size()) {
117
myCoordinatesTextField->setText(inputWithoutSpaces.c_str());
118
}
119
if (inputWithoutSpaces.size() > 0) {
120
myCreateGEOPOIButton->setText(TL("Create GEO POI"));
121
} else {
122
myCreateGEOPOIButton->setText(TL("Create GEO POI (clipboard)"));
123
}
124
// simply check if given value can be parsed to Position
125
if (GNEAttributeCarrier::canParse<Position>(myCoordinatesTextField->getText().text())) {
126
myCoordinatesTextField->setTextColor(GUIDesignTextColorBlack);
127
myCoordinatesTextField->killFocus();
128
// convert coordinates into lon-lat
129
Position geoPos = GNEAttributeCarrier::parse<Position>(myCoordinatesTextField->getText().text());
130
if (myLatLonRadioButton->getCheck() == TRUE) {
131
geoPos.swapXY();
132
}
133
GeoConvHelper::getFinal().x2cartesian_const(geoPos);
134
// check if GEO Position has to be swapped
135
// update myLabelCartesianPosition
136
myLabelCartesianPosition->setText(
137
(TL("Cartesian equivalence:") + std::string("\n- X = ") + toString(geoPos.x()) + std::string("\n- Y = ") + toString(geoPos.y())).c_str());
138
} else {
139
myCoordinatesTextField->setTextColor(GUIDesignTextColorRed);
140
myLabelCartesianPosition->setText(
141
(TL("Cartesian equivalence:") + std::string("\n") +
142
TL("- X = give valid longitude") + std::string("\n") +
143
TL("- Y = give valid latitude")).c_str());
144
};
145
return 1;
146
}
147
148
149
long
150
GNEShapeFrame::GEOPOICreator::onCmdSetFormat(FXObject* obj, FXSelector, void*) {
151
//disable other radio button depending of selected option
152
if (obj == myLonLatRadioButton) {
153
myLonLatRadioButton->setCheck(TRUE);
154
myLatLonRadioButton->setCheck(FALSE);
155
} else if (obj == myLatLonRadioButton) {
156
myLonLatRadioButton->setCheck(FALSE);
157
myLatLonRadioButton->setCheck(TRUE);
158
}
159
// in both cases call onCmdSetCoordinates(0,0,0) to set new cartesian equivalence
160
onCmdSetCoordinates(0, 0, 0);
161
return 1;
162
}
163
164
165
long
166
GNEShapeFrame::GEOPOICreator::onCmdCreateGEOPOI(FXObject*, FXSelector, void*) {
167
// first check if current GEO Position is valid
168
if (myShapeFrameParent->myShapeAttributesEditor->checkAttributes(true)) {
169
std::string geoPosStr = myCoordinatesTextField->getText().text();
170
if (geoPosStr.empty()) {
171
// use clipboard
172
WRITE_WARNING(TL("Using clipboard"));
173
geoPosStr = GUIUserIO::copyFromClipboard(*getApp());
174
myCoordinatesTextField->setText(geoPosStr.c_str());
175
// remove spaces, update cartesian value
176
onCmdSetCoordinates(0, 0, 0);
177
geoPosStr = myCoordinatesTextField->getText().text();
178
myCoordinatesTextField->setText("");
179
myCreateGEOPOIButton->setText(TL("Create GEO POI (clipboard)"));
180
}
181
if (GNEAttributeCarrier::canParse<Position>(geoPosStr)) {
182
// create baseShape object
183
myShapeFrameParent->createBaseShapeObject(SUMO_TAG_POI);
184
// obtain shape attributes and values
185
myShapeFrameParent->myShapeAttributesEditor->fillSumoBaseObject(myShapeFrameParent->myBaseShape);
186
// force GEO attribute to true and obtain position
187
myShapeFrameParent->myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);
188
Position geoPos = GNEAttributeCarrier::parse<Position>(geoPosStr);
189
// convert coordinates into lon-lat
190
if (myLatLonRadioButton->getCheck() == TRUE) {
191
geoPos.swapXY();
192
}
193
// add lon/lat
194
myShapeFrameParent->myBaseShape->addDoubleAttribute(SUMO_ATTR_LON, geoPos.x());
195
myShapeFrameParent->myBaseShape->addDoubleAttribute(SUMO_ATTR_LAT, geoPos.y());
196
// set GEO Position as true
197
myShapeFrameParent->myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);
198
// declare additional handler
199
GNEAdditionalHandler additionalHandler(myShapeFrameParent->myViewNet->getNet(),
200
myShapeFrameParent->myViewNet->getNet()->getACTemplates()->getTemplateAC(GNE_TAG_POIGEO)->getFileBucket(),
201
myShapeFrameParent->myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
202
// build shape
203
additionalHandler.parseSumoBaseObject(myShapeFrameParent->myBaseShape);
204
// check if view has to be centered over created GEO POI
205
if (myCenterViewAfterCreationCheckButton->getCheck() == TRUE) {
206
// create a boundary over given GEO Position and center view over it
207
Boundary centerPosition;
208
GeoConvHelper::getFinal().x2cartesian_const(geoPos);
209
centerPosition.add(geoPos);
210
centerPosition = centerPosition.grow(10);
211
myShapeFrameParent->myViewNet->getViewParent()->getView()->centerTo(centerPosition);
212
}
213
}
214
// refresh shape attributes
215
myShapeFrameParent->myShapeAttributesEditor->refreshAttributesEditor();
216
}
217
return 1;
218
}
219
220
221
// ---------------------------------------------------------------------------
222
// GNEShapeFrame - methods
223
// ---------------------------------------------------------------------------
224
225
GNEShapeFrame::GNEShapeFrame(GNEViewParent* viewParent, GNEViewNet* viewNet) :
226
GNEFrame(viewParent, viewNet, TL("Shapes")),
227
myBaseShape(nullptr) {
228
229
// create item Selector module for shapes
230
myShapeTagSelector = new GNETagSelector(this, GNETagProperties::Type::SHAPE, SUMO_TAG_POLY);
231
232
// Create shape parameters
233
myShapeAttributesEditor = new GNEAttributesEditor(this, GNEAttributesEditorType::EditorType::CREATOR);
234
235
// Create drawing controls
236
myDrawingShape = new GNEDrawingShape(this);
237
238
/// @brief create GEOPOICreator
239
myGEOPOICreator = new GEOPOICreator(this);
240
}
241
242
243
GNEShapeFrame::~GNEShapeFrame() {
244
// check if we have to delete base additional object
245
if (myBaseShape) {
246
delete myBaseShape;
247
}
248
}
249
250
251
void
252
GNEShapeFrame::show() {
253
// refresh tag selector
254
myShapeTagSelector->refreshTagSelector();
255
// show frame
256
GNEFrame::show();
257
}
258
259
260
bool
261
GNEShapeFrame::processClick(const Position& clickedPosition, const GNEViewNetHelper::ViewObjectsSelector& viewObjects, bool& updateTemporalShape) {
262
// reset updateTemporalShape
263
updateTemporalShape = false;
264
// check if current selected shape is valid
265
if (myShapeTagSelector->getCurrentTemplateAC() != nullptr) {
266
// get tag
267
SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();
268
// continue depending of tag
269
switch (shapeTag) {
270
case SUMO_TAG_POI:
271
return processClickPOI(shapeTag, clickedPosition);
272
case GNE_TAG_POIGEO:
273
return processClickPOIGeo(clickedPosition);
274
case GNE_TAG_POILANE:
275
return processClickPOILanes(viewObjects);
276
case SUMO_TAG_POLY:
277
case GNE_TAG_JPS_WALKABLEAREA:
278
case GNE_TAG_JPS_OBSTACLE:
279
return processClickPolygons(clickedPosition, updateTemporalShape);
280
default:
281
break;
282
}
283
}
284
myViewNet->setStatusBarText(TL("Current selected shape isn't valid."));
285
return false;
286
}
287
288
289
std::string
290
GNEShapeFrame::getIdsSelected(const FXList* list) {
291
// Obtain Id's of list
292
std::string vectorOfIds;
293
for (int i = 0; i < list->getNumItems(); i++) {
294
if (list->isItemSelected(i)) {
295
if (vectorOfIds.size() > 0) {
296
vectorOfIds += " ";
297
}
298
vectorOfIds += (list->getItem(i)->getText()).text();
299
}
300
}
301
return vectorOfIds;
302
}
303
304
305
GNEDrawingShape*
306
GNEShapeFrame::getDrawingShapeModule() const {
307
return myDrawingShape;
308
}
309
310
311
void
312
GNEShapeFrame::createBaseShapeObject(const SumoXMLTag shapeTag) {
313
// check if baseShape exist, and if yes, delete it
314
if (myBaseShape) {
315
// delete baseShape (and all children)
316
delete myBaseShape;
317
}
318
// just create a base shape
319
myBaseShape = new CommonXMLStructure::SumoBaseObject(nullptr);
320
// set tag
321
myBaseShape->setTag(shapeTag);
322
}
323
324
325
bool
326
GNEShapeFrame::shapeDrawed() {
327
// show warning dialogbox and stop check if input parameters are valid
328
if (!myShapeAttributesEditor->checkAttributes(true)) {
329
return false;
330
} else if (myDrawingShape->getTemporalShape().size() == 0) {
331
WRITE_WARNING(TL("Polygon shape cannot be empty"));
332
return false;
333
} else {
334
// get tag
335
SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();
336
// create baseShape object
337
createBaseShapeObject(shapeTag);
338
// obtain shape attributes and values
339
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
340
// obtain shape and check if has to be closed
341
PositionVector temporalShape = myDrawingShape->getTemporalShape();
342
if ((myBaseShape->hasBoolAttribute(GNE_ATTR_CLOSE_SHAPE) && myBaseShape->getBoolAttribute(GNE_ATTR_CLOSE_SHAPE)) ||
343
(shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {
344
temporalShape.closePolygon();
345
}
346
myBaseShape->addPositionVectorAttribute(SUMO_ATTR_SHAPE, temporalShape);
347
// declare additional handler
348
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(shapeTag)->getFileBucket(),
349
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
350
// build shape
351
additionalHandler.parseSumoBaseObject(myBaseShape);
352
// refresh shape attributes
353
myShapeAttributesEditor->refreshAttributesEditor();
354
// shape added, then return true;
355
return true;
356
}
357
}
358
359
360
void
361
GNEShapeFrame::tagSelected() {
362
if (myShapeTagSelector->getCurrentTemplateAC()) {
363
// show editors
364
myShapeAttributesEditor->showAttributesEditor(myShapeTagSelector->getCurrentTemplateAC(), true);
365
// get shape tag
366
SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();
367
// Check if drawing mode has to be shown
368
if ((shapeTag == SUMO_TAG_POLY) || (shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {
369
myDrawingShape->showDrawingShape();
370
} else {
371
myDrawingShape->hideDrawingShape();
372
}
373
// Check if GEO POI Creator has to be shown
374
if (shapeTag == GNE_TAG_POIGEO) {
375
myGEOPOICreator->showGEOPOICreatorModule();
376
} else {
377
myGEOPOICreator->hideGEOPOICreatorModule();
378
}
379
} else {
380
// hide all widgets
381
myShapeAttributesEditor->hideAttributesEditor();
382
myDrawingShape->hideDrawingShape();
383
myGEOPOICreator->hideGEOPOICreatorModule();
384
}
385
}
386
387
388
bool
389
GNEShapeFrame::processClickPolygons(const Position& clickedPosition, bool& updateTemporalShape) {
390
if (myDrawingShape->isDrawing()) {
391
// add or delete a new point depending of flag "delete last created point"
392
if (myDrawingShape->getDeleteLastCreatedPoint()) {
393
myDrawingShape->removeLastPoint();
394
} else {
395
myDrawingShape->addNewPoint(clickedPosition);
396
}
397
// set temporal shape
398
updateTemporalShape = true;
399
return true;
400
} else {
401
return false;
402
}
403
}
404
405
406
bool
407
GNEShapeFrame::processClickPOI(SumoXMLTag POITag, const Position& clickedPosition) {
408
// show warning dialogbox and stop if input parameters are invalid
409
if (!myShapeAttributesEditor->checkAttributes(true)) {
410
return false;
411
}
412
// create baseShape object
413
createBaseShapeObject(POITag);
414
// obtain shape attributes and values
415
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
416
// add X-Y
417
myBaseShape->addDoubleAttribute(SUMO_ATTR_X, clickedPosition.x());
418
myBaseShape->addDoubleAttribute(SUMO_ATTR_Y, clickedPosition.y());
419
// set GEO Position as false (because we have created POI clicking over View
420
myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, false);
421
// declare additional handler
422
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(POITag)->getFileBucket(),
423
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
424
// build shape
425
additionalHandler.parseSumoBaseObject(myBaseShape);
426
// refresh shape attributes
427
myShapeAttributesEditor->refreshAttributesEditor();
428
// shape added, then return true
429
return true;
430
}
431
432
433
bool
434
GNEShapeFrame::processClickPOIGeo(const Position& clickedPosition) {
435
// show warning dialogbox and stop if input parameters are invalid
436
if (!myShapeAttributesEditor->checkAttributes(true)) {
437
return false;
438
}
439
// create baseShape object
440
createBaseShapeObject(SUMO_TAG_POI);
441
// obtain shape attributes and values
442
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
443
// convert position to cartesian
444
Position GEOPos = clickedPosition;
445
GeoConvHelper::getFinal().cartesian2geo(GEOPos);
446
// add X-Y in geo format
447
myBaseShape->addDoubleAttribute(SUMO_ATTR_LON, GEOPos.x());
448
myBaseShape->addDoubleAttribute(SUMO_ATTR_LAT, GEOPos.y());
449
// set GEO Position as false (because we have created POI clicking over View
450
myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);
451
// declare additional handler
452
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(GNE_TAG_POIGEO)->getFileBucket(),
453
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
454
// build shape
455
additionalHandler.parseSumoBaseObject(myBaseShape);
456
// refresh shape attributes
457
myShapeAttributesEditor->refreshAttributesEditor();
458
// shape added, then return true
459
return true;
460
}
461
462
463
bool
464
GNEShapeFrame::processClickPOILanes(const GNEViewNetHelper::ViewObjectsSelector& viewObjects) {
465
// abort if lane is nullptr
466
if (viewObjects.getLaneFront() == nullptr) {
467
WRITE_WARNING(TL("POILane can be only placed over lanes"));
468
return false;
469
}
470
// show warning dialogbox and stop if input parameters are invalid
471
if (!myShapeAttributesEditor->checkAttributes(true)) {
472
return false;
473
}
474
// create baseShape object
475
createBaseShapeObject(SUMO_TAG_POI);
476
// obtain shape attributes and values
477
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
478
// obtain Lane
479
myBaseShape->addStringAttribute(SUMO_ATTR_LANE, viewObjects.getLaneFront()->getID());
480
// obtain position over lane
481
myBaseShape->addDoubleAttribute(SUMO_ATTR_POSITION, viewObjects.getLaneFront()->getLaneShape().nearest_offset_to_point2D(
482
myViewNet->snapToActiveGrid(myViewNet->getPositionInformation())) /
483
viewObjects.getLaneFront()->getLengthGeometryFactor());
484
// declare additional handler
485
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(GNE_TAG_POILANE)->getFileBucket(),
486
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
487
// build shape
488
additionalHandler.parseSumoBaseObject(myBaseShape);
489
// refresh shape attributes
490
myShapeAttributesEditor->refreshAttributesEditor();
491
// shape added, then return true
492
return true;
493
}
494
495
/****************************************************************************/
496
497