Replace reading borders with reading polys

This commit is contained in:
Ilya Zverev 2015-03-12 18:25:44 +03:00 committed by Alex Zolotarev
parent 3347c12b3e
commit 0dcc180da3
2 changed files with 45 additions and 152 deletions

View file

@ -1,173 +1,66 @@
#include "borders_generator.hpp"
#include "osm_xml_parser.hpp"
#include "../base/std_serialization.hpp"
#include "../base/logging.hpp"
#include "../indexer/mercator.hpp"
#include "../coding/file_reader.hpp"
#include "../coding/file_writer.hpp"
#include "../coding/streams_sink.hpp"
#include "../coding/parse_xml.hpp"
#include "../std/list.hpp"
#include "../std/vector.hpp"
#include "../base/logging.hpp"
#include "../base/string_utils.hpp"
#include "../std/fstream.hpp"
#include "../std/iostream.hpp"
#include "../std/sstream.hpp"
namespace osm
{
class BordersCreator
bool ReadPolygon(istream & stream, m2::RegionD & region, string const & filename)
{
vector<m2::RegionD> & m_borders;
OsmRawData const & m_data;
m2::RegionD m_currentRegion;
string line, name;
double lon, lat;
public:
BordersCreator(vector<m2::RegionD> & outBorders, OsmRawData const & data)
: m_borders(outBorders), m_data(data)
// read ring id, fail if it's empty
getline(stream, name);
if (name.empty() || name == "END")
return false;
while (stream.good())
{
getline(stream, line);
strings::Trim(line);
if (line.empty())
continue;
if (line == "END")
break;
istringstream iss(line);
iss >> lon >> lat;
CHECK(!iss.fail(), ("Incorrect data in", filename));
region.AddPoint(MercatorBounds::FromLatLon(lat, lon));
}
void CreateFromWays(OsmWays const & ways)
{
for (OsmWays::const_iterator it = ways.begin(); it != ways.end(); ++it)
{
// clear region
m_currentRegion = m2::RegionD();
it->ForEachPoint(*this);
CHECK(m_currentRegion.IsValid(), ("Invalid region"));
m_borders.push_back(m_currentRegion);
}
}
void operator()(OsmId nodeId)
{
try
{
OsmNode node = m_data.NodeById(nodeId);
m_currentRegion.AddPoint(MercatorBounds::FromLatLon(node.m_lat, node.m_lon));
}
catch (OsmRawData::OsmInvalidIdException const & e)
{
LOG(LWARNING, (e.what()));
}
}
};
void GenerateBordersFromOsm(string const & tagAndOptValue,
string const & osmFile,
string const & outFile)
{
OsmRawData osmData;
{
FileReader file(osmFile);
ReaderSource<FileReader> source(file);
OsmXmlParser parser(osmData);
CHECK(ParseXML(source, parser), ("Invalid XML"));
}
// extract search tag key and value
size_t const delimeterPos = tagAndOptValue.find('=');
string const searchKey = tagAndOptValue.substr(0, delimeterPos);
string searchValue;
if (delimeterPos != string::npos)
searchValue = tagAndOptValue.substr(delimeterPos + 1, string::npos);
// find country borders relation
OsmIds relationIds;
if (searchValue.empty())
relationIds = osmData.RelationsByKey(searchKey);
else
relationIds = osmData.RelationsByTag(OsmTag(searchKey, searchValue));
CHECK(!relationIds.empty(), ("No relation found with tag", searchKey, searchValue));
CHECK_EQUAL(relationIds.size(), 1, ("Found more than one relation with tag",
searchKey, searchValue));
OsmRelation countryRelation = osmData.RelationById(relationIds[0]);
// get country name
string countryName;
if (!countryRelation.TagValueByKey("name:en", countryName))
countryRelation.TagValueByKey("name", countryName);
LOG(LINFO, ("Processing boundaries for country", countryName));
// find border ways
OsmIds wayIdsOuterRole = countryRelation.MembersByTypeAndRole("way", "outer");
OsmIds wayIdsNoRole = countryRelation.MembersByTypeAndRole("way", "");
// collect all ways
list<OsmWay> ways;
for(size_t i = 0; i < wayIdsOuterRole.size(); ++i)
ways.push_back(osmData.WayById(wayIdsOuterRole[i]));
for(size_t i = 0; i < wayIdsNoRole.size(); ++i)
ways.push_back(osmData.WayById(wayIdsNoRole[i]));
CHECK(!ways.empty(), ("No any ways in country border"));
// merge all collected ways
OsmWays mergedWays;
do
{
OsmId lastMergedWayId = -1; // for debugging
OsmWay merged = ways.front();
size_t lastSize = ways.size();
ways.pop_front();
// repeat until we merge everything
while (lastSize > ways.size())
{
lastSize = ways.size();
for (list<OsmWay>::iterator it = ways.begin(); it != ways.end(); ++it)
{
if (merged.MergeWith(*it))
{
lastMergedWayId = it->Id();
ways.erase(it);
break;
}
}
}
if (!merged.IsClosed())
{
LOG(LERROR, ("Way merge unsuccessful as borders are not closed. Last merged id:",
lastMergedWayId == -1 ? merged.Id() : lastMergedWayId));
}
else
{
LOG(LINFO, ("Successfully merged boundaries with points count:", merged.PointsCount()));
mergedWays.push_back(merged);
}
} while (!ways.empty());
CHECK(!mergedWays.empty(), ("No borders were generated for country:", countryName));
// save generated borders
vector<m2::RegionD> borders;
BordersCreator doCreateBorders(borders, osmData);
doCreateBorders.CreateFromWays(mergedWays);
CHECK_EQUAL(mergedWays.size(), borders.size(), ("Can't generate borders from ways"));
FileWriter writer(outFile);
stream::SinkWriterStream<Writer> stream(writer);
stream << borders;
LOG(LINFO, ("Saved", borders.size(), "border(s) for", countryName));
// drop inner rings
return name[0] != '!';
}
bool LoadBorders(string const & borderFile, vector<m2::RegionD> & outBorders)
{
try
{
FileReader file(borderFile);
ReaderSource<FileReader> source(file);
stream::SinkReaderStream<ReaderSource<FileReader> > stream(source);
stream >> outBorders;
CHECK(!outBorders.empty(), ("No borders were loaded from", borderFile));
}
catch (FileReader::OpenException const &)
ifstream stream(borderFile);
string line;
if (!getline(stream, line).good()) // skip title
{
LOG(LERROR, ("Polygon file is empty:", borderFile));
return false;
}
m2::RegionD currentRegion;
while (ReadPolygon(stream, currentRegion, borderFile))
{
CHECK(currentRegion.IsValid(), ("Invalid region in", borderFile));
outBorders.push_back(currentRegion);
currentRegion = m2::RegionD();
}
CHECK(!outBorders.empty(), ("No borders were loaded from", borderFile));
return true;
}
}

View file

@ -6,7 +6,7 @@
#include "../std/string.hpp"
#define BORDERS_DIR "borders/"
#define BORDERS_EXTENSION ".borders"
#define BORDERS_EXTENSION ".poly"
namespace borders
{