diff --git a/docs/history.rst b/docs/history.rst
index ffab8a210..c36b1d4d9 100644
--- a/docs/history.rst
+++ b/docs/history.rst
@@ -7,6 +7,7 @@ Latest
- BUG: Fix transformer list for 3D transformations in :class:`pyproj.transformer.TransformerGroup` (discussion #1072)
- ENH: Added authority, accuracy, and allow_ballpark kwargs to :class:`pyproj.transformer.TransformerGroup` (pull #1076)
- CLN: Remove deprecated `skip_equivalent` kwarg from transformers and `errcheck` kwarg from :meth:`pyproj.crs.CRS.from_cf` (pull #1077)
+- REF: use regex to process PROJ strings in :meth:`pyproj.crs.CRS.to_dict()`(pull #1086)
3.3.1
-------
diff --git a/pyproj/crs/crs.py b/pyproj/crs/crs.py
index 6bda8eff1..fad6cfa9c 100644
--- a/pyproj/crs/crs.py
+++ b/pyproj/crs/crs.py
@@ -37,6 +37,17 @@
from pyproj.exceptions import CRSError
from pyproj.geod import Geod
+_RE_PROJ_PARAM = re.compile(
+ r"""
+ \+ # parameter starts with '+' character
+ (?P\w+) # capture parameter name
+ \=? # match both key only and key-value parameters
+ (?P\S+)? # capture all characters up to next space (None if no value)
+ \s*? # consume remaining whitespace, if any
+""",
+ re.X,
+)
+
class CRSLocal(threading.local):
"""
@@ -573,7 +584,11 @@ def to_dict(self) -> dict:
"""
- def parse(val):
+ proj_string = self.to_proj4()
+ if proj_string is None:
+ return {}
+
+ def _parse(val):
if val.lower() == "true":
return True
if val.lower() == "false":
@@ -588,16 +603,15 @@ def parse(val):
pass
return _try_list_if_string(val)
- proj_string = self.to_proj4()
- if proj_string is None:
- return {}
-
- items = map(
- lambda kv: len(kv) == 2 and (kv[0], parse(kv[1])) or (kv[0], None),
- (part.lstrip("+").split("=", 1) for part in proj_string.strip().split()),
- )
+ proj_dict = {}
+ for param in _RE_PROJ_PARAM.finditer(proj_string):
+ key, value = param.groups()
+ if value is not None:
+ value = _parse(value)
+ if value is not False:
+ proj_dict[key] = value
- return {key: value for key, value in items if value is not False}
+ return proj_dict
def to_cf(
self,