1
0
Fork 0

httpd: provide more airport information in geojson

- provide runways as polygon geometry
- provide longest runway lenght, heading and surface type
- some code cleanup
This commit is contained in:
Torsten Dreyer 2014-03-23 21:19:04 +01:00
parent 2c3d779459
commit e7dec994b8
3 changed files with 155 additions and 63 deletions

View file

@ -45,6 +45,22 @@ using std::string;
* 9 - dirt helipad * 9 - dirt helipad
* 12 - lakebed * 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, FGRunwayBase::FGRunwayBase(PositionedID aGuid, Type aTy, const string& aIdent,
const SGGeod& aGeod, const SGGeod& aGeod,

View file

@ -82,6 +82,12 @@ public:
*/ */
int surface() const int surface() const
{ return _surface_code; } { 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: protected:

View file

@ -21,6 +21,7 @@
#include "NavdbUriHandler.hxx" #include "NavdbUriHandler.hxx"
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <Navaids/navrecord.hxx> #include <Navaids/navrecord.hxx>
#include <Airports/airport.hxx>
#include <3rdparty/cjson/cJSON.h> #include <3rdparty/cjson/cJSON.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -29,9 +30,7 @@ using std::string;
namespace flightgear { namespace flightgear {
namespace http { namespace http {
//Ref: http://geojson.org/ static cJSON * createPositionArray(double x, double y)
static cJSON * createPosition(double x, double y)
{ {
cJSON * p = cJSON_CreateArray(); cJSON * p = cJSON_CreateArray();
cJSON_AddItemToArray(p, cJSON_CreateNumber(x)); cJSON_AddItemToArray(p, cJSON_CreateNumber(x));
@ -39,45 +38,92 @@ static cJSON * createPosition(double x, double y)
return p; 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(); cJSON * geometry = cJSON_CreateObject();
if (navRecord->type() == FGPositioned::LOC || navRecord->type() == FGPositioned::ILS) { // if there are runways, create a geometry collection
int range = navRecord->get_range(); cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("GeometryCollection"));
cJSON * geometryCollection = cJSON_CreateArray();
cJSON_AddItemToObject(geometry, "geometries", geometryCollection);
double width = navRecord->localizerWidth(); // the first item is the aerodrome reference point
double course = navRecord->get_multiuse(); cJSON_AddItemToArray( geometryCollection, createPointGeometry(airport) );
double px[4]; // followed by the runway polygons
double py[4]; for( FGRunwayList::iterator it = runways.begin(); it != runways.end(); ++it ) {
cJSON * polygon = cJSON_CreateObject();
px[0] = navRecord->longitude(); cJSON_AddItemToArray( geometryCollection, polygon );
py[0] = navRecord->latitude(); cJSON_AddItemToObject(polygon, "type", cJSON_CreateString("Polygon"));
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 * coordinates = cJSON_CreateArray();
cJSON_AddItemToObject(geometry, "coordinates", coordinates); cJSON_AddItemToObject(polygon, "coordinates", coordinates );
for (int i = 1; i < 4; i++) { cJSON * linearRing = cJSON_CreateArray();
cJSON * line = cJSON_CreateArray(); cJSON_AddItemToArray( coordinates, linearRing );
cJSON_AddItemToArray(coordinates, line);
cJSON_AddItemToArray(line, createPosition(px[0], py[0]));
cJSON_AddItemToArray(line, createPosition(px[i], py[i]));
}
} else { // compute the four corners of the runway
SGGeod p1 = (*it)->pointOffCenterline( 0.0, (*it)->widthM()/2 );
cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("Point")); SGGeod p2 = (*it)->pointOffCenterline( 0.0, -(*it)->widthM()/2 );
cJSON_AddItemToObject(geometry, "coordinates", createPosition(navRecord->longitude(), navRecord->latitude())); 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; return geometry;
@ -85,19 +131,57 @@ static cJSON * createGeometryFor(FGNavRecord * navRecord)
static cJSON * createGeometryFor(FGPositionedRef positioned) static cJSON * createGeometryFor(FGPositionedRef positioned)
{ {
cJSON * geometry = createGeometryFor(dynamic_cast<FGNavRecord*>(positioned.get())); switch( positioned->type() ) {
if ( NULL != geometry) return geometry; case FGPositioned::LOC:
case FGPositioned::ILS:
return createLOCGeometry( dynamic_cast<FGNavRecord*>(positioned.get()) );
geometry = cJSON_CreateObject(); case FGPositioned::AIRPORT:
cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("Point")); return createAirportGeometry( dynamic_cast<FGAirport*>(positioned.get()) );
cJSON * coordinates = cJSON_CreateArray(); default:
cJSON_AddItemToObject(geometry, "coordinates", coordinates); return createPointGeometry( positioned );
}
}
cJSON_AddItemToArray(coordinates, cJSON_CreateNumber(positioned->longitude())); static void addAirportProperties(cJSON * json, FGAirport * airport )
cJSON_AddItemToArray(coordinates, cJSON_CreateNumber(positioned->latitude())); {
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) 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, "name", cJSON_CreateString(positioned->name().c_str()));
cJSON_AddItemToObject(properties, "type", cJSON_CreateString(positioned->typeString())); cJSON_AddItemToObject(properties, "type", cJSON_CreateString(positioned->typeString()));
cJSON_AddItemToObject(properties, "elevation-m", cJSON_CreateNumber(positioned->elevationM())); cJSON_AddItemToObject(properties, "elevation-m", cJSON_CreateNumber(positioned->elevationM()));
FGNavRecord * navRecord = dynamic_cast<FGNavRecord*>(positioned.get()); addNAVProperties( properties, dynamic_cast<FGNavRecord*>(positioned.get()) );
if ( NULL != navRecord) { addAirportProperties( properties, dynamic_cast<FGAirport*>(positioned.get()) );
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;
}
}
return properties; return properties;
} }
@ -156,6 +224,7 @@ bool NavdbUriHandler::handleRequest(const HTTPRequest & request, HTTPResponse &
response.Header["Content-Type"] = "application/json; charset=UTF-8"; response.Header["Content-Type"] = "application/json; charset=UTF-8";
bool indent = request.RequestVariables.get("i") == "y"; bool indent = request.RequestVariables.get("i") == "y";
string query = request.RequestVariables.get("q"); string query = request.RequestVariables.get("q");
FGPositionedList result; FGPositionedList result;
@ -189,7 +258,7 @@ bool NavdbUriHandler::handleRequest(const HTTPRequest & request, HTTPResponse &
goto fail; goto fail;
} }
{ // create some GeoJSON from the result list { // create some GeoJSON from the result list
// GeoJSON always consists of a single object. // GeoJSON always consists of a single object.
cJSON * geoJSON = cJSON_CreateObject(); 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. // This member's value is a string that determines the type of the GeoJSON object.
cJSON_AddItemToObject(geoJSON, "type", cJSON_CreateString("FeatureCollection")); 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. // 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". // An object of type "FeatureCollection" must have a member with the name "features".
// The value corresponding to "features" is an array. // The value corresponding to "features" is an array.