1 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
2 * Copyright 2015 Pelican Mapping
3 * http://osgearth.org
4 *
5 * osgEarth is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
15 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
16 * IN THE SOFTWARE.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
20 */
21
22 #include <osgEarthQt/AnnotationDialogs>
23 #include <osgEarthQt/Common>
24
25 #include <osgEarthAnnotation/Draggers>
26 #include <osgEarth/IntersectionPicker>
27 #include <osgEarthAnnotation/AnnotationData>
28 #include <osgEarthAnnotation/AnnotationEditing>
29 #include <osgEarthAnnotation/EllipseNode>
30 #include <osgEarthAnnotation/FeatureNode>
31 #include <osgEarthAnnotation/PlaceNode>
32 #include <osgEarthSymbology/Geometry>
33
34 #include <QCheckBox>
35 #include <QColor>
36 #include <QColorDialog>
37 #include <QDialog>
38 #include <QGLWidget>
39 #include <QHBoxLayout>
40 #include <QImage>
41 #include <QLabel>
42 #include <QLineEdit>
43 #include <QPushButton>
44 #include <QVBoxLayout>
45
46 using namespace osgEarth;
47 using namespace osgEarth::QtGui;
48
49
50 #define ANNOTATION_PATH_WIDTH 12.0f
51
52
53 //---------------------------------------------------------------------------
54
BaseAnnotationDialog(osgEarth::MapNode * mapNode,const ViewVector & views,QWidget * parent,Qt::WindowFlags f)55 BaseAnnotationDialog::BaseAnnotationDialog(osgEarth::MapNode* mapNode, const ViewVector& views, QWidget* parent, Qt::WindowFlags f)
56 : QDialog(parent, f), _mapNode(mapNode), _views(views.size())
57 {
58 initDefaultUi();
59
60 std::copy(views.begin(), views.end(), _views.begin());
61 }
62
initDefaultUi()63 void BaseAnnotationDialog::initDefaultUi()
64 {
65 // main layout
66 QVBoxLayout* vLayout = new QVBoxLayout;
67 setLayout(vLayout);
68
69 // name layout and widgets
70 QHBoxLayout* hb1 = new QHBoxLayout;
71 hb1->addWidget(new QLabel(tr("Name")));
72
73 _nameEdit = new QLineEdit;
74 hb1->addWidget(_nameEdit);
75
76 vLayout->addLayout(hb1);
77
78 // description layout and widgets
79 QVBoxLayout* vb1 = new QVBoxLayout;
80 vb1->addWidget(new QLabel(tr("Description")));
81
82 _descriptionEdit = new QLineEdit();
83 vb1->addWidget(_descriptionEdit);
84
85 vLayout->addLayout(vb1);
86
87 // empty layout for custom content
88 _customLayout = new QVBoxLayout;
89 vLayout->addLayout(_customLayout);
90
91 // ok/cancel buttons
92 vLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
93
94 QHBoxLayout* hb2 = new QHBoxLayout;
95
96 hb2->addStretch();
97
98 _okButton = new QPushButton(tr("OK"));
99 hb2->addWidget(_okButton);
100
101 QPushButton* cancelButton = new QPushButton(tr("Cancel"));
102 hb2->addWidget(cancelButton);
103
104 vLayout->addLayout(hb2);
105
106 // wire up ui events
107 connect(_okButton, SIGNAL(clicked()), this, SLOT(accept()));
108 connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
109 }
110
updateButtonColorStyle(QPushButton * button,const QColor & color)111 void BaseAnnotationDialog::updateButtonColorStyle(QPushButton* button, const QColor& color)
112 {
113 if (button)
114 {
115 if (color.alpha() == 0)
116 {
117 button->setStyleSheet("QPushButton { color: black; }");
118 }
119 else
120 {
121 int invR = 255 - color.red();
122 int invG = 255 - color.green();
123 int invB = 255 - color.blue();
124 QColor invColor(invR, invG, invB);
125
126 button->setStyleSheet("QPushButton { color: " + invColor.name() + "; background-color: " + color.name() + " }");
127 }
128 }
129 }
130
131 //---------------------------------------------------------------------------
132
AddMarkerDialog(osg::Group * root,osgEarth::MapNode * mapNode,const ViewVector & views,osgEarth::Annotation::PlaceNode * marker,QWidget * parent,Qt::WindowFlags f)133 AddMarkerDialog::AddMarkerDialog(osg::Group* root, osgEarth::MapNode* mapNode, const ViewVector& views, osgEarth::Annotation::PlaceNode* marker, QWidget* parent, Qt::WindowFlags f)
134 : BaseAnnotationDialog(mapNode, views, parent, f), _root(root), _altName(""), _editing(marker ? true : false), _inPosition(marker ? marker->getPosition() : osgEarth::GeoPoint()), _inNameSet(false)
135 {
136 initialize();
137
138 if (marker)
139 {
140 osgEarth::Annotation::AnnotationData* data = marker->getAnnotationData();
141
142 //Get marker text/name
143 _inName = marker->getText().length() > 0 ? marker->getText() : (data ? data->getName() : "");
144 _nameEdit->setText(tr(_inName.c_str()));
145 if (marker->getText().length() > 0 && data && data->getName().length() > 0 && marker->getText() != data->getName())
146 _altName = data->getName();
147
148 //Get marker description
149 _inDescription = data ? data->getDescription() : "";
150 _descriptionEdit->setText(tr(_inDescription.c_str()));
151
152 //Set marker checkbox
153 _inNameSet = marker->getText().length() > 0;
154 _nameCheckbox->setChecked(_inNameSet);
155
156 _placeNode = marker;
157 }
158
159 if (_mapNode.valid() && _views.size() > 0)
160 {
161 _guiHandler = new AddPointsMouseHandler(this, _mapNode.get(), 0L, false);
162 for (ViewVector::const_iterator it = views.begin(); it != views.end(); ++it)
163 (*it)->addEventHandler(_guiHandler.get());
164 }
165 }
166
initialize()167 void AddMarkerDialog::initialize()
168 {
169 _okButton->setEnabled(_editing);
170
171 _nameEdit->setText(tr("New Marker"));
172
173 // add name display checkbox to the dialog
174 _nameCheckbox = new QCheckBox(tr("Display name"));
175 _nameCheckbox->setCheckState(Qt::Checked);
176 _customLayout->addWidget(_nameCheckbox);
177
178 // load marker image
179 QImage image(":/images/marker.png");
180 QImage glImage = QGLWidget::convertToGLFormat(image);
181
182 unsigned char* data = new unsigned char[glImage.byteCount()];
183 for(int i=0; i<glImage.byteCount(); i++)
184 {
185 data[i] = glImage.bits()[i];
186 }
187
188 _markerImage = new osg::Image();
189 _markerImage->setImage(glImage.width(),
190 glImage.height(),
191 1,
192 4,
193 GL_RGBA,
194 GL_UNSIGNED_BYTE,
195 data,
196 osg::Image::USE_NEW_DELETE,
197 1);
198
199 // setup placemark style
200 _placeStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
201
202 // wire up UI events
203 connect(_nameCheckbox, SIGNAL(stateChanged(int)), this, SLOT(onNameCheckStateChanged(int)));
204 connect(_nameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(onNameTextChanged(const QString&)));
205 }
206
clearDisplay()207 void AddMarkerDialog::clearDisplay()
208 {
209 if (!_editing && _root.valid())
210 _root->removeChild(_placeNode);
211
212 if (_guiHandler.valid())
213 for (ViewVector::const_iterator it = _views.begin(); it != _views.end(); ++it)
214 (*it)->removeEventHandler(_guiHandler.get());
215 }
216
resetValues()217 void AddMarkerDialog::resetValues()
218 {
219 _nameEdit->setText(_inNameSet ? tr(_inName.c_str()) : "");
220 _descriptionEdit->setText(tr(_inDescription.c_str()));
221
222 if (_editing)
223 {
224 _placeNode->setPosition(_inPosition);
225 }
226 else
227 {
228 if (_root.valid())
229 _root->removeChild(_placeNode);
230 }
231 }
232
mapMouseClick(const osgEarth::GeoPoint & point,int button)233 void AddMarkerDialog::mapMouseClick(const osgEarth::GeoPoint& point, int button)
234 {
235 if (button == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
236 {
237 if (!_placeNode.valid() && _root.valid())
238 {
239 _placeNode = new osgEarth::Annotation::PlaceNode(_mapNode, point, _markerImage, _nameCheckbox->checkState() == Qt::Checked ? getName() : "", _placeStyle);
240
241 if (_root.valid())
242 _root->addChild(_placeNode);
243 }
244 else
245 {
246 _placeNode->setPosition(point);
247 }
248
249 _okButton->setEnabled(true);
250 }
251 }
252
accept()253 void AddMarkerDialog::accept()
254 {
255 clearDisplay();
256
257 if (_placeNode.valid())
258 {
259 osgEarth::Annotation::AnnotationData* annoData = new osgEarth::Annotation::AnnotationData();
260 annoData->setName(_altName.length() > 0 ? _altName : getName());
261 annoData->setDescription(getDescription());
262 Viewpoint vp;
263 vp.focalPoint() = _placeNode->getPosition();
264 vp.pitch()->set(-90.0, Units::DEGREES);
265 vp.range()->set(1e5, Units::METERS);
266 annoData->setViewpoint( vp );
267 //annoData->setViewpoint(osgEarth::Viewpoint(_placeNode->getPosition().vec3d(), 0.0, -90.0, 1e5, _placeNode->getPosition().getSRS()));
268 _placeNode->setAnnotationData(annoData);
269 }
270
271 QDialog::accept();
272 }
273
reject()274 void AddMarkerDialog::reject()
275 {
276 resetValues();
277 clearDisplay();
278 QDialog::reject();
279 }
280
closeEvent(QCloseEvent * event)281 void AddMarkerDialog::closeEvent(QCloseEvent* event)
282 {
283 clearDisplay();
284 QDialog::closeEvent(event);
285 }
286
onNameCheckStateChanged(int state)287 void AddMarkerDialog::onNameCheckStateChanged(int state)
288 {
289 bool checked = state == Qt::Checked;
290 if (_placeNode.valid())
291 _placeNode->setText(checked ? getName() : "");
292 }
293
onNameTextChanged(const QString & text)294 void AddMarkerDialog::onNameTextChanged(const QString& text)
295 {
296 if (_placeNode.valid() && _nameCheckbox->checkState() == Qt::Checked)
297 _placeNode->setText(getName());
298 }
299
300 //---------------------------------------------------------------------------
301
AddPathDialog(osg::Group * root,osgEarth::MapNode * mapNode,const ViewVector & views,osgEarth::Annotation::FeatureNode * path,QWidget * parent,Qt::WindowFlags f)302 AddPathDialog::AddPathDialog(osg::Group* root, osgEarth::MapNode* mapNode, const ViewVector& views, osgEarth::Annotation::FeatureNode* path, QWidget* parent, Qt::WindowFlags f)
303 : BaseAnnotationDialog(mapNode, views, parent, f), _root(root), _draggers(0L), _pathColor(Color::White), _editing(path ? true : false)
304 {
305 initialize();
306
307 //If editing an existing path
308 if (path)
309 {
310 osgEarth::Annotation::AnnotationData* data = path->getAnnotationData();
311
312 //Get path name
313 _inName = data ? data->getName() : "";
314 _nameEdit->setText(tr(_inName.c_str()));
315
316 //Get path description
317 _inDescription = data ? data->getDescription() : "";
318 _descriptionEdit->setText(tr(_inDescription.c_str()));
319
320 _pathNode = path;
321
322 const osgEarth::Features::Feature* feat = path->getFeature();
323 if (feat)
324 {
325 _inFeature = const_cast<osgEarth::Features::Feature*>(feat);
326
327 //Get path color
328 const osgEarth::Symbology::LineSymbol* lineSymbol = feat->style()->get<LineSymbol>();
329 if (lineSymbol)
330 {
331 _pathColor = lineSymbol->stroke()->color();
332 updateButtonColorStyle(_lineColorButton, QColor::fromRgbF(_pathColor.r(), _pathColor.g(), _pathColor.b(), _pathColor.a()));
333 }
334
335 //Get path clamping
336 const osgEarth::Symbology::AltitudeSymbol* altSymbol = feat->style()->get<AltitudeSymbol>();
337 if (altSymbol)
338 _drapeCheckbox->setChecked(altSymbol->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN);
339
340 //Get path points
341 const osgEarth::Symbology::LineString* pathLine = dynamic_cast<const osgEarth::Symbology::LineString*>(feat->getGeometry());
342 if (pathLine)
343 {
344 for (osgEarth::Symbology::LineString::const_iterator it = pathLine->begin(); it != pathLine->end(); ++it)
345 addPoint(osgEarth::GeoPoint(_mapNode->getMapSRS(), (*it).x(), (*it).y(), (*it).z(), ALTMODE_RELATIVE));
346 }
347 }
348 }
349
350 if (_mapNode.valid() && _views.size() > 0)
351 {
352 _guiHandler = new AddPointsMouseHandler(this, _mapNode.get(), _root);
353 for (ViewVector::const_iterator it = views.begin(); it != views.end(); ++it)
354 (*it)->addEventHandler(_guiHandler.get());
355 }
356 }
357
initialize()358 void AddPathDialog::initialize()
359 {
360 _okButton->setEnabled(false);
361
362 _nameEdit->setText(tr("New Path"));
363
364 // add path color selection button
365 _lineColorButton = new QPushButton(tr("Path Color"));
366 _lineColorButton->setStyleSheet("QPushButton { color: black; background-color: white }");
367 _customLayout->addWidget(_lineColorButton);
368
369 // add name display checkbox to the dialog
370 _drapeCheckbox = new QCheckBox(tr("Clamp to terrain"));
371 _drapeCheckbox->setCheckState(Qt::Checked);
372 _customLayout->addWidget(_drapeCheckbox);
373
374 // wire up UI events
375 connect(_drapeCheckbox, SIGNAL(stateChanged(int)), this, SLOT(onDrapeCheckStateChanged(int)));
376 connect(_lineColorButton, SIGNAL(clicked()), this, SLOT(onLineColorButtonClicked()));
377 }
378
clearDisplay()379 void AddPathDialog::clearDisplay()
380 {
381 if (_root.valid())
382 {
383 if (!_editing)
384 _root->removeChild(_pathNode);
385
386 _root->removeChild(_draggers);
387 }
388
389 if (_guiHandler.valid())
390 {
391 for (ViewVector::const_iterator it = _views.begin(); it != _views.end(); ++it)
392 (*it)->removeEventHandler(_guiHandler.get());
393
394 _guiHandler->clearDisplay();
395 }
396 }
397
resetValues()398 void AddPathDialog::resetValues()
399 {
400 _nameEdit->setText(tr(_inName.c_str()));
401 _descriptionEdit->setText(tr(_inDescription.c_str()));
402
403 if (_inFeature.valid())
404 {
405 _pathNode->setFeature(_inFeature);
406 }
407 else
408 {
409 if (_root.valid())
410 {
411 _root->removeChild(_pathNode);
412 _pathNode = 0L;
413 _pathLine = 0L;
414 }
415 }
416 }
417
mapMouseClick(const osgEarth::GeoPoint & point,int button)418 void AddPathDialog::mapMouseClick(const osgEarth::GeoPoint& point, int button)
419 {
420 if (button == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
421 {
422 addPoint(point);
423 }
424 }
425
refreshFeatureNode()426 void AddPathDialog::refreshFeatureNode()
427 {
428 if (_pathNode.valid() && _pathFeature.valid())
429 {
430 _pathFeature->style()->getOrCreate<LineSymbol>()->stroke()->color() = _pathColor;
431 _pathFeature->style()->getOrCreate<AltitudeSymbol>()->clamping() = _drapeCheckbox->checkState() == Qt::Checked ? AltitudeSymbol::CLAMP_TO_TERRAIN : AltitudeSymbol::CLAMP_ABSOLUTE;
432 _pathNode->setFeature(_pathFeature);
433 }
434 }
435
createPointDragger(int index,const osgEarth::GeoPoint & point)436 void AddPathDialog::createPointDragger(int index, const osgEarth::GeoPoint& point)
437 {
438 osgEarth::Annotation::SphereDragger* sd = new osgEarth::Annotation::SphereDragger(_mapNode);
439 sd->setSize(4.0f);
440 sd->setColor(Color::Magenta);
441 sd->setPickColor(Color::Green);
442 sd->setPosition(point);
443 PointDraggerCallback* callback = new PointDraggerCallback(index, this);
444 sd->addPositionChangedCallback(callback);
445
446 if (!_draggers)
447 {
448 _draggers = new osg::Group();
449 _root->addChild(_draggers);
450 }
451
452 _draggers->addChild(sd);
453 }
454
movePoint(int index,const osgEarth::GeoPoint & position)455 void AddPathDialog::movePoint(int index, const osgEarth::GeoPoint& position)
456 {
457 (*_pathLine.get())[index] = position.vec3d();
458 refreshFeatureNode();
459 }
460
addPoint(const osgEarth::GeoPoint & point)461 void AddPathDialog::addPoint(const osgEarth::GeoPoint& point)
462 {
463 if (!_pathLine.valid())
464 _pathLine = new osgEarth::Symbology::LineString();
465
466 _pathLine->push_back(point.vec3d());
467
468 if (!_pathFeature.valid() && _pathLine->size() > 1)
469 {
470 osgEarth::Symbology::Style pathStyle;
471 pathStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::White;
472 pathStyle.getOrCreate<LineSymbol>()->stroke()->width() = 2.0f;
473 pathStyle.getOrCreate<LineSymbol>()->tessellation() = 20;
474 pathStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
475 pathStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
476
477 _pathFeature = new osgEarth::Features::Feature(_pathLine, _mapNode->getMapSRS(), pathStyle);
478 //_pathFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE;
479
480 if (!_pathNode.valid())
481 {
482 _pathNode = new osgEarth::Annotation::FeatureNode(_mapNode, _pathFeature);
483 _root->addChild(_pathNode);
484 }
485
486 _okButton->setEnabled(true);
487 }
488
489 refreshFeatureNode();
490 createPointDragger(_pathLine->size() - 1, point);
491 }
492
accept()493 void AddPathDialog::accept()
494 {
495 clearDisplay();
496
497 if (_pathNode.valid())
498 {
499 osgEarth::Annotation::AnnotationData* annoData = new osgEarth::Annotation::AnnotationData();
500 annoData->setName(getName());
501 annoData->setDescription(getDescription());
502 //annoData->setViewpoint(osgEarth::Viewpoint(_pathNode->getPosition().vec3d(), 0.0, -90.0, 1e5, _pathNode->getPosition().getSRS()));
503
504 _pathNode->setAnnotationData(annoData);
505 }
506
507 QDialog::accept();
508 }
509
reject()510 void AddPathDialog::reject()
511 {
512 resetValues();
513 clearDisplay();
514 QDialog::reject();
515 }
516
closeEvent(QCloseEvent * event)517 void AddPathDialog::closeEvent(QCloseEvent* event)
518 {
519 clearDisplay();
520 QDialog::closeEvent(event);
521 }
522
onDrapeCheckStateChanged(int state)523 void AddPathDialog::onDrapeCheckStateChanged(int state)
524 {
525 refreshFeatureNode();
526 }
527
onLineColorButtonClicked()528 void AddPathDialog::onLineColorButtonClicked()
529 {
530 QColor color = QColorDialog::getColor(QColor::fromRgbF(_pathColor.r(), _pathColor.g(), _pathColor.b(), _pathColor.a()), this);
531 if (color.isValid())
532 {
533 _pathColor = osgEarth::Symbology::Color(color.redF(), color.greenF(), color.blueF());
534 refreshFeatureNode();
535
536 updateButtonColorStyle(_lineColorButton, color);
537 }
538 }
539
540
541 //---------------------------------------------------------------------------
542
AddPolygonDialog(osg::Group * root,osgEarth::MapNode * mapNode,const ViewVector & views,osgEarth::Annotation::FeatureNode * polygon,QWidget * parent,Qt::WindowFlags f)543 AddPolygonDialog::AddPolygonDialog(osg::Group* root, osgEarth::MapNode* mapNode, const ViewVector& views, osgEarth::Annotation::FeatureNode* polygon, QWidget* parent, Qt::WindowFlags f)
544 : BaseAnnotationDialog(mapNode, views, parent, f), _root(root), _draggers(0L), _pathColor(Color(Color::White, 0.0)), _fillColor(Color(Color::Black, 0.5)), _editing(polygon ? true : false)
545 {
546 _polygon = new osgEarth::Symbology::Polygon();
547
548 initialize();
549
550 //If editing an existing polygon
551 if (polygon)
552 {
553 osgEarth::Annotation::AnnotationData* data = polygon->getAnnotationData();
554
555 //Get path name
556 _inName = data ? data->getName() : "";
557 _nameEdit->setText(tr(_inName.c_str()));
558
559 //Get path description
560 _inDescription = data ? data->getDescription() : "";
561 _descriptionEdit->setText(tr(_inDescription.c_str()));
562
563 _polyNode = polygon;
564
565 const osgEarth::Features::Feature* feat = polygon->getFeature();
566 if (feat)
567 {
568 _inFeature = const_cast<osgEarth::Features::Feature*>(feat);
569
570 //Get path color
571 const osgEarth::Symbology::LineSymbol* lineSymbol = feat->style()->get<LineSymbol>();
572 if (lineSymbol)
573 {
574 if (lineSymbol->stroke()->width() != 0.0)
575 {
576 _pathColor = lineSymbol->stroke()->color();
577 updateButtonColorStyle(_lineColorButton, QColor::fromRgbF(_pathColor.r(), _pathColor.g(), _pathColor.b(), _pathColor.a()));
578 }
579 }
580
581 //Get fill color
582 const osgEarth::Symbology::PolygonSymbol* polySymbol = feat->style()->get<PolygonSymbol>();
583 if (polySymbol)
584 {
585 _fillColor = polySymbol->fill()->color();
586 updateButtonColorStyle(_fillColorButton, QColor::fromRgbF(_fillColor.r(), _fillColor.g(), _fillColor.b(), _fillColor.a()));
587 }
588
589 bool draped = false;
590 if (feat->style()->has<AltitudeSymbol>() && feat->style()->get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_DRAPE)
591 {
592 draped = true;
593 }
594
595 //Get draping
596 _drapeCheckbox->setChecked(draped);
597
598 //Get path points
599 const osgEarth::Symbology::Polygon* polyGeom = dynamic_cast<const osgEarth::Symbology::Polygon*>(feat->getGeometry());
600 if (polyGeom)
601 {
602 for (osgEarth::Symbology::LineString::const_iterator it = polyGeom->begin(); it != polyGeom->end(); ++it)
603 addPoint(osgEarth::GeoPoint(_mapNode->getMapSRS(), (*it).x(), (*it).y(), (*it).z(), ALTMODE_RELATIVE));
604 }
605 }
606 }
607 else
608 {
609 //Not editing an existing polygon
610
611 //Add an end point to follow the mouse cursor
612 _polygon->push_back(osg::Vec3d(0.0, 0.0, 0.0));
613
614 //Add mouse handlers
615 if (_mapNode.valid() && _views.size() > 0)
616 {
617 _guiHandler = new AddPointsMouseHandler(this, _mapNode.get(), _root);
618 for (ViewVector::const_iterator it = views.begin(); it != views.end(); ++it)
619 (*it)->addEventHandler(_guiHandler.get());
620 }
621 }
622 }
623
initialize()624 void AddPolygonDialog::initialize()
625 {
626 _okButton->setEnabled(false);
627
628 _nameEdit->setText(tr("New Polygon"));
629
630 // add line color selection button
631 _lineColorButton = new QPushButton(tr("Line Color"));
632 _lineColorButton->setStyleSheet("QPushButton { color: black }");
633 _customLayout->addWidget(_lineColorButton);
634
635 // add fill color selection button
636 _fillColorButton = new QPushButton(tr("Fill Color"));
637 _fillColorButton->setStyleSheet("QPushButton { color: white; background-color: black }");
638 _customLayout->addWidget(_fillColorButton);
639
640 // add name display checkbox to the dialog
641 _drapeCheckbox = new QCheckBox(tr("Drape on terrain"));
642 _drapeCheckbox->setCheckState(Qt::Checked);
643 _customLayout->addWidget(_drapeCheckbox);
644
645 // wire up UI events
646 connect(_drapeCheckbox, SIGNAL(stateChanged(int)), this, SLOT(onDrapeCheckStateChanged(int)));
647 connect(_lineColorButton, SIGNAL(clicked()), this, SLOT(onLineColorButtonClicked()));
648 connect(_fillColorButton, SIGNAL(clicked()), this, SLOT(onFillColorButtonClicked()));
649 }
650
clearDisplay()651 void AddPolygonDialog::clearDisplay()
652 {
653 if (_root.valid())
654 {
655 if (!_editing)
656 _root->removeChild(_polyNode);
657
658 _root->removeChild(_draggers);
659 }
660
661 if (_guiHandler.valid())
662 {
663 for (ViewVector::const_iterator it = _views.begin(); it != _views.end(); ++it)
664 (*it)->removeEventHandler(_guiHandler.get());
665
666 _guiHandler->clearDisplay();
667 }
668 }
669
resetValues()670 void AddPolygonDialog::resetValues()
671 {
672 _nameEdit->setText(tr(_inName.c_str()));
673 _descriptionEdit->setText(tr(_inDescription.c_str()));
674
675 if (_inFeature.valid())
676 {
677 _polyNode->setFeature(_inFeature);
678 }
679 else
680 {
681 if (_root.valid())
682 {
683 _root->removeChild(_polyNode);
684 _polyNode = 0L;
685 _polygon = 0L;
686 }
687 }
688 }
689
mapMouseClick(const osgEarth::GeoPoint & point,int button)690 void AddPolygonDialog::mapMouseClick(const osgEarth::GeoPoint& point, int button)
691 {
692 if (button == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
693 {
694 addPoint(point);
695 }
696 }
697
mapMouseMove(const osgEarth::GeoPoint & point)698 void AddPolygonDialog::mapMouseMove(const osgEarth::GeoPoint& point)
699 {
700 _polygon->back().set(point.vec3d());
701 refreshFeatureNode(true);
702 }
703
refreshFeatureNode(bool geometryOnly)704 void AddPolygonDialog::refreshFeatureNode(bool geometryOnly)
705 {
706 if (_polyNode.valid() && _polyFeature.valid())
707 {
708 if (!geometryOnly)
709 {
710 if (_pathColor.a() == 0.0)
711 {
712 _polyFeature->style()->getOrCreate<LineSymbol>()->stroke()->color() = _fillColor;
713 _polyFeature->style()->getOrCreate<LineSymbol>()->stroke()->width() = 0.0;
714 }
715 else
716 {
717 _polyFeature->style()->getOrCreate<LineSymbol>()->stroke()->color() = _pathColor;
718 _polyFeature->style()->getOrCreate<LineSymbol>()->stroke()->width() = ANNOTATION_PATH_WIDTH;
719 }
720
721
722 _polyFeature->style()->getOrCreate<PolygonSymbol>()->fill()->color() = _fillColor;
723 _polyFeature->style()->getOrCreate<AltitudeSymbol>()->clamping() = _drapeCheckbox->checkState() == Qt::Checked ? AltitudeSymbol::CLAMP_TO_TERRAIN : AltitudeSymbol::CLAMP_ABSOLUTE;
724 _polyFeature->style()->getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
725 }
726
727 _polyNode->setFeature(_polyFeature);
728 }
729 }
730
createPointDragger(int index,const osgEarth::GeoPoint & point)731 void AddPolygonDialog::createPointDragger(int index, const osgEarth::GeoPoint& point)
732 {
733 osgEarth::Annotation::SphereDragger* sd = new osgEarth::Annotation::SphereDragger(_mapNode);
734 sd->setSize(4.0f);
735 sd->setColor(Color::Magenta);
736 sd->setPickColor(Color::Green);
737 sd->setPosition(point);
738 PointDraggerCallback* callback = new PointDraggerCallback(index, this);
739 sd->addPositionChangedCallback(callback);
740
741 if (!_draggers)
742 {
743 _draggers = new osg::Group();
744 _root->addChild(_draggers);
745 }
746
747 _draggers->addChild(sd);
748 }
749
movePoint(int index,const osgEarth::GeoPoint & position)750 void AddPolygonDialog::movePoint(int index, const osgEarth::GeoPoint& position)
751 {
752 (*_polygon.get())[index] = position.vec3d();
753 refreshFeatureNode();
754 }
755
addPoint(const osgEarth::GeoPoint & point)756 void AddPolygonDialog::addPoint(const osgEarth::GeoPoint& point)
757 {
758 if (_editing)
759 _polygon->push_back(point.vec3d());
760 else
761 _polygon->insert(_polygon->end() - 1, point.vec3d());
762
763 if (!_polyFeature.valid() && _polygon->size() > 2)
764 {
765 osgEarth::Symbology::Style polyStyle;
766 polyStyle.getOrCreate<LineSymbol>()->stroke()->color() = _pathColor;
767 polyStyle.getOrCreate<LineSymbol>()->stroke()->width() = ANNOTATION_PATH_WIDTH;
768 polyStyle.getOrCreate<LineSymbol>()->tessellation() = 20;
769 polyStyle.getOrCreate<PolygonSymbol>()->fill()->color() = _fillColor;
770 polyStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
771 if (_drapeCheckbox->checkState() == Qt::Checked)
772 {
773 polyStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
774 }
775
776 _polyFeature = new osgEarth::Features::Feature(_polygon, _mapNode->getMapSRS(), polyStyle);
777
778 if (!_polyNode.valid())
779 {
780 _polyNode = new osgEarth::Annotation::FeatureNode(_mapNode, _polyFeature);
781 _root->addChild(_polyNode);
782 }
783
784 _okButton->setEnabled(true);
785 }
786
787 refreshFeatureNode();
788 createPointDragger(_polygon->size() - 1, point);
789 }
790
accept()791 void AddPolygonDialog::accept()
792 {
793 clearDisplay();
794
795 if (_polyNode.valid())
796 {
797 //Remove active cursor point
798 if (!_editing)
799 {
800 _polygon->pop_back();
801 refreshFeatureNode(true);
802 }
803
804 //Create AnnotationData object for this annotation
805 osgEarth::Annotation::AnnotationData* annoData = new osgEarth::Annotation::AnnotationData();
806 annoData->setName(getName());
807 annoData->setDescription(getDescription());
808 //annoData->setViewpoint(osgEarth::Viewpoint(_pathNode->getPosition().vec3d(), 0.0, -90.0, 1e5, _pathNode->getPosition().getSRS()));
809
810 _polyNode->setAnnotationData(annoData);
811 }
812
813 QDialog::accept();
814 }
815
reject()816 void AddPolygonDialog::reject()
817 {
818 resetValues();
819 clearDisplay();
820 QDialog::reject();
821 }
822
closeEvent(QCloseEvent * event)823 void AddPolygonDialog::closeEvent(QCloseEvent* event)
824 {
825 clearDisplay();
826 QDialog::closeEvent(event);
827 }
828
onDrapeCheckStateChanged(int state)829 void AddPolygonDialog::onDrapeCheckStateChanged(int state)
830 {
831 if (_polyNode.valid())
832 {
833 _root->removeChild(_polyNode);
834
835 Style style;
836 if (_drapeCheckbox->checkState() == Qt::Checked)
837 {
838 style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
839 style.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
840 }
841 else
842 {
843 style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_NONE;
844 }
845
846 _polyNode = new osgEarth::Annotation::FeatureNode(_mapNode, _polyFeature, style);
847 _root->addChild(_polyNode);
848 }
849 }
850
onLineColorButtonClicked()851 void AddPolygonDialog::onLineColorButtonClicked()
852 {
853 QColor color = QColorDialog::getColor(QColor::fromRgbF(_pathColor.r(), _pathColor.g(), _pathColor.b(), _pathColor.a()), this, tr("Select outline color..."), QColorDialog::ShowAlphaChannel);
854 if (color.isValid())
855 {
856 _pathColor = osgEarth::Symbology::Color(color.redF(), color.greenF(), color.blueF(), color.alphaF());
857 refreshFeatureNode();
858
859 updateButtonColorStyle(_lineColorButton, color);
860 }
861 }
862
onFillColorButtonClicked()863 void AddPolygonDialog::onFillColorButtonClicked()
864 {
865 QColor color = QColorDialog::getColor(QColor::fromRgbF(_fillColor.r(), _fillColor.g(), _fillColor.b(), _fillColor.a()), this, tr("Select fill color..."), QColorDialog::ShowAlphaChannel);
866 if (color.isValid())
867 {
868 _fillColor = osgEarth::Symbology::Color(color.redF(), color.greenF(), color.blueF(), color.alphaF());
869 refreshFeatureNode();
870
871 updateButtonColorStyle(_fillColorButton, color);
872 }
873 }
874
875 //---------------------------------------------------------------------------
876
AddEllipseDialog(osg::Group * root,osgEarth::MapNode * mapNode,const ViewVector & views,osgEarth::Annotation::EllipseNode * ellipse,QWidget * parent,Qt::WindowFlags f)877 AddEllipseDialog::AddEllipseDialog(osg::Group* root, osgEarth::MapNode* mapNode, const ViewVector& views, osgEarth::Annotation::EllipseNode* ellipse, QWidget* parent, Qt::WindowFlags f)
878 : BaseAnnotationDialog(mapNode, views, parent, f), _root(root), _pathColor(Color(Color::White, 0.0)), _fillColor(Color(Color::Black, 0.5)),
879 _editing(ellipse ? true : false), _inStyle(ellipse ? ellipse->getStyle() : osgEarth::Symbology::Style())
880 {
881 initialize();
882
883 if (ellipse)
884 {
885 osgEarth::Annotation::AnnotationData* data = ellipse->getAnnotationData();
886
887 //Get path name
888 _inName = data ? data->getName() : "";
889 _nameEdit->setText(tr(_inName.c_str()));
890
891 //Get path description
892 _inDescription = data ? data->getDescription() : "";
893 _descriptionEdit->setText(tr(_inDescription.c_str()));
894
895 _inPosition = ellipse->getPosition();
896 _inRadiusMajor = ellipse->getRadiusMajor();
897 _inRadiusMinor = ellipse->getRadiusMinor();
898 _inRotationAngle = ellipse->getRotationAngle();
899
900 _ellipseNode = ellipse;
901
902 //Get path color
903 const osgEarth::Symbology::LineSymbol* lineSymbol = ellipse->getStyle().get<LineSymbol>();
904 if (lineSymbol)
905 {
906 if (lineSymbol->stroke()->width() != 0.0)
907 {
908 _pathColor = lineSymbol->stroke()->color();
909 updateButtonColorStyle(_lineColorButton, QColor::fromRgbF(_pathColor.r(), _pathColor.g(), _pathColor.b(), _pathColor.a()));
910 }
911 }
912
913 //Get fill color
914 const osgEarth::Symbology::PolygonSymbol* polySymbol = ellipse->getStyle().get<PolygonSymbol>();
915 if (polySymbol)
916 {
917 _fillColor = polySymbol->fill()->color();
918 updateButtonColorStyle(_fillColorButton, QColor::fromRgbF(_fillColor.r(), _fillColor.g(), _fillColor.b(), _fillColor.a()));
919 }
920
921 bool draped = false;
922 if (ellipse->getStyle().has<AltitudeSymbol>() && ellipse->getStyle().get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_DRAPE)
923 {
924 draped = true;
925 }
926
927 //Get draping
928 _drapeCheckbox->setChecked(draped);
929
930 //Call refreshFeatureNode to update _ellipseStyle
931 refreshFeatureNode();
932
933 //Add the ellipse
934 addEllipse(ellipse->getPosition(), ellipse->getRadiusMajor(), ellipse->getRadiusMinor(), ellipse->getRotationAngle());
935 }
936 else
937 {
938 if (_mapNode.valid() && _views.size() > 0)
939 {
940 _guiHandler = new AddEllipseMouseHandler(this, _mapNode.get(), _root);
941 for (ViewVector::const_iterator it = views.begin(); it != views.end(); ++it)
942 (*it)->addEventHandler(_guiHandler.get());
943 }
944 }
945 }
946
initialize()947 void AddEllipseDialog::initialize()
948 {
949 //Define the default ellipse style
950 _ellipseStyle.getOrCreate<LineSymbol>()->stroke()->color() = _fillColor;
951 _ellipseStyle.getOrCreate<LineSymbol>()->stroke()->width() = 0.0;
952 _ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = _fillColor;
953 _ellipseStyle.getOrCreate<osgEarth::Symbology::AltitudeSymbol>()->clamping() = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN;
954
955 //Set _okButton disabled until an ellipse is drawn
956 _okButton->setEnabled(false);
957
958 _nameEdit->setText(tr("New Ellipse"));
959
960 // add line color selection button
961 _lineColorButton = new QPushButton(tr("Line Color"));
962 _lineColorButton->setStyleSheet("QPushButton { color: black }");
963 _customLayout->addWidget(_lineColorButton);
964
965 // add fill color selection button
966 _fillColorButton = new QPushButton(tr("Fill Color"));
967 _fillColorButton->setStyleSheet("QPushButton { color: white; background-color: black }");
968 _customLayout->addWidget(_fillColorButton);
969
970 // add name display checkbox to the dialog
971 _drapeCheckbox = new QCheckBox(tr("Drape on terrain"));
972 _drapeCheckbox->setCheckState(Qt::Checked);
973 _customLayout->addWidget(_drapeCheckbox);
974
975 // wire up UI events
976 connect(_drapeCheckbox, SIGNAL(stateChanged(int)), this, SLOT(onDrapeCheckStateChanged(int)));
977 connect(_lineColorButton, SIGNAL(clicked()), this, SLOT(onLineColorButtonClicked()));
978 connect(_fillColorButton, SIGNAL(clicked()), this, SLOT(onFillColorButtonClicked()));
979 }
980
clearDisplay()981 void AddEllipseDialog::clearDisplay()
982 {
983 if (_root.valid())
984 {
985 if (!_editing)
986 _root->removeChild(_ellipseNode);
987
988 _root->removeChild(_editor);
989 }
990
991 removeGuiHandlers();
992 }
993
resetValues()994 void AddEllipseDialog::resetValues()
995 {
996 _nameEdit->setText(tr(_inName.c_str()));
997 _descriptionEdit->setText(tr(_inDescription.c_str()));
998
999 if (_editing && _ellipseNode.valid())
1000 {
1001 _ellipseNode->setPosition(_inPosition);
1002 _ellipseNode->setRadii(_inRadiusMajor, _inRadiusMinor);
1003 _ellipseNode->setRotationAngle(_inRotationAngle);
1004 _ellipseNode->setStyle(_inStyle);
1005 }
1006 }
1007
removeGuiHandlers()1008 void AddEllipseDialog::removeGuiHandlers()
1009 {
1010 if (_guiHandler.valid())
1011 {
1012 for (ViewVector::const_iterator it = _views.begin(); it != _views.end(); ++it)
1013 (*it)->removeEventHandler(_guiHandler.get());
1014
1015 _guiHandler = 0L;
1016 }
1017 }
1018
addEllipse(const osgEarth::GeoPoint & position,const Linear & radiusMajor,const Linear & radiusMinor,const Angular & rotationAngle)1019 void AddEllipseDialog::addEllipse(const osgEarth::GeoPoint& position, const Linear& radiusMajor, const Linear& radiusMinor, const Angular& rotationAngle)
1020 {
1021 if (!_ellipseNode.valid())
1022 {
1023 Style style = _ellipseStyle;
1024 if ( _drapeCheckbox->checkState() == Qt::Checked ) {
1025 style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
1026 style.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
1027 }
1028 else {
1029 style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_NONE;
1030 }
1031 _ellipseNode = new osgEarth::Annotation::EllipseNode(_mapNode, position, radiusMajor, radiusMinor, rotationAngle, style);
1032 _root->addChild(_ellipseNode);
1033 }
1034 else
1035 {
1036 _ellipseNode->setPosition(position);
1037 _ellipseNode->setRadii(radiusMajor, radiusMinor);
1038 _ellipseNode->setRotationAngle(rotationAngle);
1039 }
1040
1041 if (!_editor.valid())
1042 {
1043 _editor = new osgEarth::Annotation::EllipseNodeEditor(_ellipseNode);
1044 _root->addChild(_editor);
1045 }
1046
1047 if (_guiHandler.valid())
1048 _guiHandler->setCapturing(false);
1049
1050 _okButton->setEnabled(true);
1051 }
1052
refreshFeatureNode(bool geometryOnly)1053 void AddEllipseDialog::refreshFeatureNode(bool geometryOnly)
1054 {
1055 if (_pathColor.a() == 0.0)
1056 {
1057 _ellipseStyle.getOrCreate<LineSymbol>()->stroke()->color() = _fillColor;
1058 _ellipseStyle.getOrCreate<LineSymbol>()->stroke()->width() = 0.0;
1059 }
1060 else
1061 {
1062 _ellipseStyle.getOrCreate<LineSymbol>()->stroke()->color() = _pathColor;
1063 _ellipseStyle.getOrCreate<LineSymbol>()->stroke()->width() = ANNOTATION_PATH_WIDTH;
1064 }
1065
1066 _ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = _fillColor;
1067
1068 if (_ellipseNode.valid())
1069 _ellipseNode->setStyle(_ellipseStyle);
1070 }
1071
accept()1072 void AddEllipseDialog::accept()
1073 {
1074 clearDisplay();
1075
1076 if (_ellipseNode.valid())
1077 {
1078 //Create AnnotationData object for this annotation
1079 osgEarth::Annotation::AnnotationData* annoData = new osgEarth::Annotation::AnnotationData();
1080 annoData->setName(getName());
1081 annoData->setDescription(getDescription());
1082 Viewpoint vp;
1083 vp.focalPoint() = _ellipseNode->getPosition();
1084 vp.pitch()->set( -90.0, Units::DEGREES );
1085 vp.range()->set( 1e5, Units::METERS );
1086 annoData->setViewpoint( vp );
1087 //annoData->setViewpoint(osgEarth::Viewpoint(_ellipseNode->getPosition().vec3d(), 0.0, -90.0, 1e5, _ellipseNode->getPosition().getSRS()));
1088
1089 _ellipseNode->setAnnotationData(annoData);
1090 }
1091
1092 QDialog::accept();
1093 }
1094
reject()1095 void AddEllipseDialog::reject()
1096 {
1097 resetValues();
1098 clearDisplay();
1099 QDialog::reject();
1100 }
1101
closeEvent(QCloseEvent * event)1102 void AddEllipseDialog::closeEvent(QCloseEvent* event)
1103 {
1104 clearDisplay();
1105 QDialog::closeEvent(event);
1106 }
1107
onDrapeCheckStateChanged(int state)1108 void AddEllipseDialog::onDrapeCheckStateChanged(int state)
1109 {
1110 if (_ellipseNode.valid())
1111 addEllipse(_ellipseNode->getPosition(), _ellipseNode->getRadiusMajor(), _ellipseNode->getRadiusMinor(), _ellipseNode->getRotationAngle());
1112 }
1113
onLineColorButtonClicked()1114 void AddEllipseDialog::onLineColorButtonClicked()
1115 {
1116 QColor color = QColorDialog::getColor(QColor::fromRgbF(_pathColor.r(), _pathColor.g(), _pathColor.b(), _pathColor.a()), this, tr("Select outline color..."), QColorDialog::ShowAlphaChannel);
1117 if (color.isValid())
1118 {
1119 _pathColor = osgEarth::Symbology::Color(color.redF(), color.greenF(), color.blueF(), color.alphaF());
1120 refreshFeatureNode();
1121
1122 updateButtonColorStyle(_lineColorButton, color);
1123 }
1124 }
1125
onFillColorButtonClicked()1126 void AddEllipseDialog::onFillColorButtonClicked()
1127 {
1128 QColor color = QColorDialog::getColor(QColor::fromRgbF(_fillColor.r(), _fillColor.g(), _fillColor.b(), _fillColor.a()), this, tr("Select fill color..."), QColorDialog::ShowAlphaChannel);
1129 if (color.isValid())
1130 {
1131 _fillColor = osgEarth::Symbology::Color(color.redF(), color.greenF(), color.blueF(), color.alphaF());
1132 refreshFeatureNode();
1133
1134 updateButtonColorStyle(_fillColorButton, color);
1135 }
1136 }
1137