diff --git a/ultraplot/constructor.py b/ultraplot/constructor.py index ea5909d54..b9036cf27 100644 --- a/ultraplot/constructor.py +++ b/ultraplot/constructor.py @@ -227,9 +227,13 @@ def _build_formatter_registry() -> dict[str, object]: if hasattr(mdates, "ConciseDateFormatter"): registry["concise"] = mdates.ConciseDateFormatter if _version_cartopy >= "0.18": - registry["dms"] = partial(pticker.DegreeFormatter, dms=True) - registry["dmslon"] = partial(pticker.LongitudeFormatter, dms=True) - registry["dmslat"] = partial(pticker.LatitudeFormatter, dms=True) + # Cartopy defaults to U+2032/U+2033 prime glyphs for minutes/seconds. + # TeX Gyre Heros, UltraPlot's default sans-serif font, does not contain + # those glyphs, so use ASCII symbols for the built-in DMS presets. + dms_kwargs = {"dms": True, "minute_symbol": "'", "second_symbol": '"'} + registry["dms"] = partial(pticker.DegreeFormatter, **dms_kwargs) + registry["dmslon"] = partial(pticker.LongitudeFormatter, **dms_kwargs) + registry["dmslat"] = partial(pticker.LatitudeFormatter, **dms_kwargs) return registry diff --git a/ultraplot/tests/test_geographic.py b/ultraplot/tests/test_geographic.py index 7a3c27844..ea1edd671 100644 --- a/ultraplot/tests/test_geographic.py +++ b/ultraplot/tests/test_geographic.py @@ -306,6 +306,15 @@ def test_lon0_shifts(): uplt.close(fig) +def test_dms_formatter_symbols_are_default_font_safe(): + formatter = uplt.Formatter("dmslon") + label = formatter(1 + 1 / 60 + 1 / 3600) + assert "\N{PRIME}" not in label + assert "\N{DOUBLE PRIME}" not in label + assert "'" in label + assert '"' in label + + @pytest.mark.parametrize( "layout, expectations", [