oxjs/examples/maps/ipv4_map_of_the_internet/txt/ipv4map.py.txt
2012-06-22 09:35:36 +02:00

191 lines
7.6 KiB
Text

# ipv4map.py - no copyright 2011 0x2620.org - public domain
from __future__ import division
import Image
import ImageDraw
import math
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 = {
'U': [[0, 2, 3, 1], 'DUUC'], # Square 0 1 Segment U = 0 3 = 0 1 E F = D C
'D': [[0, 1, 3, 2], 'UDDA'], # 2 3 1 2 3 2 D C U U
'C': [[3, 2, 0, 1], 'ACCU'], # 4 7 8 B
'A': [[3, 1, 0, 2], 'CAAD'] # 5 6 9 A
}
def get_country_code_by_ip(ip):
replace = {'A1': 'NTHH', 'A2': 'NTHH', 'AN': 'ANHH', 'AP': 'FM', 'O1': 'NTHH'}
data = geoip.record_by_addr(ip) if not ip.startswith('0.') else None
country_code = data['country_code'] if data and 'country_code' in data else 'NTHH'
return replace[country_code] if country_code in replace else country_code
def get_flag_image(county_code, size):
flag_size = str(int(pow(4, math.ceil(math.log(size, 2) / 2))))
flag_image = Image.open(path + flag_size + '/' + county_code + '.png')
if size != flag_size:
flag_image = flag_image.resize((size, size), Image.ANTIALIAS)
return flag_image
def get_ip_by_n(n):
ip = []
for i in range(4):
ip.append(str(int(n / pow(256, 3 - i)) % 256))
return '.'.join(ip)
def get_n_by_ip(ip):
n = 0
for i, v in enumerate(ip.split('.')):
n += int(v) * pow(256, 3 - i)
return n
def get_tile_by_ip(ip, z):
n = get_n_by_ip(ip)
last_n = n + pow(4, 16 - z) - 1
last_ip = get_ip_by_n(last_n)
return '../png/tiles/%s/%s-%s.png' % (ip.split('.')[0], ip, last_ip)
def get_xy_by_ip(ip, z, w='U'):
x, y = 0, 0
n = int(get_n_by_ip(ip) / pow(4, 8 - z))
for i in range(8 + z):
p2 = pow(2, 7 + z - i)
p4 = pow(4, 7 + z - i)
q = int(n / p4)
xy = projection[w][0][q]
x += xy % 2 * p2
y += int(xy / 2) * p2
n -= q * p4
w = projection[w][1][q]
return [x, y]
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 = 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)
x, y = i % 16 * 64, int(i / 16) * 64
image.paste(flag, (x, y, x + 64, y + 64))
ox.image.drawText(image, (x + 5, y + 5), country['code'], font, 8, (64, 64, 64))
ox.image.drawText(image, (x + 4, y + 4), country['code'], font, 8, (192, 192, 192))
image.save('../png/flags.png')
def render_map():
# renders images of the full map
if not os.path.exists('../png/ipv4map16.png'):
mapfile = '../png/ipv4map16384.png'
mapimage = Image.new('RGB', (16384, 16384))
for a in range(0, 256, 16):
print '%d.0.0.0' % a
image = Image.open('../png/map/%d.0.0.0-%d.255.255.255.png' % (a, a + 15))
x, y = map(lambda x: int(x / 4096) * 4096, get_xy_by_ip('%d.0.0.0' % a, 6))
mapimage.paste(image.resize((4096, 4096), Image.ANTIALIAS), (x, y))
mapimage.save(mapfile)
for s in [4096, 1024, 16]:
mapimage.resize((s, s), Image.ANTIALIAS).save(mapfile.replace('16384', str(s)))
def render_square(values, maxlen, w):
# recursively renders a square region, given a list of country codes
length = len(values)
if length > 4:
square_length = int(length / 4)
square_values = map(lambda x: values[x * square_length:(x + 1) * square_length], range(4))
values = []
for i, v in enumerate(square_values):
values.append(render_square(v, maxlen, projection[w][1][i]))
equal = values[0] == values[1] == values[2] == values[3]
if length < maxlen and equal:
return values[0]
else:
order = projection[w][0]
image_size = int(math.sqrt(length) * 16)
flag_size = image_size if equal else int(image_size / 2)
image = Image.new('RGB', (image_size, image_size))
for i, value in enumerate([values[0]] if equal else values):
flag = get_flag_image(value, flag_size) if isinstance(value, str) else value
image.paste(flag, (order[i] % 2 * flag_size, int(order[i] / 2) * flag_size))
if length == pow(4, 4):
image.save('../png/_tmp/' + str(values) + '.png')
return image
def render_tile(ip, z):
# recursively renders the map tile for a given ip at a given zoom level
n = get_n_by_ip(ip)
file = get_tile_by_ip(ip, z)
if not os.path.exists(file):
tiles_n = map(lambda x: n + x * pow(4, 15 - z), range(4))
tiles_ip = map(lambda x: get_ip_by_n(x), tiles_n)
tiles = map(lambda x: render_tile(x, z + 1), tiles_ip)
image = Image.new('RGB', (256, 256))
x, y = map(lambda x: int(x / 256) * 256, get_xy_by_ip(ip, z))
for i in range(4):
x_, y_ = map(lambda x: int(x / 128) * 128, get_xy_by_ip(tiles_ip[i], z))
image.paste(tiles[i].resize((128, 128), Image.ANTIALIAS), (x_ - x, y_ - y))
image.save(file)
else:
image = Image.open(file)
return image
def render_tiles():
# renders all tiles at the maximum zoom level
w = ''
for v in projection['U'][1]:
w += projection[v][1]
file256 = '../png/ipv4map16384.png'
for a in range(256):
print '%d.0.0.0' % a
if a % 16 == 0:
values = []
file = '../png/map/%d.0.0.0-%d.255.255.255.png' % (a, a + 15)
if not os.path.exists(file):
for b in range(256):
for c in range(256):
values.append(get_country_code_by_ip('%d.%d.%d.0' % (a, b, c)))
if a % 16 == 15:
image = render_square(values, len(values), w[int(a / 16)]).save(file)
if a % 16 == 15:
image = Image.open(file)
for a_ in range(a - 15, a + 1):
for b in range(256):
file = '../png/tiles/%d/%d.%d.0.0-%d.%d.255.255.png' % (a_, a_, b, a_, b)
if not os.path.exists(file):
x, y = map(lambda x: int(x % 16384 / 256) * 256, get_xy_by_ip('%d.%d.0.0' % (a_, b), 8))
dirs = os.path.split(file)[0]
if dirs and not os.path.exists(dirs):
os.makedirs(dirs)
image.crop((x, y, x + 256, y + 256)).save(file)
def render_projection():
# renders an image of the map projection
image = Image.new('RGB', (1024, 1024))
draw = ImageDraw.Draw(image)
font = '../ttf/DejaVuSansCondensedBold.ttf'
for i in range(256):
x, y = map(lambda x: int(x / 64) * 64, get_xy_by_ip('%d.0.0.0' % i, 2))
rgb = ox.getRGB((i * 360 / 256, 1, 0.5))
draw.rectangle((x, y, x + 64, y + 64), fill=rgb)
ox.image.drawText(image, (x + 4, y + 4), str(i), font, 8, (0, 0, 0))
draw.rectangle((x + 30, y + 30, x + 34, y + 33), fill=0)
if i:
draw.line((x_ + 32, y_ + 32, x + 32, y + 32), fill=0, width=4)
x_, y_ = x, y
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()
# http://s3.amazonaws.com/alexa-static/top-1m.csv.zip