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> 33 #include "NomenclatureToolConfigDialog.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();
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());
Cube display widget for certain Isis MDI applications.
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
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.
When this status is assigned to a feature, the displayed status will be "Adopted by the IAU" and the ...
This class is designed to encapsulate the concept of a Latitude.
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.
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Target * target() const
Returns a pointer to the target object.
Contains multiple PvlContainers.
UniversalGroundMap * universalGroundMap() const
QString controlNet() const
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
QString displayName() const
bool isLinked() const
Is the viewport linked with other viewports.
QString name() const
Return target name.
QString cleanName() const
Defines an angle and provides unit conversions.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
QWidget * toWidget() const
This converts the data in this feature to a widget.
QUrl referenceUrl() const
virtual QString fileName() const
Returns the opened cube's filename.
Namespace for ISIS/Bullet specific routines.
Projection * projection() const
static bool featureDiameterGreaterThan(const FeatureNomenclature::Feature &lhs, const FeatureNomenclature::Feature &rhs)
Compare the diameter of two features.
bool isValid() const
This indicates whether we have a legitimate angle stored or are in an unset, or invalid, state.
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.