Finished basic map scale feature distance_scale
authorKaj Wallin <kaj.wallin@ixonos.com>
Wed, 9 Jun 2010 07:34:06 +0000 (10:34 +0300)
committerKaj Wallin <kaj.wallin@ixonos.com>
Wed, 9 Jun 2010 07:34:06 +0000 (10:34 +0300)
Reviewed by: Marko Niemelä

src/engine/engine.cpp
src/map/mapcommon.h
src/map/mapengine.cpp
src/map/mapengine.h
src/ui/mainwindow.cpp
src/ui/mainwindow.h
src/ui/mapscale.cpp
src/ui/mapscale.h
src/ui/zoombutton.h
src/ui/zoombuttonpanel.h

index 534a4c9..6078924 100644 (file)
@@ -469,6 +469,9 @@ void SituareEngine::signalsFromMapEngine()
 
     connect(m_mapEngine, SIGNAL(locationItemClicked(QList<QString>)),
             m_ui, SIGNAL(locationItemClicked(QList<QString>)));
+
+    connect(m_mapEngine, SIGNAL(newMapResolution(qreal)),
+            m_ui, SIGNAL(newMapResolution(qreal)));
 }
 
 void SituareEngine::signalsFromSituareService()
index 65ed145..2a2f070 100644 (file)
@@ -73,6 +73,8 @@ const int DEFAULT_ZOOM_LEVEL = 14;       ///< Default zoom level
 const qreal DEFAULT_LONGITUDE = 0.0000;  ///< Default longitude value
 const qreal DEFAULT_LATITUDE = 0.0000; ///< Default latitude value
 
+const qreal EARTH_RADIUS = 6371.01;         ///< Earth radius in km
+
 const int GRID_PADDING = 1;  ///< Grid padding used in tile grid calculation
 
 const QString OSM_LICENSE = QString::fromUtf8("© OpenStreetMap contributors, CC-BY-SA");
index ea55393..140e250 100644 (file)
@@ -278,6 +278,21 @@ bool MapEngine::isCenterTileChanged(QPoint sceneCoordinate)
     return (centerTile != temp);
 }
 
+qreal MapEngine::sceneResolution()
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    const int SHIFT = 200;
+    const int KM_TO_M = 1000;
+    qreal scale = (1 << (MAX_MAP_ZOOM_LEVEL - m_zoomLevel));
+    QPointF centerCoordinate = centerGeoCoordinate();
+    QPoint shiftedSceneCoordinate = QPoint(m_sceneCoordinate.x() + SHIFT*scale
+                                           , m_sceneCoordinate.y());
+    QPointF shiftedCoordinate = convertSceneCoordinateToLatLon(m_zoomLevel, shiftedSceneCoordinate);
+    qreal dist = greatCircleDistance(centerCoordinate, shiftedCoordinate) * KM_TO_M;
+    return (dist / SHIFT);
+}
+
 void MapEngine::mapImageReceived(int zoomLevel, int x, int y, const QPixmap &image)
 {
     qDebug() << __PRETTY_FUNCTION__;
@@ -296,6 +311,21 @@ void MapEngine::mapImageReceived(int zoomLevel, int x, int y, const QPixmap &ima
    }
 }
 
+qreal MapEngine::greatCircleDistance(QPointF firstLocation, QPointF secondLocation)
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    const qreal TORAD = (M_PI/180);
+
+    qreal dLat = (secondLocation.y() - firstLocation.y())*TORAD;
+    qreal dLon = (secondLocation.x() - firstLocation.x())*TORAD;
+    qreal a = pow(sin(dLat/2),2) + cos(firstLocation.y()*TORAD) * cos(secondLocation.y()*TORAD)
+              * pow(sin(dLon/2),2);
+    qreal c = 2 * atan2(sqrt(a), sqrt(1-a));
+
+    return (EARTH_RADIUS * c);
+}
+
 void MapEngine::receiveOwnLocation(User *user)
 {
     qDebug() << __PRETTY_FUNCTION__;
@@ -345,6 +375,8 @@ void MapEngine::setLocation(QPoint sceneCoordinate)
         getTiles(sceneCoordinate);
         m_mapScene->removeOutOfViewTiles();
     }
+
+    emit newMapResolution(sceneResolution());
 }
 
 void MapEngine::setZoomLevel(int newZoomLevel)
@@ -424,6 +456,8 @@ void MapEngine::viewZoomFinished()
         emit maxZoomLevelReached();
     else if (m_zoomLevel == MIN_VIEW_ZOOM_LEVEL)
         emit minZoomLevelReached();
+
+    emit newMapResolution(sceneResolution());
 }
 
 void MapEngine::zoomIn()
index 034ed0b..94681a7 100644 (file)
@@ -112,6 +112,18 @@ public:
     static QPoint convertTileNumberToSceneCoordinate(int zoomLevel, QPoint tileNumber);
 
     /**
+     * @brief Calculate great-circle distance between two geographic coordinates
+     *
+     * Calculate great-circle distance between two given geographic locations using
+     * haversine formula
+     *
+     * @param firstLocation Coordinates of the first location
+     * @param secondLocation Coordinates of the second location
+     * @return qreal Distance in kilometers
+     */
+    qreal greatCircleDistance(QPointF firstLocation, QPointF secondLocation);
+
+    /**
     * @brief MapEngine initializer
     *
     * Set initial location and zoom level for the engine. locationChanged and
@@ -145,6 +157,7 @@ public:
     static QString tilePath(int zoomLevel, int x, int y);
 
 public slots:
+
     /**
     * @brief Slot to catch user own location data
     *
@@ -235,6 +248,13 @@ private:
     bool isCenterTileChanged(QPoint sceneCoordinate);
 
     /**
+     * @brief Calculate scale at the map center of the map in meters/pixel
+     *
+     * @return qreal Scale of the map in meters/pixel
+     */
+    qreal sceneResolution();
+
+    /**
     * @brief Calculate maximum value for tile in this zoom level.
     *
     * @param zoomLevel zoom level
@@ -347,6 +367,11 @@ signals:
     void minZoomLevelReached();
 
     /**
+     * @brief Signal to pass the scale of the map to map scale
+     */
+    void newMapResolution(qreal scale);
+
+    /**
     * @brief Request view changing zoom level
     *
     * @param newZoomLevel New zoom level
index ec3cd6b..c032723 100644 (file)
@@ -60,6 +60,7 @@ MainWindow::MainWindow(QWidget *parent)
     m_password(),
     m_fullScreenButton(0),
     m_webView(0),
+    m_mapScale(0),
     m_cookieJar(0)
 {
     qDebug() << __PRETTY_FUNCTION__;
@@ -84,21 +85,17 @@ MainWindow::MainWindow(QWidget *parent)
     createMenus();
     setWindowTitle(tr("Situare"));
 
-    MapScale *mapScale = new MapScale(this);
-    mapScale->move(50, 400);
-
     // set stacking order of widgets
     m_zoomButtonPanel->stackUnder(m_userPanel);
     if(m_fullScreenButton) {
         m_fullScreenButton->stackUnder(m_zoomButtonPanel);
         m_osmLicense->stackUnder(m_fullScreenButton);
-    } else
-        m_osmLicense->stackUnder(m_zoomButtonPanel);    
+    } else {
+        m_osmLicense->stackUnder(m_zoomButtonPanel);
+    }
     m_ownLocationCrosshair->stackUnder(m_osmLicense);
-
-    mapScale->stackUnder(m_ownLocationCrosshair);
-
-    m_mapView->stackUnder(mapScale);
+    m_mapScale->stackUnder(m_ownLocationCrosshair);
+    m_mapView->stackUnder(m_mapScale);
 
     this->toggleProgressIndicator(true);
 
@@ -181,6 +178,7 @@ void MainWindow::buildMap()
     buildOsmLicense();
     buildManualLocationCrosshair();
     buildFullScreenButton();
+    buildMapScale();
 
     connect(m_mapView, SIGNAL(viewScrolled(QPoint)),
             this, SIGNAL(mapViewScrolled(QPoint)));
@@ -194,6 +192,9 @@ void MainWindow::buildMap()
     connect(m_mapView, SIGNAL(viewResized(QSize)),
             this, SLOT(drawFullScreenButton(QSize)));
 
+    connect(m_mapView, SIGNAL(viewResized(QSize)),
+            this, SLOT(drawMapScale(QSize)));
+
     connect(m_mapView, SIGNAL(viewResizedNewSize(int, int)),
              this, SLOT(setViewPortSize(int, int)));
 
@@ -204,6 +205,13 @@ void MainWindow::buildMap()
             this, SIGNAL(viewZoomFinished()));
 }
 
+void MainWindow::buildMapScale()
+{
+    m_mapScale = new MapScale(this);
+    connect(this, SIGNAL(newMapResolution(qreal)),
+            m_mapScale, SLOT(updateMapResolution(qreal)));
+}
+
 void MainWindow::buildOsmLicense()
 {
     qDebug() << __PRETTY_FUNCTION__;
@@ -356,6 +364,16 @@ void MainWindow::drawFullScreenButton(const QSize &size)
     }
 }
 
+void MainWindow::drawMapScale(const QSize &size)
+{
+    const int LEFT_SCALE_MARGIN = 10;
+    const int BOTTOM_SCALE_MARGIN = 2;
+    qDebug() << __PRETTY_FUNCTION__ << size.width() << "x" << size.height();
+
+    m_mapScale->move(PANEL_PEEK_AMOUNT + LEFT_SCALE_MARGIN,
+                     size.height() - m_mapScale->size().height() - BOTTOM_SCALE_MARGIN);
+}
+
 void MainWindow::drawOsmLicense(const QSize &size)
 {
     qDebug() << __PRETTY_FUNCTION__ << size.width() << "x" << size.height();
index e4b78fe..d703912 100644 (file)
@@ -36,6 +36,7 @@ class QWebView;
 
 class FacebookAuthentication;
 class FriendListPanel;
+class MapScale;
 class MapScene;
 class MapView;
 class SituareService;
@@ -199,6 +200,11 @@ private:
     void buildMap();
 
     /**
+     * @brief Build map scale and connect slots
+     */
+    void buildMapScale();
+
+    /**
       * @brief Build OSM license and connect slots
       */
     void buildOsmLicense();
@@ -247,6 +253,13 @@ private slots:
     void drawFullScreenButton(const QSize &size);
 
     /**
+    * @brief Slot for drawing the map distance scale
+    *
+    * @param size Size of the screen
+    */
+    void drawMapScale(const QSize &size);
+
+    /**
     * @brief Slot for drawing the Open Street Map license text
     *
     * @param size Size of the screen
@@ -404,6 +417,11 @@ signals:
     void minZoomLevelReached();
 
     /**
+     * @brief Forwarding signal from MapEngine to MapScale
+     */
+    void newMapResolution(qreal scale);
+
+    /**
     * @brief Signal for refreshing user data.
     *
     */
@@ -500,6 +518,7 @@ private:
     QWebView *m_webView;                    ///< Shows facebook login page
 
     FriendListPanel *m_friendsListPanel;    ///< Instance of friends list panel
+    MapScale *m_mapScale;                   ///< Instance of the map scale
     MapView *m_mapView;                     ///< Instance of the map view
     NetworkCookieJar *m_cookieJar;          ///< Placeholder for QNetworkCookies
     PanelSideBar *m_userPanelSidebar;       ///< User panel side bar
index 40c8ead..1ea485a 100644 (file)
 #include "mapscale.h"
 #include <math.h>
 
-const int METRIC_STEP = 90;                 ///< TEST value in pixels
-const qreal KM_TO_MILE = 1.609344;          ///< Kilometer to mile conversion value
-const int MAPSCALE_HEIGHT = 14;             ///< Height of the the map scale
-const int BASELINE_Y = MAPSCALE_HEIGHT / 2; ///< Y position of the baseline
-const int MAPSCALE_STOP_HEIGHT = 7;         ///< Height of each perpendicular stop on the scale
-
-const qreal INPUTVALUE = 450.35;          ///< SIMULATED input value: meters/pixel
+const int TARGET_WIDTH = 140;                   ///< Target width of the scale in pixels
+const qreal M_TO_FT = 3.2808399;                ///< Meter to feet conversion value
+const qreal FT_TO_MI = 5280;                    ///< Feet to mile conversion
+const qreal M_TO_KM = 1000;                     ///< Meters to kilometers conversion
+const int MAPSCALE_HEIGHT = 31;                 ///< Height of the the map scale
+const int CENTERLINE_Y = MAPSCALE_HEIGHT / 2;   ///< Y position of the centerline
+const int MAPSCALE_STOP_HEIGHT = 7;             ///< Height of each perpendicular stop on the scale
+const int SCALE_TEXT_X = 3;                     ///< X coordinate for the scale texts
 
 MapScale::MapScale(QWidget *parent) :
-    QWidget(parent)
+    QWidget(parent),
+    m_centerLineImperial(0),
+    m_centerLineMetric(0),
+    m_imperialText(""),
+    m_metricText("")
 {
     qDebug() << __PRETTY_FUNCTION__;
-//    setAutoFillBackground(true);
+
+//    updateMapScale(10.0);
     setAttribute(Qt::WA_TransparentForMouseEvents, true);
 }
 
@@ -47,34 +53,31 @@ void MapScale::paintEvent(QPaintEvent *event)
     qDebug() << __PRETTY_FUNCTION__;
 
     Q_UNUSED(event);
-    qreal gen_scale = METRIC_STEP * INPUTVALUE;
-    qreal baseScale;
-
-
-    baseScale = roundToBaseScale(gen_scale);
-    qreal baseLineLength = baseScale / INPUTVALUE;
 
-    qWarning() << gen_scale << ">" << baseScale << ">" << baseLineLength;
+    resize(ceil(fmax(m_centerLineMetric,m_centerLineImperial)) + 1, MAPSCALE_HEIGHT);
 
-    QLineF BaseLine(0, BASELINE_Y, baseLineLength * KM_TO_MILE, BASELINE_Y);
-    QLineF StartKm(1, BASELINE_Y, 1, (BASELINE_Y) - MAPSCALE_STOP_HEIGHT);
-    QLineF StartMi(1, BASELINE_Y, 1, (BASELINE_Y) + MAPSCALE_STOP_HEIGHT);
-    QLineF StopKm(baseLineLength, BASELINE_Y,
-                  baseLineLength, (BASELINE_Y) - MAPSCALE_STOP_HEIGHT);
-    QLineF StopMi(baseLineLength * KM_TO_MILE, BASELINE_Y,
-                  baseLineLength * KM_TO_MILE, (BASELINE_Y) + MAPSCALE_STOP_HEIGHT);
+    QLineF centerLine(0, CENTERLINE_Y, fmax(m_centerLineMetric, m_centerLineImperial), CENTERLINE_Y);
+    QLineF startKm(1, CENTERLINE_Y, 1, CENTERLINE_Y - MAPSCALE_STOP_HEIGHT);
+    QLineF stopKm(m_centerLineMetric, CENTERLINE_Y,
+                  m_centerLineMetric, CENTERLINE_Y - MAPSCALE_STOP_HEIGHT);
 
-    resize(ceil(baseLineLength * KM_TO_MILE)+1, MAPSCALE_HEIGHT);
+    QLineF startMi(1, CENTERLINE_Y, 1, CENTERLINE_Y + MAPSCALE_STOP_HEIGHT);
+    QLineF stopMi(m_centerLineImperial, CENTERLINE_Y,
+                  m_centerLineImperial, CENTERLINE_Y + MAPSCALE_STOP_HEIGHT);
 
     QPainter painter(this);
     QPen pen(Qt::black, 2);
 
+    painter.setFont(QFont("Nokia Sans", 13, QFont::Normal));
     painter.setPen(pen);
-    painter.drawLine(BaseLine);
-    painter.drawLine(StartKm);
-    painter.drawLine(StartMi);
-    painter.drawLine(StopKm);
-    painter.drawLine(StopMi);
+    painter.drawLine(centerLine);
+    painter.drawLine(startKm);
+    painter.drawLine(stopKm);
+    painter.drawText(SCALE_TEXT_X, MAPSCALE_HEIGHT / 2 - 2, m_metricText);
+
+    painter.drawLine(startMi);
+    painter.drawLine(stopMi);
+    painter.drawText(SCALE_TEXT_X, MAPSCALE_HEIGHT, m_imperialText);
 }
 
 qreal MapScale::roundToBaseScale(qreal value)
@@ -89,13 +92,59 @@ qreal MapScale::roundToBaseScale(qreal value)
     }
     if(value < 0.15)
         baseLine = 1;
-    else if (value < 0.375)
+    else if (value < 0.35)
         baseLine = 2;
     else if (value < 0.75)
         baseLine = 5;
     else
         baseLine = 10;
     baseLine = baseLine * (pow(10,scale-1));
-    qWarning() << baseLine;
     return baseLine;
 }
+
+void MapScale::updateMapResolution(const qreal &resolution)
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    //Calculate distance scale in metric units
+    qreal genericMetricScale = TARGET_WIDTH * resolution;
+    qreal baseMetricScale = roundToBaseScale(genericMetricScale);
+    if(resolution != 0)
+        m_centerLineMetric = baseMetricScale / resolution;
+    else
+        m_centerLineMetric = baseMetricScale / 0.000001;
+
+    if(baseMetricScale < M_TO_KM)
+    {
+        m_metricText.setNum(baseMetricScale);
+        m_metricText.append(" m");
+    } else {
+        m_metricText.setNum(baseMetricScale/M_TO_KM);
+        m_metricText.append(" km");
+    }
+
+    //Calculate distance scale in imperial units
+    qreal imperialScaleResolution = resolution * M_TO_FT;
+    qreal genericImperialScale = TARGET_WIDTH * imperialScaleResolution;
+    qreal baseImperialScale;
+
+    if(genericImperialScale < FT_TO_MI) {
+        baseImperialScale = roundToBaseScale(genericImperialScale);
+        if(imperialScaleResolution != 0)
+            m_centerLineImperial = baseImperialScale / imperialScaleResolution;
+        else
+            m_centerLineImperial = baseImperialScale / 0.000001;
+        m_imperialText.setNum(baseImperialScale);
+        m_imperialText.append(" ft");
+    } else {
+        baseImperialScale = roundToBaseScale(genericImperialScale / FT_TO_MI);
+        if(imperialScaleResolution != 0)
+            m_centerLineImperial = (baseImperialScale*FT_TO_MI) / imperialScaleResolution;
+        else
+            m_centerLineImperial = baseImperialScale / 0.000001;
+        m_imperialText.setNum(baseImperialScale);
+        m_imperialText.append(" mi");
+    }
+
+    update();
+}
index 46aff56..0d3c004 100644 (file)
 
 #include <QWidget>
 
+class QLineF;
+
+/**
+ * @brief Map distance scale
+ *
+ * @author Kaj Wallin - kaj.wallin (at) ixonos.com
+ */
 class MapScale : public QWidget
 {
     Q_OBJECT
 
 public:
+    /**
+     * @brief Constructor
+     *
+     * @param parent Parent
+     */
     MapScale(QWidget *parent = 0);
 
 /*******************************************************************************
@@ -38,7 +50,7 @@ public:
     /**
      * @brief Event handler for paint events
      *
-     * Paints the button and its icon
+     * Paints the scale
      * @param event Paint event
      */
     void paintEvent(QPaintEvent *event);
@@ -47,17 +59,31 @@ public:
  * MEMBER FUNCTIONS AND SLOTS
  ******************************************************************************/
 private:
+    /**
+     * @brief Rounding function for distances
+     *
+     * Rounds the given value to closest 1,2,5 or 10 in the original scale
+     * @param value Value to be rounded
+     * @return qreal Rounded value
+     */
     qreal roundToBaseScale(qreal value);
-public slots:
 
-/*******************************************************************************
- * SIGNALS
- ******************************************************************************/
-signals:
+public slots:
+    /**
+     * @brief Slot to update the scale with latest resolution
+     *
+     * @param resolution Resolution of the map in meters/pixel
+     */
+    void updateMapResolution(const qreal &resolution);
 
 /*******************************************************************************
  * DATA MEMBERS
  ******************************************************************************/
+private:
+    qreal m_centerLineImperial;     ///< Length of the imperial scale
+    qreal m_centerLineMetric;       ///< Length of the metric scale
+    QString m_imperialText;         ///< Text description of the imperial scale
+    QString m_metricText;           ///< Text description of the metric scale
 };
 
 #endif // MAPSCALE_H
index 745981f..4533254 100644 (file)
@@ -68,6 +68,9 @@ protected:
  * MEMBER FUNCTIONS AND SLOTS
  ******************************************************************************/
 public:
+    /**
+     * @brief Relative position of the event inside the widget
+     */
     const QPoint& eventPosition();
 
 /*******************************************************************************
index 8da59de..5d649ac 100644 (file)
@@ -44,8 +44,6 @@ public:
      * @brief Constructor
      *
      * @param parent Parent
-     * @param x Panel x coordinate
-     * @param y Panel y coordinate
      */
     ZoomButtonPanel(QWidget *parent = 0);