Skip to content

Commit

Permalink
Merge pull request #4 from MHenderson1988/update
Browse files Browse the repository at this point in the history
Update
  • Loading branch information
MHenderson1988 authored Jun 23, 2023
2 parents 4e7151e + 879ee26 commit 9877171
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 35 deletions.
51 changes: 49 additions & 2 deletions aixm_geo/aixm_features.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import util
from base import SinglePointAixm, MultiPointAixm
from interfaces import IAixmFeature
from settings import NAMESPACES
Expand All @@ -15,7 +16,12 @@ def get_geographic_information(self):
geo_dict(dict): A dictionary containing relevant information regarding the feature.
"""

elevation, elevation_uom = self.get_elevation()
elevation, elevation_uom = self.get_field_elevation()

if elevation_uom != 'M':
elevation = util.convert_elevation(elevation, elevation_uom)
elevation_uom = 'M'

geo_dict = {
'type': 'AirportHeliport',
'coordinates': [f"{self.get_first_value('.//aixm:ARP//gml:pos')} "
Expand All @@ -41,6 +47,10 @@ def get_geographic_information(self):
"""
elevation, elevation_uom = self.get_elevation()

if elevation_uom != 'M':
elevation = util.convert_elevation(elevation, elevation_uom)
elevation_uom = 'M'

geo_dict = {
'type': 'NavaidComponent',
'coordinates': [f"{self.get_first_value('.//aixm:location//gml:pos')}"
Expand Down Expand Up @@ -116,7 +126,9 @@ def get_geographic_information(self):

coordinate_list = self.get_coordinate_list(subroot)

lower_layer, lower_layer_uom, upper_layer, upper_layer_uom = self.get_elevation()
lower_layer, lower_layer_uom, upper_layer, upper_layer_uom = self.get_airspace_elevation()
lower_layer, lower_layer_uom = util.convert_elevation(lower_layer, lower_layer_uom)
upper_layer, upper_layer_uom = util.convert_elevation(upper_layer, upper_layer_uom)

geo_dict = {
'type': 'Airspace',
Expand All @@ -130,3 +142,38 @@ def get_geographic_information(self):
}

return geo_dict


class VerticalStructure(MultiPointAixm, IAixmFeature):
def __init__(self, root):
super().__init__(root)

def get_geographic_information(self):
"""
Args:
self
Returns:
geo_dict(dict): A dictionary containing relevant information regarding the feature.
"""

subroot = self._root.findall('.//aixm:part',
namespaces=NAMESPACES)[0]

elevation, elevation_uom = self.get_vertical_extent()
elevation, elevation_uom = util.convert_elevation(elevation, elevation_uom)

coordinate_list = self.get_coordinate_list(subroot)

if len(coordinate_list) == 1:
coordinate_list[0] = f'{coordinate_list[0]} {elevation}'

geo_dict = {
'type': 'VerticalStructure',
'obstacle_type': self.get_first_value('.//aixm:type'),
'coordinates': coordinate_list,
'elevation': elevation,
'elevation_uom': elevation_uom,
'name': f'{self.get_first_value(".//aixm:name")} ({self.get_first_value(".//aixm:type")})',
}

return geo_dict
22 changes: 12 additions & 10 deletions aixm_geo/aixm_geo.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,42 @@ def draw_features(self, kml_obj):
if geometry_type == 'cylinder':
self.draw_cylinder(aixm_feature_dict, kml_obj)
elif geometry_type == 'point':
kml_obj.point(aixm_feature_dict["coordinates"], fol=aixm_feature_dict['name'],
point_name=aixm_feature_dict['name'])
if aixm_feature_dict['type'] == 'VerticalStructure':
self.draw_vertical_structure_point(aixm_feature_dict, kml_obj)
else:
kml_obj.point(aixm_feature_dict["coordinates"], fol=aixm_feature_dict['name'],
point_name=aixm_feature_dict['name'])
elif geometry_type == 'polyhedron':
self.draw_airspace(aixm_feature_dict, kml_obj)
print(aixm_feature_dict)

else:
pass

def draw_airspace(self, aixm_feature_dict, kml_obj):
aixm_feature_dict = util.convert_elevation(aixm_feature_dict)
def draw_vertical_structure_point(self, aixm_feature_dict, kml_obj):
kml_obj.point(aixm_feature_dict["coordinates"], uom=aixm_feature_dict['elevation_uom'],
fol=aixm_feature_dict['name'], point_name=aixm_feature_dict['name'],
altitude_mode='relativeToGround', extrude=1)

def draw_airspace(self, aixm_feature_dict, kml_obj):
kml_obj.polyhedron(aixm_feature_dict["coordinates"],
# Convert to FL ie FL 95 x by 100 to get 9500 for correct conversion
aixm_feature_dict["coordinates"],
upper_layer=float(aixm_feature_dict['upper_layer']),
lower_layer=float(aixm_feature_dict['lower_layer']),
lower_layer_uom=aixm_feature_dict['lower_layer_uom'],
upper_layer_uom=aixm_feature_dict['upper_layer_uom'],
uom=aixm_feature_dict['lower_layer_uom'], fol=aixm_feature_dict['name'],
altitude_mode=util.altitude_mode(aixm_feature_dict))

def draw_cylinder(self, aixm_feature_dict, kml_obj):
aixm_feature_dict = util.convert_elevation(aixm_feature_dict)

coordinates = aixm_feature_dict['coordinates'][0].split(',')[0].strip()
radius = aixm_feature_dict['coordinates'][0].split(',')[1].split('=')[-1]
radius_uom = util.switch_radius_uom(aixm_feature_dict['coordinates'][0].split(',')[2].split('=')[-1])
lower_layer = aixm_feature_dict['lower_layer']
upper_layer = aixm_feature_dict['upper_layer']
uom = aixm_feature_dict['lower_layer_uom']

kml_obj.cylinder(coordinates, float(radius),
radius_uom=radius_uom, lower_layer=float(lower_layer),
upper_layer=float(upper_layer), uom=uom,
upper_layer=float(upper_layer),
fol=aixm_feature_dict['name'], lower_layer_uom=aixm_feature_dict['lower_layer_uom'],
upper_layer_uom=aixm_feature_dict['upper_layer_uom'],
altitude_mode=util.altitude_mode(aixm_feature_dict))
Expand Down
37 changes: 34 additions & 3 deletions aixm_geo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,23 @@ def get_first_value_attribute(self, xpath: str, **kwargs: Union[etree.Element, s

return attribute

def get_elevation(self):
def get_field_elevation(self):
elevation = self.get_first_value('.//aixm:fieldElevation')
elevation_uom = self.get_first_value_attribute('.//aixm:fieldElevation', attribute_string='uom')

if elevation == 'Unknown':
elevation = 0
elevation_uom = 'FT'
elevation_uom = 'M'

return elevation, elevation_uom

def get_vertical_extent(self):
elevation = self.get_first_value('.//aixm:elevation')
elevation_uom = self.get_first_value_attribute('.//aixm:elevation', attribute_string='uom')

if elevation == 'Unknown':
elevation = 0
elevation_uom = 'M'

return elevation, elevation_uom

Expand Down Expand Up @@ -116,6 +126,23 @@ class MultiPointAixm(SinglePointAixm):
def __init__(self, root):
super().__init__(root)

def get_airspace_elevation(self):
lower_layer = self.get_first_value('.//aixm:theAirspaceVolume//aixm:lowerLimit')
lower_layer_uom = self.get_first_value_attribute('.//aixm:theAirspaceVolume//aixm:lowerLimit',
attribute_string='uom')
upper_layer = self.get_first_value('.//aixm:theAirspaceVolume//aixm:upperLimit')
upper_layer_uom = self.get_first_value_attribute('.//aixm:theAirspaceVolume//aixm:upperLimit',
attribute_string='uom')

if lower_layer == 'Unknown':
lower_layer = 0.0
lower_layer_uom = 'M'
if upper_layer == 'Unknown':
upper_layer = 0.0
upper_layer_uom = 'M'

return lower_layer, lower_layer_uom, upper_layer, upper_layer_uom

def get_coordinate_list(self, subroot):
"""
Parses the LXML etree._Element object and returns a list of coordinate strings.
Expand All @@ -132,6 +159,10 @@ def get_coordinate_list(self, subroot):
unpacked_gml = self.unpack_gml(location)
except TypeError:
print('Coordinates can only be extracted from an LXML etree._Element object.')

for x in unpacked_gml:
x.strip("r'\'")

return unpacked_gml

def unpack_gml(self, location: etree.Element) -> list[str]:
Expand All @@ -156,7 +187,7 @@ def extract_pos_and_poslist(self, location):
"""
for child in location.iterdescendants():
tag = child.tag.split('}')[-1]
if tag == 'GeodesicString':
if tag == 'GeodesicString' or tag == 'ElevatedPoint':
for x in self.unpack_geodesic_string(child):
yield x
elif tag == 'CircleByCenterPoint':
Expand Down
3 changes: 2 additions & 1 deletion aixm_geo/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def __init__(self, root):
'DesignatedPoint': af.DesignatedPoint,
'NavaidComponent': af.NavaidComponent,
'RouteSegment': af.RouteSegment,
'Airspace': af.Airspace
'Airspace': af.Airspace,
'VerticalStructure': af.VerticalStructure,
}
self._errors = []

Expand Down
33 changes: 18 additions & 15 deletions aixm_geo/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,20 @@ def parse_timeslice(subroot: _Element) -> list:
return timeslices


def convert_elevation(aixm_feature_dict):
if aixm_feature_dict['lower_layer_uom'] == 'FL':
aixm_feature_dict['lower_layer_uom'] = 'FT'
aixm_feature_dict['lower_layer'] = float(aixm_feature_dict['lower_layer']) * 100
def convert_elevation(z_value: str, current_uom: str) -> float:
if z_value == 'GND':
current_uom = 'M'
z_value = 0.0

if aixm_feature_dict['lower_layer'] == 'GND':
aixm_feature_dict['lower_layer_uom'] = 'M'
aixm_feature_dict['lower_layer'] = 0.0
if z_value == 'UNL':
current_uom = 'M'
z_value = 18288.00 # 60,000 ft in metres

if aixm_feature_dict['upper_layer_uom'] == 'FL':
aixm_feature_dict['upper_layer_uom'] = 'FT'
aixm_feature_dict['upper_layer'] = float(aixm_feature_dict['upper_layer']) * 100
if current_uom == 'FL':
current_uom = 'M'
z_value = (float(z_value) * 100) * .3048 # 1 ft in metres

if aixm_feature_dict['upper_layer'] == 'UNL':
aixm_feature_dict['upper_layer_uom'] = 'M'
aixm_feature_dict['upper_layer'] = 18288.00 # 60,000 ft in metres

return aixm_feature_dict
return z_value, current_uom


def altitude_mode(aixm_dict):
Expand All @@ -85,6 +81,13 @@ def determine_geometry_type(aixm_feature_dict):
if aixm_feature_dict['type'] == 'RouteSegment':
geometry_type = 'LineString'

elif aixm_feature_dict['type'] == 'VerticalStructure':
if len(aixm_feature_dict['coordinates']) > 1:
aixm_feature_dict['lower_layer'] = 0.0
aixm_feature_dict['upper_layer'] = aixm_feature_dict['elevation']
geometry_type = 'Polygon'
else:
geometry_type = 'point'
elif len(aixm_feature_dict["coordinates"]) == 1:
if 'radius=' in aixm_feature_dict['coordinates'][0]:
if aixm_feature_dict["upper_layer"]:
Expand Down
1 change: 0 additions & 1 deletion test_data/donlon.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31742,7 +31742,6 @@ LIGHTED.
<aixm:VerticalStructurePart gml:id="ID_375">
<aixm:annotation>
<aixm:Note gml:id="ID_379">
<!-- <aixm:propertyName>horizontalProjection_location</aixm:propertyName> (This is invalid in AIXM 5.1, because of an error in the pattern of the propertyName attribute. It will be corrected in AIXM 5.1.1 and later)-->
<aixm:purpose>REMARK</aixm:purpose>
<aixm:translatedNote>
<aixm:LinguisticNote gml:id="ID_380">
Expand Down
6 changes: 3 additions & 3 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ def test_get_first_value_attribute(self):
self.assertEqual('unknown', attribute['indeterminatePosition'])

def test_get_elevation(self):
elevation = self.ah.get_elevation()
self.assertEqual(elevation, (0.0, 'FT'))
self.assertNotEqual(elevation, (0.0, 'M'))
elevation = self.ah.get_field_elevation()
self.assertEqual(elevation, (0.0, 'M'))
self.assertNotEqual(elevation, (0.0, 'FT'))
self.assertTrue(isinstance(elevation, tuple))


Expand Down

0 comments on commit 9877171

Please sign in to comment.