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!
Solved! Go to Solution.
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.
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!
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:
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.
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.