#region Disclaimer / License
// Copyright (C) 2010, Jackie Ng
// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com
//
// This library 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.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using OSGeo.MapGuide.MaestroAPI.Services;
using OSGeo.MapGuide.ObjectModels.Common;
using OSGeo.MapGuide.ObjectModels.MapDefinition;
using OSGeo.MapGuide.ObjectModels;
using OSGeo.MapGuide.MaestroAPI.Serialization;
using OSGeo.MapGuide.MaestroAPI.Resource;
using System.ComponentModel;
using OSGeo.MapGuide.ObjectModels.LayerDefinition;
using System.Diagnostics;
using OSGeo.MapGuide.MaestroAPI.Commands;
namespace OSGeo.MapGuide.MaestroAPI.Mapping
{
//TODO: Verify the code examples here :)
///
/// Represents a runtime instance of a Map Definition
///
///
/// If you want to use this instance with the Rendering Service APIs, it is important to set the correct
/// meters per unit value before calling the method, as an incorrect meters
/// per unit value will produce incorrect images.
///
/// Also note that to improve the creation performance, certain implementations of
/// offer a helper to return a series of layer definitions in a batch (fetching
/// layer definitions one at a time is the main performance bottleneck for large maps), batching can improve creation times by:
///
/// - HTTP: 2x
/// - Local: 3x (if using MapGuide 2.2 APIs. As this takes advantage of the GetResourceContents() API introduced in 2.2). For older versions of MapGuide there is no batching.
///
/// In particular, the HTTP implementation of uses the
/// class to fetch multiple layer definitions in parallel. If your code uses this implementation, be aware of this face and the performance implications
/// involved, as an excessively large thread pool size may negatively affect stability of your MapGuide Server.
///
///
///
/// How to create a with the correct meters per unit value using the MgCoordinateSystem API
///
///
/// IServerConnection conn = ConnectionProviderRegistry.CreateConnection("Maestro.Http",
/// "Username", "Administrator",
/// "Password", "admin",
/// "Url", "http://localhost/mapguide/mapagent/mapagent.fcgi");
///
/// //Create the Mapping Service. Some implementations of IServerConnection may not support this service, so
/// //its best to inspect the capability object of this connection to determine if this service type is supported
/// IMappingService mapSvc = (IMappingService)conn.GetService((int)ServiceType.Mapping);
///
/// //Get our map definition
/// ResourceIdentifier resId = new ResourceIdentifier("Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition");
/// IMapDefinition mdf = (IMapDefinition)conn.ResourceService.GetResource(resId.ToString());
///
/// //Calculate the meters per unit value, this requires the official MapGuide API. Otherwise, you need
/// //to know this value up-front in order to render images with this instance
/// double metersPerUnit = 1.0;
/// if (!string.IsNullOrEmpty(mdf.CoordinateSystem))
/// {
/// MgCoordinateSystemFactory factory = new MgCoordinateSystemFactory();
/// MgCoordinateSystem cs = factory.Create(mdf.CoordinateSystem);
/// metersPerUnit = cs.ConvertCoordinateSystemUnitsToMeters(1.0);
/// }
///
/// //Generate our runtime map resource id. This must be session-based
/// ResourceIdentifier rtMapId = new ResourceIdentifier(resId.Name, ResourceTypes.RuntimeMap, conn.SessionID);
///
/// //Create the runtime map using our meters per unit value
/// RuntimeMap map = mapSvc.CreateRuntimeMap(rtMapId, mdf, metersPerUnit);
///
/// //Set some display parameters for this map
/// map.DisplayWidth = 1024;
/// map.DisplayHeight = 1024;
/// map.DisplayDpi = 96;
///
/// //We have to save it first before we can render from it or use any other API that requires this
/// //current map state. Remember to call Save() everytime you change the state of the map
/// map.Save();
///
/// //Now we can render a map
/// using(Stream stream = mapSvc.RenderDynamicOverlay(map, null, "PNG"))
/// {
/// //Write this stream out to a file
/// using (var fs = new FileStream("RenderMap.png", FileMode.OpenOrCreate))
/// {
/// int read = 0;
/// do
/// {
/// read = source.Read(buf, 0, buf.Length);
/// target.Write(buf, 0, read);
/// } while (read > 0);
/// }
/// }
///
///
///
public class RuntimeMap : MapObservable
{
internal IFeatureService FeatureService { get; set; }
internal IResourceService ResourceService { get; set; }
internal Version SiteVersion { get; private set; }
///
/// The layer collection
///
protected List _layers;
///
/// The group collection
///
protected List _groups;
///
/// The id to layer lookup dictionary
///
protected Dictionary _layerIdMap;
private IMappingService _mapSvc;
private IGetResourceContents _getRes;
internal RuntimeMap(IServerConnection conn)
{
_disableChangeTracking = true;
this.SiteVersion = conn.SiteVersion;
this.SessionId = conn.SessionID;
this.ObjectId = Guid.NewGuid().ToString();
m_changeList = new Dictionary();
_finiteDisplayScales = new double[0];
this.ResourceService = conn.ResourceService;
this.FeatureService = conn.FeatureService;
if (Array.IndexOf(conn.Capabilities.SupportedServices, (int)ServiceType.Mapping) >= 0)
{
_mapSvc = (IMappingService)conn.GetService((int)ServiceType.Mapping);
}
if (Array.IndexOf(conn.Capabilities.SupportedCommands, (int)CommandType.GetResourceContents) >= 0)
{
_getRes = (IGetResourceContents)conn.CreateCommand((int)CommandType.GetResourceContents);
}
_layers = new List();
_groups = new List();
_layerIdMap = new Dictionary();
this.Selection = new MapSelection(this);
}
static IEnumerable GetLayerIds(IMapDefinition mdf)
{
foreach (var layer in mdf.MapLayer)
{
yield return layer.ResourceId;
}
if (mdf.BaseMap != null)
{
foreach (var group in mdf.BaseMap.BaseMapLayerGroup)
{
foreach (var layer in group.BaseMapLayer)
{
yield return layer.ResourceId;
}
}
}
}
///
/// Initializes a new instance of the class.
///
/// The map definition to create this map from.
/// The meters per unit value
internal RuntimeMap(IMapDefinition mdf, double metersPerUnit)
: this(mdf.CurrentConnection)
{
this.MetersPerUnit = metersPerUnit;
this.MapDefinition = mdf.ResourceID;
this.MapExtent = mdf.Extents.Clone();
this.DataExtent = mdf.Extents.Clone();
this.BackgroundColor = mdf.BackgroundColor;
this.CoordinateSystem = mdf.CoordinateSystem;
//TODO: infer real mpu from coordinate system
//If a setup helper exists, use it to get required layers in a single
//batch. Eliminating lots of chatter for really large maps.
if (_getRes != null)
{
Trace.TraceInformation("[RuntimeMap.ctor]: Batching layer requests");
var res = _getRes.Execute(GetLayerIds(mdf));
//Pre-populate layer def cache so GetLayerDefinition() returns these
//instead of making a new request
foreach (var key in res.Keys)
{
var layer = res[key] as ILayerDefinition;
if (layer != null)
layerDefinitionCache.Add(key, layer);
}
Trace.TraceInformation("[RuntimeMap.ctor]: {0} layers pre-cached", layerDefinitionCache.Count);
}
int dispIndex = 0;
//Load map layers
foreach (var layer in mdf.MapLayer)
{
var rtl = new RuntimeMapLayer(this, layer, GetLayerDefinition(layer.ResourceId));
rtl.DisplayOrder = (++dispIndex) * 1000;
AddLayerInternal(rtl);
}
//Load map groups
foreach (var group in mdf.MapLayerGroup)
{
var grp = new RuntimeMapGroup(this, group);
_groups.Add(grp);
}
//If base map specified load layers and groups there
if (mdf.BaseMap != null)
{
var bm = mdf.BaseMap;
foreach (var group in bm.BaseMapLayerGroup)
{
if (group.HasLayers())
{
foreach (var layer in group.BaseMapLayer)
{
var rtl = new RuntimeMapLayer(this, layer, GetLayerDefinition(layer.ResourceId)) { Visible = true };
rtl.Type = RuntimeMapLayer.kBaseMap;
rtl.DisplayOrder = (++dispIndex) * 1000;
AddLayerInternal(rtl);
}
}
var rtg = new RuntimeMapGroup(this, group);
_groups.Add(rtg);
}
//Init finite display scales
if (bm.ScaleCount > 0)
{
_finiteDisplayScales = new double[bm.ScaleCount];
for (int i = 0; i < bm.ScaleCount; i++)
{
_finiteDisplayScales[i] = bm.GetScaleAt(i);
}
}
}
this.LayerRefreshMode = 1;
this.ViewScale = 1.0; //TODO: Calc from extents and other parameters
this.DisplayDpi = 96;
this.DisplayWidth = 0;
this.DisplayHeight = 0;
this.ViewCenter = this.DataExtent.Center();
_disableChangeTracking = false;
}
///
/// Gets or sets the map extents.
///
/// The map extents.
public IEnvelope MapExtent
{
get;
private set;
}
///
/// The data extent
///
protected IEnvelope _dataExtent;
///
/// Gets or sets the data extent.
///
/// The data extent.
public IEnvelope DataExtent
{
get
{
return _dataExtent;
}
set
{
if (value == null)
throw new ArgumentNullException();
if (_dataExtent == null)
{
_dataExtent = value;
}
else
{
_dataExtent.MaxX = value.MaxX;
_dataExtent.MaxY = value.MaxY;
_dataExtent.MinX = value.MinX;
_dataExtent.MinY = value.MinY;
}
}
}
///
/// The dpi
///
protected int _dpi;
///
/// Gets or sets the display dpi.
///
/// The display dpi.
public int DisplayDpi
{
get
{
return _dpi;
}
set
{
SetField(ref _dpi, value, "DisplayDpi");
}
}
///
/// The display height
///
protected int _dispHeight;
///
/// Gets or sets the display height.
///
/// The display height.
public int DisplayHeight
{
get
{
return _dispHeight;
}
set
{
_dispHeight = value;
}
}
///
/// The display width
///
protected int _dispWidth;
///
/// Gets or sets the display width.
///
/// The display width.
public int DisplayWidth
{
get
{
return _dispWidth;
}
set
{
_dispWidth = value;
}
}
///
/// Gets or sets the map definition resource id
///
/// The map definition resource id.
public string MapDefinition
{
get;
internal set;
}
///
/// Gets or sets the object id.
///
/// The object id.
public string ObjectId
{
get;
internal set;
}
///
/// Gets or sets the session id.
///
/// The session id.
public string SessionId
{
get;
internal set;
}
///
/// The view center
///
protected IPoint2D _viewCenter;
///
/// Gets or sets the view center.
///
/// The view center.
public IPoint2D ViewCenter
{
get
{
return _viewCenter;
}
set
{
_viewCenter = value;
}
}
///
/// The view scale
///
protected double _viewScale;
///
/// Gets or sets the view scale.
///
/// The view scale.
public double ViewScale
{
get
{
return _viewScale;
}
set
{
SetField(ref _viewScale, value, "ViewScale");
}
}
///
/// The name of the map
///
protected string _name;
///
/// Gets or sets the name.
///
/// The name.
public string Name
{
get { return _name; }
set { SetField(ref _name, value, "Name"); }
}
///
/// The Coordinate System WKT of the map
///
protected string _mapSrs;
///
/// Gets or sets the coordinate system in WKT format
///
/// The coordinate system in WKT format.
public string CoordinateSystem
{
get { return _mapSrs; }
internal set { SetField(ref _mapSrs, value, "CoordinateSystem"); }
}
///
/// The background color of the map
///
protected System.Drawing.Color _bgColor;
///
/// Gets or sets the color of the background.
///
/// The color of the background.
public System.Drawing.Color BackgroundColor
{
get
{
return _bgColor;
}
set
{
SetField(ref _bgColor, value, "BackgroundColor");
}
}
private string _resId;
///
/// Gets or sets the resource ID. When setting, if the name of this map has
/// not been specified already, the name will be set based on this resource id
///
/// The resource ID.
public string ResourceID
{
get { return _resId; }
set
{
SetField(ref _resId, value, "ResourceID");
if (this.Name == null)
this.Name = ResourceIdentifier.GetName(_resId);
}
}
///
/// Gets the type of the resource.
///
/// The type of the resource.
public ResourceTypes ResourceType
{
get { return ResourceTypes.RuntimeMap; }
}
///
/// Gets the meters per unit value.
///
/// The meters per unit.
public double MetersPerUnit
{
get;
internal set;
}
///
/// MapGuide internal value
///
protected const int MgBinaryVersion = 262144; //1;
///
/// MapGuide internal class id
///
protected const int ClassId = 11500; //30500;
///
/// Gets the layer refresh mode.
///
/// The layer refresh mode.
public int LayerRefreshMode
{
get;
private set;
}
private double[] _finiteDisplayScales;
///
/// Serializes this instance to the specified binary stream
///
///
public void Serialize(MgBinarySerializer s)
{
if (s.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
{
s.Write(MgBinaryVersion);
s.WriteResourceIdentifier(this.ResourceID);
}
s.Write(this.Name);
s.Write(this.ObjectId);
s.WriteResourceIdentifier(this.MapDefinition);
s.Write(this.CoordinateSystem);
//base.m_extents.Serialize(s);
SerializeExtent(this.MapExtent, s);
s.WriteCoordinates(new double[] { this.ViewCenter.X, this.ViewCenter.Y }, 0);
s.Write(this.ViewScale);
SerializeExtent(this.DataExtent, s);
s.Write(this.DisplayDpi);
s.Write(this.DisplayWidth);
s.Write(this.DisplayHeight);
s.Write(Utility.SerializeHTMLColor(this.BackgroundColor, true));
s.Write(this.MetersPerUnit);
if (s.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
s.Write(this.LayerRefreshMode);
s.Write(_finiteDisplayScales.Length);
foreach (double d in _finiteDisplayScales)
s.Write(d);
if (s.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
{
SerializeChangeMap(s);
s.Write((int)0);
}
else
{
SerializeLayerData(s);
SerializeChangeMap(s);
}
}
private static void SerializeExtent(IEnvelope env, MgBinarySerializer s)
{
if (s.SiteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1))
s.WriteClassId(18001);
else
s.WriteClassId(20001);
s.Write((int)0);
s.Write(env.MinX);
s.Write(env.MinY);
s.Write(env.MaxY);
s.Write(env.MaxY);
}
private Dictionary m_changeList;
internal class Change
{
public enum ChangeType
{
removed,
added,
visibilityChanged,
displayInLegendChanged,
legendLabelChanged,
parentChanged,
selectabilityChanged,
definitionChanged
};
public ChangeType Type { get; private set; }
public string Params { get; private set; }
public Change()
{
}
public Change(ChangeType type, string param)
{
this.Type = type;
this.Params = param;
}
}
internal class ChangeList
{
public string ObjectId { get; private set; }
public bool IsLayer { get; private set; }
public List Changes { get; private set; }
public ChangeList()
{
this.Changes = new List();
}
public ChangeList(string objectId, bool isLayer)
: this()
{
this.ObjectId = objectId;
this.IsLayer = isLayer;
}
}
private void SerializeChangeMap(MgBinarySerializer s)
{
s.Write(m_changeList.Count);
foreach (ChangeList cl in m_changeList.Values)
{
s.Write(cl.IsLayer);
s.Write(cl.ObjectId);
s.Write(cl.Changes.Count);
foreach (Change c in cl.Changes)
{
s.Write((int)c.Type);
s.Write(c.Params);
}
}
}
///
/// Serializes the layer data to the specified binary stream
///
///
protected void SerializeLayerData(MgBinarySerializer s)
{
s.Write((int)_groups.Count);
foreach (var g in _groups)
g.Serialize(s);
s.Write(_layers.Count);
foreach (var t in _layers)
t.Serialize(s);
}
///
/// Initializes this instance from the specified binary stream
///
///
public void Deserialize(MgBinaryDeserializer d)
{
_disableChangeTracking = true;
if (d.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
{
if (d.ReadInt32() != MgBinaryVersion)
throw new Exception("Invalid map version");
this.ResourceID = d.ReadResourceIdentifier();
}
this.Name = d.ReadString();
this.ObjectId = d.ReadString();
this.MapDefinition = d.ReadResourceIdentifier();
this.CoordinateSystem = d.ReadString();
this.MapExtent = DeserializeExtents(d);
var cc = d.ReadCoordinates();
if (this.ViewCenter != null)
{
this.ViewCenter.X = cc[0];
this.ViewCenter.Y = cc[1];
}
else
{
this.ViewCenter = ObjectFactory.CreatePoint2D(cc[0], cc[1]);
}
this.ViewScale = d.ReadDouble();
this.DataExtent = DeserializeExtents(d);
this.DisplayDpi = d.ReadInt32();
this.DisplayWidth = d.ReadInt32();
this.DisplayHeight = d.ReadInt32();
this.BackgroundColor = Utility.ParseHTMLColor(d.ReadString());
this.MetersPerUnit = d.ReadDouble();
if (d.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
this.LayerRefreshMode = d.ReadInt32();
var fds = new List();
int finiteScaleCount = d.ReadInt32();
while (finiteScaleCount-- > 0)
fds.Add(d.ReadDouble());
_finiteDisplayScales = fds.ToArray();
m_changeList = new Dictionary();
if (d.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
{
m_changeList = DeserializeChangeMap(d);
int mapLayerCount = d.ReadInt32();
if (mapLayerCount != 0)
throw new Exception("On new versions, there should be no layer data in map");
}
else
{
//ri.LayerGroupBlob = d.ReadStreamRepeat(d.ReadInt32());
DeserializeLayerData(d);
m_changeList = DeserializeChangeMap(d);
}
_disableChangeTracking = false;
}
private static IEnvelope DeserializeExtents(MgBinaryDeserializer d)
{
int classid = d.ReadClassId();
if (d.SiteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 18001)
throw new Exception("Invalid class identifier, expected Box2D");
if (d.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 20001)
throw new Exception("Invalid class identifier, expected Box2D");
int dimensions = d.ReadInt32();
if (dimensions != 2 && dimensions != 0)
throw new Exception("Bounding box for map had " + dimensions.ToString() + " dimensions, 2 was expected");
double x1 = d.ReadDouble();
double y1 = d.ReadDouble();
double x2 = d.ReadDouble();
double y2 = d.ReadDouble();
double minx = Math.Min(x1, x2);
double miny = Math.Min(y1, y2);
double maxx = Math.Max(x1, x2);
double maxy = Math.Max(y1, y2);
return ObjectFactory.CreateEnvelope(minx, miny, maxx, maxy);
}
private Dictionary DeserializeChangeMap(MgBinaryDeserializer d)
{
Dictionary changes = new Dictionary();
int changeListCount = d.ReadInt32();
while (changeListCount-- > 0)
{
bool isLayer = d.ReadByte() > 0;
string objid = d.ReadString();
ChangeList c = null;
if (!changes.ContainsKey(objid))
{
c = new ChangeList(objid, isLayer);
changes.Add(c.ObjectId, c);
}
else
{
c = changes[objid];
}
int changeCount = d.ReadInt32();
while (changeCount-- > 0)
{
//Split up to avoid dependency on argument evaluation order
int ctype = d.ReadInt32();
c.Changes.Add(new Change((Change.ChangeType)ctype, d.ReadString()));
}
}
return changes;
}
internal void DeserializeLayerData(MgBinaryDeserializer d)
{
int groupCount = d.ReadInt32();
_groups.Clear();
for (int i = 0; i < groupCount; i++)
{
RuntimeMapGroup g = new RuntimeMapGroup();
g.Deserialize(d);
_groups.Add(g);
}
int mapLayerCount = d.ReadInt32();
_layers.Clear();
_layerIdMap.Clear();
while (mapLayerCount-- > 0)
{
RuntimeMapLayer t = new RuntimeMapLayer(this);
t.Deserialize(d);
AddLayerInternal(t);
}
}
///
/// Gets the selection set
///
/// The selection.
public MapSelection Selection
{
get;
private set;
}
///
/// Gets the group by its specified name
///
/// The name.
///
public RuntimeMapGroup GetGroupByName(string name)
{
Check.NotNull(name, "name");
foreach (var group in _groups)
{
if (name.Equals(group.Name))
return group;
}
return null;
}
///
/// Gets the layer by object id.
///
/// The id.
///
public RuntimeMapLayer GetLayerByObjectId(string id)
{
if (_layerIdMap.ContainsKey(id))
return _layerIdMap[id];
return null;
}
///
/// Gets an array of all layers in this map
///
/// The layers.
public RuntimeMapLayer[] Layers
{
get { return _layers.ToArray(); }
}
///
/// Gets an array of all groups in this map
///
/// The groups.
public RuntimeMapGroup[] Groups
{
get { return _groups.ToArray(); }
}
///
/// A cache of Layer Definition objects. Used to reduce lookup time of the same layer definitions
///
protected Dictionary layerDefinitionCache = new Dictionary();
///
/// Creates and adds the layer to the map
///
/// The layer definition id.
/// The group.
///
public RuntimeMapLayer AddLayer(string layerDefinitionId, RuntimeMapGroup group)
{
ILayerDefinition ldf = GetLayerDefinition(layerDefinitionId);
var layer = new RuntimeMapLayer(this, ldf);
AddLayerInternal(layer);
return layer;
}
private ILayerDefinition GetLayerDefinition(string layerDefinitionId)
{
ILayerDefinition ldf = null;
if (layerDefinitionCache.ContainsKey(layerDefinitionId))
{
ldf = layerDefinitionCache[layerDefinitionId];
}
else
{
ResourceIdentifier.Validate(layerDefinitionId, ResourceTypes.LayerDefinition);
ldf = (ILayerDefinition)this.ResourceService.GetResource(layerDefinitionId);
layerDefinitionCache[layerDefinitionId] = ldf;
}
return ldf;
}
///
/// Adds the layer.
///
/// The layer.
protected void AddLayerInternal(RuntimeMapLayer layer)
{
_layers.Add(layer);
_layerIdMap[layer.ObjectId] = layer;
OnLayerAdded(layer);
}
///
/// Creates the group and adds it to the map
///
/// The name.
///
public RuntimeMapGroup AddGroup(string name)
{
var group = new RuntimeMapGroup(this, name);
AddGroupInternal(group);
return group;
}
private void AddGroupInternal(RuntimeMapGroup group)
{
_groups.Add(group);
OnGroupAdded(group);
}
///
/// Removes the layer
///
/// The layer.
protected void RemoveLayerInternal(RuntimeMapLayer layer)
{
if (_layers.Remove(layer))
{
OnLayerRemoved(layer);
}
}
///
/// Removes the specified layer.
///
/// The layer.
public void RemoveLayer(RuntimeMapLayer layer)
{
Check.NotNull(layer, "layer");
RemoveLayerInternal(layer);
}
///
/// Removes the specified group.
///
/// The group.
public void RemoveGroup(RuntimeMapGroup group)
{
Check.NotNull(group, "group");
RemoveGroupInternal(group);
}
private void RemoveGroupInternal(RuntimeMapGroup group)
{
if (_groups.Remove(group))
{
OnGroupRemoved(group);
}
}
///
/// Gets the layers of the specified group
///
/// Name of the group.
///
public RuntimeMapLayer[] GetLayersOfGroup(string groupName)
{
Check.NotEmpty(groupName, "groupName");
List layers = new List();
foreach (var lyr in _layers)
{
if (groupName.Equals(lyr.Group))
layers.Add(lyr);
}
return layers.ToArray();
}
///
/// Saves this instance. The changes are propagated back to the MapGuide Server
///
public virtual void Save()
{
Save(this.ResourceID);
}
///
/// A dummy resource, used for the runtime map
///
internal const string RUNTIMEMAP_XML = "";
///
/// A dummy resource, used for the runtime map
///
internal const string RUNTIMEMAP_SELECTION_XML = "";
private void Save(string resourceID)
{
var map = this;
string selectionID = resourceID.Substring(0, resourceID.LastIndexOf(".")) + ".Selection";
this.ResourceService.SetResourceXmlData(resourceID, new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(RUNTIMEMAP_XML)));
this.ResourceService.SetResourceXmlData(selectionID, new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(RUNTIMEMAP_SELECTION_XML)));
ResourceIdentifier.Validate(resourceID, ResourceTypes.RuntimeMap);
if (!resourceID.StartsWith("Session:" + this.SessionId + "//") || !resourceID.EndsWith(".Map"))
throw new Exception("Runtime maps must be in the current session repository");
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.IO.MemoryStream ms2 = null;
//Apparently the name is used to reconstruct the resourceId rather than pass it around
//inside the map server
string r = map.Name;
string t = map.ResourceID;
string mapname = resourceID.Substring(resourceID.IndexOf("//") + 2);
mapname = mapname.Substring(0, mapname.LastIndexOf("."));
map.Name = mapname;
map.ResourceID = resourceID;
try
{
map.Serialize(new MgBinarySerializer(ms, this.SiteVersion));
if (this.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2))
{
ms2 = new System.IO.MemoryStream();
map.SerializeLayerData(new MgBinarySerializer(ms2, this.SiteVersion));
}
this.ResourceService.SetResourceData(resourceID, "RuntimeData", ResourceDataType.Stream, ms);
if (ms2 != null)
this.ResourceService.SetResourceData(resourceID, "LayerGroupData", ResourceDataType.Stream, ms2);
SaveSelectionXml(resourceID);
}
finally
{
map.Name = r;
map.ResourceID = t;
}
}
private void SaveSelectionXml(string resourceID)
{
if (this.Selection == null)
return;
ResourceIdentifier.Validate(resourceID, ResourceTypes.RuntimeMap);
string selectionID = resourceID.Substring(0, resourceID.LastIndexOf(".")) + ".Selection";
System.IO.MemoryStream ms = new System.IO.MemoryStream();
MgBinarySerializer serializer = new MgBinarySerializer(ms, this.SiteVersion);
this.Selection.Serialize(serializer);
ms.Position = 0;
this.ResourceService.SetResourceData(selectionID, "RuntimeData", ResourceDataType.Stream, ms);
}
///
/// Gets the layer by its specified name
///
/// The name.
///
public RuntimeMapLayer GetLayerByName(string name)
{
Check.NotEmpty(name, "name");
foreach (var layer in _layers)
{
if (name.Equals(layer.Name))
return layer;
}
return null;
}
#region change tracking
//This mirrors the unmanaged implementation of MgMapBase
//Turns out the 2.x implementation didn't track these map element changes
internal void TrackChange(string objectId, bool isLayer, Change.ChangeType type, string param)
{
if (_disableChangeTracking)
return;
ChangeList changes = null;
if (!m_changeList.ContainsKey(objectId))
{
changes = new ChangeList(objectId, isLayer);
m_changeList.Add(objectId, changes);
}
changes = m_changeList[objectId];
var change = new Change(type, param);
changes.Changes.Add(change);
}
///
/// Clears all tracked changes
///
protected void ClearChanges()
{
m_changeList.Clear();
}
///
/// Called when a group is removed
///
///
protected void OnGroupRemoved(RuntimeMapGroup group)
{
//???
var layers = GetLayersOfGroup(group.Name);
foreach (var lyr in layers)
{
RemoveLayerInternal(lyr);
}
TrackChange(group.ObjectId, false, Change.ChangeType.removed, string.Empty);
}
///
/// Called when a group is added
///
///
protected void OnGroupAdded(RuntimeMapGroup group)
{
//???
TrackChange(group.ObjectId, false, Change.ChangeType.added, string.Empty);
}
internal void OnGroupVisibilityChanged(RuntimeMapGroup group, string visbility)
{
TrackChange(group.ObjectId, false, Change.ChangeType.visibilityChanged, visbility);
//???
}
internal void OnGroupDisplayInLegendChanged(RuntimeMapGroup group, string displayInLegendState)
{
TrackChange(group.ObjectId, false, Change.ChangeType.displayInLegendChanged, displayInLegendState);
}
internal void OnGroupLegendLabelChanged(RuntimeMapGroup group, string legendLabel)
{
TrackChange(group.ObjectId, false, Change.ChangeType.legendLabelChanged, legendLabel);
}
internal void OnGroupParentChanged(RuntimeMapGroup group, string parentId)
{
TrackChange(group.ObjectId, false, Change.ChangeType.parentChanged, parentId);
}
internal void OnLayerRemoved(RuntimeMapLayer layer)
{
//???
if (_layerIdMap.ContainsKey(layer.ObjectId))
_layerIdMap.Remove(layer.ObjectId);
TrackChange(layer.ObjectId, true, Change.ChangeType.removed, string.Empty);
}
internal void OnLayerAdded(RuntimeMapLayer layer)
{
//???
TrackChange(layer.ObjectId, true, Change.ChangeType.added, string.Empty);
}
internal void OnLayerVisibilityChanged(RuntimeMapLayer layer, string visibility)
{
//???
TrackChange(layer.ObjectId, true, Change.ChangeType.visibilityChanged, visibility);
}
internal void OnLayerDisplayInLegendChanged(RuntimeMapLayer layer, string displayInLegendState)
{
TrackChange(layer.ObjectId, true, Change.ChangeType.displayInLegendChanged, displayInLegendState);
}
internal void OnLayerLegendLabelChanged(RuntimeMapLayer layer, string legendLabel)
{
TrackChange(layer.ObjectId, true, Change.ChangeType.legendLabelChanged, legendLabel);
}
internal void OnLayerParentChanged(RuntimeMapLayer layer, string parentId)
{
TrackChange(layer.ObjectId, true, Change.ChangeType.parentChanged, parentId);
}
internal void OnLayerSelectabilityChanged(RuntimeMapLayer layer, string selectability)
{
TrackChange(layer.ObjectId, true, Change.ChangeType.selectabilityChanged, selectability);
}
internal void OnLayerDefinitionChanged(RuntimeMapLayer layer)
{
TrackChange(layer.ObjectId, true, Change.ChangeType.definitionChanged, string.Empty);
}
#endregion
#region convenience methods
///
/// Convenience method for rendering a bitmap of the current map
///
///
///
public System.IO.Stream Render(string format)
{
if (_mapSvc == null)
throw new NotSupportedException();
return _mapSvc.RenderRuntimeMap(
this.ResourceID,
this.ViewCenter.X,
this.ViewCenter.Y,
this.ViewScale,
this.DisplayWidth,
this.DisplayHeight,
this.DisplayDpi,
format);
}
///
/// Convenience method for rendering a dynamic overlay of the current map
///
///
///
///
public System.IO.Stream RenderDynamicOverlay(string format, bool keepSelection)
{
if (_mapSvc == null)
throw new NotSupportedException();
return _mapSvc.RenderDynamicOverlay(
this,
this.Selection,
format,
keepSelection);
}
#endregion
}
}