Skip to content

Commit

Permalink
Added Country class to query country information and Region class to…
Browse files Browse the repository at this point in the history
… lookup the ISO 3166-2 subdivision code for country code and region name
  • Loading branch information
ip2location committed Oct 27, 2022
1 parent 85a0a11 commit 4c9b230
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 1 deletion.
119 changes: 119 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,122 @@ func main() {
}
}
```

## COUNTRY CLASS

## Methods
Below are the methods supported in this package.

|Method Name|Description|
|---|---|
|func OpenCountryInfo(csvFile string) (*CI, error)|Expect a IP2Location Country Information CSV file. This database is free for download at https://www.ip2location.com/free/country-information|
|func (c *CI) GetCountryInfo(countryCode ...string) ([]CountryInfoRecord, error)|Returns the country information for specified country or all countries.|

## Usage

```go
package main

import (
"github.com/ip2location/ip2location-go"
"fmt"
)

func main() {
c, err := ip2location.OpenCountryInfo("./IP2LOCATION-COUNTRY-INFORMATION.CSV")

if err != nil {
fmt.Print(err)
return
}

res, err := c.GetCountryInfo("US")

if err != nil {
fmt.Print(err)
return
}

fmt.Printf("country_code: %s\n", res[0].Country_code)
fmt.Printf("country_name: %s\n", res[0].Country_name)
fmt.Printf("country_alpha3_code: %s\n", res[0].Country_alpha3_code)
fmt.Printf("country_numeric_code: %s\n", res[0].Country_numeric_code)
fmt.Printf("capital: %s\n", res[0].Capital)
fmt.Printf("country_demonym: %s\n", res[0].Country_demonym)
fmt.Printf("total_area: %s\n", res[0].Total_area)
fmt.Printf("population: %s\n", res[0].Population)
fmt.Printf("idd_code: %s\n", res[0].Idd_code)
fmt.Printf("currency_code: %s\n", res[0].Currency_code)
fmt.Printf("currency_name: %s\n", res[0].Currency_name)
fmt.Printf("currency_symbol: %s\n", res[0].Currency_symbol)
fmt.Printf("lang_code: %s\n", res[0].Lang_code)
fmt.Printf("lang_name: %s\n", res[0].Lang_name)
fmt.Printf("cctld: %s\n", res[0].Cctld)
fmt.Print("==============================================\n")

res2, err := c.GetCountryInfo()

if err != nil {
fmt.Print(err)
return
}

for _, v := range res2 {
fmt.Printf("country_code: %s\n", v.Country_code)
fmt.Printf("country_name: %s\n", v.Country_name)
fmt.Printf("country_alpha3_code: %s\n", v.Country_alpha3_code)
fmt.Printf("country_numeric_code: %s\n", v.Country_numeric_code)
fmt.Printf("capital: %s\n", v.Capital)
fmt.Printf("country_demonym: %s\n", v.Country_demonym)
fmt.Printf("total_area: %s\n", v.Total_area)
fmt.Printf("population: %s\n", v.Population)
fmt.Printf("idd_code: %s\n", v.Idd_code)
fmt.Printf("currency_code: %s\n", v.Currency_code)
fmt.Printf("currency_name: %s\n", v.Currency_name)
fmt.Printf("currency_symbol: %s\n", v.Currency_symbol)
fmt.Printf("lang_code: %s\n", v.Lang_code)
fmt.Printf("lang_name: %s\n", v.Lang_name)
fmt.Printf("cctld: %s\n", v.Cctld)
fmt.Print("==============================================\n")
}
}
```

## REGION CLASS

## Methods
Below are the methods supported in this package.

|Method Name|Description|
|---|---|
|func OpenRegionInfo(csvFile string) (*RI, error)|Expect a IP2Location ISO 3166-2 Subdivision Code CSV file. This database is free for download at https://www.ip2location.com/free/iso3166-2|
|func (r *RI) GetRegionCode(countryCode string, regionName string) (string, error)|Returns the region code for the supplied country code and region name.|

## Usage

```go
package main

import (
"github.com/ip2location/ip2location-go"
"fmt"
)

func main() {
r, err := ip2location.OpenRegionInfo("./IP2LOCATION-ISO3166-2.CSV")

if err != nil {
fmt.Print(err)
return
}

res, err := r.GetRegionCode("US", "California")

if err != nil {
fmt.Print(err)
return
}

fmt.Printf("region code: %s\n", res)
}
```
129 changes: 129 additions & 0 deletions country.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ip2location

import (
"encoding/csv"
"errors"
"os"
)

// The CountryInfoRecord struct stores all of the available
// country info found in the country information CSV file.
type CountryInfoRecord struct {
Country_code string
Country_name string
Country_alpha3_code string
Country_numeric_code string
Capital string
Country_demonym string
Total_area string
Population string
Idd_code string
Currency_code string
Currency_name string
Currency_symbol string
Lang_code string
Lang_name string
Cctld string
}

// The CI struct is the main object used to read the country information CSV.
type CI struct {
resultsArr []CountryInfoRecord
resultsMap map[string]CountryInfoRecord
}

// OpenCountryInfo initializes with the path to the country information CSV file.
func OpenCountryInfo(csvFile string) (*CI, error) {
var ci = &CI{}

_, err := os.Stat(csvFile)
if os.IsNotExist(err) {
return nil, errors.New("The CSV file '" + csvFile + "' is not found.")
}

f, err := os.Open(csvFile)
if err != nil {
return nil, errors.New("Unable to read '" + csvFile + "'.")
}

defer f.Close()

csvReader := csv.NewReader(f)
data, err := csvReader.ReadAll()
if err != nil {
return nil, errors.New("Unable to read '" + csvFile + "'.")
}

ci.resultsMap = make(map[string]CountryInfoRecord)
var headerArr []string

for i, line := range data {
if i == 0 { // headers
for _, field := range line {
headerArr = append(headerArr, field)
}
} else {
var rec CountryInfoRecord
for j, field := range line {
switch headerArr[j] {
case "country_code":
rec.Country_code = field
case "country_name":
rec.Country_name = field
case "country_alpha3_code":
rec.Country_alpha3_code = field
case "country_numeric_code":
rec.Country_numeric_code = field
case "capital":
rec.Capital = field
case "country_demonym":
rec.Country_demonym = field
case "total_area":
rec.Total_area = field
case "population":
rec.Population = field
case "idd_code":
rec.Idd_code = field
case "currency_code":
rec.Currency_code = field
case "currency_name":
rec.Currency_name = field
case "currency_symbol":
rec.Currency_symbol = field
case "lang_code":
rec.Lang_code = field
case "lang_name":
rec.Lang_name = field
case "cctld":
rec.Cctld = field
}
}
if rec.Country_code == "" {
return nil, errors.New("Invalid country information CSV file.")
}
ci.resultsArr = append(ci.resultsArr, rec)
ci.resultsMap[rec.Country_code] = rec
}
}
return ci, nil
}

// GetCountryInfo returns the country information for the specified country or all countries if not specified
func (c *CI) GetCountryInfo(countryCode ...string) ([]CountryInfoRecord, error) {
if len(c.resultsArr) == 0 {
return nil, errors.New("No record available.")
}

if len(countryCode) == 1 {
cc := countryCode[0]
if rec, ok := c.resultsMap[cc]; ok {
var x []CountryInfoRecord
x = append(x, rec)
return x, nil // return record
} else {
return nil, errors.New("No record found.")
}
} else {
return c.resultsArr, nil // return all countries
}
}
2 changes: 1 addition & 1 deletion ip2location.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ var usagetype_position = [26]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
var addresstype_position = [26]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21}
var category_position = [26]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22}

const api_version string = "9.4.1"
const api_version string = "9.5.0"

var max_ipv4_range = big.NewInt(4294967295)
var max_ipv6_range = big.NewInt(0)
Expand Down
96 changes: 96 additions & 0 deletions region.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package ip2location

import (
"encoding/csv"
"errors"
"os"
"strings"
)

// The RegionInfoRecord struct stores all of the available
// region info found in the region information CSV file.
type RegionInfoRecord struct {
Country_code string
Name string
Code string
}

// The RI struct is the main object used to read the region information CSV.
type RI struct {
resultsMap map[string][]RegionInfoRecord
}

// OpenRegionInfo initializes with the path to the region information CSV file.
func OpenRegionInfo(csvFile string) (*RI, error) {
var ri = &RI{}

_, err := os.Stat(csvFile)
if os.IsNotExist(err) {
return nil, errors.New("The CSV file '" + csvFile + "' is not found.")
}

f, err := os.Open(csvFile)
if err != nil {
return nil, errors.New("Unable to read '" + csvFile + "'.")
}

defer f.Close()

csvReader := csv.NewReader(f)
data, err := csvReader.ReadAll()
if err != nil {
return nil, errors.New("Unable to read '" + csvFile + "'.")
}

ri.resultsMap = make(map[string][]RegionInfoRecord)
var headerArr []string
var resultsArr []RegionInfoRecord

for i, line := range data {
if i == 0 { // headers
for _, field := range line {
headerArr = append(headerArr, field)
}
} else {
var rec RegionInfoRecord
for j, field := range line {
switch headerArr[j] {
case "country_code":
rec.Country_code = field
case "subdivision_name":
rec.Name = field
case "code":
rec.Code = field
}
}
if rec.Name == "" {
return nil, errors.New("Invalid region information CSV file.")
}
resultsArr = append(resultsArr, rec)
}
}
for _, elem := range resultsArr {
if _, ok := ri.resultsMap[elem.Country_code]; !ok {
var arr []RegionInfoRecord
ri.resultsMap[elem.Country_code] = arr
}
ri.resultsMap[elem.Country_code] = append(ri.resultsMap[elem.Country_code], elem)
}
return ri, nil
}

// GetRegionCode returns the region code for the specified country and region name
func (r *RI) GetRegionCode(countryCode string, regionName string) (string, error) {
if len(r.resultsMap) == 0 {
return "", errors.New("No record available.")
}

if arr, ok := r.resultsMap[countryCode]; ok {
for _, elem := range arr {
if strings.ToUpper(elem.Name) == strings.ToUpper(regionName) {
return elem.Code, nil
}
}
}
return "", errors.New("No record found.")
}

0 comments on commit 4c9b230

Please sign in to comment.