canvas_mgr.cxx
elements/element.cxx
elements/group.cxx
+ elements/map.cxx
elements/path.cxx
elements/text.cxx
property_helper.cxx
canvas_mgr.hxx
elements/element.hxx
elements/group.hxx
+ elements/map.hxx
elements/path.hxx
elements/text.hxx
property_helper.hxx
type = TT_ROTATE;
else if( name == "s" )
type = TT_SCALE;
- else
- SG_LOG
- (
- SG_GL,
- SG_WARN,
- "Unknown transform element " << child->getPath()
- );
_transform_dirty = true;
}
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "group.hxx"
+#include "map.hxx"
#include "path.hxx"
#include "text.hxx"
element.reset( new Text(child) );
else if( child->getNameString() == "group" )
element.reset( new Group(child) );
+ else if( child->getNameString() == "map" )
+ element.reset( new Map(child) );
else if( child->getNameString() == "path" )
element.reset( new Path(child) );
{
if( node->getNameString() == "text"
|| node->getNameString() == "group"
- || node->getNameString() == "path")
+ || node->getNameString() == "map"
+ || node->getNameString() == "path" )
{
ChildMap::iterator child = _children.find(node);
--- /dev/null
+// A group of 2D canvas elements which get automatically transformed according
+// to the map parameters.
+//
+// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+#include "map.hxx"
+#include "map/geo_node_pair.hxx"
+#include "map/projection.hxx"
+
+#include <Main/fg_props.hxx>
+#include <cmath>
+
+#define LOG_GEO_RET(msg) \
+ {\
+ SG_LOG\
+ (\
+ SG_GENERAL,\
+ SG_WARN,\
+ msg << " (" << child->getStringValue()\
+ << ", " << child->getPath() << ")"\
+ );\
+ return;\
+ }
+
+namespace canvas
+{
+
+ // TODO make projection configurable
+ SansonFlamsteedProjection projection;
+ const std::string GEO = "-geo";
+
+ //----------------------------------------------------------------------------
+ Map::Map(SGPropertyNode_ptr node):
+ Group(node),
+ _projection_dirty(true)
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ Map::~Map()
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ void Map::update(double dt)
+ {
+ for( GeoNodes::iterator it = _geo_nodes.begin();
+ it != _geo_nodes.end();
+ ++it )
+ {
+ GeoNodePair* geo_node = it->second.get();
+ if( !geo_node->isComplete()
+ || (!geo_node->isDirty() && !_projection_dirty) )
+ continue;
+
+ GeoCoord lat = parseGeoCoord(geo_node->getLat());
+ if( lat.type != GeoCoord::LATITUDE )
+ continue;
+
+ GeoCoord lon = parseGeoCoord(geo_node->getLon());
+ if( lon.type != GeoCoord::LONGITUDE )
+ continue;
+
+ Projection::ScreenPosition pos =
+ projection.worldToScreen(lat.value, lon.value);
+
+ geo_node->setScreenPos(pos.x, pos.y);
+
+// geo_node->print();
+ geo_node->setDirty(false);
+ }
+ _projection_dirty = false;
+
+ Group::update(dt);
+ }
+
+ //----------------------------------------------------------------------------
+ void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
+ {
+ if( !hasSuffix(child->getNameString(), GEO) )
+ return Element::childAdded(parent, child);
+
+ _geo_nodes[child].reset(new GeoNodePair());
+ }
+
+ //----------------------------------------------------------------------------
+ void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
+ {
+ if( !hasSuffix(child->getNameString(), GEO) )
+ return Element::childRemoved(parent, child);
+
+ // TODO remove from other node
+ _geo_nodes.erase(child);
+ }
+
+ //----------------------------------------------------------------------------
+ void Map::valueChanged(SGPropertyNode * child)
+ {
+ const std::string& name = child->getNameString();
+
+ if( !hasSuffix(name, GEO) )
+ return Group::valueChanged(child);
+
+ GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
+ if( it_geo_node == _geo_nodes.end() )
+ LOG_GEO_RET("geo node not found!")
+ GeoNodePair* geo_node = it_geo_node->second.get();
+
+ geo_node->setDirty();
+
+ if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
+ {
+ // Detect lat, lon tuples...
+ GeoCoord coord = parseGeoCoord(child->getStringValue());
+ int index_other = -1;
+
+ switch( coord.type )
+ {
+ case GeoCoord::LATITUDE:
+ index_other = child->getIndex() + 1;
+ geo_node->setNodeLat(child);
+ break;
+ case GeoCoord::LONGITUDE:
+ index_other = child->getIndex() - 1;
+ geo_node->setNodeLon(child);
+ break;
+ default:
+ LOG_GEO_RET("Invalid geo coord")
+ }
+
+ SGPropertyNode *other = child->getParent()->getChild(name, index_other);
+ if( !other )
+ return;
+
+ GeoCoord coord_other = parseGeoCoord(other->getStringValue());
+ if( coord_other.type == GeoCoord::INVALID
+ || coord_other.type == coord.type )
+ return;
+
+ GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
+ if( it_geo_node_other == _geo_nodes.end() )
+ LOG_GEO_RET("other geo node not found!")
+ GeoNodePair* geo_node_other = it_geo_node_other->second.get();
+
+ // Let use both nodes use the same GeoNodePair instance
+ if( geo_node_other != geo_node )
+ it_geo_node_other->second = it_geo_node->second;
+
+ if( coord_other.type == GeoCoord::LATITUDE )
+ geo_node->setNodeLat(other);
+ else
+ geo_node->setNodeLon(other);
+
+ // Set name for resulting screen coordinate nodes
+ geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
+ }
+ }
+
+ //----------------------------------------------------------------------------
+ void Map::childChanged(SGPropertyNode * child)
+ {
+ if( child->getNameString() == "ref-lat"
+ || child->getNameString() == "ref-lon" )
+ projection.setWorldPosition( _node->getDoubleValue("ref-lat"),
+ _node->getDoubleValue("ref-lon") );
+ else if( child->getNameString() == "hdg" )
+ projection.setOrientation(child->getFloatValue());
+ else if( child->getNameString() == "range" )
+ projection.setRange(child->getDoubleValue());
+ else
+ return;
+
+ _projection_dirty = true;
+ }
+
+ //----------------------------------------------------------------------------
+ Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
+ {
+ GeoCoord coord;
+ if( val.length() < 2 )
+ return coord;
+
+ if( val[0] == 'N' || val[0] == 'S' )
+ coord.type = GeoCoord::LATITUDE;
+ else if( val[0] == 'E' || val[0] == 'W' )
+ coord.type = GeoCoord::LONGITUDE;
+ else
+ return coord;
+
+ char* end;
+ coord.value = strtod(&val[1], &end);
+
+ if( end != &val[val.length()] )
+ {
+ coord.type = GeoCoord::INVALID;
+ return coord;
+ }
+
+ if( val[0] == 'S' || val[0] == 'W' )
+ coord.value *= -1;
+
+ return coord;
+ }
+
+ //----------------------------------------------------------------------------
+ bool Map::hasSuffix(const std::string& str, const std::string& suffix) const
+ {
+ if( suffix.length() > str.length() )
+ return false;
+
+ return ( str.compare( str.length() - suffix.length(),
+ suffix.length(),
+ suffix ) == 0 );
+ }
+
+} // namespace canvas
--- /dev/null
+// A group of 2D canvas elements which get automatically transformed according
+// to the map parameters.
+//
+// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+#ifndef CANVAS_MAP_HXX_
+#define CANVAS_MAP_HXX_
+
+#include "group.hxx"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/unordered_map.hpp>
+
+namespace canvas
+{
+ class GeoNodePair;
+ class Map:
+ public Group
+ {
+ public:
+ Map(SGPropertyNode_ptr node);
+ virtual ~Map();
+
+ virtual void update(double dt);
+
+ virtual void childAdded( SGPropertyNode * parent,
+ SGPropertyNode * child );
+ virtual void childRemoved( SGPropertyNode * parent,
+ SGPropertyNode * child );
+ virtual void valueChanged(SGPropertyNode * child);
+
+ protected:
+
+ virtual void childChanged(SGPropertyNode * child);
+
+ typedef boost::unordered_map< SGPropertyNode*,
+ boost::shared_ptr<GeoNodePair>
+ > GeoNodes;
+ GeoNodes _geo_nodes;
+ bool _projection_dirty;
+
+ struct GeoCoord
+ {
+ GeoCoord():
+ type(INVALID)
+ {}
+ enum
+ {
+ INVALID,
+ LATITUDE,
+ LONGITUDE
+ } type;
+ double value;
+ };
+
+ GeoCoord parseGeoCoord(const std::string& val) const;
+
+ bool hasSuffix(const std::string& str, const std::string& suffix) const;
+ };
+
+} // namespace canvas
+
+#endif /* CANVAS_MAP_HXX_ */
--- /dev/null
+/*
+ * geo_node_pair.hxx
+ *
+ * Created on: 11.07.2012
+ * Author: tom
+ */
+
+#ifndef CANVAS_GEO_NODE_PAIR_HXX_
+#define CANVAS_GEO_NODE_PAIR_HXX_
+
+namespace canvas
+{
+ class GeoNodePair
+ {
+ public:
+ enum StatusFlags
+ {
+ LAT_MISSING = 1,
+ LON_MISSING = LAT_MISSING << 1,
+ INCOMPLETE = LAT_MISSING | LON_MISSING,
+ DIRTY = LON_MISSING << 1
+ };
+
+ GeoNodePair():
+ _status(INCOMPLETE),
+ _node_lat(0),
+ _node_lon(0)
+ {}
+
+ uint8_t getStatus() const
+ {
+ return _status;
+ }
+
+ void setDirty(bool flag = true)
+ {
+ if( flag )
+ _status |= DIRTY;
+ else
+ _status &= ~DIRTY;
+ }
+
+ bool isDirty() const
+ {
+ return _status & DIRTY;
+ }
+
+ bool isComplete() const
+ {
+ return !(_status & INCOMPLETE);
+ }
+
+ void setNodeLat(SGPropertyNode* node)
+ {
+ _node_lat = node;
+ _status &= ~LAT_MISSING;
+
+ if( node == _node_lon )
+ {
+ _node_lon = 0;
+ _status |= LON_MISSING;
+ }
+ }
+
+ void setNodeLon(SGPropertyNode* node)
+ {
+ _node_lon = node;
+ _status &= ~LON_MISSING;
+
+ if( node == _node_lat )
+ {
+ _node_lat = 0;
+ _status |= LAT_MISSING;
+ }
+ }
+
+ const char* getLat() const
+ {
+ return _node_lat ? _node_lat->getStringValue() : "";
+ }
+
+ const char* getLon() const
+ {
+ return _node_lon ? _node_lon->getStringValue() : "";
+ }
+
+ void setTargetName(const std::string& name)
+ {
+ _target_name = name;
+ }
+
+ void setScreenPos(float x, float y)
+ {
+ assert( isComplete() );
+ SGPropertyNode *parent = _node_lat->getParent();
+ parent->getChild(_target_name, _node_lat->getIndex(), true)
+ ->setDoubleValue(x);
+ parent->getChild(_target_name, _node_lon->getIndex(), true)
+ ->setDoubleValue(y);
+ }
+
+ void print()
+ {
+ std::cout << "lat=" << (_node_lat ? _node_lat->getPath() : "")
+ << ", lon=" << (_node_lon ? _node_lon->getPath() : "")
+ << std::endl;
+ }
+
+ private:
+
+ uint8_t _status;
+ SGPropertyNode *_node_lat,
+ *_node_lon;
+ std::string _target_name;
+
+ };
+
+} // namespace canvas
+
+#endif /* CANVAS_GEO_NODE_PAIR_HXX_ */
--- /dev/null
+/*
+ * projection.hxx
+ *
+ * Created on: 12.07.2012
+ * Author: tom
+ */
+
+#ifndef CANVAS_MAP_PROJECTION_HXX_
+#define CANVAS_MAP_PROJECTION_HXX_
+
+const double DEG2RAD = M_PI / 180.0;
+
+namespace canvas
+{
+
+ /**
+ * Base class for all projections
+ */
+ class Projection
+ {
+ public:
+ struct ScreenPosition
+ {
+ ScreenPosition() {}
+
+ ScreenPosition(double x, double y):
+ x(x),
+ y(y)
+ {}
+
+ double x, y;
+ };
+
+ virtual ~Projection() {}
+
+ void setScreenRange(double range)
+ {
+ _screen_range = range;
+ }
+
+ virtual ScreenPosition worldToScreen(double x, double y) = 0;
+
+ protected:
+
+ double _screen_range;
+ };
+
+ /**
+ * Base class for horizontal projections
+ */
+ class HorizontalProjection:
+ public Projection
+ {
+ public:
+
+ HorizontalProjection():
+ _cos_rot(1),
+ _sin_rot(0),
+ _range(5)
+ {
+ setScreenRange(200);
+ }
+
+ /**
+ * Set world position of center point used for the projection
+ */
+ void setWorldPosition(double lat, double lon)
+ {
+ _ref_lat = lat * DEG2RAD;
+ _ref_lon = lon * DEG2RAD;
+ }
+
+ /**
+ * Set up heading
+ */
+ void setOrientation(float hdg)
+ {
+ hdg *= DEG2RAD;
+ _sin_rot = sin(hdg);
+ _cos_rot = cos(hdg);
+ }
+
+ void setRange(double range)
+ {
+ _range = range;
+ }
+
+ /**
+ * Transform given world position to screen position
+ *
+ * @param lat Latitude in degrees
+ * @param lon Longitude in degrees
+ */
+ ScreenPosition worldToScreen(double lat, double lon)
+ {
+ lat *= DEG2RAD;
+ lon *= DEG2RAD;
+ ScreenPosition pos = project(lat, lon);
+ double scale = _screen_range / _range;
+ pos.x *= scale;
+ pos.y *= scale;
+ return ScreenPosition
+ (
+ _cos_rot * pos.x - _sin_rot * pos.y,
+ -_sin_rot * pos.x - _cos_rot * pos.y
+ );
+ }
+
+ protected:
+
+ /**
+ * Project given geographic world position to screen space
+ *
+ * @param lat Latitude in radians
+ * @param lon Longitude in radians
+ */
+ virtual ScreenPosition project(double lat, double lon) const = 0;
+
+ double _ref_lat,
+ _ref_lon,
+ _cos_rot,
+ _sin_rot,
+ _range;
+ };
+
+ /**
+ * Sanson-Flamsteed projection, relative to the projection center
+ */
+ class SansonFlamsteedProjection:
+ public HorizontalProjection
+ {
+ protected:
+
+ virtual ScreenPosition project(double lat, double lon) const
+ {
+ double d_lat = lat - _ref_lat,
+ d_lon = lon - _ref_lon;
+ double r = getEarthRadius(lat);
+
+ ScreenPosition pos;
+
+ pos.x = r * cos(lat) * d_lon;
+ pos.y = r * d_lat;
+
+ return pos;
+ }
+
+ /**
+ * Returns Earth radius at a given latitude (Ellipsoide equation with two
+ * equal axis)
+ */
+ float getEarthRadius(float lat) const
+ {
+ const float rec = 6378137.f / 1852; // earth radius, equator (?)
+ const float rpol = 6356752.314f / 1852; // earth radius, polar (?)
+
+ double a = cos(lat) / rec;
+ double b = sin(lat) / rpol;
+ return 1.0f / sqrt( a * a + b * b );
+ }
+ };
+
+} // namespace canvas
+
+#endif /* CANVAS_MAP_PROJECTION_HXX_ */