update ipv4 map example, including bug fixes
This commit is contained in:
parent
f3334c13cf
commit
a150457748
2 changed files with 83 additions and 57 deletions
|
@ -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,10 +663,13 @@ Ox.load('UI', function() {
|
|||
setIP($marker.attr('id'));
|
||||
} else {
|
||||
setXY(self.xy.map(function(xy, i) {
|
||||
return deltaZ == -1
|
||||
? 2 * xy - mouseXY[i]
|
||||
: (xy + mouseXY[i]) / 2
|
||||
}));
|
||||
return Ox.limit(
|
||||
deltaZ == -1
|
||||
? 2 * xy - mouseXY[i]
|
||||
: (xy + mouseXY[i]) / 2,
|
||||
0, self.mapSize - 1
|
||||
);
|
||||
}));
|
||||
}
|
||||
zoomBy(deltaZ);
|
||||
self.zooming = true;
|
||||
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue