mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
104 lines
3.2 KiB
Ruby
104 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Maps
|
|
class HexagonPolygonGenerator
|
|
DEFAULT_SIZE_METERS = 1000
|
|
|
|
def self.call(center_lng:, center_lat:, size_meters: DEFAULT_SIZE_METERS, use_h3: false, h3_resolution: 5)
|
|
new(
|
|
center_lng: center_lng,
|
|
center_lat: center_lat,
|
|
size_meters: size_meters,
|
|
use_h3: use_h3,
|
|
h3_resolution: h3_resolution
|
|
).call
|
|
end
|
|
|
|
def initialize(center_lng:, center_lat:, size_meters: DEFAULT_SIZE_METERS, use_h3: false, h3_resolution: 5)
|
|
@center_lng = center_lng
|
|
@center_lat = center_lat
|
|
@size_meters = size_meters
|
|
@use_h3 = use_h3
|
|
@h3_resolution = h3_resolution
|
|
end
|
|
|
|
def call
|
|
if use_h3
|
|
generate_h3_hexagon_polygon
|
|
else
|
|
generate_hexagon_polygon
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :center_lng, :center_lat, :size_meters, :use_h3, :h3_resolution
|
|
|
|
def generate_h3_hexagon_polygon
|
|
# Convert coordinates to H3 format [lat, lng]
|
|
coordinates = [center_lat, center_lng]
|
|
|
|
# Get H3 index for these coordinates at specified resolution
|
|
h3_index = H3.from_geo_coordinates(coordinates, h3_resolution)
|
|
|
|
# Get the boundary coordinates for this H3 hexagon
|
|
boundary_coordinates = H3.to_boundary(h3_index)
|
|
|
|
# Convert to GeoJSON polygon format (lng, lat)
|
|
polygon_coordinates = boundary_coordinates.map { |lat, lng| [lng, lat] }
|
|
|
|
# Close the polygon by adding the first point at the end
|
|
polygon_coordinates << polygon_coordinates.first
|
|
|
|
{
|
|
'type' => 'Polygon',
|
|
'coordinates' => [polygon_coordinates]
|
|
}
|
|
end
|
|
|
|
def generate_hexagon_polygon
|
|
# Generate hexagon vertices around center point
|
|
# For a regular hexagon:
|
|
# - Circumradius (center to vertex) = size_meters / 2
|
|
# - This creates hexagons that are approximately size_meters wide
|
|
|
|
radius_meters = size_meters / 2.0
|
|
|
|
# Convert meter radius to degrees
|
|
# 1 degree latitude ≈ 111,111 meters
|
|
# 1 degree longitude ≈ 111,111 * cos(latitude) meters at given latitude
|
|
lat_degree_in_meters = 111_111.0
|
|
lng_degree_in_meters = lat_degree_in_meters * Math.cos(center_lat * Math::PI / 180)
|
|
|
|
radius_lat_degrees = radius_meters / lat_degree_in_meters
|
|
radius_lng_degrees = radius_meters / lng_degree_in_meters
|
|
|
|
vertices = build_vertices(radius_lat_degrees, radius_lng_degrees)
|
|
|
|
{
|
|
'type' => 'Polygon',
|
|
'coordinates' => [vertices]
|
|
}
|
|
end
|
|
|
|
def build_vertices(radius_lat_degrees, radius_lng_degrees)
|
|
vertices = []
|
|
6.times do |i|
|
|
# Calculate angle for each vertex (60 degrees apart, starting from 0)
|
|
# Start at 30 degrees to orient hexagon with flat top
|
|
angle = ((i * 60) + 30) * Math::PI / 180
|
|
|
|
# Calculate vertex position using proper geographic coordinate system
|
|
# longitude (x-axis) uses cosine, latitude (y-axis) uses sine
|
|
lng_offset = radius_lng_degrees * Math.cos(angle)
|
|
lat_offset = radius_lat_degrees * Math.sin(angle)
|
|
|
|
vertices << [center_lng + lng_offset, center_lat + lat_offset]
|
|
end
|
|
|
|
# Close the polygon by adding the first vertex at the end
|
|
vertices << vertices.first
|
|
vertices
|
|
end
|
|
end
|
|
end
|