diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index d33eef15a..4a1bf82be 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -191,5 +191,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Procedure.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/FlightPlan.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Radios.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Towns.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' ) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index c856158e9..356464f50 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -180,5 +180,7 @@ __Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' ) __Moose.Include( 'Navigation\\Point.lua' ) __Moose.Include( 'Navigation\\Beacons.lua' ) +__Moose.Include( 'Navigation\\Radios.lua' ) +__Moose.Include( 'Navigation\\Towns.lua' ) __Moose.Include( 'Globals.lua' ) diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua index ec7b4d060..f22fecaf5 100644 --- a/Moose Development/Moose/Navigation/Beacons.lua +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -2,7 +2,9 @@ -- -- **Main Features:** -- --- * Beacons of the map +-- * Access beacons of the map +-- * Find closest beacon +-- * Get frequencies and channels -- -- === -- @@ -18,7 +20,6 @@ -- @module Navigation.Beacons -- @image NAVIGATION_Beacons.png - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -32,45 +33,132 @@ -- -- @extends Core.Base#BASE ---- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +--- *Hope is the beacon that guides lost ships back to the shore.* -- -- === -- -- # The BEACONS Concept -- --- The NAVFIX class has a great concept! +-- This class is desinged to make information about beacons of a map/theatre easier accessible. The information contains location, type and frequencies of all or specific beacons of the map. -- --- Bla, bla... +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `beacons.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. -- -- # Basic Setup -- --- A new `BEACONS` object can be created with the @{#BEACONS.New}() function. +-- A new `BEACONS` object can be created with the @{#BEACONS.NewFromFile}(*beacons_lua_file*) function. +-- +-- local beacons=BEACONS:NewFromFile("\Mods\terrains\\beacons.lua") +-- beacons:MarkerShow() +-- +-- This will load the beacons from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant beacons are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Get Closest Beacon -- --- local beacons=BEACONS:New("G:\Games\DCS World Testing\Mods\terrains\GermanyColdWar\beacons.lua") --- --- This is how it works. -- -- @field #BEACONS BEACONS = { ClassName = "BEACONS", - verbose = 0, + verbose = 1, beacons = {}, } +--- Mission capability. +-- @type BEACONS.Beacon +-- @field #function display_name Function that returns the localized name. +-- @field #number type Beacon type. +-- @field #string beaconId Beacon ID. +-- @field #string callsign Call sign. +-- @field #number frequency Frequency in Hz. +-- @field #number channel TACAN, RSBN or PRMG channel depending on type. +-- @field #table position Position table. +-- @field #number direction Direction in degrees. +-- @field #table positionGeo Table with latitude and longitude. +-- @field #table sceneObjects Table with scenery objects, e.g. `{t:393396742}`. +-- @field #number chartOffsetX No idea what this offset is?! +-- @field DCS#Vec3 vec3 Position vector 3D. +-- @field #number markerID ID for the F10 marker. +-- @field #string typeName Name of becon type. +-- @field Wrapper.Scenery#SCENERY scenery The scenery object. + --- BEACONS class version. -- @field #string version -BEACONS.version="0.0.0" +BEACONS.version="0.0.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: A lot... +-- DONE: TACAN channel from frequency (was already in beacon.lua as channel) +-- DONE: Scenery object ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Create a new BECAONS class instance from a given table. +-- @param #BEACONS self +-- @param #table BeaconTable Table with beacon info. +-- @return #BEACONS self +function BEACONS:NewFromTable(BeaconTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #BEACONS + + for _,_beacon in pairs(BeaconTable) do + local beacon=_beacon --#BEACONS.Beacon + + -- Get 3D vector + beacon.vec3={x=beacon.position[1], y=beacon.position[2], z=beacon.position[3]} + + -- Get coordinate + beacon.coordinate=COORDINATE:NewFromVec3(beacon.vec3) + + -- Get type name + beacon.typeName=self:_GetTypeName(beacon.type) + + -- Find closest scenery object from scan + beacon.scenery=beacon.coordinate:FindClosestScenery(20) + + -- Debug stuff for scenery object + if false then + if beacon.scenery then + env.info(string.format("FF Beacon %s %s %s got scenery object %s, %s", beacon.callsign, beacon.beaconId, beacon.typeName, beacon.scenery:GetName(), beacon.scenery:GetTypeName() )) + UTILS.PrintTableToLog(beacon.scenery.SceneryObject) + UTILS.PrintTableToLog(beacon.sceneObjects) + else + env.info(string.format("FF NO scenery object %s %s %s ", beacon.callsign, beacon.beaconId, beacon.typeName)) + end + end + + -- Add to table + table.insert(self.beacons, beacon) + end + + -- Debug output + self:I(string.format("Added %d beacons", #self.beacons)) + + if self.verbose > 0 then + local text="Beacon types:" + for typeName,typeID in pairs(BEACON.Type) do + local n=self:CountBeacons(typeID) + text=text..string.format("\n%s = %d", typeName, n) + end + self:I(text) + end + + return self +end + + --- Create a new BECAONS class instance from a given file. -- @param #BEACONS self -- @param #string FileName Full path to the file containing the map beacons. @@ -79,7 +167,20 @@ function BEACONS:NewFromFile(FileName) -- Inherit everything from BASE class. self=BASE:Inherit(self, BASE:New()) -- #BEACONS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with beacon info does not exist!")) + return nil + end + -- This will create a global table `beacons` + dofile(FileName) + + -- Get beacons from table. + self=self:NewFromTable(beacons) + return self end @@ -87,18 +188,141 @@ end -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Add marker all beacons on the F10 map. +--- Get 3D position vector of a specific beacon. -- @param #BEACONS self +-- @param #BEACONS.Beacon beacon The beacon data structure. +-- @return DCS#Vec3 Position vector. +function BEACONS:GetVec3(beacon) + return beacon.vec3 +end + +--- Get COORDINATE of a specific beacon. +-- @param #BEACONS self +-- @param #BEACONS.Beacon beacon The beacon data structure. +-- @return Core.Point#COORDINATE The coordinate. +function BEACONS:GetCoordinate(beacon) + local coordinate=COORDINATE:NewFromVec3(beacon.vec3) + return coordinate +end + +--- Find closest beacon to a given coordinate. +-- @param #BEACONS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`. +-- @param #number DistMax (Optional) Max search distance in meters. +-- @param #table ExcludeList (Optional) List of beacons to exclude. +-- @return #BEACONS.Beacon The closest beacon. +function BEACONS:GetClosestBeacon(Coordinate, TypeID, DistMax, ExcludeList) + + local beacon=nil --#BEACONS.Beacon + local distmin=math.huge + + ExcludeList=ExcludeList or {} + + for _,_beacon in pairs(self.beacons) do + local bc=_beacon --#BEACONS.Beacon + + if (TypeID==nil or TypeID==bc.type) and (not UTILS.IsInTable(ExcludeList, bc, "beaconId")) then + + local dist=Coordinate:Get2DDistance(bc.vec3) + + if dist=1e6 then + freq=freq/1e6 + unit="MHz" + elseif freq>=1e3 then + freq=freq/1e3 + unit="kHz" + end + + return freq, unit +end + +--- Get name of beacon type. +-- @param #BEACONS self +-- @param #number typeID Beacon type number. +-- @return #string Type name. +function BEACONS:_GetTypeName(typeID) + + if typeID~=nil then + for typeName,_typeID in pairs(BEACON.Type) do + if _typeID==typeID then + return typeName + end + end + end + + return "Unknown" +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Navigation/Radios.lua b/Moose Development/Moose/Navigation/Radios.lua new file mode 100644 index 000000000..7e13e57e3 --- /dev/null +++ b/Moose Development/Moose/Navigation/Radios.lua @@ -0,0 +1,330 @@ +--- **NAVIGATION** - Airbase radios. +-- +-- **Main Features:** +-- +-- * Get radio frequencies of airbases +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Radios). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Radios +-- @image NAVIGATION_Radios.png + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- RADIOS class. +-- @type RADIOS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table radios Radios. +-- +-- @extends Core.Base#BASE + +--- *It's not true I had nothing on, I had the radio on.* -- *Marilyn Monroe* +-- +-- === +-- +-- # The RADIOS Concept +-- +-- This class is desinged to make information about radios of a map/theatre easier accessible. The information contains mostly the frequencies of airbases of the map. +-- +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `radio.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. +-- +-- # Basic Setup +-- +-- A new `RADIOS` object can be created with the @{#RADIOS.NewFromFile}(*radio_lua_file*) function. +-- +-- local radios=RADIOS:NewFromFile("\Mods\terrains\\radios.lua") +-- radios:MarkerShow() +-- +-- This will load the radios from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant radios are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Closest Radio +-- +-- +-- @field #RADIOS +RADIOS = { + ClassName = "RADIOS", + verbose = 0, + radios = {}, +} + +--- Radio item data structure. +-- @type RADIOS.Radio +-- @field #string radioId Radio ID. +-- @field #table role Roles of the radio (usually {"ground", "tower", "approach"}). +-- @field #table callsign Callsigns of the radio (usually the airbase name). +-- @field #table frequency Frequencies of the radios. +-- @field #table position Position table. +-- @field #table sceneObjects Scenery objects. +-- @field #string name Name of the airbase. +-- @field Wrapper.Airbase#AIRBASE airbase Airbase. + +--- Radio item data structure. +-- @type RADIOS.Frequency +-- @field #number modu Modulation type. +-- @field #number freq Frequency in Hz. + + +--- RADIOS class version. +-- @field #string version +RADIOS.version="0.0.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new RADIOS class instance from a given table. +-- @param #RADIOS self +-- @param #table RadioTable Table with radios info. +-- @return #RADIOS self +function RADIOS:NewFromTable(RadioTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #RADIOS + + local airbasenames=AIRBASE.GetAllAirbaseNames() + + for _,_radio in pairs(RadioTable) do + local radio=_radio --#RADIOS.Radio + + --UTILS.PrintTableToLog(radio) + --UTILS.PrintTableToLog(radio.callsign) + + -- The table structure of callsign is a bit awkward. We need to get the airbase name. + local cs=radio.callsign[1] + if cs and cs.common then + radio.name=cs.common[1] + elseif cs and cs.nato then + radio.name=cs.nato[1] + else + radio.name="Unknown" + end + + --UTILS.PrintTableToLog(radio.callsign) + + radio.name=self:_GetAirbaseName(airbasenames, radio.name) + + radio.airbase=AIRBASE:FindByName(radio.name) + + if radio.airbase then + radio.coordinate=radio.airbase:GetCoordinate() + end + + -- Add to table + table.insert(self.radios, radio) + end + + -- Debug output + self:I(string.format("Added %d radios", #self.radios)) + + return self +end + + +--- Create a new RADIOS class instance from a given file. +-- @param #RADIOS self +-- @param #string FileName Full path to the file containing the map radios. +-- @return #RADIOS self +function RADIOS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #RADIOS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with radios info does not exist! File=%s", tostring(FileName))) + return nil + end + + -- Backup DCS radio table + local radiobak=UTILS.DeepCopy(radio) + + -- This will create a global table `radio` + dofile(FileName) + + -- Get radios from table. + self=self:NewFromTable(radio) + + -- Restore DCS radio table + radio=UTILS.DeepCopy(radiobak) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 3D position vector of a specific radio. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return DCS#Vec3 Position vector. +function RADIOS:GetVec3(radio) + return radio.vec3 +end + +--- Get COORDINATE of a specific radio. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return Core.Point#COORDINATE The coordinate. +function RADIOS:GetCoordinate(radio) + return radio.coordinate +end + +--- Add markers for all radios on the F10 map. +-- @param #RADIOS self +-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio. +-- @return #RADIOS self +function RADIOS:MarkerShow(Radio) + + for _,_radio in pairs(self.radios) do + local radio=_radio --#RADIOS.Radio + if Radio==nil or Radio.radioId==radio.radioId then + local coord=self:GetCoordinate(radio) + if coord then + local text=self:_GetMarkerText(radio) + if radio.markerID then + UTILS.RemoveMark(radio.markerID) + end + radio.markerID=coord:MarkToAll(text) + end + end + end + + return self +end + +--- Remove markers of all radios from the F10 map. +-- @param #RADIOS self +-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio. +-- @return #RADIOS self +function RADIOS:MarkerRemove(Radio) + + for _,_radio in pairs(self.radios) do + local radio=_radio --#RADIOS.Radio + if Radio==nil or Radio.radioId==radio.radioId then + if radio.markerID then + UTILS.RemoveMark(radio.markerID) + radio.markerID=nil + end + end + end + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get text displayed in the F10 marker. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return #string Marker text. +function RADIOS:_GetMarkerText(radio) + + local text=string.format("Radio %s", tostring(radio.name)) + for b,f in pairs(radio.frequency) do + local frequency=f --#RADIOS.Frequency + local mod=frequency[1] + local fre=frequency[2] + local freq, funit=self:_GetFrequency(fre) + --UTILS.PrintTableToLog(frequency) + local band=self:_GetBandName(b) + text=text..string.format("\n%s: %.3f %s", band, freq, funit) + end + + return text +end + + +--- Get converted frequency. +-- @param #RADIOS self +-- @param #number freq Frequency in Hz. +-- @return #number Frequency in better unit. +-- @return #string Unit ("Hz", "kHz", "MHz"). +function RADIOS:_GetFrequency(freq) + + freq=freq or 0 + local unit="Hz" + + if freq>=1e6 then + freq=freq/1e6 + unit="MHz" + elseif freq>=1e3 then + freq=freq/1e3 + unit="kHz" + end + + return freq, unit +end + +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #number BandNumber Band as number. +-- @return #string Band name. +function RADIOS:_GetBandName(BandNumber) + + if BandNumber~=nil then + for bandName,bandNumber in pairs(ENUMS.FrequencyBand) do + if bandNumber==BandNumber then + return bandName + end + end + end + + return "Unknown" +end + +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #table airbasenames Names of all airbases. +-- @param #string name Name of airbase. +-- @return #string Name of airbase +function RADIOS:_GetAirbaseName(airbasenames, name) + + local airbase=AIRBASE:FindByName(name) + + if airbase then + return name + else + for _,airbasename in pairs(airbasenames) do + if string.find(airbasename, name) then + return airbasename + end + end + end + + return "Unknown" +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Navigation/Towns.lua b/Moose Development/Moose/Navigation/Towns.lua new file mode 100644 index 000000000..dda193874 --- /dev/null +++ b/Moose Development/Moose/Navigation/Towns.lua @@ -0,0 +1,296 @@ +--- **NAVIGATION** - Beacons of the map/theatre. +-- +-- **Main Features:** +-- +-- * Find towns of map +-- * Road and rail connections +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Towns). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Towns +-- @image NAVIGATION_Towns.png + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- TOWNS class. +-- @type TOWNS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table towns Towns. +-- +-- @extends Core.Base#BASE + +--- *Hope is the beacon that guides lost ships back to the shore.* +-- +-- === +-- +-- # The TOWNS Concept +-- +-- This class is desinged to make information about towns of a map/theatre easier accessible. The information contains location and road/rail connections of the towns. +-- +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `towns.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. +-- +-- # Basic Setup +-- +-- A new `TOWNS` object can be created with the @{#TOWNS.NewFromFile}(*towns_lua_file*) function. +-- +-- local towns=TOWNS:NewFromFile("\Mods\terrains\\towns.lua") +-- towns:MarkerShow() +-- +-- This will load the towns from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant towns are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Get Closest Town +-- +-- +-- @field #TOWNS +TOWNS = { + ClassName = "TOWNS", + verbose = 0, + towns = {}, +} + +--- Town data. +-- @type TOWNS.Town +-- @field #string display_name Displayed name. +-- @field #string name Name of the town. +-- @field #number latitude Latitude. +-- @field #number longitude Longitude +-- @field DCS#Vec3 vec3 Position vector 3D. +-- @field Core.Point#COORDINATE coordinate The coordinate. +-- @field Core.Point#COORDINATE coordRoad The coordinate of the closest road. +-- @field Core.Point#COORDINATE coordRail The coordinate of the closest railway. +-- @field #number markerID ID for the F10 marker. + +--- TOWNS class version. +-- @field #string version +TOWNS.version="0.0.1" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... +-- TODO: Road connection +-- TODO: Rail connection +-- TODO: Connection between towns + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new TOWNS class instance from a given table. +-- @param #TOWNS self +-- @param #table TownTable Table with all towns data. +-- @return #TOWNS self +function TOWNS:NewFromTable(TownTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #TOWNS + + for TownName,_town in pairs(TownTable) do + local town=_town --#TOWNS.Town + + town.name=TownName + + -- Get coordinate + town.coordinate=COORDINATE:NewFromLLDD(town.latitude, town.longitude) + + -- Get coordinate of closest road + town.coordRoad=town.coordinate:GetClosestPointToRoad() + + -- Get coordinate of closest rail + town.coordRail=town.coordinate:GetClosestPointToRoad(true) + + -- Add to table + table.insert(self.towns, town) + end + + -- Debug output + self:I(string.format("Added %d towns", #self.towns)) + + return self +end + + +--- Create a new TOWNS class instance from a given file. +-- @param #TOWNS self +-- @param #string FileName Full path to the file containing the towns data. +-- @return #TOWNS self +function TOWNS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #TOWNS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with towns info does not exist!")) + return nil + end + + -- This will create a global table `towns` + dofile(FileName) + + -- Get towns from table. + self=self:NewFromTable(towns) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 3D position vector of a specific town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return DCS#Vec3 Position vector. +function TOWNS:GetVec3(town) + return town.vec3 +end + +--- Get COORDINATE of a specific town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The coordinate. +function TOWNS:GetCoordinate(town) + return town.coordinate +end + +--- Get closest road coordinate of a town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The closest road coordinate. +function TOWNS:GetCoordRoad(town) + return town.coordRoad +end + +--- Get closest rail coordinate of a town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The closest rail coordinate. +function TOWNS:GetCoordRail(town) + return town.coordRail +end + +--- Get road connection between two towns. +-- @param #TOWNS self +-- @param #TOWNS.Town townA The town data structure. +-- @param #TOWNS.Town townB The town data structure. +-- @return #table Table containing path coordinates. +function TOWNS:GetConnectionRoad(townA, townB) + + local path=townA.coordRoad:GetPathOnRoad(townB.coordRoad) + + return path +end + +--- Find closest town to a given coordinate. +-- @param #TOWNS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @return #TOWNS.Town The closest town. +function TOWNS:GetClosestTown(Coordinate) + + local Town=nil --#TOWNS.Town + local distmin=math.huge + + for _,_town in pairs(self.towns) do + local town=_town --#TOWNS.Town + + local dist=Coordinate:Get2DDistance(town.coordinate) + + if dist