From 20cdf04d3fad767cc5230992a28e9aa59dbbcf50 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Fri, 13 Nov 2015 01:09:20 +0100 Subject: [PATCH] Point geometry OK. Used resource bitmap to render. Issue #8 --- SqlServerSpatial.Toolkit.Test/Program.cs | 46 ++++++ .../Extensions/ColorExtensions.cs | 2 +- .../SqlServerSpatial.Toolkit.Core.csproj | 1 + .../Viewers/GDI/SpatialViewer_GDI.cs | 139 ++++++++++++++---- .../Viewers/GDI/SqlGeometryGDISink.cs | 14 +- .../Viewers/GDI/point.png | Bin 0 -> 318 bytes .../Viewers/SqlGeometryStyled.cs | 6 +- 7 files changed, 166 insertions(+), 42 deletions(-) create mode 100644 SqlServerSpatial.Toolkit/Viewers/GDI/point.png diff --git a/SqlServerSpatial.Toolkit.Test/Program.cs b/SqlServerSpatial.Toolkit.Test/Program.cs index aafb973..1d9b84d 100644 --- a/SqlServerSpatial.Toolkit.Test/Program.cs +++ b/SqlServerSpatial.Toolkit.Test/Program.cs @@ -17,6 +17,50 @@ static void Main(string[] args) //TestTrace(); TestCentroid(); + TestVariousGeometries(); + } + + private static void TestVariousGeometries() + { + SqlGeometry simplePoint = SqlGeometry.Point(1, 47, 4326); + SqlGeometry multiPoint = SqlGeometry.Parse(new SqlString("MULTIPOINT((1 47),(1 46),(0 46),(0 47),(1 47))")); multiPoint.STSrid = 4326; + SqlGeometry lineString = SqlGeometry.Parse(new SqlString("LINESTRING(1 47,1 46,0 46,0 47,1 47)")); lineString.STSrid = 4326; + SqlGeometry multiLineString = SqlGeometry.Parse(new SqlString("MULTILINESTRING((0.516357421875 47.6415668949958,0.516357421875 47.34463879017405,0.977783203125 47.22539733216678,1.175537109375 47.463611506072866,0.516357421875 47.6415668949958),(0.764923095703125 47.86549372980948,0.951690673828125 47.82309640371982,1.220855712890625 47.79911736820551,1.089019775390625 47.69015026565801,1.256561279296875 47.656860648589))")); + multiLineString.STSrid = 4326; + SqlGeometry simplePoly = SqlGeometry.Parse(new SqlString("POLYGON((1 47,1 46,0 46,0 47,1 47))")); simplePoly.STSrid = 4326; + SqlGeometry polyWithHole = SqlGeometry.Parse(new SqlString(@" + POLYGON( + (0.516357421875 47.6415668949958,0.516357421875 47.34463879017405,0.977783203125 47.22539733216678,1.175537109375 47.463611506072866,0.516357421875 47.6415668949958), + (0.630340576171875 47.54944962456812,0.630340576171875 47.49380564962583,0.729217529296875 47.482669772098674,0.731964111328125 47.53276262898896,0.630340576171875 47.54944962456812) + )")); polyWithHole.STSrid = 4326; + SqlGeometry multiPolygon = SqlGeometry.Parse(new SqlString(@" + MULTIPOLYGON ( + ((40 40, 20 45, 45 30, 40 40)), + ((20 35, 45 20, 30 5, 10 10, 10 30, 20 35), (30 20, 20 25, 20 15, 30 20)), + ((0.516357421875 47.6415668949958,0.516357421875 47.34463879017405,0.977783203125 47.22539733216678,1.175537109375 47.463611506072866,0.516357421875 47.6415668949958),(0.630340576171875 47.54944962456812,0.630340576171875 47.49380564962583,0.729217529296875 47.482669772098674,0.731964111328125 47.53276262898896,0.630340576171875 47.54944962456812)) + )")); multiPolygon.STSrid = 4326; + + SqlGeometry geomCol = SqlGeometry.Parse(new SqlString(@" + GEOMETRYCOLLECTION ( + POLYGON((0.516357421875 47.6415668949958,0.516357421875 47.34463879017405,0.977783203125 47.22539733216678,1.175537109375 47.463611506072866,0.516357421875 47.6415668949958),(0.630340576171875 47.54944962456812,0.630340576171875 47.49380564962583,0.729217529296875 47.482669772098674,0.731964111328125 47.53276262898896,0.630340576171875 47.54944962456812)), + LINESTRING(0.764923095703125 47.86549372980948,0.951690673828125 47.82309640371982,1.220855712890625 47.79911736820551,1.089019775390625 47.69015026565801,1.256561279296875 47.656860648589), + POINT(0.767669677734375 47.817563762851776) + )")); geomCol.STSrid = 4326; + + SpatialTrace.Enable(); + SpatialTrace.SetFillColor(Color.FromArgb(128, 0, 0, 255)); // Fill with blue + SpatialTrace.TraceGeometry(simplePoint,"simplePoint"); + SpatialTrace.SetFillColor(Color.FromArgb(128, 255, 0, 0)); // Fill with red + SpatialTrace.TraceGeometry(multiPoint, "multiPoint"); + SpatialTrace.ResetStyle(); + SpatialTrace.TraceGeometry(lineString, "lineString"); + SpatialTrace.TraceGeometry(multiLineString, "multiLineString"); + SpatialTrace.TraceGeometry(simplePoly, "simplePoly"); + SpatialTrace.TraceGeometry(polyWithHole, "polyWithHole"); + SpatialTrace.TraceGeometry(multiPolygon, "multiPolygon"); + SpatialTrace.TraceGeometry(geomCol, "geomCol"); + SpatialTrace.ShowDialog(); + SpatialTrace.Clear(); } private static void TestTrace() @@ -48,6 +92,7 @@ private static void TestTrace() SpatialTrace.Unindent(); SpatialTrace.Unindent(); SpatialTrace.ShowDialog(); + SpatialTrace.Clear(); } static void TestCentroid() @@ -59,6 +104,7 @@ static void TestCentroid() SpatialTrace.SetFillColor(Colors.Red); SpatialTrace.TraceGeometry(centroid, "Centroid"); SpatialTrace.ShowDialog(); + SpatialTrace.Clear(); } } } diff --git a/SqlServerSpatial.Toolkit/Extensions/ColorExtensions.cs b/SqlServerSpatial.Toolkit/Extensions/ColorExtensions.cs index 0431cb9..568b69a 100644 --- a/SqlServerSpatial.Toolkit/Extensions/ColorExtensions.cs +++ b/SqlServerSpatial.Toolkit/Extensions/ColorExtensions.cs @@ -23,7 +23,7 @@ public static System.Windows.Media.Color ToWpf(this System.Drawing.Color gdiColo /// /// /// - public static System.Drawing.Color ToWpf(this System.Windows.Media.Color gdiColor) + public static System.Drawing.Color ToGDI(this System.Windows.Media.Color gdiColor) { return System.Drawing.Color.FromArgb(gdiColor.A, gdiColor.R, gdiColor.G, gdiColor.B); } diff --git a/SqlServerSpatial.Toolkit/SqlServerSpatial.Toolkit.Core.csproj b/SqlServerSpatial.Toolkit/SqlServerSpatial.Toolkit.Core.csproj index c80340f..8369739 100644 --- a/SqlServerSpatial.Toolkit/SqlServerSpatial.Toolkit.Core.csproj +++ b/SqlServerSpatial.Toolkit/SqlServerSpatial.Toolkit.Core.csproj @@ -159,6 +159,7 @@ PreserveNewest + diff --git a/SqlServerSpatial.Toolkit/Viewers/GDI/SpatialViewer_GDI.cs b/SqlServerSpatial.Toolkit/Viewers/GDI/SpatialViewer_GDI.cs index 7221523..541a579 100644 --- a/SqlServerSpatial.Toolkit/Viewers/GDI/SpatialViewer_GDI.cs +++ b/SqlServerSpatial.Toolkit/Viewers/GDI/SpatialViewer_GDI.cs @@ -6,7 +6,10 @@ using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Windows; using System.Windows.Forms; @@ -19,17 +22,26 @@ namespace SqlServerSpatial.Toolkit.Viewers /// public partial class SpatialViewer_GDI : Control, ISpatialViewer, IDisposable //, IMessageFilter // for mousewheel { + // geometry bounding box BoundingBox _geomBBox; - Dictionary> _strokes; - Dictionary> _fills; + + bool _readyToDraw = false; + + // Viewport variables + float _currentFactorMouseWheel = 1f; + float _scale = 1f; + float _scaleX = 1f; + float _scaleY = 1f; Matrix _mouseTranslate; Matrix _mouseScale; Matrix _previousMatrix; - Vector _unitVectorAtGeometryScale; - bool _readyToDraw = false; public bool AutoViewPort { get; set; } - float _currentFactorMouseWheel = 1f; + // GDI+ geometries + Dictionary> _strokes; + Dictionary> _fills; + Dictionary> _points; + Bitmap _pointBmp; public SpatialViewer_GDI() { @@ -38,12 +50,20 @@ public SpatialViewer_GDI() SetStyle(ControlStyles.OptimizedDoubleBuffer, true); _strokes = new Dictionary>(); _fills = new Dictionary>(); + _points = new Dictionary>(); _mouseTranslate = new Matrix(); _mouseScale = new Matrix(); //System.Windows.Forms.Application.AddMessageFilter(this); System.Windows.Forms.Application.AddMessageFilter(new MouseWheelMessageFilter()); this.MouseWheel += SpatialViewer_GDI_MouseWheel; + + // Load point icon + Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); + using (Stream file = assembly.GetManifestResourceStream("SqlServerSpatial.Toolkit.Viewers.GDI.point.png")) + { + _pointBmp = (Bitmap)Image.FromStream(file); + } } #region Dispose and Finalize @@ -59,6 +79,10 @@ public SpatialViewer_GDI() protected override void Dispose(bool disposing) { //clean up unmanaged here + + if (_pointBmp != null) + _pointBmp.Dispose(); + DisposeGraphicsPaths(); _mouseTranslate.Dispose(); _mouseScale.Dispose(); @@ -121,6 +145,7 @@ protected override void OnPaint(PaintEventArgs pe) } pe.Graphics.SmoothingMode = SmoothingMode.HighQuality; + // Shapes foreach (var kvpFill in _fills) { using (Brush fillBrush = FromGeomStyleToBrush(kvpFill.Key)) @@ -135,6 +160,7 @@ protected override void OnPaint(PaintEventArgs pe) } } } + // Outlines foreach (var kvpStroke in _strokes) { using (Pen strokePen = FromGeomStyleToPen(kvpStroke.Key)) @@ -149,6 +175,23 @@ protected override void OnPaint(PaintEventArgs pe) } } } + + // Points + foreach (var kvpPoint in _points) + { + using (Bitmap bmp = FromGeomStyleToPoint(_pointBmp, kvpPoint.Key)) + { + PointF[] points = kvpPoint.Value.ToArray(); + if (points.Any()) + { + mat.TransformPoints(points); + foreach (PointF point in points) + { + pe.Graphics.DrawImageUnscaled(bmp, (int)point.X - _pointBmp.Width / 2, (int)point.Y - _pointBmp.Height / 2); + } + } + } + } } sw.Stop(); @@ -157,6 +200,8 @@ protected override void OnPaint(PaintEventArgs pe) } } + + #region GDI Helpers private Pen FromGeomStyleToPen(GeometryStyle geometryStyle) { @@ -168,6 +213,10 @@ private Brush FromGeomStyleToBrush(GeometryStyle geometryStyle) System.Windows.Media.Color c = geometryStyle.FillColor; return new SolidBrush(Color.FromArgb(c.A, c.R, c.G, c.B)); } + private Bitmap FromGeomStyleToPoint(Bitmap sourceBitmap, GeometryStyle geometryStyle) + { + return TintBitmap(sourceBitmap, geometryStyle.FillColor.ToGDI(), 1f); + } private void AppendStrokePath(GeometryStyle style, GraphicsPath stroke) { if (_strokes.ContainsKey(style) == false) @@ -182,6 +231,43 @@ private void AppendFilledPath(GeometryStyle style, GraphicsPath path) _fills[style].Add(path); } + private void AppendPoints(GeometryStyle style, List points) + { + if (_points.ContainsKey(style) == false) + _points[style] = new List(); + + _points[style].AddRange(points); + } + /// + /// Tints a bitmap using the specified color and intensity. + /// + /// Bitmap to be tinted + /// Color to use for tint + /// Intensity of the tint. Good ranges are .25 to .75, depending on your preference. Most images will white out around 2.0. 0 will not tint the image at all + /// A bitmap with the requested Tint + /// http://stackoverflow.com/questions/9356694/tint-property-when-drawing-image-with-vb-net + Bitmap TintBitmap(Bitmap bitmap, Color color, float intensity) + { + Bitmap outBmp = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat); + + using (ImageAttributes ia = new ImageAttributes()) + { + + ColorMatrix m = new ColorMatrix(new float[][] + {new float[] {1, 0, 0, 0, 0}, + new float[] {0, 1, 0, 0, 0}, + new float[] {0, 0, 1, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {color.R/255*intensity, color.G/255*intensity, color.B/255*intensity, 0, 1}}); + + ia.SetColorMatrix(m); + using (Graphics g = Graphics.FromImage(outBmp)) + g.DrawImage(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height), 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, ia); + } + + return outBmp; + } + #endregion public void SetGeometry(IEnumerable geometries) @@ -197,8 +283,9 @@ Matrix GenerateGeometryTransformViewMatrix() } else { - float width = this.ClientRectangle.Width; - float height = this.ClientRectangle.Height; + int margin = 20; + float width = this.ClientRectangle.Width - margin; + float height = this.ClientRectangle.Height - margin; Matrix m = new Matrix(); @@ -206,8 +293,10 @@ Matrix GenerateGeometryTransformViewMatrix() m.Translate((float)(-_geomBBox.XMin - _geomBBox.Width / 2d), (float)(-_geomBBox.yMin - _geomBBox.Height / 2d)); // Scale and invert Y as Y raises downwards - double scale = Math.Min(width / _geomBBox.Width, height / _geomBBox.Height); - m.Scale((float)scale, -(float)scale, MatrixOrder.Append); + _scaleX = (float)(width / _geomBBox.Width); + _scaleY = (float)(height / _geomBBox.Height); + _scale = (float)Math.Min(_scaleX, _scaleY); + m.Scale(_scale, -_scale, MatrixOrder.Append); // translate to map center BoundingBox bboxTrans = _geomBBox.Transform(m); @@ -218,30 +307,16 @@ Matrix GenerateGeometryTransformViewMatrix() _previousMatrix.Dispose(); } _previousMatrix = m.Clone(); - CalculateUnitVector(m, width, height); return m; } } - void CalculateUnitVector(Matrix mat, float mapWidth, float mapHeight) - { - using (Matrix matrix = mat.Clone()) - { - if (matrix.IsInvertible) - { - matrix.Invert(); - double width = mapWidth, height = mapHeight; - double scale = Math.Min(width / _geomBBox.Width - , height / _geomBBox.Height); - PointF vector1px = new PointF((float)(1d / scale), 0); - _unitVectorAtGeometryScale = new Vector(vector1px.X, vector1px.Y); - } - else - { - _unitVectorAtGeometryScale = new Vector(1, 1); - } - } - } + //void CalculateUnitVector() + //{ + // // geom units * scale = pixels + // // To get in units what is a pixel, we do W * scale = 1 => W = 1 / scale; + // _unitVectorAtGeometryScale = new Vector(1d / (_currentFactorMouseWheel * _scaleX) * 2, 1d / (_currentFactorMouseWheel * _scaleY) * 2); + //} private void Internal_SetGeometry(IEnumerable geometries) { @@ -266,6 +341,7 @@ private void Internal_SetGeometry(IEnumerable geometries) { System.Windows.Forms.MessageBox.Show("Some geometries are not valid. Will try to valid them.", "Invalid geometry", MessageBoxButtons.OK, MessageBoxIcon.Warning); } + foreach (SqlGeometryStyled geomStyled in geometries) { @@ -280,9 +356,11 @@ private void Internal_SetGeometry(IEnumerable geometries) envelope = envelope.STUnion(geometry.STEnvelope()).STEnvelope(); GraphicsPath stroke = new GraphicsPath(); GraphicsPath fill = new GraphicsPath(); - SqlGeometryGDISink.ConvertSqlGeometry(geometry, _unitVectorAtGeometryScale, ref stroke, ref fill); + List points = new List(); + SqlGeometryGDISink.ConvertSqlGeometry(geometry, ref stroke, ref fill, ref points); AppendFilledPath(geomStyled.Style, fill); AppendStrokePath(geomStyled.Style, stroke); + AppendPoints(geomStyled.Style, points); } #region BBox @@ -343,6 +421,7 @@ private void ClearGDI() DisposeGraphicsPaths(); _strokes = new Dictionary>(); _fills = new Dictionary>(); + _points = new Dictionary>(); } public void Clear() diff --git a/SqlServerSpatial.Toolkit/Viewers/GDI/SqlGeometryGDISink.cs b/SqlServerSpatial.Toolkit/Viewers/GDI/SqlGeometryGDISink.cs index b5f0a40..316c645 100644 --- a/SqlServerSpatial.Toolkit/Viewers/GDI/SqlGeometryGDISink.cs +++ b/SqlServerSpatial.Toolkit/Viewers/GDI/SqlGeometryGDISink.cs @@ -17,29 +17,27 @@ internal class SqlGeometryGDISink : IGeometrySink110 GraphicsPath _gpStroke; GraphicsPath _gpFill; List _currentLine; - Vector _unitVector; + List _points; - public static void ConvertSqlGeometry(SqlGeometry geom, Vector unitVector, ref GraphicsPath stroke, ref GraphicsPath fill) + public static void ConvertSqlGeometry(SqlGeometry geom, ref GraphicsPath stroke, ref GraphicsPath fill, ref List points) { - SqlGeometryGDISink sink = new SqlGeometryGDISink(stroke, fill, unitVector); + SqlGeometryGDISink sink = new SqlGeometryGDISink(stroke, fill, points); geom.Populate(sink); } - private SqlGeometryGDISink(GraphicsPath gpStroke, GraphicsPath gpFill, Vector unitVector) + private SqlGeometryGDISink(GraphicsPath gpStroke, GraphicsPath gpFill, List points) { _gpStroke = gpStroke; _gpFill = gpFill; _currentLine = new List(); - _unitVector = unitVector; + _points = points; } public void BeginFigure(double x, double y, double? z, double? m) { if (_curType == OpenGisGeometryType.Point) { - // for a point, we draw an ellipse with the provider unit vector - _gpFill.AddEllipse((float)x, (float)y, (float)(_unitVector.Length * 2d), (float)(_unitVector.Length * 2d)); - _gpStroke.AddEllipse((float)x, (float)y, (float)(_unitVector.Length * 2d), (float)(_unitVector.Length * 2d)); + _points.Add(new PointF((float)x, (float)y)); } else { diff --git a/SqlServerSpatial.Toolkit/Viewers/GDI/point.png b/SqlServerSpatial.Toolkit/Viewers/GDI/point.png new file mode 100644 index 0000000000000000000000000000000000000000..4b4b9b9cc34f3521c5fa579369eaadd385e99eff GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^{20#LT=By}Z;C1rt33 zJ+nVO$@hV3)_b}*hFJLb2HoauHsCpvZf7izY~Y&yg5&Nc=91=RcExQ+48xS?uBraP z(s-#_zewxZ{tY&M!h2X}^Se6q{Y*Qg_k-gS(~mQ6Pq8cOHu%MhhZZ+(V3_l3=Zaq* zAqA{6o?Kp36&LVPtD-=!dserL!@1xOM|)}%#MZml79N|vgX5j7;mPSuGw;OkS$zGe z&C_^0dg*?V858xS9vG+3-x$L9?`p}#2AR)STb?;f_SNXyug<(L@ug)2JJ9J2p00i_ I>zopr0BVGHm;e9( literal 0 HcmV?d00001 diff --git a/SqlServerSpatial.Toolkit/Viewers/SqlGeometryStyled.cs b/SqlServerSpatial.Toolkit/Viewers/SqlGeometryStyled.cs index d0ea2ac..4e0d092 100644 --- a/SqlServerSpatial.Toolkit/Viewers/SqlGeometryStyled.cs +++ b/SqlServerSpatial.Toolkit/Viewers/SqlGeometryStyled.cs @@ -85,9 +85,9 @@ public override int GetHashCode() { int hash = 17; // Maybe nullity checks, if these are objects not primitives! - hash = hash * 23 + FillColor.GetHashCode(); - hash = hash * 23 + StrokeColor.GetHashCode(); - hash = hash * 23 + StrokeWidth.GetHashCode(); + hash = hash + 23 * FillColor.ToString().GetHashCode(); + hash = hash + 23 * StrokeColor.ToString().GetHashCode(); + hash = hash + 23 * StrokeWidth.ToString().GetHashCode(); return hash; } }