Class: GeoDistView

Inherits:
Qt::Object
  • Object
show all
Includes:
BrowserLauncher
Defined in:
lib/GeoDistView.rb

Overview

This class shows the geographical distribution of visitors on a world map and as numbers per country in a list.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from BrowserLauncher

#browseURL

Constructor Details

#initialize(mapFrame, countryList = nil, routeList = nil, large = false) ⇒ GeoDistView

The mapFrame widget will hold the world map and the countryList will show the distribution by country.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/GeoDistView.rb', line 32

def initialize(mapFrame, countryList = nil, routeList = nil, large = false)
  super(nil)
  @mapFrame = mapFrame
  @countryList = countryList
  @routeList = routeList

  if @countryList
    @countryListCSLV = CSListView.new(@countryList)
    @countryList.setSorting(1, false)
  end
  if @routeList
    @routeListCSLV = CSListView.new(@routeList)
    @routeList.setSorting(0, true)

    connect(@routeList, SIGNAL('clicked(QListViewItem*)'),
        SLOT('routerListClicked(QListViewItem*)'))
  end

  # Canvas that shows the map and the location crosses
  @canvas = Qt::Canvas.new
  # The canvas view will be a child of the mapFrame widget
  @mapFrame.setColumnLayout(0, Qt::Vertical)
  @mapFrame.layout().setSpacing(6)
  @mapFrame.layout().setMargin(11)
  lm = Qt::GridLayout.new(mapFrame.layout)
  # Create the CanvasView that shows the map canvas
  @mapView = ClickableCanvasView.new(@canvas, @mapFrame)
  lm.addWidget(@mapView, 0, 0)
  # The world map is a background image of the canvas
  pm = Qt::Pixmap.new(large ? AppConfig.dataFile('worldmap-large.jpg') :
      AppConfig.dataFile('worldmap.png'))
  if large
    @mapView.setMaximumSize(pm.width + 2 * @mapView.frameWidth ,
                            pm.height + 2 * @mapView.frameWidth)
  else
    @mapView.setFixedSize(pm.width + 2 * @mapView.frameWidth ,
                          pm.height + 2 * @mapView.frameWidth)
    @mapView.setVScrollBarMode(Qt::ScrollView.AlwaysOff)
    @mapView.setHScrollBarMode(Qt::ScrollView.AlwaysOff)
  end
  connect(@mapView, SIGNAL('clicked()'), self, SLOT('mapClicked()'))

  @canvas.resize(pm.width, pm.height)
  @canvas.setBackgroundPixmap(pm)

  @showAll = false
  @showRoutes = false
  @showRouters = false
  @showIPs = false
  @showHostNames = false
  @showCities = false
  @showLast = false
  @lastMinutes = 1

  @layer = 0

  @highlightedVisitor = nil
  @highlightedRouter = nil
end

Instance Attribute Details

#lastMinutes=(value) ⇒ Object (writeonly)

Sets the attribute lastMinutes

Parameters:

  • value

    the value to set the attribute lastMinutes to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def lastMinutes=(value)
  @lastMinutes = value
end

#showAll=(value) ⇒ Object (writeonly)

Sets the attribute showAll

Parameters:

  • value

    the value to set the attribute showAll to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def showAll=(value)
  @showAll = value
end

#showCities=(value) ⇒ Object (writeonly)

Sets the attribute showCities

Parameters:

  • value

    the value to set the attribute showCities to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def showCities=(value)
  @showCities = value
end

#showHostNames=(value) ⇒ Object (writeonly)

Sets the attribute showHostNames

Parameters:

  • value

    the value to set the attribute showHostNames to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def showHostNames=(value)
  @showHostNames = value
end

#showIPs=(value) ⇒ Object (writeonly)

Sets the attribute showIPs

Parameters:

  • value

    the value to set the attribute showIPs to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def showIPs=(value)
  @showIPs = value
end

#showLast=(value) ⇒ Object (writeonly)

Sets the attribute showLast

Parameters:

  • value

    the value to set the attribute showLast to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def showLast=(value)
  @showLast = value
end

#showRouters=(value) ⇒ Object (writeonly)

Sets the attribute showRouters

Parameters:

  • value

    the value to set the attribute showRouters to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def showRouters=(value)
  @showRouters = value
end

#showRoutes=(value) ⇒ Object (writeonly)

Sets the attribute showRoutes

Parameters:

  • value

    the value to set the attribute showRoutes to.



26
27
28
# File 'lib/GeoDistView.rb', line 26

def showRoutes=(value)
  @showRoutes = value
end

Instance Method Details

#correctIP(ip) ⇒ Object



341
342
343
344
345
346
347
348
# File 'lib/GeoDistView.rb', line 341

def correctIP(ip)
  browseURL("http://www.hostip.info/correct.html?spip=#{ip}")

  # I don't know how long it takes for the hostip server to provide the
  # changed information but there seems to be a noticable delay. We set the
  # record to timeout in 60 minutes to that it will be fetched again then.
  $geoLocator.expireInMinutes(ip, 60)
end

#drawConnection(loc1, loc2) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/GeoDistView.rb', line 196

def drawConnection(loc1, loc2)
  x1, y1 = loc2xy(loc1)
  return unless x1 > 0 && y1 > 0
  x2, y2 = loc2xy(loc2)
  return unless x2 > 0 && y2 > 0

  horizRollover = (x1 - x2).abs > (@canvas.width / 2)
  vertRollover = (y1 - y2).abs > (@canvas.height / 2)

  if !horizRollover && !vertRollover
    l = Qt::CanvasLine.new(@canvas)
    l.setPen(Qt::Pen.new(Qt::Color.new('gray'), 1))
    l.setPoints(x1, y1, x2, y2)
    l.z = 2
    l.show
  elsif horizRollover
    xl, yl = x1 < x2 ? [ x1, y1 ] : [ x2, y2 ]
    xr, yr = x1 > x2 ? [ x1, y1 ] : [ x2, y2 ]
    l1 = Qt::CanvasLine.new(@canvas)
    l1.setPen(Qt::Pen.new(Qt::Color.new('gray'), 1))
    l1.setPoints(xr - @canvas.width, yr, xl, yl)
    l1.z = 2
    l1.show

    l2 = Qt::CanvasLine.new(@canvas)
    l2.setPen(Qt::Pen.new(Qt::Color.new('gray'), 1))
    l2.setPoints(xl + @canvas.width, yl, xr, yr)
    l2.z = 2
    l2.show
  else
    xt, yt = y1 < y2 ? [ x1, y1 ] : [ x2, y2 ]
    xb, yb = y1 > y2 ? [ x1, y1 ] : [ x2, y2 ]
    l1 = Qt::CanvasLine.new(@canvas)
    l1.setPen(Qt::Pen.new(Qt::Color.new('gray'), 1))
    l1.setPoints(xt, yt - @canvas.height, xb, yb)
    l1.z = 2
    l1.show

    l2 = Qt::CanvasLine.new(@canvas)
    l2.setPen(Qt::Pen.new(Qt::Color.new('gray'), 1))
    l2.setPoints(xb, yb + @canvas.height, xt, yt)
    l2.z = 2
    l2.show
  end
end

#drawLabel(px, py, frameColor, text, large) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/GeoDistView.rb', line 289

def drawLabel(px, py, frameColor, text, large)

  x = px + 5
  y = py - 5

  tl = Qt::CanvasText.new(text, @canvas)
  tl.setColor(Qt::Color.new('black'))
  font = Qt::Font.new
  font.setPixelSize(large ? 11 : 9)
  tl.setFont(font)
  tl.x = x
  tl.y = y
  tl.z = @layer + 1
  tl.show

  bg = Qt::CanvasRectangle.new(tl.boundingRect, @canvas)
  bg.setPen(Qt::Pen.new(Qt::Color.new(frameColor)))
  bg.setBrush(Qt::Brush.new(Qt::Color.new('white')))
  bg.z = @layer
  bg.show

  l = Qt::CanvasLine.new(@canvas)
  l.setPen(Qt::Pen.new(Qt::Color.new(frameColor), 1))
  l.setPoints(px, py, x, y + bg.boundingRect.height / 3)
  l.show
  l.z = 8

  @layer += 2
end

#loc2xy(geoloc) ⇒ Object



275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/GeoDistView.rb', line 275

def loc2xy(geoloc)
  return [ -1, -1 ] unless geoloc
  # Check that location is valid
  latitude = geoloc.latitude
  longitude = geoloc.longitude
  return [ -1, -1 ] if latitude == nil || longitude == nil
  mapW = @canvas.width
  mapH = @canvas.height
  # Compute the center of the cross
  x = (mapW / 2) + longitude * mapW / 360
  y = (mapH / 2) - latitude * mapH / 180
  [ x, y ]
end

#mapClickedObject



324
325
326
327
328
329
330
# File 'lib/GeoDistView.rb', line 324

def mapClicked()
  if @highlightedVisitor
    correctIP(@highlightedVisitor.ip)
  elsif @highlightedRouter
    correctIP(@highlightedRouter)
  end
end

#markOnMap(geoloc, color, pLayer, ip, mouseRect) ⇒ Object

Mark the specified location with a small red cross on the map



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/GeoDistView.rb', line 243

def markOnMap(geoloc, color, pLayer, ip, mouseRect)
  return false unless geoloc
  x , y = loc2xy(geoloc)
  return false unless x > 0 && y > 0

  # In case this IP is under the mouse cursor we will show full details
  # no matter that the show flags say.
  showFullInfo = mouseRect && mouseRect.contains(x, y)

  # Horizontal line
  l1 = Qt::CanvasLine.new(@canvas)
  l1.setPen(Qt::Pen.new(Qt::Color.new(color), 1))
  l1.setPoints(x - 1, y, x + 2, y)
  l1.z = pLayer
  l1.show
  # Vertical line
  l2 = Qt::CanvasLine.new(@canvas)
  l2.setPen(Qt::Pen.new(Qt::Color.new(color), 1))
  l2.setPoints(x, y - 1, x, y + 2)
  l2.z = pLayer
  l2.show

  label = ''
  label += ip + "\n" if @showIPs || showFullInfo
  label += $resolver.hostName(ip) + "\n" if @showHostNames || showFullInfo
  label += geoloc.city + "\n" if (@showCities || showFullInfo) && geoloc.city
  label += geoloc.country + "\n" if showFullInfo
  drawLabel(x, y, color, label, showFullInfo) unless label.empty?

  return showFullInfo
end

#markRouteOnMap(ip, mouseRect) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/GeoDistView.rb', line 150

def markRouteOnMap(ip, mouseRect)
  return unless @showRouters || @showRoutes

  rec = $traceRouter.resolve(ip)
  return unless rec

  last = nil
  rec.routers.each do |r|
    loc = $geoLocator.resolve(r)
    next unless loc && loc.latitude && loc.longitude
    drawConnection(last, loc) if @showRoutes
    last = loc
    if @showRouters && markOnMap(loc, 'yellow', 6, ip, mouseRect)
      @highlightedRouter = ip
    end
  end
  # Make sure that we always connect to the destination if we have found
  # at least one router along the way.
  if last
    loc = $geoLocator.resolve(ip)
    drawConnection(last, loc) if @showRoutes
  end
end

#routerListClicked(item) ⇒ Object



332
333
334
335
336
337
338
339
# File 'lib/GeoDistView.rb', line 332

def routerListClicked(item)
  return unless item

  ip = @routeListCSLV.item(item)
  return unless ip

  correctIP(ip)
end

#selectedItemsObject



319
320
321
322
# File 'lib/GeoDistView.rb', line 319

def selectedItems
  return [] unless @countryList
  @countryListCSLV.selectedItems
end

#update(visitors) ⇒ Object

Update the map and the country distribution list.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/GeoDistView.rb', line 93

def update(visitors)
  # Remove all crosses from the map canvas
  @canvas.allItems().each { |i| i.dispose }
  @layer = 10

  @highlightedVisitor = nil
  @highlightedRouter = nil

  # Determine a little square on the map the mouse cursor is over.
  mouseRect = nil
  pos = @mapView.mapFromGlobal(Qt::Cursor.new.pos);
  if pos.x >= 0 && pos.y >= 0 &&
     pos.x < @mapView.width && pos.y < @mapView.height
     radius = 4
     mouseRect = Qt::Rect.new(pos.x - radius, pos.y - radius,
         2 * radius, 2 * radius)
  end

  # Count the number of visitors for each country.
  countries = {}
  showSurfers = $globals.getSetting('ShowSurfers') != 0
  showRobots = $globals.getSetting('ShowRobots') != 0
  deadline = Time.now - @lastMinutes * 60
  visitors.each do |key, visitor|
    if (!@showAll &&
       ((visitor.robot != nil && !showRobots) ||
        (visitor.robot == nil && !showSurfers))) ||
       (@showLast && visitor.lastHit.timeStamp < deadline)
      next
    end
    markRouteOnMap(visitor.ip, mouseRect)
    updateRouterList(visitor.ip) if @routeList
    geoloc = $geoLocator.resolve(visitor.ip)
    if geoloc == nil || geoloc.country == nil
      label = '* Unknown *'
    else
      label = geoloc.country
      # Mark all visitors with known locations on the map
      if markOnMap(geoloc, "red", 8, visitor.ip, mouseRect)
        @highlightedVisitor = visitor
      end
    end
    countries[label] = 0 if !countries.has_key?(label)
    countries[label] += 1
  end
  @canvas.update
  if @countryList
    @countryListCSLV.startUpdate
    countries.each do |country, count|
      @countryListCSLV.insertItem(country, country, count,
          count * 100.0 / visitors.size)
    end
    @countryListCSLV.finishUpdate
  end

end

#updateRouterList(ip) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/GeoDistView.rb', line 174

def updateRouterList(ip)
  rec = $traceRouter.resolve(ip)
  return unless rec

  i = 1
  @routeListCSLV.startUpdate
  rec.routers.each do |r|
    loc = $geoLocator.resolve(r)
    if loc
      @routeListCSLV.insertItem(r, i, r, $resolver.hostName(r),
          loc.country ? loc.country : '',
          loc.city ? loc.city : '',
          loc.latitude ? loc.latitude : 0,
          loc.longitude ? loc.longitude : 0)
    else
      @routeListCSLV.insertItem(r, i, '*', '*', '', '', 0, 0)
    end
    i += 1
  end
  @routeListCSLV.finishUpdate
end