Vidalia 0.2.15
|
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.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the terms described in the LICENSE file. 00009 */ 00010 00011 /* 00012 ** \file TorMapImageView.cpp 00013 ** \brief Displays Tor servers and circuits on a map of the world 00014 */ 00015 00016 #include "config.h" 00017 #include "TorMapImageView.h" 00018 00019 #include <QStringList> 00020 00021 #if defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 730 00022 #include <math.h> 00023 #else 00024 #include <cmath> 00025 #endif 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 TorMapImageView::TorMapImageView(QWidget *parent) 00073 : ZImageView(parent) 00074 { 00075 QImage map(IMG_WORLD_MAP); 00076 setImage(map); 00077 } 00078 00079 /** Destructor */ 00080 TorMapImageView::~TorMapImageView() 00081 { 00082 clear(); 00083 } 00084 00085 /** Adds a router to the map. */ 00086 void 00087 TorMapImageView::addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip) 00088 { 00089 QString id = desc.id(); 00090 QPointF routerCoord = toMapSpace(geoip.latitude(), geoip.longitude()); 00091 00092 /* Add data the hash of known routers, and plot the point on the map */ 00093 if (_routers.contains(id)) 00094 _routers.value(id)->first = routerCoord; 00095 else 00096 _routers.insert(id, new QPair<QPointF,bool>(routerCoord, false)); 00097 } 00098 00099 /** Adds a circuit to the map using the given ordered list of router IDs. */ 00100 void 00101 TorMapImageView::addCircuit(const CircuitId &circid, const QStringList &path) 00102 { 00103 QPainterPath *circPainterPath = new QPainterPath; 00104 00105 /* Build the new circuit */ 00106 for (int i = 0; i < path.size()-1; i++) { 00107 QString fromNode = path.at(i); 00108 QString toNode = path.at(i+1); 00109 00110 /* Add the coordinates of the hops to the circuit */ 00111 if (_routers.contains(fromNode) && _routers.contains(toNode)) { 00112 /* Find the two endpoints for this path segment */ 00113 QPointF fromPos = _routers.value(fromNode)->first; 00114 QPointF endPos = _routers.value(toNode)->first; 00115 00116 /* Draw the path segment */ 00117 circPainterPath->moveTo(fromPos); 00118 circPainterPath->lineTo(endPos); 00119 circPainterPath->moveTo(endPos); 00120 } 00121 } 00122 00123 /** Add the data to the hash of known circuits and plot the circuit on the map */ 00124 if (_circuits.contains(circid)) { 00125 /* This circuit is being updated, so just update the path, making sure we 00126 * free the memory allocated to the old one. */ 00127 QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid); 00128 delete circuitPair->first; 00129 circuitPair->first = circPainterPath; 00130 } else { 00131 /* This is a new path, so just add it to our list */ 00132 _circuits.insert(circid, new QPair<QPainterPath*,bool>(circPainterPath,false)); 00133 } 00134 } 00135 00136 /** Removes a circuit from the map. */ 00137 void 00138 TorMapImageView::removeCircuit(const CircuitId &circid) 00139 { 00140 QPair<QPainterPath*,bool> *circ = _circuits.take(circid); 00141 QPainterPath *circpath = circ->first; 00142 if (circpath) { 00143 delete circpath; 00144 } 00145 delete circ; 00146 } 00147 00148 /** Selects and highlights the router on the map. */ 00149 void 00150 TorMapImageView::selectRouter(const QString &id) 00151 { 00152 if (_routers.contains(id)) { 00153 QPair<QPointF, bool> *routerPair = _routers.value(id); 00154 routerPair->second = true; 00155 } 00156 repaint(); 00157 } 00158 00159 /** Selects and highlights the circuit with the id <b>circid</b> 00160 * on the map. */ 00161 void 00162 TorMapImageView::selectCircuit(const CircuitId &circid) 00163 { 00164 if (_circuits.contains(circid)) { 00165 QPair<QPainterPath*, bool> *circuitPair = _circuits.value(circid); 00166 circuitPair->second = true; 00167 } 00168 repaint(); 00169 } 00170 00171 /** Deselects any highlighted routers or circuits */ 00172 void 00173 TorMapImageView::deselectAll() 00174 { 00175 /* Deselect all router points */ 00176 foreach (QString router, _routers.keys()) { 00177 QPair<QPointF,bool> *routerPair = _routers.value(router); 00178 routerPair->second = false; 00179 } 00180 /* Deselect all circuit paths */ 00181 foreach (CircuitId circid, _circuits.keys()) { 00182 QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid); 00183 circuitPair->second = false; 00184 } 00185 } 00186 00187 /** Clears the list of routers and removes all the data on the map */ 00188 void 00189 TorMapImageView::clear() 00190 { 00191 /* Clear out all the router points and free their memory */ 00192 foreach (QString router, _routers.keys()) { 00193 delete _routers.take(router); 00194 } 00195 /* Clear out all the circuit paths and free their memory */ 00196 foreach (CircuitId circid, _circuits.keys()) { 00197 QPair<QPainterPath*,bool> *circuitPair = _circuits.take(circid); 00198 delete circuitPair->first; 00199 delete circuitPair; 00200 } 00201 } 00202 00203 /** Draws the routers and paths onto the map image. */ 00204 void 00205 TorMapImageView::paintImage(QPainter *painter) 00206 { 00207 painter->setRenderHint(QPainter::Antialiasing); 00208 00209 /* Draw the router points */ 00210 foreach(QString router, _routers.keys()) { 00211 QPair<QPointF,bool> *routerPair = _routers.value(router); 00212 painter->setPen((routerPair->second ? PEN_SELECTED : PEN_ROUTER)); 00213 painter->drawPoint(routerPair->first); 00214 } 00215 /* Draw the circuit paths */ 00216 foreach(CircuitId circid, _circuits.keys()) { 00217 QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid); 00218 painter->setPen((circuitPair->second ? PEN_SELECTED : PEN_CIRCUIT)); 00219 painter->drawPath(*(circuitPair->first)); 00220 } 00221 } 00222 00223 /** Converts world space coordinates into map space coordinates */ 00224 QPointF 00225 TorMapImageView::toMapSpace(float latitude, float longitude) 00226 { 00227 float width = MAP_WIDTH; 00228 float height = MAP_HEIGHT; 00229 float deg = width / 360.0; 00230 longitude += MAP_ORIGIN; 00231 00232 float lat; 00233 float lon; 00234 00235 lat = floor(longitude * (deg * lerp(abs(int(latitude)), plen)) 00236 + width/2 + MAP_LEFT); 00237 00238 if (latitude < 0) { 00239 lon = floor((height/2) + (lerp(abs(int(latitude)), pdfe) * (height/2)) 00240 + MAP_TOP); 00241 } else { 00242 lon = floor((height/2) - (lerp(abs(int(latitude)), pdfe) * (height/2)) 00243 + MAP_TOP); 00244 } 00245 00246 return QPointF(lat, lon); 00247 } 00248 00249 /** Linearly interpolates using the values in the Robinson projection table */ 00250 float 00251 TorMapImageView::lerp(float input, float *table) 00252 { 00253 int x = int(floor(input / 5)); 00254 00255 return ((table[x+1] - table[x]) / 00256 (((x+1)*5) - (x*5))) * (input - x*5) + table[x]; 00257 } 00258 00259 /** Returns the minimum size of the widget */ 00260 QSize 00261 TorMapImageView::minimumSizeHint() const 00262 { 00263 return MIN_SIZE; 00264 } 00265 00266 /** Zooms to fit all currently displayed circuits on the map. If there are no 00267 * circuits on the map, the viewport will be returned to its default position 00268 * (zoomed all the way out and centered). */ 00269 void 00270 TorMapImageView::zoomToFit() 00271 { 00272 QRectF rect = circuitBoundingBox(); 00273 00274 if (rect.isNull()) { 00275 /* If there are no circuits, zoom all the way out */ 00276 resetZoomPoint(); 00277 zoom(0.0); 00278 } else { 00279 /* Zoom in on the displayed circuits */ 00280 float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT), 00281 rect.width()/float(MAP_WIDTH)); 00282 00283 zoom(rect.center().toPoint(), zoomLevel+0.2); 00284 } 00285 } 00286 00287 /** Zoom to the circuit on the map with the given <b>circid</b>. */ 00288 void 00289 TorMapImageView::zoomToCircuit(const CircuitId &circid) 00290 { 00291 if (_circuits.contains(circid)) { 00292 QPair<QPainterPath*,bool> *pair = _circuits.value(circid); 00293 QRectF rect = ((QPainterPath *)pair->first)->boundingRect(); 00294 if (!rect.isNull()) { 00295 float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT), 00296 rect.width()/float(MAP_WIDTH)); 00297 00298 zoom(rect.center().toPoint(), zoomLevel+0.2); 00299 } 00300 } 00301 } 00302 00303 /** Zooms in on the router with the given <b>id</b>. */ 00304 void 00305 TorMapImageView::zoomToRouter(const QString &id) 00306 { 00307 QPair<QPointF,bool> *routerPair; 00308 00309 if (_routers.contains(id)) { 00310 deselectAll(); 00311 routerPair = _routers.value(id); 00312 routerPair->second = true; /* Set the router point to "selected" */ 00313 zoom(routerPair->first.toPoint(), 1.0); 00314 } 00315 } 00316 00317 /** Computes a bounding box around all currently displayed circuit paths on 00318 * the map. */ 00319 QRectF 00320 TorMapImageView::circuitBoundingBox() 00321 { 00322 QRectF rect; 00323 00324 /* Compute the union of bounding rectangles for all circuit paths */ 00325 foreach (CircuitId circid, _circuits.keys()) { 00326 QPair<QPainterPath*,bool> *pair = _circuits.value(circid); 00327 QPainterPath *circuit = pair->first; 00328 rect = rect.unite(circuit->boundingRect()); 00329 } 00330 return rect; 00331 } 00332