Repository: mie-lab/trackintel
Pull request #206 (2021-04-13 10:00:03)

ENH: Merge WGS84 checking function
We now have one function to check and/or transform WGS84 `trackintel.geogr.distances.check_gdf_crs`

codecov[bot]: # [Codecov](https://codecov.io/gh/mie-lab/trackintel/pull/206?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) Report
> Merging [#206](https://codecov.io/gh/mie-lab/trackintel/pull/206?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) (216e11e) into [master](https://codecov.io/gh/mie-lab/trackintel/commit/fac0c9eb2fa4bee90e6abd8dc3c2c4ea5d5d8b89?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) (fac0c9e) will **decrease** coverage by `0.10%`.
> The diff coverage is `91.17%`.

[![Impacted file tree graph](https://codecov.io/gh/mie-lab/trackintel/pull/206/graphs/tree.svg?width=650&height=150&src=pr&token=ITDZEEE1MO&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab)](https://codecov.io/gh/mie-lab/trackintel/pull/206?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab)

```diff
@@            Coverage Diff             @@
##           master     #206      +/-   ##
==========================================
- Coverage   88.25%   88.15%   -0.11%     
==========================================
  Files          36       36              
  Lines        1490     1494       +4     
  Branches      256      257       +1     
==========================================
+ Hits         1315     1317       +2     
- Misses        136      137       +1     
- Partials       39       40       +1     
```


| [Impacted Files](https://codecov.io/gh/mie-lab/trackintel/pull/206?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab) | Coverage Δ | |
|---|---|---|
| [trackintel/visualization/util.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC92aXN1YWxpemF0aW9uL3V0aWwucHk=) | `98.14% <ø> (-0.24%)` | :arrow_down: |
| [trackintel/analysis/modal\_split.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9hbmFseXNpcy9tb2RhbF9zcGxpdC5weQ==) | `92.30% <66.66%> (ø)` | |
| [trackintel/geogr/distances.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9nZW9nci9kaXN0YW5jZXMucHk=) | `93.06% <87.50%> (-1.56%)` | :arrow_down: |
| [trackintel/analysis/label.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC9hbmFseXNpcy9sYWJlbC5weQ==) | `97.22% <100.00%> (ø)` | |
| [trackintel/visualization/locations.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC92aXN1YWxpemF0aW9uL2xvY2F0aW9ucy5weQ==) | `90.32% <100.00%> (+0.32%)` | :arrow_up: |
| [trackintel/visualization/positionfixes.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC92aXN1YWxpemF0aW9uL3Bvc2l0aW9uZml4ZXMucHk=) | `90.90% <100.00%> (+0.43%)` | :arrow_up: |
| [trackintel/visualization/staypoints.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC92aXN1YWxpemF0aW9uL3N0YXlwb2ludHMucHk=) | `90.90% <100.00%> (+0.28%)` | :arrow_up: |
| [trackintel/visualization/triplegs.py](https://codecov.io/gh/mie-lab/trackintel/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab#diff-dHJhY2tpbnRlbC92aXN1YWxpemF0aW9uL3RyaXBsZWdzLnB5) | `85.18% <100.00%> (+0.56%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mie-lab/trackintel/pull/206?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mie-lab/trackintel/pull/206?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab). Last update [fac0c9e...216e11e](https://codecov.io/gh/mie-lab/trackintel/pull/206?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mie-lab).


Patch:
diff --git a/docs/modules/geogr.rst b/docs/modules/geogr.rst
index 2e60912..023f7bd 100644
--- a/docs/modules/geogr.rst
+++ b/docs/modules/geogr.rst
@@ -8,7 +8,7 @@ distance functions for points and trajectories
 
 .. autofunction:: trackintel.geogr.distances.meters_to_decimal_degrees
 
-.. autofunction:: trackintel.geogr.distances.check_wgs_for_distance_calculation
+.. autofunction:: trackintel.geogr.distances.check_gdf_crs
 
 .. autofunction:: trackintel.geogr.distances.calculate_haversine_length
 
diff --git a/trackintel/analysis/label.py b/trackintel/analysis/label.py
index 3eb83e6..9f90f8c 100644
--- a/trackintel/analysis/label.py
+++ b/trackintel/analysis/label.py
@@ -1,7 +1,7 @@
 import datetime
 import numpy as np
 
-from trackintel.geogr.distances import check_wgs_for_distance_calculation, calculate_haversine_length
+from trackintel.geogr.distances import check_gdf_crs, calculate_haversine_length
 
 
 def create_activity_flag(staypoints, method="time_threshold", time_threshold=5.0, activity_column_name="activity"):
@@ -124,9 +124,9 @@ def _predict_transport_mode_simple_coarse(triplegs_in, categories):
         raise ValueError("the categories must be in increasing order")
 
     triplegs = triplegs_in.copy()
-    wgs = check_wgs_for_distance_calculation(triplegs.crs)
+    if_planer_crs = check_gdf_crs(triplegs)
     #
-    if wgs:
+    if not if_planer_crs:
         triplegs["distance"] = calculate_haversine_length(triplegs)
     else:
         triplegs["distance"] = triplegs.length
diff --git a/trackintel/analysis/modal_split.py b/trackintel/analysis/modal_split.py
index 34986f5..e297e13 100644
--- a/trackintel/analysis/modal_split.py
+++ b/trackintel/analysis/modal_split.py
@@ -1,7 +1,7 @@
 import numpy as np
 import pandas as pd
 
-from trackintel.geogr.distances import check_wgs_for_distance_calculation, calculate_haversine_length
+from trackintel.geogr.distances import check_gdf_crs, calculate_haversine_length
 
 
 def calculate_modal_split(tpls_in, freq=None, metric="count", per_user=False, norm=False):
@@ -48,8 +48,8 @@ def calculate_modal_split(tpls_in, freq=None, metric="count", per_user=False, no
 
     # precalculate distance and duration if required
     if metric == "distance":
-        wgs = check_wgs_for_distance_calculation(tpls.crs)
-        if wgs:
+        if_planer_crs = check_gdf_crs(tpls)
+        if not if_planer_crs:
             tpls["distance"] = calculate_haversine_length(tpls)
         else:
             tpls["distance"] = tpls.length
diff --git a/trackintel/geogr/distances.py b/trackintel/geogr/distances.py
index b9a0bd4..a61b3f4 100644
--- a/trackintel/geogr/distances.py
+++ b/trackintel/geogr/distances.py
@@ -183,40 +183,58 @@ def meters_to_decimal_degrees(meters, latitude):
     return meters / (111.32 * 1000.0 * cos(latitude * (pi / 180.0)))
 
 
-def check_wgs_for_distance_calculation(crs):
+def check_gdf_crs(gdf, transform=False):
     """
-    Evaluate if the crs is wgs 84 and warns if no crs is defined.
+    Check if GeoDataFrame has CRS or is already in WGS84.
+
+    Additionally transform a GeoDataFrame into WGS84.
 
     Parameters
     ----------
-    crs : 'pyproj.crs.crs.CRS'
-        Can be the result of `gdf.crs`
+    gdf : GeoDataFrame
+        input GeoDataFrame for checking or transform
+
+    transform : bool, default False
+        whether to transform gdf into WGS84.
 
     Returns
     -------
-    is_wgs : bool
-        True if wgs84 is assumed
+    if_planer : bool
+        True if the returned gdf has planar crs.
+
+    gdf : GeoDataFrame
+        if transform is True, return the re-projected gdf.
 
-    Notes
-    -----
-    We do not check for planar crs as this is already done when geopandas.length is called.
 
     Examples
     --------
-    >>> from trackintel.geogr.distances import check_wgs_for_distance_calculation
-    >>> check_wgs_for_distance_calculation(triplegs.crs)
+    >>> from trackintel.geogr.distances import check_gdf_crs
+    >>> check_gdf_crs(triplegs, transform=False)
     """
-    is_wgs = False
+    if_planer = False
+    if gdf.crs is None:
+        # projection is not defined
+        if_planer = False
+        if transform:
+            gdf.crs = "EPSG:4326"
+        else:
+            warnings.warn("Your data is not projected.")
 
-    if crs == 4326:
-        is_wgs = True
+    elif gdf.crs == "EPSG:4326":
+        # if projection is defined as WGS84
+        if_planer = False
 
-    elif crs is None:
-        is_wgs = True
-        warnings.warn(
-            "Your data is not projected. WGS84 is assumed and for length calculation the haversine " "distance is used"
-        )
-    return is_wgs
+    else:
+        # if projection is defined but not as WGS84
+        if_planer = True
+        if transform:
+            if_planer = False
+            gdf = gdf.to_crs("EPSG:4326")
+
+    if transform:
+        return if_planer, gdf
+    else:
+        return if_planer
 
 
 def calculate_haversine_length(gdf):
diff --git a/trackintel/visualization/locations.py b/trackintel/visualization/locations.py
index 936be18..de01b62 100644
--- a/trackintel/visualization/locations.py
+++ b/trackintel/visualization/locations.py
@@ -4,7 +4,8 @@ import warnings
 
 from trackintel.geogr.distances import meters_to_decimal_degrees
 from trackintel.visualization.osm import plot_osm_streets
-from trackintel.visualization.util import regular_figure, save_fig, transform_gdf_to_wgs84
+from trackintel.visualization.util import regular_figure, save_fig
+from trackintel.geogr.distances import check_gdf_crs
 
 
 def plot_locations(
@@ -56,7 +57,7 @@ def plot_locations(
         _, ax = regular_figure()
     else:
         ax = axis
-    locations = transform_gdf_to_wgs84(locations)
+    _, locations = check_gdf_crs(locations, transform=True)
 
     if staypoints is not None:
         staypoints.as_staypoints.plot(radius=staypoints_radius, positionfixes=positionfixes, plot_osm=plot_osm, axis=ax)
diff --git a/trackintel/visualization/positionfixes.py b/trackintel/visualization/positionfixes.py
index 6cc4de3..724933e 100644
--- a/trackintel/visualization/positionfixes.py
+++ b/trackintel/visualization/positionfixes.py
@@ -1,7 +1,8 @@
 import matplotlib.pyplot as plt
 import warnings
 from trackintel.visualization.osm import plot_osm_streets
-from trackintel.visualization.util import regular_figure, save_fig, transform_gdf_to_wgs84
+from trackintel.visualization.util import regular_figure, save_fig
+from trackintel.geogr.distances import check_gdf_crs
 
 
 def plot_positionfixes(positionfixes, out_filename=None, plot_osm=False, axis=None):
@@ -34,7 +35,7 @@ def plot_positionfixes(positionfixes, out_filename=None, plot_osm=False, axis=No
         _, ax = regular_figure()
     else:
         ax = axis
-    positionfixes = transform_gdf_to_wgs84(positionfixes)
+    _, positionfixes = check_gdf_crs(positionfixes, transform=True)
 
     if plot_osm:
         west = positionfixes.geometry.x.min()
diff --git a/trackintel/visualization/staypoints.py b/trackintel/visualization/staypoints.py
index c1632d6..68e14d8 100644
--- a/trackintel/visualization/staypoints.py
+++ b/trackintel/visualization/staypoints.py
@@ -4,7 +4,8 @@ import warnings
 
 from trackintel.geogr.distances import meters_to_decimal_degrees
 from trackintel.visualization.osm import plot_osm_streets
-from trackintel.visualization.util import regular_figure, save_fig, transform_gdf_to_wgs84
+from trackintel.visualization.util import regular_figure, save_fig
+from trackintel.geogr.distances import check_gdf_crs
 
 
 def plot_staypoints(staypoints, out_filename=None, radius=100, positionfixes=None, plot_osm=False, axis=None):
@@ -44,7 +45,7 @@ def plot_staypoints(staypoints, out_filename=None, radius=100, positionfixes=Non
     else:
         ax = axis
     name_geocol = staypoints.geometry.name
-    staypoints = transform_gdf_to_wgs84(staypoints)
+    _, staypoints = check_gdf_crs(staypoints, transform=True)
 
     if positionfixes is not None:
         positionfixes.as_positionfixes.plot(plot_osm=plot_osm, axis=ax)
diff --git a/trackintel/visualization/triplegs.py b/trackintel/visualization/triplegs.py
index 2ba75ce..ec0f4fa 100644
--- a/trackintel/visualization/triplegs.py
+++ b/trackintel/visualization/triplegs.py
@@ -3,7 +3,8 @@ import matplotlib.pyplot as plt
 import warnings
 
 from trackintel.visualization.osm import plot_osm_streets
-from trackintel.visualization.util import regular_figure, save_fig, transform_gdf_to_wgs84
+from trackintel.visualization.util import regular_figure, save_fig
+from trackintel.geogr.distances import check_gdf_crs
 
 
 def plot_triplegs(
@@ -47,7 +48,7 @@ def plot_triplegs(
         _, ax = regular_figure()
     else:
         ax = axis
-    triplegs = transform_gdf_to_wgs84(triplegs)
+    _, triplegs = check_gdf_crs(triplegs, transform=True)
 
     if staypoints is not None:
         staypoints.as_staypoints.plot(radius=staypoints_radius, positionfixes=positionfixes, plot_osm=plot_osm, axis=ax)
diff --git a/trackintel/visualization/util.py b/trackintel/visualization/util.py
index 390cafe..2e77f94 100644
--- a/trackintel/visualization/util.py
+++ b/trackintel/visualization/util.py
@@ -118,25 +118,3 @@ def save_fig(out_filename, tight="tight", formats=["png", "pdf"]):
         logging.info("...took {} s!".format(round(time.time() - ts, 2)))
     plt.close()
     logging.info("Finished!")
-
-
-def transform_gdf_to_wgs84(gdf):
-    """Transform a GeoDataFrame into WGS84.
-
-    Additionally checks if data has CRS or is already in WGS84.
-
-    Parameters
-    ----------
-    gdf : GeoDataFrame
-
-    Example
-    -------
-    >>> gdf = transform_gdf_to_wgs84(gdf)
-    """
-    crs_wgs84 = "EPSG:4326"
-    if gdf.crs is None:
-        warnings.warn("Coordinate System (CRS) is not set, default to WGS84.")
-        gdf.crs = crs_wgs84
-    elif gdf.crs != crs_wgs84:
-        gdf = gdf.to_crs(crs_wgs84)
-    return gdf


Test patch:
diff --git a/tests/analysis/test_label.py b/tests/analysis/test_label.py
index 05837a1..a6f3ec1 100644
--- a/tests/analysis/test_label.py
+++ b/tests/analysis/test_label.py
@@ -70,8 +70,7 @@ class TestPredict_transport_mode:
 
         with pytest.warns(
             UserWarning,
-            match="Your data is not projected. WGS84 is assumed and for length calculation"
-            " the haversine distance is used",
+            match="Your data is not projected.",
         ):
             tpls_transport_mode = tpls.as_triplegs.predict_transport_mode(method="simple-coarse")
 
diff --git a/tests/geogr/test_distances.py b/tests/geogr/test_distances.py
index a306567..0c67329 100644
--- a/tests/geogr/test_distances.py
+++ b/tests/geogr/test_distances.py
@@ -7,9 +7,11 @@ import pytest
 from shapely import wkt
 from shapely.geometry import LineString, MultiLineString
 from sklearn.metrics import pairwise_distances
+from geopandas.testing import assert_geodataframe_equal
 
 import trackintel as ti
 from trackintel.geogr.distances import (
+    check_gdf_crs,
     meters_to_decimal_degrees,
     calculate_distance_matrix,
     calculate_haversine_length,
@@ -171,6 +173,25 @@ class TestCalculate_distance_matrix:
             calculate_distance_matrix(X=gdf, dist_metric="dtw", n_jobs=1)
 
 
+class TestCheck_gdf_crs:
+    """Tests for check_gdf_crs() method."""
+
+    def test_transformation(self):
+        """Check if data gets transformed."""
+        file = os.path.join("tests", "data", "positionfixes.csv")
+        pfs = ti.read_positionfixes_csv(file, sep=";", crs="EPSG:4326", index_col=None)
+        pfs_2056 = pfs.to_crs("EPSG:2056")
+        _, pfs_4326 = check_gdf_crs(pfs_2056, transform=True)
+        assert_geodataframe_equal(pfs, pfs_4326, check_less_precise=True)
+
+    def test_crs_warning(self):
+        """Check if warning is raised for data without crs."""
+        file = os.path.join("tests", "data", "positionfixes.csv")
+        pfs = ti.read_positionfixes_csv(file, sep=";", crs=None, index_col=None)
+        with pytest.warns(UserWarning):
+            check_gdf_crs(pfs)
+
+
 class TestMetersToDecimalDegrees:
     """Tests for the meters_to_decimal_degrees() function."""
 
diff --git a/tests/visualization/test_util.py b/tests/visualization/test_util.py
index b6c7a65..ad2cdd1 100644
--- a/tests/visualization/test_util.py
+++ b/tests/visualization/test_util.py
@@ -1,31 +1,11 @@
 import os
 import pytest
-from geopandas.testing import assert_geodataframe_equal
 import numpy as np
 
 import trackintel as ti
 from trackintel.visualization.util import a4_figsize
 
 
-class TestTransform_gdf_to_wgs84:
-    """Tests for transform_gdf_to_wgs84() method."""
-
-    def test_transformation(self):
-        """Check if data gets transformed."""
-        file = os.path.join("tests", "data", "positionfixes.csv")
-        pfs = ti.read_positionfixes_csv(file, sep=";", crs="EPSG:4326", index_col=None)
-        pfs_2056 = pfs.to_crs("EPSG:2056")
-        pfs_4326 = ti.visualization.util.transform_gdf_to_wgs84(pfs_2056)
-        assert_geodataframe_equal(pfs, pfs_4326, check_less_precise=True)
-
-    def test_crs_warning(self):
-        """Check if warning is raised for data without crs."""
-        file = os.path.join("tests", "data", "positionfixes.csv")
-        pfs = ti.read_positionfixes_csv(file, sep=";", crs=None, index_col=None)
-        with pytest.warns(UserWarning):
-            ti.visualization.util.transform_gdf_to_wgs84(pfs)
-
-
 class TestA4_figsize:
     """Tests for a4_figsize() method."""