]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasMap.cxx
Canvas: Ensure all element types are initialized before first usage.
[simgear.git] / simgear / canvas / elements / CanvasMap.cxx
1 // A group of 2D Canvas elements which get automatically transformed according
2 // to the map parameters.
3 //
4 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Library General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
19
20 #include "CanvasMap.hxx"
21 #include "map/geo_node_pair.hxx"
22 #include "map/projection.hxx"
23
24 #include <cmath>
25
26 #include <boost/algorithm/string/predicate.hpp>
27
28 #define LOG_GEO_RET(msg) \
29   {\
30     SG_LOG\
31     (\
32       SG_GENERAL,\
33       SG_WARN,\
34       msg << " (" << child->getStringValue()\
35                   << ", " << child->getPath() << ")"\
36     );\
37     return;\
38   }
39
40 namespace simgear
41 {
42 namespace canvas
43 {
44
45   //----------------------------------------------------------------------------
46   const std::string GEO = "-geo";
47   const std::string Map::TYPE_NAME = "map";
48
49   //----------------------------------------------------------------------------
50   void Map::staticInit()
51   {
52     Group::staticInit();
53
54     if( isInit<Map>() )
55       return;
56
57     // Do some initialization if needed...
58   }
59
60   //----------------------------------------------------------------------------
61   Map::Map( const CanvasWeakPtr& canvas,
62             const SGPropertyNode_ptr& node,
63             const Style& parent_style,
64             Element* parent ):
65     Group(canvas, node, parent_style, parent),
66     // TODO make projection configurable
67     _projection(new SansonFlamsteedProjection),
68     _projection_dirty(true)
69   {
70     staticInit();
71   }
72
73   //----------------------------------------------------------------------------
74   Map::~Map()
75   {
76
77   }
78
79   //----------------------------------------------------------------------------
80   void Map::update(double dt)
81   {
82     for( GeoNodes::iterator it = _geo_nodes.begin();
83          it != _geo_nodes.end();
84          ++it )
85     {
86       GeoNodePair* geo_node = it->second.get();
87       if(    !geo_node->isComplete()
88           || (!geo_node->isDirty() && !_projection_dirty) )
89         continue;
90
91       GeoCoord lat = parseGeoCoord(geo_node->getLat());
92       if( lat.type != GeoCoord::LATITUDE )
93         continue;
94
95       GeoCoord lon = parseGeoCoord(geo_node->getLon());
96       if( lon.type != GeoCoord::LONGITUDE )
97         continue;
98
99       Projection::ScreenPosition pos =
100         _projection->worldToScreen(lat.value, lon.value);
101
102       geo_node->setScreenPos(pos.x, pos.y);
103
104 //      geo_node->print();
105       geo_node->setDirty(false);
106     }
107     _projection_dirty = false;
108
109     Group::update(dt);
110   }
111
112   //----------------------------------------------------------------------------
113   void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
114   {
115     if( !boost::ends_with(child->getNameString(), GEO) )
116       return Element::childAdded(parent, child);
117
118     _geo_nodes[child].reset(new GeoNodePair());
119   }
120
121   //----------------------------------------------------------------------------
122   void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
123   {
124     if( !boost::ends_with(child->getNameString(), GEO) )
125       return Element::childRemoved(parent, child);
126
127     // TODO remove from other node
128     _geo_nodes.erase(child);
129   }
130
131   //----------------------------------------------------------------------------
132   void Map::valueChanged(SGPropertyNode * child)
133   {
134     const std::string& name = child->getNameString();
135
136     if( !boost::ends_with(name, GEO) )
137       return Group::valueChanged(child);
138
139     GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
140     if( it_geo_node == _geo_nodes.end() )
141       LOG_GEO_RET("geo node not found!")
142     GeoNodePair* geo_node = it_geo_node->second.get();
143
144     geo_node->setDirty();
145
146     if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
147     {
148       // Detect lat, lon tuples...
149       GeoCoord coord = parseGeoCoord(child->getStringValue());
150       int index_other = -1;
151
152       switch( coord.type )
153       {
154         case GeoCoord::LATITUDE:
155           index_other = child->getIndex() + 1;
156           geo_node->setNodeLat(child);
157           break;
158         case GeoCoord::LONGITUDE:
159           index_other = child->getIndex() - 1;
160           geo_node->setNodeLon(child);
161           break;
162         default:
163           LOG_GEO_RET("Invalid geo coord")
164       }
165
166       SGPropertyNode *other = child->getParent()->getChild(name, index_other);
167       if( !other )
168         return;
169
170       GeoCoord coord_other = parseGeoCoord(other->getStringValue());
171       if(    coord_other.type == GeoCoord::INVALID
172           || coord_other.type == coord.type )
173         return;
174
175       GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
176       if( it_geo_node_other == _geo_nodes.end() )
177         LOG_GEO_RET("other geo node not found!")
178       GeoNodePair* geo_node_other = it_geo_node_other->second.get();
179
180       // Let use both nodes use the same GeoNodePair instance
181       if( geo_node_other != geo_node )
182         it_geo_node_other->second = it_geo_node->second;
183
184       if( coord_other.type == GeoCoord::LATITUDE )
185         geo_node->setNodeLat(other);
186       else
187         geo_node->setNodeLon(other);
188
189       // Set name for resulting screen coordinate nodes
190       geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
191     }
192   }
193
194   //----------------------------------------------------------------------------
195   void Map::childChanged(SGPropertyNode * child)
196   {
197     if( child->getParent() != _node )
198       return Group::childChanged(child);
199
200     if(    child->getNameString() == "ref-lat"
201         || child->getNameString() == "ref-lon" )
202       _projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
203                                      _node->getDoubleValue("ref-lon") );
204     else if( child->getNameString() == "hdg" )
205       _projection->setOrientation(child->getFloatValue());
206     else if( child->getNameString() == "range" )
207       _projection->setRange(child->getDoubleValue());
208     else
209       return Group::childChanged(child);
210
211     _projection_dirty = true;
212   }
213
214   //----------------------------------------------------------------------------
215   Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
216   {
217     GeoCoord coord;
218     if( val.length() < 2 )
219       return coord;
220
221     if( val[0] == 'N' || val[0] == 'S' )
222       coord.type = GeoCoord::LATITUDE;
223     else if( val[0] == 'E' || val[0] == 'W' )
224       coord.type = GeoCoord::LONGITUDE;
225     else
226       return coord;
227
228     char* end;
229     coord.value = strtod(&val[1], &end);
230
231     if( end != &val[val.length()] )
232     {
233       coord.type = GeoCoord::INVALID;
234       return coord;
235     }
236
237     if( val[0] == 'S' || val[0] == 'W' )
238       coord.value *= -1;
239
240     return coord;
241   }
242
243 } // namespace canvas
244 } // namespace simgear