Simulate Navigation¶
Setup¶
Prerequisites¶
Setting your API key token¶
To set your API key token, define it like this:
#define API_TOKEN "YOUR_API_KEY_TOKEN"
replacing YOUR_API_KEY_TOKEN
with your actual API key token text, within the quotes.
This can be done in the main()
function, before the following line:
#if defined(API_TOKEN)
or outside the main()
function, further up, or in a header file.
Although it is also possible to define your API key token as the text value of the GEM_TOKEN
environment variable, this is not recommended, as it is not secure.
Build and run¶
In Visual Studio, right-click on the SimulateNavigation
project, and select
Set as Startup Project
then press F5
to run.
How it works¶
1int main( int argc, char** argv )
2{
3 std::string projectApiToken = "";
4#define API_TOKEN "YOUR_API_KEY_TOKEN"
5#if defined(API_TOKEN)
6 projectApiToken = std::string( API_TOKEN );
7#endif
8 // Sdk objects can be created & used below this line
9 Environment::SdkSession session(projectApiToken, { argc > 1 ? argv[1] : "" });
10
11 // Create an interactive map view
12 CTouchEventListener pTouchEventListener;
13 gem::StrongPointer<gem::MapView> mapView = gem::MapView::produce(session.produceOpenGLContext(
14 Environment::WindowFrameworks::ImGUI, "SimulateNavigation", &pTouchEventListener, getUiRender()));
15 if ( !mapView )
16 {
17 GEM_LOGE( "Error creating gem::MapView: %d", GEM_GET_API_ERROR() );
18 }
19 WAIT_UNTIL_WINDOW_CLOSE();
20 return 0;
21}
First, the API key token is set.
Environment::SdkSession session(projectApiToken, { argc > 1 ? argv[1] : "" });
CTouchEventListener pTouchEventListener;
MapView
interactive map instance is created, using an OpenGL context for rendering,
and selecting ImGui for the graphic control interface.gem::StrongPointer<gem::MapView> mapView = gem::MapView::produce(session.produceOpenGLContext(Environment::WindowFrameworks::ImGUI,
"SimulateNavigation", &pTouchEventListener, getUiRender()));
Finally, the getUiRender()
function is passed in, which uses ImGui to render GUI elements,
capture user control input, and call the appropriate SDK functions.
1auto getUiRender()
2{
3 auto uiElems = std::make_shared<CUIElements>();
4 return UICallbacks( std::bind([=]()
5 {
6 return uiElems->initialize();
7 } ),
8 std::bind([=](gem::StrongPointer<gem::MapView> mapView)
9 {
10 //////////////////////////////////////////////////////////
11 // PANEL TO RESUME FOLLOWING POSITION, TOGGLE KM/MI AND STOP SIMULATION
12 //////////////////////////////////////////////////////////
13 static bool iskm = true;
14 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
15 ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 0, main_viewport->WorkPos.y + 120), ImGuiCond_FirstUseEver);
16 ImGui::Begin("panel", nullptr, ImGuiWindowFlags_NoMove
17 | ImGuiWindowFlags_NoDecoration
18 | ImGuiWindowFlags_AlwaysAutoResize
19 | ImGuiWindowFlags_NoSavedSettings);
20 auto navListener = gem::StrongPointerFactory<MyNavigationListener>();
21 ImGui::PushFont(uiElems->font1);
22 if (!gem::NavigationService().isNavigationActive()
23 && !gem::NavigationService().isSimulationActive())
24 {
25 if (ImGui::Button("Click to simulate navigation on a route"))
26 {
27 gem::LandmarkList waypoints({ { "San Francisco", { 37.77903, -122.41991 } }, { "San Jose", { 37.33619, -121.89058 } } });
28
29 // Compute route using these preferences: car / fastest / without alternatives in result
30 gem::RouteList routes;
31 ProgressListener routeListener;
32 gem::RoutingService().calculateRoute(routes, waypoints,
33 gem::RoutePreferences().setTransportMode(gem::RTM_Car).setRouteType(gem::RT_Fastest).setAlternativesSchema(gem::AS_Never), &routeListener);
34
35 // Wait until route calculation finished & check success
36 if (WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &routeListener), 30000) && routeListener.GetError() == gem::KNoError && !routes.empty())
37 {
38 // Add the first resulting route (at index 0) to map view
39 mapView->preferences().routes().add(routes[0]);
40
41 // Start simulated navigation along the route
42 gem::NavigationService().startSimulation(routes[0], navListener, gem::ProgressListener());
43
44 // Start follow GPS positions ( generated by the simulation ) - camera follows the position along the route
45 mapView->startFollowingPosition();
46 }
47 }
48 }
49 if (!mapView->isFollowingPosition())
50 {
51 if (ImGui::Button("Follow position"))
52 {
53 mapView->startFollowingPosition();
54 }
55 }
56 else
57 {
58 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 0.f, 1.f, 1.f));
59 ImGui::PushFont(uiElems->font1);
60 ImGui::Text("Following position!");
61 ImGui::PopFont();
62 ImGui::PopStyleColor(1);
63 }
64 if (iskm)
65 {
66 if (ImGui::Button("Switch to miles"))
67 {
68 iskm = false;
69 }
70 }
71 else
72 {
73 if (ImGui::Button("Switch to kilometers"))
74 {
75 iskm = true;
76 }
77 }
78 if (gem::NavigationService().isNavigationActive()
79 || gem::NavigationService().isSimulationActive())
80 {
81 if (ImGui::Button("Stop navigation"))
82 gem::NavigationService().cancelNavigation(navListener);
83 }
84 ImGui::PopFont();
85 ImGui::End();
First, the main ImGui viewport is obtained, and the x,y position of the ImGui window, with the identifier “panel”, in pixels, is set within the SDK OpenGL viewport.
ImGui::Begin()
function.ImGui::Button()
which the user can click to calculate a predefined route, add the first (at index 0)
resulting route to the map, and start simulated navigation on that route.At least 2 waypoints define a route. The first is the departure position, and the last is the destination. There can be zero or more intermediate waypoints through which the route passes in the order they are listed. The coordinates are {latitude,longitude} in degrees; the landmark name is optional and can be an empty string.
mapView->startFollowingPosition();
ImGui::Text("Following position!");
is displayed
instead of the button, to indicate this mode.Next is a button to toggle display between miles and kilometers. Next is a button to stop navigation, and this button appears only if navigation is active.
1 //////////////////////////////////////////////////////////
2 // TOP STATUS PANEL
3 //////////////////////////////////////////////////////////
4 if (gem::NavigationService().isNavigationActive()
5 || gem::NavigationService().isSimulationActive())
6 {
7 ImGui::SetNextWindowBgAlpha(1.0f);
8 ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 0, main_viewport->WorkPos.y + 0), ImGuiCond_FirstUseEver);
9 ImGui::SetNextWindowSize(ImVec2(main_viewport->WorkSize.x, 108));
10 ImGui::GetStyle().WindowRounding = 0.0f;
11 ImGui::Begin("panel2", nullptr, ImGuiWindowFlags_NoMove
12 | ImGuiWindowFlags_NoDecoration
13 | ImGuiWindowFlags_AlwaysAutoResize
14 //| ImGuiWindowFlags_NoBackground
15 | ImGuiWindowFlags_NoSavedSettings);
16 const ImU32 colorblack = ImGui::GetColorU32(ImVec4(0, 0, 0, 1));
17 ImVec2 wPos = ImGui::GetWindowPos();
18 ImVec2 wSize = ImGui::GetWindowSize();
19 ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(wPos.x + 0, wPos.y + 0), ImVec2(wSize.x, wSize.y), colorblack);
20 auto navinstruct = gem::NavigationService().getNavigationInstruction();
21 gem::String instructionText = navinstruct.getNextTurnInstruction();
22 instructionText.fallbackToLegacyUnicode();
23
24 if (ImGui::BeginTable("top_instruction_panel", 2))
25 {
26 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, NEXT_TURN_ICON_SIZE_PIXELS + 12);
27 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, wSize.x - 12 - NEXT_TURN_ICON_SIZE_PIXELS);
28
29 ImGui::TableNextRow();
30 ImGui::TableSetColumnIndex(0);
31
32 //////////////////////////////////////////////////////////
33 // TURN ICON
34 //////////////////////////////////////////////////////////
35 {
36 const ImVec2 nextTurnIconSize{ NEXT_TURN_ICON_SIZE_PIXELS, NEXT_TURN_ICON_SIZE_PIXELS };
37 gem::Rgba color(255, 0, 255, 255);
38 gem::AbstractGeometryImageRenderSettings settings(gem::Rgba::white(), gem::Rgba::black(), color);
39 auto bitmap = gem::StrongPointerFactory<BitmapImpl>(70, 70);
40 navinstruct.getNextTurnDetails().getAbstractGeometryImage().render(*bitmap, settings);
41 unsigned int textureId = BitmapImpl::LoadTextureIntoGPU(bitmap->size().width, bitmap->size().height, bitmap->begin());
42 ImGui::Image((void*)textureId, nextTurnIconSize);
43 }
44
45 ImGui::TableSetColumnIndex(1);
46
47 //////////////////////////////////////////////////////////
48 // NEXT STREET NAME
49 //////////////////////////////////////////////////////////
50 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f));
51 ImGui::PushFont(uiElems->font3);
52 ImGui::Text(navinstruct.getNextStreetName().toStdString().c_str());
53 ImGui::PopFont();
54 ImGui::PopStyleColor(1);
55
56 //////////////////////////////////////////////////////////
57 // NEXT TURN INSTRUCTION TEXT
58 //////////////////////////////////////////////////////////
59 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f));
60 ImGui::PushFont(uiElems->font2);
61 ImGui::Text("%s", instructionText.toStdString().c_str());
62
63 auto remainingDistanceToNextTurn = navinstruct.getTimeDistanceToNextTurn().getTotalDistance();
64 auto remainingTimeToNextTurn = navinstruct.getTimeDistanceToNextTurn().getTotalTime();
65
66 ImGui::SameLine();
67 ImGui::Text(" ");
68 ImGui::SameLine();
69
70 //////////////////////////////////////////////////////////
71 // NEXT TURN INSTRUCTION DISTANCE
72 //////////////////////////////////////////////////////////
73 if (iskm)
74 {
75 ImGui::Text("%.1f ", (float)(remainingDistanceToNextTurn));
76 ImGui::SameLine();
77 ImGui::Text("m ");
78 ImGui::SameLine();
79 }
80 else
81 {
82 ImGui::Text("%.1f ", (float)(remainingDistanceToNextTurn / 1609.344 * 5280));
83 ImGui::SameLine();
84 ImGui::Text("ft ");
85 ImGui::SameLine();
86 }
87 ImGui::PopFont();
88 ImGui::PopStyleColor(1);
89
90 ImGui::EndTable();
91
92 } // end table
93
94 ImGuiStyle& style = ImGui::GetStyle();
95 ImVec4* colors = style.Colors;
96 ImGui::End();
The top panel, displaying the next turn icon, next turn instruction, and the distance to the next turn.
1 //////////////////////////////////////////////////////////
2 // BOTTOM STATUS PANEL
3 //////////////////////////////////////////////////////////
4 {
5 ImGui::SetNextWindowBgAlpha(1.0f);
6 ImGui::SetNextWindowPos(ImVec2(main_viewport->Pos.x + 20, main_viewport->Pos.y + main_viewport->WorkSize.y - 72));
7 ImGui::SetNextWindowSize(ImVec2(main_viewport->WorkSize.x - 40, 64));
8 ImGui::GetStyle().WindowRounding = 12.0f;
9 ImGui::Begin("panel4", nullptr, ImGuiWindowFlags_NoMove
10 | ImGuiWindowFlags_NoDecoration
11 | ImGuiWindowFlags_NoSavedSettings);
12 ImVec4 mycolor0{ 0,0,0,255 };
13 ImVec4 mycolor1{ 255,0,0,255 };
14 ImVec4 mycolor2{ 0,255,0,255 };
15 ImVec4 mycolor3{ 0,0,255,255 };
16 ImVec4 mycolor7{ 255,255,255,255 };
17
18 auto remainingTravelTime = navinstruct.getRemainingTravelTimeDistance().getTotalTime();
19 auto remainingTravelDistance = navinstruct.getRemainingTravelTimeDistance().getTotalDistance();
20
21 time_t currentTimeSec = (time_t)(gem::Time::getLocalTime().asInt() / 1000);
22 time_t eta = (time_t)(currentTimeSec + remainingTravelTime);
23 struct tm tmcurrent, etatm;
24 struct tm* tmptr = gmtime((const time_t*)&(currentTimeSec));
25 memcpy(&tmcurrent, tmptr, sizeof(struct tm));
26 tmptr = gmtime((const time_t*)&(eta));
27 memcpy(&etatm, tmptr, sizeof(struct tm));
28
29 //////////////////////////////////////////////////////////
30 // REMAINING TRAVEL HOURS, MIN, SEC: PRINT ONLY HOURS:MIN IF >= 1H OR MIN:SEC IF < 1H
31 //////////////////////////////////////////////////////////
32 int remainingTravelHours = (int)(remainingTravelTime / 3600);
33 int remainingTravelMin = (int)((remainingTravelTime % 3600) / 60);
34 int remainingTravelSec = (int)(remainingTravelTime % 60);
35 //////////////////////////////////////////////////////////
36 // COMPUTE BOTTOM STAT STR LENGTH TO ENABLE CENTERING STR HORIZONTALLY AND VERTICALLY!
37 //////////////////////////////////////////////////////////
38 ImGui::PushFont(uiElems->font1);
39 float font_size1 = ImGui::GetFontSize();
40 auto textWidth1 = ImGui::CalcTextSize("ETAminkm").x;
41 ImGui::PopFont();
42 ImGui::PushFont(uiElems->font2);
43 float font_size2 = ImGui::GetFontSize();
44 auto textWidth2 = ImGui::CalcTextSize("00:00 00").x;
45 auto textHeight2 = ImGui::CalcTextSize("00").y;
46 ImGui::PopFont();
47 if (remainingTravelHours > 0)
48 {
49 int digitsInHours = (int)(log10(remainingTravelHours)) + 1;
50 ImGui::PushFont(uiElems->font2);
51 textWidth2 += digitsInHours * ImGui::CalcTextSize("0").x;
52 ImGui::PopFont();
53 ImGui::PushFont(uiElems->font1);
54 textWidth1 += ImGui::CalcTextSize("h ").x;
55 ImGui::PopFont();
56
57 }
58 else
59 {
60 ImGui::PushFont(uiElems->font2);
61 textWidth2 += ImGui::CalcTextSize(" 00").x;
62 ImGui::PopFont();
63 }
64 int digitsInMeters = (int)(log10(remainingTravelDistance)) + 1;
65 if (remainingTravelDistance > 1000)
66 {
67 digitsInMeters -= 1;//printed %.1f
68 }
69 ImGui::PushFont(uiElems->font2);
70 textWidth2 += digitsInMeters * ImGui::CalcTextSize("0").x;
71 ImGui::PopFont();
72 ImGui::PushFont(uiElems->font1);
73 textWidth1 += ImGui::CalcTextSize("km").x;
74 ImGui::PopFont();
75
76 ImGui::SetCursorScreenPos(ImVec2((ImGui::GetWindowSize().x - textWidth2 - textWidth1) * 0.4f + ImGui::GetWindowPos().x,
77 (ImGui::GetWindowSize().y - textHeight2) * 0.5f + ImGui::GetWindowPos().y));
78
79 //////////////////////////////////////////////////////////
80 // ETA
81 //////////////////////////////////////////////////////////
82 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 0.f, 0.f, 1.f));
83 //auto rpos = ImGui::GetCursorScreenPos();
84 //ImGui::SetCursorScreenPos(ImVec2(rpos.x, rpos.y + 12));
85 ImGui::PushFont(uiElems->font1);
86 ImGui::Text("ETA");
87 ImGui::PopFont();
88 ImGui::SameLine();
89 ImGui::PushFont(uiElems->font2);
90 ImGui::Text("%02d:%02d ", etatm.tm_hour, etatm.tm_min);
91 ImGui::PopFont();
92 ImGui::SameLine();
93 ImGui::PopStyleColor(1);
94
95 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, .5f, 0.f, 1.f));
96 if (remainingTravelHours > 0)
97 {
98 ImGui::PushFont(uiElems->font2);
99 ImGui::Text("%d", remainingTravelHours);
100 ImGui::PopFont();
101 ImGui::SameLine();
102 ImGui::PushFont(uiElems->font1);
103 ImGui::Text("h ");
104 ImGui::PopFont();
105 ImGui::SameLine();
106 }
107 {
108 ImGui::PushFont(uiElems->font2);
109 ImGui::Text("%02d", remainingTravelMin);
110 ImGui::PopFont();
111 ImGui::SameLine();
112 ImGui::PushFont(uiElems->font1);
113 ImGui::Text("min");
114 ImGui::PopFont();
115 ImGui::SameLine();
116 }
117 if (remainingTravelHours == 0)
118 {
119 ImGui::PushFont(uiElems->font2);
120 ImGui::Text(" %02d", remainingTravelSec);
121 ImGui::PopFont();
122 ImGui::SameLine();
123 }
124 ImGui::PopStyleColor(1);
125
126 ImGui::PushFont(uiElems->font2);
127 ImGui::Text(" ");
128 ImGui::PopFont();
129 ImGui::SameLine();
130
131 //////////////////////////////////////////////////////////
132 // REMAINING TRAVEL DISTANCE KM
133 //////////////////////////////////////////////////////////
134 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 0.f, 1.f, 1.f));
135 if (iskm)
136 {
137 if (remainingTravelDistance > 1000)
138 {
139 ImGui::PushFont(uiElems->font2);
140 ImGui::Text("%.1f", (float)(remainingTravelDistance * .001));
141 ImGui::PopFont();
142 ImGui::SameLine();
143 ImGui::PushFont(uiElems->font1);
144 ImGui::Text("km");
145 ImGui::PopFont();
146 }
147 else
148 {
149 ImGui::PushFont(uiElems->font2);
150 ImGui::Text("%d", (float)(remainingTravelDistance));
151 ImGui::PopFont();
152 ImGui::SameLine();
153 ImGui::PushFont(uiElems->font1);
154 ImGui::Text("m");
155 ImGui::PopFont();
156 }
157 }
158 else
159 {
160 if (remainingTravelDistance > 1000)
161 {
162 ImGui::PushFont(uiElems->font2);
163 ImGui::Text("%.1f", (float)(remainingTravelDistance / 1609.344));
164 ImGui::PopFont();
165 ImGui::SameLine();
166 ImGui::PushFont(uiElems->font1);
167 ImGui::Text("mi");
168 ImGui::PopFont();
169 }
170 else
171 {
172 ImGui::PushFont(uiElems->font2);
173 ImGui::Text("%.1f", (float)(remainingTravelDistance / 1609.344 * 5280));
174 ImGui::PopFont();
175 ImGui::SameLine();
176 ImGui::PushFont(uiElems->font1);
177 ImGui::Text("ft");
178 ImGui::PopFont();
179 }
180 }
181 ImGui::PopStyleColor(1);
182 colors[ImGuiCol_WindowBg] = mycolor7;
183 ImGui::End();
184 }
185 }
186 }
187 , std::placeholders::_1);
188}
The bottom panel, displaying the remaining travel time, ETA (estimated time of arrival), and remaining distance to travel.