geojson.ts (2276B)
1 import type { GeolocationPoint, GeometryPoint, GeometryPolygon } from "./types.js"; 2 3 const WGS84_RADIUS_M = 6378137; 4 const DEFAULT_CIRCLE_STEPS = 64; 5 const COORD_SCALE = 1_000_000; 6 7 const rad_from_deg = (deg: number): number => (deg * Math.PI) / 180; 8 const deg_from_rad = (rad: number): number => (rad * 180) / Math.PI; 9 const wrap_lng = (lng: number): number => ((lng + 540) % 360) - 180; 10 const round_coord = (value: number): number => Math.round(value * COORD_SCALE) / COORD_SCALE; 11 12 export const geojson_point_from_geopoint = (geol_p: GeolocationPoint): GeometryPoint => ({ 13 type: "Point", 14 coordinates: [geol_p.lng, geol_p.lat], 15 }); 16 17 export const geojson_polygon_circle_wgs84 = (opts: { 18 geol_p: GeolocationPoint; 19 radius_m?: number; 20 steps?: number; 21 }): GeometryPolygon => { 22 const { geol_p } = opts; 23 const radius_m_raw = opts.radius_m; 24 const radius_m = typeof radius_m_raw === "number" && Number.isFinite(radius_m_raw) && radius_m_raw > 0 25 ? radius_m_raw 26 : 100; 27 const steps_raw = opts.steps; 28 const steps_val = typeof steps_raw === "number" && Number.isFinite(steps_raw) && steps_raw > 0 29 ? Math.round(steps_raw) 30 : DEFAULT_CIRCLE_STEPS; 31 const step_count = Math.max(4, steps_val); 32 const lat_rad = rad_from_deg(geol_p.lat); 33 const lng_rad = rad_from_deg(geol_p.lng); 34 const angular_distance = radius_m / WGS84_RADIUS_M; 35 const sin_lat = Math.sin(lat_rad); 36 const cos_lat = Math.cos(lat_rad); 37 const sin_ad = Math.sin(angular_distance); 38 const cos_ad = Math.cos(angular_distance); 39 const coords: Array<[number, number]> = []; 40 for (let i = 0; i <= step_count; i++) { 41 const bearing = (i * 2 * Math.PI) / step_count; 42 const lat_rad_next = Math.asin( 43 sin_lat * cos_ad + cos_lat * sin_ad * Math.cos(bearing), 44 ); 45 const lng_rad_next = lng_rad + Math.atan2( 46 Math.sin(bearing) * sin_ad * cos_lat, 47 cos_ad - sin_lat * Math.sin(lat_rad_next), 48 ); 49 const lat_next = round_coord(deg_from_rad(lat_rad_next)); 50 const lng_next = round_coord(wrap_lng(deg_from_rad(lng_rad_next))); 51 coords.push([lng_next, lat_next]); 52 } 53 return { 54 type: "Polygon", 55 coordinates: [coords], 56 }; 57 };