Skip to main content

Offline country profiles, coordinate tools, and geography learning utilities

Project description

PyWorldAtlas

A compact, source-aware world atlas for Python that works completely offline.

Source 0.7.0 PyPI Python 3.10–3.14 Runtime dependencies: 0 Offline: yes License: MIT

PyWorldAtlas makes real geographic data feel like ordinary Python. Look up a country by name or code, inspect immutable country and capital objects, explore major cities, calculate geographic relationships, and build reproducible learning material—without an API key, runtime download, database server, or third-party dependency.

from pyworldatlas import Atlas

with Atlas() as atlas:
    brazil = atlas.country("Brazil")

    print(brazil.flag, brazil.name_in("pt"), brazil.capital.name)
    print(brazil.highest_point.name, brazil.highest_point.elevation_m)
    print([river.name for river in brazil.rivers[:3]])
    print(brazil.climate.dominant_zone.code, brazil.climate.dominant_zone.name)
    print([country.name for country in atlas.countries_with_river("Amazon")])

Educational purpose and editorial policy

PyWorldAtlas is a purely educational package that provides offline access to factual geographic data. It does not provide political commentary, promote a viewpoint, or decide geographic disagreements. Factual ranking methods only sort documented numeric fields; they are not judgments about countries or people. Values follow documented source conventions, and the package states its coverage and limitations clearly.

Every person, place, language, and culture must be described respectfully. Hateful, harassing, threatening, demeaning, or discriminatory content is not accepted. Read the formal educational and editorial policy and community code of conduct.

Dataset coverage

The bundled dataset contains every country and area in the captured UN M49 scope, cross-checked against GeoNames country metadata. Version 0.3.0 added a reviewed land-border graph to the profile, coordinate, capital, and populated-place records established in earlier releases. Version 0.3.1 makes that graph easier to query, teach, and explain. Version 0.5.0 added one sourced local-language identity for every record, a separate sourced English formal-name layer for 240 profiles, and the package's educational and editorial policy. Version 0.6.0 added country reference facts, practical metadata, profile filters, deterministic rankings, and nearest-capital discovery. Version 0.7.0 adds sourced physical geography, represented Köppen-Geiger climate classes, feature discovery, physical rankings, and new learning prompts.

Current dataset Coverage
Countries and areas 248
Primary capitals 241 / 248
Capital coordinates 241 / 241
Populated-place records 6,265, including retained capitals
English country/area identities 248 / 248
Sourced English formal names 240 / 248: 195 distinct long forms, 45 equal to the short form
Selected local-language names 248 / 248, across 80 languages and 21 scripts
Reviewed national official short/formal names 10 / 248
Anthem titles 234 / 248
Reviewed source-listed mottos 32 / 248
English demonym profiles 227 / 248
Country timezone records 417 across 246 profiles
Country-language metadata 722 records across 245 profiles
Postal-code formats 176 / 248
Reviewed land borders 319 undirected relationships
Countries and areas without an accepted land border 85
Total-area profiles 248 / 248
Land-area profiles 238 / 248
Numeric water-area profiles 233 / 248
Coastline profiles 238 / 248
Highest and lowest points 240 / 248 each
Mean-elevation profiles 166 / 248
Source-listed major rivers 188 records across 80 profiles
Source-listed major lakes 187 records across 69 profiles
Plain-language climate summaries 240 / 248
Köppen-Geiger climate profiles 241 / 248
Runtime dependencies 0
Bundled databases 1 SQLite file

The 0.7.0 release adds land/water area, coastline, elevation extremes, source-listed major rivers and lakes, climate summaries, Köppen-Geiger classes, physical filters, rankings, discovery helpers, and flashcards. Boundary geometry, GeoJSON, bounding boxes, centroids, point-in-country lookup, anthem credits/dates, and reference dates remain outside this release.

Installation

Install the latest published release:

python -m pip install --upgrade pyworldatlas

Install the current source checkout and its separate data builder when contributing:

python -m pip install -e . -e pipeline

You can also test the exact local wheel after running the release build:

python -m pip install --no-index --no-deps dist/pyworldatlas-0.7.0-py3-none-any.whl

The package runtime supports Python 3.10 through 3.14 during the 0.x release series. Python versions are only claimed as release-supported after CI passes.

What works in this checkout

Capability Example
Exact lookup atlas.country("Japan")
Standard identifiers atlas.country("JP"), atlas.country("JPN"), atlas.country("392")
Familiar aliases atlas.country("USA"), atlas.country("Holy See")
Collection behavior atlas["DO"], "France" in atlas, len(atlas)
Ranked search atlas.search_countries("united")
Geographic filtering atlas.countries(continent="Americas")
Capital records country.capital, .coordinates, .timezone_id
Major cities atlas.major_cities("Japan", limit=5)
Rich profile country.population, .currency, .languages, .calling_codes
Reference facts country.anthem, .motto, .demonym, .postal_code
Physical profile country.physical, .land_area_km2, .coastline_km
Elevation extremes country.highest_point, .lowest_point, .mean_elevation_m
Rivers and lakes country.rivers, .lakes, atlas.countries_with_river("Amazon")
Climate country.climate.summary, .dominant_zone, .zone_codes
Physical filters atlas.countries(coastal=False, koppen_geiger_code="Cfb")
Physical rankings atlas.rank("coastline", limit=10)
Timezone profiles country.timezones, .timezone_ids
Practical filters atlas.countries(currency_code="EUR", timezone_id="Europe/Paris")
Rankings atlas.rank("population", limit=10)
Nearby capitals atlas.nearest_capitals("Tokyo", country="JP")
English name layers country.name, .official_name, .formal_name
Flags and calculated facts country.flag_emoji, .population_density
Discovery cards country.discovery_card()
Stable country samples atlas.sample_countries(count=5, seed=42)
Structured flashcards atlas.flashcards(topic="capitals", count=10, seed=42)
City coordinates atlas.coordinates("Tokyo", country="JP")
Distance atlas.distance_between("Tokyo", "Paris", first_country="JP", second_country="FR")
Bearing and midpoint coordinate.bearing_to(other), .midpoint_to(other)
Land neighbors atlas.neighbors("France"), atlas.shares_border("ES", "MA")
Border paths atlas.border_path("Portugal", "China"), atlas.border_crossings(...)
Land connectivity atlas.has_land_route("Portugal", "China")
Land components atlas.countries_reachable_by_land("Portugal")
Borderless entities atlas.countries_with_no_land_borders()
Source inspection country.sources
Local-language names country.local_name("es"), country.name_in("zh")
Name coverage atlas.countries_with_local_names(script_code="Jpan")
Formal-name coverage atlas.countries_with_formal_names()
Serialization country.to_dict(), country.to_json()
Version inspection atlas.dataset_info()

Typed country profiles

Public results are frozen typed dataclasses rather than loosely structured dictionaries:

from pyworldatlas import Atlas

with Atlas() as atlas:
    country = atlas.country("Dominican Republic")

    print(country.name)
    print(country.official_name)
    print(country.formal_name)
    print(country.flag)
    print(country.flag_emoji)
    print(country.codes.alpha2)
    print(country.codes.alpha3)
    print(country.codes.numeric)
    print(country.continent)
    print(country.region)
    print(country.subregion)
    print(country.area_km2)
    print(country.population)
    print(country.population_density)
    print(country.currency)
    print(country.languages)
    print(country.calling_codes)
    print(country.top_level_domain)
    print(country.observed_timezones)

    if country.capital is not None:
        print(country.capital.name)
        print(country.capital.coordinates.as_tuple())
        print(country.capital.population)
        print(country.capital.timezone_id)

Country names and writing systems

English display, canonical, and formal names are separate fields:

from pyworldatlas import Atlas

with Atlas() as atlas:
    turkey = atlas.country("TR")

    print(turkey.name)           # Turkey (familiar lookup/display name)
    print(turkey.official_name)  # Türkiye (canonical UN M49 identity)
    print(turkey.formal_name)    # Republic of Türkiye (sourced long form)

The English formal-name layer covers 240 profiles. A formal name can equal the short form: Japan has formal_name == "Japan" and has_distinct_formal_name is False. Eight areas outside the captured source scope return None; the package does not manufacture a constitutional name.

Every country and area has one sourced local-language display name. Reviewed UNGEGN records add formal national names and romanization where the publication supplies them:

from pyworldatlas import Atlas

with Atlas() as atlas:
    dominican = atlas.country("DO")
    print(dominican.flag, dominican.name_in("es"))

    for code, language in (("CN", "zh"), ("IN", "hi"), ("JP", "ja")):
        country = atlas.country(code)
        local = country.local_name(language)
        print(local.short_name, local.formal_name, local.script_code)
        print(local.romanized_short_name)

    print([
        country.name
        for country in atlas.countries_with_local_names(language_code="es")
    ])

The local display layer covers all 248 records across 80 languages and 21 scripts. local.kind is "locale_display" for a Unicode CLDR label and "national_official" for a reviewed UNGEGN short/formal name. Formal names and romanizations on the local record remain None until their authoritative source supplies them. This local formal_name is language-specific and is separate from country.formal_name, which is English.

Country discovery and education

from pyworldatlas import Atlas

with Atlas() as atlas:
    japan = atlas.country("Japan")
    card = japan.discovery_card()

    print(card.flag_emoji, card.capital, card.population_density)

    for country in atlas.sample_countries(count=5, continent="Africa", seed=42):
        print(country.flag_emoji, country.name)

    for flashcard in atlas.flashcards(topic="capitals", count=3, seed=42):
        print(flashcard.prompt)
        print(flashcard.answer)

Sampling uses a versioned SHA-256 ranking over stable M49 identifiers, so the same dataset, filters, and seed produce the same ordered lesson across supported Python versions. Flashcards are immutable structured values rather than an interactive game. Supported topics cover capitals, flags, country codes, currencies, calling codes, domains, language codes, regions, local names, population, area, calculated density, reviewed neighbors, and land-border counts.

Latitude, longitude, and distance

from pyworldatlas import Atlas, Coordinate

with Atlas() as atlas:
    tokyo = atlas.city("Tokyo", country="Japan")
    paris = atlas.city("Paris", country="France")

    print(tokyo.coordinates.latitude, tokyo.coordinates.longitude)
    print(atlas.distance_between(tokyo, paris))             # kilometres
    print(atlas.distance_between(tokyo, paris, unit="mi"))  # miles
    print(tokyo.coordinates.bearing_to(paris.coordinates))
    print(tokyo.coordinates.midpoint_to(paris.coordinates))

london = Coordinate(51.5074, -0.1278)
paris_center = Coordinate(48.8566, 2.3522)
print(london.distance_to(paris_center))

Distances use the haversine formula and WGS84 mean Earth radius. They are surface great-circle distances, not road or flight-routing distances.

Search and filter

with Atlas() as atlas:
    for match in atlas.search_countries("united"):
        print(match.country.name, match.matched_name, match.score)

    for country in atlas.countries(continent="Europe"):
        capital = country.capital.name if country.capital else "not available"
        print(country.name, capital)

Search is case- and accent-insensitive. Exact country lookup accepts common names, reviewed aliases, alpha-2, alpha-3, and M49 numeric codes.

Land borders and shortest paths

The land-border graph introduced in 0.3.0 contains 319 reviewed undirected relationships. Neighbor results are alphabetical, and equal-length shortest paths are deterministic.

from pyworldatlas import Atlas

with Atlas() as atlas:
    print([country.name for country in atlas.neighbors("France")])
    print(atlas.shares_border("Spain", "Morocco"))

    path = atlas.border_path("Portugal", "China")
    print(path.crossings)
    print(" -> ".join(path.names))
    print(path.alpha2_codes)

    print(atlas.border_path("Japan", "China"))  # None
    print(atlas.has_land_route("Portugal", "China"))  # True

border_path() uses breadth-first search and returns an immutable, JSON-serializable BorderPathResult. A missing route is None, not an error. Maritime proximity, border geometry, border length, and road routing are not represented.

The structured flashcard API also supports neighbors, border_counts, climate_zones, coastlines, highest_points, rivers, and lakes. Neighbor answers come directly from the reviewed graph; border counts are the number of accepted edges attached to the selected country or area.

Small by design

The installed wheel contains only:

  • Python source files.
  • One generated, read-only SQLite database.
  • Standard package metadata.

At runtime PyWorldAtlas does not:

  • Contact the internet.
  • Require an API key.
  • Download or decompress data after installation.
  • Write into site-packages.
  • Load the complete database during Atlas() initialization.
  • Depend on pandas, NumPy, an ORM, a GIS engine, or SQLite extensions.

Data you can trace

The 0.7.0 release uses:

  • United Nations M49 for canonical identities, standard codes, regions, and subregions.
  • GeoNames for capitals, populated places, WGS84 coordinates, population snapshots, currencies, language and calling codes, country-code domains, timezone identifiers, postal formats, GeoNames IDs, and one input to border review.
  • Natural Earth public-domain 1:50m map units as an independent land-border topology check and build-time country aggregation layer for climate cells.
  • Unicode CLDR 48.2 for localized territory display names, currency labels/symbols/minor units, language labels, and likely scripts.
  • IANA Language Subtag Registry as a language-name fallback for captured codes not labelled by CLDR.
  • CIA World Factbook public-domain structured fields for the base English formal-name layer, anthem titles, English demonyms, area components, coastlines, elevation, source-listed rivers/lakes, and climate summaries.
  • Beck et al. Köppen-Geiger maps CC0 data for 1991–2020 represented climate classes and latitude-area-weighted country shares.
  • United Nations Protocol and Liaison Service for five current English formal-name excerpts where the final Factbook snapshot differs from current UN usage.
  • Wikidata CC0 statements for three reviewed English formal-name corrections and 32 explicitly reviewed source-listed mottos.

The stricter local formal-name records use the UNGEGN List of Country Names (E/CONF.105/13/CRP.13) for national official short and formal names. Ten selected records currently have this reviewed evidence level. CLDR and UNGEGN values include exact source locators; both snapshots and the deterministic CLDR extractor are retained with the builder.

Raw snapshots are preserved with SHA-256 manifests. The separate builder emits inspectable normalized JSON Lines before generating SQLite. Missing values stay missing; unsourced assumptions are never substituted for country facts.

Flag emoji are derived from alpha-2 codes, population density is a transparent ratio of sourced values, and discovery/learning tools only rearrange existing profile data. They introduce no additional country claims or third-party data.

The two border inputs agree on 315 relationships. Six differences have explicit decisions in build_data/reviewed/border_decisions.csv: four relationships are included and two are excluded. Any unreviewed source difference fails the data build.

Seven areas have no usable primary-capital record in the current snapshot. Their country.capital value is None. GeoNames-only country rows that do not have a matching identity in the captured UN M49 scope are excluded rather than inferred.

See DATA_SOURCES.md, DATA_QUALITY.md, and THIRD_PARTY_NOTICES.md.

Three different versions

with Atlas() as atlas:
    print(atlas.dataset_info())
  • Library version describes Python behavior and the public API.
  • Schema version describes compatibility with the bundled SQLite structure.
  • Dataset version identifies the captured source snapshot.

For this release they are 0.7.0, 7, and 2026.07.22.7.

Documentation and roadmap

Version 0.7.0 adds the physical-geography milestone. Boundary geometry, GeoJSON, bounding boxes, centroids, and point-in-country lookup are deferred to a separately reviewed later release.

License and attribution

PyWorldAtlas code is available under the MIT License. GeoNames data is provided under CC BY 4.0, Natural Earth and CIA World Factbook data are public domain, Wikidata and the Köppen-Geiger data release are CC0, and Unicode CLDR data is used under the Unicode License v3. Other source terms and notices are recorded in THIRD_PARTY_NOTICES.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyworldatlas-0.7.0.tar.gz (588.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyworldatlas-0.7.0-py3-none-any.whl (562.3 kB view details)

Uploaded Python 3

File details

Details for the file pyworldatlas-0.7.0.tar.gz.

File metadata

  • Download URL: pyworldatlas-0.7.0.tar.gz
  • Upload date:
  • Size: 588.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pyworldatlas-0.7.0.tar.gz
Algorithm Hash digest
SHA256 453b9a888ffcf7199872364b48da9b4aae1f23d8eade799d89d390901905da19
MD5 3b3e2ba8155ed7f0edf08b76bd52fbc3
BLAKE2b-256 1570d3c6403f0df31151180c8351f85253a93802026cf42f16177ff7d4e4f70c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyworldatlas-0.7.0.tar.gz:

Publisher: release.yml on jcari-dev/pyworldatlas

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyworldatlas-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: pyworldatlas-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 562.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pyworldatlas-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a8fd9bcdca514449a74a1e69a3c2ba0b0ff862d8ba68c0bb9461b75cbfee7140
MD5 7efba8517e24a773987ac6f4cb9918ac
BLAKE2b-256 0c8d4846edebed001e29ed447d35fcf0309e3c093cc28afc697625d1ff6c3010

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyworldatlas-0.7.0-py3-none-any.whl:

Publisher: release.yml on jcari-dev/pyworldatlas

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page