Vidalia 0.3.1
TorMapImageView.cpp
Go to the documentation of this file.
1/*
2** This file is part of Vidalia, and is subject to the license terms in the
3** LICENSE file, found in the top level directory of this distribution. If you
4** did not receive the LICENSE file with this file, you may obtain it from the
5** Vidalia source package distributed by the Vidalia Project at
6** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7** including this file, may be copied, modified, propagated, or distributed
8** except according to the terms described in the LICENSE file.
9*/
10
11/*
12** \file TorMapImageView.cpp
13** \brief Displays Tor servers and circuits on a map of the world
14*/
15
16#include "config.h"
17#include "TorMapImageView.h"
18
19#include <QStringList>
20
21#if defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 730
22#include <math.h>
23#else
24#include <cmath>
25#endif
26
27#define IMG_WORLD_MAP ":/images/map/world-map.png"
28
29/** QPens to use for drawing different map elements */
30#define PEN_ROUTER QPen(QColor("#ff030d"), 1.0)
31#define PEN_CIRCUIT QPen(Qt::yellow, 0.5)
32#define PEN_SELECTED QPen(Qt::green, 2.0)
33
34/** Size of the map image */
35#define IMG_WIDTH 1000
36#define IMG_HEIGHT 507
37
38/** Border between the edge of the image and the actual map */
39#define MAP_TOP 2
40#define MAP_BOTTOM 2
41#define MAP_RIGHT 5
42#define MAP_LEFT 5
43#define MAP_WIDTH (IMG_WIDTH-MAP_LEFT-MAP_RIGHT)
44#define MAP_HEIGHT (IMG_HEIGHT-MAP_TOP-MAP_BOTTOM)
45
46/** Map offset from zero longitude */
47#define MAP_ORIGIN -10
48
49/** Minimum allowable size for this widget */
50#define MIN_SIZE QSize(512,256)
51
52/** Robinson projection table */
53/** Length of the parallel of latitude */
54static float plen[] = {
55 1.0000, 0.9986, 0.9954, 0.9900,
56 0.9822, 0.9730, 0.9600, 0.9427,
57 0.9216, 0.8962, 0.8679, 0.8350,
58 0.7986, 0.7597, 0.7186, 0.6732,
59 0.6213, 0.5722, 0.5322
60 };
61
62/** Distance of corresponding parallel from equator */
63static float pdfe[] = {
64 0.0000, 0.0620, 0.1240, 0.1860,
65 0.2480, 0.3100, 0.3720, 0.4340,
66 0.4958, 0.5571, 0.6176, 0.6769,
67 0.7346, 0.7903, 0.8435, 0.8936,
68 0.9394, 0.9761, 1.0000
69 };
70
71/** Default constructor */
73: ZImageView(parent)
74{
75 QImage map(IMG_WORLD_MAP);
76 setImage(map);
77}
78
79/** Destructor */
81{
82 clear();
83}
84
85/** Adds a router to the map. */
86void
88{
89 QString id = desc.id();
90 QPointF routerCoord = toMapSpace(geoip.latitude(), geoip.longitude());
91
92 /* Add data the hash of known routers, and plot the point on the map */
93 if (_routers.contains(id))
94 _routers.value(id)->first = routerCoord;
95 else
96 _routers.insert(id, new QPair<QPointF,bool>(routerCoord, false));
97}
98
99/** Adds a circuit to the map using the given ordered list of router IDs. */
100void
101TorMapImageView::addCircuit(const CircuitId &circid, const QStringList &path)
102{
103 QPainterPath *circPainterPath = new QPainterPath;
104
105 /* Build the new circuit */
106 for (int i = 0; i < path.size()-1; i++) {
107 QString fromNode = path.at(i);
108 QString toNode = path.at(i+1);
109
110 /* Add the coordinates of the hops to the circuit */
111 if (_routers.contains(fromNode) && _routers.contains(toNode)) {
112 /* Find the two endpoints for this path segment */
113 QPointF fromPos = _routers.value(fromNode)->first;
114 QPointF endPos = _routers.value(toNode)->first;
115
116 /* Draw the path segment */
117 circPainterPath->moveTo(fromPos);
118 circPainterPath->lineTo(endPos);
119 circPainterPath->moveTo(endPos);
120 }
121 }
122
123 /** Add the data to the hash of known circuits and plot the circuit on the map */
124 if (_circuits.contains(circid)) {
125 /* This circuit is being updated, so just update the path, making sure we
126 * free the memory allocated to the old one. */
127 QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
128 delete circuitPair->first;
129 circuitPair->first = circPainterPath;
130 } else {
131 /* This is a new path, so just add it to our list */
132 _circuits.insert(circid, new QPair<QPainterPath*,bool>(circPainterPath,false));
133 }
134}
135
136/** Removes a circuit from the map. */
137void
139{
140 QPair<QPainterPath*,bool> *circ = _circuits.take(circid);
141 QPainterPath *circpath = circ->first;
142 if (circpath) {
143 delete circpath;
144 }
145 delete circ;
146}
147
148/** Selects and highlights the router on the map. */
149void
151{
152 if (_routers.contains(id)) {
153 QPair<QPointF, bool> *routerPair = _routers.value(id);
154 routerPair->second = true;
155 }
156 repaint();
157}
158
159/** Selects and highlights the circuit with the id <b>circid</b>
160 * on the map. */
161void
163{
164 if (_circuits.contains(circid)) {
165 QPair<QPainterPath*, bool> *circuitPair = _circuits.value(circid);
166 circuitPair->second = true;
167 }
168 repaint();
169}
170
171/** Deselects any highlighted routers or circuits */
172void
174{
175 /* Deselect all router points */
176 foreach (QString router, _routers.keys()) {
177 QPair<QPointF,bool> *routerPair = _routers.value(router);
178 routerPair->second = false;
179 }
180 /* Deselect all circuit paths */
181 foreach (CircuitId circid, _circuits.keys()) {
182 QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
183 circuitPair->second = false;
184 }
185}
186
187/** Clears the list of routers and removes all the data on the map */
188void
190{
191 /* Clear out all the router points and free their memory */
192 foreach (QString router, _routers.keys()) {
193 delete _routers.take(router);
194 }
195 /* Clear out all the circuit paths and free their memory */
196 foreach (CircuitId circid, _circuits.keys()) {
197 QPair<QPainterPath*,bool> *circuitPair = _circuits.take(circid);
198 delete circuitPair->first;
199 delete circuitPair;
200 }
201}
202
203/** Draws the routers and paths onto the map image. */
204void
206{
207 painter->setRenderHint(QPainter::Antialiasing);
208
209 /* Draw the router points */
210 foreach(QString router, _routers.keys()) {
211 QPair<QPointF,bool> *routerPair = _routers.value(router);
212 painter->setPen((routerPair->second ? PEN_SELECTED : PEN_ROUTER));
213 painter->drawPoint(routerPair->first);
214 }
215 /* Draw the circuit paths */
216 foreach(CircuitId circid, _circuits.keys()) {
217 QPair<QPainterPath*,bool> *circuitPair = _circuits.value(circid);
218 painter->setPen((circuitPair->second ? PEN_SELECTED : PEN_CIRCUIT));
219 painter->drawPath(*(circuitPair->first));
220 }
221}
222
223/** Converts world space coordinates into map space coordinates */
224QPointF
225TorMapImageView::toMapSpace(float latitude, float longitude)
226{
227 float width = MAP_WIDTH;
228 float height = MAP_HEIGHT;
229 float deg = width / 360.0;
230 longitude += MAP_ORIGIN;
231
232 float lat;
233 float lon;
234
235 lat = floor(longitude * (deg * lerp(abs(int(latitude)), plen))
236 + width/2 + MAP_LEFT);
237
238 if (latitude < 0) {
239 lon = floor((height/2) + (lerp(abs(int(latitude)), pdfe) * (height/2))
240 + MAP_TOP);
241 } else {
242 lon = floor((height/2) - (lerp(abs(int(latitude)), pdfe) * (height/2))
243 + MAP_TOP);
244 }
245
246 return QPointF(lat, lon);
247}
248
249/** Linearly interpolates using the values in the Robinson projection table */
250float
251TorMapImageView::lerp(float input, float *table)
252{
253 int x = int(floor(input / 5));
254
255 return ((table[x+1] - table[x]) /
256 (((x+1)*5) - (x*5))) * (input - x*5) + table[x];
257}
258
259/** Returns the minimum size of the widget */
260QSize
262{
263 return MIN_SIZE;
264}
265
266/** Zooms to fit all currently displayed circuits on the map. If there are no
267 * circuits on the map, the viewport will be returned to its default position
268 * (zoomed all the way out and centered). */
269void
271{
272 QRectF rect = circuitBoundingBox();
273
274 if (rect.isNull()) {
275 /* If there are no circuits, zoom all the way out */
277 zoom(0.0);
278 } else {
279 /* Zoom in on the displayed circuits */
280 float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
281 rect.width()/float(MAP_WIDTH));
282
283 zoom(rect.center().toPoint(), zoomLevel+0.2);
284 }
285}
286
287/** Zoom to the circuit on the map with the given <b>circid</b>. */
288void
290{
291 if (_circuits.contains(circid)) {
292 QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
293 QRectF rect = ((QPainterPath *)pair->first)->boundingRect();
294 if (!rect.isNull()) {
295 float zoomLevel = 1.0 - qMax(rect.height()/float(MAP_HEIGHT),
296 rect.width()/float(MAP_WIDTH));
297
298 zoom(rect.center().toPoint(), zoomLevel+0.2);
299 }
300 }
301}
302
303/** Zooms in on the router with the given <b>id</b>. */
304void
306{
307 QPair<QPointF,bool> *routerPair;
308
309 if (_routers.contains(id)) {
310 deselectAll();
311 routerPair = _routers.value(id);
312 routerPair->second = true; /* Set the router point to "selected" */
313 zoom(routerPair->first.toPoint(), 1.0);
314 }
315}
316
317/** Computes a bounding box around all currently displayed circuit paths on
318 * the map. */
319QRectF
321{
322 QRectF rect;
323
324 /* Compute the union of bounding rectangles for all circuit paths */
325 foreach (CircuitId circid, _circuits.keys()) {
326 QPair<QPainterPath*,bool> *pair = _circuits.value(circid);
327 QPainterPath *circuit = pair->first;
328 rect = rect.unite(circuit->boundingRect());
329 }
330 return rect;
331}
332
QString CircuitId
Definition: Circuit.h:24
#define PEN_ROUTER
#define MIN_SIZE
#define MAP_HEIGHT
#define PEN_CIRCUIT
#define MAP_TOP
static float plen[]
#define IMG_WORLD_MAP
#define MAP_WIDTH
#define MAP_ORIGIN
#define PEN_SELECTED
static float pdfe[]
#define MAP_LEFT
float longitude() const
Definition: GeoIpRecord.h:54
float latitude() const
Definition: GeoIpRecord.h:49
QString id() const
void selectCircuit(const CircuitId &circid)
void zoomToCircuit(const CircuitId &circid)
TorMapImageView(QWidget *parent=0)
void addCircuit(const CircuitId &circid, const QStringList &path)
QHash< QString, QPair< QPointF, bool > * > _routers
void addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip)
void selectRouter(const QString &id)
void zoomToRouter(const QString &id)
virtual void paintImage(QPainter *painter)
void removeCircuit(const CircuitId &circid)
QRectF circuitBoundingBox()
QPointF toMapSpace(float latitude, float longitude)
float lerp(float input, float *table)
QSize minimumSizeHint() const
QHash< CircuitId, QPair< QPainterPath *, bool > * > _circuits
void zoom(float pct)
Definition: ZImageView.cpp:288
void setImage(QImage &pixmap)
Definition: ZImageView.cpp:51
void resetZoomPoint()
Definition: ZImageView.cpp:260
QString i(QString str)
Definition: html.cpp:32