Canvas: Add new element type map for geo mapping.
- The new map element automatically transforms geo coordinates (lat, lon) to the according screen coordinates. - Currently one type of projection is supported (Sanson-Flamsteed projection)
This commit is contained in:
parent
a876ff93e1
commit
e81db175f4
7 changed files with 601 additions and 8 deletions
|
@ -5,6 +5,7 @@ set(SOURCES
|
||||||
canvas_mgr.cxx
|
canvas_mgr.cxx
|
||||||
elements/element.cxx
|
elements/element.cxx
|
||||||
elements/group.cxx
|
elements/group.cxx
|
||||||
|
elements/map.cxx
|
||||||
elements/path.cxx
|
elements/path.cxx
|
||||||
elements/text.cxx
|
elements/text.cxx
|
||||||
property_helper.cxx
|
property_helper.cxx
|
||||||
|
@ -15,6 +16,7 @@ set(HEADERS
|
||||||
canvas_mgr.hxx
|
canvas_mgr.hxx
|
||||||
elements/element.hxx
|
elements/element.hxx
|
||||||
elements/group.hxx
|
elements/group.hxx
|
||||||
|
elements/map.hxx
|
||||||
elements/path.hxx
|
elements/path.hxx
|
||||||
elements/text.hxx
|
elements/text.hxx
|
||||||
property_helper.hxx
|
property_helper.hxx
|
||||||
|
|
|
@ -159,13 +159,6 @@ namespace canvas
|
||||||
type = TT_ROTATE;
|
type = TT_ROTATE;
|
||||||
else if( name == "s" )
|
else if( name == "s" )
|
||||||
type = TT_SCALE;
|
type = TT_SCALE;
|
||||||
else
|
|
||||||
SG_LOG
|
|
||||||
(
|
|
||||||
SG_GL,
|
|
||||||
SG_WARN,
|
|
||||||
"Unknown transform element " << child->getPath()
|
|
||||||
);
|
|
||||||
|
|
||||||
_transform_dirty = true;
|
_transform_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#include "group.hxx"
|
#include "group.hxx"
|
||||||
|
#include "map.hxx"
|
||||||
#include "path.hxx"
|
#include "path.hxx"
|
||||||
#include "text.hxx"
|
#include "text.hxx"
|
||||||
|
|
||||||
|
@ -56,6 +57,8 @@ namespace canvas
|
||||||
element.reset( new Text(child) );
|
element.reset( new Text(child) );
|
||||||
else if( child->getNameString() == "group" )
|
else if( child->getNameString() == "group" )
|
||||||
element.reset( new Group(child) );
|
element.reset( new Group(child) );
|
||||||
|
else if( child->getNameString() == "map" )
|
||||||
|
element.reset( new Map(child) );
|
||||||
else if( child->getNameString() == "path" )
|
else if( child->getNameString() == "path" )
|
||||||
element.reset( new Path(child) );
|
element.reset( new Path(child) );
|
||||||
|
|
||||||
|
@ -72,7 +75,8 @@ namespace canvas
|
||||||
{
|
{
|
||||||
if( node->getNameString() == "text"
|
if( node->getNameString() == "text"
|
||||||
|| node->getNameString() == "group"
|
|| node->getNameString() == "group"
|
||||||
|| node->getNameString() == "path")
|
|| node->getNameString() == "map"
|
||||||
|
|| node->getNameString() == "path" )
|
||||||
{
|
{
|
||||||
ChildMap::iterator child = _children.find(node);
|
ChildMap::iterator child = _children.find(node);
|
||||||
|
|
||||||
|
|
232
src/Canvas/elements/map.cxx
Normal file
232
src/Canvas/elements/map.cxx
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
// 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
|
77
src/Canvas/elements/map.hxx
Normal file
77
src/Canvas/elements/map.hxx
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// 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_ */
|
120
src/Canvas/elements/map/geo_node_pair.hxx
Normal file
120
src/Canvas/elements/map/geo_node_pair.hxx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* 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_ */
|
165
src/Canvas/elements/map/projection.hxx
Normal file
165
src/Canvas/elements/map/projection.hxx
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* 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_ */
|
Loading…
Add table
Reference in a new issue