--- /dev/null
+LIBUCW:=$(shell cd /home/mj/src/libucw/run && pwd)
+UCWCF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --cflags libucw libucw-charset libucw-xml)
+UCWLF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --libs libucw libucw-charset libucw-xml)
+FTCF:=$(shell freetype-config --cflags)
+FTLF:=$(shell freetype-config --libs)
+PANGOCF:=$(shell pkg-config pangoft2 --cflags)
+PANGOLF:=$(shell pkg-config pangoft2 --libs)
+
+CC=gcc
+LD=gcc
+COPT=-O2
+CFLAGS=$(COPT) -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -Wno-missing-field-initializers -std=gnu99 $(UCWCF) -ggdb -DDEBUG_ASSERTS
+LDLIBS+=$(FTLF) $(PANGOLF) $(UCWLF) -lproj
+
+all: leo
+
+leo: leo.o xml.o osm.o svg.o svg-icon.o css-parse.o css-lex.o style.o css.o dict.o sym.o sym-point.o sym-line.o sym-text.o map.o shp.o
+
+INC=leo.h dict-keys.h dict-values.h dict-props.h osm.h svg.h style.h css.h dict.h map.h sym.h
+
+leo.o: leo.c $(INC)
+xml.o: xml.c $(INC)
+osm.o: osm.c $(INC)
+svg.o: svg.c $(INC)
+svg-icon.o: svg-icon.c $(INC)
+style.o: style.c $(INC)
+css.o: css.c $(INC)
+dict.o: dict.c $(INC)
+sym.o: sym.c $(INC)
+sym-point.o: sym-point.c $(INC)
+sym-line.o: sym-line.c $(INC)
+sym-text.o: sym-text.c $(INC)
+css-parse.o: css-parse.c $(INC)
+css-lex.o: css-lex.c $(INC) css-parse.c
+map.o: map.c $(INC)
+shp.o: shp.c $(INC)
+
+sym-text.o: CFLAGS+=$(FTCF) $(PANGOCF)
+
+css-parse.c: css-parse.y
+ bison --name-prefix=css_ --token-table --verbose --defines -o $@ $^
+
+dict-%.h: dict-%.t gen-dict
+ ./gen-dict <$< >$@
+
+clean:
+ rm -f leo *.o css-parse.c css-parse.h css-parse.output tags
+ rm -f dict-keys.h dict-values.h dict-props.h
+
+tags:
+ ctags *.[chy]
+
+upload:
+ rs output.pdf ps:poskole-beta/www/tmp/2014/mapa.pdf
+
+backup:
+ rs . camelot:a/priv/poskole/map/new/$$(date '+%Y%m%d-%H%M%S')/
+
+output.svg: leo dump.osm poskole.css
+ ./leo
+
+output.pdf: output.svg
+ inkscape --export-pdf=output.pdf output.svg
--- /dev/null
+- bring CSS lexer to the CSS2.1 spec (http://www.w3.org/TR/CSS2/syndata.html)
+- CSS override rules are broken. For example, text-offset-y and text-offset
+ should override each other, but currently they are mixed upon rendering.
+ Does it matter?
+- Fixing of label positions should include some margin
+- _BRUM_XXX_H -> _LEO_XXX_H
+
+- Interesting tags:
+amenity = kindergarten
+bridge = yes
+historic = archaeological_site
+place = islet
+railway = crossing
+railway = level_crossing
+tunnel = culvert
+tunnel = yes
+
+=== Opravit v OSM ===
+
+* RozliĆĄovat kostely a kaple
--- /dev/null
+/*
+ * Experimenta lMai Renderer -- MapCSS Lexer
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/chartype.h>
+#include <ucw/fastbuf.h>
+#include <ucw/mempool.h>
+
+#include <fcntl.h>
+
+#include "leo.h"
+#include "style.h"
+#include "css.h"
+#include "css-parse.h"
+
+static struct fastbuf *fb;
+static int lino;
+
+void css_error(char *err, ...)
+{
+ va_list args;
+ va_start(args, err);
+ char *msg = mp_vprintf(css_this->pool, err, args);
+ die("Error in %s, line %d: %s", css_this->filename, lino, msg);
+}
+
+void css_lex_open(void)
+{
+ fb = bopen_file(css_this->filename, O_RDONLY, NULL);
+ lino = 1;
+}
+
+void css_lex_close(void)
+{
+ bclose(fb);
+}
+
+int css_lex(void)
+{
+ struct mempool *mp = css_this->pool;
+ char *p;
+ int c, next, len;
+
+ for (;;)
+ {
+ c = bgetc(fb);
+ if (c < 0)
+ return 0;
+ if (c == '\n')
+ {
+ lino++;
+ continue;
+ }
+ if (c == ' ' || c == '\t')
+ continue;
+
+ next = bpeekc(fb);
+
+ if (c >= '0' && c <= '9' || c == '-' && next >= '0' && next <= '9')
+ {
+ // Number
+ p = mp_start(mp, 1);
+ if (c == '-')
+ {
+ p = mp_append_char(mp, p, c);
+ c = bgetc(fb);
+ }
+ while (c >= '0' && c <= '9')
+ {
+ p = mp_append_char(mp, p, c);
+ c = bgetc(fb);
+ }
+ if (c == '.')
+ {
+ p = mp_append_char(mp, p, c);
+ c = bgetc(fb);
+ while (c >= '0' && c <= '9')
+ {
+ p = mp_append_char(mp, p, c);
+ c = bgetc(fb);
+ }
+ }
+ p = mp_end_string(mp, p);
+ if (c >= 0)
+ bungetc(fb);
+ css_lval.s = p;
+ return NUMBER;
+ }
+
+ if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_')
+ {
+ // Alphabetical identifier
+ // FIXME: Identifiers starting with "-" are not supported
+ // FIXME: Unquoted identifiers should be always case-insensitive
+ p = mp_start(mp, 1);
+ while (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_')
+ {
+ p = mp_append_char(mp, p, c);
+ c = bgetc(fb);
+ }
+ p = mp_end_string(mp, p);
+ if (c >= 0)
+ bungetc(fb);
+ css_lval.s = p;
+ return IDENT;
+ }
+
+ switch (c)
+ {
+ case '"':
+ // Quoted string
+ p = mp_start(mp, 1);
+ c = bgetc(fb);
+ while (c != '"')
+ {
+ if (c < 0)
+ css_error("Unterminated string literal");
+ if (c == '\\')
+ {
+ c = bgetc(fb);
+ if (c == '\\' || c == '"')
+ ;
+ else if (c == 'n')
+ c = '\n';
+ else
+ css_error("Unknown backslash sequence \"\\%c\" in string literal", c);
+ }
+ p = mp_append_char(mp, p, c);
+ c = bgetc(fb);
+ }
+ css_lval.s = mp_end_string(mp, p);
+ return QUOTED;
+
+ case '/':
+ if (next == '*')
+ {
+ // C comment
+ bgetc(fb);
+ c = bgetc(fb);
+ for (;;)
+ {
+ if (c < 0)
+ css_error("Unterminated comment");
+ if (c == '\n')
+ {
+ lino++;
+ c = bgetc(fb);
+ }
+ else if (c == '*')
+ {
+ c = bgetc(fb);
+ if (c == '/')
+ break;
+ }
+ else
+ c = bgetc(fb);
+ }
+ continue;
+ }
+ else if (next == '/')
+ {
+ // C++ comment
+ do
+ c = bgetc(fb);
+ while (c >= 0 && c != '\n');
+ lino++;
+ continue;
+ }
+ return '/';
+
+ case '#':
+ p = mp_start(mp, 1);
+ while (next >= '0' && next <= '9' || next >= 'a' && next <= 'f' || next >= 'A' && next <= 'F')
+ {
+ p = mp_append_char(mp, p, bgetc(fb));
+ next = bpeekc(fb);
+ }
+ p = mp_end_string(mp, p);
+ len = strlen(p);
+ if (len != 3 && len != 6)
+ css_error("Invalid RGB literal");
+ css_lval.s = p;
+ return RGB;
+
+ // One-character operators
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case ';':
+ case ',':
+ case '*':
+ case '.':
+ case '=':
+ case '?':
+ return c;
+
+ // Two-character operators
+ case '!':
+ if (next == '=')
+ {
+ bgetc(fb);
+ return NE;
+ }
+ return '!';
+ case '<':
+ if (next == '=')
+ {
+ bgetc(fb);
+ return LE;
+ }
+ return '<';
+ case '>':
+ if (next == '=')
+ {
+ bgetc(fb);
+ return GE;
+ }
+ return '>';
+ case ':':
+ if (next == ':')
+ {
+ bgetc(fb);
+ return CC;
+ }
+ return ':';
+
+ default:
+ css_error("Invalid character \"%c\"", c);
+ }
+ }
+}
+
+color_t css_rgb_to_color(const char *rgb)
+{
+ uns n = strlen(rgb);
+ ASSERT(n == 3 || n == 6);
+ color_t color = 0;
+ for (uns i=0; i<n; i++)
+ {
+ uns j = Cxvalue(rgb[i]);
+ if (n == 6)
+ color = (color << 4) | j;
+ else
+ color = (color << 8) | (j * 17);
+ }
+ return color;
+}
--- /dev/null
+/*
+ * Hic Est Leo -- MapCSS Parser
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+%{
+#include <ucw/lib.h>
+#include <ucw/mempool.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "leo.h"
+#include "css.h"
+
+static void *css_alloc(size_t n)
+{
+ return mp_alloc_zero(css_this->pool, n);
+}
+
+static struct css_path *css_new_path(void)
+{
+ struct css_path *p = css_alloc(sizeof(struct css_path));
+ clist_init(&p->conditions);
+ p->layer = STYLE_LAYER_DEFAULT;
+ return p;
+}
+
+static void css_add_to_val_list(struct style_prop *list, struct style_prop *elt)
+{
+ struct style_val_list_entry *e = css_alloc(sizeof(*e));
+ clist_add_tail(list->val.list, &e->n);
+ e->val = *elt;
+}
+
+%}
+
+%union {
+ char *s;
+ struct css_rule *rule;
+ struct css_selector *selector;
+ struct css_path *path;
+ struct css_condition *condition;
+ struct css_action *action;
+ struct style_prop prop;
+ enum css_cond_op cond_op;
+}
+
+%token LE GE NE CC
+%token <s> NUMBER IDENT QUOTED RGB
+
+%type <s> ident_or_quoted
+%type <rule> rule rule_start rule_selectors rule_start_actions rule_actions
+%type <selector> selector selector_start
+%type <path> path path_start path_conditions
+%type <condition> condition
+%type <action> action
+%type <prop> prop_value prop_value_single prop_value_list
+%type <cond_op> binary_op
+
+%%
+
+input:
+ /* empty */
+ | input rule { clist_add_tail(&css_this->rules, &$2->n); }
+ ;
+
+rule_start:
+ /* empty */
+ {
+ $$ = css_alloc(sizeof(struct css_rule));
+ clist_init(&$$->selectors);
+ clist_init(&$$->actions);
+ }
+ ;
+
+rule_selectors:
+ rule_start selector
+ {
+ clist_add_tail(&$1->selectors, &$2->n);
+ $$ = $1;
+ }
+ | rule_selectors ',' selector
+ {
+ clist_add_tail(&$1->selectors, &$3->n);
+ $$ = $1;
+ }
+ ;
+
+rule_start_actions:
+ rule_start
+ | rule_selectors
+ ;
+
+rule_actions:
+ rule_start_actions '{'
+ {
+ $$ = $1;
+ }
+ | rule_actions action ';'
+ {
+ $$ = $1;
+ clist_add_tail(&$$->actions, &$2->n);
+ }
+ ;
+
+rule: rule_actions '}' ;
+
+selector_start:
+ /* empty */
+ {
+ $$ = css_alloc(sizeof(struct css_selector));
+ clist_init(&$$->path);
+ }
+ ;
+
+selector:
+ selector_start path
+ {
+ clist_add_tail(&$1->path, &$2->n);
+ $$ = $1;
+ }
+ | selector path
+ {
+ // FIXME: Descendant operator
+ clist_add_tail(&$1->path, &$2->n);
+ $$ = $1;
+ }
+ ;
+
+path_start:
+ IDENT
+ {
+ $$ = css_new_path();
+ if (!strcmp($1, "node"))
+ $$->type = CSS_TYPE_NODE;
+ else if (!strcmp($1, "way"))
+ $$->type = CSS_TYPE_WAY;
+ else if (!strcmp($1, "relation"))
+ $$->type = CSS_TYPE_RELATION;
+ else if (!strcmp($1, "area"))
+ $$->type = CSS_TYPE_AREA;
+ else if (!strcmp($1, "meta"))
+ $$->type = CSS_TYPE_META;
+ else
+ die("Invalid selector %s", $1);
+ }
+ | '*'
+ {
+ $$ = css_new_path();
+ $$->type = CSS_TYPE_ANY;
+ }
+ ;
+
+path_conditions:
+ path_start
+ | path_conditions '[' condition ']'
+ {
+ $$ = $1;
+ clist_add_tail(&$$->conditions, &$3->n);
+ }
+ ;
+
+path:
+ path_conditions
+ | path ':' IDENT
+ {
+ // So far, no modifiers are supported, so a rule with modifiers never matches
+ $$ = $1;
+ $$->modifiers |= PATH_MOD_NEVER;
+ }
+ | path CC IDENT
+ {
+ // Layer
+ $$ = $1;
+ $$->layer = style_layer_encode($3);
+ }
+ | path CC '*'
+ {
+ // All layers
+ $$ = $1;
+ $$->layer = STYLE_LAYER_ALL;
+ }
+ ;
+
+condition:
+ ident_or_quoted binary_op ident_or_quoted
+ {
+ $$ = css_alloc(sizeof(struct css_condition));
+ $$->op = $2;
+ $$->key = osm_key_encode($1);
+ $$->val = osm_val_encode($3);
+ }
+ | ident_or_quoted
+ {
+ $$ = css_alloc(sizeof(struct css_condition));
+ $$->op = COND_OP_IS_SET;
+ $$->key = osm_key_encode($1);
+ }
+ | '!' ident_or_quoted
+ {
+ $$ = css_alloc(sizeof(struct css_condition));
+ $$->op = COND_OP_IS_UNSET;
+ $$->key = osm_key_encode($2);
+ }
+ | ident_or_quoted '?'
+ {
+ $$ = css_alloc(sizeof(struct css_condition));
+ $$->op = COND_OP_IS_TRUE;
+ $$->key = osm_key_encode($1);
+ }
+ | ident_or_quoted '?' '!'
+ {
+ $$ = css_alloc(sizeof(struct css_condition));
+ $$->op = COND_OP_IS_FALSE;
+ $$->key = osm_key_encode($1);
+ }
+ ;
+
+binary_op:
+ '=' { $$ = COND_OP_EQ; }
+ | '<' { $$ = COND_OP_LT; }
+ | '>' { $$ = COND_OP_GT; }
+ | LE { $$ = COND_OP_LE; }
+ | GE { $$ = COND_OP_GE; }
+ | NE { $$ = COND_OP_NE; }
+ ;
+
+action:
+ ident_or_quoted ':' prop_value
+ {
+ $$ = css_alloc(sizeof(struct css_action));
+ $$->prop = $3;
+ $$->prop.key = style_prop_encode($1);
+ }
+ ;
+
+prop_value:
+ prop_value_single
+ | prop_value_list
+ ;
+
+prop_value_single:
+ IDENT
+ {
+ $$.type = PROP_TYPE_IDENT;
+ $$.val.id = osm_val_encode($1);
+ }
+ | QUOTED
+ {
+ $$.type = PROP_TYPE_STRING;
+ $$.val.id = osm_val_encode($1);
+ }
+ | NUMBER
+ {
+ $$.type = PROP_TYPE_NUMBER;
+ $$.val.number = atof($1);
+ }
+ | RGB
+ {
+ $$.type = PROP_TYPE_COLOR;
+ $$.val.color = css_rgb_to_color($1);
+ }
+ ;
+
+prop_value_list:
+ prop_value_single ',' prop_value_single
+ {
+ $$.type = PROP_TYPE_LIST;
+ $$.val.list = css_alloc(sizeof(clist));
+ clist_init($$.val.list);
+ css_add_to_val_list(&$$, &$1);
+ css_add_to_val_list(&$$, &$3);
+ }
+ | prop_value_list ',' prop_value_single
+ {
+ $$ = $1;
+ css_add_to_val_list(&$$, &$3);
+ }
+ ;
+
+ident_or_quoted: IDENT | QUOTED ;
+
+%%
+
+struct css_sheet *css_this;
+
+struct css_sheet *css_load(char *filename)
+{
+ struct mempool *mp = mp_new(4096);
+ struct css_sheet *ss = mp_alloc_zero(mp, sizeof(*ss));
+ ss->pool = mp;
+ clist_init(&ss->rules);
+ ss->filename = mp_strdup(mp, filename);
+
+ css_this = ss;
+ css_lex_open();
+ css_parse();
+
+ css_lex_close();
+ css_this = NULL;
+ return ss;
+}
--- /dev/null
+/*
+ * Hic Est Leo -- MapCSS Stylesheets
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/mempool.h>
+
+#include <stdio.h>
+
+#include "leo.h"
+#include "style.h"
+#include "css.h"
+
+void css_dump(struct css_sheet *ss)
+{
+ printf("Style sheet <%s>:\n", ss->filename);
+ CLIST_FOR_EACH(struct css_rule *, r, ss->rules)
+ css_dump_rule(r);
+}
+
+void css_dump_rule(struct css_rule *r)
+{
+ printf("Rule:\n");
+ CLIST_FOR_EACH(struct css_selector *, s, r->selectors)
+ css_dump_selector(s);
+ CLIST_FOR_EACH(struct css_action *, a, r->actions)
+ css_dump_action(a);
+}
+
+void css_dump_selector(struct css_selector *s)
+{
+ printf("\tSelector:\n");
+ CLIST_FOR_EACH(struct css_path *, p, s->path)
+ {
+ printf("\t\tMatch type=%u layer=%u role=%u mod=%u\n", p->type, p->layer, p->role, p->modifiers);
+ CLIST_FOR_EACH(struct css_condition *, c, p->conditions)
+ {
+ printf("\t\t\tCondition %u key=%s val=%s\n", c->op, osm_key_decode(c->key), osm_val_decode(c->val));
+ }
+ }
+}
+
+void css_dump_action(struct css_action *a)
+{
+ printf("\tAction: ");
+ style_dump_prop(&a->prop);
+}
+
+static bool css_match_condition(struct css_condition *c, struct osm_object *o)
+{
+ osm_val_t val = osm_obj_find_tag(o, c->key);
+
+ switch (c->op)
+ {
+ case COND_OP_EQ:
+ return (val == c->val); // FIXME: Case sensitivity?
+ case COND_OP_NE:
+ return (val != c->val);
+ case COND_OP_LT:
+ // FIXME
+ return 0;
+ case COND_OP_GT:
+ // FIXME
+ return 0;
+ case COND_OP_LE:
+ // FIXME
+ return 0;
+ case COND_OP_GE:
+ // FIXME
+ return 0;
+ case COND_OP_IS_SET:
+ return val;
+ case COND_OP_IS_UNSET:
+ return !val;
+ case COND_OP_IS_TRUE:
+ return (val == VALUE_1 || val == VALUE_YES || val == VALUE_TRUE);
+ case COND_OP_IS_FALSE:
+ return !(val == VALUE_1 || val == VALUE_YES || val == VALUE_TRUE);
+ default:
+ ASSERT(0);
+ }
+}
+
+static bool css_match_path_component(struct css_path *p, struct osm_object *o)
+{
+ switch (p->type)
+ {
+ case CSS_TYPE_ANY:
+ break;
+ case CSS_TYPE_NODE:
+ case CSS_TYPE_WAY:
+ case CSS_TYPE_RELATION:
+ if (o->type != p->type)
+ return 0;
+ break;
+ case CSS_TYPE_AREA:
+ if (o->type == OSM_TYPE_WAY && osm_way_cyclic_p((struct osm_way *) o))
+ break;
+ if (o->type == OSM_TYPE_MULTIPOLYGON)
+ break;
+ return 0;
+ default:
+ return 0;
+ }
+
+ if (p->modifiers & PATH_MOD_NEVER)
+ return 0;
+
+ CLIST_FOR_EACH(struct css_condition *, c, p->conditions)
+ if (!css_match_condition(c, o))
+ return 0;
+
+ return 1;
+}
+
+static bool css_match_path(struct css_selector *s, struct css_path *p, struct osm_object *o);
+
+static bool css_match_path_subtree(struct css_selector *s, struct css_path *p, struct osm_object *o)
+{
+ CLIST_FOR_EACH(struct osm_ref *, ref, o->backrefs)
+ if (css_match_path(s, p, ref->o))
+ return 1;
+
+ CLIST_FOR_EACH(struct osm_ref *, ref, o->backrefs)
+ if (css_match_path_subtree(s, p, ref->o))
+ return 1;
+
+ return 0;
+}
+
+static bool css_match_path(struct css_selector *s, struct css_path *p, struct osm_object *o)
+{
+ if (!css_match_path_component(p, o))
+ return 0;
+
+ p = clist_prev(&s->path, &p->n);
+ if (!p)
+ return 1;
+
+ return css_match_path_subtree(s, p, o);
+}
+
+static bool css_match_selector(struct css_selector *s, struct style_results *r)
+{
+ struct css_path *p = clist_tail(&s->path);
+ ASSERT(p);
+ return css_match_path(s, p, r->obj);
+}
+
+static void css_apply_action(struct css_action *sa, struct style_results *r, layer_t layer)
+{
+ style_set_by_layer(r, layer, &sa->prop);
+}
+
+void css_apply(struct css_sheet *ss, struct style_results *r)
+{
+ CLIST_FOR_EACH(struct css_rule *, sr, ss->rules)
+ {
+ layer_t last_layer = ~0U;
+ CLIST_FOR_EACH(struct css_selector *, sel, sr->selectors)
+ {
+ if (css_match_selector(sel, r))
+ {
+ // XXX: Rules with selectors in multiple layers could be further optimized
+ struct css_path *cp = clist_tail(&sel->path);
+ layer_t layer = cp ? cp->layer : STYLE_LAYER_DEFAULT;
+ if (layer != last_layer)
+ {
+ last_layer = layer;
+ CLIST_FOR_EACH(struct css_action *, sa, sr->actions)
+ css_apply_action(sa, r, layer);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Hic Est Leo -- MapCSS Stylesheets
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_CSS_H
+#define _BRUM_CSS_H
+
+#include "osm.h"
+#include "style.h"
+
+struct css_sheet {
+ struct mempool *pool;
+ clist rules;
+ char *filename;
+};
+
+struct css_rule {
+ cnode n; // In the list of rules
+ clist selectors; // Alternative selectors
+ clist actions;
+};
+
+struct css_selector {
+ cnode n;
+ clist path; // Element path
+};
+
+enum css_object_type {
+ CSS_TYPE_ANY = OSM_TYPE_INVALID,
+ CSS_TYPE_NODE = OSM_TYPE_NODE,
+ CSS_TYPE_WAY = OSM_TYPE_WAY,
+ CSS_TYPE_RELATION = OSM_TYPE_RELATION,
+ CSS_TYPE_AREA,
+ CSS_TYPE_META,
+};
+
+struct css_path {
+ cnode n;
+ enum osm_object_type type; // Object type or OSM_TYPE_ANY
+ clist conditions; // Attribute conditions
+ layer_t layer; // STYLE_LAYER_xxx or more
+ osm_val_t role;
+ uns modifiers;
+};
+
+enum css_path_modifier {
+ PATH_MOD_NEVER = 1, // Unknown modifier, never match
+};
+
+enum css_cond_op {
+ COND_OP_EQ,
+ COND_OP_NE,
+ COND_OP_LT,
+ COND_OP_GT,
+ COND_OP_LE,
+ COND_OP_GE,
+ COND_OP_IS_SET,
+ COND_OP_IS_UNSET,
+ COND_OP_IS_TRUE,
+ COND_OP_IS_FALSE,
+};
+
+struct css_condition {
+ cnode n;
+ enum css_cond_op op;
+ osm_key_t key;
+ osm_val_t val;
+};
+
+struct css_action {
+ cnode n;
+ struct style_prop prop;
+};
+
+/* css-parse.y */
+
+extern struct css_sheet *css_this;
+
+struct css_sheet *css_load(char *filename);
+
+/* css-lex.c */
+
+void css_error(char *err, ...);
+int css_lex(void);
+void css_lex_open(void);
+void css_lex_close(void);
+
+color_t css_rgb_to_color(const char *rgb);
+
+/* css.c */
+
+void css_dump(struct css_sheet *ss);
+void css_dump_rule(struct css_rule *r);
+void css_dump_selector(struct css_selector *s);
+void css_dump_action(struct css_action *a);
+
+void css_apply(struct css_sheet *ss, struct style_results *r);
+
+#endif
--- /dev/null
+osmosis --read-xml file=czech_republic-2014-05-18.osm.gz --log-progress --bounding-box top=50.17 bottom=50.12 left=14.50 right=14.60 completeWays=yes --write-xml
+# maybe also completeRelations=yes
--- /dev/null
+# List of OSM tag keys, parsed by ./gen-dict
+# Please keep sorted
+
+addr:housenumber
+brand
+highway
+leisure
+name
+name:cz
+operator
+ref
+type
--- /dev/null
+# List of style property keys, parsed by ./gen-dict
+# Please keep sorted
+
+casing-major-z-index
+fill-color
+fill-image
+fill-opacity
+fill-pattern
+fill-pattern-width
+fill-pattern-height
+fill-position
+font-family
+font-size
+font-style
+font-weight
+icon-height
+icon-image
+icon-opacity
+icon-width
+major-z-index
+object-z-index
+repeat-image
+repeat-image-align
+repeat-image-height
+repeat-image-offset
+repeat-image-spacing
+repeat-image-phase
+repeat-image-width
+symbol-fill-color
+symbol-fill-opacity
+symbol-shape
+symbol-size
+symbol-stroke-color
+symbol-stroke-opacity
+symbol-stroke-width
+text
+text-anchor-horizontal
+text-anchor-vertical
+text-color
+text-dup-threshold
+text-halo-color
+text-halo-opacity
+text-halo-radius
+text-major-z-index
+text-offset
+text-offset-x
+text-offset-y
+text-opacity
+text-position
+z-index
+
+# The following properties come in couples, which must not be split
+# (for reasons, see their use in sym-gen.c)
+
+color
+casing-color
+
+dashes
+casing-dashes
+
+dashes-offset
+casing-dashes-offset
+
+linecap
+casing-linecap
+
+linejoin
+casing-linejoin
+
+miterlimit
+casing-miterlimit
+
+opacity
+casing-opacity
+
+width
+casing-width
--- /dev/null
+# List of OSM tag values, parsed by ./gen-dict
+# Please keep sorted
+
+0
+1
+above
+auto
+below
+bevel
+bold
+bottom
+center
+circle
+false
+inner
+italic
+left
+line
+miter
+multipolygon
+no
+none
+normal
+outer
+right
+round
+square
+top
+true
+yes
--- /dev/null
+/*
+ * Hic Est Leo -- Universal Dictionaries
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/mempool.h>
+
+#include "leo.h"
+#include "dict.h"
+
+struct kv_map {
+ u32 id;
+ char *name;
+};
+
+#define HASH_NODE struct kv_map
+#define HASH_PREFIX(x) kv_map_##x
+#define HASH_KEY_STRING name
+#define HASH_WANT_LOOKUP
+#define HASH_AUTO_POOL 4096
+#define HASH_ZERO_FILL
+#define HASH_TABLE_DYNAMIC
+#define HASH_GIVE_ALLOC
+#define HASH_TABLE_VARS struct mempool *kv_pool;
+static void *kv_map_alloc(struct kv_map_table *table, uns size);
+#include <ucw/hashtable.h>
+
+static void *kv_map_alloc(struct kv_map_table *table, uns size)
+{
+ return mp_alloc(table->kv_pool, size);
+}
+
+void dict_init(struct dict *dict, const char * const *init)
+{
+ GARY_INIT(dict->names, 1);
+ dict->pool = mp_new(4096);
+ dict->hash = mp_alloc_zero(dict->pool, sizeof(struct kv_map_table));
+ dict->hash->kv_pool = dict->pool;
+ kv_map_init(dict->hash);
+
+ if (init)
+ {
+ for (uns i=1; init[i]; i++)
+ {
+ u32 id = dict_encode(dict, init[i]);
+ ASSERT(id == i);
+ }
+ }
+}
+
+u32 dict_encode(struct dict *d, const char *key)
+{
+ struct kv_map *k = kv_map_lookup(d->hash, (char *) key);
+ if (k->id)
+ return k->id;
+
+ k->id = GARY_SIZE(d->names);
+ k->name = mp_strdup(d->pool, key);
+ *GARY_PUSH(d->names) = k->name;
+ return k->id;
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Universal Dictionaries
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_DICT_H
+#define _BRUM_DICT_H
+
+#include <ucw/gary.h>
+
+struct dict {
+ const char **names; // Growing array of names
+ struct kv_map_table *hash; // Hash table mapping name -> ID
+ struct mempool *pool;
+};
+
+void dict_init(struct dict *dict, const char * const * init);
+
+static inline const char *dict_decode(struct dict *d, u32 id)
+{
+ ASSERT(id < GARY_SIZE(d->names));
+ return d->names[id];
+}
+
+u32 dict_encode(struct dict *d, const char *key);
+
+static inline u32 dict_size(struct dict *d)
+{
+ return GARY_SIZE(d->names);
+}
+
+#endif
--- /dev/null
+#!/usr/bin/perl
+# Generate dict-*.h from dict-*.t
+
+use strict;
+use warnings;
+
+print "// Auto-generated by gen-dict\n";
+while (<STDIN>) {
+ chomp;
+ next if /^$/ or /^#/;
+ my $prop = $_;
+ my $sym = $_;
+ $sym =~ s{[^A-Za-z0-9_]}{_}g;
+ $sym =~ tr{a-z}{A-Z};
+ print "P($sym, \"$prop\")\n";
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="11.843085"
+ id="svg2"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="bus_stop.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="6.7830434">
+ <g
+ id="wrapper-8"
+ transform="matrix(0.02,0,0,0.02,-2.4945105,0.12651049)">
+ <rect
+ height="323.03857"
+ id="rect3653-4"
+ ry="20.087805"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:43.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ width="295.40216"
+ x="146.60052"
+ y="15.549476" />
+ <path
+ d="m 294.60317,339.19967 0,224.75409"
+ id="path4184-3"
+ style="fill:none;stroke:#ffffff;stroke-width:43.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 383.58469,551.291 -181.03183,0"
+ id="path4186-1"
+ style="fill:none;stroke:#ffffff;stroke-width:43.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <g
+ id="wrapper"
+ transform="matrix(0.02,0,0,0.02,-2.4873596,0.11766564)">
+ <path
+ d="m 295.63339,53.000277 c 0.32324,0 0.65906,0.01236 0.98567,0.01493 0.32889,-0.0026 0.66023,-0.01493 0.98569,-0.01493 l -1.97136,0 z m 0.98567,0.01493 c -22.95139,0.181844 -54.04733,6.644254 -67.02642,12.052208 -13.16508,5.48545 -21.94443,10.973538 -24.68688,24.686875 l -9.87176,76.03199 0,104.75121 17.0105,0 0,16.42804 c 0,20.03034 29.30166,20.03033 29.30166,0 l 0,-16.42804 54.12295,0 0.16428,0 56.25859,0 0,16.42804 c 0,20.03034 29.31659,20.03033 29.31659,0 l 0,-16.42804 17.0105,0 0,-104.75121 -9.87176,-76.03199 C 385.60486,76.040957 376.82552,70.552869 363.66043,65.067419 350.68008,59.658941 319.57112,53.195848 296.61906,53.015211 z m -41.78697,19.190945 40.8013,0 42.78759,0 c 8.22846,0 8.22846,12.335971 0,12.335971 l -42.8772,0 -40.71169,0 c -8.22846,0 -8.22846,-12.335971 0,-12.335971 z M 236.6393,97.0125 l 58.90448,0 59.05506,0 c 8.69202,0 10.96704,4.41206 12.02234,11.11134 l 7.69131,55.1385 c 0.71652,5.28515 -0.8225,10.52888 -8.12441,10.52888 l -70.55469,0 -70.58334,0 c -7.30191,0 -8.82599,-5.24374 -8.10948,-10.52888 l 7.69132,-55.1385 c 1.05531,-6.69928 3.31538,-11.11134 12.00741,-11.11134 z m -9.92904,110.562 c 7.7649,-1e-5 16.05345,8.30348 16.05345,16.06838 0,7.76491 -8.28855,16.05345 -16.05345,16.05345 -7.7649,1e-5 -16.06838,-8.28855 -16.06838,-16.05345 0,-7.7649 8.30348,-16.06838 16.06838,-16.06838 z m 139.83254,0 c 7.76491,0 16.05345,8.30348 16.05346,16.06838 0,7.7649 -8.28855,16.05346 -16.05346,16.05345 -7.76489,0 -16.06838,-8.28854 -16.06837,-16.05345 0,-7.7649 8.30348,-16.06839 16.06837,-16.06838 z"
+ id="path2115"
+ sodipodi:nodetypes="cscccsccccccccccccccccsccccccccccccccccccccsssccsssc"
+ style="fill:#000000;stroke:none;fill-opacity:1" />
+ <path
+ d="m 294.60317,339.19967 0,224.75409"
+ id="path4184"
+ style="fill:none;stroke:#0000e0;stroke-width:28.79948235000000167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 383.58469,551.291 -181.03183,0"
+ id="path4186"
+ style="fill:none;stroke:#0000e0;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <rect
+ height="323.03857"
+ id="rect3653"
+ ry="20.087805"
+ style="fill:none;stroke:#0000d0;stroke-width:15;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ width="295.40216"
+ x="146.60052"
+ y="15.549476" />
+ </g>
+ <metadata
+ id="metadata1976">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>
+image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>
+en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ id="base"
+ inkscape:current-layer="svg2"
+ inkscape:cx="0.80214163"
+ inkscape:cy="7.4724256"
+ inkscape:pageopacity="0.69411765"
+ inkscape:pageshadow="2"
+ inkscape:window-height="748"
+ inkscape:window-width="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:zoom="29.493668"
+ objecttolerance="10.0"
+ pagecolor="#2f51ff"
+ showgrid="false"
+ inkscape:snap-bbox="false"
+ inkscape:snap-bbox-midpoints="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-grids="false"
+ inkscape:snap-global="false"
+ inkscape:window-maximized="1">
+ <sodipodi:guide
+ orientation="1,0"
+ position="2.8829365,10.53509"
+ id="guide3965" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="7.1550399,13.179726"
+ id="guide3967" />
+ <inkscape:grid
+ type="xygrid"
+ id="grid4491"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ id="perspective3653"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ id="ArrowStart"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="10"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 10,0 0,5 10,10 z"
+ id="path2111" />
+ </marker>
+ <marker
+ id="ArrowEnd"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="0"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 0,0 10,5 0,10 z"
+ id="path2108" />
+ </marker>
+ <inkscape:perspective
+ id="perspective2512"
+ inkscape:persp3d-origin="177.51199 : 145.02 : 1"
+ inkscape:vp_x="0 : 217.53 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="355.02399 : 217.53 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3939"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg height="11.339" id="svg2" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.46" sodipodi:docbase="s:\Data\FacilityIcons" sodipodi:docname="highway_bus_stop.svg" sodipodi:version="0.32" version="1.0" width="11.339" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
+ <metadata id="metadata1976">
+ <rdf:RDF >
+ <cc:Work rdf:about="">
+ <dc:format >
+image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language >
+en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" gridtolerance="10.0" guidetolerance="10.0" id="base" inkscape:current-layer="svg2" inkscape:cx="68.469786" inkscape:cy="186.81904" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="1003" inkscape:window-width="758" inkscape:window-x="12" inkscape:window-y="13" inkscape:zoom="0.65172414" objecttolerance="10.0" pagecolor="#ffffff" showgrid="false" />
+ <defs id="defs4">
+ <inkscape:perspective id="perspective3653" inkscape:persp3d-origin="290 : 193.33333 : 1" inkscape:vp_x="0 : 290 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="580 : 290 : 1" sodipodi:type="inkscape:persp3d" />
+ <marker id="ArrowStart" markerHeight="3" markerUnits="strokeWidth" markerWidth="4" orient="auto" refX="10" refY="5" viewBox="0 0 10 10">
+ <path d="M 10 0 L 0 5 L 10 10 z" id="path2111" />
+ </marker>
+ <marker id="ArrowEnd" markerHeight="3" markerUnits="strokeWidth" markerWidth="4" orient="auto" refX="0" refY="5" viewBox="0 0 10 10">
+ <path d="M 0 0 L 10 5 L 0 10 z" id="path2108" />
+ </marker>
+ <inkscape:perspective id="perspective2512" inkscape:persp3d-origin="177.51199 : 145.02 : 1" inkscape:vp_x="0 : 217.53 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="355.02399 : 217.53 : 1" sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <g id="wrapper" transform="scale(0.020)">
+ <path d="M 290.625 34.5625 C 291.30137 34.5625 292.00406 34.588371 292.6875 34.59375 C 293.37567 34.588298 294.069 34.5625 294.75 34.5625 L 290.625 34.5625 z M 292.6875 34.59375 C 244.66265 34.974252 179.59571 48.496579 152.4375 59.8125 C 124.89013 71.290575 106.51969 82.774174 100.78125 111.46875 L 80.125 270.5625 L 80.125 489.75 L 115.71875 489.75 L 115.71875 524.125 C 115.71875 566.03764 177.03125 566.03762 177.03125 524.125 L 177.03125 489.75 L 290.28125 489.75 L 290.625 489.75 L 408.34375 489.75 L 408.34375 524.125 C 408.34375 566.03764 469.6875 566.03762 469.6875 524.125 L 469.6875 489.75 L 505.28125 489.75 L 505.28125 270.5625 L 484.625 111.46875 C 478.88656 82.774174 460.51613 71.290573 432.96875 59.8125 C 405.80789 48.495478 340.71371 34.971722 292.6875 34.59375 z M 205.25 74.75 L 290.625 74.75 L 380.15625 74.75 C 397.37395 74.750004 397.37396 100.5625 380.15625 100.5625 L 290.4375 100.5625 L 205.25 100.5625 C 188.0323 100.5625 188.03229 74.75 205.25 74.75 z M 158.8125 126.65625 L 290.4375 126.65625 L 426.5625 126.65625 C 444.75018 126.65625 449.51057 135.88827 451.71875 149.90625 L 467.8125 265.28125 C 469.31177 276.3402 466.09144 287.31249 450.8125 287.3125 L 290.625 287.3125 L 134.5625 287.3125 C 119.28355 287.3125 116.09448 276.34019 117.59375 265.28125 L 133.6875 149.90625 C 135.8957 135.88827 140.62481 126.65625 158.8125 126.65625 z M 146.40625 362.1875 C 162.65398 362.18749 175.8125 375.37727 175.8125 391.625 C 175.8125 407.87274 162.65398 421.03125 146.40625 421.03125 C 130.15852 421.03126 116.96875 407.87273 116.96875 391.625 C 116.96875 375.37728 130.15852 362.1875 146.40625 362.1875 z M 439 362.1875 C 455.24773 362.1875 468.40624 375.37728 468.40625 391.625 C 468.40625 407.87273 455.24773 421.03126 439 421.03125 C 422.75227 421.03125 409.56249 407.87274 409.5625 391.625 C 409.5625 375.37727 422.75228 362.18749 439 362.1875 z " id="path2115" style="fill: #ff0000; stroke: none" />
+ </g>
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="11.339"
+ id="svg2"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="cave.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="11.339">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>
+image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>
+en</dc:language>
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ id="base"
+ inkscape:current-layer="wrapper"
+ inkscape:cx="7.3380697"
+ inkscape:cy="0.6565035"
+ inkscape:guide-bbox="true"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="1004"
+ inkscape:window-maximized="1"
+ inkscape:window-width="1272"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:zoom="11.313708"
+ objecttolerance="10.0"
+ pagecolor="#ffffff"
+ showgrid="false"
+ showguides="true" />
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ id="perspective12"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <style
+ id="style6"
+ type="text/css">
+ .fil0 {fill:#EF7900}</style>
+ <inkscape:perspective
+ id="perspective6807"
+ inkscape:persp3d-origin="29.116032 : 19.410688 : 1"
+ inkscape:vp_x="0 : 29.116032 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="58.232063 : 29.116032 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective6954"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2541"
+ inkscape:persp3d-origin="240 : 160 : 1"
+ inkscape:vp_x="0 : 240 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="480 : 240 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2750"
+ inkscape:persp3d-origin="215 : 143.33333 : 1"
+ inkscape:vp_x="0 : 215 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="430 : 215 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2830"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_x="0 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <g
+ id="wrapper"
+ transform="scale(0.020)">
+ <path
+ d="m 34.702986,408.13437 c 2.367196,-5.48093 32.404879,-47.87543 66.750284,-94.2099 49.25528,-66.44891 74.61409,-89.47895 120.04961,-109.0248 71.12554,-30.59742 142.32874,-32.35525 168.26002,-4.15402 61.33558,66.70455 124.63054,142.87846 142.85807,171.92632 25.68601,40.93369 12.60173,60.07407 -14.50984,21.22601 C 507.42352,378.58371 477.4635,342.01325 451.53328,312.63007 340.43961,186.74287 340.10029,186.61322 236.90791,230.57389 186.18922,252.18048 166.47754,270.06043 123.61005,333.34337 95.06701,375.47997 62.417798,411.78794 51.05624,414.0276 39.69479,416.26738 32.3358,413.61508 34.702986,408.13437 z"
+ id="path2838"
+ sodipodi:nodetypes="csssssssssc"
+ style="fill-opacity:1;fill:#000000" />
+ <path
+ d="m 34.702986,408.13437 c 2.367196,-5.48093 32.404879,-47.87543 66.750284,-94.2099 49.25528,-66.44891 74.61409,-89.47895 120.04961,-109.0248 71.12554,-30.59742 142.32874,-32.35525 168.26002,-4.15402 61.33558,66.70455 124.63054,142.87846 142.85807,171.92632 34.40213,55.71502 -22.68994,62.23235 -174.08769,65.9581 -80.61454,2.31281 -85.24178,0.87361 -168.92323,1.7133 -39.07364,-1.12238 -156.361399,-19.74053 -154.907064,-32.209 z"
+ id="path3593"
+ sodipodi:nodetypes="cssscccc"
+ style="fill-opacity:1;opacity:0.10000000000000001;fill:#000000;stroke:none;stroke-opacity:1" />
+ <path
+ d="m 150.97846,415.18101 c 0,-32.36715 90.19166,-134.76949 118.69935,-134.76949 45.60596,0 92.84717,28.28822 127.25562,76.20114 20.30607,28.27572 36.92016,54.52123 36.92016,58.32343 0,3.80207 -63.64691,6.91299 -141.43757,6.91299 -77.79065,0 -141.43756,-3.00059 -141.43756,-6.66807 z"
+ id="path3638"
+ sodipodi:nodetypes="cssssc"
+ style="fill-opacity:1;fill:#000000" />
+ </g>
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="81"
+ height="31.28476"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="jeskyne.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6"
+ inkscape:cx="36.228065"
+ inkscape:cy="-0.28235733"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:snap-bbox="false"
+ inkscape:object-paths="true"
+ inkscape:window-width="1016"
+ inkscape:window-height="748"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2816"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-264.5,-506.86215)">
+ <g
+ id="g3609"
+ style="stroke:#ffffff;stroke-width:11;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#ffffff;stroke-width:11;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3611"
+ sodipodi:cx="305"
+ sodipodi:cy="532.36218"
+ sodipodi:rx="20"
+ sodipodi:ry="20"
+ d="m 285.00173,532.62508 c -0.1452,-11.04474 8.69063,-20.11598 19.73537,-20.26117 11.04475,-0.14519 20.11598,8.69064 20.26117,19.73538 0.002,0.18253 0.002,0.36509 -3e-4,0.54762"
+ sodipodi:start="3.1284475"
+ sodipodi:end="6.297422"
+ sodipodi:open="true" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:11;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 270,532.36218 15,0"
+ id="path3613" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:11;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 325,532.36218 15,0"
+ id="path3615" />
+ </g>
+ <g
+ id="g3588">
+ <path
+ sodipodi:open="true"
+ sodipodi:end="6.297422"
+ sodipodi:start="3.1284475"
+ d="m 285.00173,532.62508 c -0.1452,-11.04474 8.69063,-20.11598 19.73537,-20.26117 11.04475,-0.14519 20.11598,8.69064 20.26117,19.73538 0.002,0.18253 0.002,0.36509 -3e-4,0.54762"
+ sodipodi:ry="20"
+ sodipodi:rx="20"
+ sodipodi:cy="532.36218"
+ sodipodi:cx="305"
+ id="path2820"
+ style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ sodipodi:type="arc" />
+ <path
+ id="path3594"
+ d="m 270,532.36218 15,0"
+ style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path3596"
+ d="m 325,532.36218 15,0"
+ style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100"
+ height="120"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="cemetery.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="91.769571"
+ inkscape:cy="64.14082"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1016"
+ inkscape:window-height="748"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2816"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-220,-446.36218)">
+ <g
+ id="g3594"
+ transform="translate(0,-6)">
+ <path
+ id="path2818"
+ d="m 230,482.36218 40,0"
+ style="fill:none;stroke:#936506;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2820"
+ d="m 250,462.36218 0,50"
+ style="fill:none;stroke:#936506;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <use
+ x="0"
+ y="0"
+ xlink:href="#g3594"
+ id="use3598"
+ transform="translate(40,60)"
+ width="744.09448"
+ height="1052.3622" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="7.021503"
+ height="12.639885"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="church.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective3607"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#4500ff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.55294118"
+ inkscape:pageshadow="2"
+ inkscape:zoom="16"
+ inkscape:cx="5.9464163"
+ inkscape:cy="-6.0866638"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-grids="true"
+ inkscape:window-width="1272"
+ inkscape:window-height="1004"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ borderlayer="false"
+ showborder="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2816"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="mm"
+ spacingx="0.3333mm"
+ spacingy="0.3333mm"
+ dotted="false" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-2.0859509,-116.21568)">
+ <g
+ id="g3594-5"
+ style="stroke:#ffffff;stroke-width:1.77165353;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ transform="matrix(0.79214264,0,0,0.79214264,0.57943399,24.302138)">
+ <path
+ transform="translate(-53.899592,-3.6683608)"
+ d="m 63.779528,131.22736 c 0,1.95691 -1.586392,3.5433 -3.543307,3.5433 -1.956914,0 -3.543307,-1.58639 -3.543307,-3.5433 0,-1.95692 1.586393,-3.54331 3.543307,-3.54331 1.956915,0 3.543307,1.58639 3.543307,3.54331 z"
+ sodipodi:ry="3.5433071"
+ sodipodi:rx="3.5433071"
+ sodipodi:cy="131.22736"
+ sodipodi:cx="60.236221"
+ id="path2858-5"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1.77165353;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ sodipodi:type="arc" />
+ <path
+ transform="translate(-53.899592,-3.6683608)"
+ sodipodi:nodetypes="cc"
+ id="path3632-0"
+ d="m 60.230197,128.85263 0,-8.26689"
+ style="fill:none;stroke:#ffffff;stroke-width:1.77165353;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ transform="translate(-53.899592,-3.6683608)"
+ sodipodi:nodetypes="cc"
+ id="path3634-5"
+ d="m 56.687244,124.12869 7.085906,0"
+ style="fill:none;stroke:#ffffff;stroke-width:1.77165353;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <g
+ id="g3594"
+ transform="matrix(0.79214264,0,0,0.79214264,0.57943399,24.302138)">
+ <path
+ transform="translate(-53.899592,-3.6683608)"
+ d="m 63.779528,131.22736 c 0,1.95691 -1.586392,3.5433 -3.543307,3.5433 -1.956914,0 -3.543307,-1.58639 -3.543307,-3.5433 0,-1.95692 1.586393,-3.54331 3.543307,-3.54331 1.956915,0 3.543307,1.58639 3.543307,3.54331 z"
+ sodipodi:ry="3.5433071"
+ sodipodi:rx="3.5433071"
+ sodipodi:cy="131.22736"
+ sodipodi:cx="60.236221"
+ id="path2858"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path3632"
+ d="m 60.230197,128.85263 0,-8.26689"
+ style="fill:none;stroke:#000000;stroke-width:1.0629921;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ transform="translate(-53.899592,-3.6683608)" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path3634"
+ d="m 56.687244,124.12869 7.085906,0"
+ style="fill:none;stroke:#000000;stroke-width:0.99921262;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ transform="translate(-53.899592,-3.6683608)" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="17.96653"
+ height="3.6682999"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="cliff.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.70710678"
+ inkscape:cx="401.49514"
+ inkscape:cy="-263.82391"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-grids="true"
+ inkscape:window-width="1272"
+ inkscape:window-height="1004"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ borderlayer="false"
+ showborder="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2816"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="mm"
+ spacingx="1mm"
+ spacingy="1mm"
+ dotted="false" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(53.899592,3.6683608)">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m -53.774592,-3.5433608 17.71653,0"
+ id="path2818"
+ sodipodi:nodetypes="cc"
+ inkscape:transform-center-x="-1.8966508"
+ inkscape:transform-center-y="0.125" />
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ d="m -46.687982,-3.5433608 1.76698,3.543300013406 1.77633,-3.543300013406 -3.54331,0"
+ id="path2822"
+ inkscape:transform-center-x="-1.8966508"
+ inkscape:transform-center-y="1.89665" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="54"
+ height="69"
+ id="svg3759"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="cross.svg">
+ <defs
+ id="defs3761" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6"
+ inkscape:cx="13.964286"
+ inkscape:cy="31.999997"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1016"
+ inkscape:window-height="748"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4283"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ originx="-298px"
+ originy="-488px" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata3764">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-298,-495.36218)">
+ <g
+ id="g4291"
+ style="stroke:#ffffff;stroke-width:18;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4285"
+ d="m 325,502.36218 0,55"
+ style="fill:none;stroke:#ffffff;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4287"
+ d="m 305,522.36218 40,0"
+ style="fill:none;stroke:#ffffff;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4289"
+ d="m 325,557.36218 10,0"
+ style="fill:none;stroke:#ffffff;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <g
+ transform="translate(0,2.6171874e-6)"
+ id="g4291-9"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4285-5"
+ d="m 325,502.36218 0,55"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4287-4"
+ d="m 305,522.36218 40,0"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4289-4"
+ d="m 325,557.36218 10,0"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="11.339"
+ id="svg2"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="deciduous.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="11.339">
+ <metadata
+ id="metadata2975">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>
+image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ id="base"
+ inkscape:current-layer="wrapper"
+ inkscape:cx="5.8105071"
+ inkscape:cy="5.5752164"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="768"
+ inkscape:window-width="1024"
+ inkscape:window-x="0"
+ inkscape:window-y="20"
+ inkscape:zoom="35.399848"
+ objecttolerance="10.0"
+ pagecolor="#ffffff"
+ showgrid="false"
+ inkscape:window-maximized="0" />
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ id="perspective2441"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2466"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3333"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3401"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3464"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3581"
+ inkscape:persp3d-origin="225 : 150 : 1"
+ inkscape:vp_x="0 : 225 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="450 : 225 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4312"
+ inkscape:persp3d-origin="225 : 150 : 1"
+ inkscape:vp_x="0 : 225 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="450 : 225 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective8860"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective8887"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4904"
+ inkscape:persp3d-origin="16 : 10.666667 : 1"
+ inkscape:vp_x="0 : 16 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="32 : 16 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4668"
+ inkscape:persp3d-origin="6 : 4 : 1"
+ inkscape:vp_x="0 : 6 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="12 : 6 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4471"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <symbol
+ id="symbol-university"
+ viewBox="244.5 110 489 219.9">
+ <path
+ d="M79,43l57,119c0,0,21-96,104-96s124,106,124,106l43-133l82-17L0,17L79,43z"
+ id="path4460" />
+ <path
+ d="M94,176l-21,39"
+ fill="none"
+ id="path4462"
+ stroke="#000000"
+ stroke-width="20" />
+ <path
+ d="M300,19c0,10.5-22.6,19-50.5,19S199,29.5,199,19s22.6-19,50.5-19S300,8.5,300,19z"
+ id="path4464" />
+ <path
+ d="M112,216l-16-38L64,88c0,0-9-8-4-35s16-24,16-24"
+ id="path4466"
+ ill="none"
+ stroke="#000000"
+ stroke-width="20" />
+ </symbol>
+ <inkscape:perspective
+ id="perspective3452"
+ inkscape:persp3d-origin="30 : 20 : 1"
+ inkscape:vp_x="0 : 30 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="60 : 30 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9479"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9690"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9819"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9817"
+ inkscape:persp3d-origin="30 : 20 : 1"
+ inkscape:vp_x="0 : 30 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="60 : 30 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <symbol
+ id="symbol9807"
+ viewBox="244.5 110 489 219.9">
+ <path
+ d="M79,43l57,119c0,0,21-96,104-96s124,106,124,106l43-133l82-17L0,17L79,43z"
+ id="path9809" />
+ <path
+ d="M94,176l-21,39"
+ fill="none"
+ id="path9811"
+ stroke="#000000"
+ stroke-width="20" />
+ <path
+ d="M300,19c0,10.5-22.6,19-50.5,19S199,29.5,199,19s22.6-19,50.5-19S300,8.5,300,19z"
+ id="path9813" />
+ <path
+ d="M112,216l-16-38L64,88c0,0-9-8-4-35s16-24,16-24"
+ id="path9815"
+ ill="none"
+ stroke="#000000"
+ stroke-width="20" />
+ </symbol>
+ <inkscape:perspective
+ id="perspective9805"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9803"
+ inkscape:persp3d-origin="6 : 4 : 1"
+ inkscape:vp_x="0 : 6 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="12 : 6 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9801"
+ inkscape:persp3d-origin="16 : 10.666667 : 1"
+ inkscape:vp_x="0 : 16 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="32 : 16 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9799"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9797"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9795"
+ inkscape:persp3d-origin="225 : 150 : 1"
+ inkscape:vp_x="0 : 225 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="450 : 225 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9793"
+ inkscape:persp3d-origin="225 : 150 : 1"
+ inkscape:vp_x="0 : 225 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="450 : 225 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9791"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9789"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9787"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9785"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective9783"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <g
+ id="wrapper"
+ transform="scale(0.020)">
+ <path
+ style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1;stroke-width:50;stroke-miterlimit:4;stroke-dasharray:none"
+ id="path3625"
+ d="M 286.8826,97.87318 C 261.09763,97.43733 233.95641,111.70565 228.47068,140.94717 C 226.14803,153.3279 224.20796,157.30914 209.5539,157.56326 C 164.41142,158.3461 145.21328,224.7052 183.35162,255.98159 C 187.1306,259.08071 184.9536,261.77225 172.23163,271.44733 C 107.48588,320.68656 177.20141,418.93839 243.42515,371.78291 C 253.12064,364.87912 254.65808,364.80358 256.84583,370.50475 C 261.46373,382.5388 259.41139,440.32236 253.65043,459.4647 L 247.89871,478.50928 L 282.53686,478.50928 C 285.96604,478.50928 287.68128,478.52126 290.46145,478.50928 C 293.27634,478.5218 295.02937,478.50928 298.51385,478.50928 L 333.152,478.50928 L 327.5281,459.4647 C 321.76713,440.32236 319.58693,382.5388 324.20488,370.50475 C 326.39263,364.80358 327.93007,364.87912 337.62556,371.78291 C 403.84931,418.93839 473.56483,320.68656 408.81908,271.44733 C 396.09711,261.77225 393.26406,257.73253 397.82691,255.98159 C 435.96524,241.34651 415.52985,165.00264 371.49681,157.56326 C 357.04533,155.12171 355.0305,153.3279 352.70785,140.94717 C 346.96239,110.32135 317.40486,96.22105 290.58926,98.1288 C 289.3671,98.04127 288.11352,97.89399 286.8826,97.87318 z" />
+ <path
+ d="M 286.8826,97.87318 C 261.09763,97.43733 233.95641,111.70565 228.47068,140.94717 C 226.14803,153.3279 224.20796,157.30914 209.5539,157.56326 C 164.41142,158.3461 145.21328,224.7052 183.35162,255.98159 C 187.1306,259.08071 184.9536,261.77225 172.23163,271.44733 C 107.48588,320.68656 177.20141,418.93839 243.42515,371.78291 C 253.12064,364.87912 254.65808,364.80358 256.84583,370.50475 C 261.46373,382.5388 259.41139,440.32236 253.65043,459.4647 L 247.89871,478.50928 L 282.53686,478.50928 C 285.96604,478.50928 287.68128,478.52126 290.46145,478.50928 C 293.27634,478.5218 295.02937,478.50928 298.51385,478.50928 L 333.152,478.50928 L 327.5281,459.4647 C 321.76713,440.32236 319.58693,382.5388 324.20488,370.50475 C 326.39263,364.80358 327.93007,364.87912 337.62556,371.78291 C 403.84931,418.93839 473.56483,320.68656 408.81908,271.44733 C 396.09711,261.77225 393.26406,257.73253 397.82691,255.98159 C 435.96524,241.34651 415.52985,165.00264 371.49681,157.56326 C 357.04533,155.12171 355.0305,153.3279 352.70785,140.94717 C 346.96239,110.32135 317.40486,96.22105 290.58926,98.1288 C 289.3671,98.04127 288.11352,97.89399 286.8826,97.87318 z"
+ id="path9696"
+ style="fill:#006322;fill-opacity:1" />
+ </g>
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="7.7474999"
+ id="svg2"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="fuel.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="6.7085013">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>
+image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>
+en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ id="base"
+ inkscape:current-layer="g5143"
+ inkscape:cx="6.4066754"
+ inkscape:cy="5.7534912"
+ inkscape:pageopacity="0.51764706"
+ inkscape:pageshadow="2"
+ inkscape:window-height="748"
+ inkscape:window-width="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:zoom="32"
+ objecttolerance="10.0"
+ pagecolor="#8467ff"
+ showgrid="false"
+ inkscape:window-maximized="1" />
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ id="perspective12"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <style
+ id="style6"
+ type="text/css">
+ .fil0 {fill:#EF7900}</style>
+ <inkscape:perspective
+ id="perspective6807"
+ inkscape:persp3d-origin="29.116032 : 19.410688 : 1"
+ inkscape:vp_x="0 : 29.116032 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="58.232063 : 29.116032 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective6954"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ id="ArrowStart"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="10"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 10,0 0,5 10,10 z"
+ id="path2295" />
+ </marker>
+ <marker
+ id="ArrowEnd"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="0"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 0,0 10,5 0,10 z"
+ id="path2292" />
+ </marker>
+ <inkscape:perspective
+ id="perspective7597"
+ inkscape:persp3d-origin="178.5405 : 158.483 : 1"
+ inkscape:vp_x="0 : 237.7245 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="357.08099 : 237.7245 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ id="marker8025"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="10"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 10,0 0,5 10,10 10,0 z"
+ id="path8027" />
+ </marker>
+ <marker
+ id="marker8021"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="0"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 0,0 10,5 0,10 0,0 z"
+ id="path8023" />
+ </marker>
+ <inkscape:perspective
+ id="perspective8018"
+ inkscape:persp3d-origin="205.96652 : 131.6262 : 1"
+ inkscape:vp_x="0 : 197.4393 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="411.93304 : 197.4393 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective8269"
+ inkscape:persp3d-origin="99.087723 : 77.339676 : 1"
+ inkscape:vp_x="0 : 116.00951 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="198.17545 : 116.00951 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5155"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <g
+ id="wrapper"
+ transform="matrix(0.02,0,0,0.02,-4.6682468,-2.1477037)">
+ <g
+ id="g5143-0"
+ transform="translate(128.5249,11.260187)">
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:31.25;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path1897-6"
+ d="m 249,111.75 c -8.48242,0 -16.9375,6.13064 -16.9375,15.9375 l -0.1875,315.25 17.4375,0 0.15625,24.9375 160.09375,-0.0937 0.125,-24.84375 15,0.125 0,-318.625 c 10e-6,-6.79608 -3.93663,-12.6875 -11.34375,-12.6875 L 249,111.75 z m 26.78125,34.84375 104.90625,0 c 5.80827,0 10.37501,2.36551 10.375,10.375 l 0,82.875 c 0,5.61371 -3.00087,9.6875 -9.40625,9.6875 L 276.0625,249.25 c -6.09827,0 -11.34375,-4.50537 -11.34375,-11.0625 l 0.53125,-81.5 c 0,-6.5237 4.51979,-10.09375 10.53125,-10.09375 z" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:31.25;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path1899-8"
+ d="m 232.83211,220.10791 -16.02161,-0.0949 c -0.10388,65.19305 0.37125,193.85491 0.64277,196.05634 0.5796,4.69931 0.22912,9.56779 -0.97746,14.00308 -25.11647,63.27313 -106.38805,34.21172 -94.84854,-27.3567 25.3448,-61.07053 60.88334,-102.39502 78.57086,-174.42538 l 1.57455,53.54482 c -15.91818,30.97568 -44.00102,85.37648 -65.77992,130.84982 -1.83488,41.98386 48.10886,55.81624 63.75073,10.75222 6.97053,-78.88168 -0.26208,-164.86493 -0.39311,-247.2974 10.16452,-30.21727 21.38001,-33.10969 32.76209,-31.67045" />
+ </g>
+ <g
+ id="g5143"
+ transform="translate(128.28204,12.00983)">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path1897"
+ d="m 249,111.75 c -8.48242,0 -16.9375,6.13064 -16.9375,15.9375 l -0.1875,315.25 17.4375,0 0.15625,24.9375 160.09375,-0.0937 0.125,-24.84375 15,0.125 0,-318.625 c 10e-6,-6.79608 -3.93663,-12.6875 -11.34375,-12.6875 L 249,111.75 z m 26.78125,34.84375 104.90625,0 c 5.80827,0 10.37501,2.36551 10.375,10.375 l 0,82.875 c 0,5.61371 -3.00087,9.6875 -9.40625,9.6875 L 276.0625,249.25 c -6.09827,0 -11.34375,-4.50537 -11.34375,-11.0625 l 0.53125,-81.5 c 0,-6.5237 4.51979,-10.09375 10.53125,-10.09375 z" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ id="path1899"
+ d="m 232.83211,220.10791 -16.02161,-0.0949 c -0.10388,65.19305 0.37125,193.85491 0.64277,196.05634 0.5796,4.69931 0.22912,9.56779 -0.97746,14.00308 -25.11647,63.27313 -106.38805,34.21172 -94.84854,-27.3567 25.3448,-61.07053 60.88334,-102.39502 78.57086,-174.42538 l 1.57455,53.54482 c -15.91818,30.97568 -44.00102,85.37648 -65.77992,130.84982 -1.83488,41.98386 48.10886,55.81624 63.75073,10.75222 6.97053,-78.88168 -0.26208,-164.86493 -0.39311,-247.2974 10.16452,-30.21727 21.38001,-33.10969 32.76209,-31.67045" />
+ </g>
+ </g>
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="8.0017128"
+ id="svg2"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="gate.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="11.063485">
+ <g
+ id="wrapper"
+ transform="matrix(0.02,0,0,0.02,-0.35950751,-1.8806165)"
+ inkscape:transform-center-y="-2"
+ style="stroke:#ffffff;stroke-width:100;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ <path
+ d="m 68.690452,226.50324 451.760378,0"
+ id="path2900"
+ style="fill:none;stroke:#ffffff;stroke-width:100;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 68.690452,360.07403 451.760378,0"
+ id="path3730"
+ sodipodi:nodetypes="cc"
+ style="fill:none;stroke:#ffffff;stroke-width:100;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ inkscape:connector-curvature="0" />
+ <path
+ d="M 132.37824,443.67778 452.51615,144.52914"
+ id="path3732"
+ sodipodi:nodetypes="cc"
+ style="fill:none;stroke:#ffffff;stroke-width:100;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ inkscape:connector-curvature="0" />
+ </g>
+ <defs
+ id="defs22">
+ <inkscape:perspective
+ id="perspective24"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ id="ArrowStart"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="10"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 10,0 0,5 10,10 z"
+ id="path3568"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ id="ArrowEnd"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="0"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 0,0 10,5 0,10 z"
+ id="path3565"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <inkscape:perspective
+ id="perspective2683"
+ inkscape:persp3d-origin="306.082 : 204.39034 : 1"
+ inkscape:vp_x="0 : 306.58551 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="612.164 : 306.58551 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ id="marker2959"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="10"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 10,0 0,5 10,10 z"
+ id="path2626"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ id="marker2956"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="0"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 0,0 10,5 0,10 z"
+ id="path2623"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <inkscape:perspective
+ id="perspective2953"
+ inkscape:persp3d-origin="306.082 : 204.39034 : 1"
+ inkscape:vp_x="0 : 306.58551 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="612.164 : 306.58551 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ id="marker3077"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="10"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 10,0 0,5 10,10 z"
+ id="path3298"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ id="marker3074"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="0"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 0,0 10,5 0,10 z"
+ id="path3295"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <inkscape:perspective
+ id="perspective3071"
+ inkscape:persp3d-origin="180.936 : 163.92867 : 1"
+ inkscape:vp_x="0 : 245.89301 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="361.87201 : 245.89301 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3724"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_x="0 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>
+image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>
+en</dc:language>
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ id="base"
+ inkscape:current-layer="svg2"
+ inkscape:cx="0.22510228"
+ inkscape:cy="5.0923615"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="748"
+ inkscape:window-maximized="1"
+ inkscape:window-width="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:zoom="22.627417"
+ objecttolerance="10.0"
+ pagecolor="#ffffff"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+ <g
+ inkscape:transform-center-y="-2"
+ transform="matrix(0.02,0,0,0.02,-0.35950751,-1.8806165)"
+ id="g3015">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:54.68502426;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3017"
+ d="m 68.690452,226.50324 451.760378,0" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:54.68502426;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ sodipodi:nodetypes="cc"
+ id="path3019"
+ d="m 68.690452,360.07403 451.760378,0" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#000000;stroke-width:54.68502426;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ sodipodi:nodetypes="cc"
+ id="path3021"
+ d="M 132.37824,443.67778 452.51615,144.52914" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="6.7570868"
+ height="10.629921"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="guidepost.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective3607"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3664"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3691"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#4500ff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.55294118"
+ inkscape:pageshadow="2"
+ inkscape:zoom="45.254834"
+ inkscape:cx="8.238623"
+ inkscape:cy="2.7539586"
+ inkscape:document-units="mm"
+ inkscape:current-layer="main"
+ showgrid="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-grids="true"
+ inkscape:window-width="1272"
+ inkscape:window-height="1004"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ borderlayer="false"
+ showborder="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2816"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="mm"
+ spacingx="0.0625mm"
+ spacingy="0.0625mm"
+ dotted="false" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-1.8451322,-118.92218)">
+ <g
+ transform="translate(-4.96063e-8,2.0953825e-6)"
+ id="border"
+ style="fill:none;stroke:#ffffff;stroke-width:0.99921262;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ inkscape:label="#g3678-5">
+ <path
+ sodipodi:nodetypes="cc"
+ transform="translate(1.9018252,116.03155)"
+ id="path3652-3"
+ d="m 3.3218504,12.634726 0,-8.8582678"
+ style="fill:none;stroke:#ffffff;stroke-width:1.77165353;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path3654-9"
+ d="m 3.0091087,120.25092 3.9862204,0 1.1072835,0.88583 -1.1072835,0.88583 -3.9862204,0 0,-1.77166 z"
+ style="fill:none;stroke:#ffffff;stroke-width:0.99921262;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path3654-8-8"
+ d="m 7.4382425,122.46548 -3.9862204,0 -1.1072835,0.88583 1.1072835,0.88583 3.9862204,0 0,-1.77166 z"
+ style="fill:none;stroke:#ffffff;stroke-width:0.99921262;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <g
+ id="main"
+ transform="translate(2.2834645e-8,2.0953825e-6)"
+ inkscape:label="#g3678">
+ <path
+ sodipodi:nodetypes="cc"
+ transform="translate(1.9018252,116.03155)"
+ id="path3652"
+ d="m 3.3218504,12.634726 0,-8.8582678"
+ style="fill:none;stroke:#643721;stroke-width:0.99921262000000000;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path3654"
+ d="m 3.0091087,120.25092 3.9862204,0 1.1072835,0.88583 -1.1072835,0.88583 -3.9862204,0 0,-1.77166 z"
+ style="fill:#643721;fill-opacity:1;stroke:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path3654-8"
+ d="m 7.4382425,122.46548 -3.9862204,0 -1.1072835,0.88583 1.1072835,0.88583 3.9862204,0 0,-1.77166 z"
+ style="fill:#643721;fill-opacity:1;stroke:none" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="logo.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective2947"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3910"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4140"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#7470d0"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7.9195959"
+ inkscape:cx="174.39239"
+ inkscape:cy="40.188039"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:snap-grids="true"
+ inkscape:snap-bbox="false"
+ inkscape:snap-nodes="true"
+ inkscape:window-width="1272"
+ inkscape:window-height="1004"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2816"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="mm"
+ spacingx="1mm"
+ spacingy="1mm" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ transform="matrix(0.09452091,0,0,0.09452091,156.91602,888.30189)"
+ id="g2916-1"
+ style="stroke:#ffffff;stroke-width:31.73900795;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ <path
+ id="path2425-4"
+ d="m -106.87586,794.79497 c 0,0 1.91986,55.86792 1.91986,55.86792"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path2423-5"
+ d="m -176.18279,810.92179 c 0.40525,-0.39416 32.8296,-21.31044 67.0031,-25.9181 57.019835,-7.10348 64.50729,3.07177 65.083247,8.44738 3.83972,26.68605 -63.739347,29.75783 -63.931327,29.75783"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2421-8"
+ d="m -115.51523,867.04697 c 0,0 8.70465,27.64598 8.70465,27.64598"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2419-0"
+ d="m -113.21139,880.35544 c 0,0 -66.30044,13.05505 -63.73935,48.12705 0.76795,17.14818 34.81474,9.98327 44.28349,7.16491 20.99174,-5.11835 41.983492,-9.46875 46.844575,4.35424 5.375609,15.10162 -33.532275,23.549 -33.532275,23.80627 0,0 -63.9966,11.26189 -63.9966,11.26189"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2416-0"
+ d="m -174.13622,860.3889 c 0,0 22.78489,16.12682 22.78489,16.12682 0,0 14.07641,-27.8994 14.07641,-27.8994"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2414-8"
+ d="m -192.30962,950.23834 c 0,0 13.31231,38.91171 13.31231,38.91171"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2412-2"
+ d="m -90.941022,867.55766 c 5.37561,13.24703 12.671076,42.04492 14.206965,58.36373"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2410-2"
+ d="m -44.096443,864.29389 c 0,0 -35.517406,38.20521 -35.517406,38.01323 0,-0.19199 41.468972,16.51079 41.468972,16.51079"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2408-3"
+ d="m 89.783052,878.30887 c 0,0 43.772808,-8.19012 43.772808,-8.19012"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2406-0"
+ d="m 93.365512,903.14034 c 0,0 46.587318,-6.40081 46.587318,-6.40081"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2404-3"
+ d="m 95.669344,925.15345 c 0,0 47.101836,-6.9115 47.101836,-6.9115"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2402-1"
+ d="m 28.474255,834.66278 c 0,0 16.768056,58.621 16.768056,58.621 0,0 28.413922,-6.9115 28.413922,-6.9115"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path2400-5"
+ d="m -153.91242,1054.936 c 12.54436,3.8397 74.233297,51.1949 74.233297,51.1949 0.144238,-0.09 147.70249,3.8398 147.70249,3.8398 0,0 -5.118346,-110.58395 -5.118346,-110.58395 0,0 -142.069621,1.79315 -142.069621,1.79315 0,0 -68.34701,44.798 -74.74782,53.7561 z"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path2398-0"
+ d="m -113.97934,1157.8405 c 0.1806,-0.2266 35.836105,-43.0049 36.477337,-51.4523 0,0 28.797897,49.4057 34.684186,54.0133"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path2396-6"
+ d="m 27.065078,1161.4229 c 0.323587,-0.3493 39.806372,-51.195 39.806372,-51.195 0,0 43.38883,48.1232 43.38883,48.1232"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2394-1"
+ d="m -77.885975,1103.8924 c 0,0 -0.767942,-102.4552 -0.767942,-102.4552"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2392-4"
+ d="m 62.905021,999.64401 c 0,0 7.422178,-16.12682 27.899396,-18.1734 6.143552,-1.79315 32.000223,4.35041 31.742963,23.54899 0,0.2573 1.0252,13.823 -14.33368,16.1268 -8.19396,-0.7679 -15.358871,-4.8649 -14.848188,-15.8695 0,0 0.510683,-23.80629 30.207068,-20.99177 12.02984,0.51068 13.82299,2.81451 23.549,12.54436"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path2390-8"
+ d="m -196.14933,1009.6273 c 0,0 198.8974655,-49.91638 285.29115,-52.60418 0,0 -113.271721,34.94145 -148.981113,57.59578 0,0 37.245278,3.4558 49.14841,8.8314 0,0 -11.135187,8.0634 -16.126821,19.5825 0.295123,0.071 23.4222875,1.9199 28.7978936,9.5993"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2388-3"
+ d="m 21.946731,898.65938 c 0,0 -51.260256,-10.17525 -54.524016,22.65435 -1.727874,20.35051 31.1017287,19.58257 36.2853491,19.00661 12.8630609,-0.95993 39.9330839,-4.99163 41.0849979,-24.5742 1.535889,-16.5108 -36.6693204,-12.67108 -36.6693204,-12.67108"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2418-9"
+ d="m -2.6274736,808.80995 c 0,0 -19.1985964,-7.87143 -34.1735034,0.76794 0,0 -14.398947,11.71115 -12.863061,22.27038 1.535889,11.32717 21.694416,14.78291 31.4857,13.82299 19.9665419,-0.38398 24.7661909,-6.52753 28.605911,-11.13519 4.799649,-10.36724 1.727875,-13.05505 -1.3439028,-16.12682 -4.799649,-6.52753 -23.4222882,-10.17526 -23.4222882,-10.17526"
+ style="fill:none;stroke:#ffffff;stroke-width:31.73900795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ id="path2429-4"
+ d="m 57.578694,247.35558 c 0,3.41532 -2.771858,6.18718 -6.187184,6.18718 -3.415326,0 -6.187184,-2.77186 -6.187184,-6.18718 0,-3.41533 2.771858,-6.18719 6.187184,-6.18719 3.415326,0 6.187184,2.77186 6.187184,6.18719 z"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:35.34571457;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ transform="matrix(0.8979592,0,0,0.8979592,-153.12491,819.07358)" />
+ </g>
+ <g
+ transform="matrix(0.09452091,0,0,0.09452091,156.91602,888.30189)"
+ id="g2916">
+ <path
+ id="path2425"
+ d="m -106.87586,794.79497 c 0,0 1.91986,55.86792 1.91986,55.86792"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path2423"
+ d="m -176.18279,810.92179 c 0.40525,-0.39416 32.8296,-21.31044 67.0031,-25.9181 57.019835,-7.10348 64.50729,3.07177 65.083247,8.44738 3.83972,26.68605 -63.739347,29.75783 -63.931327,29.75783"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2421"
+ d="m -115.51523,867.04697 c 0,0 8.70465,27.64598 8.70465,27.64598"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2419"
+ d="m -113.21139,880.35544 c 0,0 -66.30044,13.05505 -63.73935,48.12705 0.76795,17.14818 34.81474,9.98327 44.28349,7.16491 20.99174,-5.11835 41.983492,-9.46875 46.844575,4.35424 5.375609,15.10162 -33.532275,23.549 -33.532275,23.80627 0,0 -63.9966,11.26189 -63.9966,11.26189"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2416"
+ d="m -174.13622,860.3889 c 0,0 22.78489,16.12682 22.78489,16.12682 0,0 14.07641,-27.8994 14.07641,-27.8994"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2414"
+ d="m -192.30962,950.23834 c 0,0 13.31231,38.91171 13.31231,38.91171"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2412"
+ d="m -90.941022,867.55766 c 5.37561,13.24703 12.671076,42.04492 14.206965,58.36373"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2410"
+ d="m -44.096443,864.29389 c 0,0 -35.517406,38.20521 -35.517406,38.01323 0,-0.19199 41.468972,16.51079 41.468972,16.51079"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2408"
+ d="m 89.783052,878.30887 c 0,0 43.772808,-8.19012 43.772808,-8.19012"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2406"
+ d="m 93.365512,903.14034 c 0,0 46.587318,-6.40081 46.587318,-6.40081"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2404"
+ d="m 95.669344,925.15345 c 0,0 47.101836,-6.9115 47.101836,-6.9115"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2402"
+ d="m 28.474255,834.66278 c 0,0 16.768056,58.621 16.768056,58.621 0,0 28.413922,-6.9115 28.413922,-6.9115"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path2400"
+ d="m -153.91242,1054.936 c 12.54436,3.8397 74.233297,51.1949 74.233297,51.1949 0.144238,-0.09 147.70249,3.8398 147.70249,3.8398 0,0 -5.118346,-110.58395 -5.118346,-110.58395 0,0 -142.069621,1.79315 -142.069621,1.79315 0,0 -68.34701,44.798 -74.74782,53.7561 z"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path2398"
+ d="m -113.97934,1157.8405 c 0.1806,-0.2266 35.836105,-43.0049 36.477337,-51.4523 0,0 28.797897,49.4057 34.684186,54.0133"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path2396"
+ d="m 27.065078,1161.4229 c 0.323587,-0.3493 39.806372,-51.195 39.806372,-51.195 0,0 43.38883,48.1232 43.38883,48.1232"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2394"
+ d="m -77.885975,1103.8924 c 0,0 -0.767942,-102.4552 -0.767942,-102.4552"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2392"
+ d="m 62.905021,999.64401 c 0,0 7.422178,-16.12682 27.899396,-18.1734 6.143552,-1.79315 32.000223,4.35041 31.742963,23.54899 0,0.2573 1.0252,13.823 -14.33368,16.1268 -8.19396,-0.7679 -15.358871,-4.8649 -14.848188,-15.8695 0,0 0.510683,-23.80629 30.207068,-20.99177 12.02984,0.51068 13.82299,2.81451 23.549,12.54436"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="path2390"
+ d="m -196.14933,1009.6273 c 0,0 198.8974655,-49.91638 285.29115,-52.60418 0,0 -113.271721,34.94145 -148.981113,57.59578 0,0 37.245278,3.4558 49.14841,8.8314 0,0 -11.135187,8.0634 -16.126821,19.5825 0.295123,0.071 23.4222875,1.9199 28.7978936,9.5993"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2388"
+ d="m 21.946731,898.65938 c 0,0 -51.260256,-10.17525 -54.524016,22.65435 -1.727874,20.35051 31.1017287,19.58257 36.2853491,19.00661 12.8630609,-0.95993 39.9330839,-4.99163 41.0849979,-24.5742 1.535889,-16.5108 -36.6693204,-12.67108 -36.6693204,-12.67108"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2418"
+ d="m -2.6274736,808.80995 c 0,0 -19.1985964,-7.87143 -34.1735034,0.76794 0,0 -14.398947,11.71115 -12.863061,22.27038 1.535889,11.32717 21.694416,14.78291 31.4857,13.82299 19.9665419,-0.38398 24.7661909,-6.52753 28.605911,-11.13519 4.799649,-10.36724 1.727875,-13.05505 -1.3439028,-16.12682 -4.799649,-6.52753 -23.4222882,-10.17526 -23.4222882,-10.17526"
+ style="fill:none;stroke:#000000;stroke-width:11.05979252;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ id="path2429"
+ d="m 57.578694,247.35558 c 0,3.41532 -2.771858,6.18718 -6.187184,6.18718 -3.415326,0 -6.187184,-2.77186 -6.187184,-6.18718 0,-3.41533 2.771858,-6.18719 6.187184,-6.18719 3.415326,0 6.187184,2.77186 6.187184,6.18719 z"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ transform="matrix(0.8979592,0,0,0.8979592,-153.12491,819.07358)" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="190"
+ height="176.02705"
+ id="svg3617"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="memorial.svg">
+ <defs
+ id="defs3619">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective3625" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="102.49509"
+ inkscape:cy="92.041782"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1016"
+ inkscape:window-height="748"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3627"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata3622">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-192.5,-438.83513)">
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:11;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path4156"
+ sodipodi:cx="290"
+ sodipodi:cy="487.36218"
+ sodipodi:rx="50"
+ sodipodi:ry="45"
+ d="m 240.00343,487.88917 c -0.32339,-24.85111 21.79868,-45.23285 49.41103,-45.5239 27.61234,-0.29105 50.25872,19.61881 50.58211,44.46992 0.001,0.11555 0.003,0.2311 0.003,0.34665"
+ sodipodi:start="3.1298815"
+ sodipodi:end="6.2791776"
+ sodipodi:open="true"
+ transform="translate(-2.5034287,1.9730086)" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 202.5,604.86218 170,0"
+ id="path4160"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:11;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 45,53.000061 0,114.999999 100,0 0,-114.999999 -100,0 z"
+ id="path4185"
+ transform="translate(192.5,436.86212)" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="meritko.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective2947"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3910"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective4140"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#7470d0"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="15.839192"
+ inkscape:cx="192.73651"
+ inkscape:cy="-6.2998166"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:snap-grids="true"
+ inkscape:snap-bbox="false"
+ inkscape:snap-nodes="true"
+ inkscape:window-width="1272"
+ inkscape:window-height="1004"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2816"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="mm"
+ spacingx="1mm"
+ spacingy="1mm" />
+ <inkscape:grid
+ type="xygrid"
+ id="grid3972"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="mm"
+ spacingx="3.19mm"
+ spacingy="3.19mm"
+ color="#00ff00"
+ opacity="0.45490196"
+ empcolor="#00ff00"
+ empopacity="0.66666667"
+ dotted="false" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="116.92913,3.5433071"
+ id="guide3622" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="129.4258,2.0203051"
+ id="guide2833" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:0.88582676999999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:1;fill-opacity:1"
+ d="m 113.0315,1052.3622 0,-3.5433"
+ id="path3609" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:0.88582676999999999;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:1;fill-opacity:1"
+ d="m 226.06299,1052.3622 -113.03149,0"
+ id="path2848" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 124.37503,1052.4253 -0.0404,-2.0834"
+ id="path3636"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 135.67611,1052.4253 -0.0383,-2.0834"
+ id="path3638"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 146.94094,1052.3622 0,-2.0203"
+ id="path3640"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 158.24409,1052.3622 0,-2.0203"
+ id="path3642"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 169.51622,1052.3937 0.031,-3.5748"
+ id="path3644" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 180.89286,1052.3399 -0.0425,-1.998"
+ id="path3648"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 192.14286,1052.4068 0.0107,-2.0649"
+ id="path3650"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 203.45669,1052.3622 0,-2.0203"
+ id="path3656"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 214.84375,1052.3399 -0.0839,-1.998"
+ id="path3658"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:1"
+ d="m 226.06299,1052.3622 0,-3.5433"
+ id="path3660" />
+ <g
+ style="font-size:8px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L Medium;opacity:1"
+ id="text3662">
+ <path
+ d="m 162.01794,1059.8457 c 0.016,-0.01 0.032,-0.01 0.04,-0.01 0.064,0.01 0.08,0.064 0.08,0.304 l 0,3.544 c 0,0.232 -0.152,0.36 -0.456,0.376 l -0.576,0.024 0,0.296 c 0.488,-0.01 0.976,-0.016 1.12,-0.016 0.24,-0.01 0.408,-0.01 0.504,-0.01 0.096,0 0.288,0 0.552,0.01 0.104,0 0.472,0.01 0.864,0.016 l 0,-0.296 -0.496,-0.024 c -0.304,-0.016 -0.456,-0.136 -0.456,-0.376 l 0,-2.576 c 0,-0.632 0.016,-1.136 0.08,-2.032 l -0.096,-0.08 c -0.976,0.384 -1.896,0.696 -2.392,0.808 0,0.2 0.032,0.352 0.112,0.56 l 1.12,-0.52"
+ id="path3667"
+ style="opacity:1" />
+ <path
+ d="m 168.21794,1058.5977 -0.576,0.176 c -0.232,0.072 -0.504,0.12 -0.968,0.168 l 0,0.288 0.424,0.024 c 0.176,0.01 0.208,0.088 0.208,0.56 l 0,3.744 c 0,0.432 -0.04,0.496 -0.288,0.512 l -0.28,0.016 0,0.296 c 0,0 1.032,-0.024 1.032,-0.024 0.16,0 0.544,0.01 1.08,0.024 l 0,-0.296 -0.28,-0.016 c -0.248,-0.016 -0.288,-0.08 -0.288,-0.512 l 0,-1.008 0.08,0 0.848,0.96 c 0.12,0.136 0.576,0.696 0.696,0.848 l 1.376,0 0,-0.272 c -0.168,-0.016 -0.296,-0.096 -0.44,-0.256 l -1.568,-1.76 0.064,-0.064 c 0.24,-0.24 0.472,-0.456 0.56,-0.512 l 0.496,-0.36 c 0.128,-0.096 0.264,-0.144 0.384,-0.144 l 0.248,0 0,-0.304 -1.04,0 -0.856,0.904 c -0.184,0.192 -0.4,0.392 -0.848,0.776 l 0,-3.72 -0.064,-0.048"
+ id="path3669"
+ style="opacity:1" />
+ <path
+ d="m 171.58856,1061.2217 0.36,0.024 c 0.184,0.016 0.208,0.088 0.208,0.56 l 0,1.752 c 0,0.432 -0.04,0.496 -0.288,0.512 l -0.28,0.016 0,0.296 c 0,0 1.032,-0.024 1.032,-0.024 0.184,0 0.528,0.01 1.016,0.024 l 0,-0.296 -0.216,-0.016 c -0.256,-0.016 -0.288,-0.08 -0.288,-0.512 l 0,-1.736 c 0,-0.264 0.328,-0.544 0.648,-0.544 0.464,0 0.688,0.304 0.688,0.928 l 0,1.352 c 0,0.432 -0.032,0.496 -0.288,0.512 l -0.248,0.016 0,0.296 c 0.832,-0.024 0.832,-0.024 1.032,-0.024 0.192,0 0.192,0 1.048,0.024 l 0,-0.296 -0.28,-0.016 c -0.248,-0.016 -0.288,-0.08 -0.288,-0.512 l 0,-1.736 c 0,-0.264 0.328,-0.544 0.648,-0.544 0.464,0 0.688,0.304 0.688,0.928 l 0,2.176 0.744,-0.024 c 0.4,0.01 0.456,0.01 0.784,0.024 l 0,-0.296 -0.264,-0.016 c -0.248,-0.016 -0.288,-0.08 -0.288,-0.512 l 0,-1.488 c 0,-1.104 -0.288,-1.48 -1.144,-1.48 -0.264,0 -0.44,0.048 -0.576,0.152 l -0.64,0.496 c -0.184,-0.456 -0.504,-0.648 -1.056,-0.648 -0.256,0 -0.432,0.048 -0.568,0.152 l -0.64,0.496 0,-0.6 -0.048,-0.048 c -0.832,0.272 -1.056,0.32 -1.496,0.344 l 0,0.288"
+ id="path3671"
+ style="opacity:1" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="power_pole.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="89.6"
+ inkscape:cx="63.009096"
+ inkscape:cy="207.90885"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1272"
+ inkscape:window-height="1004"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2818"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#777777;stroke-width:0.10629921;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect3596"
+ width="1"
+ height="1"
+ x="60"
+ y="841.36218"
+ ry="0" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="11.898861"
+ id="svg2"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="train.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="9.9799995">
+ <defs
+ id="defs22">
+ <inkscape:perspective
+ id="perspective24"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ id="ArrowStart"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="10"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 10,0 0,5 10,10 z"
+ id="path3568" />
+ </marker>
+ <marker
+ id="ArrowEnd"
+ markerHeight="3"
+ markerUnits="strokeWidth"
+ markerWidth="4"
+ orient="auto"
+ refX="0"
+ refY="5"
+ viewBox="0 0 10 10">
+ <path
+ d="M 0,0 10,5 0,10 z"
+ id="path3565" />
+ </marker>
+ <inkscape:perspective
+ id="perspective2683"
+ inkscape:persp3d-origin="306.082 : 204.39034 : 1"
+ inkscape:vp_x="0 : 306.58551 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="612.164 : 306.58551 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5299"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>
+image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>
+en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ id="base"
+ inkscape:current-layer="wrapper"
+ inkscape:cx="0.61374335"
+ inkscape:cy="3.9329874"
+ inkscape:pageopacity="0.83137255"
+ inkscape:pageshadow="2"
+ inkscape:window-height="748"
+ inkscape:window-width="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:zoom="22.627417"
+ objecttolerance="10.0"
+ pagecolor="#7d77ff"
+ showgrid="false"
+ inkscape:window-maximized="1" />
+ <g
+ id="wrapper"
+ transform="matrix(0.02,0,0,0.02,-1.478075,-1.1469285)">
+ <path
+ d="m 322.96313,90.806136 -91.2572,0.0691 c -42.50274,-1e-5 -73.77286,34.131654 -73.77286,71.319524 l 0,263.40538 c 4e-5,36.02757 28.18623,65.57749 56.5304,70.14468 l -85.00292,127.50436 49.06673,0 60.78054,-89.11483 83.24066,0 0.069,0 0.069,0 83.24066,0 60.78054,89.11483 49.06673,0 -85.0027,-127.50436 c 28.34412,-4.56721 56.56495,-34.11709 56.56495,-70.14468 l 0,-263.40538 c -5e-5,-37.18783 -31.3047,-71.319524 -73.80741,-71.319524 l -90.56612,-0.0691 z m -36.14351,15.272884 35.72886,0 0.069,0 0.069,0 35.72887,0 c 6.46649,0 12.09391,5.4201 12.09391,11.88659 l 0,20.93974 c 0,6.4665 -5.31949,12.02481 -12.09391,12.02481 l -35.72887,0 -0.069,0 -0.069,0 -35.72886,0 c -6.77442,0 -12.09392,-5.55831 -12.09392,-12.02481 l 0,-20.93974 c 0,-6.46649 5.62741,-11.88658 12.09392,-11.88659 z m -52.66035,59.05284 88.38921,0 0.138,0 88.38921,0 c 24.01838,1e-5 36.7655,17.1963 36.76549,36.9037 l 0,47.40813 c 0.187,22.78669 -16.44223,36.76549 -36.76549,36.76549 l -88.38921,0 -0.069,0 -0.069,0 -88.38921,0 c -20.32327,0 -36.95271,-13.9788 -36.76549,-36.76549 l 0,-47.40813 c -1e-5,-19.7074 12.74709,-36.9037 36.76549,-36.9037 z m -4.00825,227.50374 c 17.866,-3e-5 32.37713,14.47658 32.37713,32.34257 0,17.86471 -14.51113,32.34258 -32.37713,32.34258 -17.86603,-3e-5 -32.34257,-14.47782 -32.34257,-32.34258 -5e-5,-17.86603 14.47658,-32.34257 32.34257,-32.34257 z m 184.17299,0 c 17.86597,-2e-5 32.34257,14.47657 32.34257,32.34257 -2e-5,17.86474 -14.47656,32.34259 -32.34257,32.34258 -17.86601,-3e-5 -32.34258,-14.47783 -32.34257,-32.34258 -2e-5,-17.86601 14.47659,-32.34257 32.34257,-32.34257 z"
+ id="path3578-9"
+ style="fill:none;stroke:#ffffff;stroke-width:62.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <g
+ id="g3574"
+ transform="matrix(1.2317123,0,0,1.2317123,-54.843627,-53.570605)"
+ style="fill:#000000;fill-opacity:1">
+ <g
+ id="g3576"
+ style="fill:#000000;fill-opacity:1">
+ <path
+ d="M 295.5625,77.1875 213.03125,77.25 c -38.43866,-1.1e-5 -66.71875,30.86801 -66.71875,64.5 l 0,238.21875 c 3e-5,32.58264 25.49108,59.30702 51.125,63.4375 l -76.875,115.3125 44.375,0 54.96875,-80.59375 75.28125,0 0.0625,0 0.0625,0 75.28125,0 54.96875,80.59375 44.375,0 -76.875,-115.3125 c 25.63388,-4.1305 51.15625,-30.85484 51.15625,-63.4375 l 0,-238.21875 c -4e-5,-33.63196 -28.31137,-64.5 -66.75,-64.5 L 295.5625,77.1875 z M 262.875,91 l 32.3125,0 0.0625,0 0.0625,0 32.3125,0 c 5.84817,0 10.9375,4.90183 10.9375,10.75 l 0,18.9375 c 0,5.84817 -4.81084,10.875 -10.9375,10.875 l -32.3125,0 -0.0625,0 -0.0625,0 -32.3125,0 c -6.12665,0 -10.9375,-5.02683 -10.9375,-10.875 l 0,-18.9375 c 0,-5.84817 5.08932,-10.749997 10.9375,-10.75 z m -47.625,53.40625 79.9375,0 0.125,0 79.9375,0 c 21.72176,1e-5 33.25001,15.552 33.25,33.375 l 0,42.875 c 0.16931,20.60784 -14.87004,33.25 -33.25,33.25 l -79.9375,0 -0.0625,0 -0.0625,0 -79.9375,0 c -18.37997,0 -33.41932,-12.64216 -33.25,-33.25 l 0,-42.875 c -1e-5,-17.823 11.52822,-33.375 33.25,-33.375 z m -3.625,205.75 c 16.15766,-3e-5 29.28125,13.09234 29.28125,29.25 0,16.1565 -13.12359,29.25 -29.28125,29.25 -16.15769,-2e-5 -29.25,-13.09346 -29.25,-29.25 -4e-5,-16.1577 13.09234,-29.25 29.25,-29.25 z m 166.5625,0 c 16.15764,-2e-5 29.25,13.09233 29.25,29.25 -2e-5,16.15652 -13.09233,29.25001 -29.25,29.25 -16.15768,-2e-5 -29.25001,-13.09347 -29.25,-29.25 -2e-5,-16.15768 13.09235,-29.25 29.25,-29.25 z"
+ id="path3578"
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ transform="matrix(0.8977168,0,0,0.8977168,40.710583,46.777129)" />
+ </g>
+ </g>
+ </g>
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="11.339"
+ id="svg2"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="view_point.svg"
+ sodipodi:version="0.32"
+ version="1.0"
+ width="11.339">
+ <g
+ id="wrapper-4"
+ transform="matrix(0.01448815,0,0,0.01448815,1.2157583,1.502448)"
+ style="fill:none;stroke:#ffffff;stroke-width:43.13870239;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ <path
+ d="m 264.50523,235.49926 c -0.16658,0.78886 -42.10316,-33.79044 -85.91597,-79.79102 -9.98712,-10.48582 -67.67963,-60.980315 -14.46661,-91.66394 54.45289,-31.398539 69.60929,44.6705 73.90403,61.46637 15.13669,59.19661 26.64513,109.19973 26.47855,109.98859 z"
+ id="use6027-4"
+ sodipodi:nodetypes="cszss"
+ style="fill:none;stroke:#ffffff;stroke-width:43.13870239;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 236.59244,283.85543 c 0.59989,0.53869 -50.31495,19.56719 -112.05903,34.50991 -14.07455,3.40619 -86.650321,28.12212 -86.616605,-33.30353 0.03451,-62.85685 73.490425,-37.94816 90.183445,-33.26958 58.83412,16.48955 107.89231,31.52451 108.49219,32.0632 z"
+ id="use6029-3"
+ sodipodi:nodetypes="cszss"
+ style="fill:none;stroke:#ffffff;stroke-width:43.13870239;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 264.09276,334.21809 c 0.76647,-0.25017 -8.21179,53.35762 -26.14305,114.30092 -4.08743,13.89202 -18.97069,89.10244 -72.15001,58.36042 -54.41837,-31.45831 3.88114,-82.61866 16.27942,-94.73595 43.69743,-42.70706 81.24718,-77.67522 82.01364,-77.92539 z"
+ id="use6031-0"
+ sodipodi:nodetypes="cszss"
+ style="fill:none;stroke:#ffffff;stroke-width:43.13870239;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 321.10177,334.86324 c 0.16658,-0.78887 42.10315,33.79043 85.91597,79.791 9.98713,10.48583 67.67963,60.98032 14.4666,91.66395 C 367.03146,537.71673 351.87505,461.6477 347.58031,444.85183 332.44363,385.65521 320.93519,335.6521 321.10177,334.86324 z"
+ id="use6033-7"
+ sodipodi:nodetypes="cszss"
+ style="fill:none;stroke:#ffffff;stroke-width:43.13870239;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 348.14597,285.63847 c -0.5999,-0.5387 50.31494,-19.56718 112.05901,-34.50991 14.07456,-3.40619 86.65033,-28.12212 86.61661,33.30353 -0.0345,62.85685 -73.49042,37.94817 -90.18344,33.26959 -58.83412,-16.48956 -107.8923,-31.52452 -108.49218,-32.06321 z"
+ id="use6035-8"
+ sodipodi:nodetypes="cszss"
+ style="fill:none;stroke:#ffffff;stroke-width:43.13870239;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 323.1887,233.94211 c -0.76647,0.25017 8.2118,-53.35763 26.14307,-114.30092 4.08742,-13.89201 18.9707,-89.10244 72.15,-58.360413 54.41838,31.458315 -3.88114,82.618663 -16.27943,94.735943 -43.69743,42.70707 -81.24718,77.67522 -82.01364,77.92539 z"
+ id="use6039-6"
+ sodipodi:nodetypes="cszss"
+ style="fill:none;stroke:#ffffff;stroke-width:43.13870239;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <metadata
+ id="metadata2975">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>
+image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>
+en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ id="base"
+ inkscape:current-layer="svg2"
+ inkscape:cx="6.3337443"
+ inkscape:cy="3.8533714"
+ inkscape:pageopacity="0.28235294"
+ inkscape:pageshadow="2"
+ inkscape:window-height="748"
+ inkscape:window-maximized="1"
+ inkscape:window-width="1016"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:zoom="83.42069"
+ objecttolerance="10.0"
+ pagecolor="#4841ff"
+ showgrid="false"
+ inkscape:snap-bbox="false"
+ inkscape:snap-bbox-midpoints="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3630"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ id="perspective3579"
+ inkscape:persp3d-origin="290 : 193.33333 : 1"
+ inkscape:vp_x="0 : 290 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="580 : 290 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2826"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <g
+ id="wrapper"
+ transform="matrix(0.01448815,0,0,0.01448815,1.2080366,1.4684273)">
+ <path
+ d="m 264.50523,235.49926 c -0.16658,0.78886 -42.10316,-33.79044 -85.91597,-79.79102 -9.98712,-10.48582 -67.67963,-60.980315 -14.46661,-91.66394 54.45289,-31.398539 69.60929,44.6705 73.90403,61.46637 15.13669,59.19661 26.64513,109.19973 26.47855,109.98859 z"
+ id="use6027"
+ sodipodi:nodetypes="cszss"
+ style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:13.05850983;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 236.59244,283.85543 c 0.59989,0.53869 -50.31495,19.56719 -112.05903,34.50991 -14.07455,3.40619 -86.650321,28.12212 -86.616605,-33.30353 0.03451,-62.85685 73.490425,-37.94816 90.183445,-33.26958 58.83412,16.48955 107.89231,31.52451 108.49219,32.0632 z"
+ id="use6029"
+ sodipodi:nodetypes="cszss"
+ style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:13.05850983;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 264.09276,334.21809 c 0.76647,-0.25017 -8.21179,53.35762 -26.14305,114.30092 -4.08743,13.89202 -18.97069,89.10244 -72.15001,58.36042 -54.41837,-31.45831 3.88114,-82.61866 16.27942,-94.73595 43.69743,-42.70706 81.24718,-77.67522 82.01364,-77.92539 z"
+ id="use6031"
+ sodipodi:nodetypes="cszss"
+ style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:13.05850983;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 321.10177,334.86324 c 0.16658,-0.78887 42.10315,33.79043 85.91597,79.791 9.98713,10.48583 67.67963,60.98032 14.4666,91.66395 C 367.03146,537.71673 351.87505,461.6477 347.58031,444.85183 332.44363,385.65521 320.93519,335.6521 321.10177,334.86324 z"
+ id="use6033"
+ sodipodi:nodetypes="cszss"
+ style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:13.05850983;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 348.14597,285.63847 c -0.5999,-0.5387 50.31494,-19.56718 112.05901,-34.50991 14.07456,-3.40619 86.65033,-28.12212 86.61661,33.30353 -0.0345,62.85685 -73.49042,37.94817 -90.18344,33.26959 -58.83412,-16.48956 -107.8923,-31.52452 -108.49218,-32.06321 z"
+ id="use6035"
+ sodipodi:nodetypes="cszss"
+ style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:13.02788831;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 323.1887,233.94211 c -0.76647,0.25017 8.2118,-53.35763 26.14307,-114.30092 4.08742,-13.89201 18.9707,-89.10244 72.15,-58.360413 54.41838,31.458315 -3.88114,82.618663 -16.27943,94.735943 -43.69743,42.70707 -81.24718,77.67522 -82.01364,77.92539 z"
+ id="use6039"
+ sodipodi:nodetypes="cszss"
+ style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:13.05850983;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <!--
+ Generated using the Perl SVG Module V2.50
+ by Ronan Oger
+ Info: http://www.roitsystems.com/
+ -->
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="51"
+ height="40"
+ id="svg3656"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="wetland.svg">
+ <defs
+ id="defs3658">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective3664" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="89.428571"
+ inkscape:cy="-29.500003"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1016"
+ inkscape:window-height="748"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3666"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata3661">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-159.5,-462.86218)">
+ <path
+ style="fill:none;stroke:#3030a5;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+ d="m 159.5,472.86218 30,0"
+ id="path3668" />
+ <path
+ style="fill:none;stroke:#3030a5;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+ d="m 179.5,492.86218 30,0"
+ id="path3670" />
+ </g>
+</svg>
--- /dev/null
+/*
+ * Hic Est Leo -- Main Program
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/conf.h>
+#include <ucw/opt.h>
+
+#include <stdio.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "svg.h"
+#include "style.h"
+#include "css.h"
+#include "sym.h"
+#include "map.h"
+
+#undef ROTATE
+
+uns debug_dump_source, debug_dump_after_proj, debug_dump_after_scaling;
+uns debug_dump_multipolygons, debug_dump_css, debug_dump_styling, debug_dump_symbols;
+
+static struct cf_section debug_cf = {
+ CF_ITEMS {
+ CF_UNS("DumpSource", &debug_dump_source),
+ CF_UNS("DumpAfterProj", &debug_dump_after_proj),
+ CF_UNS("DumpAfterScaling", &debug_dump_after_scaling),
+ CF_UNS("DumpMultipolygons", &debug_dump_multipolygons),
+ CF_UNS("DumpCSS", &debug_dump_css),
+ CF_UNS("DumpStyling", &debug_dump_styling),
+ CF_UNS("DumpSymbols", &debug_dump_symbols),
+ CF_END
+ }
+};
+
+static const struct opt_section options = {
+ OPT_ITEMS {
+ OPT_HELP("Hic Est Leo -- Experimental Map Renderer"),
+ OPT_HELP(""),
+ OPT_HELP("Options:"),
+ OPT_HELP_OPTION,
+ OPT_CONF_OPTIONS,
+ OPT_END
+ }
+};
+
+// FIXME: Make generic
+static void draw_scale(struct svg *svg)
+{
+ double dist = 1000;
+ double width = dist * map_scale;
+ double x = page_width - 10 - width;
+ double y = 50;
+
+ svg_push_element(svg, "g");
+ svg_set_attr(svg, "id", "scale");
+ svg_set_attr_format(svg, "transform", "translate(%.6g,%.6g)", x * svg->scale, y * svg->scale);
+
+ for (int outline=1; outline>=0; outline--)
+ {
+ svg_push_element(svg, "g");
+ svg_set_attr(svg, "stroke-linecap", "square");
+ if (outline)
+ {
+ svg_set_attr_dimen(svg, "stroke-width", 1.5);
+ svg_set_attr_color(svg, "stroke", 0xffffff);
+ }
+ else
+ {
+ svg_set_attr_dimen(svg, "stroke-width", 0.5);
+ svg_set_attr_color(svg, "stroke", 0);
+ }
+
+ svg_push_element(svg, "line");
+ svg_set_attr_dimen(svg, "x1", 0);
+ svg_set_attr_dimen(svg, "y1", 0);
+ svg_set_attr_dimen(svg, "x2", width);
+ svg_set_attr_dimen(svg, "y2", 0);
+ svg_pop(svg);
+
+ for (int i=0; i<=10; i++)
+ {
+ double tick;
+ switch (i)
+ {
+ case 0:
+ case 10:
+ tick = 3;
+ break;
+ case 5:
+ tick = 2;
+ break;
+ default:
+ tick = 1;
+ }
+ svg_push_element(svg, "line");
+ svg_set_attr_dimen(svg, "x1", width * i/10);
+ svg_set_attr_dimen(svg, "y1", 0);
+ svg_set_attr_dimen(svg, "x2", width * i/10);
+ svg_set_attr_dimen(svg, "y1", -tick);
+ svg_pop(svg);
+ }
+
+ svg_pop(svg);
+ }
+
+ scale_text(svg, 0, 5, osm_val_encode("0"));
+ scale_text(svg, width, 5, osm_val_encode("1 km"));
+ svg_pop(svg);
+}
+
+int main(int argc UNUSED, char **argv)
+{
+ cf_def_file = "map.cf";
+ cf_declare_section("Debug", &debug_cf, 0);
+ opt_parse(&options, argv+1);
+
+ osm_init();
+ styles_init();
+
+ msg(L_INFO, "Parsing OSM");
+ osm_xml_parse(map_xml_input);
+ if (debug_dump_source)
+ {
+ puts("=== Source data ===");
+ osm_dump();
+ }
+ osm_make_multipolygons();
+
+ msg(L_INFO, "Projecting");
+ osm_project(map_projection);
+ if (debug_dump_after_proj)
+ {
+ puts("=== Map after projection ===");
+ osm_dump();
+ }
+
+ map_set_scale();
+ if (debug_dump_after_scaling)
+ {
+ puts("=== Map after scaling ===");
+ osm_dump();
+ }
+
+ struct css_sheet *ss = css_load(map_style_sheet);
+ if (debug_dump_css)
+ {
+ puts("=== Stylesheet ===");
+ css_dump(ss);
+ }
+
+ struct svg *svg = svg_open(map_svg_output);
+#ifndef ROTATE
+ svg_set_attr_dimen(svg, "width", page_width);
+ svg_set_attr_dimen(svg, "height", page_height);
+#else
+ svg_set_attr_dimen(svg, "width", page_height);
+ svg_set_attr_dimen(svg, "height", page_width);
+#endif
+
+ struct style_results r;
+ style_init(&r);
+ sym_init();
+
+ msg(L_INFO, "Applying stylesheet");
+ for (uns i = OSM_TYPE_NODE; i <= OSM_TYPE_MULTIPOLYGON; i++)
+ CLIST_FOR_EACH(struct osm_object *, o, osm_obj_list[i])
+ {
+ if (debug_dump_styling)
+ {
+ puts("===============================");
+ osm_obj_dump(o);
+ }
+ if (!map_object_visible_p(o))
+ {
+ if (debug_dump_styling)
+ printf("--> invisible\n");
+ continue;
+ }
+ style_begin(&r, o);
+ css_apply(ss, &r);
+ if (debug_dump_styling)
+ style_dump(&r);
+ sym_from_style(o, &r, svg);
+ style_end(&r);
+ }
+
+ if (map_clip)
+ {
+ svg_push_element(svg, "defs");
+ svg_push_element(svg, "clipPath");
+ svg_set_attr(svg, "id", "boundary");
+ svg_push_path(svg);
+ svg_path_move_to(svg, page_offset_x, page_offset_y);
+ svg_path_line_to_rel(svg, page_map_width, 0);
+ svg_path_line_to_rel(svg, 0, page_map_height);
+ svg_path_line_to_rel(svg, -page_map_width, 0);
+ svg_path_close(svg);
+ svg_path_end(svg);
+ svg_pop(svg);
+ svg_pop(svg);
+ svg_pop(svg);
+
+ svg_push_element(svg, "g");
+ svg_set_attr_format(svg, "clip-path", "url(#boundary)");
+#ifdef ROTATE
+ svg_set_attr_format(svg, "transform", "translate(%.6g,0) rotate(90)", page_height * svg->scale);
+#endif
+ }
+
+ // FIXME: Replace by generic logo drawing facility
+ struct svg_icon *logo = svg_icon_load(svg, "../logo/kocka-s-okrajem.svg");
+
+ sym_draw_all(svg);
+
+ // Draw logo
+ double logo_width = 36.12;
+ double logo_height = 36.12 / logo->width * logo->height;
+ struct svg_icon_request sir = {
+ .icon = logo,
+ .x = page_width - 12 - logo_width / 2,
+ .y = 10 + logo_height / 2,
+ .width = logo_width,
+ .height = logo_height,
+ };
+ svg_icon_put(svg, &sir);
+
+ draw_scale(svg);
+
+ if (map_clip)
+ svg_pop(svg);
+
+ if (map_draw_border)
+ {
+ svg_push_element(svg, "rect");
+ svg_set_attr_dimen(svg, "x", page_offset_x);
+ svg_set_attr_dimen(svg, "y", page_offset_y);
+ svg_set_attr_dimen(svg, "width", page_map_width);
+ svg_set_attr_dimen(svg, "height", page_map_height);
+ svg_set_attr(svg, "fill", "none");
+ svg_set_attr(svg, "stroke", "blue");
+ svg_set_attr_dimen(svg, "stroke-width", 0.2);
+ svg_pop(svg);
+ }
+
+ svg_close(svg);
+
+ msg(L_INFO, "Finished");
+ return 0;
+}
--- /dev/null
+/*
+ * Hic Est Leo
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/clists.h>
+
+typedef u32 color_t; // 0x00RRGGBB
+
+#define COLOR_NONE 0xffffffff
+
+/* Debug options */
+
+extern uns debug_dump_source, debug_dump_after_proj, debug_dump_after_scaling;
+extern uns debug_dump_multipolygons, debug_dump_css, debug_dump_styling, debug_dump_symbols;
+
+/* xml.c */
+
+void osm_xml_parse(const char *name);
--- /dev/null
+/*
+ * Hic Est Leo -- Global Map Operations
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/conf.h>
+#include <ucw/gary.h>
+#include <ucw/mempool.h>
+
+#include <stdio.h>
+#include <math.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "map.h"
+
+double map_min_x, map_min_y;
+double map_max_x, map_max_y;
+double page_width, page_height;
+uns map_clip, map_draw_border;
+char *map_xml_input;
+char *map_projection;
+char *map_style_sheet;
+char *map_svg_output;
+
+static struct cf_section map_cf = {
+ CF_ITEMS {
+ CF_DOUBLE("MinX", &map_min_x),
+ CF_DOUBLE("MinY", &map_min_y),
+ CF_DOUBLE("MaxX", &map_max_x),
+ CF_DOUBLE("MaxY", &map_max_y),
+ CF_DOUBLE("PageWidth", &page_width),
+ CF_DOUBLE("PageHeight", &page_height),
+ CF_UNS("Clip", &map_clip),
+ CF_UNS("DrawBorder", &map_draw_border),
+ CF_STRING("XMLInput", &map_xml_input),
+ CF_STRING("Projection", &map_projection),
+ CF_STRING("StyleSheet", &map_style_sheet),
+ CF_STRING("SVGOutput", &map_svg_output),
+ CF_END
+ }
+};
+
+static void CONSTRUCTOR map_preinit(void)
+{
+ cf_declare_section("Map", &map_cf, 0);
+}
+
+// Calculated
+double map_scale;
+double page_offset_x, page_offset_y;
+double page_map_width, page_map_height;
+
+void map_set_scale(void)
+{
+ double x_range = map_max_x - map_min_x;
+ double y_range = map_max_y - map_min_y;
+ double x_scale = page_width / x_range;
+ double y_scale = page_height / y_range;
+ map_scale = MIN(x_scale, y_scale);
+ page_map_width = x_range * map_scale;
+ page_map_height = y_range * map_scale;
+ page_offset_x = (page_width - page_map_width) / 2;
+ page_offset_y = (page_height - page_map_height) / 2;
+
+ msg(L_INFO, "Setting scale %.3g (orig window [%.6g,%.6g], page window [%.6g,%.6g]+[%.6g,%.6g] on [%.6g,%.6g])",
+ map_scale,
+ x_range, y_range,
+ page_map_width, page_map_height,
+ page_offset_x, page_offset_y,
+ page_width, page_height);
+
+ double pmin_x = INFINITY, pmax_x = -INFINITY;
+ double pmin_y = INFINITY, pmax_y = -INFINITY;
+ double rmin_x = INFINITY, rmax_x = -INFINITY;
+ double rmin_y = INFINITY, rmax_y = -INFINITY;
+ CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
+ {
+ pmin_x = MIN(pmin_x, n->x);
+ pmax_x = MAX(pmax_x, n->x);
+ pmin_y = MIN(pmin_y, n->y);
+ pmax_y = MAX(pmax_y, n->y);
+ n->x = (n->x - map_min_x) * map_scale + page_offset_x;
+ n->y = page_height - page_offset_y - (n->y - map_min_y) * map_scale;
+ rmin_x = MIN(rmin_x, n->x);
+ rmax_x = MAX(rmax_x, n->x);
+ rmin_y = MIN(rmin_y, n->y);
+ rmax_y = MAX(rmax_y, n->y);
+ }
+ msg(L_INFO, "Bounds before scaling: [%.10g,%.10g] x [%.10g,%.10g]", pmin_x, pmax_x, pmin_y, pmax_y);
+ msg(L_INFO, "Bounds after scaling: [%.10g,%.10g] x [%.10g,%.10g]", rmin_x, rmax_x, rmin_y, rmax_y);
+}
+
+bool map_object_visible_p(struct osm_object *o)
+{
+ double margin = 10;
+
+ switch (o->type)
+ {
+ case OSM_TYPE_NODE:
+ {
+ struct osm_node *n = (struct osm_node *) o;
+ return (n->x >= page_offset_x - margin && n->x <= page_offset_x + page_map_width + margin &&
+ n->y >= page_offset_y - margin && n->y <= page_offset_y + page_map_height + margin);
+ }
+ case OSM_TYPE_WAY:
+ {
+ struct osm_way *w = (struct osm_way *) o;
+ bool ok = 0;
+ OSM_FOR_EACH_BEGIN(struct osm_object *, n, w->nodes)
+ {
+ ok |= map_object_visible_p(n);
+ }
+ OSM_FOR_EACH_END;
+ return ok;
+ }
+ case OSM_TYPE_RELATION:
+ {
+ struct osm_relation *r = (struct osm_relation *) o;
+ bool ok = 0;
+ OSM_FOR_EACH_BEGIN(struct osm_object *, n, r->members)
+ {
+ ok |= map_object_visible_p(n);
+ }
+ OSM_FOR_EACH_END;
+ return ok;
+ }
+ case OSM_TYPE_MULTIPOLYGON:
+ return map_object_visible_p(&((struct osm_multipolygon *) o)->rel->o);
+ default:
+ ASSERT(0);
+ }
+}
--- /dev/null
+Map {
+ # Source file with XML map of OSM data
+ XMLInput dump.osm
+
+ # Projection of our map
+ Projection "+proj=utm +zone=33 +ellps=WGS84"
+
+ # Which part of the map should drawn (in projected coordinates)
+ MinX 464737
+ MaxX 471140
+ MinY 5552849
+ MaxY 5557376
+
+ # Draw on A3 paper
+ PageWidth 420
+ PageHeight 297
+
+ # Draw on A4 paper
+ # PageWidth 297
+ # PageHeight 210
+
+ # Clip output to the requested rectangle
+ Clip 1
+
+ # Draw blue border around the requested rectangle
+ DrawBorder 0
+
+ # MapCSS stylesheet to apply
+ StyleSheet poskole.css
+
+ # Write SVG output here
+ SVGOutput output.svg
+}
+
+Debug {
+ # Dump map data exactly as parsed
+ DumpSource 0
+
+ # Dump map data after projection to Map.Projection
+ DumpAfterProj 0
+
+ # Dump map data after conversion to on-paper coordinates
+ DumpAfterScaling 0
+
+ # Dump intermediate representations of multipolygons
+ DumpMultipolygons 0
+
+ # Dump stylesheet as parsed
+ DumpCSS 0
+
+ # Dump styling decisions
+ DumpStyling 0
+
+ # Dump planning and drawing of symbols
+ DumpSymbols 0
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Global Map Operations
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_MAP_H
+#define _BRUM_MAP_H
+
+/* Map configuration */
+
+extern double map_min_x, map_min_y;
+extern double map_max_x, map_max_y;
+extern double page_width, page_height;
+extern uns map_clip, map_draw_border;
+extern char *map_xml_input;
+extern char *map_projection;
+extern char *map_style_sheet;
+extern char *map_svg_output;
+
+/* Calculated by map_set_scale() */
+
+extern double map_scale;
+extern double page_offset_x, page_offset_y;
+extern double page_map_width, page_map_height;
+
+void map_set_scale(void);
+bool map_object_visible_p(struct osm_object *o);
+
+#endif
--- /dev/null
+/*
+ * Hic Est Leo -- OSM Data Representation
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/gary.h>
+#include <ucw/mempool.h>
+#include <ucw/stkstring.h>
+#include <ucw/strtonum.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <proj_api.h>
+
+#include "leo.h"
+#include "osm.h"
+
+static struct mempool *osm_pool;
+
+/*** Generic objects ***/
+
+struct osm_id_to_obj {
+ osm_id_t id;
+ struct osm_object *o;
+};
+
+clist osm_obj_list[OSM_TYPE_MAX];
+
+#define P(x) [OSM_TYPE_##x] = #x,
+const char * const osm_obj_type_names[OSM_TYPE_MAX] = {
+ [OSM_TYPE_INVALID] = "Invalid",
+ [OSM_TYPE_NODE] = "Node",
+ [OSM_TYPE_WAY] = "Way",
+ [OSM_TYPE_RELATION] = "Relation",
+ [OSM_TYPE_MULTIPOLYGON] = "Multipolygon",
+};
+#undef P
+
+#define HASH_NODE struct osm_id_to_obj
+#define HASH_PREFIX(x) osm_id_hash_##x
+#define HASH_KEY_ATOMIC id
+#define HASH_ATOMIC_TYPE osm_id_t
+#define HASH_WANT_FIND
+#define HASH_WANT_LOOKUP
+#define HASH_USE_POOL osm_pool
+#define HASH_ZERO_FILL
+#define HASH_TABLE_DYNAMIC
+#include <ucw/hashtable.h>
+
+static struct osm_id_hash_table osm_id_hash[OSM_TYPE_MAX];
+
+osm_id_t osm_parse_id(const char *str)
+{
+ uintmax_t id;
+ const char *err = str_to_uintmax(&id, str, NULL, STN_WHOLE | STN_MINUS | 10);
+ if (err || id != (osm_id_t)id)
+ return OSM_INVALID_ID;
+ else
+ return id;
+}
+
+struct osm_object *osm_obj_find_by_id(enum osm_object_type type, osm_id_t id)
+{
+ ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
+ struct osm_id_to_obj *ii = osm_id_hash_find(&osm_id_hash[type], id);
+ return ii ? ii->o : NULL;
+}
+
+static void *osm_obj_new(enum osm_object_type type, osm_id_t id, uns size)
+{
+ ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
+ struct osm_id_to_obj *ii = osm_id_hash_lookup(&osm_id_hash[type], id);
+ if (ii->o)
+ die("Id %ju for type %s defined twice", (uintmax_t) id, osm_obj_type_names[type]);
+
+ struct osm_object *o = mp_alloc_zero(osm_pool, size);
+ clist_add_tail(&osm_obj_list[type], &o->n);
+ o->type = type;
+ o->id = id;
+ clist_init(&o->tags);
+ clist_init(&o->backrefs);
+ ii->o = o;
+ return o;
+}
+
+void osm_ref_add(struct osm_object *parent, clist *list, struct osm_object *son, osm_val_t role)
+{
+ ASSERT(parent != son);
+
+ struct osm_ref *ref = mp_alloc(osm_pool, sizeof(*ref));
+ ref->o = son;
+ ref->role = role;
+ clist_add_tail(list, &ref->n);
+
+ struct osm_ref *backref = mp_alloc(osm_pool, sizeof(*ref));
+ backref->o = parent;
+ backref->role = 0;
+ clist_add_tail(&son->backrefs, &backref->n);
+}
+
+void osm_obj_dump(struct osm_object *o)
+{
+ switch (o->type)
+ {
+ case OSM_TYPE_NODE:
+ osm_node_dump((struct osm_node *) o);
+ break;
+ case OSM_TYPE_WAY:
+ osm_way_dump((struct osm_way *) o);
+ break;
+ case OSM_TYPE_RELATION:
+ osm_relation_dump((struct osm_relation *) o);
+ break;
+ case OSM_TYPE_MULTIPOLYGON:
+ osm_multipolygon_dump((struct osm_multipolygon *) o);
+ break;
+ default:
+ ASSERT(0);
+ }
+}
+
+void osm_obj_warn(struct osm_object *o, const char *mesg, ...)
+{
+ va_list args;
+ va_start(args, mesg);
+ msg(L_WARN, "%s: %s", STK_OSM_NAME(o), stk_vprintf(mesg, args));
+ va_end(args);
+}
+
+/*** Tags ***/
+
+struct dict osm_key_dict, osm_value_dict;
+
+void osm_obj_add_tag_raw(struct osm_object *o, osm_key_t key, osm_val_t val)
+{
+ struct osm_tag *t = mp_alloc(osm_pool, sizeof(*t));
+ t->key = key;
+ t->val = val;
+ clist_add_tail(&o->tags, &t->n);
+}
+
+void osm_obj_add_tag(struct osm_object *o, const char *key, const char *val)
+{
+ osm_obj_add_tag_raw(o, osm_key_encode(key), osm_val_encode(val));
+}
+
+osm_val_t osm_obj_find_tag(struct osm_object *o, osm_key_t key)
+{
+ CLIST_FOR_EACH(struct osm_tag *, t, o->tags)
+ if (t->key == key)
+ return t->val;
+ return 0;
+}
+
+void osm_tag_dump(struct osm_tag *t)
+{
+ printf("\t%s = %s\n", osm_key_decode(t->key), osm_val_decode(t->val));
+}
+
+static const char * const osm_wk_keys[] = {
+ NULL,
+#define P(x,y) [KEY_##x] = y,
+#include "dict-keys.h"
+#undef P
+ NULL
+};
+
+static const char * const osm_wk_values[] = {
+ NULL,
+#define P(x,y) [VALUE_##x] = y,
+#include "dict-values.h"
+#undef P
+ NULL
+};
+
+static void osm_tag_init(void)
+{
+ dict_init(&osm_key_dict, osm_wk_keys);
+ dict_init(&osm_value_dict, osm_wk_values);
+}
+
+/*** Nodes ***/
+
+struct osm_node *osm_node_new(osm_id_t id)
+{
+ struct osm_node *n = osm_obj_new(OSM_TYPE_NODE, id, sizeof(*n));
+ return n;
+}
+
+void osm_node_dump(struct osm_node *n)
+{
+ printf("Node %ju: (%f,%f)\n", (uintmax_t) n->o.id, n->x, n->y);
+ CLIST_FOR_EACH(struct osm_tag *, t, n->o.tags)
+ osm_tag_dump(t);
+}
+
+void osm_node_dump_all(void)
+{
+ printf("Known nodes:\n");
+ CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
+ osm_node_dump(n);
+ putchar('\n');
+}
+
+/*** Ways ***/
+
+struct osm_way *osm_way_new(osm_id_t id)
+{
+ struct osm_way *w = osm_obj_new(OSM_TYPE_WAY, id, sizeof(*w));
+ clist_init(&w->nodes);
+ return w;
+}
+
+void osm_way_add_node(struct osm_way *w, struct osm_node *n)
+{
+ osm_ref_add(&w->o, &w->nodes, &n->o, 0);
+}
+
+void osm_way_dump(struct osm_way *w)
+{
+ printf("Way %ju%s\n", (uintmax_t) w->o.id, (osm_way_cyclic_p(w) ? " (cyclic)" : ""));
+ CLIST_FOR_EACH(struct osm_tag *, t, w->o.tags)
+ osm_tag_dump(t);
+ OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
+ {
+ printf("\tNode %ju\n", (uintmax_t) n->o.id);
+ }
+ OSM_FOR_EACH_END;
+}
+
+void osm_way_dump_all(void)
+{
+ printf("Known ways:\n");
+ CLIST_FOR_EACH(struct osm_way *, w, osm_obj_list[OSM_TYPE_WAY])
+ osm_way_dump(w);
+ putchar('\n');
+}
+
+bool osm_way_cyclic_p(struct osm_way *w)
+{
+ struct osm_ref *first_ref = clist_head(&w->nodes);
+ struct osm_ref *last_ref = clist_tail(&w->nodes);
+ return (first_ref && last_ref &&
+ first_ref != last_ref &&
+ first_ref->o == last_ref->o);
+}
+
+/*** Relations ***/
+
+struct osm_relation *osm_relation_new(osm_id_t id)
+{
+ struct osm_relation *r = osm_obj_new(OSM_TYPE_RELATION, id, sizeof(*r));
+ clist_init(&r->members);
+ return r;
+}
+
+void osm_relation_add_member(struct osm_relation *r, struct osm_object *o, const char *role)
+{
+ osm_ref_add(&r->o, &r->members, o, (role && role[0] ? osm_val_encode(role) : 0));
+}
+
+void osm_relation_dump(struct osm_relation *r)
+{
+ printf("Relation %ju\n", (uintmax_t) r->o.id);
+ CLIST_FOR_EACH(struct osm_tag *, t, r->o.tags)
+ osm_tag_dump(t);
+ CLIST_FOR_EACH(struct osm_ref *, f, r->members)
+ printf("\tRole %s: %s %ju\n", (f->role ? osm_val_decode(f->role) : "<none>"), osm_obj_type_names[f->o->type], (uintmax_t) f->o->id);
+}
+
+void osm_relation_dump_all(void)
+{
+ printf("Known relations:\n");
+ CLIST_FOR_EACH(struct osm_relation *, r, osm_obj_list[OSM_TYPE_RELATION])
+ osm_relation_dump(r);
+ putchar('\n');
+}
+
+/*** Multipolygons ***/
+
+void osm_multipolygon_dump(struct osm_multipolygon *m)
+{
+ printf("Multipolygon %ju\n", (uintmax_t) m->o.id);
+ CLIST_FOR_EACH(struct osm_tag *, t, m->o.tags)
+ osm_tag_dump(t);
+ CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
+ {
+ printf("\tBoundary inner=%u\n", b->inner);
+ CLIST_FOR_EACH(struct osm_ref *, f, b->nodes)
+ printf("\t\tNode %ju\n", (uintmax_t) f->o->id);
+ }
+}
+
+void osm_multipolygon_dump_all(void)
+{
+ printf("Known multipolygons:\n");
+ CLIST_FOR_EACH(struct osm_multipolygon *, r, osm_obj_list[OSM_TYPE_MULTIPOLYGON])
+ osm_multipolygon_dump(r);
+ putchar('\n');
+}
+
+static struct mempool *mpg_pool;
+static struct osm_multipolygon *mpg_current;
+
+struct mpg_vertex {
+ osm_id_t id;
+ struct osm_object *o;
+ clist edges; // of mpg_edge's
+ bool visited;
+};
+
+struct mpg_edge {
+ cnode n;
+ struct mpg_edge *twin;
+ struct mpg_vertex *dest;
+ bool used;
+};
+
+#define HASH_NODE struct mpg_vertex
+#define HASH_PREFIX(x) mpg_vertex_hash_##x
+#define HASH_KEY_ATOMIC id
+#define HASH_ATOMIC_TYPE osm_id_t
+#define HASH_USE_POOL mpg_pool
+#define HASH_TABLE_ALLOC
+#define HASH_WANT_LOOKUP
+#define HASH_LOOKUP_DETECT_NEW
+#include <ucw/hashtable.h>
+
+static void mpg_insert_way(struct osm_way *w)
+{
+ struct mpg_vertex *prev = NULL;
+ OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
+ {
+ int new = 0;
+ struct mpg_vertex *v = mpg_vertex_hash_lookup(n->o.id, &new);
+ if (new)
+ {
+ clist_init(&v->edges);
+ v->visited = 0;
+ v->o = &n->o;
+ }
+ if (prev)
+ {
+ struct mpg_edge *e1 = mp_alloc_zero(mpg_pool, sizeof(*e1));
+ struct mpg_edge *e2 = mp_alloc_zero(mpg_pool, sizeof(*e2));
+ e1->twin = e2;
+ e1->dest = v;
+ clist_add_tail(&prev->edges, &e1->n);
+ e2->twin = e1;
+ e2->dest = prev;
+ clist_add_tail(&v->edges, &e2->n);
+ }
+ prev = v;
+ }
+ OSM_FOR_EACH_END;
+}
+
+static void UNUSED mpg_dump(void)
+{
+ printf("=== Multipolygon graph for relation %ju ===\n", (uintmax_t) mpg_current->o.id);
+ HASH_FOR_ALL(mpg_vertex_hash, v)
+ {
+ printf("Vertex %ju\n", (uintmax_t) v->id);
+ CLIST_FOR_EACH(struct mpg_edge *, e, v->edges)
+ printf("\t-> %ju\n", (uintmax_t) e->dest->id);
+ }
+ HASH_END_FOR;
+ puts("=== End ===");
+}
+
+static void mpg_walk_boundary(struct osm_multipolygon *m, bool inner)
+{
+ // FIXME: Replace by a better algorithm, which respects geometry
+ // and is able to cope with higher degree vertices.
+ HASH_FOR_ALL(mpg_vertex_hash, v)
+ {
+ if (!v->visited)
+ {
+ struct osm_mpg_boundary *b = mp_alloc(osm_pool, sizeof(*b));
+ clist_add_tail(&m->boundaries, &b->n);
+ b->inner = inner;
+ clist_init(&b->nodes);
+
+ struct mpg_vertex *w = v;
+ while (!w->visited)
+ {
+ w->visited = 1;
+ struct osm_ref *f = mp_alloc(osm_pool, sizeof(*f));
+ clist_add_tail(&b->nodes, &f->n);
+ f->o = w->o;
+ f->role = 0;
+
+ struct mpg_vertex *dest = NULL;
+ CLIST_FOR_EACH(struct mpg_edge *, e, w->edges)
+ {
+ if (e->used)
+ continue;
+ if (dest)
+ {
+ if (w != v)
+ osm_obj_warn(&mpg_current->o, "Suspicious vertex degree of node %ju", (uintmax_t) w->o->id);
+ break;
+ }
+ else
+ {
+ dest = e->dest;
+ e->used = 1;
+ e->twin->used = 1;
+ }
+ }
+
+ w = dest;
+ }
+
+ if (w != v)
+ osm_obj_warn(&mpg_current->o, "Boundary not closed at node %ju", (uintmax_t) w->o->id);
+
+ struct osm_ref *f = mp_alloc(osm_pool, sizeof(*f));
+ clist_add_tail(&b->nodes, &f->n);
+ f->o = v->o;
+ f->role = 0;
+ }
+ }
+ HASH_END_FOR;
+}
+
+static void osm_multipolygon_make(struct osm_relation *r)
+{
+ struct osm_multipolygon *m = osm_obj_new(OSM_TYPE_MULTIPOLYGON, r->o.id, sizeof(*m));
+ mpg_current = m;
+ m->rel = r;
+ CLIST_FOR_EACH(struct osm_tag *, t, r->o.tags)
+ osm_obj_add_tag_raw(&m->o, t->key, t->val);
+ clist_init(&m->boundaries);
+
+ for (int inner=0; inner<2; inner++)
+ {
+ if (!mpg_pool)
+ mpg_pool = mp_new(4096);
+ mpg_vertex_hash_init();
+
+ CLIST_FOR_EACH(struct osm_ref *, f, r->members)
+ {
+ struct osm_object *o = f->o;
+ if (f->role != VALUE_INNER && f->role != VALUE_OUTER)
+ {
+ if (!inner)
+ osm_obj_warn(o, "Unknown role %s in multipolygon relation", osm_val_decode(f->role));
+ continue;
+ }
+ if ((f->role == VALUE_INNER) != inner)
+ continue;
+ if (o->type == OSM_TYPE_WAY)
+ mpg_insert_way((struct osm_way *) o);
+ else
+ osm_obj_warn(o, "Only ways are supported in multipolygon boundaries");
+ }
+
+ if (debug_dump_multipolygons)
+ mpg_dump();
+
+ mpg_walk_boundary(m, inner);
+ mp_flush(mpg_pool);
+ }
+}
+
+void osm_make_multipolygons(void)
+{
+ uns mpg_cnt = 0;
+
+ CLIST_FOR_EACH(struct osm_relation *, r, osm_obj_list[OSM_TYPE_RELATION])
+ if (osm_obj_find_tag(&r->o, KEY_TYPE) == VALUE_MULTIPOLYGON)
+ {
+ osm_multipolygon_make(r);
+ mpg_cnt++;
+ }
+ msg(L_INFO, "Converted %u relations to multipolygons", mpg_cnt);
+}
+
+/*** Projection ***/
+
+static projPJ osm_pj;
+
+void osm_project(const char *spec)
+{
+ osm_pj = pj_init_plus(spec);
+ if (!osm_pj)
+ die("Unable to initialize projection %s", spec);
+
+ CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
+ {
+ projUV p;
+ p.u = n->x * DEG_TO_RAD;
+ p.v = n->y * DEG_TO_RAD;
+ p = pj_fwd(p, osm_pj);
+ n->x = p.u;
+ n->y = p.v;
+ }
+}
+
+/*** Object centers ***/
+
+bool osm_obj_center(struct osm_object *o, double *xp, double *yp)
+{
+ switch (o->type)
+ {
+ case OSM_TYPE_NODE:
+ {
+ struct osm_node *n = (struct osm_node *) o;
+ *xp = n->x;
+ *yp = n->y;
+ return 1;
+ }
+ case OSM_TYPE_WAY:
+ {
+ struct osm_way *w = (struct osm_way *) o;
+ double sx = 0, sy = 0;
+ uns nn = 0;
+ OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
+ {
+ sx += n->x;
+ sy += n->y;
+ nn++;
+ }
+ OSM_FOR_EACH_END;
+ if (!nn)
+ return 0;
+ *xp = sx / nn;
+ *yp = sy / nn;
+ return 1;
+ }
+ case OSM_TYPE_MULTIPOLYGON:
+ {
+ struct osm_multipolygon *m = (struct osm_multipolygon *) o;
+ double sx = 0, sy = 0;
+ uns nn = 0;
+ CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
+ {
+ if (b->inner)
+ continue;
+ OSM_FOR_EACH_BEGIN(struct osm_node *, n, b->nodes)
+ {
+ sx += n->x;
+ sy += n->y;
+ nn++;
+ }
+ OSM_FOR_EACH_END;
+ }
+ if (!nn)
+ return 0;
+ *xp = sx / nn;
+ *yp = sy / nn;
+ return 1;
+ }
+ default:
+ return 0;
+ }
+}
+
+/*** Globals ***/
+
+void osm_init(void)
+{
+ osm_pool = mp_new(65536);
+ for (int i=0; i<OSM_TYPE_MAX; i++)
+ {
+ clist_init(&osm_obj_list[i]);
+ osm_id_hash_init(&osm_id_hash[i]);
+ }
+ osm_tag_init();
+}
+
+void osm_dump(void)
+{
+ osm_node_dump_all();
+ osm_way_dump_all();
+ osm_relation_dump_all();
+ osm_multipolygon_dump_all();
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Representation of OSM Data
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_OSM_H
+#define _BRUM_OSM_H
+
+#include "dict.h"
+
+typedef u64 osm_id_t;
+#define OSM_INVALID_ID ~(osm_id_t)0
+
+typedef u32 osm_key_t, osm_val_t;
+
+struct osm_object {
+ cnode n; // In the per-type list
+ byte type; // OSM_TYPE_xxx
+ byte pad[3];
+ osm_id_t id;
+ clist tags;
+ clist backrefs; // Objects pointing to this one
+};
+
+enum osm_object_type {
+ OSM_TYPE_INVALID,
+ OSM_TYPE_NODE,
+ OSM_TYPE_WAY,
+ OSM_TYPE_RELATION,
+ OSM_TYPE_MULTIPOLYGON,
+ // Remember to update osm_obj_type_names[], too
+ OSM_TYPE_MAX,
+};
+
+struct osm_tag {
+ cnode n; // In object->tags
+ osm_key_t key;
+ osm_val_t val;
+};
+
+struct osm_ref {
+ cnode n;
+ struct osm_object *o;
+ osm_val_t role;
+};
+
+struct osm_node {
+ struct osm_object o;
+ double x, y;
+ /*
+ * For WGS84 coordinates, x is longitude and y latitude.
+ * For UTM, x is easting and y northing.
+ * After map scaling, x is horizontal (left-to-right) and y vertical (top-to-bottom) on the paper.
+ */
+};
+
+struct osm_way {
+ struct osm_object o;
+ clist nodes; // List of osm_ref's
+};
+
+struct osm_relation {
+ struct osm_object o;
+ clist members; // List of osm_ref's
+};
+
+struct osm_multipolygon { // Virtual object constructed from multipolygon relations
+ struct osm_object o;
+ struct osm_relation *rel; // Original relation
+ clist boundaries; // List of osm_mpg_boundary's
+};
+
+struct osm_mpg_boundary {
+ cnode n;
+ bool inner;
+ clist nodes; // List of osm_ref's (without back-references, since the boundary is not a regular object)
+};
+
+void osm_init(void);
+void osm_dump(void);
+
+extern clist osm_obj_list[OSM_TYPE_MAX];
+
+osm_id_t osm_parse_id(const char *str);
+struct osm_object *osm_obj_find_by_id(enum osm_object_type type, osm_id_t id);
+void osm_obj_add_tag(struct osm_object *o, const char *key, const char *val);
+void osm_obj_add_tag_raw(struct osm_object *o, osm_key_t key, osm_val_t val);
+osm_val_t osm_obj_find_tag(struct osm_object *o, osm_val_t key);
+void osm_obj_dump(struct osm_object *o);
+void osm_obj_warn(struct osm_object *o, const char *msg, ...);
+extern const char * const osm_obj_type_names[OSM_TYPE_MAX];
+#define STK_OSM_NAME(o) stk_printf("%s:%ju", osm_obj_type_names[(o)->type], (uintmax_t) (o)->id)
+
+void osm_ref_add(struct osm_object *parent, clist *list, struct osm_object *son, osm_val_t role);
+#define OSM_FOR_EACH_BEGIN(_type, _var, _list) CLIST_FOR_EACH(struct osm_ref *, _ref, _list) { _type _var = (_type) _ref->o;
+#define OSM_FOR_EACH_END }
+
+struct osm_node *osm_node_new(osm_id_t id);
+void osm_node_dump(struct osm_node *n);
+void osm_node_dump_all(void);
+
+struct osm_way *osm_way_new(osm_id_t id);
+void osm_way_add_node(struct osm_way *w, struct osm_node *n);
+void osm_way_dump(struct osm_way *w);
+void osm_way_dump_all(void);
+bool osm_way_cyclic_p(struct osm_way *w);
+
+struct osm_relation *osm_relation_new(osm_id_t id);
+void osm_relation_add_member(struct osm_relation *r, struct osm_object *o, const char *role);
+void osm_relation_dump(struct osm_relation *w);
+void osm_relation_dump_all(void);
+
+void osm_multipolygon_dump(struct osm_multipolygon *w);
+void osm_multipolygon_dump_all(void);
+void osm_make_multipolygons(void);
+
+void osm_project(const char *spec);
+
+bool osm_obj_center(struct osm_object *o, double *xp, double *yp);
+
+/* Tags */
+
+extern struct dict osm_key_dict, osm_value_dict;
+
+static inline osm_key_t osm_key_encode(const char *key)
+{
+ return dict_encode(&osm_key_dict, key);
+}
+
+static inline const char *osm_key_decode(osm_key_t key)
+{
+ return dict_decode(&osm_key_dict, key);
+}
+
+static inline osm_val_t osm_val_encode(const char *val)
+{
+ return dict_encode(&osm_value_dict, val);
+}
+
+static inline const char *osm_val_decode(osm_val_t key)
+{
+ return dict_decode(&osm_value_dict, key);
+}
+
+void osm_tag_dump(struct osm_tag *t);
+
+/* Well-known tags and values */
+
+enum tag_keys {
+ KEY_NULL,
+#define P(x,y) KEY_##x,
+#include "dict-keys.h"
+#undef P
+};
+
+enum value_names {
+ VALUE_NULL,
+#define P(x,y) VALUE_##x,
+#include "dict-values.h"
+#undef P
+};
+
+#endif
--- /dev/null
+/*** General settings ***/
+
+way::* {
+ linejoin: round;
+ linecap: round;
+ casing-linejoin: round;
+ casing-linecap: round;
+}
+
+/*
+ * Assignment of z-indices:
+ *
+ * 0 default
+ * 0.1 subtypes of landuse
+ * 1.x highways
+ * 2.x railways
+ * 4 tourist routes
+ * 5 icons
+ * 9 hack:front
+ * 10 power lines and similar overhead objects
+ *
+ * Assignment of major-z-indices:
+ *
+ * 1 (default for areas}
+ * 1.1 water way casing
+ * 1.2 water ways
+ * 1.3 water areas
+ * 2 (default for casing)
+ * 2.9 (default for line patterns)
+ * 3 (default for lines)
+ * 4 (default for points)
+ * 4.9 (default for line text)
+ * 5 (default for point text)
+ */
+
+/*** Landuse ***/
+
+/*
+area[landuse], area[leisure] {
+ fill-color: #f00;
+}
+*/
+
+area[leisure=swimming_pool][access!=private] {
+ fill-color: #b5d0d0;
+ color: #00f;
+ width: 0.5;
+}
+
+area[leisure=playground] {
+ fill-color: #ccfff1;
+ color: #666;
+ width: 0.3;
+}
+
+area[leisure=attraction] {
+ fill-color: #f2caea;
+ // width: 0.3;
+}
+
+area[landuse=cemetery], area[landuse=grave_yard], area[amenity=grave_yard] {
+ fill-color: #aacbaf;
+ fill-pattern: "icons/cemetery.svg";
+ fill-pattern-width: 2;
+ // FIXME: Switch patterns according to religion=* ?
+}
+
+/*
+area[landuse=residential] {
+ fill-color: #ddd;
+}
+*/
+
+area[landuse=meadow],
+area[landuse=grass],
+area[leisure=common],
+area[leisure=garden],
+area[landuse=recreation_ground],
+area[landuse=orchard],
+area[landuse=plant_nursery],
+area[landuse=village_green] {
+ fill-color: #cfeca8;
+}
+
+area[natural=scrub],
+area[leisure=golf_course] {
+ fill-color: #b5e3b5;
+ z-index: 0.1;
+}
+
+area[leisure=park],
+area[leisure=recreation_ground] {
+ fill-color: #b6fdb6;
+ z-index: 0.1;
+}
+
+area[landuse=forest] {
+ fill-color: #8dc56c;
+ // FIXME: Pattern fill
+}
+
+area[landuse=allotments] {
+ fill-color: #e5c7ab;
+ // FIXME: Pattern fill
+}
+
+area[landuse=retail] {
+ fill-color: #f1dada;
+ color: #f00;
+ width: 0.3;
+}
+
+area[landuse=industrial],
+area[landuse=railway] {
+ fill-color: #dfd1d6;
+}
+
+area[landuse=farm],
+area[landuse=farmland] {
+ fill-color: #ead8bd;
+}
+
+area[landuse=brownfield],
+area[landuse=landfill],
+area[landuse=greenfield],
+area[landuse=construction] {
+ fill-color: #9d9d6c;
+}
+
+area[landuse=military] {
+ fill-color: #65841b;
+ color: #f55;
+ width: 0.3;
+ // FIXME: Pattern
+}
+
+area[landuse=parking] {
+ fill-color: #f7efb7;
+ color: #eeeed1;
+ width: 0.3;
+}
+
+area[aeroway=apron] {
+ fill-color: #e9d1ff;
+}
+
+area[aeroway=aerodrome] {
+ fill-color: #ccc;
+ fill-opacity: 0.2;
+ color: #666;
+ width: 0.2;
+}
+
+area[landuse=commercial],
+area[highway=services],
+area[highway=rest_area] {
+ fill-color: #efc8c8;
+}
+
+area[landuse=garages] {
+ fill-color: #996;
+ fill-opacity: 0.2;
+}
+
+/*
+area[leisure=nature_reserve] {
+ fill-color: #6c3;
+ color: #6c3;
+ width: 0.3;
+ // FIXME: Pattern?
+}
+*/
+
+area[leisure=pitch] {
+ fill-color: #8ad3af;
+ color: #888;
+ width: 0.3;
+ z-index: 0.1;
+}
+
+area[leisure=track] {
+ fill-color: #74dcba;
+ color: #888;
+ width: 0.3;
+ z-index: 0.1;
+}
+
+way[barrier] {
+ color: #666;
+ width: 0.2;
+}
+
+/*** Water ***/
+
+/*
+way[waterway] {
+ color: #f00;
+}
+*/
+
+area[landuse=basin],
+area[landuse=reservoir],
+area[landuse=water],
+area[natural=bay],
+area[natural=lake],
+area[natural=water],
+area[waterway=riverbank] {
+ fill-color: #b5d0d0;
+ major-z-index: 1.3; // Above normal areas and waterways
+ text: name;
+ text-halo-radius: 0.5;
+ text-color: #3030a5;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-position: center;
+ font-family: Times;
+ font-size: 2.5;
+ font-style: italic;
+}
+
+area[natural=marsh],
+area[natural=wetland] {
+ fill-pattern: "icons/wetland.svg";
+ fill-pattern-width: 2;
+ fill-pattern-height: 1.5;
+ major-z-index: 1.3;
+}
+
+way[waterway=stream],
+way[waterway=ditch],
+way[waterway=drain] {
+ color: #b5d0d0;
+ width: 0.3;
+ casing-width: 0.2;
+ casing-color: #fff;
+ major-z-index: 1.2; // Above normal areas, below water areas
+ casing-major-z-index: 1.1;
+ casing-opacity: 0.5;
+}
+
+way[waterway=river] {
+ color: #b5d0d0;
+ width: 0.6;
+ casing-color: #fff;
+ casing-width: 1;
+ major-z-index: 1.2; // Above normal areas, below water areas
+ casing-major-z-index: 1.1;
+ casing-opacity: 0.5;
+}
+
+// FIXME: waterway=river && tunnel=yes
+
+/*** Power lines ***/
+
+way[power=line] {
+ color: #777;
+ width: 0.3;
+ z-index: 10;
+}
+
+way[power=minor_line] {
+ color: #777;
+ width: 0.15;
+ z-index: 10;
+}
+
+node[power=tower],
+node[power=pole] {
+ symbol-shape: circle; // FIXME
+ symbol-size: 0.7;
+ symbol-stroke-color: #777;
+ symbol-stroke-width: 0.3;
+ z-index: 10;
+}
+
+/*** Highways ***/
+
+/*
+way[highway][area?!] {
+ color: #f00;
+ width: 0.3;
+}
+*/
+
+area[highway=residential],
+area[highway=unclassified],
+area[highway=service] {
+ fill-color: #fff;
+}
+
+area[highway=pedestrian],
+area[highway=footway],
+area[highway=path] {
+ fill-color: #ededed;
+}
+
+area[highway=track] {
+ fill-color: #dfcc66;
+}
+
+area[highway=platform],
+area[railway=platform] {
+ fill-color: #bbbbbb;
+}
+
+area[aeroway=runway],
+area[aeroway=taxiway],
+area[aeroway=helipad] {
+ fill-color: #bbc;
+ z-index: 1;
+}
+
+way[highway=motorway_link] {
+ color: #809bc0;
+ width: 5;
+ casing-color: #506077;
+ casing-width: 1;
+ z-index: 1.9;
+}
+
+way[highway=trunk_link] {
+ color: #a9dba9;
+ width: 2.5;
+ casing-color: #477147;
+ casing-width: 0.8;
+ z-index: 1.8;
+}
+
+way[highway=primary_link] {
+ color: #ec989a;
+ width: 1.5;
+ casing-color: #8d4346;
+ casing-width: 0.5;
+ z-index: 1.7;
+}
+
+way[highway=secondary_link] {
+ color: #fed7a5;
+ width: 1;
+ casing-color: #a37b48;
+ casing-width: 0.5;
+ z-index: 1.6;
+}
+
+way[highway=tertiary_link] {
+ color: #ffffb3;
+ width: 0.7;
+ casing-color: #bbb;
+ casing-width: 0.5;
+ z-index: 1.5;
+}
+
+way[highway=motorway] {
+ color: #809bc0;
+ width: 5;
+ casing-color: #506077;
+ casing-width: 1;
+ z-index: 1.9;
+}
+
+way[highway=trunk] {
+ color: #a9dba9;
+ width: 2.5;
+ casing-color: #477147;
+ casing-width: 0.8;
+ z-index: 1.8;
+}
+
+way[highway=primary] {
+ color: #ec989a;
+ width: 1.5;
+ casing-color: #8d4346;
+ casing-width: 0.5;
+ z-index: 1.7;
+}
+
+way[highway=secondary] {
+ color: #fed7a5;
+ width: 1;
+ casing-color: #a37b48;
+ casing-width: 0.5;
+ z-index: 1.6;
+}
+
+way[highway=tertiary] {
+ color: #ffffb3;
+ width: 0.7;
+ casing-color: #bbb;
+ casing-width: 0.5;
+ z-index: 1.5;
+}
+
+way[highway=road] {
+ color: #ddd;
+ width: 0.7;
+ casing-color: #999;
+ casing-width: 0.5;
+ z-index: 1;
+}
+
+way[highway=residential],
+way[highway=unclassified],
+way[highway=road],
+way[highway=living_street] {
+ color: #fff;
+ width: 0.7;
+ casing-color: #999;
+ casing-width: 0.5;
+ z-index: 1;
+}
+
+way[highway=service] {
+ color: #fff;
+ width: 0.5;
+ casing-color: #999;
+ casing-width: 0.3;
+ z-index: 1;
+}
+
+way[highway=pedestrian] {
+ color: #ededed;
+ width: 0.3;
+ casing-color: #808080;
+ casing-width: 0.3;
+}
+
+// In this scale, steps cannot be displayed nicely
+/*
+way[highway=steps] {
+ color: #fa8072;
+ width: 0.8;
+ dashes: 0.2, 0.2;
+}
+*/
+
+way[highway=footway],
+way[highway=path][foot=designated] {
+// color: #888;
+ color: #996600;
+ width: 0.3;
+ casing-color: #fff;
+ casing-width: 0.1;
+ casing-opacity: 0.5;
+}
+
+way[highway=track],
+way[highway=path],
+way[highway=bridleway] {
+ color: #996600;
+ width: 0.3;
+ dashes: 5, 3;
+ casing-color: #fff;
+ casing-width: 0.1;
+ casing-opacity: 0.5;
+}
+
+way[highway=cycleway],
+way[highway=path][bicycle=designated] {
+/*
+ color: #5e5eff;
+ width: 0.3;
+ dashes: 5, 3;
+*/
+// color: #888;
+ color: #996600;
+ width: 0.3;
+ casing-color: #fff;
+ casing-width: 0.1;
+ casing-opacity: 0.5;
+}
+
+way[highway=proposed] {
+ width: 0;
+}
+
+way[aeroway=runway],
+way[aeroway=taxiway] {
+ color: #bbc;
+ width: 0.3;
+}
+
+/*** Railway ***/
+
+/*
+way[railway][area?!] {
+ color: #f0f;
+ width: 0.3;
+}
+*/
+
+way[railway=rail] {
+ color: #999;
+ width: 0.6;
+ casing-color: #444;
+ casing-width: 0.2;
+ z-index: 2.5;
+}
+
+way[railway=rail]::rwint {
+ color: #fff;
+ width: 0.5;
+ dashes: 9, 9;
+ z-index: 2.6;
+}
+
+way[railway=rail][service=spur],
+way[railway=rail][service=yard] {
+ width: 0.3;
+ z-index: 2;
+}
+
+way[railway=rail][service=spur]::rwint,
+way[railway=rail][service=yard]::rwint {
+ width: 0.2;
+ z-index: 2.1;
+}
+
+/*** Buildings ***/
+
+area[building] {
+ fill-color: #bca9a9;
+ color: #330066;
+ width: 0.1;
+}
+
+/*** Tourist routes ***/
+
+relation[type=route][route=foot][kct_yellow] way::routes {
+ color: #ee3;
+ width: 0.5;
+ z-index: 4;
+}
+
+relation[type=route][route=foot][kct_red] way::routes {
+ color: #e33;
+ width: 0.5;
+ z-index: 4;
+}
+
+way[highway=footway]::routes,
+way[highway=path]::routes,
+way[highway=cycleway]::routes,
+way[highway=bridleway]::routes,
+way[highway=track]::routes {
+ opacity: 0.7;
+}
+
+/*** Icons ***/
+
+node[tourism=viewpoint] {
+ icon-image: "icons/view_point.svg";
+ icon-width: 4;
+ z-index: 5;
+}
+
+node[historic=wayside_cross][religion=christian],
+node[amenity=place_of_worship][religion=christian],
+node[amenity=crucifix],
+area[amenity=place_of_worship][religion=christian] {
+ icon-image: "icons/cross.svg";
+ icon-width: 1.5;
+ z-index: 5;
+}
+
+area[building=church][amenity=place_of_worship][religion=christian],
+area[historic=wayside_shrine][amenity=place_of_worship][religion=christian] {
+ icon-image: "icons/church.svg";
+ icon-width: 2;
+ z-index: 5;
+ text: name;
+ text-halo-radius: 0.8;
+ text-color: #000000;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-position: center;
+ text-offset: -3;
+ font-family: Times;
+ font-size: 2.5;
+ font-style: italic;
+}
+
+node[natural=cave_entrance] {
+ icon-image: "icons/cave2.svg";
+ icon-width: 3;
+ z-index: 5;
+}
+
+node[natural=tree][monument=yes][type=broad_leaved] {
+ icon-image: "icons/deciduous.svg";
+ icon-width: 3;
+ z-index: 5;
+ text: name;
+ text-halo-radius: 0.5;
+ text-color: #006322;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-anchor-horizontal: center;
+ text-anchor-vertical: bottom;
+ text-offset: -1;
+ font-family: Times;
+ font-size: 2.5;
+ font-style: italic;
+}
+
+node[hack=parkname] {
+ text: name;
+ text-halo-radius: 0.5;
+ text-color: #006322;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-anchor-horizontal: center;
+ text-anchor-vertical: center;
+ text-offset: -1;
+ font-family: Times;
+ font-size: 2.5;
+ font-style: italic;
+}
+
+node[hack=suburbname] {
+ text: name;
+ text-halo-radius: 1;
+ text-color: #888888;
+ text-halo-opacity: 0.8;
+ text-halo-color: #ffffff;
+ text-anchor-horizontal: center;
+ text-anchor-vertical: center;
+ font-family: Helvetica;
+ font-size: 6;
+ font-style: italic;
+}
+
+
+node[barrier=gate] {
+ icon-image: "icons/gate.svg";
+ icon-width: 1.5;
+ z-index: 5;
+}
+
+node[railway=station],
+node[railway=halt] {
+ icon-image: "icons/train.svg";
+ icon-width: 3;
+ z-index: 5;
+ text: name;
+ text-halo-radius: 0.5;
+ text-color: #000000;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-anchor-horizontal: center;
+ text-anchor-vertical: bottom;
+ text-offset: -2;
+ font-family: Times;
+ font-size: 2.5;
+ font-style: italic;
+}
+
+area[railway=station],
+area[railway=halt] {
+ icon-image: "icons/train.svg";
+ icon-width: 3;
+ z-index: 5;
+ text: name;
+ text-halo-radius: 0.5;
+ text-color: #000000;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-position: center;
+ text-offset: -4;
+ font-family: Times;
+ font-size: 2.5;
+ font-style: italic;
+}
+
+node[highway=bus_stop] {
+// icon-image: "icons/bus_stop2.svg";
+// icon-width: 3;
+ symbol-shape: circle;
+ symbol-size: 0.8;
+ symbol-fill-color: #f55;
+ z-index: 5;
+}
+
+node[historic=memorial],
+node[tourism=artwork][artwork_type=statue] {
+ icon-image: "icons/memorial.svg";
+ icon-width: 1;
+ z-index: 5;
+}
+
+way[natural=tree_row] {
+ repeat-image: "icons/deciduous.svg";
+ repeat-image-width: 2;
+ repeat-image-spacing: 2;
+ z-index: 5;
+}
+
+/*** Hacks ***/
+
+node[hack=front],
+way[hack=front] {
+ z-index: 9;
+}
+
+node[hack=raisetext],
+way[hack=raisetext] {
+ text-offset: 3;
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Reading ESRI Shape Files
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ *
+ * FIXME: Currently, this parser handles only the subset
+ * of shape file syntax which is used by gdal_contours.
+ */
+
+#undef LOCAL_DEBUG
+
+#include <ucw/lib.h>
+#include <ucw/conf.h>
+#include <ucw/fastbuf.h>
+#include <ucw/gary.h>
+#include <ucw/unaligned.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "map.h"
+#include "shp.h"
+
+// FIXME: Hack
+static osm_id_t shp_id_counter = 1000000000000;
+
+static double shp_get_double(byte *p)
+{
+ // FIXME: This is non-portable!
+ double x = 0;
+ memcpy(&x, p, 8);
+ return x;
+}
+
+static void shp_record_polyline(byte *buf, u32 len)
+{
+ if (len < 44)
+ die("Polyline record too short: %u bytes", len);
+
+ u32 num_parts = get_u32_le(buf+36);
+ u32 num_points = get_u32_le(buf+40);
+ DBG("%u parts, %u points", num_parts, num_points);
+ if (num_parts != 1)
+ die("Polylines with multiple parts are not supported yet");
+
+ if (len < 44 + 4*num_parts + 16*num_points)
+ die("Polyline record too short for %u parts and %u points: %u bytes", num_parts, num_points, len);
+
+ struct osm_way *w = osm_way_new(shp_id_counter++);
+ osm_obj_add_tag(&w->o, "shape", "contour");
+
+ byte *p = buf + 44 + 4*num_parts;
+ for (uint i=0; i < num_points; i++)
+ {
+ double x = shp_get_double(p);
+ double y = shp_get_double(p+8);
+ DBG(">>> x=%.10g y=%.10g", x, y);
+ struct osm_node *n = osm_node_new(shp_id_counter++);
+ n->x = x;
+ n->y = y;
+ osm_way_add_node(w, n);
+ p += 16;
+ }
+}
+
+void shp_parse(const char *name)
+{
+ msg(L_INFO, "Loading shape file %s", name);
+ struct fastbuf *fb = bopen_file(name, O_RDONLY, NULL);
+
+ byte *buf;
+ GARY_INIT(buf, 100);
+
+ uns len = bread(fb, buf, 100);
+ if (len != 100 || get_u32_be(buf) != 9994)
+ die("Invalid shape file header");
+
+ u32 version = get_u32_le(buf+28);
+ if (version != 1000)
+ die("Unknown shape file version %u", version);
+
+ u32 type = get_u32_le(buf+32);
+ if (type != 3)
+ die("Unsupported shape file type %u", type);
+
+ u32 last_recno = 0;
+ while (len = bread(fb, buf, 8))
+ {
+ if (len != 8)
+ die("Truncated record header");
+ u32 recno = get_u32_be(buf);
+ u32 reclen = get_u32_be(buf+4) * 2;
+ DBG("@%ju: recno %u len %u", (uintmax_t) btell(fb) - 8, recno, reclen);
+ if (recno != ++last_recno)
+ die("Unexpected record #%u, should be #%u", recno, last_recno);
+ if (reclen > GARY_SIZE(buf))
+ GARY_RESIZE(buf, reclen);
+ len = bread(fb, buf, reclen);
+ if (len != reclen)
+ die("Truncated record: %u < %u", len, reclen);
+ if (len < 4)
+ die("Record too short: %u bytes", reclen);
+
+ u32 rectype = get_u32_le(buf);
+ if (rectype == 3)
+ shp_record_polyline(buf, len);
+ }
+
+ GARY_FREE(buf);
+ bclose(fb);
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Reading ESRI Shape Files
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_SHP_H
+#define _BRUM_SHP_H
+
+void shp_parse(const char *name);
+
+#endif
--- /dev/null
+/*
+ * Hic Est Leo -- Styling
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/mempool.h>
+
+#include <stdio.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "style.h"
+
+struct dict style_prop_dict, style_layer_dict;
+
+static const char * const style_wk_props[] = {
+ NULL,
+#define P(x,y) [PROP_##x] = y,
+#include "dict-props.h"
+#undef P
+ NULL
+};
+
+static const char * const style_wk_layers[] = {
+ NULL,
+ "default",
+ NULL
+};
+
+void styles_init(void)
+{
+ dict_init(&style_prop_dict, style_wk_props);
+ dict_init(&style_layer_dict, style_wk_layers);
+}
+
+#define HASH_NODE struct style_prop
+#define HASH_PREFIX(x) style_prop_##x
+#define HASH_KEY_ATOMIC key
+#define HASH_WANT_FIND
+#define HASH_WANT_LOOKUP
+#define HASH_GIVE_ALLOC
+// FIXME: Make <ucw/hashtable.h> accept our pool
+#define HASH_ZERO_FILL
+#define HASH_TABLE_DYNAMIC
+#define HASH_TABLE_ALLOC
+#define HASH_TABLE_VARS struct mempool *pool;
+static void *style_prop_alloc(struct style_prop_table *table, uns size);
+static inline void style_prop_free(struct style_prop_table *table UNUSED, void *x UNUSED) { }
+#include <ucw/hashtable.h>
+
+static void *style_prop_alloc(struct style_prop_table *table, uns size)
+{
+ return mp_alloc_fast(table->pool, size);
+}
+
+void style_init(struct style_results *r)
+{
+ r->pool = mp_new(4096);
+ r->num_layers = dict_size(&style_layer_dict);
+ r->layers = mp_alloc_zero(r->pool, r->num_layers * sizeof(struct style_info *));
+ r->num_active_layers = 0;
+ r->active_layers = mp_alloc_zero(r->pool, r->num_layers * sizeof(layer_t));
+}
+
+void style_begin(struct style_results *r, struct osm_object *o)
+{
+ ASSERT(!r->num_active_layers);
+ mp_push(r->pool);
+ r->obj = o;
+}
+
+void style_end(struct style_results *r)
+{
+ for (uns i=0; i<r->num_active_layers; i++)
+ r->layers[r->active_layers[i]] = NULL;
+ r->layers[0] = NULL;
+ r->num_active_layers = 0;
+ mp_pop(r->pool);
+}
+
+static inline void style_assign(struct style_prop *dest, struct style_prop *src)
+{
+ dest->type = src->type;
+ dest->val = src->val;
+}
+
+static struct style_info *style_get_info(struct style_results *r, layer_t layer)
+{
+ ASSERT(layer < r->num_layers);
+ if (!r->layers[layer])
+ {
+ struct style_info *si = mp_alloc_zero(r->pool, sizeof(*si));
+ r->layers[layer] = si;
+ si->hash = mp_alloc_zero(r->pool, sizeof(struct style_prop_table));
+ si->hash->pool = r->pool;
+ style_prop_init(si->hash);
+
+ if (layer != STYLE_LAYER_ALL)
+ {
+ r->active_layers[r->num_active_layers++] = layer;
+
+ // Copy all properties which have been set for all layers
+ struct style_info *si_all = r->layers[STYLE_LAYER_ALL];
+ if (si_all)
+ {
+ /*
+ * CAVEAT: This is probably wrong. When no properties are set explicitly
+ * set for a layer, all-layer properties are not propagated. Hopefully harmless.
+ */
+ HASH_FOR_ALL_DYNAMIC(style_prop, si_all->hash, s)
+ {
+ style_assign(style_prop_lookup(si->hash, s->key), s);
+ }
+ HASH_END_FOR;
+ }
+ }
+ }
+ return r->layers[layer];
+}
+
+void style_set_by_layer(struct style_results *r, layer_t layer, struct style_prop *p)
+{
+ if (layer == STYLE_LAYER_ALL)
+ {
+ // Set in all existing layers
+ for (uns i=0; i < r->num_active_layers; i++)
+ style_assign(style_prop_lookup(r->layers[r->active_layers[i]]->hash, p->key), p);
+ // ... and let it propagate to STYLE_LAYER_ALL
+ }
+
+ struct style_info *si = style_get_info(r, layer);
+ style_assign(style_prop_lookup(si->hash, p->key), p);
+}
+
+void style_set(struct style_info *si, struct style_prop *p)
+{
+ style_assign(style_prop_lookup(si->hash, p->key), p);
+}
+
+struct style_prop *style_get(struct style_info *si, prop_t key)
+{
+ return style_prop_find(si->hash, key);
+}
+
+struct style_prop *style_get_and_check(struct style_info *si, prop_t key, uns allowed_types)
+{
+ struct style_prop *p = style_prop_find(si->hash, key);
+ if (!p)
+ return NULL;
+ if (!(allowed_types & (1 << p->type)))
+ {
+ // XXX: Better diagnostics?
+ msg(L_WARN, "Style property %s set to invalid type #%u", style_prop_decode(p->key), p->type);
+ return NULL;
+ }
+ return p;
+}
+
+osm_val_t style_get_ident(struct style_info *si, prop_t key)
+{
+ struct style_prop *p = style_get_and_check(si, key, 1 << PROP_TYPE_IDENT);
+ return p ? p->val.id : 0;
+}
+
+osm_val_t style_get_string(struct style_info *si, prop_t key)
+{
+ struct style_prop *p = style_get_and_check(si, key, (1 << PROP_TYPE_STRING) | (1 << PROP_TYPE_IDENT));
+ return p ? p->val.id : 0;
+}
+
+bool style_get_number(struct style_info *si, prop_t key, double *dp)
+{
+ struct style_prop *p = style_get_and_check(si, key, 1 << PROP_TYPE_NUMBER);
+ if (!p)
+ return 0;
+ *dp = p->val.number;
+ return 1;
+}
+
+bool style_get_color(struct style_info *si, prop_t key, color_t *colorp)
+{
+ struct style_prop *p = style_get_and_check(si, key, 1 << PROP_TYPE_COLOR);
+ if (!p)
+ return 0;
+ *colorp = p->val.color;
+ return 1;
+}
+
+void style_dump(struct style_results *r)
+{
+ for (uns i=0; i < r->num_active_layers; i++)
+ {
+ layer_t layer = r->active_layers[i];
+ printf("Layer %s (%u)\n", style_layer_decode(layer), layer);
+ struct style_info *si = r->layers[layer];
+ HASH_FOR_ALL_DYNAMIC(style_prop, si->hash, s)
+ {
+ printf("\t");
+ style_dump_prop(s);
+ }
+ HASH_END_FOR;
+ }
+}
+
+static void style_dump_val(struct style_prop *s)
+{
+ uns cnt = 0;
+
+ switch (s->type)
+ {
+ case PROP_TYPE_STRING:
+ printf("%s [string]", osm_val_decode(s->val.id));
+ break;
+ case PROP_TYPE_IDENT:
+ printf("%s [ident]", osm_val_decode(s->val.id));
+ break;
+ case PROP_TYPE_NUMBER:
+ printf("%.6g [number]", s->val.number);
+ break;
+ case PROP_TYPE_COLOR:
+ printf("%06x [color]", s->val.color);
+ break;
+ case PROP_TYPE_LIST:
+ putchar('(');
+ CLIST_FOR_EACH(struct style_val_list_entry *, e, *s->val.list)
+ {
+ if (cnt++)
+ printf(", ");
+ style_dump_val(&e->val);
+ }
+ printf(") [list]");
+ break;
+ default:
+ printf("[unknown type %u]", s->type);
+ }
+}
+
+void style_dump_prop(struct style_prop *s)
+{
+ printf("%s = ", style_prop_decode(s->key));
+ style_dump_val(s);
+ putchar('\n');
+}
+
+void style_scale(struct style_info *si, double *wp, double *hp, prop_t width_prop, prop_t height_prop)
+{
+ double w, h;
+ bool got_width = style_get_number(si, width_prop, &w);
+ bool got_height = style_get_number(si, height_prop, &h);
+
+ if (got_width + got_height == 2)
+ {
+ *wp = w;
+ *hp = h;
+ }
+ else if (got_width + got_height == 1)
+ {
+ if (got_width)
+ {
+ *hp *= w / *wp;
+ *wp = w;
+ }
+ else
+ {
+ *wp *= h / *hp;
+ *hp = h;
+ }
+ }
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Styling
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_STYLE_H
+#define _BRUM_STYLE_H
+
+#include "osm.h"
+#include "dict.h"
+
+typedef u32 prop_t, layer_t; // See dictionaries below
+
+enum style_layer {
+ STYLE_LAYER_ALL,
+ STYLE_LAYER_DEFAULT,
+ // The other style are defined by style_layer_dict
+};
+
+struct style_results { // Computed style information for a given object in all layers
+ struct osm_object *obj;
+ uns num_layers;
+ struct style_info **layers;
+ uns num_active_layers;
+ layer_t *active_layers;
+ struct mempool *pool;
+};
+
+struct style_info { // Computed style information per layer
+ struct style_prop_table *hash; // Hash table of style properties
+};
+
+enum style_prop_type {
+ PROP_TYPE_INVALID,
+ PROP_TYPE_STRING, // Quoted string hashed in osm_value_dict
+ PROP_TYPE_IDENT, // Unquoted string hashed in osm_value_dict
+ PROP_TYPE_NUMBER, // Floating-point number
+ PROP_TYPE_COLOR, // Color represented in RGB
+ PROP_TYPE_LIST, // List of values
+};
+
+struct style_prop {
+ prop_t key; // From style_prop_dict
+ enum style_prop_type type; // PROP_TYPE_xxx
+ union {
+ osm_val_t id;
+ color_t color;
+ double number;
+ clist *list; // Of style_val_list_entry
+ } val;
+};
+
+struct style_val_list_entry {
+ cnode n;
+ struct style_prop val; // val.key is unused
+};
+
+enum prop_keys { // Well-known properties
+ PROP_NULL,
+#define P(x,y) PROP_##x,
+#include "dict-props.h"
+#undef P
+};
+
+void styles_init(void);
+void style_init(struct style_results *r);
+void style_begin(struct style_results *r, struct osm_object *o);
+void style_end(struct style_results *r);
+
+void style_set_by_layer(struct style_results *r, layer_t layer, struct style_prop *p);
+
+void style_set(struct style_info *si, struct style_prop *p);
+struct style_prop *style_get(struct style_info *si, prop_t key);
+osm_val_t style_get_ident(struct style_info *si, prop_t key);
+osm_val_t style_get_string(struct style_info *si, prop_t key);
+bool style_get_number(struct style_info *si, prop_t key, double *dp);
+bool style_get_color(struct style_info *si, prop_t key, color_t *colorp);
+struct style_prop *style_get_and_check(struct style_info *si, prop_t key, uns allowed_types);
+
+extern struct dict style_prop_dict, style_layer_dict;
+
+static inline prop_t style_prop_encode(const char *key)
+{
+ return dict_encode(&style_prop_dict, key);
+}
+
+static inline const char *style_prop_decode(prop_t id)
+{
+ return dict_decode(&style_prop_dict, id);
+}
+
+static inline layer_t style_layer_encode(const char *key)
+{
+ return dict_encode(&style_layer_dict, key);
+}
+
+static inline const char *style_layer_decode(layer_t id)
+{
+ return dict_decode(&style_layer_dict, id);
+}
+
+void style_dump(struct style_results *r);
+void style_dump_prop(struct style_prop *p);
+
+void style_scale(struct style_info *si, double *wp, double *hp, prop_t width_prop, prop_t height_prop);
+
+#endif
--- /dev/null
+/*
+ * Hic Est Leo -- SVG Icon Embedder
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/fastbuf.h>
+#include <xml/xml.h>
+
+#include <fcntl.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "leo.h"
+#include "svg.h"
+
+#define HASH_NODE struct svg_icon
+#define HASH_PREFIX(x) icon_##x
+#define HASH_KEY_ENDSTRING name
+#define HASH_WANT_LOOKUP
+#define HASH_WANT_CLEANUP
+#define HASH_ZERO_FILL
+#define HASH_TABLE_DYNAMIC
+#include <ucw/hashtable.h>
+
+static void svg_icon_error(struct xml_context *ctx)
+{
+ fprintf(stderr, "%s at %u: %s\n", (ctx->err_code < XML_ERR_ERROR) ? "warn" : "error", xml_row(ctx), ctx->err_msg);
+}
+
+struct svg_icon *svg_icon_load(struct svg *svg, const char *name)
+{
+ struct svg_icon *icon = icon_lookup(svg->icon_hash, (char *) name);
+ if (icon->id)
+ return icon;
+ icon->id = ++svg->icon_counter;
+ clist_init(&icon->patterns);
+
+ struct xml_context *ctx = xmalloc(sizeof(*ctx));
+ xml_init(ctx);
+ ctx->h_warn = ctx->h_error = ctx->h_fatal = svg_icon_error;
+ xml_push_fastbuf(ctx, bopen_file(name, O_RDONLY, NULL));
+
+ ctx->flags |= XML_ALLOC_TAGS | XML_ALLOC_CHARS;
+ xml_parse(ctx);
+ if (ctx->err_code)
+ die("Error reading icon %s: Fatal error in XML parser", name);
+
+ icon->ctx = ctx;
+ icon->root = ctx->dom;
+
+ struct xml_node *root = icon->root;
+ ASSERT(root);
+ if (strcmp(root->name, "svg"))
+ die("Error reading icon %s: root element is <%s>, not <svg>", name, root->name);
+
+ struct xml_attr *a_width = xml_attr_find(ctx, root, "width");
+ struct xml_attr *a_height = xml_attr_find(ctx, root, "height");
+ if (!a_width || !a_height)
+ die("Error reading icon %s: cannot determine image dimensions", name);
+ icon->width = atof(a_width->val) / svg->scale;
+ icon->height = atof(a_height->val) / svg->scale;
+ if (icon->width < 0.01 || icon->height < 0.01)
+ die("Error reading icon %s: invalid icon dimensions", name);
+ // FIXME: What if dimensions are given in absolute units?
+
+ msg(L_DEBUG, "Loaded icon %s (%.5g x %.5g)", name, icon->width, icon->height);
+ return icon;
+}
+
+static void svg_icon_unload(struct svg_icon *icon)
+{
+ xml_cleanup(icon->ctx);
+ xfree(icon->ctx);
+}
+
+static bool is_white(int c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+static void normalize_white(char *r)
+{
+ char *w = r;
+
+ while (is_white(*r))
+ r++;
+ while (*r)
+ {
+ if (is_white(*r))
+ {
+ while (is_white(*r))
+ r++;
+ if (*r)
+ *w++ = *r++;
+ }
+ else
+ *w++ = *r++;
+ }
+ *w = 0;
+}
+
+static void svg_embed(struct svg *svg, struct svg_icon *icon, struct xml_node *n)
+{
+ if (n->type == XML_NODE_CHARS)
+ {
+ char *t = mp_strdup(svg->pool, n->text);
+ normalize_white(t);
+ if (*t)
+ {
+ svg_push_chars(svg)->name = n->text;
+ svg_pop(svg);
+ }
+ return;
+ }
+ ASSERT(n->type == XML_NODE_ELEM);
+
+ svg_push_element(svg, n->name);
+ XML_ATTR_FOR_EACH(a, n)
+ {
+ char *val = a->val;
+ if (!strcmp(a->name, "id"))
+ val = mp_printf(svg->pool, "i%u-%s", icon->id, val);
+ else if (!strcmp(a->name, "xlink:href") && val[0] == '#')
+ val = mp_printf(svg->pool, "#i%u-%s", icon->id, val+1);
+ // FIXME: Some attributes can have values "url(#id)"
+ svg_set_attr(svg, a->name, val);
+ }
+ XML_NODE_FOR_EACH(e, n)
+ svg_embed(svg, icon, e);
+ svg_pop(svg);
+}
+
+static void svg_icon_embed(struct svg *svg, struct svg_icon *icon)
+{
+ svg_push_element(svg, "g");
+ svg_set_attr_format(svg, "id", "icon%u", icon->id);
+
+ svg_push_comment(svg)->name = mp_printf(svg->pool, "Embedded %s", icon->name);
+ svg_pop(svg);
+
+ svg_embed(svg, icon, icon->root);
+ svg_pop(svg);
+}
+
+void svg_icon_init(struct svg *svg)
+{
+ svg->icon_hash = mp_alloc_zero(svg->pool, sizeof(struct icon_table));
+ icon_init(svg->icon_hash);
+}
+
+void svg_icon_cleanup(struct svg *svg)
+{
+ HASH_FOR_ALL_DYNAMIC(icon, svg->icon_hash, icon)
+ {
+ svg_icon_unload(icon);
+ }
+ HASH_END_FOR;
+ icon_cleanup(svg->icon_hash);
+}
+
+void svg_icon_put(struct svg *svg, struct svg_icon_request *sir)
+{
+ struct svg_icon *icon = sir->icon;
+ double scale_x = sir->width / icon->width;
+ double scale_y = sir->height / icon->height;
+
+ // Center
+ // FIXME: More alignment modes?
+ double x = sir->x - sir->width / 2;
+ double y = sir->y - sir->height / 2;
+
+ svg_push_element(svg, "use");
+ svg_set_attr_format(svg, "xlink:href", "#icon%u", icon->id);
+ svg_set_attr_dimen(svg, "x", x / scale_x);
+ svg_set_attr_dimen(svg, "y", y / scale_y);
+ if (fabs(scale_x - 1) > 1e-10 || fabs(scale_y - 1) > 1e-10)
+ svg_set_attr_format(svg, "transform", "scale(%.5g %.5g)", scale_x, scale_y);
+ svg_pop(svg);
+}
+
+struct svg_pattern *svg_icon_to_pattern(struct svg *svg, struct svg_pattern_request *spr)
+{
+ CLIST_FOR_EACH(struct svg_pattern *, patt, spr->icon->patterns)
+ {
+ if (fabs(patt->width - spr->width) < 1e-10 &&
+ fabs(patt->height - spr->height) < 1e-10)
+ return patt;
+ }
+
+ struct svg_pattern *patt = mp_alloc_zero(svg->pool, sizeof(*patt));
+ clist_add_tail(&spr->icon->patterns, &patt->n);
+ patt->id = ++svg->pattern_counter;
+ patt->icon = spr->icon;
+ patt->width = spr->width;
+ patt->height = spr->height;
+ patt->paint_server = mp_printf(svg->pool, "url(#patt%u)", patt->id);
+ return patt;
+}
+
+static void svg_pattern_embed(struct svg *svg, struct svg_pattern *patt)
+{
+ struct svg_icon *icon = patt->icon;
+
+ svg_push_element(svg, "pattern");
+ svg_set_attr_format(svg, "id", "patt%u", patt->id);
+ svg_set_attr(svg, "patternUnits", "userSpaceOnUse");
+ svg_set_attr(svg, "patternContentUnits", "userSpaceOnUse");
+ svg_set_attr_dimen(svg, "width", patt->width);
+ svg_set_attr_dimen(svg, "height", patt->height);
+
+ struct svg_icon_request sir = {
+ .icon = icon,
+ .x = patt->width / 2,
+ .y = patt->height / 2,
+ .width = patt->width,
+ .height = patt->height,
+ };
+ svg_icon_put(svg, &sir);
+
+ svg_pop(svg);
+}
+
+void svg_icon_dump_library(struct svg *svg)
+{
+ svg_push_element(svg, "defs");
+ HASH_FOR_ALL_DYNAMIC(icon, svg->icon_hash, icon)
+ {
+ svg_icon_embed(svg, icon);
+ CLIST_FOR_EACH(struct svg_pattern *, patt, icon->patterns)
+ svg_pattern_embed(svg, patt);
+ }
+ HASH_END_FOR;
+ svg_pop(svg);
+}
--- /dev/null
+/*
+ * Hic Est Leo -- SVG Generator
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/gary.h>
+#include <ucw/mempool.h>
+#include <xml/xml.h>
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "leo.h"
+#include "svg.h"
+
+static void svg_start_tag(struct svg *svg, struct svg_element *e);
+static void svg_escape_string(struct svg *svg, const char *str);
+
+struct svg *svg_open(char *filename)
+{
+ struct mempool *mp = mp_new(4096);
+ struct svg *svg = mp_alloc_zero(mp, sizeof(*svg));
+
+ svg->pool = mp;
+ svg->fb = bopen_file(filename, O_WRONLY | O_CREAT | O_TRUNC, NULL);
+ svg->scale = 90 / 25.4;
+ // FIXME: Use scale for all operations with dimensions?
+ GARY_INIT(svg->stack, 0);
+
+ svg->fb_pool = mp_alloc_zero(svg->pool, sizeof(*svg->fb_pool));
+ fbpool_init(svg->fb_pool);
+
+ svg_icon_init(svg);
+
+ bputsn(svg->fb, "<?xml version=\"1.0\" standalone=\"no\"?>");
+ bputsn(svg->fb, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">");
+
+ svg_push_element(svg, "svg");
+ svg_set_attr(svg, "version", "1.1");
+ svg_set_attr(svg, "xmlns", "http://www.w3.org/2000/svg");
+ svg_set_attr(svg, "xmlns:xlink", "http://www.w3.org/1999/xlink");
+
+ return svg;
+}
+
+void svg_close(struct svg *svg)
+{
+ ASSERT(GARY_SIZE(svg->stack) == 1);
+ svg_pop(svg);
+
+ bclose(svg->fb);
+ GARY_FREE(svg->stack);
+ svg_icon_cleanup(svg);
+ mp_delete(svg->pool);
+}
+
+static inline struct svg_element *svg_element_this_or_null(struct svg *svg)
+{
+ uns n = GARY_SIZE(svg->stack);
+ return n ? svg->stack[n-1] : NULL;
+}
+
+static inline struct svg_element *svg_element_this(struct svg *svg)
+{
+ struct svg_element *e = svg_element_this_or_null(svg);
+ ASSERT(e);
+ return e;
+}
+
+enum svg_indent_mode {
+ SVG_INDENT_OPEN = 1,
+ SVG_INDENT_CLOSE = 2,
+};
+
+static void svg_indent_start(struct svg *svg, struct svg_element *e, enum svg_indent_mode mode)
+{
+ if (e->indent < 0 || (e->flags & SVG_EF_FLAT) && mode != SVG_INDENT_OPEN)
+ return;
+ for (int i=0; i<e->indent; i++)
+ bputc(svg->fb, '\t');
+}
+
+static void svg_indent_end(struct svg *svg, struct svg_element *e, enum svg_indent_mode mode)
+{
+ if (e->indent < 0 || (e->flags & SVG_EF_FLAT) && mode != SVG_INDENT_CLOSE)
+ return;
+ bputc(svg->fb, '\n');
+}
+
+static struct svg_element *svg_push_internal(struct svg *svg, uns type)
+{
+ ASSERT(!svg->str_fb);
+
+ struct svg_element *parent = svg_element_this_or_null(svg);
+ if (parent)
+ {
+ ASSERT(parent->type == XML_NODE_ELEM);
+ parent->flags |= SVG_EF_HAS_CHILDREN;
+ if (!(parent->flags & SVG_EF_START_TAG))
+ svg_start_tag(svg, parent);
+ }
+
+ mp_push(svg->pool);
+ struct svg_element *e = mp_alloc(svg->pool, sizeof(*e));
+ e->type = type;
+ e->flags = 0;
+ e->name = NULL;
+ clist_init(&e->attrs);
+ *GARY_PUSH(svg->stack) = e;
+
+ if (parent)
+ {
+ if (parent->indent < 0 || (parent->flags & SVG_EF_FLAT))
+ e->indent = -1;
+ else
+ e->indent = parent->indent + 1;
+ }
+ else
+ e->indent = 0;
+
+ return e;
+}
+
+struct svg_element *svg_push_element(struct svg *svg, const char *name)
+{
+ struct svg_element *e = svg_push_internal(svg, XML_NODE_ELEM);
+ e->name = mp_strdup(svg->pool, name);
+ return e;
+}
+
+struct svg_element *svg_push_chars(struct svg *svg)
+{
+ return svg_push_internal(svg, XML_NODE_CHARS);
+}
+
+struct svg_element *svg_push_comment(struct svg *svg)
+{
+ return svg_push_internal(svg, XML_NODE_COMMENT);
+}
+
+void svg_pop(struct svg *svg)
+{
+ struct svg_element *e = svg_element_this(svg);
+ ASSERT(!svg->str_fb);
+
+ switch (e->type)
+ {
+ case XML_NODE_ELEM:
+ if (!(e->flags & SVG_EF_START_TAG))
+ {
+ // Will recognize that the element has no children and an auto-closing tag
+ svg_start_tag(svg, e);
+ }
+ else
+ {
+ svg_indent_start(svg, e, SVG_INDENT_CLOSE);
+ bprintf(svg->fb, "</%s>", e->name);
+ svg_indent_end(svg, e, SVG_INDENT_CLOSE);
+ }
+ break;
+ case XML_NODE_CHARS:
+ ASSERT(e->name);
+ svg_indent_start(svg, e, SVG_INDENT_OPEN);
+ svg_escape_string(svg, e->name);
+ svg_indent_end(svg, e, SVG_INDENT_CLOSE);
+ break;
+ case XML_NODE_COMMENT:
+ ASSERT(e->name);
+ svg_indent_start(svg, e, SVG_INDENT_OPEN);
+ bputs(svg->fb, "<!-- ");
+ bputs(svg->fb, e->name);
+ bputs(svg->fb, " -->");
+ svg_indent_end(svg, e, SVG_INDENT_CLOSE);
+ break;
+ default:
+ ASSERT(0);
+ }
+
+ mp_pop(svg->pool);
+ GARY_POP(svg->stack);
+}
+
+static void svg_escape_string(struct svg *svg, const char *str)
+{
+ for (const char *c = str; *c; c++)
+ switch (*c)
+ {
+ case '"':
+ bputs(svg->fb, """);
+ break;
+ case '\'':
+ bputs(svg->fb, "'");
+ break;
+ case '<':
+ bputs(svg->fb, "<");
+ break;
+ case '>':
+ bputs(svg->fb, ">");
+ break;
+ case '&':
+ bputs(svg->fb, "&");
+ break;
+ default:
+ bputc(svg->fb, *c);
+ }
+}
+
+static void svg_start_tag(struct svg *svg, struct svg_element *e)
+{
+ ASSERT(!(e->flags & SVG_EF_START_TAG));
+ e->flags |= SVG_EF_START_TAG;
+
+ svg_indent_start(svg, e, SVG_INDENT_OPEN);
+ bputc(svg->fb, '<');
+ bputs(svg->fb, e->name);
+
+ CLIST_FOR_EACH(struct svg_attr *, a, e->attrs)
+ {
+ bputc(svg->fb, ' ');
+ bputs(svg->fb, a->key);
+ bputs(svg->fb, "=\"");
+ svg_escape_string(svg, a->val);
+ bputc(svg->fb, '"');
+ }
+
+ if (e->flags & SVG_EF_HAS_CHILDREN)
+ {
+ bputc(svg->fb, '>');
+ svg_indent_end(svg, e, SVG_INDENT_OPEN);
+ }
+ else
+ {
+ bputs(svg->fb, " />");
+ svg_indent_end(svg, e, SVG_INDENT_CLOSE);
+ }
+}
+
+void svg_set_attr_ref(struct svg *svg, const char *key, const char *val)
+{
+ struct svg_element *e = svg_element_this(svg);
+ ASSERT(e->type == XML_NODE_ELEM);
+ ASSERT(!(e->flags & SVG_EF_START_TAG));
+ ASSERT(!svg->str_fb);
+
+ CLIST_FOR_EACH(struct svg_attr *, a, e->attrs)
+ if (!strcmp(a->key, key))
+ {
+ a->val = val;
+ return;
+ }
+
+ struct svg_attr *a = mp_alloc(svg->pool, sizeof(*a));
+ a->key = mp_strdup(svg->pool, key);
+ a->val = val;
+ clist_add_tail(&e->attrs, &a->n);
+}
+
+void svg_set_attr(struct svg *svg, const char *key, const char *val)
+{
+ svg_set_attr_ref(svg, key, mp_strdup(svg->pool, val));
+}
+
+void svg_set_attr_int(struct svg *svg, const char *key, int val)
+{
+ svg_set_attr_ref(svg, key, mp_printf(svg->pool, "%d", val));
+}
+
+void svg_set_attr_float(struct svg *svg, const char *key, double val)
+{
+ svg_set_attr_ref(svg, key, mp_printf(svg->pool, "%.6g", val));
+}
+
+void svg_set_attr_dimen(struct svg *svg, const char *key, double val)
+{
+ svg_set_attr_ref(svg, key, mp_printf(svg->pool, "%.6g", val * svg->scale));
+}
+
+void svg_set_attr_color(struct svg *svg, const char *key, color_t color)
+{
+ svg_set_attr_ref(svg, key, mp_printf(svg->pool, "#%06x", color));
+}
+
+void svg_set_attr_format(struct svg *svg, const char *key, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ svg_set_attr_ref(svg, key, mp_vprintf(svg->pool, fmt, args));
+ va_end(args);
+}
+
+struct fastbuf *svg_fb_open(struct svg *svg)
+{
+ ASSERT(!svg->str_fb);
+ fbpool_start(svg->fb_pool, svg->pool, 0);
+ svg->str_fb = (struct fastbuf *) svg->fb_pool;
+ return svg->str_fb;
+}
+
+const char *svg_fb_close(struct svg *svg)
+{
+ ASSERT(svg->str_fb);
+ bputc(svg->str_fb, 0);
+ const char *str = fbpool_end(svg->fb_pool);
+ svg->str_fb = NULL;
+ return str;
+}
+
+void svg_fb_close_as_attr(struct svg *svg, const char *key)
+{
+ const char *val = svg_fb_close(svg);
+ svg_set_attr_ref(svg, key, val);
+}
+
+struct svg_element *svg_push_path(struct svg *svg)
+{
+ struct svg_element *e = svg_push_element(svg, "path");
+ svg_fb_open(svg);
+
+ // Construct the path, then call svg_path_end() and finally svg_pop()
+ // CAVEAT: Do not set attributes before calling svg_path_end()
+ return e;
+}
+
+void svg_path_end(struct svg *svg)
+{
+ svg_fb_close_as_attr(svg, "d");
+}
+
+static void svg_path_printf(struct svg *svg, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (btell(svg->str_fb))
+ bputc(svg->str_fb, ' ');
+ vbprintf(svg->str_fb, fmt, args);
+ va_end(args);
+}
+
+void svg_path_move_to(struct svg *svg, double x, double y)
+{
+ svg_path_printf(svg, "M %.6g %.6g", x * svg->scale, y * svg->scale);
+}
+
+void svg_path_move_to_rel(struct svg *svg, double x, double y)
+{
+ svg_path_printf(svg, "m %.6g %.6g", x * svg->scale, y * svg->scale);
+}
+
+void svg_path_line_to(struct svg *svg, double x, double y)
+{
+ svg_path_printf(svg, "L %.6g %.6g", x * svg->scale, y * svg->scale);
+}
+
+void svg_path_line_to_rel(struct svg *svg, double x, double y)
+{
+ svg_path_printf(svg, "l %.6g %.6g", x * svg->scale, y * svg->scale);
+}
+
+void svg_path_close(struct svg *svg)
+{
+ svg_path_printf(svg, "Z");
+}
--- /dev/null
+/*
+ * Hic Est Leo -- SVG Output
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_SVG_H
+#define _BRUM_SVG_H
+
+// FIXME: Passing SVG pointers everywhere is ugly, using global context less so.
+
+struct svg {
+ struct fastbuf *fb;
+ struct svg_element **stack;
+ struct mempool *pool;
+ struct fbpool *fb_pool;
+ struct fastbuf *str_fb; // fb_pool cast to the right type
+ double scale; // 1mm is converted to this many px
+ uns icon_counter;
+ struct icon_table *icon_hash;
+ uns pattern_counter;
+};
+
+struct svg_element {
+ byte type; // XML_NODE_xxx
+ byte flags; // SVG_EF_xxx
+ byte rfu[2];
+ int indent; // -1 = inline
+ const char *name;
+ clist attrs;
+};
+
+struct svg_attr {
+ cnode n;
+ const char *key, *val;
+};
+
+enum svg_element_flags {
+ SVG_EF_FLAT = 1,
+ SVG_EF_START_TAG = 2, // Internal: start tag has been already printed
+ SVG_EF_HAS_CHILDREN = 4,
+};
+
+struct svg *svg_open(char *filename);
+void svg_close(struct svg *svg);
+
+struct svg_element *svg_push_element(struct svg *svg, const char *name);
+struct svg_element *svg_push_chars(struct svg *svg);
+struct svg_element *svg_push_comment(struct svg *svg);
+void svg_pop(struct svg *svg);
+
+void svg_set_attr(struct svg *svg, const char *key, const char *val);
+void svg_set_attr_ref(struct svg *svg, const char *key, const char *val);
+void svg_set_attr_int(struct svg *svg, const char *key, int val);
+void svg_set_attr_float(struct svg *svg, const char *key, double val);
+void svg_set_attr_dimen(struct svg *svg, const char *key, double val);
+void svg_set_attr_color(struct svg *svg, const char *key, color_t color);
+void svg_set_attr_format(struct svg *svg, const char *key, const char *fmt, ...) FORMAT_CHECK(printf,3,4);
+
+struct fastbuf *svg_fb_open(struct svg *svg);
+const char *svg_fb_close(struct svg *svg);
+void svg_fb_close_as_attr(struct svg *svg, const char *key);
+
+struct svg_element *svg_push_path(struct svg *svg);
+void svg_path_end(struct svg *svg);
+void svg_path_move_to(struct svg *svg, double x, double y);
+void svg_path_move_to_rel(struct svg *svg, double x, double y);
+void svg_path_line_to(struct svg *svg, double x, double y);
+void svg_path_line_to_rel(struct svg *svg, double x, double y);
+void svg_path_close(struct svg *svg);
+
+/* svg-icon.c */
+
+struct svg_icon {
+ uns id;
+ double width, height;
+ struct xml_context *ctx;
+ struct xml_node *root;
+ clist patterns;
+ char name[1];
+};
+
+struct svg_icon *svg_icon_load(struct svg *svg, const char *name);
+void svg_icon_dump_library(struct svg *svg);
+
+void svg_icon_init(struct svg *svg); // Called from svg_open()
+void svg_icon_cleanup(struct svg *svg); // Called from svg_close()
+
+struct svg_icon_request {
+ struct svg_icon *icon;
+ double x, y;
+ double width, height;
+};
+
+void svg_icon_put(struct svg *svg, struct svg_icon_request *sir);
+
+struct svg_pattern_request {
+ struct svg_icon *icon;
+ double width, height;
+};
+
+struct svg_pattern {
+ cnode n;
+ uns id;
+ struct svg_icon *icon;
+ double width, height;
+ char *paint_server;
+};
+
+struct svg_pattern *svg_icon_to_pattern(struct svg *svg, struct svg_pattern_request *spr);
+
+#endif
--- /dev/null
+/*
+ * Hic Est Leo -- Line and Area Symbolizer
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/fastbuf.h>
+#include <ucw/mempool.h>
+
+#include <math.h>
+#include <stdio.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "sym.h"
+
+static void sym_line_attrs(struct sym_line *l, struct svg *svg)
+{
+ svg_set_attr(svg, "fill", "none");
+ svg_set_attr_color(svg, "stroke", l->color);
+ svg_set_attr_dimen(svg, "stroke-width", l->width);
+ if (l->opacity != 1)
+ svg_set_attr_float(svg, "stroke-opacity", l->opacity);
+
+ switch (l->line_cap)
+ {
+ case VALUE_NONE:
+ // This is the default (butt)
+ break;
+ case VALUE_ROUND:
+ case VALUE_SQUARE:
+ svg_set_attr(svg, "stroke-linecap", osm_val_decode(l->line_cap));
+ break;
+ default:
+ osm_obj_warn(l->s.o, "Unknown stroke-linecap: %s", osm_val_decode(l->line_cap));
+ }
+
+ switch (l->line_join)
+ {
+ case VALUE_MITER:
+ // This is the default
+ if (l->miter_limit != 4)
+ svg_set_attr_float(svg, "stroke-miterlimit", l->miter_limit);
+ break;
+ case VALUE_ROUND:
+ case VALUE_BEVEL:
+ svg_set_attr(svg, "stroke-linejoin", osm_val_decode(l->line_join));
+ break;
+ default:
+ osm_obj_warn(l->s.o, "Unknown stroke-linejoin: %s", osm_val_decode(l->line_join));
+ }
+
+ if (l->dash_pattern)
+ {
+ struct fastbuf *fb = svg_fb_open(svg);
+ for (uns i=0; i < GARY_SIZE(l->dash_pattern); i++)
+ {
+ if (i)
+ bputc(fb, ',');
+ // FIXME: This is dimension-sensitive
+ // FIXME: Also, inkscape doesn't handle units in dash lengths
+ bprintf(fb, "%.6g", l->dash_pattern[i]);
+ }
+ svg_fb_close_as_attr(svg, "stroke-dasharray");
+ if (l->dash_offset)
+ svg_set_attr_float(svg, "stroke-dashoffset", l->dash_offset);
+ }
+}
+
+static void append_node_list(struct svg *svg, clist *list)
+{
+ struct osm_node *first = NULL;
+ OSM_FOR_EACH_BEGIN(struct osm_node *, n, *list)
+ {
+ if (!first)
+ {
+ first = n;
+ svg_path_move_to(svg, n->x, n->y);
+ }
+ else if (n == first)
+ svg_path_close(svg);
+ else
+ svg_path_line_to(svg, n->x, n->y);
+ }
+ OSM_FOR_EACH_END;
+}
+
+static void make_path(struct osm_object *o, struct svg *svg)
+{
+ svg_push_path(svg);
+ switch (o->type)
+ {
+ case OSM_TYPE_WAY:
+ {
+ struct osm_way *w = (struct osm_way *) o;
+ append_node_list(svg, &w->nodes);
+ break;
+ }
+ case OSM_TYPE_MULTIPOLYGON:
+ {
+ struct osm_multipolygon *m = (struct osm_multipolygon *) o;
+ CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
+ append_node_list(svg, &b->nodes);
+ break;
+ }
+ default:
+ ASSERT(0);
+ }
+ svg_path_end(svg);
+}
+
+static void sym_line_draw(struct symbol *sym, struct svg *svg)
+{
+ struct sym_line *l = (struct sym_line *) sym;
+ make_path(sym->o, svg);
+ sym_line_attrs(l, svg);
+ svg_pop(svg);
+}
+
+static void sym_line_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
+{
+ if (o->type != OSM_TYPE_WAY && o->type != OSM_TYPE_MULTIPOLYGON)
+ return;
+
+ struct sym_line *main_sl = NULL;
+
+ // Line and its casing are two similar objects
+ for (uns casing=0; casing<2; casing++)
+ {
+ double w;
+ if (!style_get_number(si, PROP_WIDTH + casing, &w))
+ continue;
+
+ struct sym_line *sl = sym_line_new(o);
+ sl->width = w;
+
+ if (casing)
+ {
+ if (!main_sl)
+ {
+ osm_obj_warn(o, "Casing around no line");
+ continue;
+ }
+ sl->width += main_sl->width;
+ }
+ else
+ main_sl = sl;
+
+ sl->color = 0x808080;
+ style_get_color(si, PROP_FILL_COLOR, &sl->color);
+ style_get_color(si, PROP_COLOR + casing, &sl->color);
+
+ sl->opacity = 1;
+ style_get_number(si, PROP_OPACITY + casing, &sl->opacity);
+
+ sl->line_cap = style_get_ident(si, PROP_LINECAP + casing) ? : VALUE_NONE;
+ sl->line_join = style_get_ident(si, PROP_LINEJOIN + casing) ? : VALUE_ROUND;
+ sl->miter_limit = 10;
+ style_get_number(si, PROP_MITERLIMIT + casing, &sl->miter_limit);
+
+ struct style_prop *dashes = style_get(si, PROP_DASHES + casing);
+ if (!dashes || dashes->type == PROP_TYPE_IDENT && dashes->val.id == VALUE_NONE)
+ ;
+ else if (dashes->type == PROP_TYPE_LIST)
+ {
+ GARY_INIT_ALLOC(sl->dash_pattern, 0, mp_get_allocator(sym_mp));
+ CLIST_FOR_EACH(struct style_val_list_entry *, e, *dashes->val.list)
+ {
+ if (e->val.type == PROP_TYPE_NUMBER)
+ *GARY_PUSH(sl->dash_pattern) = e->val.val.number;
+ else
+ {
+ osm_obj_warn(o, "Invalid dash pattern");
+ break;
+ }
+ }
+ style_get_number(si, PROP_DASHES_OFFSET + casing, &sl->dash_offset);
+ }
+ else
+ osm_obj_warn(o, "Invalid dash pattern");
+
+ sym_plan(&sl->s, sym_zindex(o, si, casing ? 2 : 3));
+ }
+}
+
+struct symbolizer symbolizer_line = {
+ .name = "line",
+ .draw = sym_line_draw,
+ .gen = sym_line_gen,
+};
+
+struct sym_line *sym_line_new(struct osm_object *o)
+{
+ return sym_new(SYMBOLIZER_LINE, o, sizeof(struct sym_line));
+}
+
+/*** Images along line ***/
+
+static void lineimg_node_list(struct sym_lineimg *sli, clist *nodes, struct svg *svg)
+{
+ double phase = sli->phase;
+ double step = sli->sir.width + sli->spacing;
+ struct osm_node *prev = NULL;
+ OSM_FOR_EACH_BEGIN(struct osm_node *, n, *nodes)
+ {
+ if (prev)
+ {
+ double dx = n->x - prev->x;
+ double dy = n->y - prev->y;
+ double len = hypot(dx, dy);
+ double dist = 0;
+ while (len - dist > 1e-10)
+ {
+ // FIXME: Rotate images along the path?
+ double next = MIN(len - dist, step - phase);
+ dist += next;
+ phase += next;
+ if (step - phase < 1e-10)
+ {
+ struct svg_icon_request sir = sli->sir;
+ sir.x = prev->x + dx * dist / len;
+ sir.y = prev->y + dy * dist / len;
+ svg_icon_put(svg, &sir);
+ phase -= step;
+ }
+ }
+ }
+ prev = n;
+ }
+ OSM_FOR_EACH_END;
+}
+
+static void sym_lineimg_draw(struct symbol *sym, struct svg *svg)
+{
+ struct sym_lineimg *li = (struct sym_lineimg *) sym;
+ struct osm_object *o = li->s.o;
+
+ switch (o->type)
+ {
+ case OSM_TYPE_WAY:
+ {
+ struct osm_way *w = (struct osm_way *) o;
+ lineimg_node_list(li, &w->nodes, svg);
+ break;
+ }
+ case OSM_TYPE_MULTIPOLYGON:
+ {
+ struct osm_multipolygon *m = (struct osm_multipolygon *) o;
+ CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
+ lineimg_node_list(li, &b->nodes, svg);
+ break;
+ }
+ default:
+ ASSERT(0);
+ }
+}
+
+static void sym_lineimg_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
+{
+ if (o->type != OSM_TYPE_WAY && o->type != OSM_TYPE_MULTIPOLYGON)
+ return;
+
+ osm_val_t icon_name = style_get_string(si, PROP_REPEAT_IMAGE);
+ if (!icon_name)
+ return;
+
+ struct sym_lineimg *sli = sym_lineimg_new(o);
+
+ struct svg_icon *icon = svg_icon_load(svg, osm_val_decode(icon_name));
+ sli->sir.icon = icon;
+ sli->sir.width = icon->width;
+ sli->sir.height = icon->height;
+ style_scale(si, &sli->sir.width, &sli->sir.height, PROP_REPEAT_IMAGE_WIDTH, PROP_REPEAT_IMAGE_HEIGHT);
+
+ // FIXME: Better handling of defaults in style_get_number()
+#if 0
+ // FIXME: align and offset are not supported yet
+ sli->align = style_get_ident(si, PROP_REPEAT_IMAGE_ALIGN);
+ sli->offset = 0;
+ style_get_number(si, PROP_REPEAT_IMAGE_OFFSET, &sli->offset);
+#endif
+ sli->spacing = 0;
+ style_get_number(si, PROP_REPEAT_IMAGE_SPACING, &sli->spacing);
+ sli->phase = 0;
+ style_get_number(si, PROP_REPEAT_IMAGE_PHASE, &sli->phase);
+
+ sym_plan(&sli->s, sym_zindex(o, si, 3));
+}
+
+struct symbolizer symbolizer_lineimg = {
+ .name = "lineimg",
+ .draw = sym_lineimg_draw,
+ .gen = sym_lineimg_gen,
+};
+
+struct sym_lineimg *sym_lineimg_new(struct osm_object *o)
+{
+ return sym_new(SYMBOLIZER_LINEIMG, o, sizeof(struct sym_lineimg));
+}
+
+/*** Areas ***/
+
+static void sym_area_draw(struct symbol *sym, struct svg *svg)
+{
+ struct sym_area *a = (struct sym_area *) sym;
+
+ for (int i=0; i<2; i++)
+ {
+ if (!i)
+ {
+ if (a->fill_color == COLOR_NONE)
+ continue;
+ make_path(sym->o, svg);
+ svg_set_attr_color(svg, "fill", a->fill_color);
+ }
+ else
+ {
+ if (!a->fill_pattern)
+ continue;
+ make_path(sym->o, svg);
+ svg_set_attr(svg, "fill", a->fill_pattern->paint_server);
+ }
+ if (a->fill_opacity != 1)
+ svg_set_attr_float(svg, "opacity", a->fill_opacity);
+ if (sym->o->type == OSM_TYPE_MULTIPOLYGON)
+ svg_set_attr(svg, "fill-rule", "evenodd");
+ svg_pop(svg);
+ }
+}
+
+static void sym_area_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
+{
+ if (!(o->type == OSM_TYPE_WAY && osm_way_cyclic_p((struct osm_way *) o) ||
+ o->type == OSM_TYPE_MULTIPOLYGON))
+ return;
+
+ color_t color;
+ if (!style_get_color(si, PROP_FILL_COLOR, &color))
+ color = COLOR_NONE;
+
+ osm_val_t pattern = style_get_string(si, PROP_FILL_PATTERN);
+ struct svg_pattern *patt = NULL;
+ if (pattern)
+ {
+ struct svg_icon *icon = svg_icon_load(svg, osm_val_decode(pattern));
+ struct svg_pattern_request spr = {
+ .icon = icon,
+ .width = icon->width,
+ .height = icon->height
+ };
+ style_scale(si, &spr.width, &spr.height, PROP_FILL_PATTERN_WIDTH, PROP_FILL_PATTERN_HEIGHT);
+ patt = svg_icon_to_pattern(svg, &spr);
+ }
+
+ if (color == COLOR_NONE && !patt)
+ return;
+
+ struct sym_area *sa = sym_area_new(o);
+ sa->fill_color = color;
+ sa->fill_pattern = patt;
+
+ sa->fill_opacity = 1;
+ style_get_number(si, PROP_FILL_OPACITY, &sa->fill_opacity);
+
+ sym_plan(&sa->s, sym_zindex(o, si, 1));
+}
+
+struct symbolizer symbolizer_area = {
+ .name = "area",
+ .draw = sym_area_draw,
+ .gen = sym_area_gen,
+};
+
+struct sym_area *sym_area_new(struct osm_object *o)
+{
+ return sym_new(SYMBOLIZER_AREA, o, sizeof(struct sym_area));
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Point and Icon Symbolizer
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+
+#include <stdio.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "sym.h"
+#include "svg.h"
+
+static void sym_point_draw(struct symbol *sym, struct svg *svg)
+{
+ struct sym_point *p = (struct sym_point *) sym;
+ struct osm_object *o = sym->o;
+ struct osm_node *n = (struct osm_node *) o;
+
+ if (o->type != OSM_TYPE_NODE)
+ {
+ msg(L_ERROR, "Point symbolizer works only on nodes. It's a bug, isn't it?");
+ return;
+ }
+
+ switch (p->shape)
+ {
+ case VALUE_CIRCLE:
+ svg_push_element(svg, "circle");
+ svg_set_attr_dimen(svg, "cx", n->x);
+ svg_set_attr_dimen(svg, "cy", n->y);
+ svg_set_attr_dimen(svg, "r", p->size / 2);
+ break;
+ // FIXME: Other shapes
+ default:
+ osm_obj_warn(o, "Unknown symbol-shape %s", osm_val_decode(p->shape));
+ return;
+ }
+
+ // FIMXE: Use COLOR_NONE instead of do_stroke and do_fill
+ if (p->do_stroke)
+ {
+ svg_set_attr_color(svg, "stroke", p->stroke_color);
+ svg_set_attr_dimen(svg, "stroke-width", p->stroke_width);
+ if (p->stroke_opacity != 1)
+ svg_set_attr_float(svg, "stroke-opacity", p->stroke_opacity);
+ }
+ if (p->do_fill)
+ {
+ svg_set_attr_color(svg, "fill", p->fill_color);
+ if (p->fill_opacity != 1)
+ svg_set_attr_float(svg, "fill-opacity", p->fill_opacity);
+ }
+ else
+ svg_set_attr(svg, "fill", "none");
+ svg_pop(svg);
+}
+
+static void sym_point_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
+{
+ if (o->type != OSM_TYPE_NODE)
+ return;
+
+ osm_val_t shape = style_get_ident(si, PROP_SYMBOL_SHAPE);
+ if (!shape)
+ return;
+
+ struct sym_point *sp = sym_point_new(o);
+ sp->shape = shape;
+
+ sp->size = 1;
+ style_get_number(si, PROP_SYMBOL_SIZE, &sp->size);
+
+ sp->stroke_width = 0.1;
+ sp->stroke_color = 0xffc800;
+ sp->stroke_opacity = 1;
+ if (style_get_color(si, PROP_SYMBOL_STROKE_COLOR, &sp->stroke_color) |
+ style_get_number(si, PROP_SYMBOL_STROKE_WIDTH, &sp->stroke_width))
+ {
+ sp->do_stroke = 1;
+ style_get_number(si, PROP_SYMBOL_STROKE_OPACITY, &sp->stroke_opacity);
+ }
+
+ sp->fill_color = 0x0000ff;
+ sp->fill_opacity = 1;
+ if (style_get_color(si, PROP_SYMBOL_FILL_COLOR, &sp->fill_color) || !sp->do_stroke)
+ {
+ sp->do_fill = 1;
+ style_get_number(si, PROP_SYMBOL_FILL_OPACITY, &sp->fill_opacity);
+ }
+
+ sym_plan(&sp->s, sym_zindex(o, si, 4));
+}
+
+struct symbolizer symbolizer_point = {
+ .name = "point",
+ .draw = sym_point_draw,
+ .gen = sym_point_gen,
+};
+
+struct sym_point *sym_point_new(struct osm_object *o)
+{
+ return sym_new(SYMBOLIZER_POINT, o, sizeof(struct sym_point));
+}
+
+/*** Icons ***/
+
+static void sym_icon_draw(struct symbol *sym, struct svg *svg)
+{
+ struct sym_icon *i = (struct sym_icon *) sym;
+ svg_icon_put(svg, &i->sir);
+}
+
+static void sym_icon_gen(struct osm_object *o, struct style_info *si, struct svg *svg)
+{
+ if (o->type != OSM_TYPE_NODE && o->type != OSM_TYPE_WAY && o->type != OSM_TYPE_MULTIPOLYGON)
+ return;
+
+ osm_val_t image = style_get_string(si, PROP_ICON_IMAGE);
+ if (!image)
+ return;
+
+ struct sym_icon *sic = sym_icon_new(o);
+ struct svg_icon *icon = svg_icon_load(svg, osm_val_decode(image));
+ struct svg_icon_request *sir = &sic->sir;
+ sir->icon = icon;
+
+ if (!osm_obj_center(o, &sir->x, &sir->y))
+ return;
+
+ sir->width = icon->width;
+ sir->height = icon->height;
+ style_scale(si, &sir->width, &sir->height, PROP_ICON_WIDTH, PROP_ICON_HEIGHT);
+
+ // FIXME
+ // sir->opacity = 1;
+ // style_get_number(si, PROP_ICON_OPACITY, &sir->opacity);
+
+ sym_plan(&sic->s, sym_zindex(o, si, 4));
+}
+
+struct symbolizer symbolizer_icon = {
+ .name = "icon",
+ .draw = sym_icon_draw,
+ .gen = sym_icon_gen,
+};
+
+struct sym_icon *sym_icon_new(struct osm_object *o)
+{
+ return sym_new(SYMBOLIZER_ICON, o, sizeof(struct sym_icon));
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Text Symbolizer
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/stkstring.h>
+
+#include <stdio.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <pango/pangoft2.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "sym.h"
+#include "map.h"
+
+/*** Fonts ***/
+
+struct text_font {
+ const char *family;
+ double size;
+ osm_val_t weight;
+ osm_val_t style;
+ PangoFontDescription *pango_font_desc;
+ char key[1];
+};
+
+#define HASH_NODE struct text_font
+#define HASH_PREFIX(x) text_font_##x
+#define HASH_KEY_ENDSTRING key
+#define HASH_WANT_LOOKUP
+#define HASH_LOOKUP_DETECT_NEW
+#define HASH_USE_POOL sym_mp
+#include <ucw/hashtable.h>
+
+static PangoFontMap *pango_font_map;
+static PangoContext *pango_context;
+
+static double pt_to_mm(double pt)
+{
+ return pt / 72 * 25.4;
+}
+
+static double mm_to_pt(double mm)
+{
+ return mm / 25.4 * 72;
+}
+
+static double pango_to_mm(double pango)
+{
+ return pt_to_mm(pango / PANGO_SCALE);
+}
+
+static double mm_to_pango(double mm)
+{
+ return mm_to_pt(mm) * PANGO_SCALE;
+}
+
+/*
+ * Pango tries to do pixel-based optimizations in low resolutions.
+ * Work around it by scaling everything up.
+ */
+#define FONT_HACK_FACTOR 30
+
+static struct text_font *font_get(struct text_font *req)
+{
+ char *key = stk_printf("%s:%.6g:%u:%u", req->family, req->size, req->weight, req->style);
+ int is_new = 0;
+ struct text_font *font = text_font_lookup(key, &is_new);
+ if (is_new)
+ {
+ msg(L_DEBUG, "Loading font %s (size %.6g, weight %s, style %s)", req->family, req->size, osm_val_decode(req->weight), osm_val_decode(req->style));
+ font->family = mp_strdup(sym_mp, req->family);
+ font->size = req->size;
+ font->weight = req->weight;
+ font->style = req->style;
+
+ PangoFontDescription *desc;
+ desc = pango_font_description_new();
+ ASSERT(desc);
+ pango_font_description_set_family(desc, font->family);
+ if (font->weight != VALUE_NORMAL)
+ pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
+ if (font->style != VALUE_NORMAL)
+ pango_font_description_set_style(desc, PANGO_STYLE_ITALIC);
+ pango_font_description_set_size(desc, mm_to_pango(font->size));
+ font->pango_font_desc = desc;
+
+#if 1
+ // FIXME
+ PangoFont *pfont = pango_font_map_load_font(pango_font_map, pango_context, desc);
+ ASSERT(pfont);
+ PangoFontDescription *d2 = pango_font_describe(pfont);
+ ASSERT(d2);
+ msg(L_DEBUG, "Font desc: %s", pango_font_description_to_string(d2));
+
+ PangoFontMetrics *fm = pango_font_get_metrics(pfont, NULL);
+ ASSERT(fm);
+ msg(L_DEBUG, "Font metrics: asc=%.6g desc=%.6g", pango_to_mm(pango_font_metrics_get_ascent(fm)), pango_to_mm(pango_font_metrics_get_descent(fm)));
+#endif
+ }
+ return font;
+}
+
+static void font_init(void)
+{
+ text_font_init();
+
+ pango_font_map = pango_ft2_font_map_new();
+ ASSERT(pango_font_map);
+ pango_ft2_font_map_set_resolution((PangoFT2FontMap *) pango_font_map, 72 * FONT_HACK_FACTOR, 72 * FONT_HACK_FACTOR);
+ pango_context = pango_font_map_create_context(PANGO_FONT_MAP(pango_font_map));
+ ASSERT(pango_context);
+}
+
+static void text_size(struct sym_text *st)
+{
+ PangoLayout *layout = pango_layout_new(pango_context);
+ pango_layout_set_font_description(layout, st->font->pango_font_desc);
+ pango_layout_set_text(layout, osm_val_decode(st->text), -1);
+ pango_layout_context_changed(layout);
+
+ PangoRectangle ext;
+ pango_layout_get_extents(layout, NULL, &ext);
+ // st->tx = pango_to_mm(ext.x) / FONT_HACK_FACTOR;
+ // st->ty = pango_to_mm(ext.y) / FONT_HACK_FACTOR;
+ st->tw = pango_to_mm(ext.width) / FONT_HACK_FACTOR;
+ st->th = pango_to_mm(pango_layout_get_baseline(layout)) / FONT_HACK_FACTOR;
+ st->td = pango_to_mm(ext.height) / FONT_HACK_FACTOR - st->th;
+
+ g_object_unref(layout);
+}
+
+/*** Elimination of duplicate texts ***/
+
+// FIXME: Get rid of globals
+#define DUP_TILE_SIZE 10
+static int dup_tiles_w, dup_tiles_h;
+static struct sym_text **dup_tiles;
+
+static void text_dup_init(void)
+{
+ dup_tiles_w = 1 + page_map_width / DUP_TILE_SIZE;
+ dup_tiles_h = 1 + page_map_height / DUP_TILE_SIZE;
+ dup_tiles = xmalloc_zero(sizeof(struct sym_text *) * dup_tiles_w * dup_tiles_h);
+ msg(L_DEBUG, "Allocated text tiles: %u x %u", dup_tiles_w, dup_tiles_h);
+}
+
+static int text_dup_coord(int x, int y)
+{
+ ASSERT(x >= 0 && x < dup_tiles_w && y >= 0 && y < dup_tiles_h);
+ return x + y * dup_tiles_w;
+}
+
+static double text_quad_dist(struct sym_text *a, struct sym_text *b)
+{
+ double dx = a->x - b->x;
+ double dy = a->y - b->y;
+ return dx*dx + dy*dy;
+}
+
+static bool text_dup_detect(struct sym_text *t, struct style_info *si)
+{
+ // Out-of-frame texts are dropped immediately
+ double x = t->x - page_offset_x;
+ double y = t->y - page_offset_y;
+ if (x < 0 || x >= page_map_width ||
+ y < 0 || y >= page_map_height)
+ return 0;
+
+ int tile_x = x / DUP_TILE_SIZE;
+ int tile_y = y / DUP_TILE_SIZE;
+
+ double radius = 0;
+ style_get_number(si, PROP_TEXT_DUP_THRESHOLD, &radius);
+ if (radius)
+ {
+ // A rather simple-minded algorithm, but believed to be efficient enough.
+ int r_tiles = 1 + radius / DUP_TILE_SIZE;
+ int x_start = MAX(0, tile_x - r_tiles);
+ int x_stop = MIN(dup_tiles_w - 1, tile_x + r_tiles);
+ int y_start = MAX(0, tile_y - r_tiles);
+ int y_stop = MIN(dup_tiles_h - 1, tile_y + r_tiles);
+ for (int x = x_start; x <= x_stop; x++)
+ for (int y = y_start; y <= y_stop; y++)
+ {
+ for (struct sym_text *d = dup_tiles[text_dup_coord(x, y)]; d; d = d->next_in_tile)
+ if (d->text == t->text &&
+ text_quad_dist(d, t) <= radius*radius &&
+ d->text_color == t->text_color &&
+ d->font == t->font)
+ {
+ t->next_duplicate = d->next_duplicate;
+ d->next_duplicate = t;
+ return 0;
+ }
+ }
+ }
+
+ int tile_i = text_dup_coord(tile_x, tile_y);
+ t->next_in_tile = dup_tiles[tile_i];
+ dup_tiles[tile_i] = t;
+ return 1;
+}
+
+/*** Core of the symbolizer ***/
+
+static void prepare_text_element(struct sym_text *t, struct svg *svg)
+{
+ struct text_font *font = t->font;
+ svg_push_element(svg, "text");
+ svg_set_attr_dimen(svg, "x", t->x);
+ svg_set_attr_dimen(svg, "y", t->y);
+ svg_set_attr(svg, "font-family", font->family);
+ svg_set_attr_dimen(svg, "font-size", font->size);
+ if (font->weight != VALUE_NORMAL)
+ svg_set_attr(svg, "font-weight", osm_val_decode(font->weight));
+ if (font->style != VALUE_NORMAL)
+ svg_set_attr(svg, "font-style", osm_val_decode(font->style));
+}
+
+static void sym_text_draw(struct symbol *sym, struct svg *svg)
+{
+ struct sym_text *t = (struct sym_text *) sym;
+
+ if (t->next_duplicate)
+ {
+ // If there is a cluster of duplicate texts, average their positions
+ double sx = 0, sy = 0;
+ uns nn = 0;
+ for (struct sym_text *u = t; u; u = u->next_duplicate)
+ {
+ sx += u->x;
+ sy += u->y;
+ nn++;
+ }
+ t->x = sx / nn;
+ t->y = sy / nn;
+ }
+
+ if (t->opacity != 1)
+ {
+ svg_push_element(svg, "g");
+ svg_set_attr_float(svg, "opacity", t->opacity);
+ }
+
+ if (t->halo_radius)
+ {
+ prepare_text_element(t, svg);
+ svg_set_attr(svg, "fill", "none");
+ svg_set_attr_color(svg, "stroke", t->halo_color);
+ svg_set_attr_dimen(svg, "stroke-width", t->halo_radius);
+ svg_set_attr(svg, "stroke-linecap", "round");
+ svg_set_attr(svg, "stroke-linejoin", "round");
+ if (t->halo_opacity != 1)
+ svg_set_attr_float(svg, "stroke-opacity", t->halo_opacity);
+ svg_push_chars(svg)->name = osm_val_decode(t->text);
+ svg_pop(svg);
+ svg_pop(svg);
+ }
+
+ prepare_text_element(t, svg);
+ svg_set_attr_color(svg, "fill", t->text_color);
+ svg_set_attr(svg, "stroke", "none");
+ svg_push_chars(svg)->name = osm_val_decode(t->text);
+ svg_pop(svg);
+ svg_pop(svg);
+
+#if 0
+ // Draw bounding box for debugging
+ svg_push_element(svg, "rect");
+ svg_set_attr(svg, "fill", "none");
+ svg_set_attr_color(svg, "stroke", 0x0000ff);
+ svg_set_attr_dimen(svg, "stroke-width", 0.2);
+ svg_set_attr_dimen(svg, "x", t->x);
+ svg_set_attr_dimen(svg, "y", t->y - t->th);
+ svg_set_attr_dimen(svg, "width", t->tw);
+ svg_set_attr_dimen(svg, "height", t->th + t->td);
+ svg_pop(svg);
+
+ svg_push_element(svg, "line");
+ svg_set_attr(svg, "fill", "none");
+ svg_set_attr_color(svg, "stroke", 0x0000ff);
+ svg_set_attr_dimen(svg, "stroke-width", 0.2);
+ svg_set_attr_dimen(svg, "x1", t->x);
+ svg_set_attr_dimen(svg, "y1", t->y);
+ svg_set_attr_dimen(svg, "x2", t->x + t->tw);
+ svg_set_attr_dimen(svg, "y2", t->y);
+ svg_pop(svg);
+#endif
+
+ if (t->opacity != 1)
+ svg_pop(svg);
+}
+
+static osm_val_t get_text(struct osm_object *o, struct style_info *si)
+{
+ struct style_prop *prop = style_get_and_check(si, PROP_TEXT, (1 << PROP_TYPE_STRING) | (1 << PROP_TYPE_IDENT));
+ if (!prop)
+ return 0;
+
+ if (prop->type == PROP_TYPE_IDENT && prop->val.id == VALUE_AUTO)
+ {
+ static const osm_key_t auto_text_keys[] = {
+ KEY_NAME_CZ, // FIXME: This should be configurable
+ KEY_NAME,
+ KEY_REF,
+ KEY_OPERATOR,
+ KEY_BRAND,
+ KEY_ADDR_HOUSENUMBER,
+ };
+ for (uns i=0; i < ARRAY_SIZE(auto_text_keys); i++)
+ {
+ osm_val_t val = osm_obj_find_tag(o, auto_text_keys[i]);
+ if (val)
+ return val;
+ }
+ return 0;
+ }
+
+ return osm_obj_find_tag(o, osm_key_encode(osm_val_decode(prop->val.id)));
+}
+
+static void get_text_attrs(struct sym_text *st, struct style_info *si)
+{
+ struct osm_object *o = st->s.o;
+ if (o->type == OSM_TYPE_WAY && osm_way_cyclic_p((struct osm_way *) o))
+ st->text_color = 0xc0c0c0; // FIXME: This is an ugly hack, do we need it?
+ else
+ st->text_color = 0xffffff;
+ style_get_color(si, PROP_TEXT_COLOR, &st->text_color);
+
+ struct text_font f = {
+ .family = "Helvetica",
+ .size = pt_to_mm(8),
+ };
+ osm_val_t fam = style_get_string(si, PROP_FONT_FAMILY);
+ if (fam)
+ f.family = osm_val_decode(fam);
+ style_get_number(si, PROP_FONT_SIZE, &f.size);
+ f.weight = style_get_ident(si, PROP_FONT_WEIGHT);
+ if (!f.weight)
+ f.weight = VALUE_NORMAL;
+ if (f.weight != VALUE_NORMAL && f.weight != VALUE_BOLD)
+ {
+ osm_obj_warn(o, "Unknown font-weight %s", osm_val_decode(f.weight));
+ f.weight = VALUE_NORMAL;
+ }
+ f.style = style_get_ident(si, PROP_FONT_STYLE);
+ if (!f.style)
+ f.style = VALUE_NORMAL;
+ if (f.style != VALUE_NORMAL && f.style != VALUE_ITALIC)
+ {
+ osm_obj_warn(o, "Unknown font-style %s", osm_val_decode(f.weight));
+ f.style = VALUE_NORMAL;
+ }
+ st->font = font_get(&f);
+
+ st->opacity = 1;
+ style_get_number(si, PROP_TEXT_OPACITY, &st->opacity);
+
+ st->halo_color = st->text_color ^ 0xffffff;
+ style_get_color(si, PROP_TEXT_HALO_COLOR, &st->halo_color);
+ st->halo_radius = 0;
+ style_get_number(si, PROP_TEXT_HALO_RADIUS, &st->halo_radius);
+ st->halo_opacity = 1;
+ style_get_number(si, PROP_TEXT_HALO_OPACITY, &st->halo_opacity);
+
+ double dx = 0, dy = 0;
+ style_get_number(si, PROP_TEXT_OFFSET_X, &dx);
+ style_get_number(si, PROP_TEXT_OFFSET_Y, &dy);
+ style_get_number(si, PROP_TEXT_OFFSET, &dy);
+ st->x += dx;
+ st->y -= dy;
+}
+
+static void text_fix_placement(struct sym_text *st)
+{
+ // Fix texts which do not fit on the paper
+ st->x = MIN(st->x, page_offset_x + page_map_width - st->tw);
+ st->x = MAX(st->x, page_offset_x);
+ st->y = MIN(st->y, page_offset_y + page_map_height - st->th);
+ st->y = MAX(st->y, page_offset_y + st->td);
+}
+
+static void sym_text_node(struct osm_object *o, struct style_info *si, osm_val_t text)
+{
+ struct osm_node *n = (struct osm_node *) o;
+
+ struct sym_text *st = sym_text_new(o);
+ st->text = text;
+ st->x = n->x;
+ st->y = n->y;
+
+ get_text_attrs(st, si);
+ text_size(st);
+
+ osm_val_t ah = style_get_ident(si, PROP_TEXT_ANCHOR_HORIZONTAL);
+ switch (ah)
+ {
+ case VALUE_LEFT:
+ st->x -= st->tw;
+ break;
+ case VALUE_CENTER:
+ st->x -= st->tw / 2;
+ break;
+ case 0:
+ case VALUE_RIGHT:
+ break;
+ default:
+ osm_obj_warn(o, "Unknown text-anchor-horizontal: %s", osm_val_decode(ah));
+ }
+
+ osm_val_t av = style_get_ident(si, PROP_TEXT_ANCHOR_VERTICAL);
+ switch (av)
+ {
+ case VALUE_ABOVE: // FIXME: What's the difference between above and top?
+ case VALUE_TOP:
+ st->y -= st->td;
+ break;
+ case VALUE_CENTER:
+ st->y -= (st->th + st->td) / 2;
+ // Fall thru
+ case 0:
+ case VALUE_BOTTOM:
+ case VALUE_BELOW:
+ st->y += st->th;
+ break;
+ default:
+ osm_obj_warn(o, "Unknown text-anchor-vertical: %s", osm_val_decode(av));
+ }
+
+ text_fix_placement(st);
+ if (!text_dup_detect(st, si))
+ {
+ msg(L_DEBUG, "Text <%s> dropped as duplicate", osm_val_decode(text));
+ return;
+ }
+
+ sym_plan(&st->s, sym_zindex(o, si, 5));
+}
+
+static void sym_text_center(struct osm_object *o, struct style_info *si, osm_val_t text, double x, double y)
+{
+ struct sym_text *st = sym_text_new(o);
+ st->text = text;
+ st->x = x;
+ st->y = y;
+
+ get_text_attrs(st, si);
+ text_size(st);
+ st->x -= st->tw / 2;
+ st->y += st->th - (st->th + st->td) / 2;
+ text_fix_placement(st);
+ sym_plan(&st->s, sym_zindex(o, si, 4.9));
+}
+
+static void sym_text_way(struct osm_object *o, struct style_info *si, osm_val_t text)
+{
+ double x, y;
+ osm_val_t tp = style_get_ident(si, PROP_TEXT_POSITION);
+
+ switch (tp)
+ {
+ case VALUE_CENTER:
+ if (osm_obj_center(o, &x, &y))
+ sym_text_center(o, si, text, x, y);
+ break;
+ case VALUE_LINE:
+ // FIXME
+ default:
+ osm_obj_warn(o, "Unknown text-position: %s", osm_val_decode(tp));
+ }
+}
+
+static void sym_text_mpg(struct osm_object *o, struct style_info *si, osm_val_t text)
+{
+ double x, y;
+ osm_val_t tp = style_get_ident(si, PROP_TEXT_POSITION);
+
+ switch (tp)
+ {
+ case VALUE_CENTER:
+ if (osm_obj_center(o, &x, &y))
+ sym_text_center(o, si, text, x, y);
+ break;
+ case VALUE_LINE:
+ // FIXME
+ default:
+ osm_obj_warn(o, "Unknown text-position: %s", osm_val_decode(tp));
+ }
+}
+
+static void sym_text_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
+{
+ osm_val_t text = get_text(o, si);
+ if (!text)
+ return;
+
+ switch (o->type)
+ {
+ case OSM_TYPE_NODE:
+ sym_text_node(o, si, text);
+ break;
+ case OSM_TYPE_WAY:
+ sym_text_way(o, si, text);
+ break;
+ case OSM_TYPE_MULTIPOLYGON:
+ sym_text_mpg(o, si, text);
+ break;
+ default:
+ osm_obj_warn(o, "Text symbolizer does not support this object type");
+ return;
+ }
+}
+
+static void sym_text_init(void)
+{
+ font_init();
+ text_dup_init();
+}
+
+struct symbolizer symbolizer_text = {
+ .name = "text",
+ .draw = sym_text_draw,
+ .gen = sym_text_gen,
+ .init = sym_text_init,
+};
+
+struct sym_text *sym_text_new(struct osm_object *o)
+{
+ return sym_new(SYMBOLIZER_TEXT, o, sizeof(struct sym_text));
+}
+
+// FIXME: Hack
+void scale_text(struct svg *svg, double x, double y, osm_val_t text)
+{
+ struct sym_text *st = sym_text_new(NULL);
+
+ struct text_font f = {
+ .family = "Times",
+ .weight = VALUE_NORMAL,
+ .style = VALUE_NORMAL,
+ .size = pt_to_mm(10),
+ };
+
+ st->text = text;
+ st->text_color = 0;
+ st->x = x;
+ st->y = y;
+ st->font = font_get(&f);
+ st->opacity = 1;
+ st->halo_color = 0xffffff;
+ st->halo_radius = 0.8;
+ st->halo_opacity = 1;
+ text_size(st);
+ st->x -= st->tw / 2;
+ sym_text_draw(&st->s, svg);
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Symbols
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/gary.h>
+#include <ucw/mempool.h>
+#include <ucw/stkstring.h>
+
+#include <stdio.h>
+
+#include "leo.h"
+#include "osm.h"
+#include "style.h"
+#include "sym.h"
+
+#undef CLAMP // FIXME: Fix in libucw?
+#define CLAMP(x,min,max) ({ typeof(x) _t=x; (_t < min) ? min : (_t > max) ? max : _t; }) /** Clip a number @x to interval [@min,@max] **/
+
+static struct symbolizer *symbolizers[] = {
+ [SYMBOLIZER_INVALID] = NULL,
+ [SYMBOLIZER_POINT] = &symbolizer_point,
+ [SYMBOLIZER_ICON] = &symbolizer_icon,
+ [SYMBOLIZER_LINE] = &symbolizer_line,
+ [SYMBOLIZER_AREA] = &symbolizer_area,
+ [SYMBOLIZER_TEXT] = &symbolizer_text,
+ [SYMBOLIZER_LINEIMG] = &symbolizer_lineimg,
+};
+
+struct mempool *sym_mp;
+
+struct sym_planned {
+ struct symbol *sym;
+ z_index_t zindex;
+};
+
+static struct sym_planned *sym_planned;
+
+static inline bool sym_before(struct sym_planned *x, struct sym_planned *y)
+{
+ COMPARE_LT(x->zindex, y->zindex);
+ COMPARE_LT(x->sym->o->type, y->sym->o->type);
+ COMPARE_LT(x->sym->o->id, y->sym->o->id);
+ return 0;
+}
+
+#define ASORT_PREFIX(x) sym_##x
+#define ASORT_KEY_TYPE struct sym_planned
+#define ASORT_LT(_x,_y) sym_before(&(_x), &(_y))
+#include <ucw/sorter/array-simple.h>
+
+void sym_init(void)
+{
+ sym_mp = mp_new(65536);
+ GARY_INIT(sym_planned, 0);
+
+ for (uns i = SYMBOLIZER_INVALID + 1; i < SYMBOLIZER_MAX; i++)
+ if (symbolizers[i]->init)
+ (*symbolizers[i]->init)();
+}
+
+void *sym_new(enum symbolizer_type type, struct osm_object *o, size_t size)
+{
+ struct symbol *s = mp_alloc_zero(sym_mp, size);
+ s->type = type;
+ s->o = o;
+ return s;
+}
+
+void sym_plan(struct symbol *sym, z_index_t zindex)
+{
+ struct sym_planned *p = GARY_PUSH(sym_planned);
+ p->sym = sym;
+ p->zindex = zindex;
+ if (debug_dump_symbols)
+ printf("--> planning symbol <%s> with z-index %u for %s\n",
+ symbolizers[sym->type]->name, zindex, STK_OSM_NAME(sym->o));
+}
+
+z_index_t sym_zindex(struct osm_object *o, struct style_info *si, double default_mzi)
+{
+ double zi = 0;
+ style_get_number(si, PROP_Z_INDEX, &zi);
+ double zi2 = CLAMP(zi, -100, 100);
+ if (zi2 != zi)
+ osm_obj_warn(o, "z-index clipped from %.6g to %.6g", zi, zi2);
+
+#if 0
+ // FIXME: object-z-index not used yet
+ double ozi = 0;
+ style_get_number(si, PROP_OBJECT_Z_INDEX, &ozi);
+ double ozi2 = CLAMP(ozi, -100, 100);
+ if (ozi2 != ozi)
+ osm_obj_warn(o, "object-z-index clipped from %.6g to %.6g", ozi, ozi2);
+#endif
+
+ double mzi = default_mzi;
+ if (mzi == 2)
+ {
+ // FIXME: This is a terrible hack.
+ style_get_number(si, PROP_CASING_MAJOR_Z_INDEX, &mzi);
+ }
+ else if (mzi == 4.9 || mzi == 5)
+ {
+ // FIXME: This is another terrible hack.
+ style_get_number(si, PROP_TEXT_MAJOR_Z_INDEX, &mzi);
+ }
+ else
+ style_get_number(si, PROP_MAJOR_Z_INDEX, &mzi);
+ double mzi2 = CLAMP(mzi, -100, 100);
+ if (mzi2 != mzi)
+ osm_obj_warn(o, "major-z-index clipped from %.6g to %.6g", mzi, mzi2);
+
+ return (z_index_t)(10000 + (z_index_t)(zi2 * 100) + 10000*( 10000 + (z_index_t)(mzi2 * 100) ));
+}
+
+void sym_from_style(struct osm_object *o, struct style_results *sr, struct svg *svg)
+{
+ for (uns i=0; i < sr->num_active_layers; i++)
+ {
+ for (uns j = SYMBOLIZER_INVALID + 1; j < SYMBOLIZER_MAX; j++)
+ if (symbolizers[j]->gen)
+ (symbolizers[j]->gen)(o, sr->layers[sr->active_layers[i]], svg);
+ }
+}
+
+static void sym_draw(struct symbol *sym, z_index_t zindex, struct svg *svg)
+{
+ ASSERT(sym->type && sym->type < SYMBOLIZER_MAX);
+ if (debug_dump_symbols)
+ printf("Drawing symbol <%s> at z-index %u for %s\n", symbolizers[sym->type]->name, zindex, STK_OSM_NAME(sym->o));
+ symbolizers[sym->type]->draw(sym, svg);
+}
+
+void sym_draw_all(struct svg *svg)
+{
+ msg(L_INFO, "Sorting %u symbols by depth", (uns) GARY_SIZE(sym_planned));
+ sym_sort(sym_planned, GARY_SIZE(sym_planned));
+
+ msg(L_INFO, "Dumping icon library");
+ svg_icon_dump_library(svg);
+
+ msg(L_INFO, "Drawing symbols");
+ for (uns i = 0; i < GARY_SIZE(sym_planned); i++)
+ sym_draw(sym_planned[i].sym, sym_planned[i].zindex, svg);
+}
--- /dev/null
+/*
+ * Hic Est Leo -- Symbolizers
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _BRUM_SYM_H
+#define _BRUM_SYM_H
+
+#include "osm.h"
+#include "style.h"
+#include "svg.h"
+
+enum symbolizer_type {
+ SYMBOLIZER_INVALID,
+ SYMBOLIZER_POINT,
+ SYMBOLIZER_ICON,
+ SYMBOLIZER_LINE,
+ SYMBOLIZER_AREA,
+ SYMBOLIZER_TEXT,
+ SYMBOLIZER_LINEIMG,
+ SYMBOLIZER_MAX,
+};
+
+struct symbol {
+ enum symbolizer_type type;
+ struct osm_object *o;
+ // Symbolizer-dependent data follow
+};
+
+struct symbolizer {
+ const char *name;
+ void (*draw)(struct symbol *sym, struct svg *svg);
+ void (*gen)(struct osm_object *o, struct style_info *si, struct svg *svg);
+ void (*init)(void);
+};
+
+extern struct mempool *sym_mp;
+
+/*
+ * Default values for major-z-index:
+ *
+ * 1 area
+ * 2 casing
+ * 2.1 left/right casing
+ * 2.9 line pattern
+ * 3 line
+ * 4 point
+ * 4.9 line-text
+ * 5 point-text
+ */
+typedef u32 z_index_t;
+
+void sym_init(void);
+void *sym_new(enum symbolizer_type type, struct osm_object *o, size_t size);
+void sym_plan(struct symbol *sym, z_index_t zindex);
+void sym_draw_all(struct svg *svg);
+void sym_from_style(struct osm_object *o, struct style_results *sr, struct svg *svg);
+z_index_t sym_zindex(struct osm_object *o, struct style_info *si, double default_mzi);
+
+/* sym-point.c handles point symbols and icons */
+
+struct sym_point {
+ struct symbol s;
+ osm_val_t shape;
+ double size;
+ color_t stroke_color;
+ double stroke_width;
+ double stroke_opacity;
+ color_t fill_color;
+ double fill_opacity;
+ bool do_stroke;
+ bool do_fill;
+};
+
+// FIXME: Make sym_*_new() and symbolizer structs internal
+extern struct symbolizer symbolizer_point;
+struct sym_point *sym_point_new(struct osm_object *o);
+
+struct sym_icon {
+ struct symbol s;
+ struct svg_icon_request sir;
+};
+
+extern struct symbolizer symbolizer_icon;
+struct sym_icon *sym_icon_new(struct osm_object *o);
+
+/* sym-line.c handles lines and areas */
+
+struct sym_line {
+ struct symbol s;
+ double width;
+ color_t color;
+ double opacity;
+ osm_val_t line_cap;
+ osm_val_t line_join;
+ double miter_limit;
+ double *dash_pattern; // Growing array
+ double dash_offset;
+};
+
+extern struct symbolizer symbolizer_line;
+struct sym_line *sym_line_new(struct osm_object *o);
+
+struct sym_area {
+ struct symbol s;
+ color_t fill_color;
+ double fill_opacity;
+ struct svg_pattern *fill_pattern;
+};
+
+extern struct symbolizer symbolizer_area;
+struct sym_area *sym_area_new(struct osm_object *o);
+
+struct sym_lineimg { // Images along line
+ struct symbol s;
+ struct svg_icon_request sir;
+ osm_val_t align;
+ double offset;
+ double spacing;
+ double phase;
+};
+
+extern struct symbolizer symbolizer_lineimg;
+struct sym_lineimg *sym_lineimg_new(struct osm_object *o);
+
+/* sym-text.c */
+
+struct sym_text {
+ struct symbol s;
+ osm_val_t text;
+ color_t text_color;
+ double x;
+ double y;
+ struct text_font *font;
+ double opacity;
+ color_t halo_color;
+ double halo_radius;
+ double halo_opacity;
+ struct sym_text *next_in_tile; // See text_by_tile[]
+ struct sym_text *next_duplicate;
+ double tw, th, td;
+};
+
+extern struct symbolizer symbolizer_text;
+struct sym_text *sym_text_new(struct osm_object *o);
+
+void scale_text(struct svg *svg, double x, double y, osm_val_t text);
+
+#endif
--- /dev/null
+#!/bin/sh
+./leo -SDebug.DumpSource=1 | sed 's@^[[:space:]]*\(.* = .*\)@\1@p;d' | sort -u
--- /dev/null
+// relation, area way[highway=path][magic?][!troll] { visibility: none; width: 1; }
+
+// *[water=river] { }
+
+node[highway=crossing] {
+// symbol-shape: circle;
+// symbol-stroke-width: 1;
+// symbol-stroke-color: #ff0000;
+// symbol-stroke-opacity: 0.5;
+// symbol-fill-color: #00ff00;
+// icon-image: "symbols/mj/view_point.svg";
+// icon-width: 2;
+}
+
+way[highway] {
+ width: 0.6;
+ color: #fff;
+ linecap: round;
+ casing-width: 1;
+ casing-color: #888;
+ casing-linecap: round;
+ z-index: 1;
+}
+
+way[highway=track], way[highway=footway] {
+ casing-width: 0;
+ width: 0.3;
+ color: #888;
+ z-index: 0;
+}
+
+area {
+ fill-color: #cfc;
+ width: 0.2;
+ color: #080;
+}
+
+area[natural=water] {
+ fill-color: #77f;
+ color: #00f;
+ width: 0.2;
+ z-index: 3;
+ text: name;
+ text-halo-radius: 1;
+ text-color: #000000;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-position: center;
+ font-family: Times;
+ font-size: 3;
+ font-style: italic;
+}
+
+node[highway=bus_stop] {
+ text: name;
+ text-halo-radius: 1;
+ text-color: #000000;
+ text-halo-color: #ffffff;
+ text-halo-opacity: 0.8;
+ text-dup-threshold: 20;
+ text-anchor-horizontal: center;
+ text-anchor-vertical: center;
+ font-family: Times;
+ font-size: 3;
+ font-style: italic;
+// icon-image: "symbols/mj/view_point.svg";
+// icon-width: 2;
+ symbol-shape: circle;
+ symbol-fill-color: #ff00ff;
+}
--- /dev/null
+/*
+ * Hic Est Leo -- OSM XML Parser
+ *
+ * (c) 2014 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/fastbuf.h>
+#include <xml/xml.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "leo.h"
+#include "osm.h"
+
+static void parse_tag(struct xml_context *ctx, struct osm_object *o, struct xml_node *t)
+{
+ char *tag_k = xml_attr_value(ctx, t, "k");
+ char *tag_v = xml_attr_value(ctx, t, "v");
+ if (!tag_k || !tag_v)
+ die("Object %ju has malformed tag", (uintmax_t) o->id);
+ osm_obj_add_tag(o, tag_k, tag_v);
+}
+
+static void parse_node(struct xml_context *ctx, struct xml_node *e)
+{
+ char *attr_id = xml_attr_value(ctx, e, "id");
+ if (!attr_id)
+ die("Node with no id");
+
+ char *attr_vis = xml_attr_value(ctx, e, "visible");
+ if (attr_vis && strcmp(attr_vis, "true"))
+ {
+ msg(L_DEBUG, "Node %s invisible", attr_id);
+ return;
+ }
+
+ char *attr_lat = xml_attr_value(ctx, e, "lat");
+ char *attr_lon = xml_attr_value(ctx, e, "lon");
+ if (!attr_lat || !attr_lon)
+ die("Node %s has mandatory attributes missing", attr_id);
+
+ struct osm_node *n = osm_node_new(osm_parse_id(attr_id));
+ n->x = atof(attr_lon);
+ n->y = atof(attr_lat);
+
+ XML_NODE_FOR_EACH(t, e)
+ if (t->type == XML_NODE_ELEM && !strcmp(t->name, "tag"))
+ parse_tag(ctx, &n->o, t);
+}
+
+static void parse_way(struct xml_context *ctx, struct xml_node *e)
+{
+ char *attr_id = xml_attr_value(ctx, e, "id");
+ if (!attr_id)
+ die("Way with no id");
+
+ char *attr_vis = xml_attr_value(ctx, e, "visible");
+ if (attr_vis && strcmp(attr_vis, "true"))
+ {
+ msg(L_DEBUG, "Way %s invisible", attr_id);
+ return;
+ }
+
+ struct osm_way *w = osm_way_new(osm_parse_id(attr_id));
+
+ XML_NODE_FOR_EACH(t, e)
+ if (t->type == XML_NODE_ELEM)
+ {
+ if (!strcmp(t->name, "tag"))
+ parse_tag(ctx, &w->o, t);
+ else if (!strcmp(t->name, "nd"))
+ {
+ char *nd_ref = xml_attr_value(ctx, t, "ref");
+ if (!nd_ref)
+ die("Way %s has malformed ref", attr_id);
+ struct osm_object *o = osm_obj_find_by_id(OSM_TYPE_NODE, osm_parse_id(nd_ref));
+ if (!o)
+ die("Way %ju refers to unknown node %s", (uintmax_t) w->o.id, nd_ref);
+ osm_way_add_node(w, (struct osm_node *) o);
+ }
+ }
+}
+
+static void parse_relation(struct xml_context *ctx, struct xml_node *e)
+{
+ char *attr_id = xml_attr_value(ctx, e, "id");
+ if (!attr_id)
+ die("Relation with no id");
+
+ char *attr_vis = xml_attr_value(ctx, e, "visible");
+ if (attr_vis && strcmp(attr_vis, "true"))
+ {
+ msg(L_DEBUG, "Relation %s invisible", attr_id);
+ return;
+ }
+
+ struct osm_relation *r = osm_relation_new(osm_parse_id(attr_id));
+
+ XML_NODE_FOR_EACH(t, e)
+ if (t->type == XML_NODE_ELEM)
+ {
+ if (!strcmp(t->name, "tag"))
+ parse_tag(ctx, &r->o, t);
+ else if (!strcmp(t->name, "member"))
+ {
+ char *m_role = xml_attr_value(ctx, t, "role");
+ char *m_ref = xml_attr_value(ctx, t, "ref");
+ char *m_type = xml_attr_value(ctx, t, "type");
+ if (!m_role || !m_ref || !m_type)
+ die("Relation %ju has malformed member", (uintmax_t) r->o.id);
+ osm_id_t ref = osm_parse_id(m_ref);
+
+ enum osm_object_type type;
+ if (!strcmp(m_type, "node"))
+ type = OSM_TYPE_NODE;
+ else if (!strcmp(m_type, "way"))
+ type = OSM_TYPE_WAY;
+ else if (!strcmp(m_type, "relation"))
+ {
+ type = OSM_TYPE_RELATION;
+ // Since the order of objects is topological, we need not worry about cycles
+ // msg(L_DEBUG, "Relation inside relation (%ju inside %ju)", (uintmax_t) o->id, (uintmax_t) r->o.id);
+ }
+ else
+ {
+ msg(L_WARN, "Relation %ju refers to member %ju of unknown type %s", (uintmax_t) r->o.id, (uintmax_t) ref, m_type);
+ continue;
+ }
+
+ struct osm_object *o = osm_obj_find_by_id(type, ref);
+ if (!o)
+ {
+ // This is a standard situation, so warn only when debugging
+ // msg(L_WARN, "Relation %ju refers to unknown member node %s", (uintmax_t) ref, m_ref);
+ continue;
+ }
+ osm_relation_add_member(r, o, m_role);
+ }
+ }
+}
+
+static void h_error(struct xml_context *ctx)
+{
+ fprintf(stderr, "%s at %u: %s\n", (ctx->err_code < XML_ERR_ERROR) ? "warn" : "error", xml_row(ctx), ctx->err_msg);
+}
+
+#if 0
+
+static void h_stag(struct xml_context *ctx)
+{
+ printf("STAG %s\n", ctx->node->name);
+ if (!strcmp(ctx->node->name, "node"))
+ {
+ ctx->flags &= ~XML_REPORT_TAGS;
+ ctx->flags |= XML_ALLOC_ALL;
+ }
+}
+
+static void h_etag(struct xml_context *ctx)
+{
+ printf("ETAG %s\n", ctx->node->name);
+}
+
+#endif
+
+void osm_xml_parse(const char *name)
+{
+ msg(L_INFO, "Loading %s", name);
+
+ struct xml_context ctx;
+ xml_init(&ctx);
+ ctx.h_warn = ctx.h_error = ctx.h_fatal = h_error;
+ xml_push_fastbuf(&ctx, bopen_file(name, O_RDONLY, NULL));
+
+#if 0
+ ctx.flags |= XML_REPORT_TAGS;
+ ctx.h_stag = h_stag;
+ ctx.h_etag = h_etag;
+ xml_parse(&ctx);
+#endif
+
+#if 0
+ uns state;
+ while (state = xml_next(&ctx))
+ switch (state)
+ {
+ case XML_STATE_STAG:
+ printf("STAG %s\n", ctx.node->name);
+ if (!strcmp(ctx.node->name, "node"))
+ {
+ ctx.pull = XML_PULL_ETAG;
+ ctx.flags |= XML_ALLOC_CHARS | XML_ALLOC_TAGS;
+ }
+ break;
+ case XML_STATE_ETAG:
+ printf("ETAG %s\n", ctx.node->name);
+ if (!strcmp(ctx.node->name, "node"))
+ {
+ ctx.pull = XML_PULL_STAG | XML_PULL_ETAG;
+ ctx.flags &= ~(XML_ALLOC_CHARS | XML_ALLOC_TAGS);
+ }
+ break;
+ }
+#endif
+
+ ctx.flags |= XML_ALLOC_ALL;
+ xml_parse(&ctx);
+
+ if (ctx.err_code)
+ die("Fatal error in XML parser");
+
+ struct xml_node *root = ctx.dom;
+ ASSERT(root);
+ XML_NODE_FOR_EACH(e, root)
+ if (e->type == XML_NODE_ELEM)
+ {
+ if (!strcmp(e->name, "node"))
+ parse_node(&ctx, e);
+ else if (!strcmp(e->name, "way"))
+ parse_way(&ctx, e);
+ else if (!strcmp(e->name, "relation"))
+ parse_relation(&ctx, e);
+ }
+
+ xml_cleanup(&ctx);
+}