Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add converter between AV2 city coordinate systems, and WGS84 and UTM #28

Merged
merged 14 commits into from
Apr 12, 2022
1 change: 1 addition & 0 deletions conda/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ dependencies:
- pandas
- pip
- pyarrow
- pyproj
- rich
- scipy
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ install_requires =
opencv-python
pandas
pyarrow
pyproj
rich
scipy

Expand Down
104 changes: 104 additions & 0 deletions src/av2/geometry/utm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# <Copyright 2022, Argo AI, LLC. Released under the MIT license.>

"""Utilities for converting AV2 city coordinates to UTM or WGS84 coordinate systems."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add some information about these two coordinate systems here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I added some references.


from enum import Enum
from typing import Tuple, Union

import numpy as np
from pyproj import Proj

from av2.utils.typing import NDArrayFloat, NDArrayInt


benjaminrwilson marked this conversation as resolved.
Show resolved Hide resolved
class CityName(str, Enum):
"""Abbreviations of names of cities featured in Argoverse 2."""

ATX = "ATX" # Austin, Texas
DTW = "DTW" # Detroit, Michigan
MIA = "MIA" # Miami, Florida
PAO = "PAO" # Palo Alto, California
PIT = "PIT" # Pittsburgh, PA
WDC = "WDC" # Washington, DC


# All are North UTM zones (Northern hemisphere)
UTM_ZONE_MAP = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
UTM_ZONE_MAP = {
UTM_ZONE_MAP: Final[Dict[CityName, int]] = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, updated now.

CityName.ATX: 14,
CityName.DTW: 17,
CityName.MIA: 17,
CityName.PAO: 10,
CityName.PIT: 17,
CityName.WDC: 18,
}


# as (lat, long) tuples
CITY_ORIGIN_LATLONG_DICT = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
CITY_ORIGIN_LATLONG_DICT = {
CITY_ORIGIN_LATLONG_DICT: Final[Dict[CityName, Tuple[float, float]]] = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, updated now.

CityName.ATX: (30.27464237939507, -97.7404457407424),
CityName.DTW: (42.29993066912924, -83.17555750783717),
CityName.MIA: (25.77452579915163, -80.19656914449405),
CityName.PAO: (37.416065, -122.13571963362166),
CityName.PIT: (40.44177902989321, -80.01294377242584),
CityName.WDC: (38.889377, -77.0355047439081),
}


def convert_gps_to_utm(lat: float, long: float, city_name: CityName) -> Tuple[float, float]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that long is common in the GIS community, but I think this is a bit confusing with the numerical type, long. Perhaps we could use lon?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I switched to longitude, since I think it's not much longer, and may be clearer.

"""Convert GPS coordinates to UTM coordinates.

Args:
lat: latitude of query point.
long: longitude of query point.
city_name: name of city, where query point is located.

Returns:
easting: corresponding UTM Easting.
northing: corresponding UTM Northing.
"""
projector = Proj(proj="utm", zone=UTM_ZONE_MAP[city_name], ellps="WGS84", datum="WGS84", units="m")

# convert to UTM.
easting, northing = projector(long, lat)

return easting, northing


def convert_city_coords_to_utm(points_city: Union[NDArrayFloat, NDArrayInt], city_name: CityName) -> NDArrayFloat:
"""Convert city coordinates to UTM coordinates.

Args:
points_city: 2d coordinates, representing query points in the city coordinate frame.
city_name: name of city, where query points are located.

Returns:
Points in the UTM coordinate system, as (easting, northing).
"""
lat, long = CITY_ORIGIN_LATLONG_DICT[city_name]
# get (easting, northing) of origin
origin_utm = convert_gps_to_utm(lat=lat, long=long, city_name=city_name)

points_utm = points_city + origin_utm
return points_utm


def convert_city_coords_to_wgs84(points_city: Union[NDArrayFloat, NDArrayInt], city_name: CityName) -> NDArrayFloat:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def convert_city_coords_to_wgs84(points_city: Union[NDArrayFloat, NDArrayInt], city_name: CityName) -> NDArrayFloat:
def convert_city_coords_to_wgs84(points_xy_city: Union[NDArrayFloat, NDArrayInt], city_name: CityName) -> NDArrayFloat:

Prefer xy to make it clear these points are 2D.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit biased towards what we have, since I think points_xy_city is maybe getting a bit too long for a name.

"""Convert city coordinates to WGS84 coordinates.

Args:
points_city: 2d coordinates, representing query points in the city coordinate frame.
city_name: name of city, where query points are located.

Returns:
Points in the WGS84 coordinate system, as (lat, long).
"""
points_utm = convert_city_coords_to_utm(points_city, city_name)

projector = Proj(proj="utm", zone=UTM_ZONE_MAP[city_name], ellps="WGS84", datum="WGS84", units="m")

points_wgs84 = []
for easting, northing in points_utm:
long, lat = projector(easting, northing, inverse=True)
points_wgs84.append((lat, long))

return np.array(points_wgs84)
61 changes: 61 additions & 0 deletions tests/geometry/test_utm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# <Copyright 2022, Argo AI, LLC. Released under the MIT license.>

"""Unit tests on utilities for converting AV2 city coordinates to UTM or WGS84 coordinate systems."""


import numpy as np

import av2.geometry.utm as geo_utils
from av2.geometry.utm import CityName, CITY_ORIGIN_LATLONG_DICT


def test_convert_city_coords_to_wgs84_atx() -> None:
"""Convert city coordinates from Austin, TX to GPS coordinates."""

points_city = np.array(
[
[1745.37, -1421.37],
[1738.54, -1415.03],
[1731.53, -1410.81],
]
)

wgs84_coords = geo_utils.convert_city_coords_to_wgs84(points_city, city_name=CityName.ATX)

expected_wgs84_coords = np.array(
[
[30.261642967615092, -97.72246957081633],
[30.26170086362131, -97.72253982250783],
[30.261739638233472, -97.72261222631731],
]
)
assert np.allclose(wgs84_coords, expected_wgs84_coords, atol=1e-4)


def test_convert_city_coords_to_wgs84_wdc() -> None:
"""Convert city coordinates from Washington, DC to GPS coordinates."""

points_city = np.array(
[
[1716.85, 4470.38],
[2139.70, 4606.14],
]
)

wgs84_coords = geo_utils.convert_city_coords_to_wgs84(points_city, city_name=CityName.WDC)
expected_wgs84_coords = np.array(
[
[38.9299801515994, -77.0168603173312],
[38.931286945069985, -77.0120195048271],
]
)
assert np.allclose(wgs84_coords, expected_wgs84_coords, atol=1e-4)


def test_convert_gps_to_utm() -> None:
"""Convert Pittsburgh city origin (given in WGS84) to UTM coordinates."""
lat, long = CITY_ORIGIN_LATLONG_DICT[CityName.PIT]
utm_coords = geo_utils.convert_gps_to_utm(lat, long, city_name=CityName.PIT)

expected_utm_coords = 583710, 4477260
assert np.allclose(utm_coords, expected_utm_coords, atol=0.01)