File indexing completed on 2024-04-28 11:29:28
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2014 Abhinav Gangwar <abhgang@gmail.com> 0004 // 0005 0006 0007 // Self 0008 #include "CountryByShape.h" 0009 0010 // Qt 0011 #include <QVector> 0012 #include <QTime> 0013 #include <QVariant> 0014 #include <QVariantList> 0015 #include <QFileInfo> 0016 0017 // Marble 0018 #include <marble/MarbleWidget.h> 0019 #include <marble/MarbleModel.h> 0020 #include <marble/GeoDataTreeModel.h> 0021 #include <marble/MarblePlacemarkModel.h> 0022 0023 #include <marble/GeoDataDocument.h> 0024 #include <marble/GeoDataPlacemark.h> 0025 #include <marble/GeoDataGeometry.h> 0026 #include <marble/GeoDataPolygon.h> 0027 #include <marble/GeoDataLinearRing.h> 0028 #include <marble/GeoDataMultiGeometry.h> 0029 #include <marble/GeoDataPoint.h> 0030 #include <marble/GeoDataLatLonAltBox.h> 0031 0032 namespace Marble 0033 { 0034 class CountryByShapePrivate 0035 { 0036 public: 0037 CountryByShapePrivate( MarbleWidget *marbleWidget ) 0038 : m_parent( nullptr ), 0039 m_marbleWidget( marbleWidget ), 0040 m_countryNames( nullptr ), 0041 m_countryBoundaries( nullptr ) 0042 { 0043 m_continentsAndOceans 0044 << QStringLiteral("Asia") << QStringLiteral("Africa") 0045 << QStringLiteral("North America") << QStringLiteral("South America") 0046 << QStringLiteral("Antarctica") << QStringLiteral("Europe") 0047 << QStringLiteral("Australia") 0048 << QStringLiteral("Arctic Ocean") << QStringLiteral("Indian Ocean") 0049 << QStringLiteral("North Atlantic Ocean") << QStringLiteral("North Pacific Ocean") 0050 << QStringLiteral("South Pacific Ocean") << QStringLiteral("South Atlantic Ocean") 0051 << QStringLiteral("Southern Ocean"); 0052 } 0053 0054 CountryByShape *m_parent; 0055 MarbleWidget *m_marbleWidget; 0056 0057 /** 0058 * Document to store point placemarks which 0059 * have country names ( from file "boundaryplacemarks.cache" ) 0060 */ 0061 GeoDataDocument *m_countryNames; 0062 0063 /** 0064 * Document which have placemarks whose geometry 0065 * specifies the boundaries of a country 0066 * (from file "ne_50m_admin_0_countries.pn2" ) 0067 */ 0068 GeoDataDocument *m_countryBoundaries; 0069 0070 /** 0071 * If the placemark, we select from boundaryplacemarks.cache, 0072 * is a continent, the game will highlight a country, 0073 * on map, which contains this point placemark in its geometry 0074 * instead of highlighting the whole continent. 0075 * Also, oceans are point placemark and we don't have 0076 * any geometry, provided for oceans , that we can highlight. 0077 * So, to avoid placemarks which represent continent or ocean 0078 * we will use this list. 0079 */ 0080 QStringList m_continentsAndOceans; 0081 }; 0082 0083 CountryByShape::CountryByShape( MarbleWidget *marbleWidget ) 0084 : QObject(), 0085 d( new CountryByShapePrivate(marbleWidget) ) 0086 { 0087 d->m_parent = this; 0088 connect( this, SIGNAL(announceHighlight(qreal,qreal,GeoDataCoordinates::Unit)), 0089 d->m_marbleWidget, SIGNAL(highlightedPlacemarksChanged(qreal,qreal,GeoDataCoordinates::Unit)) ); 0090 } 0091 0092 CountryByShape::~CountryByShape() 0093 { 0094 delete d; 0095 } 0096 0097 void CountryByShape::initiateGame() 0098 { 0099 if ( !d->m_countryNames ) { 0100 const GeoDataTreeModel *const treeModel = d->m_marbleWidget->model()->treeModel(); 0101 for ( int i = 0; i < treeModel->rowCount(); ++i ) { 0102 QVariant const data = treeModel->data ( treeModel->index ( i, 0 ), MarblePlacemarkModel::ObjectPointerRole ); 0103 GeoDataObject *object = qvariant_cast<GeoDataObject*>( data ); 0104 Q_ASSERT_X( object, "CountryByShape::initiateGame", 0105 "failed to get valid data from treeModel for GeoDataObject" ); 0106 if (auto doc = geodata_cast<GeoDataDocument>(object)) { 0107 QFileInfo fileInfo( doc->fileName() ); 0108 if (fileInfo.fileName() == QLatin1String("boundaryplacemarks.cache")) { 0109 d->m_countryNames = doc; 0110 break; 0111 } 0112 } 0113 } 0114 } 0115 0116 if ( !d->m_countryBoundaries ) { 0117 const GeoDataTreeModel *const treeModel = d->m_marbleWidget->model()->treeModel(); 0118 for ( int i = 0; i < treeModel->rowCount(); ++i ) { 0119 QVariant const data = treeModel->data ( treeModel->index ( i, 0 ), MarblePlacemarkModel::ObjectPointerRole ); 0120 GeoDataObject *object = qvariant_cast<GeoDataObject*>( data ); 0121 Q_ASSERT_X( object, "MainWindow::initiateGame", 0122 "failed to get valid data from treeModel for GeoDataObject" ); 0123 if (auto doc = geodata_cast<GeoDataDocument>(object)) { 0124 QFileInfo fileInfo( doc->fileName() ); 0125 if (fileInfo.fileName() == QLatin1String("ne_50m_admin_0_countries.pn2")) { 0126 d->m_countryBoundaries = doc; 0127 break; 0128 } 0129 } 0130 } 0131 } 0132 0133 d->m_marbleWidget->setHighlightEnabled( true ); 0134 0135 if ( d->m_countryBoundaries && 0136 d->m_countryNames ) 0137 { 0138 d->m_countryNames->setVisible( false ); 0139 d->m_marbleWidget->model()->treeModel()->updateFeature( d->m_countryNames ); 0140 emit gameInitialized(); 0141 } 0142 } 0143 0144 void CountryByShape::postQuestion( QObject *gameObject ) 0145 { 0146 //Find a random placemark 0147 0148 Q_ASSERT_X( d->m_countryNames, "CountryByShape::postQuestion", 0149 "CountryByShapePrivate::m_countryNames is NULL" ); 0150 0151 QVector<GeoDataPlacemark*> countryPlacemarks = d->m_countryNames->placemarkList(); 0152 0153 uint randomSeed = uint(QTime::currentTime().msec()); 0154 qsrand( randomSeed ); 0155 0156 bool found = false; 0157 GeoDataPlacemark *placemark =nullptr; 0158 GeoDataPoint *point = nullptr; 0159 GeoDataCoordinates coord; 0160 GeoDataLatLonAltBox box; 0161 QVariantList answerOptions; 0162 while ( !found ) { 0163 int randomIndex = qrand()%countryPlacemarks.size(); 0164 placemark = countryPlacemarks[randomIndex]; 0165 point = dynamic_cast<GeoDataPoint*>( placemark->geometry() ); 0166 coord = point->coordinates(); 0167 0168 if ( point ) { 0169 /** 0170 * Find the country geometry and fetch corresponding 0171 * GeoDataLatLonAltBox to zoom in to that country so that 0172 * it fills the viewport. 0173 */ 0174 0175 Q_ASSERT_X( d->m_countryBoundaries, "CountryByShape::postQuestion", 0176 "CountryByShapePrivate::m_countryBoundaries is NULL" ); 0177 0178 QVector<GeoDataFeature*>::Iterator i = d->m_countryBoundaries->begin(); 0179 QVector<GeoDataFeature*>::Iterator const end = d->m_countryBoundaries->end(); 0180 for ( ; i != end; ++i ) { 0181 GeoDataPlacemark *country = static_cast<GeoDataPlacemark*>( *i ); 0182 0183 GeoDataPolygon *polygon = dynamic_cast<GeoDataPolygon*>( country->geometry() ); 0184 GeoDataLinearRing *linearring = dynamic_cast<GeoDataLinearRing*>( country->geometry() ); 0185 GeoDataMultiGeometry *multigeom = dynamic_cast<GeoDataMultiGeometry*>( country->geometry() ); 0186 0187 if ( polygon && 0188 polygon->contains( coord ) && 0189 !d->m_continentsAndOceans.contains(country->name(), Qt::CaseSensitive) ) 0190 { 0191 box = polygon->latLonAltBox(); 0192 found = true; 0193 break; 0194 } 0195 if ( linearring && 0196 linearring->contains( coord ) && 0197 !d->m_continentsAndOceans.contains(country->name(), Qt::CaseSensitive) ) 0198 { 0199 box = linearring->latLonAltBox(); 0200 found = true; 0201 break; 0202 } 0203 if ( multigeom ) { 0204 QVector<GeoDataGeometry*>::Iterator iter = multigeom->begin(); 0205 QVector<GeoDataGeometry*>::Iterator const end = multigeom->end(); 0206 0207 for ( ; iter != end; ++iter ) { 0208 GeoDataPolygon *poly = dynamic_cast<GeoDataPolygon*>( *iter ); 0209 if ( poly && 0210 poly->contains( coord ) && 0211 !d->m_continentsAndOceans.contains(country->name(), Qt::CaseSensitive) ) 0212 { 0213 box = poly->latLonAltBox(); 0214 found = true; 0215 break; 0216 } 0217 } 0218 } 0219 if ( found ) { 0220 break; 0221 } 0222 } 0223 } 0224 } 0225 d->m_marbleWidget->setHighlightEnabled( true ); 0226 emit announceHighlight( coord.longitude(GeoDataCoordinates::Degree), 0227 coord.latitude(GeoDataCoordinates::Degree), 0228 GeoDataCoordinates::Degree ); 0229 0230 /** 0231 * Now disable the highlight feature so that 0232 * the user click doesn't disturb the highlight 0233 * we did to ask question. 0234 */ 0235 d->m_marbleWidget->setHighlightEnabled( false ); 0236 0237 d->m_marbleWidget->centerOn( box, true ); 0238 0239 answerOptions << placemark->name() 0240 << countryPlacemarks[qrand()%countryPlacemarks.size()]->name() 0241 << countryPlacemarks[qrand()%countryPlacemarks.size()]->name() 0242 << countryPlacemarks[qrand()%countryPlacemarks.size()]->name(); 0243 0244 // Randomize options in list answerOptions 0245 for ( int i = 0; i < answerOptions.size(); ++i ) { 0246 QVariant option = answerOptions.takeAt( qrand()%answerOptions.size() ); 0247 answerOptions.append( option ); 0248 } 0249 0250 if ( gameObject ) { 0251 QMetaObject::invokeMethod( gameObject, "countryByShapeQuestion", 0252 Q_ARG(QVariant, QVariant(answerOptions)), 0253 Q_ARG(QVariant, QVariant(placemark->name())) ); 0254 } 0255 } 0256 0257 } // namespace Marble 0258 0259 #include "moc_CountryByShape.cpp"