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

FIX: Removed support to SMRT.py #5707

Merged
merged 9 commits into from
Jan 31, 2025
Prev Previous commit
Next Next commit
Remove utm module
Samuelopez-ansys committed Jan 31, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit d1166cafe4304882c70f5bc8b09ade51c142ee2f
134 changes: 123 additions & 11 deletions src/ansys/aedt/core/modeler/advanced_cad/oms.py
Original file line number Diff line number Diff line change
@@ -49,10 +49,8 @@
try:
import osmnx as ox

# import utm

except ImportError: # pragma: no cover
warnings.warn("OpenStreetMap Reader requires utm, osmnx extra packages.\n" "Install with \n\npip install utm osmnx")
warnings.warn("OpenStreetMap Reader requires osmnx extra packages.\n" "Install with \n\npip install osmnx")

ZONE_LETTERS = "CDEFGHJKLMNPQRSTUVWXX"

@@ -136,7 +134,7 @@
except AttributeError:
gdf = ox.features.features_from_point(center_lat_lon, tags={"building": True}, dist=max_radius)

utm_center = convert_latlon_to_utm(center_lat_lon[0], center_lat_lon[1])

Check warning on line 137 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L137

Added line #L137 was not covered by tests
center_offset_x = utm_center[0]
center_offset_y = utm_center[1]

@@ -300,7 +298,7 @@

g_projected = ox.project_graph(graph)

utm_center = convert_latlon_to_utm(center_lat_lon[0], center_lat_lon[1])

Check warning on line 301 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L301

Added line #L301 was not covered by tests
center_offset_x = utm_center[0]
center_offset_y = utm_center[1]

@@ -406,7 +404,7 @@
Info of generated stl file.
"""

utm_center = convert_latlon_to_utm(center_lat_lon[0], center_lat_lon[1])

Check warning on line 407 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L407

Added line #L407 was not covered by tests
logger.info("Generating Terrain")
max_radius = max_radius * (buffer_percent + 1)
all_data, _, all_utm = self.get_elevation(
@@ -423,14 +421,14 @@
latlat_utm_centered = all_utm[lat_idx][lon_idx][0] - utm_center[0]
lonlon_utm_centered = all_utm[lat_idx][lon_idx][1] - utm_center[1]

if all_data[lat_idx][lon_idx] != -32768: # this is missing data, don't add if it doesn't exist

Check warning on line 424 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L424

Added line #L424 was not covered by tests
xyz.append([latlat_utm_centered, lonlon_utm_centered, all_data[lat_idx][lon_idx]])
xyz = np.array(xyz)

file_out = self.cad_path + "/terrain.stl"
logger.info("saving STL as " + file_out)
terrain_mesh = pv.PolyData(xyz)
terrain_mesh = terrain_mesh.delaunay_2d(tol=10 / (2 * max_radius) / 2)

Check warning on line 431 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L431

Added line #L431 was not covered by tests
terrain_mesh = terrain_mesh.smooth(n_iter=100, relaxation_factor=0.04)

el = terrain_mesh.points[:, 2]
@@ -462,12 +460,10 @@
-------
tuple
"""
latitude = center_lat_lon[0]
longitude = center_lat_lon[1]

Check warning on line 464 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L463-L464

Added lines #L463 - L464 were not covered by tests

import utm

utm_center = convert_latlon_to_utm(center_lat_lon[0], center_lat_lon[1])

Check warning on line 466 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L466

Added line #L466 was not covered by tests

utm_x_min = utm_center[0] - max_radius
utm_x_max = utm_center[0] + max_radius
@@ -488,7 +484,7 @@
for n, x in enumerate(x_samples):
for m, y in enumerate(y_samples):

num_percent_bins = 40

Check warning on line 487 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L487

Added line #L487 was not covered by tests
percent_complete = int((n * num_samples + m) / (num_samples * num_samples) * 100)
if percent_complete % 10 == 0 and percent_complete != last_displayed:
last_displayed = percent_complete
@@ -499,30 +495,25 @@
i = percent_symbol2 + percent_symbol1 + " " + str(percent_complete) + "% "
logger.info(f"\rPercent Complete:{i}")

zone_letter1 = utm.latitude_to_zone_letter(center_lat_lon[0])
zone_number1 = utm.latlon_to_zone_number(center_lat_lon[0], center_lat_lon[1])
current_lat_lon1 = utm.to_latlon(x, y, zone_number, zone_letter)

if -80 <= latitude <= 84:
zone_letter = ZONE_LETTERS[int(latitude + 80) >> 3]

Check warning on line 499 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L498-L499

Added lines #L498 - L499 were not covered by tests
else:
zone_letter = None

Check warning on line 501 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L501

Added line #L501 was not covered by tests

zone_number = int((longitude + 180) / 6) + 1
if 56 <= latitude < 64 and 3 <= longitude < 12:
zone_number = 32

Check warning on line 505 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L503-L505

Added lines #L503 - L505 were not covered by tests

if 72 <= latitude <= 84 and longitude >= 0:
if longitude < 9:
zone_number = 31
elif longitude < 21:
zone_number = 33
elif longitude < 33:
zone_number = 35
elif longitude < 42:
zone_number = 37

current_lat_lon = convert_latlon_to_utm(float(x), float(y), zone_letter, zone_number)
current_lat_lon = convert_utm_to_latlon(int(x), int(y), zone_number, zone_letter)

Check warning on line 516 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L507-L516

Added lines #L507 - L516 were not covered by tests
all_lat_lon[n, m] = current_lat_lon
all_utm[n, m] = [x, y]
logger.info(str(100) + "% - Done")
@@ -600,17 +591,17 @@
if zone_number is None:
zone_number = int((longitude + 180) / 6) + 1
if 56 <= latitude < 64 and 3 <= longitude < 12:
zone_number = 32

Check warning on line 594 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L594

Added line #L594 was not covered by tests

if 72 <= latitude <= 84 and longitude >= 0:
if longitude < 9:
zone_number = 31
elif longitude < 21:
zone_number = 33
elif longitude < 33:
zone_number = 35
elif longitude < 42:
zone_number = 37

Check warning on line 604 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L597-L604

Added lines #L597 - L604 were not covered by tests

if zone_letter is None and -80 <= latitude <= 84:
zone_letter = ZONE_LETTERS[int(latitude + 80) >> 3]
@@ -645,6 +636,127 @@

if latitude < 0:
# Southern hemisphere adjustment
northing += 10000000

Check warning on line 639 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L639

Added line #L639 was not covered by tests

return easting, northing, zone_letter, zone_number


@pyaedt_function_handler()
def convert_utm_to_latlon(
east: int, north: float, zone_number: int, zone_letter: str = None, northern: bool = None
) -> tuple:
"""Convert UTM (Universal Transverse Mercator) coordinates to latitude and longitude.

Parameters
----------
east : int
East value of UTM coordinates.
north : int
North value of UTM coordinates.
zone_number: int, optional
Global map numbers of a UTM zone numbers map.
zone_letter: str, optional
UTM zone designators. The default is ``None``. The valid zone letters are ``"CDEFGHJKLMNPQRSTUVWXX"``
northern: bool, optional
Indicates whether the UTM coordinates are in the Northern Hemisphere. If ``True``, the coordinates
are treated as being north of the equator, if ``False``, they are treated as being south of the equator.
The default is ``None`` in which case the method determines the hemisphere using ``zone_letter``.

Notes
-----
.. [1] https://mapref.org/UTM-ProjectionSystem.html

Returns
-------
tuple
Tuple containing latitude and longitude.
"""
if not zone_letter and northern is None:
raise ValueError("Set either zone_letter or northern.")

Check warning on line 675 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L675

Added line #L675 was not covered by tests

if zone_letter and northern is not None:
raise ValueError("Set either zone_letter or north, but not both.")

Check warning on line 678 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L678

Added line #L678 was not covered by tests

if zone_letter:
zone_letter = zone_letter.upper()
northern = zone_letter >= "N"

Check warning on line 682 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L681-L682

Added lines #L681 - L682 were not covered by tests

x = east - 500000
y = north

if not northern:
y -= 10000000

Check warning on line 688 in src/ansys/aedt/core/modeler/advanced_cad/oms.py

Codecov / codecov/patch

src/ansys/aedt/core/modeler/advanced_cad/oms.py#L688

Added line #L688 was not covered by tests

# Constant values
# Scale factor for UTM (Universal Transverse Mercator) projection
K0 = 0.9996
# Radius of the Earth in meters
R = 6378137

# Eccentricity of the Earth's ellipsoid
E = 0.00669438
E2 = E * E
E3 = E2 * E
E_P2 = E / (1.0 - E)

SQRT_E = mathlib.sqrt(1 - E)
_E = (1 - SQRT_E) / (1 + SQRT_E)
_E2 = _E * _E
_E3 = _E2 * _E
_E4 = _E3 * _E
_E5 = _E4 * _E

# Meridional arc constants for UTM projection
M1 = 1 - E / 4 - 3 * E2 / 64 - 5 * E3 / 256

P2 = 3.0 / 2 * _E - 27.0 / 32 * _E3 + 269.0 / 512 * _E5
P3 = 21.0 / 16 * _E2 - 55.0 / 32 * _E4
P4 = 151.0 / 96 * _E3 - 417.0 / 128 * _E5
P5 = 1097.0 / 512 * _E4

m = y / K0
mu = m / (R * M1)

p_rad = (
mu + P2 * mathlib.sin(2 * mu) + P3 * mathlib.sin(4 * mu) + P4 * mathlib.sin(6 * mu) + P5 * mathlib.sin(8 * mu)
)

p_sin = mathlib.sin(p_rad)
p_sin2 = p_sin * p_sin

p_cos = mathlib.cos(p_rad)

p_tan = p_sin / p_cos
p_tan2 = p_tan * p_tan
p_tan4 = p_tan2 * p_tan2

ep_sin = 1 - E * p_sin2
ep_sin_sqrt = mathlib.sqrt(1 - E * p_sin2)

n = R / ep_sin_sqrt
r = (1 - E) / ep_sin

c = E_P2 * p_cos**2
c2 = c * c

d = x / (n * K0)
d2 = d * d
d3 = d2 * d
d4 = d3 * d
d5 = d4 * d
d6 = d5 * d

latitude = (
p_rad
- (p_tan / r) * (d2 / 2 - d4 / 24 * (5 + 3 * p_tan2 + 10 * c - 4 * c2 - 9 * E_P2))
+ d6 / 720 * (61 + 90 * p_tan2 + 298 * c + 45 * p_tan4 - 252 * E_P2 - 3 * c2)
)

longitude = (
d - d3 / 6 * (1 + 2 * p_tan2 + c) + d5 / 120 * (5 - 2 * c + 28 * p_tan2 - 3 * c2 + 8 * E_P2 + 24 * p_tan4)
) / p_cos

longitude_temp = longitude + mathlib.radians((zone_number - 1) * 6 - 180 + 3)
longitude = (longitude_temp + mathlib.pi) % (2 * mathlib.pi) - mathlib.pi

return mathlib.degrees(latitude), mathlib.degrees(longitude)
61 changes: 61 additions & 0 deletions tests/unit/test_utm_latitude_longitude.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Test UTM converter functions.
"""

from ansys.aedt.core.modeler.advanced_cad.oms import convert_latlon_to_utm
from ansys.aedt.core.modeler.advanced_cad.oms import convert_utm_to_latlon
import pytest


def test_convert_latlon_to_utm():
latitude, longitude = 40.7128, -74.0060
result = convert_latlon_to_utm(latitude, longitude)

assert isinstance(result, tuple)
assert len(result) == 4


def test_convert_utm_to_latlon():
east, north, zone_number = 500000, 4649776, 33
result = convert_utm_to_latlon(east, north, zone_number, northern=True)

assert isinstance(result, tuple)
assert len(result) == 2


def test_invalid_latitude():
with pytest.raises(ValueError, match="Latitude out of range"):
convert_latlon_to_utm(100.0, 50.0)


def test_invalid_longitude():
with pytest.raises(ValueError, match="Longitude out of range"):
convert_latlon_to_utm(40.0, 200.0)


def test_invalid_zone_letter():
with pytest.raises(ValueError, match="Zone letter out of range"):
convert_latlon_to_utm(40.0, -74.0, zone_letter="Z")