├── .eslintignore ├── .npmrc ├── .gitignore ├── .mocharc.js ├── .gitattributes ├── tsconfig.build.json ├── nest-cli.json ├── .prettierrc ├── src ├── app.service.ts ├── routes │ ├── cityvn │ │ ├── cityvn.service.ts │ │ └── cityvn.controller.ts │ └── wom │ │ ├── wom.controller.ts │ │ └── wom.service.ts ├── main.ts ├── app.controller.ts ├── app.module.ts ├── app.controller.spec.ts └── wom.controller.spec.ts ├── docker-compose.yml ├── .github ├── dependabot.yml └── workflows │ ├── eslint.yml │ ├── build-image.yml │ └── unittest.yml ├── .whitesource ├── scrapers ├── instances.ts ├── cityvn.ts ├── wom.usstate.ts └── wom.world.ts ├── vercel.json ├── Dockerfile ├── database └── index.ts ├── tsconfig.json ├── utils ├── log.ts └── utils.ts ├── README.md ├── .eslintrc.js ├── package.json └── assets └── countries.json /.eslintignore: -------------------------------------------------------------------------------- 1 | .eslintrc.js -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .DS_STORE 4 | dist/* 5 | coverage/* -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | file: ['./tests/mochaSetup.spec.js'] 3 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts", ".eslintrc.js"] 4 | } 5 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": false, 5 | "printWidth": 180, 6 | "tabWidth": 4, 7 | "arrowParens": "avoid" 8 | } -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | 3 | services: 4 | ncov-api: 5 | image: ghcr.io/phamleduy04/ncov-api:latest 6 | container_name: ncov-api 7 | restart: unless-stopped 8 | ports: 9 | - 3003:3000 -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "12:00" 8 | open-pull-requests-limit: 10 9 | labels: 10 | - "dependencies" -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "baseBranches": [] 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure", 7 | "displayMode": "diff" 8 | }, 9 | "issueSettings": { 10 | "minSeverityLevel": "LOW", 11 | "issueType": "DEPENDENCY" 12 | } 13 | } -------------------------------------------------------------------------------- /scrapers/instances.ts: -------------------------------------------------------------------------------- 1 | import getWOM from './wom.world'; 2 | import getStates from './wom.usstate'; 3 | import getCityVN from './cityvn'; 4 | 5 | const execute = async (): Promise => { 6 | await Promise.all([ 7 | getWOM(), 8 | getStates(), 9 | getCityVN(), 10 | ]); 11 | }; 12 | export default execute; -------------------------------------------------------------------------------- /src/routes/cityvn/cityvn.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { get } from '../../../database'; 3 | 4 | @Injectable() 5 | export class cityVNService { 6 | async getCityVN(oneweek: boolean) { 7 | const data = await get(oneweek ? 'ncovcity1week' : 'ncovcity'); 8 | return data; 9 | } 10 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import executeSraper from '../scrapers/instances'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.create(AppModule); 7 | await app.listen(3000); 8 | }; 9 | 10 | executeSraper(); 11 | setInterval(executeSraper, 1800000); 12 | bootstrap(); 13 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | // eslint-disable-next-line no-empty-function 7 | constructor(private readonly appService: AppService) { } 8 | 9 | @Get() 10 | getHello(): string { 11 | return this.appService.getHello(); 12 | } 13 | } -------------------------------------------------------------------------------- /.github/workflows/eslint.yml: -------------------------------------------------------------------------------- 1 | name: Eslint 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | pull_request: 7 | branches: 8 | - '*' 9 | paths-ignore: 10 | - '.github/**' 11 | - '*.md' 12 | - 'assets/**' 13 | - 'LICENSE' 14 | - 'node_modules/*' 15 | jobs: 16 | eslint: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - run: yarn install --frozen-lockfile --production=false 21 | - run: yarn run lint -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "src/main.ts", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "src/main.ts", 13 | "methods": [ 14 | "GET", 15 | "POST", 16 | "PUT", 17 | "DELETE", 18 | "OPTIONS" 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine as ts-compiler 2 | WORKDIR /home/container 3 | 4 | COPY . . 5 | 6 | RUN yarn install 7 | RUN yarn run build 8 | 9 | FROM node:alpine as ts-remover 10 | WORKDIR /home/container 11 | 12 | COPY --from=ts-compiler /home/container/package*.json ./ 13 | COPY --from=ts-compiler /home/container/dist ./ 14 | RUN yarn install --production 15 | 16 | FROM gcr.io/distroless/nodejs:16 17 | WORKDIR /home/container 18 | COPY --from=ts-remover /home/container ./ 19 | USER 1000 20 | CMD ["src/main"] -------------------------------------------------------------------------------- /database/index.ts: -------------------------------------------------------------------------------- 1 | import * as Keyv from 'keyv'; 2 | import 'dotenv/config'; 3 | import { error } from '../utils/log'; 4 | 5 | const db = new Keyv(process.env.DBURL || undefined); 6 | db.on('error', err => error('Connection Error', err)); 7 | 8 | const get = async (key: string) => { 9 | if (!key) throw new Error('No key provided'); 10 | return await db.get(key); 11 | }; 12 | 13 | const set = async (key: string, value: any) => { 14 | if (!key || !value) throw new Error('Key or value is null!'); 15 | return await db.set(key, value); 16 | }; 17 | 18 | export { 19 | get, 20 | set, 21 | }; -------------------------------------------------------------------------------- /src/routes/cityvn/cityvn.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Query } from '@nestjs/common'; 2 | import { cityVNService } from './cityvn.service'; 3 | import type { cityVNToday, cityVNOneWeek } from '../../../scrapers/cityvn'; 4 | 5 | @Controller('cityvn') 6 | export class cityVNController { 7 | // eslint-disable-next-line no-empty-function 8 | constructor(private readonly appService: cityVNService) { } 9 | 10 | @Get() 11 | async getCityVN(@Query() query: { oneweek: string }): Promise { 12 | const oneweek = (query.oneweek == 'true'); 13 | return this.appService.getCityVN(oneweek); 14 | }; 15 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2022", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false, 20 | "resolveJsonModule": true, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { AppController } from './app.controller'; 4 | import { AppService } from './app.service'; 5 | 6 | import { womController } from './routes/wom/wom.controller'; 7 | import { womService } from './routes/wom/wom.service'; 8 | 9 | import { cityVNController } from './routes/cityvn/cityvn.controller'; 10 | import { cityVNService } from './routes/cityvn/cityvn.service'; 11 | 12 | @Module({ 13 | imports: [], 14 | controllers: [AppController, womController, cityVNController], 15 | providers: [AppService, womService, cityVNService], 16 | }) 17 | 18 | // eslint-disable-next-line no-empty-function 19 | export class AppModule {} 20 | -------------------------------------------------------------------------------- /src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /.github/workflows/build-image.yml: -------------------------------------------------------------------------------- 1 | name: build docker image 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Setup QEMU 14 | uses: docker/setup-qemu-action@v2 15 | - 16 | name: Set up Docker Buildx 17 | uses: docker/setup-buildx-action@v2 18 | - 19 | name: Login to DockerHub 20 | uses: docker/login-action@v2 21 | with: 22 | registry: ghcr.io 23 | username: ${{ github.actor }} 24 | password: ${{ secrets.GH_TOKEN }} 25 | - 26 | name: Build and push 27 | uses: docker/build-push-action@v3 28 | with: 29 | push: true 30 | file: Dockerfile 31 | tags: ghcr.io/${{ github.repository }}:latest -------------------------------------------------------------------------------- /.github/workflows/unittest.yml: -------------------------------------------------------------------------------- 1 | name: unittest 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | paths-ignore: 7 | - '.github/**' 8 | - '*.md' 9 | - 'assets/**' 10 | - 'LICENSE' 11 | - 'node_modules/*' 12 | pull_request: 13 | branches: 14 | - '*' 15 | schedule: 16 | - cron: "0 */2 * * *" 17 | jobs: 18 | unittests: 19 | runs-on: ubuntu-latest 20 | services: 21 | mongodb: 22 | image: mongo 23 | ports: 24 | - 27017:27017 25 | steps: 26 | - name: Git checkout 27 | uses: actions/checkout@v2 28 | - uses: zcong1993/setup-timezone@master 29 | with: 30 | timezone: America/Chicago 31 | - run: yarn install --frozen-lockfile 32 | - run: yarn run test 33 | env: 34 | MONGODB: mongodb://localhost:27017/ncovapi 35 | -------------------------------------------------------------------------------- /utils/log.ts: -------------------------------------------------------------------------------- 1 | import * as dayjs from 'dayjs'; 2 | import * as utc from 'dayjs/plugin/utc'; 3 | import * as timezone from 'dayjs/plugin/timezone'; 4 | dayjs.extend(utc); 5 | dayjs.extend(timezone); 6 | 7 | import { yellow, green, red } from 'colorette'; 8 | 9 | const timeNow = () => dayjs().tz(process.env.TZ || 'America/Chicago').format('MM/DD/YYYY hh:mm:ss'); 10 | 11 | const msg = (func: any, message: string) => func(yellow(`[${timeNow()}]`) + ' ' + green(message)); 12 | 13 | const error = (message = 'Unknown error', err:Error) => { 14 | console.error(yellow(timeNow()) + ' Error: ' + red(message)); 15 | console.error(err); 16 | }; 17 | 18 | const info = (message:string) => { 19 | console.log(message); 20 | msg(console.info, message); 21 | }; 22 | const warn = (message:string) => msg(console.warn, `${yellow('WARNING ->')} -> ${message}`); 23 | 24 | export { 25 | error, 26 | info, 27 | warn, 28 | }; -------------------------------------------------------------------------------- /scrapers/cityvn.ts: -------------------------------------------------------------------------------- 1 | import { request } from 'undici'; 2 | import * as log from '../utils/log'; 3 | import { set } from '../database'; 4 | 5 | const processCityVn = async () => { 6 | // URL chính thức trong network requset từ web https://covid19.gov.vn/ 7 | const url = `https://static.pipezero.com/covid/data.json`; 8 | const response = await request(url).then(res => res.body.json()); 9 | const todayResponse:Array = response.locations; 10 | const oneWeekResponse:Array = response.overview; 11 | 12 | await set('ncovcity', todayResponse); 13 | log.info(`NCOVcity Scraper success! ${todayResponse.length} cities`); 14 | await set('ncovcity1week', oneWeekResponse); 15 | log.info(`NCOVcity Scraper success! ${oneWeekResponse.length} cities`); 16 | }; 17 | 18 | export default processCityVn; 19 | 20 | export interface cityVNToday { 21 | name: string, 22 | death: number, 23 | treating: number, 24 | cases: number, 25 | recovered: number, 26 | casesToday: number, 27 | }; 28 | 29 | export interface cityVNOneWeek { 30 | date: string, 31 | death: number, 32 | treating: number, 33 | cases: number, 34 | recovered: number, 35 | avgCases7day: number, 36 | avgRecovered7day: number, 37 | avgDeath7day: number, 38 | }; -------------------------------------------------------------------------------- /utils/utils.ts: -------------------------------------------------------------------------------- 1 | import * as countryData from '../assets/countries.json'; 2 | 3 | const wordsStandardize = (word:string):string => word ? word.trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '') : ''; 4 | 5 | const getCountryData = (country:string):country => { 6 | const countryName = wordsStandardize(country); 7 | const nullReturn = { _id: null, country: null, iso2: null, iso3: null, lat: 0, long: 0 }; 8 | const countryFound = countryData.find(item => (wordsStandardize(item.country) === countryName || wordsStandardize(item.iso2) === countryName || wordsStandardize(item.iso3) === countryName || item.id === parseInt(countryName)) || !!(item.possibleNames ? item.possibleNames : []).find(synonym => wordsStandardize(synonym) === countryName)); 9 | return countryFound ? { 10 | _id: countryFound.id, 11 | country: countryFound.country, 12 | iso2: countryFound.iso2, 13 | iso3: countryFound.iso3, 14 | lat: countryFound.lat, 15 | long: countryFound.long, 16 | } : nullReturn; 17 | }; 18 | 19 | 20 | export { 21 | getCountryData, 22 | wordsStandardize, 23 | }; 24 | 25 | export interface country { 26 | _id: number; 27 | country: string; 28 | iso2: string; 29 | iso3: string; 30 | lat: number; 31 | long: number; 32 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ncov-api 2 | API cung cấp thông tin, dữ liệu bệnh nhân về COVID-19! 3 | 4 | [![issue](https://img.shields.io/github/issues/phamleduy04/ncov-api?style=for-the-badge)](https://github.com/phamleduy04/ncov-api/issues) 5 | [![package.json](https://img.shields.io/github/package-json/v/phamleduy04/ncov-api?label=Package.json&style=for-the-badge)](https://github.com/phamleduy04/ncov-api/blob/master/package.json) 6 | [![GitHub contributors](https://img.shields.io/github/contributors/phamleduy04/ncov-api?color=g&style=for-the-badge)](https://img.shields.io/github/contributors/phamleduy04/ncov-api?color=g&style=for-the-badge) 7 | [![Visits Badge](https://badges.pufler.dev/visits/phamleduy04/ncov-api?style=for-the-badge)](https://badges.pufler.dev) 8 | ![GitHub top language](https://img.shields.io/github/languages/top/phamleduy04/ncov-api?style=for-the-badge) 9 | [![David](https://img.shields.io/david/phamleduy04/ncov-api?style=for-the-badge)](https://david-dm.org/phamleduy04/ncov-api) 10 | [![DeepScan grade](https://deepscan.io/api/teams/11483/projects/18299/branches/446016/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=11483&pid=18299&bid=446016) 11 | 12 | 13 | 14 | ## Cách setup API chạy local 15 | ### Yêu cầu: node + mongodb 16 | - Bước 1: Clone repo về máy 17 | - Bước 2: Tải deps bằng npm, yarn hoặc pnpm. 18 | - Bước 3: Tạo file `.env` và nhập `MONGODB=` sau đó là link mongodb 19 | - Bước 4: `npm run start:dev` để bắt đầu server dev. ( Nếu bạn muốn test các endpoints thì hãy edit file test sau đó sử dụng `npm run test` ) để chạy 20 | -------------------------------------------------------------------------------- /src/routes/wom/wom.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Param, Query } from '@nestjs/common'; 2 | import { womService } from './wom.service'; 3 | import type { WOMCountryData, WOMWorldData } from '../../../scrapers/wom.world'; 4 | import type { WOMUsState } from '../../../scrapers/wom.usstate'; 5 | 6 | @Controller('wom') 7 | export class womController { 8 | // eslint-disable-next-line no-empty-function 9 | constructor(private readonly appService: womService) { } 10 | 11 | @Get() 12 | async getAllWom(@Query() query: { yesterday: string }): Promise { 13 | const yesterday = (query.yesterday == 'true'); 14 | return this.appService.getWorldData(yesterday); 15 | } 16 | 17 | @Get('countries') 18 | async getAllCountries(@Query() query: { yesterday: string }): Promise { 19 | const yesterday = (query.yesterday == 'true'); 20 | return this.appService.getAllCountries(yesterday); 21 | } 22 | 23 | @Get('countries/:country') 24 | async getCountryData(@Query() query: { yesterday: string }, @Param() param: { country: string }): Promise { 25 | const yesterday = (query.yesterday == 'true'); 26 | return this.appService.getCountryName(yesterday, param.country); 27 | } 28 | 29 | @Get('states') 30 | async getAllStatesData(@Query() query: { yesterday: string }): Promise { 31 | const yesterday = (query.yesterday == 'true'); 32 | return this.appService.getAllUSSates(yesterday); 33 | } 34 | 35 | @Get('states/:state') 36 | async getStateData(@Query() query: { yesterday: string }, @Param() param: { state: string }): Promise { 37 | const yesterday = (query.yesterday == 'true'); 38 | return this.appService.getUSState(yesterday, param.state); 39 | } 40 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "ignorePatterns": ["node_modules/*"], 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint/eslint-plugin"], 5 | "env": { 6 | "node": true, 7 | "jest": true, 8 | "es2020": true, 9 | }, 10 | extends: [ 11 | 'plugin:@typescript-eslint/recommended', 12 | 'prettier', 13 | ], 14 | "parserOptions": { 15 | "project": "tsconfig.json", 16 | "sourceType": "module", 17 | }, 18 | "rules": { 19 | "@typescript-eslint/interface-name-prefix": "off", 20 | "@typescript-eslint/explicit-function-return-type": "off", 21 | "@typescript-eslint/explicit-module-boundary-types": "off", 22 | "@typescript-eslint/no-explicit-any": "off", 23 | "comma-dangle": ["error", "always-multiline"], 24 | "comma-spacing": "error", 25 | "comma-style": "error", 26 | "dot-location": ["error", "property"], 27 | "handle-callback-err": "off", 28 | "max-nested-callbacks": ["error", { "max": 4 }], 29 | "max-statements-per-line": ["error", { "max": 2 }], 30 | "no-console": "off", 31 | "no-empty-function": "error", 32 | "no-floating-decimal": "error", 33 | "no-inline-comments": "error", 34 | "no-lonely-if": "error", 35 | "no-multi-spaces": "error", 36 | "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], 37 | "no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], 38 | "no-trailing-spaces": ["error"], 39 | "no-var": "error", 40 | "object-curly-spacing": ["error", "always"], 41 | "prefer-const": "error", 42 | "space-before-blocks": "error", 43 | "space-before-function-paren": ["error", { 44 | "anonymous": "never", 45 | "named": "never", 46 | "asyncArrow": "always", 47 | }], 48 | "space-in-parens": "error", 49 | "space-infix-ops": "error", 50 | "space-unary-ops": "error", 51 | "spaced-comment": "error", 52 | "yoda": "error", 53 | "semi": "error", 54 | }, 55 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ncov-api", 3 | "version": "0.2.0", 4 | "description": "API for the COVID-19 data", 5 | "author": "phamleduy04", 6 | "license": "GNUv3", 7 | "scripts": { 8 | "prebuild": "rimraf dist", 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "eslint \"{src,apps,libs,test,database,utils}/**/*.ts\"", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@keyv/etcd": "^1.2.0", 24 | "@keyv/mongo": "^2.2.8", 25 | "@keyv/mysql": "^1.6.8", 26 | "@keyv/postgres": "^1.4.7", 27 | "@keyv/redis": "^2.7.0", 28 | "@keyv/sqlite": "^3.6.5", 29 | "@nestjs/common": "^10.0.5", 30 | "@nestjs/core": "^10.0.5", 31 | "@nestjs/platform-express": "^10.0.5", 32 | "cheerio": "^1.0.0-rc.12", 33 | "colorette": "^2.0.20", 34 | "dayjs": "^1.11.9", 35 | "dotenv": "^16.3.1", 36 | "keyv": "^4.5.2", 37 | "reflect-metadata": "^0.1.13", 38 | "rimraf": "^5.0.1", 39 | "rxjs": "^7.8.1", 40 | "undici": "^5.22.1" 41 | }, 42 | "devDependencies": { 43 | "@nestjs/cli": "^10.1.7", 44 | "@nestjs/schematics": "^10.0.1", 45 | "@nestjs/testing": "^10.0.5", 46 | "@types/cheerio": "^0.22.31", 47 | "@types/express": "^4.17.17", 48 | "@types/jest": "29.5.2", 49 | "@types/node": "^20.4.0", 50 | "@types/supertest": "^2.0.12", 51 | "@typescript-eslint/eslint-plugin": "^5.61.0", 52 | "@typescript-eslint/parser": "^5.61.0", 53 | "eslint": "^8.44.0", 54 | "eslint-config-prettier": "^8.8.0", 55 | "eslint-plugin-prettier": "^4.2.1", 56 | "jest": "^29.6.1", 57 | "prettier": "^3.0.0", 58 | "source-map-support": "^0.5.21", 59 | "ts-jest": "^29.1.1", 60 | "ts-loader": "^9.4.4", 61 | "ts-node": "^10.9.1", 62 | "tsconfig-paths": "^4.2.0", 63 | "typescript": "^5.1.6" 64 | }, 65 | "jest": { 66 | "moduleFileExtensions": [ 67 | "js", 68 | "json", 69 | "ts" 70 | ], 71 | "rootDir": "src", 72 | "testRegex": ".*\\.spec\\.ts$", 73 | "transform": { 74 | "^.+\\.(t|j)s$": "ts-jest" 75 | }, 76 | "collectCoverageFrom": [ 77 | "**/*.(t|j)s" 78 | ], 79 | "coverageDirectory": "../coverage", 80 | "testEnvironment": "node" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /scrapers/wom.usstate.ts: -------------------------------------------------------------------------------- 1 | import * as cheerio from 'cheerio'; 2 | import * as log from '../utils/log'; 3 | 4 | import { request } from 'undici'; 5 | import { set } from '../database'; 6 | 7 | const parseNumberCell = (cell) => { 8 | const cellValue = cell.children.length !== 0 ? cell.children[0].data : ''; 9 | return parseFloat(cellValue.replace(/[,+\-\s]/g, '')) || null; 10 | }; 11 | 12 | const fillResult = (html:cheerio.Root, yesterday = false):WOMUsState[] => { 13 | const statesTable = html(yesterday ? 'table#usa_table_countries_yesterday' : 'table#usa_table_countries_today'); 14 | const tableRows = statesTable.children('tbody').children('tr:not(.total_row)').get(); 15 | const stateColIndex = 1; 16 | const dataColIndexes = { 17 | cases: 2, 18 | todayCases: 3, 19 | deaths: 4, 20 | todayDeaths: 5, 21 | recovered: 6, 22 | active: 7, 23 | casesPerOneMillion: 8, 24 | deathsPerOneMillion: 9, 25 | tests: 10, 26 | testsPerOneMillion: 11, 27 | population: 12, 28 | }; 29 | return tableRows.map((row) => { 30 | const cells = row.children.filter(el => el.name === 'td'); 31 | const stateData = { state: cheerio.load(cells[stateColIndex])('td').text().replace(/\n/g, '').trim(), updated: Date.now(), cases: 0, todayCases: 0, deaths: 0, todayDeaths: 0, recovered: 0, active: 0, casesPerOneMillion: 0, deathsPerOneMillion: 0, tests: 0, testsPerOneMillion: 0, population: 0 }; 32 | Object.keys(dataColIndexes).forEach((property) => stateData[property] = parseNumberCell(cells[dataColIndexes[property]])); 33 | return stateData; 34 | }); 35 | }; 36 | 37 | const getStates = async ():Promise => { 38 | try { 39 | const response = await request('https://www.worldometers.info/coronavirus/country/us/').then(res => res.body.text()); 40 | const html = cheerio.load(response); 41 | const statesData:WOMUsState[] = fillResult(html).filter(el => el.state !== 'USA Total'); 42 | await set('USToday', statesData); 43 | log.info(`Updated US states: ${statesData.length} states`); 44 | 45 | const statesYesterday:WOMUsState[] = fillResult(html, true).filter(el => el.state !== 'USA Total'); 46 | await set('USYesterday', statesYesterday); 47 | log.info(`Updated US states yesterday: ${statesYesterday.length} states`); 48 | } 49 | catch (err) { 50 | log.error('Error getting US states', err); 51 | return; 52 | } 53 | }; 54 | 55 | export interface WOMUsState { 56 | state: string, 57 | updated: number, 58 | cases: number, 59 | todayCases: number, 60 | deaths: number, 61 | todayDeaths: number, 62 | recovered: number, 63 | active: number, 64 | casesPerOneMillion: number, 65 | deathsPerOneMillion: number, 66 | tests: number, 67 | testsPerOneMillion: number, 68 | population: number, 69 | } 70 | 71 | export default getStates; -------------------------------------------------------------------------------- /src/routes/wom/wom.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, BadRequestException } from '@nestjs/common'; 2 | import { get } from '../../../database'; 3 | import { getCountryData, wordsStandardize } from '../../../utils/utils'; 4 | import type { WOMWorldData, WOMCountryData } from '../../../scrapers/wom.world'; 5 | import type { WOMUsState } from '../../../scrapers/wom.usstate'; 6 | 7 | @Injectable() 8 | export class womService { 9 | 10 | async getWorldData(yesterday = false): Promise { 11 | const countries = await get(yesterday ? 'womYesterday' : 'womToday'); 12 | const worldData = countries.find(elem => elem.country.toLowerCase() === 'world'); 13 | worldData.affectedCountries = countries.length - 1; 14 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 15 | const { country, countryInfo, ...cleanedWorldData } = worldData; 16 | return cleanedWorldData; 17 | } 18 | 19 | async getAllCountries(yesterday = false): Promise { 20 | const data:WOMCountryData[] = (await get(yesterday ? 'womYesterday' : 'womToday')).filter(elem => elem.country.toLowerCase() !== 'world').map(fixApostrophe).map(el => el); 21 | return data; 22 | }; 23 | 24 | async getCountryName(yesterday = false, query:string): Promise { 25 | const data:WOMCountryData[] = (await get(yesterday ? 'womYesterday' : 'womToday')).filter(elem => elem.country.toLowerCase() !== 'world').map(fixApostrophe).map(el => el); 26 | const countryData = getCountryWOHData(data, query) || null; 27 | return countryData; 28 | } 29 | 30 | async getAllUSSates(yesterday = false): Promise { 31 | const data:WOMUsState[] = (await get(yesterday ? 'USYesterday' : 'USToday')); 32 | return data; 33 | }; 34 | 35 | async getUSState(yesterday = false, query:string): Promise { 36 | const data:WOMUsState[] = (await get(yesterday ? 'USYesterday' : 'USToday')); 37 | const stateData = data.find(el => el.state.toLowerCase() === query.toLowerCase()); 38 | if (!stateData) throw new BadRequestException('Not found'); 39 | return stateData; 40 | } 41 | } 42 | 43 | function fixApostrophe(country) { 44 | country.country = country.country.replace(/"/g, '\''); 45 | return country; 46 | } 47 | 48 | function getCountryWOHData(data, nameParam) { 49 | const countryInfo = isNaN(nameParam) ? getCountryData(nameParam) : { country: null }; 50 | const standardizedName = wordsStandardize(countryInfo.country ? countryInfo.country : nameParam); 51 | return data.find((country) => { 52 | if (!isNaN(nameParam)) return country.countryInfo && country.countryInfo._id === Number(nameParam); 53 | if (country.continent && country.continent.includes('-')) return search(country, nameParam, standardizedName); 54 | else return wordsStandardize(country.country) === standardizedName; 55 | }); 56 | } 57 | 58 | function search(country, nameParam, standardizedName) { 59 | return ((country.countryInfo || {}).iso3 || '').toLowerCase() === nameParam.toLowerCase() 60 | || ((country.countryInfo || {}).iso2 || '').toLowerCase() === nameParam.toLowerCase() 61 | || wordsStandardize(country['country']).includes(standardizedName); 62 | } -------------------------------------------------------------------------------- /scrapers/wom.world.ts: -------------------------------------------------------------------------------- 1 | import * as cheerio from 'cheerio'; 2 | import { request } from 'undici'; 3 | import { info, error } from '../utils/log'; 4 | import { set } from '../database'; 5 | import { getCountryData } from '../utils/utils'; 6 | const columns: string[] = ['index', 'country', 'cases', 'todayCases', 'deaths', 'todayDeaths', 'recovered', 'todayRecovered', 'active', 'critical']; 7 | 8 | // Returns country data list ordered by country name 9 | const getOrderByCountryName = (data) => data.sort((a, b) => a.country < b.country ? -1 : 1); 10 | 11 | // Maps a row from worldometers to a country 12 | const mapRows = (_, row):WOMCountryData=> { 13 | const entry = { updated: Date.now(), countryInfo: { _id: null, iso2: null, iso3: null, lat: 0, long: 0 }, active: 0, cases: 0, recovered: 0, deaths: 0, critical: 0, todayCases: 0, todayDeaths: 0, todayRecovered: 0, country: '' }; 14 | const replaceRegex = /(\n|,)/g; 15 | cheerio.load(row)('td').each((index, cell: any) => { 16 | const selector: any = columns[index]; 17 | if (!selector) return; 18 | cell = cheerio.load(cell); 19 | switch (index) { 20 | case 0: 21 | break; 22 | case 1: { 23 | const countryInfo = getCountryData(cell.text().replace(replaceRegex, '')); 24 | entry[selector] = countryInfo.country || cell.text().replace(replaceRegex, ''); 25 | delete countryInfo.country; 26 | entry.countryInfo = countryInfo; 27 | break; 28 | } 29 | default: 30 | entry[selector] = parseFloat(cell.text().replace(replaceRegex, '')) || null; 31 | } 32 | }); 33 | !entry.active && (entry.active = entry.cases - entry.recovered - entry.deaths); 34 | return entry; 35 | }; 36 | 37 | // Fills an array full of table data parsed from worldometers 38 | function fillResult(html, idExtension) { 39 | const countriesTable = html(`table#main_table_countries_${idExtension}`); 40 | const countries = countriesTable.children('tbody:first-of-type').children('tr:not(.row_continent)').map(mapRows).get(); 41 | const world = countries.shift(); 42 | return { world, countries }; 43 | } 44 | 45 | // Scrap and update to mongodb 46 | const getWorldometerPage = async () => { 47 | try { 48 | const res = await request('https://www.worldometers.info/coronavirus/'); 49 | const html = cheerio.load(await res.body.text()); 50 | ['today', 'yesterday'].forEach(key => { 51 | const data = fillResult(html, key); 52 | set(key === 'today' ? 'womToday' : 'womYesterday', [data.world, ...getOrderByCountryName(data.countries)]); 53 | info(`Updated ${key} countries statistics: ${data.countries.length + 1}`); 54 | }); 55 | } catch (err) { 56 | error('Error: Requesting WorldoMeters failed!', err); 57 | } 58 | }; 59 | 60 | export default getWorldometerPage; 61 | 62 | export interface WOMCountryData { 63 | "updated": number, 64 | "countryInfo": { 65 | "_id": number, 66 | "iso2": string, 67 | "iso3": string, 68 | "lat": number, 69 | "long": number 70 | }, 71 | "active": number | null, 72 | "cases": number, 73 | "recovered": number, 74 | "deaths": number, 75 | "country": string, 76 | "todayCases": number | null, 77 | "todayDeaths": number | null, 78 | "todayRecovered": number | null, 79 | "critical": number | null 80 | } 81 | 82 | export interface WOMWorldData{ 83 | "updated": number, 84 | "active": number, 85 | "cases": number, 86 | "recovered": number, 87 | "deaths": number, 88 | "critical": number, 89 | "todayCases": number, 90 | "todayDeaths": number, 91 | "todayRecovered": number, 92 | "affectedCountries": number 93 | } -------------------------------------------------------------------------------- /src/wom.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { womController } from './routes/wom/wom.controller'; 3 | import { womService } from './routes/wom/wom.service'; 4 | import executeScraper from '../scrapers/instances'; 5 | 6 | describe('AppController', () => { 7 | let appController: womController; 8 | 9 | beforeEach(async () => { 10 | const app: TestingModule = await Test.createTestingModule({ 11 | controllers: [womController], 12 | providers: [womService], 13 | }).compile(); 14 | 15 | appController = app.get(womController); 16 | }); 17 | beforeAll(async () => { 18 | await executeScraper(); 19 | }); 20 | 21 | 22 | describe('root', () => { 23 | it('/wom correct type', async () => { 24 | const result = await appController.getAllWom({ yesterday: 'false' }); 25 | expect(result).toBeDefined(); 26 | expect(result).toHaveProperty('updated'); 27 | expect(result).toHaveProperty('active'); 28 | expect(result).toHaveProperty('cases'); 29 | expect(result).toHaveProperty('recovered'); 30 | expect(result).toHaveProperty('deaths'); 31 | expect(result).toHaveProperty('critical'); 32 | expect(result).toHaveProperty('todayCases'); 33 | expect(result).toHaveProperty('todayDeaths'); 34 | expect(result).toHaveProperty('todayRecovered'); 35 | expect(result).toHaveProperty('affectedCountries'); 36 | }); 37 | 38 | it('/wom yesterday correct type', async () => { 39 | const result = await appController.getAllWom({ yesterday: 'true' }); 40 | expect(result).toBeDefined(); 41 | expect(result).toHaveProperty('updated'); 42 | expect(result).toHaveProperty('active'); 43 | expect(result).toHaveProperty('cases'); 44 | expect(result).toHaveProperty('recovered'); 45 | expect(result).toHaveProperty('deaths'); 46 | expect(result).toHaveProperty('critical'); 47 | expect(result).toHaveProperty('todayCases'); 48 | expect(result).toHaveProperty('todayDeaths'); 49 | expect(result).toHaveProperty('todayRecovered'); 50 | expect(result).toHaveProperty('affectedCountries'); 51 | }); 52 | 53 | it('/wom/countries correct type', async () => { 54 | const result = await appController.getAllCountries({ yesterday: 'false' }); 55 | expect(result).toBeDefined(); 56 | expect(result).toBeInstanceOf(Array); 57 | result.forEach(element => { 58 | expect(element).toHaveProperty('updated'); 59 | expect(element).toHaveProperty('countryInfo'); 60 | expect(element.countryInfo).toHaveProperty('_id'); 61 | expect(element.countryInfo).toHaveProperty('iso2'); 62 | expect(element.countryInfo).toHaveProperty('iso3'); 63 | expect(element.countryInfo).toHaveProperty('lat'); 64 | expect(element.countryInfo).toHaveProperty('long'); 65 | expect(element).toHaveProperty('active'); 66 | expect(element).toHaveProperty('cases'); 67 | expect(element).toHaveProperty('recovered'); 68 | expect(element).toHaveProperty('deaths'); 69 | expect(element).toHaveProperty('critical'); 70 | expect(element).toHaveProperty('country'); 71 | }); 72 | }); 73 | 74 | it('/wom/countries yesterday correct type', async () => { 75 | const result = await appController.getAllCountries({ yesterday: 'true' }); 76 | expect(result).toBeDefined(); 77 | expect(result).toBeInstanceOf(Array); 78 | result.forEach(element => { 79 | expect(element).toHaveProperty('updated'); 80 | expect(element).toHaveProperty('countryInfo'); 81 | expect(element.countryInfo).toHaveProperty('_id'); 82 | expect(element.countryInfo).toHaveProperty('iso2'); 83 | expect(element.countryInfo).toHaveProperty('iso3'); 84 | expect(element.countryInfo).toHaveProperty('lat'); 85 | expect(element.countryInfo).toHaveProperty('long'); 86 | expect(element).toHaveProperty('active'); 87 | expect(element).toHaveProperty('cases'); 88 | expect(element).toHaveProperty('recovered'); 89 | expect(element).toHaveProperty('deaths'); 90 | expect(element).toHaveProperty('critical'); 91 | expect(element).toHaveProperty('country'); 92 | }); 93 | }); 94 | 95 | it('/wom/countries/ search correct type', async () => { 96 | const result = await appController.getCountryData({ yesterday: 'false' }, { country: 'Vietnam' }); 97 | expect(result).toBeDefined(); 98 | expect(result).toHaveProperty('updated'); 99 | expect(result).toHaveProperty('countryInfo'); 100 | expect(result.countryInfo).toHaveProperty('_id'); 101 | expect(result.countryInfo).toHaveProperty('iso2'); 102 | expect(result.countryInfo).toHaveProperty('iso3'); 103 | expect(result.countryInfo).toHaveProperty('lat'); 104 | expect(result.countryInfo).toHaveProperty('long'); 105 | expect(result).toHaveProperty('active'); 106 | expect(result).toHaveProperty('cases'); 107 | expect(result).toHaveProperty('recovered'); 108 | expect(result).toHaveProperty('deaths'); 109 | expect(result).toHaveProperty('critical'); 110 | expect(result).toHaveProperty('country'); 111 | }); 112 | 113 | it('/wom/countries/ yesterday search correct type', async () => { 114 | const result = await appController.getCountryData({ yesterday: 'true' }, { country: 'Vietnam' }); 115 | expect(result).toBeDefined(); 116 | expect(result).toHaveProperty('updated'); 117 | expect(result).toHaveProperty('countryInfo'); 118 | expect(result.countryInfo).toHaveProperty('_id'); 119 | expect(result.countryInfo).toHaveProperty('iso2'); 120 | expect(result.countryInfo).toHaveProperty('iso3'); 121 | expect(result.countryInfo).toHaveProperty('lat'); 122 | expect(result.countryInfo).toHaveProperty('long'); 123 | expect(result).toHaveProperty('active'); 124 | expect(result).toHaveProperty('cases'); 125 | expect(result).toHaveProperty('recovered'); 126 | expect(result).toHaveProperty('deaths'); 127 | expect(result).toHaveProperty('critical'); 128 | expect(result).toHaveProperty('country'); 129 | }); 130 | }); 131 | 132 | it('/wom/countries/ search correct alternative name', async () => { 133 | const result = await appController.getCountryData({ yesterday: 'false' }, { country: 'viet nam' }); 134 | expect(result).toBeDefined(); 135 | expect(result.country).toBe('Vietnam'); 136 | }); 137 | 138 | it('/wom/countries/ yesterday search correct alternative name', async () => { 139 | const result = await appController.getCountryData({ yesterday: 'true' }, { country: 'viet nam' }); 140 | expect(result).toBeDefined(); 141 | expect(result.country).toBe('Vietnam'); 142 | }); 143 | 144 | it('/wom/countries/ correct iso2', async () => { 145 | const result = await appController.getCountryData({ yesterday: 'false' }, { country: 'vn' }); 146 | expect(result).toBeDefined(); 147 | expect(result.country).toBe('Vietnam'); 148 | }); 149 | 150 | it('/wom/countries/ yesterday correct iso2', async () => { 151 | const result = await appController.getCountryData({ yesterday: 'true' }, { country: 'vn' }); 152 | expect(result).toBeDefined(); 153 | expect(result.country).toBe('Vietnam'); 154 | }); 155 | 156 | it('/wom/countries/ correct id', async () => { 157 | const result = await appController.getCountryData({ yesterday: 'false' }, { country: '704' }); 158 | expect(result).toBeDefined(); 159 | expect(result.country).toBe('Vietnam'); 160 | }); 161 | 162 | it('/wom/countries/ yesterday correct id', async () => { 163 | const result = await appController.getCountryData({ yesterday: 'true' }, { country: '704' }); 164 | expect(result).toBeDefined(); 165 | expect(result.country).toBe('Vietnam'); 166 | }); 167 | 168 | it('/wom/countries/ incorrect name', async () => { 169 | const result = await appController.getCountryData({ yesterday: 'false' }, { country: 'aipsdhjashgfka' }); 170 | expect(result).toBeNull(); 171 | }); 172 | 173 | it('/wom/countries/ yesteday incorrect name', async () => { 174 | const result = await appController.getCountryData({ yesterday: 'true' }, { country: 'aipsdhjashgfka' }); 175 | expect(result).toBeNull(); 176 | }); 177 | 178 | it('/wom/state correct type', async () => { 179 | const result = await appController.getAllStatesData({ yesterday: 'false' }); 180 | expect(result).toBeDefined(); 181 | expect(result).toBeInstanceOf(Array); 182 | result.forEach(element => { 183 | expect(element).toHaveProperty('state'); 184 | expect(element).toHaveProperty('updated'); 185 | expect(element).toHaveProperty('cases'); 186 | expect(element).toHaveProperty('todayCases'); 187 | expect(element).toHaveProperty('deaths'); 188 | expect(element).toHaveProperty('todayDeaths'); 189 | expect(element).toHaveProperty('recovered'); 190 | expect(element).toHaveProperty('active'); 191 | expect(element).toHaveProperty('casesPerOneMillion'); 192 | expect(element).toHaveProperty('deathsPerOneMillion'); 193 | expect(element).toHaveProperty('tests'); 194 | expect(element).toHaveProperty('testsPerOneMillion'); 195 | expect(element).toHaveProperty('population'); 196 | }); 197 | }); 198 | 199 | it('/wom/state yesterday correct type', async () => { 200 | const result = await appController.getAllStatesData({ yesterday: 'true' }); 201 | expect(result).toBeDefined(); 202 | expect(result).toBeInstanceOf(Array); 203 | result.forEach(element => { 204 | expect(element).toHaveProperty('state'); 205 | expect(element).toHaveProperty('updated'); 206 | expect(element).toHaveProperty('cases'); 207 | expect(element).toHaveProperty('todayCases'); 208 | expect(element).toHaveProperty('deaths'); 209 | expect(element).toHaveProperty('todayDeaths'); 210 | expect(element).toHaveProperty('recovered'); 211 | expect(element).toHaveProperty('active'); 212 | expect(element).toHaveProperty('casesPerOneMillion'); 213 | expect(element).toHaveProperty('deathsPerOneMillion'); 214 | expect(element).toHaveProperty('tests'); 215 | expect(element).toHaveProperty('testsPerOneMillion'); 216 | expect(element).toHaveProperty('population'); 217 | }); 218 | }); 219 | 220 | }); 221 | -------------------------------------------------------------------------------- /assets/countries.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "country": "Afghanistan", 4 | "iso2": "AF", 5 | "iso3": "AFG", 6 | "id": 4, 7 | "lat": 33, 8 | "long": 65, 9 | "woh": "afghanistan" 10 | }, 11 | { 12 | "country": "Albania", 13 | "iso2": "AL", 14 | "iso3": "ALB", 15 | "id": 8, 16 | "lat": 41, 17 | "long": 20, 18 | "woh": "albania" 19 | }, 20 | { 21 | "country": "Algeria", 22 | "iso2": "DZ", 23 | "iso3": "DZA", 24 | "id": 12, 25 | "lat": 28, 26 | "long": 3, 27 | "woh": "algeria" 28 | }, 29 | { 30 | "country": "American Samoa", 31 | "iso2": "AS", 32 | "iso3": "ASM", 33 | "id": 16, 34 | "lat": -14.3333, 35 | "long": -170, 36 | "woh": "samoa" 37 | }, 38 | { 39 | "country": "Andorra", 40 | "iso2": "AD", 41 | "iso3": "AND", 42 | "id": 20, 43 | "lat": 42.5, 44 | "long": 1.6, 45 | "woh": "andorra" 46 | }, 47 | { 48 | "country": "Angola", 49 | "iso2": "AO", 50 | "iso3": "AGO", 51 | "id": 24, 52 | "lat": -12.5, 53 | "long": 18.5, 54 | "woh": "angola" 55 | }, 56 | { 57 | "country": "Anguilla", 58 | "iso2": "AI", 59 | "iso3": "AIA", 60 | "id": 660, 61 | "lat": 18.25, 62 | "long": -63.1667, 63 | "woh": "anguilla" 64 | }, 65 | { 66 | "country": "Antarctica", 67 | "iso2": "AQ", 68 | "iso3": "ATA", 69 | "id": 10, 70 | "lat": -90, 71 | "long": 0 72 | }, 73 | { 74 | "country": "Antigua and Barbuda", 75 | "iso2": "AG", 76 | "iso3": "ATG", 77 | "id": 28, 78 | "lat": 17.05, 79 | "long": -61.8, 80 | "woh": "antigua-and-barbuda" 81 | }, 82 | { 83 | "country": "Argentina", 84 | "iso2": "AR", 85 | "iso3": "ARG", 86 | "id": 32, 87 | "lat": -34, 88 | "long": -64, 89 | "woh": "argentina" 90 | }, 91 | { 92 | "country": "Armenia", 93 | "iso2": "AM", 94 | "iso3": "ARM", 95 | "id": 51, 96 | "lat": 40, 97 | "long": 45, 98 | "woh": "armenia" 99 | }, 100 | { 101 | "country": "Aruba", 102 | "iso2": "AW", 103 | "iso3": "ABW", 104 | "id": 533, 105 | "lat": 12.5, 106 | "long": -69.9667, 107 | "woh": "aruba" 108 | }, 109 | { 110 | "country": "Australia", 111 | "iso2": "AU", 112 | "iso3": "AUS", 113 | "id": 36, 114 | "lat": -27, 115 | "long": 133, 116 | "woh": "australia" 117 | }, 118 | { 119 | "country": "Austria", 120 | "iso2": "AT", 121 | "iso3": "AUT", 122 | "id": 40, 123 | "lat": 47.3333, 124 | "long": 13.3333, 125 | "woh": "austria" 126 | }, 127 | { 128 | "country": "Azerbaijan", 129 | "iso2": "AZ", 130 | "iso3": "AZE", 131 | "id": 31, 132 | "lat": 40.5, 133 | "long": 47.5, 134 | "woh": "azerbaijan" 135 | }, 136 | { 137 | "country": "Bahamas", 138 | "iso2": "BS", 139 | "iso3": "BHS", 140 | "id": 44, 141 | "lat": 24.25, 142 | "long": -76, 143 | "woh": "bahamas" 144 | }, 145 | { 146 | "country": "Bahrain", 147 | "iso2": "BH", 148 | "iso3": "BHR", 149 | "id": 48, 150 | "lat": 26, 151 | "long": 50.55, 152 | "woh": "bahrain" 153 | }, 154 | { 155 | "country": "Bangladesh", 156 | "iso2": "BD", 157 | "iso3": "BGD", 158 | "id": 50, 159 | "lat": 24, 160 | "long": 90, 161 | "woh": "bangladesh" 162 | }, 163 | { 164 | "country": "Barbados", 165 | "iso2": "BB", 166 | "iso3": "BRB", 167 | "id": 52, 168 | "lat": 13.1667, 169 | "long": -59.5333, 170 | "woh": "barbados" 171 | }, 172 | { 173 | "country": "Belarus", 174 | "iso2": "BY", 175 | "iso3": "BLR", 176 | "id": 112, 177 | "lat": 53, 178 | "long": 28, 179 | "woh": "belarus" 180 | }, 181 | { 182 | "country": "Belgium", 183 | "iso2": "BE", 184 | "iso3": "BEL", 185 | "id": 56, 186 | "lat": 50.8333, 187 | "long": 4, 188 | "woh": "belgium" 189 | }, 190 | { 191 | "country": "Belize", 192 | "iso2": "BZ", 193 | "iso3": "BLZ", 194 | "id": 84, 195 | "lat": 17.25, 196 | "long": -88.75, 197 | "woh": "belize" 198 | }, 199 | { 200 | "country": "Benin", 201 | "iso2": "BJ", 202 | "iso3": "BEN", 203 | "id": 204, 204 | "lat": 9.5, 205 | "long": 2.25, 206 | "woh": "benin" 207 | }, 208 | { 209 | "country": "Bermuda", 210 | "iso2": "BM", 211 | "iso3": "BMU", 212 | "id": 60, 213 | "lat": 32.3333, 214 | "long": -64.75, 215 | "woh": "bermuda" 216 | }, 217 | { 218 | "country": "Bhutan", 219 | "iso2": "BT", 220 | "iso3": "BTN", 221 | "id": 64, 222 | "lat": 27.5, 223 | "long": 90.5, 224 | "woh": "bhutan" 225 | }, 226 | { 227 | "country": "Bolivia", 228 | "iso2": "BO", 229 | "iso3": "BOL", 230 | "id": 68, 231 | "lat": -17, 232 | "long": -65, 233 | "possibleNames": [ 234 | "Bolivia, Plurinational State of" 235 | ], 236 | "woh": "bolivia" 237 | }, 238 | { 239 | "country": "Bosnia", 240 | "iso2": "BA", 241 | "iso3": "BIH", 242 | "id": 70, 243 | "lat": 44, 244 | "long": 18, 245 | "possibleNames": [ 246 | "Bosnia and Herzegovina" 247 | ], 248 | "woh": "bosnia-and-herzegovina" 249 | }, 250 | { 251 | "country": "Botswana", 252 | "iso2": "BW", 253 | "iso3": "BWA", 254 | "id": 72, 255 | "lat": -22, 256 | "long": 24, 257 | "woh": "botswana" 258 | }, 259 | { 260 | "country": "Bouvet Island", 261 | "iso2": "BV", 262 | "iso3": "BVT", 263 | "id": 74, 264 | "lat": -54.4333, 265 | "long": 3.4 266 | }, 267 | { 268 | "country": "Brazil", 269 | "iso2": "BR", 270 | "iso3": "BRA", 271 | "id": 76, 272 | "lat": -10, 273 | "long": -55, 274 | "possibleNames": [ 275 | "Brasil" 276 | ], 277 | "woh": "brazil" 278 | }, 279 | { 280 | "country": "British Indian Ocean Territory", 281 | "iso2": "IO", 282 | "iso3": "IOT", 283 | "id": 86, 284 | "lat": -6, 285 | "long": 71.5 286 | }, 287 | { 288 | "country": "Brunei", 289 | "iso2": "BN", 290 | "iso3": "BRN", 291 | "id": 96, 292 | "lat": 4.5, 293 | "long": 114.6667, 294 | "possibleNames": [ 295 | "Brunei Darussalam" 296 | ], 297 | "woh": "brunei-darussalam" 298 | }, 299 | { 300 | "country": "Bulgaria", 301 | "iso2": "BG", 302 | "iso3": "BGR", 303 | "id": 100, 304 | "lat": 43, 305 | "long": 25, 306 | "woh": "bulgaria" 307 | }, 308 | { 309 | "country": "Burkina Faso", 310 | "iso2": "BF", 311 | "iso3": "BFA", 312 | "id": 854, 313 | "lat": 13, 314 | "long": -2, 315 | "woh": "burkina-faso" 316 | }, 317 | { 318 | "country": "Burundi", 319 | "iso2": "BI", 320 | "iso3": "BDI", 321 | "id": 108, 322 | "lat": -3.5, 323 | "long": 30, 324 | "woh": "burundi" 325 | }, 326 | { 327 | "country": "Cabo Verde", 328 | "iso2": "CV", 329 | "iso3": "CPV", 330 | "id": 132, 331 | "lat": 16, 332 | "long": -24, 333 | "possibleNames": [ 334 | "Cape Verde" 335 | ], 336 | "woh": "cabo-verde" 337 | }, 338 | { 339 | "country": "Cambodia", 340 | "iso2": "KH", 341 | "iso3": "KHM", 342 | "id": 116, 343 | "lat": 13, 344 | "long": 105, 345 | "woh": "cambodia" 346 | }, 347 | { 348 | "country": "Cameroon", 349 | "iso2": "CM", 350 | "iso3": "CMR", 351 | "id": 120, 352 | "lat": 6, 353 | "long": 12, 354 | "woh": "cameroon" 355 | }, 356 | { 357 | "country": "Canada", 358 | "iso2": "CA", 359 | "iso3": "CAN", 360 | "id": 124, 361 | "lat": 60, 362 | "long": -95, 363 | "woh": "canada" 364 | }, 365 | { 366 | "country": "Cayman Islands", 367 | "iso2": "KY", 368 | "iso3": "CYM", 369 | "id": 136, 370 | "lat": 19.5, 371 | "long": -80.5, 372 | "woh": "cayman-islands" 373 | }, 374 | { 375 | "country": "Central African Republic", 376 | "iso2": "CF", 377 | "iso3": "CAF", 378 | "id": 140, 379 | "lat": 7, 380 | "long": 21, 381 | "possibleNames": [ 382 | "CAR" 383 | ] 384 | }, 385 | { 386 | "country": "Caribbean Netherlands", 387 | "iso2": "BQ", 388 | "iso3": "BES", 389 | "id": 535, 390 | "lat": 12.2, 391 | "long": -68.26, 392 | "possibleNames": [ 393 | "Bonaire", 394 | "San Eustaquio", 395 | "Saba" 396 | ], 397 | "woh": "caribbean-netherlands" 398 | }, 399 | { 400 | "country": "Chad", 401 | "iso2": "TD", 402 | "iso3": "TCD", 403 | "id": 148, 404 | "lat": 15, 405 | "long": 19, 406 | "woh": "chad" 407 | }, 408 | { 409 | "country": "Chile", 410 | "iso2": "CL", 411 | "iso3": "CHL", 412 | "id": 152, 413 | "lat": -30, 414 | "long": -71, 415 | "woh": "chile" 416 | }, 417 | { 418 | "country": "China", 419 | "iso2": "CN", 420 | "iso3": "CHN", 421 | "id": 156, 422 | "lat": 35, 423 | "long": 105, 424 | "woh": "china" 425 | }, 426 | { 427 | "country": "Christmas Island", 428 | "iso2": "CX", 429 | "iso3": "CXR", 430 | "id": 162, 431 | "lat": -10.5, 432 | "long": 105.6667 433 | }, 434 | { 435 | "country": "Cocos (Keeling) Islands", 436 | "iso2": "CC", 437 | "iso3": "CCK", 438 | "id": 166, 439 | "lat": -12.5, 440 | "long": 96.8333 441 | }, 442 | { 443 | "country": "Colombia", 444 | "iso2": "CO", 445 | "iso3": "COL", 446 | "id": 170, 447 | "lat": 4, 448 | "long": -72, 449 | "woh": "colombia" 450 | }, 451 | { 452 | "country": "Comoros", 453 | "iso2": "KM", 454 | "iso3": "COM", 455 | "id": 174, 456 | "lat": -12.1667, 457 | "long": 44.25, 458 | "woh": "comoros" 459 | }, 460 | { 461 | "country": "Congo", 462 | "iso2": "CG", 463 | "iso3": "COG", 464 | "id": 178, 465 | "lat": -1, 466 | "long": 15, 467 | "possibleNames": [ 468 | "Congo, Republic of", 469 | "Congo (Brazzaville)" 470 | ], 471 | "woh": "congo" 472 | }, 473 | { 474 | "country": "DRC", 475 | "iso2": "CD", 476 | "iso3": "COD", 477 | "id": 180, 478 | "lat": 0, 479 | "long": 25, 480 | "possibleNames": [ 481 | "Congo, the Democratic Republic of the", 482 | "Congo (Kinshasa)", 483 | "Congo Kinshasa", 484 | "Kinshasa" 485 | ], 486 | "woh": "democratic-republic-of-the-congo" 487 | }, 488 | { 489 | "country": "Cook Islands", 490 | "iso2": "CK", 491 | "iso3": "COK", 492 | "id": 184, 493 | "lat": -21.2333, 494 | "long": -159.7667 495 | }, 496 | { 497 | "country": "Costa Rica", 498 | "iso2": "CR", 499 | "iso3": "CRI", 500 | "id": 188, 501 | "lat": 10, 502 | "long": -84, 503 | "woh": "costa-rica" 504 | }, 505 | { 506 | "country": "Côte d'Ivoire", 507 | "iso2": "CI", 508 | "iso3": "CIV", 509 | "id": 384, 510 | "lat": 8, 511 | "long": -5, 512 | "possibleNames": [ 513 | "Ivory Coast", 514 | "Coast D'Ivoire" 515 | ], 516 | "woh": "cote-d-ivoire" 517 | }, 518 | { 519 | "country": "Croatia", 520 | "iso2": "HR", 521 | "iso3": "HRV", 522 | "id": 191, 523 | "lat": 45.1667, 524 | "long": 15.5, 525 | "woh": "croatia" 526 | }, 527 | { 528 | "country": "Cuba", 529 | "iso2": "CU", 530 | "iso3": "CUB", 531 | "id": 192, 532 | "lat": 21.5, 533 | "long": -80, 534 | "woh": "cuba" 535 | }, 536 | { 537 | "country": "Curaçao", 538 | "iso2": "CW", 539 | "iso3": "CUW", 540 | "id": 531, 541 | "lat": 12.15, 542 | "long": -68.97, 543 | "possibleNames": [ 544 | "Curazao", 545 | "Curacao" 546 | ], 547 | "woh": "curacao" 548 | }, 549 | { 550 | "country": "Cyprus", 551 | "iso2": "CY", 552 | "iso3": "CYP", 553 | "id": 196, 554 | "lat": 35, 555 | "long": 33, 556 | "woh": "cyprus" 557 | }, 558 | { 559 | "country": "Czechia", 560 | "iso2": "CZ", 561 | "iso3": "CZE", 562 | "id": 203, 563 | "lat": 49.75, 564 | "long": 15.5, 565 | "possibleNames": [ 566 | "Czech Republic", 567 | "Chequia" 568 | ], 569 | "woh": "czech-republic" 570 | }, 571 | { 572 | "country": "Denmark", 573 | "iso2": "DK", 574 | "iso3": "DNK", 575 | "id": 208, 576 | "lat": 56, 577 | "long": 10, 578 | "woh": "denmark" 579 | }, 580 | { 581 | "country": "Djibouti", 582 | "iso2": "DJ", 583 | "iso3": "DJI", 584 | "id": 262, 585 | "lat": 11.5, 586 | "long": 43, 587 | "woh": "djibouti" 588 | }, 589 | { 590 | "country": "Dominica", 591 | "iso2": "DM", 592 | "iso3": "DMA", 593 | "id": 212, 594 | "lat": 15.4167, 595 | "long": -61.3333, 596 | "woh": "dominica" 597 | }, 598 | { 599 | "country": "Dominican Republic", 600 | "iso2": "DO", 601 | "iso3": "DOM", 602 | "id": 214, 603 | "lat": 19, 604 | "long": -70.6667, 605 | "possibleNames": [ 606 | "DR" 607 | ], 608 | "woh": "dominican-republic" 609 | }, 610 | { 611 | "country": "Ecuador", 612 | "iso2": "EC", 613 | "iso3": "ECU", 614 | "id": 218, 615 | "lat": -2, 616 | "long": -77.5, 617 | "woh": "ecuador" 618 | }, 619 | { 620 | "country": "Egypt", 621 | "iso2": "EG", 622 | "iso3": "EGY", 623 | "id": 818, 624 | "lat": 27, 625 | "long": 30, 626 | "woh": "egypt" 627 | }, 628 | { 629 | "country": "El Salvador", 630 | "iso2": "SV", 631 | "iso3": "SLV", 632 | "id": 222, 633 | "lat": 13.8333, 634 | "long": -88.9167, 635 | "woh": "el-salvador" 636 | }, 637 | { 638 | "country": "Equatorial Guinea", 639 | "iso2": "GQ", 640 | "iso3": "GNQ", 641 | "id": 226, 642 | "lat": 2, 643 | "long": 10, 644 | "woh": "equatorial-guinea" 645 | }, 646 | { 647 | "country": "Eritrea", 648 | "iso2": "ER", 649 | "iso3": "ERI", 650 | "id": 232, 651 | "lat": 15, 652 | "long": 39, 653 | "woh": "eritrea" 654 | }, 655 | { 656 | "country": "Estonia", 657 | "iso2": "EE", 658 | "iso3": "EST", 659 | "id": 233, 660 | "lat": 59, 661 | "long": 26, 662 | "woh": "estonia" 663 | }, 664 | { 665 | "country": "Ethiopia", 666 | "iso2": "ET", 667 | "iso3": "ETH", 668 | "id": 231, 669 | "lat": 8, 670 | "long": 38, 671 | "woh": "ethiopia" 672 | }, 673 | { 674 | "country": "Falkland Islands (Malvinas)", 675 | "iso2": "FK", 676 | "iso3": "FLK", 677 | "id": 238, 678 | "lat": -51.75, 679 | "long": -59, 680 | "possibleNames": [ 681 | "Falkland Islands", 682 | "Malvinas" 683 | ], 684 | "woh": "falkland-islands-malvinas" 685 | }, 686 | { 687 | "country": "Faroe Islands", 688 | "iso2": "FO", 689 | "iso3": "FRO", 690 | "id": 234, 691 | "lat": 62, 692 | "long": -7, 693 | "possibleNames": [ 694 | "Faeroe Islands" 695 | ] 696 | }, 697 | { 698 | "country": "Fiji", 699 | "iso2": "FJ", 700 | "iso3": "FJI", 701 | "id": 242, 702 | "lat": -18, 703 | "long": 175, 704 | "woh": "fiji" 705 | }, 706 | { 707 | "country": "Finland", 708 | "iso2": "FI", 709 | "iso3": "FIN", 710 | "id": 246, 711 | "lat": 64, 712 | "long": 26, 713 | "woh": "finland" 714 | }, 715 | { 716 | "country": "France", 717 | "iso2": "FR", 718 | "iso3": "FRA", 719 | "id": 250, 720 | "lat": 46, 721 | "long": 2, 722 | "woh": "france" 723 | }, 724 | { 725 | "country": "French Guiana", 726 | "iso2": "GF", 727 | "iso3": "GUF", 728 | "id": 254, 729 | "lat": 4, 730 | "long": -53, 731 | "woh": "french-guiana" 732 | }, 733 | { 734 | "country": "French Polynesia", 735 | "iso2": "PF", 736 | "iso3": "PYF", 737 | "id": 258, 738 | "lat": -15, 739 | "long": -140, 740 | "woh": "french-polynesia" 741 | }, 742 | { 743 | "country": "French Southern Territories", 744 | "iso2": "TF", 745 | "iso3": "ATF", 746 | "id": 260, 747 | "lat": -43, 748 | "long": 67 749 | }, 750 | { 751 | "country": "Gabon", 752 | "iso2": "GA", 753 | "iso3": "GAB", 754 | "id": 266, 755 | "lat": -1, 756 | "long": 11.75, 757 | "woh": "gabon" 758 | }, 759 | { 760 | "country": "Gambia", 761 | "iso2": "GM", 762 | "iso3": "GMB", 763 | "id": 270, 764 | "lat": 13.4667, 765 | "long": -16.5667, 766 | "woh": "gambia" 767 | }, 768 | { 769 | "country": "Georgia", 770 | "iso2": "GE", 771 | "iso3": "GEO", 772 | "id": 268, 773 | "lat": 42, 774 | "long": 43.5, 775 | "woh": "georgia" 776 | }, 777 | { 778 | "country": "Germany", 779 | "iso2": "DE", 780 | "iso3": "DEU", 781 | "id": 276, 782 | "lat": 51, 783 | "long": 9, 784 | "woh": "germany" 785 | }, 786 | { 787 | "country": "Ghana", 788 | "iso2": "GH", 789 | "iso3": "GHA", 790 | "id": 288, 791 | "lat": 8, 792 | "long": -2, 793 | "woh": "ghana" 794 | }, 795 | { 796 | "country": "Gibraltar", 797 | "iso2": "GI", 798 | "iso3": "GIB", 799 | "id": 292, 800 | "lat": 36.1833, 801 | "long": -5.3667, 802 | "woh": "gibraltar" 803 | }, 804 | { 805 | "country": "Greece", 806 | "iso2": "GR", 807 | "iso3": "GRC", 808 | "id": 300, 809 | "lat": 39, 810 | "long": 22, 811 | "woh": "greece" 812 | }, 813 | { 814 | "country": "Greenland", 815 | "iso2": "GL", 816 | "iso3": "GRL", 817 | "id": 304, 818 | "lat": 72, 819 | "long": -40, 820 | "woh": "greenland" 821 | }, 822 | { 823 | "country": "Grenada", 824 | "iso2": "GD", 825 | "iso3": "GRD", 826 | "id": 308, 827 | "lat": 12.1167, 828 | "long": -61.6667, 829 | "woh": "grenada" 830 | }, 831 | { 832 | "country": "Guadeloupe", 833 | "iso2": "GP", 834 | "iso3": "GLP", 835 | "id": 312, 836 | "lat": 16.25, 837 | "long": -61.5833, 838 | "woh": "guadeloupe" 839 | }, 840 | { 841 | "country": "Guam", 842 | "iso2": "GU", 843 | "iso3": "GUM", 844 | "id": 316, 845 | "lat": 13.4667, 846 | "long": 144.7833 847 | }, 848 | { 849 | "country": "Guatemala", 850 | "iso2": "GT", 851 | "iso3": "GTM", 852 | "id": 320, 853 | "lat": 15.5, 854 | "long": -90.25, 855 | "woh": "guatemala" 856 | }, 857 | { 858 | "country": "Guernsey", 859 | "iso2": "GG", 860 | "iso3": "GGY", 861 | "id": 831, 862 | "lat": 49.5, 863 | "long": -2.56 864 | }, 865 | { 866 | "country": "Guinea", 867 | "iso2": "GN", 868 | "iso3": "GIN", 869 | "id": 324, 870 | "lat": 11, 871 | "long": -10, 872 | "woh": "guinea" 873 | }, 874 | { 875 | "country": "Guinea-Bissau", 876 | "iso2": "GW", 877 | "iso3": "GNB", 878 | "id": 624, 879 | "lat": 12, 880 | "long": -15, 881 | "woh": "guinea-bissau" 882 | }, 883 | { 884 | "country": "Guyana", 885 | "iso2": "GY", 886 | "iso3": "GUY", 887 | "id": 328, 888 | "lat": 5, 889 | "long": -59, 890 | "woh": "guyana" 891 | }, 892 | { 893 | "country": "Haiti", 894 | "iso2": "HT", 895 | "iso3": "HTI", 896 | "id": 332, 897 | "lat": 19, 898 | "long": -72.4167, 899 | "woh": "haiti" 900 | }, 901 | { 902 | "country": "Heard Island and McDonald Islands", 903 | "iso2": "HM", 904 | "iso3": "HMD", 905 | "id": 334, 906 | "lat": -53.1, 907 | "long": 72.5167 908 | }, 909 | { 910 | "country": "Holy See (Vatican City State)", 911 | "iso2": "VA", 912 | "iso3": "VAT", 913 | "id": 336, 914 | "lat": 41.9, 915 | "long": 12.45, 916 | "possibleNames": [ 917 | "Vatican City" 918 | ], 919 | "woh": "holy-see" 920 | }, 921 | { 922 | "country": "Honduras", 923 | "iso2": "HN", 924 | "iso3": "HND", 925 | "id": 340, 926 | "lat": 15, 927 | "long": -86.5, 928 | "woh": "honduras" 929 | }, 930 | { 931 | "country": "Hong Kong", 932 | "iso2": "HK", 933 | "iso3": "HKG", 934 | "id": 344, 935 | "lat": 22.25, 936 | "long": 114.1667, 937 | "woh": "china-hong-kong-sar" 938 | }, 939 | { 940 | "country": "Hungary", 941 | "iso2": "HU", 942 | "iso3": "HUN", 943 | "id": 348, 944 | "lat": 47, 945 | "long": 20, 946 | "woh": "hungary" 947 | }, 948 | { 949 | "country": "Iceland", 950 | "iso2": "IS", 951 | "iso3": "ISL", 952 | "id": 352, 953 | "lat": 65, 954 | "long": -18, 955 | "woh": "iceland" 956 | }, 957 | { 958 | "country": "India", 959 | "iso2": "IN", 960 | "iso3": "IND", 961 | "id": 356, 962 | "lat": 20, 963 | "long": 77, 964 | "woh": "india" 965 | }, 966 | { 967 | "country": "Indonesia", 968 | "iso2": "ID", 969 | "iso3": "IDN", 970 | "id": 360, 971 | "lat": -5, 972 | "long": 120, 973 | "woh": "indonesia" 974 | }, 975 | { 976 | "country": "Iran", 977 | "iso2": "IR", 978 | "iso3": "IRN", 979 | "id": 364, 980 | "lat": 32, 981 | "long": 53, 982 | "possibleNames": [ 983 | "Iran, Islamic Republic of" 984 | ], 985 | "woh": "iran" 986 | }, 987 | { 988 | "country": "Iraq", 989 | "iso2": "IQ", 990 | "iso3": "IRQ", 991 | "id": 368, 992 | "lat": 33, 993 | "long": 44, 994 | "woh": "iraq" 995 | }, 996 | { 997 | "country": "Ireland", 998 | "iso2": "IE", 999 | "iso3": "IRL", 1000 | "id": 372, 1001 | "lat": 53, 1002 | "long": -8, 1003 | "woh": "ireland" 1004 | }, 1005 | { 1006 | "country": "Isle of Man", 1007 | "iso2": "IM", 1008 | "iso3": "IMN", 1009 | "id": 833, 1010 | "lat": 54.23, 1011 | "long": -4.55, 1012 | "woh": "isle-of-man" 1013 | }, 1014 | { 1015 | "country": "Israel", 1016 | "iso2": "IL", 1017 | "iso3": "ISR", 1018 | "id": 376, 1019 | "lat": 31.5, 1020 | "long": 34.75, 1021 | "woh": "israel" 1022 | }, 1023 | { 1024 | "country": "Italy", 1025 | "iso2": "IT", 1026 | "iso3": "ITA", 1027 | "id": 380, 1028 | "lat": 42.8333, 1029 | "long": 12.8333, 1030 | "possibleNames": [ 1031 | "Italia" 1032 | ], 1033 | "woh": "italy" 1034 | }, 1035 | { 1036 | "country": "Jamaica", 1037 | "iso2": "JM", 1038 | "iso3": "JAM", 1039 | "id": 388, 1040 | "lat": 18.25, 1041 | "long": -77.5, 1042 | "woh": "jamaica" 1043 | }, 1044 | { 1045 | "country": "Japan", 1046 | "iso2": "JP", 1047 | "iso3": "JPN", 1048 | "id": 392, 1049 | "lat": 36, 1050 | "long": 138, 1051 | "possibleNames": [ 1052 | "Japón" 1053 | ], 1054 | "woh": "japan" 1055 | }, 1056 | { 1057 | "country": "Channel Islands", 1058 | "iso2": "JE", 1059 | "iso3": "JEY", 1060 | "id": 832, 1061 | "lat": 49.21, 1062 | "long": -2.13, 1063 | "possibleNames": [ 1064 | "Jersey", 1065 | "Guernsey" 1066 | ], 1067 | "woh": "channel-islands" 1068 | }, 1069 | { 1070 | "country": "Jordan", 1071 | "iso2": "JO", 1072 | "iso3": "JOR", 1073 | "id": 400, 1074 | "lat": 31, 1075 | "long": 36, 1076 | "woh": "jordan" 1077 | }, 1078 | { 1079 | "country": "Kazakhstan", 1080 | "iso2": "KZ", 1081 | "iso3": "KAZ", 1082 | "id": 398, 1083 | "lat": 48, 1084 | "long": 68, 1085 | "woh": "kazakhstan" 1086 | }, 1087 | { 1088 | "country": "Kenya", 1089 | "iso2": "KE", 1090 | "iso3": "KEN", 1091 | "id": 404, 1092 | "lat": 1, 1093 | "long": 38, 1094 | "woh": "kenya" 1095 | }, 1096 | { 1097 | "country": "Kiribati", 1098 | "iso2": "KI", 1099 | "iso3": "KIR", 1100 | "id": 296, 1101 | "lat": 1.4167, 1102 | "long": 173 1103 | }, 1104 | { 1105 | "country": "Kosovo", 1106 | "iso2": "XK", 1107 | "iso3": "RKS", 1108 | "id": 895, 1109 | "lat": 42.6, 1110 | "long": 20.9 1111 | }, 1112 | { 1113 | "country": "N. Korea", 1114 | "iso2": "KP", 1115 | "iso3": "PRK", 1116 | "id": 408, 1117 | "lat": 40, 1118 | "long": 127, 1119 | "possibleNames": [ 1120 | "Korea del Norte", 1121 | "North Korea", 1122 | "Korea, North", 1123 | "Democratic People's Republic of Korea" 1124 | ] 1125 | }, 1126 | { 1127 | "country": "S. Korea", 1128 | "iso2": "KR", 1129 | "iso3": "KOR", 1130 | "id": 410, 1131 | "lat": 37, 1132 | "long": 127.5, 1133 | "possibleNames": [ 1134 | "Korea del Sur", 1135 | "South Korea", 1136 | "Korea, South", 1137 | "Republic of Korea" 1138 | ], 1139 | "woh": "south-korea" 1140 | }, 1141 | { 1142 | "country": "Kuwait", 1143 | "iso2": "KW", 1144 | "iso3": "KWT", 1145 | "id": 414, 1146 | "lat": 29.3375, 1147 | "long": 47.6581, 1148 | "woh": "kuwait" 1149 | }, 1150 | { 1151 | "country": "Kyrgyzstan", 1152 | "iso2": "KG", 1153 | "iso3": "KGZ", 1154 | "id": 417, 1155 | "lat": 41, 1156 | "long": 75, 1157 | "woh": "kyrgyzstan" 1158 | }, 1159 | { 1160 | "country": "Lao People's Democratic Republic", 1161 | "iso2": "LA", 1162 | "iso3": "LAO", 1163 | "id": 418, 1164 | "lat": 18, 1165 | "long": 105, 1166 | "possibleNames": [ 1167 | "Laos" 1168 | ], 1169 | "woh": "laos" 1170 | }, 1171 | { 1172 | "country": "Latvia", 1173 | "iso2": "LV", 1174 | "iso3": "LVA", 1175 | "id": 428, 1176 | "lat": 57, 1177 | "long": 25, 1178 | "woh": "latvia" 1179 | }, 1180 | { 1181 | "country": "Lebanon", 1182 | "iso2": "LB", 1183 | "iso3": "LBN", 1184 | "id": 422, 1185 | "lat": 33.8333, 1186 | "long": 35.8333, 1187 | "woh": "lebanon" 1188 | }, 1189 | { 1190 | "country": "Lesotho", 1191 | "iso2": "LS", 1192 | "iso3": "LSO", 1193 | "id": 426, 1194 | "lat": -29.5, 1195 | "long": 28.5, 1196 | "woh": "lesotho" 1197 | }, 1198 | { 1199 | "country": "Liberia", 1200 | "iso2": "LR", 1201 | "iso3": "LBR", 1202 | "id": 430, 1203 | "lat": 6.5, 1204 | "long": -9.5, 1205 | "woh": "liberia" 1206 | }, 1207 | { 1208 | "country": "Libyan Arab Jamahiriya", 1209 | "iso2": "LY", 1210 | "iso3": "LBY", 1211 | "id": 434, 1212 | "lat": 25, 1213 | "long": 17, 1214 | "possibleNames": [ 1215 | "Libya", 1216 | "Libia" 1217 | ], 1218 | "woh": "libya" 1219 | }, 1220 | { 1221 | "country": "Liechtenstein", 1222 | "iso2": "LI", 1223 | "iso3": "LIE", 1224 | "id": 438, 1225 | "lat": 47.1667, 1226 | "long": 9.5333, 1227 | "woh": "liechtenstein" 1228 | }, 1229 | { 1230 | "country": "Lithuania", 1231 | "iso2": "LT", 1232 | "iso3": "LTU", 1233 | "id": 440, 1234 | "lat": 56, 1235 | "long": 24, 1236 | "woh": "lithuania" 1237 | }, 1238 | { 1239 | "country": "Luxembourg", 1240 | "iso2": "LU", 1241 | "iso3": "LUX", 1242 | "id": 442, 1243 | "lat": 49.75, 1244 | "long": 6.1667, 1245 | "woh": "luxembourg" 1246 | }, 1247 | { 1248 | "country": "Macao", 1249 | "iso2": "MO", 1250 | "iso3": "MAC", 1251 | "id": 446, 1252 | "lat": 22.1667, 1253 | "long": 113.55, 1254 | "possibleNames": [ 1255 | "Macao", 1256 | "macau" 1257 | ], 1258 | "woh": "china-macao-sar" 1259 | }, 1260 | { 1261 | "country": "Macedonia", 1262 | "iso2": "MK", 1263 | "iso3": "MKD", 1264 | "id": 807, 1265 | "lat": 41.8333, 1266 | "long": 22, 1267 | "possibleNames": [ 1268 | "North Macedonia", 1269 | "Macedonia, the former Yugoslav Republic of" 1270 | ], 1271 | "woh": "macedonia" 1272 | }, 1273 | { 1274 | "country": "Madagascar", 1275 | "iso2": "MG", 1276 | "iso3": "MDG", 1277 | "id": 450, 1278 | "lat": -20, 1279 | "long": 47, 1280 | "woh": "madagascar" 1281 | }, 1282 | { 1283 | "country": "Malawi", 1284 | "iso2": "MW", 1285 | "iso3": "MWI", 1286 | "id": 454, 1287 | "lat": -13.5, 1288 | "long": 34, 1289 | "woh": "malawi" 1290 | }, 1291 | { 1292 | "country": "Malaysia", 1293 | "iso2": "MY", 1294 | "iso3": "MYS", 1295 | "id": 458, 1296 | "lat": 2.5, 1297 | "long": 112.5, 1298 | "woh": "malaysia" 1299 | }, 1300 | { 1301 | "country": "Maldives", 1302 | "iso2": "MV", 1303 | "iso3": "MDV", 1304 | "id": 462, 1305 | "lat": 3.25, 1306 | "long": 73, 1307 | "woh": "maldives" 1308 | }, 1309 | { 1310 | "country": "Mali", 1311 | "iso2": "ML", 1312 | "iso3": "MLI", 1313 | "id": 466, 1314 | "lat": 17, 1315 | "long": -4, 1316 | "woh": "mali" 1317 | }, 1318 | { 1319 | "country": "Malta", 1320 | "iso2": "MT", 1321 | "iso3": "MLT", 1322 | "id": 470, 1323 | "lat": 35.8333, 1324 | "long": 14.5833, 1325 | "woh": "malta" 1326 | }, 1327 | { 1328 | "country": "Marshall Islands", 1329 | "iso2": "MH", 1330 | "iso3": "MHL", 1331 | "id": 584, 1332 | "lat": 9, 1333 | "long": 168, 1334 | "woh": "marshall-islands" 1335 | }, 1336 | { 1337 | "country": "Martinique", 1338 | "iso2": "MQ", 1339 | "iso3": "MTQ", 1340 | "id": 474, 1341 | "lat": 14.6667, 1342 | "long": -61, 1343 | "woh": "martinique" 1344 | }, 1345 | { 1346 | "country": "Mauritania", 1347 | "iso2": "MR", 1348 | "iso3": "MRT", 1349 | "id": 478, 1350 | "lat": 20, 1351 | "long": -12, 1352 | "woh": "mauritania" 1353 | }, 1354 | { 1355 | "country": "Mauritius", 1356 | "iso2": "MU", 1357 | "iso3": "MUS", 1358 | "id": 480, 1359 | "lat": -20.2833, 1360 | "long": 57.55, 1361 | "woh": "mauritius" 1362 | }, 1363 | { 1364 | "country": "Mayotte", 1365 | "iso2": "YT", 1366 | "iso3": "MYT", 1367 | "id": 175, 1368 | "lat": -12.8333, 1369 | "long": 45.1667, 1370 | "woh": "mayotte" 1371 | }, 1372 | { 1373 | "country": "Mexico", 1374 | "iso2": "MX", 1375 | "iso3": "MEX", 1376 | "id": 484, 1377 | "lat": 23, 1378 | "long": -102, 1379 | "woh": "mexico" 1380 | }, 1381 | { 1382 | "country": "Micronesia", 1383 | "iso2": "FM", 1384 | "iso3": "FSM", 1385 | "id": 583, 1386 | "lat": 6.9167, 1387 | "long": 158.25, 1388 | "possibleNames": [ 1389 | "Micronesia, Federated States of" 1390 | ], 1391 | "woh": "micronesia" 1392 | }, 1393 | { 1394 | "country": "Moldova", 1395 | "iso2": "MD", 1396 | "iso3": "MDA", 1397 | "id": 498, 1398 | "lat": 47, 1399 | "long": 29, 1400 | "possibleNames": [ 1401 | "Moldova, Republic of" 1402 | ], 1403 | "woh": "moldova" 1404 | }, 1405 | { 1406 | "country": "Monaco", 1407 | "iso2": "MC", 1408 | "iso3": "MCO", 1409 | "id": 492, 1410 | "lat": 43.7333, 1411 | "long": 7.4, 1412 | "woh": "monaco" 1413 | }, 1414 | { 1415 | "country": "Mongolia", 1416 | "iso2": "MN", 1417 | "iso3": "MNG", 1418 | "id": 496, 1419 | "lat": 46, 1420 | "long": 105, 1421 | "woh": "mongolia" 1422 | }, 1423 | { 1424 | "country": "Montenegro", 1425 | "iso2": "ME", 1426 | "iso3": "MNE", 1427 | "id": 499, 1428 | "lat": 42, 1429 | "long": 19, 1430 | "woh": "montenegro" 1431 | }, 1432 | { 1433 | "country": "Montserrat", 1434 | "iso2": "MS", 1435 | "iso3": "MSR", 1436 | "id": 500, 1437 | "lat": 16.75, 1438 | "long": -62.2, 1439 | "woh": "montserrat" 1440 | }, 1441 | { 1442 | "country": "Morocco", 1443 | "iso2": "MA", 1444 | "iso3": "MAR", 1445 | "id": 504, 1446 | "lat": 32, 1447 | "long": -5, 1448 | "woh": "morocco" 1449 | }, 1450 | { 1451 | "country": "Mozambique", 1452 | "iso2": "MZ", 1453 | "iso3": "MOZ", 1454 | "id": 508, 1455 | "lat": -18.25, 1456 | "long": 35, 1457 | "woh": "mozambique" 1458 | }, 1459 | { 1460 | "country": "Myanmar", 1461 | "iso2": "MM", 1462 | "iso3": "MMR", 1463 | "id": 104, 1464 | "lat": 22, 1465 | "long": 98, 1466 | "woh": "myanmar" 1467 | }, 1468 | { 1469 | "country": "Burma", 1470 | "iso2": "BU", 1471 | "iso3": "BUR", 1472 | "id": 104, 1473 | "lat": 22, 1474 | "long": 98 1475 | }, 1476 | { 1477 | "country": "Namibia", 1478 | "iso2": "NA", 1479 | "iso3": "NAM", 1480 | "id": 516, 1481 | "lat": -22, 1482 | "long": 17, 1483 | "woh": "namibia" 1484 | }, 1485 | { 1486 | "country": "Nauru", 1487 | "iso2": "NR", 1488 | "iso3": "NRU", 1489 | "id": 520, 1490 | "lat": -0.5333, 1491 | "long": 166.9167 1492 | }, 1493 | { 1494 | "country": "Nepal", 1495 | "iso2": "NP", 1496 | "iso3": "NPL", 1497 | "id": 524, 1498 | "lat": 28, 1499 | "long": 84, 1500 | "woh": "nepal" 1501 | }, 1502 | { 1503 | "country": "Netherlands", 1504 | "iso2": "NL", 1505 | "iso3": "NLD", 1506 | "id": 528, 1507 | "lat": 52.5, 1508 | "long": 5.75, 1509 | "woh": "netherlands" 1510 | }, 1511 | { 1512 | "country": "Netherlands Antilles", 1513 | "iso2": "AN", 1514 | "iso3": "ANT", 1515 | "id": 530, 1516 | "lat": 12.25, 1517 | "long": -68.75 1518 | }, 1519 | { 1520 | "country": "New Caledonia", 1521 | "iso2": "NC", 1522 | "iso3": "NCL", 1523 | "id": 540, 1524 | "lat": -21.5, 1525 | "long": 165.5, 1526 | "woh": "new-caledonia" 1527 | }, 1528 | { 1529 | "country": "New Zealand", 1530 | "iso2": "NZ", 1531 | "iso3": "NZL", 1532 | "id": 554, 1533 | "lat": -41, 1534 | "long": 174, 1535 | "woh": "new-zealand" 1536 | }, 1537 | { 1538 | "country": "Nicaragua", 1539 | "iso2": "NI", 1540 | "iso3": "NIC", 1541 | "id": 558, 1542 | "lat": 13, 1543 | "long": -85, 1544 | "woh": "nicaragua" 1545 | }, 1546 | { 1547 | "country": "Niger", 1548 | "iso2": "NE", 1549 | "iso3": "NER", 1550 | "id": 562, 1551 | "lat": 16, 1552 | "long": 8, 1553 | "woh": "niger" 1554 | }, 1555 | { 1556 | "country": "Nigeria", 1557 | "iso2": "NG", 1558 | "iso3": "NGA", 1559 | "id": 566, 1560 | "lat": 10, 1561 | "long": 8, 1562 | "woh": "nigeria" 1563 | }, 1564 | { 1565 | "country": "Niue", 1566 | "iso2": "NU", 1567 | "iso3": "NIU", 1568 | "id": 570, 1569 | "lat": -19.0333, 1570 | "long": -169.8667 1571 | }, 1572 | { 1573 | "country": "Norfolk Island", 1574 | "iso2": "NF", 1575 | "iso3": "NFK", 1576 | "id": 574, 1577 | "lat": -29.0333, 1578 | "long": 167.95 1579 | }, 1580 | { 1581 | "country": "Northern Mariana Islands", 1582 | "iso2": "MP", 1583 | "iso3": "MNP", 1584 | "id": 580, 1585 | "lat": 15.2, 1586 | "long": 145.75 1587 | }, 1588 | { 1589 | "country": "Norway", 1590 | "iso2": "NO", 1591 | "iso3": "NOR", 1592 | "id": 578, 1593 | "lat": 62, 1594 | "long": 10, 1595 | "woh": "norway" 1596 | }, 1597 | { 1598 | "country": "Oman", 1599 | "iso2": "OM", 1600 | "iso3": "OMN", 1601 | "id": 512, 1602 | "lat": 21, 1603 | "long": 57, 1604 | "woh": "oman" 1605 | }, 1606 | { 1607 | "country": "Pakistan", 1608 | "iso2": "PK", 1609 | "iso3": "PAK", 1610 | "id": 586, 1611 | "lat": 30, 1612 | "long": 70, 1613 | "woh": "pakistan" 1614 | }, 1615 | { 1616 | "country": "Palau", 1617 | "iso2": "PW", 1618 | "iso3": "PLW", 1619 | "id": 585, 1620 | "lat": 7.5, 1621 | "long": 134.5 1622 | }, 1623 | { 1624 | "country": "Palestine", 1625 | "iso2": "PS", 1626 | "iso3": "PSE", 1627 | "id": 275, 1628 | "lat": 32, 1629 | "long": 35.25, 1630 | "possibleNames": [ 1631 | "Palestinian Territory, Occupied" 1632 | ], 1633 | "woh": "state-of-palestine" 1634 | }, 1635 | { 1636 | "country": "Panama", 1637 | "iso2": "PA", 1638 | "iso3": "PAN", 1639 | "id": 591, 1640 | "lat": 9, 1641 | "long": -80, 1642 | "woh": "panama" 1643 | }, 1644 | { 1645 | "country": "Papua New Guinea", 1646 | "iso2": "PG", 1647 | "iso3": "PNG", 1648 | "id": 598, 1649 | "lat": -6, 1650 | "long": 147, 1651 | "woh": "papua-new-guinea" 1652 | }, 1653 | { 1654 | "country": "Paraguay", 1655 | "iso2": "PY", 1656 | "iso3": "PRY", 1657 | "id": 600, 1658 | "lat": -23, 1659 | "long": -58, 1660 | "woh": "paraguay" 1661 | }, 1662 | { 1663 | "country": "Peru", 1664 | "iso2": "PE", 1665 | "iso3": "PER", 1666 | "id": 604, 1667 | "lat": -10, 1668 | "long": -76, 1669 | "woh": "peru" 1670 | }, 1671 | { 1672 | "country": "Philippines", 1673 | "iso2": "PH", 1674 | "iso3": "PHL", 1675 | "id": 608, 1676 | "lat": 13, 1677 | "long": 122, 1678 | "woh": "philippines" 1679 | }, 1680 | { 1681 | "country": "Pitcairn", 1682 | "iso2": "PN", 1683 | "iso3": "PCN", 1684 | "id": 612, 1685 | "lat": -24.7, 1686 | "long": -127.4 1687 | }, 1688 | { 1689 | "country": "Poland", 1690 | "iso2": "PL", 1691 | "iso3": "POL", 1692 | "id": 616, 1693 | "lat": 52, 1694 | "long": 20, 1695 | "woh": "poland" 1696 | }, 1697 | { 1698 | "country": "Portugal", 1699 | "iso2": "PT", 1700 | "iso3": "PRT", 1701 | "id": 620, 1702 | "lat": 39.5, 1703 | "long": -8, 1704 | "woh": "portugal" 1705 | }, 1706 | { 1707 | "country": "Puerto Rico", 1708 | "iso2": "PR", 1709 | "iso3": "PRI", 1710 | "id": 630, 1711 | "lat": 18.25, 1712 | "long": -66.5 1713 | }, 1714 | { 1715 | "country": "Qatar", 1716 | "iso2": "QA", 1717 | "iso3": "QAT", 1718 | "id": 634, 1719 | "lat": 25.5, 1720 | "long": 51.25, 1721 | "woh": "qatar" 1722 | }, 1723 | { 1724 | "country": "Réunion", 1725 | "iso2": "RE", 1726 | "iso3": "REU", 1727 | "id": 638, 1728 | "lat": -21.1, 1729 | "long": 55.6, 1730 | "possibleNames": [ 1731 | "Reunion" 1732 | ], 1733 | "woh": "reunion" 1734 | }, 1735 | { 1736 | "country": "Romania", 1737 | "iso2": "RO", 1738 | "iso3": "ROU", 1739 | "id": 642, 1740 | "lat": 46, 1741 | "long": 25, 1742 | "woh": "romania" 1743 | }, 1744 | { 1745 | "country": "Russia", 1746 | "iso2": "RU", 1747 | "iso3": "RUS", 1748 | "id": 643, 1749 | "lat": 60, 1750 | "long": 100, 1751 | "possibleNames": [ 1752 | "Russian Federation" 1753 | ], 1754 | "woh": "russia" 1755 | }, 1756 | { 1757 | "country": "Rwanda", 1758 | "iso2": "RW", 1759 | "iso3": "RWA", 1760 | "id": 646, 1761 | "lat": -2, 1762 | "long": 30, 1763 | "woh": "rwanda" 1764 | }, 1765 | { 1766 | "country": "St. Barth", 1767 | "iso2": "BL", 1768 | "iso3": "BLM", 1769 | "id": 652, 1770 | "lat": 17.89, 1771 | "long": -62.82, 1772 | "woh": "saint-barthelemy" 1773 | }, 1774 | { 1775 | "country": "Saint Helena", 1776 | "iso2": "SH", 1777 | "iso3": "SHN", 1778 | "id": 654, 1779 | "lat": -15.9333, 1780 | "long": -5.7, 1781 | "possibleNames": [ 1782 | "Saint Helena, Ascension and Tristan da Cunha" 1783 | ] 1784 | }, 1785 | { 1786 | "country": "Saint Kitts and Nevis", 1787 | "iso2": "KN", 1788 | "iso3": "KNA", 1789 | "id": 659, 1790 | "lat": 17.3333, 1791 | "long": -62.75, 1792 | "woh": "saint-kitts-and-nevis" 1793 | }, 1794 | { 1795 | "country": "Saint Lucia", 1796 | "iso2": "LC", 1797 | "iso3": "LCA", 1798 | "id": 662, 1799 | "lat": 13.8833, 1800 | "long": -61.1333, 1801 | "woh": "saint-lucia" 1802 | }, 1803 | { 1804 | "country": "Saint Pierre Miquelon", 1805 | "iso2": "PM", 1806 | "iso3": "SPM", 1807 | "id": 666, 1808 | "lat": 46.8333, 1809 | "long": -56.3333, 1810 | "possibleNames": [ 1811 | "Saint Pierre and Miquelon" 1812 | ], 1813 | "woh": "saint-pierre-and-miquelon" 1814 | }, 1815 | { 1816 | "country": "Saint Martin", 1817 | "iso2": "MF", 1818 | "iso3": "MAF", 1819 | "id": 663, 1820 | "lat": 18.11, 1821 | "long": -63.03, 1822 | "possibleNames": [ 1823 | "St. Martin", 1824 | "St Martin" 1825 | ], 1826 | "woh": "saint-martin" 1827 | }, 1828 | { 1829 | "country": "Sint Maarten", 1830 | "iso2": "SX", 1831 | "iso3": "SXM", 1832 | "id": 534, 1833 | "lat": 18.02, 1834 | "long": -63.06, 1835 | "woh": "sint-maarten" 1836 | }, 1837 | { 1838 | "country": "Saint Vincent and the Grenadines", 1839 | "iso2": "VC", 1840 | "iso3": "VCT", 1841 | "id": 670, 1842 | "lat": 13.25, 1843 | "long": -61.2, 1844 | "possibleNames": [ 1845 | "Saint Vincent & the Grenadines", 1846 | "St. Vincent and the Grenadines", 1847 | "St. Vincent Grenadines" 1848 | ], 1849 | "woh": "saint-vincent-and-the-grenadines" 1850 | }, 1851 | { 1852 | "country": "Samoa", 1853 | "iso2": "WS", 1854 | "iso3": "WSM", 1855 | "id": 882, 1856 | "lat": -13.5833, 1857 | "long": -172.3333, 1858 | "woh": "samoa" 1859 | }, 1860 | { 1861 | "country": "San Marino", 1862 | "iso2": "SM", 1863 | "iso3": "SMR", 1864 | "id": 674, 1865 | "lat": 43.7667, 1866 | "long": 12.4167, 1867 | "woh": "san-marino" 1868 | }, 1869 | { 1870 | "country": "Sao Tome and Principe", 1871 | "iso2": "ST", 1872 | "iso3": "STP", 1873 | "id": 678, 1874 | "lat": 1, 1875 | "long": 7, 1876 | "woh": "sao-tome-and-principe" 1877 | }, 1878 | { 1879 | "country": "Saudi Arabia", 1880 | "iso2": "SA", 1881 | "iso3": "SAU", 1882 | "id": 682, 1883 | "lat": 25, 1884 | "long": 45, 1885 | "woh": "saudi-arabia" 1886 | }, 1887 | { 1888 | "country": "Senegal", 1889 | "iso2": "SN", 1890 | "iso3": "SEN", 1891 | "id": 686, 1892 | "lat": 14, 1893 | "long": -14, 1894 | "woh": "senegal" 1895 | }, 1896 | { 1897 | "country": "Serbia", 1898 | "iso2": "RS", 1899 | "iso3": "SRB", 1900 | "id": 688, 1901 | "lat": 44, 1902 | "long": 21, 1903 | "woh": "serbia" 1904 | }, 1905 | { 1906 | "country": "Seychelles", 1907 | "iso2": "SC", 1908 | "iso3": "SYC", 1909 | "id": 690, 1910 | "lat": -4.5833, 1911 | "long": 55.6667, 1912 | "woh": "seychelles" 1913 | }, 1914 | { 1915 | "country": "Sierra Leone", 1916 | "iso2": "SL", 1917 | "iso3": "SLE", 1918 | "id": 694, 1919 | "lat": 8.5, 1920 | "long": -11.5, 1921 | "woh": "sierra-leone" 1922 | }, 1923 | { 1924 | "country": "Singapore", 1925 | "iso2": "SG", 1926 | "iso3": "SGP", 1927 | "id": 702, 1928 | "lat": 1.3667, 1929 | "long": 103.8, 1930 | "woh": "singapore" 1931 | }, 1932 | { 1933 | "country": "Slovakia", 1934 | "iso2": "SK", 1935 | "iso3": "SVK", 1936 | "id": 703, 1937 | "lat": 48.6667, 1938 | "long": 19.5, 1939 | "woh": "slovakia" 1940 | }, 1941 | { 1942 | "country": "Slovenia", 1943 | "iso2": "SI", 1944 | "iso3": "SVN", 1945 | "id": 705, 1946 | "lat": 46, 1947 | "long": 15, 1948 | "woh": "slovenia" 1949 | }, 1950 | { 1951 | "country": "Solomon Islands", 1952 | "iso2": "SB", 1953 | "iso3": "SLB", 1954 | "id": 90, 1955 | "lat": -8, 1956 | "long": 159, 1957 | "woh": "solomon-islands" 1958 | }, 1959 | { 1960 | "country": "Somalia", 1961 | "iso2": "SO", 1962 | "iso3": "SOM", 1963 | "id": 706, 1964 | "lat": 10, 1965 | "long": 49, 1966 | "woh": "somalia" 1967 | }, 1968 | { 1969 | "country": "South Africa", 1970 | "iso2": "ZA", 1971 | "iso3": "ZAF", 1972 | "id": 710, 1973 | "lat": -29, 1974 | "long": 24, 1975 | "woh": "south-africa" 1976 | }, 1977 | { 1978 | "country": "South Georgia and the South Sandwich Islands", 1979 | "iso2": "GS", 1980 | "iso3": "SGS", 1981 | "id": 239, 1982 | "lat": -54.5, 1983 | "long": -37 1984 | }, 1985 | { 1986 | "country": "South Sudan", 1987 | "iso2": "SS", 1988 | "iso3": "SSD", 1989 | "id": 728, 1990 | "lat": 6.8769, 1991 | "long": 31.3069, 1992 | "woh": "south-sudan" 1993 | }, 1994 | { 1995 | "country": "Spain", 1996 | "iso2": "ES", 1997 | "iso3": "ESP", 1998 | "id": 724, 1999 | "lat": 40, 2000 | "long": -4, 2001 | "possibleNames": [ 2002 | "España" 2003 | ], 2004 | "woh": "spain" 2005 | }, 2006 | { 2007 | "country": "Sri Lanka", 2008 | "iso2": "LK", 2009 | "iso3": "LKA", 2010 | "id": 144, 2011 | "lat": 7, 2012 | "long": 81, 2013 | "woh": "sri-lanka" 2014 | }, 2015 | { 2016 | "country": "Sudan", 2017 | "iso2": "SD", 2018 | "iso3": "SDN", 2019 | "id": 729, 2020 | "lat": 15, 2021 | "long": 30, 2022 | "woh": "sudan" 2023 | }, 2024 | { 2025 | "country": "Suriname", 2026 | "iso2": "SR", 2027 | "iso3": "SUR", 2028 | "id": 740, 2029 | "lat": 4, 2030 | "long": -56, 2031 | "woh": "suriname" 2032 | }, 2033 | { 2034 | "country": "Svalbard and Jan Mayen", 2035 | "iso2": "SJ", 2036 | "iso3": "SJM", 2037 | "id": 744, 2038 | "lat": 78, 2039 | "long": 20 2040 | }, 2041 | { 2042 | "country": "Swaziland", 2043 | "iso2": "SZ", 2044 | "iso3": "SWZ", 2045 | "id": 748, 2046 | "lat": -26.5, 2047 | "long": 31.5, 2048 | "possibleNames": [ 2049 | "Esuatini", 2050 | "Eswatini" 2051 | ], 2052 | "woh": "swaziland" 2053 | }, 2054 | { 2055 | "country": "Sweden", 2056 | "iso2": "SE", 2057 | "iso3": "SWE", 2058 | "id": 752, 2059 | "lat": 62, 2060 | "long": 15, 2061 | "woh": "sweden" 2062 | }, 2063 | { 2064 | "country": "Switzerland", 2065 | "iso2": "CH", 2066 | "iso3": "CHE", 2067 | "id": 756, 2068 | "lat": 47, 2069 | "long": 8, 2070 | "woh": "switzerland" 2071 | }, 2072 | { 2073 | "country": "Syrian Arab Republic", 2074 | "iso2": "SY", 2075 | "iso3": "SYR", 2076 | "id": 760, 2077 | "lat": 35, 2078 | "long": 38, 2079 | "possibleNames": [ 2080 | "Syria" 2081 | ], 2082 | "woh": "syria" 2083 | }, 2084 | { 2085 | "country": "Taiwan", 2086 | "iso2": "TW", 2087 | "iso3": "TWN", 2088 | "id": 158, 2089 | "lat": 23.5, 2090 | "long": 121, 2091 | "possibleNames": [ 2092 | "Taiwan, Province of China" 2093 | ], 2094 | "woh": "taiwan" 2095 | }, 2096 | { 2097 | "country": "Tajikistan", 2098 | "iso2": "TJ", 2099 | "iso3": "TJK", 2100 | "id": 762, 2101 | "lat": 39, 2102 | "long": 71, 2103 | "woh": "tajikistan" 2104 | }, 2105 | { 2106 | "country": "Tanzania", 2107 | "iso2": "TZ", 2108 | "iso3": "TZA", 2109 | "id": 834, 2110 | "lat": -6, 2111 | "long": 35, 2112 | "possibleNames": [ 2113 | "Tanzania, United Republic of" 2114 | ], 2115 | "woh": "tanzania" 2116 | }, 2117 | { 2118 | "country": "Thailand", 2119 | "iso2": "TH", 2120 | "iso3": "THA", 2121 | "id": 764, 2122 | "lat": 15, 2123 | "long": 100, 2124 | "woh": "thailand" 2125 | }, 2126 | { 2127 | "country": "Timor-Leste", 2128 | "iso2": "TL", 2129 | "iso3": "TLS", 2130 | "id": 626, 2131 | "lat": -8.55, 2132 | "long": 125.5167, 2133 | "woh": "timor-leste" 2134 | }, 2135 | { 2136 | "country": "Togo", 2137 | "iso2": "TG", 2138 | "iso3": "TGO", 2139 | "id": 768, 2140 | "lat": 8, 2141 | "long": 1.1667, 2142 | "woh": "togo" 2143 | }, 2144 | { 2145 | "country": "Tokelau", 2146 | "iso2": "TK", 2147 | "iso3": "TKL", 2148 | "id": 772, 2149 | "lat": -9, 2150 | "long": -172 2151 | }, 2152 | { 2153 | "country": "Tonga", 2154 | "iso2": "TO", 2155 | "iso3": "TON", 2156 | "id": 776, 2157 | "lat": -20, 2158 | "long": -175 2159 | }, 2160 | { 2161 | "country": "Trinidad and Tobago", 2162 | "iso2": "TT", 2163 | "iso3": "TTO", 2164 | "id": 780, 2165 | "lat": 11, 2166 | "long": -61, 2167 | "possibleNames": [ 2168 | "Triniad & Tobago" 2169 | ], 2170 | "woh": "trinidad-and-tobago" 2171 | }, 2172 | { 2173 | "country": "Tunisia", 2174 | "iso2": "TN", 2175 | "iso3": "TUN", 2176 | "id": 788, 2177 | "lat": 34, 2178 | "long": 9, 2179 | "woh": "tunisia" 2180 | }, 2181 | { 2182 | "country": "Turkey", 2183 | "iso2": "TR", 2184 | "iso3": "TUR", 2185 | "id": 792, 2186 | "lat": 39, 2187 | "long": 35, 2188 | "woh": "turkey" 2189 | }, 2190 | { 2191 | "country": "Turkmenistan", 2192 | "iso2": "TM", 2193 | "iso3": "TKM", 2194 | "id": 795, 2195 | "lat": 40, 2196 | "long": 60 2197 | }, 2198 | { 2199 | "country": "Turks and Caicos Islands", 2200 | "iso2": "TC", 2201 | "iso3": "TCA", 2202 | "id": 796, 2203 | "lat": 21.75, 2204 | "long": -71.5833, 2205 | "possibleNames": [ 2206 | "Turks and Caicos" 2207 | ], 2208 | "woh": "turks-and-caicos-islands" 2209 | }, 2210 | { 2211 | "country": "Tuvalu", 2212 | "iso2": "TV", 2213 | "iso3": "TUV", 2214 | "id": 798, 2215 | "lat": -8, 2216 | "long": 178 2217 | }, 2218 | { 2219 | "country": "Uganda", 2220 | "iso2": "UG", 2221 | "iso3": "UGA", 2222 | "id": 800, 2223 | "lat": 1, 2224 | "long": 32, 2225 | "woh": "uganda" 2226 | }, 2227 | { 2228 | "country": "Ukraine", 2229 | "iso2": "UA", 2230 | "iso3": "UKR", 2231 | "id": 804, 2232 | "lat": 49, 2233 | "long": 32, 2234 | "woh": "ukraine" 2235 | }, 2236 | { 2237 | "country": "UAE", 2238 | "iso2": "AE", 2239 | "iso3": "ARE", 2240 | "id": 784, 2241 | "lat": 24, 2242 | "long": 54, 2243 | "possibleNames": [ 2244 | "United Arab Emirates" 2245 | ], 2246 | "woh": "united-arab-emirates" 2247 | }, 2248 | { 2249 | "country": "UK", 2250 | "iso2": "GB", 2251 | "iso3": "GBR", 2252 | "id": 826, 2253 | "lat": 54, 2254 | "long": -2, 2255 | "possibleNames": [ 2256 | "England", 2257 | "United Kingdom" 2258 | ], 2259 | "woh": "uk" 2260 | }, 2261 | { 2262 | "country": "USA", 2263 | "iso2": "US", 2264 | "iso3": "USA", 2265 | "id": 840, 2266 | "lat": 38, 2267 | "long": -97, 2268 | "possibleNames": [ 2269 | "USA", 2270 | "America", 2271 | "Estados Unidos", 2272 | "United States of America", 2273 | "United States" 2274 | ], 2275 | "woh": "us" 2276 | }, 2277 | { 2278 | "country": "United States Minor Outlying Islands", 2279 | "iso2": "UM", 2280 | "iso3": "UMI", 2281 | "id": 581, 2282 | "lat": 19.2833, 2283 | "long": 166.6 2284 | }, 2285 | { 2286 | "country": "Uruguay", 2287 | "iso2": "UY", 2288 | "iso3": "URY", 2289 | "id": 858, 2290 | "lat": -33, 2291 | "long": -56, 2292 | "woh": "uruguay" 2293 | }, 2294 | { 2295 | "country": "Uzbekistan", 2296 | "iso2": "UZ", 2297 | "iso3": "UZB", 2298 | "id": 860, 2299 | "lat": 41, 2300 | "long": 64, 2301 | "woh": "uzbekistan" 2302 | }, 2303 | { 2304 | "country": "Vanuatu", 2305 | "iso2": "VU", 2306 | "iso3": "VUT", 2307 | "id": 548, 2308 | "lat": -16, 2309 | "long": 167, 2310 | "woh": "vanuatu" 2311 | }, 2312 | { 2313 | "country": "Venezuela", 2314 | "iso2": "VE", 2315 | "iso3": "VEN", 2316 | "id": 862, 2317 | "lat": 8, 2318 | "long": -66, 2319 | "possibleNames": [ 2320 | "Venezuela, Bolivarian Republic of" 2321 | ], 2322 | "woh": "venezuela" 2323 | }, 2324 | { 2325 | "country": "Vietnam", 2326 | "iso2": "VN", 2327 | "iso3": "VNM", 2328 | "id": 704, 2329 | "lat": 21, 2330 | "long": 105.8, 2331 | "possibleNames": [ 2332 | "Viet Nam" 2333 | ], 2334 | "woh": "viet-nam" 2335 | }, 2336 | { 2337 | "country": "British Virgin Islands", 2338 | "iso2": "VG", 2339 | "iso3": "VGB", 2340 | "id": 92, 2341 | "lat": 18.5, 2342 | "long": -64.5, 2343 | "possibleNames": [ 2344 | "Virgin Islands, British" 2345 | ], 2346 | "woh": "british-virgin-islands" 2347 | }, 2348 | { 2349 | "country": "U.S. Virgin Islands", 2350 | "iso2": "VI", 2351 | "iso3": "VIR", 2352 | "id": 850, 2353 | "lat": 18.3333, 2354 | "long": -64.8333, 2355 | "possibleNames": [ 2356 | "Virgin Islands" 2357 | ] 2358 | }, 2359 | { 2360 | "country": "Wallis and Futuna", 2361 | "iso2": "WF", 2362 | "iso3": "WLF", 2363 | "id": 876, 2364 | "lat": -13.3, 2365 | "long": -176.2, 2366 | "woh": "wallis-and-futuna-islands" 2367 | }, 2368 | { 2369 | "country": "Western Sahara", 2370 | "iso2": "EH", 2371 | "iso3": "ESH", 2372 | "id": 732, 2373 | "lat": 24.5, 2374 | "long": -13, 2375 | "woh": "western-sahara" 2376 | }, 2377 | { 2378 | "country": "Yemen", 2379 | "iso2": "YE", 2380 | "iso3": "YEM", 2381 | "id": 887, 2382 | "lat": 15, 2383 | "long": 48, 2384 | "woh": "yemen" 2385 | }, 2386 | { 2387 | "country": "Zambia", 2388 | "iso2": "ZM", 2389 | "iso3": "ZMB", 2390 | "id": 894, 2391 | "lat": -15, 2392 | "long": 30, 2393 | "woh": "zambia" 2394 | }, 2395 | { 2396 | "country": "Zimbabwe", 2397 | "iso2": "ZW", 2398 | "iso3": "ZWE", 2399 | "id": 716, 2400 | "lat": -20, 2401 | "long": 30, 2402 | "woh": "zimbabwe" 2403 | } 2404 | ] --------------------------------------------------------------------------------