We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ENH: New geoaccessor to calculate two coordinates distance on earth
<!--
Thanks for contributing a pull request!

Please follow these standard acronyms to start the commit message:

- ENH: enhancement
- BUG: bug fix
- DOC: documentation
- TYP: type annotations
- TST: addition or modification of tests
- MAINT: maintenance commit (refactoring, typos, etc.)
- BLD: change related to building
- REL: related to releasing
- API: an (incompatible) API change
- DEP: deprecate something, or remove a deprecated object
- DEV: development tool or utility
- REV: revert an earlier commit
- PERF: performance improvement
- BOT: always commit via a bot
- CI: related to CI or CD
- CLN: Code cleanup
-->

- [ ] closes #xxxx
- [ ] whatsnew entry


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `dtoolkit/geoaccessor/geoseries/geodistance.py`
Content:
```
1 from __future__ import annotations
2 
3 from warnings import warn
4 
5 import geopandas as gpd
6 import numpy as np
7 import pandas as pd
8 from shapely.geometry.base import BaseGeometry
9 
10 from dtoolkit.geoaccessor.register import register_geoseries_method
11 from dtoolkit.util._exception import find_stack_level
12 
13 
14 @register_geoseries_method
15 def geodistance(
16     s: gpd.GeoSeries,
17     /,
18     other: BaseGeometry | gpd.GeoSeries | gpd.GeoDataFrame,
19     align: bool = True,
20     radius: float = 6371008.7714150598,
21 ) -> pd.Series:
22     """
23     Returns a ``Series`` containing the `great-circle`__ distance to aligned other.
24 
25     __ https://en.wikipedia.org/wiki/Great-circle_distance
26 
27     The algorithm uses the Vincenty formula which is more accurate than the Haversine
28     formula.
29 
30     .. math::
31 
32         D(x, y) = \\arctan[
33             \\frac{
34                 \\sqrt{
35                     (
36                         \\cos(y_1) \\sin(y_2)
37                         - \\sin(y_1) \\cos(y_2) \\cos(x_2 - x_1)
38                     )^2
39                     + (\\cos(y_2) \\sin(x_2 - x_1))^2
40                 }
41             }{
42                 \\sin(y_1) \\sin(y_2)
43                 + \\cos(y_1) \\cos(y_2) \\cos(x_2 - x_1)
44             }
45         ]
46 
47     Parameters
48     ----------
49     other : BaseGeometry, GeoSeries, or GeoDataFrame
50 
51     align : bool, default True
52         If True, automatically aligns GeoSeries based on their indices. If False,
53         the order of elements is preserved.
54 
55     radius : float, default 6371008.7714150598
56         Great-circle distance uses a spherical model of the earth, using the mean earth
57         radius as defined by the International Union of Geodesy and Geophysics,
58         (2\\ *a* + *b*)/3 = 6371008.7714150598 meters for WGS-84.
59 
60     Returns
61     -------
62     Series
63         The values are the great-circle distances and its unit is meters.
64 
65     Raises
66     ------
67     ValueError
68         If the CRS is not ``ESGP:4326``.
69 
70     TypeError
71         If the other is not a ``BaseGeometry``, ``GeoSeries``, or ``GeoDataFrame``.
72 
73     See Also
74     --------
75     geopandas.GeoSeries.distance
76     dtoolkit.geoaccessor.geoseries.geodistance
77     dtoolkit.geoaccessor.geoseries.geodistance_matrix
78     dtoolkit.geoaccessor.geodataframe.geodistance
79     dtoolkit.geoaccessor.geodataframe.geodistance_matrix
80 
81     Notes
82     -----
83     - Currently, only supports Point geometry.
84     - The geodesic distance is the shortest distance on the surface of an ellipsoidal
85       model of the earth. Resulting in an error of up to about 0.5%.
86 
87     Examples
88     --------
89     >>> import dtoolkit.geoaccessor
90     >>> import pandas as pd
91     >>> import geopandas as gpd
92     >>> from shapely.geometry import Point
93     >>> df = (
94     ...     pd.DataFrame({"x": [122, 100], "y":[55, 1]})
95     ...     .from_xy("x", "y", crs=4326)
96     ... )
97     >>> df
98          x   y                    geometry
99     0  122  55  POINT (122.00000 55.00000)
100     1  100   1   POINT (100.00000 1.00000)
101     >>> df.geodistance(Point(120, 30)) / 1e6
102     0    2.784435
103     1    3.855604
104     dtype: float64
105 
106     Calculate the great-circle distance of corresponding points.
107 
108     >>> s = gpd.GeoSeries([Point(120, 30), Point(120, 50)], index=[1, 2], crs=4326)
109     >>> s
110     1    POINT (120.00000 30.00000)
111     2    POINT (120.00000 50.00000)
112     dtype: geometry
113     >>> df.geodistance(s) / 1e6
114     0         NaN
115     1    3.855604
116     2         NaN
117     dtype: float64
118     >>> df.geodistance(s, align=False) / 1e6
119     0    2.784435
120     1    5.768885
121     dtype: float64
122     """
123 
124     if s.crs != 4326:
125         raise ValueError(f"Only support 'EPSG:4326' CRS, but got {s.crs!r}.")
126     if not isinstance(other, (BaseGeometry, gpd.base.GeoPandasBase)):
127         raise TypeError(f"Unknown type: {type(other).__name__!r}.")
128 
129     if isinstance(other, gpd.base.GeoPandasBase):
130         if other.crs != 4326:
131             raise ValueError(f"Only support 'EPSG:4326' CRS, but got {other.crs!r}.")
132 
133         s = s.geometry
134         if align and not s.index.equals(other.index):
135             warn("The indices are different.", stacklevel=find_stack_level())
136             s, other = s.align(other)
137         else:
138             other = other.geometry
139 
140     return pd.Series(
141         distance(
142             s.geometry.x.to_numpy(),
143             s.geometry.y.to_numpy(),
144             other.x if isinstance(other, BaseGeometry) else other.x.to_numpy(),
145             other.y if isinstance(other, BaseGeometry) else other.y.to_numpy(),
146             radius=radius,
147         ),
148         index=s.index,
149     )
150 
151 
152 # based on https://github.com/geopy/geopy geopy/distance.py::great_circle.measure
153 def distance(
154     lng1: np.ndarray | float,
155     lat1: np.ndarray | float,
156     lng2: np.ndarray | float,
157     lat2: np.ndarray | float,
158     radius: float,
159 ) -> np.ndarray:
160     lng1, lat1, lng2, lat2 = map(np.radians, (lng1, lat1, lng2, lat2))
161     sin_lat1, cos_lat1 = np.sin(lat1), np.cos(lat1)
162     sin_lat2, cos_lat2 = np.sin(lat2), np.cos(lat2)
163 
164     delta_lng = lng2 - lng1
165     cos_delta_lng, sin_delta_lng = np.cos(delta_lng), np.sin(delta_lng)
166 
167     return radius * np.arctan2(
168         np.sqrt(
169             (cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_delta_lng) ** 2
170             + (cos_lat2 * sin_delta_lng) ** 2,
171         ),
172         sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos_delta_lng,
173     )
174
```

Path: `dtoolkit/geoaccessor/geoseries/geodistance_matrix.py`
Content:
```
1 from __future__ import annotations
2 
3 import geopandas as gpd
4 import numpy as np
5 import pandas as pd
6 
7 from dtoolkit.geoaccessor.register import register_geoseries_method
8 
9 
10 @register_geoseries_method
11 def geodistance_matrix(
12     s: gpd.GeoSeries,
13     /,
14     other: gpd.GeoSeries | gpd.GeoDataFrame | None = None,
15     radius: float = 6371008.7714150598,
16 ) -> pd.DataFrame:
17     """
18     Returns a ``DataFrame`` containing the `great-circle`__ distances matrix between in
19     ``s`` and ``other`` via haversine formula.
20 
21     __ https://en.wikipedia.org/wiki/Great-circle_distance
22 
23     .. math::
24 
25         D(x, y) = 2 \\arcsin [
26             \\sqrt{
27                 \\sin^2 ((x_1 - y_1) / 2)
28                 + \\cos(x_1) \\cos(y_1) \\sin^2 ((x_2 - y_2) / 2)
29             }
30         ]
31 
32     Parameters
33     ----------
34     other : GeoSeries, or GeoDataFrame, default None
35         If None, uses ``other=s``.
36 
37     radius : float, default 6371008.7714150598
38         Great-circle distance uses a spherical model of the earth, using the mean earth
39         radius as defined by the International Union of Geodesy and Geophysics,
40         (2\\ *a* + *b*)/3 = 6371008.7714150598 meters for WGS-84.
41 
42     Returns
43     -------
44     DataFrame
45         - The index and columns are the same as the index of ``s`` and ``other``.
46         - The values are the great-circle distances and its unit is meters.
47 
48     Raises
49     ------
50     ModuleNotFoundError
51         If don't have module named 'sklearn'.
52 
53     ValueError
54         If the CRS is not ``ESGP:4326``.
55 
56     See Also
57     --------
58     sklearn.metrics.pairwise.haversine_distances
59     dtoolkit.geoaccessor.geoseries.geodistance
60     dtoolkit.geoaccessor.geoseries.geodistance_matrix
61     dtoolkit.geoaccessor.geodataframe.geodistance
62     dtoolkit.geoaccessor.geodataframe.geodistance_matrix
63 
64     Notes
65     -----
66     - Currently, only supports Point geometry.
67     - The great-circle distance is the angular distance between two points on the
68       surface of a sphere. As the Earth is nearly spherical, the haversine formula
69       provides a good approximation of the distance between two points of the Earth
70       surface, with a less than 1% error on average.
71 
72     Examples
73     --------
74     >>> import dtoolkit.geoaccessor
75     >>> df = pd.DataFrame(
76     ...     {
77     ...         "x": [120, 122, 100],
78     ...         "y":[30, 55, 1],
79     ...     },
80     ... ).from_xy("x", "y", crs=4326)
81     >>> df
82          x   y                    geometry
83     0  120  30  POINT (120.00000 30.00000)
84     1  122  55  POINT (122.00000 55.00000)
85     2  100   1   POINT (100.00000 1.00000)
86     >>> other = pd.DataFrame(
87     ...     {
88     ...         "x": [120, 110],
89     ...         "y":[30, 40],
90     ...     },
91     ... ).from_xy("x", "y", crs=4326)
92     >>> other
93          x   y                    geometry
94     0  120  30  POINT (120.00000 30.00000)
95     1  110  40  POINT (110.00000 40.00000)
96     >>> df.geodistance_matrix(other)
97                   0             1
98     0  0.000000e+00  1.203540e+06
99     1  1.439971e+06  1.511958e+06
100     2  2.418544e+06  1.522752e+06
101     """
102     from sklearn.metrics.pairwise import haversine_distances
103 
104     if s.crs != 4326:
105         raise ValueError(f"Only support 'EPSG:4326' CRS, but got {s.crs!r}.")
106     if other is not None and other.crs != 4326:
107         raise ValueError(f"Only support 'EPSG:4326' CRS, but got {other.crs!r}.")
108 
109     X = np.radians(np.stack((s.x, s.y), axis=1))
110     Y = np.radians(np.stack((other.x, other.y), axis=1)) if other is not None else other
111     return pd.DataFrame(
112         radius * haversine_distances(X, Y),
113         index=s.index,
114         columns=other.index,
115     )
116
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/dtoolkit/geoaccessor/geoseries/geodistance.py b/dtoolkit/geoaccessor/geoseries/geodistance.py
--- a/dtoolkit/geoaccessor/geoseries/geodistance.py
+++ b/dtoolkit/geoaccessor/geoseries/geodistance.py
@@ -134,8 +134,9 @@
         if align and not s.index.equals(other.index):
             warn("The indices are different.", stacklevel=find_stack_level())
             s, other = s.align(other)
-        else:
-            other = other.geometry
+
+        # Force convert to GeoSeries
+        other = other.geometry
 
     return pd.Series(
         distance(
diff --git a/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py b/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py
--- a/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py
+++ b/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py
@@ -103,8 +103,13 @@
 
     if s.crs != 4326:
         raise ValueError(f"Only support 'EPSG:4326' CRS, but got {s.crs!r}.")
-    if other is not None and other.crs != 4326:
-        raise ValueError(f"Only support 'EPSG:4326' CRS, but got {other.crs!r}.")
+
+    if isinstance(other, gpd.base.GeoPandasBase):
+        if other.crs != 4326:
+            raise ValueError(f"Only support 'EPSG:4326' CRS, but got {other.crs!r}.")
+
+        # Force convert to GeoSeries
+        other = other.geometry
 
     X = np.radians(np.stack((s.x, s.y), axis=1))
     Y = np.radians(np.stack((other.x, other.y), axis=1)) if other is not None else other

{"golden_diff": "diff --git a/dtoolkit/geoaccessor/geoseries/geodistance.py b/dtoolkit/geoaccessor/geoseries/geodistance.py\n--- a/dtoolkit/geoaccessor/geoseries/geodistance.py\n+++ b/dtoolkit/geoaccessor/geoseries/geodistance.py\n@@ -134,8 +134,9 @@\n         if align and not s.index.equals(other.index):\n             warn(\"The indices are different.\", stacklevel=find_stack_level())\n             s, other = s.align(other)\n-        else:\n-            other = other.geometry\n+\n+        # Force convert to GeoSeries\n+        other = other.geometry\n \n     return pd.Series(\n         distance(\ndiff --git a/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py b/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py\n--- a/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py\n+++ b/dtoolkit/geoaccessor/geoseries/geodistance_matrix.py\n@@ -103,8 +103,13 @@\n \n     if s.crs != 4326:\n         raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {s.crs!r}.\")\n-    if other is not None and other.crs != 4326:\n-        raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {other.crs!r}.\")\n+\n+    if isinstance(other, gpd.base.GeoPandasBase):\n+        if other.crs != 4326:\n+            raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {other.crs!r}.\")\n+\n+        # Force convert to GeoSeries\n+        other = other.geometry\n \n     X = np.radians(np.stack((s.x, s.y), axis=1))\n     Y = np.radians(np.stack((other.x, other.y), axis=1)) if other is not None else other\n", "issue": "ENH: New geoaccessor to calculate two coordinates distance on earth\n<!--\r\nThanks for contributing a pull request!\r\n\r\nPlease follow these standard acronyms to start the commit message:\r\n\r\n- ENH: enhancement\r\n- BUG: bug fix\r\n- DOC: documentation\r\n- TYP: type annotations\r\n- TST: addition or modification of tests\r\n- MAINT: maintenance commit (refactoring, typos, etc.)\r\n- BLD: change related to building\r\n- REL: related to releasing\r\n- API: an (incompatible) API change\r\n- DEP: deprecate something, or remove a deprecated object\r\n- DEV: development tool or utility\r\n- REV: revert an earlier commit\r\n- PERF: performance improvement\r\n- BOT: always commit via a bot\r\n- CI: related to CI or CD\r\n- CLN: Code cleanup\r\n-->\r\n\r\n- [ ] closes #xxxx\r\n- [ ] whatsnew entry\r\n\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom warnings import warn\n\nimport geopandas as gpd\nimport numpy as np\nimport pandas as pd\nfrom shapely.geometry.base import BaseGeometry\n\nfrom dtoolkit.geoaccessor.register import register_geoseries_method\nfrom dtoolkit.util._exception import find_stack_level\n\n\n@register_geoseries_method\ndef geodistance(\n    s: gpd.GeoSeries,\n    /,\n    other: BaseGeometry | gpd.GeoSeries | gpd.GeoDataFrame,\n    align: bool = True,\n    radius: float = 6371008.7714150598,\n) -> pd.Series:\n    \"\"\"\n    Returns a ``Series`` containing the `great-circle`__ distance to aligned other.\n\n    __ https://en.wikipedia.org/wiki/Great-circle_distance\n\n    The algorithm uses the Vincenty formula which is more accurate than the Haversine\n    formula.\n\n    .. math::\n\n        D(x, y) = \\\\arctan[\n            \\\\frac{\n                \\\\sqrt{\n                    (\n                        \\\\cos(y_1) \\\\sin(y_2)\n                        - \\\\sin(y_1) \\\\cos(y_2) \\\\cos(x_2 - x_1)\n                    )^2\n                    + (\\\\cos(y_2) \\\\sin(x_2 - x_1))^2\n                }\n            }{\n                \\\\sin(y_1) \\\\sin(y_2)\n                + \\\\cos(y_1) \\\\cos(y_2) \\\\cos(x_2 - x_1)\n            }\n        ]\n\n    Parameters\n    ----------\n    other : BaseGeometry, GeoSeries, or GeoDataFrame\n\n    align : bool, default True\n        If True, automatically aligns GeoSeries based on their indices. If False,\n        the order of elements is preserved.\n\n    radius : float, default 6371008.7714150598\n        Great-circle distance uses a spherical model of the earth, using the mean earth\n        radius as defined by the International Union of Geodesy and Geophysics,\n        (2\\\\ *a* + *b*)/3 = 6371008.7714150598 meters for WGS-84.\n\n    Returns\n    -------\n    Series\n        The values are the great-circle distances and its unit is meters.\n\n    Raises\n    ------\n    ValueError\n        If the CRS is not ``ESGP:4326``.\n\n    TypeError\n        If the other is not a ``BaseGeometry``, ``GeoSeries``, or ``GeoDataFrame``.\n\n    See Also\n    --------\n    geopandas.GeoSeries.distance\n    dtoolkit.geoaccessor.geoseries.geodistance\n    dtoolkit.geoaccessor.geoseries.geodistance_matrix\n    dtoolkit.geoaccessor.geodataframe.geodistance\n    dtoolkit.geoaccessor.geodataframe.geodistance_matrix\n\n    Notes\n    -----\n    - Currently, only supports Point geometry.\n    - The geodesic distance is the shortest distance on the surface of an ellipsoidal\n      model of the earth. Resulting in an error of up to about 0.5%.\n\n    Examples\n    --------\n    >>> import dtoolkit.geoaccessor\n    >>> import pandas as pd\n    >>> import geopandas as gpd\n    >>> from shapely.geometry import Point\n    >>> df = (\n    ...     pd.DataFrame({\"x\": [122, 100], \"y\":[55, 1]})\n    ...     .from_xy(\"x\", \"y\", crs=4326)\n    ... )\n    >>> df\n         x   y                    geometry\n    0  122  55  POINT (122.00000 55.00000)\n    1  100   1   POINT (100.00000 1.00000)\n    >>> df.geodistance(Point(120, 30)) / 1e6\n    0    2.784435\n    1    3.855604\n    dtype: float64\n\n    Calculate the great-circle distance of corresponding points.\n\n    >>> s = gpd.GeoSeries([Point(120, 30), Point(120, 50)], index=[1, 2], crs=4326)\n    >>> s\n    1    POINT (120.00000 30.00000)\n    2    POINT (120.00000 50.00000)\n    dtype: geometry\n    >>> df.geodistance(s) / 1e6\n    0         NaN\n    1    3.855604\n    2         NaN\n    dtype: float64\n    >>> df.geodistance(s, align=False) / 1e6\n    0    2.784435\n    1    5.768885\n    dtype: float64\n    \"\"\"\n\n    if s.crs != 4326:\n        raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {s.crs!r}.\")\n    if not isinstance(other, (BaseGeometry, gpd.base.GeoPandasBase)):\n        raise TypeError(f\"Unknown type: {type(other).__name__!r}.\")\n\n    if isinstance(other, gpd.base.GeoPandasBase):\n        if other.crs != 4326:\n            raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {other.crs!r}.\")\n\n        s = s.geometry\n        if align and not s.index.equals(other.index):\n            warn(\"The indices are different.\", stacklevel=find_stack_level())\n            s, other = s.align(other)\n        else:\n            other = other.geometry\n\n    return pd.Series(\n        distance(\n            s.geometry.x.to_numpy(),\n            s.geometry.y.to_numpy(),\n            other.x if isinstance(other, BaseGeometry) else other.x.to_numpy(),\n            other.y if isinstance(other, BaseGeometry) else other.y.to_numpy(),\n            radius=radius,\n        ),\n        index=s.index,\n    )\n\n\n# based on https://github.com/geopy/geopy geopy/distance.py::great_circle.measure\ndef distance(\n    lng1: np.ndarray | float,\n    lat1: np.ndarray | float,\n    lng2: np.ndarray | float,\n    lat2: np.ndarray | float,\n    radius: float,\n) -> np.ndarray:\n    lng1, lat1, lng2, lat2 = map(np.radians, (lng1, lat1, lng2, lat2))\n    sin_lat1, cos_lat1 = np.sin(lat1), np.cos(lat1)\n    sin_lat2, cos_lat2 = np.sin(lat2), np.cos(lat2)\n\n    delta_lng = lng2 - lng1\n    cos_delta_lng, sin_delta_lng = np.cos(delta_lng), np.sin(delta_lng)\n\n    return radius * np.arctan2(\n        np.sqrt(\n            (cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_delta_lng) ** 2\n            + (cos_lat2 * sin_delta_lng) ** 2,\n        ),\n        sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos_delta_lng,\n    )\n", "path": "dtoolkit/geoaccessor/geoseries/geodistance.py"}, {"content": "from __future__ import annotations\n\nimport geopandas as gpd\nimport numpy as np\nimport pandas as pd\n\nfrom dtoolkit.geoaccessor.register import register_geoseries_method\n\n\n@register_geoseries_method\ndef geodistance_matrix(\n    s: gpd.GeoSeries,\n    /,\n    other: gpd.GeoSeries | gpd.GeoDataFrame | None = None,\n    radius: float = 6371008.7714150598,\n) -> pd.DataFrame:\n    \"\"\"\n    Returns a ``DataFrame`` containing the `great-circle`__ distances matrix between in\n    ``s`` and ``other`` via haversine formula.\n\n    __ https://en.wikipedia.org/wiki/Great-circle_distance\n\n    .. math::\n\n        D(x, y) = 2 \\\\arcsin [\n            \\\\sqrt{\n                \\\\sin^2 ((x_1 - y_1) / 2)\n                + \\\\cos(x_1) \\\\cos(y_1) \\\\sin^2 ((x_2 - y_2) / 2)\n            }\n        ]\n\n    Parameters\n    ----------\n    other : GeoSeries, or GeoDataFrame, default None\n        If None, uses ``other=s``.\n\n    radius : float, default 6371008.7714150598\n        Great-circle distance uses a spherical model of the earth, using the mean earth\n        radius as defined by the International Union of Geodesy and Geophysics,\n        (2\\\\ *a* + *b*)/3 = 6371008.7714150598 meters for WGS-84.\n\n    Returns\n    -------\n    DataFrame\n        - The index and columns are the same as the index of ``s`` and ``other``.\n        - The values are the great-circle distances and its unit is meters.\n\n    Raises\n    ------\n    ModuleNotFoundError\n        If don't have module named 'sklearn'.\n\n    ValueError\n        If the CRS is not ``ESGP:4326``.\n\n    See Also\n    --------\n    sklearn.metrics.pairwise.haversine_distances\n    dtoolkit.geoaccessor.geoseries.geodistance\n    dtoolkit.geoaccessor.geoseries.geodistance_matrix\n    dtoolkit.geoaccessor.geodataframe.geodistance\n    dtoolkit.geoaccessor.geodataframe.geodistance_matrix\n\n    Notes\n    -----\n    - Currently, only supports Point geometry.\n    - The great-circle distance is the angular distance between two points on the\n      surface of a sphere. As the Earth is nearly spherical, the haversine formula\n      provides a good approximation of the distance between two points of the Earth\n      surface, with a less than 1% error on average.\n\n    Examples\n    --------\n    >>> import dtoolkit.geoaccessor\n    >>> df = pd.DataFrame(\n    ...     {\n    ...         \"x\": [120, 122, 100],\n    ...         \"y\":[30, 55, 1],\n    ...     },\n    ... ).from_xy(\"x\", \"y\", crs=4326)\n    >>> df\n         x   y                    geometry\n    0  120  30  POINT (120.00000 30.00000)\n    1  122  55  POINT (122.00000 55.00000)\n    2  100   1   POINT (100.00000 1.00000)\n    >>> other = pd.DataFrame(\n    ...     {\n    ...         \"x\": [120, 110],\n    ...         \"y\":[30, 40],\n    ...     },\n    ... ).from_xy(\"x\", \"y\", crs=4326)\n    >>> other\n         x   y                    geometry\n    0  120  30  POINT (120.00000 30.00000)\n    1  110  40  POINT (110.00000 40.00000)\n    >>> df.geodistance_matrix(other)\n                  0             1\n    0  0.000000e+00  1.203540e+06\n    1  1.439971e+06  1.511958e+06\n    2  2.418544e+06  1.522752e+06\n    \"\"\"\n    from sklearn.metrics.pairwise import haversine_distances\n\n    if s.crs != 4326:\n        raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {s.crs!r}.\")\n    if other is not None and other.crs != 4326:\n        raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {other.crs!r}.\")\n\n    X = np.radians(np.stack((s.x, s.y), axis=1))\n    Y = np.radians(np.stack((other.x, other.y), axis=1)) if other is not None else other\n    return pd.DataFrame(\n        radius * haversine_distances(X, Y),\n        index=s.index,\n        columns=other.index,\n    )\n", "path": "dtoolkit/geoaccessor/geoseries/geodistance_matrix.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom warnings import warn\n\nimport geopandas as gpd\nimport numpy as np\nimport pandas as pd\nfrom shapely.geometry.base import BaseGeometry\n\nfrom dtoolkit.geoaccessor.register import register_geoseries_method\nfrom dtoolkit.util._exception import find_stack_level\n\n\n@register_geoseries_method\ndef geodistance(\n    s: gpd.GeoSeries,\n    /,\n    other: BaseGeometry | gpd.GeoSeries | gpd.GeoDataFrame,\n    align: bool = True,\n    radius: float = 6371008.7714150598,\n) -> pd.Series:\n    \"\"\"\n    Returns a ``Series`` containing the `great-circle`__ distance to aligned other.\n\n    __ https://en.wikipedia.org/wiki/Great-circle_distance\n\n    The algorithm uses the Vincenty formula which is more accurate than the Haversine\n    formula.\n\n    .. math::\n\n        D(x, y) = \\\\arctan[\n            \\\\frac{\n                \\\\sqrt{\n                    (\n                        \\\\cos(y_1) \\\\sin(y_2)\n                        - \\\\sin(y_1) \\\\cos(y_2) \\\\cos(x_2 - x_1)\n                    )^2\n                    + (\\\\cos(y_2) \\\\sin(x_2 - x_1))^2\n                }\n            }{\n                \\\\sin(y_1) \\\\sin(y_2)\n                + \\\\cos(y_1) \\\\cos(y_2) \\\\cos(x_2 - x_1)\n            }\n        ]\n\n    Parameters\n    ----------\n    other : BaseGeometry, GeoSeries, or GeoDataFrame\n\n    align : bool, default True\n        If True, automatically aligns GeoSeries based on their indices. If False,\n        the order of elements is preserved.\n\n    radius : float, default 6371008.7714150598\n        Great-circle distance uses a spherical model of the earth, using the mean earth\n        radius as defined by the International Union of Geodesy and Geophysics,\n        (2\\\\ *a* + *b*)/3 = 6371008.7714150598 meters for WGS-84.\n\n    Returns\n    -------\n    Series\n        The values are the great-circle distances and its unit is meters.\n\n    Raises\n    ------\n    ValueError\n        If the CRS is not ``ESGP:4326``.\n\n    TypeError\n        If the other is not a ``BaseGeometry``, ``GeoSeries``, or ``GeoDataFrame``.\n\n    See Also\n    --------\n    geopandas.GeoSeries.distance\n    dtoolkit.geoaccessor.geoseries.geodistance\n    dtoolkit.geoaccessor.geoseries.geodistance_matrix\n    dtoolkit.geoaccessor.geodataframe.geodistance\n    dtoolkit.geoaccessor.geodataframe.geodistance_matrix\n\n    Notes\n    -----\n    - Currently, only supports Point geometry.\n    - The geodesic distance is the shortest distance on the surface of an ellipsoidal\n      model of the earth. Resulting in an error of up to about 0.5%.\n\n    Examples\n    --------\n    >>> import dtoolkit.geoaccessor\n    >>> import pandas as pd\n    >>> import geopandas as gpd\n    >>> from shapely.geometry import Point\n    >>> df = (\n    ...     pd.DataFrame({\"x\": [122, 100], \"y\":[55, 1]})\n    ...     .from_xy(\"x\", \"y\", crs=4326)\n    ... )\n    >>> df\n         x   y                    geometry\n    0  122  55  POINT (122.00000 55.00000)\n    1  100   1   POINT (100.00000 1.00000)\n    >>> df.geodistance(Point(120, 30)) / 1e6\n    0    2.784435\n    1    3.855604\n    dtype: float64\n\n    Calculate the great-circle distance of corresponding points.\n\n    >>> s = gpd.GeoSeries([Point(120, 30), Point(120, 50)], index=[1, 2], crs=4326)\n    >>> s\n    1    POINT (120.00000 30.00000)\n    2    POINT (120.00000 50.00000)\n    dtype: geometry\n    >>> df.geodistance(s) / 1e6\n    0         NaN\n    1    3.855604\n    2         NaN\n    dtype: float64\n    >>> df.geodistance(s, align=False) / 1e6\n    0    2.784435\n    1    5.768885\n    dtype: float64\n    \"\"\"\n\n    if s.crs != 4326:\n        raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {s.crs!r}.\")\n    if not isinstance(other, (BaseGeometry, gpd.base.GeoPandasBase)):\n        raise TypeError(f\"Unknown type: {type(other).__name__!r}.\")\n\n    if isinstance(other, gpd.base.GeoPandasBase):\n        if other.crs != 4326:\n            raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {other.crs!r}.\")\n\n        s = s.geometry\n        if align and not s.index.equals(other.index):\n            warn(\"The indices are different.\", stacklevel=find_stack_level())\n            s, other = s.align(other)\n\n        # Force convert to GeoSeries\n        other = other.geometry\n\n    return pd.Series(\n        distance(\n            s.geometry.x.to_numpy(),\n            s.geometry.y.to_numpy(),\n            other.x if isinstance(other, BaseGeometry) else other.x.to_numpy(),\n            other.y if isinstance(other, BaseGeometry) else other.y.to_numpy(),\n            radius=radius,\n        ),\n        index=s.index,\n    )\n\n\n# based on https://github.com/geopy/geopy geopy/distance.py::great_circle.measure\ndef distance(\n    lng1: np.ndarray | float,\n    lat1: np.ndarray | float,\n    lng2: np.ndarray | float,\n    lat2: np.ndarray | float,\n    radius: float,\n) -> np.ndarray:\n    lng1, lat1, lng2, lat2 = map(np.radians, (lng1, lat1, lng2, lat2))\n    sin_lat1, cos_lat1 = np.sin(lat1), np.cos(lat1)\n    sin_lat2, cos_lat2 = np.sin(lat2), np.cos(lat2)\n\n    delta_lng = lng2 - lng1\n    cos_delta_lng, sin_delta_lng = np.cos(delta_lng), np.sin(delta_lng)\n\n    return radius * np.arctan2(\n        np.sqrt(\n            (cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_delta_lng) ** 2\n            + (cos_lat2 * sin_delta_lng) ** 2,\n        ),\n        sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos_delta_lng,\n    )\n", "path": "dtoolkit/geoaccessor/geoseries/geodistance.py"}, {"content": "from __future__ import annotations\n\nimport geopandas as gpd\nimport numpy as np\nimport pandas as pd\n\nfrom dtoolkit.geoaccessor.register import register_geoseries_method\n\n\n@register_geoseries_method\ndef geodistance_matrix(\n    s: gpd.GeoSeries,\n    /,\n    other: gpd.GeoSeries | gpd.GeoDataFrame | None = None,\n    radius: float = 6371008.7714150598,\n) -> pd.DataFrame:\n    \"\"\"\n    Returns a ``DataFrame`` containing the `great-circle`__ distances matrix between in\n    ``s`` and ``other`` via haversine formula.\n\n    __ https://en.wikipedia.org/wiki/Great-circle_distance\n\n    .. math::\n\n        D(x, y) = 2 \\\\arcsin [\n            \\\\sqrt{\n                \\\\sin^2 ((x_1 - y_1) / 2)\n                + \\\\cos(x_1) \\\\cos(y_1) \\\\sin^2 ((x_2 - y_2) / 2)\n            }\n        ]\n\n    Parameters\n    ----------\n    other : GeoSeries, or GeoDataFrame, default None\n        If None, uses ``other=s``.\n\n    radius : float, default 6371008.7714150598\n        Great-circle distance uses a spherical model of the earth, using the mean earth\n        radius as defined by the International Union of Geodesy and Geophysics,\n        (2\\\\ *a* + *b*)/3 = 6371008.7714150598 meters for WGS-84.\n\n    Returns\n    -------\n    DataFrame\n        - The index and columns are the same as the index of ``s`` and ``other``.\n        - The values are the great-circle distances and its unit is meters.\n\n    Raises\n    ------\n    ModuleNotFoundError\n        If don't have module named 'sklearn'.\n\n    ValueError\n        If the CRS is not ``ESGP:4326``.\n\n    See Also\n    --------\n    sklearn.metrics.pairwise.haversine_distances\n    dtoolkit.geoaccessor.geoseries.geodistance\n    dtoolkit.geoaccessor.geoseries.geodistance_matrix\n    dtoolkit.geoaccessor.geodataframe.geodistance\n    dtoolkit.geoaccessor.geodataframe.geodistance_matrix\n\n    Notes\n    -----\n    - Currently, only supports Point geometry.\n    - The great-circle distance is the angular distance between two points on the\n      surface of a sphere. As the Earth is nearly spherical, the haversine formula\n      provides a good approximation of the distance between two points of the Earth\n      surface, with a less than 1% error on average.\n\n    Examples\n    --------\n    >>> import dtoolkit.geoaccessor\n    >>> df = pd.DataFrame(\n    ...     {\n    ...         \"x\": [120, 122, 100],\n    ...         \"y\":[30, 55, 1],\n    ...     },\n    ... ).from_xy(\"x\", \"y\", crs=4326)\n    >>> df\n         x   y                    geometry\n    0  120  30  POINT (120.00000 30.00000)\n    1  122  55  POINT (122.00000 55.00000)\n    2  100   1   POINT (100.00000 1.00000)\n    >>> other = pd.DataFrame(\n    ...     {\n    ...         \"x\": [120, 110],\n    ...         \"y\":[30, 40],\n    ...     },\n    ... ).from_xy(\"x\", \"y\", crs=4326)\n    >>> other\n         x   y                    geometry\n    0  120  30  POINT (120.00000 30.00000)\n    1  110  40  POINT (110.00000 40.00000)\n    >>> df.geodistance_matrix(other)\n                  0             1\n    0  0.000000e+00  1.203540e+06\n    1  1.439971e+06  1.511958e+06\n    2  2.418544e+06  1.522752e+06\n    \"\"\"\n    from sklearn.metrics.pairwise import haversine_distances\n\n    if s.crs != 4326:\n        raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {s.crs!r}.\")\n\n    if isinstance(other, gpd.base.GeoPandasBase):\n        if other.crs != 4326:\n            raise ValueError(f\"Only support 'EPSG:4326' CRS, but got {other.crs!r}.\")\n\n        # Force convert to GeoSeries\n        other = other.geometry\n\n    X = np.radians(np.stack((s.x, s.y), axis=1))\n    Y = np.radians(np.stack((other.x, other.y), axis=1)) if other is not None else other\n    return pd.DataFrame(\n        radius * haversine_distances(X, Y),\n        index=s.index,\n        columns=other.index,\n    )\n", "path": "dtoolkit/geoaccessor/geoseries/geodistance_matrix.py"}]}