lib/aixm/component/layer.rb
using AIXM::Refinements
module AIXM
class Component
# Each airspace has one or more layers with optional airspace class and
# mandatory vertical limit.
#
# ===Cheat Sheet in Pseudo Code:
# layer = AIXM.layer(
# class: String or nil
# location_indicator: String or nil
# vertical_limit: AIXM.vertical_limit
# )
# layer.activity = String or nil
# layer.timetable = AIXM.timetable or nil
# layer.selective = true or false (default)
# layer.remarks = String or nil
# layer.add_service(AIXM.service)
#
# @see https://gitlab.com/openflightmaps/ofmx/wikis/Airspace
class Layer < Component
include AIXM::Concerns::Association
include AIXM::Concerns::Timetable
include AIXM::Concerns::Remarks
CLASSES = (:A..:G).freeze
ACTIVITIES = {
ACCIDENT: :accident_investigation,
ACROBAT: :acrobatics,
AIRGUN: :aerial_gunnery,
AIRMODEL: :aeromodelling,
AIRSHOW: :air_show,
ANTIHAIL: :anti_hail_rocket,
ARTILERY: :artillary_firing,
ASCENT: :probe,
ATS: :air_traffic_services,
BALLOON: :balloon,
BIRD: :bird_hazard,
'BIRD-MGR': :bird_migration,
BLAST: :blasting_operation,
DROP: :dropping,
DUSTING: :crop_dusting,
EQUIPMENT: :special_equipment_required,
'EQUIPMENT-833': :radio_8_33_required,
'EQUIPMENT-RNAV': :rnav_equipment_required,
'EQUIPMENT-RSVM': :rsvm_equipment_required,
EXERCISE: :combat_exercise,
FAUNA: :sensitive_fauna,
FIRE: :fire_suppression,
FIREWORK: :fireworks,
GAZ: :gaz_field,
GLIDER: :gliding,
HANGGLIDER: :hanggliding,
'HI-LIGHT': :high_intensity_light,
'HI-RADIO': :high_intensity_radio,
'IND-CHEM': :chemical_plant,
'IND-NUCLEAR': :nuclear_activity,
'IND-OIL': :oil_refinery,
JETCLIMB: :jet_climb,
LASER: :laser_light,
MILOPS: :military_operation,
MISSILES: :guided_missiles,
NATURE: :natural_reserve,
NAVAL: :ship_exercise,
'NO-NOISE': :noise_abatement,
OIL: :oil_field,
PARACHUTE: :parachuting,
PARAGLIDER: :paragliding,
POPULATION: :highly_populated,
PROCEDURE: :special_procedure,
REFUEL: :refuelling,
SHOOT: :shooting_from_ground,
SPACEFLT: :space_flight,
SPORT: :sport,
TECHNICAL: :technical_activity,
'TFC-AD': :aerodrome_traffic,
'TFC-HELI': :helicopter_traffic,
TOWING: :towing_traffic,
TRG: :training,
UAV: :drone,
ULM: :ultra_light_flight,
VIP: :vip,
'VIP-PRES': :president,
'VIP-VICE': :vice_president,
WATERBLAST: :underwater_explosion,
WINCH: :glider_winch,
WORK: :aerial_work,
OTHER: :other
}.freeze
# @!method vertical_limit
# @return [AIXM::Component::VerticalLimit] vertical limit of this layer
#
# @!method vertical_limit=(vertical_limit)
# @param vertical_limit [AIXM::Component::VerticalLimit]
has_one :vertical_limit
# @!method services
# @return [Array<AIXM::Component::Service>] services
#
# @!method add_service(service)
# @param service [AIXM::Component::Service]
has_many :services
# @!method airspace
# @return [AIXM::Feature::Airspace] airspace the layer defines
belongs_to :airspace
# Four letter location identifier as published in the ICAO DOC 7910
#
# @overload location_indicator
# @return [String, nil]
# @overload location_indicator=(value)
# @param value [String, nil]
attr_reader :location_indicator
# Primary activity
#
# @overload activity
# @return [Symbol, nil] any of {ACTIVITIES}
# @overload activity=(value)
# @param value [Symbol, nil] any of {ACTIVITIES}
attr_reader :activity
# See the {cheat sheet}[AIXM::Component::Layer] for examples on how to
# create instances of this class.
def initialize(class: nil, location_indicator: nil, vertical_limit:)
self.class = binding.local_variable_get(:class)
self.location_indicator, self.vertical_limit = location_indicator, vertical_limit
self.selective = false
end
# @return [String]
def inspect
%Q(#<#{__class__} class=#{@klass.inspect}>)
end
# Class of layer.
#
# @note Use +Object#__class__+ alias to query the Ruby object class.
#
# @!attribute class
# @overload class
# @return [Symbol] any of {CLASSES}
# @overload class=(value)
# @param value [Symbol] any of {CLASSES}
def class
@klass
end
def class=(value)
@klass = value&.to_sym&.upcase
fail(ArgumentError, "invalid class") unless @klass.nil? || CLASSES.include?(@klass)
end
def location_indicator=(value)
fail(ArgumentError, "invalid location indicator") unless value.nil? || (value.is_a?(String) && value.length == 4)
@location_indicator = value&.uptrans
end
def activity=(value)
@activity = value.nil? ? nil : ACTIVITIES.lookup(value.to_s.to_sym, nil) || fail(ArgumentError, "invalid activity")
end
# Whether the layer may be activated selectively.
#
# @!attribute selective
# @overload selective?
# @return [Boolean]
# @overload selective=(value)
# @param value [Boolean]
def selective?
@selective
end
def selective=(value)
fail(ArgumentError, "invalid selective") unless [true, false].include? value
@selective = value
end
# @!visibility private
def add_to(builder)
builder.codeClass(self.class) if self.class
builder.codeLocInd(location_indicator) if location_indicator
if activity
builder.codeActivity(ACTIVITIES.key(activity).to_s.then_if(AIXM.aixm?) { { 'AIRMODEL' => 'UAV', 'WINCH' => 'GLIDER' }[_1] || _1 })
end
vertical_limit.add_to(builder)
timetable.add_to(builder, as: :Att) if timetable
builder.codeSelAvbl(selective? ? 'Y' : 'N') if AIXM.ofmx?
builder.txtRmk(remarks) if remarks
end
end
end
end