From be17f91de513c5223447b35548a6fde89d9ef26e Mon Sep 17 00:00:00 2001 From: Kirill Erofeev Date: Wed, 28 Aug 2024 02:01:11 +0700 Subject: [PATCH] Generate geoJSON for QTH grid locator --- src/maidenhead/__init__.py | 2 +- src/maidenhead/__main__.py | 19 +++++-- src/maidenhead/to_location.py | 93 +++++++++++++++++++++++++---------- 3 files changed, 84 insertions(+), 30 deletions(-) diff --git a/src/maidenhead/__init__.py b/src/maidenhead/__init__.py index 8099694..767965d 100644 --- a/src/maidenhead/__init__.py +++ b/src/maidenhead/__init__.py @@ -7,7 +7,7 @@ Beyond 8 characters is not defined for Maidenhead. """ -from .to_location import to_location +from .to_location import to_location, to_location_rect, to_geoJSONObject from .to_maiden import to_maiden diff --git a/src/maidenhead/__main__.py b/src/maidenhead/__main__.py index b69f3fd..29f7487 100644 --- a/src/maidenhead/__main__.py +++ b/src/maidenhead/__main__.py @@ -3,6 +3,7 @@ from copy import copy import maidenhead +import json def main( @@ -10,12 +11,18 @@ def main( precision: int = 6, url: bool = False, center: bool = False, + geojson: bool = False, + nosquare: bool = False ) -> str | tuple[float, float]: if isinstance(loc, str): # maidenhead maiden = copy(loc) - loc = maidenhead.to_location(loc, center) - print(f"{loc[0]:.7f} {loc[1]:.7f}") + if geojson: + obj = maidenhead.to_geoJSONObject(loc, center=center, square=not nosquare) + print(json.dumps(obj, indent=None)) + else: + loc = maidenhead.to_location(loc, center) + print(f"{loc[0]:.7f} {loc[1]:.7f}") elif len(loc) == 2: # lat lon if isinstance(loc[0], str): loc = (float(loc[0]), float(loc[1])) @@ -39,6 +46,7 @@ def main( ) p.add_argument("-p", "--precision", help="maidenhead precision", type=int, default=3) p.add_argument("--url", help="also output Google Maps URL", action="store_true") +p.add_argument("--geojson", help="Output the geoJSON that describes given QTH lcoator", action="store_true") p.add_argument( "-c", "--center", @@ -46,6 +54,11 @@ def main( "(default output: lat lon of it's south-west corner)", action="store_true", ) +p.add_argument( + "--nosquare", + help="Do not create square feature for geoJSON", + action="store_true", +) args = p.parse_args() if len(args.loc) == 1: @@ -53,4 +66,4 @@ def main( else: loc = args.loc -main(loc, args.precision, args.url, args.center) +main(loc, args.precision, args.url, args.center, args.geojson, args.nosquare) diff --git a/src/maidenhead/to_location.py b/src/maidenhead/to_location.py index 9ab64aa..6a495e7 100644 --- a/src/maidenhead/to_location.py +++ b/src/maidenhead/to_location.py @@ -2,27 +2,7 @@ import functools -def to_location(maiden: str, center: bool = False) -> tuple[float, float]: - """ - convert Maidenhead grid to latitude, longitude - - Parameters - ---------- - - maiden : str - Maidenhead grid locator of length 2 to 8 - - center : bool - If true, return the center of provided maidenhead grid square, instead of default south-west corner - Default value = False needed to maidenhead full backward compatibility of this module. - - Returns - ------- - - latLon : tuple of float - Geographic latitude, longitude - """ - +def to_location_rect(maiden: str) -> tuple[tuple[float, float], tuple[float, float], tuple[float, float]]: maiden = maiden.strip().upper() N = len(maiden) if N < 2 or N % 2: @@ -46,10 +26,71 @@ def convert(maiden: str) -> tuple[int, int]: lat_nom, lat_den = convert(maiden[1::2]) lon_nom, lon_den = convert(maiden[::2]) - center_offset_lat = 5 if center else 0 - center_offset_lon = 10 if center else 0 + center_offset_lat = 5 + center_offset_lon = 10 - lat = (10 * (lat_nom - 9 * lat_den) + center_offset_lat) / lat_den - lon = (20 * (lon_nom - 9 * lon_den) + center_offset_lon) / lon_den + lat_1 = (10 * (lat_nom - 9 * lat_den)) / lat_den + lon_1 = (20 * (lon_nom - 9 * lon_den)) / lon_den + lat_c = (10 * (lat_nom - 9 * lat_den) + center_offset_lat) / lat_den + lon_c = (20 * (lon_nom - 9 * lon_den) + center_offset_lon) / lon_den + lat_2 = (10 * (lat_nom - 9 * lat_den) + 2*center_offset_lat) / lat_den + lon_2 = (20 * (lon_nom - 9 * lon_den) + 2*center_offset_lon) / lon_den + return ((lat_1, lon_1), (lat_2, lon_2), (lat_c, lon_c)) - return (lat, lon) + +def to_location(maiden: str, center: bool = False) -> tuple[float, float]: + """ + convert Maidenhead grid to latitude, longitude + + Parameters + ---------- + + maiden : str + Maidenhead grid locator of length 2 to 8 + + center : bool + If true, return the center of provided maidenhead grid square, instead of default south-west corner + Default value = False needed to maidenhead full backward compatibility of this module. + + Returns + ------- + + latLon : tuple of float + Geographic latitude, longitude + """ + bottomleft_point, (_, _), center_point = to_location_rect(maiden) + + return center_point if center else bottomleft_point + + +def to_geoJSONObject(maiden: str, center: bool = True, square: bool = True) -> dict: + loc1, loc2, locc = to_location_rect(maiden) + center_point = { + "type": "Feature", + "properties": {"QTHLocator_Centerpoint": maiden}, + "geometry": { + "type": "Point", + "coordinates": [locc[1], locc[0]] + } + } + + rect = { + "type": "Feature", + "properties": {"QTHLocator": maiden}, + "geometry": { + "type": "Polygon", + "coordinates": [[(loc1[1], loc1[0]), (loc1[1], loc2[0]), (loc2[1], loc2[0]), (loc2[1], loc1[0]), (loc1[1], loc1[0])]] + } + } + + features: list = [] + geo = { + "type": "FeatureCollection", + "features": features + } + if center: + features.append(center_point) + if square: + features.append(rect) + # geo["features"] = features + return geo