From 41b230564095d9771c6bef12a6c87f1b4c6f0db0 Mon Sep 17 00:00:00 2001 From: Ilham Date: Fri, 22 May 2026 08:54:19 +0000 Subject: [PATCH 1/3] Add documentation for coordinate ordering (issue #1082) --- .../user_guide/geojson/coordinate_ordering.md | 130 ++++++++++++++++++ folium/features.py | 10 +- 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 docs/user_guide/geojson/coordinate_ordering.md diff --git a/docs/user_guide/geojson/coordinate_ordering.md b/docs/user_guide/geojson/coordinate_ordering.md new file mode 100644 index 0000000000..05d8ab3677 --- /dev/null +++ b/docs/user_guide/geojson/coordinate_ordering.md @@ -0,0 +1,130 @@ +--- +nbsphinx: hidden +--- +import folium + +# Coordinate Ordering in GeoJSON and Folium + +## Understanding the Problem + +Leaflet expects coordinates in [latitude, longitude] format, but the GeoJSON standard uses [longitude, latitude] format. When you work with both GeoJSON and Folium together, this difference can cause markers and features to appear in the wrong locations on your map. + +## Why the Difference Exists + +Leaflet.js was designed to use [lat, lon] order, which is common in geographic information systems. GeoJSON follows RFC 7946, which specifies [lon, lat] order to align with mathematical conventions (x before y). Understanding both conventions is important when combining data from different sources. + +## Example 1: Correct Coordinates + +When you have GeoJSON data with [lon, lat] coordinates and want to display it in Folium, the coordinates need to be reversed for the Folium.Map location parameter. + +```python +import folium + +# GeoJSON with correct [lon, lat] order +geojson_data = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-87.6298, 41.8781] # longitude, latitude (Chicago) + } +} + +# Folium map with correct [lat, lon] order +m = folium.Map(location=[41.8781, -87.6298], zoom_start=12) +folium.GeoJson(geojson_data).add_to(m) +m +``` + +Both represent the same location (Chicago), but notice how the coordinates are reversed between the two formats. + +## Example 2: Wrong Coordinate Order Problem + +If you accidentally put coordinates in [lat, lon] order in your GeoJSON data, the marker will appear in the wrong location. This is one of the most common mistakes when combining GeoJSON with Folium. + +```python +import folium + +# GeoJSON coordinates in wrong [lat, lon] order +geojson_data = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [41.8781, -87.6298] # WRONG: latitude, longitude + } +} + +m = folium.Map(location=[41.8781, -87.6298], zoom_start=12) +folium.GeoJson(geojson_data).add_to(m) +m +``` + +GeoJSON interprets the coordinates as [lon, lat], so when you provide [lat, lon] by mistake, the point ends up in the wrong location. In this case, the marker would appear far from Chicago. + +## Example 3: Converting Between Coordinate Orders + +If you have coordinates in [lat, lon] order but need them in [lon, lat] format for GeoJSON, you can flip them by swapping their positions. This is useful when working with data from sources that use different conventions. + +```python +import folium + +# Start with coordinates in [lat, lon] order +wrong_order = [41.8781, -87.6298] # latitude, longitude + +# Flip to [lon, lat] order for GeoJSON +correct_order = [wrong_order[1], wrong_order[0]] + +geojson_data = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": correct_order # now [lon, lat] + } +} + +m = folium.Map(location=[correct_order[1], correct_order[0]], zoom_start=12) +folium.GeoJson(geojson_data).add_to(m) +m +``` + +By swapping the coordinate positions, you convert from [lat, lon] to [lon, lat]. This approach works for individual coordinates, though for large datasets you may prefer automated solutions. + +## Example 4: Using GeoPandas + +GeoPandas provides a convenient way to handle coordinate ordering automatically. GeoPandas uses [lon, lat] order by default, which matches the GeoJSON specification. + +```python +import folium +import geopandas as gpd +from shapely.geometry import Point + +# Create a GeoDataFrame with points +# Shapely/GeoPandas uses [lon, lat] order automatically +points = [Point(-87.6298, 41.8781), Point(-118.2437, 34.0522)] +gdf = gpd.GeoDataFrame(geometry=points, crs="EPSG:4326") + +# When you pass a GeoDataFrame to folium.GeoJson, +# the coordinates are already in the correct [lon, lat] order +m = folium.Map(location=[41.8781, -87.6298], zoom_start=4) +folium.GeoJson(gdf).add_to(m) +m +``` + +GeoPandas handles the coordinate order for you, eliminating the need to manually track which format you're using. + +## How to Detect Swapped Coordinates + +If your markers are appearing in the wrong location, you likely have a coordinate ordering issue. Signs include: + +- Markers appear far from where you expected them +- Markers appear completely off the map or in the ocean +- The location makes sense geographically but is in the wrong hemisphere + +If you notice these issues, check whether you're mixing [lat, lon] and [lon, lat] formats. The fix is usually as simple as reversing your coordinate order. + +## Best Practices + +- Always verify the coordinate format of your data source before loading it into Folium +- Remember the key difference: GeoJSON uses [lon, lat], Folium uses [lat, lon] +- Use GeoPandas when possible to avoid manually managing coordinate order +- Test your first few markers to confirm they appear in the correct location before processing large datasets +- When combining data from multiple sources, document which coordinate order each source uses diff --git a/folium/features.py b/folium/features.py index 12d663cb05..1139497906 100644 --- a/folium/features.py +++ b/folium/features.py @@ -663,6 +663,14 @@ class GeoJson(Layer): {% endmacro %} """) # noqa +# noqa + +See Also +-------- +For information about coordinate ordering differences between Leaflet and GeoJSON, +see :doc:`/user_guide/geojson/coordinate_ordering` + +""" def __init__( self, @@ -672,7 +680,7 @@ def __init__( popup_keep_highlighted: bool = False, name: Optional[str] = None, overlay: bool = True, - control: bool = True, + control: bool = True, show: bool = True, smooth_factor: Optional[float] = None, tooltip: Union[str, Tooltip, "GeoJsonTooltip", None] = None, From ca4c4b8f54b3767f734774887572b896975ab587 Mon Sep 17 00:00:00 2001 From: Ilham Date: Sun, 24 May 2026 22:45:38 +0000 Subject: [PATCH 2/3] Fix docstring syntax error in GeoJson class --- folium/features.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/folium/features.py b/folium/features.py index 1139497906..4dd5b65cfc 100644 --- a/folium/features.py +++ b/folium/features.py @@ -536,6 +536,10 @@ class GeoJson(Layer): ... } >>> GeoJson(geojson, style_function=style_function) + See Also + -------- + For information about coordinate ordering differences between Leaflet and GeoJSON, + see :doc:`/user_guide/geojson/coordinate_ordering` """ _template = Template(""" @@ -663,14 +667,6 @@ class GeoJson(Layer): {% endmacro %} """) # noqa -# noqa - -See Also --------- -For information about coordinate ordering differences between Leaflet and GeoJSON, -see :doc:`/user_guide/geojson/coordinate_ordering` - -""" def __init__( self, @@ -856,7 +852,7 @@ def render(self, **kwargs): if self.highlight: self.highlight_map = mapper.get_highlight_map(self.highlight_function) super().render() - + TypeStyleMapping = dict[str, Union[str, list[Union[str, int]]]] From 8fd7b9feb3388b7d86eb6c9b20d523ea472b20f4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 22:55:08 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/user_guide/geojson/coordinate_ordering.md | 13 +++++-------- folium/features.py | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/docs/user_guide/geojson/coordinate_ordering.md b/docs/user_guide/geojson/coordinate_ordering.md index 05d8ab3677..3e5008a33f 100644 --- a/docs/user_guide/geojson/coordinate_ordering.md +++ b/docs/user_guide/geojson/coordinate_ordering.md @@ -25,8 +25,8 @@ geojson_data = { "type": "Feature", "geometry": { "type": "Point", - "coordinates": [-87.6298, 41.8781] # longitude, latitude (Chicago) - } + "coordinates": [-87.6298, 41.8781], # longitude, latitude (Chicago) + }, } # Folium map with correct [lat, lon] order @@ -49,8 +49,8 @@ geojson_data = { "type": "Feature", "geometry": { "type": "Point", - "coordinates": [41.8781, -87.6298] # WRONG: latitude, longitude - } + "coordinates": [41.8781, -87.6298], # WRONG: latitude, longitude + }, } m = folium.Map(location=[41.8781, -87.6298], zoom_start=12) @@ -75,10 +75,7 @@ correct_order = [wrong_order[1], wrong_order[0]] geojson_data = { "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": correct_order # now [lon, lat] - } + "geometry": {"type": "Point", "coordinates": correct_order}, # now [lon, lat] } m = folium.Map(location=[correct_order[1], correct_order[0]], zoom_start=12) diff --git a/folium/features.py b/folium/features.py index 4dd5b65cfc..5fc6cc6346 100644 --- a/folium/features.py +++ b/folium/features.py @@ -676,7 +676,7 @@ def __init__( popup_keep_highlighted: bool = False, name: Optional[str] = None, overlay: bool = True, - control: bool = True, + control: bool = True, show: bool = True, smooth_factor: Optional[float] = None, tooltip: Union[str, Tooltip, "GeoJsonTooltip", None] = None, @@ -852,7 +852,7 @@ def render(self, **kwargs): if self.highlight: self.highlight_map = mapper.get_highlight_map(self.highlight_function) super().render() - + TypeStyleMapping = dict[str, Union[str, list[Union[str, int]]]]