12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758(** Geographic coordinate types with validation *)(** Private coordinate types with validation constraints *)typelatitude=privatefloattypelongitude=privatefloattypedegrees=privatefloat(** Coordinate pair - main type for this module *)typet={lat:latitude;lon:longitude;}(** Smart constructors for validated coordinates *)letlatitudef=iff>=-90.0&&f<=90.0thenOk(Obj.magicf:latitude)elseError(Printf.sprintf"Invalid latitude: %f (must be between -90.0 and 90.0)"f)letlongitudef=iff>=-180.0&&f<180.0thenOk(Obj.magicf:longitude)elseError(Printf.sprintf"Invalid longitude: %f (must be between -180.0 and 180.0)"f)letdegreesf=iff>=0.0&&f<360.0thenOk(Obj.magicf:degrees)elseError(Printf.sprintf"Invalid degrees: %f (must be between 0.0 and 360.0)"f)(** Convert back to float *)letlatitude_to_float(lat:latitude)=(lat:>float)letlongitude_to_float(lon:longitude)=(lon:>float)letdegrees_to_float(deg:degrees)=(deg:>float)(** Create coordinate pair *)letmakelatlon={lat;lon}(** Create coordinate pair from floats with validation *)letmake_from_floatslat_flon_f=matchlatitudelat_f,longitudelon_fwith|Oklat,Oklon->Ok{lat;lon}|Errore,_|_,Errore->Errore(** Extract components *)letlatt=t.latletlont=t.lonletto_floatst=(latitude_to_floatt.lat,longitude_to_floatt.lon)(** Compare coordinates *)letcomparet1t2=letlat_cmp=Float.compare(latitude_to_floatt1.lat)(latitude_to_floatt2.lat)iniflat_cmp<>0thenlat_cmpelseFloat.compare(longitude_to_floatt1.lon)(longitude_to_floatt2.lon)(** Equality *)letequalt1t2=comparet1t2=0(** Pretty printer *)letppppft=Format.fprintfppf"(%g, %g)"(latitude_to_floatt.lat)(longitude_to_floatt.lon)