Django app for importing and managing Who's On First geographic data
Project description
django-whosonfirst
A Django app for importing and managing Who's On First geographic data.
Who's On First (WOF) is an open, global gazetteer that provides stable place IDs, explicit hierarchies, and polygon geometries for geographic entities—from continents and countries down to cities and neighbourhoods.
Features
- Download WOF SQLite data bundles (global or per-country)
- Import places with full hierarchy (continent → country → region → locality → neighbourhood)
- Store geometries in PostGIS
- Map to GeoNames and Wikidata via concordances
- Extensible plugin system for custom processing
Requirements
- Python 3.10+
- Django 4.2+
- PostgreSQL with PostGIS extension
django.contrib.gisconfigured
Installation
pip install django-whosonfirst
Add to your INSTALLED_APPS:
INSTALLED_APPS = [
# ...
'django.contrib.gis',
'whosonfirst',
]
Run migrations:
python manage.py migrate whosonfirst
Quick Start
1. Download WOF Data
Download data for specific countries:
# Download US and Canada
python manage.py wof_download --countries=US,CA
# Download all countries (large!)
python manage.py wof_download
2. Import Data
# Import downloaded data
python manage.py wof_import
# Import specific countries
python manage.py wof_import --countries=US,CA
# Import specific placetypes
python manage.py wof_import --placetypes=country,region,locality
# Import without geometry (faster, smaller database)
python manage.py wof_import --no-geometry
3. Query Places
from whosonfirst.models import Place
# Get all countries
countries = Place.objects.filter(placetype='country')
# Get cities in the US
us_cities = Place.objects.filter(
placetype='locality',
country_code='US'
)
# Get a place by WOF ID
sf = Place.objects.get(wof_id=85922583) # San Francisco
# Get ancestors
sf.get_ancestors() # Returns California, US, North America
# Get descendants
ca = Place.objects.get(wof_id=85688637) # California
ca.get_descendants(placetype='locality') # All cities in CA
# Query by GeoNames ID
place = Place.objects.get(geonames_id=5391959)
# Query by Wikidata ID
place = Place.objects.get(wikidata_id='Q62')
Management Commands
wof_download
Download WOF SQLite data bundles.
# Download specific countries
python manage.py wof_download --countries=US,CA,MX
# Force re-download
python manage.py wof_download --countries=US --force
# List cached bundles
python manage.py wof_download --list
# Clear cache
python manage.py wof_download --clear
wof_import
Import WOF data into Django models.
# Import all downloaded data
python manage.py wof_import
# Import specific countries
python manage.py wof_import --countries=US
# Import specific placetypes
python manage.py wof_import --placetypes=country,region,locality,neighbourhood
# Flush existing data before import
python manage.py wof_import --flush=all
# Dry run (parse without saving)
python manage.py wof_import --dry-run
# Skip geometry import
python manage.py wof_import --no-geometry
# Skip alternative names
python manage.py wof_import --no-names
wof_update
Update data (download fresh and import changes).
python manage.py wof_update --countries=US
# Check for changes without importing
python manage.py wof_update --check-only
wof_build_index
Build derived indices and resolve relationships.
# Run all index operations
python manage.py wof_build_index --all
# Resolve parent relationships only
python manage.py wof_build_index --resolve-parents
Configuration
Configure via Django settings:
# Data directory for downloads (default: app's data/ directory)
WOF_DATA_DIR = '/path/to/wof/data'
# Placetypes to import by default
WOF_PLACETYPES = [
'continent',
'country',
'region',
'locality',
'neighbourhood',
]
# Country codes to import (empty = all)
WOF_COUNTRY_CODES = ['US', 'CA', 'MX']
# Whether to import geometry (default: True)
WOF_IMPORT_GEOMETRY = True
# Skip deprecated places (default: True)
WOF_SKIP_DEPRECATED = True
# Skip places marked as not current (default: False)
WOF_SKIP_NOT_CURRENT = False
# Languages to import for alternative names (ISO 639-3 codes)
# Default: ['eng'] (English only)
# Use empty list [] to import all languages
WOF_LANGUAGES = ['eng']
# HTTP download timeout in seconds (default: 300)
WOF_DOWNLOAD_TIMEOUT = 300
# Maximum download size in bytes (default: 10GB)
WOF_MAX_DOWNLOAD_SIZE = 10 * 1024 * 1024 * 1024
# Batch size for bulk operations (default: 1000)
WOF_BATCH_SIZE = 1000
# Plugin classes for custom processing
WOF_PLUGINS = ['myapp.plugins.MyWOFPlugin']
Placetypes
WOF uses a hierarchy of placetypes:
| Placetype | Description | Example |
|---|---|---|
| continent | Continents | North America |
| country | Countries | United States |
| dependency | Dependencies | Puerto Rico |
| region | First-level admin (states/provinces) | California |
| county | Counties | Los Angeles County |
| localadmin | Local administrative areas | City of Los Angeles |
| locality | Cities/towns | San Francisco |
| borough | Boroughs | Manhattan |
| neighbourhood | Neighbourhoods | Mission District |
| venue | Points of interest | Golden Gate Bridge |
Default import includes: continent, country, dependency, region, county, localadmin, locality
Models
Place
The main model storing all WOF places.
class Place(models.Model):
# Identity
wof_id = BigIntegerField(primary_key=True)
placetype = CharField(max_length=50)
name = CharField(max_length=255)
slug = SlugField(max_length=255)
country_code = CharField(max_length=2)
# Hierarchy
parent = ForeignKey('self', null=True)
parent_wof_id = BigIntegerField(null=True)
belongsto = JSONField(default=list) # Ancestor WOF IDs
hierarchy = JSONField(default=list)
# Geometry (PostGIS)
geom = MultiPolygonField(null=True, srid=4326)
centroid = PointField(null=True, srid=4326)
latitude = FloatField(null=True)
longitude = FloatField(null=True)
bbox = JSONField(null=True)
# Concordances
concordances = JSONField(default=dict)
geonames_id = BigIntegerField(null=True)
wikidata_id = CharField(max_length=20)
# Metadata
lastmodified = BigIntegerField(null=True)
is_current = BooleanField(default=True)
is_deprecated = BooleanField(default=False)
PlaceName
Alternative names in different languages.
class PlaceName(models.Model):
place = ForeignKey(Place)
name = CharField(max_length=255)
language = CharField(max_length=10) # ISO 639-3
name_type = CharField(max_length=20) # preferred, variant, etc.
is_preferred = BooleanField(default=False)
Plugins
Create custom plugins to hook into the import process:
# myapp/plugins.py
from whosonfirst.exceptions import HookException
class MyWOFPlugin:
def place_pre(self, command, item):
"""Called before parsing each place.
Args:
command: Management command instance
item: Raw data dict from parser
Raises:
HookException: Skip this item
"""
# Skip places without geometry
if not item.get('geometry'):
raise HookException('No geometry')
# Modify item in place
item['name'] = item['name'].title()
def place_post(self, command, obj, item):
"""Called after saving each place.
Args:
command: Management command instance
obj: Saved Place model instance
item: Raw data dict
"""
if obj.placetype == 'locality':
# Custom processing for cities
pass
Register in settings:
WOF_PLUGINS = ['myapp.plugins.MyWOFPlugin']
Concordances
WOF includes mappings to external datasets. Access via the concordances JSONField or dedicated fields:
place = Place.objects.get(name='San Francisco', placetype='locality')
# GeoNames ID
place.geonames_id # 5391959
# Wikidata QID
place.wikidata_id # 'Q62'
# All concordances
place.concordances
# {
# 'geonames_id': 5391959,
# 'wikidata_id': 'Q62',
# 'fips_code': '06075',
# 'iso_code': 'US-CA',
# ...
# }
Disclaimer
This project is not affiliated with, endorsed by, or associated with Who's On First, Mapzen, or Geocode Earth. It is an independent, third-party Django integration that consumes publicly available Who's On First data.
License
MIT License
WOF Data License
Who's On First data is licensed under various open licenses. See WOF Licenses for details.
When using WOF data, please provide appropriate attribution.
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_whosonfirst-0.1.0.tar.gz.
File metadata
- Download URL: django_whosonfirst-0.1.0.tar.gz
- Upload date:
- Size: 35.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
869daa5cb8d61230eb1a227281d250230d350945b1a797df8fc14e0bf2ad239c
|
|
| MD5 |
67fd345ebe6543cd338c44fa652cfb5f
|
|
| BLAKE2b-256 |
5091b8dc76272009df975c4896ce485d6d939509e4c146a78325aceea31151e1
|
Provenance
The following attestation bundles were made for django_whosonfirst-0.1.0.tar.gz:
Publisher:
publish.yml on arthanson/django-whosonfirst
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_whosonfirst-0.1.0.tar.gz -
Subject digest:
869daa5cb8d61230eb1a227281d250230d350945b1a797df8fc14e0bf2ad239c - Sigstore transparency entry: 1229845901
- Sigstore integration time:
-
Permalink:
arthanson/django-whosonfirst@dba3ab2c9bedc18734bae6d072a0df05981eac14 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arthanson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dba3ab2c9bedc18734bae6d072a0df05981eac14 -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_whosonfirst-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_whosonfirst-0.1.0-py3-none-any.whl
- Upload date:
- Size: 45.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e03cd5f9462acf87886974429d5d30132331013f426f477e53543f6ff535e9c5
|
|
| MD5 |
24e1810b3326ba80f8e458edd54cacd5
|
|
| BLAKE2b-256 |
ac886a9ca81d958deabb3b3c46ec8dafe16c82e3fd5ca558725a0aa7e44d1098
|
Provenance
The following attestation bundles were made for django_whosonfirst-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on arthanson/django-whosonfirst
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_whosonfirst-0.1.0-py3-none-any.whl -
Subject digest:
e03cd5f9462acf87886974429d5d30132331013f426f477e53543f6ff535e9c5 - Sigstore transparency entry: 1229845955
- Sigstore integration time:
-
Permalink:
arthanson/django-whosonfirst@dba3ab2c9bedc18734bae6d072a0df05981eac14 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arthanson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dba3ab2c9bedc18734bae6d072a0df05981eac14 -
Trigger Event:
release
-
Statement type: