2 #include "FeatureNomenclatureTool.h"
5 #include <QApplication>
11 #include <QDesktopServices>
13 #include <QHBoxLayout>
16 #include <QMessageBox>
17 #include <QProgressBar>
18 #include <QPushButton>
22 #include <geos/geom/Coordinate.h>
23 #include <geos/geom/CoordinateSequence.h>
24 #include <geos/geom/MultiPolygon.h>
32 #include "NomenclatureToolConfigDialog.h"
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>";
85 connect(
this, SIGNAL(toolActivated()),
116 m_action = menu->addAction(
"Show Nomenclature");
120 connect(
m_action, SIGNAL(triggered(
bool)),
138 painter->setFont(fontToUse);
220 vp->viewport()->update();
239 vp->viewport()->update();
257 vp->viewport()->update();
274 (*m_foundNomenclature)[i].applyExtentType(
m_extentType);
280 vp->viewport()->update();
303 QStackedWidget *parent) {
312 QLabel *foundFeaturesLabel =
new QLabel(
"Found Features:");
315 QComboBox::AdjustToContents);
340 QHBoxLayout *layout =
new QHBoxLayout;
341 layout->setMargin(0);
343 layout->addWidget(foundFeaturesLabel);
349 layout->addStretch(1);
350 wrapperWidget->setLayout(layout);
351 return wrapperWidget;
366 action->setIcon(QPixmap(
toolIconDir() +
"/nomenclature.png"));
367 action->setToolTip(
"Nomenclature (N)");
368 action->setShortcut(Qt::Key_N);
369 action->setObjectName(
"nomenclatureToolButton");
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);
391 QPoint p, Qt::MouseButton s) {
434 qobject_cast<QWidget *>(parent()));
435 configDialog->setAttribute(Qt::WA_DeleteOnClose);
436 configDialog->show();
477 viewport->viewport()->update();
487 if (newState == Qt::Unchecked) {
491 else if (newState == Qt::Checked) {
499 vp->viewport()->update();
513 (*m_foundNomenclature)[i].handleViewChanged(
this);
534 QMessageBox::warning(qobject_cast<QWidget *>(parent()),
552 if (viewport == vp ||
584 QProgressDialog updatingFeaturesProgress(
585 tr(
"Projecting Features for [%1]").arg(vp->
cube()->
fileName().section(
'/',-1)),
588 updatingFeaturesProgress.setWindowModality(Qt::WindowModal);
592 for (
int i = 0; i < features.count(); i++) {
593 feature = features[i];
595 int progress = floor(100 * (
double)i / (
double)features.count());
597 if (progress != updatingFeaturesProgress.value())
598 updatingFeaturesProgress.setValue(progress);
600 if (updatingFeaturesProgress.wasCanceled()) {
611 QString displayName = feature.
cleanName() +
614 QString targetName = feature.
target().toUpper();
621 while (!foundInsertPos) {
623 foundInsertPos =
true;
628 QString insertPosTarget = insetPosData.toMap()[
"Target"].toString();
630 if (targetName < insertPosTarget) {
631 foundInsertPos =
true;
633 else if (targetName == insertPosTarget) {
634 if (!insetPosData.toMap()[
"Viewport"].isNull()) {
635 foundInsertPos = displayName.compare(
637 Qt::CaseInsensitive) < 0;
647 "Target"].toString() != targetName) {
649 data[
"Target"] = targetName;
652 if (controlNet !=
"")
653 controlNet =
" (" + controlNet +
")";
656 targetName + controlNet,
673 data[
"Feature"] = QVariant::fromValue<FeatureNomenclature::Feature>(
675 data[
"Viewport"] = qVariantFromValue(vp);
676 data[
"Target"] = qVariantFromValue(targetName);
679 qVariantFromValue(data));
682 updatingFeaturesProgress.setValue( features.count() );
702 removedViewports.removeOne(vp);
712 bool removedAViewport =
false;
716 removedAViewport =
true;
719 if (removedAViewport) {
749 target = mappingGrp[
"TargetName"][0];
762 (*m_nomenclatureSearchers)[vp] = searcher;
764 (*m_nomenclatureSearchers)[vp]->queryFeatures(target.toUpper(),
815 QDialog *detailsDialog =
new QDialog(qobject_cast<QWidget *>(parent()));
816 detailsDialog->setAttribute(Qt::WA_DeleteOnClose);
818 QVBoxLayout *mainLayout =
new QVBoxLayout;
819 detailsDialog->setLayout(mainLayout);
821 mainLayout->addWidget(feature.
toWidget());
825 QHBoxLayout *buttonsAreaLayout =
new QHBoxLayout;
826 buttonsAreaWrapper->setLayout(buttonsAreaLayout);
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);
835 mainLayout->addWidget(buttonsAreaWrapper);
837 detailsDialog->show();
860 if (isCurrentlyLoading) {
894 (*m_nomenclatureSearchers)[vp]->deleteLater();
990 FileName config(
"$HOME/.Isis/qview/nomenclature.config");
992 config.expanded(), QSettings::NativeFormat);
994 m_fontSize = settings.value(
"fontSize", m_fontSize).toInt();
997 settings.value(
"defaultEnabled", m_defaultEnabled).toBool();
1012 FileName config(
"$HOME/.Isis/qview/nomenclature.config");
1014 config.expanded(), QSettings::NativeFormat);
1016 settings.setValue(
"fontColor", qVariantFromValue(*
m_fontColor));
1048 m_centerLine =
Null;
1049 m_centerSample =
Null;
1050 m_featureEdgeLineSamples = NULL;
1055 m_feature = feature;
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();
1065 applyExtentType(vectorType);
1080 m_featureEdgeLineSamples = NULL;
1097 m_centerLine =
Null;
1098 m_centerSample =
Null;
1101 delete m_featureEdgeLineSamples;
1102 m_featureEdgeLineSamples = NULL;
1112 return (m_centerSample !=
Null && m_centerLine !=
Null);
1134 return *m_featureEdgeLineSamples;
1159 Latitude centerLat = m_feature.centerLatitude();
1160 Longitude centerLon = m_feature.centerLongitude();
1162 m_featureEdgeLineSamples->clear();
1171 edgeLats.append(m_feature.northernLatitude());
1172 edgeLats.append(m_feature.centerLatitude());
1173 edgeLats.append(m_feature.southernLatitude());
1175 edgeLons.append(m_feature.easternLongitude());
1176 edgeLons.append(m_feature.centerLongitude());
1177 edgeLons.append(m_feature.westernLongitude());
1179 int edgeLatCount = edgeLats.count();
1180 int edgeLonCount = edgeLons.count();
1182 for (
int latIndex = 0; latIndex < edgeLatCount; latIndex++) {
1183 for (
int lonIndex = 0; lonIndex < edgeLonCount; lonIndex++) {
1184 Latitude &lat = edgeLats[latIndex];
1188 (lat != centerLat || lon != centerLon) &&
1189 m_gmap->SetGround(lat, lon)) {
1190 m_featureEdgeLineSamples->append(
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()));
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()));
1222 int edgeLatLonCount = edgeLatLons.count();
1224 for (
int edgeIndex = 0; edgeIndex < edgeLatLonCount; edgeIndex++) {
1225 Latitude &lat = edgeLatLons[edgeIndex].first;
1226 Longitude &lon = edgeLatLons[edgeIndex].second;
1229 (lat != centerLat || lon != centerLon) &&
1230 m_gmap->SetGround(lat, lon)) {
1231 m_featureEdgeLineSamples->append(
1249 std::swap(m_gmap, other.
m_gmap);
1286 m_fullDisplayRect = NULL;
1287 m_edgePoints = NULL;
1289 m_textRect =
new QRect;
1290 m_fullDisplayRect =
new QRect;
1306 QRect textRect, QRect fullDisplayRect,
QList<QPoint> edgePoints) {
1308 m_fullDisplayRect = NULL;
1309 m_edgePoints = NULL;
1311 m_textRect =
new QRect(textRect);
1312 m_fullDisplayRect =
new QRect(fullDisplayRect);
1324 m_fullDisplayRect = NULL;
1325 m_edgePoints = NULL;
1340 delete m_fullDisplayRect;
1341 m_fullDisplayRect = NULL;
1343 delete m_edgePoints;
1344 m_edgePoints = NULL;
1366 return *m_fullDisplayRect;
1378 return *m_edgePoints;
1416 m_sourceViewport = NULL;
1418 m_featureScreenAreas = NULL;
1419 m_viewportCubeRange = NULL;
1438 m_sourceViewport = sourceViewport;
1440 m_featureScreenAreas = NULL;
1441 m_viewportCubeRange = NULL;
1447 qSort(features.begin(), features.end(),
1450 for (
int i = 0; i < features.count(); i++) {
1453 m_features->append(display);
1457 handleViewChanged(tool);
1470 m_featureScreenAreas = NULL;
1471 m_viewportCubeRange = NULL;
1485 m_sourceViewport = NULL;
1490 delete m_featureScreenAreas;
1491 m_featureScreenAreas = NULL;
1493 delete m_viewportCubeRange;
1494 m_viewportCubeRange = NULL;
1504 for (
int i = 0; i < m_features->count(); i++) {
1505 (*m_features)[i].applyExtentType(vectorType);
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()) {
1527 if (foundIndex != -1) {
1528 m_features->prepend(m_features->takeAt(foundIndex));
1529 m_featureScreenAreas->prepend(m_featureScreenAreas->takeAt(foundIndex));
1533 m_sourceViewport->setScale(m_sourceViewport->scale(),
1534 m_features->first().center().first,
1535 m_features->first().center().second);
1536 m_sourceViewport->viewport()->update();
1551 for (
int i = 0; i < m_features->count(); i++)
1552 featureList.append((*m_features)[i].feature());
1567 for (
int i = 0; i < m_features->count(); i++)
1568 positionList.append((*m_features)[i]);
1570 return positionList;
1581 return m_sourceViewport;
1596 if (viewportCubeRange() == *m_viewportCubeRange) {
1598 for (
int i = 0; i < m_features->count() &&
1599 i < m_featureScreenAreas->count(); i++) {
1606 if (!fullArea.isNull() && fullArea != textArea && showVectors) {
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());
1617 if (vectorType !=
Box) {
1619 QLineF fullVector(textArea.center(), point);
1620 QPoint newVectorStart;
1622 QPointF intersectionPoint;
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()));
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()));
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()));
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()));
1656 if (!newVectorStart.isNull() &&
1657 QLineF(newVectorStart, point).length() > 10) {
1658 vectors.append(QLine(newVectorStart, point));
1662 foreach (QLine vector, vectors) {
1663 painter->drawLine(vector);
1666 Angle normalAngle(-1 * QLineF(vector).normalVector().angle(),
1670 double deltaX = magnitude * cos(normalAngle.
radians());
1671 double deltaY = magnitude * sin(normalAngle.
radians());
1673 QPoint normalStart(vector.x2() + deltaX, vector.y2() + deltaY);
1674 QPoint normalEnd(vector.x2() - deltaX, vector.y2() - deltaY);
1675 painter->drawLine(normalStart, normalEnd);
1679 Angle leftHead = vectorAngle - arrowheadAngle;
1680 Angle rightHead = vectorAngle + arrowheadAngle;
1682 int arrowheadMag = 10;
1683 deltaX = arrowheadMag * cos(leftHead.
radians());
1684 deltaY = arrowheadMag * sin(leftHead.
radians());
1686 vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1688 deltaX = arrowheadMag * cos(rightHead.
radians());
1689 deltaY = arrowheadMag * sin(rightHead.
radians());
1691 vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1697 QPolygon boundingPoly(pos.
edgePoints().toVector());
1698 painter->drawPolygon(boundingPoly);
1703 if (!textArea.isNull()) {
1704 QString featureName = feature.
name();
1705 painter->drawText(textArea, featureName);
1722 for (
int i = 0; i < m_featureScreenAreas->count(); i++) {
1723 QRect screenArea = m_featureScreenAreas->at(i).displayArea();
1725 if (screenArea.contains(p)) {
1727 if (s == Qt::LeftButton) {
1730 else if (s == Qt::RightButton) {
1734 title->setEnabled(
false);
1735 menu.addSeparator();
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");
1745 QAction *selectedAction = menu.exec(
1746 m_sourceViewport->viewport()->mapToGlobal(p) +
1747 QPoint(0, 20), details);
1749 if (selectedAction == details) {
1752 else if (selectedAction == website) {
1755 else if (selectedAction == center) {
1758 else if (selectedAction == copyUrl) {
1759 QApplication::clipboard()->setText(
1776 m_featureScreenAreas->clear();
1779 fontToUse.setPointSize(tool->
fontSize());
1780 QFontMetrics fontMetrics(fontToUse);
1785 for (
int i = 0; i < m_features->count(); i++) {
1793 double sample = (*m_features)[i].center().first;
1794 double line = (*m_features)[i].center().second;
1798 m_sourceViewport->cubeToViewport(sample, line,
1799 viewportX, viewportY);
1801 QString featureName = feature.
name();
1802 QRect textDisplayArea(QPoint(viewportX, viewportY),
1803 QSize(fontMetrics.width(featureName) + 4,
1804 fontMetrics.height()));
1806 textDisplayArea.moveTopLeft(textDisplayArea.topLeft() -
1807 QPoint(textDisplayArea.width() / 2, textDisplayArea.height() / 2));
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) {
1817 QRect fullDisplayArea = textDisplayArea;
1823 foreach (edge, edges) {
1824 m_sourceViewport->cubeToViewport(edge.first, edge.second,
1825 viewportX, viewportY);
1826 edgeScreenPoints.append(QPoint(viewportX, viewportY));
1830 foreach (QPoint screenPoint, edgeScreenPoints) {
1831 fullDisplayArea = fullDisplayArea.united(
1832 QRect(screenPoint.x() - 3, screenPoint.y() - 3, 6, 6));
1835 else if (edges.count() == 4) {
1837 QPolygon boundingPoly(edgeScreenPoints.toVector());
1839 if (boundingPoly.intersected(textDisplayArea) == QPolygon(textDisplayArea,
true)) {
1840 fullDisplayArea = boundingPoly.boundingRect();
1847 foreach (QRect rectToAvoid, rectsToAvoid) {
1848 if (canDisplay && fullDisplayArea.intersects(rectToAvoid)) {
1855 rectsToAvoid.append(fullDisplayArea);
1860 *m_viewportCubeRange = viewportCubeRange();
1906 sourceViewport()->viewportToCube(1, 1, minValues.rx(), minValues.ry());
1909 sourceViewport()->viewportToCube(sourceViewport()->viewport()->width(),
1910 sourceViewport()->viewport()->height(),
1911 maxValues.rx(), maxValues.ry());