Skip to content

Interactive Map

In this guide you will learn how to get user input and pass it to the SDK touch handler in order to make the map interactive. After completing this guide, you will be able to pan and zoom the map.

Prerequisites

It is required that you complete the Environment Setup - CPP Examples guide as well as the Create a Project - CPP Examples guide before starting this guide.

Touch event listener

Open the file CenterMap.cpp and define a touch event listener class, that is, add the following code before the main() function:

 1class CTouchEventListener : public ITouchEventsListener
 2{
 3  public:
 4  void setMapViewPointer(std::shared_ptr<gem::MapView> pMapView)
 5  {
 6    m_mapView = pMapView;
 7    lastXpos = 0; lastYpos = 0;
 8  }
 9  void handleTouchEvent(int eventType,int pointerId,int x,int y) override
10  {
11    lastXpos = x; lastYpos = y;
12    gem::Xy mousePos(x,y);
13    if(m_mapView.get()!=nullptr)
14      m_mapView->getScreen()->handleTouchEvent((gem::ETouchEvent)eventType,pointerId,mousePos);
15  }
16  void handleMouseScrollEvent(int delta,int x,int y) override
17  {
18    gem::Xy mousePos(x,y);
19    if(m_mapView.get()!=nullptr)
20      m_mapView->getScreen()->scrollEvent(delta,mousePos);
21  }
22  void getCursorPosition(int &x, int &y) override
23  {
24    x = lastXpos; y = lastYpos;
25  }
26  private:
27  std::shared_ptr<gem::MapView> m_mapView;
28  int lastXpos, lastYpos;
29};

The touch event listener instance keeps a pointer to the MapView instance, which is used to pass the user touch inputs to the SDK. The pointer to the MapView instance is set via setMapViewPointer()

The main function to pass touch inputs to the SDK is handleTouchEvent(), whereas handleMouseScrollEvent() is used to pass mouse scroll events to the SDK.

The function getCursorPosition() is a necessary convenience, to get the current x,y mouse cursor position in pixels, as this position must be passed to the SDK with all touch events, however, not all input callbacks contain the x,y position, so for those events that do not have an explicit x,y position, the last x,y position is stored and then retrieved using this funtion.

Replace the following line:

auto oglContext = env.ProduceOpenGLContext("centerMap");

with this code, to add the touch event listener:

CTouchEventListener pTouchEventListener;
auto oglContext = env.ProduceOpenGLContext("centerMap", &pTouchEventListener);

After the mapView = gem::MapView::produce() line, add this line:

pTouchEventListener.setMapViewPointer(mapView);

to save a pointer to the mapView in the touch event listener.

Environment.cpp

Open the file Environment.cpp and add the touch event listener to the ProduceOpenGLContext() function so that the function looks as follows:

1gem::StrongPointer<OpenGLContext> Environment::ProduceOpenGLContext(
2  std::string windowName, ITouchEventsListener* pTouchEventListener)
3{
4  if (!m_openGLContext.get())
5    m_openGLContext = OpenGLContext::Produce(windowName, pTouchEventListener);
6  return m_openGLContext;
7}

Environment.h

Open the file Environment.h and add the touch event listener to the ProduceOpenGLContext() function signature so that it looks as follows:

1gem::StrongPointer<OpenGLContext> ProduceOpenGLContext(std::string windowName = "",
2  ITouchEventsListener* pListener = nullptr);

OpenGLContext.cpp

Open the file OpenGLContext.cpp and add the touch event listener to the initialize() function so that it looks as follows:

 1bool OpenGLContext::initialize(GLFWwindow* pWindow, float nDPI,
 2  ITouchEventsListener* pEventTouchListener = nullptr)
 3{
 4  m_window = pWindow;
 5  m_dpi = nDPI;
 6  m_initialized = true;
 7  m_pTouchEventListener = pEventTouchListener;
 8  glfwSetWindowUserPointer(pWindow,this);
 9  return true;
10}

Also add a get function as shown, to return a pointer to the touch events listener: GetTouchEventHandler()

1ITouchEventsListener* OpenGLContext::GetTouchEventHandler()
2{
3  return m_pTouchEventListener;
4}

Add the 3 mouse/touch input event callback functions which receive the input events from the GLFW library and then pass them to the SDK via the touch events listener.

 1void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
 2{
 3  OpenGLContext* pOpenGLContext = (OpenGLContext*)glfwGetWindowUserPointer(window);
 4  if (pOpenGLContext && pOpenGLContext->GetTouchEventHandler())
 5  {
 6    int xpos, ypos;
 7    //get the stored cursor position because a button event does not include it
 8    pOpenGLContext->GetTouchEventHandler()->getCursorPosition(xpos, ypos);
 9    if (button == GLFW_MOUSE_BUTTON_LEFT)
10    {
11      if (action == GLFW_PRESS)
12      {
13        pOpenGLContext->GetTouchEventHandler()->handleTouchEvent(gem::ETouchEvent::TE_Down, 0, xpos, ypos);
14      }
15      else if (action == GLFW_RELEASE)
16      {
17        pOpenGLContext->GetTouchEventHandler()->handleTouchEvent(gem::ETouchEvent::TE_Up, 0, xpos, ypos);
18      }
19    }
20  }
21}
22void cursorPositionCallback(GLFWwindow* window, double xpos, double ypos)
23{
24  OpenGLContext* pOpenGLContext = (OpenGLContext*)glfwGetWindowUserPointer(window);
25  if (pOpenGLContext)
26  {
27    if (pOpenGLContext->GetTouchEventHandler())
28    {
29      pOpenGLContext->GetTouchEventHandler()->handleTouchEvent(gem::ETouchEvent::TE_Move, 0, (int)xpos, (int)ypos);
30    }
31  }
32}
33void mouseScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
34{
35  OpenGLContext* pOpenGLContext = (OpenGLContext*)glfwGetWindowUserPointer(window);
36  if (pOpenGLContext && pOpenGLContext->GetTouchEventHandler())
37  {
38    int xpos, ypos;
39    pOpenGLContext->GetTouchEventHandler()->getCursorPosition(xpos, ypos);
40    if (pOpenGLContext->GetTouchEventHandler())
41    {
42      pOpenGLContext->GetTouchEventHandler()->handleMouseScrollEvent((int)yoffset, (int)xpos, (int)ypos);
43    }
44  }
45}
The mouseButtonCallback() captures mouse button up or down events.
The cursorPositionCallback() captures mouse motion events, regardless if a mouse button is up or down.
The mouseScrollCallback() captures mouse scroll events, used for zooming the map.

Change the Produce() function signature by adding the touch event listener, so it looks like this:

gem::StrongPointer<OpenGLContext> OpenGLContext::Produce(std::string windowName, ITouchEventsListener* pEventTouchListener)

Set the 3 touch input GLFW callback functions defined above:

1glfwSetMouseButtonCallback(pDisplayWindow, mouseButtonCallback);
2glfwSetCursorPosCallback(pDisplayWindow, cursorPositionCallback);
3glfwSetScrollCallback(pDisplayWindow, mouseScrollCallback);

Also update the initialize() function call at the bottom so it looks like this:

m_pOpenGLContext->initialize(pDisplayWindow, nDPI,pEventTouchListener);

OpenGLContext.h

Open the file OpenGLContext.h and add the following include directive:

#include <API/GEM_Canvas.h> //gem::ETouchEvent

Add the touch events listener interface:

1class ITouchEventsListener
2{
3  public:
4  virtual void handleTouchEvent(int eventType,int pointerId,int x,int y) = 0;
5  virtual void handleMouseScrollEvent(int delta,int x,int y) = 0;
6  virtual void getCursorPosition(int& x, int& y) = 0;
7};

Update the initialize() public function signature so it looks like this:

bool initialize(GLFWwindow* pWindow, float nDPI,ITouchEventsListener* pEventTouchListener);

Update the Produce() public function signature by adding the touch event listener, so it looks like this:

static gem::StrongPointer<OpenGLContext> Produce(std::string windowName = "", ITouchEventsListener* pTouchEventListener = nullptr);

Add this public function signature:

ITouchEventsListener* GetTouchEventHandler();

Add this private member variable:

ITouchEventsListener* m_pTouchEventListener;

Try it out!

Click the green arrow play button at the top to compile and run the project. You can now pan the map and zoom in/out using the mouse scroll wheel.