-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathformats.ml
280 lines (217 loc) · 8 KB
/
formats.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
(* Copyright (C) 2014, Marco Stronati, All Rights Reserved.
This file is distributed under the terms of the
GNU General Public License version 3 or later. *)
(**
Parser/Printer for geojson, gpx, plt formats.
*)
open Geo
let default_timestamp = CalendarLib.Printer.Calendar.from_fstring "%FT%TZ" "1900-01-01T00:00:00Z"
(* Type of a freshly parsed trace *)
type point = {
coord: Utm.t;
idx: int;
time: CalendarLib.Calendar.t}
(* meta information attached to an elaborated trace, to be dumped *)
type pred_params = {
d: int;
turn: bool;
pred_e: float
}
type meta = {
i: int;
t: CalendarLib.Calendar.t;
h: bool;
e: float;
et: float;
l: float;
pred: pred_params
}
(* *)
(* GPX PARSER/DUMPER *)
(* *)
open Xml
(* parse a gpx and return a list of list of points
latitude and longitude are in decimal degrees and converted to planar coordinates
the head of the list are the most recent points, the last of the gpx file
*)
let rec track_of_trk node =
match node with
| PCData _ -> failwith "PCData"
| Element (t,a,c) -> match t with
| "trk"
| "trkseg" -> List.fold_left (fun list child -> List.append (track_of_trk child) list) [] c
| "trkpt" ->
let latlon = Wgs.make (float_of_string (snd (List.nth a 0))) (float_of_string (snd (List.nth a 1))) in
let time =
let elements_time = (List.filter (fun ele -> match ele with Element ("time",_,_) -> true | _ -> false) c) in
if List.length elements_time > 0
then
let time_string = match (List.hd elements_time) with Element ("time",_,[PCData value]) -> value | _ -> failwith "track_of_trk" in
CalendarLib.Printer.Calendar.from_fstring "%FT%TZ" time_string
else
CalendarLib.Printer.Calendar.from_fstring "%FT%TZ" "1900-01-01T00:00:00Z"
in
(Utm.of_latlon latlon, time)::[]
| _ -> []
let rec track_of_gpx node =
match node with
| PCData _ -> failwith "PCData"
| Element(t,_,c) ->
match t with
| "gpx" ->
let tracks_metaless = List.fold_left
(fun list child ->
let res = (track_of_trk child) in
if (List.length res) > 1 then res::list else list)
[] c
in
let track_metaless = List.flatten tracks_metaless in
let l = List.length track_metaless in
List.mapi (fun idx pt -> {coord = (fst pt); idx=l-idx; time=(snd pt)} ) track_metaless
| _ -> []
(* parse a list of list of points and return a gpx
latitude and longitude are in planar coordinates and converted to decimal degrees
the head of the list are the most recent points
*)
let gpx_of_track track =
let element_of_point pt =
let time = pt.time in
let (lat,lon) = Wgs.tuple (Wgs.of_xy pt.coord) in
Element("trkpt",
[("lat", (Printf.sprintf "%8.6f" lat));
("lon", (Printf.sprintf "%8.6f" lon))],
[Element("time",[],[PCData (CalendarLib.Printer.Calendar.sprint "%FT%TZ" time)])]) in
let element_of_track track =
let rtrack = List.rev track in
let trkpt_list = List.map (element_of_point) rtrack in
Element("trkseg",[],trkpt_list) in
let trk_list = element_of_track track in (* todo this need to be inverted? *)
Element("gpx",
[("version", "1.0"); ("creator", "pdp");
("xmlns", "http://www.topografix.com/GPX/1/0")],
[Element("trk",[],[trk_list])])
let gpx_of_rich_track rich_track =
let track = List.map (fun (pt,meta) -> {coord=pt; idx=meta.i; time=meta.t}) rich_track in
gpx_of_track track
let xml_to_file file gpx =
let header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" in
let out = open_out file in
output_string out (header^(Xml.to_string_fmt gpx));
close_out out
(*
GEO JSON DUMPER
*)
let geojson_of_rich_track track =
(* note: order for geo_json points is: longitude, latitude, altitude *)
let features = List.fold_left
(fun tmp (pt,meta) ->
let (lat,lon) = Wgs.tuple (Wgs.of_xy pt) in
let json_point = Printf.sprintf
"{ \"type\" : \"Feature\",
\"geometry\": {
\"type\": \"Point\",
\"coordinates\": [%f, %f]
},
\"properties\": {
\"idx\" : %i,
\"time\" : \"%s\",
\"hard\" : %b,
\"epsilon\" : %f,
\"epsilon_theta\" : %f,
\"alpha\" : %s,
\"depth\" : %i,
\"err\" : %f,
\"turn\": %b
}
},"
lon lat meta.i (CalendarLib.Printer.Calendar.sprint "%F %T" meta.t) meta.h meta.e meta.et
(if meta.l = infinity then "\"Infinity\"" else Printf.sprintf "%f" meta.l)
meta.pred.d meta.pred.pred_e meta.pred.turn in
tmp^json_point)
"" track in
let features_without_last_coma = String.sub features 0 (String.length features -1) in
"{ \"type\": \"FeatureCollection\",
\"features\": [" ^ features_without_last_coma ^ "]}"
let geojson_of_simple_track track =
(* note: order for geo_json points is: longitude, latitude, altitude *)
let features = List.fold_left
(fun tmp pt ->
let (lat,lon) = Wgs.tuple (Wgs.of_xy pt.coord) in
let json_point = Printf.sprintf
"{ \"type\" : \"Feature\",
\"geometry\": {
\"type\": \"Point\",
\"coordinates\": [%f, %f]
},
\"properties\": {
\"idx\" : %i,
\"time\" : \"%s\"
}
},"
lon lat pt.idx (CalendarLib.Printer.Calendar.sprint "%F %T" pt.time)
in
tmp^json_point)
"" track in
let features_without_last_coma = String.sub features 0 (String.length features -1) in
"{ \"type\": \"FeatureCollection\",
\"features\": [" ^ features_without_last_coma ^ "]}"
let geojson_to_file geojson name =
let out = open_out name in
output_string out geojson;
close_out out
(* plt parser *)
let track_of_plt filename =
let track = ref [] in
let filelines = BatFile.lines_of filename in
BatEnum.drop 6 filelines;
BatEnum.iteri (fun i line ->
(* Printf.printf "%i %s\n" i line; *)
let pt = Scanf.sscanf line "%f,%f,0,%f,%f,%s@,%s"
(fun lat lon alt timeweird date time ->
(* Printf.printf "lat %f lon %f time %s\n" lat lon date; *)
let coord = Utm.of_latlon (Wgs.make lat lon) in
let time = CalendarLib.Printer.Calendar.from_fstring "%FT%TZ" (date^"T"^time^"Z") in
{coord = coord; idx = i; time = time} )
in
track := pt::!track)
filelines;
!track
let gpx_of_plt filename_src filename_dst =
let track = track_of_plt filename_src in
xml_to_file filename_dst (gpx_of_track track)
(* tdrive custom format parser *)
let track_of_tdrive filename =
let track = ref [] in
let filelines = BatFile.lines_of filename in
(* Enum.drop 6 filelines; *)
BatEnum.iteri (fun i line ->
(* Printf.printf "%i %s\n" i line; *)
let pt = Scanf.sscanf line "%i,%s %s@,%f,%f"
(fun id date time lon lat ->
(* Printf.printf "lat %f lon %f date %s time %s\n" lat lon date time; *)
let coord = Utm.of_latlon (Wgs.make lat lon) in
let time = CalendarLib.Printer.Calendar.from_fstring "%FT%TZ" (date^"T"^time^"Z") in
{coord = coord; idx = i; time = time} )
in
track := pt::!track)
filelines;
!track
let gpx_of_tdrive filename_src filename_dst =
let track = track_of_tdrive filename_src in
xml_to_file filename_dst (gpx_of_track track)
(* gwolla *)
let track_of_gowalla filename =
let track = ref [] in
let filelines = BatFile.lines_of filename in
let time = CalendarLib.Printer.Calendar.from_fstring "%FT%TZ" "1900-01-01T00:00:00Z" in
BatEnum.iteri (fun _ line ->
(* Printf.printf "%i %s\n" i line; *)
let pt = Scanf.sscanf line "%i, %f, %f"
(fun id lat lon ->
(* Printf.printf "lat %f lon %f date %s time %s\n" lat lon date time; *)
let coord = Utm.of_latlon (Wgs.make lat lon) in
{coord = coord; idx = id; time = time} )
in
track := pt::!track)
filelines;
!track