FlightGear next
NavdbUriHandler.cxx
Go to the documentation of this file.
1// NavdbUriHandler.cxx -- Access the nav database
2//
3// Written by Torsten Dreyer, started April 2014.
4//
5// Copyright (C) 2014 Torsten Dreyer
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21#include "NavdbUriHandler.hxx"
22#include <simgear/debug/logstream.hxx>
23#include <Navaids/navrecord.hxx>
24#include <Airports/airport.hxx>
25#include <ATC/CommStation.hxx>
26#include <cJSON.h>
27
28using std::string;
29
30namespace flightgear {
31namespace http {
32
33static cJSON * createPositionArray(double x, double y, double z)
34{
35 cJSON * p = cJSON_CreateArray();
36 cJSON_AddItemToArray(p, cJSON_CreateNumber(x));
37 cJSON_AddItemToArray(p, cJSON_CreateNumber(y));
38 cJSON_AddItemToArray(p, cJSON_CreateNumber(z));
39 return p;
40}
41
42static cJSON * createPositionArray(double x, double y)
43{
44 cJSON * p = cJSON_CreateArray();
45 cJSON_AddItemToArray(p, cJSON_CreateNumber(x));
46 cJSON_AddItemToArray(p, cJSON_CreateNumber(y));
47 return p;
48}
49
50static cJSON * createLOCGeometry(FGNavRecord * navRecord)
51{
52 assert( navRecord != NULL );
53
54 cJSON * geometry = cJSON_CreateObject();
55 int range = navRecord->get_range();
56
57 double width = navRecord->localizerWidth();
58 double course = navRecord->get_multiuse();
59
60 double px[4];
61 double py[4];
62
63 px[0] = navRecord->longitude();
64 py[0] = navRecord->latitude();
65
66 for (int i = -1; i <= +1; i++) {
67 double c = SGMiscd::normalizeAngle((course + 180 + i * width / 2) * SG_DEGREES_TO_RADIANS);
68 SGGeoc geoc = SGGeoc::fromGeod(navRecord->geod());
69 SGGeod p2 = SGGeod::fromGeoc(geoc.advanceRadM(c, range * SG_NM_TO_METER));
70 px[i + 2] = p2.getLongitudeDeg();
71 py[i + 2] = p2.getLatitudeDeg();
72 }
73 // Add three lines: centerline, left and right edge
74 cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("MultiLineString"));
75 cJSON * coordinates = cJSON_CreateArray();
76 cJSON_AddItemToObject(geometry, "coordinates", coordinates);
77 for (int i = 1; i < 4; i++) {
78 cJSON * line = cJSON_CreateArray();
79 cJSON_AddItemToArray(coordinates, line);
80 cJSON_AddItemToArray(line, createPositionArray(px[0], py[0]));
81 cJSON_AddItemToArray(line, createPositionArray(px[i], py[i]));
82 }
83
84 return geometry;
85}
86
87static cJSON * createPointGeometry(FGPositioned * positioned )
88{
89 cJSON * geometry = cJSON_CreateObject();
90 cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("Point"));
91 cJSON_AddItemToObject(geometry, "coordinates",
92 createPositionArray(positioned ->longitude(), positioned->latitude(), positioned->elevationM()));
93 return geometry;
94}
95
96static cJSON * createRunwayPolygon( FGRunwayBase * rwy )
97{
98 cJSON * polygon = cJSON_CreateObject();
99 cJSON_AddItemToObject(polygon, "type", cJSON_CreateString("Polygon"));
100 cJSON * coordinates = cJSON_CreateArray();
101 cJSON_AddItemToObject(polygon, "coordinates", coordinates );
102 cJSON * linearRing = cJSON_CreateArray();
103 cJSON_AddItemToArray( coordinates, linearRing );
104
105 // compute the four corners of the runway
106 SGGeod p1 = rwy->pointOffCenterline( 0.0, rwy->widthM()/2 );
107 SGGeod p2 = rwy->pointOffCenterline( 0.0, -rwy->widthM()/2 );
108 SGGeod p3 = rwy->pointOffCenterline( rwy->lengthM(), -rwy->widthM()/2 );
109 SGGeod p4 = rwy->pointOffCenterline( rwy->lengthM(), rwy->widthM()/2 );
110 cJSON_AddItemToArray( linearRing, createPositionArray(p1.getLongitudeDeg(), p1.getLatitudeDeg()) );
111 cJSON_AddItemToArray( linearRing, createPositionArray(p2.getLongitudeDeg(), p2.getLatitudeDeg()) );
112 cJSON_AddItemToArray( linearRing, createPositionArray(p3.getLongitudeDeg(), p3.getLatitudeDeg()) );
113 cJSON_AddItemToArray( linearRing, createPositionArray(p4.getLongitudeDeg(), p4.getLatitudeDeg()) );
114 // close the ring
115 cJSON_AddItemToArray( linearRing, createPositionArray(p1.getLongitudeDeg(), p1.getLatitudeDeg()) );
116 return polygon;
117}
118
119static cJSON * createAirportGeometry(FGAirport * airport )
120{
121 assert( airport != NULL );
122 FGRunwayList runways = airport->getRunwaysWithoutReciprocals();
123
124 if( runways.empty() ) {
125 // no runways? Create a Point geometry
126 return createPointGeometry( airport );
127 }
128
129 cJSON * geometry = cJSON_CreateObject();
130
131 // if there are runways, create a geometry collection
132 cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("GeometryCollection"));
133 cJSON * geometryCollection = cJSON_CreateArray();
134 cJSON_AddItemToObject(geometry, "geometries", geometryCollection);
135
136 // the first item is the aerodrome reference point
137 cJSON_AddItemToArray( geometryCollection, createPointGeometry(airport) );
138
139 // followed by the runway polygons
140 for( FGRunwayList::iterator it = runways.begin(); it != runways.end(); ++it ) {
141 cJSON_AddItemToArray( geometryCollection, createRunwayPolygon(*it) );
142 }
143
144 FGTaxiwayList taxiways = airport->getTaxiways();
145 // followed by the taxiway polygons
146 for( FGTaxiwayList::iterator it = taxiways.begin(); it != taxiways.end(); ++it ) {
147 cJSON_AddItemToArray( geometryCollection, createRunwayPolygon(*it) );
148 }
149
150 return geometry;
151}
152
153static cJSON * createGeometryFor(FGPositioned * positioned)
154{
155 switch( positioned->type() ) {
158 return createLOCGeometry( dynamic_cast<FGNavRecord*>(positioned) );
159
161 return createAirportGeometry( dynamic_cast<FGAirport*>(positioned) );
162
163 default:
164 return createPointGeometry( positioned );
165 }
166}
167
168static void addAirportProperties(cJSON * json, FGAirport * airport )
169{
170 if( NULL == airport ) return;
171 double longestRunwayLength = 0.0;
172 double longestRunwayHeading = 0.0;
173 const char * longestRunwaySurface = "";
174
175 cJSON_AddItemToObject(json, "name", cJSON_CreateString(airport->getName().c_str()));
176 cJSON * runwaysJson = cJSON_CreateArray();
177 cJSON_AddItemToObject(json, "runways", runwaysJson);
178
179 FGRunwayList runways = airport->getRunways();
180 for( FGRunwayList::iterator it = runways.begin(); it != runways.end(); ++it ) {
181 FGRunway * runway = *it;
182 cJSON * runwayJson = cJSON_CreateObject();
183 cJSON_AddItemToArray( runwaysJson, runwayJson );
184 cJSON_AddItemToObject(runwayJson, "id", cJSON_CreateString(runway->ident().c_str()));
185 cJSON_AddItemToObject(runwayJson, "length_m", cJSON_CreateNumber(runway->lengthM()));
186 cJSON_AddItemToObject(runwayJson, "width_m", cJSON_CreateNumber(runway->widthM()));
187 cJSON_AddItemToObject(runwayJson, "surface", cJSON_CreateString(runway->surfaceName()));
188 cJSON_AddItemToObject(runwayJson, "heading_deg", cJSON_CreateNumber(runway->headingDeg()));
189 double d = runway->displacedThresholdM();
190 if( d > .0 )
191 cJSON_AddItemToObject(runwayJson, "dispacedThreshold_m", cJSON_CreateNumber(d));
192
193 d = runway->stopwayM();
194 if( d > .0 )
195 cJSON_AddItemToObject(runwayJson, "stopway_m", cJSON_CreateNumber(d));
196
197 if( runway->lengthM() > longestRunwayLength ) {
198 longestRunwayLength = runway->lengthM();
199 longestRunwayHeading = runway->headingDeg();
200 longestRunwaySurface = runway->surfaceName();
201 }
202 }
203 cJSON_AddItemToObject(json, "longestRwyLength_m", cJSON_CreateNumber(longestRunwayLength));
204 cJSON_AddItemToObject(json, "longestRwyHeading_deg", cJSON_CreateNumber(longestRunwayHeading));
205 cJSON_AddItemToObject(json, "longestRwySurface", cJSON_CreateString(longestRunwaySurface));
206 if( airport->getMetar() ) {
207 cJSON_AddItemToObject(json, "metar", cJSON_CreateTrue());
208 }
209
210 cJSON * commsJson = cJSON_CreateArray();
211 cJSON_AddItemToObject(json, "comm", commsJson);
212 flightgear::CommStationList comms = airport->commStations();
213 for( flightgear::CommStationList::iterator it = comms.begin(); it != comms.end(); ++it ) {
214 flightgear::CommStation * comm = *it;
215 cJSON * commJson = cJSON_CreateObject();
216 cJSON_AddItemToArray( commsJson, commJson );
217 cJSON_AddItemToObject(commJson, "id", cJSON_CreateString(comm->ident().c_str()));
218 cJSON_AddItemToObject(commJson, "mhz", cJSON_CreateNumber(comm->freqMHz()));
219 }
220}
221
222static void addNAVProperties(cJSON * json, FGNavRecord * navRecord )
223{
224 if( NULL == navRecord ) return;
225 cJSON_AddItemToObject(json, "range_nm", cJSON_CreateNumber(navRecord->get_range()));
226 cJSON_AddItemToObject(json, "frequency", cJSON_CreateNumber((double) navRecord->get_freq() / 100.0));
227 switch (navRecord->type()) {
230 cJSON_AddItemToObject(json, "localizer-course", cJSON_CreateNumber(navRecord->get_multiuse()));
231 break;
232
234 cJSON_AddItemToObject(json, "variation", cJSON_CreateNumber(navRecord->get_multiuse()));
235 break;
236
237 default:
238 break;
239 }
240}
241
242static cJSON * createPropertiesFor(FGPositioned * positioned)
243{
244 cJSON * properties = cJSON_CreateObject();
245
246 cJSON_AddItemToObject(properties, "name", cJSON_CreateString(positioned->name().c_str()));
247 // also add id to properties
248 cJSON_AddItemToObject(properties, "id", cJSON_CreateString(positioned->ident().c_str()));
249 cJSON_AddItemToObject(properties, "type", cJSON_CreateString(positioned->typeString()));
250 cJSON_AddItemToObject(properties, "elevation-m", cJSON_CreateNumber(positioned->elevationM()));
251 addNAVProperties( properties, dynamic_cast<FGNavRecord*>(positioned) );
252 addAirportProperties( properties, dynamic_cast<FGAirport*>(positioned) );
253 return properties;
254}
255
256static cJSON * createFeatureFor(FGPositioned * positioned)
257{
258 cJSON * feature = cJSON_CreateObject();
259
260 // A GeoJSON object with the type "Feature" is a feature object.
261 cJSON_AddItemToObject(feature, "type", cJSON_CreateString("Feature"));
262
263 // A feature object must have a member with the name "geometry".
264 // The value of the geometry member is a geometry object as defined above or a JSON null value.
265 cJSON_AddItemToObject(feature, "geometry", createGeometryFor(positioned));
266
267 // A feature object must have a member with the name "properties".
268 // The value of the properties member is an object (any JSON object or a JSON null value).
269 cJSON_AddItemToObject(feature, "properties", createPropertiesFor(positioned));
270
271 // If a feature has a commonly used identifier, that identifier should be included
272 // as a member of the feature object with the name "id".
273 cJSON_AddItemToObject(feature, "id", cJSON_CreateString(positioned->ident().c_str()));
274
275 return feature;
276}
277
278bool NavdbUriHandler::handleRequest(const HTTPRequest & request, HTTPResponse & response, Connection * connection)
279{
280
281 response.Header["Content-Type"] = "application/json; charset=UTF-8";
282 response.Header["Access-Control-Allow-Origin"] = "*";
283 response.Header["Access-Control-Allow-Methods"] = "OPTIONS, GET";
284 response.Header["Access-Control-Allow-Headers"] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token";
285
286 if( request.Method == "OPTIONS" ){
287 return true; // OPTIONS only needs the headers
288 }
289
290 if( request.Method != "GET" ){
291 response.Header["Allow"] = "OPTIONS, GET";
292 response.StatusCode = 405;
293 response.Content = "{}";
294 return true;
295 }
296
297 bool indent = request.RequestVariables.get("i") == "y";
298
299 string query = request.RequestVariables.get("q");
300 FGPositionedList result;
301
302 if (query == "findWithinRange") {
303 // ?q=findWithinRange&lat=53.5&lon=10.0&range=100&type=vor,ils
304
305 double lat, lon, range = -1;
306 try {
307 lat = std::stod(request.RequestVariables.get("lat"));
308 lon = std::stod(request.RequestVariables.get("lon"));
309 range = std::stod(request.RequestVariables.get("range"));
310 }
311 catch (...) {
312 goto fail;
313 }
314
315 if (range <= 1.0) goto fail;
316 // In remembrance of a famous bug
317
318 SGGeod pos = SGGeod::fromDeg(lon, lat);
320 try {
322 }
323 catch (...) {
324 goto fail;
325 }
326
327 result = FGPositioned::findWithinRange(pos, range, &filter);
328 } else if (query == "airports") {
329 cJSON * json = cJSON_CreateArray();
330 for( char ** airports = FGAirport::searchNamesAndIdents(""); *airports; airports++ ) {
331 cJSON_AddItemToArray(json, cJSON_CreateString(*airports));
332 }
333 char * jsonString = indent ? cJSON_Print(json) : cJSON_PrintUnformatted(json);
334 cJSON_Delete(json);
335 response.Content = jsonString;
336 free(jsonString);
337 return true;
338
339 } else if (query == "airport") {
341 if( airport.valid() )
342 result.push_back( airport );
343 } else {
344 goto fail;
345 }
346
347 { // create some GeoJSON from the result list
348 // GeoJSON always consists of a single object.
349 cJSON * geoJSON = cJSON_CreateObject();
350
351 // The GeoJSON object must have a member with the name "type".
352 // This member's value is a string that determines the type of the GeoJSON object.
353 cJSON_AddItemToObject(geoJSON, "type", cJSON_CreateString("FeatureCollection"));
354
355 // we send zero to many features - let's make it a FeatureCollection
356 // A GeoJSON object with the type "FeatureCollection" is a feature collection object.
357 // An object of type "FeatureCollection" must have a member with the name "features".
358 // The value corresponding to "features" is an array.
359 cJSON * featureCollection = cJSON_CreateArray();
360 cJSON_AddItemToObject(geoJSON, "features", featureCollection);
361
362 for (FGPositionedList::iterator it = result.begin(); it != result.end(); ++it) {
363 // Each element in the array is a feature object as defined above.
364 cJSON_AddItemToArray(featureCollection, createFeatureFor(*it));
365 }
366
367 char * jsonString = indent ? cJSON_Print(geoJSON) : cJSON_PrintUnformatted(geoJSON);
368 cJSON_Delete(geoJSON);
369 response.Content = jsonString;
370 free(jsonString);
371 }
372
373 return true;
374
375 fail: response.StatusCode = 400;
376 response.Content = "{ 'error': 'bad request' }";
377 return true;
378}
379
380} // namespace http
381} // namespace flightgear
382
double longitude
Definition ADA.cxx:54
#define p2(x, y)
#define p(x)
#define i(x)
std::vector< FGRunwayRef > FGRunwayList
SGSharedPtr< FGAirport > FGAirportRef
std::vector< FGTaxiwayRef > FGTaxiwayList
bool getMetar() const
Definition airport.hxx:62
FGRunwayList getRunwaysWithoutReciprocals() const
Retrieve all runways at the airport, but excluding the reciprocal runways.
Definition airport.cxx:308
static FGAirportRef findByIdent(const std::string &aIdent)
Helper to look up an FGAirport instance by unique ident.
Definition airport.cxx:489
static char ** searchNamesAndIdents(const std::string &aFilter)
Specialised helper to implement the AirportList dialog.
Definition airport.cxx:517
FGTaxiwayList getTaxiways() const
Definition airport.cxx:344
FGRunwayList getRunways() const
Retrieve all runways at the airport.
Definition airport.cxx:300
flightgear::CommStationList commStations() const
Definition airport.cxx:983
const std::string & getName() const
Definition airport.hxx:54
int get_freq() const
Definition navrecord.hxx:76
int get_range() const
Definition navrecord.hxx:77
double get_multiuse() const
Definition navrecord.hxx:78
double localizerWidth() const
return the localizer width, in degrees computation is based up ICAO stdandard width at the runway thr...
Definition navrecord.cxx:63
static TypeFilter fromString(const std::string &aFilterSpec)
virtual const std::string & name() const
Return the name of this positioned.
double longitude() const
const char * typeString() const
static FGPositionedList findWithinRange(const SGGeod &aPos, double aRangeNm, Filter *aFilter)
virtual const SGGeod & geod() const
double latitude() const
double elevationM() const
Type type() const
const std::string & ident() const
double headingDeg() const
Runway heading in degrees.
static const char * surfaceName(int surface_code)
Retrieve runway surface name, as define in Robin Peel's data.
SGGeod pointOffCenterline(double aOffset, double lateralOffset) const
double widthM() const
double lengthM() const
double stopwayM() const
Definition runways.hxx:72
double displacedThresholdM() const
Definition runways.hxx:69
std::string get(const std::string &key) const
virtual bool handleRequest(const HTTPRequest &request, HTTPResponse &response, Connection *connection)
This method gets called from the httpd if a request has been detected on the connection.
static void addAirportProperties(cJSON *json, FGAirport *airport)
static void addNAVProperties(cJSON *json, FGNavRecord *navRecord)
static cJSON * createPositionArray(double x, double y, double z)
static cJSON * createLOCGeometry(FGNavRecord *navRecord)
static cJSON * createPointGeometry(FGPositioned *positioned)
static cJSON * createAirportGeometry(FGAirport *airport)
static cJSON * createRunwayPolygon(FGRunwayBase *rwy)
static cJSON * createPropertiesFor(FGPositioned *positioned)
static cJSON * createGeometryFor(FGPositioned *positioned)
static cJSON * createFeatureFor(FGPositioned *positioned)
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
std::vector< CommStationRef > CommStationList
std::vector< FGPositionedRef > FGPositionedList