Land borders and paths
PyWorldAtlas includes a reviewed, undirected graph of 319 land-border relationships. It supports immediate neighbors, shared neighbors, shortest paths, connected land regions, and countries or areas with no accepted land border. Every operation works from the bundled SQLite database.
API at a glance
Call |
Returns |
Meaning |
|---|---|---|
|
|
Immediate reviewed land neighbors |
|
|
Whether one accepted edge joins the entities |
|
|
Intersection of both neighbor sets |
|
|
Deterministic shortest graph path |
|
|
Minimum number of accepted edges crossed |
|
|
Whether both entities share a graph component |
|
|
Other entities in the same component |
|
|
Entities with no accepted edge |
Neighbors
Country lookup follows the same name-and-code rules as the rest of the API. Results are immutable tuples ordered by country name.
>>> from pyworldatlas import Atlas
>>> with Atlas() as atlas:
... [country.name for country in atlas.neighbors("France")]
['Andorra', 'Belgium', 'Germany', 'Italy', 'Luxembourg', 'Monaco', 'Spain', 'Switzerland']
Maritime proximity is not a border. A country never shares a border with itself.
>>> with Atlas() as atlas:
... print(atlas.shares_border("Spain", "Morocco"))
... print(atlas.shares_border("United States", "Cuba"))
True
False
Shortest land paths
border_path() uses breadth-first search. Both
endpoints appear in the returned BorderPathResult, and
crossings is one fewer than the number of countries. Alphabetic neighbor
order makes equal-length choices deterministic.
>>> with Atlas() as atlas:
... path = atlas.border_path("Portugal", "China")
... print(path.crossings)
... print(" -> ".join(country.name for country in path.countries))
6
Portugal -> Spain -> France -> Germany -> Poland -> Russia -> China
Convenience properties avoid repeatedly unpacking the country references:
>>> path.names
('Portugal', 'Spain', 'France', 'Germany', 'Poland', 'Russia', 'China')
>>> path.alpha2_codes
('PT', 'ES', 'FR', 'DE', 'PL', 'RU', 'CN')
The result is detached from the database and serializable. If no land route
exists, border_path and border_crossings return None.
>>> with Atlas() as atlas:
... print(atlas.border_path("Japan", "China"))
... print(atlas.border_crossings("Japan", "China"))
None
None
Land connectivity
has_land_route() is a reachability test over the same
reviewed graph. It does not describe roads, border controls, ferries, visas, or
whether travel is possible. Identical endpoints return True because the
shortest graph path contains zero crossings.
>>> with Atlas() as atlas:
... print(atlas.has_land_route("Portugal", "China"))
... print(atlas.has_land_route("Japan", "China"))
... print(atlas.has_land_route("Japan", "Japan"))
True
False
True
Connected land regions
countries_reachable_by_land(country) returns the rest of the starting
country’s connected component. The starting country is excluded. An island or
other borderless entity returns an empty tuple.
countries_with_no_land_borders() returns all 85 entities that have no edge
in the reviewed graph. This classification follows the package entity scope and
border policy; it is not a general definition of an island country.
Border flashcards
The deterministic flashcard API supports two graph topics:
>>> with Atlas() as atlas:
... neighbor_card = atlas.flashcards(topic="neighbors", count=1, seed=42)[0]
... count_card = atlas.flashcards(topic="border_counts", count=1, seed=42)[0]
... print(neighbor_card.prompt)
... print(neighbor_card.answer)
... print(count_card.prompt)
... print(count_card.answer)
Which countries or areas share a reviewed land border with Kuwait?
Iraq, Saudi Arabia
How many reviewed land neighbors does Kuwait have?
2
neighbors cards exclude entities with no accepted land neighbor because
they would have no answer. border_counts cards include them and use 0.
Both topics inherit the stable seed-based sampling contract.
Where each value comes from
neighbors and shares_border read the accepted relationships stored in
the generated SQLite database. shared_neighbors is a set intersection;
border_path is breadth-first search; border_crossings is the path edge
count; has_land_route is graph reachability; and connected-component and
flashcard results are calculated from the same graph at runtime.
Country names and codes placed in results come from the ordinary country profiles. They are not copied from Natural Earth labels. Source records and the review policy are described below and in Data sources and freshness.
Data policy
The automatic acceptance rule requires agreement between the pinned GeoNames
neighbor field and shared polygon segments derived from Natural Earth 1:50m map
units. The current snapshots agree on 315 relationships. Six differences are
recorded in build_data/reviewed/border_decisions.csv; four are included and
two are excluded, producing 319 accepted relationships in total.
This graph is an attributed source convention for educational topology. Other
institutions may use different conventions. Exceptional decisions remain
inspectable, and no broader interpretation should be drawn from the technical
mappings. See Educational purpose and editorial policy and the root
docs/project/BOUNDARIES_AND_DISPUTES.md policy.
The build fails if the sources develop an unreviewed difference, if an endpoint is missing from the 248-entity scope, or if a duplicate, self-edge, or non-canonical edge appears. Geometry, border length, point-in-country tests, and route planning are outside this release.
Executable example
1"""Explore reviewed land borders and a shortest border path."""
2
3from pyworldatlas import Atlas
4
5
6with Atlas() as atlas:
7 france = atlas.neighbors("France")
8 print("France:", ", ".join(country.name for country in france))
9
10 path = atlas.border_path("Portugal", "China")
11 if path is not None:
12 route = " -> ".join(path.names)
13 print(f"{path.crossings} crossings: {route}")
14 print("Codes:", " -> ".join(path.alpha2_codes))
15
16 print("Japan to China by land:", atlas.border_path("Japan", "China"))
17 print("Portugal and China are land-connected:", atlas.has_land_route("Portugal", "China"))
18
19 card = atlas.flashcards(topic="neighbors", count=1, seed=42)[0]
20 print(card.prompt)
21 print(card.answer)