mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-20 13:23:59 +00:00
[routing] Add possibility to save routes as tracks
Signed-off-by: cyber-toad <the.cyber.toad@proton.me>
This commit is contained in:
committed by
Konstantin Pastbin
parent
9e8accc8f5
commit
45bba5fb5e
@@ -1143,6 +1143,12 @@ Java_app_organicmaps_Framework_nativeShowTrackRect(JNIEnv * env, jclass, jlong t
|
||||
frm()->ShowTrack(static_cast<kml::TrackId>(track));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_Framework_nativeSaveRoute(JNIEnv *, jclass)
|
||||
{
|
||||
frm()->SaveRoute();
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_app_organicmaps_Framework_nativeGetBookmarkDir(JNIEnv * env, jclass)
|
||||
{
|
||||
|
||||
@@ -356,4 +356,6 @@ public class Framework
|
||||
public static native void nativeDidCloseProductsPopup(String reason);
|
||||
|
||||
public static native void nativeDidSelectProduct(String title, String link);
|
||||
|
||||
public static native void nativeSaveRoute();
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
||||
|
||||
@NonNull
|
||||
static RoutingBottomMenuController newInstance(@NonNull Activity activity, @NonNull View frame,
|
||||
@Nullable RoutingBottomMenuListener listener)
|
||||
@NonNull RoutingBottomMenuListener listener)
|
||||
{
|
||||
View altitudeChartFrame = getViewById(activity, frame, R.id.altitude_chart_panel);
|
||||
View timeElevationLine = getViewById(activity, frame, R.id.time_elevation_line);
|
||||
@@ -158,6 +158,9 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
||||
res.getDimensionPixelSize(R.dimen.margin_half));
|
||||
Button manageRouteButton = altitudeChartFrame.findViewById(R.id.btn__manage_route);
|
||||
manageRouteButton.setOnClickListener(this);
|
||||
|
||||
Button saveButton = altitudeChartFrame.findViewById(R.id.btn__save);
|
||||
saveButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
void showAltitudeChartAndRoutingDetails()
|
||||
@@ -168,6 +171,9 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
||||
showRouteAltitudeChart();
|
||||
showRoutingDetails();
|
||||
UiUtils.show(mAltitudeChartFrame);
|
||||
Button saveButton = mAltitudeChartFrame.findViewById(R.id.btn__save);
|
||||
saveButton.setText(R.string.save);
|
||||
saveButton.setEnabled(true);
|
||||
}
|
||||
|
||||
void hideAltitudeChartAndRoutingDetails()
|
||||
@@ -484,14 +490,21 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
||||
public void onClick(View v)
|
||||
{
|
||||
final int id = v.getId();
|
||||
if (id == R.id.btn__my_position_use && mListener != null)
|
||||
if (id == R.id.btn__my_position_use)
|
||||
mListener.onUseMyPositionAsStart();
|
||||
else if (id == R.id.btn__search_point && mListener != null)
|
||||
else if (id == R.id.btn__search_point)
|
||||
{
|
||||
final RouteMarkType pointType = (RouteMarkType) mActionMessage.getTag();
|
||||
mListener.onSearchRoutePoint(pointType);
|
||||
}
|
||||
else if (id == R.id.btn__manage_route && mListener != null)
|
||||
else if (id == R.id.btn__manage_route)
|
||||
mListener.onManageRouteOpen();
|
||||
else if (id == R.id.btn__save)
|
||||
{
|
||||
Framework.nativeSaveRoute();
|
||||
Button saveButton = v.findViewById(R.id.btn__save);
|
||||
saveButton.setEnabled(false);
|
||||
saveButton.setText(R.string.saved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class RoutingPlanController extends ToolbarController
|
||||
RoutingPlanController(View root, Activity activity,
|
||||
ActivityResultLauncher<Intent> startDrivingOptionsForResult,
|
||||
@NonNull RoutingPlanInplaceController.RoutingPlanListener routingPlanListener,
|
||||
@Nullable RoutingBottomMenuListener listener)
|
||||
@NonNull RoutingBottomMenuListener listener)
|
||||
{
|
||||
super(root, activity);
|
||||
mFrame = root;
|
||||
|
||||
@@ -73,6 +73,17 @@
|
||||
android:drawableStart="@drawable/ic_manage_route"
|
||||
android:drawablePadding="6dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn__save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/margin_base"
|
||||
android:minHeight="@dimen/primary_button_min_height"
|
||||
style="@style/MwmWidget.Button"
|
||||
android:text="@string/save"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/start"
|
||||
style="@style/MwmWidget.Button.Primary"
|
||||
@@ -80,7 +91,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/margin_base"
|
||||
android:layout_marginStart="@dimen/margin_double"
|
||||
android:minWidth="@dimen/start_button_width"
|
||||
android:text="@string/p2p_start"
|
||||
tools:showIn="@layout/menu_route_plan_line" />
|
||||
|
||||
@@ -92,6 +92,15 @@
|
||||
android:drawableStart="@drawable/ic_manage_route"
|
||||
android:drawablePadding="6dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn__save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_base"
|
||||
style="@style/MwmWidget.Button"
|
||||
android:minHeight="@dimen/primary_button_min_height"
|
||||
android:text="@string/save"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/start"
|
||||
style="@style/MwmWidget.Button.Primary"
|
||||
@@ -99,7 +108,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/margin_base"
|
||||
android:layout_marginStart="@dimen/margin_double"
|
||||
android:minWidth="@dimen/start_button_width"
|
||||
android:text="@string/p2p_start"
|
||||
tools:showIn="@layout/menu_route_plan_line" />
|
||||
|
||||
@@ -83,13 +83,28 @@
|
||||
android:layout_gravity="center_vertical" />
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn__manage_route"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/MwmWidget.Button"
|
||||
android:text="@string/planning_route_manage_route"
|
||||
android:drawableStart="@drawable/ic_manage_route"
|
||||
android:drawablePadding="6dp"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical" >
|
||||
<Button
|
||||
android:id="@+id/btn__manage_route"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/MwmWidget.Button"
|
||||
android:text="@string/planning_route_manage_route"
|
||||
android:drawableStart="@drawable/ic_manage_route"
|
||||
android:drawablePadding="6dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn__save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/MwmWidget.Button"
|
||||
android:layout_marginStart="@dimen/margin_base"
|
||||
android:minHeight="@dimen/primary_button_min_height"
|
||||
android:text="@string/save"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -655,6 +655,7 @@
|
||||
<string name="button_plan">Plan</string>
|
||||
<string name="placepage_remove_stop">Remove Stop</string>
|
||||
<string name="placepage_add_stop">Add Stop</string>
|
||||
<string name="saved">Saved</string>
|
||||
<!-- Alert to ask user relogin to OpenStreetMap with OAuth2 flow after OAuth1 authentication is deprecated. -->
|
||||
<string name="alert_reauth_message">Please login to OpenStreetMap to automatically upload all your map edits. Learn more <a href="https://github.com/organicmaps/organicmaps/issues/6144">here</a>.</string>
|
||||
<string name="dialog_error_storage_title">Storage access problem</string>
|
||||
|
||||
@@ -1170,6 +1170,51 @@ dp::Color BookmarkManager::GenerateTrackRecordingColor() const
|
||||
return kml::ColorFromPredefinedColor(kml::GetRandomPredefinedColor());
|
||||
}
|
||||
|
||||
std::string BookmarkManager::GenerateSavedRouteName(std::string const & from, std::string const & to)
|
||||
{
|
||||
if (!from.empty() && !to.empty())
|
||||
return from + " - " + to;
|
||||
if (!from.empty())
|
||||
return from;
|
||||
if (!to.empty())
|
||||
return to;
|
||||
return GenerateTrackRecordingName();
|
||||
}
|
||||
|
||||
kml::TrackId BookmarkManager::SaveRoute(std::vector<m2::PointD> const & points, std::string const & from, std::string const & to)
|
||||
{
|
||||
kml::MultiGeometry geometry;
|
||||
geometry.m_lines.emplace_back();
|
||||
geometry.m_timestamps.emplace_back();
|
||||
auto & line = geometry.m_lines.back();
|
||||
|
||||
for (auto const & pt : points)
|
||||
line.emplace_back(pt);
|
||||
|
||||
kml::TrackData trackData;
|
||||
trackData.m_geometry = std::move(geometry);
|
||||
|
||||
auto trackName = GenerateSavedRouteName(from, to);
|
||||
kml::SetDefaultStr(trackData.m_name, trackName);
|
||||
|
||||
kml::ColorData colorData;
|
||||
colorData.m_rgba = GenerateTrackRecordingColor().GetRGBA();
|
||||
kml::TrackLayer layer;
|
||||
layer.m_color = colorData;
|
||||
std::vector<kml::TrackLayer> m_layers;
|
||||
m_layers.emplace_back(layer);
|
||||
trackData.m_layers = std::move(m_layers);
|
||||
|
||||
trackData.m_timestamp = kml::TimestampClock::now();
|
||||
|
||||
auto editSession = GetEditSession();
|
||||
auto const track = editSession.CreateTrack(std::move(trackData));
|
||||
auto const groupId = LastEditedBMCategory();
|
||||
auto const trackId = track->GetId();
|
||||
AttachTrack(trackId, groupId);
|
||||
return trackId;
|
||||
}
|
||||
|
||||
void BookmarkManager::PrepareBookmarksAddresses(std::vector<SortBookmarkData> & bookmarksForSort,
|
||||
AddressesCollection & newAddresses)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace storage
|
||||
{
|
||||
class CountryInfoGetter;
|
||||
@@ -436,6 +435,8 @@ public:
|
||||
std::string GenerateTrackRecordingName() const;
|
||||
dp::Color GenerateTrackRecordingColor() const;
|
||||
|
||||
kml::TrackId SaveRoute(std::vector<m2::PointD> const & points, std::string const & from, std::string const & to);
|
||||
|
||||
private:
|
||||
class MarksChangesTracker : public df::UserMarksProvider
|
||||
{
|
||||
@@ -624,6 +625,7 @@ private:
|
||||
void CleanupInvalidMetadata();
|
||||
std::string GetMetadataEntryName(kml::MarkGroupId groupId) const;
|
||||
|
||||
std::string GenerateSavedRouteName(std::string const & from, std::string const & to);
|
||||
void NotifyAboutStartAsyncLoading();
|
||||
void NotifyAboutFinishAsyncLoading(KMLDataCollectionPtr && collection);
|
||||
void NotifyAboutFile(bool success, std::string const & filePath, bool isTemporaryFile);
|
||||
|
||||
@@ -1773,6 +1773,11 @@ bool Framework::IsTrackRecordingEnabled() const
|
||||
return GpsTracker::Instance().IsEnabled();
|
||||
}
|
||||
|
||||
void Framework::SaveRoute()
|
||||
{
|
||||
m_routingManager.SaveRoute();
|
||||
}
|
||||
|
||||
void Framework::OnUpdateGpsTrackPointsCallback(vector<pair<size_t, location::GpsInfo>> && toAdd,
|
||||
pair<size_t, size_t> const & toRemove,
|
||||
TrackStatistics const & trackStatistics)
|
||||
|
||||
@@ -444,6 +444,8 @@ public:
|
||||
void SaveTrackRecordingWithName(std::string const & name);
|
||||
bool IsTrackRecordingEmpty() const;
|
||||
bool IsTrackRecordingEnabled() const;
|
||||
|
||||
void SaveRoute();
|
||||
/// Returns the elevation profile data of the currently recorded track.
|
||||
/// To get the data on the every track recording state update, this function should be called after receiving the callback from the `SetTrackRecordingUpdateHandler`.
|
||||
static const ElevationInfo & GetTrackRecordingElevationInfo();
|
||||
|
||||
@@ -1639,4 +1639,18 @@ UNIT_CLASS_TEST(Runner, Bookmarks_RecentlyDeleted)
|
||||
TEST(!Platform::IsFileExistsByFullPath(filePath), ());
|
||||
TEST(!Platform::IsFileExistsByFullPath(deletedFilePath), ());
|
||||
}
|
||||
|
||||
UNIT_CLASS_TEST(Runner, Bookmarks_TestSaveRoute)
|
||||
{
|
||||
BookmarkManager bmManager(BM_CALLBACKS);
|
||||
bmManager.EnableTestMode(true);
|
||||
auto const points = {m2::PointD(0.0, 0.0), m2::PointD(0.001, 0.001)};
|
||||
auto const trackId = bmManager.SaveRoute(points, "London", "Paris");
|
||||
auto const * track = bmManager.GetTrack(trackId);
|
||||
TEST_EQUAL(track->GetName(), "London - Paris", ());
|
||||
auto const line = track->GetData().m_geometry.m_lines[0];
|
||||
std::vector const expectedLine = {{geometry::PointWithAltitude(m2::PointD(0.0, 0.0)), geometry::PointWithAltitude(m2::PointD(0.001, 0.001))}};
|
||||
TEST_EQUAL(line, expectedLine, ());
|
||||
}
|
||||
|
||||
} // namespace bookmarks_test
|
||||
|
||||
@@ -1091,6 +1091,27 @@ void RoutingManager::SetUserCurrentPosition(m2::PointD const & position)
|
||||
}
|
||||
}
|
||||
|
||||
static std::string GetNameFromPoint(RouteMarkData const & rmd)
|
||||
{
|
||||
if (rmd.m_subTitle.empty())
|
||||
return "";
|
||||
return rmd.m_title;
|
||||
}
|
||||
|
||||
void RoutingManager::SaveRoute()
|
||||
{
|
||||
auto points = GetRoutePolyline().GetPolyline().GetPoints();
|
||||
auto const routePoints = GetRoutePoints();
|
||||
std::string const from = GetNameFromPoint(routePoints.front());
|
||||
std::string const to = GetNameFromPoint(routePoints.back());
|
||||
// remove equal sequential points
|
||||
points.erase(
|
||||
std::unique(points.begin(), points.end(), [](const m2::PointD & p1, const m2::PointD & p2) { return AlmostEqualAbs(p1, p2, kMwmPointAccuracy); }),
|
||||
points.end());
|
||||
|
||||
m_bmManager->SaveRoute(points, from, to);
|
||||
}
|
||||
|
||||
bool RoutingManager::DisableFollowMode()
|
||||
{
|
||||
bool const disabled = m_routingSession.DisableFollowMode();
|
||||
|
||||
@@ -142,6 +142,7 @@ public:
|
||||
// This method was added because we do not want to break the behaviour that is familiar to our
|
||||
// users.
|
||||
bool DisableFollowMode();
|
||||
void SaveRoute();
|
||||
|
||||
void SetRouteBuildingListener(RouteBuildingCallback const & buildingCallback)
|
||||
{
|
||||
@@ -204,7 +205,7 @@ public:
|
||||
return m_routingSession.GetTurnNotificationsLocale();
|
||||
}
|
||||
// @return polyline of the route.
|
||||
routing::FollowedPolyline const & GetRoutePolylineForTests() const
|
||||
routing::FollowedPolyline const & GetRoutePolyline() const
|
||||
{
|
||||
return m_routingSession.GetRouteForTests()->GetFollowedPolyline();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace qt
|
||||
{
|
||||
void RoutingTurnsVisualizer::Visualize(RoutingManager & routingManager, df::DrapeApi & drape)
|
||||
{
|
||||
auto const & polyline = routingManager.GetRoutePolylineForTests().GetPolyline();
|
||||
auto const & polyline = routingManager.GetRoutePolyline().GetPolyline();
|
||||
auto const & turns = routingManager.GetTurnsOnRouteForTests();
|
||||
|
||||
for (auto const & turn : turns)
|
||||
|
||||
Reference in New Issue
Block a user