tormapwidget.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file tormapwidget.cpp
00013 ** \version $Id: tormapwidget.cpp 2977 2008-08-17 01:28:25Z edmanm $
00014 ** \brief Displays Tor servers and circuits on a map of the world
00015 */
00016 
00017 #include <QStringList>
00018 #include <cmath>
00019 #include "tormapwidget.h"
00020 
00021 #define IMG_WORLD_MAP   ":/images/map/world-map.png"
00022 
00023 /** QPens to use for drawing different map elements */
00024 #define PEN_ROUTER        QPen(QColor("#ff030d"), 1.0)
00025 #define PEN_CIRCUIT       QPen(Qt::yellow, 0.5)
00026 #define PEN_SELECTED      QPen(Qt::green, 2.0)
00027 
00028 /** Size of the map image */
00029 #define IMG_WIDTH       1000
00030 #define IMG_HEIGHT      507
00031 
00032 /** Border between the edge of the image and the actual map */
00033 #define MAP_TOP         2
00034 #define MAP_BOTTOM      2
00035 #define MAP_RIGHT       5
00036 #define MAP_LEFT        5
00037 #define MAP_WIDTH       (IMG_WIDTH-MAP_LEFT-MAP_RIGHT)
00038 #define MAP_HEIGHT      (IMG_HEIGHT-MAP_TOP-MAP_BOTTOM)
00039 
00040 /** Map offset from zero longitude */
00041 #define MAP_ORIGIN       -10
00042 
00043 /** Minimum allowable size for this widget */
00044 #define MIN_SIZE        QSize(512,256)
00045 
00046 /** Robinson projection table */
00047 /** Length of the parallel of latitude */
00048 static float  plen[] = {
00049     1.0000, 0.9986, 0.9954, 0.9900,
00050     0.9822, 0.9730, 0.9600, 0.9427,
00051     0.9216, 0.8962, 0.8679, 0.8350,
00052     0.7986, 0.7597, 0.7186, 0.6732,
00053     0.6213, 0.5722, 0.5322
00054   };
00055 
00056 /** Distance of corresponding parallel from equator */ 
00057 static float  pdfe[] = {
00058     0.0000, 0.0620, 0.1240, 0.1860,
00059     0.2480, 0.3100, 0.3720, 0.4340,
00060     0.4958, 0.5571, 0.6176, 0.6769,
00061     0.7346, 0.7903, 0.8435, 0.8936,
00062     0.9394, 0.9761, 1.0000
00063   };
00064 
00065 /** Default constructor */
00066 TorMapWidget::TorMapWidget(QWidget *parent)
00067 : ZImageView(parent)
00068 {
00069   QImage map(IMG_WORLD_MAP);
00070   setImage(map);
00071 }
00072 
00073 /** Destructor */
00074 TorMapWidget::~TorMapWidget()
00075 {
00076   clear();
00077 }
00078 
00079 /** Adds a router to the map. */
00080 void
00081 TorMapWidget::addRouter(const QString &id, float latitude, float longitude)
00082 {
00083   QPointF routerCoord = toMapSpace(latitude, longitude);
00084   
00085   /* Add data the hash of known routers, and plot the point on the map */
00086   if (_routers.contains(id))
00087     _routers.value(id)->first = routerCoord;
00088   else
00089     _routers.insert(id, new QPair<QPointF,bool>(routerCoord, false));
00090 }
00091 
00092 /** Adds a circuit to the map using the given ordered list of router IDs. */
00093 void
00094 TorMapWidget::addCircuit(const CircuitId &circid, const QStringList &path)
00095 {
00096   QPainterPath *circPainterPath = new QPainterPath;
00097   
00098   /* Build the new circuit */
00099   for (int i = 0; i < path.size()-1; i++) {
00100     QString fromNode = path.at(i);
00101     QString toNode = path.at(i+1);
00102    
00103     /* Add the coordinates of the hops to the circuit */
00104     if (_routers.contains(fromNode) && _routers.contains(toNode)) {
00105       /* Find the two endpoints for this path segment */
00106       QPointF fromPos = _routers.value(fromNode)->first;
00107       QPointF endPos = _routers.value(toNode)->first;
00108       
00109       /* Draw the path segment */ 
00110       circPainterPath->moveTo(fromPos);
00111       circPainterPath->lineTo(endPos);
00112       circPainterPath->moveTo(endPos);
00113     }
00114   }
00115   
00116   /** Add the data to the hash of known circuits and plot the circuit on the map */
00117   if (_circuits.contains(circid)) {
00118     /* This circuit is being updated, so just update the path, making sure we
00119      * free the memory allocated to the old one. */
00120     QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
00121     delete circuitPair->first;
00122     circuitPair->first = circPainterPath;
00123   } else {
00124     /* This is a new path, so just add it to our list */
00125     _circuits.insert(circid, new QPair<QPainterPath*,bool>(circPainterPath,false));
00126   }
00127 }
00128 
00129 /** Removes a circuit from the map. */
00130 void
00131 TorMapWidget::removeCircuit(const CircuitId &circid)
00132 {
00133   QPair<QPainterPath*,bool> *circ = _circuits.take(circid);
00134   QPainterPath *circpath = circ->first;
00135   if (circpath) {
00136     delete circpath;
00137   }
00138   delete circ;
00139 }
00140 
00141 /** Selects and highlights the router on the map. */
00142 void
00143 TorMapWidget::selectRouter(const QString &id)
00144 {
00145   if (_routers.contains(id)) {
00146     QPair<QPointF, bool> *routerPair = _routers.value(id);
00147     routerPair->second = true;
00148   }
00149   repaint();
00150 }
00151 
00152 /** Selects and highlights the circuit with the id <b>circid</b> 
00153  * on the map. */
00154 void
00155 TorMapWidget::selectCircuit(const CircuitId &circid)
00156 {
00157   if (_circuits.contains(circid)) {
00158     QPair<QPainterPath*, bool> *circuitPair = _circuits.value(circid);
00159     circuitPair->second = true;
00160   }
00161   repaint();
00162 }
00163 
00164 /** Deselects any highlighted routers or circuits */
00165 void
00166 TorMapWidget::deselectAll()
00167 {
00168   /* Deselect all router points */
00169   foreach (QString router, _routers.keys()) {
00170     QPair<QPointF,bool> *routerPair = _routers.value(router);
00171     routerPair->second = false;
00172   }
00173   /* Deselect all circuit paths */
00174   foreach (CircuitId circid, _circuits.keys()) {
00175     QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
00176     circuitPair->second = false;
00177   }
00178 }
00179 
00180 /** Clears the list of routers and removes all the data on the map */
00181 void
00182 TorMapWidget::clear()
00183 {
00184   /* Clear out all the router points and free their memory */
00185   foreach (QString router, _routers.keys()) {
00186     delete _routers.take(router);
00187   }
00188   /* Clear out all the circuit paths and free their memory */
00189   foreach (CircuitId circid, _circuits.keys()) {
00190     QPair<QPainterPath*,bool> *circuitPair = _circuits.take(circid);
00191     delete circuitPair->first;
00192     delete circuitPair;
00193   }
00194 }
00195   
00196 /** Draws the routers and paths onto the map image. */
00197 void
00198 TorMapWidget::paintImage(QPainter *painter)
00199 {
00200   painter->setRenderHint(QPainter::Antialiasing);
00201   
00202   /* Draw the router points */
00203   foreach(QString router, _routers.keys()) {
00204     QPair<QPointF,bool> *routerPair = _routers.value(router);
00205     painter->setPen((routerPair->second ? PEN_SELECTED : PEN_ROUTER)); 
00206     painter->drawPoint(routerPair->first);
00207   }
00208   /* Draw the circuit paths */
00209   foreach(CircuitId circid, _circuits.keys()) {
00210     QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
00211     painter->setPen((circuitPair->second ? PEN_SELECTED : PEN_CIRCUIT));
00212     painter->drawPath(*(circuitPair->first));
00213   }
00214 }
00215 
00216 /** Converts world space coordinates into map space coordinates */
00217 QPointF
00218 TorMapWidget::toMapSpace(float latitude, float longitude)
00219 {
00220   float width  = MAP_WIDTH;
00221   float height = MAP_HEIGHT;
00222   float deg = width / 360.0;
00223   longitude += MAP_ORIGIN;
00224 
00225   float lat;
00226   float lon;
00227   
00228   lat = floor(longitude * (deg * lerp(abs(int(latitude)), plen))
00229               + width/2 + MAP_LEFT);
00230   
00231   if (latitude < 0) {
00232     lon = floor((height/2) + (lerp(abs(int(latitude)), pdfe) * (height/2))
00233                 + MAP_TOP);
00234   } else {
00235     lon = floor((height/2) - (lerp(abs(int(latitude)), pdfe) * (height/2))
00236                 + MAP_TOP);
00237   }
00238 
00239   return QPointF(lat, lon);
00240 }
00241   
00242 /** Linearly interpolates using the values in the Robinson projection table */
00243 float
00244 TorMapWidget::lerp(float input, float *table)
00245 {
00246   int x = int(floor(input / 5));
00247 
00248   return ((table[x+1] - table[x]) / 
00249           (((x+1)*5) - (x*5))) * (input - x*5) + table[x];
00250 }
00251 
00252 /** Returns the minimum size of the widget */
00253 QSize
00254 TorMapWidget::minimumSizeHint() const
00255 {
00256   return MIN_SIZE;
00257 }
00258 
00259 /** Zooms to fit all currently displayed circuits on the map. If there are no
00260  * circuits on the map, the viewport will be returned to its default position
00261  * (zoomed all the way out and centered). */
00262 void
00263 TorMapWidget::zoomToFit()
00264 {
00265   QRectF rect = circuitBoundingBox();
00266   
00267   if (rect.isNull()) {
00268     /* If there are no circuits, zoom all the way out */
00269     resetZoomPoint();
00270     zoom(0.0);
00271   } else {
00272     /* Zoom in on the displayed circuits */
00273     float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
00274                                  rect.width()/float(MAP_WIDTH));
00275     
00276     zoom(rect.center().toPoint(), zoomLevel+0.2);
00277   }
00278 }
00279 
00280 /** Zoom to the circuit on the map with the given <b>circid</b>. */
00281 void
00282 TorMapWidget::zoomToCircuit(const CircuitId &circid)
00283 {
00284   if (_circuits.contains(circid)) {
00285     QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
00286     QRectF rect = ((QPainterPath *)pair->first)->boundingRect();
00287     if (!rect.isNull()) {
00288       float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
00289                                    rect.width()/float(MAP_WIDTH));
00290 
00291       zoom(rect.center().toPoint(), zoomLevel+0.2);
00292     }
00293   }
00294 }
00295 
00296 /** Zooms in on the router with the given <b>id</b>. */
00297 void
00298 TorMapWidget::zoomToRouter(const QString &id)
00299 {
00300   QPair<QPointF,bool> *routerPair;
00301   
00302   if (_routers.contains(id)) {
00303     deselectAll();
00304     routerPair = _routers.value(id);
00305     routerPair->second = true;  /* Set the router point to "selected" */
00306     zoom(routerPair->first.toPoint(), 1.0); 
00307   }
00308 }
00309 
00310 /** Computes a bounding box around all currently displayed circuit paths on
00311  * the map. */
00312 QRectF
00313 TorMapWidget::circuitBoundingBox()
00314 {
00315   QRectF rect;
00316 
00317   /* Compute the union of bounding rectangles for all circuit paths */
00318   foreach (CircuitId circid, _circuits.keys()) {
00319     QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
00320     QPainterPath *circuit = pair->first;
00321     rect = rect.unite(circuit->boundingRect());
00322   }
00323   return rect;
00324 }
00325 

Generated on Wed Nov 26 21:02:42 2008 for Vidalia by  doxygen 1.5.7.1