alaCarte Maps
Renderer for OpenStreetMap tiles
mapcss_parser.cpp
Go to the documentation of this file.
1 
23 #include <boost/fusion/include/at_c.hpp>
24 #include <boost/spirit/include/qi.hpp>
25 #include <boost/filesystem/path.hpp>
26 #include <boost/filesystem/path.hpp>
27 #include <boost/lexical_cast.hpp>
28 #include <boost/thread/thread.hpp>
29 
30 #include "server/style.hpp"
31 #include "server/stylesheet.hpp"
32 
37 
38 
45 string TabToSpace(const string& str)
46 {
47  string ret = str;
48 
49  for(char& c : ret)
50  {
51  if(c == '\t')
52  c = ' ';
53  }
54 
55  return ret;
56 }
57 
59 // Visitors to access boost::variant
60 /*struct IntVisitor : boost::static_visitor<int>
61 {
62  int operator()(int i) const { return i; }
63  int operator()(const Color& color) const { BOOST_THROW_EXCEPTION(excp::ParseException() << excp::InfoWhat("Expected an integer!")); }
64  int operator()(double d) const { return static_cast<int>(d); }
65  int operator()(const string& s) const { return boost::lexical_cast<int>(s); }
66 };
67 
68 struct DoubleVisitor : boost::static_visitor<double>
69 {
70  double operator()(int i) const { return static_cast<double>(i); }
71  double operator()(const Color& color) const { BOOST_THROW_EXCEPTION(excp::ParseException() << excp::InfoWhat("Expected an integer!")); }
72  double operator()(double d) const { return d; }
73  double operator()(const string& s) const { return boost::lexical_cast<double>(s); }
74 };
75 
76 struct ColorVisitor : boost::static_visitor<Color>
77 {
78  Color operator()(int i) const { BOOST_THROW_EXCEPTION(excp::ParseException() << excp::InfoWhat("Expected a color!")); }
79  Color operator()(const Color& color) const { return color; }
80  Color operator()(double d) const { BOOST_THROW_EXCEPTION(excp::ParseException() << excp::InfoWhat("Expected a color!")); }
81  Color operator()(const string& s) const { return ResolveColorName(s); }
82 };
83 
84 struct StringVisitor : boost::static_visitor<string>
85 {
86  string operator()(int i) const { return boost::lexical_cast<string>(i); }
87  string operator()(const Color& color) const { return boost::lexical_cast<string>(color); }
88  string operator()(double d) const { return boost::lexical_cast<string>(d); }
89  string operator()(const string& s) const { return s; }
90 };*/
92 
98 /*Style::TextPosition SelectTextPosition(const SpecifierType& specifier)
99 {
100  string spec = boost::apply_visitor(StringVisitor(), specifier);
101  if(spec == "line")
102  {
103  return Style::POSITION_LINE;
104  }else if (spec == "center")
105  {
106  return Style::POSITION_CENTER;
107  }
108 
109  BOOST_THROW_EXCEPTION(excp::ParseException() << excp::InfoWhat("Expected a 'line' or 'center' !"));
110 }*/
111 
118 void MapCssParser::addAttributeToTemplate(StylePtr& style, const shared_ptr<AttributeCreator>& attrType, const string& specifier, const ParseInfo& info)
119 {
120  assert(style);
121  assert(attrType);
122 
123  try {
124  attrType->addAttribute(style, specifier, logger, info);
125  } catch(excp::ParseException& e) {
126 
127  // Something went wrong!
128  logger->warnStream() << "Failed to parse attribute in \"" + info.getSourceFile() + "\":";
129  logger->warnStream() << excp::ErrorOut<excp::InfoWhat>(e, "unknown reason!");
130  logger->warnStream() << "In line " << info.getLine() << " column " << info.getColumn() << ":";
131 
132  logger->warnStream() << "'" << TabToSpace(info.getLineContent()) << "'";
133  logger->warnStream() << string(info.getColumn(), ' ') << "^-here";
134  logger->warnStream() << "Attribute will be ignored!";
135  }
136 }
137 
144 void MapCssParser::applyStyleToRules(std::vector<RulePtr>& rules, const StylePtr& style)
145 {
146  for(auto& rule : rules)
147  {
148  rule->setStyleTemplate(style);
149  }
150 }
151 
161 {
162  switch(objType)
163  {
164  case obj::Any: type = Rule::Accept_Any; return next;
165  case obj::Node: type = Rule::Accept_Node; return next;
166  case obj::Way: type = Rule::Accept_Way; return next;
167  case obj::Relation: type = Rule::Accept_Relation; return next;
168  case obj::Line: type = Rule::Accept_Way; return boost::make_shared<LineSelector>(rule, next);
169  case obj::Area: type = Rule::Accept_Way; return boost::make_shared<AreaSelector>(rule, next);
170  default:
171  assert(false);
172  return next;
173  }
174 }
175 
185 {
186  switch(objType)
187  {
188  case obj::Any: return next;
189  case obj::Node: return boost::make_shared<ChildNodesSelector>(rule, next);
190  case obj::Way: return boost::make_shared<ChildWaysSelector>(rule, next);
191  case obj::Line: return boost::make_shared<ChildWaysSelector>(rule, boost::make_shared<LineSelector>(rule, next));
192  case obj::Area: return boost::make_shared<ChildWaysSelector>(rule, boost::make_shared<AreaSelector>(rule, next));
193  default:
194  assert(false);
195  return next;
196  }
197 }
198 
199 
207 SelectorPtr MapCssParser::createSelectorFromUnaryCondition(const SelectorPtr& next, const shared_ptr<Rule>& rule, const UnaryCondition& condition)
208 {
209  const string& tag = fsio::at_c<1>(condition);
210 
211  switch(fsio::at_c<0>(condition))
212  {
213  case op::HasTag: return boost::make_shared<HasTagSelector>(rule, next, tag);
214  case op::Not: return boost::make_shared<HasNotTagSelector>(rule, next, tag);
215  case op::Minus:
216  default:
217  assert(!"This unary condition is not implemented!");
218  return next;
219  }
220 }
221 
222 
230 SelectorPtr MapCssParser::createSelectorFromBinaryCondition(const SelectorPtr& next, const shared_ptr<Rule>& rule, const BinaryCondition& condition)
231 {
232  const string& tag = fsio::at_c<0>(condition);
233  const string& value = fsio::at_c<2>(condition);
234 
235  try {
236  switch(fsio::at_c<1>(condition))
237  {
238  case op::Equal: return boost::make_shared<TagEqualsSelector>(rule, next, tag, value);
239  case op::Unequal: return boost::make_shared<TagUnequalsSelector>(rule, next, tag, value);
240  case op::SameAs: return boost::make_shared<TagMatchesSelector>(rule, next, tag, value);
241  case op::LessThen: return boost::make_shared<TagSmallerSelector>(rule, next, tag, boost::lexical_cast<int>(value));
242  case op::GreaterThen: return boost::make_shared<TagLargerSelector>(rule, next, tag, boost::lexical_cast<int>(value));
243  case op::LessEqual: return boost::make_shared<TagSmallerEqualsSelector>(rule, next, tag, boost::lexical_cast<int>(value));
244  case op::GreaterEqual: return boost::make_shared<TagLargerEqualsSelector>(rule, next, tag, boost::lexical_cast<int>(value));
245  default:
246  assert(!"This binary condition is not implemented!");
247  return next;
248  }
249  }catch (boost::bad_lexical_cast&)
250  {
251  LOG_SEV(style_log, warning) << "Can not compare non numeric values";
252  return next;
253  }
254 }
255 
263 SelectorPtr MapCssParser::createSelectorFromCondition(const SelectorPtr& next, const shared_ptr<Rule>& rule, const ConditionType& condition)
264 {
265  switch(condition.which())
266  {
267  case 0:
268  // Unary condition
269  return createSelectorFromUnaryCondition(next, rule, boost::get<UnaryCondition>(condition));
270  case 1:
271  // Binary condition
272  return createSelectorFromBinaryCondition(next, rule, boost::get<BinaryCondition>(condition));
273  default:
274  assert(!"This is not a condition!");
275  return next;
276  }
277 }
278 
286 RulePtr MapCssParser::createSelectorChain(const std::vector<SelectorItem>& items)
287 {
288  RulePtr rule = boost::make_shared<Rule>(geodata);
289  ApplySelectorPtr applier = boost::make_shared<ApplySelector>(rule);
290  SelectorPtr topmost = boost::static_pointer_cast<Selector>(applier);
291 
292  Zoom zoom;
293  Rule::AcceptableTypes acceptableType = Rule::Accept_Any;
294 
295  for(auto it = items.crbegin();
296  it != items.crend();
297  ++it)
298  {
299  const SelectorItem& item = *it;
300 
301  for(auto& cond : item.conditions)
302  {
303  topmost = createSelectorFromCondition(topmost, rule, cond);
304  }
305 
306  zoom.focus(item.zoom);
307 
308  if(it == items.crend()-1) {
309  topmost = createSelectorFromObjectType(topmost, rule, item.objectType, acceptableType);
310  } else {
311  topmost = createChildSelectorFromObjectType(topmost, rule, item.objectType);
312  }
313  }
314 
315  //topmost = createSelectorFromZoom(topmost, rule, zoom);
316 
317  rule->first = topmost;
318  rule->setZoomBounds(zoom.bottom, zoom.top);
319  rule->setAcceptableType(acceptableType);
320 
321  return rule;
322 }
323 
329 void MapCssParser::warnUnsupportedAttribute(const string& attribute) const {
330  LOG_SEV(style_log, warning) << "Unsupported attribute '" << attribute << "' was ignored!";
331 }
332 
338 MapCssParser::MapCssParser(const shared_ptr<Geodata>& geodata)
339  : geodata(geodata)
340 {
341 }
342 
343 
350 void MapCssParser::load(const string& path)
351 {
352  logger = boost::make_shared<ParserLogger>(path);
353  LOG_SEV(style_log, info) << "Load stylesheet[" << path << "]";
354  MapCSSGrammar mapscc_grammar(*this);
355 
356 
357  std::ifstream cssStream(path.c_str());
358 
359 
360  if(!cssStream)
361  {
362  LOG_SEV(style_log, warning) << "Failed to load stylesheet '" << path << "'!";
363  BOOST_THROW_EXCEPTION(excp::FileNotFoundException() << excp::InfoFileName(path));
364  }
365  cssStream >> std::noskipws;
366 
367  std::string buffer;
368  buffer.reserve(1024 * 8);
369 
370  std::copy(std::istream_iterator<char>(cssStream), std::istream_iterator<char>(), std::back_inserter(buffer));
371 
372 
373  GrammarIterator position_begin(buffer.begin(), buffer.end(), path);
374  GrammarIterator position_end;
375 
376  StylesheetPtr stylesheet;
377  try
378  {
379  try
380  {
381  bool r = qi::phrase_parse(position_begin, position_end, mapscc_grammar, CommentSkipper(), stylesheet);
382 
383  if(!r || position_begin != position_end || !stylesheet)
384  {
385  BOOST_THROW_EXCEPTION(excp::ParseException() << excp::InfoWhat("Unexpected error while parsing!"));
386  }
387 
388  } catch(const qi::expectation_failure<GrammarIterator>& e)
389  {
390  const classic::file_position_base<std::string>& pos = e.first.get_position();
391 
392  BOOST_THROW_EXCEPTION(excp::ParseException()
393  << excp::InfoFailureLine(pos.line)
394  << excp::InfoFailureColumn(pos.column)
395  << excp::InfoFailureLineContent(TabToSpace(e.first.get_currentline()))
396  << excp::InfoWhat("Illegal syntax! Expected valid " + boost::lexical_cast<string>(e.what_) + "!"));
397  }
398 
399  } catch(excp::ParseException& e)
400  {
402  return;
403  }
404 
405  parsedStylesheet = stylesheet;
406 }
407 
415 shared_ptr<Stylesheet> Stylesheet::Load(const boost::filesystem::path& path, const shared_ptr<Geodata>& geodata, int timeout)
416 {
417 
418  shared_ptr<MapCssParser> parser = boost::make_shared<MapCssParser>(geodata);
419 
420  boost::thread timeoutThread(boost::bind(&MapCssParser::load, parser, path.string()));
421 
422  if(!timeoutThread.timed_join(boost::posix_time::millisec(timeout)))
423  {
424  BOOST_THROW_EXCEPTION(excp::TimeoutException() << excp::InfoWhat("Parse timeout!"));
425  }
426 
427 
428  shared_ptr<Stylesheet> stylesheet = parser->parsedStylesheet;
429 
430  if(!stylesheet)
431  {
432  throw parser->catchedException;
433  }
434 
435  stylesheet->path = path;
436  return stylesheet;
437 }
Checks if a value is recognised by a regex.
Definition: mapcss_def.hpp:68
MapCssParser(const shared_ptr< Geodata > &geodata)
Initializes the parser.
Checks if two values are equal.
Definition: mapcss_def.hpp:66
Thrown if no more time is left.
Definition: exceptions.hpp:87
SelectorPtr createChildSelectorFromObjectType(const SelectorPtr &next, const shared_ptr< Rule > &rule, obj::ObjectTypeEnum objType)
Creates a selector, selecting children of the specified type.
Checks if a value is greater then another.
Definition: mapcss_def.hpp:70
fsio::vector< op::UnaryTypesEnum, string > UnaryCondition
Stores informations about an unary condition.
SelectorPtr createSelectorFromBinaryCondition(const SelectorPtr &next, const shared_ptr< Rule > &rule, const BinaryCondition &condition)
Creates a selector using an binary operator.
The grammar for the mapcss format.
Checks if a value is lesser than another.
Definition: mapcss_def.hpp:69
SelectorPtr createSelectorFromCondition(const SelectorPtr &next, const shared_ptr< Rule > &rule, const ConditionType &condition)
Creates a selector for a given condition.
shared_ptr< Selector > SelectorPtr
Shortcut for a shared pointer to the base of all selectors.
Definition: mapcss_def.hpp:135
shared_ptr< Geodata > geodata
The geodata used to inject them in the rules.
Thrown if the parsing fails.
Definition: exceptions.hpp:85
Grammar to skipp c/c++ style comments.
Selects nodes.
Definition: mapcss_def.hpp:51
boost::error_info< struct TagParserLogger, shared_ptr< ParserLogger > > InfoParserLogger
Contains the logger used while parsing.
Definition: exceptions.hpp:57
Checks if a geoobject has a certain tag.
Definition: mapcss_def.hpp:78
shared_ptr< Stylesheet > parsedStylesheet
The parsed stylesheet.
Zoom zoom
The zoom which should be selected.
shared_ptr< ApplySelector > ApplySelectorPtr
Shortcut for a shared pointer to thr apply selector.
Definition: mapcss_def.hpp:138
Selects relations.
Definition: mapcss_def.hpp:53
fsio::vector< string, op::BinaryTypesEnum, string > BinaryCondition
Stores informations about an binary condition.
std::vector< ConditionType > conditions
The condition of the item.
Selects ways.
Definition: mapcss_def.hpp:52
shared_ptr< Rule > RulePtr
Shortcut for a shared pointer to rule.
Definition: mapcss_def.hpp:132
classic::position_iterator2< StringIterator > GrammarIterator
Iterator wrapping the file iterator and to be used in all grammars.
Definition: mapcss_def.hpp:148
shared_ptr< StyleTemplate > StylePtr
Shortcut for a shared pointer to style.
Definition: mapcss_def.hpp:141
std::string string
Definition: settings.hpp:110
#define LOG_SEV(log, lvl)
Definition: settings.hpp:78
RulePtr createSelectorChain(const std::vector< SelectorItem > &items)
Creates a chain of selectors from given subselectors.
boost::error_info< struct TagWhatInfo, string > InfoWhat
Use this info to give an what msg to the exception.
Definition: exceptions.hpp:41
const unsigned int & getLine() const
Definition: parse_info.hpp:50
boost::error_info< struct TagFailureLine, int > InfoFailureLine
Specifies the Line in the file where the failure appeared.
Definition: exceptions.hpp:51
SelectorPtr createSelectorFromObjectType(const SelectorPtr &next, const shared_ptr< Rule > &rule, obj::ObjectTypeEnum objType, Rule::AcceptableTypes &type)
Creates an selector used to match geoobject types.
boost::variant< UnaryCondition, BinaryCondition > ConditionType
Possible Conditions.
boost::error_info< struct TagFailureLineContent, string > InfoFailureLineContent
Contains the content of the line where a failure appeared.
Definition: exceptions.hpp:55
void addAttributeToTemplate(StylePtr &style, const shared_ptr< AttributeCreator > &attrType, const string &specifier, const ParseInfo &info)
Converts a given string into a text position.
Thrown if a file was not found.
Definition: exceptions.hpp:73
Checks if a geoobject does not have a certain tag.
Definition: mapcss_def.hpp:79
Selects lines (unclosed ways)
Definition: mapcss_def.hpp:55
ObjectTypeEnum
Enum with object selectors.
Definition: mapcss_def.hpp:49
Checks if two values are not equal.
Definition: mapcss_def.hpp:67
excp::ParseException catchedException
The exception that may be catched.
Checks if a value is lesser or equal to another.
Definition: mapcss_def.hpp:71
SelectorPtr createSelectorFromUnaryCondition(const SelectorPtr &next, const shared_ptr< Rule > &rule, const UnaryCondition &condition)
Creates a selector using an unary operator.
string TabToSpace(const string &str)
This file is part of alaCarte.
void applyStyleToRules(std::vector< RulePtr > &rules, const StylePtr &style)
Sets a given style as the style of given rules.
obj::ObjectTypeEnum objectType
The type of the object, which should be selected.
Selects areas (closed ways)
Definition: mapcss_def.hpp:54
Checks if a value is greater or equal to another.
Definition: mapcss_def.hpp:72
shared_ptr< ParserLogger > logger
Output logger for errors and warnings.
const unsigned int & getColumn() const
Definition: parse_info.hpp:49
Represents a part of the style selector.
shared_ptr< Stylesheet > StylesheetPtr
Shortcut for a shared pointer to stylesheet.
Definition: mapcss_def.hpp:126
Simple structure to store a zoom range.
boost::error_info< struct TagFailureColumn, int > InfoFailureColumn
Specifies the Column in the Line in the file where the failure appeared.
Definition: exceptions.hpp:53
static shared_ptr< Stylesheet > Load(const boost::filesystem::path &path, const shared_ptr< Geodata > &geodata, int timeout)
Parses the MapCSS Stylesheet at the given path and returns a new Stylesheet containing the defined ru...
void load(const string &path)
Loads a file from the given path and tries to parse the content into a stylesheet.
const string & getSourceFile() const
Definition: parse_info.hpp:48
void warnUnsupportedAttribute(const string &attribute) const
Emits a warning for unknown mapcss attribute.
boost::error_info< struct TagFileName, string > InfoFileName
Use this to inform about a file name.
Definition: exceptions.hpp:43
Selects everything.
Definition: mapcss_def.hpp:56
const string & getLineContent() const
Definition: parse_info.hpp:51
AcceptableTypes
Definition: rule.hpp:38