2 #include "FeatureNomenclatureTool.h"
5 #include <QApplication>
11 #include <QDesktopServices>
13 #include <QHBoxLayout>
16 #include <QMessageBox>
17 #include <QProgressBar>
18 #include <QPushButton>
23 #include <geos/geom/Coordinate.h>
24 #include <geos/geom/CoordinateSequence.h>
25 #include <geos/geom/MultiPolygon.h>
29 #include "ImagePolygon.h"
31 #include "Longitude.h"
32 #include "MdiCubeViewport.h"
33 #include "NomenclatureToolConfigDialog.h"
34 #include "PolygonTools.h"
71 "in your opened cube files. This tool <strong>requires</strong> an "
72 "active internet connection, projection or camera information, and a "
73 "calculatable ground range to function. The larger the ground range ("
74 "covered area on a planet), the longer it will take to populate the "
75 "nomenclature for a particular cube.<br/><br/>"
76 "<font color='red'>**WARNING**</font> The accuracy of this tool is not "
77 "perfect, features <strong>can and will be mislabeled</strong> if you "
78 "have not properly controlled your images to the control network that "
79 "identifies the latitude/longitude values of a feature. Please use the "
80 "nomenclature website to verify a label is correct for a feature. "
81 "<br/><br/>See the IAU Gazetteer of Planetary Nomenclature website for "
82 "more information.<br/>"
83 "<a href='http://planetarynames.wr.usgs.gov/'>"
84 "http://planetarynames.wr.usgs.gov/</a>";
86 connect(
this, SIGNAL(toolActivated()),
117 m_action = menu->addAction(
"Show Nomenclature");
121 connect(
m_action, SIGNAL(triggered(
bool)),
139 painter->setFont(fontToUse);
221 vp->viewport()->update();
240 vp->viewport()->update();
258 vp->viewport()->update();
275 (*m_foundNomenclature)[i].applyExtentType(
m_extentType);
281 vp->viewport()->update();
304 QStackedWidget *parent) {
313 QLabel *foundFeaturesLabel =
new QLabel(
"Found Features:");
316 QComboBox::AdjustToContents);
341 QHBoxLayout *layout =
new QHBoxLayout;
342 layout->setMargin(0);
344 layout->addWidget(foundFeaturesLabel);
350 layout->addStretch(1);
351 wrapperWidget->setLayout(layout);
352 return wrapperWidget;
367 action->setIcon(QPixmap(
toolIconDir() +
"/nomenclature.png"));
368 action->setToolTip(
"Nomenclature (N)");
369 action->setShortcut(Qt::Key_N);
370 action->setObjectName(
"nomenclatureToolButton");
373 "<b>Function:</b> Display nomenclature on the visible images.\n"
374 "<p/><b>Hint:</b> While this tool is active, you can left and right "
375 "click on any of the named features for additional options."
376 "<p/><b>Shortcut:</b> N";
377 action->setWhatsThis(text);
392 QPoint p, Qt::MouseButton s) {
435 qobject_cast<QWidget *>(parent()));
436 configDialog->setAttribute(Qt::WA_DeleteOnClose);
437 configDialog->show();
478 viewport->viewport()->update();
488 if (newState == Qt::Unchecked) {
492 else if (newState == Qt::Checked) {
500 vp->viewport()->update();
514 (*m_foundNomenclature)[i].handleViewChanged(
this);
535 QMessageBox::warning(qobject_cast<QWidget *>(parent()),
553 if (viewport == vp ||
585 QProgressDialog updatingFeaturesProgress(
586 tr(
"Projecting Features for [%1]").arg(vp->
cube()->
fileName().section(
'/',-1)),
589 updatingFeaturesProgress.setWindowModality(Qt::WindowModal);
593 for (
int i = 0; i < features.count(); i++) {
594 feature = features[i];
596 int progress = floor(100 * (
double)i / (
double)features.count());
598 if (progress != updatingFeaturesProgress.value())
599 updatingFeaturesProgress.setValue(progress);
601 if (updatingFeaturesProgress.wasCanceled()) {
612 QString displayName = feature.
cleanName() +
615 QString targetName = feature.
target().toUpper();
622 while (!foundInsertPos) {
624 foundInsertPos =
true;
629 QString insertPosTarget = insetPosData.toMap()[
"Target"].toString();
631 if (targetName < insertPosTarget) {
632 foundInsertPos =
true;
634 else if (targetName == insertPosTarget) {
635 if (!insetPosData.toMap()[
"Viewport"].isNull()) {
636 foundInsertPos = displayName.compare(
638 Qt::CaseInsensitive) < 0;
648 "Target"].toString() != targetName) {
650 data[
"Target"] = targetName;
653 if (controlNet !=
"")
654 controlNet =
" (" + controlNet +
")";
657 targetName + controlNet,
674 data[
"Feature"] = QVariant::fromValue<FeatureNomenclature::Feature>(
676 data[
"Viewport"] = qVariantFromValue(vp);
677 data[
"Target"] = qVariantFromValue(targetName);
680 qVariantFromValue(data));
683 updatingFeaturesProgress.setValue( features.count() );
703 removedViewports.removeOne(vp);
713 bool removedAViewport =
false;
717 removedAViewport =
true;
720 if (removedAViewport) {
750 target = mappingGrp[
"TargetName"][0];
763 (*m_nomenclatureSearchers)[vp] = searcher;
765 (*m_nomenclatureSearchers)[vp]->queryFeatures(target.toUpper(),
816 QDialog *detailsDialog =
new QDialog(qobject_cast<QWidget *>(parent()));
817 detailsDialog->setAttribute(Qt::WA_DeleteOnClose);
819 QVBoxLayout *mainLayout =
new QVBoxLayout;
820 detailsDialog->setLayout(mainLayout);
822 mainLayout->addWidget(feature.
toWidget());
826 QHBoxLayout *buttonsAreaLayout =
new QHBoxLayout;
827 buttonsAreaWrapper->setLayout(buttonsAreaLayout);
829 buttonsAreaLayout->addStretch();
830 QPushButton *okayBtn =
new QPushButton(
"&Ok");
831 okayBtn->setIcon(QIcon::fromTheme(
"dialog-ok"));
832 connect(okayBtn, SIGNAL(clicked()),
833 detailsDialog, SLOT(accept()));
834 buttonsAreaLayout->addWidget(okayBtn);
836 mainLayout->addWidget(buttonsAreaWrapper);
838 detailsDialog->show();
861 if (isCurrentlyLoading) {
895 (*m_nomenclatureSearchers)[vp]->deleteLater();
926 return &(*m_foundNomenclature)[i];
945 return &(*m_foundNomenclature)[i];
991 FileName config(
"$HOME/.Isis/qview/nomenclature.config");
993 config.
expanded(), QSettings::NativeFormat);
1013 FileName config(
"$HOME/.Isis/qview/nomenclature.config");
1015 config.
expanded(), QSettings::NativeFormat);
1017 settings.setValue(
"fontColor", qVariantFromValue(*
m_fontColor));
1049 m_centerLine =
Null;
1050 m_centerSample =
Null;
1051 m_featureEdgeLineSamples = NULL;
1056 m_feature = feature;
1060 Latitude centerLat = m_feature.centerLatitude();
1061 Longitude centerLon = m_feature.centerLongitude();
1062 if (m_gmap && m_gmap->SetGround(centerLat, centerLon)) {
1063 m_centerSample = m_gmap->Sample();
1064 m_centerLine = m_gmap->Line();
1081 m_featureEdgeLineSamples = NULL;
1098 m_centerLine =
Null;
1099 m_centerSample =
Null;
1102 delete m_featureEdgeLineSamples;
1103 m_featureEdgeLineSamples = NULL;
1113 return (m_centerSample !=
Null && m_centerLine !=
Null);
1135 return *m_featureEdgeLineSamples;
1160 Latitude centerLat = m_feature.centerLatitude();
1161 Longitude centerLon = m_feature.centerLongitude();
1163 m_featureEdgeLineSamples->clear();
1172 edgeLats.append(m_feature.northernLatitude());
1173 edgeLats.append(m_feature.centerLatitude());
1174 edgeLats.append(m_feature.southernLatitude());
1176 edgeLons.append(m_feature.easternLongitude());
1177 edgeLons.append(m_feature.centerLongitude());
1178 edgeLons.append(m_feature.westernLongitude());
1180 int edgeLatCount = edgeLats.count();
1181 int edgeLonCount = edgeLons.count();
1183 for (
int latIndex = 0; latIndex < edgeLatCount; latIndex++) {
1184 for (
int lonIndex = 0; lonIndex < edgeLonCount; lonIndex++) {
1185 Latitude &lat = edgeLats[latIndex];
1189 (lat != centerLat || lon != centerLon) &&
1190 m_gmap->SetGround(lat, lon)) {
1191 m_featureEdgeLineSamples->append(
1202 edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1203 m_feature.centerLongitude()));
1204 edgeLatLons.append(qMakePair(m_feature.centerLatitude(),
1205 m_feature.westernLongitude()));
1206 edgeLatLons.append(qMakePair(m_feature.centerLatitude(),
1207 m_feature.easternLongitude()));
1208 edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1209 m_feature.centerLongitude()));
1213 edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1214 m_feature.easternLongitude()));
1215 edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1216 m_feature.westernLongitude()));
1217 edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1218 m_feature.westernLongitude()));
1219 edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1220 m_feature.easternLongitude()));
1223 int edgeLatLonCount = edgeLatLons.count();
1225 for (
int edgeIndex = 0; edgeIndex < edgeLatLonCount; edgeIndex++) {
1226 Latitude &lat = edgeLatLons[edgeIndex].first;
1227 Longitude &lon = edgeLatLons[edgeIndex].second;
1230 (lat != centerLat || lon != centerLon) &&
1231 m_gmap->SetGround(lat, lon)) {
1232 m_featureEdgeLineSamples->append(
1250 std::swap(m_gmap, other.
m_gmap);
1287 m_fullDisplayRect = NULL;
1288 m_edgePoints = NULL;
1290 m_textRect =
new QRect;
1291 m_fullDisplayRect =
new QRect;
1307 QRect textRect, QRect fullDisplayRect,
QList<QPoint> edgePoints) {
1309 m_fullDisplayRect = NULL;
1310 m_edgePoints = NULL;
1312 m_textRect =
new QRect(textRect);
1313 m_fullDisplayRect =
new QRect(fullDisplayRect);
1325 m_fullDisplayRect = NULL;
1326 m_edgePoints = NULL;
1341 delete m_fullDisplayRect;
1342 m_fullDisplayRect = NULL;
1344 delete m_edgePoints;
1345 m_edgePoints = NULL;
1367 return *m_fullDisplayRect;
1379 return *m_edgePoints;
1417 m_sourceViewport = NULL;
1419 m_featureScreenAreas = NULL;
1420 m_viewportCubeRange = NULL;
1439 m_sourceViewport = sourceViewport;
1441 m_featureScreenAreas = NULL;
1442 m_viewportCubeRange = NULL;
1448 qSort(features.begin(), features.end(),
1451 for (
int i = 0; i < features.count(); i++) {
1454 m_features->append(display);
1458 handleViewChanged(tool);
1471 m_featureScreenAreas = NULL;
1472 m_viewportCubeRange = NULL;
1486 m_sourceViewport = NULL;
1491 delete m_featureScreenAreas;
1492 m_featureScreenAreas = NULL;
1494 delete m_viewportCubeRange;
1495 m_viewportCubeRange = NULL;
1505 for (
int i = 0; i < m_features->count(); i++) {
1506 (*m_features)[i].applyExtentType(
vectorType);
1521 int foundIndex = -1;
1522 for (
int i = 0; foundIndex == -1 && i < m_features->count(); i++) {
1523 if (displayName == m_features->at(i).feature().displayName()) {
1528 if (foundIndex != -1) {
1529 m_features->prepend(m_features->takeAt(foundIndex));
1530 m_featureScreenAreas->prepend(m_featureScreenAreas->takeAt(foundIndex));
1534 m_sourceViewport->setScale(m_sourceViewport->scale(),
1535 m_features->first().center().first,
1536 m_features->first().center().second);
1537 m_sourceViewport->viewport()->update();
1552 for (
int i = 0; i < m_features->count(); i++)
1553 featureList.append((*m_features)[i].feature());
1568 for (
int i = 0; i < m_features->count(); i++)
1569 positionList.append((*m_features)[i]);
1571 return positionList;
1582 return m_sourceViewport;
1597 if (viewportCubeRange() == *m_viewportCubeRange) {
1599 for (
int i = 0; i < m_features->count() &&
1600 i < m_featureScreenAreas->count(); i++) {
1607 if (!fullArea.isNull() && fullArea != textArea && showVectors) {
1609 QRect startRect = textArea.adjusted(-2, -2, 2, 2);
1610 QLineF topTextBorder(startRect.topLeft(), startRect.topRight());
1611 QLineF rightTextBorder(startRect.topRight(), startRect.bottomRight());
1612 QLineF bottomTextBorder(startRect.bottomLeft(),
1613 startRect.bottomRight());
1614 QLineF leftTextBorder(startRect.topLeft(), startRect.bottomLeft());
1620 QLineF fullVector(textArea.center(), point);
1621 QPoint newVectorStart;
1623 QPointF intersectionPoint;
1625 if (point.y() < textArea.top()) {
1626 if (topTextBorder.intersect(fullVector, &intersectionPoint) ==
1627 QLineF::BoundedIntersection) {
1628 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1629 qRound(intersectionPoint.y()));
1633 if (point.x() > textArea.right()) {
1634 if (rightTextBorder.intersect(fullVector, &intersectionPoint) ==
1635 QLineF::BoundedIntersection) {
1636 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1637 qRound(intersectionPoint.y()));
1641 if (point.y() > textArea.bottom()) {
1642 if (bottomTextBorder.intersect(fullVector, &intersectionPoint) ==
1643 QLineF::BoundedIntersection) {
1644 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1645 qRound(intersectionPoint.y()));
1649 if (point.x() < textArea.left()) {
1650 if (leftTextBorder.intersect(fullVector, &intersectionPoint) ==
1651 QLineF::BoundedIntersection) {
1652 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1653 qRound(intersectionPoint.y()));
1657 if (!newVectorStart.isNull() &&
1658 QLineF(newVectorStart, point).length() > 10) {
1659 vectors.append(QLine(newVectorStart, point));
1663 foreach (QLine vector, vectors) {
1664 painter->drawLine(vector);
1667 Angle normalAngle(-1 * QLineF(vector).normalVector().angle(),
1671 double deltaX = magnitude * cos(normalAngle.
radians());
1672 double deltaY = magnitude * sin(normalAngle.
radians());
1674 QPoint normalStart(vector.x2() + deltaX, vector.y2() + deltaY);
1675 QPoint normalEnd(vector.x2() - deltaX, vector.y2() - deltaY);
1676 painter->drawLine(normalStart, normalEnd);
1680 Angle leftHead = vectorAngle - arrowheadAngle;
1681 Angle rightHead = vectorAngle + arrowheadAngle;
1683 int arrowheadMag = 10;
1684 deltaX = arrowheadMag * cos(leftHead.
radians());
1685 deltaY = arrowheadMag * sin(leftHead.
radians());
1687 vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1689 deltaX = arrowheadMag * cos(rightHead.
radians());
1690 deltaY = arrowheadMag * sin(rightHead.
radians());
1692 vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1698 QPolygon boundingPoly(pos.
edgePoints().toVector());
1699 painter->drawPolygon(boundingPoly);
1704 if (!textArea.isNull()) {
1705 QString featureName = feature.
name();
1706 painter->drawText(textArea, featureName);
1723 for (
int i = 0; i < m_featureScreenAreas->count(); i++) {
1724 QRect screenArea = m_featureScreenAreas->at(i).displayArea();
1726 if (screenArea.contains(p)) {
1728 if (s == Qt::LeftButton) {
1731 else if (s == Qt::RightButton) {
1735 title->setEnabled(
false);
1736 menu.addSeparator();
1738 QAction *details = menu.addAction(
"Details...");
1739 QAction *website = menu.addAction(
"Website...");
1740 menu.addSeparator();
1741 QAction *center = menu.addAction(
"Center on Feature");
1742 QAction *copyUrl = menu.addAction(
"Copy Website URL");
1746 QAction *selectedAction = menu.exec(
1747 m_sourceViewport->viewport()->mapToGlobal(p) +
1748 QPoint(0, 20), details);
1750 if (selectedAction == details) {
1753 else if (selectedAction == website) {
1756 else if (selectedAction == center) {
1759 else if (selectedAction == copyUrl) {
1760 QApplication::clipboard()->setText(
1777 m_featureScreenAreas->clear();
1780 fontToUse.setPointSize(tool->
fontSize());
1781 QFontMetrics fontMetrics(fontToUse);
1786 for (
int i = 0; i < m_features->count(); i++) {
1794 double sample = (*m_features)[i].center().first;
1795 double line = (*m_features)[i].center().second;
1799 m_sourceViewport->cubeToViewport(sample, line,
1800 viewportX, viewportY);
1802 QString featureName = feature.
name();
1803 QRect textDisplayArea(QPoint(viewportX, viewportY),
1804 QSize(fontMetrics.width(featureName) + 4,
1805 fontMetrics.height()));
1807 textDisplayArea.moveTopLeft(textDisplayArea.topLeft() -
1808 QPoint(textDisplayArea.width() / 2, textDisplayArea.height() / 2));
1810 bool canDisplay =
false;
1811 if (textDisplayArea.left() < m_sourceViewport->width() &&
1812 textDisplayArea.right() > 0 &&
1813 textDisplayArea.top() < m_sourceViewport->height() &&
1814 textDisplayArea.bottom() > 0) {
1818 QRect fullDisplayArea = textDisplayArea;
1824 foreach (edge, edges) {
1825 m_sourceViewport->cubeToViewport(edge.first, edge.second,
1826 viewportX, viewportY);
1827 edgeScreenPoints.append(QPoint(viewportX, viewportY));
1831 foreach (QPoint screenPoint, edgeScreenPoints) {
1832 fullDisplayArea = fullDisplayArea.united(
1833 QRect(screenPoint.x() - 3, screenPoint.y() - 3, 6, 6));
1836 else if (edges.count() == 4) {
1838 QPolygon boundingPoly(edgeScreenPoints.toVector());
1840 if (boundingPoly.intersected(textDisplayArea) == QPolygon(textDisplayArea,
true)) {
1841 fullDisplayArea = boundingPoly.boundingRect();
1848 foreach (QRect rectToAvoid, rectsToAvoid) {
1849 if (canDisplay && fullDisplayArea.intersects(rectToAvoid)) {
1856 rectsToAvoid.append(fullDisplayArea);
1861 *m_viewportCubeRange = viewportCubeRange();
1907 sourceViewport()->viewportToCube(1, 1, minValues.rx(), minValues.ry());
1910 sourceViewport()->viewportToCube(sourceViewport()->viewport()->width(),
1911 sourceViewport()->viewport()->height(),
1912 maxValues.rx(), maxValues.ry());