Created
March 18, 2020 14:26
-
-
Save lennart/b092a5fef2b40fd1af362e5a4e0c6ea9 to your computer and use it in GitHub Desktop.
simplify location lookup (pseudo-implementation)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'json' | |
GRID_STEP_SIZE = 100 # meters | |
# https://stackoverflow.com/questions/12966638/how-to-calculate-the-distance-between-two-gps-coordinates-without-using-google-m | |
def distance(loc1, loc2) | |
rad_per_deg = Math::PI/180 # PI / 180 | |
rkm = 6371 # Earth radius in kilometers | |
rm = rkm * 1000 # Radius in meters | |
dlat_rad = (loc2[0]-loc1[0]) * rad_per_deg # Delta, converted to rad | |
dlon_rad = (loc2[1]-loc1[1]) * rad_per_deg | |
lat1_rad, lon1_rad = loc1.map {|i| i * rad_per_deg } | |
lat2_rad, lon2_rad = loc2.map {|i| i * rad_per_deg } | |
a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)**2 | |
c = 2 * Math::atan2(Math::sqrt(a), Math::sqrt(1-a)) | |
rm * c # Delta in meters | |
end | |
# assumes northern hemisphere (bottom-left -> right is wider) | |
# width/height in meters | |
def dimensions(degrees) | |
width_m = distance( | |
[degrees[:south_lat], degrees[:west_long]], | |
[degrees[:south_lat], degrees[:east_long]] | |
) | |
height_m = distance( | |
[degrees[:north_lat], degrees[:west_long]], | |
[degrees[:south_lat], degrees[:west_long]] | |
) | |
{ | |
width: width_m, | |
height: height_m, | |
num_cols: (width_m / GRID_STEP_SIZE).floor, | |
num_rows: (height_m / GRID_STEP_SIZE).floor, | |
origin: [degrees[:south_lat], degrees[:west_long]], | |
max_pos: [degrees[:north_lat], degrees[:east_long]] | |
} | |
end | |
# calculate a single number that identifies a certain | |
# GRID_STEP_SIZExGRID_STEP_SIZE quad within the given metric's bounds | |
def quad_for_gps(metric, gps) | |
max_x_rad = metric[:max_pos][0] - metric[:origin][0] | |
max_y_rad = metric[:max_pos][1] - metric[:origin][1] | |
dx_rad = gps[0] - metric[:origin][0] | |
dy_rad = gps[1] - metric[:origin][1] | |
x = dx_rad / max_x_rad | |
y = dy_rad / max_y_rad | |
col = (x * metric[:num_cols]).floor | |
row = (y * metric[:num_rows]).floor | |
{ | |
col: col, | |
row: row, | |
quad: (row * metric[:num_cols]) + col | |
} | |
end | |
def check_bounds(num_rows, num_cols, pos) | |
row, col = pos | |
row_in_bounds = row >= 0 && row < num_rows | |
col_in_bounds = col >= 0 && col < num_cols | |
row_in_bounds && col_in_bounds | |
end | |
# returns a list of 8 quads surrounding the given one | |
def quads_around_quad(metric, quad) | |
row, col = quad.divmod metric[:num_cols] | |
row_before = [ | |
[row - 1, col - 1], [row - 1, col], [row - 1, col + 1] | |
] | |
this_row = [ | |
[row, col - 1], [row, col + 1] | |
] | |
row_after = [ | |
[row + 1, col - 1], [row + 1, col], [row + 1, col + 1] | |
] | |
around = [*row_before, *this_row, *row_after] | |
around.select! { |pos| check_bounds(metric[:num_rows], metric[:num_cols], pos) } | |
around.map { |pos| (pos[0] * metric[:num_cols]) + pos[1] } | |
end | |
# just to test quad calculation | |
def gps_for_quad(metric, quad) | |
row, col = quad.divmod metric[:num_cols] | |
max_x_rad = metric[:max_pos][0] - metric[:origin][0] | |
max_y_rad = metric[:max_pos][1] - metric[:origin][1] | |
dx_rad = (col / metric[:num_cols].to_f) * max_x_rad | |
dy_rad = (row / metric[:num_rows].to_f) * max_y_rad | |
lat = metric[:origin][0] + dx_rad | |
long = metric[:origin][1] + dy_rad | |
{ | |
row: row, | |
col: col, | |
gps: [lat, long] | |
} | |
end | |
germany = { | |
west_long: 5.8641555, | |
east_long: 15.0395564, | |
north_lat: 55.0583483, | |
south_lat: 47.2701121 | |
} | |
metric_germany = dimensions(germany) | |
puts metric_germany.to_json | |
berlin = [52.520007, 13.404954] | |
berlin_quad = quad_for_gps(metric_germany, berlin) | |
puts berlin_quad.to_json | |
puts gps_for_quad(metric_germany, berlin_quad[:quad]).to_json | |
puts quads_around_quad(metric_germany, berlin_quad[:quad]).to_json |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment