├── .gitignore ├── gradle.properties ├── settings.gradle ├── .travis.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── java │ │ └── uk │ │ │ └── recurse │ │ │ └── geocoding │ │ │ └── reverse │ │ │ ├── Geometry.java │ │ │ ├── Point.java │ │ │ ├── FeatureCollection.java │ │ │ ├── Feature.java │ │ │ ├── Polygon.java │ │ │ ├── BoundingBox.java │ │ │ ├── MultiPolygon.java │ │ │ ├── Ring.java │ │ │ ├── SortTileRecursive.java │ │ │ ├── ReverseGeocoder.java │ │ │ └── Country.java │ └── resources │ │ └── countryInfo.txt ├── test │ └── java │ │ └── uk │ │ └── recurse │ │ └── geocoding │ │ └── reverse │ │ ├── ReverseGeocoderTest.java │ │ ├── CountryTest.java │ │ ├── BaselineGenerator.java │ │ └── SortTileRecursiveTest.java └── jmh │ └── java │ └── uk │ └── recurse │ └── geocoding │ └── reverse │ └── Benchmarks.java ├── README.md ├── gradlew.bat ├── gradlew └── LICENCE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.0.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'reverse-country-code' 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | after_success: 5 | - ./gradlew jacocoTestReport coveralls 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencampion/reverse-country-code/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/Geometry.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.annotation.JsonSubTypes; 4 | import com.fasterxml.jackson.annotation.JsonSubTypes.Type; 5 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 6 | import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; 7 | 8 | import java.util.stream.Stream; 9 | 10 | @JsonTypeInfo(use = Id.NAME, property = "type") 11 | @JsonSubTypes({@Type(Polygon.class), @Type(MultiPolygon.class)}) 12 | interface Geometry { 13 | 14 | boolean contains(float lat, float lon); 15 | 16 | Country getCountry(float lat, float lon); 17 | 18 | BoundingBox boundingBox(); 19 | 20 | Stream flatten(Country country); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/Point.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import com.fasterxml.jackson.annotation.JsonFormat.Shape; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 8 | 9 | @JsonFormat(shape = Shape.ARRAY) 10 | @JsonPropertyOrder({"x", "y"}) 11 | class Point { 12 | 13 | private final float lat; 14 | private final float lon; 15 | 16 | @JsonCreator 17 | Point(@JsonProperty("y") float lat, @JsonProperty("x") float lon) { 18 | this.lat = lat; 19 | this.lon = lon; 20 | } 21 | 22 | float latitude() { 23 | return lat; 24 | } 25 | 26 | float longitude() { 27 | return lon; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/FeatureCollection.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import java.util.stream.Stream; 8 | 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | class FeatureCollection { 11 | 12 | private final Geometry world; 13 | private final Country[] countries; 14 | 15 | @JsonCreator 16 | FeatureCollection(@JsonProperty("features") Feature[] features) { 17 | world = SortTileRecursive.pack(Stream.of(features).flatMap(Feature::geometries)); 18 | countries = Stream.of(features) 19 | .map(Feature::country) 20 | .toArray(Country[]::new); 21 | } 22 | 23 | Country getCountry(float lat, float lon) { 24 | return world.getCountry(lat, lon); 25 | } 26 | 27 | Stream countries() { 28 | return Stream.of(countries); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/Feature.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.annotation.JacksonInject; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | 8 | import java.util.Map; 9 | import java.util.stream.Stream; 10 | 11 | @JsonIgnoreProperties(ignoreUnknown = true) 12 | class Feature { 13 | 14 | private final Country country; 15 | private final Geometry geometry; 16 | 17 | @JsonCreator 18 | Feature( 19 | @JacksonInject Map countries, 20 | @JsonProperty("properties") Map properties, 21 | @JsonProperty("geometry") Geometry geometry 22 | ) { 23 | String id = properties.get("geoNameId"); 24 | country = countries.get(id); 25 | this.geometry = geometry; 26 | } 27 | 28 | Country country() { 29 | return country; 30 | } 31 | 32 | Stream geometries() { 33 | return geometry.flatten(country); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/uk/recurse/geocoding/reverse/ReverseGeocoderTest.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.params.ParameterizedTest; 6 | import org.junit.jupiter.params.provider.CsvFileSource; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.fail; 10 | 11 | class ReverseGeocoderTest { 12 | 13 | private static ReverseGeocoder geocoder; 14 | 15 | @BeforeAll 16 | static void setup() { 17 | geocoder = new ReverseGeocoder(); 18 | } 19 | 20 | @ParameterizedTest 21 | @CsvFileSource(resources = "/baselineCities.csv") 22 | void reverseGeocoding(float lat, float lon, String expectedIso) { 23 | String actualIso = geocoder.getCountry(lat, lon) 24 | .map(Country::iso) 25 | .orElseGet(() -> fail("Country not found")); 26 | 27 | assertEquals(expectedIso, actualIso, "lat=" + lat + " lon=" + lon); 28 | } 29 | 30 | @Test 31 | void streaming() { 32 | assertEquals(247, geocoder.countries().count()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/Polygon.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.util.stream.Stream; 7 | 8 | class Polygon implements Geometry { 9 | 10 | private final Ring ring; 11 | private final Geometry holes; 12 | private final Country country; 13 | 14 | @JsonCreator 15 | Polygon(@JsonProperty("coordinates") Ring[] rings) { 16 | this(rings[0], SortTileRecursive.pack(Stream.of(rings).skip(1)), null); 17 | } 18 | 19 | private Polygon(Ring ring, Geometry holes, Country country) { 20 | this.ring = ring; 21 | this.holes = holes; 22 | this.country = country; 23 | } 24 | 25 | @Override 26 | public boolean contains(float lat, float lon) { 27 | return ring.contains(lat, lon) && !holes.contains(lat, lon); 28 | } 29 | 30 | @Override 31 | public Country getCountry(float lat, float lon) { 32 | return contains(lat, lon) ? country : null; 33 | } 34 | 35 | @Override 36 | public BoundingBox boundingBox() { 37 | return ring.boundingBox(); 38 | } 39 | 40 | @Override 41 | public Stream flatten(Country country) { 42 | return Stream.of(new Polygon(ring, holes, country)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/BoundingBox.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import java.util.DoubleSummaryStatistics; 4 | import java.util.stream.Stream; 5 | 6 | class BoundingBox { 7 | 8 | private final Point max; 9 | private final Point min; 10 | 11 | BoundingBox(Geometry[] geometries) { 12 | this(Stream.of(geometries).map(Geometry::boundingBox).flatMap(BoundingBox::points)); 13 | } 14 | 15 | BoundingBox(Stream points) { 16 | DoubleSummaryStatistics lat = new DoubleSummaryStatistics(); 17 | DoubleSummaryStatistics lon = new DoubleSummaryStatistics(); 18 | points.forEach(point -> { 19 | lat.accept(point.latitude()); 20 | lon.accept(point.longitude()); 21 | }); 22 | max = new Point((float) lat.getMax(), (float) lon.getMax()); 23 | min = new Point((float) lat.getMin(), (float) lon.getMin()); 24 | } 25 | 26 | boolean contains(float lat, float lon) { 27 | return lat <= max.latitude() && lon <= max.longitude() 28 | && lat >= min.latitude() && lon >= min.longitude(); 29 | } 30 | 31 | float centroidLatitude() { 32 | return (max.latitude() + min.latitude()) / 2; 33 | } 34 | 35 | float centroidLongitude() { 36 | return (max.longitude() + min.longitude()) / 2; 37 | } 38 | 39 | private Stream points() { 40 | return Stream.of(max, min); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/MultiPolygon.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.util.stream.Stream; 7 | 8 | class MultiPolygon implements Geometry { 9 | 10 | private final Geometry[] geometries; 11 | private final BoundingBox boundingBox; 12 | 13 | @JsonCreator 14 | MultiPolygon(@JsonProperty("coordinates") Ring[][] rings) { 15 | this(Stream.of(rings).map(Polygon::new).toArray(Geometry[]::new)); 16 | } 17 | 18 | MultiPolygon(Geometry[] geometries) { 19 | this.geometries = geometries; 20 | boundingBox = new BoundingBox(geometries); 21 | } 22 | 23 | @Override 24 | public boolean contains(float lat, float lon) { 25 | if (boundingBox.contains(lat, lon)) { 26 | for (Geometry geometry : geometries) { 27 | if (geometry.contains(lat, lon)) { 28 | return true; 29 | } 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | @Override 36 | public Country getCountry(float lat, float lon) { 37 | if (boundingBox.contains(lat, lon)) { 38 | for (Geometry geometry : geometries) { 39 | Country country = geometry.getCountry(lat, lon); 40 | if (country != null) { 41 | return country; 42 | } 43 | } 44 | } 45 | return null; 46 | } 47 | 48 | @Override 49 | public BoundingBox boundingBox() { 50 | return boundingBox; 51 | } 52 | 53 | @Override 54 | public Stream flatten(Country country) { 55 | return Stream.of(geometries).flatMap(geometry -> geometry.flatten(country)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/uk/recurse/geocoding/reverse/CountryTest.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.StringReader; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.Locale; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class CountryTest { 14 | 15 | private static final String INPUT = "GB\tGBR\t826\tUK\tUnited Kingdom\tLondon\t244820\t62348447\tEU\t.uk\tGBP\tPound\t44\t@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA\t^((?:(?:[A-PR-UWYZ][A-HK-Y]\\d[ABEHMNPRV-Y0-9]|[A-PR-UWYZ]\\d[A-HJKPS-UW0-9])\\s\\d[ABD-HJLNP-UW-Z]{2})|GIR\\s?0AA)$\ten-GB,cy-GB,gd\t2635167\tIE"; 16 | 17 | private static Country uk; 18 | 19 | @BeforeAll 20 | static void setup() { 21 | uk = Country.load(new StringReader(INPUT)).get("2635167"); 22 | } 23 | 24 | @Test 25 | void iso() { 26 | assertEquals(uk.iso(), "GB"); 27 | } 28 | 29 | @Test 30 | void iso3() { 31 | assertEquals(uk.iso3(), "GBR"); 32 | } 33 | 34 | @Test 35 | void isoNumeric() { 36 | assertEquals(uk.isoNumeric(), 826); 37 | } 38 | 39 | @Test 40 | void name() { 41 | assertEquals(uk.name(), "United Kingdom"); 42 | } 43 | 44 | @Test 45 | void area() { 46 | assertEquals(uk.area(), 244820f); 47 | } 48 | 49 | @Test 50 | void population() { 51 | assertEquals(uk.population(), 62348447); 52 | } 53 | 54 | @Test 55 | void continent() { 56 | assertEquals(uk.continent(), "EU"); 57 | } 58 | 59 | @Test 60 | void locales() { 61 | List locales = Arrays.asList( 62 | new Locale("en","GB"), 63 | new Locale("cy", "GB"), 64 | new Locale("gd") 65 | ); 66 | assertEquals(uk.locales(), locales); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/Ring.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 | 6 | import java.util.stream.Stream; 7 | 8 | @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) 9 | class Ring implements Geometry { 10 | 11 | private final float[] latitude; 12 | private final float[] longitude; 13 | private final BoundingBox boundingBox; 14 | 15 | @JsonCreator 16 | Ring(Point... points) { 17 | latitude = new float[points.length]; 18 | longitude = new float[points.length]; 19 | for (int i = 0; i < points.length; i++) { 20 | latitude[i] = points[i].latitude(); 21 | longitude[i] = points[i].longitude(); 22 | } 23 | boundingBox = new BoundingBox(Stream.of(points)); 24 | } 25 | 26 | @Override 27 | public boolean contains(float lat, float lon) { 28 | return boundingBox.contains(lat, lon) && pnpoly(lat, lon); 29 | } 30 | 31 | @Override 32 | public Country getCountry(float lat, float lon) { 33 | throw new UnsupportedOperationException(); 34 | } 35 | 36 | @Override 37 | public BoundingBox boundingBox() { 38 | return boundingBox; 39 | } 40 | 41 | @Override 42 | public Stream flatten(Country country) { 43 | throw new UnsupportedOperationException(); 44 | } 45 | 46 | // algorithm notes: https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html 47 | private boolean pnpoly(float lat, float lon) { 48 | boolean contains = false; 49 | for (int i = 0, j = latitude.length - 1; i < latitude.length; j = i++) { 50 | if (((latitude[i] > lat) != (latitude[j] > lat)) 51 | && (lon < (longitude[j] - longitude[i]) 52 | * (lat - latitude[i]) 53 | / (latitude[j] - latitude[i]) 54 | + longitude[i])) { 55 | contains = !contains; 56 | } 57 | } 58 | return contains; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/uk/recurse/geocoding/reverse/BaselineGenerator.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.io.Writer; 8 | import java.net.URL; 9 | import java.nio.file.Files; 10 | import java.nio.file.Paths; 11 | import java.util.Iterator; 12 | import java.util.Optional; 13 | import java.util.zip.ZipInputStream; 14 | 15 | import static java.nio.charset.StandardCharsets.UTF_8; 16 | 17 | public class BaselineGenerator { 18 | 19 | public static void main(String[] args) throws IOException { 20 | ReverseGeocoder geocoder = new ReverseGeocoder(); 21 | int pass = 0; 22 | int fail = 0; 23 | try ( 24 | InputStream in = new URL("https://download.geonames.org/export/dump/cities15000.zip").openStream(); 25 | Writer baseline = Files.newBufferedWriter(Paths.get(args[0], "baselineCities.csv")) 26 | ) { 27 | ZipInputStream zipStream = new ZipInputStream(in); 28 | zipStream.getNextEntry(); 29 | BufferedReader reader = new BufferedReader(new InputStreamReader(zipStream, UTF_8)); 30 | for (Iterator lines = reader.lines().iterator(); lines.hasNext(); ) { 31 | String line = lines.next(); 32 | String[] row = line.split("\t"); 33 | float lat = Float.parseFloat(row[4]); 34 | float lon = Float.parseFloat(row[5]); 35 | String iso = row[8]; 36 | Optional output = geocoder.getCountry(lat, lon).map(Country::iso); 37 | if (output.filter(iso::equals).isPresent()) { 38 | baseline.write(String.join(",", row[4], row[5], output.get())); 39 | baseline.write('\n'); 40 | pass++; 41 | } else { 42 | fail++; 43 | } 44 | } 45 | } 46 | double percent = (double) pass / (pass + fail); 47 | System.out.printf("\nPassed=%d Failed=%d (%f%%)\n", pass, fail, percent * 100); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/uk/recurse/geocoding/reverse/SortTileRecursiveTest.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.List; 6 | import java.util.stream.IntStream; 7 | 8 | import static java.util.stream.Collectors.toList; 9 | import static org.junit.jupiter.api.Assertions.*; 10 | 11 | class SortTileRecursiveTest { 12 | 13 | @Test 14 | void given16Rectangles_sortTileRecursiveReturnsSameInput() { 15 | List rectangles = rectangles(16); 16 | 17 | List rTree = SortTileRecursive.pack(rectangles); 18 | 19 | assertSame(rectangles, rTree); 20 | } 21 | 22 | @Test 23 | void given17Rectangles_sortTileRecursiveReturnsTwoRectangles() { 24 | List rectangles = rectangles(17); 25 | 26 | List rTree = SortTileRecursive.pack(rectangles); 27 | 28 | assertAll( 29 | () -> assertEquals(2, rTree.size()), 30 | () -> assertEquals(4.0f, rTree.get(0).boundingBox().centroidLatitude()), 31 | () -> assertEquals(4.0f, rTree.get(0).boundingBox().centroidLongitude()), 32 | () -> assertEquals(12.5f, rTree.get(1).boundingBox().centroidLatitude()), 33 | () -> assertEquals(12.5f, rTree.get(1).boundingBox().centroidLongitude()) 34 | ); 35 | } 36 | 37 | @Test 38 | void given32Rectangles_sortTileRecursiveReturnsTwoRectangles() { 39 | List rectangles = rectangles(32); 40 | 41 | List rTree = SortTileRecursive.pack(rectangles); 42 | 43 | assertAll( 44 | () -> assertEquals(2, rTree.size()), 45 | () -> assertEquals(7.5f, rTree.get(0).boundingBox().centroidLatitude()), 46 | () -> assertEquals(7.5f, rTree.get(0).boundingBox().centroidLongitude()), 47 | () -> assertEquals(23.5f, rTree.get(1).boundingBox().centroidLatitude()), 48 | () -> assertEquals(23.5f, rTree.get(1).boundingBox().centroidLongitude()) 49 | ); 50 | } 51 | 52 | private List rectangles(int n) { 53 | return IntStream.iterate(0, i -> i + 1) 54 | .mapToObj(i -> new Point(i, i)) 55 | .map(Ring::new) 56 | .limit(n) 57 | .collect(toList()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/SortTileRecursive.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | import java.util.stream.Stream; 7 | 8 | import static java.util.stream.Collectors.toList; 9 | 10 | // algorithm paper: http://www.dtic.mil/dtic/tr/fulltext/u2/a324493.pdf 11 | class SortTileRecursive { 12 | 13 | private static final int PAGE_SIZE = 16; 14 | private static final Comparator X_COORDINATE = 15 | Comparator.comparingDouble(geometry -> geometry.boundingBox().centroidLongitude()); 16 | private static final Comparator Y_COORDINATE = 17 | Comparator.comparingDouble(geometry -> geometry.boundingBox().centroidLatitude()); 18 | 19 | static MultiPolygon pack(Stream rectangles) { 20 | List packed = pack(rectangles.collect(toList())); 21 | return new MultiPolygon(packed.toArray(new Geometry[0])); 22 | } 23 | 24 | static List pack(List rectangles) { 25 | int leafPages = ceilDiv(rectangles.size(), PAGE_SIZE); 26 | if (leafPages <= 1) { 27 | return rectangles; 28 | } 29 | int verticalSlices = ceilSqrt(leafPages); 30 | List nodes = new ArrayList<>(leafPages); 31 | rectangles.sort(X_COORDINATE); 32 | for (List verticalSlice : partition(rectangles, verticalSlices)) { 33 | verticalSlice.sort(Y_COORDINATE); 34 | int runs = ceilDiv(verticalSlice.size(), PAGE_SIZE); 35 | for (List run : partition(verticalSlice, runs)) { 36 | nodes.add(new MultiPolygon(run.toArray(new Geometry[0]))); 37 | } 38 | } 39 | return pack(nodes); 40 | } 41 | 42 | private static List> partition(List list, int n) { 43 | int size = ceilDiv(list.size(), n); 44 | List> partitions = new ArrayList<>(n); 45 | for (int i = 0; i < n; i++) { 46 | int start = i * size; 47 | int end = Math.min(start + size, list.size()); 48 | partitions.add(list.subList(start, end)); 49 | } 50 | return partitions; 51 | } 52 | 53 | private static int ceilDiv(double x, double y) { 54 | return (int) Math.ceil(x / y); 55 | } 56 | 57 | private static int ceilSqrt(double a) { 58 | return (int) Math.ceil(Math.sqrt(a)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reverse Country Code [![Build Status](https://travis-ci.org/bencampion/reverse-country-code.svg?branch=master)](https://travis-ci.org/bencampion/reverse-country-code) [![Coverage Status](https://img.shields.io/coveralls/bencampion/reverse-country-code.svg)](https://coveralls.io/r/bencampion/reverse-country-code?branch=master) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/uk.recurse/reverse-country-code/badge.svg)](https://maven-badges.herokuapp.com/maven-central/uk.recurse/reverse-country-code) 2 | 3 | A reverse geocoder that converts latitude and longitude coordinates to country information such as names, ISO codes and locales. 4 | 5 | ## Usage 6 | 7 | ```java 8 | ReverseGeocoder geocoder = new ReverseGeocoder(); 9 | geocoder.getCountry(51.507222, -0.1275).ifPresent(country -> { 10 | System.out.println(country.iso()); // GB 11 | System.out.println(country.iso3()); // GBR 12 | System.out.println(country.isoNumeric()); // 826 13 | System.out.println(country.name()); // United Kingdom 14 | System.out.println(country.area()); // 244820.0 15 | System.out.println(country.population()); // 62348447 16 | System.out.println(country.continent()); // EU 17 | List locales = country.locales(); 18 | System.out.println(locales.get(0)); // en_GB 19 | System.out.println(locales.get(1)); // cy_GB 20 | System.out.println(locales.get(2)); // gd 21 | }); 22 | ``` 23 | 24 | ## Dataset 25 | 26 | Country information and boundary data comes from [GeoNames](http://download.geonames.org/export/dump/). 27 | 28 | ## Accuracy 29 | 30 | 97.92% when ran against the GeoNames data set for cities with a population > 15000. 31 | 32 | ## Performance 33 | 34 | ~3 μs average to look up city from the GeoNames data set on a 13" MacBook Pro (i5-5257U) using a single thread. The retained heap size for `ReverseGeocoder` is ~7.5 MB. 35 | 36 | ## Algorithms 37 | 38 | Country bounding boxes are loaded into [R-Trees](https://en.wikipedia.org/wiki/R-tree) using the [Sort-Tile-Recursive](http://www.dtic.mil/dtic/tr/fulltext/u2/a324493.pdf) algorithm. Determining if a point lies within a polygon is performed using the [PNPOLY](http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html) algorithm. 39 | 40 | ## Gradle tasks 41 | 42 | Build jar: 43 | 44 | ./gradlew assemble 45 | 46 | Download new Geonames data: 47 | 48 | ./gradlew updateCountryInfo updateShapes 49 | 50 | Run benchmarks: 51 | 52 | ./gradlew jmh 53 | 54 | Generate new baseline test data: 55 | 56 | ./gradlew generateBaseline 57 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/ReverseGeocoder.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import com.fasterxml.jackson.databind.InjectableValues; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.io.UncheckedIOException; 10 | import java.util.Map; 11 | import java.util.Optional; 12 | import java.util.stream.Stream; 13 | 14 | import static java.nio.charset.StandardCharsets.UTF_8; 15 | 16 | /** 17 | * A reverse geocoder that converts latitude and longitude coordinates to a country. 18 | */ 19 | public class ReverseGeocoder { 20 | 21 | private final FeatureCollection featureCollection; 22 | 23 | /** 24 | * Creates a new reverse geocoder. This is an expensive operation as the country boundary data 25 | * is parsed each time the class constructed. 26 | */ 27 | public ReverseGeocoder() { 28 | Class cls = ReverseGeocoder.class; 29 | try ( 30 | InputStream countryInfo = cls.getResourceAsStream("/countryInfo.txt"); 31 | InputStream shapes = cls.getResourceAsStream("/shapes_simplified_low.json") 32 | ) { 33 | featureCollection = load(countryInfo, shapes); 34 | } catch (IOException e) { 35 | throw new UncheckedIOException(e); 36 | } 37 | } 38 | 39 | private FeatureCollection load(InputStream countryInfo, InputStream shapes) throws IOException { 40 | Map countries = Country.load(new InputStreamReader(countryInfo, UTF_8)); 41 | InjectableValues injectables = new InjectableValues.Std().addValue(Map.class, countries); 42 | return new ObjectMapper() 43 | .readerFor(FeatureCollection.class) 44 | .with(injectables) 45 | .readValue(shapes); 46 | } 47 | 48 | /** 49 | * Converts a coordinate into a country. 50 | * 51 | * @param lat degrees latitude 52 | * @param lon degrees longitude 53 | * @return the country at the given coordinate 54 | */ 55 | public Optional getCountry(double lat, double lon) { 56 | return getCountry((float) lat, (float) lon); 57 | } 58 | 59 | /** 60 | * Converts a coordinate into a country. 61 | * 62 | * @param lat degrees latitude 63 | * @param lon degrees longitude 64 | * @return the country at the given coordinate 65 | */ 66 | public Optional getCountry(float lat, float lon) { 67 | return Optional.ofNullable(featureCollection.getCountry(lat, lon)); 68 | } 69 | 70 | /** 71 | * Returns all the countries recognised by the reverse geocoder. 72 | * 73 | * @return stream of countries 74 | */ 75 | public Stream countries() { 76 | return featureCollection.countries(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/jmh/java/uk/recurse/geocoding/reverse/Benchmarks.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import org.openjdk.jmh.annotations.Benchmark; 4 | import org.openjdk.jmh.annotations.BenchmarkMode; 5 | import org.openjdk.jmh.annotations.Mode; 6 | import org.openjdk.jmh.annotations.OutputTimeUnit; 7 | import org.openjdk.jmh.annotations.Scope; 8 | import org.openjdk.jmh.annotations.Setup; 9 | import org.openjdk.jmh.annotations.State; 10 | import org.openjdk.jmh.infra.Blackhole; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.InputStreamReader; 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.List; 19 | import java.util.PrimitiveIterator; 20 | import java.util.Random; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static java.nio.charset.StandardCharsets.UTF_8; 24 | import static java.util.stream.Collectors.toList; 25 | 26 | @BenchmarkMode(Mode.SingleShotTime) 27 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 28 | @State(Scope.Benchmark) 29 | public class Benchmarks { 30 | 31 | private ReverseGeocoder geocoder; 32 | private List cities; 33 | private List random; 34 | 35 | @Setup 36 | public void prepare() throws IOException { 37 | geocoder = new ReverseGeocoder(); 38 | cities = cityPoints(); 39 | Collections.shuffle(cities, new Random(0)); 40 | random = randomPoints(); 41 | } 42 | 43 | private List cityPoints() throws IOException { 44 | try (InputStream in = Benchmarks.class.getResourceAsStream("/cities.tsv")) { 45 | return new BufferedReader(new InputStreamReader(in, UTF_8)).lines() 46 | .map(line -> line.split("\t")) 47 | .map(row -> { 48 | float lat = Float.parseFloat(row[0]); 49 | float lon = Float.parseFloat(row[1]); 50 | return new Point(lat, lon); 51 | }) 52 | .collect(toList()); 53 | } 54 | } 55 | 56 | private List randomPoints() { 57 | List points = new ArrayList<>(cities.size()); 58 | PrimitiveIterator.OfDouble lats = new Random(0).doubles(-90, 90).iterator(); 59 | PrimitiveIterator.OfDouble lons = new Random(0).doubles(-180, 180).iterator(); 60 | for (int i = 0; i < cities.size(); i++) { 61 | float lat = (float) lats.nextDouble(); 62 | float lon = (float) lons.nextDouble(); 63 | points.add(new Point(lat, lon)); 64 | } 65 | return points; 66 | } 67 | 68 | @Benchmark 69 | public void cities(Blackhole bh) { 70 | for (Point point : cities) { 71 | bh.consume(geocoder.getCountry(point.latitude(), point.longitude())); 72 | } 73 | } 74 | 75 | @Benchmark 76 | public void randomLocations(Blackhole bh) { 77 | for (Point point : random) { 78 | bh.consume(geocoder.getCountry(point.latitude(), point.longitude())); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem http://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /src/main/java/uk/recurse/geocoding/reverse/Country.java: -------------------------------------------------------------------------------- 1 | package uk.recurse.geocoding.reverse; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.Reader; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.Locale; 8 | import java.util.Map; 9 | import java.util.Objects; 10 | import java.util.stream.Stream; 11 | 12 | import static java.util.stream.Collectors.collectingAndThen; 13 | import static java.util.stream.Collectors.toList; 14 | import static java.util.stream.Collectors.toMap; 15 | 16 | /** 17 | * Information about a country. 18 | */ 19 | public class Country { 20 | 21 | private final String iso; 22 | private final String iso3; 23 | private final int isoNumeric; 24 | private final String name; 25 | private final float area; 26 | private final int population; 27 | private final String continent; 28 | private final List locales; 29 | 30 | private Country(String[] row) { 31 | iso = row[0]; 32 | iso3 = row[1]; 33 | isoNumeric = Integer.parseInt(row[2]); 34 | name = row[4]; 35 | area = Float.parseFloat(row[6]); 36 | population = Integer.parseInt(row[7]); 37 | continent = row[8]; 38 | locales = Stream.of(row[15].split(",")) 39 | .map(Locale::forLanguageTag) 40 | .collect(collectingAndThen(toList(), Collections::unmodifiableList)); 41 | } 42 | 43 | static Map load(Reader reader) { 44 | return new BufferedReader(reader).lines() 45 | .filter(line -> !line.startsWith("#")) 46 | .map(line -> line.split("\t")) 47 | .collect(toMap(row -> row[16], Country::new)); 48 | } 49 | 50 | /** 51 | * Returns the ISO two letter country code. 52 | * 53 | * @return ISO 3166-1 alpha-2 code 54 | */ 55 | public String iso() { 56 | return iso; 57 | } 58 | 59 | /** 60 | * Returns the ISO three letter country code. 61 | * 62 | * @return ISO 3166-1 alpha-3 code 63 | */ 64 | public String iso3() { 65 | return iso3; 66 | } 67 | 68 | /** 69 | * Returns the ISO three digit county code. 70 | * 71 | * @return ISO 3166-1 numeric code 72 | */ 73 | public int isoNumeric() { 74 | return isoNumeric; 75 | } 76 | 77 | /** 78 | * Returns the country name. 79 | * 80 | * @return country name in English 81 | */ 82 | public String name() { 83 | return name; 84 | } 85 | 86 | /** 87 | * Returns the country size. 88 | * 89 | * @return area in sq km 90 | */ 91 | public float area() { 92 | return area; 93 | } 94 | 95 | /** 96 | * Returns the country population. 97 | * 98 | * @return population (estimate) 99 | */ 100 | public int population() { 101 | return population; 102 | } 103 | 104 | /** 105 | * Returns the continent the country is in. 106 | * 107 | * @return two letter continent code 108 | */ 109 | public String continent() { 110 | return continent; 111 | } 112 | 113 | /** 114 | * Returns the country locales. 115 | * 116 | * @return locales ordered by the number of speakers 117 | */ 118 | public List locales() { 119 | return locales; 120 | } 121 | 122 | @Override 123 | public boolean equals(Object o) { 124 | if (this == o) { 125 | return true; 126 | } 127 | if (o == null || getClass() != o.getClass()) { 128 | return false; 129 | } 130 | Country country = (Country) o; 131 | return Objects.equals(iso, country.iso); 132 | } 133 | 134 | @Override 135 | public int hashCode() { 136 | return iso.hashCode(); 137 | } 138 | 139 | @Override 140 | public String toString() { 141 | return iso + " (" + name + ")"; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin, switch paths to Windows format before running java 129 | if $cygwin ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/main/resources/countryInfo.txt: -------------------------------------------------------------------------------- 1 | # GeoNames.org Country Information 2 | # ================================ 3 | # 4 | # 5 | # CountryCodes: 6 | # ============ 7 | # 8 | # The official ISO country code for the United Kingdom is 'GB'. The code 'UK' is reserved. 9 | # 10 | # A list of dependent countries is available here: 11 | # https://spreadsheets.google.com/ccc?key=pJpyPy-J5JSNhe7F_KxwiCA&hl=en 12 | # 13 | # 14 | # The countrycode XK temporarily stands for Kosvo: 15 | # http://geonames.wordpress.com/2010/03/08/xk-country-code-for-kosovo/ 16 | # 17 | # 18 | # CS (Serbia and Montenegro) with geonameId = 8505033 no longer exists. 19 | # AN (the Netherlands Antilles) with geonameId = 8505032 was dissolved on 10 October 2010. 20 | # 21 | # 22 | # Currencies : 23 | # ============ 24 | # 25 | # A number of territories are not included in ISO 4217, because their currencies are not per se an independent currency, 26 | # but a variant of another currency. These currencies are: 27 | # 28 | # 1. FO : Faroese krona (1:1 pegged to the Danish krone) 29 | # 2. GG : Guernsey pound (1:1 pegged to the pound sterling) 30 | # 3. JE : Jersey pound (1:1 pegged to the pound sterling) 31 | # 4. IM : Isle of Man pound (1:1 pegged to the pound sterling) 32 | # 5. TV : Tuvaluan dollar (1:1 pegged to the Australian dollar). 33 | # 6. CK : Cook Islands dollar (1:1 pegged to the New Zealand dollar). 34 | # 35 | # The following non-ISO codes are, however, sometimes used: GGP for the Guernsey pound, 36 | # JEP for the Jersey pound and IMP for the Isle of Man pound (http://en.wikipedia.org/wiki/ISO_4217) 37 | # 38 | # 39 | # A list of currency symbols is available here : http://forum.geonames.org/gforum/posts/list/437.page 40 | # another list with fractional units is here: http://forum.geonames.org/gforum/posts/list/1961.page 41 | # 42 | # 43 | # Languages : 44 | # =========== 45 | # 46 | # The column 'languages' lists the languages spoken in a country ordered by the number of speakers. The language code is a 'locale' 47 | # where any two-letter primary-tag is an ISO-639 language abbreviation and any two-letter initial subtag is an ISO-3166 country code. 48 | # 49 | # Example : es-AR is the Spanish variant spoken in Argentina. 50 | # 51 | #ISO ISO3 ISO-Numeric fips Country Capital Area(in sq km) Population Continent tld CurrencyCode CurrencyName Phone Postal Code Format Postal Code Regex Languages geonameid neighbours EquivalentFipsCode 52 | AD AND 020 AN Andorra Andorra la Vella 468 84000 EU .ad EUR Euro 376 AD### ^(?:AD)*(\d{3})$ ca 3041565 ES,FR 53 | AE ARE 784 AE United Arab Emirates Abu Dhabi 82880 4975593 AS .ae AED Dirham 971 ar-AE,fa,en,hi,ur 290557 SA,OM 54 | AF AFG 004 AF Afghanistan Kabul 647500 29121286 AS .af AFN Afghani 93 fa-AF,ps,uz-AF,tk 1149361 TM,CN,IR,TJ,PK,UZ 55 | AG ATG 028 AC Antigua and Barbuda St. John's 443 86754 NA .ag XCD Dollar +1-268 en-AG 3576396 56 | AI AIA 660 AV Anguilla The Valley 102 13254 NA .ai XCD Dollar +1-264 en-AI 3573511 57 | AL ALB 008 AL Albania Tirana 28748 2986952 EU .al ALL Lek 355 #### ^(\d{4})$ sq,el 783754 MK,GR,ME,RS,XK 58 | AM ARM 051 AM Armenia Yerevan 29800 2968000 AS .am AMD Dram 374 ###### ^(\d{6})$ hy 174982 GE,IR,AZ,TR 59 | AO AGO 024 AO Angola Luanda 1246700 13068161 AF .ao AOA Kwanza 244 pt-AO 3351879 CD,NA,ZM,CG 60 | AQ ATA 010 AY Antarctica 14000000 0 AN .aq 6697173 61 | AR ARG 032 AR Argentina Buenos Aires 2766890 41343201 SA .ar ARS Peso 54 @####@@@ ^[A-Z]?\d{4}[A-Z]{0,3}$ es-AR,en,it,de,fr,gn 3865483 CL,BO,UY,PY,BR 62 | AS ASM 016 AQ American Samoa Pago Pago 199 57881 OC .as USD Dollar +1-684 #####-#### 96799 en-AS,sm,to 5880801 63 | AT AUT 040 AU Austria Vienna 83858 8205000 EU .at EUR Euro 43 #### ^(\d{4})$ de-AT,hr,hu,sl 2782113 CH,DE,HU,SK,CZ,IT,SI,LI 64 | AU AUS 036 AS Australia Canberra 7686850 21515754 OC .au AUD Dollar 61 #### ^(\d{4})$ en-AU 2077456 65 | AW ABW 533 AA Aruba Oranjestad 193 71566 NA .aw AWG Guilder 297 nl-AW,pap,es,en 3577279 66 | AX ALA 248 Aland Islands Mariehamn 1580 26711 EU .ax EUR Euro +358-18 ##### ^(?:FI)*(\d{5})$ sv-AX 661882 FI 67 | AZ AZE 031 AJ Azerbaijan Baku 86600 8303512 AS .az AZN Manat 994 AZ #### ^(?:AZ)*(\d{4})$ az,ru,hy 587116 GE,IR,AM,TR,RU 68 | BA BIH 070 BK Bosnia and Herzegovina Sarajevo 51129 4590000 EU .ba BAM Marka 387 ##### ^(\d{5})$ bs,hr-BA,sr-BA 3277605 HR,ME,RS 69 | BB BRB 052 BB Barbados Bridgetown 431 285653 NA .bb BBD Dollar +1-246 BB##### ^(?:BB)*(\d{5})$ en-BB 3374084 70 | BD BGD 050 BG Bangladesh Dhaka 144000 156118464 AS .bd BDT Taka 880 #### ^(\d{4})$ bn-BD,en 1210997 MM,IN 71 | BE BEL 056 BE Belgium Brussels 30510 10403000 EU .be EUR Euro 32 #### ^(\d{4})$ nl-BE,fr-BE,de-BE 2802361 DE,NL,LU,FR 72 | BF BFA 854 UV Burkina Faso Ouagadougou 274200 16241811 AF .bf XOF Franc 226 fr-BF,mos 2361809 NE,BJ,GH,CI,TG,ML 73 | BG BGR 100 BU Bulgaria Sofia 110910 7148785 EU .bg BGN Lev 359 #### ^(\d{4})$ bg,tr-BG,rom 732800 MK,GR,RO,TR,RS 74 | BH BHR 048 BA Bahrain Manama 665 738004 AS .bh BHD Dinar 973 ####|### ^(\d{3}\d?)$ ar-BH,en,fa,ur 290291 75 | BI BDI 108 BY Burundi Bujumbura 27830 9863117 AF .bi BIF Franc 257 fr-BI,rn 433561 TZ,CD,RW 76 | BJ BEN 204 BN Benin Porto-Novo 112620 9056010 AF .bj XOF Franc 229 fr-BJ 2395170 NE,TG,BF,NG 77 | BL BLM 652 TB Saint Barthelemy Gustavia 21 8450 NA .gp EUR Euro 590 ##### ^(\d{5})$ fr 3578476 78 | BM BMU 060 BD Bermuda Hamilton 53 65365 NA .bm BMD Dollar +1-441 @@ ## ^([A-Z]{2}\d{2})$ en-BM,pt 3573345 79 | BN BRN 096 BX Brunei Bandar Seri Begawan 5770 395027 AS .bn BND Dollar 673 @@#### ^([A-Z]{2}\d{4})$ ms-BN,en-BN 1820814 MY 80 | BO BOL 068 BL Bolivia Sucre 1098580 9947418 SA .bo BOB Boliviano 591 es-BO,qu,ay 3923057 PE,CL,PY,BR,AR 81 | BQ BES 535 Bonaire, Saint Eustatius and Saba 328 18012 NA .bq USD Dollar 599 nl,pap,en 7626844 82 | BR BRA 076 BR Brazil Brasilia 8511965 201103330 SA .br BRL Real 55 #####-### ^\d{5}-\d{3}$ pt-BR,es,en,fr 3469034 SR,PE,BO,UY,GY,PY,GF,VE,CO,AR 83 | BS BHS 044 BF Bahamas Nassau 13940 301790 NA .bs BSD Dollar +1-242 en-BS 3572887 84 | BT BTN 064 BT Bhutan Thimphu 47000 699847 AS .bt BTN Ngultrum 975 dz 1252634 CN,IN 85 | BV BVT 074 BV Bouvet Island 49 0 AN .bv NOK Krone 3371123 86 | BW BWA 072 BC Botswana Gaborone 600370 2029307 AF .bw BWP Pula 267 en-BW,tn-BW 933860 ZW,ZA,NA 87 | BY BLR 112 BO Belarus Minsk 207600 9685000 EU .by BYN Belarusian ruble 375 ###### ^(\d{6})$ be,ru 630336 PL,LT,UA,RU,LV 88 | BZ BLZ 084 BH Belize Belmopan 22966 314522 NA .bz BZD Dollar 501 en-BZ,es 3582678 GT,MX 89 | CA CAN 124 CA Canada Ottawa 9984670 33679000 NA .ca CAD Dollar 1 @#@ #@# ^([ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ]) ?(\d[ABCEGHJKLMNPRSTVWXYZ]\d)$ en-CA,fr-CA,iu 6251999 US 90 | CC CCK 166 CK Cocos Islands West Island 14 628 AS .cc AUD Dollar 61 ms-CC,en 1547376 91 | CD COD 180 CG Democratic Republic of the Congo Kinshasa 2345410 70916439 AF .cd CDF Franc 243 fr-CD,ln,ktu,kg,sw,lua 203312 TZ,CF,SS,RW,ZM,BI,UG,CG,AO 92 | CF CAF 140 CT Central African Republic Bangui 622984 4844927 AF .cf XAF Franc 236 fr-CF,sg,ln,kg 239880 TD,SD,CD,SS,CM,CG 93 | CG COG 178 CF Republic of the Congo Brazzaville 342000 3039126 AF .cg XAF Franc 242 fr-CG,kg,ln-CG 2260494 CF,GA,CD,CM,AO 94 | CH CHE 756 SZ Switzerland Bern 41290 8484100 EU .ch CHF Franc 41 #### ^(\d{4})$ de-CH,fr-CH,it-CH,rm 2658434 DE,IT,LI,FR,AT 95 | CI CIV 384 IV Ivory Coast Yamoussoukro 322460 21058798 AF .ci XOF Franc 225 fr-CI 2287781 LR,GH,GN,BF,ML 96 | CK COK 184 CW Cook Islands Avarua 240 21388 OC .ck NZD Dollar 682 en-CK,mi 1899402 97 | CL CHL 152 CI Chile Santiago 756950 16746491 SA .cl CLP Peso 56 ####### ^(\d{7})$ es-CL 3895114 PE,BO,AR 98 | CM CMR 120 CM Cameroon Yaounde 475440 19294149 AF .cm XAF Franc 237 en-CM,fr-CM 2233387 TD,CF,GA,GQ,CG,NG 99 | CN CHN 156 CH China Beijing 9596960 1330044000 AS .cn CNY Yuan Renminbi 86 ###### ^(\d{6})$ zh-CN,yue,wuu,dta,ug,za 1814991 LA,BT,TJ,KZ,MN,AF,NP,MM,KG,PK,KP,RU,VN,IN 100 | CO COL 170 CO Colombia Bogota 1138910 47790000 SA .co COP Peso 57 ###### ^(\d{6})$ es-CO 3686110 EC,PE,PA,BR,VE 101 | CR CRI 188 CS Costa Rica San Jose 51100 4516220 NA .cr CRC Colon 506 ##### ^(\d{5})$ es-CR,en 3624060 PA,NI 102 | CU CUB 192 CU Cuba Havana 110860 11423000 NA .cu CUP Peso 53 CP ##### ^(?:CP)*(\d{5})$ es-CU,pap 3562981 US 103 | CV CPV 132 CV Cabo Verde Praia 4033 508659 AF .cv CVE Escudo 238 #### ^(\d{4})$ pt-CV 3374766 104 | CW CUW 531 UC Curacao Willemstad 444 141766 NA .cw ANG Guilder 599 nl,pap 7626836 105 | CX CXR 162 KT Christmas Island Flying Fish Cove 135 1500 OC .cx AUD Dollar 61 #### ^(\d{4})$ en,zh,ms-CC 2078138 106 | CY CYP 196 CY Cyprus Nicosia 9250 1102677 EU .cy EUR Euro 357 #### ^(\d{4})$ el-CY,tr-CY,en 146669 107 | CZ CZE 203 EZ Czechia Prague 78866 10476000 EU .cz CZK Koruna 420 ### ## ^\d{3}\s?\d{2}$ cs,sk 3077311 PL,DE,SK,AT 108 | DE DEU 276 GM Germany Berlin 357021 81802257 EU .de EUR Euro 49 ##### ^(\d{5})$ de 2921044 CH,PL,NL,DK,BE,CZ,LU,FR,AT 109 | DJ DJI 262 DJ Djibouti Djibouti 23000 740528 AF .dj DJF Franc 253 fr-DJ,ar,so-DJ,aa 223816 ER,ET,SO 110 | DK DNK 208 DA Denmark Copenhagen 43094 5484000 EU .dk DKK Krone 45 #### ^(\d{4})$ da-DK,en,fo,de-DK 2623032 DE 111 | DM DMA 212 DO Dominica Roseau 754 72813 NA .dm XCD Dollar +1-767 en-DM 3575830 112 | DO DOM 214 DR Dominican Republic Santo Domingo 48730 9823821 NA .do DOP Peso +1-809 and 1-829 ##### ^(\d{5})$ es-DO 3508796 HT 113 | DZ DZA 012 AG Algeria Algiers 2381740 34586184 AF .dz DZD Dinar 213 ##### ^(\d{5})$ ar-DZ 2589581 NE,EH,LY,MR,TN,MA,ML 114 | EC ECU 218 EC Ecuador Quito 283560 14790608 SA .ec USD Dollar 593 @####@ ^([a-zA-Z]\d{4}[a-zA-Z])$ es-EC 3658394 PE,CO 115 | EE EST 233 EN Estonia Tallinn 45226 1291170 EU .ee EUR Euro 372 ##### ^(\d{5})$ et,ru 453733 RU,LV 116 | EG EGY 818 EG Egypt Cairo 1001450 80471869 AF .eg EGP Pound 20 ##### ^(\d{5})$ ar-EG,en,fr 357994 LY,SD,IL,PS 117 | EH ESH 732 WI Western Sahara El-Aaiun 266000 273008 AF .eh MAD Dirham 212 ar,mey 2461445 DZ,MR,MA 118 | ER ERI 232 ER Eritrea Asmara 121320 5792984 AF .er ERN Nakfa 291 aa-ER,ar,tig,kun,ti-ER 338010 ET,SD,DJ 119 | ES ESP 724 SP Spain Madrid 504782 46505963 EU .es EUR Euro 34 ##### ^(\d{5})$ es-ES,ca,gl,eu,oc 2510769 AD,PT,GI,FR,MA 120 | ET ETH 231 ET Ethiopia Addis Ababa 1127127 88013491 AF .et ETB Birr 251 #### ^(\d{4})$ am,en-ET,om-ET,ti-ET,so-ET,sid 337996 ER,KE,SD,SS,SO,DJ 121 | FI FIN 246 FI Finland Helsinki 337030 5244000 EU .fi EUR Euro 358 ##### ^(?:FI)*(\d{5})$ fi-FI,sv-FI,smn 660013 NO,RU,SE 122 | FJ FJI 242 FJ Fiji Suva 18270 875983 OC .fj FJD Dollar 679 en-FJ,fj 2205218 123 | FK FLK 238 FK Falkland Islands Stanley 12173 2638 SA .fk FKP Pound 500 en-FK 3474414 124 | FM FSM 583 FM Micronesia Palikir 702 107708 OC .fm USD Dollar 691 ##### ^(\d{5})$ en-FM,chk,pon,yap,kos,uli,woe,nkr,kpg 2081918 125 | FO FRO 234 FO Faroe Islands Torshavn 1399 48228 EU .fo DKK Krone 298 ### ^(?:FO)*(\d{3})$ fo,da-FO 2622320 126 | FR FRA 250 FR France Paris 547030 64768389 EU .fr EUR Euro 33 ##### ^(\d{5})$ fr-FR,frp,br,co,ca,eu,oc 3017382 CH,DE,BE,LU,IT,AD,MC,ES 127 | GA GAB 266 GB Gabon Libreville 267667 1545255 AF .ga XAF Franc 241 fr-GA 2400553 CM,GQ,CG 128 | GB GBR 826 UK United Kingdom London 244820 62348447 EU .uk GBP Pound 44 @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA ^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$ en-GB,cy-GB,gd 2635167 IE 129 | GD GRD 308 GJ Grenada St. George's 344 107818 NA .gd XCD Dollar +1-473 en-GD 3580239 130 | GE GEO 268 GG Georgia Tbilisi 69700 4630000 AS .ge GEL Lari 995 #### ^(\d{4})$ ka,ru,hy,az 614540 AM,AZ,TR,RU 131 | GF GUF 254 FG French Guiana Cayenne 91000 195506 SA .gf EUR Euro 594 ##### ^((97|98)3\d{2})$ fr-GF 3381670 SR,BR 132 | GG GGY 831 GK Guernsey St Peter Port 78 65228 EU .gg GBP Pound +44-1481 @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA ^((?:(?:[A-PR-UWYZ][A-HK-Y]\d[ABEHMNPRV-Y0-9]|[A-PR-UWYZ]\d[A-HJKPS-UW0-9])\s\d[ABD-HJLNP-UW-Z]{2})|GIR\s?0AA)$ en,nrf 3042362 133 | GH GHA 288 GH Ghana Accra 239460 24339838 AF .gh GHS Cedi 233 en-GH,ak,ee,tw 2300660 CI,TG,BF 134 | GI GIB 292 GI Gibraltar Gibraltar 6.5 27884 EU .gi GIP Pound 350 en-GI,es,it,pt 2411586 ES 135 | GL GRL 304 GL Greenland Nuuk 2166086 56375 NA .gl DKK Krone 299 #### ^(\d{4})$ kl,da-GL,en 3425505 136 | GM GMB 270 GA Gambia Banjul 11300 1593256 AF .gm GMD Dalasi 220 en-GM,mnk,wof,wo,ff 2413451 SN 137 | GN GIN 324 GV Guinea Conakry 245857 10324025 AF .gn GNF Franc 224 fr-GN 2420477 LR,SN,SL,CI,GW,ML 138 | GP GLP 312 GP Guadeloupe Basse-Terre 1780 443000 NA .gp EUR Euro 590 ##### ^((97|98)\d{3})$ fr-GP 3579143 139 | GQ GNQ 226 EK Equatorial Guinea Malabo 28051 1014999 AF .gq XAF Franc 240 es-GQ,fr 2309096 GA,CM 140 | GR GRC 300 GR Greece Athens 131940 11000000 EU .gr EUR Euro 30 ### ## ^(\d{5})$ el-GR,en,fr 390903 AL,MK,TR,BG 141 | GS SGS 239 SX South Georgia and the South Sandwich Islands Grytviken 3903 30 AN .gs GBP Pound en 3474415 142 | GT GTM 320 GT Guatemala Guatemala City 108890 13550440 NA .gt GTQ Quetzal 502 ##### ^(\d{5})$ es-GT 3595528 MX,HN,BZ,SV 143 | GU GUM 316 GQ Guam Hagatna 549 159358 OC .gu USD Dollar +1-671 969## ^(969\d{2})$ en-GU,ch-GU 4043988 144 | GW GNB 624 PU Guinea-Bissau Bissau 36120 1565126 AF .gw XOF Franc 245 #### ^(\d{4})$ pt-GW,pov 2372248 SN,GN 145 | GY GUY 328 GY Guyana Georgetown 214970 748486 SA .gy GYD Dollar 592 en-GY 3378535 SR,BR,VE 146 | HK HKG 344 HK Hong Kong Hong Kong 1092 6898686 AS .hk HKD Dollar 852 zh-HK,yue,zh,en 1819730 147 | HM HMD 334 HM Heard Island and McDonald Islands 412 0 AN .hm AUD Dollar 1547314 148 | HN HND 340 HO Honduras Tegucigalpa 112090 7989415 NA .hn HNL Lempira 504 @@#### ^([A-Z]{2}\d{4})$ es-HN,cab,miq 3608932 GT,NI,SV 149 | HR HRV 191 HR Croatia Zagreb 56542 4284889 EU .hr HRK Kuna 385 ##### ^(?:HR)*(\d{5})$ hr-HR,sr 3202326 HU,SI,BA,ME,RS 150 | HT HTI 332 HA Haiti Port-au-Prince 27750 9648924 NA .ht HTG Gourde 509 HT#### ^(?:HT)*(\d{4})$ ht,fr-HT 3723988 DO 151 | HU HUN 348 HU Hungary Budapest 93030 9982000 EU .hu HUF Forint 36 #### ^(\d{4})$ hu-HU 719819 SK,SI,RO,UA,HR,AT,RS 152 | ID IDN 360 ID Indonesia Jakarta 1919440 242968342 AS .id IDR Rupiah 62 ##### ^(\d{5})$ id,en,nl,jv 1643084 PG,TL,MY 153 | IE IRL 372 EI Ireland Dublin 70280 4622917 EU .ie EUR Euro 353 @@@ @@@@ ^[A-Z]\d{2}$|^[A-Z]{3}[A-Z]{4}$ en-IE,ga-IE 2963597 GB 154 | IL ISR 376 IS Israel Jerusalem 20770 7353985 AS .il ILS Shekel 972 ####### ^(\d{7}|\d{5})$ he,ar-IL,en-IL, 294640 SY,JO,LB,EG,PS 155 | IM IMN 833 IM Isle of Man Douglas 572 75049 EU .im GBP Pound +44-1624 @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA ^((?:(?:[A-PR-UWYZ][A-HK-Y]\d[ABEHMNPRV-Y0-9]|[A-PR-UWYZ]\d[A-HJKPS-UW0-9])\s\d[ABD-HJLNP-UW-Z]{2})|GIR\s?0AA)$ en,gv 3042225 156 | IN IND 356 IN India New Delhi 3287590 1173108018 AS .in INR Rupee 91 ###### ^(\d{6})$ en-IN,hi,bn,te,mr,ta,ur,gu,kn,ml,or,pa,as,bh,sat,ks,ne,sd,kok,doi,mni,sit,sa,fr,lus,inc 1269750 CN,NP,MM,BT,PK,BD 157 | IO IOT 086 IO British Indian Ocean Territory Diego Garcia 60 4000 AS .io USD Dollar 246 en-IO 1282588 158 | IQ IRQ 368 IZ Iraq Baghdad 437072 29671605 AS .iq IQD Dinar 964 ##### ^(\d{5})$ ar-IQ,ku,hy 99237 SY,SA,IR,JO,TR,KW 159 | IR IRN 364 IR Iran Tehran 1648000 76923300 AS .ir IRR Rial 98 ########## ^(\d{10})$ fa-IR,ku 130758 TM,AF,IQ,AM,PK,AZ,TR 160 | IS ISL 352 IC Iceland Reykjavik 103000 308910 EU .is ISK Krona 354 ### ^(\d{3})$ is,en,de,da,sv,no 2629691 161 | IT ITA 380 IT Italy Rome 301230 60340328 EU .it EUR Euro 39 ##### ^(\d{5})$ it-IT,de-IT,fr-IT,sc,ca,co,sl 3175395 CH,VA,SI,SM,FR,AT 162 | JE JEY 832 JE Jersey Saint Helier 116 90812 EU .je GBP Pound +44-1534 @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA ^((?:(?:[A-PR-UWYZ][A-HK-Y]\d[ABEHMNPRV-Y0-9]|[A-PR-UWYZ]\d[A-HJKPS-UW0-9])\s\d[ABD-HJLNP-UW-Z]{2})|GIR\s?0AA)$ en,fr,nrf 3042142 163 | JM JAM 388 JM Jamaica Kingston 10991 2847232 NA .jm JMD Dollar +1-876 en-JM 3489940 164 | JO JOR 400 JO Jordan Amman 92300 6407085 AS .jo JOD Dinar 962 ##### ^(\d{5})$ ar-JO,en 248816 SY,SA,IQ,IL,PS 165 | JP JPN 392 JA Japan Tokyo 377835 127288000 AS .jp JPY Yen 81 ###-#### ^\d{3}-\d{4}$ ja 1861060 166 | KE KEN 404 KE Kenya Nairobi 582650 40046566 AF .ke KES Shilling 254 ##### ^(\d{5})$ en-KE,sw-KE 192950 ET,TZ,SS,SO,UG 167 | KG KGZ 417 KG Kyrgyzstan Bishkek 198500 5776500 AS .kg KGS Som 996 ###### ^(\d{6})$ ky,uz,ru 1527747 CN,TJ,UZ,KZ 168 | KH KHM 116 CB Cambodia Phnom Penh 181040 14453680 AS .kh KHR Riels 855 ##### ^(\d{5})$ km,fr,en 1831722 LA,TH,VN 169 | KI KIR 296 KR Kiribati Tarawa 811 92533 OC .ki AUD Dollar 686 en-KI,gil 4030945 170 | KM COM 174 CN Comoros Moroni 2170 773407 AF .km KMF Franc 269 ar,fr-KM 921929 171 | KN KNA 659 SC Saint Kitts and Nevis Basseterre 261 51134 NA .kn XCD Dollar +1-869 en-KN 3575174 172 | KP PRK 408 KN North Korea Pyongyang 120540 22912177 AS .kp KPW Won 850 ###-### ^(\d{6})$ ko-KP 1873107 CN,KR,RU 173 | KR KOR 410 KS South Korea Seoul 98480 48422644 AS .kr KRW Won 82 ##### ^(\d{5})$ ko-KR,en 1835841 KP 174 | XK XKX 0 KV Kosovo Pristina 10908 1800000 EU EUR Euro sq,sr 831053 RS,AL,MK,ME 175 | KW KWT 414 KU Kuwait Kuwait City 17820 2789132 AS .kw KWD Dinar 965 ##### ^(\d{5})$ ar-KW,en 285570 SA,IQ 176 | KY CYM 136 CJ Cayman Islands George Town 262 44270 NA .ky KYD Dollar +1-345 en-KY 3580718 177 | KZ KAZ 398 KZ Kazakhstan Nur-Sultan 2717300 15340000 AS .kz KZT Tenge 7 ###### ^(\d{6})$ kk,ru 1522867 TM,CN,KG,UZ,RU 178 | LA LAO 418 LA Laos Vientiane 236800 6368162 AS .la LAK Kip 856 ##### ^(\d{5})$ lo,fr,en 1655842 CN,MM,KH,TH,VN 179 | LB LBN 422 LE Lebanon Beirut 10400 4125247 AS .lb LBP Pound 961 #### ####|#### ^(\d{4}(\d{4})?)$ ar-LB,fr-LB,en,hy 272103 SY,IL 180 | LC LCA 662 ST Saint Lucia Castries 616 160922 NA .lc XCD Dollar +1-758 en-LC 3576468 181 | LI LIE 438 LS Liechtenstein Vaduz 160 35000 EU .li CHF Franc 423 #### ^(\d{4})$ de-LI 3042058 CH,AT 182 | LK LKA 144 CE Sri Lanka Colombo 65610 21513990 AS .lk LKR Rupee 94 ##### ^(\d{5})$ si,ta,en 1227603 183 | LR LBR 430 LI Liberia Monrovia 111370 3685076 AF .lr LRD Dollar 231 #### ^(\d{4})$ en-LR 2275384 SL,CI,GN 184 | LS LSO 426 LT Lesotho Maseru 30355 1919552 AF .ls LSL Loti 266 ### ^(\d{3})$ en-LS,st,zu,xh 932692 ZA 185 | LT LTU 440 LH Lithuania Vilnius 65200 2944459 EU .lt EUR Euro 370 LT-##### ^(?:LT)*(\d{5})$ lt,ru,pl 597427 PL,BY,RU,LV 186 | LU LUX 442 LU Luxembourg Luxembourg 2586 497538 EU .lu EUR Euro 352 L-#### ^(?:L-)?\d{4}$ lb,de-LU,fr-LU 2960313 DE,BE,FR 187 | LV LVA 428 LG Latvia Riga 64589 2217969 EU .lv EUR Euro 371 LV-#### ^(?:LV)*(\d{4})$ lv,ru,lt 458258 LT,EE,BY,RU 188 | LY LBY 434 LY Libya Tripoli 1759540 6461454 AF .ly LYD Dinar 218 ar-LY,it,en 2215636 TD,NE,DZ,SD,TN,EG 189 | MA MAR 504 MO Morocco Rabat 446550 33848242 AF .ma MAD Dirham 212 ##### ^(\d{5})$ ar-MA,ber,fr 2542007 DZ,EH,ES 190 | MC MCO 492 MN Monaco Monaco 1.95 32965 EU .mc EUR Euro 377 ##### ^(\d{5})$ fr-MC,en,it 2993457 FR 191 | MD MDA 498 MD Moldova Chisinau 33843 4324000 EU .md MDL Leu 373 MD-#### ^MD-\d{4}$ ro,ru,gag,tr 617790 RO,UA 192 | ME MNE 499 MJ Montenegro Podgorica 14026 666730 EU .me EUR Euro 382 ##### ^(\d{5})$ sr,hu,bs,sq,hr,rom 3194884 AL,HR,BA,RS,XK 193 | MF MAF 663 RN Saint Martin Marigot 53 35925 NA .gp EUR Euro 590 ##### ^(\d{5})$ fr 3578421 SX 194 | MG MDG 450 MA Madagascar Antananarivo 587040 21281844 AF .mg MGA Ariary 261 ### ^(\d{3})$ fr-MG,mg 1062947 195 | MH MHL 584 RM Marshall Islands Majuro 181.3 65859 OC .mh USD Dollar 692 #####-#### ^969\d{2}(-\d{4})$ mh,en-MH 2080185 196 | MK MKD 807 MK North Macedonia Skopje 25333 2062294 EU .mk MKD Denar 389 #### ^(\d{4})$ mk,sq,tr,rmm,sr 718075 AL,GR,BG,RS,XK 197 | ML MLI 466 ML Mali Bamako 1240000 13796354 AF .ml XOF Franc 223 fr-ML,bm 2453866 SN,NE,DZ,CI,GN,MR,BF 198 | MM MMR 104 BM Myanmar Nay Pyi Taw 678500 53414374 AS .mm MMK Kyat 95 ##### ^(\d{5})$ my 1327865 CN,LA,TH,BD,IN 199 | MN MNG 496 MG Mongolia Ulaanbaatar 1565000 3086918 AS .mn MNT Tugrik 976 ###### ^(\d{6})$ mn,ru 2029969 CN,RU 200 | MO MAC 446 MC Macao Macao 254 449198 AS .mo MOP Pataca 853 zh,zh-MO,pt 1821275 201 | MP MNP 580 CQ Northern Mariana Islands Saipan 477 53883 OC .mp USD Dollar +1-670 ##### ^9695\d{1}$ fil,tl,zh,ch-MP,en-MP 4041468 202 | MQ MTQ 474 MB Martinique Fort-de-France 1100 432900 NA .mq EUR Euro 596 ##### ^(\d{5})$ fr-MQ 3570311 203 | MR MRT 478 MR Mauritania Nouakchott 1030700 3205060 AF .mr MRO Ouguiya 222 ar-MR,fuc,snk,fr,mey,wo 2378080 SN,DZ,EH,ML 204 | MS MSR 500 MH Montserrat Plymouth 102 9341 NA .ms XCD Dollar +1-664 en-MS 3578097 205 | MT MLT 470 MT Malta Valletta 316 403000 EU .mt EUR Euro 356 @@@ #### ^[A-Z]{3}\s?\d{4}$ mt,en-MT 2562770 206 | MU MUS 480 MP Mauritius Port Louis 2040 1294104 AF .mu MUR Rupee 230 en-MU,bho,fr 934292 207 | MV MDV 462 MV Maldives Male 300 395650 AS .mv MVR Rufiyaa 960 ##### ^(\d{5})$ dv,en 1282028 208 | MW MWI 454 MI Malawi Lilongwe 118480 15447500 AF .mw MWK Kwacha 265 ny,yao,tum,swk 927384 TZ,MZ,ZM 209 | MX MEX 484 MX Mexico Mexico City 1972550 112468855 NA .mx MXN Peso 52 ##### ^(\d{5})$ es-MX 3996063 GT,US,BZ 210 | MY MYS 458 MY Malaysia Kuala Lumpur 329750 28274729 AS .my MYR Ringgit 60 ##### ^(\d{5})$ ms-MY,en,zh,ta,te,ml,pa,th 1733045 BN,TH,ID 211 | MZ MOZ 508 MZ Mozambique Maputo 801590 22061451 AF .mz MZN Metical 258 #### ^(\d{4})$ pt-MZ,vmw 1036973 ZW,TZ,SZ,ZA,ZM,MW 212 | NA NAM 516 WA Namibia Windhoek 825418 2128471 AF .na NAD Dollar 264 en-NA,af,de,hz,naq 3355338 ZA,BW,ZM,AO 213 | NC NCL 540 NC New Caledonia Noumea 19060 216494 OC .nc XPF Franc 687 ##### ^(\d{5})$ fr-NC 2139685 214 | NE NER 562 NG Niger Niamey 1267000 15878271 AF .ne XOF Franc 227 #### ^(\d{4})$ fr-NE,ha,kr,dje 2440476 TD,BJ,DZ,LY,BF,NG,ML 215 | NF NFK 574 NF Norfolk Island Kingston 34.6 1828 OC .nf AUD Dollar 672 #### ^(\d{4})$ en-NF 2155115 216 | NG NGA 566 NI Nigeria Abuja 923768 154000000 AF .ng NGN Naira 234 ###### ^(\d{6})$ en-NG,ha,yo,ig,ff 2328926 TD,NE,BJ,CM 217 | NI NIC 558 NU Nicaragua Managua 129494 5995928 NA .ni NIO Cordoba 505 ###-###-# ^(\d{7})$ es-NI,en 3617476 CR,HN 218 | NL NLD 528 NL Netherlands Amsterdam 41526 16645000 EU .nl EUR Euro 31 #### @@ ^(\d{4}[A-Z]{2})$ nl-NL,fy-NL 2750405 DE,BE 219 | NO NOR 578 NO Norway Oslo 324220 5009150 EU .no NOK Krone 47 #### ^(\d{4})$ no,nb,nn,se,fi 3144096 FI,RU,SE 220 | NP NPL 524 NP Nepal Kathmandu 140800 28951852 AS .np NPR Rupee 977 ##### ^(\d{5})$ ne,en 1282988 CN,IN 221 | NR NRU 520 NR Nauru Yaren 21 10065 OC .nr AUD Dollar 674 na,en-NR 2110425 222 | NU NIU 570 NE Niue Alofi 260 2166 OC .nu NZD Dollar 683 niu,en-NU 4036232 223 | NZ NZL 554 NZ New Zealand Wellington 268680 4252277 OC .nz NZD Dollar 64 #### ^(\d{4})$ en-NZ,mi 2186224 224 | OM OMN 512 MU Oman Muscat 212460 2967717 AS .om OMR Rial 968 ### ^(\d{3})$ ar-OM,en,bal,ur 286963 SA,YE,AE 225 | PA PAN 591 PM Panama Panama City 78200 3410676 NA .pa PAB Balboa 507 es-PA,en 3703430 CR,CO 226 | PE PER 604 PE Peru Lima 1285220 29907003 SA .pe PEN Sol 51 es-PE,qu,ay 3932488 EC,CL,BO,BR,CO 227 | PF PYF 258 FP French Polynesia Papeete 4167 270485 OC .pf XPF Franc 689 ##### ^((97|98)7\d{2})$ fr-PF,ty 4030656 228 | PG PNG 598 PP Papua New Guinea Port Moresby 462840 6064515 OC .pg PGK Kina 675 ### ^(\d{3})$ en-PG,ho,meu,tpi 2088628 ID 229 | PH PHL 608 RP Philippines Manila 300000 99900177 AS .ph PHP Peso 63 #### ^(\d{4})$ tl,en-PH,fil,ceb,tgl,ilo,hil,war,pam,bik,bcl,pag,mrw,tsg,mdh,cbk,krj,sgd,msb,akl,ibg,yka,mta,abx 1694008 230 | PK PAK 586 PK Pakistan Islamabad 803940 184404791 AS .pk PKR Rupee 92 ##### ^(\d{5})$ ur-PK,en-PK,pa,sd,ps,brh 1168579 CN,AF,IR,IN 231 | PL POL 616 PL Poland Warsaw 312685 38500000 EU .pl PLN Zloty 48 ##-### ^\d{2}-\d{3}$ pl 798544 DE,LT,SK,CZ,BY,UA,RU 232 | PM SPM 666 SB Saint Pierre and Miquelon Saint-Pierre 242 7012 NA .pm EUR Euro 508 ##### ^(97500)$ fr-PM 3424932 233 | PN PCN 612 PC Pitcairn Adamstown 47 46 OC .pn NZD Dollar 870 en-PN 4030699 234 | PR PRI 630 RQ Puerto Rico San Juan 9104 3916632 NA .pr USD Dollar +1-787 and 1-939 #####-#### ^00[679]\d{2}(?:-\d{4})?$ en-PR,es-PR 4566966 235 | PS PSE 275 WE Palestinian Territory East Jerusalem 5970 3800000 AS .ps ILS Shekel 970 ar-PS 6254930 JO,IL,EG 236 | PT PRT 620 PO Portugal Lisbon 92391 10676000 EU .pt EUR Euro 351 ####-### ^\d{4}-\d{3}\s?[a-zA-Z]{0,25}$ pt-PT,mwl 2264397 ES 237 | PW PLW 585 PS Palau Melekeok 458 19907 OC .pw USD Dollar 680 96940 ^(96940)$ pau,sov,en-PW,tox,ja,fil,zh 1559582 238 | PY PRY 600 PA Paraguay Asuncion 406750 6375830 SA .py PYG Guarani 595 #### ^(\d{4})$ es-PY,gn 3437598 BO,BR,AR 239 | QA QAT 634 QA Qatar Doha 11437 840926 AS .qa QAR Rial 974 ar-QA,es 289688 SA 240 | RE REU 638 RE Reunion Saint-Denis 2517 776948 AF .re EUR Euro 262 ##### ^((97|98)(4|7|8)\d{2})$ fr-RE 935317 241 | RO ROU 642 RO Romania Bucharest 237500 21959278 EU .ro RON Leu 40 ###### ^(\d{6})$ ro,hu,rom 798549 MD,HU,UA,BG,RS 242 | RS SRB 688 RI Serbia Belgrade 88361 7344847 EU .rs RSD Dinar 381 ###### ^(\d{6})$ sr,hu,bs,rom 6290252 AL,HU,MK,RO,HR,BA,BG,ME,XK 243 | RU RUS 643 RS Russia Moscow 17100000 140702000 EU .ru RUB Ruble 7 ###### ^(\d{6})$ ru,tt,xal,cau,ady,kv,ce,tyv,cv,udm,tut,mns,bua,myv,mdf,chm,ba,inh,tut,kbd,krc,av,sah,nog 2017370 GE,CN,BY,UA,KZ,LV,PL,EE,LT,FI,MN,NO,AZ,KP 244 | RW RWA 646 RW Rwanda Kigali 26338 11055976 AF .rw RWF Franc 250 rw,en-RW,fr-RW,sw 49518 TZ,CD,BI,UG 245 | SA SAU 682 SA Saudi Arabia Riyadh 1960582 25731776 AS .sa SAR Rial 966 ##### ^(\d{5})$ ar-SA 102358 QA,OM,IQ,YE,JO,AE,KW 246 | SB SLB 090 BP Solomon Islands Honiara 28450 559198 OC .sb SBD Dollar 677 en-SB,tpi 2103350 247 | SC SYC 690 SE Seychelles Victoria 455 88340 AF .sc SCR Rupee 248 en-SC,fr-SC 241170 248 | SD SDN 729 SU Sudan Khartoum 1861484 35000000 AF .sd SDG Pound 249 ##### ^(\d{5})$ ar-SD,en,fia 366755 SS,TD,EG,ET,ER,LY,CF 249 | SS SSD 728 OD South Sudan Juba 644329 8260490 AF SSP Pound 211 en 7909807 CD,CF,ET,KE,SD,UG 250 | SE SWE 752 SW Sweden Stockholm 449964 9828655 EU .se SEK Krona 46 ### ## ^(?:SE)?\d{3}\s\d{2}$ sv-SE,se,sma,fi-SE 2661886 NO,FI 251 | SG SGP 702 SN Singapore Singapore 692.7 4701069 AS .sg SGD Dollar 65 ###### ^(\d{6})$ cmn,en-SG,ms-SG,ta-SG,zh-SG 1880251 252 | SH SHN 654 SH Saint Helena Jamestown 410 7460 AF .sh SHP Pound 290 STHL 1ZZ ^(STHL1ZZ)$ en-SH 3370751 253 | SI SVN 705 SI Slovenia Ljubljana 20273 2007000 EU .si EUR Euro 386 #### ^(?:SI)*(\d{4})$ sl,sh 3190538 HU,IT,HR,AT 254 | SJ SJM 744 SV Svalbard and Jan Mayen Longyearbyen 62049 2550 EU .sj NOK Krone 47 #### ^(\d{4})$ no,ru 607072 255 | SK SVK 703 LO Slovakia Bratislava 48845 5455000 EU .sk EUR Euro 421 ### ## ^\d{3}\s?\d{2}$ sk,hu 3057568 PL,HU,CZ,UA,AT 256 | SL SLE 694 SL Sierra Leone Freetown 71740 5245695 AF .sl SLL Leone 232 en-SL,men,tem 2403846 LR,GN 257 | SM SMR 674 SM San Marino San Marino 61.2 31477 EU .sm EUR Euro 378 4789# ^(4789\d)$ it-SM 3168068 IT 258 | SN SEN 686 SG Senegal Dakar 196190 12323252 AF .sn XOF Franc 221 ##### ^(\d{5})$ fr-SN,wo,fuc,mnk 2245662 GN,MR,GW,GM,ML 259 | SO SOM 706 SO Somalia Mogadishu 637657 10112453 AF .so SOS Shilling 252 @@ ##### ^([A-Z]{2}\d{5})$ so-SO,ar-SO,it,en-SO 51537 ET,KE,DJ 260 | SR SUR 740 NS Suriname Paramaribo 163270 492829 SA .sr SRD Dollar 597 nl-SR,en,srn,hns,jv 3382998 GY,BR,GF 261 | ST STP 678 TP Sao Tome and Principe Sao Tome 1001 197700 AF .st STD Dobra 239 pt-ST 2410758 262 | SV SLV 222 ES El Salvador San Salvador 21040 6052064 NA .sv USD Dollar 503 CP #### ^(?:CP)*(\d{4})$ es-SV 3585968 GT,HN 263 | SX SXM 534 NN Sint Maarten Philipsburg 21 37429 NA .sx ANG Guilder 599 nl,en 7609695 MF 264 | SY SYR 760 SY Syria Damascus 185180 22198110 AS .sy SYP Pound 963 ar-SY,ku,hy,arc,fr,en 163843 IQ,JO,IL,TR,LB 265 | SZ SWZ 748 WZ Eswatini Mbabane 17363 1354051 AF .sz SZL Lilangeni 268 @### ^([A-Z]\d{3})$ en-SZ,ss-SZ 934841 ZA,MZ 266 | TC TCA 796 TK Turks and Caicos Islands Cockburn Town 430 20556 NA .tc USD Dollar +1-649 TKCA 1ZZ ^(TKCA 1ZZ)$ en-TC 3576916 267 | TD TCD 148 CD Chad N'Djamena 1284000 10543464 AF .td XAF Franc 235 fr-TD,ar-TD,sre 2434508 NE,LY,CF,SD,CM,NG 268 | TF ATF 260 FS French Southern Territories Port-aux-Francais 7829 140 AN .tf EUR Euro fr 1546748 269 | TG TGO 768 TO Togo Lome 56785 6587239 AF .tg XOF Franc 228 fr-TG,ee,hna,kbp,dag,ha 2363686 BJ,GH,BF 270 | TH THA 764 TH Thailand Bangkok 514000 67089500 AS .th THB Baht 66 ##### ^(\d{5})$ th,en 1605651 LA,MM,KH,MY 271 | TJ TJK 762 TI Tajikistan Dushanbe 143100 7487489 AS .tj TJS Somoni 992 ###### ^(\d{6})$ tg,ru 1220409 CN,AF,KG,UZ 272 | TK TKL 772 TL Tokelau 10 1466 OC .tk NZD Dollar 690 tkl,en-TK 4031074 273 | TL TLS 626 TT Timor Leste Dili 15007 1154625 OC .tl USD Dollar 670 tet,pt-TL,id,en 1966436 ID 274 | TM TKM 795 TX Turkmenistan Ashgabat 488100 4940916 AS .tm TMT Manat 993 ###### ^(\d{6})$ tk,ru,uz 1218197 AF,IR,UZ,KZ 275 | TN TUN 788 TS Tunisia Tunis 163610 10589025 AF .tn TND Dinar 216 #### ^(\d{4})$ ar-TN,fr 2464461 DZ,LY 276 | TO TON 776 TN Tonga Nuku'alofa 748 122580 OC .to TOP Pa'anga 676 to,en-TO 4032283 277 | TR TUR 792 TU Turkey Ankara 780580 77804122 AS .tr TRY Lira 90 ##### ^(\d{5})$ tr-TR,ku,diq,az,av 298795 SY,GE,IQ,IR,GR,AM,AZ,BG 278 | TT TTO 780 TD Trinidad and Tobago Port of Spain 5128 1328019 NA .tt TTD Dollar +1-868 en-TT,hns,fr,es,zh 3573591 279 | TV TUV 798 TV Tuvalu Funafuti 26 10472 OC .tv AUD Dollar 688 tvl,en,sm,gil 2110297 280 | TW TWN 158 TW Taiwan Taipei 35980 22894384 AS .tw TWD Dollar 886 ##### ^(\d{5})$ zh-TW,zh,nan,hak 1668284 281 | TZ TZA 834 TZ Tanzania Dodoma 945087 41892895 AF .tz TZS Shilling 255 sw-TZ,en,ar 149590 MZ,KE,CD,RW,ZM,BI,UG,MW 282 | UA UKR 804 UP Ukraine Kyiv 603700 45415596 EU .ua UAH Hryvnia 380 ##### ^(\d{5})$ uk,ru-UA,rom,pl,hu 690791 PL,MD,HU,SK,BY,RO,RU 283 | UG UGA 800 UG Uganda Kampala 236040 33398682 AF .ug UGX Shilling 256 en-UG,lg,sw,ar 226074 TZ,KE,SS,CD,RW 284 | UM UMI 581 United States Minor Outlying Islands 0 0 OC .um USD Dollar 1 en-UM 5854968 285 | US USA 840 US United States Washington 9629091 310232863 NA .us USD Dollar 1 #####-#### ^\d{5}(-\d{4})?$ en-US,es-US,haw,fr 6252001 CA,MX,CU 286 | UY URY 858 UY Uruguay Montevideo 176220 3477000 SA .uy UYU Peso 598 ##### ^(\d{5})$ es-UY 3439705 BR,AR 287 | UZ UZB 860 UZ Uzbekistan Tashkent 447400 27865738 AS .uz UZS Som 998 ###### ^(\d{6})$ uz,ru,tg 1512440 TM,AF,KG,TJ,KZ 288 | VA VAT 336 VT Vatican Vatican City 0.44 921 EU .va EUR Euro 379 ##### ^(\d{5})$ la,it,fr 3164670 IT 289 | VC VCT 670 VC Saint Vincent and the Grenadines Kingstown 389 104217 NA .vc XCD Dollar +1-784 en-VC,fr 3577815 290 | VE VEN 862 VE Venezuela Caracas 912050 27223228 SA .ve VES Bolivar Soberano 58 #### ^(\d{4})$ es-VE 3625428 GY,BR,CO 291 | VG VGB 092 VI British Virgin Islands Road Town 153 21730 NA .vg USD Dollar +1-284 en-VG 3577718 292 | VI VIR 850 VQ U.S. Virgin Islands Charlotte Amalie 352 108708 NA .vi USD Dollar +1-340 #####-#### ^008\d{2}(?:-\d{4})?$ en-VI 4796775 293 | VN VNM 704 VM Vietnam Hanoi 329560 89571130 AS .vn VND Dong 84 ###### ^(\d{6})$ vi,en,fr,zh,km 1562822 CN,LA,KH 294 | VU VUT 548 NH Vanuatu Port Vila 12200 221552 OC .vu VUV Vatu 678 bi,en-VU,fr-VU 2134431 295 | WF WLF 876 WF Wallis and Futuna Mata Utu 274 16025 OC .wf XPF Franc 681 ##### ^(986\d{2})$ wls,fud,fr-WF 4034749 296 | WS WSM 882 WS Samoa Apia 2944 192001 OC .ws WST Tala 685 sm,en-WS 4034894 297 | YE YEM 887 YM Yemen Sanaa 527970 23495361 AS .ye YER Rial 967 ar-YE 69543 SA,OM 298 | YT MYT 175 MF Mayotte Mamoudzou 374 159042 AF .yt EUR Euro 262 ##### ^(\d{5})$ fr-YT 1024031 299 | ZA ZAF 710 SF South Africa Pretoria 1219912 49000000 AF .za ZAR Rand 27 #### ^(\d{4})$ zu,xh,af,nso,en-ZA,tn,st,ts,ss,ve,nr 953987 ZW,SZ,MZ,BW,NA,LS 300 | ZM ZMB 894 ZA Zambia Lusaka 752614 13460305 AF .zm ZMW Kwacha 260 ##### ^(\d{5})$ en-ZM,bem,loz,lun,lue,ny,toi 895949 ZW,TZ,MZ,CD,NA,MW,AO 301 | ZW ZWE 716 ZI Zimbabwe Harare 390580 13061000 AF .zw ZWL Dollar 263 en-ZW,sn,nr,nd 878675 ZA,MZ,BW,ZM 302 | CS SCG 891 YI Serbia and Montenegro Belgrade 102350 10829175 EU .cs RSD Dinar 381 ##### ^(\d{5})$ cu,hu,sq,sr 8505033 AL,HU,MK,RO,HR,BA,BG 303 | AN ANT 530 NT Netherlands Antilles Willemstad 960 300000 NA .an ANG Guilder 599 nl-AN,en,es 8505032 GP 304 | --------------------------------------------------------------------------------