Merge branch 'next' of git://gitorious.org/fg/simgear into next
This commit is contained in:
@@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.6.4)
|
||||
include (CheckFunctionExists)
|
||||
include (CheckIncludeFile)
|
||||
include (CheckCXXSourceCompiles)
|
||||
include (CPack)
|
||||
|
||||
|
||||
project(SimGear)
|
||||
|
||||
@@ -18,6 +18,7 @@ SET(CPACK_PACKAGE_VENDOR "The FlightGear project")
|
||||
SET(CPACK_GENERATOR "TBZ2")
|
||||
SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/)
|
||||
|
||||
|
||||
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
|
||||
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} )
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
@@ -26,6 +27,15 @@ set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
|
||||
message(STATUS "version is ${CPACK_PACKAGE_VERSION_MAJOR} dot ${CPACK_PACKAGE_VERSION_MINOR} dot ${CPACK_PACKAGE_VERSION_PATCH}")
|
||||
|
||||
set(CPACK_SOURCE_GENERATOR TBZ2)
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "tarball basename")
|
||||
set(CPACK_SOURCE_IGNORE_FILES
|
||||
"^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
include (CPack)
|
||||
|
||||
# We have some custom .cmake scripts not in the official distribution.
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}")
|
||||
|
||||
|
||||
@@ -5,3 +5,12 @@ set(HEADERS metar.hxx precipitation.hxx)
|
||||
set(SOURCES metar.cxx precipitation.cxx)
|
||||
|
||||
simgear_component(environment environment "${SOURCES}" "${HEADERS}")
|
||||
|
||||
add_executable(test_metar test_metar.cxx)
|
||||
target_link_libraries(test_metar
|
||||
sgenvironment sgstructure sgmisc sgdebug
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY})
|
||||
|
||||
add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <time.h>
|
||||
#include <cstring>
|
||||
|
||||
#include <simgear/io/sg_socket.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
@@ -40,32 +39,28 @@
|
||||
|
||||
#define NaN SGMetarNaN
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* The constructor takes a Metar string, or a four-letter ICAO code. In the
|
||||
* latter case the metar string is downloaded from
|
||||
* http://weather.noaa.gov/pub/data/observations/metar/stations/.
|
||||
* The constructor takes a Metar string
|
||||
* The constructor throws sg_io_exceptions on failure. The "METAR"
|
||||
* keyword has no effect (apart from incrementing the group counter
|
||||
* @a grpcount) and can be left away. A keyword "SPECI" is
|
||||
* likewise accepted.
|
||||
*
|
||||
* @param m ICAO station id or metar string
|
||||
* @param proxy proxy host (optional; default: "")
|
||||
* @param port proxy port (optional; default: "80")
|
||||
* @param auth proxy authorization information (optional; default: "")
|
||||
*
|
||||
* @par Examples:
|
||||
* @code
|
||||
* SGMetar *m = new SGMetar("METAR KSFO 061656Z 19004KT 9SM SCT100 OVC200 08/03 A3013");
|
||||
* double t = m->getTemperature_F();
|
||||
* delete m;
|
||||
*
|
||||
* SGMetar n("KSFO", "proxy.provider.foo", "3128", "proxy-password");
|
||||
* double d = n.getDewpoint_C();
|
||||
|
||||
* @endcode
|
||||
*/
|
||||
SGMetar::SGMetar(const string& m, const string& proxy, const string& port,
|
||||
const string& auth, const time_t time) :
|
||||
SGMetar::SGMetar(const string& m) :
|
||||
_grpcount(0),
|
||||
_x_proxy(false),
|
||||
_year(-1),
|
||||
@@ -87,16 +82,10 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port,
|
||||
_snow(false),
|
||||
_cavok(false)
|
||||
{
|
||||
if (m.length() == 4 && isalnum(m[0]) && isalnum(m[1]) && isalnum(m[2]) && isalnum(m[3])) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
_icao[i] = toupper(m[i]);
|
||||
_icao[4] = '\0';
|
||||
_data = loadData(_icao, proxy, port, auth, time);
|
||||
} else {
|
||||
_data = new char[m.length() + 2]; // make room for " \0"
|
||||
strcpy(_data, m.c_str());
|
||||
_url = _data;
|
||||
}
|
||||
_data = new char[m.length() + 2]; // make room for " \0"
|
||||
strcpy(_data, m.c_str());
|
||||
_url = _data;
|
||||
|
||||
normalizeData();
|
||||
|
||||
_m = _data;
|
||||
@@ -169,85 +158,6 @@ void SGMetar::useCurrentDate()
|
||||
_month = now.tm_mon + 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If called with "KSFO" loads data from
|
||||
* @code
|
||||
* http://weather.noaa.gov/pub/data/observations/metar/stations/KSFO.TXT.
|
||||
* @endcode
|
||||
* Throws sg_io_exception on failure. Gives up after waiting longer than 10 seconds.
|
||||
*
|
||||
* @param id four-letter ICAO Metar station code, e.g. "KSFO".
|
||||
* @param proxy proxy host (optional; default: "")
|
||||
* @param port proxy port (optional; default: "80")
|
||||
* @param auth proxy authorization information (optional; default: "")
|
||||
* @return pointer to Metar data string, allocated by new char[].
|
||||
* @see rfc2068.txt for proxy spec ("Proxy-Authorization")
|
||||
*/
|
||||
char *SGMetar::loadData(const char *id, const string& proxy, const string& port,
|
||||
const string& auth, time_t time)
|
||||
{
|
||||
const int buflen = 512;
|
||||
char buf[2 * buflen];
|
||||
|
||||
string metar_server = "weather.noaa.gov";
|
||||
string host = proxy.empty() ? metar_server : proxy;
|
||||
string path = "/pub/data/observations/metar/stations/";
|
||||
|
||||
path += string(id) + ".TXT";
|
||||
_url = "http://" + metar_server + path;
|
||||
|
||||
SGSocket *sock = new SGSocket(host, port.empty() ? "80" : port, "tcp");
|
||||
sock->set_timeout(10000);
|
||||
if (!sock->open(SG_IO_OUT)) {
|
||||
delete sock;
|
||||
throw sg_io_exception("cannot connect to ", sg_location(host));
|
||||
}
|
||||
|
||||
string get = "GET ";
|
||||
if (!proxy.empty())
|
||||
get += "http://" + metar_server;
|
||||
|
||||
sprintf(buf, "%ld", time);
|
||||
get += path + " HTTP/1.0\015\012X-Time: " + buf + "\015\012";
|
||||
get += "Host: " + metar_server + "\015\012";
|
||||
|
||||
if (!auth.empty())
|
||||
get += "Proxy-Authorization: " + auth + "\015\012";
|
||||
|
||||
get += "\015\012";
|
||||
sock->writestring(get.c_str());
|
||||
|
||||
int i;
|
||||
|
||||
// skip HTTP header
|
||||
while ((i = sock->readline(buf, buflen))) {
|
||||
if (i <= 2 && isspace(buf[0]) && (!buf[1] || isspace(buf[1])))
|
||||
break;
|
||||
if (!strncmp(buf, "X-MetarProxy: ", 13))
|
||||
_x_proxy = true;
|
||||
}
|
||||
if (i) {
|
||||
i = sock->readline(buf, buflen);
|
||||
if (i)
|
||||
sock->readline(&buf[i], buflen);
|
||||
}
|
||||
|
||||
sock->close();
|
||||
delete sock;
|
||||
|
||||
char *b = buf;
|
||||
scanBoundary(&b);
|
||||
if (*b == '<')
|
||||
throw sg_io_exception("no metar data available from ",
|
||||
sg_location(_url));
|
||||
|
||||
char *metar = new char[strlen(b) + 2]; // make room for " \0"
|
||||
strcpy(metar, b);
|
||||
return metar;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace any number of subsequent spaces by just one space, and add
|
||||
* a trailing space. This makes scanning for things like "ALL RWY" easier.
|
||||
|
||||
@@ -29,18 +29,12 @@
|
||||
|
||||
#include <simgear/constants.h>
|
||||
|
||||
using std::vector;
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
const double SGMetarNaN = -1E20;
|
||||
#define NaN SGMetarNaN
|
||||
|
||||
struct Token {
|
||||
const char *id;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
const double SGMetarNaN = -1E20;
|
||||
|
||||
class SGMetar;
|
||||
|
||||
@@ -48,7 +42,7 @@ class SGMetarVisibility {
|
||||
friend class SGMetar;
|
||||
public:
|
||||
SGMetarVisibility() :
|
||||
_distance(NaN),
|
||||
_distance(SGMetarNaN),
|
||||
_direction(-1),
|
||||
_modifier(EQUALS),
|
||||
_tendency(NONE) {}
|
||||
@@ -70,8 +64,8 @@ public:
|
||||
void set(double dist, int dir = -1, int mod = -1, int tend = -1);
|
||||
|
||||
inline double getVisibility_m() const { return _distance; }
|
||||
inline double getVisibility_ft() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_FEET; }
|
||||
inline double getVisibility_sm() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_SM; }
|
||||
inline double getVisibility_ft() const { return _distance == SGMetarNaN ? SGMetarNaN : _distance * SG_METER_TO_FEET; }
|
||||
inline double getVisibility_sm() const { return _distance == SGMetarNaN ? SGMetarNaN : _distance * SG_METER_TO_SM; }
|
||||
inline int getDirection() const { return _direction; }
|
||||
inline int getModifier() const { return _modifier; }
|
||||
inline int getTendency() const { return _tendency; }
|
||||
@@ -93,8 +87,8 @@ public:
|
||||
_deposit_string(0),
|
||||
_extent(-1),
|
||||
_extent_string(0),
|
||||
_depth(NaN),
|
||||
_friction(NaN),
|
||||
_depth(SGMetarNaN),
|
||||
_friction(SGMetarNaN),
|
||||
_friction_string(0),
|
||||
_comment(0),
|
||||
_wind_shear(false) {}
|
||||
@@ -146,14 +140,14 @@ public:
|
||||
static const char * COVERAGE_BROKEN_STRING;
|
||||
static const char * COVERAGE_OVERCAST_STRING;
|
||||
|
||||
SGMetarCloud() : _coverage(COVERAGE_NIL), _altitude(NaN), _type(0), _type_long(0) {}
|
||||
SGMetarCloud() : _coverage(COVERAGE_NIL), _altitude(SGMetarNaN), _type(0), _type_long(0) {}
|
||||
|
||||
void set(double alt, Coverage cov = COVERAGE_NIL );
|
||||
|
||||
inline Coverage getCoverage() const { return _coverage; }
|
||||
static Coverage getCoverage( const std::string & coverage );
|
||||
inline double getAltitude_m() const { return _altitude; }
|
||||
inline double getAltitude_ft() const { return _altitude == NaN ? NaN : _altitude * SG_METER_TO_FEET; }
|
||||
inline double getAltitude_ft() const { return _altitude == SGMetarNaN ? SGMetarNaN : _altitude * SG_METER_TO_FEET; }
|
||||
inline const char *getTypeString() const { return _type; }
|
||||
inline const char *getTypeLongString() const { return _type_long; }
|
||||
|
||||
@@ -167,8 +161,7 @@ protected:
|
||||
|
||||
class SGMetar {
|
||||
public:
|
||||
SGMetar(const string& m, const string& proxy = "", const string& port = "",
|
||||
const string &auth = "", const time_t time = 0);
|
||||
SGMetar(const std::string& m);
|
||||
~SGMetar();
|
||||
|
||||
enum ReportType {
|
||||
@@ -189,8 +182,8 @@ public:
|
||||
Weather() { intensity = NIL; vincinity = false; }
|
||||
Intensity intensity;
|
||||
bool vincinity;
|
||||
vector<string> descriptions;
|
||||
vector<string> phenomena;
|
||||
std::vector<std::string> descriptions;
|
||||
std::vector<std::string> phenomena;
|
||||
};
|
||||
|
||||
inline const char *getData() const { return _data; }
|
||||
@@ -206,14 +199,14 @@ public:
|
||||
|
||||
inline int getWindDir() const { return _wind_dir; }
|
||||
inline double getWindSpeed_mps() const { return _wind_speed; }
|
||||
inline double getWindSpeed_kmh() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_KMH; }
|
||||
inline double getWindSpeed_kt() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_KT; }
|
||||
inline double getWindSpeed_mph() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_MPH; }
|
||||
inline double getWindSpeed_kmh() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_KMH; }
|
||||
inline double getWindSpeed_kt() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_KT; }
|
||||
inline double getWindSpeed_mph() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_MPH; }
|
||||
|
||||
inline double getGustSpeed_mps() const { return _gust_speed; }
|
||||
inline double getGustSpeed_kmh() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_KMH; }
|
||||
inline double getGustSpeed_kt() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_KT; }
|
||||
inline double getGustSpeed_mph() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_MPH; }
|
||||
inline double getGustSpeed_kmh() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_KMH; }
|
||||
inline double getGustSpeed_kt() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_KT; }
|
||||
inline double getGustSpeed_mph() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_MPH; }
|
||||
|
||||
inline int getWindRangeFrom() const { return _wind_range_from; }
|
||||
inline int getWindRangeTo() const { return _wind_range_to; }
|
||||
@@ -224,11 +217,11 @@ public:
|
||||
inline const SGMetarVisibility *getDirVisibility() const { return _dir_visibility; }
|
||||
|
||||
inline double getTemperature_C() const { return _temp; }
|
||||
inline double getTemperature_F() const { return _temp == NaN ? NaN : 1.8 * _temp + 32; }
|
||||
inline double getTemperature_F() const { return _temp == SGMetarNaN ? SGMetarNaN : 1.8 * _temp + 32; }
|
||||
inline double getDewpoint_C() const { return _dewp; }
|
||||
inline double getDewpoint_F() const { return _dewp == NaN ? NaN : 1.8 * _dewp + 32; }
|
||||
inline double getPressure_hPa() const { return _pressure == NaN ? NaN : _pressure / 100; }
|
||||
inline double getPressure_inHg() const { return _pressure == NaN ? NaN : _pressure * SG_PA_TO_INHG; }
|
||||
inline double getDewpoint_F() const { return _dewp == SGMetarNaN ? SGMetarNaN : 1.8 * _dewp + 32; }
|
||||
inline double getPressure_hPa() const { return _pressure == SGMetarNaN ? SGMetarNaN : _pressure / 100; }
|
||||
inline double getPressure_inHg() const { return _pressure == SGMetarNaN ? SGMetarNaN : _pressure * SG_PA_TO_INHG; }
|
||||
|
||||
inline int getRain() const { return _rain; }
|
||||
inline int getHail() const { return _hail; }
|
||||
@@ -237,13 +230,13 @@ public:
|
||||
|
||||
double getRelHumidity() const;
|
||||
|
||||
inline const vector<SGMetarCloud>& getClouds() const { return _clouds; }
|
||||
inline const map<string, SGMetarRunway>& getRunways() const { return _runways; }
|
||||
inline const vector<string>& getWeather() const { return _weather; }
|
||||
inline const vector<struct Weather> getWeather2() const { return _weather2; }
|
||||
inline const std::vector<SGMetarCloud>& getClouds() const { return _clouds; }
|
||||
inline const std::map<std::string, SGMetarRunway>& getRunways() const { return _runways; }
|
||||
inline const std::vector<std::string>& getWeather() const { return _weather; }
|
||||
inline const std::vector<struct Weather> getWeather2() const { return _weather2; }
|
||||
|
||||
protected:
|
||||
string _url;
|
||||
std::string _url;
|
||||
int _grpcount;
|
||||
bool _x_proxy;
|
||||
char *_data;
|
||||
@@ -267,15 +260,15 @@ protected:
|
||||
int _hail;
|
||||
int _snow;
|
||||
bool _cavok;
|
||||
vector<struct Weather> _weather2;
|
||||
std::vector<struct Weather> _weather2;
|
||||
|
||||
SGMetarVisibility _min_visibility;
|
||||
SGMetarVisibility _max_visibility;
|
||||
SGMetarVisibility _vert_visibility;
|
||||
SGMetarVisibility _dir_visibility[8];
|
||||
vector<SGMetarCloud> _clouds;
|
||||
map<string, SGMetarRunway> _runways;
|
||||
vector<string> _weather;
|
||||
std::vector<SGMetarCloud> _clouds;
|
||||
std::map<std::string, SGMetarRunway> _runways;
|
||||
std::vector<std::string> _weather;
|
||||
|
||||
bool scanPreambleDate();
|
||||
bool scanPreambleTime();
|
||||
@@ -303,10 +296,7 @@ protected:
|
||||
int scanNumber(char **str, int *num, int min, int max = 0);
|
||||
bool scanBoundary(char **str);
|
||||
const struct Token *scanToken(char **str, const struct Token *list);
|
||||
char *loadData(const char *id, const string& proxy, const string& port,
|
||||
const string &auth, time_t time);
|
||||
void normalizeData();
|
||||
};
|
||||
|
||||
#undef NaN
|
||||
#endif // _METAR_HXX
|
||||
|
||||
76
simgear/environment/test_metar.cxx
Normal file
76
simgear/environment/test_metar.cxx
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define random rand
|
||||
#endif
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include "metar.hxx"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:" << a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define FUZZY_COMPARE(a, b, epsilon) \
|
||||
if (fabs(a - b) > epsilon) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:" << a << endl; \
|
||||
cerr << "\tepsilon:" << epsilon << endl; \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
const double TEST_EPSILON = 1e-9;
|
||||
|
||||
void test_basic()
|
||||
{
|
||||
SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 VCSH FEW025CB SCT048 10/05 Q1025 TEMPO VRB03KT");
|
||||
COMPARE(m1.getYear(), 2011);
|
||||
COMPARE(m1.getMonth(), 10);
|
||||
COMPARE(m1.getDay(), 20);
|
||||
COMPARE(m1.getHour(), 11);
|
||||
COMPARE(m1.getMinute(), 25);
|
||||
COMPARE(m1.getReportType(), -1); // should default to NIL?
|
||||
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try {
|
||||
test_basic();
|
||||
} catch (sg_exception& e) {
|
||||
cerr << "got exception:" << e.getMessage() << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -47,22 +47,37 @@ using std::string;
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
Dir::Dir()
|
||||
Dir::Dir() :
|
||||
_removeOnDestroy(false)
|
||||
{
|
||||
}
|
||||
|
||||
Dir::Dir(const SGPath& path) :
|
||||
_path(path)
|
||||
_path(path),
|
||||
_removeOnDestroy(false)
|
||||
{
|
||||
_path.set_cached(false); // disable caching, so create/remove work
|
||||
}
|
||||
|
||||
Dir::Dir(const Dir& rel, const SGPath& relPath) :
|
||||
_path(rel.file(relPath.str()))
|
||||
_path(rel.file(relPath.str())),
|
||||
_removeOnDestroy(false)
|
||||
{
|
||||
_path.set_cached(false); // disable caching, so create/remove work
|
||||
}
|
||||
|
||||
Dir::~Dir()
|
||||
{
|
||||
if (_removeOnDestroy) {
|
||||
remove(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Dir::setRemoveOnDestroy()
|
||||
{
|
||||
_removeOnDestroy = true;
|
||||
}
|
||||
|
||||
Dir Dir::current()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -79,7 +94,7 @@ Dir Dir::tempDir(const std::string& templ)
|
||||
{
|
||||
#ifdef HAVE_MKDTEMP
|
||||
char buf[1024];
|
||||
char* tempPath = ::getenv("TMPDIR");
|
||||
const char* tempPath = ::getenv("TMPDIR");
|
||||
if (!tempPath) {
|
||||
tempPath = "/tmp/";
|
||||
}
|
||||
|
||||
@@ -43,7 +43,15 @@ namespace simgear
|
||||
{
|
||||
public:
|
||||
Dir();
|
||||
|
||||
~Dir();
|
||||
|
||||
/**
|
||||
* when this directory object is destroyed, remove the corresponding
|
||||
* diretory (and its contents) from the disk. Often used with temporary
|
||||
* directories to ensure they are cleaned up.
|
||||
*/
|
||||
void setRemoveOnDestroy();
|
||||
|
||||
static Dir current();
|
||||
|
||||
/**
|
||||
@@ -93,6 +101,7 @@ namespace simgear
|
||||
Dir parent() const;
|
||||
private:
|
||||
mutable SGPath _path;
|
||||
bool _removeOnDestroy;
|
||||
};
|
||||
} // of namespace simgear
|
||||
|
||||
|
||||
@@ -471,3 +471,16 @@ bool SGPath::operator!=(const SGPath& other) const
|
||||
return (path != other.path);
|
||||
}
|
||||
|
||||
bool SGPath::rename(const SGPath& newName)
|
||||
{
|
||||
if (::rename(c_str(), newName.c_str()) != 0) {
|
||||
SG_LOG(SG_IO, SG_WARN, "renamed failed: from " << str() << " to " << newName.str()
|
||||
<< " reason: " << strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
path = newName.path;
|
||||
_cached = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +213,13 @@ public:
|
||||
* modification time of the file
|
||||
*/
|
||||
time_t modTime() const;
|
||||
|
||||
/**
|
||||
* rename the file / directory we point at, to a new name
|
||||
* this may fail if the new location is on a different volume / share,
|
||||
* or if the destination already exists, or is not writeable
|
||||
*/
|
||||
bool rename(const SGPath& newName);
|
||||
private:
|
||||
|
||||
void fix();
|
||||
|
||||
@@ -278,6 +278,19 @@ namespace simgear {
|
||||
return v1parts.size() - v2parts.size();
|
||||
}
|
||||
|
||||
string join(const string_list& l, const string& joinWith)
|
||||
{
|
||||
string result;
|
||||
unsigned int count = l.size();
|
||||
for (unsigned int i=0; i < count; ++i) {
|
||||
result += l[i];
|
||||
if (i < (count - 1)) {
|
||||
result += joinWith;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // end namespace strutils
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
|
||||
typedef std::vector < std::string > string_list;
|
||||
|
||||
namespace simgear {
|
||||
namespace strutils {
|
||||
@@ -93,11 +94,17 @@ namespace simgear {
|
||||
* resulting in at most maxsplit+1 words.
|
||||
* @return Array of words.
|
||||
*/
|
||||
std::vector<std::string>
|
||||
string_list
|
||||
split( const std::string& s,
|
||||
const char* sep = 0,
|
||||
int maxsplit = 0 );
|
||||
|
||||
/**
|
||||
* create a single string by joining the elements of a list with
|
||||
* another string.
|
||||
*/
|
||||
std::string join(const string_list& l, const std::string& joinWith = "");
|
||||
|
||||
/**
|
||||
* Test if a string starts with a string
|
||||
*
|
||||
|
||||
@@ -63,6 +63,20 @@ int main (int ac, char ** av)
|
||||
// since we compare numerically, leasing zeros shouldn't matter
|
||||
VERIFY(compare_versions("0.06.7", "0.6.07") == 0);
|
||||
|
||||
string_list la = split("zero one two three four five");
|
||||
COMPARE(la[2], "two");
|
||||
COMPARE(la[5], "five");
|
||||
COMPARE(la.size(), 6);
|
||||
|
||||
string_list lb = split("alpha:beta:gamma:delta", ":", 2);
|
||||
COMPARE(lb.size(), 3);
|
||||
COMPARE(lb[0], "alpha");
|
||||
COMPARE(lb[1], "beta");
|
||||
COMPARE(lb[2], "gamma:delta");
|
||||
|
||||
std::string j = join(la, "&");
|
||||
COMPARE(j, "zero&one&two&three&four&five");
|
||||
|
||||
cout << "all tests passed successfully!" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ float SGCloudField::fieldSize = 50000.0f;
|
||||
double SGCloudField::timer_dt = 0.0;
|
||||
float SGCloudField::view_distance = 20000.0f;
|
||||
bool SGCloudField::wrap = true;
|
||||
float SGCloudField::RADIUS_LEVEL_1 = 20000.0f;
|
||||
float SGCloudField::RADIUS_LEVEL_2 = 5000.0f;
|
||||
float SGCloudField::RADIUS_LEVEL_1 = 5000.0f;
|
||||
float SGCloudField::RADIUS_LEVEL_2 = 1000.0f;
|
||||
|
||||
SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
|
||||
|
||||
@@ -81,7 +81,9 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
|
||||
if (placed_root->getNumChildren() == 0) return false;
|
||||
|
||||
SGVec3<double> cart;
|
||||
SGGeodesy::SGGeodToCart(SGGeod::fromRadFt(lon, lat, 0.0f), cart);
|
||||
SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f);
|
||||
|
||||
SGGeodesy::SGGeodToCart(new_pos, cart);
|
||||
osg::Vec3f osg_pos = toOsg(cart);
|
||||
osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
|
||||
|
||||
@@ -93,8 +95,6 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
|
||||
osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
|
||||
sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
|
||||
0.0f);
|
||||
//cout << "Wind: " << direction << "@" << speed << "\n";
|
||||
|
||||
osg::Vec3f windosg = field_transform->getAttitude() * wind;
|
||||
field_transform->setPosition(field_transform->getPosition() + windosg);
|
||||
|
||||
@@ -108,39 +108,40 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
|
||||
field_transform->setPosition(osg_pos);
|
||||
field_transform->setAttitude(orient);
|
||||
old_pos = osg_pos;
|
||||
} else {
|
||||
} else {
|
||||
osg::Quat fta = field_transform->getAttitude();
|
||||
osg::Quat ftainv = field_transform->getAttitude().inverse();
|
||||
|
||||
// delta is the vector from the old position to the new position in cloud-coords
|
||||
osg::Vec3f delta = field_transform->getAttitude().inverse() * (osg_pos - old_pos);
|
||||
//cout << "Delta: " << delta.length() << "\n";
|
||||
|
||||
for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) {
|
||||
osg::ref_ptr<osg::LOD> lodnode1 = (osg::LOD*) placed_root->getChild(i);
|
||||
osg::Vec3f v = delta - lodnode1->getCenter();
|
||||
|
||||
if ((v.x() < -0.5*fieldSize) ||
|
||||
(v.x() > 0.5*fieldSize) ||
|
||||
(v.y() < -0.5*fieldSize) ||
|
||||
(v.y() > 0.5*fieldSize) ) {
|
||||
//cout << "V: " << v.x() << ", " << v.y() << "\n";
|
||||
|
||||
osg::Vec3f shift = osg::Vec3f(0.0f, 0.0f, 0.0f);
|
||||
if (v.x() > 0.5*fieldSize) { shift += osg::Vec3f(fieldSize, 0.0f, 0.0f); }
|
||||
if (v.x() < -0.5*fieldSize) { shift -= osg::Vec3f(fieldSize, 0.0f, 0.0f); }
|
||||
|
||||
if (v.y() > 0.5*fieldSize) { shift += osg::Vec3f(0.0f, fieldSize, 0.0f); }
|
||||
if (v.y() < -0.5*fieldSize) { shift -= osg::Vec3f(0.0f, fieldSize, 0.0f); }
|
||||
|
||||
//cout << "Shift: " << shift.x() << ", " << shift.y() << "\n\n";
|
||||
|
||||
for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) {
|
||||
osg::ref_ptr<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
|
||||
for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> pat =(osg::PositionAttitudeTransform*) lodnode2->getChild(k);
|
||||
pat->setPosition(pat->getPosition() + shift);
|
||||
}
|
||||
}
|
||||
osg::Vec3f delta = ftainv * (osg_pos - old_pos);
|
||||
old_pos = osg_pos;
|
||||
|
||||
// Check if any of the clouds should be moved.
|
||||
for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
|
||||
itr != end;
|
||||
++itr) {
|
||||
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> pat = itr->second;
|
||||
osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition();
|
||||
|
||||
// Determine the vector from the new position to the cloud in cloud-space.
|
||||
osg::Vec3f w = ftainv * (currpos - toOsg(cart));
|
||||
|
||||
// Determine a course if required. Note that this involves some axis translation.
|
||||
float x = 0.0;
|
||||
float y = 0.0;
|
||||
if (w.x() > 0.6*fieldSize) { y = fieldSize; }
|
||||
if (w.x() < -0.6*fieldSize) { y = -fieldSize; }
|
||||
if (w.y() > 0.6*fieldSize) { x = -fieldSize; }
|
||||
if (w.y() < -0.6*fieldSize) { x = fieldSize; }
|
||||
|
||||
if ((x != 0.0) || (y != 0.0)) {
|
||||
removeCloudFromTree(pat);
|
||||
SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() +
|
||||
fta * pat->getPosition()));
|
||||
addCloudToTree(pat, p, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the clouds in order from farthest away layer to nearest one.
|
||||
@@ -231,28 +232,32 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
||||
float lon, float lat, float alt, float x, float y) {
|
||||
|
||||
// Get the base position
|
||||
SGGeod loc = SGGeod::fromDegFt(lon, lat, 0.0);
|
||||
|
||||
SGGeod loc = SGGeod::fromDegFt(lon, lat, alt);
|
||||
addCloudToTree(transform, loc, x, y);
|
||||
}
|
||||
|
||||
|
||||
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
|
||||
SGGeod loc, float x, float y) {
|
||||
|
||||
float alt = loc.getElevationFt();
|
||||
// Determine any shift by x/y
|
||||
if ((x != 0.0f) || (y != 0.0f)) {
|
||||
double crs;
|
||||
|
||||
if (y == 0.0f) {
|
||||
crs = (x < 0.0f) ? -90.0 : 90.0;
|
||||
} else {
|
||||
crs = SG_RADIANS_TO_DEGREES * tan(x/y);
|
||||
}
|
||||
|
||||
double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x);
|
||||
double dst = sqrt(x*x + y*y);
|
||||
double endcrs;
|
||||
|
||||
SGGeod base_pos = SGGeod::fromDegFt(lon, lat, 0.0f);
|
||||
SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);
|
||||
SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
|
||||
}
|
||||
|
||||
// The direct call provides the position at 0 alt, so adjust as required.
|
||||
loc.setElevationFt(alt);
|
||||
// The direct call provides the position at 0 alt, so adjust as required.
|
||||
loc.setElevationFt(alt);
|
||||
addCloudToTree(transform, loc);
|
||||
}
|
||||
|
||||
|
||||
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {
|
||||
// Work out where this cloud should go in OSG coordinates.
|
||||
SGVec3<double> cart;
|
||||
SGGeodesy::SGGeodToCart(loc, cart);
|
||||
@@ -266,16 +271,17 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
||||
// Convert to the scenegraph orientation where we just rotate around
|
||||
// the y axis 180 degrees.
|
||||
osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
|
||||
|
||||
field_transform->setPosition(toOsg(fieldcenter));
|
||||
|
||||
osg::Vec3f osg_pos = toOsg(fieldcenter);
|
||||
|
||||
field_transform->setPosition(osg_pos);
|
||||
field_transform->setAttitude(orient);
|
||||
old_pos = toOsg(fieldcenter);
|
||||
old_pos = osg_pos;
|
||||
}
|
||||
|
||||
|
||||
// Convert position to cloud-coordinates
|
||||
pos = pos - field_transform->getPosition();
|
||||
|
||||
//pos = orient.inverse() * pos;
|
||||
pos = field_transform->getAttitude().inverse() * pos;
|
||||
pos = field_transform->getAttitude().inverse() * pos;
|
||||
|
||||
// We have a two level dynamic quad tree which the cloud will be added
|
||||
// to. If there are no appropriate nodes in the quad tree, they are
|
||||
@@ -288,7 +294,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
||||
lodnode1 = (osg::LOD*) placed_root->getChild(i);
|
||||
if ((lodnode1->getCenter() - pos).length2() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) {
|
||||
// New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
|
||||
//cout << "Adding cloud to existing LoD level 1 node. Distance:" << (lodnode1->getCenter() - pos).length() << "\n";
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
@@ -296,7 +301,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
||||
if (!found) {
|
||||
lodnode1 = new osg::LOD();
|
||||
placed_root->addChild(lodnode1.get());
|
||||
//cout << "Adding cloud to new LoD node\n";
|
||||
}
|
||||
|
||||
// Now check if there is a second level LOD node at an appropriate distance
|
||||
@@ -306,17 +310,15 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
||||
lodnode = (osg::LOD*) lodnode1->getChild(j);
|
||||
if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) {
|
||||
// We've found the right leaf LOD node
|
||||
//cout << "Found existing LOD leaf node. Distance:"<< (lodnode->getCenter() - pos).length() << "\n";
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// No suitable leave node was found, so we need to add one.
|
||||
lodnode = new osg::LOD();
|
||||
lodnode1->addChild(lodnode, 0.0f, 4*RADIUS_LEVEL_1);
|
||||
//cout << "Adding cloud to new LoD node\n";
|
||||
}
|
||||
}
|
||||
|
||||
transform->setPosition(pos);
|
||||
lodnode->addChild(transform.get(), 0.0f, view_distance);
|
||||
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
bool visible;
|
||||
};
|
||||
|
||||
float Rnd(float);
|
||||
float Rnd(float);
|
||||
|
||||
// Radius of the LoD nodes for the dynamic quadtrees.
|
||||
static float RADIUS_LEVEL_1;
|
||||
@@ -79,23 +79,25 @@ private:
|
||||
// this is a relative position only, with that we can move all clouds at once
|
||||
SGVec3f relative_position;
|
||||
|
||||
osg::ref_ptr<osg::Group> field_root;
|
||||
osg::ref_ptr<osg::Group> field_root;
|
||||
osg::ref_ptr<osg::Group> placed_root;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> field_transform;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> altitude_transform;
|
||||
osg::ref_ptr<osg::LOD> field_lod;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> altitude_transform;
|
||||
osg::ref_ptr<osg::LOD> field_lod;
|
||||
|
||||
osg::Vec3f old_pos;
|
||||
CloudHash cloud_hash;
|
||||
|
||||
struct CloudFog : public simgear::Singleton<CloudFog>
|
||||
{
|
||||
CloudFog();
|
||||
osg::ref_ptr<osg::Fog> fog;
|
||||
};
|
||||
struct CloudFog : public simgear::Singleton<CloudFog>
|
||||
{
|
||||
CloudFog();
|
||||
osg::ref_ptr<osg::Fog> fog;
|
||||
};
|
||||
|
||||
void removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform);
|
||||
void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, float lon, float lat, float alt, float x, float y);
|
||||
void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc, float x, float y);
|
||||
void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc);
|
||||
void applyVisRangeAndCoverage(void);
|
||||
|
||||
public:
|
||||
@@ -122,44 +124,44 @@ public:
|
||||
|
||||
|
||||
/**
|
||||
* reposition the cloud layer at the specified origin and
|
||||
* orientation.
|
||||
* @param p position vector
|
||||
* @param up the local up vector
|
||||
* @param lon specifies a rotation about the Z axis
|
||||
* @param lat specifies a rotation about the new Y axis
|
||||
* @param dt the time elapsed since the last call
|
||||
* @param asl altitude of the layer
|
||||
* @param speed of cloud layer movement (due to wind)
|
||||
* @param direction of cloud layer movement (due to wind)
|
||||
*/
|
||||
bool reposition( const SGVec3f& p, const SGVec3f& up,
|
||||
double lon, double lat, double dt, int asl, float speed, float direction);
|
||||
* reposition the cloud layer at the specified origin and
|
||||
* orientation.
|
||||
* @param p position vector
|
||||
* @param up the local up vector
|
||||
* @param lon specifies a rotation about the Z axis
|
||||
* @param lat specifies a rotation about the new Y axis
|
||||
* @param dt the time elapsed since the last call
|
||||
* @param asl altitude of the layer
|
||||
* @param speed of cloud layer movement (due to wind)
|
||||
* @param direction of cloud layer movement (due to wind)
|
||||
*/
|
||||
bool reposition( const SGVec3f& p, const SGVec3f& up,
|
||||
double lon, double lat, double dt, int asl, float speed, float direction);
|
||||
|
||||
osg::Group* getNode() { return field_root.get(); }
|
||||
osg::Group* getNode() { return field_root.get(); }
|
||||
|
||||
// visibility distance for clouds in meters
|
||||
static float CloudVis;
|
||||
// visibility distance for clouds in meters
|
||||
static float CloudVis;
|
||||
|
||||
static SGVec3f view_vec, view_X, view_Y;
|
||||
static SGVec3f view_vec, view_X, view_Y;
|
||||
|
||||
static float view_distance;
|
||||
static double timer_dt;
|
||||
static float fieldSize;
|
||||
static bool wrap;
|
||||
|
||||
static bool getWrap(void) { return wrap; }
|
||||
static void setWrap(bool w) { wrap = w; }
|
||||
static float view_distance;
|
||||
static double timer_dt;
|
||||
static float fieldSize;
|
||||
static bool wrap;
|
||||
|
||||
static float getVisRange(void) { return view_distance; }
|
||||
static void setVisRange(float d) { view_distance = d; }
|
||||
void applyVisRange(void);
|
||||
|
||||
static osg::Fog* getFog()
|
||||
{
|
||||
return CloudFog::instance()->fog.get();
|
||||
}
|
||||
static void updateFog(double visibility, const osg::Vec4f& color);
|
||||
static bool getWrap(void) { return wrap; }
|
||||
static void setWrap(bool w) { wrap = w; }
|
||||
|
||||
static float getVisRange(void) { return view_distance; }
|
||||
static void setVisRange(float d) { view_distance = d; }
|
||||
void applyVisRange(void);
|
||||
|
||||
static osg::Fog* getFog()
|
||||
{
|
||||
return CloudFog::instance()->fog.get();
|
||||
}
|
||||
static void updateFog(double visibility, const osg::Vec4f& color);
|
||||
};
|
||||
|
||||
#endif // _CLOUDFIELD_HXX
|
||||
|
||||
@@ -135,11 +135,12 @@ osg::Geometry* SGNewCloud::createOrthQuad(float w, float h, int varieties_x, int
|
||||
osg::Vec2Array& t = *(new osg::Vec2Array(4));
|
||||
|
||||
float cw = w*0.5f;
|
||||
|
||||
v[0].set(0.0f, -cw, 0.0f);
|
||||
v[1].set(0.0f, cw, 0.0f);
|
||||
v[2].set(0.0f, cw, h);
|
||||
v[3].set(0.0f, -cw, h);
|
||||
float ch = h*0.5f;
|
||||
|
||||
v[0].set(0.0f, -cw, -ch);
|
||||
v[1].set(0.0f, cw, -ch);
|
||||
v[2].set(0.0f, cw, ch);
|
||||
v[3].set(0.0f, -cw, ch);
|
||||
|
||||
// The texture coordinate range is not the
|
||||
// entire coordinate space - as the texture
|
||||
|
||||
Reference in New Issue
Block a user