File indexing completed on 2024-05-05 03:50:38

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2009 Andrew Manson <g.real.ate@gmail.com>
0004 // SPDX-FileCopyrightText: 2013 Thibaut Gridel <tgridel@free.fr>
0005 // SPDX-FileCopyrightText: 2014 Calin Cruceru <crucerucalincristian@gmail.com>
0006 //
0007 
0008 // Self
0009 #include "AreaAnnotation.h"
0010 
0011 // Qt
0012 #include <qmath.h>
0013 #include <QPair>
0014 #include <QApplication>
0015 #include <QPalette>
0016 
0017 // Marble
0018 #include "GeoDataPlacemark.h"
0019 #include "GeoDataLinearRing.h"
0020 #include "GeoDataPolygon.h"
0021 #include "GeoPainter.h"
0022 #include "ViewportParams.h"
0023 #include "SceneGraphicsTypes.h"
0024 #include "MarbleColors.h"
0025 #include "MergingPolygonNodesAnimation.h"
0026 #include "PolylineNode.h"
0027 #include "osm/OsmPlacemarkData.h"
0028 
0029 
0030 namespace Marble {
0031 
0032 const int AreaAnnotation::regularDim = 15;
0033 const int AreaAnnotation::selectedDim = 15;
0034 const int AreaAnnotation::mergedDim = 20;
0035 const int AreaAnnotation::hoveredDim = 20;
0036 const QColor AreaAnnotation::regularColor = Oxygen::aluminumGray3;
0037 const QColor AreaAnnotation::mergedColor = Oxygen::emeraldGreen6;
0038 
0039 AreaAnnotation::AreaAnnotation( GeoDataPlacemark *placemark ) :
0040     SceneGraphicsItem( placemark ),
0041     m_viewport( nullptr ),
0042     m_regionsInitialized( false ),
0043     m_busy( false ),
0044     m_hoveredNode( -1, -1 ),
0045     m_interactingObj( InteractingNothing ),
0046     m_virtualHovered( -1, -1 )
0047 {
0048     setPaintLayers(QStringList() << "AreaAnnotation");
0049 }
0050 
0051 AreaAnnotation::~AreaAnnotation()
0052 {
0053     delete m_animation;
0054 }
0055 
0056 void AreaAnnotation::paint(GeoPainter *painter, const ViewportParams *viewport , const QString &layer, int tileZoomLevel)
0057 {
0058     Q_UNUSED(layer);
0059     Q_UNUSED(tileZoomLevel);
0060     m_viewport = viewport;
0061     Q_ASSERT(geodata_cast<GeoDataPolygon>(placemark()->geometry()));
0062 
0063     painter->save();
0064     if ( state() == SceneGraphicsItem::DrawingPolygon || !m_regionsInitialized ) {
0065         setupRegionsLists( painter );
0066         m_regionsInitialized = true;
0067     } else {
0068         updateRegions( painter );
0069     }
0070 
0071     if ( hasFocus() ) {
0072         drawNodes( painter );
0073     }
0074     painter->restore();
0075 }
0076 
0077 bool AreaAnnotation::containsPoint( const QPoint &point ) const
0078 {
0079     if ( m_busy ) {
0080         return false;
0081     }
0082 
0083     if ( state() == SceneGraphicsItem::Editing ) {
0084         return ( polygonContains( point ) && innerBoundsContain( point ) == -1 ) ||
0085                outerNodeContains( point ) != -1 ||
0086                innerNodeContains( point ) != QPair<int, int>( -1, -1 );
0087 
0088     } else if ( state() == SceneGraphicsItem::AddingPolygonHole ) {
0089         return polygonContains( point ) && outerNodeContains( point ) == -1 &&
0090                innerNodeContains( point ) == QPair<int, int>( -1, -1 );
0091 
0092     } else if ( state() == SceneGraphicsItem::MergingNodes ) {
0093         return outerNodeContains( point ) != -1 ||
0094                innerNodeContains( point ) != QPair<int, int>( -1, -1 );
0095 
0096     } else if ( state() == SceneGraphicsItem::AddingNodes ) {
0097         return ( polygonContains( point ) && innerBoundsContain( point ) == -1 ) ||
0098                virtualNodeContains( point ) != QPair<int, int>( -1, -1 ) ||
0099                innerNodeContains( point ) != QPair<int, int>( -1, -1 ) ||
0100                outerNodeContains( point ) != -1;
0101     }
0102 
0103     return false;
0104 }
0105 
0106 void AreaAnnotation::dealWithItemChange( const SceneGraphicsItem *other )
0107 {
0108     Q_UNUSED( other );
0109 
0110     // So far we only deal with item changes when hovering nodes, so that
0111     // they do not remain hovered when changing the item we interact with.
0112     if ( state() == SceneGraphicsItem::Editing ) {
0113         if ( m_hoveredNode != QPair<int, int>( -1, -1 ) ) {
0114             const int i = m_hoveredNode.first;
0115             const int j = m_hoveredNode.second;
0116 
0117             if ( j == -1 ) {
0118                 m_outerNodesList[i].setFlag( PolylineNode::NodeIsEditingHighlighted, false );
0119             } else {
0120                 m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsEditingHighlighted, false );
0121             }
0122         }
0123 
0124         m_hoveredNode = QPair<int, int>( -1, -1 );
0125     } else if ( state() == SceneGraphicsItem::MergingNodes ) {
0126         if ( m_hoveredNode != QPair<int, int>( -1, -1 ) ) {
0127             const int i = m_hoveredNode.first;
0128             const int j = m_hoveredNode.second;
0129 
0130             if ( j == -1 ) {
0131                 m_outerNodesList[i].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
0132             } else {
0133                 m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
0134             }
0135         }
0136 
0137         m_hoveredNode = QPair<int, int>( -1, -1 );
0138     } else if ( state() == SceneGraphicsItem::AddingNodes ) {
0139         m_virtualHovered = QPair<int, int>( -1, -1 );
0140     }
0141 }
0142 
0143 void AreaAnnotation::move( const GeoDataCoordinates &source, const GeoDataCoordinates &destination )
0144 {
0145     GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
0146     GeoDataLinearRing outerRing = polygon->outerBoundary();
0147     QVector<GeoDataLinearRing> innerRings = polygon->innerBoundaries();
0148     OsmPlacemarkData *osmData = nullptr;
0149     if ( placemark()->hasOsmData() ) {
0150         osmData = &placemark()->osmData();
0151     }
0152 
0153     polygon->outerBoundary().clear();
0154     polygon->innerBoundaries().clear();
0155 
0156     const qreal deltaLat = destination.latitude() - source.latitude();
0157     const qreal deltaLon = destination.longitude() - source.longitude();
0158 
0159     Quaternion latRectAxis = Quaternion::fromEuler( 0, destination.longitude(), 0);
0160     Quaternion latAxis = Quaternion::fromEuler( -deltaLat, 0, 0);
0161     Quaternion lonAxis = Quaternion::fromEuler(0, deltaLon, 0);
0162     Quaternion rotAxis = latRectAxis * latAxis * latRectAxis.inverse() * lonAxis;
0163 
0164     for ( int i = 0; i < outerRing.size(); ++i ) {
0165         const GeoDataCoordinates movedPoint = outerRing.at(i).rotateAround(rotAxis);
0166         // Keeping the OsmPlacemarkData synchronized with the geometry
0167         if ( osmData ) {
0168             osmData->memberReference( -1 ).changeNodeReference( outerRing.at( i ), movedPoint );
0169         }
0170         polygon->outerBoundary().append( movedPoint );
0171     }
0172 
0173     for ( int i = 0; i < innerRings.size(); ++i ) {
0174         GeoDataLinearRing newRing( Tessellate );
0175         for ( int j = 0; j < innerRings.at(i).size(); ++j ) {
0176             const GeoDataCoordinates movedPoint = innerRings.at(i).at(j).rotateAround(rotAxis);
0177             if ( osmData ) {
0178                 osmData->memberReference( i ).changeNodeReference( innerRings.at( i ).at( j ), movedPoint );
0179             }
0180             newRing.append( movedPoint );
0181         }
0182         polygon->innerBoundaries().append( newRing );
0183     }
0184 }
0185 
0186 void AreaAnnotation::setBusy( bool enabled )
0187 {
0188     m_busy = enabled;
0189 
0190     if ( !enabled && m_animation && state() == SceneGraphicsItem::MergingNodes ) {
0191         // Update the PolylineNodes lists after the animation has finished its execution.
0192         const int ff = m_firstMergedNode.first;
0193         const int fs = m_firstMergedNode.second;
0194         const int sf = m_secondMergedNode.first;
0195         const int ss = m_secondMergedNode.second;
0196 
0197         if ( ff != -1 && fs == -1 && sf != -1 && ss == -1 ) {
0198             m_outerNodesList[sf].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
0199             m_hoveredNode = QPair<int, int>( -1, -1 );
0200 
0201             // Remove the merging node flag and add the NodeIsSelected flag if either one of the
0202             // merged nodes had been selected before merging them.
0203             m_outerNodesList[sf].setFlag( PolylineNode::NodeIsMerged, false );
0204             if ( m_outerNodesList.at(ff).isSelected() ) {
0205                 m_outerNodesList[sf].setFlag( PolylineNode::NodeIsSelected );
0206             }
0207             m_outerNodesList.removeAt( ff );
0208 
0209             m_firstMergedNode = QPair<int, int>( -1, -1 );
0210             m_secondMergedNode = QPair<int, int>( -1, -1 );
0211         } else if ( ff != -1 && fs != -1 && sf != -1 && ss != -1 ) {
0212             m_innerNodesList[sf][ss].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
0213             m_hoveredNode = QPair<int, int>( -1, -1 );
0214 
0215             m_innerNodesList[sf][ss].setFlag( PolylineNode::NodeIsMerged, false );
0216             if ( m_innerNodesList.at(ff).at(fs).isSelected() ) {
0217                 m_innerNodesList[sf][ss].setFlag( PolylineNode::NodeIsSelected );
0218             }
0219             m_innerNodesList[sf].removeAt( fs );
0220 
0221             m_firstMergedNode = QPair<int, int>( -1, -1 );
0222             m_secondMergedNode = QPair<int, int>( -1, -1 );
0223         }
0224 
0225         delete m_animation;
0226     }
0227 }
0228 
0229 bool AreaAnnotation::isBusy() const
0230 {
0231     return m_busy;
0232 }
0233 
0234 void AreaAnnotation::deselectAllNodes()
0235 {
0236     if ( state() != SceneGraphicsItem::Editing ) {
0237         return;
0238     }
0239 
0240     for ( int i = 0 ; i < m_outerNodesList.size(); ++i ) {
0241         m_outerNodesList[i].setFlag( PolylineNode::NodeIsSelected, false );
0242     }
0243 
0244     for ( int i = 0; i < m_innerNodesList.size(); ++i ) {
0245         for ( int j = 0; j < m_innerNodesList.at(i).size(); ++j ) {
0246             m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsSelected, false );
0247         }
0248     }
0249 }
0250 
0251 void AreaAnnotation::deleteAllSelectedNodes()
0252 {
0253     if ( state() != SceneGraphicsItem::Editing ) {
0254         return;
0255     }
0256 
0257     GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
0258     GeoDataLinearRing &outerRing = polygon->outerBoundary();
0259     QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
0260     OsmPlacemarkData *osmData = nullptr;
0261     OsmPlacemarkData initialOsmData;
0262     if ( placemark()->hasOsmData() ) {
0263         osmData = &placemark()->osmData();
0264         initialOsmData = placemark()->osmData();
0265     }
0266 
0267     // If it proves inefficient, try something different.
0268     GeoDataLinearRing initialOuterRing = polygon->outerBoundary();
0269     QVector<GeoDataLinearRing> initialInnerRings = polygon->innerBoundaries();
0270     const QVector<PolylineNode> initialOuterNodes = m_outerNodesList;
0271     const QVector< QVector<PolylineNode> > initialInnerNodes = m_innerNodesList;
0272 
0273     for ( int i = 0; i < outerRing.size(); ++i ) {
0274         if ( m_outerNodesList.at(i).isSelected() ) {
0275             if ( m_outerNodesList.size() <= 3 ) {
0276                 setRequest( SceneGraphicsItem::RemovePolygonRequest );
0277                 return;
0278             }
0279             if ( osmData ) {
0280                 osmData->memberReference( -1 ).removeNodeReference( initialOuterRing.at( i ) );
0281             }
0282             m_outerNodesList.removeAt( i );
0283             outerRing.remove( i );
0284             --i;
0285         }
0286     }
0287 
0288     for ( int i = 0; i < innerRings.size(); ++i ) {
0289         for ( int j = 0; j < innerRings.at(i).size(); ++j ) {
0290             if ( m_innerNodesList.at(i).at(j).isSelected() ) {
0291                 if ( m_innerNodesList.at(i).size() <= 3 ) {
0292 
0293                     if ( osmData ) {
0294                         osmData->removeMemberReference( i );
0295                     }
0296                     innerRings.remove( i );
0297                     m_innerNodesList.removeAt( i );
0298                     --i;
0299                     break;
0300                 }
0301 
0302                 if ( osmData ) {
0303                     osmData->memberReference( i ).removeNodeReference( initialInnerRings.at( i ).at( j ) );
0304                 }
0305                 innerRings[i].remove( j );
0306                 m_innerNodesList[i].removeAt( j );
0307                 --j;
0308             }
0309         }
0310     }
0311 
0312     if ( !isValidPolygon() ) {
0313         if ( osmData ) {
0314             placemark()->setOsmData( initialOsmData );
0315         }
0316         polygon->outerBoundary() = initialOuterRing;
0317         polygon->innerBoundaries() = initialInnerRings;
0318         m_outerNodesList = initialOuterNodes;
0319         m_innerNodesList = initialInnerNodes;
0320         setRequest( SceneGraphicsItem::InvalidShapeWarning );
0321     }
0322 }
0323 
0324 void AreaAnnotation::deleteClickedNode()
0325 {
0326     if ( state() != SceneGraphicsItem::Editing ) {
0327         return;
0328     }
0329 
0330     GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
0331     GeoDataLinearRing &outerRing = polygon->outerBoundary();
0332     QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
0333     OsmPlacemarkData *osmData = nullptr;
0334     OsmPlacemarkData initialOsmData;
0335     if ( placemark()->hasOsmData() ) {
0336         osmData = &placemark()->osmData();
0337         initialOsmData = placemark()->osmData();
0338     }
0339 
0340     // If it proves inefficient, try something different.
0341     GeoDataLinearRing initialOuterRing = polygon->outerBoundary();
0342     QVector<GeoDataLinearRing> initialInnerRings = polygon->innerBoundaries();
0343     const QVector<PolylineNode> initialOuterNodes = m_outerNodesList;
0344     const QVector< QVector<PolylineNode> > initialInnerNodes = m_innerNodesList;
0345 
0346     int i = m_clickedNodeIndexes.first;
0347     int j = m_clickedNodeIndexes.second;
0348 
0349     m_hoveredNode = QPair<int, int>( -1, -1 );
0350 
0351     if ( i != -1 && j == -1 ) {
0352         if ( m_outerNodesList.size() <= 3 ) {
0353             setRequest( SceneGraphicsItem::RemovePolygonRequest );
0354             return;
0355         }
0356 
0357         // Keep the OsmPlacemarkData synchronized with the geometry
0358         if ( osmData ) {
0359             osmData->removeNodeReference( outerRing.at( i ) );
0360         }
0361         outerRing.remove( i );
0362         m_outerNodesList.removeAt( i );
0363     } else if ( i != -1 && j != -1 ) {
0364         if ( m_innerNodesList.at(i).size() <= 3 ) {
0365             if ( osmData ) {
0366                 osmData->removeMemberReference( i );
0367             }
0368             innerRings.remove( i );
0369             m_innerNodesList.removeAt( i );
0370             return;
0371         }
0372         if ( osmData ) {
0373             osmData->memberReference( i ).removeNodeReference( innerRings.at( i ).at( j ) );
0374         }
0375         innerRings[i].remove( j );
0376         m_innerNodesList[i].removeAt( j );
0377     }
0378 
0379     if ( !isValidPolygon() ) {
0380         if ( osmData ) {
0381             placemark()->setOsmData( initialOsmData );
0382         }
0383         polygon->outerBoundary() = initialOuterRing;
0384         polygon->innerBoundaries() = initialInnerRings;
0385         m_outerNodesList = initialOuterNodes;
0386         m_innerNodesList = initialInnerNodes;
0387         setRequest( SceneGraphicsItem::InvalidShapeWarning );
0388     }
0389 }
0390 
0391 void AreaAnnotation::changeClickedNodeSelection()
0392 {
0393     if ( state() != SceneGraphicsItem::Editing ) {
0394         return;
0395     }
0396 
0397     const int i = m_clickedNodeIndexes.first;
0398     const int j = m_clickedNodeIndexes.second;
0399 
0400     if ( i != -1 && j == -1 ) {
0401         m_outerNodesList[i].setFlag( PolylineNode::NodeIsSelected,
0402                                      !m_outerNodesList.at(i).isSelected() );
0403     } else if ( i != -1 && j != -1 ) {
0404         m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsSelected,
0405                                         !m_innerNodesList.at(i).at(j).isSelected() );
0406     }
0407 }
0408 
0409 bool AreaAnnotation::hasNodesSelected() const
0410 {
0411     for ( int i = 0; i < m_outerNodesList.size(); ++i ) {
0412         if ( m_outerNodesList.at(i).isSelected() ) {
0413             return true;
0414         }
0415     }
0416 
0417     for ( int i = 0; i < m_innerNodesList.size(); ++i ) {
0418         for ( int j = 0; j < m_innerNodesList.at(i).size(); ++j ) {
0419             if ( m_innerNodesList.at(i).at(j).isSelected() ) {
0420                 return true;
0421             }
0422         }
0423     }
0424 
0425     return false;
0426 }
0427 
0428 bool AreaAnnotation::clickedNodeIsSelected() const
0429 {
0430     const int i = m_clickedNodeIndexes.first;
0431     const int j = m_clickedNodeIndexes.second;
0432 
0433     return ( i != -1 && j == -1 && m_outerNodesList.at(i).isSelected() ) ||
0434            ( i != -1 && j != -1 && m_innerNodesList.at(i).at(j).isSelected() );
0435 }
0436 
0437 QPointer<MergingPolygonNodesAnimation> AreaAnnotation::animation()
0438 {
0439     return m_animation;
0440 }
0441 
0442 bool AreaAnnotation::mousePressEvent( QMouseEvent *event )
0443 {
0444     if ( !m_viewport || m_busy ) {
0445         return false;
0446     }
0447 
0448     setRequest( SceneGraphicsItem::NoRequest );
0449 
0450     if ( state() == SceneGraphicsItem::Editing ) {
0451         return processEditingOnPress( event );
0452     } else if ( state() == SceneGraphicsItem::AddingPolygonHole ) {
0453         return processAddingHoleOnPress( event );
0454     } else if ( state() == SceneGraphicsItem::MergingNodes ) {
0455         return processMergingOnPress( event );
0456     } else if ( state() == SceneGraphicsItem::AddingNodes ) {
0457         return processAddingNodesOnPress( event );
0458     }
0459 
0460     return false;
0461 }
0462 
0463 bool AreaAnnotation::mouseMoveEvent( QMouseEvent *event )
0464 {
0465     if ( !m_viewport || m_busy ) {
0466         return false;
0467     }
0468 
0469     setRequest( SceneGraphicsItem::NoRequest );
0470 
0471     if ( state() == SceneGraphicsItem::Editing ) {
0472         return processEditingOnMove( event );
0473     } else if ( state() == SceneGraphicsItem::AddingPolygonHole ) {
0474         return processAddingHoleOnMove( event );
0475     } else if ( state() == SceneGraphicsItem::MergingNodes ) {
0476         return processMergingOnMove( event );
0477     } else if ( state() == SceneGraphicsItem::AddingNodes ) {
0478         return processAddingNodesOnMove( event );
0479     }
0480 
0481     return false;
0482 }
0483 
0484 bool AreaAnnotation::mouseReleaseEvent( QMouseEvent *event )
0485 {
0486     if ( !m_viewport || m_busy ) {
0487         return false;
0488     }
0489 
0490     setRequest( SceneGraphicsItem::NoRequest );
0491 
0492     if ( state() == SceneGraphicsItem::Editing ) {
0493         return processEditingOnRelease( event );
0494     } else if ( state() == SceneGraphicsItem::AddingPolygonHole ) {
0495         return processAddingHoleOnRelease( event );
0496     } else if ( state() == SceneGraphicsItem::MergingNodes ) {
0497         return processMergingOnRelease( event );
0498     } else if ( state() == SceneGraphicsItem::AddingNodes ) {
0499         return processAddingNodesOnRelease( event );
0500     }
0501 
0502     return false;
0503 }
0504 
0505 void AreaAnnotation::dealWithStateChange( SceneGraphicsItem::ActionState previousState )
0506 {
0507     // Dealing with cases when exiting a state has an effect on this item.
0508     if ( previousState == SceneGraphicsItem::Editing ) {
0509         // Make sure that when changing the state, there is no highlighted node.
0510         if ( m_hoveredNode != QPair<int, int>( -1, -1 ) ) {
0511             const int i = m_hoveredNode.first;
0512             const int j = m_hoveredNode.second;
0513 
0514             if ( j == -1 ) {
0515                 m_outerNodesList[i].setFlag( PolylineNode::NodeIsEditingHighlighted, false );
0516             } else {
0517                 m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsEditingHighlighted, false );
0518             }
0519         }
0520 
0521         m_clickedNodeIndexes = QPair<int, int>( -1, -1 );
0522         m_hoveredNode = QPair<int, int>( -1, -1 );
0523     } else if ( previousState == SceneGraphicsItem::AddingPolygonHole ) {
0524         // Check if a polygon hole was being drawn before changing state.
0525         GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
0526         QVector<GeoDataLinearRing> &innerBounds = polygon->innerBoundaries();
0527 
0528         if ( innerBounds.size() && innerBounds.last().size() <= 2 ) {
0529             // If only two nodes were added, remove this inner boundary entirely.
0530             innerBounds.remove( innerBounds.size() - 1 );
0531             m_innerNodesList.removeLast();
0532             return;
0533         }
0534     } else if ( previousState == SceneGraphicsItem::MergingNodes ) {
0535         // If there was only a node selected for being merged and the state changed,
0536         // deselect it.
0537         const int i = m_firstMergedNode.first;
0538         const int j = m_firstMergedNode.second;
0539 
0540         if ( i != -1 && j != -1 ) {
0541             m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsMerged, false );
0542         } else if ( i != -1 && j == -1 ) {
0543             m_outerNodesList[i].setFlag( PolylineNode::NodeIsMerged, false );
0544         }
0545 
0546         // Make sure that when changing the state, there is no highlighted node.
0547         if ( m_hoveredNode != QPair<int, int>( -1, -1 ) ) {
0548             int i = m_hoveredNode.first;
0549             int j = m_hoveredNode.second;
0550 
0551             if ( j == -1 ) {
0552                 m_outerNodesList[i].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
0553             } else {
0554                 m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
0555             }
0556         }
0557 
0558         m_firstMergedNode = QPair<int, int>( -1, -1 );
0559         m_hoveredNode = QPair<int, int>( -1, -1 );
0560         delete m_animation;
0561     } else if ( previousState == SceneGraphicsItem::AddingNodes ) {
0562         m_outerVirtualNodes.clear();
0563         m_innerVirtualNodes.clear();
0564         m_virtualHovered = QPair<int, int>( -1, -1 );
0565         m_adjustedNode = -2;
0566     }
0567 
0568     // Dealing with cases when entering a state has an effect on this item, or
0569     // initializations are needed.
0570     if ( state() == SceneGraphicsItem::Editing ) {
0571         m_interactingObj = InteractingNothing;
0572         m_clickedNodeIndexes = QPair<int, int>( -1, -1 );
0573         m_hoveredNode = QPair<int, int>( -1, -1 );
0574     } else if ( state() == SceneGraphicsItem::AddingPolygonHole ) {
0575         // Nothing to do so far when entering this state.
0576         GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
0577         QVector<GeoDataLinearRing> &innerBounds = polygon->innerBoundaries();
0578 
0579         m_innerNodesList.append(QVector<PolylineNode>());
0580         innerBounds.append( GeoDataLinearRing( Tessellate ) );
0581     } else if ( state() == SceneGraphicsItem::MergingNodes ) {
0582         m_firstMergedNode = QPair<int, int>( -1, -1 );
0583         m_secondMergedNode = QPair<int, int>( -1, -1 );
0584         m_hoveredNode = QPair<int, int>( -1, -1 );
0585         m_animation = nullptr;
0586     } else if ( state() == SceneGraphicsItem::AddingNodes ) {
0587         m_virtualHovered = QPair<int, int>( -1, -1 );
0588         m_adjustedNode = -2;
0589     }
0590 }
0591 
0592 const char *AreaAnnotation::graphicType() const
0593 {
0594     return SceneGraphicsTypes::SceneGraphicAreaAnnotation;
0595 }
0596 
0597 bool AreaAnnotation::isValidPolygon() const
0598 {
0599     const GeoDataPolygon *poly = static_cast<const GeoDataPolygon*>( placemark()->geometry() );
0600     const QVector<GeoDataLinearRing> &innerRings = poly->innerBoundaries();
0601 
0602     for ( const GeoDataLinearRing &innerRing: innerRings ) {
0603         for ( int i = 0; i < innerRing.size(); ++i ) {
0604             if ( !poly->outerBoundary().contains( innerRing.at(i) ) ) {
0605                 return false;
0606             }
0607         }
0608     }
0609 
0610     return true;
0611 }
0612 
0613 void AreaAnnotation::setupRegionsLists( GeoPainter *painter )
0614 {
0615     const GeoDataPolygon *polygon = static_cast<const GeoDataPolygon*>( placemark()->geometry() );
0616     const GeoDataLinearRing &outerRing = polygon->outerBoundary();
0617     const QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
0618 
0619     // Add the outer boundary nodes.
0620     QVector<GeoDataCoordinates>::ConstIterator itBegin = outerRing.constBegin();
0621     QVector<GeoDataCoordinates>::ConstIterator itEnd = outerRing.constEnd();
0622 
0623     m_outerNodesList.clear();
0624     m_innerNodesList.clear();
0625     m_boundariesList.clear();
0626 
0627     for ( ; itBegin != itEnd; ++itBegin ) {
0628         const PolylineNode newNode = PolylineNode( painter->regionFromEllipse( *itBegin, regularDim, regularDim ) );
0629         m_outerNodesList.append( newNode );
0630     }
0631 
0632     for ( const GeoDataLinearRing &innerRing: innerRings ) {
0633         QVector<GeoDataCoordinates>::ConstIterator itBegin = innerRing.constBegin();
0634         QVector<GeoDataCoordinates>::ConstIterator itEnd = innerRing.constEnd();
0635         QVector<PolylineNode> innerNodes;
0636         innerNodes.reserve(innerRing.size());
0637 
0638         for ( ; itBegin != itEnd; ++itBegin ) {
0639             const PolylineNode newNode = PolylineNode( painter->regionFromEllipse( *itBegin, regularDim, regularDim ) );
0640             innerNodes.append( newNode );
0641         }
0642         m_innerNodesList.append( innerNodes );
0643     }
0644 
0645     // Add the outer boundary to the boundaries list.
0646     m_boundariesList.append( painter->regionFromPolygon( outerRing, Qt::OddEvenFill ) );
0647 }
0648 
0649 void AreaAnnotation::updateRegions( GeoPainter *painter )
0650 {
0651     if ( m_busy ) {
0652         return;
0653     }
0654 
0655     const GeoDataPolygon *polygon = static_cast<const GeoDataPolygon*>( placemark()->geometry() );
0656     const GeoDataLinearRing &outerRing = polygon->outerBoundary();
0657     const QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
0658 
0659 
0660     if ( state() == SceneGraphicsItem::AddingNodes ) {
0661         // Create and update virtual nodes lists when being in the AddingPolgonNodes state, to
0662         // avoid overhead in other states.
0663         m_outerVirtualNodes.clear();
0664         const QRegion firstRegion( painter->regionFromEllipse( outerRing.first().interpolate(
0665                                              outerRing.last(), 0.5 ), hoveredDim, hoveredDim ) );
0666         m_outerVirtualNodes.append( PolylineNode( firstRegion ) );
0667         for ( int i = 0; i < outerRing.size() - 1; ++i ) {
0668             const QRegion newRegion( painter->regionFromEllipse( outerRing.at(i).interpolate(
0669                                              outerRing.at(i+1), 0.5 ), hoveredDim, hoveredDim ) );
0670             m_outerVirtualNodes.append( PolylineNode( newRegion ) );
0671         }
0672 
0673         m_innerVirtualNodes.clear();
0674         m_innerVirtualNodes.reserve(innerRings.size());
0675         for ( int i = 0; i < innerRings.size(); ++i ) {
0676             m_innerVirtualNodes.append(QVector<PolylineNode>());
0677             const QRegion firstRegion( painter->regionFromEllipse( innerRings.at(i).first().interpolate(
0678                                              innerRings.at(i).last(), 0.5 ), hoveredDim, hoveredDim ) );
0679             m_innerVirtualNodes[i].append( PolylineNode( firstRegion ) );
0680             for ( int j = 0; j < innerRings.at(i).size() - 1; ++j ) {
0681                 const QRegion newRegion( painter->regionFromEllipse( innerRings.at(i).at(j).interpolate(
0682                                              innerRings.at(i).at(j+1), 0.5 ), hoveredDim, hoveredDim ) );
0683                 m_innerVirtualNodes[i].append( PolylineNode( newRegion ) );
0684             }
0685         }
0686     }
0687 
0688     // Update the boundaries list.
0689     m_boundariesList.clear();
0690     m_boundariesList.reserve(1 + innerRings.size());
0691 
0692     m_boundariesList.append( painter->regionFromPolygon( outerRing, Qt::OddEvenFill ) );
0693     for ( const GeoDataLinearRing &ring: innerRings ) {
0694         m_boundariesList.append( painter->regionFromPolygon( ring, Qt::OddEvenFill ) );
0695     }
0696 
0697     // Update the outer and inner nodes lists.
0698     Q_ASSERT( m_outerNodesList.size() == outerRing.size() );
0699     for ( int i = 0; i < m_outerNodesList.size(); ++i ) {
0700         const QRegion newRegion = m_outerNodesList.at(i).isSelected() ?
0701                                   painter->regionFromEllipse( outerRing.at(i), selectedDim, selectedDim ) :
0702                                   painter->regionFromEllipse( outerRing.at(i), regularDim, regularDim );
0703         m_outerNodesList[i].setRegion( newRegion );
0704     }
0705 
0706     Q_ASSERT( m_innerNodesList.size() == innerRings.size() );
0707     for ( int i = 0; i < m_innerNodesList.size(); ++i ) {
0708         Q_ASSERT( m_innerNodesList.at(i).size() == innerRings.at(i).size() );
0709         for ( int j = 0; j < m_innerNodesList.at(i).size(); ++j ) {
0710             const QRegion newRegion = m_innerNodesList.at(i).at(j).isSelected() ?
0711                            painter->regionFromEllipse( innerRings.at(i).at(j), selectedDim, selectedDim ) :
0712                            painter->regionFromEllipse( innerRings.at(i).at(j), regularDim, regularDim );
0713             m_innerNodesList[i][j].setRegion( newRegion );
0714         }
0715     }
0716 }
0717 
0718 void AreaAnnotation::drawNodes( GeoPainter *painter )
0719 {
0720     // These are the 'real' dimensions of the drawn nodes. The ones which have class scope are used
0721     // to generate the regions and they are a little bit larger, because, for example, it would be
0722     // a little bit too hard to select nodes.
0723     static const int d_regularDim = 10;
0724     static const int d_selectedDim = 10;
0725     static const int d_mergedDim = 20;
0726     static const int d_hoveredDim = 20;
0727 
0728     const GeoDataPolygon *polygon = static_cast<const GeoDataPolygon*>( placemark()->geometry() );
0729     const GeoDataLinearRing &outerRing = polygon->outerBoundary();
0730     const QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
0731 
0732     QColor glowColor = QApplication::palette().highlightedText().color();
0733     glowColor.setAlpha(120);
0734 
0735     auto const selectedColor = QApplication::palette().highlight().color();
0736     auto const hoveredColor = selectedColor;
0737     for ( int i = 0; i < outerRing.size(); ++i ) {
0738         // The order here is important, because a merged node can be at the same time selected.
0739         if ( m_outerNodesList.at(i).isBeingMerged() ) {
0740             painter->setBrush( mergedColor );
0741             painter->drawEllipse( outerRing.at(i), d_mergedDim, d_mergedDim );
0742         } else if ( m_outerNodesList.at(i).isSelected() ) {
0743             painter->setBrush( selectedColor );
0744             painter->drawEllipse( outerRing.at(i), d_selectedDim, d_selectedDim );
0745 
0746             if ( m_outerNodesList.at(i).isEditingHighlighted() ||
0747                  m_outerNodesList.at(i).isMergingHighlighted() ) {
0748                 QPen defaultPen = painter->pen();
0749                 QPen newPen;
0750                 newPen.setWidth( defaultPen.width() + 3 );
0751                 newPen.setColor( glowColor );
0752 
0753                 painter->setBrush( Qt::NoBrush );
0754                 painter->setPen( newPen );
0755                 painter->drawEllipse( outerRing.at(i), d_selectedDim + 2, d_selectedDim + 2 );
0756                 painter->setPen( defaultPen );
0757             }
0758         } else {
0759             painter->setBrush( regularColor );
0760             painter->drawEllipse( outerRing.at(i), d_regularDim, d_regularDim );
0761 
0762             if ( m_outerNodesList.at(i).isEditingHighlighted() ||
0763                  m_outerNodesList.at(i).isMergingHighlighted() ) {
0764                 QPen defaultPen = painter->pen();
0765                 QPen newPen;
0766                 newPen.setWidth( defaultPen.width() + 3 );
0767                 newPen.setColor( glowColor );
0768 
0769                 painter->setPen( newPen );
0770                 painter->setBrush( Qt::NoBrush );
0771                 painter->drawEllipse( outerRing.at(i), d_regularDim + 2, d_regularDim + 2 );
0772                 painter->setPen( defaultPen );
0773             }
0774         }
0775     }
0776 
0777     for ( int i = 0; i < innerRings.size(); ++i ) {
0778         for ( int j = 0; j < innerRings.at(i).size(); ++j ) {
0779             if ( m_innerNodesList.at(i).at(j).isBeingMerged() ) {
0780                 painter->setBrush( mergedColor );
0781                 painter->drawEllipse( innerRings.at(i).at(j), d_mergedDim, d_mergedDim );
0782             } else if ( m_innerNodesList.at(i).at(j).isSelected() ) {
0783                 painter->setBrush( selectedColor );
0784                 painter->drawEllipse( innerRings.at(i).at(j), d_selectedDim, d_selectedDim );
0785 
0786                 if ( m_innerNodesList.at(i).at(j).isEditingHighlighted() ||
0787                      m_innerNodesList.at(i).at(j).isMergingHighlighted() ) {
0788                     QPen defaultPen = painter->pen();
0789                     QPen newPen;
0790                     newPen.setWidth( defaultPen.width() + 3 );
0791                     newPen.setColor( glowColor );
0792 
0793                     painter->setBrush( Qt::NoBrush );
0794                     painter->setPen( newPen );
0795                     painter->drawEllipse( innerRings.at(i).at(j), d_selectedDim + 2, d_selectedDim + 2 );
0796                     painter->setPen( defaultPen );
0797                 }
0798             } else {
0799                 painter->setBrush( regularColor );
0800                 painter->drawEllipse( innerRings.at(i).at(j), d_regularDim, d_regularDim );
0801 
0802                 if ( m_innerNodesList.at(i).at(j).isEditingHighlighted() ||
0803                      m_innerNodesList.at(i).at(j).isMergingHighlighted() ) {
0804                     QPen defaultPen = painter->pen();
0805                     QPen newPen;
0806                     newPen.setWidth( defaultPen.width() + 3 );
0807                     newPen.setColor( glowColor );
0808 
0809                     painter->setBrush( Qt::NoBrush );
0810                     painter->setPen( newPen );
0811                     painter->drawEllipse( innerRings.at(i).at(j), d_regularDim + 2, d_regularDim + 2 );
0812                     painter->setPen( defaultPen );
0813                 }
0814             }
0815         }
0816     }
0817 
0818     if ( m_virtualHovered != QPair<int, int>( -1, -1 ) ) {
0819         const int i = m_virtualHovered.first;
0820         const int j = m_virtualHovered.second;
0821 
0822         painter->setBrush( hoveredColor );
0823 
0824         if ( i != -1 && j == -1 ) {
0825             const GeoDataCoordinates coords = i ?
0826                                               outerRing.at(i).interpolate( outerRing.at(i - 1), 0.5 ) :
0827                                               outerRing.first().interpolate( outerRing.last(), 0.5 );
0828             painter->drawEllipse( coords, d_hoveredDim, d_hoveredDim );
0829         } else {
0830             Q_ASSERT( i != -1 && j != -1 );
0831 
0832             const GeoDataCoordinates coords = j ?
0833                                 innerRings.at(i).at(j).interpolate( innerRings.at(i).at(j - 1), 0.5 ) :
0834                                 innerRings.at(i).first().interpolate( innerRings.at(i).last(), 0.5 );
0835             painter->drawEllipse( coords, d_hoveredDim, d_hoveredDim );
0836         }
0837     }
0838 }
0839 
0840 int AreaAnnotation::outerNodeContains( const QPoint &point ) const
0841 {
0842     if ( !hasFocus() ) {
0843         return -1;
0844     }
0845 
0846     for ( int i = 0; i < m_outerNodesList.size(); ++i ) {
0847         if ( m_outerNodesList.at(i).containsPoint( point ) ) {
0848             return i;
0849         }
0850     }
0851 
0852     return -1;
0853 }
0854 
0855 QPair<int, int> AreaAnnotation::innerNodeContains( const QPoint &point ) const
0856 {
0857     if ( !hasFocus() ) {
0858         return QPair<int, int>( -1, -1 );
0859     }
0860 
0861     for ( int i = 0; i < m_innerNodesList.size(); ++i ) {
0862         for ( int j = 0; j < m_innerNodesList.at(i).size(); ++j ) {
0863             if ( m_innerNodesList.at(i).at(j).containsPoint( point ) ) {
0864                 return QPair<int, int>( i, j );
0865             }
0866         }
0867     }
0868 
0869     return QPair<int, int>( -1, -1 );
0870 }
0871 
0872 QPair<int, int> AreaAnnotation::virtualNodeContains( const QPoint &point ) const
0873 {
0874     if ( !hasFocus() ) {
0875         return QPair<int, int>( -1, -1 );
0876     }
0877 
0878     for ( int i = 0; i < m_outerVirtualNodes.size(); ++i ) {
0879         if ( m_outerVirtualNodes.at(i).containsPoint( point ) ) {
0880             return QPair<int, int>( i, -1 );
0881         }
0882     }
0883 
0884     for ( int i = 0; i < m_innerVirtualNodes.size(); ++i ) {
0885         for ( int j = 0; j < m_innerVirtualNodes.at(i).size(); ++j ) {
0886             if ( m_innerVirtualNodes.at(i).at(j).containsPoint( point ) ) {
0887                 return QPair<int, int>( i, j );
0888             }
0889         }
0890     }
0891 
0892     return QPair<int, int>( -1, -1 );
0893 }
0894 
0895 int AreaAnnotation::innerBoundsContain( const QPoint &point ) const
0896 {
0897     // There are no inner boundaries.
0898     if ( m_boundariesList.size() == 1 ) {
0899         return -1;
0900     }
0901 
0902     // Starting from 1 because on index 0 is stored the region representing the whole polygon.
0903     for ( int i = 1; i < m_boundariesList.size(); ++i ) {
0904         if ( m_boundariesList.at(i).contains( point ) ) {
0905             return i;
0906         }
0907     }
0908 
0909     return -1;
0910 }
0911 
0912 bool AreaAnnotation::polygonContains( const QPoint &point ) const
0913 {
0914     return m_boundariesList.first().contains( point );
0915 }
0916 
0917 bool AreaAnnotation::processEditingOnPress( QMouseEvent *mouseEvent )
0918 {
0919     if ( mouseEvent->button() != Qt::LeftButton && mouseEvent->button() != Qt::RightButton ) {
0920         return false;
0921     }
0922 
0923     qreal lat, lon;
0924     m_viewport->geoCoordinates( mouseEvent->pos().x(),
0925                                 mouseEvent->pos().y(),
0926                                 lon, lat,
0927                                 GeoDataCoordinates::Radian );
0928     m_movedPointCoords.set( lon, lat );
0929 
0930     // First check if one of the nodes from outer boundary has been clicked.
0931     const int outerIndex = outerNodeContains( mouseEvent->pos() );
0932     if ( outerIndex != -1 ) {
0933         m_clickedNodeIndexes = QPair<int, int>( outerIndex, -1 );
0934 
0935         if ( mouseEvent->button() == Qt::RightButton ) {
0936             setRequest( SceneGraphicsItem::ShowNodeRmbMenu );
0937         } else {
0938             m_interactingObj = InteractingNode;
0939         }
0940 
0941         return true;
0942     }
0943 
0944     // Then check if one of the nodes which form an inner boundary has been clicked.
0945     const QPair<int, int> innerIndexes = innerNodeContains( mouseEvent->pos() );
0946     if ( innerIndexes.first != -1 && innerIndexes.second != -1 ) {
0947         m_clickedNodeIndexes = innerIndexes;
0948 
0949         if ( mouseEvent->button() == Qt::RightButton ) {
0950             setRequest( SceneGraphicsItem::ShowNodeRmbMenu );
0951         } else {
0952             m_interactingObj = InteractingNode;
0953         }
0954         return true;
0955     }
0956 
0957     // If neither outer boundary nodes nor inner boundary nodes contain the event position,
0958     // then check if the interior of the polygon (excepting its 'holes') contains this point.
0959     if ( polygonContains( mouseEvent->pos() ) &&
0960          innerBoundsContain( mouseEvent->pos() ) == -1 ) {
0961         if ( mouseEvent->button() == Qt::RightButton ) {
0962             setRequest( SceneGraphicsItem::ShowPolygonRmbMenu );
0963         } else {
0964             m_interactingObj = InteractingPolygon;
0965         }
0966         return true;
0967     }
0968 
0969     return false;
0970 }
0971 
0972 bool AreaAnnotation::processEditingOnMove( QMouseEvent *mouseEvent )
0973 {
0974    if ( !m_viewport ) {
0975         return false;
0976     }
0977 
0978     qreal lon, lat;
0979     m_viewport->geoCoordinates( mouseEvent->pos().x(),
0980                                 mouseEvent->pos().y(),
0981                                 lon, lat,
0982                                 GeoDataCoordinates::Radian );
0983     const GeoDataCoordinates newCoords( lon, lat );
0984 
0985     const qreal deltaLat = lat - m_movedPointCoords.latitude();
0986     const qreal deltaLon = lon - m_movedPointCoords.longitude();
0987 
0988     if ( m_interactingObj == InteractingNode ) {
0989         GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
0990         GeoDataLinearRing &outerRing = polygon->outerBoundary();
0991         QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
0992         OsmPlacemarkData *osmData = nullptr;
0993         if ( placemark()->hasOsmData() ) {
0994             osmData = &placemark()->osmData();
0995         }
0996 
0997         const int i = m_clickedNodeIndexes.first;
0998         const int j = m_clickedNodeIndexes.second;
0999 
1000         if ( j == -1 ) {
1001             // Keeping the osmPlacemarkData synchronized with the geometry
1002             if ( osmData ) {
1003                  osmData->memberReference( -1 ).changeNodeReference( outerRing.at( i ), newCoords );
1004             }
1005             outerRing[i] = newCoords;
1006         } else {
1007             Q_ASSERT( i != -1 && j != -1 );
1008             if ( osmData ) {
1009                  osmData->memberReference( i ).changeNodeReference( innerRings.at( i ).at( j ), newCoords );
1010             }
1011             innerRings[i].at(j) = newCoords;
1012         }
1013 
1014         return true;
1015     } else if ( m_interactingObj == InteractingPolygon ) {
1016         GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
1017         GeoDataLinearRing outerRing = polygon->outerBoundary();
1018         QVector<GeoDataLinearRing> innerRings = polygon->innerBoundaries();
1019         OsmPlacemarkData *osmData = nullptr;
1020         if ( placemark()->hasOsmData() ) {
1021             osmData = &placemark()->osmData();
1022         }
1023 
1024         Quaternion latRectAxis = Quaternion::fromEuler( 0, lon, 0);
1025         Quaternion latAxis = Quaternion::fromEuler( -deltaLat, 0, 0);
1026         Quaternion lonAxis = Quaternion::fromEuler(0, deltaLon, 0);
1027         Quaternion rotAxis = latRectAxis * latAxis * latRectAxis.inverse() * lonAxis;
1028 
1029 
1030         polygon->outerBoundary().clear();
1031         polygon->innerBoundaries().clear();
1032 
1033         for ( int i = 0; i < outerRing.size(); ++i ) {
1034             const GeoDataCoordinates movedPoint = outerRing.at(i).rotateAround(rotAxis);
1035             if ( osmData ) {
1036                 osmData->memberReference( -1 ).changeNodeReference( outerRing.at( i ), movedPoint );
1037             }
1038             polygon->outerBoundary().append( movedPoint );
1039         }
1040 
1041         for ( int i = 0; i < innerRings.size(); ++i ) {
1042             GeoDataLinearRing newRing( Tessellate );
1043             for ( int j = 0; j < innerRings.at(i).size(); ++j ) {
1044                 const GeoDataCoordinates movedPoint = innerRings.at(i).at(j).rotateAround(rotAxis);
1045                 if ( osmData ) {
1046                     osmData->memberReference( i ).changeNodeReference( innerRings.at( i ).at( j ) , movedPoint );
1047                 }
1048                 newRing.append( movedPoint );
1049             }
1050             polygon->innerBoundaries().append( newRing );
1051         }
1052 
1053         m_movedPointCoords = newCoords;
1054         return true;
1055     } else if ( m_interactingObj == InteractingNothing ) {
1056         return dealWithHovering( mouseEvent );
1057     }
1058 
1059     return false;
1060 }
1061 
1062 bool AreaAnnotation::processEditingOnRelease( QMouseEvent *mouseEvent )
1063 {
1064     static const int mouseMoveOffset = 1;
1065 
1066     if ( mouseEvent->button() != Qt::LeftButton ) {
1067         return false;
1068     }
1069 
1070     if ( m_interactingObj == InteractingNode ) {
1071         qreal x, y;
1072         m_viewport->screenCoordinates( m_movedPointCoords.longitude(),
1073                                        m_movedPointCoords.latitude(),
1074                                        x, y );
1075         // The node gets selected only if it is clicked and not moved.
1076         if ( qFabs(mouseEvent->pos().x() - x) > mouseMoveOffset ||
1077              qFabs(mouseEvent->pos().y() - y) > mouseMoveOffset ) {
1078             m_interactingObj = InteractingNothing;
1079             return true;
1080         }
1081 
1082         const int i = m_clickedNodeIndexes.first;
1083         const int j = m_clickedNodeIndexes.second;
1084 
1085         if ( j == -1 ) {
1086             m_outerNodesList[i].setFlag( PolylineNode::NodeIsSelected,
1087                                          !m_outerNodesList[i].isSelected() );
1088         } else {
1089             m_innerNodesList[i][j].setFlag ( PolylineNode::NodeIsSelected,
1090                                              !m_innerNodesList.at(i).at(j).isSelected() );
1091         }
1092 
1093         m_interactingObj = InteractingNothing;
1094         return true;
1095     } else if ( m_interactingObj == InteractingPolygon ) {
1096         // Nothing special happens at polygon release.
1097         m_interactingObj = InteractingNothing;
1098         return true;
1099     }
1100 
1101     return false;
1102 }
1103 
1104 bool AreaAnnotation::processAddingHoleOnPress( QMouseEvent *mouseEvent )
1105 {
1106     if ( mouseEvent->button() != Qt::LeftButton ) {
1107         return false;
1108     }
1109 
1110     qreal lon, lat;
1111     m_viewport->geoCoordinates( mouseEvent->pos().x(),
1112                                     mouseEvent->pos().y(),
1113                                     lon, lat,
1114                                     GeoDataCoordinates::Radian );
1115     const GeoDataCoordinates newCoords( lon, lat );
1116 
1117     GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
1118     QVector<GeoDataLinearRing> &innerBounds = polygon->innerBoundaries();
1119 
1120     innerBounds.last().append( newCoords );
1121     m_innerNodesList.last().append( PolylineNode() );
1122 
1123     return true;
1124 }
1125 
1126 bool AreaAnnotation::processAddingHoleOnMove( QMouseEvent *mouseEvent )
1127 {
1128     Q_UNUSED( mouseEvent );
1129     return true;
1130 }
1131 
1132 bool AreaAnnotation::processAddingHoleOnRelease( QMouseEvent *mouseEvent )
1133 {
1134     Q_UNUSED( mouseEvent );
1135     return true;
1136 }
1137 
1138 bool AreaAnnotation::processMergingOnPress( QMouseEvent *mouseEvent )
1139 {
1140     if ( mouseEvent->button() != Qt::LeftButton ) {
1141         return false;
1142     }
1143 
1144     GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
1145     GeoDataLinearRing initialOuterRing = polygon->outerBoundary();
1146     OsmPlacemarkData *osmData = nullptr;
1147     OsmPlacemarkData initialOsmData;
1148     if ( placemark()->hasOsmData() ) {
1149         osmData = &placemark()->osmData();
1150     }
1151 
1152     GeoDataLinearRing &outerRing = polygon->outerBoundary();
1153     QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
1154 
1155     const int outerIndex = outerNodeContains( mouseEvent->pos() );
1156     // If the selected node is an outer boundary node.
1157     if ( outerIndex != -1 ) {
1158         // If this is the first node selected to be merged.
1159         if ( m_firstMergedNode.first == -1 && m_firstMergedNode.second == -1 ) {
1160             m_firstMergedNode = QPair<int, int>( outerIndex, -1 );
1161             m_outerNodesList[outerIndex].setFlag( PolylineNode::NodeIsMerged );
1162         // If the first selected node was an inner boundary node, raise the request for showing
1163         // warning.
1164         } else if ( m_firstMergedNode.first != -1 && m_firstMergedNode.second != -1 ) {
1165             setRequest( SceneGraphicsItem::OuterInnerMergingWarning );
1166             m_innerNodesList[m_firstMergedNode.first][m_firstMergedNode.second].setFlag(
1167                                                         PolylineNode::NodeIsMerged, false );
1168 
1169             if ( m_hoveredNode.first != -1 ) {
1170                 // We can be sure that the hovered node is an outer node.
1171                 Q_ASSERT( m_hoveredNode.second == -1 );
1172                 m_outerNodesList[m_hoveredNode.first].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
1173             }
1174 
1175             m_hoveredNode = m_firstMergedNode = QPair<int, int>( -1, -1 );
1176         } else {
1177             Q_ASSERT( m_firstMergedNode.first != -1 && m_firstMergedNode.second == -1 );
1178 
1179             // Clicking two times the same node results in unmarking it for merging.
1180             if ( m_firstMergedNode.first == outerIndex ) {
1181                 m_outerNodesList[outerIndex].setFlag( PolylineNode::NodeIsMerged, false );
1182                 m_firstMergedNode = QPair<int, int>( -1, -1 );
1183                 return true;
1184             }
1185 
1186             // If two nodes which form a triangle are merged, the whole triangle should be
1187             // destroyed.
1188             if ( outerRing.size() <= 3 ) {
1189                 setRequest( SceneGraphicsItem::RemovePolygonRequest );
1190                 return true;
1191             }
1192             GeoDataCoordinates mergedNode = outerRing.at(m_firstMergedNode.first).interpolate( outerRing.at(outerIndex),
1193                                                                                                                  0.5 );
1194             // Keeping the osm data synchronized with the geometry
1195             if ( osmData ) {
1196                 osmData->memberReference( -1 ).changeNodeReference( outerRing.at( outerIndex ), mergedNode );
1197                 osmData->memberReference( -1 ).removeNodeReference( outerRing.at( m_firstMergedNode.first ) );
1198             }
1199 
1200             outerRing[outerIndex] = mergedNode;
1201             outerRing.remove( m_firstMergedNode.first );
1202             if ( !isValidPolygon() ) {
1203                 if ( osmData ) {
1204                     placemark()->setOsmData( initialOsmData );
1205                 }
1206                 polygon->outerBoundary() = initialOuterRing;
1207                 m_outerNodesList[m_firstMergedNode.first].setFlag( PolylineNode::NodeIsMerged,  false );
1208 
1209                 // Remove highlight effect before showing warning
1210                 if ( m_hoveredNode.first != -1 ) {
1211                     m_outerNodesList[m_hoveredNode.first].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
1212                 }
1213 
1214                 m_hoveredNode = m_firstMergedNode = QPair<int, int>( -1, -1 );
1215                 setRequest( SceneGraphicsItem::InvalidShapeWarning );
1216                 return true;
1217             }
1218 
1219             // Do not modify it here. The animation has access to the object. It will modify the polygon.
1220             polygon->outerBoundary() = initialOuterRing;
1221 
1222             m_outerNodesList[outerIndex].setFlag( PolylineNode::NodeIsMerged );
1223             m_secondMergedNode = QPair<int, int>( outerIndex, -1 );
1224 
1225             delete m_animation;
1226             m_animation = new MergingPolygonNodesAnimation( this );
1227             setRequest( SceneGraphicsItem::StartPolygonAnimation );
1228         }
1229 
1230         return true;
1231     }
1232 
1233     // If the selected node is an inner boundary node.
1234     const QPair<int, int> innerIndexes = innerNodeContains( mouseEvent->pos() );
1235     if ( innerIndexes.first != -1 && innerIndexes.second != -1 ) {
1236         const int i = m_firstMergedNode.first;
1237         const int j = m_firstMergedNode.second;
1238 
1239         // If this is the first selected node.
1240         if ( i == -1 && j == -1 ) {
1241             m_firstMergedNode = innerIndexes;
1242             m_innerNodesList[innerIndexes.first][innerIndexes.second].setFlag( PolylineNode::NodeIsMerged );
1243         // If the first selected node has been an outer boundary one, raise the request for showing warning.
1244         } else if ( i != -1 && j == -1 ) {
1245             setRequest( SceneGraphicsItem::OuterInnerMergingWarning );
1246             m_outerNodesList[i].setFlag( PolylineNode::NodeIsMerged, false );
1247 
1248             if ( m_hoveredNode.first != -1 ) {
1249                 // We can now be sure that the highlighted node is a node from polygon's outer boundary
1250                 Q_ASSERT( m_hoveredNode.second != -1 );
1251                 m_outerNodesList[m_hoveredNode.first].setFlag( PolylineNode::NodeIsMergingHighlighted, false );
1252             }
1253             m_firstMergedNode = QPair<int, int>( -1, -1 );
1254         } else {
1255             Q_ASSERT( i != -1 && j != -1 );
1256             if ( i != innerIndexes.first ) {
1257                 setRequest( SceneGraphicsItem::InnerInnerMergingWarning );
1258                 m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsMerged, false );
1259 
1260                 if ( m_hoveredNode.first != -1 && m_hoveredNode.second != -1 ) {
1261                     m_innerNodesList[m_hoveredNode.first][m_hoveredNode.second].setFlag(
1262                                                         PolylineNode::NodeIsMergingHighlighted, false );
1263                 }
1264 
1265                 m_hoveredNode = m_firstMergedNode = QPair<int, int>( -1, -1 );
1266                 return true;
1267             }
1268 
1269             // Clicking two times the same node results in unmarking it for merging.
1270             if ( m_firstMergedNode == innerIndexes ) {
1271                 m_innerNodesList[i][j].setFlag( PolylineNode::NodeIsMerged, false );
1272                 m_firstMergedNode = QPair<int, int>( -1, -1 );
1273                 return true;
1274             }
1275 
1276             // If two nodes which form an inner boundary of a polygon with a size smaller than
1277             // 3 are merged, remove the whole inner boundary.
1278             if ( innerRings.at(i).size() <= 3 ) {
1279                 innerRings.remove( i );
1280                 m_innerNodesList.removeAt( i );
1281 
1282                 m_firstMergedNode = m_secondMergedNode = m_hoveredNode = QPair<int, int>( -1, -1 );
1283                 return true;
1284             }
1285 
1286             m_innerNodesList[innerIndexes.first][innerIndexes.second].setFlag( PolylineNode::NodeIsMerged );
1287             m_secondMergedNode = innerIndexes;
1288 
1289             m_animation = new MergingPolygonNodesAnimation( this );
1290             setRequest( SceneGraphicsItem::StartPolygonAnimation );
1291         }
1292 
1293         return true;
1294     }
1295 
1296     return false;
1297 }
1298 
1299 bool AreaAnnotation::processMergingOnMove( QMouseEvent *mouseEvent )
1300 {
1301     return dealWithHovering( mouseEvent );
1302 }
1303 
1304 bool AreaAnnotation::processMergingOnRelease( QMouseEvent *mouseEvent )
1305 {
1306     Q_UNUSED( mouseEvent );
1307     return true;
1308 }
1309 
1310 bool AreaAnnotation::processAddingNodesOnPress( QMouseEvent *mouseEvent )
1311 {
1312     if ( mouseEvent->button() != Qt::LeftButton ) {
1313         return false;
1314     }
1315 
1316     GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
1317     GeoDataLinearRing &outerRing = polygon->outerBoundary();
1318     QVector<GeoDataLinearRing> &innerRings = polygon->innerBoundaries();
1319 
1320     // If a virtual node has just been clicked, add it to the polygon's outer boundary
1321     // and start 'adjusting' its position.
1322     const QPair<int, int> index = virtualNodeContains( mouseEvent->pos() );
1323     if ( index != QPair<int, int>( -1, -1 ) && m_adjustedNode == -2 ) {
1324         Q_ASSERT( m_virtualHovered == index );
1325         const int i = index.first;
1326         const int j = index.second;
1327 
1328         if ( i != -1 && j == -1 ) {
1329             GeoDataLinearRing newRing( Tessellate );
1330             QVector<PolylineNode> newList;
1331             newList.reserve(outerRing.size());
1332             for ( int k = i; k < i + outerRing.size(); ++k ) {
1333                 newRing.append( outerRing.at(k % outerRing.size()) );
1334 
1335                 PolylineNode newNode;
1336                 newNode.setFlags( m_outerNodesList.at(k % outerRing.size()).flags() );
1337                 newList.append( newNode );
1338             }
1339             GeoDataCoordinates newCoords = newRing.first().interpolate( newRing.last(), 0.5 );
1340             newRing.append( newCoords );
1341 
1342             m_outerNodesList = newList;
1343             m_outerNodesList.append( PolylineNode( QRegion() ) );
1344 
1345             polygon->outerBoundary() = newRing;
1346             m_adjustedNode = -1;
1347         } else {
1348             Q_ASSERT( i != -1 && j != -1 );
1349 
1350             GeoDataLinearRing newRing( Tessellate );
1351             QVector<PolylineNode> newList;
1352             newList.reserve(innerRings.at(i).size());
1353             for ( int k = j; k < j + innerRings.at(i).size(); ++k ) {
1354                 newRing.append( innerRings.at(i).at(k % innerRings.at(i).size()) );
1355 
1356                 PolylineNode newNode;
1357                 newNode.setFlags( m_innerNodesList.at(i).at(k % innerRings.at(i).size()).flags() );
1358                 newList.append( newNode );
1359             }
1360             GeoDataCoordinates newCoords = newRing.first().interpolate( newRing.last(), 0.5 );
1361             newRing.append( newCoords );
1362 
1363             m_innerNodesList[i] = newList;
1364             m_innerNodesList[i].append( PolylineNode( QRegion() ) );
1365 
1366             polygon->innerBoundaries()[i] = newRing;
1367             m_adjustedNode = i;
1368         }
1369 
1370         m_virtualHovered = QPair<int, int>( -1, -1 );
1371         return true;
1372     }
1373 
1374     // If a virtual node which has been previously clicked and selected to become a
1375     // 'real node' is clicked one more time, it stops from being 'adjusted'.
1376     const int outerIndex = outerNodeContains( mouseEvent->pos() );
1377     if ( outerIndex != -1 && m_adjustedNode != -2 ) {
1378         m_adjustedNode = -2;
1379         return true;
1380     }
1381 
1382     const QPair<int,int> innerIndex = innerNodeContains( mouseEvent->pos() );
1383     if ( innerIndex != QPair<int, int>( -1, -1 ) && m_adjustedNode != -2 ) {
1384         m_adjustedNode = -2;
1385         return true;
1386     }
1387 
1388     return false;
1389 }
1390 
1391 bool AreaAnnotation::processAddingNodesOnMove( QMouseEvent *mouseEvent )
1392 {
1393     Q_ASSERT( mouseEvent->button() == Qt::NoButton );
1394 
1395     const QPair<int, int> index = virtualNodeContains( mouseEvent->pos() );
1396     // If we are adjusting a virtual node which has just been clicked and became real, just
1397     // change its coordinates when moving it, as we do with nodes in Editing state on move.
1398     if ( m_adjustedNode != -2 ) {
1399         // The virtual node which has just been added is always the last within
1400         // GeoDataLinearRing's container.qreal lon, lat;
1401         qreal lon, lat;
1402         m_viewport->geoCoordinates( mouseEvent->pos().x(),
1403                                     mouseEvent->pos().y(),
1404                                     lon, lat,
1405                                     GeoDataCoordinates::Radian );
1406         const GeoDataCoordinates newCoords( lon, lat );
1407         GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( placemark()->geometry() );
1408 
1409         if ( m_adjustedNode == -1 ) {
1410             polygon->outerBoundary().last() = newCoords;
1411         } else {
1412             Q_ASSERT( m_adjustedNode >= 0 );
1413             polygon->innerBoundaries()[m_adjustedNode].last() = newCoords;
1414         }
1415 
1416         return true;
1417 
1418     // If we are hovering a virtual node, store its index in order to be painted in drawNodes
1419     // method.
1420     } else if ( index != QPair<int, int>( -1, -1 ) ) {
1421         m_virtualHovered = index;
1422         return true;
1423     }
1424 
1425     // This means that the interior of the polygon has been hovered. Let the event propagate
1426     // since there may be overlapping polygons.
1427     return false;
1428 }
1429 
1430 bool AreaAnnotation::processAddingNodesOnRelease( QMouseEvent *mouseEvent )
1431 {
1432     Q_UNUSED( mouseEvent );
1433     return m_adjustedNode == -2;
1434 }
1435 
1436 bool AreaAnnotation::dealWithHovering( QMouseEvent *mouseEvent )
1437 {
1438     const PolylineNode::PolyNodeFlag flag = state() == SceneGraphicsItem::Editing ?
1439                                                        PolylineNode::NodeIsEditingHighlighted :
1440                                                        PolylineNode::NodeIsMergingHighlighted;
1441 
1442     const int outerIndex = outerNodeContains( mouseEvent->pos() );
1443     if ( outerIndex != -1 ) {
1444         if ( !m_outerNodesList.at(outerIndex).isEditingHighlighted() &&
1445              !m_outerNodesList.at(outerIndex).isMergingHighlighted() ) {
1446             // Deal with the case when two nodes are very close to each other.
1447             if ( m_hoveredNode != QPair<int, int>( -1, -1 ) ) {
1448                 const int i = m_hoveredNode.first;
1449                 const int j = m_hoveredNode.second;
1450 
1451                 if ( j == -1 ) {
1452                     m_outerNodesList[i].setFlag( flag, false );
1453                 } else {
1454                     m_innerNodesList[i][j].setFlag( flag, false );
1455                 }
1456             }
1457 
1458             m_hoveredNode = QPair<int, int>( outerIndex, -1 );
1459             m_outerNodesList[outerIndex].setFlag( flag );
1460             setRequest( ChangeCursorPolygonNodeHover );
1461         }
1462 
1463         return true;
1464     } else if ( m_hoveredNode != QPair<int, int>( -1, -1 ) && m_hoveredNode.second == -1 ) {
1465         m_outerNodesList[m_hoveredNode.first].setFlag( flag, false );
1466         m_hoveredNode = QPair<int, int>( -1, -1 );
1467 
1468         return true;
1469     }
1470 
1471     const QPair<int, int> innerIndex = innerNodeContains( mouseEvent->pos() );
1472     if ( innerIndex != QPair<int, int>( -1, -1 ) ) {
1473         if ( !m_innerNodesList.at(innerIndex.first).at(innerIndex.second).isEditingHighlighted() &&
1474              !m_innerNodesList.at(innerIndex.first).at(innerIndex.second).isMergingHighlighted()) {
1475             // Deal with the case when two nodes are very close to each other.
1476             if ( m_hoveredNode != QPair<int, int>( -1, -1 ) ) {
1477                 const int i = m_hoveredNode.first;
1478                 const int j = m_hoveredNode.second;
1479 
1480                 if ( j == -1 ) {
1481                     m_outerNodesList[i].setFlag( flag, false );
1482                 } else {
1483                     m_innerNodesList[i][j].setFlag( flag, false );
1484                 }
1485             }
1486 
1487             m_hoveredNode = innerIndex;
1488             m_innerNodesList[innerIndex.first][innerIndex.second].setFlag( flag );
1489             setRequest( ChangeCursorPolygonNodeHover );
1490         }
1491 
1492         return true;
1493     } else if ( m_hoveredNode != QPair<int, int>( -1, -1 ) && m_hoveredNode.second != -1 ) {
1494         m_innerNodesList[m_hoveredNode.first][m_hoveredNode.second].setFlag( flag, false );
1495         m_hoveredNode = QPair<int, int>( -1, -1 );
1496 
1497         return true;
1498     }
1499 
1500     // This means that the interior of the polygon has been covered so we catch this event too.
1501     setRequest( ChangeCursorPolygonBodyHover );
1502     return true;
1503 }
1504 
1505 }