|
import re |
|
import warnings |
|
from typing import Tuple |
|
|
|
Latitude = float |
|
Longitude = float |
|
Coordinate = Tuple[Latitude, Longitude] |
|
|
|
|
|
def valid_coordinate_jp(p: Coordinate): |
|
lat, lon = p |
|
lat_is_valid = 20 <= lat <= 46 |
|
lon_is_valid = 122 <= lon <= 154 |
|
return lat_is_valid & lon_is_valid |
|
|
|
|
|
def _coord2code(p: Coordinate, key="1km"): |
|
self = _coord2code |
|
if key == "80km": |
|
lat, lon = p |
|
a, c = divmod(lat * 60, 40) |
|
b, d = divmod(lon - 100, 1) |
|
return (a, b), (c, d) |
|
elif key == "10km": |
|
codes, (c, d) = self(p, key="80km") |
|
a, c = divmod(c, 5) |
|
b, d = divmod(d * 60, 7.5) |
|
codes += (a, b) |
|
return codes, (c, d) |
|
elif key == "1km": |
|
codes, (c, d) = self(p, key="10km") |
|
a, c = divmod(c * 60, 30) |
|
b, d = divmod(d * 60, 45) |
|
codes += (a, b) |
|
return codes, (c, d) |
|
elif key == "500m": |
|
codes, (c, d) = self(p, key="1km") |
|
a, c = divmod(c, 15) |
|
b, d = divmod(d, 22.5) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (c, d) |
|
elif key == "250m": |
|
codes, (c, d) = self(p, key="500m") |
|
a, c = divmod(c, 7.5) |
|
b, d = divmod(d, 11.25) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (c, d) |
|
elif key == "125m": |
|
codes, (c, d) = self(p, key="250m") |
|
a, c = divmod(c, 3.75) |
|
b, d = divmod(d, 5.625) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (c, d) |
|
elif key == "5km": |
|
codes, (c, d) = self(p, key="10km") |
|
a, c = divmod(c, 2.5) |
|
b, d = divmod(d, 3.75) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (c, d) |
|
elif key == "2km": |
|
codes, (c, d) = self(p, key="10km") |
|
a, c = divmod(c, 1) |
|
b, d = divmod(d, 1.5) |
|
codes += (a * 2, b * 2, 5) |
|
return codes, (c, d) |
|
else: |
|
raise ValueError("key: %s is not defined." % key) |
|
|
|
|
|
def coord2code(p: Coordinate, key="1km"): |
|
codes, _ = _coord2code(p=p, key=key) |
|
return "".join(map(str, map(int, codes))) |
|
|
|
|
|
def to_mesh(latitude: float, longitude: float): |
|
a, c = divmod(latitude * 60, 40) |
|
b, d = divmod(longitude - 100, 1) |
|
e, g = divmod(c, 5) |
|
f, h = divmod(d * 60, 7.5) |
|
i, _ = divmod(g * 60, 30) |
|
j, _ = divmod(h * 60, 45) |
|
return "".join(map(str, map(int, (a, b, e, f, i, j)))) |
|
|
|
|
|
class MeshCode: |
|
regex = r"~" |
|
parent = None |
|
scale = 1 |
|
|
|
def __init__(self, code: str): |
|
self.code = code |
|
assert self.match(self.code) |
|
self.latitude, self.longitude = self.code2coord(code) |
|
|
|
@property |
|
def width(self): |
|
return 1 / self.scale |
|
|
|
@property |
|
def height(self): |
|
return 2 / 3 / self.scale |
|
|
|
@property |
|
def center(self): |
|
return self.latitude + self.height / 2, self.longitude + self.width / 2 |
|
|
|
def coordinates(self): |
|
return [ |
|
[self.latitude, self.longitude], |
|
[self.latitude, self.longitude + self.width], |
|
[self.latitude + self.height, self.longitude + self.width], |
|
[self.latitude + self.height, self.longitude], |
|
[self.latitude, self.longitude], |
|
] |
|
|
|
@classmethod |
|
def match(cls, code: str): |
|
return re.match(cls.regex, code) is not None |
|
|
|
@classmethod |
|
def _coord2code(cls, codes:Tuple[float], lat:Latitude, lon:Longitude): |
|
return (), (lat, lon - 100) |
|
|
|
@classmethod |
|
def _coord2code_(cls, p:Coordinate): |
|
if cls.parent is None: |
|
codes, (lat, lon) = cls._coord2code((), *p) |
|
else: |
|
codes, (lat, lon) = cls.parent._coord2code_(p) |
|
codes, (lat, lon) = cls._coord2code(codes, lat, lon) |
|
return codes, (lat, lon) |
|
|
|
@classmethod |
|
def coord2code(cls, p:Coordinate): |
|
codes, _ = cls._coord2code_(p) |
|
code = "".join(map(str, map(int, codes))) |
|
return code |
|
|
|
@classmethod |
|
def from_coord(cls, p:Coordinate): |
|
code = cls.coord2code(p) |
|
return cls(code) |
|
|
|
@classmethod |
|
def _code2coord(cls, code: str): |
|
return (0, 100) |
|
|
|
@classmethod |
|
def code2coord(cls, code: str): |
|
if cls.parent is None: |
|
return cls._code2coord(code) |
|
plat, plon = cls.parent.code2coord(code) |
|
lat, lon = cls._code2coord(code) |
|
return (lat + plat, lon + plon) |
|
|
|
|
|
class MeshCode80km(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])$" |
|
parent = MeshCode |
|
scale = 1 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat * 60, 40) |
|
b, d = divmod(lon, 1) |
|
codes += (a, b) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code:str): |
|
lat, lon = map(float, (code[0:2], code[2:4])) |
|
return lat * 2 / 3, lon |
|
|
|
|
|
class MeshCode10km(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])[0-7]{2}$" |
|
parent = MeshCode80km |
|
scale = 8 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat, 5) |
|
b, d = divmod(lon * 60, 7.5) |
|
codes += (a, b) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code): |
|
lat, lon = map(float, (code[4:5], code[5:6])) |
|
return lat * 2 / 3 / 8, lon / 8 |
|
|
|
|
|
class MeshCode5km(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])[0-7]{2}[1-4]$" |
|
parent = MeshCode10km |
|
scale = 16 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat, 2.5) |
|
b, d = divmod(lon, 3.75) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code): |
|
lat, lon = divmod(int(code[6:7]) - 1, 2) |
|
return lat * 2 / 3 / 16, lon / 16 |
|
|
|
|
|
class MeshCode2km(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])[0-7]{2}[02468]{2}5$" |
|
parent = MeshCode10km |
|
scale = 40 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat, 1) |
|
b, d = divmod(lon, 1.5) |
|
codes += (a * 2, b * 2, 5) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code): |
|
lat, lon = map(float, (code[6:7], code[7:8])) |
|
return lat * 2 / 3 / 80, lon / 80 |
|
|
|
|
|
class MeshCode1km(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])[0-7]{2}\d{2}$" |
|
parent = MeshCode10km |
|
scale = 80 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat * 60, 30) |
|
b, d = divmod(lon * 60, 45) |
|
codes += (a, b) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code): |
|
lat, lon = map(float, (code[6:7], code[7:8])) |
|
return lat * 2 / 3 / 80, lon / 80 |
|
|
|
|
|
class MeshCode500m(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])[0-7]{2}\d{2}[1-4]$" |
|
parent = MeshCode1km |
|
scale = 160 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat, 15) |
|
b, d = divmod(lon, 22.5) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code): |
|
lat, lon = divmod(int(code[8:9]) - 1, 2) |
|
return lat * 2 / 3 / 160, lon / 160 |
|
|
|
|
|
class MeshCode250m(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])[0-7]{2}\d{2}[1-4]{2}$" |
|
parent = MeshCode500m |
|
scale = 320 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat, 7.5) |
|
b, d = divmod(lon, 11.25) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code): |
|
lat, lon = divmod(int(code[9:10]) - 1, 2) |
|
return lat * 2 / 3 / 320, lon / 320 |
|
|
|
|
|
class MeshCode125m(MeshCode): |
|
regex = r"^([3-6][0-9])(2[2-9]|[3-4][0-9]|5[0-5])[0-7]{2}\d{2}[1-4]{3}$" |
|
parent = MeshCode250m |
|
scale = 640 |
|
|
|
@classmethod |
|
def _coord2code(cls, codes, lat, lon): |
|
a, c = divmod(lat, 3.75) |
|
b, d = divmod(lon, 5.625) |
|
codes += ((a * 2) + (b + 1),) |
|
return codes, (round(c, 10), round(d, 10)) |
|
|
|
@classmethod |
|
def _code2coord(cls, code): |
|
lat, lon = divmod(int(code[10:11]) - 1, 2) |
|
return lat * 2 / 3 / 640, lon / 640 |
|
|
|
|
|
if __name__ == "__main__": |
|
import pandas as pd |
|
|
|
# fmt: off |
|
df = pd.DataFrame( |
|
[ |
|
["稚内", 45.41715379, 141.67690228, "6841", "684115", "68411504", "684115041", "6841150411", "68411504112", "6841151", "684115045"], |
|
["沖ノ鳥島", 20.425550297436146, 136.081078346884, "3036", "303650", "30365016", "303650161", "3036501612", "30365016122", "3036502", "303650065"], |
|
["南鳥島", 24.286699972409636, 153.98078108610335, "3653", "365337", "36533748", "365337481", "3653374814", "36533748144", "3653372", "365337485"], |
|
["与那国島", 24.450187920552747, 122.93420216940896, "3622", "362257", "36225744", "362257442", "3622574421", "36225744212", "3622571", "362257445"], |
|
["福岡", 33.58972334954021, 130.4207522946408, "5030", "503033", "50303303", "503033034", "5030330343", "50303303432", "5030331", "503033025"], |
|
["大阪", 34.702475212431786, 135.4959362881235, "5235", "523503", "52350349", "523503492", "5235034923", "52350349232", "5235032", "523503485"], |
|
["東京", 35.68121852142085, 139.76670013272258, "5339", "533946", "53394611", "533946113", "5339461132", "53394611323", "5339461", "533946005"], |
|
["青森", 40.828881020977185, 140.73419079481246, "6140", "614015", "61401598", "614015982", "6140159823", "61401598234", "6140154", "614015885"], |
|
["札幌", 43.06866073331351, 141.3507799065603, "6441", "644142", "64414288", "644142881", "6441428811", "64414288113", "6441424", "644142885"], |
|
["金沢", 36.57804475344767, 136.64795535242703, "5436", "543665", "54366591", "543665912", "5436659124", "54366591241", "5436653", "543665805"], |
|
["浮動小数Error", 20.425, 136.075, "3036", "303650", "30365016", "303650161", "3036501611", "30365016111", "3036502", "303650065"], |
|
], |
|
columns=["place", "lat", "lon", "80km", "10km", "1km", "500m", "250m", "125m", "5km", "2km"], |
|
) |
|
|
|
for _, row in df.iterrows(): |
|
for key, m in zip( |
|
["80km", "10km", "1km", "500m", "250m", "125m", "5km", "2km"], |
|
[MeshCode80km, MeshCode10km, MeshCode1km, MeshCode500m, MeshCode250m, MeshCode125m, MeshCode5km, MeshCode2km] |
|
): |
|
# fmt: on |
|
p0 = row[['lat', 'lon']] |
|
code = m.coord2code(p0) |
|
assert row[key] == code, '%s key="%s": expected="%s" got="%s"' % (row['place'], key, row[key], code) |
|
|
|
p1 = m.code2coord(code) |
|
code2 = m.coord2code(p1) |
|
p2 = m.code2coord(code2) |
|
assert code == code2 |
|
assert p1== p2 |