USGS

Isis 3.0 Object Programmers' Reference

Home

FeatureNomenclatureTool.cpp
1 #include "IsisDebug.h"
2 #include "FeatureNomenclatureTool.h"
3 
4 #include <QAction>
5 #include <QApplication>
6 #include <QBitmap>
7 #include <QCheckBox>
8 #include <QClipboard>
9 #include <QComboBox>
10 #include <QDebug>
11 #include <QDesktopServices>
12 #include <QDialog>
13 #include <QHBoxLayout>
14 #include <QLabel>
15 #include <QMenu>
16 #include <QMessageBox>
17 #include <QProgressBar>
18 #include <QPushButton>
19 #include <QSettings>
20 #include <QWebView>
21 
22 #include <geos/geom/Coordinate.h>
23 #include <geos/geom/CoordinateSequence.h>
24 #include <geos/geom/MultiPolygon.h>
25 
26 #include "Distance.h"
27 #include "FileName.h"
28 #include "ImagePolygon.h"
29 #include "Latitude.h"
30 #include "Longitude.h"
31 #include "MdiCubeViewport.h"
32 #include "NomenclatureToolConfigDialog.h"
33 #include "PolygonTools.h"
34 #include "Target.h"
35 #include "ToolPad.h"
36 
37 
38 namespace Isis {
39 
46  Tool(parent) {
47  m_foundNomenclature = NULL;
50  m_foundFeaturesCombo = NULL;
53  m_disclaimerBtn = NULL;
54  m_queryingProgress = NULL;
55  m_nomenclatureEnabled = false;
56  m_fontColor = NULL;
57 
61 
62  m_fontSize = 12;
63  m_fontColor = new QColor(237, 170, 171);
64  m_defaultEnabled = false;
65  m_disclaimedAlready = false;
66  m_showApprovedOnly = true;
68 
69  m_disclaimerText = "The nomenclature qview tool will label named features "
70  "in your opened cube files. This tool <strong>requires</strong> an "
71  "active internet connection, projection or camera information, and a "
72  "calculatable ground range to function. The larger the ground range ("
73  "covered area on a planet), the longer it will take to populate the "
74  "nomenclature for a particular cube.<br/><br/>"
75  "<font color='red'>**WARNING**</font> The accuracy of this tool is not "
76  "perfect, features <strong>can and will be mislabeled</strong> if you "
77  "have not properly controlled your images to the control network that "
78  "identifies the latitude/longitude values of a feature. Please use the "
79  "nomenclature website to verify a label is correct for a feature. "
80  "<br/><br/>See the IAU Gazetteer of Planetary Nomenclature website for "
81  "more information.<br/>"
82  "<a href='http://planetarynames.wr.usgs.gov/'>"
83  "http://planetarynames.wr.usgs.gov/</a>";
84 
85  connect(this, SIGNAL(toolActivated()),
86  this, SLOT(onToolActivated()));
87 
88  readSettings();
89 
91  }
92 
93 
98  delete m_foundNomenclature;
99  m_foundNomenclature = NULL;
100 
101  foreach (FeatureNomenclature *nomenclature, *m_nomenclatureSearchers) {
102  delete nomenclature;
103  }
104 
107  }
108 
109 
115  void FeatureNomenclatureTool::addTo(QMenu *menu) {
116  m_action = menu->addAction("Show Nomenclature");
117  m_action->setCheckable(true);
118  m_action->setChecked(m_nomenclatureEnabled);
119 
120  connect(m_action, SIGNAL(triggered(bool)),
121  m_findNomenclatureCheckBox, SLOT(setChecked(bool)));
122  }
123 
124 
132  QPainter *painter) {
135 
136  QFont fontToUse;
137  fontToUse.setPointSize(m_fontSize);
138  painter->setFont(fontToUse);
139  painter->setPen(QPen(*m_fontColor));
140  display.paint(painter, (m_extentType != None), m_extentType, m_showApprovedOnly);
141  }
142  }
143 
144 
151  return m_defaultEnabled;
152  }
153 
154 
161  return *m_fontColor;
162  }
163 
164 
171  return m_fontSize;
172  }
173 
174 
181  return m_showApprovedOnly;
182  }
183 
184 
191  return m_extentType;
192  }
193 
194 
200  void FeatureNomenclatureTool::setDefaultEnabled(bool defaultEnabled) {
201  if (m_defaultEnabled != defaultEnabled) {
203  writeSettings();
204  }
205  }
206 
207 
215  if (*m_fontColor != color) {
216  *m_fontColor = color;
217  writeSettings();
218 
219  foreach (MdiCubeViewport *vp, *cubeViewportList())
220  vp->viewport()->update();
221  }
222  }
223 
224 
231  void FeatureNomenclatureTool::setFontSize(int newFontSize) {
232  if (m_fontSize != newFontSize) {
233  m_fontSize = newFontSize;
234  writeSettings();
235 
237 
238  foreach (MdiCubeViewport *vp, *cubeViewportList())
239  vp->viewport()->update();
240  }
241  }
242 
243 
250  if (m_showApprovedOnly != approvedOnly) {
251  m_showApprovedOnly = approvedOnly;
252  writeSettings();
255 
256  foreach (MdiCubeViewport *vp, *cubeViewportList())
257  vp->viewport()->update();
258  }
259  }
260 
261 
269  if (m_extentType != show) {
270  m_extentType = show;
271  writeSettings();
272 
273  for (int i = 0; i < m_foundNomenclature->count(); i++) {
274  (*m_foundNomenclature)[i].applyExtentType(m_extentType);
275  }
276 
278 
279  foreach (MdiCubeViewport *vp, *cubeViewportList())
280  vp->viewport()->update();
281  }
282  }
283 
284 
291  return "&Options";
292  }
293 
294 
303  QStackedWidget *parent) {
304  QWidget *wrapperWidget = new QWidget;
305 
306  m_findNomenclatureCheckBox = new QCheckBox;
307  m_findNomenclatureCheckBox->setText("Name Features");
309  connect(m_findNomenclatureCheckBox, SIGNAL(stateChanged(int)),
310  this, SLOT(findNomenclatureStateChanged(int)));
311 
312  QLabel *foundFeaturesLabel = new QLabel("Found Features:");
314  m_foundFeaturesCombo->setSizeAdjustPolicy(
315  QComboBox::AdjustToContents);
316  m_foundFeaturesCombo->addItem("");
317 
318  m_nomenclatureCenterBtn = new QPushButton("Center on Feature");
319  m_nomenclatureCenterBtn->setEnabled(false);
320 
321  connect(m_nomenclatureCenterBtn, SIGNAL(clicked()),
322  this, SLOT(centerOnSelectedFeature()));
323 
324  m_nomenclatureOptionsBtn = new QPushButton("Tool Options");
325  connect(m_nomenclatureOptionsBtn, SIGNAL(clicked()),
326  this, SLOT(configure()));
327 
328  m_disclaimerBtn = new QPushButton("Disclaimer");
329  connect(m_disclaimerBtn, SIGNAL(clicked()),
330  this, SLOT(showDisclaimer()));
331 
332  connect(m_foundFeaturesCombo, SIGNAL(currentIndexChanged(int)),
333  this, SLOT(featureSelected()));
334 
336  m_queryingProgress->setObjectName("nomenclatureQueryProgress");
337  m_queryingProgress->setVisible(false);
338  m_queryingProgress->setRange(0, 0);
339 
340  QHBoxLayout *layout = new QHBoxLayout;
341  layout->setMargin(0);
342  layout->addWidget(m_findNomenclatureCheckBox);
343  layout->addWidget(foundFeaturesLabel);
344  layout->addWidget(m_foundFeaturesCombo);
345  layout->addWidget(m_nomenclatureCenterBtn);
346  layout->addWidget(m_nomenclatureOptionsBtn);
347  layout->addWidget(m_disclaimerBtn);
348  layout->addWidget(m_queryingProgress);
349  layout->addStretch(1);
350  wrapperWidget->setLayout(layout);
351  return wrapperWidget;
352  }
353 
354 
364  QAction *action = new QAction(toolpad);
365 
366  action->setIcon(QPixmap(toolIconDir() + "/nomenclature.png"));
367  action->setToolTip("Nomenclature (N)");
368  action->setShortcut(Qt::Key_N);
369  action->setObjectName("nomenclatureToolButton");
370 
371  QString text =
372  "<b>Function:</b> Display nomenclature on the visible images.\n"
373  "<p/><b>Hint:</b> While this tool is active, you can left and right "
374  "click on any of the named features for additional options."
375  "<p/><b>Shortcut:</b> N";
376  action->setWhatsThis(text);
377 
378  return action;
379  }
380 
381 
391  QPoint p, Qt::MouseButton s) {
392  if (m_nomenclatureEnabled &&
395  }
396  }
397 
398 
405  }
406 
407 
413  QVariant variant = m_foundFeaturesCombo->itemData(
414  m_foundFeaturesCombo->currentIndex());
415 
416  if (variant.toMap()["Viewport"].value<MdiCubeViewport *>() != NULL) {
417  MdiCubeViewport *vp =
418  variant.toMap()["Viewport"].value<MdiCubeViewport *>();
420  variant.toMap()["Feature"].value<FeatureNomenclature::Feature>();
421 
422  // We need to find the appropriate viewport also...
423  centerOnFeature(vp, feature);
424  }
425  }
426 
427 
432  NomenclatureToolConfigDialog *configDialog =
434  qobject_cast<QWidget *>(parent()));
435  configDialog->setAttribute(Qt::WA_DeleteOnClose);
436  configDialog->show();
437  }
438 
439 
447  QVariant variant = m_foundFeaturesCombo->itemData(
448  m_foundFeaturesCombo->currentIndex());
449 
450  bool featureSelected =
451  variant.toMap()["Viewport"].value<MdiCubeViewport *>() != NULL;
452 
453  m_nomenclatureCenterBtn->setEnabled(
454  m_nomenclatureEnabled && featureSelected);
455  }
456 
457 
463  FeatureNomenclature *searcher) {
464  MdiCubeViewport *viewport = m_nomenclatureSearchers->key(searcher);
465 
466  // The viewport could have gone away when we were still querying... handle
467  // that case.
468  bool vpValid = cubeViewportList()->contains(viewport);
469  if (vpValid)
470  featuresForViewportFound(viewport);
471 
472  if (m_nomenclatureEnabled) {
473  viewportDone(viewport);
474  }
475 
476  if (vpValid)
477  viewport->viewport()->update();
478  }
479 
480 
487  if (newState == Qt::Unchecked) {
488  m_nomenclatureEnabled = false;
490  }
491  else if (newState == Qt::Checked) {
492  m_nomenclatureEnabled = true;
494 
496  }
497 
498  foreach (MdiCubeViewport *vp, *cubeViewportList())
499  vp->viewport()->update();
500  }
501 
502 
510  if (m_nomenclatureEnabled) {
511 
512  for (int i = 0; i < m_foundNomenclature->count(); i++) {
513  (*m_foundNomenclature)[i].handleViewChanged(this);
514  }
515  }
516  }
517 
518 
524  if (!m_findNomenclatureCheckBox->isChecked())
525  m_findNomenclatureCheckBox->setChecked(true);
526  }
527 
528 
534  QMessageBox::warning(qobject_cast<QWidget *>(parent()),
535  "Nomenclature Disclaimer", m_disclaimerText);
536  m_disclaimedAlready = true;
537  writeSettings();
538  }
539 
540 
551  foreach (MdiCubeViewport *viewport, viewportsWithFoundNomenclature()) {
552  if (viewport == vp ||
553  (vp->isLinked() && viewport->isLinked())) {
554  viewportFeatureDisplay(viewport)->centerFeature(feature);
555  }
556  }
557  }
558 
559 
569  if (vp) {
571  if (m_nomenclatureSearchers->find(vp) != m_nomenclatureSearchers->end()) {
572  if ((*m_nomenclatureSearchers)[vp]->hasResult())
573  features.append((*m_nomenclatureSearchers)[vp]->features());
574  else
575  m_findNomenclatureCheckBox->setChecked(false);
576  }
577 
578  if (!viewportFeatureDisplay(vp)) {
579  m_foundNomenclature->append(ViewportFeatureDisplay(this, vp, features, m_extentType));
580  }
581 
582  features = viewportFeatureDisplay(vp)->features();
583 
584  QProgressDialog updatingFeaturesProgress(
585  tr("Projecting Features for [%1]").arg(vp->cube()->fileName().section('/',-1)),
586  QString(), 0, 100);
587 
588  updatingFeaturesProgress.setWindowModality(Qt::WindowModal);
589 
591 
592  for (int i = 0; i < features.count(); i++) {
593  feature = features[i];
594 
595  int progress = floor(100 * (double)i / (double)features.count());
596 
597  if (progress != updatingFeaturesProgress.value())
598  updatingFeaturesProgress.setValue(progress);
599 
600  if (updatingFeaturesProgress.wasCanceled()) {
601  m_nomenclatureSearchers->clear();
602  m_foundNomenclature->clear();
603  m_foundFeaturesCombo->clear();
604  m_findNomenclatureCheckBox->setChecked(false);
605  break;
606  }
607 
608  if ( !m_showApprovedOnly ||
610 
611  QString displayName = feature.cleanName() +
612  " (" + FileName(vp->cube()->fileName()).name() + ")";
613 
614  QString targetName = feature.target().toUpper();
615 
616  // never insert above the blank (at 0)
617  int insertPos = 1;
618 
619  bool foundInsertPos = m_foundFeaturesCombo->count() == 0;
620 
621  while (!foundInsertPos) {
622  if (insertPos >= m_foundFeaturesCombo->count()) {
623  foundInsertPos = true;
624  }
625  else {
626  QVariant insetPosData = m_foundFeaturesCombo->itemData(
627  insertPos);
628  QString insertPosTarget = insetPosData.toMap()["Target"].toString();
629 
630  if (targetName < insertPosTarget) {
631  foundInsertPos = true;
632  }
633  else if (targetName == insertPosTarget) {
634  if (!insetPosData.toMap()["Viewport"].isNull()) {
635  foundInsertPos = displayName.compare(
636  m_foundFeaturesCombo->itemText(insertPos),
637  Qt::CaseInsensitive) < 0;
638  }
639  }
640  }
641 
642  if (!foundInsertPos)
643  insertPos++;
644  }
645 
646  if (m_foundFeaturesCombo->itemData(insertPos - 1).toMap()[
647  "Target"].toString() != targetName) {
649  data["Target"] = targetName;
650 
651  QString controlNet = feature.controlNet();
652  if (controlNet != "")
653  controlNet = " (" + controlNet + ")";
654 
655  m_foundFeaturesCombo->insertItem(insertPos,
656  targetName + controlNet,
657  data);
658  QVariant font = m_foundFeaturesCombo->itemData(insertPos,
659  Qt::ForegroundRole);
660  m_foundFeaturesCombo->setItemData(insertPos, QColor(Qt::gray),
661  Qt::ForegroundRole);
662  insertPos++;
663 
664  m_foundFeaturesCombo->insertItem(insertPos,
665  "-----------",
666  data);
667  m_foundFeaturesCombo->setItemData(insertPos, QColor(Qt::gray),
668  Qt::ForegroundRole);
669  insertPos++;
670  }
671 
673  data["Feature"] = QVariant::fromValue<FeatureNomenclature::Feature>(
674  feature);
675  data["Viewport"] = qVariantFromValue(vp);
676  data["Target"] = qVariantFromValue(targetName);
677 
678  m_foundFeaturesCombo->insertItem(insertPos, displayName,
679  qVariantFromValue(data));
680  }
681  }
682  updatingFeaturesProgress.setValue( features.count() );
683  }
684  }
685 
686 
695  if (m_nomenclatureEnabled) {
696  // We're looking for viewports with nomenclature results that no longer
697  // exist with removedViewports.
698  QList<MdiCubeViewport *> removedViewports =
700 
701  foreach (MdiCubeViewport *vp, *cubeViewportList()) {
702  removedViewports.removeOne(vp);
703 
706 
707  if (!viewportFeaturesFound(vp) && finding.find(vp) == finding.end()) {
709  }
710  }
711 
712  bool removedAViewport = false;
713  foreach (MdiCubeViewport *vp, removedViewports) {
714  // A viewport disappeared; let's remove all references of it.
716  removedAViewport = true;
717  }
718 
719  if (removedAViewport) {
720  // Rebuild the combo box...
722  }
723  }
724  }
725 
726 
734  try {
735  // verify we can project before anything else
737 
738  if (!ugm)
739  throw IException();
740 
741  QString target;
742  if (vp->camera()) {
743  target = vp->camera()->target()->name();
744  }
745  else if (vp->projection()) {
746  PvlGroup mappingGrp = vp->projection()->Mapping();
747 
748  if (mappingGrp.hasKeyword("TargetName"))
749  target = mappingGrp["TargetName"][0];
750  }
751 
752  Latitude minLat;
753  Latitude maxLat;
754  Longitude minLon;
755  Longitude maxLon;
756  if (ugm->GroundRange(vp->cube(), minLat, maxLat, minLon, maxLon) &&
757  target != "") {
759  connect(searcher, SIGNAL(featuresIdentified(FeatureNomenclature *)),
760  this, SLOT(featuresIdentified(FeatureNomenclature *)));
761 
762  (*m_nomenclatureSearchers)[vp] = searcher;
764  (*m_nomenclatureSearchers)[vp]->queryFeatures(target.toUpper(),
765  minLat, minLon,
766  maxLat, maxLon);
767  }
768  else {
769  viewportDone(vp);
770  }
771  }
772  catch (IException &) {
773  viewportDone(vp);
774  }
775  }
776 
777 
783  if (m_foundFeaturesCombo) {
784  m_foundFeaturesCombo->clear();
785  m_foundFeaturesCombo->addItem("");
786 
789  }
790  }
791  }
792 
793 
801  for (int i = m_foundNomenclature->count() - 1; i >= 0; i--) {
802  if (m_foundNomenclature->at(i).sourceViewport() == vp)
803  m_foundNomenclature->removeAt(i);
804  }
805  }
806 
807 
815  QDialog *detailsDialog = new QDialog(qobject_cast<QWidget *>(parent()));
816  detailsDialog->setAttribute(Qt::WA_DeleteOnClose);
817 
818  QVBoxLayout *mainLayout = new QVBoxLayout;
819  detailsDialog->setLayout(mainLayout);
820 
821  mainLayout->addWidget(feature.toWidget());
822 
823  QWidget *buttonsAreaWrapper = new QWidget;
824 
825  QHBoxLayout *buttonsAreaLayout = new QHBoxLayout;
826  buttonsAreaWrapper->setLayout(buttonsAreaLayout);
827 
828  buttonsAreaLayout->addStretch();
829  QPushButton *okayBtn = new QPushButton("&Ok");
830  okayBtn->setIcon(QIcon::fromTheme("dialog-ok"));
831  connect(okayBtn, SIGNAL(clicked()),
832  detailsDialog, SLOT(accept()));
833  buttonsAreaLayout->addWidget(okayBtn);
834 
835  mainLayout->addWidget(buttonsAreaWrapper);
836 
837  detailsDialog->show();
838  }
839 
840 
848  QDesktopServices::openUrl(feature.referenceUrl());
849  }
850 
851 
858  bool isCurrentlyLoading = (m_nomenclatureSearchers->keys().count() > 0);
859 
860  if (isCurrentlyLoading) {
861  m_findNomenclatureCheckBox->setEnabled(false);
862  m_queryingProgress->setVisible(true);
863  }
864  else {
865  m_findNomenclatureCheckBox->setEnabled(true);
866  m_queryingProgress->setVisible(false);
868 
870  showDisclaimer();
871  }
872  }
873 
874  if (m_action) {
875  m_action->setChecked(m_nomenclatureEnabled);
876  }
877 
878  featureSelected();
879  }
880 
881 
892  if (m_nomenclatureSearchers->find(vp) != m_nomenclatureSearchers->end()) {
893  if ((*m_nomenclatureSearchers)[vp]) {
894  (*m_nomenclatureSearchers)[vp]->deleteLater();
895  }
896 
897  m_nomenclatureSearchers->remove(vp);
898  }
899 
900  if (viewportFeatureDisplay(vp) == NULL) {
901  m_foundNomenclature->append(
902  ViewportFeatureDisplay(this, vp,
904  }
905  else {
906  connect(vp, SIGNAL(screenPixelsChanged()),
907  this, SLOT(nomenclaturePositionsOutdated()));
908  }
909 
911  }
912 
913 
923  for (int i = 0; i < m_foundNomenclature->count(); i++) {
924  if (m_foundNomenclature->at(i).sourceViewport() == vp)
925  return &(*m_foundNomenclature)[i];
926  }
927 
928  return NULL;
929  }
930 
931 
941  const {
942  for (int i = 0; i < m_foundNomenclature->count(); i++) {
943  if (m_foundNomenclature->at(i).sourceViewport() == vp)
944  return &(*m_foundNomenclature)[i];
945  }
946 
947  return NULL;
948  }
949 
950 
960  const {
961  return (viewportFeatureDisplay(vp) != NULL);
962  }
963 
964 
974 
975  for (int i = 0; i < m_foundNomenclature->count(); i++) {
976  if (m_foundNomenclature->at(i).sourceViewport() != NULL)
977  result.append(m_foundNomenclature->at(i).sourceViewport());
978  }
979 
980  return result;
981  }
982 
983 
990  FileName config("$HOME/.Isis/qview/nomenclature.config");
991  QSettings settings(
992  config.expanded(), QSettings::NativeFormat);
993 
994  m_fontSize = settings.value("fontSize", m_fontSize).toInt();
995  *m_fontColor = settings.value("fontColor", *m_fontColor).value<QColor>();
997  settings.value("defaultEnabled", m_defaultEnabled).toBool();
999  settings.value("disclaimerShown", m_disclaimedAlready).toBool();
1001  settings.value("showApprovedOnly", m_showApprovedOnly).toBool();
1002  m_extentType =
1003  VectorType(settings.value("vectorsShown", m_extentType).toInt());
1004  }
1005 
1006 
1012  FileName config("$HOME/.Isis/qview/nomenclature.config");
1013  QSettings settings(
1014  config.expanded(), QSettings::NativeFormat);
1015  settings.setValue("fontSize", m_fontSize);
1016  settings.setValue("fontColor", qVariantFromValue(*m_fontColor));
1017  settings.setValue("defaultEnabled", m_defaultEnabled);
1018  settings.setValue("disclaimerShown", m_disclaimedAlready);
1019  settings.setValue("showApprovedOnly", m_showApprovedOnly);
1020  settings.setValue("vectorsShown", m_extentType);
1021  }
1022 
1023 
1028  m_centerLine = Null;
1029  m_centerSample = Null;
1030  m_featureEdgeLineSamples = NULL;
1031  m_gmap = NULL;
1032 
1034  }
1035 
1036 
1048  m_centerLine = Null;
1049  m_centerSample = Null;
1050  m_featureEdgeLineSamples = NULL;
1051  m_gmap = NULL;
1052 
1053  m_featureEdgeLineSamples = new QList< QPair<double, double> >;
1054 
1055  m_feature = feature;
1056 
1057  if (vp) {
1058  m_gmap = vp->universalGroundMap();
1059  Latitude centerLat = m_feature.centerLatitude();
1060  Longitude centerLon = m_feature.centerLongitude();
1061  if (m_gmap && m_gmap->SetGround(centerLat, centerLon)) {
1062  m_centerSample = m_gmap->Sample();
1063  m_centerLine = m_gmap->Line();
1064 
1065  applyExtentType(vectorType);
1066  }
1067  }
1068  }
1069 
1070 
1077  const FeaturePosition &other) {
1078  m_centerLine = other.m_centerLine;
1079  m_centerSample = other.m_centerSample;
1080  m_featureEdgeLineSamples = NULL;
1081  m_gmap = NULL;
1082 
1083  m_featureEdgeLineSamples = new QList< QPair<double, double> >(
1084  *other.m_featureEdgeLineSamples);
1085 
1086  m_feature = other.m_feature;
1087 
1088  if (other.m_gmap)
1089  m_gmap = new UniversalGroundMap(*other.m_gmap);
1090  }
1091 
1092 
1097  m_centerLine = Null;
1098  m_centerSample = Null;
1099  m_gmap = NULL;
1100 
1101  delete m_featureEdgeLineSamples;
1102  m_featureEdgeLineSamples = NULL;
1103  }
1104 
1105 
1112  return (m_centerSample != Null && m_centerLine != Null);
1113  }
1114 
1115 
1122  const {
1123  return QPair<double, double>(m_centerSample, m_centerLine);
1124  }
1125 
1126 
1134  return *m_featureEdgeLineSamples;
1135  }
1136 
1137 
1145  return m_feature;
1146  }
1147 
1158 
1159  Latitude centerLat = m_feature.centerLatitude();
1160  Longitude centerLon = m_feature.centerLongitude();
1161 
1162  m_featureEdgeLineSamples->clear();
1163 
1164  if (vectorType == FeatureNomenclatureTool::Arrows8) {
1165 
1166  // We're going to permute the edge lats/lons excluding the center, so
1167  // these lists are independent of each other.
1168  QList<Latitude> edgeLats;
1169  QList<Longitude> edgeLons;
1170 
1171  edgeLats.append(m_feature.northernLatitude());
1172  edgeLats.append(m_feature.centerLatitude());
1173  edgeLats.append(m_feature.southernLatitude());
1174 
1175  edgeLons.append(m_feature.easternLongitude());
1176  edgeLons.append(m_feature.centerLongitude());
1177  edgeLons.append(m_feature.westernLongitude());
1178 
1179  int edgeLatCount = edgeLats.count();
1180  int edgeLonCount = edgeLons.count();
1181 
1182  for (int latIndex = 0; latIndex < edgeLatCount; latIndex++) {
1183  for (int lonIndex = 0; lonIndex < edgeLonCount; lonIndex++) {
1184  Latitude &lat = edgeLats[latIndex];
1185  Longitude &lon = edgeLons[lonIndex];
1186 
1187  if (lat.isValid() && lon.isValid() &&
1188  (lat != centerLat || lon != centerLon) &&
1189  m_gmap->SetGround(lat, lon)) {
1190  m_featureEdgeLineSamples->append(
1191  QPair<double, double>(m_gmap->Sample(), m_gmap->Line()));
1192  }
1193  }
1194  }
1195  }
1196 
1197  else {
1198  QList< QPair<Latitude, Longitude> > edgeLatLons;
1199 
1200  if (vectorType == FeatureNomenclatureTool::Arrows4) {
1201  edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1202  m_feature.centerLongitude()));
1203  edgeLatLons.append(qMakePair(m_feature.centerLatitude(),
1204  m_feature.westernLongitude()));
1205  edgeLatLons.append(qMakePair(m_feature.centerLatitude(),
1206  m_feature.easternLongitude()));
1207  edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1208  m_feature.centerLongitude()));
1209  }
1210 
1211  if (vectorType == FeatureNomenclatureTool::Box) {
1212  edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1213  m_feature.easternLongitude()));
1214  edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1215  m_feature.westernLongitude()));
1216  edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1217  m_feature.westernLongitude()));
1218  edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1219  m_feature.easternLongitude()));
1220  }
1221 
1222  int edgeLatLonCount = edgeLatLons.count();
1223 
1224  for (int edgeIndex = 0; edgeIndex < edgeLatLonCount; edgeIndex++) {
1225  Latitude &lat = edgeLatLons[edgeIndex].first;
1226  Longitude &lon = edgeLatLons[edgeIndex].second;
1227 
1228  if (lat.isValid() && lon.isValid() &&
1229  (lat != centerLat || lon != centerLon) &&
1230  m_gmap->SetGround(lat, lon)) {
1231  m_featureEdgeLineSamples->append(
1232  QPair<double, double>(m_gmap->Sample(), m_gmap->Line()));
1233  }
1234  }
1235  }
1236  }
1237 
1238 
1245  std::swap(m_centerLine, other.m_centerLine);
1246  std::swap(m_centerSample, other.m_centerSample);
1247  std::swap(m_featureEdgeLineSamples, other.m_featureEdgeLineSamples);
1248  std::swap(m_feature, other.m_feature);
1249  std::swap(m_gmap, other.m_gmap);
1250  }
1251 
1252 
1263  const FeaturePosition &rhs) {
1264  FeaturePosition copy(rhs);
1265  swap(copy);
1266  return *this;
1267  }
1268 
1269 
1277  return m_feature;
1278  }
1279 
1280 
1285  m_textRect = NULL;
1286  m_fullDisplayRect = NULL;
1287  m_edgePoints = NULL;
1288 
1289  m_textRect = new QRect;
1290  m_fullDisplayRect = new QRect;
1291  m_edgePoints = new QList<QPoint>;
1292  }
1293 
1294 
1306  QRect textRect, QRect fullDisplayRect, QList<QPoint> edgePoints) {
1307  m_textRect = NULL;
1308  m_fullDisplayRect = NULL;
1309  m_edgePoints = NULL;
1310 
1311  m_textRect = new QRect(textRect);
1312  m_fullDisplayRect = new QRect(fullDisplayRect);
1313  m_edgePoints = new QList<QPoint>(edgePoints);
1314  }
1315 
1316 
1322  const FeatureDisplayPosition &other) {
1323  m_textRect = NULL;
1324  m_fullDisplayRect = NULL;
1325  m_edgePoints = NULL;
1326 
1327  m_textRect = new QRect(*other.m_textRect);
1328  m_fullDisplayRect = new QRect(*other.m_fullDisplayRect);
1329  m_edgePoints = new QList<QPoint>(*other.m_edgePoints);
1330  }
1331 
1332 
1337  delete m_textRect;
1338  m_textRect = NULL;
1339 
1340  delete m_fullDisplayRect;
1341  m_fullDisplayRect = NULL;
1342 
1343  delete m_edgePoints;
1344  m_edgePoints = NULL;
1345  }
1346 
1347 
1355  return *m_textRect;
1356  }
1357 
1358 
1366  return *m_fullDisplayRect;
1367  }
1368 
1369 
1377  const {
1378  return *m_edgePoints;
1379  }
1380 
1381 
1389  FeatureDisplayPosition &other) {
1390  std::swap(m_textRect, other.m_textRect);
1391  std::swap(m_fullDisplayRect, other.m_fullDisplayRect);
1392  std::swap(m_edgePoints, other.m_edgePoints);
1393  }
1394 
1395 
1405  const FeatureDisplayPosition &rhs) {
1406  FeatureDisplayPosition copy(rhs);
1407  swap(copy);
1408  return *this;
1409  }
1410 
1411 
1416  m_sourceViewport = NULL;
1417  m_features = NULL;
1418  m_featureScreenAreas = NULL;
1419  m_viewportCubeRange = NULL;
1420 
1421  m_features = new QList<FeaturePosition>;
1422  m_featureScreenAreas = new QList<FeatureDisplayPosition>;
1423  m_viewportCubeRange = new QPair<QPointF, QPointF>;
1424  }
1425 
1426 
1436  FeatureNomenclatureTool *tool, MdiCubeViewport *sourceViewport,
1438  m_sourceViewport = sourceViewport;
1439  m_features = NULL;
1440  m_featureScreenAreas = NULL;
1441  m_viewportCubeRange = NULL;
1442 
1443  m_features = new QList<FeaturePosition>;
1444  m_featureScreenAreas = new QList<FeatureDisplayPosition>;
1445  m_viewportCubeRange = new QPair<QPointF, QPointF>;
1446 
1447  qSort(features.begin(), features.end(),
1449 
1450  for (int i = 0; i < features.count(); i++) {
1451  FeaturePosition display(sourceViewport, features[i], vectorType);
1452  if (display.isValid()) {
1453  m_features->append(display);
1454  }
1455  }
1456 
1457  handleViewChanged(tool);
1458  }
1459 
1460 
1467  const ViewportFeatureDisplay &other) {
1468  m_sourceViewport = other.m_sourceViewport;
1469  m_features = NULL;
1470  m_featureScreenAreas = NULL;
1471  m_viewportCubeRange = NULL;
1472 
1473  m_features = new QList<FeaturePosition>(*other.m_features);
1474  m_featureScreenAreas = new QList<FeatureDisplayPosition>(
1475  *other.m_featureScreenAreas);
1476  m_viewportCubeRange = new QPair<QPointF, QPointF>(
1477  *other.m_viewportCubeRange);
1478  }
1479 
1480 
1485  m_sourceViewport = NULL;
1486 
1487  delete m_features;
1488  m_features = NULL;
1489 
1490  delete m_featureScreenAreas;
1491  m_featureScreenAreas = NULL;
1492 
1493  delete m_viewportCubeRange;
1494  m_viewportCubeRange = NULL;
1495  }
1496 
1497 
1504  for (int i = 0; i < m_features->count(); i++) {
1505  (*m_features)[i].applyExtentType(vectorType);
1506  }
1507  }
1508 
1509 
1517  FeatureNomenclature::Feature feature) {
1518  QString displayName = feature.displayName();
1519 
1520  int foundIndex = -1;
1521  for (int i = 0; foundIndex == -1 && i < m_features->count(); i++) {
1522  if (displayName == m_features->at(i).feature().displayName()) {
1523  foundIndex = i;
1524  }
1525  }
1526 
1527  if (foundIndex != -1) {
1528  m_features->prepend(m_features->takeAt(foundIndex));
1529  m_featureScreenAreas->prepend(m_featureScreenAreas->takeAt(foundIndex));
1530 
1531  // The center() method creates artifacts/displays bad data in the viewport
1532  // ... the old 'cube' before centering stays around.
1533  m_sourceViewport->setScale(m_sourceViewport->scale(),
1534  m_features->first().center().first,
1535  m_features->first().center().second);
1536  m_sourceViewport->viewport()->update();
1537  }
1538  }
1539 
1540 
1550 
1551  for (int i = 0; i < m_features->count(); i++)
1552  featureList.append((*m_features)[i].feature());
1553 
1554  return featureList;
1555  }
1556 
1557 
1566 
1567  for (int i = 0; i < m_features->count(); i++)
1568  positionList.append((*m_features)[i]);
1569 
1570  return positionList;
1571  }
1572 
1573 
1579  MdiCubeViewport *
1581  return m_sourceViewport;
1582  }
1583 
1584 
1594  QPainter *painter, bool showVectors, VectorType vectorType, bool approvedOnly) const {
1595 
1596  if (viewportCubeRange() == *m_viewportCubeRange) {
1597 
1598  for (int i = 0; i < m_features->count() &&
1599  i < m_featureScreenAreas->count(); i++) {
1600 
1601  FeatureNomenclature::Feature feature = (*m_features)[i].feature();
1602  FeatureDisplayPosition pos = (*m_featureScreenAreas)[i];
1603  QRect textArea = pos.textArea();
1604  QRect fullArea = pos.displayArea();
1605 
1606  if (!fullArea.isNull() && fullArea != textArea && showVectors) {
1607  // For efficiency's sake
1608  QRect startRect = textArea.adjusted(-2, -2, 2, 2);
1609  QLineF topTextBorder(startRect.topLeft(), startRect.topRight());
1610  QLineF rightTextBorder(startRect.topRight(), startRect.bottomRight());
1611  QLineF bottomTextBorder(startRect.bottomLeft(),
1612  startRect.bottomRight());
1613  QLineF leftTextBorder(startRect.topLeft(), startRect.bottomLeft());
1614 
1615  QList<QLine> vectors;
1616 
1617  if (vectorType != Box) {
1618  foreach (QPoint point, pos.edgePoints()) {
1619  QLineF fullVector(textArea.center(), point);
1620  QPoint newVectorStart;
1621 
1622  QPointF intersectionPoint;
1623 
1624  if (point.y() < textArea.top()) {
1625  if (topTextBorder.intersect(fullVector, &intersectionPoint) ==
1626  QLineF::BoundedIntersection) {
1627  newVectorStart = QPoint(qRound(intersectionPoint.x()),
1628  qRound(intersectionPoint.y()));
1629  }
1630  }
1631 
1632  if (point.x() > textArea.right()) {
1633  if (rightTextBorder.intersect(fullVector, &intersectionPoint) ==
1634  QLineF::BoundedIntersection) {
1635  newVectorStart = QPoint(qRound(intersectionPoint.x()),
1636  qRound(intersectionPoint.y()));
1637  }
1638  }
1639 
1640  if (point.y() > textArea.bottom()) {
1641  if (bottomTextBorder.intersect(fullVector, &intersectionPoint) ==
1642  QLineF::BoundedIntersection) {
1643  newVectorStart = QPoint(qRound(intersectionPoint.x()),
1644  qRound(intersectionPoint.y()));
1645  }
1646  }
1647 
1648  if (point.x() < textArea.left()) {
1649  if (leftTextBorder.intersect(fullVector, &intersectionPoint) ==
1650  QLineF::BoundedIntersection) {
1651  newVectorStart = QPoint(qRound(intersectionPoint.x()),
1652  qRound(intersectionPoint.y()));
1653  }
1654  }
1655 
1656  if (!newVectorStart.isNull() &&
1657  QLineF(newVectorStart, point).length() > 10) {
1658  vectors.append(QLine(newVectorStart, point));
1659  }
1660  }
1661 
1662  foreach (QLine vector, vectors) {
1663  painter->drawLine(vector);
1664 
1665  //For 4 or 8 arrows
1666  Angle normalAngle(-1 * QLineF(vector).normalVector().angle(),
1667  Angle::Degrees);
1668 
1669  int magnitude = 10;
1670  double deltaX = magnitude * cos(normalAngle.radians());
1671  double deltaY = magnitude * sin(normalAngle.radians());
1672 
1673  QPoint normalStart(vector.x2() + deltaX, vector.y2() + deltaY);
1674  QPoint normalEnd(vector.x2() - deltaX, vector.y2() - deltaY);
1675  painter->drawLine(normalStart, normalEnd);
1676 
1677  Angle arrowheadAngle(30, Angle::Degrees);
1678  Angle vectorAngle(-1 * QLineF(vector).angle(), Angle::Degrees);
1679  Angle leftHead = vectorAngle - arrowheadAngle;
1680  Angle rightHead = vectorAngle + arrowheadAngle;
1681 
1682  int arrowheadMag = 10;
1683  deltaX = arrowheadMag * cos(leftHead.radians());
1684  deltaY = arrowheadMag * sin(leftHead.radians());
1685  painter->drawLine(
1686  vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1687 
1688  deltaX = arrowheadMag * cos(rightHead.radians());
1689  deltaY = arrowheadMag * sin(rightHead.radians());
1690  painter->drawLine(
1691  vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1692  }
1693  }
1694  else {
1695  // vector type == Box, draw the box
1696  if (pos.edgePoints().count() == 4) {
1697  QPolygon boundingPoly(pos.edgePoints().toVector());
1698  painter->drawPolygon(boundingPoly);
1699  }
1700  }
1701  }
1702 
1703  if (!textArea.isNull()) {
1704  QString featureName = feature.name();
1705  painter->drawText(textArea, featureName);
1706  }
1707  }
1708  }
1709  }
1710 
1711 
1721  FeatureNomenclatureTool *tool, QPoint p, Qt::MouseButton s) {
1722  for (int i = 0; i < m_featureScreenAreas->count(); i++) {
1723  QRect screenArea = m_featureScreenAreas->at(i).displayArea();
1724 
1725  if (screenArea.contains(p)) {
1726  FeatureNomenclature::Feature feature = m_features->at(i).feature();
1727  if (s == Qt::LeftButton) {
1728  tool->showFeatureDetails(feature);
1729  }
1730  else if (s == Qt::RightButton) {
1731  QMenu menu;
1732 
1733  QAction *title = menu.addAction(feature.displayName());
1734  title->setEnabled(false);
1735  menu.addSeparator();
1736 
1737  QAction *details = menu.addAction("Details...");
1738  QAction *website = menu.addAction("Website...");
1739  menu.addSeparator();
1740  QAction *center = menu.addAction("Center on Feature");
1741  QAction *copyUrl = menu.addAction("Copy Website URL");
1742 
1743  // This manual adjustment of the pos is a hack... probably due to our
1744  // cursors.
1745  QAction *selectedAction = menu.exec(
1746  m_sourceViewport->viewport()->mapToGlobal(p) +
1747  QPoint(0, 20), details);
1748 
1749  if (selectedAction == details) {
1750  tool->showFeatureDetails(feature);
1751  }
1752  else if (selectedAction == website) {
1753  tool->showFeatureWebsite(feature);
1754  }
1755  else if (selectedAction == center) {
1756  tool->centerOnFeature(m_sourceViewport, feature);
1757  }
1758  else if (selectedAction == copyUrl) {
1759  QApplication::clipboard()->setText(
1760  feature.referenceUrl().toString());
1761  }
1762  }
1763  }
1764  }
1765  }
1766 
1767 
1775  FeatureNomenclatureTool *tool) {
1776  m_featureScreenAreas->clear();
1777 
1778  QFont fontToUse;
1779  fontToUse.setPointSize(tool->fontSize());
1780  QFontMetrics fontMetrics(fontToUse);
1781 
1782  // Don't draw text that overlaps existing text.
1783  QList<QRect> rectsToAvoid;
1784 
1785  for (int i = 0; i < m_features->count(); i++) {
1786  FeatureNomenclature::Feature feature = (*m_features)[i].feature();
1787 
1788  m_featureScreenAreas->append(FeatureDisplayPosition());
1789 
1790  if ( !tool->m_showApprovedOnly ||
1791  (tool->m_showApprovedOnly && feature.status() == FeatureNomenclature::Approved) ) {
1792 
1793  double sample = (*m_features)[i].center().first;
1794  double line = (*m_features)[i].center().second;
1795 
1796  int viewportX;
1797  int viewportY;
1798  m_sourceViewport->cubeToViewport(sample, line,
1799  viewportX, viewportY);
1800 
1801  QString featureName = feature.name();
1802  QRect textDisplayArea(QPoint(viewportX, viewportY),
1803  QSize(fontMetrics.width(featureName) + 4,
1804  fontMetrics.height()));
1805  // Center the text on the viewportX,Y instead of starting it there...
1806  textDisplayArea.moveTopLeft(textDisplayArea.topLeft() -
1807  QPoint(textDisplayArea.width() / 2, textDisplayArea.height() / 2));
1808 
1809  bool canDisplay = false;
1810  if (textDisplayArea.left() < m_sourceViewport->width() &&
1811  textDisplayArea.right() > 0 &&
1812  textDisplayArea.top() < m_sourceViewport->height() &&
1813  textDisplayArea.bottom() > 0) {
1814  canDisplay = true;
1815  }
1816 
1817  QRect fullDisplayArea = textDisplayArea;
1818  QPair<double, double> edge;
1819  QList<QPoint> edgeScreenPoints;
1820 
1821  if (canDisplay && tool->vectorType() != None) {
1822  QList< QPair<double, double> > edges = (*m_features)[i].edges();
1823  foreach (edge, edges) {
1824  m_sourceViewport->cubeToViewport(edge.first, edge.second,
1825  viewportX, viewportY);
1826  edgeScreenPoints.append(QPoint(viewportX, viewportY));
1827  }
1828 
1829  if (tool->vectorType() != Box) {
1830  foreach (QPoint screenPoint, edgeScreenPoints) {
1831  fullDisplayArea = fullDisplayArea.united(
1832  QRect(screenPoint.x() - 3, screenPoint.y() - 3, 6, 6));
1833  }
1834  }
1835  else if (edges.count() == 4) {
1836 
1837  QPolygon boundingPoly(edgeScreenPoints.toVector());
1838 
1839  if (boundingPoly.intersected(textDisplayArea) == QPolygon(textDisplayArea, true)) {
1840  fullDisplayArea = boundingPoly.boundingRect();
1841  }
1842  }
1843  }
1844 
1845  if (canDisplay) {
1846  // If we intersect another feature, do not draw
1847  foreach (QRect rectToAvoid, rectsToAvoid) {
1848  if (canDisplay && fullDisplayArea.intersects(rectToAvoid)) {
1849  canDisplay = false;
1850  }
1851  }
1852  }
1853 
1854  if (canDisplay) {
1855  rectsToAvoid.append(fullDisplayArea);
1856  m_featureScreenAreas->last() = FeatureDisplayPosition(textDisplayArea, fullDisplayArea,
1857  edgeScreenPoints);
1858  }
1859  }
1860  *m_viewportCubeRange = viewportCubeRange();
1861  }
1862  }
1863 
1864 
1871  ViewportFeatureDisplay &other) {
1872  std::swap(m_sourceViewport, other.m_sourceViewport);
1873  std::swap(m_features, other.m_features);
1874  std::swap(m_featureScreenAreas, other.m_featureScreenAreas);
1875  std::swap(m_viewportCubeRange, other.m_viewportCubeRange);
1876  }
1877 
1878 
1888  const ViewportFeatureDisplay &rhs) {
1889  ViewportFeatureDisplay copy(rhs);
1890  swap(copy);
1891  return *this;
1892  }
1893 
1894 
1904  const {
1905  QPointF minValues;
1906  sourceViewport()->viewportToCube(1, 1, minValues.rx(), minValues.ry());
1907 
1908  QPointF maxValues;
1909  sourceViewport()->viewportToCube(sourceViewport()->viewport()->width(),
1910  sourceViewport()->viewport()->height(),
1911  maxValues.rx(), maxValues.ry());
1912 
1913  return QPair<QPointF, QPointF>(minValues, maxValues);
1914  }
1915 }