lib/ymaps/action_view/ymapsml_helper.rb
require 'geokit/geocoders/yandex_geocoder'
module YMaps
module ActionView
YMAPS_XMLNS = 'http://maps.yandex.ru/ymaps/1.x'
GML_XMLNS = 'http://www.opengis.net/gml'
REPR_XMLNS = 'http://maps.yandex.ru/representation/1.x'
module YMapsMLHelper
def ymapsml(options = {}, &block)
xml = options.delete(:xml) { eval('xml', block.binding) }
xml.instruct!
ymapsml_opts = {
'xml:lang' => options.fetch(:language) { 'en-US' },
'xmlns' => YMAPS_XMLNS,
'xmlns:gml' => GML_XMLNS,
'xmlns:repr' => REPR_XMLNS
}
ymapsml_opts.merge!(options).reject! { |key, value| !key.match(/^xml/) }
xml.ymaps(ymapsml_opts) do
yield YMapsBuilder.new(xml, self, options)
end
end
end
class Builder
YMAPS_TAG_NAMES = [:GeoObject, :GeoObjectCollection, :style, :ymaps, :AnyMetaData]
GML_TAG_NAMES = [:boundedBy, :description, :Envelope, :exterior, :featureMember, :featureMembers,
:interior, :LineString, :LinearRing, :lowerCorner, :metaDataProperty, :name, :Point,
:Polygon, :pos, :posList, :upperCorner]
REPR_TAG_NAMES = [:balloonContentStyle, :fill, :fillColor, :hintContentStyle, :iconContentStyle, :lineStyle,
:href, :iconStyle, :mapType, :offset, :outline, :parentStyle, :polygonStyle, :Representation,
:shadow, :size, :strokeColor, :strokeWidth, :Style, :Template, :template, :text, :View]
def initialize(xml)
@xml = xml
end
protected
def prefixed_method(method, *arguments, &block)
@xml.__send__(*xmlns_prefix!(method, arguments), &block)
end
def link_to(name, href)
prefixed_method(name, "\##{href}")
end
private
def method_missing(method, *arguments, &block)
prefixed_method(method, *arguments, &block)
end
def xmlns_prefix!(method, arguments)
if GML_TAG_NAMES.include?(method)
[:gml, method, *arguments]
elsif REPR_TAG_NAMES.include?(method)
[:repr, method, *arguments]
else
[method, *arguments]
end
end
end
class YMapsBuilder < Builder
def initialize(xml, view, ymaps_options = {})
@xml, @view, @ymaps_options = xml, view, ymaps_options
end
def collection(options = {})
GeoObjectCollection do
if options.key?(:style)
@xml.style("\##{options.delete(:style)}")
end
featureMembers { yield }
end
end
def object(object, options = {})
gml_id = options.delete(:id) { object.id }
GeoObject('gml:id' => gml_id) do
if options.key?(:style)
@xml.style("\##{options.delete(:style)}")
end
point(object.latlng) if object.respond_to?(:latlng)
bounds(object.bounds) if object.respond_to?(:bounds)
name(options.delete(:name) { object.to_s })
yield self
end
end
def point(latlng)
Point {
pos(latlng.gml_pos)
} if latlng
end
def bounds(bounds)
boundedBy {
Envelope {
lowerCorner(bounds.lower.gml_pos)
upperCorner(bounds.upper.gml_pos)
}
} if bounds
end
def meta_data
metaDataProperty {
AnyMetaData {
yield(@xml)
}
}
end
def representation
Representation {
yield(YMapsReprBuilder.new(@xml, @view))
}
end
end
class YMapsReprBuilder < YMapsBuilder
ACCEPTABLE_STYLES = {
:balloon_content => [:template],
:hint_content => [:template],
:icon => [:href, :offset, :shadow, :size, :template],
:icon_content => [:template],
:line => [:stroke_color, :stroke_width],
:polygon => [:fill, :fill_color, :outline, :stroke_color, :stroke_width],
}
def view(options = {})
View {
mapType(options[:type].to_s.upcase) if options[:type]
bounds(options[:bounds]) if options[:bounds]
yield if block_given?
}
end
# Add style definition
# @param [Symbol, String] id style name
# @param [Hash] options style options
def style(id, options = {})
options[:hasBalloon] = options.delete(:balloon) if options.key?(:balloon)
options[:hasHint] = options.delete(:hint) if options.key?(:hint)
parent = options.delete(:parent) { false }
Style(options.merge('gml:id' => id.to_s)) {
link_to(:parentStyle, parent) if parent
yield
}
end
ACCEPTABLE_STYLES.each do |name, values|
define_method(name) do |options|
tag_name = "#{ActiveSupport::Inflector.camelize(name.to_s, false)}Style"
send(tag_name) do
style_options(options, values)
end
end
end
def template(id, template_text = nil)
Template('gml:id' => id.to_s) do
text do
cdata!(template_text || yield)
end
end
end
protected
def style_options(options = {}, acceptable = nil)
if acceptable
options.assert_valid_keys(acceptable)
end
# Filling options
fill(options[:fill] ? 1 : 0) if options.key?(:fill)
fillColor(options[:fill_color]) if options.key?(:fill_color)
# Outline options
outline(options[:outline] ? 1 : 0) if options.key?(:outline)
strokeColor(options[:stroke_color]) if options.key?(:stroke_color)
strokeWidth(options[:stroke_width]) if options.key?(:stroke_width)
href(options[:href]) if options.key?(:href)
size(*Array(options[:size])) if options.key?(:size)
offset(*Array(options[:offset])) if options.key?(:offset)
link_to(:template, options[:template]) if options.key?(:template)
shadow do
style_options(options[:shadow], [:href, :size, :template, :offset])
end if options.key?(:shadow)
end
def size(x, y = nil)
if x.is_a?(Hash)
x, y = x[:x], x[:y]
end
prefixed_method(:size, :x => x, :y => y)
end
def offset(x, y = nil)
if x.is_a?(Hash)
x, y = x[:x], x[:y]
end
prefixed_method(:offset, :x => x, :y => y)
end
end
end
end