123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226(** Track types and operations *)(** Track point is an alias for waypoint *)typepoint=Waypoint.t(** Track segment *)typesegment={trkpts:pointlist;extensions:Extension.tlist;}(** Main track type *)typet={name:stringoption;cmt:stringoption;desc:stringoption;src:stringoption;links:Link.tlist;number:intoption;type_:stringoption;extensions:Extension.tlist;trksegs:segmentlist;}(** {2 Track Segment Operations} *)moduleSegment=structtypet=segment(** Create empty segment *)letempty={trkpts=[];extensions=[]}(** Create segment with points *)letmakepoints={trkpts=points;extensions=[]}(** Create segment from coordinates *)letmake_from_coordscoords=letmake_trkpt(lat_f,lon_f)=matchWaypoint.make_from_floats~lat:lat_f~lon:lon_f()with|Okwpt->wpt|Errore->invalid_argeinlettrkpts=List.mapmake_trkptcoordsin{trkpts;extensions=[]}(** Get points *)letpointst=t.trkpts(** Get point count *)letpoint_countt=List.lengtht.trkpts(** Get extensions *)letextensions(seg:segment)=seg.extensions(** Add point *)letadd_pointtpoint={twithtrkpts=t.trkpts@[point]}(** Add points *)letadd_pointstpoints={twithtrkpts=t.trkpts@points}(** Extract coordinates *)letto_coordst=List.mapWaypoint.to_floatst.trkpts(** Calculate segment distance *)letdistancet=Route.total_distance{Route.emptywithrtepts=t.trkpts}(** Check if empty *)letis_emptyt=List.lengtht.trkpts=0(** First point *)letfirst_pointt=matcht.trkptswith|[]->None|p::_->Somep(** Last point *)letlast_pointt=matchList.revt.trkptswith|[]->None|p::_->Somep(** Compare segments *)letcomparet1t2=List.compareWaypoint.comparet1.trkptst2.trkpts(** Test segment equality *)letequalt1t2=comparet1t2=0(** Pretty print segment *)letppppft=Format.fprintfppf"segment (%d points)"(point_countt)end(** {2 Track Operations} *)(** Create empty track *)letempty={name=None;cmt=None;desc=None;src=None;links=[];number=None;type_=None;extensions=[];trksegs=[];}(** Create track with name *)letmake~name={emptywithname=Somename}(** Create track from coordinate list (single segment) *)letmake_from_coords~namecoords=letsegment=Segment.make_from_coordscoordsin{emptywithname=Somename;trksegs=[segment]}(** Get track name *)letnamet=t.name(** Get track description *)letdescriptiont=t.desc(** Get track comment *)letcommentt=t.cmt(** Get track source *)letsourcet=t.src(** Get track links *)letlinkst=t.links(** Get track number *)letnumbert=t.number(** Get track type *)lettype_t=t.type_(** Get track extensions *)letextensionst=t.extensions(** Get track segments *)letsegmentst=t.trksegs(** Get segment count *)letsegment_countt=List.lengtht.trksegs(** Get total point count across all segments *)letpoint_countt=List.fold_left(funaccseg->acc+Segment.point_countseg)0t.trksegs(** Clear all segments *)letclear_segmentst={twithtrksegs=[]}(** Extract all coordinates from track *)letto_coordst=List.fold_left(funaccseg->List.fold_left(funacctrkpt->Waypoint.to_floatstrkpt::acc)accseg.trkpts)[]t.trksegs|>List.rev(** Calculate total track distance across all segments *)lettotal_distancet=List.fold_left(funaccseg->acc+.Segment.distanceseg)0.0t.trksegs(** Check if track is empty *)letis_emptyt=List.lengtht.trksegs=0(** Get all points from all segments *)letall_pointst=List.fold_left(funaccseg->acc@seg.trkpts)[]t.trksegs(** Get first point from first segment *)letfirst_pointt=matcht.trksegswith|[]->None|seg::_->Segment.first_pointseg(** Get last point from last segment *)letlast_pointt=matchList.revt.trksegswith|[]->None|seg::_->Segment.last_pointseg(** Compare tracks *)letcomparet1t2=letname_cmp=Option.compareString.comparet1.namet2.nameinifname_cmp<>0thenname_cmpelseletdesc_cmp=Option.compareString.comparet1.desct2.descinifdesc_cmp<>0thendesc_cmpelseList.compareSegment.comparet1.trksegst2.trksegs(** Test track equality *)letequalt1t2=comparet1t2=0(** {2 Functional Operations} *)(** Update name *)letwith_nametname={twithname=Somename}(** Update comment *)letwith_commenttcmt={twithcmt=Somecmt}(** Update description *)letwith_descriptiontdesc={twithdesc=Somedesc}(** Update source *)letwith_sourcetsrc={twithsrc=Somesrc}(** Update number *)letwith_numbertnumber={twithnumber=Somenumber}(** Update type *)letwith_typettype_={twithtype_=Sometype_}(** Add segment *)letadd_segmentttrkseg={twithtrksegs=t.trksegs@[trkseg]}(** Add link *)letadd_linktlink={twithlinks=t.links@[link]}(** Add extensions *)letadd_extensionstextensions={twithextensions=t.extensions@extensions}(** Pretty print track *)letppppft=matcht.namewith|Somename->Format.fprintfppf"\"%s\" (%d segments, %d points)"name(segment_countt)(point_countt)|None->Format.fprintfppf"(unnamed track, %d segments, %d points)"(segment_countt)(point_countt)