text: //
  // ImportOSM.cpp: The main Builder class of the VTBuilder
  //
  // Copyright (c) 2006-2012 Virtual Terrain Project
  // Free for all uses, see license.txt for details.
  //
  
  // For compilers that support precompilation, includes "wx/wx.h".
  #include "wx/wxprec.h"
  
  #ifndef WX_PRECOMP
  #include "wx/wx.h"
  #endif
  
  #include <map>
  
  #include "vtdata/PolyChecker.h"
  #include "vtui/Helper.h"
  
  #include "Builder.h"
  
  // Layers
  #include "RoadLayer.h"
  #include "StructLayer.h"
  #include "UtilityLayer.h"
  #include "WaterLayer.h"
  
  ////////////////////////////////////////////////////////////////////////
  // Visitor class, for XML parsing of an OpenStreetMap file.
  //
  
  struct OSMNode {
  	DPoint2 p;
  	bool signal_lights;
  	vtPole *pole;
  };
  
  class VisitorOSM : public XMLVisitor
  {
  public:
  	VisitorOSM();
  	void startElement(const char *name, const XMLAttributes &atts);
  	void endElement(const char *name);
  	void data(const char *s, int length) {}	// OSM doesn't use actual XML data
  	void SetSignalLights();
  
  	vtRoadLayer *m_road_layer;
  	vtStructureLayer *m_struct_layer;
  	vtUtilityLayer *m_util_layer;
  
  	vtPole *m_pole;
  	vtLine *m_line;
  
  private:
  	void MakeRoad();
  	void MakeStructure();
  	void MakeBuilding();
  	void MakeLinear();
  
  	void StartPowerPole();
  	void MakePowerLine();
  	void ParseOSMTag(const vtString &key, const vtString &value);
  
  	enum ParseState {
  		PS_NONE,
  		PS_NODE,
  		PS_WAY
  	} m_state;
  
  	typedef std::map<int, OSMNode> NodeMap;
  	NodeMap m_nodes;
  	std::vector<int> m_refs;
  
  	vtProjection m_proj;
  
  	vtString	m_Name, m_URL;
  	LayerType	m_WayType;
  	bool		m_bIsArea;
  	int			m_id;
  
  	int			m_iRoadLanes;
  	int			m_iRoadFlags;
  	SurfaceType m_eSurfaceType;
  
  	vtStructureType m_eStructureType;
  	vtLinearStyle	m_eLinearStyle;
  	int				m_iNumStories;
  	float			m_fHeight;
  	RoofType		m_RoofType;
  };
  
  VisitorOSM::VisitorOSM() : m_state(PS_NONE)
  {
  	m_road_layer = NULL;
  	m_struct_layer = NULL;
  	m_util_layer = NULL;
  
  	// OSM is always in Geo WGS84
  	m_proj.SetWellKnownGeogCS("WGS84");
  }
  
  void VisitorOSM::SetSignalLights()
  {
  	// For all the nodes which have signal lights, set the state
  	for (NodeMap::iterator it = m_nodes.begin(); it != m_nodes.end(); it++)
  	{
  		OSMNode &node = it->second;
  		if (node.signal_lights)
  		{
  			TNode *tnode = m_road_layer->FindNodeByID(it->first);
  			if (tnode)
  			{
  				for (int j = 0; j < tnode->NumLinks(); j++)
  					tnode->SetIntersectType(j, IT_LIGHT);
  			}
  		}
  	}
  	m_road_layer->GuessIntersectionTypes();
  }
  
  void VisitorOSM::startElement(const char *name, const XMLAttributes &atts)
  {
  	const char *val;
  
  	if (m_state == 0)
  	{
  		if (!strcmp(name, "node"))
  		{
  			DPoint2 p;
  
  			val = atts.getValue("id");
  			if (val)
  				m_id = atoi(val);
  			else
  				m_id = -1;	// Shouldn't happen.
  
  			val = atts.getValue("lon");
  			if (val)
  				p.x = atof(val);
  
  			val = atts.getValue("lat");
  			if (val)
  				p.y = atof(val);
  
  			OSMNode node;
  			node.p = p;
  			node.signal_lights = false;
  			m_nodes[m_id] = node;
  
  			m_state = PS_NODE;
  
  			m_pole = NULL;
  		}
  		else if (!strcmp(name, "way"))
  		{
  			m_refs.clear();
  			m_state = PS_WAY;
  			val = atts.getValue("id");
  			if (val)
  				m_id = atoi(val);
  			else
  				m_id = -1;	// Shouldn't happen.
  
  			// Defaults
  			m_Name = "";
  			m_URL = "";
  			m_WayType = LT_UNKNOWN;
  			m_bIsArea = false;
  			m_iRoadLanes = 2;
  			m_iRoadFlags = RF_FORWARD | RF_REVERSE;	// bidrectional traffic
  			m_eSurfaceType = SURFT_PAVED;
  			m_eStructureType = ST_NONE;
  			m_iNumStories = -1;
  			m_fHeight = -1;
  			m_RoofType = NUM_ROOFTYPES;
  			m_line = NULL;
  		}
  	}
  	else if (m_state == PS_NODE && !strcmp(name, "tag"))
  	{
  		vtString key, value;
  
  		val = atts.getValue("k");
  		if (val)
  			key = val;
  
  		val = atts.getValue("v");
  		if (val)
  			value = val;
  
  		if (key == "power" && value == "tower")
  			StartPowerPole();
  
  		// Node key/value
  		else if (key == "highway")
  		{
  			if (value == "traffic_signals")
  			{
  				m_nodes[m_nodes.size()-1].signal_lights = true;
  			}
  		}
  		else if (m_pole)
  		{
  			// Add all node tags for power towers
  			m_pole->AddTag(key, value);
  		}
  	}
  	else if (m_state == PS_WAY)
  	{
  		if (!strcmp(name, "nd"))
  		{
  			val = atts.getValue("ref");
  			if (val)
  			{
  				int ref = atoi(val);
  				m_refs.push_back(ref);
  			}
  		}
  		else if (!strcmp(name, "tag"))
  		{
  			vtString key, value;
  
  			val = atts.getValue("k");
  			if (val)
  				key = val;
  
  			val = atts.getValue("v");
  			if (val)
  				value = val;
  
  			if (m_line)
  				m_line->AddTag(key, value);
  			else
  				ParseOSMTag(key, value);
  		}
  	}
  }
  
  void VisitorOSM::endElement(const char *name)
  {
  	if (m_state == PS_NODE && !strcmp(name, "node"))
  	{
  		m_state = PS_NONE;
  	}
  	else if (m_state == PS_WAY && !strcmp(name, "way"))
  	{
  		// Look at the referenced nodes, turn them into a vt link
  		uint refs = m_refs.size();
  
  		// must have at least 2 refs
  		if (m_refs.size() >= 2)
  		{
  			if (m_WayType == LT_ROAD && !m_bIsArea)	// Areas aren't roads
  				MakeRoad();
  			if (m_WayType == LT_STRUCTURE)
  				MakeStructure();
  		}
  		m_state = PS_NONE;
  	}
  }
  
  void VisitorOSM::ParseOSMTag(const vtString &key, const vtString &value)
  {
  	// There are hundreds of possible Way tags
  	if (key == "aeroway")
  	{
  		if (value == "terminal")
  		{
  			m_WayType = LT_STRUCTURE;
  			m_eStructureType = ST_BUILDING;
  		}
  	}
  	if (key == "aerialway")
  		m_WayType = LT_UNKNOWN;
  
  	if (key == "abutters")
  	{
  		// describes the predominant usage of land along a road or other way,
  		// not directly useful for visualization
  	}
  	if (key == "amenity")
  	{
  		// Mostly, types of building classified by use (like a post office,
  		//  library, school, hospital..)
  		// Except for some non-building values.
  		if (value == "parking" ||
  			value == "bench" ||
  			value == "fuel" ||
  			value == "grave_yard" ||
  			value == "post_box")
  		{
  			// Nothing yet.
  		}
  		if (value == "school" ||
  			value == "place_of_worship" ||
  			value == "restaurant" ||
  			value == "bank" ||
  			value == "fast_food" ||
  			value == "cafe" ||
  			value == "kindergarten" ||
  			value == "public_building" ||
  			value == "hospital" ||
  			value == "post_office")
  		{
  			m_WayType = LT_STRUCTURE;
  			m_eStructureType = ST_BUILDING;
  		}
  	}
  	if (key == "area")
  	{
  		if (value == "yes")
  			m_bIsArea = true;
  	}
  	if (key == "barrier")
  	{
  		// Some kinds of barrier are structures.
  		if (value == "city_wall" || value == "wall")
  		{
  			m_WayType = LT_STRUCTURE;
  			m_eStructureType = ST_LINEAR;
  			m_eLinearStyle = FS_STONE;
  		}
  		if (value == "fence")
  		{
  			m_WayType = LT_STRUCTURE;
  			m_eStructureType = ST_LINEAR;
  			m_eLinearStyle = FS_METAL_POSTS_WIRE;
  		}
  		if (value == "hedge")
  		{
  			m_WayType = LT_STRUCTURE;
  			m_eStructureType = ST_LINEAR;
  			m_eLinearStyle = FS_PRIVET;
  		}
  	}
  	if (key == "building" || key == "building:part")
  	{
  		// Values for "building" may be "yes" (94%), "house", "residential", "hut", "garage"..
  		m_WayType = LT_STRUCTURE;
  		m_eStructureType = ST_BUILDING;
  	}
  	if (key == "building:levels")
  	{
  		m_iNumStories = atoi(value);
  	}
  	if (key == "fence_type")
  	{
  		if (value == "chain")
  			m_eLinearStyle = FS_CHAINLINK;
  
  		if (value == "split_rail")
  			m_eLinearStyle = FS_WOOD_POSTS_WIRE;	// We don't do wooden fences yet.
  
  		if (value == "wood")
  			m_eLinearStyle = FS_WOOD_POSTS_WIRE;	// We don't do wooden fences yet.
  
  		if (value == "barbed_wire")
  			m_eLinearStyle = FS_WOOD_POSTS_WIRE;
  
  		if (value == "wire")
  			m_eLinearStyle = FS_METAL_POSTS_WIRE;
  
  		if (value == "pole")
  			m_eLinearStyle = FS_WOOD_POSTS_WIRE;	// We don't do wooden fences yet.
  
  		if (value == "hedge")
  			m_eLinearStyle = FS_PRIVET;
  	}
  	if (key == "height")
  	{
  		m_fHeight = atof((const char *)value);
  	}
  	if (key == "highway")
  	{
  		m_WayType = LT_ROAD;
  		// Common types include: residential, service, unclassified, primary,
  		//  secondary; none of which say anything about how the road looks.
  		// Look for values that do.
  		if (value == "bridleway")
  			m_eSurfaceType = SURFT_GRAVEL;
  		if (value == "cycleway")
  		{
  			m_iRoadLanes = 1;
  			m_eSurfaceType = SURFT_PAVED;
  		}
  		if (value == "footway")
  		{
  			m_iRoadLanes = 1;
  			m_eSurfaceType = SURFT_GRAVEL;
  		}
  		if (value == "motorway")	// like a freeway
  			m_iRoadLanes = 4;
  		if (value == "motorway_link")	// on/offramp
  			m_iRoadLanes = 1;
  		if (value == "path")
  		{
  			m_iRoadLanes = 1;
  			m_eSurfaceType = SURFT_TRAIL;
  		}
  		if (value == "pedestrian")
  		{
  			// Does this tell us anything about its width or appearance?
  			// Beware area=yes, as this is used for shopping areas.
  		}
  		if (value == "track")
  		{
  			// Roads "for agricultural use": farm roads, forest tracks, etc.
  			m_iRoadLanes = 1;
  			m_eSurfaceType = SURFT_2TRACK;
  		}
  		if (value == "unclassified")	// lowest form of the interconnecting grid network.
  			m_iRoadLanes = 1;
  		if (value == "unsurfaced")
  			m_eSurfaceType = SURFT_DIRT;
  	}
  	if (key == "lanes")
  		m_iRoadLanes = atoi(value);
  
  	if (key == "leisure")
  		m_WayType = LT_UNKNOWN;		// gardens, golf courses, public lawns, etc.
  
  	if (key == "man_made")
  		m_WayType = LT_UNKNOWN;		// Piers, towers, windmills, etc.
  
  	if (key == "name")
  		m_Name = value;
  
  	if (key == "oneway")
  	{
  		if (value == "yes")
  			m_iRoadFlags &= (~RF_REVERSE);
  		if (value == "-1")
  			m_iRoadFlags &= (~RF_FORWARD);
  	}
  
  	if (key.Left(13) == "parking:lane:")
  	{
  		vtString after = key.Mid(13);
  		if (after == "left")
  			m_iRoadFlags |= RF_PARKING_LEFT;
  		if (after == "right")
  			m_iRoadFlags |= RF_PARKING_RIGHT;
  		if (after == "both")
  			m_iRoadFlags |= (RF_PARKING_LEFT | RF_PARKING_RIGHT);
  	}
  
  	if (key == "power" && value == "line")
  	{
  		m_WayType = LT_UTILITY;
  		MakePowerLine();
  	}
  
  	if (key == "natural")	// value is coastline, marsh, etc.
  		m_WayType = LT_UNKNOWN;
  
  	if (key == "railway")
  		m_eSurfaceType = SURFT_RAILROAD;
  
  	if (key == "roof:shape")
  	{
  		// http://wiki.openstreetmap.org/wiki/Key:roof:shape#Roof
  		if (value == "flat") m_RoofType = ROOF_FLAT;
  		if (value == "gabled") m_RoofType = ROOF_GABLE;
  		if (value == "hipped") m_RoofType = ROOF_HIP;
  		// Less-common values gnored: half-hipped, pyramidal, gambrel, mansard, dome.
  	}
  
  	if (key == "route" && value == "ferry")
  		m_WayType = LT_UNKNOWN;
  
  	if (key == "sidewalk")
  	{
  		if (value == "left")
  			m_iRoadFlags |= RF_SIDEWALK_LEFT;
  		if (value == "right")
  			m_iRoadFlags |= RF_SIDEWALK_RIGHT;
  		if (value == "both")
  			m_iRoadFlags |= (RF_SIDEWALK_LEFT | RF_SIDEWALK_RIGHT);
  	}
  
  	if (key == "shop")
  	{
  		// Values may be "supermarket"..
  		m_WayType = LT_STRUCTURE;
  		m_eStructureType = ST_BUILDING;
  	}
  	if (key == "surface")
  	{
  		if (value == "asphalt")
  			m_eSurfaceType = SURFT_PAVED;
  		if (value == "compacted")
  			m_eSurfaceType = SURFT_GRAVEL;
  		if (value == "concrete")
  			m_eSurfaceType = SURFT_PAVED;
  		if (value == "dirt")
  			m_eSurfaceType = SURFT_DIRT;
  		if (value == "earth")
  			m_eSurfaceType = SURFT_DIRT;
  		if (value == "fine_gravel")
  			m_eSurfaceType = SURFT_GRAVEL;
  		if (value == "ground")
  			m_eSurfaceType = SURFT_2TRACK;	// or SURFT_TRAIL
  		if (value == "gravel")
  			m_eSurfaceType = SURFT_GRAVEL;
  		if (value == "paved")
  			m_eSurfaceType = SURFT_PAVED;
  		if (value == "sand")
  			m_eSurfaceType = SURFT_DIRT;
  		if (value == "unpaved")
  			m_eSurfaceType = SURFT_GRAVEL;	// or SURFT_DIRT
  	}
  	if (key == "tunnel")
  	{
  		// We can't do tunnels, so ignore them for now.
  		if (value == "yes")
  			m_WayType = LT_UNKNOWN;
  	}
  	if (key == "url")
  		m_URL = value;
  
  	if (key == "waterway")
  		m_WayType = LT_WATER;
  
  	if (key == "website")
  		m_URL = value;
  }
  
  void VisitorOSM::MakeRoad()
  {
  	if (!m_road_layer)
  	{
  		m_road_layer = new vtRoadLayer;
  		m_road_layer->SetProjection(m_proj);
  	}
  
  	LinkEdit *link = m_road_layer->AddNewLink();
  
  	link->m_iLanes = m_iRoadLanes;
  	link->m_iFlags = m_iRoadFlags;
  	link->m_Surface = m_eSurfaceType;
  
  	int ref_first = m_refs[0];
  	int ref_last = m_refs[m_refs.size() - 1];
  
  	// Make nodes at the first and last points.
  	NodeEdit *node0 = (NodeEdit *) m_road_layer->FindNodeByID(ref_first);
  	NodeEdit *node1 = (NodeEdit *) m_road_layer->FindNodeByID(ref_last);
  	if (!node0)
  	{
  		// No node there yet, create it
  		node0 = m_road_layer->AddNewNode();
  		node0->SetPos(m_nodes[ref_first].p);
  		node0->m_id = ref_first;
  	}
  	if (!node1)
  	{
  		// No node there yet, create it
  		node1 = m_road_layer->AddNewNode();
  		node1->SetPos(m_nodes[ref_last].p);
  		node1->m_id = ref_last;
  	}
  	link->ConnectNodes(node0, node1);
  
  	// Copy all the points
  	for (uint r = 0; r < m_refs.size(); r++)
  	{
  		int idx = m_refs[r];
  		link->Append(m_nodes[idx].p);
  	}
  	link->Dirtied();
  
  	// If either end of this link shares a node in the middle of another link,
  	// we need to split that link to maintain topology.
  	LinkEdit *next = NULL;
  	for (LinkEdit *it = m_road_layer->GetFirstLink(); it; it = next)
  	{
  		next = it->GetNext();
  		if (it == link)
  			continue;	// Don't compare to itself
  
  		// Look in the middle section [1 .. size-1]
  		for (int i = 1; i < (int) it->GetSize() - 1; i++)
  		{
  			// We use matching by exact position, not by id
  			if (it->GetAt(i) == node0->Pos())
  			{
  				// Found one. Split by making two new links.
  				m_road_layer->SplitLinkAtIndex(it, i, node0);
  				break;
  			}
  			if (it->GetAt(i) == node1->Pos())
  			{
  				// Found one. Split by making two new links.
  				m_road_layer->SplitLinkAtIndex(it, i, node1);
  				break;
  			}
  		}
  	}
  
  	bool bMayNeedSplit = true;
  	while (bMayNeedSplit)
  	{
  		bMayNeedSplit = false;
  
  		// Also look for places where a mid-point of this the new link shares a
  		// node with the mid-point of an existing link.
  		for (LinkEdit *it = m_road_layer->GetFirstLink(); it && !bMayNeedSplit; it = next)
  		{
  			next = it->GetNext();
  			if (it == link)
  				continue;	// Don't compare to itself
  
  			// Look in the middle section [1 .. size-1]
  			for (int i = 1; i < (int) it->GetSize() - 1 && !bMayNeedSplit; i++)
  			{
  				for (int j = 1; j < (int) link->GetSize() - 1 && !bMayNeedSplit; j++)
  				{
  					// We use matching by exact position, not by id
  					if (it->GetAt(i) == link->GetAt(j))
  					{
  						// Found one. Split both links.
  						NodeEdit *node = m_road_layer->AddNewNode();
  						node->SetPos(it->GetAt(i));
  
  						m_road_layer->SplitLinkAtIndex(it, i, node);
  
  						LinkEdit *link1, *link2;
  						m_road_layer->SplitLinkAtIndex(link, j, node, &link1, &link2);
  
  						// We need to continue checking the second part of our
  						// split link for further intersections.
  						link = link2;
  
  						// This will drop us out of all three for loops
  						bMayNeedSplit = true;
  					}
  				}
  			}
  		}
  	}
  }
  
  void VisitorOSM::MakeStructure()
  {
  	if (!m_struct_layer)
  	{
  		m_struct_layer = new vtStructureLayer;
  		m_struct_layer->SetProjection(m_proj);
  	}
  	if (m_eStructureType == ST_BUILDING)
  		MakeBuilding();
  
  	if (m_eStructureType == ST_LINEAR)
  		MakeLinear();
  }
  
  void VisitorOSM::MakeBuilding()
  {
  	// We expect the building to be closed, which means the last node should
  	// be the same as the first.  If not, something is wrong.
  	if (m_refs.size() < 4)
  	{
  		VTLOG("Bad building, id %d, only %d nodes\n", m_id, m_refs.size());
  		return;
  	}
  	if (m_refs[0] != m_refs[m_refs.size()-1])
  	{
  		VTLOG("Bad building, id %d, not closed\n", m_id);
  		return;
  	}
  
  	// Our polylines are implicitly closed so we don't need redundancy.
  	m_refs.erase(m_refs.end() - 1);
  
  	// Apply footprint
  	DLine2 foot(m_refs.size());
  	for (uint r = 0; r < m_refs.size(); r++)
  	{
  		int idx = m_refs[r];
  		foot[r] = m_nodes[idx].p;
  	}
  	// The order of vertices in OSM does not seem to have a consistent
  	// direction. We use a counter-clockwise convention.
  	PolyChecker pc;
  	if (pc.IsClockwisePolygon(foot))
  		foot.ReverseOrder();
  
  	vtBuilding *bld = m_struct_layer->AddNewBuilding();
  
  	// Make two levels: walls and a roof.
  	bld->SetFootprint(0, foot);
  	bld->SetFootprint(1, foot);
  
  	// Apply a default style of building
  	vtBuilding *pDefBld = GetClosestDefault(bld);
  	if (pDefBld)
  		bld->CopyStyleFrom(pDefBld, true);
  	else
  		bld->SetRoofType(ROOF_FLAT);
  
  	// Apply other building info, if we have it.
  	// Do we have both a height and a number of stories?
  	if (m_fHeight != -1 && m_iNumStories != -1 && m_iNumStories != 0)
  	{
  		bld->SetNumStories(m_iNumStories);
  		bld->GetLevel(0)->m_fStoryHeight = m_fHeight / m_iNumStories;
  	}
  	else if (m_fHeight != -1)
  	{
  		// We have height, but not number of stories. Estimate it.
  		int num = (int) (m_fHeight / 3.65);
  		if (num < 1)
  			num = 1;
  		bld->SetNumStories(num);
  		bld->GetLevel(0)->m_fStoryHeight = m_fHeight / num;
  	}
  	else if (m_iNumStories != -1)
  		bld->SetNumStories(m_iNumStories);
  
  	if (m_RoofType != NUM_ROOFTYPES)
  		bld->SetRoofType(m_RoofType);
  
  	if (m_Name != "")
  		bld->AddTag("name", m_Name);
  	if (m_URL != "")
  		bld->AddTag("url", m_URL);
  	if (m_id != -1)
  	{
  		char id_string[40];
  		sprintf(id_string, "%d", m_id);
  		bld->AddTag("id", id_string);
  	}
  }
  
  void VisitorOSM::MakeLinear()
  {
  	// We expect a linear feature to have at least 2 points.
  	if (m_refs.size() < 2)
  		return;
  
  	vtFence *ls = m_struct_layer->AddNewFence();
  
  	// Apply footprint
  	DLine2 foot(m_refs.size());
  	for (uint r = 0; r < m_refs.size(); r++)
  	{
  		int idx = m_refs[r];
  		foot[r] = m_nodes[idx].p;
  	}
  	ls->SetFencePoints(foot);
  
  	// Apply style;
  	ls->ApplyStyle(m_eLinearStyle);
  
  	if (m_id != -1)
  	{
  		char id_string[40];
  		sprintf(id_string, "%d", m_id);
  		ls->AddTag("id", id_string);
  	}
  }
  
  void VisitorOSM::StartPowerPole()
  {
  	if (!m_util_layer)
  	{
  		m_util_layer = new vtUtilityLayer;
  		m_util_layer->SetProjection(m_proj);
  		m_util_layer->SetModified(true);
  	}
  
  	OSMNode &node = m_nodes[m_id];
  
  	m_pole = m_util_layer->AddNewPole();
  	m_pole->m_id = m_id;
  	m_pole->m_p = node.p;
  
  	node.pole = m_pole;
  }
  
  void VisitorOSM::MakePowerLine()
  {
  	m_line = m_util_layer->AddNewLine();
  
  	m_line->m_poles.resize(m_refs.size());
  	for (uint r = 0; r < m_refs.size(); r++)
  	{
  		int idx = m_refs[r];
  
  		// Look for that node by id; if we don't find it, then it wasn't a tower;
  		// it was probably a start or end point at a non-tower feature.
  		NodeMap::iterator it = m_nodes.find(m_id);
  		if (it != m_nodes.end())
  		{
  			// Connect to a known pole.
  			m_line->m_poles[r] = m_nodes[idx].pole;
  		}
  		else
  		{
  			// We need to make a new pole node.
  			OSMNode &node = m_nodes[idx];
  
  			m_pole = m_util_layer->AddNewPole();
  			m_pole->m_id = idx;
  			m_pole->m_p = node.p;
  
  			node.pole = m_pole;
  
  			// Then we can connect it
  			m_line->m_poles[r] = m_pole;
  		}
  	}
  }
  
  /**
   * Import what we can from OpenStreetMap.
   */
  void Builder::ImportDataFromOSM(const wxString &strFileName, LayerArray &layers,
  	bool progress_callback(int))
  {
  	// Avoid trouble with '.' and ',' in Europe
  	//  OSM always has English punctuation
  	ScopedLocale normal_numbers(LC_NUMERIC, "C");
  
  	std::string fname_local = (const char *) strFileName.ToUTF8();
  
  	VisitorOSM visitor;
  	try
  	{
  		readXML(fname_local, visitor, progress_callback);
  	}
  	catch (xh_exception &ex)
  	{
  		DisplayAndLog(ex.getFormattedMessage().c_str());
  		return;
  	}
  
  	if (visitor.m_road_layer)
  	{
  		visitor.SetSignalLights();
  		layers.push_back(visitor.m_road_layer);
  	}
  
  	if (visitor.m_struct_layer)
  		layers.push_back(visitor.m_struct_layer);
  
  	if (visitor.m_util_layer)
  		layers.push_back(visitor.m_util_layer);
  }
  