Select to view content in your preferred language

Follow a GeoElement in MapView (OrbitGeoElementCameraController but for MapView)

275
3
Jump to solution
07-23-2024 07:17 AM
imbachb
Regular Contributor

In our MapView we have many GeoElements that may change positions over time (for example cars).

Is there a straight forward way to follow such a GeoElement's position in a MapView? When following a GeoElement's position the user should not be able to pan away from the GeoElement. Zooming should only zoom in / out towards the followed GeoElement.

Basically it should have the same functionality as OrbitGeoElementCameraController Class | ArcGIS Maps SDK for Qt | Esri Developer, just for the MapView.

There is the LocationDisplay Class | ArcGIS Maps SDK for Qt | Esri Developer , is this the suggested way? I think one could "hide" the location marker and make it appear as if it was following the GeoElement using a simulated data source. Still seems a bit cumbersome... Would be neat if we could just update the position of the GeoElement and the camera adjusts accordingly, which is the way the OrbitGeoElementCameraController works.

Many thanks!

0 Kudos
1 Solution

Accepted Solutions
imbachb
Regular Contributor

I went ahead and tried to implement @Tanner_Yould suggestions while fixing the mentioned issues. The solution seems to work quite well. If anyone else wants to implement something like this, here are the key functions:

On start follow GeoElement

1. Prevent pan
2. Prevent native zoom behavior since it zooms towards the mouse pointer instead of the GeoElement. Replace it with own zoom behavior. Calculate and store the target scale. This stored target scale is needed when updating the viewpoint.
3. Update viewpoint whenever geometry of Graphic changes.

void
FollowGraphicViewpointController::connectFollowSignals(MapQuickView& view, Graphic& graphic)
{
	disconnectFollowSignals();

	m_disablePanInputConnection =
		connect(&view, &MapQuickView::mousePressed, this, [this](QMouseEvent& event) { event.accept(); });

	m_zoomConnection = connect(&view, &MapQuickView::mouseWheelChanged, this, [this, &view](QWheelEvent& event) {
		auto verticalAngleDelta = event.angleDelta().y();
		m_targetScale -= m_targetScale * (m_zoomFactor * verticalAngleDelta / m_angleUnitsPerWheelChange);
		updateViewpoint(m_targetGraphic->geometry());

		event.accept();
	});

	m_updateViewpointConnection =
		connect(&graphic, &Graphic::geometryChanged, this, [this, &graphic]() { updateViewpoint(graphic.geometry()); });
}

 When updating the viewpoint we must pass the previously stored target scale, otherwise the zooming animation is cancelled whenever we update the viewpoint.

void
FollowGraphicViewpointController::updateViewpoint(const Geometry& geometry) const
{
	m_mapView->setViewpointCenterAsync(Point(geometry), m_targetScale);
}

 This results in a relatively good feeling follow GeoElement functionality.

View solution in original post

3 Replies
Tanner_Yould
Esri Contributor

Hi radimbachb, thanks for reaching out! You should be able to get the GeoElement's geometry and set the MapView's viewpointCenter with that geometry.

For example:

 

connect(dynamicEntity, &DynamicEntity::dynamicEntityChanged, this, [this](DynamicEntityChangedInfo* changedInfo)
{
  // Other code

  m_followingObservation = true;
  m_mapView->setViewpointCenterAsync(Point(currentObservation->geometry()));
});

 

 

You could then disable all interaction with the map with the GeoView's setInteractionEnabled method, however if you want to still allow zooming, you'll need to selectively take control of each interaction event in the MapQuickView class that you don't want and call accept on it to prevent it from being processed by the Maps SDK.

For example:

 

connect(m_mapView, &MapQuickView::mousePressed, this, [this](QMouseEvent& mouseEvent) {
  if (!m_followingObservation) // boolean check to enable or disable mouse interactionj
    return;

  mouseEvent.accept(); // mousePressed won't be processed by the Maps SDK anymore
});

 

 

I hope this helps and let me know if I can do anything else for you. Thanks!

Tanner Yould
Product Engineer
ArcGIS Maps SDK for Native Apps
Esri
imbachb
Regular Contributor

@Tanner_Yould 

Thank you very much for the reply. Accepting the user input events and setting the viewpoint using the geometry could work.

There are 2 issues remaining that I struggle with:

  • When zooming in / out via mouse wheel it always zooms from the mouse pointer position, which in turn pans the map away from the followed GeoElement. On mouse wheel input, is there a way to zoom to the GeoElement, which should be at the center of the map view, instead of zooming to the mouse pointer? Do we need to connect to the wheel input event and handle the zoom ourselves?

 

  •  Zooming animations get cancelled whenever the position of the GeoElement changes and the viewpoint is set to the GeoElement's position. Is there a way to continue the zoom animation after updating the viewpoint position? Can we get a "target scale" of the zoom animation that we could pass  to setViewpointCenterAsync when we update the position of the GeoElement?

I think both these issues can be solved if we handle the mouse wheel input events ourselves, but I'm not sure if there's a setting or something like that that we miss.

0 Kudos
imbachb
Regular Contributor

I went ahead and tried to implement @Tanner_Yould suggestions while fixing the mentioned issues. The solution seems to work quite well. If anyone else wants to implement something like this, here are the key functions:

On start follow GeoElement

1. Prevent pan
2. Prevent native zoom behavior since it zooms towards the mouse pointer instead of the GeoElement. Replace it with own zoom behavior. Calculate and store the target scale. This stored target scale is needed when updating the viewpoint.
3. Update viewpoint whenever geometry of Graphic changes.

void
FollowGraphicViewpointController::connectFollowSignals(MapQuickView& view, Graphic& graphic)
{
	disconnectFollowSignals();

	m_disablePanInputConnection =
		connect(&view, &MapQuickView::mousePressed, this, [this](QMouseEvent& event) { event.accept(); });

	m_zoomConnection = connect(&view, &MapQuickView::mouseWheelChanged, this, [this, &view](QWheelEvent& event) {
		auto verticalAngleDelta = event.angleDelta().y();
		m_targetScale -= m_targetScale * (m_zoomFactor * verticalAngleDelta / m_angleUnitsPerWheelChange);
		updateViewpoint(m_targetGraphic->geometry());

		event.accept();
	});

	m_updateViewpointConnection =
		connect(&graphic, &Graphic::geometryChanged, this, [this, &graphic]() { updateViewpoint(graphic.geometry()); });
}

 When updating the viewpoint we must pass the previously stored target scale, otherwise the zooming animation is cancelled whenever we update the viewpoint.

void
FollowGraphicViewpointController::updateViewpoint(const Geometry& geometry) const
{
	m_mapView->setViewpointCenterAsync(Point(geometry), m_targetScale);
}

 This results in a relatively good feeling follow GeoElement functionality.