Permalink
Browse files

Alpha version of parser

  • Loading branch information...
systemed committed Aug 3, 2011
0 parents commit 2720a88a5400c1cddd96d83ec4f4146fd873312d
@@ -0,0 +1,45 @@
+Ruby MapCSS parser
+==================
+
+This is an experimental Ruby parser for MapCSS 0.2, based on the Halcyon parser.
+
+== Dependencies ==
+
+You'll require Jochen Topf's OSMlib library. We use the Node, Way and Relation objects from this. Unlike Halcyon, OSMlib doesn't keep track of 'parent' objects, so we have a singleton Dictionary class to store this mapping.
+
+== How to use ==
+
+Install style_parser and style_parser.rb into a lib/ directory. Require style_parser as usual.
+
+Read a MapCSS file like this:
+
+ css=IO.read('opencyclemap.css')
+ ruleset=StyleParser::RuleSet.new(12,20) # 12 and 20 are min/max zoom levels
+ ruleset.parse(css)
+
+Create the parent object mappings from an OSMlib database:
+
+ dictionary = StyleParser::Dictionary.instance
+ dictionary.populate(db)
+
+Then get the style for any OSM object like this:
+
+ entity=db.get_way(3044458)
+ stylelist=ruleset.get_styles(entity,entity.tags,14) # 14 is the target zoom level
+ puts stylelist
+
+You can see an example of this in osm_test.rb.
+
+== Limitations ==
+
+- It hasn't really been tested at all.
+- The test stylesheet is MapCSS 0.1. That's not too helpful. ;)
+- No evals yet, though these should be pretty trivial to implement in Ruby (not like AS3...).
+- Any limitations of Halcyon's parser are also present here.
+- We don't yet handle @import directives for nested CSS files. You'll need to parse these yourself.
+
+== Licence and author ==
+
+WTFPL. You can do whatever the fuck you want with this code. Code by Richard Fairhurst, summer 2011.
+
+Example files: OpenCycleMap Potlatch 2 style by Andy Allan, Charlbury OpenStreetMap data by OpenStreetMap contributors (CC-BY-SA).
18,784 charlbury.osm

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -0,0 +1,20 @@
+module StyleParser #:nodoc:
+ VERSION = "0.1"
+end
+
+require "style_parser/dictionary"
+
+require "style_parser/rule_set"
+require "style_parser/style_chooser"
+require "style_parser/style_list"
+
+require "style_parser/rule"
+require "style_parser/rule_chain"
+require "style_parser/condition"
+
+require "style_parser/style"
+require "style_parser/instruction_style"
+require "style_parser/point_style"
+require "style_parser/shape_style"
+require "style_parser/shield_style"
+require "style_parser/text_style"
@@ -0,0 +1,39 @@
+module StyleParser
+ class Condition
+
+ def initialize(type,param1,param2=nil)
+ @type=type
+ @param1=param1
+ @param2=param2
+ end
+
+ def test(tags)
+ case @type
+ when 'eq'
+ return tags[@param1]==@param2
+ when 'ne'
+ return tags[@param1]!=@param2
+ when 'regex'
+ return tags[@param1]=~Regexp.new(@param2)
+ when 'true'
+ return (tags[@param1]=='true' or tags[@param1]=='yes' or tags[@param1]=='1')
+ when 'false'
+ return (tags[@param1]=='false' or tags[@param1]=='no' or tags[@param1]=='0')
+ when 'set'
+ return (tags[@param1] and tags[@param1]!='')
+ when 'unset'
+ return (tags[@param1].nil? or tags[@param1]=='')
+ when '<'
+ return tags[@param1].to_f < @param2.to_f
+ when '<='
+ return tags[@param1].to_f <= @param2.to_f
+ when '>'
+ return tags[@param1].to_f > @param2.to_f
+ when '>='
+ return tags[@param1].to_f >= @param2.to_f
+ end
+ false
+ end
+
+ end
+end
@@ -0,0 +1,56 @@
+require 'singleton'
+
+module StyleParser
+ class Dictionary
+
+ include Singleton
+
+ def populate(database)
+ @dictionary={}
+
+ database.relations.values.each do |relation|
+ ignore_not_found {
+ relation.member_objects.each { |member| add(relation,member) }
+ }
+ end
+
+ database.ways.values.each do |way|
+ way.node_objects.each { |node| add(way,node) }
+ end
+ end
+
+ def parent_objects(child)
+ @dictionary[child] ? @dictionary[child] : []
+ end
+
+ def parent_relations(child)
+ parent_objects(child).collect do |parent|
+ if parent.type=='relation' then parent end
+ end
+ end
+
+ def parent_ways(child)
+ parent_objects(child).collect do |parent|
+ if parent.type=='way' then parent end
+ end
+ end
+
+ private
+
+ def add(parent,child)
+ if @dictionary[child] and !@dictionary[child].index(parent)
+ @dictionary[child].push(parent)
+ elsif !@dictionary[child]
+ @dictionary[child]=[parent]
+ end
+ end
+
+ def ignore_not_found
+ begin
+ yield
+ rescue OSM::NotFoundError
+ end
+ end
+
+ end
+end
@@ -0,0 +1,13 @@
+module StyleParser
+ class InstructionStyle < Style
+
+ attr_accessor :set_tags, :breaker
+
+ def add_set_tag(k,v)
+ @edited=true;
+ if (!@set_tags) then set_tags={} end
+ set_tags[k]=v
+ end
+
+ end
+end
@@ -0,0 +1,11 @@
+module StyleParser
+ class PointStyle < Style
+
+ PROPNAMES = ['icon_image','icon_width','icon_height','rotation'];
+
+ def drawn
+ return !properties['icon_image'].nil?
+ end
+
+ end
+end
@@ -0,0 +1,34 @@
+module StyleParser
+ class Rule
+
+ attr_accessor :conditions, :isand, :minzoom, :maxzoom
+
+ def initialize(subject)
+ @subject=subject
+ @conditions=[]
+ @minzoom=0
+ @maxzoom=255
+ end
+
+ def test(entity,tags,zoom)
+ if (@subject!='' and entity.type!=@subject) then return false end
+ if (zoom<@minzoom or zoom>@maxzoom) then return false end
+
+ v=true
+ i=0
+ @conditions.each do |condition|
+ r=condition.test(tags)
+ if i==0
+ v=r
+ elsif isand
+ v=v and r
+ else
+ v=v or r
+ end
+ i+=1
+ end
+ v
+ end
+
+ end
+end
@@ -0,0 +1,47 @@
+module StyleParser
+ class RuleChain
+
+ attr_accessor :rules, :subpart
+
+ def initialize
+ @rules=[]
+ @subpart='default'
+ end
+
+ def test(pos, entity, tags, zoom)
+ if @rules.length==0 then return false end
+ if pos==-1 then pos=@rules.length-1 end
+
+ r=@rules[pos]
+ unless (r.test(entity, tags, zoom)) then return false end
+ if pos==0 then return true end
+
+ Dictionary.instance.parent_objects(entity).each do |p|
+ if (test(pos-1, p, p.tags, zoom)) then return true end
+ end
+ return false
+ end
+
+ def set_subpart(s)
+ @subpart = (s=='') ? 'default' : s
+ end
+
+ def add_rule(e='')
+ @rules.push(Rule.new(e))
+ end
+
+ def add_condition_to_last(c)
+ @rules[@rules.length-1].conditions.push(c)
+ end
+
+ def add_zoom_to_last(z1,z2)
+ @rules[@rules.length-1].minzoom=z1
+ @rules[@rules.length-1].maxzoom=z2
+ end
+
+ def to_s
+ "[RuleChain #{@subpart}: #{@rules}]"
+ end
+
+ end
+end
Oops, something went wrong.

0 comments on commit 2720a88

Please sign in to comment.