Skip to content

Instantly share code, notes, and snippets.

@lennart
Created March 18, 2020 14:26
Show Gist options
  • Save lennart/b092a5fef2b40fd1af362e5a4e0c6ea9 to your computer and use it in GitHub Desktop.
Save lennart/b092a5fef2b40fd1af362e5a4e0c6ea9 to your computer and use it in GitHub Desktop.
simplify location lookup (pseudo-implementation)
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