From 1acbb7bcdf9a7b424e318941658c31914a22b98a Mon Sep 17 00:00:00 2001 From: Constantin Shalnev Date: Fri, 18 Sep 2015 14:34:04 +0300 Subject: [PATCH 1/5] Extended drules struct --- indexer/drules_struct.pb.cc | 32 +++++++++ indexer/drules_struct.pb.h | 71 ++++++++++++++++++++ indexer/drules_struct.proto | 1 + tools/kothic/drules_struct_pb2.py | 27 +++++--- tools/python/stylesheet/drules_struct_pb2.py | 27 +++++--- 5 files changed, 138 insertions(+), 20 deletions(-) diff --git a/indexer/drules_struct.pb.cc b/indexer/drules_struct.pb.cc index cc95ab4de4..c1450e53cf 100644 --- a/indexer/drules_struct.pb.cc +++ b/indexer/drules_struct.pb.cc @@ -3569,6 +3569,7 @@ const int DrawElementProto::kCaptionFieldNumber; const int DrawElementProto::kCircleFieldNumber; const int DrawElementProto::kPathTextFieldNumber; const int DrawElementProto::kShieldFieldNumber; +const int DrawElementProto::kApplyIfFieldNumber; #endif // !_MSC_VER DrawElementProto::DrawElementProto() @@ -3624,6 +3625,7 @@ DrawElementProto::DrawElementProto(const DrawElementProto& from) } void DrawElementProto::SharedCtor() { + ::google::protobuf::internal::GetEmptyString(); _cached_size_ = 0; scale_ = 0; area_ = NULL; @@ -3698,6 +3700,7 @@ void DrawElementProto::Clear() { } } lines_.Clear(); + apply_if_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); mutable_unknown_fields()->clear(); } @@ -3818,6 +3821,20 @@ bool DrawElementProto::MergePartialFromCodedStream( } else { goto handle_unusual; } + if (input->ExpectTag(74)) goto parse_apply_if; + break; + } + + // repeated string apply_if = 9; + case 9: { + if (tag == 74) { + parse_apply_if: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_apply_if())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(74)) goto parse_apply_if; if (input->ExpectAtEnd()) goto success; break; } @@ -3894,6 +3911,12 @@ void DrawElementProto::SerializeWithCachedSizes( 8, this->shield(), output); } + // repeated string apply_if = 9; + for (int i = 0; i < this->apply_if_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteString( + 9, this->apply_if(i), output); + } + output->WriteRaw(unknown_fields().data(), unknown_fields().size()); // @@protoc_insertion_point(serialize_end:DrawElementProto) @@ -3961,6 +3984,13 @@ int DrawElementProto::ByteSize() const { this->lines(i)); } + // repeated string apply_if = 9; + total_size += 1 * this->apply_if_size(); + for (int i = 0; i < this->apply_if_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->apply_if(i)); + } + total_size += unknown_fields().size(); GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); @@ -3977,6 +4007,7 @@ void DrawElementProto::CheckTypeAndMergeFrom( void DrawElementProto::MergeFrom(const DrawElementProto& from) { GOOGLE_CHECK_NE(&from, this); lines_.MergeFrom(from.lines_); + apply_if_.MergeFrom(from.apply_if_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_scale()) { set_scale(from.scale()); @@ -4044,6 +4075,7 @@ void DrawElementProto::Swap(DrawElementProto* other) { std::swap(circle_, other->circle_); std::swap(path_text_, other->path_text_); std::swap(shield_, other->shield_); + apply_if_.Swap(&other->apply_if_); std::swap(_has_bits_[0], other->_has_bits_[0]); _unknown_fields_.swap(other->_unknown_fields_); std::swap(_cached_size_, other->_cached_size_); diff --git a/indexer/drules_struct.pb.h b/indexer/drules_struct.pb.h index 9d324dcaf9..90a6bf80d0 100644 --- a/indexer/drules_struct.pb.h +++ b/indexer/drules_struct.pb.h @@ -1555,6 +1555,22 @@ class DrawElementProto : public ::google::protobuf::MessageLite { inline ::ShieldRuleProto* release_shield(); inline void set_allocated_shield(::ShieldRuleProto* shield); + // repeated string apply_if = 9; + inline int apply_if_size() const; + inline void clear_apply_if(); + static const int kApplyIfFieldNumber = 9; + inline const ::std::string& apply_if(int index) const; + inline ::std::string* mutable_apply_if(int index); + inline void set_apply_if(int index, const ::std::string& value); + inline void set_apply_if(int index, const char* value); + inline void set_apply_if(int index, const char* value, size_t size); + inline ::std::string* add_apply_if(); + inline void add_apply_if(const ::std::string& value); + inline void add_apply_if(const char* value); + inline void add_apply_if(const char* value, size_t size); + inline const ::google::protobuf::RepeatedPtrField< ::std::string>& apply_if() const; + inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_apply_if(); + // @@protoc_insertion_point(class_scope:DrawElementProto) private: inline void set_has_scale(); @@ -1583,6 +1599,7 @@ class DrawElementProto : public ::google::protobuf::MessageLite { ::CircleRuleProto* circle_; ::PathTextRuleProto* path_text_; ::ShieldRuleProto* shield_; + ::google::protobuf::RepeatedPtrField< ::std::string> apply_if_; ::google::protobuf::int32 scale_; #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER friend void protobuf_AddDesc_drules_5fstruct_2eproto_impl(); @@ -3529,6 +3546,60 @@ inline void DrawElementProto::set_allocated_shield(::ShieldRuleProto* shield) { // @@protoc_insertion_point(field_set_allocated:DrawElementProto.shield) } +// repeated string apply_if = 9; +inline int DrawElementProto::apply_if_size() const { + return apply_if_.size(); +} +inline void DrawElementProto::clear_apply_if() { + apply_if_.Clear(); +} +inline const ::std::string& DrawElementProto::apply_if(int index) const { + // @@protoc_insertion_point(field_get:DrawElementProto.apply_if) + return apply_if_.Get(index); +} +inline ::std::string* DrawElementProto::mutable_apply_if(int index) { + // @@protoc_insertion_point(field_mutable:DrawElementProto.apply_if) + return apply_if_.Mutable(index); +} +inline void DrawElementProto::set_apply_if(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:DrawElementProto.apply_if) + apply_if_.Mutable(index)->assign(value); +} +inline void DrawElementProto::set_apply_if(int index, const char* value) { + apply_if_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:DrawElementProto.apply_if) +} +inline void DrawElementProto::set_apply_if(int index, const char* value, size_t size) { + apply_if_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:DrawElementProto.apply_if) +} +inline ::std::string* DrawElementProto::add_apply_if() { + return apply_if_.Add(); +} +inline void DrawElementProto::add_apply_if(const ::std::string& value) { + apply_if_.Add()->assign(value); + // @@protoc_insertion_point(field_add:DrawElementProto.apply_if) +} +inline void DrawElementProto::add_apply_if(const char* value) { + apply_if_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:DrawElementProto.apply_if) +} +inline void DrawElementProto::add_apply_if(const char* value, size_t size) { + apply_if_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:DrawElementProto.apply_if) +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +DrawElementProto::apply_if() const { + // @@protoc_insertion_point(field_list:DrawElementProto.apply_if) + return apply_if_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +DrawElementProto::mutable_apply_if() { + // @@protoc_insertion_point(field_mutable_list:DrawElementProto.apply_if) + return &apply_if_; +} + // ------------------------------------------------------------------- // ClassifElementProto diff --git a/indexer/drules_struct.proto b/indexer/drules_struct.proto index 9a18a68497..401a381004 100644 --- a/indexer/drules_struct.proto +++ b/indexer/drules_struct.proto @@ -111,6 +111,7 @@ message DrawElementProto optional CircleRuleProto circle = 6; optional PathTextRuleProto path_text = 7; optional ShieldRuleProto shield = 8; + repeated string apply_if = 9; } message ClassifElementProto diff --git a/tools/kothic/drules_struct_pb2.py b/tools/kothic/drules_struct_pb2.py index d30e24a482..1a177d2b50 100644 --- a/tools/kothic/drules_struct_pb2.py +++ b/tools/kothic/drules_struct_pb2.py @@ -19,7 +19,7 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='drules_struct.proto', package='', - serialized_pb=_b('\n\x13\x64rules_struct.proto\"*\n\x0c\x44\x61shDotProto\x12\n\n\x02\x64\x64\x18\x01 \x03(\x01\x12\x0e\n\x06offset\x18\x02 \x01(\x01\":\n\x0cPathSymProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04step\x18\x02 \x02(\x01\x12\x0e\n\x06offset\x18\x03 \x01(\x01\"\xaf\x01\n\rLineRuleProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\x12\x1e\n\x07pathsym\x18\x05 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"\x9c\x01\n\x0cLineDefProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x1e\n\x07pathsym\x18\x04 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"O\n\rAreaRuleProto\x12\r\n\x05\x63olor\x18\x01 \x02(\r\x12\x1d\n\x06\x62order\x18\x02 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"I\n\x0fSymbolRuleProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x16\n\x0e\x61pply_for_type\x18\x02 \x01(\x05\x12\x10\n\x08priority\x18\x03 \x02(\x05\"j\n\x0f\x43\x61ptionDefProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08offset_x\x18\x04 \x01(\x05\x12\x10\n\x08offset_y\x18\x05 \x01(\x05\"l\n\x10\x43\x61ptionRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"a\n\x0f\x43ircleRuleProto\x12\x0e\n\x06radius\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1d\n\x06\x62order\x18\x03 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\"m\n\x11PathTextRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"X\n\x0fShieldRuleProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08priority\x18\x04 \x02(\x05\"\x8f\x02\n\x10\x44rawElementProto\x12\r\n\x05scale\x18\x01 \x02(\x05\x12\x1d\n\x05lines\x18\x02 \x03(\x0b\x32\x0e.LineRuleProto\x12\x1c\n\x04\x61rea\x18\x03 \x01(\x0b\x32\x0e.AreaRuleProto\x12 \n\x06symbol\x18\x04 \x01(\x0b\x32\x10.SymbolRuleProto\x12\"\n\x07\x63\x61ption\x18\x05 \x01(\x0b\x32\x11.CaptionRuleProto\x12 \n\x06\x63ircle\x18\x06 \x01(\x0b\x32\x10.CircleRuleProto\x12%\n\tpath_text\x18\x07 \x01(\x0b\x32\x12.PathTextRuleProto\x12 \n\x06shield\x18\x08 \x01(\x0b\x32\x10.ShieldRuleProto\"G\n\x13\x43lassifElementProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\"\n\x07\x65lement\x18\x02 \x03(\x0b\x32\x11.DrawElementProto\"4\n\x0e\x43ontainerProto\x12\"\n\x04\x63ont\x18\x01 \x03(\x0b\x32\x14.ClassifElementProto*4\n\x08LineJoin\x12\r\n\tROUNDJOIN\x10\x00\x12\r\n\tBEVELJOIN\x10\x01\x12\n\n\x06NOJOIN\x10\x02*3\n\x07LineCap\x12\x0c\n\x08ROUNDCAP\x10\x00\x12\x0b\n\x07\x42UTTCAP\x10\x01\x12\r\n\tSQUARECAP\x10\x02\x42\x02H\x03') + serialized_pb=_b('\n\x13\x64rules_struct.proto\"*\n\x0c\x44\x61shDotProto\x12\n\n\x02\x64\x64\x18\x01 \x03(\x01\x12\x0e\n\x06offset\x18\x02 \x01(\x01\":\n\x0cPathSymProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04step\x18\x02 \x02(\x01\x12\x0e\n\x06offset\x18\x03 \x01(\x01\"\xaf\x01\n\rLineRuleProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\x12\x1e\n\x07pathsym\x18\x05 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"\x9c\x01\n\x0cLineDefProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x1e\n\x07pathsym\x18\x04 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"O\n\rAreaRuleProto\x12\r\n\x05\x63olor\x18\x01 \x02(\r\x12\x1d\n\x06\x62order\x18\x02 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"I\n\x0fSymbolRuleProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x16\n\x0e\x61pply_for_type\x18\x02 \x01(\x05\x12\x10\n\x08priority\x18\x03 \x02(\x05\"j\n\x0f\x43\x61ptionDefProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08offset_x\x18\x04 \x01(\x05\x12\x10\n\x08offset_y\x18\x05 \x01(\x05\"l\n\x10\x43\x61ptionRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"a\n\x0f\x43ircleRuleProto\x12\x0e\n\x06radius\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1d\n\x06\x62order\x18\x03 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\"m\n\x11PathTextRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"X\n\x0fShieldRuleProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08priority\x18\x04 \x02(\x05\"\xa1\x02\n\x10\x44rawElementProto\x12\r\n\x05scale\x18\x01 \x02(\x05\x12\x1d\n\x05lines\x18\x02 \x03(\x0b\x32\x0e.LineRuleProto\x12\x1c\n\x04\x61rea\x18\x03 \x01(\x0b\x32\x0e.AreaRuleProto\x12 \n\x06symbol\x18\x04 \x01(\x0b\x32\x10.SymbolRuleProto\x12\"\n\x07\x63\x61ption\x18\x05 \x01(\x0b\x32\x11.CaptionRuleProto\x12 \n\x06\x63ircle\x18\x06 \x01(\x0b\x32\x10.CircleRuleProto\x12%\n\tpath_text\x18\x07 \x01(\x0b\x32\x12.PathTextRuleProto\x12 \n\x06shield\x18\x08 \x01(\x0b\x32\x10.ShieldRuleProto\x12\x10\n\x08\x61pply_if\x18\t \x03(\t\"G\n\x13\x43lassifElementProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\"\n\x07\x65lement\x18\x02 \x03(\x0b\x32\x11.DrawElementProto\"4\n\x0e\x43ontainerProto\x12\"\n\x04\x63ont\x18\x01 \x03(\x0b\x32\x14.ClassifElementProto*4\n\x08LineJoin\x12\r\n\tROUNDJOIN\x10\x00\x12\r\n\tBEVELJOIN\x10\x01\x12\n\n\x06NOJOIN\x10\x02*3\n\x07LineCap\x12\x0c\n\x08ROUNDCAP\x10\x00\x12\x0b\n\x07\x42UTTCAP\x10\x01\x12\r\n\tSQUARECAP\x10\x02\x42\x02H\x03') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -44,8 +44,8 @@ _LINEJOIN = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=1539, - serialized_end=1591, + serialized_start=1557, + serialized_end=1609, ) _sym_db.RegisterEnumDescriptor(_LINEJOIN) @@ -71,8 +71,8 @@ _LINECAP = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=1593, - serialized_end=1644, + serialized_start=1611, + serialized_end=1662, ) _sym_db.RegisterEnumDescriptor(_LINECAP) @@ -703,6 +703,13 @@ _DRAWELEMENTPROTO = _descriptor.Descriptor( message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), + _descriptor.FieldDescriptor( + name='apply_if', full_name='DrawElementProto.apply_if', index=8, + number=9, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), ], extensions=[ ], @@ -715,7 +722,7 @@ _DRAWELEMENTPROTO = _descriptor.Descriptor( oneofs=[ ], serialized_start=1139, - serialized_end=1410, + serialized_end=1428, ) @@ -751,8 +758,8 @@ _CLASSIFELEMENTPROTO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1412, - serialized_end=1483, + serialized_start=1430, + serialized_end=1501, ) @@ -781,8 +788,8 @@ _CONTAINERPROTO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1485, - serialized_end=1537, + serialized_start=1503, + serialized_end=1555, ) _LINERULEPROTO.fields_by_name['dashdot'].message_type = _DASHDOTPROTO diff --git a/tools/python/stylesheet/drules_struct_pb2.py b/tools/python/stylesheet/drules_struct_pb2.py index d30e24a482..1a177d2b50 100644 --- a/tools/python/stylesheet/drules_struct_pb2.py +++ b/tools/python/stylesheet/drules_struct_pb2.py @@ -19,7 +19,7 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='drules_struct.proto', package='', - serialized_pb=_b('\n\x13\x64rules_struct.proto\"*\n\x0c\x44\x61shDotProto\x12\n\n\x02\x64\x64\x18\x01 \x03(\x01\x12\x0e\n\x06offset\x18\x02 \x01(\x01\":\n\x0cPathSymProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04step\x18\x02 \x02(\x01\x12\x0e\n\x06offset\x18\x03 \x01(\x01\"\xaf\x01\n\rLineRuleProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\x12\x1e\n\x07pathsym\x18\x05 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"\x9c\x01\n\x0cLineDefProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x1e\n\x07pathsym\x18\x04 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"O\n\rAreaRuleProto\x12\r\n\x05\x63olor\x18\x01 \x02(\r\x12\x1d\n\x06\x62order\x18\x02 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"I\n\x0fSymbolRuleProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x16\n\x0e\x61pply_for_type\x18\x02 \x01(\x05\x12\x10\n\x08priority\x18\x03 \x02(\x05\"j\n\x0f\x43\x61ptionDefProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08offset_x\x18\x04 \x01(\x05\x12\x10\n\x08offset_y\x18\x05 \x01(\x05\"l\n\x10\x43\x61ptionRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"a\n\x0f\x43ircleRuleProto\x12\x0e\n\x06radius\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1d\n\x06\x62order\x18\x03 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\"m\n\x11PathTextRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"X\n\x0fShieldRuleProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08priority\x18\x04 \x02(\x05\"\x8f\x02\n\x10\x44rawElementProto\x12\r\n\x05scale\x18\x01 \x02(\x05\x12\x1d\n\x05lines\x18\x02 \x03(\x0b\x32\x0e.LineRuleProto\x12\x1c\n\x04\x61rea\x18\x03 \x01(\x0b\x32\x0e.AreaRuleProto\x12 \n\x06symbol\x18\x04 \x01(\x0b\x32\x10.SymbolRuleProto\x12\"\n\x07\x63\x61ption\x18\x05 \x01(\x0b\x32\x11.CaptionRuleProto\x12 \n\x06\x63ircle\x18\x06 \x01(\x0b\x32\x10.CircleRuleProto\x12%\n\tpath_text\x18\x07 \x01(\x0b\x32\x12.PathTextRuleProto\x12 \n\x06shield\x18\x08 \x01(\x0b\x32\x10.ShieldRuleProto\"G\n\x13\x43lassifElementProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\"\n\x07\x65lement\x18\x02 \x03(\x0b\x32\x11.DrawElementProto\"4\n\x0e\x43ontainerProto\x12\"\n\x04\x63ont\x18\x01 \x03(\x0b\x32\x14.ClassifElementProto*4\n\x08LineJoin\x12\r\n\tROUNDJOIN\x10\x00\x12\r\n\tBEVELJOIN\x10\x01\x12\n\n\x06NOJOIN\x10\x02*3\n\x07LineCap\x12\x0c\n\x08ROUNDCAP\x10\x00\x12\x0b\n\x07\x42UTTCAP\x10\x01\x12\r\n\tSQUARECAP\x10\x02\x42\x02H\x03') + serialized_pb=_b('\n\x13\x64rules_struct.proto\"*\n\x0c\x44\x61shDotProto\x12\n\n\x02\x64\x64\x18\x01 \x03(\x01\x12\x0e\n\x06offset\x18\x02 \x01(\x01\":\n\x0cPathSymProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04step\x18\x02 \x02(\x01\x12\x0e\n\x06offset\x18\x03 \x01(\x01\"\xaf\x01\n\rLineRuleProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\x12\x1e\n\x07pathsym\x18\x05 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"\x9c\x01\n\x0cLineDefProto\x12\r\n\x05width\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1e\n\x07\x64\x61shdot\x18\x03 \x01(\x0b\x32\r.DashDotProto\x12\x1e\n\x07pathsym\x18\x04 \x01(\x0b\x32\r.PathSymProto\x12\x17\n\x04join\x18\x06 \x01(\x0e\x32\t.LineJoin\x12\x15\n\x03\x63\x61p\x18\x07 \x01(\x0e\x32\x08.LineCap\"O\n\rAreaRuleProto\x12\r\n\x05\x63olor\x18\x01 \x02(\r\x12\x1d\n\x06\x62order\x18\x02 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"I\n\x0fSymbolRuleProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x16\n\x0e\x61pply_for_type\x18\x02 \x01(\x05\x12\x10\n\x08priority\x18\x03 \x02(\x05\"j\n\x0f\x43\x61ptionDefProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08offset_x\x18\x04 \x01(\x05\x12\x10\n\x08offset_y\x18\x05 \x01(\x05\"l\n\x10\x43\x61ptionRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"a\n\x0f\x43ircleRuleProto\x12\x0e\n\x06radius\x18\x01 \x02(\x01\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x1d\n\x06\x62order\x18\x03 \x01(\x0b\x32\r.LineDefProto\x12\x10\n\x08priority\x18\x04 \x02(\x05\"m\n\x11PathTextRuleProto\x12!\n\x07primary\x18\x01 \x02(\x0b\x32\x10.CaptionDefProto\x12#\n\tsecondary\x18\x02 \x01(\x0b\x32\x10.CaptionDefProto\x12\x10\n\x08priority\x18\x03 \x02(\x05\"X\n\x0fShieldRuleProto\x12\x0e\n\x06height\x18\x01 \x02(\x05\x12\r\n\x05\x63olor\x18\x02 \x02(\r\x12\x14\n\x0cstroke_color\x18\x03 \x01(\r\x12\x10\n\x08priority\x18\x04 \x02(\x05\"\xa1\x02\n\x10\x44rawElementProto\x12\r\n\x05scale\x18\x01 \x02(\x05\x12\x1d\n\x05lines\x18\x02 \x03(\x0b\x32\x0e.LineRuleProto\x12\x1c\n\x04\x61rea\x18\x03 \x01(\x0b\x32\x0e.AreaRuleProto\x12 \n\x06symbol\x18\x04 \x01(\x0b\x32\x10.SymbolRuleProto\x12\"\n\x07\x63\x61ption\x18\x05 \x01(\x0b\x32\x11.CaptionRuleProto\x12 \n\x06\x63ircle\x18\x06 \x01(\x0b\x32\x10.CircleRuleProto\x12%\n\tpath_text\x18\x07 \x01(\x0b\x32\x12.PathTextRuleProto\x12 \n\x06shield\x18\x08 \x01(\x0b\x32\x10.ShieldRuleProto\x12\x10\n\x08\x61pply_if\x18\t \x03(\t\"G\n\x13\x43lassifElementProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\"\n\x07\x65lement\x18\x02 \x03(\x0b\x32\x11.DrawElementProto\"4\n\x0e\x43ontainerProto\x12\"\n\x04\x63ont\x18\x01 \x03(\x0b\x32\x14.ClassifElementProto*4\n\x08LineJoin\x12\r\n\tROUNDJOIN\x10\x00\x12\r\n\tBEVELJOIN\x10\x01\x12\n\n\x06NOJOIN\x10\x02*3\n\x07LineCap\x12\x0c\n\x08ROUNDCAP\x10\x00\x12\x0b\n\x07\x42UTTCAP\x10\x01\x12\r\n\tSQUARECAP\x10\x02\x42\x02H\x03') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -44,8 +44,8 @@ _LINEJOIN = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=1539, - serialized_end=1591, + serialized_start=1557, + serialized_end=1609, ) _sym_db.RegisterEnumDescriptor(_LINEJOIN) @@ -71,8 +71,8 @@ _LINECAP = _descriptor.EnumDescriptor( ], containing_type=None, options=None, - serialized_start=1593, - serialized_end=1644, + serialized_start=1611, + serialized_end=1662, ) _sym_db.RegisterEnumDescriptor(_LINECAP) @@ -703,6 +703,13 @@ _DRAWELEMENTPROTO = _descriptor.Descriptor( message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), + _descriptor.FieldDescriptor( + name='apply_if', full_name='DrawElementProto.apply_if', index=8, + number=9, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), ], extensions=[ ], @@ -715,7 +722,7 @@ _DRAWELEMENTPROTO = _descriptor.Descriptor( oneofs=[ ], serialized_start=1139, - serialized_end=1410, + serialized_end=1428, ) @@ -751,8 +758,8 @@ _CLASSIFELEMENTPROTO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1412, - serialized_end=1483, + serialized_start=1430, + serialized_end=1501, ) @@ -781,8 +788,8 @@ _CONTAINERPROTO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1485, - serialized_end=1537, + serialized_start=1503, + serialized_end=1555, ) _LINERULEPROTO.fields_by_name['dashdot'].message_type = _DASHDOTPROTO From 2ed83972365ab46dedcd456d088e4b8310fd0e1f Mon Sep 17 00:00:00 2001 From: Constantin Shalnev Date: Thu, 10 Sep 2015 17:02:54 +0300 Subject: [PATCH 2/5] Added processing of apply_if in the kernel --- indexer/classificator.cpp | 26 +-- indexer/drawing_rules.cpp | 58 +++++-- indexer/drawing_rules.hpp | 11 ++ indexer/drules_selector.cpp | 157 ++++++++++++++++++ indexer/drules_selector.hpp | 29 ++++ indexer/drules_selector_parser.cpp | 124 ++++++++++++++ indexer/drules_selector_parser.hpp | 32 ++++ indexer/indexer.pro | 4 + .../drules_selector_parser_test.cpp | 147 ++++++++++++++++ indexer/indexer_tests/indexer_tests.pro | 1 + render/feature_styler.cpp | 19 +++ 11 files changed, 586 insertions(+), 22 deletions(-) create mode 100644 indexer/drules_selector.cpp create mode 100644 indexer/drules_selector.hpp create mode 100644 indexer/drules_selector_parser.cpp create mode 100644 indexer/drules_selector_parser.hpp create mode 100644 indexer/indexer_tests/drules_selector_parser_test.cpp diff --git a/indexer/classificator.cpp b/indexer/classificator.cpp index aa2ae74c2b..a1dbc11fb4 100644 --- a/indexer/classificator.cpp +++ b/indexer/classificator.cpp @@ -8,6 +8,15 @@ #include "std/algorithm.hpp" #include "std/iterator.hpp" +namespace +{ + struct less_scales + { + bool operator() (drule::Key const & l, int r) const { return l.m_scale < r; } + bool operator() (int l, drule::Key const & r) const { return l < r.m_scale; } + bool operator() (drule::Key const & l, drule::Key const & r) const { return l.m_scale < r.m_scale; } + }; +} // namespace ///////////////////////////////////////////////////////////////////////////////////////// // ClassifObject implementation @@ -39,9 +48,12 @@ ClassifObject * ClassifObject::Find(string const & s) void ClassifObject::AddDrawRule(drule::Key const & k) { for (size_t i = 0; i < m_drawRule.size(); ++i) - if (k == m_drawRule[i]) return; + if (k == m_drawRule[i]) + return; - m_drawRule.push_back(k); + auto i = lower_bound(m_drawRule.begin(), m_drawRule.end(), k.m_scale, less_scales()); + while (i != m_drawRule.end() && i->m_scale == k.m_scale) ++i; + m_drawRule.insert(i, k); } ClassifObjectPtr ClassifObject::BinaryFind(string const & s) const @@ -61,16 +73,6 @@ void ClassifObject::LoadPolicy::Start(size_t i) base_type::Start(i); } -namespace -{ - struct less_scales - { - bool operator() (drule::Key const & l, int r) const { return l.m_scale < r; } - bool operator() (int l, drule::Key const & r) const { return l < r.m_scale; } - bool operator() (drule::Key const & l, drule::Key const & r) const { return l.m_scale < r.m_scale; } - }; -} - void ClassifObject::LoadPolicy::EndChilds() { ClassifObject * p = Current(); diff --git a/indexer/drawing_rules.cpp b/indexer/drawing_rules.cpp index a825971743..d2fb39af84 100644 --- a/indexer/drawing_rules.cpp +++ b/indexer/drawing_rules.cpp @@ -1,8 +1,8 @@ #include "indexer/drawing_rules.hpp" -#include "indexer/scales.hpp" #include "indexer/classificator.hpp" #include "indexer/drules_include.hpp" #include "indexer/map_style_reader.hpp" +#include "indexer/scales.hpp" #include "defines.hpp" @@ -89,6 +89,18 @@ ShieldRuleProto const * BaseRule::GetShield() const return nullptr; } +bool BaseRule::TestFeature(FeatureType const & ft, int /* zoom */) const +{ + if (nullptr == m_selector) + return true; + return m_selector->Test(ft); +} + +void BaseRule::SetSelector(unique_ptr && selector) +{ + m_selector = move(selector); +} + RulesHolder::RulesHolder() : m_bgColors(scales::UPPER_STYLE_SCALE+1, DEFAULT_BG_COLOR) , m_cityRankTable(GetConstRankCityRankTable()) @@ -330,14 +342,36 @@ namespace RulesHolder & m_holder; template - void AddRule(ClassifObject * p, int scale, rule_type_t type, TProtoRule const & rule) + void AddRule(ClassifObject * p, int scale, rule_type_t type, TProtoRule const & rule, + vector const & apply_if) { - Key k = m_holder.AddRule(scale, type, new TRule(rule)); + unique_ptr selector; + if (!apply_if.empty()) + { + selector = ParseSelector(apply_if); + if (selector == nullptr) + { + LOG(LERROR, ("Runtime selector has not been created:", apply_if)); + return; + } + } + + BaseRule * obj = new TRule(rule); + obj->SetSelector(move(selector)); + Key k = m_holder.AddRule(scale, type, obj); p->SetVisibilityOnScale(true, scale); k.SetPriority(rule.priority()); p->AddDrawRule(k); } + static void DrawElementGetApplyIf(DrawElementProto const & de, vector & apply_if) + { + apply_if.clear(); + apply_if.reserve(de.apply_if_size()); + for (int i = 0; i < de.apply_if_size(); ++i) + apply_if.emplace_back(de.apply_if(i)); + } + public: DoSetIndex(RulesHolder & holder) : m_holder(holder) {} @@ -349,6 +383,8 @@ namespace int const i = FindIndex(); if (i != -1) { + vector apply_if; + ClassifElementProto const & ce = m_cont.cont(i); for (int j = 0; j < ce.element_size(); ++j) { @@ -356,26 +392,28 @@ namespace using namespace proto_rules; + DrawElementGetApplyIf(de, apply_if); + for (int k = 0; k < de.lines_size(); ++k) - AddRule(p, de.scale(), line, de.lines(k)); + AddRule(p, de.scale(), line, de.lines(k), apply_if); if (de.has_area()) - AddRule(p, de.scale(), area, de.area()); + AddRule(p, de.scale(), area, de.area(), apply_if); if (de.has_symbol()) - AddRule(p, de.scale(), symbol, de.symbol()); + AddRule(p, de.scale(), symbol, de.symbol(), apply_if); if (de.has_caption()) - AddRule(p, de.scale(), caption, de.caption()); + AddRule(p, de.scale(), caption, de.caption(), apply_if); if (de.has_circle()) - AddRule(p, de.scale(), circle, de.circle()); + AddRule(p, de.scale(), circle, de.circle(), apply_if); if (de.has_path_text()) - AddRule(p, de.scale(), pathtext, de.path_text()); + AddRule(p, de.scale(), pathtext, de.path_text(), apply_if); if (de.has_shield()) - AddRule(p, de.scale(), shield, de.shield()); + AddRule(p, de.scale(), shield, de.shield(), apply_if); } } diff --git a/indexer/drawing_rules.hpp b/indexer/drawing_rules.hpp index 8292836fea..ab9e92f956 100644 --- a/indexer/drawing_rules.hpp +++ b/indexer/drawing_rules.hpp @@ -2,6 +2,7 @@ #include "indexer/drawing_rule_def.hpp" #include "indexer/drules_city_rank_table.hpp" +#include "indexer/drules_selector.hpp" #include "base/base.hpp" #include "base/buffer_vector.hpp" @@ -21,6 +22,7 @@ class CaptionDefProto; class CircleRuleProto; class ShieldRuleProto; class ContainerProto; +class FeatureType; namespace drule @@ -30,6 +32,8 @@ namespace drule mutable buffer_vector m_id1; char m_type; // obsolete for new styles, can be removed + unique_ptr m_selector; + public: static uint32_t const empty_id = 0xFFFFFFFF; @@ -53,6 +57,13 @@ namespace drule virtual CaptionDefProto const * GetCaption(int) const; virtual CircleRuleProto const * GetCircle() const; virtual ShieldRuleProto const * GetShield() const; + + // Test feature by runtime feature style selector + // Returns true if rule is applicable for feature, otherwise it returns false + bool TestFeature(FeatureType const & ft, int zoom) const; + + // Set runtime feature style selector + void SetSelector(unique_ptr && selector); }; class RulesHolder diff --git a/indexer/drules_selector.cpp b/indexer/drules_selector.cpp new file mode 100644 index 0000000000..4d6de04006 --- /dev/null +++ b/indexer/drules_selector.cpp @@ -0,0 +1,157 @@ +#include "indexer/drules_selector.hpp" +#include "indexer/drules_selector_parser.hpp" +#include "indexer/ftypes_matcher.hpp" + +#include "base/assert.hpp" +#include "base/logging.hpp" + +namespace drule +{ + +namespace +{ + +class CompositeSelector : public ISelector +{ +public: + explicit CompositeSelector(size_t capacity) + { + m_selectors.reserve(capacity); + } + + void Add(unique_ptr && selector) + { + m_selectors.emplace_back(move(selector)); + } + + // ISelector overrides: + bool Test(FeatureType const & ft) const override + { + for (auto const & selector : m_selectors) + if (!selector->Test(ft)) + return false; + return true; + } + +private: + vector> m_selectors; +}; + +// Runtime feature style selector implementation +template +class Selector : public ISelector +{ +public: + // Signature of function which takes a property from a feature + typedef bool (*TGetFeatureTagValueFn)(FeatureType const &, TType & value); + + Selector(TGetFeatureTagValueFn fn, SelectorOperatorType op, TType const & value) + : m_getFeatureValueFn(fn), m_evalFn(nullptr), m_value(value) + { + ASSERT(fn != nullptr, ()); + + switch (op) + { + case SelectorOperatorUnknown: m_evalFn = nullptr; break; + case SelectorOperatorNotEqual: m_evalFn = &Selector::NotEqual; break; + case SelectorOperatorLessOrEqual: m_evalFn = &Selector::LessOrEqual; break; + case SelectorOperatorGreaterOrEqual: m_evalFn = &Selector::GreaterOrEqual; break; + case SelectorOperatorEqual: m_evalFn = &Selector::Equal; break; + case SelectorOperatorLess: m_evalFn = &Selector::Less; break; + case SelectorOperatorGreater: m_evalFn = &Selector::Greater; break; + case SelectorOperatorIsNotSet: m_evalFn = &Selector::IsNotSet; break; + case SelectorOperatorIsSet: m_evalFn = &Selector::IsSet; break; + } + + ASSERT(m_evalFn != nullptr, ("Unknown or unexpected selector operator type")); + if (nullptr == m_evalFn) + m_evalFn = &Selector::Unknown; + } + + // ISelector overrides: + bool Test(FeatureType const & ft) const override + { + TType tagValue; + if (!m_getFeatureValueFn(ft, tagValue)) + return false; + return (this->*m_evalFn)(tagValue); + } + +private: + bool Unknown(TType const &) const { return false; } + bool NotEqual(TType const & tagValue) const { return tagValue != m_value; } + bool LessOrEqual(TType const & tagValue) const { return tagValue <= m_value; } + bool GreaterOrEqual(TType const & tagValue) const { return tagValue >= m_value; } + bool Equal(TType const & tagValue) const { return tagValue == m_value; } + bool Less(TType const & tagValue) const { return tagValue < m_value; } + bool Greater(TType const & tagValue) const { return tagValue > m_value; } + bool IsNotSet(TType const & tagValue) const { return tagValue == TType(); } + bool IsSet(TType const & tagValue) const { return tagValue != TType(); } + + typedef bool (Selector::*TOperationFn)(TType const &) const; + + TGetFeatureTagValueFn m_getFeatureValueFn; + TOperationFn m_evalFn; + TType const m_value; +}; + +// Feature tag value evaluator for tag 'population' +bool GetPopulation(FeatureType const & ft, uint32_t & population) +{ + population = ftypes::GetPopulation(ft); + return true; +} + +// Add new tag value evaluator here + +} // namespace + +unique_ptr ParseSelector(string const & str) +{ + SelectorExpression e; + if (!ParseSelector(str, e)) + { + // bad string format + LOG(LDEBUG, ("Invalid selector format: ", str)); + return unique_ptr(); + } + + // Tag 'population' + if (e.m_tag == "population") + { + int value = 0; + if (!e.m_value.empty() && (!strings::to_int(e.m_value, value) || value < 0)) + { + // bad string format + LOG(LDEBUG, ("Invalid selector: ", str)); + return unique_ptr(); + } + return make_unique>(&GetPopulation, e.m_operator, static_cast(value)); + } + + // Add new tag here + + // unrecognized selector + LOG(LDEBUG, ("Unrecognized selector: ", str)); + return unique_ptr(); +} + +unique_ptr ParseSelector(vector const & strs) +{ + unique_ptr cs = make_unique(strs.size()); + + for (string const & str : strs) + { + unique_ptr s = ParseSelector(str); + if (nullptr == s) + { + LOG(LDEBUG, ("Invalid composite selector: ", str)); + return unique_ptr(); + } + cs->Add(move(s)); + } + + return unique_ptr(cs.release()); +} + +} // namespace drule diff --git a/indexer/drules_selector.hpp b/indexer/drules_selector.hpp new file mode 100644 index 0000000000..a63000a1aa --- /dev/null +++ b/indexer/drules_selector.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "indexer/feature.hpp" + +#include "std/string.hpp" +#include "std/unique_ptr.hpp" +#include "std/vector.hpp" + +namespace drule +{ + +// Runtime feature style selector absract interface. +class ISelector +{ +public: + virtual ~ISelector() = default; + + // If ISelector.Test returns true then style is applicable for the feature, + // otherwise, if ISelector.Test returns false, style cannot be applied to the feature. + virtual bool Test(FeatureType const & ft) const = 0; +}; + +// Factory method which builds ISelector from a string. +unique_ptr ParseSelector(string const & str); + +// Factory method which builds composite ISelector from a set of string. +unique_ptr ParseSelector(vector const & strs); + +} // namespace drule diff --git a/indexer/drules_selector_parser.cpp b/indexer/drules_selector_parser.cpp new file mode 100644 index 0000000000..0433e33fb6 --- /dev/null +++ b/indexer/drules_selector_parser.cpp @@ -0,0 +1,124 @@ +#include "indexer/drules_selector_parser.hpp" + +#include "base/assert.hpp" + +#include "std/algorithm.hpp" + +namespace drule +{ + +namespace +{ + +bool IsTag(string const & str) +{ + // tag consists of a-z or A-Z letters and not empty + for (auto const c : str) + { + if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')) + return false; + } + return !str.empty(); +} + +} // namespace + +bool ParseSelector(string const & str, SelectorExpression & e) +{ + // See http://wiki.openstreetmap.org/wiki/MapCSS/0.2 + // Now we support following expressions + // [tag!=value] + // [tag>=value] + // [tag<=value] + // [tag=value] + // [tag>value] + // [tag, < or = + size_t pos = string::npos; + size_t len = 0; + char const c[] = { '>', '<', '=', 0 }; + for (size_t i = 0; c[i] != 0; ++i) + { + size_t p = str.find(c[i]); + if (p != string::npos) + { + pos = (pos == string::npos) ? p : min(p, pos); + len = 1; + } + } + + // If there is no entrance or no space for tag or value then it is invalid format + if (pos == 0 || len == 0 || pos == str.length()-1) + return false; // invalid format + + // Dedicate the operator type, real operator position and length + SelectorOperatorType op = SelectorOperatorUnknown; + if (str[pos] == '>') + { + op = SelectorOperatorGreater; + if (str[pos+1] == '=') + { + ++len; + op = SelectorOperatorGreaterOrEqual; + } + } + else if (str[pos] == '<') + { + op = SelectorOperatorLess; + if (str[pos+1] == '=') + { + ++len; + op = SelectorOperatorLessOrEqual; + } + } + else + { + ASSERT(str[pos] == '=', ()); + op = SelectorOperatorEqual; + if (str[pos-1] == '!') + { + --pos; + ++len; + op = SelectorOperatorNotEqual; + } + } + + string tag(str.begin(), str.begin() + pos); + if (!IsTag(tag)) + return false; // invalid format + + e.m_operator = op; + e.m_tag = move(tag); + e.m_value = string(str.begin() + pos + len, str.end()); + return true; +} + +} // namespace drule diff --git a/indexer/drules_selector_parser.hpp b/indexer/drules_selector_parser.hpp new file mode 100644 index 0000000000..f0e511a24f --- /dev/null +++ b/indexer/drules_selector_parser.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "std/string.hpp" + +namespace drule +{ + +enum SelectorOperatorType +{ + SelectorOperatorUnknown = 0, + SelectorOperatorNotEqual, // [tag!=value] + SelectorOperatorLessOrEqual, // [tag<=value] + SelectorOperatorGreaterOrEqual, // [tag>=value] + SelectorOperatorEqual, // [tag=value] + SelectorOperatorLess, // [tagvalue] + SelectorOperatorIsNotSet, // [!tag] + SelectorOperatorIsSet, // [tag] +}; + +struct SelectorExpression +{ + SelectorOperatorType m_operator; + string m_tag; + string m_value; + + SelectorExpression() : m_operator(SelectorOperatorUnknown) {} +}; + +bool ParseSelector(string const & str, SelectorExpression & e); + +} // namespace drule diff --git a/indexer/indexer.pro b/indexer/indexer.pro index 01e22175af..61b3e66101 100644 --- a/indexer/indexer.pro +++ b/indexer/indexer.pro @@ -19,6 +19,8 @@ SOURCES += \ drawing_rule_def.cpp \ drawing_rules.cpp \ drules_city_rank_table.cpp \ + drules_selector.cpp \ + drules_selector_parser.cpp \ feature.cpp \ feature_algo.cpp \ feature_covering.cpp \ @@ -60,6 +62,8 @@ HEADERS += \ drawing_rules.hpp \ drules_city_rank_table.hpp \ drules_include.hpp \ + drules_selector.cpp \ + drules_selector_parser.cpp \ feature.hpp \ feature_algo.hpp \ feature_covering.hpp \ diff --git a/indexer/indexer_tests/drules_selector_parser_test.cpp b/indexer/indexer_tests/drules_selector_parser_test.cpp new file mode 100644 index 0000000000..c8bc6c8622 --- /dev/null +++ b/indexer/indexer_tests/drules_selector_parser_test.cpp @@ -0,0 +1,147 @@ + +#include "testing/testing.hpp" + +#include "indexer/drules_selector.hpp" +#include "indexer/drules_selector_parser.hpp" + +using namespace drule; + +UNIT_TEST(TestDruleSelectorIsSet) +{ + SelectorExpression e; + TEST(ParseSelector("name", e), ()); + + TEST_EQUAL("name", e.m_tag, ()); + TEST_EQUAL("", e.m_value, ()); + TEST_EQUAL(SelectorOperatorIsSet, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorIsNotSet) +{ + SelectorExpression e; + TEST(ParseSelector("!name", e), ()); + + TEST_EQUAL("name", e.m_tag, ()); + TEST_EQUAL("", e.m_value, ()); + TEST_EQUAL(SelectorOperatorIsNotSet, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorEqual) +{ + SelectorExpression e; + TEST(ParseSelector("population=1000", e), ()); + + TEST_EQUAL("population", e.m_tag, ()); + TEST_EQUAL("1000", e.m_value, ()); + TEST_EQUAL(SelectorOperatorEqual, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorNotEqual) +{ + SelectorExpression e; + TEST(ParseSelector("population!=1000", e), ()); + + TEST_EQUAL("population", e.m_tag, ()); + TEST_EQUAL("1000", e.m_value, ()); + TEST_EQUAL(SelectorOperatorNotEqual, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorLess) +{ + SelectorExpression e; + TEST(ParseSelector("population<1000", e), ()); + + TEST_EQUAL("population", e.m_tag, ()); + TEST_EQUAL("1000", e.m_value, ()); + TEST_EQUAL(SelectorOperatorLess, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorGreater) +{ + SelectorExpression e; + TEST(ParseSelector("population>1000", e), ()); + + TEST_EQUAL("population", e.m_tag, ()); + TEST_EQUAL("1000", e.m_value, ()); + TEST_EQUAL(SelectorOperatorGreater, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorLessOrEqual) +{ + SelectorExpression e; + TEST(ParseSelector("population<=1000", e), ()); + + TEST_EQUAL("population", e.m_tag, ()); + TEST_EQUAL("1000", e.m_value, ()); + TEST_EQUAL(SelectorOperatorLessOrEqual, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorGreaterOrEqual) +{ + SelectorExpression e; + TEST(ParseSelector("population>=1000", e), ()); + + TEST_EQUAL("population", e.m_tag, ()); + TEST_EQUAL("1000", e.m_value, ()); + TEST_EQUAL(SelectorOperatorGreaterOrEqual, e.m_operator, ()); +} + +UNIT_TEST(TestDruleSelectorInvalid) +{ + char const * const badFormats[] = + { + "", + "=badformat", + "!=badformat", + ">badformat", + "=badformat", + "<=badformat", + "bad$name", + "!bad$name", + "bad$name=1000", + }; + size_t const n = sizeof(badFormats) / sizeof(badFormats[0]); + + for (size_t i = 0; i < n; ++i) + { + SelectorExpression e; + TEST_EQUAL(false, ParseSelector(badFormats[i], e), ("string is", badFormats[i])); + } +} + +UNIT_TEST(TestDruleParseSelectorValid1) +{ + auto selector = ParseSelector("population<1000"); + TEST(selector != nullptr, ()); +} + +UNIT_TEST(TestDruleParseSelectorValid2) +{ + auto selector = ParseSelector(vector({"population>1000"})); + TEST(selector != nullptr, ()); +} + +UNIT_TEST(TestDruleParseSelectorValid3) +{ + auto selector = ParseSelector(vector({"population>=1000","population<=1000000"})); + TEST(selector != nullptr, ()); +} + +UNIT_TEST(TestDruleParseSelectorInvalid1) +{ + auto selector = ParseSelector(""); + TEST(selector == nullptr, ()); +} + +UNIT_TEST(TestDruleParseSelectorInvalid2) +{ + auto selector = ParseSelector(vector({""})); + TEST(selector == nullptr, ()); +} + +UNIT_TEST(TestDruleParseSelectorInvalid3) +{ + auto selector = ParseSelector(vector({"population>=1000","population<=1000000", ""})); + TEST(selector == nullptr, ()); +} diff --git a/indexer/indexer_tests/indexer_tests.pro b/indexer/indexer_tests/indexer_tests.pro index 030f44f458..ff56761665 100644 --- a/indexer/indexer_tests/indexer_tests.pro +++ b/indexer/indexer_tests/indexer_tests.pro @@ -28,6 +28,7 @@ SOURCES += \ cell_id_test.cpp \ checker_test.cpp \ city_rank_table_test.cpp \ + drules_selector_parser_test.cpp \ features_offsets_table_test.cpp \ geometry_coding_test.cpp \ geometry_serialization_test.cpp \ diff --git a/render/feature_styler.cpp b/render/feature_styler.cpp index 5f4aabed25..72e76ee028 100644 --- a/render/feature_styler.cpp +++ b/render/feature_styler.cpp @@ -30,6 +30,23 @@ namespace return (r1.m_depth < r2.m_depth); } }; + + void FilterRulesByRuntimeSelector(FeatureType const & ft, int zoom, drule::KeysT & keys) + { + if (keys.empty()) + return; + size_t const n = keys.size(); + size_t w = 0; + for (size_t r = 0; r < n; ++r) + { + drule::BaseRule const * const rule = drule::rules().Find(keys[r]); + ASSERT(rule != nullptr, ()); + if (rule->TestFeature(ft, zoom)) + keys[w++] = keys[r]; + } + if (n != w) + keys.resize(w); + } } namespace di @@ -64,6 +81,8 @@ namespace di drule::KeysT keys; pair type = feature::GetDrawRule(f, zoom, keys); + FilterRulesByRuntimeSelector(f, zoom, keys); + // don't try to do anything to invisible feature if (keys.empty()) return; From d028412ec24fbe2a9888952ae4f9bd56f0b990d5 Mon Sep 17 00:00:00 2001 From: Constantin Shalnev Date: Fri, 18 Sep 2015 14:49:57 +0300 Subject: [PATCH 3/5] Kothic: cleanup and improvement --- indexer/classificator.cpp | 1 - indexer/drules_selector_parser.cpp | 6 +- render/feature_styler.cpp | 15 +- tools/kothic/libkomwm.py | 262 ++++++++++++++-------------- tools/kothic/mapcss/Condition.py | 2 - tools/kothic/mapcss/StyleChooser.py | 5 +- tools/kothic/mapcss/__init__.py | 19 +- 7 files changed, 140 insertions(+), 170 deletions(-) diff --git a/indexer/classificator.cpp b/indexer/classificator.cpp index a1dbc11fb4..fff10dcd0b 100644 --- a/indexer/classificator.cpp +++ b/indexer/classificator.cpp @@ -52,7 +52,6 @@ void ClassifObject::AddDrawRule(drule::Key const & k) return; auto i = lower_bound(m_drawRule.begin(), m_drawRule.end(), k.m_scale, less_scales()); - while (i != m_drawRule.end() && i->m_scale == k.m_scale) ++i; m_drawRule.insert(i, k); } diff --git a/indexer/drules_selector_parser.cpp b/indexer/drules_selector_parser.cpp index 0433e33fb6..a051935f89 100644 --- a/indexer/drules_selector_parser.cpp +++ b/indexer/drules_selector_parser.cpp @@ -48,7 +48,7 @@ bool ParseSelector(string const & str, SelectorExpression & e) e.m_operator = SelectorOperatorIsNotSet; e.m_tag = move(tag); - e.m_value = move(string()); + e.m_value.clear(); return true; } @@ -57,7 +57,7 @@ bool ParseSelector(string const & str, SelectorExpression & e) { e.m_operator = SelectorOperatorIsSet; e.m_tag = str; - e.m_value = move(string()); + e.m_value.clear(); return true; } @@ -76,7 +76,7 @@ bool ParseSelector(string const & str, SelectorExpression & e) } // If there is no entrance or no space for tag or value then it is invalid format - if (pos == 0 || len == 0 || pos == str.length()-1) + if (pos == 0 || len == 0 || pos == str.size()-1) return false; // invalid format // Dedicate the operator type, real operator position and length diff --git a/render/feature_styler.cpp b/render/feature_styler.cpp index 72e76ee028..a4dd66f138 100644 --- a/render/feature_styler.cpp +++ b/render/feature_styler.cpp @@ -33,19 +33,12 @@ namespace void FilterRulesByRuntimeSelector(FeatureType const & ft, int zoom, drule::KeysT & keys) { - if (keys.empty()) - return; - size_t const n = keys.size(); - size_t w = 0; - for (size_t r = 0; r < n; ++r) + keys.erase_if([&ft, zoom](drule::Key const & key)->bool { - drule::BaseRule const * const rule = drule::rules().Find(keys[r]); + drule::BaseRule const * const rule = drule::rules().Find(key); ASSERT(rule != nullptr, ()); - if (rule->TestFeature(ft, zoom)) - keys[w++] = keys[r]; - } - if (n != w) - keys.resize(w); + return !rule->TestFeature(ft, zoom); + }); } } diff --git a/tools/kothic/libkomwm.py b/tools/kothic/libkomwm.py index 2270aefa3c..9e180941fc 100644 --- a/tools/kothic/libkomwm.py +++ b/tools/kothic/libkomwm.py @@ -5,14 +5,15 @@ from optparse import OptionParser import os import csv import sys -import json import mapcss.webcolors whatever_to_hex = mapcss.webcolors.webcolors.whatever_to_hex whatever_to_cairo = mapcss.webcolors.webcolors.whatever_to_cairo + WIDTH_SCALE = 1.0 -def komap_mapswithme(options, style, filename): + +def komap_mapswithme(options, style): if options.outfile == "-": print "Please specify base output path." exit() @@ -23,7 +24,6 @@ def komap_mapswithme(options, style, filename): drules_bin = open(os.path.join(options.outfile + '.bin'), "wb") drules_txt = open(os.path.join(options.outfile + '.txt'), "wb") - drules = ContainerProto() classificator = {} class_order = [] class_tree = {} @@ -96,12 +96,14 @@ def komap_mapswithme(options, style, filename): style.build_choosers_tree(clname, "area", classificator[cl]) style.build_choosers_tree(clname, "node", classificator[cl]) - style.restore_choosers_order("line"); - style.restore_choosers_order("area"); - style.restore_choosers_order("node"); + style.restore_choosers_order("line") + style.restore_choosers_order("area") + style.restore_choosers_order("node") # atbuild.Stop() + drules = ContainerProto() + for cl in class_order: visstring = ["0"] * (options.maxzoom - options.minzoom + 1) @@ -133,7 +135,7 @@ def komap_mapswithme(options, style, filename): if check_line: if "area" not in txclass: # atline.Start() - linestyle = style.get_style_dict(clname, "line", txclass, zoom, olddict=zstyle, cache=False) + linestyle = style.get_style_dict(clname, "line", txclass, zoom, olddict=zstyle) if prev_line_len == -1: prev_line_len = len(linestyle) if len(linestyle) == 0: @@ -144,7 +146,7 @@ def komap_mapswithme(options, style, filename): if check_area: # atarea.Start() - areastyle = style.get_style_dict(clname, "area", txclass, zoom, olddict=zstyle, cache=False) + areastyle = style.get_style_dict(clname, "area", txclass, zoom, olddict=zstyle) for st in areastyle.values(): if "icon-image" in st or 'symbol-shape' in st or 'symbol-image' in st: has_icons_for_areas = True @@ -160,7 +162,7 @@ def komap_mapswithme(options, style, filename): if check_node: if "area" not in txclass: # atnode.Start() - nodestyle = style.get_style_dict(clname, "node", txclass, zoom, olddict=zstyle, cache=False) + nodestyle = style.get_style_dict(clname, "node", txclass, zoom, olddict=zstyle) if prev_node_len == -1: prev_node_len = len(nodestyle) if len(nodestyle) == 0: @@ -183,10 +185,12 @@ def komap_mapswithme(options, style, filename): dr_cont = ClassifElementProto() dr_cont.name = cl + for zoom in xrange(options.minzoom, options.maxzoom + 1): zstyle = zstyles_arr[zoom - options.minzoom] if zstyle is None or len(zstyle) == 0: continue + has_icons_for_areas = has_icons_for_areas_arr[zoom - options.minzoom] has_lines = False @@ -210,129 +214,131 @@ def komap_mapswithme(options, style, filename): has_text = [] has_text.append(st) - if has_lines or has_text or has_fills or has_icons: - visstring[zoom] = "1" - dr_element = DrawElementProto() - dr_element.scale = zoom + if (not has_lines) and (not has_text) and (not has_fills) and (not has_icons): + continue - for st in zstyle: - if st.get('-x-kot-layer') == 'top': - st['z-index'] = float(st.get('z-index', 0)) + 15001. - elif st.get('-x-kot-layer') == 'bottom': - st['z-index'] = float(st.get('z-index', 0)) - 15001. + visstring[zoom] = "1" + dr_element = DrawElementProto() + dr_element.scale = zoom - if st.get('casing-width') not in (None, 0): # and (st.get('width') or st.get('fill-color')): - if st.get('casing-linecap', 'butt') == 'butt': - dr_line = LineRuleProto() - dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) - dr_line.color = mwm_encode_color(st, "casing") - dr_line.priority = min(int(st.get('z-index', 0) + 999), 20000) - dashes = st.get('casing-dashes', st.get('dashes', [])) - dr_line.dashdot.dd.extend(dashes) - dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'butt'), BUTTCAP) - dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) - dr_element.lines.extend([dr_line]) + for st in zstyle: + if st.get('-x-kot-layer') == 'top': + st['z-index'] = float(st.get('z-index', 0)) + 15001. + elif st.get('-x-kot-layer') == 'bottom': + st['z-index'] = float(st.get('z-index', 0)) - 15001. - # Let's try without this additional line style overhead. Needed only for casing in road endings. - # if st.get('casing-linecap', st.get('linecap', 'round')) != 'butt': - # dr_line = LineRuleProto() - # dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) - # dr_line.color = mwm_encode_color(st, "casing") - # dr_line.priority = -15000 - # dashes = st.get('casing-dashes', st.get('dashes', [])) - # dr_line.dashdot.dd.extend(dashes) - # dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'round'), ROUNDCAP) - # dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) - # dr_element.lines.extend([dr_line]) + if st.get('casing-width') not in (None, 0): # and (st.get('width') or st.get('fill-color')): + if st.get('casing-linecap', 'butt') == 'butt': + dr_line = LineRuleProto() + dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) + dr_line.color = mwm_encode_color(st, "casing") + dr_line.priority = min(int(st.get('z-index', 0) + 999), 20000) + dashes = st.get('casing-dashes', st.get('dashes', [])) + dr_line.dashdot.dd.extend(dashes) + dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'butt'), BUTTCAP) + dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) + dr_element.lines.extend([dr_line]) - if has_lines: - if st.get('width'): - dr_line = LineRuleProto() - dr_line.width = (st.get('width', 0) * WIDTH_SCALE) - dr_line.color = mwm_encode_color(st) - for i in st.get('dashes', []): - dr_line.dashdot.dd.extend([max(float(i), 1) * WIDTH_SCALE]) - dr_line.cap = dr_linecaps.get(st.get('linecap', 'butt'), BUTTCAP) - dr_line.join = dr_linejoins.get(st.get('linejoin', 'round'), ROUNDJOIN) - dr_line.priority = min((int(st.get('z-index', 0)) + 1000), 20000) - dr_element.lines.extend([dr_line]) - if st.get('pattern-image'): - dr_line = LineRuleProto() - dr_line.width = 0 - dr_line.color = 0 - icon = mwm_encode_image(st, prefix='pattern') - dr_line.pathsym.name = icon[0] - dr_line.pathsym.step = float(st.get('pattern-spacing', 0)) - 16 - dr_line.pathsym.offset = st.get('pattern-offset', 0) - dr_line.priority = int(st.get('z-index', 0)) + 1000 - dr_element.lines.extend([dr_line]) - textures[icon[0]] = icon[1] - if st.get('shield-font-size'): - dr_element.shield.height = int(st.get('shield-font-size', 10)) - dr_element.shield.color = mwm_encode_color(st, "shield-text") - if st.get('shield-text-halo-radius', 0) != 0: - dr_element.shield.stroke_color = mwm_encode_color(st, "shield-text-halo", "white") - dr_element.shield.priority = min(19100, (16000 + int(st.get('z-index', 0)))) + # Let's try without this additional line style overhead. Needed only for casing in road endings. + # if st.get('casing-linecap', st.get('linecap', 'round')) != 'butt': + # dr_line = LineRuleProto() + # dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) + # dr_line.color = mwm_encode_color(st, "casing") + # dr_line.priority = -15000 + # dashes = st.get('casing-dashes', st.get('dashes', [])) + # dr_line.dashdot.dd.extend(dashes) + # dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'round'), ROUNDCAP) + # dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) + # dr_element.lines.extend([dr_line]) - if has_icons: - if st.get('icon-image'): - if not has_icons_for_areas: - dr_element.symbol.apply_for_type = 1 - icon = mwm_encode_image(st) - dr_element.symbol.name = icon[0] - dr_element.symbol.priority = min(19100, (16000 + int(st.get('z-index', 0)))) - textures[icon[0]] = icon[1] - has_icons = False - if st.get('symbol-shape'): - dr_element.circle.radius = float(st.get('symbol-size')) - dr_element.circle.color = mwm_encode_color(st, 'symbol-fill') - dr_element.circle.priority = min(19000, (14000 + int(st.get('z-index', 0)))) - has_icons = False + if has_lines: + if st.get('width'): + dr_line = LineRuleProto() + dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + dr_line.color = mwm_encode_color(st) + for i in st.get('dashes', []): + dr_line.dashdot.dd.extend([max(float(i), 1) * WIDTH_SCALE]) + dr_line.cap = dr_linecaps.get(st.get('linecap', 'butt'), BUTTCAP) + dr_line.join = dr_linejoins.get(st.get('linejoin', 'round'), ROUNDJOIN) + dr_line.priority = min((int(st.get('z-index', 0)) + 1000), 20000) + dr_element.lines.extend([dr_line]) + if st.get('pattern-image'): + dr_line = LineRuleProto() + dr_line.width = 0 + dr_line.color = 0 + icon = mwm_encode_image(st, prefix='pattern') + dr_line.pathsym.name = icon[0] + dr_line.pathsym.step = float(st.get('pattern-spacing', 0)) - 16 + dr_line.pathsym.offset = st.get('pattern-offset', 0) + dr_line.priority = int(st.get('z-index', 0)) + 1000 + dr_element.lines.extend([dr_line]) + textures[icon[0]] = icon[1] + if st.get('shield-font-size'): + dr_element.shield.height = int(st.get('shield-font-size', 10)) + dr_element.shield.color = mwm_encode_color(st, "shield-text") + if st.get('shield-text-halo-radius', 0) != 0: + dr_element.shield.stroke_color = mwm_encode_color(st, "shield-text-halo", "white") + dr_element.shield.priority = min(19100, (16000 + int(st.get('z-index', 0)))) - if has_text and st.get('text'): - has_text = has_text[:2] - has_text.reverse() + if has_icons: + if st.get('icon-image'): + if not has_icons_for_areas: + dr_element.symbol.apply_for_type = 1 + icon = mwm_encode_image(st) + dr_element.symbol.name = icon[0] + dr_element.symbol.priority = min(19100, (16000 + int(st.get('z-index', 0)))) + textures[icon[0]] = icon[1] + has_icons = False + if st.get('symbol-shape'): + dr_element.circle.radius = float(st.get('symbol-size')) + dr_element.circle.color = mwm_encode_color(st, 'symbol-fill') + dr_element.circle.priority = min(19000, (14000 + int(st.get('z-index', 0)))) + has_icons = False + + if has_text and st.get('text'): + has_text = has_text[:2] + has_text.reverse() + dr_text = dr_element.path_text + base_z = 15000 + if st.get('text-position', 'center') == 'line': dr_text = dr_element.path_text - base_z = 15000 - if st.get('text-position', 'center') == 'line': - dr_text = dr_element.path_text - base_z = 16000 - else: - dr_text = dr_element.caption - for sp in has_text[:]: - dr_cur_subtext = dr_text.primary - if len(has_text) == 2: - dr_cur_subtext = dr_text.secondary - dr_cur_subtext.height = int(float(sp.get('font-size', "10").split(",")[0])) - dr_cur_subtext.color = mwm_encode_color(sp, "text") - if st.get('text-halo-radius', 0) != 0: - dr_cur_subtext.stroke_color = mwm_encode_color(sp, "text-halo", "white") - if 'text-offset' in sp or 'text-offset-y' in sp: - dr_cur_subtext.offset_y = int(sp.get('text-offset-y', sp.get('text-offset', 0))) - if 'text-offset-x' in sp: - dr_cur_subtext.offset_x = int(sp.get('text-offset-x', 0)) - has_text.pop() - dr_text.priority = min(19000, (base_z + int(st.get('z-index', 0)))) - has_text = None + base_z = 16000 + else: + dr_text = dr_element.caption + for sp in has_text[:]: + dr_cur_subtext = dr_text.primary + if len(has_text) == 2: + dr_cur_subtext = dr_text.secondary + dr_cur_subtext.height = int(float(sp.get('font-size', "10").split(",")[0])) + dr_cur_subtext.color = mwm_encode_color(sp, "text") + if st.get('text-halo-radius', 0) != 0: + dr_cur_subtext.stroke_color = mwm_encode_color(sp, "text-halo", "white") + if 'text-offset' in sp or 'text-offset-y' in sp: + dr_cur_subtext.offset_y = int(sp.get('text-offset-y', sp.get('text-offset', 0))) + if 'text-offset-x' in sp: + dr_cur_subtext.offset_x = int(sp.get('text-offset-x', 0)) + has_text.pop() + dr_text.priority = min(19000, (base_z + int(st.get('z-index', 0)))) + has_text = None - if has_fills: - if ('fill-color' in st) and (float(st.get('fill-opacity', 1)) > 0): - dr_element.area.color = mwm_encode_color(st, "fill") - if st.get('fill-position', 'foreground') == 'background': - if 'z-index' not in st: - bgpos -= 1 - dr_element.area.priority = bgpos - 16000 - else: - zzz = int(st.get('z-index', 0)) - if zzz > 0: - dr_element.area.priority = zzz - 16000 - else: - dr_element.area.priority = zzz - 16700 + if has_fills: + if ('fill-color' in st) and (float(st.get('fill-opacity', 1)) > 0): + dr_element.area.color = mwm_encode_color(st, "fill") + if st.get('fill-position', 'foreground') == 'background': + if 'z-index' not in st: + bgpos -= 1 + dr_element.area.priority = bgpos - 16000 else: - dr_element.area.priority = (int(st.get('z-index', 0)) + 1 + 1000) - has_fills = False + zzz = int(st.get('z-index', 0)) + if zzz > 0: + dr_element.area.priority = zzz - 16000 + else: + dr_element.area.priority = zzz - 16700 + else: + dr_element.area.priority = (int(st.get('z-index', 0)) + 1 + 1000) + has_fills = False - dr_cont.element.extend([dr_element]) + dr_cont.element.extend([dr_element]) if dr_cont.element: drules.cont.extend([dr_cont]) @@ -403,18 +409,8 @@ parser.add_option("-f", "--minzoom", dest="minzoom", default=0, type="int", help="minimal available zoom level", metavar="ZOOM") parser.add_option("-t", "--maxzoom", dest="maxzoom", default=19, type="int", help="maximal available zoom level", metavar="ZOOM") -parser.add_option("-l", "--locale", dest="locale", - help="language that should be used for labels (ru, en, be, uk..)", metavar="LANG") parser.add_option("-o", "--output-file", dest="outfile", default="-", help="output filename (defaults to stdout)", metavar="FILE") -parser.add_option("-p", "--osm2pgsql-style", dest="osm2pgsqlstyle", default="-", - help="osm2pgsql stylesheet filename", metavar="FILE") -parser.add_option("-b", "--background-only", dest="bgonly", action="store_true", default=False, - help="Skip rendering of icons and labels", metavar="BOOL") -parser.add_option("-T", "--text-scale", dest="textscale", default=1, type="float", - help="text size scale", metavar="SCALE") -parser.add_option("-c", "--config", dest="conffile", default="komap.conf", - help="config file name", metavar="FILE") (options, args) = parser.parse_args() @@ -431,7 +427,7 @@ try: # atparse.Stop() # atbuild.Start() - komap_mapswithme(options, style, options.filename) + komap_mapswithme(options, style) # atbuild.Stop() # print "mapcss parse, sec: %s" % (atparse.ElapsedSec()) diff --git a/tools/kothic/mapcss/Condition.py b/tools/kothic/mapcss/Condition.py index 768919dcbe..17156438b0 100644 --- a/tools/kothic/mapcss/Condition.py +++ b/tools/kothic/mapcss/Condition.py @@ -101,7 +101,6 @@ class Condition: self.params = params # e.g. ('highway','primary') if typez == "regex": self.regex = re.compile(self.params[0], re.I) - self.compiled_regex = "" def extract_tags(self): if self.params[0][:2] == "::" or self.type == "regex": @@ -136,7 +135,6 @@ class Condition: if params[0] in tags: return tags[params[0]] == '' return True - if t == '<': return (Number(tags[params[0]]) < Number(params[1])) if t == '<=': diff --git a/tools/kothic/mapcss/StyleChooser.py b/tools/kothic/mapcss/StyleChooser.py index 77e8d6cbbf..1cfd2b684a 100644 --- a/tools/kothic/mapcss/StyleChooser.py +++ b/tools/kothic/mapcss/StyleChooser.py @@ -113,15 +113,13 @@ class StyleChooser: return sl #if ftype not in self.compatible_types: -# return sl + #return sl object_id = self.testChain(self.ruleChains, ftype, tags, zoom) if not object_id: return sl - w = 0 - for r in self.styles: if self.has_evals: ra = {} @@ -136,7 +134,6 @@ class StyleChooser: combined_style[p] = cairo_to_hex(q) b = b.compute(tags, combined_style, scale, zscale) ra[a] = b - #r = ra ra = make_nice_style(ra) else: ra = r.copy() diff --git a/tools/kothic/mapcss/__init__.py b/tools/kothic/mapcss/__init__.py index 9efbff8694..9e87b1c1f1 100644 --- a/tools/kothic/mapcss/__init__.py +++ b/tools/kothic/mapcss/__init__.py @@ -18,11 +18,6 @@ import re import os import logging -from hashlib import md5 - -from copy import copy, deepcopy - - from StyleChooser import StyleChooser from Condition import Condition @@ -30,7 +25,6 @@ from Condition import Condition NEEDED_KEYS = set(["width", "casing-width", "fill-color", "fill-image", "icon-image", "text", "extrude", "background-image", "background-color", "pattern-image", "shield-text", "symbol-shape"]) - WHITESPACE = re.compile(r'^ \s+ ', re.S | re.X) COMMENT = re.compile(r'^ \/\* .+? \*\/ \s* ', re.S | re.X) @@ -139,14 +133,10 @@ class MapCSS(): tmp.append(ec) self.choosers_by_type_and_tag[type][tag] = tmp - def get_style(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5, cache=True): + def get_style(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5): """ Kothic styling API """ - if cache: - shash = md5(repr(type) + repr(tags) + repr(zoom)).digest() - if shash in self.cache["style"]: - return deepcopy(self.cache["style"][shash]) style = [] if type in self.choosers_by_type_and_tag: choosers = self.choosers_by_type_and_tag[type][clname] @@ -163,13 +153,10 @@ class MapCSS(): if not NEEDED_KEYS.isdisjoint(x): st.append(x) style = st - - if cache: - self.cache["style"][shash] = deepcopy(style) return style - def get_style_dict(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5, olddict={}, cache=True): - r = self.get_style(clname, type, tags, zoom, scale, zscale, cache) + def get_style_dict(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5, olddict={}): + r = self.get_style(clname, type, tags, zoom, scale, zscale) d = olddict for x in r: if x.get('object-id', '') not in d: From b29c52d807cf510e1e111f3ec866f2d1c54962d8 Mon Sep 17 00:00:00 2001 From: Constantin Shalnev Date: Mon, 21 Sep 2015 12:19:46 +0300 Subject: [PATCH 4/5] Kothic: implemented runtime selectors --- tools/kothic/libkomwm.py | 560 +++++++++++++--------------- tools/kothic/mapcss/Condition.py | 130 ++----- tools/kothic/mapcss/Rule.py | 6 +- tools/kothic/mapcss/StyleChooser.py | 42 ++- tools/kothic/mapcss/__init__.py | 49 ++- 5 files changed, 364 insertions(+), 423 deletions(-) diff --git a/tools/kothic/libkomwm.py b/tools/kothic/libkomwm.py index 9e180941fc..ebab913e57 100644 --- a/tools/kothic/libkomwm.py +++ b/tools/kothic/libkomwm.py @@ -1,5 +1,4 @@ from drules_struct_pb2 import * -from timer import * from mapcss import MapCSS from optparse import OptionParser import os @@ -13,24 +12,37 @@ whatever_to_cairo = mapcss.webcolors.webcolors.whatever_to_cairo WIDTH_SCALE = 1.0 -def komap_mapswithme(options, style): - if options.outfile == "-": - print "Please specify base output path." - exit() - else: - ddir = os.path.dirname(options.outfile) +def mwm_encode_color(st, prefix='', default='black'): + if prefix: + prefix += "-" + opacity = hex(255 - int(255 * float(st.get(prefix + "opacity", 1)))) + color = whatever_to_hex(st.get(prefix + 'color', default)) + color = color[1] + color[1] + color[3] + color[3] + color[5] + color[5] + return int(opacity + color, 16) - types_file = open(os.path.join(ddir, 'types.txt'), "w") - drules_bin = open(os.path.join(options.outfile + '.bin'), "wb") - drules_txt = open(os.path.join(options.outfile + '.txt'), "wb") +def mwm_encode_image(st, prefix='icon', bgprefix='symbol'): + if prefix: + prefix += "-" + if bgprefix: + bgprefix += "-" + if prefix + "image" not in st: + return False + # strip last ".svg" + handle = st.get(prefix + "image")[:-4] + return handle, handle + +def komap_mapswithme(options): + + ddir = os.path.dirname(options.outfile) classificator = {} class_order = [] class_tree = {} - visibility = {} - textures = {} + # Build classificator tree from mapcss-mapping.csv file + types_file = open(os.path.join(ddir, 'types.txt'), "w") for row in csv.reader(open(os.path.join(ddir, 'mapcss-mapping.csv')), delimiter=';'): + cl = row[0].replace("|", "-") pairs = [i.strip(']').split("=") for i in row[1].split(',')[0].split('[')] kv = {} for i in pairs: @@ -42,9 +54,9 @@ def komap_mapswithme(options, style): kv[i[0].strip('?')] = "yes" else: kv[i[0]] = i[1] - classificator[row[0].replace("|", "-")] = kv + classificator[cl] = kv if row[2] != "x": - class_order.append(row[0].replace("|", "-")) + class_order.append(cl) print >> types_file, row[0] else: # compatibility mode @@ -52,306 +64,262 @@ def komap_mapswithme(options, style): print >> types_file, row[6] else: print >> types_file, "mapswithme" - class_tree[row[0].replace("|", "-")] = row[0] + class_tree[cl] = row[0] class_order.sort() types_file.close() - def mwm_encode_color(st, prefix='', default='black'): - if prefix: - prefix += "-" - opacity = hex(255 - int(255 * float(st.get(prefix + "opacity", 1)))) - color = whatever_to_hex(st.get(prefix + 'color', default)) - color = color[1] + color[1] + color[3] + color[3] + color[5] + color[5] - return int(opacity + color, 16) + # Get all mapcss tags which are used in mapcss-mapping.csv + mapcss_mapping_tags = set() + for v in classificator.values(): + for t in v.keys(): + mapcss_mapping_tags.add(t) - def mwm_encode_image(st, prefix='icon', bgprefix='symbol'): - if prefix: - prefix += "-" - if bgprefix: - bgprefix += "-" - if prefix + "image" not in st: - return False - # strip last ".svg" - handle = st.get(prefix + "image")[:-4] - return handle, handle + # Parse style mapcss + style = MapCSS(options.minzoom, options.maxzoom + 1) + style.parse(filename = options.filename, mapcss_tags = mapcss_mapping_tags) + + # Build optimization tree - class/type -> StyleChoosers + for cl in class_order: + clname = cl if cl.find('-') == -1 else cl[:cl.find('-')] + cltags = classificator[cl] + style.build_choosers_tree(clname, "line", cltags) + style.build_choosers_tree(clname, "area", cltags) + style.build_choosers_tree(clname, "node", cltags) + style.restore_choosers_order("line") + style.restore_choosers_order("area") + style.restore_choosers_order("node") + + visibility = {} bgpos = 0 dr_linecaps = {'none': BUTTCAP, 'butt': BUTTCAP, 'round': ROUNDCAP} dr_linejoins = {'none': NOJOIN, 'bevel': BEVELJOIN, 'round': ROUNDJOIN} - # atbuild = AccumulativeTimer() - # atzstyles = AccumulativeTimer() - # atdrcont = AccumulativeTimer() - # atline = AccumulativeTimer() - # atarea = AccumulativeTimer() - # atnode = AccumulativeTimer() - - # atbuild.Start() - - for cl in class_order: - clname = cl if cl.find('-') == -1 else cl[:cl.find('-')] - # clname = cl - style.build_choosers_tree(clname, "line", classificator[cl]) - style.build_choosers_tree(clname, "area", classificator[cl]) - style.build_choosers_tree(clname, "node", classificator[cl]) - - style.restore_choosers_order("line") - style.restore_choosers_order("area") - style.restore_choosers_order("node") - - # atbuild.Stop() + # Build drules tree drules = ContainerProto() for cl in class_order: - visstring = ["0"] * (options.maxzoom - options.minzoom + 1) clname = cl if cl.find('-') == -1 else cl[:cl.find('-')] - # clname = cl - txclass = classificator[cl] - txclass["name"] = "name" - txclass["addr:housenumber"] = "addr:housenumber" - txclass["ref"] = "ref" - txclass["int_name"] = "int_name" - txclass["addr:flats"] = "addr:flats" - prev_area_len = -1 - prev_node_len = -1 - prev_line_len = -1 - check_area = True - check_node = True - check_line = True - - # atzstyles.Start() - - zstyles_arr = [None] * (options.maxzoom - options.minzoom + 1) - has_icons_for_areas_arr = [False] * (options.maxzoom - options.minzoom + 1) - - for zoom in xrange(options.maxzoom, options.minzoom - 1, -1): - has_icons_for_areas = False - zstyle = {} - - if check_line: - if "area" not in txclass: - # atline.Start() - linestyle = style.get_style_dict(clname, "line", txclass, zoom, olddict=zstyle) - if prev_line_len == -1: - prev_line_len = len(linestyle) - if len(linestyle) == 0: - if prev_line_len != 0: - check_line = False - zstyle = linestyle - # atline.Stop() - - if check_area: - # atarea.Start() - areastyle = style.get_style_dict(clname, "area", txclass, zoom, olddict=zstyle) - for st in areastyle.values(): - if "icon-image" in st or 'symbol-shape' in st or 'symbol-image' in st: - has_icons_for_areas = True - break - if prev_area_len == -1: - prev_area_len = len(areastyle) - if len(areastyle) == 0: - if prev_area_len != 0: - check_area = False - zstyle = areastyle - # atarea.Stop() - - if check_node: - if "area" not in txclass: - # atnode.Start() - nodestyle = style.get_style_dict(clname, "node", txclass, zoom, olddict=zstyle) - if prev_node_len == -1: - prev_node_len = len(nodestyle) - if len(nodestyle) == 0: - if prev_node_len != 0: - check_node = False - zstyle = nodestyle - # atnode.Stop() - - if not check_line and not check_area and not check_node: - break - - zstyle = zstyle.values() - - zstyles_arr[zoom - options.minzoom] = zstyle - has_icons_for_areas_arr[zoom - options.minzoom]= has_icons_for_areas - - # atzstyles.Stop() - - # atdrcont.Start() + cltags = classificator[cl] + cltags["name"] = "name" + cltags["addr:housenumber"] = "addr:housenumber" + cltags["ref"] = "ref" + cltags["int_name"] = "int_name" + cltags["addr:flats"] = "addr:flats" dr_cont = ClassifElementProto() dr_cont.name = cl + visstring = ["0"] * (options.maxzoom - options.minzoom + 1) + for zoom in xrange(options.minzoom, options.maxzoom + 1): - zstyle = zstyles_arr[zoom - options.minzoom] - if zstyle is None or len(zstyle) == 0: - continue - has_icons_for_areas = has_icons_for_areas_arr[zoom - options.minzoom] + runtime_conditions_arr = [] - has_lines = False - has_icons = False - has_fills = False - for st in zstyle: - st = dict([(k, v) for k, v in st.iteritems() if str(v).strip(" 0.")]) - if 'width' in st or 'pattern-image' in st: - has_lines = True - if 'icon-image' in st or 'symbol-shape' in st or 'symbol-image' in st: - has_icons = True - if 'fill-color' in st: - has_fills = True + # Get runtime conditions which are used for class 'cl' on zoom 'zoom' + if "area" not in cltags: + runtime_conditions_arr.extend( style.get_runtime_rules(clname, "line", cltags, zoom) ) + runtime_conditions_arr.extend( style.get_runtime_rules(clname, "area", cltags, zoom) ) + if "area" not in cltags: + runtime_conditions_arr.extend( style.get_runtime_rules(clname, "node", cltags, zoom) ) - has_text = None - txfmt = [] - for st in zstyle: - if st.get('text') and not st.get('text') in txfmt: - txfmt.append(st.get('text')) - if has_text is None: - has_text = [] - has_text.append(st) + # If there is no any runtime conditions, do not filter style by runtime conditions + if len(runtime_conditions_arr) == 0: + runtime_conditions_arr.append(None) - if (not has_lines) and (not has_text) and (not has_fills) and (not has_icons): - continue + for runtime_conditions in runtime_conditions_arr: - visstring[zoom] = "1" - dr_element = DrawElementProto() - dr_element.scale = zoom + has_icons_for_areas = False + zstyle = {} - for st in zstyle: - if st.get('-x-kot-layer') == 'top': - st['z-index'] = float(st.get('z-index', 0)) + 15001. - elif st.get('-x-kot-layer') == 'bottom': - st['z-index'] = float(st.get('z-index', 0)) - 15001. + # Get style for class 'cl' on zoom 'zoom' with corresponding runtime conditions + if "area" not in cltags: + linestyle = style.get_style_dict(clname, "line", cltags, zoom, olddict=zstyle, filter_by_runtime_conditions=runtime_conditions) + zstyle = linestyle + areastyle = style.get_style_dict(clname, "area", cltags, zoom, olddict=zstyle, filter_by_runtime_conditions=runtime_conditions) + for st in areastyle.values(): + if "icon-image" in st or 'symbol-shape' in st or 'symbol-image' in st: + has_icons_for_areas = True + break + zstyle = areastyle + if "area" not in cltags: + nodestyle = style.get_style_dict(clname, "node", cltags, zoom, olddict=zstyle, filter_by_runtime_conditions=runtime_conditions) + zstyle = nodestyle - if st.get('casing-width') not in (None, 0): # and (st.get('width') or st.get('fill-color')): - if st.get('casing-linecap', 'butt') == 'butt': - dr_line = LineRuleProto() - dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) - dr_line.color = mwm_encode_color(st, "casing") - dr_line.priority = min(int(st.get('z-index', 0) + 999), 20000) - dashes = st.get('casing-dashes', st.get('dashes', [])) - dr_line.dashdot.dd.extend(dashes) - dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'butt'), BUTTCAP) - dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) - dr_element.lines.extend([dr_line]) + zstyle = zstyle.values() - # Let's try without this additional line style overhead. Needed only for casing in road endings. - # if st.get('casing-linecap', st.get('linecap', 'round')) != 'butt': - # dr_line = LineRuleProto() - # dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) - # dr_line.color = mwm_encode_color(st, "casing") - # dr_line.priority = -15000 - # dashes = st.get('casing-dashes', st.get('dashes', [])) - # dr_line.dashdot.dd.extend(dashes) - # dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'round'), ROUNDCAP) - # dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) - # dr_element.lines.extend([dr_line]) + if len(zstyle) == 0: + continue - if has_lines: - if st.get('width'): - dr_line = LineRuleProto() - dr_line.width = (st.get('width', 0) * WIDTH_SCALE) - dr_line.color = mwm_encode_color(st) - for i in st.get('dashes', []): - dr_line.dashdot.dd.extend([max(float(i), 1) * WIDTH_SCALE]) - dr_line.cap = dr_linecaps.get(st.get('linecap', 'butt'), BUTTCAP) - dr_line.join = dr_linejoins.get(st.get('linejoin', 'round'), ROUNDJOIN) - dr_line.priority = min((int(st.get('z-index', 0)) + 1000), 20000) - dr_element.lines.extend([dr_line]) - if st.get('pattern-image'): - dr_line = LineRuleProto() - dr_line.width = 0 - dr_line.color = 0 - icon = mwm_encode_image(st, prefix='pattern') - dr_line.pathsym.name = icon[0] - dr_line.pathsym.step = float(st.get('pattern-spacing', 0)) - 16 - dr_line.pathsym.offset = st.get('pattern-offset', 0) - dr_line.priority = int(st.get('z-index', 0)) + 1000 - dr_element.lines.extend([dr_line]) - textures[icon[0]] = icon[1] - if st.get('shield-font-size'): - dr_element.shield.height = int(st.get('shield-font-size', 10)) - dr_element.shield.color = mwm_encode_color(st, "shield-text") - if st.get('shield-text-halo-radius', 0) != 0: - dr_element.shield.stroke_color = mwm_encode_color(st, "shield-text-halo", "white") - dr_element.shield.priority = min(19100, (16000 + int(st.get('z-index', 0)))) + has_lines = False + has_icons = False + has_fills = False + for st in zstyle: + st = dict([(k, v) for k, v in st.iteritems() if str(v).strip(" 0.")]) + if 'width' in st or 'pattern-image' in st: + has_lines = True + if 'icon-image' in st or 'symbol-shape' in st or 'symbol-image' in st: + has_icons = True + if 'fill-color' in st: + has_fills = True - if has_icons: - if st.get('icon-image'): - if not has_icons_for_areas: - dr_element.symbol.apply_for_type = 1 - icon = mwm_encode_image(st) - dr_element.symbol.name = icon[0] - dr_element.symbol.priority = min(19100, (16000 + int(st.get('z-index', 0)))) - textures[icon[0]] = icon[1] - has_icons = False - if st.get('symbol-shape'): - dr_element.circle.radius = float(st.get('symbol-size')) - dr_element.circle.color = mwm_encode_color(st, 'symbol-fill') - dr_element.circle.priority = min(19000, (14000 + int(st.get('z-index', 0)))) - has_icons = False + has_text = None + txfmt = [] + for st in zstyle: + if st.get('text') and not st.get('text') in txfmt: + txfmt.append(st.get('text')) + if has_text is None: + has_text = [] + has_text.append(st) - if has_text and st.get('text'): - has_text = has_text[:2] - has_text.reverse() - dr_text = dr_element.path_text - base_z = 15000 - if st.get('text-position', 'center') == 'line': + if (not has_lines) and (not has_text) and (not has_fills) and (not has_icons): + continue + + visstring[zoom] = "1" + + dr_element = DrawElementProto() + dr_element.scale = zoom + + if runtime_conditions: + for rc in runtime_conditions: + dr_element.apply_if.append(str(rc)) + + for st in zstyle: + if st.get('-x-kot-layer') == 'top': + st['z-index'] = float(st.get('z-index', 0)) + 15001. + elif st.get('-x-kot-layer') == 'bottom': + st['z-index'] = float(st.get('z-index', 0)) - 15001. + + if st.get('casing-width') not in (None, 0): # and (st.get('width') or st.get('fill-color')): + if st.get('casing-linecap', 'butt') == 'butt': + dr_line = LineRuleProto() + dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) + dr_line.color = mwm_encode_color(st, "casing") + dr_line.priority = min(int(st.get('z-index', 0) + 999), 20000) + dashes = st.get('casing-dashes', st.get('dashes', [])) + dr_line.dashdot.dd.extend(dashes) + dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'butt'), BUTTCAP) + dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) + dr_element.lines.extend([dr_line]) + + # Let's try without this additional line style overhead. Needed only for casing in road endings. + # if st.get('casing-linecap', st.get('linecap', 'round')) != 'butt': + # dr_line = LineRuleProto() + # dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + (st.get('casing-width') * WIDTH_SCALE * 2) + # dr_line.color = mwm_encode_color(st, "casing") + # dr_line.priority = -15000 + # dashes = st.get('casing-dashes', st.get('dashes', [])) + # dr_line.dashdot.dd.extend(dashes) + # dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'round'), ROUNDCAP) + # dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) + # dr_element.lines.extend([dr_line]) + + if has_lines: + if st.get('width'): + dr_line = LineRuleProto() + dr_line.width = (st.get('width', 0) * WIDTH_SCALE) + dr_line.color = mwm_encode_color(st) + for i in st.get('dashes', []): + dr_line.dashdot.dd.extend([max(float(i), 1) * WIDTH_SCALE]) + dr_line.cap = dr_linecaps.get(st.get('linecap', 'butt'), BUTTCAP) + dr_line.join = dr_linejoins.get(st.get('linejoin', 'round'), ROUNDJOIN) + dr_line.priority = min((int(st.get('z-index', 0)) + 1000), 20000) + dr_element.lines.extend([dr_line]) + if st.get('pattern-image'): + dr_line = LineRuleProto() + dr_line.width = 0 + dr_line.color = 0 + icon = mwm_encode_image(st, prefix='pattern') + dr_line.pathsym.name = icon[0] + dr_line.pathsym.step = float(st.get('pattern-spacing', 0)) - 16 + dr_line.pathsym.offset = st.get('pattern-offset', 0) + dr_line.priority = int(st.get('z-index', 0)) + 1000 + dr_element.lines.extend([dr_line]) + if st.get('shield-font-size'): + dr_element.shield.height = int(st.get('shield-font-size', 10)) + dr_element.shield.color = mwm_encode_color(st, "shield-text") + if st.get('shield-text-halo-radius', 0) != 0: + dr_element.shield.stroke_color = mwm_encode_color(st, "shield-text-halo", "white") + dr_element.shield.priority = min(19100, (16000 + int(st.get('z-index', 0)))) + + if has_icons: + if st.get('icon-image'): + if not has_icons_for_areas: + dr_element.symbol.apply_for_type = 1 + icon = mwm_encode_image(st) + dr_element.symbol.name = icon[0] + dr_element.symbol.priority = min(19100, (16000 + int(st.get('z-index', 0)))) + has_icons = False + if st.get('symbol-shape'): + dr_element.circle.radius = float(st.get('symbol-size')) + dr_element.circle.color = mwm_encode_color(st, 'symbol-fill') + dr_element.circle.priority = min(19000, (14000 + int(st.get('z-index', 0)))) + has_icons = False + + if has_text and st.get('text'): + has_text = has_text[:2] + has_text.reverse() dr_text = dr_element.path_text - base_z = 16000 - else: - dr_text = dr_element.caption - for sp in has_text[:]: - dr_cur_subtext = dr_text.primary - if len(has_text) == 2: - dr_cur_subtext = dr_text.secondary - dr_cur_subtext.height = int(float(sp.get('font-size', "10").split(",")[0])) - dr_cur_subtext.color = mwm_encode_color(sp, "text") - if st.get('text-halo-radius', 0) != 0: - dr_cur_subtext.stroke_color = mwm_encode_color(sp, "text-halo", "white") - if 'text-offset' in sp or 'text-offset-y' in sp: - dr_cur_subtext.offset_y = int(sp.get('text-offset-y', sp.get('text-offset', 0))) - if 'text-offset-x' in sp: - dr_cur_subtext.offset_x = int(sp.get('text-offset-x', 0)) - has_text.pop() - dr_text.priority = min(19000, (base_z + int(st.get('z-index', 0)))) - has_text = None - - if has_fills: - if ('fill-color' in st) and (float(st.get('fill-opacity', 1)) > 0): - dr_element.area.color = mwm_encode_color(st, "fill") - if st.get('fill-position', 'foreground') == 'background': - if 'z-index' not in st: - bgpos -= 1 - dr_element.area.priority = bgpos - 16000 - else: - zzz = int(st.get('z-index', 0)) - if zzz > 0: - dr_element.area.priority = zzz - 16000 - else: - dr_element.area.priority = zzz - 16700 + base_z = 15000 + if st.get('text-position', 'center') == 'line': + dr_text = dr_element.path_text + base_z = 16000 else: - dr_element.area.priority = (int(st.get('z-index', 0)) + 1 + 1000) - has_fills = False + dr_text = dr_element.caption + for sp in has_text[:]: + dr_cur_subtext = dr_text.primary + if len(has_text) == 2: + dr_cur_subtext = dr_text.secondary + dr_cur_subtext.height = int(float(sp.get('font-size', "10").split(",")[0])) + dr_cur_subtext.color = mwm_encode_color(sp, "text") + if st.get('text-halo-radius', 0) != 0: + dr_cur_subtext.stroke_color = mwm_encode_color(sp, "text-halo", "white") + if 'text-offset' in sp or 'text-offset-y' in sp: + dr_cur_subtext.offset_y = int(sp.get('text-offset-y', sp.get('text-offset', 0))) + if 'text-offset-x' in sp: + dr_cur_subtext.offset_x = int(sp.get('text-offset-x', 0)) + has_text.pop() + dr_text.priority = min(19000, (base_z + int(st.get('z-index', 0)))) + has_text = None - dr_cont.element.extend([dr_element]) + if has_fills: + if ('fill-color' in st) and (float(st.get('fill-opacity', 1)) > 0): + dr_element.area.color = mwm_encode_color(st, "fill") + if st.get('fill-position', 'foreground') == 'background': + if 'z-index' not in st: + bgpos -= 1 + dr_element.area.priority = bgpos - 16000 + else: + zzz = int(st.get('z-index', 0)) + if zzz > 0: + dr_element.area.priority = zzz - 16000 + else: + dr_element.area.priority = zzz - 16700 + else: + dr_element.area.priority = (int(st.get('z-index', 0)) + 1 + 1000) + has_fills = False + + dr_cont.element.extend([dr_element]) if dr_cont.element: drules.cont.extend([dr_cont]) - # atdrcont.Stop() - visibility["world|" + class_tree[cl] + "|"] = "".join(visstring) - # atwrite = AccumulativeTimer() - # atwrite.Start() + # Write drules_proto.bin and drules_proto.txt files + drules_bin = open(os.path.join(options.outfile + '.bin'), "wb") + drules_txt = open(os.path.join(options.outfile + '.txt'), "wb") drules_bin.write(drules.SerializeToString()) drules_txt.write(unicode(drules)) + drules_bin.close() + drules_txt.close() + + # Write classificator.txt and visibility.txt files visnodes = set() for k, v in visibility.iteritems(): @@ -379,7 +347,6 @@ def komap_mapswithme(options, style): for i in range(len(oldoffset) / 4, len(offset) / 4, -1): print >> visibility_file, " " * i + "{}" print >> classificator_file, " " * i + "{}" - oldoffset = offset end = "-" if k in visnodes: @@ -390,48 +357,31 @@ def komap_mapswithme(options, style): print >> visibility_file, " " * i + "{}" print >> classificator_file, " " * i + "{}" - # atwrite.Stop() - - # print "build, sec: %s" % (atbuild.ElapsedSec()) - # print "zstyle %s times, sec: %s" % (atzstyles.Count(), atzstyles.ElapsedSec()) - # print "drcont %s times, sec: %s" % (atdrcont.Count(), atdrcont.ElapsedSec()) - # print "line %s times, sec: %s" % (atline.Count(), atline.ElapsedSec()) - # print "area %s times, sec: %s" % (atarea.Count(), atarea.ElapsedSec()) - # print "node %s times, sec: %s" % (atnode.Count(), atnode.ElapsedSec()) - # print "writing files, sec: %s" % (atwrite.ElapsedSec()) + visibility_file.close() + classificator_file.close() # Main -parser = OptionParser() -parser.add_option("-s", "--stylesheet", dest="filename", - help="read MapCSS stylesheet from FILE", metavar="FILE") -parser.add_option("-f", "--minzoom", dest="minzoom", default=0, type="int", - help="minimal available zoom level", metavar="ZOOM") -parser.add_option("-t", "--maxzoom", dest="maxzoom", default=19, type="int", - help="maximal available zoom level", metavar="ZOOM") -parser.add_option("-o", "--output-file", dest="outfile", default="-", - help="output filename (defaults to stdout)", metavar="FILE") - -(options, args) = parser.parse_args() - -if (options.filename is None): - parser.error("MapCSS stylesheet filename is required") - try: - # atparse = AccumulativeTimer() - # atbuild = AccumulativeTimer() + parser = OptionParser() + parser.add_option("-s", "--stylesheet", dest="filename", + help="read MapCSS stylesheet from FILE", metavar="FILE") + parser.add_option("-f", "--minzoom", dest="minzoom", default=0, type="int", + help="minimal available zoom level", metavar="ZOOM") + parser.add_option("-t", "--maxzoom", dest="maxzoom", default=19, type="int", + help="maximal available zoom level", metavar="ZOOM") + parser.add_option("-o", "--output-file", dest="outfile", default="-", + help="output filename (defaults to stdout)", metavar="FILE") - # atparse.Start() - style = MapCSS(options.minzoom, options.maxzoom + 1) # zoom levels - style.parse(filename = options.filename) - # atparse.Stop() + (options, args) = parser.parse_args() - # atbuild.Start() - komap_mapswithme(options, style) - # atbuild.Stop() + if (options.filename is None): + parser.error("MapCSS stylesheet filename is required") - # print "mapcss parse, sec: %s" % (atparse.ElapsedSec()) - # print "build, sec: %s" % (atbuild.ElapsedSec()) + if options.outfile == "-": + parser.error("Please specify base output path.") + + komap_mapswithme(options) exit(0) diff --git a/tools/kothic/mapcss/Condition.py b/tools/kothic/mapcss/Condition.py index 17156438b0..f58194f806 100644 --- a/tools/kothic/mapcss/Condition.py +++ b/tools/kothic/mapcss/Condition.py @@ -17,95 +17,19 @@ import re -# Fast conditions - -class EqConditionDD: - def __init__(self, params): - self.value = params[1] - def extract_tags(self): - return set(["*"]) - def test(self, tags): - return self.value - -class EqCondition: - def __init__(self, params): - self.tag = params[0] - self.value = params[1] - def extract_tags(self): - return set([self.tag]) - def test(self, tags): - if self.tag in tags: - return tags[self.tag] == self.value - else: - return False - -class NotEqCondition: - def __init__(self, params): - self.tag = params[0] - self.value = params[1] - def extract_tags(self): - return set([self.tag]) - def test(self, tags): - if self.tag in tags: - return tags[self.tag] != self.value - else: - return False - -class SetCondition: - def __init__(self, params): - self.tag = params[0] - def extract_tags(self): - return set([self.tag]) - def test(self, tags): - if self.tag in tags: - return tags[self.tag] != '' - return False - -class UnsetCondition: - def __init__(self, params): - self.tag = params[0] - def extract_tags(self): - return set([self.tag]) - def test(self, tags): - if self.tag in tags: - return tags[self.tag] == '' - return True - -class TrueCondition: - def __init__(self, params): - self.tag = params[0] - def extract_tags(self): - return set([self.tag]) - def test(self, tags): - if self.tag in tags: - return tags[self.tag] == 'yes' - return False - -class UntrueCondition: - def __init__(self, params): - self.tag = params[0] - def extract_tags(self): - return set([self.tag]) - def test(self, tags): - if self.tag in tags: - return tags[self.tag] == 'no' - return False - -# Slow condition - class Condition: def __init__(self, typez, params): self.type = typez # eq, regex, lt, gt etc. if type(params) == type(str()): params = (params,) - self.params = params # e.g. ('highway','primary') + self.params = params # e.g. ('highway','primary') if typez == "regex": self.regex = re.compile(self.params[0], re.I) - def extract_tags(self): + def extract_tag(self): if self.params[0][:2] == "::" or self.type == "regex": - return set(["*"]) # unknown - return set([self.params[0]]) + return "*" # unknown + return self.params[0] def test(self, tags): """ @@ -148,6 +72,32 @@ class Condition: return False def __repr__(self): + t = self.type + params = self.params + if t == 'eq' and params[0][:2] == "::": + return "::%s" % (params[1]) + if t == 'eq': + return "%s=%s" % (params[0], params[1]) + if t == 'ne': + return "%s=%s" % (params[0], params[1]) + if t == 'regex': + return "%s=~/%s/" % (params[0], params[1]); + if t == 'true': + return "%s?" % (params[0]) + if t == 'untrue': + return "!%s?" % (params[0]) + if t == 'set': + return "%s" % (params[0]) + if t == 'unset': + return "!%s" % (params[0]) + if t == '<': + return "%s<%s" % (params[0], params[1]) + if t == '<=': + return "%s<=%s" % (params[0], params[1]) + if t == '>': + return "%s>%s" % (params[0], params[1]) + if t == '>=': + return "%s>=%s" % (params[0], params[1]) return "%s %s " % (self.type, repr(self.params)) def __eq__(self, a): @@ -162,23 +112,3 @@ def Number(tt): except ValueError: return 0 -# Some conditions we can optimize by using "python polymorthism" - -def OptimizeCondition(condition): - if (condition.type == "eq"): - if (condition.params[0][:2] == "::"): - return EqConditionDD(condition.params) - else: - return EqCondition(condition.params) - elif (condition.type == "ne"): - return NotEqCondition(condition.params) - elif (condition.type == "set"): - return SetCondition(condition.params) - elif (condition.type == "unset"): - return UnsetCondition(condition.params) - elif (condition.type == "true"): - return TrueCondition(condition.params) - elif (condition.type == "untrue"): - return UntrueCondition(condition.params) - else: - return condition diff --git a/tools/kothic/mapcss/Rule.py b/tools/kothic/mapcss/Rule.py index 68c404818d..a652f30841 100644 --- a/tools/kothic/mapcss/Rule.py +++ b/tools/kothic/mapcss/Rule.py @@ -25,6 +25,7 @@ type_matches = { class Rule(): def __init__(self, s=''): + self.runtime_conditions = [] self.conditions = [] # self.isAnd = True self.minZoom = 0 @@ -34,7 +35,7 @@ class Rule(): self.subject = s # "", "way", "node" or "relation" def __repr__(self): - return "%s|z%s-%s %s" % (self.subject, self.minZoom, self.maxZoom, self.conditions) + return "%s|z%s-%s %s %s" % (self.subject, self.minZoom, self.maxZoom, self.conditions, self.runtime_conditions) def test(self, obj, tags, zoom): if (zoom < self.minZoom) or (zoom > self.maxZoom): @@ -58,8 +59,9 @@ class Rule(): def extract_tags(self): a = set() for condition in self.conditions: - a.update(condition.extract_tags()) + a.add(condition.extract_tag()) if "*" in a: + a = set(["*"]) break return a diff --git a/tools/kothic/mapcss/StyleChooser.py b/tools/kothic/mapcss/StyleChooser.py index 1cfd2b684a..898edd98ad 100644 --- a/tools/kothic/mapcss/StyleChooser.py +++ b/tools/kothic/mapcss/StyleChooser.py @@ -106,7 +106,24 @@ class StyleChooser: a.add("*") return a - def updateStyles(self, sl, ftype, tags, zoom, scale, zscale): + def get_runtime_conditions(self, ftype, tags, zoom): + if self.selzooms: + if zoom < self.selzooms[0] or zoom > self.selzooms[1]: + return None + + rule_and_object_id = self.testChain(self.ruleChains, ftype, tags, zoom) + + if not rule_and_object_id: + return None + + rule = rule_and_object_id[0] + + if (len(rule.runtime_conditions) == 0): + return None + + return rule.runtime_conditions + + def updateStyles(self, sl, ftype, tags, zoom, xscale, zscale, filter_by_runtime_conditions): # Are any of the ruleChains fulfilled? if self.selzooms: if zoom < self.selzooms[0] or zoom > self.selzooms[1]: @@ -115,9 +132,15 @@ class StyleChooser: #if ftype not in self.compatible_types: #return sl - object_id = self.testChain(self.ruleChains, ftype, tags, zoom) + rule_and_object_id = self.testChain(self.ruleChains, ftype, tags, zoom) - if not object_id: + if not rule_and_object_id: + return sl + + rule = rule_and_object_id[0] + object_id = rule_and_object_id[1] + + if filter_by_runtime_conditions and (filter_by_runtime_conditions != rule.runtime_conditions): return sl for r in self.styles: @@ -132,7 +155,7 @@ class StyleChooser: for p, q in combined_style.iteritems(): if "color" in p: combined_style[p] = cairo_to_hex(q) - b = b.compute(tags, combined_style, scale, zscale) + b = b.compute(tags, combined_style, xscale, zscale) ra[a] = b ra = make_nice_style(ra) else: @@ -168,7 +191,7 @@ class StyleChooser: for r in chain: tt = r.test(obj, tags, zoom) if tt: - return tt + return r, tt return False def newGroup(self): @@ -200,9 +223,16 @@ class StyleChooser: """ adds into the current ruleChain (existing Rule) """ - c = OptimizeCondition(c) self.ruleChains[-1].conditions.append(c) + def addRuntimeCondition(self, c): + # print "addRuntimeCondition ", c + """ + adds into the current ruleChain (existing Rule) + """ + self.ruleChains[-1].runtime_conditions.append(c) + self.ruleChains[-1].runtime_conditions.sort() + def addStyles(self, a): # print "addStyle ", a """ diff --git a/tools/kothic/mapcss/__init__.py b/tools/kothic/mapcss/__init__.py index 9e87b1c1f1..f2ff6f9c51 100644 --- a/tools/kothic/mapcss/__init__.py +++ b/tools/kothic/mapcss/__init__.py @@ -33,6 +33,7 @@ CLASS = re.compile(r'^ ([\.:]:?[*\w]+) \s* ', re.S | re.X) ZOOM = re.compile(r'^ \| \s* z([\d\-]+) \s* ', re.I | re.S | re.X) GROUP = re.compile(r'^ , \s* ', re.I | re.S | re.X) CONDITION = re.compile(r'^ \[(.+?)\] \s* ', re.S | re.X) +RUNTIME_CONDITION = re.compile(r'^ \((.+?)\) \s* ', re.S | re.X) OBJECT = re.compile(r'^ (\*|[\w]+) \s* ', re.S | re.X) DECLARATION = re.compile(r'^ \{(.+?)\} \s* ', re.S | re.X) IMPORT = re.compile(r'^@import\("(.+?)"\); \s* ', re.S | re.X) @@ -133,15 +134,23 @@ class MapCSS(): tmp.append(ec) self.choosers_by_type_and_tag[type][tag] = tmp - def get_style(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5): + def get_runtime_rules(self, clname, type, tags, zoom): """ - Kothic styling API + Returns array of runtime_conditions which are used for clname/type/tags/zoom """ + runtime_rules = [] + if type in self.choosers_by_type_and_tag: + for chooser in self.choosers_by_type_and_tag[type][clname]: + runtime_conditions = chooser.get_runtime_conditions(type, tags, zoom) + if runtime_conditions: + runtime_rules.append(runtime_conditions) + return runtime_rules + + def get_style(self, clname, type, tags, zoom, xscale, zscale, filter_by_runtime_conditions): style = [] if type in self.choosers_by_type_and_tag: - choosers = self.choosers_by_type_and_tag[type][clname] - for chooser in choosers: - style = chooser.updateStyles(style, type, tags, zoom, scale, zscale) + for chooser in self.choosers_by_type_and_tag[type][clname]: + style = chooser.updateStyles(style, type, tags, zoom, xscale, zscale, filter_by_runtime_conditions) style = [x for x in style if x["object-id"] != "::*"] for x in style: for k, v in [('width', 0), ('casing-width', 0)]: @@ -155,8 +164,11 @@ class MapCSS(): style = st return style - def get_style_dict(self, clname, type, tags={}, zoom=0, scale=1, zscale=.5, olddict={}): - r = self.get_style(clname, type, tags, zoom, scale, zscale) + def get_style_dict(self, clname, type, tags={}, zoom=0, xscale=1, zscale=.5, olddict={}, filter_by_runtime_conditions=None): + """ + Kothic styling API + """ + r = self.get_style(clname, type, tags, zoom, xscale, zscale, filter_by_runtime_conditions) d = olddict for x in r: if x.get('object-id', '') not in d: @@ -165,7 +177,7 @@ class MapCSS(): return d def subst_variables(self, t): - """Expects an array from parseDeclaration.""" + """ Expects an array from parseDeclaration. """ for k in t[0]: t[0][k] = VARIABLE.sub(self.get_variable, t[0][k]) return t @@ -176,7 +188,7 @@ class MapCSS(): raise Exception("Variable not found: " + str(format(name))) return self.variables[name] if name in self.variables else m.group() - def parse(self, css=None, clamp=True, stretch=1000, filename=None): + def parse(self, css=None, clamp=True, stretch=1000, filename=None, mapcss_tags=None): """ Parses MapCSS given as string """ @@ -238,6 +250,19 @@ class MapCSS(): sc.newGroup() previous = oGROUP + # RuntimeCondition - (population>=10000) + elif RUNTIME_CONDITION.match(css): + if (previous == oDECLARATION): + self.choosers.append(sc) + sc = StyleChooser(self.scalepair) + if (previous != oOBJECT) and (previous != oZOOM) and (previous != oCONDITION): + sc.newObject() + cond = RUNTIME_CONDITION.match(css).groups()[0] + log.debug("runtime condition found: %s" % (cond)) + css = RUNTIME_CONDITION.sub("", css) + sc.addRuntimeCondition(parseCondition(cond)) + previous = oCONDITION + # Condition - [highway=primary] elif CONDITION.match(css): if (previous == oDECLARATION): @@ -247,8 +272,12 @@ class MapCSS(): sc.newObject() cond = CONDITION.match(css).groups()[0] log.debug("condition found: %s" % (cond)) + c = parseCondition(cond) + tag = c.extract_tag() + if (tag != "*") and (mapcss_tags != None) and (tag not in mapcss_tags): + raise Exception("Unknown tag '" + tag + "' in condition " + cond) css = CONDITION.sub("", css) - sc.addCondition(parseCondition(cond)) + sc.addCondition(c) previous = oCONDITION # Object - way, node, relation From 35bac07d87abe4ef4216008bf4b1d4e2867b8524 Mon Sep 17 00:00:00 2001 From: Constantin Shalnev Date: Wed, 23 Sep 2015 19:03:34 +0300 Subject: [PATCH 5/5] Fixed notes --- indexer/classificator.cpp | 7 +++---- indexer/drules_selector_parser.hpp | 32 ++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/indexer/classificator.cpp b/indexer/classificator.cpp index fff10dcd0b..f66acc8589 100644 --- a/indexer/classificator.cpp +++ b/indexer/classificator.cpp @@ -47,11 +47,10 @@ ClassifObject * ClassifObject::Find(string const & s) void ClassifObject::AddDrawRule(drule::Key const & k) { - for (size_t i = 0; i < m_drawRule.size(); ++i) - if (k == m_drawRule[i]) - return; - auto i = lower_bound(m_drawRule.begin(), m_drawRule.end(), k.m_scale, less_scales()); + for (; i != m_drawRule.end() && i->m_scale == k.m_scale; ++i) + if (k == *i) + return; // already exists m_drawRule.insert(i, k); } diff --git a/indexer/drules_selector_parser.hpp b/indexer/drules_selector_parser.hpp index f0e511a24f..0a52e0d746 100644 --- a/indexer/drules_selector_parser.hpp +++ b/indexer/drules_selector_parser.hpp @@ -8,14 +8,30 @@ namespace drule enum SelectorOperatorType { SelectorOperatorUnknown = 0, - SelectorOperatorNotEqual, // [tag!=value] - SelectorOperatorLessOrEqual, // [tag<=value] - SelectorOperatorGreaterOrEqual, // [tag>=value] - SelectorOperatorEqual, // [tag=value] - SelectorOperatorLess, // [tagvalue] - SelectorOperatorIsNotSet, // [!tag] - SelectorOperatorIsSet, // [tag] + + // [tag!=value] + SelectorOperatorNotEqual, + + // [tag<=value] + SelectorOperatorLessOrEqual, + + // [tag>=value] + SelectorOperatorGreaterOrEqual, + + // [tag=value] + SelectorOperatorEqual, + + // [tagvalue] + SelectorOperatorGreater, + + // [!tag] + SelectorOperatorIsNotSet, + + // [tag] + SelectorOperatorIsSet, }; struct SelectorExpression