lib/distribution.rb
# An array of OTU distributions
#
class Distribution
# A cache of the aggregate data to be parsed into json per OTU
# {
# otu_id => [
# [ asserted_distribution, geographic_area, type ],
# [ collecting_event, georeference, type ],
# [ collecting_event, geographic_area, type ]
# ]
# }
attr_accessor :map_source_objects
# A parameter that indicates which types of data should be extracted for the list of OTUs provided
# [ asserted_distribution, :collecting_event_geographic_area, :collecting_event_georeference ]
attr_accessor :source_object_types
# the list of OTUs to generate distributions for
attr_accessor :otus
attr_accessor :preferred_georeference_only
# @param [Hash] args
def initialize(
source_object_types: [
:asserted_distribution,
:collecting_event_geographic_area,
:collecting_event_georeference],
otus: [],
preferred_georeference_only: false # does nothing at present
)
@preferred_georeference_only = preferred_georeference_only
@source_object_types = source_object_types
@otus = otus
end
# @return [Hash]
def map_source_objects
@map_source_objects ||= build_map_source_objects
end
# @return [Hash]
def build_map_source_objects
@map_source_objects = {} if @map_source_objects.nil?
otus.each do |o|
@map_source_objects[o.id] = []
source_object_types.each do |t|
insert_source_objects(o, t)
end
end
@map_source_objects
end
# @param [Otu] otu
# @param [String] type
def insert_source_objects(otu, type)
records_with_data = self.send("get_data_for_#{type}s", otu) # all returned data must have geographic_item at this point
records_with_data.each do |r|
self.send("insert_for_#{type}", otu, r, type)
end
end
# json insert necessary
# @param [Otu] otu
# @param [Source] source
# @param [String] type
# @return [Array]
def insert_for_asserted_distribution(otu, source, type)
insert_source_object(otu, source, source.geographic_area, type)
end
# @param [Otu] otu
# @param [Source] source
# @param [String] type
# @return [Array]
def insert_for_collecting_event_geographic_area(otu, source, type)
insert_source_object(otu, source, source.geographic_area, type)
end
# @param [Otu] otu
# @param [Source] source
# @param [String] type
def insert_for_collecting_event_georeference(otu, source, type)
georeferences = (preferred_georeference_only ? [source.georeferences.first] : source.georeferences)
georeferences.each do |g|
insert_source_object(otu, source, g, type)
end
end
# @param [Otu] otu
# @param [Source] source
# @param [Object] data
# @param [String] type
def insert_source_object(otu, source, data, type)
@map_source_objects[otu.id].push([source, data, type])
end
# @param [CollectingEvent] collecting_event
# @return [GeographicItem, nil]
def geographic_item_for_collecting_event_geographic_area(collecting_event)
collecting_event.geographic_area.geographic_items.first
end
# @param [CollectingEvent] collecting_event
# @return [GeographicItem, nil]
def geographic_item_for_collecting_event_georeference(collecting_event)
asserted_distribution.geographic_area.geographic_items.first
end
# @param [Otu] otu
# @return [Scope]
def get_data_for_asserted_distributions(otu)
otu.asserted_distributions # .where(geographic_area has a shape clause)
end
# have a better has_many method for this I think
# @param [Otu] otu
# @return [Scope]
def get_data_for_collecting_event_geographic_areas(otu)
otu.collecting_events.joins(geographic_area: [:geographic_items])
end
# @param [Otu] otu
# @return [Array]
def get_data_for_collecting_event_georeferences(otu)
[otu]
end
# rubocop:disable Metrics/MethodLength
# rubocop:disable Style/StringHashKeys
# @return [JSON]
def to_json
result = {
'otu_ids' => map_source_objects.keys
}
i = 1
j = 0
map_source_objects.each_key do |otu_id|
feature_collection = {
'type' => 'FeatureCollection',
'features' => []
}
colors = [
['black', 0x000000], ['blue', 0x000088], ['orange', 0xDD6600], ['green', 0x008800], ['red', 0x880000],
['purple', 0x880088], ['yellow', 0xAAAA55], ['brown', 0x664400], ['gray', 0x666666],
['white', 0xFFFFFF], ['shadow', 0x888888]]
opacities = {'asserted_distribution' => 0.66, 'collecting_event_georeference' => 0.44, 'collecting_event_geographic_area' => 0.22}
map_source_objects[otu_id].each do |source, data, type|
source_class = source.class.name
route_base = source.class.table_name
color_index = (j + 1) % 8 # only 8 colors, excluding black and white (mousover color)
color_name = colors[color_index][0]
color = sprintf('#%06X', colors[color_index][1])
json = data.to_simple_json_feature
json['properties'].merge!(
'id' => i,
'source' => source_class,
'source_type' => type.to_s,
'metadata' => {source_class => source.attributes},
'api' => {
'concise_details' => "/#{route_base}/#{data.id}/concise_details",
'expanded_details' => "/#{route_base}/#{data.id}/expanded_details"
},
'colorName' => color_name,
'fillColor' => color,
'fillOpacity' => opacities[type.to_s]
)
send("#{type}_properties", json, source, data)
feature_collection['features'].push(json)
i = i + 1
end
j = j + 1
result.merge!(otu_id => feature_collection)
end
result
end
# rubocop:enable Metrics/MethodLength
# @param [JSON] json
# @param [AssertedDistribution] asserted_distribution
# @param [Object] data
# @return [JSON]
def asserted_distribution_properties(json, asserted_distribution, data)
json['properties']['label'] = asserted_distribution.geographic_area.name
json['properties']['metadata']['GeographicArea'] = asserted_distribution.geographic_area.attributes
json['properties']['metadata']['Source'] = asserted_distribution.source.attributes if asserted_distribution.source
end
# @param [JSON] json
# @param [CollectingEvent] collecting_event
# @param [Object] data
# @return [JSON]
def collecting_event_geographic_area_properties(json, collecting_event, data)
json['properties']['label'] = collecting_event.geographic_area.name
json['properties']['metadata']['GeographicArea'] = collecting_event.geographic_area.attributes
end
# @param [JSON] json
# @param [Geoeference] georeference
# @param [CollectingEvent] data
# @return [JSON]
def collecting_event_georeference_properties(json, georeference, data)
json['properties']['label'] = "Collecting event #{data.id}."
end
# rubocop:enable Style/StringHashKeys
end