SpeciesFileGroup/taxonworks

View on GitHub
app/models/collecting_event/georeference.rb

Summary

Maintainability
A
1 hr
Test Coverage
module CollectingEvent::Georeference
  extend ActiveSupport::Concern

  included do
    has_many :georeferences, dependent: :destroy, class_name: '::Georeference', inverse_of: :collecting_event

    has_one :verbatim_data_georeference, class_name: '::Georeference::VerbatimData'

    has_many :error_geographic_items, through: :georeferences, source: :error_geographic_item
    has_many :geographic_items, through: :georeferences # See also all_geographic_items, the union
    has_many :geo_locate_georeferences, class_name: '::Georeference::GeoLocate', dependent: :destroy
    has_many :gpx_georeferences, class_name: '::Georeference::GPX', dependent: :destroy

    accepts_nested_attributes_for :verbatim_data_georeference
    accepts_nested_attributes_for :geo_locate_georeferences
    accepts_nested_attributes_for :gpx_georeferences

    def preferred_georeference
      georeferences.order(:position).first
    end

    def preferred_georeference_geographic_item_id
      georeferences.order(:position).limit(1).pluck(:geographic_item_id).first
    end
  end

  # @param [Float] delta_z, will be used to fill in the z coordinate of the point
  # @return [RGeo::Geographic::ProjectedPointImpl, nil]
  #   for the *verbatim* latitude/longitude only
  def verbatim_map_center(delta_z = 0.0)
    retval = nil
    unless verbatim_latitude.blank? or verbatim_longitude.blank?
      lat = Utilities::Geo.degrees_minutes_seconds_to_decimal_degrees(verbatim_latitude.to_s)
      long = Utilities::Geo.degrees_minutes_seconds_to_decimal_degrees(verbatim_longitude.to_s)
      elev = Utilities::Geo.distance_in_meters(verbatim_elevation.to_s)
      delta_z = elev unless elev == 0.0
      retval  = Gis::FACTORY.point(long, lat, delta_z)
    end
    retval
  end

  def latitude
    verbatim_map_center.try(:y)
  end

  def longitude
    verbatim_map_center.try(:x)
  end

  # TODO: Helper method
  # @return [CollectingEvent]
  #   return the next collecting event without a georeference in this collecting events project sort order
  #   1.  verbatim_locality
  #   2.  geography_id
  #   3.  start_date_year
  #   4.  updated_on
  #   5.  id
  def next_without_georeference
    CollectingEvent.not_including(self).
      includes(:georeferences).
      where(project_id: project_id, georeferences: {collecting_event_id: nil}).
      order(:verbatim_locality, :geographic_area_id, :start_date_year, :updated_at, :id).
      first
  end

  # TODO: refactor to nil on no georeference
  def georeference_latitude
    retval = 0.0
    if georeferences.count > 0
      retval = Georeference.where(collecting_event_id: self.id).order(:position).limit(1)[0].latitude.to_f
    end
    retval.round(6)
  end

  # TODO: refactor to nil on no georeference
  def georeference_longitude
    retval = 0.0
    if georeferences.count > 0
      retval = Georeference.where(collecting_event_id: self.id).order(:position).limit(1)[0].longitude.to_f
    end
    retval.round(6)
  end

  # @return [String]
  #   coordinates for centering a Google map
  def verbatim_center_coordinates
    if self.verbatim_latitude.blank? || self.verbatim_longitude.blank?
      'POINT (0.0 0.0 0.0)'
    else
      self.verbatim_map_center.to_s
    end
  end

  # @return [Symbol, nil]
  #   the name of the method that will return an Rgeo object that represent
  #   the "preferred" centroid for this collecing event
  def map_center_method
    return :preferred_georeference if preferred_georeference # => { georeferenceProtocol => ?  }
    return :verbatim_map_center if verbatim_map_center # => { }
    return :geographic_area if geographic_area.try(:has_shape?)
    nil
  end

  # @return [Rgeo::Geographic::ProjectedPointImpl, nil]
  def map_center
    case map_center_method
    when :preferred_georeference
      preferred_georeference.geographic_item.centroid
    when :verbatim_map_center
      verbatim_map_center
    when :geographic_area
      geographic_area.default_geographic_item.centroid
    else
      nil
    end
  end

  # @return [Integer]
  # @TODO figure out how to convert verbatim_geolocation_uncertainty in different units (ft, m, km, mi) into meters
  # @TODO: See Utilities::Geo.distance_in_meters(String)
  def get_error_radius
    return nil if verbatim_geolocation_uncertainty.blank?
    return verbatim_geolocation_uncertainty.to_i if is.number?(verbatim_geolocation_uncertainty)
    nil
  end

  # CollectingEvent.select {|d| !(d.verbatim_latitude.nil? || d.verbatim_longitude.nil?)}
  # .select {|ce| ce.georeferences.empty?}
  # @param [Boolean] reference_self
  # @param [Boolean] no_cached
  # @return [Georeference::VerbatimData, false]
  #   generates (creates) a Georeference::VerbatimReference from verbatim_latitude and verbatim_longitude values
  def generate_verbatim_data_georeference(reference_self = false, no_cached: false)
    return false if (verbatim_latitude.nil? || verbatim_longitude.nil?)
    begin
      CollectingEvent.transaction do
        vg_attributes = {collecting_event_id: id.to_s, no_cached: no_cached}
        vg_attributes.merge!(by: self.creator.id, project_id: self.project_id) if reference_self
        a = Georeference::VerbatimData.new(vg_attributes)
        if a.valid?
          a.save
        end
        return a
      end
    rescue
      raise
    end
    false
  end

  # @return [Symbol, nil]
  #   Prioritizes and identifies the source of the latitude/longitude values that
  #   will be calculated for DWCA and primary display
  def dwc_georeference_source
    if !preferred_georeference.nil?
      :georeference
    elsif verbatim_latitude && verbatim_longitude
      :verbatim
    elsif geographic_area && geographic_area.has_shape?
      :geographic_area
    else
      nil
    end
  end

end