""" Grid extension for qrm --- Copyright (C) 2019-2020 Abigail Gold, 0x5c This file is part of qrm2 and is released under the terms of the GNU General Public License, version 2. """ import math import discord.ext.commands as commands import common as cmn class GridCog(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot @commands.command(name="grid", category=cmn.cat.maps) async def _grid_sq_lookup(self, ctx: commands.Context, lat: str, lon: str): ("""Calculates the grid square for latitude and longitude coordinates, """ """with negative being latitude South and longitude West.""") grid = "**" latf = float(lat) + 90 lonf = float(lon) + 180 if 0 <= latf <= 180 and 0 <= lonf <= 360: grid += chr(ord("A") + int(lonf / 20)) grid += chr(ord("A") + int(latf / 10)) grid += chr(ord("0") + int((lonf % 20)/2)) grid += chr(ord("0") + int((latf % 10)/1)) grid += chr(ord("a") + int((lonf - (int(lonf/2)*2)) / (5/60))) grid += chr(ord("a") + int((latf - (int(latf/1)*1)) / (2.5/60))) grid += "**" embed = cmn.embed_factory(ctx) embed.title = f"Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}" embed.description = grid embed.colour = cmn.colours.good else: embed = cmn.embed_factory(ctx) embed.title = f"Error generating grid square for {lat}, {lon}." embed.description = """Coordinates out of range. The valid ranges are: - Latitude: `-90` to `+90` - Longitude: `-180` to `+180`""" embed.colour = cmn.colours.bad await ctx.send(embed=embed) @commands.command(name="ungrid", aliases=["loc"], category=cmn.cat.maps) async def _location_lookup(self, ctx: commands.Context, grid: str, grid2: str = None): """Calculates the latitude and longitude for the center of a grid square. If two grid squares are given, the distance and azimuth between them is calculated.""" if grid2 is None or grid2 == "": try: grid = grid.upper() loc = get_coords(grid) embed = cmn.embed_factory(ctx) embed.title = f"Latitude and Longitude for {grid}" embed.colour = cmn.colours.good if len(grid) >= 6: embed.description = f"**{loc[0]:.5f}, {loc[1]:.5f}**" embed.url = f"https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}" else: embed.description = f"**{loc[0]:.1f}, {loc[1]:.1f}**" embed.url = f"https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}" except Exception as e: embed = cmn.embed_factory(ctx) embed.title = f"Error generating latitude and longitude for grid {grid}." embed.description = str(e) embed.colour = cmn.colours.bad else: radius = 6371 try: grid = grid.upper() grid2 = grid2.upper() loc = get_coords(grid) loc2 = get_coords(grid2) # Haversine formula d_lat = math.radians(loc2[0] - loc[0]) d_lon = math.radians(loc2[1] - loc[1]) a = (math.sin(d_lat/2) ** 2 + math.cos(math.radians(loc[0])) * math.cos(math.radians(loc2[0])) * math.sin(d_lon/2) ** 2) c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) d = radius * c d_mi = 0.6213712 * d # Bearing y_dist = math.sin(math.radians(loc2[1]-loc[1])) * math.cos(math.radians(loc2[0])) x_dist = (math.cos(math.radians(loc[0])) * math.sin(math.radians(loc2[0])) - math.sin(math.radians(loc[0])) * math.cos(math.radians(loc2[0])) * math.cos(math.radians(loc2[1] - loc[1]))) bearing = (math.degrees(math.atan2(y_dist, x_dist)) + 360) % 360 embed = cmn.embed_factory(ctx) embed.title = f"Great Circle Distance and Bearing from {grid} to {grid2}" embed.description = f"**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°" embed.colour = cmn.colours.good except Exception as e: embed = cmn.embed_factory(ctx) embed.title = f"Error generating great circle distance and bearing from {grid} and {grid2}." embed.description = str(e) embed.colour = cmn.colours.bad await ctx.send(embed=embed) def get_coords(grid: str): if len(grid) < 3: raise ValueError("The grid locator must be at least 4 characters long.") if not grid[0:2].isalpha() or not grid[2:4].isdigit(): if len(grid) <= 4: raise ValueError("The grid locator must be of the form AA##.") if len(grid) >= 6 and not grid[5:7].isalpha(): raise ValueError("The grid locator must be of the form AA##AA.") lon = ((ord(grid[0]) - ord("A")) * 20) - 180 lat = ((ord(grid[1]) - ord("A")) * 10) - 90 lon += ((ord(grid[2]) - ord("0")) * 2) lat += ((ord(grid[3]) - ord("0")) * 1) if len(grid) >= 6: # have subsquares lon += ((ord(grid[4])) - ord("A")) * (5/60) lat += ((ord(grid[5])) - ord("A")) * (2.5/60) # move to center of subsquare lon += (2.5/60) lat += (1.25/60) return (lat, lon) # move to center of square lon += 1 lat += 0.5 return (lat, lon) def setup(bot: commands.Bot): bot.add_cog(GridCog(bot))