commit fe51bdb10e6730d28426973f05ba86e6b3800ce0 Author: s3lph Date: Mon Dec 16 22:43:37 2024 +0100 feat: first experimental version diff --git a/map.py b/map.py new file mode 100644 index 0000000..7267b88 --- /dev/null +++ b/map.py @@ -0,0 +1,102 @@ + +from math import * +import numpy as np +import sys +import json + + +EPS = sys.float_info.epsilon + + +class Triangle: + + def __init__(self, v1, v2, v3): + # Vertices + self.v1 = v1 + self.v2 = v2 + self.v3 = v3 + # Edges of the origin vertex and their L2 norms + self.e1 = v2 - v1 + self.e2 = v3 - v1 + self.e1n = np.linalg.norm(self.e1) + self.e2n = np.linalg.norm(self.e2) + # Normal vector of the triangle's plane + self.n = np.cross(self.e1 / self.e1n, self.e2 / self.e2n) + # The vertex coordinates in plane coordinates + self.v1p = self.plane_coords(self.v1) + self.v2p = self.plane_coords(self.v2) + self.v3p = self.plane_coords(self.v3) + + def intersect(self, ray): + # https://en.wikipedia.org/wiki/Möller–Trumbore_intersection_algorithm + ray_cross_e2 = np.cross(ray, self.e2) + dot = np.dot(self.e1, ray_cross_e2) + if abs(dot) < EPS: + return None + s = -self.v1 + u = np.dot(s, ray_cross_e2) / dot + if (u < 0 and abs(u) > EPS) or (u > 1 and abs(u - 1) > EPS): + return None + s_cross_e1 = np.cross(s, self.e1) + v = np.dot(ray, s_cross_e1) / dot + if (v < 0 and abs(v) > EPS) or (u + v > 1 and abs(u + v - 1) > EPS): + return None + t = np.dot(self.e2, s_cross_e1) / dot + if t <= EPS: + return None + return ray * t + + def plane_coords(self, v): + u = np.dot(self.e1, v - self.v1) / self.e1n + n = np.cross(self.n, self.e1 / self.e1n) + v = np.dot(n, v-self.v2) + return np.array([u, v]) + + +def convert_to_cartesian(lon, lat): + x = cos(lon*pi/180) + y = sin(lon*pi/180) + z = tan(lat*pi/180) + mag = sqrt(x**2 + y**2 + z**2) + return np.array([x/mag, y/mag, z/mag]) + + +def map_poly(tri, poly): + p0 = convert_to_cartesian(*poly[0]) + p0i = tri.intersect(p0) + mapped = [] + for i in range(1, len(poly)): + p1 = convert_to_cartesian(*poly[i]) + p1i = tri.intersect(p1) + if p0i is not None and p1i is not None: + mapped.append((tri.plane_coords(p0i), tri.plane_coords(p1i))) + p0 = p1 + p0i = p1i + return mapped + + +if __name__ == '__main__': + t = Triangle(np.array([5,0,0]), np.array([0,5,0]), np.array([0,0,5])) + countries = {} + with open('/tmp/countries.geojson', 'r') as f: + j = json.load(f) + for f in j['features']: + cc = f['properties']['ADMIN'] + if f['geometry']['type'] == 'MultiPolygon': + countries[cc] = [] + for poly in f['geometry']['coordinates'][0]: + countries[cc].extend(map_poly(t, poly)) + elif f['geometry']['type'] == 'Polygon': + countries[cc] = map_poly(tri, f['geometry']['coordinates'][0]) + minx = min(t.v1p[0], t.v2p[0], t.v3p[0]) + miny = min(t.v1p[1], t.v2p[1], t.v3p[1]) + maxx = max(t.v1p[0], t.v2p[0], t.v3p[0]) + maxy = max(t.v1p[1], t.v2p[1], t.v3p[1]) + w = maxx - minx + h = maxy - miny + print(f'') + print(f'') + for c, l in countries.items(): + for a, b in l: + print(f'') + print('')