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