void MeasureToolPlugin::drawSegments( GeoPainter* painter ) { for ( int segmentIndex = 0; segmentIndex < m_measureLineString.size() - 1; ++segmentIndex ) { GeoDataLineString segment( Tessellate ); segment << m_measureLineString[segmentIndex] ; segment << m_measureLineString[segmentIndex + 1]; QPen shadowPen( Oxygen::aluminumGray5 ); shadowPen.setWidthF(4.0); painter->setPen( shadowPen ); painter->drawPolyline( segment ); QString infoString; if ( (m_paintMode == Polygon && m_showDistanceLabel) || (m_paintMode == Circular && m_showRadius) ) { const qreal segmentLength = segment.length( marbleModel()->planet()->radius() ); m_radius = segmentLength; infoString = meterToPreferredUnit(segmentLength); } if ( m_showBearingLabel && m_paintMode != Circular ) { GeoDataCoordinates coordinates = segment.first(); qreal bearing = coordinates.bearing( segment.last(), GeoDataCoordinates::Degree ); if ( bearing < 0 ) { bearing += 360; } QString bearingString = QString::fromUtf8( "%1°" ).arg( bearing, 0, 'f', 2 ); if ( !infoString.isEmpty() ) { infoString.append( "\n" ); } infoString.append( bearingString ); } if ( m_showBearingChangeLabel && segmentIndex != 0 ) { GeoDataCoordinates currentCoordinates = m_measureLineString[segmentIndex]; qreal currentBearing = currentCoordinates.bearing(m_measureLineString[segmentIndex+1]); qreal previousBearing = currentCoordinates.bearing( m_measureLineString[segmentIndex-1]); GeoDataLinearRing ring; painter->setPen( Qt::NoPen ); painter->setBrush( QBrush ( QColor ( 127, 127, 127, 127 ) ) ); if (currentBearing < previousBearing) currentBearing += 2 * M_PI; ring << currentCoordinates; qreal angleLength = qAbs(m_latLonAltBox.north() - m_latLonAltBox.south()) / 20; qreal iterBearing = previousBearing; while ( iterBearing < currentBearing ) { ring << currentCoordinates.moveByBearing( iterBearing, angleLength );; iterBearing += 0.1; } ring << currentCoordinates.moveByBearing( currentBearing, angleLength );; painter->drawPolygon( ring ); qreal currentBearingChange = (currentBearing - previousBearing) * RAD2DEG; if (currentBearingChange < 0) currentBearingChange += 360; QString bearingChangedString = QString::fromUtf8( "%1°" ).arg( currentBearingChange, 0, 'f', 2 ); painter->setPen( Qt::black ); GeoDataCoordinates textPosition = ring.latLonAltBox().center(); qreal deltaEast = ring.latLonAltBox().east() - currentCoordinates.longitude(); qreal deltaWest = currentCoordinates.longitude() - ring.latLonAltBox().west(); if (deltaEast > deltaWest) { textPosition.setLongitude(currentCoordinates.longitude() + deltaEast / 2); } else { textPosition.setLongitude(currentCoordinates.longitude() - deltaWest); } painter->drawText(textPosition, bearingChangedString ); } // Drawing ellipse around 1st point towards the 2nd if ( m_paintMode == Circular ) { GeoDataCoordinates currentCoordinates = m_measureLineString[segmentIndex]; GeoDataLinearRing ring; // planetRadius - planet radius // d - distance between points // S - area of the painted circle qreal planetRadius = marbleModel()->planet()->radius(); qreal d = m_measureLineString.length(1); m_circularArea = 2 * M_PI * planetRadius * planetRadius * (1 - qCos(d)); qreal iterBearing = 0; while ( iterBearing < 2 * M_PI ) { ring << currentCoordinates.moveByBearing(iterBearing, d); iterBearing += 0.1; } painter->setPen( Qt::NoPen ); painter->setBrush( QBrush ( QColor ( 127, 127, 127, 127 ) ) ); painter->drawPolygon(ring); if ( m_showCircularArea ) { painter->setPen(Qt::white); GeoDataCoordinates textPosition = ring.latLonAltBox().center(); QString areaText = tr("Area:\n%1").arg(meterToPreferredUnit(m_circularArea, true)); QFontMetrics fontMetrics = painter->fontMetrics(); QRect boundingRect = fontMetrics.boundingRect(QRect(), Qt::AlignCenter, areaText); painter->drawText(textPosition, areaText, -boundingRect.width()/2, -boundingRect.height()*1.5, boundingRect.width(), boundingRect.height(), QTextOption(Qt::AlignCenter)); } if ( m_showCircumference ) { painter->setPen(Qt::white); GeoDataCoordinates textPosition = ring.latLonAltBox().center(); m_circumference = 2 * M_PI * planetRadius * qSin(d); QString circumferenceText = tr("Circumference:\n%1").arg(meterToPreferredUnit(m_circumference)); QFontMetrics fontMetrics = painter->fontMetrics(); QRect boundingRect = fontMetrics.boundingRect(QRect(),Qt::AlignCenter, circumferenceText); painter->drawText(textPosition, circumferenceText, -boundingRect.width()/2, boundingRect.height(), boundingRect.width(), boundingRect.height(), QTextOption(Qt::AlignCenter)); } } if ( !infoString.isEmpty() ) { QPen linePen; // have three alternating colors for the segments switch ( segmentIndex % 3 ) { case 0: linePen.setColor( Oxygen::brickRed4 ); break; case 1: linePen.setColor( Oxygen::forestGreen4 ); break; case 2: linePen.setColor( Oxygen::skyBlue4 ); break; } linePen.setWidthF(2.0); painter->setPen( linePen ); painter->drawPolyline( segment, infoString, LineCenter ); } } if (m_paintMode == Polygon && m_measureLineString.size() > 2) { GeoDataLinearRing measureRing = m_measureLineString; if (m_showPolygonArea || m_showPerimeter) { painter->setPen( Qt::NoPen ); painter->setBrush( QBrush ( QColor ( 127, 127, 127, 127 ) ) ); painter->drawPolygon(measureRing); QPen shadowPen( Oxygen::aluminumGray5 ); shadowPen.setStyle(Qt::DashLine); shadowPen.setWidthF(3.0); painter->setPen( shadowPen ); painter->drawPolyline(GeoDataLineString( Tessellate ) << m_measureLineString.first() << m_measureLineString.last()); } if (m_showPolygonArea) { qreal theta1 = 0.0; qreal n = m_measureLineString.size(); for (int segmentIndex = 1; segmentIndex < m_measureLineString.size()-1; segmentIndex++) { GeoDataCoordinates current = m_measureLineString[segmentIndex]; qreal prevBearing = current.bearing(m_measureLineString[segmentIndex-1]); qreal nextBearing = current.bearing(m_measureLineString[segmentIndex+1]); if (nextBearing < prevBearing) nextBearing += 2 * M_PI; qreal angle = nextBearing - prevBearing; theta1 += angle; } // Traversing first vertex GeoDataCoordinates current = m_measureLineString[0]; qreal prevBearing = current.bearing(m_measureLineString[n-1]); qreal nextBearing = current.bearing(m_measureLineString[1]); if (nextBearing < prevBearing) nextBearing += 2 * M_PI; qreal angle = nextBearing - prevBearing; theta1 += angle; // And the last one current = m_measureLineString[n-1]; prevBearing = current.bearing(m_measureLineString[n-2]); nextBearing = current.bearing(m_measureLineString[0]); if (nextBearing < prevBearing) nextBearing += 2 * M_PI; angle = nextBearing - prevBearing; theta1 += angle; qreal theta2 = 2 * M_PI * n - theta1; // theta = smaller of theta1 and theta2 qreal theta = (theta1 < theta2) ? theta1 : theta2; qreal planetRadius = marbleModel()->planet()->radius(); qreal S = qAbs((theta - (n-2) * M_PI) * planetRadius * planetRadius); m_polygonArea = S; painter->setPen(Qt::white); GeoDataCoordinates textPosition = measureRing.latLonAltBox().center(); QString areaText = tr("Area:\n%1").arg(meterToPreferredUnit(S, true)); QFontMetrics fontMetrics = painter->fontMetrics(); QRect boundingRect = fontMetrics.boundingRect(QRect(), Qt::AlignCenter, areaText); painter->drawText(textPosition, areaText, -boundingRect.width()/2, -(boundingRect.height()+fontMetrics.height()*0.25), boundingRect.width(), boundingRect.height(), QTextOption(Qt::AlignCenter)); } if (m_showPerimeter) { painter->setPen(Qt::white); GeoDataCoordinates textPosition = measureRing.latLonAltBox().center(); qreal P = measureRing.length(marbleModel()->planet()->radius()); m_perimeter = P; QString perimeterText = tr("Perimeter:\n%1").arg(meterToPreferredUnit(P)); QFontMetrics fontMetrics = painter->fontMetrics(); QRect boundingRect = fontMetrics.boundingRect(QRect(),Qt::AlignCenter, perimeterText); painter->drawText(textPosition, perimeterText, -boundingRect.width()/2, 0, boundingRect.width(), boundingRect.height(), QTextOption(Qt::AlignCenter)); } } }
void CountryByShape::postQuestion( QObject *gameObject ) { //Find a random placemark Q_ASSERT_X( d->m_countryNames, "CountryByShape::postQuestion", "CountryByShapePrivate::m_countryNames is NULL" ); QVector<GeoDataPlacemark*> countryPlacemarks = d->m_countryNames->placemarkList(); uint randomSeed = uint(QTime::currentTime().msec()); qsrand( randomSeed ); bool found = false; GeoDataPlacemark *placemark =0; GeoDataPoint *point = 0; GeoDataCoordinates coord; GeoDataLatLonAltBox box; QVariantList answerOptions; while ( !found ) { int randomIndex = qrand()%countryPlacemarks.size(); placemark = countryPlacemarks[randomIndex]; point = dynamic_cast<GeoDataPoint*>( placemark->geometry() ); coord = point->coordinates(); if ( point ) { /** * Find the country geometry and fetch corresponding * GeoDataLatLonAltBox to zoom in to that country so that * it fills the viewport. */ Q_ASSERT_X( d->m_countryBoundaries, "CountryByShape::postQuestion", "CountryByShapePrivate::m_countryBoundaries is NULL" ); QVector<GeoDataFeature*>::Iterator i = d->m_countryBoundaries->begin(); QVector<GeoDataFeature*>::Iterator const end = d->m_countryBoundaries->end(); for ( ; i != end; ++i ) { GeoDataPlacemark *country = static_cast<GeoDataPlacemark*>( *i ); GeoDataPolygon *polygon = dynamic_cast<GeoDataPolygon*>( country->geometry() ); GeoDataLinearRing *linearring = dynamic_cast<GeoDataLinearRing*>( country->geometry() ); GeoDataMultiGeometry *multigeom = dynamic_cast<GeoDataMultiGeometry*>( country->geometry() ); if ( polygon && polygon->contains( coord ) && !d->m_continentsAndOceans.contains(country->name(), Qt::CaseSensitive) ) { box = polygon->latLonAltBox(); found = true; break; } if ( linearring && linearring->contains( coord ) && !d->m_continentsAndOceans.contains(country->name(), Qt::CaseSensitive) ) { box = linearring->latLonAltBox(); found = true; break; } if ( multigeom ) { QVector<GeoDataGeometry*>::Iterator iter = multigeom->begin(); QVector<GeoDataGeometry*>::Iterator const end = multigeom->end(); for ( ; iter != end; ++iter ) { GeoDataPolygon *poly = dynamic_cast<GeoDataPolygon*>( *iter ); if ( poly && poly->contains( coord ) && !d->m_continentsAndOceans.contains(country->name(), Qt::CaseSensitive) ) { box = poly->latLonAltBox(); found = true; break; } } } if ( found ) { break; } } } } d->m_marbleWidget->setHighlightEnabled( true ); emit announceHighlight( coord.longitude(GeoDataCoordinates::Degree), coord.latitude(GeoDataCoordinates::Degree), GeoDataCoordinates::Degree ); /** * Now disable the highlight feature so that * the user click doesn't disturbe the highlight * we did to ask question. */ d->m_marbleWidget->setHighlightEnabled( false ); d->m_marbleWidget->centerOn( box, true ); answerOptions << placemark->name() << countryPlacemarks[qrand()%countryPlacemarks.size()]->name() << countryPlacemarks[qrand()%countryPlacemarks.size()]->name() << countryPlacemarks[qrand()%countryPlacemarks.size()]->name(); // Randomize options in list answerOptions for ( int i = 0; i < answerOptions.size(); ++i ) { QVariant option = answerOptions.takeAt( qrand()%answerOptions.size() ); answerOptions.append( option ); } if ( gameObject ) { QMetaObject::invokeMethod( gameObject, "countryByShapeQuestion", Q_ARG(QVariant, QVariant(answerOptions)), Q_ARG(QVariant, QVariant(placemark->name())) ); } }