diff --git a/src/Airports/runwaybase.cxx b/src/Airports/runwaybase.cxx index 4f60682a9..d5176c88b 100644 --- a/src/Airports/runwaybase.cxx +++ b/src/Airports/runwaybase.cxx @@ -45,6 +45,22 @@ using std::string; * 9 - dirt helipad * 12 - lakebed */ +const char * FGRunwayBase::surfaceName( int surface_code ) +{ + switch( surface_code ) { + case 1: return "asphalt"; + case 2: return "concrete"; + case 3: return "turf"; + case 4: return "dirt"; + case 5: return "gravel"; + case 6: return "asphalt helipad"; + case 7: return "concrete helipad"; + case 8: return "turf helipad"; + case 9: return "dirt helipad"; + case 12: return "lakebed"; + default: return "unknown"; + } +} FGRunwayBase::FGRunwayBase(PositionedID aGuid, Type aTy, const string& aIdent, const SGGeod& aGeod, diff --git a/src/Airports/runwaybase.hxx b/src/Airports/runwaybase.hxx index e13101b19..c365f69e5 100644 --- a/src/Airports/runwaybase.hxx +++ b/src/Airports/runwaybase.hxx @@ -82,6 +82,12 @@ public: */ int surface() const { return _surface_code; } + + /** + * Retrieve runway surface name, as define in Robin Peel's data + */ + static const char * surfaceName( int surface_code ); + const char * surfaceName() { return surfaceName( _surface_code ); } protected: diff --git a/src/Network/http/NavdbUriHandler.cxx b/src/Network/http/NavdbUriHandler.cxx index 7cc341d6a..2b568dc1c 100644 --- a/src/Network/http/NavdbUriHandler.cxx +++ b/src/Network/http/NavdbUriHandler.cxx @@ -21,6 +21,7 @@ #include "NavdbUriHandler.hxx" #include #include +#include #include <3rdparty/cjson/cJSON.h> #include @@ -29,9 +30,7 @@ using std::string; namespace flightgear { namespace http { -//Ref: http://geojson.org/ - -static cJSON * createPosition(double x, double y) +static cJSON * createPositionArray(double x, double y) { cJSON * p = cJSON_CreateArray(); cJSON_AddItemToArray(p, cJSON_CreateNumber(x)); @@ -39,45 +38,92 @@ static cJSON * createPosition(double x, double y) return p; } -static cJSON * createGeometryFor(FGNavRecord * navRecord) +static cJSON * createLOCGeometry(FGNavRecord * navRecord) { - if ( NULL == navRecord) return NULL; + assert( navRecord != NULL ); + + cJSON * geometry = cJSON_CreateObject(); + int range = navRecord->get_range(); + + double width = navRecord->localizerWidth(); + double course = navRecord->get_multiuse(); + + double px[4]; + double py[4]; + + px[0] = navRecord->longitude(); + py[0] = navRecord->latitude(); + + for (int i = -1; i <= +1; i++) { + double c = SGMiscd::normalizeAngle((course + 180 + i * width / 2) * SG_DEGREES_TO_RADIANS); + SGGeoc geoc = SGGeoc::fromGeod(navRecord->geod()); + SGGeod p2 = SGGeod::fromGeoc(geoc.advanceRadM(c, range * SG_NM_TO_METER)); + px[i + 2] = p2.getLongitudeDeg(); + py[i + 2] = p2.getLatitudeDeg(); + } + // Add three lines: centerline, left and right edge + cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("MultiLineString")); + cJSON * coordinates = cJSON_CreateArray(); + cJSON_AddItemToObject(geometry, "coordinates", coordinates); + for (int i = 1; i < 4; i++) { + cJSON * line = cJSON_CreateArray(); + cJSON_AddItemToArray(coordinates, line); + cJSON_AddItemToArray(line, createPositionArray(px[0], py[0])); + cJSON_AddItemToArray(line, createPositionArray(px[i], py[i])); + } + + return geometry; +} + +static cJSON * createPointGeometry(FGPositionedRef positioned ) +{ + cJSON * geometry = cJSON_CreateObject(); + cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("Point")); + cJSON_AddItemToObject(geometry, "coordinates", createPositionArray(positioned ->longitude(), positioned->latitude())); + return geometry; +} + +static cJSON * createAirportGeometry(FGAirport * airport ) +{ + assert( airport != NULL ); + FGRunwayList runways = airport->getRunwaysWithoutReciprocals(); + + if( runways.empty() ) { + // no runways? Create a Point geometry + return createPointGeometry( airport ); + } + cJSON * geometry = cJSON_CreateObject(); - if (navRecord->type() == FGPositioned::LOC || navRecord->type() == FGPositioned::ILS) { - int range = navRecord->get_range(); + // if there are runways, create a geometry collection + cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("GeometryCollection")); + cJSON * geometryCollection = cJSON_CreateArray(); + cJSON_AddItemToObject(geometry, "geometries", geometryCollection); - double width = navRecord->localizerWidth(); - double course = navRecord->get_multiuse(); + // the first item is the aerodrome reference point + cJSON_AddItemToArray( geometryCollection, createPointGeometry(airport) ); - double px[4]; - double py[4]; - - px[0] = navRecord->longitude(); - py[0] = navRecord->latitude(); - - for (int i = -1; i <= +1; i++) { - double c = SGMiscd::normalizeAngle((course + 180 + i*width/2 ) * SG_DEGREES_TO_RADIANS); - SGGeoc geoc = SGGeoc::fromGeod(navRecord->geod()); - SGGeod p2 = SGGeod::fromGeoc(geoc.advanceRadM(c, range * SG_NM_TO_METER)); - px[i+2] = p2.getLongitudeDeg(); - py[i+2] = p2.getLatitudeDeg(); - } - // Add three lines: centerline, left and right edge - cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("MultiLineString")); + // followed by the runway polygons + for( FGRunwayList::iterator it = runways.begin(); it != runways.end(); ++it ) { + cJSON * polygon = cJSON_CreateObject(); + cJSON_AddItemToArray( geometryCollection, polygon ); + cJSON_AddItemToObject(polygon, "type", cJSON_CreateString("Polygon")); cJSON * coordinates = cJSON_CreateArray(); - cJSON_AddItemToObject(geometry, "coordinates", coordinates); - for (int i = 1; i < 4; i++) { - cJSON * line = cJSON_CreateArray(); - cJSON_AddItemToArray(coordinates, line); - cJSON_AddItemToArray(line, createPosition(px[0], py[0])); - cJSON_AddItemToArray(line, createPosition(px[i], py[i])); - } + cJSON_AddItemToObject(polygon, "coordinates", coordinates ); + cJSON * linearRing = cJSON_CreateArray(); + cJSON_AddItemToArray( coordinates, linearRing ); - } else { - - cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("Point")); - cJSON_AddItemToObject(geometry, "coordinates", createPosition(navRecord->longitude(), navRecord->latitude())); + // compute the four corners of the runway + SGGeod p1 = (*it)->pointOffCenterline( 0.0, (*it)->widthM()/2 ); + SGGeod p2 = (*it)->pointOffCenterline( 0.0, -(*it)->widthM()/2 ); + SGGeod p3 = (*it)->pointOffCenterline( (*it)->lengthM(), -(*it)->widthM()/2 ); + SGGeod p4 = (*it)->pointOffCenterline( (*it)->lengthM(), (*it)->widthM()/2 ); + cJSON_AddItemToArray( linearRing, createPositionArray(p1.getLongitudeDeg(), p1.getLatitudeDeg()) ); + cJSON_AddItemToArray( linearRing, createPositionArray(p2.getLongitudeDeg(), p2.getLatitudeDeg()) ); + cJSON_AddItemToArray( linearRing, createPositionArray(p3.getLongitudeDeg(), p3.getLatitudeDeg()) ); + cJSON_AddItemToArray( linearRing, createPositionArray(p4.getLongitudeDeg(), p4.getLatitudeDeg()) ); + // close the ring + cJSON_AddItemToArray( linearRing, createPositionArray(p1.getLongitudeDeg(), p1.getLatitudeDeg()) ); } return geometry; @@ -85,19 +131,57 @@ static cJSON * createGeometryFor(FGNavRecord * navRecord) static cJSON * createGeometryFor(FGPositionedRef positioned) { - cJSON * geometry = createGeometryFor(dynamic_cast(positioned.get())); - if ( NULL != geometry) return geometry; + switch( positioned->type() ) { + case FGPositioned::LOC: + case FGPositioned::ILS: + return createLOCGeometry( dynamic_cast(positioned.get()) ); - geometry = cJSON_CreateObject(); - cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("Point")); + case FGPositioned::AIRPORT: + return createAirportGeometry( dynamic_cast(positioned.get()) ); - cJSON * coordinates = cJSON_CreateArray(); - cJSON_AddItemToObject(geometry, "coordinates", coordinates); + default: + return createPointGeometry( positioned ); + } +} - cJSON_AddItemToArray(coordinates, cJSON_CreateNumber(positioned->longitude())); - cJSON_AddItemToArray(coordinates, cJSON_CreateNumber(positioned->latitude())); +static void addAirportProperties(cJSON * json, FGAirport * airport ) +{ + if( NULL == airport ) return; + FGRunwayList runways = airport->getRunwaysWithoutReciprocals(); + double longestRunwayLength = 0.0; + double longestRunwayHeading = 0.0; + const char * longestRunwaySurface = ""; + for( FGRunwayList::iterator it = runways.begin(); it != runways.end(); ++it ) { + FGRunwayRef runway = *it; + if( runway->lengthM() > longestRunwayLength ) { + longestRunwayLength = runway->lengthM(); + longestRunwayHeading = runway->headingDeg(); + longestRunwaySurface = runway->surfaceName(); + } + } + cJSON_AddItemToObject(json, "longestRwyLength_m", cJSON_CreateNumber(longestRunwayLength)); + cJSON_AddItemToObject(json, "longestRwyHeading_deg", cJSON_CreateNumber(longestRunwayHeading)); + cJSON_AddItemToObject(json, "longestRwySurface", cJSON_CreateString(longestRunwaySurface)); +} - return geometry; +static void addNAVProperties(cJSON * json, FGNavRecord * navRecord ) +{ + if( NULL == navRecord ) return; + cJSON_AddItemToObject(json, "range_nm", cJSON_CreateNumber(navRecord->get_range())); + cJSON_AddItemToObject(json, "frequency", cJSON_CreateNumber((double) navRecord->get_freq() / 100.0)); + switch (navRecord->type()) { + case FGPositioned::ILS: + case FGPositioned::LOC: + cJSON_AddItemToObject(json, "localizer-course", cJSON_CreateNumber(navRecord->get_multiuse())); + break; + + case FGPositioned::VOR: + cJSON_AddItemToObject(json, "variation", cJSON_CreateNumber(navRecord->get_multiuse())); + break; + + default: + break; + } } static cJSON * createPropertiesFor(FGPositionedRef positioned) @@ -107,24 +191,8 @@ static cJSON * createPropertiesFor(FGPositionedRef positioned) cJSON_AddItemToObject(properties, "name", cJSON_CreateString(positioned->name().c_str())); cJSON_AddItemToObject(properties, "type", cJSON_CreateString(positioned->typeString())); cJSON_AddItemToObject(properties, "elevation-m", cJSON_CreateNumber(positioned->elevationM())); - FGNavRecord * navRecord = dynamic_cast(positioned.get()); - if ( NULL != navRecord) { - cJSON_AddItemToObject(properties, "range-nm", cJSON_CreateNumber(navRecord->get_range())); - cJSON_AddItemToObject(properties, "frequency", cJSON_CreateNumber((double) navRecord->get_freq() / 100.0)); - switch (navRecord->type()) { - case FGPositioned::ILS: - cJSON_AddItemToObject(properties, "localizer-course", cJSON_CreateNumber(navRecord->get_multiuse())); - break; - - case FGPositioned::VOR: - cJSON_AddItemToObject(properties, "variation", cJSON_CreateNumber(navRecord->get_multiuse())); - break; - - default: - break; - } - - } + addNAVProperties( properties, dynamic_cast(positioned.get()) ); + addAirportProperties( properties, dynamic_cast(positioned.get()) ); return properties; } @@ -156,6 +224,7 @@ bool NavdbUriHandler::handleRequest(const HTTPRequest & request, HTTPResponse & response.Header["Content-Type"] = "application/json; charset=UTF-8"; bool indent = request.RequestVariables.get("i") == "y"; + string query = request.RequestVariables.get("q"); FGPositionedList result; @@ -189,7 +258,7 @@ bool NavdbUriHandler::handleRequest(const HTTPRequest & request, HTTPResponse & goto fail; } - { // create some GeoJSON from the result list + { // create some GeoJSON from the result list // GeoJSON always consists of a single object. cJSON * geoJSON = cJSON_CreateObject(); @@ -197,6 +266,7 @@ bool NavdbUriHandler::handleRequest(const HTTPRequest & request, HTTPResponse & // This member's value is a string that determines the type of the GeoJSON object. cJSON_AddItemToObject(geoJSON, "type", cJSON_CreateString("FeatureCollection")); + // we send zero to many features - let's make it a FeatureCollection // A GeoJSON object with the type "FeatureCollection" is a feature collection object. // An object of type "FeatureCollection" must have a member with the name "features". // The value corresponding to "features" is an array.