CitySDK v2.0 (Beta)
Thank Yous Due to some very generous Clojurians:
- @thheller
- @cgrand
- @mfikes
Installation
npm install citysdk
citysdk
Function
The CitySDK v2.0 exports a single function, which takes two arguments:
- The first is an options object with a set of key/value pair parameters (See "Parameters" below)
- The second is a conventional (error, response) node-style callback, which will be called upon completion of the
census
function and applied to the response
Parameters
Brief overview of each argument parameter that can be passed into CitySDK v2.0
Parameter | Description | Geocodes | Stats | GeoJSON | GeoJSON with Stats |
---|---|---|---|---|---|
vintage |
Refers to the reference year (typically release year) of the data requested | ✔ | ✔ | ✔ | ✔ |
geoHierarchy |
The geographic scope and hierarchical path to the data requested | ✔ | ✔ | ✔ | ✔ |
sourcePath |
Refers to the Census survey and/or product from which your request should be filled | ✔ | ✔ | ||
values |
For Census statistics, values request counts/estimates by their unique identifier |
✔ | ✔ | ||
predicates |
Used as a filter available on some values with ranges denoted using :
|
✔*
|
✔*
|
||
statsKey |
You may request a key for Census' statistics API here | ✔**
|
✔**
|
||
geoResolution |
One of three available resolutions of GeoJSON ("20m" , "5m" , and "500k" ) |
✔ | ✔ |
*
: optional
**
: optional for < 500 requests daily
Geocoding (latitude/longitude -> FIPS code)
With the exception of "microdata" statistics (not yet available via Census' API), all Census data is aggregated to geographic areas of different sizes. As such, all of Census' API's require a set of/unique geographic identifier(s) to return any data (AKA: GEOIDs). Given that these identifiers are not common knowledge, the CitySDK provides a way for the user to identify their geographic scope of interest using a geographic coordinate (latitude + longitude).
Under the hood, this functionality calls the TigerWeb Web Mapping Service with the lat
& lng
provided and returns a JSON object that includes the appropriate GEOIDs for identifying your geographic area of interest. The signature of this returned object matches that of the geoHierarchy
parameter allowing it to be combined with other options.
There are two ways to scope your request using this functionality:
- Request a single geographic area by coordinate
- Request all of a descendant geography-type within a coordinate-specified geographic area
Method 1: Request a single geographic area by coordinate
RETURN TYPE: JSON
You may pass a {"lat" : <float>, "lng" : <float>}
object as the first and only subargument to a geography within the geoHierarchy
parameter:
import census from 'citysdk'
census({
"vintage" : 2015,
"geoHierarchy" : {
"county" : {
"lat" : 28.2639,
"lng" : -80.7214
}
}
},
(err, res) => console.log(res)
)
// -> {"vintage":"2015","geoHierarchy":{"state":"12","county":"009"}}
Notice how the function prepends an additional geographic component ("state" : "12"
) to the options object. This is the correct behavior. In this example, we requested a specific county using a coordinate. However, in order to fully qualify the geographic area (GEOID) associated with the county, the state is needed. In this example the fully qualified GEOID would be 12009
with the first two digits (12
) qualifying the state and 009
qualifying the county within that state. This appropriate geographic hierarchy creation is handled by the function for you.
Method 2: Request all of a descendant geography-type within a coordinate-specified geographic area
RETURN TYPE: JSON
import census from 'citysdk'
census({
"vintage" : "2015",
"geoHierarchy" : {
"state" : {
"lat" : 28.2639,
"lng" : -80.7214
},
"county" : "*" // <- syntax = "<descendant>" : "*"
}
},
(err, res) => console.log(res)
)
// -> {"vintage":"2015","geoHierarchy":{"state":"12","county":"*"}}
All Census-defined geographic areas are composed of Census "Blocks". Some of these composed areas - themselves - compose into higher-order areas. These nested relationships between certain geographic areas allows the Census data user to request all descendants of a particular type.
Caveats
- In this example, we added a second geographic level to our
geoHierarchy
object ("county" : "*"
). It is important to use the"*"
expression signifying that you want all of the specified level of descendants within the geography for which you supply a coordinate. No other expression will work. - Internally, the CitySDK converts the
geoHierarchy
object to an ordered set, so this part of your request object must be in descending hierarchical order from parent -> child/descendant. E.g. - in the above - an object that contained{"county" : "*", "state" : {"lat" <lat> "lng" <lng>}}
would not work.
Statistics
This parameter set will call the Census Statistics API and reformat the results with a couple highly requested features:
- Census statistics are returned as a standard JSON object rather than the csv-like format of the "naked" API
- Statistical values are translated into properly typed numbers (Integers and Floats instead of strings), whereas all values are returned as strings via the "naked" API
There are two ways to request Census statistics using census
:
- Calling for
values
of estimates and other statistical values - Apply a filter by using
predicates
"values"
by ID:
Method 1: get RETURN TYPE: JSON
import census from 'citysdk'
census({
"vintage" : "2015",
"geoHierarchy" : {
"county" : {
"lat" : 28.2639,
"lng" : -80.7214
}
},
"sourcePath" : ["cbp"], // required
"values" : ["ESTAB"] // required
},
(err, res) => console.log(res)
)
// -> [{"ESTAB":13648,"state":"12","county":"009"}]
Here, we added the parameters for sourcePath
(the path to the survey and/or source of the statistics) and values
(the identifiers of the statistics we're interested in). By including these parameters within your argument object, you trigger the census
function to get statistics. This "deploy on parameter set" strategy is how the census
function determines your intent.
🤔 Help for Discovering Census data
You're probably thinking: "How am I supposed to know what codes to use inside those parameters?" - or - "Where did that "cbp"
& "ESTAB"
stuff come from?" The data sets covered by the CitySDK are vast. As such, this is the steepest part of the learning curve. But, don't worry, there are a number of different resources available to assist you in your quest:
- The Census Developers' Microsite <- START HERE
- The Census Discovery Tool.
- Census Slack and Gitter developer communities.
- Data Experts
"values"
by ID:
Method 1: get RETURN TYPE: JSON
census({
"vintage" : "2015",
"geoHierarchy" : {
"county" : {
"lat" : 28.2639,
"lng" : -80.7214
}
},
"sourcePath" : ["cbp"], // required
"values" : ["ESTAB"], // required
"statsKey" : "<your key here>" // required for > 500 calls per day
},
(err, res) => console.log(res)
)
// -> [{"ESTAB":13648,"state":"12","county":"009"}]
predicates
:
Method 2: Filter results by RETURN TYPE: JSON
predicates
Predicates are used to create a sub-selection of statistical values based on a given range or categorical qualifyer.
census({
"vintage" : "2017",
"geoHierarchy" : {
"state" : "51",
"county" : "*"
},
"sourcePath" : ["acs", "acs1"],
"values" : ["NAME"],
"predicates" : {
"B01001_001E" : "0:100000" // number range separated by `:`
},
"statsKey" : "<your key here>"
},
(err, res) => console.log(res)
)
/* ->
[
{
"NAME":"Augusta County, Virginia",
"B01001_001E" : 75144,
"state":"51",
"county":"015"
},
{
"NAME":"Bedford County, Virginia",
"B01001_001E" : 77974,
"state":"51",
"county":"019"
},
...
]
*/
For some sources (e.g., the American Community Survey), most of the values
can also be used as predicates
, but are optional. In others, (e.g., International Trade), predicates
are a key part of the statistical query. In either case, at least one value within values
must be supplied.
Cartographic GeoJSON
You can also use the CitySDK to retrieve Cartographic Boundary files, which have been translated into GeoJSON. The only additional parameter you'll need to know is a simple declaration of geoResolution
of which there are three options:
Resolution | Map Scale | Benefits | Costs |
---|---|---|---|
500k | 1:500,000 | Greatest variety of summary levels & Most detailed | largest file sizes |
5m | 1:5,000,000 | Balance between size and detectable area size | lowest variety of available area types |
20m | 1:20,000,000 | Smallest file sizes | lowest level of detail |
See the full available Cartographic GeoJSON in the Geographic Area Types Availability by Vintage section
Example:
RETURN TYPE: JSON STRING
census({
"vintage" : "2017",
"geoHierarchy" : {
"metropolitan statistical area/micropolitan statistical area": "*"
},
"geoResolution" : "500k" // required
},
(err, res) => console.log(JSON.parse(res))
)
This query would actually return the raw
file associated with this URL:
or:
Notable Example:
census({
"vintage" : "2017",
"geoHierarchy" : {
"state": "51",
"county": "*"
},
"geoResolution" : "500k" // required
},
(err, res) => console.log(res)
)
It's important to note that - when querying for these GeoJSON files - you may retrieve a larger area than your request argument specifies. The reason for this is that the files are (currently) stored at two geographic levels: National and by State. Thus, the query above will attempt to resolve, at the state level, all counties, but because counties are stored at the national level in vintage 2017, all the counties in the US will be returned by this query.
If you wish to get back only those geographies you specify, you may do so by using the last and perhaps most useful feature included in the v2.0 release: Getting GeoJSON with statistics included within the "FeatureCollection"
properties
object!
GeoJSON Merged with Statistics
RETURN TYPE: JSON
There are a number of reasons you might want to merge your statistics into their GeoJSON/geographic boundaries, all of which are relevant when seeking to map Census data:
- Creating [choropleth] maps of statistics (e.g., using
values
) - Mapping only those geographies that meet a certain set of criteria (i.e., using
predicates
) - Showing a user their current Census geographic context (i.e., leveraging the Geocoding capabilities of CitySDK)
Dynamic Use Example
A more dynamic example of using stats merged with geojson on the fly with citysdk can be found here
=== INSERT IMAGE ===
TYPE IN A COUNTY AND SEE THE UNWEIGHTED SAMPLE COUNT OF ALL BLOCK GROUPS THEREIN (CHROME):
https://loganpowell.github.io/census-js-examples/examples/with-mapbox-gl_geocoding/index.html
source code: https://github.com/loganpowell/census-js-examples/tree/master/examples/with-mapbox-gl_geocoding
All Counties
census({
"vintage" : "2017",
"geoHierarchy" : {
"county": "*"
},
"sourcePath" : ["acs", "acs5"],
"values" : ["B19083_001E"], // GINI index
"statsKey" : "<your key here>",
"geoResolution" : "500k"
},
)
In this example, we use citysdk
to create the payload and then save it via Nodes fs.writeFileSync
and then serve it via a Mapbox-GL map.
=== INSERT IMAGE ===
https://loganpowell.github.io/census-js-examples/examples/counties_static/index.html
source code: https://github.com/loganpowell/census-js-examples/tree/master/examples/counties_static
Notable Example:
All ZCTAs (zip code tabulation areas in the US)
census({
"vintage" : "2017",
"geoHierarchy" : {
"zip-code-tabulation-area" : "*"
},
"sourcePath" : ["acs", "acs5"],
"values" : ["B19083_001E"], // GINI index
"statsKey" : "<your key here>",
"geoResolution" : "500k"
},
)
This is a very large request, in fact, one of the largest you could possibly make in a single citysdk
function call. It is so large, in fact that it currently only works on Node and only if you increase your node --max-old-space-size=4096
. With large merges (such as all counties or zctas), it is recommended not to try to use citysdk dynamically, but - rather - to munge your geojson before hand and then serve it statically to your mapping library, as was done here:
=== INSERT IMAGE ===
https://loganpowell.github.io/census-js-examples/examples/zip-code-tabulation-areas_static/index.html source code: https://github.com/loganpowell/census-js-examples/tree/master/examples/zip-code-tabulation-areas_static
Other Argument Examples:
// Call the WMS only
{
vintage: 2014,
geoHierarchy: { state: { lat: 28.2639, lng: -80.7214 }, county: '*' }
}
// Getting the stats for a single county
{
vintage: 2016,
geoHierarchy: { county: { lat: 28.2639, lng: -80.7214 } },
sourcePath: [ 'acs', 'acs5' ],
predicates: { B00001_001E: '0:1000000' },
values: [ 'B01001_001E' ]
}
// strings are valid as vintages as well
{
vintage: '2015',
geoHierarchy: { county: { lat: 28.2639, lng: -80.7214 } },
sourcePath: [ 'cbp' ],
values: [ 'ESTAB' ]
}
// Just geojson for all the counties within a state located by a given coordinate
{
vintage: 2014,
geoHierarchy: { state: { lat: 28.2639, lng: -80.7214 }, county: '*' },
geoResolution: '500k'
}
// For large request expect to have to increase `node --max-old-space-size=4096`
{
vintage: 2016,
sourcePath: [ 'acs', 'acs5' ],
values: [ 'B25001_001E' ],
geoHierarchy: { 'zip-code-tabulation-area': '*' },
geoResolution: '500k'
}
Census Cartography Files in GeoJSON Format
The Census Bureau publishes both high and low accuracy geographic area files to accommodate the widest possible variety of user needs (within feasibility). Cartography Files are simplified representations of selected geographic areas from the Census Bureau’s Master Address File/Topologically Integrated Geographic Encoding and Referencing (MAF/TIGER) system. These boundary files are specifically designed for small scale thematic mapping (i.e., for visualizations).
For a while now, we have published our cartography files in the .shp
format. More recently, we expanded our portfolio of available formats to .kml
. It is with this release that we follow suit with the community at large to release these boundaries in .json
(GeoJSON) format.
Geographic Area Types Availability by Vintage
The most comprehensive set of geographies and vintages can be found within the 500k set.
Some vintages - 103
through 110
- are references to sessions of Congress and only contain a single geographic summary level: congressional-district
The following tables represent the availability of various geographic summary levels through the remaining vintages:
Geographic Area Type | 1990 | 2000 | 2010 | 2012 | 2013 - 2015 | 2016 - 2017 |
---|---|---|---|---|---|---|
us |
✔ | ✔ | ✔ | |||
region |
✔ | ✔ | ✔ | ✔ | ||
division |
✔ | ✔ | ✔ | ✔ | ||
state |
✔ | ✔ | ✔ | ✔ | ✔ | |
county |
✔ | ✔ | ✔ | ✔ | ✔ | |
consolidated cities |
✔ | ✔ | ✔ | ✔ | ||
county subdivision |
✔ | ✔ | ✔ | ✔ | ✔ | |
tract |
✔ | ✔ | ✔ | ✔ | ✔ | |
place |
✔ | ✔ | ✔ | ✔ | ✔ | |
alaska native regional corporation |
✔ | ✔ | ✔ | ✔ | ✔ | |
american indian-area/alaska native area/hawaiian home land |
✔ | ✔ | ✔ | ✔ | ✔ | |
metropolitan statistical area/micropolitan statistical area |
✔ | ✔ | ✔ | |||
combined statistical area |
✔ | ✔ | ✔ | |||
new england city and town area |
✔ | ✔ | ✔ | |||
combined new england city and town area |
✔ | ✔ | ||||
urban area |
✔ | ✔ | ✔ | ✔ | ✔ | |
congressional district |
✔ | ✔ | ✔ | ✔ | ||
school district (elementary) |
✔ | ✔ | ✔ | |||
school district (secondary) |
✔ | ✔ | ✔ | |||
school district (unified) |
✔ | ✔ | ✔ | |||
block group |
✔ | ✔ | ✔ | ✔ | ✔ | |
public use microdata area |
✔ | ✔ | ||||
zip code tabulation area |
✔ | ✔ | ✔ | |||
state legislative district (upper chamber) |
✔ | ✔ | ✔ | ✔ | ✔ | |
state legislative district (lower chamber) |
✔ | ✔ | ✔ | ✔ | ✔ |
More Information
- For more information about the files translated herein please visit the Census Bureau's Cartographic Boundary File Description Page
- For a comparison of the available formats of geographic area files, please visit the Census Bureau's TIGER Products page
Community
- Join us on Gitter
- Join us on Slack @ the
#cartography
channel. - Send us an email: cnmp.developers.list@census.gov
Dedicated Data Experts
- Ryan Dolan: ryan.s.dolan@census.gov
- Gerson Vasquez: gerson.d.vasquez@census.gov
- Alexandra Barker: alexandra.s.barker@census.gov