diff --git a/_scripts/env.sh b/_scripts/env.sh old mode 100755 new mode 100644 diff --git a/_scripts/tests_coverage.sh b/_scripts/tests_coverage.sh old mode 100755 new mode 100644 diff --git a/_scripts/tests_unit.sh b/_scripts/tests_unit.sh old mode 100755 new mode 100644 diff --git a/address.go b/address.go deleted file mode 100644 index 915d2dc..0000000 --- a/address.go +++ /dev/null @@ -1,44 +0,0 @@ -package geocodio - -type Address struct { - Query string `json:"query"` - Components Components `json:"address_components"` - Formatted string `json:"formatted_address"` - Location Location `json:"location"` - Accuracy float64 `json:"accuracy"` - AccuracyType string `json:"accuracy_type"` - Source string `json:"source"` - Fields Fields `json:"fields,omitempty"` -} - -// Components -/* - "address_components": { - "number": "1109", - "predirectional": "N", - "street": "Highland", - "suffix": "St", - "formatted_street": "N Highland St", - "city": "Arlington", - "county": "Arlington County", - "state": "VA", - "zip": "22201", - "country": "US" - }, -*/ -type Components struct { - Number string `json:"number"` - Street string `json:"street"` - Suffix string `json:"suffix"` - SecondaryNumber string `json:"secondarynumber"` - SecondaryUnit string `json:"secondaryunit"` - PostDirectional string `json:"postdirectional"` - FormattedStreet string `json:"formatted_street"` - City string `json:"city"` - State string `json:"state"` - Zip string `json:"zip"` - County string `json:"county"` - Country string `json:"country"` - PreDirectional string `json:"predirectional"` - Prefix string `json:"prefix"` -} diff --git a/api.go b/api.go deleted file mode 100644 index 1927cf1..0000000 --- a/api.go +++ /dev/null @@ -1,106 +0,0 @@ -package geocodio - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - - // "fmt" - "io/ioutil" - "net/http" - "net/url" - "strings" - "time" -) - -const ( - // MethodGet constant - MethodGet = "GET" - // MethodPost constant - MethodPost = "POST" -) - -type saver interface { - SaveDebug(requestedURL, status string, statusCode int, body []byte) -} - -func (g *Geocodio) get(path string, query map[string]string, result saver) error { - return g.call(MethodGet, path, nil, query, result) -} - -func (g *Geocodio) post(path string, payload interface{}, query map[string]string, result saver) error { - return g.call(MethodPost, path, payload, query, result) -} - -func (g *Geocodio) call(method, path string, payload interface{}, query map[string]string, result saver) error { - - if strings.Index(path, "/") != 0 { - return errors.New("Path must start with a forward slash: ' / ' ") - } - - rawURL := GeocodioAPIBaseURLv1 + path + "?api_key=" + g.APIKey - - if query != nil { - for k, v := range query { - rawURL = fmt.Sprintf("%s&%s=%s", rawURL, k, url.QueryEscape(v)) - } - } - - timeout := time.Duration(10 * time.Second) - client := http.Client{ - Timeout: timeout, - } - - u, err := url.Parse(rawURL) - if err != nil { - return nil - } - - if query != nil { - for k, v := range query { - if u.Query().Get(k) != "" { - u.Query().Set(k, v) - continue - } - u.Query().Add(k, v) - } - } - - req := http.Request{ - Method: method, - URL: u, - Header: http.Header{}, - } - - if payload != nil { - body, err := json.Marshal(payload) - if err != nil { - return err - } - - req.Body = ioutil.NopCloser(bytes.NewReader(body)) - - req.Header.Add("Content-Type", "application/json") - } - - resp, err := client.Do(&req) - if err != nil { - return err - } - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - result.SaveDebug(u.String(), resp.Status, resp.StatusCode, body) - - err = json.Unmarshal(body, &result) - if err != nil { - return err - } - - return nil -} diff --git a/canada.go b/canada.go new file mode 100644 index 0000000..df6c4b4 --- /dev/null +++ b/canada.go @@ -0,0 +1,51 @@ +package geocodio + +type Riding struct { + Code string `json:"code"` + NameFrench string `json:"name_french"` + NameEnglish string `json:"name_english"` +} + +type Statcan struct { + Division CanadaDivision `json:"division"` + ConsolidatedSubdivision CanadaConsolidatedSubdivision `json:"consolidated_subdivision"` + Subdivision CanadaSubdivision `json:"subdivision"` + EconomicRegion string `json:"economic_region"` + StatisticalArea CanadaStatisticalArea `json:"statistical_area"` + CensusMetroArea CanadaCensusMetroArea `json:"cma_ca"` + Tract string `json:"tract"` + CensusYear int `json:"census_year"` +} + +type CanadaDivision struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + TypeDescription string `json:"type_description"` +} + +type CanadaConsolidatedSubdivision struct { + ID string `json:"id"` + Name string `json:"name"` +} + +type CanadaSubdivision struct { + ID string `json:"id"` + Name string `json:"name"` + TypeCode string `json:"type"` + TypeDescription string `json:"type_description"` +} + +type CanadaStatisticalArea struct { + Code string `json:"code"` + CodeDescription string `json:"code_description"` + Type string `json:"type"` + TypeDescription string `json:"type_description"` +} + +type CanadaCensusMetroArea struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + TypeDescription string `json:"type_description"` +} diff --git a/census.go b/census.go index 745d36f..639d1ae 100644 --- a/census.go +++ b/census.go @@ -15,35 +15,6 @@ type CensusResults struct { } // Census field -/* -{ - "census_year": 2010, - "state_fips": "51", - "county_fips": "51013", - "tract_code": "101801", - "block_code": "1004", - "block_group": "1", - "full_fips": "510131018011004", - "place": { - "name": "Arlington", - "fips": "5103000" - }, - "metro_micro_statistical_area": { - "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", - "area_code": "47900", - "type": "metropolitan" - }, - "combined_statistical_area": { - "name": "Washington-Baltimore-Northern Virginia, DC-MD-VA-WV", - "area_code": "51548" - }, - "metropolitan_division": { - "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", - "area_code": "47894" - }, - "source": "US Census Bureau" -} -*/ type Census struct { Year int `json:"census_year"` StateFIPS string `json:"state_fips"` @@ -80,9 +51,9 @@ type CensusACS struct { } type Economics struct { - NumberOfHouseholds NumberOfHouseholds `json:"Number of households"` - MedianHouseholdIncome MedianHouseholdIncome `json:"Median household income"` - HouseholdIncome HouseholdIncome `json:"Household income"` + NumberOfHouseholds NumberOfHouseholds `json:"Number of households"` + MedianHouseholdIncome MedianHouseholdIncome `json:"Median household income"` + HouseholdIncome map[string]CensusDataPoint `json:"Household income"` // use map since Go tags cannot have "," in them } type NumberOfHouseholds struct { @@ -95,26 +66,6 @@ type MedianHouseholdIncome struct { Total CensusDataPoint `json:"Total"` } -type HouseholdIncome struct { - Meta CensusMeta `json:"meta"` - LessThan10000 CensusDataPoint `json:"Less than $10,000"` - Income10000to14999 CensusDataPoint `json:"$10,000 to $14,999"` - Income15000to19999 CensusDataPoint `json:"$15,000 to $19,999"` - Income20000to24999 CensusDataPoint `json:"$20,000 to $24,999"` - Income25000to29999 CensusDataPoint `json:"$25,000 to $29,999"` - Income30000to34999 CensusDataPoint `json:"$30,000 to $34,999"` - Income35000to39999 CensusDataPoint `json:"$35,000 to $39,999"` - Income40000to44999 CensusDataPoint `json:"$40,000 to $44,999"` - Income45000to49999 CensusDataPoint `json:"$45,000 to $49,999"` - Income50000to59000 CensusDataPoint `json:"$50,000 to $59,999"` - Income60000to74999 CensusDataPoint `json:"$60,000 to $74,999"` - Income75000to99999 CensusDataPoint `json:"$75,000 to $99,999"` - Income100000to124999 CensusDataPoint `json:"$100,000 to $124,999"` - Income125000to149000 CensusDataPoint `json:"$125,000 to $149,999"` - Income150000to199999 CensusDataPoint `json:"$150,000 to $199,999"` - Income200000orMore CensusDataPoint `json:"$200,000 or more"` -} - type Demographic struct { MedianAge map[string]CensusDataPoint `json:"Median age"` PopulationByAgeRange map[string]CensusDataPoint `json:"Population by age range"` diff --git a/congressional.go b/congressional.go index d5b177c..fee9095 100644 --- a/congressional.go +++ b/congressional.go @@ -1,34 +1,16 @@ package geocodio // Congressional District field -/* -"name": "Congressional District 8", -"district_number": 8, -"congress_number": "116th", -"congress_years": "2019-2021", -"proportion": 1, -"current_legislators": [...] -*/ type CongressionalDistrict struct { Name string `json:"name"` DistrictNumber int `json:"district_number"` CongressNumber string `json:"congress_number"` CongressYears string `json:"congress_years"` - Proportion int `json:"congress_years"` + Proportion int `json:"proportion"` CurrentLegislators []Legislator `json:"current_legislators"` // v1.2+ } // Legislator field -/* -{ - "type": "representative", - "bio": {...}, - "contact": {...}, - "social": {...}, - "references": {...}, - "source": "Legislator data is originally collected and aggregated by https://github.com/unitedstates/" -} -*/ type Legislator struct { Type string `json:"type"` Bio Bio `json:"bio"` @@ -39,15 +21,6 @@ type Legislator struct { } // Bio field -/* - "bio": { - "last_name": "Beyer", - "first_name": "Donald", - "birthday": "1950-06-20", - "gender": "M", - "party": "Democrat" - } -*/ type Bio struct { LastName string `json:"last_name"` FirstName string `json:"first_name"` @@ -57,14 +30,6 @@ type Bio struct { } // Contact field -/* -"contact": { - "url": "https://beyer.house.gov", - "address": "1119 Longworth House Office Building Washington DC 20515-4608", - "phone": "(202) 225-4376", - "contact_form": null -} -*/ type Contact struct { URL string `json:"url"` Address string `json:"address"` @@ -73,15 +38,6 @@ type Contact struct { } // Social field -/* -"social": { - "rss_url": null, - "twitter": "RepDonBeyer", - "facebook": "RepDonBeyer", - "youtube": null, - "youtube_id": "UCPJGVbOVcAVGiBwq8qr_T9w" -} -*/ type CongressionalSocial struct { RSSURL string `json:"rss_url"` Twitter string `json:"twitter"` @@ -91,21 +47,6 @@ type CongressionalSocial struct { } // References field -/* -"references": { - "bioguide_id": "B001292", - "thomas_id": "02272", - "opensecrets_id": "N00036018", - "lis_id": null, - "cspan_id": "21141", - "govtrack_id": "412657", - "votesmart_id": "1707", - "ballotpedia_id": null, - "washington_post_id": null, - "icpsr_id": "21554", - "wikipedia_id": "Don Beyer" -} -*/ type References struct { BioguideID string `json:"bioguide_id"` ThomasID string `json:"thomas_id"` diff --git a/env.go b/env.go deleted file mode 100644 index 21f911b..0000000 --- a/env.go +++ /dev/null @@ -1,6 +0,0 @@ -package geocodio - -const ( - EnvGeocodioAPIKey = "GEOCODIO_API_KEY" - EnvOldAPIKey = "API_KEY" -) diff --git a/fields.go b/fields.go deleted file mode 100644 index c20aa37..0000000 --- a/fields.go +++ /dev/null @@ -1,13 +0,0 @@ -package geocodio - -// Fields -type Fields struct { - Timezone Timezone `json:"timezone,omitempty"` - Zip4 Zip4 `json:"zip4,omitempty"` - CongressionalDistrict CongressionalDistrict `json:"congressional_district,omitempty"` // v1.0 - CongressionalDistricts []CongressionalDistrict `json:"congressional_districts,omitempty"` // v1.1+ - StateLegislativeDistricts StateLegislativeDistricts `json:"state_legislative_districts,omitempty"` - SchoolDistricts SchoolDistricts `json:"school_districts,omitempty"` - Census CensusResults `json:"census,omitempty"` - ACS CensusACS `json:"acs,omitempty"` -} diff --git a/geocode.go b/geocode.go index f1f95eb..5f2819c 100644 --- a/geocode.go +++ b/geocode.go @@ -2,18 +2,13 @@ package geocodio import ( "errors" + "fmt" "strings" ) // BatchResponse type BatchResponse struct { Results []BatchResult `json:"results"` - Debug struct { - RawResponse []byte `json:"-"` - RequestedURL string `json:"requested_url"` - Status string `json:"status"` - StatusCode int `json:"status_code"` - } `json:"-"` } // BatchResult @@ -22,6 +17,7 @@ type BatchResult struct { Response BatchResultItem `json:"response"` } +// BatchResultItem type BatchResultItem struct { Input Input `json:"input,omitempty"` Results []Address `json:"results"` @@ -32,12 +28,6 @@ type BatchResultItem struct { type GeocodeResult struct { Input Input `json:"input,omitempty"` Results []Result `json:"results"` - Debug struct { - RawResponse []byte `json:"-"` - RequestedURL string `json:"requested_url"` - Status string `json:"status"` - StatusCode int `json:"status_code"` - } `json:"-"` } type ErrorResponse struct { @@ -50,39 +40,6 @@ type Result struct { Error *ErrorResponse `json:"response,omitempty"` } -func (self *GeocodeResult) SaveDebug(requestedURL, status string, statusCode int, body []byte) { - self.Debug.RequestedURL = requestedURL - self.Debug.Status = status - self.Debug.StatusCode = statusCode - self.Debug.RawResponse = body -} - -func (self *GeocodeResult) Error() string { - if len(self.Results) > 0 { - if self.Results[0].Error != nil { - return self.Results[0].Error.Message - } - } - return "" -} - -// ResponseAsString helper to return raw response -func (self *GeocodeResult) ResponseAsString() string { - return string(self.Debug.RawResponse) -} - -func (self *BatchResponse) SaveDebug(requestedURL, status string, statusCode int, body []byte) { - self.Debug.RequestedURL = requestedURL - self.Debug.Status = status - self.Debug.StatusCode = statusCode - self.Debug.RawResponse = body -} - -// ResponseAsString helper to return raw response -func (self *BatchResponse) ResponseAsString() string { - return string(self.Debug.RawResponse) -} - // Geocode single address // See: http://geocod.io/docs/#toc_4 func (g *Geocodio) Geocode(address string) (GeocodeResult, error) { @@ -91,9 +48,29 @@ func (g *Geocodio) Geocode(address string) (GeocodeResult, error) { return resp, ErrAddressIsEmpty } - err := g.get("/geocode", map[string]string{"q": address}, &resp) + return g.geocode(map[string]string{"q": address}) +} + +// Geocode single address with full component list +// See: https://www.geocod.io/docs/#single-address +func (g *Geocodio) GeocodeComponents(address InputAddress) (GeocodeResult, error) { + if address.Street == "" && address.City == "" && address.State == "" && address.PostalCode == "" && address.Country == "" { + return GeocodeResult{}, ErrAddressIsEmpty + } + + return g.geocode(map[string]string{ + "street": address.Street, + "city": address.City, + "state": address.State, + "postal_code": address.PostalCode, + "country": address.Country}) +} + +func (g *Geocodio) geocode(params map[string]string) (GeocodeResult, error) { + resp := GeocodeResult{} + err := g.do("GET", "/geocode", params, nil, &resp) if err != nil { - return GeocodeResult{}, err + return resp, err } if len(resp.Results) == 0 { @@ -103,17 +80,20 @@ func (g *Geocodio) Geocode(address string) (GeocodeResult, error) { return resp, nil } -// GeocodeBatch look up addresses -func (g *Geocodio) GeocodeBatch(addresses ...string) (BatchResponse, error) { +// GeocodeBatch lookup list of addresses (either string or InputAddress) +func (g *Geocodio) GeocodeBatch(addresses ...interface{}) (BatchResponse, error) { resp := BatchResponse{} if len(addresses) == 0 { return resp, ErrBatchAddressesIsEmpty } + if err := verifyValidAddresses(addresses); err != nil { + return resp, err + } // TODO: support limit - err := g.post("/geocode", addresses, nil, &resp) + err := g.do("POST", "/geocode", nil, addresses, &resp) if err != nil { - return BatchResponse{}, err + return resp, err } if len(resp.Results) == 0 { @@ -123,6 +103,21 @@ func (g *Geocodio) GeocodeBatch(addresses ...string) (BatchResponse, error) { return resp, nil } +func verifyValidAddresses(addresses []interface{}) error { + var builder strings.Builder + for i := range addresses { + switch addresses[i].(type) { + case string, InputAddress: + default: + builder.WriteString(fmt.Sprintf("address[%d]: %t ", i, addresses[i])) + } + } + if builder.Len() > 0 { + return fmt.Errorf("all addresses must be of type string or InputAddress: %s", builder.String()) + } + return nil +} + // GeocodeAndReturnTimezone will geocode and include Timezone in the fields response func (g *Geocodio) GeocodeAndReturnTimezone(address string) (GeocodeResult, error) { return g.GeocodeReturnFields(address, "timezone") @@ -159,11 +154,10 @@ func (g *Geocodio) GeocodeReturnFields(address string, fields ...string) (Geocod fieldsCommaSeparated := strings.Join(fields, ",") - err := g.get("/geocode", - map[string]string{ - "q": address, - "fields": fieldsCommaSeparated, - }, &resp) + err := g.do("GET", "/geocode", map[string]string{ + "q": address, + "fields": fieldsCommaSeparated, + }, nil, &resp) if err != nil { return resp, err } diff --git a/geocode_test.go b/geocode_test.go index 58865b7..11e2694 100644 --- a/geocode_test.go +++ b/geocode_test.go @@ -1,125 +1,57 @@ package geocodio_test import ( - "fmt" "testing" "github.com/stevepartridge/geocodio" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestGeocodeWithEmptyAddress(t *testing.T) { gc, err := geocodio.New() - if err != nil { - t.Error("Failed with API KEY set.", err) - } - _, err = gc.Geocode("") - if err == nil { - t.Error("Error should not be nil.") - } -} - -func TestGeocodeDebugResponseAsString(t *testing.T) { - gc, err := geocodio.New() - if err != nil { - t.Error("Failed with API KEY set.", err) - } - result, err := gc.Geocode(AddressTestOneFull) - if err != nil { - t.Error(err) - } - - if result.ResponseAsString() == "" { - t.Error("Response should be a valid string.") - } + require.NoError(t, err) + _, err = gc.Geocode("") + assert.Error(t, err) } func TestGeocodeFullAddress(t *testing.T) { gc, err := geocodio.New() - if err != nil { - t.Error("Failed with API KEY set.", err) - } - result, err := gc.Geocode(AddressTestOneFull) - if err != nil { - t.Error(err) - } - - // t.Log(result.ResponseAsString()) + require.NoError(t, err) - if len(result.Results) == 0 { - t.Error("Results length is 0") - } - - if result.Results[0].Location.Latitude != AddressTestOneLatitude { - t.Errorf("Location latitude %f does not match %f", result.Results[0].Location.Latitude, AddressTestOneLatitude) - } - - if result.Results[0].Location.Longitude != AddressTestOneLongitude { - t.Errorf("Location longitude %f does not match %f", result.Results[0].Location.Longitude, AddressTestOneLongitude) - } + result, err := gc.Geocode(AddressTestOneFull) + require.NoError(t, err) + require.True(t, len(result.Results) > 0) + assert.Equal(t, AddressTestOneLatitude, result.Results[0].Location.Latitude) + assert.Equal(t, AddressTestOneLongitude, result.Results[0].Location.Longitude) } func TestGeocodeFullAddressReturningTimezone(t *testing.T) { gc, err := geocodio.New() - if err != nil { - t.Error("Failed with API KEY set.", err) - } - result, err := gc.GeocodeAndReturnTimezone(AddressTestOneFull) - if err != nil { - t.Error(err) - } - - if len(result.Results) == 0 { - t.Error("Results length is 0") - } - - if result.Results[0].Location.Latitude != AddressTestOneLatitude { - t.Errorf("Location latitude %f does not match %f", result.Results[0].Location.Latitude, AddressTestOneLatitude) - } - - if result.Results[0].Location.Longitude != AddressTestOneLongitude { - t.Errorf("Location longitude %f does not match %f", result.Results[0].Location.Longitude, AddressTestOneLongitude) - } + require.NoError(t, err) - if result.Results[0].Fields.Timezone.Name == "" { - t.Error("Timezone field not found") - } + result, err := gc.GeocodeAndReturnTimezone(AddressTestOneFull) + assert.NoError(t, err) + require.True(t, len(result.Results) > 0) - if !result.Results[0].Fields.Timezone.ObservesDST { - t.Error("Timezone field does not match", result.Results[0].Fields.Timezone) - } + assert.Equal(t, AddressTestOneLatitude, result.Results[0].Location.Latitude) + assert.Equal(t, AddressTestOneLongitude, result.Results[0].Location.Longitude) + assert.NotEmpty(t, result.Results[0].Fields.Timezone.Name) + assert.True(t, result.Results[0].Fields.Timezone.ObservesDST) } func TestGeocodeFullAddressReturningZip4(t *testing.T) { gc, err := geocodio.New() - if err != nil { - t.Error("Failed with API KEY set.", err) - } + require.NoError(t, err) result, err := gc.GeocodeAndReturnZip4(AddressTestOneFull) - if err != nil { - t.Error(err) - } - - if len(result.Results) == 0 { - t.Error("Results length is 0") - } - - if result.Results[0].Location.Latitude != AddressTestOneLatitude { - t.Errorf("Location latitude %f does not match %f", result.Results[0].Location.Latitude, AddressTestOneLatitude) - } - - if result.Results[0].Location.Longitude != AddressTestOneLongitude { - t.Errorf("Location longitude %f does not match %f", result.Results[0].Location.Longitude, AddressTestOneLongitude) - } - - if len(result.Results[0].Fields.Zip4.Plus4) == 0 { - t.Error("Zip4 field not found") - } + require.NoError(t, err) + require.True(t, len(result.Results) > 0) - // if !result.Results[0].Fields.Timezone.ObservesDST { - // t.Error("Zip4 field does not match", result.Results[0].Fields.Timezone) - // } + assert.Equal(t, AddressTestOneLatitude, result.Results[0].Location.Latitude) + assert.Equal(t, AddressTestOneLongitude, result.Results[0].Location.Longitude) + assert.True(t, len(result.Results[0].Fields.Zip4.Plus4) > 0) } func TestGeocodeFullAddressReturningCongressionalDistrict(t *testing.T) { @@ -149,7 +81,7 @@ func TestGeocodeFullAddressReturningCongressionalDistrict(t *testing.T) { } if len(result.Results[0].Fields.CongressionalDistricts) == 0 { - t.Error("Congressional District field not found", result.Results[0].Fields.CongressionalDistrict) + t.Error("Congressional District field not found", result.Results[0].Fields.CongressionalDistricts) t.Fail() } @@ -159,7 +91,7 @@ func TestGeocodeFullAddressReturningCongressionalDistrict(t *testing.T) { } if result.Results[0].Fields.CongressionalDistricts[0].DistrictNumber != 8 { - t.Error("Congressional District field does not match", result.Results[0].Fields.CongressionalDistrict) + t.Error("Congressional District field does not match", result.Results[0].Fields.CongressionalDistricts) t.Fail() } } @@ -238,19 +170,16 @@ func TestGeocodeFullAddressReturningMultipleFields(t *testing.T) { congressionalDistrict := geocodio.CongressionalDistrict{} - // check congressional district - if result.Results[0].Fields.CongressionalDistrict.Name != "" { - congressionalDistrict = result.Results[0].Fields.CongressionalDistrict - } else if len(result.Results[0].Fields.CongressionalDistricts) > 0 { + if len(result.Results[0].Fields.CongressionalDistricts) > 0 { congressionalDistrict = result.Results[0].Fields.CongressionalDistricts[0] } if congressionalDistrict.Name == "" { - t.Error("Congressional District field not found", congressionalDistrict) + t.Error("Congressional District field not found", congressionalDistrict.Name) } if congressionalDistrict.DistrictNumber != 8 { - t.Error("Congressional District field does not match", result.Results[0].Fields.CongressionalDistrict) + t.Error("Congressional District field does not match", congressionalDistrict.DistrictNumber) } } @@ -298,10 +227,9 @@ func TestGeocodeInvalidNoResults(t *testing.T) { t.Error("Failed with API KEY set.", err) } - resp, err := gc.Geocode("123 Nonsense Ln, Nowhere, XX") + _, err = gc.Geocode("123 Nonsense Ln, Nowhere, XX") if err == nil { t.Error("Expected to see an error") - fmt.Println(resp.ResponseAsString()) return } if err != geocodio.ErrNoResultsFound { @@ -321,7 +249,6 @@ func TestGeocodeBatchInvalidNoResults(t *testing.T) { } if resp.Results[0].Response.Error == "" { t.Error("Expected to see an error") - fmt.Println(resp.ResponseAsString()) } } diff --git a/geocodio.go b/geocodio.go index 1f75f10..475717f 100644 --- a/geocodio.go +++ b/geocodio.go @@ -1,19 +1,24 @@ package geocodio import ( - "fmt" + "net/http" "os" "strings" + "time" + + "github.com/dghubble/sling" ) const ( // GeocodioAPIBaseURLv1 is the Geocod.io Base URL GeocodioAPIBaseURLv1 = "https://api.geocod.io/v1.6" + EnvGeocodioAPIKey = "GEOCODIO_API_KEY" ) // Geocodio is the base struct type Geocodio struct { - APIKey string + APIKey string `url:"api_key"` + client sling.Doer } type Input struct { @@ -21,15 +26,67 @@ type Input struct { FormattedAddress string `json:"formatted_address"` } +// InputAddress contains the address components available +// for either a single address request or a batch request +type InputAddress struct { + Street string `json:"street"` + City string `json:"city"` + State string `json:"state"` + PostalCode string `json:"postal_code"` + Country string `json:"country"` +} + +type Address struct { + Query string `json:"query"` + Components Components `json:"address_components"` + Formatted string `json:"formatted_address"` + Location Location `json:"location"` + Accuracy float64 `json:"accuracy"` + AccuracyType string `json:"accuracy_type"` + Source string `json:"source"` + Fields Fields `json:"fields,omitempty"` +} + +// Components +type Components struct { + Number string `json:"number"` + Street string `json:"street"` + Suffix string `json:"suffix"` + SecondaryNumber string `json:"secondarynumber"` + SecondaryUnit string `json:"secondaryunit"` + PostDirectional string `json:"postdirectional"` + FormattedStreet string `json:"formatted_street"` + City string `json:"city"` + State string `json:"state"` + Zip string `json:"zip"` + County string `json:"county"` + Country string `json:"country"` + PreDirectional string `json:"predirectional"` + Prefix string `json:"prefix"` +} + +type Location struct { + Latitude float64 `json:"lat"` + Longitude float64 `json:"lng"` +} + +// Fields +type Fields struct { + Timezone Timezone `json:"timezone,omitempty"` + Zip4 Zip4 `json:"zip4,omitempty"` + CongressionalDistricts []CongressionalDistrict `json:"congressional_districts,omitempty"` // v1.1+ + StateLegislativeDistricts StateLegislativeDistricts `json:"state_legislative_districts,omitempty"` + SchoolDistricts SchoolDistricts `json:"school_districts,omitempty"` + Census CensusResults `json:"census,omitempty"` + ACS CensusACS `json:"acs,omitempty"` + Riding Riding `json:"riding,omitempty"` + Statcan Statcan `json:"statcan,omitempty"` +} + // New creates a Geocodio instance based on an API key in either the environment // or passed in as the first string value func New(apiKey ...string) (*Geocodio, error) { - key := os.Getenv(EnvGeocodioAPIKey) - if strings.TrimSpace(key) == "" { - key = os.Getenv(EnvOldAPIKey) - } - if len(apiKey) == 0 && strings.TrimSpace(key) == "" { return nil, ErrMissingAPIKey } @@ -42,31 +99,43 @@ func New(apiKey ...string) (*Geocodio, error) { return nil, ErrMissingAPIKey } + timeout := time.Duration(10 * time.Second) + client := &http.Client{ + Timeout: timeout, + } + g := Geocodio{ APIKey: key, + client: client, } return &g, nil } -// NewGeocodio is a helper to create new Geocodio reference -// since 1.6+ this is kept for backwards compatiblity -// this is deprecatd and will be removed in 2+ -func NewGeocodio(apiKey string) (*Geocodio, error) { - - fmt.Println(` - NewGeocodio() is deprecated and will be removed in 2+ - Use geocodio.New("YOUR_API_KEY") - or with the environment variable ` + EnvGeocodioAPIKey + ` - Use geocodio.New()`) - - if apiKey == "" { - return nil, ErrMissingAPIKey +func (g *Geocodio) do(method, path string, params map[string]string, bodyJSON, result interface{}) error { + s := sling.New(). + Doer(g.client). + Base(GeocodioAPIBaseURLv1). + QueryStruct(g). + Set("Content-Type", "application/json"). + Path(path). + BodyJSON(bodyJSON) + + req, err := s.Request() + if err != nil { + return err } + req.Method = method + req.URL.RawQuery = getQueryString(req, params) - g := Geocodio{ - APIKey: apiKey, - } + _, err = s.Do(req, result, nil) + return err +} - return &g, nil +func getQueryString(req *http.Request, params map[string]string) string { + query := req.URL.Query() + for key, value := range params { + query.Add(key, value) + } + return query.Encode() } diff --git a/geocodio_test.go b/geocodio_test.go index ead5c48..e7526a1 100644 --- a/geocodio_test.go +++ b/geocodio_test.go @@ -4,6 +4,7 @@ import ( "os" "testing" + "github.com/mypricehealth/jsonassert" "github.com/stevepartridge/geocodio" ) @@ -56,12 +57,7 @@ func TestGeocodioWithApiKey(t *testing.T) { func TestGeocodioWithoutApiKey(t *testing.T) { key := os.Getenv(geocodio.EnvGeocodioAPIKey) - if key == "" && os.Getenv(geocodio.EnvOldAPIKey) != "" { - key = os.Getenv(geocodio.EnvOldAPIKey) - } - os.Setenv(geocodio.EnvGeocodioAPIKey, "") - os.Setenv(geocodio.EnvOldAPIKey, "") _, err := geocodio.New() if err == nil { @@ -72,14 +68,8 @@ func TestGeocodioWithoutApiKey(t *testing.T) { } func TestGeocodioWithoutApiKeyEnvAndEmptyString(t *testing.T) { - key := os.Getenv(geocodio.EnvGeocodioAPIKey) - if key == "" && os.Getenv(geocodio.EnvOldAPIKey) != "" { - key = os.Getenv(geocodio.EnvOldAPIKey) - } - os.Setenv(geocodio.EnvGeocodioAPIKey, "") - os.Setenv(geocodio.EnvOldAPIKey, "") _, err := geocodio.New("") if err == nil { @@ -89,29 +79,22 @@ func TestGeocodioWithoutApiKeyEnvAndEmptyString(t *testing.T) { os.Setenv(geocodio.EnvGeocodioAPIKey, key) } -func TestGeocodioDeprecatedWithApiKey(t *testing.T) { - _, err := geocodio.NewGeocodio(os.Getenv(geocodio.EnvGeocodioAPIKey)) - if err != nil { - t.Error("Failed with API KEY set.", err) - } -} - -func TestGeocodioDeprecatedWithoutApiKey(t *testing.T) { - _, err := geocodio.NewGeocodio("") - if err == nil { - t.Error("Expected error:", geocodio.ErrMissingAPIKey, " but did not see any error") - } -} - -func TestGeocodioWithOldApiKey(t *testing.T) { - os.Setenv(geocodio.EnvOldAPIKey, os.Getenv(geocodio.EnvGeocodioAPIKey)) - os.Setenv(geocodio.EnvGeocodioAPIKey, "") - - _, err := geocodio.New() - if err != nil { - t.Error("Failed with API KEY set.", err) - } - - os.Setenv(geocodio.EnvGeocodioAPIKey, os.Getenv(geocodio.EnvOldAPIKey)) - os.Setenv(geocodio.EnvOldAPIKey, "") +func TestVerifyStructs(t *testing.T) { + jsonassert.StructCheck(t, "testdata/batchResponse.json", &geocodio.BatchResponse{}) + jsonassert.StructCheck(t, "testdata/fields-acs-demographics.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-acs-economics.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-acs-families.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-acs-housing.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-acs-social.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-cd.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-census.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-riding.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-school (unified).json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-school.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-statcan.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-stateleg.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-timezone.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/fields-zip4.json", &geocodio.Fields{}) + jsonassert.StructCheck(t, "testdata/singleResponse.json", &geocodio.GeocodeResult{}) + jsonassert.StructCheck(t, "testdata/singleResponseWithAllFields.json", &geocodio.GeocodeResult{}) } diff --git a/go.mod b/go.mod index be0d320..dee1902 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,9 @@ module github.com/stevepartridge/geocodio go 1.13 + +require ( + github.com/dghubble/sling v1.4.0 + github.com/mypricehealth/jsonassert v0.0.0-20211011194146-84dc9e20f6e9 + github.com/stretchr/testify v1.7.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1f6c3aa --- /dev/null +++ b/go.sum @@ -0,0 +1,21 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dghubble/sling v1.4.0 h1:/n8MRosVTthvMbwlNZgLx579OGVjUOy3GNEv5BIqAWY= +github.com/dghubble/sling v1.4.0/go.mod h1:0r40aNsU9EdDUVBNhfCstAtFgutjgJGYbO1oNzkMoM8= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/mypricehealth/jsonassert v0.0.0-20211011194146-84dc9e20f6e9 h1://4h8UKY5Sx/tXEUsEXJtX6fnUNLQ24bEUaBCQMnrIE= +github.com/mypricehealth/jsonassert v0.0.0-20211011194146-84dc9e20f6e9/go.mod h1:VRikT2/QYZ7/pDFDI6YIaLGaityo8z0+VKM/ahm+hls= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/location.go b/location.go deleted file mode 100644 index df564f6..0000000 --- a/location.go +++ /dev/null @@ -1,6 +0,0 @@ -package geocodio - -type Location struct { - Latitude float64 `json:"lat"` - Longitude float64 `json:"lng"` -} diff --git a/reverse.go b/reverse.go index e712bcc..9fc1eb0 100644 --- a/reverse.go +++ b/reverse.go @@ -20,7 +20,7 @@ func (g *Geocodio) Reverse(latitude, longitude float64) (GeocodeResult, error) { lngStr := strconv.FormatFloat(longitude, 'f', 9, 64) resp := GeocodeResult{} - err := g.get("/reverse", map[string]string{"q": latStr + "," + lngStr}, &resp) + err := g.do("GET", "/reverse", map[string]string{"q": latStr + "," + lngStr}, nil, &resp) if err != nil { return resp, err } @@ -74,7 +74,7 @@ func (g *Geocodio) ReverseBatch(latlngs ...float64) (BatchResponse, error) { pair = coord } - err := g.post("/reverse", payload, nil, &resp) + err := g.do("POST", "/reverse", nil, payload, &resp) if err != nil { return resp, err } diff --git a/reverse_test.go b/reverse_test.go index b09d3af..88ebe19 100644 --- a/reverse_test.go +++ b/reverse_test.go @@ -1,7 +1,6 @@ package geocodio_test import ( - "fmt" "testing" "github.com/stevepartridge/geocodio" @@ -87,7 +86,6 @@ func TestReverseBatchLookup(t *testing.T) { AddressTestTwoLatitude, AddressTestTwoLongitude, AddressTestThreeLatitude, AddressTestThreeLongitude, ) - fmt.Println(result.ResponseAsString()) if err != nil { t.Error(err) } diff --git a/school.go b/school.go index aa62c62..8255e35 100644 --- a/school.go +++ b/school.go @@ -3,18 +3,10 @@ package geocodio type SchoolDistricts struct { Unified SchoolDistrict `json:"unified"` Elementary SchoolDistrict `json:"elementary"` - Secondar SchoolDistrict `json:"secondary"` + Secondary SchoolDistrict `json:"secondary"` } // SchoolDistrict field -/* -{ - "name": "Desert Sands Unified School District", - "lea_code": "11110", - "grade_low": "KG", - "grade_high": "12" -} -*/ type SchoolDistrict struct { Name string `json:"name"` LEACode string `json:"lea_code"` diff --git a/testdata/batchResponse.json b/testdata/batchResponse.json new file mode 100644 index 0000000..1833889 --- /dev/null +++ b/testdata/batchResponse.json @@ -0,0 +1,106 @@ +{ + "results": [ + { + "query": "1109 N Highland St, Arlington VA", + "response": { + "input": { + "address_components": { + "number": "1109", + "predirectional": "N", + "street": "Highland", + "suffix": "St", + "formatted_street": "N Highland St", + "city": "Arlington", + "state": "VA", + "country": "US" + }, + "formatted_address": "1109 N Highland St, Arlington, VA" + }, + "results": [ + { + "address_components": { + "number": "1109", + "predirectional": "N", + "street": "Highland", + "suffix": "St", + "formatted_street": "N Highland St", + "city": "Arlington", + "county": "Arlington County", + "state": "VA", + "zip": "22201", + "country": "US" + }, + "formatted_address": "1109 N Highland St, Arlington, VA 22201", + "location": { + "lat": 38.886672, + "lng": -77.094735 + }, + "accuracy": 1, + "accuracy_type": "rooftop", + "source": "Arlington" + }, + { + "address_components": { + "number": "1109", + "predirectional": "N", + "street": "Highland", + "suffix": "St", + "formatted_street": "N Highland St", + "city": "Arlington", + "county": "Arlington County", + "state": "VA", + "zip": "22201", + "country": "US" + }, + "formatted_address": "1109 N Highland St, Arlington, VA 22201", + "location": { + "lat": 38.886665, + "lng": -77.094733 + }, + "accuracy": 1, + "accuracy_type": "rooftop", + "source": "Virginia Geographic Information Network (VGIN)" + } + ] + } + }, + { + "query": "525 University Ave, Toronto, ON, Canada", + "response": { + "input": { + "address_components": { + "number": "525", + "street": "University", + "suffix": "Ave", + "formatted_street": "University Ave", + "city": "Toronto", + "state": "ON", + "country": "CA" + }, + "formatted_address": "525 University Ave, Toronto, ON" + }, + "results": [ + { + "address_components": { + "number": "525", + "street": "University", + "suffix": "Ave", + "formatted_street": "University Ave", + "city": "Toronto", + "state": "ON", + "country": "CA" + }, + "formatted_address": "525 University Ave, Toronto, ON", + "location": { + "lat": 43.656258, + "lng": -79.388223 + }, + "accuracy": 1, + "accuracy_type": "rooftop", + "source": "City of Toronto Open Data" + } + ] + } + } + ] +} diff --git a/testdata/fields-acs-demographics.json b/testdata/fields-acs-demographics.json new file mode 100644 index 0000000..62ae16f --- /dev/null +++ b/testdata/fields-acs-demographics.json @@ -0,0 +1,409 @@ +{ + "acs": { + "meta": { + "source": "American Community Survey from the US Census Bureau", + "survey_years": "2015-2019", + "survey_duration_years": "5" + }, + "demographics": { + "Median age": { + "meta": { + "table_id": "B01002", + "universe": "Total population" + }, + "Total": { + "value": 32.6, + "margin_of_error": 0.6 + }, + "Male": { + "value": 33.8, + "margin_of_error": 1.8 + }, + "Female": { + "value": 32.1, + "margin_of_error": 0.7 + } + }, + "Population by age range": { + "meta": { + "table_id": "B01001", + "universe": "Total population" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "Male": { + "value": 1835, + "margin_of_error": 195, + "percentage": 0.501 + }, + "Male: Under 5 years": { + "value": 101, + "margin_of_error": 66, + "percentage": 0.055 + }, + "Male: 5 to 9 years": { + "value": 32, + "margin_of_error": 27, + "percentage": 0.017 + }, + "Male: 10 to 14 years": { + "value": 21, + "margin_of_error": 28, + "percentage": 0.011 + }, + "Male: 15 to 17 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 18 and 19 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 20 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 21 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 22 to 24 years": { + "value": 174, + "margin_of_error": 77, + "percentage": 0.095 + }, + "Male: 25 to 29 years": { + "value": 302, + "margin_of_error": 109, + "percentage": 0.165 + }, + "Male: 30 to 34 years": { + "value": 354, + "margin_of_error": 94, + "percentage": 0.193 + }, + "Male: 35 to 39 years": { + "value": 209, + "margin_of_error": 81, + "percentage": 0.114 + }, + "Male: 40 to 44 years": { + "value": 205, + "margin_of_error": 81, + "percentage": 0.112 + }, + "Male: 45 to 49 years": { + "value": 48, + "margin_of_error": 31, + "percentage": 0.026 + }, + "Male: 50 to 54 years": { + "value": 162, + "margin_of_error": 86, + "percentage": 0.088 + }, + "Male: 55 to 59 years": { + "value": 120, + "margin_of_error": 80, + "percentage": 0.065 + }, + "Male: 60 and 61 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 62 to 64 years": { + "value": 8, + "margin_of_error": 12, + "percentage": 0.004 + }, + "Male: 65 and 66 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 67 to 69 years": { + "value": 54, + "margin_of_error": 35, + "percentage": 0.029 + }, + "Male: 70 to 74 years": { + "value": 31, + "margin_of_error": 29, + "percentage": 0.017 + }, + "Male: 75 to 79 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 80 to 84 years": { + "value": 14, + "margin_of_error": 23, + "percentage": 0.008 + }, + "Male: 85 years and over": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female": { + "value": 1829, + "margin_of_error": 203, + "percentage": 0.499 + }, + "Female: Under 5 years": { + "value": 116, + "margin_of_error": 65, + "percentage": 0.063 + }, + "Female: 5 to 9 years": { + "value": 91, + "margin_of_error": 79, + "percentage": 0.05 + }, + "Female: 10 to 14 years": { + "value": 32, + "margin_of_error": 40, + "percentage": 0.017 + }, + "Female: 15 to 17 years": { + "value": 32, + "margin_of_error": 42, + "percentage": 0.017 + }, + "Female: 18 and 19 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 20 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 21 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 22 to 24 years": { + "value": 108, + "margin_of_error": 75, + "percentage": 0.059 + }, + "Female: 25 to 29 years": { + "value": 366, + "margin_of_error": 93, + "percentage": 0.2 + }, + "Female: 30 to 34 years": { + "value": 419, + "margin_of_error": 122, + "percentage": 0.229 + }, + "Female: 35 to 39 years": { + "value": 212, + "margin_of_error": 74, + "percentage": 0.116 + }, + "Female: 40 to 44 years": { + "value": 124, + "margin_of_error": 55, + "percentage": 0.068 + }, + "Female: 45 to 49 years": { + "value": 136, + "margin_of_error": 64, + "percentage": 0.074 + }, + "Female: 50 to 54 years": { + "value": 51, + "margin_of_error": 41, + "percentage": 0.028 + }, + "Female: 55 to 59 years": { + "value": 33, + "margin_of_error": 30, + "percentage": 0.018 + }, + "Female: 60 and 61 years": { + "value": 11, + "margin_of_error": 15, + "percentage": 0.006 + }, + "Female: 62 to 64 years": { + "value": 37, + "margin_of_error": 45, + "percentage": 0.02 + }, + "Female: 65 and 66 years": { + "value": 12, + "margin_of_error": 16, + "percentage": 0.007 + }, + "Female: 67 to 69 years": { + "value": 13, + "margin_of_error": 19, + "percentage": 0.007 + }, + "Female: 70 to 74 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 75 to 79 years": { + "value": 36, + "margin_of_error": 37, + "percentage": 0.02 + }, + "Female: 80 to 84 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 85 years and over": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + }, + "Sex": { + "meta": { + "table_id": "B01001", + "universe": "Total population" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "Male": { + "value": 1835, + "margin_of_error": 195, + "percentage": 0.501 + }, + "Female": { + "value": 1829, + "margin_of_error": 203, + "percentage": 0.499 + } + }, + "Race and ethnicity": { + "meta": { + "table_id": "B03002", + "universe": "Total population" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "Not Hispanic or Latino": { + "value": 3396, + "margin_of_error": 324, + "percentage": 0.927 + }, + "Not Hispanic or Latino: White alone": { + "value": 2556, + "margin_of_error": 330, + "percentage": 0.753 + }, + "Not Hispanic or Latino: Black or African American alone": { + "value": 93, + "margin_of_error": 71, + "percentage": 0.027 + }, + "Not Hispanic or Latino: American Indian and Alaska Native alone": { + "value": 21, + "margin_of_error": 26, + "percentage": 0.006 + }, + "Not Hispanic or Latino: Asian alone": { + "value": 573, + "margin_of_error": 175, + "percentage": 0.169 + }, + "Not Hispanic or Latino: Native Hawaiian and Other Pacific Islander alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Not Hispanic or Latino: Some other race alone": { + "value": 22, + "margin_of_error": 26, + "percentage": 0.006 + }, + "Not Hispanic or Latino: Two or more races": { + "value": 131, + "margin_of_error": 69, + "percentage": 0.039 + }, + "Not Hispanic or Latino: Two or more races: Two races including Some other race": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Not Hispanic or Latino: Two or more races: Two races excluding Some other race, and three or more races": { + "value": 131, + "margin_of_error": 69, + "percentage": 1 + }, + "Hispanic or Latino": { + "value": 268, + "margin_of_error": 128, + "percentage": 0.073 + }, + "Hispanic or Latino: White alone": { + "value": 244, + "margin_of_error": 120, + "percentage": 0.91 + }, + "Hispanic or Latino: Black or African American alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: American Indian and Alaska Native alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Asian alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Native Hawaiian and Other Pacific Islander alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Some other race alone": { + "value": 24, + "margin_of_error": 28, + "percentage": 0.09 + }, + "Hispanic or Latino: Two or more races": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Two or more races: Two races including Some other race": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Two or more races: Two races excluding Some other race, and three or more races": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + } + } + } +} diff --git a/testdata/fields-acs-economics.json b/testdata/fields-acs-economics.json new file mode 100644 index 0000000..c2f588e --- /dev/null +++ b/testdata/fields-acs-economics.json @@ -0,0 +1,117 @@ +{ + "acs": { + "meta": { + "source": "American Community Survey from the US Census Bureau", + "survey_years": "2015-2019", + "survey_duration_years": "5" + }, + "economics": { + "Number of households": { + "meta": { + "table_id": "B19001", + "universe": "Households" + }, + "Total": { + "value": 1999, + "margin_of_error": 87 + } + }, + "Median household income": { + "meta": { + "table_id": "B19013", + "universe": "Households" + }, + "Total": { + "value": 158656, + "margin_of_error": 9227 + } + }, + "Household income": { + "meta": { + "table_id": "B19001", + "universe": "Households" + }, + "Less than $10,000": { + "value": 24, + "margin_of_error": 26, + "percentage": 0.012 + }, + "$10,000 to $14,999": { + "value": 20, + "margin_of_error": 22, + "percentage": 0.01 + }, + "$15,000 to $19,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$20,000 to $24,999": { + "value": 18, + "margin_of_error": 26, + "percentage": 0.009 + }, + "$25,000 to $29,999": { + "value": 10, + "margin_of_error": 16, + "percentage": 0.005 + }, + "$30,000 to $34,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$35,000 to $39,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$40,000 to $44,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$45,000 to $49,999": { + "value": 49, + "margin_of_error": 55, + "percentage": 0.025 + }, + "$50,000 to $59,999": { + "value": 10, + "margin_of_error": 15, + "percentage": 0.005 + }, + "$60,000 to $74,999": { + "value": 87, + "margin_of_error": 49, + "percentage": 0.044 + }, + "$75,000 to $99,999": { + "value": 175, + "margin_of_error": 71, + "percentage": 0.088 + }, + "$100,000 to $124,999": { + "value": 275, + "margin_of_error": 100, + "percentage": 0.138 + }, + "$125,000 to $149,999": { + "value": 223, + "margin_of_error": 88, + "percentage": 0.112 + }, + "$150,000 to $199,999": { + "value": 461, + "margin_of_error": 111, + "percentage": 0.231 + }, + "$200,000 or more": { + "value": 647, + "margin_of_error": 142, + "percentage": 0.324 + } + } + } + } +} diff --git a/testdata/fields-acs-families.json b/testdata/fields-acs-families.json new file mode 100644 index 0000000..87fe519 --- /dev/null +++ b/testdata/fields-acs-families.json @@ -0,0 +1,222 @@ +{ + "acs": { + "meta": { + "source": "American Community Survey from the US Census Bureau", + "survey_years": "2015-2019", + "survey_duration_years": "5" + }, + "families": { + "Household type by household": { + "meta": { + "table_id": "B11001", + "universe": "Households" + }, + "Total": { + "value": 1999, + "margin_of_error": 87 + }, + "Family households": { + "value": 689, + "margin_of_error": 138, + "percentage": 0.345 + }, + "Family households: Married-couple family": { + "value": 585, + "margin_of_error": 116, + "percentage": 0.849 + }, + "Family households: Other family": { + "value": 104, + "margin_of_error": 78, + "percentage": 0.151 + }, + "Family households: Other family: Male householder, no spouse present": { + "value": 51, + "margin_of_error": 67, + "percentage": 0.49 + }, + "Family households: Other family: Female householder, no spouse present": { + "value": 53, + "margin_of_error": 33, + "percentage": 0.51 + }, + "Nonfamily households": { + "value": 1310, + "margin_of_error": 139, + "percentage": 0.655 + }, + "Nonfamily households: Householder living alone": { + "value": 836, + "margin_of_error": 134, + "percentage": 0.638 + }, + "Nonfamily households: Householder not living alone": { + "value": 474, + "margin_of_error": 109, + "percentage": 0.362 + } + }, + "Household type by population": { + "meta": { + "table_id": "B11002", + "universe": "Population in Households" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "In family households": { + "value": 1799, + "margin_of_error": 379, + "percentage": 0.491 + }, + "In family households: In married-couple family": { + "value": 1539, + "margin_of_error": 323, + "percentage": 0.855 + }, + "In family households: In married-couple family: Relatives": { + "value": 1539, + "margin_of_error": 323, + "percentage": 1 + }, + "In family households: In married-couple family: Nonrelatives": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "In family households: In male householder, no spouse present, family": { + "value": 112, + "margin_of_error": 144, + "percentage": 0.062 + }, + "In family households: In male householder, no spouse present, family: Relatives": { + "value": 110, + "margin_of_error": 144, + "percentage": 0.982 + }, + "In family households: In male householder, no spouse present, family: Nonrelatives": { + "value": 2, + "margin_of_error": 5, + "percentage": 0.018 + }, + "In family households: In female householder, no spouse present, family": { + "value": 148, + "margin_of_error": 95, + "percentage": 0.082 + }, + "In family households: In female householder, no spouse present, family: Relatives": { + "value": 138, + "margin_of_error": 86, + "percentage": 0.932 + }, + "In family households: In female householder, no spouse present, family: Nonrelatives": { + "value": 10, + "margin_of_error": 16, + "percentage": 0.068 + }, + "In nonfamily households": { + "value": 1865, + "margin_of_error": 234, + "percentage": 0.509 + } + }, + "Marital status": { + "meta": { + "table_id": "B12001", + "universe": "Population 15 Years And Older" + }, + "Male": { + "value": 1681, + "margin_of_error": 162, + "percentage": 0.514 + }, + "Male: Never married": { + "value": 911, + "margin_of_error": 166, + "percentage": 0.542 + }, + "Male: Now married": { + "value": 672, + "margin_of_error": 135, + "percentage": 0.4 + }, + "Male: Now married: Married, spouse present": { + "value": 588, + "margin_of_error": 113, + "percentage": 0.875 + }, + "Male: Now married: Married, spouse absent": { + "value": 84, + "margin_of_error": 73, + "percentage": 0.125 + }, + "Male: Now married: Married, spouse absent: Separated": { + "value": 7, + "margin_of_error": 12, + "percentage": 0.083 + }, + "Male: Now married: Married, spouse absent: Other": { + "value": 77, + "margin_of_error": 72, + "percentage": 0.917 + }, + "Male: Widowed": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: Divorced": { + "value": 98, + "margin_of_error": 53, + "percentage": 0.058 + }, + "Female": { + "value": 1590, + "margin_of_error": 152, + "percentage": 0.486 + }, + "Female: Never married": { + "value": 827, + "margin_of_error": 129, + "percentage": 0.52 + }, + "Female: Now married": { + "value": 566, + "margin_of_error": 111, + "percentage": 0.356 + }, + "Female: Now married: Married, spouse present": { + "value": 553, + "margin_of_error": 109, + "percentage": 0.977 + }, + "Female: Now married: Married, spouse absent": { + "value": 13, + "margin_of_error": 19, + "percentage": 0.023 + }, + "Female: Now married: Married, spouse absent: Separated": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Now married: Married, spouse absent: Other": { + "value": 13, + "margin_of_error": 19, + "percentage": 1 + }, + "Female: Widowed": { + "value": 26, + "margin_of_error": 29, + "percentage": 0.016 + }, + "Female: Divorced": { + "value": 171, + "margin_of_error": 67, + "percentage": 0.108 + } + } + } + } +} diff --git a/testdata/fields-acs-housing.json b/testdata/fields-acs-housing.json new file mode 100644 index 0000000..a9e28d0 --- /dev/null +++ b/testdata/fields-acs-housing.json @@ -0,0 +1,255 @@ +{ + "acs": { + "meta": { + "source": "American Community Survey from the US Census Bureau", + "survey_years": "2015-2019", + "survey_duration_years": "5" + }, + "housing": { + "Number of housing units": { + "meta": { + "table_id": "B25002", + "universe": "Housing Units" + }, + "Total": { + "value": 2120, + "margin_of_error": 49 + } + }, + "Occupancy status": { + "meta": { + "table_id": "B25002", + "universe": "Housing Units" + }, + "Occupied": { + "value": 1999, + "margin_of_error": 87, + "percentage": 0.943 + }, + "Vacant": { + "value": 121, + "margin_of_error": 71, + "percentage": 0.057 + } + }, + "Ownership of occupied units": { + "meta": { + "table_id": "B25003", + "universe": "Occupied Housing Units" + }, + "Owner occupied": { + "value": 610, + "margin_of_error": 102, + "percentage": 0.305 + }, + "Renter occupied": { + "value": 1389, + "margin_of_error": 108, + "percentage": 0.695 + } + }, + "Units in structure": { + "meta": { + "table_id": "B25024", + "universe": "Housing Units" + }, + "1, detached unit": { + "value": 170, + "margin_of_error": 69, + "percentage": 0.08 + }, + "1, attached unit": { + "value": 90, + "margin_of_error": 31, + "percentage": 0.042 + }, + "2 units": { + "value": 27, + "margin_of_error": 43, + "percentage": 0.013 + }, + "3 or 4 units": { + "value": 46, + "margin_of_error": 56, + "percentage": 0.022 + }, + "5 to 9 units": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "10 to 19 unit": { + "value": 15, + "margin_of_error": 24, + "percentage": 0.007 + }, + "20 to 49 units": { + "value": 98, + "margin_of_error": 77, + "percentage": 0.046 + }, + "50 or more units": { + "value": 1674, + "margin_of_error": 109, + "percentage": 0.79 + }, + "Mobile home units": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Boat, RV, van, etc. units": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + }, + "Median value of owner-occupied housing units": { + "meta": { + "table_id": "B25077", + "universe": "Owner-Occupied Housing Units" + }, + "Total": { + "value": 624500, + "margin_of_error": 67024 + } + }, + "Value of owner-occupied housing units": { + "meta": { + "table_id": "B25075", + "universe": "Owner-Occupied Housing Units" + }, + "Less than $10,000": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$10,000 to $14,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$15,000 to $19,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$20,000 to $24,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$25,000 to $29,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$30,000 to $34,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$35,000 to $39,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$40,000 to $49,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$50,000 to $59,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$60,000 to $69,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$70,000 to $79,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$80,000 to $89,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$90,000 to $99,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$100,000 to $124,999": { + "value": 5, + "margin_of_error": 7, + "percentage": 0.008 + }, + "$125,000 to $149,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$150,000 to $174,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$175,000 to $199,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$200,000 to $249,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$250,000 to $299,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$300,000 to $399,999": { + "value": 6, + "margin_of_error": 11, + "percentage": 0.01 + }, + "$400,000 to $499,999": { + "value": 174, + "margin_of_error": 94, + "percentage": 0.285 + }, + "$500,000 to $749,999": { + "value": 241, + "margin_of_error": 64, + "percentage": 0.395 + }, + "$750,000 to $999,999": { + "value": 93, + "margin_of_error": 51, + "percentage": 0.152 + }, + "$1,000,000 to $1,499,999": { + "value": 53, + "margin_of_error": 32, + "percentage": 0.087 + }, + "$1,500,000 to $1,999,999": { + "value": 38, + "margin_of_error": 23, + "percentage": 0.062 + }, + "$2,000,000 or more": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + } + } + } +} diff --git a/testdata/fields-acs-social.json b/testdata/fields-acs-social.json new file mode 100644 index 0000000..b35d552 --- /dev/null +++ b/testdata/fields-acs-social.json @@ -0,0 +1,476 @@ +{ + "acs": { + "meta": { + "source": "American Community Survey from the US Census Bureau", + "survey_years": "2015-2019", + "survey_duration_years": "5" + }, + "social": { + "Population by minimum level of education": { + "meta": { + "table_id": "B15002", + "universe": "Population 25 Years And Over" + }, + "Total": { + "value": 2957, + "margin_of_error": 214 + }, + "Male": { + "value": 1507, + "margin_of_error": 154, + "percentage": 0.51 + }, + "Male: No schooling completed": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: Nursery to 4th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 5th and 6th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 7th and 8th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 9th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 10th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 11th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 12th grade, no diploma": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: High school graduate (includes equivalency)": { + "value": 48, + "margin_of_error": 36, + "percentage": 0.032 + }, + "Male: Some college, less than 1 year": { + "value": 17, + "margin_of_error": 18, + "percentage": 0.011 + }, + "Male: Some college, 1 or more years, no degree": { + "value": 46, + "margin_of_error": 34, + "percentage": 0.031 + }, + "Male: Associate's degree": { + "value": 36, + "margin_of_error": 28, + "percentage": 0.024 + }, + "Male: Bachelor's degree": { + "value": 540, + "margin_of_error": 163, + "percentage": 0.358 + }, + "Male: Master's degree": { + "value": 442, + "margin_of_error": 112, + "percentage": 0.293 + }, + "Male: Professional school degree": { + "value": 307, + "margin_of_error": 82, + "percentage": 0.204 + }, + "Male: Doctorate degree": { + "value": 71, + "margin_of_error": 40, + "percentage": 0.047 + }, + "Female": { + "value": 1450, + "margin_of_error": 152, + "percentage": 0.49 + }, + "Female: No schooling completed": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Nursery to 4th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 5th and 6th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 7th and 8th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 9th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 10th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 11th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 12th grade, no diploma": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: High school graduate (includes equivalency)": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Some college, less than 1 year": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Some college, 1 or more years, no degree": { + "value": 43, + "margin_of_error": 37, + "percentage": 0.03 + }, + "Female: Associate's degree": { + "value": 38, + "margin_of_error": 54, + "percentage": 0.026 + }, + "Female: Bachelor's degree": { + "value": 619, + "margin_of_error": 142, + "percentage": 0.427 + }, + "Female: Master's degree": { + "value": 425, + "margin_of_error": 106, + "percentage": 0.293 + }, + "Female: Professional school degree": { + "value": 253, + "margin_of_error": 70, + "percentage": 0.174 + }, + "Female: Doctorate degree": { + "value": 72, + "margin_of_error": 44, + "percentage": 0.05 + } + }, + "Population with veteran status": { + "meta": { + "table_id": "B21001", + "universe": "Civilian Population 18 Years And Over" + }, + "Total": { + "value": 3219, + "margin_of_error": 226 + }, + "Veteran": { + "value": 199, + "margin_of_error": 106, + "percentage": 0.062 + }, + "Nonveteran": { + "value": 3020, + "margin_of_error": 233, + "percentage": 0.938 + }, + "Male": { + "value": 1675, + "margin_of_error": 164, + "percentage": 0.52 + }, + "Male: Veteran": { + "value": 167, + "margin_of_error": 98, + "percentage": 0.1 + }, + "Male: Nonveteran": { + "value": 1508, + "margin_of_error": 185, + "percentage": 0.9 + }, + "Male: 18 to 34 years": { + "value": 828, + "margin_of_error": 148, + "percentage": 0.494 + }, + "Male: 18 to 34 years: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 18 to 34 years: Nonveteran": { + "value": 828, + "margin_of_error": 148, + "percentage": 1 + }, + "Male: 35 to 54 years": { + "value": 620, + "margin_of_error": 129, + "percentage": 0.37 + }, + "Male: 35 to 54 years: Veteran": { + "value": 130, + "margin_of_error": 98, + "percentage": 0.21 + }, + "Male: 35 to 54 years: Nonveteran": { + "value": 490, + "margin_of_error": 91, + "percentage": 0.79 + }, + "Male: 55 to 64 years": { + "value": 128, + "margin_of_error": 78, + "percentage": 0.076 + }, + "Male: 55 to 64 years: Veteran": { + "value": 17, + "margin_of_error": 22, + "percentage": 0.133 + }, + "Male: 55 to 64 years: Nonveteran": { + "value": 111, + "margin_of_error": 72, + "percentage": 0.867 + }, + "Male: 65 to 74 years": { + "value": 85, + "margin_of_error": 54, + "percentage": 0.051 + }, + "Male: 65 to 74 years: Veteran": { + "value": 6, + "margin_of_error": 10, + "percentage": 0.071 + }, + "Male: 65 to 74 years: Nonveteran": { + "value": 79, + "margin_of_error": 52, + "percentage": 0.929 + }, + "Male: 75 years and over": { + "value": 14, + "margin_of_error": 23, + "percentage": 0.008 + }, + "Male: 75 years and over: Veteran": { + "value": 14, + "margin_of_error": 23, + "percentage": 1 + }, + "Male: 75 years and over: Nonveteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female": { + "value": 1544, + "margin_of_error": 153, + "percentage": 0.48 + }, + "Female: Veteran": { + "value": 32, + "margin_of_error": 34, + "percentage": 0.021 + }, + "Female: Nonveteran": { + "value": 1512, + "margin_of_error": 156, + "percentage": 0.979 + }, + "Female: 18 to 34 years": { + "value": 891, + "margin_of_error": 136, + "percentage": 0.577 + }, + "Female: 18 to 34 years: Veteran": { + "value": 22, + "margin_of_error": 31, + "percentage": 0.025 + }, + "Female: 18 to 34 years: Nonveteran": { + "value": 869, + "margin_of_error": 137, + "percentage": 0.975 + }, + "Female: 35 to 54 years": { + "value": 511, + "margin_of_error": 115, + "percentage": 0.331 + }, + "Female: 35 to 54 years: Veteran": { + "value": 10, + "margin_of_error": 15, + "percentage": 0.02 + }, + "Female: 35 to 54 years: Nonveteran": { + "value": 501, + "margin_of_error": 112, + "percentage": 0.98 + }, + "Female: 55 to 64 years": { + "value": 81, + "margin_of_error": 57, + "percentage": 0.052 + }, + "Female: 55 to 64 years: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 55 to 64 years: Nonveteran": { + "value": 81, + "margin_of_error": 57, + "percentage": 1 + }, + "Female: 65 to 74 years": { + "value": 25, + "margin_of_error": 35, + "percentage": 0.016 + }, + "Female: 65 to 74 years: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 65 to 74 years: Nonveteran": { + "value": 25, + "margin_of_error": 35, + "percentage": 1 + }, + "Female: 75 years and over": { + "value": 36, + "margin_of_error": 37, + "percentage": 0.023 + }, + "Female: 75 years and over: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 75 years and over: Nonveteran": { + "value": 36, + "margin_of_error": 37, + "percentage": 1 + } + }, + "Period of military service for veterans": { + "meta": { + "table_id": "B21002", + "universe": "Civilian Veterans 18 Years And Over" + }, + "Total": { + "value": 199, + "margin_of_error": 106 + }, + "Gulf War (9/2001 or later), no Gulf War (8/1990 to 8/2001), no Vietnam Era": { + "value": 78, + "margin_of_error": 50, + "percentage": 0.392 + }, + "Gulf War (9/2001 or later) and Gulf War (8/1990 to 8/2001), no Vietnam Era": { + "value": 14, + "margin_of_error": 15, + "percentage": 0.07 + }, + "Gulf War (9/2001 or later), and Gulf War (8/1990 to 8/2001), and Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Gulf War (8/1990 to 8/2001), no Vietnam Era": { + "value": 9, + "margin_of_error": 14, + "percentage": 0.045 + }, + "Gulf War (8/1990 to 8/2001) and Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Vietnam Era, no Korean War, no World War II": { + "value": 14, + "margin_of_error": 23, + "percentage": 0.07 + }, + "Vietnam Era and Korean War, no World War II": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Vietnam Era and Korean War and World War II": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Korean War, no Vietnam Era, no World War II": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Korean War and World War II, no Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "World War II, no Korean War, no Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Between Gulf War and Vietnam Era only": { + "value": 78, + "margin_of_error": 82, + "percentage": 0.392 + }, + "Between Vietnam Era and Korean War only": { + "value": 6, + "margin_of_error": 10, + "percentage": 0.03 + }, + "Between Korean War and World War II only": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Pre-World War II only": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + } + } + } +} diff --git a/testdata/fields-cd.json b/testdata/fields-cd.json new file mode 100644 index 0000000..3d40ad0 --- /dev/null +++ b/testdata/fields-cd.json @@ -0,0 +1,124 @@ +{ + "congressional_districts": [ + { + "name": "Congressional District 8", + "district_number": 8, + "congress_number": "117th", + "congress_years": "2021-2023", + "proportion": 1, + "current_legislators": [ + { + "type": "representative", + "bio": { + "last_name": "Beyer", + "first_name": "Donald", + "birthday": "1950-06-20", + "gender": "M", + "party": "Democrat" + }, + "contact": { + "url": "https://beyer.house.gov", + "address": "1119 Longworth House Office Building Washington DC 20515-4608", + "phone": "202-225-4376", + "contact_form": null + }, + "social": { + "rss_url": null, + "twitter": "RepDonBeyer", + "facebook": "RepDonBeyer", + "youtube": null, + "youtube_id": "UCPJGVbOVcAVGiBwq8qr_T9w" + }, + "references": { + "bioguide_id": "B001292", + "thomas_id": "02272", + "opensecrets_id": "N00036018", + "lis_id": null, + "cspan_id": "21141", + "govtrack_id": "412657", + "votesmart_id": "1707", + "ballotpedia_id": "Don Beyer", + "washington_post_id": null, + "icpsr_id": "21554", + "wikipedia_id": "Don Beyer" + }, + "source": "Legislator data is originally collected and aggregated by https://github.com/unitedstates/" + }, + { + "type": "senator", + "bio": { + "last_name": "Warner", + "first_name": "Mark", + "birthday": "1954-12-15", + "gender": "M", + "party": "Democrat" + }, + "contact": { + "url": "https://www.warner.senate.gov", + "address": "703 Hart Senate Office Building Washington DC 20510", + "phone": "202-224-2023", + "contact_form": "http://www.warner.senate.gov/public/index.cfm?p=Contact" + }, + "social": { + "rss_url": "http://www.warner.senate.gov/public/?a=rss.feed", + "twitter": "MarkWarner", + "facebook": "MarkRWarner", + "youtube": "SenatorMarkWarner", + "youtube_id": "UCwyivNlEGf4sGd1oDLfY5jw" + }, + "references": { + "bioguide_id": "W000805", + "thomas_id": "01897", + "opensecrets_id": "N00002097", + "lis_id": "S327", + "cspan_id": "7630", + "govtrack_id": "412321", + "votesmart_id": "535", + "ballotpedia_id": "Mark Warner", + "washington_post_id": null, + "icpsr_id": "40909", + "wikipedia_id": "Mark Warner" + }, + "source": "Legislator data is originally collected and aggregated by https://github.com/unitedstates/" + }, + { + "type": "senator", + "bio": { + "last_name": "Kaine", + "first_name": "Timothy", + "birthday": "1958-02-26", + "gender": "M", + "party": "Democrat" + }, + "contact": { + "url": "https://www.kaine.senate.gov", + "address": "231 Russell Senate Office Building Washington DC 20510", + "phone": "202-224-4024", + "contact_form": "https://www.kaine.senate.gov/contact" + }, + "social": { + "rss_url": "http://www.kaine.senate.gov/rss/feeds/?type=all", + "twitter": null, + "facebook": "SenatorKaine", + "youtube": "SenatorTimKaine", + "youtube_id": "UC27LgTZlUnBQoNEQFZdn9LA" + }, + "references": { + "bioguide_id": "K000384", + "thomas_id": "02176", + "opensecrets_id": "N00033177", + "lis_id": "S362", + "cspan_id": "49219", + "govtrack_id": "412582", + "votesmart_id": "50772", + "ballotpedia_id": "Tim Kaine", + "washington_post_id": null, + "icpsr_id": "41305", + "wikipedia_id": "Tim Kaine" + }, + "source": "Legislator data is originally collected and aggregated by https://github.com/unitedstates/" + } + ] + } + ] +} diff --git a/testdata/fields-census.json b/testdata/fields-census.json new file mode 100644 index 0000000..83d4c76 --- /dev/null +++ b/testdata/fields-census.json @@ -0,0 +1,58 @@ +{ + "census": { + "2010": { + "census_year": 2010, + "state_fips": "51", + "county_fips": "51013", + "tract_code": "101801", + "block_code": "1004", + "block_group": "1", + "full_fips": "510131018011004", + "place": { + "name": "Arlington", + "fips": "5103000" + }, + "metro_micro_statistical_area": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47900", + "type": "metropolitan" + }, + "combined_statistical_area": { + "name": "Washington-Baltimore-Northern Virginia, DC-MD-VA-WV", + "area_code": "51548" + }, + "metropolitan_division": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47894" + }, + "source": "US Census Bureau" + }, + "2020": { + "census_year": 2020, + "state_fips": "51", + "county_fips": "51013", + "tract_code": "101801", + "block_code": "1004", + "block_group": "1", + "full_fips": "510131018011004", + "place": { + "name": "Arlington", + "fips": "5103000" + }, + "metro_micro_statistical_area": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47900", + "type": "metropolitan" + }, + "combined_statistical_area": { + "name": "Washington-Baltimore-Arlington, DC-MD-VA-WV-PA", + "area_code": "548" + }, + "metropolitan_division": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47894" + }, + "source": "US Census Bureau" + } + } +} diff --git a/testdata/fields-riding.json b/testdata/fields-riding.json new file mode 100644 index 0000000..e6c7786 --- /dev/null +++ b/testdata/fields-riding.json @@ -0,0 +1,7 @@ +{ + "riding": { + "code": "24068", + "name_french": "Saint-Laurent", + "name_english": "Saint-Laurent" + } +} diff --git a/testdata/fields-school (unified).json b/testdata/fields-school (unified).json new file mode 100644 index 0000000..8feb80a --- /dev/null +++ b/testdata/fields-school (unified).json @@ -0,0 +1,10 @@ +{ + "school_districts": { + "unified": { + "name": "Desert Sands Unified School District", + "lea_code": "11110", + "grade_low": "KG", + "grade_high": "12" + } + } +} diff --git a/testdata/fields-school.json b/testdata/fields-school.json new file mode 100644 index 0000000..e3779aa --- /dev/null +++ b/testdata/fields-school.json @@ -0,0 +1,16 @@ +{ + "school_districts": { + "elementary": { + "name": "Topsfield School District", + "lea_code": "11670", + "grade_low": "PK", + "grade_high": "06" + }, + "secondary": { + "name": "Masconomet School District", + "lea_code": "07410", + "grade_low": "07", + "grade_high": "12" + } + } +} diff --git a/testdata/fields-statcan.json b/testdata/fields-statcan.json new file mode 100644 index 0000000..e1e04ee --- /dev/null +++ b/testdata/fields-statcan.json @@ -0,0 +1,35 @@ +{ + "statcan": { + "division": { + "id": "2466", + "name": "Montréal", + "type": "TÉ", + "type_description": "Territoire équivalent" + }, + "consolidated_subdivision": { + "id": "2466023", + "name": "Montréal" + }, + "subdivision": { + "id": "2466023", + "name": "Montréal", + "type": "V", + "type_description": "Ville" + }, + "economic_region": "Montréal", + "statistical_area": { + "code": "462", + "code_description": "CMA or CA", + "type": "1", + "type_description": "Census subdivision within census metropolitan area" + }, + "cma_ca": { + "id": "462", + "name": "Montréal", + "type": "B", + "type_description": "Census metropolitan area (CMA)" + }, + "tract": "4620415.04", + "census_year": 2016 + } +} diff --git a/testdata/fields-stateleg.json b/testdata/fields-stateleg.json new file mode 100644 index 0000000..49ce9d2 --- /dev/null +++ b/testdata/fields-stateleg.json @@ -0,0 +1,14 @@ +{ + "state_legislative_districts": { + "house": { + "name": "Assembly District 42", + "district_number": "42", + "is_upcoming_state_legislative_district": false + }, + "senate": { + "name": "State Senate District 28", + "district_number": "28", + "is_upcoming_state_legislative_district": false + } + } +} diff --git a/testdata/fields-timezone.json b/testdata/fields-timezone.json new file mode 100644 index 0000000..bbdfa37 --- /dev/null +++ b/testdata/fields-timezone.json @@ -0,0 +1,9 @@ +{ + "timezone": { + "name": "America/New_York", + "utc_offset": -5, + "observes_dst": true, + "abbreviation": "EST", + "source": "© OpenStreetMap contributors" + } +} diff --git a/testdata/fields-zip4.json b/testdata/fields-zip4.json new file mode 100644 index 0000000..c1fd5ff --- /dev/null +++ b/testdata/fields-zip4.json @@ -0,0 +1,27 @@ +{ + "zip4": { + "record_type": { + "code": "S", + "description": "Street" + }, + "carrier_route": { + "id": "C007", + "description": "City Delivery" + }, + "building_or_firm_name": null, + "plus4": [ + "2890" + ], + "zip9": [ + "22201-2890" + ], + "government_building": null, + "facility_code": { + "code": "P", + "description": "Post Office" + }, + "city_delivery": true, + "valid_delivery_area": true, + "exact_match": true + } +} diff --git a/testdata/singleResponse.json b/testdata/singleResponse.json new file mode 100644 index 0000000..b0744ab --- /dev/null +++ b/testdata/singleResponse.json @@ -0,0 +1,40 @@ +{ + "input": { + "address_components": { + "number": "1109", + "predirectional": "N", + "street": "Highland", + "suffix": "St", + "formatted_street": "N Highland St", + "city": "Arlington", + "state": "VA", + "zip": "22201", + "country": "US" + }, + "formatted_address": "1109 N Highland St, Arlington, VA 22201" + }, + "results": [ + { + "address_components": { + "number": "1109", + "predirectional": "N", + "street": "Highland", + "suffix": "St", + "formatted_street": "N Highland St", + "city": "Arlington", + "county": "Arlington County", + "state": "VA", + "zip": "22201", + "country": "US" + }, + "formatted_address": "1109 N Highland St, Arlington, VA 22201", + "location": { + "lat": 38.886665, + "lng": -77.094733 + }, + "accuracy": 1, + "accuracy_type": "rooftop", + "source": "Virginia GIS Clearinghouse" + } + ] +} diff --git a/testdata/singleResponseWithAllFields.json b/testdata/singleResponseWithAllFields.json new file mode 100644 index 0000000..0b6a95e --- /dev/null +++ b/testdata/singleResponseWithAllFields.json @@ -0,0 +1,1692 @@ +{ + "input": { + "address_components": { + "number": "1109", + "predirectional": "N", + "street": "Highland", + "suffix": "St", + "formatted_street": "N Highland St", + "city": "Arlington", + "state": "VA", + "country": "US" + }, + "formatted_address": "1109 N Highland St, Arlington, VA" + }, + "results": [ + { + "address_components": { + "number": "1109", + "predirectional": "N", + "street": "Highland", + "suffix": "St", + "formatted_street": "N Highland St", + "city": "Arlington", + "county": "Arlington County", + "state": "VA", + "zip": "22201", + "country": "US" + }, + "formatted_address": "1109 N Highland St, Arlington, VA 22201", + "location": { + "lat": 38.886672, + "lng": -77.094735 + }, + "accuracy": 1, + "accuracy_type": "rooftop", + "source": "Arlington", + "fields": { + "acs": { + "meta": { + "source": "American Community Survey from the US Census Bureau", + "survey_years": "2015-2019", + "survey_duration_years": "5" + }, + "demographics": { + "Median age": { + "meta": { + "table_id": "B01002", + "universe": "Total population" + }, + "Total": { + "value": 32.6, + "margin_of_error": 0.6 + }, + "Male": { + "value": 33.8, + "margin_of_error": 1.8 + }, + "Female": { + "value": 32.1, + "margin_of_error": 0.7 + } + }, + "Population by age range": { + "meta": { + "table_id": "B01001", + "universe": "Total population" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "Male": { + "value": 1835, + "margin_of_error": 195, + "percentage": 0.501 + }, + "Male: Under 5 years": { + "value": 101, + "margin_of_error": 66, + "percentage": 0.055 + }, + "Male: 5 to 9 years": { + "value": 32, + "margin_of_error": 27, + "percentage": 0.017 + }, + "Male: 10 to 14 years": { + "value": 21, + "margin_of_error": 28, + "percentage": 0.011 + }, + "Male: 15 to 17 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 18 and 19 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 20 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 21 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 22 to 24 years": { + "value": 174, + "margin_of_error": 77, + "percentage": 0.095 + }, + "Male: 25 to 29 years": { + "value": 302, + "margin_of_error": 109, + "percentage": 0.165 + }, + "Male: 30 to 34 years": { + "value": 354, + "margin_of_error": 94, + "percentage": 0.193 + }, + "Male: 35 to 39 years": { + "value": 209, + "margin_of_error": 81, + "percentage": 0.114 + }, + "Male: 40 to 44 years": { + "value": 205, + "margin_of_error": 81, + "percentage": 0.112 + }, + "Male: 45 to 49 years": { + "value": 48, + "margin_of_error": 31, + "percentage": 0.026 + }, + "Male: 50 to 54 years": { + "value": 162, + "margin_of_error": 86, + "percentage": 0.088 + }, + "Male: 55 to 59 years": { + "value": 120, + "margin_of_error": 80, + "percentage": 0.065 + }, + "Male: 60 and 61 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 62 to 64 years": { + "value": 8, + "margin_of_error": 12, + "percentage": 0.004 + }, + "Male: 65 and 66 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 67 to 69 years": { + "value": 54, + "margin_of_error": 35, + "percentage": 0.029 + }, + "Male: 70 to 74 years": { + "value": 31, + "margin_of_error": 29, + "percentage": 0.017 + }, + "Male: 75 to 79 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 80 to 84 years": { + "value": 14, + "margin_of_error": 23, + "percentage": 0.008 + }, + "Male: 85 years and over": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female": { + "value": 1829, + "margin_of_error": 203, + "percentage": 0.499 + }, + "Female: Under 5 years": { + "value": 116, + "margin_of_error": 65, + "percentage": 0.063 + }, + "Female: 5 to 9 years": { + "value": 91, + "margin_of_error": 79, + "percentage": 0.05 + }, + "Female: 10 to 14 years": { + "value": 32, + "margin_of_error": 40, + "percentage": 0.017 + }, + "Female: 15 to 17 years": { + "value": 32, + "margin_of_error": 42, + "percentage": 0.017 + }, + "Female: 18 and 19 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 20 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 21 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 22 to 24 years": { + "value": 108, + "margin_of_error": 75, + "percentage": 0.059 + }, + "Female: 25 to 29 years": { + "value": 366, + "margin_of_error": 93, + "percentage": 0.2 + }, + "Female: 30 to 34 years": { + "value": 419, + "margin_of_error": 122, + "percentage": 0.229 + }, + "Female: 35 to 39 years": { + "value": 212, + "margin_of_error": 74, + "percentage": 0.116 + }, + "Female: 40 to 44 years": { + "value": 124, + "margin_of_error": 55, + "percentage": 0.068 + }, + "Female: 45 to 49 years": { + "value": 136, + "margin_of_error": 64, + "percentage": 0.074 + }, + "Female: 50 to 54 years": { + "value": 51, + "margin_of_error": 41, + "percentage": 0.028 + }, + "Female: 55 to 59 years": { + "value": 33, + "margin_of_error": 30, + "percentage": 0.018 + }, + "Female: 60 and 61 years": { + "value": 11, + "margin_of_error": 15, + "percentage": 0.006 + }, + "Female: 62 to 64 years": { + "value": 37, + "margin_of_error": 45, + "percentage": 0.02 + }, + "Female: 65 and 66 years": { + "value": 12, + "margin_of_error": 16, + "percentage": 0.007 + }, + "Female: 67 to 69 years": { + "value": 13, + "margin_of_error": 19, + "percentage": 0.007 + }, + "Female: 70 to 74 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 75 to 79 years": { + "value": 36, + "margin_of_error": 37, + "percentage": 0.02 + }, + "Female: 80 to 84 years": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 85 years and over": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + }, + "Sex": { + "meta": { + "table_id": "B01001", + "universe": "Total population" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "Male": { + "value": 1835, + "margin_of_error": 195, + "percentage": 0.501 + }, + "Female": { + "value": 1829, + "margin_of_error": 203, + "percentage": 0.499 + } + }, + "Race and ethnicity": { + "meta": { + "table_id": "B03002", + "universe": "Total population" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "Not Hispanic or Latino": { + "value": 3396, + "margin_of_error": 324, + "percentage": 0.927 + }, + "Not Hispanic or Latino: White alone": { + "value": 2556, + "margin_of_error": 330, + "percentage": 0.753 + }, + "Not Hispanic or Latino: Black or African American alone": { + "value": 93, + "margin_of_error": 71, + "percentage": 0.027 + }, + "Not Hispanic or Latino: American Indian and Alaska Native alone": { + "value": 21, + "margin_of_error": 26, + "percentage": 0.006 + }, + "Not Hispanic or Latino: Asian alone": { + "value": 573, + "margin_of_error": 175, + "percentage": 0.169 + }, + "Not Hispanic or Latino: Native Hawaiian and Other Pacific Islander alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Not Hispanic or Latino: Some other race alone": { + "value": 22, + "margin_of_error": 26, + "percentage": 0.006 + }, + "Not Hispanic or Latino: Two or more races": { + "value": 131, + "margin_of_error": 69, + "percentage": 0.039 + }, + "Not Hispanic or Latino: Two or more races: Two races including Some other race": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Not Hispanic or Latino: Two or more races: Two races excluding Some other race, and three or more races": { + "value": 131, + "margin_of_error": 69, + "percentage": 1 + }, + "Hispanic or Latino": { + "value": 268, + "margin_of_error": 128, + "percentage": 0.073 + }, + "Hispanic or Latino: White alone": { + "value": 244, + "margin_of_error": 120, + "percentage": 0.91 + }, + "Hispanic or Latino: Black or African American alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: American Indian and Alaska Native alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Asian alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Native Hawaiian and Other Pacific Islander alone": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Some other race alone": { + "value": 24, + "margin_of_error": 28, + "percentage": 0.09 + }, + "Hispanic or Latino: Two or more races": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Two or more races: Two races including Some other race": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Hispanic or Latino: Two or more races: Two races excluding Some other race, and three or more races": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + } + }, + "economics": { + "Number of households": { + "meta": { + "table_id": "B19001", + "universe": "Households" + }, + "Total": { + "value": 1999, + "margin_of_error": 87 + } + }, + "Median household income": { + "meta": { + "table_id": "B19013", + "universe": "Households" + }, + "Total": { + "value": 158656, + "margin_of_error": 9227 + } + }, + "Household income": { + "meta": { + "table_id": "B19001", + "universe": "Households" + }, + "Less than $10,000": { + "value": 24, + "margin_of_error": 26, + "percentage": 0.012 + }, + "$10,000 to $14,999": { + "value": 20, + "margin_of_error": 22, + "percentage": 0.01 + }, + "$15,000 to $19,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$20,000 to $24,999": { + "value": 18, + "margin_of_error": 26, + "percentage": 0.009 + }, + "$25,000 to $29,999": { + "value": 10, + "margin_of_error": 16, + "percentage": 0.005 + }, + "$30,000 to $34,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$35,000 to $39,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$40,000 to $44,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$45,000 to $49,999": { + "value": 49, + "margin_of_error": 55, + "percentage": 0.025 + }, + "$50,000 to $59,999": { + "value": 10, + "margin_of_error": 15, + "percentage": 0.005 + }, + "$60,000 to $74,999": { + "value": 87, + "margin_of_error": 49, + "percentage": 0.044 + }, + "$75,000 to $99,999": { + "value": 175, + "margin_of_error": 71, + "percentage": 0.088 + }, + "$100,000 to $124,999": { + "value": 275, + "margin_of_error": 100, + "percentage": 0.138 + }, + "$125,000 to $149,999": { + "value": 223, + "margin_of_error": 88, + "percentage": 0.112 + }, + "$150,000 to $199,999": { + "value": 461, + "margin_of_error": 111, + "percentage": 0.231 + }, + "$200,000 or more": { + "value": 647, + "margin_of_error": 142, + "percentage": 0.324 + } + } + }, + "families": { + "Household type by household": { + "meta": { + "table_id": "B11001", + "universe": "Households" + }, + "Total": { + "value": 1999, + "margin_of_error": 87 + }, + "Family households": { + "value": 689, + "margin_of_error": 138, + "percentage": 0.345 + }, + "Family households: Married-couple family": { + "value": 585, + "margin_of_error": 116, + "percentage": 0.849 + }, + "Family households: Other family": { + "value": 104, + "margin_of_error": 78, + "percentage": 0.151 + }, + "Family households: Other family: Male householder, no spouse present": { + "value": 51, + "margin_of_error": 67, + "percentage": 0.49 + }, + "Family households: Other family: Female householder, no spouse present": { + "value": 53, + "margin_of_error": 33, + "percentage": 0.51 + }, + "Nonfamily households": { + "value": 1310, + "margin_of_error": 139, + "percentage": 0.655 + }, + "Nonfamily households: Householder living alone": { + "value": 836, + "margin_of_error": 134, + "percentage": 0.638 + }, + "Nonfamily households: Householder not living alone": { + "value": 474, + "margin_of_error": 109, + "percentage": 0.362 + } + }, + "Household type by population": { + "meta": { + "table_id": "B11002", + "universe": "Population in Households" + }, + "Total": { + "value": 3664, + "margin_of_error": 320 + }, + "In family households": { + "value": 1799, + "margin_of_error": 379, + "percentage": 0.491 + }, + "In family households: In married-couple family": { + "value": 1539, + "margin_of_error": 323, + "percentage": 0.855 + }, + "In family households: In married-couple family: Relatives": { + "value": 1539, + "margin_of_error": 323, + "percentage": 1 + }, + "In family households: In married-couple family: Nonrelatives": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "In family households: In male householder, no spouse present, family": { + "value": 112, + "margin_of_error": 144, + "percentage": 0.062 + }, + "In family households: In male householder, no spouse present, family: Relatives": { + "value": 110, + "margin_of_error": 144, + "percentage": 0.982 + }, + "In family households: In male householder, no spouse present, family: Nonrelatives": { + "value": 2, + "margin_of_error": 5, + "percentage": 0.018 + }, + "In family households: In female householder, no spouse present, family": { + "value": 148, + "margin_of_error": 95, + "percentage": 0.082 + }, + "In family households: In female householder, no spouse present, family: Relatives": { + "value": 138, + "margin_of_error": 86, + "percentage": 0.932 + }, + "In family households: In female householder, no spouse present, family: Nonrelatives": { + "value": 10, + "margin_of_error": 16, + "percentage": 0.068 + }, + "In nonfamily households": { + "value": 1865, + "margin_of_error": 234, + "percentage": 0.509 + } + }, + "Marital status": { + "meta": { + "table_id": "B12001", + "universe": "Population 15 Years And Older" + }, + "Male": { + "value": 1681, + "margin_of_error": 162, + "percentage": 0.514 + }, + "Male: Never married": { + "value": 911, + "margin_of_error": 166, + "percentage": 0.542 + }, + "Male: Now married": { + "value": 672, + "margin_of_error": 135, + "percentage": 0.4 + }, + "Male: Now married: Married, spouse present": { + "value": 588, + "margin_of_error": 113, + "percentage": 0.875 + }, + "Male: Now married: Married, spouse absent": { + "value": 84, + "margin_of_error": 73, + "percentage": 0.125 + }, + "Male: Now married: Married, spouse absent: Separated": { + "value": 7, + "margin_of_error": 12, + "percentage": 0.083 + }, + "Male: Now married: Married, spouse absent: Other": { + "value": 77, + "margin_of_error": 72, + "percentage": 0.917 + }, + "Male: Widowed": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: Divorced": { + "value": 98, + "margin_of_error": 53, + "percentage": 0.058 + }, + "Female": { + "value": 1590, + "margin_of_error": 152, + "percentage": 0.486 + }, + "Female: Never married": { + "value": 827, + "margin_of_error": 129, + "percentage": 0.52 + }, + "Female: Now married": { + "value": 566, + "margin_of_error": 111, + "percentage": 0.356 + }, + "Female: Now married: Married, spouse present": { + "value": 553, + "margin_of_error": 109, + "percentage": 0.977 + }, + "Female: Now married: Married, spouse absent": { + "value": 13, + "margin_of_error": 19, + "percentage": 0.023 + }, + "Female: Now married: Married, spouse absent: Separated": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Now married: Married, spouse absent: Other": { + "value": 13, + "margin_of_error": 19, + "percentage": 1 + }, + "Female: Widowed": { + "value": 26, + "margin_of_error": 29, + "percentage": 0.016 + }, + "Female: Divorced": { + "value": 171, + "margin_of_error": 67, + "percentage": 0.108 + } + } + }, + "housing": { + "Number of housing units": { + "meta": { + "table_id": "B25002", + "universe": "Housing Units" + }, + "Total": { + "value": 2120, + "margin_of_error": 49 + } + }, + "Occupancy status": { + "meta": { + "table_id": "B25002", + "universe": "Housing Units" + }, + "Occupied": { + "value": 1999, + "margin_of_error": 87, + "percentage": 0.943 + }, + "Vacant": { + "value": 121, + "margin_of_error": 71, + "percentage": 0.057 + } + }, + "Ownership of occupied units": { + "meta": { + "table_id": "B25003", + "universe": "Occupied Housing Units" + }, + "Owner occupied": { + "value": 610, + "margin_of_error": 102, + "percentage": 0.305 + }, + "Renter occupied": { + "value": 1389, + "margin_of_error": 108, + "percentage": 0.695 + } + }, + "Units in structure": { + "meta": { + "table_id": "B25024", + "universe": "Housing Units" + }, + "1, detached unit": { + "value": 170, + "margin_of_error": 69, + "percentage": 0.08 + }, + "1, attached unit": { + "value": 90, + "margin_of_error": 31, + "percentage": 0.042 + }, + "2 units": { + "value": 27, + "margin_of_error": 43, + "percentage": 0.013 + }, + "3 or 4 units": { + "value": 46, + "margin_of_error": 56, + "percentage": 0.022 + }, + "5 to 9 units": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "10 to 19 unit": { + "value": 15, + "margin_of_error": 24, + "percentage": 0.007 + }, + "20 to 49 units": { + "value": 98, + "margin_of_error": 77, + "percentage": 0.046 + }, + "50 or more units": { + "value": 1674, + "margin_of_error": 109, + "percentage": 0.79 + }, + "Mobile home units": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Boat, RV, van, etc. units": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + }, + "Median value of owner-occupied housing units": { + "meta": { + "table_id": "B25077", + "universe": "Owner-Occupied Housing Units" + }, + "Total": { + "value": 624500, + "margin_of_error": 67024 + } + }, + "Value of owner-occupied housing units": { + "meta": { + "table_id": "B25075", + "universe": "Owner-Occupied Housing Units" + }, + "Less than $10,000": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$10,000 to $14,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$15,000 to $19,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$20,000 to $24,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$25,000 to $29,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$30,000 to $34,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$35,000 to $39,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$40,000 to $49,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$50,000 to $59,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$60,000 to $69,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$70,000 to $79,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$80,000 to $89,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$90,000 to $99,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$100,000 to $124,999": { + "value": 5, + "margin_of_error": 7, + "percentage": 0.008 + }, + "$125,000 to $149,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$150,000 to $174,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$175,000 to $199,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$200,000 to $249,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$250,000 to $299,999": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "$300,000 to $399,999": { + "value": 6, + "margin_of_error": 11, + "percentage": 0.01 + }, + "$400,000 to $499,999": { + "value": 174, + "margin_of_error": 94, + "percentage": 0.285 + }, + "$500,000 to $749,999": { + "value": 241, + "margin_of_error": 64, + "percentage": 0.395 + }, + "$750,000 to $999,999": { + "value": 93, + "margin_of_error": 51, + "percentage": 0.152 + }, + "$1,000,000 to $1,499,999": { + "value": 53, + "margin_of_error": 32, + "percentage": 0.087 + }, + "$1,500,000 to $1,999,999": { + "value": 38, + "margin_of_error": 23, + "percentage": 0.062 + }, + "$2,000,000 or more": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + } + }, + "social": { + "Population by minimum level of education": { + "meta": { + "table_id": "B15002", + "universe": "Population 25 Years And Over" + }, + "Total": { + "value": 2957, + "margin_of_error": 214 + }, + "Male": { + "value": 1507, + "margin_of_error": 154, + "percentage": 0.51 + }, + "Male: No schooling completed": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: Nursery to 4th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 5th and 6th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 7th and 8th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 9th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 10th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 11th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 12th grade, no diploma": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: High school graduate (includes equivalency)": { + "value": 48, + "margin_of_error": 36, + "percentage": 0.032 + }, + "Male: Some college, less than 1 year": { + "value": 17, + "margin_of_error": 18, + "percentage": 0.011 + }, + "Male: Some college, 1 or more years, no degree": { + "value": 46, + "margin_of_error": 34, + "percentage": 0.031 + }, + "Male: Associate's degree": { + "value": 36, + "margin_of_error": 28, + "percentage": 0.024 + }, + "Male: Bachelor's degree": { + "value": 540, + "margin_of_error": 163, + "percentage": 0.358 + }, + "Male: Master's degree": { + "value": 442, + "margin_of_error": 112, + "percentage": 0.293 + }, + "Male: Professional school degree": { + "value": 307, + "margin_of_error": 82, + "percentage": 0.204 + }, + "Male: Doctorate degree": { + "value": 71, + "margin_of_error": 40, + "percentage": 0.047 + }, + "Female": { + "value": 1450, + "margin_of_error": 152, + "percentage": 0.49 + }, + "Female: No schooling completed": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Nursery to 4th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 5th and 6th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 7th and 8th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 9th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 10th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 11th grade": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 12th grade, no diploma": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: High school graduate (includes equivalency)": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Some college, less than 1 year": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: Some college, 1 or more years, no degree": { + "value": 43, + "margin_of_error": 37, + "percentage": 0.03 + }, + "Female: Associate's degree": { + "value": 38, + "margin_of_error": 54, + "percentage": 0.026 + }, + "Female: Bachelor's degree": { + "value": 619, + "margin_of_error": 142, + "percentage": 0.427 + }, + "Female: Master's degree": { + "value": 425, + "margin_of_error": 106, + "percentage": 0.293 + }, + "Female: Professional school degree": { + "value": 253, + "margin_of_error": 70, + "percentage": 0.174 + }, + "Female: Doctorate degree": { + "value": 72, + "margin_of_error": 44, + "percentage": 0.05 + } + }, + "Population with veteran status": { + "meta": { + "table_id": "B21001", + "universe": "Civilian Population 18 Years And Over" + }, + "Total": { + "value": 3219, + "margin_of_error": 226 + }, + "Veteran": { + "value": 199, + "margin_of_error": 106, + "percentage": 0.062 + }, + "Nonveteran": { + "value": 3020, + "margin_of_error": 233, + "percentage": 0.938 + }, + "Male": { + "value": 1675, + "margin_of_error": 164, + "percentage": 0.52 + }, + "Male: Veteran": { + "value": 167, + "margin_of_error": 98, + "percentage": 0.1 + }, + "Male: Nonveteran": { + "value": 1508, + "margin_of_error": 185, + "percentage": 0.9 + }, + "Male: 18 to 34 years": { + "value": 828, + "margin_of_error": 148, + "percentage": 0.494 + }, + "Male: 18 to 34 years: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Male: 18 to 34 years: Nonveteran": { + "value": 828, + "margin_of_error": 148, + "percentage": 1 + }, + "Male: 35 to 54 years": { + "value": 620, + "margin_of_error": 129, + "percentage": 0.37 + }, + "Male: 35 to 54 years: Veteran": { + "value": 130, + "margin_of_error": 98, + "percentage": 0.21 + }, + "Male: 35 to 54 years: Nonveteran": { + "value": 490, + "margin_of_error": 91, + "percentage": 0.79 + }, + "Male: 55 to 64 years": { + "value": 128, + "margin_of_error": 78, + "percentage": 0.076 + }, + "Male: 55 to 64 years: Veteran": { + "value": 17, + "margin_of_error": 22, + "percentage": 0.133 + }, + "Male: 55 to 64 years: Nonveteran": { + "value": 111, + "margin_of_error": 72, + "percentage": 0.867 + }, + "Male: 65 to 74 years": { + "value": 85, + "margin_of_error": 54, + "percentage": 0.051 + }, + "Male: 65 to 74 years: Veteran": { + "value": 6, + "margin_of_error": 10, + "percentage": 0.071 + }, + "Male: 65 to 74 years: Nonveteran": { + "value": 79, + "margin_of_error": 52, + "percentage": 0.929 + }, + "Male: 75 years and over": { + "value": 14, + "margin_of_error": 23, + "percentage": 0.008 + }, + "Male: 75 years and over: Veteran": { + "value": 14, + "margin_of_error": 23, + "percentage": 1 + }, + "Male: 75 years and over: Nonveteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female": { + "value": 1544, + "margin_of_error": 153, + "percentage": 0.48 + }, + "Female: Veteran": { + "value": 32, + "margin_of_error": 34, + "percentage": 0.021 + }, + "Female: Nonveteran": { + "value": 1512, + "margin_of_error": 156, + "percentage": 0.979 + }, + "Female: 18 to 34 years": { + "value": 891, + "margin_of_error": 136, + "percentage": 0.577 + }, + "Female: 18 to 34 years: Veteran": { + "value": 22, + "margin_of_error": 31, + "percentage": 0.025 + }, + "Female: 18 to 34 years: Nonveteran": { + "value": 869, + "margin_of_error": 137, + "percentage": 0.975 + }, + "Female: 35 to 54 years": { + "value": 511, + "margin_of_error": 115, + "percentage": 0.331 + }, + "Female: 35 to 54 years: Veteran": { + "value": 10, + "margin_of_error": 15, + "percentage": 0.02 + }, + "Female: 35 to 54 years: Nonveteran": { + "value": 501, + "margin_of_error": 112, + "percentage": 0.98 + }, + "Female: 55 to 64 years": { + "value": 81, + "margin_of_error": 57, + "percentage": 0.052 + }, + "Female: 55 to 64 years: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 55 to 64 years: Nonveteran": { + "value": 81, + "margin_of_error": 57, + "percentage": 1 + }, + "Female: 65 to 74 years": { + "value": 25, + "margin_of_error": 35, + "percentage": 0.016 + }, + "Female: 65 to 74 years: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 65 to 74 years: Nonveteran": { + "value": 25, + "margin_of_error": 35, + "percentage": 1 + }, + "Female: 75 years and over": { + "value": 36, + "margin_of_error": 37, + "percentage": 0.023 + }, + "Female: 75 years and over: Veteran": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Female: 75 years and over: Nonveteran": { + "value": 36, + "margin_of_error": 37, + "percentage": 1 + } + }, + "Period of military service for veterans": { + "meta": { + "table_id": "B21002", + "universe": "Civilian Veterans 18 Years And Over" + }, + "Total": { + "value": 199, + "margin_of_error": 106 + }, + "Gulf War (9/2001 or later), no Gulf War (8/1990 to 8/2001), no Vietnam Era": { + "value": 78, + "margin_of_error": 50, + "percentage": 0.392 + }, + "Gulf War (9/2001 or later) and Gulf War (8/1990 to 8/2001), no Vietnam Era": { + "value": 14, + "margin_of_error": 15, + "percentage": 0.07 + }, + "Gulf War (9/2001 or later), and Gulf War (8/1990 to 8/2001), and Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Gulf War (8/1990 to 8/2001), no Vietnam Era": { + "value": 9, + "margin_of_error": 14, + "percentage": 0.045 + }, + "Gulf War (8/1990 to 8/2001) and Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Vietnam Era, no Korean War, no World War II": { + "value": 14, + "margin_of_error": 23, + "percentage": 0.07 + }, + "Vietnam Era and Korean War, no World War II": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Vietnam Era and Korean War and World War II": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Korean War, no Vietnam Era, no World War II": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Korean War and World War II, no Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "World War II, no Korean War, no Vietnam Era": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Between Gulf War and Vietnam Era only": { + "value": 78, + "margin_of_error": 82, + "percentage": 0.392 + }, + "Between Vietnam Era and Korean War only": { + "value": 6, + "margin_of_error": 10, + "percentage": 0.03 + }, + "Between Korean War and World War II only": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + }, + "Pre-World War II only": { + "value": 0, + "margin_of_error": 12, + "percentage": 0 + } + } + } + }, + "census": { + "2010": { + "census_year": 2010, + "state_fips": "51", + "county_fips": "51013", + "tract_code": "101801", + "block_code": "1004", + "block_group": "1", + "full_fips": "510131018011004", + "place": { + "name": "Arlington", + "fips": "5103000" + }, + "metro_micro_statistical_area": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47900", + "type": "metropolitan" + }, + "combined_statistical_area": { + "name": "Washington-Baltimore-Northern Virginia, DC-MD-VA-WV", + "area_code": "51548" + }, + "metropolitan_division": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47894" + }, + "source": "US Census Bureau" + }, + "2020": { + "census_year": 2020, + "state_fips": "51", + "county_fips": "51013", + "tract_code": "101801", + "block_code": "1004", + "block_group": "1", + "full_fips": "510131018011004", + "place": { + "name": "Arlington", + "fips": "5103000" + }, + "metro_micro_statistical_area": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47900", + "type": "metropolitan" + }, + "combined_statistical_area": { + "name": "Washington-Baltimore-Arlington, DC-MD-VA-WV-PA", + "area_code": "548" + }, + "metropolitan_division": { + "name": "Washington-Arlington-Alexandria, DC-VA-MD-WV", + "area_code": "47894" + }, + "source": "US Census Bureau" + } + }, + "congressional_districts": [ + { + "name": "Congressional District 8", + "district_number": 8, + "congress_number": "117th", + "congress_years": "2021-2023", + "proportion": 1, + "current_legislators": [ + { + "type": "representative", + "bio": { + "last_name": "Beyer", + "first_name": "Donald", + "birthday": "1950-06-20", + "gender": "M", + "party": "Democrat" + }, + "contact": { + "url": "https://beyer.house.gov", + "address": "1119 Longworth House Office Building Washington DC 20515-4608", + "phone": "202-225-4376", + "contact_form": null + }, + "social": { + "rss_url": null, + "twitter": "RepDonBeyer", + "facebook": "RepDonBeyer", + "youtube": null, + "youtube_id": "UCPJGVbOVcAVGiBwq8qr_T9w" + }, + "references": { + "bioguide_id": "B001292", + "thomas_id": "02272", + "opensecrets_id": "N00036018", + "lis_id": null, + "cspan_id": "21141", + "govtrack_id": "412657", + "votesmart_id": "1707", + "ballotpedia_id": "Don Beyer", + "washington_post_id": null, + "icpsr_id": "21554", + "wikipedia_id": "Don Beyer" + }, + "source": "Legislator data is originally collected and aggregated by https://github.com/unitedstates/" + }, + { + "type": "senator", + "bio": { + "last_name": "Warner", + "first_name": "Mark", + "birthday": "1954-12-15", + "gender": "M", + "party": "Democrat" + }, + "contact": { + "url": "https://www.warner.senate.gov", + "address": "703 Hart Senate Office Building Washington DC 20510", + "phone": "202-224-2023", + "contact_form": "http://www.warner.senate.gov/public/index.cfm?p=Contact" + }, + "social": { + "rss_url": "http://www.warner.senate.gov/public/?a=rss.feed", + "twitter": "MarkWarner", + "facebook": "MarkRWarner", + "youtube": "SenatorMarkWarner", + "youtube_id": "UCwyivNlEGf4sGd1oDLfY5jw" + }, + "references": { + "bioguide_id": "W000805", + "thomas_id": "01897", + "opensecrets_id": "N00002097", + "lis_id": "S327", + "cspan_id": "7630", + "govtrack_id": "412321", + "votesmart_id": "535", + "ballotpedia_id": "Mark Warner", + "washington_post_id": null, + "icpsr_id": "40909", + "wikipedia_id": "Mark Warner" + }, + "source": "Legislator data is originally collected and aggregated by https://github.com/unitedstates/" + }, + { + "type": "senator", + "bio": { + "last_name": "Kaine", + "first_name": "Timothy", + "birthday": "1958-02-26", + "gender": "M", + "party": "Democrat" + }, + "contact": { + "url": "https://www.kaine.senate.gov", + "address": "231 Russell Senate Office Building Washington DC 20510", + "phone": "202-224-4024", + "contact_form": "https://www.kaine.senate.gov/contact" + }, + "social": { + "rss_url": "http://www.kaine.senate.gov/rss/feeds/?type=all", + "twitter": null, + "facebook": "SenatorKaine", + "youtube": "SenatorTimKaine", + "youtube_id": "UC27LgTZlUnBQoNEQFZdn9LA" + }, + "references": { + "bioguide_id": "K000384", + "thomas_id": "02176", + "opensecrets_id": "N00033177", + "lis_id": "S362", + "cspan_id": "49219", + "govtrack_id": "412582", + "votesmart_id": "50772", + "ballotpedia_id": "Tim Kaine", + "washington_post_id": null, + "icpsr_id": "41305", + "wikipedia_id": "Tim Kaine" + }, + "source": "Legislator data is originally collected and aggregated by https://github.com/unitedstates/" + } + ] + } + ], + "riding": { + "code": "24068", + "name_french": "Saint-Laurent", + "name_english": "Saint-Laurent" + }, + "school_districts": { + "unified": { + "name": "Desert Sands Unified School District", + "lea_code": "11110", + "grade_low": "KG", + "grade_high": "12" + } + }, + "state_legislative_districts": { + "house": { + "name": "State House District 47", + "district_number": "47", + "is_upcoming_state_legislative_district": false + }, + "senate": { + "name": "State Senate District 31", + "district_number": "31", + "is_upcoming_state_legislative_district": false + } + }, + "timezone": { + "name": "America/New_York", + "utc_offset": -5, + "observes_dst": true, + "abbreviation": "EST", + "source": "© OpenStreetMap contributors" + } + } + } + ] +} diff --git a/timezone.go b/timezone.go index 6d47be5..6b9db4e 100644 --- a/timezone.go +++ b/timezone.go @@ -1,16 +1,6 @@ package geocodio // Timezone based on this payload -/* - "timezone": { - "name": "America/New_York", - "utc_offset": -5, - "observes_dst": true, - "abbreviation": "EST", - "source": "© OpenStreetMap contributors" - } -*/ - type Timezone struct { Name string `json:"name"` Abbreviation string `json:"abbreviation"` // v1.3+ diff --git a/zip.go b/zip.go index 5b71389..c6418a8 100644 --- a/zip.go +++ b/zip.go @@ -1,36 +1,8 @@ package geocodio // Zip4 based on this payload example -/* -{ - "record_type": { - "code": "S", - "description": "Street" - }, - "carrier_route": { - "id": "C007", - "description": "City Delivery" - }, - "building_or_firm_name": null, - "plus4": [ - "2890" - ], - "zip9": [ - "22201-2890" - ], - "government_building": null, - "facility_code": { - "code": "P", - "description": "Post Office" - }, - "city_delivery": true, - "valid_delivery_area": true, - "exact_match": true -} -*/ - type Zip4 struct { - RecodeType RecordType `json:"record_type,omitempty"` + RecordType RecordType `json:"record_type,omitempty"` CarrierRoute CarrierRoute `json:"carrier_route,omitempty"` BuildingOrFirmName string `json:"building_or_firm_name,omitempty"` Plus4 []string `json:"plus4,omitempty"`