1#include "FeatureNomenclatureTool.h"
10#include <QDesktopServices>
16#include <QProgressBar>
22#include <geos/geom/Coordinate.h>
23#include <geos/geom/CoordinateSequence.h>
24#include <geos/geom/MultiPolygon.h>
28#include "ImagePolygon.h"
31#include "MdiCubeViewport.h"
32#include "NomenclatureToolConfigDialog.h"
33#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"] = QVariant::fromValue(vp);
677 data[
"Target"] = QVariant::fromValue(targetName);
680 QVariant::fromValue(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", QVariant::fromValue(*
m_fontColor));
1062 if (
m_gmap &&
m_gmap->SetGround(centerLat, centerLon)) {
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)) {
1202 edgeLatLons.append(qMakePair(
m_feature.northernLatitude(),
1204 edgeLatLons.append(qMakePair(
m_feature.centerLatitude(),
1206 edgeLatLons.append(qMakePair(
m_feature.centerLatitude(),
1208 edgeLatLons.append(qMakePair(
m_feature.southernLatitude(),
1213 edgeLatLons.append(qMakePair(
m_feature.northernLatitude(),
1215 edgeLatLons.append(qMakePair(
m_feature.northernLatitude(),
1217 edgeLatLons.append(qMakePair(
m_feature.southernLatitude(),
1219 edgeLatLons.append(qMakePair(
m_feature.southernLatitude(),
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)) {
1451 for (
int i = 0; i <
features.count(); i++) {
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) {
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++)
1571 return positionList;
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.intersects(fullVector, &intersectionPoint) ==
1627 QLineF::BoundedIntersection) {
1628 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1629 qRound(intersectionPoint.y()));
1633 if (point.x() > textArea.right()) {
1634 if (rightTextBorder.intersects(fullVector, &intersectionPoint) ==
1635 QLineF::BoundedIntersection) {
1636 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1637 qRound(intersectionPoint.y()));
1641 if (point.y() > textArea.bottom()) {
1642 if (bottomTextBorder.intersects(fullVector, &intersectionPoint) ==
1643 QLineF::BoundedIntersection) {
1644 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1645 qRound(intersectionPoint.y()));
1649 if (point.x() < textArea.left()) {
1650 if (leftTextBorder.intersects(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);
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(
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(
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;
1800 viewportX, viewportY);
1802 QString featureName = feature.
name();
1803 QRect textDisplayArea(QPoint(viewportX, viewportY),
1804 QSize(fontMetrics.horizontalAdvance(featureName) + 4,
1805 fontMetrics.height()));
1807 textDisplayArea.moveTopLeft(textDisplayArea.topLeft() -
1808 QPoint(textDisplayArea.width() / 2, textDisplayArea.height() / 2));
1810 bool canDisplay =
false;
1812 textDisplayArea.right() > 0 &&
1814 textDisplayArea.bottom() > 0) {
1818 QRect fullDisplayArea = textDisplayArea;
1824 foreach (edge, edges) {
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);
1907 sourceViewport()->viewportToCube(1, 1, minValues.rx(), minValues.ry());
1912 maxValues.rx(), maxValues.ry());
Defines an angle and provides unit conversions.
bool isValid() const
This indicates whether we have a legitimate angle stored or are in an unset, or invalid,...
double radians() const
Convert an angle to a double.
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
virtual QString fileName() const
Returns the opened cube's filename.
UniversalGroundMap * universalGroundMap() const
Projection * projection() const
A named feature on a target.
QUrl referenceUrl() const
QString cleanName() const
QString controlNet() const
QWidget * toWidget() const
This converts the data in this feature to a widget.
QString displayName() const
Feature nomenclature database querier.
@ Approved
When this status is assigned to a feature, the displayed status will be "Adoptedby the IAU" and the f...
static bool featureDiameterGreaterThan(const FeatureNomenclature::Feature &lhs, const FeatureNomenclature::Feature &rhs)
Compare the diameter of two features.
File name manipulation and expansion.
QString name() const
Returns the name of the file excluding the path and the attributes in the file name.
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
This class is designed to encapsulate the concept of a Latitude.
This class is designed to encapsulate the concept of a Longitude.
Cube display widget for certain Isis MDI applications.
bool isLinked() const
Is the viewport linked with other viewports.
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Contains multiple PvlContainers.
virtual Target * target() const
Returns a pointer to the target object.
QString name() const
Return target name.
bool GroundRange(Cube *cube, Latitude &minLat, Latitude &maxLat, Longitude &minLon, Longitude &maxLon, bool allowEstimation=true)
Find the lat/lon range of the image.
This is free and unencumbered software released into the public domain.
This is free and unencumbered software released into the public domain.
This is free and unencumbered software released into the public domain.
const double Null
Value for an Isis Null pixel.
Namespace for the standard library.
This is free and unencumbered software released into the public domain.