update ipv4 map example, including bug fixes

This commit is contained in:
rolux 2012-07-03 18:19:55 +02:00
parent f3334c13cf
commit a150457748
2 changed files with 83 additions and 57 deletions

View file

@ -1,3 +1,14 @@
/*
The code below implements a map widget to navigate a zoomable globe made of
tiled images — similar to Google Maps. The catch is that the map doesn't
show the surface of the Earth, but the Internet   instead of geographical
or political boundaries, it maps the country-wise allocation of the IPv4 address
space.
Inline comments, below, are minimal — but the widget has its own "About"
section that explains how it was done.
*/
'use strict';
/*
@ -57,12 +68,13 @@ Ox.load('UI', function() {
key_m: function() { self.$toggle.trigger('click'); },
key_minus: function() { zoomBy(-1); },
key_right: function() { panBy([0.5, 0]); },
key_shift_down: function() { panBy([0, 1]) },
key_shift_down: function() { panBy([0, 1]); },
key_shift_enter: function() { find(''); },
key_shift_equal: function() { zoomBy(2); },
key_shift_left: function() { panBy([-1, 0]) },
key_shift_left: function() { panBy([-1, 0]); },
key_shift_minus: function() { zoomBy(-2); },
key_shift_right: function() { panBy([1, 0]) },
key_shift_up: function() { panBy([0, -1]) },
key_shift_right: function() { panBy([1, 0]); },
key_shift_up: function() { panBy([0, -1]); },
key_slash: function() { toggleOverlay('milliondollarhomepage'); },
key_up: function() { panBy([0, -0.5]); },
mousedown: function(e) {
@ -102,7 +114,8 @@ Ox.load('UI', function() {
self.mapSize = Math.pow(2, self.options.zoom) * self.tileSize;
/*
Tiles layer.
Tiles layer with a dynamic tooltip that reacts to mousewheel, drag and
click events.
*/
self.$tiles = Ox.Element({
tooltip: function(e) {
@ -131,7 +144,7 @@ Ox.load('UI', function() {
.appendTo(that);
/*
About button.
About button that opens a dialog.
*/
self.$about = Ox.Button({
selectable: false,
@ -156,7 +169,7 @@ Ox.load('UI', function() {
.appendTo(that);
/*
The panel inside the about dialog.
The tabbed panel inside the about dialog.
*/
self.$panel = Ox.TabPanel({
content: {
@ -187,6 +200,25 @@ Ox.load('UI', function() {
]
});
/*
The images in the "About" section.
*/
[
'png/xkcd.png', 'png/projection.png', 'png/flags.png', 'png/map.png'
].forEach(function(image) {
$('<a>')
.attr({href: image, target: '_blank'})
.append(
$('<img>')
.attr({src: image})
.css({width: '256px', marginBottom: '16px'})
)
.appendTo(self.$images);
});
/*
The contents of the "Mouse & Keyboard Controls" panel.
*/
Ox.forEach({
'Mouse': {
'Click': 'Pan to position / Select marker',
@ -238,7 +270,7 @@ Ox.load('UI', function() {
});
/*
About dialog.
The dialog itself.
*/
self.$dialog = Ox.Dialog({
buttons: [
@ -261,21 +293,9 @@ Ox.load('UI', function() {
width: 560 + Ox.UI.SCROLLBAR_SIZE
});
[
'png/xkcd.png', 'png/projection.png', 'png/flags.png', 'png/map.png'
].forEach(function(image) {
$('<a>')
.attr({href: image, target: '_blank'})
.append(
$('<img>')
.attr({src: image})
.css({width: '256px', marginBottom: '16px'})
)
.appendTo(self.$images);
});
/*
Find element.
Find element to search for host names or IP addresses, with a menu to
select pre-defined bookmarks.
*/
self.$findElement = Ox.FormElementGroup({
elements: [
@ -318,7 +338,7 @@ Ox.load('UI', function() {
.appendTo(that);
/*
Zoom control.
Zoom control. 0 is the lowest zoom level, 8 is the highest.
*/
self.$zoom = Ox.Range({
arrows: true,
@ -339,7 +359,7 @@ Ox.load('UI', function() {
.appendTo(that);
/*
Overview map control.
Button that toggles the overview map.
*/
self.$toggle = Ox.Button({
title: 'Show Overview Map',
@ -353,7 +373,7 @@ Ox.load('UI', function() {
.appendTo(that);
/*
Overview map.
The overview map itself, showing the entire internet.
*/
self.$world = Ox.Element({
tooltip: function(e) {
@ -381,7 +401,7 @@ Ox.load('UI', function() {
.appendTo(that);
/*
Position marker on overview map.
The position marker on overview map.
*/
self.$point = Ox.Element()
.addClass('marker')
@ -394,6 +414,9 @@ Ox.load('UI', function() {
.attr({id: 'regions'})
.appendTo(self.$world);
/*
The regions of the overview map, 'center' being the visible area.
*/
[
'center', 'left', 'right', 'top', 'bottom'
].forEach(function(region) {
@ -415,7 +438,8 @@ Ox.load('UI', function() {
document.location.hash && onHashchange();
/*
Looks up a given host name or IP address.
Looks up a given host name or IP address and then, if there is a result,
pans to its position and adds a marker.
*/
function find(value) {
var isHost, query = '';
@ -434,7 +458,6 @@ Ox.load('UI', function() {
self.$find.options({value: value});
getJSONP(self.options.lookupURL + query, function(data) {
if (isHost && !isIP(data.ip)) {
//document.location.hash = '';
self.$find.addClass('OxError');
} else {
data.host = value ? data.host : 'Me';
@ -456,7 +479,7 @@ Ox.load('UI', function() {
}
/*
Returns an IP address for a given number.
Translates an given integer into an IP address.
*/
function getIPByN(n) {
return Ox.range(4).map(function(i) {
@ -471,14 +494,12 @@ Ox.load('UI', function() {
var n = 0, path = self.path,
z = zoom === void 0 ? self.options.zoom : zoom;
Ox.loop(8 + z, function(i) {
try {
var p2 = Math.pow(2, 7 + z - i),
p4 = Math.pow(4, 7 + z - i),
xy_ = xy.map(function(v) {
return Math.floor(v / p2);
}),
q = self.projection[path][0].indexOf(xy_[0] + xy_[1] * 2);
} catch(e) { Ox.print('ERROR', q, path); }
n += q * p4;
xy = xy.map(function(v, i) {
return v - xy_[i] * p2;
@ -494,7 +515,7 @@ Ox.load('UI', function() {
var getJSONP = Ox.cache(Ox.getJSONP, {async: true});
/*
Returns a marker for a given mouse event.
Returns the marker that recieved a given click event, or null.
*/
function getMarker(e) {
var $target = $(e.target);
@ -504,7 +525,7 @@ Ox.load('UI', function() {
}
/*
Returns a number for a given IP address.
Translates a given IP adress into an integer.
*/
function getNByIP(ip) {
return ip.split('.').reduce(function(prev, curr, i) {
@ -583,14 +604,14 @@ Ox.load('UI', function() {
}
/*
Handles doubleclick event.
Handles doubleclick events by delegating to the mousewheel handler.
*/
function onDoubleclick(e) {
onMousewheel(e, 0, 0, e.shiftKey ? - 1 : 1);
}
/*
Handles drag event for map and overview map.
Handles drag events for both the main map and the overview map.
*/
function onDrag(e) {
var delta = [e.clientDX, e.clientDY];
@ -601,7 +622,7 @@ Ox.load('UI', function() {
}
/*
Handles dragstart event for map and overview map.
Handles dragstart events for both the main map and the overview map.
*/
function onDragstart(e, isWorld) {
self.dragstartXY = self.xy;
@ -609,7 +630,7 @@ Ox.load('UI', function() {
}
/*
Handles hashchange event.
Handles hashchange events.
*/
function onHashchange() {
var parts;
@ -624,7 +645,8 @@ Ox.load('UI', function() {
}
/*
Handles mousewheel event.
Handles mousewheel events (zooms in or out, but maintains the position
that the mouse is pointing at).
*/
function onMousewheel(e, delta, deltaX, deltaY) {
var $marker = getMarker(e),
@ -641,9 +663,12 @@ Ox.load('UI', function() {
setIP($marker.attr('id'));
} else {
setXY(self.xy.map(function(xy, i) {
return deltaZ == -1
return Ox.limit(
deltaZ == -1
? 2 * xy - mouseXY[i]
: (xy + mouseXY[i]) / 2
: (xy + mouseXY[i]) / 2,
0, self.mapSize - 1
);
}));
}
zoomBy(deltaZ);
@ -656,7 +681,7 @@ Ox.load('UI', function() {
}
/*
Handles window resize event.
Handles window resize events.
*/
function onResize() {
self.mapCenter = [
@ -680,11 +705,13 @@ Ox.load('UI', function() {
}
/*
Handles singleclick event.
Handles singleclick events. Clicking on a marker selects it, but holding
the Meta key toggles its selected state instead, and holding shift adds
it to the current selection. Clicking on the map and holding shift adds
a marker at that place.
*/
function onSingleclick(e) {
var $marker = getMarker(e),
ip = getIPByMouseXY(e);
var $marker = getMarker(e), ip;
if ($marker) {
if (e.metaKey) {
$marker.toggleClass('selected');
@ -694,6 +721,7 @@ Ox.load('UI', function() {
}
} else {
$('.marker.selected').removeClass('selected');
ip = getIPByMouseXY(e);
panTo(getXYByMouseXY(e), function() {
e.shiftKey && find(ip);
});
@ -731,7 +759,7 @@ Ox.load('UI', function() {
}
/*
Renders the map.
Renders the tiles of the map.
*/
function renderMap() {
var halfWidth, halfHeight;
@ -766,11 +794,10 @@ Ox.load('UI', function() {
});
updateWorld();
$('.OxTooltip').remove();
//document.location.hash = self.options.ip + ',' + self.options.zoom;
}
/*
Renders a marker.
Renders a map marker with the given properties.
*/
function renderMarker(data) {
var $icon, $marker = self.$tiles.find('div[id="' + data.ip + '"]'),
@ -875,7 +902,7 @@ Ox.load('UI', function() {
}
/*
Sets IP and XY.
Updates XY when setting the IP address.
*/
function setIP(ip) {
self.options.ip = ip;
@ -883,7 +910,7 @@ Ox.load('UI', function() {
}
/*
Sets XY and IP.
Updates the IP address when setting XY.
*/
function setXY(xy) {
self.xy = xy;
@ -891,7 +918,7 @@ Ox.load('UI', function() {
}
/*
Toggles overlay image.
Toggles the overlay image.
*/
function toggleOverlay(image) {
var $overlay = $('#overlay'), src = 'png/' + image + '.png';

View file

@ -8,8 +8,6 @@ import os
import ox
import pygeoip
# fixme: pow(a, b) -> a ** b
geoip = pygeoip.GeoIP('../dat/GeoLiteCity.dat', pygeoip.MEMORY_CACHE)
path = '../../../../oxjs.org/source/Ox.Geo/png/flags/'
projection = {
@ -68,7 +66,7 @@ def render_flags():
# renders an image of 256 flags
image = Image.new('RGB', (1024, 1024))
font = '../ttf/DejaVuSansMonoBold.ttf'
countries = ox.file.read_json('../../../../oxjs.org/oxjs/source/Ox.Geo/json/Ox.Geo.json')
countries = ox.file.read_json(path.replace('png/flags/', 'json/Ox.Geo.json'))
countries = filter(lambda x: len(x['code']) == 2 and not x['code'] in ['UK', 'XK'], countries)
for i, country in enumerate(sorted(countries, key=lambda x: x['code'])):
flag = get_flag_image(country['code'], 64)
@ -180,12 +178,13 @@ def render_projection():
image.save('../png/projection.png')
if __name__ == '__main__':
# qlmanage -t -s 16384 -o . ../../../../oxjs.org/tools/geo/svg/icons/NTHH.svg
render_flags()
render_projection()
render_tiles()
render_tile('0.0.0.0', 0)
render_map()
# to get a sufficiently large "international" icon, run:
# qlmanage -t -s 16384 -o . ../../../../oxjs.org/tools/geo/svg/icons/NTHH.svg
# http://s3.amazonaws.com/alexa-static/top-1m.csv.zip