#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; using OSGeo.MapGuide.MaestroAPI.Schema; 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; } } /// /// Extension methods for /// public static class Extensions { /// /// Gets the parent group /// /// /// public static RuntimeMapGroup GetParentGroup(this RuntimeMapLayer layer) { Check.NotNull(layer, "layer"); //NOXLATE if (string.IsNullOrEmpty(layer.Group)) return null; return layer.Parent.Groups[layer.Group]; } } /// /// 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; } /// /// Initializes this instances from the specified Layer Definition /// /// protected void Initialize(ILayerDefinition ldf) { Check.NotNull(ldf, "ldf"); //NOXLATE 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) { _featureSourceId = ldf.SubLayer.ResourceId; var dl = ((IDrawingLayerDefinition)ldf.SubLayer); _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; } /// /// Initializes this instance /// /// protected 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. protected internal RuntimeMapLayer(RuntimeMap parent, ILayerDefinition ldf) : this(parent) { _disableChangeTracking = true; Initialize(ldf); _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. protected internal RuntimeMapLayer(RuntimeMap parent, IMapLayer source) : this(parent, source, (ILayerDefinition)parent.CurrentConnection.ResourceService.GetResource(source.ResourceId)) { _disableChangeTracking = false; } /// /// Initializes a new instance /// /// /// /// protected internal RuntimeMapLayer(RuntimeMap parent, IMapLayer source, ILayerDefinition ldf) : this(parent, (IBaseMapLayer)source, ldf) { _disableChangeTracking = true; this.Group = source.Group; _visible = source.Visible; _disableChangeTracking = false; } /// /// Initializes a new instance /// /// /// /// protected internal RuntimeMapLayer(RuntimeMap parent, IBaseMapLayer source, ILayerDefinition ldf) : this(parent, ldf) { Check.NotNull(source, "source"); //NOXLATE Check.NotNull(ldf, "ldf"); //NOXLATE Check.Precondition(source.ResourceId == ldf.ResourceID, "source.ResourceId == ldf.ResourceID"); //NOXLATE } 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); if (cls == null) throw new Exception(string.Format(Strings.ERR_CLASS_NOT_FOUND, 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 or in the FDO provider { this.IdentityProperties = new PropertyInfo[0]; Trace.TraceWarning(string.Format(Strings.ERR_INIT_IDENTITY_PROPS, Environment.NewLine, this.Name, ex.ToString())); } } private bool _visible; /// /// Gets or sets a value indicating whether this is visible. /// /// true if visible; otherwise, false. public virtual bool Visible { get { return _visible; } set { if (this.Type == kBaseMap) throw new InvalidOperationException(Strings.ErrorSettingVisibilityOfTiledLayer); if (SetField(ref _visible, value, "Visible")) //NOXLATE Parent.IsDirty = true; } } private string _group; /// /// Gets or sets the group. /// /// The group. public virtual string Group { get { return _group; } set { if (SetField(ref _group, value, "Group")) //NOXLATE Parent.IsDirty = true; } } /// /// Gets the layer definition ID. /// /// The layer definition ID. public virtual string LayerDefinitionID { get; internal set; } private bool _selectable; /// /// Gets or sets a value indicating whether this is selectable. /// /// true if selectable; otherwise, false. public virtual bool Selectable { get { return _selectable; } set { if (SetField(ref _selectable, value, "Selectable")) //NOXLATE Parent.IsDirty = true; } } private string _name; /// /// Gets or sets the name. /// /// The name. public virtual string Name { get { return _name; } set { if (SetField(ref _name, value, "Name")) //NOXLATE Parent.IsDirty = true; } } private bool _showInLegend; /// /// Gets or sets a value indicating whether [show in legend]. /// /// true if [show in legend]; otherwise, false. public virtual bool ShowInLegend { get { return _showInLegend; } set { if (SetField(ref _showInLegend, value, "ShowInLegend")) //NOXLATE Parent.IsDirty = true; } } private string _legendLabel; /// /// Gets or sets the legend label. /// /// The legend label. public virtual string LegendLabel { get { return _legendLabel; } set { if (SetField(ref _legendLabel, value, "LegendLabel")) //NOXLATE Parent.IsDirty = true; } } private bool _expandInLegend; /// /// Gets or sets a value indicating whether [expand in legend]. /// /// true if [expand in legend]; otherwise, false. public virtual bool ExpandInLegend { get { return _expandInLegend; } set { if (SetField(ref _expandInLegend, value, "ExpandInLegend")) //NOXLATE Parent.IsDirty = true; } } private string _featureSourceId; /// /// Gets or sets the feature source ID. /// /// The feature source ID. public virtual 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 virtual 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 virtual 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 virtual string GeometryPropertyName { get { return _geometryPropertyName; } private set { _geometryPropertyName = value; } } private string _filter; /// /// Gets the filter. /// /// The filter. public virtual string Filter { get { return _filter; } internal set { _filter = value; } } private int _type; /// /// Gets the type /// public virtual int Type { get { return _type; } internal set { _type = value; } } private double _displayOrder; /// /// Gets the display order /// public virtual double DisplayOrder { get { return _displayOrder; } internal set { _displayOrder = value; } } private bool _needsRefresh; /// /// Gets whether this layer needs to be refreshed /// public virtual bool NeedsRefresh { get { return _needsRefresh; } internal set { _needsRefresh = value; } } /// /// Sets the refresh flag for this layer /// public virtual void ForceRefresh() { if (!this.IsVisibleAtScale(this.Parent.ViewScale)) return; this.NeedsRefresh = true; } private bool _hasTooltips; /// /// Gets whether this layer has tooltips /// public virtual bool HasTooltips { get { return _hasTooltips; } internal set { _hasTooltips = value; } } /// /// Gets the schema name /// /// /// For drawing layers, the schema name will always be empty /// public string SchemaName { get { if (!this.FeatureSourceID.EndsWith("DrawingSource")) //NOXLATE { var tokens = this.QualifiedClassName.Split(':'); //NOXLATE if (tokens.Length == 2) return tokens[0]; } return string.Empty; } } private double[] _scaleRanges; /// /// Serializes this instance to a binary stream /// /// public virtual 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(double)) { tmp[i] = BitConverter.ToDouble(data, index); index += MgBinarySerializer.DoubleLen; } else if (type == typeof(string)) { int pos = index; while (pos < data.Length && data[pos] != 0) pos++; if (pos >= data.Length) throw new Exception(Strings.ErrorBadNullEncodedString); tmp[i] = System.Text.Encoding.UTF8.GetString(data, index, pos - index); index = pos + 1; } else throw new Exception(string.Format(Strings.ErrorUnsupportedPkType, type.ToString())); } return tmp; } /// /// Initializes this instance with the specified binary stream /// /// public virtual 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(string.Format(Strings.ErrorResourceIdentifierClassIdNotFound, classid)); if (d.SiteVersion > SiteVersions.GetVersion(KnownSiteVersions.MapGuideEP1_1) && classid != 30501) throw new Exception(string.Format(Strings.ErrorResourceIdentifierClassIdNotFound, classid)); 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(string.Format(Strings.ErrorFailedToFindTypeForClrType, idType)); } } /// /// Raises the event /// /// protected override void OnPropertyChanged(string propertyName) { if (_disableChangeTracking) return; //register change items on map switch (propertyName) { case "Group": //NOXLATE 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": //NOXLATE this.Parent.OnLayerVisibilityChanged(this, this.Visible ? "1" : "0"); //NOXLATE break; case "ShowInLegend": //NOXLATE this.Parent.OnLayerDisplayInLegendChanged(this, this.ShowInLegend ? "1" : "0"); //NOXLATE break; case "LegendLabel": //NOXLATE this.Parent.OnLayerLegendLabelChanged(this, this.LegendLabel); break; case "LayerDefinitionID": //NOXLATE this.Parent.OnLayerDefinitionChanged(this); break; case "Selectable": //NOXLATE this.Parent.OnLayerSelectabilityChanged(this, this.Selectable ? "1" : "0"); //NOXLATE 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; } /// /// Convenience method to get the associated class definition /// /// public ClassDefinition GetClassDefinition() { var tokens = this.QualifiedClassName.Split(':'); return this.Parent.FeatureService.GetClassDefinition(tokens[0], tokens[1]); } /// /// Gets a display string for this layer for presentation purposes /// public string DisplayString { get { return this.LegendLabel + " (" + this.Name + ")"; } } //NOXLATE } }