1220 lines
46 KiB
Ruby
1220 lines
46 KiB
Ruby
require_relative '../../../spec_helper'
|
|
require_relative '../../../../app/controllers/carto/api/layer_presenter'
|
|
require_relative '../../api/json/layer_presenter_shared_examples'
|
|
|
|
describe "Carto::Api::LayersController - Layer Model" do
|
|
it_behaves_like 'layer presenters', Carto::Api::LayerPresenter, ::Layer
|
|
end
|
|
|
|
describe "Carto::Api::LayersController - Carto::Layer" do
|
|
it_behaves_like 'layer presenters', Carto::Api::LayerPresenter, Carto::Layer
|
|
end
|
|
|
|
describe Carto::Api::LayerPresenter do
|
|
def presenter_with_style_properties(layer)
|
|
Carto::Api::LayerPresenter.new(layer, with_style_properties: true)
|
|
end
|
|
|
|
describe 'wizard_properties migration to style_properties' do
|
|
def wizard_properties(type: 'polygon', properties: {})
|
|
{ "type" => type, 'properties' => properties }
|
|
end
|
|
|
|
def build_layer_with_wizard_properties(properties)
|
|
FactoryGirl.build(:carto_layer, options: { 'wizard_properties' => properties })
|
|
end
|
|
|
|
it "doesn't autogenerate `style_properties` if `wizard_properties` isn't present but it's not requested" do
|
|
layer = build_layer_with_wizard_properties(wizard_properties)
|
|
poro_options = Carto::Api::LayerPresenter.new(layer).to_poro['options']
|
|
poro_options['wizard_properties'].should_not be_nil
|
|
poro_options['style_properties'].should be_nil
|
|
end
|
|
|
|
it "autogenerates `style_properties` based on `wizard_properties` if it isn't present and it's requested" do
|
|
layer = build_layer_with_wizard_properties(wizard_properties)
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties'].should_not be_nil
|
|
style_properties = poro_options['style_properties']
|
|
style_properties.should_not be_nil
|
|
style_properties['autogenerated'].should be_true
|
|
end
|
|
|
|
it "autogenerates `style_properties` based on `wizard_properties` if it is present but autogenerated" do
|
|
layer = build_layer_with_wizard_properties(wizard_properties)
|
|
layer.options['style_properties'] = { 'autogenerated' => true, 'wadus' => 'wadus' }
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties'].should_not be_nil
|
|
style_properties = poro_options['style_properties']
|
|
style_properties.should_not be_nil
|
|
style_properties['autogenerated'].should be_true
|
|
style_properties['type'].should_not be_nil
|
|
style_properties['wadus'].should be_nil
|
|
end
|
|
|
|
it "doesn't autogenerate `style_properties` based on `wizard_properties` if it is present and not autogenerated" do
|
|
layer = build_layer_with_wizard_properties(wizard_properties)
|
|
layer.options['style_properties'] = { 'autogenerated' => false, 'wadus' => 'wadus' }
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties'].should_not be_nil
|
|
style_properties = poro_options['style_properties']
|
|
style_properties.should_not be_nil
|
|
style_properties['autogenerated'].should be_false
|
|
style_properties['wadus'].should eq 'wadus'
|
|
end
|
|
|
|
it "doesn't autogenerate `style_properties` if `wizard_properties` is not present or is empty" do
|
|
layer = build_layer_with_wizard_properties(wizard_properties)
|
|
layer.options.delete('wizard_properties')
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties'].should be_nil
|
|
style_properties = poro_options['style_properties']
|
|
style_properties.should be_nil
|
|
|
|
layer = build_layer_with_wizard_properties(wizard_properties)
|
|
layer.options['wizard_properties'] = nil
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties'].should be_nil
|
|
style_properties = poro_options['style_properties']
|
|
style_properties.should be_nil
|
|
|
|
layer = build_layer_with_wizard_properties({})
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties'].should_not be_nil
|
|
style_properties = poro_options['style_properties']
|
|
style_properties.should be_nil
|
|
end
|
|
|
|
it "doesn't autogenerate `style_properties` if type is not mapped" do
|
|
unknown_type = 'wadus'
|
|
layer = build_layer_with_wizard_properties('type' => unknown_type)
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties']['type'].should eq unknown_type
|
|
poro_options['style_properties'].should be_nil
|
|
end
|
|
|
|
describe 'options migration' do
|
|
it 'migrates tile_style_custom to cartocss_custom (defaults to false)' do
|
|
layer = build_layer_with_wizard_properties(wizard_properties(properties: {}))
|
|
layer.options.delete('tile_style_custom')
|
|
presenter_with_style_properties(layer).to_poro['options']['cartocss_custom'].should eq false
|
|
layer.options['tile_style_custom'] = true
|
|
presenter_with_style_properties(layer).to_poro['options']['cartocss_custom'].should eq true
|
|
layer.options['tile_style_custom'] = false
|
|
presenter_with_style_properties(layer).to_poro['options']['cartocss_custom'].should eq false
|
|
end
|
|
|
|
it 'migrates tile_style_history to cartocss_history (defaults to [])' do
|
|
layer = build_layer_with_wizard_properties(wizard_properties(properties: {}))
|
|
layer.options.delete('tile_style_history')
|
|
presenter_with_style_properties(layer).to_poro['options']['cartocss_history'].should eq []
|
|
layer.options['tile_style_history'] = ['wadus', 'wadus2']
|
|
presenter_with_style_properties(layer).to_poro['options']['cartocss_history'].should eq ['wadus', 'wadus2']
|
|
layer.options['tile_style_history'] = []
|
|
presenter_with_style_properties(layer).to_poro['options']['cartocss_history'].should eq []
|
|
end
|
|
end
|
|
|
|
describe 'simple' do
|
|
it 'is generated from several types' do
|
|
types_generating_simple = %w(polygon bubble choropleth category)
|
|
types_generating_simple.each do |type_generating_simple|
|
|
layer = build_layer_with_wizard_properties('type' => type_generating_simple)
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
poro_options['wizard_properties']['type'].should eq type_generating_simple
|
|
style_properties = poro_options['style_properties']
|
|
style_properties['type'].should eq 'simple'
|
|
end
|
|
end
|
|
|
|
it 'does not have animated' do
|
|
layer = build_layer_with_wizard_properties(wizard_properties(properties: {}))
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
animated = poro_options['style_properties']['properties']['animated']
|
|
animated.should be_nil
|
|
end
|
|
|
|
it 'has defaults for labels' do
|
|
layer = build_layer_with_wizard_properties(wizard_properties(properties: {}))
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
labels = poro_options['style_properties']['properties']['labels']
|
|
labels.should_not be_nil
|
|
|
|
labels['enabled'].should eq false
|
|
labels['attribute'].should be_nil
|
|
labels['font'].should eq 'DejaVu Sans Book'
|
|
fill = labels['fill']
|
|
fill['size']['fixed'].should eq 10
|
|
fill_color = fill['color']
|
|
fill_color['fixed'].should eq '#000'
|
|
fill_color['opacity'].should eq 1
|
|
halo = labels['halo']
|
|
halo['size']['fixed'].should eq 1
|
|
halo_color = halo['color']
|
|
halo_color['fixed'].should eq '#111'
|
|
halo_color['opacity'].should eq 1
|
|
labels['offset'].should eq(-10)
|
|
labels['overlap'].should eq true
|
|
labels['placement'].should eq 'point'
|
|
end
|
|
|
|
it 'defaults missing labels attributes' do
|
|
properties = {
|
|
"text-face-name" => "whatever"
|
|
}
|
|
layer = build_layer_with_wizard_properties(wizard_properties(properties: properties))
|
|
poro_options = presenter_with_style_properties(layer).to_poro['options']
|
|
labels = poro_options['style_properties']['properties']['labels']
|
|
labels.should_not be_nil
|
|
|
|
labels['enabled'].should eq false
|
|
labels['font'].should eq 'whatever'
|
|
|
|
labels['attribute'].should be_nil
|
|
fill = labels['fill']
|
|
fill['size']['fixed'].should eq 10
|
|
fill_color = fill['color']
|
|
fill_color['fixed'].should eq '#000'
|
|
fill_color['opacity'].should eq 1
|
|
halo = labels['halo']
|
|
halo['size']['fixed'].should eq 1
|
|
halo_color = halo['color']
|
|
halo_color['fixed'].should eq '#111'
|
|
halo_color['opacity'].should eq 1
|
|
labels['offset'].should eq(-10)
|
|
labels['overlap'].should eq true
|
|
labels['placement'].should eq 'point'
|
|
end
|
|
end
|
|
|
|
describe 'wizard migration' do
|
|
COLOR = '#fabada'.freeze
|
|
COLOR_1 = "#FACADA".freeze
|
|
COLOR_2 = "#TACADA".freeze
|
|
OPACITY = 0.3
|
|
|
|
describe 'polygon' do
|
|
describe 'polygon' do
|
|
let(:marker_width) { 10 }
|
|
let(:polygon_wizard_properties) do
|
|
{
|
|
"type" => "polygon",
|
|
"properties" =>
|
|
{
|
|
"marker-width" => marker_width,
|
|
"marker-fill" => "#FF6600",
|
|
"marker-opacity" => 0.9,
|
|
"marker-allow-overlap" => true,
|
|
"marker-placement" => "point",
|
|
"marker-type" => "ellipse",
|
|
"marker-line-width" => 1,
|
|
"marker-line-color" => "#FFF",
|
|
"marker-line-opacity" => 1,
|
|
"marker-comp-op" => "none",
|
|
"text-name" => "None",
|
|
"text-face-name" => "DejaVu Sans Book",
|
|
"text-size" => 10,
|
|
"text-fill" => "#000",
|
|
"text-halo-fill" => "#FFF",
|
|
"text-halo-radius" => 1,
|
|
"text-dy" => -10,
|
|
"text-allow-overlap" => true,
|
|
"text-placement-type" => "dummy",
|
|
"text-label-position-tolerance" => 0,
|
|
"text-placement" => "point",
|
|
"geometry_type" => "point"
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'sets fill size from marker-width' do
|
|
layer = build_layer_with_wizard_properties(polygon_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
properties = options['style_properties']['properties']
|
|
fill_size = properties['fill']['size']
|
|
fill_size['fixed'].should eq marker_width
|
|
end
|
|
|
|
it 'ignores property' do
|
|
polygon_wizard_properties['properties']['property'] = 'wadus_property'
|
|
layer = build_layer_with_wizard_properties(polygon_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
|
|
properties = options['style_properties']['properties']
|
|
JSON.dump(properties).should_not include 'wadus_property'
|
|
end
|
|
end
|
|
|
|
describe 'polygon-fill, marker-fill become "color fill" structure' do
|
|
it 'setting opacity 1 if unknown' do
|
|
%w(polygon-fill marker-fill).each do |property|
|
|
properties = { property => COLOR }
|
|
layer = build_layer_with_wizard_properties(wizard_properties(properties: properties))
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
|
|
options['wizard_properties']['properties'][property].should eq COLOR
|
|
|
|
fill_color = options['style_properties']['properties']['fill']['color']
|
|
fill_color.should eq('fixed' => COLOR, 'opacity' => 1)
|
|
end
|
|
end
|
|
|
|
it 'setting related opacity if known' do
|
|
%w(polygon marker).each do |property|
|
|
properties = { "#{property}-fill" => COLOR, "#{property}-opacity" => OPACITY }
|
|
layer = build_layer_with_wizard_properties(wizard_properties(properties: properties))
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
|
|
options['wizard_properties']['properties']["#{property}-fill"].should eq COLOR
|
|
|
|
fill_color = options['style_properties']['properties']['fill']['color']
|
|
fill_color.should eq('fixed' => COLOR, 'opacity' => OPACITY)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'cluster' do
|
|
let(:query_wrapper) { "with meta ... <%= sql %> ..." }
|
|
let(:cluster_options) do
|
|
{
|
|
"query_wrapper" => query_wrapper,
|
|
"wizard_properties" => {
|
|
"type" => "cluster",
|
|
"properties" => { "method" => "3 Buckets", "marker-fill" => "#FD8D3C" }
|
|
}
|
|
}
|
|
end
|
|
before(:each) do
|
|
layer = FactoryGirl.build(:carto_layer, options: cluster_options)
|
|
@options = presenter_with_style_properties(layer).to_poro['options']
|
|
@properties = @options['style_properties']['properties']
|
|
end
|
|
|
|
it 'sets type simple and cartocss_custom to true' do
|
|
@options['style_properties']['type'].should eq 'simple'
|
|
@options['cartocss_custom'].should eq true
|
|
end
|
|
|
|
it 'sets query_wrapper at sql_wrap' do
|
|
@options['sql_wrap'].should eq query_wrapper
|
|
end
|
|
|
|
it 'ignores property' do
|
|
cluster_options['wizard_properties']['properties']['property'] = 'wadus_property'
|
|
layer = FactoryGirl.build(:carto_layer, options: cluster_options)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
properties = options['style_properties']['properties']
|
|
JSON.dump(properties).should_not include 'wadus_property'
|
|
end
|
|
end
|
|
|
|
describe 'bubble' do
|
|
let(:property) { "actor_foll" }
|
|
let(:quantile_function) { "Quantile" }
|
|
let(:qfunction) { quantile_function }
|
|
let(:radius_min) { 10 }
|
|
let(:radius_max) { 25 }
|
|
let(:marker_comp_op) { "multiply" }
|
|
let(:bubble_wizard_properties) do
|
|
{
|
|
"type" => "bubble",
|
|
"properties" =>
|
|
{
|
|
"property" => property,
|
|
"qfunction" => qfunction,
|
|
"radius_min" => radius_min,
|
|
"radius_max" => radius_max,
|
|
"marker-fill" => COLOR,
|
|
"marker-opacity" => OPACITY,
|
|
"marker-line-width" => 1,
|
|
"marker-line-color" => "#FFF",
|
|
"marker-line-opacity" => 1,
|
|
"zoom" => 4,
|
|
"geometry_type" => "point",
|
|
"text-placement-type" => "simple",
|
|
"text-label-position-tolerance" => 10,
|
|
"marker-comp-op" => marker_comp_op
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
layer = build_layer_with_wizard_properties(bubble_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
@properties = options['style_properties']['properties']
|
|
@fill_color = @properties['fill']['color']
|
|
@fill_size = @properties['fill']['size']
|
|
end
|
|
|
|
it 'marker-comp-op becomes blending' do
|
|
expect(@properties).to include('blending' => marker_comp_op)
|
|
end
|
|
|
|
describe 'fill size' do
|
|
it 'groups radius_min, radius_max into fill size range' do
|
|
expect(@fill_size).to include('range' => [radius_min, radius_max])
|
|
end
|
|
|
|
it 'property becomes attribute' do
|
|
expect(@fill_size).to include('attribute' => property)
|
|
end
|
|
|
|
it 'bins is set to 10' do
|
|
expect(@fill_size).to include('bins' => 10)
|
|
end
|
|
|
|
it 'qfunction becomes quantification, and Quantile is translated to quantiles' do
|
|
expect(@fill_size).to include('quantification' => 'quantiles')
|
|
end
|
|
|
|
it 'does not include animated' do
|
|
expect(@properties['animated']).to be_nil
|
|
end
|
|
end
|
|
|
|
describe 'fill color' do
|
|
it 'takes fixed color and opacity from marker-*' do
|
|
expect(@fill_color).to include('fixed' => COLOR, 'opacity' => OPACITY)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'choropleth' do
|
|
let(:property) { "actor_foll" }
|
|
let(:number_of_buckets) { 7 }
|
|
let(:qfunction) { "Quantile" }
|
|
let(:color_ramp) { "red" }
|
|
let(:choropleth_wizard_properties) do
|
|
{
|
|
"type" => "choropleth",
|
|
"properties" =>
|
|
{
|
|
"property" => property,
|
|
"method" => "#{number_of_buckets} Buckets",
|
|
"qfunction" => qfunction,
|
|
"color_ramp" => color_ramp,
|
|
"marker-opacity" => OPACITY,
|
|
"marker-width" => 10,
|
|
"marker-allow-overlap" => true,
|
|
"marker-placement" => "point",
|
|
"marker-type" => "ellipse",
|
|
"marker-line-width" => 1,
|
|
"marker-line-color" => "#FFF",
|
|
"marker-line-opacity" => 1,
|
|
"marker-comp-op" => "none",
|
|
"zoom" => 4,
|
|
"geometry_type" => "point"
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
layer = build_layer_with_wizard_properties(choropleth_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
@fill_color = options['style_properties']['properties']['fill']['color']
|
|
@fill_size = options['style_properties']['properties']['fill']['size']
|
|
end
|
|
|
|
describe 'fill' do
|
|
it 'has fixed size 10 by default' do
|
|
expect(@fill_size).to include('fixed' => 10)
|
|
end
|
|
|
|
it 'transform color_ramp to color array in range' do
|
|
# Commented because this might change soon
|
|
# expect(@fill_color).to include('range' => "['#FFEDA0', '#FEB24C', '#F03B20']")
|
|
expect(@fill_color).to include('range' => color_ramp)
|
|
end
|
|
|
|
it 'property becomes attribute' do
|
|
expect(@fill_color).to include('attribute' => property)
|
|
end
|
|
|
|
it 'method generates bins' do
|
|
expect(@fill_color).to include('bins' => number_of_buckets)
|
|
end
|
|
|
|
it 'qfunction becomes quantification, and Quantile is translated to quantiles' do
|
|
expect(@fill_color).to include('quantification' => 'quantiles')
|
|
end
|
|
|
|
it 'takes opacity from marker-* or polygon-*' do
|
|
expect(@fill_color).to include('opacity' => OPACITY)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'category' do
|
|
let(:property) { "aforo" }
|
|
let(:opacity) { 0.456 }
|
|
let(:marker_line_width) { 1 }
|
|
let(:marker_line_color) { "#FFF" }
|
|
let(:marker_line_opacity) { 0.7 }
|
|
let(:title_1) { 100 }
|
|
let(:title_2) { 200 }
|
|
let(:line_width) { 0.43 }
|
|
let(:line_color) { "#FFA" }
|
|
let(:line_opacity) { 0.91 }
|
|
let(:category_wizard_properties) do
|
|
{
|
|
"type" => "category",
|
|
"properties" => {
|
|
"property" => property,
|
|
"zoom" => "15",
|
|
"geometry_type" => 'point',
|
|
"text-placement-type" => "simple",
|
|
"text-label-position-tolerance" => 10,
|
|
"categories" => [
|
|
{
|
|
"title" => title_1,
|
|
"title_type" => "number",
|
|
"color" => COLOR_1,
|
|
"value_type" => "color"
|
|
},
|
|
{
|
|
"title" => title_2,
|
|
"title_type" => "number",
|
|
"color" => COLOR_2,
|
|
"value_type" => "color"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:marker_category_properties) do
|
|
{
|
|
"marker-width" => 10,
|
|
"marker-opacity" => opacity,
|
|
"marker-allow-overlap" => true,
|
|
"marker-placement" => "point",
|
|
"marker-type" => "ellipse",
|
|
"marker-line-width" => marker_line_width,
|
|
"marker-line-color" => marker_line_color,
|
|
"marker-line-opacity" => marker_line_opacity
|
|
}
|
|
end
|
|
|
|
let(:line_category_properties) do
|
|
{
|
|
"line-width" => line_width,
|
|
"line-color" => line_color,
|
|
"line-opacity" => line_opacity
|
|
}
|
|
end
|
|
|
|
describe 'polygon geometry type' do
|
|
before(:each) do
|
|
properties = category_wizard_properties
|
|
properties['properties']['geometry_type'] = 'polygon'
|
|
properties['properties'].merge!(line_category_properties)
|
|
layer = build_layer_with_wizard_properties(properties)
|
|
@options = presenter_with_style_properties(layer).to_poro['options']
|
|
@fill_color = @options['style_properties']['properties']['fill']['color']
|
|
@fill_size = @options['style_properties']['properties']['fill']['size']
|
|
@stroke_color = @options['style_properties']['properties']['stroke']['color']
|
|
@stroke_size = @options['style_properties']['properties']['stroke']['size']
|
|
end
|
|
|
|
it 'sets "none" as default blending' do
|
|
@options['style_properties']['properties']['blending'].should eq 'none'
|
|
end
|
|
|
|
it 'line-width becomes fixed stroke size' do
|
|
expect(@stroke_size).to include('fixed' => line_width)
|
|
end
|
|
|
|
it 'line-color becomes fixed stroke color' do
|
|
expect(@stroke_color).to include('fixed' => line_color)
|
|
end
|
|
|
|
it 'line-opacity becomes opacity' do
|
|
expect(@stroke_color).to include('opacity' => line_opacity)
|
|
end
|
|
end
|
|
|
|
describe 'point geometry type' do
|
|
before(:each) do
|
|
properties = category_wizard_properties
|
|
properties['properties'].merge!(marker_category_properties)
|
|
layer = build_layer_with_wizard_properties(properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
@fill_color = options['style_properties']['properties']['fill']['color']
|
|
@fill_size = options['style_properties']['properties']['fill']['size']
|
|
@stroke_color = options['style_properties']['properties']['stroke']['color']
|
|
@stroke_size = options['style_properties']['properties']['stroke']['size']
|
|
end
|
|
|
|
describe 'fill' do
|
|
it 'has fill size fixed 10' do
|
|
expect(@fill_size).to include('fixed' => 10)
|
|
end
|
|
|
|
it 'generates not fixed color but range from categories colors' do
|
|
expect(@fill_color).not_to include('fixed' => anything)
|
|
expect(@fill_color).to include('range' => [COLOR_1, COLOR_2])
|
|
end
|
|
|
|
it 'generates domain from titles' do
|
|
expect(@fill_color).to include('domain' => [title_1, title_2])
|
|
end
|
|
|
|
it 'property becomes attribute' do
|
|
expect(@fill_color).to include('attribute' => property)
|
|
expect(@fill_size).not_to include('attribute' => property)
|
|
end
|
|
|
|
it 'marker-opacity becomes opacity' do
|
|
expect(@fill_color).to include('opacity' => opacity)
|
|
end
|
|
|
|
it 'bins defaults to 10' do
|
|
expect(@fill_color).to include('bins' => 10)
|
|
end
|
|
end
|
|
|
|
describe 'stroke' do
|
|
it 'marker-line-width becomes fixed stroke size' do
|
|
expect(@stroke_size).to include('fixed' => marker_line_width)
|
|
end
|
|
|
|
it 'marker-line-color becomes fixed stroke color' do
|
|
expect(@stroke_color).to include('fixed' => marker_line_color)
|
|
end
|
|
|
|
it 'marker-line-opacity becomes opacity' do
|
|
expect(@stroke_color).to include('opacity' => marker_line_opacity)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'line geometry type' do
|
|
before(:each) do
|
|
properties = category_wizard_properties
|
|
properties['properties'].merge!(line_category_properties)
|
|
properties['properties']['geometry_type'] = 'line'
|
|
layer = build_layer_with_wizard_properties(properties)
|
|
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
@stroke_color = options['style_properties']['properties']['stroke']['color']
|
|
@stroke_size = options['style_properties']['properties']['stroke']['size']
|
|
end
|
|
|
|
it 'generates fixed size from line_width' do
|
|
expect(@stroke_size).to include('fixed' => line_width)
|
|
end
|
|
|
|
it 'generates color range from categories colors' do
|
|
expect(@stroke_color).to include('range' => [COLOR_1, COLOR_2])
|
|
end
|
|
|
|
it 'property becomes attribute' do
|
|
expect(@stroke_color).to include('attribute' => property)
|
|
expect(@stroke_size).not_to include('attribute' => property)
|
|
end
|
|
|
|
it 'line-width becomes fixed stroke size' do
|
|
expect(@stroke_size).to include('fixed' => line_width)
|
|
end
|
|
|
|
it 'line-color becomes fixed stroke color' do
|
|
expect(@stroke_color).to include('fixed' => line_color)
|
|
end
|
|
|
|
it 'line-opacity becomes opacity' do
|
|
expect(@stroke_color).to include('opacity' => line_opacity)
|
|
end
|
|
|
|
it 'bins defaults to 10' do
|
|
expect(@stroke_color).to include('bins' => 10)
|
|
end
|
|
|
|
describe 'stroke' do
|
|
let(:line_width) { 6 }
|
|
let(:line_color) { "#0F3B82" }
|
|
let(:line_opacity) { 0.7 }
|
|
let(:line_comp_op) { "none" }
|
|
let(:simple_line_wizard_properties) do
|
|
{
|
|
"type" => "polygon",
|
|
"geometry_type" => "line",
|
|
"properties" =>
|
|
{
|
|
"line-width" => line_width,
|
|
"line-color" => line_color,
|
|
"line-opacity" => line_opacity,
|
|
"line-comp-op" => line_comp_op,
|
|
"geometry_type" => "line"
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
layer = build_layer_with_wizard_properties(simple_line_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
@properties = options['style_properties']['properties']
|
|
@stroke = @properties['stroke']
|
|
@stroke_color = @stroke['color']
|
|
@stroke_size = @stroke['size']
|
|
end
|
|
|
|
it 'generates stroke' do
|
|
expect(@stroke_size).to include('fixed' => line_width)
|
|
expect(@stroke_color).to include('fixed' => line_color)
|
|
expect(@stroke_color).to include('opacity' => line_opacity)
|
|
expect(@properties).to include('blending' => line_comp_op)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'torque wizard family' do
|
|
describe 'animated' do
|
|
it 'property becomes attribute' do
|
|
expect(@animated).to include('attribute' => property)
|
|
end
|
|
|
|
it 'property becomes attribute only at animated' do
|
|
expect(@fill['color']).not_to include('attribute' => property)
|
|
end
|
|
|
|
it 'torque-duration becomes duration' do
|
|
expect(@animated).to include('duration' => torque_duration)
|
|
end
|
|
|
|
it 'torque-frame-count becomes steps' do
|
|
expect(@animated).to include('steps' => torque_frame_count)
|
|
end
|
|
|
|
it 'torque-resolution becomes resolution' do
|
|
expect(@animated).to include('resolution' => torque_resolution)
|
|
end
|
|
|
|
it 'torque-trails becomes trails' do
|
|
expect(@animated).to include('trails' => torque_trails)
|
|
end
|
|
|
|
it "doesn't include redundant animated enabled option" do
|
|
expect(@animated).not_to include('enabled')
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'torque blend wizard family' do
|
|
describe 'torque-blend-mode' do
|
|
it 'becomes blending' do
|
|
expect(@properties).to include('blending' => torque_blend_mode)
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'torque animated family' do
|
|
it_behaves_like 'torque wizard family'
|
|
|
|
it 'type is animated' do
|
|
expect(@style).to include('type' => 'animation')
|
|
end
|
|
|
|
it 'generates a timeseries widget' do
|
|
@layer.widgets.where(type: 'time-series').any?
|
|
end
|
|
end
|
|
|
|
describe 'torque' do
|
|
it_behaves_like 'torque animated family'
|
|
it_behaves_like 'torque blend wizard family'
|
|
|
|
let(:torque_blend_mode) { "lighter" }
|
|
let(:property) { "fecha_date" }
|
|
let(:marker_width) { 6 }
|
|
let(:torque_cumulative) { false }
|
|
let(:torque_duration) { 30 }
|
|
let(:torque_frame_count) { 256 }
|
|
let(:torque_resolution) { 2 }
|
|
let(:torque_trails) { 3 }
|
|
let(:torque_wizard_properties) do
|
|
{
|
|
"type" => "torque",
|
|
"properties" =>
|
|
{
|
|
"torque-cumulative" => torque_cumulative,
|
|
"property" => property,
|
|
"marker-type" => "ellipse",
|
|
"layer-type" => "torque",
|
|
"marker-width" => marker_width,
|
|
"marker-fill" => "#0F3B82",
|
|
"marker-opacity" => 0.9,
|
|
"marker-line-width" => 0,
|
|
"marker-line-color" => "#FFF",
|
|
"marker-line-opacity" => 1,
|
|
"torque-duration" => torque_duration,
|
|
"torque-frame-count" => torque_frame_count,
|
|
"torque-blend-mode" => torque_blend_mode,
|
|
"torque-trails" => torque_trails,
|
|
"torque-resolution" => torque_resolution,
|
|
"zoom" => 15,
|
|
"geometry_type" => "point"
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
@layer = build_layer_with_wizard_properties(torque_wizard_properties)
|
|
options = presenter_with_style_properties(@layer).to_poro['options']
|
|
@style = options['style_properties']
|
|
@properties = @style['properties']
|
|
@animated = @properties['animated']
|
|
@fill = @properties['fill']
|
|
@fill_color = @fill['color']
|
|
@fill_size = @fill['size']
|
|
end
|
|
|
|
it 'torque-cumulative becomes overlap' do
|
|
expect(@animated).to include('overlap' => torque_cumulative)
|
|
end
|
|
|
|
it 'marker-width becomes fill size fixed' do
|
|
expect(@fill_size).to include('fixed' => marker_width)
|
|
end
|
|
|
|
describe 'torque-blend-mode' do
|
|
it 'turns source-over into src-over' do
|
|
source_over_torque_wizard_properties = torque_wizard_properties
|
|
source_over_torque_wizard_properties['properties']['torque-blend-mode'] = 'source-over'
|
|
layer = build_layer_with_wizard_properties(source_over_torque_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
options['style_properties']['properties']['blending'].should eq 'src-over'
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'torque cat' do
|
|
it_behaves_like 'torque animated family'
|
|
it_behaves_like 'torque blend wizard family'
|
|
|
|
let(:torque_blend_mode) { "lighter" }
|
|
let(:property) { "fecha_date" }
|
|
let(:property_cat) { "aforo" }
|
|
let(:torque_duration) { 30 }
|
|
let(:torque_frame_count) { 256 }
|
|
let(:torque_resolution) { 2 }
|
|
let(:torque_trails) { 3 }
|
|
let(:torque_cat_wizard_properties) do
|
|
{
|
|
"type" => "torque",
|
|
"properties" =>
|
|
{
|
|
"property" => property,
|
|
"marker-type" => "ellipse",
|
|
"layer-type" => "torque",
|
|
"property_cat" => property_cat,
|
|
"marker-width" => 6,
|
|
"marker-fill" => "#0F3B82",
|
|
"marker-opacity" => 0.9,
|
|
"marker-line-width" => 0,
|
|
"marker-line-color" => "#FFF",
|
|
"marker-line-opacity" => 1,
|
|
"torque-duration" => torque_duration,
|
|
"torque-frame-count" => torque_frame_count,
|
|
"torque-blend-mode" => torque_blend_mode,
|
|
"torque-trails" => torque_trails,
|
|
"torque-resolution" => torque_resolution,
|
|
"zoom" => 15,
|
|
"geometry_type" => "point",
|
|
"categories" => [
|
|
{
|
|
"title" => 100,
|
|
"title_type" => "number",
|
|
"color" => COLOR_1,
|
|
"value_type" => "color"
|
|
},
|
|
{
|
|
"title" => 200,
|
|
"title_type" => "number",
|
|
"color" => COLOR_2,
|
|
"value_type" => "color"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
@layer = build_layer_with_wizard_properties(torque_cat_wizard_properties)
|
|
options = presenter_with_style_properties(@layer).to_poro['options']
|
|
@style = options['style_properties']
|
|
@properties = @style['properties']
|
|
@animated = @properties['animated']
|
|
@fill = @properties['fill']
|
|
@fill_color = @fill['color']
|
|
@fill_size = @fill['size']
|
|
end
|
|
|
|
it 'generates color range from categories colors' do
|
|
expect(@fill_color).to include('range' => [COLOR_1, COLOR_2])
|
|
end
|
|
|
|
it 'generates color range based on attribute' do
|
|
expect(@fill_color).to include('attribute' => 'aforo')
|
|
end
|
|
end
|
|
|
|
describe 'density' do
|
|
let(:query_wrapper) { "with meta ... <%= sql %> ..." }
|
|
let(:color_ramp) { "red" }
|
|
let(:polygon_size) { 15 }
|
|
let(:density_wizard_properties) do
|
|
{
|
|
"type" => "density",
|
|
"properties" =>
|
|
{
|
|
"geometry_type" => "point",
|
|
"method" => "5 Buckets",
|
|
"color_ramp" => color_ramp,
|
|
"polygon-opacity" => 0.8,
|
|
"line-width" => 0.5,
|
|
"line-color" => "#FFF",
|
|
"line-opacity" => 1,
|
|
"polygon-size" => polygon_size,
|
|
"polygon-comp-op" => "none",
|
|
"zoom" => 15
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
options = { 'query_wrapper' => query_wrapper, 'wizard_properties' => density_wizard_properties }
|
|
layer = FactoryGirl.build(:carto_layer, options: options)
|
|
@options = presenter_with_style_properties(layer).to_poro['options']
|
|
|
|
@style = @options['style_properties']
|
|
@properties = @style['properties']
|
|
@aggregation = @properties['aggregation']
|
|
@fill_color = @properties['fill']['color']
|
|
@stroke = @properties['stroke']
|
|
end
|
|
|
|
it 'sets query_wrapper at sql_wrap' do
|
|
@options['sql_wrap'].should eq query_wrapper
|
|
end
|
|
|
|
it 'maps point geometry_type to hexabins type' do
|
|
expect(@style).to include('type' => 'hexabins')
|
|
end
|
|
|
|
it 'maps Rectangles geometry_type to squares type' do
|
|
properties = density_wizard_properties
|
|
properties['properties']['geometry_type'] = 'Rectangles'
|
|
layer = build_layer_with_wizard_properties(properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
expect(options['style_properties']).to include('type' => 'squares')
|
|
end
|
|
|
|
describe 'aggregation' do
|
|
it 'has defaults' do
|
|
expect(@aggregation).to include(
|
|
"value" => {
|
|
"operator" => 'COUNT',
|
|
"attribute" => ''
|
|
}
|
|
)
|
|
end
|
|
|
|
it 'takes size from polygon-size' do
|
|
expect(@aggregation).to include("size" => polygon_size)
|
|
end
|
|
end
|
|
|
|
describe 'fill' do
|
|
it 'transform color_ramp to color range' do
|
|
expect(@fill_color).to include('range' => color_ramp)
|
|
end
|
|
end
|
|
|
|
describe 'stroke' do
|
|
it 'takes width from line-width' do
|
|
expect(@stroke['size']).to include('fixed' => 0.5)
|
|
end
|
|
|
|
it 'takes color from line-color' do
|
|
expect(@stroke['color']).to include('fixed' => '#FFF')
|
|
end
|
|
|
|
it 'takes opacity from line-opacity' do
|
|
expect(@stroke['color']).to include('opacity' => 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'heatmap' do
|
|
shared_examples_for 'heatmap' do
|
|
describe 'aggregation' do
|
|
before(:each) do
|
|
@aggregation = @style['properties']['aggregation']
|
|
end
|
|
|
|
it 'takes size from torque-resolution' do
|
|
expect(@aggregation).to include('size' => torque_resolution)
|
|
end
|
|
|
|
it 'has COUNT for value operator and empty string for attribute' do
|
|
expect(@aggregation['value']).to include('operator' => 'COUNT', 'attribute' => '')
|
|
end
|
|
end
|
|
|
|
describe 'properties' do
|
|
before(:each) do
|
|
@properties = @style['properties']
|
|
end
|
|
|
|
describe 'fill' do
|
|
it 'has size fixed 35' do
|
|
expect(@fill['size']).to include('fixed' => 35)
|
|
end
|
|
|
|
describe 'color' do
|
|
before(:each) do
|
|
@color = @fill['color']
|
|
end
|
|
|
|
it "has attribute: points_agg, default range color array, bins: 6 and opacity from marker_opacity" do
|
|
expect(@color).to include('attribute' => 'points_agg',
|
|
'range' => ['blue', 'cyan', 'lightgreen', 'yellow', 'orange', 'red'],
|
|
'bins' => 6,
|
|
'opacity' => marker_opacity)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
let(:heatmap_wizard_properties) do
|
|
{ "type" => "torque_heat",
|
|
"properties" =>
|
|
{
|
|
"marker-width" => 35,
|
|
"layer-type" => "torque",
|
|
"marker-file" => "url(http://s3.amazonaws.com/com.cartodb.assets.static/alphamarker.png)",
|
|
"image-filters" => "colorize-alpha(blue, cyan, lightgreen, yellow , orange, red)",
|
|
"marker-opacity" => marker_opacity,
|
|
"heat-animated" => animated,
|
|
"torque-cumulative" => false,
|
|
"property" => property,
|
|
"torque-duration" => torque_duration,
|
|
"torque-frame-count" => torque_frame_count,
|
|
"torque-trails" => torque_trails,
|
|
"torque-resolution" => torque_resolution,
|
|
"zoom" => 3,
|
|
"geometry_type" => "point",
|
|
"text-placement-type" => "simple",
|
|
"text-label-position-tolerance" => 10
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
@layer = build_layer_with_wizard_properties(heatmap_wizard_properties)
|
|
options = presenter_with_style_properties(@layer).to_poro['options']
|
|
|
|
@style = options['style_properties']
|
|
@properties = @style['properties']
|
|
@animated = @properties['animated']
|
|
@fill = @properties['fill']
|
|
@fill_color = @fill['color']
|
|
@fill_size = @fill['size']
|
|
end
|
|
|
|
describe 'animated' do
|
|
let(:torque_resolution) { 8 }
|
|
let(:marker_opacity) { 0.4 }
|
|
let(:torque_trails) { 2 }
|
|
let(:torque_frame_count) { 32 }
|
|
let(:torque_duration) { 10 }
|
|
let(:property) { "actor_post" }
|
|
let(:animated) { true }
|
|
|
|
it_behaves_like 'torque animated family'
|
|
it_behaves_like 'heatmap'
|
|
end
|
|
|
|
describe 'without animation' do
|
|
let(:torque_resolution) { 8 }
|
|
let(:marker_opacity) { 0.4 }
|
|
let(:torque_trails) { 2 }
|
|
let(:torque_frame_count) { 1 }
|
|
let(:torque_duration) { 10 }
|
|
let(:property) { "cartodb_id" }
|
|
let(:animated) { false }
|
|
|
|
it 'is generated from torque_heat' do
|
|
expect(@style).to include('type' => 'heatmap')
|
|
end
|
|
|
|
it_behaves_like 'torque wizard family'
|
|
it_behaves_like 'heatmap'
|
|
end
|
|
end
|
|
|
|
describe 'labels' do
|
|
describe 'without text-* properties' do
|
|
let(:no_text_wizard_properties) do
|
|
{
|
|
"type" => "choropleth",
|
|
"properties" => { "property" => 'actor_foll' }
|
|
}
|
|
end
|
|
|
|
it 'generates disabled labels' do
|
|
layer = build_layer_with_wizard_properties(no_text_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
options['style_properties']['properties']['labels']['enabled'].should be_false
|
|
end
|
|
end
|
|
|
|
describe 'with text-name `None`' do
|
|
let(:none_text_name_wizard_properties) do
|
|
{
|
|
"type" => "choropleth",
|
|
"properties" =>
|
|
{
|
|
"text-name" => 'None',
|
|
"text-face-name" => 'something'
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'generates disabled labels' do
|
|
layer = build_layer_with_wizard_properties(none_text_name_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
options['style_properties']['properties']['labels']['enabled'].should be_false
|
|
end
|
|
end
|
|
|
|
describe 'with text-* properties' do
|
|
let(:text_name) { "Something" }
|
|
let(:text_face_name) { "DejaVu Sans Book" }
|
|
let(:text_size) { 10 }
|
|
let(:text_fill) { "#000" }
|
|
let(:text_halo_radius) { 1 }
|
|
let(:text_halo_fill) { "#ABC" }
|
|
let(:text_dy) { -10 }
|
|
let(:text_allow_overlap) { true }
|
|
let(:text_placement_type) { "simple" }
|
|
let(:text_wizard_properties) do
|
|
{
|
|
"type" => "choropleth",
|
|
"properties" =>
|
|
{
|
|
"text-name" => text_name,
|
|
"text-face-name" => text_face_name,
|
|
"text-size" => text_size,
|
|
"text-fill" => text_fill,
|
|
"text-halo-fill" => text_halo_fill,
|
|
"text-halo-radius" => text_halo_radius,
|
|
"text-dy" => text_dy,
|
|
"text-allow-overlap" => text_allow_overlap,
|
|
"text-placement-type" => text_placement_type,
|
|
"text-label-position-tolerance" => 10,
|
|
"text-placement" => "point"
|
|
}
|
|
}
|
|
end
|
|
|
|
before(:each) do
|
|
layer = build_layer_with_wizard_properties(text_wizard_properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
@labels = options['style_properties']['properties']['labels']
|
|
end
|
|
|
|
it 'generates labels' do
|
|
@labels.should_not be_nil
|
|
@labels['enabled'].should be_true
|
|
end
|
|
|
|
it 'text-name generates attribute' do
|
|
expect(@labels).to include('attribute' => text_name)
|
|
end
|
|
|
|
it 'text-name `None` generates `nil` attribute' do
|
|
properties = text_wizard_properties
|
|
properties['properties']['text-name'] = 'None'
|
|
layer = build_layer_with_wizard_properties(properties)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
labels = options['style_properties']['properties']['labels']
|
|
expect(labels).to include('attribute' => nil)
|
|
end
|
|
|
|
it 'text-face-name generates font' do
|
|
expect(@labels).to include('font' => text_face_name)
|
|
end
|
|
|
|
it 'text-dy generates offset' do
|
|
expect(@labels).to include('offset' => text_dy)
|
|
end
|
|
|
|
it 'text-allow-overlap generates overlap' do
|
|
expect(@labels).to include('overlap' => text_allow_overlap)
|
|
end
|
|
|
|
it 'text-placement-type generates placement' do
|
|
expect(@labels).to include('placement' => text_placement_type)
|
|
end
|
|
|
|
describe 'fill' do
|
|
before(:each) do
|
|
@labels_fill = @labels['fill']
|
|
@labels_fill.should_not be_nil
|
|
@labels_fill_size = @labels_fill['size']
|
|
@labels_fill_color = @labels_fill['color']
|
|
end
|
|
|
|
it 'text-size generates fill size fixed' do
|
|
expect(@labels_fill_size).to include('fixed' => text_size)
|
|
end
|
|
|
|
it 'text-fill generates fill color fixed and opacity 1 if not present' do
|
|
expect(@labels_fill_color).to include('fixed' => text_fill, 'opacity' => 1)
|
|
end
|
|
|
|
it 'text-fill generates fill color fixed and opacity if present' do
|
|
text_with_opacity = text_wizard_properties
|
|
text_with_opacity["properties"]["text-opacity"] = OPACITY
|
|
layer = build_layer_with_wizard_properties(text_with_opacity)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
labels_fill_color = options['style_properties']['properties']['labels']['fill']['color']
|
|
expect(labels_fill_color).to include('fixed' => text_fill, 'opacity' => OPACITY)
|
|
end
|
|
end
|
|
|
|
describe 'halo' do
|
|
before(:each) do
|
|
@labels_halo = @labels['halo']
|
|
@labels_halo.should_not be_nil
|
|
@labels_halo_size = @labels_halo['size']
|
|
@labels_halo_color = @labels_halo['color']
|
|
end
|
|
|
|
it 'text-halo-radius generates halo size fixed' do
|
|
expect(@labels_halo_size).to include('fixed' => text_halo_radius)
|
|
end
|
|
|
|
it 'text-halo-fill generates halo color fixed and opacity 1 if not present' do
|
|
expect(@labels_halo_color).to include('fixed' => text_halo_fill, 'opacity' => 1)
|
|
end
|
|
|
|
it 'text-halo-fill generates fill color fixed and opacity if present' do
|
|
halo_with_opacity = text_wizard_properties
|
|
halo_with_opacity["properties"]["text-halo-opacity"] = OPACITY
|
|
layer = build_layer_with_wizard_properties(halo_with_opacity)
|
|
options = presenter_with_style_properties(layer).to_poro['options']
|
|
labels_halo_color = options['style_properties']['properties']['labels']['halo']['color']
|
|
expect(labels_halo_color).to include('fixed' => text_halo_fill, 'opacity' => OPACITY)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|