How to Retrieve Country Names from Latitude/Longitude using GeoPandas and naturalearthdata ?

Introduction

relying on Shapely for geometric operations. It provides an intuitive framework to manipulate and analyze geographic data directly within Python, making it possible to work seamlessly with formats such as shapefiles, GeoJSON, and even spatial databases.

A common challenge in geospatial analysis is determining the geographic context of a location. In practice, this often translates into a simple but fundamental question: given a pair of latitude and longitude coordinates, which country does this point belong to?

In this guide, we will walk through a complete and practical workflow to answer this question. We will start by loading country boundary data from Natural Earth, then show how to query individual points efficiently. Along the way, we will highlight common pitfalls, such as duplicate matches arising from multi-part geometries, and demonstrate how to address them. Finally, we will compare a straightforward approach with a more optimized, vectorized solution that scales well to large datasets.

Download Natural Earth dataset

We will use the Natural Earth 110m resolution dataset, which provides global country boundaries.

Natural Earth Data is a public domain dataset that provides geographical data at various scales, including country boundaries, roads, cities, and more. Natural Earth Data can be downloaded for free from their website.

Download here:
https://www.naturalearthdata.com/downloads/110m-cultural-vectors/

Download the file:

1
ne_110m_admin_0_countries.shp

Unzip it and place it in your working directory.

Install GeoPandas

Before we dive into retrieving country names, let's first make sure that we have geopandas installed. To install geopandas, you can use pip for example:

1
pip install geopandas

Load the dataset

Let's utilize geopandas to explore the Natural Earth 110m cultural dataset:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import geopandas as gpd
from shapely.geometry import Point

# Load shapefile
gdf = gpd.read_file("110m_cultural/ne_110m_admin_0_countries.shp")

# Keep only relevant columns
gdf = gdf[['NAME', 'geometry']]

print(gdf.head())

How to Retrieve Country Names from Latitude/Longitude using GeoPandas and naturalearthdata ?
How to Retrieve Country Names from Latitude/Longitude using GeoPandas and naturalearthdata ?

Important:
Natural Earth data is already in geographic coordinates (EPSG:4326), which matches latitude/longitude.

Retrieve Country Names from Latitude/Longitude

Define a geographic point

To retrieve the country name for a specific point, we first need to define our point using its latitude and longitude values. Let's say we want to find out which country is located at the following coordinates.

In geopandas, we can use the shapely Point class to create our point:

1
2
3
4
target_lon = 2.2137
target_lat = 46.227

point = Point(target_lon, target_lat)

Method 1 — Basic approach (loop + contains)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
found_country = None

for _, row in gdf.iterrows():
    geom = row.geometry

    if geom.contains(point):
        found_country = row['NAME']
        break

print(found_country)

Output:

1
France

Note: Why duplicates can occur (e.g. "France" twice). Example with

1
2
3
4
5
6
7
8
9
for index, row in gdf.iterrows():
        if 'MultiPolygon' in row['geometry'].geom_type:
            for polygon in row['geometry'].geoms:
                if polygon.contains(point):
                    print( row['NAME'] )
        if 'Polygon' in row['geometry'].geom_type:
            polygon = row['geometry']
            if polygon.contains(point):
                print( row['NAME'] )

Countries like France are stored as MultiPolygon geometries:

  • mainland France
  • overseas territories (e.g., French Guiana, islands)

If you loop through each sub-geometry manually, you may match multiple polygons → duplicate outputs.

This is the best and most scalable approach.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Create GeoDataFrame for the point
point_gdf = gpd.GeoDataFrame(
    geometry=[Point(target_lon, target_lat)],
    crs="EPSG:4326"
)

# Ensure CRS consistency
gdf = gdf.to_crs(point_gdf.crs)

# Spatial join
result = gpd.sjoin(point_gdf, gdf, predicate="within")

print(result['NAME'].iloc[0])

Output:

1
France

Why this method is better

  • No loops (vectorized)
  • Uses spatial indexing (R-tree)
  • Avoids duplicate results
  • Much faster for large datasets

This is especially important if you:

  • process large datasets
  • run batch geolocation pipelines
  • integrate with H3 grids

Handle edge cases (border points)

Sometimes a point lies exactly on a boundary, and contains() returns False.

Use instead:

1
result = gpd.sjoin(point_gdf, gdf, predicate="intersects")

Method 3 — Multiple points at once

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
points = gpd.GeoDataFrame(
    geometry=[
        Point(2.21, 46.22),   # France
        Point(-95.71, 37.09), # USA
    ],
    crs="EPSG:4326"
)

result = gpd.sjoin(points, gdf, predicate="within")

print(result[['geometry', 'NAME']])

Output:

1
2
3
geometry                      NAME
0    POINT (2.21 46.22)                    France
1  POINT (-95.71 37.09)  United States of America
1
2
3
4
5
6
7
8
import folium

m = folium.Map(location=[46.2, 2.2], zoom_start=5)

folium.GeoJson(gdf).add_to(m)
folium.Marker([target_lat, target_lon]).add_to(m)

m

How to Retrieve Country Names from Latitude/Longitude using GeoPandas and naturalearthdata ?
How to Retrieve Country Names from Latitude/Longitude using GeoPandas and naturalearthdata ?

This is very useful for:

  • debugging
  • validating spatial joins
  • quick QA checks

Summary

Method Pros Cons
Loop + contains Simple Slow
Loop + break Fixes duplicates Still slow
Spatial join (sjoin) Fast, scalable, clean Requires GeoDataFrame

Other methods

See also your related article:
https://en.moonbooks.org/Articles/How-to-retrieve-country-name-for-a-given-latitude-and-longitude-using-python-/

References

Final tip (advanced users)

For high-performance pipelines (like with VIIRS / H3):

Consider:

  • pre-building spatial indexes
  • using vectorized joins on batches
  • combining with H3 indexing for coarse filtering

This can improve performance by orders of magnitude on large-scale datasets.

Image

of