Added kothic and drules generation script

This commit is contained in:
Constantin Shalnev 2015-06-03 20:34:48 +03:00 committed by Alex Zolotarev
parent f09f8296bb
commit 131ecd156e
14 changed files with 4474 additions and 0 deletions

View file

@ -0,0 +1,817 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
from google.protobuf import descriptor
from google.protobuf import message
from google.protobuf import reflection
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)
DESCRIPTOR = descriptor.FileDescriptor(
name='drules_struct.proto',
package='',
serialized_pb='\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\"\xed\x01\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\"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')
_LINEJOIN = descriptor.EnumDescriptor(
name='LineJoin',
full_name='LineJoin',
filename=None,
file=DESCRIPTOR,
values=[
descriptor.EnumValueDescriptor(
name='ROUNDJOIN', index=0, number=0,
options=None,
type=None),
descriptor.EnumValueDescriptor(
name='BEVELJOIN', index=1, number=1,
options=None,
type=None),
descriptor.EnumValueDescriptor(
name='NOJOIN', index=2, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=1415,
serialized_end=1467,
)
_LINECAP = descriptor.EnumDescriptor(
name='LineCap',
full_name='LineCap',
filename=None,
file=DESCRIPTOR,
values=[
descriptor.EnumValueDescriptor(
name='ROUNDCAP', index=0, number=0,
options=None,
type=None),
descriptor.EnumValueDescriptor(
name='BUTTCAP', index=1, number=1,
options=None,
type=None),
descriptor.EnumValueDescriptor(
name='SQUARECAP', index=2, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=1469,
serialized_end=1520,
)
ROUNDJOIN = 0
BEVELJOIN = 1
NOJOIN = 2
ROUNDCAP = 0
BUTTCAP = 1
SQUARECAP = 2
_DASHDOTPROTO = descriptor.Descriptor(
name='DashDotProto',
full_name='DashDotProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='dd', full_name='DashDotProto.dd', index=0,
number=1, type=1, cpp_type=5, 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),
descriptor.FieldDescriptor(
name='offset', full_name='DashDotProto.offset', index=1,
number=2, type=1, cpp_type=5, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=23,
serialized_end=65,
)
_PATHSYMPROTO = descriptor.Descriptor(
name='PathSymProto',
full_name='PathSymProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='name', full_name='PathSymProto.name', index=0,
number=1, type=9, cpp_type=9, label=2,
has_default_value=False, default_value=unicode("", "utf-8"),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='step', full_name='PathSymProto.step', index=1,
number=2, type=1, cpp_type=5, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='offset', full_name='PathSymProto.offset', index=2,
number=3, type=1, cpp_type=5, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=67,
serialized_end=125,
)
_LINERULEPROTO = descriptor.Descriptor(
name='LineRuleProto',
full_name='LineRuleProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='width', full_name='LineRuleProto.width', index=0,
number=1, type=1, cpp_type=5, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='color', full_name='LineRuleProto.color', index=1,
number=2, type=13, cpp_type=3, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='dashdot', full_name='LineRuleProto.dashdot', index=2,
number=3, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='priority', full_name='LineRuleProto.priority', index=3,
number=4, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='pathsym', full_name='LineRuleProto.pathsym', index=4,
number=5, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='join', full_name='LineRuleProto.join', index=5,
number=6, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='cap', full_name='LineRuleProto.cap', index=6,
number=7, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=128,
serialized_end=303,
)
_LINEDEFPROTO = descriptor.Descriptor(
name='LineDefProto',
full_name='LineDefProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='width', full_name='LineDefProto.width', index=0,
number=1, type=1, cpp_type=5, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='color', full_name='LineDefProto.color', index=1,
number=2, type=13, cpp_type=3, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='dashdot', full_name='LineDefProto.dashdot', index=2,
number=3, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='pathsym', full_name='LineDefProto.pathsym', index=3,
number=4, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='join', full_name='LineDefProto.join', index=4,
number=6, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='cap', full_name='LineDefProto.cap', index=5,
number=7, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=306,
serialized_end=462,
)
_AREARULEPROTO = descriptor.Descriptor(
name='AreaRuleProto',
full_name='AreaRuleProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='color', full_name='AreaRuleProto.color', index=0,
number=1, type=13, cpp_type=3, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='border', full_name='AreaRuleProto.border', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='priority', full_name='AreaRuleProto.priority', index=2,
number=3, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=464,
serialized_end=543,
)
_SYMBOLRULEPROTO = descriptor.Descriptor(
name='SymbolRuleProto',
full_name='SymbolRuleProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='name', full_name='SymbolRuleProto.name', index=0,
number=1, type=9, cpp_type=9, label=2,
has_default_value=False, default_value=unicode("", "utf-8"),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='apply_for_type', full_name='SymbolRuleProto.apply_for_type', index=1,
number=2, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='priority', full_name='SymbolRuleProto.priority', index=2,
number=3, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=545,
serialized_end=618,
)
_CAPTIONDEFPROTO = descriptor.Descriptor(
name='CaptionDefProto',
full_name='CaptionDefProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='height', full_name='CaptionDefProto.height', index=0,
number=1, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='color', full_name='CaptionDefProto.color', index=1,
number=2, type=13, cpp_type=3, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='stroke_color', full_name='CaptionDefProto.stroke_color', index=2,
number=3, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='offset_x', full_name='CaptionDefProto.offset_x', index=3,
number=4, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='offset_y', full_name='CaptionDefProto.offset_y', index=4,
number=5, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=620,
serialized_end=726,
)
_CAPTIONRULEPROTO = descriptor.Descriptor(
name='CaptionRuleProto',
full_name='CaptionRuleProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='primary', full_name='CaptionRuleProto.primary', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='secondary', full_name='CaptionRuleProto.secondary', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='priority', full_name='CaptionRuleProto.priority', index=2,
number=3, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=728,
serialized_end=836,
)
_CIRCLERULEPROTO = descriptor.Descriptor(
name='CircleRuleProto',
full_name='CircleRuleProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='radius', full_name='CircleRuleProto.radius', index=0,
number=1, type=1, cpp_type=5, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='color', full_name='CircleRuleProto.color', index=1,
number=2, type=13, cpp_type=3, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='border', full_name='CircleRuleProto.border', index=2,
number=3, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='priority', full_name='CircleRuleProto.priority', index=3,
number=4, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=838,
serialized_end=935,
)
_PATHTEXTRULEPROTO = descriptor.Descriptor(
name='PathTextRuleProto',
full_name='PathTextRuleProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='primary', full_name='PathTextRuleProto.primary', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='secondary', full_name='PathTextRuleProto.secondary', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='priority', full_name='PathTextRuleProto.priority', index=2,
number=3, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=937,
serialized_end=1046,
)
_DRAWELEMENTPROTO = descriptor.Descriptor(
name='DrawElementProto',
full_name='DrawElementProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='scale', full_name='DrawElementProto.scale', index=0,
number=1, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='lines', full_name='DrawElementProto.lines', index=1,
number=2, type=11, cpp_type=10, 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),
descriptor.FieldDescriptor(
name='area', full_name='DrawElementProto.area', index=2,
number=3, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='symbol', full_name='DrawElementProto.symbol', index=3,
number=4, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='caption', full_name='DrawElementProto.caption', index=4,
number=5, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='circle', full_name='DrawElementProto.circle', index=5,
number=6, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='path_text', full_name='DrawElementProto.path_text', index=6,
number=7, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=1049,
serialized_end=1286,
)
_CLASSIFELEMENTPROTO = descriptor.Descriptor(
name='ClassifElementProto',
full_name='ClassifElementProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='name', full_name='ClassifElementProto.name', index=0,
number=1, type=9, cpp_type=9, label=2,
has_default_value=False, default_value=unicode("", "utf-8"),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
descriptor.FieldDescriptor(
name='element', full_name='ClassifElementProto.element', index=1,
number=2, type=11, cpp_type=10, 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=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=1288,
serialized_end=1359,
)
_CONTAINERPROTO = descriptor.Descriptor(
name='ContainerProto',
full_name='ContainerProto',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
descriptor.FieldDescriptor(
name='cont', full_name='ContainerProto.cont', index=0,
number=1, type=11, cpp_type=10, 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=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
extension_ranges=[],
serialized_start=1361,
serialized_end=1413,
)
_LINERULEPROTO.fields_by_name['dashdot'].message_type = _DASHDOTPROTO
_LINERULEPROTO.fields_by_name['pathsym'].message_type = _PATHSYMPROTO
_LINERULEPROTO.fields_by_name['join'].enum_type = _LINEJOIN
_LINERULEPROTO.fields_by_name['cap'].enum_type = _LINECAP
_LINEDEFPROTO.fields_by_name['dashdot'].message_type = _DASHDOTPROTO
_LINEDEFPROTO.fields_by_name['pathsym'].message_type = _PATHSYMPROTO
_LINEDEFPROTO.fields_by_name['join'].enum_type = _LINEJOIN
_LINEDEFPROTO.fields_by_name['cap'].enum_type = _LINECAP
_AREARULEPROTO.fields_by_name['border'].message_type = _LINEDEFPROTO
_CAPTIONRULEPROTO.fields_by_name['primary'].message_type = _CAPTIONDEFPROTO
_CAPTIONRULEPROTO.fields_by_name['secondary'].message_type = _CAPTIONDEFPROTO
_CIRCLERULEPROTO.fields_by_name['border'].message_type = _LINEDEFPROTO
_PATHTEXTRULEPROTO.fields_by_name['primary'].message_type = _CAPTIONDEFPROTO
_PATHTEXTRULEPROTO.fields_by_name['secondary'].message_type = _CAPTIONDEFPROTO
_DRAWELEMENTPROTO.fields_by_name['lines'].message_type = _LINERULEPROTO
_DRAWELEMENTPROTO.fields_by_name['area'].message_type = _AREARULEPROTO
_DRAWELEMENTPROTO.fields_by_name['symbol'].message_type = _SYMBOLRULEPROTO
_DRAWELEMENTPROTO.fields_by_name['caption'].message_type = _CAPTIONRULEPROTO
_DRAWELEMENTPROTO.fields_by_name['circle'].message_type = _CIRCLERULEPROTO
_DRAWELEMENTPROTO.fields_by_name['path_text'].message_type = _PATHTEXTRULEPROTO
_CLASSIFELEMENTPROTO.fields_by_name['element'].message_type = _DRAWELEMENTPROTO
_CONTAINERPROTO.fields_by_name['cont'].message_type = _CLASSIFELEMENTPROTO
DESCRIPTOR.message_types_by_name['DashDotProto'] = _DASHDOTPROTO
DESCRIPTOR.message_types_by_name['PathSymProto'] = _PATHSYMPROTO
DESCRIPTOR.message_types_by_name['LineRuleProto'] = _LINERULEPROTO
DESCRIPTOR.message_types_by_name['LineDefProto'] = _LINEDEFPROTO
DESCRIPTOR.message_types_by_name['AreaRuleProto'] = _AREARULEPROTO
DESCRIPTOR.message_types_by_name['SymbolRuleProto'] = _SYMBOLRULEPROTO
DESCRIPTOR.message_types_by_name['CaptionDefProto'] = _CAPTIONDEFPROTO
DESCRIPTOR.message_types_by_name['CaptionRuleProto'] = _CAPTIONRULEPROTO
DESCRIPTOR.message_types_by_name['CircleRuleProto'] = _CIRCLERULEPROTO
DESCRIPTOR.message_types_by_name['PathTextRuleProto'] = _PATHTEXTRULEPROTO
DESCRIPTOR.message_types_by_name['DrawElementProto'] = _DRAWELEMENTPROTO
DESCRIPTOR.message_types_by_name['ClassifElementProto'] = _CLASSIFELEMENTPROTO
DESCRIPTOR.message_types_by_name['ContainerProto'] = _CONTAINERPROTO
class DashDotProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _DASHDOTPROTO
# @@protoc_insertion_point(class_scope:DashDotProto)
class PathSymProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _PATHSYMPROTO
# @@protoc_insertion_point(class_scope:PathSymProto)
class LineRuleProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _LINERULEPROTO
# @@protoc_insertion_point(class_scope:LineRuleProto)
class LineDefProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _LINEDEFPROTO
# @@protoc_insertion_point(class_scope:LineDefProto)
class AreaRuleProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _AREARULEPROTO
# @@protoc_insertion_point(class_scope:AreaRuleProto)
class SymbolRuleProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _SYMBOLRULEPROTO
# @@protoc_insertion_point(class_scope:SymbolRuleProto)
class CaptionDefProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _CAPTIONDEFPROTO
# @@protoc_insertion_point(class_scope:CaptionDefProto)
class CaptionRuleProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _CAPTIONRULEPROTO
# @@protoc_insertion_point(class_scope:CaptionRuleProto)
class CircleRuleProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _CIRCLERULEPROTO
# @@protoc_insertion_point(class_scope:CircleRuleProto)
class PathTextRuleProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _PATHTEXTRULEPROTO
# @@protoc_insertion_point(class_scope:PathTextRuleProto)
class DrawElementProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _DRAWELEMENTPROTO
# @@protoc_insertion_point(class_scope:DrawElementProto)
class ClassifElementProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _CLASSIFELEMENTPROTO
# @@protoc_insertion_point(class_scope:ClassifElementProto)
class ContainerProto(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _CONTAINERPROTO
# @@protoc_insertion_point(class_scope:ContainerProto)
# @@protoc_insertion_point(module_scope)

438
tools/kothic/libkomwm.py Normal file
View file

@ -0,0 +1,438 @@
from drules_struct_pb2 import *
from timer import *
from mapcss import MapCSS
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):
if options.outfile == "-":
print "Please specify base output path."
exit()
else:
ddir = os.path.dirname(options.outfile)
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")
drules = ContainerProto()
classificator = {}
class_order = []
class_tree = {}
visibility = {}
textures = {}
for row in csv.reader(open(os.path.join(ddir, 'mapcss-mapping.csv')), delimiter=';'):
pairs = [i.strip(']').split("=") for i in row[1].split(',')[0].split('[')]
kv = {}
for i in pairs:
if len(i) == 1:
if i[0]:
if i[0][0] == "!":
kv[i[0][1:].strip('?')] = "no"
else:
kv[i[0].strip('?')] = "yes"
else:
kv[i[0]] = i[1]
classificator[row[0].replace("|", "-")] = kv
if row[2] != "x":
class_order.append(row[0].replace("|", "-"))
print >> types_file, row[0]
else:
# compatibility mode
if row[6]:
print >> types_file, row[6]
else:
print >> types_file, "mapswithme"
class_tree[row[0].replace("|", "-")] = 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)
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
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()
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, cache=False)
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, cache=False)
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, cache=False)
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()
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
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
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_lines or has_text or has_fills or has_icons:
visstring[zoom] = "1"
dr_element = DrawElementProto()
dr_element.scale = zoom
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])
textures[icon[0]] = icon[1]
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 = 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
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()
drules_bin.write(drules.SerializeToString())
drules_txt.write(unicode(drules))
visnodes = set()
for k, v in visibility.iteritems():
vis = k.split("|")
for i in range(1, len(vis) - 1):
visnodes.add("|".join(vis[0:i]) + "|")
viskeys = list(set(visibility.keys() + list(visnodes)))
def cmprepl(a, b):
if a == b:
return 0
a = a.replace("|", "-")
b = b.replace("|", "-")
if a > b:
return 1
return -1
viskeys.sort(cmprepl)
visibility_file = open(os.path.join(ddir, 'visibility.txt'), "w")
classificator_file = open(os.path.join(ddir, 'classificator.txt'), "w")
oldoffset = ""
for k in viskeys:
offset = " " * (k.count("|") - 1)
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:
end = "+"
print >> visibility_file, offset + k.split("|")[-2] + " " + visibility.get(k, "0" * (options.maxzoom + 1)) + " " + end
print >> classificator_file, offset + k.split("|")[-2] + " " + end
for i in range(len(offset) / 4, 0, -1):
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())
# 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("-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()
if (options.filename is None):
parser.error("MapCSS stylesheet filename is required")
try:
# atparse = AccumulativeTimer()
# atbuild = AccumulativeTimer()
# atparse.Start()
style = MapCSS(options.minzoom, options.maxzoom + 1) # zoom levels
style.parse(filename = options.filename)
# atparse.Stop()
# atbuild.Start()
komap_mapswithme(options, style, options.filename)
# atbuild.Stop()
# print "mapcss parse, sec: %s" % (atparse.ElapsedSec())
# print "build, sec: %s" % (atbuild.ElapsedSec())
exit(0)
except Exception as e:
print >> sys.stderr, "Error\n" + str(e)
exit(-1)

View file

@ -0,0 +1,329 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of kothic, the realtime map renderer.
# kothic is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# kothic is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with kothic. If not, see <http://www.gnu.org/licenses/>.
import re
INVERSIONS = {"eq": "ne", "true": "false", "set": "unset", "<": ">=", ">": "<="}
in2 = {}
for a, b in INVERSIONS.iteritems():
in2[b] = a
INVERSIONS.update(in2)
del in2
# 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')
if typez == "regex":
self.regex = re.compile(self.params[0], re.I)
self.compiled_regex = ""
def get_interesting_tags(self):
if self.params[0][:2] == "::":
return []
return set([self.params[0]])
def extract_tags(self):
if self.params[0][:2] == "::" or self.type == "regex":
return set(["*"]) # unknown
return set([self.params[0]])
def get_numerics(self):
if self.type in ("<", ">", ">=", "<="):
return self.params[0]
else:
return False
def test(self, tags):
"""
Test a hash against this condition
"""
t = self.type
params = self.params
if t == 'eq': # don't compare tags against sublayers
if params[0][:2] == "::":
return params[1]
try:
if t == 'eq':
return tags[params[0]] == params[1]
if t == 'ne':
return tags.get(params[0], "") != params[1]
if t == 'regex':
return bool(self.regex.match(tags[params[0]]))
if t == 'true':
return tags.get(params[0]) == 'yes'
if t == 'untrue':
return tags.get(params[0]) == 'no'
if t == 'set':
if params[0] in tags:
return tags[params[0]] != ''
return False
if t == 'unset':
if params[0] in tags:
return tags[params[0]] == ''
return True
if t == '<':
return (Number(tags[params[0]]) < Number(params[1]))
if t == '<=':
return (Number(tags[params[0]]) <= Number(params[1]))
if t == '>':
return (Number(tags[params[0]]) > Number(params[1]))
if t == '>=':
return (Number(tags[params[0]]) >= Number(params[1]))
except KeyError:
pass
return False
def inverse(self):
"""
Get a not-A for condition A
"""
t = self.type
params = self.params
try:
return Condition(INVERSIONS[t], params)
if t == 'regex':
### FIXME: learn how to invert regexes
return Condition("regex", params)
except KeyError:
pass
return self
def get_sql(self):
# params = [re.escape(x) for x in self.params]
params = self.params
t = self.type
if t == 'eq': # don't compare tags against sublayers
if params[0][:2] == "::":
return ("", "")
try:
if t == 'eq':
return params[0], '"%s" = \'%s\'' % (params[0], params[1])
if t == 'ne':
return params[0], '("%s" != \'%s\' or "%s" IS NULL)' % (params[0], params[1], params[0])
if t == 'regex':
return params[0], '"%s" ~ \'%s\'' % (params[0], params[1].replace("'", "\\'"))
if t == 'true':
return params[0], '"%s" = \'yes\'' % (params[0])
if t == 'untrue':
return params[0], '"%s" = \'no\'' % (params[0])
if t == 'set':
return params[0], '"%s" IS NOT NULL' % (params[0])
if t == 'unset':
return params[0], '"%s" IS NULL' % (params[0])
if t == '<':
return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) &lt; %s ELSE false END) """ % (params[0], params[0], params[1])
if t == '<=':
return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) &lt;= %s ELSE false END)""" % (params[0], params[0], params[1])
if t == '>':
return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) > %s ELSE false END) """ % (params[0], params[0], params[1])
if t == '>=':
return params[0], """(CASE WHEN "%s" ~ E'^[-]?[[:digit:]]+([.][[:digit:]]+)?$' THEN CAST ("%s" AS FLOAT) >= %s ELSE false END) """ % (params[0], params[0], params[1])
except KeyError:
pass
def get_mapnik_filter(self):
# params = [re.escape(x) for x in self.params]
params = self.params
t = self.type
if t == 'eq': # don't compare tags against sublayers
if params[0][:2] == "::":
return ''
try:
if t == 'eq':
return '[%s] = \'%s\'' % (params[0], params[1])
if t == 'ne':
return 'not([%s] = \'%s\')' % (params[0], params[1])
if t == 'regex':
return '[%s].match(\'%s\')' % (params[0], params[1].replace("'", "\\'"))
if t == 'true':
return '[%s] = \'yes\'' % (params[0])
if t == 'untrue':
return '[%s] = \'no\'' % (params[0])
if t == 'set':
return '[%s] != \'\'' % (params[0])
if t == 'unset':
return 'not([%s] != \'\')' % (params[0])
if t == '<':
return '[%s__num] &lt; %s' % (params[0], float(params[1]))
if t == '<=':
return '[%s__num] &lt;= %s' % (params[0], float(params[1]))
if t == '>':
return '[%s__num] &gt; %s' % (params[0], float(params[1]))
if t == '>=':
return '[%s__num] &gt;= %s' % (params[0], float(params[1]))
# return ""
except KeyError:
pass
def __repr__(self):
return "%s %s " % (self.type, repr(self.params))
def __eq__(self, a):
return (self.params == a.params) and (self.type == a.type)
def and_with(self, c2):
"""
merges two rules with AND.
"""
# TODO: possible other minimizations
if c2.params[0] == self.params[0]:
if c2.params == self.params:
if c2.type == INVERSIONS[self.type]: # for example, eq AND ne = 0
return False
if c2.type == self.type:
return (self,)
if self.type == ">=" and c2.type == "<=": # a<=2 and a>=2 --> a=2
return (Condition("eq", self.params),)
if self.type == "<=" and c2.type == ">=":
return (Condition("eq", self.params),)
if self.type == ">" and c2.type == "<":
return False
if self.type == "<" and c2.type == ">":
return False
if c2.type == "eq" and self.type in ("ne", "<", ">"):
if c2.params[1] != self.params[1]:
return (c2,)
if self.type == "eq" and c2.type in ("ne", "<", ">"):
if c2.params[1] != self.params[1]:
return (self,)
if self.type == "eq" and c2.type == "eq":
if c2.params[1] != self.params[1]:
return False
if c2.type == "set" and self.type in ("eq", "ne", "regex", "<", "<=", ">", ">="): # a is set and a == b -> a == b
return (self,)
if c2.type == "unset" and self.type in ("eq", "ne", "regex", "<", "<=", ">", ">="): # a is unset and a == b -> impossible
return False
if self.type == "set" and c2.type in ("eq", "ne", "regex", "<", "<=", ">", ">="):
return (c2,)
if self.type == "unset" and c2.type in ("eq", "ne", "regex", "<", "<=", ">", ">="):
return False
return self, c2
def Number(tt):
"""
Wrap float() not to produce exceptions
"""
try:
return float(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

183
tools/kothic/mapcss/Eval.py Normal file
View file

@ -0,0 +1,183 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of kothic, the realtime map renderer.
# kothic is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# kothic is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with kothic. If not, see <http://www.gnu.org/licenses/>.
NONE = ""
class Eval():
def __init__(self, s='eval()'):
"""
Parse expression and convert it into Python
"""
s = s.strip()[5:-1].strip()
self.expr_text = s
try:
self.expr = compile(s, "MapCSS expression", "eval")
except:
# print "Can't compile %s" % s
self.expr = compile("0", "MapCSS expression", "eval")
def extract_tags(self):
"""
Extracts list of tags that might be used in calculation
"""
def fake_compute(*x):
"""
Perform a fake computation. Always computes all the parameters, always returns 0.
WARNING: Might not cope with complex statements.
"""
for t in x:
q = x
return 0
# print self.expr_text
tags = set([])
a = eval(self.expr, {}, {
"tag": lambda x: max([tags.add(x), 0]),
"prop": lambda x: 0,
"num": lambda x: 0,
"metric": fake_compute,
"zmetric": fake_compute,
"str": lambda x: "",
"any": fake_compute,
"min": fake_compute,
"max": fake_compute,
})
return tags
def compute(self, tags={}, props={}, xscale=1., zscale=0.5):
"""
Compute this eval()
"""
"""
for k, v in tags.iteritems():
try:
tags[k] = float(v)
except:
pass
"""
try:
return str(eval(self.expr, {}, {
"tag": lambda x: tags.get(x, ""),
"prop": lambda x: props.get(x, ""),
"num": m_num,
"metric": lambda x: m_metric(x, xscale),
"zmetric": lambda x: m_metric(x, zscale),
"str": str,
"any": m_any,
"min": m_min,
"max": m_max,
"cond": m_cond,
"boolean": m_boolean
}))
except:
return ""
def __repr__(self):
return "eval(%s)" % self.expr_text
def m_boolean(expr):
expr = str(expr)
if expr in ("", "0", "no", "false", "False"):
return False
else:
return True
def m_cond(why, yes, no):
if m_boolean(why):
return yes
else:
return no
def m_min(*x):
"""
min() MapCSS Feature
"""
try:
return min([m_num(t) for t in x])
except:
return 0
def m_max(*x):
"""
max() MapCSS Feature
"""
try:
return max([m_num(t) for t in x])
except:
return 0
def m_any(*x):
"""
any() MapCSS feature
"""
for t in x:
if t:
return t
else:
return ""
def m_num(x):
"""
num() MapCSS feature
"""
try:
return float(str(x))
except ValueError:
return 0
def m_metric(x, t):
"""
metric() and zmetric() function.
"""
x = str(x)
try:
return float(x) * float(t)
except:
"Heuristics."
# FIXME: add ft, m and friends
x = x.strip()
try:
if x[-2:] in ("cm", "CM", "см"):
return float(x[0:-2]) * float(t) / 100
if x[-2:] in ("mm", "MM", "мм"):
return float(x[0:-2]) * float(t) / 1000
if x[-1] in ("m", "M", "м"):
return float(x[0:-1]) * float(t)
except:
return ""
# def str(x):
#"""
# str() MapCSS feature
#"""
# return __builtins__.str(x)
if __name__ == "__main__":
a = Eval(""" eval( any( metric(tag("height")), metric ( num(tag("building:levels")) * 3), metric("1m"))) """)
print repr(a)
print a.compute({"building:levels": "3"})
print a.extract_tags()

129
tools/kothic/mapcss/Rule.py Normal file
View file

@ -0,0 +1,129 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of kothic, the realtime map renderer.
# kothic is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# kothic is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with kothic. If not, see <http://www.gnu.org/licenses/>.
type_matches = {
"": ('area', 'line', 'way', 'node'),
"area": ("area", "way"),
"node": ("node",),
"way": ("line", "area", "way"),
"line": ("line", "area"),
}
class Rule():
def __init__(self, s=''):
self.conditions = []
# self.isAnd = True
self.minZoom = 0
self.maxZoom = 19
if s == "*":
s = ""
self.subject = s # "", "way", "node" or "relation"
def __repr__(self):
return "%s|z%s-%s %s" % (self.subject, self.minZoom, self.maxZoom, self.conditions)
def test(self, obj, tags, zoom):
if (zoom < self.minZoom) or (zoom > self.maxZoom):
return False
if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, tags):
return False
subpart = "::default"
for condition in self.conditions:
res = condition.test(tags)
if not res:
return False
if type(res) != bool:
subpart = res
return subpart
def test_zoom(self, zoom):
return (zoom >= self.minZoom) and (zoom <= self.maxZoom)
def get_compatible_types(self):
return type_matches.get(self.subject, (self.subject,))
def get_interesting_tags(self, obj, zoom):
if obj:
if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, {}):
return set()
if zoom and not self.test_zoom(zoom):
return set()
a = set()
for condition in self.conditions:
a.update(condition.get_interesting_tags())
return a
def extract_tags(self):
a = set()
for condition in self.conditions:
a.update(condition.extract_tags())
if "*" in a:
break
return a
def get_numerics(self):
a = set()
for condition in self.conditions:
a.add(condition.get_numerics())
a.discard(False)
return a
def get_sql_hints(self, obj, zoom):
if obj:
if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, {":area": "yes"}):
return set()
if not self.test_zoom(zoom):
return set()
a = set()
b = set()
for condition in self.conditions:
q = condition.get_sql()
if q:
if q[1]:
a.add(q[0])
b.add(q[1])
b = " AND ".join(b)
return a, b
def _test_feature_compatibility(f1, f2, tags={}):
"""
Checks if feature of type f1 is compatible with f2.
"""
if f2 == f1:
return True
if f2 not in ("way", "area", "line"):
return False
elif f2 == "way" and f1 == "line":
return True
elif f2 == "way" and f1 == "area":
return True
elif f2 == "area" and f1 in ("way", "area"):
# if ":area" in tags:
return True
# else:
# return False
elif f2 == "line" and f1 in ("way", "line", "area"):
return True
else:
return False
# print f1, f2, True
return True

View file

@ -0,0 +1,291 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of kothic, the realtime map renderer.
# kothic is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# kothic is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with kothic. If not, see <http://www.gnu.org/licenses/>.
from Rule import Rule
from webcolors.webcolors import whatever_to_cairo as colorparser
from webcolors.webcolors import cairo_to_hex
from Eval import Eval
from Condition import *
TYPE_EVAL = type(Eval())
def make_nice_style(r):
ra = {}
for a, b in r.iteritems():
"checking and nicifying style table"
if type(b) == TYPE_EVAL:
ra[a] = b
elif "color" in a:
"parsing color value to 3-tuple"
# print "res:", b
if b and (type(b) != tuple):
# if not b:
# print sl, ftype, tags, zoom, scale, zscale
# else:
ra[a] = colorparser(b)
elif b:
ra[a] = b
elif any(x in a for x in ("width", "z-index", "opacity", "offset", "radius", "extrude")):
"these things are float's or not in table at all"
try:
ra[a] = float(b)
except ValueError:
pass
elif "dashes" in a and type(b) != list:
"these things are arrays of float's or not in table at all"
try:
b = b.split(",")
b = [float(x) for x in b]
ra[a] = b
except ValueError:
ra[a] = []
else:
ra[a] = b
return ra
class StyleChooser:
"""
A StyleChooser object is equivalent to one CSS selector+declaration.
Its ruleChains property is an array of all the selectors, which would
traditionally be comma-separated. For example:
h1, h2, h3 em
is three ruleChains.
Each ruleChain is itself an array of nested selectors. So the above
example would roughly be encoded as:
[[h1],[h2],[h3,em]]
^^ ^^ ^^ ^^ each of these is a Rule
The styles property is an array of all the style objects to be drawn
if any of the ruleChains evaluate to true.
"""
def __repr__(self):
return "{(%s) : [%s] }\n" % (self.ruleChains, self.styles)
def __init__(self, scalepair):
self.ruleChains = []
self.styles = []
self.eval_type = TYPE_EVAL
self.scalepair = scalepair
self.selzooms = None
self.compatible_types = set()
self.has_evals = False
def get_numerics(self):
"""
Returns a set of number-compared values.
"""
a = set()
for r in self.ruleChains:
a.update(r.get_numerics())
a.discard(False)
return a
def get_interesting_tags(self, ztype, zoom):
"""
Returns a set of tags that were used in here.
"""
### FIXME
a = set()
for r in self.ruleChains:
a.update(r.get_interesting_tags(ztype, zoom))
if a: # FIXME: semi-illegal optimization, may wreck in future on tagless matches
for r in self.styles:
for c, b in r.iteritems():
if type(b) == self.eval_type:
a.update(b.extract_tags())
return a
def extract_tags(self):
a = set()
for r in self.ruleChains:
a.update(r.extract_tags())
if "*" in a:
a.clear()
a.add("*")
break
if self.has_evals and "*" not in a:
for s in self.styles:
for v in s.values():
if type(v) == self.eval_type:
a.update(v.extract_tags())
if "*" in a or len(a) == 0:
a.clear()
a.add("*")
return a
def get_sql_hints(self, type, zoom):
"""
Returns a set of tags that were used in here in form of SQL-hints.
"""
a = set()
b = ""
needed = set(["width", "casing-width", "fill-color", "fill-image", "icon-image", "text", "extrude", "background-image", "background-color", "pattern-image", "shield-text"])
if not needed.isdisjoint(set(self.styles[0].keys())):
for r in self.ruleChains:
p = r.get_sql_hints(type, zoom)
if p:
q = "(" + p[1] + ")" # [t[1] for t in p]
if q == "()":
q = ""
if b and q:
b += " OR " + q
else:
b = q
a.update(p[0])
# no need to check for eval's
return a, b
def updateStyles(self, sl, ftype, tags, zoom, scale, zscale):
# Are any of the ruleChains fulfilled?
if self.selzooms:
if zoom < self.selzooms[0] or zoom > self.selzooms[1]:
return sl
#if ftype not in self.compatible_types:
# 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 = {}
for a, b in r.iteritems():
"calculating eval()'s"
if type(b) == self.eval_type:
combined_style = {}
for t in sl:
combined_style.update(t)
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)
ra[a] = b
#r = ra
ra = make_nice_style(ra)
else:
ra = r.copy()
ra["object-id"] = str(object_id)
hasall = False
allinit = {}
for x in sl:
if x.get("object-id") == "::*":
allinit = x.copy()
if ra["object-id"] == "::*":
oid = x.get("object-id")
x.update(ra)
x["object-id"] = oid
if oid == "::*":
hasall = True
else:
if x.get("object-id") == ra["object-id"]:
x.update(ra)
break
else:
if not hasall:
allinit.update(ra)
sl.append(allinit)
return sl
def testChain(self, chain, obj, tags, zoom):
"""
Tests an object against a chain
"""
for r in chain:
tt = r.test(obj, tags, zoom)
if tt:
return tt
return False
def newGroup(self):
"""
starts a new ruleChain in this.ruleChains
"""
pass
def newObject(self, e=''):
# print "newRule"
"""
adds into the current ruleChain (starting a new Rule)
"""
rule = Rule(e)
rule.minZoom = float(self.scalepair[0])
rule.maxZoom = float(self.scalepair[1])
self.ruleChains.append(rule)
def addZoom(self, z):
# print "addZoom ", float(z[0]), ", ", float(z[1])
"""
adds into the current ruleChain (existing Rule)
"""
self.ruleChains[-1].minZoom = float(z[0])
self.ruleChains[-1].maxZoom = float(z[1])
def addCondition(self, c):
# print "addCondition ", c
"""
adds into the current ruleChain (existing Rule)
"""
c = OptimizeCondition(c)
self.ruleChains[-1].conditions.append(c)
def addStyles(self, a):
# print "addStyle ", a
"""
adds to this.styles
"""
for r in self.ruleChains:
if not self.selzooms:
self.selzooms = [r.minZoom, r.maxZoom]
else:
self.selzooms[0] = min(self.selzooms[0], r.minZoom)
self.selzooms[1] = max(self.selzooms[1], r.maxZoom)
self.compatible_types.update(r.get_compatible_types())
rb = []
for r in a:
ra = {}
for a, b in r.iteritems():
a = a.strip()
b = b.strip()
if a == "casing-width":
"josm support"
if b[0] == "+":
try:
b = str(float(b) / 2)
except:
pass
if "text" == a[-4:]:
if b[:5] != "eval(":
b = "eval(tag(\"" + b + "\"))"
if b[:5] == "eval(":
b = Eval(b)
self.has_evals = True
ra[a] = b
ra = make_nice_style(ra)
rb.append(ra)
self.styles = self.styles + rb

View file

@ -0,0 +1,471 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of kothic, the realtime map renderer.
# kothic is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# kothic is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with kothic. If not, see <http://www.gnu.org/licenses/>.
import re
import os
import logging
from hashlib import md5
from copy import copy, deepcopy
from StyleChooser import StyleChooser
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)
CLASS = re.compile(r'^ ([\.:]:?[*\w]+) \s* ', re.S | re.X)
#NOT_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)
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)
VARIABLE_SET = re.compile(r'^@([a-z][\w\d]*) \s* : \s* (.+?) \s* ; \s* ', re.S | re.X | re.I)
UNKNOWN = re.compile(r'^ (\S+) \s* ', re.S | re.X)
ZOOM_MINMAX = re.compile(r'^ (\d+)\-(\d+) $', re.S | re.X)
ZOOM_MIN = re.compile(r'^ (\d+)\- $', re.S | re.X)
ZOOM_MAX = re.compile(r'^ \-(\d+) $', re.S | re.X)
ZOOM_SINGLE = re.compile(r'^ (\d+) $', re.S | re.X)
CONDITION_TRUE = re.compile(r'^ \s* ([:\w]+) \s* [?] \s* $', re.I | re.S | re.X)
CONDITION_invTRUE = re.compile(r'^ \s* [!] \s* ([:\w]+) \s* [?] \s* $', re.I | re.S | re.X)
CONDITION_FALSE = re.compile(r'^ \s* ([:\w]+) \s* = \s* no \s* $', re.I | re.S | re.X)
CONDITION_SET = re.compile(r'^ \s* ([-:\w]+) \s* $', re.S | re.X)
CONDITION_UNSET = re.compile(r'^ \s* !([:\w]+) \s* $', re.S | re.X)
CONDITION_EQ = re.compile(r'^ \s* ([:\w]+) \s* = \s* (.+) \s* $', re.S | re.X)
CONDITION_NE = re.compile(r'^ \s* ([:\w]+) \s* != \s* (.+) \s* $', re.S | re.X)
CONDITION_GT = re.compile(r'^ \s* ([:\w]+) \s* > \s* (.+) \s* $', re.S | re.X)
CONDITION_GE = re.compile(r'^ \s* ([:\w]+) \s* >= \s* (.+) \s* $', re.S | re.X)
CONDITION_LT = re.compile(r'^ \s* ([:\w]+) \s* < \s* (.+) \s* $', re.S | re.X)
CONDITION_LE = re.compile(r'^ \s* ([:\w]+) \s* <= \s* (.+) \s* $', re.S | re.X)
CONDITION_REGEX = re.compile(r'^ \s* ([:\w]+) \s* =~\/ \s* (.+) \/ \s* $', re.S | re.X)
ASSIGNMENT_EVAL = re.compile(r"^ \s* (\S+) \s* \: \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $", re.I | re.S | re.X)
ASSIGNMENT = re.compile(r'^ \s* (\S+) \s* \: \s* (.+?) \s* $', re.S | re.X)
SET_TAG_EVAL = re.compile(r"^ \s* set \s+(\S+)\s* = \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $", re.I | re.S | re.X)
SET_TAG = re.compile(r'^ \s* set \s+(\S+)\s* = \s* (.+?) \s* $', re.I | re.S | re.X)
SET_TAG_TRUE = re.compile(r'^ \s* set \s+(\S+)\s* $', re.I | re.S | re.X)
EXIT = re.compile(r'^ \s* exit \s* $', re.I | re.S | re.X)
oZOOM = 2
oGROUP = 3
oCONDITION = 4
oOBJECT = 5
oDECLARATION = 6
oSUBPART = 7
oVARIABLE_SET = 8
DASH = re.compile(r'\-/g')
COLOR = re.compile(r'color$/')
BOLD = re.compile(r'^bold$/i')
ITALIC = re.compile(r'^italic|oblique$/i')
UNDERLINE = re.compile(r'^underline$/i')
CAPS = re.compile(r'^uppercase$/i')
CENTER = re.compile(r'^center$/i')
HEX = re.compile(r'^#([0-9a-f]+)$/i')
VARIABLE = re.compile(r'@([a-z][\w\d]*)')
class MapCSS():
def __init__(self, minscale=0, maxscale=19):
"""
"""
self.cache = {}
self.cache["style"] = {}
self.minscale = minscale
self.maxscale = maxscale
self.scalepair = (minscale, maxscale)
self.choosers = []
self.choosers_by_type = {}
self.choosers_by_type_and_tag = {}
self.variables = {}
self.style_loaded = False
def parseZoom(self, s):
if ZOOM_MINMAX.match(s):
return tuple([float(i) for i in ZOOM_MINMAX.match(s).groups()])
elif ZOOM_MIN.match(s):
return float(ZOOM_MIN.match(s).groups()[0]), self.maxscale
elif ZOOM_MAX.match(s):
return float(self.minscale), float(ZOOM_MAX.match(s).groups()[0])
elif ZOOM_SINGLE.match(s):
return float(ZOOM_SINGLE.match(s).groups()[0]), float(ZOOM_SINGLE.match(s).groups()[0])
else:
logging.error("unparsed zoom: %s" % s)
def build_choosers_tree(self, clname, type, tags={}):
if type not in self.choosers_by_type_and_tag:
self.choosers_by_type_and_tag[type] = {}
if clname not in self.choosers_by_type_and_tag[type]:
self.choosers_by_type_and_tag[type][clname] = set()
if type in self.choosers_by_type:
for chooser in self.choosers_by_type[type]:
for tag in chooser.extract_tags():
if tag == "*" or tag in tags:
if chooser not in self.choosers_by_type_and_tag[type][clname]:
self.choosers_by_type_and_tag[type][clname].add(chooser)
break
def restore_choosers_order(self, type):
ethalon_choosers = self.choosers_by_type[type]
for tag, choosers_for_tag in self.choosers_by_type_and_tag[type].items():
tmp = []
for ec in ethalon_choosers:
if ec in choosers_for_tag:
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):
"""
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]
for chooser in choosers:
style = chooser.updateStyles(style, type, tags, zoom, scale, zscale)
style = [x for x in style if x["object-id"] != "::*"]
for x in style:
for k, v in [('width', 0), ('casing-width', 0)]:
if k in x:
if x[k] == v:
del x[k]
st = []
for x in style:
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)
d = olddict
for x in r:
if x.get('object-id', '') not in d:
d[x.get('object-id', '')] = {}
d[x.get('object-id', '')].update(x)
return d
def get_interesting_tags(self, type=None, zoom=None):
"""
Get set of interesting tags.
"""
tags = set()
for chooser in self.choosers:
tags.update(chooser.get_interesting_tags(type, zoom))
return tags
def get_sql_hints(self, type=None, zoom=None):
"""
Get set of interesting tags.
"""
hints = []
for chooser in self.choosers:
p = chooser.get_sql_hints(type, zoom)
if p:
if p[0] and p[1]:
hints.append(p)
return hints
def subst_variables(self, t):
"""Expects an array from parseDeclaration."""
for k in t[0]:
t[0][k] = VARIABLE.sub(self.get_variable, t[0][k])
return t
def get_variable(self, m):
name = m.group()[1:]
if not name in self.variables:
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):
"""
Parses MapCSS given as string
"""
basepath = os.curdir
if filename:
basepath = os.path.dirname(filename)
if not css:
css = open(filename).read()
if not self.style_loaded:
self.choosers = []
log = logging.getLogger('mapcss.parser')
previous = 0 # what was the previous CSS word?
sc = StyleChooser(self.scalepair) # currently being assembled
stck = [] # filename, original, remained
stck.append([filename, css, css])
try:
while (len(stck) > 0):
css = stck[-1][1].lstrip() # remained
wasBroken = False
while (css):
# Class - :motorway, :builtup, :hover
if CLASS.match(css):
if previous == oDECLARATION:
self.choosers.append(sc)
sc = StyleChooser(self.scalepair)
cond = CLASS.match(css).groups()[0]
log.debug("class found: %s" % (cond))
css = CLASS.sub("", css)
sc.addCondition(Condition('eq', ("::class", cond)))
previous = oCONDITION
## Not class - !.motorway, !.builtup, !:hover
#elif NOT_CLASS.match(css):
#if (previous == oDECLARATION):
#self.choosers.append(sc)
#sc = StyleChooser(self.scalepair)
#cond = NOT_CLASS.match(css).groups()[0]
#log.debug("not_class found: %s" % (cond))
#css = NOT_CLASS.sub("", css)
#sc.addCondition(Condition('ne', ("::class", cond)))
#previous = oCONDITION
# Zoom
elif ZOOM.match(css):
if (previous != oOBJECT & previous != oCONDITION):
sc.newObject()
cond = ZOOM.match(css).groups()[0]
log.debug("zoom found: %s" % (cond))
css = ZOOM.sub("", css)
sc.addZoom(self.parseZoom(cond))
previous = oZOOM
# Grouping - just a comma
elif GROUP.match(css):
css = GROUP.sub("", css)
sc.newGroup()
previous = oGROUP
# Condition - [highway=primary]
elif 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 = CONDITION.match(css).groups()[0]
log.debug("condition found: %s" % (cond))
css = CONDITION.sub("", css)
sc.addCondition(parseCondition(cond))
previous = oCONDITION
# Object - way, node, relation
elif OBJECT.match(css):
if (previous == oDECLARATION):
self.choosers.append(sc)
sc = StyleChooser(self.scalepair)
obj = OBJECT.match(css).groups()[0]
log.debug("object found: %s" % (obj))
css = OBJECT.sub("", css)
sc.newObject(obj)
previous = oOBJECT
# Declaration - {...}
elif DECLARATION.match(css):
decl = DECLARATION.match(css).groups()[0]
log.debug("declaration found: %s" % (decl))
sc.addStyles(self.subst_variables(parseDeclaration(decl)))
css = DECLARATION.sub("", css)
previous = oDECLARATION
# CSS comment
elif COMMENT.match(css):
log.debug("comment found")
css = COMMENT.sub("", css)
# @import("filename.css");
elif IMPORT.match(css):
log.debug("import found")
import_filename = os.path.join(basepath, IMPORT.match(css).groups()[0])
try:
css = IMPORT.sub("", css)
import_text = open(import_filename, "r").read()
stck[-1][1] = css # store remained part
stck.append([import_filename, import_text, import_text])
wasBroken = True
break
except IOError as e:
raise Exception("Cannot import file " + import_filename + "\n" + str(e))
# Variables
elif VARIABLE_SET.match(css):
name = VARIABLE_SET.match(css).groups()[0]
log.debug("variable set found: %s" % name)
self.variables[name] = VARIABLE_SET.match(css).groups()[1]
css = VARIABLE_SET.sub("", css)
previous = oVARIABLE_SET
# Unknown pattern
elif UNKNOWN.match(css):
raise Exception("Unknown construction: " + UNKNOWN.match(css).group())
# Must be unreacheable
else:
raise Exception("Unexpected construction: " + css)
if not wasBroken:
stck.pop()
if (previous == oDECLARATION):
self.choosers.append(sc)
sc = StyleChooser(self.scalepair)
except Exception as e:
filename = stck[-1][0] # filename
css_orig = stck[-1][2] # original
css = stck[-1][1] # remained
line = unicode(css_orig[:-len(unicode(css))]).count("\n") + 1
msg = str(e) + "\nFile: " + filename + "\nLine: " + str(line)
raise Exception(msg)
try:
if clamp:
"clamp z-indexes, so they're tightly following integers"
zindex = set()
for chooser in self.choosers:
for stylez in chooser.styles:
zindex.add(float(stylez.get('z-index', 0)))
zindex = list(zindex)
zindex.sort()
zoffset = len([x for x in zindex if x < 0])
for chooser in self.choosers:
for stylez in chooser.styles:
if 'z-index' in stylez:
res = zindex.index(float(stylez.get('z-index', 0))) - zoffset
if stretch:
stylez['z-index'] = 1. * res / len(zindex) * stretch
else:
stylez['z-index'] = res
except TypeError:
pass
for chooser in self.choosers:
for t in chooser.compatible_types:
if t not in self.choosers_by_type:
self.choosers_by_type[t] = [chooser]
else:
self.choosers_by_type[t].append(chooser)
def parseCondition(s):
log = logging.getLogger('mapcss.parser.condition')
if CONDITION_TRUE.match(s):
a = CONDITION_TRUE.match(s).groups()
log.debug("condition true: %s" % (a[0]))
return Condition('true', a)
if CONDITION_invTRUE.match(s):
a = CONDITION_invTRUE.match(s).groups()
log.debug("condition invtrue: %s" % (a[0]))
return Condition('ne', (a[0], "yes"))
if CONDITION_FALSE.match(s):
a = CONDITION_FALSE.match(s).groups()
log.debug("condition false: %s" % (a[0]))
return Condition('false', a)
if CONDITION_SET.match(s):
a = CONDITION_SET.match(s).groups()
log.debug("condition set: %s" % (a))
return Condition('set', a)
if CONDITION_UNSET.match(s):
a = CONDITION_UNSET.match(s).groups()
log.debug("condition unset: %s" % (a))
return Condition('unset', a)
if CONDITION_NE.match(s):
a = CONDITION_NE.match(s).groups()
log.debug("condition NE: %s = %s" % (a[0], a[1]))
return Condition('ne', a)
if CONDITION_LE.match(s):
a = CONDITION_LE.match(s).groups()
log.debug("condition LE: %s <= %s" % (a[0], a[1]))
return Condition('<=', a)
if CONDITION_GE.match(s):
a = CONDITION_GE.match(s).groups()
log.debug("condition GE: %s >= %s" % (a[0], a[1]))
return Condition('>=', a)
if CONDITION_LT.match(s):
a = CONDITION_LT.match(s).groups()
log.debug("condition LT: %s < %s" % (a[0], a[1]))
return Condition('<', a)
if CONDITION_GT.match(s):
a = CONDITION_GT.match(s).groups()
log.debug("condition GT: %s > %s" % (a[0], a[1]))
return Condition('>', a)
if CONDITION_REGEX.match(s):
a = CONDITION_REGEX.match(s).groups()
log.debug("condition REGEX: %s = %s" % (a[0], a[1]))
return Condition('regex', a)
if CONDITION_EQ.match(s):
a = CONDITION_EQ.match(s).groups()
log.debug("condition EQ: %s = %s" % (a[0], a[1]))
return Condition('eq', a)
else:
raise Exception("condition UNKNOWN: " + s)
def parseDeclaration(s):
"""
Parse declaration string into list of styles
"""
t = {}
for a in s.split(';'):
# if ((o=ASSIGNMENT_EVAL.exec(a))) { t[o[1].replace(DASH,'_')]=new Eval(o[2]); }
if ASSIGNMENT.match(a):
tzz = ASSIGNMENT.match(a).groups()
t[tzz[0]] = tzz[1].strip().strip('"')
logging.debug("%s == %s" % (tzz[0], tzz[1]))
else:
logging.debug("unknown %s" % (a))
return [t]
if __name__ == "__main__":
logging.basicConfig(level=logging.WARNING)
mc = MapCSS(0, 19)

View file

@ -0,0 +1,28 @@
Copyright (c) 2008-2009, James Bennett
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the author nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,439 @@
Metadata-Version: 1.0
Name: webcolors
Version: 1.3
Summary: A library for working with sRGB color specifications as used in HTML and CSS.
Home-page: http://www.bitbucket.org/ubernostrum/webcolors/overview/
Author: James Bennett
Author-email: james@b-list.org
License: UNKNOWN
Download-URL: http://bitbucket.org/ubernostrum/webcolors/downloads/webcolors-1.3.tar.gz
Description: Web colors
==========
A simple library for working with the color names and color codes
defined by the HTML and CSS specifications.
An overview of HTML and CSS colors
----------------------------------
Colors on the Web are specified in `the sRGB color space`_, where each
color is made up of a red component, a green component and a blue
component. This is useful because it maps (fairly) cleanly to the red,
green and blue components of pixels on a computer display, and to the
cone cells of a human eye, which come in three sets roughly
corresponding to the wavelengths of light associated with red, green
and blue.
`The HTML 4 standard`_ defines two ways to specify sRGB colors:
* A hash mark ('#') followed by three pairs of hexdecimal digits,
specifying values for red, green and blue components in that order;
for example, ``#0099cc``. Since each pair of hexadecimal digits can
express 256 different values, this allows up to 256**3 or 16,777,216
unique colors to be specified (though, due to differences in display
technology, not all of these colors may be clearly distinguished on
any given physical display).
* A set of predefined color names which correspond to specific
hexadecimal values; for example, ``white``. HTML 4 defines sixteen
such colors.
`The CSS 2 standard`_ allows any valid HTML 4 color specification, and
adds three new ways to specify sRGB colors:
* A hash mark followed by three hexadecimal digits, which is expanded
into three hexadecimal pairs by repeating each digit; thus ``#09c``
is equivalent to ``#0099cc``.
* The string 'rgb', followed by parentheses, between which are three
numeric values each between 0 and 255, inclusive, which are taken to
be the values of the red, green and blue components in that order;
for example, ``rgb(0, 153, 204)``.
* The same as above, except using percentages instead of numeric
values; for example, ``rgb(0%, 60%, 80%)``.
`The CSS 2.1 revision`_ does not add any new methods of specifying
sRGB colors, but does add one additional named color.
`The CSS 3 color module`_ (currently a W3C Candidate Recommendation)
adds one new way to specify sRGB colors:
* A hue-saturation-lightness triple (HSL), using the construct
``hsl()``.
It also adds support for variable opacity of colors, by allowing the
specification of alpha-channel information, through the ``rgba()`` and
``hsla()`` constructs, which are identical to ``rgb()`` and ``hsl()``
with one exception: a fourth value is supplied, indicating the level
of opacity from ``0.0`` (completely transparent) to ``1.0``
(completely opaque). Though not technically a color, the keyword
``transparent`` is also made available in lieu of a color value, and
corresponds to ``rgba(0,0,0,0)``.
Additionally, CSS3 defines a new set of color names; this set is taken
directly from the named colors defined for SVG (Scalable Vector
Graphics) markup, and is a proper superset of the named colors defined
in CSS 2.1. This set also has significant overlap with traditional X11
color sets as defined by the ``rgb.txt`` file on many Unix and
Unix-like operating systems, though the correspondence is not exact;
the set of X11 colors is not standardized, and the set of CSS3 colors
contains some definitions which diverge significantly from customary
X11 definitions (for example, CSS3's ``green`` is not equivalent to
X11's ``green``; the value which X11 designates ``green`` is
designated ``lime`` in CSS3).
.. _the sRGB color space: http://www.w3.org/Graphics/Color/sRGB
.. _The HTML 4 standard: http://www.w3.org/TR/html401/types.html#h-6.5
.. _The CSS 2 standard: http://www.w3.org/TR/REC-CSS2/syndata.html#value-def-color
.. _The CSS 2.1 revision: http://www.w3.org/TR/CSS21/
.. _The CSS 3 color module: http://www.w3.org/TR/css3-color/
What this module supports
-------------------------
The mappings and functions within this module support the following
methods of specifying sRGB colors, and conversions between them:
* Six-digit hexadecimal.
* Three-digit hexadecimal.
* Integer ``rgb()`` triplet.
* Percentage ``rgb()`` triplet.
* Varying selections of predefined color names (see below).
This module does not support ``hsl()`` triplets, nor does it support
opacity/alpha-channel information via ``rgba()`` or ``hsla()``.
If you need to convert between RGB-specified colors and HSL-specified
colors, or colors specified via other means, consult `the colorsys
module`_ in the Python standard library, which can perform conversions
amongst several common color spaces.
.. _the colorsys module: http://docs.python.org/library/colorsys.html
Normalization
-------------
For colors specified via hexadecimal values, this module will accept
input in the following formats:
* A hash mark (#) followed by three hexadecimal digits, where letters
may be upper- or lower-case.
* A hash mark (#) followed by six hexadecimal digits, where letters
may be upper- or lower-case.
For output which consists of a color specified via hexadecimal values,
and for functions which perform intermediate conversion to hexadecimal
before returning a result in another format, this module always
normalizes such values to the following format:
* A hash mark (#) followed by six hexadecimal digits, with letters
forced to lower-case.
The function ``normalize_hex()`` in this module can be used to perform
this normalization manually if desired; see its documentation for an
explanation of the normalization process.
For colors specified via predefined names, this module will accept
input in the following formats:
* An entirely lower-case name, such as ``aliceblue``.
* A name using initial capitals, such as ``AliceBlue``.
For output which consists of a color specified via a predefined name,
and for functions which perform intermediate conversion to a
predefined name before returning a result in another format, this
module always normalizes such values to be entirely lower-case.
Mappings of color names
-----------------------
For each set of defined color names -- HTML 4, CSS 2, CSS 2.1 and CSS
3 -- this module exports two mappings: one of normalized color names
to normalized hexadecimal values, and one of normalized hexadecimal
values to normalized color names. These eight mappings are as follows:
``html4_names_to_hex``
Mapping of normalized HTML 4 color names to normalized hexadecimal
values.
``html4_hex_to_names``
Mapping of normalized hexadecimal values to normalized HTML 4
color names.
``css2_names_to_hex``
Mapping of normalized CSS 2 color names to normalized hexadecimal
values. Because CSS 2 defines the same set of named colors as HTML
4, this is merely an alias for ``html4_names_to_hex``.
``css2_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS 2 color
nams. For the reasons described above, this is merely an alias for
``html4_hex_to_names``.
``css21_names_to_hex``
Mapping of normalized CSS 2.1 color names to normalized
hexadecimal values. This is identical to ``html4_names_to_hex``,
except for one addition: ``orange``.
``css21_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS 2.1
color names. As above, this is identical to ``html4_hex_to_names``
except for the addition of ``orange``.
``css3_names_to_hex``
Mapping of normalized CSS3 color names to normalized hexadecimal
values.
``css3_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS3 color
names.
Normalization functions
-----------------------
``normalize_hex(hex_value)``
Normalize a hexadecimal color value to the following form and
return the result::
#[a-f0-9]{6}
In other words, the following transformations are applied as
needed:
* If the value contains only three hexadecimal digits, it is
expanded to six.
* The value is normalized to lower-case.
If the supplied value cannot be interpreted as a hexadecimal color
value, ``ValueError`` is raised.
Example:
>>> normalize_hex('#09c')
'#0099cc'
Conversions from named colors
-----------------------------
``name_to_hex(name, spec='css3')``
Convert a color name to a normalized hexadecimal color value.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Example:
>>> name_to_hex('deepskyblue')
'#00bfff'
``name_to_rgb(name, spec='css3')``
Convert a color name to a 3-tuple of integers suitable for use in
an ``rgb()`` triplet specifying that color.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Example:
>>> name_to_rgb('navy')
(0, 0, 128)
``name_to_rgb_percent(name, spec='css3')``
Convert a color name to a 3-tuple of percentages suitable for use
in an ``rgb()`` triplet specifying that color.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Example:
>>> name_to_rgb_percent('navy')
('0%', '0%', '50%')
Conversions from hexadecimal values
-----------------------------------
``hex_to_name(hex_value, spec='css3')``
Convert a hexadecimal color value to its corresponding normalized
color name, if any such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The hexadecimal value will be normalized before being looked up,
and when no color name for the value is found in the given
specification, ``ValueError`` is raised.
Example:
>>> hex_to_name('#000080')
'navy'
``hex_to_rgb(hex_value)``
Convert a hexadecimal color value to a 3-tuple of integers
suitable for use in an ``rgb()`` triplet specifying that color.
The hexadecimal value will be normalized before being converted.
Example:
>>> hex_to_rgb('#000080')
(0, 0, 128)
``hex_to_rgb_percent(hex_value)``
Convert a hexadecimal color value to a 3-tuple of percentages
suitable for use in an ``rgb()`` triplet representing that color.
The hexadecimal value will be normalized before converting.
Example:
>>> hex_to_rgb_percent('#ffffff')
('100%', '100%', '100%')
Conversions from integer rgb() triplets
---------------------------------------
``rgb_to_name(rgb_triplet, spec='css3')``
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to its corresponding normalized color name, if any
such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
If there is no matching name, ``ValueError`` is raised.
Example:
>>> rgb_to_name((0, 0, 0))
'black'
``rgb_to_hex(rgb_triplet)``
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to a normalized hexadecimal value for that color.
Example:
>>> rgb_to_hex((255, 255, 255))
'#ffffff'
``rgb_to_rgb_percent(rgb_triplet)``
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to a 3-tuple of percentages suitable for use in
representing that color.
This function makes some trade-offs in terms of the accuracy of
the final representation; for some common integer values,
special-case logic is used to ensure a precise result (e.g.,
integer 128 will always convert to '50%', integer 32 will always
convert to '12.5%'), but for all other values a standard Python
``float`` is used and rounded to two decimal places, which may
result in a loss of precision for some values.
Examples:
>>> rgb_to_rgb_percent((255, 255, 255))
('100%', '100%', '100%')
>>> rgb_to_rgb_percent((0, 0, 128))
('0%', '0%', '50%')
>>> rgb_to_rgb_percent((33, 56, 192))
('12.94%', '21.96%', '75.29%')
>>> rgb_to_rgb_percent((64, 32, 16))
('25%', '12.5%', '6.25%')
Conversions from percentage rgb() triplets
------------------------------------------
``rgb_percent_to_name(rgb_percent_triplet, spec='css3')``
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to its corresponding normalized color name, if any
such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
If there is no matching name, ``ValueError`` is raised.
Example:
>>> rgb_percent_to_name(('0%', '0%', '50%'))
'navy'
``rgb_percent_to_hex(rgb_percent_triplet)``
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to a normalized hexadecimal color value for that
color.
Example:
>>> rgb_percent_to_hex(('100%', '100%', '0%'))
'#ffff00'
``rgb_percent_to_rgb(rgb_percent_triplet)``
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to a 3-tuple of integers suitable for use in
representing that color.
Some precision may be lost in this conversion. See the note
regarding precision for ``rgb_to_rgb_percent()`` for details;
generally speaking, the following is true for any 3-tuple ``t`` of
integers in the range 0...255 inclusive::
t == rgb_percent_to_rgb(rgb_to_rgb_percent(t))
Examples:
>>> rgb_percent_to_rgb(('100%', '100%', '100%'))
(255, 255, 255)
>>> rgb_percent_to_rgb(('0%', '0%', '50%'))
(0, 0, 128)
>>> rgb_percent_to_rgb(('25%', '12.5%', '6.25%'))
(64, 32, 16)
>>> rgb_percent_to_rgb(('12.94%', '21.96%', '75.29%'))
(33, 56, 192)
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Utilities

View file

@ -0,0 +1,421 @@
Web colors
==========
A simple library for working with the color names and color codes
defined by the HTML and CSS specifications.
An overview of HTML and CSS colors
----------------------------------
Colors on the Web are specified in `the sRGB color space`_, where each
color is made up of a red component, a green component and a blue
component. This is useful because it maps (fairly) cleanly to the red,
green and blue components of pixels on a computer display, and to the
cone cells of a human eye, which come in three sets roughly
corresponding to the wavelengths of light associated with red, green
and blue.
`The HTML 4 standard`_ defines two ways to specify sRGB colors:
* A hash mark ('#') followed by three pairs of hexdecimal digits,
specifying values for red, green and blue components in that order;
for example, ``#0099cc``. Since each pair of hexadecimal digits can
express 256 different values, this allows up to 256**3 or 16,777,216
unique colors to be specified (though, due to differences in display
technology, not all of these colors may be clearly distinguished on
any given physical display).
* A set of predefined color names which correspond to specific
hexadecimal values; for example, ``white``. HTML 4 defines sixteen
such colors.
`The CSS 2 standard`_ allows any valid HTML 4 color specification, and
adds three new ways to specify sRGB colors:
* A hash mark followed by three hexadecimal digits, which is expanded
into three hexadecimal pairs by repeating each digit; thus ``#09c``
is equivalent to ``#0099cc``.
* The string 'rgb', followed by parentheses, between which are three
numeric values each between 0 and 255, inclusive, which are taken to
be the values of the red, green and blue components in that order;
for example, ``rgb(0, 153, 204)``.
* The same as above, except using percentages instead of numeric
values; for example, ``rgb(0%, 60%, 80%)``.
`The CSS 2.1 revision`_ does not add any new methods of specifying
sRGB colors, but does add one additional named color.
`The CSS 3 color module`_ (currently a W3C Candidate Recommendation)
adds one new way to specify sRGB colors:
* A hue-saturation-lightness triple (HSL), using the construct
``hsl()``.
It also adds support for variable opacity of colors, by allowing the
specification of alpha-channel information, through the ``rgba()`` and
``hsla()`` constructs, which are identical to ``rgb()`` and ``hsl()``
with one exception: a fourth value is supplied, indicating the level
of opacity from ``0.0`` (completely transparent) to ``1.0``
(completely opaque). Though not technically a color, the keyword
``transparent`` is also made available in lieu of a color value, and
corresponds to ``rgba(0,0,0,0)``.
Additionally, CSS3 defines a new set of color names; this set is taken
directly from the named colors defined for SVG (Scalable Vector
Graphics) markup, and is a proper superset of the named colors defined
in CSS 2.1. This set also has significant overlap with traditional X11
color sets as defined by the ``rgb.txt`` file on many Unix and
Unix-like operating systems, though the correspondence is not exact;
the set of X11 colors is not standardized, and the set of CSS3 colors
contains some definitions which diverge significantly from customary
X11 definitions (for example, CSS3's ``green`` is not equivalent to
X11's ``green``; the value which X11 designates ``green`` is
designated ``lime`` in CSS3).
.. _the sRGB color space: http://www.w3.org/Graphics/Color/sRGB
.. _The HTML 4 standard: http://www.w3.org/TR/html401/types.html#h-6.5
.. _The CSS 2 standard: http://www.w3.org/TR/REC-CSS2/syndata.html#value-def-color
.. _The CSS 2.1 revision: http://www.w3.org/TR/CSS21/
.. _The CSS 3 color module: http://www.w3.org/TR/css3-color/
What this module supports
-------------------------
The mappings and functions within this module support the following
methods of specifying sRGB colors, and conversions between them:
* Six-digit hexadecimal.
* Three-digit hexadecimal.
* Integer ``rgb()`` triplet.
* Percentage ``rgb()`` triplet.
* Varying selections of predefined color names (see below).
This module does not support ``hsl()`` triplets, nor does it support
opacity/alpha-channel information via ``rgba()`` or ``hsla()``.
If you need to convert between RGB-specified colors and HSL-specified
colors, or colors specified via other means, consult `the colorsys
module`_ in the Python standard library, which can perform conversions
amongst several common color spaces.
.. _the colorsys module: http://docs.python.org/library/colorsys.html
Normalization
-------------
For colors specified via hexadecimal values, this module will accept
input in the following formats:
* A hash mark (#) followed by three hexadecimal digits, where letters
may be upper- or lower-case.
* A hash mark (#) followed by six hexadecimal digits, where letters
may be upper- or lower-case.
For output which consists of a color specified via hexadecimal values,
and for functions which perform intermediate conversion to hexadecimal
before returning a result in another format, this module always
normalizes such values to the following format:
* A hash mark (#) followed by six hexadecimal digits, with letters
forced to lower-case.
The function ``normalize_hex()`` in this module can be used to perform
this normalization manually if desired; see its documentation for an
explanation of the normalization process.
For colors specified via predefined names, this module will accept
input in the following formats:
* An entirely lower-case name, such as ``aliceblue``.
* A name using initial capitals, such as ``AliceBlue``.
For output which consists of a color specified via a predefined name,
and for functions which perform intermediate conversion to a
predefined name before returning a result in another format, this
module always normalizes such values to be entirely lower-case.
Mappings of color names
-----------------------
For each set of defined color names -- HTML 4, CSS 2, CSS 2.1 and CSS
3 -- this module exports two mappings: one of normalized color names
to normalized hexadecimal values, and one of normalized hexadecimal
values to normalized color names. These eight mappings are as follows:
``html4_names_to_hex``
Mapping of normalized HTML 4 color names to normalized hexadecimal
values.
``html4_hex_to_names``
Mapping of normalized hexadecimal values to normalized HTML 4
color names.
``css2_names_to_hex``
Mapping of normalized CSS 2 color names to normalized hexadecimal
values. Because CSS 2 defines the same set of named colors as HTML
4, this is merely an alias for ``html4_names_to_hex``.
``css2_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS 2 color
nams. For the reasons described above, this is merely an alias for
``html4_hex_to_names``.
``css21_names_to_hex``
Mapping of normalized CSS 2.1 color names to normalized
hexadecimal values. This is identical to ``html4_names_to_hex``,
except for one addition: ``orange``.
``css21_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS 2.1
color names. As above, this is identical to ``html4_hex_to_names``
except for the addition of ``orange``.
``css3_names_to_hex``
Mapping of normalized CSS3 color names to normalized hexadecimal
values.
``css3_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS3 color
names.
Normalization functions
-----------------------
``normalize_hex(hex_value)``
Normalize a hexadecimal color value to the following form and
return the result::
#[a-f0-9]{6}
In other words, the following transformations are applied as
needed:
* If the value contains only three hexadecimal digits, it is
expanded to six.
* The value is normalized to lower-case.
If the supplied value cannot be interpreted as a hexadecimal color
value, ``ValueError`` is raised.
Example:
>>> normalize_hex('#09c')
'#0099cc'
Conversions from named colors
-----------------------------
``name_to_hex(name, spec='css3')``
Convert a color name to a normalized hexadecimal color value.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Example:
>>> name_to_hex('deepskyblue')
'#00bfff'
``name_to_rgb(name, spec='css3')``
Convert a color name to a 3-tuple of integers suitable for use in
an ``rgb()`` triplet specifying that color.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Example:
>>> name_to_rgb('navy')
(0, 0, 128)
``name_to_rgb_percent(name, spec='css3')``
Convert a color name to a 3-tuple of percentages suitable for use
in an ``rgb()`` triplet specifying that color.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Example:
>>> name_to_rgb_percent('navy')
('0%', '0%', '50%')
Conversions from hexadecimal values
-----------------------------------
``hex_to_name(hex_value, spec='css3')``
Convert a hexadecimal color value to its corresponding normalized
color name, if any such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The hexadecimal value will be normalized before being looked up,
and when no color name for the value is found in the given
specification, ``ValueError`` is raised.
Example:
>>> hex_to_name('#000080')
'navy'
``hex_to_rgb(hex_value)``
Convert a hexadecimal color value to a 3-tuple of integers
suitable for use in an ``rgb()`` triplet specifying that color.
The hexadecimal value will be normalized before being converted.
Example:
>>> hex_to_rgb('#000080')
(0, 0, 128)
``hex_to_rgb_percent(hex_value)``
Convert a hexadecimal color value to a 3-tuple of percentages
suitable for use in an ``rgb()`` triplet representing that color.
The hexadecimal value will be normalized before converting.
Example:
>>> hex_to_rgb_percent('#ffffff')
('100%', '100%', '100%')
Conversions from integer rgb() triplets
---------------------------------------
``rgb_to_name(rgb_triplet, spec='css3')``
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to its corresponding normalized color name, if any
such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
If there is no matching name, ``ValueError`` is raised.
Example:
>>> rgb_to_name((0, 0, 0))
'black'
``rgb_to_hex(rgb_triplet)``
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to a normalized hexadecimal value for that color.
Example:
>>> rgb_to_hex((255, 255, 255))
'#ffffff'
``rgb_to_rgb_percent(rgb_triplet)``
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to a 3-tuple of percentages suitable for use in
representing that color.
This function makes some trade-offs in terms of the accuracy of
the final representation; for some common integer values,
special-case logic is used to ensure a precise result (e.g.,
integer 128 will always convert to '50%', integer 32 will always
convert to '12.5%'), but for all other values a standard Python
``float`` is used and rounded to two decimal places, which may
result in a loss of precision for some values.
Examples:
>>> rgb_to_rgb_percent((255, 255, 255))
('100%', '100%', '100%')
>>> rgb_to_rgb_percent((0, 0, 128))
('0%', '0%', '50%')
>>> rgb_to_rgb_percent((33, 56, 192))
('12.94%', '21.96%', '75.29%')
>>> rgb_to_rgb_percent((64, 32, 16))
('25%', '12.5%', '6.25%')
Conversions from percentage rgb() triplets
------------------------------------------
``rgb_percent_to_name(rgb_percent_triplet, spec='css3')``
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to its corresponding normalized color name, if any
such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
If there is no matching name, ``ValueError`` is raised.
Example:
>>> rgb_percent_to_name(('0%', '0%', '50%'))
'navy'
``rgb_percent_to_hex(rgb_percent_triplet)``
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to a normalized hexadecimal color value for that
color.
Example:
>>> rgb_percent_to_hex(('100%', '100%', '0%'))
'#ffff00'
``rgb_percent_to_rgb(rgb_percent_triplet)``
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to a 3-tuple of integers suitable for use in
representing that color.
Some precision may be lost in this conversion. See the note
regarding precision for ``rgb_to_rgb_percent()`` for details;
generally speaking, the following is true for any 3-tuple ``t`` of
integers in the range 0...255 inclusive::
t == rgb_percent_to_rgb(rgb_to_rgb_percent(t))
Examples:
>>> rgb_percent_to_rgb(('100%', '100%', '100%'))
(255, 255, 255)
>>> rgb_percent_to_rgb(('0%', '0%', '50%'))
(0, 0, 128)
>>> rgb_percent_to_rgb(('25%', '12.5%', '6.25%'))
(64, 32, 16)
>>> rgb_percent_to_rgb(('12.94%', '21.96%', '75.29%'))
(33, 56, 192)

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,880 @@
# -*- coding: utf-8 -*-
"""
A simple library for working with the color names and color codes
defined by the HTML and CSS specifications.
An overview of HTML and CSS colors
----------------------------------
Colors on the Web are specified in `the sRGB color space`_, where each
color is made up of a red component, a green component and a blue
component. This is useful because it maps (fairly) cleanly to the red,
green and blue components of pixels on a computer display, and to the
cone cells of a human eye, which come in three sets roughly
corresponding to the wavelengths of light associated with red, green
and blue.
`The HTML 4 standard`_ defines two ways to specify sRGB colors:
* A hash mark ('#') followed by three pairs of hexdecimal digits,
specifying values for red, green and blue components in that order;
for example, ``#0099cc``. Since each pair of hexadecimal digits can
express 256 different values, this allows up to 256**3 or 16,777,216
unique colors to be specified (though, due to differences in display
technology, not all of these colors may be clearly distinguished on
any given physical display).
* A set of predefined color names which correspond to specific
hexadecimal values; for example, ``white``. HTML 4 defines sixteen
such colors.
`The CSS 2 standard`_ allows any valid HTML 4 color specification, and
adds three new ways to specify sRGB colors:
* A hash mark followed by three hexadecimal digits, which is expanded
into three hexadecimal pairs by repeating each digit; thus ``#09c``
is equivalent to ``#0099cc``.
* The string 'rgb', followed by parentheses, between which are three
numeric values each between 0 and 255, inclusive, which are taken to
be the values of the red, green and blue components in that order;
for example, ``rgb(0, 153, 204)``.
* The same as above, except using percentages instead of numeric
values; for example, ``rgb(0%, 60%, 80%)``.
`The CSS 2.1 revision`_ does not add any new methods of specifying
sRGB colors, but does add one additional named color.
`The CSS 3 color module`_ (currently a W3C Candidate Recommendation)
adds one new way to specify sRGB colors:
* A hue-saturation-lightness triple (HSL), using the construct
``hsl()``.
It also adds support for variable opacity of colors, by allowing the
specification of alpha-channel information, through the ``rgba()`` and
``hsla()`` constructs, which are identical to ``rgb()`` and ``hsl()``
with one exception: a fourth value is supplied, indicating the level
of opacity from ``0.0`` (completely transparent) to ``1.0``
(completely opaque). Though not technically a color, the keyword
``transparent`` is also made available in lieu of a color value, and
corresponds to ``rgba(0,0,0,0)``.
Additionally, CSS3 defines a new set of color names; this set is taken
directly from the named colors defined for SVG (Scalable Vector
Graphics) markup, and is a proper superset of the named colors defined
in CSS 2.1. This set also has significant overlap with traditional X11
color sets as defined by the ``rgb.txt`` file on many Unix and
Unix-like operating systems, though the correspondence is not exact;
the set of X11 colors is not standardized, and the set of CSS3 colors
contains some definitions which diverge significantly from customary
X11 definitions (for example, CSS3's ``green`` is not equivalent to
X11's ``green``; the value which X11 designates ``green`` is
designated ``lime`` in CSS3).
.. _the sRGB color space: http://www.w3.org/Graphics/Color/sRGB
.. _The HTML 4 standard: http://www.w3.org/TR/html401/types.html#h-6.5
.. _The CSS 2 standard: http://www.w3.org/TR/REC-CSS2/syndata.html#value-def-color
.. _The CSS 2.1 revision: http://www.w3.org/TR/CSS21/
.. _The CSS 3 color module: http://www.w3.org/TR/css3-color/
What this module supports
-------------------------
The mappings and functions within this module support the following
methods of specifying sRGB colors, and conversions between them:
* Six-digit hexadecimal.
* Three-digit hexadecimal.
* Integer ``rgb()`` triplet.
* Percentage ``rgb()`` triplet.
* Varying selections of predefined color names (see below).
This module does not support ``hsl()`` triplets, nor does it support
opacity/alpha-channel information via ``rgba()`` or ``hsla()``.
If you need to convert between RGB-specified colors and HSL-specified
colors, or colors specified via other means, consult `the colorsys
module`_ in the Python standard library, which can perform conversions
amongst several common color spaces.
.. _the colorsys module: http://docs.python.org/library/colorsys.html
Normalization
-------------
For colors specified via hexadecimal values, this module will accept
input in the following formats:
* A hash mark (#) followed by three hexadecimal digits, where letters
may be upper- or lower-case.
* A hash mark (#) followed by six hexadecimal digits, where letters
may be upper- or lower-case.
For output which consists of a color specified via hexadecimal values,
and for functions which perform intermediate conversion to hexadecimal
before returning a result in another format, this module always
normalizes such values to the following format:
* A hash mark (#) followed by six hexadecimal digits, with letters
forced to lower-case.
The function ``normalize_hex()`` in this module can be used to perform
this normalization manually if desired; see its documentation for an
explanation of the normalization process.
For colors specified via predefined names, this module will accept
input in the following formats:
* An entirely lower-case name, such as ``aliceblue``.
* A name using initial capitals, such as ``AliceBlue``.
For output which consists of a color specified via a predefined name,
and for functions which perform intermediate conversion to a
predefined name before returning a result in another format, this
module always normalizes such values to be entirely lower-case.
Mappings of color names
-----------------------
For each set of defined color names -- HTML 4, CSS 2, CSS 2.1 and CSS
3 -- this module exports two mappings: one of normalized color names
to normalized hexadecimal values, and one of normalized hexadecimal
values to normalized color names. These eight mappings are as follows:
``html4_names_to_hex``
Mapping of normalized HTML 4 color names to normalized hexadecimal
values.
``html4_hex_to_names``
Mapping of normalized hexadecimal values to normalized HTML 4
color names.
``css2_names_to_hex``
Mapping of normalized CSS 2 color names to normalized hexadecimal
values. Because CSS 2 defines the same set of named colors as HTML
4, this is merely an alias for ``html4_names_to_hex``.
``css2_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS 2 color
nams. For the reasons described above, this is merely an alias for
``html4_hex_to_names``.
``css21_names_to_hex``
Mapping of normalized CSS 2.1 color names to normalized
hexadecimal values. This is identical to ``html4_names_to_hex``,
except for one addition: ``orange``.
``css21_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS 2.1
color names. As above, this is identical to ``html4_hex_to_names``
except for the addition of ``orange``.
``css3_names_to_hex``
Mapping of normalized CSS3 color names to normalized hexadecimal
values.
``css3_hex_to_names``
Mapping of normalized hexadecimal values to normalized CSS3 color
names.
"""
import math
import re
from hashlib import md5
def _reversedict(d):
"""
Internal helper for generating reverse mappings; given a
dictionary, returns a new dictionary with keys and values swapped.
"""
return dict(zip(d.values(), d.keys()))
HEX_COLOR_RE = re.compile(r'^#([a-fA-F0-9]|[a-fA-F0-9]{3}|[a-fA-F0-9]{6})$')
SUPPORTED_SPECIFICATIONS = ('html4', 'css2', 'css21', 'css3')
######################################################################
# Mappings of color names to normalized hexadecimal color values.
######################################################################
html4_names_to_hex = {
'aqua': '#00ffff',
'black': '#000000',
'blue': '#0000ff',
'fuchsia': '#ff00ff',
'green': '#008000',
'grey': '#808080',
'lime': '#00ff00',
'maroon': '#800000',
'navy': '#000080',
'olive': '#808000',
'purple': '#800080',
'red': '#ff0000',
'silver': '#c0c0c0',
'teal': '#008080',
'white': '#ffffff',
'yellow': '#ffff00'
}
css2_names_to_hex = html4_names_to_hex
css21_names_to_hex = dict(html4_names_to_hex, orange='#ffa500')
css3_names_to_hex = {
'aliceblue': '#f0f8ff',
'antiquewhite': '#faebd7',
'aqua': '#00ffff',
'aquamarine': '#7fffd4',
'azure': '#f0ffff',
'beige': '#f5f5dc',
'bisque': '#ffe4c4',
'black': '#000000',
'blanchedalmond': '#ffebcd',
'blue': '#0000ff',
'blueviolet': '#8a2be2',
'brown': '#a52a2a',
'burlywood': '#deb887',
'cadetblue': '#5f9ea0',
'chartreuse': '#7fff00',
'chocolate': '#d2691e',
'coral': '#ff7f50',
'cornflowerblue': '#6495ed',
'cornsilk': '#fff8dc',
'crimson': '#dc143c',
'cyan': '#00ffff',
'darkblue': '#00008b',
'darkcyan': '#008b8b',
'darkgoldenrod': '#b8860b',
'darkgray': '#a9a9a9',
'darkgrey': '#a9a9a9',
'darkgreen': '#006400',
'darkkhaki': '#bdb76b',
'darkmagenta': '#8b008b',
'darkolivegreen': '#556b2f',
'darkorange': '#ff8c00',
'darkorchid': '#9932cc',
'darkred': '#8b0000',
'darksalmon': '#e9967a',
'darkseagreen': '#8fbc8f',
'darkslateblue': '#483d8b',
'darkslategray': '#2f4f4f',
'darkslategrey': '#2f4f4f',
'darkturquoise': '#00ced1',
'darkviolet': '#9400d3',
'deeppink': '#ff1493',
'deepskyblue': '#00bfff',
'dimgray': '#696969',
'dimgrey': '#696969',
'dodgerblue': '#1e90ff',
'firebrick': '#b22222',
'floralwhite': '#fffaf0',
'forestgreen': '#228b22',
'fuchsia': '#ff00ff',
'gainsboro': '#dcdcdc',
'ghostwhite': '#f8f8ff',
'gold': '#ffd700',
'goldenrod': '#daa520',
'gray': '#808080',
'grey': '#808080',
'green': '#008000',
'greenyellow': '#adff2f',
'honeydew': '#f0fff0',
'hotpink': '#ff69b4',
'indianred': '#cd5c5c',
'indigo': '#4b0082',
'ivory': '#fffff0',
'khaki': '#f0e68c',
'lavender': '#e6e6fa',
'lavenderblush': '#fff0f5',
'lawngreen': '#7cfc00',
'lemonchiffon': '#fffacd',
'lightblue': '#add8e6',
'lightcoral': '#f08080',
'lightcyan': '#e0ffff',
'lightgoldenrodyellow': '#fafad2',
'lightgray': '#d3d3d3',
'lightgrey': '#d3d3d3',
'lightgreen': '#90ee90',
'lightpink': '#ffb6c1',
'lightsalmon': '#ffa07a',
'lightseagreen': '#20b2aa',
'lightskyblue': '#87cefa',
'lightslategray': '#778899',
'lightslategrey': '#778899',
'lightsteelblue': '#b0c4de',
'lightyellow': '#ffffe0',
'lime': '#00ff00',
'limegreen': '#32cd32',
'linen': '#faf0e6',
'magenta': '#ff00ff',
'maroon': '#800000',
'mediumaquamarine': '#66cdaa',
'mediumblue': '#0000cd',
'mediumorchid': '#ba55d3',
'mediumpurple': '#9370d8',
'mediumseagreen': '#3cb371',
'mediumslateblue': '#7b68ee',
'mediumspringgreen': '#00fa9a',
'mediumturquoise': '#48d1cc',
'mediumvioletred': '#c71585',
'midnightblue': '#191970',
'mintcream': '#f5fffa',
'mistyrose': '#ffe4e1',
'moccasin': '#ffe4b5',
'navajowhite': '#ffdead',
'navy': '#000080',
'oldlace': '#fdf5e6',
'olive': '#808000',
'olivedrab': '#6b8e23',
'orange': '#ffa500',
'orangered': '#ff4500',
'orchid': '#da70d6',
'palegoldenrod': '#eee8aa',
'palegreen': '#98fb98',
'paleturquoise': '#afeeee',
'palevioletred': '#d87093',
'papayawhip': '#ffefd5',
'peachpuff': '#ffdab9',
'peru': '#cd853f',
'pink': '#ffc0cb',
'plum': '#dda0dd',
'powderblue': '#b0e0e6',
'purple': '#800080',
'red': '#ff0000',
'rosybrown': '#bc8f8f',
'royalblue': '#4169e1',
'saddlebrown': '#8b4513',
'salmon': '#fa8072',
'sandybrown': '#f4a460',
'seagreen': '#2e8b57',
'seashell': '#fff5ee',
'sienna': '#a0522d',
'silver': '#c0c0c0',
'skyblue': '#87ceeb',
'slateblue': '#6a5acd',
'slategray': '#708090',
'slategrey': '#708090',
'snow': '#fffafa',
'springgreen': '#00ff7f',
'steelblue': '#4682b4',
'tan': '#d2b48c',
'teal': '#008080',
'thistle': '#d8bfd8',
'tomato': '#ff6347',
'turquoise': '#40e0d0',
'violet': '#ee82ee',
'wheat': '#f5deb3',
'white': '#ffffff',
'whitesmoke': '#f5f5f5',
'yellow': '#ffff00',
'yellowgreen': '#9acd32',
}
######################################################################
# Mappings of normalized hexadecimal color values to color names.
######################################################################
html4_hex_to_names = _reversedict(html4_names_to_hex)
css2_hex_to_names = html4_hex_to_names
css21_hex_to_names = _reversedict(css21_names_to_hex)
css3_hex_to_names = _reversedict(css3_names_to_hex)
######################################################################
# Normalization routines.
######################################################################
def normalize_hex(hex_value):
"""
Normalize a hexadecimal color value to the following form and
return the result::
#[a-f0-9]{6}
In other words, the following transformations are applied as
needed:
* If the value contains only three hexadecimal digits, it is
expanded to six.
* The value is normalized to lower-case.
If the supplied value cannot be interpreted as a hexadecimal color
value, ``ValueError`` is raised.
Examples:
>>> normalize_hex('#09c')
'#0099cc'
>>> normalize_hex('#0099cc')
'#0099cc'
>>> normalize_hex('#09C')
'#0099cc'
>>> normalize_hex('#0099CC')
'#0099cc'
>>> normalize_hex('0099cc')
Traceback (most recent call last):
...
ValueError: '0099cc' is not a valid hexadecimal color value.
>>> normalize_hex('#0099QX')
Traceback (most recent call last):
...
ValueError: '#0099QX' is not a valid hexadecimal color value.
>>> normalize_hex('foobarbaz')
Traceback (most recent call last):
...
ValueError: 'foobarbaz' is not a valid hexadecimal color value.
>>> normalize_hex('#0')
Traceback (most recent call last):
...
ValueError: '#0' is not a valid hexadecimal color value.
"""
try:
hex_digits = HEX_COLOR_RE.match(hex_value).groups()[0]
except AttributeError:
raise ValueError("'%s' is not a valid hexadecimal color value." % hex_value)
if len(hex_digits) == 3:
hex_digits = ''.join(map(lambda s: 2 * s, hex_digits))
elif len(hex_digits) == 1:
hex_digits = hex_digits * 6
return '#%s' % hex_digits.lower()
######################################################################
# Conversions from color names to various formats.
######################################################################
def name_to_hex(name, spec='css3'):
"""
Convert a color name to a normalized hexadecimal color value.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Examples:
>>> name_to_hex('deepskyblue')
'#00bfff'
>>> name_to_hex('DeepSkyBlue')
'#00bfff'
>>> name_to_hex('white', spec='html4')
'#ffffff'
>>> name_to_hex('white', spec='css2')
'#ffffff'
>>> name_to_hex('white', spec='css21')
'#ffffff'
>>> name_to_hex('white', spec='css3')
'#ffffff'
>>> name_to_hex('white', spec='css4')
Traceback (most recent call last):
...
TypeError: 'css4' is not a supported specification for color name lookups; supported specifications are: html4, css2, css21, css3.
>>> name_to_hex('deepskyblue', spec='css2')
Traceback (most recent call last):
...
ValueError: 'deepskyblue' is not defined as a named color in css2.
"""
if spec not in SUPPORTED_SPECIFICATIONS:
raise TypeError("'%s' is not a supported specification for color name lookups; supported specifications are: %s." % (spec,
', '.join(SUPPORTED_SPECIFICATIONS)))
normalized = name.lower()
try:
hex_value = globals()['%s_names_to_hex' % spec][normalized]
except KeyError:
raise ValueError("'%s' is not defined as a named color in %s." % (name, spec))
return hex_value
def name_to_rgb(name, spec='css3'):
"""
Convert a color name to a 3-tuple of integers suitable for use in
an ``rgb()`` triplet specifying that color.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Examples:
>>> name_to_rgb('navy')
(0, 0, 128)
>>> name_to_rgb('cadetblue')
(95, 158, 160)
>>> name_to_rgb('cadetblue', spec='html4')
Traceback (most recent call last):
...
ValueError: 'cadetblue' is not defined as a named color in html4.
"""
return hex_to_rgb(name_to_hex(name, spec=spec))
def name_to_rgb_percent(name, spec='css3'):
"""
Convert a color name to a 3-tuple of percentages suitable for use
in an ``rgb()`` triplet specifying that color.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The color name will be normalized to lower-case before being
looked up, and when no color of that name exists in the given
specification, ``ValueError`` is raised.
Examples:
>>> name_to_rgb_percent('white')
('100%', '100%', '100%')
>>> name_to_rgb_percent('navy')
('0%', '0%', '50%')
>>> name_to_rgb_percent('goldenrod')
('85.49%', '64.71%', '12.5%')
"""
return rgb_to_rgb_percent(name_to_rgb(name, spec=spec))
######################################################################
# Conversions from hexadecimal color values to various formats.
######################################################################
def hex_to_name(hex_value, spec='css3'):
"""
Convert a hexadecimal color value to its corresponding normalized
color name, if any such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
The hexadecimal value will be normalized before being looked up,
and when no color name for the value is found in the given
specification, ``ValueError`` is raised.
Examples:
>>> hex_to_name('#000080')
'navy'
>>> hex_to_name('#000080', spec='html4')
'navy'
>>> hex_to_name('#000080', spec='css2')
'navy'
>>> hex_to_name('#000080', spec='css21')
'navy'
>>> hex_to_name('#8b4513')
'saddlebrown'
>>> hex_to_name('#8b4513', spec='html4')
Traceback (most recent call last):
...
ValueError: '#8b4513' has no defined color name in html4.
>>> hex_to_name('#8b4513', spec='css4')
Traceback (most recent call last):
...
TypeError: 'css4' is not a supported specification for color name lookups; supported specifications are: html4, css2, css21, css3.
"""
if spec not in SUPPORTED_SPECIFICATIONS:
raise TypeError("'%s' is not a supported specification for color name lookups; supported specifications are: %s." % (spec,
', '.join(SUPPORTED_SPECIFICATIONS)))
normalized = normalize_hex(hex_value)
try:
name = globals()['%s_hex_to_names' % spec][normalized]
except KeyError:
raise ValueError("'%s' has no defined color name in %s." % (hex_value, spec))
return name
def any_hex_to_name(hex_value):
try:
return hex_to_name(hex_value)
except ValueError:
return hex_value
def hex_to_rgb(hex_value):
"""
Convert a hexadecimal color value to a 3-tuple of integers
suitable for use in an ``rgb()`` triplet specifying that color.
The hexadecimal value will be normalized before being converted.
Examples:
>>> hex_to_rgb('#000080')
(0, 0, 128)
>>> hex_to_rgb('#ffff00')
(255, 255, 0)
>>> hex_to_rgb('#f00')
(255, 0, 0)
>>> hex_to_rgb('#deb887')
(222, 184, 135)
"""
hex_digits = normalize_hex(hex_value)
return tuple(map(lambda s: int(s, 16),
(hex_digits[1:3], hex_digits[3:5], hex_digits[5:7])))
def hex_to_rgb_percent(hex_value):
"""
Convert a hexadecimal color value to a 3-tuple of percentages
suitable for use in an ``rgb()`` triplet representing that color.
The hexadecimal value will be normalized before converting.
Examples:
>>> hex_to_rgb_percent('#ffffff')
('100%', '100%', '100%')
>>> hex_to_rgb_percent('#000080')
('0%', '0%', '50%')
"""
return rgb_to_rgb_percent(hex_to_rgb(hex_value))
######################################################################
# Conversions from integer rgb() triplets to various formats.
######################################################################
def rgb_to_name(rgb_triplet, spec='css3'):
"""
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to its corresponding normalized color name, if any
such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
If there is no matching name, ``ValueError`` is raised.
Examples:
>>> rgb_to_name((0, 0, 0))
'black'
>>> rgb_to_name((0, 0, 128))
'navy'
>>> rgb_to_name((95, 158, 160))
'cadetblue'
"""
return hex_to_name(rgb_to_hex(rgb_triplet), spec=spec)
def rgb_to_hex(rgb_triplet):
"""
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to a normalized hexadecimal value for that color.
Examples:
>>> rgb_to_hex((255, 255, 255))
'#ffffff'
>>> rgb_to_hex((0, 0, 128))
'#000080'
>>> rgb_to_hex((33, 56, 192))
'#2138c0'
"""
return '#%02x%02x%02x' % rgb_triplet
def rgb_to_rgb_percent(rgb_triplet):
"""
Convert a 3-tuple of integers, suitable for use in an ``rgb()``
color triplet, to a 3-tuple of percentages suitable for use in
representing that color.
This function makes some trade-offs in terms of the accuracy of
the final representation; for some common integer values,
special-case logic is used to ensure a precise result (e.g.,
integer 128 will always convert to '50%', integer 32 will always
convert to '12.5%'), but for all other values a standard Python
``float`` is used and rounded to two decimal places, which may
result in a loss of precision for some values.
Examples:
>>> rgb_to_rgb_percent((255, 255, 255))
('100%', '100%', '100%')
>>> rgb_to_rgb_percent((0, 0, 128))
('0%', '0%', '50%')
>>> rgb_to_rgb_percent((33, 56, 192))
('12.94%', '21.96%', '75.29%')
>>> rgb_to_rgb_percent((64, 32, 16))
('25%', '12.5%', '6.25%')
"""
# In order to maintain precision for common values,
# 256 / 2**n is special-cased for values of n
# from 0 through 4, as well as 0 itself.
specials = {255: '100%', 128: '50%', 64: '25%',
32: '12.5%', 16: '6.25%', 0: '0%'}
return tuple(map(lambda d: specials.get(d, '%.02f%%' % ((d / 255.0) * 100)),
rgb_triplet))
######################################################################
# Conversions from percentage rgb() triplets to various formats.
######################################################################
def rgb_percent_to_name(rgb_percent_triplet, spec='css3'):
"""
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to its corresponding normalized color name, if any
such name exists.
The optional keyword argument ``spec`` determines which
specification's list of color names will be used; valid values are
``html4``, ``css2``, ``css21`` and ``css3``, and the default is
``css3``.
If there is no matching name, ``ValueError`` is raised.
Examples:
>>> rgb_percent_to_name(('0%', '0%', '0%'))
'black'
>>> rgb_percent_to_name(('0%', '0%', '50%'))
'navy'
>>> rgb_percent_to_name(('85.49%', '64.71%', '12.5%'))
'goldenrod'
"""
return rgb_to_name(rgb_percent_to_rgb(rgb_percent_triplet), spec=spec)
def rgb_percent_to_hex(rgb_percent_triplet):
"""
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to a normalized hexadecimal color value for that
color.
Examples:
>>> rgb_percent_to_hex(('100%', '100%', '0%'))
'#ffff00'
>>> rgb_percent_to_hex(('0%', '0%', '50%'))
'#000080'
>>> rgb_percent_to_hex(('85.49%', '64.71%', '12.5%'))
'#daa520'
"""
return rgb_to_hex(rgb_percent_to_rgb(rgb_percent_triplet))
def _percent_to_integer(percent):
"""
Internal helper for converting a percentage value to an integer
between 0 and 255 inclusive.
"""
num = float(percent.split('%')[0]) / 100.0 * 255
e = num - math.floor(num)
return e < 0.5 and int(math.floor(num)) or int(math.ceil(num))
def rgb_percent_to_rgb(rgb_percent_triplet):
"""
Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
color triplet, to a 3-tuple of integers suitable for use in
representing that color.
Some precision may be lost in this conversion. See the note
regarding precision for ``rgb_to_rgb_percent()`` for details;
generally speaking, the following is true for any 3-tuple ``t`` of
integers in the range 0...255 inclusive::
t == rgb_percent_to_rgb(rgb_to_rgb_percent(t))
Examples:
>>> rgb_percent_to_rgb(('100%', '100%', '100%'))
(255, 255, 255)
>>> rgb_percent_to_rgb(('0%', '0%', '50%'))
(0, 0, 128)
>>> rgb_percent_to_rgb(('25%', '12.5%', '6.25%'))
(64, 32, 16)
>>> rgb_percent_to_rgb(('12.94%', '21.96%', '75.29%'))
(33, 56, 192)
"""
return tuple(map(_percent_to_integer, rgb_percent_triplet))
def whatever_to_rgb(string):
"""
Converts CSS3 color or a hex into rgb triplet; hash of string if fails.
"""
string = string.strip().lower()
try:
return name_to_rgb(string)
except ValueError:
try:
return hex_to_rgb(string)
except ValueError:
try:
if string[:3] == "rgb":
return tuple([float(i) for i in string[4:-1].split(",")][0:3])
except:
return hex_to_rgb("#" + md5(string).hexdigest()[:6])
def whatever_to_hex(string):
if type(string) == tuple:
return cairo_to_hex(string).upper()
return rgb_to_hex(whatever_to_rgb(string)).upper()
def whatever_to_cairo(string):
a = whatever_to_rgb(string)
return a[0] / 255., a[1] / 255., a[2] / 255.
def cairo_to_hex(cairo):
return rgb_to_hex((cairo[0] * 255., cairo[1] * 255., cairo[2] * 255.))
if __name__ == '__main__':
import doctest
doctest.testmod()

42
tools/kothic/timer.py Normal file
View file

@ -0,0 +1,42 @@
from timeit import default_timer
class Timer(object):
def __init__(self):
self.timer = default_timer
self.start = self.timer()
def Reset(self):
self.start = self.timer()
def ElapsedMsec(self):
elapsed_secs = self.timer() - self.start
return elapsed_secs * 1000
def ElapsedSec(self):
elapsed_secs = self.timer() - self.start
return elapsed_secs
class AccumulativeTimer(object):
def __init__(self):
self.timer = default_timer
self.elapsed_secs = 0
self.start = 0
self.count = 0
def Start(self):
self.start = self.timer()
def Stop(self):
self.elapsed_secs += self.timer() - self.start
self.start = 0
self.count += 1
def ElapsedMsec(self):
elapsed_msec = self.elapsed_secs * 1000
return elapsed_msec
def ElapsedSec(self):
return self.elapsed_secs
def Count(self):
return self.count

5
tools/unix/generate_drules.sh Executable file
View file

@ -0,0 +1,5 @@
echo "Build drawing rules light"
python ../kothic/libkomwm.py -s ../../data/styles/normal_light.mapcss -o ../../data/drules_proto
echo "Build drawing rules dark"
python ../kothic/libkomwm.py -s ../../data/styles/normal_dark.mapcss -o ../../data/drules_proto_dark