Hi,

I have a patch that will get a more similar selection and highlight of the placemarks like google earth and other Virtual globes where one select the lines instead of selection inside the polygon. Will allow for selection of linestrings, linerings, and polygons.

Regards,
Lars


diff --git a/src/lib/marble/layers/GeometryLayer.cpp b/src/lib/marble/layers/GeometryLayer.cpp
index c7acdea..f4eb745 100644
--- a/src/lib/marble/layers/GeometryLayer.cpp
+++ b/src/lib/marble/layers/GeometryLayer.cpp
@@ -50,6 +50,158 @@
 #include <QAbstractItemModel>
 #include <QModelIndex>
 #include <QColor>
+#include <QVector>
+
+namespace
+{
+
+const double EPSILON = 0.001;
+
+double
+perpDotProduct(const QPointF &v1, const QPointF &v2, const QPointF &p)
+{
+    double distance = (v1.x() - p.x()) * (v2.y() - p.y()) - (v1.y() - p.y()) * (v2.x() - p.x());
+    qDebug() << "perpDotProduct" << QString::number(distance, 'f', 20);
+    return distance;
+}
+
+bool
+isPointOnLine(const QPointF &v1, const QPointF &v2, const QPointF &p)
+{
+    if (!( (v1.x() <= p.x() && p.x() <= v2.x()) || (v2.x() <= p.x() && p.x() <= v1.x()) ))
+    {
+      // test point not in x-range
+      return false;
+    }
+    if (!( (v1.y() <= p.y() && p.y() <= v2.y()) || (v2.y() <= p.y() && p.y() <= v1.y()) ))
+    {
+      // test point not in y-range
+      return false;
+    }
+
+    return ( qAbs(perpDotProduct(v1, v2, p)) < EPSILON );
+}
+
+QPointF
+getPointDegrees(const Marble::GeoDataCoordinates &coord)
+{
+    double lat;
+    double lon;
+    coord.geoCoordinates(lon, lat, Marble::GeoDataCoordinates::Degree);
+
+    return QPointF(lon, lat);
+}
+
+bool
+checkOnLine(const QVector<Marble::GeoDataLineString> &lineStrings, const Marble::GeoDataCoordinates &coordinate)
+{
+    QPointF pos = getPointDegrees(coordinate);
+
+    foreach(const Marble::GeoDataLineString &lineString, lineStrings)
+    {
+        int size = lineString.size();
+        if(size == 1)
+        {
+            QPointF v1 = getPointDegrees(lineString.at(0));
+
+            double dx1 = pos.x() - v1.x();
+            double dy1 = pos.y() - v1.y();
+
+            if(dx1 * dx1 + dy1 * dy1 < EPSILON)
+            {
+                return true;
+            }
+        }
+        else
+        {
+            for(int i = 1; i < size; ++i)
+            {
+                QPointF v1 = getPointDegrees(lineString.at(i-1));
+                QPointF v2 = getPointDegrees(lineString.at(i));
+
+                bool found = isPointOnLine(v1, v2, pos);
+                if(found)
+                {
+                    qDebug() << "found" << i << v1 << v2 << pos;
+                    return true;
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+bool
+hitPlacemark(Marble::GeoDataPlacemark *placemark, Marble::GeoDataCoordinates &clickedPoint)
+{
+    /* Do not handle empty placemarks */
+    if(placemark->name().isEmpty())
+    {
+        return false;
+    }
+
+    Marble::GeoDataPolygon *polygon = dynamic_cast<Marble::GeoDataPolygon*>( placemark->geometry() );
+    Marble::GeoDataLineString *lineString = dynamic_cast<Marble::GeoDataLineString*>( placemark->geometry() );
+    Marble::GeoDataMultiGeometry *multiGeometry = dynamic_cast<Marble::GeoDataMultiGeometry*>(placemark->geometry() );
+
+    qDebug() << "GeometryLayer::whichFeatureAt iterate" << placemark << polygon << lineString << multiGeometry;
+
+    if ( polygon )
+    {
+
+        QVector<Marble::GeoDataLineString> boundaries;
+        boundaries.append(polygon->outerBoundary());
+        foreach(const Marble::GeoDataLinearRing &ring, polygon->innerBoundaries())
+        {
+            boundaries.append(ring);
+        }
+
+        if ( checkOnLine(boundaries, clickedPoint))
+        {
+            return true;
+        }
+    }
+
+    if ( lineString && checkOnLine(QVector<Marble::GeoDataLineString>() << *lineString, clickedPoint))
+    {
+        return true;
+    }
+
+    if ( multiGeometry ) {
+        QVector<Marble::GeoDataGeometry*>::Iterator multiIter = multiGeometry->begin();
+        QVector<Marble::GeoDataGeometry*>::Iterator const multiEnd = multiGeometry->end();
+
+        for ( ; multiIter != multiEnd; ++multiIter ) {
+            Marble::GeoDataPolygon *polygon = dynamic_cast<Marble::GeoDataPolygon*>( *multiIter );
+            Marble::GeoDataLineString *lineString = dynamic_cast<Marble::GeoDataLineString*>( *multiIter );
+
+            if ( polygon )
+            {
+                QVector<Marble::GeoDataLineString> boundaries;
+                boundaries.append(polygon->outerBoundary());
+                foreach(const Marble::GeoDataLinearRing &ring, polygon->innerBoundaries())
+                {
+                    boundaries.append(ring);
+                }
+
+                if(checkOnLine(boundaries, clickedPoint))
+                {
+                    return true;
+                }
+            }
+
+            if ( lineString && checkOnLine(QVector<Marble::GeoDataLineString>() << *lineString, clickedPoint))
+            {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+} //namespace
 
 namespace Marble
 {
@@ -572,8 +724,15 @@ QVector<const GeoDataFeature*> GeometryLayer::whichFeatureAt(const QPoint& curpo
 {
     QVector<const GeoDataFeature*> result;
     int maxZoom = qMin<int>( qMax<int>( qLn( viewport->radius() *4 / 256 ) / qLn( 2.0 ), 1), GeometryLayerPrivate::maximumZoomLevel() );
-    foreach ( GeoGraphicsItem * item, d->m_scene.items( viewport->viewLatLonAltBox(), maxZoom ) ) {
-        if ( item->feature()->nodeType() == GeoDataTypes::GeoDataPhotoOverlayType ) {
+    double lon;
+    double lat;
+    viewport->geoCoordinates(curpos.x(), curpos.y(), lon, lat, GeoDataCoordinates::Radian);
+    GeoDataCoordinates clickedPoint(lon, lat, 0, GeoDataCoordinates::Radian);
+
+    foreach ( GeoGraphicsItem * item, d->m_scene.items( viewport->viewLatLonAltBox(), maxZoom ) )
+    {
+        if ( item->feature()->nodeType() == GeoDataTypes::GeoDataPhotoOverlayType )
+        {
             GeoPhotoGraphicsItem* photoItem = dynamic_cast<GeoPhotoGraphicsItem*>( item );
             qreal x(0.0), y( 0.0 );
             viewport->screenCoordinates( photoItem->point().coordinates(), x, y );
@@ -594,6 +753,21 @@ QVector<const GeoDataFeature*> GeometryLayer::whichFeatureAt(const QPoint& curpo
                 result.push_back( item->feature() );
             }
         }
+        else if(item->feature()->nodeType() == GeoDataTypes::GeoDataPlacemarkType)
+        {
+            if(!item->feature()->isVisible() ||
+               !item->latLonAltBox().contains(clickedPoint) ||
+                    !item->style())
+            {
+                continue;
+            }
+
+            GeoDataPlacemark *placemark = const_cast<GeoDataPlacemark*>(static_cast<const GeoDataPlacemark*>( item->feature()));
+            if(hitPlacemark(placemark, clickedPoint))
+            {
+                result.append( item->feature());
+            }
+        }
     }
 
     return result;
@@ -611,77 +785,41 @@ void GeometryLayer::handleHighlight( qreal lon, qreal lat, GeoDataCoordinates::U
         if ( object->nodeType() == GeoDataTypes::GeoDataDocumentType ) {
             Q_ASSERT( dynamic_cast<const GeoDataDocument *>( object ) != 0 );
             GeoDataDocument* doc = static_cast<GeoDataDocument*> ( object );
-                bool isHighlight = false;
+            bool isHighlight = false;
 
-                foreach ( const GeoDataStyleMap &styleMap, doc->styleMaps() ) {
-                    if ( styleMap.contains( QString("highlight") ) ) {
-                        isHighlight = true;
-                        break;
-                    }
+            foreach ( const GeoDataStyleMap &styleMap, doc->styleMaps() ) {
+                if ( styleMap.contains( QString("highlight") ) ) {
+                    isHighlight = true;
+                    break;
                 }
+            }
 
-                /*
-                 * If a document doesn't specify any highlight
-                 * styleId in its style maps then there is no need
-                 * to further check that document for placemarks
-                 * which have been clicked because we won't
-                 * highlight them.
-                 */
-                if ( isHighlight ) {
-                    QVector<GeoDataFeature*>::Iterator iter = doc->begin();
-                    QVector<GeoDataFeature*>::Iterator const end = doc->end();
-
-                    for ( ; iter != end; ++iter ) {
-                        if ( (*iter)->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) {
-                            GeoDataPlacemark *placemark = static_cast<GeoDataPlacemark*>( *iter );
-                            GeoDataPolygon *polygon = dynamic_cast<GeoDataPolygon*>( placemark->geometry() );
-                            GeoDataLineString *lineString = dynamic_cast<GeoDataLineString*>( placemark->geometry() );
-                            GeoDataMultiGeometry *multiGeometry = dynamic_cast<GeoDataMultiGeometry*>(placemark->geometry() );
-                            if ( polygon &&
-                                polygon->contains( clickedPoint ) )
-                            {
-                                selectedPlacemarks.push_back( placemark );
-                            }
-
-                            if ( lineString &&
-                                lineString->nodeType() == GeoDataTypes::GeoDataLinearRingType )
-                            {
-                                GeoDataLinearRing *linearRing = static_cast<GeoDataLinearRing*>( lineString );
-                                if ( linearRing->contains( clickedPoint ) ) {
-                                    selectedPlacemarks.push_back( placemark );
-                                }
-                            }
-
-                            if ( multiGeometry ) {
-                                QVector<GeoDataGeometry*>::Iterator multiIter = multiGeometry->begin();
-                                QVector<GeoDataGeometry*>::Iterator const multiEnd = multiGeometry->end();
-
-                                for ( ; multiIter != multiEnd; ++multiIter ) {
-                                    GeoDataPolygon *poly = dynamic_cast<GeoDataPolygon*>( *multiIter );
-                                    GeoDataLineString *linestring = dynamic_cast<GeoDataLineString*>( *multiIter );
-
-                                    if ( poly &&
-                                        poly->contains( clickedPoint ) )
-                                    {
-                                        selectedPlacemarks.push_back( placemark );
-                                        break;
-                                    }
-
-                                    if ( linestring &&
-                                        linestring->nodeType() == GeoDataTypes::GeoDataLinearRingType )
-                                    {
-                                        GeoDataLinearRing *linearRing = static_cast<GeoDataLinearRing*>( linestring );
-                                        if ( linearRing->contains( clickedPoint ) ) {
-                                            selectedPlacemarks.push_back( placemark );
-                                            break;
-                                        }
-                                    }
-                                }
-                            }
+            qDebug() << "GeometryLayer::handleHighlight" << isHighlight << object->nodeType();
+            /*
+             * If a document doesn't specify any highlight
+             * styleId in its style maps then there is no need
+             * to further check that document for placemarks
+             * which have been clicked because we won't
+             * highlight them.
+             */
+            if ( isHighlight )
+            {
+                QVector<GeoDataFeature*>::Iterator iter = doc->begin();
+                QVector<GeoDataFeature*>::Iterator const end = doc->end();
+
+                for ( ; iter != end; ++iter )
+                {
+                    if ( (*iter)->nodeType() == GeoDataTypes::GeoDataPlacemarkType )
+                    {
+                        GeoDataPlacemark *placemark = static_cast<GeoDataPlacemark*>( *iter );
+                        if(hitPlacemark(placemark, clickedPoint))
+                        {
+                            selectedPlacemarks.push_back( placemark );
                         }
                     }
                 }
-        }
+            }
+    }
     }
 
     emit highlightedPlacemarksChanged( selectedPlacemarks );

_______________________________________________
Marble-devel mailing list
[email protected]
https://mail.kde.org/mailman/listinfo/marble-devel

Reply via email to