2 #include "FeatureNomenclatureTool.h"
5 #include <QApplication>
11 #include <QDesktopServices>
13 #include <QHBoxLayout>
16 #include <QMessageBox>
17 #include <QProgressBar>
18 #include <QPushButton>
20 #if defined(__APPLE__)
21 #include <QtWebEngineWidgets/QWebEngineView>
23 #include <QWebEngineView>
26 #include <geos/geom/Coordinate.h>
27 #include <geos/geom/CoordinateSequence.h>
28 #include <geos/geom/MultiPolygon.h>
36 #include "NomenclatureToolConfigDialog.h"
74 "in your opened cube files. This tool <strong>requires</strong> an "
75 "active internet connection, projection or camera information, and a "
76 "calculatable ground range to function. The larger the ground range ("
77 "covered area on a planet), the longer it will take to populate the "
78 "nomenclature for a particular cube.<br/><br/>"
79 "<font color='red'>**WARNING**</font> The accuracy of this tool is not "
80 "perfect, features <strong>can and will be mislabeled</strong> if you "
81 "have not properly controlled your images to the control network that "
82 "identifies the latitude/longitude values of a feature. Please use the "
83 "nomenclature website to verify a label is correct for a feature. "
84 "<br/><br/>See the IAU Gazetteer of Planetary Nomenclature website for "
85 "more information.<br/>"
86 "<a href='http://planetarynames.wr.usgs.gov/'>"
87 "http://planetarynames.wr.usgs.gov/</a>";
89 connect(
this, SIGNAL(toolActivated()),
120 m_action = menu->addAction(
"Show Nomenclature");
124 connect(
m_action, SIGNAL(triggered(
bool)),
142 painter->setFont(fontToUse);
224 vp->viewport()->update();
243 vp->viewport()->update();
261 vp->viewport()->update();
278 (*m_foundNomenclature)[i].applyExtentType(
m_extentType);
284 vp->viewport()->update();
307 QStackedWidget *parent) {
316 QLabel *foundFeaturesLabel =
new QLabel(
"Found Features:");
319 QComboBox::AdjustToContents);
344 QHBoxLayout *layout =
new QHBoxLayout;
345 layout->setMargin(0);
347 layout->addWidget(foundFeaturesLabel);
353 layout->addStretch(1);
354 wrapperWidget->setLayout(layout);
355 return wrapperWidget;
370 action->setIcon(QPixmap(
toolIconDir() +
"/nomenclature.png"));
371 action->setToolTip(
"Nomenclature (N)");
372 action->setShortcut(Qt::Key_N);
373 action->setObjectName(
"nomenclatureToolButton");
376 "<b>Function:</b> Display nomenclature on the visible images.\n"
377 "<p/><b>Hint:</b> While this tool is active, you can left and right "
378 "click on any of the named features for additional options."
379 "<p/><b>Shortcut:</b> N";
380 action->setWhatsThis(text);
395 QPoint p, Qt::MouseButton s) {
438 qobject_cast<QWidget *>(parent()));
439 configDialog->setAttribute(Qt::WA_DeleteOnClose);
440 configDialog->show();
481 viewport->viewport()->update();
491 if (newState == Qt::Unchecked) {
495 else if (newState == Qt::Checked) {
503 vp->viewport()->update();
517 (*m_foundNomenclature)[i].handleViewChanged(
this);
538 QMessageBox::warning(qobject_cast<QWidget *>(parent()),
556 if (viewport == vp ||
588 QProgressDialog updatingFeaturesProgress(
589 tr(
"Projecting Features for [%1]").arg(vp->
cube()->
fileName().section(
'/',-1)),
592 updatingFeaturesProgress.setWindowModality(Qt::WindowModal);
596 for (
int i = 0; i < features.count(); i++) {
597 feature = features[i];
599 int progress = floor(100 * (
double)i / (
double)features.count());
601 if (progress != updatingFeaturesProgress.value())
602 updatingFeaturesProgress.setValue(progress);
604 if (updatingFeaturesProgress.wasCanceled()) {
615 QString displayName = feature.
cleanName() +
618 QString targetName = feature.
target().toUpper();
625 while (!foundInsertPos) {
627 foundInsertPos =
true;
632 QString insertPosTarget = insetPosData.toMap()[
"Target"].toString();
634 if (targetName < insertPosTarget) {
635 foundInsertPos =
true;
637 else if (targetName == insertPosTarget) {
638 if (!insetPosData.toMap()[
"Viewport"].isNull()) {
639 foundInsertPos = displayName.compare(
641 Qt::CaseInsensitive) < 0;
651 "Target"].toString() != targetName) {
653 data[
"Target"] = targetName;
656 if (controlNet !=
"")
657 controlNet =
" (" + controlNet +
")";
660 targetName + controlNet,
677 data[
"Feature"] = QVariant::fromValue<FeatureNomenclature::Feature>(
679 data[
"Viewport"] = qVariantFromValue(vp);
680 data[
"Target"] = qVariantFromValue(targetName);
683 qVariantFromValue(data));
686 updatingFeaturesProgress.setValue( features.count() );
706 removedViewports.removeOne(vp);
716 bool removedAViewport =
false;
720 removedAViewport =
true;
723 if (removedAViewport) {
753 target = mappingGrp[
"TargetName"][0];
766 (*m_nomenclatureSearchers)[vp] = searcher;
768 (*m_nomenclatureSearchers)[vp]->queryFeatures(target.toUpper(),
819 QDialog *detailsDialog =
new QDialog(qobject_cast<QWidget *>(parent()));
820 detailsDialog->setAttribute(Qt::WA_DeleteOnClose);
822 QVBoxLayout *mainLayout =
new QVBoxLayout;
823 detailsDialog->setLayout(mainLayout);
825 mainLayout->addWidget(feature.
toWidget());
829 QHBoxLayout *buttonsAreaLayout =
new QHBoxLayout;
830 buttonsAreaWrapper->setLayout(buttonsAreaLayout);
832 buttonsAreaLayout->addStretch();
833 QPushButton *okayBtn =
new QPushButton(
"&Ok");
834 okayBtn->setIcon(QIcon::fromTheme(
"dialog-ok"));
835 connect(okayBtn, SIGNAL(clicked()),
836 detailsDialog, SLOT(accept()));
837 buttonsAreaLayout->addWidget(okayBtn);
839 mainLayout->addWidget(buttonsAreaWrapper);
841 detailsDialog->show();
864 if (isCurrentlyLoading) {
898 (*m_nomenclatureSearchers)[vp]->deleteLater();
994 FileName config(
"$HOME/.Isis/qview/nomenclature.config");
996 config.expanded(), QSettings::NativeFormat);
998 m_fontSize = settings.value(
"fontSize", m_fontSize).toInt();
1001 settings.value(
"defaultEnabled", m_defaultEnabled).toBool();
1016 FileName config(
"$HOME/.Isis/qview/nomenclature.config");
1018 config.expanded(), QSettings::NativeFormat);
1020 settings.setValue(
"fontColor", qVariantFromValue(*
m_fontColor));
1052 m_centerLine =
Null;
1053 m_centerSample =
Null;
1054 m_featureEdgeLineSamples = NULL;
1059 m_feature = feature;
1063 Latitude centerLat = m_feature.centerLatitude();
1064 Longitude centerLon = m_feature.centerLongitude();
1065 if (m_gmap && m_gmap->SetGround(centerLat, centerLon)) {
1066 m_centerSample = m_gmap->Sample();
1067 m_centerLine = m_gmap->Line();
1069 applyExtentType(vectorType);
1084 m_featureEdgeLineSamples = NULL;
1101 m_centerLine =
Null;
1102 m_centerSample =
Null;
1105 delete m_featureEdgeLineSamples;
1106 m_featureEdgeLineSamples = NULL;
1116 return (m_centerSample !=
Null && m_centerLine !=
Null);
1138 return *m_featureEdgeLineSamples;
1163 Latitude centerLat = m_feature.centerLatitude();
1164 Longitude centerLon = m_feature.centerLongitude();
1166 m_featureEdgeLineSamples->clear();
1175 edgeLats.append(m_feature.northernLatitude());
1176 edgeLats.append(m_feature.centerLatitude());
1177 edgeLats.append(m_feature.southernLatitude());
1179 edgeLons.append(m_feature.easternLongitude());
1180 edgeLons.append(m_feature.centerLongitude());
1181 edgeLons.append(m_feature.westernLongitude());
1183 int edgeLatCount = edgeLats.count();
1184 int edgeLonCount = edgeLons.count();
1186 for (
int latIndex = 0; latIndex < edgeLatCount; latIndex++) {
1187 for (
int lonIndex = 0; lonIndex < edgeLonCount; lonIndex++) {
1188 Latitude &lat = edgeLats[latIndex];
1192 (lat != centerLat || lon != centerLon) &&
1193 m_gmap->SetGround(lat, lon)) {
1194 m_featureEdgeLineSamples->append(
1205 edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1206 m_feature.centerLongitude()));
1207 edgeLatLons.append(qMakePair(m_feature.centerLatitude(),
1208 m_feature.westernLongitude()));
1209 edgeLatLons.append(qMakePair(m_feature.centerLatitude(),
1210 m_feature.easternLongitude()));
1211 edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1212 m_feature.centerLongitude()));
1216 edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1217 m_feature.easternLongitude()));
1218 edgeLatLons.append(qMakePair(m_feature.northernLatitude(),
1219 m_feature.westernLongitude()));
1220 edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1221 m_feature.westernLongitude()));
1222 edgeLatLons.append(qMakePair(m_feature.southernLatitude(),
1223 m_feature.easternLongitude()));
1226 int edgeLatLonCount = edgeLatLons.count();
1228 for (
int edgeIndex = 0; edgeIndex < edgeLatLonCount; edgeIndex++) {
1229 Latitude &lat = edgeLatLons[edgeIndex].first;
1230 Longitude &lon = edgeLatLons[edgeIndex].second;
1233 (lat != centerLat || lon != centerLon) &&
1234 m_gmap->SetGround(lat, lon)) {
1235 m_featureEdgeLineSamples->append(
1253 std::swap(m_gmap, other.
m_gmap);
1290 m_fullDisplayRect = NULL;
1291 m_edgePoints = NULL;
1293 m_textRect =
new QRect;
1294 m_fullDisplayRect =
new QRect;
1310 QRect textRect, QRect fullDisplayRect,
QList<QPoint> edgePoints) {
1312 m_fullDisplayRect = NULL;
1313 m_edgePoints = NULL;
1315 m_textRect =
new QRect(textRect);
1316 m_fullDisplayRect =
new QRect(fullDisplayRect);
1328 m_fullDisplayRect = NULL;
1329 m_edgePoints = NULL;
1344 delete m_fullDisplayRect;
1345 m_fullDisplayRect = NULL;
1347 delete m_edgePoints;
1348 m_edgePoints = NULL;
1370 return *m_fullDisplayRect;
1382 return *m_edgePoints;
1420 m_sourceViewport = NULL;
1422 m_featureScreenAreas = NULL;
1423 m_viewportCubeRange = NULL;
1442 m_sourceViewport = sourceViewport;
1444 m_featureScreenAreas = NULL;
1445 m_viewportCubeRange = NULL;
1451 qSort(features.begin(), features.end(),
1454 for (
int i = 0; i < features.count(); i++) {
1457 m_features->append(display);
1461 handleViewChanged(tool);
1474 m_featureScreenAreas = NULL;
1475 m_viewportCubeRange = NULL;
1489 m_sourceViewport = NULL;
1494 delete m_featureScreenAreas;
1495 m_featureScreenAreas = NULL;
1497 delete m_viewportCubeRange;
1498 m_viewportCubeRange = NULL;
1508 for (
int i = 0; i < m_features->count(); i++) {
1509 (*m_features)[i].applyExtentType(vectorType);
1524 int foundIndex = -1;
1525 for (
int i = 0; foundIndex == -1 && i < m_features->count(); i++) {
1526 if (displayName == m_features->at(i).feature().displayName()) {
1531 if (foundIndex != -1) {
1532 m_features->prepend(m_features->takeAt(foundIndex));
1533 m_featureScreenAreas->prepend(m_featureScreenAreas->takeAt(foundIndex));
1537 m_sourceViewport->setScale(m_sourceViewport->scale(),
1538 m_features->first().center().first,
1539 m_features->first().center().second);
1540 m_sourceViewport->viewport()->update();
1555 for (
int i = 0; i < m_features->count(); i++)
1556 featureList.append((*m_features)[i].feature());
1571 for (
int i = 0; i < m_features->count(); i++)
1572 positionList.append((*m_features)[i]);
1574 return positionList;
1585 return m_sourceViewport;
1600 if (viewportCubeRange() == *m_viewportCubeRange) {
1602 for (
int i = 0; i < m_features->count() &&
1603 i < m_featureScreenAreas->count(); i++) {
1610 if (!fullArea.isNull() && fullArea != textArea && showVectors) {
1612 QRect startRect = textArea.adjusted(-2, -2, 2, 2);
1613 QLineF topTextBorder(startRect.topLeft(), startRect.topRight());
1614 QLineF rightTextBorder(startRect.topRight(), startRect.bottomRight());
1615 QLineF bottomTextBorder(startRect.bottomLeft(),
1616 startRect.bottomRight());
1617 QLineF leftTextBorder(startRect.topLeft(), startRect.bottomLeft());
1621 if (vectorType !=
Box) {
1623 QLineF fullVector(textArea.center(), point);
1624 QPoint newVectorStart;
1626 QPointF intersectionPoint;
1628 if (point.y() < textArea.top()) {
1629 if (topTextBorder.intersect(fullVector, &intersectionPoint) ==
1630 QLineF::BoundedIntersection) {
1631 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1632 qRound(intersectionPoint.y()));
1636 if (point.x() > textArea.right()) {
1637 if (rightTextBorder.intersect(fullVector, &intersectionPoint) ==
1638 QLineF::BoundedIntersection) {
1639 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1640 qRound(intersectionPoint.y()));
1644 if (point.y() > textArea.bottom()) {
1645 if (bottomTextBorder.intersect(fullVector, &intersectionPoint) ==
1646 QLineF::BoundedIntersection) {
1647 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1648 qRound(intersectionPoint.y()));
1652 if (point.x() < textArea.left()) {
1653 if (leftTextBorder.intersect(fullVector, &intersectionPoint) ==
1654 QLineF::BoundedIntersection) {
1655 newVectorStart = QPoint(qRound(intersectionPoint.x()),
1656 qRound(intersectionPoint.y()));
1660 if (!newVectorStart.isNull() &&
1661 QLineF(newVectorStart, point).length() > 10) {
1662 vectors.append(QLine(newVectorStart, point));
1666 foreach (QLine vector, vectors) {
1667 painter->drawLine(vector);
1670 Angle normalAngle(-1 * QLineF(vector).normalVector().angle(),
1674 double deltaX = magnitude * cos(normalAngle.
radians());
1675 double deltaY = magnitude * sin(normalAngle.
radians());
1677 QPoint normalStart(vector.x2() + deltaX, vector.y2() + deltaY);
1678 QPoint normalEnd(vector.x2() - deltaX, vector.y2() - deltaY);
1679 painter->drawLine(normalStart, normalEnd);
1683 Angle leftHead = vectorAngle - arrowheadAngle;
1684 Angle rightHead = vectorAngle + arrowheadAngle;
1686 int arrowheadMag = 10;
1687 deltaX = arrowheadMag * cos(leftHead.
radians());
1688 deltaY = arrowheadMag * sin(leftHead.
radians());
1690 vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1692 deltaX = arrowheadMag * cos(rightHead.
radians());
1693 deltaY = arrowheadMag * sin(rightHead.
radians());
1695 vector.p2(), vector.p2() - QPoint(deltaX, deltaY));
1701 QPolygon boundingPoly(pos.
edgePoints().toVector());
1702 painter->drawPolygon(boundingPoly);
1707 if (!textArea.isNull()) {
1708 QString featureName = feature.
name();
1709 painter->drawText(textArea, featureName);
1726 for (
int i = 0; i < m_featureScreenAreas->count(); i++) {
1727 QRect screenArea = m_featureScreenAreas->at(i).displayArea();
1729 if (screenArea.contains(p)) {
1731 if (s == Qt::LeftButton) {
1734 else if (s == Qt::RightButton) {
1738 title->setEnabled(
false);
1739 menu.addSeparator();
1741 QAction *details = menu.addAction(
"Details...");
1742 QAction *website = menu.addAction(
"Website...");
1743 menu.addSeparator();
1744 QAction *center = menu.addAction(
"Center on Feature");
1745 QAction *copyUrl = menu.addAction(
"Copy Website URL");
1749 QAction *selectedAction = menu.exec(
1750 m_sourceViewport->viewport()->mapToGlobal(p) +
1751 QPoint(0, 20), details);
1753 if (selectedAction == details) {
1756 else if (selectedAction == website) {
1759 else if (selectedAction == center) {
1762 else if (selectedAction == copyUrl) {
1763 QApplication::clipboard()->setText(
1780 m_featureScreenAreas->clear();
1783 fontToUse.setPointSize(tool->
fontSize());
1784 QFontMetrics fontMetrics(fontToUse);
1789 for (
int i = 0; i < m_features->count(); i++) {
1797 double sample = (*m_features)[i].center().first;
1798 double line = (*m_features)[i].center().second;
1802 m_sourceViewport->cubeToViewport(sample, line,
1803 viewportX, viewportY);
1805 QString featureName = feature.
name();
1806 QRect textDisplayArea(QPoint(viewportX, viewportY),
1807 QSize(fontMetrics.width(featureName) + 4,
1808 fontMetrics.height()));
1810 textDisplayArea.moveTopLeft(textDisplayArea.topLeft() -
1811 QPoint(textDisplayArea.width() / 2, textDisplayArea.height() / 2));
1813 bool canDisplay =
false;
1814 if (textDisplayArea.left() < m_sourceViewport->width() &&
1815 textDisplayArea.right() > 0 &&
1816 textDisplayArea.top() < m_sourceViewport->height() &&
1817 textDisplayArea.bottom() > 0) {
1821 QRect fullDisplayArea = textDisplayArea;
1827 foreach (edge, edges) {
1828 m_sourceViewport->cubeToViewport(edge.first, edge.second,
1829 viewportX, viewportY);
1830 edgeScreenPoints.append(QPoint(viewportX, viewportY));
1834 foreach (QPoint screenPoint, edgeScreenPoints) {
1835 fullDisplayArea = fullDisplayArea.united(
1836 QRect(screenPoint.x() - 3, screenPoint.y() - 3, 6, 6));
1839 else if (edges.count() == 4) {
1841 QPolygon boundingPoly(edgeScreenPoints.toVector());
1843 if (boundingPoly.intersected(textDisplayArea) == QPolygon(textDisplayArea,
true)) {
1844 fullDisplayArea = boundingPoly.boundingRect();
1851 foreach (QRect rectToAvoid, rectsToAvoid) {
1852 if (canDisplay && fullDisplayArea.intersects(rectToAvoid)) {
1859 rectsToAvoid.append(fullDisplayArea);
1864 *m_viewportCubeRange = viewportCubeRange();
1910 sourceViewport()->viewportToCube(1, 1, minValues.rx(), minValues.ry());
1913 sourceViewport()->viewportToCube(sourceViewport()->viewport()->width(),
1914 sourceViewport()->viewport()->height(),
1915 maxValues.rx(), maxValues.ry());
Cube display widget for certain Isis MDI applications.
UniversalGroundMap * universalGroundMap() const
Return the universal ground map associated with the cube (NULL implies none)
const double Null
Value for an Isis Null pixel.
File name manipulation and expansion.
double radians() const
Convert an angle to a double.
Feature nomenclature database querier.
QWidget * toWidget() const
This converts the data in this feature to a widget.
When this status is assigned to a feature, the displayed status will be "Adopted by the IAU" and the ...
QString controlNet() const
This class is designed to encapsulate the concept of a Latitude.
QString displayName() const
Projection * projection() const
Return the projection associated with cube (NULL implies none)
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
This class is designed to encapsulate the concept of a Longitude.
Target * target() const
Returns a pointer to the target object.
QString name() const
Return target name.
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Cube * cube() const
Return the cube associated with viewport.
Contains multiple PvlContainers.
Camera * camera() const
Return the camera associated with the cube (NULL implies none)
bool isLinked() const
Is the viewport linked with other viewports.
QUrl referenceUrl() const
bool isValid() const
This indicates whether we have a legitimate angle stored or are in an unset, or invalid, state.
Defines an angle and provides unit conversions.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
QString cleanName() const
static bool featureDiameterGreaterThan(const FeatureNomenclature::Feature &lhs, const FeatureNomenclature::Feature &rhs)
Compare the diameter of two features.
QString fileName() const
Returns the opened cube's filename.
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
A named feature on a target.
bool GroundRange(Cube *cube, Latitude &minLat, Latitude &maxLat, Longitude &minLon, Longitude &maxLon, bool allowEstimation=true)
Find the lat/lon range of the image.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.