Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/frames/network/GNEShapeFrame.cpp
169685 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 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, MFXGroupBoxModule, 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
MFXGroupBoxModule(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->myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?
201
myShapeFrameParent->myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",
202
myShapeFrameParent->myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
203
// build shape
204
additionalHandler.parseSumoBaseObject(myShapeFrameParent->myBaseShape);
205
// check if view has to be centered over created GEO POI
206
if (myCenterViewAfterCreationCheckButton->getCheck() == TRUE) {
207
// create a boundary over given GEO Position and center view over it
208
Boundary centerPosition;
209
GeoConvHelper::getFinal().x2cartesian_const(geoPos);
210
centerPosition.add(geoPos);
211
centerPosition = centerPosition.grow(10);
212
myShapeFrameParent->myViewNet->getViewParent()->getView()->centerTo(centerPosition);
213
}
214
}
215
// refresh shape attributes
216
myShapeFrameParent->myShapeAttributesEditor->refreshAttributesEditor();
217
}
218
return 1;
219
}
220
221
222
// ---------------------------------------------------------------------------
223
// GNEShapeFrame - methods
224
// ---------------------------------------------------------------------------
225
226
GNEShapeFrame::GNEShapeFrame(GNEViewParent* viewParent, GNEViewNet* viewNet) :
227
GNEFrame(viewParent, viewNet, TL("Shapes")),
228
myBaseShape(nullptr) {
229
230
// create item Selector module for shapes
231
myShapeTagSelector = new GNETagSelector(this, GNETagProperties::Type::SHAPE, SUMO_TAG_POLY);
232
233
// Create shape parameters
234
myShapeAttributesEditor = new GNEAttributesEditor(this, GNEAttributesEditorType::EditorType::CREATOR);
235
236
// Create drawing controls
237
myDrawingShape = new GNEDrawingShape(this);
238
239
/// @brief create GEOPOICreator
240
myGEOPOICreator = new GEOPOICreator(this);
241
}
242
243
244
GNEShapeFrame::~GNEShapeFrame() {
245
// check if we have to delete base additional object
246
if (myBaseShape) {
247
delete myBaseShape;
248
}
249
}
250
251
252
void
253
GNEShapeFrame::show() {
254
// refresh tag selector
255
myShapeTagSelector->refreshTagSelector();
256
// show frame
257
GNEFrame::show();
258
}
259
260
261
bool
262
GNEShapeFrame::processClick(const Position& clickedPosition, const GNEViewNetHelper::ViewObjectsSelector& viewObjects, bool& updateTemporalShape) {
263
// reset updateTemporalShape
264
updateTemporalShape = false;
265
// check if current selected shape is valid
266
if (myShapeTagSelector->getCurrentTemplateAC() != nullptr) {
267
// get tag
268
SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();
269
// continue depending of tag
270
switch (shapeTag) {
271
case SUMO_TAG_POI:
272
return processClickPOI(shapeTag, clickedPosition);
273
case GNE_TAG_POIGEO:
274
return processClickPOIGeo(clickedPosition);
275
case GNE_TAG_POILANE:
276
return processClickPOILanes(viewObjects);
277
case SUMO_TAG_POLY:
278
case GNE_TAG_JPS_WALKABLEAREA:
279
case GNE_TAG_JPS_OBSTACLE:
280
return processClickPolygons(clickedPosition, updateTemporalShape);
281
default:
282
break;
283
}
284
}
285
myViewNet->setStatusBarText(TL("Current selected shape isn't valid."));
286
return false;
287
}
288
289
290
std::string
291
GNEShapeFrame::getIdsSelected(const FXList* list) {
292
// Obtain Id's of list
293
std::string vectorOfIds;
294
for (int i = 0; i < list->getNumItems(); i++) {
295
if (list->isItemSelected(i)) {
296
if (vectorOfIds.size() > 0) {
297
vectorOfIds += " ";
298
}
299
vectorOfIds += (list->getItem(i)->getText()).text();
300
}
301
}
302
return vectorOfIds;
303
}
304
305
306
GNEDrawingShape*
307
GNEShapeFrame::getDrawingShapeModule() const {
308
return myDrawingShape;
309
}
310
311
312
void
313
GNEShapeFrame::createBaseShapeObject(const SumoXMLTag shapeTag) {
314
// check if baseShape exist, and if yes, delete it
315
if (myBaseShape) {
316
// delete baseShape (and all children)
317
delete myBaseShape;
318
}
319
// just create a base shape
320
myBaseShape = new CommonXMLStructure::SumoBaseObject(nullptr);
321
// set tag
322
myBaseShape->setTag(shapeTag);
323
}
324
325
326
bool
327
GNEShapeFrame::shapeDrawed() {
328
// show warning dialogbox and stop check if input parameters are valid
329
if (!myShapeAttributesEditor->checkAttributes(true)) {
330
return false;
331
} else if (myDrawingShape->getTemporalShape().size() == 0) {
332
WRITE_WARNING(TL("Polygon shape cannot be empty"));
333
return false;
334
} else {
335
// get tag
336
SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();
337
// create baseShape object
338
createBaseShapeObject(shapeTag);
339
// obtain shape attributes and values
340
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
341
// obtain shape and check if has to be closed
342
PositionVector temporalShape = myDrawingShape->getTemporalShape();
343
if ((myBaseShape->hasBoolAttribute(GNE_ATTR_CLOSE_SHAPE) && myBaseShape->getBoolAttribute(GNE_ATTR_CLOSE_SHAPE)) ||
344
(shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {
345
temporalShape.closePolygon();
346
}
347
myBaseShape->addPositionVectorAttribute(SUMO_ATTR_SHAPE, temporalShape);
348
// declare additional handler
349
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_DEMAND_FILE) ?
350
myBaseShape->getStringAttribute(GNE_ATTR_DEMAND_FILE) : "",
351
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
352
// build shape
353
additionalHandler.parseSumoBaseObject(myBaseShape);
354
// refresh shape attributes
355
myShapeAttributesEditor->refreshAttributesEditor();
356
// shape added, then return true;
357
return true;
358
}
359
}
360
361
362
void
363
GNEShapeFrame::tagSelected() {
364
if (myShapeTagSelector->getCurrentTemplateAC()) {
365
// show editors
366
myShapeAttributesEditor->showAttributesEditor(myShapeTagSelector->getCurrentTemplateAC(), true);
367
// get shape tag
368
SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();
369
// Check if drawing mode has to be shown
370
if ((shapeTag == SUMO_TAG_POLY) || (shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {
371
myDrawingShape->showDrawingShape();
372
} else {
373
myDrawingShape->hideDrawingShape();
374
}
375
// Check if GEO POI Creator has to be shown
376
if (shapeTag == GNE_TAG_POIGEO) {
377
myGEOPOICreator->showGEOPOICreatorModule();
378
} else {
379
myGEOPOICreator->hideGEOPOICreatorModule();
380
}
381
} else {
382
// hide all widgets
383
myShapeAttributesEditor->hideAttributesEditor();
384
myDrawingShape->hideDrawingShape();
385
myGEOPOICreator->hideGEOPOICreatorModule();
386
}
387
}
388
389
390
bool
391
GNEShapeFrame::processClickPolygons(const Position& clickedPosition, bool& updateTemporalShape) {
392
if (myDrawingShape->isDrawing()) {
393
// add or delete a new point depending of flag "delete last created point"
394
if (myDrawingShape->getDeleteLastCreatedPoint()) {
395
myDrawingShape->removeLastPoint();
396
} else {
397
myDrawingShape->addNewPoint(clickedPosition);
398
}
399
// set temporal shape
400
updateTemporalShape = true;
401
return true;
402
} else {
403
return false;
404
}
405
}
406
407
408
bool
409
GNEShapeFrame::processClickPOI(SumoXMLTag POITag, const Position& clickedPosition) {
410
// show warning dialogbox and stop if input parameters are invalid
411
if (!myShapeAttributesEditor->checkAttributes(true)) {
412
return false;
413
}
414
// create baseShape object
415
createBaseShapeObject(POITag);
416
// obtain shape attributes and values
417
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
418
// add X-Y
419
myBaseShape->addDoubleAttribute(SUMO_ATTR_X, clickedPosition.x());
420
myBaseShape->addDoubleAttribute(SUMO_ATTR_Y, clickedPosition.y());
421
// set GEO Position as false (because we have created POI clicking over View
422
myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, false);
423
// declare additional handler
424
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?
425
myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",
426
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
427
// build shape
428
additionalHandler.parseSumoBaseObject(myBaseShape);
429
// refresh shape attributes
430
myShapeAttributesEditor->refreshAttributesEditor();
431
// shape added, then return true
432
return true;
433
}
434
435
436
bool
437
GNEShapeFrame::processClickPOIGeo(const Position& clickedPosition) {
438
// show warning dialogbox and stop if input parameters are invalid
439
if (!myShapeAttributesEditor->checkAttributes(true)) {
440
return false;
441
}
442
// create baseShape object
443
createBaseShapeObject(SUMO_TAG_POI);
444
// obtain shape attributes and values
445
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
446
// convert position to cartesian
447
Position GEOPos = clickedPosition;
448
GeoConvHelper::getFinal().cartesian2geo(GEOPos);
449
// add X-Y in geo format
450
myBaseShape->addDoubleAttribute(SUMO_ATTR_LON, GEOPos.x());
451
myBaseShape->addDoubleAttribute(SUMO_ATTR_LAT, GEOPos.y());
452
// set GEO Position as false (because we have created POI clicking over View
453
myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);
454
// declare additional handler
455
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?
456
myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",
457
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
458
// build shape
459
additionalHandler.parseSumoBaseObject(myBaseShape);
460
// refresh shape attributes
461
myShapeAttributesEditor->refreshAttributesEditor();
462
// shape added, then return true
463
return true;
464
}
465
466
467
bool
468
GNEShapeFrame::processClickPOILanes(const GNEViewNetHelper::ViewObjectsSelector& viewObjects) {
469
// abort if lane is nullptr
470
if (viewObjects.getLaneFront() == nullptr) {
471
WRITE_WARNING(TL("POILane can be only placed over lanes"));
472
return false;
473
}
474
// show warning dialogbox and stop if input parameters are invalid
475
if (!myShapeAttributesEditor->checkAttributes(true)) {
476
return false;
477
}
478
// create baseShape object
479
createBaseShapeObject(SUMO_TAG_POI);
480
// obtain shape attributes and values
481
myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);
482
// obtain Lane
483
myBaseShape->addStringAttribute(SUMO_ATTR_LANE, viewObjects.getLaneFront()->getID());
484
// obtain position over lane
485
myBaseShape->addDoubleAttribute(SUMO_ATTR_POSITION, viewObjects.getLaneFront()->getLaneShape().nearest_offset_to_point2D(
486
myViewNet->snapToActiveGrid(myViewNet->getPositionInformation())) /
487
viewObjects.getLaneFront()->getLengthGeometryFactor());
488
// declare additional handler
489
GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?
490
myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",
491
myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());
492
// build shape
493
additionalHandler.parseSumoBaseObject(myBaseShape);
494
// refresh shape attributes
495
myShapeAttributesEditor->refreshAttributesEditor();
496
// shape added, then return true
497
return true;
498
}
499
500
/****************************************************************************/
501
502