#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.ObjectModels.MapDefinition; using OSGeo.MapGuide.ObjectModels.LayerDefinition; using OSGeo.MapGuide.MaestroAPI.Serialization; using OSGeo.MapGuide.MaestroAPI.Resource; using OSGeo.MapGuide.ObjectModels.FeatureSource; using OSGeo.MapGuide.MaestroAPI.Feature; using System.Diagnostics; namespace OSGeo.MapGuide.MaestroAPI.Mapping { /// /// Describes a FDO property /// public class PropertyInfo { /// /// Initializes a new instance of the class. /// /// The name. /// The type. public PropertyInfo(string name, Type type) { this.Name = name; this.Type = type; } /// /// Gets or sets the name. /// /// The name. public string Name { get; set; } /// /// Gets or sets the type. /// /// The type. public Type Type { get; set; } } /// /// Represents a runtime map layer. Use to create /// instances of this class. /// public class RuntimeMapLayer : MapObservable { /// /// Represents a scale range /// public class ScaleRange { internal ScaleRange(double minVal, double maxVal) { this.MinScale = minVal; this.MaxScale = maxVal; } /// /// Gets the min scale. /// public double MinScale { get; private set; } /// /// Gets the max scale. /// public double MaxScale { get; private set; } } //From MgLayerType internal const int kBaseMap = 2; internal const int kDynamic = 1; const double InfinityScale = double.MaxValue; /// /// Gets the that this layer belongs to /// public RuntimeMap Parent { get; private set; } internal RuntimeMapLayer(RuntimeMap parent) { _scaleRanges = new double[] { 0.0, InfinityScale }; _type = kDynamic; this.IdentityProperties = new PropertyInfo[0]; _objectId = Guid.NewGuid().ToString(); this.Parent = parent; _group = string.Empty; } /// /// Initializes a new instance of the class. /// /// The parent. /// The LDF. internal RuntimeMapLayer(RuntimeMap parent, ILayerDefinition ldf) : this(parent) { Check.NotNull(ldf, "ldf"); this.LayerDefinitionID = ldf.ResourceID; if (ldf.SubLayer.LayerType == LayerType.Vector) { var vl = ((IVectorLayerDefinition)ldf.SubLayer); _qualifiedClassName = vl.FeatureName; _geometryPropertyName = vl.Geometry; _featureSourceId = vl.ResourceId; _filter = vl.Filter; InitIdentityProperties(vl); InitScaleRanges(vl); _hasTooltips = !string.IsNullOrEmpty(vl.ToolTip); } else if (ldf.SubLayer.LayerType == LayerType.Raster) { var rl = ((IRasterLayerDefinition)ldf.SubLayer); _qualifiedClassName = rl.FeatureName; _geometryPropertyName = rl.Geometry; _featureSourceId = rl.ResourceId; InitScaleRanges(rl); } else if (ldf.SubLayer.LayerType == LayerType.Drawing) { var dl = ((IDrawingLayerDefinition)ldf.SubLayer); _featureSourceId = dl.ResourceId; _scaleRanges = new double[] { dl.MinScale, dl.MaxScale }; EnsureOrderedMinMaxScales(); } _expandInLegend = false; this.Name = ResourceIdentifier.GetName(ldf.ResourceID); _legendLabel = this.Name; _selectable = true; _showInLegend = true; _visible = true; _disableChangeTracking = false; } private void InitScaleRanges(IRasterLayerDefinition rl) { List scales = new List(); foreach (var gsr in rl.GridScaleRange) { if (gsr.MinScale.HasValue) scales.Add(gsr.MinScale.Value); else scales.Add(0.0); if (gsr.MaxScale.HasValue) scales.Add(gsr.MaxScale.Value); else scales.Add(InfinityScale); } _scaleRanges = scales.ToArray(); EnsureOrderedMinMaxScales(); } private void InitScaleRanges(IVectorLayerDefinition vl) { List scales = new List(); foreach (var vsr in vl.VectorScaleRange) { if (vsr.MinScale.HasValue) scales.Add(vsr.MinScale.Value); else scales.Add(0.0); if (vsr.MaxScale.HasValue) scales.Add(vsr.MaxScale.Value); else scales.Add(InfinityScale); } _scaleRanges = scales.ToArray(); EnsureOrderedMinMaxScales(); } /// /// Initializes a new instance of the class. /// /// The parent. /// The source. internal RuntimeMapLayer(RuntimeMap parent, IMapLayer source) : this(parent, source, (ILayerDefinition)parent.CurrentConnection.ResourceService.GetResource(source.ResourceId)) { _disableChangeTracking = false; } internal RuntimeMapLayer(RuntimeMap parent, IMapLayer source, ILayerDefinition ldf) : this(parent, (IBaseMapLayer)source, ldf) { _disableChangeTracking = true; this.Group = source.Group; _visible = source.Visible; _disableChangeTracking = false; } internal RuntimeMapLayer(RuntimeMap parent, IBaseMapLayer source, ILayerDefinition ldf) : this(parent, ldf) { Check.NotNull(source, "source"); Check.NotNull(ldf, "ldf"); Check.Precondition(source.ResourceId == ldf.ResourceID, "source.ResourceId == ldf.ResourceID"); _disableChangeTracking = true; this.LayerDefinitionID = source.ResourceId; _expandInLegend = source.ExpandInLegend; this.Name = source.Name; _selectable = source.Selectable; _showInLegend = source.ShowInLegend; _legendLabel = source.LegendLabel; _needsRefresh = false; _displayOrder = 0; switch (ldf.SubLayer.LayerType) { case LayerType.Drawing: { IDrawingLayerDefinition dl = (IDrawingLayerDefinition)ldf.SubLayer; _featureSourceId = dl.ResourceId; _scaleRanges = new double[] { dl.MinScale, dl.MaxScale }; EnsureOrderedMinMaxScales(); } break; case LayerType.Raster: { IRasterLayerDefinition rdf = (IRasterLayerDefinition)ldf.SubLayer; _featureSourceId = rdf.ResourceId; _geometryPropertyName = rdf.Geometry; _qualifiedClassName = rdf.FeatureName; InitScaleRanges(rdf); } break; case LayerType.Vector: { IVectorLayerDefinition vld = (IVectorLayerDefinition)ldf.SubLayer; _featureSourceId = vld.ResourceId; _geometryPropertyName = vld.Geometry; _qualifiedClassName = vld.FeatureName; _filter = vld.Filter; InitScaleRanges(vld); _hasTooltips = !string.IsNullOrEmpty(vld.ToolTip); //get identity property information InitIdentityProperties(vld); } break; } EnsureOrderedMinMaxScales(); _disableChangeTracking = false; } private void EnsureOrderedMinMaxScales() { Debug.Assert(_scaleRanges.Length % 2 == 0); int scaleCount = _scaleRanges.Length / 2; for (int i = 0; i < scaleCount; i++) { int minPos = i * 2; int maxPos = i * 2 + 1; if (_scaleRanges[minPos] > _scaleRanges[maxPos]) { double temp = _scaleRanges[minPos]; _scaleRanges[minPos] = _scaleRanges[maxPos]; _scaleRanges[maxPos] = temp; } } List ranges = new List(); for (int i = 0; i < scaleCount; i++) { ranges.Add(new ScaleRange(_scaleRanges[i * 2], _scaleRanges[i * 2 + 1])); } this.ScaleRanges = ranges.ToArray(); } /// /// Gets the applicable scale ranges for this layer /// public ScaleRange[] ScaleRanges { get; private set; } private void InitIdentityProperties(IVectorLayerDefinition vl) { try { var fs = (IFeatureSource)this.Parent.ResourceService.GetResource(vl.ResourceId); var cls = fs.GetClass(vl.FeatureName); var idProps = cls.IdentityProperties; var propInfo = new PropertyInfo[idProps.Count]; int i = 0; foreach (var prop in idProps) { propInfo[i] = new PropertyInfo(prop.Name, ClrFdoTypeMap.GetClrType(prop.DataType)); i++; } this.IdentityProperties = propInfo; } catch (Exception ex) //Has to be a bug in MapGuide { Trace.TraceWarning("Could fetch and initialize identity properties for " + this.Name + " This is likely a bug in MapGuide " + Environment.NewLine + ex.ToString()); } } private bool _visible; /// /// Gets or sets a value indicating whether this is visible. /// /// true if visible; otherwise, false. public bool Visible { get { return _visible; } set { if (this.Type == kBaseMap) throw new InvalidOperationException("Setting visbility of a tiled map layer is not permitted"); SetField(ref _visible, value, "Visible"); } } private string _group; /// /// Gets or sets the group. /// /// The group. public string Group { get { return _group; } set { SetField(ref _group, value, "Group"); } } /// /// Gets the layer definition ID. /// /// The layer definition ID. public string LayerDefinitionID { get; private set; } private bool _selectable; /// /// Gets or sets a value indicating whether this is selectable. /// /// true if selectable; otherwise, false. public bool Selectable { get { return _selectable; } set { SetField(ref _selectable, value, "Selectable"); } } private string _name; /// /// Gets or sets the name. /// /// The name. public string Name { get { return _name; } set { SetField(ref _name, value, "Name"); } } private bool _showInLegend; /// /// Gets or sets a value indicating whether [show in legend]. /// /// true if [show in legend]; otherwise, false. public bool ShowInLegend { get { return _showInLegend; } set { SetField(ref _showInLegend, value, "ShowInLegend"); } } private string _legendLabel; /// /// Gets or sets the legend label. /// /// The legend label. public string LegendLabel { get { return _legendLabel; } set { SetField(ref _legendLabel, value, "LegendLabel"); } } private bool _expandInLegend; /// /// Gets or sets a value indicating whether [expand in legend]. /// /// true if [expand in legend]; otherwise, false. public bool ExpandInLegend { get { return _expandInLegend; } set { SetField(ref _expandInLegend, value, "ExpandInLegend"); } } private string _featureSourceId; /// /// Gets or sets the feature source ID. /// /// The feature source ID. public string FeatureSourceID { get { return _featureSourceId; } internal set { _featureSourceId = value; } } private string _qualifiedClassName; /// /// Gets the name of the qualified name of the feature class. /// /// The name of the qualified name of the feature class. public string QualifiedClassName { get { return _qualifiedClassName; } internal set { _qualifiedClassName = value; } } /// /// Gets the identity properties. /// /// The identity properties. public PropertyInfo[] IdentityProperties { get; private set; } private string _objectId; /// /// Gets the object id. /// /// The object id. public string ObjectId { get { return _objectId; } internal set { _objectId = value; } } private string _geometryPropertyName; /// /// Gets the name of the geometry property. /// /// The name of the geometry property. public string GeometryPropertyName { get { return _geometryPropertyName; } private set { _geometryPropertyName = value; } } private string _filter; /// /// Gets the filter. /// /// The filter. public string Filter { get { return _filter; } internal set { _filter = value; } } private int _type; /// /// Gets the type /// public int Type { get { return _type; } internal set { _type = value; } } private double _displayOrder; /// /// Gets the display order /// public double DisplayOrder { get { return _displayOrder; } internal set { _displayOrder = value; } } private bool _needsRefresh; /// /// Gets whether this layer needs to be refreshed /// public bool NeedsRefresh { get { return _needsRefresh; } internal set { _needsRefresh = value; } } /// /// Sets the refresh flag for this layer /// public void ForceRefresh() { if (!this.IsVisibleAtScale(this.Parent.ViewScale)) return; this.NeedsRefresh = true; } private bool _hasTooltips; /// /// Gets whether this layer has tooltips /// public bool HasTooltips { get { return _hasTooltips; } internal set { _hasTooltips = value; } } /// /// Gets the schema name /// public string SchemaName { get { var tokens = this.QualifiedClassName.Split(':'); if (tokens.Length == 2) return tokens[0]; return string.Empty; } } private double[] _scaleRanges; /// /// Serializes this instance to a binary stream /// /// public void Serialize(MgBinarySerializer s) { s.Write(this.Group); if (s.SiteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1)) s.WriteClassId(19003); else s.WriteClassId(30501); s.WriteResourceIdentifier(this.LayerDefinitionID); if (s.SiteVersion < SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2)) { s.Write(this.Name); s.Write(this.ObjectId); s.Write(this.Type); s.Write((byte)(this.Visible ? 1 : 0)); s.Write((byte)(this.Selectable ? 1 : 0)); s.Write((byte)(this.ShowInLegend ? 1 : 0)); s.Write((byte)(this.ExpandInLegend ? 1 : 0)); s.Write(this.LegendLabel); s.Write((byte)(this.NeedsRefresh ? 1 : 0)); s.Write(this.DisplayOrder); s.Write(_scaleRanges.Length); foreach (double d in _scaleRanges) s.Write(d); s.Write(this.FeatureSourceID); s.Write(this.QualifiedClassName); s.Write(this.GeometryPropertyName); s.Write(this.IdentityProperties.Length); foreach (var x in this.IdentityProperties) { s.Write((short)ConvertNetTypeToMgType(x.Type)); s.Write(x.Name); } } else { s.WriteStringInternal(this.Name); s.WriteStringInternal(this.ObjectId); s.WriteRaw(BitConverter.GetBytes(this.Type)); int flags = 0; flags |= this.Visible ? 1 : 0; flags |= this.Selectable ? 2 : 0; flags |= this.ShowInLegend ? 4 : 0; flags |= this.ExpandInLegend ? 8 : 0; flags |= this.NeedsRefresh ? 16 : 0; flags |= this.HasTooltips ? 32 : 0; s.WriteRaw(new byte[] { (byte)flags }); s.WriteStringInternal(this.LegendLabel); s.WriteRaw(BitConverter.GetBytes(this.DisplayOrder)); s.WriteRaw(BitConverter.GetBytes(_scaleRanges.Length)); foreach (double d in _scaleRanges) s.WriteRaw(BitConverter.GetBytes(d)); s.WriteStringInternal(this.FeatureSourceID); s.WriteStringInternal(this.QualifiedClassName); if (s.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS2_1)) s.WriteStringInternal(this.Filter); s.WriteStringInternal(this.SchemaName); s.WriteStringInternal(this.GeometryPropertyName); s.WriteRaw(BitConverter.GetBytes(this.IdentityProperties.Length)); foreach (var x in this.IdentityProperties) { s.WriteRaw(BitConverter.GetBytes((short)ConvertNetTypeToMgType(x.Type))); s.WriteStringInternal(x.Name); } } } /// /// Parses encoded id string into an array of values /// /// The encoded id string. /// public object[] ParseSelectionValues(string encodedId) { int index = 0; byte[] data = Convert.FromBase64String(encodedId); object[] tmp = new object[this.IdentityProperties.Length]; for (int i = 0; i < this.IdentityProperties.Length; i++) { Type type = this.IdentityProperties[i].Type; if (type == typeof(short)) { tmp[i] = BitConverter.ToInt16(data, index); index += MgBinarySerializer.UInt16Len; } else if (type == typeof(int)) { tmp[i] = BitConverter.ToInt32(data, index); index += MgBinarySerializer.UInt32Len; } else if (type == typeof(long)) { tmp[i] = BitConverter.ToInt64(data, index); index += MgBinarySerializer.UInt64Len; } else if (type == typeof(string)) { int pos = index; while (pos < data.Length && data[pos] != 0) pos++; if (pos >= data.Length) throw new Exception("Bad null encoded string"); tmp[i] = System.Text.Encoding.UTF8.GetString(data, index, pos - index); index = pos + 1; } else throw new Exception(string.Format("The type {0} is not supported for primary keys", type.ToString())); } return tmp; } /// /// Initializes this instance with the specified binary stream /// /// public void Deserialize(MgBinaryDeserializer d) { this.Group = d.ReadString(); int classid = d.ReadClassId(); if (d.SiteVersion <= SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 19003) throw new Exception("Resource Identifier expected, but got: " + classid.ToString()); if (d.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 30501) throw new Exception("Resource Identifier expected, but got: " + classid.ToString()); this.LayerDefinitionID = d.ReadResourceIdentifier(); if (d.SiteVersion < SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2)) { this.Name = d.ReadString(); _objectId = d.ReadString(); _type = d.ReadInt32(); _visible = d.ReadByte() > 0; _selectable = d.ReadByte() > 0; _showInLegend = d.ReadByte() > 0; _expandInLegend = d.ReadByte() > 0; _legendLabel = d.ReadString(); _needsRefresh = d.ReadByte() > 0; _displayOrder = d.ReadDouble(); var scaleRanges = new List(); int scales = d.ReadInt32(); while (scales-- > 0) scaleRanges.Add(d.ReadDouble()); _scaleRanges = scaleRanges.ToArray(); _featureSourceId = d.ReadString(); _qualifiedClassName = d.ReadString(); _geometryPropertyName = d.ReadString(); var ids = new List(); int idCount = d.ReadInt32(); while (idCount-- > 0) { short idType = d.ReadInt16(); string idName = d.ReadString(); ids.Add(new PropertyInfo(idName, ConvertMgTypeToNetType(idType))); } this.IdentityProperties = ids.ToArray(); } else { //AAARGH!!! Now they bypass their own header system .... this.Name = d.ReadInternalString(); _objectId = d.ReadInternalString(); _type = BitConverter.ToInt32(d.ReadStreamRepeat(4), 0); int flags = d.ReadStreamRepeat(1)[0]; _visible = (flags & 1) > 0; _selectable = (flags & 2) > 0; _showInLegend = (flags & 4) > 0; _expandInLegend = (flags & 8) > 0; _needsRefresh = (flags & 16) > 0; _hasTooltips = (flags & 32) > 0; _legendLabel = d.ReadInternalString(); _displayOrder = BitConverter.ToDouble(d.ReadStreamRepeat(8), 0); var scaleRanges = new List(); int scales = BitConverter.ToInt32(d.ReadStreamRepeat(4), 0); while (scales-- > 0) scaleRanges.Add(BitConverter.ToDouble(d.ReadStreamRepeat(8), 0)); _scaleRanges = scaleRanges.ToArray(); _featureSourceId = d.ReadInternalString(); _qualifiedClassName = d.ReadInternalString(); if (d.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS2_1)) _filter = d.ReadInternalString(); //this.SchemaName = d.ReadInternalString(); d.ReadInternalString(); _geometryPropertyName = d.ReadInternalString(); var ids = new List(); int idCount = BitConverter.ToInt32(d.ReadStreamRepeat(4), 0); while (idCount-- > 0) { short idType = BitConverter.ToInt16(d.ReadStreamRepeat(2), 0); string idName = d.ReadInternalString(); ids.Add(new PropertyInfo(idName, ConvertMgTypeToNetType(idType))); } this.IdentityProperties = ids.ToArray(); } EnsureOrderedMinMaxScales(); } //from MgPropertyType const int Blob = 10; const int Boolean = 1; const int Byte = 2; const int Clob = 11; const int DateTime = 3; const int Double = 5; const int Feature = 12; const int Geometry = 13; const int Int16 = 6; const int Int32 = 7; const int Int64 = 8; const int Null = 0; const int Raster = 14; const int Single = 4; const int String = 9; private static short ConvertNetTypeToMgType(Type type) { if (type == typeof(short)) return Int16; else if (type == typeof(byte)) return Byte; else if (type == typeof(bool)) return Boolean; else if (type == typeof(int)) return Int32; else if (type == typeof(long)) return Int64; else if (type == typeof(float)) return Single; else if (type == typeof(double)) return Double; else if (type == Utility.GeometryType) return Geometry; else if (type == typeof(string)) return String; else if (type == typeof(DateTime)) return DateTime; else if (type == Utility.RasterType) return Raster; else if (type == typeof(byte[])) return Blob; throw new Exception("Failed to find type for: " + type.FullName.ToString()); } private static Type ConvertMgTypeToNetType(short idType) { switch (idType) { case Byte: return typeof(byte); case Int16: return typeof(short); case Int32: return typeof(int); case Int64: return typeof(long); case Single: return typeof(float); case Double: return typeof(double); case Boolean: return typeof(bool); case Geometry: return Utility.GeometryType; case String: return typeof(string); case DateTime: return typeof(DateTime); case Raster: return Utility.RasterType; case Blob: return typeof(byte[]); case Clob: return typeof(byte[]); default: throw new Exception("Failed to find type for: " + idType.ToString()); } } /// /// Raises the event /// /// protected override void OnPropertyChanged(string propertyName) { if (_disableChangeTracking) return; //register change items on map switch (propertyName) { case "Group": var name = this.Group; if (this.Parent.Groups[name] != null) this.Parent.OnLayerParentChanged(this, this.Parent.Groups[name].ObjectId); else this.Parent.OnLayerParentChanged(this, string.Empty); break; case "Visible": this.Parent.OnLayerVisibilityChanged(this, this.Visible ? "1" : "0"); break; case "ShowInLegend": this.Parent.OnLayerDisplayInLegendChanged(this, this.ShowInLegend ? "1" : "0"); break; case "LegendLabel": this.Parent.OnLayerLegendLabelChanged(this, this.LegendLabel); break; case "LayerDefinitionID": this.Parent.OnLayerDefinitionChanged(this); break; case "Selectable": this.Parent.OnLayerSelectabilityChanged(this, this.Selectable ? "1" : "0"); break; } base.OnPropertyChanged(propertyName); } /// /// Determines whether this layer is potentially visible at the specified scale /// /// /// Current layer visibility does not factor into the final result /// /// The scale to check for potential visibility /// public bool IsVisibleAtScale(double scale) { for (int i = 0; i < _scaleRanges.Length; i += 2) { if (scale >= _scaleRanges[i] && scale <= _scaleRanges[i + 1]) return true; } return false; } } }