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