My agentic slop goes here. Not intended for anyone else!
at jsont 4.4 kB view raw
1(** Coordinate parsing module *) 2 3let parse_dms s = 4 let s = String.trim s in 5 6 (* Check for hemisphere markers *) 7 let has_s = String.contains s 'S' || String.contains s 's' in 8 let has_w = String.contains s 'W' || String.contains s 'w' in 9 10 let sign = 11 if has_s || has_w then -1.0 12 else 1.0 in 13 14 (* Remove hemisphere markers and clean up *) 15 let cleaned = 16 s 17 |> Str.global_replace (Str.regexp "[NnSsEeWw]") "" 18 |> String.trim in 19 20 (* Try to parse different formats *) 21 try 22 (* First try as simple decimal degrees *) 23 if not (String.contains cleaned (Char.chr 176) || (* degree symbol *) 24 String.contains cleaned '\'' || 25 String.contains cleaned '"' || 26 String.contains cleaned ' ' || 27 String.contains cleaned ':') then 28 sign *. float_of_string cleaned 29 else 30 (* Parse DMS format *) 31 let deg_char = Char.chr 176 in (* degree symbol *) 32 let parts = 33 if String.contains cleaned deg_char then 34 (* Format: DD degree MM min SS sec or DD degree MM.MMM min *) 35 let deg_parts = String.split_on_char deg_char cleaned in 36 match deg_parts with 37 | [deg_str; rest] -> 38 let deg = float_of_string (String.trim deg_str) in 39 if String.contains rest '\'' then 40 let min_parts = String.split_on_char '\'' rest in 41 match min_parts with 42 | [min_str; sec_str] -> 43 let min = float_of_string (String.trim min_str) in 44 let sec_str = String.trim sec_str in 45 let sec_str = 46 if String.contains sec_str '"' then 47 String.sub sec_str 0 (String.index sec_str '"') 48 else sec_str in 49 let sec = 50 if sec_str = "" then 0.0 51 else float_of_string sec_str in 52 [deg; min; sec] 53 | [min_str] -> 54 let min = float_of_string (String.trim min_str) in 55 [deg; min; 0.0] 56 | _ -> raise (Error.Coordinate_error 57 (Error.Parse_error "Invalid DMS format")) 58 else 59 [deg; 0.0; 0.0] 60 | _ -> raise (Error.Coordinate_error 61 (Error.Parse_error "Invalid degree format")) 62 else if String.contains cleaned ':' then 63 (* Format: DD:MM:SS *) 64 cleaned 65 |> String.split_on_char ':' 66 |> List.map String.trim 67 |> List.map float_of_string 68 else if String.contains cleaned ' ' then 69 (* Format: DD MM SS *) 70 cleaned 71 |> String.split_on_char ' ' 72 |> List.filter (fun s -> String.length s > 0) 73 |> List.map String.trim 74 |> List.map float_of_string 75 else 76 raise (Error.Coordinate_error 77 (Error.Parse_error "Unknown coordinate format")) 78 in 79 80 (* Convert to decimal degrees *) 81 match parts with 82 | [deg] -> sign *. deg 83 | [deg; min] -> sign *. (abs_float deg +. min /. 60.0) 84 | [deg; min; sec] -> 85 sign *. (abs_float deg +. min /. 60.0 +. sec /. 3600.0) 86 | _ -> raise (Error.Coordinate_error 87 (Error.Parse_error "Invalid number of components")) 88 with 89 | Failure _ -> raise (Error.Coordinate_error 90 (Error.Parse_error ("Failed to parse coordinate: " ^ s))) 91 92let parse_lat s = 93 let value = parse_dms s in 94 Lat.create value 95 96let parse_lon s = 97 let value = parse_dms s in 98 Lon.create value 99 100let parse_coord s = 101 let s = String.trim s in 102 103 (* Try comma-separated first *) 104 let parts = 105 if String.contains s ',' then 106 String.split_on_char ',' s 107 |> List.map String.trim 108 else 109 (* Try space-separated *) 110 s 111 |> String.split_on_char ' ' 112 |> List.filter (fun s -> String.length s > 0) 113 |> List.map String.trim 114 in 115 116 match parts with 117 | [lat_str; lon_str] -> 118 let lat = parse_dms lat_str in 119 let lon = parse_dms lon_str in 120 Coord.create ~lat ~lon 121 | _ -> raise (Error.Coordinate_error 122 (Error.Parse_error "Expected lat,lon or lat lon format")) 123 124let try_parse_lat s = 125 try Some (parse_lat s) 126 with Error.Coordinate_error _ -> None 127 128let try_parse_lon s = 129 try Some (parse_lon s) 130 with Error.Coordinate_error _ -> None 131 132let try_parse_coord s = 133 try Some (parse_coord s) 134 with Error.Coordinate_error _ -> None