QgsLayerTreeMapCanvasBridge::QgsLayerTreeMapCanvasBridge( QgsLayerTree *root, QgsMapCanvas *canvas, QObject *parent )
  : QObject( parent )
  , mRoot( root )
  , mCanvas( canvas )
  , mPendingCanvasUpdate( false )
  , mAutoSetupOnFirstLayer( true )
  , mHasLayersLoaded( !root->findLayers().isEmpty() )
{
  connect( root, &QgsLayerTreeGroup::customPropertyChanged, this, &QgsLayerTreeMapCanvasBridge::nodeCustomPropertyChanged );
  connect( root, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeMapCanvasBridge::nodeVisibilityChanged );
  connect( root, &QgsLayerTree::layerOrderChanged, this, &QgsLayerTreeMapCanvasBridge::deferredSetCanvasLayers );

  setCanvasLayers();
}
QgsLayerTreeMapCanvasBridge::QgsLayerTreeMapCanvasBridge( QgsLayerTreeGroup *root, QgsMapCanvas *canvas, QObject* parent )
    : QObject( parent )
    , mRoot( root )
    , mCanvas( canvas )
    , mPendingCanvasUpdate( false )
    , mHasCustomLayerOrder( false )
    , mAutoSetupOnFirstLayer( true )
    , mAutoEnableCrsTransform( true )
    , mLastLayerCount( root->findLayers().count() )
{
  connect( root, SIGNAL( addedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeAddedChildren( QgsLayerTreeNode*, int, int ) ) );
  connect( root, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) );
  connect( root, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeRemovedChildren() ) );
  connect( root, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged() ) );

  setCanvasLayers();
}
LayerTreeMapCanvasBridge::LayerTreeMapCanvasBridge( LayerTreeModel* model, MapSettings* mapSettings, QObject* parent )
  : QObject( parent )
  , mModel( model )
  , mMapSettings( mapSettings )
  , mPendingCanvasUpdate( false )
  , mHasCustomLayerOrder( false )
  , mAutoSetupOnFirstLayer( false )
  , mAutoEnableCrsTransform( true )
  , mNoLayersLoaded( !model->rootGroup()->findLayers().isEmpty() )
{
  connect( model->rootGroup(), &QgsLayerTreeGroup::addedChildren, this, &LayerTreeMapCanvasBridge::nodeAddedChildren );
  connect( model->rootGroup(), &QgsLayerTreeGroup::customPropertyChanged, this, &LayerTreeMapCanvasBridge::nodeCustomPropertyChanged );
  connect( model->rootGroup(), &QgsLayerTreeGroup::removedChildren, this, &LayerTreeMapCanvasBridge::nodeRemovedChildren );
  connect( model->rootGroup(), &QgsLayerTreeGroup::visibilityChanged, this, &LayerTreeMapCanvasBridge::nodeVisibilityChanged );
  connect( model, &LayerTreeModel::mapThemeChanged, this, &LayerTreeMapCanvasBridge::mapThemeChanged );

  setCanvasLayers();
}
void QgsLayerTreeMapCanvasBridge::setCanvasLayers( QgsLayerTreeNode *node, QList<QgsMapLayer *> &canvasLayers, QList<QgsMapLayer *> &overviewLayers, QList<QgsMapLayer *> &allLayers )
{
  if ( QgsLayerTree::isLayer( node ) )
  {
    QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
    if ( nodeLayer->layer() && nodeLayer->layer()->isSpatial() )
    {
      allLayers << nodeLayer->layer();
      if ( nodeLayer->isVisible() )
        canvasLayers << nodeLayer->layer();
      if ( nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() )
        overviewLayers << nodeLayer->layer();
    }
  }

  const QList<QgsLayerTreeNode *> children = node->children();
  for ( QgsLayerTreeNode *child : children )
    setCanvasLayers( child, canvasLayers, overviewLayers, allLayers );
}
void QgsLayerTreeMapCanvasBridge::setCanvasLayers()
{
  QList<QgsMapLayer *> canvasLayers, overviewLayers, allLayerOrder;

  if ( mRoot->hasCustomLayerOrder() )
  {
    const QList<QgsMapLayer *> customOrderLayers = mRoot->customLayerOrder();
    for ( const QgsMapLayer *layer : customOrderLayers )
    {
      QgsLayerTreeLayer *nodeLayer = mRoot->findLayer( layer->id() );
      if ( nodeLayer )
      {
        if ( !nodeLayer->layer()->isSpatial() )
          continue;

        allLayerOrder << nodeLayer->layer();
        if ( nodeLayer->isVisible() )
          canvasLayers << nodeLayer->layer();
        if ( nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() )
          overviewLayers << nodeLayer->layer();
      }
    }
  }
  else
  {
    setCanvasLayers( mRoot, canvasLayers, overviewLayers, allLayerOrder );
  }

  const QList<QgsLayerTreeLayer *> layerNodes = mRoot->findLayers();
  int currentSpatialLayerCount = 0;
  for ( QgsLayerTreeLayer *layerNode : layerNodes )
  {
    if ( layerNode->layer() && layerNode->layer()->isSpatial() )
      currentSpatialLayerCount++;
  }

  bool firstLayers = mAutoSetupOnFirstLayer && !mHasLayersLoaded && currentSpatialLayerCount != 0;

  mCanvas->setLayers( canvasLayers );
  if ( mOverviewCanvas )
    mOverviewCanvas->setLayers( overviewLayers );

  if ( firstLayers )
  {
    // if we are moving from zero to non-zero layers, let's zoom to those data
    mCanvas->zoomToFullExtent();
  }

  if ( !mFirstCRS.isValid() )
  {
    // find out what is the first used CRS in case we may need to turn on OTF projections later
    for ( const QgsLayerTreeLayer *layerNode : layerNodes )
    {
      if ( layerNode->layer() && layerNode->layer()->crs().isValid() )
      {
        mFirstCRS = layerNode->layer()->crs();
        break;
      }
    }
  }

  if ( mFirstCRS.isValid() && firstLayers )
  {
    QgsProject::instance()->setCrs( mFirstCRS );
  }

  mHasLayersLoaded = currentSpatialLayerCount;
  if ( currentSpatialLayerCount == 0 )
    mFirstCRS = QgsCoordinateReferenceSystem();

  mPendingCanvasUpdate = false;

  emit canvasLayersChanged( canvasLayers );
}