Class: CachedMap

Inherits:
ApplicationRecord show all
Includes:
Housekeeping::Projects, Housekeeping::Timestamps, Shared::IsData
Defined in:
app/models/cached_map.rb

Overview

A CachedMap is a OTU specific map derived from AssertedDistribution and Georeference data via aggregation of the intermediate CachedMapItem level.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Shared::IsData

#errors_excepting, #full_error_messages_excepting, #identical, #is_community?, #is_destroyable?, #is_editable?, #is_in_use?, #is_in_users_projects?, #metamorphosize, #similar

Methods included from Housekeeping::Projects

#annotates_community_object?, #is_community?, #set_project_id

Methods inherited from ApplicationRecord

transaction_with_retry

Instance Attribute Details

#force_rebuildObject

Returns Boolean, nil.

Returns:

  • Boolean, nil



13
14
15
# File 'app/models/cached_map.rb', line 13

def force_rebuild
  @force_rebuild
end

#geo_json_string=(value) ⇒ Object (writeonly)

Parameters:

  • GeoJSON

    in string form



10
11
12
# File 'app/models/cached_map.rb', line 10

def geo_json_string=(value)
  @geo_json_string = value
end

Class Method Details

.calculate_union(otu_scope, cached_map_type = 'CachedMapItem::WebLevel1') ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'app/models/cached_map.rb', line 112

def self.calculate_union(otu_scope, cached_map_type = 'CachedMapItem::WebLevel1')
  sql = union_sql(otu_scope, cached_map_type = 'CachedMapItem::WebLevel1')
  begin
    r = ActiveRecord::Base.connection.execute(sql)
  rescue ActiveRecord::StatementInvalid => e
    if e.message.include?('GEOSUnaryUnion')
      return nil
    else
      raise e
    end
  end
  r[0]['geojson']
end

.union_sql(otu_scope, cached_map_type = 'CachedMapItem::WebLevel1') ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'app/models/cached_map.rb', line 80

def self.union_sql(otu_scope, cached_map_type = 'CachedMapItem::WebLevel1')
  i = ::GeographicItem.select("#{GeographicItem::GEOMETRY_SQL.to_sql}")
    .joins('JOIN cached_map_items cmi on cmi.geographic_item_id = geographic_items.id')
    .joins('JOIN otu_scope AS otu_scope1 on otu_scope1.id = cmi.otu_id')
    .where('cmi.untranslated IS NULL OR cmi.untranslated <> true')
    .distinct

  s = "WITH otu_scope AS (#{otu_scope.to_sql}) " + i.to_sql

  # TODO: with untranslated handled we probable don't need Homogenize?
  # TODO: explore simplification optimization
  #   - 0.01 drops the number of points by > 5x

  sql = "SELECT
           ST_AsGeoJSON(
            ST_SimplifyPreserveTopology(
               ST_CollectionHomogenize (
                 ST_CollectionExtract(
                   ST_Union(geom_array)
                 )
               ),
               0.01
             )
          ) AS geojson
        FROM (
          SELECT ARRAY(
            #{s}
          ) AS geom_array
        ) AS subquery;"
  sql
end

Instance Method Details

#cached_map_itemsObject

All cached_map items used to compose this cached_map.



37
38
39
# File 'app/models/cached_map.rb', line 37

def cached_map_items
  CachedMapItem.where(type: cached_map_type, otu: otu_scope)
end

#cached_map_items_reference_totalObject



60
61
62
# File 'app/models/cached_map.rb', line 60

def cached_map_items_reference_total
  CachedMapItem.where(otu: otu_scope).sum(:reference_count)
end

#geo_json_to_sObject



72
73
74
75
76
77
78
# File 'app/models/cached_map.rb', line 72

def geo_json_to_s
  if respond_to?(:geo_json) # loaded as string in query
    geo_json
  else
    CachedMap.select('ST_AsGeoJSON(geometry) geo_json').find(id).geo_json
  end
end

#latest_cached_map_itemObject



56
57
58
# File 'app/models/cached_map.rb', line 56

def latest_cached_map_item
  cached_map_items.order(:updated_at).first
end

#otu_scopeObject



64
65
66
67
68
69
70
# File 'app/models/cached_map.rb', line 64

def otu_scope
  if otu.taxon_name
    Otu.descendant_of_taxon_name(otu.taxon_name_id)
  else
    Otu.coordinate_otus(otu.id)
  end
end

#rebuildObject

TODO: Current production strategy is to present “out of date” when visualizing a CacheMap, then do complete rebuilds.

This strategy can change when if/when we manage to get a comprehensive number of hooks into models such that state can be maintained.

Since all hooks are synchronized through a change in a CachedMapItem we can trigger syncronizations that Update CachedMaps with callback hooks here, in theory.



28
29
30
31
32
# File 'app/models/cached_map.rb', line 28

def rebuild
  if !synced?
    self.geo_json_string = CachedMap.calculate_union(otu, cached_map_type: )
  end
end

#synced?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'app/models/cached_map.rb', line 52

def synced?
  cached_map_items_reference_total == reference_count && latest_cached_map_item.created_at <= created_at
end