From dce7c2186d4d485d804d90d94a004212145dabbe Mon Sep 17 00:00:00 2001 From: cyber-toad <128428520+cyber-toad@users.noreply.github.com> Date: Sun, 4 Jun 2023 19:14:43 +0200 Subject: [PATCH] [Draft] [gpx] Add gpx import (#5166) * [gpx][ios] Add GPX to OMaps.plist Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> * [gpx][android] Add GPX to AndroidManifest.xml Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> * [gpx] Add serdes_gpx Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> * [gpx] Add GPX to bookmark_helpers Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> * [gpx][android] Add GPX to Framework.cpp Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> * [gpx] Add GPX test Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> * [android] Adjust gpx filter Signed-off-by: cyber-toad * [gpx] Update test and manifest Signed-off-by: cyber-toad * [gpx] Apply code-review suggestions Signed-off-by: cyber-toad * [gpx] Add basic parser tests for real gpx tracks Signed-off-by: cyber-toad * [gpx] Remove unused code, add test for waypoints only Signed-off-by: cyber-toad * [gpx] Support of gpx route (rte/rtept tag) Signed-off-by: cyber-toad * [gpx] Rework parser to use string_view Signed-off-by: cyber-toad * [gpx] Remove unsupported parameters Signed-off-by: cyber-toad * [gpx] Add support for tag Signed-off-by: cyber-toad * [gpx] Fix name/description extraction Signed-off-by: cyber-toad * [gpx] Implement code-review suggestions Signed-off-by: cyber-toad * [gpx] Extract track & category names from file name Signed-off-by: cyber-toad * [gpx] Implement code-review suggestions Signed-off-by: cyber-toad --------- Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> Signed-off-by: cyber-toad Co-authored-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com> --- android/AndroidManifest.xml | 18 + android/jni/app/organicmaps/Framework.cpp | 2 +- .../bookmarks/data/BookmarkManager.java | 2 + data/gpx_test_data/color.gpx | 80 + data/gpx_test_data/empty.gpx | 15 + data/gpx_test_data/empty_names1.gpx | 24 + data/gpx_test_data/empty_names2.gpx | 13 + data/gpx_test_data/go_map.gpx | 2 + data/gpx_test_data/gpx_studio.gpx | 3179 +++++++++++++++++ data/gpx_test_data/osm_track.gpx | 2515 +++++++++++++ data/gpx_test_data/points.gpx | 32 + data/gpx_test_data/route.gpx | 16 + data/gpx_test_data/tower_collector.gpx | 357 ++ iphone/Maps/OMaps.plist | 36 + kml/CMakeLists.txt | 3 + kml/kml_tests/CMakeLists.txt | 3 +- kml/kml_tests/gpx_tests.cpp | 157 + kml/serdes.cpp | 12 - kml/serdes.hpp | 1 + kml/serdes_common.hpp | 18 + kml/serdes_gpx.cpp | 251 ++ kml/serdes_gpx.hpp | 95 + map/bookmark_helpers.cpp | 52 +- map/bookmark_helpers.hpp | 5 +- map/bookmark_manager.cpp | 12 +- map/map_tests/bookmarks_test.cpp | 17 + xcode/kml/kml.xcodeproj/project.pbxproj | 10 + 27 files changed, 6910 insertions(+), 17 deletions(-) create mode 100644 data/gpx_test_data/color.gpx create mode 100644 data/gpx_test_data/empty.gpx create mode 100644 data/gpx_test_data/empty_names1.gpx create mode 100644 data/gpx_test_data/empty_names2.gpx create mode 100644 data/gpx_test_data/go_map.gpx create mode 100644 data/gpx_test_data/gpx_studio.gpx create mode 100644 data/gpx_test_data/osm_track.gpx create mode 100644 data/gpx_test_data/points.gpx create mode 100644 data/gpx_test_data/route.gpx create mode 100644 data/gpx_test_data/tower_collector.gpx create mode 100644 kml/kml_tests/gpx_tests.cpp create mode 100644 kml/serdes_common.hpp create mode 100644 kml/serdes_gpx.cpp create mode 100644 kml/serdes_gpx.hpp diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 23fee74851..bd410d50ba 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -584,6 +584,8 @@ + + @@ -591,6 +593,8 @@ + + @@ -608,17 +612,31 @@ + + + + + + + + + + + + + + diff --git a/android/jni/app/organicmaps/Framework.cpp b/android/jni/app/organicmaps/Framework.cpp index dbe536d110..ac5ad0a6af 100644 --- a/android/jni/app/organicmaps/Framework.cpp +++ b/android/jni/app/organicmaps/Framework.cpp @@ -1106,7 +1106,7 @@ Java_app_organicmaps_Framework_nativeGetMovableFilesExts(JNIEnv * env, jclass) JNIEXPORT jobjectArray JNICALL Java_app_organicmaps_Framework_nativeGetBookmarksFilesExts(JNIEnv * env, jclass) { - const vector exts = { kKmzExtension, kKmlExtension, kKmbExtension }; + const vector exts = { kKmzExtension, kKmlExtension, kKmbExtension, kGpxExtension }; return jni::ToJavaStringArray(env, exts); } diff --git a/android/src/app/organicmaps/bookmarks/data/BookmarkManager.java b/android/src/app/organicmaps/bookmarks/data/BookmarkManager.java index cf46904b0f..b53f5af8af 100644 --- a/android/src/app/organicmaps/bookmarks/data/BookmarkManager.java +++ b/android/src/app/organicmaps/bookmarks/data/BookmarkManager.java @@ -464,6 +464,8 @@ public enum BookmarkManager else if (type.equalsIgnoreCase("kml+xml")) return filename + ".kml"; } + if (mime.endsWith("gpx+xml") || mime.endsWith("gpx")) // match application/gpx, application/gpx+xml + return filename + ".gpx"; } return null; diff --git a/data/gpx_test_data/color.gpx b/data/gpx_test_data/color.gpx new file mode 100644 index 0000000000..56a81bef96 --- /dev/null +++ b/data/gpx_test_data/color.gpx @@ -0,0 +1,80 @@ + + + + new + + gpx.studio + + + Short description + + + new red + description 1 + Running + + + ff0000 + 0.7 + 3 + + + + + 184.8 + + + + asphalt + + + + + + 184.8 + + + + asphalt + + + + + + + + + new blue + description 2 + Running + + + 0000ff + 0.7 + 3 + + + + + 184.8 + + + + asphalt + + + + + + 184.8 + + + + asphalt + + + + + + + \ No newline at end of file diff --git a/data/gpx_test_data/empty.gpx b/data/gpx_test_data/empty.gpx new file mode 100644 index 0000000000..6a3e5504b4 --- /dev/null +++ b/data/gpx_test_data/empty.gpx @@ -0,0 +1,15 @@ + + + +new + + + +empty 1 + + + +empty 2 + + + \ No newline at end of file diff --git a/data/gpx_test_data/empty_names1.gpx b/data/gpx_test_data/empty_names1.gpx new file mode 100644 index 0000000000..d123470855 --- /dev/null +++ b/data/gpx_test_data/empty_names1.gpx @@ -0,0 +1,24 @@ + + + + + + 184.8 + + + 184.8 + + + + + + + + 184.8 + + + 184.8 + + + + \ No newline at end of file diff --git a/data/gpx_test_data/empty_names2.gpx b/data/gpx_test_data/empty_names2.gpx new file mode 100644 index 0000000000..ecf3525b68 --- /dev/null +++ b/data/gpx_test_data/empty_names2.gpx @@ -0,0 +1,13 @@ + + + + + + 184.8 + + + 184.8 + + + + \ No newline at end of file diff --git a/data/gpx_test_data/go_map.gpx b/data/gpx_test_data/go_map.gpx new file mode 100644 index 0000000000..3988c34546 --- /dev/null +++ b/data/gpx_test_data/go_map.gpx @@ -0,0 +1,2 @@ + +679.8561394287109679.8699347741699679.9026802755127679.9265541908813679.9473436820276679.9487085198141679.9256089196083679.8990741060721679.8697238473749679.8512431672082679.8319268374728679.8176355853586679.794563393741679.7922561745793679.7920254526631679.7920023804714679.7920000732523679.7919998425303679.7919998194582679.791999817151679.7919998169202679.7919998168971679.7919998168949679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.7919998168946679.8287490844727679.895046081543679.9678134765625680.0077745422363680.0258331488037680.039723970398680.0540219881043680.0822034988593680.119848310091680.1662946759798680.2174114805374680.2959118328682680.3441915556014680.4053244595152680.4616452694379680.5068830633209680.5421685614591680.5778321210386680.6007344144964 \ No newline at end of file diff --git a/data/gpx_test_data/gpx_studio.gpx b/data/gpx_test_data/gpx_studio.gpx new file mode 100644 index 0000000000..3fc9e87a45 --- /dev/null +++ b/data/gpx_test_data/gpx_studio.gpx @@ -0,0 +1,3179 @@ + + + + new + + gpx.studio + + + + + new + Cycling + + + 185.0 + + + + asphalt + + + + + + 185.3 + + + + asphalt + + + + + + 184.8 + + + + asphalt + + + + + + 184.5 + + + + paved + + + + + + 184.3 + + + + paved + + + + + + 184.0 + + + + paved + + + + + + 183.8 + + + + paved + + + + + + 184.3 + + + + asphalt + + + + + + 184.8 + + + + asphalt + + + + + + 185.8 + + + + asphalt + + + + + + 185.8 + + + + asphalt + + + + + + 182.8 + + + + asphalt + + + + + + 185.0 + + + + asphalt + + + + + + 185.5 + + + + asphalt + + + + + + 186.0 + + + + asphalt + + + + + + 186.8 + + + + asphalt + + + + + + 186.3 + + + + asphalt + + + + + + 186.0 + + + + asphalt + + + + + + 185.5 + + + + asphalt + + + + + + 185.3 + + + + asphalt + + + + + + 185.8 + + + + asphalt + + + + + + 186.0 + + + + asphalt + + + + + + 186.3 + + + + asphalt + + + + + + 187.3 + + + + asphalt + + + + + + 187.5 + + + + asphalt + + + + + + 186.8 + + + 186.5 + + + 186.3 + + + + asphalt + + + + + + 186.0 + + + + asphalt + + + + + + 186.5 + + + + asphalt + + + + + + 185.0 + + + + asphalt + + + + + + 181.8 + + + + asphalt + + + + + + 182.0 + + + + asphalt + + + + + + 181.8 + + + + asphalt + + + + + + 181.8 + + + + asphalt + + + + + + 181.5 + + + + asphalt + + + + + + 181.3 + + + + asphalt + + + + + + 181.0 + + + + asphalt + + + + + + 180.0 + + + + asphalt + + + + + + 179.8 + + + + asphalt + + + + + + 180.5 + + + + asphalt + + + + + + 180.8 + + + + asphalt + + + + + + 181.3 + + + + asphalt + + + + + + 181.5 + + + + asphalt + + + + + + 182.3 + + + + asphalt + + + + + + 183.0 + + + + asphalt + + + + + + 183.0 + + + + asphalt + + + + + + 183.5 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 183.8 + + + + asphalt + + + + + + 183.5 + + + + asphalt + + + + + + 183.3 + + + + asphalt + + + + + + 183.0 + + + + asphalt + + + + + + 182.5 + + + + asphalt + + + + + + 183.0 + + + + asphalt + + + + + + 183.5 + + + + asphalt + + + + + + 183.8 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 184.3 + + + + asphalt + + + + + + 183.8 + + + + asphalt + + + + + + 183.0 + + + + asphalt + + + + + + 182.8 + + + + paving_stones + + + + + + 183.0 + + + + paving_stones + + + + + + 183.8 + + + + asphalt + + + + + + 184.3 + + + + asphalt + + + + + + 186.3 + + + + asphalt + + + + + + 188.3 + + + + asphalt + + + + + + 188.5 + + + + asphalt + + + + + + 188.5 + + + + asphalt + + + + + + 188.5 + + + + asphalt + + + + + + 188.5 + + + + asphalt + + + + + + 190.3 + + + + asphalt + + + + + + 190.3 + + + + asphalt + + + + + + 189.8 + + + + asphalt + + + + + + 189.8 + + + + asphalt + + + + + + 189.5 + + + + asphalt + + + + + + 189.5 + + + + asphalt + + + + + + 189.5 + + + + asphalt + + + + + + 189.5 + + + + asphalt + + + + + + 194.8 + + + + asphalt + + + + + + 194.8 + + + + asphalt + + + + + + 198.0 + + + + asphalt + + + + + + 198.5 + + + + asphalt + + + + + + 200.0 + + + + asphalt + + + + + + 201.0 + + + + asphalt + + + + + + 201.0 + + + + asphalt + + + + + + 201.0 + + + + asphalt + + + + + + 196.3 + + + + asphalt + + + + + + 195.3 + + + + asphalt + + + + + + 191.0 + + + + asphalt + + + + + + 190.0 + + + + asphalt + + + + + + 189.3 + + + + asphalt + + + + + + 188.8 + + + + asphalt + + + + + + 189.3 + + + + concrete + + + + + + 189.3 + + + + concrete + + + + + + 189.8 + + + + concrete + + + + + + 193.0 + + + + concrete + + + + + + 194.5 + + + + concrete + + + + + + 197.3 + + + + concrete + + + + + + 200.3 + + + + asphalt + + + + + + 201.0 + + + + asphalt + + + + + + 201.0 + + + + asphalt + + + + + + 202.3 + + + + asphalt + + + + + + 202.8 + + + + asphalt + + + + + + 202.8 + + + + asphalt + + + + + + 202.5 + + + + asphalt + + + + + + 202.5 + + + + asphalt + + + + + + 202.5 + + + + asphalt + + + + + + 202.5 + + + + asphalt + + + + + + 202.8 + + + + asphalt + + + + + + 202.8 + + + + asphalt + + + + + + 203.0 + + + + asphalt + + + + + + 203.3 + + + + asphalt + + + + + + 203.3 + + + + asphalt + + + + + + 203.5 + + + + asphalt + + + + + + 205.5 + + + + asphalt + + + + + + 205.0 + + + + asphalt + + + + + + 205.0 + + + + asphalt + + + + + + 205.0 + + + + asphalt + + + + + + 205.0 + + + + asphalt + + + + + + 203.0 + + + + asphalt + + + + + + 202.8 + + + + asphalt + + + + + + 203.3 + + + + asphalt + + + + + + 204.0 + + + + asphalt + + + + + + 203.8 + + + + asphalt + + + + + + 204.8 + + + + asphalt + + + + + + 205.3 + + + + asphalt + + + + + + 205.3 + + + + asphalt + + + + + + 209.8 + + + + concrete + + + + + + 210.3 + + + + concrete + + + + + + 210.5 + + + + concrete + + + + + + 215.0 + + + + concrete + + + + + + 215.5 + + + + concrete + + + + + + 215.5 + + + + concrete + + + + + + 215.5 + + + + asphalt + + + + + + 215.5 + + + + asphalt + + + + + + 215.0 + + + + asphalt + + + + + + 214.0 + + + + asphalt + + + + + + 214.0 + + + + asphalt + + + + + + 218.3 + + + + asphalt + + + + + + 216.3 + + + + asphalt + + + + + + 216.3 + + + + asphalt + + + + + + 216.0 + + + + asphalt + + + + + + 216.0 + + + + asphalt + + + + + + 211.8 + + + + asphalt + + + + + + 211.5 + + + + asphalt + + + + + + 211.8 + + + + asphalt + + + + + + 213.3 + + + + asphalt + + + + + + 213.3 + + + + asphalt + + + + + + 213.3 + + + + asphalt + + + + + + 213.0 + + + + asphalt + + + + + + 213.0 + + + + asphalt + + + + + + 213.0 + + + + asphalt + + + + + + 213.0 + + + + asphalt + + + + + + 213.0 + + + + asphalt + + + + + + 213.0 + + + + asphalt + + + + + + 214.5 + + + + asphalt + + + + + + 212.8 + + + + asphalt + + + + + + 212.8 + + + + asphalt + + + + + + 212.8 + + + + asphalt + + + + + + 212.8 + + + + asphalt + + + + + + 212.8 + + + + asphalt + + + + + + 212.8 + + + + asphalt + + + + + + 212.8 + + + + asphalt + + + + + + 213.0 + + + + paving_stones + + + + + + 213.3 + + + + paving_stones + + + + + + 214.8 + + + + paving_stones + + + + + + 216.8 + + + + paving_stones + + + + + + 217.3 + + + + paving_stones + + + + + + 216.5 + + + + paving_stones + + + + + + 215.5 + + + + paving_stones + + + + + + 215.3 + + + + paving_stones + + + + + + 215.0 + + + + paving_stones + + + + + + 213.3 + + + + paving_stones + + + + + + 212.0 + + + + paving_stones + + + + + + 210.8 + + + + paving_stones + + + + + + 208.8 + + + + paving_stones + + + + + + 208.3 + + + + paving_stones + + + + + + 208.0 + + + + asphalt + + + + + + 208.0 + + + + asphalt + + + + + + 208.0 + + + + asphalt + + + + + + 207.8 + + + + asphalt + + + + + + 207.5 + + + + asphalt + + + + + + 206.8 + + + + asphalt + + + + + + 205.5 + + + + asphalt + + + + + + 204.0 + + + + asphalt + + + + + + 201.3 + + + + asphalt + + + + + + 200.8 + + + + asphalt + + + + + + 200.3 + + + + asphalt + + + + + + 199.8 + + + + asphalt + + + + + + 199.5 + + + + asphalt + + + + + + 199.3 + + + + asphalt + + + + + + 198.8 + + + + asphalt + + + + + + 191.3 + + + + asphalt + + + + + + 190.8 + + + + asphalt + + + + + + 186.8 + + + + asphalt + + + + + + 182.0 + + + + asphalt + + + + + + 176.8 + + + + asphalt + + + + + + 176.8 + + + + asphalt + + + + + + 176.5 + + + + asphalt + + + + + + 176.0 + + + + asphalt + + + + + + 175.5 + + + + asphalt + + + + + + 177.8 + + + + asphalt + + + + + + 177.8 + + + + asphalt + + + + + + 177.3 + + + + asphalt + + + + + + 176.8 + + + + asphalt + + + + + + 176.5 + + + + asphalt + + + + + + 176.0 + + + + asphalt + + + + + + 176.0 + + + + asphalt + + + + + + 176.0 + + + + asphalt + + + + + + 176.3 + + + + asphalt + + + + + + 179.3 + + + + asphalt + + + + + + 180.0 + + + + asphalt + + + + + + 181.3 + + + + asphalt + + + + + + 181.5 + + + + asphalt + + + + + + 179.5 + + + + asphalt + + + + + + 178.0 + + + + asphalt + + + + + + 178.3 + + + + asphalt + + + + + + 178.3 + + + + asphalt + + + + + + 178.3 + + + + asphalt + + + + + + 177.3 + + + + asphalt + + + + + + 177.0 + + + + asphalt + + + + + + 177.0 + + + + asphalt + + + + + + 177.0 + + + + asphalt + + + + + + 177.0 + + + + asphalt + + + + + + 177.0 + + + + asphalt + + + + + + 177.0 + + + + asphalt + + + + + + 177.0 + + + + asphalt + + + + + + 178.3 + + + + asphalt + + + + + + 178.3 + + + + asphalt + + + + + + 178.3 + + + + concrete + + + + + + 177.8 + + + + concrete + + + + + + 179.5 + + + + concrete + + + + + + 181.8 + + + + concrete + + + + + + 182.3 + + + + asphalt + + + + + + 182.5 + + + + asphalt + + + + + + 183.5 + + + + asphalt + + + + + + 181.3 + + + + asphalt + + + + + + 180.5 + + + + asphalt + + + + + + 180.0 + + + + asphalt + + + + + + 179.5 + + + + sett + + + + + + 179.3 + + + + sett + + + + + + 182.3 + + + + sett + + + + + + 183.3 + + + + sett + + + + + + 184.5 + + + + sett + + + + + + 184.5 + + + + sett + + + + + + 184.3 + + + + sett + + + + + + 184.3 + + + + asphalt + + + + + + 184.3 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 183.8 + + + + asphalt + + + + + + 183.8 + + + + asphalt + + + + + + 183.8 + + + + asphalt + + + + + + 184.0 + + + + asphalt + + + + + + 184.3 + + + + asphalt + + + + + + 184.5 + + + + asphalt + + + + + + 184.8 + + + + asphalt + + + + + + 184.8 + + + + asphalt + + + + + + 184.5 + + + + asphalt + + + + + + 184.5 + + + + asphalt + + + + + + 184.5 + + + + asphalt + + + + + + 184.5 + + + + asphalt + + + + + + 184.5 + + + 183.8 + + + 183.5 + + + + paving_stones + + + + + + 183.5 + + + + paving_stones + + + + + + 183.3 + + + + asphalt + + + + + + 183.3 + + + + asphalt + + + + + + 185.3 + + + + sett + + + + + + 185.3 + + + + sett + + + + + + 185.3 + + + + sett + + + + + + 183.3 + + + + sett + + + + + + 183.8 + + + + asphalt + + + + + + 183.5 + + + + asphalt + + + + + + 183.5 + + + + asphalt + + + + + + 181.3 + + + + asphalt + + + + + + 180.5 + + + + paving_stones + + + + + + 180.5 + + + + paving_stones + + + + + + 182.0 + + + + paving_stones + + + + + + 182.8 + + + + paving_stones + + + + + + 183.0 + + + + paving_stones + + + + + + 180.3 + + + + paving_stones + + + + + + 177.5 + + + + paving_stones + + + + + + 175.8 + + + + paving_stones + + + + + + 173.3 + + + + paving_stones + + + + + + 173.0 + + + + paving_stones + + + + + + 173.0 + + + + paving_stones + + + + + + 172.8 + + + + paving_stones + + + + + + 172.5 + + + + paving_stones + + + + + + 173.0 + + + + asphalt + + + + + + 173.0 + + + 173.0 + + + 173.3 + + + 173.5 + + + 174.0 + + + 174.0 + + + 174.3 + + + 174.8 + + + 175.0 + + + + asphalt + + + + + + 176.5 + + + + asphalt + + + + + + 188.8 + + + + asphalt + + + + + + 185.8 + + + + asphalt + + + + + + 185.5 + + + + asphalt + + + + + + 181.5 + + + + asphalt + + + + + + 181.8 + + + + asphalt + + + + + + 182.3 + + + + asphalt + + + + + + 182.3 + + + + asphalt + + + + + + 182.5 + + + + asphalt + + + + + + 182.8 + + + + asphalt + + + + + + 182.3 + + + + asphalt + + + + + + 182.0 + + + + asphalt + + + + + + 182.3 + + + + asphalt + + + + + + 182.3 + + + + asphalt + + + + + + 182.3 + + + + asphalt + + + + + + 182.8 + + + + asphalt + + + + + + 183.0 + + + + asphalt + + + + + + 183.0 + + + + asphalt + + + + + + 183.0 + + + 183.3 + + + 183.3 + + + 183.3 + + + 183.3 + + + 183.3 + + + 183.3 + + + 183.3 + + + 183.3 + + + + \ No newline at end of file diff --git a/data/gpx_test_data/osm_track.gpx b/data/gpx_test_data/osm_track.gpx new file mode 100644 index 0000000000..99612a14b6 --- /dev/null +++ b/data/gpx_test_data/osm_track.gpx @@ -0,0 +1,2515 @@ + + + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 119.0 + + + + 119.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 124.0 + + + + 125.0 + + + + 125.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 130.0 + + + + 130.0 + + + + 130.0 + + + + 130.0 + + + + 130.0 + + + + 130.0 + + + + 130.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 131.0 + + + + 130.0 + + + + 130.0 + + + + 130.0 + + + + 130.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 129.0 + + + + 127.0 + + + + 127.0 + + + + 127.0 + + + + 127.0 + + + + 127.0 + + + + 127.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 129.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 128.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 126.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 125.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 124.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 123.0 + + + + 122.0 + + + + 122.0 + + + + 122.0 + + + + 122.0 + + + + 122.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 120.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 121.0 + + + + 120.0 + + + + 120.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 119.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 118.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 111.0 + + + + 111.0 + + + + 111.0 + + + + 111.0 + + + + 111.0 + + + + 111.0 + + + + 111.0 + + + + 111.0 + + + + 111.0 + + + + 110.0 + + + + 110.0 + + + + 109.0 + + + + 109.0 + + + + 108.0 + + + + 108.0 + + + + 108.0 + + + + 108.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 107.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 106.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 105.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 112.0 + + + + 114.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 117.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 117.0 + + + + 117.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 114.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 113.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 115.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 115.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 116.0 + + + + 115.0 + + + + 115.0 + + + + + diff --git a/data/gpx_test_data/points.gpx b/data/gpx_test_data/points.gpx new file mode 100644 index 0000000000..6443a36673 --- /dev/null +++ b/data/gpx_test_data/points.gpx @@ -0,0 +1,32 @@ + + + + new + + gpx.studio + + + + + 184.4 + Point 1 + Point 1 + Library + + + 186.9 + Point 2 + + Point 2 + + Letter A, Blue + + + 174.4 + Point 3 + + Point 3 + + Contact, Clown + + \ No newline at end of file diff --git a/data/gpx_test_data/route.gpx b/data/gpx_test_data/route.gpx new file mode 100644 index 0000000000..9751eb9a07 --- /dev/null +++ b/data/gpx_test_data/route.gpx @@ -0,0 +1,16 @@ + + + + Welcome to my route + + +Some random route + +184.4 +Point 1 + + +186.9 +Point 2 + + \ No newline at end of file diff --git a/data/gpx_test_data/tower_collector.gpx b/data/gpx_test_data/tower_collector.gpx new file mode 100644 index 0000000000..40b3e99b30 --- /dev/null +++ b/data/gpx_test_data/tower_collector.gpx @@ -0,0 +1,357 @@ + + + + Sessions taken between 2022-07-16T09:33:18Z and 2022-07-16T09:54:56Z + File generated by Tower Collector 2.14.1 on Fairphone FP2 + + + + + Sessions taken between 2022-07-16T09:33:18Z and 2022-07-16T09:54:56Z + + + 756 + + + + + 739 + + + + + 740 + + + + + 738 + + + + + 736 + + + + + 732 + + + + + 1.1 + 218.3 + + + + + 725 + + + + + 2.21 + 226.9 + + + + + 712 + + + + + 0.97 + 210.3 + + + + + 703 + + + + + 1.24 + 218.3 + + + + + 713 + + + + + 1.29 + 220.6 + + + + + 720 + + + + + 1.29 + 219.2 + + + + + 719 + + + + + 1.23 + 169.7 + + + + + 722 + + + + + 1.33 + 168.5 + + + + + 725 + + + + + 1.2 + 160.7 + + + + + 729 + + + + + 1.27 + 139.6 + + + + + 733 + + + + + 1.6 + 177.8 + + + + + 734 + + + + + 1.55 + 187.4 + + + + + 737 + + + + + 1.41 + 139.5 + + + + + 724 + + + + + 1.21 + 152.4 + + + + + 734 + + + + + 1.19 + 149.2 + + + + + 725 + + + + + 0.96 + 131.9 + + + + + 723 + + + + + 0.91 + 166.6 + + + + + 723 + + + + + 1.24 + 185.5 + + + + + 726 + + + + + 0.96 + 196.6 + + + + + 723 + + + + + 728 + + + + + 728 + + + + + 1.39 + 113.8 + + + + + 725 + + + + + 1.13 + 128.7 + + + + + 729 + + + + + 1.41 + 110.4 + + + + + 715 + + + + + 1.39 + 143.5 + + + + + 724 + + + + + 1.57 + 165.1 + + + + + 705 + + + + + 1.35 + 156.4 + + + + + 731 + + + + + 0.62 + 214.6 + + + + + 710 + + + + + 0.97 + 141.9 + + + + + 697 + + + + + 0.61 + 125.5 + + + + + + diff --git a/iphone/Maps/OMaps.plist b/iphone/Maps/OMaps.plist index ee0c5202cf..a42c210416 100644 --- a/iphone/Maps/OMaps.plist +++ b/iphone/Maps/OMaps.plist @@ -8,6 +8,18 @@ ${PRODUCT_NAME} CFBundleDocumentTypes + + CFBundleTypeIconFiles + + CFBundleTypeName + GPS Exchange Format (GPX) + LSHandlerRank + Default + LSItemContentTypes + + com.topografix.gpx + + CFBundleTypeIconFiles @@ -215,6 +227,30 @@ application/vnd.google-earth.kmz+xml + + UTTypeConformsTo + + public.xml + + UTTypeDescription + GPS Exchange Format (GPX) + UTTypeIdentifier + com.topografix.gpx + UTTypeReferenceURL + http://www.topografix.com/GPX/1/ + UTTypeTagSpecification + + public.filename-extension + + gpx + GPX + + public.mime-type + + application/gpx+xml + + + diff --git a/kml/CMakeLists.txt b/kml/CMakeLists.txt index 27ffc8a4bf..cab79c16b7 100644 --- a/kml/CMakeLists.txt +++ b/kml/CMakeLists.txt @@ -3,10 +3,13 @@ project(kml) set(SRC header_binary.hpp minzoom_quadtree.hpp + serdes_common.hpp serdes.cpp serdes.hpp serdes_binary.cpp serdes_binary.hpp + serdes_gpx.cpp + serdes_gpx.hpp type_utils.cpp type_utils.hpp types.cpp diff --git a/kml/kml_tests/CMakeLists.txt b/kml/kml_tests/CMakeLists.txt index 16a15f9af2..c74eb34367 100644 --- a/kml/kml_tests/CMakeLists.txt +++ b/kml/kml_tests/CMakeLists.txt @@ -1,9 +1,10 @@ project(kml_tests) set(SRC + gpx_tests.cpp + minzoom_quadtree_tests.cpp serdes_tests.cpp tests_data.hpp - minzoom_quadtree_tests.cpp ) omim_add_test(${PROJECT_NAME} ${SRC}) diff --git a/kml/kml_tests/gpx_tests.cpp b/kml/kml_tests/gpx_tests.cpp new file mode 100644 index 0000000000..8b0b53822a --- /dev/null +++ b/kml/kml_tests/gpx_tests.cpp @@ -0,0 +1,157 @@ +#include "testing/testing.hpp" +#include "map/bookmark_helpers.hpp" +#include "kml/serdes_gpx.hpp" +#include "coding/string_utf8_multilang.hpp" +#include "geometry/mercator.hpp" +#include "platform/platform.hpp" + +auto constexpr kDefaultCode = StringUtf8Multilang::kDefaultCode; + +kml::FileData loadGpxFromString(std::string const & content) { + kml::FileData dataFromText; + try + { + const char * input = content.c_str(); + kml::DeserializerGpx des(dataFromText); + MemReader const reader(input, strlen(input)); + des.Deserialize(reader); + return dataFromText; + } + catch (kml::DeserializerGpx::DeserializeException const & exc) + { + TEST(false, ("Exception raised", exc.what())); + } +} + +kml::FileData loadGpxFromFile(std::string const & file) { + auto const fileName = GetPlatform().TestsDataPathForFile(file); + std::string text; + FileReader(fileName).ReadAsString(text); + return loadGpxFromString(text); +} + +UNIT_TEST(Gpx_Test_Point) +{ + std::string const input = R"( + + + + Waypoint 1 + +)"; + + kml::FileData const dataFromText = loadGpxFromString(input); + + kml::FileData data; + kml::BookmarkData bookmarkData; + bookmarkData.m_name[kDefaultCode] = "Waypoint 1"; + bookmarkData.m_point = mercator::FromLatLon(42.81025, -1.65727); + bookmarkData.m_customName[kDefaultCode] = "Waypoint 1"; + bookmarkData.m_color = {kml::PredefinedColor::Red, 0}; + data.m_bookmarksData.emplace_back(std::move(bookmarkData)); + + TEST_EQUAL(dataFromText, data, ()); +} + + +UNIT_TEST(Gpx_Test_Route) +{ + std::string const input = R"( + + + new + Cycling + + + 130.5 + + + 0.0 + + + 0.0 + + + + +)"; + + kml::FileData const dataFromText = loadGpxFromString(input); + auto line = dataFromText.m_tracksData[0].m_geometry.m_lines[0]; + TEST_EQUAL(line.size(), 3, ()); + TEST_EQUAL(line[0], mercator::FromLatLon(54.23955053156179, 24.114990234375004), ()); +} + +UNIT_TEST(GoMap) +{ + kml::FileData const dataFromFile = loadGpxFromFile("gpx_test_data/go_map.gpx"); + auto line = dataFromFile.m_tracksData[0].m_geometry.m_lines[0]; + TEST_EQUAL(line.size(), 101, ()); +} + +UNIT_TEST(GpxStudio) +{ + kml::FileData const dataFromFile = loadGpxFromFile("gpx_test_data/gpx_studio.gpx"); + auto line = dataFromFile.m_tracksData[0].m_geometry.m_lines[0]; + TEST_EQUAL(line.size(), 328, ()); +} + +UNIT_TEST(OsmTrack) +{ + kml::FileData const dataFromFile = loadGpxFromFile("gpx_test_data/osm_track.gpx"); + auto line = dataFromFile.m_tracksData[0].m_geometry.m_lines[0]; + TEST_EQUAL(line.size(), 182, ()); +} + +UNIT_TEST(TowerCollector) +{ + kml::FileData const dataFromFile = loadGpxFromFile("gpx_test_data/tower_collector.gpx"); + auto line = dataFromFile.m_tracksData[0].m_geometry.m_lines[0]; + TEST_EQUAL(line.size(), 35, ()); +} + +UNIT_TEST(PointsOnly) +{ + kml::FileData const dataFromFile = loadGpxFromFile("gpx_test_data/points.gpx"); + auto bookmarks = dataFromFile.m_bookmarksData; + TEST_EQUAL(bookmarks.size(), 3, ()); + TEST_EQUAL("Point 1", bookmarks[0].m_name[kDefaultCode], ()); + TEST_EQUAL(bookmarks[0].m_point, mercator::FromLatLon(48.20984622935899, 16.376023292541507), ()); +} + +UNIT_TEST(Route) +{ + kml::FileData dataFromFile = loadGpxFromFile("gpx_test_data/route.gpx"); + auto line = dataFromFile.m_tracksData[0].m_geometry.m_lines[0]; + TEST_EQUAL(line.size(), 2, ()); + TEST_EQUAL(dataFromFile.m_categoryData.m_name[kDefaultCode], "Some random route", ()); + TEST_EQUAL(line[0], mercator::FromLatLon(48.20984622935899, 16.376023292541507), ()); + TEST_EQUAL(line[1], mercator::FromLatLon(48.209503040543545, 16.381065845489506), ()); +} + +UNIT_TEST(Color) +{ + kml::FileData const dataFromFile = loadGpxFromFile("gpx_test_data/color.gpx"); + TEST_EQUAL(4278190335, dataFromFile.m_tracksData[0].m_layers[0].m_color.m_rgba, ()); + TEST_EQUAL(65535, dataFromFile.m_tracksData[1].m_layers[0].m_color.m_rgba, ()); +} + +UNIT_TEST(MultiTrackNames) +{ + kml::FileData dataFromFile = loadGpxFromFile("gpx_test_data/color.gpx"); + TEST_EQUAL("new", dataFromFile.m_categoryData.m_name[kml::kDefaultLang], ()); + TEST_EQUAL("Short description", dataFromFile.m_categoryData.m_description[kml::kDefaultLang], ()); + TEST_EQUAL("new red", dataFromFile.m_tracksData[0].m_name[kml::kDefaultLang], ()); + TEST_EQUAL("description 1", dataFromFile.m_tracksData[0].m_description[kml::kDefaultLang], ()); + TEST_EQUAL("new blue", dataFromFile.m_tracksData[1].m_name[kml::kDefaultLang], ()); + TEST_EQUAL("description 2", dataFromFile.m_tracksData[1].m_description[kml::kDefaultLang], ()); +} + +UNIT_TEST(Empty) +{ + kml::FileData dataFromFile = loadGpxFromFile("gpx_test_data/empty.gpx"); + TEST_EQUAL("new", dataFromFile.m_categoryData.m_name[kml::kDefaultLang], ()); + TEST_EQUAL(0, dataFromFile.m_tracksData.size(), ()); +} + + diff --git a/kml/serdes.cpp b/kml/serdes.cpp index 370920d338..fc217ca40a 100644 --- a/kml/serdes.cpp +++ b/kml/serdes.cpp @@ -52,11 +52,6 @@ std::string const kExtendedDataFooter = std::string const kCompilationFooter = "\n"; -auto const kDefaultLang = StringUtf8Multilang::kDefaultCode; - -auto const kDefaultTrackWidth = 5.0; -auto const kDefaultTrackColor = 0x006ec7ff; - std::string Indent(size_t count) { return std::string(count, ' '); @@ -173,13 +168,6 @@ BookmarkIcon GetIcon(std::string const & iconName) return BookmarkIcon::None; } -template -uint32_t ToRGBA(Channel red, Channel green, Channel blue, Channel alpha) -{ - return static_cast(red) << 24 | static_cast(green) << 16 | - static_cast(blue) << 8 | static_cast(alpha); -} - void SaveStringWithCDATA(KmlWriter::WriterWrapper & writer, std::string s) { if (s.empty()) diff --git a/kml/serdes.hpp b/kml/serdes.hpp index 0c8b7f2aea..e96b79be38 100644 --- a/kml/serdes.hpp +++ b/kml/serdes.hpp @@ -1,6 +1,7 @@ #pragma once #include "kml/types.hpp" +#include "kml/serdes_common.hpp" #include "coding/parse_xml.hpp" #include "coding/reader.hpp" diff --git a/kml/serdes_common.hpp b/kml/serdes_common.hpp new file mode 100644 index 0000000000..a93082e74e --- /dev/null +++ b/kml/serdes_common.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "coding/string_utf8_multilang.hpp" + +namespace kml +{ +auto constexpr kDefaultLang = StringUtf8Multilang::kDefaultCode; +auto constexpr kDefaultTrackWidth = 5.0; +auto constexpr kDefaultTrackColor = 0x006ec7ff; + +template +uint32_t ToRGBA(Channel red, Channel green, Channel blue, Channel alpha) +{ + return static_cast(red) << 24 | static_cast(green) << 16 | + static_cast(blue) << 8 | static_cast(alpha); +} + +} // namespace kml diff --git a/kml/serdes_gpx.cpp b/kml/serdes_gpx.cpp new file mode 100644 index 0000000000..3dd5c25c51 --- /dev/null +++ b/kml/serdes_gpx.cpp @@ -0,0 +1,251 @@ +#include "kml/serdes_gpx.hpp" + +#include "coding/hex.hpp" +#include "coding/point_coding.hpp" +#include "coding/string_utf8_multilang.hpp" + +#include "geometry/mercator.hpp" + +#include "base/assert.hpp" +#include "base/string_utils.hpp" + +#include + + +namespace kml +{ +namespace gpx +{ + +using namespace std::string_view_literals; + +std::string_view constexpr kTrk = "trk"; +std::string_view constexpr kTrkSeg = "trkseg"; +std::string_view constexpr kRte = "rte"; +std::string_view constexpr kTrkPt = "trkpt"; +std::string_view constexpr kWpt = "wpt"; +std::string_view constexpr kRtePt = "rtept"; +std::string_view constexpr kName = "name"; +std::string_view constexpr kColor = "color"; +std::string_view constexpr kDesc = "desc"; +std::string_view constexpr kMetadata = "metadata"; + + +std::string PointToString(m2::PointD const & org) +{ + double const lon = mercator::XToLon(org.x); + double const lat = mercator::YToLat(org.y); + + std::ostringstream ss; + ss.precision(8); + + ss << lon << "," << lat; + return ss.str(); +} + +GpxParser::GpxParser(FileData & data) +: m_data(data) +, m_categoryData(&m_data.m_categoryData) +{ + ResetPoint(); +} + +void GpxParser::ResetPoint() +{ + m_name.clear(); + m_description.clear(); + m_org = {}; + m_predefinedColor = PredefinedColor::None; + m_color = 0; + m_customName.clear(); + m_trackLayers.clear(); + m_geometry.Clear(); + m_geometryType = GEOMETRY_TYPE_UNKNOWN; +} + +bool GpxParser::MakeValid() +{ + if (GEOMETRY_TYPE_POINT == m_geometryType) + { + if (mercator::ValidX(m_org.x) && mercator::ValidY(m_org.y)) + { + // Set default name. + if (m_name.empty()) + m_name[kml::kDefaultLang] = gpx::PointToString(m_org); + + // Set default pin. + if (m_predefinedColor == PredefinedColor::None) + m_predefinedColor = PredefinedColor::Red; + + return true; + } + return false; + } + else if (GEOMETRY_TYPE_LINE == m_geometryType) + { + return m_geometry.IsValid(); + } + + return false; +} + +bool GpxParser::Push(std::string_view tag) +{ + m_tags.push_back(tag); + if (GetTagFromEnd(0) == gpx::kWpt) + m_geometryType = GEOMETRY_TYPE_POINT; + else if (GetTagFromEnd(0) == gpx::kTrkPt || GetTagFromEnd(0) == gpx::kRtePt) + m_geometryType = GEOMETRY_TYPE_LINE; + return true; +} + +void GpxParser::AddAttr(std::string const & attr, std::string const & value) +{ + std::string attrInLowerCase = attr; + strings::AsciiToLower(attrInLowerCase); + + if (GetTagFromEnd(0) == gpx::kWpt) + { + if (attr == "lat") + m_lat = stod(value); + else if (attr == "lon") + m_lon = stod(value); + } + else if ((GetTagFromEnd(0) == gpx::kTrkPt && GetTagFromEnd(1) == gpx::kTrkSeg) || + (GetTagFromEnd(0) == gpx::kRtePt && GetTagFromEnd(1) == gpx::kRte)) + { + if (attr == "lat") + m_lat = stod(value); + else if (attr == "lon") + m_lon = stod(value); + } + +} + +std::string_view GpxParser::GetTagFromEnd(size_t n) const +{ + ASSERT_LESS(n, m_tags.size(), ()); + return m_tags[m_tags.size() - n - 1]; +} + +void GpxParser::ParseColor(std::string const & value) +{ + auto const colorBytes = FromHex(value); + if (colorBytes.size() != 3) + { + LOG(LWARNING, ("Invalid color value", value)); + return; + } + m_color = kml::ToRGBA(colorBytes[0], colorBytes[1], colorBytes[2], (char)255); +} + +void GpxParser::Pop(std::string_view tag) +{ + ASSERT_EQUAL(m_tags.back(), tag, ()); + + if (tag == gpx::kTrkPt || tag == gpx::kRtePt) + { + m2::Point p = mercator::FromLatLon(m_lat, m_lon); + if (m_line.empty() || !AlmostEqualAbs(m_line.back().GetPoint(), p, kMwmPointAccuracy)) + m_line.emplace_back(std::move(p)); + } + else if (tag == gpx::kTrkSeg || tag == gpx::kRte) + { + m_geometry.m_lines.push_back(std::move(m_line)); + } + else if (tag == gpx::kWpt) + { + m_org = mercator::FromLatLon(m_lat, m_lon); + } + + if (tag == gpx::kRte || tag == gpx::kTrkSeg || tag == gpx::kWpt) + { + if (MakeValid()) + { + if (GEOMETRY_TYPE_POINT == m_geometryType) + { + BookmarkData data; + data.m_name = std::move(m_name); + data.m_description = std::move(m_description); + data.m_color.m_predefinedColor = m_predefinedColor; + data.m_color.m_rgba = m_color; + data.m_point = m_org; + data.m_customName = std::move(m_customName); + // Here we set custom name from 'name' field for KML-files exported from 3rd-party services. + if (data.m_name.size() == 1 && data.m_name.begin()->first == kDefaultLangCode && data.m_customName.empty()) + data.m_customName = data.m_name; + + m_data.m_bookmarksData.push_back(std::move(data)); + } + else if (GEOMETRY_TYPE_LINE == m_geometryType) + { + TrackLayer layer; + layer.m_lineWidth = kml::kDefaultTrackWidth; + layer.m_color.m_rgba = (m_color != 0 ? m_color : kml::kDefaultTrackColor); + m_trackLayers.push_back(std::move(layer)); + + TrackData data; + data.m_name = std::move(m_name); + data.m_description = std::move(m_description); + data.m_layers = std::move(m_trackLayers); + data.m_geometry = std::move(m_geometry); + m_data.m_tracksData.push_back(std::move(data)); + } + } + ResetPoint(); + } + m_tags.pop_back(); +} + +void GpxParser::CharData(std::string value) +{ + strings::Trim(value); + + size_t const count = m_tags.size(); + if (count > 1 && !value.empty()) + { + std::string_view const & currTag = m_tags[count - 1]; + std::string_view const & prevTag = m_tags[count - 2]; + + if (prevTag == gpx::kWpt) + { + if (currTag == gpx::kName) + m_name[kml::kDefaultLang] = value; + else if (currTag == gpx::kDesc) + m_description[kml::kDefaultLang] = value; + } + else if (prevTag == gpx::kTrk || prevTag == gpx::kRte) + { + if (currTag == gpx::kName) + { + m_name[kml::kDefaultLang] = value; + if (m_categoryData->m_name[kml::kDefaultLang].empty()) + m_categoryData->m_name[kml::kDefaultLang] = value; + } + else if (currTag == gpx::kDesc) + { + m_description[kml::kDefaultLang] = value; + if (m_categoryData->m_description[kml::kDefaultLang].empty()) + m_categoryData->m_description[kml::kDefaultLang] = value; + } + } + else if (prevTag == gpx::kMetadata) + { + if (currTag == gpx::kName) + m_categoryData->m_name[kml::kDefaultLang] = value; + else if (currTag == gpx::kDesc) + m_categoryData->m_description[kml::kDefaultLang] = value; + } + if (currTag == gpx::kColor) + ParseColor(value); + } +} +} // namespace gpx + +DeserializerGpx::DeserializerGpx(FileData & fileData) +: m_fileData(fileData) +{ + m_fileData = {}; +} + +} // namespace kml diff --git a/kml/serdes_gpx.hpp b/kml/serdes_gpx.hpp new file mode 100644 index 0000000000..9c780b4d9c --- /dev/null +++ b/kml/serdes_gpx.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include "kml/types.hpp" +#include "kml/serdes_common.hpp" + +#include "coding/parse_xml.hpp" +#include "coding/reader.hpp" +#include "coding/writer.hpp" + +#include "geometry/point2d.hpp" +#include "geometry/point_with_altitude.hpp" + +#include "base/exception.hpp" +#include "base/stl_helpers.hpp" + +#include +#include + +namespace kml +{ +namespace gpx +{ +class GpxParser +{ +public: + explicit GpxParser(FileData & data); + bool Push(std::string_view name); + void AddAttr(std::string const & attr, std::string const & value); + std::string_view GetTagFromEnd(size_t n) const; + void Pop(std::string_view tag); + void CharData(std::string value); + +private: + enum GeometryType + { + GEOMETRY_TYPE_UNKNOWN, + GEOMETRY_TYPE_POINT, + GEOMETRY_TYPE_LINE + }; + + void ResetPoint(); + bool MakeValid(); + void ParseColor(std::string const & value); + + FileData & m_data; + CategoryData m_compilationData; + CategoryData * m_categoryData; // never null + + std::vector m_tags; + GeometryType m_geometryType; + MultiGeometry m_geometry; + uint32_t m_color; + + LocalizableString m_name; + LocalizableString m_description; + PredefinedColor m_predefinedColor; + m2::PointD m_org; + + double m_lat; + double m_lon; + + MultiGeometry::LineT m_line; + LocalizableString m_customName; + std::vector m_trackLayers; +}; +} // namespace gpx + +class DeserializerGpx +{ +public: + DECLARE_EXCEPTION(DeserializeException, RootException); + + explicit DeserializerGpx(FileData & fileData); + + template + void Deserialize(ReaderType const & reader) + { + NonOwningReaderSource src(reader); + + gpx::GpxParser parser(m_fileData); + if (!ParseXML(src, parser, true)) + { + // Print corrupted GPX file for debug and restore purposes. + std::string gpxText; + reader.ReadAsString(gpxText); + if (!gpxText.empty() && gpxText[0] == '<') + LOG(LWARNING, (gpxText)); + MYTHROW(DeserializeException, ("Could not parse GPX.")); + } + } + +private: + FileData & m_fileData; +}; +} // namespace kml diff --git a/map/bookmark_helpers.cpp b/map/bookmark_helpers.cpp index 1468f094c9..15c9512a71 100644 --- a/map/bookmark_helpers.cpp +++ b/map/bookmark_helpers.cpp @@ -4,6 +4,7 @@ #include "kml/serdes.hpp" #include "kml/serdes_binary.hpp" +#include "kml/serdes_gpx.hpp" #include "indexer/classificator.hpp" #include "indexer/feature_data.hpp" @@ -269,14 +270,58 @@ std::string GenerateValidAndUniqueFilePathForKML(std::string const & fileName) std::string const kKmzExtension = ".kmz"; std::string const kKmlExtension = ".kml"; std::string const kKmbExtension = ".kmb"; +std::string const kGpxExtension = ".gpx"; std::string const kDefaultBookmarksFileName = "Bookmarks"; +// Populate empty category & track names based on file name: assign file name to category name, +// if there is only one unnamed track - assign file name to it, otherwise add numbers 1, 2, 3... +// to file name to build names for all unnamed tracks +void FillEmptyNames(std::unique_ptr & kmlData, std::string const & file) +{ + auto start = file.find_last_of('/') + 1; + auto end = file.find_last_of('.'); + if (end == std::string::npos) + end = file.size(); + auto const name = file.substr(start, end - start); + + if (kmlData->m_categoryData.m_name.empty()) + kmlData->m_categoryData.m_name[kml::kDefaultLang] = name; + + if (kmlData->m_tracksData.empty()) + return; + + auto const emptyNames = std::count_if(kmlData->m_tracksData.begin(), kmlData->m_tracksData.end(), + [](const kml::TrackData & t) { return t.m_name.empty(); }); + if (emptyNames == 0) + return; + + auto emptyTrackNum = 1; + for (auto & track : kmlData->m_tracksData) + { + if (track.m_name.empty()) + { + if (emptyNames == 1) + { + track.m_name[kml::kDefaultLang] = name; + return; + } + else + { + track.m_name[kml::kDefaultLang] = name + " " + std::to_string(emptyTrackNum); + emptyTrackNum++; + } + } + } +} + std::unique_ptr LoadKmlFile(std::string const & file, KmlFileType fileType) { std::unique_ptr kmlData; try { kmlData = LoadKmlData(FileReader(file), fileType); + if (kmlData != nullptr) + FillEmptyNames(kmlData, file); } catch (std::exception const & e) { @@ -292,7 +337,7 @@ std::string GetKMLPath(std::string const & filePath) { std::string const fileExt = GetFileExt(filePath); std::string fileSavePath; - if (fileExt == kKmlExtension) + if (fileExt == kKmlExtension || fileExt == kGpxExtension) { fileSavePath = GenerateValidAndUniqueFilePathForKML(base::FileNameFromFullPath(filePath)); if (!base::CopyFileX(filePath, fileSavePath)) @@ -360,6 +405,11 @@ std::unique_ptr LoadKmlData(Reader const & reader, KmlFileType fi kml::DeserializerKml des(*data); des.Deserialize(reader); } + else if (fileType == KmlFileType::Gpx) + { + kml::DeserializerGpx des(*data); + des.Deserialize(reader); + } else { CHECK(false, ("Not supported KmlFileType")); diff --git a/map/bookmark_helpers.hpp b/map/bookmark_helpers.hpp index 59820cc6a6..e539988b95 100644 --- a/map/bookmark_helpers.hpp +++ b/map/bookmark_helpers.hpp @@ -68,12 +68,14 @@ enum class BookmarkBaseType : uint16_t extern std::string const kKmzExtension; extern std::string const kKmlExtension; extern std::string const kKmbExtension; +extern std::string const kGpxExtension; extern std::string const kDefaultBookmarksFileName; enum class KmlFileType { Text, - Binary + Binary, + Gpx }; inline std::string DebugPrint(KmlFileType fileType) @@ -82,6 +84,7 @@ inline std::string DebugPrint(KmlFileType fileType) { case KmlFileType::Text: return "Text"; case KmlFileType::Binary: return "Binary"; + case KmlFileType::Gpx: return "GPX"; } UNREACHABLE(); } diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index 81baba3ca9..34ecb9cb0c 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -1871,7 +1871,17 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT std::string fileSavePath = GetKMLPath(filePath); if (!fileSavePath.empty()) { - auto kmlData = LoadKmlFile(fileSavePath, KmlFileType::Text); + auto const ext = base::GetFileExtension(filePath); + std::unique_ptr kmlData; + if (ext == ".kml" || ext == ".kmz") + kmlData = LoadKmlFile(fileSavePath, KmlFileType::Text); + else if (ext == ".gpx") + kmlData = LoadKmlFile(fileSavePath, KmlFileType::Gpx); + else if (ext == ".kmb") + kmlData = LoadKmlFile(fileSavePath, KmlFileType::Binary); + else + ASSERT(false, ("Unsupported bookmarks extension", ext)); + if (m_needTeardown) return; diff --git a/map/map_tests/bookmarks_test.cpp b/map/map_tests/bookmarks_test.cpp index 96dcb1c28e..1c687b7a8c 100644 --- a/map/map_tests/bookmarks_test.cpp +++ b/map/map_tests/bookmarks_test.cpp @@ -1121,6 +1121,23 @@ UNIT_CLASS_TEST(Runner, TrackParsingTest_1) } } +UNIT_CLASS_TEST(Runner, FillEmptyTrackNames) +{ + BookmarkManager bmManager(BM_CALLBACKS); + bmManager.EnableTestMode(true); + + string const kmlFile1 = GetPlatform().TestsDataPathForFile("gpx_test_data/empty_names1.gpx"); + auto fileData1 = LoadKmlFile(kmlFile1, KmlFileType::Gpx); + TEST_EQUAL(fileData1->m_categoryData.m_name[kml::kDefaultLangCode], "empty_names1", ()); + TEST_EQUAL(fileData1->m_tracksData[0].m_name[kml::kDefaultLangCode], "empty_names1 1", ()); + TEST_EQUAL(fileData1->m_tracksData[1].m_name[kml::kDefaultLangCode], "empty_names1 2", ()); + + string const kmlFile2 = GetPlatform().TestsDataPathForFile("gpx_test_data/empty_names2.gpx"); + auto fileData2 = LoadKmlFile(kmlFile2, KmlFileType::Gpx); + TEST_EQUAL(fileData2->m_categoryData.m_name[kml::kDefaultLangCode], "empty_names2", ()); + TEST_EQUAL(fileData2->m_tracksData[0].m_name[kml::kDefaultLangCode], "empty_names2", ()); +} + UNIT_CLASS_TEST(Runner, TrackParsingTest_2) { string const kmlFile = GetPlatform().TestsDataPathForFile("kml_test_data/track-from-google-earth.kml"); diff --git a/xcode/kml/kml.xcodeproj/project.pbxproj b/xcode/kml/kml.xcodeproj/project.pbxproj index 35510560eb..6424248b62 100644 --- a/xcode/kml/kml.xcodeproj/project.pbxproj +++ b/xcode/kml/kml.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 45E4560120584DF200D9F45E /* serdes_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45E455FF20584DEF00D9F45E /* serdes_tests.cpp */; }; 45E4560320584E1C00D9F45E /* libkml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4557D205849A600D9F45E /* libkml.a */; }; 45E456142058509200D9F45E /* testingmain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45E456122058508C00D9F45E /* testingmain.cpp */; }; + 464344F3294F952700984CB7 /* gpx_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 464344F2294F952700984CB7 /* gpx_tests.cpp */; }; + 46AA9E60294549B000ECED73 /* serdes_gpx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 464BD0FB294546B20011955A /* serdes_gpx.cpp */; }; E2AA225E25275C6B002589E2 /* minzoom_quadtree_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E2AA225C25275C6B002589E2 /* minzoom_quadtree_tests.cpp */; }; E2DC9C9125264E3E0098174E /* types.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E2DC9C9025264E3E0098174E /* types.cpp */; }; FA67C84B26BB365600B33DCA /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA67C84A26BB365600B33DCA /* libplatform.a */; }; @@ -55,6 +57,9 @@ 45E455ED20584DCB00D9F45E /* kml_tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = kml_tests.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45E455FF20584DEF00D9F45E /* serdes_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serdes_tests.cpp; sourceTree = ""; }; 45E456122058508C00D9F45E /* testingmain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testingmain.cpp; path = ../../testing/testingmain.cpp; sourceTree = ""; }; + 464344F2294F952700984CB7 /* gpx_tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = gpx_tests.cpp; sourceTree = ""; }; + 464BD0FB294546B20011955A /* serdes_gpx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; path = serdes_gpx.cpp; sourceTree = ""; }; + 464BD0FC294546B20011955A /* serdes_gpx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = serdes_gpx.hpp; sourceTree = ""; }; E2AA225925275C1D002589E2 /* minzoom_quadtree.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = minzoom_quadtree.hpp; sourceTree = ""; }; E2AA225C25275C6B002589E2 /* minzoom_quadtree_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minzoom_quadtree_tests.cpp; sourceTree = ""; }; E2AA225D25275C6B002589E2 /* tests_data.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tests_data.hpp; sourceTree = ""; }; @@ -145,6 +150,8 @@ E2DC9C9025264E3E0098174E /* types.cpp */, 45E4559420584ABA00D9F45E /* types.hpp */, 45E4559120584ABA00D9F45E /* visitors.hpp */, + 464BD0FB294546B20011955A /* serdes_gpx.cpp */, + 464BD0FC294546B20011955A /* serdes_gpx.hpp */, ); name = kml; path = ../../kml; @@ -157,6 +164,7 @@ 45E455FF20584DEF00D9F45E /* serdes_tests.cpp */, 45E456122058508C00D9F45E /* testingmain.cpp */, E2AA225D25275C6B002589E2 /* tests_data.hpp */, + 464344F2294F952700984CB7 /* gpx_tests.cpp */, ); name = kml_tests; path = ../../kml/kml_tests; @@ -269,7 +277,9 @@ buildActionMask = 2147483647; files = ( 45E4559620584ABA00D9F45E /* serdes_binary.cpp in Sources */, + 464344F3294F952700984CB7 /* gpx_tests.cpp in Sources */, 4568C86420BD455700E2192B /* type_utils.cpp in Sources */, + 46AA9E60294549B000ECED73 /* serdes_gpx.cpp in Sources */, 45E4559520584ABA00D9F45E /* serdes.cpp in Sources */, E2DC9C9125264E3E0098174E /* types.cpp in Sources */, );