using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using OSGeo.MapGuide;
using System.Drawing;
using System.ComponentModel;
using System.IO;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Threading;
using System.Xml;
using System.Collections.Specialized;
namespace OSGeo.MapGuide.Viewer
{
///
/// A map viewer component
///
public class MgMapViewer : Control, IMapViewer
{
private BackgroundWorker renderWorker;
private MgResourceService _resSvc;
private MgMapBase _map;
private MgSelectionBase _selection;
private MgMapViewerProvider _provider;
private MgViewerRenderingOptions _overlayRenderOpts;
private MgViewerRenderingOptions _selectionRenderOpts;
private MgWktReaderWriter _wktRW;
private MgAgfReaderWriter _agfRW;
private MgGeometryFactory _geomFact;
private MgMeasure _mapMeasure;
private Color _mapBgColor;
private bool firstRun = true;
private double _orgX1;
private double _orgX2;
private double _orgY1;
private double _orgY2;
private double _extX1;
private double _extX2;
private double _extY1;
private double _extY2;
private Image _selectionImage;
private Image _mapImage;
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal Image Image
{
get { return _mapImage; }
set
{
_mapImage = value;
//Invalidate();
}
}
const double MINIMUM_ZOOM_SCALE = 5.0;
#if VIEWER_DEBUG
private MgdLayer _debugLayer;
private void CreateDebugFeatureSource()
{
var id = new MgDataPropertyDefinition("ID");
id.DataType = MgPropertyType.Int32;
id.Nullable = false;
id.SetAutoGeneration(true);
var geom = new MgGeometricPropertyDefinition("Geometry");
geom.GeometryTypes = MgFeatureGeometricType.Point;
geom.SpatialContextAssociation = "MapCs";
var cls = new MgClassDefinition();
cls.Name = "Debug";
var props = cls.GetProperties();
props.Add(id);
props.Add(geom);
var idProps = cls.GetIdentityProperties();
idProps.Add(id);
cls.DefaultGeometryPropertyName = "Geometry";
var schema = new MgFeatureSchema("Default", "Default schema");
var classes = schema.GetClasses();
classes.Add(cls);
//We can make anything up here, there's no real concept of sessions
var sessionId = Guid.NewGuid().ToString();
var debugFsId = new MgResourceIdentifier("Session:" + sessionId + "//Debug" + Guid.NewGuid().ToString() + ".FeatureSource");
var createSdf = new MgCreateSdfParams("MapCs", _map.GetMapSRS(), schema);
var featureSvc = (MgdFeatureService)fact.CreateService(MgServiceType.FeatureService);
var resSvc = (MgResourceService)fact.CreateService(MgServiceType.ResourceService);
featureSvc.CreateFeatureSource(debugFsId, createSdf);
byte[] bytes = Encoding.UTF8.GetBytes(string.Format(Debug.DebugLayer, debugFsId.ToString(), "Default:Debug", "Geometry"));
var source = new MgByteSource(bytes, bytes.Length);
var debugLayerId = new MgResourceIdentifier("Session:" + sessionId + "//" + debugFsId.Name + ".LayerDefinition");
var breader = source.GetReader();
resSvc.SetResource(debugLayerId, breader, null);
_debugLayer = new MgdLayer(debugLayerId, resSvc);
_debugLayer.SetLegendLabel("Debug Layer");
_debugLayer.SetVisible(true);
_debugLayer.SetDisplayInLegend(true);
var mapLayers = _map.GetLayers();
mapLayers.Insert(0, _debugLayer);
UpdateCenterDebugPoint();
}
private MgPropertyCollection _debugCenter;
private void UpdateCenterDebugPoint()
{
if (_debugCenter == null)
_debugCenter = new MgPropertyCollection();
var center = _wktRW.Read("POINT (" + _map.ViewCenter.Coordinate.X + " " + _map.ViewCenter.Coordinate.Y + ")");
var agf = _agfRW.Write(center);
if (!_debugCenter.Contains("Geometry"))
{
MgGeometryProperty geom = new MgGeometryProperty("Geometry", agf);
_debugCenter.Add(geom);
}
else
{
MgGeometryProperty geom = (MgGeometryProperty)_debugCenter.GetItem("Geometry");
geom.SetValue(agf);
}
int deleted = _debugLayer.DeleteFeatures("");
Trace.TraceInformation("Deleted {0} debug points", deleted);
var reader = _debugLayer.InsertFeatures(_debugCenter);
int inserted = 0;
while (reader.ReadNext())
{
inserted++;
}
reader.Close();
Trace.TraceInformation("Added {0} debug points", inserted);
_debugLayer.ForceRefresh();
}
#endif
private MgCoordinateSystem _mapCs;
///
/// Initializes a new instance of the class.
///
public MgMapViewer()
{
this.ShowVertexCoordinatesWhenDigitizing = false;
this.FeatureTooltipsEnabled = false;
this.TooltipsEnabled = false;
this.ZoomInFactor = 0.5;
this.ZoomOutFactor = 2.0;
this.SelectionColor = Color.Blue;
this.DigitizingFillTransparency = 100;
this.DigitizingOutline = Brushes.Red;
this.DigitzingFillColor = Color.White;
this.TooltipFillColor = Color.LightYellow;
this.TooltipFillTransparency = 200;
this.ActiveTool = MapActiveTool.None;
this.DoubleBuffered = true;
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
_mapBgColor = Color.Transparent;
renderWorker = new BackgroundWorker();
renderWorker.DoWork += renderWorker_DoWork;
renderWorker.RunWorkerCompleted += renderWorker_RunWorkerCompleted;
base.MouseUp += OnMapMouseUp;
base.MouseMove += OnMapMouseMove;
base.MouseDown += OnMapMouseDown;
base.MouseClick += OnMapMouseClick;
base.MouseDoubleClick += OnMapMouseDoubleClick;
base.MouseHover += OnMapMouseHover;
base.MouseEnter += OnMouseEnter;
}
///
/// Raises the event.
///
/// A that contains the event data.
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
CancelDigitization();
}
}
private void CancelDigitization()
{
if (this.DigitizingType != MapDigitizationType.None)
{
dPath.Clear();
dPtStart.X = 0;
dPtStart.Y = 0;
this.DigitizingType = MapDigitizationType.None;
Trace.TraceInformation("Digitization cancelled");
this.Invalidate();
}
}
void OnMouseEnter(object sender, EventArgs e)
{
this.Focus();
}
void OnMapMouseHover(object sender, EventArgs e)
{
HandleMouseHover(e);
}
private void HandleMouseHover(EventArgs e)
{
}
///
/// Releases the unmanaged resources used by the and its child controls and optionally releases the managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (disposing)
{
base.MouseUp -= OnMapMouseUp;
base.MouseMove -= OnMapMouseMove;
base.MouseDown -= OnMapMouseDown;
base.MouseClick -= OnMapMouseClick;
base.MouseDoubleClick -= OnMapMouseDoubleClick;
base.MouseHover -= OnMapMouseHover;
base.MouseEnter -= OnMouseEnter;
if (renderWorker != null)
{
renderWorker.DoWork -= renderWorker_DoWork;
renderWorker.RunWorkerCompleted -= renderWorker_RunWorkerCompleted;
}
if (_resSvc != null)
{
_resSvc.Dispose();
_resSvc = null;
}
if (_selection != null)
{
_selection.Dispose();
_selection = null;
}
if (_mapCs != null)
{
_mapCs.Dispose();
_mapCs = null;
}
if (_agfRW != null)
{
_agfRW.Dispose();
_agfRW = null;
}
if (_wktRW != null)
{
_wktRW.Dispose();
_wktRW = null;
}
if (_geomFact != null)
{
_geomFact.Dispose();
_geomFact = null;
}
if (_mapMeasure != null)
{
_mapMeasure.Dispose();
_mapMeasure = null;
}
}
base.Dispose(disposing);
}
private Color _selColor;
///
/// Gets or sets the color used to render selected features
///
[Category("MapGuide Viewer")]
[Description("The color to use for active selections")]
public Color SelectionColor
{
get { return _selColor; }
set
{
_selColor = value;
OnPropertyChanged("SelectionColor");
}
}
private Color _tooltipFillColor;
///
/// Gets or sets the color of the tooltip fill.
///
///
/// The color of the tooltip fill.
///
[Category("MapGuide Viewer")]
[Description("The color background for feature tooltips")]
internal Color TooltipFillColor
{
get { return _tooltipFillColor; }
set
{
if (!value.Equals(_tooltipFillColor))
{
_tooltipFillColor = value;
OnPropertyChanged("TooltipFillColor");
}
}
}
private int _tooltipFillTransparency;
[Category("MapGuide Viewer")]
[Description("The color background transparency for feature tooltips")]
[DefaultValue(200)]
internal int TooltipFillTransparency
{
get { return _tooltipFillTransparency; }
set
{
if (!value.Equals(_tooltipFillTransparency))
{
_tooltipFillTransparency = value;
OnPropertyChanged("TooltipFillTransparency");
}
}
}
private void UpdateSelectionRenderingOptions()
{
var value = this.SelectionColor;
if (_selectionRenderOpts != null)
{
var color = _selectionRenderOpts.Color;
if (color != null && (color.Red != value.R ||
color.Green != value.G ||
color.Blue != value.B))
{
_selectionRenderOpts = null;
_selectionRenderOpts = CreateSelectionRenderingOptions(value.R, value.G, value.B);
Trace.TraceInformation("Selection color updated to ({0}, {1}, {2})", value.R, value.G, value.B);
}
}
}
///
/// Gets the coordinate system of the runtime map
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MgCoordinateSystem CoordinateSystem { get { return _mapCs; } }
private bool _showVertexCoords;
///
/// Gets or sets a value indicating whether [show vertex coordinates when digitizing].
///
///
/// true if [show vertex coordinates when digitizing]; otherwise, false.
///
[Category("MapGuide Viewer")]
[Description("Indicates whether coordinate values are shown when digitizing geometry")]
[DefaultValue(false)]
public bool ShowVertexCoordinatesWhenDigitizing
{
get { return _showVertexCoords; }
set
{
if (!value.Equals(_showVertexCoords))
{
_showVertexCoords = value;
OnPropertyChanged("ShowVertexCoordinatesWhenDigitizing");
}
}
}
///
/// Raises the event.
///
/// A that contains the event data.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Trace.TraceInformation("OnPaint(e)");
if (!translate.IsEmpty)
e.Graphics.TranslateTransform(translate.X, translate.Y);
if (_mapImage != null)
{
Trace.TraceInformation("Render Map");
e.Graphics.DrawImage(_mapImage, new PointF(0, 0));
}
//Thread.Sleep(100);
if (_selectionImage != null)
{
Trace.TraceInformation("Render Selection");
e.Graphics.DrawImage(_selectionImage, new PointF(0, 0));
}
if (isDragging && (this.ActiveTool == MapActiveTool.Select || this.ActiveTool == MapActiveTool.ZoomIn))
{
DrawDragRectangle(e);
}
else
{
if (this.DigitizingType != MapDigitizationType.None)
{
if (this.DigitizingType == MapDigitizationType.Point)
{
DrawTrackingTooltip(e, "Click to finish. Press ESC to cancel");
}
else
{
if (!dPtStart.IsEmpty)
{
switch (this.DigitizingType)
{
case MapDigitizationType.Circle:
DrawTracingCircle(e);
break;
case MapDigitizationType.Line:
DrawTracingLine(e);
break;
case MapDigitizationType.Rectangle:
DrawTracingRectangle(e);
break;
}
}
else if (dPath.Count > 0)
{
switch (this.DigitizingType)
{
case MapDigitizationType.LineString:
DrawTracingLineString(e);
break;
case MapDigitizationType.Polygon:
DrawTracingPolygon(e);
break;
}
}
}
}
else //None
{
if (this.ActiveTool != MapActiveTool.None)
{
if (!string.IsNullOrEmpty(_activeTooltipText))
DrawTrackingTooltip(e, _activeTooltipText);
}
}
}
}
private Brush _digitizingOutline;
[Category("MapGuide Viewer")]
[Description("The outline color for geometries being digitized")]
internal Brush DigitizingOutline
{
get { return _digitizingOutline; }
set
{
_digitizingOutline = value;
OnPropertyChanged("DigitizingOutline");
}
}
private int _digitizingFillTransparency;
[Category("MapGuide Viewer")]
[Description("The fill color transparency for geometries being digitized")]
[DefaultValue(100)]
internal int DigitizingFillTransparency
{
get { return _digitizingFillTransparency; }
set
{
if (!value.Equals(_digitizingFillTransparency))
{
_digitizingFillTransparency = value;
OnPropertyChanged("DigitizingFillTransparency");
}
}
}
private Color _digitizingFillColor;
[Category("MapGuide Viewer")]
[Description("The fill color for geometries being digitized")]
internal Color DigitzingFillColor
{
get { return _digitizingFillColor; }
set
{
_digitizingFillColor = value;
OnPropertyChanged("DigitzingFillColor");
}
}
private Pen CreateOutlinePen()
{
return new Pen(this.DigitizingOutline, 2.0f);
}
private Brush CreateFillBrush()
{
return new SolidBrush(Color.FromArgb(this.DigitizingFillTransparency, this.DigitzingFillColor));
}
private static double GetDistanceBetween(PointF a, PointF b)
{
return (Math.Sqrt(Math.Pow(Math.Abs(a.X - b.X), 2) + Math.Pow(Math.Abs(a.Y - b.Y), 2)));
}
private void DrawVertexCoordinates(PaintEventArgs e, double devX, double devY, bool mapSpace)
{
if (!this.ShowVertexCoordinatesWhenDigitizing)
return;
string text = "";
if (mapSpace)
{
var mapPt = ScreenToMapUnits(devX, devY);
text = string.Format("X: {0}, Y: {1}", mapPt.X, mapPt.Y);
}
else
{
text = string.Format("X: {0}, Y: {1}", devX, devY);
}
var f = Control.DefaultFont;
SizeF size = e.Graphics.MeasureString(text, Font);
var vertex = new PointF((float)devX, (float)devY);
//Offset so that the "box" for this string is centered on the vertex itself
vertex.X -= (size.Width / 2);
//Fill the surrounding box
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(200, Color.WhiteSmoke)), vertex.X, vertex.Y, size.Width, size.Height);
e.Graphics.DrawRectangle(Pens.Red, vertex.X, vertex.Y, size.Width, size.Height);
//Draw the string
e.Graphics.DrawString(text, f, Brushes.Black, vertex);
}
private void DrawTrackingTooltip(PaintEventArgs e, string text)
{
if (string.IsNullOrEmpty(text)) //Nothing to draw
return;
var f = Control.DefaultFont;
int height = 0;
int width = 0;
string [] tokens = text.Split(new string[] {"\\n", "\\r\\n", "\n", Environment.NewLine }, StringSplitOptions.None);
foreach(string t in tokens)
{
var size = e.Graphics.MeasureString(t, f);
height += (int)size.Height;
width = Math.Max(width, (int)size.Width);
}
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(this.TooltipFillTransparency, this.TooltipFillColor)), new Rectangle(_mouseX, _mouseY, width + 10, height + 4));
float y = 2.0f;
float heightPerLine = height / tokens.Length;
foreach (string t in tokens)
{
e.Graphics.DrawString(t, f, Brushes.Black, new PointF(_mouseX + 5.0f, _mouseY + y));
y += heightPerLine;
}
}
private void DrawTracingCircle(PaintEventArgs e)
{
var pt2 = new Point(dPtStart.X, dPtStart.Y);
var diameter = (float)GetDistanceBetween(dPtStart, new PointF(_mouseX, _mouseY)) * 2.0f;
//Trace.TraceInformation("Diameter ({0}, {1} -> {2}, {3}): {4}", dPtStart.X, dPtStart.Y, _mouseX, _mouseY, diameter);
pt2.Offset((int)-(diameter / 2), (int)-(diameter / 2));
e.Graphics.DrawEllipse(CreateOutlinePen(), pt2.X, pt2.Y, diameter, diameter);
e.Graphics.FillEllipse(CreateFillBrush(), pt2.X, pt2.Y, diameter, diameter);
DrawTrackingTooltip(e, "Click to finish. Press ESC to cancel");
}
private void DrawTracingLine(PaintEventArgs e)
{
e.Graphics.DrawLine(CreateOutlinePen(), dPtStart, new Point(_mouseX, _mouseY));
DrawVertexCoordinates(e, dPtStart.X, dPtStart.Y, true);
DrawVertexCoordinates(e, _mouseX, _mouseY, true);
DrawTrackingTooltip(e, "Click to finish. Press ESC to cancel");
}
private void DrawTracingLineString(PaintEventArgs e)
{
//Not enough points to constitute a line string or polygon
if (dPath.Count < 2)
return;
e.Graphics.DrawLines(CreateOutlinePen(), dPath.ToArray());
foreach (var pt in dPath)
{
DrawVertexCoordinates(e, pt.X, pt.Y, true);
}
DrawTrackingTooltip(e, "Click again to add a new vertex.\nDouble-click to finish. Press ESC to cancel");
}
private void DrawTracingPolygon(PaintEventArgs e)
{
//Not enough points to constitute a line string or polygon
if (dPath.Count < 2)
return;
e.Graphics.DrawPolygon(CreateOutlinePen(), dPath.ToArray());
e.Graphics.FillPolygon(CreateFillBrush(), dPath.ToArray());
foreach (var pt in dPath)
{
DrawVertexCoordinates(e, pt.X, pt.Y, true);
}
DrawTrackingTooltip(e, "Click again to add a new vertex.\nDouble-click to finish. Press ESC to cancel");
}
private void DrawTracingRectangle(PaintEventArgs e)
{
var rect = GetRectangle(dragStart, new Point(_mouseX, _mouseY));
if (rect.HasValue)
{
var r = rect.Value;
Trace.TraceInformation("Draw rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
e.Graphics.DrawRectangle(CreateOutlinePen(), r);
Trace.TraceInformation("Fill rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
e.Graphics.FillRectangle(CreateFillBrush(), r);
DrawVertexCoordinates(e, r.Left, r.Top, true);
DrawVertexCoordinates(e, r.Left, r.Bottom, true);
DrawVertexCoordinates(e, r.Right, r.Top, true);
DrawVertexCoordinates(e, r.Right, r.Bottom, true);
DrawTrackingTooltip(e, "Click to finish. Press ESC to cancel");
}
}
private void DrawDragRectangle(PaintEventArgs e)
{
var rect = GetRectangle(dragStart, new Point(_mouseX, _mouseY));
if (rect.HasValue)
{
var r = rect.Value;
Trace.TraceInformation("Draw rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
e.Graphics.DrawRectangle(CreateOutlinePen(), r);
Trace.TraceInformation("Fill rangle ({0} {1}, {2} {3})", r.Left, r.Top, r.Right, r.Bottom);
e.Graphics.FillRectangle(CreateFillBrush(), r);
}
}
private bool _featTooltipsEnabled;
///
/// Gets or sets whether feature tooltips are enabled. If set to true, tooltip queries are
/// executed at the current mouse position if the active tool is Pan or Select
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool FeatureTooltipsEnabled
{
get { return _featTooltipsEnabled; }
set
{
if (value.Equals(_featTooltipsEnabled))
return;
_featTooltipsEnabled = value;
if (!value)
{
_activeTooltipText = null;
Invalidate();
}
OnPropertyChanged("FeatureTooltipsEnabled");
}
}
///
/// Internally determines whether a tooltip query can be executed. Must be true along
/// with in order for a tooltip query to be executed
///
internal bool TooltipsEnabled
{
get;
set;
}
#region Digitization
/*
* Digitization behaviour with respect to mouse and paint events
*
* Point:
* MouseClick -> Invoke Callback
*
* Rectangle:
* MouseClick -> set start, temp end
* MouseMove -> update temp end
* OnPaint -> Draw rectangle from start/temp end
* MouseClick -> set end -> Invoke Callback
*
* Line:
* MouseClick -> set start, temp end
* MouseMove -> update temp end
* OnPaint -> Draw line from start/temp end
* MouseClick -> set end -> Invoke Callback
*
* LineString:
* MouseClick -> append point to path
* MouseMove -> update temp end
* OnPaint -> Draw line with points in path + temp end
* MouseDoubleClick -> append point to path -> Invoke Callback
*
* Polygon:
* MouseClick -> append point to path
* MouseMove -> update temp end
* OnPaint -> Draw polygon fill with points in path + temp end
* MouseDoubleClick -> append point to path -> Invoke Callback
*
* Circle:
* MouseClick -> set start, temp end
* MouseMove -> update temp end
* OnPaint -> Draw circle from start with radius = (dist from start to temp end)
* MouseClick -> set end -> Invoke Callback
*/
private Point dPtStart; //Rectangle, Line, Circle
private Point dPtEnd; //Rectangle, Line, Circle
private List dPath = new List(); //LineString, Polygon
private Delegate _digitzationCallback;
private bool _digitizationYetToStart = true;
///
/// Starts the digitization process for a circle
///
/// The callback to be invoked when the digitization process completes
public void DigitizeCircle(CircleDigitizationCallback callback)
{
this.DigitizingType = MapDigitizationType.Circle;
_digitzationCallback = callback;
_digitizationYetToStart = true;
}
///
/// Starts the digitization process for a line
///
/// The callback to be invoked when the digitization process completes
public void DigitizeLine(LineDigitizationCallback callback)
{
this.DigitizingType = MapDigitizationType.Line;
_digitzationCallback = callback;
_digitizationYetToStart = true;
}
///
/// Starts the digitization process for a point
///
/// The callback to be invoked when the digitization process completes
public void DigitizePoint(PointDigitizationCallback callback)
{
this.DigitizingType = MapDigitizationType.Point;
_digitzationCallback = callback;
_digitizationYetToStart = true;
}
///
/// Starts the digitization process for a polygon
///
/// The callback to be invoked when the digitization process completes
public void DigitizePolygon(PolygonDigitizationCallback callback)
{
this.DigitizingType = MapDigitizationType.Polygon;
_digitzationCallback = callback;
_digitizationYetToStart = true;
}
///
/// Starts the digitization process for a line string (polyline)
///
/// The callback to be invoked when the digitization process completes
public void DigitizeLineString(LineStringDigitizationCallback callback)
{
this.DigitizingType = MapDigitizationType.LineString;
_digitzationCallback = callback;
_digitizationYetToStart = true;
}
///
/// Starts the digitization process for a rectangle
///
/// The callback to be invoked when the digitization process completes
public void DigitizeRectangle(RectangleDigitizationCallback callback)
{
this.DigitizingType = MapDigitizationType.Rectangle;
_digitzationCallback = callback;
_digitizationYetToStart = true;
}
private void ResetDigitzationState()
{
_digitzationCallback = null;
dPath.Clear();
dPtEnd.X = dPtStart.Y = 0;
dPtStart.X = dPtStart.Y = 0;
this.DigitizingType = MapDigitizationType.None;
Invalidate();
}
private void OnCircleDigitized(Point ptStart, Point ptEnd)
{
var mapPt = ScreenToMapUnits(ptStart.X, ptStart.Y);
var mapEnd = ScreenToMapUnits(ptEnd.X, ptEnd.Y);
var radius = Math.Sqrt(Math.Pow(mapEnd.X - mapPt.X, 2) + Math.Pow(mapEnd.Y - mapPt.Y, 2));
var cb = (CircleDigitizationCallback)_digitzationCallback;
ResetDigitzationState();
cb(mapPt.X, mapPt.Y, radius);
}
private void OnPolygonDigitized(List path)
{
double[,] coords = new double[path.Count, 2];
for (int i = 0; i < path.Count; i++)
{
var pt = ScreenToMapUnits(path[i].X, path[i].Y);
coords[i, 0] = pt.X;
coords[i, 1] = pt.Y;
}
var cb = (PolygonDigitizationCallback)_digitzationCallback;
ResetDigitzationState();
cb(coords);
}
private void OnLineStringDigitized(List path)
{
double[,] coords = new double[path.Count, 2];
for (int i = 0; i < path.Count; i++)
{
var pt = ScreenToMapUnits(path[i].X, path[i].Y);
coords[i, 0] = pt.X;
coords[i, 1] = pt.Y;
}
var cb = (LineStringDigitizationCallback)_digitzationCallback;
ResetDigitzationState();
cb(coords);
}
private void OnLineDigitized(Point start, Point end)
{
var mapStart = ScreenToMapUnits(start.X, start.Y);
var mapEnd = ScreenToMapUnits(end.X, end.Y);
var cb = (LineDigitizationCallback)_digitzationCallback;
ResetDigitzationState();
cb(mapStart.X, mapStart.Y, mapEnd.X, mapEnd.Y);
}
private void OnRectangleDigitized(Rectangle rect)
{
var mapLL = ScreenToMapUnits(rect.Left, rect.Bottom);
var mapUR = ScreenToMapUnits(rect.Right, rect.Top);
var cb = (RectangleDigitizationCallback)_digitzationCallback;
ResetDigitzationState();
cb(mapLL.X, mapLL.Y, mapUR.X, mapUR.Y);
}
private void OnPointDigitizationCompleted(Point p)
{
var mapPt = ScreenToMapUnits(p.X, p.Y);
var cb = (PointDigitizationCallback)_digitzationCallback;
ResetDigitzationState();
cb(mapPt.X, mapPt.Y);
}
#endregion
static MgViewerRenderingOptions CreateMapRenderingOptions(short red, short green, short blue)
{
return new MgViewerRenderingOptions("PNG", 2, new MgColor(red, green, blue));
}
static MgViewerRenderingOptions CreateSelectionRenderingOptions(short red, short green, short blue)
{
return new MgViewerRenderingOptions("PNG", (1 | 4), new MgColor(red, green, blue));
}
///
/// Initializes this viewer with the specified runtime map
///
/// The provider.
public void Init(MgMapViewerProvider provider)
{
if (_agfRW == null)
_agfRW = new MgAgfReaderWriter();
if (_wktRW == null)
_wktRW = new MgWktReaderWriter();
if (_geomFact == null)
_geomFact = new MgGeometryFactory();
_provider = provider;
_mapCs = _provider.GetMapCoordinateSystem();
_mapMeasure = _mapCs.GetMeasure();
if (_resSvc == null)
{
if (_resSvc == null)
_resSvc = (MgResourceService)_provider.CreateService(MgServiceType.ResourceService);
}
_overlayRenderOpts = CreateMapRenderingOptions(0, 0, 255);
_selectionRenderOpts = CreateSelectionRenderingOptions(0, 0, 255);
_provider = provider;
_map = provider.GetMap();
var bgColor = _map.GetBackgroundColor();
if (bgColor.Length == 8 || bgColor.Length == 6)
{
_mapBgColor = ColorTranslator.FromHtml("#" + bgColor);
this.BackColor = _mapBgColor;
}
_provider.SetDisplaySize(this.Width, this.Height);
_selection = _provider.CreateSelectionForMap();
var env = _map.GetMapExtent();
var ll = env.LowerLeftCoordinate;
var ur = env.UpperRightCoordinate;
_extX1 = _orgX1 = ll.X;
_extY2 = _orgY2 = ll.Y;
_extX2 = _orgX2 = ur.X;
_extY1 = _orgY1 = ur.Y;
if ((_orgX1 - _orgX2) == 0 || (_orgY1 - _orgY2) == 0)
{
_extX1 = _orgX1 = -.1;
_extY2 = _orgX2 = .1;
_extX2 = _orgY1 = -.1;
_extY1 = _orgY2 = .1;
}
if (this.ConvertTiledGroupsToNonTiled)
{
var groups = _map.GetLayerGroups();
for (int i = 0; i < groups.GetCount(); i++)
{
var group = groups.GetItem(i);
_provider.MakeGroupNormal(group);
}
}
_provider.RebuildLayerInfoCache();
_provider.CacheGeometryProperties(_map.GetLayers());
#if VIEWER_DEBUG
CreateDebugFeatureSource();
#endif
this.Focus();
var handler = this.ViewerInitialized;
if (handler != null)
handler(this, EventArgs.Empty);
InitialMapView();
}
internal double MetersPerUnit
{
get
{
return _provider.GetMetersPerUnit();
}
}
private double CalculateScale(double mcsW, double mcsH, int devW, int devH)
{
var mpu = this.MetersPerUnit;
var mpp = 0.0254 / _map.DisplayDpi;
if (devH * mcsW > devW * mcsH)
return mcsW * mpu / (devW * mpp); //width-limited
else
return mcsH * mpu / (devH * mpp); //height-limited
}
///
/// Gets or sets a value indicating whether tiled groups are converted to normal groups. Must be set before
/// a map is loaded via the method
///
[Category("MapGuide Viewer")]
[Description("If true, the map being viewed will have all its tiled groups converted to non-tiled groups. Tiled groups are not supported by this viewer and are not rendered")]
[DefaultValue(false)]
public bool ConvertTiledGroupsToNonTiled
{
get;
set;
}
///
/// Raised when the viewer has been initialized
///
[Category("MapGuide Viewer")]
[Description("Raised when the viewer has been initialized with a runtime map")]
public event EventHandler ViewerInitialized;
private System.Timers.Timer _delayedResizeTimer;
void OnDelayResizeTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
var action = new MethodInvoker(() =>
{
if (_map != null)
{
Trace.TraceInformation("Performing delayed resize to (" + this.Width + ", " + this.Height + ")");
_provider.SetDisplaySize(this.Width, this.Height);
UpdateExtents();
RefreshMap(false);
}
_delayedResizeTimer.Stop();
Trace.TraceInformation("Delayed resize timer stopped");
});
if (this.InvokeRequired)
this.Invoke(action);
else
action();
}
void OnControlResized(object sender, EventArgs e)
{
if (_delayedResizeTimer == null)
{
_delayedResizeTimer = new System.Timers.Timer();
_delayedResizeTimer.Elapsed += OnDelayResizeTimerElapsed;
Trace.TraceInformation("Delay resize timer initialized");
}
if (_delayedResizeTimer.Enabled)
{
Trace.TraceInformation("Stopped delayed resize");
_delayedResizeTimer.Stop();
}
_delayedResizeTimer.Interval = 500;
_delayedResizeTimer.Start();
Trace.TraceInformation("Delayed resize re-scheduled");
}
///
/// Clears the current selection
///
public void ClearSelection()
{
_provider.ClearSelection(_selection);
if (_selectionImage != null)
{
_selectionImage.Dispose();
_selectionImage = null;
}
var handler = this.SelectionChanged;
if (handler != null)
handler(this, EventArgs.Empty);
this.Refresh();
}
///
/// Gets the current runtime map
///
///
public MgMapBase GetMap()
{
return _map;
}
///
/// Gets the map viewer provider for this control
///
///
public MgMapViewerProvider GetProvider()
{
return _provider;
}
///
/// Gets the selection set of the runtime map
///
///
public MgSelectionBase GetSelection()
{
return _selection;
}
private bool HasSelection()
{
var layers = _selection.GetLayers();
return layers != null;
}
private static int GetSelectionTotal(MgSelectionBase sel)
{
int total = 0;
var layers = sel.GetLayers();
if (layers != null)
{
for (int i = 0; i < layers.GetCount(); i++)
{
var layer = layers.GetItem(i);
total += sel.GetSelectedFeaturesCount(layer, layer.FeatureClassName);
}
}
return total;
}
private MapDigitizationType _digitizingType = MapDigitizationType.None;
///
/// Gets the type of object being currently digitized. If the digitization type is None, then
/// the viewer is not currently digitizing
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MapDigitizationType DigitizingType
{
get { return _digitizingType; }
private set
{
if (_digitizingType.Equals(value))
return;
if (value != MapDigitizationType.None)
{
this.ActiveTool = MapActiveTool.None;
this.Cursor = Cursors.Cross;
}
else
{
this.Cursor = Cursors.Default;
}
_digitizingType = value;
OnPropertyChanged("DigitizingType");
}
}
class RenderWorkArgs
{
public MgViewerRenderingOptions SelectionRenderingOptions { get; set; }
public MgViewerRenderingOptions MapRenderingOptions { get; set; }
public bool RaiseEvents { get; set; }
}
class RenderResult
{
public Image Image { get; set; }
public Image SelectionImage { get; set; }
public bool RaiseEvents { get; set; }
}
///
/// Refreshes the current map view
///
public void RefreshMap()
{
RefreshMap(true);
}
///
/// Updates the rendered selection. Call this method if you have manipulated the selection
/// set outside of the viewer
///
public void UpdateSelection()
{
RenderSelection();
}
internal void RenderSelection()
{
//This is our refresh action
RefreshAction action = new RefreshAction(() =>
{
if (HasSelection())
{
this.IsBusy = true;
UpdateSelectionRenderingOptions();
renderWorker.RunWorkerAsync(new RenderWorkArgs()
{
SelectionRenderingOptions = _selectionRenderOpts,
RaiseEvents = false,
});
}
});
//If an existing rendering operation is in progress queue it if
//there isn't one queued. Because there is no point in doing the
//same thing more than once
if (this.IsBusy)
{
if (_queuedRefresh == null) //No refresh operations currently queued
_queuedRefresh = action;
}
else //Otherwise execute it immediately
{
action();
}
}
delegate void RefreshAction();
RefreshAction _queuedRefresh = null;
internal void RefreshMap(bool raiseEvents)
{
//This is our refresh action
RefreshAction action = new RefreshAction(() =>
{
var args = new RenderWorkArgs()
{
MapRenderingOptions = _overlayRenderOpts,
RaiseEvents = raiseEvents
};
if (HasSelection())
{
UpdateSelectionRenderingOptions();
args.SelectionRenderingOptions = _selectionRenderOpts;
}
this.IsBusy = true;
renderWorker.RunWorkerAsync(args);
});
//If an existing rendering operation is in progress queue it if
//there isn't one queued. Because there is no point in doing the
//same thing more than once
if (this.IsBusy)
{
if (_queuedRefresh == null) //No refresh operations currently queued
_queuedRefresh = action;
}
else //Otherwise execute it immediately
{
action();
}
}
///
/// Raised when the map has been refreshed
///
[Category("MapGuide Viewer")]
[Description("Raised after the viewer has refreshed")]
public event EventHandler MapRefreshed;
private bool _busy = false;
#if TRACE
private Stopwatch _renderSw = new Stopwatch();
#endif
///
/// Indicates whether a rendering operation is in progress
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsBusy
{
get { return _busy; }
private set
{
if (_busy.Equals(value))
return;
_busy = value;
#if TRACE
Trace.TraceInformation("IsBusy = " + _busy);
if (value)
{
_renderSw.Reset();
_renderSw.Start();
}
else
{
_renderSw.Stop();
Trace.TraceInformation("Rendering operation took {0}ms", _renderSw.ElapsedMilliseconds);
}
#endif
OnPropertyChanged("IsBusy");
}
}
///
/// Pans the view left by a pre-defined distance
///
///
public void PanLeft(bool refresh)
{
PanTo(_extX1 + (_extX2 - _extX1) / 3, _extY2 + (_extY1 - _extY2) / 2, refresh);
}
///
/// Pans the view up by a pre-defined distance
///
///
public void PanUp(bool refresh)
{
PanTo(_extX1 + (_extX2 - _extX1) / 2, _extY1 - (_extY1 - _extY2) / 3, refresh);
}
///
/// Pans the view right by a pre-defined distance
///
///
public void PanRight(bool refresh)
{
PanTo(_extX2 - (_extX2 - _extX1) / 3, _extY2 + (_extY1 - _extY2) / 2, refresh);
}
///
/// Pans the view down by a pre-defined distance
///
///
public void PanDown(bool refresh)
{
PanTo(_extX1 + (_extX2 - _extX1) / 2, _extY2 + (_extY1 - _extY2) / 3, refresh);
}
///
/// Zooms the extents.
///
public void ZoomExtents()
{
var scale = CalculateScale((_orgX2 - _orgX1), (_orgY1 - _orgY2), this.Width, this.Height);
ZoomToView(_orgX1 + ((_orgX2 - _orgX1) / 2), _orgY2 + ((_orgY1 - _orgY2) / 2), scale, true);
}
///
/// Zooms to the view defined by the specified extent
///
///
///
///
///
public void ZoomToExtents(double llx, double lly, double urx, double ury)
{
var scale = CalculateScale((urx - llx), (ury - lly), this.Width, this.Height);
ZoomToView(llx + ((urx - llx) / 2), ury + ((lly - ury) / 2), scale, true);
}
///
/// Zooms to scale.
///
/// The scale.
public void ZoomToScale(double scale)
{
ZoomToView(_extX1 + (_extX2 - _extX1) / 2, _extY2 + (_extY1 - _extY2) / 2, scale, true);
}
///
/// Zooms to the specified map view
///
///
///
///
///
public void ZoomToView(double x, double y, double scale, bool refresh)
{
ZoomToView(x, y, scale, refresh, true);
}
internal void PanTo(double x, double y, bool refresh)
{
ZoomToView(x, y, _map.ViewScale, refresh);
}
private void UpdateExtents()
{
//Update current extents
double mpu = this.MetersPerUnit;
double scale = _map.ViewScale;
double mpp = 0.0254 / _map.DisplayDpi;
var pt = _map.ViewCenter;
var coord = pt.Coordinate;
var mcsWidth = _map.DisplayWidth * mpp * scale / mpu;
var mcsHeight = _map.DisplayHeight * mpp * scale / mpu;
_extX1 = coord.X - mcsWidth / 2;
_extY1 = coord.Y + mcsHeight / 2;
_extX2 = coord.X + mcsWidth / 2;
_extY2 = coord.Y - mcsHeight / 2;
}
internal void ZoomToView(double x, double y, double scale, bool refresh, bool raiseEvents)
{
_provider.SetViewCenterXY(x, y);
#if VIEWER_DEBUG
UpdateCenterDebugPoint();
//var mapExt = _map.MapExtent;
//var dataExt = _map.DataExtent;
Trace.TraceInformation("Map Extent is ({0},{1} {2},{3})", mapExt.LowerLeftCoordinate.X, mapExt.LowerLeftCoordinate.Y, mapExt.UpperRightCoordinate.X, mapExt.UpperRightCoordinate.Y);
Trace.TraceInformation("Data Extent is ({0},{1} {2},{3})", dataExt.LowerLeftCoordinate.X, dataExt.LowerLeftCoordinate.Y, dataExt.UpperRightCoordinate.X, dataExt.UpperRightCoordinate.Y);
Trace.TraceInformation("Center is (" + x + ", " + y + ")");
#endif
var oldScale = _map.ViewScale;
_provider.SetViewScale(Math.Max(scale, MINIMUM_ZOOM_SCALE));
if (oldScale != _map.ViewScale)
{
var handler = this.MapScaleChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
UpdateExtents();
#if VIEWER_DEBUG
Trace.TraceInformation("Current extents is ({0},{1} {2},{3})", _extX1, _extY1, _extX2, _extY2);
#endif
//Then refresh
if (refresh)
RefreshMap(raiseEvents);
}
///
/// Raised when the scale of the current runtime map has changed
///
[Category("MapGuide Viewer")]
[Description("Raised when the zoom scale of the map has changed")]
public event EventHandler MapScaleChanged;
///
/// Raised when the selection has changed. Note that programmatic selection modifications
/// will not raise this event.
///
[Category("MapGuide Viewer")]
[Description("Raised when active viewer selection has changed")]
public event EventHandler SelectionChanged;
private void renderWorker_DoWork(object sender, DoWorkEventArgs e)
{
var args = (RenderWorkArgs)e.Argument;
var res = new RenderResult() { RaiseEvents = args.RaiseEvents };
if (args.MapRenderingOptions != null)
{
var br = _provider.RenderDynamicOverlay(null, args.MapRenderingOptions);
byte[] b = new byte[br.GetLength()];
br.Read(b, b.Length);
using (var ms = new MemoryStream(b))
{
res.Image = Image.FromStream(ms);
}
}
if (args.SelectionRenderingOptions != null)
{
var br = _provider.RenderDynamicOverlay(_selection, args.SelectionRenderingOptions);
byte[] b = new byte[br.GetLength()];
br.Read(b, b.Length);
using (var ms = new MemoryStream(b))
{
res.SelectionImage = Image.FromStream(ms);
}
}
e.Result = res;
}
private void renderWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.IsBusy = AreWorkersBusy();
if (e.Error != null)
{
MessageBox.Show(e.Error.Message, "Error");
}
else
{
var res = (RenderResult)e.Result;
//reset translation
translate = new System.Drawing.Point();
bool bInvalidate = false;
//set the image
if (res.Image != null)
{
if (this.Image != null)
{
this.Image.Dispose();
this.Image = null;
}
Trace.TraceInformation("Set map image");
this.Image = res.Image;
bInvalidate = true;
}
if (res.SelectionImage != null)
{
if (_selectionImage != null)
{
_selectionImage.Dispose();
_selectionImage = null;
}
Trace.TraceInformation("Set selection image");
_selectionImage = res.SelectionImage;
bInvalidate = true;
}
//If there is a queued refresh action, execute it now
if (_queuedRefresh != null)
{
Trace.TraceInformation("Executing queued rendering operation");
_queuedRefresh();
_queuedRefresh = null;
}
else
{
if (bInvalidate)
Invalidate(true);
/*
var center = _map.ViewCenter;
var ext = _map.DataExtent;
System.Diagnostics.Trace.TraceInformation(
"**POST-RENDER**{2}Map Center: {0}, {1}{2}Lower left: {3}, {4}{2}Upper Right: {5}, {6}",
center.Coordinate.X,
center.Coordinate.Y,
Environment.NewLine,
ext.LowerLeftCoordinate.X,
ext.LowerLeftCoordinate.Y,
ext.UpperRightCoordinate.X,
ext.UpperRightCoordinate.Y);
*/
if (res.RaiseEvents)
{
var handler = this.MapRefreshed;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
}
}
private bool AreWorkersBusy()
{
return renderWorker.IsBusy;
}
///
/// Zooms to the initial map view
///
public void InitialMapView()
{
InitialMapView(true);
}
private void InitialMapView(bool refreshMap)
{
var scale = CalculateScale((_orgX2 - _orgX1), (_orgY1 - _orgY2), this.Width, this.Height);
ZoomToView(_orgX1 + ((_orgX2 - _orgX1) / 2), _orgY2 + ((_orgY1 - _orgY2) / 2), scale, refreshMap);
}
private static Rectangle? GetRectangle(Point dPtStart, Point dPtEnd)
{
int? left = null;
int? right = null;
int? top = null;
int? bottom = null;
if (dPtEnd.X < dPtStart.X)
{
if (dPtEnd.Y < dPtStart.Y)
{
left = dPtEnd.X;
bottom = dPtStart.Y;
top = dPtEnd.Y;
right = dPtStart.X;
}
else if (dPtEnd.Y > dPtStart.Y)
{
left = dPtEnd.X;
bottom = dPtEnd.Y;
top = dPtStart.Y;
right = dPtStart.X;
}
else
{
//Equal
}
}
else
{
if (dPtEnd.X > dPtStart.X)
{
if (dPtEnd.Y < dPtStart.Y)
{
left = dPtStart.X;
bottom = dPtStart.Y;
top = dPtEnd.Y;
right = dPtEnd.X;
}
else if (dPtEnd.Y > dPtStart.Y)
{
left = dPtStart.X;
bottom = dPtEnd.Y;
top = dPtStart.Y;
right = dPtEnd.X;
}
else
{
//Equal
}
}
//else equal
}
if (left.HasValue && right.HasValue && top.HasValue && bottom.HasValue)
{
return new Rectangle(left.Value, top.Value, (right.Value - left.Value), (bottom.Value - top.Value));
}
return null;
}
private double _zoomInFactor;
private double _zoomOutFactor;
///
/// Gets or sets the factor by which to multiply the scale to zoom in
///
[Category("MapGuide Viewer")]
[Description("The zoom in factor")]
public double ZoomInFactor
{
get { return _zoomInFactor; }
set
{
if (value.Equals(_zoomInFactor))
return;
_zoomInFactor = value;
OnPropertyChanged("ZoomInFactor");
}
}
///
/// Gets or sets the factor by which to multiply the scale to zoom out
///
[Category("MapGuide Viewer")]
[Description("The zoom out factor")]
public double ZoomOutFactor
{
get { return _zoomOutFactor; }
set
{
if (value.Equals(_zoomOutFactor))
return;
_zoomOutFactor = value;
OnPropertyChanged("ZoomOutFactor");
}
}
private static string MakeWktPolygon(double x1, double y1, double x2, double y2)
{
return "POLYGON((" + x1 + " " + y1 + ", " + x2 + " " + y1 + ", " + x2 + " " + y2 + ", " + x1 + " " + y2 + ", " + x1 + " " + y1 + "))";
}
private int? _lastTooltipX;
private int? _lastTooltipY;
private string QueryFirstVisibleTooltip(int x, int y)
{
//No intialized map
if (_map == null)
return "";
//No change in position
if (_lastTooltipX == x && _lastTooltipY == y && !string.IsNullOrEmpty(_activeTooltipText))
return _activeTooltipText;
if (_lastTooltipX.HasValue && _lastTooltipY.HasValue)
{
//Not considered a significant change
if (Math.Abs(x - _lastTooltipX.Value) < MOUSE_MOVE_TOLERANCE ||
Math.Abs(y - _lastTooltipY.Value) < MOUSE_MOVE_TOLERANCE)
return _activeTooltipText;
}
_lastTooltipX = x;
_lastTooltipY = y;
var layers = _map.GetLayers();
var mapPt1 = ScreenToMapUnits(x - 2, y - 2);
var mapPt2 = ScreenToMapUnits(x + 2, y + 2);
var ringCoords = new MgCoordinateCollection();
ringCoords.Add(_geomFact.CreateCoordinateXY(mapPt2.X, mapPt2.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapPt1.X, mapPt2.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapPt1.X, mapPt1.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapPt2.X, mapPt1.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapPt2.X, mapPt2.Y)); //Close it
var poly = _geomFact.CreatePolygon(_geomFact.CreateLinearRing(ringCoords), new MgLinearRingCollection());
for (int i = 0; i < layers.GetCount(); i++)
{
var layer = layers.GetItem(i);
var layerGroup = layer.GetGroup();
//Layer not visible or its parent is not visible
if (!layer.IsVisible() || (layerGroup != null && !layerGroup.IsVisible()))
continue;
//No defined tooltips
if (!_provider.LayerHasTooltips(layer))
continue;
//Drawing layers have no intelligence
string fsId = layer.FeatureSourceId;
if (fsId.EndsWith(MgResourceType.DrawingSource))
continue;
//Nor do rasters
if (IsRasterLayer(layer))
continue;
var objId = layer.GetObjectId();
var ldfId = layer.GetLayerDefinition();
var ldfIdStr = ldfId.ToString();
//No tooltip detected
//if (!_tooltipExpressions.ContainsKey(ldfIdStr))
if (!_provider.HasTooltips(ldfId))
continue;
string propName = "QUERYTOOLTIP";
MgFeatureQueryOptions query = new MgFeatureQueryOptions();
query.AddComputedProperty(propName, _provider.GetTooltipExpression(ldfId));
query.SetSpatialFilter(_provider.GetGeometryProperty(objId), poly, MgFeatureSpatialOperations.Intersects);
MgFeatureReader reader = null;
reader = layer.SelectFeatures(query);
try
{
if (reader.ReadNext())
{
object value = null;
var pt = reader.GetPropertyType(propName);
switch (pt)
{
case MgPropertyType.String:
value = reader.GetString(propName);
break;
case MgPropertyType.Boolean:
value = reader.GetBoolean(propName);
break;
case MgPropertyType.Byte:
value = reader.GetByte(propName);
break;
case MgPropertyType.DateTime:
value = reader.GetByte(propName);
break;
case MgPropertyType.Double:
case MgPropertyType.Decimal:
value = reader.GetDouble(propName);
break;
case MgPropertyType.Int16:
value = reader.GetInt16(propName);
break;
case MgPropertyType.Int32:
value = reader.GetInt32(propName);
break;
case MgPropertyType.Int64:
value = reader.GetInt64(propName);
break;
case MgPropertyType.Single:
value = reader.GetSingle(propName);
break;
}
if (value != null)
return value.ToString(); //.Replace("\n", Environment.NewLine);
}
}
finally
{
reader.Close();
}
}
return string.Empty;
}
private static bool IsRasterClass(MgClassDefinition cls)
{
var props = cls.GetProperties();
for (int i = 0; i < props.GetCount(); i++)
{
var p = props.GetItem(i);
if (p.PropertyType == MgFeaturePropertyType.RasterProperty)
return true;
}
return false;
}
private static bool IsRasterLayer(MgLayerBase layer)
{
var cls = layer.GetClassDefinition();
return IsRasterClass(cls);
}
///
/// Copies the image of the current map to the clipboard
///
public void CopyMap()
{
var bmp = new Bitmap(this.Width, this.Height);
this.DrawToBitmap(bmp, this.ClientRectangle);
Clipboard.SetImage(bmp);
}
///
/// Selects features from all selectable layers that intersects the given geometry
///
///
public void SelectByGeometry(MgGeometry geom)
{
//Don't select if dragging. This is the cause of the failure to render
//multiple selections, which required a manual refresh afterwards
if (isDragging)
return;
#if TRACE
var sw = new Stopwatch();
sw.Start();
#endif
var layers = _map.GetLayers();
if (ModifierKeys != Keys.Control)
_provider.ClearSelection(_selection);
for (int i = 0; i < layers.GetCount(); i++)
{
var layer = layers.GetItem(i);
if (!layer.Selectable || !layer.IsVisible())
continue;
string fsId = layer.FeatureSourceId;
if (fsId.EndsWith(MgResourceType.DrawingSource))
continue;
//Nor do rasters
if (IsRasterLayer(layer))
continue;
//This could be a newly added layer
_provider.CheckAndCacheGeometryProperty(layer);
var objId = layer.GetObjectId();
MgFeatureQueryOptions query = new MgFeatureQueryOptions();
string filter = layer.GetFilter();
if (!string.IsNullOrEmpty(filter))
query.SetFilter(filter);
query.SetSpatialFilter(_provider.GetGeometryProperty(objId), geom, MgFeatureSpatialOperations.Intersects);
MgFeatureReader reader = layer.SelectFeatures(query);
try
{
_selection.AddFeatures(layer, reader, 0);
}
finally
{
reader.Close();
}
}
#if TRACE
sw.Stop();
Trace.TraceInformation("Selection processing completed in {0}ms", sw.ElapsedMilliseconds);
#endif
RenderSelection(); //This is either async or queued up. Either way do this before firing off selection changed
var handler = this.SelectionChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
protected override void OnResize(EventArgs e)
{
OnControlResized(this, e);
base.OnResize(e);
}
#region Mouse handlers
private void OnMapMouseDown(object sender, MouseEventArgs e)
{
HandleMouseDown(e);
}
private void OnMapMouseMove(object sender, MouseEventArgs e)
{
HandleMouseMove(e);
}
private void OnMapMouseUp(object sender, MouseEventArgs e)
{
HandleMouseUp(e);
}
private void OnMapMouseClick(object sender, MouseEventArgs e)
{
this.Focus();
HandleMouseClick(e);
}
private void OnMapMouseDoubleClick(object sender, MouseEventArgs e)
{
this.Focus();
HandleMouseDoubleClick(e);
}
private void HandleMouseDoubleClick(MouseEventArgs e)
{
//Not enough points to constitute a line string or polygon
if (dPath.Count < 2)
return;
if (this.DigitizingType == MapDigitizationType.LineString)
{
//Fix the last one, can't edit last one because points are value types
dPath.RemoveAt(dPath.Count - 1);
dPath.Add(new Point(e.X, e.Y));
OnLineStringDigitized(dPath);
}
else if (this.DigitizingType == MapDigitizationType.Polygon)
{
//Fix the last one, can't edit last one because points are value types
dPath.RemoveAt(dPath.Count - 1);
dPath.Add(new Point(e.X, e.Y));
OnPolygonDigitized(dPath);
}
}
private void HandleMouseClick(MouseEventArgs e)
{
if (this.DigitizingType != MapDigitizationType.None)
{
//Points are easy, one click and you're done
if (this.DigitizingType == MapDigitizationType.Point)
{
OnPointDigitizationCompleted(new Point(e.X, e.Y));
}
else
{
//Check first click in digitization
if (_digitizationYetToStart)
{
if (this.DigitizingType == MapDigitizationType.LineString ||
this.DigitizingType == MapDigitizationType.Polygon)
{
dPath.Add(new Point(e.X, e.Y));
dPath.Add(new Point(e.X, e.Y)); //This is a transient one
}
else
{
dPtStart.X = e.X;
dPtStart.Y = e.Y;
}
_digitizationYetToStart = false;
}
else
{
if (this.DigitizingType == MapDigitizationType.LineString ||
this.DigitizingType == MapDigitizationType.Polygon)
{
var pt = dPath[dPath.Count - 1];
pt.X = e.X;
pt.Y = e.Y;
dPath.Add(new Point(e.X, e.Y)); //This is a transient one
}
else
{
//Fortunately, these are all 2-click affairs meaning this is
//the second click
switch (this.DigitizingType)
{
case MapDigitizationType.Circle:
{
dPtEnd.X = e.X;
dPtEnd.Y = e.Y;
OnCircleDigitized(dPtStart, dPtEnd);
}
break;
case MapDigitizationType.Line:
{
dPtEnd.X = e.X;
dPtEnd.Y = e.Y;
OnLineDigitized(dPtStart, dPtEnd);
}
break;
case MapDigitizationType.Rectangle:
{
dPtEnd.X = e.X;
dPtEnd.Y = e.Y;
var rect = GetRectangle(dPtStart, dPtEnd);
if (rect.HasValue)
OnRectangleDigitized(rect.Value);
}
break;
}
}
}
}
}
else
{
if (this.ActiveTool == MapActiveTool.Select)
{
var mapPt1 = ScreenToMapUnits(e.X - 2, e.Y - 2);
var mapPt2 = ScreenToMapUnits(e.X + 2, e.Y + 2);
var coord1 = _geomFact.CreateCoordinateXY(mapPt1.X, mapPt1.Y);
var coord2 = _geomFact.CreateCoordinateXY(mapPt2.X, mapPt2.Y);
var dist = _mapMeasure.GetDistance(coord1, coord2);
MgGeometry geom = _wktRW.Read(MakeWktPolygon(mapPt1.X, mapPt1.Y, mapPt2.X, mapPt2.Y));
SelectByGeometry(geom);
}
else if (this.ActiveTool == MapActiveTool.ZoomIn)
{
if (!isDragging)
{
var mapPt = ScreenToMapUnits(e.X, e.Y);
var scale = _map.ViewScale;
ZoomToView(mapPt.X, mapPt.Y, scale * ZoomInFactor, true);
}
}
else if (this.ActiveTool == MapActiveTool.ZoomOut)
{
if (!isDragging)
{
var mapPt = ScreenToMapUnits(e.X, e.Y);
var scale = _map.ViewScale;
ZoomToView(mapPt.X, mapPt.Y, scale * ZoomOutFactor, true);
}
}
}
}
private void HandleMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
dragStart = e.Location;
Trace.TraceInformation("Drag started at (" + dragStart.X + ", " + dragStart.Y + ")");
switch (this.ActiveTool)
{
case MapActiveTool.Pan:
Trace.TraceInformation("START PANNING");
break;
case MapActiveTool.Select:
Trace.TraceInformation("START SELECT");
break;
case MapActiveTool.ZoomIn:
Trace.TraceInformation("START ZOOM");
break;
}
}
}
private System.Drawing.Point translate;
private System.Drawing.Point dragStart;
bool isDragging = false;
private int _mouseX;
private int _mouseY;
/*
class ToolTipWaitArgs
{
public int Interval { get; set; }
public int MouseX { get; set; }
public int MouseY { get; set; }
}
void TooltipWaitProc(ToolTipWaitArgs e)
{
Thread.Sleep(e.Interval);
this.BeginInvoke(new MethodInvoker(() =>
{
//Compare old position against current
if ((Math.Abs(e.MouseX - _mouseX) < 2) &&
(Math.Abs(e.MouseY - _mouseY) < 2))
{
FireTooltipQuery();
}
}));
}
private void FireTooltipQuery()
{
string tooltip = QueryFirstMatchingTooltip();
if (tooltip != null)
{
_tooltip.Show(tooltip, this);
}
else
{
_tooltip.Hide(this);
}
}
private string QueryFirstMatchingTooltip()
{
throw new NotImplementedException();
}
*/
private string _activeTooltipText;
private int _mouseDx;
private int _mouseDy;
///
/// A mouse is considered to have moved if the differerence in either X or Y directions is greater
/// than this number
///
const int MOUSE_MOVE_TOLERANCE = 2;
private void HandleMouseMove(MouseEventArgs e)
{
if (_mouseX == e.X &&
_mouseY == e.Y)
{
return;
}
//Record displacement
_mouseDx = e.X - _mouseX;
_mouseDy = e.Y - _mouseY;
_mouseX = e.X;
_mouseY = e.Y;
var mapPt = ScreenToMapUnits(e.X, e.Y);
OnMouseMapPositionChanged(mapPt.X, mapPt.Y);
if (this.ActiveTool == MapActiveTool.Pan || this.ActiveTool == MapActiveTool.Select || this.ActiveTool == MapActiveTool.ZoomIn)
{
if (e.Location != dragStart && !isDragging && e.Button == MouseButtons.Left)
{
isDragging = true;
}
if (this.ActiveTool == MapActiveTool.Pan)
{
if (isDragging)
{
translate = new System.Drawing.Point(e.X - dragStart.X, e.Y - dragStart.Y);
}
}
// FIXME:
//
// We really need a JS setTimeout() equivalent for C# because that's what we want
// to do here, set a delayed call to QueryFirstVisibleTooltip() that is aborted if
// the mouse pointer has moved significantly since the last time.
//
// A timer based approach could probably work, but I haven't figured out the best
// way yet.
this.TooltipsEnabled = !isDragging && this.FeatureTooltipsEnabled;
//Only query for tooltips if not digitizing
if (this.DigitizingType == MapDigitizationType.None &&
(this.ActiveTool == MapActiveTool.Select || this.ActiveTool == MapActiveTool.Pan) &&
this.TooltipsEnabled)
{
#if TRACE
var sw = new Stopwatch();
sw.Start();
#endif
_activeTooltipText = QueryFirstVisibleTooltip(e.X, e.Y);
#if TRACE
sw.Stop();
Trace.TraceInformation("QueryFirstVisibleTooltip() executed in {0}ms", sw.ElapsedMilliseconds);
#endif
}
else
{
_activeTooltipText = null;
}
if (e.Button == MouseButtons.Left || !string.IsNullOrEmpty(_activeTooltipText))
Invalidate();
}
else if (this.DigitizingType != MapDigitizationType.None)
{
if (dPath.Count >= 2)
{
//Fix the last one, can't edit last one because points are value types
dPath.RemoveAt(dPath.Count - 1);
dPath.Add(new Point(e.X, e.Y));
Trace.TraceInformation("Updating last point of a {0} point path", dPath.Count);
}
Invalidate();
}
}
private void HandleMouseUp(MouseEventArgs e)
{
if (isDragging)
{
isDragging = false;
if (this.ActiveTool == MapActiveTool.Pan)
{
//FIXME: This is not perfect. The view will be slightly off of where you released the mouse button
//System.Diagnostics.Trace.TraceInformation("Dragged screen distance (" + translate.X + ", " + translate.Y + ")");
int dx = e.X - dragStart.X;
int dy = e.Y - dragStart.Y;
var centerScreen = new Point(this.Location.X + (this.Width / 2), this.Location.Y + (this.Height / 2));
centerScreen.X -= translate.X;
centerScreen.Y -= translate.Y;
var pt = _map.ViewCenter.Coordinate;
var coord = ScreenToMapUnits(centerScreen.X, centerScreen.Y);
double mdx = coord.X - pt.X;
double mdy = coord.Y - pt.Y;
ZoomToView(coord.X, coord.Y, _map.ViewScale, true);
Trace.TraceInformation("END PANNING");
}
else if (this.ActiveTool == MapActiveTool.Select)
{
var mapPt = ScreenToMapUnits(e.X, e.Y);
var mapDragPt = ScreenToMapUnits(dragStart.X, dragStart.Y);
var ringCoords = new MgCoordinateCollection();
ringCoords.Add(_geomFact.CreateCoordinateXY(mapDragPt.X, mapDragPt.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapPt.X, mapDragPt.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapPt.X, mapPt.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapDragPt.X, mapPt.Y));
ringCoords.Add(_geomFact.CreateCoordinateXY(mapDragPt.X, mapDragPt.Y)); //Close it
var poly = _geomFact.CreatePolygon(_geomFact.CreateLinearRing(ringCoords), new MgLinearRingCollection());
SelectByGeometry(poly);
}
else if (this.ActiveTool == MapActiveTool.ZoomIn)
{
var mapPt = ScreenToMapUnits(e.X, e.Y);
var mapDragPt = ScreenToMapUnits(dragStart.X, dragStart.Y);
PointF ll;
PointF ur;
if (mapPt.X <= mapDragPt.X && mapPt.Y <= mapDragPt.Y)
{
ll = mapPt;
ur = mapDragPt;
}
else
{
ll = mapDragPt;
ur = mapPt;
}
ZoomToExtents(ll.X, ll.Y, ur.X, ur.Y);
}
}
}
private void OnMouseMapPositionChanged(double x, double y)
{
var handler = this.MouseMapPositionChanged;
if (handler != null)
handler(this, new MapPointEventArgs(x, y));
}
///
/// Raised when the map cursor position has changed
///
[Category("MapGuide Viewer")]
[Description("Raised when the map position as indicated by the current mouse pointer has changed")]
public event EventHandler MouseMapPositionChanged;
#endregion
private MapActiveTool _tool;
///
/// Gets or sets the active tool
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MapActiveTool ActiveTool
{
get
{
return _tool;
}
set
{
if (_tool.Equals(value))
return;
_tool = value;
switch (value)
{
case MapActiveTool.Pan:
using (var ms = new MemoryStream(Properties.Resources.grab))
{
this.Cursor = new Cursor(ms);
}
break;
case MapActiveTool.ZoomIn:
using (var ms = new MemoryStream(Properties.Resources.zoomin))
{
this.Cursor = new Cursor(ms);
}
break;
case MapActiveTool.ZoomOut:
using (var ms = new MemoryStream(Properties.Resources.zoomout))
{
this.Cursor = new Cursor(ms);
}
break;
case MapActiveTool.None:
case MapActiveTool.Select:
{
this.Cursor = Cursors.Default;
}
break;
}
//Clear to prevent stray tooltips from being rendered
if (value != MapActiveTool.Select &&
value != MapActiveTool.Pan)
{
_activeTooltipText = null;
}
if (value != MapActiveTool.None)
this.DigitizingType = MapDigitizationType.None;
OnPropertyChanged("ActiveTool");
}
}
///
/// Screens to map units.
///
/// The x.
/// The y.
///
public PointF ScreenToMapUnits(double x, double y)
{
return ScreenToMapUnits(x, y, false);
}
private PointF ScreenToMapUnits(double x, double y, bool allowOutsideWindow)
{
if (!allowOutsideWindow)
{
if (x > this.Width - 1) x = this.Width - 1;
else if (x < 0) x = 0;
if (y > this.Height - 1) y = this.Height - 1;
else if (y < 0) y = 0;
}
x = _extX1 + (_extX2 - _extX1) * (x / this.Width);
y = _extY1 - (_extY1 - _extY2) * (y / this.Height);
return new PointF((float)x, (float)y);
}
///
/// Occurs when a property value changes.
///
[Category("MapGuide Viewer")]
[Description("Raised when a public property of this component has changed")]
public event PropertyChangedEventHandler PropertyChanged;
///
/// Called when a public property has changed
///
/// The name.
protected void OnPropertyChanged(string name)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
}
}