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:
parent
2c3d779459
commit
e7dec994b8
3 changed files with 155 additions and 63 deletions
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Add table
Reference in a new issue