// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
//
// This file is part of SharpMap.
// SharpMap is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// SharpMap is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public License
// along with SharpMap; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using DelftTools.Utils.Aop;
using DelftTools.Utils.Collections;
using DelftTools.Utils.Collections.Generic;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using SharpMap.Api;
using SharpMap.Api.Layers;
namespace SharpMap.Layers
{
///
/// Class for holding a group of layers.
///
///
/// The Group layer is useful for grouping a set of layers,
/// for instance a set of image tiles, and expose them as a single layer
///
[Entity(FireOnCollectionChange = false)]
//[NotifyPropertyChanged(AttributeTargetMembers = "SharpMap.Layers.LayerGroup.Map", AttributeExclude = true, AttributePriority = 2)]
public class GroupLayer : Layer, IGroupLayer //, IDisposable, INotifyCollectionChange
{
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
public virtual event NotifyCollectionChangingEventHandler CollectionChanging;
protected bool layersReadOnly;
private readonly bool created = false;
private IEventedList layers;
private bool isMapInitialized; // performance (lazy initialization)
private bool cloning;
public GroupLayer() : this("group layer")
{
FeatureEditor = null;
}
///
/// Initializes a new group layer
///
/// Name of layer
public GroupLayer(string layername)
{
Layers = new EventedList();
Name = layername;
created = true;
}
[NoNotifyPropertyChange]
public override bool RenderRequired
{
get
{
if (!created)
{
return false;
}
if (Map != null && Map.IsDisposing)
{
return false;
}
/* If subLayer needs redrawing the grouplayer needs redrawing.
* test with moving cross section along a branch. */
foreach (ILayer layer in Layers.Where(l => l.Visible))
{
if (layer.RenderRequired)
{
return true;
}
}
/**/
return base.RenderRequired;
}
set
{
if (!created)
{
return;
}
if (Map != null && Map.IsDisposing)
{
return;
}
/**/
if (value) //due to render order, propagating render required false seems wrong
{
foreach (ILayer layer in Layers.Where(l => l.Visible))
{
layer.RenderRequired = value;
}
}
/**/
base.RenderRequired = value;
}
}
[NoNotifyPropertyChange]
public override IMap Map
{
get
{
return base.Map;
}
set
{
base.Map = value;
isMapInitialized = false;
}
}
///
/// Sublayers in the group
///
public virtual IEventedList Layers
{
get
{
if (!isMapInitialized)
{
isMapInitialized = true;
foreach (ILayer layer in Layers)
{
layer.Map = Map;
}
}
return layers;
}
set
{
if (layers != null)
{
layers.CollectionChanged -= LayersCollectionChanged;
layers.CollectionChanging -= LayersCollectionChanging;
}
layers = value;
if (layers != null)
{
layers.CollectionChanged += LayersCollectionChanged;
layers.CollectionChanging += LayersCollectionChanging;
}
}
}
public virtual bool LayersReadOnly
{
get
{
return layersReadOnly;
}
set
{
layersReadOnly = value;
}
}
///
/// Returns the extent of the layer
///
/// Bounding box corresponding to the extent of the features in the layer
public override IEnvelope Envelope
{
get
{
if (Layers.Count == 0)
{
return null;
}
IEnvelope envelope = new Envelope();
foreach (var layer in Layers.Where(l => l.Visible && !l.ExcludeFromMapExtent))
{
var subEnvelope = layer.Envelope;
if (subEnvelope == null || subEnvelope.IsNull)
{
continue;
}
envelope.ExpandToInclude(subEnvelope);
}
return envelope;
}
}
bool INotifyCollectionChange.SkipChildItemEventBubbling { get; set; }
public virtual IEnumerable GetAllLayers(bool includeGroupLayers)
{
return SharpMap.Map.GetLayers(Layers, includeGroupLayers, true);
}
///
/// Renders the layer
///
/// Graphics object reference
/// Map which is rendered
public override void OnRender(Graphics g, IMap map)
{
// layers of the group layer are rendered by themselves
}
public override void ClearImage()
{
base.ClearImage();
foreach (var layer in layers)
{
layer.ClearImage();
}
}
///
/// Clones the layer
///
/// cloned object
public override object Clone()
{
var clonedLayerGroup = (GroupLayer) Activator.CreateInstance(GetType());
clonedLayerGroup.cloning = true;
clonedLayerGroup.name = name;
clonedLayerGroup.NameIsReadOnly = NameIsReadOnly;
//clonedLayerGroup.LayersReadOnly = false;
clonedLayerGroup.layers.Clear();
foreach (var layer in layers)
{
clonedLayerGroup.layers.Add((ILayer) layer.Clone());
}
clonedLayerGroup.Visible = Visible;
clonedLayerGroup.LayersReadOnly = LayersReadOnly;
clonedLayerGroup.cloning = false;
return clonedLayerGroup;
}
///
/// Disposes the object
///
public override void Dispose(bool disposeDataSource)
{
var disposables = Layers.OfType();
foreach (var disposable in disposables)
{
var layer = disposable as Layer;
if (layer != null)
{
layer.Dispose(disposeDataSource);
}
else
{
disposable.Dispose();
}
}
}
protected void LayersCollectionChanged(object sender, NotifyCollectionChangingEventArgs e)
{
// performance
if (!created || cloning)
{
return;
}
OnLayersCollectionChanged(e);
if (CollectionChanged != null)
{
CollectionChanged(sender, e);
}
}
protected override void OnLayerPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!created || cloning)
{
return;
}
//skip the render required logic for group layers...layers handle this individually
//group layer should only react to local changes and renderrequired of child layers
//if not it will result in a lot of loading exceptions during save/load
if (ReferenceEquals(sender, this))
{
base.OnLayerPropertyChanged(sender, e); //handle like a 'normal' layer
}
}
private void OnLayersCollectionChanged(NotifyCollectionChangingEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangeAction.Add: //set map property for layers being added
((ILayer) e.Item).Map = Map;
((ILayer) e.Item).RenderRequired = true;
break;
case NotifyCollectionChangeAction.Remove:
RenderRequired = true; //render the group if a layer got removed.
break;
case NotifyCollectionChangeAction.Replace:
throw new NotImplementedException();
}
}
private void LayersCollectionChanging(object sender, NotifyCollectionChangingEventArgs e)
{
// performance
if (!created || cloning)
{
return;
}
if (sender == layers) //only for own layer collection
{
if (LayersReadOnly)
{
throw new InvalidOperationException("It is not allowed to add or remove layers from a grouplayer that has a read-only layers collection");
}
}
if (CollectionChanging != null)
{
CollectionChanging(sender, e);
}
}
}
}