dawarich/app/services/maps/hexagon_request_handler.rb
2025-09-17 01:55:42 +02:00

97 lines
2.6 KiB
Ruby

# frozen_string_literal: true
module Maps
class HexagonRequestHandler
def self.call(params:, current_api_user: nil)
new(params: params, current_api_user: current_api_user).call
end
def initialize(params:, current_api_user: nil)
@params = params
@current_api_user = current_api_user
end
def call
context = resolve_context
# Try to use pre-calculated hexagon centers first
if context[:stat]
cached_result = Maps::HexagonCenterManager.call(
stat: context[:stat],
target_user: context[:target_user]
)
return cached_result[:data] if cached_result&.dig(:success)
end
# Fall back to on-the-fly calculation
Rails.logger.debug 'No pre-calculated data available, calculating hexagons on-the-fly'
generate_hexagons_on_the_fly(context)
end
private
attr_reader :params, :current_api_user
def resolve_context
Maps::HexagonContextResolver.call(
params: params,
current_api_user: current_api_user
)
end
def generate_hexagons_on_the_fly(context)
# Parse dates for H3 calculator which expects Time objects
start_date = parse_date_for_h3(context[:start_date])
end_date = parse_date_for_h3(context[:end_date])
result = Maps::H3HexagonCalculator.new(
context[:target_user]&.id,
start_date,
end_date,
h3_resolution
).call
return result[:data] if result[:success]
# If H3 calculation fails, log error and return empty feature collection
Rails.logger.error "H3 calculation failed: #{result[:error]}"
empty_feature_collection
end
def empty_feature_collection
{
type: 'FeatureCollection',
features: [],
metadata: {
hexagon_count: 0,
total_points: 0,
source: 'h3'
}
}
end
def h3_resolution
# Allow custom resolution via parameter, default to 8
resolution = params[:h3_resolution]&.to_i || 8
# Clamp to valid H3 resolution range (0-15)
resolution.clamp(0, 15)
end
def parse_date_for_h3(date_param)
# If already a Time object (from public sharing context), return as-is
return date_param if date_param.is_a?(Time)
# If it's a string ISO date, parse it directly to Time
return Time.parse(date_param) if date_param.is_a?(String)
# If it's an integer timestamp, convert to Time
return Time.at(date_param) if date_param.is_a?(Integer)
# For other cases, try coercing and converting
timestamp = Maps::DateParameterCoercer.call(date_param)
Time.at(timestamp)
end
end
end