Qt SDK C++: Correctly Supporting 2D and 3D Map Display Options

1576
7
04-03-2020 01:43 PM
BradDuBois
New Contributor II

Hello,

I have created an application with Qt using the C++ method.  In the QML I declare both a scene view and a map view and set them in my MapManager class. I provide a button on the application to toggle between map and scene views by changing the visibility of the QML objects and in the C++ class I remove the graphics overlay object from the map that is being hidden and adding to the map that is being shown.  This mostly seems to work but there are some inconsistencies that I'm trying to work through.

So my first question is whether or not this is the best way to handle 2D/3D combined?  If it is not, I want to do it the recommended way before I go any further.

If it is OK, then my follow on questions are related to the inconsistencies that I have seen. The first is that the 2D map is not currently showing the same graphic in the same way that the 3D map is showing it.  I am creating routes for use with flying unmanned air vehicles with waypoints at each vertex that has a symbol and a label.  There can be different symbols depending on the type of waypoint.  On the 3D map, I am seeing all the vertex symbols and labels as expected but on the 2D map I only see the route legs.

3D:

2D:

The next question is kind of similar but in this case, when I set the map to be in 3D at startup and then add a route, the route is not displayed until I toggle to 2D and back to 3D. The route is immediately visible if I'm in 2D mode (albeit missing the vertex graphics) when I add the route.  If I am in 3D map mode, then no route graphics show up at all until I zoom in or out and then toggle to 2D and back to 3D.

I've been looking for some way to invalidate or otherwise force a redraw but I am coming up empty there.

I have other questions that will warrant separate posts but I'm looking for the right way to proceed on this first part before I invest any more time going in what might be the wrong direction.

Thanks for any and all help!

Brad DuBois

0 Kudos
7 Replies
LucasDanzinger
Esri Frequent Contributor

Hey Brad-

whether or not this is the best way to handle 2D/3D combined

This sounds like a good approach.

The first is that the 2D map is not currently showing the same graphic in the same way that the 3D map is showing it.

Seems strange. Are you trying to share the Graphic and GraphicsOverlay instances between the MapView and SceneView? Maybe you can share some code.

route is not displayed until I toggle to 2D and back to 3D

Hard to say. Same question - are you sharing the graphic/overlay instances between the 2 GeoViews?

0 Kudos
BradDuBois
New Contributor II

Hello Lucas,

Thanks for getting back to me, I am glad that my initial approach is at least correct.  

What I am doing for the graphics is creating a single instance at construction of my MissionMap object that is moved between the two GeoViews when toggling between them.  (I only have a single overlay at the moment so I'm just grabbing the first item in the collection):

in the QML

void MissionMap::toggle2D3D()
{
    if (is3DMode_)
    {
        // assign route layer to 2D map
        routeOverlay_ = sceneView_->graphicsOverlays()->at(0);
        sceneView_->graphicsOverlays()->removeAt(0);
        mapView_->graphicsOverlays()->append(routeOverlay_);
        sceneView_->update();
        setIs3DMode(false);
    }
    else
    {
        routeOverlay_ = mapView_->graphicsOverlays()->at(0);
        mapView_->graphicsOverlays()->removeAt(0);
        sceneView_->graphicsOverlays()->append(routeOverlay_);
        mapView_->update();
        setIs3DMode(true);
    }
}
I did discover that the waypoint vertex symbols are indeed being added to the map, but way off from where I expect, near lat/lon of 0, 0.
Not sure why those symbol positions are being treated differently in the two GeoViews yet.  
I plan to look into that soon, or better yet, you may know exactly what silly thing I'm doing!

Should I expect that swapping the over route overlay object between the two GeoViews to work correctly?

Thanks,

Brad

0 Kudos
LucasDanzinger
Esri Frequent Contributor

I have a sample started to try and reproduce the issues you mentioned. At the moment it just moves a graphic (in a graphics overlay) with a simple marker symbol back and forth between the map and scene views. This seems to work ok. Could you share some of your code that you use to create the symbols that look wrong?

0 Kudos
BradDuBois
New Contributor II

Hopefully there is enough below.  If you need more details, I'll be happy to provide but I tried not to include anything that seemed like it didn't affect the question at hand.

I recorded the screen to show what's happening (with a bit of kid noise in the background):

MapTroubleshooting - YouTube 

// constructor to show how routeOverlay_ is initialized
MissionMap
::MissionMap(QQuickItem* parent) : QQuickItem(parent)

, tileServerUrl_("https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_Brest/SceneServer/layers/0")
, elevationDataServerUrl_("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer")
, initialCameraPt_(-120.615988, 35.400014, 5000, SpatialReference(spatialReferenceWkid_))
, routeOverlay_(new  GraphicsOverlay(this))
, camera_(initialCameraPt_, cameraHeading_, cameraPitch_, cameraRoll_)
, lineBuilderFromMouse_(SpatialReference(spatialReferenceWkid_), this)
, wptFlyoverImage_(":/images/flyover.png")
{
    // license ArcGIS:
    LicenseResult result = ArcGISRuntimeEnvironment::setLicense(ARCGIS_LICENSE);
    missionMap_ = this;
    new AirTrafficOverlayManager(this);
}

// where the mission is added and the graphics overlay is updated with route/waypoint items

void MissionMap::addMission(Mission newMission)
{
    loadedMissions_.insert(newMission.getMissionId(), std::make_shared<Mission>(newMission));
    std::shared_ptr<Mission> mission = loadedMissions_[newMission.getMissionId()];
    if (mission.get()->getLoiter() != nullptr)
    {
        mission.get()->getLoiter().get()->getLoiterWaypointGraphic(routeOverlay_);
    }
    for (auto route : mission.get()->getRoutes())
    {
        // put waypoint symbols on map
        for (auto waypoint : route->getWaypoints())
        {
            routeOverlay_->graphics()->append(waypoint->getWaypointGraphic().get());
        }
        // put route line on the map
        routeOverlay_->graphics()->append(route->getRouteGraphic().get());
        // this forces route to show up immediately when loading a mission from either 2D or 3D
        // but if starting from 2D and toggling to 3D, 3D is good, but then back to 2D it's not displayed until the next toggle away and back to 2D
        if (is3DMode_)
        {
            refreshSceneViewGraphics();
        }
        else
        {
            refreshMapViewGraphics();
        }
        mapView_->update();
        sceneView_->update();
        //int widx = 0; widx < route->getWaypoints().count(); widx++
    }
}

void MissionMap::refreshSceneViewGraphics()
{
    sceneView_->graphicsOverlays()->removeAt(0);
    sceneView_->graphicsOverlays()->append(routeOverlay_);
}
void MissionMap::refreshMapViewGraphics()
{
    mapView_->graphicsOverlays()->removeAt(0);
    mapView_->graphicsOverlays()->append(routeOverlay_);
}
void MissionMap::toggle2D3D()
{
    if (is3DMode_)
    {
        // assign route layer to 2D map
        sceneView_->graphicsOverlays()->removeAt(0);
        mapView_->graphicsOverlays()->append(routeOverlay_);
        sceneView_->update();
        setIs3DMode(false);
    }
    else
    {
        mapView_->graphicsOverlays()->removeAt(0);
        sceneView_->graphicsOverlays()->append(routeOverlay_);
        mapView_->update();
        setIs3DMode(true);
    }
}

///----------------

std::shared_ptr<Graphic> Waypoint::getWaypointGraphic()
{
    waypointSymbol_ = std::make_shared<CompositeSymbol>(this);
    waypointSymbol_->symbols()->append(waypointImage_.get());
    waypointSymbol_->symbols()->append(waypointLabel_.get());
    waypointImageGraphic_ = std::make_shared<Graphic>(waypointPos_, attributes_, waypointSymbol_.get(), this);
    return waypointImageGraphic_;
}
///---------------------
std::shared_ptr<Graphic> Route::getRouteGraphic()
{
    routeLineGraphic_ = std::make_shared<Graphic>(linebuilder_->toGeometry(), attributes_, lineRenderer_.get(), this);
    return routeLineGraphic_;
}

Thanks for the assistance!

0 Kudos
BradDuBois
New Contributor II

I believe I have fixed the issue regarding the route not always showing up on the 3D view when switching between maps.  I am now using removeOne() instead of remove(0):

void MissionMap::toggle2D3D()
{
    if (is3DMode_)
    {
        // assign route layer to 2D map
        sceneView_->graphicsOverlays()->removeOne(routeOverlay_);
        mapView_->graphicsOverlays()->append(routeOverlay_);
        sceneView_->update();
        setIs3DMode(false);
    }
    else
    {
        mapView_->graphicsOverlays()->removeOne(routeOverlay_);
        sceneView_->graphicsOverlays()->append(routeOverlay_);
        mapView_->update();
        setIs3DMode(true);
    }
}

There is another overlay graphic added in a different module for the aircraft icon and I think that was being removed instead of the route overlay in the case when the route overlay was not showing up on the 3D scene view.

I still have the issue with the waypoint markers showing up at (0,0) though.

0 Kudos
LucasDanzinger
Esri Frequent Contributor

Sorry for the late reply. We are finalizing our 100.8 release and just finished up testing. Should be uploaded to the developers site soon for download....

Anyway, for the markers showing at null island, have you set spatial reference in the Point c'tor?

0 Kudos
BradDuBois
New Contributor II

Excellent, that was the issue.  Now I shall (hopefully) never make that mistake again!  Is it interesting that it didn't affect the placement on the 3D map?  I use the same routeOverlay for both the 2D and 3D maps and only 2D had that problem.

Thanks!

0 Kudos