Skip to content

Commit f3f10a1

Browse files
authored
v1.1
New places ids and smart places grouping
2 parents 6152387 + b119ca4 commit f3f10a1

File tree

7 files changed

+221
-91
lines changed

7 files changed

+221
-91
lines changed

Arc-app-export-converter/Arc-app-export-converter.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
<Compile Include="BurnedCalCalculator.cs" />
4141
<Compile Include="XmlReader.cs" />
4242
<Compile Include="JsonParser.cs" />
43+
<Compile Include="PlacesManager.cs" />
44+
<Compile Include="HelpMethods.cs" />
4345
</ItemGroup>
4446
<ItemGroup>
4547
<None Include="packages.config" />
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
3+
public static class HelpMethods {
4+
5+
// Distance measure
6+
public static float DistanceTo(XmlTimeline.Coordinates wp1, XmlTimeline.Coordinates wp2, char unit = 'K') {
7+
// Copied from
8+
// https://stackoverflow.com/questions/6366408/calculating-distance-between-two-latitude-and-longitude-geocoordinates
9+
10+
11+
double rlat1 = Math.PI * wp1.lat / 180;
12+
13+
double rlat2 = Math.PI * wp2.lat / 180;
14+
double theta = wp1.lon - wp2.lon;
15+
double rtheta = Math.PI * theta / 180;
16+
double dist =
17+
Math.Sin(rlat1) * Math.Sin(rlat2) + Math.Cos(rlat1) *
18+
Math.Cos(rlat2) * Math.Cos(rtheta);
19+
dist = Math.Acos(dist);
20+
dist = dist * 180 / Math.PI;
21+
dist = dist * 60 * 1.1515;
22+
23+
float distF = (float)dist;
24+
25+
switch (unit) {
26+
case 'K': //Kilometers -> default
27+
return distF * 1.609344f;
28+
case 'N': //Nautical Miles
29+
return distF * 0.8684f;
30+
case 'M': //Miles
31+
return distF;
32+
}
33+
34+
return distF;
35+
}
36+
public static float DistanceTo(JsonMoves.Day.Segment.Place.Location jwp1, JsonMoves.Day.Segment.Place.Location jwp2, char unit = 'K') {
37+
XmlTimeline.Coordinates wp1 = new XmlTimeline.Coordinates(jwp1.lat, jwp1.lon);
38+
XmlTimeline.Coordinates wp2 = new XmlTimeline.Coordinates(jwp2.lat, jwp2.lon);
39+
return DistanceTo(wp1, wp2, unit);
40+
}
41+
42+
// Time conversations
43+
public static DateTime ParseIso8601(string iso8601Time) {
44+
return DateTime.Parse(iso8601Time, null, System.Globalization.DateTimeStyles.RoundtripKind);
45+
}
46+
public static string ConvertToIso1601(DateTime time) {
47+
string output = time.ToString(Iso1601Format);
48+
return output.Replace(":", "");
49+
}
50+
public static string Iso1601Format = "yyyyMMddTHHmmsszzz";
51+
52+
// GPX conversion
53+
public static XmlTimeline.Coordinates GetLatLon(string line) {
54+
string lat = "";
55+
string lon = "";
56+
bool captureMode = false;
57+
string capture = "";
58+
foreach (char item in line) {
59+
if (item == '"') {
60+
captureMode = !captureMode;
61+
if (captureMode == false) {
62+
if (lat == "")
63+
lat = capture;
64+
else
65+
lon = capture;
66+
capture = "";
67+
}
68+
} else if (captureMode) {
69+
capture += item;
70+
}
71+
}
72+
return new XmlTimeline.Coordinates(lat, lon);
73+
}
74+
public static string LeaveCenterFromString(string text, string removeLeft, string removeRight) {
75+
string temp = text;
76+
temp = temp.Replace(removeLeft, "");
77+
temp = temp.Replace(removeRight, "");
78+
return temp;
79+
}
80+
81+
// To Json conversion
82+
public static JsonMoves.ActivityGroup ReturnGroup(ActivityType type) {
83+
switch (type) {
84+
case ActivityType.walking:
85+
return JsonMoves.ActivityGroup.walking;
86+
case ActivityType.running:
87+
return JsonMoves.ActivityGroup.running;
88+
case ActivityType.cycling:
89+
return JsonMoves.ActivityGroup.cycling;
90+
default:
91+
return JsonMoves.ActivityGroup.transport;
92+
}
93+
}
94+
}
95+

Arc-app-export-converter/JsonParser.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@ public Location(double lat, double lon) {
4747
this.lon = lon;
4848
}
4949
}
50-
public long id = 1234567;
50+
public int id;
5151
public string name;
5252
public string type = "user";
5353
public Location location;
5454

55-
public Place(string name, Location location) {
55+
public Place(string name, Location location, DateTime startTime, DateTime endTime) {
5656
this.name = name;
5757
this.location = location;
58+
this.id = PlacesManager.ReturnPlaceId(this, startTime, endTime);
5859
}
5960

6061
}
@@ -179,7 +180,10 @@ static void ParseSegments(int i, JsonMoves json, List<XmlReader> xml) {
179180
}
180181
static JsonMoves.Day.Segment SegmentPlace(XmlTimeline.Place item) {
181182
JsonMoves.Day.Segment output = new JsonMoves.Day.Segment(JsonMoves.SegmentType.place, item.startTime.Value, item.endTime.Value);
182-
output.place = new JsonMoves.Day.Segment.Place(item.name, new JsonMoves.Day.Segment.Place.Location(item.position.lat, item.position.lon));
183+
output.place = new JsonMoves.Day.Segment.Place(item.name,
184+
new JsonMoves.Day.Segment.Place.Location(item.position.lat, item.position.lon),
185+
item.startTime.Value,
186+
item.endTime.Value);
183187
return output;
184188
}
185189
static JsonMoves.Day.Segment SegmentMove(XmlTimeline.Activity item) {
@@ -217,7 +221,7 @@ static void WriteToFile(string json) {
217221
sw.Write(json);
218222
sw.Close();
219223
Console.ForegroundColor = ConsoleColor.DarkGreen;
220-
Console.Write("Json file created!");
224+
Console.WriteLine("Json file created!");
221225
}
222226
}
223227

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Newtonsoft.Json;
5+
6+
public class Place {
7+
public class PlaceVisit {
8+
public DateTime startTime;
9+
public DateTime endTime;
10+
11+
public PlaceVisit(DateTime startTime, DateTime endTime) {
12+
this.startTime = startTime;
13+
this.endTime = endTime;
14+
}
15+
}
16+
public int id;
17+
public string name;
18+
public JsonMoves.Day.Segment.Place.Location location;
19+
public List<PlaceVisit> visits = new List<PlaceVisit>();
20+
21+
public Place(int id, string name, JsonMoves.Day.Segment.Place.Location location) {
22+
this.id = id;
23+
this.name = name;
24+
this.location = location;
25+
}
26+
27+
public void AddVisit(DateTime startTime, DateTime endTime) {
28+
PlaceVisit tempVisit = new PlaceVisit(startTime, endTime);
29+
foreach (var item in visits) {
30+
if (item.startTime == startTime)
31+
return;
32+
}
33+
visits.Add(tempVisit);
34+
}
35+
}
36+
37+
public class PlacesSave {
38+
public List<Place> places = new List<Place>();
39+
public int currentId;
40+
41+
public PlacesSave(List<Place> places, int currentId) {
42+
this.places = places;
43+
this.currentId = currentId;
44+
}
45+
}
46+
47+
public class PlacesManager {
48+
static bool loaded;
49+
static string placesFileName = "places.json";
50+
static float distanceTolerance = 0.07f;
51+
52+
static List<Place> places = new List<Place>();
53+
static int currentId;
54+
55+
public static bool Loaded {
56+
get {
57+
if (!loaded) {
58+
LoadPlaces();
59+
loaded = true;
60+
}
61+
return true;
62+
}
63+
}
64+
65+
public static int ReturnPlaceId(JsonMoves.Day.Segment.Place place, DateTime startTime, DateTime endTime) {
66+
foreach (var item in places) {
67+
if (CheckIfSamePlace(item, place)) {
68+
item.AddVisit(startTime, endTime);
69+
return item.id;
70+
}
71+
}
72+
Place newPlace = new Place(currentId++, place.name, place.location);
73+
places.Add(newPlace);
74+
newPlace.AddVisit(startTime, endTime);
75+
return newPlace.id;
76+
}
77+
static bool CheckIfSamePlace(Place place, JsonMoves.Day.Segment.Place jsonPlace) {
78+
if (place.name == jsonPlace.name) {
79+
if (HelpMethods.DistanceTo(place.location, jsonPlace.location) <= distanceTolerance)
80+
return true;
81+
}
82+
return false;
83+
}
84+
85+
// Loading files
86+
static void LoadPlaces() {
87+
if (File.Exists(placesFileName)) {
88+
StreamReader sr = new StreamReader(placesFileName);
89+
string line = sr.ReadLine();
90+
sr.Close();
91+
PlacesSave save = JsonConvert.DeserializeObject<PlacesSave>(line);
92+
places = save.places;
93+
currentId = save.currentId;
94+
}
95+
}
96+
public static void SavePlaces() {
97+
StreamWriter sw = new StreamWriter(placesFileName);
98+
sw.Write(JsonConvert.SerializeObject(new PlacesSave(places, currentId)));
99+
sw.Close();
100+
Console.WriteLine("Places database saved to file " + placesFileName);
101+
}
102+
103+
}

Arc-app-export-converter/Program.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ class MainClass {
66

77
[STAThread]
88
public static void Main() {
9+
if (PlacesManager.Loaded)
10+
Console.WriteLine("Places initialised");
911
Console.Write("Input your weight (in kg): ");
1012
weight = Convert.ToInt32(Console.ReadLine());
1113
XmlReader xr = new XmlReader();
1214
xr.LoadFile();
1315
xr.ParseToJson();
16+
17+
// On finish
18+
PlacesManager.SavePlaces();
1419
}
1520
}

Arc-app-export-converter/XmlReader.cs

Lines changed: 7 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public Coordinates(string lat, string lon) {
4242
this.lat = Convert.ToDouble(lat);
4343
this.lon = Convert.ToDouble(lon);
4444
}
45+
public Coordinates(double lat, double lon) {
46+
this.lat = lat;
47+
this.lon = lon;
48+
}
4549
public override string ToString() {
4650
return string.Format("Lat: {0} Lon: {1} Ele: {2}", lat, lon, ele);
4751
}
@@ -141,7 +145,7 @@ public Activity(ActivityType activity, Coordinates[] waypoints) {
141145
}
142146

143147
public void MargeWithNew(Coordinates[] waypoints) {
144-
List<Coordinates> tempMerge = waypoints.ToList();
148+
List<Coordinates> tempMerge = this.waypoints.ToList();
145149
foreach (var item in waypoints) {
146150
tempMerge.Add(item);
147151
}
@@ -217,7 +221,7 @@ public void LoadFile(string path = "file.gpx") {
217221
SetStartEnd();
218222
SetSummary();
219223

220-
//Display();
224+
Display();
221225
}
222226
void GetPlace(string line, StreamReader sr) {
223227
XmlTimeline.Coordinates location = HelpMethods.GetLatLon(line);
@@ -335,87 +339,4 @@ public void ParseToJson() {
335339
};
336340
JsonParser.Parse(tempList);
337341
}
338-
}
339-
340-
public static class HelpMethods {
341-
342-
// Copied from
343-
// https://stackoverflow.com/questions/6366408/calculating-distance-between-two-latitude-and-longitude-geocoordinates
344-
345-
public static float DistanceTo(XmlTimeline.Coordinates wp1, XmlTimeline.Coordinates wp2, char unit = 'K') {
346-
double rlat1 = Math.PI * wp1.lat / 180;
347-
double rlat2 = Math.PI * wp2.lat / 180;
348-
double theta = wp1.lon - wp2.lon;
349-
double rtheta = Math.PI * theta / 180;
350-
double dist =
351-
Math.Sin(rlat1) * Math.Sin(rlat2) + Math.Cos(rlat1) *
352-
Math.Cos(rlat2) * Math.Cos(rtheta);
353-
dist = Math.Acos(dist);
354-
dist = dist * 180 / Math.PI;
355-
dist = dist * 60 * 1.1515;
356-
357-
float distF = (float)dist;
358-
359-
switch (unit) {
360-
case 'K': //Kilometers -> default
361-
return distF * 1.609344f;
362-
case 'N': //Nautical Miles
363-
return distF * 0.8684f;
364-
case 'M': //Miles
365-
return distF;
366-
}
367-
368-
return distF;
369-
}
370-
371-
public static DateTime ParseIso8601 (string iso8601Time) {
372-
return DateTime.Parse(iso8601Time, null, System.Globalization.DateTimeStyles.RoundtripKind);
373-
}
374-
375-
public static XmlTimeline.Coordinates GetLatLon(string line) {
376-
string lat = "";
377-
string lon = "";
378-
bool captureMode = false;
379-
string capture = "";
380-
foreach (char item in line) {
381-
if (item == '"') {
382-
captureMode = !captureMode;
383-
if (captureMode == false) {
384-
if (lat == "")
385-
lat = capture;
386-
else
387-
lon = capture;
388-
capture = "";
389-
}
390-
} else if (captureMode) {
391-
capture += item;
392-
}
393-
}
394-
return new XmlTimeline.Coordinates(lat, lon);
395-
}
396-
public static string LeaveCenterFromString(string text, string removeLeft, string removeRight) {
397-
string temp = text;
398-
temp = temp.Replace(removeLeft, "");
399-
temp = temp.Replace(removeRight, "");
400-
return temp;
401-
}
402-
403-
public static string ConvertToIso1601(DateTime time) {
404-
string output = time.ToString(Iso1601Format);
405-
return output.Replace(":", "");
406-
}
407-
public static string Iso1601Format = "yyyyMMddTHHmmsszzz";
408-
409-
public static JsonMoves.ActivityGroup ReturnGroup(ActivityType type) {
410-
switch (type) {
411-
case ActivityType.walking:
412-
return JsonMoves.ActivityGroup.walking;
413-
case ActivityType.running:
414-
return JsonMoves.ActivityGroup.running;
415-
case ActivityType.cycling:
416-
return JsonMoves.ActivityGroup.cycling;
417-
default:
418-
return JsonMoves.ActivityGroup.transport;
419-
}
420-
}
421-
}
342+
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Convert GPX exported from [Arc App][1] to storyline Json format used by abandome
77
Converter generates all information used by Moves developers:
88
- Calculates burned calories based on movement speed and body weight
99
- Creates summary of all movement types
10+
- Sometimes GPX file can contain two different places with the same name - if those are too far from each other, converter will split it into two different places
1011

1112
### Usage:
1213
1. Download [newest release][2]
@@ -20,7 +21,6 @@ Converter generates all information used by Moves developers:
2021

2122
### TODO:
2223
- Export many files/month file into a single json
23-
- Create place id’s with smart place grouping based on name and coordinates
2424

2525
[1]: https://itunes.apple.com/app/arc-app-location-activity-tracker/id1063151918?mt=8
2626
[2]: https://github.com/bionicl/Arc-app-export-converter/releases/

0 commit comments

Comments
 (0)