Geo Django
GeoDjango integration: models, services, management commands, and serializers for the spatial platform.
GeoDjango integration for temporal geographic feature data.
This module provides Django models for storing and querying geographic boundaries with full GeoDjango spatial support and temporal validity tracking.
- Requirements:
Django >= 4.2
djangorestframework-gis >= 1.0
PostGIS database
- Usage:
Add ‘siege_utilities.geo.django’ to INSTALLED_APPS.
Example
>>> from siege_utilities.geo.django.models import State, County, Tract
>>> from siege_utilities.geo.django.services import BoundaryPopulationService
# Fetch boundaries containing a point >>> from django.contrib.gis.geos import Point # doctest: +SKIP >>> point = Point(-122.4194, 37.7749, srid=4326) # doctest: +SKIP >>> tract = Tract.objects.filter(geometry__contains=point).first() # doctest: +SKIP
# Populate boundaries from TIGER/Line >>> service = BoundaryPopulationService() # doctest: +SKIP >>> service.populate_counties(year=2020, state_fips=’06’) # doctest: +SKIP
- siege_utilities.geo.django.get_model(model_name)[source]
Lazily import a model by name.
- Parameters:
model_name (str) – Name of the model (e.g., ‘State’, ‘County’, ‘Tract’)
- Returns:
The Django model class
- Raises:
ImportError – If Django/GeoDjango is not available
ValueError – If the model name is not recognized
- siege_utilities.geo.django.get_service(service_name)[source]
Lazily import a service by name.
- Parameters:
service_name (str) – Name of the service (e.g., ‘BoundaryPopulationService’)
- Returns:
The service class
- Raises:
ImportError – If Django/GeoDjango is not available
Models
Abstract base models for temporal geographic features.
- Hierarchy:
TemporalGeographicFeature (root abstract — no geometry, pure temporal identity) ├── TemporalBoundary (abstract — MultiPolygon, area-based features) │ └── CensusTIGERBoundary (abstract — GEOID, FIPS codes, TIGER metadata) ├── TemporalLinearFeature (abstract — MultiLineString, e.g. roads, rivers) └── TemporalPointFeature (abstract — Point, e.g. addresses, facilities)
- class siege_utilities.geo.django.models.base.CensusTIGERBoundary[source]
Bases:
TemporalBoundaryAbstract model for US Census TIGER/Line boundaries.
Adds GEOID-based identity and TIGER-specific metadata fields that are common to ALL Census geography types (states, counties, tracts, etc.).
The geoid is the authoritative identifier; feature_id and boundary_id are automatically synced from it in save().
- GEOID Format by Geography Type:
State: 2 digits (e.g., “06” for California) County: 5 digits = state (2) + county (3) (e.g., “06037”) Tract: 11 digits = state (2) + county (3) + tract (6) Block Group: 12 digits = tract (11) + block group (1) Block: 15 digits = tract (11) + block (4) Place: 7 digits = state (2) + place (5) ZCTA: 5 digits CD: 4 digits = state (2) + district (2)
- class siege_utilities.geo.django.models.base.TemporalBoundary[source]
Bases:
TemporalGeographicFeatureAbstract model for area/polygon-based geographic features.
All boundary types (Census, GADM, NLRB regions, judicial districts, etc.) share these fields. The geometry is always stored as MultiPolygon in WGS 84 (SRID 4326) for maximum interoperability.
- Fields inherited from TemporalGeographicFeature:
feature_id, name, vintage_year, valid_from, valid_to, source
- Fields added here:
- boundary_id: Alias for feature_id, kept for readability in boundary contexts.
Automatically synced from feature_id in save().
geometry: MultiPolygon, SRID 4326. area_land / area_water: Area in square meters (from TIGER ALAND/AWATER). internal_point: Representative point for labeling (INTPTLAT/INTPTLON).
- class siege_utilities.geo.django.models.base.TemporalGeographicFeature[source]
Bases:
ModelRoot abstract model for all temporal geographic features.
Every geographic feature — whether a polygon boundary, a road centerline, or a point address — has a temporal dimension: it was valid during some period and belongs to some vintage/edition of its source dataset.
This base provides the identity, temporal, and metadata fields shared by ALL geographic feature types. Geometry is intentionally omitted — each geometry-type subclass (TemporalBoundary, TemporalLinearFeature, TemporalPointFeature) adds the appropriate geometry field.
- Fields:
- feature_id: Stable identifier unique within (feature_id, vintage_year).
Polygons sync this from geoid; points from address hash; etc.
name: Human-readable label. vintage_year: The edition year of the source dataset (e.g. 2020 TIGER). valid_from / valid_to: Date range during which this feature was in effect.
NULL means open-ended (still valid / always valid).
source: Provenance string (e.g. “TIGER/Line”, “GADM”, “OpenStreetMap”).
- class siege_utilities.geo.django.models.base.TemporalLinearFeature[source]
Bases:
TemporalGeographicFeatureAbstract model for linear geographic features (roads, rivers, rail lines).
These features have temporal validity — a highway may not have existed before a certain date, a road may be reclassified, a rail line may be decommissioned.
Concrete subclasses (future): Road, RailLine, Waterway, etc.
- class siege_utilities.geo.django.models.base.TemporalPointFeature[source]
Bases:
TemporalGeographicFeatureAbstract model for point geographic features (addresses, facilities, landmarks).
These features have temporal validity — a building may be demolished, a new address may be created, a facility may relocate.
Concrete subclasses (future): Address, Facility, Landmark, etc.
Concrete Census TIGER/Line boundary models.
Each model represents a specific Census geography type with appropriate parent relationships and GEOID parsing. All inherit from CensusTIGERBoundary, which provides: geoid, state_fips, lsad, mtfcc, funcstat, vintage_year, valid_from/valid_to, geometry, area_land, area_water, internal_point, source.
- class siege_utilities.geo.django.models.boundaries.Block[source]
Bases:
CensusTIGERBoundaryCensus Block boundary.
GEOID: 15 digits = state (2) + county (3) + tract (6) + block (4) Example: “060371011001001” for block 1001 in tract 1011.00, LA County, CA
Note: Blocks are very numerous (millions per state). Consider using a separate table or partitioning for large-scale deployments.
- block_group_ref
alias of
BlockGroup
- class Meta[source]
Bases:
object- verbose_name = 'Census Block'
- verbose_name_plural = 'Census Blocks'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- classmethod get_geoid_length()[source]
Return the expected GEOID length for this geography type.
- Return type:
- class siege_utilities.geo.django.models.boundaries.BlockGroup[source]
Bases:
CensusTIGERBoundaryCensus Block Group boundary.
GEOID: 12 digits = state (2) + county (3) + tract (6) + block group (1) Example: “060371011001” for block group 1 in tract 1011.00, LA County, CA
- class Meta[source]
Bases:
object- verbose_name = 'Block Group'
- verbose_name_plural = 'Block Groups'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- class siege_utilities.geo.django.models.boundaries.CongressionalDistrict[source]
Bases:
CensusTIGERBoundaryCongressional District boundary.
GEOID: 4 digits = state (2) + district (2) Example: “0614” for California’s 14th district
Note: District numbers change after reapportionment (every 10 years). Use vintage_year to track which Congress the districts represent.
- class Meta[source]
Bases:
object- verbose_name = 'Congressional District'
- verbose_name_plural = 'Congressional Districts'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- classmethod get_geoid_length()[source]
Return the expected GEOID length for this geography type.
- Return type:
- class siege_utilities.geo.django.models.boundaries.County[source]
Bases:
CensusTIGERBoundaryCounty or county-equivalent boundary.
GEOID: 5 digits = state (2) + county (3) Example: “06037” for Los Angeles County, CA
- class Meta[source]
Bases:
object- verbose_name = 'County'
- verbose_name_plural = 'Counties'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- class siege_utilities.geo.django.models.boundaries.Place[source]
Bases:
CensusTIGERBoundaryCensus Place (city, town, CDP) boundary.
GEOID: 7 digits = state (2) + place (5) Example: “0644000” for Los Angeles city, CA
- class Meta[source]
Bases:
object- verbose_name = 'Place'
- verbose_name_plural = 'Places'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- class siege_utilities.geo.django.models.boundaries.State[source]
Bases:
CensusTIGERBoundaryUS State or Territory boundary.
GEOID: 2 digits (state FIPS code) Example: “06” for California
- class Meta[source]
Bases:
object- verbose_name = 'State'
- verbose_name_plural = 'States'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- class siege_utilities.geo.django.models.boundaries.Tract[source]
Bases:
CensusTIGERBoundaryCensus Tract boundary.
GEOID: 11 digits = state (2) + county (3) + tract (6) Example: “06037101100” for tract 1011.00 in LA County, CA
- class Meta[source]
Bases:
object- verbose_name = 'Census Tract'
- verbose_name_plural = 'Census Tracts'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- classmethod get_geoid_length()[source]
Return the expected GEOID length for this geography type.
- Return type:
- property urbanicity_category
NCES locale category (city, suburban, town, rural) or None.
- property urbanicity_subcategory
NCES locale subcategory (e.g., city_large, rural_remote) or None.
- class siege_utilities.geo.django.models.boundaries.ZCTA[source]
Bases:
CensusTIGERBoundaryZIP Code Tabulation Area boundary.
GEOID: 5 digits (the ZCTA code, similar to ZIP) Example: “90210” for Beverly Hills area
Note: ZCTAs approximate but do not exactly match USPS ZIP codes. They are defined by Census blocks and may cross state lines.
- class Meta[source]
Bases:
object- verbose_name = 'ZCTA'
- verbose_name_plural = 'ZCTAs'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
Extended Census boundary models — non-nesting geographies.
CBSA, UrbanArea, PUMA, TribalArea, and CountySubdivision are Census-defined boundaries that either don’t nest in the standard state→county→tract hierarchy or belong to parallel hierarchy branches. All inherit from CensusTIGERBoundary.
- class siege_utilities.geo.django.models.census_extended.CBSA[source]
Bases:
CensusTIGERBoundaryCore Based Statistical Area (Metropolitan/Micropolitan).
CBSAs are defined by OMB around urban cores of 10,000+ population. They consist of one or more counties (or county-equivalents). CBSAs do NOT nest in the Census hierarchy — they can span states.
GEOID: 5 digits (CBSA FIPS code) Example: “31080” for Los Angeles-Long Beach-Anaheim, CA
- CBSA_TYPE_CHOICES = [('metro', 'Metropolitan Statistical Area'), ('micro', 'Micropolitan Statistical Area')]
- class Meta[source]
Bases:
object- verbose_name = 'CBSA'
- verbose_name_plural = 'CBSAs'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index, django.db.models.Index]
- class siege_utilities.geo.django.models.census_extended.UrbanArea[source]
Bases:
CensusTIGERBoundaryCensus Urban Area boundary.
Urban Areas represent densely developed territory. Two types: - Urbanized Area (UA): 50,000+ population - Urban Cluster (UC): 2,500–49,999 population
Like CBSAs, Urban Areas can span county and state boundaries.
GEOID: 5 digits (Urban Area code) Example: “51445” for Los Angeles—Long Beach—Anaheim, CA UA
- UA_TYPE_CHOICES = [('UA', 'Urbanized Area (50,000+)'), ('UC', 'Urban Cluster (2,500-49,999)')]
- class Meta[source]
Bases:
object- verbose_name = 'Urban Area'
- verbose_name_plural = 'Urban Areas'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- class siege_utilities.geo.django.models.census_extended.PUMA[source]
Bases:
CensusTIGERBoundaryPublic Use Microdata Area boundary.
PUMAs are non-overlapping, statistical geographic areas of contiguous counties or census tracts containing ~100,000+ people. Used by ACS public-use microdata samples (PUMS) and small-area estimates.
GEOID: 7 digits = state (2) + PUMA (5) Example: “0603701” for PUMA 03701 in California (Los Angeles area)
- class Meta[source]
Bases:
object- verbose_name = 'PUMA'
- verbose_name_plural = 'PUMAs'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- class siege_utilities.geo.django.models.census_extended.TribalArea[source]
Bases:
CensusTIGERBoundaryAmerican Indian / Alaska Native / Native Hawaiian Area boundary.
TIGER layer: tl_YYYY_us_aiannh (national file, not per-state). GEOIDs vary in length (typically 4–5 digits); no fixed-length constraint. Tribal areas can span state boundaries.
GEOID example: “0010” for Agua Caliente Indian Reservation
- class Meta[source]
Bases:
object- verbose_name = 'Tribal Area'
- verbose_name_plural = 'Tribal Areas'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- class siege_utilities.geo.django.models.census_extended.CountySubdivision[source]
Bases:
CensusTIGERBoundaryCounty Subdivision (MCD/CCD) boundary.
Minor Civil Divisions (MCDs) or Census County Divisions (CCDs) are the primary legal or statistical subdivision of a county. MCDs are used in 28 states + DC; CCDs fill in where MCDs don’t exist.
TIGER layer: tl_YYYY_SS_cousub GEOID: 10 digits = state (2) + county (3) + cousub (5) Example: “2500109000” for Abington town, Plymouth County, MA
- class Meta[source]
Bases:
object- verbose_name = 'County Subdivision'
- verbose_name_plural = 'County Subdivisions'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
Demographic data models for Census boundaries.
These models store demographic snapshots that can be attached to any Census boundary type using Django’s contenttypes framework.
- class siege_utilities.geo.django.models.demographics.DemographicVariable[source]
Bases:
ModelReference table for Census variables.
Stores metadata about Census variables for documentation and validation purposes.
- DATASET_CHOICES = [('acs5', 'American Community Survey 5-Year'), ('acs1', 'American Community Survey 1-Year'), ('dec', 'Decennial Census'), ('dec_pl', 'Decennial Census P.L. 94-171')]
- class siege_utilities.geo.django.models.demographics.DemographicSnapshot[source]
Bases:
ModelDemographic data snapshot for a Census boundary.
Uses generic foreign key to attach to any boundary type. Stores values as JSON to allow flexible variable sets.
Example
>>> snapshot = DemographicSnapshot.objects.create( ... content_object=tract, ... year=2022, ... dataset='acs5', ... values={'B01001_001E': 4521, 'B19013_001E': 75000}, ... moe_values={'B01001_001E': 150, 'B19013_001E': 5000} ... )
- DATASET_CHOICES = [('acs5', 'American Community Survey 5-Year'), ('acs1', 'American Community Survey 1-Year'), ('dec', 'Decennial Census'), ('dec_pl', 'Decennial Census P.L. 94-171')]
- content_object = 'content_type'
- class Meta[source]
Bases:
object- verbose_name = 'Demographic Snapshot'
- verbose_name_plural = 'Demographic Snapshots'
- unique_together = [('content_type', 'object_id', 'year', 'dataset')]
- indexes = [django.db.models.Index, django.db.models.Index, django.db.models.Index]
- get_value(variable_code, default=None)[source]
Get a specific variable value.
- Parameters:
variable_code (str) – Census variable code (e.g., ‘B01001_001E’)
default – Value to return if variable not present
- Returns:
The variable value or default
- class siege_utilities.geo.django.models.demographics.DemographicTimeSeries[source]
Bases:
ModelPre-computed time series for frequently-accessed demographic trends.
This model stores aggregated time series data for performance, avoiding repeated joins across multiple DemographicSnapshot records.
- content_object = 'content_type'
NCES education models.
School district boundaries from Census TIGER/Line, enhanced with NCES locale classification data (12-code urban-centric system). Plus NCES-specific models for locale territory polygons and school locations.
School districts inherit from CensusTIGERBoundary. NCESLocaleBoundary inherits from TemporalBoundary (not Census-specific). SchoolLocation inherits from TemporalPointFeature.
- class siege_utilities.geo.django.models.education.SchoolDistrictBase[source]
Bases:
CensusTIGERBoundaryAbstract base for NCES school district boundaries.
Adds LEA (Local Education Agency) identifier and NCES locale classification fields. Locale codes are denormalized here for query performance — the authoritative classification logic lives in siege_utilities.geo.locale.NCESLocaleClassifier.
GEOID: 7 digits = state (2) + LEA (5)
- class siege_utilities.geo.django.models.education.SchoolDistrictElementary[source]
Bases:
SchoolDistrictBaseElementary school district boundary.
- class siege_utilities.geo.django.models.education.SchoolDistrictSecondary[source]
Bases:
SchoolDistrictBaseSecondary school district boundary.
- class siege_utilities.geo.django.models.education.SchoolDistrictUnified[source]
Bases:
SchoolDistrictBaseUnified school district boundary (serves all grades).
- class siege_utilities.geo.django.models.education.NCESLocaleBoundary[source]
Bases:
TemporalBoundaryNCES locale territory polygon.
NCES publishes 12 locale territory polygons per year — one for each code (11=City-Large through 43=Rural-Remote). These are pre-classified boundaries that can be used for spatial joins without recomputing.
Not a CensusTIGERBoundary because these are NCES-published, not TIGER.
- class siege_utilities.geo.django.models.education.SchoolLocation[source]
Bases:
TemporalPointFeatureIndividual school geocoded point location.
Downloaded from NCES EDGE school location data. Each row represents a school with its geocoded coordinates and NCES locale classification.
Federal boundary models (non-Census).
These models inherit directly from TemporalBoundary (not CensusTIGERBoundary) because they don’t use Census GEOIDs or TIGER metadata.
- class siege_utilities.geo.django.models.federal.NLRBRegion[source]
Bases:
TemporalBoundaryNational Labor Relations Board regional boundary.
The NLRB divides the US into 26 regions (numbered, with some gaps). Boundaries change infrequently but do shift over time.
- class siege_utilities.geo.django.models.federal.FederalJudicialDistrict[source]
Bases:
TemporalBoundaryUS Federal Judicial District boundary.
94 districts organized into 12 regional circuits (plus DC and Federal). District boundaries follow state lines and sometimes county lines.
GADM (Global Administrative Areas Database) boundary models.
Six-level administrative hierarchy from country through Admin5. All inherit from TemporalBoundary (NOT CensusTIGERBoundary, since GADM doesn’t use Census GEOIDs).
Preserves the enterprise dual-FK pattern: each child has both a Django FK to the parent model and a string gid_N field for parallel bulk loading (FK resolved after load via populate_parent_relationships()).
- class siege_utilities.geo.django.models.gadm.GADMBoundary[source]
Bases:
TemporalBoundaryAbstract base for all GADM boundaries.
GADM uses its own identifier scheme: GID_0 through GID_5. Source defaults to “GADM”.
- class siege_utilities.geo.django.models.gadm.GADMCountry[source]
Bases:
GADMBoundaryGADM Level 0 — Country.
Example: GID_0 = “USA”, NAME_0 = “United States”
- class siege_utilities.geo.django.models.gadm.GADMAdmin1[source]
Bases:
GADMBoundaryGADM Level 1 — First-level subdivision (state, province, region).
Example: GID_1 = “USA.5_1”, NAME_1 = “California”
- country
alias of
GADMCountry
- class siege_utilities.geo.django.models.gadm.GADMAdmin2[source]
Bases:
GADMBoundaryGADM Level 2 — Second-level subdivision (county, department, district).
Example: GID_2 = “USA.5.37_1”, NAME_2 = “Los Angeles”
- admin1
alias of
GADMAdmin1
- class siege_utilities.geo.django.models.gadm.GADMAdmin3[source]
Bases:
GADMBoundaryGADM Level 3 — Third-level subdivision.
Example: GID_3 = “USA.5.37.1_1”
- admin2
alias of
GADMAdmin2
- class siege_utilities.geo.django.models.gadm.GADMAdmin4[source]
Bases:
GADMBoundaryGADM Level 4 — Fourth-level subdivision.
- admin3
alias of
GADMAdmin3
- class siege_utilities.geo.django.models.gadm.GADMAdmin5[source]
Bases:
GADMBoundaryGADM Level 5 — Fifth-level subdivision (finest granularity).
- admin4
alias of
GADMAdmin4
Boundary intersection models — pre-computed spatial overlaps.
Generic BoundaryIntersection stores any pair of overlapping boundaries. Typed convenience models (CountyCDIntersection, VTDCDIntersection, TractCDIntersection) add FK constraints and dominant-boundary tracking for the most commonly-queried intersection pairs.
- class siege_utilities.geo.django.models.intersections.BoundaryIntersection[source]
Bases:
ModelGeneric intersection between any two boundary types.
Records the area overlap (intersection geometry is NOT stored — too expensive for millions of rows; use PostGIS ST_Intersection on the fly). Source/target types are string-typed for maximum flexibility.
Example
- BoundaryIntersection(
source_type=’county’, source_boundary_id=’06037’, target_type=’cd’, target_boundary_id=’0614’, vintage_year=2020, intersection_area=12345678, pct_of_source=0.45, pct_of_target=0.72,
)
- class Meta[source]
Bases:
object- verbose_name = 'Boundary Intersection'
- verbose_name_plural = 'Boundary Intersections'
- unique_together = [('source_type', 'source_boundary_id', 'target_type', 'target_boundary_id', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index, django.db.models.Index, django.db.models.Index]
- class siege_utilities.geo.django.models.intersections.CountyCDIntersection[source]
Bases:
ModelPre-computed County ↔ Congressional District intersection.
Typed model with FKs for the most commonly-queried pair. Matches the enterprise pattern: pct_of_county, pct_of_cd, is_dominant.
- congressional_district
alias of
CongressionalDistrict
- class siege_utilities.geo.django.models.intersections.VTDCDIntersection[source]
Bases:
ModelPre-computed VTD ↔ Congressional District intersection.
- congressional_district
alias of
CongressionalDistrict
- class siege_utilities.geo.django.models.intersections.TractCDIntersection[source]
Bases:
ModelPre-computed Tract ↔ Congressional District intersection.
- congressional_district
alias of
CongressionalDistrict
Isochrone result model.
Stores computed isochrone polygons (travel-time contours) in PostGIS for caching, spatial joins, and historical analysis. Each record represents a single isochrone polygon for a given origin point, travel time, profile, and provider.
- class siege_utilities.geo.django.models.isochrone.IsochroneResult[source]
Bases:
TemporalBoundaryCached isochrone polygon from an external routing provider.
Avoids repeated API calls by storing computed isochrones in PostGIS. Supports spatial queries like “which tracts are within 15 min of X?”
Data source: OpenRouteService, Valhalla, or other isochrone providers.
Example
>>> iso = IsochroneResult.objects.get( ... travel_minutes=15, ... profile="driving-car", ... ) >>> iso.geometry # MultiPolygon of the 15-min driving contour
NLRB case, election, ULP, and bargaining unit Django models.
Non-spatial models for NLRB case data. These link to NLRBRegion via FK but don’t carry their own geometry.
- class siege_utilities.geo.django.models.nlrb_cases.NLRBCase[source]
Bases:
ModelAn NLRB case (representation petition, ULP charge, etc.).
- region = 'NLRBRegion'
- class siege_utilities.geo.django.models.nlrb_cases.ElectionResult[source]
Bases:
ModelElection tally result for an NLRB representation case.
- class siege_utilities.geo.django.models.nlrb_cases.ULPCharge[source]
Bases:
ModelUnfair labor practice charge filed with the NLRB.
- class siege_utilities.geo.django.models.nlrb_cases.BargainingUnit[source]
Bases:
ModelBargaining unit description with industry/occupation codes.
Political Census boundary models.
State legislative districts (upper/lower chambers), voting tabulation districts (VTDs), and precincts. All inherit from CensusTIGERBoundary.
- class siege_utilities.geo.django.models.political.StateLegislativeUpper[source]
Bases:
CensusTIGERBoundaryState Legislative District — Upper Chamber (Senate).
GEOID: 5 digits = state (2) + district (3) Example: “06001” for California State Senate District 1
- class Meta[source]
Bases:
object- verbose_name = 'State Legislative District (Upper)'
- verbose_name_plural = 'State Legislative Districts (Upper)'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- class siege_utilities.geo.django.models.political.StateLegislativeLower[source]
Bases:
CensusTIGERBoundaryState Legislative District — Lower Chamber (House/Assembly).
GEOID: 5 digits = state (2) + district (3) Example: “06001” for California State Assembly District 1
- class Meta[source]
Bases:
object- verbose_name = 'State Legislative District (Lower)'
- verbose_name_plural = 'State Legislative Districts (Lower)'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- class siege_utilities.geo.django.models.political.VTD[source]
Bases:
CensusTIGERBoundaryVoting Tabulation District (VTD) boundary.
VTDs are the smallest geographic units for which Census tabulates voting-age population data. They approximate but don’t exactly match election precincts.
GEOID: 11 digits = state (2) + county (3) + VTD (6) Example: “06037001234” for VTD 001234 in LA County, CA
Enhanced fields support enterprise-compatible workflows: - precinct_name/precinct_code for election data linkage - registered_voters for turnout analysis - data_source for provenance tracking - populate_parent_relationships() for FK resolution
- congressional_district
alias of
CongressionalDistrict
- class Meta[source]
Bases:
object- verbose_name = 'Voting Tabulation District'
- verbose_name_plural = 'Voting Tabulation Districts'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index, django.db.models.Index]
- constraints = [django.db.models.CheckConstraint]
- classmethod get_geoid_length()[source]
Return the expected GEOID length for this geography type.
- Return type:
- class siege_utilities.geo.django.models.political.Precinct[source]
Bases:
CensusTIGERBoundaryElection Precinct boundary.
Precincts are the fundamental unit of election administration. They may not align perfectly with Census VTDs. Stored as TIGER boundaries when sourced from Census, but precinct_source tracks actual provenance.
GEOID format varies by state; typically state (2) + county (3) + precinct code.
- class Meta[source]
Bases:
object- verbose_name = 'Precinct'
- verbose_name_plural = 'Precincts'
- unique_together = [('geoid', 'vintage_year')]
- indexes = [django.db.models.Index, django.db.models.Index, django.db.models.Index]
Per-kind special-district boundary models.
Per downstream socialwarehouse design SW#207 Q2 = “one model per kind” (no parent SpecialDistrict model). Each Census special-district function gets its own concrete model. Downstream consumers cache the geoid in a per-kind field on Address (SW already shipped those caches via SW#191’s C-medium batch).
GEOID format varies by district type but is at most 10 digits in Census Special Districts file outputs. We use a shared 10-char CharField with a permissive regex.
The Census-published “function” code distinguishes kinds within the Special Districts file; this module realizes those into separate Django models for type-safety + reverse-accessor clarity from downstream consumers (an Address’s fire_protection_districts is unambiguous about which kind it returns).
- class siege_utilities.geo.django.models.special_districts.SpecialDistrictBase[source]
Bases:
CensusTIGERBoundaryShared shape for all per-kind special-district models.
Abstract: each concrete kind below inherits and gets its own table.
- class siege_utilities.geo.django.models.special_districts.FireProtectionDistrict[source]
Bases:
SpecialDistrictBaseFire protection special district. Census function code typically ‘24’.
- class siege_utilities.geo.django.models.special_districts.WaterSupplyDistrict[source]
Bases:
SpecialDistrictBaseWater supply special district. Census function code typically ‘91’.
- class siege_utilities.geo.django.models.special_districts.HospitalDistrict[source]
Bases:
SpecialDistrictBaseHospital special district. Census function code typically ‘40’.
- class siege_utilities.geo.django.models.special_districts.LibraryDistrict[source]
Bases:
SpecialDistrictBaseLibrary special district. Census function code typically ‘52’.
- class siege_utilities.geo.django.models.special_districts.CemeteryDistrict[source]
Bases:
SpecialDistrictBaseCemetery special district. Census function code typically ‘05’.
- class siege_utilities.geo.django.models.special_districts.MosquitoAbatementDistrict[source]
Bases:
SpecialDistrictBaseMosquito abatement special district. Census function code typically ‘63’.
- class siege_utilities.geo.django.models.special_districts.OtherSpecialDistrict[source]
Bases:
SpecialDistrictBaseCatch-all for special-district kinds not represented by a dedicated model. Downstream consumers can filter by function_code to scope to a specific kind that hasn’t graduated to its own model yet.
Timezone boundary model.
Stores IANA timezone boundaries from the timezone-boundary-builder (https://github.com/evansiroky/timezone-boundary-builder) as PostGIS geometries with DST offset metadata.
- class siege_utilities.geo.django.models.timezone.TimezoneGeometry[source]
Bases:
TemporalBoundaryIANA timezone boundary geometry.
Data source: timezone-boundary-builder GeoJSON releases. Each record represents a single IANA timezone polygon for a given vintage year.
Example
>>> tz = TimezoneGeometry.objects.get(timezone_id='America/New_York') >>> tz.utc_offset_std Decimal('-5.0')
Managers
Custom managers for Census boundary models.
- class siege_utilities.geo.django.managers.BoundaryManager[source]
Bases:
ManagerCustom manager for temporal boundary models.
Provides convenient methods for common spatial queries.
Example
>>> from siege_utilities.geo.django.models import Tract >>> point = Point(-122.4194, 37.7749, srid=4326) >>> tract = Tract.objects.containing_point(point).for_year(2020).first()
- containing_point(point, srid=4326)[source]
Find boundaries containing a point.
- Parameters:
srid (int)
- within_distance(point, distance_meters)[source]
Find boundaries within a distance of a point.
- Parameters:
distance_meters (float)
- nearest(point, max_distance_m=None, srid=4326)[source]
Find boundaries ordered by distance from a point.
- Parameters:
srid (int)
- get_children(parent_geoid, year)[source]
Get child boundaries for a parent GEOID.
For example, get all counties in a state or all tracts in a county.
- class siege_utilities.geo.django.managers.BoundaryQuerySet[source]
Bases:
QuerySetCustom QuerySet for boundary models with spatial queries.
- containing_point(point, srid=4326)[source]
Find boundaries containing a point.
- Parameters:
point (django.contrib.gis.geos.Point) – A GEOS Point object or (lon, lat) tuple
srid (int) – Spatial reference ID (default: 4326 WGS 84)
- Returns:
QuerySet filtered to boundaries containing the point
- intersecting(geometry)[source]
Find boundaries intersecting a geometry.
- Parameters:
geometry (django.contrib.gis.geos.GEOSGeometry) – Any GEOS geometry
- Returns:
QuerySet filtered to intersecting boundaries
- within_distance(point, distance_meters)[source]
Find boundaries within a distance of a point.
Uses geodesic distance via PostGIS ST_DWithin(geography).
- Parameters:
point (django.contrib.gis.geos.Point) – A GEOS Point object
distance_meters (float) – Distance in meters
- Returns:
QuerySet filtered to boundaries within distance
- nearest(point, max_distance_m=None, srid=4326)[source]
Find boundaries ordered by distance from a point (nearest first).
Uses geodesic distance via PostGIS ST_Distance(geography).
- Parameters:
point – A GEOS Point object or (lon, lat) tuple
max_distance_m – Optional max distance in meters to filter results
srid (int) – Spatial reference ID for tuple input (default: 4326 WGS 84)
- Returns:
QuerySet annotated with ‘distance’ and ordered nearest-first
- with_area()[source]
Annotate each boundary with its calculated area.
- Returns:
QuerySet with ‘calculated_area’ annotation (square meters)
- by_land_area(ascending=True)[source]
Order boundaries by land area.
- Parameters:
ascending (bool) – True for smallest first, False for largest first
- Returns:
Ordered QuerySet
- by_population(year=None, ascending=False)[source]
Order boundaries by population.
Requires DemographicSnapshot records to be joined.
- geojson()[source]
Return boundaries as a GeoJSON FeatureCollection dict.
GeoJSON is always WGS 84 (EPSG:4326) per RFC 7946. The global default CRS set via
set_default_crs()does not apply here — useisochrone_to_geodataframe()or convert the result to a GeoDataFrame if you need reprojection.- Returns:
4326).
- Return type:
GeoJSON FeatureCollection dict (always EPSG
Custom manager for temporal boundary models with spatial query helpers.
- class siege_utilities.geo.django.managers.boundary_manager.BoundaryQuerySet[source]
Bases:
QuerySetCustom QuerySet for boundary models with spatial queries.
- containing_point(point, srid=4326)[source]
Find boundaries containing a point.
- Parameters:
point (django.contrib.gis.geos.Point) – A GEOS Point object or (lon, lat) tuple
srid (int) – Spatial reference ID (default: 4326 WGS 84)
- Returns:
QuerySet filtered to boundaries containing the point
- intersecting(geometry)[source]
Find boundaries intersecting a geometry.
- Parameters:
geometry (django.contrib.gis.geos.GEOSGeometry) – Any GEOS geometry
- Returns:
QuerySet filtered to intersecting boundaries
- within_distance(point, distance_meters)[source]
Find boundaries within a distance of a point.
Uses geodesic distance via PostGIS ST_DWithin(geography).
- Parameters:
point (django.contrib.gis.geos.Point) – A GEOS Point object
distance_meters (float) – Distance in meters
- Returns:
QuerySet filtered to boundaries within distance
- nearest(point, max_distance_m=None, srid=4326)[source]
Find boundaries ordered by distance from a point (nearest first).
Uses geodesic distance via PostGIS ST_Distance(geography).
- Parameters:
point – A GEOS Point object or (lon, lat) tuple
max_distance_m – Optional max distance in meters to filter results
srid (int) – Spatial reference ID for tuple input (default: 4326 WGS 84)
- Returns:
QuerySet annotated with ‘distance’ and ordered nearest-first
- with_area()[source]
Annotate each boundary with its calculated area.
- Returns:
QuerySet with ‘calculated_area’ annotation (square meters)
- by_land_area(ascending=True)[source]
Order boundaries by land area.
- Parameters:
ascending (bool) – True for smallest first, False for largest first
- Returns:
Ordered QuerySet
- by_population(year=None, ascending=False)[source]
Order boundaries by population.
Requires DemographicSnapshot records to be joined.
- geojson()[source]
Return boundaries as a GeoJSON FeatureCollection dict.
GeoJSON is always WGS 84 (EPSG:4326) per RFC 7946. The global default CRS set via
set_default_crs()does not apply here — useisochrone_to_geodataframe()or convert the result to a GeoDataFrame if you need reprojection.- Returns:
4326).
- Return type:
GeoJSON FeatureCollection dict (always EPSG
- class siege_utilities.geo.django.managers.boundary_manager.BoundaryManager[source]
Bases:
ManagerCustom manager for temporal boundary models.
Provides convenient methods for common spatial queries.
Example
>>> from siege_utilities.geo.django.models import Tract >>> point = Point(-122.4194, 37.7749, srid=4326) >>> tract = Tract.objects.containing_point(point).for_year(2020).first()
- containing_point(point, srid=4326)[source]
Find boundaries containing a point.
- Parameters:
srid (int)
- within_distance(point, distance_meters)[source]
Find boundaries within a distance of a point.
- Parameters:
distance_meters (float)
- nearest(point, max_distance_m=None, srid=4326)[source]
Find boundaries ordered by distance from a point.
- Parameters:
srid (int)
- get_children(parent_geoid, year)[source]
Get child boundaries for a parent GEOID.
For example, get all counties in a state or all tracts in a county.
Services
Services for populating Census boundary and NCES education data.
Imports are deferred to avoid requiring Django setup at import time.
Use: from siege_utilities.geo.django.services.nces_service import NCESPopulationService
Service for populating boundary crosswalk data.
Uses the existing siege_utilities.geo.crosswalk module to load crosswalk relationships into Django models.
- class siege_utilities.geo.django.services.crosswalk_service.CrosswalkPopulationResult[source]
Bases:
objectResult of a crosswalk population operation.
- __init__(geography_type, source_year, target_year, state_fips, records_created, records_updated, records_skipped, errors)
- class siege_utilities.geo.django.services.crosswalk_service.CrosswalkPopulationService[source]
Bases:
objectService for populating boundary crosswalk data.
This service integrates with the existing siege_utilities.geo.crosswalk module to load crosswalk relationships into TemporalCrosswalk models.
Example
>>> service = CrosswalkPopulationService() >>> result = service.populate( ... geography_type='tract', ... source_year=2010, ... target_year=2020, ... state_fips='06' ... ) >>> print(f"Created {result.records_created} crosswalk records")
- __init__(cache_dir=None)[source]
Initialize the service.
- Parameters:
cache_dir (str | None) – Directory for caching downloaded crosswalk files
- populate(geography_type, source_year, target_year, state_fips=None, weight_type='population', update_existing=False, batch_size=1000)
Populate crosswalk data for a geography type.
- Parameters:
geography_type (str) – Type of geography (tract, blockgroup, etc.)
source_year (int) – Source Census year (earlier)
target_year (int) – Target Census year (later)
state_fips (str | None) – Optional state filter
weight_type (str) – Type of weight (population, housing, area)
update_existing (bool) – If True, update existing records; otherwise skip
batch_size (int) – Number of records per database batch
- Returns:
CrosswalkPopulationResult with statistics
- Return type:
- populate_tract_crosswalk(source_year=2010, target_year=2020, state_fips=None, **kwargs)[source]
Populate tract crosswalk data.
- Parameters:
- Return type:
Service for populating demographic data from Census API.
Uses the existing siege_utilities.geo.census_api_client module to fetch demographic data, then loads it into Django models.
- class siege_utilities.geo.django.services.demographic_service.DemographicPopulationResult[source]
Bases:
objectResult of a demographic population operation.
- __init__(geography_type, year, dataset, state_fips, variable_group, records_created, records_updated, records_skipped, errors)
- class siege_utilities.geo.django.services.demographic_service.DemographicPopulationService[source]
Bases:
objectService for populating demographic data from Census API.
This service integrates with the existing siege_utilities.geo.census_api_client to fetch demographic data and store it in DemographicSnapshot models.
Example
>>> service = DemographicPopulationService(api_key='your-key') >>> result = service.populate( ... geography_type='tract', ... year=2022, ... state_fips='06', ... variable_group='income' ... ) >>> print(f"Created {result.records_created} demographic snapshots")
- CENSUS_GEOGRAPHIES = {'blockgroup': 'block group:*', 'county': 'county:*', 'place': 'place:*', 'state': 'state:*', 'tract': 'tract:*'}
- property client
Lazy-load the Census API client.
- populate(geography_type, year, state_fips, variable_group='demographics_basic', dataset='acs5', update_existing=False, batch_size=500)
Populate demographic data for boundaries.
- Parameters:
geography_type (str) – Type of geography (state, county, tract, etc.)
year (int) – ACS/Census year
state_fips (str) – State FIPS code
variable_group (str) – Variable group name (income, education, housing, etc.)
dataset (str) – Census dataset (acs5, acs1, dec)
update_existing (bool) – If True, update existing records; otherwise skip
batch_size (int) – Number of records per database batch
- Returns:
DemographicPopulationResult with statistics
- Return type:
- populate_income(geography_type, year, state_fips, **kwargs)[source]
Populate income demographic data.
- Parameters:
- Return type:
- populate_education(geography_type, year, state_fips, **kwargs)[source]
Populate education demographic data.
- Parameters:
- Return type:
- populate_housing(geography_type, year, state_fips, **kwargs)[source]
Populate housing demographic data.
- Parameters:
- Return type:
- populate_race_ethnicity(geography_type, year, state_fips, **kwargs)[source]
Populate race/ethnicity demographic data.
- Parameters:
- Return type:
Service for computing and caching isochrone results in PostGIS.
Wraps the siege_utilities.geo.isochrones module to fetch isochrone polygons from routing providers and store them as IsochroneResult model instances for spatial queries and historical analysis.
- class siege_utilities.geo.django.services.isochrone_service.IsochroneComputeResult[source]
Bases:
objectResult of an isochrone compute-and-store operation.
- class siege_utilities.geo.django.services.isochrone_service.IsochroneComputeService[source]
Bases:
objectCompute isochrones via external providers and cache in PostGIS.
Example
>>> service = IsochroneComputeService() >>> result = service.compute_and_store( ... latitude=41.8781, longitude=-87.6298, ... travel_minutes=15, profile="driving-car", ... ) >>> print(f"Created: {result.records_created}, Cached: {result.records_cached}")
- compute_and_store(latitude, longitude, travel_minutes, profile='driving-car', provider='openrouteservice', vintage_year=None, api_key=None, base_url=None, max_age_days=None, **provider_kwargs)[source]
Fetch an isochrone from a provider and store it in PostGIS.
If a cached result exists within
max_age_days, returns the cached record without making an API call.- Parameters:
latitude (float) – Origin latitude.
longitude (float) – Origin longitude.
travel_minutes (int) – Travel time in minutes.
profile (str) – Routing profile (e.g. “driving-car”, “foot-walking”).
provider (str) – Isochrone provider (“openrouteservice” or “valhalla”).
vintage_year (int | None) – Year to associate with this isochrone (default: current year).
api_key (str | None) – API key for the provider (if required).
base_url (str | None) – Custom base URL for self-hosted providers.
max_age_days (int | None) – If set, return cached result if younger than this many days.
**provider_kwargs – Extra parameters forwarded to get_isochrone().
- Returns:
IsochroneComputeResult with created/cached counts.
- Return type:
Service for populating NCES education data into Django models.
Downloads NCES locale boundaries, school locations, and district data using NCESDownloader, then loads into NCESLocaleBoundary, SchoolLocation, and enriches existing SchoolDistrict* records with locale codes.
- class siege_utilities.geo.django.services.nces_service.NCESPopulationResult[source]
Bases:
objectResult of an NCES population operation.
- class siege_utilities.geo.django.services.nces_service.NCESPopulationService[source]
Bases:
objectService for downloading and populating NCES education data.
Three actions: 1. populate_locale_boundaries — download 12 territory polygons per year 2. populate_school_locations — download geocoded school points 3. enrich_school_districts — match NCES data to existing district records
Example:
service = NCESPopulationService(cache_dir="/data/nces") result = service.populate_locale_boundaries(year=2023) print(f"Created {result.records_created} locale boundaries")
- populate_locale_boundaries(year=2023, update_existing=False, batch_size=100)
Download and populate NCESLocaleBoundary records.
- Parameters:
- Returns:
NCESPopulationResult with statistics.
- Return type:
- populate_school_locations(year=2023, state_fips=None, update_existing=False, batch_size=500)
Download and populate SchoolLocation records.
- Parameters:
- Returns:
NCESPopulationResult with statistics.
- Return type:
- enrich_school_districts(year=2023, update_existing=False, batch_size=500)[source]
Enrich existing SchoolDistrict* records with NCES locale codes.
Matches NCES district administrative data to existing Django SchoolDistrictElementary/Secondary/Unified records by LEA ID, then updates locale_code, locale_category, and locale_subcategory.
- Parameters:
- Returns:
NCESPopulationResult with statistics.
- Return type:
Service for populating NLRB (National Labor Relations Board) region data.
NLRB divides the US into 26 active regions (numbered 1-31 with gaps). Boundaries are county-based and change infrequently. Since the NLRB does not publish shapefiles, this service builds region geometries by dissolving county boundaries for the counties assigned to each region.
Data source: NLRB region-to-county mapping (manual maintenance).
- class siege_utilities.geo.django.services.nlrb_service.NLRBPopulationResult[source]
Bases:
objectResult of an NLRB region population run.
- class siege_utilities.geo.django.services.nlrb_service.NLRBPopulationService[source]
Bases:
objectPopulate NLRBRegion model instances from the built-in region registry.
Since the NLRB does not publish GIS shapefiles, this service creates region records with state-level metadata. When county boundaries are available in the database, it can optionally dissolve them into region geometries.
Example
>>> service = NLRBPopulationService() >>> result = service.populate(vintage_year=2024) >>> print(f"Created {result.records_created} regions")
Service for populating Census boundary data from TIGER/Line shapefiles.
Uses the existing siege_utilities.geo.spatial_data module to download boundaries, then loads them into Django models.
- class siege_utilities.geo.django.services.population_service.PopulationResult[source]
Bases:
objectResult of a boundary population operation.
- class siege_utilities.geo.django.services.population_service.BoundaryPopulationService[source]
Bases:
objectService for populating Census boundary models from TIGER/Line data.
This service integrates with the existing siege_utilities.geo module to download TIGER/Line shapefiles and load them into Django models.
Example
>>> service = BoundaryPopulationService() >>> result = service.populate_counties(year=2020, state_fips='06') >>> print(f"Created {result.records_created} counties")
>>> # Or use the generic method >>> result = service.populate( ... geography_type='tract', ... year=2020, ... state_fips='06' ... )
- GEOGRAPHY_MODELS = {'block': 'Block', 'blockgroup': 'BlockGroup', 'cbsa': 'CBSA', 'cd': 'CongressionalDistrict', 'county': 'County', 'elsd': 'SchoolDistrictElementary', 'place': 'Place', 'precinct': 'Precinct', 'scsd': 'SchoolDistrictSecondary', 'sldl': 'StateLegislativeLower', 'sldu': 'StateLegislativeUpper', 'state': 'State', 'tract': 'Tract', 'unsd': 'SchoolDistrictUnified', 'urbanarea': 'UrbanArea', 'vtd': 'VTD', 'zcta': 'ZCTA'}
- TIGER_TYPES = {'block': 'TABBLOCK', 'blockgroup': 'BG', 'cbsa': 'CBSA', 'cd': 'CD', 'county': 'COUNTY', 'elsd': 'ELSD', 'place': 'PLACE', 'scsd': 'SCSD', 'sldl': 'SLDL', 'sldu': 'SLDU', 'state': 'STATE', 'tract': 'TRACT', 'unsd': 'UNSD', 'urbanarea': 'UA', 'vtd': 'VTD', 'zcta': 'ZCTA5'}
- __init__(cache_dir=None)[source]
Initialize the service.
- Parameters:
cache_dir (str | None) – Directory for caching downloaded shapefiles
- populate(geography_type, year, state_fips=None, update_existing=False, batch_size=500)
Populate boundary data for a geography type.
- Parameters:
geography_type (str) – Type of geography (state, county, tract, etc.)
year (int) – Census year
state_fips (str | None) – State FIPS code (required for sub-state geographies)
update_existing (bool) – If True, update existing records; otherwise skip
batch_size (int) – Number of records per database batch
- Returns:
PopulationResult with statistics
- Return type:
- populate_states(year, **kwargs)[source]
Populate state boundaries.
- Parameters:
year (int)
- Return type:
- populate_counties(year, state_fips=None, **kwargs)[source]
Populate county boundaries.
- Parameters:
- Return type:
- populate_tracts(year, state_fips, **kwargs)[source]
Populate tract boundaries (requires state).
- Parameters:
- Return type:
- populate_block_groups(year, state_fips, **kwargs)[source]
Populate block group boundaries (requires state).
- Parameters:
- Return type:
- populate_blocks(year, state_fips, **kwargs)[source]
Populate block boundaries (requires state).
- Parameters:
- Return type:
- populate_places(year, state_fips=None, **kwargs)[source]
Populate place boundaries.
- Parameters:
- Return type:
- populate_zctas(year, **kwargs)[source]
Populate ZCTA boundaries.
- Parameters:
year (int)
- Return type:
- populate_congressional_districts(year, state_fips=None, **kwargs)[source]
Populate congressional district boundaries.
- Parameters:
- Return type:
Service for loading Redistricting Data Hub data into Django models.
Orchestrates fetching data from RDH (via the data client), computing compactness scores, overlaying demographics, and persisting to the redistricting Django models.
- class siege_utilities.geo.django.services.rdh_loader.RDHLoadResult[source]
Bases:
objectResult of an RDH load operation.
- class siege_utilities.geo.django.services.rdh_loader.RDHLoaderService[source]
Bases:
objectLoads RDH data into Django redistricting models.
Example:
from siege_utilities.geo.django.services.rdh_loader import RDHLoaderService service = RDHLoaderService() plan = service.load_enacted_plan("VA", "congress", 2020) service.compute_plan_compactness(plan) count = service.load_demographics_for_plan(plan, acs_year=2022)
- __init__(client=None)[source]
- Parameters:
client (RDHClient, optional) – Pre-configured RDH client. Created from env vars if omitted.
- load_enacted_plan(state, chamber, cycle_year, year=None)
Fetch and load an enacted redistricting plan with all districts.
- Parameters:
- Returns:
The created or updated plan instance.
- Return type:
- load_precinct_results(state, election_year, year=None, plan=None)
Fetch and load precinct election results.
- Parameters:
state (str) – Two-letter state abbreviation.
election_year (int) – Election year.
year (str, optional) – Year filter for RDH query.
plan (RedistrictingPlan, optional) – Link results to a specific plan.
- Return type:
- load_cvap_for_plan(plan, year)
Overlay CVAP data onto plan districts via spatial join.
- Parameters:
plan (RedistrictingPlan) – Plan whose districts will be updated.
year (int) – CVAP data year.
- Return type:
- load_demographics_for_plan(plan, acs_year, census_gdf=None)
Overlay ACS demographics onto plan districts via spatial join.
- Parameters:
plan (RedistrictingPlan) – Plan to populate.
acs_year (int) – ACS 5-year data year.
census_gdf (GeoDataFrame, optional) – Pre-loaded Census data. If omitted, fetches from RDH.
- Return type:
- compute_plan_compactness(plan)[source]
Compute and store compactness scores for all districts in a plan.
- Parameters:
plan (RedistrictingPlan) – Plan whose districts will be scored.
- Return type:
None
Service for demographic roll-up aggregation across geography levels.
Aggregates demographic data from a finer geography (e.g., tracts) to a coarser geography (e.g., counties) using GEOID prefix hierarchy or temporal crosswalk mappings. Supports sum, avg, and weighted_avg (population-weighted) operations.
- class siege_utilities.geo.django.services.rollup_service.RollupResult[source]
Bases:
objectResult of a rollup aggregation run.
- class siege_utilities.geo.django.services.rollup_service.DemographicRollupService[source]
Bases:
objectAggregate demographic values from child geographies to parent geographies.
Uses GEOID prefix hierarchy for parent resolution: - Block (15) → Tract (11) → County (5) → State (2) - BlockGroup (12) → Tract (11) → County (5) → State (2)
Supports operations: - sum: Sum of child values (e.g., population) - avg: Simple average of child values - weighted_avg: Population-weighted average (e.g., median income)
Example
service = DemographicRollupService() result = service.rollup(
source_level=’tract’, target_level=’county’, year=2022, variables=[‘B01001_001E’], operation=’sum’,
)
- GEOID_PREFIX_LENGTHS = {'block': 15, 'block_group': 12, 'county': 5, 'state': 2, 'tract': 11}
- rollup(source_level, target_level, year, variables, operation='sum', state_fips=None, batch_size=500, crosswalk_year=None, min_coverage=0.5)[source]
Aggregate demographic data from source to target geography level.
- Parameters:
source_level (str) – Source (finer) geography (‘tract’, ‘block_group’, etc.)
target_level (str) – Target (coarser) geography (‘county’, ‘state’, etc.)
year (int) – Census year for the demographic data
operation (str) – Aggregation operation (‘sum’, ‘avg’, ‘weighted_avg’)
state_fips (str | None) – Optional state filter
batch_size (int) – Bulk create batch size
crosswalk_year (int | None) – When set, use TemporalCrosswalk mappings from
crosswalk_year→yearinstead of GEOID prefix truncation. Enables rollup across boundary changes.min_coverage (float) – Minimum coverage ratio (0-1, default 0.5). When a target geography has data for fewer than
min_coverageof its expected children, a warning is logged.
- Returns:
List of RollupResult (one per variable)
- Return type:
Service for populating DemographicTimeSeries from multi-year Census data.
Fetches Census data across multiple years for a given geography level, computes trend statistics (CAGR, std_dev, trend direction), and bulk-creates DemographicTimeSeries records.
- class siege_utilities.geo.django.services.timeseries_service.TimeseriesResult[source]
Bases:
objectResult of a timeseries population run.
- class siege_utilities.geo.django.services.timeseries_service.TimeseriesService[source]
Bases:
objectPopulate DemographicTimeSeries from multi-year Census snapshots.
This service reads existing DemographicSnapshot records for a boundary, assembles time series, computes statistics, and stores the result as a DemographicTimeSeries record.
Example
service = TimeseriesService() result = service.populate_timeseries(
variables=[‘B01001_001E’], years=range(2015, 2023), geography_level=’tract’, state_fips=’06’,
)
- __init__(dataset='acs5')[source]
- Parameters:
dataset (str) – Census dataset identifier (acs5, acs1, dec)
- populate_timeseries(variables, years, geography_level, state_fips=None, update_existing=False, batch_size=500)[source]
Build DemographicTimeSeries records from existing DemographicSnapshots.
- Parameters:
variables (List[str]) – List of Census variable codes (e.g., [‘B01001_001E’])
geography_level (str) – Geography level (‘tract’, ‘county’, etc.)
state_fips (str | None) – Optional state FIPS to filter boundaries
update_existing (bool) – If True, update existing series; else skip
batch_size (int) – Bulk create batch size
- Returns:
List of TimeseriesResult (one per variable)
- Return type:
Service for populating TimezoneGeometry from timezone-boundary-builder GeoJSON.
Data source: https://github.com/evansiroky/timezone-boundary-builder Releases provide GeoJSON files with IANA timezone boundaries.
- class siege_utilities.geo.django.services.timezone_service.TimezonePopulationResult[source]
Bases:
objectResult of a timezone population run.
- class siege_utilities.geo.django.services.timezone_service.TimezonePopulationService[source]
Bases:
objectPopulate TimezoneGeometry model from timezone-boundary-builder GeoJSON.
Example
>>> service = TimezonePopulationService() >>> result = service.populate_from_geojson('/path/to/timezones.geojson') >>> print(f"Created {result.records_created} timezone boundaries")
Urbanicity classification service for Census tracts.
Computes NCES-style locale codes (11-43) for tracts in years where official NCES locale data is not available. Uses tract population (from DemographicSnapshot) and distance to the nearest Census Urban Area boundary to apply the NCES classification thresholds.
Usage:
from siege_utilities.geo.django.services import UrbanicityClassificationService
service = UrbanicityClassificationService()
result = service.classify(year=2020, state_fips="06")
- class siege_utilities.geo.django.services.urbanicity_service.ClassificationResult[source]
Bases:
objectResult of an urbanicity classification run.
- class siege_utilities.geo.django.services.urbanicity_service.UrbanicityClassificationService[source]
Bases:
objectClassify Census tracts into NCES locale codes using population and distance-to-urban-area.
- Algorithm:
For each tract, look up total population from DemographicSnapshot.
Compute geodesic distance from the tract’s internal_point to the nearest UrbanArea boundary.
Pass (population, distance_miles) to
classify_urbanicity()to get the subcategory string (e.g."city_large").Convert subcategory to the numeric NCES code (e.g. 11).
Bulk-update
Tract.urbanicity_code.
- classify(year, state_fips=None, urban_area_year=None, dataset='acs5', batch_size=500, overwrite=False, srid=4326)[source]
Classify tracts for the given vintage year.
- Parameters:
year (int) – Census vintage year for tracts and demographics.
state_fips (str | None) – Limit to a single state (2-digit FIPS). If None, classifies all states.
urban_area_year (int | None) – Vintage year for UrbanArea boundaries. Defaults to year.
dataset (str) – Census dataset used for the DemographicSnapshot (default
"acs5").batch_size (int) – Number of tracts to update per bulk_update call.
overwrite (bool) – If False (default), skip tracts that already have a non-null urbanicity_code.
srid (int) – SRID for spatial operations (default 4326).
- Returns:
ClassificationResult with counts.
- Return type:
Serializers
Management Commands
Django management commands for Census boundary data.
App Configuration
Django app configuration for siege_utilities.geo.django.