]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/NasalPositioned_cppbind.cxx
Parse geod/position and use it with new airportinfo.
[flightgear.git] / src / Scripting / NasalPositioned_cppbind.cxx
1 // NasalPositioned_cppbind.cxx -- expose FGPositioned classes to Nasal
2 //
3 // Port of NasalPositioned.cpp to the new nasal/cppbind helpers. Will replace
4 // old NasalPositioned.cpp once finished.
5 //
6 // Copyright (C) 2013  Thomas Geymayer <tomgey@gmail.com>
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include "NasalPositioned.hxx"
27
28 #include <algorithm>
29 #include <functional>
30
31 #include <boost/foreach.hpp>
32 #include <boost/algorithm/string/case_conv.hpp>
33
34 #include <simgear/nasal/cppbind/from_nasal.hxx>
35 #include <simgear/nasal/cppbind/to_nasal.hxx>
36 #include <simgear/nasal/cppbind/NasalHash.hxx>
37 #include <simgear/nasal/cppbind/Ghost.hxx>
38
39 #include <Airports/airport.hxx>
40 #include <Airports/dynamics.hxx>
41 #include <ATC/CommStation.hxx>
42 #include <Main/globals.hxx>
43 #include <Navaids/NavDataCache.hxx>
44
45 typedef nasal::Ghost<FGPositionedRef> NasalPositioned;
46 typedef nasal::Ghost<FGRunwayRef> NasalRunway;
47 typedef nasal::Ghost<FGParkingRef> NasalParking;
48 typedef nasal::Ghost<FGAirportRef> NasalAirport;
49 typedef nasal::Ghost<flightgear::CommStationRef> NasalCommStation;
50
51 //------------------------------------------------------------------------------
52 naRef to_nasal_helper(naContext c, FGPositioned* positioned)
53 {
54   return NasalPositioned::create(c, positioned);
55 }
56
57 //------------------------------------------------------------------------------
58 naRef to_nasal_helper(naContext c, FGPavement* rwy)
59 {
60   return NasalPositioned::create(c, (FGPositioned*)rwy);
61 }
62
63 //------------------------------------------------------------------------------
64 naRef to_nasal_helper(naContext c, FGRunwayBase* rwy)
65 {
66   return NasalPositioned::create(c, (FGPositioned*)rwy);
67 }
68
69 //------------------------------------------------------------------------------
70 naRef to_nasal_helper(naContext c, FGParking* parking)
71 {
72   return NasalParking::create(c, parking);
73 }
74
75 //------------------------------------------------------------------------------
76 naRef to_nasal_helper(naContext c, flightgear::SID* sid)
77 {
78   // TODO SID ghost
79   return nasal::to_nasal(c, sid->ident());
80 }
81
82 //------------------------------------------------------------------------------
83 naRef to_nasal_helper(naContext c, flightgear::STAR* star)
84 {
85   // TODO STAR ghost
86   return nasal::to_nasal(c, star->ident());
87 }
88
89 //------------------------------------------------------------------------------
90 naRef to_nasal_helper(naContext c, flightgear::Approach* iap)
91 {
92   // TODO Approach ghost
93   return nasal::to_nasal(c, iap->ident());
94 }
95
96 //------------------------------------------------------------------------------
97 naRef to_nasal_helper(naContext c, FGAirport* apt)
98 {
99   return NasalAirport::create(c, apt);
100 }
101
102 //------------------------------------------------------------------------------
103 naRef to_nasal_helper(naContext c, const SGGeod& pos)
104 {
105   nasal::Hash hash(c);
106   hash.set("lat", pos.getLatitudeDeg());
107   hash.set("lon", pos.getLongitudeDeg());
108   hash.set("elevation", pos.getElevationM());
109   return hash.get_naRef();
110 }
111
112 //------------------------------------------------------------------------------
113 static FGRunwayBase* f_airport_runway(FGAirport& apt, std::string ident)
114 {
115   boost::to_upper(ident);
116
117   if( apt.hasRunwayWithIdent(ident) )
118     return apt.getRunwayByIdent(ident);
119   else if( apt.hasHelipadWithIdent(ident) )
120     return apt.getHelipadByIdent(ident);
121
122   return 0;
123 }
124
125 //------------------------------------------------------------------------------
126 template<class T, class C1, class C2>
127 std::vector<T> extract( const std::vector<C1*>& in,
128                         T (C2::*getter)() const )
129 {
130   std::vector<T> ret(in.size());
131   std::transform(in.begin(), in.end(), ret.begin(), std::mem_fun(getter));
132   return ret;
133 }
134
135 //------------------------------------------------------------------------------
136 static naRef f_airport_comms(FGAirport& apt, const nasal::CallContext& ctx)
137 {
138   FGPositioned::Type comm_type =
139     FGPositioned::typeFromName( ctx.getArg<std::string>(0) );
140
141   // if we have an explicit type, return a simple vector of frequencies
142   if( comm_type != FGPositioned::INVALID )
143     return ctx.to_nasal
144     (
145       extract( apt.commStationsOfType(comm_type),
146                &flightgear::CommStation::freqMHz )
147     );
148   else
149     // otherwise return a vector of ghosts, one for each comm station.
150     return ctx.to_nasal(apt.commStations());
151 }
152
153 //------------------------------------------------------------------------------
154 FGRunway* runwayFromNasalArg( const FGAirport& apt,
155                               const nasal::CallContext& ctx,
156                               size_t index = 0 )
157 {
158   if( index >= ctx.argc )
159     return NULL;
160
161   try
162   {
163     std::string ident = ctx.getArg<std::string>(index);
164     if( !ident.empty() )
165     {
166       if( !apt.hasRunwayWithIdent(ident) )
167         // TODO warning/exception?
168         return NULL;
169
170       return apt.getRunwayByIdent(ident);
171     }
172   }
173   catch(...)
174   {}
175
176   // TODO warn/error if no runway?
177   return NasalRunway::fromNasal(ctx.c, ctx.args[index]);
178 }
179
180 //------------------------------------------------------------------------------
181 static naRef f_airport_sids(FGAirport& apt, const nasal::CallContext& ctx)
182 {
183   FGRunway* rwy = runwayFromNasalArg(apt, ctx);
184   return ctx.to_nasal
185   (
186     extract(rwy ? rwy->getSIDs() : apt.getSIDs(), &flightgear::SID::ident)
187   );
188 }
189
190 //------------------------------------------------------------------------------
191 static naRef f_airport_stars(FGAirport& apt, const nasal::CallContext& ctx)
192 {
193   FGRunway* rwy = runwayFromNasalArg(apt, ctx);
194   return ctx.to_nasal
195   (
196     extract(rwy ? rwy->getSTARs() : apt.getSTARs(), &flightgear::STAR::ident)
197   );
198 }
199
200 //------------------------------------------------------------------------------
201 static naRef f_airport_approaches(FGAirport& apt, const nasal::CallContext& ctx)
202 {
203   FGRunway* rwy = runwayFromNasalArg(apt, ctx);
204
205   flightgear::ProcedureType type = flightgear::PROCEDURE_INVALID;
206   std::string type_str = ctx.getArg<std::string>(1);
207   if( !type_str.empty() )
208   {
209     boost::to_upper(type_str);
210     if(      type_str == "NDB" ) type = flightgear::PROCEDURE_APPROACH_NDB;
211     else if( type_str == "VOR" ) type = flightgear::PROCEDURE_APPROACH_VOR;
212     else if( type_str == "ILS" ) type = flightgear::PROCEDURE_APPROACH_ILS;
213     else if( type_str == "RNAV") type = flightgear::PROCEDURE_APPROACH_RNAV;
214   }
215
216   return ctx.to_nasal
217   (
218     extract( rwy ? rwy->getApproaches(type)
219                  // no runway specified, report them all
220                  : apt.getApproaches(type),
221              &flightgear::Approach::ident )
222   );
223 }
224
225 //------------------------------------------------------------------------------
226 static FGParkingList
227 f_airport_parking(FGAirport& apt, nasal::CallContext ctx)
228 {
229   std::string type = ctx.getArg<std::string>(0);
230   bool only_available = ctx.getArg<bool>(1);
231
232   FGAirportDynamics* dynamics = apt.getDynamics();
233   PositionedIDVec parkings =
234     flightgear::NavDataCache::instance()
235       ->airportItemsOfType(apt.guid(), FGPositioned::PARKING);
236
237   FGParkingList ret;
238   BOOST_FOREACH(PositionedID parking, parkings)
239   {
240     // filter out based on availability and type
241     if( only_available && !dynamics->isParkingAvailable(parking) )
242       continue;
243
244     FGParking* park = dynamics->getParking(parking);
245     if( !type.empty() && (park->getType() != type) )
246       continue;
247
248     ret.push_back(park);
249   }
250
251   return ret;
252 }
253
254 /**
255  * Extract a SGGeod from a nasal function argument list.
256  *
257  * <lat>, <lon>
258  * {"lat": <lat-deg>, "lon": <lon-deg>}
259  * geo.Coord.new() (aka. {"_lat": <lat-rad>, "_lon": <lon-rad>})
260  */
261 static bool extractGeod(nasal::CallContext& ctx, SGGeod& result)
262 {
263   if( !ctx.argc )
264     return false;
265
266   if( ctx.isGhost(0) )
267   {
268     FGPositioned* pos =
269       NasalPositioned::fromNasal(ctx.c, ctx.requireArg<naRef>(0));
270
271     if( pos )
272     {
273       result = pos->geod();
274       ctx.popFront();
275       return true;
276     }
277   }
278   else if( ctx.isHash(0) )
279   {
280     nasal::Hash pos_hash = ctx.requireArg<nasal::Hash>(0);
281
282     // check for manual latitude / longitude names
283     naRef lat = pos_hash.get("lat"),
284           lon = pos_hash.get("lon");
285     if( naIsNum(lat) && naIsNum(lon) )
286     {
287       result = SGGeod::fromDeg( ctx.from_nasal<double>(lon),
288                                 ctx.from_nasal<double>(lat) );
289       ctx.popFront();
290       return true;
291     }
292
293     // geo.Coord uses _lat/_lon in radians
294     // TODO should we check if its really a geo.Coord?
295     lat = pos_hash.get("_lat");
296     lon = pos_hash.get("_lon");
297     if( naIsNum(lat) && naIsNum(lon) )
298     {
299       result = SGGeod::fromRad( ctx.from_nasal<double>(lon),
300                                 ctx.from_nasal<double>(lat) );
301       ctx.popFront();
302       return true;
303     }
304   }
305   else if( ctx.isNumeric(0) && ctx.isNumeric(1) )
306   {
307     // lat, lon
308     result = SGGeod::fromDeg( ctx.requireArg<double>(1),
309                               ctx.requireArg<double>(0) );
310     ctx.popFront(2);
311     return true;
312   }
313
314   return false;
315 }
316
317 //------------------------------------------------------------------------------
318 // Returns Nasal ghost for particular or nearest airport of a <type>, or nil
319 // on error.
320 //
321 // airportinfo(<id>);                   e.g. "KSFO"
322 // airportinfo(<type>);                 type := ("airport"|"seaport"|"heliport")
323 // airportinfo()                        same as  airportinfo("airport")
324 // airportinfo(<lat>, <lon> [, <type>]);
325 static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
326 {
327   nasal::CallContext ctx(c, argc, args);
328
329   SGGeod pos;
330   if( !extractGeod(ctx, pos) )
331     pos = globals->get_aircraft_position();
332
333   if( ctx.argc > 1 )
334     naRuntimeError(ctx.c, "airportinfo() with invalid function arguments");
335
336   // optional type/ident
337   std::string ident = ctx.getArg<std::string>(0, "airport");
338
339   FGAirport::TypeRunwayFilter filter;
340   if( !filter.fromTypeString(ident) )
341     // user provided an <id>, hopefully
342     return ctx.to_nasal(FGAirport::findByIdent(ident));
343
344   double maxRange = 10000.0; // expose this? or pick a smaller value?
345   return ctx.to_nasal( FGAirport::findClosest(pos, maxRange, &filter) );
346 }
347
348 //------------------------------------------------------------------------------
349 naRef initNasalPositioned_cppbind(naRef globalsRef, naContext c, naRef gcSave)
350 {
351   NasalPositioned::init("FGPositioned")
352     .member("id", &FGPositioned::ident)
353     .member("ident", &FGPositioned::ident) // TODO to we really need id and ident?
354     .member("name", &FGPositioned::name)
355     .member("lat", &FGPositioned::latitude)
356     .member("lon", &FGPositioned::longitude)
357     .member("elevation", &FGPositioned::elevationM);
358   NasalRunway::init("FGRunway")
359     .bases<NasalPositioned>();
360   NasalParking::init("FGParking")
361     .bases<NasalPositioned>();
362   NasalCommStation::init("CommStation")
363     .bases<NasalPositioned>()
364     .member("frequency", &flightgear::CommStation::freqMHz);
365   NasalAirport::init("FGAirport")
366     .bases<NasalPositioned>()
367     .member("has_metar", &FGAirport::getMetar)
368     .member("runways", &FGAirport::getRunwayMap)
369     .member("helipads", &FGAirport::getHelipadMap)
370     .member("taxiways", &FGAirport::getTaxiways)
371     .member("pavements", &FGAirport::getPavements)
372     .method("runway", &f_airport_runway)
373     .method("helipad", &f_airport_runway)
374     .method("tower", &FGAirport::getTowerLocation)
375     .method("comms", &f_airport_comms)
376     .method("sids", &f_airport_sids)
377     .method("stars", &f_airport_stars)
378     .method("getApproachList", f_airport_approaches)
379     .method("parking", &f_airport_parking)
380     .method("getSid", &FGAirport::findSIDWithIdent)
381     .method("getStar", &FGAirport::findSTARWithIdent)
382     .method("getIAP", &FGAirport::findApproachWithIdent)
383     .method("tostring", &FGAirport::toString);
384
385   nasal::Hash globals(globalsRef, c),
386               positioned( globals.createHash("positioned") );
387
388   positioned.set("airportinfo", &f_airportinfo);
389
390   return naNil();
391 }