Vidalia 0.2.10
|
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 4378 2010-08-05 20:28:54Z 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 GeoIpRecord &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