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

Generated on Wed Dec 23 21:11:10 2009 for Vidalia by  doxygen 1.6.1