1 // A group of 2D canvas elements which get automatically transformed according
2 // to the map parameters.
4 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "map/geo_node_pair.hxx"
22 #include "map/projection.hxx"
24 #include <Main/fg_props.hxx>
27 #include <boost/algorithm/string/predicate.hpp>
29 #define LOG_GEO_RET(msg) \
35 msg << " (" << child->getStringValue()\
36 << ", " << child->getPath() << ")"\
44 // TODO make projection configurable
45 SansonFlamsteedProjection projection;
46 const std::string GEO = "-geo";
48 //----------------------------------------------------------------------------
49 Map::Map(SGPropertyNode_ptr node):
51 _projection_dirty(true)
56 //----------------------------------------------------------------------------
62 //----------------------------------------------------------------------------
63 void Map::update(double dt)
65 for( GeoNodes::iterator it = _geo_nodes.begin();
66 it != _geo_nodes.end();
69 GeoNodePair* geo_node = it->second.get();
70 if( !geo_node->isComplete()
71 || (!geo_node->isDirty() && !_projection_dirty) )
74 GeoCoord lat = parseGeoCoord(geo_node->getLat());
75 if( lat.type != GeoCoord::LATITUDE )
78 GeoCoord lon = parseGeoCoord(geo_node->getLon());
79 if( lon.type != GeoCoord::LONGITUDE )
82 Projection::ScreenPosition pos =
83 projection.worldToScreen(lat.value, lon.value);
85 geo_node->setScreenPos(pos.x, pos.y);
88 geo_node->setDirty(false);
90 _projection_dirty = false;
95 //----------------------------------------------------------------------------
96 void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
98 if( !boost::ends_with(child->getNameString(), GEO) )
99 return Element::childAdded(parent, child);
101 _geo_nodes[child].reset(new GeoNodePair());
104 //----------------------------------------------------------------------------
105 void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
107 if( !boost::ends_with(child->getNameString(), GEO) )
108 return Element::childRemoved(parent, child);
110 // TODO remove from other node
111 _geo_nodes.erase(child);
114 //----------------------------------------------------------------------------
115 void Map::valueChanged(SGPropertyNode * child)
117 const std::string& name = child->getNameString();
119 if( !boost::ends_with(name, GEO) )
120 return Group::valueChanged(child);
122 GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
123 if( it_geo_node == _geo_nodes.end() )
124 LOG_GEO_RET("geo node not found!")
125 GeoNodePair* geo_node = it_geo_node->second.get();
127 geo_node->setDirty();
129 if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
131 // Detect lat, lon tuples...
132 GeoCoord coord = parseGeoCoord(child->getStringValue());
133 int index_other = -1;
137 case GeoCoord::LATITUDE:
138 index_other = child->getIndex() + 1;
139 geo_node->setNodeLat(child);
141 case GeoCoord::LONGITUDE:
142 index_other = child->getIndex() - 1;
143 geo_node->setNodeLon(child);
146 LOG_GEO_RET("Invalid geo coord")
149 SGPropertyNode *other = child->getParent()->getChild(name, index_other);
153 GeoCoord coord_other = parseGeoCoord(other->getStringValue());
154 if( coord_other.type == GeoCoord::INVALID
155 || coord_other.type == coord.type )
158 GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
159 if( it_geo_node_other == _geo_nodes.end() )
160 LOG_GEO_RET("other geo node not found!")
161 GeoNodePair* geo_node_other = it_geo_node_other->second.get();
163 // Let use both nodes use the same GeoNodePair instance
164 if( geo_node_other != geo_node )
165 it_geo_node_other->second = it_geo_node->second;
167 if( coord_other.type == GeoCoord::LATITUDE )
168 geo_node->setNodeLat(other);
170 geo_node->setNodeLon(other);
172 // Set name for resulting screen coordinate nodes
173 geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
177 //----------------------------------------------------------------------------
178 void Map::childChanged(SGPropertyNode * child)
180 if( child->getNameString() == "ref-lat"
181 || child->getNameString() == "ref-lon" )
182 projection.setWorldPosition( _node->getDoubleValue("ref-lat"),
183 _node->getDoubleValue("ref-lon") );
184 else if( child->getNameString() == "hdg" )
185 projection.setOrientation(child->getFloatValue());
186 else if( child->getNameString() == "range" )
187 projection.setRange(child->getDoubleValue());
191 _projection_dirty = true;
194 //----------------------------------------------------------------------------
195 Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
198 if( val.length() < 2 )
201 if( val[0] == 'N' || val[0] == 'S' )
202 coord.type = GeoCoord::LATITUDE;
203 else if( val[0] == 'E' || val[0] == 'W' )
204 coord.type = GeoCoord::LONGITUDE;
209 coord.value = strtod(&val[1], &end);
211 if( end != &val[val.length()] )
213 coord.type = GeoCoord::INVALID;
217 if( val[0] == 'S' || val[0] == 'W' )
223 } // namespace canvas