using System; using System.IO; using System.ComponentModel; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using System.Globalization; using FastReport.Utils; using FastReport.Data; using FastReport.Map.Import.Shp; using System.Drawing.Design; namespace FastReport.Map { /// /// Specifies the type of objects that layer contains. /// public enum LayerType { /// /// The layer contains points. /// Point, /// /// The layer contains lines. /// Line, /// /// The layer contains polygons. /// Polygon } /// /// Specifies the spatial source for the layer. /// public enum SpatialSource { /// /// Source is ESRI shapefile. /// ShpFile, /// /// Source is a latitude/longitude/name provided by an application. /// ApplicationData } /// /// Determines how map labels are displayed. /// public enum MapLabelKind { /// /// No label displayed. /// None, /// /// The shape name is displayed. /// Name, /// /// The value is displayed. /// Value, /// /// Both name and value displayed. /// NameAndValue } /// /// Represents a map layer. /// public class MapLayer : Base, IParent, ICustomTypeDescriptor { #region Fields private LayerType type; private SpatialSource spatialSource; private string shapefile; private bool visible; private BoundingBox box; private ShapeCollection shapes; private ShapeStyle defaultShapeStyle; private MapPalette palette; private DataSourceBase dataSource; private string filter; // used if SpatialSource is ShpFile private string spatialColumn; private string spatialValue; // used if SpatialSource is ApplicationData private string latitudeValue; private string longitudeValue; private string labelValue; // private string analyticalValue; private string labelColumn; private MapLabelKind labelKind; private string labelFormat; private TotalType function; private ColorRanges colorRanges; private SizeRanges sizeRanges; private float accuracy; private float labelsVisibleAtZoom; private string zoomPolygon; private SortedList values; private SortedList counts; #endregion // Fields #region Properties /// /// Gets or sets a type of layer. /// [Browsable(false)] public LayerType Type { get { return type; } set { type = value; } } /// /// Gets or sets the spatial source for the layer. /// [DefaultValue(SpatialSource.ShpFile)] [Category("Data")] public SpatialSource SpatialSource { get { return spatialSource; } set { spatialSource = value; if (value == SpatialSource.ApplicationData) { LabelColumn = "NAME"; SpatialColumn = "LOCATION"; Box.MinX = -180; Box.MaxX = 180; Box.MinY = -90; Box.MaxY = 83.623031; } } } /// /// Gets or sets the name of ESRI shapefile. /// public string Shapefile { get { return shapefile; } set { shapefile = value; } } /// /// Gets or sets the data source. /// [Category("Data")] public DataSourceBase DataSource { get { return dataSource; } set { dataSource = value; } } /// /// Gets or sets the datasource filter expression. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string Filter { get { return filter; } set { filter = value; } } /// /// Gets or sets spatial column name. /// /// /// This property is used if the is set to ShpFile. /// [Category("Data")] [Editor("FastReport.Map.SpatialColumnEditor, FastReport", typeof(UITypeEditor))] public string SpatialColumn { get { return spatialColumn; } set { spatialColumn = value; } } /// /// Gets or sets an expression that returns spatial value. /// /// /// This property is used if the is set to ShpFile. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string SpatialValue { get { return spatialValue; } set { spatialValue = value; } } /// /// Gets or sets an expression that returns latitude value. /// /// /// This property is used if the is set to ApplicationData. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string LatitudeValue { get { return latitudeValue; } set { latitudeValue = value; } } /// /// Gets or sets an expression that returns longitude value. /// /// /// This property is used if the is set to ApplicationData. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string LongitudeValue { get { return longitudeValue; } set { longitudeValue = value; } } /// /// Gets or sets an expression that returns label value. /// /// /// This property is used if the is set to ApplicationData. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string LabelValue { get { return labelValue; } set { labelValue = value; } } /// /// Gets or sets an expression that returns analytical value. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string AnalyticalValue { get { return analyticalValue; } set { analyticalValue = value; } } /// /// Gets or sets label's column name. /// [Category("Appearance")] [Editor("FastReport.Map.SpatialColumnEditor, FastReport", typeof(UITypeEditor))] public string LabelColumn { get { return labelColumn; } set { labelColumn = value; } } /// /// Gets or sets a value that determines how map labels are displayed. /// [DefaultValue(MapLabelKind.Name)] [Category("Appearance")] public MapLabelKind LabelKind { get { return labelKind; } set { labelKind = value; } } /// /// Gets or sets the format of label's value. /// [Category("Appearance")] public string LabelFormat { get { return labelFormat; } set { labelFormat = value; } } /// /// Gets or sets the map accuracy. Lower value is better, but slower. /// [DefaultValue(2f)] [Category("Appearance")] public float Accuracy { get { return accuracy; } set { accuracy = value; } } /// /// Gets or sets the value that determines the labels visiblity at a certain zoom value. /// [DefaultValue(1f)] [Category("Appearance")] public float LabelsVisibleAtZoom { get { return labelsVisibleAtZoom; } set { labelsVisibleAtZoom = value; } } /// /// Gets or sets the aggregate function. /// [Category("Data")] [DefaultValue(TotalType.Sum)] public TotalType Function { get { return function; } set { function = value; } } /// /// Gets or sets a value indicating that the layer is visible. /// [DefaultValue(true)] [Category("Behavior")] public bool Visible { get { return visible; } set { visible = value; } } /// /// Gets or sets a bounding box of layer. /// [Browsable(false)] public BoundingBox Box { get { return box; } set { box = value; } } /// /// Gets a collection of map objects. /// [Browsable(false)] public ShapeCollection Shapes { get { return shapes; } } /// /// Gets the default style of shapes in this layer. /// [Category("Appearance")] public ShapeStyle DefaultShapeStyle { get { return defaultShapeStyle; } } /// /// Gets or sets the palette used to highlight shapes. /// [Category("Appearance")] [DefaultValue(MapPalette.None)] public MapPalette Palette { get { return palette; } set { palette = value; } } /// /// Gets the color ranges used to highlight shapes based on analytical value. /// [Category("Appearance")] public ColorRanges ColorRanges { get { return colorRanges; } } /// /// Gets the size ranges used to draw points based on analytical value. /// [Category("Appearance")] public SizeRanges SizeRanges { get { return sizeRanges; } } /// /// Gets or sets the expression that returns the name of polygon to zoom. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string ZoomPolygon { get { return zoomPolygon; } set { zoomPolygon = value; } } /// /// Gets or sets the bounding box as a string. /// [Browsable(false)] public string BoxAsString { get { return Box.GetAsString(); } set { Box.SetAsString(value); } } /// /// Gets a reference to the Map object. /// [Browsable(false)] public MapObject Map { get { return Parent as MapObject; } } internal List SpatialColumns { get { if (Shapes.Count > 0) return Shapes[0].SpatialData.GetKeys(); return new List(); } } internal bool IsShapefileEmbedded { get { return String.IsNullOrEmpty(Shapefile); } } #endregion // Properties #region Public Methods /// public override void Assign(Base source) { base.Assign(source); MapLayer src = source as MapLayer; Shapefile = src.Shapefile; Type = src.Type; SpatialSource = src.SpatialSource; DataSource = src.DataSource; Filter = src.Filter; SpatialColumn = src.SpatialColumn; SpatialValue = src.SpatialValue; Function = src.Function; LatitudeValue = src.LatitudeValue; LongitudeValue = src.LongitudeValue; LabelValue = src.LabelValue; AnalyticalValue = src.AnalyticalValue; LabelColumn = src.LabelColumn; LabelKind = src.LabelKind; LabelFormat = src.LabelFormat; Accuracy = src.Accuracy; LabelsVisibleAtZoom = src.LabelsVisibleAtZoom; Visible = src.Visible; ZoomPolygon = src.ZoomPolygon; Box.Assign(src.Box); DefaultShapeStyle.Assign(src.DefaultShapeStyle); Palette = src.Palette; ColorRanges.Assign(src.ColorRanges); SizeRanges.Assign(src.SizeRanges); } /// /// Draws the layer. /// /// The drawing parameters. public void Draw(FRPaintEventArgs e) { if (Visible) { for (int i = 0; i < Shapes.Count; i++) { ShapeBase shape = Shapes[i]; shape.ShapeIndex = i; shape.SetPrinting(IsPrinting); if (Map.IsDesigning || shape.Visible) shape.Draw(e); } if (LabelKind != MapLabelKind.None && (Map.Zoom >= LabelsVisibleAtZoom)) { foreach (ShapeBase shape in Shapes) { if (Map.IsDesigning || shape.Visible) shape.DrawLabel(e); } } } } /// /// Finds the shape under cursor. /// /// The cursor coordinates. /// The ShapeBase object if found. public ShapeBase HitTest(PointF point) { if (Visible) { foreach (ShapeBase shape in Shapes) { if (shape.HitTest(point)) return shape; } } return null; } /// public override void Serialize(FRWriter writer) { MapLayer c = writer.DiffObject as MapLayer; base.Serialize(writer); if (Shapefile != c.Shapefile) { // when saving to the report file, convert absolute path to the shapefile to relative path // (based on the reportfile path). string value = Shapefile; if (writer.SerializeTo == SerializeTo.Report && Report != null && !String.IsNullOrEmpty(Report.FileName)) value = FileUtils.GetRelativePath(Shapefile, Path.GetDirectoryName(Report.FileName)); writer.WriteStr("Shapefile", value); } if (Type != c.Type) writer.WriteValue("Type", Type); if (SpatialSource != c.SpatialSource) writer.WriteValue("SpatialSource", SpatialSource); if (DataSource != null) writer.WriteRef("DataSource", DataSource); if (Filter != c.Filter) writer.WriteStr("Filter", Filter); if (SpatialColumn != c.SpatialColumn) writer.WriteStr("SpatialColumn", SpatialColumn); if (SpatialValue != c.SpatialValue) writer.WriteStr("SpatialValue", SpatialValue); if (Function != c.Function) writer.WriteValue("Function", Function); if (LatitudeValue != c.LatitudeValue) writer.WriteStr("LatitudeValue", LatitudeValue); if (LongitudeValue != c.LongitudeValue) writer.WriteStr("LongitudeValue", LongitudeValue); if (LabelValue != c.LabelValue) writer.WriteStr("LabelValue", LabelValue); if (AnalyticalValue != c.AnalyticalValue) writer.WriteStr("AnalyticalValue", AnalyticalValue); if (LabelColumn != c.LabelColumn) writer.WriteStr("LabelColumn", LabelColumn); if (LabelKind != c.LabelKind) writer.WriteValue("LabelKind", LabelKind); if (LabelFormat != c.LabelFormat) writer.WriteStr("LabelFormat", LabelFormat); if (Accuracy != c.Accuracy) writer.WriteFloat("Accuracy", Accuracy); if (LabelsVisibleAtZoom != c.LabelsVisibleAtZoom) writer.WriteFloat("LabelsVisibleAtZoom", LabelsVisibleAtZoom); if (Visible != c.Visible) writer.WriteBool("Visible", Visible); if (ZoomPolygon != c.ZoomPolygon) writer.WriteStr("ZoomPolygon", ZoomPolygon); if (BoxAsString != c.BoxAsString) writer.WriteStr("BoxAsString", BoxAsString); DefaultShapeStyle.Serialize(writer, "DefaultShapeStyle", c.DefaultShapeStyle); if (Palette != c.Palette) writer.WriteValue("Palette", Palette); ColorRanges.Serialize(writer, "ColorRanges"); SizeRanges.Serialize(writer, "SizeRanges"); if (writer.SerializeTo == SerializeTo.Preview && !IsShapefileEmbedded) { // write children by itself foreach (ShapeBase shape in Shapes) { writer.Write(shape); } } } /// /// Creates unique names for all contained objects such as points, lines, polygons, etc. /// public void CreateUniqueNames() { if (Report != null) { FastNameCreator nameCreator = new FastNameCreator(Report.AllNamedObjects); if (String.IsNullOrEmpty(Name)) nameCreator.CreateUniqueName(this); foreach (ShapeBase shape in Shapes) { if (String.IsNullOrEmpty(shape.Name)) nameCreator.CreateUniqueName(shape); } } } /// /// Reduces the number of points in the shapes in this layer. /// /// The accuracy value. public void Simplify(double accuracy) { if (accuracy <= 0) return; foreach (ShapeBase shape in Shapes) { shape.Simplify(accuracy); } } private void Zoom_Shape(string value) { foreach (ShapeBase shape in Shapes) { if (shape.SpatialValue == value) { ShapePolygon shapePoly = shape as ShapePolygon; using (Bitmap bmp = new Bitmap(1, 1)) using (Graphics g = Graphics.FromImage(bmp)) using (GraphicCache cache = new GraphicCache()) { FRPaintEventArgs args = new FRPaintEventArgs(g, 1, 1, cache); Map.OffsetX = 0; Map.OffsetY = 0; Map.Zoom = 1; Map.Draw(args); // this will calculate largestBoundsRect shapePoly.GetGraphicsPath(args).Dispose(); RectangleF largestBoundsRect = shapePoly.largestBoundsRect; float distanceX = (Map.AbsLeft + Map.Width / 2) - (largestBoundsRect.Left + largestBoundsRect.Width / 2); float distanceY = (Map.AbsTop + Map.Height / 2) - (largestBoundsRect.Top + largestBoundsRect.Height / 2); float width = Map.Width - Map.Padding.Horizontal; float height = Map.Height - Map.Padding.Vertical; float zoomX = width / largestBoundsRect.Width; float zoomY = height / largestBoundsRect.Height; float zoom = Math.Min(zoomX, zoomY) * 0.95f; Map.OffsetX = distanceX; Map.OffsetY = distanceY; Map.Zoom = zoom; } break; } } } /// /// Loads the layer contents from ESRI shapefile (*.shp/*.dbf). /// /// The file name. public void LoadShapefile(string fileName) { using (ShpMapImport import = new ShpMapImport()) { import.ImportMap(Map, this, fileName); Simplify(0.03); CreateUniqueNames(); } } /// public override void OnAfterLoad() { // convert relative path to the shapefile to absolute path (based on the reportfile path). if (String.IsNullOrEmpty(Shapefile)) return; Shapefile = Shapefile.Replace('\\', Path.DirectorySeparatorChar); if (!Path.IsPathRooted(Shapefile) && Report != null && !String.IsNullOrEmpty(Report.FileName)) Shapefile = Path.GetDirectoryName(Report.FileName) + Path.DirectorySeparatorChar + Shapefile; LoadShapefile(Shapefile); } #endregion // Public Methods #region IParent Members /// public bool CanContain(Base child) { return child is ShapeBase; } /// public void GetChildObjects(ObjectCollection list) { if (IsShapefileEmbedded) { foreach (ShapeBase shape in Shapes) { list.Add(shape); } } } /// public void AddChild(Base child) { Shapes.Add(child as ShapeBase); } /// public void RemoveChild(Base child) { Shapes.Remove(child as ShapeBase); } /// public int GetChildOrder(Base child) { return Shapes.IndexOf(child as ShapeBase); } /// public void SetChildOrder(Base child, int order) { int oldOrder = child.ZOrder; if (oldOrder != -1 && order != -1 && oldOrder != order) { if (order > Shapes.Count) order = Shapes.Count; if (oldOrder <= order) order--; Shapes.Remove(child as ShapeBase); Shapes.Insert(order, child as ShapeBase); } } /// public void UpdateLayout(float dx, float dy) { // do nothing } #endregion #region Report Engine internal void SaveState() { ColorRanges.SaveState(); SizeRanges.SaveState(); foreach (ShapeBase shape in Shapes) { shape.SaveState(); } } internal void RestoreState() { ColorRanges.RestoreState(); SizeRanges.RestoreState(); foreach (ShapeBase shape in Shapes) { shape.RestoreState(); } } internal void InitializeComponent() { foreach (ShapeBase shape in Shapes) { shape.InitializeComponent(); } } internal void FinalizeComponent() { foreach (ShapeBase shape in Shapes) { shape.FinalizeComponent(); } } /// public override string[] GetExpressions() { List expressions = new List(); if (!String.IsNullOrEmpty(Filter)) expressions.Add(Filter); if (!String.IsNullOrEmpty(SpatialValue)) expressions.Add(SpatialValue); if (!String.IsNullOrEmpty(LatitudeValue)) expressions.Add(LatitudeValue); if (!String.IsNullOrEmpty(LongitudeValue)) expressions.Add(LongitudeValue); if (!String.IsNullOrEmpty(LabelValue)) expressions.Add(LabelValue); if (!String.IsNullOrEmpty(AnalyticalValue)) expressions.Add(AnalyticalValue); if (!String.IsNullOrEmpty(ZoomPolygon)) expressions.Add(ZoomPolygon); return expressions.ToArray(); } internal void GetData() { InitializeData(); if (DataSource != null) { DataSource.Init(Filter); DataSource.First(); while (DataSource.HasMoreRows) { if (SpatialSource == SpatialSource.ShpFile) { if (!String.IsNullOrEmpty(SpatialValue) && !String.IsNullOrEmpty(AnalyticalValue)) { object spatialValue = Report.Calc(SpatialValue); object analyticalValue = Report.Calc(AnalyticalValue); if (spatialValue != null && !(spatialValue is DBNull) && analyticalValue != null && !(analyticalValue is DBNull)) { AddValue(spatialValue.ToString(), Convert.ToDouble(analyticalValue)); } } } else { if (!String.IsNullOrEmpty(LatitudeValue) && !String.IsNullOrEmpty(LongitudeValue) && !String.IsNullOrEmpty(LabelValue) && !String.IsNullOrEmpty(AnalyticalValue)) { object latitudeValue = Report.Calc(LatitudeValue); object longitudeValue = Report.Calc(LongitudeValue); object labelValue = Report.Calc(LabelValue); object analyticalValue = Report.Calc(AnalyticalValue); if (latitudeValue != null && !(latitudeValue is DBNull) && longitudeValue != null && !(longitudeValue is DBNull) && labelValue != null && !(labelValue is DBNull) && analyticalValue != null && !(analyticalValue is DBNull)) { AddValue(Convert.ToDouble(latitudeValue), Convert.ToDouble(longitudeValue), labelValue.ToString(), Convert.ToDouble(analyticalValue)); } } } DataSource.Next(); } } FinalizeData(); } internal void InitializeData() { values.Clear(); counts.Clear(); if (SpatialSource == SpatialSource.ApplicationData) Shapes.Clear(); } internal void FinalizeData() { double min = 1e10; double max = -1e10; IList spatialValues = values.Keys; // finalize avg calculation, find min and max for (int i = 0; i < spatialValues.Count; i++) { string spatialValue = spatialValues[i]; double analyticalValue = values[spatialValue]; if (Function == TotalType.Avg) { analyticalValue = analyticalValue / counts[spatialValue]; values[spatialValue] = analyticalValue; } if (analyticalValue < min) min = analyticalValue; if (analyticalValue > max) max = analyticalValue; } if (spatialValues.Count > 0) { ColorRanges.Fill(min, max); SizeRanges.Fill(min, max); } // set shape values foreach (ShapeBase shape in Shapes) { string spatialValue = shape.SpatialValue; if (values.ContainsKey(spatialValue)) shape.Value = values[spatialValue]; } if (!String.IsNullOrEmpty(ZoomPolygon)) { object zoomShape = Report.Calc(ZoomPolygon); if (zoomShape != null && !(zoomShape is DBNull)) Zoom_Shape(zoomShape.ToString()); } } /// /// Adds application provided data. /// /// Latitude value. /// Longitude value. /// The name displayed as a label. /// Analytical value. /// /// Use this method if the is set to ApplicationData. /// public void AddValue(double latitude, double longitude, string name, double analyticalValue) { string spatialValue = latitude.ToString(CultureInfo.InvariantCulture.NumberFormat) + "," + longitude.ToString(CultureInfo.InvariantCulture.NumberFormat); if (!values.ContainsKey(spatialValue)) { ShapePoint point = new ShapePoint(); point.X = longitude; point.Y = latitude; point.SpatialData.SetValue("NAME", name); point.SpatialData.SetValue("LOCATION", spatialValue); Shapes.Add(point); } AddValue(spatialValue, analyticalValue); } /// /// Adds a spatial/analytical value pair to the list. /// /// The spatial value. /// The analytical value. /// /// Use this method if the is set to ShpFile. /// public void AddValue(string spatialValue, double analyticalValue) { if (!values.ContainsKey(spatialValue)) { if (Function == TotalType.Count) analyticalValue = 1; values.Add(spatialValue, analyticalValue); counts.Add(spatialValue, 1); return; } double value = values[spatialValue]; switch (Function) { case TotalType.Sum: case TotalType.Avg: value += analyticalValue; break; case TotalType.Max: if (analyticalValue > value) value = analyticalValue; break; case TotalType.Min: if (analyticalValue < value) value = analyticalValue; break; case TotalType.Count: value++; break; } values[spatialValue] = value; counts[spatialValue]++; } #endregion #region ICustomTypeDescriptor Members /// public PropertyDescriptorCollection GetProperties() { return GetProperties(null); } /// public PropertyDescriptorCollection GetProperties(Attribute[] attr) { PropertyDescriptorCollection typeProps = TypeDescriptor.GetProperties(this.GetType(), attr); PropertyDescriptorCollection properties = new PropertyDescriptorCollection(null); foreach (PropertyDescriptor desc in typeProps) { bool skip = false; if (SpatialSource == SpatialSource.ShpFile) { skip = desc.Name == "LatitudeValue" || desc.Name == "LongitudeValue" || desc.Name == "LabelValue" || desc.Name == "LabelsVisibleAtZoom"; } else if (SpatialSource == SpatialSource.ApplicationData) { skip = desc.Name == "SpatialColumn" || desc.Name == "SpatialValue" || desc.Name == "LabelColumn" || desc.Name == "Accuracy"; } if (!skip) properties.Add(desc); } return properties; } /// public String GetClassName() { return TypeDescriptor.GetClassName(this, true); } /// public AttributeCollection GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } /// public String GetComponentName() { return TypeDescriptor.GetComponentName(this, true); } /// public TypeConverter GetConverter() { return TypeDescriptor.GetConverter(this, true); } /// public EventDescriptor GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } /// public PropertyDescriptor GetDefaultProperty() { return TypeDescriptor.GetDefaultProperty(this, true); } /// public object GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } /// public EventDescriptorCollection GetEvents(Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } /// public EventDescriptorCollection GetEvents() { return TypeDescriptor.GetEvents(this, true); } /// public object GetPropertyOwner(PropertyDescriptor pd) { return this; } #endregion /// /// Initializes a new instance of the class. /// public MapLayer() { visible = true; shapefile = ""; shapes = new ShapeCollection(this); box = new BoundingBox(); values = new SortedList(); counts = new SortedList(); function = TotalType.Sum; filter = ""; spatialColumn = ""; spatialValue = ""; latitudeValue = ""; longitudeValue = ""; labelValue = ""; analyticalValue = ""; labelColumn = ""; labelFormat = ""; defaultShapeStyle = new ShapeStyle(); colorRanges = new ColorRanges(); sizeRanges = new SizeRanges(); labelKind = MapLabelKind.Name; accuracy = 2; labelsVisibleAtZoom = 1; zoomPolygon = ""; BaseName = "Layer"; SetFlags(Flags.CanShowChildrenInReportTree, false); } } }