From 402f4f8dbf3a0e6a46688f480b2a3ccba483b178 Mon Sep 17 00:00:00 2001 From: Zero Date: Sun, 26 Jun 2022 19:34:49 +0800 Subject: [PATCH 01/55] Add comments for environments --- ci/env/310-minimal.yaml | 1 + ci/env/38-minimal.yaml | 1 + ci/env/39-minimal.yaml | 1 + ci/env/dev.yaml | 2 ++ ci/env/latest.yaml | 1 + 5 files changed, 6 insertions(+) diff --git a/ci/env/310-minimal.yaml b/ci/env/310-minimal.yaml index ee1679c02..958f58dfd 100644 --- a/ci/env/310-minimal.yaml +++ b/ci/env/310-minimal.yaml @@ -5,6 +5,7 @@ dependencies: - python=3.10 # required - pandas=1.3.4 + # optional - scikit-learn=1.0 - geopandas=0.9.0 - pygeos=0.11.1 diff --git a/ci/env/38-minimal.yaml b/ci/env/38-minimal.yaml index 4579a164c..646c6a410 100644 --- a/ci/env/38-minimal.yaml +++ b/ci/env/38-minimal.yaml @@ -5,6 +5,7 @@ dependencies: - python=3.8 # required - pandas=1.1.0 + # optional - scikit-learn=1.0 - geopandas=0.9.0 - pygeos=0.8 diff --git a/ci/env/39-minimal.yaml b/ci/env/39-minimal.yaml index c4c1a7995..156ed9910 100644 --- a/ci/env/39-minimal.yaml +++ b/ci/env/39-minimal.yaml @@ -5,6 +5,7 @@ dependencies: - python=3.9 # required - pandas=1.1.3 + # optional - scikit-learn=1.0 - geopandas=0.9.0 - pygeos=0.8 diff --git a/ci/env/dev.yaml b/ci/env/dev.yaml index 694d81594..071a3bcb9 100644 --- a/ci/env/dev.yaml +++ b/ci/env/dev.yaml @@ -25,7 +25,9 @@ dependencies: # dev versions of packages - pip - pip: + # required - git+https://github.com/pydata/pandas + # optional - git+https://github.com/scikit-learn/scikit-learn - git+https://github.com/geopandas/geopandas - git+https://github.com/pygeos/pygeos diff --git a/ci/env/latest.yaml b/ci/env/latest.yaml index 0a6db567b..a6e9581d8 100644 --- a/ci/env/latest.yaml +++ b/ci/env/latest.yaml @@ -6,6 +6,7 @@ dependencies: - pandas - scikit-learn - geopandas + # optional - pygeos # testing - pytest From f56d19f0e6f6039ef7508cbe590a1a00c14b92e8 Mon Sep 17 00:00:00 2001 From: Zero Date: Sun, 26 Jun 2022 19:35:58 +0800 Subject: [PATCH 02/55] Adjust the place of comment --- ci/env/latest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/env/latest.yaml b/ci/env/latest.yaml index a6e9581d8..c074b4de6 100644 --- a/ci/env/latest.yaml +++ b/ci/env/latest.yaml @@ -4,9 +4,9 @@ channels: dependencies: # required - pandas + # optional - scikit-learn - geopandas - # optional - pygeos # testing - pytest From ddfec7f4aa243632c445683faeaa9363447d725b Mon Sep 17 00:00:00 2001 From: Zero Date: Sun, 26 Jun 2022 19:36:51 +0800 Subject: [PATCH 03/55] Optionally required geopy --- ci/env/310-minimal.yaml | 1 + ci/env/38-minimal.yaml | 1 + ci/env/39-minimal.yaml | 1 + ci/env/dev.yaml | 1 + ci/env/latest.yaml | 1 + 5 files changed, 5 insertions(+) diff --git a/ci/env/310-minimal.yaml b/ci/env/310-minimal.yaml index 958f58dfd..943cfa09b 100644 --- a/ci/env/310-minimal.yaml +++ b/ci/env/310-minimal.yaml @@ -9,6 +9,7 @@ dependencies: - scikit-learn=1.0 - geopandas=0.9.0 - pygeos=0.11.1 + - geopy # testing - pytest - pytest-cov diff --git a/ci/env/38-minimal.yaml b/ci/env/38-minimal.yaml index 646c6a410..096343d48 100644 --- a/ci/env/38-minimal.yaml +++ b/ci/env/38-minimal.yaml @@ -9,6 +9,7 @@ dependencies: - scikit-learn=1.0 - geopandas=0.9.0 - pygeos=0.8 + - geopy # testing - pytest - pytest-cov diff --git a/ci/env/39-minimal.yaml b/ci/env/39-minimal.yaml index 156ed9910..7ecbfa8bd 100644 --- a/ci/env/39-minimal.yaml +++ b/ci/env/39-minimal.yaml @@ -9,6 +9,7 @@ dependencies: - scikit-learn=1.0 - geopandas=0.9.0 - pygeos=0.8 + - geopy # testing - pytest - pytest-cov diff --git a/ci/env/dev.yaml b/ci/env/dev.yaml index 071a3bcb9..dc511d99a 100644 --- a/ci/env/dev.yaml +++ b/ci/env/dev.yaml @@ -17,6 +17,7 @@ dependencies: - fiona - pyproj - shapely + - geopy # testing - pytest - pytest-cov diff --git a/ci/env/latest.yaml b/ci/env/latest.yaml index c074b4de6..b54701017 100644 --- a/ci/env/latest.yaml +++ b/ci/env/latest.yaml @@ -8,6 +8,7 @@ dependencies: - scikit-learn - geopandas - pygeos + - geopy # testing - pytest - pytest-cov From c1daa8678746f614388a6e88e4e522a90d67e730 Mon Sep 17 00:00:00 2001 From: Zero Date: Sun, 26 Jun 2022 19:46:37 +0800 Subject: [PATCH 04/55] EHN: New geoaccessor `to_geocode` --- dtoolkit/geoaccessor/series/__init__.py | 1 + dtoolkit/geoaccessor/series/to_geocode.py | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 dtoolkit/geoaccessor/series/__init__.py create mode 100644 dtoolkit/geoaccessor/series/to_geocode.py diff --git a/dtoolkit/geoaccessor/series/__init__.py b/dtoolkit/geoaccessor/series/__init__.py new file mode 100644 index 000000000..e4c1d4d19 --- /dev/null +++ b/dtoolkit/geoaccessor/series/__init__.py @@ -0,0 +1 @@ +from .to_geocode import to_geocode # noqa diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py new file mode 100644 index 000000000..3e452d7ca --- /dev/null +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -0,0 +1,13 @@ +import pandas as pd +import geopandas as gpd + +from dtoolkit.accessor.register import register_series_method + + +@register_series_method +def to_geocode(s: pd.Series, drop: bool = False) -> gpd.GeoDataFrame: + if s.name is None and not drop: + raise ValueError("") + + df = gpd.tools.geocode(s) + return df if drop else pd.concat((s, df), axis=1) From 40e7ea76cde87e453867e08007fee2c77c5be748 Mon Sep 17 00:00:00 2001 From: Zero Date: Sun, 26 Jun 2022 19:52:45 +0800 Subject: [PATCH 05/55] DOC: support single label --- dtoolkit/accessor/dataframe/drop_or_not.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dtoolkit/accessor/dataframe/drop_or_not.py b/dtoolkit/accessor/dataframe/drop_or_not.py index 47455cc50..c96091949 100644 --- a/dtoolkit/accessor/dataframe/drop_or_not.py +++ b/dtoolkit/accessor/dataframe/drop_or_not.py @@ -16,6 +16,7 @@ def drop_or_not(df: pd.DataFrame, drop: bool = True, **kwargs) -> pd.DataFrame: ---------- drop : bool, default True Choose to drop or not. If True will drop else don't. + **kwargs See the documentation for :meth:`~pandas.DataFrame.drop` for complete details on the keyword arguments. @@ -38,7 +39,7 @@ def drop_or_not(df: pd.DataFrame, drop: bool = True, **kwargs) -> pd.DataFrame: a b 0 1 3 1 2 4 - >>> df.drop_or_not(True, columns=["b"]) + >>> df.drop_or_not(True, columns="b") a 0 1 1 2 From 450a9cfffd68c11b14c183a7230c15038a0ce0bd Mon Sep 17 00:00:00 2001 From: Zero Date: Sun, 26 Jun 2022 19:53:55 +0800 Subject: [PATCH 06/55] EHN: New geoaccessor `to_geocode` for `DataFrame` --- dtoolkit/geoaccessor/dataframe/__init__.py | 1 + dtoolkit/geoaccessor/dataframe/to_geocode.py | 22 ++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 dtoolkit/geoaccessor/dataframe/to_geocode.py diff --git a/dtoolkit/geoaccessor/dataframe/__init__.py b/dtoolkit/geoaccessor/dataframe/__init__.py index 37fa536c0..dba283d94 100644 --- a/dtoolkit/geoaccessor/dataframe/__init__.py +++ b/dtoolkit/geoaccessor/dataframe/__init__.py @@ -1,3 +1,4 @@ from dtoolkit.geoaccessor.dataframe.from_wkt import from_wkt # noqa from dtoolkit.geoaccessor.dataframe.from_xy import from_xy # noqa +from dtoolkit.geoaccessor.dataframe.to_geocode import to_geocode # noqa from dtoolkit.geoaccessor.dataframe.to_geoframe import to_geoframe # noqa diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/to_geocode.py new file mode 100644 index 000000000..60be13192 --- /dev/null +++ b/dtoolkit/geoaccessor/dataframe/to_geocode.py @@ -0,0 +1,22 @@ +from typing import Hashable + +import pandas as pd +import geopandas as gpd + +from dtoolkit.accessor.dataframe import drop_or_not # noqa +from dtoolkit.accessor.register import register_dataframe_method + + +@register_dataframe_method +def to_geocode( + df: pd.DataFrame, + column: Hashable, + drop: bool = False, +) -> gpd.GeoDataFrame: + return pd.concat( + ( + df.drop_or_not(drop=drop, columns=column), + gpd.tools.geocode(df[column]), + ), + axis=1, + ) From 491263b184147626c937a8e1f08246a31a31337e Mon Sep 17 00:00:00 2001 From: Zero Date: Sun, 26 Jun 2022 21:18:33 +0800 Subject: [PATCH 07/55] lint codes --- dtoolkit/geoaccessor/dataframe/to_geocode.py | 2 +- dtoolkit/geoaccessor/series/__init__.py | 2 +- dtoolkit/geoaccessor/series/to_geocode.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/to_geocode.py index 60be13192..228fec1bb 100644 --- a/dtoolkit/geoaccessor/dataframe/to_geocode.py +++ b/dtoolkit/geoaccessor/dataframe/to_geocode.py @@ -1,7 +1,7 @@ from typing import Hashable -import pandas as pd import geopandas as gpd +import pandas as pd from dtoolkit.accessor.dataframe import drop_or_not # noqa from dtoolkit.accessor.register import register_dataframe_method diff --git a/dtoolkit/geoaccessor/series/__init__.py b/dtoolkit/geoaccessor/series/__init__.py index e4c1d4d19..5ee9ecac5 100644 --- a/dtoolkit/geoaccessor/series/__init__.py +++ b/dtoolkit/geoaccessor/series/__init__.py @@ -1 +1 @@ -from .to_geocode import to_geocode # noqa +from dtoolkit.geoaccessor.series.to_geocode import to_geocode # noqa diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index 3e452d7ca..6bb03e794 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -1,5 +1,5 @@ -import pandas as pd import geopandas as gpd +import pandas as pd from dtoolkit.accessor.register import register_series_method From 8f79b754f99562a68702c3d604edc73d64d3806b Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 10:40:54 +0800 Subject: [PATCH 08/55] Import geoaccessor.series all methods --- dtoolkit/geoaccessor/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dtoolkit/geoaccessor/__init__.py b/dtoolkit/geoaccessor/__init__.py index ded8efd8a..09029aae9 100644 --- a/dtoolkit/geoaccessor/__init__.py +++ b/dtoolkit/geoaccessor/__init__.py @@ -1,4 +1,5 @@ from dtoolkit.geoaccessor import dataframe # noqa +from dtoolkit.geoaccessor import series # noqa from dtoolkit.geoaccessor import geodataframe # noqa from dtoolkit.geoaccessor import geoseries # noqa from dtoolkit.geoaccessor.accessor import register_geodataframe_accessor # noqa From 64fd41b5e7486099c7e32b26d9653721b7420919 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 10:41:55 +0800 Subject: [PATCH 09/55] Finished raising error --- dtoolkit/geoaccessor/series/to_geocode.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index 6bb03e794..899ef941c 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -7,7 +7,10 @@ @register_series_method def to_geocode(s: pd.Series, drop: bool = False) -> gpd.GeoDataFrame: if s.name is None and not drop: - raise ValueError("") + raise ValueError( + "to keep the original data requires setting the 'name' of " + f"{s.__class__.__name__!r}", + ) df = gpd.tools.geocode(s) return df if drop else pd.concat((s, df), axis=1) From f63f4aee61f2d2d2a22192057a39e1d7069a4c21 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 10:57:35 +0800 Subject: [PATCH 10/55] Add kwargs for geocode --- dtoolkit/geoaccessor/series/to_geocode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index 899ef941c..ab004b93f 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -5,12 +5,12 @@ @register_series_method -def to_geocode(s: pd.Series, drop: bool = False) -> gpd.GeoDataFrame: +def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: if s.name is None and not drop: raise ValueError( "to keep the original data requires setting the 'name' of " f"{s.__class__.__name__!r}", ) - df = gpd.tools.geocode(s) + df = gpd.tools.geocode(s, **kwargs) return df if drop else pd.concat((s, df), axis=1) From 32d1a00b7f330d1f407a7d8879f5ac43a1dd02c4 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 11:06:47 +0800 Subject: [PATCH 11/55] Finished description --- dtoolkit/geoaccessor/series/to_geocode.py | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index ab004b93f..6263c3a60 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -6,6 +6,46 @@ @register_series_method def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: + """ + Geocode string type Series and get a GeoDataFrame of the resulting points. + + Parameters + ---------- + drop : bool, default False + Don't contain the original data anymore. + + **kwargs + See the documentation for :func:`~geopandas.tools.geocode` for complete details + on the keyword arguments. + + Returns + ------- + GeoDataFrame + + Raises + ------ + - ModuleNotFoundError + If don't have module named 'geopy'. + + - ValueError + If 'drop' is True and the name of Series is empty. + + Examples + -------- + >>> import dtoolkit.geoaccessor + >>> import pandas as pd + >>> s = pd.Series( + ... [ + ... "boston, ma", + ... "1600 pennsylvania ave. washington, dc", + ... ], + ... ) + >>> s.to_geocode(drop=True) + geometry address + 0 POINT (-71.06051 42.35543) Boston, Massachusetts, United States + 1 POINT (-77.03655 38.89770) White House, 1600, Pennsylvania Avenue Northwe... + """ + if s.name is None and not drop: raise ValueError( "to keep the original data requires setting the 'name' of " From 1605ff1e37e8cbee259f8e77a5b9dfecfc6f1b10 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 11:07:56 +0800 Subject: [PATCH 12/55] Index methods --- doc/source/reference/geoaccessor.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/source/reference/geoaccessor.rst b/doc/source/reference/geoaccessor.rst index 64229789c..932ace8cb 100644 --- a/doc/source/reference/geoaccessor.rst +++ b/doc/source/reference/geoaccessor.rst @@ -26,6 +26,15 @@ GeoDataFrame Accessor utm_crs +Series Accessor (to GeoPandas) +--------------- +.. currentmodule:: dtoolkit.geoaccessor.series +.. autosummary:: + :toctree: api/ + + to_geocode + + DataFrame Accessor (to GeoPandas) --------------------------------- .. currentmodule:: dtoolkit.geoaccessor.dataframe @@ -34,6 +43,7 @@ DataFrame Accessor (to GeoPandas) from_wkt from_xy + to_geocode to_geoframe From 0b0a195045c55210b4440e318b9b22580ba2d873 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 11:22:08 +0800 Subject: [PATCH 13/55] Add see-also section --- dtoolkit/geoaccessor/series/to_geocode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index 6263c3a60..8672af8a3 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -30,6 +30,11 @@ def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: - ValueError If 'drop' is True and the name of Series is empty. + See Also + -------- + geopandas.tools.geocode + dtoolkit.geoaccessor.dataframe.to_geocode + Examples -------- >>> import dtoolkit.geoaccessor From ae0c131878720c14b59b581b5691d8cd6e48cdd9 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 11:27:22 +0800 Subject: [PATCH 14/55] Delete marks --- dtoolkit/geoaccessor/series/to_geocode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index 8672af8a3..7f0afbd98 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -24,10 +24,10 @@ def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: Raises ------ - - ModuleNotFoundError + ModuleNotFoundError If don't have module named 'geopy'. - - ValueError + ValueError If 'drop' is True and the name of Series is empty. See Also From c83c9047058ecec5a903f8cae7428758245c2155 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 11:39:46 +0800 Subject: [PATCH 15/55] adjust the sequences of condition --- dtoolkit/geoaccessor/series/to_geocode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index 7f0afbd98..efac74586 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -51,7 +51,7 @@ def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: 1 POINT (-77.03655 38.89770) White House, 1600, Pennsylvania Avenue Northwe... """ - if s.name is None and not drop: + if not drop and s.name is None: raise ValueError( "to keep the original data requires setting the 'name' of " f"{s.__class__.__name__!r}", From 5ee0b89ad916a4f353d850a9efdfe07582ac86d0 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:10:15 +0800 Subject: [PATCH 16/55] Add **kwargs for to_geocode --- dtoolkit/geoaccessor/dataframe/to_geocode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/to_geocode.py index 228fec1bb..eb78f3abf 100644 --- a/dtoolkit/geoaccessor/dataframe/to_geocode.py +++ b/dtoolkit/geoaccessor/dataframe/to_geocode.py @@ -12,11 +12,12 @@ def to_geocode( df: pd.DataFrame, column: Hashable, drop: bool = False, + **kwargs, ) -> gpd.GeoDataFrame: return pd.concat( ( df.drop_or_not(drop=drop, columns=column), - gpd.tools.geocode(df[column]), + gpd.tools.geocode(df[column], **kwargs), ), axis=1, ) From c96a2ff2c2532a70506331fea1e8cf34afd0799d Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:12:10 +0800 Subject: [PATCH 17/55] Finished basic description for to_geocode --- dtoolkit/geoaccessor/dataframe/to_geocode.py | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/to_geocode.py index eb78f3abf..5c70d3802 100644 --- a/dtoolkit/geoaccessor/dataframe/to_geocode.py +++ b/dtoolkit/geoaccessor/dataframe/to_geocode.py @@ -14,6 +14,32 @@ def to_geocode( drop: bool = False, **kwargs, ) -> gpd.GeoDataFrame: + """ + Geocode string type Series and get a GeoDataFrame of the resulting points. + + Parameters + ---------- + column : Hashable + The name of the column to geocode. + + drop : bool, default False + Don't contain the original data anymore. + + **kwargs + See the documentation for :func:`~geopandas.tools.geocode` for complete details + on the keyword arguments. + + Returns + ------- + GeoDataFrame + + + See Also + -------- + geopandas.tools.geocode + dtoolkit.geoaccessor.series.to_geocode + """ + return pd.concat( ( df.drop_or_not(drop=drop, columns=column), From b378ba01384e265c7f3dc1b3be5e46bece0e1f97 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:15:23 +0800 Subject: [PATCH 18/55] display data --- dtoolkit/geoaccessor/series/to_geocode.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/to_geocode.py index efac74586..138c3966c 100644 --- a/dtoolkit/geoaccessor/series/to_geocode.py +++ b/dtoolkit/geoaccessor/series/to_geocode.py @@ -45,6 +45,10 @@ def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: ... "1600 pennsylvania ave. washington, dc", ... ], ... ) + >>> s + 0 boston, ma + 1 1600 pennsylvania ave. washington, dc + dtype: object >>> s.to_geocode(drop=True) geometry address 0 POINT (-71.06051 42.35543) Boston, Massachusetts, United States From b57adcb21c3cbbdfb3d2396cd52c91dcaf9c27db Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:24:48 +0800 Subject: [PATCH 19/55] lint codes --- dtoolkit/geoaccessor/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/__init__.py b/dtoolkit/geoaccessor/__init__.py index 09029aae9..b84c657a4 100644 --- a/dtoolkit/geoaccessor/__init__.py +++ b/dtoolkit/geoaccessor/__init__.py @@ -1,7 +1,7 @@ from dtoolkit.geoaccessor import dataframe # noqa -from dtoolkit.geoaccessor import series # noqa from dtoolkit.geoaccessor import geodataframe # noqa from dtoolkit.geoaccessor import geoseries # noqa +from dtoolkit.geoaccessor import series # noqa from dtoolkit.geoaccessor.accessor import register_geodataframe_accessor # noqa from dtoolkit.geoaccessor.accessor import register_geoseries_accessor # noqa from dtoolkit.geoaccessor.register import register_geodataframe_method # noqa From cc7537875b83d028a2f261357b91425adc8574e4 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:25:19 +0800 Subject: [PATCH 20/55] Finished to_geocode examples section --- dtoolkit/geoaccessor/dataframe/to_geocode.py | 22 +++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/to_geocode.py index 5c70d3802..f051c3ed4 100644 --- a/dtoolkit/geoaccessor/dataframe/to_geocode.py +++ b/dtoolkit/geoaccessor/dataframe/to_geocode.py @@ -33,11 +33,31 @@ def to_geocode( ------- GeoDataFrame - See Also -------- geopandas.tools.geocode dtoolkit.geoaccessor.series.to_geocode + + Examples + -------- + >>> import dtoolkit.geoaccessor + >>> import pandas as pd + >>> df = pd.DataFrame( + ... { + ... "name": [ + ... "boston, ma", + ... "1600 pennsylvania ave. washington, dc", + ... ], + ... } + ... ) + >>> df + name + 0 boston, ma + 1 1600 pennsylvania ave. washington, dc + >>> df.to_geocode("name") + geometry address + 0 POINT (-71.06051 42.35543) Boston, Massachusetts, United States + 1 POINT (-77.03655 38.89770) White House, 1600, Pennsylvania Avenue Northwe... """ return pd.concat( From aae4adb1c37c565b7588977485a4267147789a3b Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:26:35 +0800 Subject: [PATCH 21/55] Test error case --- test/geoaccessor/series/__init__.py | 0 test/geoaccessor/series/test_to_geocode.py | 24 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 test/geoaccessor/series/__init__.py create mode 100644 test/geoaccessor/series/test_to_geocode.py diff --git a/test/geoaccessor/series/__init__.py b/test/geoaccessor/series/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/geoaccessor/series/test_to_geocode.py b/test/geoaccessor/series/test_to_geocode.py new file mode 100644 index 000000000..6d4d46875 --- /dev/null +++ b/test/geoaccessor/series/test_to_geocode.py @@ -0,0 +1,24 @@ +from dtoolkit.geoaccessor.series import to_geocode # noqa +import pandas as pd + +import pytest + + +@pytest.mark.parametrize( + "s, drop, error", + [ + ( + pd.Series( + [ + "boston, ma", + "1600 pennsylvania ave. washington, dc", + ] + ), + False, + ValueError, + ), + ], +) +def test_error(s, drop, error): + with pytest.raises(error): + s.to_geocode(drop=drop) From 2a0f1dc9947d3de34557d1c28d21f0c6e69caefa Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:29:26 +0800 Subject: [PATCH 22/55] update the description of dataframe.to_geocdoe --- dtoolkit/geoaccessor/dataframe/to_geocode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/to_geocode.py index f051c3ed4..96ff8e71b 100644 --- a/dtoolkit/geoaccessor/dataframe/to_geocode.py +++ b/dtoolkit/geoaccessor/dataframe/to_geocode.py @@ -15,7 +15,8 @@ def to_geocode( **kwargs, ) -> gpd.GeoDataFrame: """ - Geocode string type Series and get a GeoDataFrame of the resulting points. + Geocode a string type column from a DataFrame and get a GeoDataFrame of the + resulting points. Parameters ---------- From 2b7fc1e31193d641c37675b5ef5fd3b81b64cc10 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Jun 2022 08:30:23 +0000 Subject: [PATCH 23/55] BOT: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test/geoaccessor/series/test_to_geocode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/geoaccessor/series/test_to_geocode.py b/test/geoaccessor/series/test_to_geocode.py index 6d4d46875..e01032583 100644 --- a/test/geoaccessor/series/test_to_geocode.py +++ b/test/geoaccessor/series/test_to_geocode.py @@ -1,8 +1,8 @@ -from dtoolkit.geoaccessor.series import to_geocode # noqa import pandas as pd - import pytest +from dtoolkit.geoaccessor.series import to_geocode # noqa + @pytest.mark.parametrize( "s, drop, error", @@ -12,7 +12,7 @@ [ "boston, ma", "1600 pennsylvania ave. washington, dc", - ] + ], ), False, ValueError, From 1909fd424652ed1fce38033f11ab1296690fe298 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 16:35:27 +0800 Subject: [PATCH 24/55] Update the argument value of example --- dtoolkit/geoaccessor/dataframe/to_geocode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/to_geocode.py index 96ff8e71b..4fabb05d8 100644 --- a/dtoolkit/geoaccessor/dataframe/to_geocode.py +++ b/dtoolkit/geoaccessor/dataframe/to_geocode.py @@ -55,7 +55,7 @@ def to_geocode( name 0 boston, ma 1 1600 pennsylvania ave. washington, dc - >>> df.to_geocode("name") + >>> df.to_geocode("name", drop=True) geometry address 0 POINT (-71.06051 42.35543) Boston, Massachusetts, United States 1 POINT (-77.03655 38.89770) White House, 1600, Pennsylvania Avenue Northwe... From 82c0a49f8b97bc404607c8ce52d29073272166b7 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 17:02:10 +0800 Subject: [PATCH 25/55] rename `to_geocode` to `geocode` --- doc/source/reference/geoaccessor.rst | 4 ++-- dtoolkit/geoaccessor/dataframe/__init__.py | 2 +- dtoolkit/geoaccessor/dataframe/{to_geocode.py => geocode.py} | 0 dtoolkit/geoaccessor/series/__init__.py | 2 +- dtoolkit/geoaccessor/series/{to_geocode.py => geocode.py} | 0 .../series/{test_to_geocode.py => test_geocode.py} | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename dtoolkit/geoaccessor/dataframe/{to_geocode.py => geocode.py} (100%) rename dtoolkit/geoaccessor/series/{to_geocode.py => geocode.py} (100%) rename test/geoaccessor/series/{test_to_geocode.py => test_geocode.py} (87%) diff --git a/doc/source/reference/geoaccessor.rst b/doc/source/reference/geoaccessor.rst index 932ace8cb..6ea9f9d67 100644 --- a/doc/source/reference/geoaccessor.rst +++ b/doc/source/reference/geoaccessor.rst @@ -32,7 +32,7 @@ Series Accessor (to GeoPandas) .. autosummary:: :toctree: api/ - to_geocode + geocode DataFrame Accessor (to GeoPandas) @@ -43,7 +43,7 @@ DataFrame Accessor (to GeoPandas) from_wkt from_xy - to_geocode + geocode to_geoframe diff --git a/dtoolkit/geoaccessor/dataframe/__init__.py b/dtoolkit/geoaccessor/dataframe/__init__.py index dba283d94..b9593f3dc 100644 --- a/dtoolkit/geoaccessor/dataframe/__init__.py +++ b/dtoolkit/geoaccessor/dataframe/__init__.py @@ -1,4 +1,4 @@ from dtoolkit.geoaccessor.dataframe.from_wkt import from_wkt # noqa from dtoolkit.geoaccessor.dataframe.from_xy import from_xy # noqa -from dtoolkit.geoaccessor.dataframe.to_geocode import to_geocode # noqa +from dtoolkit.geoaccessor.dataframe.geocode import geocode # noqa from dtoolkit.geoaccessor.dataframe.to_geoframe import to_geoframe # noqa diff --git a/dtoolkit/geoaccessor/dataframe/to_geocode.py b/dtoolkit/geoaccessor/dataframe/geocode.py similarity index 100% rename from dtoolkit/geoaccessor/dataframe/to_geocode.py rename to dtoolkit/geoaccessor/dataframe/geocode.py diff --git a/dtoolkit/geoaccessor/series/__init__.py b/dtoolkit/geoaccessor/series/__init__.py index 5ee9ecac5..f273c6c6c 100644 --- a/dtoolkit/geoaccessor/series/__init__.py +++ b/dtoolkit/geoaccessor/series/__init__.py @@ -1 +1 @@ -from dtoolkit.geoaccessor.series.to_geocode import to_geocode # noqa +from dtoolkit.geoaccessor.series.geocode import geocode # noqa diff --git a/dtoolkit/geoaccessor/series/to_geocode.py b/dtoolkit/geoaccessor/series/geocode.py similarity index 100% rename from dtoolkit/geoaccessor/series/to_geocode.py rename to dtoolkit/geoaccessor/series/geocode.py diff --git a/test/geoaccessor/series/test_to_geocode.py b/test/geoaccessor/series/test_geocode.py similarity index 87% rename from test/geoaccessor/series/test_to_geocode.py rename to test/geoaccessor/series/test_geocode.py index e01032583..d23e9e0d5 100644 --- a/test/geoaccessor/series/test_to_geocode.py +++ b/test/geoaccessor/series/test_geocode.py @@ -1,7 +1,7 @@ import pandas as pd import pytest -from dtoolkit.geoaccessor.series import to_geocode # noqa +from dtoolkit.geoaccessor.series import geocode # noqa @pytest.mark.parametrize( From ee46f2d11aef3ba4849b56a8351bd6b5b7eab2b8 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 17:07:37 +0800 Subject: [PATCH 26/55] Add raises section --- dtoolkit/geoaccessor/dataframe/geocode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dtoolkit/geoaccessor/dataframe/geocode.py b/dtoolkit/geoaccessor/dataframe/geocode.py index 4fabb05d8..9208f5e98 100644 --- a/dtoolkit/geoaccessor/dataframe/geocode.py +++ b/dtoolkit/geoaccessor/dataframe/geocode.py @@ -34,6 +34,11 @@ def to_geocode( ------- GeoDataFrame + Raises + ------ + ModuleNotFoundError + If don't have module named 'geopy'. + See Also -------- geopandas.tools.geocode From f4d7e89024211e2935d6afe8b604be85ea85a2b4 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 27 Jun 2022 17:08:26 +0800 Subject: [PATCH 27/55] rename `to_geocode` to `geocode` --- dtoolkit/geoaccessor/dataframe/geocode.py | 6 +++--- dtoolkit/geoaccessor/series/geocode.py | 6 +++--- test/geoaccessor/series/test_geocode.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dtoolkit/geoaccessor/dataframe/geocode.py b/dtoolkit/geoaccessor/dataframe/geocode.py index 9208f5e98..284e183c6 100644 --- a/dtoolkit/geoaccessor/dataframe/geocode.py +++ b/dtoolkit/geoaccessor/dataframe/geocode.py @@ -8,7 +8,7 @@ @register_dataframe_method -def to_geocode( +def geocode( df: pd.DataFrame, column: Hashable, drop: bool = False, @@ -42,7 +42,7 @@ def to_geocode( See Also -------- geopandas.tools.geocode - dtoolkit.geoaccessor.series.to_geocode + dtoolkit.geoaccessor.series.geocode Examples -------- @@ -60,7 +60,7 @@ def to_geocode( name 0 boston, ma 1 1600 pennsylvania ave. washington, dc - >>> df.to_geocode("name", drop=True) + >>> df.geocode("name", drop=True) geometry address 0 POINT (-71.06051 42.35543) Boston, Massachusetts, United States 1 POINT (-77.03655 38.89770) White House, 1600, Pennsylvania Avenue Northwe... diff --git a/dtoolkit/geoaccessor/series/geocode.py b/dtoolkit/geoaccessor/series/geocode.py index 138c3966c..017dd9a7e 100644 --- a/dtoolkit/geoaccessor/series/geocode.py +++ b/dtoolkit/geoaccessor/series/geocode.py @@ -5,7 +5,7 @@ @register_series_method -def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: +def geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: """ Geocode string type Series and get a GeoDataFrame of the resulting points. @@ -33,7 +33,7 @@ def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: See Also -------- geopandas.tools.geocode - dtoolkit.geoaccessor.dataframe.to_geocode + dtoolkit.geoaccessor.dataframe.geocode Examples -------- @@ -49,7 +49,7 @@ def to_geocode(s: pd.Series, drop: bool = False, **kwargs) -> gpd.GeoDataFrame: 0 boston, ma 1 1600 pennsylvania ave. washington, dc dtype: object - >>> s.to_geocode(drop=True) + >>> s.geocode(drop=True) geometry address 0 POINT (-71.06051 42.35543) Boston, Massachusetts, United States 1 POINT (-77.03655 38.89770) White House, 1600, Pennsylvania Avenue Northwe... diff --git a/test/geoaccessor/series/test_geocode.py b/test/geoaccessor/series/test_geocode.py index d23e9e0d5..25d3a5e09 100644 --- a/test/geoaccessor/series/test_geocode.py +++ b/test/geoaccessor/series/test_geocode.py @@ -21,4 +21,4 @@ ) def test_error(s, drop, error): with pytest.raises(error): - s.to_geocode(drop=drop) + s.geocode(drop=drop) From be026f384122703f0d765cd1e9c12573d7cc0213 Mon Sep 17 00:00:00 2001 From: Zero Date: Sat, 2 Jul 2022 21:04:32 +0800 Subject: [PATCH 28/55] EHN: Add Tencent geocoder as geopy's geocoder provider --- dtoolkit/geoaccessor/geocoder/__init__.py | 1 + dtoolkit/geoaccessor/geocoder/tencent.py | 272 ++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 dtoolkit/geoaccessor/geocoder/__init__.py create mode 100644 dtoolkit/geoaccessor/geocoder/tencent.py diff --git a/dtoolkit/geoaccessor/geocoder/__init__.py b/dtoolkit/geoaccessor/geocoder/__init__.py new file mode 100644 index 000000000..2d43e39ef --- /dev/null +++ b/dtoolkit/geoaccessor/geocoder/__init__.py @@ -0,0 +1 @@ +from .tencent import Tencent # noqa diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py new file mode 100644 index 000000000..4bcb94302 --- /dev/null +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -0,0 +1,272 @@ +from __future__ import annotations + +import hashlib +from functools import partial +from typing import Callable +from urllib.parse import quote_plus, urlencode + +from geopy.exc import ( + GeocoderAuthenticationFailure, + GeocoderQueryError, + GeocoderQuotaExceeded, + GeocoderServiceError, +) +from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder +from geopy.location import Location +from geopy.util import logger + + +__all__ = ("Tencent",) + + +class Tencent(Geocoder): + """ + Geocoder using the Tencent Maps API. + + See Also + -------- + Documentation of the Tencent Maps API + https://lbs.qq.com/service/webService/webServiceGuide/webServiceOverview + """ + + api_path = reverse_path = "/ws/geocoder/v1/" + + def __init__( + self, + api_key, + *, + scheme: str = None, + timeout: int = DEFAULT_SENTINEL, + proxies: dict = DEFAULT_SENTINEL, + user_agent: str = None, + ssl_context: "ssl.SSLContext" = DEFAULT_SENTINEL, + adapter_factory: Callable = None, + ): + """ + :param str api_key: The API key required by Tencent Map to perform + geocoding requests. API keys are managed through the Tencent APIs + console (https://lbs.qq.com/dev/console/application/mine). + + :param str scheme: + See :attr:`geopy.geocoders.options.default_scheme`. + + :param int timeout: + See :attr:`geopy.geocoders.options.default_timeout`. + + :param dict proxies: + See :attr:`geopy.geocoders.options.default_proxies`. + + :param str user_agent: + See :attr:`geopy.geocoders.options.default_user_agent`. + + :type ssl_context: :class:`ssl.SSLContext` + :param ssl_context: + See :attr:`geopy.geocoders.options.default_ssl_context`. + + :param callable adapter_factory: + See :attr:`geopy.geocoders.options.default_adapter_factory`. + """ + + super().__init__( + scheme=scheme, + timeout=timeout, + proxies=proxies, + user_agent=user_agent, + ssl_context=ssl_context, + adapter_factory=adapter_factory, + ) + self.api_key = api_key + domin = "apis.map.qq.com" + self.api = f"{self.scheme}://{domin}{self.api_path}" + self.reverse_api = f"{self.scheme}://{domin}{self.reverse_path}" + + def geocode( + self, + query: str, + *, + region: str = None, + exactly_one: bool = True, + timeout: int = DEFAULT_SENTINEL, + ) -> None | Location | list[Location]: + """ + Return a location point by address. + + :param str query: The address or query you wish to geocode. + + :param bool exactly_one: Return one result or a list of results, if + available. + + :param int timeout: Time, in seconds, to wait for the geocoding service + to respond before raising a :class:`geopy.exc.GeocoderTimedOut` + exception. Set this only if you wish to override, on this call + only, the value set during the geocoder's initialization. + + :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if + ``exactly_one=False``. + """ + + params = { + "address": query, + **({"region": region} if region else {}), + "key": self.api_key, + "output": "json", + } + url = self._construct_url(self.api, params) + + logger.debug(f"{self.__class__.__name__}.geocode: {url}") + callback = partial(self._parse_json, exactly_one=exactly_one, address="title") + + return self._call_geocoder(url, callback, timeout=timeout) + + def reverse( + self, + query: str, + *, + exactly_one: bool = True, + timeout: int = DEFAULT_SENTINEL, + ) -> None | Location | list[Location]: + """ + Return an address by location point. + + :type query: :class:`geopy.point.Point`, list or tuple of ``(latitude, + longitude)``, or string as ``"%(latitude)s, %(longitude)s"``. + :param query: The coordinates for which you wish to obtain the + closest human-readable addresses. + + :param bool exactly_one: Return one result or a list of results, if + available. Tencent's API always return at most one result. + + :param int timeout: Time, in seconds, to wait for the geocoding service + to respond before raising a :class:`geopy.exc.GeocoderTimedOut` + exception. Set this only if you wish to override, on this call + only, the value set during the geocoder's initialization. + + :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if + ``exactly_one=False``. + """ + + params = { + "location": self._coerce_point_to_string(query), + "key": self.api_key, + "output": "json", + } + url = self._construct_url(self.reverse_api, params) + + logger.debug(f"{self.__class__.__name__}.reverse: {url}") + callback = partial(self._parse_json, exactly_one=exactly_one) + + return self._call_geocoder(url, callback, timeout=timeout) + + def _construct_url(self, base_api: str, params: dict) -> str: + """ + Construct geocoding request url. + + :param str base_api: Geocoding function base address - self.api + or self.reverse_api. + + :param dict params: Geocoding params. + + :return: string URL. + """ + + return "?".join((base_api, urlencode(params))) + + def _parse_json( + self, + response: dict, + exactly_one: bool = True, + status: str = "status", + address: str = "address", + location: str = "location", + lat: str = "lat", + lng: str = "lng", + ) -> None | Location | list[Location]: + """ + Returns location, (latitude, longitude) from JSON feed. + """ + + self._check_status(response.get(status)) + if response is None or "result" not in response: + return + + place = self._parse_place( + response["result"], + location=location, + address=address, + lat=lat, + lng=lng, + ) + return place if exactly_one else [place] + + def _parse_place( + self, + place: dict, + address: str = "address", + location: str = "location", + lat: str = "lat", + lng: str = "lng", + ) -> None | Location: + """ + Get the location, lat, and lng from a single JSON place. + """ + + if place is None or address not in place or location not in place: + return + + return Location( + place[address], + self._parse_coordinate( + place[location], + lat=lat, + lng=lng, + ), + place, + ) + + def _parse_coordinate( + self, + location: dict, + lat: str = "lat", + lng: str = "lng", + ) -> tuple(float | None, float | None): + """ + Get the lat and lng from a single JSON location. + """ + + if location is None or lat not in location or lng not in location: + return (None, None) + + return (location[lat], location[lng]) + + def _check_status(self, status: str | int): + """ + Validates error statuses. + """ + + if status == 0: + # When there are no results, just return. + return + if status == 1: + raise GeocoderServiceError("Internal server error.") + elif status == 2: + raise GeocoderQueryError("Invalid request.") + elif status == 3: + raise GeocoderAuthenticationFailure("Authentication failure.") + elif status == 4: + raise GeocoderQuotaExceeded("Quota validate failure.") + elif status == 5: + raise GeocoderQueryError("AK Illegal or Not Exist.") + elif status == 101: + raise GeocoderAuthenticationFailure("No AK") + elif status == 102: + raise GeocoderAuthenticationFailure("MCODE Error") + elif status == 200: + raise GeocoderAuthenticationFailure("Invalid AK") + elif status == 211: + raise GeocoderAuthenticationFailure("Invalid SN") + elif 200 <= status < 300: + raise GeocoderAuthenticationFailure("Authentication Failure") + elif 300 <= status < 500: + raise GeocoderQuotaExceeded("Quota Error.") + else: + raise GeocoderQueryError("Unknown error. Status: %r" % status) From b55a7bf0abed2c6e85562d7e257117299ce14c1f Mon Sep 17 00:00:00 2001 From: Zero Date: Sat, 2 Jul 2022 21:04:46 +0800 Subject: [PATCH 29/55] Index this geocoder --- doc/source/reference/geoaccessor.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/source/reference/geoaccessor.rst b/doc/source/reference/geoaccessor.rst index bbafaaf62..4194f9b17 100644 --- a/doc/source/reference/geoaccessor.rst +++ b/doc/source/reference/geoaccessor.rst @@ -48,6 +48,15 @@ DataFrame Accessor (to GeoPandas) to_geoframe +Geocoder +-------- +.. currentmodule:: dtoolkit.geoaccessor.geocoder +.. autosummary:: + :toctree: api/ + + Tencent + + GeoPandas Base Accessor ----------------------- .. currentmodule:: dtoolkit.geoaccessor From 8062deefd8fb3d77b19ce80cdf0e14a6b5a88814 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 2 Jul 2022 13:07:50 +0000 Subject: [PATCH 30/55] BOT: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dtoolkit/geoaccessor/geocoder/__init__.py | 2 +- dtoolkit/geoaccessor/geocoder/tencent.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/__init__.py b/dtoolkit/geoaccessor/geocoder/__init__.py index 2d43e39ef..82b9c27d3 100644 --- a/dtoolkit/geoaccessor/geocoder/__init__.py +++ b/dtoolkit/geoaccessor/geocoder/__init__.py @@ -1 +1 @@ -from .tencent import Tencent # noqa +from dtoolkit.geoaccessor.geocoder.tencent import Tencent # noqa diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 4bcb94302..bcf6a46c3 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -3,15 +3,15 @@ import hashlib from functools import partial from typing import Callable -from urllib.parse import quote_plus, urlencode - -from geopy.exc import ( - GeocoderAuthenticationFailure, - GeocoderQueryError, - GeocoderQuotaExceeded, - GeocoderServiceError, -) -from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder +from urllib.parse import quote_plus +from urllib.parse import urlencode + +from geopy.exc import GeocoderAuthenticationFailure +from geopy.exc import GeocoderQueryError +from geopy.exc import GeocoderQuotaExceeded +from geopy.exc import GeocoderServiceError +from geopy.geocoders.base import DEFAULT_SENTINEL +from geopy.geocoders.base import Geocoder from geopy.location import Location from geopy.util import logger @@ -39,7 +39,7 @@ def __init__( timeout: int = DEFAULT_SENTINEL, proxies: dict = DEFAULT_SENTINEL, user_agent: str = None, - ssl_context: "ssl.SSLContext" = DEFAULT_SENTINEL, + ssl_context: ssl.SSLContext = DEFAULT_SENTINEL, adapter_factory: Callable = None, ): """ From 73a14c4a66e484b033d9f5b28ce88169779573ce Mon Sep 17 00:00:00 2001 From: Zero Date: Sat, 2 Jul 2022 21:12:36 +0800 Subject: [PATCH 31/55] Remove default parameters --- dtoolkit/geoaccessor/geocoder/tencent.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index bcf6a46c3..744a36ba7 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -109,7 +109,6 @@ def geocode( "address": query, **({"region": region} if region else {}), "key": self.api_key, - "output": "json", } url = self._construct_url(self.api, params) @@ -148,7 +147,6 @@ def reverse( params = { "location": self._coerce_point_to_string(query), "key": self.api_key, - "output": "json", } url = self._construct_url(self.reverse_api, params) From b1b84179d6d8ddad7889923cfc0608ef4d27a453 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 13 Jul 2022 16:14:14 +0800 Subject: [PATCH 32/55] suit with docstrings style --- dtoolkit/geoaccessor/geocoder/tencent.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 744a36ba7..5611c150a 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -23,9 +23,7 @@ class Tencent(Geocoder): """ Geocoder using the Tencent Maps API. - See Also - -------- - Documentation of the Tencent Maps API + Documentation at: https://lbs.qq.com/service/webService/webServiceGuide/webServiceOverview """ From c2755d58e48b4abed3a2a518c1f002034b86cff1 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 13 Jul 2022 16:15:00 +0800 Subject: [PATCH 33/55] use `self.domain` replace `domain` --- dtoolkit/geoaccessor/geocoder/tencent.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 5611c150a..4058cb76a 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -73,10 +73,10 @@ def __init__( ssl_context=ssl_context, adapter_factory=adapter_factory, ) + self.domin = "apis.map.qq.com" self.api_key = api_key - domin = "apis.map.qq.com" - self.api = f"{self.scheme}://{domin}{self.api_path}" - self.reverse_api = f"{self.scheme}://{domin}{self.reverse_path}" + self.api = f"{self.scheme}://{self.domin}{self.api_path}" + self.reverse_api = f"{self.scheme}://{self.domin}{self.reverse_path}" def geocode( self, From 55e5cc1510fe4e3d55aa8067da7f5ba3f6b41d79 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 13 Jul 2022 17:24:38 +0800 Subject: [PATCH 34/55] Update checking status --- dtoolkit/geoaccessor/geocoder/tencent.py | 62 +++++++++++++++--------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 4058cb76a..a34f7f40a 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -10,6 +10,8 @@ from geopy.exc import GeocoderQueryError from geopy.exc import GeocoderQuotaExceeded from geopy.exc import GeocoderServiceError +from geopy.exc import GeocoderTimedOut +from geopy.exc import GeocoderRateLimited from geopy.geocoders.base import DEFAULT_SENTINEL from geopy.geocoders.base import Geocoder from geopy.location import Location @@ -237,32 +239,48 @@ def _parse_coordinate( def _check_status(self, status: str | int): """ Validates error statuses. + + Documentation at: + https://lbs.qq.com/service/webService/webServiceGuide/status """ if status == 0: - # When there are no results, just return. return - if status == 1: - raise GeocoderServiceError("Internal server error.") - elif status == 2: - raise GeocoderQueryError("Invalid request.") - elif status == 3: + elif status == 110: raise GeocoderAuthenticationFailure("Authentication failure.") - elif status == 4: - raise GeocoderQuotaExceeded("Quota validate failure.") - elif status == 5: - raise GeocoderQueryError("AK Illegal or Not Exist.") - elif status == 101: - raise GeocoderAuthenticationFailure("No AK") - elif status == 102: - raise GeocoderAuthenticationFailure("MCODE Error") - elif status == 200: - raise GeocoderAuthenticationFailure("Invalid AK") - elif status == 211: - raise GeocoderAuthenticationFailure("Invalid SN") - elif 200 <= status < 300: - raise GeocoderAuthenticationFailure("Authentication Failure") - elif 300 <= status < 500: - raise GeocoderQuotaExceeded("Quota Error.") + elif status == 111: + raise GeocoderAuthenticationFailure("Signature verification failed.") + elif status == 112: + raise GeocoderAuthenticationFailure("Invalid IP.") + elif status == 113: + raise GeocoderAuthenticationFailure("This feature is not authorized.") + elif status == 120: + raise GeocoderQuotaExceeded( + "The number of requests per second has reached the upper limit." + ) + elif status == 121: + raise GeocoderQuotaExceeded( + "The number of requests daily has reached the upper limit." + ) + elif status in 190: + raise GeocoderAuthenticationFailure("Invalid KEY.") + elif status == 199: + raise GeocoderAuthenticationFailure("The webservice isn't enabled.") + elif status in {301, 311}: + raise GeocoderQueryError("KEY Illegal or Not Exist.") + elif status in {300, 306, 301, 320, 330, 331, 348, 351, 394, 395, 399}: + raise GeocoderQueryError("Invalid parameters.") + elif status in {347, 393}: + raise GeocoderQueryError("No results.") + elif status in {400, 402}: + raise GeocoderQueryError("Can't decode the request url.") + elif status == 404: + raise GeocoderQueryError("Invalid request path.") + elif status == 407: + raise GeocoderQueryError("Invalid request method.") + elif status == 500: + raise GeocoderTimedOut("Request timed out.") + elif 500 < status < 600: + raise GeocoderServiceError("Request server error.") else: raise GeocoderQueryError("Unknown error. Status: %r" % status) From 99840e560143b905cef0ac424e447551b374a44f Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 13 Jul 2022 17:25:18 +0800 Subject: [PATCH 35/55] use lower case --- dtoolkit/geoaccessor/geocoder/tencent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index a34f7f40a..406f9cd5c 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -267,7 +267,7 @@ def _check_status(self, status: str | int): elif status == 199: raise GeocoderAuthenticationFailure("The webservice isn't enabled.") elif status in {301, 311}: - raise GeocoderQueryError("KEY Illegal or Not Exist.") + raise GeocoderQueryError("KEY illegal or not exist.") elif status in {300, 306, 301, 320, 330, 331, 348, 351, 394, 395, 399}: raise GeocoderQueryError("Invalid parameters.") elif status in {347, 393}: From fe6ea83c4efc7071a3e12cd12f9509d7ba7ced16 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 13 Jul 2022 17:25:42 +0800 Subject: [PATCH 36/55] use upper case --- dtoolkit/geoaccessor/geocoder/tencent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 406f9cd5c..7a687c882 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -273,7 +273,7 @@ def _check_status(self, status: str | int): elif status in {347, 393}: raise GeocoderQueryError("No results.") elif status in {400, 402}: - raise GeocoderQueryError("Can't decode the request url.") + raise GeocoderQueryError("Can't decode the request URL.") elif status == 404: raise GeocoderQueryError("Invalid request path.") elif status == 407: From 3729a7e0bc41e2c7070b4ed9c82feb9d7ae5b8ff Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 13 Jul 2022 17:45:11 +0800 Subject: [PATCH 37/55] Add keyword `SmartGeocoder` --- dtoolkit/geoaccessor/geocoder/tencent.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 7a687c882..1354bc18f 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -35,6 +35,7 @@ def __init__( self, api_key, *, + SmartGeocoder: bool = False, scheme: str = None, timeout: int = DEFAULT_SENTINEL, proxies: dict = DEFAULT_SENTINEL, @@ -47,6 +48,9 @@ def __init__( geocoding requests. API keys are managed through the Tencent APIs console (https://lbs.qq.com/dev/console/application/mine). + :param bool SmartGeocoder: If True, use the SmartGeocoder API. + https://lbs.qq.com/service/webService/webServiceGuide/SmartGeocoder + :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. @@ -79,6 +83,7 @@ def __init__( self.api_key = api_key self.api = f"{self.scheme}://{self.domin}{self.api_path}" self.reverse_api = f"{self.scheme}://{self.domin}{self.reverse_path}" + self.SmartGeocoder = SmartGeocoder def geocode( self, @@ -106,7 +111,7 @@ def geocode( """ params = { - "address": query, + ("smart_address" if self.SmartGeocoder else "address"): query, **({"region": region} if region else {}), "key": self.api_key, } From 4dde97e739789771f9fd1a3dfa7e0a450c0021c9 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 13 Jul 2022 17:45:30 +0800 Subject: [PATCH 38/55] lint codes --- dtoolkit/geoaccessor/geocoder/tencent.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 1354bc18f..a5075dd52 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -1,9 +1,7 @@ from __future__ import annotations -import hashlib from functools import partial from typing import Callable -from urllib.parse import quote_plus from urllib.parse import urlencode from geopy.exc import GeocoderAuthenticationFailure @@ -11,7 +9,6 @@ from geopy.exc import GeocoderQuotaExceeded from geopy.exc import GeocoderServiceError from geopy.exc import GeocoderTimedOut -from geopy.exc import GeocoderRateLimited from geopy.geocoders.base import DEFAULT_SENTINEL from geopy.geocoders.base import Geocoder from geopy.location import Location @@ -40,7 +37,7 @@ def __init__( timeout: int = DEFAULT_SENTINEL, proxies: dict = DEFAULT_SENTINEL, user_agent: str = None, - ssl_context: ssl.SSLContext = DEFAULT_SENTINEL, + ssl_context: "ssl.SSLContext" = DEFAULT_SENTINEL, adapter_factory: Callable = None, ): """ @@ -261,11 +258,11 @@ def _check_status(self, status: str | int): raise GeocoderAuthenticationFailure("This feature is not authorized.") elif status == 120: raise GeocoderQuotaExceeded( - "The number of requests per second has reached the upper limit." + "The number of requests per second has reached the upper limit.", ) elif status == 121: raise GeocoderQuotaExceeded( - "The number of requests daily has reached the upper limit." + "The number of requests daily has reached the upper limit.", ) elif status in 190: raise GeocoderAuthenticationFailure("Invalid KEY.") From 656c9a985dac17f914a106f629d48942bd647594 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 13 Jul 2022 09:47:13 +0000 Subject: [PATCH 39/55] BOT: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dtoolkit/geoaccessor/geocoder/tencent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index a5075dd52..7584fd5a7 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -37,7 +37,7 @@ def __init__( timeout: int = DEFAULT_SENTINEL, proxies: dict = DEFAULT_SENTINEL, user_agent: str = None, - ssl_context: "ssl.SSLContext" = DEFAULT_SENTINEL, + ssl_context: ssl.SSLContext = DEFAULT_SENTINEL, adapter_factory: Callable = None, ): """ From 7f130fbe88b8cc19a6e8758c324eb6f2eb92e05c Mon Sep 17 00:00:00 2001 From: Zero Date: Fri, 15 Jul 2022 14:30:59 +0800 Subject: [PATCH 40/55] adjust sequences of codes --- dtoolkit/geoaccessor/geocoder/tencent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 7584fd5a7..903cd826f 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -76,8 +76,8 @@ def __init__( ssl_context=ssl_context, adapter_factory=adapter_factory, ) - self.domin = "apis.map.qq.com" self.api_key = api_key + self.domin = "apis.map.qq.com" self.api = f"{self.scheme}://{self.domin}{self.api_path}" self.reverse_api = f"{self.scheme}://{self.domin}{self.reverse_path}" self.SmartGeocoder = SmartGeocoder From 4f898676995b1714dad9bdcc85df80a58268744c Mon Sep 17 00:00:00 2001 From: Zero Date: Fri, 15 Jul 2022 14:31:14 +0800 Subject: [PATCH 41/55] add `region` parameter description --- dtoolkit/geoaccessor/geocoder/tencent.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 903cd826f..a5bf271aa 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -95,6 +95,8 @@ def geocode( :param str query: The address or query you wish to geocode. + :param str region: The city of address. + :param bool exactly_one: Return one result or a list of results, if available. From 08a3fa8321c3e54d5086d7317377ae598e13f03d Mon Sep 17 00:00:00 2001 From: Zero Date: Fri, 15 Jul 2022 14:31:26 +0800 Subject: [PATCH 42/55] add typing for api_key --- dtoolkit/geoaccessor/geocoder/tencent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index a5bf271aa..4e47c786b 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -30,7 +30,7 @@ class Tencent(Geocoder): def __init__( self, - api_key, + api_key: str, *, SmartGeocoder: bool = False, scheme: str = None, From f76e85559a04fe06d89bf61129ccdae7712fac2b Mon Sep 17 00:00:00 2001 From: Zero Date: Fri, 15 Jul 2022 14:55:29 +0800 Subject: [PATCH 43/55] Create amap.py --- dtoolkit/geoaccessor/geocoder/amap.py | 286 ++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 dtoolkit/geoaccessor/geocoder/amap.py diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py new file mode 100644 index 000000000..1882fd20d --- /dev/null +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -0,0 +1,286 @@ +from __future__ import annotations + +from functools import partial +from typing import Callable +from urllib.parse import urlencode + +from geopy.exc import GeocoderAuthenticationFailure +from geopy.exc import GeocoderQueryError +from geopy.exc import GeocoderQuotaExceeded +from geopy.exc import GeocoderServiceError +from geopy.exc import GeocoderTimedOut +from geopy.geocoders.base import DEFAULT_SENTINEL +from geopy.geocoders.base import Geocoder +from geopy.location import Location +from geopy.util import logger + + +__all__ = ("Amap",) + + +class Amap(Geocoder): + """ + Geocoder using the Amap Maps API. + + Documentation at: + https://lbs.amap.com/api/webservice/guide/api/georegeo + """ + + api_path = "/v3/geocode/geo/" + reverse_path = "/v3/geocode/regeo/" + + def __init__( + self, + api_key: str, + *, + sig: str = None, + scheme: str = None, + timeout: int = DEFAULT_SENTINEL, + proxies: dict = DEFAULT_SENTINEL, + user_agent: str = None, + ssl_context: ssl.SSLContext = DEFAULT_SENTINEL, + adapter_factory: Callable = None, + ): + """ + :param str api_key: The API key required by Amap Map to perform + geocoding requests. API keys are managed through the Amap APIs + console (https://console.amap.com/dev/key/app). + + :param str sig: The digital signature (sig) to calculate the SN + parameter in request if authentication setting requires + (https://lbs.amap.com/faq/quota-key/key/41169). + + :param str scheme: + See :attr:`geopy.geocoders.options.default_scheme`. + + :param int timeout: + See :attr:`geopy.geocoders.options.default_timeout`. + + :param dict proxies: + See :attr:`geopy.geocoders.options.default_proxies`. + + :param str user_agent: + See :attr:`geopy.geocoders.options.default_user_agent`. + + :type ssl_context: :class:`ssl.SSLContext` + :param ssl_context: + See :attr:`geopy.geocoders.options.default_ssl_context`. + + :param callable adapter_factory: + See :attr:`geopy.geocoders.options.default_adapter_factory`. + """ + + super().__init__( + scheme=scheme, + timeout=timeout, + proxies=proxies, + user_agent=user_agent, + ssl_context=ssl_context, + adapter_factory=adapter_factory, + ) + self.api_key = api_key + self.sig = sig + self.domin = "restapi.amap.com" + self.api = f"{self.scheme}://{self.domin}{self.api_path}" + self.reverse_api = f"{self.scheme}://{self.domin}{self.reverse_path}" + + def geocode( + self, + query: str, + *, + city: str = None, + exactly_one: bool = True, + timeout: int = DEFAULT_SENTINEL, + ) -> None | Location | list[Location]: + """ + Return a location point by address. + + :param str query: The address or query you wish to geocode. + + :param str city: The city of address. + + :param bool exactly_one: Return one result or a list of results, if + available. + + :param int timeout: Time, in seconds, to wait for the geocoding service + to respond before raising a :class:`geopy.exc.GeocoderTimedOut` + exception. Set this only if you wish to override, on this call + only, the value set during the geocoder's initialization. + + :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if + ``exactly_one=False``. + """ + + params = { + "address": query, + **({"city": city} if city else {}), + "key": self.api_key, + } + url = self._construct_url(self.api, params) + + logger.debug(f"{self.__class__.__name__}.geocode: {url}") + callback = partial( + self._parse_json, + exactly_one=exactly_one, + result="geocodes", + ) + + return self._call_geocoder(url, callback, timeout=timeout) + + def reverse( + self, + query: str, + *, + exactly_one: bool = True, + timeout: int = DEFAULT_SENTINEL, + ) -> None | Location | list[Location]: + """ + Return an address by location point. + + :type query: :class:`geopy.point.Point`, list or tuple of ``(latitude, + longitude)``, or string as ``"%(latitude)s, %(longitude)s"``. + :param query: The coordinates for which you wish to obtain the + closest human-readable addresses. + + :param bool exactly_one: Return one result or a list of results, if + available. Tencent's API always return at most one result. + + :param int timeout: Time, in seconds, to wait for the geocoding service + to respond before raising a :class:`geopy.exc.GeocoderTimedOut` + exception. Set this only if you wish to override, on this call + only, the value set during the geocoder's initialization. + + :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if + ``exactly_one=False``. + """ + + params = { + "location": self._coerce_point_to_string(query), + "key": self.api_key, + } + url = self._construct_url(self.reverse_api, params) + + logger.debug(f"{self.__class__.__name__}.reverse: {url}") + callback = partial( + self._parse_json, + exactly_one=exactly_one, + result="regeocode", + ) + + return self._call_geocoder(url, callback, timeout=timeout) + + def _construct_url(self, base_api: str, params: dict) -> str: + """ + Construct geocoding request url. + + :param str base_api: Geocoding function base address - self.api + or self.reverse_api. + + :param dict params: Geocoding params. + + :return: string URL. + """ + + return "?".join((base_api, urlencode(params))) + + def _parse_json( + self, + response: dict, + exactly_one: bool = True, + result: str = "result", + status: str = "status", + address: str = "formatted_address", + location: str = "location", + ) -> None | Location | list[Location]: + """ + Returns location, (latitude, longitude) from JSON feed. + """ + + self._check_status(response.get(status)) + if response is None or result not in response: + return + + place = self._parse_place( + response["result"], + location=location, + address=address, + ) + return place if exactly_one else [place] + + def _parse_place( + self, + place: dict, + address: str = "formatted_address", + location: str = "location", + ) -> None | Location: + """ + Get the location, lat, and lng from a single JSON place. + """ + + if place is None or address not in place or location not in place: + return + + return Location( + place[address], + self._parse_coordinate(place[location]), + place, + ) + + def _parse_coordinate(self, location: str) -> tuple(float | None, float | None): + """ + Get the lat and lng from a string ("lat,lng"). + """ + + if location is None: + return (None, None) + + return tuple(map(float, location.split(","))) + + def _check_status(self, status: str | int): + """ + Validates error statuses. + + Documentation at: + https://lbs.amap.com/api/webservice/guide/tools/info + """ + + if status == 0: + return + elif status == 110: + raise GeocoderAuthenticationFailure("Authentication failure.") + elif status == 111: + raise GeocoderAuthenticationFailure("Signature verification failed.") + elif status == 112: + raise GeocoderAuthenticationFailure("Invalid IP.") + elif status == 113: + raise GeocoderAuthenticationFailure("This feature is not authorized.") + elif status == 120: + raise GeocoderQuotaExceeded( + "The number of requests per second has reached the upper limit.", + ) + elif status == 121: + raise GeocoderQuotaExceeded( + "The number of requests daily has reached the upper limit.", + ) + elif status in 190: + raise GeocoderAuthenticationFailure("Invalid KEY.") + elif status == 199: + raise GeocoderAuthenticationFailure("The webservice isn't enabled.") + elif status in {301, 311}: + raise GeocoderQueryError("KEY illegal or not exist.") + elif status in {300, 306, 301, 320, 330, 331, 348, 351, 394, 395, 399}: + raise GeocoderQueryError("Invalid parameters.") + elif status in {347, 393}: + raise GeocoderQueryError("No results.") + elif status in {400, 402}: + raise GeocoderQueryError("Can't decode the request URL.") + elif status == 404: + raise GeocoderQueryError("Invalid request path.") + elif status == 407: + raise GeocoderQueryError("Invalid request method.") + elif status == 500: + raise GeocoderTimedOut("Request timed out.") + elif 500 < status < 600: + raise GeocoderServiceError("Request server error.") + else: + raise GeocoderQueryError("Unknown error. Status: %r" % status) From 15c67d0b78f7960a4d018f85ce5bf168237d213c Mon Sep 17 00:00:00 2001 From: Zero Date: Thu, 11 Aug 2022 18:08:16 +0800 Subject: [PATCH 44/55] Remove SmartGeocoder Tencent SmartGeocoder could use only once each day --- dtoolkit/geoaccessor/geocoder/tencent.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 4e47c786b..25833caf5 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -32,7 +32,6 @@ def __init__( self, api_key: str, *, - SmartGeocoder: bool = False, scheme: str = None, timeout: int = DEFAULT_SENTINEL, proxies: dict = DEFAULT_SENTINEL, @@ -45,9 +44,6 @@ def __init__( geocoding requests. API keys are managed through the Tencent APIs console (https://lbs.qq.com/dev/console/application/mine). - :param bool SmartGeocoder: If True, use the SmartGeocoder API. - https://lbs.qq.com/service/webService/webServiceGuide/SmartGeocoder - :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. @@ -80,7 +76,6 @@ def __init__( self.domin = "apis.map.qq.com" self.api = f"{self.scheme}://{self.domin}{self.api_path}" self.reverse_api = f"{self.scheme}://{self.domin}{self.reverse_path}" - self.SmartGeocoder = SmartGeocoder def geocode( self, @@ -110,7 +105,7 @@ def geocode( """ params = { - ("smart_address" if self.SmartGeocoder else "address"): query, + "address": query, **({"region": region} if region else {}), "key": self.api_key, } From 6dd12c9aa0fa541fa73b14bd6bde33d35d96aaf2 Mon Sep 17 00:00:00 2001 From: Zero Date: Fri, 12 Aug 2022 09:40:11 +0800 Subject: [PATCH 45/55] Add related tencent api documentation link --- dtoolkit/geoaccessor/geocoder/tencent.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dtoolkit/geoaccessor/geocoder/tencent.py b/dtoolkit/geoaccessor/geocoder/tencent.py index 25833caf5..40becad9b 100644 --- a/dtoolkit/geoaccessor/geocoder/tencent.py +++ b/dtoolkit/geoaccessor/geocoder/tencent.py @@ -102,6 +102,9 @@ def geocode( :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. + + Documentation at: + https://lbs.qq.com/service/webService/webServiceGuide/webServiceGeocoder """ params = { @@ -141,6 +144,9 @@ def reverse( :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. + + Documentation at: + https://lbs.qq.com/service/webService/webServiceGuide/webServiceGcoder """ params = { From b9a142f417c5de14e2bb4f3a2cb876a740cffc61 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 10:10:58 +0800 Subject: [PATCH 46/55] correct `_check_status` --- dtoolkit/geoaccessor/geocoder/amap.py | 81 +++++++++++++-------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index 1882fd20d..c69d65b1a 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -188,7 +188,8 @@ def _parse_json( response: dict, exactly_one: bool = True, result: str = "result", - status: str = "status", + code: str = "infocode", + info: str = "info", address: str = "formatted_address", location: str = "location", ) -> None | Location | list[Location]: @@ -196,7 +197,7 @@ def _parse_json( Returns location, (latitude, longitude) from JSON feed. """ - self._check_status(response.get(status)) + self._check_status(response.get(code), response.get(info)) if response is None or result not in response: return @@ -236,7 +237,7 @@ def _parse_coordinate(self, location: str) -> tuple(float | None, float | None): return tuple(map(float, location.split(","))) - def _check_status(self, status: str | int): + def _check_status(self, code: int, info: str): """ Validates error statuses. @@ -244,43 +245,41 @@ def _check_status(self, status: str | int): https://lbs.amap.com/api/webservice/guide/tools/info """ - if status == 0: + if code == 10000: return - elif status == 110: - raise GeocoderAuthenticationFailure("Authentication failure.") - elif status == 111: - raise GeocoderAuthenticationFailure("Signature verification failed.") - elif status == 112: - raise GeocoderAuthenticationFailure("Invalid IP.") - elif status == 113: - raise GeocoderAuthenticationFailure("This feature is not authorized.") - elif status == 120: - raise GeocoderQuotaExceeded( - "The number of requests per second has reached the upper limit.", - ) - elif status == 121: - raise GeocoderQuotaExceeded( - "The number of requests daily has reached the upper limit.", - ) - elif status in 190: - raise GeocoderAuthenticationFailure("Invalid KEY.") - elif status == 199: - raise GeocoderAuthenticationFailure("The webservice isn't enabled.") - elif status in {301, 311}: - raise GeocoderQueryError("KEY illegal or not exist.") - elif status in {300, 306, 301, 320, 330, 331, 348, 351, 394, 395, 399}: - raise GeocoderQueryError("Invalid parameters.") - elif status in {347, 393}: - raise GeocoderQueryError("No results.") - elif status in {400, 402}: - raise GeocoderQueryError("Can't decode the request URL.") - elif status == 404: - raise GeocoderQueryError("Invalid request path.") - elif status == 407: - raise GeocoderQueryError("Invalid request method.") - elif status == 500: - raise GeocoderTimedOut("Request timed out.") - elif 500 < status < 600: - raise GeocoderServiceError("Request server error.") + elif code in { + 10001, + 10002, + 10005, + 10006, + 10007, + 10009, + 10010, + 10012, + 10026, + 10041, + }: + raise GeocoderAuthenticationFailure(f"{info}.") + elif ( + code + in { + 10003, + 10004, + 10014, + 10015, + 10019, + 10020, + 10021, + 10029, + 10044, + 10045, + } + or 40000 <= code <= 50000 + ): + raise GeocoderQuotaExceeded(f"{info}.") + elif code in {10013, 10017} or 20000 <= code < 30000: + raise GeocoderQueryError(f"{info}.") + elif code in {10011} or 30000 <= code < 40000: + raise GeocoderServiceError(f"{info}.") else: - raise GeocoderQueryError("Unknown error. Status: %r" % status) + raise GeocoderQueryError(f"{info}.") From bd6d2eb7a7a8cde408a8b000b4de3dd4c6bb610d Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 10:11:21 +0800 Subject: [PATCH 47/55] update logic --- dtoolkit/geoaccessor/geocoder/amap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index c69d65b1a..8b463cbb3 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -279,7 +279,7 @@ def _check_status(self, code: int, info: str): raise GeocoderQuotaExceeded(f"{info}.") elif code in {10013, 10017} or 20000 <= code < 30000: raise GeocoderQueryError(f"{info}.") - elif code in {10011} or 30000 <= code < 40000: + elif code == 10011 or 30000 <= code < 40000: raise GeocoderServiceError(f"{info}.") else: raise GeocoderQueryError(f"{info}.") From c808d67076d548f5c72319bf1d2efcf8e22c59c2 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 10:31:05 +0800 Subject: [PATCH 48/55] make code more readable --- dtoolkit/geoaccessor/geocoder/amap.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index 8b463cbb3..098c813ed 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -2,7 +2,6 @@ from functools import partial from typing import Callable -from urllib.parse import urlencode from geopy.exc import GeocoderAuthenticationFailure from geopy.exc import GeocoderQueryError @@ -113,7 +112,7 @@ def geocode( params = { "address": query, - **({"city": city} if city else {}), + "city": city, "key": self.api_key, } url = self._construct_url(self.api, params) @@ -180,7 +179,10 @@ def _construct_url(self, base_api: str, params: dict) -> str: :return: string URL. """ + from urllib.parse import urlencode + # Remove empty value item + params = {k: v for k, v in params.items() if v} return "?".join((base_api, urlencode(params))) def _parse_json( From cb71bafc8d0e7cf798e528b1116299a4173e918a Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 10:35:28 +0800 Subject: [PATCH 49/55] index Amap --- dtoolkit/geoaccessor/geocoder/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dtoolkit/geoaccessor/geocoder/__init__.py b/dtoolkit/geoaccessor/geocoder/__init__.py index 82b9c27d3..bc73a6cf8 100644 --- a/dtoolkit/geoaccessor/geocoder/__init__.py +++ b/dtoolkit/geoaccessor/geocoder/__init__.py @@ -1 +1,2 @@ +from dtoolkit.geoaccessor.geocoder.amap import Amap # noqa from dtoolkit.geoaccessor.geocoder.tencent import Tencent # noqa From f36c1f3b9909b9529927465b837cdfb130faec13 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 16:06:59 +0800 Subject: [PATCH 50/55] complete basic logic --- dtoolkit/geoaccessor/geocoder/amap.py | 104 +++++++++++++------------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index 098c813ed..5699451ae 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -25,8 +25,8 @@ class Amap(Geocoder): https://lbs.amap.com/api/webservice/guide/api/georegeo """ - api_path = "/v3/geocode/geo/" - reverse_path = "/v3/geocode/regeo/" + api_path = "/v3/geocode/geo" + reverse_path = "/v3/geocode/regeo" def __init__( self, @@ -110,19 +110,11 @@ def geocode( ``exactly_one=False``. """ - params = { - "address": query, - "city": city, - "key": self.api_key, - } + params = {"address": query, "city": city} url = self._construct_url(self.api, params) logger.debug(f"{self.__class__.__name__}.geocode: {url}") - callback = partial( - self._parse_json, - exactly_one=exactly_one, - result="geocodes", - ) + callback = partial(self._parse_geocode_json, exactly_one=exactly_one) return self._call_geocoder(url, callback, timeout=timeout) @@ -154,17 +146,15 @@ def reverse( """ params = { - "location": self._coerce_point_to_string(query), - "key": self.api_key, + "location": self._coerce_point_to_string( + query, + output_format="%(lon)s,%(lat)s", + ) } url = self._construct_url(self.reverse_api, params) logger.debug(f"{self.__class__.__name__}.reverse: {url}") - callback = partial( - self._parse_json, - exactly_one=exactly_one, - result="regeocode", - ) + callback = partial(self._parse_reverse_json, exactly_one=exactly_one) return self._call_geocoder(url, callback, timeout=timeout) @@ -183,63 +173,75 @@ def _construct_url(self, base_api: str, params: dict) -> str: # Remove empty value item params = {k: v for k, v in params.items() if v} - return "?".join((base_api, urlencode(params))) + query_string = urlencode(params) + return f"{base_api}?key={self.api_key}&{query_string}" - def _parse_json( + def _parse_geocode_json( self, response: dict, exactly_one: bool = True, - result: str = "result", - code: str = "infocode", - info: str = "info", - address: str = "formatted_address", - location: str = "location", ) -> None | Location | list[Location]: """ Returns location, (latitude, longitude) from JSON feed. """ - self._check_status(response.get(code), response.get(info)) - if response is None or result not in response: + def _parse_place(place: dict) -> None | Location: + """ + Returns location, (latitude, longitude) from JSON feed. + """ + + if not isinstance(place, dict): + return + + address = place.get("formatted_address") + point = self._parse_coordinate(place.get("location")) + return Location(address, point, place) + + if not isinstance(response, dict): return + self._check_status(response.get("infocode"), response.get("info")) - place = self._parse_place( - response["result"], - location=location, - address=address, - ) - return place if exactly_one else [place] + result = response.get("geocodes") + if exactly_one: + return _parse_place(result[0]) + return [_parse_place(place) for place in result] - def _parse_place( - self, - place: dict, - address: str = "formatted_address", - location: str = "location", - ) -> None | Location: + def _parse_reverse_json(self, response: dict, exactly_one: bool = True): """ - Get the location, lat, and lng from a single JSON place. + Returns location, (latitude, longitude) from JSON feed. """ - if place is None or address not in place or location not in place: + def _parse_place(place: dict) -> None | Location: + """ + Returns location, (latitude, longitude) from JSON feed. + """ + + if not isinstance(place, dict): + return + + address = place.get("formatted_address") + location = place.get("addressComponent", {}).get("streetNumber", {}) + point = self._parse_coordinate(location) + return Location(address, point, place) + + if not isinstance(response, dict): return + self._check_status(response.get("infocode"), response.get("info")) - return Location( - place[address], - self._parse_coordinate(place[location]), - place, - ) + place = _parse_place(response.get("regeocode")) + return place if exactly_one else [place] def _parse_coordinate(self, location: str) -> tuple(float | None, float | None): """ Get the lat and lng from a string ("lat,lng"). """ - if location is None: + if not isinstance(location, str): return (None, None) - return tuple(map(float, location.split(","))) + return tuple(reversed(tuple(map(float, location.split(","))))) - def _check_status(self, code: int, info: str): + def _check_status(self, code: str, info: str): """ Validates error statuses. @@ -247,6 +249,8 @@ def _check_status(self, code: int, info: str): https://lbs.amap.com/api/webservice/guide/tools/info """ + code = int(code) + if code == 10000: return elif code in { From 3d3a3a33ecd09539f06c1c4e65d24b2109ace919 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 08:08:23 +0000 Subject: [PATCH 51/55] BOT: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dtoolkit/geoaccessor/geocoder/amap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index 5699451ae..75789fa9f 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -149,7 +149,7 @@ def reverse( "location": self._coerce_point_to_string( query, output_format="%(lon)s,%(lat)s", - ) + ), } url = self._construct_url(self.reverse_api, params) From a5ba79c07204f6591937a4f0694db2eeae296e51 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 16:29:55 +0800 Subject: [PATCH 52/55] remove sig --- dtoolkit/geoaccessor/geocoder/amap.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index 5699451ae..147652c4c 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -32,7 +32,6 @@ def __init__( self, api_key: str, *, - sig: str = None, scheme: str = None, timeout: int = DEFAULT_SENTINEL, proxies: dict = DEFAULT_SENTINEL, @@ -45,10 +44,6 @@ def __init__( geocoding requests. API keys are managed through the Amap APIs console (https://console.amap.com/dev/key/app). - :param str sig: The digital signature (sig) to calculate the SN - parameter in request if authentication setting requires - (https://lbs.amap.com/faq/quota-key/key/41169). - :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. @@ -78,7 +73,6 @@ def __init__( adapter_factory=adapter_factory, ) self.api_key = api_key - self.sig = sig self.domin = "restapi.amap.com" self.api = f"{self.scheme}://{self.domin}{self.api_path}" self.reverse_api = f"{self.scheme}://{self.domin}{self.reverse_path}" @@ -110,7 +104,7 @@ def geocode( ``exactly_one=False``. """ - params = {"address": query, "city": city} + params = {"address": query, "city": city, "key": self.api_key} url = self._construct_url(self.api, params) logger.debug(f"{self.__class__.__name__}.geocode: {url}") @@ -146,10 +140,11 @@ def reverse( """ params = { + "key": self.api_key, "location": self._coerce_point_to_string( query, output_format="%(lon)s,%(lat)s", - ) + ), } url = self._construct_url(self.reverse_api, params) @@ -174,7 +169,7 @@ def _construct_url(self, base_api: str, params: dict) -> str: # Remove empty value item params = {k: v for k, v in params.items() if v} query_string = urlencode(params) - return f"{base_api}?key={self.api_key}&{query_string}" + return f"{base_api}?{query_string}" def _parse_geocode_json( self, From a86532afa0e0efd965429911afdbd255f920375e Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 17:11:33 +0800 Subject: [PATCH 53/55] simplify a bit --- dtoolkit/geoaccessor/geocoder/amap.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index 147652c4c..e566cf4a1 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -206,25 +206,19 @@ def _parse_reverse_json(self, response: dict, exactly_one: bool = True): Returns location, (latitude, longitude) from JSON feed. """ - def _parse_place(place: dict) -> None | Location: - """ - Returns location, (latitude, longitude) from JSON feed. - """ - - if not isinstance(place, dict): - return - - address = place.get("formatted_address") - location = place.get("addressComponent", {}).get("streetNumber", {}) - point = self._parse_coordinate(location) - return Location(address, point, place) - if not isinstance(response, dict): return self._check_status(response.get("infocode"), response.get("info")) - place = _parse_place(response.get("regeocode")) - return place if exactly_one else [place] + place = response.get("regeocode", {}) + address = place.get("formatted_address") + point = self._parse_coordinate( + place.get("addressComponent", {}) + .get("streetNumber", {}) + .get("location", None) + ) + location = Location(address, point, place) + return location if exactly_one else [location] def _parse_coordinate(self, location: str) -> tuple(float | None, float | None): """ From a755e56d5064a7008ed212704116f5bb63cf15d6 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Aug 2022 17:12:12 +0800 Subject: [PATCH 54/55] rename variable --- dtoolkit/geoaccessor/geocoder/amap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index e566cf4a1..46ab1b0fe 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -196,10 +196,10 @@ def _parse_place(place: dict) -> None | Location: return self._check_status(response.get("infocode"), response.get("info")) - result = response.get("geocodes") + places = response.get("geocodes") if exactly_one: - return _parse_place(result[0]) - return [_parse_place(place) for place in result] + return _parse_place(places[0]) + return [_parse_place(place) for place in places] def _parse_reverse_json(self, response: dict, exactly_one: bool = True): """ From 832e03d3afdd0b238155ac4faf0129262e8e4864 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 09:12:33 +0000 Subject: [PATCH 55/55] BOT: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- dtoolkit/geoaccessor/geocoder/amap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtoolkit/geoaccessor/geocoder/amap.py b/dtoolkit/geoaccessor/geocoder/amap.py index 46ab1b0fe..a1bddd084 100644 --- a/dtoolkit/geoaccessor/geocoder/amap.py +++ b/dtoolkit/geoaccessor/geocoder/amap.py @@ -215,7 +215,7 @@ def _parse_reverse_json(self, response: dict, exactly_one: bool = True): point = self._parse_coordinate( place.get("addressComponent", {}) .get("streetNumber", {}) - .get("location", None) + .get("location", None), ) location = Location(address, point, place) return location if exactly_one else [location]