// // This file is part of the Marble Desktop Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009 Andrew Manson // #include "OsmAnnotatePlugin.h" #include //#include //#include #include #include #include #include #include "MarbleDebug.h" #include "AbstractProjection.h" #include "AreaAnnotation.h" #include "GeoDataDocument.h" #include "GeoDataLatLonBox.h" #include "GeoDataParser.h" #include "GeoPainter.h" #include "GeoWriter.h" #include "MarbleDirs.h" #include "MarbleWidget.h" #include "osm/OsmBoundsGraphicsItem.h" #include "PlacemarkTextAnnotation.h" #include "PointScreenGraphicsItem.h" #include "TextAnnotation.h" namespace Marble { OsmAnnotatePlugin::OsmAnnotatePlugin() : RenderPlugin(), m_itemModel( 0 ), m_networkAccessManager( 0 ), m_isInitialized( false ) { } OsmAnnotatePlugin::~OsmAnnotatePlugin() { delete m_itemModel; delete m_networkAccessManager; } QStringList OsmAnnotatePlugin::backendTypes() const { return QStringList( "osmannotation" ); } QString OsmAnnotatePlugin::renderPolicy() const { return QString( "ALWAYS" ); } QStringList OsmAnnotatePlugin::renderPosition() const { QStringList positions; positions.append( "HOVERS_ABOVE_SURFACE" ); positions.append( "USER_TOOLS" ); return positions; } QString OsmAnnotatePlugin::name() const { return tr( "OSM Annotation Plugin" ); } QString OsmAnnotatePlugin::guiString() const { return tr( "&OSM Annotation Plugin" ); } QString OsmAnnotatePlugin::nameId() const { return QString( "osm-annotation-plugin" ); } QString OsmAnnotatePlugin::description() const { return tr( "This is a render and interaction plugin used for annotating OSM data." ); } QIcon OsmAnnotatePlugin::icon () const { return QIcon(); } void OsmAnnotatePlugin::initialize () { widgetInitalised= false; m_tmp_lineString = 0; m_itemModel = new QList(); m_addingPlacemark = false; m_drawingPolygon = false; m_actions = 0; m_toolbarActions = 0; PointScreenGraphicsItem* topLeft = new PointScreenGraphicsItem(); PointScreenGraphicsItem* bottomRight = new PointScreenGraphicsItem(); topLeft->setVisible(false); bottomRight->setVisible(false); m_selectionBox.first = topLeft; m_selectionBox.second = bottomRight; m_isInitialized = true; } bool OsmAnnotatePlugin::isInitialized () const { return m_isInitialized; } QList* OsmAnnotatePlugin::actionGroups() const { return m_actions; } QList* OsmAnnotatePlugin::toolbarActionGroups() const { return m_toolbarActions; } bool OsmAnnotatePlugin::render( GeoPainter *painter, ViewportParams *viewport, const QString& renderPos, GeoSceneLayer * layer ) { if ( renderPos != "HOVERS_ABOVE_SURFACE" && renderPos != "USER_TOOLS" ) { return true; } if( !widgetInitalised ) { MarbleWidget* marbleWidget = (MarbleWidget*) painter->device(); m_marbleWidget = marbleWidget; setupActions( marbleWidget ); connect(this, SIGNAL(redraw()), marbleWidget, SLOT(repaint()) ); widgetInitalised = true; } if( renderPos == "HOVERS_ABOVE_SURFACE" ) { painter->autoMapQuality(); //so the user can keep track of the current polygon drawing if( m_tmp_lineString ) { painter->drawPolyline( *m_tmp_lineString ); } if( m_itemModel ) { QListIterator it( *m_itemModel ); while( it.hasNext() ) { GeoGraphicsItem* i = it.next(); if( i->flags() & GeoGraphicsItem::ItemIsVisible ) { i->paint( painter, viewport, renderPos, layer ); } } } } QListIterator i(model); while(i.hasNext()) { TmpGraphicsItem* tmp= i.next(); tmp->paint(painter, viewport, renderPos, layer); } if( m_selectionBox.first->visible() ) { m_selectionBox.first->paint( painter, viewport, renderPos, layer ); m_selectionBox.second->paint( painter, viewport, renderPos, layer ); } return true; } void OsmAnnotatePlugin::setAddingPlacemark( bool b) { m_addingPlacemark = b; } void OsmAnnotatePlugin::setDrawingPolygon(bool b) { m_drawingPolygon = b; if( !b ) { //stopped drawing the polygon if ( m_tmp_lineString != 0 ) { AreaAnnotation* area = new AreaAnnotation(); GeoDataPolygon poly( Tessellate ); poly.setOuterBoundary( GeoDataLinearRing(*m_tmp_lineString) ); delete m_tmp_lineString; m_tmp_lineString = 0; area->setGeometry( poly ); model.append(area); //FIXME only redraw the new polygon emit(redraw()); } } } void OsmAnnotatePlugin::receiveNetworkReply( QNetworkReply *reply ) { if( reply->error() == QNetworkReply::NoError ) { readOsmFile( reply, false ); } else { m_errorMessage.showMessage( tr("Error while trying to download the " "OSM file from the server. The " "error was:\n %1" ).arg(reply->errorString()) ); } } void OsmAnnotatePlugin::loadOsmFile() { QString filename; filename = QFileDialog::getOpenFileName(0, tr("Open File"), QString(), tr("All Supported Files (*.osm);;Open Street Map Data (*.osm)")); if ( ! filename.isNull() ) { QFile file( filename ); if ( !file.exists() ) { qWarning( "File does not exist!" ); return; } // Open file in right mode file.open( QIODevice::ReadOnly ); readOsmFile( &file, true ); file.close(); } } void OsmAnnotatePlugin::downloadOsmFile() { if( !m_selectionBox.first->visible() ) { m_errorMessage.showMessage( tr("Please select an area before trying to " "download an OSM file" ) ); return; } QPointF topLeft = m_selectionBox.first->position(); QPointF bottomRight = m_selectionBox.second->position(); qreal lonTop, latTop; qreal lonBottom, latBottom; GeoDataCoordinates topLeftCoordinates; GeoDataCoordinates bottomRightCoordinates; bool topIsOnGlobe = m_marbleWidget->geoCoordinates( topLeft.x(), topLeft.y(), lonTop, latTop, GeoDataCoordinates::Radian); bool bottomIsOnGlobe = m_marbleWidget->geoCoordinates( bottomRight.x(), bottomRight.y(), lonBottom, latBottom, GeoDataCoordinates::Radian ); if( ! ( topIsOnGlobe && bottomIsOnGlobe ) ) { m_errorMessage.showMessage( tr("One of the selection points is not on" " the Globe. Please only select a region" " on the globe.") ); return; } topLeftCoordinates = GeoDataCoordinates( lonTop, latTop, 0, GeoDataCoordinates::Radian ); bottomRightCoordinates = GeoDataCoordinates( lonBottom, latBottom, 0, GeoDataCoordinates::Radian ); GeoDataLineString tempString; tempString.append( topLeftCoordinates ); tempString.append( bottomRightCoordinates ); GeoDataLatLonAltBox bounds = GeoDataLatLonAltBox::fromLineString( tempString ); QString request; request = QString("http://api.openstreetmap.org/api/0.6/map?bbox=%1,%2,%3,%4") .arg(bounds.west(GeoDataCoordinates::Degree) ) .arg(bounds.south(GeoDataCoordinates::Degree) ) .arg(bounds.east( GeoDataCoordinates::Degree) ) .arg( bounds.north( GeoDataCoordinates::Degree ) ); QNetworkRequest networkRequest; networkRequest.setUrl(QUrl(request) ); if( ! m_networkAccessManager ) { m_networkAccessManager = new QNetworkAccessManager( this ) ; connect( m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveNetworkReply(QNetworkReply*)) ); } m_networkAccessManager->get( networkRequest ); } void OsmAnnotatePlugin::saveAnnotationFile() { GeoDataDocument document; QList allAnnotations = annotations(); TextAnnotation* annotation; foreach( annotation, allAnnotations ) { document.append( annotation->toGeoData() ); } QString filename; filename = QFileDialog::getSaveFileName( 0, tr("Save Annotation File"), QString(), tr("All Supported Files (*.kml);;KML file (*.kml)")); if ( ! filename.isNull() ) { GeoWriter writer; //FIXME: a better way to do this? writer.setDocumentType( "http://earth.google.com/kml/2.2" ); QFile file( filename ); // Open file in right mode file.open( QIODevice::ReadWrite ); if ( !writer.write( &file, document ) ) { qWarning( "Could not write the file." ); } } } void OsmAnnotatePlugin::loadAnnotationFile() { //load the file here QString filename; filename = QFileDialog::getOpenFileName(0, tr("Open Annotation File"), QString(), tr("All Supported Files (*.kml);;Kml Annotation file (*.kml)")); if ( ! filename.isNull() ) { GeoDataParser parser( GeoData_KML ); QFile file( filename ); if ( !file.exists() ) { qWarning( "File does not exist!" ); return; } // Open file in right mode file.open( QIODevice::ReadOnly ); if ( !parser.read( &file ) ) { qWarning( "Could not parse file!" ); //do not quit on a failed read! //return } GeoDataDocument* document = dynamic_cast(parser.releaseDocument() ); Q_ASSERT( document ); file.close(); QVector::ConstIterator it = document->constBegin(); for( ; it < document->constEnd(); ++it ) { PlacemarkTextAnnotation* annotation = new PlacemarkTextAnnotation(); annotation->setName( (*it).name() ); annotation->setDescription( (*it).description() ); annotation->setCoordinate( GeoDataPlacemark((*it)).coordinate() ); model.append( annotation ); } delete document; emit repaintNeeded(QRegion()); } } void OsmAnnotatePlugin::selectArea( bool startSelectingArea ) { if ( !startSelectingArea ) { //finished selecting m_selectionBox.first->setVisible( false ); m_selectionBox.second->setVisible( false ); return; } //move the 2 squares to the middle QPointF middle( m_marbleWidget->size().width() /2 , m_marbleWidget->size().height() /2 ); m_selectionBox.first->setPosition( middle - QPointF( 20, 20 ) ); m_selectionBox.second->setPosition( middle + QPointF( 20, 20 ) ); m_selectionBox.first->setVisible( true ); m_selectionBox.second->setVisible( true ); emit repaintNeeded(QRegion()); } bool OsmAnnotatePlugin::eventFilter(QObject* watched, QEvent* event) { MarbleWidget* marbleWidget = dynamic_cast( watched ); Q_ASSERT( marbleWidget ); //FIXME why is the QEvent::MousePress not working? caught somewhere else? //does this mean we need to centralize the event handling? //so far only accept mouse events if( event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseButtonRelease && event->type() != QEvent::MouseMove ) { return false; } QMouseEvent* mouseEvent = dynamic_cast(event); Q_ASSERT( mouseEvent ); GeoDataCoordinates coordinates; //Pass event on to Geo Graphics Items if there is one // Only working in Geo Coordinates for hit tests // FIXME: currently waiting for a decision on http://reviewboard.kde.org/r/1264/ // bool isOnGlobe = marbleWidget->geoCoordinates(mouseEvent->pos(), coordinates ); qreal lon, lat; bool isOnGlobe = marbleWidget->geoCoordinates( mouseEvent->pos().x(), mouseEvent->pos().y(), lon, lat, GeoDataCoordinates::Radian ); coordinates = GeoDataCoordinates( lon, lat, 0, GeoDataCoordinates::Radian ); if( !isOnGlobe ) { //don't handle this event return false; } //Pass the event to Graphics Items QList::ConstIterator itemIterator = model.constBegin(); for( ; itemIterator < model.constEnd() ; ++itemIterator ) { QListIterator it ( (*itemIterator)->regions() ); while ( it.hasNext() ) { QRegion p = it.next(); if( p.contains( mouseEvent->pos() ) ) { if( (*itemIterator)->sceneEvent( event ) ) { return true; } } } } //FIXME: finish cleaning this up // deal with adding a placemark if ( mouseEvent->button() == Qt::LeftButton && m_addingPlacemark ) { //Add a placemark on the screen qreal lon, lat; bool valid = ((MarbleWidget*)watched)->geoCoordinates(((QMouseEvent*)event)->pos().x(), ((QMouseEvent*)event)->pos().y(), lon, lat, GeoDataCoordinates::Radian); if ( valid ) { GeoDataCoordinates point( lon, lat ); PlacemarkTextAnnotation* t = new PlacemarkTextAnnotation(); t->setCoordinate(point); model.append(t); //FIXME only repaint the new placemark ( ( MarbleWidget* ) watched)->repaint(); emit placemarkAdded(); return true; } } // deal with drawing a polygon if ( mouseEvent->button() == Qt::LeftButton && m_drawingPolygon ) { qreal lon, lat; bool valid = ((MarbleWidget*)watched)->geoCoordinates( mouseEvent->pos().x(), mouseEvent->pos().y(), lon, lat, GeoDataCoordinates::Radian); if ( valid ) { if ( m_tmp_lineString == 0 ) { m_tmp_lineString = new GeoDataLineString( Tessellate ); } m_tmp_lineString->append(GeoDataCoordinates(lon, lat)); //FIXME only repaint the line string so far marbleWidget->repaint(); } return true; } return false; } void OsmAnnotatePlugin::setupActions(MarbleWidget* widget) { QList* toolbarActions = new QList(); QList* actions = new QList(); QActionGroup* initial = new QActionGroup(0); initial->setExclusive( false ); QActionGroup* group = new QActionGroup(0); group->setExclusive( true ); QActionGroup* nonExclusiveGroup = new QActionGroup(0); nonExclusiveGroup->setExclusive( false ); QAction* addPlacemark; QAction* drawPolygon; QAction* beginSeparator; QAction* endSeparator; QAction* loadOsmFile; QAction* saveAnnotationFile; QAction* loadAnnotationFile; QAction* enableInputAction; QAction* selectArea; QAction* downloadOsm; addPlacemark = new QAction(this); addPlacemark->setText( tr("Add Placemark") ); addPlacemark->setCheckable( true ); connect( addPlacemark, SIGNAL( toggled(bool)), this, SLOT(setAddingPlacemark(bool)) ); connect( this, SIGNAL(placemarkAdded()) , addPlacemark, SLOT(toggle()) ); drawPolygon = new QAction( this ); drawPolygon->setText( tr("Draw Polygon") ); drawPolygon->setCheckable( true ); connect( drawPolygon, SIGNAL(toggled(bool)), this, SLOT(setDrawingPolygon(bool)) ); loadOsmFile = new QAction( this ); loadOsmFile->setText( tr("Load Osm File") ); connect( loadOsmFile, SIGNAL(triggered()), this, SLOT(loadOsmFile()) ); saveAnnotationFile = new QAction( this ); saveAnnotationFile->setText( tr("Save Annotation File") ); connect( saveAnnotationFile, SIGNAL(triggered()), this, SLOT(saveAnnotationFile()) ); loadAnnotationFile = new QAction( this ); loadAnnotationFile->setText( tr("Load Annotation File" ) ); connect( loadAnnotationFile, SIGNAL(triggered()), this, SLOT(loadAnnotationFile()) ); beginSeparator = new QAction( this ); beginSeparator->setSeparator( true ); endSeparator = new QAction ( this ); endSeparator->setSeparator( true ); enableInputAction = new QAction(this); enableInputAction->setToolTip(tr("Enable Marble Input")); enableInputAction->setCheckable(true); enableInputAction->setChecked( true ); enableInputAction->setIcon( QIcon( MarbleDirs::path("bitmaps/hand.png") ) ); connect( enableInputAction, SIGNAL(toggled(bool)), widget, SLOT( setInputEnabled(bool)) ); selectArea = new QAction( this ); selectArea->setText( tr("Select Map Area") ); selectArea->setCheckable( true ); connect( selectArea, SIGNAL(triggered(bool)), this, SLOT(selectArea(bool)) ); downloadOsm = new QAction( this ); downloadOsm->setText( tr("Download Osm File") ); downloadOsm->setToolTip(tr("Download Osm File for selected area")); connect( downloadOsm, SIGNAL(triggered()), this, SLOT(downloadOsmFile()) ); initial->addAction( enableInputAction ); initial->addAction( beginSeparator ); group->addAction( addPlacemark ); group->addAction( drawPolygon ); group->addAction( loadOsmFile ); group->addAction( saveAnnotationFile ); group->addAction( loadAnnotationFile ); group->addAction( endSeparator ); nonExclusiveGroup->addAction( selectArea ); nonExclusiveGroup->addAction( downloadOsm ); actions->append( initial ); actions->append( group ); actions->append( nonExclusiveGroup ); toolbarActions->append( initial ); toolbarActions->append( group ); toolbarActions->append( nonExclusiveGroup ); //delete the old groups if they exist delete m_actions; delete m_toolbarActions; m_actions = actions; m_toolbarActions = toolbarActions; emit actionGroupsChanged(); } void OsmAnnotatePlugin::readOsmFile( QIODevice *device, bool flyToFile ) { GeoDataParser parser( GeoData_OSM ); if ( !parser.read( device ) ) { qWarning( "Could not parse file!" ); //do not quit on a failed read! //return } QList* model = parser.releaseModel(); Q_ASSERT( model ); m_itemModel->append(*model); // now zoom to the newly added OSM file if( flyToFile && m_itemModel->size() > 0 ) { OsmBoundsGraphicsItem* item; // mostly guaranteed that the first item will be a bounds item // if not then don't centre on anything item = dynamic_cast( model->first() ); if( item ) { m_marbleWidget->centerOn( item->latLonBox() ); } } model->clear(); delete model; emit repaintNeeded(QRegion()); } QList OsmAnnotatePlugin::annotations() const { QList tmpAnnotations; TmpGraphicsItem* item; foreach( item, model ) { TextAnnotation* annotation = dynamic_cast(item); if( annotation ) { tmpAnnotations.append( annotation ); } } return tmpAnnotations; } } Q_EXPORT_PLUGIN2( OsmAnnotatePlugin, Marble::OsmAnnotatePlugin ) #include "OsmAnnotatePlugin.moc"