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 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.
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.
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
20 #include "CanvasMap.hxx"
21 #include "map/geo_node_pair.hxx"
22 #include "map/projection.hxx"
26 #include <boost/algorithm/string/predicate.hpp>
28 #define LOG_GEO_RET(msg) \
34 msg << " (" << child->getStringValue()\
35 << ", " << child->getPath() << ")"\
45 //----------------------------------------------------------------------------
46 const std::string GEO = "-geo";
47 const std::string Map::TYPE_NAME = "map";
49 //----------------------------------------------------------------------------
50 void Map::staticInit()
57 // Do some initialization if needed...
60 //----------------------------------------------------------------------------
61 Map::Map( const CanvasWeakPtr& canvas,
62 const SGPropertyNode_ptr& node,
63 const Style& parent_style,
65 Group(canvas, node, parent_style, parent),
66 // TODO make projection configurable
67 _projection(new SansonFlamsteedProjection),
68 _projection_dirty(true)
73 //----------------------------------------------------------------------------
79 //----------------------------------------------------------------------------
80 void Map::update(double dt)
82 for( GeoNodes::iterator it = _geo_nodes.begin();
83 it != _geo_nodes.end();
86 GeoNodePair* geo_node = it->second.get();
87 if( !geo_node->isComplete()
88 || (!geo_node->isDirty() && !_projection_dirty) )
91 GeoCoord lat = parseGeoCoord(geo_node->getLat());
92 if( lat.type != GeoCoord::LATITUDE )
95 GeoCoord lon = parseGeoCoord(geo_node->getLon());
96 if( lon.type != GeoCoord::LONGITUDE )
99 Projection::ScreenPosition pos =
100 _projection->worldToScreen(lat.value, lon.value);
102 geo_node->setScreenPos(pos.x, pos.y);
104 // geo_node->print();
105 geo_node->setDirty(false);
107 _projection_dirty = false;
112 //----------------------------------------------------------------------------
113 void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
115 if( !boost::ends_with(child->getNameString(), GEO) )
116 return Element::childAdded(parent, child);
118 _geo_nodes[child].reset(new GeoNodePair());
121 //----------------------------------------------------------------------------
122 void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
124 if( !boost::ends_with(child->getNameString(), GEO) )
125 return Element::childRemoved(parent, child);
127 // TODO remove from other node
128 _geo_nodes.erase(child);
131 //----------------------------------------------------------------------------
132 void Map::valueChanged(SGPropertyNode * child)
134 const std::string& name = child->getNameString();
136 if( !boost::ends_with(name, GEO) )
137 return Group::valueChanged(child);
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();
144 geo_node->setDirty();
146 if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
148 // Detect lat, lon tuples...
149 GeoCoord coord = parseGeoCoord(child->getStringValue());
150 int index_other = -1;
154 case GeoCoord::LATITUDE:
155 index_other = child->getIndex() + 1;
156 geo_node->setNodeLat(child);
158 case GeoCoord::LONGITUDE:
159 index_other = child->getIndex() - 1;
160 geo_node->setNodeLon(child);
163 LOG_GEO_RET("Invalid geo coord")
166 SGPropertyNode *other = child->getParent()->getChild(name, index_other);
170 GeoCoord coord_other = parseGeoCoord(other->getStringValue());
171 if( coord_other.type == GeoCoord::INVALID
172 || coord_other.type == coord.type )
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();
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;
184 if( coord_other.type == GeoCoord::LATITUDE )
185 geo_node->setNodeLat(other);
187 geo_node->setNodeLon(other);
189 // Set name for resulting screen coordinate nodes
190 geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
194 //----------------------------------------------------------------------------
195 void Map::childChanged(SGPropertyNode * child)
197 if( child->getParent() != _node )
198 return Group::childChanged(child);
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 if( child->getNameString() == "screen-range" )
209 _projection->setScreenRange(child->getDoubleValue());
211 return Group::childChanged(child);
213 _projection_dirty = true;
216 //----------------------------------------------------------------------------
217 Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
220 if( val.length() < 2 )
223 if( val[0] == 'N' || val[0] == 'S' )
224 coord.type = GeoCoord::LATITUDE;
225 else if( val[0] == 'E' || val[0] == 'W' )
226 coord.type = GeoCoord::LONGITUDE;
231 coord.value = strtod(&val[1], &end);
233 if( end != &val[val.length()] )
235 coord.type = GeoCoord::INVALID;
239 if( val[0] == 'S' || val[0] == 'W' )
245 } // namespace canvas
246 } // namespace simgear