class Glug::Layer

—– Layer

a layer in a GL style
this is where most of the hard work happens, including 'method_missing' and 'on' calls to provide the grammar

Constants

EXPRESSIONS
HIDDEN
LAYOUT

GL properties (as distinct from OSM keys)

PAINT
REF_PROPERTIES

Shared properties that can be recalled by using a ‘ref’

TOP_LEVEL

Attributes

condition[RW]
kv[RW]
stylesheet[RW]

Public Class Methods

new(stylesheet, args={}) click to toggle source
# File lib/glug/layer.rb, line 57
def initialize(stylesheet, args={})
        @stylesheet = stylesheet
        @condition = args[:condition]
        @kv = args[:kv] || {}
        @kv[:id] = args[:id]
        if args[:zoom] then @kv[:zoom]=args[:zoom] end

        @type = nil                                                  # auto-detected layer type
        @write = true                                                # write this layer out, or has it been suppressed?
        @cascade_cond = nil                                  # are we currently evaluating a cascade directive?
        @cascades = args[:cascades] || []    # cascade list to apply to all subsequent layers
        @uncascaded = nil                                    # condition to add to non-cascaded layers

        @kv[:source] ||= stylesheet.sources.find {|k,v| v[:default] }[0]
        @kv[:source_layer] ||= args[:id] if stylesheet.sources[@kv[:source]][:type]=="vector"
        @child_num = 0                               # incremented sublayer suffix
end

Public Instance Methods

_add_cascade_condition(k, v) click to toggle source
# File lib/glug/layer.rb, line 159
def _add_cascade_condition(k, v)
        if @cascades.length>0 && @cascades[-1][0].to_s==@cascade_cond.to_s
                @cascades[-1][1][k]=v
        else
                @cascades << [@cascade_cond, { k=>v }]
        end
end
_set_filter(condition) click to toggle source
# File lib/glug/layer.rb, line 179
def _set_filter(condition)
        @condition = condition.nil? ? nil : condition.dup
end
all() click to toggle source
# File lib/glug/layer.rb, line 194
def all ; return Subscriptable.new(:all ) end
any() click to toggle source

Square-bracket filters (any, all)

# File lib/glug/layer.rb, line 193
def any ; return Subscriptable.new(:any ) end
cascade(*args, &block) click to toggle source

Add a cascading condition

# File lib/glug/layer.rb, line 153
def cascade(*args, &block)
        cond = (args.length==1) ? args[0] : Condition.new.from_list(:any,args)
        @cascade_cond = cond
        self.instance_eval(&block)
        @cascade_cond = nil
end
current_value(key) click to toggle source

Return a current value from @kv This allows us to do: line_width current_value(:line_width)/2.0

# File lib/glug/layer.rb, line 107
def current_value(key)
        @kv[key]
end
filter(*args) click to toggle source

Setters for @condition (making sure we copy when inheriting)

# File lib/glug/layer.rb, line 176
def filter(*args)
        _set_filter(args.length==1 ? args[0] : Condition.new.from_list(:any,args))
end
id(name) click to toggle source

Set layer name

# File lib/glug/layer.rb, line 184
def id(name)
        @kv[:id] = name
end
literal(*args) click to toggle source

Convenience so we can write literal(1,2,3) rather than literal()

# File lib/glug/layer.rb, line 95
def literal(*args)
        if args.length==1 && args[0].is_a?(Hash)
                # Hashes - literal(frog: 1, bill: 2)
                Condition.new.from_list(:literal, [args[0]])
        else
                # Arrays - literal(1,2,3)
                Condition.new.from_list(:literal, [args])
        end
end
method_missing(method_sym, *arguments) click to toggle source

Handle all missing ‘method’ calls If we can match it to a GL property, it’s an assignment: otherwise it’s an OSM key

# File lib/glug/layer.rb, line 78
def method_missing(method_sym, *arguments)
        if EXPRESSIONS.include?(method_sym)
                return Condition.new.from_list(method_sym, arguments)
        elsif LAYOUT.include?(method_sym) || PAINT.include?(method_sym) || TOP_LEVEL.include?(method_sym)
                v = arguments.length==1 ? arguments[0] : arguments
                if v.is_a?(Proc) then v=v.call(@kv[method_sym]) end
                if @cascade_cond.nil?
                        @kv[method_sym] = v
                else
                        _add_cascade_condition(method_sym, v)
                end
        else
                return Condition.new.from_list("get", [method_sym])
        end
end
nilsafe_merge(a,b) click to toggle source

Nil-safe merge

# File lib/glug/layer.rb, line 148
def nilsafe_merge(a,b)
        a.nil? ? b : (a & b)
end
on(*args, &block) click to toggle source

Add a sublayer with an additional filter

# File lib/glug/layer.rb, line 112
def on(*args, &block)
        @child_num+=1
        r = Layer.new(@stylesheet,
                        :id => "#{@kv[:id]}__#{@child_num}".to_sym,
                        :kv => @kv.dup, :cascades => @cascades.dup)

        # Set zoom level
        if args[0].is_a?(Range) || args[0].is_a?(Integer)
                r.kv[:zoom] = args.shift
        end

        # Set condition
        sub_cond = nil
        if args.empty?
                sub_cond = @condition                                               # just inherit parent layer's condition
        else
                sub_cond = (args.length==1) ? args[0] : Condition.new.from_list(:any,args)
                sub_cond = nilsafe_merge(sub_cond, @condition)
        end
        r._set_filter(nilsafe_merge(sub_cond, @uncascaded))
        r.instance_eval(&block)
        @stylesheet._add_layer(r)

        # Create cascaded layers
        child_chr='a'
        @cascades.each do |c|
                c_cond, c_kv = c
                l = Layer.new(@stylesheet, :id=>"#{r.kv[:id]}__#{child_chr}", :kv=>r.kv.dup)
                l._set_filter(nilsafe_merge(sub_cond, c_cond))
                l.kv.merge!(c_kv)
                @stylesheet._add_layer(l)
                child_chr.next!
        end
end
ref_key(hash) click to toggle source

Key to identify matching layer properties (slow but…)

# File lib/glug/layer.rb, line 256
def ref_key(hash)
        (REF_PROPERTIES.collect { |k| hash[k] } ).to_json
end
set_type_from(s) click to toggle source

Deduce ‘type’ attribute from style attributes

# File lib/glug/layer.rb, line 197
def set_type_from(s)
        return unless s.include?('-')
        t = (s=~/^fill-extrusion/ ? "fill-extrusion" : s.split('-')[0]).to_sym
        if t==:icon || t==:text then t=:symbol end
        if @type && @type!=t then raise "Attribute #{s} conflicts with deduced type #{@type} in layer #{@kv[:id]}" end
        @type=t
end
suppress() click to toggle source

Suppress output of this layer

# File lib/glug/layer.rb, line 189
def suppress; @write = false end
to_hash() click to toggle source

Create a GL-format hash from a layer definition

# File lib/glug/layer.rb, line 206
def to_hash
        hash = { :layout=> {}, :paint => {} }

        # Assign key/values to correct place
        @kv.each do |k,v|
                s = k.to_s.gsub('_','-')
                if s.include?('-color') && v.is_a?(Integer) then v = "#%06x" % v end
                if v.respond_to?(:encode) then v=v.encode end

                if LAYOUT.include?(k)
                        hash[:layout][s]=v
                        set_type_from s
                elsif PAINT.include?(k)
                        hash[:paint][s]=v
                        set_type_from s
                elsif TOP_LEVEL.include?(k) || HIDDEN.include?(k)
                        hash[s]=v
                else raise "#{s} isn't a recognised layer attribute"
                end
        end

        hash['type'] = @type
        if @condition then hash['filter'] = @condition.encode end

        # Convert zoom level
        if (v=hash['zoom'])
                hash['minzoom'] = v.is_a?(Range) ? v.first : v
                hash['maxzoom'] = v.is_a?(Range) ? v.last  : v
                hash.delete('zoom')
        end

        # See if we can reuse an earlier layer's properties
        mk = ref_key(hash)
        if stylesheet.refs[mk]
                REF_PROPERTIES.each { |k| hash.delete(k) }
                hash['ref'] = stylesheet.refs[mk]
        else
                stylesheet.refs[mk] = hash['id']
        end

        if hash[:layout].empty? && hash[:paint].empty?
                nil
        else
                hash.delete(:layout) if hash[:layout].empty?
                hash.delete(:paint) if hash[:paint].empty?
                hash
        end
end
uncascaded(*args) click to toggle source
# File lib/glug/layer.rb, line 166
def uncascaded(*args)
        cond = case args.length
                when 0; nil
                when 1; args[0]
                else; Condition.new.from_list(:any,args)
        end
        @uncascaded = cond
end
write?() click to toggle source
# File lib/glug/layer.rb, line 190
def write?; @write end