diff --git a/src/openutm_verification/auth/dev_auth.py b/src/openutm_verification/auth/dev_auth.py index fa89eae5..1fe7b0b8 100644 --- a/src/openutm_verification/auth/dev_auth.py +++ b/src/openutm_verification/auth/dev_auth.py @@ -1,14 +1,10 @@ import datetime -import urllib.parse import uuid -from typing import Dict, List, Optional +from abc import ABC, abstractmethod +from typing import List -import jwcrypto.common import jwcrypto.jwk -import jwcrypto.jws import jwcrypto.jwt -import jwt -import requests ALL_SCOPES = [ "dss.write.identification_service_areas", @@ -23,47 +19,13 @@ CLIENT_TIMEOUT = 60 # seconds -class AuthAdapter(object): - """Base class for an adapter that add JWTs to requests.""" - - def __init__(self): - self._tokens = {} +class AuthAdapter(ABC): + """Abstract base class for an adapter that adds JWTs to requests.""" + @abstractmethod def issue_token(self, intended_audience: str, scopes: List[str]) -> str: """Subclasses must return a bearer token for the given audience.""" - - raise NotImplementedError() - - def get_headers(self, url: str, scopes: List[str] = None) -> Dict[str, str]: - if scopes is None: - scopes = ALL_SCOPES - intended_audience = urllib.parse.urlparse(url).hostname - scope_string = " ".join(scopes) - if intended_audience not in self._tokens: - self._tokens[intended_audience] = {} - if scope_string not in self._tokens[intended_audience]: - token = self.issue_token(intended_audience, scopes) - else: - token = self._tokens[intended_audience][scope_string] - payload = jwt.decode(token, options={"verify_signature": False}) - expires = EPOCH + datetime.timedelta(seconds=payload["exp"]) - if datetime.datetime.utcnow() > expires - TOKEN_REFRESH_MARGIN: - token = self.issue_token(intended_audience, scopes) - self._tokens[intended_audience][scope_string] = token - return {"Authorization": "Bearer " + token} - - def add_headers(self, request: requests.PreparedRequest, scopes: List[str]): - for k, v in self.get_headers(request.url, scopes).items(): - request.headers[k] = v - - def get_sub(self) -> Optional[str]: - """Retrieve `sub` claim from one of the existing tokens""" - for _, tokens_by_scope in self._tokens.items(): - for token in tokens_by_scope.values(): - payload = jwt.decode(token, options={"verify_signature": False}) - if "sub" in payload: - return payload["sub"] - return None + pass class NoAuth(AuthAdapter): diff --git a/src/openutm_verification/auth/noauth.py b/src/openutm_verification/auth/noauth.py index e455e8bd..f46d4d66 100644 --- a/src/openutm_verification/auth/noauth.py +++ b/src/openutm_verification/auth/noauth.py @@ -10,9 +10,6 @@ class NoAuthCredentialsGetter: """Credentials getter that uses dummy authentication for development.""" - def __init__(self): - pass - def get_cached_credentials(self, audience: str, scopes: List[str]): """Get cached credentials using dummy authentication.""" if not audience: diff --git a/src/openutm_verification/auth/passport_client.py b/src/openutm_verification/auth/passport_client.py deleted file mode 100644 index e25f5d12..00000000 --- a/src/openutm_verification/auth/passport_client.py +++ /dev/null @@ -1,159 +0,0 @@ -import inspect -import os -import sys - -currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) -parentdir = os.path.dirname(currentdir) -sys.path.insert(0, parentdir) - -import json -from datetime import datetime, timedelta -from os import environ as env -from typing import List, Optional - -import requests -from dotenv import find_dotenv, load_dotenv - -from openutm_verification.auth.dev_auth import NoAuth -from openutm_verification.utils.redis_utils import get_redis - -ENV_FILE = find_dotenv() -if ENV_FILE: - load_dotenv(ENV_FILE) - - -class PassportSpotlightCredentialsGetter: - def __init__(self): - pass - - def get_cached_credentials(self, audience: Optional[str] = None, scopes: Optional[List[str]] = None): - r = get_redis() - - if not audience: - return {"error": "An audience parameter must be provided"} - try: - scopes_str = " ".join(scopes) - except Exception as e: - return {"error": "An list of scopes parameter must be provided"} - - now = datetime.now() - - token_details = r.get("spotlight_write_air_traffic_token") - if token_details: - token_details = json.loads(token_details) - created_at = token_details["created_at"] - set_date = datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%S.%f") - if now < (set_date - timedelta(minutes=58)): - credentials = self.get_write_credentials() - r.set( - "spotlight_write_air_traffic_token", - json.dumps({"credentials": credentials, "created_at": now.isoformat()}), - ) - else: - credentials = token_details["credentials"] - else: - credentials = self.get_write_credentials() - r.set( - "spotlight_write_air_traffic_token", - json.dumps({"credentials": credentials, "created_at": now.isoformat()}), - ) - r.expire("spotlight_write_air_traffic_token", timedelta(minutes=58)) - - return credentials - - def get_write_credentials(self, audience: str, scopes: str): - payload = { - "grant_type": "client_credentials", - "client_id": env.get("SPOTLIGHT_WRITE_CLIENT_ID"), - "client_secret": env.get("SPOTLIGHT_WRITE_CLIENT_SECRET"), - "audience": audience, - "scope": scopes, - } - url = env.get("PASSPORT_URL") + env.get("PASSPORT_TOKEN_URL") - - token_data = requests.post(url, data=payload) - t_data = token_data.json() - - return t_data - - -class PassportCredentialsGetter: - def __init__(self): - pass - - def get_cached_credentials(self, audience: Optional[str] = None, scopes: Optional[List[str]] = None): - r = get_redis() - - if not audience: - return {"error": "An audience parameter must be provided"} - try: - scopes_str = " ".join(scopes) - except Exception as e: - return {"error": "An list of scopes parameter must be provided"} - - scopes_str = " ".join(scopes) - - now = datetime.now() - - token_details = r.get("flight_blender_write_air_traffic_token") - if token_details: - token_details = json.loads(token_details) - created_at = token_details["created_at"] - set_date = datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%S.%f") - if now < (set_date - timedelta(minutes=58)): - credentials = self.get_write_credentials(audience=audience, scopes=scopes_str) - r.set( - "flight_blender_write_air_traffic_token", - json.dumps({"credentials": credentials, "created_at": now.isoformat()}), - ) - else: - credentials = token_details["credentials"] - else: - credentials = self.get_write_credentials(audience=audience, scopes=scopes_str) - if "error" in credentials.keys(): - pass - else: - r.set( - "flight_blender_write_air_traffic_token", - json.dumps({"credentials": credentials, "created_at": now.isoformat()}), - ) - r.expire("flight_blender_write_air_traffic_token", timedelta(minutes=58)) - - return credentials - - def get_write_credentials(self, audience: str, scopes: str): - payload = { - "grant_type": "client_credentials", - "client_id": env.get("BLENDER_WRITE_CLIENT_ID"), - "client_secret": env.get("BLENDER_WRITE_CLIENT_SECRET"), - "audience": audience, - "scope": scopes, - } - url = env.get("PASSPORT_URL") + env.get("PASSPORT_TOKEN_URL") - token_data = requests.post(url, data=payload, headers={"Content-Type": "application/x-www-form-urlencoded"}, timeout=10) - t_data = token_data.json() - return t_data - - -class NoAuthCredentialsGetter: - def __init__(self): - pass - - def get_cached_credentials(self, audience: Optional[str] = None, scopes: Optional[List[str]] = None): - if not audience: - return {"error": "An audience parameter must be provided"} - if not scopes: - return {"error": "A list of scopes parameter must be provided"} - try: - scopes_str = " ".join(scopes) - except Exception as e: - return {"error": "An list of scopes parameter must be provided"} - - audience = audience - scopes = scopes_str - adapter = NoAuth() - token = adapter.issue_token(audience, scopes.split(" ")) - - t_data = {"access_token": token} - - return t_data diff --git a/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py b/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py index 0154b5c8..5399607c 100644 --- a/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py +++ b/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py @@ -1,6 +1,6 @@ import json import uuid -from typing import Any, Dict, List, Optional +from typing import Optional from uuid import UUID from loguru import logger diff --git a/src/openutm_verification/core/clients/air_traffic/base_client.py b/src/openutm_verification/core/clients/air_traffic/base_client.py index 4332994b..2a2b543b 100644 --- a/src/openutm_verification/core/clients/air_traffic/base_client.py +++ b/src/openutm_verification/core/clients/air_traffic/base_client.py @@ -1,10 +1,5 @@ -from typing import Optional, Tuple - -import httpx -from loguru import logger from pydantic_settings import BaseSettings -from openutm_verification.auth.oauth2 import OAuth2Client from openutm_verification.core.execution.config_models import get_settings config = get_settings() diff --git a/src/openutm_verification/core/clients/opensky/opensky_client.py b/src/openutm_verification/core/clients/opensky/opensky_client.py index 40351572..7985e9a1 100644 --- a/src/openutm_verification/core/clients/opensky/opensky_client.py +++ b/src/openutm_verification/core/clients/opensky/opensky_client.py @@ -1,4 +1,3 @@ -import json from typing import Optional import pandas as pd @@ -9,11 +8,7 @@ OpenSkySettings, ) from openutm_verification.core.execution.scenario_runner import scenario_step -from openutm_verification.simulator.geo_json_telemetry import ( - GeoJSONAirtrafficSimulator, -) from openutm_verification.simulator.models.flight_data_types import ( - AirTrafficGeneratorConfiguration, FlightObservationSchema, ) diff --git a/src/openutm_verification/core/reporting/reporting_models.py b/src/openutm_verification/core/reporting/reporting_models.py index f5c6e995..d428108b 100644 --- a/src/openutm_verification/core/reporting/reporting_models.py +++ b/src/openutm_verification/core/reporting/reporting_models.py @@ -3,9 +3,9 @@ """ from enum import StrEnum -from typing import Any, Dict, List, Literal, Optional +from typing import Any, Dict, List, Optional -from pydantic import BaseModel, Field +from pydantic import BaseModel from openutm_verification.core.execution.config_models import DeploymentDetails diff --git a/src/openutm_verification/drip/auth_decoder.py b/src/openutm_verification/drip/auth_decoder.py index a5e543f5..fdbaf757 100644 --- a/src/openutm_verification/drip/auth_decoder.py +++ b/src/openutm_verification/drip/auth_decoder.py @@ -32,10 +32,7 @@ For more information about the DRIP protocol and the Authentication message format, refer to the ASTM F3411 specification. """ - import ctypes -import struct -from ctypes import POINTER, Structure, c_char, c_double, c_float, c_uint8, c_uint16, c_uint32, c_void_p, sizeof import openutm_verification.drip.drip_messages as common diff --git a/src/openutm_verification/drip/drip_messages.py b/src/openutm_verification/drip/drip_messages.py index 465266ca..686d188f 100644 --- a/src/openutm_verification/drip/drip_messages.py +++ b/src/openutm_verification/drip/drip_messages.py @@ -32,8 +32,7 @@ """ import ctypes -import struct -from ctypes import POINTER, Structure, c_char, c_double, c_float, c_uint8, c_uint16, c_uint32, c_void_p, sizeof +from ctypes import Structure, c_char, c_double, c_float, c_uint8, c_uint16, c_uint32, c_void_p # Size constants for DRIP messages DRIP_ID_SIZE = 20 diff --git a/src/openutm_verification/drip/import_drip_decoder.py b/src/openutm_verification/drip/import_drip_decoder.py index 4ce82834..395aee0d 100644 --- a/src/openutm_verification/drip/import_drip_decoder.py +++ b/src/openutm_verification/drip/import_drip_decoder.py @@ -29,7 +29,6 @@ import argparse import ctypes import os -import struct import openutm_verification.drip.drip_messages as common from openutm_verification.drip.auth_decoder import AuthDecoder @@ -48,7 +47,6 @@ def decode_drone_id(uas_data, raw_data): if common.DRIP_MESSAGE_SIZE <= message_size: msg_type = raw_data[0] >> 4 - message_type_bytes = struct.pack("B", raw_data[0]) if msg_type == common.DRIP_MESSAGE_BASIC_ID: print("DRIP_MESSAGE_BASIC_ID") @@ -112,7 +110,7 @@ def decodeMessagePack(data): uasData = common.DRIP_UAS_Data() pack = common.DRIP_MessagePack_encoded() ctypes.memmove(ctypes.addressof(pack), data, ctypes.sizeof(pack)) - ret = decode_message_pack(uasData, pack, data) + decode_message_pack(uasData, pack, data) return uasData diff --git a/src/openutm_verification/drip/location_decoder.py b/src/openutm_verification/drip/location_decoder.py index 4983d6a8..5cc40430 100644 --- a/src/openutm_verification/drip/location_decoder.py +++ b/src/openutm_verification/drip/location_decoder.py @@ -32,7 +32,6 @@ For more information about the DRIP protocol and the Location message format, refer to the ASTM F3411 specification. """ -import ctypes import struct import openutm_verification.drip.drip_messages as common diff --git a/src/openutm_verification/drip/operator_id_decoder.py b/src/openutm_verification/drip/operator_id_decoder.py index 23e3d8ca..7ca7aa53 100644 --- a/src/openutm_verification/drip/operator_id_decoder.py +++ b/src/openutm_verification/drip/operator_id_decoder.py @@ -30,8 +30,6 @@ For more information about the DRIP protocol and the Operator ID message format, refer to the ASTM F3411 specification. """ -import ctypes - from openutm_verification.drip import drip_messages as common diff --git a/src/openutm_verification/drip/self_id_decoder.py b/src/openutm_verification/drip/self_id_decoder.py index 367ff7f6..7cdbd861 100644 --- a/src/openutm_verification/drip/self_id_decoder.py +++ b/src/openutm_verification/drip/self_id_decoder.py @@ -30,8 +30,6 @@ For more information about the DRIP protocol and the Self ID message format, refer to the ASTM F3411 specification. """ -import ctypes - from openutm_verification.drip import drip_messages as common diff --git a/src/openutm_verification/drip/system_decoder.py b/src/openutm_verification/drip/system_decoder.py index 590ba860..d5aec169 100644 --- a/src/openutm_verification/drip/system_decoder.py +++ b/src/openutm_verification/drip/system_decoder.py @@ -28,8 +28,6 @@ For more information about the DRIP protocol and the System message format, refer to the ASTM F3411 specification. """ -import ctypes - from openutm_verification.drip import drip_messages as common diff --git a/src/openutm_verification/importers/amqp_consumer.py b/src/openutm_verification/importers/amqp_consumer.py index aad04d56..72dbabc0 100644 --- a/src/openutm_verification/importers/amqp_consumer.py +++ b/src/openutm_verification/importers/amqp_consumer.py @@ -7,8 +7,6 @@ import pika from dotenv import find_dotenv, load_dotenv -load_dotenv(find_dotenv()) - def parse_args(argv: List[str]): parser = argparse.ArgumentParser(description="Retrieve an access token") @@ -24,6 +22,7 @@ def parse_args(argv: List[str]): def main(queue_id): + load_dotenv(find_dotenv()) amqp_connection_url = env.get("AMQP_URL", "localhost") params = pika.URLParameters(amqp_connection_url) connection = pika.BlockingConnection(params) diff --git a/src/openutm_verification/importers/dss_rid_uploader.py b/src/openutm_verification/importers/dss_rid_uploader.py index 3a60bc13..6448d041 100644 --- a/src/openutm_verification/importers/dss_rid_uploader.py +++ b/src/openutm_verification/importers/dss_rid_uploader.py @@ -8,14 +8,12 @@ import requests from dotenv import find_dotenv, load_dotenv -from openutm_verification.auth.passport_client import NoAuthCredentialsGetter, PassportCredentialsGetter - -ENV_FILE = find_dotenv() -if ENV_FILE: - load_dotenv(ENV_FILE) +from openutm_verification.auth.noauth import NoAuthCredentialsGetter if __name__ == "__main__": - # my_credentials = PassportCredentialsGetter() + ENV_FILE = find_dotenv() + if ENV_FILE: + load_dotenv(ENV_FILE) my_credentials = NoAuthCredentialsGetter() credentials = my_credentials.get_cached_credentials(audience="testflight.flightblender.com", scopes=["flight_blender.write"]) diff --git a/src/openutm_verification/importers/get_access_token.py b/src/openutm_verification/importers/get_access_token.py index 3c8ae1ad..1ea7ae18 100644 --- a/src/openutm_verification/importers/get_access_token.py +++ b/src/openutm_verification/importers/get_access_token.py @@ -1,15 +1,9 @@ import argparse -import inspect -import os import sys from typing import List from openutm_verification.auth.dev_auth import NoAuth -currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) -parentdir = os.path.dirname(currentdir) -sys.path.insert(0, parentdir) - def parse_args(argv: List[str]): parser = argparse.ArgumentParser(description="Retrieve an access token") diff --git a/src/openutm_verification/importers/import_flight_json.py b/src/openutm_verification/importers/import_flight_json.py index a811ac31..0fa3f2e2 100644 --- a/src/openutm_verification/importers/import_flight_json.py +++ b/src/openutm_verification/importers/import_flight_json.py @@ -8,7 +8,7 @@ import requests -from openutm_verification.auth.passport_client import NoAuthCredentialsGetter, PassportCredentialsGetter +from openutm_verification.auth.noauth import NoAuthCredentialsGetter class FlightBlenderUploader: @@ -86,7 +86,6 @@ def upload_to_server(self, filename): if __name__ == "__main__": - # my_credentials = PassportCredentialsGetter() my_credentials = NoAuthCredentialsGetter() credentials = my_credentials.get_cached_credentials(audience="testflight.flightblender.com", scopes=["flight_blender.write"]) diff --git a/src/openutm_verification/importers/import_rid_data.py b/src/openutm_verification/importers/import_rid_data.py index fa77a835..9ab946fc 100644 --- a/src/openutm_verification/importers/import_rid_data.py +++ b/src/openutm_verification/importers/import_rid_data.py @@ -1,14 +1,12 @@ import json import os import time -from dataclasses import asdict, dataclass +from dataclasses import asdict from os.path import abspath, dirname -from typing import Optional import requests -from dacite import from_dict -from openutm_verification.auth.passport_client import NoAuthCredentialsGetter, PassportCredentialsGetter +from openutm_verification.auth.noauth import NoAuthCredentialsGetter from openutm_verification.rid import ( UASID, LatLngPoint, @@ -77,7 +75,6 @@ def upload_to_server(self, filename): if __name__ == "__main__": - # my_credentials = PassportCredentialsGetter() my_credentials = NoAuthCredentialsGetter() credentials = my_credentials.get_cached_credentials(audience="testflight.flightblender.com", scopes=["flight_blender.write"]) parent_dir = dirname(abspath(__file__)) # <-- absolute dir the raw input file is in diff --git a/src/openutm_verification/importers/import_rid_data_utm_adapter.py b/src/openutm_verification/importers/import_rid_data_utm_adapter.py index 96eb8246..9f0ef1ff 100644 --- a/src/openutm_verification/importers/import_rid_data_utm_adapter.py +++ b/src/openutm_verification/importers/import_rid_data_utm_adapter.py @@ -1,14 +1,12 @@ import json import os import time -from dataclasses import asdict, dataclass +from dataclasses import asdict from os.path import abspath, dirname -from typing import Optional import requests -from dacite import from_dict -from openutm_verification.auth.passport_client import NoAuthCredentialsGetter, PassportCredentialsGetter +from openutm_verification.auth.noauth import NoAuthCredentialsGetter from openutm_verification.rid import UASID, LatLngPoint, OperatorLocation, RIDOperatorDetails, UAClassificationEU @@ -70,7 +68,6 @@ def upload_to_server(self, filename): if __name__ == "__main__": - # my_credentials = PassportCredentialsGetter() my_credentials = NoAuthCredentialsGetter() credentials = my_credentials.get_cached_credentials(audience="testflight.flightblender.com", scopes=["flight_blender.write"]) parent_dir = dirname(abspath(__file__)) # <-- absolute dir the raw input file is in diff --git a/src/openutm_verification/importers/submit_signed_telemetry.py b/src/openutm_verification/importers/submit_signed_telemetry.py index 003cc169..ee617941 100644 --- a/src/openutm_verification/importers/submit_signed_telemetry.py +++ b/src/openutm_verification/importers/submit_signed_telemetry.py @@ -1,4 +1,3 @@ -import base64 import hashlib import json import os @@ -10,16 +9,13 @@ import http_sfv import jwt import requests -from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.serialization import ( load_pem_private_key, - load_pem_public_key, ) -from http_message_signatures import HTTPMessageSigner, HTTPMessageVerifier, HTTPSignatureKeyResolver, algorithms +from http_message_signatures import HTTPMessageSigner, HTTPSignatureKeyResolver, algorithms from jwt.exceptions import DecodeError, ExpiredSignatureError, InvalidKeyError, InvalidSignatureError, InvalidTokenError import openutm_verification.rid -from openutm_verification.auth.passport_client import NoAuthCredentialsGetter # This file send signed requests to ArgonServer and verifies responses from Blendre # Source: https://github.com/pyauth/http-message-signatures/blob/main/test/test.py diff --git a/src/openutm_verification/scenarios/common.py b/src/openutm_verification/scenarios/common.py index 34d403ac..49e36171 100644 --- a/src/openutm_verification/scenarios/common.py +++ b/src/openutm_verification/scenarios/common.py @@ -1,9 +1,7 @@ import json -from dataclasses import asdict from functools import partial from pathlib import Path from typing import Any, List, cast -from unittest.mock import DEFAULT from loguru import logger @@ -23,8 +21,6 @@ from openutm_verification.simulator.flight_declaration import FlightDeclarationGenerator from openutm_verification.simulator.geo_json_telemetry import GeoJSONFlightsSimulator from openutm_verification.simulator.models.flight_data_types import ( - AirTrafficGeneratorConfiguration, - FlightObservationSchema, GeoJSONFlightsSimulatorConfiguration, ) diff --git a/src/openutm_verification/scenarios/test_sdsp_track.py b/src/openutm_verification/scenarios/test_sdsp_track.py index 31ed224d..058ca0b0 100644 --- a/src/openutm_verification/scenarios/test_sdsp_track.py +++ b/src/openutm_verification/scenarios/test_sdsp_track.py @@ -1,4 +1,3 @@ -import time import uuid from functools import partial @@ -7,7 +6,6 @@ from openutm_verification.core.clients.flight_blender.flight_blender_client import ( FlightBlenderClient, ) -from openutm_verification.core.execution.scenario_runner import scenario_step from openutm_verification.core.reporting.reporting_models import ScenarioResult from openutm_verification.models import SDSPSessionAction from openutm_verification.scenarios.common import run_sdsp_scenario_template diff --git a/src/openutm_verification/simulator/geo_json_telemetry.py b/src/openutm_verification/simulator/geo_json_telemetry.py index b5c2e102..d58c21e2 100644 --- a/src/openutm_verification/simulator/geo_json_telemetry.py +++ b/src/openutm_verification/simulator/geo_json_telemetry.py @@ -1,7 +1,6 @@ import json import random import sys -import uuid from pathlib import Path from typing import List, Optional from uuid import UUID diff --git a/src/openutm_verification/simulator/models/geo_json_models.py b/src/openutm_verification/simulator/models/geo_json_models.py index d67f7c02..4e5c3af3 100644 --- a/src/openutm_verification/simulator/models/geo_json_models.py +++ b/src/openutm_verification/simulator/models/geo_json_models.py @@ -12,7 +12,7 @@ from typing import Any, Dict, List, Literal, Tuple import shapely.geometry -from pydantic import BaseModel, Field, ValidationError, field_validator, model_validator +from pydantic import BaseModel, Field, ValidationError, field_validator from pyproj import Geod LngLat = Tuple[float, float] diff --git a/src/openutm_verification/simulator/operator_flight_data.py b/src/openutm_verification/simulator/operator_flight_data.py index 359cb08c..ffa5ff3b 100644 --- a/src/openutm_verification/simulator/operator_flight_data.py +++ b/src/openutm_verification/simulator/operator_flight_data.py @@ -1,6 +1,5 @@ import random import string -import uuid from faker import Faker from uas_standards.ansi_cta_2063_a import SerialNumber diff --git a/src/openutm_verification/simulator/test.py b/src/openutm_verification/simulator/test.py index 5ed69513..a9a36094 100644 --- a/src/openutm_verification/simulator/test.py +++ b/src/openutm_verification/simulator/test.py @@ -1,5 +1,4 @@ import shapely -from pyproj import Geod, Proj, Transformer if __name__ == "__main__": geojson = {