forked from I2P_Developers/i2p.i2p
Compare commits
4 Commits
i2p-2.6.0-
...
i2p.i2p-ne
Author | SHA1 | Date | |
---|---|---|---|
850bbb6c84 | |||
f3f6ce83b2 | |||
4ff293c9ee | |||
308f60caa6 |
@ -0,0 +1,406 @@
|
||||
package net.i2p.router.web.helpers;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImageObserver;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelInfo;
|
||||
import net.i2p.router.TunnelManagerFacade;
|
||||
import net.i2p.router.web.ContextHelper;
|
||||
import net.i2p.router.tunnel.pool.TunnelPool;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.ObjectCounterUnsafe;
|
||||
|
||||
/**
|
||||
* Generate a transparent image to overlay the world map in a Web Mercator format.
|
||||
*
|
||||
* Also contains commented-out code to generate the mercator.txt file.
|
||||
*
|
||||
* @since 0.9.xx
|
||||
*/
|
||||
public class MapMaker {
|
||||
private final RouterContext _context;
|
||||
private final Log _log;
|
||||
|
||||
private static final Map<String, Mercator> _mercator = new HashMap<String, Mercator>(256);
|
||||
|
||||
static {
|
||||
readMercatorFile();
|
||||
}
|
||||
|
||||
private static final String LATLONG_DEFAULT = "latlong.csv";
|
||||
private static final String MERCATOR_DEFAULT = "mercator.txt";
|
||||
private static final String BASEMAP_DEFAULT = "mapbase72.png";
|
||||
private static final int WIDTH = 1600;
|
||||
private static final int HEIGHT = 1600;
|
||||
private static final int MAP_HEIGHT = 828;
|
||||
// offsets from mercator to image.
|
||||
// left side at 171.9 degrees (rotated 36 pixels)
|
||||
// tweak to make it line up, eyeball Taiwan
|
||||
private static final int IMG_X_OFF = -34;
|
||||
// We crop the top from 85 degrees down to about 75 degrees (283 pixels)
|
||||
// We crop the bottom from 85 degrees down to about 57 degrees (489 pixels)
|
||||
private static final int IMG_Y_OFF = -283;
|
||||
// center text on the spot
|
||||
private static final int TEXT_Y_OFF = 5;
|
||||
private static final Color TEXT_COLOR = new Color(255, 0, 0);
|
||||
private static final String FONT_NAME = "Dialog";
|
||||
private static final int FONT_STYLE = Font.BOLD;
|
||||
private static final int FONT_SIZE = 12;
|
||||
private static final Color CIRCLE_BORDER_COLOR = new Color(192, 0, 0, 192);
|
||||
private static final Color CIRCLE_COLOR = new Color(160, 0, 0, 128);
|
||||
private static final double CIRCLE_SIZE_FACTOR = 4.0;
|
||||
private static final int MIN_CIRCLE_SIZE = 5;
|
||||
private static final Color SQUARE_BORDER_COLOR = new Color(0, 0, 0);
|
||||
private static final Color SQUARE_COLOR = new Color(255, 50, 255, 160);
|
||||
private static final Color EXPL_COLOR = new Color(255, 100, 0);
|
||||
private static final Color CLIENT_COLOR = new Color(255, 160, 160);
|
||||
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public MapMaker() {
|
||||
this(ContextHelper.getContext(null));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public MapMaker(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(MapMaker.class);
|
||||
}
|
||||
|
||||
/*
|
||||
private static class LatLong {
|
||||
public final float lat, lon;
|
||||
public LatLong(float lat, float lon) {
|
||||
this.lat = lat; this.lon = lon;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private static class Mercator {
|
||||
public final int x, y;
|
||||
public Mercator(int x, int y) {
|
||||
this.x = x; this.y = y;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return x + y;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
Mercator m = (Mercator) o;
|
||||
return x == m.x && y == m.y;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private static class DummyImageObserver implements ImageObserver {
|
||||
public boolean imageUpdate(Image imgs, int infoflags, int x, int y, int width, int height) { return false; }
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param mode ignored for now, could be different for tunnels or routers or specific subsets
|
||||
*/
|
||||
public boolean render(int mode, OutputStream out) throws IOException {
|
||||
if (_mercator.isEmpty()) {
|
||||
_log.warn("mercator file not found");
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
// Putting the map and the overlay in the same image makes it large.
|
||||
// Now we load the map separately and overlay the circles and lines with CSS.
|
||||
// map source https://github.com/mfeldheim/hermap
|
||||
InputStream is = MapMaker.class.getResourceAsStream("/net/i2p/router/web/resources/" + BASEMAP_DEFAULT);
|
||||
if (is == null) {
|
||||
_log.warn("base map not found");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
ObjectCounterUnsafe<String> countries = new ObjectCounterUnsafe<String>();
|
||||
for (RouterInfo ri : _context.netDb().getRouters()) {
|
||||
Hash key = ri.getIdentity().getHash();
|
||||
String country = _context.commSystem().getCountry(key);
|
||||
if (country != null)
|
||||
countries.increment(country);
|
||||
}
|
||||
Set<String> counts = countries.objects();
|
||||
|
||||
//BufferedImage bi = ImageIO.read(is);
|
||||
//is.close();
|
||||
BufferedImage bi = new BufferedImage(WIDTH, MAP_HEIGHT, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = bi.createGraphics();
|
||||
//g.drawImage(base, 0, 0, new DummyImageObserver());
|
||||
Font large = new Font(FONT_NAME, FONT_STYLE, FONT_SIZE);
|
||||
g.setFont(large);
|
||||
g.setBackground(TRANSPARENT);
|
||||
g.setPaint(TEXT_COLOR);
|
||||
g.setStroke(new BasicStroke(1));
|
||||
|
||||
for (String c : countries.objects()) {
|
||||
Mercator m = _mercator.get(c);
|
||||
if (m == null)
|
||||
continue;
|
||||
int count = countries.count(c);
|
||||
int sz = Math.max(MIN_CIRCLE_SIZE, (int) (CIRCLE_SIZE_FACTOR * Math.sqrt(count)));
|
||||
drawCircle(g, rotate(m.x), m.y + IMG_Y_OFF, sz);
|
||||
c = c.toUpperCase(Locale.US);
|
||||
double width = getStringWidth(c, large, g);
|
||||
int xoff = (int) (width / 2);
|
||||
g.drawString(c.toUpperCase(Locale.US), rotate(m.x) - xoff, m.y + IMG_Y_OFF + TEXT_Y_OFF);
|
||||
}
|
||||
|
||||
String us = _context.commSystem().getOurCountry();
|
||||
if (us != null) {
|
||||
Mercator mus = _mercator.get(us);
|
||||
if (mus != null) {
|
||||
drawSquare(g, rotate(mus.x), mus.y + IMG_Y_OFF, 24);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.setStroke(new BasicStroke(2));
|
||||
TunnelManagerFacade tm = _context.tunnelManager();
|
||||
renderPool(g, mus, tm.getInboundExploratoryPool(), EXPL_COLOR);
|
||||
renderPool(g, mus, tm.getOutboundExploratoryPool(), EXPL_COLOR);
|
||||
Map<Hash, TunnelPool> pools = tm.getInboundClientPools();
|
||||
// TODO skip aliases
|
||||
for (TunnelPool tp : pools.values()) {
|
||||
renderPool(g, mus, tp, CLIENT_COLOR);
|
||||
}
|
||||
pools = tm.getOutboundClientPools();
|
||||
for (TunnelPool tp : pools.values()) {
|
||||
renderPool(g, mus, tp, CLIENT_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageOutputStream ios = new MemoryCacheImageOutputStream(out);
|
||||
ImageIO.write(bi, "png", ios);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw circle centered on x,y with a radius given
|
||||
*/
|
||||
private void drawCircle(Graphics2D g, int x, int y, int radius) {
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
Color c = g.getColor();
|
||||
g.setColor(CIRCLE_BORDER_COLOR);
|
||||
g.drawArc(x - radius, y - radius, radius * 2, radius * 2, 0, 360);
|
||||
g.setColor(CIRCLE_COLOR);
|
||||
g.fillArc(x - radius, y - radius, radius * 2, radius * 2, 0, 360);
|
||||
g.setColor(c);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw square centered on x,y with a width/height given
|
||||
*/
|
||||
private void drawSquare(Graphics2D g, int x, int y, int sz) {
|
||||
Color c = g.getColor();
|
||||
g.setColor(SQUARE_BORDER_COLOR);
|
||||
g.drawRect(x - (sz/2), y - (sz/2), sz, sz);
|
||||
g.setColor(SQUARE_COLOR);
|
||||
g.fillRect(x - (sz/2), y - (sz/2), sz, sz);
|
||||
g.setColor(c);
|
||||
}
|
||||
|
||||
private void renderPool(Graphics2D g, Mercator mus, TunnelPool tp, Color color) {
|
||||
Color c = g.getColor();
|
||||
g.setColor(color);
|
||||
List<TunnelInfo> tunnels = tp.listTunnels();
|
||||
List<Mercator> hops = new ArrayList<Mercator>(8);
|
||||
int[] x = new int[8];
|
||||
int[] y = new int[8];
|
||||
for (TunnelInfo info : tunnels) {
|
||||
int length = info.getLength();
|
||||
if (length < 2)
|
||||
continue;
|
||||
boolean isInbound = info.isInbound();
|
||||
// gateway first
|
||||
for (int j = 0; j < length; j++) {
|
||||
Mercator m;
|
||||
if (isInbound && j == length - 1) {
|
||||
m = mus;
|
||||
} else if (!isInbound && j == 0) {
|
||||
m = mus;
|
||||
} else {
|
||||
Hash peer = info.getPeer(j);
|
||||
String country = _context.commSystem().getCountry(peer);
|
||||
if (country == null)
|
||||
continue;
|
||||
Mercator mc = _mercator.get(country);
|
||||
if (mc == null)
|
||||
continue;
|
||||
m = mc;
|
||||
}
|
||||
if (hops.isEmpty() || !m.equals(hops.get(hops.size() - 1))) {
|
||||
hops.add(m);
|
||||
}
|
||||
}
|
||||
int sz = hops.size();
|
||||
if (sz > 1) {
|
||||
for (int i = 0; i < sz; i++) {
|
||||
Mercator m = hops.get(i);
|
||||
x[i] = rotate(m.x);
|
||||
y[i] = m.y + IMG_Y_OFF;
|
||||
}
|
||||
g.drawPolyline(x, y, sz);
|
||||
}
|
||||
hops.clear();
|
||||
}
|
||||
g.setColor(c);
|
||||
}
|
||||
|
||||
private static double getStringWidth(String text, Font font, Graphics2D g) {
|
||||
return font.getStringBounds(text, 0, text.length(), g.getFontRenderContext()).getBounds().getWidth();
|
||||
}
|
||||
|
||||
private static int rotate(int x) {
|
||||
x += IMG_X_OFF;
|
||||
if (x < 0)
|
||||
x += WIDTH;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in and parse the mercator country file.
|
||||
* The file need not be sorted.
|
||||
* This file was created from the lat/long data at
|
||||
* https://developers.google.com/public-data/docs/canonical/countries_csv
|
||||
* using the convertLatLongFile() method below.
|
||||
*/
|
||||
private static void readMercatorFile() {
|
||||
InputStream is = MapMaker.class.getResourceAsStream("/net/i2p/router/web/resources/" + MERCATOR_DEFAULT);
|
||||
if (is == null) {
|
||||
System.out.println("Country file not found");
|
||||
return;
|
||||
}
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
||||
String line = null;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
try {
|
||||
if (line.charAt(0) == '#')
|
||||
continue;
|
||||
String[] s = DataHelper.split(line, ",", 3);
|
||||
if (s.length < 3)
|
||||
continue;
|
||||
int x = Integer.parseInt(s[1]);
|
||||
int y = Integer.parseInt(s[2]);
|
||||
_mercator.put(s[0], new Mercator(x, y));
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.out.println("Bad line " + nfe);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Error reading the Country File " + ioe);
|
||||
} finally {
|
||||
if (is != null) try { is.close(); } catch (IOException ioe) {}
|
||||
if (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in and parse the lat/long file.
|
||||
* The file need not be sorted.
|
||||
* Convert the lat/long data from
|
||||
* https://developers.google.com/public-data/docs/canonical/countries_csv
|
||||
* to a 1200x1200 web mercator (85 degree) format.
|
||||
* latlong.csv input format: XX,lat,long,countryname (lat and long are signed floats)
|
||||
* mercator.txt output format: xx,x,y (x and y are integers 0-1200, not adjusted for a cropped projection)
|
||||
* Output is sorted by country code.
|
||||
*/
|
||||
/****
|
||||
private static void convertLatLongFile() {
|
||||
Map<String, LatLong> latlong = new HashMap<String, LatLong>();
|
||||
InputStream is = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
is = new FileInputStream(LATLONG_DEFAULT);
|
||||
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
||||
String line = null;
|
||||
while ( (line = br.readLine()) != null) {
|
||||
try {
|
||||
if (line.charAt(0) == '#')
|
||||
continue;
|
||||
String[] s = DataHelper.split(line, ",", 4);
|
||||
if (s.length < 3)
|
||||
continue;
|
||||
String lc = s[0].toLowerCase(Locale.US);
|
||||
float lat = Float.parseFloat(s[1]);
|
||||
float lon = Float.parseFloat(s[2]);
|
||||
latlong.put(lc, new LatLong(lat, lon));
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.out.println("Bad line " + nfe);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("Error reading the Country File " + ioe);
|
||||
} finally {
|
||||
if (is != null) try { is.close(); } catch (IOException ioe) {}
|
||||
if (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
Map<String, Mercator> mercator = new TreeMap<String, Mercator>();
|
||||
for (Map.Entry<String, LatLong> e : latlong.entrySet()) {
|
||||
String c = e.getKey();
|
||||
LatLong ll = e.getValue();
|
||||
mercator.put(c, convert(ll));
|
||||
}
|
||||
for (Map.Entry<String, Mercator> e : mercator.entrySet()) {
|
||||
String c = e.getKey();
|
||||
Mercator m = e.getValue();
|
||||
System.out.println(c + ',' + m.x + ',' + m.y);
|
||||
}
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/57322997/convert-geolocation-to-pixels-on-a-mercator-projection-image
|
||||
*/
|
||||
/****
|
||||
private static Mercator convert(LatLong latlong) {
|
||||
double rad = latlong.lat * Math.PI / 180;
|
||||
double mercn = Math.log(Math.tan((Math.PI / 4) + (rad / 2)));
|
||||
double x = (latlong.lon + 180d) * (WIDTH / 360d);
|
||||
double y = (HEIGHT / 2d) - ((WIDTH * mercn) / (2 * Math.PI));
|
||||
return new Mercator((int) Math.round(x), (int) Math.round(y));
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
convertLatLongFile();
|
||||
}
|
||||
****/
|
||||
}
|
@ -56,6 +56,7 @@ import net.i2p.util.Addresses;
|
||||
import net.i2p.util.ConvertToHash;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.ObjectCounterUnsafe;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.Translate;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
@ -1021,7 +1022,15 @@ class NetDbRenderer {
|
||||
// the summary table
|
||||
buf.append("<table id=\"netdboverview\" border=\"0\" cellspacing=\"30\"><tr><th colspan=\"3\">");
|
||||
buf.append(_t("Network Database Router Statistics"));
|
||||
buf.append("</th></tr><tr><td style=\"vertical-align: top;\">");
|
||||
buf.append("</th></tr>");
|
||||
if (!SystemVersion.isSlow() && !_context.commSystem().isDummy()) {
|
||||
// https://stackoverflow.com/questions/48474/how-do-i-position-one-image-on-top-of-another-in-html
|
||||
buf.append("<tr><td class=\"mapcontainer\" colspan=\"3\">" +
|
||||
"<img class=\"mapbase\" src=\"/themes/console/images/mapbase72.png\" width=\"1600\" height=\"828\">" +
|
||||
"<img class=\"mapoverlay\" src=\"viewmap.jsp\" width=\"1600\" height=\"828\">" +
|
||||
"</td></tr>");
|
||||
}
|
||||
buf.append("<tr><td style=\"vertical-align: top;\">");
|
||||
// versions table
|
||||
List<String> versionList = new ArrayList<String>(versions.objects());
|
||||
if (!versionList.isEmpty()) {
|
||||
|
@ -4643,6 +4643,24 @@ ul#upnphelp li:last-child,
|
||||
|
||||
/* netdb (main section) */
|
||||
|
||||
.mapcontainer {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.mapbase {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.mapoverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
table#netdboverview {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
BIN
apps/routerconsole/jsp/themes/console/images/mapbase72.png
Normal file
BIN
apps/routerconsole/jsp/themes/console/images/mapbase72.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
@ -5811,6 +5811,24 @@ tt a {
|
||||
|
||||
/* netdb */
|
||||
|
||||
.mapcontainer {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.mapbase {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.mapoverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.confignav+.netdbentry {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
22
apps/routerconsole/jsp/viewmap.jsp
Normal file
22
apps/routerconsole/jsp/viewmap.jsp
Normal file
@ -0,0 +1,22 @@
|
||||
<%
|
||||
/*
|
||||
* USE CAUTION WHEN EDITING
|
||||
* Trailing whitespace OR NEWLINE on the last line will cause
|
||||
* IllegalStateExceptions !!!
|
||||
*
|
||||
* Do not tag this file for translation.
|
||||
*/
|
||||
response.setContentType("image/png");
|
||||
response.setHeader("Content-Disposition", "inline; filename=\"i2pmap.png\"");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Accept-Ranges", "none");
|
||||
response.setHeader("Connection", "Close");
|
||||
java.io.OutputStream cout = response.getOutputStream();
|
||||
net.i2p.router.web.helpers.MapMaker mm = new net.i2p.router.web.helpers.MapMaker();
|
||||
boolean rendered = mm.render(0, cout);
|
||||
|
||||
if (rendered)
|
||||
cout.close();
|
||||
else
|
||||
response.sendError(403, "Map not available");
|
||||
%>
|
244
apps/routerconsole/resources/mercator.txt
Normal file
244
apps/routerconsole/resources/mercator.txt
Normal file
@ -0,0 +1,244 @@
|
||||
ad,807,591
|
||||
ae,1039,693
|
||||
af,1101,639
|
||||
ag,525,723
|
||||
ai,520,718
|
||||
al,890,599
|
||||
am,1000,605
|
||||
an,493,745
|
||||
ao,879,850
|
||||
aq,800,1321
|
||||
ar,517,985
|
||||
as,44,864
|
||||
at,865,559
|
||||
au,1395,916
|
||||
aw,489,744
|
||||
az,1011,605
|
||||
ba,879,582
|
||||
bb,535,741
|
||||
bd,1202,692
|
||||
be,820,539
|
||||
bf,793,745
|
||||
bg,913,590
|
||||
bh,1025,681
|
||||
bi,933,815
|
||||
bj,810,758
|
||||
bm,512,648
|
||||
bn,1310,780
|
||||
bo,517,873
|
||||
br,569,864
|
||||
bs,456,685
|
||||
bt,1202,673
|
||||
bv,815,1089
|
||||
bw,910,902
|
||||
by,924,516
|
||||
bz,407,722
|
||||
ca,327,497
|
||||
cc,1231,854
|
||||
cd,897,818
|
||||
cf,893,771
|
||||
cg,870,801
|
||||
ch,837,564
|
||||
ci,775,766
|
||||
ck,90,897
|
||||
cl,482,970
|
||||
cm,855,767
|
||||
cn,1263,629
|
||||
co,470,780
|
||||
cr,428,756
|
||||
cu,454,702
|
||||
cv,693,728
|
||||
cx,1270,847
|
||||
cy,949,633
|
||||
cz,869,544
|
||||
de,846,534
|
||||
dj,989,747
|
||||
dk,842,496
|
||||
dm,527,731
|
||||
do,488,715
|
||||
dz,807,670
|
||||
ec,453,808
|
||||
ee,911,477
|
||||
eg,937,676
|
||||
eh,743,689
|
||||
er,977,732
|
||||
es,783,603
|
||||
et,980,759
|
||||
fi,914,447
|
||||
fj,1597,875
|
||||
fk,535,1070
|
||||
fm,1469,767
|
||||
fo,769,447
|
||||
fr,810,568
|
||||
ga,852,804
|
||||
gb,785,503
|
||||
gd,526,745
|
||||
ge,993,592
|
||||
gf,564,783
|
||||
gg,789,546
|
||||
gh,795,765
|
||||
gi,776,628
|
||||
gl,611,335
|
||||
gm,732,740
|
||||
gn,757,756
|
||||
gp,524,723
|
||||
gq,846,793
|
||||
gr,897,611
|
||||
gs,637,1090
|
||||
gt,399,729
|
||||
gu,1444,740
|
||||
gw,733,747
|
||||
gy,538,778
|
||||
gz,952,653
|
||||
hk,1307,698
|
||||
hm,1127,1079
|
||||
hn,417,732
|
||||
hr,868,575
|
||||
ht,479,714
|
||||
hu,887,562
|
||||
id,1306,804
|
||||
ie,763,518
|
||||
il,955,655
|
||||
im,780,512
|
||||
in,1151,706
|
||||
io,1119,828
|
||||
iq,994,643
|
||||
ir,1039,648
|
||||
is,715,417
|
||||
it,856,595
|
||||
je,791,548
|
||||
jm,456,718
|
||||
jo,961,657
|
||||
jp,1414,627
|
||||
ke,968,800
|
||||
kg,1132,599
|
||||
kh,1267,744
|
||||
ki,50,815
|
||||
km,995,853
|
||||
kn,521,722
|
||||
kp,1367,604
|
||||
kr,1368,629
|
||||
kw,1011,664
|
||||
ky,442,712
|
||||
kz,1097,556
|
||||
la,1256,710
|
||||
lb,959,640
|
||||
lc,529,738
|
||||
li,842,562
|
||||
lk,1159,765
|
||||
lr,758,771
|
||||
ls,925,938
|
||||
lt,906,505
|
||||
lu,827,544
|
||||
lv,909,491
|
||||
ly,877,679
|
||||
ma,768,651
|
||||
mc,833,583
|
||||
md,926,560
|
||||
me,886,590
|
||||
mg,1008,885
|
||||
mh,1561,768
|
||||
mk,897,596
|
||||
ml,782,721
|
||||
mm,1226,700
|
||||
mn,1262,564
|
||||
mo,1305,699
|
||||
mp,1446,722
|
||||
mq,529,734
|
||||
mr,751,704
|
||||
ms,524,725
|
||||
mt,864,629
|
||||
mu,1056,892
|
||||
mv,1125,786
|
||||
mw,952,859
|
||||
mx,344,692
|
||||
my,1253,781
|
||||
mz,958,884
|
||||
na,882,905
|
||||
nc,1536,895
|
||||
ne,836,720
|
||||
nf,1546,935
|
||||
ng,839,759
|
||||
ni,421,742
|
||||
nl,824,528
|
||||
no,838,460
|
||||
np,1174,668
|
||||
nr,1542,802
|
||||
nu,45,886
|
||||
nz,1577,1000
|
||||
om,1049,702
|
||||
pa,441,762
|
||||
pe,467,841
|
||||
pf,136,880
|
||||
pg,1440,828
|
||||
ph,1341,742
|
||||
pk,1108,658
|
||||
pl,885,529
|
||||
pm,550,563
|
||||
pn,234,913
|
||||
pr,504,718
|
||||
ps,957,650
|
||||
pt,763,609
|
||||
pw,1398,767
|
||||
py,540,907
|
||||
qa,1027,683
|
||||
re,1047,896
|
||||
ro,911,570
|
||||
rs,893,582
|
||||
ru,1268,451
|
||||
rw,933,809
|
||||
sa,1000,691
|
||||
sb,1512,843
|
||||
sc,1047,821
|
||||
sd,934,742
|
||||
se,883,463
|
||||
sg,1261,794
|
||||
sh,755,911
|
||||
si,867,568
|
||||
sj,905,236
|
||||
sk,888,552
|
||||
sl,748,762
|
||||
sm,855,582
|
||||
sn,736,735
|
||||
so,1005,777
|
||||
sr,551,783
|
||||
st,829,799
|
||||
sv,405,738
|
||||
sy,973,635
|
||||
sz,940,922
|
||||
tc,481,701
|
||||
td,883,730
|
||||
tf,1108,1052
|
||||
tg,804,762
|
||||
th,1249,729
|
||||
tj,1117,612
|
||||
tk,36,840
|
||||
tl,1359,840
|
||||
tm,1065,612
|
||||
tn,842,640
|
||||
to,21,896
|
||||
tr,957,612
|
||||
tt,528,752
|
||||
tv,1590,832
|
||||
tw,1338,692
|
||||
tz,955,828
|
||||
ua,939,554
|
||||
ug,944,794
|
||||
us,375,622
|
||||
uy,552,953
|
||||
uz,1087,598
|
||||
va,855,595
|
||||
vc,528,742
|
||||
ve,504,771
|
||||
vg,513,717
|
||||
vi,512,717
|
||||
vn,1281,737
|
||||
vu,1542,869
|
||||
wf,13,862
|
||||
ws,35,862
|
||||
xk,893,590
|
||||
ye,1016,730
|
||||
yt,1001,857
|
||||
za,902,943
|
||||
zm,924,859
|
||||
zw,930,886
|
Reference in New Issue
Block a user